def _formula_parse_iso(self): 'Parse chemical formula of the salt and get list of all isotopes' tot_moles: float = 0.0 # Total molar fraction, should add to 1 for meltpart in self.formula.split('+'): # Separate melt components mfract, comp = meltpart.split( '%') # Separate component pct. fractions mfract = float(mfract) / 100.0 # Molar % -> fraction tot_moles += mfract # Add molar fractions of compositions comp_f = molmass.Formula( comp) # Turn component into a molmass formula for symbol in comp_f._elements: # Elements in a component ele = self.ELEMENTS[symbol] # Get the element object for m, n_atoms in comp_f._elements[symbol].items( ): # Number of atoms in a component if (m != 0): # This should be zero per molmass raise Exception("Error in parsing") for A in ele.isotopes.keys(): # Get data for each isotope Z = ele.protons amass = ele.isotopes[A].mass wfrac = ele.isotopes[A].abundance if wfrac > 0.0: isotuple = self.SaltIso(Z, A, n_atoms, amass, wfrac, mfract) self.isolist.append(isotuple) self.isolist.sort() # Looks nicer sorted if abs(tot_moles - 1.0) > 1e-5: # Sanity check raise ValueError("User Error: Formula " + self.formula + " molar fractions do not add to 1.0!")
def formula(self): """ >>> a = InChI('InChI=1S/C4H6O4.2Na/c5-3(6)1-2-4(7)8;;/h1-2H2,(H,5,6)(H,7,8);;/q;2*+1/p-2') >>> print a.formula C4H4Na2O4 >>> a = InChI('InChI=1S/C4H6O4.2Na/c5-3(6)1-2-4(7)8;;/h1-2H2,(H,5,6)(H,7,8);;/q;2*+1/p-2', 'H2O') >>> print a.formula H2O """ if self.__formula is None: self.__formula = convert_inchi_to_formula(self.__inchi) return molmass.Formula(self.__formula)
def ppm_to_ugm3(da, da_T2, da_PSFC): name = da.name new_attrs = da.attrs # get variables needed chem = da.name.upper() M = molmass.Formula(chem).mass R = 8.3145 # convert unit ppmv -> µg/m³ da = (M * da_PSFC * da) / (R * da_T2) # update attributes with new units new_attrs['units'] = 'ug m^-3' new_attrs['description'] = chem + \ ' mixing ratio converted to mass concentration' da.attrs = new_attrs da.name = name return (da)
def analyze(formula, maxatoms=250): """Return analysis of formula as HTML string.""" def html(formula): """Return formula as HTML string.""" formula = re.sub(r'\[(\d+)([A-Za-z]{1,2})\]', r'<sup>\1</sup>\2', formula) formula = re.sub(r'([A-Za-z]{1,2})(\d+)', r'\1<sub>\2</sub>', formula) return formula result = [] try: f = molmass.Formula(formula) result.append( f'<p><strong>Hill notation</strong>: {html(f.formula)}</p>') if f.formula != f.empirical: result.append(f'<p><strong>Empirical formula</strong>:' f' {html(f.empirical)}</p>') prec = molmass.precision_digits(f.mass, 8) if f.mass != f.isotope.mass: result.append( f'<p><strong>Average mass</strong>: {f.mass:.{prec}f}</p>') result.extend(( f'<p><strong>Monoisotopic mass</strong>:' f' {f.isotope.mass:.{prec}f}</p>', f'<p><strong>Nominal mass</strong>:' f' {f.isotope.massnumber}</p>', )) c = f.composition() if len(c) > 1: result.extend(( '<h3>Elemental Composition</h3>' '<table border="0" cellpadding="2">', '<tr>', '<th scope="col" align="left">Element</th>', '<th scope="col" align="right">Number</th>', '<th scope="col" align="right">Relative mass</th>', '<th scope="col" align="right">Fraction %</th>', '</tr>', )) for symbol, count, mass, fraction in c: symbol = re.sub(r'^(\d+)(.+)', r'<sup>\1</sup>\2', symbol) result.extend(( f'<tr><td>{symbol}</td>', f'<td align="center">{count}</td>', f'<td align="right">{mass:.{prec}f}</td>', f'<td align="right">{fraction * 100:.4f}</td></tr>', )) count, mass, fraction = c.total result.extend(( '<tr><td><em>Total</em></td>', f'<td align="center"><em>{count}</em></td>', f'<td align="right"><em>{mass:.{prec}f}</em></td>', f'<td align="right"><em>{fraction * 100:.4f}</em></td>', '</tr>', '</table>', )) if f.atoms < maxatoms: s = f.spectrum() if len(s) > 1: norm = 100.0 / s.peak[1] result.extend(( '<h3>Mass Distribution</h3>', '<p><strong>Most abundant mass</strong>: ', f'{s.peak[0]:.{prec}f} ({s.peak[1] * 100:.3f}%)</p>', f'<p><strong>Mean mass</strong>: {s.mean:.{prec}}</p>', '<table border="0" cellpadding="2">', '<tr>', '<th scope="col" align="left">Relative mass</th>', '<th scope="col" align="right">Fraction %</th>', '<th scope="col" align="right">Intensity</th>', '</tr>', )) for mass, fraction in s.values(): result.extend(( f'<tr><td>{mass:.{prec}f}</td>', f'<td align="right">{fraction*100.:.6}</td>', f'<td align="right">{fraction*norm:.6}</td></tr>', )) result.append('</table>') except Exception as exc: exc = str(exc).splitlines() text = exc[0][0].upper() + exc[0][1:] details = '\n'.join(exc[1:]) result.append('<h2>Error</h2><p>{}</p><pre>{}</pre>'.format( escape(text, True), escape(details, True))) return '\n'.join(result)
def formula(self): return molmass.Formula(self.__formula)
def _weight(x): return molmass.Formula(x).mass * si.gram / si.mole
def unit_conversion_ratio(df, unit_conversion_factor={}, feature_units={}, **kwargs): """Compute conversion factor between original units and new units (used by hgc).""" # generate a temporary dataframe to compute ratios df2 = pd.DataFrame() # split units into inidivual symbols (incl. prefix) and fill NaN df2[['orig1', 'orig2', 'orig3']] = df['Unit_orig0'].fillna('').str.split( r"/| ", expand=True, n=2).reindex(columns=range(3)).copy() df2[['new1', 'new2', 'new3']] = df['Unit'].fillna('').str.split( r"/| ", expand=True, n=2).reindex(columns=range(3)).copy() df2.loc[:, 'unit_conversion_correct'] = True for orig, new in {'orig1': 'new1', 'orig2': 'new2'}.items(): # force column to string (for example 1/m --> '1', 'm') df2[orig] = df2[orig].fillna('').astype(str) df2[new] = df2[new].fillna('').astype(str) # replace non-dimensional units by "1" df2[orig] = df2[orig].replace(['n', '-', '', ' '], '1').astype(str) df2[new] = df2[new].replace(['n', '-', '', ' '], '1').astype(str) # find where to correct for "mol" maska = df2[orig].str.contains("mol") maskb = df2[new].str.contains("mol") mask = maska | maskb # compute ratio mol --> gram features = df['Feature'][mask].unique() map_molwt = {} for feature in features: try: map_molwt[feature] = molmass.Formula(feature).mass except: # in case feature is not a chemical formula map_molwt[feature] = np.nan df2[orig + 'ratio'] = np.where(maska, df['Feature'].map(map_molwt), 1.) df2[new + 'ratio'] = np.where(maskb, df['Feature'].map(map_molwt), 1.) # log errors df2.loc[df2[orig + 'ratio'].isnull(), 'unit_conversion_correct'] = False df2.loc[df2[new + 'ratio'].isnull(), 'unit_conversion_correct'] = False # replace mol by gram (to prevent error in next step) and remove N, P, S df2[orig] = df2[orig].str.replace('mol', 'g') df2[new] = df2[new].str.replace('mol', 'g') df2.loc[maska, 'orig3'] = '' df2.loc[maskb, 'new3'] = '' # compute unit conversion ratio df2[orig + 'ratio'] = df2[orig + 'ratio'] * df2[orig].map(unit_conversion_factor) df2[new + 'ratio'] = df2[new + 'ratio'] * df2[new].map(unit_conversion_factor) # log errors. If orig and new both np.nan --> replace ratio's by 1 df2.loc[df2[orig + 'ratio'].isnull() | df2[new + 'ratio'].isnull(), 'unit_conversion_correct'] = False df2.loc[(df2[orig] == '') & (df2[new] == ''), [orig + 'ratio', new + 'ratio']] = 1. # there is no error if the units are on purpose empty (eg ph) df2.loc[(df['Unit'] == '1') & (df['Unit_orig0'] == '1'), 'unit_conversion_correct'] = True # force column to string df2['orig3'] = df2['orig3'].fillna('').astype(str) df2['new3'] = df2['new3'].fillna('').astype(str) # find where to compute N, P, S, correction factor (for example mg/L N --> mg/L) mask3a = df2['orig3'] != '' mask3b = df2['new3'] != '' mask3 = mask3a | mask3b df2.loc[mask3 & ~mask3a, 'orig3'] = df['Feature'] df2.loc[mask3 & ~mask3b, 'new3'] = df['Feature'] # compute weight of atoms/ features atoms = pd.unique(df2[['orig3', 'new3']][mask3].values.ravel()) map_molwt3 = {} for atom in atoms: if atom == 'PO4_ortho': atom = 'PO4' try: map_molwt3[atom] = molmass.Formula(atom).mass except: # in case atom is not a chemical formula map_molwt3[atom] = np.nan df2['orig3ratio'] = np.where(mask3, df2['orig3'].map(map_molwt3), 1.) df2['new3ratio'] = np.where(mask3, df2['new3'].map(map_molwt3), 1.) # log errors df2.loc[df2['orig3ratio'].isnull(), 'unit_conversion_correct'] = False df2.loc[df2['new3ratio'].isnull(), 'unit_conversion_correct'] = False # Compute ratio, set ratio to 1 if original and desired unit are equal df['Ratio'] = ((df2['orig1ratio'] / df2['new1ratio']) * (df2['new2ratio'] / df2['orig2ratio']) * (df2['new3ratio'] / df2['orig3ratio'])) df['Ratio'] = np.where( (df['Unit_orig'] == df['Unit']) | (df['Unit_orig0'] == df['Unit']), 1., df['Ratio']) return df