ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
erf_terrain_file Namespace Reference

Classes

struct  HeightTable
 

Functions

std::string TableProblem (bool is_usgs, int nx, int ny, const Vector< Real > &x, const Vector< Real > &y, const Vector< Real > &z, const GpuArray< Real, AMREX_SPACEDIM > &prob_lo, const GpuArray< Real, AMREX_SPACEDIM > &prob_hi, bool require_domain_coverage)
 
std::string TableSummary (const std::string &fname, bool is_usgs, int nx, int ny, const Vector< Real > &x, const Vector< Real > &y, const Vector< Real > &z)
 
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)
 
Array< Real, 4 > FaceBandSlopes (const MultiFab &z_phys_nd, const MultiFab &mf_mx, const MultiFab &mf_my, const Geometry &geom, int band)
 
std::string OpenFaceSlopeWarning (int lev, int band, const Array< Real, 4 > &slopes, const Array< bool, 4 > &open, const Array< std::string, 4 > &bc_names)
 
const HeightTable & ReadHeightTable (const std::string &fname, bool is_usgs, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &prob_lo, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &prob_hi, bool require_domain_coverage=true)
 
std::string TableProblem (bool is_usgs, int nx, int ny, const amrex::Vector< amrex::Real > &x, const amrex::Vector< amrex::Real > &y, const amrex::Vector< amrex::Real > &z, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &prob_lo, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &prob_hi, bool require_domain_coverage=true)
 
std::string TableSummary (const std::string &fname, bool is_usgs, int nx, int ny, const amrex::Vector< amrex::Real > &x, const amrex::Vector< amrex::Real > &y, const amrex::Vector< amrex::Real > &z)
 
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real CellTerrainSlope (int i, int j, int k, const amrex::Array4< const amrex::Real > &z_nd, amrex::Real mfx, amrex::Real mfy, amrex::Real dxinv, amrex::Real dyinv) noexcept
 
amrex::Array< amrex::Real, 4 > FaceBandSlopes (const amrex::MultiFab &z_phys_nd, const amrex::MultiFab &mf_mx, const amrex::MultiFab &mf_my, const amrex::Geometry &geom, int band)
 
std::string OpenFaceSlopeWarning (int lev, int band, const amrex::Array< amrex::Real, 4 > &slopes, const amrex::Array< bool, 4 > &open, const amrex::Array< std::string, 4 > &bc_names)
 

Variables

constexpr amrex::Real open_face_slope_warn = amrex::Real(0.3)
 

Function Documentation

◆ CellTerrainSlope()

AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real erf_terrain_file::CellTerrainSlope ( int  i,
int  j,
int  k,
const amrex::Array4< const amrex::Real > &  z_nd,
amrex::Real  mfx,
amrex::Real  mfy,
amrex::Real  dxinv,
amrex::Real  dyinv 
)
noexcept

Physical slope |grad h| of the terrain over cell (i,j,k): the terrain drops of ComputeTerrainCellDrops (the same drops as the diffusive limits and the slope factor) times the map factor over the cell size in each direction.

Parameters
[in]i,j,kcell indices
[in]z_ndnodal heights
[in]mfx,mfymap factors of the cell
[in]dxinv,dyinvinverse horizontal cell sizes
168 {
169  const TerrainCellDrops d = ComputeTerrainCellDrops(i, j, k, z_nd);
170  const amrex::Real hx = mfx * dxinv * d.dx_drop;
171  const amrex::Real hy = mfy * dyinv * d.dy_drop;
172  return std::sqrt(hx*hx + hy*hy);
173 }
amrex::Real Real
Definition: ERF_ShocInterface.H:19
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE TerrainCellDrops ComputeTerrainCellDrops(int i, int j, int k, const amrex::Array4< const amrex::Real > &z_nd) noexcept
Definition: ERF_TerrainDiffusionLimits.H:44
Definition: ERF_TerrainDiffusionLimits.H:29
amrex::Real dy_drop
Definition: ERF_TerrainDiffusionLimits.H:31
amrex::Real dx_drop
Definition: ERF_TerrainDiffusionLimits.H:30

Referenced by FaceBandSlopes().

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

◆ FaceBandSlopes() [1/2]

amrex::Array<amrex::Real,4> erf_terrain_file::FaceBandSlopes ( const amrex::MultiFab &  z_phys_nd,
const amrex::MultiFab &  mf_mx,
const amrex::MultiFab &  mf_my,
const amrex::Geometry &  geom,
int  band 
)

Largest CellTerrainSlope, on one level, over the surface cells within band cells of each lateral domain face, in the order xlo, xhi, ylo, yhi; -1 for a face whose band holds no cell of this level. Reduced over all ranks.

Parameters
[in]z_phys_ndnodal heights of the level (surface at the domain's lowest k)
[in]mf_mxmap factor in x at cell centres (ERF's mapfac m_x, on ba2d)
[in]mf_mymap factor in y at cell centres (m_y)
[in]geomgeometry of the level
[in]bandwidth of the band in cells (>= 1)

◆ FaceBandSlopes() [2/2]

Array<Real,4> erf_terrain_file::FaceBandSlopes ( const MultiFab &  z_phys_nd,
const MultiFab &  mf_mx,
const MultiFab &  mf_my,
const Geometry &  geom,
int  band 
)
352 {
353  AMREX_ALWAYS_ASSERT(band >= 1);
354  const Box& dom = geom.Domain();
355  const int klo = dom.smallEnd(2);
356  const Real dxinv = geom.InvCellSize(0);
357  const Real dyinv = geom.InvCellSize(1);
358 
359  // The bands of cells next to each lateral face, at the surface only
360  Array<Box,4> bands;
361  for (int f = 0; f < 4; ++f) {
362  Box b(dom);
363  b.setRange(2, klo);
364  const int dir = f / 2;
365  if (f % 2 == 0) {
366  b.setBig(dir, amrex::min(dom.smallEnd(dir) + band - 1, dom.bigEnd(dir)));
367  } else {
368  b.setSmall(dir, amrex::max(dom.bigEnd(dir) - band + 1, dom.smallEnd(dir)));
369  }
370  bands[f] = b;
371  }
372 
373  Array<Real,4> smax{Real(-1.), Real(-1.), Real(-1.), Real(-1.)};
374  for (int f = 0; f < 4; ++f) {
375  ReduceOps<ReduceOpMax> reduce_op;
376  ReduceData<Real> reduce_data(reduce_op);
377  using ReduceTuple = typename decltype(reduce_data)::Type;
378  bool any = false;
379  for (MFIter mfi(z_phys_nd); mfi.isValid(); ++mfi) {
380  // z_phys_nd is nodal: its valid cells are the enclosed cells of the nodal box
381  const Box bx = enclosedCells(mfi.validbox()) & bands[f];
382  if (bx.isEmpty()) { continue; }
383  any = true;
384  const Array4<const Real> z_nd = z_phys_nd.const_array(mfi);
385  // the map factors live on the same boxes flattened to one k (ba2d)
386  const Array4<const Real> mfx = mf_mx.const_array(mfi);
387  const Array4<const Real> mfy = mf_my.const_array(mfi);
388  reduce_op.eval(bx, reduce_data, [=] AMREX_GPU_DEVICE (int i, int j, int k) -> ReduceTuple
389  {
390  return {CellTerrainSlope(i, j, k, z_nd, mfx(i,j,0), mfy(i,j,0), dxinv, dyinv)};
391  });
392  }
393  if (any) { smax[f] = amrex::get<0>(reduce_data.value(reduce_op)); }
394  }
395  ParallelDescriptor::ReduceRealMax(smax.data(), 4);
396  return smax;
397 }
AMREX_ALWAYS_ASSERT(bx.length()[2]==khi+1)
const int klo
Definition: ERF_InitCustomPert_KE.H:9
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real CellTerrainSlope(int i, int j, int k, const amrex::Array4< const amrex::Real > &z_nd, amrex::Real mfx, amrex::Real mfy, amrex::Real dxinv, amrex::Real dyinv) noexcept
Definition: ERF_TerrainFileChecks.H:166

Referenced by ERF::WarnSteepTerrainAtOpenFaces().

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

◆ OpenFaceSlopeWarning() [1/2]

std::string erf_terrain_file::OpenFaceSlopeWarning ( int  lev,
int  band,
const amrex::Array< amrex::Real, 4 > &  slopes,
const amrex::Array< bool, 4 > &  open,
const amrex::Array< std::string, 4 > &  bc_names 
)

Warning text for the inflow and outflow faces of one level whose band slope (from FaceBandSlopes) exceeds open_face_slope_warn, or an empty string if there is none. The caller prints it only when no steep-terrain option is on at that level.

Parameters
[in]levlevel, for the text
[in]bandband width in cells, for the text
[in]slopesband slopes in the order xlo, xhi, ylo, yhi (-1 = no cells)
[in]openwhether each face is an inflow or outflow face
[in]bc_namesboundary type of each face, for the text

◆ OpenFaceSlopeWarning() [2/2]

std::string erf_terrain_file::OpenFaceSlopeWarning ( int  lev,
int  band,
const Array< Real, 4 > &  slopes,
const Array< bool, 4 > &  open,
const Array< std::string, 4 > &  bc_names 
)
404 {
405  static const char* face_names[4] = {"xlo", "xhi", "ylo", "yhi"};
406  std::ostringstream os;
407  for (int f = 0; f < 4; ++f) {
408  if (open[f] && slopes[f] > open_face_slope_warn) {
409  os << "WARNING: at level " << lev << " the terrain slope |grad h| reaches " << slopes[f]
410  << " within " << band << " cells of the " << face_names[f] << " face ("
411  << bc_names[f] << "), above " << open_face_slope_warn << "\n";
412  }
413  }
414  if (os.str().empty()) { return ""; }
415  os << " Flattening the terrain within " << band << " cells of inflow and outflow faces avoids"
416  << " this. With an eddy-diffusivity closure, erf.smag2d_slope_limiter or"
417  << " erf.smag2d_kh_cap (Smagorinsky2D), erf.implicit_terrain_metric (needs"
418  << " erf.vert_implicit_fac > 0) or erf.diffusive_dt_limit limit the terrain-metric"
419  << " diffusion on steep cells; none of them is on here\n";
420  return os.str();
421 }
constexpr amrex::Real open_face_slope_warn
Definition: ERF_TerrainFileChecks.H:151

Referenced by ERF::WarnSteepTerrainAtOpenFaces().

Here is the caller graph for this function:

◆ ReadHeightTable() [1/2]

const HeightTable& erf_terrain_file::ReadHeightTable ( const std::string &  fname,
bool  is_usgs,
const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &  prob_lo,
const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &  prob_hi,
bool  require_domain_coverage = true 
)

The height table in file fname, read, checked (TableProblem) and summarised (TableSummary) on the I/O rank the first time the file is asked for, then broadcast; later calls return the same table without reading the file again. Aborts, naming the file and the line, on a file that cannot be read as the layout says.

Parameters
[in]fnamefile name
[in]is_usgsUSGS layout
[in]prob_lo,prob_hidomain extent, for the coverage check
[in]require_domain_coveragefalse for local building patches; all other checks remain

◆ ReadHeightTable() [2/2]

const HeightTable& erf_terrain_file::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 
)
335 {
336  // Include the coverage policy so a cached building patch cannot bypass the terrain check
337  // if the same file is subsequently requested as terrain.
338  static std::map<std::string, HeightTable> tables;
339  const std::string key = std::string(require_domain_coverage ? "covered:" : "partial:")
340  + (is_usgs ? "USGS:" : "ERF:") + fname;
341  auto it = tables.find(key);
342  if (it == tables.end()) {
343  it = tables.emplace(key, read_and_broadcast(fname, is_usgs, prob_lo, prob_hi,
344  require_domain_coverage)).first;
345  }
346  return it->second;
347 }
const GpuArray< Real, AMREX_SPACEDIM > prob_lo
Definition: ERF_InitCustomPert_CloudChamber.H:33
const amrex::Real * prob_hi
Definition: ERF_InitCustomPert_DataAssimilation_ISV.H:17

Referenced by ProblemBase::read_custom_terrain().

Here is the caller graph for this function:

◆ TableProblem() [1/2]

std::string erf_terrain_file::TableProblem ( bool  is_usgs,
int  nx,
int  ny,
const amrex::Vector< amrex::Real > &  x,
const amrex::Vector< amrex::Real > &  y,
const amrex::Vector< amrex::Real > &  z,
const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &  prob_lo,
const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &  prob_hi,
bool  require_domain_coverage = true 
)

The first problem found in a height table, or an empty string if there is none. Problems: fewer points than the layout needs (ERF format: one in each direction, USGS: two; one value in a direction is not checked further), list sizes that do not match nx and ny, a value that is not finite, x or y values that do not increase strictly (in the USGS format: the x values of each row, and the y values from row to row), and a table that does not cover the domain: each end of the x and y lists may stop at most one table spacing inside prob_lo or prob_hi (the layout of a file with one height per cell starting at prob_lo), with an allowance of 1e-3 of that spacing for rounded coordinates. Beyond the table the reader only extends the heights from its edge: linearly before the first point, and (ERF format) by holding the last value after the last point; USGS extends linearly at both ends. Setting require_domain_coverage to false skips only the coverage check, for building files that describe a local patch. Coordinate ordering and all format checks still apply.

Parameters
[in]is_usgsUSGS layout (x varies fastest, one x per point)
[in]nx,nytable size
[in]x,y,ztable coordinates and heights
[in]prob_lo,prob_hidomain extent
[in]require_domain_coveragewhether the table must cover the domain

◆ TableProblem() [2/2]

std::string erf_terrain_file::TableProblem ( bool  is_usgs,
int  nx,
int  ny,
const Vector< Real > &  x,
const Vector< Real > &  y,
const Vector< Real > &  z,
const GpuArray< Real, AMREX_SPACEDIM > &  prob_lo,
const GpuArray< Real, AMREX_SPACEDIM > &  prob_hi,
bool  require_domain_coverage 
)
80 {
81  std::ostringstream os;
82  const int nmin = is_usgs ? 2 : 1;
83  if (nx < nmin || ny < nmin) {
84  os << "nx = " << nx << " and ny = " << ny << " must both be at least " << nmin
85  << (is_usgs ? " in the USGS format" : "");
86  return os.str();
87  }
88  const Long npts = Long(nx) * Long(ny);
89  const Long nxv = is_usgs ? npts : Long(nx);
90  if (Long(x.size()) != nxv || Long(y.size()) != Long(ny) || Long(z.size()) != npts) {
91  os << "expected " << nxv << " x values, " << ny << " y values and " << npts
92  << " heights, found " << x.size() << ", " << y.size() << " and " << z.size();
93  return os.str();
94  }
95  // A file cannot hold nan or inf (the reader stops on them as "not a number"); this is a
96  // guard for tables built otherwise
97  auto finite = [] (const Vector<Real>& v, const char* name) -> std::string {
98  for (Long n = 0; n < Long(v.size()); ++n) {
99  if (!std::isfinite(v[n])) {
100  std::ostringstream o;
101  o << name << " value " << n << " is not finite (" << v[n] << ")";
102  return o.str();
103  }
104  }
105  return "";
106  };
107  for (const auto& s : {finite(x,"x"), finite(y,"y"), finite(z,"height")}) {
108  if (!s.empty()) { return s; }
109  }
110 
111  std::string s = list_problem("y", ny, [&] (int m) { return y[m]; }, prob_lo[1], prob_hi[1],
112  require_domain_coverage);
113  if (!s.empty()) { return s; }
114  if (is_usgs) {
115  for (int j = 0; j < ny; ++j) {
116  s = list_problem("x", nx, [&] (int m) { return x[j*nx + m]; }, prob_lo[0], prob_hi[0],
117  require_domain_coverage);
118  if (!s.empty()) {
119  return s + " (row " + std::to_string(j) + " of the x-fastest USGS layout)";
120  }
121  }
122  } else {
123  s = list_problem("x", nx, [&] (int m) { return x[m]; }, prob_lo[0], prob_hi[0],
124  require_domain_coverage);
125  if (!s.empty()) { return s; }
126  }
127  return "";
128 }
const int nx
Definition: ERF_InitCustomPertVels_CloudChamber.H:14
const int ny
Definition: ERF_InitCustomPertVels_CloudChamber.H:15
std::string name
Definition: ERF_Plotfile2DCatalog.cpp:116
bool finite(amrex::Real value) noexcept
Definition: ERF_CloudChamber.H:375
Here is the call graph for this function:

◆ TableSummary() [1/2]

std::string erf_terrain_file::TableSummary ( const std::string &  fname,
bool  is_usgs,
int  nx,
int  ny,
const amrex::Vector< amrex::Real > &  x,
const amrex::Vector< amrex::Real > &  y,
const amrex::Vector< amrex::Real > &  z 
)

Text describing a valid height table: its size and extent, the order its heights are read in, where the lowest and highest heights sit, and the largest slopes |dh/dx| and |dh/dy| between neighbouring table points with where they sit. Meant to be printed once per file, so that a transposed or mis-ordered file can be spotted at start-up.

Parameters
[in]fnamefile name, for the first line
[in]is_usgsUSGS layout
[in]nx,nytable size
[in]x,y,ztable coordinates and heights (must pass TableProblem)

◆ TableSummary() [2/2]

std::string erf_terrain_file::TableSummary ( const std::string &  fname,
bool  is_usgs,
int  nx,
int  ny,
const Vector< Real > &  x,
const Vector< Real > &  y,
const Vector< Real > &  z 
)
133 {
134  int imin = 0, jmin = 0, imax = 0, jmax = 0;
135  Real zmin = z_at(is_usgs, nx, ny, z, 0, 0), zmax = zmin;
136  Real sx = Real(-1.), sy = Real(-1.);
137  Real sx_x = 0, sx_y = 0, sy_x = 0, sy_y = 0;
138  for (int j = 0; j < ny; ++j) {
139  for (int i = 0; i < nx; ++i) {
140  const Real h = z_at(is_usgs, nx, ny, z, i, j);
141  if (h < zmin) { zmin = h; imin = i; jmin = j; }
142  if (h > zmax) { zmax = h; imax = i; jmax = j; }
143  if (i+1 < nx) {
144  const Real x0 = x_at(is_usgs, nx, x, i, j), x1 = x_at(is_usgs, nx, x, i+1, j);
145  const Real s = std::abs(z_at(is_usgs, nx, ny, z, i+1, j) - h) / (x1 - x0);
146  if (s > sx) { sx = s; sx_x = Real(0.5)*(x0 + x1); sx_y = y[j]; }
147  }
148  if (j+1 < ny) {
149  const Real s = std::abs(z_at(is_usgs, nx, ny, z, i, j+1) - h) / (y[j+1] - y[j]);
150  if (s > sy) { sy = s; sy_x = x_at(is_usgs, nx, x, i, j); sy_y = Real(0.5)*(y[j] + y[j+1]); }
151  }
152  }
153  }
154 
155  std::ostringstream os;
156  os << "Height file " << fname << ": " << nx << " x " << ny << " heights over x "
157  << x_at(is_usgs, nx, x, 0, 0) << " .. " << x_at(is_usgs, nx, x, nx-1, 0)
158  << ", y " << y[0] << " .. " << y[ny-1] << "\n";
159  if (is_usgs) {
160  os << " read in USGS order: x varies fastest, height n is at (x[n], y[n / nx])\n";
161  } else {
162  os << " read in ERF order: y varies fastest, height n is at (x[n / ny], y[n % ny])\n";
163  }
164  os << " lowest " << zmin << " m at (x, y) = (" << x_at(is_usgs, nx, x, imin, jmin)
165  << ", " << y[jmin] << ")\n";
166  os << " highest " << zmax << " m at (x, y) = (" << x_at(is_usgs, nx, x, imax, jmax)
167  << ", " << y[jmax] << ")\n";
168  if (sx < Real(0.)) {
169  os << " one x value: constant in x";
170  } else {
171  os << " largest |dh/dx| " << sx << " at (" << sx_x << ", " << sx_y << ")";
172  }
173  if (sy < Real(0.)) {
174  os << ", one y value: constant in y\n";
175  } else {
176  os << ", largest |dh/dy| " << sy << " at (" << sy_x << ", " << sy_y << ")\n";
177  }
178  return os.str();
179 }

Variable Documentation

◆ open_face_slope_warn

constexpr amrex::Real erf_terrain_file::open_face_slope_warn = amrex::Real(0.3)
constexpr

Terrain slope |grad h| above which a band next to an inflow or outflow face is reported.

Referenced by OpenFaceSlopeWarning(), and ERF::WarnSteepTerrainAtOpenFaces().