ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
ERF_PrognosticCloudFraction.H
Go to the documentation of this file.
1 #ifndef ERF_PROGNOSTIC_CLOUD_FRACTION_H_
2 #define ERF_PROGNOSTIC_CLOUD_FRACTION_H_
3 
4 #include <AMReX_GpuControl.H>
5 #include <AMReX_Math.H>
6 #include <AMReX_FArrayBox.H>
7 #include <cmath>
8 
9 /**
10  * @file ERF_PrognosticCloudFraction.H
11  * @brief Prognostic cloud fraction diagnosis from relative humidity and cloud water.
12  *
13  * Implements RH/qc-based cloud fraction diagnosis for use in TwoStream radiation.
14  *
15  * Cloud fraction diagnosis formula:
16  * cf_rh(k) = max(0, min(1, (rh(k) - rh_min) / (rh_max - rh_min))) [linear ramp]
17  * cf_qc(k) = min(1, qc / qc_scale(k)) [qc contribution]
18  * cf_diag(k) = min(1, cf_rh(k) + cf_qc(k)) [combined with saturation]
19  *
20  * References:
21  * -----------
22  */
23 
24 /**
25  * @brief Compute relative humidity from water vapor mixing ratio.
26  *
27  * Approximate RH from qv and temperature using saturation mixing ratio.
28  * The saturation vapour pressure over liquid water is the Magnus formula
29  * with the WMO (2008) coefficients, applied to the temperature in Celsius.
30  *
31  * @param[in] qv Water vapor mixing ratio [kg/kg]
32  * @param[in] T Temperature [K]
33  * @param[in] P Pressure [Pa]
34  * @return Relative humidity [0, 1]
35  */
36 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
38 {
39  // Guard against invalid inputs
40  if (T <= 0.0 || P <= 0.0 || qv < 0.0 || !amrex::Math::isfinite(qv) ||
41  !amrex::Math::isfinite(T) || !amrex::Math::isfinite(P)) {
42  return 0.0;
43  }
44 
45  // Saturation vapour pressure over liquid water from the Magnus formula,
46  // with the WMO (2008) coefficients for the temperature in Celsius:
47  // e_sat = e0 * exp(a * tc / (tc + b)), tc = T - T0
48  // and the saturation mixing ratio qsat = epsilon * e_sat / max(e_sat, P - e_sat).
49  const amrex::Real e0 = 611.2; // Pa (saturation vapor pressure at 0°C)
50  const amrex::Real T0 = 273.15; // K
51  const amrex::Real a = 17.62;
52  const amrex::Real b = 243.12; // °C
53 
54  // tc + b <= 0 only below about 30 K, where e_sat tends to 0. Otherwise
55  // arg < a, so e_sat stays below e0 * exp(a) and cannot overflow.
56  const amrex::Real tc = T - T0;
57  const amrex::Real arg = (tc + b > amrex::Real(0.0)) ? a * tc / (tc + b) : amrex::Real(-100.0);
58  const amrex::Real e_sat = e0 * std::exp(arg);
59 
60  // Saturation mixing ratio. The denominator is floored at e_sat, as in
61  // erf_qsatw, so qsat stays positive and bounded by epsilon when e_sat
62  // approaches or exceeds P (hot or very low-pressure air).
63  const amrex::Real epsilon = 0.622; // ratio of molecular weights
64  amrex::Real qsat = epsilon * e_sat / amrex::max(e_sat, P - e_sat);
65  if (qsat <= 0.0) {
66  qsat = 1.0e-6; // fallback when e_sat underflows (unphysically cold air)
67  }
68 
69  // RH = qv / qsat, clamped to [0, 1]
70  amrex::Real rh = qv / qsat;
71  if (rh < 0.0) rh = 0.0;
72  if (rh > 1.0) rh = 1.0;
73 
74  return rh;
75 }
76 
77 /**
78  * @brief Diagnose cloud fraction from relative humidity and cloud water.
79  *
80  * Computes diagnosed cloud fraction from RH and qc:
81  * - RH contribution: linear ramp from 0 at rh_min to 1 at rh_max
82  * - qc contribution: scaled by qc_scale coefficient
83  * - Combined: saturated blend (cf ≤ 1)
84  *
85  * This is a GPU-safe inline function for device-side kernels.
86  *
87  * @param[in] rh Relative humidity [0, 1]
88  * @param[in] qc Cloud liquid water mixing ratio [kg/kg]
89  * @param[in] rh_min Minimum RH threshold [0, 1]
90  * @param[in] rh_max Maximum RH threshold [0, 1]
91  * @param[in] qc_scale Scaling coefficient for qc contribution
92  *
93  * @return Diagnosed cloud fraction [0, 1]
94  *
95  * @note All inputs are assumed to be validated and finite.
96  * @note Output is always in [0, 1], finite, and safe for further use.
97  */
98 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
101  amrex::Real rh_min, amrex::Real rh_max,
102  amrex::Real qc_scale)
103 {
104  // Guard against invalid inputs
105  if (!amrex::Math::isfinite(rh) || !amrex::Math::isfinite(qc) ||
106  !amrex::Math::isfinite(rh_min) || !amrex::Math::isfinite(rh_max) ||
107  !amrex::Math::isfinite(qc_scale)) {
108  return 0.0; // Safe fallback on invalid input
109  }
110 
111  // Clamp RH and qc to valid ranges
112  if (rh < 0.0) rh = 0.0;
113  if (rh > 1.0) rh = 1.0;
114  if (qc < 0.0) qc = 0.0;
115 
116  amrex::Real cf = 0.0;
117 
118  // RH contribution: linear ramp from 0 at rh_min to 1 at rh_max
119  if (rh_max > rh_min) {
120  // Linear interpolation
121  amrex::Real cf_rh = (rh - rh_min) / (rh_max - rh_min);
122  if (cf_rh < 0.0) cf_rh = 0.0;
123  if (cf_rh > 1.0) cf_rh = 1.0;
124  cf += cf_rh;
125  } else if (rh >= rh_min) {
126  // rh_max == rh_min: step function
127  cf += 1.0;
128  }
129 
130  // qc contribution: qc_scale is the cloud water [kg/kg] at which the
131  // liquid-water term alone saturates, so cf_qc = qc / qc_scale, capped at 1.
132  if (qc_scale > 0.0) {
133  amrex::Real cf_qc = qc / qc_scale;
134  if (cf_qc > 1.0) cf_qc = 1.0;
135  cf += cf_qc;
136  }
137 
138  // Saturate combined cloud fraction to [0, 1]
139  if (cf < 0.0) cf = 0.0;
140  if (cf > 1.0) cf = 1.0;
141 
142  // Final sanity check
143  if (!amrex::Math::isfinite(cf)) {
144  cf = 0.0;
145  }
146 
147  return cf;
148 }
149 
150 #endif // ERF_PROGNOSTIC_CLOUD_FRACTION_H_
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real compute_relative_humidity(amrex::Real qv, amrex::Real T, amrex::Real P)
Compute relative humidity from water vapor mixing ratio.
Definition: ERF_PrognosticCloudFraction.H:37
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real diagnose_cloud_fraction_from_rh_qc(amrex::Real rh, amrex::Real qc, amrex::Real rh_min, amrex::Real rh_max, amrex::Real qc_scale)
Diagnose cloud fraction from relative humidity and cloud water.
Definition: ERF_PrognosticCloudFraction.H:99
amrex::Real Real
Definition: ERF_ShocInterface.H:19
@ P
Definition: ERF_IndexDefines.H:244
@ qv
Definition: ERF_Kessler.H:31
@ qc
Definition: ERF_Morrison.H:110
@ T
Definition: ERF_IndexDefines.H:168
real(c_double), parameter epsilon
Definition: ERF_module_model_constants.F90:12