Rust library (with optional CLI) to estimate Practical Salinity SP, Absolute Salinity SA, density ρ, and specific gravities from macro chemical analyses of seawater or reef aquaria.
Note
Validation is in progress.
Calculations follow TEOS‑10 conventions and couple charge/mass balances with density via a simple fixed‑point iteration. The library exposes both high‑level helpers and low‑level building blocks; the CLI prints a concise summary (SP, SA, ρ, SG).
- Compute
SPandSAfrom elemental/ionic analyses - TEOS-10 density
ρ(SA,CT,p)and specific gravity at a reference temperature - Self-consistent conversion between mg/L and mg/kg via
ρ - Chloride estimation from electroneutrality with adaptive reference‑ion ratio blending when Cl⁻ is missing
- Configurable assumptions: temperature
T, pressurep, alkalinity, borate fraction - Library support for component tables (mg/L, mg/kg, SP=35 normalization). Note: current CLI prints summary only.
Add the crate to your project. Until published on crates.io, you can depend on the Git repository. Disable default features to avoid pulling the optional CLI dependency:
[dependencies]
salinity_rs = { git = "https://github.com/u8array/salinity_rs", default-features = false }Quick start (Rust):
use salinity_rs::{compute_summary, Inputs, Assumptions};
fn main() {
// concentrations in mg/L; temperature in °C; pressure in dbar
let inputs = Inputs {
na: 10780.0, ca: 420.0, mg: 1290.0, k: 400.0, sr: 8.0,
br: 65.0, s: 900.0, b: 4.4,
cl: None, // let the model estimate Cl⁻ from electroneutrality
f: None, // fall back to default F⁻ if not provided
alk_dkh: Some(8.0),
};
// Environmental and reference assumptions
let ass = Assumptions { temp: 20.0, pressure_dbar: 0.0, ..Default::default() };
let out = compute_summary(&inputs, &ass);
println!(
"SP={:.4} SA={:.4} g/kg ρ={:.3} kg/m³ SG20/20={:.5} SG25/25={:.5}",
out.sp, out.sa, out.density_kg_per_m3, out.sg_20_20, out.sg_25_25
);
}Public API highlights:
- Convenience API:
compute_summary(inputs, assumptions)→ SP, SA, ρ, SG(20/20), SG(25/25) - Solver API:
calc_salinity_sp_teos10,calc_salinity_sp_iterative(&Inputs, &Assumptions, max_iter, tol),rho_from_sp,specific_gravity,sa_from_sp - Types:
Inputs,Assumptions,CalcResult,DetailedResult,Components
Minimum supported Rust: a recent stable with Edition 2024 support.
The CLI is behind the cli feature. Build and run locally:
cargo run --features cli -- --helpThe binary accepts JSON inputs for measurements and assumptions:
--inputs-json <JSON>: Inline JSON for input values (shape ofInputs).--assumptions-json <JSON>: Optional, adds/overrides assumptions (shape ofAssumptions).--input <FILE>: Read a file containing an object withinputsand optionalassumptions. Use-for stdin.--json: Output machine‑readable JSON.
JSON fields (excerpt):
- Inputs (mg/L unless noted):
na, ca, mg, k, sr, br, s, b, cl(optional; omit or setnullfor auto‑estimate),f(optional),alk_dkh(dKH, optional) - Assumptions (conditions and options):
temp(°C, default 20),pressure_dbar(dbar, default 0),alkalinity(dKH, optional; used ifinputs.alk_dkhis missing),assume_borate(default true),borate_fraction,ref_alk_dkh(default 8.0),alk_mg_per_meq,default_f_mg_l(default 1.296),return_components(default false)
SP: 34.9800
SA: 35.1600 g/kg
Density: 1024.600 kg/m^3
SG 20/20: 1.02600
SG 25/25: 1.02480
-
$\delta SA_{\text{composition}}$ neglected; acceptable for near-NSW compositions. - Fixed alkalinity fractions are pH-independent; accuracy drops for unusual pH/CO₂.
- Cl⁻ by electroneutrality is sensitive to input errors.
- Optional default F⁻ used if missing.
- Iteration enforces consistency between volume-based inputs and mass-based reference.
cli— enables the command‑line interface and pulls in the optionalclapdependency. Not needed for library use.approx_ct— enables a shallow‑water approximation for CT from t (CT≈θ via small adiabatic lapse). For aquaria/near‑surface use only; otherwise leave disabled (default).std— enables use of the Rust standard library; when disabled the crate can be built withno_std.
Example with concentrations in mg/L, t = 20 °C, p = 0 dbar, and total alkalinity 8 dKH.
Two invocation methods are supported:
- Inline JSON (on the command line)
salinity_rs --inputs-json '{
"na":10780, "mg":1290, "ca":420, "k":400, "sr":8,
"br":65, "s":900, "b":4.4,
"alk_dkh":8
}' --assumptions-json '{
"temp":20, "pressure_dbar":0, "return_components":true
}'- File-based (file contains an object with
inputsand optionalassumptions)
inputs.json
{
"inputs": {
"na":10780, "mg":1290, "ca":420, "k":400, "sr":8,
"br":65, "s":900, "b":4.4,
"alk_dkh":8
},
"assumptions": {
"temp": 20, "pressure_dbar": 0, "return_components": true
}
}Run:
salinity_rs --input inputs.json- SP: dimensionless, historically defined from conductivity relative to standard seawater at 15 °C and 1 bar.
- SA: g/kg of dissolved material. Primary TEOS-10 salinity variable.
For near-standard composition:
Composition anomalies would add
For species
Total alkalinity in dKH:
Split into species by fixed fractions:
with stoichiometry
Mass equivalent for reporting uses a configurable
Note: exact speciation is pH and DIC dependent; fixed fractions are a robust aquarium approximation.
Total B split by borate fraction
This affects charge and mass because species have different molar masses.
If Cl⁻ is not provided, solve from
Cations:
Anions:
Assign the residual negative charge to
When
From measured mass concentrations
For each species with a sufficiently stable ratio in near‑NSW (Na⁺, Mg²⁺, Ca²⁺, K⁺, Sr²⁺, Br⁻, and SO₄²⁻), form a candidate
Use reference mmol/kg as weights
Derive
Finally, blend the charge‑balance and ratio estimates adaptively:
Report chloride as
Rationale: charge balance alone can underestimate when inputs miss anions or contain measurement noise; the ratio pathway leverages the stability of major-ion ratios in standard seawater. The adaptive rule prefers the ratio estimate when the two disagree strongly and otherwise uses a conservative blend.
Limitations:
- Assumes composition near standard seawater; large deviations (e.g. hypersaline brines, engineered systems) can bias ratio-derived
$n_{\mathrm{Cl}}$ . - Sensitive to analytical errors in high-weight ions (especially Na⁺); propagate uncertainties if available.
- Fixed weights ignore minor temperature/salinity dependence of ionic ratios.
- Fluoride default introduces small uncertainty but typically negligible for overall charge.
A reference composition in mmol/kg is converted to g/kg using molar masses. Two corrections apply:
- Replace elemental B by chosen species masses
$\mathrm{B(OH)_3},\ \mathrm{B(OH)_4^-}$ per$\alpha_B$ . - Optionally add a reference alkalinity mass from a chosen
ref_alk_dKH(default 8.0; 6.2 available for RN compatibility).
Denote the resulting reference total as
Measured mass per liter:
Convert to g/kg via density
Define relative salinity from the ratio to reference:
Because
- Initialize
$SP=35$ ,$SA = SP\cdot SR_{\mathrm{REF}}/35$ . - Compute
$CT=\mathrm{CT}(SA,T,p)$ and$ρ=\mathrm{ρ}(SA,CT,p)$ via TEOS‑10. - Convert all component g/L to g/kg using
$ρ$ and sum to$\Sigma_{\mathrm{g/kg}}$ . - Update
$SR$ ,$SP$ ,$SA$ . - Stop when
$|SP_{\text{new}}-SP|<\varepsilon$ .
Convergence is fast because the density feedback is weak in this range.
- Convert
SP→SA:
- Conservative temperature:
- Density:
- Specific gravity at
$t_\mathrm{ref}$ :
For comparability, normalize to SP = 35:
- Castelao, G. P., and L. Irber (2024): Gibbs Sea Water Oceanographic Toolbox of TEOS‑10 implemented in Rust. Journal of Open Source Software, 9(93), 5988. https://doi.org/10.21105/joss.05988
- McDougall, T. J., and P. M. Barker (2011): Getting Started with TEOS‑10 and the Gibbs Seawater (GSW) Oceanographic Toolbox. TEOS‑10 Publication. https://www.teos-10.org/pubs/Getting_Started.pdf
- IOC, SCOR and IAPSO (2010): The International Thermodynamic Equation of Seawater – 2010 (TEOS‑10) Manual. TEOS‑10 Publication. https://www.teos-10.org/pubs/TEOS-10_Manual.pdf
- Millero, F. J., R. Feistel, D. G. Wright, and T. J. McDougall (2008): The composition of Standard Seawater and the definition of the Reference‑Composition Salinity Scale. Deep‑Sea Research Part I, 55, 50–72. https://doi.org/10.1016/j.dsr.2008.03.004
- UNESCO (1981): The Practical Salinity Scale 1978 (PSS‑78). UNESCO Technical Papers in Marine Science No. 36.
Notes:
- In this tool, SA is taken as approximately equal to SR as per TEOS‑10; the constant SR_REF = 35.16504 g/kg follows Millero et al. (2008).
- The fixed alkalinity split and borate fraction are practical approximations for aquarium contexts rather than strict thermodynamic speciation.
Requires a recent stable Rust toolchain.
- Library only:
cargo build --release- CLI:
cargo build --release --features cliRun the CLI:
target/release/salinity_rs --helpMIT license. See LICENSE.
This project builds on TEOS‑10 relations via the excellent gsw crate.