Water Stability Calculator (LSI, RSI, PSI, CCPP)¶
This is an interactive Langelier Saturation Index calculator that also reports the Ryznar Stability Index (RSI), the Puckorius (Practical) Scaling Index (PSI), and the Calcium Carbonate Precipitation Potential (CCPP) for cooling-tower and process water. Enter a pH, temperature, calcium hardness, total alkalinity, and TDS, and it tells you whether the water tends to scale or corrode — and, via CCPP, roughly how much calcium carbonate is at stake.
The math runs entirely in your browser (nothing is uploaded). Every formula is a faithful
port of the cooling-tower-chem Python library, using the same constants and
the same interpretation bands, and the numbers are
cross-checked against the library on this page. For the
full-analysis Python API, the ctchem command line, and the cooling-tower
cycles of concentration / blowdown balance, see the
rest of the docs.
Water analysis
Results
How it's computed¶
Every value above comes from the same formulas the Python library uses, ported unchanged.
The shared building block is the pH of saturation (pHs), the standard analytical form
of Langelier's equation:
pHs = (9.3 + A + B) - (C + D)
A = (log10(TDS) - 1) / 10
B = -13.12 * log10(T_kelvin) + 34.55
C = log10(calcium_hardness_as_CaCO3) - 0.4
D = log10(total_alkalinity_as_CaCO3)
| Index | Formula | Source |
|---|---|---|
| LSI (Langelier Saturation Index) | LSI = pH - pHs |
Langelier, W. F. (1936), J. AWWA 28(10) |
| RSI (Ryznar Stability Index) | RSI = 2*pHs - pH |
Ryznar, J. W. (1944), J. AWWA 36(4) |
| PSI (Puckorius Scaling Index) | PSI = 2*pHs - pH_eq, with pH_eq = 1.465*log10(alkalinity) + 4.54 |
Puckorius & Brooke (1991), Corrosion 47(4) |
| CCPP (Calcium Carbonate Precipitation Potential) | closed-system carbonate equilibrium solved to [Ca][CO3] = Ksp, in mg/L as CaCO₃ |
Plummer & Busenberg (1982); Wojtowicz (2001) |
The PSI deliberately replaces the measured pH with an equilibrium pH driven only by
alkalinity, which makes it a better fit for the highly buffered, recirculating water typical
of cooling towers. CCPP goes beyond the direction the indices give and estimates the
quantity of calcium carbonate that would precipitate (+) or dissolve (−) to reach
calcite saturation; it is solved iteratively with the Plummer & Busenberg equilibrium
constants and a Davies activity model (ionic strength estimated as μ ≈ 2.5×10⁻⁵ · TDS).
The interpretation bands (LSI target ≈ 0 to +1; RSI/PSI < 6 scaling, 6–7 balanced,
> 7 corrosive) and their wording are ported verbatim from the library's
interpret module. For the full derivation, the Stiff-Davis index for high-salinity water,
and the cooling-tower water balance, see Indices & the science and
CCPP.
Note
These are screening calculations, not a substitute for site-specific engineering judgment or a jar test. Band edges vary between published sources (some Ryznar tables put the balanced/corrosive boundary at 6.8 rather than 7.0), so treat values near a boundary as borderline. CCPP in particular is a semi-quantitative screen: its magnitude is dominated by the ionic-strength (activity) correction, and the dependency-free Davies model carries no ion pairing, so it tends to over-predict precipitation for hard, high-TDS water relative to a full speciation model such as PHREEQC. See the interpretation guidance for details.
Run the same numbers in Python¶
The calculator is a browser port of cooling-tower-chem. To compute the same
indices in a script, notebook, or pipeline — and get input validation, the Stiff-Davis and
Larson-Skold indices, unit conversions, and the cooling-tower water balance — install the
library:
# From source (the first PyPI release is pending):
pip install git+https://github.com/Madhvansh/cooling-tower-chem
from cooling_tower_chem import WaterSample
# The default inputs of the calculator above (TDS 1560 mg/L).
sample = WaterSample(
ph=8.2, temperature_c=32,
calcium_hardness=450, total_alkalinity=250, # mg/L as CaCO3
tds=1560,
)
print(round(sample.lsi(), 2)) # 1.38 -> scale-forming
print(round(sample.rsi(), 2)) # 5.44 -> heavy scale
print(round(sample.psi(), 2)) # 5.59
print(round(sample.ccpp(), 1)) # 66.6 mg/L as CaCO3
Installing the package also gives you the ctchem command line, which reproduces the same
figures:
$ ctchem report --ph 8.2 --temp 32 --calcium 450 --alkalinity 250 --tds 1560
pH of saturation (pHs): 6.821
LSI: 1.379 [scale_forming]
LSI +1.38: scale-forming; calcium carbonate will tend to precipitate.
RSI: 5.443 [severely_scale_forming]
RSI 5.44: heavy scale formation expected.
...
$ ctchem ccpp --ph 8.2 --temp 32 --calcium 450 --alkalinity 250 --tds 1560
CCPP = +66.59 mg/L as CaCO3
See the Command line page for every subcommand and --json output.
Cross-checked against the library¶
To keep the browser calculator honest, this page runs a self-test on load: it computes
four reference cases with the in-page JavaScript and asserts each matches values produced by
the cooling-tower-chem Python library (.venv CPython) to within 0.01. The badge under the
calculator reports the result; the observed maximum deviation is on the order of 1e-6.
| Case | Inputs (pH, °C, TDS, Ca, Alk) | LSI | RSI | PSI | CCPP (mg/L as CaCO₃) |
|---|---|---|---|---|---|
| A | 8.2, 32, 1560, 450, 250 | +1.38 | 5.44 | 5.59 | +66.6 |
| B | 7.5, 25, 400, 240, 180 | +0.19 | 7.12 | 6.78 | +17.5 |
| C | 8.0, 30, 2000, 900, 250 | +1.43 | 5.14 | 5.08 | +88.3 |
| D | 7.0, 20, 150, 50, 40 | −1.70 | 10.40 | 10.51 | −17.3 |
The JavaScript and Python columns agree on all four cases (the table shows the rounded Python values; the self-test compares against the full-precision figures).