Forest fire model
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 at x=1
on the grid are burning
. The model is also available from the Models
module as Models.forest_fire
.
Defining the core structures
We start by defining the agent type
using Agents, Random, Plots
mutable struct Tree <: AbstractAgent
id::Int
pos::Dims{2}
status::Symbol #:green, :burning, :burnt
end
The agent type Tree
has three fields: id
and pos
, which have to be there for any agent, and a status
field that we introduce for this specific model. The status
field will be :green
when the tree is ok, :burning
when on fire, and finally :burnt
.
We then make a setup function that initializes the model.
function forest_fire(; density = 0.7, griddims = (100, 100))
space = GridSpace(griddims; periodic = false, metric = :euclidean)
forest = AgentBasedModel(Tree, space)
# create and add trees to each position with a probability
# determined by the `density`.
for position in positions(forest)
if rand() < density
# Set the trees at position x=1 on fire
state = position[1] == 1 ? :burning : :green
add_agent!(position, forest, state)
end
end
return forest
end
forest = forest_fire()
AgentBasedModel with 7045 agents of type Tree space: GridSpace with size (100, 100), metric=euclidean and periodic=false scheduler: fastest
Defining the step!
Because of the way the forest fire model is defined, we only need a stepping function for the agents
function tree_step!(tree, forest)
# The current tree is burning
if tree.status == :burning
# Find all green neighbors and set them on fire
for neighbor in nearby_agents(tree, forest)
if neighbor.status == :green
neighbor.status = :burning
end
end
tree.status = :burnt
end
end
Running the model
step!(forest, tree_step!, 1)
count(t->t.status == :burnt, allagents(forest))
125
step!(forest, tree_step!, 10)
count(t->t.status == :burnt, allagents(forest))
1328
Now we can do some data collection as well using an aggregate function percentage
:
Random.seed!(2)
forest = forest_fire(griddims = (20, 20))
burnt_percentage(m) = count(t->t.status == :burnt, allagents(m)) / length(positions(m))
mdata = [burnt_percentage]
_, data = run!(forest, tree_step!, 10; mdata)
data
step | burnt_percentage | |
---|---|---|
Int64 | Float64 | |
1 | 0 | 0.0 |
2 | 1 | 0.065 |
3 | 2 | 0.14 |
4 | 3 | 0.23 |
5 | 4 | 0.295 |
6 | 5 | 0.35 |
7 | 6 | 0.4 |
8 | 7 | 0.4475 |
9 | 8 | 0.4975 |
10 | 9 | 0.56 |
11 | 10 | 0.635 |
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, tree_step!, 1)
function treecolor(a)
color = :green
if a.status == :burning
color = :red
elseif a.status == :burnt
color = :darkred
end
color
end
plotabm(forest; ac = treecolor, ms = 5)
or animate it
forest = forest_fire(density = 0.6)
anim = @animate for i in 0:10
i > 0 && step!(forest, tree_step!, 5)
p1 = plotabm(forest; ac = treecolor, ms = 5, msw = 0)
title!(p1, "step $(i)")
end
gif(anim, "forest.gif", fps = 2)