CREST-Global · progress briefing

A differentiable CREST:
from CONUS calibration to global parameters

Re-implementing the CREST distributed hydrologic model as a GPU-native, fully differentiable PyTorch program — so parameters can be learned by gradient descent and regionalized to ungauged basins worldwide, while staying byte-faithful to the EF5 C++ model.

Interim report · 2026-07-03 Three stages · Snow-17 → Water balance → Routing · tested on the continental U.S.

Motivation

Why differentiate CREST?

The parameter problem

CREST has ~10 spatial parameters per cell. Traditional calibration (SCE-UA, DREAM) is per-basin and needs 10⁵–10⁶ model runs — impossible to scale to the globe, and it leaves the 99% of ungauged land untouched.

The differentiable answer

If the model is differentiable, one backprop pass gives the gradient of a basin-loss w.r.t. every parameter at once. Then a small neural net g(attributes)→parameters can be trained end-to-end through the physics and applied anywhere — differentiable parameter learning (dPL).

Design commitments

Byte-faithful to EF5 C++GPU-native PyTorch Physics stays; only parameters are learned EF5 a-priori grids as the base map CONUS first, then global
Architecture

Three physical stages, one differentiable graph

① Snow-17
T, P → SWE, snowmelt + rain-through = liquid input. Temperature-index snowpack.
② Water balance
Liquid input, PET → cell runoff. VIC infiltration + soil store + interflow/GW reservoirs.
③ Routing
Cell runoff → gauge discharge. Kinematic wave along the river network.

Calibrated & frozen

Stage-1 fit to SNODAS SWE, then frozen — feeds a physically-consistent liquid-water signal downstream.

The learning core

Stage-2 carries the parameters we calibrate and regionalize. Monthly runoff is the training signal (routing washes out monthly).

Prior, evaluated daily

Stage-3 uses the EF5-Global KW prior as-is; daily streamflow tells us how much routing adds.

Engineering · pycrest

Writing CREST into GPU-native Python

Port, don't re-invent

Each EF5 C++ kernel is re-implemented as a vectorized PyTorch step and checked byte-for-byte against the original on identical forcing.

pycrest/
 ├ snow17_torch.py  # + numpy twin
 ├ crest_torch.py   # CT.step(p,sm,P,PET,dt)
 ├ route_kw        # stage-3 (in progress)
 └ params.py

Why it runs on GPU

The water balance is cell-independent → 146,250 CONUS cells evaluate in parallel. One joint calibration of all 424 basins × 14 years ≈ 460k cell-years/iteration, ~70 s/iter, converges in ~1 hour on one RTX 4500.

vs. days–weeks for per-basin DREAM over the same set. Gradient-safe powers, per-step NaN sanitization, per-month checkpointing made the gradients usable.

Stage 1 · Snow-17

A calibrated, physically-bounded snowpack

Stage-1 snow product
Final CONUS Snow-17 product: SWE test-KGE, snow-correction & melt factors, temperature correction (0.1°, WY2010–2024).
0.61

median SWE KGE across the snow domain

Physically bounded

Prior λ=200 cuts bound-railing 54–76%→<3% at only −0.036 KGE. Calibration adds ~+0.6 per-cell over defaults.

Frozen coupling

Liquid input drives Stage-2; snow & snow-free basins score alike downstream (0.66 vs 0.70) — coupling is sound.

Stage 2 · Water balance

Parameters = multiplier × EF5 a-priori

The EF5 multiplier scheme

θ_cell = m_basin × apriori_grid — we learn a small per-basin multiplier on the physically-derived EF5 base map, not raw parameters. Same scheme EF5 uses; it is what makes global transfer plausible.

Original CREST params: wm, b, im, fc, ke (fc frozen — unidentifiable monthly).

Process extensions (each earned by held-out gain)

GW reservoir φ, gwcquickflow qc precip multiplier pmloss fraction lf fast→GW recharge fgw

As correct process terms were added, the original multipliers relaxed toward 1 (wm 1.57→1.06, ke 2.35→1.48) — parameters becoming physical is evidence the structure is right.

Stage 2 · Result

Monthly runoff skill — 424 U.S. basins

Stage-2 result
Per-basin monthly-runoff KGE map & CDF, calibrated multiplier ranges, and sample hydrographs across regions (median KGE 0.79; 80% > 0.5).
0.786

median monthly KGE, calibrated

0.719

temporal holdout (unseen years)

One joint fit

~4,200 free multipliers across all 424 basins, ~1 h GPU. Biggest lever: interannual Oudin PET (r 0.59→0.78).

Stage 2 · Regionalization (dPL)

From 424 gauges to any basin

v2 parameter fields
Full-coverage v2 parameter fields: per-basin calibration inside gauged basins, dPL-ensemble multipliers from each cell's AlphaEarth embedding elsewhere.

A shared MLP g(AlphaEarth 64-dim embedding) → the ten multipliers, trained end-to-end through the water balance.

Ungauged testmed KGE>0
PUB (212 held-out, ensemble)0.6590%
Independent 295 Reference, zero-shot0.5286%
vs nearest-basin transfer0.4574%
vs a-priori only−1.3111%

~80% of gauged skill at ungauged sites. Finding: the embedding alone suffices; ensembling was the one reliable lever.

Stage 3 · Routing (fresh — 2026-07-03)

Does the EF5-Global KW prior help daily?

Stage-3 routing result
Left: daily timing-skill CDF (bias-removed) — KW & UH shift the whole distribution right of no-routing. Right: routing gain grows with river-network depth.

Forward-only: calibrated runoff → EF5-Global KW prior (0.05°, α₀/α/β, D8, zero calibration) → daily gauge Q.

Enginedaily KGE*Δ
No routing0.503
Dynamic KW prior0.549+0.02
Travel-time UH0.564+0.03
  • Works, small daily lever (+0.05 for the deepest nets).
  • UH ≈ KW → a cheap differentiable UH suffices.
  • Real gap is volumetric bias (β≈0.62), not timing.
The point of it all

Path to global CREST parameters

Why CONUS transfers

  • Same EF5-Global a-priori base map worldwide (Clark 2017, 5 km).
  • Same AlphaEarth embedding for every land pixel on Earth.
  • Multipliers relaxed toward 1 → base map largely unbiased; dPL learns a modest correction field.

Proposed global template

  • HydroBASINS L6/7 tiling (seamless, nested, ~1k–10k km²).
  • Per-unit basin-mean embedding → g → multipliers → one static parameter grid.
  • Gauged units (GRDC) keep calibrated multipliers; dPL fills the rest.
  • Train/apply granularity matches → PUB/independent numbers carry over.

The CONUS pipeline is the dress rehearsal; every ingredient has a global counterpart already in hand.

For discussion

Open points we'd like expert input on

1 · A-priori transfer. EF5-US and EF5-Global base maps differ (wm median 181 vs 87 mm) → CONUS multipliers won't transfer directly; global work needs re-calibration on the global base map. Right base, or harmonize base maps first?
2 · Process extensions vs CREST orthodoxy. We added φ/gwc/qc/pm/lf/fgw reservoirs. Acceptable within "CREST," or should timing live entirely in routing (EF5's under/leaki)?
3 · Monthly training, daily use. We calibrate on monthly runoff, evaluate daily. The daily bias (β≈0.62) suggests monthly under-constrains storm magnitude — calibrate daily directly?
4 · Routing scope. Daily gain +0.02 (→+0.05 big/deep nets). Worth a differentiable KW, or is a static UH sufficient until we target large rivers?
5 · Basin-boundary seams. Product keeps per-basin multipliers inside gauged basins, dPL elsewhere → visible seams (largest in lf/fgw). Ship accurate-patchy, smooth all-dPL, or both?
6 · Global gauges & attributes. Which reference network (GRDC, GSIM) and which attributes beyond AlphaEarth (aridity, snow fraction, lithology) matter most for global dPL?
Status & next · 2026-07-03

Where we are

Done

  • pycrest: Snow-17 + WB, GPU, byte-faithful
  • Stage-1 snow calibrated (0.61)
  • Stage-2 WB calibrated (0.786 / holdout 0.719)
  • dPL regionalization (PUB 0.65, indep 0.52)
  • v2 full-coverage CONUS parameter set
  • Stage-3 routing prior evaluated daily

In progress

  • Routing module into pycrest
  • Daily-bias diagnosis (β≈0.62)
  • Seam-free product variant

Next

  • Global base-map re-calibration
  • HydroBASINS + GRDC global dPL
  • Daily-scale calibration option
  • Differentiable UH/KW if warranted

Companion pages: Stage 1 — Snow · Stage 2 — Water balance & dPL

Differentiable CREST · CONUS→Global · interim briefing 2026-07-03