ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
ERF_SlowRhsPost.cpp File Reference
Include dependency graph for ERF_SlowRhsPost.cpp:

Functions

void erf_slow_rhs_post (int level, int finest_level, int nrk, double step_old_time, double input_time, double target_time, double dt_d, int n_qstate, Vector< MultiFab > &S_rhs, Vector< MultiFab > &S_old, Vector< MultiFab > &S_new, Vector< MultiFab > &S_data, const MultiFab &S_prim, MultiFab &avg_xmom, MultiFab &avg_ymom, MultiFab &avg_zmom, const MultiFab &xvel, const MultiFab &yvel, const MultiFab &zvel, const MultiFab &source, MultiFab *terrain_blank, MultiFab *terrain_blank_xface, MultiFab *terrain_blank_yface, MultiFab *terrain_blank_zface, const MultiFab *SmnSmn, const MultiFab *eddyDiffs, MultiFab *Hfx1, MultiFab *Hfx2, MultiFab *Hfx3, MultiFab *Q1fx1, MultiFab *Q1fx2, MultiFab *Q1fx3, MultiFab *Q2fx3, MultiFab *Diss, MultiFab *Tau13, MultiFab *Tau23, const Geometry geom, const SolverChoice &solverChoice, const Vector< std::unique_ptr< SurfaceLayer >> &SurfLayer, const Gpu::DeviceVector< BCRec > &domain_bcs_type_d, const Vector< BCRec > &domain_bcs_type_h, std::unique_ptr< MultiFab > &z_phys_nd, std::unique_ptr< MultiFab > &z_phys_cc, std::unique_ptr< MultiFab > &ax, std::unique_ptr< MultiFab > &ay, std::unique_ptr< MultiFab > &az, std::unique_ptr< MultiFab > &detJ, MultiFab *detJ_new, Gpu::DeviceVector< Real > &stretched_dz_d, Vector< std::unique_ptr< MultiFab >> &mapfac, const eb_ &ebfact, ShocDriver *native_shoc_lev, YAFluxRegister *fr_as_crse, YAFluxRegister *fr_as_fine, std::unique_ptr< ReadBndryPlanes > &m_r2d, const MultiFab *cloud_chamber_base_state, const erf_cloud_chamber::Config *cloud_chamber_config, CloudChamberBudget *cloud_budget, erf_auxiliary::AuxiliaryInertTracer *auxiliary_inert_tracer, erf_sbm::SBMStateManager *sbm_state_manager, erf_sbm::SBMTransport *sbm_transport)
 

Function Documentation

◆ erf_slow_rhs_post()

void erf_slow_rhs_post ( int  level,
int  finest_level,
int  nrk,
double  step_old_time,
double  input_time,
double  target_time,
double  dt_d,
int  n_qstate,
Vector< MultiFab > &  S_rhs,
Vector< MultiFab > &  S_old,
Vector< MultiFab > &  S_new,
Vector< MultiFab > &  S_data,
const MultiFab &  S_prim,
MultiFab &  avg_xmom,
MultiFab &  avg_ymom,
MultiFab &  avg_zmom,
const MultiFab &  xvel,
const MultiFab &  yvel,
const MultiFab &  zvel,
const MultiFab &  source,
MultiFab *  terrain_blank,
MultiFab *  terrain_blank_xface,
MultiFab *  terrain_blank_yface,
MultiFab *  terrain_blank_zface,
const MultiFab *  SmnSmn,
const MultiFab *  eddyDiffs,
MultiFab *  Hfx1,
MultiFab *  Hfx2,
MultiFab *  Hfx3,
MultiFab *  Q1fx1,
MultiFab *  Q1fx2,
MultiFab *  Q1fx3,
MultiFab *  Q2fx3,
MultiFab *  Diss,
MultiFab *  Tau13,
MultiFab *  Tau23,
const Geometry  geom,
const SolverChoice &  solverChoice,
const Vector< std::unique_ptr< SurfaceLayer >> &  SurfLayer,
const Gpu::DeviceVector< BCRec > &  domain_bcs_type_d,
const Vector< BCRec > &  domain_bcs_type_h,
std::unique_ptr< MultiFab > &  z_phys_nd,
std::unique_ptr< MultiFab > &  z_phys_cc,
std::unique_ptr< MultiFab > &  ax,
std::unique_ptr< MultiFab > &  ay,
std::unique_ptr< MultiFab > &  az,
std::unique_ptr< MultiFab > &  detJ,
MultiFab *  detJ_new,
Gpu::DeviceVector< Real > &  stretched_dz_d,
Vector< std::unique_ptr< MultiFab >> &  mapfac,
const eb_ &  ebfact,
ShocDriver *  native_shoc_lev,
YAFluxRegister *  fr_as_crse,
YAFluxRegister *  fr_as_fine,
std::unique_ptr< ReadBndryPlanes > &  m_r2d,
const MultiFab *  cloud_chamber_base_state,
const erf_cloud_chamber::Config *  cloud_chamber_config,
CloudChamberBudget *  cloud_budget,
erf_auxiliary::AuxiliaryInertTracer *  auxiliary_inert_tracer,
erf_sbm::SBMStateManager *  sbm_state_manager,
erf_sbm::SBMTransport *  sbm_transport 
)

Function for computing the slow RHS for the evolution equations for the scalars other than density or potential temperature

Parameters
[in]evellevel of resolution
[in]finest_levelfinest level of resolution
[in]nrkwhich RK stage
[in]dtslow time step
[out]S_rhsRHS computed here
[in]S_oldsolution at start of time step
[in]S_newsolution at end of current RK stage
[in]S_datacurrent solution
[in]S_primprimitive variables (i.e. conserved variables divided by density)
[in]avg_xmom
[in]avg_ymom
[in]avg_zmom
[in]xvelx-component of velocity
[in]yvely-component of velocity
[in]zvelz-component of velocity
[in]sourcesource terms for conserved variables
[in]terrain_blankimmersed forcing mask
[in]SmnSmnstrain rate magnitude
[in]eddyDiffsdiffusion coefficients for LES turbulence models
[in]Hfx3heat flux in z-dir
[in]Dissdissipation of turbulent kinetic energy
[in]Tau13rho<u'w'>, carrying the surface-layer stress on the zlo face
[in]Tau23rho<v'w'>, carrying the surface-layer stress on the zlo face
[in]geomContainer for geometric information
[in]solverChoiceContainer for solver parameters
[in]SurfLayerPointer to SurfaceLayer class for Monin-Obukhov Similarity Theory boundary condition
[in]domain_bcs_type_ddevice vector for domain boundary conditions
[in]z_phys_ndheight coordinate at nodes
[in]axarea fractions on x-faces
[in]ayarea fractions on y-faces
[in]azarea fractions on z-faces
[in]detJJacobian of the metric transformation at start of time step (= 1 if use_terrain is false)
[in]detJ_newJacobian of the metric transformation at new RK stage time (= 1 if use_terrain is false)
[in]mapfacmap factors
[in,out]fr_as_crseYAFluxRegister at level l at level l / l+1 interface
[in,out]fr_as_fineYAFluxRegister at level l at level l-1 / l interface
117 {
118  BL_PROFILE_REGION("erf_slow_rhs_post()");
119 
120  Real dt = static_cast<Real>(dt_d);
121 
122  const bool sbm_active = solverChoice.moisture_type == MoistureType::SBM;
123 
124  const BCRec* bc_ptr_d = domain_bcs_type_d.data();
125  const BCRec* bc_ptr_h = domain_bcs_type_h.data();
126 
127  AdvChoice ac = solverChoice.advChoice;
128  DiffChoice dc = solverChoice.diffChoice;
129  TurbChoice tc = solverChoice.turbChoice[level];
130 
131  const MultiFab* t_mean_mf = nullptr;
132  if (SurfLayer[Orientation(Direction::z, Orientation::low)]) { t_mean_mf = SurfLayer[Orientation(Direction::z, Orientation::low)]->get_mac_avg(level,3); }
133 
134  const bool l_use_terrain = (solverChoice.mesh_type != MeshType::ConstantDz);
135  const bool l_moving_terrain = (solverChoice.terrain_type == TerrainType::MovingFittedMesh);
136  if (l_moving_terrain) AMREX_ALWAYS_ASSERT(l_use_terrain);
137 
138  const bool l_anelastic = solverChoice.anelastic[level];
139  const bool l_anelastic_rk2 = (solverChoice.anelastic_type[level] == AnelasticType::RK2);
140 
141  // Only add to the flux registers on the final RK stage. The anelastic integrator
142  // takes two stages (nrk = 0,1) and the compressible one takes three (nrk = 0,1,2),
143  // so testing nrk == 2 alone would never reflux the scalar and moisture fluxes in an
144  // anelastic run. This matches the condition used in erf_slow_rhs_pre.
145  const bool l_reflux = ( (solverChoice.coupling_type == CouplingType::TwoWay) && (finest_level > 0) &&
146  ( (l_anelastic && nrk == 1) || (!l_anelastic && nrk == 2) ) );
147 
148  const bool l_use_KE = ( tc.use_tke );
149  const bool l_need_SmnSmn = ( tc.les_type == LESType::Deardorff ||
150  tc.rans_type == RANSType::kEqn );
151  // k-eqn RANS with a Dirichlet wall value: SurfaceLayer::update_fluxes
152  // writes AL01 Eq. 16 into the first cell of S_old at the start of the
153  // step; keep that value through every RK stage.
154  const bool l_dirichlet_k = ( tc.rans_type == RANSType::kEqn && tc.dirichlet_k &&
155  (SurfLayer[Orientation(Direction::z, Orientation::low)] != nullptr) );
156  // Implicit TKE dissipation: eps = c * (rho k)_new with c = diss_old / (rho k)_old,
157  // i.e. Cmu0^3 sqrt(k_old) / L; the source skips the explicit sink and the
158  // update divides by (1 + dt c).
159  const bool l_implicit_diss = ( tc.use_keqn && tc.implicit_tke_dissipation );
160  const bool l_advect_KE = ( tc.use_tke && tc.advect_tke );
161  const bool l_use_diff = ((dc.molec_diff_type != MolecDiffType::None) ||
162  (tc.les_type != LESType::None) ||
163  (tc.rans_type != RANSType::None) ||
164  (tc.pbl_type != PBLType::None) );
165  const bool l_use_turb = tc.use_kturb;
166  const bool l_rotate = (solverChoice.use_rotate_surface_flux);
167  const bool l_do_scalar = (solverChoice.transport_scalar);
168  const bool l_use_eb = (solverChoice.terrain_type == TerrainType::EB);
169 
170  auto any_SurfLayer = [&SurfLayer_ = SurfLayer]() -> bool {
171  for (auto it = SurfLayer_.begin(); it != SurfLayer_.end(); it++)
172  {
173  if (*it != nullptr) { return true; }
174  }
175  return false;
176  };
177 
178  amrex::ignore_unused(m_r2d);
179 
180  const Box& domain = geom.Domain();
181 
182  bool l_apply_surface_layer_fluxes_in_diffusion = any_SurfLayer();
183 #ifdef ERF_USE_EAMXX_SHOC
184  if (tc.uses_eamxx_shoc()) {
185  l_apply_surface_layer_fluxes_in_diffusion = false;
186  }
187 #endif
188  if (tc.uses_native_shoc()) {
189  AMREX_ALWAYS_ASSERT(native_shoc_lev != nullptr);
190  l_apply_surface_layer_fluxes_in_diffusion = false;
191  }
192 
193  const GpuArray<Real, AMREX_SPACEDIM> dxInv = geom.InvCellSizeArray();
194  const Real* dx = geom.CellSize();
195 
196  // *************************************************************************
197  // Set gravity as a vector
198  // *************************************************************************
199  const Array<Real,AMREX_SPACEDIM> grav{zero, zero, -solverChoice.gravity};
200  const GpuArray<Real,AMREX_SPACEDIM> grav_gpu{grav[0], grav[1], grav[2]};
201 
202  // *************************************************************************
203  // Pre-computed quantities
204  // *************************************************************************
205  int nvars = S_data[IntVars::cons].nComp();
206 
207  // Total number of q state components. This is n_qstate (the water species) plus
208  // any non-water species that the microphysics model appends after them, and is the
209  // count that the advection, diffusion, state update and reflux all work over.
210  const int n_qstate_total = nvars - RhoQ1_comp;
211 
212  const BoxArray& ba = S_data[IntVars::cons].boxArray();
213  const DistributionMapping& dm = S_data[IntVars::cons].DistributionMap();
214  const bool use_physical_chamber_wall_flux =
215  cloud_chamber_config != nullptr && cloud_chamber_base_state != nullptr &&
216  cloud_chamber_config->physical_initialization;
217  const erf_wall_thermodynamics::Boundary chamber_walls =
218  use_physical_chamber_wall_flux ? cloud_chamber_config->wall_boundary() :
220 
221  std::unique_ptr<MultiFab> dflux_x;
222  std::unique_ptr<MultiFab> dflux_y;
223  std::unique_ptr<MultiFab> dflux_z;
224 
225  if (l_use_diff) {
226  IntVect ng(0,0,1);
227  // The physical chamber needs one persistent component for each moist
228  // state even when budgets are disabled: qv and qc are corrected and
229  // retained independently. All other configurations retain ERF's
230  // established one-component reusable diffusion storage.
231  const int n_flux_components = use_physical_chamber_wall_flux ?
232  std::max(1, n_qstate_total) : 1;
233  dflux_x = std::make_unique<MultiFab>(convert(ba,IntVect(1,0,0)), dm, n_flux_components, ng);
234  dflux_y = std::make_unique<MultiFab>(convert(ba,IntVect(0,1,0)), dm, n_flux_components, ng);
235  dflux_z = std::make_unique<MultiFab>(convert(ba,IntVect(0,0,1)), dm, n_flux_components, 0);
236  // Every physical wall override reads the old face flux before
237  // replacing it; make that read deterministic regardless of budget
238  // diagnostics.
239  if (use_physical_chamber_wall_flux) {
240  dflux_x->setVal(0.0);
241  dflux_y->setVal(0.0);
242  dflux_z->setVal(0.0);
243  }
244  } else {
245  dflux_x = nullptr;
246  dflux_y = nullptr;
247  dflux_z = nullptr;
248  }
249 
250  // Valid vars
251  Vector<int> is_valid_slow_var; is_valid_slow_var.resize(RhoQ1_comp+1,0);
252  if (l_use_KE) { is_valid_slow_var[ RhoKE_comp] = 1; }
253  if (l_do_scalar) { is_valid_slow_var[RhoScalar_comp] = 1; }
254  if (solverChoice.moisture_type != MoistureType::None) {
255  is_valid_slow_var[RhoQ1_comp] = 1;
256  }
257 
258  // *************************************************************************
259  // Calculate cell-centered eddy viscosity & diffusivities
260  //
261  // Notes -- we fill all the data in ghost cells before calling this so
262  // that we can fill the eddy viscosity in the ghost regions and
263  // not have to call a boundary filler on this data itself
264  //
265  // LES - updates both horizontal and vertical eddy viscosityS_tmp components
266  // PBL - only updates vertical eddy viscosity components so horizontal
267  // components come from the LES model or are left as zero.
268  // *************************************************************************
269 
270  // EB Anelastic: Copy projected momentum with ghost-cell synchronization
271  if (l_anelastic && l_use_eb) {
272  avg_xmom.ParallelCopy(S_data[IntVars::xmom], 0, 0, 1, 0, 1, geom.periodicity());
273  avg_ymom.ParallelCopy(S_data[IntVars::ymom], 0, 0, 1, 0, 1, geom.periodicity());
274  avg_zmom.ParallelCopy(S_data[IntVars::zmom], 0, 0, 1, 0, 1, geom.periodicity());
275  }
276 
277  // Non-EB Anelastic: the slow scalars are advected with the projected momentum. Copy it
278  // before the tile loop below: a copy per tile inside that loop left the faces of the tiles
279  // not yet visited at their values from the previous stage while earlier tiles were already
280  // advecting with them, so the scalars depended on the tile size.
281  if (l_anelastic && !l_use_eb) {
282  MultiFab::Copy(avg_xmom, S_data[IntVars::xmom], 0, 0, 1, 0);
283  MultiFab::Copy(avg_ymom, S_data[IntVars::ymom], 0, 0, 1, 0);
284  MultiFab::Copy(avg_zmom, S_data[IntVars::zmom], 0, 0, 1, 0);
285  }
286 
287  // M2 proof consumer: run after the stage carrier is final and before the
288  // caller copies S_data over S_new, preserving the predictor density view.
289  if (auxiliary_inert_tracer != nullptr) {
290  const auto method = !l_anelastic ? erf_auxiliary::HostIntegrator::CompressibleRK3 :
291  (solverChoice.anelastic_type[level] == AnelasticType::RK2 ?
294  auxiliary_inert_tracer->advance_stage(
295  level, method, nrk, step_old_time, input_time, target_time, dt_d,
296  S_old[IntVars::cons], S_new[IntVars::cons], S_data[IntVars::cons],
297  avg_xmom, avg_ymom, avg_zmom, solverChoice.advChoice, geom);
298  }
299 
300  // M3 advances the authoritative spectrum after the host carrier has been
301  // finalized and before the caller commits this stage's S_data into S_new.
302  // The compact qc/qr lanes are refreshed as a projection of the accepted
303  // spectrum at the same semantic target time.
304  if (sbm_active) {
305  AMREX_ALWAYS_ASSERT(sbm_state_manager != nullptr && sbm_transport != nullptr);
306  if (solverChoice.sbm_test_donor_cfl_fault) {
307  // Force an outgoing-demand violation at the production donor CFL gate.
308  avg_xmom.setVal(Real(1.0e8));
309  }
310  const auto method = !l_anelastic ? erf_auxiliary::HostIntegrator::CompressibleRK3 :
311  (solverChoice.anelastic_type[level] == AnelasticType::RK2 ?
314  const MultiFab& stage_cons_anchor = S_old[IntVars::cons];
315  const MultiFab& stage_cons_input = S_new[IntVars::cons];
316  MultiFab& stage_cons_target = S_data[IntVars::cons];
317  sbm_transport->advance_stage_from_host(
318  level, method, nrk, step_old_time, input_time, target_time, dt_d,
319  *sbm_state_manager, stage_cons_anchor, stage_cons_input,
320  stage_cons_target, avg_xmom, avg_ymom, avg_zmom, geom,
321  solverChoice.moisture_indices.qc, solverChoice.moisture_indices.qr);
322  }
323 
324  // *************************************************************************
325  // Define updates and fluxes in the current RK stage
326  // *************************************************************************
327 #ifdef _OPENMP
328 #pragma omp parallel if (Gpu::notInLaunchRegion())
329 #endif
330  {
331  std::array<FArrayBox,AMREX_SPACEDIM> flux;
332 
333  int start_comp;
334  int num_comp;
335 
336  // Cell-centered masks for EB (used for flux interpolation)
337  iMultiFab physbnd_mask;
338  bool already_on_centroids = false;
339  if (l_use_eb) {
340  physbnd_mask.define(S_data[IntVars::cons].boxArray(), S_data[IntVars::cons].DistributionMap(), 1, 1);
341  physbnd_mask.BuildMask(geom.Domain(), geom.periodicity(), 1, 1, 0, 1);
342  }
343 
344  // NOTE: this loop must NOT be tiled. It both reads and writes S_new[cons]:
345  // - the diffusion and PBL source routines below read new_cons with stencils
346  // that reach outside the box they are given (AddTurbKESources, for instance,
347  // forms a vertical theta_v gradient from k-1 and k+1);
348  // - "rhs_post_9" near the end of the loop writes new_cons = cur_cons over tbx.
349  //
350  // The better fix is to split this into two passes -- all the reads, then all the
351  // writes -- which would let tiling come back. Until then, do not restore
352  // TilingIfNotGPU() here without also moving the new_cons writes out.
353  for (MFIter mfi(S_data[IntVars::cons],false); mfi.isValid(); ++mfi)
354  {
355  Box tbx = mfi.tilebox();
356 
357  // *************************************************************************
358  // Define flux arrays for use in advection
359  // *************************************************************************
360  for (int dir = 0; dir < AMREX_SPACEDIM; ++dir) {
361  if (!l_use_eb) {
362  flux[dir].resize(surroundingNodes(tbx,dir),nvars,The_Async_Arena());
363  } else {
364  flux[dir].resize(surroundingNodes(tbx,dir).grow(1),nvars,The_Async_Arena());
365  }
366  flux[dir].setVal<RunOn::Device>(0);
367  }
368  const GpuArray<const Array4<Real>, AMREX_SPACEDIM>
369  flx_arr{{AMREX_D_DECL(flux[0].array(), flux[1].array(), flux[2].array())}};
370 
371  // *************************************************************************
372  // Define Array4's
373  // *************************************************************************
374  const Array4<const Real> & old_cons = S_old[IntVars::cons].array(mfi);
375  const Array4< Real> & cell_rhs = S_rhs[IntVars::cons].array(mfi);
376 
377  const Array4< Real> & new_cons = S_new[IntVars::cons].array(mfi);
378  const Array4< Real> & new_xmom = S_new[IntVars::xmom].array(mfi);
379  const Array4< Real> & new_ymom = S_new[IntVars::ymom].array(mfi);
380  const Array4< Real> & new_zmom = S_new[IntVars::zmom].array(mfi);
381 
382  const Array4< Real> & cur_cons = S_data[IntVars::cons].array(mfi);
383  const Array4<const Real> & cur_prim = S_prim.array(mfi);
384  const Array4< Real> & cur_xmom = S_data[IntVars::xmom].array(mfi);
385  const Array4< Real> & cur_ymom = S_data[IntVars::ymom].array(mfi);
386  const Array4< Real> & cur_zmom = S_data[IntVars::zmom].array(mfi);
387 
388  Array4<Real> avg_xmom_arr = avg_xmom.array(mfi);
389  Array4<Real> avg_ymom_arr = avg_ymom.array(mfi);
390  Array4<Real> avg_zmom_arr = avg_zmom.array(mfi);
391 
392  const Array4<const Real> & u = xvel.array(mfi);
393  const Array4<const Real> & v = yvel.array(mfi);
394  const Array4<const Real> & w = zvel.array(mfi);
395 
396  const Array4<const Real>& z_nd = z_phys_nd->const_array(mfi);
397  const Array4<const Real>& z_cc = z_phys_cc->const_array(mfi);
398  const Array4<const Real>& detJ_new_arr = l_moving_terrain ? detJ_new->const_array(mfi) : Array4<const Real>{};
399 
400  const Array4<const Real>& t_blank_arr = (terrain_blank) ? terrain_blank->const_array(mfi) :
401  Array4<const Real>{};
402  const Array4<const Real>& t_blank_xface_arr = (terrain_blank_xface) ? terrain_blank_xface->const_array(mfi) :
403  Array4<const Real>{};
404  const Array4<const Real>& t_blank_yface_arr = (terrain_blank_yface) ? terrain_blank_yface->const_array(mfi) :
405  Array4<const Real>{};
406  const Array4<const Real>& t_blank_zface_arr = (terrain_blank_zface) ? terrain_blank_zface->const_array(mfi) :
407  Array4<const Real>{};
408 
409  // Map factors
410  const Array4<const Real>& mf_mx = mapfac[MapFacType::m_x]->const_array(mfi);
411  const Array4<const Real>& mf_ux = mapfac[MapFacType::u_x]->const_array(mfi);
412  const Array4<const Real>& mf_vx = mapfac[MapFacType::v_x]->const_array(mfi);
413  const Array4<const Real>& mf_my = mapfac[MapFacType::m_y]->const_array(mfi);
414  const Array4<const Real>& mf_uy = mapfac[MapFacType::u_y]->const_array(mfi);
415  const Array4<const Real>& mf_vy = mapfac[MapFacType::v_y]->const_array(mfi);
416 
417  // SmnSmn for KE src with Deardorff or k-eqn RANS
418  const Array4<const Real>& SmnSmn_a = l_need_SmnSmn ? SmnSmn->const_array(mfi) : Array4<const Real>{};
419 
420  // **************************************************************************
421  // Here we fill the "current" data with "new" data because that is the result of the previous RK stage
422  // **************************************************************************
423  int nsv = S_old[IntVars::cons].nComp() - 2;
424  const GpuArray<int, IntVars::NumTypes> scomp_slow = { 2,0,0,0};
425  const GpuArray<int, IntVars::NumTypes> ncomp_slow = {nsv,0,0,0};
426  const bool copy_sbm_active = sbm_active;
427  const int copy_sbm_qc = solverChoice.moisture_indices.qc;
428  const int copy_sbm_qr = solverChoice.moisture_indices.qr;
429 
430  // **************************************************************************
431  // Note that here we do copy only the "slow" variables, not (rho) or (rho theta)
432  // **************************************************************************
433  ParallelFor(tbx, ncomp_slow[IntVars::cons],
434  [=] AMREX_GPU_DEVICE (int i, int j, int k, int nn) {
435  const int n = scomp_slow[IntVars::cons] + nn;
437  cur_cons, new_cons, i, j, k, n, copy_sbm_active,
438  copy_sbm_qc, copy_sbm_qr);
439  });
440 
441  // **************************************************************************
442  // Define updates in the RHS of continuity, temperature, and scalar equations
443  // **************************************************************************
444  bool l_eb_terrain_cc = false; // EB terrain on cell-centered grid
445  Array4<const int> mask_arr{};
446  Array4<const EBCellFlag> cfg_arr{};
447  Array4<const Real> ax_arr{};
448  Array4<const Real> ay_arr{};
449  Array4<const Real> az_arr{};
450  Array4<const Real> fcx_arr{};
451  Array4<const Real> fcy_arr{};
452  Array4<const Real> fcz_arr{};
453  Array4<const Real> detJ_arr{};
454  Array4<const Real> barea_arr{};
455  Array4<const Real> bcent_arr{};
456 
457  if (l_use_eb) {
458  EBFArrayBoxFactory const& ebfact_cc = *(ebfact.get_const_factory());
459  EBCellFlagFab const& cfg = ebfact_cc.getMultiEBCellFlagFab()[mfi];
460  cfg_arr = cfg.const_array();
461  if (cfg.getType(tbx) == FabType::singlevalued) {
462  l_eb_terrain_cc = true;
463  ax_arr = ebfact_cc.getAreaFrac()[0]->const_array(mfi);
464  ay_arr = ebfact_cc.getAreaFrac()[1]->const_array(mfi);
465  az_arr = ebfact_cc.getAreaFrac()[2]->const_array(mfi);
466  fcx_arr = ebfact_cc.getFaceCent()[0]->const_array(mfi);
467  fcy_arr = ebfact_cc.getFaceCent()[1]->const_array(mfi);
468  fcz_arr = ebfact_cc.getFaceCent()[2]->const_array(mfi);
469  detJ_arr = ebfact_cc.getVolFrac().const_array(mfi);
470  mask_arr = physbnd_mask.const_array(mfi);
471  barea_arr = ebfact_cc.getBndryArea().const_array(mfi);
472  bcent_arr = ebfact_cc.getBndryCent().const_array(mfi);
473  } else {
474  ax_arr = ax->const_array(mfi);
475  ay_arr = ay->const_array(mfi);
476  az_arr = az->const_array(mfi);
477  detJ_arr = detJ->const_array(mfi);
478  }
479  } else {
480  ax_arr = ax->const_array(mfi);
481  ay_arr = ay->const_array(mfi);
482  az_arr = az->const_array(mfi);
483  detJ_arr = detJ->const_array(mfi);
484  }
485 
486  AdvType horiz_adv_type, vert_adv_type;
487  Real horiz_upw_frac, vert_upw_frac;
488 
489  Array4<Real> diffflux_x, diffflux_y, diffflux_z;
490  Array4<Real> hfx_x, hfx_y, hfx_z, diss;
491  Array4<Real> q1fx_x, q1fx_y, q1fx_z, q2fx_z;
492  Array4<Real> hfx_EB{};
493 
494  // The surface-layer stresses on the zlo face, consumed by the MYNN QKE
495  // source at k = klo (see ERF_TurbKESources.H)
496  Array4<const Real> tau13, tau23;
497 
498  if (l_use_diff) {
499  if (Tau13) { tau13 = Tau13->const_array(mfi); }
500  if (Tau23) { tau23 = Tau23->const_array(mfi); }
501  diffflux_x = dflux_x->array(mfi);
502  diffflux_y = dflux_y->array(mfi);
503  diffflux_z = dflux_z->array(mfi);
504 
505  hfx_x = Hfx1->array(mfi);
506  hfx_y = Hfx2->array(mfi);
507  hfx_z = Hfx3->array(mfi);
508  diss = Diss->array(mfi);
509 
510  if (Q1fx1) q1fx_x = Q1fx1->array(mfi);
511  if (Q1fx2) q1fx_y = Q1fx2->array(mfi);
512  if (Q1fx3) q1fx_z = Q1fx3->array(mfi);
513  if (Q2fx3) q2fx_z = Q2fx3->array(mfi);
514  }
515 
516  if (l_use_diff && l_use_turb) {
518  eddyDiffs != nullptr,
519  "erf_slow_rhs_post: active turbulence requires non-null eddyDiffs");
520  }
521  const Array4<const Real>& mu_turb =
522  l_use_turb ? eddyDiffs->const_array(mfi) : Array4<const Real>{};
523 
524  //
525  // Note that we either advect and diffuse all or none of the moisture variables
526  //
527  for (int ivar(RhoKE_comp); ivar<= RhoQ1_comp; ++ivar)
528  {
529 
530  if (is_valid_slow_var[ivar])
531  {
532  start_comp = ivar;
533  num_comp = 1;
534 
535  if (ivar == RhoQ1_comp) {
536  horiz_adv_type = ac.moistscal_horiz_adv_type;
537  vert_adv_type = ac.moistscal_vert_adv_type;
538  horiz_upw_frac = ac.moistscal_horiz_upw_frac;
539  vert_upw_frac = ac.moistscal_vert_upw_frac;
540 
541  if (ac.use_efficient_advection){
542  horiz_adv_type = EfficientAdvType(nrk,ac.moistscal_horiz_adv_type);
543  vert_adv_type = EfficientAdvType(nrk,ac.moistscal_vert_adv_type);
544  }
545 
546  // Every state component from RhoQ1_comp to the end of the state, not
547  // just the n_qstate water species: a microphysics model may append
548  // non-water species after those (SuperDropletsMoist does), and they
549  // are advanced by the state update below and included in the reflux.
550  // Computing residuals for only the first n_qstate would leave the
551  // rest to be updated with a residual nothing ever wrote.
552  num_comp = n_qstate_total;
553  if (sbm_active) {
554  // Keep vapor on the normal path. The test fault widens
555  // the actual range so the ownership guard is exercised
556  // before native advection can write projected liquid.
557  num_comp = solverChoice.sbm_test_native_qc_write_fault ? 2 : 1;
558  }
559 
560  } else {
561  horiz_adv_type = ac.dryscal_horiz_adv_type;
562  vert_adv_type = ac.dryscal_vert_adv_type;
563  horiz_upw_frac = ac.dryscal_horiz_upw_frac;
564  vert_upw_frac = ac.dryscal_vert_upw_frac;
565 
566  if (ac.use_efficient_advection){
567  horiz_adv_type = EfficientAdvType(nrk,ac.dryscal_horiz_adv_type);
568  vert_adv_type = EfficientAdvType(nrk,ac.dryscal_vert_adv_type);
569  }
570 
571  if (ivar == RhoScalar_comp) {
572  num_comp = NSCALARS;
573  }
574  }
575 
576  if (( ivar != RhoKE_comp ) ||
577  ((ivar == RhoKE_comp) && l_advect_KE))
578  {
579  if (!l_eb_terrain_cc){
580  if (sbm_active && ivar == RhoQ1_comp) {
582  true, start_comp, num_comp, erf_sbm::HostWritePath::Advection);
583  }
584  AdvectionSrcForScalars(tbx, start_comp, num_comp,
585  avg_xmom_arr, avg_ymom_arr, avg_zmom_arr,
586  cur_prim, cell_rhs,
587  detJ_arr, dxInv, mf_mx, mf_my,
588  horiz_adv_type, vert_adv_type,
589  horiz_upw_frac, vert_upw_frac,
590  flx_arr, domain, bc_ptr_h);
591  } else {
592  if (sbm_active && ivar == RhoQ1_comp) {
594  true, start_comp, num_comp, erf_sbm::HostWritePath::Advection);
595  }
596  EBAdvectionSrcForScalars(tbx, start_comp, num_comp,
597  avg_xmom_arr, avg_ymom_arr, avg_zmom_arr,
598  cur_prim, cell_rhs,
599  mask_arr, cfg_arr, ax_arr, ay_arr, az_arr,
600  fcx_arr, fcy_arr, fcz_arr,
601  detJ_arr, dxInv, mf_mx, mf_my,
602  horiz_adv_type, vert_adv_type,
603  horiz_upw_frac, vert_upw_frac,
604  flx_arr, domain, bc_ptr_h,
605  already_on_centroids);
606  }
607  }
608 
609  if (l_use_diff)
610  {
611  // Allow for implicit moisture diffusion
613  if ( (ivar == RhoKE_comp && solverChoice.implicit_ke_diffusion ) ||
614  (ivar == RhoQ1_comp && solverChoice.implicit_moisture_diffusion) ) {
615  l_vert_implicit_fac = solverChoice.vert_implicit_fac[level][nrk];
616  }
617 
618  // Only the physical chamber needs separate qv/qc calls:
619  // its wall correction must be applied to distinct flux
620  // components. Generic moisture models retain the
621  // established multi-component diffusion call.
622  const bool componentwise_moisture =
623  use_physical_chamber_wall_flux && ivar == RhoQ1_comp;
624  const int n_diff_calls = componentwise_moisture ? n_qstate_total : 1;
625  for (int qstate = 0; qstate < n_diff_calls; ++qstate) {
626  const int state_comp = componentwise_moisture ?
627  RhoQ1_comp + qstate : start_comp;
628  const int diffusion_start = state_comp;
629  const int diffusion_num = componentwise_moisture ? 1 : num_comp;
631  sbm_active, diffusion_start, diffusion_num,
633  const int flux_comp = componentwise_moisture ? qstate : 0;
634  AMREX_ALWAYS_ASSERT(state_comp >= 0 && state_comp < nvars);
635  AMREX_ALWAYS_ASSERT(flux_comp < dflux_x->nComp());
636  AMREX_ALWAYS_ASSERT(flux_comp < dflux_y->nComp());
637  AMREX_ALWAYS_ASSERT(flux_comp < dflux_z->nComp());
638  const Array4<Real> diffusion_x = dflux_x->array(mfi, flux_comp);
639  const Array4<Real> diffusion_y = dflux_y->array(mfi, flux_comp);
640  const Array4<Real> diffusion_z = dflux_z->array(mfi, flux_comp);
641 
642  if (solverChoice.mesh_type == MeshType::StretchedDz) {
643  DiffusionSrcForState_S(tbx, domain, diffusion_start, diffusion_num,
644  new_cons, cur_prim, cell_rhs,
645  diffusion_x, diffusion_y, diffusion_z,
646  stretched_dz_d, dxInv,
647  mf_mx, mf_ux, mf_vx,
648  mf_my, mf_uy, mf_vy,
649  hfx_x, hfx_y, hfx_z, q1fx_x, q1fx_y, q1fx_z,q2fx_z,
650  mu_turb, solverChoice, level,
651  bc_ptr_d,
652  l_apply_surface_layer_fluxes_in_diffusion, SurfLayer, l_vert_implicit_fac);
653  } else if (l_use_terrain) {
654  DiffusionSrcForState_T(tbx, domain, diffusion_start, diffusion_num, l_rotate,
655  new_cons, cur_prim, cell_rhs,
656  diffusion_x, diffusion_y, diffusion_z,
657  z_nd, z_cc, ax_arr, ay_arr, az_arr,
658  detJ_arr, dxInv,
659  mf_mx, mf_ux, mf_vx,
660  mf_my, mf_uy, mf_vy,
661  hfx_x, hfx_y, hfx_z, q1fx_x, q1fx_y, q1fx_z,q2fx_z,
662  mu_turb, solverChoice, level,
663  bc_ptr_d,
664  l_apply_surface_layer_fluxes_in_diffusion, SurfLayer, l_vert_implicit_fac);
665  } else if (l_use_eb) {
666  DiffusionSrcForState_EB(tbx, domain, diffusion_start, diffusion_num, u, v,
667  new_cons, cur_prim, cell_rhs,
668  diffusion_x, diffusion_y, diffusion_z,
669  cfg_arr, ax_arr, ay_arr, az_arr, detJ_arr,
670  barea_arr, bcent_arr,
671  dx, dxInv,
672  hfx_z, q1fx_z, q2fx_z, hfx_EB,
673  mu_turb, solverChoice, level,
674  bc_ptr_d, l_apply_surface_layer_fluxes_in_diffusion, SurfLayer);
675  } else {
676  DiffusionSrcForState_N(tbx, domain, diffusion_start, diffusion_num, u, v,
677  new_cons, cur_prim, cell_rhs,
678  diffusion_x, diffusion_y, diffusion_z, dxInv,
679  mf_mx, mf_ux, mf_vx,
680  mf_my, mf_uy, mf_vy,
681  hfx_x, hfx_y, hfx_z, q1fx_x, q1fx_y, q1fx_z, q2fx_z,
682  mu_turb, solverChoice, level,
683  bc_ptr_d,
684  l_apply_surface_layer_fluxes_in_diffusion, SurfLayer, l_vert_implicit_fac);
685  }
686  if (use_physical_chamber_wall_flux) {
687  // Apply the physical wall correction immediately to
688  // the flux component just computed. This keeps the
689  // q-state diffusion path identical with budgets on
690  // and off and guarantees no stale flux is consumed.
691  // The diffusion views are component-shifted; the
692  // wall helper receives the unshifted views and the
693  // explicit flux component index.
695  tbx, domain, state_comp, flux_comp, new_cons, cur_prim,
696  cloud_chamber_base_state->const_array(mfi), u, v, w, cell_rhs,
697  diffflux_x, diffflux_y, diffflux_z, dxInv,
698  chamber_walls, dc.alpha_T, dc.alpha_C,
699  solverChoice.rdOcp, cloud_chamber_config->cloudy);
700  }
701  }
702  } // use_diff
703  } // valid slow var
704  } // loop ivar
705 
706  // **************************************************************************
707  // TKE / QKE production and dissipation. These consume the face fluxes of
708  // this stage's scalar diffusion (theta in erf_slow_rhs_pre, qv above), so
709  // they are added only once every scalar has been diffused.
710  // **************************************************************************
711  if (l_use_diff && is_valid_slow_var[RhoKE_comp])
712  {
713  const Array4<const Real> tm_arr = t_mean_mf ? t_mean_mf->const_array(mfi) : Array4<const Real>{};
714 
715  if (solverChoice.mesh_type == MeshType::StretchedDz) {
716  const PBLDerivativeDzInv_S pbl_dz_inv{stretched_dz_d.data(),
717  domain.smallEnd(2), domain.bigEnd(2)};
718  AddTurbKESources(tbx, domain, u, v, new_cons, cur_prim, cell_rhs,
719  SmnSmn_a, hfx_z, q1fx_z, diss, tau13, tau23, mu_turb,
720  solverChoice, level, tm_arr, grav_gpu, bc_ptr_d,
721  l_apply_surface_layer_fluxes_in_diffusion, SurfLayer, pbl_dz_inv);
722  } else if (l_use_terrain) {
723  const PBLDerivativeDzInv_T pbl_dz_inv{z_cc};
724  AddTurbKESources(tbx, domain, u, v, new_cons, cur_prim, cell_rhs,
725  SmnSmn_a, hfx_z, q1fx_z, diss, tau13, tau23, mu_turb,
726  solverChoice, level, tm_arr, grav_gpu, bc_ptr_d,
727  l_apply_surface_layer_fluxes_in_diffusion, SurfLayer, pbl_dz_inv);
728  } else if (!l_use_eb) {
729  const PBLDerivativeDzInv_N pbl_dz_inv{dxInv[2]};
730  AddTurbKESources(tbx, domain, u, v, new_cons, cur_prim, cell_rhs,
731  SmnSmn_a, hfx_z, q1fx_z, diss, tau13, tau23, mu_turb,
732  solverChoice, level, tm_arr, grav_gpu, bc_ptr_d,
733  l_apply_surface_layer_fluxes_in_diffusion, SurfLayer, pbl_dz_inv);
734  }
735  }
736 
737 #ifdef ERF_USE_EAMXX_SHOC
738  if (tc.uses_eamxx_shoc() && eamxx_shoc_lev) {
739  eamxx_shoc_lev->add_slow_tend(mfi,tbx,cell_rhs);
740  }
741 #endif
742  // This updates just the "slow" conserved variables
743  {
744  BL_PROFILE("rhs_post_8");
745 
747 
748  auto const& src_arr = source.const_array(mfi);
749 
750  for (int ivar(RhoKE_comp); ivar<= RhoQ1_comp; ++ivar)
751  {
752  if (is_valid_slow_var[ivar])
753  {
754  start_comp = ivar;
755  num_comp = 1;
756  if (ivar == RhoQ1_comp) {
757  num_comp = n_qstate_total;
758  if (sbm_active) {
759  // The host update applies source terms (positivity
760  // clipping is applied afterward in ERF_ScalarLimitPost.H
761  // with the same restriction). Keep both projected liquid lanes out of
762  // that write path; they are refreshed from the spectrum
763  // after the no-op microphysics handoff.
764  num_comp = 1;
765  }
766  } else if (ivar == RhoScalar_comp) {
767  num_comp = NSCALARS;
768  }
769 
770  if (l_moving_terrain)
771  {
773  sbm_active, start_comp, num_comp, erf_sbm::HostWritePath::Positivity);
774  ParallelFor(tbx, num_comp,
775  [=] AMREX_GPU_DEVICE (int i, int j, int k, int nn) noexcept {
776  const int n = start_comp + nn;
777  cell_rhs(i,j,k,n) += src_arr(i,j,k,n);
778  Real temp_val = detJ_arr(i,j,k) * old_cons(i,j,k,n) + dt * detJ_arr(i,j,k) * cell_rhs(i,j,k,n);
779  cur_cons(i,j,k,n) = temp_val / detJ_new_arr(i,j,k);
780  if (ivar == RhoKE_comp && l_implicit_diss) {
781  cur_cons(i,j,k,n) /= (one + dt * diss(i,j,k) / amrex::max(old_cons(i,j,k,n), eps));
782  }
783  });
784 
785  } else if (l_anelastic && l_anelastic_rk2 && (nrk == 1)) { // not moving and ( (anelastic) and second RK stage) )
786 
788  sbm_active, start_comp, num_comp, erf_sbm::HostWritePath::Positivity);
789  ParallelFor(tbx, num_comp,
790  [=] AMREX_GPU_DEVICE (int i, int j, int k, int nn) noexcept {
791  const int n = start_comp + nn;
792  cell_rhs(i,j,k,n) += src_arr(i,j,k,n);
793 
794  // Re-construct the cell_rhs used in the first RK stage
795  Real dt_times_old_cell_rhs = cur_cons(i,j,k,n) - old_cons(i,j,k,n);
796 
797  // Add the time-averaged RHS to the old state
798  cur_cons(i,j,k,n) = old_cons(i,j,k,n) + myhalf * (dt_times_old_cell_rhs + dt * cell_rhs(i,j,k,n));
799 
800  if (ivar == RhoKE_comp && l_implicit_diss) {
801  // stage 1 of the trapezoidal update: half the step is implicit
802  cur_cons(i,j,k,n) /= (one + myhalf * dt * diss(i,j,k) / amrex::max(old_cons(i,j,k,n), eps));
803  }
804  });
805 
806  } else { // not moving and ( (not anelastic) or (first RK stage) )
807 
809  sbm_active, start_comp, num_comp, erf_sbm::HostWritePath::Positivity);
810  ParallelFor(tbx, num_comp,
811  [=] AMREX_GPU_DEVICE (int i, int j, int k, int nn) noexcept {
812  const int n = start_comp + nn;
813  cell_rhs(i,j,k,n) += src_arr(i,j,k,n);
814  cur_cons(i,j,k,n) = old_cons(i,j,k,n) + dt * cell_rhs(i,j,k,n);
815  if (ivar == RhoKE_comp && l_implicit_diss) {
816  cur_cons(i,j,k,n) /= (one + dt * diss(i,j,k) / amrex::max(old_cons(i,j,k,n), eps));
817  }
818  });
819 
820  } // moving, anelastic or neither?
821 
822  } // is_valid
823  } // ivar
824 
825  // Re-impose the Dirichlet wall value of k (first cell above the wall)
826  if (l_dirichlet_k && is_valid_slow_var[RhoKE_comp]) {
827  const int klo = domain.smallEnd(2);
828  if (tbx.smallEnd(2) <= klo && tbx.bigEnd(2) >= klo) {
829  ParallelFor(makeSlab(tbx,2,klo), [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept {
830  // Hold k, not rho*k. Eq. 16 defines the primitive wall value, and
831  // cur_cons(Rho_comp) has already been updated for this stage (and,
832  // with moving terrain, rescaled by detJ/detJ_new along with RhoKE),
833  // so copying the conserved variable straight across would let the
834  // wall value drift by the first-cell density change every step.
835  // Both states carry the same detJ convention, so the ratio is exact.
836  cur_cons(i,j,k,RhoKE_comp) = cur_cons(i,j,k,Rho_comp) *
837  ( old_cons(i,j,k,RhoKE_comp) / old_cons(i,j,k,Rho_comp) );
838  });
839  }
840  }
841  } // profile
842 
843  {
844  BL_PROFILE("rhs_post_9");
845  // This updates all the conserved variables (not just the "slow" ones)
846  int num_comp_all = S_data[IntVars::cons].nComp();
847  ParallelFor(tbx, num_comp_all,
848  [=] AMREX_GPU_DEVICE (int i, int j, int k, int n) noexcept {
849  new_cons(i,j,k,n) = cur_cons(i,j,k,n);
850  });
851  } // end profile
852 
853  Box xtbx = mfi.nodaltilebox(0);
854  Box ytbx = mfi.nodaltilebox(1);
855  Box ztbx = mfi.nodaltilebox(2);
856 
857  {
858  BL_PROFILE("rhs_post_10()");
859  if (l_anelastic && terrain_blank) { // explicitly set fully immersed cells to have 0 velocities for anelastic (unstable for fully compressible).
860  ParallelFor(xtbx, ytbx, ztbx,
861  [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept {
862  // Use face-centered terrain_blanking if available, otherwise average from cell centers
863  Real t_blank = (t_blank_xface_arr) ? t_blank_xface_arr(i, j, k) :
864  myhalf * (t_blank_arr(i, j, k) + t_blank_arr(i-1, j, k));
865  if (t_blank == one) {
866  new_xmom(i,j,k) = zero;
867  } else {
868  new_xmom(i,j,k) = cur_xmom(i,j,k);
869  }
870  },
871  [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept {
872  // Use face-centered terrain_blanking if available, otherwise average from cell centers
873  Real t_blank = (t_blank_yface_arr) ? t_blank_yface_arr(i, j, k) :
874  myhalf * (t_blank_arr(i, j, k) + t_blank_arr(i, j-1, k));
875  if (t_blank == one) {
876  new_ymom(i,j,k) = zero;
877  } else {
878  new_ymom(i,j,k) = cur_ymom(i,j,k);
879  }
880  },
881  [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept {
882  // Use face-centered terrain_blanking if available, otherwise average from cell centers
883  Real t_blank = (t_blank_zface_arr) ? t_blank_zface_arr(i, j, k) :
884  myhalf * (t_blank_arr(i, j, k) + t_blank_arr(i, j, k-1));
885  if (t_blank == one) {
886  new_zmom(i,j,k) = zero;
887  } else {
888  new_zmom(i,j,k) = cur_zmom(i,j,k);
889  }
890  });
891  ParallelFor(tbx,
892  [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept {
893  Real t_blank = t_blank_arr(i, j, k );
894  if (t_blank == one) { // don't update rho and theta for fully immersed cells
895  new_cons(i, j, k, Rho_comp) = old_cons(i, j, k, Rho_comp);
896  new_cons(i, j, k, RhoTheta_comp) = old_cons(i, j, k, RhoTheta_comp);
897  }
898  });
899  } else {
900  ParallelFor(xtbx, ytbx, ztbx,
901  [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept {
902  new_xmom(i,j,k) = cur_xmom(i,j,k);
903  },
904  [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept {
905  new_ymom(i,j,k) = cur_ymom(i,j,k);
906  },
907  [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept {
908  new_zmom(i,j,k) = cur_zmom(i,j,k);
909  });
910  }
911  } // end profile
912 
913  {
914  BL_PROFILE("rhs_post_10");
915  // We only add to the flux registers in the final RK step
916  if (l_reflux) {
917  int strt_comp_reflux = RhoTheta_comp + 1;
918  int num_comp_reflux = nvars - strt_comp_reflux;
919  if (level < finest_level) {
920  fr_as_crse->CrseAdd(mfi,
921  {{AMREX_D_DECL(&(flux[0]), &(flux[1]), &(flux[2]))}},
922  dx, dt, strt_comp_reflux, strt_comp_reflux, num_comp_reflux, RunOn::Device);
923  }
924  if (level > 0) {
925  fr_as_fine->FineAdd(mfi,
926  {{AMREX_D_DECL(&(flux[0]), &(flux[1]), &(flux[2]))}},
927  dx, dt, strt_comp_reflux, strt_comp_reflux, num_comp_reflux, RunOn::Device);
928  }
929 
930  // This is necessary here so we don't go on to the next FArrayBox without
931  // having finished copying the fluxes into the FluxRegisters (since the fluxes
932  // are stored in temporary FArrayBox's)
933  Gpu::streamSynchronize();
934 
935  } // two-way coupling
936  } // end profile
937  } // mfi
938  } // OMP
939  if (cloud_budget && l_use_diff && n_qstate > 0) {
940  bool use_trapezoidal = (!l_anelastic || l_anelastic_rk2);
941  for (int qstate = 0; qstate < n_qstate; ++qstate) {
942  MultiFab qflux_x(*dflux_x, make_alias, qstate, 1);
943  MultiFab qflux_y(*dflux_y, make_alias, qstate, 1);
944  MultiFab qflux_z(*dflux_z, make_alias, qstate, 1);
945  cloud_budget->capture_stage(
947  nrk, static_cast<Real>(dt_d), qflux_x, qflux_y, qflux_z, geom,
948  0, use_trapezoidal);
949  }
950  }
951 }
void AdvectionSrcForScalars(const amrex::Box &bx, const int icomp, const int ncomp, const amrex::Array4< const amrex::Real > &avg_xmom, const amrex::Array4< const amrex::Real > &avg_ymom, const amrex::Array4< const amrex::Real > &avg_zmom, const amrex::Array4< const amrex::Real > &cell_prim, const amrex::Array4< amrex::Real > &src, const amrex::Array4< const amrex::Real > &vf_arr, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &cellSizeInv, const amrex::Array4< const amrex::Real > &mf_mx, const amrex::Array4< const amrex::Real > &mf_my, const AdvType horiz_adv_type, const AdvType vert_adv_type, const amrex::Real horiz_upw_frac, const amrex::Real vert_upw_frac, const amrex::GpuArray< const amrex::Array4< amrex::Real >, AMREX_SPACEDIM > &flx_arr, const amrex::Box &domain, const amrex::BCRec *bc_ptr_h)
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE AdvType EfficientAdvType(int nrk, AdvType adv_type)
Definition: ERF_Advection.H:467
@ tau23
Definition: ERF_DataStruct.H:48
@ tau13
Definition: ERF_DataStruct.H:48
@ nvars
Definition: ERF_DataStruct.H:194
@ v_x
Definition: ERF_DataStruct.H:37
@ u_y
Definition: ERF_DataStruct.H:38
@ v_y
Definition: ERF_DataStruct.H:38
@ m_y
Definition: ERF_DataStruct.H:38
@ u_x
Definition: ERF_DataStruct.H:37
@ m_x
Definition: ERF_DataStruct.H:37
void DiffusionSrcForState_S(const amrex::Box &bx, const amrex::Box &domain, int start_comp, int num_comp, const amrex::Array4< const amrex::Real > &cell_data, const amrex::Array4< const amrex::Real > &cell_prim, const amrex::Array4< amrex::Real > &cell_rhs, const amrex::Array4< amrex::Real > &xflux, const amrex::Array4< amrex::Real > &yflux, const amrex::Array4< amrex::Real > &zflux, const amrex::Gpu::DeviceVector< amrex::Real > &stretched_dz_d, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &dxInv, const amrex::Array4< const amrex::Real > &mf_mx, const amrex::Array4< const amrex::Real > &mf_ux, const amrex::Array4< const amrex::Real > &mf_vx, const amrex::Array4< const amrex::Real > &mf_my, const amrex::Array4< const amrex::Real > &mf_uy, const amrex::Array4< const amrex::Real > &mf_vy, amrex::Array4< amrex::Real > &hfx_x, amrex::Array4< amrex::Real > &hfx_y, amrex::Array4< amrex::Real > &hfx_z, amrex::Array4< amrex::Real > &qfx1_x, amrex::Array4< amrex::Real > &qfx1_y, amrex::Array4< amrex::Real > &qfx1_z, amrex::Array4< amrex::Real > &qfx2_z, const amrex::Array4< const amrex::Real > &mu_turb, const SolverChoice &solverChoice, const int level, const amrex::BCRec *bc_ptr, const bool use_SurfLayer, const amrex::Vector< std::unique_ptr< SurfaceLayer >> &SurfLayer, const amrex::Real implicit_fac)
Native adapter for regular scalar diffusion on a vertically stretched grid.
void DiffusionSrcForState_N(const amrex::Box &bx, const amrex::Box &domain, int start_comp, int num_comp, const amrex::Array4< const amrex::Real > &u, const amrex::Array4< const amrex::Real > &v, const amrex::Array4< const amrex::Real > &cell_data, const amrex::Array4< const amrex::Real > &cell_prim, const amrex::Array4< amrex::Real > &cell_rhs, const amrex::Array4< amrex::Real > &xflux, const amrex::Array4< amrex::Real > &yflux, const amrex::Array4< amrex::Real > &zflux, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &cellSizeInv, const amrex::Array4< const amrex::Real > &mf_mx, const amrex::Array4< const amrex::Real > &mf_ux, const amrex::Array4< const amrex::Real > &mf_vx, const amrex::Array4< const amrex::Real > &mf_my, const amrex::Array4< const amrex::Real > &mf_uy, const amrex::Array4< const amrex::Real > &mf_vy, amrex::Array4< amrex::Real > &hfx_x, amrex::Array4< amrex::Real > &hfx_y, amrex::Array4< amrex::Real > &hfx_z, amrex::Array4< amrex::Real > &qfx1_x, amrex::Array4< amrex::Real > &qfx1_y, amrex::Array4< amrex::Real > &qfx1_z, amrex::Array4< amrex::Real > &qfx2_z, const amrex::Array4< const amrex::Real > &mu_turb, const SolverChoice &solverChoice, const int level, const amrex::BCRec *bc_ptr, const bool use_SurfLayer, const amrex::Vector< std::unique_ptr< SurfaceLayer >> &SurfLayer, const amrex::Real implicit_fac)
Add regular-grid state diffusion on a uniform grid without terrain.
void DiffusionSrcForState_EB(const amrex::Box &bx, const amrex::Box &domain, int start_comp, int num_comp, const amrex::Array4< const amrex::Real > &u, const amrex::Array4< const amrex::Real > &v, const amrex::Array4< const amrex::Real > &cell_data, const amrex::Array4< const amrex::Real > &cell_prim, const amrex::Array4< amrex::Real > &cell_rhs, const amrex::Array4< amrex::Real > &xflux, const amrex::Array4< amrex::Real > &yflux, const amrex::Array4< amrex::Real > &zflux, const amrex::Array4< const amrex::EBCellFlag > &cfg_arr, const amrex::Array4< const amrex::Real > &ax_arr, const amrex::Array4< const amrex::Real > &ay_arr, const amrex::Array4< const amrex::Real > &az_arr, const amrex::Array4< const amrex::Real > &detJ, const amrex::Array4< const amrex::Real > &barea_arr, const amrex::Array4< const amrex::Real > &bcent_arr, const amrex::Real *dx_arr, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &cellSizeInv, amrex::Array4< amrex::Real > &hfx_z, amrex::Array4< amrex::Real > &qfx1_z, amrex::Array4< amrex::Real > &qfx2_z, amrex::Array4< amrex::Real > &hfx_EB, const amrex::Array4< const amrex::Real > &mu_turb, const SolverChoice &solverChoice, const int level, const amrex::BCRec *bc_ptr, const bool use_SurfLayer, const amrex::Vector< std::unique_ptr< SurfaceLayer >> &SurfLayer)
Add state diffusion source terms for embedded-boundary cells.
void DiffusionSrcForState_T(const amrex::Box &bx, const amrex::Box &domain, int start_comp, int num_comp, const bool &rotate, const amrex::Array4< const amrex::Real > &cell_data, const amrex::Array4< const amrex::Real > &cell_prim, const amrex::Array4< amrex::Real > &cell_rhs, const amrex::Array4< amrex::Real > &xflux, const amrex::Array4< amrex::Real > &yflux, const amrex::Array4< amrex::Real > &zflux, const amrex::Array4< const amrex::Real > &z_nd, const amrex::Array4< const amrex::Real > &z_cc, const amrex::Array4< const amrex::Real > &ax, const amrex::Array4< const amrex::Real > &ay, const amrex::Array4< const amrex::Real > &az, const amrex::Array4< const amrex::Real > &detJ, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &dxInv, const amrex::Array4< const amrex::Real > &mf_mx, const amrex::Array4< const amrex::Real > &mf_ux, const amrex::Array4< const amrex::Real > &mf_vx, const amrex::Array4< const amrex::Real > &mf_my, const amrex::Array4< const amrex::Real > &mf_uy, const amrex::Array4< const amrex::Real > &mf_vy, amrex::Array4< amrex::Real > &hfx_x, amrex::Array4< amrex::Real > &hfx_y, amrex::Array4< amrex::Real > &hfx_z, amrex::Array4< amrex::Real > &qfx1_x, amrex::Array4< amrex::Real > &qfx1_y, amrex::Array4< amrex::Real > &qfx1_z, amrex::Array4< amrex::Real > &qfx2_z, const amrex::Array4< const amrex::Real > &mu_turb, const SolverChoice &solverChoice, const int level, const amrex::BCRec *bc_ptr, const bool use_SurfLayer, const amrex::Vector< std::unique_ptr< SurfaceLayer >> &SurfLayer, const amrex::Real implicit_fac)
Native adapter for regular scalar diffusion on terrain-following coordinates.
void EBAdvectionSrcForScalars(const amrex::Box &bx, const int icomp, const int ncomp, const amrex::Array4< const amrex::Real > &avg_xmom, const amrex::Array4< const amrex::Real > &avg_ymom, const amrex::Array4< const amrex::Real > &avg_zmom, const amrex::Array4< const amrex::Real > &cell_prim, const amrex::Array4< amrex::Real > &src, const amrex::Array4< const int > &mask_arr, const amrex::Array4< const amrex::EBCellFlag > &cfg_arr, const amrex::Array4< const amrex::Real > &ax_arr, const amrex::Array4< const amrex::Real > &ay_arr, const amrex::Array4< const amrex::Real > &az_arr, const amrex::Array4< const amrex::Real > &fcx_arr, const amrex::Array4< const amrex::Real > &fcy_arr, const amrex::Array4< const amrex::Real > &fcz_arr, const amrex::Array4< const amrex::Real > &vf_arr, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &cellSizeInv, const amrex::Array4< const amrex::Real > &mf_mx, const amrex::Array4< const amrex::Real > &mf_my, const AdvType horiz_adv_type, const AdvType vert_adv_type, const amrex::Real horiz_upw_frac, const amrex::Real vert_upw_frac, const amrex::GpuArray< const amrex::Array4< amrex::Real >, AMREX_SPACEDIM > &flx_arr, const amrex::Box &domain, const amrex::BCRec *bc_ptr_h, bool already_on_centroids)
Compute EB advection tendency for scalars other than density.
const Real l_vert_implicit_fac
Definition: ERF_ImplicitPost.H:6
#define RhoScalar_comp
Definition: ERF_IndexDefines.H:46
#define Rho_comp
Definition: ERF_IndexDefines.H:42
#define RhoTheta_comp
Definition: ERF_IndexDefines.H:43
#define NSCALARS
Definition: ERF_IndexDefines.H:16
#define RhoQ1_comp
Definition: ERF_IndexDefines.H:48
AdvType
Definition: ERF_IndexDefines.H:312
#define RhoKE_comp
Definition: ERF_IndexDefines.H:44
amrex::GpuArray< Real, AMREX_SPACEDIM > dxInv
Definition: ERF_InitCustomPertVels_ParticleTests.H:17
const int klo
Definition: ERF_InitCustomPert_ABL.H:75
const Real dx
Definition: ERF_InitCustomPert_ABL.H:44
AMREX_ALWAYS_ASSERT(bx.length()[2]==khi+1)
AMREX_ALWAYS_ASSERT_WITH_MESSAGE(m_cloud_chamber_config.active, "Cloud Chamber: initializer reached without a parsed configuration")
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);})
constexpr amrex::Real one
Definition: ERF_NumericalConstants.H:37
constexpr amrex::Real zero
Definition: ERF_NumericalConstants.H:36
constexpr amrex::Real myhalf
Definition: ERF_NumericalConstants.H:41
Real w
Definition: ERF_Plotfile2DInterpolator.cpp:22
const bool l_dirichlet_k
Definition: ERF_ScalarLimitPost.H:20
amrex::Real Real
Definition: ERF_ShocInterface.H:19
void AddTurbKESources(const amrex::Box &bx, const amrex::Box &domain, const amrex::Array4< const amrex::Real > &u, const amrex::Array4< const amrex::Real > &v, const amrex::Array4< const amrex::Real > &cell_data, const amrex::Array4< const amrex::Real > &cell_prim, const amrex::Array4< amrex::Real > &cell_rhs, const amrex::Array4< const amrex::Real > &SmnSmn_a, const amrex::Array4< const amrex::Real > &hfx_z, const amrex::Array4< const amrex::Real > &qfx1_z, const amrex::Array4< const amrex::Real > &diss, const amrex::Array4< const amrex::Real > &tau13, const amrex::Array4< const amrex::Real > &tau23, const amrex::Array4< const amrex::Real > &mu_turb, const SolverChoice &solverChoice, const int level, const amrex::Array4< const amrex::Real > &tm_arr, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > grav_gpu, const amrex::BCRec *bc_ptr, const bool use_SurfLayer, const amrex::Vector< std::unique_ptr< SurfaceLayer >> &SurfLayer, const PBLDerivativeDzInvFunctor &pbl_derivative_dz_inv)
Definition: ERF_TurbKESources.H:47
void capture_stage(int scalar, int nrk, amrex::Real dt, const amrex::MultiFab &xflux, const amrex::MultiFab &yflux, const amrex::MultiFab &zflux, const amrex::Geometry &geom, int flux_comp=0, bool trapezoidal=true)
Definition: ERF_CloudChamberBudget.H:94
@ RhoQv
Definition: ERF_CloudChamberBudget.H:33
@ RhoQc
Definition: ERF_CloudChamberBudget.H:33
const std::unique_ptr< amrex::EBFArrayBoxFactory > & get_const_factory() const noexcept
Return the cell-centered EB factory.
Definition: ERF_EB.H:102
void advance_stage(int level, HostIntegrator method, int stage, double step_old_time, double input_time, double target_time, double host_stage_interval, const amrex::MultiFab &conserved_anchor, const amrex::MultiFab &conserved_input, const amrex::MultiFab &conserved_target, const amrex::MultiFab &avg_xmom, const amrex::MultiFab &avg_ymom, const amrex::MultiFab &avg_zmom, const AdvChoice &advection, const amrex::Geometry &geometry)
Definition: ERF_AuxiliaryInertTracer.cpp:136
void advance_stage_from_host(int level, erf_auxiliary::HostIntegrator method, int stage, double step_old_time, double input_time, double target_time, double host_stage_interval, SBMStateManager &state_manager, const amrex::MultiFab &S_old_cons, const amrex::MultiFab &S_new_cons, amrex::MultiFab &S_data_cons, const amrex::MultiFab &avg_xmom, const amrex::MultiFab &avg_ymom, const amrex::MultiFab &avg_zmom, const amrex::Geometry &geometry, int qc_component, int qr_component)
Definition: ERF_SBMTransport.cpp:595
@ ymom
Definition: ERF_IndexDefines.H:237
@ cons
Definition: ERF_IndexDefines.H:235
@ zmom
Definition: ERF_IndexDefines.H:238
@ xmom
Definition: ERF_IndexDefines.H:236
@ None
Definition: ERF_LSM_Types.H:6
@ ng
Definition: ERF_Morrison.H:50
@ nn
Definition: ERF_WDM6.H:32
@ xvel
Definition: ERF_IndexDefines.H:218
@ zvel
Definition: ERF_IndexDefines.H:220
@ yvel
Definition: ERF_IndexDefines.H:219
void apply(const amrex::Box &bx, const amrex::Box &domain, const int quantity, const int flux_comp, const amrex::Array4< const amrex::Real > &state, const amrex::Array4< const amrex::Real > &prim, const amrex::Array4< const amrex::Real > &base_state, const amrex::Array4< const amrex::Real > &u, const amrex::Array4< const amrex::Real > &v, const amrex::Array4< const amrex::Real > &w, const amrex::Array4< amrex::Real > &rhs, const amrex::Array4< amrex::Real > &xflux, const amrex::Array4< amrex::Real > &yflux, const amrex::Array4< amrex::Real > &zflux, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &dx_inv, const erf_wall_thermodynamics::Boundary &walls, const amrex::Real alpha_T, const amrex::Real alpha_C, const amrex::Real rdOcp, const bool cloudy=false)
Definition: ERF_CloudChamberWallFlux.H:1037
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void copy_host_slow_component(const amrex::Array4< amrex::Real > &current, const amrex::Array4< const amrex::Real > &predictor, const int i, const int j, const int k, const int component, const bool sbm_active, const int qc_component, const int qr_component) noexcept
Definition: ERF_SBMStageOwnership.H:11
void require_host_write_range_allowed(const bool sbm_active, const int start_component, const int num_components, const HostWritePath path)
Definition: ERF_SBMOwnership.cpp:47
amrex::GpuArray< FaceWall, 2 *AMREX_SPACEDIM > Boundary
Definition: ERF_WallThermodynamics.H:70
real(c_double), parameter epsilon
Definition: ERF_module_model_constants.F90:12
real(c_double), private ac
Definition: ERF_module_mp_morr_two_moment.F90:181
Definition: ERF_AdvStruct.H:19
Definition: ERF_DiffStruct.H:22
MolecDiffType molec_diff_type
Selected molecular transport model.
Definition: ERF_DiffStruct.H:94
amrex::Real alpha_C
Kinematic scalar diffusivity [m2/s].
Definition: ERF_DiffStruct.H:98
amrex::Real alpha_T
Kinematic temperature diffusivity [m2/s].
Definition: ERF_DiffStruct.H:97
int qr
rain
Definition: ERF_DataStruct.H:252
int qc
cloud liquid water
Definition: ERF_DataStruct.H:250
Functor for inverse vertical spacings with constant grid spacing.
Definition: ERF_PBLModels.H:619
Functor for inverse vertical spacings for stretched grids using a spacing array.
Definition: ERF_PBLModels.H:637
Functor for inverse vertical spacings for terrain-following grids using cell-center heights.
Definition: ERF_PBLModels.H:663
amrex::Vector< TurbChoice > turbChoice
Turbulence options for each AMR level.
Definition: ERF_DataStruct.H:2252
amrex::Real gravity
Effective gravitational acceleration.
Definition: ERF_DataStruct.H:2343
bool implicit_moisture_diffusion
Whether implicit vertical moisture diffusion is included.
Definition: ERF_DataStruct.H:2281
MoistureType moisture_type
Moisture or microphysics model.
Definition: ERF_DataStruct.H:2524
CouplingType coupling_type
Multilevel coupling strategy.
Definition: ERF_DataStruct.H:2523
bool sbm_test_native_qc_write_fault
Definition: ERF_DataStruct.H:2528
bool use_rotate_surface_flux
Whether MOST surface fluxes are rotated with terrain.
Definition: ERF_DataStruct.H:2392
bool implicit_ke_diffusion
Whether implicit vertical TKE diffusion is included.
Definition: ERF_DataStruct.H:2282
amrex::Real rdOcp
Ratio of dry-air gas constant to c_p.
Definition: ERF_DataStruct.H:2345
static MeshType mesh_type
Vertical mesh representation.
Definition: ERF_DataStruct.H:2236
AdvChoice advChoice
Advection-related options.
Definition: ERF_DataStruct.H:2248
amrex::Vector< int > anelastic
Per-level flag selecting anelastic dynamics.
Definition: ERF_DataStruct.H:2259
amrex::Vector< AnelasticType > anelastic_type
Per-level two-stage scheme used by the anelastic integrator.
Definition: ERF_DataStruct.H:2260
amrex::Vector< amrex::Vector< amrex::Real > > vert_implicit_fac
Per-level, per-stage implicit vertical diffusion factors.
Definition: ERF_DataStruct.H:2277
bool transport_scalar
Whether the passive scalar component is transported.
Definition: ERF_DataStruct.H:2403
static TerrainType terrain_type
Terrain or immersed-boundary representation.
Definition: ERF_DataStruct.H:2224
bool sbm_test_donor_cfl_fault
Definition: ERF_DataStruct.H:2529
DiffChoice diffChoice
Diffusion-related options.
Definition: ERF_DataStruct.H:2249
MoistureComponentIndices moisture_indices
Index map of the moisture data carried by the active scheme: conserved-state components for the speci...
Definition: ERF_DataStruct.H:2575
Definition: ERF_TurbStruct.H:115
bool advect_tke
Whether TKE/QKE is advected.
Definition: ERF_TurbStruct.H:1011
bool implicit_tke_dissipation
Definition: ERF_TurbStruct.H:839
bool use_tke
Whether any TKE or QKE closure is active.
Definition: ERF_TurbStruct.H:927
RANSType rans_type
Selected RANS closure.
Definition: ERF_TurbStruct.H:828
bool use_keqn
Whether a microscale TKE closure is active.
Definition: ERF_TurbStruct.H:922
bool uses_eamxx_shoc() const noexcept
Query whether this level uses the EAMxx SHOC PBL scheme.
Definition: ERF_TurbStruct.H:859
bool uses_native_shoc() const noexcept
Query whether this level uses the native SHOC PBL scheme.
Definition: ERF_TurbStruct.H:868
bool dirichlet_k
Whether TKE uses Dirichlet boundary treatment.
Definition: ERF_TurbStruct.H:830
LESType les_type
Selected LES closure.
Definition: ERF_TurbStruct.H:777
bool use_kturb
Whether any turbulence model is active.
Definition: ERF_TurbStruct.H:921
PBLType pbl_type
Selected PBL closure.
Definition: ERF_TurbStruct.H:853
erf_wall_thermodynamics::Boundary wall_boundary() const noexcept
Collect thermodynamic boundary conditions for all chamber walls.
Definition: ERF_CloudChamber.H:61
bool cloudy
Definition: ERF_CloudChamber.H:42
bool physical_initialization
Definition: ERF_CloudChamber.H:43
Here is the call graph for this function: