diff --git a/.claude/PHYSICS-CORRECTIONS.md b/.claude/PHYSICS-CORRECTIONS.md index 53f5665..9a8d920 100644 --- a/.claude/PHYSICS-CORRECTIONS.md +++ b/.claude/PHYSICS-CORRECTIONS.md @@ -1,8 +1,17 @@ # `ddr_match` — the two physics paths -`params.ddr_match: bool` (default **true**) selects which forward chain runs. -`true` reproduces DDR bit-for-bit and keeps `compare_ddr_sandbox` an ABSOLUTE -MATCH (invariant 1). `false` enables the corrected physics. +**DEPRECATED (2026-08-19): default flipped to `false`.** DDR ported these +corrections in DeepGroundwater/ddr#192 as a clean break (no flag), so the +corrected physics is now what BOTH implementations run by default, the sandbox +fixture is regenerated from post-#192 DDR, and `compare_ddr_sandbox` holds its +ABSOLUTE MATCH on the corrected path (verified 2026-08-19: max abs 1.5e-5 m³/s, +f32 floor). Setting `ddr_match: true` emits a deprecation WARN; the legacy path +remains only to reproduce pre-#192 results and for `use_cuda_graphs: true` +(whose captured kernel hardcodes the legacy 5/3 celerity), and will be removed. + +`params.ddr_match: bool` selects which forward chain runs. +`true` reproduces PRE-#192 DDR bit-for-bit. `false` (default) enables the +corrected physics. ``` forward_chain_inner (src/routing/mmc_op.rs) diff --git a/.claude/skills/ddrs-dev/SKILL.md b/.claude/skills/ddrs-dev/SKILL.md index 0828eb7..f6faea3 100644 --- a/.claude/skills/ddrs-dev/SKILL.md +++ b/.claude/skills/ddrs-dev/SKILL.md @@ -112,6 +112,7 @@ Full commands and rationale in `references/testing.md`. | `src/nn/`, `Cargo.toml` rskan tag | **B** | 4-test KAN fixture sweep, then Tier A | | `src/config.rs`, `src/training/`, other `src/` | **C** | `cargo test --lib`, `cargo test`, `compare_ddr_sandbox` | | `config/**/*.yaml` only | **D** | `ddrs plan --config … --workspace …` exits 0, no drift | +| `examples/juniata/**` (bundle, config, README) | — | `cargo test --test juniata_bundle` (never skips — bundle is committed), and `ddrs --config examples/juniata/ddrs.yaml plan` exits 0 from the repo root | | Plotting / analysis scripts only | — | no gate | | `epochs`, `learning_rate`, `batch_size`, loss weights within documented ranges | — | no gate | @@ -151,6 +152,34 @@ warm start: no .mpk — Adam starts cold There is no `"precip loading"` string — two retired skills told you to grep for it. +## Juniata single-catchment sample (`examples/juniata/`) + +The fastest full end-to-end exercise of the CLI, and the mirror of DDR's +`examples/juniata` (DeepGroundwater/ddr PR #193): one gauge (USGS 01567000, +8,657 km², 213 reaches), 8.9 MB committed bundle, no external stores or CUDA. +Run **from the repo root** (data paths in the config are repo-root-relative); +the workspace intentionally lands beside the config at `examples/juniata/.ddrs/` +(gitignored) — the one sanctioned exception to fact 2 above. + +```bash +target/release/ddrs --config examples/juniata/ddrs.yaml plan +target/release/ddrs --config examples/juniata/ddrs.yaml run --workflow train-and-test --backend cpu +``` + +Verified 2026-08-19: `plan` baseline NSE 0.695 / KGE 0.819 (matches DDR's +Python readers to rounding — a live cross-implementation check); 30-epoch CPU +train-and-test finishes in ~21 s at routed NSE 0.790 / KGE 0.881 vs DDR-Python's +0.784 / 0.877 (residual = window-sampling RNG streams only; exact match is +impossible by construction; ddrs 4-seed spread NSE 0.790–0.800). Both examples +run the corrected physics — since 2026-08-19 `ddr_match` is DEPRECATED and +defaults to `false`, matching DDR post-#192. (Historical: with legacy physics +this gauge scored 0.840 / 0.913 — *better* here, but not the same model.) +Two deviations from DDR's bundle: `data/statistics/*.json` is **committed** +(ddrs never recomputes statistics), and `.gitignore` carries +`!examples/juniata/ddrs.yaml` so the example config survives the global +`ddrs.yaml` ignore. Regenerate the bundle in the ddr repo +(`extract_bundle.py`), then re-copy `data/` plus the generated statistics JSON. + ## Maintenance This skill and `ddrs-eval-plots` are the only two skills in this repo. When a run, diff --git a/.claude/skills/ddrs-dev/references/build-and-env.md b/.claude/skills/ddrs-dev/references/build-and-env.md index 2a0f9ff..84e3f3f 100644 --- a/.claude/skills/ddrs-dev/references/build-and-env.md +++ b/.claude/skills/ddrs-dev/references/build-and-env.md @@ -46,10 +46,12 @@ Regenerate with: ```bash cd ~/projects/ddr && uv run python ~/projects/ddrs/scripts/export_ddr_sandbox.py ``` -> **Only the desktop's `~/projects/ddr` working tree is a valid reference.** It -> carries unpushed `geometry/trapezoidal.py` work. A fixture regenerated from a -> clean DDR clone diverges ~1% (max abs ≈ 0.55 m³/s) at *every* ddrs commit — -> that is a wrong reference, not a port bug. Details: +> **Any DDR checkout at or past DeepGroundwater/ddr#192 is a valid reference** +> (2026-08-19: fixture regenerated from post-#192 master; comparison matches at +> the f32 floor on the corrected physics, which is now ddrs's default — +> `ddr_match` is deprecated). The old rule — only the desktop's working tree +> with unpushed `geometry/trapezoidal.py` work — applied before #192 landed; +> a PRE-#192 clean clone still diverges ~1% (max abs ≈ 0.55 m³/s). Details: > `docs/reference/ddr-comparison.md` §Regenerating fixtures. **KAN parity** — `tests/fixtures/` (tracked, unlike `/fixtures/`), loaded behind diff --git a/.claude/skills/ddrs-dev/references/config.md b/.claude/skills/ddrs-dev/references/config.md index a501945..44f344a 100644 --- a/.claude/skills/ddrs-dev/references/config.md +++ b/.claude/skills/ddrs-dev/references/config.md @@ -144,7 +144,8 @@ Ten production `input_var_names`: `SoilGrids1km_clay`, `aridity`, `meanelevation | Key | Default | Notes | |---|---|---| | `sparse_solver` | `cpu` | On a non-CUDA backend, `cuda` **silently WARN-falls-back** to cpu. An unrecognized value **panics** | -| `use_cuda_graphs` | **false** | `config/merit_training.yaml` *sets* true — that is a YAML value, not the code default | +| `use_cuda_graphs` | false | Requires the DEPRECATED `ddr_match: true` (the captured kernel hardcodes the legacy 5/3 celerity); rejected alongside the corrected-physics default. `config/merit_training.yaml` set it true until 2026-08-19 | +| `ddr_match` | **false** (since 2026-08-19) | DEPRECATED. `true` = legacy pre-#192 DDR physics (5/3 celerity, X ≡ 0.3, upstream-cols readout) — parses with a WARN, kept only for pre-#192 reproduction and CUDA graphs. DDR itself runs the corrected physics since DeepGroundwater/ddr#192. See `.claude/PHYSICS-CORRECTIONS.md` | | `use_leakance` | false | | | `leakance_losing_only` | **true** | Clamps `head = max(0, depth − d_gw)`, so gaining reaches produce `zeta ≡ 0` | | `leakance_impervious_threshold` | 0.7 | Masks reaches whose `corridor_impervious` is **`>`** this value (not `≥`) | @@ -177,6 +178,7 @@ Four validators run at `Config::from_yaml_file`, plus one at dataset open. | | neither adjacency nor fabric | `"adjacency sources are missing"` | | | `geospatial_fabric_layer` on a non-gpkg | `"geospatial_fabric_layer"` + `".gpkg"` | | `validate_leakance` | `use_leakance` + `use_cuda_graphs` | both key names | +| `validate_ddr_match` | `use_cuda_graphs: true` without the deprecated `ddr_match: true` | `"use_cuda_graphs: true` requires the DEPRECATED `ddr_match: true"` | | `validate_disagg_pretrained` | `freeze: true` without `pretrained_checkpoint` | `"freeze: true requires pretrained_checkpoint"` | | `validate_grad_accum` | `grad_accum_steps: 0` | `"grad_accum_steps: 0"` | | | `use_grad_accum: true` with steps < 2 | `"requires grad_accum_steps: N with N >= 2"` | diff --git a/.claude/skills/ddrs-dev/references/traps.md b/.claude/skills/ddrs-dev/references/traps.md index 0b31e94..13fb7f9 100644 --- a/.claude/skills/ddrs-dev/references/traps.md +++ b/.claude/skills/ddrs-dev/references/traps.md @@ -60,9 +60,11 @@ worktree, silently replacing the worktree build. 4. `cargo test --test sparse_gradcheck`. **Also failing ⇒ the algorithm changed. V1-only ⇒ kernel ordering or arithmetic fusion.** -**Wrong-reference failure mode:** a fixture regenerated from a clean DDR clone -diverges ~1% (max abs ≈ 0.55 m³/s) at *every* ddrs commit. Only the desktop's -`~/projects/ddr` working tree is valid — see `build-and-env.md`. +**Wrong-reference failure mode:** a fixture regenerated from a PRE-#192 DDR +clone diverges ~1% (max abs ≈ 0.55 m³/s) at *every* ddrs commit. Since +2026-08-19 any DDR checkout at or past DeepGroundwater/ddr#192 is a valid +reference (fixture + comparison both on corrected physics) — see +`build-and-env.md`. ## T3 — CUDA graphs mask NaN diff --git a/.gitignore b/.gitignore index 718c273..01ea83a 100644 --- a/.gitignore +++ b/.gitignore @@ -50,3 +50,6 @@ __pycache__/ .ddrs/ .ddrs-synthetic-n-*/ ddrs.yaml +# …but the Juniata sample's config is a committed example, not a personal +# workspace config (its .ddrs/ workspace is still ignored by the rule above). +!examples/juniata/ddrs.yaml diff --git a/CLAUDE.md b/CLAUDE.md index f7ebe3f..92a8513 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -19,10 +19,13 @@ overview is in `~/projects/ddr/CLAUDE.md`. fixture is generated by `scripts/export_ddr_sandbox.py` run under DDR's `uv` venv. Re-run this benchmark after every change to `src/routing/`, `src/geometry.rs`, or `src/sparse/`. - **Caveat (2026-06-06):** the reference DDR state lives only in the - desktop's `~/projects/ddr` working tree (unpushed `geometry/trapezoidal.py` - work). A fixture regenerated from a clean DDR clone diverges ~1% at every - ddrs commit — wrong reference, not a port bug. See + **Update (2026-08-19):** the fixture is regenerated from DDR master + post-#192 (DDR ported ddrs's corrected physics as a clean break), and the + comparison now runs — and matches at the f32 floor — on the corrected + physics, which is ddrs's default since `ddr_match` was deprecated (default + flipped to `false`; see `.claude/PHYSICS-CORRECTIONS.md`). The old + 2026-06-06 "desktop-only reference" caveat is obsolete: any DDR checkout at + or past #192 is a valid reference. See `docs/reference/ddr-comparison.md` §Regenerating fixtures. 2. **f32 throughout the routing core.** No mixed precision. The DDR comparison sits at the f32 precision floor (~1e-7 rel diff per reach); any cast to diff --git a/config/merit_training.yaml b/config/merit_training.yaml index 0b16a90..f48469a 100644 --- a/config/merit_training.yaml +++ b/config/merit_training.yaml @@ -137,8 +137,10 @@ params: # backend; remove or set to cpu to fall back to the CPU solver). sparse_solver: cuda # Capture the routing forward pass as a CUDA graph and replay it each - # timestep — faster, CUDA backend only. - use_cuda_graphs: true + # timestep — faster, CUDA backend only. OFF because the captured kernel + # hardcodes the legacy (deprecated `ddr_match: true`) celerity; it cannot + # be used with the corrected physics that is now the default. + use_cuda_graphs: false # Testing-mode overlay: when mode: testing (workflow: eval), these keys # REPLACE the matching experiment: keys; absent keys inherit. diff --git a/docs/reference/ddr-comparison.md b/docs/reference/ddr-comparison.md index 82df1f9..7e4e994 100644 --- a/docs/reference/ddr-comparison.md +++ b/docs/reference/ddr-comparison.md @@ -152,7 +152,16 @@ If you regenerate fixtures, V1 must still pass. If it suddenly fails after regeneration, DDR's solver moved — investigate the DDR diff before touching ddrs. -### The desktop-only DDR reference (2026-06-06) +### The desktop-only DDR reference (2026-06-06) — OBSOLETE since 2026-08-19 + +> **Update (2026-08-19):** DDR merged the corrected physics +> (DeepGroundwater/ddr#192, porting ddrs's `ddr_match: false` path as a clean +> break), so the caveat below no longer binds. The V1 fixture is regenerated +> from post-#192 DDR master and `compare_ddr_sandbox` — now running the +> corrected physics, since `ddr_match` is deprecated with default `false` — +> reports ABSOLUTE MATCH at the f32 floor (max abs 1.5e-5 m³/s, max rel +> ~2e-7). Any DDR checkout at or past #192 is a valid reference. The section +> below is kept for interpreting PRE-#192 fixtures. > **The reference DDR state is NOT a pushed commit (as of 2026-06-06).** > The port mirrors the desktop checkout of `~/projects/ddr`, which diff --git a/examples/juniata/README.md b/examples/juniata/README.md new file mode 100644 index 0000000..eb77a03 --- /dev/null +++ b/examples/juniata/README.md @@ -0,0 +1,81 @@ +# Juniata single-catchment sample + +ddrs on one basin: the Juniata River at Newport, PA (USGS 01567000, +8,657 km², 213 MERIT reaches). Everything needed is in `data/` — no HPC, +S3, CUDA, or external stores. This is the ddrs mirror of DDR's +`examples/juniata` (DeepGroundwater/ddr PR #193); the two run on the +byte-identical bundle with the same hyperparameters, so results are +directly comparable across implementations. + +## Quickstart + +From the **repo root** (the config's data paths are repo-root-relative): + + cargo build --release --bin ddrs + target/release/ddrs --config examples/juniata/ddrs.yaml plan + target/release/ddrs --config examples/juniata/ddrs.yaml run --workflow train-and-test --backend cpu + +`plan` validates the bundle and prints the summed-Q' baseline (no learned +parameters — the bar routing has to beat). `run` trains the KAN head for +30 optimizer steps (one random 90-day window per epoch on a single gauge), +then evaluates over water years 1996–2010. The workspace lands beside the +config at `examples/juniata/.ddrs/` (gitignored); inspect a finished run +with: + + target/release/ddrs --config examples/juniata/ddrs.yaml show + +30 steps demonstrates learning and physically plausible parameters, not a +converged CONUS-grade model. The full train-and-test workflow takes well +under a minute on CPU. + +Reference results (CPU, 30 epochs, test 1995-10-01 – 2010-09-30): + +| | NSE | KGE | +|---|---|---| +| ddrs routed | 0.790 | 0.881 | +| DDR (Python) routed | 0.784 | 0.877 | +| summed-Q' baseline | 0.695 | 0.819 | + +The ddrs baseline reproduces DDR's (0.695 / 0.820) to rounding — the two +implementations read the same bundle through independent readers. The +routed numbers agree to well within sampling noise: the residual comes +only from the 30 random training windows being drawn from different RNG +streams (torch's global RNG vs ChaCha12), so exact-match is impossible by +construction. With 30 noisy single-gauge steps, expect seed-to-seed +spread in the routed metrics: ddrs seeds {42, 7, 123, 2026} scored NSE +0.790–0.800 / KGE 0.881–0.886. + +Both examples run the corrected physics (trapezoid-exact celerity β, +Cunge-matched X, own-reach gauge readout): DDR removed its legacy path in +PR #192, and ddrs deprecated `params.ddr_match` on 2026-08-19 — the +default is now `false` (corrected), so this config needs no flag at all. + +## What's in the bundle + +| File | Contents | +|---|---| +| `juniata_qprime.ic` | icechunk, `Qr(divide_id, time)` daily m³/s, 213 divides, 1980–2010 (dHBV2 UH retrospective) | +| `juniata_obs.ic` | icechunk, `streamflow(gage_id, time)` daily m³/s, USGS 01567000, 1980–2010 | +| `juniata_attributes.nc` | 10 KAN input attributes per COMID | +| `juniata_conus_adjacency.zarr` | binsparse COO subgraph + `length_m`, `slope`, `order` (compact 0..212 indexing, topologically ordered, lower-triangular) | +| `juniata_gages_adjacency.zarr` | single-gage COO group, same schema as the CONUS store | +| `juniata_gage.csv` | one-row gage metadata (gages_3000 schema) | +| `statistics/…json` | attribute normalization statistics over the 213 catchments | + +One deviation from DDR's bundle: `statistics/` is **committed** here. +DDR computes it on first run (`set_statistics`); ddrs never recomputes +statistics (`src/data/statistics.rs`), so the JSON ships with the bundle. + +Both icechunk stores start 1980-01-01 (required by the readers' +positional time origin). The bundle itself is regenerated from the CONUS +stores by DDR's `examples/juniata/extract_bundle.py` (maintainer-run); +this copy is taken verbatim from the ddr repo — regenerate there, then +re-copy `data/` and re-copy the generated statistics JSON. + +## Gate + +`tests/juniata_bundle.rs` asserts the bundle contract through the real +ddrs readers (213 reaches / 212 edges, lower-triangular adjacency, +1980-01-01 time origins, all 10 KAN inputs present). It runs in the +default `cargo test` sweep; if you touch the bundle or `src/data/`, make +sure it still passes. diff --git a/examples/juniata/data/juniata_attributes.nc b/examples/juniata/data/juniata_attributes.nc new file mode 100644 index 0000000..1dac7b8 Binary files /dev/null and b/examples/juniata/data/juniata_attributes.nc differ diff --git a/examples/juniata/data/juniata_conus_adjacency.zarr/indices_0/c/0 b/examples/juniata/data/juniata_conus_adjacency.zarr/indices_0/c/0 new file mode 100644 index 0000000..15842c9 Binary files /dev/null and b/examples/juniata/data/juniata_conus_adjacency.zarr/indices_0/c/0 differ diff --git a/examples/juniata/data/juniata_conus_adjacency.zarr/indices_0/zarr.json b/examples/juniata/data/juniata_conus_adjacency.zarr/indices_0/zarr.json new file mode 100644 index 0000000..17a0710 --- /dev/null +++ b/examples/juniata/data/juniata_conus_adjacency.zarr/indices_0/zarr.json @@ -0,0 +1,40 @@ +{ + "shape": [ + 212 + ], + "data_type": "int32", + "chunk_grid": { + "name": "regular", + "configuration": { + "chunk_shape": [ + 212 + ] + } + }, + "chunk_key_encoding": { + "name": "default", + "configuration": { + "separator": "/" + } + }, + "fill_value": 0, + "codecs": [ + { + "name": "bytes", + "configuration": { + "endian": "little" + } + }, + { + "name": "zstd", + "configuration": { + "level": 0, + "checksum": false + } + } + ], + "attributes": {}, + "zarr_format": 3, + "node_type": "array", + "storage_transformers": [] +} diff --git a/examples/juniata/data/juniata_conus_adjacency.zarr/indices_1/c/0 b/examples/juniata/data/juniata_conus_adjacency.zarr/indices_1/c/0 new file mode 100644 index 0000000..a26c26e Binary files /dev/null and b/examples/juniata/data/juniata_conus_adjacency.zarr/indices_1/c/0 differ diff --git a/examples/juniata/data/juniata_conus_adjacency.zarr/indices_1/zarr.json b/examples/juniata/data/juniata_conus_adjacency.zarr/indices_1/zarr.json new file mode 100644 index 0000000..17a0710 --- /dev/null +++ b/examples/juniata/data/juniata_conus_adjacency.zarr/indices_1/zarr.json @@ -0,0 +1,40 @@ +{ + "shape": [ + 212 + ], + "data_type": "int32", + "chunk_grid": { + "name": "regular", + "configuration": { + "chunk_shape": [ + 212 + ] + } + }, + "chunk_key_encoding": { + "name": "default", + "configuration": { + "separator": "/" + } + }, + "fill_value": 0, + "codecs": [ + { + "name": "bytes", + "configuration": { + "endian": "little" + } + }, + { + "name": "zstd", + "configuration": { + "level": 0, + "checksum": false + } + } + ], + "attributes": {}, + "zarr_format": 3, + "node_type": "array", + "storage_transformers": [] +} diff --git a/examples/juniata/data/juniata_conus_adjacency.zarr/length_m/c/0 b/examples/juniata/data/juniata_conus_adjacency.zarr/length_m/c/0 new file mode 100644 index 0000000..ba59609 Binary files /dev/null and b/examples/juniata/data/juniata_conus_adjacency.zarr/length_m/c/0 differ diff --git a/examples/juniata/data/juniata_conus_adjacency.zarr/length_m/zarr.json b/examples/juniata/data/juniata_conus_adjacency.zarr/length_m/zarr.json new file mode 100644 index 0000000..a2ff9a1 --- /dev/null +++ b/examples/juniata/data/juniata_conus_adjacency.zarr/length_m/zarr.json @@ -0,0 +1,40 @@ +{ + "shape": [ + 213 + ], + "data_type": "float32", + "chunk_grid": { + "name": "regular", + "configuration": { + "chunk_shape": [ + 213 + ] + } + }, + "chunk_key_encoding": { + "name": "default", + "configuration": { + "separator": "/" + } + }, + "fill_value": 0.0, + "codecs": [ + { + "name": "bytes", + "configuration": { + "endian": "little" + } + }, + { + "name": "zstd", + "configuration": { + "level": 0, + "checksum": false + } + } + ], + "attributes": {}, + "zarr_format": 3, + "node_type": "array", + "storage_transformers": [] +} diff --git a/examples/juniata/data/juniata_conus_adjacency.zarr/order/c/0 b/examples/juniata/data/juniata_conus_adjacency.zarr/order/c/0 new file mode 100644 index 0000000..2951732 Binary files /dev/null and b/examples/juniata/data/juniata_conus_adjacency.zarr/order/c/0 differ diff --git a/examples/juniata/data/juniata_conus_adjacency.zarr/order/zarr.json b/examples/juniata/data/juniata_conus_adjacency.zarr/order/zarr.json new file mode 100644 index 0000000..b8cb794 --- /dev/null +++ b/examples/juniata/data/juniata_conus_adjacency.zarr/order/zarr.json @@ -0,0 +1,40 @@ +{ + "shape": [ + 213 + ], + "data_type": "int32", + "chunk_grid": { + "name": "regular", + "configuration": { + "chunk_shape": [ + 213 + ] + } + }, + "chunk_key_encoding": { + "name": "default", + "configuration": { + "separator": "/" + } + }, + "fill_value": 0, + "codecs": [ + { + "name": "bytes", + "configuration": { + "endian": "little" + } + }, + { + "name": "zstd", + "configuration": { + "level": 0, + "checksum": false + } + } + ], + "attributes": {}, + "zarr_format": 3, + "node_type": "array", + "storage_transformers": [] +} diff --git a/examples/juniata/data/juniata_conus_adjacency.zarr/slope/c/0 b/examples/juniata/data/juniata_conus_adjacency.zarr/slope/c/0 new file mode 100644 index 0000000..e6d3bb2 Binary files /dev/null and b/examples/juniata/data/juniata_conus_adjacency.zarr/slope/c/0 differ diff --git a/examples/juniata/data/juniata_conus_adjacency.zarr/slope/zarr.json b/examples/juniata/data/juniata_conus_adjacency.zarr/slope/zarr.json new file mode 100644 index 0000000..a2ff9a1 --- /dev/null +++ b/examples/juniata/data/juniata_conus_adjacency.zarr/slope/zarr.json @@ -0,0 +1,40 @@ +{ + "shape": [ + 213 + ], + "data_type": "float32", + "chunk_grid": { + "name": "regular", + "configuration": { + "chunk_shape": [ + 213 + ] + } + }, + "chunk_key_encoding": { + "name": "default", + "configuration": { + "separator": "/" + } + }, + "fill_value": 0.0, + "codecs": [ + { + "name": "bytes", + "configuration": { + "endian": "little" + } + }, + { + "name": "zstd", + "configuration": { + "level": 0, + "checksum": false + } + } + ], + "attributes": {}, + "zarr_format": 3, + "node_type": "array", + "storage_transformers": [] +} diff --git a/examples/juniata/data/juniata_conus_adjacency.zarr/values/c/0 b/examples/juniata/data/juniata_conus_adjacency.zarr/values/c/0 new file mode 100644 index 0000000..4770798 Binary files /dev/null and b/examples/juniata/data/juniata_conus_adjacency.zarr/values/c/0 differ diff --git a/examples/juniata/data/juniata_conus_adjacency.zarr/values/zarr.json b/examples/juniata/data/juniata_conus_adjacency.zarr/values/zarr.json new file mode 100644 index 0000000..0a39132 --- /dev/null +++ b/examples/juniata/data/juniata_conus_adjacency.zarr/values/zarr.json @@ -0,0 +1,37 @@ +{ + "shape": [ + 212 + ], + "data_type": "uint8", + "chunk_grid": { + "name": "regular", + "configuration": { + "chunk_shape": [ + 212 + ] + } + }, + "chunk_key_encoding": { + "name": "default", + "configuration": { + "separator": "/" + } + }, + "fill_value": 0, + "codecs": [ + { + "name": "bytes" + }, + { + "name": "zstd", + "configuration": { + "level": 0, + "checksum": false + } + } + ], + "attributes": {}, + "zarr_format": 3, + "node_type": "array", + "storage_transformers": [] +} diff --git a/examples/juniata/data/juniata_conus_adjacency.zarr/zarr.json b/examples/juniata/data/juniata_conus_adjacency.zarr/zarr.json new file mode 100644 index 0000000..17b7a7d --- /dev/null +++ b/examples/juniata/data/juniata_conus_adjacency.zarr/zarr.json @@ -0,0 +1,12 @@ +{ + "attributes": { + "format": "COO", + "shape": [ + 213, + 213 + ], + "geodataset": "merit" + }, + "zarr_format": 3, + "node_type": "group" +} diff --git a/examples/juniata/data/juniata_gage.csv b/examples/juniata/data/juniata_gage.csv new file mode 100644 index 0000000..29cdb30 --- /dev/null +++ b/examples/juniata/data/juniata_gage.csv @@ -0,0 +1,2 @@ +STAID,STANAME,DRAIN_SQKM,LAT_GAGE,LNG_GAGE,COMID,COMID_DRAIN_SQKM,COMID_UNITAREA_SQKM,ABS_DIFF,DA_VALID,FLOW_SCALE +01567000,"Juniata River at Newport, PA",8657.286,40.478417,-77.129148,73005278,8683.350844552115,6.015237372026057,26.064844552114664,True,1.0 diff --git a/examples/juniata/data/juniata_gages_adjacency.zarr/01567000/indices_0/c/0 b/examples/juniata/data/juniata_gages_adjacency.zarr/01567000/indices_0/c/0 new file mode 100644 index 0000000..15842c9 Binary files /dev/null and b/examples/juniata/data/juniata_gages_adjacency.zarr/01567000/indices_0/c/0 differ diff --git a/examples/juniata/data/juniata_gages_adjacency.zarr/01567000/indices_0/zarr.json b/examples/juniata/data/juniata_gages_adjacency.zarr/01567000/indices_0/zarr.json new file mode 100644 index 0000000..17a0710 --- /dev/null +++ b/examples/juniata/data/juniata_gages_adjacency.zarr/01567000/indices_0/zarr.json @@ -0,0 +1,40 @@ +{ + "shape": [ + 212 + ], + "data_type": "int32", + "chunk_grid": { + "name": "regular", + "configuration": { + "chunk_shape": [ + 212 + ] + } + }, + "chunk_key_encoding": { + "name": "default", + "configuration": { + "separator": "/" + } + }, + "fill_value": 0, + "codecs": [ + { + "name": "bytes", + "configuration": { + "endian": "little" + } + }, + { + "name": "zstd", + "configuration": { + "level": 0, + "checksum": false + } + } + ], + "attributes": {}, + "zarr_format": 3, + "node_type": "array", + "storage_transformers": [] +} diff --git a/examples/juniata/data/juniata_gages_adjacency.zarr/01567000/indices_1/c/0 b/examples/juniata/data/juniata_gages_adjacency.zarr/01567000/indices_1/c/0 new file mode 100644 index 0000000..a26c26e Binary files /dev/null and b/examples/juniata/data/juniata_gages_adjacency.zarr/01567000/indices_1/c/0 differ diff --git a/examples/juniata/data/juniata_gages_adjacency.zarr/01567000/indices_1/zarr.json b/examples/juniata/data/juniata_gages_adjacency.zarr/01567000/indices_1/zarr.json new file mode 100644 index 0000000..17a0710 --- /dev/null +++ b/examples/juniata/data/juniata_gages_adjacency.zarr/01567000/indices_1/zarr.json @@ -0,0 +1,40 @@ +{ + "shape": [ + 212 + ], + "data_type": "int32", + "chunk_grid": { + "name": "regular", + "configuration": { + "chunk_shape": [ + 212 + ] + } + }, + "chunk_key_encoding": { + "name": "default", + "configuration": { + "separator": "/" + } + }, + "fill_value": 0, + "codecs": [ + { + "name": "bytes", + "configuration": { + "endian": "little" + } + }, + { + "name": "zstd", + "configuration": { + "level": 0, + "checksum": false + } + } + ], + "attributes": {}, + "zarr_format": 3, + "node_type": "array", + "storage_transformers": [] +} diff --git a/examples/juniata/data/juniata_gages_adjacency.zarr/01567000/order/c/0 b/examples/juniata/data/juniata_gages_adjacency.zarr/01567000/order/c/0 new file mode 100644 index 0000000..2951732 Binary files /dev/null and b/examples/juniata/data/juniata_gages_adjacency.zarr/01567000/order/c/0 differ diff --git a/examples/juniata/data/juniata_gages_adjacency.zarr/01567000/order/zarr.json b/examples/juniata/data/juniata_gages_adjacency.zarr/01567000/order/zarr.json new file mode 100644 index 0000000..b8cb794 --- /dev/null +++ b/examples/juniata/data/juniata_gages_adjacency.zarr/01567000/order/zarr.json @@ -0,0 +1,40 @@ +{ + "shape": [ + 213 + ], + "data_type": "int32", + "chunk_grid": { + "name": "regular", + "configuration": { + "chunk_shape": [ + 213 + ] + } + }, + "chunk_key_encoding": { + "name": "default", + "configuration": { + "separator": "/" + } + }, + "fill_value": 0, + "codecs": [ + { + "name": "bytes", + "configuration": { + "endian": "little" + } + }, + { + "name": "zstd", + "configuration": { + "level": 0, + "checksum": false + } + } + ], + "attributes": {}, + "zarr_format": 3, + "node_type": "array", + "storage_transformers": [] +} diff --git a/examples/juniata/data/juniata_gages_adjacency.zarr/01567000/values/c/0 b/examples/juniata/data/juniata_gages_adjacency.zarr/01567000/values/c/0 new file mode 100644 index 0000000..4770798 Binary files /dev/null and b/examples/juniata/data/juniata_gages_adjacency.zarr/01567000/values/c/0 differ diff --git a/examples/juniata/data/juniata_gages_adjacency.zarr/01567000/values/zarr.json b/examples/juniata/data/juniata_gages_adjacency.zarr/01567000/values/zarr.json new file mode 100644 index 0000000..0a39132 --- /dev/null +++ b/examples/juniata/data/juniata_gages_adjacency.zarr/01567000/values/zarr.json @@ -0,0 +1,37 @@ +{ + "shape": [ + 212 + ], + "data_type": "uint8", + "chunk_grid": { + "name": "regular", + "configuration": { + "chunk_shape": [ + 212 + ] + } + }, + "chunk_key_encoding": { + "name": "default", + "configuration": { + "separator": "/" + } + }, + "fill_value": 0, + "codecs": [ + { + "name": "bytes" + }, + { + "name": "zstd", + "configuration": { + "level": 0, + "checksum": false + } + } + ], + "attributes": {}, + "zarr_format": 3, + "node_type": "array", + "storage_transformers": [] +} diff --git a/examples/juniata/data/juniata_gages_adjacency.zarr/01567000/zarr.json b/examples/juniata/data/juniata_gages_adjacency.zarr/01567000/zarr.json new file mode 100644 index 0000000..af0dbcd --- /dev/null +++ b/examples/juniata/data/juniata_gages_adjacency.zarr/01567000/zarr.json @@ -0,0 +1,19 @@ +{ + "attributes": { + "format": "COO", + "shape": [ + 213, + 213 + ], + "geodataset": "merit", + "gage_catchment": 73005278, + "gage_idx": 212, + "data_types": { + "indices_0": "int32", + "indices_1": "int32", + "values": "uint8" + } + }, + "zarr_format": 3, + "node_type": "group" +} diff --git a/examples/juniata/data/juniata_gages_adjacency.zarr/zarr.json b/examples/juniata/data/juniata_gages_adjacency.zarr/zarr.json new file mode 100644 index 0000000..23547ba --- /dev/null +++ b/examples/juniata/data/juniata_gages_adjacency.zarr/zarr.json @@ -0,0 +1,5 @@ +{ + "attributes": {}, + "zarr_format": 3, + "node_type": "group" +} diff --git a/examples/juniata/data/juniata_obs.ic/chunks/0DHRH50DTQ4BTB55N0ZG b/examples/juniata/data/juniata_obs.ic/chunks/0DHRH50DTQ4BTB55N0ZG new file mode 100644 index 0000000..4891a77 Binary files /dev/null and b/examples/juniata/data/juniata_obs.ic/chunks/0DHRH50DTQ4BTB55N0ZG differ diff --git a/examples/juniata/data/juniata_obs.ic/chunks/789G7X07A1Z5S45ART8G b/examples/juniata/data/juniata_obs.ic/chunks/789G7X07A1Z5S45ART8G new file mode 100644 index 0000000..d58eeeb Binary files /dev/null and b/examples/juniata/data/juniata_obs.ic/chunks/789G7X07A1Z5S45ART8G differ diff --git a/examples/juniata/data/juniata_obs.ic/chunks/8TEVFRW9NDZWRQEP2NV0 b/examples/juniata/data/juniata_obs.ic/chunks/8TEVFRW9NDZWRQEP2NV0 new file mode 100644 index 0000000..8f16acb Binary files /dev/null and b/examples/juniata/data/juniata_obs.ic/chunks/8TEVFRW9NDZWRQEP2NV0 differ diff --git a/examples/juniata/data/juniata_obs.ic/chunks/B3E2REXD951T3WYPN7Y0 b/examples/juniata/data/juniata_obs.ic/chunks/B3E2REXD951T3WYPN7Y0 new file mode 100644 index 0000000..c49c76e Binary files /dev/null and b/examples/juniata/data/juniata_obs.ic/chunks/B3E2REXD951T3WYPN7Y0 differ diff --git a/examples/juniata/data/juniata_obs.ic/chunks/DN99EDAPNMZX34EAM2EG b/examples/juniata/data/juniata_obs.ic/chunks/DN99EDAPNMZX34EAM2EG new file mode 100644 index 0000000..c3b4f58 Binary files /dev/null and b/examples/juniata/data/juniata_obs.ic/chunks/DN99EDAPNMZX34EAM2EG differ diff --git a/examples/juniata/data/juniata_obs.ic/chunks/EC7YVW33SW9PJVNEZ2A0 b/examples/juniata/data/juniata_obs.ic/chunks/EC7YVW33SW9PJVNEZ2A0 new file mode 100644 index 0000000..9494796 Binary files /dev/null and b/examples/juniata/data/juniata_obs.ic/chunks/EC7YVW33SW9PJVNEZ2A0 differ diff --git a/examples/juniata/data/juniata_obs.ic/chunks/EZ6KCSYZSQNA0Z8GBWP0 b/examples/juniata/data/juniata_obs.ic/chunks/EZ6KCSYZSQNA0Z8GBWP0 new file mode 100644 index 0000000..5248d40 Binary files /dev/null and b/examples/juniata/data/juniata_obs.ic/chunks/EZ6KCSYZSQNA0Z8GBWP0 differ diff --git a/examples/juniata/data/juniata_obs.ic/chunks/JJH78M3V0938BNVYVZW0 b/examples/juniata/data/juniata_obs.ic/chunks/JJH78M3V0938BNVYVZW0 new file mode 100644 index 0000000..26d7b13 Binary files /dev/null and b/examples/juniata/data/juniata_obs.ic/chunks/JJH78M3V0938BNVYVZW0 differ diff --git a/examples/juniata/data/juniata_obs.ic/chunks/MJ4NHYC8YC3RTXAK5MGG b/examples/juniata/data/juniata_obs.ic/chunks/MJ4NHYC8YC3RTXAK5MGG new file mode 100644 index 0000000..8418a90 Binary files /dev/null and b/examples/juniata/data/juniata_obs.ic/chunks/MJ4NHYC8YC3RTXAK5MGG differ diff --git a/examples/juniata/data/juniata_obs.ic/chunks/PQ8K0MYSJ111QZFTCMG0 b/examples/juniata/data/juniata_obs.ic/chunks/PQ8K0MYSJ111QZFTCMG0 new file mode 100644 index 0000000..ef15477 Binary files /dev/null and b/examples/juniata/data/juniata_obs.ic/chunks/PQ8K0MYSJ111QZFTCMG0 differ diff --git a/examples/juniata/data/juniata_obs.ic/chunks/QWSMRF3W1JGWXAK16D3G b/examples/juniata/data/juniata_obs.ic/chunks/QWSMRF3W1JGWXAK16D3G new file mode 100644 index 0000000..4ffa498 Binary files /dev/null and b/examples/juniata/data/juniata_obs.ic/chunks/QWSMRF3W1JGWXAK16D3G differ diff --git a/examples/juniata/data/juniata_obs.ic/chunks/VE6N8FAJQVZ6Q51S2FV0 b/examples/juniata/data/juniata_obs.ic/chunks/VE6N8FAJQVZ6Q51S2FV0 new file mode 100644 index 0000000..466aa45 Binary files /dev/null and b/examples/juniata/data/juniata_obs.ic/chunks/VE6N8FAJQVZ6Q51S2FV0 differ diff --git a/examples/juniata/data/juniata_obs.ic/chunks/YYTDPXWZAZ7AFBDJS070 b/examples/juniata/data/juniata_obs.ic/chunks/YYTDPXWZAZ7AFBDJS070 new file mode 100644 index 0000000..02fd74e Binary files /dev/null and b/examples/juniata/data/juniata_obs.ic/chunks/YYTDPXWZAZ7AFBDJS070 differ diff --git a/examples/juniata/data/juniata_obs.ic/chunks/Z3Q96SB6D0EF4J3KE1NG b/examples/juniata/data/juniata_obs.ic/chunks/Z3Q96SB6D0EF4J3KE1NG new file mode 100644 index 0000000..83a10b4 Binary files /dev/null and b/examples/juniata/data/juniata_obs.ic/chunks/Z3Q96SB6D0EF4J3KE1NG differ diff --git a/examples/juniata/data/juniata_obs.ic/manifests/60HN44QGWFQX2CXSWK2G b/examples/juniata/data/juniata_obs.ic/manifests/60HN44QGWFQX2CXSWK2G new file mode 100644 index 0000000..a7ac9be Binary files /dev/null and b/examples/juniata/data/juniata_obs.ic/manifests/60HN44QGWFQX2CXSWK2G differ diff --git a/examples/juniata/data/juniata_obs.ic/manifests/CSZ1NYABTBPAKSJYG8W0 b/examples/juniata/data/juniata_obs.ic/manifests/CSZ1NYABTBPAKSJYG8W0 new file mode 100644 index 0000000..e104782 Binary files /dev/null and b/examples/juniata/data/juniata_obs.ic/manifests/CSZ1NYABTBPAKSJYG8W0 differ diff --git a/examples/juniata/data/juniata_obs.ic/manifests/V5Q86TCR3GYY2RQ32MHG b/examples/juniata/data/juniata_obs.ic/manifests/V5Q86TCR3GYY2RQ32MHG new file mode 100644 index 0000000..5f49b61 Binary files /dev/null and b/examples/juniata/data/juniata_obs.ic/manifests/V5Q86TCR3GYY2RQ32MHG differ diff --git a/examples/juniata/data/juniata_obs.ic/overwritten/repo.30716619265784.RGB2J2VH00V052RJT4WG b/examples/juniata/data/juniata_obs.ic/overwritten/repo.30716619265784.RGB2J2VH00V052RJT4WG new file mode 100644 index 0000000..5ae315e Binary files /dev/null and b/examples/juniata/data/juniata_obs.ic/overwritten/repo.30716619265784.RGB2J2VH00V052RJT4WG differ diff --git a/examples/juniata/data/juniata_obs.ic/repo b/examples/juniata/data/juniata_obs.ic/repo new file mode 100644 index 0000000..4633e90 Binary files /dev/null and b/examples/juniata/data/juniata_obs.ic/repo differ diff --git a/examples/juniata/data/juniata_obs.ic/snapshots/1CECHNKREP0F1RSTCMT0 b/examples/juniata/data/juniata_obs.ic/snapshots/1CECHNKREP0F1RSTCMT0 new file mode 100644 index 0000000..fe20bae Binary files /dev/null and b/examples/juniata/data/juniata_obs.ic/snapshots/1CECHNKREP0F1RSTCMT0 differ diff --git a/examples/juniata/data/juniata_obs.ic/snapshots/BPY4SW34FJGXWC3T806G b/examples/juniata/data/juniata_obs.ic/snapshots/BPY4SW34FJGXWC3T806G new file mode 100644 index 0000000..37f2c45 Binary files /dev/null and b/examples/juniata/data/juniata_obs.ic/snapshots/BPY4SW34FJGXWC3T806G differ diff --git a/examples/juniata/data/juniata_obs.ic/transactions/1CECHNKREP0F1RSTCMT0 b/examples/juniata/data/juniata_obs.ic/transactions/1CECHNKREP0F1RSTCMT0 new file mode 100644 index 0000000..2a1bf88 Binary files /dev/null and b/examples/juniata/data/juniata_obs.ic/transactions/1CECHNKREP0F1RSTCMT0 differ diff --git a/examples/juniata/data/juniata_obs.ic/transactions/BPY4SW34FJGXWC3T806G b/examples/juniata/data/juniata_obs.ic/transactions/BPY4SW34FJGXWC3T806G new file mode 100644 index 0000000..f63331c Binary files /dev/null and b/examples/juniata/data/juniata_obs.ic/transactions/BPY4SW34FJGXWC3T806G differ diff --git a/examples/juniata/data/juniata_qprime.ic/chunks/0CFGY0HBHEJ8B11HHP3G b/examples/juniata/data/juniata_qprime.ic/chunks/0CFGY0HBHEJ8B11HHP3G new file mode 100644 index 0000000..ce79ce2 Binary files /dev/null and b/examples/juniata/data/juniata_qprime.ic/chunks/0CFGY0HBHEJ8B11HHP3G differ diff --git a/examples/juniata/data/juniata_qprime.ic/chunks/4EBBN1BKRKRQWKS5JPTG b/examples/juniata/data/juniata_qprime.ic/chunks/4EBBN1BKRKRQWKS5JPTG new file mode 100644 index 0000000..b0eb350 Binary files /dev/null and b/examples/juniata/data/juniata_qprime.ic/chunks/4EBBN1BKRKRQWKS5JPTG differ diff --git a/examples/juniata/data/juniata_qprime.ic/chunks/4TY0DN35PHF0VY9SM1M0 b/examples/juniata/data/juniata_qprime.ic/chunks/4TY0DN35PHF0VY9SM1M0 new file mode 100644 index 0000000..7982227 Binary files /dev/null and b/examples/juniata/data/juniata_qprime.ic/chunks/4TY0DN35PHF0VY9SM1M0 differ diff --git a/examples/juniata/data/juniata_qprime.ic/chunks/52EXCSJT37V0G98PXEYG b/examples/juniata/data/juniata_qprime.ic/chunks/52EXCSJT37V0G98PXEYG new file mode 100644 index 0000000..701dd5c Binary files /dev/null and b/examples/juniata/data/juniata_qprime.ic/chunks/52EXCSJT37V0G98PXEYG differ diff --git a/examples/juniata/data/juniata_qprime.ic/chunks/77J4SE12GHQCNJAJ7MJ0 b/examples/juniata/data/juniata_qprime.ic/chunks/77J4SE12GHQCNJAJ7MJ0 new file mode 100644 index 0000000..c9a7336 Binary files /dev/null and b/examples/juniata/data/juniata_qprime.ic/chunks/77J4SE12GHQCNJAJ7MJ0 differ diff --git a/examples/juniata/data/juniata_qprime.ic/chunks/83F3F079V99RTSD234KG b/examples/juniata/data/juniata_qprime.ic/chunks/83F3F079V99RTSD234KG new file mode 100644 index 0000000..4ffb62f Binary files /dev/null and b/examples/juniata/data/juniata_qprime.ic/chunks/83F3F079V99RTSD234KG differ diff --git a/examples/juniata/data/juniata_qprime.ic/chunks/83Y22MZW9QT49B59F2Z0 b/examples/juniata/data/juniata_qprime.ic/chunks/83Y22MZW9QT49B59F2Z0 new file mode 100644 index 0000000..cd3c78d Binary files /dev/null and b/examples/juniata/data/juniata_qprime.ic/chunks/83Y22MZW9QT49B59F2Z0 differ diff --git a/examples/juniata/data/juniata_qprime.ic/chunks/8N1WMHZAF61N6TPFQX6G b/examples/juniata/data/juniata_qprime.ic/chunks/8N1WMHZAF61N6TPFQX6G new file mode 100644 index 0000000..132132e Binary files /dev/null and b/examples/juniata/data/juniata_qprime.ic/chunks/8N1WMHZAF61N6TPFQX6G differ diff --git a/examples/juniata/data/juniata_qprime.ic/chunks/8PR34AW86D1BEH0227Y0 b/examples/juniata/data/juniata_qprime.ic/chunks/8PR34AW86D1BEH0227Y0 new file mode 100644 index 0000000..03911a8 Binary files /dev/null and b/examples/juniata/data/juniata_qprime.ic/chunks/8PR34AW86D1BEH0227Y0 differ diff --git a/examples/juniata/data/juniata_qprime.ic/chunks/BCE1XP19KBFT7HPGRNSG b/examples/juniata/data/juniata_qprime.ic/chunks/BCE1XP19KBFT7HPGRNSG new file mode 100644 index 0000000..333eacc Binary files /dev/null and b/examples/juniata/data/juniata_qprime.ic/chunks/BCE1XP19KBFT7HPGRNSG differ diff --git a/examples/juniata/data/juniata_qprime.ic/chunks/BDBKEK1A2HQ7WHRY4DRG b/examples/juniata/data/juniata_qprime.ic/chunks/BDBKEK1A2HQ7WHRY4DRG new file mode 100644 index 0000000..f671d4c Binary files /dev/null and b/examples/juniata/data/juniata_qprime.ic/chunks/BDBKEK1A2HQ7WHRY4DRG differ diff --git a/examples/juniata/data/juniata_qprime.ic/chunks/D8GBRAF73C7DYE6VZEJ0 b/examples/juniata/data/juniata_qprime.ic/chunks/D8GBRAF73C7DYE6VZEJ0 new file mode 100644 index 0000000..0cce947 Binary files /dev/null and b/examples/juniata/data/juniata_qprime.ic/chunks/D8GBRAF73C7DYE6VZEJ0 differ diff --git a/examples/juniata/data/juniata_qprime.ic/chunks/DHX36G8FGY5XX17TZCC0 b/examples/juniata/data/juniata_qprime.ic/chunks/DHX36G8FGY5XX17TZCC0 new file mode 100644 index 0000000..2e3c18e Binary files /dev/null and b/examples/juniata/data/juniata_qprime.ic/chunks/DHX36G8FGY5XX17TZCC0 differ diff --git a/examples/juniata/data/juniata_qprime.ic/chunks/FCJ9HMMP12ZERHZ25ZAG b/examples/juniata/data/juniata_qprime.ic/chunks/FCJ9HMMP12ZERHZ25ZAG new file mode 100644 index 0000000..b925b7c Binary files /dev/null and b/examples/juniata/data/juniata_qprime.ic/chunks/FCJ9HMMP12ZERHZ25ZAG differ diff --git a/examples/juniata/data/juniata_qprime.ic/chunks/FZHYXX742JT2F6VVA910 b/examples/juniata/data/juniata_qprime.ic/chunks/FZHYXX742JT2F6VVA910 new file mode 100644 index 0000000..9f1a405 Binary files /dev/null and b/examples/juniata/data/juniata_qprime.ic/chunks/FZHYXX742JT2F6VVA910 differ diff --git a/examples/juniata/data/juniata_qprime.ic/chunks/H2B5X4Z6C4A5D68C20K0 b/examples/juniata/data/juniata_qprime.ic/chunks/H2B5X4Z6C4A5D68C20K0 new file mode 100644 index 0000000..c91aae5 Binary files /dev/null and b/examples/juniata/data/juniata_qprime.ic/chunks/H2B5X4Z6C4A5D68C20K0 differ diff --git a/examples/juniata/data/juniata_qprime.ic/chunks/H72KQX7HV3QY14Z79770 b/examples/juniata/data/juniata_qprime.ic/chunks/H72KQX7HV3QY14Z79770 new file mode 100644 index 0000000..9a2600d Binary files /dev/null and b/examples/juniata/data/juniata_qprime.ic/chunks/H72KQX7HV3QY14Z79770 differ diff --git a/examples/juniata/data/juniata_qprime.ic/chunks/N452E0YX5VBPKH903VK0 b/examples/juniata/data/juniata_qprime.ic/chunks/N452E0YX5VBPKH903VK0 new file mode 100644 index 0000000..60d708b Binary files /dev/null and b/examples/juniata/data/juniata_qprime.ic/chunks/N452E0YX5VBPKH903VK0 differ diff --git a/examples/juniata/data/juniata_qprime.ic/chunks/NN70EZDFPTBZZ7D6ZG0G b/examples/juniata/data/juniata_qprime.ic/chunks/NN70EZDFPTBZZ7D6ZG0G new file mode 100644 index 0000000..4b60e85 Binary files /dev/null and b/examples/juniata/data/juniata_qprime.ic/chunks/NN70EZDFPTBZZ7D6ZG0G differ diff --git a/examples/juniata/data/juniata_qprime.ic/chunks/PBM5GXDNEXXMMEK2EVV0 b/examples/juniata/data/juniata_qprime.ic/chunks/PBM5GXDNEXXMMEK2EVV0 new file mode 100644 index 0000000..05d9254 Binary files /dev/null and b/examples/juniata/data/juniata_qprime.ic/chunks/PBM5GXDNEXXMMEK2EVV0 differ diff --git a/examples/juniata/data/juniata_qprime.ic/chunks/QH21VZGSDF6D182DHPNG b/examples/juniata/data/juniata_qprime.ic/chunks/QH21VZGSDF6D182DHPNG new file mode 100644 index 0000000..13144b8 Binary files /dev/null and b/examples/juniata/data/juniata_qprime.ic/chunks/QH21VZGSDF6D182DHPNG differ diff --git a/examples/juniata/data/juniata_qprime.ic/chunks/RZ1J0XSPJGJDCERSA4GG b/examples/juniata/data/juniata_qprime.ic/chunks/RZ1J0XSPJGJDCERSA4GG new file mode 100644 index 0000000..bd0236c Binary files /dev/null and b/examples/juniata/data/juniata_qprime.ic/chunks/RZ1J0XSPJGJDCERSA4GG differ diff --git a/examples/juniata/data/juniata_qprime.ic/chunks/SVEGG3V2H3E49NP0XJD0 b/examples/juniata/data/juniata_qprime.ic/chunks/SVEGG3V2H3E49NP0XJD0 new file mode 100644 index 0000000..5e3ffc9 Binary files /dev/null and b/examples/juniata/data/juniata_qprime.ic/chunks/SVEGG3V2H3E49NP0XJD0 differ diff --git a/examples/juniata/data/juniata_qprime.ic/chunks/YYBTPVHCHECBGW6K5YGG b/examples/juniata/data/juniata_qprime.ic/chunks/YYBTPVHCHECBGW6K5YGG new file mode 100644 index 0000000..7babd0d Binary files /dev/null and b/examples/juniata/data/juniata_qprime.ic/chunks/YYBTPVHCHECBGW6K5YGG differ diff --git a/examples/juniata/data/juniata_qprime.ic/chunks/Z5A4BX3H286YX4DX2EDG b/examples/juniata/data/juniata_qprime.ic/chunks/Z5A4BX3H286YX4DX2EDG new file mode 100644 index 0000000..761fbce Binary files /dev/null and b/examples/juniata/data/juniata_qprime.ic/chunks/Z5A4BX3H286YX4DX2EDG differ diff --git a/examples/juniata/data/juniata_qprime.ic/chunks/ZTXYJE6S1S43R5TY8BK0 b/examples/juniata/data/juniata_qprime.ic/chunks/ZTXYJE6S1S43R5TY8BK0 new file mode 100644 index 0000000..938b1db Binary files /dev/null and b/examples/juniata/data/juniata_qprime.ic/chunks/ZTXYJE6S1S43R5TY8BK0 differ diff --git a/examples/juniata/data/juniata_qprime.ic/manifests/8GAWF9RGH6ABJBV6ZEBG b/examples/juniata/data/juniata_qprime.ic/manifests/8GAWF9RGH6ABJBV6ZEBG new file mode 100644 index 0000000..8551433 Binary files /dev/null and b/examples/juniata/data/juniata_qprime.ic/manifests/8GAWF9RGH6ABJBV6ZEBG differ diff --git a/examples/juniata/data/juniata_qprime.ic/manifests/CCVWS44T4203RGTZT7C0 b/examples/juniata/data/juniata_qprime.ic/manifests/CCVWS44T4203RGTZT7C0 new file mode 100644 index 0000000..9953862 Binary files /dev/null and b/examples/juniata/data/juniata_qprime.ic/manifests/CCVWS44T4203RGTZT7C0 differ diff --git a/examples/juniata/data/juniata_qprime.ic/manifests/NHTSEDN7G9V0SBVX32J0 b/examples/juniata/data/juniata_qprime.ic/manifests/NHTSEDN7G9V0SBVX32J0 new file mode 100644 index 0000000..c2dfcdb Binary files /dev/null and b/examples/juniata/data/juniata_qprime.ic/manifests/NHTSEDN7G9V0SBVX32J0 differ diff --git a/examples/juniata/data/juniata_qprime.ic/overwritten/repo.30716619265825.BMNC9EJZSCNY6DFD61KG b/examples/juniata/data/juniata_qprime.ic/overwritten/repo.30716619265825.BMNC9EJZSCNY6DFD61KG new file mode 100644 index 0000000..46e8247 Binary files /dev/null and b/examples/juniata/data/juniata_qprime.ic/overwritten/repo.30716619265825.BMNC9EJZSCNY6DFD61KG differ diff --git a/examples/juniata/data/juniata_qprime.ic/repo b/examples/juniata/data/juniata_qprime.ic/repo new file mode 100644 index 0000000..5d883a1 Binary files /dev/null and b/examples/juniata/data/juniata_qprime.ic/repo differ diff --git a/examples/juniata/data/juniata_qprime.ic/snapshots/1CECHNKREP0F1RSTCMT0 b/examples/juniata/data/juniata_qprime.ic/snapshots/1CECHNKREP0F1RSTCMT0 new file mode 100644 index 0000000..26eab89 Binary files /dev/null and b/examples/juniata/data/juniata_qprime.ic/snapshots/1CECHNKREP0F1RSTCMT0 differ diff --git a/examples/juniata/data/juniata_qprime.ic/snapshots/E0M3W6W1881H868V2KRG b/examples/juniata/data/juniata_qprime.ic/snapshots/E0M3W6W1881H868V2KRG new file mode 100644 index 0000000..8d2a5f6 Binary files /dev/null and b/examples/juniata/data/juniata_qprime.ic/snapshots/E0M3W6W1881H868V2KRG differ diff --git a/examples/juniata/data/juniata_qprime.ic/transactions/1CECHNKREP0F1RSTCMT0 b/examples/juniata/data/juniata_qprime.ic/transactions/1CECHNKREP0F1RSTCMT0 new file mode 100644 index 0000000..2a1bf88 Binary files /dev/null and b/examples/juniata/data/juniata_qprime.ic/transactions/1CECHNKREP0F1RSTCMT0 differ diff --git a/examples/juniata/data/juniata_qprime.ic/transactions/E0M3W6W1881H868V2KRG b/examples/juniata/data/juniata_qprime.ic/transactions/E0M3W6W1881H868V2KRG new file mode 100644 index 0000000..2525b3c Binary files /dev/null and b/examples/juniata/data/juniata_qprime.ic/transactions/E0M3W6W1881H868V2KRG differ diff --git a/examples/juniata/data/statistics/merit_attribute_statistics_juniata_attributes.nc.json b/examples/juniata/data/statistics/merit_attribute_statistics_juniata_attributes.nc.json new file mode 100644 index 0000000..59a4512 --- /dev/null +++ b/examples/juniata/data/statistics/merit_attribute_statistics_juniata_attributes.nc.json @@ -0,0 +1,82 @@ +{ + "SoilGrids1km_clay": { + "min": 21.88942939710815, + "max": 25.927380304831964, + "mean": 23.25540866395325, + "std": 0.5793661441872536, + "p10": 22.621687395389316, + "p90": 24.023412082349495 + }, + "aridity": { + "min": 0.8240336210476241, + "max": 1.0949050188064575, + "mean": 0.9853940571840696, + "std": 0.054801032514259024, + "p10": 0.916491804731276, + "p90": 1.0551865794355908 + }, + "meanelevation": { + "min": 158.3125, + "max": 648.0079097510373, + "mean": 367.43346241176107, + "std": 112.8865862244467, + "p10": 215.0853484674729, + "p90": 520.2894136437395 + }, + "meanP": { + "min": 963.36769985843, + "max": 1155.5321323587436, + "mean": 1033.724645425951, + "std": 43.781517822191255, + "p10": 981.4833231206057, + "p90": 1093.6917084659844 + }, + "NDVI": { + "min": 0.5437140727261884, + "max": 0.6253577860550769, + "mean": 0.5910866248195384, + "std": 0.01601110242956763, + "p10": 0.5702357023795875, + "p90": 0.6097918221016521 + }, + "meanslope": { + "min": 3.483990442443204, + "max": 13.024765014648438, + "mean": 8.293673689677012, + "std": 1.5976541733516987, + "p10": 6.29551649748618, + "p90": 10.40500724893326 + }, + "log10_uparea": { + "min": 1.4009894249646762, + "max": 3.938687348733695, + "mean": 2.235698558959507, + "std": 0.7257017773201839, + "p10": 1.4778819608622773, + "p90": 3.3387839489893048 + }, + "SoilGrids1km_sand": { + "min": 33.73611593619962, + "max": 40.39583206176758, + "mean": 38.312905934819184, + "std": 1.2344747152137185, + "p10": 36.41702057330096, + "p90": 39.62939344155507 + }, + "ETPOT_Hargr": { + "min": 932.6208064192432, + "max": 1064.862060546875, + "mean": 1015.5457412265196, + "std": 27.984803439336886, + "p10": 974.1416413034515, + "p90": 1045.6820321251641 + }, + "Porosity": { + "min": 0.0099999997764825, + "max": 0.2800000011920929, + "mean": 0.14840419738012917, + "std": 0.09329649160537543, + "p10": 0.0099999997764825, + "p90": 0.23813311077601854 + } +} \ No newline at end of file diff --git a/examples/juniata/ddrs.yaml b/examples/juniata/ddrs.yaml new file mode 100644 index 0000000..4a13230 --- /dev/null +++ b/examples/juniata/ddrs.yaml @@ -0,0 +1,112 @@ +# Juniata single-catchment sample — the ddrs mirror of DDR's +# examples/juniata (DeepGroundwater/ddr PR #193). +# +# One gauge: Juniata River at Newport, PA (USGS 01567000, 8,657 km², +# 213 MERIT reaches). The 8.9 MB bundle under examples/juniata/data/ is +# self-contained — no CONUS store, S3, or HPC access required. +# +# Run FROM THE REPO ROOT (data paths below are repo-root-relative): +# +# ddrs --config examples/juniata/ddrs.yaml plan +# ddrs --config examples/juniata/ddrs.yaml run --workflow train-and-test --backend cpu +# +# The workspace (.ddrs/) is created beside this file: examples/juniata/.ddrs/. +# +# Hyperparameters mirror DDR's examples/juniata/train_and_test.py::make_config +# — keep them in sync when comparing against the Python implementation. + +mode: training +workflow: train-and-test +geodataset: merit +device: 0 +seed: 42 +np_seed: 42 + +data_sources: + # Per-reach catchment attributes (NetCDF), subset to the 213 Juniata + # reaches. Normalization statistics live at + # data/statistics/merit_attribute_statistics_juniata_attributes.nc.json + # (found automatically; ddrs never recomputes statistics). + attributes: examples/juniata/data/juniata_attributes.nc + # Pre-built adjacency stores (COO zarr v3), re-indexed to a compact + # 0..212 space. Topologically ordered, lower-triangular. + conus_adjacency: examples/juniata/data/juniata_conus_adjacency.zarr + gages_adjacency: examples/juniata/data/juniata_gages_adjacency.zarr + # Daily lateral inflow (Q') per reach from the dHBV2 UH retrospective + # (icechunk store, 1980-01-01 origin). Interpolated to hourly internally. + streamflow: examples/juniata/data/juniata_qprime.ic + # USGS daily observed discharge at 01567000 (icechunk store). + observations: examples/juniata/data/juniata_obs.ic + # One-row gauge table: station ID + the MERIT COMID it sits on. + gages: examples/juniata/data/juniata_gage.csv + +experiment: + # Single gauge → one mini-batch (one random rho-day window) per epoch, + # so `epochs` is the optimizer-step count. 30 steps demonstrates + # learning; it is not a converged model. + batch_size: 1 + # Training window (water years 1982–1995). + start_time: 1981/10/01 + end_time: 1995/09/30 + epochs: 30 + rho: 90 + shuffle: true + warmup: 5 + learning_rate: + 1: 0.001 + 15: 0.0005 + grad_clip_max_norm: 1.0 + +kan_head: + hidden_size: 21 + num_hidden_layers: 2 + grid: 50 + k: 2 + input_var_names: + - SoilGrids1km_clay + - aridity + - meanelevation + - meanP + - NDVI + - meanslope + - log10_uparea + - SoilGrids1km_sand + - ETPOT_Hargr + - Porosity + learnable_parameters: + - n + - q_spatial + - p_spatial + +params: + parameter_ranges: + n: [0.015, 0.25] + q_spatial: [0.0, 1.0] + p_spatial: [1.0, 200.0] + attribute_minimums: + discharge: 1.0e-4 + slope: 1.0e-3 + velocity: 0.01 + depth: 0.01 + bottom_width: 0.01 + defaults: + p_spatial: 21.0 + log_space_parameters: + - p_spatial + # Hours the routed output is advanced before daily scoring (signed-at-zero + # convention; matches DDR's tau: 9). + tau: 9 + # Corrected physics (trapezoid-exact celerity β, Cunge-matched X, own-reach + # gauge readout) is the default — the same physics as DDR's Juniata example + # since DDR PR #192. + # CPU-friendly: no CUDA requirement anywhere in the sample. + sparse_solver: cpu + use_cuda_graphs: false + +# Evaluation overlay (water years 1996–2010). +# CAUTION — batch_size changes meaning: GAUGES in training, DAYS here. +testing: + start_time: 1995/10/01 + end_time: 2010/09/30 + batch_size: 15 + rho: null diff --git a/src/config.rs b/src/config.rs index 4e57b10..f9225e2 100644 --- a/src/config.rs +++ b/src/config.rs @@ -603,7 +603,13 @@ pub struct Params { /// supplied at routing setup (the mask is precomputed by the caller). /// Default 0.7 (70% impervious surface ≈ concrete-lined channel). pub leakance_impervious_threshold: f32, - /// When `true` (default) the routing core reproduces DDR's formulation + /// **DEPRECATED — default `false` since 2026-08-19; the legacy path will + /// be removed.** DDR ported the corrected physics in + /// DeepGroundwater/ddr#192 (a clean break, no flag), so `false` — the + /// corrected physics — is now the behaviour BOTH implementations share + /// and the DDR sandbox fixture is regenerated from post-#192 DDR. + /// + /// When `true`, the routing core reproduces DDR's PRE-#192 formulation /// bit-for-bit, including three known defects: /// * celerity `c = v · 5/3` (the wide-rectangular Kleitz-Seddon limit, /// ~22-27% high for the trapezoid this code actually builds), @@ -613,11 +619,10 @@ pub struct Params { /// gauge's own reach, which drops that reach's local drainage from /// every prediction (`src/data/collate.rs` step 5). /// - /// Set `false` to enable the corrected physics. The first two CHANGE - /// FORWARD OUTPUT and will break `examples/compare_ddr_sandbox`'s ABSOLUTE - /// MATCH (invariant 1) — which is why the default preserves DDR behaviour. - /// The `outflow_idx` correction is downstream of the solver and does NOT - /// affect the sandbox. See `.claude/PHYSICS-CORRECTIONS.md`. + /// The only remaining reasons to set `true`: reproducing a pre-#192 + /// result, or `use_cuda_graphs: true` (the captured kernel hardcodes the + /// legacy `5/3` celerity — see `validate_ddr_match`). Setting it emits a + /// deprecation WARN. See `.claude/PHYSICS-CORRECTIONS.md`. pub ddr_match: bool, /// Enforce the Muskingum non-negativity window `2X <= Cr <= 2(1-X)` /// (`Cr = dt/K`) on every reach-timestep, so the S27 solve can never @@ -638,7 +643,10 @@ pub struct Params { } fn default_ddr_match() -> bool { - true + // Corrected physics. Flipped from `true` on 2026-08-19 after + // DeepGroundwater/ddr#192 removed DDR's legacy path — both + // implementations now default to the same (corrected) formulation. + false } impl Default for Params { @@ -760,6 +768,14 @@ impl From for Params { p.leakance_impervious_threshold = v; } if let Some(b) = r.ddr_match { + if b { + eprintln!( + "WARN(config): `ddr_match: true` is DEPRECATED (legacy pre-#192 DDR \ + physics; DDR itself now runs the corrected formulation). It remains \ + only for reproducing old results and for `use_cuda_graphs: true`, \ + and will be removed in a future release." + ); + } p.ddr_match = b; } if let Some(b) = r.enforce_positivity { @@ -1006,11 +1022,13 @@ fn validate_leakance(cfg: &Config) -> std::result::Result<(), String> { fn validate_ddr_match(cfg: &Config) -> std::result::Result<(), String> { if !cfg.params.ddr_match && cfg.params.use_cuda_graphs { return Err( - "params: `ddr_match: false` requires `use_cuda_graphs: false` — the \ - CUDA-graph kernel hardcodes DDR's `5/3` celerity, so the corrected \ - forward would not be captured while the backward would use the corrected \ - chain rule, producing a silent forward/backward mismatch. \ - Set `use_cuda_graphs: false` to use `ddr_match: false`." + "params: `use_cuda_graphs: true` requires the DEPRECATED `ddr_match: true` \ + (legacy physics) — the CUDA-graph kernel hardcodes DDR's `5/3` celerity, so \ + the corrected forward would not be captured while the backward would use the \ + corrected chain rule, producing a silent forward/backward mismatch. \ + `ddr_match` now defaults to false (corrected physics). Set \ + `use_cuda_graphs: false` (recommended), or explicitly set the deprecated \ + `ddr_match: true`." .to_string(), ); } @@ -1209,9 +1227,13 @@ mod tests { assert_eq!(cfg.params.tau, 9); // sparse_solver is set to Cuda by merit_training.yaml (since SP-9). assert_eq!(cfg.params.sparse_solver, SparseSolver::Cuda); - // SP-10: merit_training.yaml now sets use_cuda_graphs: true - // (flipped by commit e35af29 after V7a=0.385 landed). - assert!(cfg.params.use_cuda_graphs); + // 2026-08-19: back to use_cuda_graphs: false — the captured kernel + // hardcodes the legacy (deprecated ddr_match: true) celerity, and the + // template runs the corrected-physics default. (SP-10 had flipped it + // true in e35af29 when legacy physics was the default.) + assert!(!cfg.params.use_cuda_graphs); + // ddr_match deprecated 2026-08-19: absent key ⇒ corrected physics. + assert!(!cfg.params.ddr_match); // top-level scalars. assert_eq!(cfg.seed, 42); assert_eq!(cfg.mode, "training"); diff --git a/tests/ddr_match_flag.rs b/tests/ddr_match_flag.rs index bebc4f4..213c7c8 100644 --- a/tests/ddr_match_flag.rs +++ b/tests/ddr_match_flag.rs @@ -1,9 +1,11 @@ -//! `ddr_match` defaults to true so every existing config and the DDR sandbox -//! parity example keep their current behaviour (invariant 1). +//! `ddr_match` is DEPRECATED and defaults to false (corrected physics) since +//! 2026-08-19 — DDR removed its legacy path in DeepGroundwater/ddr#192, so +//! both implementations now share the corrected formulation by default. The +//! legacy path stays reachable via an explicit (warned) `ddr_match: true`. use ddrs::config::Config; #[test] -fn ddr_match_defaults_to_true() { +fn ddr_match_defaults_to_false() { let yaml = r#" mode: training geodataset: merit @@ -18,18 +20,24 @@ params: let path = std::env::temp_dir().join("ddrs_ddr_match_default_test.yaml"); std::fs::write(&path, yaml).unwrap(); let cfg = Config::from_yaml_file(&path).expect("parse"); - assert!(cfg.params.ddr_match, "ddr_match must default to true"); + assert!( + !cfg.params.ddr_match, + "ddr_match must default to false (corrected physics, DDR post-#192)" + ); } #[test] -fn ddr_match_can_be_disabled() { +fn ddr_match_legacy_path_still_loads() { + // Deprecated but not removed: explicit true must still parse (it emits a + // WARN on stderr) so pre-#192 results stay reproducible and CUDA graphs + // stay usable. let yaml = r#" mode: training geodataset: merit seed: 42 np_seed: 42 params: - ddr_match: false + ddr_match: true parameter_ranges: n: [0.015, 0.25] q_spatial: [0.0, 1.0] @@ -38,7 +46,7 @@ params: let path = std::env::temp_dir().join("ddrs_ddr_match_disabled_test.yaml"); std::fs::write(&path, yaml).unwrap(); let cfg = Config::from_yaml_file(&path).expect("parse"); - assert!(!cfg.params.ddr_match); + assert!(cfg.params.ddr_match); } #[test] diff --git a/tests/juniata_bundle.rs b/tests/juniata_bundle.rs new file mode 100644 index 0000000..7eb6219 --- /dev/null +++ b/tests/juniata_bundle.rs @@ -0,0 +1,94 @@ +//! Contract test for the committed Juniata sample bundle +//! (`examples/juniata/data/`, mirrored from DeepGroundwater/ddr PR #193). +//! +//! Asserts the bundle through the REAL readers — the same code paths +//! `ddrs plan` / `ddrs run` use — so a bundle regeneration or a reader +//! change that breaks the sample fails `cargo test`, not a fresh user's +//! first run. Unlike the live-store tests, this never skips: the bundle +//! is committed in-repo. + +use ddrs::config::Config; +use ddrs::data::{AttrStats, ConusAdjacencyStore, GagesAdjacencyStore, MeritGagesDataset, Staid}; + +const BUNDLE: &str = "examples/juniata/data"; +const GAGE: &str = "01567000"; + +#[test] +fn conus_adjacency_is_213_reaches_lower_triangular() { + let store = ConusAdjacencyStore::open(format!("{BUNDLE}/juniata_conus_adjacency.zarr")) + .expect("open bundle conus adjacency"); + + // Re-indexed compact 0..212 space: 213 reaches, 212 edges (a tree). + assert_eq!(store.n, 213); + assert_eq!(store.nnz, 212); + assert_eq!(store.order.len(), store.n); + assert_eq!(store.length_m.len(), store.n); + assert_eq!(store.slope.len(), store.n); + + // Topological, lower-triangular: downstream row >= upstream col. + let violations = store + .indices_0 + .iter() + .zip(&store.indices_1) + .filter(|(r, c)| r < c) + .count(); + assert_eq!(violations, 0, "{violations} edges violate row >= col"); +} + +#[test] +fn gage_subgraph_covers_the_whole_bundle() { + let conus = ConusAdjacencyStore::open(format!("{BUNDLE}/juniata_conus_adjacency.zarr")) + .expect("open bundle conus adjacency"); + let gages = GagesAdjacencyStore::open( + format!("{BUNDLE}/juniata_gages_adjacency.zarr"), + &[Staid::from(GAGE)], + ) + .expect("open bundle gages adjacency"); + + let sub = gages.subgraphs.get(&Staid::from(GAGE)).expect("01567000 subgraph present"); + assert!(!sub.is_headwater(), "Juniata gauge must not be filtered as a headwater"); + // Single-gauge bundle: the upstream set IS the whole 213-reach network. + assert_eq!(sub.upstream_comids(&conus).len(), 213); +} + +#[test] +fn statistics_cover_all_ten_kan_inputs() { + let stats = AttrStats::open(format!( + "{BUNDLE}/statistics/merit_attribute_statistics_juniata_attributes.nc.json" + )) + .expect("open bundle attribute statistics"); + + let vars: Vec = [ + "SoilGrids1km_clay", + "aridity", + "meanelevation", + "meanP", + "NDVI", + "meanslope", + "log10_uparea", + "SoilGrids1km_sand", + "ETPOT_Hargr", + "Porosity", + ] + .iter() + .map(|s| s.to_string()) + .collect(); + + let means = stats.means_f32(&vars); + let stds = stats.stds_f32(&vars); + for (i, v) in vars.iter().enumerate() { + assert!(means[i].is_finite(), "mean for {v} not finite"); + assert!(stds[i].is_finite() && stds[i] > 0.0, "std for {v} not usable"); + } +} + +/// End-to-end: the shipped example config opens through the full dataset +/// pipeline (attributes + statistics + both icechunk stores + gage CSV + +/// the training filter), exactly as `ddrs run` would. +#[test] +fn example_config_opens_the_dataset() { + let cfg = Config::from_yaml_file("examples/juniata/ddrs.yaml").expect("load example config"); + let ds = MeritGagesDataset::open(&cfg).expect("open Juniata dataset"); + assert_eq!(ds.len(), 1); + assert_eq!(ds.staids(), &[Staid::from(GAGE)]); +} diff --git a/tests/leakance_off_parity.rs b/tests/leakance_off_parity.rs index d7d66dc..bd6fa22 100644 --- a/tests/leakance_off_parity.rs +++ b/tests/leakance_off_parity.rs @@ -113,7 +113,12 @@ fn leakance_none_matches_baseline_chain() { let device = TestDevice::default(); let n = 5usize; let t = 24usize; - let cfg = mock_config(); + // EXPECTED was captured under the legacy physics (then the default; since + // 2026-08-19 `ddr_match` defaults to false) — pin the legacy path so the + // fixture keeps guarding the exact chain it was captured from. Recapture + // under corrected physics when the legacy path is removed. + let mut cfg = mock_config(); + cfg.params.ddr_match = true; let mut mc = MuskingumCunge::::new(cfg, device.clone()); mc.setup_inputs(