Methods

Method

MFC — Butler-Volmer + Monod single-overpotential snapshot

mfc-butler-volmer-monod-v2 · anodic_oxidation · MFC

Two MFC models are in use. The lab MFC tiles use mfc-butler-volmer-monod-v2; /api/ml/predict, the MCP server and the chat assistant use Enhanced_MFC_Experimental. For the same design they can differ. Whether to unify them is an open decision for the maintainers. Enhanced_MFC_Experimental

What it predicts

Areal power and current density, coulombic efficiency and COD removal for an air- or O₂-cathode MFC.

Governing relations

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

  1. Anodic Butler-Volmer current
    jBV=i0[exp⁡ ⁣(αnFηaRT)−exp⁡ ⁣(−(1−α)nFηaRT)]j_{BV} = i_0\left[\exp\!\left(\frac{\alpha n F \eta_a}{RT}\right) - \exp\!\left(-\frac{(1-\alpha) n F \eta_a}{RT}\right)\right]

    Source: Textbook Butler-Volmer relation (calculateButlerVolmer in scientificConstants.ts); predict.ts cites no source for it.

    SymbolMeaningUnit
    jBVj_{BV}anodic current density before substrate and transport limits (code works in A/cm², reports ×10⁴)A/m²
    i0i_0exchange current density after the Arrhenius and roughness correctionsA/cm²
    α\alphaanodic transfer coefficient (catalog value, else 0.5)1
    nnelectrons transferred, fixed at 21
    FFFaraday constant, 96 485.332 12C/mol
    ηa\eta_aanode overpotential, a single snapshot at 0.15 VV
    RRgas constant, 8.314 462 618J/(mol·K)
    TTtemperatureK
  2. Exchange current: Arrhenius and roughness
    i0=i0,cat exp⁡ ⁣[EaR(1298.15−1T)]fR,fR=min⁡ ⁣(100, max⁡ ⁣(1, 2log⁡10aBET+1))i_0 = i_{0,\mathrm{cat}}\, \exp\!\left[\frac{E_a}{R}\left(\frac{1}{298.15} - \frac{1}{T}\right)\right] f_R, \qquad f_R = \min\!\left(100,\ \max\!\left(1,\ 2\log_{10} a_{BET} + 1\right)\right)

    Source: Arrhenius form with E_a = 25 kJ/mol; roughness form calibrated in-house (Phase B, 2026-04-29); predict.ts cites no source for it.

    The roughness factor is a log-scaled heuristic with a floor of 1, not a measured area ratio.

    SymbolMeaningUnit
    i0,cati_{0,\mathrm{cat}}catalog exchange current density for the named anode; 5×10⁻⁸ when none is named or the slug is unknownA/cm²
    EaE_aactivation energy of a mixed-culture biofilm, 25 000J/mol
    RRgas constant, 8.314 (anode-kinetics.ts)J/(mol·K)
    fRf_Rroughness factor; 1 when the catalog has no BET area1
    aBETa_{BET}specific surface area of the anode from the catalogm²/g
  3. Monod substrate saturation
    fS=SKs+Sf_S = \frac{S}{K_s + S}

    Source: Rittmann & McCarty 2001, Environmental Biotechnology (K_s = 120 mg/L, mixed anaerobic culture); other buckets calibrated against the validation-tuple corpus.

    SymbolMeaningUnit
    fSf_Ssubstrate factor applied to the current1
    SSinfluent CODmg/L
    KsK_shalf-saturation constant from the substrate bucket, or a citation-backed overridemg/L
  4. Mass-transport ceiling
    j=jBVfS[1−exp⁡ ⁣(−jlimjBVfS)],jlim=max⁡ ⁣(0.5, 50 S1000 T298.15)j = j_{BV} f_S \left[1 - \exp\!\left(-\frac{j_{lim}}{j_{BV} f_S}\right)\right], \qquad j_{lim} = \max\!\left(0.5,\ 50\,\frac{S}{1000}\,\frac{T}{298.15}\right)

    Source: Unsourced engineering default in predict.ts (2026-10 citation audit).

    A smooth saturation that bounds the unphysical high edge; it does not model species transport.

    SymbolMeaningUnit
    jjserved current densityA/m²
    jlimj_{lim}substrate-diffusion ceilingA/m²
    SSinfluent CODmg/L
    TTtemperatureK
  5. Cell voltage
    Vcell=0.75 fdfpH−min⁡ ⁣(j104(ASRa+ASRc), 0.7⋅0.75 fdfpH),fpH=e−(pH−7)2/2V_{cell} = 0.75\, f_d f_{pH} - \min\!\left(\frac{j}{10^4}\left(ASR_a + ASR_c\right),\ 0.7 \cdot 0.75\, f_d f_{pH}\right), \qquad f_{pH} = e^{-(\mathrm{pH}-7)^2/2}

    Source: Representative OCV, Gaussian pH factor and linear spacing penalty; predict.ts cites no source for it.

    The IR drop is capped at 70 % of the working voltage so pathological inputs cannot drive it negative.

    SymbolMeaningUnit
    VcellV_{cell}operating cell voltageV
    0.750.75representative open-circuit voltage of an acetate-fed membraneless MFCV
    fdf_dspacing penalty: 1 up to 2 cm, then 0.12 per cm, floor 0.41
    fpHf_{pH}Gaussian pH factor, optimum 7, σ = 11
    ASRa,ASRcASR_a, ASR_celectrode area-specific resistances from the catalog, else 5 eachΩ·cm²
    jjserved current densityA/m²
  6. Areal power
    P=103 j VcellP = 10^{3}\, j\, V_{cell}

    Source: Definition of electrical power, P = I·V.

    SymbolMeaningUnit
    PPareal power densitymW/m²
    jjserved current densityA/m²
    VcellV_{cell}operating cell voltageV

    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 · result.powerDensity = 115 mW/m², within ±0.5 %

    By hand · No anode named: i₀ = 5×10⁻⁸ A/cm², f_R = 1, Arrhenius factor = 1 at 25 °C. F·η_a/(R·T) = 96 485.33 × 0.15 / (8.314 46 × 298.15) = 5.838, so j_BV = 5×10⁻⁸ × (e^5.838 − e^−5.838) × 10⁴ = 0.1716 A/m². f_S = 1000/1120 = 0.893, j = 0.1532 A/m² (the transport factor is 1.000). V_cell = 0.75 − 0.1532/10⁴ × 10 = 0.7498 V. P = 0.1532 × 0.7498 × 1000 = 114.9, reported to the nearest whole number as 115 mW/m².

    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 "result.powerDensity"
  7. Coulombic efficiency and COD removal
    CE=min⁡(80, 20 fHRTfpH),COD=min⁡(95, 75 fHRTfSfT),fHRT=min⁡(1.2, 0.3+0.5log⁡10(HRT+1))CE = \min\left(80,\ 20\, f_{HRT} f_{pH}\right), \quad \mathrm{COD} = \min\left(95,\ 75\, f_{HRT} f_S f_T\right), \quad f_{HRT} = \min\left(1.2,\ 0.3 + 0.5\log_{10}(\mathrm{HRT}+1)\right)

    Source: Empirical saturation forms; predict.ts cites no source for it.

    Heuristic saturation curves, not a substrate mass balance.

    SymbolMeaningUnit
    CECEcoulombic efficiency%
    COD\mathrm{COD}COD removal%
    fHRTf_{HRT}retention-time saturation factor1
    HRT\mathrm{HRT}hydraulic retention timeh
    fTf_Ttemperature step: 1 above 20 °C, else 0.81

