def identify_salt(Solution): ''' Analyze the components of a solution and identify the salt that most closely approximates it. (e.g., if a solution contains 0.5 mol/kg of Na+ and Cl-, plus traces of H+ and OH-, the matched salt is 0.5 mol/kg NaCl) Create a Salt object for this salt. Returns: ------- A Salt object. ''' # sort the components by moles sort_list = _sort_components(Solution) # default to returning water as the salt cation = 'H+' anion = 'OH-' # return water if there are no solutes if len(sort_list) < 3 and sort_list[0] == 'H2O': logger.info( 'Salt matching aborted because there are not enough solutes.') return Salt(cation, anion) # warn if something other than water is the predominant component if sort_list[0] != 'H2O': logger.warning('H2O is not the most prominent component') # take the dominant cation and anion and assemble a salt from them for item in sort_list: if chem.get_formal_charge(item) > 0 and cation == 'H+': cation = item elif chem.get_formal_charge(item) < 0 and anion == 'OH-': anion = item else: pass # assemble the salt return Salt(cation, anion)
def identify_salt(Solution): ''' Analyze the components of a solution and identify the salt that most closely approximates it. (e.g., if a solution contains 0.5 mol/kg of Na+ and Cl-, plus traces of H+ and OH-, the matched salt is 0.5 mol/kg NaCl) Create a Salt object for this salt. Returns: ------- A Salt object. ''' # sort the components by moles sort_list = _sort_components(Solution) # default to returning water as the salt cation = 'H+' anion = 'OH-' # return water if there are no solutes if len(sort_list) < 3 and sort_list[0] == 'H2O': logger.info('Salt matching aborted because there are not enough solutes.') return Salt(cation,anion) # warn if something other than water is the predominant component if sort_list[0] != 'H2O': logger.warning('H2O is not the most prominent component') # take the dominant cation and anion and assemble a salt from them for item in sort_list: if chem.get_formal_charge(item) > 0 and cation == 'H+': cation = item elif chem.get_formal_charge(item) < 0 and anion == 'OH-': anion = item else: pass # assemble the salt return Salt(cation,anion)
def __init__(self, formula, amount, volume, solvent_mass, parameters={}): """ Parameters ---------- formula : str Chemical formula for the solute. Charged species must contain a + or - and (for polyvalent solutes) a number representing the net charge (e.g. 'SO4-2'). amount : str The amount of substance in the specified unit system. The string should contain both a quantity and a pint-compatible representation of a unit. e.g. '5 mol/kg' or '0.1 g/L' volume : pint Quantity The volume of the solution solvent_mass : pint Quantity The mass of solvent in the parent solution. parameters : dictionary, optional Dictionary of custom parameters, such as diffusion coefficients, transport numbers, etc. Specify parameters as key:value pairs separated by commas within curly braces, e.g. {diffusion_coeff:5e-10,transport_number:0.8}. The 'key' is the name that will be used to access the parameter, the value is its value. """ # import the chemical formula interpreter module import pyEQL.chemical_formula as chem # check that 'formula' is a valid chemical formula if not chem.is_valid_formula: logger.error("Invalid chemical formula specified.") return None else: self.formula = formula # set molecular weight self.mw = chem.get_molecular_weight(formula) * unit("g/mol") # set formal charge self.charge = chem.get_formal_charge(formula) # translate the 'amount' string into a pint Quantity quantity = unit(amount) self.moles = quantity.to("moles", "chem", mw=self.mw, volume=volume, solvent_mass=solvent_mass) # trigger the function that checks whether parameters already exist for this species, and if not, # searches the database files and creates them db.search_parameters(self.formula)
def __init__(self,formula,amount,volume,solvent_mass,parameters={}): ''' Parameters ---------- formula : str Chemical formula for the solute. Charged species must contain a + or - and (for polyvalent solutes) a number representing the net charge (e.g. 'SO4-2'). amount : str The amount of substance in the specified unit system. The string should contain both a quantity and a pint-compatible representation of a unit. e.g. '5 mol/kg' or '0.1 g/L' volume : pint Quantity The volume of the solution solvent_mass : pint Quantity The mass of solvent in the parent solution. parameters : dictionary, optional Dictionary of custom parameters, such as diffusion coefficients, transport numbers, etc. Specify parameters as key:value pairs separated by commas within curly braces, e.g. {diffusion_coeff:5e-10,transport_number:0.8}. The 'key' is the name that will be used to access the parameter, the value is its value. ''' # import the chemical formula interpreter module import pyEQL.chemical_formula as chem # check that 'formula' is a valid chemical formula if not chem.is_valid_formula: logger.error('Invalid chemical formula specified.') return None else: self.formula = formula # set molecular weight self.mw = chem.get_molecular_weight(formula) * unit('g/mol') # set formal charge self.charge = chem.get_formal_charge(formula) # translate the 'amount' string into a pint Quantity quantity = unit(amount) self.moles = quantity.to('moles','chem',mw=self.mw,volume=volume,solvent_mass=solvent_mass) # trigger the function that checks whether parameters already exist for this species, and if not, # searches the database files and creates them db.search_parameters(self.formula)
def _trim_formal_charge(formula): ''' remove the formal charge from a chemical formula Examples: -------- >>> _trim_formal_charge('Fe+++') 'Fe' >>> _trim_formal_charge('SO4-2') 'SO4' >>> _trim_formal_charge('Na+') 'Na' ''' charge = chem.get_formal_charge(formula) output = '' if charge > 0: output = formula.split('+')[0] elif charge < 0: output = formula.split('-')[0] return output
def _trim_formal_charge(formula): """ remove the formal charge from a chemical formula Examples: -------- >>> _trim_formal_charge('Fe+++') 'Fe' >>> _trim_formal_charge('SO4-2') 'SO4' >>> _trim_formal_charge('Na+') 'Na' """ charge = chem.get_formal_charge(formula) output = "" if charge > 0: output = formula.split("+")[0] elif charge < 0: output = formula.split("-")[0] return output
def __init__(self,cation,anion): self.cation=cation self.anion=anion ''' Create a salt object based on its component ions Parameters: ---------- cation, anion : str Chemical formula of the cation and anion, respectively Returns: ------- Salt : An object representing the properties of the salt Examples: -------- >>> Salt('Na+','Cl-').formula 'NaCl' >>> Salt('Mg++','Cl-').formula 'MgCl2' ''' # get the charges on cation and anion self.z_cation = chem.get_formal_charge(cation) self.z_anion = chem.get_formal_charge(anion) # assign stoichiometric coefficients by finding a common multiple self.nu_cation = abs(self.z_anion) self.nu_anion = abs(self.z_cation) # if both coefficients are the same, set each to one if self.nu_cation == self.nu_anion: self.nu_cation = 1 self.nu_anion = 1 # start building the formula, cation first salt_formula='' if self.nu_cation > 1: # add parentheses if the cation is a polyatomic ion if len(chem.get_elements(cation)) > 1: salt_formula+='(' salt_formula+= _trim_formal_charge(cation) salt_formula+=')' else: salt_formula+= _trim_formal_charge(cation) salt_formula+=str(self.nu_cation) else: salt_formula+= _trim_formal_charge(cation) if self.nu_anion > 1: # add parentheses if the anion is a polyatomic ion if len(chem.get_elements(anion)) > 1: salt_formula+='(' salt_formula+= _trim_formal_charge(anion) salt_formula+=')' else: salt_formula+= _trim_formal_charge(anion) salt_formula+=str(self.nu_anion) else: salt_formula+= _trim_formal_charge(anion) self.formula = salt_formula
def generate_salt_list(Solution,unit='mol/kg'): ''' Generate a list of salts that represents the ionic composition of a solution. Returns: ------- dict A dictionary of Salt objects, keyed to the formula of the salt. ''' salt_list={} # sort the cations and anions by moles cation_list = _sort_components(Solution,type='cations') anion_list = _sort_components(Solution,type='anions') # iterate through the lists of ions # create salts by matching the equivalent concentrations of cations # and anions along the way len_cat = len(cation_list) len_an = len(anion_list) # start with the first cation and anion index_cat=0 index_an = 0 # calculate the equivalent concentrations of each ion c1 = Solution.get_amount(cation_list[index_cat],unit) * chem.get_formal_charge(cation_list[index_cat]) a1 = Solution.get_amount(anion_list[index_an],unit) * abs(chem.get_formal_charge(anion_list[index_an])) while index_cat < len_cat and index_an < len_an: # if the cation concentration is greater, there will be leftover cations if c1 > a1: # create the salt x = Salt(cation_list[index_cat],anion_list[index_an]) # there will be leftover cation, so use the anion amount amount = a1 / abs(x.z_anion) # add it to the list salt_list.update({x:amount}) # adjust the amounts of the respective ions c1 = c1 - a1 # move to the next anion index_an += 1 try: a1 = Solution.get_amount(anion_list[index_an],unit) * abs(chem.get_formal_charge(anion_list[index_an])) except IndexError: continue # if the anion concentration is greater, there will be leftover anions if c1 < a1: # create the salt x = Salt(cation_list[index_cat],anion_list[index_an]) # there will be leftover anion, so use the cation amount amount = c1 / x.z_cation # add it to the list salt_list.update({x:amount}) # calculate the leftover cation amount a1 = a1 - c1 # move to the next cation index_cat += 1 try: c1 = Solution.get_amount(cation_list[index_cat],unit) * chem.get_formal_charge(cation_list[index_cat]) except IndexError: continue if c1 == a1: # create the salt x = Salt(cation_list[index_cat],anion_list[index_an]) # there will be nothing leftover, so it doesn't matter which ion you use amount = c1 / x.z_cation # add it to the list salt_list.update({x:amount}) # move to the next cation and anion index_an += 1 index_cat += 1 try: c1 = Solution.get_amount(cation_list[index_cat],unit) * chem.get_formal_charge(cation_list[index_cat]) a1 = Solution.get_amount(anion_list[index_an],unit) * abs(chem.get_formal_charge(anion_list[index_an])) except IndexError: continue return salt_list
def generate_salt_list(Solution, unit='mol/kg'): ''' Generate a list of salts that represents the ionic composition of a solution. Returns: ------- dict A dictionary of Salt objects, keyed to the formula of the salt. ''' salt_list = {} # sort the cations and anions by moles cation_list = _sort_components(Solution, type='cations') anion_list = _sort_components(Solution, type='anions') # iterate through the lists of ions # create salts by matching the equivalent concentrations of cations # and anions along the way len_cat = len(cation_list) len_an = len(anion_list) # start with the first cation and anion index_cat = 0 index_an = 0 # calculate the equivalent concentrations of each ion c1 = Solution.get_amount(cation_list[index_cat], unit) * chem.get_formal_charge( cation_list[index_cat]) a1 = Solution.get_amount(anion_list[index_an], unit) * abs( chem.get_formal_charge(anion_list[index_an])) while index_cat < len_cat and index_an < len_an: # if the cation concentration is greater, there will be leftover cations if c1 > a1: # create the salt x = Salt(cation_list[index_cat], anion_list[index_an]) # there will be leftover cation, so use the anion amount amount = a1 / abs(x.z_anion) # add it to the list salt_list.update({x: amount}) # adjust the amounts of the respective ions c1 = c1 - a1 # move to the next anion index_an += 1 try: a1 = Solution.get_amount(anion_list[index_an], unit) * abs( chem.get_formal_charge(anion_list[index_an])) except IndexError: continue # if the anion concentration is greater, there will be leftover anions if c1 < a1: # create the salt x = Salt(cation_list[index_cat], anion_list[index_an]) # there will be leftover anion, so use the cation amount amount = c1 / x.z_cation # add it to the list salt_list.update({x: amount}) # calculate the leftover cation amount a1 = a1 - c1 # move to the next cation index_cat += 1 try: c1 = Solution.get_amount(cation_list[index_cat], unit) * chem.get_formal_charge( cation_list[index_cat]) except IndexError: continue if c1 == a1: # create the salt x = Salt(cation_list[index_cat], anion_list[index_an]) # there will be nothing leftover, so it doesn't matter which ion you use amount = c1 / x.z_cation # add it to the list salt_list.update({x: amount}) # move to the next cation and anion index_an += 1 index_cat += 1 try: c1 = Solution.get_amount(cation_list[index_cat], unit) * chem.get_formal_charge( cation_list[index_cat]) a1 = Solution.get_amount(anion_list[index_an], unit) * abs( chem.get_formal_charge(anion_list[index_an])) except IndexError: continue return salt_list