ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
ERF_TopographicShadingKernels.H
Go to the documentation of this file.
1 #ifndef ERF_TOPOGRAPHIC_SHADING_KERNELS_H_
2 #define ERF_TOPOGRAPHIC_SHADING_KERNELS_H_
3 
4 #include <AMReX_Array4.H>
5 #include <AMReX_GpuQualifiers.H>
6 #include <ERF_Constants.H>
7 #include <cmath>
8 #include <limits>
9 
10 namespace erf_topography {
11 using amrex::Real;
12 
13 struct Sun { Real x, y, z; };
14 
15 // Geographic east/north/up, using the same orbital hour-angle convention as
16 // orbital_cos_zenith_instant. Return a vector rather than recovering an azimuth
17 // from the radiation solver's possibly interval-averaged zenith cosine.
18 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
19 Sun sun_direction (double calday, double declin, Real lat_deg, Real lon_deg)
20 {
21  const double lat = double(lat_deg) * double(PI) / 180.0;
22  const double h = 2.0 * double(PI) * (calday - std::floor(calday))
23  + double(lon_deg) * double(PI) / 180.0 - double(PI);
24  return {Real(-std::cos(declin) * std::sin(h)),
25  Real(std::cos(lat)*std::sin(declin) - std::sin(lat)*std::cos(declin)*std::cos(h)),
26  Real(std::sin(lat)*std::sin(declin) + std::cos(lat)*std::cos(declin)*std::cos(h))};
27 }
28 
29 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
30 Real incidence (Real hx, Real hy, Sun s)
31 {
32  return amrex::max(Real(0), (s.z - hx*s.x - hy*s.y) / std::sqrt(Real(1) + hx*hx + hy*hy));
33 }
34 
35 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
36 int wrap (int i, int lo, int n) { return lo + ((i-lo) % n + n) % n; }
37 
38 // Scan grid-line crossings toward the sun, interpolating the resolved terrain
39 // between mass points (WRF's toposhad construction). dx/dy are physical lengths
40 // at the target, including its map factors; the ray uses that local tangent plane.
41 // The search cutoff is horizontal distance, not a count of grid rows.
42 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
43 bool shadowed (int i, int j, const amrex::Array4<const Real>& terrain,
44  int ilo, int ihi, int jlo, int jhi, bool periodic_x, bool periodic_y,
45  Real dx, Real dy, Sun s, Real search_length)
46 {
47  const Real horizontal = std::sqrt(s.x*s.x + s.y*s.y);
49  // A horizontal component below sqrt(eps) cannot be distinguished reliably
50  // from a vertical ray when the unit sun vector is rounded to Real.
51  if (s.z <= Real(1.e-4) || horizontal <= std::sqrt(real_eps)) { return false; }
52  // Trigonometric roundoff at cardinal azimuths must not send a ray out
53  // through a domain edge to which it is parallel (e.g. cos(90 degrees)).
54  constexpr Real angular_eps = Real(8)*real_eps;
55  const Real di = std::abs(s.x) < angular_eps*horizontal ? Real(0) : s.x / (horizontal * dx);
56  const Real dj = std::abs(s.y) < angular_eps*horizontal ? Real(0) : s.y / (horizontal * dy);
57  const Real ds = Real(1) / amrex::max(std::abs(di), std::abs(dj));
58  const Real z = terrain(i,j,0,0);
59  for (Real r = ds; r <= search_length; r += ds) {
60  const Real x = Real(i) + r*di, y = Real(j) + r*dj;
61  if ((!periodic_x && (x < ilo || x > ihi)) ||
62  (!periodic_y && (y < jlo || y > jhi))) { break; }
63  int ix = int(std::floor(x)), jy = int(std::floor(y));
64  const Real wx = x-Real(ix), wy = y-Real(jy);
65  int ip = ix+1, jp = jy+1;
66  if (periodic_x) { ix = wrap(ix,ilo,ihi-ilo+1); ip = wrap(ip,ilo,ihi-ilo+1); }
67  else { ip = amrex::min(ip,ihi); }
68  if (periodic_y) { jy = wrap(jy,jlo,jhi-jlo+1); jp = wrap(jp,jlo,jhi-jlo+1); }
69  else { jp = amrex::min(jp,jhi); }
70  AMREX_ASSERT(terrain.contains(ix,jy,0) && terrain.contains(ip,jp,0));
71  const Real h = (Real(1)-wy)*((Real(1)-wx)*terrain(ix,jy,0,0) + wx*terrain(ip,jy,0,0))
72  + wy*((Real(1)-wx)*terrain(ix,jp,0,0) + wx*terrain(ip,jp,0,0));
73  // Interpolation and ray-height arithmetic can differ by several ulps
74  // at the local terrain height, especially with single precision.
75  const Real ray_height = z + r*s.z/horizontal;
76  // All heights are metres; use a 1 m scale near sea level.
77  const Real height_scale = amrex::max(Real(1),
78  amrex::max(std::abs(z), amrex::max(std::abs(h), std::abs(ray_height))));
79  const Real height_tol = Real(8)*real_eps*height_scale;
80  if (h > ray_height + height_tol) { return true; }
81  }
82  return false;
83 }
84 
85 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
86 Real corrected_shortwave (Real direct, Real diffuse, Real factor)
87 {
88  return diffuse + direct*factor;
89 }
90 } // namespace erf_topography
91 #endif
const Real dy
Definition: ERF_InitCustomPert_ABL.H:45
const Real dx
Definition: ERF_InitCustomPert_ABL.H:44
constexpr amrex::Real real_eps
Definition: ERF_NumericalConstants.H:34
constexpr amrex::Real PI
Definition: ERF_NumericalConstants.H:46
amrex::Real Real
Definition: ERF_ShocInterface.H:19
Definition: ERF_TopographicShadingKernels.H:10
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE bool shadowed(int i, int j, const amrex::Array4< const Real > &terrain, int ilo, int ihi, int jlo, int jhi, bool periodic_x, bool periodic_y, Real dx, Real dy, Sun s, Real search_length)
Definition: ERF_TopographicShadingKernels.H:43
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE Real incidence(Real hx, Real hy, Sun s)
Definition: ERF_TopographicShadingKernels.H:30
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE int wrap(int i, int lo, int n)
Definition: ERF_TopographicShadingKernels.H:36
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE Sun sun_direction(double calday, double declin, Real lat_deg, Real lon_deg)
Definition: ERF_TopographicShadingKernels.H:19
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE Real corrected_shortwave(Real direct, Real diffuse, Real factor)
Definition: ERF_TopographicShadingKernels.H:86
real(c_double), parameter epsilon
Definition: ERF_module_model_constants.F90:12
real(c_double), private ds
Definition: ERF_module_mp_morr_two_moment.F90:203
real(c_double), private di
Definition: ERF_module_mp_morr_two_moment.F90:203
Definition: ERF_TopographicShadingKernels.H:13
Real x
Definition: ERF_TopographicShadingKernels.H:13
Real z
Definition: ERF_TopographicShadingKernels.H:13
Real y
Definition: ERF_TopographicShadingKernels.H:13