ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
ERF_MakeMomSources.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_PlaneAverage.H"
#include "ERF_TI_slow_headers.H"
#include "ERF_SrcHeaders.H"
#include "ERF_Utils.H"
Include dependency graph for ERF_MakeMomSources.cpp:

Functions

AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real compute_if_most_target_vel (const amrex::Real u1_2r, const amrex::Real u2_2r, const amrex::Real delta, const amrex::Real z0, const amrex::Real t_blank, const amrex::Real theta_xface, const amrex::Real theta_surf, const amrex::Real tflux_in, const amrex::Real Olen_in, const bool stability_correction)
 
void make_mom_sources (double time_d, double dt, const Vector< MultiFab > &S_data, const MultiFab *z_phys_nd, const MultiFab *z_phys_cc, Vector< Real > &stretched_dz_h, const MultiFab &xvel, const MultiFab &yvel, const MultiFab &wvel, MultiFab &xmom_src, MultiFab &ymom_src, MultiFab &zmom_src, const MultiFab &base_state, MultiFab *forest_drag, MultiFab *terrain_blank, MultiFab *cosPhi_mf, MultiFab *sinPhi_mf, const Geometry geom, const SolverChoice &solverChoice, Vector< std::unique_ptr< MultiFab >> &, const Real *dptr_u_geos, const Real *dptr_v_geos, const Real *dptr_wbar_sub, const Vector< Real * > d_rayleigh_ptrs_at_lev, const amrex::Real *d_sinesq_at_lev, const amrex::Real *d_sinesq_stag_at_lev, const Vector< Real * > d_sponge_ptrs_at_lev, const Vector< MultiFab > *forecast_state_at_lev, InputSoundingData &input_sounding_data, const eb_ &ebfact, bool is_slow_step)
 

Function Documentation

◆ compute_if_most_target_vel()

AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real compute_if_most_target_vel ( const amrex::Real  u1_2r,
const amrex::Real  u2_2r,
const amrex::Real  delta,
const amrex::Real  z0,
const amrex::Real  t_blank,
const amrex::Real  theta_xface,
const amrex::Real  theta_surf,
const amrex::Real  tflux_in,
const amrex::Real  Olen_in,
const bool  stability_correction 
)
30 {
32  Real psi_m = zero;
33  Real psi_h = zero;
34  Real tang_windspeed2r = std::sqrt(u1_2r * u1_2r + u2_2r * u2_2r);
35 
36  Real ustar = tang_windspeed2r * KAPPA / (std::log(1.5 * delta / z0) - psi_m);
37  Real tflux = (tflux_in != Real(1.e-8)) ? tflux_in : -(theta_xface - theta_surf) * ustar * KAPPA / (std::log(1.5 * delta / z0) - psi_h);
38  Real Olen = (Olen_in != Real(1.e-8)) ? Olen_in : -ustar * ustar * ustar * theta_xface / (KAPPA * CONST_GRAV * tflux + tiny);
39  Real zeta = 1.5 * delta / Olen;
40 
41  // similarity functions
42  similarity_funs sfuns;
43  if (stability_correction){
44  psi_m = sfuns.calc_psi_m(zeta);
45  psi_h = sfuns.calc_psi_h(zeta);
46  }
47  ustar = tang_windspeed2r * KAPPA / (std::log(1.5 * delta / z0) - psi_m);
48 
49  // prevent some unphysical math
50  if (!(ustar > zero && !std::isnan(ustar))) { ustar = zero; }
51  if (!(ustar < 2.0 && !std::isnan(ustar))) { ustar = 2.0; }
52  if (psi_m > std::log(myhalf * delta / z0)) { psi_m = std::log(myhalf * delta / z0); }
53 
54  Real uTarget = (1 - t_blank) * ustar / KAPPA * (std::log(myhalf * delta / z0) - psi_m);
55  Real u1Target = uTarget * u1_2r / (tiny + tang_windspeed2r);
56 
57  return u1Target;
58 }
constexpr amrex::Real KAPPA
Definition: ERF_Constants.H:63
constexpr amrex::Real zero
Definition: ERF_Constants.H:8
constexpr amrex::Real myhalf
Definition: ERF_Constants.H:13
constexpr amrex::Real CONST_GRAV
Definition: ERF_Constants.H:64
Real z0
Definition: ERF_InitCustomPertVels_ScalarAdvDiff.H:8
amrex::Real Real
Definition: ERF_ShocInterface.H:19
real(c_double), parameter epsilon
Definition: ERF_module_model_constants.F90:12
Definition: ERF_MOSTStress.H:40
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real calc_psi_m(amrex::Real zeta) const
Definition: ERF_MOSTStress.H:105
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real calc_psi_h(amrex::Real zeta) const
Definition: ERF_MOSTStress.H:124

Referenced by make_mom_sources().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ make_mom_sources()

void make_mom_sources ( double  time_d,
double  dt,
const Vector< MultiFab > &  S_data,
const MultiFab *  z_phys_nd,
const MultiFab *  z_phys_cc,
Vector< Real > &  stretched_dz_h,
const MultiFab &  xvel,
const MultiFab &  yvel,
const MultiFab &  wvel,
MultiFab &  xmom_src,
MultiFab &  ymom_src,
MultiFab &  zmom_src,
const MultiFab &  base_state,
MultiFab *  forest_drag,
MultiFab *  terrain_blank,
MultiFab *  cosPhi_mf,
MultiFab *  sinPhi_mf,
const Geometry  geom,
const SolverChoice solverChoice,
Vector< std::unique_ptr< MultiFab >> &  ,
const Real dptr_u_geos,
const Real dptr_v_geos,
const Real dptr_wbar_sub,
const Vector< Real * >  d_rayleigh_ptrs_at_lev,
const amrex::Real d_sinesq_at_lev,
const amrex::Real d_sinesq_stag_at_lev,
const Vector< Real * >  d_sponge_ptrs_at_lev,
const Vector< MultiFab > *  forecast_state_at_lev,
InputSoundingData input_sounding_data,
const eb_ ebfact,
bool  is_slow_step 
)

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

Parameters
[in]timecurrent time
[in]dtcurrent slow or fast timestep size
[in]S_datacurrent solution
[in]xvelx-component of velocity
[in]yvely-component of velocity
[in]xmom_srcsource terms for x-momentum
[in]ymom_srcsource terms for y-momentum
[in]zmom_srcsource terms for z-momentum
[in]geomContainer for geometric information
[in]solverChoiceContainer for solver parameters
[in]mapfacmap factors
[in]dptr_u_geoscustom geostrophic wind profile
[in]dptr_v_geoscustom geostrophic wind profile
[in]dptr_wbar_subsubsidence source term
[in]d_rayleigh_ptrs_at_levVector of {strength of Rayleigh damping, reference value for xvel/yvel/zvel/theta} used to define Rayleigh damping
[in]d_sinesq_at_levsin( (pi/2) (z-z_t)/(damping depth)) at cell centers
[in]d_sinesq_stag_at_levsin( (pi/2) (z-z_t)/(damping depth)) at z-faces
113 {
114  BL_PROFILE_REGION("erf_make_mom_sources()");
115 
116  Real time = static_cast<Real>(time_d);
117 
118  Box domain(geom.Domain());
119  const GpuArray<Real, AMREX_SPACEDIM> dxInv = geom.InvCellSizeArray();
120 
121  // Initialize sources to zero each time we may use them
122  xmom_src.setVal(0.0);
123  ymom_src.setVal(0.0);
124  zmom_src.setVal(0.0);
125 
126  MultiFab r_hse (base_state, make_alias, BaseState::r0_comp , 1);
127 
128  // flags to apply certain source terms in substep call only
129  bool use_Rayleigh_fast_uv = ( (solverChoice.dampingChoice.rayleigh_damping_type == RayleighDampingType::FastExplicit) ||
131  bool use_Rayleigh_fast_w = (solverChoice.dampingChoice.rayleigh_damping_type == RayleighDampingType::FastExplicit);
132  bool use_canopy_fast = solverChoice.forest_substep;
133  bool use_ImmersedForcing_fast = solverChoice.immersed_forcing_substep;
134 
135  // *****************************************************************************
136  // Define source term for all three components of momenta from
137  // 1. Coriolis forcing for (xmom,ymom,zmom)
138  // 2. Rayleigh damping for (xmom,ymom,zmom)
139  // 3. Constant / height-dependent geostrophic forcing
140  // 4. Subsidence
141  // 5. Nudging towards input sounding data
142  // 6. Numerical diffusion for (xmom,ymom,zmom)
143  // 7. Sponge
144  // 8. Forest canopy
145  // 9a. Immersed forcing for terrain
146  // 9b. Immersed forcing for buildings
147  // 10. Constant mass flux
148  // *****************************************************************************
149  // NOTE: buoyancy is now computed in a separate routine - it should not appear here
150  // *****************************************************************************
151  //const bool l_use_ndiff = solverChoice.use_num_diff;
152 
153  if (solverChoice.terrain_type == TerrainType::ImmersedForcing) {
154  if (solverChoice.do_forest_drag) {
155  amrex::Error(" Currently forest canopy cannot be used with immersed forcing");
156  }
157  }
158 
159 
160  // *****************************************************************************
161  // Data for Coriolis forcing
162  // *****************************************************************************
163  auto use_coriolis = solverChoice.use_coriolis;
164  auto coriolis_factor = solverChoice.coriolis_factor;
165  auto cosphi = solverChoice.cosphi;
166  auto sinphi = solverChoice.sinphi;
167  auto var_coriolis = solverChoice.variable_coriolis;
168 
169  // *****************************************************************************
170  // Flag for Geostrophic forcing
171  // *****************************************************************************
172  auto abl_geo_forcing = solverChoice.abl_geo_forcing;
173  auto geo_wind_profile = solverChoice.have_geo_wind_profile;
174 
175  // *****************************************************************************
176  // Data for Rayleigh damping
177  // *****************************************************************************
178  auto rayleigh_damp_U = solverChoice.dampingChoice.rayleigh_damp_U;
179  auto rayleigh_damp_V = solverChoice.dampingChoice.rayleigh_damp_V;
180  auto rayleigh_damp_W = solverChoice.dampingChoice.rayleigh_damp_W;
181 
182  Real* ubar = d_rayleigh_ptrs_at_lev[Rayleigh::ubar];
183  Real* vbar = d_rayleigh_ptrs_at_lev[Rayleigh::vbar];
184  Real* wbar = d_rayleigh_ptrs_at_lev[Rayleigh::wbar];
185 
186  // *****************************************************************************
187  // Data for constant mass flux
188  // *****************************************************************************
189  bool enforce_massflux_x = (solverChoice.const_massflux_u != 0);
190  bool enforce_massflux_y = (solverChoice.const_massflux_v != 0);
191  Real U_target = solverChoice.const_massflux_u;
192  Real V_target = solverChoice.const_massflux_v;
193  int massflux_klo = solverChoice.massflux_klo;
194  int massflux_khi = solverChoice.massflux_khi;
195 
196  // These will be updated by integrating through the planar average profiles
197  Real rhoUA_target{0};
198  Real rhoVA_target{0};
199  Real rhoUA{0};
200  Real rhoVA{0};
201 
202  // *****************************************************************************
203  // Planar averages for subsidence, nudging, or constant mass flux
204  // *****************************************************************************
205  Table1D<Real> dptr_r_plane, dptr_u_plane, dptr_v_plane;
206  TableData<Real, 1> r_plane_tab, u_plane_tab, v_plane_tab;
207 
208  if (is_slow_step && (dptr_wbar_sub || solverChoice.nudging_from_input_sounding ||
209  enforce_massflux_x || enforce_massflux_y))
210  {
211  // The plane averaging operates at fixed z not fixed height so is not correct for variable dz
212  AMREX_ALWAYS_ASSERT(solverChoice.mesh_type != MeshType::VariableDz);
213 
214  const int offset = 1;
215  const int u_offset = 1;
216  const int v_offset = 1;
217 
218  //
219  // We use the alias here to control ncomp inside the PlaneAverage
220  //
221  MultiFab cons(S_data[IntVars::cons], make_alias, 0, 1);
222 
223  IntVect ng_c = S_data[IntVars::cons].nGrowVect(); ng_c[2] = offset;
224  PlaneAverage r_ave(&cons, geom, solverChoice.ave_plane, ng_c);
225  r_ave.compute_averages(ZDir(), r_ave.field());
226 
227  int ncell = r_ave.ncell_line();
228  Gpu::HostVector< Real> r_plane_h(ncell);
229  Gpu::DeviceVector< Real> r_plane_d(ncell);
230 
231  r_ave.line_average(Rho_comp, r_plane_h);
232 
233  Gpu::copyAsync(Gpu::hostToDevice, r_plane_h.begin(), r_plane_h.end(), r_plane_d.begin());
234 
235  Real* dptr_r = r_plane_d.data();
236 
237  Box tdomain = domain; tdomain.grow(2,ng_c[2]);
238  r_plane_tab.resize({tdomain.smallEnd(2)}, {tdomain.bigEnd(2)});
239 
240  dptr_r_plane = r_plane_tab.table();
241  ParallelFor(ncell, [=] AMREX_GPU_DEVICE (int k) noexcept
242  {
243  dptr_r_plane(k-offset) = dptr_r[k];
244  });
245 
246  // U and V momentum
247  IntVect ng_u = S_data[IntVars::xmom].nGrowVect(); ng_u[2] = u_offset;
248  PlaneAverage u_ave(&(S_data[IntVars::xmom]), geom, solverChoice.ave_plane, ng_u);
249 
250  IntVect ng_v = S_data[IntVars::ymom].nGrowVect(); ng_v[2] = v_offset;
251  PlaneAverage v_ave(&(S_data[IntVars::ymom]), geom, solverChoice.ave_plane, ng_v);
252 
253  u_ave.compute_averages(ZDir(), u_ave.field());
254  v_ave.compute_averages(ZDir(), v_ave.field());
255 
256  int u_ncell = u_ave.ncell_line();
257  int v_ncell = v_ave.ncell_line();
258  Gpu::HostVector< Real> u_plane_h(u_ncell), v_plane_h(v_ncell);
259  Gpu::DeviceVector< Real> u_plane_d(u_ncell), v_plane_d(v_ncell);
260 
261  u_ave.line_average(0, u_plane_h);
262  v_ave.line_average(0, v_plane_h);
263 
264  Gpu::copyAsync(Gpu::hostToDevice, u_plane_h.begin(), u_plane_h.end(), u_plane_d.begin());
265  Gpu::copyAsync(Gpu::hostToDevice, v_plane_h.begin(), v_plane_h.end(), v_plane_d.begin());
266 
267  Real* dptr_u = u_plane_d.data();
268  Real* dptr_v = v_plane_d.data();
269 
270  Box udomain = domain; udomain.grow(2,ng_u[2]);
271  Box vdomain = domain; vdomain.grow(2,ng_v[2]);
272  u_plane_tab.resize({udomain.smallEnd(2)}, {udomain.bigEnd(2)});
273  v_plane_tab.resize({vdomain.smallEnd(2)}, {vdomain.bigEnd(2)});
274 
275  dptr_u_plane = u_plane_tab.table();
276  ParallelFor(u_ncell, [=] AMREX_GPU_DEVICE (int k) noexcept
277  {
278  dptr_u_plane(k-u_offset) = dptr_u[k];
279  });
280 
281  dptr_v_plane = v_plane_tab.table();
282  ParallelFor(v_ncell, [=] AMREX_GPU_DEVICE (int k) noexcept
283  {
284  dptr_v_plane(k-v_offset) = dptr_v[k];
285  });
286 
287  // sum in z for massflux adjustment
288  if (enforce_massflux_x || enforce_massflux_y) {
289  Real Lx = geom.ProbHi(0) - geom.ProbLo(0);
290  Real Ly = geom.ProbHi(1) - geom.ProbLo(1);
291 
292  if (solverChoice.mesh_type == MeshType::ConstantDz) {
293  // note: massflux_khi corresponds to unstaggered indices in this case
294  rhoUA = std::accumulate(u_plane_h.begin() + u_offset + massflux_klo,
295  u_plane_h.begin() + u_offset + massflux_khi+1, zero);
296  rhoVA = std::accumulate(v_plane_h.begin() + v_offset + massflux_klo,
297  v_plane_h.begin() + v_offset + massflux_khi+1, zero);
298  rhoUA_target = std::accumulate(r_plane_h.begin() + offset + massflux_klo,
299  r_plane_h.begin() + offset + massflux_khi+1, zero);
300  rhoVA_target = rhoUA_target;
301 
302  rhoUA *= geom.CellSize(2) * Ly;
303  rhoVA *= geom.CellSize(2) * Lx;
304  rhoUA_target *= geom.CellSize(2) * Ly;
305  rhoVA_target *= geom.CellSize(2) * Lx;
306 
307  } else if (solverChoice.mesh_type == MeshType::StretchedDz) {
308  // note: massflux_khi corresponds to staggered indices in this case
309  for (int k=massflux_klo; k < massflux_khi; ++k) {
310  rhoUA += u_plane_h[k + u_offset] * stretched_dz_h[k];
311  rhoVA += v_plane_h[k + v_offset] * stretched_dz_h[k];
312  rhoUA_target += r_plane_h[k + offset] * stretched_dz_h[k];
313  }
314  rhoVA_target = rhoUA_target;
315 
316  rhoUA *= Ly;
317  rhoVA *= Lx;
318  rhoUA_target *= Ly;
319  rhoVA_target *= Lx;
320  }
321 
322  // at this point, this is integrated rho*dA
323  rhoUA_target *= U_target;
324  rhoVA_target *= V_target;
325 
326  Print() << "Integrated mass flux : " << rhoUA << " " << rhoVA
327  << " (target: " << rhoUA_target << " " << rhoVA_target << ")"
328  << std::endl;
329  }
330  }
331 
332  // *****************************************************************************
333  // Add all the other forcings
334  // *****************************************************************************
335  for ( MFIter mfi(S_data[IntVars::cons]); mfi.isValid(); ++mfi)
336  {
337  Box tbx = mfi.nodaltilebox(0);
338  Box tby = mfi.nodaltilebox(1);
339  Box tbz = mfi.nodaltilebox(2);
340  if (tbz.bigEnd(2) == domain.bigEnd(2)+1) tbz.growHi(2,-1);
341 
342  const Array4<const Real>& cell_data = S_data[IntVars::cons].array(mfi);
343  const Array4<const Real>& rho_u = S_data[IntVars::xmom].array(mfi);
344  const Array4<const Real>& rho_v = S_data[IntVars::ymom].array(mfi);
345  const Array4<const Real>& rho_w = S_data[IntVars::zmom].array(mfi);
346 
347  const Array4<const Real>& u = xvel.array(mfi);
348  const Array4<const Real>& v = yvel.array(mfi);
349  const Array4<const Real>& w = wvel.array(mfi);
350 
351  const Array4< Real>& xmom_src_arr = xmom_src.array(mfi);
352  const Array4< Real>& ymom_src_arr = ymom_src.array(mfi);
353  const Array4< Real>& zmom_src_arr = zmom_src.array(mfi);
354 
355  const Array4<const Real>& r0 = r_hse.const_array(mfi);
356 
357  const Array4<const Real>& f_drag_arr = (forest_drag) ? forest_drag->const_array(mfi) :
358  Array4<const Real>{};
359  const Array4<const Real>& t_blank_arr = (terrain_blank) ? terrain_blank->const_array(mfi) :
360  Array4<const Real>{};
361 
362  const Array4<const Real>& cphi_arr = (cosPhi_mf) ? cosPhi_mf->const_array(mfi) :
363  Array4<const Real>{};
364  const Array4<const Real>& sphi_arr = (sinPhi_mf) ? sinPhi_mf->const_array(mfi) :
365  Array4<const Real>{};
366 
367  const Array4<const Real>& z_nd_arr = z_phys_nd->const_array(mfi);
368  const Array4<const Real>& z_cc_arr = z_phys_cc->const_array(mfi);
369 
370 
371  // *****************************************************************************
372  // 1. Add Coriolis forcing (this assumes east is +x, north is +y)
373  // *****************************************************************************
374  if (use_coriolis && is_slow_step) {
375  if(solverChoice.init_type == InitType::HindCast) {
376  const Array4<const Real>& latlon_arr = (*forecast_state_at_lev)[4].array(mfi);
377  ParallelFor(tbx, tby, tbz,
378  [=] AMREX_GPU_DEVICE (int i, int j, int k)
379  {
380  Real rho_on_u_face = myhalf * ( cell_data(i,j,k,Rho_comp) + cell_data(i-1,j,k,Rho_comp) );
381  Real v_loc = fourth * (v(i,j+1,k) + v(i,j,k) + v(i-1,j+1,k) + v(i-1,j,k));
382  Real w_loc = fourth * (w(i,j,k+1) + w(i,j,k) + w(i-1,j,k+1) + w(i-1,j,k));
383  Real latitude = latlon_arr(i,j,k,0);
384  Real sphi_loc = std::sin(latitude*PI/Real(180.0));
385  Real cphi_loc = std::cos(latitude*PI/Real(180.0));
386  xmom_src_arr(i, j, k) += coriolis_factor * rho_on_u_face * (v_loc * sphi_loc - w_loc * cphi_loc);
387  },
388  [=] AMREX_GPU_DEVICE (int i, int j, int k) {
389  Real rho_on_v_face = myhalf * ( cell_data(i,j,k,Rho_comp) + cell_data(i,j-1,k,Rho_comp) );
390  Real u_loc = fourth * (u(i+1,j,k) + u(i,j,k) + u(i+1,j-1,k) + u(i,j-1,k));
391  Real latitude = latlon_arr(i,j,k,0);
392  Real sphi_loc = std::sin(latitude*PI/Real(180.0));
393  ymom_src_arr(i, j, k) += -coriolis_factor * rho_on_v_face * u_loc * sphi_loc;
394  },
395  [=] AMREX_GPU_DEVICE (int i, int j, int k) {
396  Real rho_on_w_face = myhalf * ( cell_data(i,j,k,Rho_comp) + cell_data(i,j,k-1,Rho_comp) );
397  Real u_loc = fourth * (u(i+1,j,k) + u(i,j,k) + u(i+1,j,k-1) + u(i,j,k-1));
398  Real latitude = latlon_arr(i,j,k,0);
399  Real cphi_loc = std::cos(latitude*PI/Real(180.0));
400  zmom_src_arr(i, j, k) += coriolis_factor * rho_on_w_face * u_loc * cphi_loc;
401  });
402  }
403  else if (var_coriolis && (sinPhi_mf) && (cosPhi_mf)) {
404  ParallelFor(tbx, tby, tbz,
405  [=] AMREX_GPU_DEVICE (int i, int j, int k)
406  {
407  Real rho_on_u_face = myhalf * ( cell_data(i,j,k,Rho_comp) + cell_data(i-1,j,k,Rho_comp) );
408  Real v_loc = fourth * (v(i,j+1,k) + v(i,j,k) + v(i-1,j+1,k) + v(i-1,j,k));
409  Real w_loc = fourth * (w(i,j,k+1) + w(i,j,k) + w(i-1,j,k+1) + w(i-1,j,k));
410  Real sphi_loc = myhalf * (sphi_arr(i,j,0) + sphi_arr(i-1,j,0));
411  Real cphi_loc = myhalf * (cphi_arr(i,j,0) + cphi_arr(i-1,j,0));
412  xmom_src_arr(i, j, k) += coriolis_factor * rho_on_u_face * (v_loc * sphi_loc - w_loc * cphi_loc);
413  },
414  [=] AMREX_GPU_DEVICE (int i, int j, int k) {
415  Real rho_on_v_face = myhalf * ( cell_data(i,j,k,Rho_comp) + cell_data(i,j-1,k,Rho_comp) );
416  Real u_loc = fourth * (u(i+1,j,k) + u(i,j,k) + u(i+1,j-1,k) + u(i,j-1,k));
417  Real sphi_loc = myhalf * (sphi_arr(i,j,0) + sphi_arr(i,j-1,0));
418  ymom_src_arr(i, j, k) += -coriolis_factor * rho_on_v_face * u_loc * sphi_loc;
419  },
420  [=] AMREX_GPU_DEVICE (int i, int j, int k) {
421  Real rho_on_w_face = myhalf * ( cell_data(i,j,k,Rho_comp) + cell_data(i,j,k-1,Rho_comp) );
422  Real u_loc = fourth * (u(i+1,j,k) + u(i,j,k) + u(i+1,j,k-1) + u(i,j,k-1));
423  zmom_src_arr(i, j, k) += coriolis_factor * rho_on_w_face * u_loc * cphi_arr(i,j,0);
424  });
425  } else {
426  if (solverChoice.terrain_type == TerrainType::EB) {
427  Array4<const Real> u_volfrac = (ebfact.get_u_const_factory())->getVolFrac().const_array(mfi);
428  Array4<const Real> v_volfrac = (ebfact.get_v_const_factory())->getVolFrac().const_array(mfi);
429  Array4<const Real> w_volfrac = (ebfact.get_w_const_factory())->getVolFrac().const_array(mfi);
430  ParallelFor(tbx, [=] AMREX_GPU_DEVICE (int i, int j, int k) {
431  Real v_loc = 0.0;
432  Real w_loc = 0.0;
433  Real v_vol = v_volfrac(i,j+1,k) + v_volfrac(i,j,k) + v_volfrac(i-1,j+1,k) + v_volfrac(i-1,j,k);
434  Real w_vol = w_volfrac(i,j,k+1) + w_volfrac(i,j,k) + w_volfrac(i-1,j,k+1) + w_volfrac(i-1,j,k);
435  Real rho_on_u_face = myhalf * ( cell_data(i,j,k,Rho_comp) + cell_data(i-1,j,k,Rho_comp) );
436  if (v_vol > 0.0) {
437  v_loc = ( v_volfrac(i ,j+1,k) * v(i ,j+1,k) + v_volfrac(i ,j,k) * v(i ,j,k)
438  + v_volfrac(i-1,j+1,k) * v(i-1,j+1,k) + v_volfrac(i-1,j,k) * v(i-1,j,k)) / v_vol;
439  }
440  if (w_vol > 0.0) {
441  w_loc = ( w_volfrac(i ,j,k+1) * w(i ,j,k+1) + w_volfrac(i ,j,k) * w(i ,j,k)
442  + w_volfrac(i-1,j,k+1) * w(i-1,j,k+1) + w_volfrac(i-1,j,k) * w(i-1,j,k)) / w_vol;
443  }
444  xmom_src_arr(i, j, k) += coriolis_factor * rho_on_u_face * (v_loc * sinphi - w_loc * cosphi);
445  });
446  ParallelFor(tby, [=] AMREX_GPU_DEVICE (int i, int j, int k) {
447  Real u_loc = 0.0;
448  Real u_vol = u_volfrac(i+1,j,k) + u_volfrac(i,j,k) + u_volfrac(i+1,j-1,k) + u_volfrac(i,j-1,k);
449  Real rho_on_v_face = myhalf * ( cell_data(i,j,k,Rho_comp) + cell_data(i,j-1,k,Rho_comp) );
450  if (u_vol > 0.0) {
451  u_loc = ( u_volfrac(i+1,j ,k) * u(i+1,j ,k) + u_volfrac(i,j ,k) * u(i,j ,k)
452  + u_volfrac(i+1,j-1,k) * u(i+1,j-1,k) + u_volfrac(i,j-1,k) * u(i,j-1,k)) / u_vol;
453  }
454  ymom_src_arr(i, j, k) += -coriolis_factor * rho_on_v_face * u_loc * sinphi;
455  });
456  ParallelFor(tbz, [=] AMREX_GPU_DEVICE (int i, int j, int k) {
457  Real u_loc = 0.0;
458  Real u_vol = u_volfrac(i+1,j,k) + u_volfrac(i,j,k) + u_volfrac(i+1,j,k-1) + u_volfrac(i,j,k-1);
459  Real rho_on_w_face = myhalf * ( cell_data(i,j,k,Rho_comp) + cell_data(i,j,k-1,Rho_comp) );
460  if (u_vol > 0.0) {
461  u_loc = ( u_volfrac(i+1,j,k ) * u(i+1,j,k ) + u_volfrac(i,j,k) * u(i,j,k )
462  + u_volfrac(i+1,j,k-1) * u(i+1,j,k-1) + u_volfrac(i,j,k-1) * u(i,j,k-1)) / u_vol;
463  }
464  zmom_src_arr(i, j, k) += coriolis_factor * rho_on_w_face * u_loc * cosphi;
465  });
466  } else {
467  ParallelFor(tbx, tby, tbz,
468  [=] AMREX_GPU_DEVICE (int i, int j, int k)
469  {
470  Real v_loc = fourth * (v(i,j+1,k) + v(i,j,k) + v(i-1,j+1,k) + v(i-1,j,k));
471  Real w_loc = fourth * (w(i,j,k+1) + w(i,j,k) + w(i-1,j,k+1) + w(i-1,j,k));
472  Real rho_on_u_face = myhalf * ( cell_data(i,j,k,Rho_comp) + cell_data(i-1,j,k,Rho_comp) );
473  xmom_src_arr(i, j, k) += rho_on_u_face * ( coriolis_factor * (v_loc * sinphi - w_loc * cosphi) );
474  },
475  [=] AMREX_GPU_DEVICE (int i, int j, int k) {
476  Real rho_on_v_face = myhalf * ( cell_data(i,j,k,Rho_comp) + cell_data(i,j-1,k,Rho_comp) );
477  Real u_loc = fourth * (u(i+1,j,k) + u(i,j,k) + u(i+1,j-1,k) + u(i,j-1,k));
478  ymom_src_arr(i, j, k) += rho_on_v_face * ( -coriolis_factor * u_loc * sinphi );
479  },
480  [=] AMREX_GPU_DEVICE (int i, int j, int k) {
481  Real u_loc = fourth * (u(i+1,j,k) + u(i,j,k) + u(i+1,j,k-1) + u(i,j,k-1));
482  Real rho_on_w_face = myhalf * ( cell_data(i,j,k,Rho_comp) + cell_data(i,j,k-1,Rho_comp) );
483  zmom_src_arr(i, j, k) += rho_on_w_face * ( coriolis_factor * u_loc * cosphi );
484  });
485  }
486  } // var_coriolis
487  } // use_coriolis
488 
489  // *****************************************************************************
490  // 2. Add RAYLEIGH damping
491  // *****************************************************************************
492  Real dampcoef = solverChoice.dampingChoice.rayleigh_dampcoef;
493 
494  if ( (is_slow_step && !use_Rayleigh_fast_uv) || (!is_slow_step && use_Rayleigh_fast_uv)) {
495  if (rayleigh_damp_U) {
496  ParallelFor(tbx, [=] AMREX_GPU_DEVICE (int i, int j, int k)
497  {
498  Real rho_on_u_face = myhalf * ( cell_data(i,j,k,Rho_comp) + cell_data(i-1,j,k,Rho_comp) );
499  Real uu = rho_u(i,j,k) / rho_on_u_face;
500  Real sinesq = d_sinesq_at_lev[k];
501  xmom_src_arr(i, j, k) -= dampcoef*sinesq * (uu - ubar[k]) * rho_on_u_face;
502  });
503  }
504 
505  if (rayleigh_damp_V) {
506  ParallelFor(tby, [=] AMREX_GPU_DEVICE (int i, int j, int k)
507  {
508  Real rho_on_v_face = myhalf * ( cell_data(i,j,k,Rho_comp) + cell_data(i,j-1,k,Rho_comp) );
509  Real vv = rho_v(i,j,k) / rho_on_v_face;
510  Real sinesq = d_sinesq_at_lev[k];
511  ymom_src_arr(i, j, k) -= dampcoef*sinesq * (vv - vbar[k]) * rho_on_v_face;
512  });
513  }
514  } // fast or slow step
515 
516  if ( (is_slow_step && !use_Rayleigh_fast_w) || (!is_slow_step && use_Rayleigh_fast_w)) {
517  if (rayleigh_damp_W) {
518  ParallelFor(tbz, [=] AMREX_GPU_DEVICE (int i, int j, int k)
519  {
520  Real rho_on_w_face = myhalf * ( cell_data(i,j,k,Rho_comp) + cell_data(i,j,k-1,Rho_comp) );
521  Real ww = rho_w(i,j,k) / rho_on_w_face;
522  Real sinesq = d_sinesq_stag_at_lev[k];
523  zmom_src_arr(i, j, k) -= dampcoef*sinesq * (ww - wbar[k]) * rho_on_w_face;
524  });
525  }
526  } // fast or slow step
527 
528  // *****************************************************************************
529  // 3a. Add constant GEOSTROPHIC forcing
530  // *****************************************************************************
531  if (is_slow_step) {
532  ParallelFor(tbx, tby, tbz,
533  [=] AMREX_GPU_DEVICE (int i, int j, int k)
534  {
535  Real rho_on_u_face = myhalf * ( cell_data(i,j,k,Rho_comp) + cell_data(i-1,j,k,Rho_comp) );
536  xmom_src_arr(i, j, k) += rho_on_u_face * abl_geo_forcing[0];
537  },
538  [=] AMREX_GPU_DEVICE (int i, int j, int k)
539  {
540  Real rho_on_v_face = myhalf * ( cell_data(i,j,k,Rho_comp) + cell_data(i,j-1,k,Rho_comp) );
541  ymom_src_arr(i, j, k) += rho_on_v_face * abl_geo_forcing[1];
542  },
543  [=] AMREX_GPU_DEVICE (int i, int j, int k)
544  {
545  Real rho_on_w_face = myhalf * ( cell_data(i,j,k,Rho_comp) + cell_data(i,j,k-1,Rho_comp) );
546  zmom_src_arr(i, j, k) += rho_on_w_face * abl_geo_forcing[2];
547  });
548  }
549 
550  // *****************************************************************************
551  // 3b. Add height-dependent GEOSTROPHIC forcing
552  // *****************************************************************************
553  if (geo_wind_profile && is_slow_step) {
554  ParallelFor(tbx, tby,
555  [=] AMREX_GPU_DEVICE (int i, int j, int k)
556  {
557  Real rho_on_u_face = myhalf * ( cell_data(i,j,k,Rho_comp) + cell_data(i-1,j,k,Rho_comp) );
558  xmom_src_arr(i, j, k) -= coriolis_factor * rho_on_u_face * dptr_v_geos[k] * sinphi;
559  },
560  [=] AMREX_GPU_DEVICE (int i, int j, int k)
561  {
562  Real rho_on_v_face = myhalf * ( cell_data(i,j,k,Rho_comp) + cell_data(i,j-1,k,Rho_comp) );
563  ymom_src_arr(i, j, k) += coriolis_factor * rho_on_v_face * dptr_u_geos[k] * sinphi;
564  });
565  } // geo_wind_profile
566 
567  // *****************************************************************************
568  // 4. Add custom SUBSIDENCE terms
569  // *****************************************************************************
570  if (solverChoice.custom_w_subsidence && is_slow_step && solverChoice.do_mom_advection) {
571  if (solverChoice.custom_forcing_prim_vars) {
572  const int nr = Rho_comp;
573  ParallelFor(tbx, tby,
574  [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
575  {
576  Real dzInv = myhalf*dxInv[2];
577  if (z_nd_arr) {
578  Real z_xf_lo = fourth * ( z_nd_arr(i,j,k ) + z_nd_arr(i,j+1,k )
579  + z_nd_arr(i,j,k-1) + z_nd_arr(i,j+1,k-1) );
580  Real z_xf_hi = fourth * ( z_nd_arr(i,j,k+1) + z_nd_arr(i,j+1,k+1)
581  + z_nd_arr(i,j,k+2) + z_nd_arr(i,j+1,k+2) );
582  dzInv = one / (z_xf_hi - z_xf_lo);
583  }
584  Real rho_on_u_face = myhalf * ( cell_data(i,j,k,nr) + cell_data(i-1,j,k,nr) );
585  Real U_hi = dptr_u_plane(k+1) / dptr_r_plane(k+1);
586  Real U_lo = dptr_u_plane(k-1) / dptr_r_plane(k-1);
587  Real wbar_xf = myhalf * (dptr_wbar_sub[k] + dptr_wbar_sub[k+1]);
588  xmom_src_arr(i, j, k) -= rho_on_u_face * wbar_xf * (U_hi - U_lo) * dzInv;
589  },
590  [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
591  {
592  Real dzInv = myhalf*dxInv[2];
593  if (z_nd_arr) {
594  Real z_yf_lo = fourth * ( z_nd_arr(i,j,k ) + z_nd_arr(i+1,j,k )
595  + z_nd_arr(i,j,k-1) + z_nd_arr(i+1,j,k-1) );
596  Real z_yf_hi = fourth * ( z_nd_arr(i,j,k+1) + z_nd_arr(i+1,j,k+1)
597  + z_nd_arr(i,j,k+2) + z_nd_arr(i+1,j,k+2) );
598  dzInv = one / (z_yf_hi - z_yf_lo);
599  }
600  Real rho_on_v_face = myhalf * ( cell_data(i,j,k,nr) + cell_data(i,j-1,k,nr) );
601  Real V_hi = dptr_v_plane(k+1) / dptr_r_plane(k+1);
602  Real V_lo = dptr_v_plane(k-1) / dptr_r_plane(k-1);
603  Real wbar_yf = myhalf * (dptr_wbar_sub[k] + dptr_wbar_sub[k+1]);
604  ymom_src_arr(i, j, k) -= rho_on_v_face * wbar_yf * (V_hi - V_lo) * dzInv;
605  });
606  } else {
607  ParallelFor(tbx, tby,
608  [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
609  {
610  Real dzInv = myhalf*dxInv[2];
611  if (z_nd_arr) {
612  Real z_xf_lo = fourth * ( z_nd_arr(i,j,k ) + z_nd_arr(i,j+1,k )
613  + z_nd_arr(i,j,k-1) + z_nd_arr(i,j+1,k-1) );
614  Real z_xf_hi = fourth * ( z_nd_arr(i,j,k+1) + z_nd_arr(i,j+1,k+1)
615  + z_nd_arr(i,j,k+2) + z_nd_arr(i,j+1,k+2) );
616  dzInv = one / (z_xf_hi - z_xf_lo);
617  }
618  Real U_hi = dptr_u_plane(k+1) / dptr_r_plane(k+1);
619  Real U_lo = dptr_u_plane(k-1) / dptr_r_plane(k-1);
620  Real wbar_xf = myhalf * (dptr_wbar_sub[k] + dptr_wbar_sub[k+1]);
621  xmom_src_arr(i, j, k) -= wbar_xf * (U_hi - U_lo) * dzInv;
622  },
623  [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
624  {
625  Real dzInv = myhalf*dxInv[2];
626  if (z_nd_arr) {
627  Real z_yf_lo = fourth * ( z_nd_arr(i,j,k ) + z_nd_arr(i+1,j,k )
628  + z_nd_arr(i,j,k-1) + z_nd_arr(i+1,j,k-1) );
629  Real z_yf_hi = fourth * ( z_nd_arr(i,j,k+1) + z_nd_arr(i+1,j,k+1)
630  + z_nd_arr(i,j,k+2) + z_nd_arr(i+1,j,k+2) );
631  dzInv = one / (z_yf_hi - z_yf_lo);
632  }
633  Real V_hi = dptr_v_plane(k+1) / dptr_r_plane(k+1);
634  Real V_lo = dptr_v_plane(k-1) / dptr_r_plane(k-1);
635  Real wbar_yf = myhalf * (dptr_wbar_sub[k] + dptr_wbar_sub[k+1]);
636  ymom_src_arr(i, j, k) -= wbar_yf * (V_hi - V_lo) * dzInv;
637  });
638  }
639  }
640 
641  // *************************************************************************************
642  // 5. Add nudging towards value specified in input sounding
643  // *************************************************************************************
644  if (solverChoice.nudging_from_input_sounding && is_slow_step)
645  {
646  int itime_n = 0;
647  int itime_np1 = 0;
648  Real coeff_n = one;
649  Real coeff_np1 = zero;
650 
651  Real tau_inv = one / input_sounding_data.tau_nudging;
652 
653  int n_sounding_times = input_sounding_data.input_sounding_time.size();
654 
655  for (int nt = 1; nt < n_sounding_times; nt++) {
656  if (time > input_sounding_data.input_sounding_time[nt]) itime_n = nt;
657  }
658  if (itime_n == n_sounding_times-1) {
659  itime_np1 = itime_n;
660  } else {
661  itime_np1 = itime_n+1;
662  coeff_np1 = (time - input_sounding_data.input_sounding_time[itime_n]) /
663  (input_sounding_data.input_sounding_time[itime_np1] - input_sounding_data.input_sounding_time[itime_n]);
664  coeff_n = one - coeff_np1;
665  }
666 
667  int nr = Rho_comp;
668 
669  const Real* u_inp_sound_n = input_sounding_data.U_inp_sound_d[itime_n].dataPtr();
670  const Real* u_inp_sound_np1 = input_sounding_data.U_inp_sound_d[itime_np1].dataPtr();
671  const Real* v_inp_sound_n = input_sounding_data.V_inp_sound_d[itime_n].dataPtr();
672  const Real* v_inp_sound_np1 = input_sounding_data.V_inp_sound_d[itime_np1].dataPtr();
673  ParallelFor(tbx, tby,
674  [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
675  {
676  Real nudge_u = (coeff_n*u_inp_sound_n[k] + coeff_np1*u_inp_sound_np1[k]) - (dptr_u_plane(k)/dptr_r_plane(k));
677  nudge_u *= tau_inv;
678  xmom_src_arr(i, j, k) += cell_data(i, j, k, nr) * nudge_u;
679  },
680  [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
681  {
682  Real nudge_v = (coeff_n*v_inp_sound_n[k] + coeff_np1*v_inp_sound_np1[k]) - (dptr_v_plane(k)/dptr_r_plane(k));
683  nudge_v *= tau_inv;
684  ymom_src_arr(i, j, k) += cell_data(i, j, k, nr) * nudge_v;
685  });
686  }
687 
688  // *****************************************************************************
689  // 6. Add NUMERICAL DIFFUSION terms
690  // *****************************************************************************
691 #if 0
692  if (l_use_ndiff) {
693  const Array4<const Real>& mf_ux = mapfac[MapFac::ux]->const_array(mfi);
694  const Array4<const Real>& mf_uy = mapfac[MapFac::uy]->const_array(mfi);
695  const Array4<const Real>& mf_vx = mapfac[MapFac::vx]->const_array(mfi);
696  const Array4<const Real>& mf_vy = mapfac[MapFac::vy]->const_array(mfi);
697  NumericalDiffusion_Xmom(tbx, dt, solverChoice.num_diff_coeff,
698  u, cell_data, xmom_src_arr, mf_ux, mf_uy);
699  NumericalDiffusion_Ymom(tby, dt, solverChoice.num_diff_coeff,
700  v, cell_data, ymom_src_arr, mf_vx, mf_vy);
701  }
702 #endif
703 
704  // *****************************************************************************
705  // 7. Add SPONGING
706  // *****************************************************************************
707  if (is_slow_step) {
708  if (solverChoice.spongeChoice.sponge_type == SpongeType::Input_Sponge)
709  {
710  ApplySpongeZoneBCsForMom_ReadFromFile(solverChoice.spongeChoice, geom, tbx, tby, cell_data,
711  z_cc_arr, xmom_src_arr, ymom_src_arr,
712  rho_u, rho_v, d_sponge_ptrs_at_lev);
713  }
714  else if (solverChoice.spongeChoice.sponge_type == SpongeType::Standard)
715  {
716  ApplySpongeZoneBCsForMom(solverChoice.spongeChoice, geom, tbx, tby, tbz,
717  xmom_src_arr, ymom_src_arr, zmom_src_arr, rho_u, rho_v, rho_w,
718  r0, z_nd_arr, z_cc_arr);
719  }
720 
721  if(solverChoice.init_type == InitType::HindCast and solverChoice.hindcast_lateral_forcing){
722 
723  const Array4<const Real>& rho_u_forecast_state = (*forecast_state_at_lev)[IntVars::xmom].array(mfi);
724  const Array4<const Real>& rho_v_forecast_state = (*forecast_state_at_lev)[IntVars::ymom].array(mfi);
725  const Array4<const Real>& rho_w_forecast_state = (*forecast_state_at_lev)[IntVars::zmom].array(mfi);
726  const Array4<const Real>& cons_forecast_state = (*forecast_state_at_lev)[IntVars::cons].array(mfi);
727  ApplyBndryForcing_Forecast(solverChoice, geom, tbx, tby, tbz, z_nd_arr,
728  xmom_src_arr, ymom_src_arr, zmom_src_arr,
729  rho_u, rho_v, rho_w,
730  rho_u_forecast_state, rho_v_forecast_state, rho_w_forecast_state,
731  cons_forecast_state);
732  }
733  }
734 
735  // *****************************************************************************
736  // 8. Add CANOPY source terms
737  // *****************************************************************************
738  if (solverChoice.do_forest_drag &&
739  ((is_slow_step && !use_canopy_fast) || (!is_slow_step && use_canopy_fast))) {
740  ParallelFor(tbx, tby, tbz,
741  [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
742  {
743  const Real ux = u(i, j, k);
744  const Real uy = fourth * ( v(i, j , k ) + v(i-1, j , k )
745  + v(i, j+1, k ) + v(i-1, j+1, k ) );
746  const Real uz = fourth * ( w(i, j , k ) + w(i-1, j , k )
747  + w(i, j , k+1) + w(i-1, j , k+1) );
748  const Real windspeed = std::sqrt(ux * ux + uy * uy + uz * uz);
749  const Real f_drag = myhalf * (f_drag_arr(i, j, k) + f_drag_arr(i-1, j, k));
750  xmom_src_arr(i, j, k) -= f_drag * ux * windspeed;
751  },
752  [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
753  {
754  const Real ux = fourth * ( u(i , j , k ) + u(i , j-1, k )
755  + u(i+1, j , k ) + u(i+1, j-1, k ) );
756  const Real uy = v(i, j, k);
757  const Real uz = fourth * ( w(i , j , k ) + w(i , j-1, k )
758  + w(i , j , k+1) + w(i , j-1, k+1) );
759  const amrex::Real windspeed = std::sqrt(ux * ux + uy * uy + uz * uz);
760  const Real f_drag = myhalf * (f_drag_arr(i, j, k) + f_drag_arr(i, j-1, k));
761  ymom_src_arr(i, j, k) -= f_drag * uy * windspeed;
762  },
763  [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
764  {
765  const amrex::Real ux = fourth * ( u(i , j , k ) + u(i+1, j , k )
766  + u(i , j , k-1) + u(i+1, j , k-1) );
767  const amrex::Real uy = fourth * ( v(i , j , k ) + v(i , j+1, k )
768  + v(i , j , k-1) + v(i , j+1, k-1) );
769  const amrex::Real uz = w(i, j, k);
770  const amrex::Real windspeed = std::sqrt(ux * ux + uy * uy + uz * uz);
771  const Real f_drag = myhalf * (f_drag_arr(i, j, k) + f_drag_arr(i, j, k-1));
772  zmom_src_arr(i, j, k) -= f_drag * uz * windspeed;
773  });
774  }
775  // *****************************************************************************
776  // 9a. Add immersed source terms for terrain
777  // *****************************************************************************
778  if (solverChoice.terrain_type == TerrainType::ImmersedForcing &&
779  ((is_slow_step && !use_ImmersedForcing_fast) || (!is_slow_step && use_ImmersedForcing_fast))) {
780  // geometric properties
781  const Real* dx_arr = geom.CellSize();
782  const Real dx_x = dx_arr[0];
783  const Real dx_y = dx_arr[1];
784 
785  const Real alpha_m = solverChoice.if_Cd_momentum;
787  const Real U_s = one; // unit velocity scale
788  const bool l_implicit_drag = solverChoice.if_implicit_drag;
789 
790  // MOST parameters
791  similarity_funs sfuns;
792  const Real ggg = CONST_GRAV;
793  const Real kappa = KAPPA;
794  const Real z0 = solverChoice.if_z0;
795  const Real tflux_in = solverChoice.if_surf_temp_flux;
796  const Real Olen_in = solverChoice.if_Olen_in;
797  const bool l_use_most = solverChoice.if_use_most;
798 
799  ParallelFor(tbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
800  {
801  const Real ux = u(i, j, k);
802  const Real uy = fourth * ( v(i, j , k ) + v(i-1, j , k )
803  + v(i, j+1, k ) + v(i-1, j+1, k ) );
804  const Real uz = fourth * ( w(i, j , k ) + w(i-1, j , k )
805  + w(i, j , k+1) + w(i-1, j , k+1) );
806  const Real windspeed = std::sqrt(ux * ux + uy * uy + uz * uz);
807  const Real t_blank = myhalf * (t_blank_arr(i, j, k) + t_blank_arr(i-1, j, k));
808  const Real t_blank_above = myhalf * (t_blank_arr(i, j, k+1) + t_blank_arr(i-1, j, k+1));
809 
810  const Real dx_z = (z_cc_arr) ? (z_cc_arr(i,j,k) - z_cc_arr(i,j,k-1)) : dx_arr[2];
811  const Real drag_coefficient = alpha_m / std::pow(dx_x*dx_y*dx_z, one/three);
812  const Real CdM = std::min(drag_coefficient / (windspeed + tiny), drag_coefficient);
813 
814  const Real rho_xface = myhalf * ( cell_data(i,j,k,Rho_comp) + cell_data(i-1,j,k,Rho_comp) );
815 
816  if ((t_blank > 0 && (t_blank_above == zero)) && l_use_most) { // force to MOST value
817  // calculate tangential velocity one cell above
818  const Real ux2r = u(i, j, k+1) ;
819  const Real uy2r = fourth * ( v(i, j , k+1) + v(i-1, j , k+1)
820  + v(i, j+1, k+1) + v(i-1, j+1, k+1) ) ;
821  const Real h_windspeed2r = std::sqrt(ux2r * ux2r + uy2r * uy2r);
822 
823  // MOST
824  const Real theta_xface = (myhalf * (cell_data(i,j,k ,RhoTheta_comp) + cell_data(i-1,j,k, RhoTheta_comp))) / rho_xface;
825  const Real rho_xface_below = myhalf * ( cell_data(i,j,k-1,Rho_comp) + cell_data(i-1,j,k-1,Rho_comp) );
826  const Real theta_xface_below = (myhalf * (cell_data(i,j,k-1,RhoTheta_comp) + cell_data(i-1,j,k-1, RhoTheta_comp))) / rho_xface_below;
827  const Real theta_surf = theta_xface_below;
828 
829  Real psi_m = zero;
830  Real psi_h = zero;
831  Real ustar = h_windspeed2r * kappa / (std::log(Real(1.5) * dx_z / z0) - psi_m); // calculated from bottom of cell. Maintains flexibility for different Vf values
832  Real tflux = (tflux_in != Real(1e-8)) ? tflux_in : -(theta_xface - theta_surf) * ustar * kappa / (std::log(Real(1.5) * dx_z / z0) - psi_h);
833  Real Olen = (Olen_in != Real(1e-8)) ? Olen_in : -ustar * ustar * ustar * theta_xface / (kappa * ggg * tflux + tiny);
834  Real zeta = Real(1.5) * dx_z / Olen;
835 
836  // similarity functions
837  psi_m = sfuns.calc_psi_m(zeta);
838  psi_h = sfuns.calc_psi_h(zeta);
839  ustar = h_windspeed2r * kappa / (std::log(Real(1.5) * dx_z / z0) - psi_m);
840 
841  // prevent some unphysical math
842  if (!(ustar > zero && !std::isnan(ustar))) { ustar = zero; }
843  if (!(ustar < two && !std::isnan(ustar))) { ustar = two; }
844  if (psi_m > std::log(myhalf * dx_z / z0)) { psi_m = std::log(myhalf * dx_z / z0); }
845 
846  // determine target velocity
847  const Real uTarget = ustar / kappa * (std::log(myhalf * dx_z / z0) - psi_m);
848  Real uxTarget = uTarget * ux2r / (tiny + h_windspeed2r);
849  const Real bc_forcing_x = -(uxTarget - ux); // BC forcing pushes nonrelative velocity toward target velocity
850  const Real lambda = (1-t_blank) * CdM * U_s; // affine relaxation rate toward MOST target [1/s]
851  const Real fac = l_implicit_drag ? lambda / (one + lambda*dt) : lambda; // point-implicit rescale (else explicit)
852  xmom_src_arr(i, j, k) -= fac * rho_xface * bc_forcing_x; // if Vf low, force more strongly to MOST. If high, less forcing.
853  } else {
854  const Real lambda = t_blank * CdM * windspeed; // linear drag rate [1/s]
855  const Real fac = l_implicit_drag ? lambda / (one + lambda*dt) : lambda; // point-implicit rescale (else explicit)
856  xmom_src_arr(i, j, k) -= fac * rho_xface * ux;
857  }
858  });
859  ParallelFor(tby, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
860  {
861  const Real ux = fourth * ( u(i , j , k ) + u(i , j-1, k )
862  + u(i+1, j , k ) + u(i+1, j-1, k ) );
863  const Real uy = v(i, j, k);
864  const Real uz = fourth * ( w(i , j , k ) + w(i , j-1, k )
865  + w(i , j , k+1) + w(i , j-1, k+1) );
866  const Real windspeed = std::sqrt(ux * ux + uy * uy + uz * uz);
867  const Real t_blank = myhalf * (t_blank_arr(i, j, k) + t_blank_arr(i, j-1, k));
868  const Real t_blank_above = myhalf * (t_blank_arr(i, j, k+1) + t_blank_arr(i, j-1, k+1));
869 
870  const Real dx_z = (z_cc_arr) ? (z_cc_arr(i,j,k) - z_cc_arr(i,j,k-1)) : dx_arr[2];
871  const Real drag_coefficient = alpha_m / std::pow(dx_x*dx_y*dx_z, one/three);
872  const Real CdM = std::min(drag_coefficient / (windspeed + tiny), drag_coefficient);
873 
874  const Real rho_yface = myhalf * ( cell_data(i,j,k,Rho_comp) + cell_data(i,j-1,k,Rho_comp) );
875 
876  if ((t_blank > 0 && (t_blank_above == zero)) && l_use_most) { // force to MOST value
877  // calculate tangential velocity one cell above
878  const Real ux2r = fourth * ( u(i , j , k+1) + u(i , j-1, k+1)
879  + u(i+1, j , k+1) + u(i+1, j-1, k+1) );
880  const Real uy2r = v(i, j, k+1) ;
881  const Real h_windspeed2r = std::sqrt(ux2r * ux2r + uy2r * uy2r);
882 
883  // MOST
884  const Real theta_yface = (myhalf * (cell_data(i,j,k ,RhoTheta_comp) + cell_data(i,j-1,k, RhoTheta_comp))) / rho_yface;
885  const Real rho_yface_below = myhalf * ( cell_data(i,j,k-1,Rho_comp) + cell_data(i,j-1,k-1,Rho_comp) );
886  const Real theta_yface_below = (myhalf * (cell_data(i,j,k-1,RhoTheta_comp) + cell_data(i,j-1,k-1, RhoTheta_comp))) / rho_yface_below;
887  const Real theta_surf = theta_yface_below;
888 
889  Real psi_m = zero;
890  Real psi_h = zero;
891  Real ustar = h_windspeed2r * kappa / (std::log(Real(1.5) * dx_z / z0) - psi_m); // calculated from bottom of cell. Maintains flexibility for different Vf values
892  Real tflux = (tflux_in != Real(1e-8)) ? tflux_in : -(theta_yface - theta_surf) * ustar * kappa / (std::log(Real(1.5) * dx_z / z0) - psi_h);
893  Real Olen = (Olen_in != Real(1e-8)) ? Olen_in : -ustar * ustar * ustar * theta_yface / (kappa * ggg * tflux + tiny);
894  Real zeta = Real(1.5) * dx_z / Olen;
895 
896  // similarity functions
897  psi_m = sfuns.calc_psi_m(zeta);
898  psi_h = sfuns.calc_psi_h(zeta);
899  ustar = h_windspeed2r * kappa / (std::log(Real(1.5) * dx_z / z0) - psi_m);
900 
901  // prevent some unphysical math
902  if (!(ustar > zero && !std::isnan(ustar))) { ustar = zero; }
903  if (!(ustar < two && !std::isnan(ustar))) { ustar = two; }
904  if (psi_m > std::log(myhalf * dx_z / z0)) { psi_m = std::log(myhalf * dx_z / z0); }
905 
906  // determine target velocity
907  const Real uTarget = ustar / kappa * (std::log(myhalf * dx_z / z0) - psi_m);
908  Real uyTarget = uTarget * uy2r / (tiny + h_windspeed2r);
909  const Real bc_forcing_y = -(uyTarget - uy); // BC forcing pushes nonrelative velocity toward target velocity
910  const Real lambda = (1 - t_blank) * CdM * U_s; // affine relaxation rate toward MOST target [1/s]
911  const Real fac = l_implicit_drag ? lambda / (one + lambda*dt) : lambda; // point-implicit rescale (else explicit)
912  ymom_src_arr(i, j, k) -= fac * rho_yface * bc_forcing_y; // if Vf low, force more strongly to MOST. If high, less forcing.
913  } else {
914  const Real lambda = t_blank * CdM * windspeed; // linear drag rate [1/s]
915  const Real fac = l_implicit_drag ? lambda / (one + lambda*dt) : lambda; // point-implicit rescale (else explicit)
916  ymom_src_arr(i, j, k) -= fac * rho_yface * uy;
917  }
918  });
919  ParallelFor(tbz, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
920  {
921  const Real ux = fourth * ( u(i , j , k ) + u(i+1, j , k )
922  + u(i , j , k-1) + u(i+1, j , k-1) );
923  const Real uy = fourth * ( v(i , j , k ) + v(i , j+1, k )
924  + v(i , j , k-1) + v(i , j+1, k-1) );
925  const Real uz = w(i, j, k);
926  const Real windspeed = std::sqrt(ux * ux + uy * uy + uz * uz);
927  const Real t_blank = myhalf * (t_blank_arr(i, j, k) + t_blank_arr(i, j, k-1));
928 
929  const Real dx_z = (z_cc_arr) ? (z_cc_arr(i,j,k) - z_cc_arr(i,j,k-1)) : dx_arr[2];
930  const Real drag_coefficient = alpha_m / std::pow(dx_x*dx_y*dx_z, one/three);
931  const Real CdM = std::min(drag_coefficient / (windspeed + tiny), drag_coefficient);
932 
933  const Real rho_zface = myhalf * ( cell_data(i,j,k,Rho_comp) + cell_data(i,j,k-1,Rho_comp) );
934  const Real lambda = t_blank * CdM * windspeed; // linear drag rate [1/s]
935  const Real fac = l_implicit_drag ? lambda / (one + lambda*dt) : lambda; // point-implicit rescale (else explicit)
936  zmom_src_arr(i, j, k) -= fac * rho_zface * uz;
937  });
938  }
939 
940  // *****************************************************************************
941  // 9b. Add immersed source terms for buildings
942  // *****************************************************************************
943  // geometric properties
944  const Real* dx_arr = geom.CellSize();
945  const Real dx_x = dx_arr[0];
946  const Real dx_y = dx_arr[1];
947  const Real delta_xy = std::pow(dx_x*dx_y, myhalf);
948  if ((solverChoice.buildings_type == BuildingsType::ImmersedForcing ) &&
949  ((is_slow_step && !use_ImmersedForcing_fast) || (!is_slow_step && use_ImmersedForcing_fast)) &&
950  (delta_xy <= 50.0)) // only apply immersed forcing when grid spacing is less than 50m
951  {
952  const Real alpha_m = solverChoice.if_Cd_momentum;
954  const Real min_t_blank = Real(1.e-4); // threshold for where immersed forcing acts
955  const Real U_s = one; // unit velocity scale
956 
957  // MOST parameters
958  const Real z0 = solverChoice.if_z0;
959  const Real tflux_in = solverChoice.if_surf_temp_flux;
960  const Real Olen_in = solverChoice.if_Olen_in;
961  const bool l_use_most = solverChoice.if_use_most;
962  const bool l_stability_correction = solverChoice.if_stability_correction;
963 
964  // To limit stiffness of drag when using anelastic
965  const Real ws_floor = solverChoice.if_ws_floor;
966  const Real damp_alpha = solverChoice.if_damp_alpha;
967  // Point-implicit alternative to the clamp above; stabilizes both compressible and anelastic
968  const bool l_implicit_drag = solverChoice.if_implicit_drag;
969 
970  ParallelFor(tbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
971  {
972  const Real ux = u(i, j, k );
973  const Real uy = fourth * ( v(i, j , k ) + v(i-1, j , k )
974  + v(i, j+1, k ) + v(i-1, j+1, k ) );
975  const Real uz = fourth * ( w(i, j , k ) + w(i-1, j , k )
976  + w(i, j , k+1) + w(i-1, j , k+1) );
977  const amrex::Real windspeed = std::sqrt(ux * ux + uy * uy + uz * uz);
978 
979  const Real rho_xface = myhalf * ( cell_data(i,j,k,Rho_comp) + cell_data(i-1,j,k,Rho_comp) );
980  const Real theta_xface = (myhalf * (cell_data(i,j,k,RhoTheta_comp) + cell_data(i-1,j,k, RhoTheta_comp))) / rho_xface;
981 
982  Real t_blank = myhalf * (t_blank_arr(i, j , k ) + t_blank_arr(i-1, j , k ));
983  Real t_blank_below = myhalf * (t_blank_arr(i, j , k-1) + t_blank_arr(i-1, j , k-1));
984  Real t_blank_above = myhalf * (t_blank_arr(i, j , k+1) + t_blank_arr(i-1, j , k+1));
985  Real t_blank_north = myhalf * (t_blank_arr(i, j+1, k ) + t_blank_arr(i-1, j+1, k ));
986  Real t_blank_south = myhalf * (t_blank_arr(i, j-1, k ) + t_blank_arr(i-1, j-1, k ));
987  if (t_blank < min_t_blank) { t_blank = zero; }
988  if (k == 0) { t_blank_below = zero; }
989  if (t_blank_below < min_t_blank) { t_blank_below = zero; }
990  if (t_blank_above < min_t_blank) { t_blank_above = zero; }
991  if (t_blank_north < min_t_blank) { t_blank_north = zero; }
992  if (t_blank_south < min_t_blank) { t_blank_south = zero; }
993  // round to four decimal places to avoid issues for cells with very small volfracs.
994  t_blank = std::round(t_blank * Real(10000.0)) / Real(10000.0);
995  t_blank_below = std::round(t_blank_below * Real(10000.0)) / Real(10000.0);
996  t_blank_above = std::round(t_blank_above * Real(10000.0)) / Real(10000.0);
997  t_blank_north = std::round(t_blank_north * Real(10000.0)) / Real(10000.0);
998  t_blank_south = std::round(t_blank_south * Real(10000.0)) / Real(10000.0);
999 
1000  const Real dx_z = (z_cc_arr) ? (z_cc_arr(i,j,k) - z_cc_arr(i,j,k-1)) : dx_arr[2];
1001  const Real drag_coefficient = alpha_m / std::pow(dx_x*dx_y*dx_z, one/three);
1002  const Real CdM = std::min(drag_coefficient / (windspeed + tiny), drag_coefficient);
1003 
1004  const Real roof_mask = (t_blank > zero && t_blank < t_blank_below && t_blank_above == zero && l_use_most) ? one : zero; // roof cell
1005  const Real south_mask = (t_blank > zero && t_blank <= t_blank_north && t_blank_south == zero && l_use_most) ? one : zero; // south wall cell
1006  const Real north_mask = (t_blank > zero && t_blank <= t_blank_south && t_blank_north == zero && l_use_most) ? one : zero; // north wall cell
1007  const Real wall_mask = (t_blank > zero && t_blank < one && !l_use_most) ? one : zero; // wall cell (not using most)
1008  const Real interior_mask = (t_blank == 1.0) ? one : zero; // interior cell
1009 
1010  Real drag = zero;
1011  Real u1_cellaway = zero;
1012  Real u2_cellaway = zero;
1013  Real rho_xface_inside = rho_xface;
1014  Real theta_surf = theta_xface;
1015  Real bc_forcing_x = zero;
1016  Real u_target = zero;
1017 
1018  // roof forcing
1019  u1_cellaway = u(i, j, k+1) ;
1020  u2_cellaway = fourth * ( v(i, j , k+1) + v(i-1, j , k+1)
1021  + v(i, j+1, k+1) + v(i-1, j+1, k+1) ) ;
1022  rho_xface_inside = myhalf * (cell_data(i,j,k-1,Rho_comp) + cell_data(i-1,j,k-1,Rho_comp));
1023  theta_surf = (myhalf * (cell_data(i,j,k-1,RhoTheta_comp) + cell_data(i-1,j,k-1, RhoTheta_comp))) / rho_xface_inside;
1024  u_target = compute_if_most_target_vel(u1_cellaway, u2_cellaway, dx_z, z0, t_blank, theta_xface, theta_surf, tflux_in, Olen_in, l_stability_correction);
1025  bc_forcing_x = -(u_target - ux); // BC forcing pushes nonrelative velocity toward target velocity
1026  drag += bc_forcing_x * roof_mask * rho_xface * CdM * U_s;
1027 
1028  // south wall forcing
1029  u1_cellaway = u(i, j-1, k );
1030  u2_cellaway = fourth * ( w(i, j-1, k ) + w(i-1, j-1, k )
1031  + w(i, j-1, k+1) + w(i-1, j-1, k+1) ) ;
1032  rho_xface_inside = myhalf * ( cell_data(i,j+1,k,Rho_comp) + cell_data(i-1,j+1,k,Rho_comp) );
1033  theta_surf = (myhalf * (cell_data(i,j+1,k,RhoTheta_comp) + cell_data(i-1,j+1,k, RhoTheta_comp))) / rho_xface_inside;
1034  u_target = compute_if_most_target_vel(u1_cellaway, u2_cellaway, dx_z, z0, t_blank, theta_xface, theta_surf, tflux_in, Olen_in, l_stability_correction);
1035  bc_forcing_x = -(u_target - ux); // BC forcing pushes nonrelative velocity toward target velocity
1036  drag += bc_forcing_x * south_mask * rho_xface * CdM * U_s;
1037 
1038  // north wall forcing
1039  u1_cellaway = u(i, j+1, k ) ;
1040  u2_cellaway = fourth * ( w(i, j+1, k ) + w(i-1, j+1, k )
1041  + w(i, j+1, k+1) + w(i-1, j+1, k+1) ) ;
1042  rho_xface_inside = myhalf * ( cell_data(i,j-1,k,Rho_comp) + cell_data(i-1,j-1,k,Rho_comp) );
1043  theta_surf = (myhalf * (cell_data(i,j-1,k,RhoTheta_comp) + cell_data(i-1,j-1,k, RhoTheta_comp))) / rho_xface_inside;
1044  u_target = compute_if_most_target_vel(u1_cellaway, u2_cellaway, dx_z, z0, t_blank, theta_xface, theta_surf, tflux_in, Olen_in, l_stability_correction);
1045  bc_forcing_x = -(u_target - ux); // BC forcing pushes nonrelative velocity toward target velocity
1046  drag += bc_forcing_x * north_mask * rho_xface * CdM * U_s;
1047 
1048  // wall forcing (if not using most)
1049  drag += wall_mask * t_blank * rho_xface * CdM * ux * windspeed;
1050 
1051  // interior cell forcing
1052  drag += interior_mask * rho_xface * CdM * ux * windspeed;
1053 
1054  if (l_implicit_drag) {
1055  // point-implicit rescale of the aggregated drag
1056  const Real lambda = CdM * ( (roof_mask + south_mask + north_mask) * U_s
1057  + wall_mask * t_blank * windspeed
1058  + interior_mask * windspeed );
1059  xmom_src_arr(i,j,k) -= drag / (one + lambda*dt);
1060  } else if (is_slow_step && !use_ImmersedForcing_fast) {
1061  // limit drag term for anelastic for numerical stability
1062  Real d_drag = dt * -drag; // time step * acceleration like tendency
1063  Real wsmax_change = damp_alpha * amrex::max(amrex::Math::abs(ux), ws_floor); // aims to prevent oscillations around 0.
1064  if (amrex::Math::abs(ux) < 0.1){ // no damping for smaller velocities
1065  wsmax_change =one * amrex::max(amrex::Math::abs(ux), ws_floor);
1066  }
1067  d_drag = amrex::min(amrex::max(d_drag, -wsmax_change), wsmax_change);
1068  xmom_src_arr(i,j,k) += d_drag / dt; // put back as limited tendency
1069  } else {
1070  xmom_src_arr(i, j, k) -= drag;
1071  }
1072  });
1073  ParallelFor(tby, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
1074  {
1075  const Real ux = fourth * ( u(i , j , k ) + u(i , j-1, k )
1076  + u(i+1, j , k ) + u(i+1, j-1, k ) );
1077  const Real uy = v(i, j, k);
1078  const Real uz = fourth * ( w(i , j , k ) + w(i , j-1, k )
1079  + w(i , j , k+1) + w(i , j-1, k+1) );
1080  const amrex::Real windspeed = std::sqrt(ux * ux + uy * uy + uz * uz);
1081 
1082  const Real rho_yface = myhalf * ( cell_data(i,j,k,Rho_comp) + cell_data(i,j-1,k,Rho_comp) );
1083  const Real theta_yface = (myhalf * (cell_data(i,j,k ,RhoTheta_comp) + cell_data(i,j-1,k,RhoTheta_comp))) / rho_yface;
1084 
1085  Real t_blank = myhalf * (t_blank_arr(i , j , k ) + t_blank_arr(i-1, j , k ));
1086  Real t_blank_below = myhalf * (t_blank_arr(i , j , k-1) + t_blank_arr(i-1, j , k-1));
1087  Real t_blank_above = myhalf * (t_blank_arr(i , j , k+1) + t_blank_arr(i-1, j , k+1));
1088  Real t_blank_east = myhalf * (t_blank_arr(i+1, j , k ) + t_blank_arr(i+1, j-1, k ));
1089  Real t_blank_west = myhalf * (t_blank_arr(i-1, j , k ) + t_blank_arr(i-1, j-1, k ));
1090  if (t_blank < min_t_blank) { t_blank = zero; }
1091  if (k == 0) { t_blank_below = zero; }
1092  if (t_blank_below < min_t_blank) { t_blank_below = zero; }
1093  if (t_blank_above < min_t_blank) { t_blank_above = zero; }
1094  if (t_blank_east < min_t_blank) { t_blank_east = zero; }
1095  if (t_blank_west < min_t_blank) { t_blank_west = zero; }
1096  // round to four decimal places to avoid issues for cells with very small volfracs.
1097  t_blank = std::round(t_blank * Real(10000.0)) / Real(10000.0);
1098  t_blank_below = std::round(t_blank_below * Real(10000.0)) / Real(10000.0);
1099  t_blank_above = std::round(t_blank_above * Real(10000.0)) / Real(10000.0);
1100  t_blank_east = std::round(t_blank_east * Real(10000.0)) / Real(10000.0);
1101  t_blank_west = std::round(t_blank_west * Real(10000.0)) / Real(10000.0);
1102 
1103  const Real dx_z = (z_cc_arr) ? (z_cc_arr(i,j,k) - z_cc_arr(i,j,k-1)) : dx_arr[2];
1104  const Real drag_coefficient = alpha_m / std::pow(dx_x*dx_y*dx_z, one/three);
1105  const Real CdM = std::min(drag_coefficient / (windspeed + tiny), drag_coefficient);
1106 
1107  const Real roof_mask = (t_blank > zero && t_blank < t_blank_below && t_blank_above == zero && l_use_most) ? one : zero; // roof cell
1108  const Real west_mask = (t_blank > zero && t_blank <= t_blank_east && t_blank_west == zero && l_use_most) ? one : zero; // west wall cell
1109  const Real east_mask = (t_blank > zero && t_blank <= t_blank_west && t_blank_east == zero && l_use_most) ? one : zero; // east wall cell
1110  const Real wall_mask = (t_blank > zero && t_blank < one && !l_use_most) ? one : zero; // wall cell (not using most)
1111  const Real interior_mask = (t_blank == 1.0) ? one : zero; // interior cell
1112 
1113  Real drag = zero;
1114  Real u1_cellaway = zero;
1115  Real u2_cellaway = zero;
1116  Real rho_yface_inside = rho_yface;
1117  Real theta_surf = theta_yface;
1118  Real bc_forcing_y = zero;
1119  Real u_target = zero;
1120 
1121  // roof forcing
1122  u1_cellaway = fourth * ( u(i , j , k+1) + u(i , j-1, k+1)
1123  + u(i+1, j , k+1) + u(i+1, j-1, k+1) );
1124  u2_cellaway = v(i, j, k+1);
1125  rho_yface_inside = myhalf * ( cell_data(i,j,k-1,Rho_comp) + cell_data(i,j-1,k-1,Rho_comp) );
1126  theta_surf = (myhalf * (cell_data(i,j,k-1,RhoTheta_comp) + cell_data(i,j-1,k-1,RhoTheta_comp))) / rho_yface_inside;
1127  u_target = compute_if_most_target_vel(u1_cellaway, u2_cellaway, dx_z, z0, t_blank, theta_yface, theta_surf, tflux_in, Olen_in, l_stability_correction);
1128  bc_forcing_y = -(u_target - uy); // BC forcing pushes nonrelative velocity toward target velocity
1129  drag += bc_forcing_y * roof_mask * rho_yface * CdM * U_s;
1130 
1131  // west wall forcing
1132  u1_cellaway = v(i-1, j , k );
1133  u2_cellaway = fourth * ( w(i-1, j , k ) + w(i-1, j-1, k )
1134  + w(i-1, j , k+1) + w(i-1, j-1, k+1) );
1135  rho_yface_inside = myhalf * ( cell_data(i+1,j,k,Rho_comp) + cell_data(i+1,j-1,k,Rho_comp) );
1136  theta_surf = (myhalf * (cell_data(i+1,j,k,RhoTheta_comp) + cell_data(i+1,j-1,k,RhoTheta_comp))) / rho_yface_inside;
1137  u_target = compute_if_most_target_vel(u1_cellaway, u2_cellaway, dx_x, z0, t_blank, theta_yface, theta_surf, tflux_in, Olen_in, l_stability_correction);
1138  bc_forcing_y = -(u_target - uy); // BC forcing pushes nonrelative velocity toward target velocity
1139  drag += bc_forcing_y * west_mask * rho_yface * CdM * U_s;
1140 
1141  // east wall forcing
1142  u1_cellaway = v(i+1, j , k );
1143  u2_cellaway = fourth * ( w(i+1, j , k ) + w(i+1, j-1, k )
1144  + w(i+1, j , k+1) + w(i+1, j-1, k+1) );
1145  rho_yface_inside = myhalf * ( cell_data(i-1,j,k,Rho_comp) + cell_data(i-1,j-1,k,Rho_comp) );
1146  theta_surf = (myhalf * (cell_data(i-1,j,k,RhoTheta_comp) + cell_data(i-1,j-1,k,RhoTheta_comp))) / rho_yface_inside;
1147  u_target = compute_if_most_target_vel(u1_cellaway, u2_cellaway, dx_x, z0, t_blank, theta_yface, theta_surf, tflux_in, Olen_in, l_stability_correction);
1148  bc_forcing_y = -(u_target - uy); // BC forcing pushes nonrelative velocity toward target velocity
1149  drag += bc_forcing_y * east_mask * rho_yface * CdM * U_s;
1150 
1151  // wall forcing (if not using most)
1152  drag += wall_mask * t_blank * rho_yface * CdM * uy * windspeed;
1153 
1154  // interior cell forcing
1155  drag += interior_mask * rho_yface * CdM * uy * windspeed;
1156 
1157  if (l_implicit_drag) {
1158  // point-implicit rescale of the aggregated drag
1159  const Real lambda = CdM * ( (roof_mask + west_mask + east_mask) * U_s
1160  + wall_mask * t_blank * windspeed
1161  + interior_mask * windspeed );
1162  ymom_src_arr(i,j,k) -= drag / (one + lambda*dt);
1163  } else if (is_slow_step && !use_ImmersedForcing_fast) {
1164  // limit drag term for anelastic for numerical stability
1165  Real d_drag = dt * -drag; // time step * acceleration like tendency
1166  Real wsmax_change = damp_alpha * amrex::max(amrex::Math::abs(uy), ws_floor); // aims to prevent oscillations around 0.
1167  if (amrex::Math::abs(uy) < 0.1){ // no damping for smaller velocities
1168  wsmax_change =one * amrex::max(amrex::Math::abs(uy), ws_floor);
1169  }
1170  d_drag = amrex::min(amrex::max(d_drag, -wsmax_change), wsmax_change);
1171  ymom_src_arr(i,j,k) += d_drag / dt; // put back as limited tendency
1172  } else {
1173  ymom_src_arr(i, j, k) -= drag;
1174  }
1175  });
1176  ParallelFor(tbz, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
1177  {
1178  const Real ux = fourth * ( u(i , j , k ) + u(i+1, j , k )
1179  + u(i , j , k-1) + u(i+1, j , k-1) );
1180  const Real uy = fourth * ( v(i, j , k ) + v(i, j+1, k )
1181  + v(i, j , k-1) + v(i, j+1, k-1) );
1182  const Real uz = w(i, j, k);
1183  const amrex::Real windspeed = std::sqrt(ux * ux + uy * uy + uz * uz);
1184 
1185  const Real rho_zface = myhalf * ( cell_data(i,j,k,Rho_comp) + cell_data(i,j,k-1,Rho_comp) );
1186  const Real theta_zface = (myhalf * (cell_data(i,j,k,RhoTheta_comp) + cell_data(i,j,k-1,RhoTheta_comp))) / rho_zface;
1187 
1188  Real t_blank = myhalf * (t_blank_arr(i ,j , k) + t_blank_arr(i , j , k-1));
1189  Real t_blank_below = myhalf * (t_blank_arr(i ,j , k-1) + t_blank_arr(i , j , k-2));
1190  Real t_blank_above = myhalf * (t_blank_arr(i ,j , k) + t_blank_arr(i , j , k+1));
1191  Real t_blank_north = myhalf * (t_blank_arr(i ,j+1, k) + t_blank_arr(i , j+1, k-1));
1192  Real t_blank_south = myhalf * (t_blank_arr(i ,j-1, k) + t_blank_arr(i , j-1, k-1));
1193  Real t_blank_east = myhalf * (t_blank_arr(i+1,j , k) + t_blank_arr(i+1, j , k-1));
1194  Real t_blank_west = myhalf * (t_blank_arr(i-1,j , k) + t_blank_arr(i-1, j , k-1));
1195  if (t_blank < min_t_blank) { t_blank = zero; }
1196  if (k == 0) { t_blank_below = zero; }
1197  if (t_blank_below < min_t_blank) { t_blank_below = zero; }
1198  if (t_blank_above < min_t_blank) { t_blank_above = zero; }
1199  if (t_blank_north < min_t_blank) { t_blank_north = zero; }
1200  if (t_blank_south < min_t_blank) { t_blank_south = zero; }
1201  if (t_blank_east < min_t_blank) { t_blank_east = zero; }
1202  if (t_blank_west < min_t_blank) { t_blank_west = zero; }
1203  // round to four decimal places to avoid issues for cells with very small volfracs.
1204  t_blank = std::round(t_blank * Real(10000.0)) / Real(10000.0);
1205  t_blank_below = std::round(t_blank_below * Real(10000.0)) / Real(10000.0);
1206  t_blank_above = std::round(t_blank_above * Real(10000.0)) / Real(10000.0);
1207  t_blank_north = std::round(t_blank_north * Real(10000.0)) / Real(10000.0);
1208  t_blank_south = std::round(t_blank_south * Real(10000.0)) / Real(10000.0);
1209  t_blank_east = std::round(t_blank_east * Real(10000.0)) / Real(10000.0);
1210  t_blank_west = std::round(t_blank_west * Real(10000.0)) / Real(10000.0);
1211 
1212  const Real dx_z = (z_cc_arr) ? (z_cc_arr(i,j,k) - z_cc_arr(i,j,k-1)) : dx_arr[2];
1213  const Real drag_coefficient = alpha_m / std::pow(dx_x*dx_y*dx_z, one/three);
1214  const Real CdM = std::min(drag_coefficient / (windspeed + tiny), drag_coefficient);
1215 
1216  const Real south_mask = (t_blank > zero && t_blank <= t_blank_north && t_blank_south == zero && l_use_most && k >= 1) ? one : zero; // south wall cell
1217  const Real north_mask = (t_blank > zero && t_blank <= t_blank_south && t_blank_north == zero && l_use_most && k >= 1) ? one : zero; // north wall cell
1218  const Real west_mask = (t_blank > zero && t_blank <= t_blank_east && t_blank_west == zero && l_use_most && k >= 1) ? one : zero; // west wall cell
1219  const Real east_mask = (t_blank > zero && t_blank <= t_blank_west && t_blank_east == zero && l_use_most && k >= 1) ? one : zero; // east wall cell
1220  const Real wall_mask = (t_blank > zero && t_blank < one && !l_use_most) ? one : zero; // wall cell (not using most)
1221  const Real interior_mask = (t_blank == 1.0) ? one : zero; // interior cell
1222 
1223  Real drag = zero;
1224  Real u1_cellaway = zero;
1225  Real u2_cellaway = zero;
1226  Real rho_zface_inside = rho_zface;
1227  Real theta_surf = theta_zface;
1228  Real bc_forcing_z = zero;
1229  Real u_target = zero;
1230 
1231  // south wall forcing
1232  u1_cellaway = fourth * ( u(i , j-1, k ) + u(i+1, j-1, k )
1233  + u(i , j-1, k-1) + u(i+1, j-1, k-1) );
1234  u2_cellaway = w(i, j-1, k);
1235  rho_zface_inside = myhalf * ( cell_data(i,j+1,k,Rho_comp) + cell_data(i,j+1,k-1,Rho_comp) );
1236  theta_surf = (myhalf * (cell_data(i,j+1,k,RhoTheta_comp) + cell_data(i,j+1,k-1,RhoTheta_comp))) / rho_zface_inside;
1237  u_target = compute_if_most_target_vel(u1_cellaway, u2_cellaway, dx_y, z0, t_blank, theta_zface, theta_surf, tflux_in, Olen_in, l_stability_correction);
1238  bc_forcing_z = -(u_target - uz); // BC forcing pushes nonrelative velocity toward target velocity
1239  drag += bc_forcing_z * south_mask * rho_zface * CdM * U_s;
1240 
1241  // north wall forcing
1242  u1_cellaway = fourth * ( u(i , j+1, k ) + u(i+1, j+1, k )
1243  + u(i , j+1, k-1) + u(i+1, j+1, k-1) );
1244  u2_cellaway = w(i, j+1, k);
1245  rho_zface_inside = myhalf * ( cell_data(i,j-1,k,Rho_comp) + cell_data(i,j-1,k-1,Rho_comp) );
1246  theta_surf = (myhalf * (cell_data(i,j-1,k,RhoTheta_comp) + cell_data(i,j-1,k-1,RhoTheta_comp))) / rho_zface_inside;
1247  u_target = compute_if_most_target_vel(u1_cellaway, u2_cellaway, dx_y, z0, t_blank, theta_zface, theta_surf, tflux_in, Olen_in, l_stability_correction);
1248  bc_forcing_z = -(u_target - uz); // BC forcing pushes nonrelative velocity toward target velocity
1249  drag += bc_forcing_z * north_mask * rho_zface * CdM * U_s;
1250 
1251  // west wall forcing
1252  u1_cellaway = fourth * ( v(i-1, j , k ) + v(i-1, j+1, k )
1253  + v(i-1, j , k-1) + v(i-1, j+1, k-1) );
1254  u2_cellaway = w(i-1, j, k);
1255  rho_zface_inside = myhalf * ( cell_data(i+1,j,k,Rho_comp) + cell_data(i+1,j,k-1,Rho_comp) );
1256  theta_surf = (myhalf * (cell_data(i+1,j,k,RhoTheta_comp) + cell_data(i+1,j,k-1,RhoTheta_comp))) / rho_zface_inside;
1257  u_target = compute_if_most_target_vel(u1_cellaway, u2_cellaway, dx_x, z0, t_blank, theta_zface, theta_surf, tflux_in, Olen_in, l_stability_correction);
1258  bc_forcing_z = -(u_target - uz); // BC forcing pushes nonrelative velocity toward target velocity
1259  drag += bc_forcing_z * west_mask * rho_zface * CdM * U_s;
1260 
1261  // east wall forcing
1262  u1_cellaway = fourth * ( v(i+1, j , k ) + v(i+1, j+1, k )
1263  + v(i+1, j , k-1) + v(i+1, j+1, k-1) );
1264  u2_cellaway = w(i+1, j, k);
1265  rho_zface_inside = myhalf * ( cell_data(i-1,j,k,Rho_comp) + cell_data(i-1,j,k-1,Rho_comp) );
1266  theta_surf = (myhalf * (cell_data(i-1,j,k,RhoTheta_comp) + cell_data(i-1,j,k-1,RhoTheta_comp))) / rho_zface_inside;
1267  u_target = compute_if_most_target_vel(u1_cellaway, u2_cellaway, dx_x, z0, t_blank, theta_zface, theta_surf, tflux_in, Olen_in, l_stability_correction);
1268  bc_forcing_z = -(u_target - uz); // BC forcing pushes nonrelative velocity toward target velocity
1269  drag += bc_forcing_z * east_mask * rho_zface * CdM * U_s;
1270 
1271  // wall forcing (if not using most)
1272  drag += wall_mask * t_blank * rho_zface * CdM * uz * windspeed;
1273 
1274  // interior cell forcing
1275  drag += interior_mask * rho_zface * CdM * uz * windspeed;
1276 
1277  if (l_implicit_drag) {
1278  // point-implicit rescale of the aggregated drag
1279  const Real lambda = CdM * ( (south_mask + north_mask + west_mask + east_mask) * U_s
1280  + wall_mask * t_blank * windspeed
1281  + interior_mask * windspeed );
1282  zmom_src_arr(i,j,k) -= drag / (one + lambda*dt);
1283  } else if (is_slow_step && !use_ImmersedForcing_fast) {
1284  // limit drag term for anelastic for numerical stability
1285  Real d_drag = dt * -drag; // time step * acceleration like tendency
1286  Real wsmax_change = damp_alpha * amrex::max(amrex::Math::abs(uz), ws_floor); // aims to prevent oscillations around 0.
1287  if (amrex::Math::abs(uz) < 0.1){ // no damping for smaller velocities
1288  wsmax_change = one * amrex::max(amrex::Math::abs(uz), ws_floor);
1289  }
1290  d_drag = amrex::min(amrex::max(d_drag, -wsmax_change), wsmax_change);
1291  zmom_src_arr(i,j,k) += d_drag / dt; // put back as limited tendency
1292  } else {
1293  zmom_src_arr(i, j, k) -= drag;
1294  }
1295  });
1296  }
1297 
1298  // *****************************************************************************
1299  // 10. Enforce constant mass flux
1300  // *****************************************************************************
1301  if (is_slow_step && (enforce_massflux_x || enforce_massflux_y)) {
1302  Real tau_inv = one / solverChoice.const_massflux_tau;
1303 
1304  ParallelFor(tbx, tby,
1305  [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
1306  xmom_src_arr(i, j, k) += tau_inv * (rhoUA_target - rhoUA);
1307  },
1308  [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
1309  ymom_src_arr(i, j, k) += tau_inv * (rhoVA_target - rhoVA);
1310  });
1311  }
1312 
1313  } // mfi
1314 }
void ApplyBndryForcing_Forecast(const SolverChoice &solverChoice, const Geometry geom, const Box &tbx, const Box &tby, const Box &tbz, const Array4< const Real > &z_phys_nd, const Array4< Real > &rho_u_rhs, const Array4< Real > &rho_v_rhs, const Array4< Real > &rho_w_rhs, const Array4< const Real > &rho_u, const Array4< const Real > &rho_v, const Array4< const Real > &rho_w, const Array4< const Real > &rho_u_initial_state, const Array4< const Real > &rho_v_initial_state, const Array4< const Real > &rho_w_initial_state, const Array4< const Real > &cons_initial_state)
Definition: ERF_ApplyBndryForcing_Forecast.cpp:8
void ApplySpongeZoneBCsForMom(const SpongeChoice &spongeChoice, const Geometry geom, const Box &tbx, const Box &tby, const Box &tbz, const Array4< Real > &rho_u_rhs, const Array4< Real > &rho_v_rhs, const Array4< Real > &rho_w_rhs, const Array4< const Real > &rho_u, const Array4< const Real > &rho_v, const Array4< const Real > &rho_w, const Array4< const Real > &r0, const Array4< const Real > &z_phys_nd, const Array4< const Real > &z_phys_cc)
Definition: ERF_ApplySpongeZoneBCs.cpp:169
void ApplySpongeZoneBCsForMom_ReadFromFile(const SpongeChoice &spongeChoice, const Geometry geom, const Box &tbx, const Box &tby, const Array4< const Real > &cell_data, const Array4< const Real > &z_phys_cc, const Array4< Real > &rho_u_rhs, const Array4< Real > &rho_v_rhs, const Array4< const Real > &rho_u, const Array4< const Real > &rho_v, const Vector< Real * > d_sponge_ptrs_at_lev)
Definition: ERF_ApplySpongeZoneBCs_ReadFromFile.cpp:8
constexpr amrex::Real three
Definition: ERF_Constants.H:11
constexpr amrex::Real two
Definition: ERF_Constants.H:10
constexpr amrex::Real one
Definition: ERF_Constants.H:9
constexpr amrex::Real fourth
Definition: ERF_Constants.H:14
constexpr amrex::Real PI
Definition: ERF_Constants.H:42
@ ubar
Definition: ERF_DataStruct.H:152
@ wbar
Definition: ERF_DataStruct.H:152
@ vbar
Definition: ERF_DataStruct.H:152
DirectionSelector< 2 > ZDir
Definition: ERF_DirectionSelector.H:38
#define Rho_comp
Definition: ERF_IndexDefines.H:36
#define RhoTheta_comp
Definition: ERF_IndexDefines.H:37
amrex::GpuArray< Real, AMREX_SPACEDIM > dxInv
Definition: ERF_InitCustomPertVels_ParticleTests.H:17
AMREX_ALWAYS_ASSERT(bx.length()[2]==khi+1)
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real compute_if_most_target_vel(const amrex::Real u1_2r, const amrex::Real u2_2r, const amrex::Real delta, const amrex::Real z0, const amrex::Real t_blank, const amrex::Real theta_xface, const amrex::Real theta_surf, const amrex::Real tflux_in, const amrex::Real Olen_in, const bool stability_correction)
Definition: ERF_MakeMomSources.cpp:18
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);})
void NumericalDiffusion_Ymom(const Box &bx, 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:151
void NumericalDiffusion_Xmom(const Box &bx, 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:85
Real w
Definition: ERF_Plotfile2DInterpolator.cpp:22
AMREX_FORCE_INLINE IntVect offset(const int face_dir, const int normal)
Definition: ERF_ReadBndryPlanes.cpp:31
Definition: ERF_PlaneAverage.H:14
eb_aux_ const * get_w_const_factory() const noexcept
Return the ERF auxiliary z-face EB factory.
Definition: ERF_EB.H:123
eb_aux_ const * get_v_const_factory() const noexcept
Return the ERF auxiliary y-face EB factory.
Definition: ERF_EB.H:121
eb_aux_ const * get_u_const_factory() const noexcept
Return the ERF auxiliary x-face EB factory.
Definition: ERF_EB.H:119
@ r0_comp
Definition: ERF_IndexDefines.H:73
@ ymom
Definition: ERF_IndexDefines.H:196
@ cons
Definition: ERF_IndexDefines.H:194
@ zmom
Definition: ERF_IndexDefines.H:197
@ xmom
Definition: ERF_IndexDefines.H:195
@ nr
Definition: ERF_Morrison.H:46
@ xvel
Definition: ERF_IndexDefines.H:177
@ cons
Definition: ERF_IndexDefines.H:176
@ yvel
Definition: ERF_IndexDefines.H:178
@ ww
Definition: ERF_AdvanceWSM6.cpp:105
real(kind=kind_phys), parameter, private r0
Definition: ERF_module_mp_wsm6.F90:21
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_U
Whether Rayleigh damping is applied to x-momentum.
Definition: ERF_DampingStruct.H:94
bool rayleigh_damp_V
Whether Rayleigh damping is applied to y-momentum.
Definition: ERF_DampingStruct.H:95
bool rayleigh_damp_W
Whether Rayleigh damping is applied to vertical momentum.
Definition: ERF_DampingStruct.H:96
amrex::Vector< amrex::Real > input_sounding_time
Physical time associated with each input sounding file.
Definition: ERF_InputSoundingData.H:462
amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real > > V_inp_sound_d
Device y velocity profiles.
Definition: ERF_InputSoundingData.H:475
amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real > > U_inp_sound_d
Device x velocity profiles.
Definition: ERF_InputSoundingData.H:475
amrex::Real tau_nudging
Nudging time scale for relaxing toward input sounding data.
Definition: ERF_InputSoundingData.H:459
amrex::Real const_massflux_v
Target constant mass flux in the y direction.
Definition: ERF_DataStruct.H:1651
static InitType init_type
Initial-condition source selected for the run.
Definition: ERF_DataStruct.H:1362
amrex::Real coriolis_factor
Twice the planetary rotation rate used for Coriolis forcing.
Definition: ERF_DataStruct.H:1485
bool variable_coriolis
Whether spatially varying Coriolis forcing is enabled.
Definition: ERF_DataStruct.H:1616
amrex::Real if_Cd_momentum
Immersed-forcing drag coefficient for momentum.
Definition: ERF_DataStruct.H:1452
amrex::Real if_Olen_in
Input Obukhov length for immersed-forcing MOST [m].
Definition: ERF_DataStruct.H:1462
int ave_plane
Averaging plane index used by diagnostics.
Definition: ERF_DataStruct.H:1618
amrex::Real const_massflux_u
Target constant mass flux in the x direction.
Definition: ERF_DataStruct.H:1650
amrex::Real if_z0
Immersed-forcing roughness length [m].
Definition: ERF_DataStruct.H:1458
bool forest_substep
Whether canopy source terms are applied only during substeps.
Definition: ERF_DataStruct.H:1449
amrex::Real num_diff_coeff
Numerical diffusion coefficient after input scaling.
Definition: ERF_DataStruct.H:1601
bool hindcast_lateral_forcing
Whether hindcast lateral forcing is enabled.
Definition: ERF_DataStruct.H:1660
bool use_coriolis
Whether Coriolis forcing is enabled.
Definition: ERF_DataStruct.H:1442
static MeshType mesh_type
Vertical mesh representation.
Definition: ERF_DataStruct.H:1377
SpongeChoice spongeChoice
Sponge-layer options.
Definition: ERF_DataStruct.H:1392
bool have_geo_wind_profile
Whether a geostrophic wind profile has been configured.
Definition: ERF_DataStruct.H:1614
DampingChoice dampingChoice
Damping-related options.
Definition: ERF_DataStruct.H:1391
amrex::Real if_damp_alpha
Immersed-forcing damping coefficient.
Definition: ERF_DataStruct.H:1466
amrex::Real if_ws_floor
Wind-speed floor for immersed-forcing MOST [m/s].
Definition: ERF_DataStruct.H:1465
bool do_forest_drag
Whether forest canopy drag is enabled.
Definition: ERF_DataStruct.H:1647
int massflux_khi
Upper vertical index for constant-mass-flux forcing.
Definition: ERF_DataStruct.H:1656
static TerrainType terrain_type
Terrain or immersed-boundary representation.
Definition: ERF_DataStruct.H:1368
amrex::Real cosphi
Cosine of the latitude used for Coriolis forcing.
Definition: ERF_DataStruct.H:1486
static BuildingsType buildings_type
Building representation.
Definition: ERF_DataStruct.H:1371
int massflux_klo
Lower vertical index for constant-mass-flux forcing.
Definition: ERF_DataStruct.H:1655
amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > abl_geo_forcing
Applied geostrophic-wind forcing vector.
Definition: ERF_DataStruct.H:1612
amrex::Real if_surf_temp_flux
Immersed-forcing surface temperature flux [K m/s].
Definition: ERF_DataStruct.H:1459
bool custom_w_subsidence
Whether custom vertical subsidence is enabled.
Definition: ERF_DataStruct.H:1492
bool immersed_forcing_substep
Whether immersed-forcing source terms are applied only during substeps.
Definition: ERF_DataStruct.H:1448
bool if_use_most
Whether immersed-forcing MOST is enabled.
Definition: ERF_DataStruct.H:1463
amrex::Real const_massflux_tau
Relaxation time scale for constant-mass-flux forcing.
Definition: ERF_DataStruct.H:1652
amrex::Real sinphi
Sine of the latitude used for Coriolis forcing.
Definition: ERF_DataStruct.H:1487
bool if_implicit_drag
Definition: ERF_DataStruct.H:1456
bool do_mom_advection
Whether custom vertical subsidence is applied to momentum.
Definition: ERF_DataStruct.H:1494
bool if_stability_correction
Whether immersed-forcing stability corrections are enabled.
Definition: ERF_DataStruct.H:1464
bool custom_forcing_prim_vars
Whether custom forcing operates on primitive variables.
Definition: ERF_DataStruct.H:1496
bool nudging_from_input_sounding
Whether solution fields are nudged toward input sounding data.
Definition: ERF_DataStruct.H:1502
static SpongeType sponge_type
Selected sponge damping model.
Definition: ERF_SpongeStruct.H:100
Here is the call graph for this function: