ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
ERF_MakeSources.cpp File Reference
#include <AMReX_MultiFab.H>
#include "ERF_Constants.H"
#include <AMReX_ArrayLim.H>
#include <AMReX_BCRec.H>
#include <AMReX_TableData.H>
#include <AMReX_GpuContainers.H>
#include <ERF_ImmersedForcing.H>
#include <ERF_NumericalDiffusion.H>
#include <ERF_PlaneAverage.H>
#include <ERF_SrcHeaders.H>
#include <ERF_TI_slow_headers.H>
#include <ERF_MOSTStress.H>
Include dependency graph for ERF_MakeSources.cpp:

Functions

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, bool is_slow_step)
 

Function Documentation

◆ make_sources()

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,
bool  is_slow_step 
)

Function for computing the slow RHS for the evolution equations for the density, potential temperature and momentum.

Parameters
[in]levellevel of resolution
[in]nrkwhich RK stage
[in]dtslow time step
[in]S_datacurrent solution
[in]S_primprimitive variables (i.e. conserved variables divided by density)
[in]sourcesource terms for conserved variables
[in]geomContainer for geometric information
[in]solverChoiceContainer for solver parameters
[in]mapfacmap factors
[in]dptr_rhotheta_srccustom temperature source term
[in]dptr_rhoqt_srccustom moisture source term
[in]dptr_wbar_subsubsidence source term
[in]d_rayleigh_ptrs_at_levVector of {strength of Rayleigh damping, reference value of theta} used to define Rayleigh damping
[in]d_sinesq_at_levsin( (pi/2) (z-z_t)/(damping depth)) at cell centers
62 {
63  BL_PROFILE_REGION("erf_make_sources()");
64 
65  Real time = static_cast<Real>(time_d);
66 
67  // *****************************************************************************
68  // Initialize source to zero since we re-compute it every RK stage
69  // *****************************************************************************
70  if (is_slow_step) {
71  source.setVal(0.);
72  } else {
73  source.setVal(0.0,Rho_comp,2);
74  }
75 
76  const bool l_use_ndiff = solverChoice.use_num_diff;
77 
78  TurbChoice tc = solverChoice.turbChoice[level];
79  const bool l_use_KE = tc.use_tke;
80  const bool l_diff_KE = tc.diffuse_tke_3D;
81 
82  const Box& domain = geom.Domain();
83 
84  const GpuArray<Real, AMREX_SPACEDIM> dxInv = geom.InvCellSizeArray();
85  const GpuArray<Real, AMREX_SPACEDIM> dx = geom.CellSizeArray();
86 
87  MultiFab r_hse (base_state, make_alias, BaseState::r0_comp , 1);
88  MultiFab th_hse (base_state, make_alias, BaseState::th0_comp , 1);
89  MultiFab qv_hse (base_state, make_alias, BaseState::qv0_comp , 1);
90 
91  Real* thetabar = d_rayleigh_ptrs_at_lev[Rayleigh::thetabar];
92 
93  // flags to apply certain source terms in substep call only
94  bool use_Rayleigh_fast = ( (solverChoice.dampingChoice.rayleigh_damping_type == RayleighDampingType::FastExplicit) ||
96  bool use_ImmersedForcing_fast = solverChoice.immersed_forcing_substep;
97 
98  // flag for a moisture model
99  bool has_moisture = (solverChoice.moisture_type != MoistureType::None);
100 
101  // *****************************************************************************
102  // Planar averages for subsidence terms and immersed forcing
103  // *****************************************************************************
104  Table1D<Real> dptr_r_plane, dptr_t_plane, dptr_qv_plane, dptr_qc_plane;
105  Table1D<Real> dptr_r_plane_if, dptr_t_plane_if; // Separate tables for immersed forcing
106  TableData<Real, 1> r_plane_tab, t_plane_tab, qv_plane_tab, qc_plane_tab;
107 
108  bool use_immersed_forcing = (solverChoice.terrain_type == TerrainType::ImmersedForcing ||
109  solverChoice.buildings_type == BuildingsType::ImmersedForcing);
110 
111  // Use passed-in planar averages for immersed forcing (computed once in ERF_AdvanceDycore.cpp)
112  AMREX_ALWAYS_ASSERT(!use_immersed_forcing || (r_plane_avg && t_plane_avg));
113  if (use_immersed_forcing && r_plane_avg && t_plane_avg) {
114  // Use pre-computed values from persistent storage (already in Table1D format)
115  dptr_r_plane_if = r_plane_avg;
116  dptr_t_plane_if = t_plane_avg;
117  }
118 
119  // Compute planar averages if needed for subsidence
120  bool compute_averages = (is_slow_step && dptr_wbar_sub);
121 
122  if (compute_averages)
123  {
124  // The plane averaging operates at fixed k-index (not fixed height), which is
125  // acceptable for immersed forcing but not correct for subsidence with variable dz
126  AMREX_ALWAYS_ASSERT(solverChoice.mesh_type != MeshType::VariableDz);
127 
128  //
129  // The call to "compute_averages" currently does all the components in one call
130  // We can then extract each component separately with the "line_average" call
131  //
132  // We need just one ghost cell in the vertical
133  //
134  IntVect ng_c(S_data[IntVars::cons].nGrowVect()); ng_c[2] = 1;
135  //
136  // With no moisture we only (rho) and (rho theta); with moisture we also do qv and qc
137  // We use the alias here to control ncomp inside the PlaneAverage
138  //
139  int ncomp = (!has_moisture) ? 2 : RhoQ2_comp+1;
140  MultiFab cons(S_data[IntVars::cons], make_alias, 0, ncomp);
141 
142  PlaneAverage cons_ave(&cons, geom, solverChoice.ave_plane, ng_c);
143  cons_ave.compute_averages(ZDir(), cons_ave.field());
144 
145  int ncell = cons_ave.ncell_line();
146 
147  Gpu::HostVector< Real> r_plane_h(ncell);
148  Gpu::DeviceVector< Real> r_plane_d(ncell);
149 
150  Gpu::HostVector< Real> t_plane_h(ncell);
151  Gpu::DeviceVector< Real> t_plane_d(ncell);
152 
153  cons_ave.line_average(Rho_comp , r_plane_h);
154  cons_ave.line_average(RhoTheta_comp, t_plane_h);
155 
156  Gpu::copyAsync(Gpu::hostToDevice, r_plane_h.begin(), r_plane_h.end(), r_plane_d.begin());
157  Gpu::copyAsync(Gpu::hostToDevice, t_plane_h.begin(), t_plane_h.end(), t_plane_d.begin());
158 
159  Real* dptr_r = r_plane_d.data();
160  Real* dptr_t = t_plane_d.data();
161 
162  Box tdomain = domain; tdomain.grow(2,ng_c[2]);
163  r_plane_tab.resize({tdomain.smallEnd(2)}, {tdomain.bigEnd(2)});
164  t_plane_tab.resize({tdomain.smallEnd(2)}, {tdomain.bigEnd(2)});
165 
166  int offset = ng_c[2];
167 
168  dptr_r_plane = r_plane_tab.table();
169  dptr_t_plane = t_plane_tab.table();
170  ParallelFor(ncell, [=] AMREX_GPU_DEVICE (int k) noexcept
171  {
172  dptr_r_plane(k-offset) = dptr_r[k];
173  dptr_t_plane(k-offset) = dptr_t[k];
174  });
175 
176  if (has_moisture)
177  {
178  Gpu::HostVector< Real> qv_plane_h(ncell), qc_plane_h(ncell);
179  Gpu::DeviceVector<Real> qv_plane_d(ncell), qc_plane_d(ncell);
180 
181  // Water vapor
182  cons_ave.line_average(RhoQ1_comp, qv_plane_h);
183  Gpu::copyAsync(Gpu::hostToDevice, qv_plane_h.begin(), qv_plane_h.end(), qv_plane_d.begin());
184 
185  // Cloud water
186  cons_ave.line_average(RhoQ2_comp, qc_plane_h);
187  Gpu::copyAsync(Gpu::hostToDevice, qc_plane_h.begin(), qc_plane_h.end(), qc_plane_d.begin());
188 
189  Real* dptr_qv = qv_plane_d.data();
190  Real* dptr_qc = qc_plane_d.data();
191 
192  qv_plane_tab.resize({tdomain.smallEnd(2)}, {tdomain.bigEnd(2)});
193  qc_plane_tab.resize({tdomain.smallEnd(2)}, {tdomain.bigEnd(2)});
194 
195  dptr_qv_plane = qv_plane_tab.table();
196  dptr_qc_plane = qc_plane_tab.table();
197  ParallelFor(ncell, [=] AMREX_GPU_DEVICE (int k) noexcept
198  {
199  dptr_qv_plane(k-offset) = dptr_qv[k];
200  dptr_qc_plane(k-offset) = dptr_qc[k];
201  });
202  }
203  }
204 
205  // *****************************************************************************
206  // Vertical extent used by the four stream radiation approximation
207  // *****************************************************************************
208  // NOTE: The fluxes live on w-faces
209  //
210  // NOTE: There is deliberately no scratch storage for the Q integral or the
211  // radiative flux here. Those used to be two nk-length device vectors
212  // shared by every (i,j) thread of the 2-D ParallelFor below, so all the
213  // columns raced on the same buffers (issue 3714). The kernel now keeps
214  // the running integral and the lower-face flux in thread-private scalars.
215  int klo = domain.smallEnd(2);
216  int khi = domain.bigEnd(2);
217 
218  // *****************************************************************************
219  // Define source term for cell-centered conserved variables, from
220  // 1. user-defined source terms for (rho theta) and (rho q_t)
221  // 2. radiation for (rho theta)
222  // 3. Rayleigh damping for (rho theta)
223  // 4. custom forcing for (rho theta) and (rho Q1)
224  // 5. custom subsidence for (rho theta) and (rho Q1)
225  // 6. numerical diffusion for (rho theta)
226  // 7. sponging
227  // 8. turbulent perturbation
228  // 9. nudging towards input sounding values (only for theta)
229  // 10a. Immersed forcing for terrain
230  // 10b. Immersed forcing for buildings
231  // Real(11.) Four stream radiation source for (rho theta)
232  // *****************************************************************************
233 
234  // ***********************************************************************************************
235  // Add remaining source terms
236  // ***********************************************************************************************
237 #ifdef _OPENMP
238 #pragma omp parallel if (Gpu::notInLaunchRegion())
239 #endif
240  {
241  for ( MFIter mfi(S_data[IntVars::cons],TileNoZ()); mfi.isValid(); ++mfi)
242  {
243  Box bx = mfi.tilebox();
244 
245  const Array4<const Real>& cell_data = S_data[IntVars::cons].array(mfi);
246  const Array4<const Real>& cell_prim = S_prim.array(mfi);
247  const Array4<Real> & cell_src = source.array(mfi);
248 
249  const Array4<const Real>& r0 = r_hse.const_array(mfi);
250  const Array4<const Real>& th0 = th_hse.const_array(mfi);
251  const Array4<const Real>& qv0 = qv_hse.const_array(mfi);
252 
253  const Array4<const Real>& z_cc_arr = z_phys_cc->const_array(mfi);
254 
255  const Array4<const Real>& t_blank_arr = (terrain_blank) ? terrain_blank->const_array(mfi) :
256  Array4<const Real>{};
257 
258 
259 
260  // *************************************************************************************
261  // 2. Add radiation source terms to (rho theta)
262  // *************************************************************************************
263  // Every erf.radiation_model (RRTMGP, Simple, TwoStream) writes the
264  // same 2-component (SW, LW) qheating_rates MultiFab (see
265  // Source/ERF_MakeNewArrays.cpp and
266  // Source/Radiation/TwoStream/ERF_TwoStreamRadiation.cpp), so the
267  // injection formula is the same either way. The nullptr check is
268  // defensive: qheating_rates is only allocated when at least one
269  // solver is active, and this can be reached before that allocation
270  // during early init.
271  //
272  // Temporal consistency is guaranteed by where advance_radiation() sits
273  // and by the is_slow_step gating:
274  // 1. qheating_rates[lev] contains heating rates computed from the old
275  // state (t^n) at the beginning of the slow step (called in
276  // ERF::Advance before dycore, see ERF_AdvanceRadiation.cpp).
277  // 2. This source term is ONLY added when is_slow_step==true, ensuring
278  // it is computed once per slow step and NOT repeated in fast
279  // substeps.
280  // 3. The resulting radiative tendency is consistent with the old-state
281  // atmosphere throughout all fast substeps of the current slow step,
282  // providing a single radiative "kick" per slow step.
283  // 4. No adaptation or re-evaluation of radiation occurs within a slow
284  // step; the heating field is frozen at the beginning of the slow
285  // step and applies uniformly to all fast substeps.
286  if (solverChoice.rad_type != RadiationType::None &&
287  is_slow_step && qheating_rates != nullptr) {
288  auto const& qheating_arr = qheating_rates->const_array(mfi);
289  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
290  {
291  // Short-wavelength and long-wavelength radiation source terms
292  // Computed once per slow step from the old state (t^n)
293  cell_src(i,j,k,RhoTheta_comp) += cell_data(i,j,k,Rho_comp) * ( qheating_arr(i,j,k,0) + qheating_arr(i,j,k,1) );
294  });
295  }
296 
297  // *************************************************************************************
298  // 3. Add Rayleigh damping for (rho theta)
299  // *************************************************************************************
300  Real dampcoef = solverChoice.dampingChoice.rayleigh_dampcoef;
301 
302  if (solverChoice.dampingChoice.rayleigh_damp_T) {
303  if ((is_slow_step && !use_Rayleigh_fast) || (!is_slow_step && use_Rayleigh_fast)) {
304  int n = RhoTheta_comp;
305  int nr = Rho_comp;
306  int np = PrimTheta_comp;
307 
308  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
309  {
310  Real theta = cell_prim(i,j,k,np);
311  Real sinesq = d_sinesq_at_lev[k];
312  cell_src(i, j, k, n) -= dampcoef*sinesq * (theta - thetabar[k]) * cell_data(i,j,k,nr);
313  });
314  }
315  }
316 
317  // *************************************************************************************
318  // 4. Add custom forcing for (rho theta)
319  // *************************************************************************************
320  if (solverChoice.custom_rhotheta_forcing && is_slow_step) {
321  const int n = RhoTheta_comp;
322  auto const& rhotheta_src_arr = rhotheta_src->const_array(mfi);
323  if (solverChoice.spatial_rhotheta_forcing)
324  {
325  if (solverChoice.custom_forcing_prim_vars) {
326  const int nr = Rho_comp;
327  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
328  {
329  cell_src(i, j, k, n) += cell_data(i,j,k,nr) * rhotheta_src_arr(i, j, k);
330  });
331  } else {
332  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
333  {
334  cell_src(i, j, k, n) += rhotheta_src_arr(i, j, k);
335  });
336  }
337  } else {
338  if (solverChoice.custom_forcing_prim_vars) {
339  const int nr = Rho_comp;
340  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
341  {
342  cell_src(i, j, k, n) += cell_data(i,j,k,nr) * rhotheta_src_arr(0, 0, k);
343  });
344  } else {
345  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
346  {
347  cell_src(i, j, k, n) += rhotheta_src_arr(0, 0, k);
348  });
349  }
350  }
351  }
352 
353  // *************************************************************************************
354  // 4. Add custom forcing for RhoQ1
355  // *************************************************************************************
356  if (solverChoice.custom_moisture_forcing && is_slow_step) {
357  const int n = RhoQ1_comp;
358  auto const& rhoqt_src_arr = rhoqt_src->const_array(mfi);
359  if (solverChoice.spatial_moisture_forcing)
360  {
361  if (solverChoice.custom_forcing_prim_vars) {
362  const int nr = Rho_comp;
363  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
364  {
365  cell_src(i, j, k, n) += cell_data(i,j,k,nr) * rhoqt_src_arr(i, j, k);
366  });
367  } else {
368  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
369  {
370  cell_src(i, j, k, n) += rhoqt_src_arr(i, j, k);
371  });
372  }
373  } else {
374  if (solverChoice.custom_forcing_prim_vars) {
375  const int nr = Rho_comp;
376  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
377  {
378  cell_src(i, j, k, n) += cell_data(i,j,k,nr) * rhoqt_src_arr(0, 0, k);
379  });
380  } else {
381  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
382  {
383  cell_src(i, j, k, n) += rhoqt_src_arr(0, 0, k);
384  });
385  }
386  }
387  }
388 
389  // *************************************************************************************
390  // 5. Add custom subsidence for (rho theta)
391  // *************************************************************************************
392  if (solverChoice.custom_w_subsidence && is_slow_step && solverChoice.do_theta_advection) {
393  const int n = RhoTheta_comp;
394  if (solverChoice.custom_forcing_prim_vars) {
395  const int nr = Rho_comp;
396  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
397  {
398  Real dzInv = (z_cc_arr) ? one/ (z_cc_arr(i,j,k+1) - z_cc_arr(i,j,k-1)) : myhalf*dxInv[2];
399  Real T_hi = dptr_t_plane(k+1) / dptr_r_plane(k+1);
400  Real T_lo = dptr_t_plane(k-1) / dptr_r_plane(k-1);
401  Real wbar_cc = myhalf * (dptr_wbar_sub[k] + dptr_wbar_sub[k+1]);
402  cell_src(i, j, k, n) -= cell_data(i,j,k,nr) * wbar_cc * (T_hi - T_lo) * dzInv;
403  });
404  } else {
405  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
406  {
407  Real dzInv = (z_cc_arr) ? one/ (z_cc_arr(i,j,k+1) - z_cc_arr(i,j,k-1)) : myhalf*dxInv[2];
408  Real T_hi = dptr_t_plane(k+1) / dptr_r_plane(k+1);
409  Real T_lo = dptr_t_plane(k-1) / dptr_r_plane(k-1);
410  Real wbar_cc = myhalf * (dptr_wbar_sub[k] + dptr_wbar_sub[k+1]);
411  cell_src(i, j, k, n) -= wbar_cc * (T_hi - T_lo) * dzInv;
412  });
413  }
414  }
415 
416  // *************************************************************************************
417  // 5. Add custom subsidence for RhoQ1 and RhoQ2
418  // *************************************************************************************
419  if (solverChoice.custom_w_subsidence && (solverChoice.moisture_type != MoistureType::None) && is_slow_step) {
420  const int nv = RhoQ1_comp;
421  if (solverChoice.custom_forcing_prim_vars) {
422  const int nr = Rho_comp;
423  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
424  {
425  Real dzInv = (z_cc_arr) ? one/ (z_cc_arr(i,j,k+1) - z_cc_arr(i,j,k-1)) : myhalf*dxInv[2];
426  Real Qv_hi = dptr_qv_plane(k+1) / dptr_r_plane(k+1);
427  Real Qv_lo = dptr_qv_plane(k-1) / dptr_r_plane(k-1);
428  Real Qc_hi = dptr_qc_plane(k+1) / dptr_r_plane(k+1);
429  Real Qc_lo = dptr_qc_plane(k-1) / dptr_r_plane(k-1);
430  Real wbar_cc = myhalf * (dptr_wbar_sub[k] + dptr_wbar_sub[k+1]);
431  cell_src(i, j, k, nv ) -= cell_data(i,j,k,nr) * wbar_cc * (Qv_hi - Qv_lo) * dzInv;
432  cell_src(i, j, k, nv+1) -= cell_data(i,j,k,nr) * wbar_cc * (Qc_hi - Qc_lo) * dzInv;
433  });
434  } else {
435  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
436  {
437  Real dzInv = (z_cc_arr) ? one/ (z_cc_arr(i,j,k+1) - z_cc_arr(i,j,k-1)) : myhalf*dxInv[2];
438  Real Qv_hi = dptr_qv_plane(k+1) / dptr_r_plane(k+1);
439  Real Qv_lo = dptr_qv_plane(k-1) / dptr_r_plane(k-1);
440  Real Qc_hi = dptr_qc_plane(k+1) / dptr_r_plane(k+1);
441  Real Qc_lo = dptr_qc_plane(k-1) / dptr_r_plane(k-1);
442  Real wbar_cc = myhalf * (dptr_wbar_sub[k] + dptr_wbar_sub[k+1]);
443  cell_src(i, j, k, nv ) -= wbar_cc * (Qv_hi - Qv_lo) * dzInv;
444  cell_src(i, j, k, nv+1) -= wbar_cc * (Qc_hi - Qc_lo) * dzInv;
445  });
446  }
447  }
448 
449  // *************************************************************************************
450  // 6. Add numerical diffusion for rho and (rho theta)
451  // *************************************************************************************
452  if (l_use_ndiff && is_slow_step)
453  {
454  const Array4<const Real>& mf_mx = mapfac[MapFacType::m_x]->const_array(mfi);
455  const Array4<const Real>& mf_my = mapfac[MapFacType::m_y]->const_array(mfi);
456 
457  // Rho is a special case
458  NumericalDiffusion_Scal(bx, 0, 1, dt, solverChoice.num_diff_coeff,
459  cell_data, cell_data, cell_src, mf_mx, mf_my);
460 
461  // Other scalars proceed as normal
462  NumericalDiffusion_Scal(bx, 1, 1, dt, solverChoice.num_diff_coeff,
463  cell_prim, cell_data, cell_src, mf_mx, mf_my);
464 
465 
466  if (l_use_KE && l_diff_KE) {
467  NumericalDiffusion_Scal(bx, RhoKE_comp, 1, dt, solverChoice.num_diff_coeff,
468  cell_prim, cell_data, cell_src, mf_mx, mf_my);
469  }
470 
472  cell_prim, cell_data, cell_src, mf_mx, mf_my);
473  }
474 
475  // *************************************************************************************
476  // 7. Add sponging
477  // *************************************************************************************
478  if ( (solverChoice.spongeChoice.sponge_type != SpongeType::None) && is_slow_step) {
479  const int n_qstate = S_data[IntVars::cons].nComp() - (NDRY + NSCALARS);
480  ApplySpongeZoneBCsForCC(solverChoice.spongeChoice, geom, bx, cell_src, cell_data, r0, th0, qv0, z_cc_arr, n_qstate);
481  }
482 
483  // *************************************************************************************
484  // 8. Add perturbation
485  // *************************************************************************************
486  if (solverChoice.use_source_perturbation(level) && is_slow_step) {
487  auto m_ixtype = S_data[IntVars::cons].boxArray().ixType(); // Conserved term
488  const amrex::Array4<const amrex::Real>& pert_cell = turbPert.pb_cell[level].const_array(mfi);
489  turbPert.apply_tpi(level, bx, RhoTheta_comp, m_ixtype, cell_src, pert_cell); // Applied as source term
490  }
491 
492  // *************************************************************************************
493  // 10a. Add immersed source terms for terrain
494  // *************************************************************************************
495  if (solverChoice.terrain_type == TerrainType::ImmersedForcing &&
496  ((is_slow_step && !use_ImmersedForcing_fast) || (!is_slow_step && use_ImmersedForcing_fast))) {
497 
498  const Array4<const Real>& u = xvel.array(mfi);
499  const Array4<const Real>& v = yvel.array(mfi);
500 
501  AMREX_ALWAYS_ASSERT(dptr_r_plane_if && dptr_t_plane_if); // Tables must be filled before immersed forcing
502  ImmersedForcingTerrain_Scalar(bx, u, v, cell_data, t_blank_arr, z_cc_arr,
503  cell_src, geom, solverChoice, dptr_r_plane_if, dptr_t_plane_if, time);
504  }
505 
506  // *************************************************************************************
507  // 10b. Add immersed source terms for buildings
508  // *************************************************************************************
509  // geometric properties
510  const Real* dx_arr = geom.CellSize();
511  const Real delta_xy = std::sqrt(dx_arr[0] * dx_arr[1]);
512 
513  if ((solverChoice.buildings_type == BuildingsType::ImmersedForcing) &&
514  ((is_slow_step && !use_ImmersedForcing_fast) || (!is_slow_step && use_ImmersedForcing_fast)) &&
515  (delta_xy <= 50.0)) { // only apply immersed forcing when grid spacing is less than 50m
516 
517  const Array4<const Real>& u = xvel.array(mfi);
518  const Array4<const Real>& v = yvel.array(mfi);
519  const Array4<const Real>& w = zvel.array(mfi);
520 
521  AMREX_ALWAYS_ASSERT(dptr_r_plane_if && dptr_t_plane_if); // Tables must be filled before immersed forcing
522  ImmersedForcingBuildings_Scalar(bx, u, v, w, cell_data, t_blank_arr, z_cc_arr,
523  cell_src, geom, solverChoice, dptr_r_plane_if, dptr_t_plane_if, time);
524  }
525 
526  // *************************************************************************************
527  // Real(11.) Add 4 stream radiation src to RhoTheta
528  // *************************************************************************************
529  if (solverChoice.four_stream_radiation && has_moisture && is_slow_step)
530  {
531  AMREX_ALWAYS_ASSERT((bx.smallEnd(2) == klo) && (bx.bigEnd(2) == khi));
532  Real D = Real(3.75e-6); // [s^-1]
533  Real F0 = 70; // [W/m^2]
534  Real F1 = 22; // [W/m^2]
535  Real krad = 85; // [m^2 kg^-1]
536  Real qt_i = Real(0.008);
537 
538  Box xybx = makeSlab(bx,2,klo);
539  ParallelFor(xybx, [=]
540  AMREX_GPU_DEVICE(int i, int j, int /*k*/) noexcept
541  {
542  // Pass 1: total Q integral through the column (also find "i" values).
543  // NOTE: the running integral is kept in a thread-private scalar rather
544  // than a shared nk-length buffer, and is simply re-formed in the
545  // second pass below. The accumulation order is unchanged, so the
546  // values are identical to the old per-level array.
547  Real q_int_inf = zero;
548  Real zi = myhalf * (z_cc_arr(i,j,khi) + z_cc_arr(i,j,khi-1));
549  Real rhoi = myhalf * (cell_data(i,j,khi,Rho_comp) + cell_data(i,j,khi-1,Rho_comp));
550  for (int k(klo+1); k<=khi+1; ++k) {
551  // Average to w-faces when looping w-faces
552  Real dz = (z_cc_arr) ? myhalf * (z_cc_arr(i,j,k) - z_cc_arr(i,j,k-2)) : dx[2];
553  q_int_inf += krad * cell_data(i,j,k-1,Rho_comp) * cell_data(i,j,k-1,RhoQ2_comp) * dz;
554  Real qt_hi = cell_data(i,j,k ,RhoQ1_comp) + cell_data(i,j,k ,RhoQ2_comp);
555  Real qt_lo = cell_data(i,j,k-1,RhoQ1_comp) + cell_data(i,j,k-1,RhoQ2_comp);
556  if ( (qt_lo > qt_i) && (qt_hi < qt_i) ) {
557  zi = myhalf * (z_cc_arr(i,j,k) + z_cc_arr(i,j,k-1));
558  rhoi = myhalf * (cell_data(i,j,k,Rho_comp) + cell_data(i,j,k-1,Rho_comp));
559  }
560  }
561 
562  // Pass 2: re-form the integral level by level, decompose it into the
563  // w-face flux, and difference the flux to get the heating. The flux on
564  // the lower face is carried across iterations so each face is evaluated
565  // once, exactly as when both faces were read from the shared array.
566  Real q_int = zero; // face klo
567  Real z_lo = myhalf * (z_cc_arr(i,j,klo) + z_cc_arr(i,j,klo-1));
568  Real flux_lo = F1*std::exp(-q_int) + F0*std::exp(-(q_int_inf - q_int));
569  if (z_lo > zi) {
570  flux_lo += rhoi * Cp_d * D * ( std::pow(z_lo-zi,Real(4.)/three)/Real(4.) + zi*std::pow(z_lo-zi,one/three) ) ;
571  }
572 
573  for (int k(klo); k<=khi; ++k) {
574  // Advance the integral from face k to face k+1
575  Real dz = (z_cc_arr) ? myhalf * (z_cc_arr(i,j,k+1) - z_cc_arr(i,j,k-1)) : dx[2];
576  q_int += krad * cell_data(i,j,k,Rho_comp) * cell_data(i,j,k,RhoQ2_comp) * dz;
577 
578  Real z_hi = myhalf * (z_cc_arr(i,j,k+1) + z_cc_arr(i,j,k));
579  Real flux_hi = F1*std::exp(-q_int) + F0*std::exp(-(q_int_inf - q_int));
580  if (z_hi > zi) {
581  flux_hi += rhoi * Cp_d * D * ( std::pow(z_hi-zi,Real(4.)/three)/Real(4.) + zi*std::pow(z_hi-zi,one/three) ) ;
582  }
583 
584  // Average to w-faces when looping CC
585  Real dzInv = (z_cc_arr) ? one/ (myhalf * (z_cc_arr(i,j,k+1) - z_cc_arr(i,j,k-1))) : dxInv[2];
586  // NOTE: Fnet = Up - Dn (all fluxes are up here)
587  // dT/dt = dF/dz * (1/(-rho*Cp))
588  Real dTdt = (flux_hi - flux_lo) * dzInv / (-cell_data(i,j,k,Rho_comp)*Cp_d);
589  Real qv = cell_data(i,j,k,RhoQ1_comp)/cell_data(i,j,k,Rho_comp);
590  Real iexner = one/getExnergivenRTh(cell_data(i,j,k,RhoTheta_comp), RdoCp, qv);
591  // Convert dT/dt to dTheta/dt and multiply rho
592  cell_src(i,j,k,RhoTheta_comp) += cell_data(i,j,k,Rho_comp) * dTdt * iexner;
593 
594  flux_lo = flux_hi;
595  }
596  });
597  }
598  } // mfi
599  } // OMP
600 }
void ApplySpongeZoneBCsForCC(const SpongeChoice &spongeChoice, const Geometry geom, const Box &bx, const Array4< Real > &cell_rhs, const Array4< const Real > &cell_data, const Array4< const Real > &r0, const Array4< const Real > &th0, const Array4< const Real > &qv0, const Array4< const Real > &z_phys_cc, int n_qstate)
Apply sponge zone damping to cell-centered state variables.
Definition: ERF_ApplySpongeZoneBCs.cpp:21
constexpr amrex::Real Cp_d
Definition: ERF_Constants.H:36
constexpr amrex::Real RdoCp
Definition: ERF_Constants.H:41
@ thetabar
Definition: ERF_DataStruct.H:179
@ m_y
Definition: ERF_DataStruct.H:30
@ m_x
Definition: ERF_DataStruct.H:29
DirectionSelector< 2 > ZDir
Definition: ERF_DirectionSelector.H:55
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
void ImmersedForcingTerrain_Scalar(const Box &bx, const Array4< const Real > &u, const Array4< const Real > &v, const Array4< const Real > &cell_data, const Array4< const Real > &t_blank_arr, const Array4< const Real > &z_cc_arr, const Array4< Real > &cell_src, const Geometry &geom, const SolverChoice &solverChoice, const Table1D< Real > &r_avg, const Table1D< Real > &t_avg, const Real time)
Definition: ERF_ImmersedForcing.cpp:949
void ImmersedForcingBuildings_Scalar(const Box &bx, const Array4< const Real > &u, const Array4< const Real > &v, const Array4< const Real > &w, const Array4< const Real > &cell_data, const Array4< const Real > &t_blank_arr, const Array4< const Real > &z_cc_arr, const Array4< Real > &cell_src, const Geometry &geom, const SolverChoice &solverChoice, const Table1D< Real > &r_avg, const Table1D< Real > &t_avg, const Real time)
Definition: ERF_ImmersedForcing.cpp:1084
#define RhoScalar_comp
Definition: ERF_IndexDefines.H:43
#define Rho_comp
Definition: ERF_IndexDefines.H:39
#define RhoTheta_comp
Definition: ERF_IndexDefines.H:40
#define NDRY
Definition: ERF_IndexDefines.H:13
#define RhoQ2_comp
Definition: ERF_IndexDefines.H:46
#define NSCALARS
Definition: ERF_IndexDefines.H:16
#define PrimTheta_comp
Definition: ERF_IndexDefines.H:58
#define RhoQ1_comp
Definition: ERF_IndexDefines.H:45
#define RhoKE_comp
Definition: ERF_IndexDefines.H:41
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
const int khi
Definition: ERF_InitCustomPert_Bubble.H:21
AMREX_ALWAYS_ASSERT(bx.length()[2]==khi+1)
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 three
Definition: ERF_NumericalConstants.H:32
constexpr amrex::Real one
Definition: ERF_NumericalConstants.H:30
constexpr amrex::Real zero
Definition: ERF_NumericalConstants.H:29
constexpr amrex::Real myhalf
Definition: ERF_NumericalConstants.H:34
void NumericalDiffusion_Scal(const Box &bx, const int start_comp, const int num_comp, const double dt, const Real num_diff_coeff, const Array4< const Real > &prim_data, const Array4< const Real > &cell_data, const Array4< Real > &rhs, const Array4< const Real > &mfx_arr, const Array4< const Real > &mfy_arr)
Definition: ERF_NumericalDiffusion.cpp:18
Real w
Definition: ERF_Plotfile2DInterpolator.cpp:22
AMREX_FORCE_INLINE IntVect offset(const int face_dir, const int normal)
Definition: ERF_ReadBndryPlanes.cpp:32
amrex::Real Real
Definition: ERF_ShocInterface.H:19
AMREX_FORCE_INLINE amrex::IntVect TileNoZ()
Definition: ERF_TileNoZ.H:11
Definition: ERF_PlaneAverage.H:14
@ qv0_comp
Definition: ERF_IndexDefines.H:80
@ th0_comp
Definition: ERF_IndexDefines.H:79
@ r0_comp
Definition: ERF_IndexDefines.H:76
@ cons
Definition: ERF_IndexDefines.H:232
@ theta
Definition: ERF_SLM.H:19
@ qv
Definition: ERF_Kessler.H:31
@ nr
Definition: ERF_Morrison.H:47
@ xvel
Definition: ERF_IndexDefines.H:215
@ cons
Definition: ERF_IndexDefines.H:214
@ zvel
Definition: ERF_IndexDefines.H:217
@ yvel
Definition: ERF_IndexDefines.H:216
@ dz
Definition: ERF_AdvanceWDM6.cpp:272
@ zi
Definition: ERF_AdvanceWDM6.cpp:276
real(c_double), private rhoi
Definition: ERF_module_mp_morr_two_moment.F90:188
real(kind=kind_phys), parameter, private r0
Definition: ERF_module_mp_wdm6.F90:75
RayleighDampingType rayleigh_damping_type
Selected Rayleigh damping time-integration treatment.
Definition: ERF_DampingStruct.H:111
amrex::Real rayleigh_dampcoef
Rayleigh damping inverse time scale [1/s].
Definition: ERF_DampingStruct.H:98
bool rayleigh_damp_T
Whether Rayleigh damping is applied to potential temperature.
Definition: ERF_DampingStruct.H:97
amrex::Vector< TurbChoice > turbChoice
Turbulence options for each AMR level.
Definition: ERF_DataStruct.H:1974
MoistureType moisture_type
Moisture or microphysics model.
Definition: ERF_DataStruct.H:2237
int ave_plane
Averaging plane index used by diagnostics.
Definition: ERF_DataStruct.H:2260
amrex::Real num_diff_coeff
Numerical diffusion coefficient after input scaling.
Definition: ERF_DataStruct.H:2234
bool do_theta_advection
Whether custom vertical subsidence is applied to rho-theta.
Definition: ERF_DataStruct.H:2078
bool spatial_moisture_forcing
Whether spatially varying moisture forcing is enabled.
Definition: ERF_DataStruct.H:2083
static MeshType mesh_type
Vertical mesh representation.
Definition: ERF_DataStruct.H:1958
SpongeChoice spongeChoice
Sponge-layer options.
Definition: ERF_DataStruct.H:1973
bool four_stream_radiation
Whether the four-stream radiation approximation is enabled.
Definition: ERF_DataStruct.H:2029
DampingChoice dampingChoice
Damping-related options.
Definition: ERF_DataStruct.H:1972
static TerrainType terrain_type
Terrain or immersed-boundary representation.
Definition: ERF_DataStruct.H:1949
static BuildingsType buildings_type
Building representation.
Definition: ERF_DataStruct.H:1952
bool custom_rhotheta_forcing
Whether custom rho-theta forcing is enabled.
Definition: ERF_DataStruct.H:2075
bool spatial_rhotheta_forcing
Whether spatially varying rho-theta forcing is enabled.
Definition: ERF_DataStruct.H:2082
bool use_source_perturbation(int lev) const
Query whether source-term turbulent perturbations are enabled on a level.
Definition: ERF_DataStruct.H:2158
bool custom_w_subsidence
Whether custom vertical subsidence is enabled.
Definition: ERF_DataStruct.H:2077
bool custom_moisture_forcing
Whether custom moisture forcing is enabled.
Definition: ERF_DataStruct.H:2076
RadiationType rad_type
Radiation model.
Definition: ERF_DataStruct.H:2241
bool immersed_forcing_substep
Whether immersed-forcing source terms are applied only during substeps.
Definition: ERF_DataStruct.H:2032
bool custom_forcing_prim_vars
Whether custom forcing operates on primitive variables.
Definition: ERF_DataStruct.H:2081
bool use_num_diff
Whether sixth-order numerical diffusion is enabled.
Definition: ERF_DataStruct.H:2233
static SpongeType sponge_type
Selected sponge damping model.
Definition: ERF_SpongeStruct.H:100
Definition: ERF_TurbStruct.H:115
bool diffuse_tke_3D
Whether three-dimensional numerical diffusion is applied to TKE/QKE.
Definition: ERF_TurbStruct.H:927
bool use_tke
Whether any TKE or QKE closure is active.
Definition: ERF_TurbStruct.H:841
amrex::Vector< amrex::MultiFab > pb_cell
Per-cell perturbation amplitude storage.
Definition: ERF_TurbPertStruct.H:763
void apply_tpi(const int &lev, const amrex::Box &vbx, const int &comp, const amrex::IndexType &m_ixtype, const amrex::Array4< amrex::Real > &src_arr, const amrex::Array4< amrex::Real const > &pert_cell)
Apply stored turbulent perturbations to a source or state array.
Definition: ERF_TurbPertStruct.H:409
Here is the call graph for this function: