TERRANOETIS

Flood inundation (local-inertial shallow water)

Platform v3.0.0 Docs v3.0.0 MEASURED LOCALLY

Full description: Read the full narrative

Rainfall-runoff inundation over real (or, when no terrain is supplied, procedurally generated) topography. The solver is the local-inertial (diffusive-inertial) form of the 2D shallow-water equations with a uniform rainfall source — the approach family of LISFLOOD-FP, SFINCS and RIM2D — discretised with Rusanov (local Lax–Friedrichs) fluxes with Audusse hydrostatic reconstruction and a semi-implicit Bates-2010 Manning friction update.

Execution mode
Kaggle kernel push→poll→download (numpy-only code; the kernel metadata requests the GPU accelerator) server/kaggle/simRunner.ts:126-141 (not in LOCAL_SIM_TYPES) kaggle-kernels/flood-sim/kernel-metadata.json:enable_gpu=true
Verification
PHYSICS MEASURED locally (solver completed; mass balance closure 0.0000 %)
Wire scenario type
flood_inundation — contract at src/services/kaggleSim.ts:28-240; job API at Simulation job API
Evidence legend

MEASURED marks values produced by executing the actual code on this machine (commands in Reproduction; raw logs summarised in Methodology → Evidence runs).

Governing equations and coefficients

Source attribution as stated in the kernel header (verbatim):

Solves the 2D local-inertial (diffusive-inertial) shallow water equations with a spatially uniform rainfall source term — the approach used by LISFLOOD-FP, SFINCS, and RIM2D. … Solver: Rusanov (local Lax-Friedrichs) for continuity with hydrostatic reconstruction (Audusse et al. 2004), semi-implicit Bates (2010) formulation for Manning friction.
kaggle-kernels/flood-sim/main.py:5-20
∂h/∂t = rainfall_rate − infiltration + flux divergence
Continuity with rain source (h = surface water depth). kaggle-kernels/flood-sim/main.py:12,524-543
∂(hu)/∂t = −g·h·∇η − g·n²·u|u|/h^(4/3), u_new = (u + dt·pressure) / (1 + dt·g·n²·|u|/h^(4/3))
Local-inertial momentum with semi-implicit Manning friction (Bates 2010). kaggle-kernels/flood-sim/main.py:584-599
infiltration = 10 mm/hr × (1 − 0.7·soil_saturation)
Constant-loss rate labelled "Green-Ampt" in the source — a fixed base loss modulated by saturation, not the full Green–Ampt infiltration equation (no wetting-front redistribution). Labeled honestly here. kaggle-kernels/flood-sim/main.py:57,470-471
n(cell) = WorldCover-class lookup; default 0.035
Manning roughness from ESA WorldCover v200 class codes; roughness "from Chow (1959) + common flood-model lookup tables (LISFLOOD-FP / HEC-RAS)". kaggle-kernels/flood-sim/main.py:56,61-84
dt = min(0.5·dx/(√(g·h_max)+V_MAX), 1.0 s), V_MAX = 5 m/s
Adaptive CFL with physical velocity cap; positivity-preserving face limiter with tracked clip-loss volume. kaggle-kernels/flood-sim/main.py:503-507,554-579,59

Coefficients and named constants

WorldCover v200 → Manning n lookup (class: n [s/m^1/3]).
WorldCover class10203040506070809095100
n0.1000.0700.0350.0400.0150.0250.0250.0300.0600.1500.050

Parameter contract

Flood parameters. Shared request envelope (validated at the client boundary before dispatch).

Flood parameters.
UI controlWire fieldUnitValid range (UI · wire · kernel)DefaultPhysical meaningSource
Total Rainfall sliderrainfall_mmmm (event total)UI 50–2000; wire 10–3000; kernel applies unvalidatedUI 500 · kernel 400Spatially uniform rain depthsrc/components/scenarios/ScenarioEditor.tsx:102-108 src/services/kaggleSim.ts:34 kaggle-kernels/flood-sim/main.py:372
Soil Saturation slidersoil_saturationfraction0–1UI 0.8 · kernel 0.8Scales infiltration loss and initial ponded depth (h_init = 0.02·S)src/components/scenarios/ScenarioEditor.tsx:102-108 src/services/kaggleSim.ts:36 kaggle-kernels/flood-sim/main.py:374,485-486
Duration sliderduration_hourshUI 1–168 (auto-derived from rainfall/catchment); wire ≤ 720UI 72 · kernel 12Rain event lengthsrc/components/scenarios/ScenarioEditor.tsx:102-108 src/services/kaggleSim.ts:35,640 kaggle-kernels/flood-sim/main.py:373
Wind Speed sliderwind_speed_ms (optional) = km/h÷3.6m/sUI 0–100 km/h; wire 0–60UI 40 km/hAdvisory field — not used in the solver momentum termssrc/components/scenarios/ScenarioEditor.tsx:102-108 src/services/kaggleSim.ts:38,434
Catchment Area slider— (not sent)km²UI 100–20,0002000UI-only hint for derived runoff/duration suggestionssrc/components/scenarios/ScenarioEditor.tsx:102-108 src/services/kaggleSim.ts:631-641
dam_breachdam_breachliteral true required by the schematrueContract constant only — the kernel contains no dam-breach hydrograph; inundation is rainfall-drivensrc/services/kaggleSim.ts:37
terrain / landcover (server-sampled)terrain(+_gs), landcover(+_gs)m; WorldCover codes≤ 65,536 cells eachautoServer auto-samples land cover for flood runs when absentsrc/services/kaggleSim.ts:41-55 server/kaggle/simRunner.ts:520-563

Outputs

Files written per run (also served by GET /api/kaggle/simulate/:jobId/grid/:name and …/geotiff/:name)
FieldUnitMeaningSource
water_depth_final.npymFinal flood depthkaggle-kernels/flood-sim/main.py:789-807
velocity_x/y.npym/sDepth-averaged velocitykaggle-kernels/flood-sim/main.py:789-807
terrain.npymElevation usedkaggle-kernels/flood-sim/main.py:789-807
snapshots_depth.npy + snapshot_times.npym; hoursAnimation serieskaggle-kernels/flood-sim/main.py:789-807
metadata.jsonmodel "local_inertial_swe", solver "bates_2010_semi_implicit", mass_balance blockkaggle-kernels/flood-sim/main.py:702-722,809-819

Spatial-origin semantics

Cell-centred (NY, NX) square grid, uniform dx from extent; row 0 = north (bilinear-upsample docstring shared across kernels); east face = roll(z_b, −1, axis=1), south face = roll(z_b, −1, axis=0). lat/lon are decorative (metadata/printing). kaggle-kernels/flood-sim/main.py:16,476-478,525-530,91-92,387,375-381

Documented validity limits (verbatim from the code)

'covers the entire DEM domain (not just valleys downstream of a breach)' · solver attribution: 'Rusanov (local Lax-Friedrichs) for continuity with hydrostatic reconstruction (Audusse et al. 2004), semi-implicit Bates (2010) formulation for Manning friction.' · boundary: 'the boundary can remove water but can never create it. (np.minimum = drain only.)' · truncation warning: '[WARNING] Simulation truncated at t=…h — results are partial'
kaggle-kernels/flood-sim/main.py:8-9,18-20,614-617,698-700

Two fatal conservation gates run before the simulation (each raises and aborts): a closed-box mass-conservation test (tol 1e-9) and a lake-at-rest well-balanced test (tol 1e-6, 10 % sloped bed): "Water would spontaneously flow on sloped terrain." kaggle-kernels/flood-sim/main.py:121-366,771-783

Measured runs and timings

Executed on an Apple-Silicon laptop (Python 3.14.5, NumPy 2.4.2). These are observations of one environment, not performance guarantees.
Run / checkMeasured result
Local direct run (64², 300 mm rain, 4 h, sat 0.8, 20 km box)Solver completed: max depth 4.01 m, flooded 78.2 % of grid; mass balance rainfall 120.0 M m³, infiltration 7.04 M, outflow 7.73 M, clip-loss 0.000 M, closure error 0.0000 %; wall 7.5 s. Both conservation gates ran first (stdout).
Minimum-rain edge (10 mm, 1 h)max depth 0.00 m, flooded 0 % — infiltration absorbed all rainfall; closure 0.0000 %
Output limitation observedThe npy/metadata write step fails off-Kaggle with "Read-only file system: '/kaggle'" because the output dir is hard-coded (main.py:786) — this is why flood is NOT in LOCAL_SIM_TYPES; the web pipeline runs it on Kaggle where /kaggle/working exists

Reproduction

Commands

# Physics (computation) locally — output writing intentionally fails off-Kaggle:
cd /tmp && mkdir fl && cp <repo>/kaggle-kernels/flood-sim/main.py fl/ && cd fl && \
  echo '{"grid_size":64,"rainfall_mm":300,"duration_hours":4,"soil_saturation":0.8,"extent_km":20,"wallclock_max_sec":120}' > params.json && \
  python3 main.py | grep -E "VERIFY|MASS BALANCE|DONE"

# Full pipeline requires ~/.kaggle/kaggle.json (see deployment page):
curl -s -X POST http://localhost:3001/api/kaggle/simulate \
  -H 'Content-Type: application/json' -d '{"type":"flood_inundation","lat":29.76,"lon":-95.37,"grid_size":256,"extent_km":20,"rainfall_mm":300,"duration_hours":4,"soil_saturation":0.8,"dam_breach":true}'