ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
ERF_MicrophysicsUtils.H
Go to the documentation of this file.
1 /*
2  * utility tools for microphysics
3  *
4  */
5 #ifndef ERF_Microphysics_Utils_H
6 #define ERF_Microphysics_Utils_H
7 
8 #include <algorithm>
9 #include <cmath>
10 #include <limits>
11 #include <vector>
12 #include <AMReX_REAL.H>
13 #include <AMReX_Array.H>
14 #include <ERF_Constants.H>
15 #include "ERF_EOS.H"
16 
17 /**
18  * Thermodynamic state passed from ERF into an Eulerian microphysics scheme.
19  * Pressure is always in Pa here; schemes with legacy mbar storage convert it
20  * once at their copy-in boundary.
21  */
25 };
26 
27 /**
28  * Diagnose the state used by one Eulerian microphysics cell.
29  *
30  * In anelastic mode, the conserved state supplies theta while the hydrostatic
31  * reference pressure supplied by BaseState supplies the thermodynamic pressure.
32  * In compressible mode, retain ERF's existing local-EOS diagnosis.
33  */
34 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
36  const amrex::Real rho,
37  const amrex::Real rho_theta,
38  const amrex::Real qv,
39  const amrex::Real rdOcp,
40  const bool use_anelastic_reference_pressure,
41  const amrex::Real p0) noexcept
42 {
43  if (use_anelastic_reference_pressure) {
44  const amrex::Real theta = rho_theta / rho;
45  return {p0, getTgivenPandTh(p0, theta, rdOcp)};
46  }
47 
48  return {getPgivenRTh(rho_theta, qv),
49  getTgivenRandRTh(rho, rho_theta, qv)};
50 }
51 
52 // Positive-argument gamma wrapper. std::lgamma returns log(abs(Gamma(x))) for
53 // negative non-integers, so ERF keeps a positive-only contract here.
54 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
57  return std::exp(std::lgamma(x));
58 }
59 
60 // Saturation vapor pressure of ice [mbar / hPa].
61 //
62 // Flatau et al. (1992), Polynomial Fits to Saturation Vapor Pressure,
63 // J. Appl. Meteorol. Coefficients come from Table 4 and the value fit is valid
64 // over [-90, 0] C, i.e. down to about 183.16 K. ERF intentionally clamps the
65 // above-freezing branch to 0.
66 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
68  amrex::Real const a0 = amrex::Real(6.11147274);
69  amrex::Real const a1 = amrex::Real(0.503160820);
70  amrex::Real const a2 = amrex::Real(0.188439774e-1);
71  amrex::Real const a3 = amrex::Real(0.420895665e-3);
72  amrex::Real const a4 = amrex::Real(0.615021634e-5);
73  amrex::Real const a5 = amrex::Real(0.602588177e-7);
74  amrex::Real const a6 = amrex::Real(0.385852041e-9);
75  amrex::Real const a7 = amrex::Real(0.146898966e-11);
76  amrex::Real const a8 = amrex::Real(0.252751365e-14);
77 
78  amrex::Real dtt = t-amrex::Real(273.16);
80  AMREX_ALWAYS_ASSERT(dtt >= -amrex::Real(90.0) - tol);
81 
82  amrex::Real esati;
83  if (dtt > amrex::Real(0)) {
84  esati = amrex::Real(0);
85  } else {
86  esati = a0 + dtt*(a1+dtt*(a2+dtt*(a3+dtt*(a4+dtt*(a5+dtt*(a6+dtt*(a7+a8*dtt)))))));
87  }
88  return esati;
89 }
90 
91 // Magnus-style exponential saturation-pressure approximation over water
92 // [mbar / hPa]. This closed-form formula is commonly motivated by
93 // simplified integrations of the Clausius-Clapeyron relation, but ERF
94 // evaluates the empirical exponential expression below rather than
95 // performing a direct Clausius-Clapeyron integration. The `_cc` suffix is
96 // retained for API compatibility.
97 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
99  constexpr amrex::Real svp1 = amrex::Real(0.6112);
100  constexpr amrex::Real svp2 = amrex::Real(17.67);
101  constexpr amrex::Real svp3 = amrex::Real(29.65);
102  constexpr amrex::Real svpt0 = amrex::Real(273.15);
103  amrex::Real esatw = amrex::Real(10.0) * svp1 * std::exp(svp2 * (t - svpt0) / (t - svp3));
104  return esatw;
105 }
106 
107 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
109 {
110  amrex::Real const a0 = amrex::Real(6.11239921);
111  amrex::Real const a1 = amrex::Real(0.443987641);
112  amrex::Real const a2 = amrex::Real(0.142986287e-1);
113  amrex::Real const a3 = amrex::Real(0.264847430e-3);
114  amrex::Real const a4 = amrex::Real(0.302950461e-5);
115  amrex::Real const a5 = amrex::Real(0.206739458e-7);
116  amrex::Real const a6 = amrex::Real(0.640689451e-10);
117  amrex::Real const a7 = -amrex::Real(0.952447341e-13);
118  amrex::Real const a8 = -amrex::Real(0.976195544e-15);
119 
120  return a0 + dtt*(a1+dtt*(a2+dtt*(a3+dtt*(a4+dtt*(a5+dtt*(a6+dtt*(a7+a8*dtt)))))));
121 }
122 
123 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
125 {
126  amrex::Real const a0 = amrex::Real(0.443956472);
127  amrex::Real const a1 = amrex::Real(0.285976452e-1);
128  amrex::Real const a2 = amrex::Real(0.794747212e-3);
129  amrex::Real const a3 = amrex::Real(0.121167162e-4);
130  amrex::Real const a4 = amrex::Real(0.103167413e-6);
131  amrex::Real const a5 = amrex::Real(0.385208005e-9);
132  amrex::Real const a6 = -amrex::Real(0.604119582e-12);
133  amrex::Real const a7 = -amrex::Real(0.792933209e-14);
134  amrex::Real const a8 = -amrex::Real(0.599634321e-17);
135 
136  return a0 + dtt*(a1+dtt*(a2+dtt*(a3+dtt*(a4+dtt*(a5+dtt*(a6+dtt*(a7+a8*dtt)))))));
137 }
138 
139 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
141 {
142  amrex::Real const dtt = t - amrex::Real(273.16);
143  amrex::Real const poly_min_dtt = -amrex::Real(70.0);
144  return dtt > poly_min_dtt && dtt < amrex::Real(70.0) &&
146 }
147 
148 // Saturation vapor pressure of water vapor [mbar / hPa].
149 //
150 // Flatau et al. (1992), Polynomial Fits to Saturation Vapor Pressure,
151 // J. Appl. Meteorol. The default branch uses the Flatau polynomial only on the
152 // current positive in-range interval selected by erf_use_positive_esatw_poly,
153 // currently about [-70, 70] C. Outside that interval, or whenever the
154 // polynomial would be non-positive, it falls back to the Magnus-style
155 // exponential approximation. The optional empirical fit is originally in Pa;
156 // ERF converts it to mbar / hPa before returning so both warm-water branches
157 // use the same units.
158 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
160 {
161  if (use_empirical) {
162  return amrex::Real(0.01) *
163  std::exp(amrex::Real(34.494) - amrex::Real(4924.99)/(t - amrex::Real(273.15) + amrex::Real(237.1))) /
164  std::pow(t - amrex::Real(273.15) + amrex::Real(105.0), amrex::Real(1.57));
165  }
166 
168  return erf_esatw_flatau_poly(t - amrex::Real(273.16));
169  }
170 
171  return erf_esatw_cc(t);
172 }
173 
174 // Flatau et al. (1992), Polynomial Fits to Saturation Vapor Pressure,
175 // J. Appl. Meteorol. The derivative fit is valid over the narrower
176 // [-85, 0] C interval, i.e. down to about 188.16 K. ERF intentionally returns
177 // 0 above freezing to match erf_esati.
178 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
180  amrex::Real const a0 = amrex::Real(0.503223089);
181  amrex::Real const a1 = amrex::Real(0.377174432e-1);
182  amrex::Real const a2 = amrex::Real(0.126710138e-2);
183  amrex::Real const a3 = amrex::Real(0.249065913e-4);
184  amrex::Real const a4 = amrex::Real(0.312668753e-6);
185  amrex::Real const a5 = amrex::Real(0.255653718e-8);
186  amrex::Real const a6 = amrex::Real(0.132073448e-10);
187  amrex::Real const a7 = amrex::Real(0.390204672e-13);
188  amrex::Real const a8 = amrex::Real(0.497275778e-16);
189 
190  amrex::Real dtt = t-amrex::Real(273.16);
192  AMREX_ALWAYS_ASSERT(dtt >= -amrex::Real(85.0) - tol);
193  amrex::Real dtesati;
194  if (dtt > amrex::Real(0)) {
195  dtesati = amrex::Real(0);
196  } else {
197  dtesati = a0 + dtt*(a1+dtt*(a2+dtt*(a3+dtt*(a4+dtt*(a5+dtt*(a6+dtt*(a7+a8*dtt)))))));
198  }
199  return dtesati;
200 }
201 
202 // Temperature derivative of the Magnus-style exponential water
203 // saturation-pressure approximation [mbar / hPa / K]. This differentiates
204 // the closed-form formula in erf_esatw_cc.
205 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
207  constexpr amrex::Real svp1 = amrex::Real(0.6112);
208  constexpr amrex::Real svp2 = amrex::Real(17.67);
209  constexpr amrex::Real svp3 = amrex::Real(29.65);
210  constexpr amrex::Real svpt0 = amrex::Real(273.15);
211  amrex::Real dtesatw = amrex::Real(10.0) * svp1 * svp2 * std::exp(svp2 * (t - svpt0) / (t - svp3))
212  * (svpt0 - svp3) / ((t - svp3) * (t - svp3));
213  return dtesatw;
214 }
215 
216 // Flatau et al. (1992), Polynomial Fits to Saturation Vapor Pressure,
217 // J. Appl. Meteorol. Use the same branch selection as erf_esatw so the value
218 // and derivative stay on the same function. Small derivative jumps can remain
219 // at the switch because the Flatau polynomial and Magnus-style exponential
220 // approximation are independent fits rather than a matched composite model.
221 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
224  return erf_dtesatw_flatau_poly(t - amrex::Real(273.16));
225  }
226 
227  return erf_dtesatw_cc(t);
228 }
229 
230 // Saturation mixing-ratio helper for a precomputed saturation pressure. qsat is
231 // capped at RdoRv whenever esat > p/2, i.e. when max(esat, p - esat) = esat.
232 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
234 {
235  return RdoRv * esat / std::max(esat, p - esat);
236 }
237 
238 // Temperature derivative of the capped saturation-mixing-ratio helper. The
239 // derivative is zero on the capped branch where qsat == RdoRv.
240 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
242 {
243  if ((p - esat) >= esat) {
244  amrex::Real const denom = p - esat;
245  return RdoRv * dtesat * p / (denom * denom);
246  }
247 
248  return amrex::Real(0);
249 }
250 
251 // Saturated ice vapor mixing ratio [-]. Input temperature is in K and input
252 // pressure is in mbar. The result is capped at RdoRv when esat > p/2.
253 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
256 }
257 
258 /* saturated water vapor mixing ratio
259  * t: temperature [K]
260  * p: pressure [mbar]
261  * return value is capped at RdoRv when esat > p/2.
262  */
263 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
266 }
267 
268 // Temperature derivative of the capped erf_qsati relation. This is zero on the
269 // capped branch where qsat == RdoRv.
270 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
272  amrex::Real esati = erf_esati(t);
273  amrex::Real dtesati = erf_dtesati(t);
274  dtqsati = erf_dtqsat_from_esat(esati, dtesati, p);
275 }
276 
277 // Temperature derivative of the capped erf_qsatw relation. This is zero on the
278 // capped branch where qsat == RdoRv.
279 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
281  amrex::Real esatw = erf_esatw(t);
282  amrex::Real dtesatw = erf_dtesatw(t);
283  dtqsatw = erf_dtqsat_from_esat(esatw, dtesatw, p);
284 }
285 #endif
constexpr amrex::Real RdoRv
Definition: ERF_Constants.H:44
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real getTgivenRandRTh(const amrex::Real rho, const amrex::Real rhotheta, const amrex::Real qv=amrex::Real(0))
Definition: ERF_EOS.H:46
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real getPgivenRTh(const amrex::Real rhotheta, const amrex::Real qv=amrex::Real(0))
Definition: ERF_EOS.H:81
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real getTgivenPandTh(const amrex::Real P, const amrex::Real th, const amrex::Real rdOcp)
Definition: ERF_EOS.H:32
const Real rdOcp
Definition: ERF_InitCustomPert_ABL.H:72
AMREX_ALWAYS_ASSERT(bx.length()[2]==khi+1)
bool use_empirical
Definition: ERF_InitCustomPert_MultiSpeciesBubble.H:25
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE bool erf_use_positive_esatw_poly(amrex::Real t)
Definition: ERF_MicrophysicsUtils.H:140
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real erf_dtesatw(amrex::Real t)
Definition: ERF_MicrophysicsUtils.H:222
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void erf_dtqsatw(amrex::Real t, amrex::Real p, amrex::Real &dtqsatw)
Definition: ERF_MicrophysicsUtils.H:280
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real erf_dtesati(amrex::Real t)
Definition: ERF_MicrophysicsUtils.H:179
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real erf_esatw_flatau_poly(amrex::Real dtt)
Definition: ERF_MicrophysicsUtils.H:108
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real erf_dtesatw_flatau_poly(amrex::Real dtt)
Definition: ERF_MicrophysicsUtils.H:124
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real erf_esati(amrex::Real t)
Definition: ERF_MicrophysicsUtils.H:67
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE MicrophysicsThermoState diagnose_microphysics_thermo_state(const amrex::Real rho, const amrex::Real rho_theta, const amrex::Real qv, const amrex::Real rdOcp, const bool use_anelastic_reference_pressure, const amrex::Real p0) noexcept
Definition: ERF_MicrophysicsUtils.H:35
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real erf_dtesatw_cc(amrex::Real t)
Definition: ERF_MicrophysicsUtils.H:206
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real erf_qsat_from_esat(amrex::Real esat, amrex::Real p)
Definition: ERF_MicrophysicsUtils.H:233
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void erf_dtqsati(amrex::Real t, amrex::Real p, amrex::Real &dtqsati)
Definition: ERF_MicrophysicsUtils.H:271
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void erf_qsatw(amrex::Real t, amrex::Real p, amrex::Real &qsatw)
Definition: ERF_MicrophysicsUtils.H:264
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real erf_gammafff(amrex::Real x)
Definition: ERF_MicrophysicsUtils.H:55
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real erf_dtqsat_from_esat(amrex::Real esat, amrex::Real dtesat, amrex::Real p)
Definition: ERF_MicrophysicsUtils.H:241
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void erf_qsati(amrex::Real t, amrex::Real p, amrex::Real &qsati)
Definition: ERF_MicrophysicsUtils.H:254
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real erf_esatw(amrex::Real t, bool use_empirical=false)
Definition: ERF_MicrophysicsUtils.H:159
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real erf_esatw_cc(amrex::Real t)
Definition: ERF_MicrophysicsUtils.H:98
amrex::Real Real
Definition: ERF_ShocInterface.H:19
@ theta
Definition: ERF_SLM.H:19
@ rho
Definition: ERF_Kessler.H:25
@ qv
Definition: ERF_Kessler.H:31
@ qsatw
Definition: ERF_WSM6.H:340
@ qsati
Definition: ERF_WSM6.H:340
@ t
Definition: ERF_WSM6.H:272
@ p
Definition: ERF_WSM6.H:280
real(c_double), parameter svp1
Definition: ERF_module_model_constants.F90:78
real(c_double), parameter p0
Definition: ERF_module_model_constants.F90:40
real(c_double), parameter epsilon
Definition: ERF_module_model_constants.F90:12
real(c_double), parameter a2
Definition: ERF_module_model_constants.F90:95
real(c_double), parameter a3
Definition: ERF_module_model_constants.F90:96
real(c_double), parameter svp3
Definition: ERF_module_model_constants.F90:80
real(c_double), parameter svp2
Definition: ERF_module_model_constants.F90:79
real(c_double), parameter a4
Definition: ERF_module_model_constants.F90:97
real(c_double), parameter svpt0
Definition: ERF_module_model_constants.F90:81
Definition: ERF_MicrophysicsUtils.H:22
amrex::Real pressure_pa
Definition: ERF_MicrophysicsUtils.H:23
amrex::Real temperature
Definition: ERF_MicrophysicsUtils.H:24