Skip to content

All 40 standard Scott & Burgan fuel models are hardcoded isDynamic = true, including the 23 static ones #65

Description

@jimmynotjim

Disclosure: This came up during a wildfire fuel modeling exploration being run by an AI agent. I'm a human submitting it, but I did not discover this on my own and typically work in JS/TS rather than C++ so I'm not personally familiar with the workings of this library. I have confirmed the source myself by adding a unit test locally and am preparing a PR with the test and fix.

In src/behave/fuelModels.cpp, FuelModels::populateFuelModels() passes isDynamic = true to setFuelModelRecord(...) for every standard Scott & Burgan (2005) fuel model (model numbers 101–204), regardless of whether the model is actually dynamic. As a result, getIsDynamic() returns true for 23 models that are static per S&B 2005 — the ones with no transferable live herbaceous load.

This surfaced downstream in the pyrothermel Python bindings (j-tenny/pyrothermel#2), where FuelModel.from_existing(code).is_dynamic returns True for e.g. SH4, TU5, TL1, SB1.

The static vs. dynamic distinction

A fuel model is "dynamic" when its live herbaceous load transfers to dead based on curing (S&B 2005) — i.e. exactly the models with non-zero fuelLoadLiveHerbaceous. Per S&B 2005 (and the load data in this very file), the dynamic standard models are GR1–GR9, GS1–GS4, SH1, SH9, TU1, TU3 — and only those.

Image

What's wrong

The entire 101–204 block passes isDynamic = true (the 2nd-to-last argument of setFuelModelRecord). For the static models that's incorrect. The 23 mislabeled records:

Code Model # Name (note the trailing (S)) live-herb load isDynamic now should be
SH2 142 Moderate load, dry climate shrub (S) 0 true false
SH3 143 Moderate load, humid climate shrub (S) 0 true false
SH4 144 Low load, humid climate timber-shrub (S) 0 true false
SH5 145 High load, dry climate shrub (S) 0 true false
SH6 146 Low load, humid climate shrub (S) 0 true false
SH7 147 Very high load, dry climate shrub (S) 0 true false
SH8 148 High load, humid climate shrub (S) 0 true false
TU2 162 Moderate load, humid climate timber-shrub (S) 0 true false
TU4 164 Dwarf conifer understory (S) 0 true false
TU5 165 Very high load, dry climate timber-shrub (S) 0 true false
TL1–TL9 181–189 all (S) 0 true false
SB1–SB4 201–204 all (S) 0 true false

(GR1–9, GS1–4, SH1, SH9, TU1, TU3 are already correctly true.)

Impact: metadata only (which is why it likely went unnoticed)

isDynamic only does work when there's live herbaceous load to transfer to dead. Every mislabeled record has fuelLoadLiveHerbaceous = 0, so the dynamic load-transfer step is a no-op for them — fire-behavior outputs (rate of spread, flame length, etc.) are unaffected. The defect is purely in the reported getIsDynamic() attribute. It only matters to consumers that read the flag as metadata — e.g. pyrothermel exposing is_dynamic.

Minimal repro (data-level)

For any of the 23 records above, getIsDynamic(n) returns true while getFuelLoadLiveHerbaceous(n, ...) returns 0. For example, TU5 (#165):

getIsDynamic(165)                    -> true   // expected false
getFuelLoadLiveHerbaceous(165, ...)  -> 0      // static: nothing to transfer

A sweep of the standard burnable set (101–204) shows isDynamic == true for all of them, including every (S) model.

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions