ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
FireCoupler Class Reference

#include <ERF_FireCoupler.H>

Collaboration diagram for FireCoupler:

Public Member Functions

 FireCoupler (const FireCouplingParams &params, std::unique_ptr< FireEngine > engine)
 
void initialize (const ERF &erf, int lev, const SurfaceLayer *surface_layer, const amrex::MultiFab *z_phys_nd_atm)
 Check the configuration, build the fire grid and set the engine up. More...
 
void set_fire_grid (int lev, const FireGrid &fg, const amrex::BoxArray &ba_atm, const amrex::DistributionMapping &dm_atm, const amrex::Geometry &geom_atm)
 Lay the coupling out on a fire grid: what initialize() does after its start-up checks and before it sets the engine up. More...
 
void advance (amrex::Real time, amrex::Real dt, const amrex::MultiFab &S_old, const amrex::MultiFab &xvel_old, const amrex::MultiFab &yvel_old, const amrex::MultiFab &xvel_new, const amrex::MultiFab &yvel_new, const amrex::MultiFab &z_phys_cc, const amrex::BoxArray &ba2d, const amrex::Geometry &geom, const SurfacePrecipAccumulationSources &precip_sources)
 Advance the fire one step of its level and refill the lagged fluxes. More...
 
void add_sources (amrex::MultiFab &cc_source, const amrex::MultiFab &S_old, const amrex::MultiFab &z_phys_cc, const amrex::MultiFab *z_phys_nd, const amrex::Geometry &geom, bool has_moisture, int step)
 Inject the lagged fire heat, vapour and smoke into the cell sources. More...
 
void write_checkpoint (const std::string &checkpointname) const
 Write the fire to a checkpoint: the engine's state (FireEngine::write_checkpoint), then the coupling's lagged fluxes (FireQAtmPrev and FireQLatAtmPrev) under checkpointname/Level_<lev>/. More...
 
std::string checkpoint_mismatch (int chk_lev, const amrex::Box &chk_region) const
 Why a checkpoint's lagged fluxes do not belong to this run's fire grid, or an empty string when they do. More...
 
void read_checkpoint (const std::string &restart_chkfile, int max_level, int step)
 Restore the engine's state, then the lagged fluxes, on a restart. More...
 
const std::vector< std::string > & plot_var_names () const
 
void fill_plot_var (const std::string &name, amrex::MultiFab &dst, int dcomp, int lev) const
 Fill component dcomp of an ERF plotfile MultiFab of level lev with the fire field name. More...
 
int level () const
 The level the fire grid refines (erf.fire.anchor_level, resolved at initialize). More...
 
const FireCouplingParams & params () const
 The coupling inputs. More...
 
FireEngine & engine ()
 The fire engine. More...
 
const FireEngine & engine () const
 
const FireGrid & fire_grid () const
 The fire grid. More...
 
const amrex::MultiFab * get_Q_atm_prev () const
 Sensible heat flux [W/m2] of the previous fire step on the atmospheric columns of the fire grid's level, with 5 ghost columns (for a reader that samples a halo around each tile, such as a boundary-layer scheme taking the fire's heat into its mixing); zero outside the fire region. Allocated at initialize, so never null afterwards. More...
 
const amrex::MultiFab * get_Q_lat_atm_prev () const
 Latent heat flux [W/m2] of the previous fire step, laid out as get_Q_atm_prev(). More...
 

Private Member Functions

void update_flux_buffer (const amrex::Geometry &geom_atm)
 Average the engine's fluxes onto the atmospheric columns for the next step. More...
 

Private Attributes

FireCouplingParams m_params
 
std::unique_ptr< FireEngine > m_engine
 
FireGrid m_fg
 
int m_lev = 0
 
amrex::Geometry m_geom_atm
 geometry of the fire grid's level More...
 
std::unique_ptr< amrex::MultiFab > m_Q_atm_prev
 sensible [W/m2] More...
 
std::unique_ptr< amrex::MultiFab > m_Q_lat_atm_prev
 latent [W/m2] More...
 
std::vector< std::string > m_plot_var_names
 FireEngine::plot_var_names(), read at construction. More...
 

Constructor & Destructor Documentation

◆ FireCoupler()

FireCoupler::FireCoupler ( const FireCouplingParams &  params,
std::unique_ptr< FireEngine >  engine 
)
Parameters
[in]paramsThe coupling inputs (FireCouplingParams::read())
[in]engineThe fire engine erf.fire.engine selects
71  : m_params(params), m_engine(std::move(engine))
72 {
73  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(m_engine != nullptr, "[FIRE] FireCoupler needs a fire engine");
74  m_plot_var_names = m_engine->plot_var_names();
75  if (!m_plot_var_names.empty()) {
76  amrex::Print() << "[FIRE] " << m_engine->name()
77  << ": the following additional variables are available to plot:\n";
78  for (const auto& n : m_plot_var_names) { amrex::Print() << " " << n << "\n"; }
79  }
80 }
AMREX_ALWAYS_ASSERT_WITH_MESSAGE(axis >=0 &&axis< AMREX_SPACEDIM, "prob.axis must be 0, 1 or 2")
const FireCouplingParams & params() const
The coupling inputs.
Definition: ERF_FireCoupler.H:196
std::unique_ptr< FireEngine > m_engine
Definition: ERF_FireCoupler.H:221
FireCouplingParams m_params
Definition: ERF_FireCoupler.H:220
FireEngine & engine()
The fire engine.
Definition: ERF_FireCoupler.H:199
std::vector< std::string > m_plot_var_names
FireEngine::plot_var_names(), read at construction.
Definition: ERF_FireCoupler.H:235
Here is the call graph for this function:

Member Function Documentation

◆ add_sources()

void FireCoupler::add_sources ( amrex::MultiFab &  cc_source,
const amrex::MultiFab &  S_old,
const amrex::MultiFab &  z_phys_cc,
const amrex::MultiFab *  z_phys_nd,
const amrex::Geometry &  geom,
bool  has_moisture,
int  step 
)

Inject the lagged fire heat, vapour and smoke into the cell sources.

Called from the slow right-hand side at every Runge-Kutta stage on the fire grid's level, after make_sources has rebuilt cc_source. The lagged fluxes are fixed over the step, so every stage receives the same tendency (erf.fire.source_mode decides whether it replaces or adds to the theta and vapour sources). Heat and vapour only with lagged or synchronous coupling; smoke with erf.fire.smoke_enable.

Parameters
[in,out]cc_sourceCell-centred sources of the level
[in]S_oldConserved state at the start of the step (density)
[in]z_phys_ccCell-centre height [m]
[in]z_phys_ndNodal height [m] (ERF always passes it): the cell thickness and roof datum of the heat, and the smoke cell's thickness
[in]geomGeometry of the level
[in]has_moistureThe vapour source exists
[in]stepStep of the level, for the debug output
282 {
283  // Heat and vapour, with the WRF-Fire profile (ERF_FireAtmCoupling.H)
285  if (m_params.fire_debug) {
286  amrex::Print() << "[FIRE DEBUG] Applying fire coupling to atmosphere (coupling_type="
287  << m_params.coupling_type << ", feedback=" << m_params.fire_atm_feedback
288  << ", max_Q_prev=" << m_Q_atm_prev->max(0) << " W/m2)" << std::endl;
289  }
290 
291  const MultiFab* Q_lat_ptr = (m_params.inject_latent && has_moisture)
292  ? m_Q_lat_atm_prev.get() : nullptr;
293 
294  // Pass fire_debug so apply_fire_tendency_to_cc_source prints tendency diagnostics.
295  // With fire_debug=true you will see per-RK-stage output:
296  // [FIRE COUPLING] Q_atm_max=... alfg=...
297  // [FIRE COUPLING] RhoTheta tendency sum=... max=... expected_surface_max=...
299  cc_source,
300  *m_Q_atm_prev,
301  Q_lat_ptr,
302  z_phys_cc,
303  z_phys_nd,
304  S_old,
305  geom,
308  has_moisture,
310  m_params.source_mode == "add",
311  m_engine->heat_open_fraction_atm(),
312  m_engine->roof_height_atm(),
317  }
318 
319  // The smoke tracer, after the heat: proportional to the fuel burnt, which
320  // the engine's heat per kilogram recovers from the lagged sensible flux
322  inject_smoke_from_fire(
323  cc_source,
324  *m_Q_atm_prev,
325  z_phys_nd,
326  geom,
328  m_engine->smoke_heat_per_kg(),
329  RhoSmoke_comp,
331  step);
332  }
333 }
void apply_fire_tendency_to_cc_source(amrex::MultiFab &cc_source, const amrex::MultiFab &Q_atm, const amrex::MultiFab *Q_lat_atm, const amrex::MultiFab &z_phys_cc, const amrex::MultiFab *z_phys_nd, const amrex::MultiFab &S_old, const amrex::Geometry &geom_atm, amrex::Real alfg, amrex::Real feedback, bool has_moisture, bool fire_debug=false, bool additive=false, const amrex::MultiFab *open_frac_atm=nullptr, const amrex::MultiFab *roof_h_atm=nullptr, bool use_open_weights=false, bool density_factor=false, bool exner_factor=false, bool bottom_cell=false)
Definition: ERF_FireAtmCoupling.H:203
std::unique_ptr< amrex::MultiFab > m_Q_lat_atm_prev
latent [W/m2]
Definition: ERF_FireCoupler.H:233
std::unique_ptr< amrex::MultiFab > m_Q_atm_prev
sensible [W/m2]
Definition: ERF_FireCoupler.H:232
bool heat_in_bottom_cell() const
Definition: ERF_FireCouplingParams.H:62
bool fire_debug
erf.fire.fire_debug: debug output of the coupling
Definition: ERF_FireCouplingParams.H:55
bool heat_tendency_exner
erf.fire.heat_tendency_exner: Exner factor of the theta tendency
Definition: ERF_FireCouplingParams.H:46
bool inject_latent
erf.fire.inject_latent: inject the engine's latent flux as vapour
Definition: ERF_FireCouplingParams.H:35
bool heat_tendency_density
erf.fire.heat_tendency_density: density factor of the theta tendency
Definition: ERF_FireCouplingParams.H:44
bool heat_open_fraction
erf.fire.heat_open_fraction: place the heat by the engine's open fraction
Definition: ERF_FireCouplingParams.H:42
bool smoke_enable
erf.fire.smoke_enable: inject the smoke tracer
Definition: ERF_FireCouplingParams.H:51
amrex::Real smoke_emission_factor
erf.fire.smoke_emission_factor: kg smoke per kg fuel burned [-]
Definition: ERF_FireCouplingParams.H:53
bool injects_flux() const
Heat (and vapour) reach the atmosphere: lagged and synchronous coupling.
Definition: ERF_FireCouplingParams.H:61
std::string source_mode
erf.fire.source_mode: overwrite or add to the theta and vapour sources
Definition: ERF_FireCouplingParams.H:37
std::string coupling_type
erf.fire.coupling_type: passive, lagged or synchronous
Definition: ERF_FireCouplingParams.H:29
amrex::Real heat_flux_alfg
erf.fire.heat_flux_alfg: e-folding height of the injected heat [m]
Definition: ERF_FireCouplingParams.H:31
amrex::Real fire_atm_feedback
erf.fire.fire_atm_feedback: multiplier on the injected fluxes
Definition: ERF_FireCouplingParams.H:33
Here is the call graph for this function:

