Wildfire spread (Rothermel 1972 + Anderson 13)
Full description: Read the full narrative
The kernel is a surface-fire cellular spread model: Rothermel's (1972) rate-of-spread formulation over the Anderson (1982) thirteen NFFL fuel models, evaluated per cell from slope, aspect, midflame wind and moisture, then propagated with a wind/slope-elongated directional probability. Byram (1959) fireline intensity, Simard (1968) equilibrium moisture content and a simplified Albini (1979) spotting term accompany the front. It reports ROS in m/s and intensity in kW/m.
- Execution mode
- Local CPU (python3) — no Kaggle token required server/kaggle/simRunner.ts:126-141
- Verification
- MEASURED — executed directly (3 parameter sets incl. boundary cases)
- Wire scenario type
wildfire_spread— contract at src/services/kaggleSim.ts:28-240; job API at Simulation job API
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):
Rothermel (1972) surface fire rate-of-spread model over the Anderson (1982) 13 NFFL fuel models, with a directional (wind/slope-elongated) time-marching spread algorithm on a raster grid. Physics references: - Rothermel, R. C. (1972) USDA Forest Service RP INT-115. - Anderson, H. E. (1982) USDA Forest Service GTR INT-122 (NFFL fuel models). - Byram, G. M. (1959) fireline intensity I = H * w * R.
Coefficients and named constants
| Name | Value | Meaning | Source |
|---|---|---|---|
| K_ROS_TARGET_FM2 | 0.105 m/s | Published fm2 reference ROS used to calibrate the model-form constant | kaggle-kernels/fire-sim/main.py:365-371 |
| K_ROS (computed) | 29.90 in observed run | Only empirical freedom in the model (per code comment) | kaggle-kernels/fire-sim/main.py:374-387 |
| _ST, _SE, _RHO_P | 0.0555, 0.010, 32.0 lb/ft³ | Stoichiometric excess, mineral damping, particle density | kaggle-kernels/fire-sim/main.py:120 |
| Live-fuel heats h1 = hl | 8000 BTU/lb | Rothermel/Anderson default live heat content | kaggle-kernels/fire-sim/main.py:122 |
| Simard EMC clip | [0.01, 0.60] | Equilibrium dead-fuel moisture fraction bounds | kaggle-kernels/fire-sim/main.py:182-195 |
| Anderson 13 table | fm1…fm13 (w0, σ, depth, mx, h′) | Full published fuel-model coefficients | kaggle-kernels/fire-sim/main.py:117-148 |
Parameter contract
Wildfire parameters (UI keys → wire fields). Shared request envelope (validated at the client boundary before dispatch).
| UI control | Wire field | Unit | Valid range (UI · wire · kernel) | Default | Physical meaning | Source |
|---|---|---|---|---|---|---|
| Wind Speed slider | wind_speed_ms = km/h ÷ 3.6 | m/s | UI 0–120 km/h; wire 0–60 m/s | UI 20 km/h · kernel 10 m/s | Midflame wind input; also "blows FROM" per meteorological convention | src/components/scenarios/ScenarioEditor.tsx:77-88 src/services/kaggleSim.ts:62,441 kaggle-kernels/fire-sim/main.py:396-397 |
| Wind Direction slider | wind_dir_deg | °, clockwise from N (FROM) | UI 0–360 step 5; wire 0–360 | UI 270 · kernel 270 | Spread vector uses dir+180° | src/components/scenarios/ScenarioEditor.tsx:77-88 src/services/kaggleSim.ts:63 kaggle-kernels/fire-sim/main.py:302,397 |
| Relative Humidity slider | humidity_pct | % | UI 1–100; wire 0–100 | UI 15 · kernel 20 | Drives Simard EMC unless dead_moisture override given | src/components/scenarios/ScenarioEditor.tsx:77-88 src/services/kaggleSim.ts:64 kaggle-kernels/fire-sim/main.py:398,479-481 |
| Fuel Type select | fuel_type | grass|shrub|forest|urban | enum; mapped to Anderson fm 2/5/8/1 | forest | Unknown strings rejected at the boundary | src/components/scenarios/ScenarioEditor.tsx:81-85 src/services/kaggleSim.ts:66,446-452 kaggle-kernels/fire-sim/main.py:150-161 |
| Duration slider | duration_hours | h | UI 1–168; wire ≤ 720 | UI 72 · kernel 2 | Simulation length | src/components/scenarios/ScenarioEditor.tsx:77-88 src/services/kaggleSim.ts:65 kaggle-kernels/fire-sim/main.py:399 |
| Terrain (study-box sample) | terrain + terrain_gs | m above ellipsoid | ≤ 65,536 cells; gs 2–256; server compacts to base64 | — | Real globe relief; kernel derives slope/aspect from it | src/services/kaggleSim.ts:69-80 server/kaggle/simRunner.ts:456-495 |
| dead_moisture / live_moisture / temperature_c | same names (optional) | fraction; fraction; °C | 0–0.6; 0–2; −40…60 | kernel: Simard-derived / fuel table / 25 | Scientific overrides — not exposed as UI sliders (wire-only) | src/services/kaggleSim.ts:81-85 kaggle-kernels/fire-sim/main.py:404-405,409,479-481 |
Outputs
| Field | Unit | Meaning | Source |
|---|---|---|---|
fire_state.npy | 0 unburned · 1 burning · 2 burned | Final combustion state grid | kaggle-kernels/fire-sim/main.py:546,737 |
fire_intensity.npy | 0…1 continuous | Per-cell combustion intensity (0.15 residual on burned) | kaggle-kernels/fire-sim/main.py:633-641,738 |
fuel_map.npy | load multiplier 0–1.2 | Per-cell fuel-load field | kaggle-kernels/fire-sim/main.py:220,739 |
terrain_slope.npy | degrees | Slope grid (stored under final key "terrain") | kaggle-kernels/fire-sim/main.py:666,740 |
fireline_intensity_npy.npy | kW/m | Byram intensity | kaggle-kernels/fire-sim/main.py:741 |
rate_of_spread_npy.npy | m/s | Directional ROS field | kaggle-kernels/fire-sim/main.py:742 |
snapshots_state/intensity.npy + snapshot_times.npy | —; h | 20-frame animation series | kaggle-kernels/fire-sim/main.py:743-752 |
metadata.json | — | model "rothermel_anderson13", terrain_source, final burned stats | kaggle-kernels/fire-sim/main.py:680-685,759-764 |
Spatial-origin semantics
Square domain, row-major grid, row 0 = north (the "compaction contract" shared with all terrain-bearing kernels); state[ignition_y, ignition_x] addresses row= y, col = x. Slope and aspect are derived from the supplied (or synthetic) elevation with np.gradient(elev, cell_m); wind direction is meteorological ("blows FROM"). kaggle-kernels/fire-sim/main.py:553-557,433-435,459-461,302
Documented validity limits (verbatim from the code)
the kernel must never silently invent topography for a study area the user has already drawn on the globe · Previously fuel_type was silently dropped and every run used fm2. · slow-ROS fuels (e.g. fm8 forest-floor duff) … burned out before igniting anything — the fire stalled at the ignition cell. Residence = clamp(flame-flush, cell-backing-sweep, 240 min). · WUI structure fuels are outside the Anderson-13 system
A non-fatal validation prints the no-wind/no-slope ROS of the active fuel against the 0.105 m/s reference (± 0.01) — "PASS" or "CHECK" kaggle-kernels/fire-sim/main.py:501-508. Wall-clock cap default 480 s bounds the march kaggle-kernels/fire-sim/main.py:407,568-570.
Measured runs and timings
| Run / check | Measured result |
|---|---|
| Calibration fuel (fm2 grass, no wind) | ROS_nowind_noslope = 0.103 m/s vs reference 0.105 → PASS (observed stdout) |
| Representative fm8 (forest) run: 128², 8 m/s, RH 20 %, 6 h, 40 km box, seed 42 | K_ROS = 29.90; mean R = 0.370 m/s, max R = 0.503 m/s, mean I = 4,072 kW/m; burned 25 cells (0.15 % of grid); wall 1.06 s. [VALIDATE] prints "CHECK" for non-fm2 fuels because the reference applies to fm2 — expected behaviour, non-fatal. |
| Boundary calm-humid (0 m/s wind, RH 100 %, grass) | Completes in 0.40 s; minimal advance (moisture damping η_M → 0 near saturation) |
| Boundary hot-dry (15 m/s, RH 5 %, shrub, T = 40 °C, dead moisture 0.04) | Completes in 0.60 s; fire starts and advances |
Reproduction
Commands
curl -s -X POST http://localhost:3001/api/kaggle/simulate \
-H 'Content-Type: application/json' -d '{
"type":"wildfire_spread","lat":34.5,"lon":-117.5,"grid_size":128,
"extent_km":40,"wind_speed_ms":8,"wind_dir_deg":270,
"humidity_pct":20,"duration_hours":6,"fuel_type":"forest","seed":42 }'
# direct kernel run:
cd /tmp && mkdir fire && cp <repo>/kaggle-kernels/fire-sim/main.py fire/ && cd fire && \
echo '{"grid_size":64,"wind_speed_ms":0,"fuel_type":"grass","humidity_pct":15,"duration_hours":1,"extent_km":10,"seed":1}' > params.json && \
TERRANOETIS_OUT_DIR=$PWD python3 main.py | grep VALIDATE