Note

You can download this demonstration as a Jupyter Notebook here

Forest fire

This notebook presents an agent-based model that simulates a forest fire. It demonstrates how to use the agentpy package to work with a spatial grid and create animations, and perform a parameter sweep.

[1]:
# Model design
import agentpy as ap
import numpy as np

# Visualization
import matplotlib.pyplot as plt
import seaborn as sns
import IPython

About the model

The model ist based on the NetLogo FireSimple model by Uri Wilensky and William Rand, who describe it as follows:

“This model simulates the spread of a fire through a forest. It shows that the fire’s chance of reaching the right edge of the forest depends critically on the density of trees. This is an example of a common feature of complex systems, the presence of a non-linear threshold or critical parameter. […]

The fire starts on the left edge of the forest, and spreads to neighboring trees. The fire spreads in four directions: north, east, south, and west.

The model assumes there is no wind. So, the fire must have trees along its path in order to advance. That is, the fire cannot skip over an unwooded area (patch), so such a patch blocks the fire’s motion in that direction.”

Model definition

[2]:
class ForestModel(ap.Model):

    def setup(self):

        # Create grid (forest)
        forest = self.add_grid(self.p.size)

        # Create agents (trees)
        n_trees = int(self.p.density * (self.p.size**2))
        forest.add_agents(n_trees, random=True)

        # Initiate a dynamic variable for all trees
        # Condition 0: Alive, 1: Burning, 2: Burned
        self.agents.condition = 0

        # Start a fire from the left side of the grid
        unfortunate_trees = forest.get_agents([(0, self.p.size), (0, 0)])
        unfortunate_trees.condition = 1

    def step(self):

        # Select burning trees
        burning_trees = self.agents.select(self.agents.condition == 1)

        # Spread fire
        for agent in burning_trees:
            for neighbor in agent.neighbors():
                if neighbor.condition == 0:
                    neighbor.condition = 1 # Neighbor starts burning
            agent.condition = 2 # Tree burns out

        # Stop simulation if no fire is left
        if len(burning_trees) == 0: self.stop()

    def end(self):

        # Document a measure at the end of the simulation
        burned_trees = len(self.agents.select(self.agents.condition == 2))
        self.measure('Percentage of burned trees',
                    burned_trees / len(self.agents))

Single-run animation

[3]:
# Define parameters

parameters = {
    'density': 0.6, # Percentage of grid covered by trees
    'size': 50 # Height and length of the grid
}
[4]:
# Create single-run animation with custom colors

def animation_plot(model, ax):
    attr_grid = model.env.attribute('condition', empty=3)
    color_dict = {0:'#7FC97F', 1:'#d62c2c', 2:'#e5e5e5', 3:'#d5e5d5'}
    ap.gridplot(attr_grid, ax=ax, color_dict=color_dict, convert=True)
    ax.set_title(f"Simulation of a forest fire\n"
                 f"Time-step: {model.t}, Trees left: "
                 f"{len(model.agents.select(model.agents.condition == 0))}")

fig, ax = plt.subplots()
model = ForestModel(parameters)
animation = ap.animate(model, fig, ax, animation_plot)
IPython.display.HTML(animation.to_jshtml())
[4]:

Parameter sweep

[5]:
# Prepare parameter sample
# Arranges 30 values for density from 0.1 to 1

parameter_ranges = {
    'density': (0.2,0.6),
    'size': 100
    }

sample = ap.sample(parameter_ranges, n=30)
[6]:
# Perform experiment
# Repeats simulation 30 times for each value of density

exp = ap.Experiment(ForestModel, sample, iterations=30)
results = exp.run()
Scheduled runs: 900
Completed: 900, estimated time remaining: 0:00:00
Experiment finished
Run time: 0:04:52.087680
[7]:
# To save and load data

# results.save()
# results = ap.load('ForestModel')
[8]:
# Plot sensitivity
# Every point shows average over 50 runs

data = results.arrange_measures() # Create plotting data

sns.set()
ax = sns.lineplot(data=data, x='density', y='Percentage of burned trees')
_images/agentpy_forest_fire_13_0.png