◆ advance()

void FireCoupler::advance ( amrex::Real  time,
amrex::Real  dt,
const amrex::MultiFab &  S_old,
const amrex::MultiFab &  xvel_old,
const amrex::MultiFab &  yvel_old,
const amrex::MultiFab &  xvel_new,
const amrex::MultiFab &  yvel_new,
const amrex::MultiFab &  z_phys_cc,
const amrex::BoxArray &  ba2d,
const amrex::Geometry &  geom,
const SurfacePrecipAccumulationSources &  precip_sources 
)

Advance the fire one step of its level and refill the lagged fluxes.

Called from ERF::Advance() once per step of the fire grid's level, after that level's dycore. The air temperature and humidity at k = 0 come from the state before the dycore; the wind from before the dycore (passive and lagged coupling) or after it (synchronous).

Parameters
[in]timeStart of the step [s]
[in]dtStep of the level [s]
[in]S_oldConserved state before the dycore
[in]xvel_old,yvel_oldFace velocities before the dycore [m/s]
[in]xvel_new,yvel_newFace velocities after the dycore [m/s]
[in]z_phys_ccCell-centre height of the level [m]
[in]ba2dThe level's boxes coarsened to one cell in z
[in]geomGeometry of the level
[in]precip_sourcesThe microphysics surface precipitation accumulators of the level
144 {
145  // Air temperature and RH at k=0 (an engine may drive a fuel moisture
146  // model with them), derived from the pre-dycore state. This is used in both lagged and synchronous
147  // modes: the moisture ODE uses time-averaged T, and the change in T from
148  // fire heating within one atmospheric step is small relative to the
149  // moisture time lag. The temperature is theta * Exner(p) (the moisture
150  // curves, Balbi's T_a and the threshold ignition take an air
151  // temperature); the plain k = 0 potential temperature used here before
152  // was 14-16 K too warm at 840 hPa.
153  MultiFab T_atm_k0(ba2d, S_old.DistributionMap(), 1, 0);
154  compute_t_from_conservative(T_atm_k0, S_old);
155  MultiFab RH_atm_k0(ba2d, S_old.DistributionMap(), 1, 0);
156  compute_rh_from_conservative(RH_atm_k0, S_old, geom);
157 
158  // Surface precipitation accumulation of every column after this step's
159  // microphysics, for an engine that takes the rain per column
160  // (FireEngine::wants_surface_precip); the engine differences it against the
161  // accumulation it saw at its previous step.
162  std::unique_ptr<MultiFab> precip_accum_k0;
163  if (m_engine->wants_surface_precip()) {
164  precip_accum_k0 = std::make_unique<MultiFab>(ba2d, S_old.DistributionMap(), 1, 0);
165  fire_surface_precip_accum_k0(*precip_accum_k0, precip_sources, geom.Domain().smallEnd(2));
166  }
167 
168  // In passive and lagged modes, fire uses pre-dycore wind (vars_old).
169  // In synchronous mode, fire uses post-dycore wind (vars_new) so that
170  // the fire spread rate at step n+1 reflects the atmospheric momentum
171  // response to fire heating from step n.
172  const bool post_dycore = m_params.is_synchronous();
173  if (m_params.fire_debug) {
174  const char* mode = m_params.is_synchronous() ? "SYNCHRONOUS coupling with POST-dycore"
175  : m_params.is_lagged() ? "LAGGED coupling with PRE-dycore"
176  : "PASSIVE coupling with PRE-dycore";
177  amrex::Print() << "[FIRE DEBUG] Fire advance using " << mode << " wind at t="
178  << time << ", dt=" << dt << std::endl;
179  }
180  const FireForcing forcing{time, dt,
181  post_dycore ? xvel_new : xvel_old,
182  post_dycore ? yvel_new : yvel_old,
183  z_phys_cc, T_atm_k0, RH_atm_k0, precip_accum_k0.get()};
184  m_engine->advance(forcing);
185  if (m_params.fire_debug) {
186  const Real area = m_engine->burned_area(); // every rank: it may reduce over the fire grid
187  if (area >= 0.0_rt) {
188  amrex::Print() << "[FIRE DEBUG] Burned area at t=" << time + dt << ": " << area << " m2" << std::endl;
189  }
190  }
191 
192  // Store the fire flux of this step for injection at the next step via the
193  // Runge-Kutta source term (add_sources). This applies in both lagged and
194  // synchronous modes; in passive mode the buffer keeps its zero unless the
195  // smoke tracer needs it.
196  update_flux_buffer(geom);
197 }
void fire_surface_precip_accum_k0(amrex::MultiFab &accum_2d, const SurfacePrecipAccumulationSources &sources, int klo)
Total liquid-water-equivalent surface precipitation accumulation [kg/m2] of every column,...
Definition: ERF_FirePrecip.H:63
void compute_t_from_conservative(amrex::MultiFab &T_out, const amrex::MultiFab &S_atm)
Air temperature [K] at k = 0 from the conserved state.
Definition: ERF_FireUtils.H:117
void compute_rh_from_conservative(amrex::MultiFab &RH_out, const amrex::MultiFab &S_atm, const amrex::Geometry &)
Compute relative humidity from conserved atmospheric variables at k=0.
Definition: ERF_FireUtils.H:37
amrex::Real Real
Definition: ERF_ShocInterface.H:19
void update_flux_buffer(const amrex::Geometry &geom_atm)
Average the engine's fluxes onto the atmospheric columns for the next step.
Definition: ERF_FireCoupler.cpp:232
bool is_lagged() const
Definition: ERF_FireCouplingParams.H:58
bool is_synchronous() const
Definition: ERF_FireCouplingParams.H:59
The atmospheric state a fire engine reads at the start of its step.
Definition: ERF_FireEngine.H:67
Here is the call graph for this function:

◆ checkpoint_mismatch()

std::string FireCoupler::checkpoint_mismatch ( int  chk_lev,
const amrex::Box &  chk_region 
) const

Why a checkpoint's lagged fluxes do not belong to this run's fire grid, or an empty string when they do.

Parameters
[in]chk_levThe level the checkpoint holds them on
[in]chk_regionThe columns they cover (minimal box of their boxes)
354 {
355  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(m_Q_atm_prev, "[FIRE] FireCoupler: set_fire_grid() before the checkpoint");
356  // The fluxes cover the columns of the level's boxes, which form the fire
357  // region (verify_fire_prerequisites, check 7b)
358  const Box run_region = m_Q_atm_prev->boxArray().minimalBox();
359  if (chk_lev == m_lev && chk_region == run_region) { return {}; }
360  std::ostringstream msg;
361  msg << "holds the lagged fire fluxes on level " << chk_lev << " over the columns " << chk_region
362  << ", but this run's fire grid covers the columns " << run_region << " of level " << m_lev
363  << ". Restart with the erf.fire.anchor_level and the refinement the checkpoint was written with.";
364  return msg.str();
365 }
int m_lev
Definition: ERF_FireCoupler.H:223

Referenced by read_checkpoint().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ engine() [1/2]

FireEngine& FireCoupler::engine ( )
inline

The fire engine.

199 { return *m_engine; }

◆ engine() [2/2]

const FireEngine& FireCoupler::engine ( ) const
inline
200 { return *m_engine; }

◆ fill_plot_var()

void FireCoupler::fill_plot_var ( const std::string &  name,
amrex::MultiFab &  dst,
int  dcomp,
int  lev 
) const

Fill component dcomp of an ERF plotfile MultiFab of level lev with the fire field name.

On the fire grid's level, the k = 0 cell of each column holds the mean of the engine's field (FireEngine::plot_var) over the fire cells under it; every other cell, every other level and a field the engine returns as nullptr hold zero.

Parameters
[in]nameOne of plot_var_names()
[out]dstCell-centred MultiFab on the boxes of level lev
[in]dcompComponent of dst to fill
[in]levThe level dst belongs to
201 {
202  dst.setVal(0.0_rt, dcomp, 1, 0);
203  if (lev != m_lev || !m_Q_atm_prev) { return; }
204  const MultiFab* fire_field = m_engine->plot_var(name);
205  if (!fire_field) { return; }
206 
207  // The column means, on the k = 0 slabs the lagged fluxes use
208  MultiFab col(m_Q_atm_prev->boxArray(), m_Q_atm_prev->DistributionMap(), 1, 0);
209  col.setVal(0.0_rt);
210  coarsen_fire_flux_to_atm(col, *fire_field, m_geom_atm, m_fg);
211 
212  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(dst.boxArray().size() == col.boxArray().size() &&
213  dst.DistributionMap() == col.DistributionMap(),
214  "[FIRE] fill_plot_var: the plotfile MultiFab is not on the fire grid's level");
215  const int k0 = m_geom_atm.Domain().smallEnd(2);
216  for (MFIter mfi(dst); mfi.isValid(); ++mfi) {
217  Box bx = mfi.validbox();
218  if (bx.smallEnd(2) > k0) { continue; }
219  bx.setSmall(2, k0);
220  bx.setBig(2, k0);
221  const auto d = dst.array(mfi);
222  const auto c = col.const_array(mfi);
223  const int kc = col.boxArray()[mfi.index()].smallEnd(2);
224  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
225  {
226  d(i, j, k, dcomp) = c(i, j, kc);
227  });
228  }
229 }
void coarsen_fire_flux_to_atm(amrex::MultiFab &Q_atm_out, const amrex::MultiFab &Q_fire, const amrex::Geometry &geom_atm, const FireGrid &fg)
Definition: ERF_FireAtmCoupling.H:112
ParallelFor(fab_box, [=] AMREX_GPU_DEVICE(int i, int j, int k) { qrcuten_arr(i, j, k)=Real(0);qscuten_arr(i, j, k)=Real(0);qicuten_arr(i, j, k)=Real(0);})
std::string name
Definition: ERF_Plotfile2DCatalog.cpp:116
FireGrid m_fg
Definition: ERF_FireCoupler.H:222
amrex::Geometry m_geom_atm
geometry of the fire grid's level
Definition: ERF_FireCoupler.H:224
Here is the call graph for this function:

◆ fire_grid()

const FireGrid& FireCoupler::fire_grid ( ) const
inline

The fire grid.

203 { return m_fg; }

◆ get_Q_atm_prev()

const amrex::MultiFab* FireCoupler::get_Q_atm_prev ( ) const
inline

Sensible heat flux [W/m2] of the previous fire step on the atmospheric columns of the fire grid's level, with 5 ghost columns (for a reader that samples a halo around each tile, such as a boundary-layer scheme taking the fire's heat into its mixing); zero outside the fire region. Allocated at initialize, so never null afterwards.

212 { return m_Q_atm_prev.get(); }

◆ get_Q_lat_atm_prev()

const amrex::MultiFab* FireCoupler::get_Q_lat_atm_prev ( ) const
inline

Latent heat flux [W/m2] of the previous fire step, laid out as get_Q_atm_prev().

214 { return m_Q_lat_atm_prev.get(); }

◆ initialize()

void FireCoupler::initialize ( const ERF &  erf,
int  lev,
const SurfaceLayer *  surface_layer,
const amrex::MultiFab *  z_phys_nd_atm 
)

Check the configuration, build the fire grid and set the engine up.

Called once from ERF::InitData_post() after every surface layer exists. Runs the coupling's checks (verify_fire_prerequisites), then the engine's, builds the fire grid on level lev, allocates the lagged flux buffers (zero) and initialises the engine.

Parameters
[in]erfERF instance (public AmrCore accessors only)
[in]levThe level the fire grid refines (fire_anchor_level)
[in]surface_layerThe bottom surface layer, or nullptr (aborts when the engine needs one, FireEngine::needs_surface_layer)
[in]z_phys_nd_atmNodal height of level lev [m] (ERF always passes it)
87 {
88  // The ERF configuration a fire grid needs, then the engine's own inputs
90  m_engine->needs_surface_layer());
91  m_engine->verify_prerequisites(erf, lev);
92 
93  FireGrid fg = create_fire_grid(erf.boxArray(lev), erf.DistributionMap(lev),
94  erf.Geom(lev), m_params.grid_ratio);
95  fg.lev = lev;
96  report_fire_region(erf, lev, fg);
97  set_fire_grid(lev, fg, erf.boxArray(lev), erf.DistributionMap(lev), erf.Geom(lev));
98 
99  m_engine->initialize(erf, lev, m_fg, surface_layer, z_phys_nd_atm);
100 
101  // erf.fire.heat_open_fraction places the heat by the open fraction the
102  // engine reports; without one the heat is placed as if every column were open
104  (!m_engine->heat_open_fraction_atm() || !m_engine->roof_height_atm())) {
105  amrex::Print() << "[FIRE] WARNING: erf.fire.heat_open_fraction = true, but the fire engine ("
106  << m_engine->name() << ") reports no open fraction;"
107  << " the heat is placed as if every column were open\n";
108  }
109 }
FireGrid create_fire_grid(const BoxArray &ba_atm, const DistributionMapping &dm_atm, const Geometry &geom_atm, int C)
Definition: ERF_FireGrid.cpp:10
void verify_fire_prerequisites(const ERF &erf, int lev, const SurfaceLayer *surface_layer, int grid_ratio, bool needs_surface_layer)
Verify the ERF configuration the fire coupling needs.
Definition: ERF_FirePrerequisites.cpp:78
@ surface_layer
void set_fire_grid(int lev, const FireGrid &fg, const amrex::BoxArray &ba_atm, const amrex::DistributionMapping &dm_atm, const amrex::Geometry &geom_atm)
Lay the coupling out on a fire grid: what initialize() does after its start-up checks and before it s...
Definition: ERF_FireCoupler.cpp:112
@ fg
Definition: ERF_AdvanceMorrison.cpp:111
int grid_ratio
erf.fire.grid_ratio: fire cells per atmospheric cell in x and y
Definition: ERF_FireCouplingParams.H:25
2D horizontal fire computational grid
Definition: ERF_FireGrid.H:29
Here is the call graph for this function:

◆ level()

int FireCoupler::level ( ) const
inline

The level the fire grid refines (erf.fire.anchor_level, resolved at initialize).

193 { return m_lev; }

◆ params()

const FireCouplingParams& FireCoupler::params ( ) const
inline

The coupling inputs.

196 { return m_params; }

◆ plot_var_names()

const std::vector<std::string>& FireCoupler::plot_var_names ( ) const
inline

Fire fields the ERF plotfile can carry (FireEngine::plot_var_names(), read at construction, before ERF reads erf.plot_vars_*).

174 { return m_plot_var_names; }

◆ read_checkpoint()

void FireCoupler::read_checkpoint ( const std::string &  restart_chkfile,
int  max_level,
int  step 
)

Restore the engine's state, then the lagged fluxes, on a restart.

Aborts when the checkpoint holds the lagged fluxes on another level or over another region than this run's fire grid. A checkpoint without them (no fire state, or written before they were checkpointed) leaves them at zero: the first restarted step injects no fire heat.

Parameters
[in]restart_chkfileThe checkpoint directory
[in]max_levelERF's max_level
[in]stepThe step count of the fire grid's level at the checkpoint
369 {
370  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(m_Q_atm_prev, "[FIRE] FireCoupler: set_fire_grid() before the checkpoint");
371  // The engine first: it may check, naming its own fields, that the
372  // checkpoint's fire grid sits on this level and region
373  m_engine->read_checkpoint(restart_chkfile, max_level, step);
374 
375  // The lagged fluxes, on the level of the fire grid. A checkpoint without them
376  // (written with no fire running): the buffers keep their zero, said once.
377  // Decided on the I/O rank and broadcast, so every rank takes the same branch
378  // into the collective reads below
379  int chk_lev = -1;
380  if (ParallelDescriptor::IOProcessor()) {
381  for (int l = 0; l <= max_level; ++l) {
382  if (amrex::FileExists(restart_chkfile + "/Level_" + std::to_string(l) + "/FireQAtmPrev_H")) {
383  chk_lev = l;
384  break;
385  }
386  }
387  }
388  ParallelDescriptor::Bcast(&chk_lev, 1, ParallelDescriptor::IOProcessorNumber());
389  if (chk_lev < 0) {
390  amrex::Print() << "[FIRE] Checkpoint has no lagged fire fluxes; the first restarted step injects none.\n";
391  return;
392  }
393  // Whatever the engine: the fluxes belong to this run's fire grid, on its
394  // level and over its region (an engine may have checked its own fields
395  // already; this check holds whatever the engine)
396  const Box chk_region = VisMF(MultiFabFileFullPrefix(chk_lev, restart_chkfile, "Level_", "FireQAtmPrev"))
397  .boxArray().minimalBox();
398  if (const std::string why = checkpoint_mismatch(chk_lev, chk_region); !why.empty()) {
399  amrex::Abort("[FIRE] The checkpoint " + restart_chkfile + " " + why);
400  }
401  for (const auto& [mf, name] : {std::pair<MultiFab*, const char*>{m_Q_atm_prev.get(), "FireQAtmPrev"},
402  std::pair<MultiFab*, const char*>{m_Q_lat_atm_prev.get(), "FireQLatAtmPrev"}}) {
403  if (mf == nullptr) { continue; }
404  // decided on one rank and broadcast, so every rank takes the same
405  // path into the collective read below
406  const std::string header = restart_chkfile + "/Level_" + std::to_string(m_lev) + "/" + name + "_H";
407  int has_field = 0;
408  if (ParallelDescriptor::IOProcessor()) { has_field = amrex::FileExists(header) ? 1 : 0; }
409  ParallelDescriptor::Bcast(&has_field, 1, ParallelDescriptor::IOProcessorNumber());
410  if (has_field == 0) {
411  amrex::Print() << "[FIRE] Checkpoint has no " << name << "; keeping the initialized values.\n";
412  continue;
413  }
414  VisMF::Read(*mf, MultiFabFileFullPrefix(m_lev, restart_chkfile, "Level_", name));
415  // VisMF restores the valid cells (a checkpoint may hold no ghost
416  // columns), so refill the ghosts the way update_flux_buffer() does.
418  }
419 }
void fire_fill_boundary(amrex::MultiFab &mf, const amrex::Geometry &geom)
Definition: ERF_FireGrid.H:82
std::string checkpoint_mismatch(int chk_lev, const amrex::Box &chk_region) const
Why a checkpoint's lagged fluxes do not belong to this run's fire grid, or an empty string when they ...
Definition: ERF_FireCoupler.cpp:353
Here is the call graph for this function:

◆ set_fire_grid()

void FireCoupler::set_fire_grid ( int  lev,
const FireGrid &  fg,
const amrex::BoxArray &  ba_atm,
const amrex::DistributionMapping &  dm_atm,
const amrex::Geometry &  geom_atm 
)

Lay the coupling out on a fire grid: what initialize() does after its start-up checks and before it sets the engine up.

Takes the fire grid and allocates the lagged flux buffers (zero) on the k = 0 slabs of ba_atm, with 5 ghost columns. Public so that a test can drive the coupling with an engine of its own without an ERF object (Tests/Unit/Fire/ERF_GTestFireCoupler.cpp).

Parameters
[in]levThe level the fire grid refines
[in]fgThe fire grid (create_fire_grid)
[in]ba_atmThe level's boxes
[in]dm_atmTheir owners
[in]geom_atmThe level's geometry
116 {
117  m_lev = lev;
118  m_fg = fg;
119  m_geom_atm = geom_atm;
120  const int nz = geom_atm.Domain().length(2);
121 
122  // The lagged fluxes live on the k = 0 slabs of the level's boxes, with the
123  // level's owners. Ghost columns for a reader that samples the fluxes in the
124  // halo around every tile: 5, the most ERF's state carries
125  // (ComputeGhostCells() + 1); update_flux_buffer() fills them.
126  BoxArray ba_atm_2d = ba_atm;
127  ba_atm_2d.coarsen(IntVect(1, 1, nz));
128  const IntVect ng_flux(5, 5, 0);
129  m_Q_atm_prev = std::make_unique<MultiFab>(ba_atm_2d, dm_atm, 1, ng_flux);
130  m_Q_lat_atm_prev = std::make_unique<MultiFab>(ba_atm_2d, dm_atm, 1, ng_flux);
131  m_Q_atm_prev->setVal(0.0_rt);
132  m_Q_lat_atm_prev->setVal(0.0_rt);
133 }

Referenced by initialize().

Here is the caller graph for this function:

◆ update_flux_buffer()

void FireCoupler::update_flux_buffer ( const amrex::Geometry &  geom_atm)
private

Average the engine's fluxes onto the atmospheric columns for the next step.

233 {
235  if (m_params.fire_debug) {
236  amrex::Print() << "[FIRE DEBUG] Skipping flux buffer update: coupling_type is passive (injects_flux=false) and smoke_enable=false" << std::endl;
237  }
238  return;
239  }
240 
241  if (!m_Q_atm_prev) { return; }
242  FireFluxes fluxes;
243  m_engine->fill_fluxes(fluxes);
244  if (fluxes.sensible) {
245  coarsen_fire_flux_to_atm(*m_Q_atm_prev, *fluxes.sensible, geom_atm, m_fg);
246  if (m_params.fire_debug) {
247  amrex::Print() << "[FIRE DEBUG] Sensible flux to the atmosphere: max " << m_Q_atm_prev->max(0)
248  << " W/m2" << fluxes.debug_note << std::endl;
249  }
250  } else {
251  // Clear the stale sensible flux without discarding independent latent
252  // feedback reported by the engine during this same step.
253  m_Q_atm_prev->setVal(0.0_rt);
254  }
255 
256  if (m_params.inject_latent && fluxes.latent) {
258  } else {
260  amrex::Print() << "[FIRE DEBUG] Skipping latent heat injection (inject_latent=false or the engine reports no latent flux)" << std::endl;
261  }
262  m_Q_lat_atm_prev->setVal(0.0_rt);
263  }
264 
265  // A reader may sample these fluxes in the halo columns around every tile;
266  // give them the neighbouring box's value (and the edge column's
267  // outside a non-periodic face), so a column sees the same flux whichever box
268  // holds it. On a finer level the columns outside the refined region have no
269  // fire grid and keep the zero they were allocated with.
270  fire_fill_boundary(*m_Q_atm_prev, geom_atm);
272 }
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

Referenced by advance().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ write_checkpoint()

void FireCoupler::write_checkpoint ( const std::string &  checkpointname) const

Write the fire to a checkpoint: the engine's state (FireEngine::write_checkpoint), then the coupling's lagged fluxes (FireQAtmPrev and FireQLatAtmPrev) under checkpointname/Level_<lev>/.

337 {
338  // The engine's own state first, as read_checkpoint restores it
339  m_engine->write_checkpoint(checkpointname);
340 
341  // The lagged fluxes injected into the atmosphere on the next step. Without
342  // them the first step after a restart injects no fire heat at all.
343  if (m_Q_atm_prev) {
344  amrex::Print() << "Writing fire atmosphere flux buffer to checkpoint" << std::endl;
345  VisMF::Write(*m_Q_atm_prev, MultiFabFileFullPrefix(m_lev, checkpointname, "Level_", "FireQAtmPrev"));
346  }
347  if (m_Q_lat_atm_prev) {
348  VisMF::Write(*m_Q_lat_atm_prev, MultiFabFileFullPrefix(m_lev, checkpointname, "Level_", "FireQLatAtmPrev"));
349  }
350 }

Member Data Documentation

◆ m_engine

◆ m_fg

FireGrid FireCoupler::m_fg
private

◆ m_geom_atm

amrex::Geometry FireCoupler::m_geom_atm
private

geometry of the fire grid's level

Referenced by fill_plot_var(), read_checkpoint(), and set_fire_grid().

◆ m_lev

int FireCoupler::m_lev = 0
private

◆ m_params

FireCouplingParams FireCoupler::m_params
private

◆ m_plot_var_names

std::vector<std::string> FireCoupler::m_plot_var_names
private

FireEngine::plot_var_names(), read at construction.

Referenced by FireCoupler(), and plot_var_names().

◆ m_Q_atm_prev

std::unique_ptr<amrex::MultiFab> FireCoupler::m_Q_atm_prev
private

◆ m_Q_lat_atm_prev

std::unique_ptr<amrex::MultiFab> FireCoupler::m_Q_lat_atm_prev
private

The documentation for this class was generated from the following files: