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