ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
ERF_TI_slow_rhs_pre.H
Go to the documentation of this file.
1 #include "ERF_SrcHeaders.H"
2 
3 #ifdef ERF_USE_FIRE
4 #include <ERF_FireCoupler.H>
5 #endif
6 
7 /**
8  * Wrapper for calling the routine that creates the slow RHS
9  */
10  auto slow_rhs_fun_pre = [&,zero_d=zero,one_d=one](Vector<MultiFab>& S_rhs,
11  Vector<MultiFab>& S_old,
12  Vector<MultiFab>& S_data,
13  const double old_step_time,
14  const double old_stage_time,
15  const double new_stage_time,
16  const int nrk)
17  {
18  BL_PROFILE("slow_rhs_fun_pre");
19  //
20  // Define primitive variables for all later RK stages
21  // (We have already done this for the first RK step)
22  // Note that it is essential this happen before the call to make_mom_sources
23  // because some of the buoyancy routines use the primitive variables
24  //
25  if (nrk > 0) {
26  int ng_cons = S_data[IntVars::cons].nGrow();
27  cons_to_prim(S_data[IntVars::cons], S_prim, ng_cons);
29  }
30 
31  if (verbose) Print() << std::setprecision(timeprecision)
32  << "Making slow rhs at time " << old_stage_time
33  << " for fast variables advancing from " << old_step_time
34  << " to " << new_stage_time << std::endl;
35 
36  double slow_dt = new_stage_time - old_step_time;
37 
38  const GpuArray<Real, AMREX_SPACEDIM> dxInv = fine_geom.InvCellSizeArray();
39 
40  // *************************************************************************
41  // Set up flux registers if using two_way coupling
42  // *************************************************************************
43  YAFluxRegister* fr_as_crse = nullptr;
44  YAFluxRegister* fr_as_fine = nullptr;
45  if (solverChoice.coupling_type == CouplingType::TwoWay && finest_level > 0) {
46  if (level < finest_level) {
47  fr_as_crse = getAdvFluxReg(level+1);
48  // Once per step: the anelastic RK2 integrator registers fluxes at both stages
49  // (RefluxStageWeight)
50  if (nrk == 0) { fr_as_crse->reset(); }
51  }
52  if (level > 0) {
53  fr_as_fine = getAdvFluxReg(level);
54  }
55  }
56 
57  // *************************************************************************
58  // Get multifab pointers
59  // *************************************************************************
60 
61  // Canopy data for mom sources
62  MultiFab* forest_drag = (solverChoice.do_forest_drag) ?
63  m_forest_drag[level]->get_drag_field() : nullptr;
64  MultiFab* frontal_area = (solverChoice.do_forest_drag) ?
65  m_forest_drag[level]->get_frontal_area() : nullptr;
66 
67  // Immersed Forcing
68  MultiFab* terrain_blank = (solverChoice.terrain_type == TerrainType::ImmersedForcing ||
69  solverChoice.buildings_type == BuildingsType::ImmersedForcing) ?
70  terrain_blanking[level].get() : nullptr;
71  MultiFab* terrain_blank_xface = (solverChoice.terrain_type == TerrainType::ImmersedForcing ||
72  solverChoice.buildings_type == BuildingsType::ImmersedForcing) ?
73  terrain_blanking_xface[level].get() : nullptr;
74  MultiFab* terrain_blank_yface = (solverChoice.terrain_type == TerrainType::ImmersedForcing ||
75  solverChoice.buildings_type == BuildingsType::ImmersedForcing) ?
76  terrain_blanking_yface[level].get() : nullptr;
77  MultiFab* terrain_blank_zface = (solverChoice.terrain_type == TerrainType::ImmersedForcing ||
78  solverChoice.buildings_type == BuildingsType::ImmersedForcing) ?
79  terrain_blanking_zface[level].get() : nullptr;
80 
81  // Update the total moisture variable *before* computing sources since this is used in
82  // the buoyancy calculation
83  if (solverChoice.moisture_type != MoistureType::None) {
84  int n_qstate_into_total = micro->Get_Qstate_Moist_Size() - micro->Get_Qstate_Moist_NumConc_Size();
85  make_qt(S_data[IntVars::cons], qt, n_qstate_into_total);
86  }
87 
88  MultiFab p0_to_use, base_to_use;
89  MultiFab *zpn_to_use, *zpc_to_use, *ax_to_use, *ay_to_use, *az_to_use, *dJ_to_use;
90 
91  // Moving terrain
92  std::unique_ptr<MultiFab> z_t_pert;
93  if ( solverChoice.terrain_type == TerrainType::MovingFittedMesh )
94  {
95  z_t_pert = std::make_unique<MultiFab>(S_data[IntVars::zmom].boxArray(), S_data[IntVars::zmom].DistributionMap(), 1, 1);
96  update_terrain_stage(level, old_step_time, old_stage_time, new_stage_time, slow_dt);
97 
98  p0_to_use = MultiFab(base_state_new[level], make_alias, BaseState::p0_comp, 1);
99  base_to_use = MultiFab(base_state_new[level], make_alias, 0, BaseState::num_comps);
100  zpn_to_use = z_phys_nd_src[level].get();
101  zpc_to_use = z_phys_cc_src[level].get();
102  ax_to_use = ax_src[level].get();
103  ay_to_use = ay_src[level].get();
104  az_to_use = az_src[level].get();
105  dJ_to_use = detJ_cc_src[level].get();
106 
107  } else {
108  p0_to_use = MultiFab(base_state[level], make_alias, BaseState::p0_comp, 1);
109  base_to_use = MultiFab(base_state[level], make_alias, 0, BaseState::num_comps);
110  zpn_to_use = z_phys_nd[level].get();
111  zpc_to_use = z_phys_cc[level].get();
112  ax_to_use = ax[level].get();
113  ay_to_use = ay[level].get();
114  az_to_use = az[level].get();
115  dJ_to_use = detJ_cc[level].get();
116  }
117 
118  // Get planar averages from persistent storage for immersed forcing
119  bool l_use_IF = (solverChoice.terrain_type == TerrainType::ImmersedForcing ||
120  solverChoice.buildings_type == BuildingsType::ImmersedForcing);
121  Table1D<Real> r_avg_to_pass = l_use_IF ? r_plane_avg[level].table() : Table1D<Real>();
122  Table1D<Real> t_avg_to_pass = l_use_IF ? t_plane_avg[level].table() : Table1D<Real>();
123 
124  // *****************************************************************************
125  // Construct the source terms for the cell-centered (conserved) variables
126  // *****************************************************************************
127  make_sources(level, nrk, slow_dt, old_stage_time,
128  S_data, S_prim, cc_src, base_state[level], zpc_to_use,
129  xvel_new, yvel_new, zvel_new,
130  qheating_rates[level].get(),
131  terrain_blank, fine_geom, solverChoice,
132  mapfac[level],
133  rhotheta_src[level].get(), rhoqt_src[level].get(),
134  dptr_wbar_sub, d_rayleigh_ptrs_at_lev, d_sinesq_at_lev,
135  turbPert,
136  r_avg_to_pass, t_avg_to_pass,
137  forest_drag, &S_old[IntVars::cons],
138  true, ib_wall_hfx[level].get(), ib_wall_ktarget[level].get());
139 
140  // *****************************************************************************
141  // Inject the fire-to-atmosphere heat flux AFTER make_sources, which resets
142  // cc_src at every RK stage. The lagged fire flux (FireCoupler) is constant
143  // across all RK stages and must be added here to survive the integrator.
144  // *****************************************************************************
145 #ifdef ERF_USE_FIRE
146  // on the level the fire grid refines; coarser levels get it by average-down.
147  // The heat and vapour with the WRF-Fire profile, then the smoke tracer,
148  // proportional to the fuel burnt (FireCoupler::add_sources).
149  if (m_fire_coupler && level == m_fire_coupler->level()) {
150  bool has_moisture = (solverChoice.moisture_type != MoistureType::None);
151  m_fire_coupler->add_sources(cc_src,
152  S_old[IntVars::cons],
153  *zpc_to_use,
154  zpn_to_use,
155  fine_geom,
156  has_moisture,
157  istep[level]);
158  }
159 #endif
160 
161  // Heat flux from the building faces (immersed-boundary surface energy
162  // balance), added to the freshly rebuilt source; no-op unless enabled.
163  if (ibseb_params.enable && ibseb_params.couple_heat &&
164  level < static_cast<int>(m_ibseb.size()) && m_ibseb[level]) {
165  m_ibseb[level]->add_heat_flux_to_source(cc_src, S_data[IntVars::cons], fine_geom,
166  solverChoice.c_p, solverChoice.rdOcp);
167  }
168 
169  // Nudging of potential temperature towards observations at stations
170  if (solverChoice.nudging_from_observations && obs_nudging) {
171  obs_nudging->add_theta_source(level, old_stage_time, static_cast<Real>(slow_dt), fine_geom,
172  S_data[IntVars::cons], cc_src, zpn_to_use, terrain_blank);
173  }
174 
175  // *****************************************************************************
176  // Add nudging terms on moist variables if we have a non-zero relaxation region
177  // *****************************************************************************
178 #if defined(ERF_USE_NETCDF)
179  if ( solverChoice.use_real_bcs && (level==0) &&
180  (solverChoice.moisture_type != MoistureType::None) )
181  {
182  Real bdy_factor = solverChoice.bdy_nudge_factor;
183  Real l_rdOcp = solverChoice.rdOcp;
184  Real l_c_p = solverChoice.c_p;
185  int moist_nudge_type = solverChoice.bdy_moist_nudge_type;
186  int num_q = micro->Get_Qstate_Moist_Size() - micro->Get_Qstate_Moist_NumConc_Size();
187  AMREX_ALWAYS_ASSERT( (solverChoice.bdy_moist_nudge_type != 2) ||
188  (solverChoice.moisture_type != MoistureType::Morrison_NoIce &&
189  solverChoice.moisture_type != MoistureType::SAM_NoIce) );
190  add_moist_nudging_terms(S_data[IntVars::cons], cc_src, num_q, slow_dt,
191  start_time+old_stage_time,
192  start_bdy_time, final_bdy_time, bdy_time_interval,
193  bdy_factor, real_width, geom[level],
194  bdy_data_xlo, bdy_data_xhi, bdy_data_ylo, bdy_data_yhi,
195  m_r2d, l_c_p, l_rdOcp,
196  solverChoice.moisture_indices,
197  solverChoice.use_wrf_bdy_density, moist_nudge_type);
198  }
199 #endif
200 
201  // Accumulate fixed-leaf-temperature canopy exchange in the staged
202  // source storage. The existing slow-pre and slow-post paths then
203  // apply rho-theta and rho-qv with their normal RK/anelastic weighting.
204  if (solverChoice.do_forest_drag && solverChoice.forest_biophysics &&
205  solverChoice.forest_biophysics_heat && frontal_area) {
207  S_data[IntVars::cons],
208  xvel_new, yvel_new, zvel_new,
209  frontal_area, base_to_use,
210  solverChoice);
211  }
212 
213  // *****************************************************************************
214  // Define the pressure gradient
215  // *****************************************************************************
216  make_gradp_pert(level, solverChoice, fine_geom, S_data,
217  p0_to_use, *zpn_to_use, *zpc_to_use,
218  mapfac[level],
219  get_eb(level), gradp[level]);
220 
221  // *****************************************************************************
222  // Define the buoyancy forcing term in the z-direction
223  // *****************************************************************************
224  int num_q = micro->Get_Qstate_Moist_Size() - micro->Get_Qstate_Moist_NumConc_Size();
225  make_buoyancy(level, S_data, S_prim, qt, buoyancy, fine_geom, solverChoice, base_to_use,
226  num_q, get_eb(level), solverChoice.anelastic[level]);
227 
228  // *****************************************************************************
229  // Make remaining (not gradp or buoyancy) momentum sources
230  // *****************************************************************************
231  make_mom_sources(old_stage_time, slow_dt,
232  S_data, zpn_to_use, zpc_to_use, stretched_dz_h[level],
233  xvel_new, yvel_new, zvel_new,
234  xmom_src, ymom_src, zmom_src,
235  base_to_use, forest_drag, terrain_blank,
236  terrain_blank_xface, terrain_blank_yface, terrain_blank_zface,
237  cosPhi_m[level].get(), sinPhi_m[level].get(), fine_geom, solverChoice,
238  mapfac[level],
239  (solverChoice.have_geo_wind_profile) ? d_u_geos[level].data(): nullptr,
240  (solverChoice.have_geo_wind_profile) ? d_v_geos[level].data(): nullptr,
241  dptr_wbar_sub, d_rayleigh_ptrs_at_lev, d_sinesq_at_lev, d_sinesq_stag_at_lev,
242  d_sponge_ptrs_at_lev,
243  (solverChoice.hindcast_lateral_forcing? &forecast_state_interp[level] : nullptr),
244  sounding_at_level(level), lsf_at_level(level), lsf_data[level],
245  get_eb(level), true);
246 
247  // Nudging of the velocity towards observations at stations
248  if (solverChoice.nudging_from_observations && obs_nudging) {
249  obs_nudging->add_momentum_sources(level, old_stage_time, static_cast<Real>(slow_dt), fine_geom,
250  S_data[IntVars::cons], xvel_new, yvel_new, zvel_new,
251  xmom_src, ymom_src, zmom_src, zpn_to_use,
252  terrain_blank_xface, terrain_blank_yface,
253  terrain_blank_zface);
254  }
255 
256  // *****************************************************************************
257  // Add body sources if doing flow around a body
258  // *****************************************************************************
259  add_thin_body_sources(xmom_src, ymom_src, zmom_src,
260  xflux_imask[level], yflux_imask[level], zflux_imask[level],
261  thin_xforce[level], thin_yforce[level], thin_zforce[level]);
262 
263  // *****************************************************************************
264  // Define RHS for rho, rho_theta and momenta
265  // *****************************************************************************
266  erf_slow_rhs_pre(level, finest_level, nrk, slow_dt, S_rhs, S_old, S_data,
267  S_prim, qt, avg_xmom[level], avg_ymom[level], avg_zmom[level],
268  xvel_new, yvel_new, zvel_new,
269  z_t_rk[level], cc_src, xmom_src, ymom_src, zmom_src, buoyancy,
270  (level > 0) ? &zmom_crse_rhs[level] : nullptr,
271  Tau[level], Tau_corr[level], Tau_EB[level],
272  SmnSmn, eddyDiffs, Hfx1, Hfx2, Hfx3, Q1fx1, Q1fx2, Q1fx3, Q2fx3, Diss, mf_surface_fluxes,
273  Hfx3_EB, fine_geom, solverChoice, m_SurfaceLayer, domain_bcs_type_d, domain_bcs_type,
274  *zpn_to_use, *zpc_to_use, *ax_to_use, *ay_to_use, *az_to_use, *dJ_to_use,
275  stretched_dz_d[level], gradp[level],
276  mapfac[level], get_eb(level),
277 #ifdef ERF_USE_EAMXX_SHOC
278  eamxx_shoc_interface[level].get(),
279 #endif
280  native_shoc_driver[level].get(),
281  fr_as_crse, fr_as_fine, &base_to_use,
282  cloud_chamber_config.active ? &cloud_chamber_config : nullptr,
283  cloud_chamber_budget.get(),
284  ib_wall_face13[level].get(), ib_wall_face23[level].get(),
285  ib_wall_face33[level].get(), ib_wall_hfx[level].get());
286 
287  // Anelastic coarse/fine faces in time (ERF_CoarseFineFaces.cpp): a coarse level adds
288  // this stage's mass fluxes to those of its step, a fine level registers the coarse
289  // momenta its coarse/fine faces are filled from in this stage
290  if (solverChoice.anelastic[level]) {
291  if (level < finest_level) { cf_anelastic_record(level, nrk); }
292  if (level > 0) { cf_anelastic_stage_registration(level, nrk); }
293  }
294 
295  if ((solverChoice.vert_implicit_fac[level][nrk] > zero_d) && solverChoice.implicit_before_substep) {
296  const Real stage_dt = static_cast<Real>(slow_dt);
297  /**
298  * @brief Temporary MultiFab for conserved variable updates.
299  */
300  MultiFab scratch(S_data[IntVars::cons].boxArray(),S_data[IntVars::cons].DistributionMap(), 2,
301  S_data[IntVars::cons].nGrowVect());
302  MultiFab::Copy(scratch, S_old[IntVars::cons], 0, 0, 2, S_data[IntVars::cons].nGrowVect()); // scratch := S_old (for rho, rhotheta)
303  MultiFab::Saxpy(scratch, stage_dt, S_rhs[IntVars::cons], 0, 0, 2, 0); // scratch := S_old + stage_dt*Src (for rho, rhotheta)
304  scratch.FillBoundary(geom[level].periodicity());
305 
306  /**
307  * @brief Temporary MultiFab for x-momentum updates.
308  */
309  MultiFab scratch_xmom(S_data[IntVars::xmom].boxArray(),
310  S_data[IntVars::xmom].DistributionMap(), 1,
311  S_data[IntVars::xmom].nGrowVect());
312  /**
313  * @brief Temporary MultiFab for y-momentum updates.
314  */
315  MultiFab scratch_ymom(S_data[IntVars::ymom].boxArray(),
316  S_data[IntVars::ymom].DistributionMap(), 1,
317  S_data[IntVars::ymom].nGrowVect());
318  if (solverChoice.implicit_momentum_diffusion) {
319  MultiFab::Copy(scratch_xmom, S_old[IntVars::xmom], 0, 0, 1, S_data[IntVars::xmom].nGrowVect()); // scratch := S_old
320  MultiFab::Saxpy(scratch_xmom, stage_dt, S_rhs[IntVars::xmom], 0, 0, 1, 0); // scratch := S_old + stage_dt*Src
321  scratch_xmom.FillBoundary(geom[level].periodicity());
322 
323  MultiFab::Copy(scratch_ymom, S_old[IntVars::ymom], 0, 0, 1, S_data[IntVars::ymom].nGrowVect()); // scratch := S_old
324  MultiFab::Saxpy(scratch_ymom, stage_dt, S_rhs[IntVars::ymom], 0, 0, 1, 0); // scratch := S_old + stage_dt*Src
325  scratch_ymom.FillBoundary(geom[level].periodicity());
326  }
327 
328  // The ghost cells of scratch still hold S_old; at a coarse/fine boundary fill them with
329  // the coarse data at the time we are advancing to, since a column that ends there is
330  // closed with the value on the other side
331  if (level > 0) {
332  const bool l_mom = solverChoice.implicit_momentum_diffusion;
333  Vector<MultiFab*> scratch_mom = {l_mom ? &scratch_xmom : nullptr,
334  l_mom ? &scratch_ymom : nullptr,
335  nullptr};
336  FillCoarseFineGhostsForImplicit(level, new_stage_time, scratch, {RhoTheta_comp}, scratch_mom);
337  }
338 
339 #include "ERF_ImplicitPre.H"
340 
341  // NOTE: the implicit part of the vertical (rho theta) diffusion is not added to the flux
342  // registers. (rho theta) is refluxed only on anelastic levels, and there no stage
343  // whose fluxes are registered solves implicitly: RK2 turns the solve off, and
344  // MidPoint solves in stage 0 but registers stage 1 (see SolverChoice and
345  // StageFluxWeight).
346 
347  MultiFab::Saxpy(scratch, -one_d, S_old[IntVars::cons], 1, 1, 1, 0); // scratch := (S_new - S_old) (for rhotheta only)
348  scratch.mult(one_d / stage_dt); // scratch := (S_new - S_old) / stage_dt
349  MultiFab::Copy(S_rhs[IntVars::cons], scratch, 1, 1, 1, 0); // slow_rhs := (S_new - S_old) / stage_dt (for rhotheta only)
350 
351  if (solverChoice.implicit_momentum_diffusion) {
352  MultiFab::Saxpy(scratch_xmom, -one_d, S_old[IntVars::xmom], 0, 0, 1, 0); // scratch := (S_new - S_old)
353  scratch_xmom.mult(one_d / stage_dt); // scratch := (S_new - S_old) / stage_dt
354  MultiFab::Copy(S_rhs[IntVars::xmom], scratch_xmom, 0, 0, 1, 0); // slow_rhs := (S_new - S_old) / stage_dt
355 
356  MultiFab::Saxpy(scratch_ymom, -one_d, S_old[IntVars::ymom], 0, 0, 1, 0); // scratch := (S_new - S_old)
357  scratch_ymom.mult(one_d / stage_dt); // scratch := (S_new - S_old) / stage_dt
358  MultiFab::Copy(S_rhs[IntVars::ymom], scratch_ymom, 0, 0, 1, 0); // slow_rhs := (S_new - S_old) / stage_dt
359  }
360  }
361 
362 #ifdef ERF_USE_EAMXX_SHOC
363  if (solverChoice.turbChoice[level].uses_eamxx_shoc() && eamxx_shoc_interface[level]) {
364  eamxx_shoc_interface[level]->add_fast_tend(S_rhs);
365  }
366 #endif
367  if (solverChoice.turbChoice[level].uses_native_shoc() && native_shoc_driver[level]) {
368  // Native SHOC applies its increment directly to state; ERF no
369  // longer adds a separate fast RHS contribution.
370  }
371 
372  // *****************************************************************************
373  // Update for moving terrain
374  // *****************************************************************************
375  if ( solverChoice.terrain_type == TerrainType::MovingFittedMesh )
376  {
377  MultiFab r_hse_new (base_state_new[level], make_alias, BaseState::r0_comp, 1);
378  MultiFab p_hse_new (base_state_new[level], make_alias, BaseState::p0_comp, 1);
379  MultiFab pi_hse_new (base_state_new[level], make_alias, BaseState::pi0_comp, 1);
380  MultiFab th_hse_new (base_state_new[level], make_alias, BaseState::th0_comp, 1);
381 
382  MultiFab* r0_new = &r_hse_new;
383  MultiFab* p0_new = &p_hse_new;
384  MultiFab* pi0_new = &pi_hse_new;
385  MultiFab* th0_new = &th_hse_new;
386 
387  // We define and evolve (rho theta)_0 in order to re-create p_0 in a way that is consistent
388  // with our update of (rho theta) but does NOT maintain dp_0 / dz = -rho_0 g. This is why
389  // we no longer discretize the vertical pressure gradient in perturbational form.
390  MultiFab rt0(p0->boxArray(),p0->DistributionMap(),1,1);
391  MultiFab rt0_new(p0->boxArray(),p0->DistributionMap(),1,1);
392  MultiFab r0_temp(p0->boxArray(),p0->DistributionMap(),1,1);
393 
394  // Remember this does NOT maintain dp_0 / dz = -rho_0 g, so we can no longer
395  // discretize the vertical pressure gradient in perturbational form.
396  AMREX_ALWAYS_ASSERT(solverChoice.advChoice.dycore_horiz_adv_type == AdvType::Centered_2nd);
397  AMREX_ALWAYS_ASSERT(solverChoice.advChoice.dycore_vert_adv_type == AdvType::Centered_2nd);
398 
399  double dt_base = new_stage_time - old_step_time;
400 
401  const Real l_rdOcp = solverChoice.rdOcp;
402 
403 #ifdef _OPENMP
404 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
405 #endif
406  // Tile only in x and y: the second kernel reads r0_tmp and rt0_tmp at k-1 and k+1,
407  // which the first kernel fills tile by tile, so a tile boundary in z would read
408  // values another thread may not have written yet
409  for ( MFIter mfi(*p0,TileNoZ()); mfi.isValid(); ++mfi)
410  {
411  const Array4<Real > rt0_arr = rt0.array(mfi);
412  const Array4<Real > rt0_tmp_arr = rt0_new.array(mfi);
413 
414  const Array4<Real const> r0_arr = r0->const_array(mfi);
415  const Array4<Real > r0_new_arr = r0_new->array(mfi);
416  const Array4<Real > r0_tmp_arr = r0_temp.array(mfi);
417 
418  const Array4<Real const> p0_arr = p0->const_array(mfi);
419  const Array4<Real > p0_new_arr = p0_new->array(mfi);
420  const Array4<Real > pi0_new_arr = pi0_new->array(mfi);
421  const Array4<Real > th0_new_arr = th0_new->array(mfi);
422 
423  const Array4<Real >& z_t_arr = z_t_rk[level]->array(mfi);
424 
425  const Array4<Real const>& dJ_old_arr = detJ_cc[level]->const_array(mfi);
426  const Array4<Real const>& dJ_new_arr = detJ_cc_new[level]->const_array(mfi);
427  const Array4<Real const>& dJ_src_arr = detJ_cc_src[level]->const_array(mfi);
428 
429  Box gbx = mfi.growntilebox({1,1,1});
430  amrex::ParallelFor(gbx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
431  {
432  rt0_arr(i,j,k) = getRhoThetagivenP(p0_arr(i,j,k));
433  rt0_tmp_arr(i,j,k) = getRhoThetagivenP(p0_new_arr(i,j,k));
434  r0_tmp_arr(i,j,k) = r0_new_arr(i,j,k);
435  });
436 
437  Box gbx2 = mfi.growntilebox({1,1,0});
438  amrex::ParallelFor(gbx2, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
439  {
440  Real zflux_r_lo = -z_t_arr(i,j,k ) * myhalf * (r0_tmp_arr(i,j,k) + r0_tmp_arr(i,j,k-1));
441  Real zflux_r_hi = -z_t_arr(i,j,k+1) * myhalf * (r0_tmp_arr(i,j,k) + r0_tmp_arr(i,j,k+1));
442 
443  Real zflux_rt_lo = zflux_r_lo * myhalf * (rt0_tmp_arr(i,j,k)/r0_tmp_arr(i,j,k) + rt0_tmp_arr(i,j,k-1)/r0_tmp_arr(i,j,k-1));
444  Real zflux_rt_hi = zflux_r_hi * myhalf * (rt0_tmp_arr(i,j,k)/r0_tmp_arr(i,j,k) + rt0_tmp_arr(i,j,k+1)/r0_tmp_arr(i,j,k+1));
445 
446  Real invdetJ = one / dJ_src_arr(i,j,k);
447 
448  Real src_r = - invdetJ * ( zflux_r_hi - zflux_r_lo ) * dxInv[2];
449  Real src_rt = - invdetJ * ( zflux_rt_hi - zflux_rt_lo ) * dxInv[2];
450 
451  Real dt_b = static_cast<Real>(dt_base);
452  Real rho0_new = dJ_old_arr(i,j,k) * r0_arr(i,j,k) + dt_b * dJ_src_arr(i,j,k) * src_r;
453  Real rt0_tmp_new = dJ_old_arr(i,j,k) * rt0_arr(i,j,k) + dt_b * dJ_src_arr(i,j,k) * src_rt;
454 
455  r0_new_arr(i,j,k) = rho0_new / dJ_new_arr(i,j,k);
456  rt0_tmp_new /= dJ_new_arr(i,j,k);
457 
458  p0_new_arr(i,j,k) = getPgivenRTh(rt0_tmp_new);
459  pi0_new_arr(i,j,k) = getExnergivenRTh(rt0_tmp_new, l_rdOcp);
460  th0_new_arr(i,j,k) = rt0_tmp_new / r0_new_arr(i,j,k);
461  });
462  } // MFIter
463  r0_new->FillBoundary(fine_geom.periodicity());
464  p0_new->FillBoundary(fine_geom.periodicity());
465  th0_new->FillBoundary(fine_geom.periodicity());
466  }
467 
468 #ifdef ERF_USE_NETCDF
469  // Populate RHS for relaxation zones if using real bcs
470  if (solverChoice.use_real_bcs && (level == 0)) {
471  const Real bdy_factor = solverChoice.bdy_nudge_factor;
472  if (real_width>0) {
473  //
474  // Note that old_stage_time is elapsed time, but (start_time+old_stage_time) is total time
475  // start_bdy_time and final_bdy_time are total time
476  //
477  double total_time = start_time + old_stage_time;
478  Real l_rdOcp = solverChoice.rdOcp;
479  Real l_c_p = solverChoice.c_p;
480  realbdy_compute_interior_ghost_rhs(total_time, slow_dt,
481  start_bdy_time, final_bdy_time, bdy_time_interval,
482  bdy_factor, real_width, fine_geom,
483  S_rhs, S_data,
484  bdy_data_xlo, bdy_data_xhi,
485  bdy_data_ylo, bdy_data_yhi,
486  m_r2d, l_c_p, l_rdOcp,
487  solverChoice.use_wrf_bdy_density,
488  solverChoice.bdy_rho_nudge_factor);
489  }
490  }
491 #endif
492  }; // end slow_rhs_fun_pre
void add_thin_body_sources(MultiFab &xmom_src, MultiFab &ymom_src, MultiFab &zmom_src, std::unique_ptr< iMultiFab > &xflux_imask_lev, std::unique_ptr< iMultiFab > &yflux_imask_lev, std::unique_ptr< iMultiFab > &zflux_imask_lev, std::unique_ptr< MultiFab > &thin_xforce_lev, std::unique_ptr< MultiFab > &thin_yforce_lev, std::unique_ptr< MultiFab > &thin_zforce_lev)
Definition: ERF_AddThinBodySources.cpp:27
AMREX_INLINE void AddCanopyBiophysicsHeatSources(amrex::MultiFab &cell_source, const amrex::MultiFab &S_data, const amrex::MultiFab &xvel, const amrex::MultiFab &yvel, const amrex::MultiFab &zvel, const amrex::MultiFab *frontal_area, const amrex::MultiFab &base_state, const SolverChoice &solver_choice)
Definition: ERF_CanopyBiophysics.H:28
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real getRhoThetagivenP(const amrex::Real p, const amrex::Real qv=amrex::Real(0))
Definition: ERF_EOS.H:172
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real getExnergivenRTh(const amrex::Real rhotheta, const amrex::Real rdOcp, const amrex::Real qv=amrex::Real(0))
Definition: ERF_EOS.H:156
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real getPgivenRTh(const amrex::Real rhotheta, const amrex::Real qv=amrex::Real(0))
Definition: ERF_EOS.H:81
The atmosphere side of the fire coupling, shared by every fire engine.
#define RhoTheta_comp
Definition: ERF_IndexDefines.H:54
@ Centered_2nd
pp_for_pert_vels get("prob_type", prob_type)
amrex::GpuArray< Real, AMREX_SPACEDIM > dxInv
Definition: ERF_InitCustomPertVels_ParticleTests.H:17
AMREX_ALWAYS_ASSERT(bx.length()[2]==khi+1)
void realbdy_compute_interior_ghost_rhs(const double &time, const double &delta_t_d, const double &start_bdy_time, const double &final_bdy_time, const double &bdy_time_interval, const Real &nudge_factor, int width, const Geometry &geom, Vector< MultiFab > &S_rhs, Vector< MultiFab > &S_cur_data, Vector< Vector< FArrayBox >> &bdy_data_xlo, Vector< Vector< FArrayBox >> &bdy_data_xhi, Vector< Vector< FArrayBox >> &bdy_data_ylo, Vector< Vector< FArrayBox >> &bdy_data_yhi, std::unique_ptr< ReadBndryPlanes > &m_r2d, const Real &c_p, const Real &rdOcp, const bool use_wrf_bdy_density, const Real &bdy_rho_nudge_factor)
Definition: ERF_InteriorGhostCells.cpp:161
void make_buoyancy(int lev, const Vector< MultiFab > &S_data, const MultiFab &S_prim, const MultiFab &qt, MultiFab &buoyancy, const Geometry geom, const SolverChoice &solverChoice, const MultiFab &base_state, const int n_qstate, const eb_ &ebfact, const int anelastic)
Definition: ERF_MakeBuoyancy.cpp:34
void make_gradp_pert(int level, const SolverChoice &solverChoice, const Geometry &geom, Vector< MultiFab > &S_data, const MultiFab &p0, const MultiFab &z_phys_nd, const MultiFab &z_phys_cc, Vector< std::unique_ptr< MultiFab >> &mapfac, const eb_ &ebfact, Vector< MultiFab > &gradp)
Definition: ERF_MakeGradP.cpp:28
void make_mom_sources(double time_d, double dt, const Vector< MultiFab > &S_data, const MultiFab *z_phys_nd, const MultiFab *z_phys_cc, Vector< Real > &stretched_dz_h, const MultiFab &xvel, const MultiFab &yvel, const MultiFab &wvel, MultiFab &xmom_src, MultiFab &ymom_src, MultiFab &zmom_src, const MultiFab &base_state, MultiFab *forest_drag, MultiFab *terrain_blank, MultiFab *terrain_blank_xface, MultiFab *terrain_blank_yface, MultiFab *terrain_blank_zface, MultiFab *cosPhi_mf, MultiFab *sinPhi_mf, const Geometry geom, const SolverChoice &solverChoice, Vector< std::unique_ptr< MultiFab >> &, const Real *dptr_u_geos, const Real *dptr_v_geos, const Real *dptr_wbar_sub, const Vector< Real * > d_rayleigh_ptrs_at_lev, const amrex::Real *d_sinesq_at_lev, const amrex::Real *d_sinesq_stag_at_lev, const Vector< Real * > d_sponge_ptrs_at_lev, const Vector< MultiFab > *forecast_state_at_lev, const InputSoundingData &input_sounding_data, const LargeScaleForcingData &lsf_data, std::unique_ptr< amrex::MultiFab > &lsf_tendencies, const eb_ &ebfact, bool is_slow_step)
Definition: ERF_MakeMomSources.cpp:40
void make_sources(int level, int, double dt, double time_d, const Vector< MultiFab > &S_data, const MultiFab &S_prim, MultiFab &source, const MultiFab &base_state, const MultiFab *z_phys_cc, const MultiFab &xvel, const MultiFab &yvel, const MultiFab &zvel, const MultiFab *qheating_rates, MultiFab *terrain_blank, const Geometry geom, const SolverChoice &solverChoice, Vector< std::unique_ptr< MultiFab >> &mapfac, const MultiFab *rhotheta_src, const MultiFab *rhoqt_src, const Real *dptr_wbar_sub, const Vector< Real * > d_rayleigh_ptrs_at_lev, const Real *d_sinesq_at_lev, TurbulentPerturbation &turbPert, const Table1D< Real > r_plane_avg, const Table1D< Real > t_plane_avg, const MultiFab *forest_drag, const MultiFab *cons_old, bool is_slow_step, MultiFab *ib_wall_hfx, MultiFab *ib_wall_ktarget)
Definition: ERF_MakeSources.cpp:39
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
amrex::Real Real
Definition: ERF_ShocInterface.H:19
void erf_slow_rhs_pre(int level, int finest_level, int nrk, double dt, Vector< MultiFab > &S_rhs, Vector< MultiFab > &S_old, Vector< MultiFab > &S_data, const MultiFab &S_prim, const MultiFab &qt, MultiFab &avg_xmom, MultiFab &avg_ymom, MultiFab &avg_zmom, const MultiFab &xvel, const MultiFab &yvel, const MultiFab &zvel, std::unique_ptr< MultiFab > &z_t_mf, const MultiFab &cc_src, const MultiFab &xmom_src, const MultiFab &ymom_src, const MultiFab &zmom_src, const MultiFab &buoyancy, const MultiFab *zmom_crse_rhs, Vector< std::unique_ptr< MultiFab >> &Tau_lev, Vector< std::unique_ptr< MultiFab >> &Tau_corr_lev, Vector< Vector< std::unique_ptr< MultiFab >>> &Tau_EB, MultiFab *SmnSmn, MultiFab *eddyDiffs, MultiFab *Hfx1, MultiFab *Hfx2, MultiFab *Hfx3, MultiFab *Q1fx1, MultiFab *Q1fx2, MultiFab *Q1fx3, MultiFab *Q2fx3, MultiFab *, MultiFab *mf_surface_fluxes, MultiFab *Hfx3_EB, const Geometry geom, const SolverChoice &solverChoice, const amrex::Vector< std::unique_ptr< SurfaceLayer >> &SurfLayer, const Gpu::DeviceVector< BCRec > &domain_bcs_type_d, const Vector< BCRec > &domain_bcs_type_h, const MultiFab &z_phys_nd, const MultiFab &z_phys_cc, const MultiFab &ax, const MultiFab &ay, const MultiFab &az, const MultiFab &detJ, Gpu::DeviceVector< Real > &stretched_dz_d, Vector< MultiFab > &gradp, Vector< std::unique_ptr< MultiFab >> &mapfac, const eb_ &ebfact, ShocDriver *native_shoc_lev, YAFluxRegister *fr_as_crse, YAFluxRegister *fr_as_fine, const MultiFab *cloud_chamber_base_state, const erf_cloud_chamber::Config *cloud_chamber_config, CloudChamberBudget *cloud_budget, const MultiFab *ib_wall_face13, const MultiFab *ib_wall_face23, const MultiFab *ib_wall_face33, const MultiFab *ib_wall_hfx)
Definition: ERF_SlowRhsPre.cpp:70
auto slow_rhs_fun_pre
Definition: ERF_TI_slow_rhs_pre.H:10
auto make_pi_stage
Definition: ERF_TI_utils.H:4
auto update_terrain_stage
Definition: ERF_TI_utils.H:125
AMREX_FORCE_INLINE amrex::IntVect TileNoZ()
Definition: ERF_TileNoZ.H:11
void cons_to_prim(const MultiFab &cons_state, MultiFab &S_prim, int ng)
Definition: ERF_Utils.cpp:17
void make_qt(const MultiFab &cons_state, MultiFab &qt, int n_qstate_into_total)
Definition: ERF_Utils.cpp:112
@ num_comps
Definition: ERF_IndexDefines.H:102
@ pi0_comp
Definition: ERF_IndexDefines.H:99
@ p0_comp
Definition: ERF_IndexDefines.H:98
@ th0_comp
Definition: ERF_IndexDefines.H:100
@ r0_comp
Definition: ERF_IndexDefines.H:97
@ ymom
Definition: ERF_IndexDefines.H:274
@ cons
Definition: ERF_IndexDefines.H:272
@ zmom
Definition: ERF_IndexDefines.H:275
@ xmom
Definition: ERF_IndexDefines.H:273
@ None
Definition: ERF_LSM_Types.H:6
@ qt
Definition: ERF_Kessler.H:30
real(c_double), parameter p0
Definition: ERF_module_model_constants.F90:40
real(kind=kind_phys), parameter, private r0
Definition: ERF_module_mp_wdm6.F90:75
Definition: ERF_ObsNudging.cpp:22