Fundamentals 10 min read

Simulating Forest Fires with Python Cellular Automata: A Step-by-Step Guide

Learn how to model and visualize forest fire spread using a cellular automaton in Python, covering grid initialization, state transition rules, visualization with Matplotlib, statistical tracking, and parameter experiments to observe how tree density influences fire dynamics.

Model Perspective
Model Perspective
Model Perspective
Simulating Forest Fires with Python Cellular Automata: A Step-by-Step Guide

Simulating Forest Fires with Cellular Automata

Problem: In a forest grid (some cells may be empty), when a fire starts it spreads to neighboring trees, burning them into empty cells; empty cells remain empty.

Programming Approach

Initialize the grid using 0, 1, 2 to represent empty ground, trees, and fire.

Determine the next state of each cell based on the previous state: If the previous cell was empty, it stays empty. If the previous cell was fire, it becomes empty. If the previous cell was a tree, it becomes fire if any of its four orthogonal neighbors were fire; otherwise it remains a tree.

NumPy is used for random number generation and array operations; Matplotlib visualizes the grid.

Python Code

Import Python Libraries

<code># Import Python libraries
import numpy as np  # generate arrays
import numpy.random as rand  # random integers
import matplotlib.pyplot as plt  # plotting
from IPython.display import display, clear_output  # display
import time  # time handling
</code>

Set Up Simulation Space

<code>def set_board(board_size=50, f_trees_start=0.5):
    '''
    Create the initial grid.
    Input:
        board_size: length of the grid side
        f_trees_start: probability that a cell contains a tree
    Output: 2D numpy array with values 0, 1, 2 (empty, tree, fire)
    '''
    # All cells start as empty
    game_board = np.zeros((board_size, board_size), dtype='int64')
    # Randomly place trees according to f_trees_start
    for i in range(board_size):
        for j in range(board_size):
            if rand.random() <= f_trees_start:
                game_board[i, j] = 1
    # Set the leftmost column on fire
    game_board[:, 0] = 2
    return game_board
</code>

Generate a 50 × 50 grid:

<code>set_board()
</code>

Plot the Grid

<code>def plotgrid(myarray=np.random.choice((0,1,2), size=(50,50), p=(0.55,0.4,0.05))):
    # Hide axes
    plt.tick_params(axis='both', which='both', bottom=False, top=False,
                    left=False, right=False, labelbottom=False, labelleft=False)
    # Create coordinate ranges
    x_range = np.linspace(0, myarray.shape[1] - 1, myarray.shape[1])
    y_range = np.linspace(0, myarray.shape[0] - 1, myarray.shape[0])
    x_indices, y_indices = np.meshgrid(x_range, y_range)
    # Locate trees and fire
    tree_x = x_indices[myarray == 1]
    tree_y = y_indices[myarray == 1]
    fire_x = x_indices[myarray == 2]
    fire_y = y_indices[myarray == 2]
    # Plot trees (green squares) and fire (red squares)
    plt.plot(tree_x, myarray.shape[0] - tree_y - 1, 'gs', markersize=10)
    plt.plot(fire_x, myarray.shape[0] - fire_y - 1, 'rs', markersize=10)
    plt.xlim([-1, myarray.shape[1]])
    plt.ylim([-1, myarray.shape[0]])
</code>

Randomly generate a forest image:

<code>plotgrid()
plt.show()
</code>

White cells represent empty ground, red cells fire, and green cells trees.

Update the Grid

As the fire spreads, the numbers of trees and empty cells change.

<code># Update the grid
def advance_board(game_board):
    '''
    Advance the simulation by one step.
    Input: current grid
    Output: next grid
    '''
    new_board = np.zeros_like(game_board)
    for i in range(len(game_board)):
        for j in range(len(game_board)):
            place = game_board[i, j]
            # Empty stays empty
            if place == 0:
                new_board[i, j] = 0
            # Fire becomes empty
            if place == 2:
                new_board[i, j] = 0
            # Tree may catch fire from neighbors
            if place == 1:
                new_board[i, j] = 1
                if i > 0 and game_board[i - 1, j] == 2:
                    new_board[i, j] = 2
                if i < game_board.shape[0] - 1 and game_board[i + 1, j] == 2:
                    new_board[i, j] = 2
                if j > 0 and game_board[i, j - 1] == 2:
                    new_board[i, j] = 2
                if j < game_board.shape[1] - 1 and game_board[i, j + 1] == 2:
                    new_board[i, j] = 2
    return new_board
</code>

Calculate Statistics

Compute the proportion of trees and empty cells; if these proportions stop changing, the simulation ends.

<code># Calculate statistics
def calc_stats(game_board, start_board):
    '''
    Compute the fractions of empty ground and trees.
    Input: current grid
    Output: (fraction_empty, fraction_tree)
    '''
    frac_empty = np.sum(game_board[game_board == 0]) / (game_board.shape[0] * game_board.shape[1])
    frac_tree = np.sum(game_board[game_board == 1]) / (game_board.shape[0] * game_board.shape[1])
    return frac_empty, frac_tree
</code>

Main Program

Start the fire from the western edge and let it spread.

<code># Start simulation
start_board = set_board()
f_trees_start = 0.7
board_size = 50
fig = plt.figure(figsize=(10, 10))
game_board = set_board(board_size=board_size, f_trees_start=f_trees_start)
plotgrid(game_board)
on_fire = True
frac_empty_last, frac_trees_last = 0, 0
frac_fire_last = 0
while on_fire:
    game_board = advance_board(game_board)
    plotgrid(game_board)
    time.sleep(0.1)
    clear_output(wait=True)
    display(fig)
    fig.clear()
    frac_empty, frac_trees = calc_stats(game_board, start_board)
    frac_fire = 1 - frac_empty - frac_trees
    print(frac_empty, frac_trees)
    if frac_empty_last == frac_empty and frac_trees_last == frac_trees_last and frac_fire == frac_fire_last:
        on_fire = False
    frac_empty_last, frac_trees_last, frac_fire_last = frac_empty, frac_trees, frac_fire
plt.close()
</code>

Initial forest image (tree density 0.7):

Evolution process:

Reducing density to 0.5 creates a sparser forest and limits fire spread:

Increasing density to 0.9 produces a denser forest and a wider fire front:

You can experiment with other parameters to observe different fire dynamics.

simulationPythonMatplotlibNumPygridcellular automataforest fire
Model Perspective
Written by

Model Perspective

Insights, knowledge, and enjoyment from a mathematical modeling researcher and educator. Hosted by Haihua Wang, a modeling instructor and author of "Clever Use of Chat for Mathematical Modeling", "Modeling: The Mathematics of Thinking", "Mathematical Modeling Practice: A Hands‑On Guide to Competitions", and co‑author of "Mathematical Modeling: Teaching Design and Cases".

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.