ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
ERF_FireUtils.H
Go to the documentation of this file.
1 /**
2  * @file ERF_FireUtils.H
3  * @brief Utility functions for fire model atmospheric state extraction
4  */
5 
6 #ifndef ERF_FIRE_UTILS_H
7 #define ERF_FIRE_UTILS_H
8 
9 #include <AMReX_MultiFab.H>
10 #include <AMReX_Geometry.H>
11 #include <AMReX_MFIter.H>
12 #include <ERF_MicrophysicsUtils.H>
13 #include <ERF_EOS.H>
14 #include <ERF_IndexDefines.H>
15 #include <ERF_Constants.H>
16 
17 /**
18  * @brief Compute relative humidity from conserved atmospheric variables at k=0.
19  *
20  * Computes RH from pressure, temperature, and water vapor mixing ratio at
21  * the first atmospheric level (k=0). Uses the ERF microphysics saturation
22  * vapor pressure functions.
23  *
24  * @param[out] RH_out Output relative humidity MultiFab [fraction 0-1]
25  * Must be allocated with ncomp=1 on same grid as inputs
26  * @param[in] S_atm Conservative state at k=0
27  * Contains RhoTheta (comp 1) and RhoQ1 (comp 4)
28  * @param[in] geom Geometry at level 0
29  *
30  * @note
31  * - Output is clamped to [0, 1] range
32  * - Assumes S_atm contains standard conserved variables (Rho, RhoTheta, RhoQ1)
33  * - Temperature extracted from RhoTheta using standard EOS
34  * - Pressure computed from RhoTheta and qv using getPgivenRTh
35  */
36 inline
37 void compute_rh_from_conservative(amrex::MultiFab& RH_out,
38  const amrex::MultiFab& S_atm,
39  const amrex::Geometry& /*geom*/)
40 {
41  using namespace amrex;
42 
43  // Precompute rdOcp = R_d / Cp_d for use in Exner function
44  // Cp_d = 1004.5 J/(kg-K), R_d = 287.0 J/(kg-K)
45  constexpr Real rdOcp = R_d / Cp_d;
46 
47  // Iterate over all boxes
48  for (MFIter mfi(RH_out); mfi.isValid(); ++mfi) {
49  const Box& bx = mfi.tilebox();
50  Array4<Real> rh_arr = RH_out.array(mfi);
51  Array4<const Real> S_arr = S_atm.const_array(mfi);
52 
53  // Kernel to compute RH at k=0
54  ParallelFor(bx, [=] AMREX_GPU_DEVICE(const IntVect& iv) {
55  int i = iv[0];
56  int j = iv[1];
57  int k = 0; // First atmospheric level
58 
59  // Extract conserved variables at k=0
60  Real rho = S_arr(i, j, k, Rho_comp); // Density
61  Real rho_th = S_arr(i, j, k, RhoTheta_comp); // RhoTheta
62 
63  // Compute mixing ratios (guard against moisture not being present)
64  Real qv = 0.0_rt; // Water vapor mixing ratio [kg/kg]
65  if (RhoQ1_comp < S_arr.nComp()) {
66  Real rho_qv = S_arr(i, j, k, RhoQ1_comp); // RhoQ1 (water vapor)
67  qv = rho_qv / rho;
68  }
69 
70  // Compute potential temperature
71  Real theta = rho_th / rho; // Potential temperature [K]
72 
73  // Compute pressure from RhoTheta and qv using standard EOS
74  Real p = getPgivenRTh(rho_th, qv); // Pressure [Pa]
75 
76  // Convert pressure to mbar for saturation vapor pressure functions
77  Real p_mbar = p * 0.01_rt; // Pa to mbar (1 mbar = 100 Pa)
78 
79  // Compute temperature from potential temperature
80  // T = theta * (p / p_0)^(R_d / c_p)
81  // Use Exner function: pi = (p / p_0)^(R_d / c_p)
82  Real exner = getExnergivenP(p, rdOcp);
83  Real T_K = theta * exner;
84 
85  // Compute saturation vapor pressure [mbar]
86  Real e_sat = erf_esatw(T_K, false); // Use Flatau polynomial
87 
88  // Compute saturation mixing ratio [kg/kg]
89  Real q_sat = erf_qsat_from_esat(e_sat, p_mbar);
90 
91  // Compute relative humidity as RH = qv / qsat
92  Real RH = 0.0_rt;
93  if (q_sat > 1.0e-10_rt) {
94  RH = qv / q_sat;
95  }
96 
97  // Clamp RH to valid range [0, 1]
98  RH = amrex::min(1.0_rt, amrex::max(0.0_rt, RH));
99 
100  rh_arr(i, j, k) = RH;
101  });
102  }
103 }
104 
105 /**
106  * @brief Air temperature [K] at k = 0 from the conserved state.
107  *
108  * T = theta * (p / p_0)^(R_d / c_p), with p from RhoTheta and qv as in
109  * compute_rh_from_conservative. Fuel-moisture curves and ignition criteria take
110  * an air temperature, not the potential temperature of the first cell, which at
111  * 840 hPa is 14-16 K above it.
112  *
113  * @param[out] T_out 2D MultiFab (one cell in z) on the level's boxes
114  * @param[in] S_atm Conserved state (Rho, RhoTheta, RhoQ1 when moist)
115  */
116 inline
117 void compute_t_from_conservative(amrex::MultiFab& T_out,
118  const amrex::MultiFab& S_atm)
119 {
120  using namespace amrex;
121  constexpr Real rdOcp = R_d / Cp_d;
122  for (MFIter mfi(T_out); mfi.isValid(); ++mfi) {
123  const Box& bx = mfi.tilebox();
124  Array4<Real> t_arr = T_out.array(mfi);
125  Array4<const Real> S_arr = S_atm.const_array(mfi);
126  ParallelFor(bx, [=] AMREX_GPU_DEVICE(const IntVect& iv) {
127  const int i = iv[0];
128  const int j = iv[1];
129  const int k = 0;
130  const Real rho = S_arr(i, j, k, Rho_comp);
131  const Real rho_th = S_arr(i, j, k, RhoTheta_comp);
132  Real qv = 0.0_rt;
133  if (RhoQ1_comp < S_arr.nComp()) {
134  qv = S_arr(i, j, k, RhoQ1_comp) / rho;
135  }
136  const Real p = getPgivenRTh(rho_th, qv);
137  const Real exner = getExnergivenP(p, rdOcp);
138  t_arr(i, j, k) = (rho_th / rho) * exner;
139  });
140  }
141 }
142 
143 /**
144  * Copy the k = 0 plane of a cell-centred atmospheric field into a MultiFab on the
145  * same boxes coarsened to one cell in z. A namespace-scope function because nvcc
146  * rejects a device kernel inside a private member such as ERF::Advance.
147  */
148 inline
149 void fire_copy_k0_plane(amrex::MultiFab& dst_2d, const amrex::MultiFab& src_3d)
150 {
151  for (amrex::MFIter mfi(dst_2d); mfi.isValid(); ++mfi) {
152  amrex::Array4<amrex::Real> d = dst_2d.array(mfi);
153  amrex::Array4<const amrex::Real> s = src_3d.const_array(mfi);
154  amrex::ParallelFor(mfi.tilebox(), [=] AMREX_GPU_DEVICE (const amrex::IntVect& iv) {
155  d(iv[0], iv[1], 0) = s(iv[0], iv[1], 0);
156  });
157  }
158 }
159 
160 /**
161  * @brief Copy a MultiFab between device and host-accessible memory, for an
162  * engine whose fire physics runs on the host
163  *
164  * The memory contract of ERF_FireEngine.H: the coupling's fields are in the
165  * default arena (device memory on a GPU build) and are only valid during the
166  * call that hands them over; an engine's returned fields must be readable
167  * there too. dst and src have the same BoxArray and DistributionMapping;
168  * ncomp components and nghost ghost cells are copied from component 0. dst
169  * belongs to the caller (allocate a host copy once with
170  * amrex::MFInfo().SetArena(amrex::The_Pinned_Arena()), or The_Managed_Arena(),
171  * and reuse it). The copy completes before the call returns: the GPU stream is
172  * synchronised, so the host may read dst, or the device read it, right away.
173  * On a CPU build it is a plain copy.
174  */
175 inline
176 void fire_host_copy(amrex::MultiFab& dst, const amrex::MultiFab& src, int ncomp = 1, int nghost = 0)
177 {
178  AMREX_ALWAYS_ASSERT(dst.boxArray() == src.boxArray() && dst.DistributionMap() == src.DistributionMap());
179  amrex::MultiFab::Copy(dst, src, 0, 0, ncomp, nghost);
180  amrex::Gpu::streamSynchronize();
181 }
182 
183 #endif // ERF_FIRE_UTILS_H
constexpr amrex::Real Cp_d
Definition: ERF_Constants.H:51
constexpr amrex::Real R_d
Definition: ERF_Constants.H:49
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real getExnergivenP(const amrex::Real P, const amrex::Real rdOcp)
Definition: ERF_EOS.H:141
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real getPgivenRTh(const amrex::Real rhotheta, const amrex::Real qv=amrex::Real(0))
Definition: ERF_EOS.H:81
void fire_copy_k0_plane(amrex::MultiFab &dst_2d, const amrex::MultiFab &src_3d)
Definition: ERF_FireUtils.H:149
void fire_host_copy(amrex::MultiFab &dst, const amrex::MultiFab &src, int ncomp=1, int nghost=0)
Copy a MultiFab between device and host-accessible memory, for an engine whose fire physics runs on t...
Definition: ERF_FireUtils.H:176
void compute_t_from_conservative(amrex::MultiFab &T_out, const amrex::MultiFab &S_atm)
Air temperature [K] at k = 0 from the conserved state.
Definition: ERF_FireUtils.H:117
void compute_rh_from_conservative(amrex::MultiFab &RH_out, const amrex::MultiFab &S_atm, const amrex::Geometry &)
Compute relative humidity from conserved atmospheric variables at k=0.
Definition: ERF_FireUtils.H:37
#define Rho_comp
Definition: ERF_IndexDefines.H:53
#define RhoTheta_comp
Definition: ERF_IndexDefines.H:54
#define RhoQ1_comp
Definition: ERF_IndexDefines.H:66
const Real rdOcp
Definition: ERF_InitCustomPert_ABL.H:72
AMREX_ALWAYS_ASSERT(bx.length()[2]==khi+1)
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real erf_qsat_from_esat(amrex::Real esat, amrex::Real p)
Definition: ERF_MicrophysicsUtils.H:233
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real erf_esatw(amrex::Real t, bool use_empirical=false)
Definition: ERF_MicrophysicsUtils.H:159
ParallelFor(fab_box, [=] AMREX_GPU_DEVICE(int i, int j, int k) { qrcuten_arr(i, j, k)=Real(0);qscuten_arr(i, j, k)=Real(0);qicuten_arr(i, j, k)=Real(0);})
amrex::Real Real
Definition: ERF_ShocInterface.H:19
@ rho
Definition: ERF_Kessler.H:25
@ theta
Definition: ERF_Kessler.H:26
@ qv
Definition: ERF_Kessler.H:31
@ p
Definition: ERF_WSM6.H:301
Definition: ERF_ConsoleIO.cpp:17