Rothermel

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

How It Works

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.

Types

FuelClasses

A container for values across the five Rothermel fuel size classes.

printdoc(FuelClasses)
FuelClasses{T}(; d1, d10, d100, herb, wood)

Values for the five Rothermel fuel size classes.

Fields

  • 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 herbaceous
  • wood::T - Live woody

Supported operations:

fc = FuelClasses(d1=1.0, d10=2.0, d100=3.0, herb=4.0, wood=5.0)

sum(fc)
15.0
map(x -> 2x, fc)
FuelClasses{Float64}(d1=2.0, d10=4.0, d100=6.0, herb=8.0, wood=10.0)

Rothermel

The fuel model struct parameterized by the Rothermel (1972) equations.

printdoc(typeof(SHORT_GRASS))
Rothermel{T}(; name, w, σ, h, δ, Mx)

Fuel model for the Rothermel (1972) surface fire spread model.

Parameterized by numeric type T (e.g. Float64, Float32, Unitful quantities).

Fields (US customary units, matching original publications)

  • name::String - Description
  • w::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).

NFFL Fuel Models

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
SHORT_GRASS
Rothermel{Float64}("NFFL 1: Short grass (1 ft)")

Rate of Spread

printdoc(rate_of_spread)
rate_of_spread(fuel::Rothermel; moisture, wind, slope)

Compute the forward rate of fire spread using the Rothermel (1972) model.

Arguments

  • fuel::Rothermel - Fuel model
  • moisture::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]

Returns

Forward rate of spread at the fire head [m/min].

Example

M = FuelClasses(d1=0.06, d10=0.07, d100=0.08, herb=0.0, wood=0.0)
R = rate_of_spread(SHORT_GRASS, moisture=M, wind=8.0, slope=0.0)

Example

M = FuelClasses(d1=0.06, d10=0.07, d100=0.08, herb=0.0, wood=0.0)
rate_of_spread(SHORT_GRASS, moisture=M, wind=8.0, slope=0.0)
31.119112730486354

Effect of Wind Speed

using CairoMakie

winds = 0:2:30
rates = [
    rate_of_spread(SHORT_GRASS, moisture=M,
        wind=w, slope=0.0)
    for w in winds
]

fig = Figure()
ax = Axis(fig[1,1],
    xlabel="Wind speed (km/h)",
    ylabel="Rate of spread (m/min)",
    title="NFFL 1: Short Grass")
lines!(ax, collect(winds), rates)
fig

Comparing All 13 Fuel Models

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)

fig

Wind Sensitivity by Fuel Category

groups = [
    "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...)
fig

Fuel Loading by Size Class

fig = 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

References

  • Rothermel, R.C. (1972). A Mathematical Model for Predicting Fire Spread in Wildland Fuels. Res. Paper INT-115, USDA Forest Service.
  • Anderson, H.E. (1982). Aids to Determining Fuel Models for Estimating Fire Behavior. Gen. Tech. Rep. INT-122, USDA Forest Service.
  • Andrews, P.L. (2018). The Rothermel Surface Fire Spread Model and Associated Developments. Gen. Tech. Rep. RMRS-GTR-371, USDA Forest Service.