How to customize a rate of spread model
In FireBench,
a rate-of-spread model is a class inherited from firebench.ros_models.RateOfSpreadModel
. The goal of the class is to provide a wrapper to a function that computes the rate of spread. To be compatible with other parts of the library, the wrapper contains:
- a
compute_ros
static method that takes a dictionary as input and returns the rate of spread as a float. This provides a common interface to all the rate of spread models. - a metadata dictionary containing information about the model’s inputs and outputs. This links the ros model internal variable names and the Standard Variable Namespace. It also provides expected units and ranges for conversion handling and range check functions.
Structure of Rothermel_SFIRE as an example
The class firebench.ros_models.Rothermel_SFIRE
is built around the ros model described in the static method Rothermel
. This function computes the rate of spread with inputs (fuel_data dictionary, fuel class numbers, wind speed, slope angle, and fuel moisture content). The wrapper class firebench.ros_models.Rothermel_SFIRE
contains the following metadata dictionary:
metadata = {
"windrf": {
"std_name": svn.FUEL_WIND_REDUCTION_FACTOR,
"units": ureg.dimensionless,
"range": (0, 1),
},
"fgi": {
"std_name": svn.FUEL_LOAD_DRY_TOTAL,
"units": ureg.kilogram / ureg.meter**2,
"range": (0, np.inf),
},
"fueldepthm": {
"std_name": svn.FUEL_HEIGHT,
"units": ureg.meter,
"range": (0, np.inf),
},
"fueldens": {
"std_name": svn.FUEL_DENSITY,
"units": ureg.pound / ureg.foot**3,
"range": (0, np.inf),
},
"savr": {
"std_name": svn.FUEL_SURFACE_AREA_VOLUME_RATIO,
"units": 1 / ureg.foot,
"range": (0, np.inf),
},
"fuelmce": {
"std_name": svn.FUEL_MOISTURE_EXTINCTION,
"units": ureg.percent,
"range": (0, np.inf),
},
"st": {
"std_name": svn.FUEL_MINERAL_CONTENT_TOTAL,
"units": ureg.dimensionless,
"range": (0, 1),
},
"se": {
"std_name": svn.FUEL_MINERAL_CONTENT_EFFECTIVE,
"units": ureg.dimensionless,
"range": (0, 1),
},
"ichap": {
"std_name": svn.FUEL_CHAPARRAL_FLAG,
"units": ureg.dimensionless,
"range": (0, 1),
},
"wind": {
"std_name": svn.WIND_SPEED,
"units": ureg.meter / ureg.second,
"range": (-np.inf, np.inf),
},
"slope": {
"std_name": svn.SLOPE_ANGLE,
"units": ureg.degree,
"range": (-90, 90),
},
"fmc": {
"std_name": svn.FUEL_MOISTURE_CONTENT,
"units": ureg.percent,
"range": (0, 200),
},
"output_rate_of_spread": {
"std_name": svn.RATE_OF_SPREAD,
"units": ureg.meter / ureg.second,
"item": (0, np.inf),
},
}
This dictionary contains each variable used in rothermel
method:
- the name of the variable as used in the ros model function as key
- the corresponding standard name
- the expected unit of the variable
- the validity range
In addition, the wrapper function compute_ros
is dedicated to input redirection and data pre-processing.
How to create a custom rate of spread class
We have a rate of spread function as follows:
def custom_ros(fuel_data: dict, fuel_class: int, wind: float):
return 0.5 * fuel_data["fgi"][fuel_class] * wind ** 2
This function uses two variables, fgi
and wind
. The variable fgi
is contained in the fuel_data
dictionary. The metadata dictionary must contain information for each input and for the output. The wrapper function will be used to redirect inputs.
The wrapper class MyCustomROS
can be defined as:
class MyCustomROS(RateOfSpreadModel):
metadata = {
"fgi": {
"std_name": svn.FUEL_LOAD_DRY_TOTAL,
"units": ureg.kilogram / ureg.meter**2,
"range": (0, np.inf),
},
"wind": {
"std_name": svn.WIND_SPEED,
"units": ureg.meter / ureg.second,
"range": (-np.inf, np.inf),
},
"output_rate_of_spread": {
"std_name": svn.RATE_OF_SPREAD,
"units": ureg.meter / ureg.second,
"range": (0, np.inf),
},
}
@staticmethod
def custom_ros(fuel_data: dict, fuel_class: int, wind: float):
return 0.5 * fuel_data["fgi"][fuel_class] * wind ** 2
@staticmethod
def compute_ros(
input_dict: dict[str, list[float]],
**opt,
) -> float:
fuel_dict_list_vars = [
"fgi",
]
fuel_dict = {}
for var in fuel_dict_list_vars:
fuel_dict[var] = input_dict[Rothermel_SFIRE.metadata[var]["std_name"]]
return MyCustomROS.custom_ros(
fueldata=fuel_dict,
fuelclass=input_dict[svn.FUEL_CLASS],
wind=input_dict[svn.WIND_SPEED],
**opt,
)
The function custom_ros
is defined within the class here, but it can also be defined elsewhere and called within compute_ros
.