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 
16 // Positive-argument gamma wrapper. std::lgamma returns log(abs(Gamma(x))) for
17 // negative non-integers, so ERF keeps a positive-only contract here.
18 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
21  return std::exp(std::lgamma(x));
22 }
23 
24 // Saturation vapor pressure of ice [mbar / hPa].
25 //
26 // Flatau et al. (1992), Polynomial Fits to Saturation Vapor Pressure,
27 // J. Appl. Meteorol. Coefficients come from Table 4 and the value fit is valid
28 // over [-90, 0] C, i.e. down to about 183.16 K. ERF intentionally clamps the
29 // above-freezing branch to 0.
30 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
32  amrex::Real const a0 = amrex::Real(6.11147274);
33  amrex::Real const a1 = amrex::Real(0.503160820);
34  amrex::Real const a2 = amrex::Real(0.188439774e-1);
35  amrex::Real const a3 = amrex::Real(0.420895665e-3);
36  amrex::Real const a4 = amrex::Real(0.615021634e-5);
37  amrex::Real const a5 = amrex::Real(0.602588177e-7);
38  amrex::Real const a6 = amrex::Real(0.385852041e-9);
39  amrex::Real const a7 = amrex::Real(0.146898966e-11);
40  amrex::Real const a8 = amrex::Real(0.252751365e-14);
41 
42  amrex::Real dtt = t-amrex::Real(273.16);
44  AMREX_ALWAYS_ASSERT(dtt >= -amrex::Real(90.0) - tol);
45 
46  amrex::Real esati;
47  if (dtt > amrex::Real(0)) {
48  esati = amrex::Real(0);
49  } else {
50  esati = a0 + dtt*(a1+dtt*(a2+dtt*(a3+dtt*(a4+dtt*(a5+dtt*(a6+dtt*(a7+a8*dtt)))))));
51  }
52  return esati;
53 }
54 
55 // Magnus-style exponential saturation-pressure approximation over water
56 // [mbar / hPa]. This closed-form formula is commonly motivated by
57 // simplified integrations of the Clausius-Clapeyron relation, but ERF
58 // evaluates the empirical exponential expression below rather than
59 // performing a direct Clausius-Clapeyron integration. The `_cc` suffix is
60 // retained for API compatibility.
61 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
63  constexpr amrex::Real svp1 = amrex::Real(0.6112);
64  constexpr amrex::Real svp2 = amrex::Real(17.67);
65  constexpr amrex::Real svp3 = amrex::Real(29.65);
66  constexpr amrex::Real svpt0 = amrex::Real(273.15);
67  amrex::Real esatw = amrex::Real(10.0) * svp1 * std::exp(svp2 * (t - svpt0) / (t - svp3));
68  return esatw;
69 }
70 
71 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
73 {
74  amrex::Real const a0 = amrex::Real(6.11239921);
75  amrex::Real const a1 = amrex::Real(0.443987641);
76  amrex::Real const a2 = amrex::Real(0.142986287e-1);
77  amrex::Real const a3 = amrex::Real(0.264847430e-3);
78  amrex::Real const a4 = amrex::Real(0.302950461e-5);
79  amrex::Real const a5 = amrex::Real(0.206739458e-7);
80  amrex::Real const a6 = amrex::Real(0.640689451e-10);
81  amrex::Real const a7 = -amrex::Real(0.952447341e-13);
82  amrex::Real const a8 = -amrex::Real(0.976195544e-15);
83 
84  return a0 + dtt*(a1+dtt*(a2+dtt*(a3+dtt*(a4+dtt*(a5+dtt*(a6+dtt*(a7+a8*dtt)))))));
85 }
86 
87 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
89 {
90  amrex::Real const a0 = amrex::Real(0.443956472);
91  amrex::Real const a1 = amrex::Real(0.285976452e-1);
92  amrex::Real const a2 = amrex::Real(0.794747212e-3);
93  amrex::Real const a3 = amrex::Real(0.121167162e-4);
94  amrex::Real const a4 = amrex::Real(0.103167413e-6);
95  amrex::Real const a5 = amrex::Real(0.385208005e-9);
96  amrex::Real const a6 = -amrex::Real(0.604119582e-12);
97  amrex::Real const a7 = -amrex::Real(0.792933209e-14);
98  amrex::Real const a8 = -amrex::Real(0.599634321e-17);
99 
100  return a0 + dtt*(a1+dtt*(a2+dtt*(a3+dtt*(a4+dtt*(a5+dtt*(a6+dtt*(a7+a8*dtt)))))));
101 }
102 
103 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
105 {
106  amrex::Real const dtt = t - amrex::Real(273.16);
107  amrex::Real const poly_min_dtt = -amrex::Real(70.0);
108  return dtt > poly_min_dtt && dtt < amrex::Real(70.0) &&
110 }
111 
112 // Saturation vapor pressure of water vapor [mbar / hPa].
113 //
114 // Flatau et al. (1992), Polynomial Fits to Saturation Vapor Pressure,
115 // J. Appl. Meteorol. The default branch uses the Flatau polynomial only on the
116 // current positive in-range interval selected by erf_use_positive_esatw_poly,
117 // currently about [-70, 70] C. Outside that interval, or whenever the
118 // polynomial would be non-positive, it falls back to the Magnus-style
119 // exponential approximation. The optional empirical fit is originally in Pa;
120 // ERF converts it to mbar / hPa before returning so both warm-water branches
121 // use the same units.
122 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
124 {
125  if (use_empirical) {
126  return amrex::Real(0.01) *
127  std::exp(amrex::Real(34.494) - amrex::Real(4924.99)/(t - amrex::Real(273.15) + amrex::Real(237.1))) /
128  std::pow(t - amrex::Real(273.15) + amrex::Real(105.0), amrex::Real(1.57));
129  }
130 
132  return erf_esatw_flatau_poly(t - amrex::Real(273.16));
133  }
134 
135  return erf_esatw_cc(t);
136 }
137 
138 // Flatau et al. (1992), Polynomial Fits to Saturation Vapor Pressure,
139 // J. Appl. Meteorol. The derivative fit is valid over the narrower
140 // [-85, 0] C interval, i.e. down to about 188.16 K. ERF intentionally returns
141 // 0 above freezing to match erf_esati.
142 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
144  amrex::Real const a0 = amrex::Real(0.503223089);
145  amrex::Real const a1 = amrex::Real(0.377174432e-1);
146  amrex::Real const a2 = amrex::Real(0.126710138e-2);
147  amrex::Real const a3 = amrex::Real(0.249065913e-4);
148  amrex::Real const a4 = amrex::Real(0.312668753e-6);
149  amrex::Real const a5 = amrex::Real(0.255653718e-8);
150  amrex::Real const a6 = amrex::Real(0.132073448e-10);
151  amrex::Real const a7 = amrex::Real(0.390204672e-13);
152  amrex::Real const a8 = amrex::Real(0.497275778e-16);
153 
154  amrex::Real dtt = t-amrex::Real(273.16);
156  AMREX_ALWAYS_ASSERT(dtt >= -amrex::Real(85.0) - tol);
157  amrex::Real dtesati;
158  if (dtt > amrex::Real(0)) {
159  dtesati = amrex::Real(0);
160  } else {
161  dtesati = a0 + dtt*(a1+dtt*(a2+dtt*(a3+dtt*(a4+dtt*(a5+dtt*(a6+dtt*(a7+a8*dtt)))))));
162  }
163  return dtesati;
164 }
165 
166 // Temperature derivative of the Magnus-style exponential water
167 // saturation-pressure approximation [mbar / hPa / K]. This differentiates
168 // the closed-form formula in erf_esatw_cc.
169 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
171  constexpr amrex::Real svp1 = amrex::Real(0.6112);
172  constexpr amrex::Real svp2 = amrex::Real(17.67);
173  constexpr amrex::Real svp3 = amrex::Real(29.65);
174  constexpr amrex::Real svpt0 = amrex::Real(273.15);
175  amrex::Real dtesatw = amrex::Real(10.0) * svp1 * svp2 * std::exp(svp2 * (t - svpt0) / (t - svp3))
176  * (svpt0 - svp3) / ((t - svp3) * (t - svp3));
177  return dtesatw;
178 }
179 
180 // Flatau et al. (1992), Polynomial Fits to Saturation Vapor Pressure,
181 // J. Appl. Meteorol. Use the same branch selection as erf_esatw so the value
182 // and derivative stay on the same function. Small derivative jumps can remain
183 // at the switch because the Flatau polynomial and Magnus-style exponential
184 // approximation are independent fits rather than a matched composite model.
185 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
188  return erf_dtesatw_flatau_poly(t - amrex::Real(273.16));
189  }
190 
191  return erf_dtesatw_cc(t);
192 }
193 
194 // Saturation mixing-ratio helper for a precomputed saturation pressure. qsat is
195 // capped at Rd_on_Rv whenever esat > p/2, i.e. when max(esat, p - esat) = esat.
196 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
198 {
199  return Rd_on_Rv * esat / std::max(esat, p - esat);
200 }
201 
202 // Temperature derivative of the capped saturation-mixing-ratio helper. The
203 // derivative is zero on the capped branch where qsat == Rd_on_Rv.
204 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
206 {
207  if ((p - esat) >= esat) {
208  amrex::Real const denom = p - esat;
209  return Rd_on_Rv * dtesat * p / (denom * denom);
210  }
211 
212  return amrex::Real(0);
213 }
214 
215 // Saturated ice vapor mixing ratio [-]. Input temperature is in K and input
216 // pressure is in mbar. The result is capped at Rd_on_Rv when esat > p/2.
217 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
219  qsati = erf_qsat_from_esat(erf_esati(t), p);
220 }
221 
222 /* saturated water vapor mixing ratio
223  * t: temperature [K]
224  * p: pressure [mbar]
225  * return value is capped at Rd_on_Rv when esat > p/2.
226  */
227 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
229  qsatw = erf_qsat_from_esat(erf_esatw(t), p);
230 }
231 
232 // Temperature derivative of the capped erf_qsati relation. This is zero on the
233 // capped branch where qsat == Rd_on_Rv.
234 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
236  amrex::Real esati = erf_esati(t);
237  amrex::Real dtesati = erf_dtesati(t);
238  dtqsati = erf_dtqsat_from_esat(esati, dtesati, p);
239 }
240 
241 // Temperature derivative of the capped erf_qsatw relation. This is zero on the
242 // capped branch where qsat == Rd_on_Rv.
243 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
245  amrex::Real esatw = erf_esatw(t);
246  amrex::Real dtesatw = erf_dtesatw(t);
247  dtqsatw = erf_dtqsat_from_esat(esatw, dtesatw, p);
248 }
249 #endif
constexpr amrex::Real Rd_on_Rv
Definition: ERF_Constants.H:98
AMREX_ALWAYS_ASSERT(bx.length()[2]==khi+1)
bool use_empirical
Definition: ERF_InitCustomPert_MultiSpeciesBubble.H:25
Real * t
Definition: ERF_InitCustomPert_SquallLine.H:60
Real * p
Definition: ERF_InitCustomPert_SquallLine.H:61
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE bool erf_use_positive_esatw_poly(amrex::Real t)
Definition: ERF_MicrophysicsUtils.H:104
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real erf_dtesatw(amrex::Real t)
Definition: ERF_MicrophysicsUtils.H:186
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void erf_dtqsatw(amrex::Real t, amrex::Real p, amrex::Real &dtqsatw)
Definition: ERF_MicrophysicsUtils.H:244
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real erf_dtesati(amrex::Real t)
Definition: ERF_MicrophysicsUtils.H:143
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real erf_esatw_flatau_poly(amrex::Real dtt)
Definition: ERF_MicrophysicsUtils.H:72
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real erf_dtesatw_flatau_poly(amrex::Real dtt)
Definition: ERF_MicrophysicsUtils.H:88
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real erf_esati(amrex::Real t)
Definition: ERF_MicrophysicsUtils.H:31
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real erf_dtesatw_cc(amrex::Real t)
Definition: ERF_MicrophysicsUtils.H:170
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real erf_qsat_from_esat(amrex::Real esat, amrex::Real p)
Definition: ERF_MicrophysicsUtils.H:197
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void erf_dtqsati(amrex::Real t, amrex::Real p, amrex::Real &dtqsati)
Definition: ERF_MicrophysicsUtils.H:235
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void erf_qsatw(amrex::Real t, amrex::Real p, amrex::Real &qsatw)
Definition: ERF_MicrophysicsUtils.H:228
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real erf_gammafff(amrex::Real x)
Definition: ERF_MicrophysicsUtils.H:19
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:205
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void erf_qsati(amrex::Real t, amrex::Real p, amrex::Real &qsati)
Definition: ERF_MicrophysicsUtils.H:218
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real erf_esatw(amrex::Real t, bool use_empirical=false)
Definition: ERF_MicrophysicsUtils.H:123
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real erf_esatw_cc(amrex::Real t)
Definition: ERF_MicrophysicsUtils.H:62
amrex::Real Real
Definition: ERF_ShocInterface.H:19
real(c_double), parameter svp1
Definition: ERF_module_model_constants.F90:78
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