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, Real z_offset)
 
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

661 {
662  auto const& ma_z_nd_arr = z_phys_nd.const_arrays();
663  GpuTuple<Real> min = ParReduce(TypeList<ReduceOpMin>{},
664  TypeList<Real>{},
665  z_phys_nd, IntVect(0),
666  [=] AMREX_GPU_DEVICE (int box_no, int i, int j, int k) noexcept
667  -> GpuTuple<Real>
668  {
669  amrex::Real dz = Compute_Z_AtWFace(i,j,k+1,ma_z_nd_arr[box_no]) -
670  Compute_Z_AtWFace(i,j,k ,ma_z_nd_arr[box_no]);
671  return { dz };
672  });
673  Real r = (get<0>(min) + std::numeric_limits<amrex::Real>::epsilon());
674  ParallelDescriptor::ReduceRealMin(r);
675  return r;
676 }
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:376
@ dz
Definition: ERF_AdvanceWSM6.cpp:104
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,
Real  z_offset 
)

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

20 {
21  const auto& dx = geom.CellSize();
22  Real dz = dx[2];
23 
24  for (MFIter mfi(z_phys_nd,true); mfi.isValid(); ++mfi)
25  {
26  const Box& bx = mfi.growntilebox();
27  const Array4< Real> z_nd_arr = z_phys_nd.array(mfi);
28  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k)
29  {
30  z_nd_arr(i,j,k) = k * dz - z_offset;
31  });
32  }
33 
34  for (MFIter mfi(z_phys_cc,true); mfi.isValid(); ++mfi)
35  {
36  const Box& bx = mfi.growntilebox();
37  const Array4< Real> z_cc_arr = z_phys_cc.array(mfi);
38  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k)
39  {
40  z_cc_arr(i,j,k) = (k + myhalf) * dz - z_offset;
41  });
42  }
43 }
constexpr amrex::Real myhalf
Definition: ERF_Constants.H:13
const Real dx
Definition: ERF_InitCustomPert_ABL.H:23
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);})

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

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

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

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

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

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

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

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

638 {
639 #ifdef _OPENMP
640 #pragma omp parallel if (Gpu::notInLaunchRegion())
641 #endif
642  for ( MFIter mfi(z_phys_cc, TilingIfNotGPU()); mfi.isValid(); ++mfi )
643  {
644  Box gbx = mfi.growntilebox();
645  Array4<Real const> z_nd = z_phys_nd.const_array(mfi);
646  Array4<Real > z_cc = z_phys_cc.array(mfi);
647  ParallelFor(gbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
648  z_cc(i, j, k) = Real(.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 )
649  +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) );
650  });
651  }
652  z_phys_cc.FillBoundary(geom.periodicity());
653 }

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: