ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
ProblemBase Class Reference

#include <ERF_ProbCommon.H>

Collaboration diagram for ProblemBase:

Public Member Functions

virtual ~ProblemBase ()=default
 
virtual void erf_init_dens_hse_dry (amrex::MultiFab &, std::unique_ptr< amrex::MultiFab > &, std::unique_ptr< amrex::MultiFab > &, amrex::Geometry const &, const amrex::Vector< amrex::Real > &, bool, bool)
 
virtual void erf_init_const_dens_hse (amrex::MultiFab &)
 
virtual void erf_init_const_dens_and_th_hse (amrex::MultiFab &, amrex::MultiFab &, amrex::MultiFab &, amrex::MultiFab &, amrex::MultiFab &, amrex::Real)
 
virtual void erf_init_const_dens_and_linear_th_hse (amrex::MultiFab &, amrex::MultiFab &, amrex::MultiFab &, amrex::MultiFab &, amrex::MultiFab &, amrex::Real, std::unique_ptr< amrex::MultiFab > &)
 
virtual void erf_init_dens_hse_moist (amrex::MultiFab &, std::unique_ptr< amrex::MultiFab > &, amrex::Geometry const &, const amrex::Vector< amrex::Real > &)
 
virtual void init_custom_pert (const amrex::Box &, amrex::Array4< amrex::Real const > const &, amrex::Array4< amrex::Real > const &, amrex::Array4< amrex::Real > const &, amrex::Array4< amrex::Real > const &, amrex::Array4< amrex::Real const > const &, amrex::Array4< amrex::Real const > const &, amrex::Array4< amrex::Real const > const &, amrex::GeometryData const &, amrex::Array4< amrex::Real const > const &, const SolverChoice &, const int)
 
virtual void init_custom_pert_vels (const amrex::Box &, const amrex::Box &, const amrex::Box &, amrex::Array4< amrex::Real > const &, amrex::Array4< amrex::Real > const &, amrex::Array4< amrex::Real > const &, amrex::Array4< amrex::Real const > const &, amrex::Array4< amrex::Real const > const &, amrex::GeometryData const &, amrex::Array4< amrex::Real const > const &, amrex::Array4< amrex::Real const > const &, const SolverChoice &, const int)
 
virtual void update_rhotheta_sources (const double &, amrex::MultiFab *src, const amrex::Geometry &, std::unique_ptr< amrex::MultiFab > &, const amrex::Vector< amrex::Real > &)
 
virtual void update_rhoqt_sources (const double &, amrex::MultiFab *qsrc, const amrex::Geometry &, std::unique_ptr< amrex::MultiFab > &, const amrex::Vector< amrex::Real > &)
 
virtual void update_w_subsidence (const double &, amrex::Vector< amrex::Real > &wbar, amrex::Gpu::DeviceVector< amrex::Real > &d_wbar, const amrex::MultiFab &, const amrex::Geometry &geom, std::unique_ptr< amrex::MultiFab > &, const amrex::Vector< amrex::Real > &)
 
virtual void update_geostrophic_profile (const double &, amrex::Vector< amrex::Real > &u_geos, amrex::Gpu::DeviceVector< amrex::Real > &d_u_geos, amrex::Vector< amrex::Real > &v_geos, amrex::Gpu::DeviceVector< amrex::Real > &d_v_geos, const amrex::Geometry &geom, std::unique_ptr< amrex::MultiFab > &, const amrex::Vector< amrex::Real > &)
 
void init_buildings_surface (const amrex::Geometry &geom, amrex::FArrayBox &buildings_fab, const double &time)
 
void init_terrain_surface (const amrex::Geometry &geom, amrex::FArrayBox &terrain_fab, const double &time)
 
virtual bool terrain_is_specified ()
 
void read_custom_terrain (const std::string &fname, const bool is_usgs, const amrex::Geometry &geom, amrex::FArrayBox &terrain_fab, const double &, bool require_domain_coverage=true, bool zero_outside_table=false)
 
virtual void init_custom_terrain (const amrex::Geometry &geom, amrex::FArrayBox &terrain_fab, const double &time)
 
virtual void erf_init_rayleigh (amrex::Vector< amrex::Vector< amrex::Real > > &, amrex::Geometry const &, std::unique_ptr< amrex::MultiFab > &, amrex::Real)
 
void set_zlevels_stag (const amrex::Vector< amrex::Vector< amrex::Real >> &zlevels_stag)
 

Static Public Member Functions

static void make_terrain_surface_periodic (const amrex::Geometry &geom, amrex::FArrayBox &terrain_fab)
 

Protected Member Functions

void init_base_parms (amrex::Real rho_0, amrex::Real T_0)
 
std::string name ()
 
amrex::Real phys_zlo (const amrex::GeometryData &geomdata, int lev) const
 Physical bottom of the domain at AMR level lev. More...
 
amrex::Real phys_zhi (const amrex::GeometryData &geomdata, int lev) const
 Physical top of the domain at AMR level lev. More...
 

Static Protected Member Functions

static bool use_physical_z ()
 True when heights must come from the vertical levels or z_phys, not from the geometry. More...
 

Protected Attributes

ProbParmDefaults base_parms
 
amrex::Vector< amrex::Vector< amrex::Real > > m_zlevels_stag
 Staggered vertical levels of AMR level lev (set by set_zlevels_stag) More...
 

Detailed Description

Class to hold problem-specific routines

Constructor & Destructor Documentation

◆ ~ProblemBase()

virtual ProblemBase::~ProblemBase ( )
virtualdefault

Virtual destructor to avoid data leakage with derived class

Member Function Documentation

◆ erf_init_const_dens_and_linear_th_hse()

virtual void ProblemBase::erf_init_const_dens_and_linear_th_hse ( amrex::MultiFab &  ,
amrex::MultiFab &  ,
amrex::MultiFab &  ,
amrex::MultiFab &  ,
amrex::MultiFab &  ,
amrex::Real  ,
std::unique_ptr< amrex::MultiFab > &   
)
inlinevirtual
90  {
91  amrex::Error("Should never call this version of erf_init_const_dens_and_linear_th_hse for "+name()+" problem");
92  }
std::string name()
Definition: ERF_ProbCommon.H:1234
Here is the call graph for this function:

◆ erf_init_const_dens_and_th_hse()

virtual void ProblemBase::erf_init_const_dens_and_th_hse ( amrex::MultiFab &  ,
amrex::MultiFab &  ,
amrex::MultiFab &  ,
amrex::MultiFab &  ,
amrex::MultiFab &  ,
amrex::Real   
)
inlinevirtual
81  {
82  amrex::Error("Should never call this version of erf_init_const_dens_and_th_hse for "+name()+" problem");
83  }
Here is the call graph for this function:

◆ erf_init_const_dens_hse()

virtual void ProblemBase::erf_init_const_dens_hse ( amrex::MultiFab &  )
inlinevirtual
73  {
74  amrex::Error("Should never call this version of erf_init_const_dens_hse for "+name()+" problem");
75  }
Here is the call graph for this function:

◆ erf_init_dens_hse_dry()

virtual void ProblemBase::erf_init_dens_hse_dry ( amrex::MultiFab &  ,
std::unique_ptr< amrex::MultiFab > &  ,
std::unique_ptr< amrex::MultiFab > &  ,
amrex::Geometry const &  ,
const amrex::Vector< amrex::Real > &  ,
bool  ,
bool   
)
inlinevirtual

Function to initialize the hydrostatic reference density

Parameters
[out]rho_hsehydrostatic reference density
[in]z_phys_ndheight coordinate at nodes
[in]z_phys_ccheight coordinate at cell centers
[in]geomcontainer for geometric information
63  {
64  amrex::Print() << "Hydrostatically balanced density was NOT set"
65  << " -- an appropriate init_type should probably have been specified"
66  << " (e.g., input_sounding, WRFInput, or Metgrid)"
67  << std::endl;
68  amrex::Error("Should never call this version of erf_init_dens_hse_dry for "+name()+" problem");
69  }
Here is the call graph for this function:

◆ erf_init_dens_hse_moist()

virtual void ProblemBase::erf_init_dens_hse_moist ( amrex::MultiFab &  ,
std::unique_ptr< amrex::MultiFab > &  ,
amrex::Geometry const &  ,
const amrex::Vector< amrex::Real > &   
)
inlinevirtual
99  {
100 
101  }

◆ erf_init_rayleigh()

virtual void ProblemBase::erf_init_rayleigh ( amrex::Vector< amrex::Vector< amrex::Real > > &  ,
amrex::Geometry const &  ,
std::unique_ptr< amrex::MultiFab > &  ,
amrex::Real   
)
inlinevirtual

Function to define the quantities needed to impose Rayleigh damping

Parameters
[out]rayleigh_ptrs= {strength of Rayleigh damping, reference values for xvel/yvel/zvel/theta used to define Rayleigh damping}
[in]geomcontainer for geometric information
[in]z_phys_ccheight coordinate at cell centers
1217  {
1218  // Default which does no harm
1219  }

◆ init_base_parms()

void ProblemBase::init_base_parms ( amrex::Real  rho_0,
amrex::Real  T_0 
)
inlineprotected

Function to update default base parameters, currently only used for init_type == InitType::Uniform

1229  {
1231  base_parms.T_0 = T_0;
1232  }
Real rho_0
Definition: ERF_InitCustomPert_ABL.H:18
Real T_0
Definition: ERF_InitCustomPert_ABL.H:19
ProbParmDefaults base_parms
Definition: ERF_ProbCommon.H:1223
amrex::Real T_0
Definition: ERF_ProbCommon.H:28
amrex::Real rho_0
Definition: ERF_ProbCommon.H:27

◆ init_buildings_surface()

void ProblemBase::init_buildings_surface ( const amrex::Geometry &  geom,
amrex::FArrayBox &  buildings_fab,
const double &  time 
)
inline

Function to perform custom initialization of buildings

Parameters
[in]geomcontainer for geometric information
[out]z_phys_ndheight coordinate at nodes
[in]timecurrent time
331  {
332  // Check if a valid text file exists for the buildings
333  std::string fname;
334  amrex::ParmParse pp("erf");
335  auto valid_fname = pp.queryAdd("buildings_file_name",fname);
336 
337  if (valid_fname) {
338  // Buildings may occupy only a local patch of the physical domain.
339  read_custom_terrain(fname,false,geom,buildings_fab,time,
340  /*require_domain_coverage=*/false,
341  /*zero_outside_table=*/true);
342  } else {
343  init_my_custom_terrain(geom, buildings_fab, time);
344  }
345 
346  make_terrain_surface_periodic(geom, buildings_fab);
347  }
ParmParse pp("prob")
void init_my_custom_terrain(const amrex::Geometry &geom, amrex::FArrayBox &terrain_fab, const double &time)
static void make_terrain_surface_periodic(const amrex::Geometry &geom, amrex::FArrayBox &terrain_fab)
Definition: ERF_ProbCommon.H:407
void read_custom_terrain(const std::string &fname, const bool is_usgs, const amrex::Geometry &geom, amrex::FArrayBox &terrain_fab, const double &, bool require_domain_coverage=true, bool zero_outside_table=false)
Definition: ERF_ProbCommon.H:934
Here is the call graph for this function:

◆ init_custom_pert()

virtual void ProblemBase::init_custom_pert ( const amrex::Box &  ,
amrex::Array4< amrex::Real const > const &  ,
amrex::Array4< amrex::Real > const &  ,
amrex::Array4< amrex::Real > const &  ,
amrex::Array4< amrex::Real > const &  ,
amrex::Array4< amrex::Real const > const &  ,
amrex::Array4< amrex::Real const > const &  ,
amrex::Array4< amrex::Real const > const &  ,
amrex::GeometryData const &  ,
amrex::Array4< amrex::Real const > const &  ,
const SolverChoice &  ,
const int   
)
inlinevirtual

Function to perform custom initialization of a test problem

Parameters
[in]bxcell-centered box on which to initialize scalars
[out]statecell-centered variables to be filled in this routine
[out]r_hsehydrostatic reference density
[out]p_hsehydrostatic reference pressure
[in]z_ndheight coordinate at nodes
[in]z_ccheight coordinate at cell centers
[in]z_sfc_ndheight of the ground at the nodes of the bottom face of each column, at k = 0, over the planar footprint of bx
[in]mf_mmap factor on cell centers
[in]mf_umap factor on x-faces
[in]mf_vmap factor on y-faces
[in]scSolverChoice structure that carries parameters
[in]levAMR level
132  {
133  amrex::Print() << "No perturbation to background fields supplied for "
134  << name() << " problem" << std::endl;
135  }
Here is the call graph for this function:

◆ init_custom_pert_vels()

virtual void ProblemBase::init_custom_pert_vels ( const amrex::Box &  ,
const amrex::Box &  ,
const amrex::Box &  ,
amrex::Array4< amrex::Real > const &  ,
amrex::Array4< amrex::Real > const &  ,
amrex::Array4< amrex::Real > const &  ,
amrex::Array4< amrex::Real const > const &  ,
amrex::Array4< amrex::Real const > const &  ,
amrex::GeometryData const &  ,
amrex::Array4< amrex::Real const > const &  ,
amrex::Array4< amrex::Real const > const &  ,
const SolverChoice &  ,
const int   
)
inlinevirtual

Function to perform custom initialization of the velocities

Parameters
[in]xbxbox on which to initialize x_vel_pert
[in]ybxbox on which to initialize y_vel_pert
[in]zbxbox on which to initialize z_vel_pert
[out]x_vel_pertx-component of velocity perturbation to be filled in this routine
[out]y_vel_perty-component of velocity perturbation to be filled in this routine
[out]z_vel_pertz-component of velocity perturbation to be filled in this routine
[in]z_ndheight coordinate at nodes
[in]z_sfc_ndheight of the ground at the nodes of the bottom face of each column, at k = 0 (see init_custom_pert)
[in]mf_mmap factor on cell centers
[in]mf_umap factor on x-faces
[in]mf_vmap factor on y-faces
[in]scSolverChoice structure that carries parameters
[in]levAMR level
168  {
169  amrex::Print() << "No perturbation velocities supplied for " << name() << " problem" << std::endl;
170  }
Here is the call graph for this function:

◆ init_custom_terrain()

virtual void ProblemBase::init_custom_terrain ( const amrex::Geometry &  geom,
amrex::FArrayBox &  terrain_fab,
const double &  time 
)
inlinevirtual

Function to perform custom initialization of terrain

Note: Terrain functionality can also be used to provide grid stretching.

Parameters
[in]geomcontainer for geometric information
[out]terrain_mfheight coordinate at nodes
[in]timecurrent time
1177  {
1178  std::string custom_terrain_type = "None";
1179  amrex::ParmParse pp_prob("prob"); pp_prob.queryAdd("custom_terrain_type",custom_terrain_type);
1180 
1181  if (custom_terrain_type != "None")
1182  {
1183  amrex::Print() << "Calling custom terrain initialization" << std::endl;
1184  init_my_custom_terrain(geom,terrain_fab,time);
1185 
1186  } else {
1187  amrex::Print() << "Initializing flat terrain" << std::endl;
1188  // A flat surface at the bottom of the domain
1189  terrain_fab.template setVal<amrex::RunOn::Device>(geom.ProbLo(2));
1190 
1191  if (SolverChoice::mesh_type == MeshType::VariableDz) {
1192  SolverChoice::set_mesh_type(MeshType::StretchedDz);
1193  amrex::Print() << "Resetting mesh type to StretchedDz" << std::endl;
1194  }
1195  }
1196  }
ParmParse pp_prob("prob")
static MeshType mesh_type
Vertical mesh representation.
Definition: ERF_DataStruct.H:2604
static void set_mesh_type(MeshType new_mesh_type)
Override the globally selected mesh type.
Definition: ERF_DataStruct.H:2611
Here is the call graph for this function:

◆ init_terrain_surface()

void ProblemBase::init_terrain_surface ( const amrex::Geometry &  geom,
amrex::FArrayBox &  terrain_fab,
const double &  time 
)
inline

Function to perform custom initialization of terrain

