Chapter 10: Carbon Modelling
============================
Learning Objectives
-------------------
After reading this chapter, you should be able to:
- Explain the role of carbon accounting in modern forest management
- Calculate standing carbon stocks from forest inventory data
- Model carbon fluxes from harvest and decomposition
- Integrate carbon accounting into wood supply optimization
- Understand the policy context (BC Emissions Trading System, carbon credits)
Why Carbon Matters in Forest Management
---------------------------------------
Carbon accounting has become a central concern in forest management for
several reasons:
1. **Climate policy**: British Columbia's Emissions Trading System (ETS)
requires forest operators to report and offset emissions
2. **Carbon credits**: Forest carbon sequestration can generate revenue
through carbon markets
3. **Sustainability reporting**: Stakeholders demand carbon-transparent
management plans
4. **Regulatory compliance**: The BC Forest and Range Practices Act
requires carbon considerations in planning
Carbon in the Forest System
---------------------------
Forest carbon exists in multiple pools:
.. list-table::
:header-rows: 1
:widths: 25 30 45
* - Pool
- Location
- Description
* - Above-ground biomass
- Living trees (bole, branches, foliage)
- Largest pool in most stands
* - Below-ground biomass
- Roots
- Typically 15-25% of above-ground
* - Dead wood
- Standing dead trees, coarse woody debris
- Decomposes slowly (decades)
* - Litter
- Leaf litter, fine woody debris
- Decomposes moderately (years)
* - Soil organic matter
- Humus layer
- Largest but slowest-changing pool
.. mermaid::
graph TD
ATMOSPHERE["Atmosphere
(CO₂)"] --> SEQUESTER["Photosynthesis
(carbon uptake)"]
SEQUESTER --> TREES["Living trees"]
TREES --> ABOVE["Above-ground
biomass"]
TREES --> BELOW["Below-ground
biomass"]
TREES --> HARVEST["Harvest"]
HARVEST --> PRODUCTS["Wood products
(long-term storage)"]
HARVEST --> DECOMP["Decomposition
(CO₂ release)"]
DECOMP --> ATMOSPHERE
TREES --> DEAD["Dead wood"]
DEAD --> DECOMP
Carbon Calculation Basics
-------------------------
Carbon is approximately 50% of dry biomass. The conversion from biomass
to carbon uses a simple factor:
.. math::
\\text{Carbon (tonnes)} = \\text{Biomass (tonnes)} \\times 0.5
Biomass can be estimated from volume using species-specific factors:
.. math::
\\text{Biomass (tonnes/ha)} = \\text{Volume (m³/ha)} \\times \\text{Bulk Density (tonnes/m³)} \\times (1 + \\text{Root-Shoot Ratio})
Using ws3 for Carbon Calculations
----------------------------------
ws3 doesn't have built-in carbon functions, but you can calculate carbon
stocks from the model output:
.. code-block:: python
from ws3.forest import ForestModel
from ws3.core import Curve
# Define biomass curves (tonnes/ha of carbon)
df_carbon = Curve(
label="DF-SI50_carbon",
points=[(0, 0), (10, 1), (20, 5), (30, 12), (40, 22),
(50, 35), (60, 50), (70, 65), (80, 78), (90, 88), (100, 95)]
)
spruce_carbon = Curve(
label="SP-SI40_carbon",
points=[(0, 0), (10, 0.5), (20, 3), (30, 8), (40, 15),
(50, 25), (60, 38), (70, 50), (80, 60), (90, 68), (100, 73)]
)
# Calculate carbon stock for a development type
dt_area = 500.0 # hectares
dt_age = 60 # years
carbon_per_ha = df_carbon(dt_age)
total_carbon = carbon_per_ha * dt_area
print(f"Carbon stock: {total_carbon:.1f} tonnes C")
print(f"CO₂ equivalent: {total_carbon * 3.67:.1f} tonnes CO₂")
Carbon Fluxes from Harvest
--------------------------
When trees are harvested, carbon is released through:
1. **Immediate decomposition**: Slash and residues decompose (5-20 year half-life)
2. **Product decay**: Wood products gradually release carbon (decades to centuries)
3. **Soil disturbance**: Harvesting disturbs soil organic matter
.. mermaid::
graph TD
HARVEST["Harvest"] --> SLASH["Slash decomposition
(fast, 5-20 yr)"]
HARVEST --> PRODUCTS["Wood products
(slow, decades)"]
HARVEST --> SOIL["Soil disturbance
(very slow, centuries)"]
SLASH --> RELEASE["CO₂ release"]
PRODUCTS --> RELEASE
SOIL --> RELEASE
A simple carbon accounting model:
.. code-block:: python
def calculate_harvest_carbon_flux(volume_harvested_m3, species="DF"):
"""Calculate carbon flux from a harvest event."""
# Conversion: m³ to tonnes biomass (approximate)
bulk_density = {"DF": 0.45, "SP": 0.40, "CE": 0.35}
biomass_factor = bulk_density.get(species, 0.40)
biomass_tonnes = volume_harvested_m3 * biomass_factor
carbon_tonnes = biomass_tonnes * 0.5
# Split into pools
slash_fraction = 0.3 # 30% goes to slash (fast decomposition)
product_fraction = 0.6 # 60% goes to products (slow decomposition)
soil_fraction = 0.1 # 10% from soil disturbance
slash_carbon = carbon_tonnes * slash_fraction
product_carbon = carbon_tonnes * product_fraction
soil_carbon = carbon_tonnes * soil_fraction
return {
"slash": slash_carbon,
"products": product_carbon,
"soil": soil_carbon,
"total": carbon_tonnes
}
flux = calculate_harvest_carbon_flux(1000, species="DF")
print(f"Slash carbon: {flux['slash']:.1f} tonnes C")
print(f"Product carbon: {flux['products']:.1f} tonnes C")
print(f"Soil carbon: {flux['soil']:.1f} tonnes C")
print(f"Total flux: {flux['total']:.1f} tonnes C")
Carbon in Optimization
----------------------
Carbon can be incorporated into the optimization objective:
.. code-block:: python
from ws3.opt import Problem
prob = Problem("carbon_optimization")
# Decision variables
harvest_var_names = {}
for dt_code in ["DF-SI50", "SP-SI40"]:
for period in range(20):
var_name = f"harv_{dt_code}_p{period}"
prob.add_var(var_name, vtype="continuous", lb=0)
harvest_var_names[(dt_code, period)] = var_name
# Objective: maximize NPV + carbon revenue
# z() takes a dict keyed on variable names
timber_price = 50 # $/m³
carbon_price = 50 # $/tonne CO₂ (ETS price)
discount_rate = 0.05
npv_coeffs = {}
for (dt_code, period), var_name in harvest_var_names.items():
volume_per_ha = 200 # m³/ha
timber_revenue = volume_per_ha * timber_price
carbon_revenue = volume_per_ha * 0.45 * 0.5 * 3.67 * carbon_price
coeff = (timber_revenue + carbon_revenue) * (1 + discount_rate) ** (-period * 5)
npv_coeffs[var_name] = coeff
prob.z(coeffs=npv_coeffs)
# Constraint: carbon budget (max allowable emissions)
max_carbon_budget = 10000 # tonnes CO₂ over 100 years
carbon_per_m3 = 0.45 * 0.5 * 3.67 # tonnes CO₂ per m³
carbon_coeffs = {}
for var_name in harvest_var_names.values():
carbon_coeffs[var_name] = 200 * carbon_per_m3
prob.add_constraint("carbon_budget", coeffs=carbon_coeffs, sense="leq", rhs=max_carbon_budget)
# Set solver and solve
prob.solver("highs")
prob.solve()
Carbon Reporting
----------------
For regulatory compliance, you need to report carbon stocks and fluxes:
.. code-block:: python
import pandas as pd
# Calculate carbon stocks by period
carbon_by_period = []
for period in range(20):
age = 20 + period * 5 # starting age + periods elapsed
carbon_stock = df_carbon(age) * 500 # tonnes C
carbon_by_period.append({
"period": period,
"age": age,
"carbon_stock_tonnes_C": carbon_stock,
"carbon_stock_tonnes_CO2": carbon_stock * 3.67
})
df_carbon_report = pd.DataFrame(carbon_by_period)
print(df_carbon_report)
# Export for reporting
df_carbon_report.to_csv("carbon_report.csv", index=False)
Policy Context
--------------
British Columbia's carbon pricing framework:
- **Carbon tax**: Applied to fossil fuel combustion (not directly to forestry)
- **Emissions Trading System (ETS)**: Cap-and-trade for large emitters
- **Forest Management Carbon Budget**: Each FMU has an allowable carbon
budget based on projected stock changes
Key references:
- BC Ministry of Forests: *Carbon Accounting Guidelines for Forest Management*
- BC Emissions Trading Scheme: *Forest Sector Participation*
- IPCC: *Good Practice Guidance for Land Use, Land-Use Change and Forestry*
Limitations
-----------
Carbon modelling in ws3 has limitations:
1. **Simplified pools**: Only above-ground biomass is typically modelled
2. **No soil dynamics**: Soil carbon changes are approximated
3. **No product substitution**: Benefits of wood substitution for concrete/steel
are not modelled
4. **Static prices**: Carbon prices are assumed constant
For more sophisticated carbon accounting, consider coupling ws3 with
specialized tools like CO2STATS or the BC Forest Carbon Calculator.
Exercises
---------
**Exercise 1 (Easy)**: Calculate the carbon stock for a 500-hectare
Douglas-fir stand at ages 40, 60, 80, and 100.
**Exercise 2 (Medium)**: Extend the carbon calculation to include below-ground
biomass (assume root-shoot ratio of 0.2) and calculate total ecosystem carbon.
**Exercise 3 (Hard)**: Formulate an optimization problem that maximizes
NPV subject to a carbon budget constraint. Compare the optimal harvest
schedule with and without the carbon constraint.
Further Reading
---------------
- :doc:`ch07_financial_analysis` — Financial analysis
- :doc:`ch05_optimization` — Optimization fundamentals
- :doc:`ch07_financial_analysis` — Financial scenario analysis
- IPCC Good Practice Guidance for Land Use, Land-Use Change and Forestry