Electrolyte Modeling Engines

Overview

Every Solution is instantiated with an electrolyte modeling “engine”, which is a subclass of pyEQL.engine.EOS. The modeling engine performs three functions:

  1. Calculate species activity and osmotic coefficients via get_activity_coefficient and get_osmotic_coefficient, respectively

  2. Update the composition of the Solution (i.e., speciation) via equilibrate

  3. Calculate the volume occupied by the solutes via get_solute_volume

All other calculations are performed directly by the Solution class and are the same, regardless of the engine selected

The purpose of this architecture is to allow Solution to provide a consistent interface for working with electrolyte solutions, but allow the underlying models to be customized as needed to particular use cases.

pyEQL currently supports three modeling engines: ideal, native, and phreeqc, which are selected via the engine kwarg to Solution.__init__(). Each engine is briefly described below.

Warning

If you are using a Mac with an Apple M1, M2, etc. chip (i.e., Arm64 architecture), some features of pyEQL will be unavailable. Specifically, anything which depends on PHREEQC (e.g., the equilibrate method in the native engine and the entire phreeqc engine) will not work. This is because phreeqpython is currently not available for this platform. All other functions of pyEQL should work as expected.

Feel free to post your experiences or proposed solutions at https://github.com/KingsburyLab/pyEQL/issues/109

The 'native' engine (Default)

The native engine is the default choice and was the only option available prior to version 0.6.0.

Activity and osmotic coefficients

Activity coefficients are calculated using the “effetive Pitzer model” of Mistry et al. when possible. pyEQL selects parameters by identifying the predominant salt in the solution (see Salt Matching). The ionic strength is calculated based on all solutes, but only the predominant salt parameters are used in the Pitzer calculation.

If the required parameters are not available in the property database, the native engine decays gracefully through several models more appropriate for dilute solutions, including Davies, Guntelberg, and Debye-Huckel. See the module reference for full details.

Solute volumes

Solute volumes are also calculated according to the Pitzer model whenever parameters are available. Specifically, the apparent molar volume of the primary salt is calculated via Pitzer. The volumes of all other components (except the solvent, water) are added based on fixed partial molar volumes, if the data are available in the property database. If data are not available, the volume for that solute is not accounted for.

Speciation

Speciation calculations are provided by PHREEQC via phreeqpython. We use the llnl.dat PHREEQC database due to its applicability for moderate salinity water and the large number of species included (see Lu et al.). See pyEQL.equilibrium.eqiulibrate_phreeqc in the module reference for more details.

Warning

Speciation support was added to the native engine in v0.8.0 and should be considered experimental. Specifically, because the native engine uses a non-Pitzer PHREEQC database for speciation but uses the Pitzer model (when possible) for activity coefficients. As such, there may be subtle thermodynamic inconsistencies between the activities and the equilibrium concentrations returned by equilibrate().

The 'phreeqc' engine

The phreeqc engine uses phreeqpython for speciation, activity, and volume calculations. The PHREEQC engine uses the phreeqc.dat PHREEQC database by default, although it is possible to instantiate the engine with other databases such as llnl.dat, pitzer.dat, etc. See pyEQL.equilibrium.eqiulibrate_phreeqc in the module reference for more details.

Activity and osmotic coefficients

Activity coefficients are calculated by dividing the PHREEQC activity by the molal concentration of the solute.

Due to limitations in the phreeqpython interface, the osmotic coefficient is always returned as 1 at present.

Warning

The phreeqc engine currently returns an osmotic coefficient of 1 and solute volume of 0 for all solutions. There appear to be limitations in the phreeqpython interface that make it difficult to access these properties.

Solute volumes

Due to limitations in the phreeqpython interface, solute volumes are ignored (as in the ideal engine). More research is needed to determine whether this is consistent with intended PHREEQC behavior (when using the default database) or not.

Warning

The phreeqc engine currently returns an osmotic coefficient of 1 and solute volume of 0 for all solutions. There appear to be limitations in the phreeqpython interface that make it difficult to access these properties.

Speciation

Speciation calculations are provided by PHREEQC via phreeqpython.

The 'ideal' engine

The 'ideal' engine applies ideal solution behavior. Activity and osmotic coefficients are always equal to 1, solute volumes are always equal to zero, and there is no support for speciation.

Custom engines

The modeling engine system is designed to be extensible and customizable. To define a custom engine, you simply need to inherit from pyEQL.engines.EOS (or a pre-existing engine class) and then populate the abstract methods get_activity_coefficient), get_osmotic_coefficient, get_solute_volume, and equilibrate.

Equations that implement commonly used models or the above properties (such as the Debye-Huckel and Pitzer activity models, among others) are available in pyEQL.activity_correction and pyEQL.equilibrium, respectively. The idea is that end users can “compose” custom engine classes by mixing and matching the desired functions from these modules, adding custom logic as necessary.