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  * Uses a simple saturation vapor pressure formula (Magnus or similar).
29  *
30  * @param[in] qv Water vapor mixing ratio [kg/kg]
31  * @param[in] T Temperature [K]
32  * @param[in] P Pressure [Pa]
33  * @return Relative humidity [0, 1]
34  */
35 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
37 {
38  // Guard against invalid inputs
39  if (T <= 0.0 || P <= 0.0 || qv < 0.0 || !amrex::Math::isfinite(qv) ||
40  !amrex::Math::isfinite(T) || !amrex::Math::isfinite(P)) {
41  return 0.0;
42  }
43 
44  // Simple saturation mixing ratio approximation (Magnus formula variant)
45  // qsat = 0.622 * e_sat / (P - e_sat)
46  // where e_sat = e0 * exp(a * (T - T0) / (T - b))
47  // For simplicity, use approximate saturation vapor pressure
48  const amrex::Real e0 = 611.2; // Pa (saturation vapor pressure at 0°C)
49  const amrex::Real T0 = 273.15; // K
50  const amrex::Real a = 17.62;
51  const amrex::Real b = 243.12; // K
52 
53  // Compute saturation vapor pressure (Magnus formula)
54  amrex::Real arg = a * (T - T0) / (T - b);
55  if (!amrex::Math::isfinite(arg) || arg > 100.0) {
56  // Protect against overflow
57  arg = 100.0;
58  }
59  amrex::Real e_sat = e0 * std::exp(arg);
60 
61  // Saturation mixing ratio: qsat = epsilon * e_sat / (P - e_sat)
62  const amrex::Real epsilon = 0.622; // ratio of molecular weights
63  amrex::Real qsat = epsilon * e_sat / (P - e_sat);
64  if (qsat < 0.0 || !amrex::Math::isfinite(qsat)) {
65  qsat = 1.0e-6; // fallback
66  }
67 
68  // RH = qv / qsat, clamped to [0, 1]
69  amrex::Real rh = qv / qsat;
70  if (rh < 0.0) rh = 0.0;
71  if (rh > 1.0) rh = 1.0;
72 
73  return rh;
74 }
75 
76 /**
77  * @brief Diagnose cloud fraction from relative humidity and cloud water.
78  *
79  * Computes diagnosed cloud fraction from RH and qc:
80  * - RH contribution: linear ramp from 0 at rh_min to 1 at rh_max
81  * - qc contribution: scaled by qc_scale coefficient
82  * - Combined: saturated blend (cf ≤ 1)
83  *
84  * This is a GPU-safe inline function for device-side kernels.
85  *
86  * @param[in] rh Relative humidity [0, 1]
87  * @param[in] qc Cloud liquid water mixing ratio [kg/kg]
88  * @param[in] rh_min Minimum RH threshold [0, 1]
89  * @param[in] rh_max Maximum RH threshold [0, 1]
90  * @param[in] qc_scale Scaling coefficient for qc contribution
91  *
92  * @return Diagnosed cloud fraction [0, 1]
93  *
94  * @note All inputs are assumed to be validated and finite.
95  * @note Output is always in [0, 1], finite, and safe for further use.
96  */
97 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
100  amrex::Real rh_min, amrex::Real rh_max,
101  amrex::Real qc_scale)
102 {
103  // Guard against invalid inputs
104  if (!amrex::Math::isfinite(rh) || !amrex::Math::isfinite(qc) ||
105  !amrex::Math::isfinite(rh_min) || !amrex::Math::isfinite(rh_max) ||
106  !amrex::Math::isfinite(qc_scale)) {
107  return 0.0; // Safe fallback on invalid input
108  }
109 
110  // Clamp RH and qc to valid ranges
111  if (rh < 0.0) rh = 0.0;
112  if (rh > 1.0) rh = 1.0;
113  if (qc < 0.0) qc = 0.0;
114 
115  amrex::Real cf = 0.0;
116 
117  // RH contribution: linear ramp from 0 at rh_min to 1 at rh_max
118  if (rh_max > rh_min) {
119  // Linear interpolation
120  amrex::Real cf_rh = (rh - rh_min) / (rh_max - rh_min);
121  if (cf_rh < 0.0) cf_rh = 0.0;
122  if (cf_rh > 1.0) cf_rh = 1.0;
123  cf += cf_rh;
124  } else if (rh >= rh_min) {
125  // rh_max == rh_min: step function
126  cf += 1.0;
127  }
128 
129  // qc contribution: qc_scale is the cloud water [kg/kg] at which the
130  // liquid-water term alone saturates, so cf_qc = qc / qc_scale, capped at 1.
131  if (qc_scale > 0.0) {
132  amrex::Real cf_qc = qc / qc_scale;
133  if (cf_qc > 1.0) cf_qc = 1.0;
134  cf += cf_qc;
135  }
136 
137  // Saturate combined cloud fraction to [0, 1]
138  if (cf < 0.0) cf = 0.0;
139  if (cf > 1.0) cf = 1.0;
140 
141  // Final sanity check
142  if (!amrex::Math::isfinite(cf)) {
143  cf = 0.0;
144  }
145 
146  return cf;
147 }
148 
149 #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:36
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:98
amrex::Real Real
Definition: ERF_ShocInterface.H:19
@ P
Definition: ERF_IndexDefines.H:204
@ qv
Definition: ERF_Kessler.H:31
@ qc
Definition: ERF_SatAdj.H:42
@ T
Definition: ERF_IndexDefines.H:128
real(c_double), parameter epsilon
Definition: ERF_module_model_constants.F90:12