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

650 {
651  auto const& ma_z_nd_arr = z_phys_nd.const_arrays();
652  GpuTuple<Real> min = ParReduce(TypeList<ReduceOpMin>{},
653  TypeList<Real>{},
654  z_phys_nd, IntVect(0),
655  [=] AMREX_GPU_DEVICE (int box_no, int i, int j, int k) noexcept
656  -> GpuTuple<Real>
657  {
658  amrex::Real dz = Compute_Z_AtWFace(i,j,k+1,ma_z_nd_arr[box_no]) -
659  Compute_Z_AtWFace(i,j,k ,ma_z_nd_arr[box_no]);
660  return { dz };
661  });
662  Real r = (get<0>(min) + std::numeric_limits<amrex::Real>::epsilon());
663  ParallelDescriptor::ReduceRealMin(r);
664  return r;
665 }
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 }

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

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

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

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

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 bx_zhi = mfi.growntilebox(ngrow);
145  bx_zhi.setSmall(2,domhi_z+1);
146  Array4<Real> const& z_arr = z_phys_nd.array(mfi);
147 
148  // Extrapolate top layer
149  ParallelFor(bx_zhi, [=] AMREX_GPU_DEVICE (int i, int j, int k) {
150  z_arr(i,j,k) = z_arr(i,j,domhi_z)
151  + (k-domhi_z) * (z_arr(i,j,domhi_z) - z_arr(i,j,domhi_z-1));
152  });
153  }
154  }
155 } // 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:158

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

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

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

Here is the caller graph for this function: