Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
e12b35c
initial conversion
brettedw Jan 15, 2026
1f39aa4
cfb_calc tests
brettedw Jan 16, 2026
82cbc08
c6 tests
brettedw Jan 16, 2026
9a7d6d3
cbh tests
brettedw Jan 16, 2026
cc2197f
crown_fuel_load tweak
brettedw Jan 16, 2026
8d21719
crown base height import unused
brettedw Jan 16, 2026
5f56713
r helpers and distance_at_time tests
brettedw Jan 16, 2026
7e92146
fire intensity test
brettedw Jan 16, 2026
9395fb5
flank ros test
brettedw Jan 16, 2026
b3d84f0
fmc test
brettedw Jan 16, 2026
413810a
test grass fuel moisture
brettedw Jan 16, 2026
383cdaf
test length to breadth at time
brettedw Jan 16, 2026
62014e6
test length to breadth
brettedw Jan 16, 2026
078d3e6
test ros theta
brettedw Jan 16, 2026
e168dad
test rate of spread at time
brettedw Jan 16, 2026
84683f2
rate of spread tests and cleanup
brettedw Jan 16, 2026
4ab1a63
test surface fuel consumption
brettedw Jan 16, 2026
f59a402
test total fuel consumption
brettedw Jan 16, 2026
af51eb6
absolute errors
brettedw Jan 17, 2026
9a75a8c
slope test
brettedw Jan 17, 2026
30bc3cb
comment
brettedw Jan 19, 2026
367de50
r equivalent tweak
brettedw Jan 20, 2026
5a3d3cc
fbp fuel type strict
brettedw Jan 20, 2026
241bb98
r behaviour tweak
brettedw Jan 20, 2026
77e2cd9
input validation and clamping
brettedw Jan 20, 2026
b3cbe58
fbp tests
brettedw Jan 20, 2026
38d96e2
test file & field names = R
brettedw Jan 26, 2026
4a63b50
hffmc tests cleanup
brettedw Jan 26, 2026
0b5a83b
function names and backward compatibility
brettedw Jan 26, 2026
05bfe1e
conftest cleanup
brettedw Jan 26, 2026
5ca2b92
overwinter drought code
brettedw Jan 26, 2026
ae801ba
sdmc & tests
brettedw Jan 26, 2026
4400a03
lros & pros
brettedw Jan 27, 2026
3f78b07
format code
brettedw Jan 27, 2026
171cf94
add ruff
brettedw Jan 27, 2026
c20bdf1
cfb tests fix
brettedw Jan 27, 2026
1c7b6ac
c6 tweak
brettedw Jan 27, 2026
045d42f
slope calc test adjustments
brettedw Jan 27, 2026
1a7651e
test reason cleanup
brettedw Jan 27, 2026
ef9177e
remove safe_power
brettedw Jan 27, 2026
77220af
docstring cleanup
brettedw Jan 27, 2026
ef7f5e8
delete commented decorator
brettedw Jan 27, 2026
bf37d38
link to code reference default
brettedw Jan 27, 2026
4d93297
combine classes
brettedw Jan 27, 2026
bd0ace8
snake case
brettedw Jan 28, 2026
1c217b1
outputs & cleanup
brettedw Jan 28, 2026
42d45e4
rate_of_spread comments
brettedw Jan 28, 2026
8568fea
BUIo and q description
brettedw Jan 28, 2026
ccfcbbe
r behaviour doc string
brettedw Jan 28, 2026
cc7e9ee
remove depreciated
brettedw Jan 28, 2026
43bf077
unused import
brettedw Jan 28, 2026
be561d3
FUEL_TYPE_ROS comments
brettedw Jan 29, 2026
3fb0bd0
direction doc string
brettedw Jan 29, 2026
4b03c5f
readme update
brettedw Jan 30, 2026
384dfca
readme cleanup
brettedw Jan 30, 2026
a83f808
pros abs error threshold
brettedw Feb 13, 2026
34c0eab
reference table 7
brettedw Feb 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 9 additions & 6 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
{
"python.testing.pytestArgs": [
"cffdrs"
],
"python.testing.unittestEnabled": false,
"python.testing.pytestEnabled": true
}
"python.testing.pytestArgs": ["cffdrs"],
"python.testing.unittestEnabled": false,
"python.testing.pytestEnabled": true,
"[python]": {
"editor.defaultFormatter": "charliermarsh.ruff"
},
"editor.formatOnSave": true,
"editor.rulers": [100]
}
120 changes: 119 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,122 @@
# cffdrs
This is a python translation of the R code available from [cran](https://cran.r-project.org/web/packages/cffdrs/index.html) - this does not include all the functionality available in that package yet.

This is a python translation of the R code available from [cran](https://cran.r-project.org/web/packages/cffdrs/index.html)

This project provides a group of new functions to calculate the outputs of the two main components of the [Canadian Forest Fire Danger Rating System (CFFDRS) Van Wagner and Pickett (1985)](https://cfs.nrcan.gc.ca/publications?id=19973) at various time scales: the [Fire Weather Index (FWI) System Wan Wagner (1985)](https://cfs.nrcan.gc.ca/publications?id=19927) and the [Fire Behaviour Prediction (FBP) System Forestry Canada Fire Danger Group (1992)](http://cfs.nrcan.gc.ca/pubwarehouse/pdfs/10068.pdf). Some functions have two versions, table and raster based.

## Usage

### Fire Weather Index (FWI) System

The FWI system consists of several components that can be calculated individually or chained together.

#### Fine Fuel Moisture Code (FFMC)

```python
from cffdrs.fwi import fine_fuel_moisture_code

# Calculate FFMC
ffmc = fine_fuel_moisture_code(ffmc_yda=85.0, temp=20.0, rh=60.0, ws=15.0, prec=0.0)
```

#### Duff Moisture Code (DMC)

```python
from cffdrs.fwi import duff_moisture_code

# Calculate DMC
dmc = duff_moisture_code(dmc_yda=20.0, temp=25.0, rh=45.0, prec=0.0, lat=55.0, mon=6)
```

#### Drought Code (DC)

```python
from cffdrs.fwi import drought_code

# Calculate DC
dc = drought_code(dc_yda=150.0, temp=30.0, rh=30.0, prec=0.0, lat=50.0, mon=7)
```

#### Initial Spread Index (ISI)

```python
from cffdrs.fwi import initial_spread_index

# Calculate ISI
isi = initial_spread_index(ffmc=85.0, ws=20.0)
```

#### Buildup Index (BUI)

```python
from cffdrs.fwi import buildup_index

# Calculate BUI
bui = buildup_index(dmc=25.0, dc=160.0)
```

#### Fire Weather Index (FWI)

```python
from cffdrs.fwi import fire_weather_index

# Calculate FWI
fwi = fire_weather_index(isi=10.0, bui=50.0)
```

### Fire Behaviour Prediction (FBP) System

The FBP system predicts fire behavior based on fuel type and weather conditions.

```python
from cffdrs.fbp import fbp
from cffdrs.models import FBPInput

# Create input parameters
input_data = FBPInput(
fuel_type="C2",
ffmc=85.0,
bui=40.0,
ws=15.0,
wd=45.0,
gs=0.0,
lat=55.0,
lon=-120.0,
elv=100.0,
dj=180.0,
d0=0.0,
hr=1.0,
pc=50.0,
pdf=35.0,
cc=80.0,
gfl=0.35,
cbh=2.0,
cfl=1.0,
isi=8.0,
fmc=0.0,
theta=0.0,
accel=0,
aspect=0.0,
bui_eff=1
)

# Calculate primary outputs
primary_results = fbp(input=input_data, output="Primary")


# Calculate all outputs
all_results = fbp(input=input_data, output="All")
print(all_results[0])
# Output: FBPAllOutput(id='1', cfb=..., cfc=..., fd='S', hfi=..., raz=..., ros=..., sfc=..., tfc=..., be=..., sf=..., isi=..., ffmc=..., fmc=..., d0=..., rso=..., csi=..., fros=..., bros=..., hrost=..., frost=..., brost=..., fcfb=..., bcfb=..., ffi=..., bfi=..., ftfc=..., btfc=..., ti=..., fti=..., bti=..., lb=..., lbt=..., wsv=..., dh=..., db=..., df=..., tros=..., trost=..., tcfb=..., tfi=..., ttfc=..., tti=...)
```

#### Batch Processing

You can process multiple inputs at once:

```python
inputs = [FBPInput(fuel_type="C2", ffmc=85.0, bui=40.0, ws=15.0), FBPInput(fuel_type="C3", ffmc=90.0, bui=50.0, ws=20.0)]
results = fbp(input=inputs, output="Primary")
for result in results:
print(result)
```
41 changes: 41 additions & 0 deletions cffdrs/back_rate_of_spread.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import math
from cffdrs.constants import FuelType, FFMC_COEFFICIENT
from cffdrs.rate_of_spread import rate_of_spread


def back_rate_of_spread(fuel_type: FuelType, ffmc, bui, wsv, fmc, sfc, pc, pdf, cc, cbh):
"""
Back Fire Rate of Spread Calculator

Calculate the Back Fire Spread Rate. All variables names are
laid out in the same manner as Forestry Canada Fire Danger Group (FCFDG)
(1992).

:param fuel_type: The Fire Behaviour Prediction FuelType
:param ffmc: Fine Fuel Moisture Code
:param bui: Buildup Index
:param wsv: Wind Speed Vector
:param fmc: Foliar Moisture Content
:param sfc: Surface Fuel Consumption
:param pc: Percent Conifer
:param pdf: Percent Dead Balsam Fir
:param cc: Degree of Curing (just "C" in FCFDG 1992)
:param cbh: Crown Base Height

:returns: BROS Back Fire Rate of Spread
"""
# Eq. 46 (FCFDG 1992)
# Calculate the FFMC function from the ISI equation
m = FFMC_COEFFICIENT * (101 - ffmc) / (59.5 + ffmc)
# Eq. 45 (FCFDG 1992)
fF = 91.9 * math.exp(-0.1386 * m) * (1.0 + (m**5.31) / 49300000)
# Eq. 75 (FCFDG 1992)
# Calculate the Back fire wind function
bf_w = math.exp(-0.05039 * wsv)
# Calculate the ISI associated with the back fire spread rate
# Eq. 76 (FCFDG 1992)
bisi = 0.208 * bf_w * fF
# Eq. 77 (FCFDG 1992)
# Calculate final Back fire spread rate
bros = rate_of_spread(fuel_type, bisi, bui, fmc, sfc, pc, pdf, cc, cbh)
return bros
32 changes: 32 additions & 0 deletions cffdrs/buildup_effect.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import math
from cffdrs.constants import FuelType, FUEL_TYPE_DEFAULTS


def buildup_effect(fuel_type: FuelType, bui):
"""
Build Up Effect Calculator

Computes the Buildup Effect on Fire Spread Rate. All variables
names are laid out in the same manner as Forestry Canada Fire Danger Group
(FCFDG)(1992).

R behavior: if fuel unknown and BUI > 0 -> NA, else -> 1

:param fuel_type: The Fire Behaviour Prediction FuelType
:param bui: The Buildup Index value

:returns: BE Build up effect
"""
# Eq. 54 (FCFDG 1992) The Buildup Effect
fuel = FUEL_TYPE_DEFAULTS.get(fuel_type)

if fuel is None:
return math.nan if bui > 0 else 1.0

buio = fuel["BUIo"]
q = fuel["Q"]

if bui > 0 and buio > 0:
return math.exp(50 * math.log(q) * (1 / bui - 1 / buio))

return 1.0
94 changes: 94 additions & 0 deletions cffdrs/c6_calc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import math
from cffdrs.buildup_effect import buildup_effect
from cffdrs.cfb_calc import crown_fraction_burned


def intermediate_surface_rate_of_spread_c6(isi):
"""
Intermediate Surface Rate of Spread for C6 Calculator

:param isi: Initial Spread Index

:returns: RSI Intermediate surface fire spread rate
"""
# Eq. 62 (FCFDG 1992) Intermediate surface fire spread rate
rsi = 30 * (1 - math.exp(-0.08 * isi)) ** 3.0
return rsi


def surface_rate_of_spread_c6(rsi, bui):
"""
Surface Rate of Spread for C6 Calculator

:param rsi: Intermediate surface fire spread rate
:param bui: Buildup Index

:returns: RSS Surface fire spread rate (m/min)
"""
# Eq. 63 (FCFDG 1992) Surface fire spread rate (m/min)
rss = rsi * buildup_effect("C6", bui)
return rss


def crown_rate_of_spread_c6(isi, fmc):
"""
Crown Rate of Spread for C6 Calculator

:param isi: Initial Spread Index
:param fmc: Foliar Moisture Content

:returns: RSC Crown fire spread rate (m/min)
"""
# Average foliar moisture effect
fme_avg = 0.778
# Eq. 59 (FCFDG 1992) Crown flame temperature (degrees K)
tt = 1500 - 2.75 * fmc # this comes from the source R code but is not used
# Eq. 60 (FCFDG 1992) Head of ignition (kJ/kg)
H = 460 + 25.9 * fmc # this comes from the source R code but is not used
# Eq. 61 (FCFDG 1992) Average foliar moisture effect
fme = ((1.5 - 0.00275 * fmc) ** 4.0) / (460 + 25.9 * fmc) * 1000
# Eq. 64 (FCFDG 1992) Crown fire spread rate (m/min)
rsc = 60 * (1 - math.exp(-0.0497 * isi)) * fme / fme_avg
return rsc


def crown_fraction_burned_c6(rsc, rss, rso):
"""
Crown Fraction Burned for C6 Calculator

:param rsc: Crown fire spread rate
:param rss: Surface fire spread rate
:param rso: Surface fire rate of spread

:returns: CFB Crown fraction burned
"""

cond1 = rsc > rss

if math.isnan(rso):
cond2 = math.nan
else:
cond2 = rss > rso

if cond1 is False or cond2 is False:
return 0.0

if math.isnan(cond2):
return math.nan

return crown_fraction_burned(rss, rso)


def rate_of_spread_c6(rsc, rss, cfb):
"""
Rate of Spread for C6 Calculator

:param rsc: Crown fire spread rate
:param rss: Surface fire spread rate
:param cfb: Crown fraction burned

:returns: ROS Rate of spread (m/min)
"""
# Eq. 65 (FCFDG 1992) Calculate Rate of spread (m/min)
ros = rss + cfb * (rsc - rss) if rsc > rss else rss
return ros
54 changes: 54 additions & 0 deletions cffdrs/cfb_calc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import math

from cffdrs.r_helpers import safe_div


def critical_surface_intensity(fmc, cbh):
"""
Critical Surface Intensity Calculator

:param fmc: Foliar Moisture Content
:param cbh: Crown Base Height

:returns: CSI Critical surface intensity
"""
# Eq. 56 (FCFDG 1992) Critical surface intensity
csi = 0.001 * (cbh**1.5) * (460 + 25.9 * fmc) ** 1.5
return csi


def surface_fire_rate_of_spread(csi, sfc):
"""
Surface Fire Rate of Spread Calculator

Follows R rules:
- x / 0 -> Inf or -Inf
- 0 / 0 -> NaN
- NaN propagates

:param csi: Critical surface intensity
:param sfc: Surface Fuel Consumption

:returns: RSO Surface fire rate of spread (m/min)
"""
# Eq. 57 (FCFDG 1992) Surface fire rate of spread (m/min)
rso = safe_div(csi, (300 * sfc))
return rso


def crown_fraction_burned(ros, rso):
"""
Crown Fraction Burned Calculator

:param ros: Rate of Spread
:param rso: Surface fire rate of spread

:returns: CFB Crown fraction burned
"""
# Eq. 58 (FCFDG 1992) Crown fraction burned
if math.isnan(rso):
return math.nan

if ros > rso:
return 1 - math.exp(-0.23 * (ros - rso))
return 0.0
Loading