Assumptions and constants

  • Single anode overpotential snapshot η_anode = 0.15 V — not a polarisation sweep.
  • Mixed-culture Monod constant K_M = 120 mg COD/L unless the substrate lookup gives another.
  • Arrhenius activation energy E_a = 25 kJ/mol for a mixed-culture biofilm; i₀ normalised to 298 K.
  • pH response is a Gaussian around 7.0 with σ = 1.0.
  • An anode the catalog cannot resolve, or none, uses i₀ = 5×10⁻⁸ A/cm².

Corrections applied

  • Electrode roughness factor on i₀ from the electrode catalog.
  • Ohmic penalty from electrode spacing (linear above 2 cm).
  • Ohmic IR drop from electrode area-specific resistance (gated by NEXT_PUBLIC_LAB_PHYSICS_OHMIC).
  • Mass-transport ceiling on the current (gated by NEXT_PUBLIC_LAB_PHYSICS_MASS_TRANSPORT).
  • Biofilm thickness clamped to 0–5 mm.

Not modelled

None stated in the source.

The predictor’s source file declares nothing in this section, so nothing is hidden here. Add it to the registry entry to show it.

Literature anchors in the source

None stated in the source.

The predictor’s source file declares nothing in this section, so nothing is hidden here. Add it to the registry entry to show it.

Implementation

libs/shared/electrochemistry/src/predict.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
cod_removalMFC60217288.5%
cod_removalMFC|bioremediation46856.5%
cod_removalMFC|power_generation1344188.8%
cod_removalMFC|unknown53885.7%
cod_removalMFC|wastewater_treatment36011387.8%
coulombic_efficiencyMFC3019789.7%
coulombic_efficiencyMFC|power_generation1073186.9%
coulombic_efficiencyMFC|unknown301073.7%
coulombic_efficiencyMFC|wastewater_treatment1354877.0%
current_density_arealMFC49613489.3%
current_density_arealMFC|power_generation2085488.0%
current_density_arealMFC|unknown491483.7%
current_density_arealMFC|wastewater_treatment2015785.6%
power_density_arealMFC87124588.9%
power_density_arealMFC|bioremediation49969.4%
power_density_arealMFC|power_generation34311489.5%
power_density_arealMFC|unknown1062485.8%
power_density_arealMFC|wastewater_treatment3509185.4%

Prior trust by served parameter

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

parametersystemstatepapersprior
power_density_arealMFCcurated275weakly_informative
current_density_arealMFCcurated150weakly_informative
coulombic_efficiencyMFCcurated96literature
cod_removalMFCcalibrated183weakly_informative

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