コード例 #1
0
    def test_effective_pitzer_mgcl2_activity(self):
        # test the activity coefficient of MgCl2
        # corresponds to 0.515m, 1.03m, 2.58m, and 4.1m
        multiple = [1, 2, 5, 8]
        expected = [0.5, 0.5, 0.67, 1.15]

        # import the parameters database
        from pyEQL import paramsDB as db

        for item in range(len(multiple)):
            s1 = self.mock_seawater(multiple[item])
            Salt = pyEQL.salt_ion_match.Salt('Mg+2', 'Cl-')
            db.search_parameters(Salt.formula)
            param = db.get_parameter(Salt.formula,
                                     'pitzer_parameters_activity')
            alpha1 = 2
            alpha2 = 0
            molality = Salt.get_effective_molality(s1.get_ionic_strength())
            temperature = str(s1.get_temperature())

            activity_coefficient=pyEQL.activity_correction.get_activity_coefficient_pitzer(s1.get_ionic_strength(), \
            molality,alpha1,alpha2,param.get_value()[0],param.get_value()[1],param.get_value()[2],param.get_value()[3], \
            Salt.z_cation,Salt.z_anion,Salt.nu_cation,Salt.nu_anion,temperature)

            # convert the result to a rational activity coefficient
            result = activity_coefficient * (
                1 + pyEQL.unit('0.018 kg/mol') * s1.get_total_moles_solute() /
                s1.get_solvent_mass())
            #print(result,expected[item])
            self.assertWithinExperimentalError(result, expected[item],
                                               self.tol)
コード例 #2
0
 def test_effective_pitzer_mgcl2_activity(self):
     # test the activity coefficient of MgCl2
     # corresponds to 0.515m, 1.03m, 2.58m, and 4.1m
     multiple = [1,2,5,8]
     expected=[0.5,0.5,0.67,1.15]
     
     # import the parameters database
     from pyEQL import paramsDB as db
         
     for item in range(len(multiple)):
         s1 = self.mock_seawater(multiple[item])
         Salt = pyEQL.salt_ion_match.Salt('Mg+2','Cl-')
         db.search_parameters(Salt.formula)
         param = db.get_parameter(Salt.formula,'pitzer_parameters_activity')
         alpha1 = 2
         alpha2 = 0
         molality = Salt.get_effective_molality(s1.get_ionic_strength())
         temperature = str(s1.get_temperature())
         
         activity_coefficient=pyEQL.activity_correction.get_activity_coefficient_pitzer(s1.get_ionic_strength(), \
         molality,alpha1,alpha2,param.get_value()[0],param.get_value()[1],param.get_value()[2],param.get_value()[3], \
         Salt.z_cation,Salt.z_anion,Salt.nu_cation,Salt.nu_anion,temperature)
         
         # convert the result to a rational activity coefficient
         result = activity_coefficient * (1+pyEQL.unit('0.018 kg/mol')*s1.get_total_moles_solute()/s1.get_solvent_mass())
         #print(result,expected[item])
         self.assertWithinExperimentalError(result,expected[item],self.tol) 
コード例 #3
0
 def get_parameter(self,parameter,temperature=None,pressure=None,ionic_strength=None):
     '''
     Return the value of the parameter named 'parameter'
     
     '''
     # Search for this solute in the database of parameters
     
     # TODO - handle the case where multiple parameters with same name exist. Need function
     # to compare specified conditions and choose the most appropriate parameter
     param = db.get_parameter(self.formula,parameter)
     
     return param.get_value(temperature,pressure,ionic_strength)
コード例 #4
0
    def get_parameter(self,
                      parameter,
                      temperature=None,
                      pressure=None,
                      ionic_strength=None):
        '''
        Return the value of the parameter named 'parameter'
        
        '''
        # Search for this solute in the database of parameters

        # TODO - handle the case where multiple parameters with same name exist. Need function
        # to compare specified conditions and choose the most appropriate parameter
        param = db.get_parameter(self.formula, parameter)

        return param.get_value(temperature, pressure, ionic_strength)
コード例 #5
0
def donnan_eql(solution,fixed_charge):
    '''
    Return a solution object in equilibrium with fixed_charge
    
    Parameters
    ----------
    Solution : Solution object
        The external solution to be brought into equilibrium with the fixed
        charges
    fixed_charge : str quantity
        String representing the concentration of fixed charges, including sign. 
        May be specified in mol/L or mol/kg units. e.g. '1 mol/kg'
        
    Returns
    -------
    Solution
        A solution that has established Donnan equilibrium with the external
        (input) Solution
    
    Notes
    -----
    
    The general equation representing the equilibrium between an external 
    electrolyte solution and an ion-exchange medium containing fixed charges
    is:[#]_
    
    .. math:: {a_- \\over \\bar a_-}^{1 \\over z_-} {\\bar a_+ \\over a_+}^{1 \\over z_+} \
    = exp({\\Delta \\pi \\bar V \\over {RT z_+ \\nu_+}})
    
    Where subscripts :math:`+` and :math:`-` indicate the cation and anion, respectively, 
    the overbar indicates the membrane phase,
    :math:`a` represents activity, :math:`z` represents charge, :math:`\\nu` represents the stoichiometric
    coefficient, :math:`V` represents the partial molar volume of the salt, and 
    :math:`\\Delta \\pi` is the difference in osmotic pressure between the membrane and the
    solution phase.
    
    In addition, electroneutrality must prevail within the membrane phase:
    
    .. math:: \\bar C_+ z_+ + \\bar X + \\bar C_- z_- = 0
    
    Where :math:`C` represents concentration and :math:`X` is the fixed charge concentration
    in the membrane or ion exchange phase.
    
    This function solves these two equations simultaneously to arrive at the 
    concentrations of the cation and anion in the membrane phase. It returns
    a solution equal to the input solution except that the concentrations of
    the predominant cation and anion have been adjusted according to this 
    equilibrium.
    
    NOTE that this treatment is only capable of equilibrating a single salt.
    This salt is identified by the get_salt() method.
    
    References
    ----------
    
    .. [#] Strathmann, Heiner, ed. *Membrane Science and Technology* vol. 9, 2004. \
           Chapter 2, p. 51. http://dx.doi.org/10.1016/S0927-5193(04)80033-0

    
    Examples
    --------
    TODO
    
    See Also
    --------
    get_salt()
    
    '''
    # identify the salt
    salt = solution.get_salt()
    
    # convert fixed_charge in to a quantity
    fixed_charge = unit(fixed_charge)
    
    # identify variables from the external solution
    conc_cation_soln = solution.get_amount(salt.cation,str(fixed_charge.units))
    conc_anion_soln = solution.get_amount(salt.anion,str(fixed_charge.units))
    act_cation_soln = solution.get_activity(salt.cation)
    act_anion_soln = solution.get_activity(salt.anion)
    z_cation= salt.z_cation
    z_anion = salt.z_anion
    nu_cation = salt.nu_cation
    
    # get the partial molar volume for the salt, or calculate it from the ions
    # TODO - consider how to incorporate pitzer parameters
    if db.has_parameter(salt.formula,'partial_molar_volume'):
            item = db.get_parameter(salt.formula,'partial_molar_volume')  
            molar_volume = item.get_value()
    elif db.has_parameter(salt.cation,'partial_molar_volume') and db.has_parameter(salt.anion,'partial_molar_volume'):
        cation_vol = solution.get_solute(salt.cation).get_parameter('partial_molar_volume')
        anion_vol = solution.get_solute(salt.anion).get_parameter('partial_molar_volume')
        molar_volume = cation_vol + anion_vol
    else:
        logger.error('Required partial molar volume information not available. Aborting.')
        return None
    
    # initialize the equilibrated solution - start with a direct copy of the 
    # input / external solution
    donnan_soln = solution.copy()
    
    # do nothing if either of the ion concentrations is zero
    if conc_cation_soln.magnitude == 0 or conc_anion_soln.magnitude == 0:
        return donnan_soln
  
    # define a function representing the donnan equilibrium as a function
    # of the two unknown actvities to feed to the nonlinear solver
    
    # the stuff in the term below doesn't change on iteration, so calculate it up-front
    # assign it the correct units and extract the magnitude for a performance gain
    exp_term =  (molar_volume / (unit.R * solution.get_temperature() * z_cation * nu_cation)).to('1/Pa').magnitude
    
    def donnan_solve(x):
        '''Where x is the magnitude of co-ion concentration
        '''
        # solve for the counter-ion concentration by enforcing electroneutrality
        # using only floats / ints here instead of quantities helps performance
        if fixed_charge.magnitude >= 0:
            # counter-ion is the anion
            conc_cation_mem = x / abs(z_cation)
            conc_anion_mem = -(conc_cation_mem * z_cation + fixed_charge.magnitude) / z_anion
        elif fixed_charge.magnitude < 0:
            # counter-ion is the cation
            conc_anion_mem = x / abs(z_anion) 
            conc_cation_mem = -(conc_anion_mem * z_anion + fixed_charge.magnitude) / z_cation
        
        # match the units given for fixed_charge
        units = str(fixed_charge.units)
        
        # set the cation and anion concentrations in the membrane phase equal
        # to the current guess
        donnan_soln.set_amount(salt.cation,str(conc_cation_mem)+units)
        donnan_soln.set_amount(salt.anion,str(conc_anion_mem)+units)

        # get the new concentrations and activities
        act_cation_mem = donnan_soln.get_activity(salt.cation)
        act_anion_mem = donnan_soln.get_activity(salt.anion)
        
        # compute the difference in osmotic pressure
        # using the magnitudes here helps performance
        delta_pi = donnan_soln.get_osmotic_pressure().magnitude - solution.get_osmotic_pressure().magnitude
        
        return (act_cation_mem/act_cation_soln) ** (1/z_cation) * (act_anion_soln/act_anion_mem)**(1/z_anion) - math.exp(delta_pi * exp_term)

    # solve the function above using one of scipy's nonlinear solvers

    from scipy.optimize import brentq
    
    # determine which ion concentration represents the co-ion
    # call a nonlinear solver to adjust the concentrations per the donnan
    # equilibrium, unless the membrane is uncharged
    # the initial guess is to set the co-ion concentration in the membrane
    # equal to that in the solution
    if fixed_charge.magnitude >0:
        x = conc_cation_soln.magnitude
        brentq(donnan_solve,1e-10,x,xtol=0.001)
    elif fixed_charge.magnitude <0:
        x = conc_anion_soln.magnitude
        brentq(donnan_solve,1e-10,x,xtol=0.001)
    else:
        pass

    # return the equilibrated solution
    return donnan_soln