using Elmfire
using Plots
using Random
Random.seed!(42)TaskLocalRNG()
Ember generation and transport
Spotting (ember transport) creates new ignitions downwind of the main fire. This tutorial covers Elmfire.jl’s spotting capabilities.
Embers (firebrands) are generated by: - Burning vegetation lofted by convection - Higher intensity fires produce more embers - Crown fires generate significantly more embers
# Create spotting parameters
spotting_params = SpottingParameters{Float64}(
mean_distance = 100.0, # Mean spotting distance (m)
normalized_variance = 0.5, # Normalized variance
ws_exponent = 1.0, # Wind speed exponent
flin_exponent = 0.5, # Fireline intensity exponent
nembers_max = 10, # Max embers per cell per timestep
surface_spotting_percent = 1.0, # Surface fire spotting probability (%)
crown_spotting_percent = 10.0, # Crown fire spotting probability (%)
pign = 50.0, # Ignition probability (%)
min_distance = 10.0, # Min distance (m)
max_distance = 2000.0 # Max distance (m)
)SpottingParameters{Float64}(100.0, 0.5, 1.0, 0.5, 10, 1.0, 10.0, 50.0, 10.0, 2000.0)
Embers are transported downwind with distances that scale with wind speed and fireline intensity. The SpottingParameters control:
ncols, nrows = 150, 150
cellsize = 30.0
state = FireState{Float64}(ncols, nrows, cellsize)
fuel_table = create_standard_fuel_table(Float64)
fuel_ids = fill(4, ncols, nrows) # Chaparral (high intensity)
slope = zeros(Float64, ncols, nrows)
aspect = zeros(Float64, ncols, nrows)
weather = ConstantWeather{Float64}(
wind_speed_mph = 20.0,
wind_direction = 270.0,
M1 = 0.04, M10 = 0.06, M100 = 0.08,
MLH = 0.40, MLW = 0.70
)
config = SimulationConfig{Float64}(
enable_crown_fire = false,
enable_spotting = true,
spotting_params = spotting_params
)
ignite!(state, 40, 75, 0.0)
weather_interp = create_constant_interpolator(weather, ncols, nrows, cellsize)
simulate_full!(state, fuel_ids, fuel_table, weather_interp, slope, aspect,
0.0, 45.0; config = config, rng = MersenneTwister(42))
# Visualize
toa = copy(state.time_of_arrival)
toa[toa .< 0] .= NaN
heatmap(toa',
title = "Fire Spread with Spotting Enabled",
xlabel = "X (cells)",
ylabel = "Y (cells)",
color = :viridis,
aspect_ratio = 1,
size = (600, 500)
)# Without spotting
state_no_spot = FireState{Float64}(ncols, nrows, cellsize)
ignite!(state_no_spot, 40, 75, 0.0)
config_no_spot = SimulationConfig{Float64}(enable_spotting = false)
simulate_full!(state_no_spot, fuel_ids, fuel_table, weather_interp, slope, aspect,
0.0, 45.0; config = config_no_spot)
# With spotting
state_spot = FireState{Float64}(ncols, nrows, cellsize)
ignite!(state_spot, 40, 75, 0.0)
simulate_full!(state_spot, fuel_ids, fuel_table, weather_interp, slope, aspect,
0.0, 45.0; config = config, rng = MersenneTwister(42))
p1 = heatmap(state_no_spot.burned',
title = "Without Spotting\n$(round(get_burned_area_acres(state_no_spot), digits=1)) acres",
color = :YlOrRd, aspect_ratio = 1)
p2 = heatmap(state_spot.burned',
title = "With Spotting\n$(round(get_burned_area_acres(state_spot), digits=1)) acres",
color = :YlOrRd, aspect_ratio = 1)
plot(p1, p2, layout = (1, 2), size = (800, 400))The number of embers depends on:
Extreme fires can spot miles ahead:
# High-intensity scenario
ncols_big, nrows_big = 300, 200
state_big = FireState{Float64}(ncols_big, nrows_big, cellsize)
fuel_ids_big = fill(4, ncols_big, nrows_big)
slope_big = zeros(Float64, ncols_big, nrows_big)
aspect_big = zeros(Float64, ncols_big, nrows_big)
extreme_weather = ConstantWeather{Float64}(
wind_speed_mph = 35.0,
wind_direction = 270.0,
M1 = 0.03, M10 = 0.05, M100 = 0.07,
MLH = 0.35, MLW = 0.60
)
extreme_spotting = SpottingParameters{Float64}(
mean_distance = 200.0, # Longer mean distance (m)
normalized_variance = 0.5,
ws_exponent = 1.0,
flin_exponent = 0.5,
nembers_max = 20, # More embers
surface_spotting_percent = 2.0,
crown_spotting_percent = 15.0,
pign = 60.0, # Higher ignition probability
min_distance = 10.0,
max_distance = 5000.0 # Longer max distance (m)
)
config_extreme = SimulationConfig{Float64}(
enable_spotting = true,
spotting_params = extreme_spotting
)
ignite!(state_big, 50, 100, 0.0)
weather_interp_big = create_constant_interpolator(extreme_weather, ncols_big, nrows_big, cellsize)
simulate_full!(state_big, fuel_ids_big, fuel_table, weather_interp_big,
slope_big, aspect_big, 0.0, 60.0;
config = config_extreme, rng = MersenneTwister(42))
toa = copy(state_big.time_of_arrival)
toa[toa .< 0] .= NaN
heatmap(toa',
title = "Extreme Fire with Long-Range Spotting",
color = :viridis,
aspect_ratio = 1,
size = (800, 500)
)