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 <algorithm>
#include <array>
#include <cmath>
#include <map>
#include <numeric>
#include <utility>
#include <vector>
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)
 
BoxArray join_boxes_stacked_in_z (const BoxArray &ba)
 
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, FineTerrain fine_terrain, MultiFab const *z_phys_interp)
 
FineTerrain which_fine_terrain ()
 
void init_fine_terrain_grid (int lev, Geometry const &geom, MultiFab &z_phys_nd, Vector< Real > const &z_levels_h, FineTerrain fine_terrain, MultiFab const *z_phys_interp)
 
void init_which_terrain_grid (int lev, Geometry const &geom, MultiFab &z_phys_nd, Vector< Real > const &z_levels_h, FineTerrain fine_terrain, MultiFab const *z_phys_interp)
 
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

1074 {
1075  auto const& ma_z_nd_arr = z_phys_nd.const_arrays();
1076  GpuTuple<Real> min = ParReduce(TypeList<ReduceOpMin>{},
1077  TypeList<Real>{},
1078  z_phys_nd, IntVect(0),
1079  [=] AMREX_GPU_DEVICE (int box_no, int i, int j, int k) noexcept
1080  -> GpuTuple<Real>
1081  {
1082  amrex::Real dz = Compute_Z_AtWFace(i,j,k+1,ma_z_nd_arr[box_no]) -
1083  Compute_Z_AtWFace(i,j,k ,ma_z_nd_arr[box_no]);
1084  return { dz };
1085  });
1086  Real r = (get<0>(min) + std::numeric_limits<amrex::Real>::epsilon());
1087  ParallelDescriptor::ReduceRealMin(r);
1088  return r;
1089 }
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:729
@ dz
Definition: ERF_AdvanceWDM6.cpp:272
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

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

Referenced by ERF::init_stuff().

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

◆ init_fine_terrain_grid()

void init_fine_terrain_grid ( int  lev,
Geometry const &  geom,
MultiFab &  z_phys_nd,
Vector< Real > const &  z_levels_h,
FineTerrain  fine_terrain,
MultiFab const *  z_phys_interp 
)

Finish the terrain-fitted mesh on a fine level built from the coarse level.

On entry z_phys_nd holds the mesh interpolated from the coarse level in its valid region, with – for Transform – the fine-resolution terrain already written into the k=0 slab. The nodes outside the domain are still bogus_large_value, because neither the interpolation nor make_terrain_fitted_coords()'s setDomainBndry() call gave them a usable value, so we fill them here the same way the BTF branch does.

445 {
446  AMREX_ALWAYS_ASSERT(lev > 0);
447  AMREX_ALWAYS_ASSERT(fine_terrain != FineTerrain::None);
448 
449  const Box& domain = geom.Domain();
450  int domlo_x = domain.smallEnd(0); int domhi_x = domain.bigEnd(0) + 1;
451  int domlo_y = domain.smallEnd(1); int domhi_y = domain.bigEnd(1) + 1;
452  int domlo_z = domain.smallEnd(2);
453 
454  IntVect ngrowVect = z_phys_nd.nGrowVect();
455 
456  if (fine_terrain == FineTerrain::Transform) {
457  AMREX_ALWAYS_ASSERT(z_phys_interp != nullptr);
458 
459  const BoxArray& ba = z_phys_nd.boxArray();
460 
461  //
462  // The correction is anchored at the surface, so a box that does not reach the
463  // surface would keep the uncorrected interpolated mesh and would not join the
464  // box below it. Say so rather than silently building a tangled mesh.
465  //
466  for (int i = 0; i < ba.size(); i++) {
467  if (ba[i].smallEnd(2) != domlo_z) {
468  Abort("erf.amr_terrain_refinement = transform requires that every box on a fine level reach the surface: increase amr.max_grid_size_z or use erf.amr_terrain_refinement = interpolate");
469  }
470  }
471 
472  //
473  // Decay over the height of the fine grids as a whole and never over the height
474  // of an individual box, so that the mesh does not depend on the decomposition.
475  //
476  int khi_lev = ba.minimalBox().bigEnd(2);
477  Real z_lev_top = z_levels_h[khi_lev];
478  Real z_lev_bot = z_levels_h[domlo_z];
479  AMREX_ALWAYS_ASSERT(z_lev_top > z_lev_bot);
480 
481  Gpu::DeviceVector<Real> z_levels_d(z_levels_h.size());
482  Gpu::copy(Gpu::hostToDevice, z_levels_h.begin(), z_levels_h.end(), z_levels_d.begin());
483  auto const& z_lev = z_levels_d.data();
484 
485  for (MFIter mfi(z_phys_nd, TilingIfNotGPU()); mfi.isValid(); ++mfi)
486  {
487  const Box& bx = mfi.tilebox();
488 
489  Array4<Real > const& z_arr = z_phys_nd.array(mfi);
490  Array4<Real const> const& z_interp = z_phys_interp->const_array(mfi);
491 
492  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k)
493  {
494  if (k > domlo_z && k <= khi_lev) {
495  // How much taller the fine terrain is than the interpolated surface
496  Real delta_terrain = z_arr(i,j,domlo_z) - z_interp(i,j,domlo_z);
497 
498  Real decay = (z_lev_top - z_lev[k]) / (z_lev_top - z_lev_bot);
499  decay = amrex::max(Real(0.0), amrex::min(Real(1.0), decay));
500 
501  z_arr(i,j,k) = z_interp(i,j,k) + decay * delta_terrain;
502  }
503  });
504  } // mfi
505 
506  // The loop above only covers the valid region, so bring the ghost nodes inside
507  // the domain up to date before the fill below reads them
508  z_phys_nd.FillBoundary(geom.periodicity());
509  }
510 
511  //
512  // Extend the mesh to the nodes outside the lateral domain boundary by copying the
513  // nearest column inside it, which is the value the BTF branch gives those nodes.
514  // Like that branch we clamp in a periodic direction too, even though FillBoundary
515  // below will replace what we write there with the periodic image: that way every
516  // node we read here is one inside the domain, whose value we know is already set.
517  //
518  int imin = domlo_x; int imax = domhi_x;
519  int jmin = domlo_y; int jmax = domhi_y;
520 
521  for (MFIter mfi(z_phys_nd, TilingIfNotGPU()); mfi.isValid(); ++mfi)
522  {
523  Box gbx = mfi.growntilebox(ngrowVect);
524  Array4<Real> const& z_arr = z_phys_nd.array(mfi);
525 
526  // Note that we only write nodes outside the domain and only read nodes inside
527  // it, so no node is both read and written here.
528  ParallelFor(gbx, [=] AMREX_GPU_DEVICE (int i, int j, int k)
529  {
530  int ii = amrex::max(amrex::min(i,imax),imin);
531  int jj = amrex::max(amrex::min(j,jmax),jmin);
532  if (ii != i || jj != j) {
533  z_arr(i,j,k) = z_arr(ii,jj,k);
534  }
535  });
536  } // mfi
537 
538  z_phys_nd.FillBoundary(geom.periodicity());
539 
540  for (MFIter mfi(z_phys_nd, TilingIfNotGPU()); mfi.isValid(); ++mfi)
541  {
542  const Box& bx = mfi.validbox();
543  Box gbx = mfi.growntilebox(ngrowVect);
544 
545  if (bx.smallEnd(2) == domlo_z)
546  {
547  Array4<Real> const& z_arr = z_phys_nd.array(mfi);
548 
549  // Fill lateral boundaries below the bottom surface
550  ParallelFor(makeSlab(gbx,2,domlo_z), [=] AMREX_GPU_DEVICE (int i, int j, int)
551  {
552  z_arr(i,j,domlo_z-1) = two*z_arr(i,j,domlo_z) - z_arr(i,j,domlo_z+1);
553  });
554  }
555  } // mfi
556 } // init_fine_terrain_grid
AMREX_ALWAYS_ASSERT(bx.length()[2]==khi+1)
constexpr amrex::Real two
Definition: ERF_NumericalConstants.H:31
@ Transform
add the fine terrain as a correction that decays with height
@ None
caller has not prepared a fine mesh from the coarse level