Parameters
[in]geomcontainer for geometric information
[out]z_phys_ndheight coordinate at nodes
[in]timecurrent time
361  {
362  amrex::ParmParse pp("erf");
363 
364  // NetCDF terrain file takes priority
365  std::string fname_nc;
366  auto valid_fname_nc = pp.query("terrain_file_name_nc", fname_nc);
367 
368  std::string fname, fname_usgs;
369  auto valid_fname = pp.query("terrain_file_name", fname);
370  auto valid_fname_USGS = pp.query("terrain_file_name_USGS", fname_usgs);
371 
372  if (valid_fname_nc) {
373 #ifdef ERF_USE_NETCDF
374  read_terrain_netcdf(fname_nc, geom, terrain_fab, time);
375 #else
376  amrex::Abort("ERF must be compiled with NetCDF support to use terrain_file_name_nc");
377 #endif
378  } else if (valid_fname) {
379  read_custom_terrain(fname, false, geom, terrain_fab, time);
380 
381  } else if (valid_fname_USGS) {
382  read_custom_terrain(fname_usgs, true, geom, terrain_fab, time);
383 
384  } else {
385  init_my_custom_terrain (geom, terrain_fab, time);
386  }
387 
388  make_terrain_surface_periodic(geom, terrain_fab);
389  }

Referenced by fill_terrain_surface_slab().

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

◆ make_terrain_surface_periodic()

static void ProblemBase::make_terrain_surface_periodic ( const amrex::Geometry &  geom,
amrex::FArrayBox &  terrain_fab 
)
inlinestatic

Give every node in a periodic direction the height of its periodic image inside the domain. The nodes on the high side of a periodic domain are the same nodes as those on the low side, but none of the sources above knows that: the file readers clip a node outside the table to its edge (and look every node up 1e-3 past where it is), and an analytic surface is periodic only to round-off. Left as they are the two copies of a node differ, and so do the metrics of the faces on either side of it – which means the two grids that share a face update it differently, the mass flux through it is no longer the same on both sides, and which copy a fill keeps depends on how the grids are distributed. The low side is the one we keep.

terrain_fab must hold every node of the domain in a periodic direction, since that is where the images are read from. A surface that is far from periodic is still made periodic here, but we say so, since the cells next to the high side then see a step.

409  {
410  const bool per_x = geom.isPeriodic(0);
411  const bool per_y = geom.isPeriodic(1);
412  if (!per_x && !per_y) { return; }
413 
414  const amrex::Box& domain = geom.Domain();
415  const int ilo = domain.smallEnd(0); const int nx = domain.length(0);
416  const int jlo = domain.smallEnd(1); const int ny = domain.length(1);
417 
418  const amrex::Box bx = terrain_fab.box();
419  const auto blo = amrex::lbound(bx);
420  const auto bhi = amrex::ubound(bx);
421  amrex::Array4<amrex::Real> const& z_arr = terrain_fab.array();
422 
424  (!per_x || (blo.x <= ilo && bhi.x >= ilo+nx-1)) &&
425  (!per_y || (blo.y <= jlo && bhi.y >= jlo+ny-1)),
426  "make_terrain_surface_periodic: the terrain FArrayBox must hold every node of the domain in a periodic direction");
427 
428  // How far the surface is from periodic: the largest difference between a node on the
429  // high side of the domain and its image on the low side (ghost nodes are left out:
430  // they hold whatever the source clipped them to)
431  amrex::Box hi_bx = bx & amrex::surroundingNodes(domain);
432  hi_bx.setSmall(2, bx.smallEnd(2)); hi_bx.setBig(2, bx.smallEnd(2));
433  if (!hi_bx.isEmpty()) {
434  amrex::ReduceOps<amrex::ReduceOpMax> reduce_op;
435  amrex::ReduceData<amrex::Real> reduce_data(reduce_op);
436  reduce_op.eval(hi_bx, reduce_data,
437  [=] AMREX_GPU_DEVICE (int i, int j, int k) -> amrex::GpuTuple<amrex::Real>
438  {
439  int ii = (per_x) ? ilo + ((i-ilo) % nx + nx) % nx : i;
440  int jj = (per_y) ? jlo + ((j-jlo) % ny + ny) % ny : j;
441  return { (ii != i || jj != j) ? amrex::Math::abs(z_arr(i,j,k) - z_arr(ii,jj,k))
442  : amrex::Real(0.0) };
443  });
444  // Every rank fills its own copy of the whole surface, so there is nothing to reduce
445  // across ranks. Warn once: with a moving mesh we are called several times a step.
446  const amrex::Real max_diff = amrex::get<0>(reduce_data.value());
447  static bool warned = false;
448  if (!warned && max_diff > amrex::Real(0.1) * geom.CellSize(2)) {
449  warned = true;
450  amrex::Print() << "WARNING: the terrain surface is not periodic in a periodic direction: "
451  << "a node on the high side of the domain differs from its image on the low "
452  << "side by up to " << max_diff << " m. The low side is used for both, so the "
453  << "cells next to the high side see a step in the surface.\n";
454  }
455  }
456 
457  // We only write nodes outside [lo, lo+n) in a periodic direction and only read
458  // nodes inside it, so no node is both read and written here
459  amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k)
460  {
461  int ii = (per_x) ? ilo + ((i-ilo) % nx + nx) % nx : i;
462  int jj = (per_y) ? jlo + ((j-jlo) % ny + ny) % ny : j;
463  if ((ii != i || jj != j) &&
464  ii >= blo.x && ii <= bhi.x && jj >= blo.y && jj <= bhi.y) {
465  z_arr(i,j,k) = z_arr(ii,jj,k);
466  }
467  });
468  }
const int nx
Definition: ERF_InitCustomPertVels_CloudChamber.H:14
const int ny
Definition: ERF_InitCustomPertVels_CloudChamber.H:15
AMREX_ALWAYS_ASSERT_WITH_MESSAGE(axis >=0 &&axis< AMREX_SPACEDIM, "prob.axis must be 0, 1 or 2")
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);})
amrex::Real Real
Definition: ERF_ShocInterface.H:19

Referenced by init_buildings_surface(), and init_terrain_surface().

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

◆ name()

std::string ProblemBase::name ( )
inlineprotected
1234  {
1235  std::string prob_name;
1236  amrex::ParmParse pp("erf");
1237  pp.get("prob_name",prob_name); return prob_name;
1238  }

Referenced by erf_init_const_dens_and_linear_th_hse(), erf_init_const_dens_and_th_hse(), erf_init_const_dens_hse(), erf_init_dens_hse_dry(), init_custom_pert(), init_custom_pert_vels(), update_geostrophic_profile(), update_rhoqt_sources(), update_rhotheta_sources(), and update_w_subsidence().

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

◆ phys_zhi()

amrex::Real ProblemBase::phys_zhi ( const amrex::GeometryData &  geomdata,
int  lev 
) const
inlineprotected

Physical top of the domain at AMR level lev.

1260  {
1261  return use_physical_z() ? m_zlevels_stag[lev].back() : geomdata.ProbHi(2);
1262  }
static bool use_physical_z()
True when heights must come from the vertical levels or z_phys, not from the geometry.
Definition: ERF_ProbCommon.H:1252
amrex::Vector< amrex::Vector< amrex::Real > > m_zlevels_stag
Staggered vertical levels of AMR level lev (set by set_zlevels_stag)
Definition: ERF_ProbCommon.H:1265
Here is the call graph for this function:

◆ phys_zlo()

amrex::Real ProblemBase::phys_zlo ( const amrex::GeometryData &  geomdata,
int  lev 
) const
inlineprotected

Physical bottom of the domain at AMR level lev.

1255  {
1256  return use_physical_z() ? m_zlevels_stag[lev].front() : geomdata.ProbLo(2);
1257  }
Here is the call graph for this function:

◆ read_custom_terrain()

void ProblemBase::read_custom_terrain ( const std::string &  fname,
const bool  is_usgs,
const amrex::Geometry &  geom,
amrex::FArrayBox &  terrain_fab,
const double &  ,
bool  require_domain_coverage = true,
bool  zero_outside_table = false 
)
inline
941  {
942  // Read, checked and broadcast once per file; other levels, regrids, the EB build and
943  // the station slabs reuse the table
944  const auto& table = erf_terrain_file::ReadHeightTable(fname, is_usgs,
945  geom.ProbLoArray(), geom.ProbHiArray(),
946  require_domain_coverage);
947  const int nx = table.nx;
948  const int ny = table.ny;
949  const amrex::Vector<amrex::Real>& m_xterrain = table.x;
950  const amrex::Vector<amrex::Real>& m_yterrain = table.y;
951  const amrex::Vector<amrex::Real>& m_zterrain = table.z;
952 
953  const int nz = nx * ny;
954  const int nx_vals = is_usgs ? (nx * ny) : nx;
955 
956  // Copy data to the GPU
957  amrex::Gpu::DeviceVector<amrex::Real> d_xterrain(nx_vals),d_yterrain(ny),d_zterrain(nz);
958  amrex::Gpu::copy(amrex::Gpu::hostToDevice, m_xterrain.begin(), m_xterrain.end(), d_xterrain.begin());
959  amrex::Gpu::copy(amrex::Gpu::hostToDevice, m_yterrain.begin(), m_yterrain.end(), d_yterrain.begin());
960  amrex::Gpu::copy(amrex::Gpu::hostToDevice, m_zterrain.begin(), m_zterrain.end(), d_zterrain.begin());
961 
962  amrex::Real* d_xt = d_xterrain.data();
963  amrex::Real* d_yt = d_yterrain.data();
964  amrex::Real* d_zt = d_zterrain.data();
965 
966  auto dx = geom.CellSizeArray();
967  auto ProbLoArr = geom.ProbLoArray();
968 
969  int ilo = geom.Domain().smallEnd(0);
970  int jlo = geom.Domain().smallEnd(1);
971  int klo = geom.Domain().smallEnd(2);
972  int ihi = geom.Domain().bigEnd(0) + 1;
973  int jhi = geom.Domain().bigEnd(1) + 1;
974 
975  amrex::Box zbx = terrain_fab.box();
976  amrex::Array4<amrex::Real> const& z_arr = terrain_fab.array();
977 
978  amrex::ParallelFor(zbx, [=] AMREX_GPU_DEVICE (int i, int j, int /*k*/)
979  {
980  // Clip indices for ghost cells
981  int ii = amrex::min(amrex::max(i,ilo),ihi);
982  int jj = amrex::min(amrex::max(j,jlo),jhi);
983 
984  // Location of nodes
985  amrex::Real x_node = ProbLoArr[0] + ii * dx[0];
986  amrex::Real y_node = ProbLoArr[1] + jj * dx[1];
987  amrex::Real x = x_node + amrex::Real(1e-3);
988  amrex::Real y = y_node + amrex::Real(1e-3);
989 
990  // A local building table describes no buildings beyond its footprint.
991  // Use the unshifted node position so a node on the last table point
992  // remains inside, despite the interpolation offset above.
993  if (zero_outside_table) {
994  const bool out_x = nx > 1 && (x_node < d_xt[0] || x_node > d_xt[nx-1]);
995  const bool out_y = ny > 1 && (y_node < d_yt[0] || y_node > d_yt[ny-1]);
996  if (out_x || out_y) {
997  z_arr(i,j,klo) = amrex::Real(0.0);
998  return;
999  }
1000  }
1001 
1002  int ind11, ind12, ind21, ind22;
1003  amrex::Real x1, x2, y1, y2;
1004 
1005  int iindex_terrain=-1;
1006  int jindex_terrain=-1;
1007  // *******************************************************************
1008  // NOTE: usgs-format is contiguous in x
1009  // *******************************************************************
1010  if (is_usgs) {
1011 
1012  // NOTE: the searches below must distinguish "the node is below the first
1013  // table value" (bracket index -1) from "no table value exceeds the
1014  // node" (search never triggered), so we can not use the initial
1015  // value of -1 as the not-found flag
1016  bool jfound = false;
1017  for (int it = 0; it < ny && !jfound; it++) {
1018  if (d_yt[it] > y) {
1019  jindex_terrain = it-1; jfound = true;
1020  }
1021  }
1022  if (!jfound) {
1023  jindex_terrain = ny-1;
1024  }
1025  // Keep the 2x2 stencil inside the table
1026  jindex_terrain = amrex::min(amrex::max(jindex_terrain,0), ny-2);
1027 
1028  int gstart = (jindex_terrain )*nx;
1029  int gend = (jindex_terrain+1)*nx-1;
1030  bool ifound = false;
1031  for (int it = gstart; it <= gend && !ifound; it++) {
1032  if (d_xt[it] > x) {
1033  iindex_terrain = it-gstart-1; ifound = true;
1034  }
1035  }
1036  if (!ifound) {
1037  iindex_terrain = nx-1;
1038  }
1039  // Keep the 2x2 stencil inside the table
1040  iindex_terrain = amrex::min(amrex::max(iindex_terrain,0), nx-2);
1041 
1042  // Define the four values to interpolate between
1043  ind11 = jindex_terrain*nx + iindex_terrain; // (x1,y1)
1044  ind12 = ind11+nx; // (x1,y2)
1045  ind21 = ind11+1; // (x2,y1)
1046  ind22 = ind12+1; // (x2,y2)
1047 
1048  x1 = d_xt[ind11];
1049  x2 = d_xt[ind21];
1050  y1 = d_yt[jindex_terrain];
1051  y2 = d_yt[jindex_terrain+1];
1052 
1053  amrex::Real denom = (x2-x1)*(y2-y1);
1054 
1055  if (denom == amrex::Real(0.0)) {
1056  // Degenerate cell (repeated x or y in the table) -- use the corner value
1057  z_arr(i,j,klo) = d_zt[ind11];
1058  } else {
1059  amrex::Real w_11 = (x2-x)*(y2-y)/denom; // (x1,y1)
1060  amrex::Real w_12 = (x2-x)*(y-y1)/denom; // (x1,y2)
1061  amrex::Real w_21 = (x-x1)*(y2-y)/denom; // (x2,y1)
1062  amrex::Real w_22 = (x-x1)*(y-y1)/denom; // (x2,y2)
1063 
1064  // Do bilinear interpolation
1065  z_arr(i,j,klo) = w_11*d_zt[ind11] + w_12*d_zt[ind12] + w_21*d_zt[ind21] + w_22*d_zt[ind22];
1066  }
1067 
1068  } else {
1069 
1070  bool jfound = false;
1071  for (int it = 0; it < ny && !jfound; it++) {
1072  if (d_yt[it] > y) {
1073  jindex_terrain = it-1; jfound = true;
1074  }
1075  }
1076  if (!jfound) {
1077  // No table value exceeds y -- fall back to the last row
1078  jindex_terrain = ny-1;
1079  }
1080  jindex_terrain = amrex::max(jindex_terrain,0);
1081 
1082  bool ifound = false;
1083  for (int it = 0; it < nx && !ifound; it++) {
1084  if (d_xt[it] > x) {
1085  iindex_terrain = it-1; ifound = true;
1086  }
1087  }
1088  if (!ifound) {
1089  // No table value exceeds x -- fall back to the last column
1090  iindex_terrain = nx-1;
1091  }
1092  iindex_terrain = amrex::max(iindex_terrain,0);
1093 
1094  // NOTE: these must be clamped since the fallbacks above can leave
1095  // iindex_terrain = nx-1 (jindex_terrain = ny-1)
1096  int ip1 = amrex::min(iindex_terrain+1,nx-1);
1097  int jp1 = amrex::min(jindex_terrain+1,ny-1);
1098 
1099  // Define the four values to interpolate between
1100  x1 = d_xt[iindex_terrain];
1101  x2 = d_xt[ip1];
1102  y1 = d_yt[jindex_terrain];
1103  y2 = d_yt[jp1];
1104 
1105 #if 1
1106  // *******************************************************************
1107  // NOTE: this format is contiguous in y to match the AMR-Wind read
1108  // *******************************************************************
1109  ind11 = iindex_terrain * ny + jindex_terrain; // (x1,y1)
1110  ind21 = ip1 * ny + jindex_terrain; // (x2,y1)
1111 
1112  ind12 = iindex_terrain * ny + jp1; // (x1,y2)
1113  ind22 = ip1 * ny + jp1; // (x2,y2)
1114 #else
1115  // *******************************************************************
1116  // NOTE: this format is contiguous in x as an alternative
1117  // *******************************************************************
1118 
1119  ind11 = jindex_terrain * nx + iindex_terrain; // (x1,y1)
1120  ind12 = jp1 * nx + iindex_terrain; // (x1,y2)
1121 
1122  ind21 = jindex_terrain * nx + ip1; // (x2,y1)
1123  ind22 = jp1 * nx + ip1; // (x2,y2)
1124 #endif
1125 
1126  // Only interpolate in a direction if we have a non-degenerate interval there
1127  bool interp_x = (ip1 != iindex_terrain) && (x2 != x1);
1128  bool interp_y = (jp1 != jindex_terrain) && (y2 != y1);
1129 
1130  if (!interp_x && !interp_y)
1131  {
1132  z_arr(i,j,klo) = d_zt[ind11];
1133  }
1134  else if (interp_x && !interp_y)
1135  {
1136  amrex::Real w_11 = (x2-x); // (x1,y1)
1137  amrex::Real w_21 = (x-x1); // (x2,y1)
1138  amrex::Real denom = (x2-x1);
1139  z_arr(i,j,klo) = (w_11*d_zt[ind11] + w_21*d_zt[ind21])/denom;
1140  }
1141  else if (!interp_x && interp_y)
1142  {
1143  amrex::Real w_11 = (y2-y); // (x1,y1)
1144  amrex::Real w_12 = (y-y1); // (x1,y2)
1145  amrex::Real denom = (y2-y1);
1146  z_arr(i,j,klo) = (w_11*d_zt[ind11] + w_12*d_zt[ind12])/denom;
1147  }
1148  else
1149  {
1150  amrex::Real w_11 = (x2-x)*(y2-y); // (x1,y1)
1151  amrex::Real w_21 = (x-x1)*(y2-y); // (x2,y1)
1152  amrex::Real w_12 = (x2-x)*(y-y1); // (x1,y2)
1153  amrex::Real w_22 = (x-x1)*(y-y1); // (x2,y2)
1154  amrex::Real denom = (x2-x1)*(y2-y1);
1155  z_arr(i,j,klo) = (w_11*d_zt[ind11] + w_12*d_zt[ind12] + w_21*d_zt[ind21] + w_22*d_zt[ind22]) / denom;
1156  }
1157  } // usgs?
1158  });
1159 
1160  // The kernel reads the device vectors above: finish it before they are freed
1161  amrex::Gpu::streamSynchronize();
1162  }
const Real dx
Definition: ERF_InitCustomPert_ABL.H:44
const int klo
Definition: ERF_InitCustomPert_KE.H:9
const HeightTable & ReadHeightTable(const std::string &fname, bool is_usgs, const GpuArray< Real, AMREX_SPACEDIM > &prob_lo, const GpuArray< Real, AMREX_SPACEDIM > &prob_hi, bool require_domain_coverage)
Definition: ERF_TerrainFileChecks.cpp:331

Referenced by init_buildings_surface(), and init_terrain_surface().

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

◆ set_zlevels_stag()

void ProblemBase::set_zlevels_stag ( const amrex::Vector< amrex::Vector< amrex::Real >> &  zlevels_stag)
inline

Store the staggered vertical levels of every AMR level. On a mesh other than ConstantDz, geometry.prob_lo[2], prob_hi[2] and CellSize(2) only bound and step the computational coordinate; the physical heights are these levels (and z_phys_cc/nd).

1246  {
1247  m_zlevels_stag = zlevels_stag;
1248  }

◆ terrain_is_specified()

virtual bool ProblemBase::terrain_is_specified ( )
inlinevirtual

Function to report whether the user has actually specified a terrain surface, i.e. whether init_terrain_surface will do anything other than return flat terrain.

This mirrors the source precedence in init_terrain_surface, and is deliberately kept next to it so that the two cannot drift apart. A problem that overrides the terrain unconditionally, rather than keying off prob.custom_terrain_type, should override this to return true.

481  {
482  amrex::ParmParse pp("erf");
483  std::string fname;
484  if (pp.query("terrain_file_name_nc", fname) ||
485  pp.query("terrain_file_name", fname) ||
486  pp.query("terrain_file_name_USGS", fname)) {
487  return true;
488  }
489 
490  std::string custom_terrain_type = "None";
491  amrex::ParmParse pp_prob("prob");
492  pp_prob.query("custom_terrain_type", custom_terrain_type);
493  return (custom_terrain_type != "None");
494  }
Here is the call graph for this function:

◆ update_geostrophic_profile()

virtual void ProblemBase::update_geostrophic_profile ( const double &  ,
amrex::Vector< amrex::Real > &  u_geos,
amrex::Gpu::DeviceVector< amrex::Real > &  d_u_geos,
amrex::Vector< amrex::Real > &  v_geos,
amrex::Gpu::DeviceVector< amrex::Real > &  d_v_geos,
const amrex::Geometry &  geom,
std::unique_ptr< amrex::MultiFab > &  ,
const amrex::Vector< amrex::Real > &   
)
inlinevirtual

Function to update user-specified geostrophic wind profile.

Parameters
[in]timecurrent time
[out]u_geosgeostrophic wind profile
[out]v_geosgeostrophic wind profile
[in]geomcontainer for geometric information
[in]z_phys_ccheight coordinate at cell centers
[in]zlevels_stagnominal heights of the z faces of this level
299  {
300  if (u_geos.empty()) return;
301 
302  amrex::Warning("Geostrophic wind profile not defined for "+name()+" problem");
303 
304  const int khi = geom.Domain().bigEnd()[2];
305  // const amrex::Real* prob_lo = geom.ProbLo();
306  // const auto dx = geom.CellSize();
307  for (int k = 0; k <= khi; k++)
308  {
309  // const amrex::Real z_cc = prob_lo[2] + (k+myhalf)* dx[2];
310  // set RHS term of RhoTheta equation based on time, z_cc here
311  u_geos[k] = zero;
312  v_geos[k] = zero;
313  }
314 
315  // Copy from host version to device version
316  amrex::Gpu::copy(amrex::Gpu::hostToDevice, u_geos.begin(), u_geos.end(), d_u_geos.begin());
317  amrex::Gpu::copy(amrex::Gpu::hostToDevice, v_geos.begin(), v_geos.end(), d_v_geos.begin());
318  }
const int khi
Definition: ERF_InitCustomPert_Bubble.H:25
constexpr amrex::Real zero
Definition: ERF_NumericalConstants.H:36
Here is the call graph for this function:

◆ update_rhoqt_sources()

virtual void ProblemBase::update_rhoqt_sources ( const double &  ,
amrex::MultiFab *  qsrc,
const amrex::Geometry &  ,
std::unique_ptr< amrex::MultiFab > &  ,
const amrex::Vector< amrex::Real > &   
)
inlinevirtual

Function to update user-specified moisture source terms that can vary with time and height.

Parameters
[in]timecurrent time
[out]rhoqt_sourcemoisture forcing profile
[in]geomcontainer for geometric information
[in]z_phys_ccheight coordinate at cell centers
[in]zlevels_stagnominal heights of the z faces of this level
222  {
223  if (qsrc->empty()) return;
224 
225  amrex::Warning("Moisture forcing not defined for "+name()+" problem");
226  for ( amrex::MFIter mfi(*qsrc, amrex::TilingIfNotGPU()); mfi.isValid(); ++mfi )
227  {
228  const auto &box = mfi.tilebox();
229  const amrex::Array4<amrex::Real>& qsrc_arr = qsrc->array(mfi);
230  // src is a spatial function if erf.spatial_moisture_forcing = true
231  // otherwise, qsrc_arr is defined only in over Z (box x and y dimensions are 1)
232  ParallelFor(box, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept {
233  // const amrex::Real z_cc = prob_lo[2] + (k+myhalf)* dx[2];
234  // set RHS term of RhoQ1 equation based on time, z_cc here
235  qsrc_arr(i, j, k) = zero;
236  });
237  }
238  }
Here is the call graph for this function:

◆ update_rhotheta_sources()

virtual void ProblemBase::update_rhotheta_sources ( const double &  ,
amrex::MultiFab *  src,
const amrex::Geometry &  ,
std::unique_ptr< amrex::MultiFab > &  ,
const amrex::Vector< amrex::Real > &   
)
inlinevirtual

Function to update user-specified temperature source terms that can vary with time and height.

Parameters
[in]timecurrent time
[out]rhotheta_sourceforcing profile
[in]geomcontainer for geometric information
[in]z_phys_ccheight coordinate at cell centers
[in]zlevels_stagnominal heights of the z faces of this level
188  {
189  if (src->empty()) return;
190 
191  amrex::Warning("Temperature forcing not defined for "+name()+" problem");
192  for ( amrex::MFIter mfi(*src, amrex::TilingIfNotGPU()); mfi.isValid(); ++mfi )
193  {
194  const auto &box = mfi.tilebox();
195  const amrex::Array4<amrex::Real>& src_arr = src->array(mfi);
196  // src is a spatial function if erf.spatial_rhotheta_forcing = true
197  // otherwise, qsrc_arr is defined only in over Z (box x and y dimensions are 1)
198  ParallelFor(box, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept {
199  // const amrex::Real z_cc = prob_lo[2] + (k+myhalf)* dx[2];
200  // set RHS term of RhoTheta equation based on time, z_cc here
201  src_arr(i, j, k) = zero;
202  });
203  }
204  }
Here is the call graph for this function:

◆ update_w_subsidence()

virtual void ProblemBase::update_w_subsidence ( const double &  ,
amrex::Vector< amrex::Real > &  wbar,
amrex::Gpu::DeviceVector< amrex::Real > &  d_wbar,
const amrex::MultiFab &  ,
const amrex::Geometry &  geom,
std::unique_ptr< amrex::MultiFab > &  ,
const amrex::Vector< amrex::Real > &   
)
inlinevirtual

Function to update the vertical velocity profile, used to add subsidence source terms for x-mom, y-mom, rho*theta, rho*Q1, and rho*Q2.

TODO: Currently, this is only called by InitData, so there is no time dependence.

Parameters
[in]timecurrent time
[out]wbarw vel forcing profile
[in]geomcontainer for geometric information
[in]z_phys_ccheight coordinate at cell centers
[in]zlevels_stagnominal heights of the z faces of this level
261  {
262  if (wbar.empty()) return;
263 
264  amrex::Warning("Moisture forcing not defined for "+name()+" problem");
265 
266  const int khi = geom.Domain().bigEnd()[2];
267  // const amrex::Real* prob_lo = geom.ProbLo();
268  // const auto dx = geom.CellSize();
269  for (int k = 0; k <= khi; k++)
270  {
271  // const amrex::Real z_cc = prob_lo[2] + (k+myhalf)* dx[2];
272  // set vertical velocity profile based on time, z_cc here
273  wbar[k] = zero;
274  }
275 
276  // Copy from host version to device version
277  amrex::Gpu::copy(amrex::Gpu::hostToDevice, wbar.begin(), wbar.end(), d_wbar.begin());
278  }
@ wbar
Definition: ERF_DataStruct.H:207
Here is the call graph for this function:

◆ use_physical_z()

static bool ProblemBase::use_physical_z ( )
inlinestaticprotected

True when heights must come from the vertical levels or z_phys, not from the geometry.

1252 { return SolverChoice::mesh_type != MeshType::ConstantDz; }

Referenced by phys_zhi(), and phys_zlo().

Here is the caller graph for this function:

Member Data Documentation

◆ base_parms

ProbParmDefaults ProblemBase::base_parms
protected

Referenced by init_base_parms().

◆ m_zlevels_stag

amrex::Vector<amrex::Vector<amrex::Real> > ProblemBase::m_zlevels_stag
protected

Staggered vertical levels of AMR level lev (set by set_zlevels_stag)

Referenced by phys_zhi(), phys_zlo(), and set_zlevels_stag().


The documentation for this class was generated from the following file: