ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
ERF_AerosolOpticalDepth.H
Go to the documentation of this file.
1 #ifndef ERF_AEROSOL_OPTICAL_DEPTH_H_
2 #define ERF_AEROSOL_OPTICAL_DEPTH_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_AerosolOpticalDepth.H
11  * @brief Prescribed bulk aerosol/turbidity optical depth diagnosis.
12  *
13  * Implements profile-based aerosol optical depth diagnosis for use in TwoStream radiation.
14  * Supports Constant, Exponential, and Table (future) profile types.
15  *
16  * Aerosol optical depth is added on top of existing tau contributions (tau_base + tau_cloud + tau_dynamic).
17  *
18  * Constant profile:
19  * tau_aerosol(k) = aerosol_tau_per_layer [uniform at all levels]
20  *
21  * Exponential profile:
22  * tau_aerosol(k) = aerosol_tau_surface * exp(-z(k) / aerosol_scale_height_m)
23  * [decay with height]
24  *
25  * Table profile (future):
26  * tau_aerosol(k) = lookup_table[k] [per-level prescribed values]
27  *
28  * References:
29  * -----------
30  */
31 
32 /**
33  * @brief Diagnose aerosol optical depth for Constant profile.
34  *
35  * Returns uniform aerosol tau_per_layer (independent of height/level).
36  * Clamped to [0, 100] for physical reasonableness.
37  *
38  * @param[in] tau_aerosol_const Constant aerosol optical depth per layer [dimensionless]
39  * @return Clamped aerosol optical depth [0, 100]
40  */
41 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
43 {
44  // Guard against invalid input
45  if (!amrex::Math::isfinite(tau_aerosol_const)) {
46  return 0.0;
47  }
48 
49  // Clamp to physically reasonable range [0, 100]
50  amrex::Real tau_clipped = tau_aerosol_const;
51  if (tau_clipped < 0.0) tau_clipped = 0.0;
52  if (tau_clipped > 100.0) tau_clipped = 100.0;
53 
54  return tau_clipped;
55 }
56 
57 /**
58  * @brief Diagnose aerosol optical depth for Exponential profile.
59  *
60  * Returns the optical depth of one layer of an exponentially decaying aerosol
61  * profile, i.e. the profile integrated across that layer,
62  * tau_aerosol(k) = (tau_surface / scale_height) * dz * exp(-z(k) / scale_height)
63  * so the column sum approaches tau_surface once the domain is much deeper
64  * than the scale height. This is a per-layer contribution, not the column
65  * value above z(k).
66  *
67  * Clamped to [0, 100] for physical reasonableness.
68  * Handles invalid parameters safely with fallback to 0.
69  *
70  * @param[in] z_level Height at level k [m]
71  * @param[in] dz Thickness of layer k [m]
72  * @param[in] tau_surface Total-column aerosol optical depth at surface [dimensionless]
73  * @param[in] scale_height_m Decay scale height [m]
74  * @return Clamped aerosol optical depth [0, 100]
75  */
76 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
79  amrex::Real tau_surface,
80  amrex::Real scale_height_m)
81 {
82  if (!amrex::Math::isfinite(z_level) || !amrex::Math::isfinite(dz) ||
83  !amrex::Math::isfinite(tau_surface) || !amrex::Math::isfinite(scale_height_m)) {
84  return 0.0;
85  }
86  if (scale_height_m <= 0.0 || dz <= 0.0) {
87  return 0.0;
88  }
89  if (tau_surface < 0.0) {
90  return 0.0;
91  }
92  if (z_level < 0.0) {
93  z_level = 0.0;
94  }
95 
96  amrex::Real arg = -z_level / scale_height_m;
97  if (arg < -100.0) {
98  return 0.0;
99  }
100 
101  // Per-layer contribution = (tau_surface / scale_height) * dz * exp(-z/H)
102  // so the vertical integral over all layers approaches tau_surface as domain height >> H
103  amrex::Real tau_aerosol = (tau_surface / scale_height_m) * dz * std::exp(arg);
104 
105  if (tau_aerosol < 0.0) tau_aerosol = 0.0;
106  if (tau_aerosol > 100.0) tau_aerosol = 100.0;
107 
108  return tau_aerosol;
109 }
110 
111 
112 /**
113  * @brief Diagnose aerosol optical depth for the Table profile.
114  *
115  * Not implemented: a per-level table lookup would be supplied through a
116  * fixed-size array or a MultiFab. RadChoice::init_params() rejects this
117  * profile type, so this function is unreachable from a configured run.
118  *
119  * @param[in] k Level index
120  * @return Zero
121  */
122 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
124 {
125  // Future extension: implement per-level table lookup
126  // For now, return 0 (no contribution)
127  (void)k; // Suppress unused parameter warning
128  return 0.0;
129 }
130 
131 #endif // ERF_AEROSOL_OPTICAL_DEPTH_H_
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real diagnose_tau_aerosol_constant(amrex::Real tau_aerosol_const)
Diagnose aerosol optical depth for Constant profile.
Definition: ERF_AerosolOpticalDepth.H:42
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real diagnose_tau_aerosol_exponential(amrex::Real z_level, amrex::Real dz, amrex::Real tau_surface, amrex::Real scale_height_m)
Diagnose aerosol optical depth for Exponential profile.
Definition: ERF_AerosolOpticalDepth.H:77
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real diagnose_tau_aerosol_table(int k)
Diagnose aerosol optical depth for the Table profile.
Definition: ERF_AerosolOpticalDepth.H:123
amrex::Real Real
Definition: ERF_ShocInterface.H:19
@ dz
Definition: ERF_AdvanceWDM6.cpp:272