Methods

Method

MNRC — struvite precipitation and NH₃ stripping

mnrc-struvite-v1 · selective_reduction · MNRC

What it predicts

N and P recovery, struvite mass and purity from N/P-rich streams — not power.

Governing relations

Each relation is transcribed from the implementation. Where the code cites no source, the source line says so.

  1. Cathode pH rise
    pH=min⁡ ⁣(11, pH0+3(1−e−x)),x=jAt/FAlk/50 000⋅V\mathrm{pH} = \min\!\left(11,\ \mathrm{pH}_0 + 3\left(1 - e^{-x}\right)\right), \qquad x = \frac{j A t / F}{\mathrm{Alk}/50\,000 \cdot V}

    Source: Empirical buffer-saturation curve with a soft ceiling at pH 11; mnrc.ts cites no source for it.

    An empirical curve, not a carbonate speciation model.

    SymbolMeaningUnit
    pH0\mathrm{pH}_0influent pHpH
    xxH⁺ consumed per mole of alkalinity1
    jjanode current density from predict.tsA/m²
    AAanode area, fixed at 0.003m²
    ttrun duration (default 24 h)s
    Alk\mathrm{Alk}alkalinity (default 500); 50 000 mg CaCO₃ per equivalentmg CaCO₃/L
    VVrecovery chamber volume (default 100 mL)L

    Worked example

    These inputs, passed to the predictor, return the output below. A test re-runs the example on every change, so this number is the one the code produces.

    inputvalueunit
    temperatureC25°C
    ph7pH
    electrodeSpacingCm2cm
    hrtHours24h
    codMgL1,000mg/L

    Output · mnrc.catholytePh = 7.995 pH, within ±0.1 %

    By hand · j = 0.15 A/m² from predict.ts at 25 °C, pH 7, 1000 mg/L COD, no anode named (see the MFC worked example). I = 0.15 × 0.003 = 4.5×10⁻⁴ A; charge over 24 h = 38.88 C = 4.030×10⁻⁴ mol H⁺. Alkalinity = 500/50 000 × 0.1 = 1.0×10⁻³ mol, so x = 0.403. pH = 7 + 3 × (1 − e^−0.403) = 7.995.

    Reproduce · Pinned by libs/shared/electrochemistry/src/predictors/worked-examples.test.ts. Run it from the repository root:

    pnpm exec jest -c libs/shared/electrochemistry/jest.config.js worked-examples.test.ts -t "mnrc.catholytePh"
  2. Struvite saturation index
    SI=log⁡10IAP+pKsp,IAP=[Mg] (1−fNH3)[NH4] 10pH−12.35[PO4],pKsp=13.27SI = \log_{10}\mathrm{IAP} + pK_{sp}, \qquad \mathrm{IAP} = [\mathrm{Mg}]\,(1 - f_{NH_3})[\mathrm{NH_4}]\,10^{\mathrm{pH}-12.35}[\mathrm{PO_4}], \quad pK_{sp} = 13.27

    Source: K_sp from Münch & Barr 2001, Water Res. 35:151.

    Activity coefficients are ignored (dilute-solution assumption).

    SymbolMeaningUnit
    SISIstruvite saturation index; nucleation above 0.51
    [Mg],[NH4],[PO4][\mathrm{Mg}], [\mathrm{NH_4}], [\mathrm{PO_4}]total influent concentrations (activities taken as 1)mol/L
    10pH−12.3510^{\mathrm{pH}-12.35}free PO₄³⁻ fraction1
    pKsppK_{sp}−log₁₀ of the struvite solubility product at 25 °C1
  3. Free-ammonia fraction
    fNH3=11+10 pKa−pH,pKa=9.25f_{NH_3} = \frac{1}{1 + 10^{\,pK_a - \mathrm{pH}}}, \qquad pK_a = 9.25

    Source: Henderson-Hasselbalch speciation with pK_a = 9.25; mnrc.ts cites no source for it.

    SymbolMeaningUnit
    fNH3f_{NH_3}free-ammonia share of total ammoniacal nitrogen1
    pKapK_aNH₄⁺/NH₃ acid constant at 25 °C1
  4. Struvite yield
    ns=min⁡(nN,nP,nMg)⋅min⁡(0.95, 0.4+0.55 (SI−0.5))(SI>0.5)n_{s} = \min\left(n_N, n_P, n_{Mg}\right)\cdot \min\left(0.95,\ 0.4 + 0.55\,(SI - 0.5)\right) \quad (SI > 0.5)

    Source: Linear precipitation-efficiency ramp above the nucleation threshold; mnrc.ts cites no source for it.

    A heuristic ramp from 40 % at SI = 0.5 to a 95 % cap.

    SymbolMeaningUnit
    nsn_sstruvite precipitatedmol
    nN,nP,nMgn_N, n_P, n_{Mg}moles of each reactant in the chambermol

Assumptions and constants

  • Anode current from the shared Butler-Volmer biofilm kinetics in predict.ts (Geobacter on acetate), with the same Arrhenius, substrate, pH and ohmic corrections as the MFC predictor.
  • Cathode pH rise is an empirical function of charge passed per unit alkalinity.
  • Nucleation above SI = 0.5.
  • Default urine matrix: 200 mg/L NH₄-N, 30 mg/L PO₄-P, 25 mg/L Mg — unsourced defaults.

Corrections applied

  • Purity penalty from deviation of Mg:NH₄:PO₄ from 1:1:1.

Not modelled

  • Crystal habit / dewaterability (needs CFD + supersaturation-gradient modelling).
  • Calcium carbonate scaling — only a binary purity penalty.
  • Continuous-flow operation — assumes batch over the stated duration.
  • Temperature dependence of Henry's constant — 25 °C value used.

Literature anchors in the source

  • Münch & Barr 2001, Water Res 35:151
  • Kuntke et al. 2012, Water Res 46:2627

Implementation

libs/shared/electrochemistry/src/predictors/mnrc.ts

Interval the predictor ships

heuristic_fixed_fraction — A fixed ±25 % placeholder band. It is NOT a calibrated interval; when served through /api/ml/predict it is replaced by the benchmark band below, or withheld. How served intervals are built

Calibration artifacts used when served

  • Benchmark out-of-fold residual quantiles: bes-benchmark-v1 · gkf5 · generated 2026-09-09
  • Split-conformal calibration: split-conformal · generated 2026-05-21
  • Hierarchical-priors v2 trust snapshot: populated · generated 2026-05-16

Benchmark cells for this predictor’s systems

Each row is a stratum with n ≥ 30 residuals at nominal level 90%. Coverage is the cross-validated share of held-out points inside the band.

targetstratumnpaperscoverageCV
No data (n = 0)

No benchmark cell reaches the minimum n for these systems — intervals are withheld.

Prior trust by served parameter

The state the lab badge shows for each served parameter and system, from the v2 priors fit.

parametersystemstatepapersprior
No data (n = 0)

No v2 prior row for these systems.

Scientific-rigor gates

pnpm verify:science runs these rules over the committed priors and validation artifacts in pre-commit. Known violations are baselined per rule and slug; a new one blocks the commit. Baseline from hierarchical-priors-v2.json@2026-10-26T00:10:47.721880+00:00.

rulebaselined
gate-honestyNo parameter may pass a rounded-threshold gate read while converged=false1
support-containmentBounded quantities must have ci95 inside physical support, width <= range3
magnitude-sanityNo ci95 bound may exceed the plausibility ceiling (1.0e+6)0
slug-uniquenessNo two slugs may share a spelling-independent identity; scale must agree2
secondary-source-purityNo fitted prior may carry a REVIEW (secondary-source) stratum0
artifact-pairingEach fit must have its own coverage evidence; the served fit must have some0
coverage-sharpnessAny coverage figure must ship its interval width alongside0
interval-calibrationHeld-out interval coverage per parameter may not newly degrade128
effects-gap-completenesseffects.json is schema 2.0; every non-high effect has a gap per archetype; no gap is "high"0