REMORA
Regional Modeling of Oceans Refined Adaptively
Loading...
Searching...
No Matches
REMORA_LatLonRegridMath.H
Go to the documentation of this file.
1#ifndef REMORA_LATLONREGRIDMATH_H_
2#define REMORA_LATLONREGRIDMATH_H_
3
4/**
5 * \file REMORA_LatLonRegridMath.H
6 *
7 * The arithmetic of interpolating forcing from a rectilinear lat-lon grid onto REMORA's rho
8 * points and rotating vectors onto the grid axes (LatLonRegrid, REMORA::rotate_to_grid_axes),
9 * kept free of MultiFabs and NetCDF so that Tests/Unit can check it directly. Nothing here
10 * depends on REMORA_USE_NETCDF.
11 */
12
13#include <AMReX_Algorithm.H>
14#include <AMReX_GpuQualifiers.H>
15#include <AMReX_REAL.H>
16
17#include <algorithm>
18#include <cmath>
19#include <limits>
20#include <vector>
21
22namespace latlon_regrid {
23
24/** Largest m with a[m] <= x in an increasing array of n values, or -1 */
25AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
26int bracket (const amrex::Real* a, int n, amrex::Real x)
27{
28 int lo = -1, hi = n; // a[lo] <= x < a[hi], with a[-1] = -inf and a[n] = +inf
29 while (hi - lo > 1) {
30 const int mid = (lo + hi) / 2;
31 if (a[mid] <= x) { lo = mid; } else { hi = mid; }
32 }
33 return lo;
34}
35
36/** Whether a source value is data rather than a missing-data marker turned to NaN */
37AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
38bool is_valid (amrex::Real v)
39{
40 return (v == v) && std::abs(v) <= std::numeric_limits<amrex::Real>::max();
41}
42
43/** A longitude taken into [ref, ref + 360) */
44AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
45amrex::Real wrap_longitude (amrex::Real lon, amrex::Real ref)
46{
47 return lon - amrex::Real(360.0) * std::floor((lon - ref) / amrex::Real(360.0));
48}
49
50/** Four bilinear weights and the source cell (i, j) of each, in the file's index order:
51 * corner 0 is the lower-left of the target's cell in increasing coordinates, then
52 * counterclockwise. `inside` is false when the target lies outside the source grid. */
53struct Stencil
54{
55 int i[4];
56 int j[4];
57 amrex::Real w[4];
58 bool inside;
59};
60
61/**
62 * The bilinear stencil of one target point.
63 *
64 * @param lon longitude axis, increasing and continuous (see unwrap_longitudes)
65 * @param nlon its length
66 * @param lat latitude axis, increasing
67 * @param nlat its length
68 * @param periodic whether the longitude axis closes the circle, its last interval running
69 * from lon[nlon-1] to lon[0] + 360
70 * @param flip_lon whether the file stores longitude decreasing, so that index a in the
71 * increasing axis is nlon-1-a in the file
72 * @param flip_lat likewise for latitude
73 * @param lt target longitude, already in the axis's window (see wrap_longitude)
74 * @param pt target latitude
75 * @param tol how far outside the axes a target may lie and still count as inside
76 */
77AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
78Stencil bilinear_stencil (const amrex::Real* lon, int nlon, const amrex::Real* lat, int nlat,
79 bool periodic, bool flip_lon, bool flip_lat,
80 amrex::Real lt, amrex::Real pt, amrex::Real tol)
81{
82 using amrex::Real;
83 int m, m2;
84 Real p;
85 bool inside = true;
86 if (periodic) {
87 m = amrex::max(0, bracket(lon, nlon, lt));
88 m2 = (m + 1) % nlon;
89 const Real right = (m == nlon-1) ? lon[0] + Real(360.0) : lon[m+1];
90 p = (lt - lon[m]) / (right - lon[m]);
91 } else {
92 inside = (lt >= lon[0] - tol) && (lt <= lon[nlon-1] + tol);
93 m = amrex::min(amrex::max(bracket(lon, nlon, lt), 0), nlon-2);
94 m2 = m + 1;
95 p = (lt - lon[m]) / (lon[m2] - lon[m]);
96 }
97 inside = inside && (pt >= lat[0] - tol) && (pt <= lat[nlat-1] + tol);
98 const int n = amrex::min(amrex::max(bracket(lat, nlat, pt), 0), nlat-2);
99 const int n2 = n + 1;
100 Real q = (pt - lat[n]) / (lat[n2] - lat[n]);
101 p = amrex::min(amrex::max(p, Real(0.0)), Real(1.0));
102 q = amrex::min(amrex::max(q, Real(0.0)), Real(1.0));
103
104 const int fm = flip_lon ? nlon-1-m : m;
105 const int fm2 = flip_lon ? nlon-1-m2 : m2;
106 const int fn = flip_lat ? nlat-1-n : n;
107 const int fn2 = flip_lat ? nlat-1-n2 : n2;
108
109 Stencil s;
110 s.i[0] = fm; s.i[1] = fm2; s.i[2] = fm2; s.i[3] = fm;
111 s.j[0] = fn; s.j[1] = fn; s.j[2] = fn2; s.j[3] = fn2;
112 s.w[0] = (Real(1.0)-p)*(Real(1.0)-q);
113 s.w[1] = p*(Real(1.0)-q);
114 s.w[2] = p*q;
115 s.w[3] = (Real(1.0)-p)*q;
116 s.inside = inside;
117 return s;
118}
119
120/**
121 * Rotate a vector given as east and north components onto grid axes at angle a from east,
122 * counterclockwise (ROMS's `angle`), as ROMS's set_data does:
123 * xi = E cos a + N sin a, eta = -E sin a + N cos a.
124 */
125AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
126void rotate_to_grid (amrex::Real east, amrex::Real north, amrex::Real angle,
127 amrex::Real& xi, amrex::Real& eta)
128{
129 const amrex::Real c = std::cos(angle);
130 const amrex::Real s = std::sin(angle);
131 xi = east * c + north * s;
132 eta = -east * s + north * c;
133}
134
135/** Make a longitude axis continuous across the branch cut (170 .. 180, -180 .. -170 becomes
136 * 170 .. 190), each step taken modulo 360 */
137inline void unwrap_longitudes (std::vector<double>& lon)
138{
139 for (std::size_t m = 1; m < lon.size(); ++m) {
140 lon[m] -= 360.0 * std::round((lon[m] - lon[m-1]) / 360.0);
141 }
142}
143
144/** The span in degrees of an increasing, continuous longitude axis */
145inline double longitude_span (const std::vector<double>& lon_inc)
146{
147 return lon_inc.empty() ? 0.0 : lon_inc.back() - lon_inc.front();
148}
149
150/** Whether an increasing, continuous longitude axis closes the circle: its span plus one
151 * spacing is 360 degrees */
152inline bool is_periodic (const std::vector<double>& lon_inc)
153{
154 if (lon_inc.size() < 2) { return false; }
155 const double span = longitude_span(lon_inc);
156 const double dlon = span / static_cast<double>(lon_inc.size() - 1);
157 return std::abs(span + dlon - 360.0) < 1.e-3 * dlon;
158}
159
160/** The start of the longitude window targets are wrapped into: the axis's first point when
161 * it covers the globe, and otherwise centered on the axis, so that a point outside it is
162 * reported on the side it misses */
163inline double longitude_reference (const std::vector<double>& lon_inc, bool periodic)
164{
165 return periodic ? lon_inc.front() : 0.5 * (lon_inc.front() + lon_inc.back()) - 180.0;
166}
167
168} // namespace latlon_regrid
169
170#endif // REMORA_LATLONREGRIDMATH_H_
bool is_periodic(const std::vector< double > &lon_inc)
double longitude_span(const std::vector< double > &lon_inc)
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE int bracket(const amrex::Real *a, int n, amrex::Real x)
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE Stencil bilinear_stencil(const amrex::Real *lon, int nlon, const amrex::Real *lat, int nlat, bool periodic, bool flip_lon, bool flip_lat, amrex::Real lt, amrex::Real pt, amrex::Real tol)
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE bool is_valid(amrex::Real v)
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void rotate_to_grid(amrex::Real east, amrex::Real north, amrex::Real angle, amrex::Real &xi, amrex::Real &eta)
double longitude_reference(const std::vector< double > &lon_inc, bool periodic)
void unwrap_longitudes(std::vector< double > &lon)
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real wrap_longitude(amrex::Real lon, amrex::Real ref)