Referenced by init_which_terrain_grid().

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,
FineTerrain  fine_terrain,
MultiFab const *  z_phys_interp 
)

Initialize the terrain grid using the selected model (BTF, STF, or Sullivan TF).

Parameters
[in]levLevel in the AMR hierarchy.
[in]geomGeometry defining the domain.
[out]z_phys_ndMultiFab to be filled with nodal physical heights.
[in]z_levels_hReference z-levels for grid stretching.
573 {
574  // User-selected method from inputs file (BTF default)
575  ParmParse pp("erf");
576  int terrain_smoothing = 0;
577  pp.queryAdd("terrain_smoothing", terrain_smoothing);
578 
579  //
580  // The STF and Sullivan TF transformations need the full column from the surface to
581  // the domain top, so on a fine level we build the mesh from the one interpolated
582  // from the coarse level instead. Only a caller that has done that interpolation
583  // (and told us so) may take this path; everyone else still aborts.
584  //
585  if (lev > 0 && terrain_smoothing != 0) {
586  if (fine_terrain == FineTerrain::None) {
587  Abort("terrain_smoothing = " + std::to_string(terrain_smoothing) +
588  " on level > 0 requires setting erf.amr_terrain_refinement (transform or interpolate)");
589  }
590  init_fine_terrain_grid(lev, geom, z_phys_nd, z_levels_h, fine_terrain, z_phys_interp);
591  return;
592  }
593 
594  // Number of ghost cells
595  int ngrow = z_phys_nd.nGrow();
596  IntVect ngrowVect = z_phys_nd.nGrowVect();
597 
598  const Box& domain = geom.Domain();
599  int domlo_x = domain.smallEnd(0); int domhi_x = domain.bigEnd(0) + 1;
600  int domlo_y = domain.smallEnd(1); int domhi_y = domain.bigEnd(1) + 1;
601  int domlo_z = domain.smallEnd(2); int domhi_z = domain.bigEnd(2) + 1;
602 
603  int imin = domlo_x; // if (geom.isPeriodic(0)) imin -= z_phys_nd.nGrowVect()[0];
604  int jmin = domlo_y; // if (geom.isPeriodic(1)) jmin -= z_phys_nd.nGrowVect()[1];
605 
606  int imax = domhi_x; // if (geom.isPeriodic(0)) imax += z_phys_nd.nGrowVect()[0];
607  int jmax = domhi_y; // if (geom.isPeriodic(1)) jmax += z_phys_nd.nGrowVect()[1];
608 
609  int nz = static_cast<int>(z_levels_h.size());
610  Real z_top = z_levels_h[nz-1];
611 
612  Gpu::DeviceVector<Real> z_levels_d;
613  z_levels_d.resize(nz);
614  Gpu::copy(Gpu::hostToDevice, z_levels_h.begin(), z_levels_h.end(), z_levels_d.begin());
615 
616  switch(terrain_smoothing) {
617  case 0: // BTF Method
618  {
619  for ( MFIter mfi(z_phys_nd, TilingIfNotGPU()); mfi.isValid(); ++mfi )
620  {
621  // Note that this box is nodal because it is based on z_phys_nd
622  const Box& bx = mfi.validbox();
623 
624  int k0 = bx.smallEnd()[2];
625 
626  // Grown box with corrected ghost cells at top
627  Box gbx = mfi.growntilebox(ngrowVect);
628 
629  if (bx.smallEnd(2) == domlo_z) {
630  gbx.setSmall(2,domlo_z);
631  } else {
632  gbx.growLo(2,-1);
633  }
634 
635  // Note that we don't overwrite the values at the high end of the box
636  // regardless of whether the box reaches the top of the domain or not.
637  // In the case of lev > 0, this ensures that the fine nodes at the top
638  // of a fine box are those that are interpolated from the coarse grid
639  if (bx.bigEnd(2) == domhi_z) {
640  gbx.setBig(2,domhi_z);
641  } else {
642  gbx.growHi(2,-1);
643  if (gbx.bigEnd(2) > domhi_z) {
644  gbx.setBig(2,domhi_z);
645  }
646  }
647 
648  Array4<Real> const& z_arr = z_phys_nd.array(mfi);
649  auto const& z_lev = z_levels_d.data();
650 
651  //
652  // Vertical grid stretching using BTF model from p2163 of Klemp2011
653  // z_levels are only defined from k = dom_lo to dom_hi (nodal)
654  //
655  ParallelFor(gbx, [=] AMREX_GPU_DEVICE (int i, int j, int k)
656  {
657  int ii = amrex::max(amrex::min(i,imax),imin);
658  int jj = amrex::max(amrex::min(j,jmax),jmin);
659 
660  //
661  // Start with flat z_lev set either with uniform cell size or specified z_levels
662  // If k0 = 0 then z_arr at k0 has already been filled from the terrain data
663  // If k0 > 0 then z_arr at k0 has already been filled from interpolation
664  //
665  Real z = z_lev[k];
666  Real z_sfc = z_arr(ii,jj,k0);
667  Real z_lev_sfc = z_lev[k0];
668 
669  z_arr(i,j,k) = ( (z_sfc - z_lev_sfc) * z_top +
670  (z_top - z_sfc ) * z ) / (z_top - z_lev_sfc);
671  });
672  } // mfi
673 
674  z_phys_nd.FillBoundary(geom.periodicity());
675 
676  for ( MFIter mfi(z_phys_nd, TilingIfNotGPU()); mfi.isValid(); ++mfi )
677  {
678  // Note that this box is nodal because it is based on z_phys_nd
679  const Box& bx = mfi.validbox();
680  Box gbx = mfi.growntilebox(ngrowVect);
681 
682  int k0 = bx.smallEnd()[2];
683 
684  if (k0 == 0)
685  {
686  Array4<Real> const& z_arr = z_phys_nd.array(mfi);
687 
688  // Fill lateral boundaries below the bottom surface
689  ParallelFor(makeSlab(gbx,2,0), [=] AMREX_GPU_DEVICE (int i, int j, int)
690  {
691  z_arr(i,j,-1) = two*z_arr(i,j,0) - z_arr(i,j,1);
692  });
693  }
694  } // mfi
695  break;
696  } // case 0
697 
698  case 1: // STF Method
699  {
700  // Get MultiFab spanning domain with 1 level of ghost cells
701  MultiFab h_mf( z_phys_nd.boxArray(), z_phys_nd.DistributionMap(), 1, ngrow+1);
702  MultiFab h_mf_old(z_phys_nd.boxArray(), z_phys_nd.DistributionMap(), 1, ngrow+1);
703 
704  // Save max height for smoothing
705  Real h_m;
706 
707  // Create 2D MF without allocation
708  MultiFab mf2d;
709  {
710  BoxList bl2d = h_mf.boxArray().boxList();
711  for (auto& b : bl2d) { b.setRange(2,b.smallEnd(2)); }
712  BoxArray ba2d(std::move(bl2d));
713  mf2d = MultiFab(ba2d, h_mf.DistributionMap(), 1, ngrow, MFInfo().SetAlloc(false));
714  }
715 
716  // Get MultiArray4s from the multifabs
717  MultiArray4<Real> const& ma_h_s = h_mf.arrays();
718  MultiArray4<Real> const& ma_h_s_old = h_mf_old.arrays();
719  MultiArray4<Real> const& ma_z_phys = z_phys_nd.arrays();
720 
721  // Bottom boundary
722  int k0 = domlo_z;
723 
724  // Get max value
725  h_m = ParReduce(TypeList<ReduceOpMax>{}, TypeList<Real>{}, mf2d, IntVect(ngrow,ngrow,0),
726  [=] AMREX_GPU_DEVICE (int box_no, int i, int j, int) noexcept
727  -> GpuTuple<Real>
728  {
729  // Get Array4s
730  const auto & h = ma_h_s[box_no];
731  const auto & z_arr = ma_z_phys[box_no];
732 
733  int ii = amrex::max(amrex::min(i,imax),imin);
734  int jj = amrex::max(amrex::min(j,jmax),jmin);
735 
736  // Fill the lateral boundaries
737  z_arr(i,j,k0) = z_arr(ii,jj,k0);
738 
739  // Populate h with terrain
740  h(i,j,k0) = z_arr(i,j,k0);
741 
742  // Return height for max
743  return { z_arr(i,j,k0) };
744  });
745  amrex::ParallelDescriptor::ReduceRealMax(h_m);
746 
747  if (h_m < std::numeric_limits<Real>::epsilon()) h_m = Real(1e-16);
748 
749  // Fill ghost cells (neglects domain boundary if not periodic)
750  h_mf.FillBoundary(geom.periodicity());
751 
752  // Make h_mf copy for old values
753  MultiFab::Copy(h_mf_old, h_mf,0,0,1,h_mf_old.nGrow());
754 
755  // Minimum allowed fractional grid spacing
756  Real gamma_m = myhalf;
757  pp.queryAdd("terrain_gamma_m", gamma_m);
758  Real z_H = Real(2.44)*h_m/(1-gamma_m); // Klemp2011 Eqn. 11
759 
760  // Populate h_mf at k>0 with h_s, solving in ordered 2D slices
761  for (int k = domlo_z+1; k <= domhi_z; k++) // skip terrain level
762  {
763  auto const& z_lev_h = z_levels_h.data();
764 
765  Real zz = z_lev_h[k];
766  Real zz_minus = z_lev_h[k-1];
767 
768  // Hybrid attenuation profile, Klemp2011 Eqn. 9
769  Real A;
770  Real foo = std::cos((PI/2)*(zz/z_H));
771  if(zz < z_H) { A = foo*foo*foo*foo*foo*foo; } // A controls rate of return to atm
772  else { A = 0; }
773  Real foo_minus = std::cos((PI/2)*(zz_minus/z_H));
774  Real A_minus;
775  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
776  else { A_minus = 0; }
777 
778  unsigned maxIter = 50; // M_k in paper
779  unsigned iter = 0;
780  Real threshold = gamma_m;
781  Real diff = bogus_large_value;
782  while (iter < maxIter && diff > threshold)
783  {
784 
785  diff = ParReduce(TypeList<ReduceOpMin>{}, TypeList<Real>{}, mf2d, IntVect(ngrow,ngrow,0),
786  [=] AMREX_GPU_DEVICE (int box_no, int i, int j, int) noexcept
787  -> GpuTuple<Real>
788  {
789  const auto & h_s = ma_h_s[box_no];
790  const auto & h_s_old = ma_h_s_old[box_no];
791 
792  Real beta_k = Real(0.2)*std::min(zz/(2*h_m),Real(1.0)); //smoothing coefficient (Eqn. 8)
793 
794  // Clip indices for ghost-cells
795  int ii = amrex::min(amrex::max(i,domlo_x),domhi_x);
796  int jj = amrex::min(amrex::max(j,domlo_y),domhi_y);
797 
798  if (iter == 0) {
799  h_s(i,j,k) = h_s_old(i,j,k-1) + beta_k*(h_s_old(ii+1,jj ,k-1)
800  + h_s_old(ii-1,jj ,k-1)
801  + h_s_old(ii ,jj+1,k-1)
802  + h_s_old(ii ,jj-1,k-1) - 4*h_s_old(ii,jj,k-1));
803  }
804  else {
805  h_s(i,j,k) = h_s_old(i,j,k ) + beta_k*(h_s_old(ii+1,jj ,k )
806  + h_s_old(ii-1,jj ,k )
807  + h_s_old(ii ,jj+1,k )
808  + h_s_old(ii ,jj-1,k ) - 4*h_s_old(ii,jj,k ));
809  }
810 
811  // Minimum vertical grid spacing condition (Klemp2011 Eqn. 7)
812  return { (zz + A * h_s(i,j,k) - (zz_minus + A_minus * h_s(i,j,k-1))) / (zz - zz_minus) };
813 
814  }); //ParReduce
815 
816  MultiFab::Copy(h_mf_old, h_mf,0,0,1,h_mf_old.nGrow());
817 
818  ParallelDescriptor::ReduceRealMin(diff);
819 
820  iter++;
821 
822  // Fill ghost points
823  h_mf_old.FillBoundary(geom.periodicity());
824 
825  } // while
826 
827  auto const& z_lev_d = z_levels_d.data();
828 
829  // Populate z_phys_nd by solving z_arr(i,j,k) = z + A*h_s(i,j,k)
830  for ( MFIter mfi(z_phys_nd, TilingIfNotGPU()); mfi.isValid(); ++mfi )
831  {
832  // Grown box with no z range
833  Box xybx = mfi.growntilebox(ngrow);
834  xybx.setRange(2,0);
835 
836  Array4<Real> const& h_s = h_mf_old.array(mfi);
837  Array4<Real> const& z_arr = z_phys_nd.array(mfi);
838 
839  ParallelFor(xybx, [=] AMREX_GPU_DEVICE (int i, int j, int) {
840 
841  // Location of nodes
842  Real z = z_lev_d[k];
843 
844  // STF model from p2164 of Klemp2011 (Eqn. 4)
845  z_arr(i,j,k) = z + A*h_s(i,j,k);
846 
847  // Fill below the bottom surface
848  if (k == 1) {
849  z_arr(i,j,k0-1) = two*z_arr(i,j,k0) - z_arr(i,j,k);
850  }
851  });
852  } // mfi
853  } // k
854 
855  Gpu::streamSynchronize();
856 
857  break;
858  } // case 1
859 
860  case 2: // Sullivan TF Method
861  {
862  int k0 = 0;
863 
864  for ( MFIter mfi(z_phys_nd, TilingIfNotGPU()); mfi.isValid(); ++mfi )
865  {
866  // Grown box with corrected ghost cells at top
867  Box gbx = mfi.growntilebox(ngrow);
868  gbx.setRange(2,domlo_z,domhi_z+1);
869 
870  Array4<Real> const& z_arr = z_phys_nd.array(mfi);
871  auto const& z_lev = z_levels_d.data();
872 
873  ParallelFor(gbx, [=] AMREX_GPU_DEVICE (int i, int j, int k)
874  {
875  // Vertical grid stretching
876  Real z = z_lev[k];
877 
878  int ii = amrex::max(amrex::min(i,imax),imin);
879  int jj = amrex::max(amrex::min(j,jmax),jmin);
880 
881  // Fill levels using model from Sullivan et. al. 2014
882  int omega = 3; //Used to adjust how rapidly grid lines level out. omega=1 is BTF!
883  z_arr(i,j,k) = z + static_cast<Real>(std::pow((one - (z/z_top)),omega) * z_arr(ii,jj,k0));
884 
885  // Fill lateral boundaries and below the bottom surface
886  if (k == k0) {
887  z_arr(i,j,k0 ) = z_arr(ii,jj,k0);
888  }
889  if (k == 1) {
890  z_arr(i,j,k0-1) = two*z_arr(ii,jj,k0) - z_arr(i,j,k);
891  }
892  });
893  } // mfi
894  break;
895  } // case 2
896 
897  case 3: // Debugging Test Method -- applies Sullivan TF starting at k = 1 so that domain does not change size
898  {
899  int k0 = 0;
900 
901  for ( MFIter mfi(z_phys_nd, TilingIfNotGPU()); mfi.isValid(); ++mfi )
902  {
903  // Grown box with corrected ghost cells at top
904  Box gbx = mfi.growntilebox(ngrow);
905  gbx.setRange(2,domlo_z,domhi_z+1);
906 
907  Array4<Real> const& z_arr = z_phys_nd.array(mfi);
908  auto const& z_lev = z_levels_d.data();
909 
910  ParallelFor(gbx, [=] AMREX_GPU_DEVICE (int i, int j, int k)
911  {
912  // Vertical grid stretching
913  Real z = z_lev[k];
914 
915  int ii = amrex::max(amrex::min(i,imax),imin);
916  int jj = amrex::max(amrex::min(j,jmax),jmin);
917 
918  // Fill values outside the lateral boundaries and below the bottom surface (necessary if init_type = "WRFInput")
919  if (k == k0+1) {
920  z_arr(i,j,k) = z + z_arr(ii,jj,k0);
921  } else {
922  // Fill levels using model from Sullivan et. al. 2014
923  int omega = 3; //Used to adjust how rapidly grid lines level out. omega=1 is BTF!
924  z_arr(i,j,k) = z + static_cast<Real>(std::pow((one - (z/z_top)),omega) * z_arr(ii,jj,k0));
925  }
926  });
927  gbx.setBig(2,0);
928  ParallelFor(gbx, [=] AMREX_GPU_DEVICE (int i, int j, int k)
929  {
930  z_arr(i,j,k ) = zero;
931  z_arr(i,j,k-1) = -z_arr(i,j,k+1);
932  });
933  } // mfi
934  break;
935  } // case 3
936  } //switch
937 }
constexpr amrex::Real bogus_large_value
Definition: ERF_Constants.H:17
ParmParse pp("prob")
constexpr amrex::Real one
Definition: ERF_NumericalConstants.H:30
constexpr amrex::Real zero
Definition: ERF_NumericalConstants.H:29
constexpr amrex::Real PI
Definition: ERF_NumericalConstants.H:39
void init_fine_terrain_grid(int lev, Geometry const &geom, MultiFab &z_phys_nd, Vector< Real > const &z_levels_h, FineTerrain fine_terrain, MultiFab const *z_phys_interp)
Definition: ERF_TerrainMetrics.cpp:439
@ omega
Definition: ERF_Morrison.H:55
constexpr int A
Definition: ERF_TwoStreamColumn.H:603

Referenced by make_terrain_fitted_coords().

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

◆ join_boxes_stacked_in_z()

BoxArray join_boxes_stacked_in_z ( const BoxArray &  ba)

The cells of a (cell-centered) BoxArray as boxes that are never stacked on each other.

Wherever a box of ba sits on the top face of another box of ba, the run of cells in z that the two belong to becomes a single box. Boxes stacked with different lateral extents are first cut at each other's lateral edges; the joined pieces are then merged in x and y and chopped to no more than the largest lateral size of the boxes they came from. Returns ba itself when no two boxes of ba are stacked.

62 {
63  AMREX_ALWAYS_ASSERT(ba.ixType().cellCentered());
64 
65  const int nboxes = static_cast<int>(ba.size());
66 
67  // Gather the boxes into stacks: link every box to the boxes that sit on its top face
68  std::vector<int> stack_of(nboxes);
69  std::iota(stack_of.begin(), stack_of.end(), 0);
70  auto find_stack = [&stack_of] (int i) {
71  while (stack_of[i] != i) {
72  stack_of[i] = stack_of[stack_of[i]];
73  i = stack_of[i];
74  }
75  return i;
76  };
77 
78  bool any_stacked = false;
79  for (int i = 0; i < nboxes; ++i) {
80  Box above(ba[i]);
81  above.setRange(2, ba[i].bigEnd(2)+1);
82  for (const auto& isect : ba.intersections(above)) {
83  stack_of[find_stack(isect.first)] = find_stack(i);
84  any_stacked = true;
85  }
86  }
87  if (!any_stacked) { return ba; }
88 
89  // std::map keeps the order, and so the joined boxes, the same on every rank
90  std::map<int, std::vector<Box>> stacks;
91  for (int i = 0; i < nboxes; ++i) {
92  stacks[find_stack(i)].push_back(ba[i]);
93  }
94 
95  BoxList bl_joined;
96  for (const auto& stack : stacks)
97  {
98  const std::vector<Box>& boxes = stack.second;
99  if (boxes.size() == 1) {
100  bl_joined.push_back(boxes[0]);
101  continue;
102  }
103 
104  // Cut the stack at every lateral edge of its boxes
105  std::array<std::vector<int>,2> cuts;
106  IntVect max_size(1);
107  for (const auto& b : boxes) {
108  for (int idim = 0; idim < 2; ++idim) {
109  cuts[idim].push_back(b.smallEnd(idim));
110  cuts[idim].push_back(b.bigEnd(idim)+1);
111  max_size[idim] = std::max(max_size[idim], b.length(idim));
112  }
113  }
114  for (auto& c : cuts) {
115  std::sort(c.begin(), c.end());
116  c.erase(std::unique(c.begin(), c.end()), c.end());
117  }
118  auto cut_index = [&cuts] (int idim, int coord) {
119  return static_cast<int>(std::lower_bound(cuts[idim].begin(), cuts[idim].end(), coord)
120  - cuts[idim].begin());
121  };
122 
123  // The z extents of the boxes over each piece, keyed by the piece's cut indices
124  std::map<std::pair<int,int>, std::vector<std::pair<int,int>>> zruns_of_piece;
125  for (const auto& b : boxes) {
126  for (int ix = cut_index(0, b.smallEnd(0)); ix < cut_index(0, b.bigEnd(0)+1); ++ix) {
127  for (int iy = cut_index(1, b.smallEnd(1)); iy < cut_index(1, b.bigEnd(1)+1); ++iy) {
128  zruns_of_piece[std::make_pair(ix,iy)].emplace_back(b.smallEnd(2), b.bigEnd(2));
129  }
130  }
131  }
132 
133  // Over each piece, join the extents that touch into one box per run of cells
134  BoxList bl_stack;
135  for (auto& piece : zruns_of_piece) {
136  const int ix = piece.first.first;
137  const int iy = piece.first.second;
138  auto& zruns = piece.second;
139  std::sort(zruns.begin(), zruns.end());
140  std::size_t n_first = 0;
141  for (std::size_t n = 1; n <= zruns.size(); ++n) {
142  if (n == zruns.size() || zruns[n].first != zruns[n-1].second+1) {
143  bl_stack.push_back(Box(IntVect(cuts[0][ix], cuts[1][iy], zruns[n_first].first),
144  IntVect(cuts[0][ix+1]-1, cuts[1][iy+1]-1, zruns[n-1].second)));
145  n_first = n;
146  }
147  }
148  }
149 
150  // Merging in x and y joins pieces with the same z extent, so it cannot stack them again
151  bl_stack.simplify();
152  max_size[2] = bl_stack.minimalBox().length(2);
153  bl_stack.maxSize(max_size);
154  bl_joined.join(bl_stack);
155  }
156 
157  return BoxArray(std::move(bl_joined));
158 }

Referenced by SurfaceLayer::define_pblh_columns().

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

986 {
987  const auto* dx = geom.CellSize();
988  Real dzInv = one/dx[2];
989 
990  // Domain valid box (z_nd is nodal)
991  const Box& domain = geom.Domain();
992  int domlo_z = domain.smallEnd(2);
993 
994  // The z-faces are always full when using terrain-fitted coordinates
995  az.setVal(1.0);
996 
997  //
998  // x-areas
999  //
1000 #ifdef _OPENMP
1001 #pragma omp parallel if (Gpu::notInLaunchRegion())
1002 #endif
1003  for ( MFIter mfi(ax, TilingIfNotGPU()); mfi.isValid(); ++mfi )
1004  {
1005  Box gbx = mfi.growntilebox(ax.nGrow());
1006  if (gbx.smallEnd(2) < domlo_z) {
1007  gbx.setSmall(2,domlo_z);
1008  }
1009 
1010  Array4<Real const> z_nd = z_phys_nd.const_array(mfi);
1011  Array4<Real > ax_arr = ax.array(mfi);
1012  ParallelFor(gbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
1013  ax_arr(i, j, k) = Real(.5) * dzInv * (
1014  z_nd(i,j,k+1) + z_nd(i,j+1,k+1) - z_nd(i,j,k) - z_nd(i,j+1,k));
1015  });
1016  }
1017 
1018  //
1019  // y-areas
1020  //
1021 #ifdef _OPENMP
1022 #pragma omp parallel if (Gpu::notInLaunchRegion())
1023 #endif
1024  for ( MFIter mfi(ay, TilingIfNotGPU()); mfi.isValid(); ++mfi )
1025  {
1026  Box gbx = mfi.growntilebox(ay.nGrow());
1027  if (gbx.smallEnd(2) < domlo_z) {
1028  gbx.setSmall(2,domlo_z);
1029  }
1030 
1031  Array4<Real const> z_nd = z_phys_nd.const_array(mfi);
1032  Array4<Real > ay_arr = ay.array(mfi);
1033  ParallelFor(gbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
1034  ay_arr(i, j, k) = Real(.5) * dzInv * (
1035  z_nd(i,j,k+1) + z_nd(i+1,j,k+1) - z_nd(i,j,k) - z_nd(i+1,j,k));
1036  });
1037  }
1038 
1039  ax.FillBoundary(geom.periodicity());
1040  ay.FillBoundary(geom.periodicity());
1041  az.FillBoundary(geom.periodicity());
1042 }

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

946 {
947  const auto *dx = geom.CellSize();
948  Real dzInv = one/dx[2];
949 
950  // Domain valid box (z_nd is nodal)
951  const Box& domain = geom.Domain();
952  int domlo_z = domain.smallEnd(2);
953 
954  // Number of ghost cells
955  int ngrow= detJ_cc.nGrow();
956 
957 #ifdef _OPENMP
958 #pragma omp parallel if (Gpu::notInLaunchRegion())
959 #endif
960  for ( MFIter mfi(detJ_cc, TilingIfNotGPU()); mfi.isValid(); ++mfi )
961  {
962  Box gbx = mfi.growntilebox(ngrow);
963  if (gbx.smallEnd(2) < domlo_z) {
964  gbx.setSmall(2,domlo_z);
965  }
966  Array4<Real const> z_nd = z_phys_nd.const_array(mfi);
967  Array4<Real > detJ = detJ_cc.array(mfi);
968  ParallelFor(gbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
969  detJ(i, j, k) = Real(.25) * dzInv * (
970  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)
971  -z_nd(i,j,k ) - z_nd(i+1,j,k ) - z_nd(i,j+1,k ) - z_nd(i+1,j+1,k ) );
972  });
973  }
974  detJ_cc.FillBoundary(geom.periodicity());
975 }

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,
FineTerrain  fine_terrain,
MultiFab const *  z_phys_interp 
)

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

238 {
239  const Box& domain = geom.Domain();
240 
241  int domlo_z = domain.smallEnd(2);
242  int domhi_z = domain.bigEnd(2) + 1;
243 
244  // Just in case ...
245  z_phys_nd.setDomainBndry(bogus_large_value,0,1,geom);
246 
247  // ****************************************************************************
248 
249  if (lev == 0) {
250  BoxArray ba(z_phys_nd.boxArray());
251  bool all_boxes_touch_bottom = true;
252  for (int i = 0; i < ba.size(); i++) {
253  if (ba[i].smallEnd(2) != domlo_z) {
254  all_boxes_touch_bottom = false;
255  }
256  }
257 
258  if (all_boxes_touch_bottom) {
259  init_which_terrain_grid(lev, geom, z_phys_nd, z_levels_h, fine_terrain, z_phys_interp);
260  } else {
261 
262  BoxArray ba_new(domain);
263  ChopGrids2D(ba_new, domain, ParallelDescriptor::NProcs());
264 
265  DistributionMapping dm_new(ba_new);
266  ba_new.surroundingNodes();
267 
268  MultiFab z_phys_nd_new(ba_new, dm_new, 1, z_phys_nd.nGrowVect());
269 
270  z_phys_nd_new.ParallelCopy(z_phys_nd,0,0,1,z_phys_nd.nGrowVect(),z_phys_nd.nGrowVect());
271 
272  init_which_terrain_grid(lev, geom, z_phys_nd_new, z_levels_h, fine_terrain, z_phys_interp);
273 
274  z_phys_nd.ParallelCopy(z_phys_nd_new,0,0,1,z_phys_nd.nGrowVect(),z_phys_nd.nGrowVect());
275  }
276  } else { // lev > 0
277  //
278  // BTF maps each column of a box between the box's lowest node and the domain top.
279  // A box that sits on another box of this level must continue the columns of the box
280  // below it: its own lowest node holds only the mesh interpolated from the coarse
281  // level, so starting from there would make the mesh above it follow the coarse
282  // terrain and depend on where the grids are split in z. As on level 0 we therefore
283  // build the mesh on boxes that hold whole columns and copy it back. The bottom box
284  // of a column, such as a refined patch aloft, still starts from its own lowest node.
285  //
286  ParmParse pp("erf");
287  int terrain_smoothing = 0;
288  pp.query("terrain_smoothing", terrain_smoothing);
289 
290  const ColumnGrids* cg = (terrain_smoothing == 0)
291  ? &column_grids(lev, geom, amrex::convert(z_phys_nd.boxArray(), IntVect::TheCellVector()))
292  : nullptr;
293 
294  if (cg == nullptr || !cg->stacked) {
295  init_which_terrain_grid(lev, geom, z_phys_nd, z_levels_h, fine_terrain, z_phys_interp);
296  } else {
297  const IntVect& ng = z_phys_nd.nGrowVect();
298  MultiFab z_phys_nd_col(cg->ba_col_nd, cg->dm_col, 1, ng);
299 
300  z_phys_nd_col.ParallelCopy(z_phys_nd,0,0,1,ng,ng);
301 
302  init_which_terrain_grid(lev, geom, z_phys_nd_col, z_levels_h, fine_terrain, z_phys_interp);
303 
304  // Copy the ghost nodes too, then give every node the value of a box that holds it
305  // as valid, rather than one a neighbouring box computed for its ghost node
306  z_phys_nd.ParallelCopy(z_phys_nd_col,0,0,1,ng,ng);
307  z_phys_nd.ParallelCopy(z_phys_nd_col,0,0,1,IntVect(0),ng);
308  }
309  }
310 
311  //
312  // Fill ghost layers and corners (including periodic) -- no matter what level
313  //
314  z_phys_nd.FillBoundary(geom.periodicity());
315 
316  if (phys_bc_type[Orientation(0,Orientation::low )] == ERF_BC::symmetry ||
317  phys_bc_type[Orientation(0,Orientation::high)] == ERF_BC::symmetry ||
318  phys_bc_type[Orientation(1,Orientation::low )] == ERF_BC::symmetry ||
319  phys_bc_type[Orientation(1,Orientation::high)] == ERF_BC::symmetry) {
320 
321  const auto& dom_lo = lbound(convert(domain,IntVect(1,1,1)));
322  const auto& dom_hi = ubound(convert(domain,IntVect(1,1,1)));
323 
324  //
325  // Mirror the whole ghost region, not just its first layer: the base state carries
326  // ComputeGhostCells+1 layers and the state stencils reach several cells past the
327  // boundary, so mirroring only the first would leave the rest of the ghost region
328  // holding whatever the terrain construction extrapolated into it.
329  //
330  const IntVect& ngv = z_phys_nd.nGrowVect();
331 
332  for (MFIter mfi(z_phys_nd,true); mfi.isValid(); ++mfi) {
333  //
334  // Whether this tile reaches a domain boundary is a question about its valid
335  // nodes, so ask it of the tilebox. The grown tilebox begins a ghost width short
336  // of the boundary and so never equals it -- asking it of the grown box is what
337  // kept this mirroring from ever running. The grown box is still what we sweep,
338  // so that the ghost rows in the other two directions are covered as well.
339  //
340  const Box& vbx = mfi.tilebox();
341  const Box& bx = mfi.growntilebox();
342  const Array4< Real> z_nd_arr = z_phys_nd.array(mfi);
343  if (phys_bc_type[Orientation(0,Orientation::low)] == ERF_BC::symmetry && vbx.smallEnd(0) == dom_lo.x) {
344  const int ng = ngv[0];
345  ParallelFor(makeSlab(bx,0,1), [=] AMREX_GPU_DEVICE (int , int j, int k)
346  {
347  for (int n = 1; n <= ng; ++n) {
348  z_nd_arr(dom_lo.x-n,j,k) = z_nd_arr(dom_lo.x+n,j,k);
349  }
350  });
351  }
352  if (phys_bc_type[Orientation(0,Orientation::high)] == ERF_BC::symmetry && vbx.bigEnd(0) == dom_hi.x) {
353  const int ng = ngv[0];
354  ParallelFor(makeSlab(bx,0,1), [=] AMREX_GPU_DEVICE (int , int j, int k)
355  {
356  for (int n = 1; n <= ng; ++n) {
357  z_nd_arr(dom_hi.x+n,j,k) = z_nd_arr(dom_hi.x-n,j,k);
358  }
359  });
360  }
361  if (phys_bc_type[Orientation(1,Orientation::low)] == ERF_BC::symmetry && vbx.smallEnd(1) == dom_lo.y) {
362  const int ng = ngv[1];
363  ParallelFor(makeSlab(bx,1,1), [=] AMREX_GPU_DEVICE (int i, int , int k)
364  {
365  for (int n = 1; n <= ng; ++n) {
366  z_nd_arr(i,dom_lo.y-n,k) = z_nd_arr(i,dom_lo.y+n,k);
367  }
368  });
369  }
370  if (phys_bc_type[Orientation(1,Orientation::high)] == ERF_BC::symmetry && vbx.bigEnd(1) == dom_hi.y) {
371  const int ng = ngv[1];
372  ParallelFor(makeSlab(bx,1,1), [=] AMREX_GPU_DEVICE (int i, int , int k)
373  {
374  for (int n = 1; n <= ng; ++n) {
375  z_nd_arr(i,dom_hi.y+n,k) = z_nd_arr(i,dom_hi.y-n,k);
376  }
377  });
378  }
379  }
380  }
381 
382  //********************************************************************************
383  // Populate domain boundary cells in z-direction
384  //********************************************************************************
385  int ngrow = z_phys_nd.nGrow();
386 
387  for ( MFIter mfi(z_phys_nd, TilingIfNotGPU()); mfi.isValid(); ++mfi )
388  {
389  // Only set values above top of domain if this box reaches that far
390  Box nd_bx = mfi.tilebox();
391 
392  // Note that domhi_z is already nodal in the z-direction
393  if (nd_bx.bigEnd(2) >= domhi_z) {
394  // Grown box with no z range
395  Box bx_zhi = mfi.growntilebox(ngrow);
396  bx_zhi.setSmall(2,domhi_z+1);
397  Array4<Real> const& z_arr = z_phys_nd.array(mfi);
398 
399  // Extrapolate top layer
400  ParallelFor(bx_zhi, [=] AMREX_GPU_DEVICE (int i, int j, int k) {
401  z_arr(i,j,k) = z_arr(i,j,domhi_z)
402  + (k-domhi_z) * (z_arr(i,j,domhi_z) - z_arr(i,j,domhi_z-1));
403  });
404  }
405  }
406 } // make_terrain_fitted_coords
void ChopGrids2D(BoxArray &ba, const Box &domain, int target_size)
Definition: ERF_ChopGrids.cpp:35
void init_which_terrain_grid(int lev, Geometry const &geom, MultiFab &z_phys_nd, Vector< Real > const &z_levels_h, FineTerrain fine_terrain, MultiFab const *z_phys_interp)
Definition: ERF_TerrainMetrics.cpp:567
@ ng
Definition: ERF_Morrison.H:50
real(c_double), private cg
Definition: ERF_module_mp_morr_two_moment.F90:203

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

1051 {
1052 #ifdef _OPENMP
1053 #pragma omp parallel if (Gpu::notInLaunchRegion())
1054 #endif
1055  for ( MFIter mfi(z_phys_cc, TilingIfNotGPU()); mfi.isValid(); ++mfi )
1056  {
1057  Box gbx = mfi.growntilebox();
1058  Array4<Real const> z_nd = z_phys_nd.const_array(mfi);
1059  Array4<Real > z_cc = z_phys_cc.array(mfi);
1060  ParallelFor(gbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
1061  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 )
1062  +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) );
1063  });
1064  }
1065  z_phys_cc.FillBoundary(geom.periodicity());
1066 }

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:

◆ which_fine_terrain()

FineTerrain which_fine_terrain ( )

Translate erf.amr_terrain_refinement into a FineTerrain.

413 {
414  ParmParse pp("erf");
415  std::string amr_terrain_refinement = "interpolate";
416  pp.query("amr_terrain_refinement", amr_terrain_refinement);
417 
418  if (amr_terrain_refinement == "interpolate") {
420  } else if (amr_terrain_refinement == "transform") {
421  return FineTerrain::Transform;
422  }
423 
424  Abort("erf.amr_terrain_refinement = " + amr_terrain_refinement +
425  " is not recognized; it must be \"interpolate\" or \"transform\"");
427 }
@ Interpolate
use the coordinates interpolated from the coarse level as-is

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: