ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
ERF_FireEngine.H
Go to the documentation of this file.
1 #ifndef ERF_FIRE_ENGINE_H
2 #define ERF_FIRE_ENGINE_H
3 
4 #include <AMReX.H>
5 #include <AMReX_MultiFab.H>
6 #include <AMReX_REAL.H>
7 #include <string>
8 #include <vector>
9 #include <ERF_FireGrid.H>
10 
11 class ERF;
12 class SurfaceLayer;
13 
14 /**
15  * @file ERF_FireEngine.H
16  * @brief The interface between the fire coupling and a fire engine
17  *
18  * A fire engine advances a fire front on the fire grid and reports the heat
19  * it releases. The shared coupling layer (FireCoupler) owns everything that
20  * touches the atmosphere: the fire grid's placement, the atmospheric fields
21  * handed to the engine, the area average of the engine's fluxes onto the
22  * atmospheric columns, their one-step lag, their injection into the
23  * atmosphere's sources and the smoke tracer. An engine sees only this
24  * interface, the two structs below and the shared helpers it chooses to use
25  * (the wind sampler of ERF_FireWindExtract.H, the column grounds of
26  * ERF_FireSurfaceHeight.H).
27  *
28  * ERF itself contains no engine. An engine lives in its own directory outside
29  * ERF (FIRE_HOME in GNU make, ERF_FIRE_ENGINE_DIRS in CMake) and registers
30  * itself with ERF_REGISTER_FIRE_ENGINE (ERF_FireEngineRegistry.H).
31  * ERF_FireEngineTemplate.H declares every method below for new engines: a
32  * method added to FireEngine is declared there too (a pure virtual one that is
33  * missing fails the build; an optional one does not).
34  *
35  * Memory contract. Every MultiFab the coupling hands an engine (FireForcing,
36  * the FireGrid layout) and every one the engine hands back (FireFluxes, the
37  * getters below) is an ordinary AMReX MultiFab in the default arena, so on a
38  * GPU build its data is on the device. The coupling reads the engine's fields
39  * on the device right after the call that returned them. An engine that runs
40  * on the host:
41  * - copies what it reads from FireForcing to the host inside advance()
42  * (fire_host_copy(), ERF_FireUtils.H, does the copy and the stream
43  * synchronisation), because those fields are temporaries of that call;
44  * - owns the MultiFabs it returns, allocated on fg.ba and fg.dm (or the
45  * level's k = 0 slabs where stated), and keeps them alive until its next
46  * advance(); it fills them on the host and copies them back to device
47  * memory (fire_host_copy() the other way) before returning from
48  * fill_fluxes() or the getter;
49  * - may hold managed or pinned memory internally, but synchronises the GPU
50  * stream (amrex::Gpu::streamSynchronize()) before it touches the host side
51  * of data the device may still be writing.
52  * The coupling never frees, resizes or keeps across steps a pointer an engine
53  * returns.
54  */
55 
56 /**
57  * @brief The atmospheric state a fire engine reads at the start of its step
58  *
59  * Every field lives on the level the fire grid refines (erf.fire.anchor_level),
60  * in that level's index space and with its ghost cells; the engine samples
61  * them onto its own grid (FireGrid::atm_lo gives the corner of the fire
62  * region). The k = 0 slabs hold one cell in z per box of the level. The
63  * slabs and the precipitation are temporaries of FireCoupler::advance(): an
64  * engine reads them during its advance() and keeps no pointer to them.
65  */
67 {
68  amrex::Real time; ///< Start of the step [s]
69  amrex::Real dt; ///< Step of the fire grid's level [s]
70  const amrex::MultiFab& xvel; ///< x-velocity on x-faces [m/s]: before the dycore (passive, lagged) or after it (synchronous)
71  const amrex::MultiFab& yvel; ///< y-velocity on y-faces [m/s], the same time level as xvel
72  const amrex::MultiFab& z_phys_cc; ///< Cell-centre height [m] of the terrain-following mesh
73  const amrex::MultiFab& T_k0; ///< Air temperature at k = 0 [K] (theta times Exner), before the dycore
74  const amrex::MultiFab& RH_k0; ///< Relative humidity at k = 0 [0-1], before the dycore
75  const amrex::MultiFab* precip_accum_k0; ///< Surface precipitation accumulated since t = 0 [kg/m2] after this step's microphysics, or nullptr unless the engine asks for it (FireEngine::wants_surface_precip)
76 };
77 
78 /**
79  * @brief The fluxes a fire engine hands to the atmosphere after its step
80  *
81  * Both fields live on the fire grid (FireGrid::ba, FireGrid::dm), per unit
82  * ground area, and are positive into the atmosphere. Each is the time mean
83  * over exactly the interval of the last advance(), [forcing.time,
84  * forcing.time + forcing.dt], whatever steps the engine takes inside it: an
85  * engine that subcycles accumulates its fluxes over its substeps and divides
86  * by forcing.dt. The coupling averages them onto the atmospheric columns and
87  * injects them, unchanged, at every stage of the next ERF step of the fire
88  * grid's level (the one-step lag of WRF-Fire; erf.fire.coupling_type =
89  * synchronous only changes the wind the engine saw).
90  *
91  * The latent flux is the energy of the water vapour released, so the vapour
92  * mass flux is latent / L_v [kg/m2/s] with L_v of ERF_Constants.H; an engine
93  * that computes a mass flux multiplies by that same L_v. The pointers stay
94  * valid until the engine's next advance(). Sensible and latent pointers are
95  * independently optional: nullptr means no flux for that channel on this
96  * step, without suppressing a valid flux from the other channel.
97  */
98 struct FireFluxes
99 {
100  const amrex::MultiFab* sensible = nullptr; ///< Sensible heat flux into the air [W/m2]; nullptr = none this step
101  const amrex::MultiFab* latent = nullptr; ///< Latent heat flux into the air [W/m2]; nullptr = no water released
102  /// With erf.fire.fire_debug: text the coupling appends to its debug line of
103  /// the sensible flux (for example how the engine split its heat)
104  std::string debug_note;
105 };
106 
107 /**
108  * @brief A fire engine behind the shared coupling layer
109  *
110  * The coupling calls, in this order:
111  * - verify_prerequisites() once at start-up, after its own checks of the
112  * ERF configuration and before it builds the fire grid;
113  * - initialize() once, with the fire grid built, then start_run();
114  * - read_checkpoint() on a restart, right after start_run();
115  * - advance() then fill_fluxes() once per step of the fire grid's level,
116  * after that level's dycore;
117  * - smoke_heat_per_kg(), heat_open_fraction_atm() and roof_height_atm() when
118  * it injects the lagged fluxes, at every Runge-Kutta stage of the next step;
119  * - write_plotfile() on the fire plotfile schedule (erf.fire.plot_int,
120  * erf.fire.plot_per), and write_checkpoint() from FireCoupler::write_checkpoint
121  * when ERF writes a checkpoint;
122  * - plot_var_names() once, when the coupler is constructed (before
123  * verify_prerequisites), and plot_var() each time ERF writes its own
124  * plotfile, for the fire fields that file carries.
125  *
126  * The getters at the end (level set, reference wind, heat release) are what
127  * other surface models may read from the fire (a dust or smoke source model,
128  * for example); an engine that has none returns nullptr and those couplings
129  * do nothing.
130  */
132 {
133 public:
134  virtual ~FireEngine() = default;
135 
136  /// Name of the engine, as erf.fire.engine selects it.
137  virtual const char* name () const = 0;
138 
139  /**
140  * @brief Check the engine's own inputs against the ERF configuration
141  *
142  * Called once at start-up, after the coupling's checks of the grid
143  * (verify_fire_prerequisites) and before the fire grid is built. Aborts,
144  * naming the input, on a configuration the engine cannot run.
145  *
146  * @param[in] erf ERF instance (public AmrCore accessors only)
147  * @param[in] lev The level the fire grid refines
148  */
149  virtual void verify_prerequisites (const ERF& erf, int lev) = 0;
150 
151  /**
152  * @brief Set the engine up on the fire grid
153  *
154  * @param[in] erf ERF instance (public AmrCore accessors only)
155  * @param[in] lev The level the fire grid refines
156  * @param[in] fg The fire grid the coupling built
157  * @param[in] surface_layer The bottom surface layer; never null when
158  * needs_surface_layer() is true, and nullptr when
159  * it is false and the bottom boundary is not one
160  * @param[in] z_phys_nd_atm Nodal height of level lev [m] (ERF always passes it)
161  */
162  virtual void initialize (const ERF& erf,
163  int lev,
164  const FireGrid& fg,
166  const amrex::MultiFab* z_phys_nd_atm) = 0;
167 
168  /**
169  * @brief Start-up after initialize(), once the run's end is known
170  *
171  * @param[in] stop_time ERF's stop_time [s] (numeric_limits max when unset)
172  * @param[in] max_step ERF's max_step, counting level-0 steps (negative when unset)
173  * @param[in] steps_per_coarse_step Steps of the fire grid's level per level-0 step
174  */
175  virtual void start_run (amrex::Real stop_time, int max_step, int steps_per_coarse_step)
176  {
177  amrex::ignore_unused(stop_time, max_step, steps_per_coarse_step);
178  }
179 
180  /// Advance the fire over one step of its level.
181  virtual void advance (const FireForcing& forcing) = 0;
182 
183  /**
184  * @brief The fluxes of the step just taken, on the fire grid
185  *
186  * Means over exactly [forcing.time, forcing.time + forcing.dt] of the last
187  * advance() (see FireFluxes). Called after advance(), only when the
188  * coupling injects heat or smoke.
189  */
190  virtual void fill_fluxes (FireFluxes& fluxes) = 0;
191 
192  /**
193  * @brief Heat released per kilogram of fuel burned [J/kg], matching the
194  * sensible flux last handed over, to turn it into the fuel burnt
195  * that the smoke emission factor applies to
196  */
197  virtual amrex::Real smoke_heat_per_kg () const = 0;
198 
199  /// The engine needs the surface precipitation accumulation in FireForcing.
200  virtual bool wants_surface_precip () const { return false; }
201 
202  /**
203  * @brief The engine needs a surface layer at the bottom boundary
204  *
205  * True by default: the coupling then stops at start-up unless zlo.type =
206  * surface_layer. An engine that reads nothing from the surface layer (it
207  * takes the wind and heights from FireForcing alone) returns false and may
208  * run over any bottom boundary.
209  */
210  virtual bool needs_surface_layer () const { return true; }
211 
212  /**
213  * @brief Optional heat placement per atmospheric column, for
214  * erf.fire.heat_open_fraction: the fraction of the column's
215  * footprint open to the air [0-1] and the mean roof height of the
216  * rest [m], on the k = 0 slabs of the fire grid's level with one
217  * ghost cell: one slab per box of the level, in the level's box
218  * order and on its DistributionMapping (the coupling reads them
219  * with the level's MFIter and stops otherwise). nullptr when the
220  * engine has no such blocking.
221  */
222  virtual const amrex::MultiFab* heat_open_fraction_atm () const { return nullptr; }
223  virtual const amrex::MultiFab* roof_height_atm () const { return nullptr; }
224 
225  /**
226  * @brief Write the engine's own state to the checkpoint directory
227  *
228  * Everything the engine carries from one step to the next, under
229  * checkpointname/Level_<lev>/ of the fire grid's level. Called by
230  * FireCoupler::write_checkpoint, which then writes the lagged fluxes. The
231  * default writes nothing; such an engine cannot restart (read_checkpoint).
232  */
233  virtual void write_checkpoint (const std::string& checkpointname) const
234  {
235  amrex::ignore_unused(checkpointname);
236  }
237 
238  /**
239  * @brief Restore the engine's state from a checkpoint on a restart
240  *
241  * @param[in] restart_chkfile The checkpoint directory
242  * @param[in] max_level ERF's max_level (the levels to look for the fire state on)
243  * @param[in] step The step count of the fire grid's level at the checkpoint
244  *
245  * The default stops the run: an engine that does not implement restart
246  * says so instead of continuing from its initial state.
247  */
248  virtual void read_checkpoint (const std::string& restart_chkfile, int max_level, int step)
249  {
250  amrex::ignore_unused(restart_chkfile, max_level, step);
251  amrex::Abort(std::string("[FIRE] fire engine \"") + name()
252  + "\" does not support restart from a checkpoint");
253  }
254 
255  /**
256  * @brief Write the engine's plotfile of the fire grid
257  *
258  * @param[in] plotfile_prefix erf.fire.plot_file (the step is appended)
259  * @param[in] time Simulation time [s]
260  * @param[in] step Level-0 step number
261  *
262  * The default writes nothing (the engine's fields may still reach the ERF
263  * plotfile through plot_var_names()).
264  */
265  virtual void write_plotfile (const std::string& plotfile_prefix, amrex::Real time, int step) const
266  {
267  amrex::ignore_unused(plotfile_prefix, time, step);
268  }
269 
270  /**
271  * @brief Fire fields the ERF plotfile can carry, by name
272  *
273  * The names the deck may list in erf.plot_vars_* (for example "fire_phi",
274  * "fire_heat_flux"); they should start with "fire_" so they never collide
275  * with an ERF variable. The coupler asks once, when it is constructed:
276  * before initialize() and before ERF reads its plot variable lists, so the
277  * answer depends on the engine's inputs only. The default lists none.
278  */
279  virtual std::vector<std::string> plot_var_names () const { return {}; }
280 
281  /**
282  * @brief The field plot_var_names() lists as name, on the fire grid
283  *
284  * One component, on FireGrid::ba and FireGrid::dm, valid until the next
285  * advance(). The coupler averages it over each atmospheric column (the
286  * grid_ratio^2 fire cells under it) and writes it into the k = 0 layer of
287  * the ERF plotfile, zero elsewhere. nullptr writes zeros.
288  */
289  virtual const amrex::MultiFab* plot_var (const std::string& name) const
290  {
291  amrex::ignore_unused(name);
292  return nullptr;
293  }
294 
295  /// Area burned so far [m2], for the run log; negative when the engine does not track it.
296  virtual amrex::Real burned_area () const { return amrex::Real(-1.0); }
297 
298  /// Level set on the fire grid, negative where burned; nullptr if the engine has none.
299  virtual const amrex::MultiFab* level_set () const { return nullptr; }
300  /// Wind (u, v) [m/s] the engine sampled at reference_wind_height() on the fire grid; nullptr if none.
301  virtual const amrex::MultiFab* reference_wind () const { return nullptr; }
302  /// Height above ground [m] of reference_wind().
303  virtual amrex::Real reference_wind_height () const { return amrex::Real(0.0); }
304  /// Heat released by the fire on the fire grid [W/m2], before any partition into
305  /// sensible and latent flux; nullptr if the engine has none.
306  virtual const amrex::MultiFab* heat_release () const { return nullptr; }
307 };
308 
309 #endif
Fire grid data structure and construction.
@ surface_layer
amrex::Real Real
Definition: ERF_ShocInterface.H:19
A fire engine behind the shared coupling layer.
Definition: ERF_FireEngine.H:132
virtual std::vector< std::string > plot_var_names() const
Fire fields the ERF plotfile can carry, by name.
Definition: ERF_FireEngine.H:279
virtual const amrex::MultiFab * plot_var(const std::string &name) const
The field plot_var_names() lists as name, on the fire grid.
Definition: ERF_FireEngine.H:289
virtual amrex::Real reference_wind_height() const
Height above ground [m] of reference_wind().
Definition: ERF_FireEngine.H:303
virtual amrex::Real burned_area() const
Area burned so far [m2], for the run log; negative when the engine does not track it.
Definition: ERF_FireEngine.H:296
virtual void write_plotfile(const std::string &plotfile_prefix, amrex::Real time, int step) const
Write the engine's plotfile of the fire grid.
Definition: ERF_FireEngine.H:265
virtual void initialize(const ERF &erf, int lev, const FireGrid &fg, const SurfaceLayer *surface_layer, const amrex::MultiFab *z_phys_nd_atm)=0
Set the engine up on the fire grid.
virtual void write_checkpoint(const std::string &checkpointname) const
Write the engine's own state to the checkpoint directory.
Definition: ERF_FireEngine.H:233
virtual void verify_prerequisites(const ERF &erf, int lev)=0
Check the engine's own inputs against the ERF configuration.
virtual void read_checkpoint(const std::string &restart_chkfile, int max_level, int step)
Restore the engine's state from a checkpoint on a restart.
Definition: ERF_FireEngine.H:248
virtual ~FireEngine()=default
virtual amrex::Real smoke_heat_per_kg() const =0
Heat released per kilogram of fuel burned [J/kg], matching the sensible flux last handed over,...
virtual const amrex::MultiFab * reference_wind() const
Wind (u, v) [m/s] the engine sampled at reference_wind_height() on the fire grid; nullptr if none.
Definition: ERF_FireEngine.H:301
virtual void start_run(amrex::Real stop_time, int max_step, int steps_per_coarse_step)
Start-up after initialize(), once the run's end is known.
Definition: ERF_FireEngine.H:175
virtual bool wants_surface_precip() const
The engine needs the surface precipitation accumulation in FireForcing.
Definition: ERF_FireEngine.H:200
virtual void advance(const FireForcing &forcing)=0
Advance the fire over one step of its level.
virtual const amrex::MultiFab * heat_release() const
Definition: ERF_FireEngine.H:306
virtual const amrex::MultiFab * roof_height_atm() const
Definition: ERF_FireEngine.H:223
virtual const char * name() const =0
Name of the engine, as erf.fire.engine selects it.
virtual const amrex::MultiFab * level_set() const
Level set on the fire grid, negative where burned; nullptr if the engine has none.
Definition: ERF_FireEngine.H:299
virtual bool needs_surface_layer() const
The engine needs a surface layer at the bottom boundary.
Definition: ERF_FireEngine.H:210
virtual const amrex::MultiFab * heat_open_fraction_atm() const
Optional heat placement per atmospheric column, for erf.fire.heat_open_fraction: the fraction of the ...
Definition: ERF_FireEngine.H:222
virtual void fill_fluxes(FireFluxes &fluxes)=0
The fluxes of the step just taken, on the fire grid.
Definition: ERF_SurfaceLayer.H:60
Definition: ERF_InterpolationUtils.H:15
@ fg
Definition: ERF_AdvanceMorrison.cpp:111
The fluxes a fire engine hands to the atmosphere after its step.
Definition: ERF_FireEngine.H:99
const amrex::MultiFab * latent
Definition: ERF_FireEngine.H:101
std::string debug_note
Definition: ERF_FireEngine.H:104
const amrex::MultiFab * sensible
Sensible heat flux into the air [W/m2]; nullptr = none this step.
Definition: ERF_FireEngine.H:100
The atmospheric state a fire engine reads at the start of its step.
Definition: ERF_FireEngine.H:67
amrex::Real time
Start of the step [s].
Definition: ERF_FireEngine.H:68
const amrex::MultiFab & T_k0
Air temperature at k = 0 [K] (theta times Exner), before the dycore.
Definition: ERF_FireEngine.H:73
amrex::Real dt
Step of the fire grid's level [s].
Definition: ERF_FireEngine.H:69
const amrex::MultiFab & z_phys_cc
Cell-centre height [m] of the terrain-following mesh.
Definition: ERF_FireEngine.H:72
const amrex::MultiFab & yvel
y-velocity on y-faces [m/s], the same time level as xvel
Definition: ERF_FireEngine.H:71
const amrex::MultiFab * precip_accum_k0
Surface precipitation accumulated since t = 0 [kg/m2] after this step's microphysics,...
Definition: ERF_FireEngine.H:75
const amrex::MultiFab & RH_k0
Relative humidity at k = 0 [0-1], before the dycore.
Definition: ERF_FireEngine.H:74
const amrex::MultiFab & xvel
x-velocity on x-faces [m/s]: before the dycore (passive, lagged) or after it (synchronous)
Definition: ERF_FireEngine.H:70
2D horizontal fire computational grid
Definition: ERF_FireGrid.H:29