Skip to content

Validation

The FWI equations are a standard, not a design choice. The only way to know they were transcribed correctly is to compare them with a known, published case. A test suite of properties alone ("FWI goes up with wind") would stay green with a wrong coefficient. That is why the suite has two layers.

1. The reference day, to 1e-9

tests/test_chain.py::test_published_reference_day runs the first day of the test series that ships with cffdrs, the reference R implementation of Van Wagner's equations:

Input Value
Start state FFMC 85, DMC 6, DC 15 (the standard start-up codes)
Month April
Temperature 17.0 °C
Relative humidity 42 %
Wind 25 km/h
Rain 0 mm
Output Expected Tolerance
FFMC 87.692980092774 1e-9 absolute
DMC 8.5450511359998 1e-9
DC 19.013999999999996 1e-9
ISI 10.853661073312 1e-9
BUI 8.4904265358371 1e-9
FWI 10.096371392382 1e-9

The tolerance is tight on purpose. Nothing is being calibrated: a transcription is being checked. A deviation larger than 1e-9 means a wrong coefficient, not floating-point noise.

Check it yourself in one line:

python examples/01_single_day.py

The DC can even be checked by hand. See the worked example.

2. What a single day cannot reach

The reference day is dry, so it never exercises the rain branches. The rest of the suite covers them, along with the behaviour the science requires:

Test What it guards
test_light_rain_is_intercepted_by_the_canopy rain below 0.5 / 1.5 / 2.8 mm does not wet the fuel
test_heavy_rain_lowers_every_code heavy rain wets all three codes, not just FFMC
test_dmc_wetting_covers_the_three_slopes_of_eq_13 every branch of Eq. 13 (DMC ≤ 33, ≤ 65, > 65)
test_the_saturation_correction_reads_the_pre_rain_moisture Eq. 3a applies only to fuel already saturated before the rain
test_codes_stay_inside_their_domains no NaN or negative values under extreme sirocco, frost, or a 200 mm cloudburst
test_buildup_index_survives_both_codes_at_zero Eq. 27's division by zero on a soaked landscape
test_the_drought_code_remembers_far_longer_than_the_fine_fuel the memory asymmetry: after a storm and a dry week FFMC fully recovers, DC recovers less than half
test_fwi_is_monotone_in_both_of_its_inputs more spread or more fuel never lowers the index
test_a_day_length_table_of_the_wrong_size_is_refused an 11-month table cannot silently shift the year
tests/test_examples.py every script in examples/ runs

3. The quality gate

Every push and pull request runs, on Python 3.12 and 3.13:

uv run ruff check            # lint
uv run ruff format --check   # formatting
uv run mypy                  # --strict, on src and tests
uv run pytest --cov          # tests, coverage must stay ≥ 90 %
uv build                     # the package builds

Locally, make check runs the same gate.

Found a disagreement?

If fwi-chain disagrees with another implementation (cffdrs, xclim, a national service's published values), please open an issue with the inputs, the start state, and both outputs. New reference vectors are among the most valuable contributions to this project.