ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
ERF_MakeSources.cpp File Reference
#include <AMReX_MultiFab.H>
#include <AMReX_ArrayLim.H>
#include <AMReX_BCRec.H>
#include <AMReX_TableData.H>
#include <AMReX_GpuContainers.H>
#include <ERF_NumericalDiffusion.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, Real dt, Real time, 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 *qheating_rates, MultiFab *terrain_blank, const Geometry geom, const SolverChoice &solverChoice, Vector< std::unique_ptr< MultiFab >> &mapfac, const Real *dptr_rhotheta_src, const Real *dptr_rhoqt_src, const Real *dptr_wbar_sub, const Vector< Real * > d_rayleigh_ptrs_at_lev, InputSoundingData &input_sounding_data, TurbulentPerturbation &turbPert, bool is_slow_step)
 

Function Documentation

◆ make_sources()

void make_sources ( int  level,
int  ,
Real  dt,
Real  time,
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 *  qheating_rates,
MultiFab *  terrain_blank,
const Geometry  geom,
const SolverChoice solverChoice,
Vector< std::unique_ptr< MultiFab >> &  mapfac,
const Real dptr_rhotheta_src,
const Real dptr_rhoqt_src,
const Real dptr_wbar_sub,
const Vector< Real * >  d_rayleigh_ptrs_at_lev,
InputSoundingData input_sounding_data,
TurbulentPerturbation turbPert,
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
55 {
56  BL_PROFILE_REGION("erf_make_sources()");
57 
58  // *****************************************************************************
59  // Initialize source to zero since we re-compute it every RK stage
60  // *****************************************************************************
61  if (is_slow_step) {
62  source.setVal(0.);
63  } else {
64  source.setVal(0.0,Rho_comp,2);
65  }
66 
67  const bool l_use_ndiff = solverChoice.use_num_diff;
68 
69  TurbChoice tc = solverChoice.turbChoice[level];
70  const bool l_use_KE = tc.use_tke;
71  const bool l_diff_KE = tc.diffuse_tke_3D;
72 
73  const Box& domain = geom.Domain();
74 
75  const GpuArray<Real, AMREX_SPACEDIM> dxInv = geom.InvCellSizeArray();
76  const GpuArray<Real, AMREX_SPACEDIM> dx = geom.CellSizeArray();
77 
78  MultiFab r_hse (base_state, make_alias, BaseState::r0_comp , 1);
79 
80  Real* thetabar = d_rayleigh_ptrs_at_lev[Rayleigh::thetabar];
81 
82  // flags to apply certain source terms in substep call only
83  bool use_Rayleigh_fast = solverChoice.rayleigh_damp_substep;
84  bool use_ImmersedForcing_fast = solverChoice.immersed_forcing_substep;
85 
86  // flag for a moisture model
87  bool has_moisture = (solverChoice.moisture_type != MoistureType::None);
88 
89  // *****************************************************************************
90  // Planar averages for subsidence terms
91  // *****************************************************************************
92  Table1D<Real> dptr_r_plane, dptr_t_plane, dptr_qv_plane, dptr_qc_plane;
93  TableData<Real, 1> r_plane_tab, t_plane_tab, qv_plane_tab, qc_plane_tab;
94  bool compute_averages = false;
95  compute_averages = compute_averages ||
96  ( is_slow_step && (dptr_wbar_sub || solverChoice.nudging_from_input_sounding) );
97 
98  if (compute_averages)
99  {
100  //
101  // The call to "compute_averages" currently does all the components in one call
102  // We can then extract each component separately with the "line_average" call
103  //
104  // We need just one ghost cell in the vertical
105  //
106  IntVect ng_c(S_data[IntVars::cons].nGrowVect()); ng_c[2] = 1;
107  //
108  // With no moisture we only (rho) and (rho theta); with moisture we also do qv and qc
109  // We use the alias here to control ncomp inside the PlaneAverage
110  //
111  int ncomp = (!has_moisture) ? 2 : RhoQ2_comp+1;
112  MultiFab cons(S_data[IntVars::cons], make_alias, 0, ncomp);
113 
114  PlaneAverage cons_ave(&cons, geom, solverChoice.ave_plane, ng_c);
115  cons_ave.compute_averages(ZDir(), cons_ave.field());
116 
117  int ncell = cons_ave.ncell_line();
118 
119  Gpu::HostVector< Real> r_plane_h(ncell);
120  Gpu::DeviceVector< Real> r_plane_d(ncell);
121 
122  Gpu::HostVector< Real> t_plane_h(ncell);
123  Gpu::DeviceVector< Real> t_plane_d(ncell);
124 
125  cons_ave.line_average(Rho_comp , r_plane_h);
126  cons_ave.line_average(RhoTheta_comp, t_plane_h);
127 
128  Gpu::copyAsync(Gpu::hostToDevice, r_plane_h.begin(), r_plane_h.end(), r_plane_d.begin());
129  Gpu::copyAsync(Gpu::hostToDevice, t_plane_h.begin(), t_plane_h.end(), t_plane_d.begin());
130 
131  Real* dptr_r = r_plane_d.data();
132  Real* dptr_t = t_plane_d.data();
133 
134  Box tdomain = domain; tdomain.grow(2,ng_c[2]);
135  r_plane_tab.resize({tdomain.smallEnd(2)}, {tdomain.bigEnd(2)});
136  t_plane_tab.resize({tdomain.smallEnd(2)}, {tdomain.bigEnd(2)});
137 
138  int offset = ng_c[2];
139 
140  dptr_r_plane = r_plane_tab.table();
141  dptr_t_plane = t_plane_tab.table();
142  ParallelFor(ncell, [=] AMREX_GPU_DEVICE (int k) noexcept
143  {
144  dptr_r_plane(k-offset) = dptr_r[k];
145  dptr_t_plane(k-offset) = dptr_t[k];
146  });
147 
148  if (has_moisture)
149  {
150  Gpu::HostVector< Real> qv_plane_h(ncell), qc_plane_h(ncell);
151  Gpu::DeviceVector<Real> qv_plane_d(ncell), qc_plane_d(ncell);
152 
153  // Water vapor
154  cons_ave.line_average(RhoQ1_comp, qv_plane_h);
155  Gpu::copyAsync(Gpu::hostToDevice, qv_plane_h.begin(), qv_plane_h.end(), qv_plane_d.begin());
156 
157  // Cloud water
158  cons_ave.line_average(RhoQ2_comp, qc_plane_h);
159  Gpu::copyAsync(Gpu::hostToDevice, qc_plane_h.begin(), qc_plane_h.end(), qc_plane_d.begin());
160 
161  Real* dptr_qv = qv_plane_d.data();
162  Real* dptr_qc = qc_plane_d.data();
163 
164  qv_plane_tab.resize({tdomain.smallEnd(2)}, {tdomain.bigEnd(2)});
165  qc_plane_tab.resize({tdomain.smallEnd(2)}, {tdomain.bigEnd(2)});
166 
167  dptr_qv_plane = qv_plane_tab.table();
168  dptr_qc_plane = qc_plane_tab.table();
169  ParallelFor(ncell, [=] AMREX_GPU_DEVICE (int k) noexcept
170  {
171  dptr_qv_plane(k-offset) = dptr_qv[k];
172  dptr_qc_plane(k-offset) = dptr_qc[k];
173  });
174  }
175  }
176 
177  // *****************************************************************************
178  // Radiation flux vector for four stream approximation
179  // *****************************************************************************
180  // NOTE: The fluxes live on w-faces
181  int klo = domain.smallEnd(0);
182  int khi = domain.bigEnd(2);
183  int nk = khi - klo + 2;
184  Gpu::DeviceVector<Real> radiation_flux(nk,0.0);
185  Gpu::DeviceVector<Real> q_integral(nk,0.0);
186  Real* rad_flux = radiation_flux.data();
187  Real* q_int = q_integral.data();
188 
189  // *****************************************************************************
190  // Define source term for cell-centered conserved variables, from
191  // 1. user-defined source terms for (rho theta) and (rho q_t)
192  // 2. radiation for (rho theta)
193  // 3. Rayleigh damping for (rho theta)
194  // 4. custom forcing for (rho theta) and (rho Q1)
195  // 5. custom subsidence for (rho theta) and (rho Q1)
196  // 6. numerical diffusion for (rho theta)
197  // 7. sponging
198  // 8. turbulent perturbation
199  // 9. nudging towards input sounding values (only for theta)
200  // 10. Immersed forcing
201  // 11. Four stream radiation source for (rho theta)
202  // *****************************************************************************
203 
204  // ***********************************************************************************************
205  // Add remaining source terms
206  // ***********************************************************************************************
207 #ifdef _OPENMP
208 #pragma omp parallel if (Gpu::notInLaunchRegion())
209 #endif
210  {
211  for ( MFIter mfi(S_data[IntVars::cons],TileNoZ()); mfi.isValid(); ++mfi)
212  {
213  Box bx = mfi.tilebox();
214 
215  const Array4<const Real>& cell_data = S_data[IntVars::cons].array(mfi);
216  const Array4<const Real>& cell_prim = S_prim.array(mfi);
217  const Array4<Real> & cell_src = source.array(mfi);
218 
219  const Array4<const Real>& r0 = r_hse.const_array(mfi);
220 
221  const Array4<const Real>& z_cc_arr = z_phys_cc->const_array(mfi);
222 
223  const Array4<const Real>& t_blank_arr = (terrain_blank) ? terrain_blank->const_array(mfi) :
224  Array4<const Real>{};
225 
226 
227  // *************************************************************************************
228  // 2. Add radiation source terms to (rho theta)
229  // *************************************************************************************
230  if (solverChoice.rad_type != RadiationType::None && is_slow_step) {
231  auto const& qheating_arr = qheating_rates->const_array(mfi);
232  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
233  {
234  // Short-wavelength and long-wavelength radiation source terms
235  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) );
236  });
237  }
238 
239 
240  // *************************************************************************************
241  // 3. Add Rayleigh damping for (rho theta)
242  // *************************************************************************************
243  Real ztop = solverChoice.rayleigh_ztop;
244  Real zdamp = solverChoice.rayleigh_zdamp;
245  Real dampcoef = solverChoice.rayleigh_dampcoef;
246 
247  if ((is_slow_step && !use_Rayleigh_fast) || (!is_slow_step && use_Rayleigh_fast)) {
248  if (solverChoice.rayleigh_damp_T) {
249  int n = RhoTheta_comp;
250  int nr = Rho_comp;
251  int np = PrimTheta_comp;
252  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
253  {
254  Real zcc = z_cc_arr(i,j,k);
255  Real zfrac = 1 - (ztop - zcc) / zdamp;
256  if (zfrac > 0) {
257  Real theta = cell_prim(i,j,k,np);
258  Real sinefac = std::sin(PIoTwo*zfrac);
259  cell_src(i, j, k, n) -= dampcoef*sinefac*sinefac * (theta - thetabar[k]) * cell_data(i,j,k,nr);
260  }
261  });
262  }
263  }
264 
265  // *************************************************************************************
266  // 4. Add custom forcing for (rho theta)
267  // *************************************************************************************
268  if (solverChoice.custom_rhotheta_forcing && is_slow_step) {
269  const int n = RhoTheta_comp;
270  if (solverChoice.custom_forcing_prim_vars) {
271  const int nr = Rho_comp;
272  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
273  {
274  cell_src(i, j, k, n) += cell_data(i,j,k,nr) * dptr_rhotheta_src[k];
275  });
276  } else {
277  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
278  {
279  cell_src(i, j, k, n) += dptr_rhotheta_src[k];
280  });
281  }
282  }
283 
284  // *************************************************************************************
285  // 4. Add custom forcing for RhoQ1
286  // *************************************************************************************
287  if (solverChoice.custom_moisture_forcing && is_slow_step) {
288  const int n = RhoQ1_comp;
289  if (solverChoice.custom_forcing_prim_vars) {
290  const int nr = Rho_comp;
291  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
292  {
293  cell_src(i, j, k, n) += cell_data(i,j,k,nr) * dptr_rhoqt_src[k];
294  });
295  } else {
296  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
297  {
298  cell_src(i, j, k, n) += dptr_rhoqt_src[k];
299  });
300  }
301  }
302 
303  // *************************************************************************************
304  // 5. Add custom subsidence for (rho theta)
305  // *************************************************************************************
306  if (solverChoice.custom_w_subsidence && is_slow_step) {
307  const int n = RhoTheta_comp;
308  if (solverChoice.custom_forcing_prim_vars) {
309  const int nr = Rho_comp;
310  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
311  {
312  Real dzInv = (z_cc_arr) ? 1.0/ (z_cc_arr(i,j,k+1) - z_cc_arr(i,j,k-1)) : 0.5*dxInv[2];
313  Real T_hi = dptr_t_plane(k+1) / dptr_r_plane(k+1);
314  Real T_lo = dptr_t_plane(k-1) / dptr_r_plane(k-1);
315  Real wbar_cc = 0.5 * (dptr_wbar_sub[k] + dptr_wbar_sub[k+1]);
316  cell_src(i, j, k, n) -= cell_data(i,j,k,nr) * wbar_cc * (T_hi - T_lo) * dzInv;
317  });
318  } else {
319  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
320  {
321  Real dzInv = (z_cc_arr) ? 1.0/ (z_cc_arr(i,j,k+1) - z_cc_arr(i,j,k-1)) : 0.5*dxInv[2];
322  Real T_hi = dptr_t_plane(k+1) / dptr_r_plane(k+1);
323  Real T_lo = dptr_t_plane(k-1) / dptr_r_plane(k-1);
324  Real wbar_cc = 0.5 * (dptr_wbar_sub[k] + dptr_wbar_sub[k+1]);
325  cell_src(i, j, k, n) -= wbar_cc * (T_hi - T_lo) * dzInv;
326  });
327  }
328  }
329 
330  // *************************************************************************************
331  // 5. Add custom subsidence for RhoQ1 and RhoQ2
332  // *************************************************************************************
333  if (solverChoice.custom_w_subsidence && (solverChoice.moisture_type != MoistureType::None) && is_slow_step) {
334  const int nv = RhoQ1_comp;
335  if (solverChoice.custom_forcing_prim_vars) {
336  const int nr = Rho_comp;
337  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
338  {
339  Real dzInv = (z_cc_arr) ? 1.0/ (z_cc_arr(i,j,k+1) - z_cc_arr(i,j,k-1)) : 0.5*dxInv[2];
340  Real Qv_hi = dptr_qv_plane(k+1) / dptr_r_plane(k+1);
341  Real Qv_lo = dptr_qv_plane(k-1) / dptr_r_plane(k-1);
342  Real Qc_hi = dptr_qc_plane(k+1) / dptr_r_plane(k+1);
343  Real Qc_lo = dptr_qc_plane(k-1) / dptr_r_plane(k-1);
344  Real wbar_cc = 0.5 * (dptr_wbar_sub[k] + dptr_wbar_sub[k+1]);
345  cell_src(i, j, k, nv ) -= cell_data(i,j,k,nr) * wbar_cc * (Qv_hi - Qv_lo) * dzInv;
346  cell_src(i, j, k, nv+1) -= cell_data(i,j,k,nr) * wbar_cc * (Qc_hi - Qc_lo) * dzInv;
347  });
348  } else {
349  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
350  {
351  Real dzInv = (z_cc_arr) ? 1.0/ (z_cc_arr(i,j,k+1) - z_cc_arr(i,j,k-1)) : 0.5*dxInv[2];
352  Real Qv_hi = dptr_qv_plane(k+1) / dptr_r_plane(k+1);
353  Real Qv_lo = dptr_qv_plane(k-1) / dptr_r_plane(k-1);
354  Real Qc_hi = dptr_qc_plane(k+1) / dptr_r_plane(k+1);
355  Real Qc_lo = dptr_qc_plane(k-1) / dptr_r_plane(k-1);
356  Real wbar_cc = 0.5 * (dptr_wbar_sub[k] + dptr_wbar_sub[k+1]);
357  cell_src(i, j, k, nv ) -= wbar_cc * (Qv_hi - Qv_lo) * dzInv;
358  cell_src(i, j, k, nv+1) -= wbar_cc * (Qc_hi - Qc_lo) * dzInv;
359  });
360  }
361  }
362 
363  // *************************************************************************************
364  // 6. Add numerical diffuion for rho and (rho theta)
365  // *************************************************************************************
366  if (l_use_ndiff && is_slow_step) {
367  int sc;
368  int nc;
369 
370  const Array4<const Real>& mf_mx = mapfac[MapFacType::m_x]->const_array(mfi);
371  const Array4<const Real>& mf_my = mapfac[MapFacType::m_y]->const_array(mfi);
372 
373  // Rho is a special case
374  NumericalDiffusion_Scal(bx, sc=0, nc=1, dt, solverChoice.num_diff_coeff,
375  cell_data, cell_data, cell_src, mf_mx, mf_my);
376 
377  // Other scalars proceed as normal
378  NumericalDiffusion_Scal(bx, sc=1, nc=1, dt, solverChoice.num_diff_coeff,
379  cell_prim, cell_data, cell_src, mf_mx, mf_my);
380 
381 
382  if (l_use_KE && l_diff_KE) {
383  NumericalDiffusion_Scal(bx, sc=RhoKE_comp, nc=1, dt, solverChoice.num_diff_coeff,
384  cell_prim, cell_data, cell_src, mf_mx, mf_my);
385  }
386 
388  cell_prim, cell_data, cell_src, mf_mx, mf_my);
389  }
390 
391  // *************************************************************************************
392  // 7. Add sponging
393  // *************************************************************************************
394  if(!(solverChoice.spongeChoice.sponge_type == "input_sponge") && is_slow_step){
395  ApplySpongeZoneBCsForCC(solverChoice.spongeChoice, geom, bx, cell_src, cell_data, r0, z_cc_arr);
396  }
397 
398  // *************************************************************************************
399  // 8. Add perturbation
400  // *************************************************************************************
401  if (solverChoice.pert_type == PerturbationType::Source && is_slow_step) {
402  auto m_ixtype = S_data[IntVars::cons].boxArray().ixType(); // Conserved term
403  const amrex::Array4<const amrex::Real>& pert_cell = turbPert.pb_cell[level].const_array(mfi);
404  turbPert.apply_tpi(level, bx, RhoTheta_comp, m_ixtype, cell_src, pert_cell); // Applied as source term
405  }
406 
407  // *************************************************************************************
408  // 9. Add nudging towards value specified in input sounding
409  // *************************************************************************************
410  if (solverChoice.nudging_from_input_sounding && is_slow_step)
411  {
412  int itime_n = 0;
413  int itime_np1 = 0;
414  Real coeff_n = Real(1.0);
415  Real coeff_np1 = Real(0.0);
416 
417  Real tau_inv = Real(1.0) / input_sounding_data.tau_nudging;
418 
419  int n_sounding_times = input_sounding_data.input_sounding_time.size();
420 
421  for (int nt = 1; nt < n_sounding_times; nt++) {
422  if (time > input_sounding_data.input_sounding_time[nt]) itime_n = nt;
423  }
424  if (itime_n == n_sounding_times-1) {
425  itime_np1 = itime_n;
426  } else {
427  itime_np1 = itime_n+1;
428  coeff_np1 = (time - input_sounding_data.input_sounding_time[itime_n]) /
429  (input_sounding_data.input_sounding_time[itime_np1] - input_sounding_data.input_sounding_time[itime_n]);
430  coeff_n = Real(1.0) - coeff_np1;
431  }
432 
433  const Real* theta_inp_sound_n = input_sounding_data.theta_inp_sound_d[itime_n].dataPtr();
434  const Real* theta_inp_sound_np1 = input_sounding_data.theta_inp_sound_d[itime_np1].dataPtr();
435 
436  const int n = RhoTheta_comp;
437  const int nr = Rho_comp;
438 
439  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
440  {
441  Real nudge = (coeff_n*theta_inp_sound_n[k] + coeff_np1*theta_inp_sound_np1[k]) - (dptr_t_plane(k)/dptr_r_plane(k));
442  nudge *= tau_inv;
443  cell_src(i, j, k, n) += cell_data(i, j, k, nr) * nudge;
444  });
445  }
446 
447  // *************************************************************************************
448  // 10. Add Immersed source terms
449  // *************************************************************************************
450  if (solverChoice.terrain_type == TerrainType::ImmersedForcing &&
451  ((is_slow_step && !use_ImmersedForcing_fast) || (!is_slow_step && use_ImmersedForcing_fast)))
452  {
453  const Array4<const Real>& u = xvel.array(mfi);
454  const Array4<const Real>& v = yvel.array(mfi);
455 
456  // geometric properties
457  const Real* dx_arr = geom.CellSize();
458  const Real dx_x = dx_arr[0];
459  const Real dx_y = dx_arr[1];
460  const Real dx_z = dx_arr[2];
461 
462  const Real alpha_h = solverChoice.if_Cd_scalar;
463  const Real drag_coefficient = alpha_h / std::pow(dx_x*dx_y*dx_z, 1./3.);
465  const Real U_s = 1.0; // unit velocity scale
466 
467  // MOST parameters
468  similarity_funs sfuns;
469  const Real ggg = CONST_GRAV;
470  const Real kappa = KAPPA;
471  const Real z0 = solverChoice.if_z0;
472  const Real tflux = solverChoice.if_surf_temp_flux;
473  const Real init_surf_temp = solverChoice.if_init_surf_temp;
474  const Real surf_heating_rate = solverChoice.if_surf_heating_rate;
475  const Real Olen_in = solverChoice.if_Olen_in;
476 
477  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
478  {
479  const Real t_blank = t_blank_arr(i, j, k);
480  const Real t_blank_above = t_blank_arr(i, j, k+1);
481  const Real ux_cc_2r = 0.5 * (u(i ,j ,k+1) + u(i+1,j ,k+1));
482  const Real uy_cc_2r = 0.5 * (v(i ,j ,k+1) + v(i ,j+1,k+1));
483  const Real h_windspeed2r = std::sqrt(ux_cc_2r * ux_cc_2r + uy_cc_2r * uy_cc_2r);
484 
485  const Real theta = cell_data(i,j,k ,RhoTheta_comp) / cell_data(i,j,k ,Rho_comp);
486  const Real theta_neighbor = cell_data(i,j,k+1,RhoTheta_comp) / cell_data(i,j,k+1,Rho_comp);
487 
488  // SURFACE TEMP AND HEATING/COOLING RATE
489  if (init_surf_temp > 0.0) {
490  if (t_blank > 0 && (t_blank_above == 0.0)) { // force to MOST value
491  const Real surf_temp = init_surf_temp + surf_heating_rate*time/3600;
492  const Real bc_forcing_rt_srf = -(cell_data(i,j,k-1,Rho_comp) * surf_temp - cell_data(i,j,k-1,RhoTheta_comp));
493  cell_src(i, j, k-1, RhoTheta_comp) -= drag_coefficient * U_s * bc_forcing_rt_srf; // k-1
494  }
495  }
496 
497  // SURFACE HEAT FLUX
498  if (tflux != 1e-8){
499  if (t_blank > 0 && (t_blank_above == 0.0)) { // force to MOST value
500  Real psi_m = 0.0;
501  Real psi_h = 0.0;
502  Real psi_h_neighbor = 0.0;
503  Real ustar = h_windspeed2r * kappa / (std::log((1.5) * dx_z / z0) - psi_m);
504  const Real Olen = -ustar * ustar * ustar * theta / (kappa * ggg * tflux + tiny);
505  const Real zeta = (0.5) * dx_z / Olen;
506  const Real zeta_neighbor = (1.5) * dx_z / Olen;
507 
508  // similarity functions
509  psi_m = sfuns.calc_psi_m(zeta);
510  psi_h = sfuns.calc_psi_h(zeta);
511  psi_h_neighbor = sfuns.calc_psi_h(zeta_neighbor);
512  ustar = h_windspeed2r * kappa / (std::log((1.5) * dx_z / z0) - psi_m);
513 
514  // prevent some unphysical math
515  if (!(ustar > 0.0 && !std::isnan(ustar))) { ustar = 0.0; }
516  if (!(ustar < 2.0 && !std::isnan(ustar))) { ustar = 2.0; }
517  if (psi_h_neighbor > std::log(1.5 * dx_z / z0)) { psi_h_neighbor = std::log(1.5 * dx_z / z0); }
518  if (psi_h > std::log(0.5 * dx_z / z0)) { psi_h = std::log(0.5 * dx_z / z0); }
519 
520  // We do not know the actual temperature so use cell above
521  const Real thetastar = theta * ustar * ustar / (kappa * ggg * Olen);
522  const Real surf_temp = theta_neighbor - thetastar / kappa * (std::log((1.5) * dx_z / z0) - psi_h_neighbor);
523  const Real tTarget = surf_temp + thetastar / kappa * (std::log((0.5) * dx_z / z0) - psi_h);
524 
525  const Real bc_forcing_rt = -(cell_data(i,j,k,Rho_comp) * tTarget - cell_data(i,j,k,RhoTheta_comp));
526  cell_src(i, j, k, RhoTheta_comp) -= drag_coefficient * U_s * bc_forcing_rt;
527  }
528  }
529 
530  // OBUKHOV LENGTH
531  if (Olen_in != 1e-8){
532  if (t_blank > 0 && (t_blank_above == 0.0)) { // force to MOST value
533  const Real Olen = Olen_in;
534  const Real zeta = (0.5) * dx_z / Olen;
535  const Real zeta_neighbor = (1.5) * dx_z / Olen;
536 
537  // similarity functions
538  const Real psi_m = sfuns.calc_psi_m(zeta);
539  const Real psi_h = sfuns.calc_psi_h(zeta);
540  const Real psi_h_neighbor = sfuns.calc_psi_h(zeta_neighbor);
541  const Real ustar = h_windspeed2r * kappa / (std::log((1.5) * dx_z / z0) - psi_m);
542 
543  // We do not know the actual temperature so use cell above
544  const Real thetastar = theta * ustar * ustar / (kappa * ggg * Olen);
545  const Real surf_temp = theta_neighbor - thetastar / kappa * (std::log((1.5) * dx_z / z0) - psi_h_neighbor);
546  const Real tTarget = surf_temp + thetastar / kappa * (std::log((0.5) * dx_z / z0) - psi_h);
547 
548  const Real bc_forcing_rt = -(cell_data(i,j,k,Rho_comp) * tTarget - cell_data(i,j,k,RhoTheta_comp));
549  cell_src(i, j, k, RhoTheta_comp) -= drag_coefficient * U_s * bc_forcing_rt;
550  }
551  }
552 
553  });
554  }
555 
556  // *************************************************************************************
557  // 11. Add 4 stream radiation src to RhoTheta
558  // *************************************************************************************
559  if (solverChoice.four_stream_radiation && has_moisture && is_slow_step)
560  {
561  AMREX_ALWAYS_ASSERT((bx.smallEnd(2) == klo) && (bx.bigEnd(2) == khi));
562  Real D = 3.75e-6; // [s^-1]
563  Real F0 = 70; // [W/m^2]
564  Real F1 = 22; // [W/m^2]
565  Real krad = 85; // [m^2 kg^-1]
566  Real qt_i = 0.008;
567 
568  Box xybx = makeSlab(bx,2,klo);
569  ParallelFor(xybx, [=] AMREX_GPU_DEVICE(int i, int j, int /*k*/) noexcept
570  {
571  // Inclusive scan at w-faces for the Q integral (also find "i" values)
572  q_int[0] = 0.0;
573  Real zi = 0.5 * (z_cc_arr(i,j,khi) + z_cc_arr(i,j,khi-1));
574  Real rhoi = 0.5 * (cell_data(i,j,khi,Rho_comp) + cell_data(i,j,khi-1,Rho_comp));
575  for (int k(klo+1); k<=khi+1; ++k) {
576  int lk = k - klo;
577  // Average to w-faces when looping w-faces
578  Real dz = (z_cc_arr) ? 0.5 * (z_cc_arr(i,j,k) - z_cc_arr(i,j,k-2)) : dx[2];
579  q_int[lk] = q_int[lk-1] + krad * cell_data(i,j,k-1,Rho_comp) * cell_data(i,j,k-1,RhoQ2_comp) * dz;
580  Real qt_hi = cell_data(i,j,k ,RhoQ1_comp) + cell_data(i,j,k ,RhoQ2_comp);
581  Real qt_lo = cell_data(i,j,k-1,RhoQ1_comp) + cell_data(i,j,k-1,RhoQ2_comp);
582  if ( (qt_lo > qt_i) && (qt_hi < qt_i) ) {
583  zi = 0.5 * (z_cc_arr(i,j,k) + z_cc_arr(i,j,k-1));
584  rhoi = 0.5 * (cell_data(i,j,k,Rho_comp) + cell_data(i,j,k-1,Rho_comp));
585  }
586  }
587 
588  // Decompose the integral to get the fluxes at w-faces
589  Real q_int_inf = q_int[khi+1];
590  for (int k(klo); k<=khi+1; ++k) {
591  int lk = k - klo;
592  Real z = 0.5 * (z_cc_arr(i,j,k) + z_cc_arr(i,j,k-1));
593  rad_flux[lk] = F1*std::exp(-q_int[lk]) + F0*std::exp(-(q_int_inf - q_int[lk]));
594  if (z > zi) {
595  rad_flux[lk] += rhoi * Cp_d * D * ( std::pow(z-zi,4./3.)/4. + zi*std::pow(z-zi,1./3.) ) ;
596  }
597  }
598 
599  // Compute the radiative heating source
600  for (int k(klo); k<=khi; ++k) {
601  int lk = k - klo;
602  // Average to w-faces when looping CC
603  Real dzInv = (z_cc_arr) ? 1.0/ (0.5 * (z_cc_arr(i,j,k+1) - z_cc_arr(i,j,k-1))) : dxInv[2];
604  // NOTE: Fnet = Up - Dn (all fluxes are up here)
605  // dT/dt = dF/dz * (1/(-rho*Cp))
606  Real dTdt = (rad_flux[lk+1] - rad_flux[lk]) * dzInv / (-cell_data(i,j,k,Rho_comp)*Cp_d);
607  Real qv = cell_data(i,j,k,RhoQ1_comp)/cell_data(i,j,k,Rho_comp);
608  Real iexner = 1./getExnergivenRTh(cell_data(i,j,k,RhoTheta_comp), R_d/Cp_d, qv);
609  // Convert dT/dt to dTheta/dt and multiply rho
610  cell_src(i,j,k,RhoTheta_comp) += cell_data(i,j,k,Rho_comp) * dTdt * iexner;
611  }
612  });
613  }
614  } // mfi
615  } // OMP
616 }
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 > &z_phys_cc)
Definition: ERF_ApplySpongeZoneBCs.cpp:7
constexpr amrex::Real KAPPA
Definition: ERF_Constants.H:20
constexpr amrex::Real Cp_d
Definition: ERF_Constants.H:12
constexpr amrex::Real CONST_GRAV
Definition: ERF_Constants.H:21
constexpr amrex::Real PIoTwo
Definition: ERF_Constants.H:7
constexpr amrex::Real R_d
Definition: ERF_Constants.H:10
@ thetabar
Definition: ERF_DataStruct.H:91
@ m_y
Definition: ERF_DataStruct.H:23
@ m_x
Definition: ERF_DataStruct.H:22
DirectionSelector< 2 > ZDir
Definition: ERF_DirectionSelector.H:38
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real getExnergivenRTh(const amrex::Real rhotheta, const amrex::Real rdOcp, const amrex::Real qv=0.0)
Definition: ERF_EOS.H:156
#define RhoScalar_comp
Definition: ERF_IndexDefines.H:40
#define Rho_comp
Definition: ERF_IndexDefines.H:36
#define RhoTheta_comp
Definition: ERF_IndexDefines.H:37
#define RhoQ2_comp
Definition: ERF_IndexDefines.H:43
#define NSCALARS
Definition: ERF_IndexDefines.H:16
#define PrimTheta_comp
Definition: ERF_IndexDefines.H:50
#define RhoQ1_comp
Definition: ERF_IndexDefines.H:42
#define RhoKE_comp
Definition: ERF_IndexDefines.H:38
void NumericalDiffusion_Scal(const Box &bx, const int start_comp, const int num_comp, const Real 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
AMREX_FORCE_INLINE IntVect offset(const int face_dir, const int normal)
Definition: ERF_ReadBndryPlanes.cpp:28
amrex::Real Real
Definition: ERF_ShocInterface.H:19
AMREX_FORCE_INLINE amrex::IntVect TileNoZ()
Definition: ERF_TileNoZ.H:11
Definition: ERF_PlaneAverage.H:14
@ r0_comp
Definition: ERF_IndexDefines.H:63
@ cons
Definition: ERF_IndexDefines.H:158
@ theta
Definition: ERF_MM5.H:20
@ qv
Definition: ERF_Kessler.H:28
@ nc
Definition: ERF_Morrison.H:44
@ nr
Definition: ERF_Morrison.H:45
@ xvel
Definition: ERF_IndexDefines.H:141
@ cons
Definition: ERF_IndexDefines.H:140
@ yvel
Definition: ERF_IndexDefines.H:142
real(c_double), parameter epsilon
Definition: ERF_module_model_constants.F90:12
real(c_double), private rhoi
Definition: ERF_module_mp_morr_two_moment.F90:188
amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real > > theta_inp_sound_d
Definition: ERF_InputSoundingData.H:407
amrex::Vector< amrex::Real > input_sounding_time
Definition: ERF_InputSoundingData.H:394
amrex::Real tau_nudging
Definition: ERF_InputSoundingData.H:391
bool rayleigh_damp_T
Definition: ERF_DataStruct.H:942
amrex::Real if_init_surf_temp
Definition: ERF_DataStruct.H:964
amrex::Real if_surf_temp_flux
Definition: ERF_DataStruct.H:963
bool rayleigh_damp_substep
Definition: ERF_DataStruct.H:946
amrex::Real rayleigh_zdamp
Definition: ERF_DataStruct.H:944
RadiationType rad_type
Definition: ERF_DataStruct.H:1024
amrex::Real if_z0
Definition: ERF_DataStruct.H:962
bool custom_rhotheta_forcing
Definition: ERF_DataStruct.H:990
bool custom_w_subsidence
Definition: ERF_DataStruct.H:992
bool nudging_from_input_sounding
Definition: ERF_DataStruct.H:998
amrex::Real if_Cd_scalar
Definition: ERF_DataStruct.H:959
amrex::Real rayleigh_ztop
Definition: ERF_DataStruct.H:945
bool immersed_forcing_substep
Definition: ERF_DataStruct.H:955
amrex::Real if_Olen_in
Definition: ERF_DataStruct.H:966
bool use_num_diff
Definition: ERF_DataStruct.H:1013
bool four_stream_radiation
Definition: ERF_DataStruct.H:952
bool custom_moisture_forcing
Definition: ERF_DataStruct.H:991
amrex::Real num_diff_coeff
Definition: ERF_DataStruct.H:1014
amrex::Real if_surf_heating_rate
Definition: ERF_DataStruct.H:965
amrex::Vector< TurbChoice > turbChoice
Definition: ERF_DataStruct.H:905
MoistureType moisture_type
Definition: ERF_DataStruct.H:1020
bool custom_forcing_prim_vars
Definition: ERF_DataStruct.H:994
static TerrainType terrain_type
Definition: ERF_DataStruct.H:885
PerturbationType pert_type
Definition: ERF_DataStruct.H:1010
SpongeChoice spongeChoice
Definition: ERF_DataStruct.H:904
amrex::Real rayleigh_dampcoef
Definition: ERF_DataStruct.H:943
int ave_plane
Definition: ERF_DataStruct.H:1035
std::string sponge_type
Definition: ERF_SpongeStruct.H:58
Definition: ERF_TurbStruct.H:41
bool use_tke
Definition: ERF_TurbStruct.H:402
bool diffuse_tke_3D
Definition: ERF_TurbStruct.H:437
amrex::Vector< amrex::MultiFab > pb_cell
Definition: ERF_TurbPertStruct.H:640
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)
Definition: ERF_TurbPertStruct.H:324
Definition: ERF_MOSTStress.H:41
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real calc_psi_m(amrex::Real zeta) const
Definition: ERF_MOSTStress.H:91
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real calc_psi_h(amrex::Real zeta) const
Definition: ERF_MOSTStress.H:105
Here is the call graph for this function: