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.
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.
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).
Stage-1 fit to SNODAS SWE, then frozen — feeds a physically-consistent liquid-water signal downstream.
Stage-2 carries the parameters we calibrate and regionalize. Monthly runoff is the training signal (routing washes out monthly).
Stage-3 uses the EF5-Global KW prior as-is; daily streamflow tells us how much routing adds.
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
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.
median SWE KGE across the snow domain
Prior λ=200 cuts bound-railing 54–76%→<3% at only −0.036 KGE. Calibration adds ~+0.6 per-cell over defaults.
Liquid input drives Stage-2; snow & snow-free basins score alike downstream (0.66 vs 0.70) — coupling is sound.
θ_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).
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.
median monthly KGE, calibrated
temporal holdout (unseen years)
~4,200 free multipliers across all 424 basins, ~1 h GPU. Biggest lever: interannual Oudin PET (r 0.59→0.78).
A shared MLP g(AlphaEarth 64-dim embedding) → the ten multipliers, trained end-to-end through the water balance.
| Ungauged test | med KGE | >0 |
|---|---|---|
| PUB (212 held-out, ensemble) | 0.65 | 90% |
| Independent 295 Reference, zero-shot | 0.52 | 86% |
| vs nearest-basin transfer | 0.45 | 74% |
| vs a-priori only | −1.31 | 11% |
~80% of gauged skill at ungauged sites. Finding: the embedding alone suffices; ensembling was the one reliable lever.
Forward-only: calibrated runoff → EF5-Global KW prior (0.05°, α₀/α/β, D8, zero calibration) → daily gauge Q.
| Engine | daily KGE* | Δ |
|---|---|---|
| No routing | 0.503 | — |
| Dynamic KW prior | 0.549 | +0.02 |
| Travel-time UH | 0.564 | +0.03 |
The CONUS pipeline is the dress rehearsal; every ingredient has a global counterpart already in hand.
Companion pages: Stage 1 — Snow · Stage 2 — Water balance & dPL