FireSTARR
Loading...
Searching...
No Matches
FuelType.h
1/* Copyright (c) Queen's Printer for Ontario, 2020. */
2/* Copyright (c) His Majesty the King in Right of Canada as represented by the Minister of Natural Resources, 2025. */
3
4/* SPDX-License-Identifier: AGPL-3.0-or-later */
5
6#pragma once
7#include "Duff.h"
8#include "FWI.h"
9namespace fs
10{
11namespace sim
12{
13class SpreadInfo;
14}
15namespace data
16{
17class LogValue;
18}
19using sim::SpreadInfo;
20using data::LogValue;
21namespace fuel
22{
23constexpr FuelCodeSize INVALID_FUEL_CODE = 0;
24// References
25// Forestry Canada
26// Development and Structure of the Canadian Forest Fire Behaviour Prediction System (ST-X-3)
27// https://cfs.nrcan.gc.ca/pubwarehouse/pdfs/10068.pdf
28//
29// Wotton, B.M., Alexander, M.E., Taylor, S.W.
30// Updates and revision to the 1992 Canadian Forest Fire Behavior Prediction System (GLC-X-10)
31// https://cfs.nrcan.gc.ca/pubwarehouse/pdfs/31414.pdf
32//
33// Anderson, Kerry
34// Incorporating Smoldering Into Fire Growth Modelling
35// https://www.cfs.nrcan.gc.ca/pubwarehouse/pdfs/19950.pdf
36//
37// default grass fuel load (kg/m^2)
38static constexpr MathSize DEFAULT_GRASS_FUEL_LOAD = 0.35;
39// amount of duff to apply ffmc moisture to (cm) (1.2 cm is from Kerry's paper)
40static constexpr MathSize DUFF_FFMC_DEPTH = 1.2;
47[[nodiscard]] constexpr MathSize fire_intensity(const MathSize fc, const MathSize ros)
48{
49 return 300.0 * fc * ros;
50}
55{
56public:
62 [[nodiscard]] static constexpr FuelCodeSize safeCode(const FuelType* fuel)
63 {
64 return nullptr == fuel ? static_cast<FuelCodeSize>(INVALID_FUEL_CODE) : fuel->code();
65 }
71 [[nodiscard]] static constexpr const char* safeName(const FuelType* fuel)
72 {
73 return nullptr == fuel ? "NULL" : fuel->name();
74 }
81 [[nodiscard]] static constexpr MathSize criticalRos(const MathSize sfc, const MathSize csi)
82 {
83 return sfc > 0 ? csi / (300.0 * sfc) : 0.0;
84 }
91 [[nodiscard]] static constexpr bool isCrown(const MathSize csi, const MathSize sfi)
92 {
93 return sfi > csi;
94 }
99 [[nodiscard]] virtual MathSize cfl() const = 0;
100 virtual ~FuelType() noexcept = default;
107 constexpr FuelType(const FuelCodeSize& code,
108 const char* name,
109 const bool can_crown) noexcept
110 : name_(name), can_crown_(can_crown), code_(code)
111 {
112 }
113 FuelType(FuelType&& rhs) noexcept = delete;
114 FuelType(const FuelType& rhs) noexcept = delete;
115 FuelType& operator=(FuelType&& rhs) noexcept = delete;
116 FuelType& operator=(const FuelType& rhs) noexcept = delete;
121 [[nodiscard]] constexpr bool canCrown() const
122 {
123 return can_crown_;
124 }
129 [[nodiscard]] virtual MathSize grass_curing(const int, const wx::FwiWeather&) const
130 {
131 // NOTE: grass overrides this but everything else doesn't have curing
132 return INVALID_CURING;
133 }
134
139 [[nodiscard]] virtual MathSize cbh() const = 0;
146 [[nodiscard]] virtual MathSize crownFractionBurned(MathSize rss, MathSize rso) const noexcept = 0;
152 [[nodiscard]] virtual MathSize probabilityPeat(MathSize mc_fraction) const noexcept = 0;
158 [[nodiscard]] virtual ThresholdSize survivalProbability(
159 const wx::FwiWeather& wx) const noexcept = 0;
165 [[nodiscard]] virtual MathSize buiEffect(MathSize bui) const = 0;
171 [[nodiscard]] virtual MathSize crownConsumption(MathSize cfb) const = 0;
179 [[nodiscard]] virtual MathSize calculateRos(int nd,
180 const wx::FwiWeather& wx,
181 MathSize isi) const = 0;
188 [[nodiscard]] virtual MathSize calculateIsf(const SpreadInfo& spread,
189 MathSize isi) const = 0;
195 [[nodiscard]] virtual MathSize surfaceFuelConsumption(
196 const SpreadInfo& spread) const = 0;
202 [[nodiscard]] virtual MathSize lengthToBreadth(MathSize ws) const = 0;
211 [[nodiscard]] virtual MathSize finalRos(const SpreadInfo& spread,
212 MathSize isi,
213 MathSize cfb,
214 MathSize rss) const = 0;
220 [[nodiscard]] virtual MathSize criticalSurfaceIntensity(
221 const SpreadInfo& spread) const = 0;
226 [[nodiscard]] constexpr const char* name() const
227 {
228 return name_;
229 }
234 [[nodiscard]] constexpr FuelCodeSize code() const
235 {
236 return code_;
237 }
238private:
242 const char* name_;
246 const bool can_crown_;
250 FuelCodeSize code_;
251};
258template <int BulkDensity, int InorganicPercent, int DuffDepth>
260 : public FuelType
261{
262public:
263 ~FuelBase() override = default;
272 constexpr FuelBase(const FuelCodeSize& code,
273 const char* name,
274 const bool can_crown,
275 const Duff* duff_ffmc,
276 const Duff* duff_dmc)
277 : FuelType(code, name, can_crown),
278 duff_ffmc_(duff_ffmc),
279 duff_dmc_(duff_dmc)
280 {
281 }
282 FuelBase(FuelBase&& rhs) noexcept = delete;
283 FuelBase(const FuelBase& rhs) = delete;
284 FuelBase& operator=(FuelBase&& rhs) noexcept = delete;
285 FuelBase& operator=(const FuelBase& rhs) = delete;
292 [[nodiscard]] MathSize crownFractionBurned(const MathSize rss,
293 const MathSize rso) const noexcept override
294 {
295 // can't burn crown if it doesn't exist
296 return cfl() > 0 ? max(0.0, 1.0 - exp(-0.230 * (rss - rso))) : 0.0;
297 }
303 [[nodiscard]] ThresholdSize probabilityPeat(const MathSize mc_fraction) const noexcept override
304 {
305 // Anderson table 1
306 constexpr auto pb = bulkDensity();
307 // Anderson table 1
308 constexpr auto fi = inorganicPercent();
309 constexpr auto pi = fi * pb;
310 // Inorganic ratio
311 constexpr auto ri = fi / (1 - fi);
312 constexpr auto const_part = -19.329 + 1.7170 * ri + 23.059 * pi;
313 // Anderson eq 1
314 return 1 / (1 + exp(17.047 * mc_fraction / (1 - fi) + const_part));
315 }
321 [[nodiscard]] ThresholdSize survivalProbability(const wx::FwiWeather& wx) const noexcept
322 override
323 {
324 // divide by 100 since we need moisture ratio
325 // IFERROR(((1 / (1 + EXP($G$43 + $I$43 *
326 // (Q$44 * $O$43 + $N$43)))) -
327 // (1 / (1 + EXP($G$43 + $I$43 * (2.5 * $O$43 + $N$43)))))
328 // / (1 / (1 + EXP($G$43 + $I$43 * $N$43))), 0)
329 // HACK: use same constants for all fuels because they seem to work nicer than
330 // using the ratios, but they change anyway because of the other fuel attributes
331 static const auto WFfmc = 0.25;
332 static const auto WDmc = 1.0;
333 static const auto RatioHartford = 0.5;
334 static const auto RatioFrandsen = 1.0 - RatioHartford;
335 static const auto RatioAspen = 0.5;
336 static const auto RatioFuel = 1.0 - RatioAspen;
337 const auto mc_ffmc = wx.mcFfmc() * WFfmc + WDmc;
338 static const auto McFfmcSaturated = 2.5 * WFfmc + WDmc;
339 static const auto McDmc = WDmc;
340 const auto prob_ffmc_peat = probabilityPeat(mc_ffmc);
341 const auto prob_ffmc_peat_saturated = probabilityPeat(McFfmcSaturated);
342 const auto prob_ffmc_peat_zero = probabilityPeat(McDmc);
343 const auto prob_ffmc_peat_weighted = (prob_ffmc_peat - prob_ffmc_peat_saturated) / prob_ffmc_peat_zero;
344 const auto prob_ffmc = duffFfmcType()->probabilityOfSurvival(mc_ffmc * 100);
345 const auto prob_ffmc_saturated = duffFfmcType()->probabilityOfSurvival(
346 McFfmcSaturated * 100);
347 const auto prob_ffmc_zero = duffFfmcType()->probabilityOfSurvival(McDmc);
348 const auto prob_ffmc_weighted = (prob_ffmc - prob_ffmc_saturated) / prob_ffmc_zero;
349 const auto term_otway = exp(-3.11 + 0.12 * wx.dmc().asValue());
350 const auto prob_otway = term_otway / (1 + term_otway);
351 const auto mc_pct = wx.mcDmcPct() * dmcRatio() + wx.mcFfmcPct() * ffmcRatio();
352 const auto prob_weight_ffmc = duffFfmcType()->probabilityOfSurvival(mc_pct);
353 const auto prob_weight_ffmc_peat = probabilityPeat(mc_pct / 100);
354 const auto prob_weight_dmc = duffDmcType()->probabilityOfSurvival(wx.mcDmcPct());
355 const auto prob_weight_dmc_peat = probabilityPeat(wx.mcDmc());
356 // chance of survival is 1 - chance of it not surviving in every fuel
357 const auto tot_prob = 1 - (1 - prob_ffmc_peat_weighted) * (1 - prob_ffmc_weighted) * ((1 - prob_otway) * RatioAspen + ((1 - prob_weight_ffmc_peat) * RatioHartford + (1 - prob_weight_ffmc) * RatioFrandsen) * ((1 - prob_weight_dmc_peat) * RatioHartford + (1 - prob_weight_dmc) * RatioFrandsen) * RatioFuel);
358 return tot_prob;
359 }
364 [[nodiscard]] static constexpr MathSize bulkDensity()
365 {
366 return BulkDensity / 1000.0;
367 }
372 [[nodiscard]] static constexpr MathSize inorganicPercent()
373 {
374 return InorganicPercent / 100.0;
375 }
380 [[nodiscard]] static constexpr MathSize duffDepth()
381 {
382 return DuffDepth / 10.0;
383 }
388 [[nodiscard]] constexpr const Duff* duffDmcType() const
389 {
390 return duff_dmc_;
391 }
396 [[nodiscard]] constexpr const Duff* duffFfmcType() const
397 {
398 return duff_ffmc_;
399 }
404 [[nodiscard]] static constexpr MathSize ffmcRatio()
405 {
406 return 1 - dmcRatio();
407 }
412 [[nodiscard]] static constexpr MathSize dmcRatio()
413 {
414 return (duffDepth() - DUFF_FFMC_DEPTH) / duffDepth();
415 }
416private:
425};
429class InvalidFuel final
430 : public FuelType
431{
432public:
433 InvalidFuel() noexcept
434 : InvalidFuel(0, nullptr)
435 {
436 }
442 constexpr InvalidFuel(const FuelCodeSize& code, const char* name) noexcept
443 : FuelType(code, name, false)
444 {
445 }
446 ~InvalidFuel() override = default;
447 InvalidFuel(const InvalidFuel& rhs) noexcept = delete;
448 InvalidFuel(InvalidFuel&& rhs) noexcept = delete;
449 InvalidFuel& operator=(const InvalidFuel& rhs) noexcept = delete;
450 InvalidFuel& operator=(InvalidFuel&& rhs) noexcept = delete;
455 [[nodiscard]] MathSize grass_curing(const int nd, const wx::FwiWeather& wx) const override;
460 [[nodiscard]] MathSize cbh() const override;
465 [[nodiscard]] MathSize cfl() const override;
470 [[nodiscard]] MathSize buiEffect(MathSize) const override;
475 [[nodiscard]] MathSize crownConsumption(MathSize) const override;
480 [[nodiscard]] MathSize calculateRos(int, const wx::FwiWeather&, MathSize) const
481 override;
486 [[nodiscard]] MathSize calculateIsf(const SpreadInfo&, MathSize) const
487 override;
492 [[nodiscard]] MathSize surfaceFuelConsumption(const SpreadInfo&) const
493 override;
498 [[nodiscard]] MathSize lengthToBreadth(MathSize) const override;
503 [[nodiscard]] MathSize finalRos(const SpreadInfo&,
504 MathSize,
505 MathSize,
506 MathSize) const override;
511 [[nodiscard]] MathSize criticalSurfaceIntensity(const SpreadInfo&) const
512 override;
517 [[nodiscard]] MathSize crownFractionBurned(MathSize,
518 MathSize) const noexcept override;
523 [[nodiscard]] ThresholdSize probabilityPeat(MathSize) const noexcept override;
528 [[nodiscard]] ThresholdSize survivalProbability(const wx::FwiWeather&) const noexcept
529 override;
530};
531}
532}
Base class for DuffType.
Definition Duff.h:43
virtual ThresholdSize probabilityOfSurvival(MathSize mc_pct) const noexcept=0
Survival probability calculated using probability of ony survival based on multiple formulae.
Base class for all FuelTypes.
Definition FuelType.h:261
ThresholdSize probabilityPeat(const MathSize mc_fraction) const noexcept override
Calculate probability of burning [Anderson eq 1].
Definition FuelType.h:303
ThresholdSize survivalProbability(const wx::FwiWeather &wx) const noexcept override
Survival probability calculated using probability of ony survival based on multiple formulae.
Definition FuelType.h:321
static constexpr MathSize inorganicPercent()
Inorganic Percent (% / 100) [Anderson table 1].
Definition FuelType.h:372
static constexpr MathSize duffDepth()
DuffDepth Depth of Duff layer (cm) [Anderson table 1].
Definition FuelType.h:380
const Duff * duff_ffmc_
Type of duff near the surface.
Definition FuelType.h:420
const Duff * duff_dmc_
Type of duff deeper underground.
Definition FuelType.h:424
constexpr const Duff * duffDmcType() const
Type of duff deeper underground.
Definition FuelType.h:388
static constexpr MathSize ffmcRatio()
What fraction of the duff layer should use FFMC to determine moisture.
Definition FuelType.h:404
static constexpr MathSize dmcRatio()
What fraction of the duff layer should use DMC to determine moisture.
Definition FuelType.h:412
MathSize crownFractionBurned(const MathSize rss, const MathSize rso) const noexcept override
Crown Fraction Burned (CFB) [ST-X-3 eq 58].
Definition FuelType.h:292
constexpr FuelBase(const FuelCodeSize &code, const char *name, const bool can_crown, const Duff *duff_ffmc, const Duff *duff_dmc)
Constructor.
Definition FuelType.h:272
static constexpr MathSize bulkDensity()
Duff Bulk Density (kg/m^3) [Anderson table 1].
Definition FuelType.h:364
constexpr const Duff * duffFfmcType() const
Type of duff near the surface.
Definition FuelType.h:396
An FBP fuel type.
Definition FuelType.h:55
const char * name_
Name of the fuel.
Definition FuelType.h:242
virtual MathSize cfl() const =0
Crown fuel load (kg/m^2) [ST-X-3 table 8].
static constexpr bool isCrown(const MathSize csi, const MathSize sfi)
Whether or not this is a crown fire.
Definition FuelType.h:91
virtual MathSize grass_curing(const int, const wx::FwiWeather &) const
Grass curing.
Definition FuelType.h:129
constexpr bool canCrown() const
Whether or not this fuel can have a crown fire.
Definition FuelType.h:121
virtual MathSize calculateIsf(const SpreadInfo &spread, MathSize isi) const =0
Calculate ISI with slope influence and zero wind (ISF) [ST-X-3 eq 41/42].
virtual MathSize crownConsumption(MathSize cfb) const =0
Crown Fuel Consumption (CFC) (kg/m^2) [ST-X-3 eq 66].
virtual MathSize lengthToBreadth(MathSize ws) const =0
Length to Breadth ratio [ST-X-3 eq 79].
constexpr FuelCodeSize code() const
Code for this fuel type.
Definition FuelType.h:234
static constexpr const char * safeName(const FuelType *fuel)
Convert FuelType to its name, or 0 if nullptr.
Definition FuelType.h:71
virtual MathSize buiEffect(MathSize bui) const =0
BUI Effect on surface fire rate of spread [ST-X-3 eq 54].
static constexpr MathSize criticalRos(const MathSize sfc, const MathSize csi)
Critical rate of spread (m/min)
Definition FuelType.h:81
virtual MathSize cbh() const =0
Crown base height (m) [ST-X-3 table 8].
static constexpr FuelCodeSize safeCode(const FuelType *fuel)
Convert FuelType to its code, or 0 if nullptr.
Definition FuelType.h:62
virtual MathSize crownFractionBurned(MathSize rss, MathSize rso) const noexcept=0
Crown Fraction Burned (CFB) [ST-X-3 eq 58].
constexpr FuelType(const FuelCodeSize &code, const char *name, const bool can_crown) noexcept
Fuel type.
Definition FuelType.h:107
virtual MathSize criticalSurfaceIntensity(const SpreadInfo &spread) const =0
Critical Surface Fire Intensity (CSI) [ST-X-3 eq 56].
virtual MathSize probabilityPeat(MathSize mc_fraction) const noexcept=0
Calculate probability of burning [Anderson eq 1].
virtual ThresholdSize survivalProbability(const wx::FwiWeather &wx) const noexcept=0
Survival probability calculated using probability of ony survival based on multiple formulae.
FuelCodeSize code_
Code to identify fuel with.
Definition FuelType.h:250
virtual MathSize finalRos(const SpreadInfo &spread, MathSize isi, MathSize cfb, MathSize rss) const =0
Final rate of spread (m/min)
const bool can_crown_
Whether or not this fuel can have a crown fire.
Definition FuelType.h:246
virtual MathSize surfaceFuelConsumption(const SpreadInfo &spread) const =0
Surface fuel consumption (SFC) (kg/m^2) [ST-X-3 eq 9-25].
virtual MathSize calculateRos(int nd, const wx::FwiWeather &wx, MathSize isi) const =0
Calculate rate of spread (m/min)
constexpr const char * name() const
Name of the fuel.
Definition FuelType.h:226
Placeholder fuel that throws exceptions if it ever gets used.
Definition FuelType.h:431
MathSize buiEffect(MathSize) const override
Throw a runtime_error.
Definition FuelType.cpp:25
MathSize crownFractionBurned(MathSize, MathSize) const noexcept override
Throw a runtime_error.
Definition FuelType.cpp:57
MathSize calculateIsf(const SpreadInfo &, MathSize) const override
Throw a runtime_error.
Definition FuelType.cpp:37
MathSize crownConsumption(MathSize) const override
Throw a runtime_error.
Definition FuelType.cpp:29
MathSize lengthToBreadth(MathSize) const override
Throw a runtime_error.
Definition FuelType.cpp:45
MathSize calculateRos(int, const wx::FwiWeather &, MathSize) const override
Throw a runtime_error.
Definition FuelType.cpp:33
MathSize cfl() const override
Throw a runtime_error.
Definition FuelType.cpp:21
constexpr InvalidFuel(const FuelCodeSize &code, const char *name) noexcept
Placeholder fuel that throws exceptions if it ever gets used.
Definition FuelType.h:442
MathSize surfaceFuelConsumption(const SpreadInfo &) const override
Throw a runtime_error.
Definition FuelType.cpp:41
MathSize criticalSurfaceIntensity(const SpreadInfo &) const override
Throw a runtime_error.
Definition FuelType.cpp:53
MathSize grass_curing(const int nd, const wx::FwiWeather &wx) const override
Throw a runtime_error.
Definition FuelType.cpp:13
ThresholdSize survivalProbability(const wx::FwiWeather &) const noexcept override
Throw a runtime_error.
Definition FuelType.cpp:65
ThresholdSize probabilityPeat(MathSize) const noexcept override
Throw a runtime_error.
Definition FuelType.cpp:61
MathSize cbh() const override
Throw a runtime_error.
Definition FuelType.cpp:17
MathSize finalRos(const SpreadInfo &, MathSize, MathSize, MathSize) const override
Throw a runtime_error.
Definition FuelType.cpp:49
Information regarding spread within a Cell for a specific Scenario and time.
Definition FireSpread.h:30
A Weather value with calculated FWI indices.
Definition FWI.h:209