ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
ERF_SatAdj.H
Go to the documentation of this file.
1 #ifndef ERF_SATADJ_H
2 #define ERF_SATADJ_H
3 
4 /*
5  * SatAdj is a cell-local, fixed-pressure saturation-adjustment source step.
6  * It is not a flux-form finite-volume update.
7  *
8  * The scheme reads rho, rhoTheta, rhoQv, and rhoQc from the conserved state,
9  * adjusts theta/qv/qc locally, and writes rhoTheta/rhoQv/rhoQc back.
10  * Density is copied for diagnostics and conservative reconstruction, but rho is
11  * not a SatAdj prognostic variable and is not modified by this module.
12  */
13 
14 #include <string>
15 #include <vector>
16 #include <memory>
17 
18 #include <AMReX_FArrayBox.H>
19 #include <AMReX_Geometry.H>
20 #include <AMReX_MultiFabUtil.H>
21 #include <AMReX_GpuContainers.H>
22 
23 #include "ERF_EOS.H"
24 #include "ERF_Constants.H"
25 #include "ERF_MicrophysicsUtils.H"
26 #include "ERF_IndexDefines.H"
27 #include "ERF_DataStruct.H"
28 #include "ERF_NullMoist.H"
29 #include "ERF_TileNoZ.H"
30 
31 namespace MicVar_SatAdj {
32  enum {
33  // independent variables
34  rho=0, // density copied from conserved state; not modified by SatAdj
35  theta, // dry potential temperature
36  tabs, // absolute temperature [K]
37  pres, // pressure [mbar/hPa] for saturation helpers
38  // non-precipitating vars
39  qv, // water-vapor mixing ratio
40  qc, // cloud-water mixing ratio
41  NumVars
42  };
43 }
44 
45 class SatAdj : public NullMoist {
46 
47  using FabPtr = std::shared_ptr<amrex::MultiFab>;
48 
49 public:
50  // constructor
51  SatAdj () {}
52 
53  // destructor
54  virtual ~SatAdj () = default;
55 
56  // cloud physics
57  void AdvanceSatAdj (const SolverChoice& /*solverChoice*/);
58 
59  // Set up for first time
60  void
61  Define (SolverChoice& sc) override
62  {
63  m_fac_cond = lcond / sc.c_p;
64  m_rdOcp = sc.rdOcp;
65  m_do_cond = (!sc.uses_shoc_family());
66  }
67 
68  // init
69  void
70  Init (const amrex::MultiFab& cons_in,
71  const amrex::BoxArray& /*grids*/,
72  const amrex::Geometry& geom,
73  const amrex::Real& dt_advance,
74  std::unique_ptr<amrex::MultiFab>& /*z_phys_nd*/,
75  std::unique_ptr<amrex::MultiFab>& /*detJ_cc*/) override;
76 
77  // Copy state into micro vars
78  void
79  Copy_State_to_Micro (const amrex::MultiFab& cons_in) override;
80 
81  void
82  Update_Micro_Vars (amrex::MultiFab& cons_in,
83  const amrex::MultiFab* base_state) override;
84 
85  // Copy state into micro vars
86  void
87  Copy_Micro_to_State (amrex::MultiFab& cons_in) override;
88 
89  // update micro vars
90  void
91  Update_Micro_Vars (amrex::MultiFab& cons_in) override
92  {
93  if (!m_do_cond) { return; }
94  this->Copy_State_to_Micro(cons_in);
95  }
96 
97  // update state vars
98  void
99  Update_State_Vars (amrex::MultiFab& cons_in,
100  const amrex::MultiFab& /*z_phys_nd*/) override
101  {
102  if (!m_do_cond) { return; }
103  this->Copy_Micro_to_State(cons_in);
104  }
105 
106  // wrapper to advance micro vars
107  void
108  Advance (const amrex::Real& dt_advance,
109  const SolverChoice& solverChoice) override
110  {
111  dt = dt_advance;
112 
113  this->AdvanceSatAdj(solverChoice);
114  }
115 
116  amrex::MultiFab*
117  Qmoist_Ptr (const int& varIdx) override
118  {
120  return nullptr;
121  }
122 
123  int
124  Qmoist_Size () override { return SatAdj::m_qmoist_size; }
125 
126  int
128 
129  int
131 
132  void
134  std::vector<int>& a_idx,
135  std::vector<std::string>& a_names) const override
136  {
137  a_idx.clear();
138  a_names.clear();
139  }
140 
141  AMREX_GPU_HOST_DEVICE
142  AMREX_FORCE_INLINE
143  static amrex::Real
144  // Newton solve for the saturated final temperature at fixed pressure.
145  // The invariant is T + (L/cp) * qv for the local cell. The solve finds
146  // T_new such that qv_new = qsat(T_new, p). The pressure argument is held
147  // fixed in mbar/hPa throughout the solve.
148  //
149  // Solves the fixed-pressure saturation equation
150  // 0 = -T_new + T_old + (L/cp) * (qv_old - qsat(T_new, p))
151  // for T_new. On return, qsat is evaluated exactly at the returned
152  // temperature. The caller uses that qsat to set qv and qc while
153  // conserving qv + qc. Density does not enter this solve.
154  // If the Newton iteration reaches max_niter before the tolerance, the
155  // routine returns the last iterate and recomputes qsat at that temperature.
157  const amrex::Real tabs_old,
158  const amrex::Real pres_mbar,
159  const amrex::Real qv_old,
160  amrex::Real& qsat)
161  {
162 #ifdef AMREX_USE_FLOAT
163  constexpr amrex::Real tol = amrex::Real(1.e-4);
164 #else
165  constexpr amrex::Real tol = amrex::Real(1.e-8);
166 #endif
167  constexpr int max_iter = 20;
168 
169  int niter = 0;
170  amrex::Real F, dFdT, dqsat;
171  amrex::Real tabs = tabs_old;
172 
173  //==================================================
174  // Newton iteration to qv=qsat (cloud phase only)
175  //==================================================
176  // Saturation moisture fractions
177  erf_qsatw(tabs, pres_mbar, qsat);
178  erf_dtqsatw(tabs, pres_mbar, dqsat);
179 
180  // Function for root finding:
181  // 0 = -T_new + T_old + L_eff/C_p * (qv - qsat)
182  F = -tabs + tabs_old + fac_cond*(qv_old - qsat);
183 
184  // Iterate when necessary
185  while (std::abs(F) > tol && niter < max_iter) {
186  // Derivative of function (T_new iterated on)
187  dFdT = -one - fac_cond*dqsat;
188 
189  // Update the temperature
190  tabs -= F/dFdT;
191 
192  // Saturation moisture fractions
193  erf_qsatw(tabs, pres_mbar, qsat);
194  erf_dtqsatw(tabs, pres_mbar, dqsat);
195 
196  // Function for root finding:
197  // 0 = -T_new + T_old + L_eff/C_p * (qv - qsat)
198  F = -tabs + tabs_old + fac_cond*(qv_old - qsat);
199 
200  // Update iteration
201  ++niter;
202  }
203 
204  bool valid_solution = (std::abs(F) <= tol && niter < max_iter);
205  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(valid_solution, "SatAdj single phase saturation adjustment failed to converge.");
206 
207  return tabs;
208  }
209 
210  AMREX_GPU_HOST_DEVICE
211  AMREX_FORCE_INLINE
212  static void
213  // Cell-local fixed-pressure saturation adjustment.
214  // Inputs/outputs are specific quantities for one cell. The density is not
215  // used or modified. The update conserves qv + qc and, at the pressure
216  // passed in, conserves T + (L/cp) * qv. The final state should satisfy
217  // qc >= 0 and either qc == 0 or qv == qsat(T,p).
218  AdjustSatAdjCell (const amrex::Real fac_cond,
219  const amrex::Real rdOcp,
220  amrex::Real& tabs,
221  const amrex::Real pres_mbar,
223  amrex::Real& qv,
224  amrex::Real& qc)
225  {
226  amrex::Real qsat;
227  erf_qsatw(tabs, pres_mbar, qsat);
228 
229  if ((qv + qc) > qsat) {
230  // Total water exceeds saturation at the initial temperature, so the final state
231  // is cloudy and saturated. Solve directly for the final saturated temperature.
232  // This is algebraically equivalent to evaporating existing cloud water first
233  // and then recondensing to the fixed-pressure equilibrium state.
234 #if defined(AMREX_DEBUG)
235  const amrex::Real qvprev = qv;
236  const amrex::Real qcprev = qc;
237 #endif
238 
239  if (qc < 0) {
240  // Repair negative cloud water by transferring the deficit back to vapor
241  // before solving the saturated equilibrium and clamping qc to zero.
242  qv += qc;
243  qc = zero;
244  }
245 
246  tabs = NewtonSolveSatTemperature(fac_cond, tabs, pres_mbar, qv, qsat);
247 
248  const amrex::Real delta_qv = qv - qsat;
249  qv = qsat;
250  qc += delta_qv;
251 
252 #if defined(AMREX_DEBUG)
253  amrex::Real qsatnew;
254  erf_qsatw(tabs, pres_mbar, qsatnew);
255  AMREX_ASSERT(std::abs(qv-qsatnew) < 1e-12);
256  AMREX_ASSERT(std::abs(qv+qc-qvprev-qcprev) < 1e-14);
257 #endif
258 
259  theta = getThgivenTandP(tabs, amrex::Real(100.0)*pres_mbar, rdOcp);
260  } else {
261  // Total water does not exceed initial saturation. Evaporate all cloud water,
262  // cooling the cell by latent heat absorption. The cooled cell can become
263  // supersaturated, so recondense if needed.
264  const amrex::Real delta_qc = qc;
265 
266  qv += qc;
267  qc = zero;
268 
269  tabs -= fac_cond * delta_qc;
270  theta = getThgivenTandP(tabs, amrex::Real(100.0)*pres_mbar, rdOcp);
271 
272  erf_qsatw(tabs, pres_mbar, qsat);
273  if (qv > qsat) {
274 #if defined(AMREX_DEBUG)
275  const amrex::Real qvprev = qv;
276  const amrex::Real qcprev = qc;
277  const amrex::Real Tprev = tabs;
278 #endif
279 
280  tabs = NewtonSolveSatTemperature(fac_cond, tabs, pres_mbar, qv, qsat);
281 
282  const amrex::Real delta_qv = qv - qsat;
283  qv = qsat;
284  qc += delta_qv;
285 
286 #if defined(AMREX_DEBUG)
287  amrex::Real qsatnew;
288  erf_qsatw(tabs, pres_mbar, qsatnew);
289  AMREX_ASSERT(qv < qvprev);
290  AMREX_ASSERT(qc > qcprev);
291  AMREX_ASSERT(tabs > Tprev);
292  AMREX_ASSERT(std::abs(qv-qsatnew) < 1e-14);
293  AMREX_ASSERT(std::abs(qv+qc-qvprev-qcprev) < 1e-14);
294 #endif
295 
296  theta = getThgivenTandP(tabs, amrex::Real(100.0)*pres_mbar, rdOcp);
297  }
298  }
299  }
300 
301 private:
302  // Number of qmoist variables (no precipitating comps to accumulate)
303  int m_qmoist_size = 0;
304 
305  // Number of qstate variables
307 
308  // Number of qstate variables that are number concentrations
310 
311  // geometry
312  amrex::Geometry m_geom;
313 
314  // timestep
316 
317  // constants
320  bool m_do_cond;
321 
322  // independent variables
323  amrex::Array<FabPtr, MicVar_SatAdj::NumVars> mic_fab_vars;
324 };
325 #endif
constexpr amrex::Real one
Definition: ERF_Constants.H:9
constexpr amrex::Real zero
Definition: ERF_Constants.H:8
constexpr amrex::Real lcond
Definition: ERF_Constants.H:109
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real getThgivenTandP(const amrex::Real T, const amrex::Real P, const amrex::Real rdOcp)
Definition: ERF_EOS.H:18
const Real rdOcp
Definition: ERF_InitCustomPert_Bomex.H:16
AMREX_ALWAYS_ASSERT(bx.length()[2]==khi+1)
AMREX_ALWAYS_ASSERT_WITH_MESSAGE(m_cloud_chamber_config.active, "Cloud Chamber: initializer reached without a parsed configuration")
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void erf_dtqsatw(amrex::Real t, amrex::Real p, amrex::Real &dtqsatw)
Definition: ERF_MicrophysicsUtils.H:244
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void erf_qsatw(amrex::Real t, amrex::Real p, amrex::Real &qsatw)
Definition: ERF_MicrophysicsUtils.H:228
amrex::Real Real
Definition: ERF_ShocInterface.H:19
Definition: ERF_NullMoist.H:9
Definition: ERF_SatAdj.H:45
void Update_Micro_Vars(amrex::MultiFab &cons_in, const amrex::MultiFab *base_state) override
int n_qstate_moist_size
Definition: ERF_SatAdj.H:306
AMREX_GPU_HOST_DEVICE static AMREX_FORCE_INLINE void AdjustSatAdjCell(const amrex::Real fac_cond, const amrex::Real rdOcp, amrex::Real &tabs, const amrex::Real pres_mbar, amrex::Real &theta, amrex::Real &qv, amrex::Real &qc)
Definition: ERF_SatAdj.H:218
amrex::Real m_fac_cond
Definition: ERF_SatAdj.H:318
int Qmoist_Size() override
Definition: ERF_SatAdj.H:124
void Update_Micro_Vars(amrex::MultiFab &cons_in) override
Definition: ERF_SatAdj.H:91
amrex::Geometry m_geom
Definition: ERF_SatAdj.H:312
void Qmoist_Restart_Vars(const SolverChoice &, std::vector< int > &a_idx, std::vector< std::string > &a_names) const override
Definition: ERF_SatAdj.H:133
amrex::Array< FabPtr, MicVar_SatAdj::NumVars > mic_fab_vars
Definition: ERF_SatAdj.H:323
amrex::Real m_rdOcp
Definition: ERF_SatAdj.H:319
SatAdj()
Definition: ERF_SatAdj.H:51
virtual ~SatAdj()=default
void Update_State_Vars(amrex::MultiFab &cons_in, const amrex::MultiFab &) override
Definition: ERF_SatAdj.H:99
amrex::MultiFab * Qmoist_Ptr(const int &varIdx) override
Definition: ERF_SatAdj.H:117
int n_qstate_moist_numconc_size
Definition: ERF_SatAdj.H:309
int Qstate_Moist_NumConc_Size() override
Definition: ERF_SatAdj.H:130
int Qstate_Moist_Size() override
Definition: ERF_SatAdj.H:127
std::shared_ptr< amrex::MultiFab > FabPtr
Definition: ERF_SatAdj.H:47
void Copy_Micro_to_State(amrex::MultiFab &cons_in) override
Definition: ERF_UpdateSatAdj.cpp:13
bool m_do_cond
Definition: ERF_SatAdj.H:320
void Init(const amrex::MultiFab &cons_in, const amrex::BoxArray &, const amrex::Geometry &geom, const amrex::Real &dt_advance, std::unique_ptr< amrex::MultiFab > &, std::unique_ptr< amrex::MultiFab > &) override
Definition: ERF_InitSatAdj.cpp:17
void Advance(const amrex::Real &dt_advance, const SolverChoice &solverChoice) override
Definition: ERF_SatAdj.H:108
int m_qmoist_size
Definition: ERF_SatAdj.H:303
void AdvanceSatAdj(const SolverChoice &)
Definition: ERF_SatAdj.cpp:11
amrex::Real dt
Definition: ERF_SatAdj.H:315
void Define(SolverChoice &sc) override
Definition: ERF_SatAdj.H:61
AMREX_GPU_HOST_DEVICE static AMREX_FORCE_INLINE amrex::Real NewtonSolveSatTemperature(const amrex::Real fac_cond, const amrex::Real tabs_old, const amrex::Real pres_mbar, const amrex::Real qv_old, amrex::Real &qsat)
Definition: ERF_SatAdj.H:156
void Copy_State_to_Micro(const amrex::MultiFab &cons_in) override
Definition: ERF_InitSatAdj.cpp:44
Definition: ERF_SatAdj.H:31
@ theta
Definition: ERF_SatAdj.H:35
@ rho
Definition: ERF_SatAdj.H:34
@ pres
Definition: ERF_SatAdj.H:37
@ NumVars
Definition: ERF_SatAdj.H:41
@ qv
Definition: ERF_SatAdj.H:39
@ tabs
Definition: ERF_SatAdj.H:36
@ qc
Definition: ERF_SatAdj.H:40
Definition: ERF_DataStruct.H:241
amrex::Real rdOcp
Ratio of dry-air gas constant to c_p.
Definition: ERF_DataStruct.H:1477
bool uses_shoc_family() const noexcept
Query whether any SHOC-family PBL scheme is active.
Definition: ERF_DataStruct.H:1566
amrex::Real c_p
Specific heat at constant pressure for dry air [J/(kg-K)].
Definition: ERF_DataStruct.H:1476