{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Forest fire" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This notebook presents an agent-based model that simulates a forest fire.\n", "It demonstrates how to use the [agentpy](https://agentpy.readthedocs.io) package to work with a spatial grid and create animations, and perform a parameter sweep. " ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "# Model design\n", "import agentpy as ap\n", "\n", "# Visualization\n", "import matplotlib.pyplot as plt\n", "import seaborn as sns\n", "import IPython" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## About the model\n", "\n", "The model ist based on the [NetLogo FireSimple model](http://ccl.northwestern.edu/netlogo/models/FireSimple) by Uri Wilensky and William Rand, who describe it as follows:\n", "\n", "> \"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. [...] \n", ">\n", "> 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.\n", ">\n", ">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.\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Model definition" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "class ForestModel(ap.Model):\n", " \n", " def setup(self):\n", " \n", " # Create agents (trees) \n", " n_trees = int(self.p['Tree density'] * (self.p.size**2))\n", " trees = self.agents = ap.AgentList(self, n_trees)\n", " \n", " # Create grid (forest)\n", " self.forest = ap.Grid(self, [self.p.size]*2, track_empty=True) \n", " self.forest.add_agents(trees, random=True, empty=True)\n", " \n", " # Initiate a dynamic variable for all trees\n", " # Condition 0: Alive, 1: Burning, 2: Burned\n", " self.agents.condition = 0 \n", " \n", " # Start a fire from the left side of the grid\n", " unfortunate_trees = self.forest.agents[0:self.p.size, 0:2]\n", " unfortunate_trees.condition = 1 \n", " \n", " def step(self):\n", " \n", " # Select burning trees\n", " burning_trees = self.agents.select(self.agents.condition == 1)\n", "\n", " # Spread fire \n", " for tree in burning_trees:\n", " for neighbor in self.forest.neighbors(tree):\n", " if neighbor.condition == 0:\n", " neighbor.condition = 1 # Neighbor starts burning\n", " tree.condition = 2 # Tree burns out \n", " \n", " # Stop simulation if no fire is left\n", " if len(burning_trees) == 0: \n", " self.stop()\n", " \n", " def end(self):\n", " \n", " # Document a measure at the end of the simulation\n", " burned_trees = len(self.agents.select(self.agents.condition == 2))\n", " self.report('Percentage of burned trees', \n", " burned_trees / len(self.agents))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Single-run animation" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "# Define parameters\n", "\n", "parameters = {\n", " 'Tree density': 0.6, # Percentage of grid covered by trees\n", " 'size': 50, # Height and length of the grid\n", " 'steps': 100,\n", "}" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", "\n", "\n", "\n", "\n", "\n", "