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