fc = FuelClasses(d1=1.0, d10=2.0, d100=3.0, herb=4.0, wood=5.0)
sum(fc)15.0
The Rothermel module implements the Rothermel (1972) surface fire spread model, the foundational model used by most US wildfire behavior systems (BehavePlus, FARSITE, ELMFIRE, etc.).
The model computes the rate of spread (m/min) of a surface fire given:
| Input | Description | Source |
|---|---|---|
| Fuel model | Static fuel bed properties (loading, SAV ratio, heat content, depth, moisture of extinction) | Rothermel struct |
| Moisture | Fuel moisture content per size class | FuelClasses (keyword arg) |
| Wind | Midflame wind speed | Scalar (km/h) |
| Slope | Terrain slope as rise/run | Scalar (fraction) |
The fuel model is a static landscape property while moisture, wind, and slope are dynamic environmental inputs — matching how operational fire behavior systems separate these concerns.
FuelClassesA container for values across the five Rothermel fuel size classes.
Values for the five Rothermel fuel size classes.
d1::T - 1-hr dead fuel (< 0.25 in diameter)d10::T - 10-hr dead fuel (0.25–1.0 in)d100::T - 100-hr dead fuel (1.0–3.0 in)herb::T - Live herbaceouswood::T - Live woodySupported operations:
RothermelThe fuel model struct parameterized by the Rothermel (1972) equations.
Fuel model for the Rothermel (1972) surface fire spread model.
Parameterized by numeric type T (e.g. Float64, Float32, Unitful quantities).
name::String - Descriptionw::FuelClasses{T} - Fuel loading [tons/acre]σ::FuelClasses{T} - Surface-area-to-volume ratio [1/ft]h::FuelClasses{T} - Heat content [BTU/lb]δ::T - Fuel bed depth [ft]Mx::T - Dead fuel moisture of extinction [fraction]See NFFL for the 13 standard fuel models from Anderson (1982).
The 13 standard fuel models from Anderson (1982) are provided as named constants:
| Constant | NFFL | Description | Depth (ft) | Mx |
|---|---|---|---|---|
SHORT_GRASS |
1 | Short grass (1 ft) | 1.0 | 0.12 |
TIMBER_GRASS |
2 | Timber grass/understory | 1.0 | 0.15 |
TALL_GRASS |
3 | Tall grass (2.5 ft) | 2.5 | 0.25 |
CHAPARRAL |
4 | Chaparral (6 ft) | 6.0 | 0.20 |
BRUSH |
5 | Brush (2 ft) | 2.0 | 0.20 |
DORMANT_BRUSH |
6 | Dormant brush/hardwood slash | 2.5 | 0.25 |
SOUTHERN_ROUGH |
7 | Southern rough | 2.5 | 0.40 |
CLOSED_TIMBER_LITTER |
8 | Closed timber litter | 0.2 | 0.30 |
HARDWOOD_LITTER |
9 | Hardwood litter | 0.2 | 0.25 |
TIMBER_UNDERSTORY |
10 | Timber litter/understory | 1.0 | 0.25 |
LIGHT_SLASH |
11 | Light logging slash | 1.0 | 0.15 |
MEDIUM_SLASH |
12 | Medium logging slash | 2.3 | 0.20 |
HEAVY_SLASH |
13 | Heavy logging slash | 3.0 | 0.25 |
Compute the forward rate of fire spread using the Rothermel (1972) model.
fuel::Rothermel - Fuel modelmoisture::FuelClasses - Moisture content per fuel class [fraction, 0–1]. Use 0.0 for unused classes.wind - Midflame wind speed [km/h]slope - Terrain slope as rise/run [fraction]Forward rate of spread at the fire head [m/min].
Rate of spread at a reference condition (wind = 8 km/h, slope = 0, dead fuel moisture 6–8%):
M_ref = FuelClasses(
d1=0.06, d10=0.07, d100=0.08,
herb=0.0, wood=0.0)
M_live = FuelClasses(
d1=0.06, d10=0.07, d100=0.08,
herb=0.60, wood=0.90)
all_fuels = [
SHORT_GRASS, TIMBER_GRASS, TALL_GRASS,
CHAPARRAL, BRUSH, DORMANT_BRUSH,
SOUTHERN_ROUGH, CLOSED_TIMBER_LITTER,
HARDWOOD_LITTER, TIMBER_UNDERSTORY,
LIGHT_SLASH, MEDIUM_SLASH, HEAVY_SLASH,
]
nffl_labels = string.(1:13)
cat_colors = Dict(
"Grass" => :forestgreen,
"Shrub" => :goldenrod,
"Timber" => :sienna,
"Slash" => :slategray,
)
categories = [
"Grass", "Grass", "Grass",
"Shrub", "Shrub", "Shrub", "Shrub",
"Timber", "Timber", "Timber",
"Slash", "Slash", "Slash",
]
colors = [cat_colors[c] for c in categories]
function has_live_fuel(f)
sum(f.w) > f.w.d1 + f.w.d10 + f.w.d100
end
ros = [
rate_of_spread(f,
moisture=has_live_fuel(f) ? M_live : M_ref,
wind=8.0, slope=0.0)
for f in all_fuels
]
fig = Figure(size=(700, 350))
ax = Axis(fig[1,1],
xlabel="NFFL Model",
ylabel="Rate of spread (m/min)",
title="Rate of Spread at 8 km/h Wind",
xticks=(1:13, nffl_labels))
barplot!(ax, 1:13, ros, color=colors)
cat_names = ["Grass", "Shrub", "Timber", "Slash"]
elems = [PolyElement(color=cat_colors[c])
for c in cat_names]
Legend(fig[1,2], elems, cat_names,
framevisible=false)
figgroups = [
"Grass" => [
("1: Short", SHORT_GRASS),
("2: Timber", TIMBER_GRASS),
("3: Tall", TALL_GRASS),
],
"Shrub" => [
("4: Chaparral", CHAPARRAL),
("5: Brush", BRUSH),
("6: Dormant", DORMANT_BRUSH),
("7: Southern", SOUTHERN_ROUGH),
],
"Timber" => [
("8: Closed", CLOSED_TIMBER_LITTER),
("9: Hardwood", HARDWOOD_LITTER),
("10: Understory", TIMBER_UNDERSTORY),
],
"Slash" => [
("11: Light", LIGHT_SLASH),
("12: Medium", MEDIUM_SLASH),
("13: Heavy", HEAVY_SLASH),
],
]
mph = 0:1:15
mph_to_kmh = 1.60934
fig = Figure(size=(700, 500))
axes = []
for (col, (title, fuels)) in enumerate(groups)
row = col <= 2 ? 1 : 2
c = col <= 2 ? col : col - 2
ax = Axis(fig[row, c],
xlabel="Wind (mph)",
ylabel="ROS (m/min)",
title=title)
push!(axes, ax)
for (label, fuel) in fuels
m = has_live_fuel(fuel) ? M_live : M_ref
r = [
rate_of_spread(fuel,
moisture=m,
wind=w * mph_to_kmh,
slope=0.0)
for w in mph
]
lines!(ax, collect(mph), r, label=label)
end
axislegend(ax, position=:lt, labelsize=10)
end
linkyaxes!(axes...)
figfig = Figure(size=(700, 350))
ax = Axis(fig[1,1],
xlabel="NFFL Model",
ylabel="Fuel loading (tons/acre)",
title="Fuel Loading Breakdown",
xticks=(1:13, nffl_labels))
xs = repeat(1:13, 5)
vals = vcat(
[f.w.d1 for f in all_fuels],
[f.w.d10 for f in all_fuels],
[f.w.d100 for f in all_fuels],
[f.w.herb for f in all_fuels],
[f.w.wood for f in all_fuels],
)
grp = vcat(
fill(1, 13), fill(2, 13), fill(3, 13),
fill(4, 13), fill(5, 13),
)
bar_colors = [
:tomato, :sandybrown, :tan,
:limegreen, :forestgreen,
]
barplot!(ax, xs, vals,
stack=grp,
color=[bar_colors[g] for g in grp])
bar_labels = [
"1-hr dead", "10-hr dead", "100-hr dead",
"Herb", "Wood",
]
elems = [PolyElement(color=c) for c in bar_colors]
Legend(fig[1,2], elems, bar_labels,
framevisible=false)
fig