Overview
The electrolyte CPA (Cubic Plus Association) model in NeqSim extends the standard CPA equation of state to handle aqueous electrolyte solutions. The model is based on the work of Solbraa (2002) and combines:
- CPA equation of state for non-electrolyte interactions (van der Waals + association)
- Fürst electrostatic contribution for ion-ion and ion-solvent interactions
- Short-range Wij parameters for specific ion-solvent and ion-ion correlations
Implementation Classes
SystemElectrolyteCPAstatoil (Recommended)
The Statoil (now Equinor) implementation of the electrolyte CPA model:
import neqsim.thermo.system.SystemElectrolyteCPAstatoil;
SystemElectrolyteCPAstatoil system = new SystemElectrolyteCPAstatoil(298.15, 1.01325);
system.addComponent("water", 55.5);
system.addComponent("Na+", 1.0);
system.addComponent("Cl-", 1.0);
system.chemicalReactionInit();
system.createDatabase(true);
system.setMixingRule(10); // Required: CPA mixing rule with temperature/composition dependency
Key Features of the Statoil Implementation
| Feature | Description |
|---|---|
| Model Name | Electrolyte-CPA-EOS-statoil |
| Base Class | Extends SystemFurstElectrolyteEos |
| Phase Class | PhaseElectrolyteCPAstatoil |
| Component Class | ComponentElectrolyteCPAstatoil |
| Attractive Term | Term 15 (Mathias-Copeman alpha function) |
| Volume Correction | Enabled by default |
| Fürst Parameters | Uses electrolyteCPA parameter set |
Ionic covolume selection and compatibility
CPA ionic components read covolume coefficients [0] and [1] directly from
FurstElectrolyteConstants.furstParamsCPA, including construction and
initFurstParam() reinitialization. This also applies to the Statoil and Advanced
subclasses. ScRK components retain their separate furstParams defaults.
Constructing an electrolyte CPA system no longer reassigns those shared ScRK
defaults. Interleaving systems, adding ions later, cloning and serialization must
therefore preserve each model’s covolume identity.
With the default tables, Na+ covolume is approximately 2.58461732544 for ScRK
and 3.87214760778 for CPA in NeqSim internal units. Earlier versions could give
the CPA value to ScRK solely because a CPA system had been constructed first.
Removing that construction-order dependence can change affected ScRK results; it
is not a new parameter fit or an estimate of fluid-property error.
For deliberate customization, use setFurstParamCPA(index, value) for the CPA
table and setFurstParam(index, value) for the ScRK table, then reinitialize the
affected components/phases. These remain process-wide mutable customization APIs,
not per-system or thread-safe parameter stores. The legacy
setFurstParams("electrolyteCPA") explicitly aliases the ScRK table to CPA and
should not be used to select a system model. Code that relied on a CPA constructor
making setFurstParam target CPA must migrate to setFurstParamCPA.
The covolume correction is independent of the reference-interaction correction described below. Neither changes fitted database pairs.
Class Hierarchy
SystemThermo
└── SystemSrkEos
└── SystemFurstElectrolyteEos
└── SystemElectrolyteCPAstatoil
├── PhaseElectrolyteCPAstatoil (phase calculations)
└── ComponentElectrolyteCPAstatoil (component properties)
Mixing Rule 10 - CPA with Temperature/Composition Dependency
Overview
Mixing rule 10 is the recommended mixing rule for all CPA and electrolyte CPA systems. It automatically selects the appropriate sub-type based on the binary interaction parameters:
system.setMixingRule(10); // Automatically selects optimal sub-type
Automatic Sub-Type Selection
The mixing rule analyzes the binary interaction parameter matrices and selects:
| Condition | Sub-Type | Class | Description |
|---|---|---|---|
| Symmetric kij, no T-dependency | classic-CPA |
ClassicSRK |
Simple symmetric mixing |
| Symmetric kij, with T-dependency | classic-CPA_T |
ClassicSRKT2 |
Temperature-dependent symmetric |
| Asymmetric kij (kij ≠ kji) | classic-CPA_Tx |
ClassicSRKT2x |
Full asymmetric + T-dependent |
Mathematical Formulation
The a parameter mixing rule:
For asymmetric mixing (ClassicSRKT2x):
\[k_{ij} \neq k_{ji}\]Temperature dependency:
\[k_{ij}(T) = k_{ij,0} + k_{ij,T} \cdot T\]Why Mixing Rule 10?
- Automatic optimization: Selects the simplest sufficient mixing rule
- Temperature dependency: Captures T-dependent phase behavior
- Asymmetric parameters: Handles non-symmetric ion-solvent interactions
- Database integration: Uses binary parameters from NeqSim database
Theoretical Background
Helmholtz Energy Decomposition
The total residual Helmholtz energy is decomposed as:
\[A^{res} = A^{CPA} + A^{elec}\]Where:
- $A^{CPA}$ = Standard CPA contribution (SRK + association)
- $A^{elec}$ = Electrostatic contribution (Fürst model)
Fürst Electrostatic Model
The electrostatic contribution follows the Fürst model, which combines:
- Mean Spherical Approximation (MSA) for ion-ion interactions
- Born solvation term for ion-solvent interactions
- Short-range Wij terms for specific ion interactions
Mean Spherical Approximation (MSA)
The MSA term accounts for the electrostatic screening between ions:
\[\frac{A^{MSA}}{RT} = -\frac{V}{3\pi} \left[ \Gamma^3 + \frac{3\Gamma\sigma_+ \sigma_-}{1 + \Gamma\sigma_{+-}} \right]\]Where:
- $\Gamma$ = MSA screening parameter
- $\sigma_i$ = ionic diameter of species $i$
Born Solvation Term
The Born term accounts for the solvation energy of ions in the dielectric medium:
\[\frac{A^{Born}}{RT} = -\frac{e^2 N_A}{4\pi\varepsilon_0 RT} \sum_i n_i \frac{z_i^2}{\sigma_i} \left(1 - \frac{1}{\varepsilon_r}\right)\]Where:
- $z_i$ = ionic charge
- $\sigma_i$ = ionic diameter
- $\varepsilon_r$ = relative permittivity (dielectric constant) of the solvent mixture
Here $n_i$ is in mol and $\sigma_i$ is the diameter in metres used by the Furst-based implementation. The equivalent radius convention uses $8\pi\varepsilon_0 RT$ and $r_i=\sigma_i/2$. Do not combine a diameter with the radius prefactor, or use $N_A/k_B$ in place of $N_A/R$ for this molar expression.
The mole derivative includes both the ionic contribution and the derivative of solvent permittivity. The latter must scale inversely with phase amount so that the chemical potential stays intensive. See Born derivatives and phase-size invariance for the chain rule, the legacy Mod2004 correction, the related EOS audit, and the literature-based development recommendations.
Short-Range Interaction Parameters (Wij)
Overview
The short-range Wij parameters capture specific ion-solvent and ion-ion interactions not described by the electrostatic terms. These are fitted to experimental activity coefficient and osmotic coefficient data.
Parameter Correlations
The reference coefficient wij[0] for calculated cation-water and ordinary
cation-anion pairs follows the phase’s EOS family. SystemFurstElectrolyteEos
and SystemFurstElectrolyteEosMod2004 use the existing ScRK furstParams table;
the three electrolyte CPA variants use furstParamsCPA. Selection follows the
phase type, including CPA subclasses, and is independent of construction order.
Database pairs marked fitted (CalcWij != 0) retain their reference and
temperature coefficients. The named-solvent tables, the Piperazine approximation,
the MDEA+ ion-ion override, and gas-ion/organic-inhibitor overrides are unchanged.
For ScRK, the existing six-coefficient table gives the same correlation for monovalent and divalent cations, with diameters in angstroms:
Wij(cation-water, 298.15 K) = 6.99219e-5 * stokesDiameter + 4.3984e-6
Wij(cation-anion, 298.15 K) = -6.06e-8 * (d_cat + d_an)^4 - 2.1795e-5
These are the current ScRK table values, not a new fit. The separate ScRK/CPA model identity follows the correlations discussed by Solbraa (2002), equations 8.14-8.15 and table 8-12; the mutable repository tables remain the exact numerical source. The CPA-specific valence fits are described below and are unchanged.
Numerical compatibility: correcting the unconditional CPA dispatch introduced
in #1787 changes calculated ScRK pairs. With the default Na+ Stokes diameter
of 5.68 angstroms, wij[0](Na+, water) changes from 1.6297556353584585e-4
to 4.01554792e-4. ScRK phase equilibria and properties that depend on these
pairs can therefore change. This is a model-selection regression correction
(#3850), not evidence that the original or corrected calibration meets a given
experimental accuracy target. CPA reference interactions retain their values.
The current shared furstParamsCPA_TDep temperature correction is deliberately
retained for both families; the correction does not restore a complete
historical ScRK calibration. Regressions check the reference coefficients,
symmetric/reversed pairs, both valences, repeated initialization, CPA interleaving,
fitted precedence, and the retained temperature formula at 298.15 and 323.15 K.
Qualification of the combined ScRK correlation against experimental data,
including temperature dependence, remains part of #3144.
The CPA Wij values are calculated using the following ionic-diameter correlations:
Monovalent (1+) Cations
Wij(cation-water) = furstParamsCPA[2] × stokesDiameter + furstParamsCPA[3]
Wij(cation-anion) = furstParamsCPA[4] × (d_cat + d_an)^4 + furstParamsCPA[5]
Current fitted values (2024):
[2]= 4.985e-05 (slope for cation-water)[3]= -1.215e-04 (intercept for cation-water)[4]= -2.059e-08 (prefactor for cation-anion)[5]= -9.495e-05 (intercept for cation-anion)
Divalent (2+) Cations
Wij(2+ cation-water) = furstParamsCPA[6] × stokesDiameter + furstParamsCPA[7]
Wij(2+ cation-anion) = furstParamsCPA[8] × (d_cat + d_an)^4 + furstParamsCPA[9]
Current fitted values (refitted December 2024):
[6]= 5.40e-05 (slope for 2+ cation-water)[7]= -1.72e-04 (intercept for 2+ cation-water)[8]= -4.398e-08 (prefactor for 2+ cation-anion)[9]= -5.970e-17 (intercept for 2+ cation-anion)
Why Separate Parameters for Divalent Cations?
The divalent cation parameters differ significantly from monovalent:
- Intercept: 42% more negative for 2+ cations
- Physical interpretation: Stronger ion-water interaction for doubly-charged ions
Using unified parameters would give dramatically wrong Wij values for divalent cations (sometimes even wrong sign), making separate parameters essential for accuracy.
| Parameter | Monovalent | Divalent | Ratio |
|---|---|---|---|
| Slope | 4.98e-05 | 5.40e-05 | 1.08 |
| Intercept | -1.22e-04 | -1.72e-04 | 1.42 |
Validation Against Experimental Data
Robinson & Stokes Data (1965)
The model has been validated against Robinson & Stokes experimental data for mean activity coefficients (γ±) and osmotic coefficients (φ) at 25°C.
Monovalent Salts (1:1 electrolytes)
| Salt | Type | γ± Error | φ Error |
|---|---|---|---|
| NaCl | 1-1 | 2.4% | 1.6% |
| KCl | 1-1 | 4.3% | 1.0% |
| LiCl | 1-1 | 3.4% | 2.5% |
| NaBr | 1-1 | 2.8% | 2.0% |
| KBr | 1-1 | 1.4% | 2.0% |
Divalent Cation Salts (2:1 electrolytes)
| Salt | Type | γ± Error | φ Error |
|---|---|---|---|
| CaCl₂ | 2-1 | 7.0% | 4.2% |
| MgCl₂ | 2-1 | 9.6% | 4.6% |
| BaCl₂ | 2-1 | 2.3% | 1.5% |
Divalent Anion Salts (1:2 electrolytes)
| Salt | Type | γ± Error | φ Error |
|---|---|---|---|
| Na₂SO₄ | 1-2 | 20.0% | 19.7% |
| K₂SO₄ | 1-2 | 2.9% | 1.6% |
Overall Performance
- Mean activity coefficient (γ±): 6.0% average error
- Osmotic coefficient (φ): 4.3% average error
Dielectric Constant Mixing Rules
The model supports three dielectric constant mixing rules:
1. MOLAR_AVERAGE (Default, Recommended)
\[\varepsilon_{mix} = \sum_i x_i \varepsilon_i\]- Thermodynamically consistent (passes all derivative checks)
- Recommended for general use
2. VOLUME_AVERAGE
\[\varepsilon_{mix} = \sum_i \phi_i \varepsilon_i\]Where $\phi_i$ is the volume fraction.
- Warning: Composition derivatives are incomplete
- May cause thermodynamic inconsistencies
3. LOOYENGA
\[\varepsilon_{mix}^{1/3} = \sum_i \phi_i \varepsilon_i^{1/3}\]- Good for water-organic mixtures
- Warning: Composition derivatives are incomplete
Thermodynamic Consistency
The model has been verified for thermodynamic consistency using built-in checks:
Fugacity Coefficient Identities
✅ $\sum_i x_i \ln\phi_i = \frac{G^{res}}{RT}$ - PASSED
Derivative Consistency
✅ $\left(\frac{\partial \ln\phi_i}{\partial P}\right)T = \frac{\bar{V}_i - V{ig}}{RT}$ - PASSED
✅ $\left(\frac{\partial \ln\phi_i}{\partial T}\right)P = \frac{H{ig} - \bar{H}_i}{RT^2}$ - PASSED
✅ $\left(\frac{\partial \ln\phi_i}{\partial n_j}\right){T,P,n{k\neq j}} = \left(\frac{\partial \ln\phi_j}{\partial n_i}\right){T,P,n{k\neq i}}$ (symmetry) - PASSED
Usage Example
// Create electrolyte CPA system
SystemInterface system = new SystemElectrolyteCPAstatoil(298.15, 1.01325);
// Add water (solvent)
system.addComponent("water", 55.5); // mol
// Add electrolyte
system.addComponent("Na+", 1.0);
system.addComponent("Cl-", 1.0);
// Initialize
system.chemicalReactionInit();
system.createDatabase(true);
system.setMixingRule(10); // Electrolyte CPA mixing rule
// Run flash calculation
ThermodynamicOperations ops = new ThermodynamicOperations(system);
ops.TPflash();
// Get activity coefficients
int aq = system.getPhaseNumberOfPhase("aqueous");
int naIdx = system.getPhase(aq).getComponent("Na+").getComponentNumber();
int clIdx = system.getPhase(aq).getComponent("Cl-").getComponentNumber();
int waterIdx = system.getPhase(aq).getComponent("water").getComponentNumber();
double gammaNa = system.getPhase(aq).getActivityCoefficient(naIdx, waterIdx);
double gammaCl = system.getPhase(aq).getActivityCoefficient(clIdx, waterIdx);
double meanGamma = Math.sqrt(gammaNa * gammaCl); // Mean activity coefficient
double phi = system.getPhase(aq).getOsmoticCoefficientOfWater();
Mixed Solvent Systems
The model supports mixed solvent systems including:
- Water + MEG (monoethylene glycol)
- Water + TEG (triethylene glycol; initial parameter estimates)
- Water + Methanol
- Water + MDEA (methyldiethanolamine)
Separate Wij parameters are available for each solvent system.
For calculated cation-glycol pairs, TEG and the mixing-rule alias
triethylene glycol use furstParamsCPA_TEG; MEG and ethylene glycol
use furstParamsCPA_MEG. Each set uses indices [2]/[3] for the
monovalent slope/intercept and [6]/[7] for divalent cations. The TEG
parameters are initial estimates based on water parameters and have not
been fitted to experimental TEG-water-electrolyte data.
The correction in issue #3846
removes an earlier MEG fallback that shadowed the TEG-specific branch. With
the default parameters and the Na+ Stokes diameter of 5.68, the calculated
Na+-TEG reference parameter is now 0.000160864, instead of the MEG value
0.0003394. TEG-containing calculations using the shared Furst short-range
mixing rule can therefore change. Explicitly fitted pair parameters retain
precedence, and the existing temperature-dependent coefficients are unchanged.
This corrects parameter selection; it does not validate the TEG estimates
against experimental data.
Gas-Ion Interaction Parameters (Salting-Out Effect)
The electrolyte CPA model includes gas-ion interaction parameters to correctly predict the salting-out effect. The salting-out coefficient (k_s) determines how much dissolved gas solubility decreases with salt concentration.
Setschenow Equation
\[\log\left(\frac{S_0}{S}\right) = k_s \cdot c_{salt}\]Where:
- $S_0$ = gas solubility in pure water
- $S$ = gas solubility in salt solution
- $k_s$ = Setschenow (salting-out) coefficient (L/mol)
- $c_{salt}$ = salt concentration (mol/L)
Fitted Gas-Ion Parameters
| Gas | k_s (L/mol) | W_cation | W_anion | Notes |
|---|---|---|---|---|
| CO₂ | 0.10 | 1.05e-4 | 1.05e-4 | Polar, acidic gas |
| CH₄ | 0.12 | 1.10e-4 | 1.10e-4 | Reference hydrocarbon |
| C₂H₆ | 0.13 | 1.13e-4 | 1.13e-4 | Slightly larger |
| C₃H₈ | 0.14 | 1.15e-4 | 1.15e-4 | Larger hydrocarbon |
| C₄ (butanes) | 0.15 | 1.20e-4 | 1.20e-4 | Heavier hydrocarbon |
| C₅+ | 0.16 | 1.25e-4 | 1.25e-4 | Heaviest fractions |
| N₂ | 0.10-0.12 | 1.05e-4 | 1.05e-4 | Similar to CH₄ |
| H₂S | 0.06-0.08 | 1.10e-4 | 1.10e-4 | Polar, acidic |
| H₂ | 0.10 | 1.30e-4 | 1.30e-4 | Very small molecule |
Usage Example - Methane Solubility with Salt:
SystemInterface fluid = new SystemElectrolyteCPAstatoil(298.15, 50.0);
fluid.addComponent("methane", 0.1);
fluid.addComponent("water", 10.0);
fluid.addComponent("Na+", 0.5);
fluid.addComponent("Cl-", 0.5);
fluid.setMixingRule(10);
ThermodynamicOperations ops = new ThermodynamicOperations(fluid);
ops.TPflash();
// Methane solubility decreases with salt (salting-out effect)
double ch4InAqueous = fluid.getPhase(PhaseType.AQUEOUS).getComponent("methane").getx();
Organic Inhibitor-Ion Interaction Parameters
Hu-Lee-Sum Correlation for Hydrate Inhibition
For hydrate equilibrium calculations with combined salt + organic inhibitor systems, the Hu-Lee-Sum universal correlation (AIChE Journal 2017, 2018) states that water activity effects should be additive:
\[\ln(a_w^{combined}) = \ln(a_w^{salt}) + \ln(a_w^{OI})\]This is critical for accurate hydrate inhibitor dosing calculations when both thermodynamic inhibitors (MEG, methanol) and formation water salts are present.
OI-Ion Interaction Parameters
Without explicit organic inhibitor-ion (OI-ion) parameters, the combined effect may not be additive. The following parameters ensure correct additive behavior:
| Inhibitor | W_MeOH-cation | W_MeOH-anion | Purpose |
|---|---|---|---|
| Methanol | 1.5e-4 | 1.5e-4 | Ensures MEG+salt gives more inhibition |
| MEG | 0.0 | 0.0 | Default calculation works correctly |
| Ethanol | 1.3e-4 | 1.3e-4 | Interpolated value |
Validation Results
| System | Hydrate T (°C) | Validation |
|---|---|---|
| Pure gas + water | +20.0 | Baseline |
| With NaCl only | +10.8 | ✅ Salt inhibition |
| With MEG only | -2.3 | ✅ MEG inhibition |
| With methanol only | -4.6 | ✅ Methanol inhibition |
| MEG + NaCl | -18.9 | ✅ ~16°C additional depression |
| Methanol + NaCl | -5.4 | ✅ ~0.8°C additional depression |
Example - Combined Inhibitor Hydrate Calculation:
SystemInterface fluid = new SystemElectrolyteCPAstatoil(273.15 + 10.0, 100.0);
fluid.addComponent("methane", 0.85);
fluid.addComponent("water", 0.12);
fluid.addComponent("methanol", 0.03);
fluid.addComponent("Na+", 0.01);
fluid.addComponent("Cl-", 0.01);
fluid.setMixingRule(10);
fluid.setHydrateCheck(true);
ThermodynamicOperations ops = new ThermodynamicOperations(fluid);
ops.hydrateFormationTemperature();
// Combined effect is additive per Hu-Lee-Sum correlation
System.out.println("Hydrate T: " + fluid.getTemperature("C") + " °C");
Component conservation in hydrate-temperature calculations
Non-reactive brines retain the supplied molecular and ionic inventories. The multiphase solver temporarily uses a normalized, ion-free molecular feed for phase discovery. Stripped ions are excluded from its phase-fraction equations; their very small fugacity coefficients must not enter the molecular Newton matrix. Restoring the ions transforms both phase fractions and aqueous composition back to the full feed basis. Charge balance alone does not establish component conservation.
The final ionic gas/aqueous refinement compares Gibbs energies only when the reference state conserves the feed. A converged, normalized, conservative candidate must not be rejected because a state with a different inventory has a lower extensive Gibbs energy.
At every fluid evaluation, the hydrate-temperature operation checks phase and
composition normalization, each component’s recovered inventory against the
input, and aqueous ion confinement. An invalid non-reactive electrolyte state
raises IllegalStateException with the failed diagnostic instead of returning
a hydrate temperature. The caller’s multiphase-check setting is restored on
success and failure. Reactive calculations retain their existing species and
element-balance handling.
Regression coverage includes CO2 with NaCl–CaCl2 and NaCl–KCl mixtures on a 1 kg water / 10 mol CO2 basis, nearby pressures and salt concentrations, and equivalent salt-addition orders and repeated calculations. These are numerical conservation checks, not experimental qualification of mixed-salt hydrate temperatures.
For the CO2/water/explicit-ion subset, hydrate-temperature calculations also
compare independently initialized vapour and liquid CO2 trials with a conserved
aqueous feed. The constrained solver keeps ions in water, checks molecular
fugacity equality for both gas/aqueous and liquid-CO2/aqueous splits, and accepts
a single aqueous phase only after CO2 stability testing. This addresses the
phase-state failures in issue #3584.
It is a hydrate-temperature fluid-evaluation path; the generic TPflash() API
and mixed-inhibitor/chemical paths are unchanged.
Use the operation’s getDiagnostics() snapshot to distinguish a numerically
converged finite-inventory result from a saturated CO2 boundary. The
hydrate phase-state guide
documents the public API and the separate saturated/undersaturated experimental
assessment. The selected public data give maximum absolute temperature errors
of 1.12 K for saturated systems and 4.10 K for undersaturated systems, so this
numerical correction does not establish full-range experimental accuracy or
high-pressure drilling-fluid applicability.
Known Limitations
- Divalent anions (SO₄²⁻): Higher errors for 1:2 electrolytes like Na₂SO₄ (~20%)
- High concentrations: Accuracy decreases above ~2 mol/kg for some salts
- VOLUME_AVERAGE/LOOYENGA mixing rules: Incomplete composition derivatives
- Temperature range: Parameters fitted at 25°C, extrapolation accuracy may vary
Comparison: CPA vs Electrolyte CPA
| Feature | SystemSrkCPAstatoil | SystemElectrolyteCPAstatoil |
|---|---|---|
| Use Case | Non-ionic associating systems | Aqueous electrolyte solutions |
| Electrostatics | None | MSA + Born solvation |
| Ions | Not supported | Na+, K+, Ca++, Mg++, Cl-, etc. |
| Mixing Rule | 10 (recommended) | 10 (required) |
| Chemical Reactions | Optional | Recommended (pH, speciation) |
| Phase Class | PhaseSrkCPAs |
PhaseElectrolyteCPAstatoil |
Quick Start Examples
Simple NaCl Solution
SystemInterface system = new SystemElectrolyteCPAstatoil(298.15, 1.01325);
system.addComponent("water", 55.5);
system.addComponent("Na+", 0.5);
system.addComponent("Cl-", 0.5);
system.createDatabase(true);
system.setMixingRule(10);
system.init(0);
system.init(1);
// Get mean activity coefficient
double gammaMean = system.getPhase(0).getMeanIonicActivityCoefficient("Na+", "Cl-");
With Chemical Reactions (pH Calculation)
SystemInterface system = new SystemElectrolyteCPAstatoil(298.15, 1.01325);
system.addComponent("water", 55.5);
system.addComponent("CO2", 0.01);
system.addComponent("Na+", 0.1);
system.addComponent("Cl-", 0.1);
system.chemicalReactionInit(); // Enable pH and speciation
system.createDatabase(true);
system.setMixingRule(10);
ThermodynamicOperations ops = new ThermodynamicOperations(system);
ops.TPflash();
// Access aqueous phase
int aq = system.getPhaseNumberOfPhase("aqueous");
double pH = -Math.log10(system.getPhase(aq).getComponent("H3O+").getx() * 55.5);
In-situ pH — getpH() and the acid-gas fallback
The recommended way to read the aqueous pH is getpH() on the aqueous phase (or
SystemInterface.getpH(), which delegates to it). It supports several scales:
| Method | Definition | Notes |
|---|---|---|
getpH() / getpH("activity") |
-log10(γ_x · x_H3O+) |
default; mole-fraction activity, consistent with NeqSim’s reaction K’s |
getpH("molality") |
-log10(γ_m · m_H3O+) |
IUPAC standard (mol H₃O⁺ / kg water) |
getpH("molarity") |
-log10([H3O+]) |
mol/L, ignores activity coefficient |
getpH("acidgas") |
carbonic/hydrosulfuric acid dissociation estimate | screening in-situ pH from dissolved CO₂/H₂S |
Acid-gas fallback (no chemicalReactionInit() required). The rigorous
activity/molality/molarity scales need explicit H3O+ species, which only
exist after chemicalReactionInit() has solved the water/carbonic dissociation
equilibria. If those reactions were not initialised (or the electrolyte
solver is numerically unstable and yields no/NaN H3O+, e.g. at low pressure),
getpH() now falls back to an acid-gas dissociation estimate whenever the
aqueous phase contains water together with dissolved CO₂ and/or H₂S — instead of
silently returning a flat, unphysical 7.0.
The fallback uses the first-dissociation equilibria of carbonic and hydrosulfuric acid and an electroneutrality balance dominated by the conjugate bases (HCO₃⁻, HS⁻):
\[[\mathrm{H}^+] = \sqrt{K_1^{\mathrm{CO_2}}\,C_{\mathrm{CO_2}} + K_1^{\mathrm{H_2S}}\,C_{\mathrm{H_2S}} + K_w}, \qquad \mathrm{pH} = -\log_{10}[\mathrm{H}^+]\]where $C_i$ are aqueous molar concentrations (mol/L) obtained from the aqueous phase density and molar mass. The constants are temperature-dependent: $K_1^{\mathrm{CO_2}}$ from Plummer & Busenberg (1982), $K_1^{\mathrm{H_2S}}$ from a van’t Hoff fit anchored at pK₁ = 7.05 (25 °C), and $K_w$ from a van’t Hoff fit anchored at 1.0×10⁻¹⁴ (25 °C). For CO₂-saturated water at ambient conditions this reproduces the textbook value pH ≈ 3.9; acid-gas-free water returns ≈ 7.0.
// Plain electrolyte fluid — NO chemicalReactionInit(), excess CO2 over water
SystemInterface system = new SystemElectrolyteCPAstatoil(298.15, 1.01325);
system.addComponent("CO2", 5.0);
system.addComponent("water", 10.0);
system.createDatabase(true);
system.setMixingRule(10);
system.setMultiPhaseCheck(true);
ThermodynamicOperations ops = new ThermodynamicOperations(system);
ops.TPflash();
system.initProperties();
double pH = system.getpH(); // ≈ 3.9 for CO2-saturated water (acidic)
Screening scope. The acid-gas fallback ignores alkalinity from other ions (bicarbonate buffering, dissolved salts) and the second dissociation steps. It is intended for acid-gas corrosion screening (e.g. NORSOK M-506), not for buffered-brine speciation. For a fully rigorous speciated pH in a buffered brine, run
chemicalReactionInit()so explicitH3O+/HCO3-/OH-species are solved. For corrosion work that must always return a finite, source-tagged value, seeneqsim.process.corrosion.RobustAqueousPH.
Gas-Liquid Equilibrium with Electrolytes
SystemInterface system = new SystemElectrolyteCPAstatoil(323.15, 50.0);
system.addComponent("methane", 10.0);
system.addComponent("water", 100.0);
system.addComponent("MEG", 20.0);
system.addComponent("Na+", 1.0);
system.addComponent("Cl-", 1.0);
system.createDatabase(true);
system.setMixingRule(10);
ThermodynamicOperations ops = new ThermodynamicOperations(system);
ops.TPflash();
// Check phase compositions
for (int i = 0; i < system.getNumberOfPhases(); i++) {
System.out.println("Phase " + i + ": " + system.getPhase(i).getType());
}
References
-
Solbraa, E. (2002). “Measurement and Modelling of Absorption of Carbon Dioxide into Methyldiethanolamine Solutions at High Pressures.” PhD Thesis, Norwegian University of Science and Technology.
-
Fürst, W., & Renon, H. (1993). “Representation of excess properties of electrolyte solutions using a new equation of state.” AIChE Journal, 39(2), 335-343.
-
Robinson, R.A., & Stokes, R.H. (1965). “Electrolyte Solutions.” 2nd Edition, Butterworths, London.
-
Kontogeorgis, G.M., & Folas, G.K. (2010). “Thermodynamic Models for Industrial Applications.” Wiley.
-
Michelsen, M.L., & Mollerup, J.M. (2007). “Thermodynamic Models: Fundamentals & Computational Aspects.” Tie-Line Publications.
-
Hu, Y., Lee, B.R., Sum, A.K. (2017). “Universal correlation for gas hydrates suppression temperature of inhibited systems: I. Single salts.” AIChE Journal, 63(11), 5111-5124. DOI: 10.1002/aic.15868
-
Hu, Y., Lee, B.R., Sum, A.K. (2018). “Universal correlation for gas hydrates suppression temperature of inhibited systems: II. Mixed salts and structure type.” AIChE Journal, 64(6), 2240-2250. DOI: 10.1002/aic.16generalized
Parameter History
| Date | Change | Impact |
|---|---|---|
| 2002 | Initial parameters from Solbraa thesis | Baseline model |
| 2024 | Refitted monovalent parameters to Robinson & Stokes | γ± error: 2.8% |
| Dec 2024 | Refitted divalent cation parameters [6-9] | CaCl₂: 16%→7%, MgCl₂: 22%→10% |
| Dec 2024 | Updated chemical equilibrium solver | Improved pH accuracy |
| Dec 2024 | Added gas-ion parameters for C2-C5+, N₂, H₂S, H₂ | Correct salting-out for all gases |
| Feb 2026 | Added OI-ion parameters for Hu-Lee-Sum compliance | Additive hydrate inhibition with combined inhibitors |
| Jul 2026 | getpH() acid-gas dissociation fallback |
Acidic in-situ pH for CO₂/H₂S water without chemicalReactionInit(); no more silent 7.0 |
Source Code References
System Classes
SystemElectrolyteCPAstatoil.java- Main system class (Statoil implementation)SystemElectrolyteCPA.java- Generic electrolyte CPA systemSystemSrkCPAstatoil.java- Non-electrolyte CPA (for comparison)
Phase Classes
PhaseElectrolyteCPAstatoil.java- Phase calculations (Statoil g-function)PhaseElectrolyteCPA.java- Base electrolyte CPA phasePhaseModifiedFurstElectrolyteEos.java- Fürst electrostatic contributions
Component Classes
ComponentElectrolyteCPAstatoil.java- Component propertiesComponentElectrolyteCPA.java- Base electrolyte CPA component
Mixing Rules
EosMixingRuleHandler.java- Mixing rule selection and Wij calculationsCPAMixingRuleHandler.java- CPA association mixing rules
Parameters
FurstElectrolyteConstants.java- Wij correlation parameters
Tests
SystemElectrolyteCPATest.java- Basic electrolyte CPA testsElectrolyteCPAThermodynamicConsistencyTest.java- Thermodynamic consistencyElectrolyteCPARobinsonValidationTest.java- Validation against experimental data
Last updated: December 27, 2024