Example #1
0
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)
Example #2
0
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)
Example #3
0
File: solute.py Project: qize/pyEQL
    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)
Example #4
0
    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)
Example #5
0
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
Example #6
0
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
Example #7
0
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
Example #8
0
 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
Example #9
0
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
Example #10
0
 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
Example #11
0
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