ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
ERF_NearSurfaceDiagnostics.H
Go to the documentation of this file.
1 #ifndef ERF_NEAR_SURFACE_DIAGNOSTICS_H_
2 #define ERF_NEAR_SURFACE_DIAGNOSTICS_H_
3 
4 #include <AMReX_GpuQualifiers.H>
5 #include <AMReX_Math.H>
6 #include <AMReX_MultiFab.H>
7 #include <AMReX_iMultiFab.H>
8 #include <cmath>
9 
10 #include "ERF_Constants.H"
11 #include "ERF_MOSTStress.H"
13 
15 {
16 
18 {
24 };
25 
27 {
30 };
31 
32 struct MostState
33 {
40 };
41 
43 {
46 };
47 
48 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
49 bool valid_real (amrex::Real value) noexcept
50 {
51  return amrex::Math::isfinite(value);
52 }
53 
54 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
55 bool valid_noahmp_component (amrex::Real value) noexcept
56 {
57  return valid_real(value) && value > amrex::Real(-9990.0) &&
58  value < amrex::Real(0.5) * lsm_undefined;
59 }
60 
61 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
62 bool native_bundle_temperature_is_valid (const NativeBundle& bundle) noexcept;
63 
64 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
65 bool native_bundle_mixing_ratio_is_valid (const NativeBundle& bundle) noexcept;
66 
67 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
68 bool native_bundle_vegetation_fraction_is_valid (const NativeBundle& bundle) noexcept;
69 
70 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
71 bool native_bundle_is_valid (const NativeBundle& bundle) noexcept
72 {
73  return native_bundle_temperature_is_valid(bundle) &&
76 }
77 
78 // A tile with zero weight does not enter the aggregate, so its value is not
79 // checked: Noah-MP writes T2MVXY = 0 where FVEGXY = 0 (and the bare tile is
80 // likewise unused where FVEGXY = 1). Any fraction strictly between 0 and 1
81 // still requires both tiles to be valid.
82 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
83 bool native_vegetated_tile_is_used (amrex::Real vegetation_fraction) noexcept
84 {
85  return vegetation_fraction > amrex::Real(0.0);
86 }
87 
88 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
89 bool native_bare_tile_is_used (amrex::Real vegetation_fraction) noexcept
90 {
91  return vegetation_fraction < amrex::Real(1.0);
92 }
93 
94 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
95 bool native_bundle_temperature_is_valid (const NativeBundle& bundle) noexcept
96 {
97  if (!native_bundle_vegetation_fraction_is_valid(bundle)) { return false; }
98  const amrex::Real f = bundle.vegetation_fraction;
99  const bool vegetated_ok = !native_vegetated_tile_is_used(f) ||
100  (valid_noahmp_component(bundle.temperature_vegetated) &&
101  bundle.temperature_vegetated > amrex::Real(0.0));
102  const bool bare_ok = !native_bare_tile_is_used(f) ||
103  (valid_noahmp_component(bundle.temperature_bare) &&
104  bundle.temperature_bare > amrex::Real(0.0));
105  return vegetated_ok && bare_ok;
106 }
107 
108 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
110 {
111  if (!native_bundle_vegetation_fraction_is_valid(bundle)) { return false; }
112  const amrex::Real f = bundle.vegetation_fraction;
113  const bool vegetated_ok = !native_vegetated_tile_is_used(f) ||
114  (valid_noahmp_component(bundle.mixing_ratio_vegetated) &&
115  bundle.mixing_ratio_vegetated >= amrex::Real(0.0));
116  const bool bare_ok = !native_bare_tile_is_used(f) ||
117  (valid_noahmp_component(bundle.mixing_ratio_bare) &&
118  bundle.mixing_ratio_bare >= amrex::Real(0.0));
119  return vegetated_ok && bare_ok;
120 }
121 
122 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
124 {
125  return valid_noahmp_component(bundle.vegetation_fraction) &&
126  bundle.vegetation_fraction >= amrex::Real(0.0) &&
127  bundle.vegetation_fraction <= amrex::Real(1.0);
128 }
129 
130 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
132 {
133  const amrex::Real f = bundle.vegetation_fraction;
134  // At the endpoints take the one tile in use, so that an unused tile's
135  // placeholder (which may be a sentinel) cannot leak in through 0 * value.
137  return {bundle.temperature_bare, bundle.mixing_ratio_bare};
138  }
139  if (!native_bare_tile_is_used(f)) {
140  return {bundle.temperature_vegetated, bundle.mixing_ratio_vegetated};
141  }
142  return {
143  f * bundle.temperature_vegetated + (amrex::Real(1.0)-f) * bundle.temperature_bare,
144  f * bundle.mixing_ratio_vegetated + (amrex::Real(1.0)-f) * bundle.mixing_ratio_bare
145  };
146 }
147 
148 // Largest stability coordinate z/L used by the 2 m profile. The stable
149 // correction is linear in z/L and is calibrated for z/L of order one; with a
150 // very small positive L it would otherwise grow without bound.
151 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
152 amrex::Real most_profile_zeta_max () noexcept { return amrex::Real(1.0); }
153 
154 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
155 bool evaluate_most_factor (const MostState& state,
157  amrex::Real& factor) noexcept
158 {
159  if (!valid_real(state.roughness_height) ||
160  !valid_real(state.obukhov_length) ||
161  state.roughness_height <= amrex::Real(0.0) ||
162  height <= state.roughness_height ||
163  state.obukhov_length == amrex::Real(0.0)) {
164  return false;
165  }
166  const similarity_funs sfuns{};
167  const amrex::Real zeta = amrex::min(height / state.obukhov_length,
169  factor = std::log(height / state.roughness_height) - sfuns.calc_psi_h(zeta);
170  return valid_real(factor);
171 }
172 
173 // The momentum counterpart of evaluate_most_factor: ln(z/z0) - psi_m(z/L), so that
174 // the wind speed at height z is u_star / KAPPA times it.
175 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
177  amrex::Real obukhov_length,
179  amrex::Real& factor) noexcept
180 {
181  if (!valid_real(roughness_height) || !valid_real(obukhov_length) ||
182  roughness_height <= amrex::Real(0.0) ||
183  height <= roughness_height ||
184  obukhov_length == amrex::Real(0.0)) {
185  return false;
186  }
187  const similarity_funs sfuns{};
188  const amrex::Real zeta = amrex::min(height / obukhov_length,
190  factor = std::log(height / roughness_height) - sfuns.calc_psi_m(zeta);
191  return valid_real(factor);
192 }
193 
194 // Evaluate ERF's integrated scalar profile without changing the production
195 // surface-layer state. The inputs use theta and mixing-ratio scales.
196 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
197 bool evaluate_most_profile (const MostState& state,
199  MostProfile& profile) noexcept
200 {
201  if (!valid_real(state.theta_surface) || !valid_real(state.theta_star) ||
202  !valid_real(state.mixing_ratio_surface) || !valid_real(state.mixing_ratio_star)) {
203  return false;
204  }
205 
206  amrex::Real profile_factor = amrex::Real(0.0);
207  if (!evaluate_most_factor(state, height, profile_factor)) {
208  return false;
209  }
210 
211  profile.potential_temperature = state.theta_surface +
212  state.theta_star / KAPPA * profile_factor;
213  profile.mixing_ratio = state.mixing_ratio_surface +
214  state.mixing_ratio_star / KAPPA * profile_factor;
215  return valid_real(profile.potential_temperature) &&
216  valid_real(profile.mixing_ratio) &&
217  profile.potential_temperature > amrex::Real(0.0) &&
218  profile.mixing_ratio >= amrex::Real(0.0);
219 }
220 
221 struct Sources
222 {
223  const amrex::MultiFab* native_temperature_vegetated = nullptr;
224  const amrex::MultiFab* native_temperature_bare = nullptr;
225  const amrex::MultiFab* native_mixing_ratio_vegetated = nullptr;
226  const amrex::MultiFab* native_mixing_ratio_bare = nullptr;
227  const amrex::MultiFab* native_vegetation_fraction = nullptr;
228 
229  const amrex::MultiFab* theta_surface = nullptr;
230  const amrex::MultiFab* theta_star = nullptr;
231  const amrex::MultiFab* mixing_ratio_surface = nullptr;
232  const amrex::MultiFab* mixing_ratio_star = nullptr;
233  const amrex::MultiFab* roughness_height = nullptr;
234  const amrex::MultiFab* obukhov_length = nullptr;
235  const amrex::MultiFab* source_mask = nullptr;
236  const amrex::iMultiFab* land_mask = nullptr;
237  const amrex::MultiFab* cons = nullptr;
238  const amrex::MultiFab* z_phys_nd = nullptr;
239 
241  int klo = 0;
242  bool moist = false;
243  bool has_lsm = false;
244 };
245 
246 // Fill any requested unified 2-m components. A component index below zero
247 // means that the caller did not select that field. A source-only request uses
248 // the temperature path; provenance paired with humidity describes humidity
249 // without adding a temperature prerequisite. Dry runs retain a meaningful
250 // temperature source while humidity remains unavailable.
251 void fill (amrex::MultiFab& dst,
252  int temperature_comp,
253  int mixing_ratio_comp,
254  int source_comp,
255  const Sources& sources,
256  amrex::Real missing_value = amrex::Real(-999.0));
257 
258 // Inputs to the 10 m wind: the surface layer's friction velocity, roughness height
259 // and Obukhov length set the speed; the grid-relative wind at the centre of the first
260 // cell sets the direction; cos_alpha and sin_alpha, where given, turn it earth-relative.
262 {
263  const amrex::MultiFab* u_star = nullptr;
264  const amrex::MultiFab* roughness_height = nullptr;
265  const amrex::MultiFab* obukhov_length = nullptr;
266  const amrex::MultiFab* xvel = nullptr;
267  const amrex::MultiFab* yvel = nullptr;
268  const amrex::MultiFab* cos_alpha = nullptr;
269  const amrex::MultiFab* sin_alpha = nullptr;
270  int klo = 0;
271 };
272 
273 // Height of the wind diagnostics above the local surface
275 
276 // Fill any requested 10 m wind components: grid-relative u and v, and earth-relative
277 // u_east and v_north. A component index below zero means that field was not
278 // requested. Where the surface layer is absent or its state cannot be evaluated at
279 // 10 m (for example z0 >= 10 m), every requested component is missing_value.
280 void fill_wind_10m (amrex::MultiFab& dst,
281  int u_comp,
282  int v_comp,
283  int u_east_comp,
284  int v_north_comp,
285  const WindSources& sources,
286  amrex::Real missing_value = amrex::Real(-999.0));
287 
288 } // namespace near_surface_diagnostics
289 
290 #endif
constexpr amrex::Real KAPPA
Definition: ERF_Constants.H:71
constexpr amrex::Real lsm_undefined
Definition: ERF_Constants.H:28
Real height
Definition: ERF_InitCustomPert_SquallLine.H:37
amrex::Real Real
Definition: ERF_ShocInterface.H:19
Definition: ERF_NearSurfaceDiagnostics.cpp:15
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE bool native_vegetated_tile_is_used(amrex::Real vegetation_fraction) noexcept
Definition: ERF_NearSurfaceDiagnostics.H:83
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE bool native_bundle_vegetation_fraction_is_valid(const NativeBundle &bundle) noexcept
Definition: ERF_NearSurfaceDiagnostics.H:123
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE bool native_bundle_temperature_is_valid(const NativeBundle &bundle) noexcept
Definition: ERF_NearSurfaceDiagnostics.H:95
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE NativeAggregate aggregate_native_bundle(const NativeBundle &bundle) noexcept
Definition: ERF_NearSurfaceDiagnostics.H:131
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE bool native_bundle_is_valid(const NativeBundle &bundle) noexcept
Definition: ERF_NearSurfaceDiagnostics.H:71
constexpr amrex::Real wind_height
Definition: ERF_NearSurfaceDiagnostics.H:274
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE bool native_bare_tile_is_used(amrex::Real vegetation_fraction) noexcept
Definition: ERF_NearSurfaceDiagnostics.H:89
void fill(MultiFab &dst, int temperature_comp, int mixing_ratio_comp, int source_comp, const Sources &sources, Real missing_value)
Definition: ERF_NearSurfaceDiagnostics.cpp:42
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE bool evaluate_most_momentum_factor(amrex::Real roughness_height, amrex::Real obukhov_length, amrex::Real height, amrex::Real &factor) noexcept
Definition: ERF_NearSurfaceDiagnostics.H:176
void fill_wind_10m(MultiFab &dst, int u_comp, int v_comp, int u_east_comp, int v_north_comp, const WindSources &sources, Real missing_value)
Definition: ERF_NearSurfaceDiagnostics.cpp:222
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE bool native_bundle_mixing_ratio_is_valid(const NativeBundle &bundle) noexcept
Definition: ERF_NearSurfaceDiagnostics.H:109
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real most_profile_zeta_max() noexcept
Definition: ERF_NearSurfaceDiagnostics.H:152
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE bool valid_noahmp_component(amrex::Real value) noexcept
Definition: ERF_NearSurfaceDiagnostics.H:55
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE bool evaluate_most_profile(const MostState &state, amrex::Real height, MostProfile &profile) noexcept
Definition: ERF_NearSurfaceDiagnostics.H:197
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE bool valid_real(amrex::Real value) noexcept
Definition: ERF_NearSurfaceDiagnostics.H:49
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE bool evaluate_most_factor(const MostState &state, amrex::Real height, amrex::Real &factor) noexcept
Definition: ERF_NearSurfaceDiagnostics.H:155
Definition: ERF_NearSurfaceDiagnostics.H:43
amrex::Real potential_temperature
Definition: ERF_NearSurfaceDiagnostics.H:44
amrex::Real mixing_ratio
Definition: ERF_NearSurfaceDiagnostics.H:45
Definition: ERF_NearSurfaceDiagnostics.H:33
amrex::Real mixing_ratio_star
Definition: ERF_NearSurfaceDiagnostics.H:37
amrex::Real theta_star
Definition: ERF_NearSurfaceDiagnostics.H:35
amrex::Real theta_surface
Definition: ERF_NearSurfaceDiagnostics.H:34
amrex::Real obukhov_length
Definition: ERF_NearSurfaceDiagnostics.H:39
amrex::Real mixing_ratio_surface
Definition: ERF_NearSurfaceDiagnostics.H:36
amrex::Real roughness_height
Definition: ERF_NearSurfaceDiagnostics.H:38
Definition: ERF_NearSurfaceDiagnostics.H:27
amrex::Real temperature
Definition: ERF_NearSurfaceDiagnostics.H:28
amrex::Real mixing_ratio
Definition: ERF_NearSurfaceDiagnostics.H:29
Definition: ERF_NearSurfaceDiagnostics.H:18
amrex::Real mixing_ratio_vegetated
Definition: ERF_NearSurfaceDiagnostics.H:21
amrex::Real temperature_vegetated
Definition: ERF_NearSurfaceDiagnostics.H:19
amrex::Real vegetation_fraction
Definition: ERF_NearSurfaceDiagnostics.H:23
amrex::Real mixing_ratio_bare
Definition: ERF_NearSurfaceDiagnostics.H:22
amrex::Real temperature_bare
Definition: ERF_NearSurfaceDiagnostics.H:20
Definition: ERF_NearSurfaceDiagnostics.H:222
const amrex::MultiFab * theta_surface
Definition: ERF_NearSurfaceDiagnostics.H:229
const amrex::MultiFab * z_phys_nd
Definition: ERF_NearSurfaceDiagnostics.H:238
bool moist
Definition: ERF_NearSurfaceDiagnostics.H:242
bool has_lsm
Definition: ERF_NearSurfaceDiagnostics.H:243
int klo
Definition: ERF_NearSurfaceDiagnostics.H:241
const amrex::iMultiFab * land_mask
Definition: ERF_NearSurfaceDiagnostics.H:236
const amrex::MultiFab * obukhov_length
Definition: ERF_NearSurfaceDiagnostics.H:234
const amrex::MultiFab * roughness_height
Definition: ERF_NearSurfaceDiagnostics.H:233
const amrex::MultiFab * native_temperature_bare
Definition: ERF_NearSurfaceDiagnostics.H:224
const amrex::MultiFab * cons
Definition: ERF_NearSurfaceDiagnostics.H:237
const amrex::MultiFab * theta_star
Definition: ERF_NearSurfaceDiagnostics.H:230
const amrex::MultiFab * native_temperature_vegetated
Definition: ERF_NearSurfaceDiagnostics.H:223
const amrex::MultiFab * mixing_ratio_surface
Definition: ERF_NearSurfaceDiagnostics.H:231
const amrex::MultiFab * native_mixing_ratio_bare
Definition: ERF_NearSurfaceDiagnostics.H:226
amrex::Real dz
Definition: ERF_NearSurfaceDiagnostics.H:240
const amrex::MultiFab * source_mask
Definition: ERF_NearSurfaceDiagnostics.H:235
const amrex::MultiFab * mixing_ratio_star
Definition: ERF_NearSurfaceDiagnostics.H:232
const amrex::MultiFab * native_mixing_ratio_vegetated
Definition: ERF_NearSurfaceDiagnostics.H:225
const amrex::MultiFab * native_vegetation_fraction
Definition: ERF_NearSurfaceDiagnostics.H:227
Definition: ERF_NearSurfaceDiagnostics.H:262
const amrex::MultiFab * u_star
Definition: ERF_NearSurfaceDiagnostics.H:263
const amrex::MultiFab * yvel
Definition: ERF_NearSurfaceDiagnostics.H:267
const amrex::MultiFab * cos_alpha
Definition: ERF_NearSurfaceDiagnostics.H:268
const amrex::MultiFab * roughness_height
Definition: ERF_NearSurfaceDiagnostics.H:264
const amrex::MultiFab * xvel
Definition: ERF_NearSurfaceDiagnostics.H:266
const amrex::MultiFab * sin_alpha
Definition: ERF_NearSurfaceDiagnostics.H:269
const amrex::MultiFab * obukhov_length
Definition: ERF_NearSurfaceDiagnostics.H:265
int klo
Definition: ERF_NearSurfaceDiagnostics.H:270
Definition: ERF_MOSTUtils.H:67