Skip to content

Add runoff module with SCS Curve Number derivation#13

Merged
rehsani merged 1 commit into
mainfrom
step-6-curve-number
May 4, 2026
Merged

Add runoff module with SCS Curve Number derivation#13
rehsani merged 1 commit into
mainfrom
step-6-curve-number

Conversation

@rehsani
Copy link
Copy Markdown
Owner

@rehsani rehsani commented May 4, 2026

Summary

  • New `floodpath.runoff` module: combines ESA WorldCover landuse with the NEH 630 Ch7 hydrologic soil group into a per-cell SCS Curve Number raster — the missing link between the soil + landuse data layers and the SCS-CN runoff equation.
  • Default `WORLDCOVER_NEH_CN` lookup uses NEH 630 Ch9 Table 9-1 typical values (AMC II) with documented WorldCover-to-NEH cover-type mappings; users can override per-class CN values for site-calibrated work.

What this enables

`CurveNumberGrid.potential_retention_mm()` returns `S = 25400/CN - 254` ready to feed into `Q = (P - 0.2S)² / (P + 0.8S)`. Once a rainfall source lands (next PR), the SCS-CN equation itself is a 5-line addition.

Module surface

Item What
`compute_curve_number(landuse, hsg, mapping=None, fallback=70)` Pure transformation. HSG (~250 m) is upsampled to landuse grid (10 m) via nearest-neighbour reproject.
`CurveNumberGrid` uint8 CN raster + `stats()` that ignore nodata + `potential_retention_mm()`
`WORLDCOVER_NEH_CN` Default 11×4 lookup (NEH 630 Ch9 Table 9-1)

Test plan

  • `pytest -m "not integration"` — 240 passed (211 prior + 29 new runoff tests; 16 deselected integration)
  • Table invariants: every WorldCover class has all four HSG letters, CN ∈ [30,100], CN monotone-increasing across A→D for every cover type
  • Pinned NEH-iconic values: cropland × D = 89, woods × A = 30, water = 100, snow/ice = 98
  • S formula round-trip: CN=100 → S=0, CN=77 → S=75.87 mm, CN=50 → S=254 mm exactly
  • Pinned Robit Bata CN derivation against committed fixtures: mean CN = 87.72, dominant value 89 (cropland on D), 809,271/810,000 cells classified
  • End-to-end smoke test on Robit Bata: 15 stages all succeed, including new CN plot at stage 13

Notes

  • WorldCover-to-NEH cover-type mappings (e.g. `built_up` → "Residential 1/4 acre") are documented in `runoff/constants.py` with a note that users with dense urban patches should override (e.g. with the NEH "Commercial" curve A=89/B=92/C=94/D=95).
  • 729 nodata cells in the Robit Bata CN raster come from the single SoilGrids HSG nodata pixel projected onto the 27× finer WorldCover grid — expected behaviour.

floodpath.runoff combines ESA WorldCover landuse with NEH 630 Ch7
hydrologic soil group into the per-cell SCS Curve Number raster needed
by the SCS-CN runoff equation
    Q = (P - 0.2*S)^2 / (P + 0.8*S),    S = 25400/CN - 254 (mm).

This is the second-to-last piece of the rainfall-runoff chain — only
the rainfall fetcher and the equation itself remain.

- floodpath/runoff/curve_number.py: compute_curve_number(landuse, hsg)
  walks each (WorldCover code, HSG letter) pair and applies the table
  in WORLDCOVER_NEH_CN. SoilGrids HSG (~250 m) is upsampled to the
  WorldCover grid (10 m) via rasterio.warp.reproject with nearest-
  neighbour resampling (categorical data — averaging would be wrong).
- floodpath/runoff/constants.py: WORLDCOVER_NEH_CN — the 11x4 lookup
  table (NEH 630 Ch9 Table 9-1, AMC II) mapping every WorldCover class
  to a CN per HSG letter. Mappings cite the chosen NEH cover type
  (Woods/Brush/Pasture/Row crops/Residential 1/4 acre/Fallow/...).
- floodpath/runoff/models.py: CurveNumberGrid carries the uint8 CN
  raster, stats() that ignore nodata, and potential_retention_mm()
  returning S = 25400/CN - 254 ready to plug into the SCS-CN equation.
- Tests: 29 new (constants table invariants, monotonicity vs HSG, S
  formula, georef inheritance, fallback behaviour, custom mapping).
  Pinned values against the existing Robit Bata WorldCover + soil
  fixtures (no new fixture needed): mean CN = 87.72, dominant class
  CN = 89 (cropland on D), min/max 77/100.
@rehsani rehsani merged commit 4458468 into main May 4, 2026
1 check passed
@rehsani rehsani deleted the step-6-curve-number branch May 4, 2026 18:13
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