Coefficient libraries

The casual user has no need to explicitly call this module.

.cflibs provides specific combinations of coefficients that have been used in published Pitzer models, to use with Pytzer.

To use a Pitzer model we need to define a set of coefficients that quantify the interactions between different combinations of ions. We do this by creating a coefficient library, which contains functions that evaluate the coefficients for every possible interaction. The functions themselves are defined separately.

A number of pre-defined coefficient libraries are included in pytzer.cflibs. To use these, all you need to do is assign the variable cflib appropriately:

>>> import pytzer as pz
>>> cflib = pz.cflibs.M88 # Use M88 coefficients

This cflib can then be passed into all of the Pitzer model functions.


Pre-defined coefficient libraries

Several ready-to-use coefficient libraries are available in this module. To decode their sources, see the literature references table.

cflib name System Source
CRP94 $\ce{H^+}$, $\ce{HSO4^-}$, $\ce{SO4^2-}$ CRP94
GM89 $\ce{Ca^2+}$, $\ce{Cl^-}$, $\ce{K^+}$, $\ce{Na^+}$, $\ce{SO4^2-}$ GM89
M88 $\ce{Ca^2+}$, $\ce{Cl^-}$, $\ce{Na^+}$, $\ce{SO4^2-}$ M88
WM13 $\ce{Ca^2+}$, $\ce{Cl-}$, $\ce{H+}$, $\ce{HSO4-}$, $\ce{K+}$, $\ce{Mg^2+}$, $\ce{MgOH+}$, $\ce{Na+}$, $\ce{OH-}$, $\ce{SO4^2-}$ WM13

cflib methods

A few handy methods are provided as part of the CoefficientLibrary class. Brief summaries are provided below, and here is a usage example of all of them together:

import pytzer as pz
import numpy as np
from copy import deepcopy

# Copy a pre-defined cflib
cflib = deepcopy(pz.cflibs.M88)

# Get ions within it
cflib.get_contents()

# Add a new ion into the mix
cflib.ions = np.append(cflib.ions, 'K')

# Add zero-functions for all interactions with the new ion
cflib.add_zeros(cflib.ions)

# Update cflib name to show we've changed it
cflib.name = 'M88-modified'

# Print out the coefficients evaluated at 298.15 K
cflib.print_coefficients(298.15, 'coeff_file.txt')

The methods are as follows:

.add_zeros - add entries for missing interactions

CoefficientLibrary.add_zeros(ions) adds zero-functions for all missing interactions, given a list of ions.

.get_contents - create lists of ions and sources

CoefficientLibrary.get_contents() scans through all functions within the CoefficientLibrary, and puts lists of all ions and of all sources in its ions and srcs fields.

The list of ions is determined from the dict keys, while sources are determined from the function names.

CoefficientLibrary.print_coefficients(T,filename) evaluates all coefficients in a cflib at a single input temperature and pressure, and prints the results to a text file (filename).


How a CoefficientLibrary works

To modify an existing CoefficientLibrary, or create a new one, it is first necessary to understand how they are used within Pytzer, as follows. A basic understanding of the workings of the Pitzer model is assumed.

A CoefficientLibrary or cflib is an object of the class CoefficientLibrary. From the initalisation function we can see that it contains the following fields:

class CoefficientLibrary:

    # Initialise
    def __init__(self):
        self.name  = ''
        self.dh    = {} # Aosm
        self.bC    = {} # c-a
        self.theta = {} # c-c' and a-a'
        self.jfunc = [] # unsymmetrical mixing
        self.psi   = {} # c-c'-a and c-a-a'
        self.lambd = {} # n-c and n-a
        self.zeta  = {} # n-c-a
        self.mu    = {} # n-n-n
        self.ions  = []
        self.srcs  = []

Each field is then filled with functions from modules debyehueckel, coefficients or jfuncs that define the Pitzer model interaction coefficients, as follows. (Descriptions of the required contents of the functions themselves are in the separate coefficients documentation.)

Debye-Hückel limiting slope

The function for the Debye-Hückel limiting slope (i.e. Aϕ) is stored as CoefficientLibrary.dh['Aosm'].

Cation-anion interactions

Functions to evaluate the $\beta$ and $C$ coefficients for interactions between cations and anions are contained within the cflib.bC dict. The function for each specific interaction gets its own field within the dict. The fields are named as <cation>-<anion>, with the ionic names matching those described for an input file - see the page on naming conventions for a full description. Some examples:

cflib.bC['Na-Cl'] = <Na-Cl interaction coefficients function>
cflib.bC['Mg-Cl'] = <Mg-Cl interaction coefficients function>
cflib.bC['K-SO4'] = <K-SO4 interaction coefficients function>

Cation-cation and anion-anion interactions

Functions that evaluate the $\theta$ coefficients for interactions between ion pairs with a common charge sign are contained within the cflib.theta dict. The function for each specific interaction gets its own field within the dict. The fields are named as <cation0>-<cation1>, with the cations in alphabetical order, and with the ionic names matching those described for an input file. Some examples:

cflib.theta['Ca-Mg']  = <Ca-Mg interaction coefficients function>
cflib.theta['Mg-Na']  = <Mg-Na interaction coefficients function>
cflib.theta['Cl-SO4'] = <Cl-SO4 interaction coefficients function>

Triplet interactions

Functions that evaluate the $\psi$ coefficients for interactions between ion pairs with a common charge sign and a third ion of opposite sign are contained within the cflib.psi dict. The function for each specific triplet interaction gets its own field within the dict. The fields are named as <ion0>-<ion1>-<ion2>, with the order of the ions obeying the following rules, given here in order of precedence:

  1. Cations before anions;

  2. In alphabetical order.

The ionic names should match those described for an input file.

Some examples:

cflib.psi['Ca-Mg-Cl']  = <Ca-Mg-Cl interaction coefficients function>
cflib.psi['Mg-Na-SO4'] = <Mg-Na-SO4 interaction coefficients function>
cflib.psi['Na-Cl-SO4'] = <Na-Cl-SO4 interaction coefficients function>

Neutral interactions

Functions that evaluate the $\lambda$, $\zeta$ and $\mu$ coefficients for the interactions between a neutral solute and an ion or other neutral ($\lambda$), the three-way between a neutral, cation and anion ($\zeta$) and the three-way between three neutrals of the same kind ($\mu$) are contained within cflib.lambd, cflib.eta and cflib.mu respectively.

The field names obey the rules, in order of precedence:

  1. Neutrals first, then cations, then anions;

  2. In alphabetical order.

Assigning functions is exactly the same as described for the other interaction types.

Unsymmetrical mixing terms

A function to evaluate the J and J' equations are contained in cflib.jfunc. Unlike the other fields within the cflib, only one function is provided, so this field directly contains the relevant function, rather than storing it in a dict.

Different options for the functions needed here can be found in pytzer.jfuncs.


Modify an existing cflib

The functions within an existing cflib can easily be switched by reassignment. For example, if you wanted to use the Møller (1988) model, but replace only the Na-Cl interaction equations with the model of Archer (1992), you could write:

import pytzer as pz

# Get Møller (1988) cflib
cflib = pz.cflibs.M88

# Update Na-Cl interaction function to Archer (1992)
cflib.bC['Na-Cl'] = pz.coefficients.bC_Na_Cl_A92ii

Note that the statement to get the cflib (cflib = pz.cflibs.M88) only references, not copies, from pytzer.cflibs. To copy, and make changes without modifying the original, use:

import pytzer as pz
from copy import deepcopy

# Get Møller (1988) cflib
cflib = deepcopy(pz.cflibs.M88)
cflib.name = 'M88-modified' # so we know it's been changed

# Update Na-Cl interaction function to Archer (1992)
cflib.bC['Na-Cl'] = pz.coefficients.bC_Na_Cl_A92ii

Build your own

You can also construct your own cflib from scratch. In the example below, we initialise a cflib using the pytzer.cflibs.CoefficientLibrary class. We add functions from pytzer.coefficients for the system Na-Ca-Cl using functions from Møller (1988). Finally, we use the method add_zeros to fill out any interactions that we have neglected to provide functions for with zeros.

import pytzer as pz
import numpy as np

# Initialise
mycflib = pz.cflibs.CoefficientLibrary()

# Debye-Hueckel limiting slope
mycflib.dh['Aosm'] = coefficients.Aosm_M88

# Cation-anion interactions (betas and Cs)
mycflib.bC['Ca-Cl' ] = coefficients.bC_Ca_Cl_M88
mycflib.bC['Na-Cl' ] = coefficients.bC_Na_Cl_M88

# Cation-cation and anion-anion interactions (theta)
# c-c'
mycflib.theta['Ca-Na' ] = coefficients.theta_Ca_Na_M88

# Unsymmetrical mixing functions
mycflib.jfunc = jfuncs.Harvie

# Triplet interactions (psi)
# c-c'-a
mycflib.psi['Ca-Na-Cl' ] = coefficients.psi_Ca_Na_Cl_M88

# Fill missing functions with zeros (none in this instance)
mycflib.add_zeros(np.array(['Na','Ca','Cl']))

To explicitly assign zeros to any interaction (i.e. the interaction is ignored by the model), you can use the appropriate zero-functions from pytzer.coefficients:

mycflib.bC['Ba-SO4']   = coefficients.bC_zero    # ignore Ba-SO4 interactions
mycflib.bC['H-Na']     = coefficients.theta_zero # ignore H-Na interactions
mycflib.psi['H-Mg-OH'] = coefficients.psi_zero   # ignore H-Mg-OH interactions

You can use the function CoefficientLibrary.print_coefficients to create a file containing every coefficient, evaluated at a single input temperature and pressure of your choice. For example:

mycflib.print_coefficients(298.15,'mycoefficients.txt')

would evaluate every coefficient at 298.15 K and print the results to the file mycoefficients.txt.