ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
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)
 

Function Documentation

◆ 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 }

Referenced by ERF::init_stuff().

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

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

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

Referenced by ERF::update_terrain_arrays().

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

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

Referenced by ERF::update_terrain_arrays().

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

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

626 {
627 #ifdef _OPENMP
628 #pragma omp parallel if (Gpu::notInLaunchRegion())
629 #endif
630  for ( MFIter mfi(z_phys_cc, TilingIfNotGPU()); mfi.isValid(); ++mfi )
631  {
632  Box gbx = mfi.growntilebox();
633  Array4<Real const> z_nd = z_phys_nd.const_array(mfi);
634  Array4<Real > z_cc = z_phys_cc.array(mfi);
635  ParallelFor(gbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
636  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 )
637  +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) );
638  });
639  }
640  z_phys_cc.FillBoundary(geom.periodicity());
641 }

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

Here is the caller graph for this function: