ReadonlybicarbonateHCO₃⁻, ppm. Alkalinity as CaCO₃ = this × 0.820162.
ReadonlycalciumCalcium (Ca²⁺), ppm.
ReadonlychlorideChloride (Cl⁻), ppm.
ReadonlymagnesiumMagnesium (Mg²⁺), ppm.
Optional ReadonlynameDisplay name, e.g. "Burton on Trent". Purely informational.
Optional ReadonlyphMeasured pH of the water as drawn. Used to work out how much of the alkalinity titrates.
ReadonlysodiumSodium (Na⁺), ppm.
ReadonlysulfateSulfate (SO₄²⁻), ppm.
Optional ReadonlyunmeasuredIons the analysis never printed, as distinct from ions it printed as zero.
RO water has 0 ppm sulfate, but a utility report that leaves the row out
tells you nothing, and the two have to be told apart. Each ion listed here
still holds 0 in its own field. The list only records where that zero
came from. It's never a second source for the value.
Absent (the usual case) means the whole analysis was measured. Blending, dilution and salt additions take the union of the lists. Adding 30 ppm of chloride to a water whose chloride was never measured still leaves the total unknown, so nothing downstream can report a ratio from it.
A brewing water analysis, every ion in ppm (mg/L).
Alkalinity is stored as bicarbonate, because that's the ion actually in the water. "Alkalinity as CaCO₃" is a reporting convention.
waterProfile()in./wateraccepts either form (plus German °dH) and converts to this one.Unmeasured ions
A water report that doesn't list sulfate isn't reporting zero sulfate. But the arithmetic downstream (charge balance, dilution, the salt solver) needs a number for every ion, or else a
nullbranch on every line. So unmeasured ions are stored as zero, and unmeasured records which ones they are. The calculations ignore that list. Only the code that tells a brewer something about the water checks it.