Source code for latools.helpers.chemistry

"""
Functions for dealing with chemical formulae, and converting between molar 
ratios and mass fractions.

(c) Oscar Branson : https://github.com/oscarbranson
"""

import re
import pkg_resources as pkgrs
import pandas as pd

# masses of all elements.
[docs]def elements(all_isotopes=True): """ Loads a DataFrame of all elements and isotopes. Scraped from https://www.webelements.com/ Returns ------- pandas DataFrame with columns (element, atomic_number, isotope, atomic_weight, percent) """ el = pd.read_pickle(pkgrs.resource_filename('latools', 'resources/elements.pkl')) if all_isotopes: return el.set_index('element') else: def wmean(g): return (g.atomic_weight * g.percent).sum() / 100 iel = el.groupby('element').apply(wmean) iel.name = 'atomic_weight' return iel
[docs]def calc_M(molecule): """ Returns molecular weight of molecule. Where molecule is in standard chemical notation, e.g. 'CO2', 'HCO3' or B(OH)4 Returns ------- molecular_weight : float """ # load periodic table els = elements() # define regexs parens = re.compile('\(([A-z0-9]+)\)([0-9]+)?') stoich = re.compile('([A-Z][a-z]?)([0-9]+)?') ps = parens.findall(molecule) # find subgroups in parentheses rem = parens.sub('', molecule) # get remainder m = 0 # deal with sub-groups if len(ps) > 0: for sub, ns in ps: ms = 0 for e, n in stoich.findall(sub): me = (els.loc[e, 'atomic_weight'] * els.loc[e, 'percent'] / 100).sum() if n == '': n = 1 else: n = int(n) ms += me * n if ns == '': ns = 1 else: ns = int(ns) m += ms * ns # deal with remainder for e, n in stoich.findall(rem): me = (els.loc[e, 'atomic_weight'] * els.loc[e, 'percent'] / 100).sum() if n == '': n = 1 else: n = int(n) m += me * n return m
[docs]def decompose_molecule(molecule, n=1): """ Returns the chemical constituents of the molecule, and their number. Parameters ---------- molecule : str A molecule in standard chemical notation, e.g. 'CO2', 'HCO3' or 'B(OH)4'. Returns ------- All elements in molecule with their associated counts : dict """ if isinstance(n, str): n = int(n) # define regexs parens = re.compile('\(([A-z0-9()]+)\)([0-9]+)?') stoich = re.compile('([A-Z][a-z]?)([0-9]+)?') ps = parens.findall(molecule) # find subgroups in parentheses rem = parens.sub('', molecule) # get remainder if len(ps) > 0: for s, ns in ps: comp = decompose_molecule(s, ns) for k, v in comp.items(): comp[k] = v * n else: comp = {} for e, ns in stoich.findall(rem): if e not in comp: comp[e] = 0 if ns == '': ns = 1 * n else: ns = int(ns) * n comp[e] += ns return comp
[docs]def analyte_mass(analyte, in_name=True): """ Returns the mass of a given analyte. If the name contains a number (e.g. Ca43), that number is returned. If the name contains no number but an element name (e.g. Ca), the average mass of that element is returned. Parameters ---------- analyte : str or array-like The name or names of the analytes to be considered. in_name : bool If True, numbers in the analyte name are preferred. """ if isinstance(analyte, str): if '_' in analyte: return analyte_mass(analyte.split('_')) nums = re.findall('[0-9]+', analyte) if in_name and nums: return {analyte: float(nums[0])} else: return {analyte: calc_M(re.findall('[A-z]+', analyte)[0])} else: masses = {} for i, a in enumerate(analyte): masses.update(analyte_mass(a, in_name)) return masses
# functions for converting between mass fraction and molar ratio
[docs]def to_molar_ratio(massfrac_numerator, massfrac_denominator, numerator_mass, denominator_mass): """ Converts per-mass concentrations to molar elemental ratios. Be careful with units. Parameters ---------- numerator_mass, denominator_mass : float or array-like The atomic mass of the numerator and denominator. massfrac_numerator, massfrac_denominator : float or array-like The per-mass fraction of the numnerator and denominator. Returns ------- float or array-like : The molar ratio of elements in the material """ return (massfrac_numerator / numerator_mass) / (massfrac_denominator / denominator_mass)
[docs]def to_mass_fraction(molar_ratio, massfrac_denominator, numerator_mass, denominator_mass): """ Converts per-mass concentrations to molar elemental ratios. Be careful with units. Parameters ---------- molar_ratio : float or array-like The molar ratio of elements. massfrac_denominator : float or array-like The mass fraction of the denominator element numerator_mass, denominator_mass : float or array-like The atomic mass of the numerator and denominator. Returns ------- float or array-like : The mass fraction of the numerator element. """ return molar_ratio * massfrac_denominator * numerator_mass / denominator_mass