Activity models
Every equilibrium this package computes rests on
and on nothing else about the solution. The standard potential
A model has to answer two questions, not one:
the solute activity
, which sets every solubility and every saturation index;the solvent activity
, which is what a hydrate reaction consumes, and which in a cement paste short of mixing water is the quantity that decides how far the reaction can go.
The three built-in models — DiluteSolutionModel, DaviesActivityModel, HKFActivityModel — differ in both, and §5 measures by how much.
1. Where the comes from
An ion in a solution of ions is not in the same state as one alone at the same molality, and the reason is electrostatic. Around a cation, anions are slightly more likely to be found than cations — not because of any structure, only because their energy there is lower — so the ion sits at the center of a diffuse ionic cloud of opposite net charge. That cloud screens the ion's own field and lowers its energy, which is why activity coefficients of ions are below 1.
The classical treatment takes three steps, and the assumptions it makes on the way are exactly the assumptions that later fail:
Poisson's equation relates the mean potential to the mean charge density,
, with the solvent entering only as a continuum of bulk permittivity. Boltzmann statistics give the local ion densities,
, treating the ions as independent in the mean field of all the others.Linearization,
, turns the pair into , whose solution is a screened Coulomb potential with a single length scale
the Debye length. Charging the central ion reversibly inside its own cloud gives the excess chemical potential, and hence
exact as
Two ions cannot approach closer than the sum of their radii, so the cloud is excluded from a shell of radius HKFActivityModel implements:
and are properties of water, not fitting constants
Collecting the constants of step 3 gives, with
which is what hkf_debye_huckel_params evaluates from this package's own equation of state for water. So the
Both rise with temperature, because water's dielectric constant falls faster than
The screening length is the size of a gel pore
The Debye length is
A cement pore solution sits around
2. The term is a deviation function, not a physical term
Measured activity coefficients turn back upwards at high ionic strength, which no screening argument produces. The B-dot model adds a linear term for it,
Its status is that of an empirical correlation. (Anderson and Crerar, 1993) (§17.7.1, pp. 445–446) record that Helgeson defined
That is why this model has a ceiling rather than an asymptote, and why the ceiling is quoted vaguely as "about a molal": the term is not wrong so much as it is standing in for physics it does not contain.
Neutral species get the Setschenow form, CO₂(aq) is the case that matters for carbonation.
3. The water activity, and why it cannot be assumed separately
The solvent is not a solute and its activity is not obtained by the same formula. Two routes exist in this package.
Raoult — DiluteSolutionModel and DaviesActivityModel use. It counts molecules and knows nothing about what they are.
The osmotic coefficient HKFActivityModel uses:
with
which is not an optional refinement: it is the statement that the solvent and the solutes are parts of one thermodynamic system. A model that corrects its solutes and leaves its solvent ideal violates it by construction, and §5 measures by how much.
The one approximation in the B-dot route is that
4. Inputs and outputs, model by model
What each model needs and what it returns — the full parameter tables, with the provenance of every default, are in the docstrings (HKFActivityModel, DaviesActivityModel); this is the summary that lets you choose.
DiluteSolutionModel | DaviesActivityModel | HKFActivityModel | PitzerActivityModel | |
|---|---|---|---|---|
| solute scale | molarity | molality | molality | molality |
| Davies | extended D-H + | virial expansion | ||
| Raoult | Raoult | osmotic coefficient | osmotic coefficient | |
| per-species data | none | none | ion radii | a parameter per ion pair and per triplet — caller input |
| scalar inputs | none | the shape constants | ||
| none | ||||
| returns | same | same | same | |
| the range its set was fitted over, a few mol/kg | ||||
| Gibbs-Duhem consistent | approximately | no (§5) | to | exactly, by construction (§6) |
All three return the same object — a vector of cs.species, covering solutes, solvent, pure crystals (concentration_scale tells the accessors which convention was used.
5. What the difference is worth, measured
The models are compared, on an imposed NaCl composition and without solving anything, in What the choice of activity model costs. Three results from that page belong here, because they are about the theory rather than about the numbers:
The ideal model is already several percent off at a millimolal. Ideality is not a safe default that degrades gracefully; it is exact only in a limit.
Davies and the B-dot model part company around a tenth molal, and by 3 mol/kg Davies returns
The values of
The third result follows from §3: Davies is less thermodynamically consistent than assuming ideality. Correcting the solutes while leaving the solvent at
The B-dot model's own defect — the single charge-weighted mean radius in its osmotic coefficient — appears only along a composition change that holds test/activities.jl uses, which is why its tolerance is 5e-3 rather than solver tolerance.
6. The ion-interaction model: a different kind of object
Everything above is a corrected Debye-Hückel law: one screening term derived from electrostatics, one size correction, and one empirical term standing in for the rest. PitzerActivityModel is not that. It starts from the excess Gibbs energy and expands it as a virial series in the molalities, exactly as the pressure of a non-ideal gas is expanded in its density — (Anderson and Crerar, 1993) (§17.8) derive it as a cluster expansion with osmotic pressure in place of pressure, which is where the analogy is exact:
the first term in a virial equation always represents ideal behavior; in the second term
represents the non-ideal contribution from pairwise interactions of molecules; gives the interactions of triples.
Hence the shape of the parameter set: one coefficient per ion pair, one per triplet, and no per-species radius at all.
The long-range term keeps the Debye-Hückel physics — it must, since the limiting law is exact — in the form
and the pair term carries the ionic-strength dependence that a single constant cannot:
Why the whole construction matters here
What it costs
The parameters are caller input, and there is no way around it: no thermodynamic database ships them, and this package refuses to invent them. A PitzerParameters takes every table as a keyword without a default, and completeness is checked against the species list rather than against the set alone — every cation-anion pair present must have a
Two limitations belong here rather than in a footnote:
the higher-order electrostatic terms
, are not implemented. They vanish identically for a symmetrical pair, so a single 1-1 or 2-2 electrolyte is unaffected; in a mixture of Na⁺ with Ca²⁺ — a cement pore solution — they are a real omission; a published set is fitted at one temperature, and nothing here extrapolates the interaction parameters away from it.
The speciation a set assumes is part of the set
This one is easy to miss and changes results. A Pitzer set absorbs ion association into its CaSO₄⁰ complex. A species list that carries both the free ions and the ion pairs therefore counts the same association twice, and CEMDATA18 does carry them — Ca(SO4)@, CaOH+, Na(SO4)-, NaOH@ among others. It also names the silica species differently (HSiO3-, SiO2@) from the set's H3SiO4- and H2SiO4-2.
So the shipped Reardon set does not drop into a CEMDATA18 cement calculation, and the completeness check refuses such a system rather than returning a number. That refusal is the correct outcome and not a limitation of the implementation: combining a dissociated parameterization with an associated speciation is not a defensible calculation in any code.
7. Outside the domain
None of this survives arbitrary concentration, and the failure is silent: the formulas go on returning finite, plausible numbers. Two guards exist rather than one warning.
solvent_fraction reports the mole fraction of water within the aqueous phase, and SOLVENT_FRACTION_FLOOR is where equilibrate_certified refuses to call the answer a solution at all. The regime it catches is real: a cement paste below
A Pitzer model does not rescue that regime. Its fitted range is a few mol/kg, not hundreds, and no activity model repairs a composition that has left the physical picture of a solution.