Skip to content

Add precip module + SCS-CN equation: end-to-end rainfall->runoff pipeline#14

Merged
rehsani merged 1 commit into
mainfrom
step-7-precip-and-scs-cn
May 4, 2026
Merged

Add precip module + SCS-CN equation: end-to-end rainfall->runoff pipeline#14
rehsani merged 1 commit into
mainfrom
step-7-precip-and-scs-cn

Conversation

@rehsani
Copy link
Copy Markdown
Owner

@rehsani rehsani commented May 4, 2026

Summary

  • New `floodpath.precip` module: `PrecipGrid` dataclass + `uniform_precip` helper. Symmetric with `landuse`/`soil` so future fetchers (ERA5 / IMERG / CHIRPS) slot in cleanly.
  • `floodpath.runoff.apply_scs_cn(cn, precip)` implements the SCS-CN equation `Q = (P - 0.2S)² / (P + 0.8S)` with the initial-abstraction threshold (P ≤ 0.2S → Q=0). Closes the rainfall-runoff loop for the project — the pipeline is now end-to-end without waiting on a precip data-source decision.

Why now (per discussion)

Building a uniform-precip input first lets us validate the SCS-CN math and the whole chain (landuse + soil → CN → Q) before tackling the precip data-source choice. When ERA5/IMERG/CHIRPS lands later, it returns a `PrecipGrid` and feeds the same `apply_scs_cn` — no consumer change.

Resolution alignment (the question that motivated this PR)

Each layer keeps its native resolution; `compute_curve_number` already upsamples HSG (~250 m) to the landuse grid (10 m) via nearest-neighbour. The eventual common grid for routing will be DEM 30 m; that reprojection step properly belongs to the routing module (next milestone after a real precip source).

Test plan

  • `pytest -m "not integration"` — 264 passed (240 prior + 24 new; 16 deselected integration)
  • SCS-CN equation pinned at canonical CN/P values: CN=89, P=100 → Q=70.21; CN=77, P=100 → Q=44.78; CN=100 → Q=P (water passes everything through)
  • Asymptotic limit: as P → ∞, Q → P − 1.2·S (verified for CN=80 at P=10000)
  • Monotonicity: higher CN → higher Q for the same rainfall
  • Nodata propagates: CN=0 cells produce NaN in Q (NOT 0), so unmodelled and zero-runoff cells stay distinguishable
  • Custom `initial_abstraction_ratio=0.05` (modern recalibration) gives more runoff than the NEH default 0.2
  • End-to-end smoke test on Robit Bata: 16 stages, including new stage 14 (uniform 100 mm → SCS-CN runoff). Mean Q = 67.5 mm, C = Q/P = 0.674 — physically meaningful for clay-rich cropland.

Notes

  • Default `lambda = 0.2` per NEH 630 Ch10. Pass `initial_abstraction_ratio=0.05` for the newer recalibration (Hawkins et al. 2002).
  • `RunoffGrid.precip_source` records the source string (`"uniform"` here) so downstream consumers can attribute results when we eventually mix precipitation sources.
  • Smoke test exposes `--uniform-precip-mm` (default 100 mm).

…line

Closes the rainfall-runoff loop with synthetic precipitation. floodpath
now turns ANY precipitation depth raster into a per-cell runoff depth
raster via SCS-CN, with no pending data-source decision blocking the
pipeline. Real precipitation fetchers (ERA5 / IMERG / CHIRPS) plug into
this same interface in a follow-up.

floodpath.precip:
- PrecipGrid: depth (mm) + georef + source attribution + optional
  (start, end) UTC window. total_volume_m3() helper for water-balance
  diagnostics.
- uniform_precip(bbox, transform, shape, depth_mm) and
  uniform_precip_like(grid, depth_mm) — synthetic spatially-flat input.

floodpath.runoff (extended):
- runoff.apply_scs_cn(cn, precip, initial_abstraction_ratio=0.2):
  the SCS-CN equation
      S  = 25400/CN - 254
      Ia = lambda * S
      Q  = (P - Ia)^2 / (P - Ia + S)  if P > Ia, else 0
  Defaults to NEH 630 Ch10's lambda=0.2; users can pass 0.05 for newer
  calibrations. CN nodata propagates as NaN in Q (so consumers can
  distinguish unmodelled cells from no-runoff cells).
- RunoffGrid: Q (mm) + georef + precip_source + stats() ignoring NaN.

Tests: 24 new — 6 for PrecipGrid + uniform_precip helpers, 18 for the
SCS-CN equation including pinned values at canonical CN/P pairs,
asymptotic limit (Q -> P - 1.2*S as P -> infinity), monotonicity in CN,
nodata propagation, custom lambda, and shape-mismatch errors. End-to-end
sanity: 100 mm uniform rain on the Robit Bata fixtures yields
mean Q = 67.5 mm, runoff coefficient C = Q/P = 0.674 (high — consistent
with the all-D HSG / cropland-dominant patch).
@rehsani rehsani merged commit 94c02b1 into main May 4, 2026
1 check passed
@rehsani rehsani deleted the step-7-precip-and-scs-cn branch May 4, 2026 18:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant