ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
ERF_TerrainMetrics.cpp File Reference
#include <ERF_TerrainMetrics.H>
#include <ERF_Utils.H>
#include <AMReX_ParmParse.H>
#include <ERF_Constants.H>
#include <ERF_Interpolation_1D.H>
#include <cmath>
Include dependency graph for ERF_TerrainMetrics.cpp:

Functions

void init_default_zphys (int, const Geometry &geom, MultiFab &z_phys_nd, MultiFab &z_phys_cc)
 
void make_terrain_fitted_coords (int lev, const Geometry &geom, MultiFab &z_phys_nd, Vector< Real > const &z_levels_h, GpuArray< ERF_BC, AMREX_SPACEDIM *2 > &phys_bc_type)
 
void init_which_terrain_grid (int lev, Geometry const &geom, MultiFab &z_phys_nd, Vector< Real > const &z_levels_h)
 
void make_J (const Geometry &geom, MultiFab &z_phys_nd, MultiFab &detJ_cc)
 
void make_areas (const Geometry &geom, MultiFab &z_phys_nd, MultiFab &ax, MultiFab &ay, MultiFab &az)
 
void make_zcc (const Geometry &geom, MultiFab &z_phys_nd, MultiFab &z_phys_cc)
 
Real get_dzmin_terrain (MultiFab &z_phys_nd)
 

Function Documentation

◆ get_dzmin_terrain()

Real get_dzmin_terrain ( MultiFab &  z_phys_nd)

Computation min dz at cell-center

655 {
656  auto const& ma_z_nd_arr = z_phys_nd.const_arrays();
657  GpuTuple<Real> min = ParReduce(TypeList<ReduceOpMin>{},
658  TypeList<Real>{},
659  z_phys_nd, IntVect(0),
660  [=] AMREX_GPU_DEVICE (int box_no, int i, int j, int k) noexcept
661  -> GpuTuple<Real>
662  {
663  amrex::Real dz = Compute_Z_AtWFace(i,j,k+1,ma_z_nd_arr[box_no]) -
664  Compute_Z_AtWFace(i,j,k ,ma_z_nd_arr[box_no]);
665  return { dz };
666  });
667  Real r = (get<0>(min) + std::numeric_limits<amrex::Real>::epsilon());
668  ParallelDescriptor::ReduceRealMin(r);
669  return r;
670 }
amrex::Real Real
Definition: ERF_ShocInterface.H:19
AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real Compute_Z_AtWFace(const int &i, const int &j, const int &k, const amrex::Array4< const amrex::Real > &z_nd)
Definition: ERF_TerrainMetrics.H:374
real(c_double), parameter epsilon
Definition: ERF_module_model_constants.F90:12

Referenced by ERF::init_zphys(), ERF::ReadCheckpointFile(), and ERF::remake_zphys().

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

◆ init_default_zphys()

void init_default_zphys ( int  ,
const Geometry &  geom,
MultiFab &  z_phys_nd,
MultiFab &  z_phys_cc 
)

Define a default z_phys so we have it even if a completely regular grid This will be over-written if we use z_levels, or grid stretching, or terrain-fitted grids

16 {
17  const auto& dx = geom.CellSize();
18  Real dz = dx[2];
19 
20  for (MFIter mfi(z_phys_nd,true); mfi.isValid(); ++mfi)
21  {
22  const Box& bx = mfi.growntilebox();
23  const Array4< Real> z_nd_arr = z_phys_nd.array(mfi);
24  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k)
25  {
26  z_nd_arr(i,j,k) = k * dz;
27  });
28  }
29 
30  for (MFIter mfi(z_phys_cc,true); mfi.isValid(); ++mfi)
31  {
32  const Box& bx = mfi.growntilebox();
33  const Array4< Real> z_cc_arr = z_phys_cc.array(mfi);
34  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k)
35  {
36  z_cc_arr(i,j,k) = (k + 0.5) * dz;
37  });
38  }
39 }
const auto dx
Definition: ERF_InitCustomPertVels_ParticleTests.H:15
ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept { const auto prob_lo=geomdata.ProbLo();const auto dx=geomdata.CellSize();const Real x=(prob_lo[0]+(i+0.5) *dx[0])/mf_m(i, j, 0);const Real z=z_cc(i, j, k);Real L=std::sqrt(std::pow((x - x_c)/x_r, 2)+std::pow((z - z_c)/z_r, 2));if(L<=1.0) { Real dT=T_pert *(std::cos(PI *L)+1.0)/2.0;Real Tbar_hse=p_hse(i, j, k)/(R_d *r_hse(i, j, k));Real theta_perturbed=(Tbar_hse+dT) *std::pow(p_0/p_hse(i, j, k), rdOcp);Real theta_0=(Tbar_hse) *std::pow(p_0/p_hse(i, j, k), rdOcp);if(const_rho) { state_pert(i, j, k, RhoTheta_comp)=r_hse(i, j, k) *(theta_perturbed - theta_0);} else { state_pert(i, j, k, Rho_comp)=getRhoThetagivenP(p_hse(i, j, k))/theta_perturbed - r_hse(i, j, k);} } })

Referenced by ERF::init_stuff().

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

◆ init_which_terrain_grid()

void init_which_terrain_grid ( int  lev,
Geometry const &  geom,
MultiFab &  z_phys_nd,
Vector< Real > const &  z_levels_h 
)
163 {
164  // User-selected method from inputs file (BTF default)
165  ParmParse pp("erf");
166  int terrain_smoothing = 0;
167  pp.query("terrain_smoothing", terrain_smoothing);
168 
169  if (lev > 0 && terrain_smoothing != 0) {
170  Abort("Must use terrain_smoothing = 0 when doing multilevel");
171  }
172 
173  // Number of ghost cells
174  int ngrow = z_phys_nd.nGrow();
175  IntVect ngrowVect = z_phys_nd.nGrowVect();
176 
177  const Box& domain = geom.Domain();
178  int domlo_x = domain.smallEnd(0); int domhi_x = domain.bigEnd(0) + 1;
179  int domlo_y = domain.smallEnd(1); int domhi_y = domain.bigEnd(1) + 1;
180  int domlo_z = domain.smallEnd(2); int domhi_z = domain.bigEnd(2) + 1;
181 
182  int imin = domlo_x; // if (geom.isPeriodic(0)) imin -= z_phys_nd.nGrowVect()[0];
183  int jmin = domlo_y; // if (geom.isPeriodic(1)) jmin -= z_phys_nd.nGrowVect()[1];
184 
185  int imax = domhi_x; // if (geom.isPeriodic(0)) imax += z_phys_nd.nGrowVect()[0];
186  int jmax = domhi_y; // if (geom.isPeriodic(1)) jmax += z_phys_nd.nGrowVect()[1];
187 
188  int nz = z_levels_h.size();
189  Real z_top = z_levels_h[nz-1];
190 
191  Gpu::DeviceVector<Real> z_levels_d;
192  z_levels_d.resize(nz);
193  Gpu::copy(Gpu::hostToDevice, z_levels_h.begin(), z_levels_h.end(), z_levels_d.begin());
194 
195  switch(terrain_smoothing) {
196  case 0: // BTF Method
197  {
198  for ( MFIter mfi(z_phys_nd, TilingIfNotGPU()); mfi.isValid(); ++mfi )
199  {
200  // Note that this box is nodal because it is based on z_phys_nd
201  const Box& bx = mfi.validbox();
202 
203  int k0 = bx.smallEnd()[2];
204 
205  // Grown box with corrected ghost cells at top
206  Box gbx = mfi.growntilebox(ngrowVect);
207 
208  if (bx.smallEnd(2) == domlo_z) {
209  gbx.setSmall(2,domlo_z);
210  } else {
211  gbx.growLo(2,-1);
212  }
213 
214  // Note that we don't overwrite the values at the high end of the box
215  // regardless of whether the box reaches the top of the domain or not.
216  // In the case of lev > 0, this ensures that the fine nodes at the top
217  // of a fine box are those that are interpolated from the coarse grid
218  if (bx.bigEnd(2) == domhi_z) {
219  gbx.setBig(2,domhi_z);
220  } else {
221  gbx.growHi(2,-1);
222  if (gbx.bigEnd(2) > domhi_z) {
223  gbx.setBig(2,domhi_z);
224  }
225  }
226 
227  Array4<Real> const& z_arr = z_phys_nd.array(mfi);
228  auto const& z_lev = z_levels_d.data();
229 
230  //
231  // Vertical grid stretching using BTF model from p2163 of Klemp2011
232  // z_levels are only defined from k = dom_lo to dom_hi (nodal)
233  //
234  ParallelFor(gbx, [=] AMREX_GPU_DEVICE (int i, int j, int k)
235  {
236  int ii = amrex::max(amrex::min(i,imax),imin);
237  int jj = amrex::max(amrex::min(j,jmax),jmin);
238 
239  //
240  // Start with flat z_lev set either with uniform cell size or specified z_levels
241  // If k0 = 0 then z_arr at k0 has already been filled from the terrain data
242  // If k0 > 0 then z_arr at k0 has already been filled from interpolation
243  //
244  Real z = z_lev[k];
245  Real z_sfc = z_arr(ii,jj,k0);
246  Real z_lev_sfc = z_lev[k0];
247 
248  z_arr(i,j,k) = ( (z_sfc - z_lev_sfc) * z_top +
249  (z_top - z_sfc ) * z ) / (z_top - z_lev_sfc);
250  });
251  } // mfi
252 
253  z_phys_nd.FillBoundary(geom.periodicity());
254 
255  for ( MFIter mfi(z_phys_nd, TilingIfNotGPU()); mfi.isValid(); ++mfi )
256  {
257  // Note that this box is nodal because it is based on z_phys_nd
258  const Box& bx = mfi.validbox();
259  Box gbx = mfi.growntilebox(ngrowVect);
260 
261  int k0 = bx.smallEnd()[2];
262 
263  if (k0 == 0)
264  {
265  Array4<Real> const& z_arr = z_phys_nd.array(mfi);
266 
267  // Fill lateral boundaries below the bottom surface
268  ParallelFor(makeSlab(gbx,2,0), [=] AMREX_GPU_DEVICE (int i, int j, int)
269  {
270  z_arr(i,j,-1) = 2.0*z_arr(i,j,0) - z_arr(i,j,1);
271  });
272  }
273  } // mfi
274  break;
275  } // case 0
276 
277  case 1: // STF Method
278  {
279  // Get MultiFab spanning domain with 1 level of ghost cells
280  MultiFab h_mf( z_phys_nd.boxArray(), z_phys_nd.DistributionMap(), 1, ngrow+1);
281  MultiFab h_mf_old(z_phys_nd.boxArray(), z_phys_nd.DistributionMap(), 1, ngrow+1);
282 
283  // Save max height for smoothing
284  Real h_m;
285 
286  // Create 2D MF without allocation
287  MultiFab mf2d;
288  {
289  BoxList bl2d = h_mf.boxArray().boxList();
290  for (auto& b : bl2d) {
291  b.setRange(2,0);
292  }
293  BoxArray ba2d(std::move(bl2d));
294  mf2d = MultiFab(ba2d, h_mf.DistributionMap(), 1, ngrow, MFInfo().SetAlloc(false));
295  }
296 
297  // Get MultiArray4s from the multifabs
298  MultiArray4<Real> const& ma_h_s = h_mf.arrays();
299  MultiArray4<Real> const& ma_h_s_old = h_mf_old.arrays();
300  MultiArray4<Real> const& ma_z_phys = z_phys_nd.arrays();
301 
302  // Bottom boundary
303  int k0 = domlo_z;
304 
305  // Get max value
306  h_m = ParReduce(TypeList<ReduceOpMax>{}, TypeList<Real>{}, mf2d, IntVect(ngrow,ngrow,0),
307  [=] AMREX_GPU_DEVICE (int box_no, int i, int j, int) noexcept
308  -> GpuTuple<Real>
309  {
310  // Get Array4s
311  const auto & h = ma_h_s[box_no];
312  const auto & z_arr = ma_z_phys[box_no];
313 
314  int ii = amrex::max(amrex::min(i,imax),imin);
315  int jj = amrex::max(amrex::min(j,jmax),jmin);
316 
317  // Fill the lateral boundaries
318  z_arr(i,j,k0) = z_arr(ii,jj,k0);
319 
320  // Populate h with terrain
321  h(i,j,k0) = z_arr(i,j,k0);
322 
323  // Return height for max
324  return { z_arr(i,j,k0) };
325  });
326  amrex::ParallelDescriptor::ReduceRealMax(h_m);
327 
328  if (h_m < std::numeric_limits<Real>::epsilon()) h_m = 1e-16;
329 
330  // Fill ghost cells (neglects domain boundary if not periodic)
331  h_mf.FillBoundary(geom.periodicity());
332 
333  // Make h_mf copy for old values
334  MultiFab::Copy(h_mf_old, h_mf,0,0,1,h_mf_old.nGrow());
335 
336  // Minimum allowed fractional grid spacing
337  Real gamma_m = 0.5;
338  pp.query("terrain_gamma_m", gamma_m);
339  Real z_H = 2.44*h_m/(1-gamma_m); // Klemp2011 Eqn. 11
340 
341  // Populate h_mf at k>0 with h_s, solving in ordered 2D slices
342  for (int k = domlo_z+1; k <= domhi_z; k++) // skip terrain level
343  {
344  auto const& z_lev_h = z_levels_h.data();
345 
346  Real zz = z_lev_h[k];
347  Real zz_minus = z_lev_h[k-1];
348 
349  // Hybrid attenuation profile, Klemp2011 Eqn. 9
350  Real A;
351  Real foo = cos((PI/2)*(zz/z_H));
352  if(zz < z_H) { A = foo*foo*foo*foo*foo*foo; } // A controls rate of return to atm
353  else { A = 0; }
354  Real foo_minus = cos((PI/2)*(zz_minus/z_H));
355  Real A_minus;
356  if(zz_minus < z_H) { A_minus = foo_minus*foo_minus*foo_minus*foo_minus*foo_minus*foo_minus; } // A controls rate of return to atm
357  else { A_minus = 0; }
358 
359  unsigned maxIter = 50; // M_k in paper
360  unsigned iter = 0;
361  Real threshold = gamma_m;
362  Real diff = 1.e20;
363  while (iter < maxIter && diff > threshold)
364  {
365 
366  diff = ParReduce(TypeList<ReduceOpMin>{}, TypeList<Real>{}, mf2d, IntVect(ngrow,ngrow,0),
367  [=] AMREX_GPU_DEVICE (int box_no, int i, int j, int) noexcept
368  -> GpuTuple<Real>
369  {
370  const auto & h_s = ma_h_s[box_no];
371  const auto & h_s_old = ma_h_s_old[box_no];
372 
373  Real beta_k = 0.2*std::min(zz/(2*h_m),1.0); //smoothing coefficient (Eqn. 8)
374 
375  // Clip indices for ghost-cells
376  int ii = amrex::min(amrex::max(i,domlo_x),domhi_x);
377  int jj = amrex::min(amrex::max(j,domlo_y),domhi_y);
378 
379  if (iter == 0) {
380  h_s(i,j,k) = h_s_old(i,j,k-1) + beta_k*(h_s_old(ii+1,jj ,k-1)
381  + h_s_old(ii-1,jj ,k-1)
382  + h_s_old(ii ,jj+1,k-1)
383  + h_s_old(ii ,jj-1,k-1) - 4*h_s_old(ii,jj,k-1));
384  }
385  else {
386  h_s(i,j,k) = h_s_old(i,j,k ) + beta_k*(h_s_old(ii+1,jj ,k )
387  + h_s_old(ii-1,jj ,k )
388  + h_s_old(ii ,jj+1,k )
389  + h_s_old(ii ,jj-1,k ) - 4*h_s_old(ii,jj,k ));
390  }
391 
392  // Minimum vertical grid spacing condition (Klemp2011 Eqn. 7)
393  return { (zz + A * h_s(i,j,k) - (zz_minus + A_minus * h_s(i,j,k-1))) / (zz - zz_minus) };
394 
395  }); //ParReduce
396 
397  MultiFab::Copy(h_mf_old, h_mf,0,0,1,h_mf_old.nGrow());
398 
399  ParallelDescriptor::ReduceRealMin(diff);
400 
401  iter++;
402 
403  //fill ghost points
404  h_mf_old.FillBoundary(geom.periodicity());
405 
406  } //while
407 
408  auto const& z_lev_d = z_levels_d.data();
409 
410  //Populate z_phys_nd by solving z_arr(i,j,k) = z + A*h_s(i,j,k)
411  for ( MFIter mfi(z_phys_nd, TilingIfNotGPU()); mfi.isValid(); ++mfi )
412  {
413  // Grown box with no z range
414  Box xybx = mfi.growntilebox(ngrow);
415  xybx.setRange(2,0);
416 
417  Array4<Real> const& h_s = h_mf_old.array(mfi);
418  Array4<Real> const& z_arr = z_phys_nd.array(mfi);
419 
420  ParallelFor(xybx, [=] AMREX_GPU_DEVICE (int i, int j, int) {
421 
422  // Location of nodes
423  Real z = z_lev_d[k];
424 
425  // STF model from p2164 of Klemp2011 (Eqn. 4)
426  z_arr(i,j,k) = z + A*h_s(i,j,k);
427 
428  // Fill below the bottom surface
429  if (k == 1) {
430  z_arr(i,j,k0-1) = 2.0*z_arr(i,j,k0) - z_arr(i,j,k);
431  }
432  });
433  } // mfi
434  } // k
435 
436  Gpu::streamSynchronize();
437 
438  break;
439  } // case 1
440 
441  case 2: // Sullivan TF Method
442  {
443  int k0 = 0;
444 
445  for ( MFIter mfi(z_phys_nd, TilingIfNotGPU()); mfi.isValid(); ++mfi )
446  {
447  // Grown box with corrected ghost cells at top
448  Box gbx = mfi.growntilebox(ngrow);
449  gbx.setRange(2,domlo_z,domhi_z+1);
450 
451  Array4<Real> const& z_arr = z_phys_nd.array(mfi);
452  auto const& z_lev = z_levels_d.data();
453 
454  ParallelFor(gbx, [=] AMREX_GPU_DEVICE (int i, int j, int k)
455  {
456  // Vertical grid stretching
457  Real z = z_lev[k];
458 
459  int ii = amrex::max(amrex::min(i,imax),imin);
460  int jj = amrex::max(amrex::min(j,jmax),jmin);
461 
462  // Fill levels using model from Sullivan et. al. 2014
463  int omega = 3; //Used to adjust how rapidly grid lines level out. omega=1 is BTF!
464  z_arr(i,j,k) = z + (std::pow((1. - (z/z_top)),omega) * z_arr(ii,jj,k0));
465 
466  // Fill lateral boundaries and below the bottom surface
467  if (k == k0) {
468  z_arr(i,j,k0 ) = z_arr(ii,jj,k0);
469  }
470  if (k == 1) {
471  z_arr(i,j,k0-1) = 2.0*z_arr(ii,jj,k0) - z_arr(i,j,k);
472  }
473  });
474  } // mfi
475  break;
476  } // case 2
477 
478  case 3: // Debugging Test Method -- applies Sullivan TF starting at k = 1 so that domain does not change size
479  {
480  int k0 = 0;
481 
482  for ( MFIter mfi(z_phys_nd, TilingIfNotGPU()); mfi.isValid(); ++mfi )
483  {
484  // Grown box with corrected ghost cells at top
485  Box gbx = mfi.growntilebox(ngrow);
486  gbx.setRange(2,domlo_z,domhi_z+1);
487 
488  Array4<Real> const& z_arr = z_phys_nd.array(mfi);
489  auto const& z_lev = z_levels_d.data();
490 
491  ParallelFor(gbx, [=] AMREX_GPU_DEVICE (int i, int j, int k)
492  {
493  // Vertical grid stretching
494  Real z = z_lev[k];
495 
496  int ii = amrex::max(amrex::min(i,imax),imin);
497  int jj = amrex::max(amrex::min(j,jmax),jmin);
498 
499  // Fill values outside the lateral boundaries and below the bottom surface (necessary if init_type = "WRFInput")
500  if (k == k0+1) {
501  z_arr(i,j,k) = z + z_arr(ii,jj,k0);
502  } else {
503  // Fill levels using model from Sullivan et. al. 2014
504  int omega = 3; //Used to adjust how rapidly grid lines level out. omega=1 is BTF!
505  z_arr(i,j,k) = z + (std::pow((1. - (z/z_top)),omega) * z_arr(ii,jj,k0));
506  }
507  });
508  gbx.setBig(2,0);
509  ParallelFor(gbx, [=] AMREX_GPU_DEVICE (int i, int j, int k)
510  {
511  z_arr(i,j,k ) = 0.0;
512  z_arr(i,j,k-1) = -z_arr(i,j,k+1);
513  });
514  } // mfi
515  break;
516  } // case 3
517  } //switch
518 }
constexpr amrex::Real PI
Definition: ERF_Constants.H:6
ParmParse pp("prob")
@ omega
Definition: ERF_Morrison.H:53

Referenced by make_terrain_fitted_coords().

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

◆ make_areas()

void make_areas ( const Geometry &  geom,
MultiFab &  z_phys_nd,
MultiFab &  ax,
MultiFab &  ay,
MultiFab &  az 
)

Computation of area fractions on faces

567 {
568  const auto* dx = geom.CellSize();
569  Real dzInv = 1.0/dx[2];
570 
571  // Domain valid box (z_nd is nodal)
572  const Box& domain = geom.Domain();
573  int domlo_z = domain.smallEnd(2);
574 
575  // The z-faces are always full when using terrain-fitted coordinates
576  az.setVal(1.0);
577 
578  //
579  // x-areas
580  //
581 #ifdef _OPENMP
582 #pragma omp parallel if (Gpu::notInLaunchRegion())
583 #endif
584  for ( MFIter mfi(ax, TilingIfNotGPU()); mfi.isValid(); ++mfi )
585  {
586  Box gbx = mfi.growntilebox(ax.nGrow());
587  if (gbx.smallEnd(2) < domlo_z) {
588  gbx.setSmall(2,domlo_z);
589  }
590 
591  Array4<Real const> z_nd = z_phys_nd.const_array(mfi);
592  Array4<Real > ax_arr = ax.array(mfi);
593  ParallelFor(gbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
594  ax_arr(i, j, k) = .5 * dzInv * (
595  z_nd(i,j,k+1) + z_nd(i,j+1,k+1) - z_nd(i,j,k) - z_nd(i,j+1,k));
596  });
597  }
598 
599  //
600  // y-areas
601  //
602 #ifdef _OPENMP
603 #pragma omp parallel if (Gpu::notInLaunchRegion())
604 #endif
605  for ( MFIter mfi(ay, TilingIfNotGPU()); mfi.isValid(); ++mfi )
606  {
607  Box gbx = mfi.growntilebox(ay.nGrow());
608  if (gbx.smallEnd(2) < domlo_z) {
609  gbx.setSmall(2,domlo_z);
610  }
611 
612  Array4<Real const> z_nd = z_phys_nd.const_array(mfi);
613  Array4<Real > ay_arr = ay.array(mfi);
614  ParallelFor(gbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
615  ay_arr(i, j, k) = .5 * dzInv * (
616  z_nd(i,j,k+1) + z_nd(i+1,j,k+1) - z_nd(i,j,k) - z_nd(i+1,j,k));
617  });
618  }
619 
620  ax.FillBoundary(geom.periodicity());
621  ay.FillBoundary(geom.periodicity());
622  az.FillBoundary(geom.periodicity());
623 }

Referenced by ERF::update_terrain_arrays().

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

◆ make_J()

void make_J ( const Geometry &  geom,
MultiFab &  z_phys_nd,
MultiFab &  detJ_cc 
)

Computation of detJ at cell-center

527 {
528  const auto *dx = geom.CellSize();
529  Real dzInv = 1.0/dx[2];
530 
531  // Domain valid box (z_nd is nodal)
532  const Box& domain = geom.Domain();
533  int domlo_z = domain.smallEnd(2);
534 
535  // Number of ghost cells
536  int ngrow= detJ_cc.nGrow();
537 
538 #ifdef _OPENMP
539 #pragma omp parallel if (Gpu::notInLaunchRegion())
540 #endif
541  for ( MFIter mfi(detJ_cc, TilingIfNotGPU()); mfi.isValid(); ++mfi )
542  {
543  Box gbx = mfi.growntilebox(ngrow);
544  if (gbx.smallEnd(2) < domlo_z) {
545  gbx.setSmall(2,domlo_z);
546  }
547  Array4<Real const> z_nd = z_phys_nd.const_array(mfi);
548  Array4<Real > detJ = detJ_cc.array(mfi);
549  ParallelFor(gbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
550  detJ(i, j, k) = .25 * dzInv * (
551  z_nd(i,j,k+1) + z_nd(i+1,j,k+1) + z_nd(i,j+1,k+1) + z_nd(i+1,j+1,k+1)
552  -z_nd(i,j,k ) - z_nd(i+1,j,k ) - z_nd(i,j+1,k ) - z_nd(i+1,j+1,k ) );
553  });
554  }
555  detJ_cc.FillBoundary(geom.periodicity());
556 }

Referenced by ERF::update_terrain_arrays().

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

◆ make_terrain_fitted_coords()

void make_terrain_fitted_coords ( int  lev,
const Geometry &  geom,
MultiFab &  z_phys_nd,
Vector< Real > const &  z_levels_h,
GpuArray< ERF_BC, AMREX_SPACEDIM *2 > &  phys_bc_type 
)

Computation of the terrain grid from BTF, STF, or Sullivan TF model

49 {
50  const Box& domain = geom.Domain();
51 
52  int domlo_z = domain.smallEnd(2);
53  int domhi_z = domain.bigEnd(2) + 1;
54 
55  // Just in case ...
56  z_phys_nd.setDomainBndry(1.234e20,0,1,geom);
57 
58  // ****************************************************************************
59 
60  if (lev == 0) {
61  BoxArray ba(z_phys_nd.boxArray());
62  bool all_boxes_touch_bottom = true;
63  for (int i = 0; i < ba.size(); i++) {
64  if (ba[i].smallEnd(2) != domlo_z) {
65  all_boxes_touch_bottom = false;
66  }
67  }
68 
69  if (all_boxes_touch_bottom) {
70  init_which_terrain_grid(lev, geom, z_phys_nd, z_levels_h);
71  } else {
72 
73  BoxArray ba_new(domain);
74  ChopGrids2D(ba_new, domain, ParallelDescriptor::NProcs());
75 
76  DistributionMapping dm_new(ba_new);
77  ba_new.surroundingNodes();
78 
79  MultiFab z_phys_nd_new(ba_new, dm_new, 1, z_phys_nd.nGrowVect());
80 
81  z_phys_nd_new.ParallelCopy(z_phys_nd,0,0,1,z_phys_nd.nGrowVect(),z_phys_nd.nGrowVect());
82 
83  init_which_terrain_grid(lev, geom, z_phys_nd_new, z_levels_h);
84 
85  z_phys_nd.ParallelCopy(z_phys_nd_new,0,0,1,z_phys_nd.nGrowVect(),z_phys_nd.nGrowVect());
86  }
87  } else { // lev > 0
88  init_which_terrain_grid(lev, geom, z_phys_nd, z_levels_h);
89  }
90 
91  //
92  // Fill ghost layers and corners (including periodic) -- no matter what level
93  //
94  z_phys_nd.FillBoundary(geom.periodicity());
95 
96  if (phys_bc_type[Orientation(0,Orientation::low )] == ERF_BC::symmetry ||
97  phys_bc_type[Orientation(0,Orientation::high)] == ERF_BC::symmetry ||
98  phys_bc_type[Orientation(1,Orientation::low )] == ERF_BC::symmetry ||
99  phys_bc_type[Orientation(1,Orientation::high)] == ERF_BC::symmetry) {
100 
101  const auto& dom_lo = lbound(convert(domain,IntVect(1,1,1)));
102  const auto& dom_hi = ubound(convert(domain,IntVect(1,1,1)));
103 
104  for (MFIter mfi(z_phys_nd,true); mfi.isValid(); ++mfi) {
105  const Box& bx = mfi.growntilebox();
106  const Array4< Real> z_nd_arr = z_phys_nd.array(mfi);
107  if (phys_bc_type[Orientation(0,Orientation::low)] == ERF_BC::symmetry && bx.smallEnd(0) == dom_lo.x) {
108  ParallelFor(makeSlab(bx,0,1), [=] AMREX_GPU_DEVICE (int , int j, int k)
109  {
110  z_nd_arr(dom_lo.x-1,j,k) = z_nd_arr(dom_lo.x+1,j,k);
111  });
112  }
113  if (phys_bc_type[Orientation(0,Orientation::high)] == ERF_BC::symmetry && bx.bigEnd(0) == dom_hi.x) {
114  ParallelFor(makeSlab(bx,0,1), [=] AMREX_GPU_DEVICE (int , int j, int k)
115  {
116  z_nd_arr(dom_hi.x+1,j,k) = z_nd_arr(dom_hi.x-1,j,k);
117  });
118  }
119  if (phys_bc_type[Orientation(1,Orientation::low)] == ERF_BC::symmetry && bx.smallEnd(1) == dom_lo.y) {
120  ParallelFor(makeSlab(bx,1,1), [=] AMREX_GPU_DEVICE (int i, int , int k)
121  {
122  z_nd_arr(i,dom_lo.y-1,k) = z_nd_arr(i,dom_lo.y+1,k);
123  });
124  }
125  if (phys_bc_type[Orientation(1,Orientation::high)] == ERF_BC::symmetry && bx.bigEnd(1) == dom_hi.y) {
126  ParallelFor(makeSlab(bx,1,1), [=] AMREX_GPU_DEVICE (int i, int , int k)
127  {
128  z_nd_arr(i,dom_hi.y+1,k) = z_nd_arr(i,dom_hi.y-1,k);
129  });
130  }
131  }
132  }
133 
134  //********************************************************************************
135  // Populate domain boundary cells in z-direction
136  //********************************************************************************
137  int ngrow = z_phys_nd.nGrow();
138 
139  for ( MFIter mfi(z_phys_nd, TilingIfNotGPU()); mfi.isValid(); ++mfi )
140  {
141  // Only set values above top of domain if this box reaches that far
142  Box nd_bx = mfi.tilebox();
143 
144  // Note that domhi_z is already nodal in the z-direction
145  if (nd_bx.bigEnd(2) >= domhi_z) {
146  // Grown box with no z range
147  Box bx_zhi = mfi.growntilebox(ngrow);
148  bx_zhi.setSmall(2,domhi_z+1);
149  Array4<Real> const& z_arr = z_phys_nd.array(mfi);
150 
151  // Extrapolate top layer
152  ParallelFor(bx_zhi, [=] AMREX_GPU_DEVICE (int i, int j, int k) {
153  z_arr(i,j,k) = z_arr(i,j,domhi_z)
154  + (k-domhi_z) * (z_arr(i,j,domhi_z) - z_arr(i,j,domhi_z-1));
155  });
156  }
157  }
158 } // make_terrain_fitted_coords
void ChopGrids2D(BoxArray &ba, const Box &domain, int target_size)
Definition: ERF_ChopGrids.cpp:21
const auto & dom_hi
Definition: ERF_SetupVertDiff.H:2
const auto & dom_lo
Definition: ERF_SetupVertDiff.H:1
void init_which_terrain_grid(int lev, Geometry const &geom, MultiFab &z_phys_nd, Vector< Real > const &z_levels_h)
Definition: ERF_TerrainMetrics.cpp:161

Referenced by ERF::init_zphys(), and ERF::remake_zphys().

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

◆ make_zcc()

void make_zcc ( const Geometry &  geom,
MultiFab &  z_phys_nd,
MultiFab &  z_phys_cc 
)

Computation of z_phys at cell-center

632 {
633 #ifdef _OPENMP
634 #pragma omp parallel if (Gpu::notInLaunchRegion())
635 #endif
636  for ( MFIter mfi(z_phys_cc, TilingIfNotGPU()); mfi.isValid(); ++mfi )
637  {
638  Box gbx = mfi.growntilebox();
639  Array4<Real const> z_nd = z_phys_nd.const_array(mfi);
640  Array4<Real > z_cc = z_phys_cc.array(mfi);
641  ParallelFor(gbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
642  z_cc(i, j, k) = .125 * ( z_nd(i,j,k ) + z_nd(i+1,j,k ) + z_nd(i,j+1,k ) + z_nd(i+1,j+1,k )
643  +z_nd(i,j,k+1) + z_nd(i+1,j,k+1) + z_nd(i,j+1,k+1) + z_nd(i+1,j+1,k+1) );
644  });
645  }
646  z_phys_cc.FillBoundary(geom.periodicity());
647 }

Referenced by ERF::post_timestep(), and ERF::update_terrain_arrays().

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