TERRANOETIS

Wildfire spread (Rothermel 1972 + Anderson 13)

Platform v3.0.0 Docs v3.0.0 MEASURED LOCALLY

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
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):

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.
kaggle-kernels/fire-sim/main.py:1-10
R [ft/min] = K_ROS · I_R · ξ · (1 + φ_Ew + φ_Es) / (σ_b · ε · Q_ig)
Rothermel core rate of spread; converted to m/s via ×0.3048/60. kaggle-kernels/fire-sim/main.py:327-339
I_R = γ′ · (W_d·h_d·η_Md + W_l·h_l·η_Ml) · η_S
Reaction intensity (air volume basis). kaggle-kernels/fire-sim/main.py:322-324
γ_max = σ^1.5 / (495.0 + 0.0594·σ^1.5); A = clip(1/(4.774σ^0.1 − 7.27), 0.5, 3.0); γ′ = γ_max·(β/β_opt)^A·exp(A(1−β/β_opt)); β_opt = 3.348·σ^−0.8189
Optimum packing ratio and surface-to-volume heat-release skew. kaggle-kernels/fire-sim/main.py:284-290
φ_Ew = C·U^B·(β/β_opt)^−E; B = 0.02526·σ^0.54; C = 7.47·exp(−0.133·σ^0.55); E = 0.715·exp(−3.59e−4·σ)
Wind term; U = effective midflame wind (vector sum of wind and up-slope component), ft/min per Rothermel. kaggle-kernels/fire-sim/main.py:293-310
φ_Es = 5.275 · β^−0.3 · tan²θ
Slope term (θ clipped to 0–45°; tan clipped at 89.9°). kaggle-kernels/fire-sim/main.py:297-299
η_M = 1 − 2.59r + 5.11r² − 3.52r³ (r = M/FM, clipped [0,1]); η_S = 0.174·S_e^−0.19; ε = exp(−138/σ); Q_ig = max(250 + 1116·M_d, 1) BTU/lb
Moisture damping, mineral damping, effective heating number, ignition heat; live moisture-of-extinction floor per Rothermel 1972 eq. 88. kaggle-kernels/fire-sim/main.py:256-272,314-315,327
I [kW/m] = H_eff · w_load · R[ft/min] · 0.0577
Byram (1959) fireline intensity. The code comment records a prior unit error: "The previous code used 3.155e-3 kW/m, i.e. 18.3× too small — Byram (1959) is reproduced only with the full 0.0577 factor." kaggle-kernels/fire-sim/main.py:349-355
P(ignition) = 1 − exp(−R_dir·Δt / dist) per neighbour, 8-cell stencil
Stochastic directional ignition; R_dir from the Anderson ellipse with eccentricity from LB = R_head/R_back ∈ [1,12]. kaggle-kernels/fire-sim/main.py:342-347,589-593
spot prob = min(0.6, 0.05 + 0.03·U); spot distance = cell·(5 + 3·U)·U(0.5,2) (downwind)
Simplified Albini (1979) spotting ("both the leap distance and the ignition odds grow with midflame wind speed"). kaggle-kernels/fire-sim/main.py:599-617

Coefficients and named constants

Rothermel constants and K_ROS self-calibration. At import the kernel computes the raw (K=1) fm2 no-wind/no-slope ROS and scales it so the published FARSITE reference 0.105 m/s (8 % dead-fuel moisture) is reproduced; every other coefficient stays tabulated.
NameValueMeaningSource
K_ROS_TARGET_FM20.105 m/sPublished fm2 reference ROS used to calibrate the model-form constantkaggle-kernels/fire-sim/main.py:365-371
K_ROS (computed)29.90 in observed runOnly empirical freedom in the model (per code comment)kaggle-kernels/fire-sim/main.py:374-387
_ST, _SE, _RHO_P0.0555, 0.010, 32.0 lb/ft³Stoichiometric excess, mineral damping, particle densitykaggle-kernels/fire-sim/main.py:120
Live-fuel heats h1 = hl8000 BTU/lbRothermel/Anderson default live heat contentkaggle-kernels/fire-sim/main.py:122
Simard EMC clip[0.01, 0.60]Equilibrium dead-fuel moisture fraction boundskaggle-kernels/fire-sim/main.py:182-195
Anderson 13 tablefm1…fm13 (w0, σ, depth, mx, h′)Full published fuel-model coefficientskaggle-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).

Wildfire parameters (UI keys → wire fields).
UI controlWire fieldUnitValid range (UI · wire · kernel)DefaultPhysical meaningSource
Wind Speed sliderwind_speed_ms = km/h ÷ 3.6m/sUI 0–120 km/h; wire 0–60 m/sUI 20 km/h · kernel 10 m/sMidflame wind input; also "blows FROM" per meteorological conventionsrc/components/scenarios/ScenarioEditor.tsx:77-88 src/services/kaggleSim.ts:62,441 kaggle-kernels/fire-sim/main.py:396-397
Wind Direction sliderwind_dir_deg°, clockwise from N (FROM)UI 0–360 step 5; wire 0–360UI 270 · kernel 270Spread 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 sliderhumidity_pct%UI 1–100; wire 0–100UI 15 · kernel 20Drives Simard EMC unless dead_moisture override givensrc/components/scenarios/ScenarioEditor.tsx:77-88 src/services/kaggleSim.ts:64 kaggle-kernels/fire-sim/main.py:398,479-481
Fuel Type selectfuel_typegrass|shrub|forest|urbanenum; mapped to Anderson fm 2/5/8/1forestUnknown strings rejected at the boundarysrc/components/scenarios/ScenarioEditor.tsx:81-85 src/services/kaggleSim.ts:66,446-452 kaggle-kernels/fire-sim/main.py:150-161
Duration sliderduration_hourshUI 1–168; wire ≤ 720UI 72 · kernel 2Simulation lengthsrc/components/scenarios/ScenarioEditor.tsx:77-88 src/services/kaggleSim.ts:65 kaggle-kernels/fire-sim/main.py:399
Terrain (study-box sample)terrain + terrain_gsm above ellipsoid≤ 65,536 cells; gs 2–256; server compacts to base64Real globe relief; kernel derives slope/aspect from itsrc/services/kaggleSim.ts:69-80 server/kaggle/simRunner.ts:456-495
dead_moisture / live_moisture / temperature_csame names (optional)fraction; fraction; °C0–0.6; 0–2; −40…60kernel: Simard-derived / fuel table / 25Scientific 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

Files written per run (also served by GET /api/kaggle/simulate/:jobId/grid/:name and …/geotiff/:name)
FieldUnitMeaningSource
fire_state.npy0 unburned · 1 burning · 2 burnedFinal combustion state gridkaggle-kernels/fire-sim/main.py:546,737
fire_intensity.npy0…1 continuousPer-cell combustion intensity (0.15 residual on burned)kaggle-kernels/fire-sim/main.py:633-641,738
fuel_map.npyload multiplier 0–1.2Per-cell fuel-load fieldkaggle-kernels/fire-sim/main.py:220,739
terrain_slope.npydegreesSlope grid (stored under final key "terrain")kaggle-kernels/fire-sim/main.py:666,740
fireline_intensity_npy.npykW/mByram intensitykaggle-kernels/fire-sim/main.py:741
rate_of_spread_npy.npym/sDirectional ROS fieldkaggle-kernels/fire-sim/main.py:742
snapshots_state/intensity.npy + snapshot_times.npy—; h20-frame animation serieskaggle-kernels/fire-sim/main.py:743-752
metadata.jsonmodel "rothermel_anderson13", terrain_source, final burned statskaggle-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
kaggle-kernels/fire-sim/main.py:425-426,151-152,527-532,158-160

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

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
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 42K_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