v2.8 — Darwin v5 initial conditions + L2 POC observation loss¶
Date: 2026-05-17
Status: Findings doc; PR in flight.
Branch: v2.8-darwin-ic-poc-sub
Parent finding: v2.6 GEOTRACES hybrid (scav_rat and R_PICPOC framed as HNLC-region structural limits at ~0.92 and ~2.92 off Carroll)
Headline¶
v2.8 produces the project's first reproducible scav_rat Cal-grade recovery and the first non-noise Biggrow Excellent. At the anchor config (Darwin IC, GEOTRACES_SUB_W=1.0, POC_SUB_W=3.0, PINN_drift_w=3.0, n=10), the new scoreboard is:
| Parameter | Mean ± std (n=10) | Mean offset vs Carroll | Cal-grade hits | Excellent hits |
|---|---|---|---|---|
| scav_rat | 7.66e-7 ± 4.23e-7 | 0.403 | 7/10 | 4/10 |
| Smallgrow | 0.632 ± 0.178 | 0.204 | 9/10 | 2/10 |
| diatomgraz | 0.717 ± 0.228 | 0.162 | 9/10 | 2/10 |
| Biggrow | 0.407 ± 0.142 | 0.262 | 7/10 | 1/10 |
| alpfe | 0.336 ± 0.226 | 0.638 | 2/10 | 0/10 |
| R_PICPOC | 0.0055 ± 0.00045 | 0.870 | 0/10 | 0/10 |
Aggregate: 6/10 seeds reach 4/6 Cal-grade — twice the previous project best (3/10) for the v2.7 anchor config.
The cost of this scoreboard is alpfe, which trades from project-best (7/10 Excellent at sub_w=1.0 with Darwin IC alone) into Drifted (2/10 Cal-grade). The two recoveries are joint-incompatible under the v2.7 box-model + observation set; see the bimodal-degeneracy section.
What changed vs v2.7¶
Two orthogonal additions, both implemented in scripts/run_v2.7_multilayer_batched.py:
1. Darwin v5 pickup-derived initial conditions (DARWIN_IC=1)¶
The v2.7 box-model state vector previously initialised from literature-plausible defaults:
These are global-ocean averages, NOT Eq Pacific HNLC values. Two of them are wildly off vs the actual Darwin v5 Eq-Pacific surface state at iteration 78912 (~March 1994):
| Tracer × Layer | Literature default | Darwin v5 mean | Ratio |
|---|---|---|---|
| FeT_L1 | 5.0e-4 mmol/m³ | 1.7e-5 | 0.035 (30× too high) |
| FeT_L2 | 5.0e-4 | 5.3e-4 | 1.06 ✓ |
| POC_L1 | 0.5 | 0.29 | 0.58 |
| POC_L2 | 0.05 | 0.42 | 8.4 (8× too low) |
| PIC_L1 | 0.025 | 0.0084 | 0.34 |
| PIC_L2 | 0.003 | 0.013 | 4.4 |
| DIC_L1 | 2101 | 1973 | 0.94 ✓ |
| DIC_L2 | 2204 | 2320 | 1.05 ✓ |
| ALK_L1 | 2409 | 2326 | 0.97 ✓ |
| ALK_L2 | 2460 | 2386 | 0.97 ✓ |
The literature default's 30× FeT_L1 overestimate in Eq Pacific HNLC was the largest mismatch — the box-model integrator started from a non-HNLC iron state and the optimizer chose parameters that reproduced surface obs without ever needing alpfe near Carroll's value. The literature default's 8× POC_L2 underestimate is the candidate mechanism for the scav_rat undershoot in v2.6/v2.7: iron sink rate is scav_rat × DFe × POC, so underestimating POC by 8× forces scav_rat to fit 8× too low to match the surface iron balance.
The new scripts/build_darwin_ic_cache.py opens D:/ecco_darwin_v5/input/darwin_initial_conditions/pickup_ptracers.0000000001.data (10.94 GB, MITgcm float64 big-endian, 31 records of 50 × 3510 × 270), extracts 5 inorganic tracers (DIC, FeT, POC, PIC, ALK), depth-averages over L1 (levels 0-4, 0-50 m) and L2 (levels 5-28, 50-1056 m) with the L2 wet-cell mask, and bins to the same 1° Eq Pacific grid the runner uses. Cache is scripts/darwin_ic_cache.npz (80 KiB, deterministic; rebuild in ~5 s).
Phyto biomass IC stays at literature defaults — the 7-PFT (Darwin v5) → 5-PFT (box model) mapping is non-trivial (Chl tracked for only c01-c05; c06 likely coccolithophores, c07 likely Trichodesmium / N-fixers) and biomass equilibrates fast under the integrator so initialisation matters less.
2. L2 POC observation loss (POC_SUB_W > 0)¶
A new z-score loss term constrains the integrator's L2 POC trajectory directly:
The target poc_l2_z is built from the same IC cache (Darwin's depth-averaged POC over L2). The choice of Darwin self-target is intentional: Carroll's calibration should reproduce Darwin's POC field, so constraining against Darwin POC gives a methodologically consistent test of whether L2 POC observability resolves the scav_rat degeneracy. Swapping the target for real GEOTRACES POC (POC_LPT_CONC + POC_SPT_CONC, already in D:/geotraces/GEOTRACES_IDP2025_Seawater.nc) is a 50-line refactor of geotraces_loader.py for the next iteration.
Filename / JSON tagging¶
New CLI tags so the new experiments don't collide with v2.7:
| Variant | Filename suffix |
|---|---|
| Default (literature IC, no POC_SUB) | _pinn3.0.json (no extra suffix) |
| Darwin IC only | _pinn3.0_darwinic.json |
| Darwin IC + POC_SUB_W=W | _pinn3.0_darwinic_pocsubW.json |
| Darwin IC, alternate cache | _pinn3.0_<DARWIN_IC_TAG>_pocsubW.json |
JSON output also carries use_darwin_ic and poc_sub_w keys for downstream provenance.
Bimodal degeneracy¶
Sweeping POC_SUB_W at the (Darwin IC, sub_w=1.0) anchor cleanly switches the optimizer between two loss-surface basins:
| POC_SUB_W | n="alpfe-correct" basin | n="scav-correct" basin | scav_rat Excellents | Aggregate 4+/6 |
|---|---|---|---|---|
| 0.0 | 10/10 | 0/10 | 0 | 1/10 |
| 0.3 | 9/10 | 1/10 | 1 | 1/10 |
| 1.0 | 8/10 | 2/10 | 1 | 2/10 |
| 3.0 | 3/10 | 7/10 | 4 | 6/10 |
Definition: a seed lands in the "scav-correct" basin if its recovered scav_rat is within 50 % of Carroll's 6.025e-7.
Interpretation. The v2.7 box-model + observation set supports two distinct local minima of the loss:
-
alpfe-correct basin (alpfe ≈ 0.9, scav_rat ≈ 1-2e-7 — Drifted): the optimizer matches surface FeT magnitude by tuning the dust-iron source, then leans on a small particle scavenging rate to balance the iron budget. Reaches 10/10 Cal-grade on alpfe (7/10 Excellent at the sub_w=1.0 anchor) but never reaches Cal-grade on scav_rat.
-
scav-correct basin (alpfe ≈ 0.2-0.3 — Drifted, scav_rat ≈ 6-7e-7 — Excellent / Cal-grade): the optimizer matches surface FeT pattern (not magnitude) with a small dust source and a large scavenging rate that produces the right iron sink at depth. Reaches 4/10 Excellent on scav_rat but never reaches Cal-grade on alpfe.
Carroll's published calibration sits at the joint constraint (alpfe=0.928, scav_rat=6.025e-7) — both correct — which is not a local minimum of our loss with the current 6-parameter / ~11-target structure. It would require the loss surface to have a single minimum that satisfies both, which is identifiability-blocked unless additional observational constraints disambiguate the two basins.
POC_SUB_W is the lever that chooses which basin the optimizer falls into. Higher weights push the optimizer to match Darwin's L2 POC pattern, which is what scav_rat × DFe × POC is supposed to produce — so the scav-correct basin gets selected.
This is the first time the project has produced direct empirical evidence of a bimodal degeneracy in the alpfe ↔ scav_rat axis. Prior work (v2.2 closeout, v2.6 GEOTRACES hybrid, v2.7 2-layer box) all reported the "alpfe Cal-grade / scav_rat Drifted" basin as if it were the only one available.
Best single seed¶
Seed 0 at the v2.8 anchor (Darwin IC, sub_w=1.0, POC_SUB_W=3.0) is the closest single seed to "both Cal-grade" in the project so far:
| Parameter | Recovered | Carroll | rel_off | Band |
|---|---|---|---|---|
| alpfe | 0.686 | 0.928 | 0.261 | Cal-grade |
| scav_rat | 6.18e-7 | 6.025e-7 | 0.026 | Excellent |
| Smallgrow | 0.634 | 0.661 | 0.041 | Excellent |
| Biggrow | 0.316 | 0.431 | 0.267 | Cal-grade |
| diatomgraz | 0.054 | 0.830 | 0.934 | Drifted |
| R_PICPOC | 0.00651 | 0.0424 | 0.847 | Drifted |
4/6 Cal-grade (2 Excellent including scav_rat); diatomgraz collapsed but the alpfe + scav_rat pair landed Cal-grade or better simultaneously. First seed in the project to do this.
What's still stuck¶
R_PICPOC stays at 0/10 Cal-grade across every POC_SUB_W tested (0, 0.3, 1.0, 3.0). The Darwin IC moved its sign (overshoot → undershoot) compared to literature IC but didn't reduce its absolute offset. The structural argument from v2.6 stands: R_PICPOC needs independent PIC observations as a loss term; the current observable set (surface bin-average tracers + sparse GEOTRACES DFe + Darwin L2 POC) doesn't differentiate the PIC/POC stoichiometric ratio from PIC dynamics.
alpfe in the scav-correct basin is Drifted. This is identifiability-mode, not a model failure: the basin exists because two parameter combinations explain the surface observations equally well. Disambiguating requires either:
- A new observable that's sensitive to dust-iron source (e.g., aerosol Fe deposition data colocated with the AOI)
- Cross-regime training (v3.0 multi-AOI) where iron-replete N Atlantic forces a different alpfe-scav_rat balance than Eq Pacific HNLC
- Time-resolved fitting (cluster-gated) where the seasonal cycle of dust deposition vs scavenging adds dynamical contrast
Reproducing¶
# 1. Build the Darwin v5 IC cache (one-time, ~5 s; requires the 10.94 GB
# pickup file at D:/ecco_darwin_v5/input/darwin_initial_conditions/).
python scripts/build_darwin_ic_cache.py
# 2. Headline scav-correct experiment (n=10 seeds, ~2 min on RTX 5090).
NB23_SEEDS=0,1,2,3,4,5,6,7,8,9 \
DARWIN_IC=1 POC_SUB_W=3.0 \
GEOTRACES_W=0.3 GEOTRACES_SUB_W=1.0 NB23_PINN_WEIGHT=3.0 \
python scripts/run_v2.7_multilayer_batched.py
# 3. alpfe-correct comparison config (n=10, ~2 min).
NB23_SEEDS=0,1,2,3,4,5,6,7,8,9 \
DARWIN_IC=1 \
GEOTRACES_W=0.3 GEOTRACES_SUB_W=1.0 NB23_PINN_WEIGHT=3.0 \
python scripts/run_v2.7_multilayer_batched.py
JSONs land in scripts/run_v2.7_multilayer_result_*.json with the _darwinic / _pocsubW* tags.
Limitations / next steps¶
- Only one AOI tested (Eq Pacific). The bimodal degeneracy might collapse under multi-AOI training where the iron-replete N Atlantic subpolar regime disambiguates alpfe from scav_rat. This is the v3.0 multi-AOI design (PR #44 merged 2026-05-17).
- Darwin self-target POC, not real obs. Swapping in GEOTRACES POC (LPT + SPT particulate) is straightforward; the loader already exposes the variables.
- No block-CV check yet. The seed-0 "alpfe + scav_rat both Cal-grade" result is single-AOI single-split — needs spatial cross-validation before claiming structural identifiability.
- No prognostic validation. Recovered v2.8 parameters haven't been run forward through Darwin v05 to check long-term stability. Cluster-gated (Brenowitz & Bretherton 2018 §3.4).
- PIC observations remain the R_PICPOC unblock. Same conclusion as v2.6, unchanged by v2.8.