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_Array.H>
5 #include <AMReX_Array4.H>
6 #include <AMReX_GpuQualifiers.H>
7 #include <AMReX_REAL.H>
8 
9 // Particle position storage convention:
10 // * pos(0), pos(1): physical x, y (no horizontal mapping in ERF).
11 // * pos(AMREX_SPACEDIM-1): computational vertical coordinate zeta.
12 //
13 // For uniform-z grids the computational zeta equals the physical z, so the
14 // helpers below are identity transforms.
15 //
16 // For terrain-fitted grids the physical z at a column (x, y) is bilinearly
17 // interpolated from z_phys_nd(i, j, k_face).
18 
19 namespace ERF {
20 namespace ParticlePos {
21 
22 // Bilinear interpolation of the height array at (x, y) for a given vertical
23 // node index k_face. Returns the physical z of node k_face at column (x, y).
24 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
27  amrex::GpuArray<amrex::Real, AMREX_SPACEDIM> const& plo,
28  amrex::GpuArray<amrex::Real, AMREX_SPACEDIM> const& dxi,
29  amrex::Array4<amrex::Real const> const& height_arr) noexcept
30 {
31  const amrex::Real lx = (x - plo[0]) * dxi[0];
32  const amrex::Real ly = (y - plo[1]) * dxi[1];
33  const int ix = static_cast<int>(amrex::Math::floor(lx));
34  const int iy = static_cast<int>(amrex::Math::floor(ly));
35  const amrex::Real fx = lx - static_cast<amrex::Real>(ix);
36  const amrex::Real fy = ly - static_cast<amrex::Real>(iy);
37  return height_arr(ix , iy , k_face) * (amrex::Real(1) - fx) * (amrex::Real(1) - fy)
38  + height_arr(ix+1, iy , k_face) * fx * (amrex::Real(1) - fy)
39  + height_arr(ix , iy+1, k_face) * (amrex::Real(1) - fx) * fy
40  + height_arr(ix+1, iy+1, k_face) * fx * fy;
41 }
42 
43 // Computational zeta -> physical z at column (x, y).
44 // zeta is in the same units as the cell-centred z coordinate (i.e. it ranges
45 // over the level's z domain box scaled by dxi[2]). The integer cell index
46 // k = floor((zeta - plo_z) * dxi_z) is the same as for a uniform-z grid;
47 // the sub-cell fraction is interpolated linearly between the two terrain
48 // nodes that bound the cell.
49 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
52  amrex::GpuArray<amrex::Real, AMREX_SPACEDIM> const& plo,
53  amrex::GpuArray<amrex::Real, AMREX_SPACEDIM> const& dxi,
54  amrex::Array4<amrex::Real const> const& height_arr) noexcept
55 {
56  if (!height_arr) {
57  return zeta; // no terrain: identity
58  }
59  const amrex::Real lz = (zeta - plo[AMREX_SPACEDIM-1]) * dxi[AMREX_SPACEDIM-1];
60  const int k = static_cast<int>(amrex::Math::floor(lz));
61  const amrex::Real fz = lz - static_cast<amrex::Real>(k);
62  const amrex::Real z_lo = z_face_at_xy(x, y, k , plo, dxi, height_arr);
63  const amrex::Real z_hi = z_face_at_xy(x, y, k + 1, plo, dxi, height_arr);
64  return (amrex::Real(1) - fz) * z_lo + fz * z_hi;
65 }
66 
67 // Physical z -> computational zeta at column (x, y).
68 // Walks the terrain nodes at (x, y) to find the cell whose physical z range
69 // straddles the requested z, then linearly interpolates the sub-cell
70 // fraction. k_max is the largest valid cell index in the level; the walk
71 // is further clamped to the actual k extent of height_arr (which can be a
72 // smaller partial-z tile under AMR).
73 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
76  amrex::GpuArray<amrex::Real, AMREX_SPACEDIM> const& plo,
77  amrex::GpuArray<amrex::Real, AMREX_SPACEDIM> const& dxi,
78  amrex::Array4<amrex::Real const> const& height_arr,
79  int k_max) noexcept
80 {
81  if (!height_arr) {
82  return z; // no terrain: identity
83  }
84  const int k_lo = amrex::max(0, height_arr.begin[2]);
85  const int k_hi = amrex::min(k_max, height_arr.end[2] - 2);
86  int k = k_lo;
87  while (k < k_hi) {
88  const amrex::Real z_hi = z_face_at_xy(x, y, k + 1, plo, dxi, height_arr);
89  if (z < z_hi) { break; }
90  ++k;
91  }
92  const amrex::Real z_lo = z_face_at_xy(x, y, k , plo, dxi, height_arr);
93  const amrex::Real z_hi = z_face_at_xy(x, y, k + 1, plo, dxi, height_arr);
94  const amrex::Real fz = (z_hi > z_lo)
95  ? (z - z_lo) / (z_hi - z_lo)
96  : amrex::Real(0);
97  return plo[AMREX_SPACEDIM-1]
98  + (static_cast<amrex::Real>(k) + fz) / dxi[AMREX_SPACEDIM-1];
99 }
100 
101 } // namespace ParticlePos
102 } // namespace ERF
103 
104 #endif
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
Definition: ERF_TerrainConversion.H:51
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
Definition: ERF_TerrainConversion.H:75
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
Definition: ERF_TerrainConversion.H:26
Definition: ERF_InterpolationUtils.H:16