Forest fire
The forest fire model is defined as a cellular automaton on a grid. A position can be empty or occupied by a tree which is ok, burning or burnt. We implement a slightly different ruleset to that of Drossel and Schwabl (1992), so that our implementation can be compared with other ABM frameworks
- A burning position turns into a burnt position
- A tree will burn if at least one neighbor is burning
The forest has an innate density
, which is the proportion of trees initialized as green
, however all trees that reside on the left side of the grid are burning
. The model is also available from the Models
module as Models.forest_fire
.
Defining the core structures
Cellular automata don't necessarily require an agent-like structure. Here we will demonstrate how a model focused solution is possible.
using Agents, Random
using CairoMakie
@agent struct Automata(GridAgent{2}) end
The agent type Automata
is effectively a dummy agent, for which we will invoke dummystep
when stepping the model.
We then make a setup function that initializes the model.
function forest_fire(; density = 0.7, griddims = (100, 100), seed = 2)
space = GridSpaceSingle(griddims; periodic = false, metric = :manhattan)
rng = Random.MersenneTwister(seed)
# The `trees` field is coded such that
# Empty = 0, Green = 1, Burning = 2, Burnt = 3
forest = StandardABM(Automata, space; rng, model_step! = tree_step!,
properties = (trees = zeros(Int, griddims),), container = Vector)
for I in CartesianIndices(forest.trees)
if rand(abmrng(forest)) < density
# Set the trees at the left edge on fire
forest.trees[I] = I[1] == 1 ? 2 : 1
end
end
return forest
end
forest_fire (generic function with 1 method)
Defining the step!
function tree_step!(forest)
# Find trees that are burning (coded as 2)
for I in findall(isequal(2), forest.trees)
for idx in nearby_positions(I.I, forest)
# If a neighbor is Green (1), set it on fire (2)
if forest.trees[idx...] == 1
forest.trees[idx...] = 2
end
end
# Finally, any burning tree is burnt out (2)
forest.trees[I] = 3
end
return forest.trees
end
tree_step! (generic function with 1 method)
Running the model
forest = forest_fire()
step!(forest, 1)
count(t == 3 for t in forest.trees) # Number of burnt trees on step 1
61
step!(forest, 10)
count(t == 3 for t in forest.trees) # Number of burnt trees on step 11
529
Now we can do some data collection as well using an aggregate function percentage
:
forest = forest_fire(griddims = (20, 20))
burnt_percentage(f) = count(t == 3 for t in f.trees) / prod(size(f.trees))
mdata = [burnt_percentage]
_, data = run!(forest, 10; mdata)
data
Row | time | burnt_percentage |
---|---|---|
Int64 | Float64 | |
1 | 0 | 0.0 |
2 | 1 | 0.03 |
3 | 2 | 0.0575 |
4 | 3 | 0.095 |
5 | 4 | 0.13 |
6 | 5 | 0.16 |
7 | 6 | 0.1775 |
8 | 7 | 0.195 |
9 | 8 | 0.2125 |
10 | 9 | 0.2275 |
11 | 10 | 0.25 |
Now let's plot the model. We use green for unburnt trees, red for burning and a dark red for burnt.
forest = forest_fire()
step!(forest, 1)
plotkwargs = (
add_colorbar = false,
heatarray = :trees,
heatkwargs = (
colorrange = (0, 3),
colormap = cgrad([:white, :green, :red, :darkred]; categorical = true),
),
)
fig, _ = abmplot(forest; plotkwargs...)
fig
or animate it
forest = forest_fire(density = 0.7, seed = 10)
abmvideo(
"forest.mp4",
forest;
framerate = 5,
frames = 20,
spf = 5,
title = "Forest Fire",
plotkwargs...,
)
┌ Warning: keyword `spf` is deprecated in favor of `dt`.
└ @ AgentsVisualizations ~/.julia/packages/Agents/eKASH/ext/AgentsVisualizations/src/convenience.jl:102