wind = UniformWind(speed=8.0, direction=π/4)
wind(0.0, 0.0, 0.0) # (speed, direction)(8.0, 0.7853981633974483)
The Components module defines the abstract interfaces and concrete types used by SpreadModel to represent wind, moisture, terrain, burnout, burn-in, and directional spread. Each component is a callable struct with signature (t, x, y) that returns the relevant physical quantity at a given time and location.
Every component category has an abstract supertype. Custom components subtype these and implement the callable interface.
| Abstract Type | Signature | Returns |
|---|---|---|
AbstractWind |
wind(t, x, y) |
(speed, direction) — speed [km/h], direction FROM [rad] |
AbstractMoisture |
moisture(t, x, y) |
FuelClasses — moisture fractions per size class |
AbstractTerrain |
terrain(t, x, y) |
(slope, aspect) — slope [fraction], aspect [rad] |
AbstractBurnout |
burnout(t_burning) |
Float64 ∈ [0, 1] — spread intensity scale factor |
AbstractBurnin |
burnin(t_burning) |
Float64 ∈ [0, 1] — spread intensity ramp-up |
Future interfaces include AbstractSpotting (ember transport) and AbstractSuppression (fireline construction).
Spatially and temporally constant wind field.
speed::T - Midflame wind speed [km/h]direction::T - Direction wind blows FROM [radians](8.0, 0.7853981633974483)
To create a spatially varying wind field, subtype AbstractWind:
Spatially and temporally constant fuel moisture.
FuelClasses{Float64}(d1=0.06, d10=0.07, d100=0.08, herb=0.0, wood=0.0)
Spatially varying fuel moisture that responds to fire-induced drying.
The 1-hr dead fuel moisture (d1) varies spatially while other size classes remain constant. As the fire front approaches, radiative heat flux dries unburned fuel ahead of the front. Moisture also recovers toward the ambient value over time.
The drying model at each unburned cell:
where φ is the level set value (approximate distance to the fire front in meters). Moisture is clamped to [min_d1, ambient_d1] to prevent unrealistic drying.
d1::M - Spatially varying 1-hr dead fuel moisture (M <: AbstractMatrix{T}) [fraction]base::FuelClasses{T} - Moisture values for other size classesambient_d1::T - Equilibrium d1 moisture from weather [fraction]dry_rate::T - Fire-induced drying coefficient [fraction/min]recovery_rate::T - Moisture recovery rate toward ambient [1/min]min_d1::T - Minimum d1 moisture floor [fraction]dx::T, dy::T, x0::T, y0::T - Grid geometry for coordinate lookupFuelClasses{Float64}(d1=0.06, d10=0.07, d100=0.08, herb=0.0, wood=0.0)
Flat terrain (zero slope everywhere).
Spatially constant terrain slope.
slope::T - Terrain slope as rise/run [fraction]aspect::T - Downslope direction [radians]Burnout models scale the spread rate of already-burning cells to simulate fuel exhaustion.
| Type | Behavior |
|---|---|
NoBurnout() |
No decay — fire spreads at full intensity indefinitely |
ExponentialBurnout(τ) |
Intensity decays as exp(-t/τ) |
LinearBurnout(τ) |
Intensity decreases linearly to zero over τ minutes |
t = 0:0.001:0.05
bo_exp = ExponentialBurnout(0.01)
bo_lin = LinearBurnout(0.02)
fig = Figure(size=(600, 300))
ax = Axis(fig[1, 1], xlabel="Time since ignition (min)", ylabel="Spread intensity",
title="Burnout Models")
lines!(ax, collect(t), bo_exp.(t), label="Exponential (τ=0.01)")
lines!(ax, collect(t), bo_lin.(t), label="Linear (τ=0.02)")
hlines!(ax, [1.0], color=:gray, linestyle=:dash, label="No burnout")
axislegend(ax, position=:rt)
figBurn-in models ramp up spread intensity after ignition, preventing freshly ignited cells from immediately propagating fire in all directions.
| Type | Behavior |
|---|---|
NoBurnin() |
Full intensity immediately upon ignition |
ExponentialBurnin(τ) |
Ramps as 1 - exp(-t/τ) |
LinearBurnin(τ) |
Linear ramp from 0 to 1 over τ minutes |
t = 0:0.01:5.0
bi_exp = ExponentialBurnin(1.0)
bi_lin = LinearBurnin(2.0)
fig = Figure(size=(600, 300))
ax = Axis(fig[1, 1], xlabel="Time since ignition (min)", ylabel="Spread intensity",
title="Burn-in Models")
lines!(ax, collect(t), bi_exp.(t), label="Exponential (τ=1.0)")
lines!(ax, collect(t), bi_lin.(t), label="Linear (τ=2.0)")
hlines!(ax, [1.0], color=:gray, linestyle=:dash, label="No burn-in")
axislegend(ax, position=:rb)
figDirectional models control how the head-fire rate of spread varies with angle from the push direction (combined wind + slope vector).
Cosine-based directional spread model (default).
The spread rate at angle θ from the push direction is:
This produces fires that are somewhat wider than observed in practice.
Elliptical fire spread model based on Anderson (1983).
The normal speed at angle θ from the push direction is:
where ε is the fire eccentricity and LB the length-to-breadth ratio, both derived from the effective midflame wind speed. The first term is the ellipse expansion and the second is the drift that places the ignition at the rear focus (as in the Anderson/Richards fire ellipse convention).
formula::Symbol - Length-to-breadth formula: :anderson (default) or :greenM = FuelClasses(d1=0.06, d10=0.07, d100=0.08, herb=0.0, wood=0.0)
g_cos = LevelSetGrid(150, 150, dx=30.0)
ignite!(g_cos, 2250.0, 2250.0, 200.0)
m_cos = FireSpreadModel(SHORT_GRASS, UniformWind(speed=10.0), UniformMoisture(M), FlatTerrain())
simulate!(g_cos, m_cos, steps=200)
g_ell = LevelSetGrid(150, 150, dx=30.0)
ignite!(g_ell, 2250.0, 2250.0, 200.0)
m_ell = FireSpreadModel(SHORT_GRASS, UniformWind(speed=10.0), UniformMoisture(M), FlatTerrain(), EllipticalBlending())
simulate!(g_ell, m_ell, steps=200)
fig = Figure(size=(700, 300))
ax1 = Axis(fig[1, 1], title="CosineBlending (default)", aspect=DataAspect())
ax2 = Axis(fig[1, 2], title="EllipticalBlending", aspect=DataAspect())
heatmap!(ax1, burned(g_cos))
heatmap!(ax2, burned(g_ell))
figCompute the fire length-to-breadth ratio from effective midflame wind speed U [m/s].
:anderson (default) — Anderson (1983): LB = 0.936 · exp(0.2566 · U) + 0.461 · exp(-0.1548 · U) - 0.397:green — Green (1983): LB = 1.1 · U^0.464 (suitable for grass)Returns at least 1.0 (a circle at zero wind).
U = 0:0.1:10.0
fig = Figure(size=(600, 300))
ax = Axis(fig[1, 1], xlabel="Wind speed (m/s)", ylabel="Length-to-breadth ratio",
title="Fire Shape vs. Wind Speed")
lines!(ax, collect(U), length_to_breadth.(U, formula=:anderson), label="Anderson (1983)")
lines!(ax, collect(U), length_to_breadth.(U, formula=:green), label="Green (1983)")
axislegend(ax, position=:lt)
figAll components follow the same pattern: subtype the abstract type and implement the callable interface.
struct MyWindField <: AbstractWind
data::Matrix{Float64}
dx::Float64
dy::Float64
end
function (w::MyWindField)(t, x, y)
# Look up wind from gridded data
i = clamp(round(Int, y / w.dy), 1, size(w.data, 1))
j = clamp(round(Int, x / w.dx), 1, size(w.data, 2))
speed = w.data[i, j]
direction = 0.0 # always from north
(speed, direction)
endFor dynamic components that evolve with the fire, implement update!: