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

void make_mom_sources (double time_d, double, 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, const MultiFab *surface_state_at_lev, InputSoundingData &input_sounding_data, const eb_ &ebfact, bool is_slow_step)
 

Function Documentation

◆ make_mom_sources()

void make_mom_sources ( double  time_d,
double  ,
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,
const MultiFab *  surface_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
69 {
70  BL_PROFILE_REGION("erf_make_mom_sources()");
71 
72  Real time = static_cast<Real>(time_d);
73 
74  Box domain(geom.Domain());
75  const GpuArray<Real, AMREX_SPACEDIM> dxInv = geom.InvCellSizeArray();
76 
77  // Initialize sources to zero each time we may use them
78  xmom_src.setVal(0.0);
79  ymom_src.setVal(0.0);
80  zmom_src.setVal(0.0);
81 
82  MultiFab r_hse (base_state, make_alias, BaseState::r0_comp , 1);
83 
84  // flags to apply certain source terms in substep call only
85  bool use_Rayleigh_fast_uv = ( (solverChoice.dampingChoice.rayleigh_damping_type == RayleighDampingType::FastExplicit) ||
87  bool use_Rayleigh_fast_w = (solverChoice.dampingChoice.rayleigh_damping_type == RayleighDampingType::FastExplicit);
88  bool use_canopy_fast = solverChoice.forest_substep;
89  bool use_ImmersedForcing_fast = solverChoice.immersed_forcing_substep;
90 
91  // *****************************************************************************
92  // Define source term for all three components of momenta from
93  // 1. Coriolis forcing for (xmom,ymom,zmom)
94  // 2. Rayleigh damping for (xmom,ymom,zmom)
95  // 3. Constant / height-dependent geostrophic forcing
96  // 4. Subsidence
97  // 5. Nudging towards input sounding data
98  // 6. Numerical diffusion for (xmom,ymom,zmom)
99  // 7. Sponge
100  // 8. Forest canopy
101  // 9a. Immersed forcing for terrain
102  // 9b. Immersed forcing for buildings
103  // 10. Constant mass flux
104  // *****************************************************************************
105  // NOTE: buoyancy is now computed in a separate routine - it should not appear here
106  // *****************************************************************************
107  //const bool l_use_ndiff = solverChoice.use_num_diff;
108 
109  if (solverChoice.terrain_type == TerrainType::ImmersedForcing) {
110  if (solverChoice.do_forest_drag) {
111  amrex::Error(" Currently forest canopy cannot be used with immersed forcing");
112  }
113  }
114 
115 
116  // *****************************************************************************
117  // Data for Coriolis forcing
118  // *****************************************************************************
119  auto use_coriolis = solverChoice.use_coriolis;
120  auto coriolis_factor = solverChoice.coriolis_factor;
121  auto cosphi = solverChoice.cosphi;
122  auto sinphi = solverChoice.sinphi;
123  auto var_coriolis = solverChoice.variable_coriolis;
124 
125  // *****************************************************************************
126  // Flag for Geostrophic forcing
127  // *****************************************************************************
128  auto abl_geo_forcing = solverChoice.abl_geo_forcing;
129  auto geo_wind_profile = solverChoice.have_geo_wind_profile;
130 
131  // *****************************************************************************
132  // Data for Rayleigh damping
133  // *****************************************************************************
134  auto rayleigh_damp_U = solverChoice.dampingChoice.rayleigh_damp_U;
135  auto rayleigh_damp_V = solverChoice.dampingChoice.rayleigh_damp_V;
136  auto rayleigh_damp_W = solverChoice.dampingChoice.rayleigh_damp_W;
137 
138  Real* ubar = d_rayleigh_ptrs_at_lev[Rayleigh::ubar];
139  Real* vbar = d_rayleigh_ptrs_at_lev[Rayleigh::vbar];
140  Real* wbar = d_rayleigh_ptrs_at_lev[Rayleigh::wbar];
141 
142  // *****************************************************************************
143  // Data for constant mass flux
144  // *****************************************************************************
145  bool enforce_massflux_x = (solverChoice.const_massflux_u != 0);
146  bool enforce_massflux_y = (solverChoice.const_massflux_v != 0);
147  Real U_target = solverChoice.const_massflux_u;
148  Real V_target = solverChoice.const_massflux_v;
149  int massflux_klo = solverChoice.massflux_klo;
150  int massflux_khi = solverChoice.massflux_khi;
151 
152  // These will be updated by integrating through the planar average profiles
153  Real rhoUA_target{0};
154  Real rhoVA_target{0};
155  Real rhoUA{0};
156  Real rhoVA{0};
157 
158  // *****************************************************************************
159  // Planar averages for subsidence, nudging, or constant mass flux
160  // *****************************************************************************
161  Table1D<Real> dptr_r_plane, dptr_u_plane, dptr_v_plane;
162  TableData<Real, 1> r_plane_tab, u_plane_tab, v_plane_tab;
163 
164  if (is_slow_step && (dptr_wbar_sub || solverChoice.nudging_from_input_sounding ||
165  enforce_massflux_x || enforce_massflux_y))
166  {
167  // The plane averaging operates at fixed z not fixed height so is not correct for variable dz
168  AMREX_ALWAYS_ASSERT(solverChoice.mesh_type != MeshType::VariableDz);
169 
170  const int offset = 1;
171  const int u_offset = 1;
172  const int v_offset = 1;
173 
174  //
175  // We use the alias here to control ncomp inside the PlaneAverage
176  //
177  MultiFab cons(S_data[IntVars::cons], make_alias, 0, 1);
178 
179  IntVect ng_c = S_data[IntVars::cons].nGrowVect(); ng_c[2] = offset;
180  PlaneAverage r_ave(&cons, geom, solverChoice.ave_plane, ng_c);
181  r_ave.compute_averages(ZDir(), r_ave.field());
182 
183  int ncell = r_ave.ncell_line();
184  Gpu::HostVector< Real> r_plane_h(ncell);
185  Gpu::DeviceVector< Real> r_plane_d(ncell);
186 
187  r_ave.line_average(Rho_comp, r_plane_h);
188 
189  Gpu::copyAsync(Gpu::hostToDevice, r_plane_h.begin(), r_plane_h.end(), r_plane_d.begin());
190 
191  Real* dptr_r = r_plane_d.data();
192 
193  Box tdomain = domain; tdomain.grow(2,ng_c[2]);
194  r_plane_tab.resize({tdomain.smallEnd(2)}, {tdomain.bigEnd(2)});
195 
196  dptr_r_plane = r_plane_tab.table();
197  ParallelFor(ncell, [=] AMREX_GPU_DEVICE (int k) noexcept
198  {
199  dptr_r_plane(k-offset) = dptr_r[k];
200  });
201 
202  // U and V momentum
203  IntVect ng_u = S_data[IntVars::xmom].nGrowVect(); ng_u[2] = u_offset;
204  PlaneAverage u_ave(&(S_data[IntVars::xmom]), geom, solverChoice.ave_plane, ng_u);
205 
206  IntVect ng_v = S_data[IntVars::ymom].nGrowVect(); ng_v[2] = v_offset;
207  PlaneAverage v_ave(&(S_data[IntVars::ymom]), geom, solverChoice.ave_plane, ng_v);
208 
209  u_ave.compute_averages(ZDir(), u_ave.field());
210  v_ave.compute_averages(ZDir(), v_ave.field());
211 
212  int u_ncell = u_ave.ncell_line();
213  int v_ncell = v_ave.ncell_line();
214  Gpu::HostVector< Real> u_plane_h(u_ncell), v_plane_h(v_ncell);
215  Gpu::DeviceVector< Real> u_plane_d(u_ncell), v_plane_d(v_ncell);
216 
217  u_ave.line_average(0, u_plane_h);
218  v_ave.line_average(0, v_plane_h);
219 
220  Gpu::copyAsync(Gpu::hostToDevice, u_plane_h.begin(), u_plane_h.end(), u_plane_d.begin());
221  Gpu::copyAsync(Gpu::hostToDevice, v_plane_h.begin(), v_plane_h.end(), v_plane_d.begin());
222 
223  Real* dptr_u = u_plane_d.data();
224  Real* dptr_v = v_plane_d.data();
225 
226  Box udomain = domain; udomain.grow(2,ng_u[2]);
227  Box vdomain = domain; vdomain.grow(2,ng_v[2]);
228  u_plane_tab.resize({udomain.smallEnd(2)}, {udomain.bigEnd(2)});
229  v_plane_tab.resize({vdomain.smallEnd(2)}, {vdomain.bigEnd(2)});
230 
231  dptr_u_plane = u_plane_tab.table();
232  ParallelFor(u_ncell, [=] AMREX_GPU_DEVICE (int k) noexcept
233  {
234  dptr_u_plane(k-u_offset) = dptr_u[k];
235  });
236 
237  dptr_v_plane = v_plane_tab.table();
238  ParallelFor(v_ncell, [=] AMREX_GPU_DEVICE (int k) noexcept
239  {
240  dptr_v_plane(k-v_offset) = dptr_v[k];
241  });
242 
243  // sum in z for massflux adjustment
244  if (enforce_massflux_x || enforce_massflux_y) {
245  Real Lx = geom.ProbHi(0) - geom.ProbLo(0);
246  Real Ly = geom.ProbHi(1) - geom.ProbLo(1);
247 
248  if (solverChoice.mesh_type == MeshType::ConstantDz) {
249  // note: massflux_khi corresponds to unstaggered indices in this case
250  rhoUA = std::accumulate(u_plane_h.begin() + u_offset + massflux_klo,
251  u_plane_h.begin() + u_offset + massflux_khi+1, zero);
252  rhoVA = std::accumulate(v_plane_h.begin() + v_offset + massflux_klo,
253  v_plane_h.begin() + v_offset + massflux_khi+1, zero);
254  rhoUA_target = std::accumulate(r_plane_h.begin() + offset + massflux_klo,
255  r_plane_h.begin() + offset + massflux_khi+1, zero);
256  rhoVA_target = rhoUA_target;
257 
258  rhoUA *= geom.CellSize(2) * Ly;
259  rhoVA *= geom.CellSize(2) * Lx;
260  rhoUA_target *= geom.CellSize(2) * Ly;
261  rhoVA_target *= geom.CellSize(2) * Lx;
262 
263  } else if (solverChoice.mesh_type == MeshType::StretchedDz) {
264  // note: massflux_khi corresponds to staggered indices in this case
265  for (int k=massflux_klo; k < massflux_khi; ++k) {
266  rhoUA += u_plane_h[k + u_offset] * stretched_dz_h[k];
267  rhoVA += v_plane_h[k + v_offset] * stretched_dz_h[k];
268  rhoUA_target += r_plane_h[k + offset] * stretched_dz_h[k];
269  }
270  rhoVA_target = rhoUA_target;
271 
272  rhoUA *= Ly;
273  rhoVA *= Lx;
274  rhoUA_target *= Ly;
275  rhoVA_target *= Lx;
276  }
277 
278  // at this point, this is integrated rho*dA
279  rhoUA_target *= U_target;
280  rhoVA_target *= V_target;
281 
282  Print() << "Integrated mass flux : " << rhoUA << " " << rhoVA
283  << " (target: " << rhoUA_target << " " << rhoVA_target << ")"
284  << std::endl;
285  }
286  }
287 
288  // *****************************************************************************
289  // Add all the other forcings
290  // *****************************************************************************
291  for ( MFIter mfi(S_data[IntVars::cons]); mfi.isValid(); ++mfi)
292  {
293  Box tbx = mfi.nodaltilebox(0);
294  Box tby = mfi.nodaltilebox(1);
295  Box tbz = mfi.nodaltilebox(2);
296  if (tbz.bigEnd(2) == domain.bigEnd(2)+1) tbz.growHi(2,-1);
297 
298  const Array4<const Real>& cell_data = S_data[IntVars::cons].array(mfi);
299  const Array4<const Real>& rho_u = S_data[IntVars::xmom].array(mfi);
300  const Array4<const Real>& rho_v = S_data[IntVars::ymom].array(mfi);
301  const Array4<const Real>& rho_w = S_data[IntVars::zmom].array(mfi);
302 
303  const Array4<const Real>& u = xvel.array(mfi);
304  const Array4<const Real>& v = yvel.array(mfi);
305  const Array4<const Real>& w = wvel.array(mfi);
306 
307  const Array4< Real>& xmom_src_arr = xmom_src.array(mfi);
308  const Array4< Real>& ymom_src_arr = ymom_src.array(mfi);
309  const Array4< Real>& zmom_src_arr = zmom_src.array(mfi);
310 
311  const Array4<const Real>& r0 = r_hse.const_array(mfi);
312 
313  const Array4<const Real>& f_drag_arr = (forest_drag) ? forest_drag->const_array(mfi) :
314  Array4<const Real>{};
315  const Array4<const Real>& t_blank_arr = (terrain_blank) ? terrain_blank->const_array(mfi) :
316  Array4<const Real>{};
317 
318  const Array4<const Real>& cphi_arr = (cosPhi_mf) ? cosPhi_mf->const_array(mfi) :
319  Array4<const Real>{};
320  const Array4<const Real>& sphi_arr = (sinPhi_mf) ? sinPhi_mf->const_array(mfi) :
321  Array4<const Real>{};
322 
323  const Array4<const Real>& z_nd_arr = z_phys_nd->const_array(mfi);
324  const Array4<const Real>& z_cc_arr = z_phys_cc->const_array(mfi);
325 
326 
327  // *****************************************************************************
328  // 1. Add Coriolis forcing (this assumes east is +x, north is +y)
329  // *****************************************************************************
330  if (use_coriolis && is_slow_step) {
331  if(solverChoice.init_type == InitType::HindCast) {
332  const Array4<const Real>& latlon_arr = (*forecast_state_at_lev)[4].array(mfi);
333  ParallelFor(tbx, tby, tbz,
334  [=] AMREX_GPU_DEVICE (int i, int j, int k)
335  {
336  Real rho_on_u_face = myhalf * ( cell_data(i,j,k,Rho_comp) + cell_data(i-1,j,k,Rho_comp) );
337  Real v_loc = fourth * (v(i,j+1,k) + v(i,j,k) + v(i-1,j+1,k) + v(i-1,j,k));
338  Real w_loc = fourth * (w(i,j,k+1) + w(i,j,k) + w(i,j-1,k+1) + w(i,j-1,k));
339  Real latitude = latlon_arr(i,j,k,0);
340  Real sphi_loc = std::sin(latitude*PI/Real(180.0));
341  Real cphi_loc = std::cos(latitude*PI/Real(180.0));
342  xmom_src_arr(i, j, k) += coriolis_factor * rho_on_u_face * (v_loc * sphi_loc - w_loc * cphi_loc);
343  },
344  [=] AMREX_GPU_DEVICE (int i, int j, int k) {
345  Real rho_on_v_face = myhalf * ( cell_data(i,j,k,Rho_comp) + cell_data(i,j-1,k,Rho_comp) );
346  Real u_loc = fourth * (u(i+1,j,k) + u(i,j,k) + u(i+1,j-1,k) + u(i,j-1,k));
347  Real latitude = latlon_arr(i,j,k,0);
348  Real sphi_loc = std::sin(latitude*PI/Real(180.0));
349  ymom_src_arr(i, j, k) += -coriolis_factor * rho_on_v_face * u_loc * sphi_loc;
350  },
351  [=] AMREX_GPU_DEVICE (int i, int j, int k) {
352  Real rho_on_w_face = myhalf * ( cell_data(i,j,k,Rho_comp) + cell_data(i,j,k-1,Rho_comp) );
353  Real u_loc = fourth * (u(i+1,j,k) + u(i,j,k) + u(i+1,j,k-1) + u(i,j,k-1));
354  Real latitude = latlon_arr(i,j,k,0);
355  Real cphi_loc = std::cos(latitude*PI/Real(180.0));
356  zmom_src_arr(i, j, k) += coriolis_factor * rho_on_w_face * u_loc * cphi_loc;
357  });
358  }
359  else if (var_coriolis && (sinPhi_mf) && (cosPhi_mf)) {
360  ParallelFor(tbx, tby, tbz,
361  [=] AMREX_GPU_DEVICE (int i, int j, int k)
362  {
363  Real rho_on_u_face = myhalf * ( cell_data(i,j,k,Rho_comp) + cell_data(i-1,j,k,Rho_comp) );
364  Real v_loc = fourth * (v(i,j+1,k) + v(i,j,k) + v(i-1,j+1,k) + v(i-1,j,k));
365  Real w_loc = fourth * (w(i,j,k+1) + w(i,j,k) + w(i-1,j,k+1) + w(i-1,j,k));
366  Real sphi_loc = myhalf * (sphi_arr(i,j,0) + sphi_arr(i-1,j,0));
367  Real cphi_loc = myhalf * (cphi_arr(i,j,0) + cphi_arr(i-1,j,0));
368  xmom_src_arr(i, j, k) += coriolis_factor * rho_on_u_face * (v_loc * sphi_loc - w_loc * cphi_loc);
369  },
370  [=] AMREX_GPU_DEVICE (int i, int j, int k) {
371  Real rho_on_v_face = myhalf * ( cell_data(i,j,k,Rho_comp) + cell_data(i,j-1,k,Rho_comp) );
372  Real u_loc = fourth * (u(i+1,j,k) + u(i,j,k) + u(i+1,j-1,k) + u(i,j-1,k));
373  Real sphi_loc = myhalf * (sphi_arr(i,j,0) + sphi_arr(i,j-1,0));
374  ymom_src_arr(i, j, k) += -coriolis_factor * rho_on_v_face * u_loc * sphi_loc;
375  },
376  [=] AMREX_GPU_DEVICE (int i, int j, int k) {
377  Real rho_on_w_face = myhalf * ( cell_data(i,j,k,Rho_comp) + cell_data(i,j,k-1,Rho_comp) );
378  Real u_loc = fourth * (u(i+1,j,k) + u(i,j,k) + u(i+1,j,k-1) + u(i,j,k-1));
379  zmom_src_arr(i, j, k) += coriolis_factor * rho_on_w_face * u_loc * cphi_arr(i,j,0);
380  });
381  } else {
382  if (solverChoice.terrain_type == TerrainType::EB) {
383  Array4<const Real> u_volfrac = (ebfact.get_u_const_factory())->getVolFrac().const_array(mfi);
384  Array4<const Real> v_volfrac = (ebfact.get_v_const_factory())->getVolFrac().const_array(mfi);
385  Array4<const Real> w_volfrac = (ebfact.get_w_const_factory())->getVolFrac().const_array(mfi);
386  ParallelFor(tbx, [=] AMREX_GPU_DEVICE (int i, int j, int k) {
387  Real v_loc = 0.0;
388  Real w_loc = 0.0;
389  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);
390  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);
391  Real rho_on_u_face = myhalf * ( cell_data(i,j,k,Rho_comp) + cell_data(i-1,j,k,Rho_comp) );
392  if (v_vol > 0.0) {
393  v_loc = ( v_volfrac(i ,j+1,k) * v(i ,j+1,k) + v_volfrac(i ,j,k) * v(i ,j,k)
394  + 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;
395  }
396  if (w_vol > 0.0) {
397  w_loc = ( w_volfrac(i ,j,k+1) * w(i ,j,k+1) + w_volfrac(i ,j,k) * w(i ,j,k)
398  + 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;
399  }
400  xmom_src_arr(i, j, k) += coriolis_factor * rho_on_u_face * (v_loc * sinphi - w_loc * cosphi);
401  });
402  ParallelFor(tby, [=] AMREX_GPU_DEVICE (int i, int j, int k) {
403  Real u_loc = 0.0;
404  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);
405  Real rho_on_v_face = myhalf * ( cell_data(i,j,k,Rho_comp) + cell_data(i,j-1,k,Rho_comp) );
406  if (u_vol > 0.0) {
407  u_loc = ( u_volfrac(i+1,j ,k) * u(i+1,j ,k) + u_volfrac(i,j ,k) * u(i,j ,k)
408  + 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;
409  }
410  ymom_src_arr(i, j, k) += -coriolis_factor * rho_on_v_face * u_loc * sinphi;
411  });
412  ParallelFor(tbz, [=] AMREX_GPU_DEVICE (int i, int j, int k) {
413  Real u_loc = 0.0;
414  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);
415  Real rho_on_w_face = myhalf * ( cell_data(i,j,k,Rho_comp) + cell_data(i,j,k-1,Rho_comp) );
416  if (u_vol > 0.0) {
417  u_loc = ( u_volfrac(i+1,j,k ) * u(i+1,j,k ) + u_volfrac(i,j,k) * u(i,j,k )
418  + 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;
419  }
420  zmom_src_arr(i, j, k) += coriolis_factor * rho_on_w_face * u_loc * cosphi;
421  });
422  } else {
423  ParallelFor(tbx, tby, tbz,
424  [=] AMREX_GPU_DEVICE (int i, int j, int k)
425  {
426  Real v_loc = fourth * (v(i,j+1,k) + v(i,j,k) + v(i-1,j+1,k) + v(i-1,j,k));
427  Real w_loc = fourth * (w(i,j,k+1) + w(i,j,k) + w(i-1,j,k+1) + w(i-1,j,k));
428  Real rho_on_u_face = myhalf * ( cell_data(i,j,k,Rho_comp) + cell_data(i-1,j,k,Rho_comp) );
429  xmom_src_arr(i, j, k) += rho_on_u_face * ( coriolis_factor * (v_loc * sinphi - w_loc * cosphi) );
430  },
431  [=] AMREX_GPU_DEVICE (int i, int j, int k) {
432  Real rho_on_v_face = myhalf * ( cell_data(i,j,k,Rho_comp) + cell_data(i,j-1,k,Rho_comp) );
433  Real u_loc = fourth * (u(i+1,j,k) + u(i,j,k) + u(i+1,j-1,k) + u(i,j-1,k));
434  ymom_src_arr(i, j, k) += rho_on_v_face * ( -coriolis_factor * u_loc * sinphi );
435  },
436  [=] AMREX_GPU_DEVICE (int i, int j, int k) {
437  Real u_loc = fourth * (u(i+1,j,k) + u(i,j,k) + u(i+1,j,k-1) + u(i,j,k-1));
438  Real rho_on_w_face = myhalf * ( cell_data(i,j,k,Rho_comp) + cell_data(i,j,k-1,Rho_comp) );
439  zmom_src_arr(i, j, k) += rho_on_w_face * ( coriolis_factor * u_loc * cosphi );
440  });
441  }
442  } // var_coriolis
443  } // use_coriolis
444 
445  // *****************************************************************************
446  // 2. Add RAYLEIGH damping
447  // *****************************************************************************
448  Real dampcoef = solverChoice.dampingChoice.rayleigh_dampcoef;
449 
450  if ( (is_slow_step && !use_Rayleigh_fast_uv) || (!is_slow_step && use_Rayleigh_fast_uv)) {
451  if (rayleigh_damp_U) {
452  ParallelFor(tbx, [=] AMREX_GPU_DEVICE (int i, int j, int k)
453  {
454  Real rho_on_u_face = myhalf * ( cell_data(i,j,k,Rho_comp) + cell_data(i-1,j,k,Rho_comp) );
455  Real uu = rho_u(i,j,k) / rho_on_u_face;
456  Real sinesq = d_sinesq_at_lev[k];
457  xmom_src_arr(i, j, k) -= dampcoef*sinesq * (uu - ubar[k]) * rho_on_u_face;
458  });
459  }
460 
461  if (rayleigh_damp_V) {
462  ParallelFor(tby, [=] AMREX_GPU_DEVICE (int i, int j, int k)
463  {
464  Real rho_on_v_face = myhalf * ( cell_data(i,j,k,Rho_comp) + cell_data(i,j-1,k,Rho_comp) );
465  Real vv = rho_v(i,j,k) / rho_on_v_face;
466  Real sinesq = d_sinesq_at_lev[k];
467  ymom_src_arr(i, j, k) -= dampcoef*sinesq * (vv - vbar[k]) * rho_on_v_face;
468  });
469  }
470  } // fast or slow step
471 
472  if ( (is_slow_step && !use_Rayleigh_fast_w) || (!is_slow_step && use_Rayleigh_fast_w)) {
473  if (rayleigh_damp_W) {
474  ParallelFor(tbz, [=] AMREX_GPU_DEVICE (int i, int j, int k)
475  {
476  Real rho_on_w_face = myhalf * ( cell_data(i,j,k,Rho_comp) + cell_data(i,j,k-1,Rho_comp) );
477  Real ww = rho_w(i,j,k) / rho_on_w_face;
478  Real sinesq = d_sinesq_stag_at_lev[k];
479  zmom_src_arr(i, j, k) -= dampcoef*sinesq * (ww - wbar[k]) * rho_on_w_face;
480  });
481  }
482  } // fast or slow step
483 
484  // *****************************************************************************
485  // 3a. Add constant GEOSTROPHIC forcing
486  // *****************************************************************************
487  if (is_slow_step) {
488  ParallelFor(tbx, tby, tbz,
489  [=] AMREX_GPU_DEVICE (int i, int j, int k)
490  {
491  Real rho_on_u_face = myhalf * ( cell_data(i,j,k,Rho_comp) + cell_data(i-1,j,k,Rho_comp) );
492  xmom_src_arr(i, j, k) += rho_on_u_face * abl_geo_forcing[0];
493  },
494  [=] AMREX_GPU_DEVICE (int i, int j, int k)
495  {
496  Real rho_on_v_face = myhalf * ( cell_data(i,j,k,Rho_comp) + cell_data(i,j-1,k,Rho_comp) );
497  ymom_src_arr(i, j, k) += rho_on_v_face * abl_geo_forcing[1];
498  },
499  [=] AMREX_GPU_DEVICE (int i, int j, int k)
500  {
501  Real rho_on_w_face = myhalf * ( cell_data(i,j,k,Rho_comp) + cell_data(i,j,k-1,Rho_comp) );
502  zmom_src_arr(i, j, k) += rho_on_w_face * abl_geo_forcing[2];
503  });
504  }
505 
506  // *****************************************************************************
507  // 3b. Add height-dependent GEOSTROPHIC forcing
508  // *****************************************************************************
509  if (geo_wind_profile && is_slow_step) {
510  ParallelFor(tbx, tby,
511  [=] AMREX_GPU_DEVICE (int i, int j, int k)
512  {
513  Real rho_on_u_face = myhalf * ( cell_data(i,j,k,Rho_comp) + cell_data(i-1,j,k,Rho_comp) );
514  xmom_src_arr(i, j, k) -= coriolis_factor * rho_on_u_face * dptr_v_geos[k] * sinphi;
515  },
516  [=] AMREX_GPU_DEVICE (int i, int j, int k)
517  {
518  Real rho_on_v_face = myhalf * ( cell_data(i,j,k,Rho_comp) + cell_data(i,j-1,k,Rho_comp) );
519  ymom_src_arr(i, j, k) += coriolis_factor * rho_on_v_face * dptr_u_geos[k] * sinphi;
520  });
521  } // geo_wind_profile
522 
523  // *****************************************************************************
524  // 4. Add custom SUBSIDENCE terms
525  // *****************************************************************************
526  if (solverChoice.custom_w_subsidence && is_slow_step && solverChoice.do_mom_advection) {
527  if (solverChoice.custom_forcing_prim_vars) {
528  const int nr = Rho_comp;
529  ParallelFor(tbx, tby,
530  [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
531  {
532  Real dzInv = myhalf*dxInv[2];
533  if (z_nd_arr) {
534  Real z_xf_lo = fourth * ( z_nd_arr(i,j,k ) + z_nd_arr(i,j+1,k )
535  + z_nd_arr(i,j,k-1) + z_nd_arr(i,j+1,k-1) );
536  Real z_xf_hi = fourth * ( z_nd_arr(i,j,k+1) + z_nd_arr(i,j+1,k+1)
537  + z_nd_arr(i,j,k+2) + z_nd_arr(i,j+1,k+2) );
538  dzInv = one / (z_xf_hi - z_xf_lo);
539  }
540  Real rho_on_u_face = myhalf * ( cell_data(i,j,k,nr) + cell_data(i-1,j,k,nr) );
541  Real U_hi = dptr_u_plane(k+1) / dptr_r_plane(k+1);
542  Real U_lo = dptr_u_plane(k-1) / dptr_r_plane(k-1);
543  Real wbar_xf = myhalf * (dptr_wbar_sub[k] + dptr_wbar_sub[k+1]);
544  xmom_src_arr(i, j, k) -= rho_on_u_face * wbar_xf * (U_hi - U_lo) * dzInv;
545  },
546  [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
547  {
548  Real dzInv = myhalf*dxInv[2];
549  if (z_nd_arr) {
550  Real z_yf_lo = fourth * ( z_nd_arr(i,j,k ) + z_nd_arr(i+1,j,k )
551  + z_nd_arr(i,j,k-1) + z_nd_arr(i+1,j,k-1) );
552  Real z_yf_hi = fourth * ( z_nd_arr(i,j,k+1) + z_nd_arr(i+1,j,k+1)
553  + z_nd_arr(i,j,k+2) + z_nd_arr(i+1,j,k+2) );
554  dzInv = one / (z_yf_hi - z_yf_lo);
555  }
556  Real rho_on_v_face = myhalf * ( cell_data(i,j,k,nr) + cell_data(i,j-1,k,nr) );
557  Real V_hi = dptr_v_plane(k+1) / dptr_r_plane(k+1);
558  Real V_lo = dptr_v_plane(k-1) / dptr_r_plane(k-1);
559  Real wbar_yf = myhalf * (dptr_wbar_sub[k] + dptr_wbar_sub[k+1]);
560  ymom_src_arr(i, j, k) -= rho_on_v_face * wbar_yf * (V_hi - V_lo) * dzInv;
561  });
562  } else {
563  ParallelFor(tbx, tby,
564  [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
565  {
566  Real dzInv = myhalf*dxInv[2];
567  if (z_nd_arr) {
568  Real z_xf_lo = fourth * ( z_nd_arr(i,j,k ) + z_nd_arr(i,j+1,k )
569  + z_nd_arr(i,j,k-1) + z_nd_arr(i,j+1,k-1) );
570  Real z_xf_hi = fourth * ( z_nd_arr(i,j,k+1) + z_nd_arr(i,j+1,k+1)
571  + z_nd_arr(i,j,k+2) + z_nd_arr(i,j+1,k+2) );
572  dzInv = one / (z_xf_hi - z_xf_lo);
573  }
574  Real U_hi = dptr_u_plane(k+1) / dptr_r_plane(k+1);
575  Real U_lo = dptr_u_plane(k-1) / dptr_r_plane(k-1);
576  Real wbar_xf = myhalf * (dptr_wbar_sub[k] + dptr_wbar_sub[k+1]);
577  xmom_src_arr(i, j, k) -= wbar_xf * (U_hi - U_lo) * dzInv;
578  },
579  [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
580  {
581  Real dzInv = myhalf*dxInv[2];
582  if (z_nd_arr) {
583  Real z_yf_lo = fourth * ( z_nd_arr(i,j,k ) + z_nd_arr(i+1,j,k )
584  + z_nd_arr(i,j,k-1) + z_nd_arr(i+1,j,k-1) );
585  Real z_yf_hi = fourth * ( z_nd_arr(i,j,k+1) + z_nd_arr(i+1,j,k+1)
586  + z_nd_arr(i,j,k+2) + z_nd_arr(i+1,j,k+2) );
587  dzInv = one / (z_yf_hi - z_yf_lo);
588  }
589  Real V_hi = dptr_v_plane(k+1) / dptr_r_plane(k+1);
590  Real V_lo = dptr_v_plane(k-1) / dptr_r_plane(k-1);
591  Real wbar_yf = myhalf * (dptr_wbar_sub[k] + dptr_wbar_sub[k+1]);
592  ymom_src_arr(i, j, k) -= wbar_yf * (V_hi - V_lo) * dzInv;
593  });
594  }
595  }
596 
597  // *************************************************************************************
598  // 5. Add nudging towards value specified in input sounding
599  // *************************************************************************************
600  if (solverChoice.nudging_from_input_sounding && is_slow_step)
601  {
602  int itime_n = 0;
603  int itime_np1 = 0;
604  Real coeff_n = one;
605  Real coeff_np1 = zero;
606 
607  Real tau_inv = one / input_sounding_data.tau_nudging;
608 
609  int n_sounding_times = input_sounding_data.input_sounding_time.size();
610 
611  for (int nt = 1; nt < n_sounding_times; nt++) {
612  if (time > input_sounding_data.input_sounding_time[nt]) itime_n = nt;
613  }
614  if (itime_n == n_sounding_times-1) {
615  itime_np1 = itime_n;
616  } else {
617  itime_np1 = itime_n+1;
618  coeff_np1 = (time - input_sounding_data.input_sounding_time[itime_n]) /
619  (input_sounding_data.input_sounding_time[itime_np1] - input_sounding_data.input_sounding_time[itime_n]);
620  coeff_n = one - coeff_np1;
621  }
622 
623  int nr = Rho_comp;
624 
625  const Real* u_inp_sound_n = input_sounding_data.U_inp_sound_d[itime_n].dataPtr();
626  const Real* u_inp_sound_np1 = input_sounding_data.U_inp_sound_d[itime_np1].dataPtr();
627  const Real* v_inp_sound_n = input_sounding_data.V_inp_sound_d[itime_n].dataPtr();
628  const Real* v_inp_sound_np1 = input_sounding_data.V_inp_sound_d[itime_np1].dataPtr();
629  ParallelFor(tbx, tby,
630  [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
631  {
632  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));
633  nudge_u *= tau_inv;
634  xmom_src_arr(i, j, k) += cell_data(i, j, k, nr) * nudge_u;
635  },
636  [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
637  {
638  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));
639  nudge_v *= tau_inv;
640  ymom_src_arr(i, j, k) += cell_data(i, j, k, nr) * nudge_v;
641  });
642  }
643 
644  // *****************************************************************************
645  // 6. Add NUMERICAL DIFFUSION terms
646  // *****************************************************************************
647 #if 0
648  if (l_use_ndiff) {
649  const Array4<const Real>& mf_ux = mapfac[MapFac::ux]->const_array(mfi);
650  const Array4<const Real>& mf_uy = mapfac[MapFac::uy]->const_array(mfi);
651  const Array4<const Real>& mf_vx = mapfac[MapFac::vx]->const_array(mfi);
652  const Array4<const Real>& mf_vy = mapfac[MapFac::vy]->const_array(mfi);
653  NumericalDiffusion_Xmom(tbx, dt, solverChoice.num_diff_coeff,
654  u, cell_data, xmom_src_arr, mf_ux, mf_uy);
655  NumericalDiffusion_Ymom(tby, dt, solverChoice.num_diff_coeff,
656  v, cell_data, ymom_src_arr, mf_vx, mf_vy);
657  }
658 #endif
659 
660  // *****************************************************************************
661  // 7. Add SPONGING
662  // *****************************************************************************
663  if (is_slow_step) {
664  if (solverChoice.spongeChoice.sponge_type == SpongeType::Input_Sponge)
665  {
666  ApplySpongeZoneBCsForMom_ReadFromFile(solverChoice.spongeChoice, geom, tbx, tby, cell_data,
667  z_cc_arr, xmom_src_arr, ymom_src_arr,
668  rho_u, rho_v, d_sponge_ptrs_at_lev);
669  }
670  else if (solverChoice.spongeChoice.sponge_type == SpongeType::Standard)
671  {
672  ApplySpongeZoneBCsForMom(solverChoice.spongeChoice, geom, tbx, tby, tbz,
673  xmom_src_arr, ymom_src_arr, zmom_src_arr, rho_u, rho_v, rho_w,
674  r0, z_nd_arr, z_cc_arr);
675  }
676 
677  if(solverChoice.init_type == InitType::HindCast and solverChoice.hindcast_lateral_forcing){
678 
679  const Array4<const Real>& rho_u_forecast_state = (*forecast_state_at_lev)[IntVars::xmom].array(mfi);
680  const Array4<const Real>& rho_v_forecast_state = (*forecast_state_at_lev)[IntVars::ymom].array(mfi);
681  const Array4<const Real>& rho_w_forecast_state = (*forecast_state_at_lev)[IntVars::zmom].array(mfi);
682  const Array4<const Real>& cons_forecast_state = (*forecast_state_at_lev)[IntVars::cons].array(mfi);
683  ApplyBndryForcing_Forecast(solverChoice, geom, tbx, tby, tbz, z_nd_arr,
684  xmom_src_arr, ymom_src_arr, zmom_src_arr,
685  rho_u, rho_v, rho_w,
686  rho_u_forecast_state, rho_v_forecast_state, rho_w_forecast_state,
687  cons_forecast_state);
688  }
689  if(solverChoice.init_type == InitType::HindCast and solverChoice.hindcast_surface_bcs) {
690  const Array4<const Real>& surface_state_arr = (*surface_state_at_lev).array(mfi);
692  xmom_src_arr, ymom_src_arr,
693  rho_u, rho_v,
694  cell_data, z_nd_arr,
695  surface_state_arr);
696  }
697  }
698 
699  // *****************************************************************************
700  // 8. Add CANOPY source terms
701  // *****************************************************************************
702  if (solverChoice.do_forest_drag &&
703  ((is_slow_step && !use_canopy_fast) || (!is_slow_step && use_canopy_fast))) {
704  ParallelFor(tbx, tby, tbz,
705  [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
706  {
707  const Real ux = u(i, j, k);
708  const Real uy = fourth * ( v(i, j , k ) + v(i-1, j , k )
709  + v(i, j+1, k ) + v(i-1, j+1, k ) );
710  const Real uz = fourth * ( w(i, j , k ) + w(i-1, j , k )
711  + w(i, j , k+1) + w(i-1, j , k+1) );
712  const Real windspeed = std::sqrt(ux * ux + uy * uy + uz * uz);
713  const Real f_drag = myhalf * (f_drag_arr(i, j, k) + f_drag_arr(i-1, j, k));
714  xmom_src_arr(i, j, k) -= f_drag * ux * windspeed;
715  },
716  [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
717  {
718  const Real ux = fourth * ( u(i , j , k ) + u(i , j-1, k )
719  + u(i+1, j , k ) + u(i+1, j-1, k ) );
720  const Real uy = v(i, j, k);
721  const Real uz = fourth * ( w(i , j , k ) + w(i , j-1, k )
722  + w(i , j , k+1) + w(i , j-1, k+1) );
723  const amrex::Real windspeed = std::sqrt(ux * ux + uy * uy + uz * uz);
724  const Real f_drag = myhalf * (f_drag_arr(i, j, k) + f_drag_arr(i, j-1, k));
725  ymom_src_arr(i, j, k) -= f_drag * uy * windspeed;
726  },
727  [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
728  {
729  const amrex::Real ux = fourth * ( u(i , j , k ) + u(i+1, j , k )
730  + u(i , j , k-1) + u(i+1, j , k-1) );
731  const amrex::Real uy = fourth * ( v(i , j , k ) + v(i , j+1, k )
732  + v(i , j , k-1) + v(i , j+1, k-1) );
733  const amrex::Real uz = w(i, j, k);
734  const amrex::Real windspeed = std::sqrt(ux * ux + uy * uy + uz * uz);
735  const Real f_drag = myhalf * (f_drag_arr(i, j, k) + f_drag_arr(i, j, k-1));
736  zmom_src_arr(i, j, k) -= f_drag * uz * windspeed;
737  });
738  }
739  // *****************************************************************************
740  // 9a. Add immersed source terms for terrain
741  // *****************************************************************************
742  if (solverChoice.terrain_type == TerrainType::ImmersedForcing &&
743  ((is_slow_step && !use_ImmersedForcing_fast) || (!is_slow_step && use_ImmersedForcing_fast))) {
744  // geometric properties
745  const Real* dx_arr = geom.CellSize();
746  const Real dx_x = dx_arr[0];
747  const Real dx_y = dx_arr[1];
748  const Real dx_z = dx_arr[2];
749 
750  const Real alpha_m = solverChoice.if_Cd_momentum;
751  const Real drag_coefficient = alpha_m / std::pow(dx_x*dx_y*dx_z, one/three);
753  const Real U_s = one; // unit velocity scale
754 
755  // MOST parameters
756  similarity_funs sfuns;
757  const Real ggg = CONST_GRAV;
758  const Real kappa = KAPPA;
759  const Real z0 = solverChoice.if_z0;
760  const Real tflux_in = solverChoice.if_surf_temp_flux;
761  const Real Olen_in = solverChoice.if_Olen_in;
762  const bool l_use_most = solverChoice.if_use_most;
763 
764  ParallelFor(tbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
765  {
766  const Real ux = u(i, j, k);
767  const Real uy = fourth * ( v(i, j , k ) + v(i-1, j , k )
768  + v(i, j+1, k ) + v(i-1, j+1, k ) );
769  const Real uz = fourth * ( w(i, j , k ) + w(i-1, j , k )
770  + w(i, j , k+1) + w(i-1, j , k+1) );
771  const Real windspeed = std::sqrt(ux * ux + uy * uy + uz * uz);
772  const Real t_blank = myhalf * (t_blank_arr(i, j, k) + t_blank_arr(i-1, j, k));
773  const Real t_blank_above = myhalf * (t_blank_arr(i, j, k+1) + t_blank_arr(i-1, j, k+1));
774  const Real CdM = std::min(drag_coefficient / (windspeed + tiny), Real(1000.0));
775  const Real rho_xface = myhalf * ( cell_data(i,j,k,Rho_comp) + cell_data(i-1,j,k,Rho_comp) );
776 
777  if ((t_blank > 0 && (t_blank_above == zero)) && l_use_most) { // force to MOST value
778  // calculate tangential velocity one cell above
779  const Real ux2r = u(i, j, k+1) ;
780  const Real uy2r = fourth * ( v(i, j , k+1) + v(i-1, j , k+1)
781  + v(i, j+1, k+1) + v(i-1, j+1, k+1) ) ;
782  const Real h_windspeed2r = std::sqrt(ux2r * ux2r + uy2r * uy2r);
783 
784  // MOST
785  const Real theta_xface = (myhalf * (cell_data(i,j,k ,RhoTheta_comp) + cell_data(i-1,j,k, RhoTheta_comp))) / rho_xface;
786  const Real rho_xface_below = myhalf * ( cell_data(i,j,k-1,Rho_comp) + cell_data(i-1,j,k-1,Rho_comp) );
787  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;
788  const Real theta_surf = theta_xface_below;
789 
790  Real psi_m = zero;
791  Real psi_h = zero;
792  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
793  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);
794  Real Olen = (Olen_in != Real(1e-8)) ? Olen_in : -ustar * ustar * ustar * theta_xface / (kappa * ggg * tflux + tiny);
795  Real zeta = Real(1.5) * dx_z / Olen;
796 
797  // similarity functions
798  psi_m = sfuns.calc_psi_m(zeta);
799  psi_h = sfuns.calc_psi_h(zeta);
800  ustar = h_windspeed2r * kappa / (std::log(Real(1.5) * dx_z / z0) - psi_m);
801 
802  // prevent some unphysical math
803  if (!(ustar > zero && !std::isnan(ustar))) { ustar = zero; }
804  if (!(ustar < two && !std::isnan(ustar))) { ustar = two; }
805  if (psi_m > std::log(myhalf * dx_z / z0)) { psi_m = std::log(myhalf * dx_z / z0); }
806 
807  // determine target velocity
808  const Real uTarget = ustar / kappa * (std::log(myhalf * dx_z / z0) - psi_m);
809  Real uxTarget = uTarget * ux2r / (tiny + h_windspeed2r);
810  const Real bc_forcing_x = -(uxTarget - ux); // BC forcing pushes nonrelative velocity toward target velocity
811  xmom_src_arr(i, j, k) -= (1-t_blank) * rho_xface * CdM * U_s * bc_forcing_x; // if Vf low, force more strongly to MOST. If high, less forcing.
812  } else {
813  xmom_src_arr(i, j, k) -= t_blank * rho_xface * CdM * ux * windspeed;
814  }
815  });
816  ParallelFor(tby, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
817  {
818  const Real ux = fourth * ( u(i , j , k ) + u(i , j-1, k )
819  + u(i+1, j , k ) + u(i+1, j-1, k ) );
820  const Real uy = v(i, j, k);
821  const Real uz = fourth * ( w(i , j , k ) + w(i , j-1, k )
822  + w(i , j , k+1) + w(i , j-1, k+1) );
823  const Real windspeed = std::sqrt(ux * ux + uy * uy + uz * uz);
824  const Real t_blank = myhalf * (t_blank_arr(i, j, k) + t_blank_arr(i, j-1, k));
825  const Real t_blank_above = myhalf * (t_blank_arr(i, j, k+1) + t_blank_arr(i, j-1, k+1));
826  const Real CdM = std::min(drag_coefficient / (windspeed + tiny), Real(1000.0));
827  const Real rho_yface = myhalf * ( cell_data(i,j,k,Rho_comp) + cell_data(i,j-1,k,Rho_comp) );
828 
829  if ((t_blank > 0 && (t_blank_above == zero)) && l_use_most) { // force to MOST value
830  // calculate tangential velocity one cell above
831  const Real ux2r = fourth * ( u(i , j , k+1) + u(i , j-1, k+1)
832  + u(i+1, j , k+1) + u(i+1, j-1, k+1) );
833  const Real uy2r = v(i, j, k+1) ;
834  const Real h_windspeed2r = std::sqrt(ux2r * ux2r + uy2r * uy2r);
835 
836  // MOST
837  const Real theta_yface = (myhalf * (cell_data(i,j,k ,RhoTheta_comp) + cell_data(i,j-1,k, RhoTheta_comp))) / rho_yface;
838  const Real rho_yface_below = myhalf * ( cell_data(i,j,k-1,Rho_comp) + cell_data(i,j-1,k-1,Rho_comp) );
839  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;
840  const Real theta_surf = theta_yface_below;
841 
842  Real psi_m = zero;
843  Real psi_h = zero;
844  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
845  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);
846  Real Olen = (Olen_in != Real(1e-8)) ? Olen_in : -ustar * ustar * ustar * theta_yface / (kappa * ggg * tflux + tiny);
847  Real zeta = Real(1.5) * dx_z / Olen;
848 
849  // similarity functions
850  psi_m = sfuns.calc_psi_m(zeta);
851  psi_h = sfuns.calc_psi_h(zeta);
852  ustar = h_windspeed2r * kappa / (std::log(Real(1.5) * dx_z / z0) - psi_m);
853 
854  // prevent some unphysical math
855  if (!(ustar > zero && !std::isnan(ustar))) { ustar = zero; }
856  if (!(ustar < two && !std::isnan(ustar))) { ustar = two; }
857  if (psi_m > std::log(myhalf * dx_z / z0)) { psi_m = std::log(myhalf * dx_z / z0); }
858 
859  // determine target velocity
860  const Real uTarget = ustar / kappa * (std::log(myhalf * dx_z / z0) - psi_m);
861  Real uyTarget = uTarget * uy2r / (tiny + h_windspeed2r);
862  const Real bc_forcing_y = -(uyTarget - uy); // BC forcing pushes nonrelative velocity toward target velocity
863  ymom_src_arr(i, j, k) -= (1 - t_blank) * rho_yface * CdM * U_s * bc_forcing_y; // if Vf low, force more strongly to MOST. If high, less forcing.
864  } else {
865  ymom_src_arr(i, j, k) -= t_blank * rho_yface * CdM * uy * windspeed;
866  }
867  });
868  ParallelFor(tbz, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
869  {
870  const Real ux = fourth * ( u(i , j , k ) + u(i+1, j , k )
871  + u(i , j , k-1) + u(i+1, j , k-1) );
872  const Real uy = fourth * ( v(i , j , k ) + v(i , j+1, k )
873  + v(i , j , k-1) + v(i , j+1, k-1) );
874  const Real uz = w(i, j, k);
875  const Real windspeed = std::sqrt(ux * ux + uy * uy + uz * uz);
876  const Real t_blank = myhalf * (t_blank_arr(i, j, k) + t_blank_arr(i, j, k-1));
877  const Real CdM = std::min(drag_coefficient / (windspeed + tiny), Real(1000.0));
878  const Real rho_zface = myhalf * ( cell_data(i,j,k,Rho_comp) + cell_data(i,j,k-1,Rho_comp) );
879  zmom_src_arr(i, j, k) -= t_blank * rho_zface * CdM * uz * windspeed;
880  });
881  }
882 
883  // *****************************************************************************
884  // 9b. Add immersed source terms for buildings
885  // *****************************************************************************
886  if ((solverChoice.buildings_type == BuildingsType::ImmersedForcing ) &&
887  ((is_slow_step && !use_ImmersedForcing_fast) || (!is_slow_step && use_ImmersedForcing_fast)))
888  {
889  // geometric properties
890  const Real* dx_arr = geom.CellSize();
891  const Real dx_x = dx_arr[0];
892  const Real dx_y = dx_arr[1];
893 
894  const Real alpha_m = solverChoice.if_Cd_momentum;
896  const Real min_t_blank = Real(0.005); // threshold for where immersed forcing acts
897 
898  ParallelFor(tbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
899  {
900  const Real ux = u(i, j, k);
901  const Real uy = fourth * ( v(i, j , k ) + v(i-1, j , k )
902  + v(i, j+1, k ) + v(i-1, j+1, k ) );
903  const Real uz = fourth * ( w(i, j , k ) + w(i-1, j , k )
904  + w(i, j , k+1) + w(i-1, j , k+1) );
905  const Real windspeed = std::sqrt(ux * ux + uy * uy + uz * uz);
906 
907  Real t_blank = myhalf * (t_blank_arr(i, j, k) + t_blank_arr(i-1, j, k));
908  if (t_blank < min_t_blank) { t_blank = zero; }
909  const Real dx_z = (z_cc_arr) ? (z_cc_arr(i,j,k) - z_cc_arr(i,j,k-1)) : dx_arr[2];
910  const Real drag_coefficient = alpha_m / std::pow(dx_x*dx_y*dx_z, one/three);
911  const Real CdM = std::min(drag_coefficient / (windspeed + tiny), Real(1000.0));
912  const Real rho_xface = myhalf * ( cell_data(i,j,k,Rho_comp) + cell_data(i-1,j,k,Rho_comp) );
913  xmom_src_arr(i, j, k) -= t_blank * rho_xface * CdM * ux * windspeed;
914  });
915  ParallelFor(tby, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
916  {
917  const Real ux = fourth * ( u(i , j , k ) + u(i , j-1, k )
918  + u(i+1, j , k ) + u(i+1, j-1, k ) );
919  const Real uy = v(i, j, k);
920  const Real uz = fourth * ( w(i , j , k ) + w(i , j-1, k )
921  + w(i , j , k+1) + w(i , j-1, k+1) );
922  const Real windspeed = std::sqrt(ux * ux + uy * uy + uz * uz);
923 
924  Real t_blank = myhalf * (t_blank_arr(i, j, k) + t_blank_arr(i, j-1, k));
925  if (t_blank < min_t_blank) { t_blank = zero; }
926  const Real dx_z = (z_cc_arr) ? (z_cc_arr(i,j,k) - z_cc_arr(i,j,k-1)) : dx_arr[2];
927  const Real drag_coefficient = alpha_m / std::pow(dx_x*dx_y*dx_z, one/three);
928  const Real CdM = std::min(drag_coefficient / (windspeed + tiny), Real(1000.0));
929  const Real rho_yface = myhalf * ( cell_data(i,j,k,Rho_comp) + cell_data(i,j-1,k,Rho_comp) );
930  ymom_src_arr(i, j, k) -= t_blank * rho_yface * CdM * uy * windspeed;
931  });
932  ParallelFor(tbz, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
933  {
934  const Real ux = fourth * ( u(i , j , k ) + u(i+1, j , k )
935  + u(i , j , k-1) + u(i+1, j , k-1) );
936  const Real uy = fourth * ( v(i , j , k ) + v(i , j+1, k )
937  + v(i , j , k-1) + v(i , j+1, k-1) );
938  const Real uz = w(i, j, k);
939  const Real windspeed = std::sqrt(ux * ux + uy * uy + uz * uz);
940 
941  Real t_blank = myhalf * (t_blank_arr(i, j, k) + t_blank_arr(i, j, k-1));
942  if (t_blank < min_t_blank) { t_blank = zero; }
943  const Real dx_z = (z_nd_arr) ? (z_nd_arr(i,j,k) - z_nd_arr(i,j,k-1)) : dx_arr[2]; // ASW double check
944  const Real drag_coefficient = alpha_m / std::pow(dx_x*dx_y*dx_z, one/three);
945  const Real CdM = std::min(drag_coefficient / (windspeed + tiny), Real(1000.0));
946  const Real rho_zface = myhalf * ( cell_data(i,j,k,Rho_comp) + cell_data(i,j,k-1,Rho_comp) );
947  zmom_src_arr(i, j, k) -= t_blank * rho_zface * CdM * uz * windspeed;
948  });
949  }
950 
951  // *****************************************************************************
952  // 10. Enforce constant mass flux
953  // *****************************************************************************
954  if (is_slow_step && (enforce_massflux_x || enforce_massflux_y)) {
955  Real tau_inv = one / solverChoice.const_massflux_tau;
956 
957  ParallelFor(tbx, tby,
958  [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
959  xmom_src_arr(i, j, k) += tau_inv * (rhoUA_target - rhoUA);
960  },
961  [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
962  ymom_src_arr(i, j, k) += tau_inv * (rhoVA_target - rhoVA);
963  });
964  }
965 
966  } // mfi
967 }
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
void ApplySurfaceTreatment_BulkCoeff_Mom(const Box &tbx, const Box &tby, 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 Array4< const Real > &cons_state, const Array4< const Real > &z_phys_nd, const Array4< const Real > &surface_state_arr)
Definition: ERF_ApplySurfaceTreatment_BulkCoeff.cpp:8
constexpr amrex::Real three
Definition: ERF_Constants.H:11
constexpr amrex::Real KAPPA
Definition: ERF_Constants.H:63
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 zero
Definition: ERF_Constants.H:8
constexpr amrex::Real myhalf
Definition: ERF_Constants.H:13
constexpr amrex::Real PI
Definition: ERF_Constants.H:42
constexpr amrex::Real CONST_GRAV
Definition: ERF_Constants.H:64
@ ubar
Definition: ERF_DataStruct.H:98
@ wbar
Definition: ERF_DataStruct.H:98
@ vbar
Definition: ERF_DataStruct.H:98
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
Real z0
Definition: ERF_InitCustomPertVels_ScalarAdvDiff.H:8
AMREX_ALWAYS_ASSERT(bx.length()[2]==khi+1)
ParallelFor(grown_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:19
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
Definition: ERF_PlaneAverage.H:14
eb_aux_ const * get_w_const_factory() const noexcept
Definition: ERF_EB.H:52
eb_aux_ const * get_v_const_factory() const noexcept
Definition: ERF_EB.H:51
eb_aux_ const * get_u_const_factory() const noexcept
Definition: ERF_EB.H:50
@ r0_comp
Definition: ERF_IndexDefines.H:73
@ ymom
Definition: ERF_IndexDefines.H:195
@ cons
Definition: ERF_IndexDefines.H:193
@ zmom
Definition: ERF_IndexDefines.H:196
@ xmom
Definition: ERF_IndexDefines.H:194
@ nr
Definition: ERF_Morrison.H:46
@ xvel
Definition: ERF_IndexDefines.H:176
@ cons
Definition: ERF_IndexDefines.H:175
@ yvel
Definition: ERF_IndexDefines.H:177
@ ww
Definition: ERF_AdvanceWSM6.cpp:105
real(c_double), parameter epsilon
Definition: ERF_module_model_constants.F90:12
real(kind=kind_phys), parameter, private r0
Definition: ERF_module_mp_wsm6.F90:21
bool rayleigh_damp_V
Definition: ERF_DampingStruct.H:85
amrex::Real rayleigh_dampcoef
Definition: ERF_DampingStruct.H:88
bool rayleigh_damp_W
Definition: ERF_DampingStruct.H:86
RayleighDampingType rayleigh_damping_type
Definition: ERF_DampingStruct.H:101
bool rayleigh_damp_U
Definition: ERF_DampingStruct.H:84
amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real > > V_inp_sound_d
Definition: ERF_InputSoundingData.H:418
amrex::Vector< amrex::Real > input_sounding_time
Definition: ERF_InputSoundingData.H:405
amrex::Real tau_nudging
Definition: ERF_InputSoundingData.H:402
amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real > > U_inp_sound_d
Definition: ERF_InputSoundingData.H:418
bool do_mom_advection
Definition: ERF_DataStruct.H:1346
amrex::Real coriolis_factor
Definition: ERF_DataStruct.H:1337
static MeshType mesh_type
Definition: ERF_DataStruct.H:1239
amrex::Real if_surf_temp_flux
Definition: ERF_DataStruct.H:1314
bool if_use_most
Definition: ERF_DataStruct.H:1318
DampingChoice dampingChoice
Definition: ERF_DataStruct.H:1249
amrex::Real const_massflux_v
Definition: ERF_DataStruct.H:1463
amrex::Real if_z0
Definition: ERF_DataStruct.H:1313
amrex::Real cosphi
Definition: ERF_DataStruct.H:1338
amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > abl_geo_forcing
Definition: ERF_DataStruct.H:1432
bool hindcast_lateral_forcing
Definition: ERF_DataStruct.H:1472
int massflux_klo
Definition: ERF_DataStruct.H:1467
bool custom_w_subsidence
Definition: ERF_DataStruct.H:1344
bool nudging_from_input_sounding
Definition: ERF_DataStruct.H:1354
bool immersed_forcing_substep
Definition: ERF_DataStruct.H:1306
amrex::Real sinphi
Definition: ERF_DataStruct.H:1339
bool have_geo_wind_profile
Definition: ERF_DataStruct.H:1434
amrex::Real const_massflux_u
Definition: ERF_DataStruct.H:1462
amrex::Real if_Olen_in
Definition: ERF_DataStruct.H:1317
bool use_coriolis
Definition: ERF_DataStruct.H:1300
amrex::Real num_diff_coeff
Definition: ERF_DataStruct.H:1421
bool variable_coriolis
Definition: ERF_DataStruct.H:1436
amrex::Real if_Cd_momentum
Definition: ERF_DataStruct.H:1310
bool custom_forcing_prim_vars
Definition: ERF_DataStruct.H:1348
static BuildingsType buildings_type
Definition: ERF_DataStruct.H:1233
static TerrainType terrain_type
Definition: ERF_DataStruct.H:1230
SpongeChoice spongeChoice
Definition: ERF_DataStruct.H:1250
static InitType init_type
Definition: ERF_DataStruct.H:1224
bool hindcast_surface_bcs
Definition: ERF_DataStruct.H:1473
bool do_forest_drag
Definition: ERF_DataStruct.H:1459
amrex::Real const_massflux_tau
Definition: ERF_DataStruct.H:1464
int massflux_khi
Definition: ERF_DataStruct.H:1468
bool forest_substep
Definition: ERF_DataStruct.H:1307
int ave_plane
Definition: ERF_DataStruct.H:1438
static SpongeType sponge_type
Definition: ERF_SpongeStruct.H:90
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:90
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real calc_psi_h(amrex::Real zeta) const
Definition: ERF_MOSTStress.H:104
Here is the call graph for this function: