Suppression
Suppression actions change where the fire can spread while the run is
going: fire lines built at a rate, retardant drops that stop or slow the
spread for a while, a hold test that lets a line fail under a hot enough
fire, and burnout ignitions fired from a built line. They are read from an
action file that the run watches, so a run can be steered while it is
running and reproduced afterwards from its log. Everything is off by default
and switched on with erf.fire.suppression.enable.
Mechanism
Both propagation paths (Front Propagation) already read one
non-burnable mask, fire_nonburnable, whose cells get zero rate of
spread, a level set held outside the burned region, and no FARSITE target;
structures, non-burnable fuel codes and masked firebreaks feed it at
start-up. Suppression adds a second, time-dependent source. Once per fire
step, before the rate of spread is used, the active actions are stamped
into a suppression mask on the fire grid and fire_nonburnable is rebuilt
as the OR of the static mask and that one; the level set is then clamped in
the mask cells as it always is. When the run has no static source, an
all-zero mask is allocated so that the suppression cells have somewhere to
go (an all-zero mask changes nothing on either path). The suppression
fields are rebuilt from the active actions on every step, so an expired
action’s cells revert by themselves.
A retardant drop with a rate factor between zero and one does not enter the
mask. It fills a second field, fire_ros_factor, with which fire_ros
is multiplied just before the front update, after the acceleration ramp and
the crown-fire enhancement. The level-set paths that rebuild the rate inside
every Runge-Kutta stage (the directional, Balbi and hybrid paths) take the
factor through the per-stage scale they already apply, so the reduction
reaches them too.
Lines. A line is a polyline built from its first vertex at
\(\mathrm{rate}\) metres per second from start_s. A cell is covered
when the built part comes within half a fire-cell spacing of its centre or
passes through the cell’s interior, so the built cells form a barrier one
cell wide that no four-neighbour path crosses, whichever way the line runs
(the FARSITE front update reads four neighbours; the level-set stencils are
wider, see the limitations). expiry_s removes the line that many
seconds after its start; -1 keeps it.
Drops. A drop is a polygon. With ros_factor = 0 its cells enter the
mask; with \(0 < f < 1\) the rate of spread inside it is multiplied by
\(f\). Both last expiry_s seconds after start_s (-1 for
ever) and then revert.
Burned cells are never stamped. A line through a burning cell or
retardant on burned ground does nothing to the front, so a cell with an
arrival time, or a negative level set, is skipped when an action is applied
and counted out of the action’s cell count. A line started after the front
has passed therefore has a gap where it crossed the fire, and the head runs
on through it: Exec/RegTests/FireSuppression inputs_line_late shows
this.
Hold test. A line with a flame_limit_m is tested every step against
the flame length of the previous step: a built cell with a four-neighbour
whose flame length exceeds the limit reverts to burnable, stays out of the
line for the rest of the run, and is logged as hold_failed. The plotted
mask shows such cells as \(-1\). - means the line always holds.
Burnout. A burnout follows a line (ref=<line id>) and, from its
start_s, ignites one point per fire cell along the part of the line that
is already built, offset metres from it on the side of the nearest
burning cell (the side is decided per segment of the polyline). Each point
is a scheduled ignition of radius three quarters of a cell, applied by the
same machinery as the ignition schedule, and only where the level set is
still positive. As the line is built further the burnout follows it. When
no cell is burning yet the burnout waits and tries again on the next step.
Reverted cells. When a drop expires or a hold test fails, the cells go
back to burnable with whatever level set the clamp left them, zero where
the front was pressing against them. On the level-set path the next advance
moves the front through them at the rate of spread and the periodic
reinitialisation repairs the distance function around them; on the FARSITE
path the front-cell update ignites a reverted cell whose neighbour has been
burning, with the head-rate distance accumulated since that neighbour
ignited, so a front held at a barrier for a while moves into the released
cells at once. inputs_drop_hold measures both.
Action file
One action per line, whitespace separated, # comments, geometry in
fire-grid metres, every id unique:
# id type start_s geometry param expiry_s flame_limit_m
L1 line 3600 500 200 900 600 950 650 rate=0.5 -1 2.4
D1 drop 5400 poly:x1,y1;x2,y2;x3,y3 ros_factor=0.0 1800 -
B1 burnout 4000 ref=L1 offset=20 - -
line: the vertices as a flat list (at least two), thenrate=<m/s>(\(> 0\)),expiry_s(\(-1\) or \(> 0\)) andflame_limit_m(-or \(> 0\)).drop: onepoly:token with at least threex,yvertices separated by;, thenros_factor=<f>in \([0, 1)\),expiry_sand-(the hold test applies to lines only).burnout:ref=<line id>(a line in the file or read earlier), thenoffset=<m>(\(\ge 0\)) and--.
A malformed line stops the run at the read with a message that names the
file, the line number, the reason and the line itself; so does a duplicate
id and a burnout that references an unknown line. start_s earlier than
the current time applies the action at once.
Polling and reproducibility
The file is read on the IO rank at start-up and, when
erf.fire.suppression.poll_interval is positive, every that many fire
steps: the rank compares the file’s modification time and size with those
of the last read, and when either changed reads the text and broadcasts it,
after which every rank parses the same bytes. Ids already known are
skipped, so an action is never applied twice, and an edited line of a known
id has no effect: to change an action, add a new id (the old one expires
when its expiry_s passes, or holds for ever). poll_interval = 0
reads the file once at start-up.
Every event is appended to erf.fire.suppression.log as
time_s,step,id,type,event,cells,detail: applied when an action
starts (with the cells it covers and its parameters), completed when a
line is fully built, expired when a timed action is removed (with the
cells that revert), hold_failed with the number of newly failed cells,
burnout with the number of ignition points placed and the side taken,
and reread when a poll found new actions. The time is that of the start
of the fire step the event was applied in. A fresh run starts the log over;
a restart appends to it. Together with the action file the log gives every
action and the step it took effect, so a run that was steered live can be
rerun with poll_interval = 0 and a file holding the same actions with
the logged start times.
Checkpoint and restart
The checkpoint carries the suppression mask, the rate factor, the line
progress and the hold-failed cells (FireSuppressionMask,
FireSuppressionFactor, FireSuppressionProgress,
FireSuppressionFailed) and, in FireSuppression, every action with
its geometry and state: whether it was applied, expired or completed, the
built length of a line, the length of a line a burnout has fired along, and
the last cell count. A restart continues a line where it was and reapplies
nothing. The file is re-read at start-up as always, but for ids the
checkpoint knows the checkpoint’s copy wins; only new ids are added.
Output
The fire plotfile gains fire_suppression_mask (1 in cells suppressed
on this step, \(-1\) where a line cell failed its hold test and nothing
else covers it, 0 elsewhere), fire_ros_factor (the multiplier, 1
outside drops) and fire_line_progress (the ordinal, in file order, of
the line a built cell belongs to; 0 elsewhere). fire_nonburnable is
present whenever suppression is on, as the combined mask.
Inputs
Input |
Meaning |
Default |
|---|---|---|
|
Read and apply the action file |
false |
|
The action file; required when enabled |
none |
|
Fire steps between modification-time checks; 0 reads once at start-up |
10 |
|
The event log |
|
Verification
Exec/RegTests/FireSuppression runs every scenario on both propagation
paths on a still-air deck with a prescribed rate of spread of 1 m/s and 2 m
fire cells, so every arrival time follows from the geometry: a line built in
time stops the head and the same line started late is overrun; a drop with
ros_factor = 0 holds until its expiry and the head crosses afterwards; a
drop with ros_factor = 0.3 slows the head to the expected distance; a
line with a low flame limit fails where the hotter cells of a rate gradient
arrive and holds elsewhere; a burnout fires the strip along a line long
before the main front; a writer process appends the line to an empty file
while the run polls, on one rank and on two; and a restart with a line under
construction reproduces the straight run’s fire plotfile in every field.
Each check fails on a binary without the feature (the plotfile lacks the
suppression fields). The action-file parser and the geometry kernels have
their own unit tests (Tests/Unit/Fire/ERF_GTestFireSuppression.cpp).
Limitations
Lines are one cell wide. The FARSITE front-cell update, which reads four neighbours, cannot cross one; the level-set gradient stencils read up to three cells across a line, and with strong spread a small amount of the level set can leak past a one-cell line before the clamp catches it. The regression decks hold on the level-set path with the default stencil; for a production case set
erf.fire.levelset.wall_extrapolateso the line acts as a wall of the stencil.There is no direct attack that walks along the front, no per-fuel production rate or crew type, and no coupling of lines to structures.
The FARSITE
legacyfront update is not tested with the rate factor; the run warns once when both are set.The hold test compares the flame length of the previous step, so a line fails one step after the flame exceeded the limit.
Actions are geometry in fire-grid metres; there is no map projection.