ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
ERF_TerrainConversion.H
Go to the documentation of this file.
1 #ifndef ERF_TERRAIN_CONVERSION_H_
2 #define ERF_TERRAIN_CONVERSION_H_
3 
4 #include <AMReX_Algorithm.H>
5 #include <AMReX_Array.H>
6 #include <AMReX_Array4.H>
7 #include <AMReX_GpuQualifiers.H>
8 #include <AMReX_REAL.H>
9 
10 // Particle position storage convention:
11 // * pos(0), pos(1): physical x, y (no horizontal mapping in ERF).
12 // * pos(AMREX_SPACEDIM-1): computational vertical coordinate zeta.
13 //
14 // For uniform-z grids the computational zeta equals the physical z, so the
15 // helpers below are identity transforms.
16 //
17 // For terrain-fitted grids the physical z at a column (x, y) is bilinearly
18 // interpolated from z_phys_nd(i, j, k_face).
19 
20 namespace ERF {
21 namespace ParticlePos {
22 
23 // Bilinear interpolation of the height array at (x, y) for a given vertical
24 // node index k_face. Returns the physical z of node k_face at column (x, y).
25 /**
26  * @brief Bilinear interpolation of the height array at (x, y) for a given vertical node index k_face.
27  * @param[in] x Physical x-coordinate.
28  * @param[in] y Physical y-coordinate.
29  * @param[in] k_face Vertical node index.
30  * @param[in] plo Lower bound of the box.
31  * @param[in] dxi Inverse grid spacing.
32  * @param[in] height_arr Array of node-centered heights.
33  * @return Physical height z at the specified coordinates.
34  */
35 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
38  amrex::GpuArray<amrex::Real, AMREX_SPACEDIM> const& plo,
39  amrex::GpuArray<amrex::Real, AMREX_SPACEDIM> const& dxi,
40  amrex::Array4<amrex::Real const> const& height_arr) noexcept
41 {
42  const amrex::Real lx = (x - plo[0]) * dxi[0];
43  const amrex::Real ly = (y - plo[1]) * dxi[1];
44  // Keep the stencil (ix..ix+1, iy..iy+1) inside height_arr: a point on the upper
45  // boundary, or just past it, takes the boundary node's value instead of reading
46  // one node beyond the array
47  const auto hlo = amrex::lbound(height_arr);
48  const auto hhi = amrex::ubound(height_arr);
49  const int ix = amrex::Clamp(static_cast<int>(amrex::Math::floor(lx)), hlo.x, hhi.x - 1);
50  const int iy = amrex::Clamp(static_cast<int>(amrex::Math::floor(ly)), hlo.y, hhi.y - 1);
51  const amrex::Real fx = amrex::Clamp(lx - static_cast<amrex::Real>(ix), amrex::Real(0), amrex::Real(1));
52  const amrex::Real fy = amrex::Clamp(ly - static_cast<amrex::Real>(iy), amrex::Real(0), amrex::Real(1));
53  return height_arr(ix , iy , k_face) * (amrex::Real(1) - fx) * (amrex::Real(1) - fy)
54  + height_arr(ix+1, iy , k_face) * fx * (amrex::Real(1) - fy)
55  + height_arr(ix , iy+1, k_face) * (amrex::Real(1) - fx) * fy
56  + height_arr(ix+1, iy+1, k_face) * fx * fy;
57 }
58 
59 // Computational zeta -> physical z at column (x, y).
60 // zeta is in the same units as the cell-centred z coordinate (i.e. it ranges
61 // over the level's z domain box scaled by dxi[2]). The integer cell index
62 // k = floor((zeta - plo_z) * dxi_z) is the same as for a uniform-z grid;
63 // the sub-cell fraction is interpolated linearly between the two terrain
64 // nodes that bound the cell.
65 /**
66  * @brief Convert computational vertical coordinate zeta to physical z at column (x, y).
67  * @param[in] x Physical x-coordinate.
68  * @param[in] y Physical y-coordinate.
69  * @param[in] zeta Computational vertical coordinate.
70  * @param[in] plo Lower bound of the box.
71  * @param[in] dxi Inverse grid spacing.
72  * @param[in] height_arr Array of node-centered heights.
73  * @return Physical height z.
74  */
75 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
78  amrex::GpuArray<amrex::Real, AMREX_SPACEDIM> const& plo,
79  amrex::GpuArray<amrex::Real, AMREX_SPACEDIM> const& dxi,
80  amrex::Array4<amrex::Real const> const& height_arr) noexcept
81 {
82  if (!height_arr) {
83  return zeta; // no terrain: identity
84  }
85  const amrex::Real lz = (zeta - plo[AMREX_SPACEDIM-1]) * dxi[AMREX_SPACEDIM-1];
86  const int k = static_cast<int>(amrex::Math::floor(lz));
87  const amrex::Real fz = lz - static_cast<amrex::Real>(k);
88  const amrex::Real z_lo = z_face_at_xy(x, y, k , plo, dxi, height_arr);
89  const amrex::Real z_hi = z_face_at_xy(x, y, k + 1, plo, dxi, height_arr);
90  return (amrex::Real(1) - fz) * z_lo + fz * z_hi;
91 }
92 
93 // Physical z -> computational zeta at column (x, y).
94 // Walks the terrain nodes at (x, y) to find the cell whose physical z range
95 // straddles the requested z, then linearly interpolates the sub-cell
96 // fraction. k_max is the largest valid cell index in the level; the walk
97 // is further clamped to the actual k extent of height_arr (which can be a
98 // smaller partial-z tile under AMR).
99 /**
100  * @brief Convert physical z to computational vertical coordinate zeta at column (x, y).
101  * @param[in] x Physical x-coordinate.
102  * @param[in] y Physical y-coordinate.
103  * @param[in] z Physical z-coordinate.
104  * @param[in] plo Lower bound of the box.
105  * @param[in] dxi Inverse grid spacing.
106  * @param[in] height_arr Array of node-centered heights.
107  * @param[in] k_max Largest valid cell index in the level.
108  * @return Computational vertical coordinate zeta.
109  */
110 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
113  amrex::GpuArray<amrex::Real, AMREX_SPACEDIM> const& plo,
114  amrex::GpuArray<amrex::Real, AMREX_SPACEDIM> const& dxi,
115  amrex::Array4<amrex::Real const> const& height_arr,
116  int k_max) noexcept
117 {
118  if (!height_arr) {
119  return z; // no terrain: identity
120  }
121  const int k_lo = amrex::max(0, height_arr.begin[2]);
122  const int k_hi = amrex::min(k_max, height_arr.end[2] - 2);
123  int k = k_lo;
124  while (k < k_hi) {
125  const amrex::Real z_hi = z_face_at_xy(x, y, k + 1, plo, dxi, height_arr);
126  if (z < z_hi) { break; }
127  ++k;
128  }
129  const amrex::Real z_lo = z_face_at_xy(x, y, k , plo, dxi, height_arr);
130  const amrex::Real z_hi = z_face_at_xy(x, y, k + 1, plo, dxi, height_arr);
131  const amrex::Real fz = (z_hi > z_lo)
132  ? (z - z_lo) / (z_hi - z_lo)
133  : amrex::Real(0);
134  return plo[AMREX_SPACEDIM-1]
135  + (static_cast<amrex::Real>(k) + fz) / dxi[AMREX_SPACEDIM-1];
136 }
137 
138 } // namespace ParticlePos
139 } // namespace ERF
140 
141 #endif
const Real lz
Definition: ERF_InitCustomPertVels_CloudChamber.H:12
const Real lx
Definition: ERF_InitCustomPertVels_CloudChamber.H:10
const Real ly
Definition: ERF_InitCustomPertVels_CloudChamber.H:11
amrex::Real Real
Definition: ERF_ShocInterface.H:19
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real z_from_zeta(amrex::Real x, amrex::Real y, amrex::Real zeta, amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > const &plo, amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > const &dxi, amrex::Array4< amrex::Real const > const &height_arr) noexcept
Convert computational vertical coordinate zeta to physical z at column (x, y).
Definition: ERF_TerrainConversion.H:77
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real zeta_from_z(amrex::Real x, amrex::Real y, amrex::Real z, amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > const &plo, amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > const &dxi, amrex::Array4< amrex::Real const > const &height_arr, int k_max) noexcept
Convert physical z to computational vertical coordinate zeta at column (x, y).
Definition: ERF_TerrainConversion.H:112
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real z_face_at_xy(amrex::Real x, amrex::Real y, int k_face, amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > const &plo, amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > const &dxi, amrex::Array4< amrex::Real const > const &height_arr) noexcept
Bilinear interpolation of the height array at (x, y) for a given vertical node index k_face.
Definition: ERF_TerrainConversion.H:37
Definition: ERF_InterpolationUtils.H:15