ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
ERF_KesslerUtils.H
Go to the documentation of this file.
1 #ifndef ERF_KESSLER_UTILS_H_
2 #define ERF_KESSLER_UTILS_H_
3 
4 #include <algorithm>
5 #include <cmath>
6 #include <limits>
7 
8 #include <AMReX_GpuQualifiers.H>
9 #include <AMReX_REAL.H>
10 
11 #include <ERF_NumericalConstants.H>
13 #include <ERF_MicrophysicsUtils.H>
14 
20 };
21 
25 };
26 
30 };
31 
35 
36 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
37 int kessler_face_donor_k (const int k,
38  const int k_hi) noexcept
39 {
40  return (k == k_hi + 1) ? k - 1 : k;
41 }
42 
43 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
45  const amrex::Real qp) noexcept
46 {
47  // MUST MATCH: current AdvanceKessler terminal-velocity coefficients.
48  return amrex::Real(36.34)
49  * std::pow(rho * amrex::Real(0.001) * qp, amrex::Real(0.1346))
50  * std::pow(rho / amrex::Real(1.16), -myhalf);
51 }
52 
53 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
55  const amrex::Real terminal_velocity,
56  const amrex::Real qp) noexcept
57 {
58  return rho * terminal_velocity * qp;
59 }
60 
61 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
62 int kessler_num_sedimentation_substeps (const amrex::Real current_reduced_value,
63  const amrex::Real dt,
64  const amrex::Real dzmin) noexcept
65 {
66  // MUST MATCH: current AdvanceKessler CFL_MAX precipitation substep formula.
67  return static_cast<int>(std::ceil((current_reduced_value + std::numeric_limits<amrex::Real>::epsilon())
68  * (dt / dzmin) / kessler_sedimentation_cfl_max));
69 }
70 
71 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
72 amrex::Real kessler_rain_accumulation_increment (const amrex::Real precip_mass_per_area) noexcept
73 {
74  // Convert precipitation mass per unit area [kg m^-2] to liquid-water depth
75  // [mm]: divide by water density [kg m^-3], then multiply by mm per meter.
76  return precip_mass_per_area
79 }
80 
81 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
83 {
84  // MUST MATCH: current AdvanceKessler sedimentation zero threshold literal.
85  return std::fabs(value) < 1e-14;
86 }
87 
88 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
90  const amrex::Real qc,
91  const amrex::Real qsat,
92  const amrex::Real dtqsat,
93  const bool do_cond,
94  const amrex::Real latent_over_cp) noexcept
95 {
97 
98  const amrex::Real fac = latent_over_cp * dtqsat;
99 
100  // MUST MATCH: current AdvanceKessler condensation branch behavior.
101  if ((qv > qsat) && do_cond) {
102  source_terms.dq_vapor_to_cloud = std::min(qv, (qv - qsat) / (amrex::Real(1) + fac));
103  }
104 
105  // MUST MATCH: current AdvanceKessler cloud-evaporation branch behavior.
106  if ((qv < qsat) && (qc > amrex::Real(0)) && do_cond) {
107  source_terms.dq_cloud_to_vapor = std::min(qc, (qsat - qv) / (amrex::Real(1) + fac));
108  }
109 
110  return source_terms;
111 }
112 
113 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
115  const int k_hi,
116  const amrex::Real rho_km1,
117  const amrex::Real rho_k,
118  const amrex::Real qp_km1,
119  const amrex::Real qp_k) noexcept
120 {
121  KesslerFaceState face_state{amrex::Real(0), amrex::Real(0)};
122 
123  // Sedimentation is one-way downward transport. Use a face-centered density
124  // for the z-face flux, but upwind the transported rain mixing ratio from the
125  // donor cell. A centered qp can create flux from downstream rain when the
126  // donor cell is dry. The top boundary face uses the top cell below the
127  // boundary.
128  if (k == k_hi + 1) {
129  face_state.rho = rho_km1;
130  face_state.qp = qp_km1;
131  } else if (k == 0) {
132  face_state.rho = rho_k;
133  face_state.qp = qp_k;
134  } else {
135  face_state.rho = amrex::Real(0.5) * (rho_km1 + rho_k);
136  face_state.qp = qp_k;
137  }
138 
139  // MUST MATCH: current AdvanceKessler nonnegative donor-qp clip before flux formation.
140  face_state.qp = std::max(amrex::Real(0), face_state.qp);
141  return face_state;
142 }
143 
144 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
146  const amrex::Real fz_lo,
147  const amrex::Real rho,
148  const amrex::Real dJinv,
149  const amrex::Real coef) noexcept
150 {
151  // MUST MATCH: current AdvanceKessler sedimentation tendency arithmetic order.
152  return dJinv * (amrex::Real(1) / rho) * (fz_hi - fz_lo) * coef;
153 }
154 
155 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
157  const amrex::Real qc,
158  const amrex::Real qp,
159  const amrex::Real rho,
160  const amrex::Real pressure_current_units,
161  const amrex::Real qsat,
162  const amrex::Real dtqsat,
163  const amrex::Real dt,
164  const bool do_cond,
165  const amrex::Real latent_over_cp) noexcept
166 {
167  // Units must match current AdvanceKessler: q* are mass fractions, rho is in
168  // the production density units currently carried by Kessler, pressure is in
169  // Kessler's mbar / hPa path for qsat helpers, and dt is in seconds.
170  const KesslerSaturationAdjustment saturation_adjustment =
171  kessler_saturation_adjustment(qv, qc, qsat, dtqsat, do_cond, latent_over_cp);
172  KesslerSourceTerms source_terms{
173  saturation_adjustment.dq_vapor_to_cloud,
174  saturation_adjustment.dq_cloud_to_vapor,
175  amrex::Real(0),
176  amrex::Real(0)};
177 
178  const amrex::Real capacity = amrex::max(
179  amrex::Real(0), (qsat - qv) / (amrex::Real(1) + latent_over_cp * dtqsat));
180  const amrex::Real remaining_capacity = amrex::max(
181  amrex::Real(0), capacity - saturation_adjustment.dq_cloud_to_vapor);
182  // Cloud evaporation gets first claim on the linearized subsaturation
183  // capacity. Rain evaporation can use only the remaining capacity.
184  const amrex::Real qv_eff = qv
185  - saturation_adjustment.dq_vapor_to_cloud
186  + saturation_adjustment.dq_cloud_to_vapor;
187 
188  if ((qp > amrex::Real(0)) && (qsat > amrex::Real(0))
189  && (pressure_current_units > amrex::Real(0))
190  && (qv_eff < qsat) && (remaining_capacity > amrex::Real(0))) {
191  // MUST MATCH: current AdvanceKessler rain-evaporation coefficients and exponents.
192  const amrex::Real coeff = amrex::Real(1.6)
193  + amrex::Real(124.9) * std::pow(amrex::Real(0.001) * rho * qp, amrex::Real(0.2046));
194  const amrex::Real raw_rain_to_vapor = amrex::Real(1) / (amrex::Real(0.001) * rho)
195  * (amrex::Real(1) - qv_eff / qsat)
196  * coeff
197  * std::pow(amrex::Real(0.001) * rho * qp, amrex::Real(0.525))
198  / (amrex::Real(5.4e5) + amrex::Real(2.55e6) / (pressure_current_units * qsat))
199  * dt;
200  source_terms.dq_rain_to_vapor = amrex::min(qp, amrex::min(raw_rain_to_vapor, remaining_capacity));
201  }
202 
203  if (qc > amrex::Real(0)) {
204  const amrex::Real qcc = qc;
205  // Cloud-to-rain conversion is capped by cloud water available after
206  // saturation adjustment, not by the original qc.
207  const amrex::Real available_cloud = amrex::max(
208  amrex::Real(0), qc + saturation_adjustment.dq_vapor_to_cloud - saturation_adjustment.dq_cloud_to_vapor);
209  amrex::Real auto_r = amrex::Real(0);
210  if (qcc > qcw0) {
211  // MUST MATCH: current AdvanceKessler autoconversion trigger.
212  auto_r = alphaelq;
213  }
214 
215  // MUST MATCH: current AdvanceKessler accretion coefficient and exponent.
216  amrex::Real accrr = amrex::Real(0);
217  accrr = amrex::Real(2.2) * std::pow(qp, amrex::Real(0.875));
218  source_terms.dq_cloud_to_rain = dt * (accrr * qcc + auto_r * (qcc - qcw0));
219  source_terms.dq_cloud_to_rain = amrex::min(source_terms.dq_cloud_to_rain, available_cloud);
220  }
221 
222  return source_terms;
223 }
224 
225 #endif
static constexpr amrex::Real kessler_sedimentation_cfl_max
Definition: ERF_KesslerUtils.H:32
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE KesslerSaturationAdjustment kessler_saturation_adjustment(const amrex::Real qv, const amrex::Real qc, const amrex::Real qsat, const amrex::Real dtqsat, const bool do_cond, const amrex::Real latent_over_cp) noexcept
Definition: ERF_KesslerUtils.H:89
static constexpr amrex::Real kessler_water_depth_m_per_kg_m2
Definition: ERF_KesslerUtils.H:33
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE int kessler_num_sedimentation_substeps(const amrex::Real current_reduced_value, const amrex::Real dt, const amrex::Real dzmin) noexcept
Definition: ERF_KesslerUtils.H:62
static constexpr amrex::Real kessler_millimeters_per_meter
Definition: ERF_KesslerUtils.H:34
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE KesslerFaceState kessler_face_state(const int k, const int k_hi, const amrex::Real rho_km1, const amrex::Real rho_k, const amrex::Real qp_km1, const amrex::Real qp_k) noexcept
Definition: ERF_KesslerUtils.H:114
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE KesslerSourceTerms kessler_warm_rain_sources(const amrex::Real qv, const amrex::Real qc, const amrex::Real qp, const amrex::Real rho, const amrex::Real pressure_current_units, const amrex::Real qsat, const amrex::Real dtqsat, const amrex::Real dt, const bool do_cond, const amrex::Real latent_over_cp) noexcept
Definition: ERF_KesslerUtils.H:156
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real kessler_sedimentation_tendency(const amrex::Real fz_hi, const amrex::Real fz_lo, const amrex::Real rho, const amrex::Real dJinv, const amrex::Real coef) noexcept
Definition: ERF_KesslerUtils.H:145
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real kessler_terminal_velocity(const amrex::Real rho, const amrex::Real qp) noexcept
Definition: ERF_KesslerUtils.H:44
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real kessler_rain_accumulation_increment(const amrex::Real precip_mass_per_area) noexcept
Definition: ERF_KesslerUtils.H:72
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real kessler_precip_flux(const amrex::Real rho, const amrex::Real terminal_velocity, const amrex::Real qp) noexcept
Definition: ERF_KesslerUtils.H:54
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE bool kessler_is_small_sedimentation_value(const amrex::Real value) noexcept
Definition: ERF_KesslerUtils.H:82
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE int kessler_face_donor_k(const int k, const int k_hi) noexcept
Definition: ERF_KesslerUtils.H:37
Physical constants and tuning parameters used only by the moisture and cloud-physics code.
constexpr amrex::Real alphaelq
Definition: ERF_MicrophysicsConstants.H:78
constexpr amrex::Real qcw0
Definition: ERF_MicrophysicsConstants.H:76
constexpr amrex::Real rhor
Definition: ERF_MicrophysicsConstants.H:39
Dimensionless numeric literals and pure mathematical constants.
constexpr amrex::Real myhalf
Definition: ERF_NumericalConstants.H:34
amrex::Real Real
Definition: ERF_ShocInterface.H:19
@ qp
Definition: ERF_Kessler.H:34
@ rho
Definition: ERF_Kessler.H:25
@ qv
Definition: ERF_Kessler.H:31
@ qc
Definition: ERF_SatAdj.H:42
real(c_double), parameter epsilon
Definition: ERF_module_model_constants.F90:12
Definition: ERF_KesslerUtils.H:27
amrex::Real rho
Definition: ERF_KesslerUtils.H:28
amrex::Real qp
Definition: ERF_KesslerUtils.H:29
Definition: ERF_KesslerUtils.H:22
amrex::Real dq_cloud_to_vapor
Definition: ERF_KesslerUtils.H:24
amrex::Real dq_vapor_to_cloud
Definition: ERF_KesslerUtils.H:23
Definition: ERF_KesslerUtils.H:15
amrex::Real dq_rain_to_vapor
Definition: ERF_KesslerUtils.H:19
amrex::Real dq_cloud_to_vapor
Definition: ERF_KesslerUtils.H:17
amrex::Real dq_cloud_to_rain
Definition: ERF_KesslerUtils.H:18
amrex::Real dq_vapor_to_cloud
Definition: ERF_KesslerUtils.H:16