4.12. DSS Example: Avoided Fire
This notebook is an example implementation of a notebook-based decision-support system (DSS) for evaluating the net system-level carbon emissions impact of an avoided fire type project.
4.12.1. DSS implementation notes
At its core this DSS prototype uses ws3 and libcbm_py to do most of the heavy lifting in terms of simulating forest growth, simulating harvesting disturbances, and simulating forest ecosystem carbon stocks and fluxes. ws3 includes built-in functions that automate most of process of compiling CBM input data, importing this input data into CBM, running CBM, and extracting CBM output data in tablular format.
This DSS assumes we are using the standard “alternative scenario net of baseline scenario” approach to defining net carbon emissions, so we include functions that automate the process of pushing a pair of (baseline, alternative) scenarios through the simulation pipeline, collecting the output from each scenario, calculating the difference across scenarios (alternative minus baseline) and displaying this contrast as a function of time.
Note that most of the complex code that defines the behaviour of the DSS and underlying simulation engines is implemented in a companion util Python module. The functions we need from the util module are imported at the top of this notebook.
It is also possible to further “hide” the code parts of a Jupyter notebook-based DSS dashboard application by linking all required parameters and controls to interactive widgets, and then running the notebook throught the Voilà dashboard interface (not tested here, but Voilà is part of the standard ecosystem of Jupyter software should not be a problem). For anyone more familiar with the R programming environment, Voilà is to JupyterLab as Shiny is to RStudio (if this analogy is lost on you, then it was not meant for you).
4.12.2. Description of case
The study area for this case is a small subset of timber supply area (TSA) 22 in British Columbia. There has been little or no harvesting in this area to date, but in this hypothetical case we are supposing that there is a substantial risk of wildfire burning part of this forest in the future (e.g., as a result of climate-change-induced drought, decades of fire suppression policy, etc). We are assuming that it is possible to reduce the risk of future fire by deploying effort toward various types of fire risk reducing treatments (e.g., collecting fuel, controlled burns, thinning, creating fire breaks to limit fire spread, etc). We do not have any good data in this hypothetical case on either the cost or the effectiveness of these hypothetical treatments, but we nonetheless want to try put some bounds on the solution space to get a better idea of what is possible in terms of hypothetical emissions reductions.
To achieve this, we simulate a “maximum annual area burned” landscape level fire scenario based on the fire return internal of each stand type in the model. We then simulate ten reduced burning scenarios, in 10% increments (i.e., all the way down to “no fire”). This is not a “process-based” simulation, so we offer no mechanistic explanation for how the implicit “fire mitigation” treatments achieve these incremental reductions in burned area, or how much effort (cost) is required to implement the treatments at each increment (typically in the real world, one might expect a “diminishing marginal benefit” pattern for this sort of system, assuming that the next highest marginal return stands available are treated at each increment).
The idea here is to skip over all of the details of the mitigation treatments (because there is no hope of modelling that at this stage, given the data we have available) and calculate the net emission impact of achieving a given incremental level of reduced annual burned area. Given a maximum unit emission reduction price one was willing to pay, it is possible to use the output from this DSS to estimate the maximum price one should be willing to pay to achive a given area-burned reduction effect. Obviously there are limitations to how far we can go with this analysis (given limited available input data), but running through this analysis can still perhaps help set some bounds on the maximum “credible” fire risk reduction we can hope to achieve for this forest, and use that as the starting point for developing and testing actual treatment plans.
Note that most of the complex code that defines the behaviour of the DSS and underlying simulation engines is implemented in a companion util Python module. The functions we need from the util module are imported at the top of this notebook.
4.12.3. Deterministic versus Stochastic fire simulation
Note that DSS includes two versions of the fire simulation. The first and simplest version is a deterministic algorithm that burns the oldest stands first at every time step (with burning simulation stopping when a period area-burned target is achieved). The second and more complex version is a stochastic heuristic that selects random-aged stands to burn at each time step until the area-burned target is reached. We display results from both versions of the model in the graph at the bottom of the notebook. Interestingly both version of the model produce very similar results. Considering that the simpler deterministic version of the model runs several orders of magnitude faster than the stochastic version, we recommend using the simpler version in this case.
The purpose of this demo is not specifically to prove that deterministic models are better than stochastic models (they are not… that would be a silly conclusion to draw from a sample size of one). Rather, we wanted to illustrate the flexibility of the modelling framework and the relative ease of testing and comparing multiple simulation approaches before committing to one approach for a given case or situation.
[1]:
%load_ext autoreload
%autoreload
Optionally, uninstall the ws3 package and replace it with a pointer to this local clone of the GitHub repository code (useful if you want ot tweak the source code for whatever reason).
[2]:
clobber_ws3 = True
if clobber_ws3:
%pip uninstall -y ws3
%pip install -e ..
Found existing installation: ws3 1.1.0.dev0
Uninstalling ws3-1.1.0.dev0:
Successfully uninstalled ws3-1.1.0.dev0
Note: you may need to restart the kernel to use updated packages.
Obtaining file:///home/gep/tmp/ws3
Installing build dependencies ... done
Checking if build backend supports build_editable ... done
Getting requirements to build editable ... done
Installing backend dependencies ... done
Preparing editable metadata (pyproject.toml) ... done
Requirement already satisfied: dill in /home/gep/tmp/ws3/.venv/lib/python3.12/site-packages (from ws3==1.1.0.dev0) (0.4.0)
Requirement already satisfied: fiona in /home/gep/tmp/ws3/.venv/lib/python3.12/site-packages (from ws3==1.1.0.dev0) (1.10.1)
Requirement already satisfied: gurobipy in /home/gep/tmp/ws3/.venv/lib/python3.12/site-packages (from ws3==1.1.0.dev0) (12.0.3)
Requirement already satisfied: highspy in /home/gep/tmp/ws3/.venv/lib/python3.12/site-packages (from ws3==1.1.0.dev0) (1.11.0)
Requirement already satisfied: libcbm in /home/gep/tmp/ws3/.venv/lib/python3.12/site-packages (from ws3==1.1.0.dev0) (2.8.1)
Requirement already satisfied: matplotlib in /home/gep/tmp/ws3/.venv/lib/python3.12/site-packages (from ws3==1.1.0.dev0) (3.10.5)
Requirement already satisfied: numpy>=1.21 in /home/gep/tmp/ws3/.venv/lib/python3.12/site-packages (from ws3==1.1.0.dev0) (2.2.6)
Requirement already satisfied: pandas>=1.3 in /home/gep/tmp/ws3/.venv/lib/python3.12/site-packages (from ws3==1.1.0.dev0) (2.3.1)
Requirement already satisfied: profilehooks in /home/gep/tmp/ws3/.venv/lib/python3.12/site-packages (from ws3==1.1.0.dev0) (1.13.0)
Requirement already satisfied: pulp in /home/gep/tmp/ws3/.venv/lib/python3.12/site-packages (from ws3==1.1.0.dev0) (3.2.2)
Requirement already satisfied: rasterio in /home/gep/tmp/ws3/.venv/lib/python3.12/site-packages (from ws3==1.1.0.dev0) (1.4.3)
Requirement already satisfied: scipy>=1.7 in /home/gep/tmp/ws3/.venv/lib/python3.12/site-packages (from ws3==1.1.0.dev0) (1.16.1)
Requirement already satisfied: python-dateutil>=2.8.2 in /home/gep/tmp/ws3/.venv/lib/python3.12/site-packages (from pandas>=1.3->ws3==1.1.0.dev0) (2.9.0.post0)
Requirement already satisfied: pytz>=2020.1 in /home/gep/tmp/ws3/.venv/lib/python3.12/site-packages (from pandas>=1.3->ws3==1.1.0.dev0) (2025.2)
Requirement already satisfied: tzdata>=2022.7 in /home/gep/tmp/ws3/.venv/lib/python3.12/site-packages (from pandas>=1.3->ws3==1.1.0.dev0) (2025.2)
Requirement already satisfied: six>=1.5 in /home/gep/tmp/ws3/.venv/lib/python3.12/site-packages (from python-dateutil>=2.8.2->pandas>=1.3->ws3==1.1.0.dev0) (1.17.0)
Requirement already satisfied: attrs>=19.2.0 in /home/gep/tmp/ws3/.venv/lib/python3.12/site-packages (from fiona->ws3==1.1.0.dev0) (25.3.0)
Requirement already satisfied: certifi in /home/gep/tmp/ws3/.venv/lib/python3.12/site-packages (from fiona->ws3==1.1.0.dev0) (2025.8.3)
Requirement already satisfied: click~=8.0 in /home/gep/tmp/ws3/.venv/lib/python3.12/site-packages (from fiona->ws3==1.1.0.dev0) (8.2.1)
Requirement already satisfied: click-plugins>=1.0 in /home/gep/tmp/ws3/.venv/lib/python3.12/site-packages (from fiona->ws3==1.1.0.dev0) (1.1.1.2)
Requirement already satisfied: cligj>=0.5 in /home/gep/tmp/ws3/.venv/lib/python3.12/site-packages (from fiona->ws3==1.1.0.dev0) (0.7.2)
Requirement already satisfied: numexpr>=2.8.7 in /home/gep/tmp/ws3/.venv/lib/python3.12/site-packages (from libcbm->ws3==1.1.0.dev0) (2.11.0)
Requirement already satisfied: numba in /home/gep/tmp/ws3/.venv/lib/python3.12/site-packages (from libcbm->ws3==1.1.0.dev0) (0.61.2)
Requirement already satisfied: pyyaml in /home/gep/tmp/ws3/.venv/lib/python3.12/site-packages (from libcbm->ws3==1.1.0.dev0) (6.0.2)
Requirement already satisfied: mock in /home/gep/tmp/ws3/.venv/lib/python3.12/site-packages (from libcbm->ws3==1.1.0.dev0) (5.2.0)
Requirement already satisfied: openpyxl in /home/gep/tmp/ws3/.venv/lib/python3.12/site-packages (from libcbm->ws3==1.1.0.dev0) (3.1.5)
Requirement already satisfied: contourpy>=1.0.1 in /home/gep/tmp/ws3/.venv/lib/python3.12/site-packages (from matplotlib->ws3==1.1.0.dev0) (1.3.3)
Requirement already satisfied: cycler>=0.10 in /home/gep/tmp/ws3/.venv/lib/python3.12/site-packages (from matplotlib->ws3==1.1.0.dev0) (0.12.1)
Requirement already satisfied: fonttools>=4.22.0 in /home/gep/tmp/ws3/.venv/lib/python3.12/site-packages (from matplotlib->ws3==1.1.0.dev0) (4.59.0)
Requirement already satisfied: kiwisolver>=1.3.1 in /home/gep/tmp/ws3/.venv/lib/python3.12/site-packages (from matplotlib->ws3==1.1.0.dev0) (1.4.8)
Requirement already satisfied: packaging>=20.0 in /home/gep/tmp/ws3/.venv/lib/python3.12/site-packages (from matplotlib->ws3==1.1.0.dev0) (25.0)
Requirement already satisfied: pillow>=8 in /home/gep/tmp/ws3/.venv/lib/python3.12/site-packages (from matplotlib->ws3==1.1.0.dev0) (11.3.0)
Requirement already satisfied: pyparsing>=2.3.1 in /home/gep/tmp/ws3/.venv/lib/python3.12/site-packages (from matplotlib->ws3==1.1.0.dev0) (3.2.3)
Requirement already satisfied: llvmlite<0.45,>=0.44.0dev0 in /home/gep/tmp/ws3/.venv/lib/python3.12/site-packages (from numba->libcbm->ws3==1.1.0.dev0) (0.44.0)
Requirement already satisfied: et-xmlfile in /home/gep/tmp/ws3/.venv/lib/python3.12/site-packages (from openpyxl->libcbm->ws3==1.1.0.dev0) (2.0.0)
Requirement already satisfied: affine in /home/gep/tmp/ws3/.venv/lib/python3.12/site-packages (from rasterio->ws3==1.1.0.dev0) (2.4.0)
Building wheels for collected packages: ws3
Building editable for ws3 (pyproject.toml) ... done
Created wheel for ws3: filename=ws3-1.1.0.dev0-py3-none-any.whl size=4136 sha256=5df49d1809d2f15004f3046010971c142390fb47fb3bfa7620b09e11903424a7
Stored in directory: /tmp/pip-ephem-wheel-cache-v9qe22uf/wheels/fa/4b/10/3fe4b92a02fb87987a6fe53a10fad0a22a781bf98cd7b63f17
Successfully built ws3
Installing collected packages: ws3
Successfully installed ws3-1.1.0.dev0
Note: you may need to restart the kernel to use updated packages.
[3]:
import ws3.forest
import numpy as np
from util import schedule_fire_areacontrol, compile_scenario, plot_scenario, run_cbm_fire
from util import RandomAreaSelector, GreedyAreaSelector
from util import plot_resultsFuelMitigate_deter_stoch, resultsFuelMitigate_deter_stoch
[4]:
base_year = 2020
horizon = 10
period_length = 10
max_age = 1000
tvy_name = "totvol"
intensity = np.round(np.arange(0, 1.1, 0.1), decimals=1)
n_rep = 30
[5]:
fm = ws3.forest.ForestModel(model_name="tsa22",
model_path="data/woodstock_model_files_tsa22",
base_year=base_year,
horizon=horizon,
period_length=period_length,
max_age=max_age)
fm.import_landscape_section()
fm.import_areas_section(convert_periods_to_years=period_length)
fm.import_yields_section(convert_periods_to_years=period_length)
fm.import_actions_section(convert_periods_to_years=period_length)
fm.import_transitions_section(convert_periods_to_years=period_length)
fm.initialize_areas()
fm.add_null_action()
fm.reset_actions()
[6]:
df_deter_stoch = resultsFuelMitigate_deter_stoch(fm, intensity, n_rep, is_use_pickle=True)
[7]:
plot_resultsFuelMitigate_deter_stoch(df_deter_stoch)