ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
SAM Class Reference

#include <ERF_SAM.H>

Inheritance diagram for SAM:
Collaboration diagram for SAM:

Public Member Functions

 SAM ()
 
virtual ~SAM ()=default
 
void Cloud (const SolverChoice &sc)
 
void IceFall (const SolverChoice &sc)
 
void Precip (const SolverChoice &sc)
 
void PrecipFall (const SolverChoice &sc)
 
void Define (SolverChoice &sc) override
 
void Init (const amrex::MultiFab &cons_in, const amrex::BoxArray &grids, const amrex::Geometry &geom, const amrex::Real &dt_advance, std::unique_ptr< amrex::MultiFab > &z_phys_nd, std::unique_ptr< amrex::MultiFab > &detJ_cc) override
 
void Set_dzmin (const amrex::Real dz_min) override
 
void Copy_State_to_Micro (const amrex::MultiFab &cons_in) override
 
void Copy_State_to_Micro (const amrex::MultiFab &cons_in, const amrex::MultiFab *base_state)
 
void Update_Micro_Vars (amrex::MultiFab &cons_in, const amrex::MultiFab *base_state) override
 
void Copy_Micro_to_State (amrex::MultiFab &cons_in) override
 
void Update_Micro_Vars (amrex::MultiFab &cons_in) override
 
void Update_State_Vars (amrex::MultiFab &cons_in, const amrex::MultiFab &) override
 
void Advance (const amrex::Real &dt_advance, const SolverChoice &sc) override
 
const SedimentationFlux * Sed_Flux () const override
 The sedimentation face fluxes of the most recent Advance, or null for a scheme that does not sediment (see ERF_SedimentationFlux.H) More...
 
amrex::MultiFab * Qmoist_Ptr (const int &varIdx) override
 
const amrex::MultiFab * Qmoist_Ptr (const int &varIdx) const
 
void Compute_Coefficients (bool use_anelastic_reference_pressure=false)
 
SAMCoefficientRow CoefficientRowAt (int k) const
 
int Qmoist_Size () override
 
int Qstate_Moist_Size () override
 
int Qstate_Moist_NumConc_Size () override
 
void Qmoist_Restart_Vars (const SolverChoice &, std::vector< int > &a_idx, std::vector< std::string > &a_names) const override
 
SurfacePrecipAccumulationSources Get_Surface_Precip_Accumulation_Ptrs (const int &) const override
 
- Public Member Functions inherited from NullMoist
 NullMoist ()
 
virtual ~NullMoist ()=default
 
virtual int Qstate_NonMoist_Size ()
 
virtual void GetPlotVarNames (amrex::Vector< std::string > &a_vec) const
 
virtual void GetPlotVar (const std::string &, amrex::MultiFab &) const
 
virtual void GetPlotVar (const std::string &a_name, amrex::MultiFab &a_mf, const int) const
 
virtual void SetCurrentLevel (const int &lev)
 
virtual void InitLevel (const int, const amrex::MultiFab &)
 
virtual int getDiagnosticsInterval () const
 
virtual void Set_Lmask (amrex::iMultiFab *)
 Import ERF's land/water mask. Only schemes whose physics branches on land vs water need to override this. More...
 
virtual void Set_Sed_Inflow (const amrex::MultiFab *inflow)
 The inflow through the top of a fine patch for the next Advance, or null (see ERF_SedimentationFlux.H); the caller keeps it alive for that Advance. More...
 
virtual void Set_RealWidth (const int)
 

Static Public Member Functions

AMREX_GPU_HOST_DEVICE static AMREX_FORCE_INLINE amrex::Real NewtonIterSat (int &i, int &j, int &k, const int &SAM_moisture_type, const amrex::Real &fac_cond, const amrex::Real &fac_fus, const amrex::Real &, const amrex::Real &an, const amrex::Real &bn, const amrex::Array4< amrex::Real > &tabs_array, const amrex::Array4< amrex::Real > &pres_array, const amrex::Array4< amrex::Real > &qv_array, const amrex::Array4< amrex::Real > &qc_array, const amrex::Array4< amrex::Real > &qi_array, const amrex::Array4< amrex::Real > &qn_array, const amrex::Array4< amrex::Real > &qt_array)
 

Private Types

using FabPtr = std::shared_ptr< amrex::MultiFab >
 

Private Attributes

int m_qmoist_size = 4
 
int n_qstate_moist_size = 6
 
int n_qstate_moist_numconc_size = 0
 
amrex::Vector< int > MicVarMap
 
amrex::Geometry m_geom
 
amrex::Real dt
 
int nlev
 
int zlo
 
int zhi
 
int m_axis
 
amrex::Real m_fac_cond
 
amrex::Real m_fac_fus
 
amrex::Real m_fac_sub
 
amrex::Real m_rdOcp {RdoCp}
 
bool m_do_cond
 
MoistureType m_moisture_type = MoistureType::None
 
amrex::Real m_dzmin {0.0}
 
amrex::MultiFab * m_z_phys_nd
 
amrex::MultiFab * m_detJ_cc
 
amrex::Array< FabPtr, MicVar::NumVars > mic_fab_vars
 
SedimentationFlux m_sed
 
amrex::TableData< amrex::Real, 1 > accrrc
 
amrex::TableData< amrex::Real, 1 > accrsi
 
amrex::TableData< amrex::Real, 1 > accrsc
 
amrex::TableData< amrex::Real, 1 > coefice
 
amrex::TableData< amrex::Real, 1 > evaps1
 
amrex::TableData< amrex::Real, 1 > evaps2
 
amrex::TableData< amrex::Real, 1 > accrgi
 
amrex::TableData< amrex::Real, 1 > accrgc
 
amrex::TableData< amrex::Real, 1 > evapg1
 
amrex::TableData< amrex::Real, 1 > evapg2
 
amrex::TableData< amrex::Real, 1 > evapr1
 
amrex::TableData< amrex::Real, 1 > evapr2
 
amrex::TableData< amrex::Real, 1 > rho1d
 
amrex::TableData< amrex::Real, 1 > pres1d
 
amrex::TableData< amrex::Real, 1 > tabs1d
 
amrex::TableData< amrex::Real, 1 > qt1d
 
amrex::TableData< amrex::Real, 1 > qv1d
 
amrex::TableData< amrex::Real, 1 > qn1d
 

Static Private Attributes

static constexpr amrex::Real CFL_MAX = myhalf
 

Additional Inherited Members

- Protected Member Functions inherited from NullMoist
void set_anelastic_reference_pressure_mode (const SolverChoice &sc)
 
void assert_base_state_available (const amrex::MultiFab *base_state) const
 
- Protected Attributes inherited from NullMoist
const amrex::MultiFab * m_sed_inflow = nullptr
 
int m_level {0}
 
bool m_use_anelastic_reference_pressure {false}
 

Member Typedef Documentation

◆ FabPtr

using SAM::FabPtr = std::shared_ptr<amrex::MultiFab>
private

Constructor & Destructor Documentation

◆ SAM()

SAM::SAM ( )
inline
63 {}

◆ ~SAM()

virtual SAM::~SAM ( )
virtualdefault

Member Function Documentation

◆ Advance()

void SAM::Advance ( const amrex::Real &  dt_advance,
const SolverChoice &  sc 
)
inlineoverridevirtual

Reimplemented from NullMoist.

144  {
145  dt = dt_advance;
146  m_sed.reset(dt_advance);
147 
148  this->Cloud(sc);
149  this->IceFall(sc);
150  this->Precip(sc);
151  this->PrecipFall(sc);
152  }
void Precip(const SolverChoice &sc)
Definition: ERF_Precip.cpp:12
void IceFall(const SolverChoice &sc)
Definition: ERF_IceFall.cpp:13
void Cloud(const SolverChoice &sc)
Definition: ERF_CloudSAM.cpp:14
SedimentationFlux m_sed
Definition: ERF_SAM.H:395
void PrecipFall(const SolverChoice &sc)
Definition: ERF_PrecipFall.cpp:19
amrex::Real dt
Definition: ERF_SAM.H:367
void reset(amrex::Real a_dt)
Zero the fluxes at the start of an Advance of length a_dt.
Definition: ERF_SedimentationFlux.H:57
Here is the call graph for this function:

◆ Cloud()

void SAM::Cloud ( const SolverChoice &  sc)

Split cloud components according to saturation pressures; source theta from latent heat.

15 {
16  if (!m_do_cond) { return; }
17 
18  constexpr Real an = one/(tbgmax-tbgmin);
19  constexpr Real bn = tbgmin*an;
20 
21  Real fac_cond = m_fac_cond;
22  Real fac_sub = m_fac_sub;
23  Real fac_fus = m_fac_fus;
24  Real rdOcp = m_rdOcp;
25 
26  int SAM_moisture_type = 1;
27  if (sam_is_no_ice(sc.moisture_type)) {
28  SAM_moisture_type = 2;
29  }
30 
31  for ( MFIter mfi(*(mic_fab_vars[MicVar::tabs]), TilingIfNotGPU()); mfi.isValid(); ++mfi) {
32  auto qt_array = mic_fab_vars[MicVar::qt]->array(mfi);
33  auto qn_array = mic_fab_vars[MicVar::qn]->array(mfi);
34  auto qv_array = mic_fab_vars[MicVar::qv]->array(mfi);
35  auto qcl_array = mic_fab_vars[MicVar::qcl]->array(mfi);
36  auto qci_array = mic_fab_vars[MicVar::qci]->array(mfi);
37 
38  auto tabs_array = mic_fab_vars[MicVar::tabs]->array(mfi);
39  auto theta_array = mic_fab_vars[MicVar::theta]->array(mfi);
40  auto pres_array = mic_fab_vars[MicVar::pres]->array(mfi);
41 
42  auto tbx = mfi.tilebox();
43 
44  ParallelFor(tbx, [=] AMREX_GPU_DEVICE (int i, int j, int k)
45  {
46  // Saturation moisture fractions
47  Real omn;
48  Real qsat;
49  Real qsatw;
50  Real qsati;
51 
52  // Newton iteration vars
53  Real delta_qv, delta_qc, delta_qi;
54 
55  // NOTE: Conversion before iterations is necessary to
56  // convert cloud water to ice or vice versa.
57  // This ensures the omn splitting is enforced
58  // before the Newton iteration, which assumes it is.
59 
60  const SAMCloudPhaseChange phase_change =
61  sam_partition_cloud_phase(SAM_moisture_type,
62  tabs_array(i,j,k),
63  qn_array(i,j,k),
64  qcl_array(i,j,k),
65  qci_array(i,j,k),
66  fac_cond, fac_fus,
67  an, bn);
68  omn = phase_change.omn;
69  delta_qc = phase_change.delta_qc;
70  delta_qi = phase_change.delta_qi;
71  qcl_array(i,j,k) = phase_change.qcl;
72  qci_array(i,j,k) = phase_change.qci;
73  tabs_array(i,j,k) = phase_change.tabs;
74  // Cloud adjustment updates tabs under the held-pressure SAM source
75  // convention, then refreshes theta from the same stored pressure.
76  theta_array(i,j,k) = sam_theta_from_stored_mbar_converted_to_pa(tabs_array(i,j,k),
77  pres_array(i,j,k), rdOcp);
78 
79  // Saturation moisture fractions
80  erf_qsatw(tabs_array(i,j,k), pres_array(i,j,k), qsatw);
81  erf_qsati(tabs_array(i,j,k), pres_array(i,j,k), qsati);
82  qsat = sam_mixed_qsat(omn, qsatw, qsati);
83 
84  // We have enough total moisture to relax to equilibrium
85  if (qt_array(i,j,k) > qsat) {
86 
87  // Update temperature
88  tabs_array(i,j,k) = NewtonIterSat(i, j, k , SAM_moisture_type ,
89  fac_cond , fac_fus , fac_sub ,
90  an , bn ,
91  tabs_array, pres_array,
92  qv_array , qcl_array , qci_array,
93  qn_array , qt_array);
94 
95  // Update theta
96  theta_array(i,j,k) = sam_theta_from_stored_mbar_converted_to_pa(tabs_array(i,j,k),
97  pres_array(i,j,k), rdOcp);
98 
99  //
100  // We cannot blindly relax to qsat, but we can convert qc/qi -> qv.
101  // The concept here is that if we put all the moisture into qv and modify
102  // the temperature, we can then check if qv > qsat occurs (for final T/P/qv).
103  // If the reduction in T/qsat and increase in qv does trigger the
104  // aforementioned condition, we can do Newton iteration to drive qv = qsat.
105  //
106  } else {
107  // Changes in each component
108  delta_qv = qcl_array(i,j,k) + qci_array(i,j,k);
109  delta_qc = qcl_array(i,j,k);
110  delta_qi = qci_array(i,j,k);
111 
112  // Partition the change in non-precipitating q
113  qv_array(i,j,k) += delta_qv;
114  qcl_array(i,j,k) = zero;
115  qci_array(i,j,k) = zero;
116  qn_array(i,j,k) = zero;
117  qt_array(i,j,k) = qv_array(i,j,k);
118 
119  // Update temperature (endothermic since we evap/sublime)
120  tabs_array(i,j,k) -= fac_cond * delta_qc + fac_sub * delta_qi;
121 
122  // Update theta
123  theta_array(i,j,k) = sam_theta_from_stored_mbar_converted_to_pa(tabs_array(i,j,k),
124  pres_array(i,j,k), rdOcp);
125 
126  // Verify assumption that qv > qsat does not occur
127  erf_qsatw(tabs_array(i,j,k), pres_array(i,j,k), qsatw);
128  erf_qsati(tabs_array(i,j,k), pres_array(i,j,k), qsati);
129  qsat = sam_mixed_qsat(omn, qsatw, qsati);
130  if (qt_array(i,j,k) > qsat) {
131 
132  // Update temperature
133  tabs_array(i,j,k) = NewtonIterSat(i, j, k , SAM_moisture_type ,
134  fac_cond , fac_fus , fac_sub ,
135  an , bn ,
136  tabs_array, pres_array,
137  qv_array , qcl_array , qci_array,
138  qn_array , qt_array);
139 
140  // Update theta
141  theta_array(i,j,k) = sam_theta_from_stored_mbar_converted_to_pa(tabs_array(i,j,k),
142  pres_array(i,j,k), rdOcp);
143 
144  }
145  }
146  });
147  } // mfi
148 }
const Real rdOcp
Definition: ERF_InitCustomPert_ABL.H:72
constexpr amrex::Real tbgmax
Definition: ERF_MicrophysicsConstants.H:49
constexpr amrex::Real tbgmin
Definition: ERF_MicrophysicsConstants.H:48
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void erf_qsatw(amrex::Real t, amrex::Real p, amrex::Real &qsatw)
Definition: ERF_MicrophysicsUtils.H:264
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void erf_qsati(amrex::Real t, amrex::Real p, amrex::Real &qsati)
Definition: ERF_MicrophysicsUtils.H:254
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);})
constexpr amrex::Real one
Definition: ERF_NumericalConstants.H:37
constexpr amrex::Real zero
Definition: ERF_NumericalConstants.H:36
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE SAMCloudPhaseChange sam_partition_cloud_phase(const int &SAM_moisture_type, const amrex::Real &tabs, const amrex::Real &qn, const amrex::Real &qcl, const amrex::Real &qci, const amrex::Real fac_cond, const amrex::Real fac_fus, const amrex::Real an, const amrex::Real bn) noexcept
Definition: ERF_SAMUtils.H:391
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE bool sam_is_no_ice(const MoistureType &moisture_type) noexcept
Definition: ERF_SAMUtils.H:333
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real sam_theta_from_stored_mbar_converted_to_pa(const amrex::Real &tabs, const amrex::Real &pres_mbar, const amrex::Real rdOcp) noexcept
Definition: ERF_SAMUtils.H:172
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real sam_mixed_qsat(const amrex::Real &omn, const amrex::Real &qsatw, const amrex::Real &qsati) noexcept
Definition: ERF_SAMUtils.H:445
amrex::Real Real
Definition: ERF_ShocInterface.H:19
amrex::Array< FabPtr, MicVar::NumVars > mic_fab_vars
Definition: ERF_SAM.H:391
amrex::Real m_rdOcp
Definition: ERF_SAM.H:379
amrex::Real m_fac_fus
Definition: ERF_SAM.H:377
bool m_do_cond
Definition: ERF_SAM.H:380
amrex::Real m_fac_cond
Definition: ERF_SAM.H:376
AMREX_GPU_HOST_DEVICE static AMREX_FORCE_INLINE amrex::Real NewtonIterSat(int &i, int &j, int &k, const int &SAM_moisture_type, const amrex::Real &fac_cond, const amrex::Real &fac_fus, const amrex::Real &, const amrex::Real &an, const amrex::Real &bn, const amrex::Array4< amrex::Real > &tabs_array, const amrex::Array4< amrex::Real > &pres_array, const amrex::Array4< amrex::Real > &qv_array, const amrex::Array4< amrex::Real > &qc_array, const amrex::Array4< amrex::Real > &qi_array, const amrex::Array4< amrex::Real > &qn_array, const amrex::Array4< amrex::Real > &qt_array)
Definition: ERF_SAM.H:223
amrex::Real m_fac_sub
Definition: ERF_SAM.H:378
@ pres
Definition: ERF_SAM.H:36
@ qci
Definition: ERF_SAM.H:42
@ qv
Definition: ERF_SAM.H:40
@ qt
Definition: ERF_SAM.H:38
@ qn
Definition: ERF_SAM.H:39
@ theta
Definition: ERF_SAM.H:34
@ qcl
Definition: ERF_SAM.H:41
@ tabs
Definition: ERF_SAM.H:35
@ qsatw
Definition: ERF_WSM6.H:361
@ qsati
Definition: ERF_WSM6.H:361
Definition: ERF_SAMUtils.H:23
amrex::Real delta_qi
Definition: ERF_SAMUtils.H:27
amrex::Real qcl
Definition: ERF_SAMUtils.H:29
amrex::Real tabs
Definition: ERF_SAMUtils.H:33
amrex::Real delta_qc
Definition: ERF_SAMUtils.H:26
amrex::Real omn
Definition: ERF_SAMUtils.H:24
amrex::Real qci
Definition: ERF_SAMUtils.H:30
MoistureType moisture_type
Moisture or microphysics model.
Definition: ERF_DataStruct.H:2942

Referenced by Advance().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ CoefficientRowAt()

SAMCoefficientRow SAM::CoefficientRowAt ( int  k) const
276 {
277  AMREX_ALWAYS_ASSERT(k >= zlo && k <= zhi);
278 
279  SAMCoefficientRow row{};
280  row.accrrc = copy_table_value_to_host(accrrc, zlo, zhi, k);
281  row.accrsi = copy_table_value_to_host(accrsi, zlo, zhi, k);
282  row.accrsc = copy_table_value_to_host(accrsc, zlo, zhi, k);
283  row.coefice = copy_table_value_to_host(coefice, zlo, zhi, k);
284  row.evaps1 = copy_table_value_to_host(evaps1, zlo, zhi, k);
285  row.evaps2 = copy_table_value_to_host(evaps2, zlo, zhi, k);
286  row.accrgi = copy_table_value_to_host(accrgi, zlo, zhi, k);
287  row.accrgc = copy_table_value_to_host(accrgc, zlo, zhi, k);
288  row.evapg1 = copy_table_value_to_host(evapg1, zlo, zhi, k);
289  row.evapg2 = copy_table_value_to_host(evapg2, zlo, zhi, k);
290  row.evapr1 = copy_table_value_to_host(evapr1, zlo, zhi, k);
291  row.evapr2 = copy_table_value_to_host(evapr2, zlo, zhi, k);
292  return row;
293 }
AMREX_ALWAYS_ASSERT(bx.length()[2]==khi+1)
int zlo
Definition: ERF_SAM.H:370
amrex::TableData< amrex::Real, 1 > evapr2
Definition: ERF_SAM.H:409
amrex::TableData< amrex::Real, 1 > evapg1
Definition: ERF_SAM.H:406
amrex::TableData< amrex::Real, 1 > accrgi
Definition: ERF_SAM.H:404
amrex::TableData< amrex::Real, 1 > evapg2
Definition: ERF_SAM.H:407
amrex::TableData< amrex::Real, 1 > evaps1
Definition: ERF_SAM.H:402
amrex::TableData< amrex::Real, 1 > accrsi
Definition: ERF_SAM.H:399
amrex::TableData< amrex::Real, 1 > accrrc
Definition: ERF_SAM.H:398
amrex::TableData< amrex::Real, 1 > evapr1
Definition: ERF_SAM.H:408
amrex::TableData< amrex::Real, 1 > evaps2
Definition: ERF_SAM.H:403
amrex::TableData< amrex::Real, 1 > accrgc
Definition: ERF_SAM.H:405
amrex::TableData< amrex::Real, 1 > accrsc
Definition: ERF_SAM.H:400
amrex::TableData< amrex::Real, 1 > coefice
Definition: ERF_SAM.H:401
int zhi
Definition: ERF_SAM.H:370
Definition: ERF_SAMUtils.H:99
amrex::Real accrrc
Definition: ERF_SAMUtils.H:100
Here is the call graph for this function:

◆ Compute_Coefficients()

void SAM::Compute_Coefficients ( bool  use_anelastic_reference_pressure = false)
158 {
159  auto accrrc_t = accrrc.table();
160  auto accrsi_t = accrsi.table();
161  auto accrsc_t = accrsc.table();
162  auto coefice_t = coefice.table();
163  auto evaps1_t = evaps1.table();
164  auto evaps2_t = evaps2.table();
165  auto accrgi_t = accrgi.table();
166  auto accrgc_t = accrgc.table();
167  auto evapg1_t = evapg1.table();
168  auto evapg2_t = evapg2.table();
169  auto evapr1_t = evapr1.table();
170  auto evapr2_t = evapr2.table();
171 
172  auto rho1d_t = rho1d.table();
173  auto pres1d_t = pres1d.table();
174  auto tabs1d_t = tabs1d.table();
175 
176  Real gam3 = erf_gammafff(three );
177  Real gamr1 = erf_gammafff(three+b_rain );
178  Real gamr2 = erf_gammafff((Real(5.0)+b_rain)/two);
179  Real gams1 = erf_gammafff(three+b_snow );
180  Real gams2 = erf_gammafff((Real(5.0)+b_snow)/two);
181  Real gamg1 = erf_gammafff(three+b_grau );
182  Real gamg2 = erf_gammafff((Real(5.0)+b_grau)/two);
183 
184  // calculate the plane average variables
190  rho_ave.compute_averages(ZDir(), rho_ave.field());
191  if (use_anelastic_reference_pressure) {
192  pres_ave.compute_averages(ZDir(), pres_ave.field());
193  tabs_ave.compute_averages(ZDir(), tabs_ave.field());
194  } else {
195  theta_ave.compute_averages(ZDir(), theta_ave.field());
196  qv_ave.compute_averages(ZDir(), qv_ave.field());
197  }
198 
199  // get host variable rho, and rhotheta
200  int ncell = rho_ave.ncell_line();
201 
202  Gpu::HostVector<Real> rho_h(ncell), theta_h(ncell), qv_h(ncell);
203  Gpu::HostVector<Real> pres_h(ncell), tabs_h(ncell);
204  rho_ave.line_average(0, rho_h);
205  if (use_anelastic_reference_pressure) {
206  pres_ave.line_average(0, pres_h);
207  tabs_ave.line_average(0, tabs_h);
208  } else {
209  theta_ave.line_average(0, theta_h);
210  qv_ave.line_average(0, qv_h);
211  }
212 
213  // copy data to device
214  Gpu::DeviceVector<Real> rho_d(ncell), theta_d(ncell), qv_d(ncell);
215  Gpu::DeviceVector<Real> pres_d(ncell), tabs_d(ncell);
216  Gpu::copyAsync(Gpu::hostToDevice, rho_h.begin(), rho_h.end(), rho_d.begin());
217  if (use_anelastic_reference_pressure) {
218  Gpu::copyAsync(Gpu::hostToDevice, pres_h.begin(), pres_h.end(), pres_d.begin());
219  Gpu::copyAsync(Gpu::hostToDevice, tabs_h.begin(), tabs_h.end(), tabs_d.begin());
220  } else {
221  Gpu::copyAsync(Gpu::hostToDevice, theta_h.begin(), theta_h.end(), theta_d.begin());
222  Gpu::copyAsync(Gpu::hostToDevice, qv_h.begin(), qv_h.end(), qv_d.begin());
223  }
224  Gpu::streamSynchronize();
225 
226  Real* rho_dptr = rho_d.data();
227  Real* theta_dptr = theta_d.data();
228  Real* qv_dptr = qv_d.data();
229  Real* pres_dptr = pres_d.data();
230  Real* tabs_dptr = tabs_d.data();
231 
232  ParallelFor(nlev, [=] AMREX_GPU_DEVICE (int k) noexcept
233  {
234  rho1d_t(k) = rho_dptr[k];
235  if (use_anelastic_reference_pressure) {
236  pres1d_t(k) = pres_dptr[k];
237  tabs1d_t(k) = amrex::min(tabs_dptr[k], Real(273.16));
238  } else {
239  Real RhoTheta = rho_dptr[k]*theta_dptr[k];
240  Real pressure = getPgivenRTh(RhoTheta, qv_dptr[k]);
241  pres1d_t(k) = sam_pa_to_mbar(pressure);
242  // NOTE: Limit the temperature to the melting point of ice to avoid
243  // a divide by 0 condition when computing cold coefficients.
244  tabs1d_t(k) = amrex::min(getTgivenRandRTh(rho_dptr[k], RhoTheta, qv_dptr[k]),
245  Real(273.16));
246  }
247  });
248 
249  if(round(gam3) != 2) {
250  amrex::Abort("cannot compute gamma-function in Microphysics::Init");
251  }
252 
253  // Populate all the coefficients
254  ParallelFor(nlev, [=] AMREX_GPU_DEVICE (int k) noexcept
255  {
256  const SAMCoefficientRow row =
257  sam_compute_coefficient_row(rho1d_t(k), tabs1d_t(k),
258  gamr1, gamr2, gams1, gams2, gamg1, gamg2);
259  accrrc_t(k) = row.accrrc;
260  accrsi_t(k) = row.accrsi;
261  accrsc_t(k) = row.accrsc;
262  coefice_t(k) = row.coefice;
263  evaps1_t(k) = row.evaps1;
264  evaps2_t(k) = row.evaps2;
265  accrgi_t(k) = row.accrgi;
266  accrgc_t(k) = row.accrgc;
267  evapg1_t(k) = row.evapg1;
268  evapg2_t(k) = row.evapg2;
269  evapr1_t(k) = row.evapr1;
270  evapr2_t(k) = row.evapr2;
271  });
272 }
DirectionSelector< 2 > ZDir
Definition: ERF_DirectionSelector.H:55
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real getTgivenRandRTh(const amrex::Real rho, const amrex::Real rhotheta, const amrex::Real qv=amrex::Real(0))
Definition: ERF_EOS.H:46
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
pp_for_pert_vels get("prob_type", prob_type)
constexpr amrex::Real b_grau
Definition: ERF_MicrophysicsConstants.H:67
constexpr amrex::Real b_rain
Definition: ERF_MicrophysicsConstants.H:63
constexpr amrex::Real b_snow
Definition: ERF_MicrophysicsConstants.H:65
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real erf_gammafff(amrex::Real x)
Definition: ERF_MicrophysicsUtils.H:55
constexpr amrex::Real three
Definition: ERF_NumericalConstants.H:39
constexpr amrex::Real two
Definition: ERF_NumericalConstants.H:38
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real sam_pa_to_mbar(const amrex::Real &pres_pa) noexcept
Definition: ERF_SAMUtils.H:166
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE SAMCoefficientRow sam_compute_coefficient_row(const amrex::Real &rho, const amrex::Real &tabs, const amrex::Real &gamr1, const amrex::Real &gamr2, const amrex::Real &gams1, const amrex::Real &gams2, const amrex::Real &gamg1, const amrex::Real &gamg2) noexcept
Definition: ERF_SAMUtils.H:804
Vector< Real > rho_h(khi+1, zero)
Gpu::DeviceVector< Real > rho_d(khi+1, zero)
Definition: ERF_PlaneAverage.H:14
amrex::TableData< amrex::Real, 1 > rho1d
Definition: ERF_SAM.H:412
int m_axis
Definition: ERF_SAM.H:373
amrex::TableData< amrex::Real, 1 > pres1d
Definition: ERF_SAM.H:413
int nlev
Definition: ERF_SAM.H:370
amrex::Geometry m_geom
Definition: ERF_SAM.H:364
amrex::TableData< amrex::Real, 1 > tabs1d
Definition: ERF_SAM.H:414
@ rho
Definition: ERF_SAM.H:33
amrex::Real coefice
Definition: ERF_SAMUtils.H:103
amrex::Real evaps2
Definition: ERF_SAMUtils.H:105
amrex::Real accrsi
Definition: ERF_SAMUtils.H:101
amrex::Real accrgc
Definition: ERF_SAMUtils.H:107
amrex::Real accrgi
Definition: ERF_SAMUtils.H:106
amrex::Real accrsc
Definition: ERF_SAMUtils.H:102
amrex::Real evapg1
Definition: ERF_SAMUtils.H:108
amrex::Real evapr1
Definition: ERF_SAMUtils.H:110
amrex::Real evapg2
Definition: ERF_SAMUtils.H:109
amrex::Real evapr2
Definition: ERF_SAMUtils.H:111
amrex::Real evaps1
Definition: ERF_SAMUtils.H:104

Referenced by Update_Micro_Vars().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ Copy_Micro_to_State()

void SAM::Copy_Micro_to_State ( amrex::MultiFab &  cons_in)
overridevirtual

Updates conserved and microphysics variables in the provided MultiFabs from the internal MultiFabs that store Microphysics module data.

Parameters
[out]consConserved variables
[out]qmoistqv, qc, qi, qr, qs, qg

Reimplemented from NullMoist.

16 {
17  // Get the temperature, density, theta, qt and qp from input
18  for ( MFIter mfi(cons,TilingIfNotGPU()); mfi.isValid(); ++mfi) {
19  const auto& box3d = mfi.tilebox();
20 
21  auto states_arr = cons.array(mfi);
22 
23  auto rho_arr = mic_fab_vars[MicVar::rho]->array(mfi);
24  auto theta_arr = mic_fab_vars[MicVar::theta]->array(mfi);
25 
26  auto qv_arr = mic_fab_vars[MicVar::qv]->array(mfi);
27  auto qc_arr = mic_fab_vars[MicVar::qcl]->array(mfi);
28  auto qi_arr = mic_fab_vars[MicVar::qci]->array(mfi);
29 
30  auto qpr_arr = mic_fab_vars[MicVar::qpr]->array(mfi);
31  auto qps_arr = mic_fab_vars[MicVar::qps]->array(mfi);
32  auto qpg_arr = mic_fab_vars[MicVar::qpg]->array(mfi);
33 
34  // get potential total density, temperature, qt, qp
35  ParallelFor( box3d, [=] AMREX_GPU_DEVICE (int i, int j, int k)
36  {
37  SAMPrimitiveCell primitive{};
38  primitive.rho = rho_arr(i,j,k);
39  primitive.theta = theta_arr(i,j,k);
40  primitive.qv = qv_arr(i,j,k);
41  primitive.qcl = qc_arr(i,j,k);
42  primitive.qci = qi_arr(i,j,k);
43  primitive.qpr = qpr_arr(i,j,k);
44  primitive.qps = qps_arr(i,j,k);
45  primitive.qpg = qpg_arr(i,j,k);
46  sam_primitive_to_cons(primitive, states_arr, i, j, k);
47  });
48  }
49 
50  // Fill interior ghost cells and periodic boundaries
51  cons.FillBoundary(m_geom.periodicity());
52 }
auto qv_arr
Definition: ERF_InitCustomPert_MultiSpeciesBubble.H:222
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void sam_primitive_to_cons(const SAMPrimitiveCell &primitive, const amrex::Array4< amrex::Real > &states_arr, const int &i, const int &j, const int &k) noexcept
Definition: ERF_SAMUtils.H:314
auto rho_arr
Definition: ERF_UpdateWSubsidence_SineMassFlux.H:3
@ qpr
Definition: ERF_SAM.H:45
@ qpg
Definition: ERF_SAM.H:47
@ qps
Definition: ERF_SAM.H:46
@ cons
Definition: ERF_IndexDefines.H:254
Definition: ERF_SAMUtils.H:52
amrex::Real rho
Definition: ERF_SAMUtils.H:53

Referenced by Update_State_Vars().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ Copy_State_to_Micro() [1/2]

void SAM::Copy_State_to_Micro ( const amrex::MultiFab &  cons_in)
overridevirtual

Reimplemented from NullMoist.

Referenced by Update_Micro_Vars().

Here is the caller graph for this function:

◆ Copy_State_to_Micro() [2/2]

void SAM::Copy_State_to_Micro ( const amrex::MultiFab &  cons_in,
const amrex::MultiFab *  base_state 
)

◆ Define()

void SAM::Define ( SolverChoice &  sc)
inlineoverridevirtual

Reimplemented from NullMoist.

83  {
85  m_fac_cond = lcond / sc.c_p;
86  m_fac_fus = lfus / sc.c_p;
87  m_fac_sub = lsub / sc.c_p;
88  m_axis = sc.ave_plane;
89  m_rdOcp = sc.rdOcp;
90  m_do_cond = (!sc.uses_shoc_family());
92  }
constexpr amrex::Real lfus
Definition: ERF_MicrophysicsConstants.H:110
constexpr amrex::Real lsub
Definition: ERF_MicrophysicsConstants.H:111
constexpr amrex::Real lcond
Definition: ERF_MicrophysicsConstants.H:109
void set_anelastic_reference_pressure_mode(const SolverChoice &sc)
Definition: ERF_NullMoist.H:171
MoistureType m_moisture_type
Definition: ERF_SAM.H:381
int ave_plane
Averaging plane index used by diagnostics.
Definition: ERF_DataStruct.H:2990
amrex::Real rdOcp
Ratio of dry-air gas constant to c_p.
Definition: ERF_DataStruct.H:2738
bool uses_shoc_family() const noexcept
Query whether any SHOC-family PBL scheme is active.
Definition: ERF_DataStruct.H:2864
amrex::Real c_p
Specific heat at constant pressure for dry air [J/(kg-K)].
Definition: ERF_DataStruct.H:2737
Here is the call graph for this function:

◆ Get_Surface_Precip_Accumulation_Ptrs()

SurfacePrecipAccumulationSources SAM::Get_Surface_Precip_Accumulation_Ptrs ( const int &  ) const
inlineoverridevirtual

Reimplemented from NullMoist.

202  {
203  if (m_moisture_type == MoistureType::SAM_NoPrecip_NoIce) {
204  return {};
205  }
206 
208  // No total is offered: the land and fire couplings take the total where a
209  // scheme offers one and the rain otherwise, and SAM has always given them rain
210  sources.rain = {mic_fab_vars[MicVar::rain_accum].get(), rhor / amrex::Real(1000.0)};
211 
212  if (m_moisture_type == MoistureType::SAM) {
213  sources.snow = {mic_fab_vars[MicVar::snow_accum].get(), rhos / amrex::Real(1000.0)};
214  sources.graupel = {mic_fab_vars[MicVar::graupel_accum].get(), rhog / amrex::Real(1000.0)};
215  }
216 
217  return sources;
218  }
constexpr amrex::Real rhog
Definition: ERF_MicrophysicsConstants.H:41
constexpr amrex::Real rhos
Definition: ERF_MicrophysicsConstants.H:40
constexpr amrex::Real rhor
Definition: ERF_MicrophysicsConstants.H:39
@ snow_accum
Definition: ERF_SAM.H:50
@ rain_accum
Definition: ERF_SAM.H:49
@ graupel_accum
Definition: ERF_SAM.H:51
Definition: ERF_SurfacePrecipitation.H:34
SurfacePrecipAccumulationSource rain
Definition: ERF_SurfacePrecipitation.H:36
SurfacePrecipAccumulationSource snow
Definition: ERF_SurfacePrecipitation.H:37
SurfacePrecipAccumulationSource graupel
Definition: ERF_SurfacePrecipitation.H:38

◆ IceFall()

void SAM::IceFall ( const SolverChoice &  sc)

Sedimentation of cloud ice (A32)

13  {
14 
16  return;
17 
18  Real dtn = dt;
19  // NOTE: coef carries only the reference vertical spacing. The physical
20  // spacing is supplied by the inverse Jacobian in the tendency below,
21  // since detJ = dz_phys/CellSize(2). Dividing by m_dzmin here as well
22  // would apply the vertical metric twice. m_dzmin is still the correct
23  // length scale for the substep (CFL) count, which must bound the
24  // thinnest cell in the domain.
25  Real coef = dtn * m_geom.InvCellSize(2);
26 
27  auto domain = m_geom.Domain();
28  int k_lo = domain.smallEnd(2);
29  int k_hi = domain.bigEnd(2);
30 
33  auto qn = mic_fab_vars[MicVar::qn];
34  auto qt = mic_fab_vars[MicVar::qt];
37 
38  // Cloud ice that falls out of the bottom reaches the ground, so it counts in
39  // the precipitation total (though in none of the rain, snow or graupel ones)
41 
42  MultiFab fz;
43  IntVect ng = qcl->nGrowVect();
44  BoxArray ba = qcl->boxArray();
45  DistributionMapping dm = qcl->DistributionMap();
46  fz.define(convert(ba, IntVect(0,0,1)), dm, 1, ng);
47  fz.setVal(0.);
48 
49  // The face state reads qci at k-1, a ghost cell whenever a box boundary is interior in
50  // z, and Cloud has just updated qci in valid cells only: refresh before the fluxes that
51  // size the substeps are computed (the substep loop refreshes it again as it falls).
52  qci->FillBoundary(m_geom.periodicity());
53 
54  for (MFIter mfi(fz, TileNoZ()); mfi.isValid(); ++mfi) {
55  auto qci_array = qci->array(mfi);
56  auto rho_array = rho->array(mfi);
57  auto fz_array = fz.array(mfi);
58 
59  const auto& box3d = mfi.tilebox();
60 
61  ParallelFor(box3d, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
62  {
63  const Real rho_km1 = (k == k_lo) ? rho_array(i,j,k) : rho_array(i,j,k-1);
64  const Real rho_k = (k == k_hi + 1) ? rho_array(i,j,k-1) : rho_array(i,j,k);
65  const Real qci_km1 = (k == k_lo) ? qci_array(i,j,k) : qci_array(i,j,k-1);
66  const Real qci_k = (k == k_hi + 1) ? qci_array(i,j,k-1) : qci_array(i,j,k);
67  const SAMFaceState face_state = sam_face_average_state(k, k_lo, k_hi,
68  rho_km1, rho_k,
69  zero, zero,
70  qci_km1, qci_k,
71  zero, zero);
72  const Real vt_ice = sam_cloud_ice_terminal_velocity(face_state.qci_avg);
73 
74  // NOTE: Fz is the sedimentation flux from the advective operator.
75  // In the terrain-following coordinate system, the z-deriv in
76  // the divergence uses the normal velocity (Omega). However,
77  // there are no u/v components to the sedimentation velocity.
78  // Therefore, we simply end up with a division by detJ when
79  // evaluating the source term: dJinv * (flux_hi - flux_lo) * dzinv.
80  fz_array(i,j,k) = face_state.rho_avg * vt_ice * face_state.qci_avg;
81  });
82  }
83 
84  // Compute number of substeps from maximum terminal velocity
85  Real wt_max;
86  int n_substep;
87  auto const& ma_fz_arr = fz.const_arrays();
88  GpuTuple<Real> max = ParReduce(TypeList<ReduceOpMax>{},
89  TypeList<Real>{},
90  fz, IntVect(0),
91  [=] AMREX_GPU_DEVICE (int box_no, int i, int j, int k) noexcept
92  -> GpuTuple<Real>
93  {
94  return { ma_fz_arr[box_no](i,j,k) };
95  });
96  wt_max = get<0>(max);
97  // ParReduce over a MultiFab gives the local rank maximum here. IceFall
98  // needs one global substep count across ranks, matching PrecipFall.
99  ParallelDescriptor::ReduceRealMax(wt_max);
102  "SAM::IceFall: the minimum cell height was never set (Set_dzmin), so the fall substeps cannot be sized");
103  n_substep = sam_substep_count_from_reduced_flux(wt_max, dtn, m_dzmin);
104  AMREX_ALWAYS_ASSERT(n_substep >= 1);
105  coef /= Real(n_substep);
106  dtn /= Real(n_substep);
107 
108  // Substep the vertical advection
109  for (int nsub(0); nsub<n_substep; ++nsub) {
110  // The face state and the donor-cap limiter read qci at k-1, which is a ghost cell
111  // whenever a box boundary is interior in z. The previous substep updated qci in
112  // valid cells only, so refresh ghosts here, as PrecipFall does; otherwise the two
113  // sides of a shared z face see different donor values.
114  qci->FillBoundary(m_geom.periodicity());
115 
116  for (MFIter mfi(*qci, TileNoZ()); mfi.isValid(); ++mfi) {
117  auto qci_array = qci->array(mfi);
118  auto qn_array = qn->array(mfi);
119  auto qt_array = qt->array(mfi);
120  auto rho_array = rho->array(mfi);
121  auto fz_array = fz.array(mfi);
122  auto sed_array = m_sed.down->array(mfi);
123  auto precip_accum_array = precip_accum->array(mfi);
124  const auto inflow_array = (m_sed_inflow) ? m_sed_inflow->const_array(mfi)
125  : Array4<const Real>{};
126  const int inflow_flag = m_sed.inflow_flag_comp();
127  const Real inv_dt = one / dt;
128 
129  const auto dJ_array = (m_detJ_cc) ? m_detJ_cc->const_array(mfi) : Array4<const Real>{};
130 
131  const auto& tbx = mfi.tilebox();
132  const auto& tbz = mfi.tilebox(IntVect(0,0,1),IntVect(0));
133 
134  // Update vertical flux every substep
135  ParallelFor(tbz, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
136  {
137  const Real rho_km1 = (k == k_lo) ? rho_array(i,j,k) : rho_array(i,j,k-1);
138  const Real rho_k = (k == k_hi + 1) ? rho_array(i,j,k-1) : rho_array(i,j,k);
139  const Real qci_km1 = (k == k_lo) ? qci_array(i,j,k) : qci_array(i,j,k-1);
140  const Real qci_k = (k == k_hi + 1) ? qci_array(i,j,k-1) : qci_array(i,j,k);
141  const SAMFaceState face_state = sam_face_average_state(k, k_lo, k_hi,
142  rho_km1, rho_k,
143  zero, zero,
144  qci_km1, qci_k,
145  zero, zero);
146  const Real vt_ice = sam_cloud_ice_terminal_velocity(face_state.qci_avg);
147  const int donor_k = sam_precip_face_donor_k(k, k_lo, k_hi);
148  const Real detJ_donor = (dJ_array) ? dJ_array(i,j,donor_k) : one;
149 
150  // NOTE: Fz is the sedimentation flux from the advective operator.
151  // In the terrain-following coordinate system, the z-deriv in
152  // the divergence uses the normal velocity (Omega). However,
153  // there are no u/v components to the sedimentation velocity.
154  // Therefore, we simply end up with a division by detJ when
155  // evaluating the source term: dJinv * (flux_hi - flux_lo) * dzinv.
156  //
157  // Cap the flux by the ice the donor cell holds, as PrecipFall does, so a
158  // substep cannot export more than the cell contains. The clip on dqi
159  // below then binds only at roundoff, so the column budget closes.
160  fz_array(i,j,k) = sam_limit_precip_component_flux(face_state.rho_avg * vt_ice * face_state.qci_avg,
161  rho_array(i,j,donor_k),
162  qci_array(i,j,donor_k),
163  detJ_donor,
164  coef);
165  // At the top of a fine patch the ice coming in is the coarse level's flux
166  // through that face, not one built from interpolated ghost cells
167  if (inflow_array && inflow_array(i,j,k,inflow_flag) > Real(0.5)) {
168  fz_array(i,j,k) = inflow_array(i,j,k,3) * inv_dt;
169  }
170  sed_array(i,j,k,3) += fz_array(i,j,k) * dtn;
171 
172  // In mm of liquid water, as the other accumulations
173  if (k == k_lo) {
174  precip_accum_array(i,j,k) += fz_array(i,j,k) * dtn / rhor * Real(1000.0);
175  }
176  });
177 
178  // Update precip every substep
179  ParallelFor(tbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
180  {
181  // Jacobian determinant
182  Real dJinv = (dJ_array) ? one/dJ_array(i,j,k) : one;
183 
184  //==================================================
185  // Cloud ice sedimentation (A32)
186  //==================================================
187  Real dqi = sam_sedimentation_tendency(fz_array(i,j,k+1), fz_array(i,j,k),
188  rho_array(i,j,k), dJinv, coef);
189  dqi = std::max(-qci_array(i,j,k), dqi);
190 
191  // Add this increment to both non-precipitating and total water.
192  qci_array(i,j,k) += dqi;
193  qn_array(i,j,k) += dqi;
194  qt_array(i,j,k) += dqi;
195 
196  // NOTE: Sedimentation does not affect the potential temperature,
197  // but it does affect the liquid/ice static energy.
198  // No source to Theta occurs here.
199  });
200  } // mfi
201  } // nsub
202 }
AMREX_ALWAYS_ASSERT_WITH_MESSAGE(axis >=0 &&axis< AMREX_SPACEDIM, "prob.axis must be 0, 1 or 2")
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE int sam_substep_count_from_reduced_flux(const amrex::Real &reduced_flux, const amrex::Real &dt, const amrex::Real &dz) noexcept
Definition: ERF_SAMUtils.H:922
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real sam_limit_precip_component_flux(const amrex::Real &raw_flux, const amrex::Real &rho_donor, const amrex::Real &q_donor, const amrex::Real &detJ_donor, const amrex::Real &coef) noexcept
Definition: ERF_SAMUtils.H:1098
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real sam_sedimentation_tendency(const amrex::Real &fz_hi, const amrex::Real &fz_lo, const amrex::Real &rho, const amrex::Real &dJinv, const amrex::Real &coef) noexcept
Definition: ERF_SAMUtils.H:1113
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real sam_cloud_ice_terminal_velocity(const amrex::Real &qci_avg) noexcept
Definition: ERF_SAMUtils.H:861
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE SAMFaceState sam_face_average_state(const int &k, const int &k_lo, const int &k_hi, const amrex::Real &rho_km1, const amrex::Real &rho_k, const amrex::Real tabs_km1, const amrex::Real tabs_k, const amrex::Real &, const amrex::Real &qci_k, const amrex::Real, const amrex::Real qp_k) noexcept
Definition: ERF_SAMUtils.H:869
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE int sam_precip_face_donor_k(const int &face_k, const int &k_lo, const int &k_hi) noexcept
Definition: ERF_SAMUtils.H:908
AMREX_FORCE_INLINE amrex::IntVect TileNoZ()
Definition: ERF_TileNoZ.H:11
const amrex::MultiFab * m_sed_inflow
Definition: ERF_NullMoist.H:168
amrex::MultiFab * m_detJ_cc
Definition: ERF_SAM.H:388
amrex::Real m_dzmin
Definition: ERF_SAM.H:384
@ qcl
Definition: ERF_Kessler.H:32
@ tabs
Definition: ERF_Kessler.H:27
@ rho
Definition: ERF_Kessler.H:25
@ qt
Definition: ERF_Kessler.H:30
@ ng
Definition: ERF_Morrison.H:51
@ qn
Definition: ERF_Morrison.H:36
@ qci
Definition: ERF_Morrison.H:39
@ precip_accum
Definition: ERF_Morrison.H:56
@ precip_accum
Definition: ERF_SAM.H:52
real(c_double), parameter epsilon
Definition: ERF_module_model_constants.F90:12
Definition: ERF_SAMUtils.H:133
amrex::Real qci_avg
Definition: ERF_SAMUtils.H:136
amrex::Real rho_avg
Definition: ERF_SAMUtils.H:134
int inflow_flag_comp() const
The component of a sedimentation inflow (see above) that flags the faces it applies at.
Definition: ERF_SedimentationFlux.H:54
std::shared_ptr< amrex::MultiFab > down
Definition: ERF_SedimentationFlux.H:39

Referenced by Advance().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ Init()

void SAM::Init ( const amrex::MultiFab &  cons_in,
const amrex::BoxArray &  grids,
const amrex::Geometry &  geom,
const amrex::Real &  dt_advance,
std::unique_ptr< amrex::MultiFab > &  z_phys_nd,
std::unique_ptr< amrex::MultiFab > &  detJ_cc 
)
overridevirtual

Initializes the Microphysics module.

Parameters
[in]cons_inConserved variables input
[in]qc_inCloud variables input
[in,out]qv_inVapor variables input
[in]qi_inIce variables input
[in]gridsThe boxes on which we will evolve the solution
[in]geomGeometry associated with these MultiFabs and grids
[in]dt_advanceTimestep for the advance

Reimplemented from NullMoist.

44 {
45  dt = dt_advance;
46  m_geom = geom;
47 
48  m_z_phys_nd = z_phys_nd.get();
49  m_detJ_cc = detJ_cc.get();
50 
51  MicVarMap.resize(m_qmoist_size);
53 
54  // initialize microphysics variables
55  for (auto ivar = 0; ivar < MicVar::NumVars; ++ivar) {
56  mic_fab_vars[ivar] = std::make_shared<MultiFab>(cons_in.boxArray(), cons_in.DistributionMap(),
57  1, cons_in.nGrowVect());
58  mic_fab_vars[ivar]->setVal(0.);
59  }
60 
61  m_sed.define(cons_in.boxArray(), cons_in.DistributionMap(),
62  {RhoQ4_comp, RhoQ5_comp, RhoQ6_comp, RhoQ3_comp});
63 
64  // NOTE: For multi-level not all ranks will own a box.
65  // Furthermore, the plane average allocates space
66  // for the entire domain. We make this consistent.
67  nlev = m_geom.Domain().length(2);
68  zlo = m_geom.Domain().smallEnd(2);
69  zhi = m_geom.Domain().bigEnd(2);
70 
71  // parameters
72  accrrc.resize({zlo}, {zhi});
73  accrsi.resize({zlo}, {zhi});
74  accrsc.resize({zlo}, {zhi});
75  coefice.resize({zlo}, {zhi});
76  evaps1.resize({zlo}, {zhi});
77  evaps2.resize({zlo}, {zhi});
78  accrgi.resize({zlo}, {zhi});
79  accrgc.resize({zlo}, {zhi});
80  evapg1.resize({zlo}, {zhi});
81  evapg2.resize({zlo}, {zhi});
82  evapr1.resize({zlo}, {zhi});
83  evapr2.resize({zlo}, {zhi});
84 
85  // data (input)
86  rho1d.resize({zlo}, {zhi});
87  pres1d.resize({zlo}, {zhi});
88  tabs1d.resize({zlo}, {zhi});
89 }
int m_qmoist_size
Definition: ERF_SAM.H:349
amrex::MultiFab * m_z_phys_nd
Definition: ERF_SAM.H:387
amrex::Vector< int > MicVarMap
Definition: ERF_SAM.H:361
@ NumVars
Definition: ERF_SAM.H:53
void define(const amrex::BoxArray &cc_ba, const amrex::DistributionMapping &dm, const amrex::Vector< int > &a_cons_comp)
Definition: ERF_SedimentationFlux.H:43

◆ NewtonIterSat()

AMREX_GPU_HOST_DEVICE static AMREX_FORCE_INLINE amrex::Real SAM::NewtonIterSat ( int &  i,
int &  j,
int &  k,
const int &  SAM_moisture_type,
const amrex::Real &  fac_cond,
const amrex::Real &  fac_fus,
const amrex::Real &  ,
const amrex::Real &  an,
const amrex::Real &  bn,
const amrex::Array4< amrex::Real > &  tabs_array,
const amrex::Array4< amrex::Real > &  pres_array,
const amrex::Array4< amrex::Real > &  qv_array,
const amrex::Array4< amrex::Real > &  qc_array,
const amrex::Array4< amrex::Real > &  qi_array,
const amrex::Array4< amrex::Real > &  qn_array,
const amrex::Array4< amrex::Real > &  qt_array 
)
inlinestatic
237  {
238  // Solution tolerance
239 #ifdef AMREX_USE_FLOAT
240  constexpr amrex::Real tol = amrex::Real(1.e-4);
241 #else
242  constexpr amrex::Real tol = amrex::Real(1.e-8);
243 #endif
244  constexpr int max_iter = 20;
245 
246  // Saturation moisture fractions
247  amrex::Real omn, domn;
248  amrex::Real qsatm, dqsatm;
249  amrex::Real qsatw, dqsatw;
250  amrex::Real qsati, dqsati;
251  amrex::Real qn, qif, dqif;
252 
253  // Newton iteration vars
254  int niter = 0;
255  amrex::Real F, dFdT;
256 
257  //=======================================================================
258  // Mixed-phase Newton iteration to qv=qsat (cloud phase only)
259  //=======================================================================
260 
261  // Initial guess for temperature & pressure
262  amrex::Real tabs = tabs_array(i,j,k);
263  amrex::Real pres = pres_array(i,j,k);
264 
265  // Mixed saturation moisture fractions
268  omn = sam_cloud_liquid_fraction(SAM_moisture_type, tabs, an, bn);
269  qsatm = sam_mixed_qsat(omn, qsatw, qsati);
270 
271  // Saturation derivative WRT temperature
272  domn = zero;
273  if ( (SAM_moisture_type == 1) &&
274  (tabs < tbgmax) && (tabs > tbgmin) ) {
275  domn = an;
276  }
277  erf_dtqsatw(tabs, pres, dqsatw);
278  erf_dtqsati(tabs, pres, dqsati);
279  dqsatm = sam_mixed_dqsat_dT(omn, domn, qsatw, qsati, dqsatw, dqsati);
280 
281  // Enforce omega splitting
282  qn = (qt_array(i,j,k) - qsatm);
283  qif = qn * (one - omn);
284  dqif = sam_mixed_dqif_dT(omn, domn, qn, dqsatm);
285 
286  // Function for root finding (conserve latent proxy):
287  // 0 = -T_new + T_old + L_v/C_p * (qv,0 - qs,m) - L_f/C_p * (qi,0 - qi,f)
288  F = sam_newton_residual(tabs, tabs_array(i,j,k),
289  fac_cond, qv_array(i,j,k), qsatm,
290  fac_fus , qi_array(i,j,k), qif );
291  while (std::abs(F) > tol && niter < max_iter) {
292  // Derivative of function (T_new iterated on)
293  dFdT = sam_newton_residual_derivative(fac_cond, fac_fus, dqsatm, dqif);
294 
295  // Update the temperature
296  tabs -= F/dFdT;
297 
298  // Mixed saturation moisture fractions
301  omn = sam_cloud_liquid_fraction(SAM_moisture_type, tabs, an, bn);
302  qsatm = sam_mixed_qsat(omn, qsatw, qsati);
303 
304  // Saturation derivative WRT temperature
305  domn = zero;
306  if ( (SAM_moisture_type == 1) &&
307  (tabs < tbgmax) && (tabs > tbgmin) ) {
308  domn = an;
309  }
310  erf_dtqsatw(tabs, pres, dqsatw);
311  erf_dtqsati(tabs, pres, dqsati);
312  dqsatm = sam_mixed_dqsat_dT(omn, domn, qsatw, qsati, dqsatw, dqsati);
313 
314  // Enforce omega splitting
315  qn = (qt_array(i,j,k) - qsatm);
316  qif = qn * (one - omn);
317  dqif = sam_mixed_dqif_dT(omn, domn, qn, dqsatm);
318 
319  // Function for root finding (conserve latent proxy):
320  // 0 = -T_new + T_old + L_v/C_p * (qv,0 - qs,m) - L_f/C_p * (qi,0 - qi,f)
321  F = sam_newton_residual(tabs, tabs_array(i,j,k),
322  fac_cond, qv_array(i,j,k), qsatm,
323  fac_fus , qi_array(i,j,k), qif );
324 
325  // Update iteration
326  ++niter;
327  }
328 
329  // NOTE: the while loop already enforces the iteration limit, so the
330  // residual alone determines whether we found a valid root; testing
331  // niter < max_iter here would spuriously fail a solve that
332  // converged on exactly the last allowed iteration.
333  bool valid_solution = (std::abs(F) <= tol);
334  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(valid_solution, "SAM mixed phase saturation adjustment failed to converge.");
335 
336  // Partition the change in non-precipitating q
337  qv_array(i,j,k) = qsatm;
338  qc_array(i,j,k) = qn * omn;
339  qi_array(i,j,k) = qn * (one - omn);
340  qn_array(i,j,k) = qn;
341  qt_array(i,j,k) = qv_array(i,j,k) + qn_array(i,j,k);
342 
343  // Return to temperature
344  return tabs;
345  }
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void erf_dtqsatw(amrex::Real t, amrex::Real p, amrex::Real &dtqsatw)
Definition: ERF_MicrophysicsUtils.H:280
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void erf_dtqsati(amrex::Real t, amrex::Real p, amrex::Real &dtqsati)
Definition: ERF_MicrophysicsUtils.H:271
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real sam_newton_residual(const amrex::Real &tabs_new, const amrex::Real &tabs_old, const amrex::Real fcond, const amrex::Real &qv, const amrex::Real &qsatm, const amrex::Real ffus, const amrex::Real &qi, const amrex::Real &qif) noexcept
Definition: ERF_SAMUtils.H:477
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real sam_mixed_dqif_dT(const amrex::Real &omn, const amrex::Real &domn, const amrex::Real &qn, const amrex::Real &dqsatm) noexcept
Definition: ERF_SAMUtils.H:467
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real sam_newton_residual_derivative(const amrex::Real fcond, const amrex::Real ffus, const amrex::Real &dqsatm, const amrex::Real &dqif) noexcept
Definition: ERF_SAMUtils.H:491
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real sam_cloud_liquid_fraction(const int &SAM_moisture_type, const amrex::Real &tabs, const amrex::Real an, const amrex::Real bn) noexcept
Definition: ERF_SAMUtils.H:346
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real sam_mixed_dqsat_dT(const amrex::Real &omn, const amrex::Real &domn, const amrex::Real &qsatw, const amrex::Real &qsati, const amrex::Real &dqsatw, const amrex::Real &dqsati) noexcept
Definition: ERF_SAMUtils.H:454
Here is the call graph for this function:

◆ Precip()

void SAM::Precip ( const SolverChoice &  sc)

Autoconversion (A30), Accretion (A28), Evaporation (A24)

13 {
14  if (sam_is_no_precip(sc.moisture_type)) return;
15 
16  Real powr1 = (three + b_rain) / Real(4.0);
17  Real powr2 = (Real(5.0) + b_rain) / Real(8.0);
18  Real pows1 = (three + b_snow) / Real(4.0);
19  Real pows2 = (Real(5.0) + b_snow) / Real(8.0);
20  Real powg1 = (three + b_grau) / Real(4.0);
21  Real powg2 = (Real(5.0) + b_grau) / Real(8.0);
22 
23  auto accrrc_t = accrrc.table();
24  auto accrsc_t = accrsc.table();
25  auto accrsi_t = accrsi.table();
26  auto accrgc_t = accrgc.table();
27  auto accrgi_t = accrgi.table();
28  auto coefice_t = coefice.table();
29  auto evapr1_t = evapr1.table();
30  auto evapr2_t = evapr2.table();
31  auto evaps1_t = evaps1.table();
32  auto evaps2_t = evaps2.table();
33  auto evapg1_t = evapg1.table();
34  auto evapg2_t = evapg2.table();
35 
36  Real fac_cond = m_fac_cond;
37  Real fac_sub = m_fac_sub;
38  Real fac_fus = m_fac_fus;
39  Real rdOcp = m_rdOcp;
40 
42 
43  Real dtn = dt;
44 
45  int SAM_moisture_type = 1;
46  if (sc.moisture_type == MoistureType::SAM_NoIce) {
47  SAM_moisture_type = 2;
48  }
49 
50  const SAMPrecipConfig precip_config{
51  SAM_moisture_type,
52  true,
53  dtn,
54  rdOcp,
55  fac_cond,
56  fac_fus,
57  fac_sub,
58  eps,
59  powr1,
60  pows1,
61  powg1,
62  powr2,
63  pows2,
64  powg2};
65  // get the temperature, density, theta, qt and qp from input
66  for ( MFIter mfi(*(mic_fab_vars[MicVar::tabs]),TilingIfNotGPU()); mfi.isValid(); ++mfi) {
67  auto rho_array = mic_fab_vars[MicVar::rho]->array(mfi);
68  auto theta_array = mic_fab_vars[MicVar::theta]->array(mfi);
69  auto tabs_array = mic_fab_vars[MicVar::tabs]->array(mfi);
70  auto pres_array = mic_fab_vars[MicVar::pres]->array(mfi);
71 
72  // Non-precipitating
73  auto qv_array = mic_fab_vars[MicVar::qv]->array(mfi);
74  auto qcl_array = mic_fab_vars[MicVar::qcl]->array(mfi);
75  auto qci_array = mic_fab_vars[MicVar::qci]->array(mfi);
76  auto qn_array = mic_fab_vars[MicVar::qn]->array(mfi);
77  auto qt_array = mic_fab_vars[MicVar::qt]->array(mfi);
78 
79  // Precipitating
80  auto qpr_array = mic_fab_vars[MicVar::qpr]->array(mfi);
81  auto qps_array = mic_fab_vars[MicVar::qps]->array(mfi);
82  auto qpg_array = mic_fab_vars[MicVar::qpg]->array(mfi);
83  auto qp_array = mic_fab_vars[MicVar::qp]->array(mfi);
84 
85  auto tbx = mfi.tilebox();
86 
87  ParallelFor(tbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
88  {
89  SAMCellState state{};
90  state.rho = rho_array(i,j,k);
91  state.theta = theta_array(i,j,k);
92  state.tabs = tabs_array(i,j,k);
93  state.pres_mbar = pres_array(i,j,k);
94  state.qv = qv_array(i,j,k);
95  state.qcl = qcl_array(i,j,k);
96  state.qci = qci_array(i,j,k);
97  state.qn = qn_array(i,j,k);
98  state.qt = qt_array(i,j,k);
99  state.qpr = qpr_array(i,j,k);
100  state.qps = qps_array(i,j,k);
101  state.qpg = qpg_array(i,j,k);
102  state.qp = qp_array(i,j,k);
103 
104  const SAMCoefficientRow coeffs{
105  accrrc_t(k),
106  accrsi_t(k),
107  accrsc_t(k),
108  coefice_t(k),
109  evaps1_t(k),
110  evaps2_t(k),
111  accrgi_t(k),
112  accrgc_t(k),
113  evapg1_t(k),
114  evapg2_t(k),
115  evapr1_t(k),
116  evapr2_t(k)};
117 
118  // The scalar helper owns the local held-pressure source update;
119  // this public kernel handles state staging and coefficient lookup.
120  state = sam_precip_cell_update(state, coeffs, precip_config);
121 
122  theta_array(i,j,k) = state.theta;
123  tabs_array(i,j,k) = state.tabs;
124  qv_array(i,j,k) = state.qv;
125  qcl_array(i,j,k) = state.qcl;
126  qci_array(i,j,k) = state.qci;
127  qn_array(i,j,k) = state.qn;
128  qt_array(i,j,k) = state.qt;
129  qpr_array(i,j,k) = state.qpr;
130  qps_array(i,j,k) = state.qps;
131  qpg_array(i,j,k) = state.qpg;
132  qp_array(i,j,k) = state.qp;
133  });
134  }
135 }
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE SAMCellState sam_precip_cell_update(SAMCellState state, const SAMCoefficientRow &coeffs, const SAMPrecipConfig &config, SAMPrecipCellDiagnostics *diagnostics=nullptr) noexcept
Definition: ERF_SAMUtils.H:652
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE bool sam_is_no_precip(const MoistureType &moisture_type) noexcept
Definition: ERF_SAMUtils.H:340
@ qp
Definition: ERF_SAM.H:44
Definition: ERF_SAMUtils.H:70

Referenced by Advance().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ PrecipFall()

void SAM::PrecipFall ( const SolverChoice &  sc)

Precipitation fluxes P_{r/s/g} (A19)

Code modified from SAMXX, the C++ version of the SAM code.

Parameters
[in]hydro_typeType selection for the precipitation advection hydrodynamics scheme (0-3)
20 {
21  if (sam_is_no_precip(sc.moisture_type)) return;
22 
23  // Conservative component-sedimentation contract: rain, snow, and graupel
24  // sediment with separate component fluxes. Each face flux is limited by
25  // the donor cell's available component mass, including detJ when present.
26  // The same limited bottom fluxes update surface accumulation and the
27  // in-column qpr/qps/qpg flux-divergence update. This closes component and
28  // total precipitation column budgets up to roundoff. Sedimentation does
29  // not update theta.
30 
31  Real rho_0 = Real(1.29);
32 
33  Real gamr3 = erf_gammafff(Real(4.0)+b_rain);
34  Real gams3 = erf_gammafff(Real(4.0)+b_snow);
35  Real gamg3 = erf_gammafff(Real(4.0)+b_grau);
36 
37  Real vrain = (a_rain*gamr3/Real(6.0))*std::pow((PI*rhor*nzeror),-crain);
38  Real vsnow = (a_snow*gams3/Real(6.0))*std::pow((PI*rhos*nzeros),-csnow);
39  Real vgrau = (a_grau*gamg3/Real(6.0))*std::pow((PI*rhog*nzerog),-cgrau);
40 
41  Real dtn = dt;
42  // NOTE: coef carries only the reference vertical spacing. The physical
43  // spacing is supplied by the inverse Jacobian in the tendency below,
44  // since detJ = dz_phys/CellSize(2). Dividing by m_dzmin here as well
45  // would apply the vertical metric twice. m_dzmin is still the correct
46  // length scale for the substep (CFL) count, which must bound the
47  // thinnest cell in the domain.
48  Real coef = dtn * m_geom.InvCellSize(2);
49 
50  auto domain = m_geom.Domain();
51  int k_lo = domain.smallEnd(2);
52  int k_hi = domain.bigEnd(2);
53 
57  auto qp = mic_fab_vars[MicVar::qp];
64 
65  auto ba = tabs->boxArray();
66  auto dm = tabs->DistributionMap();
67  auto ngrow = tabs->nGrowVect();
68 
69  MultiFab fz;
70  fz.define(convert(ba, IntVect(0,0,1)), dm, 3, ngrow);
71 
72  // The face states read tabs, rho and the precipitating species at k-1, a ghost cell
73  // whenever a box boundary is interior in z. Cloud and Precip have just updated them in
74  // valid cells only, so refresh the ghosts before the fluxes that size the substeps are
75  // computed; the substep loop below refreshes the species again as they fall. tabs does
76  // not change here, so once is enough for it.
77  tabs->FillBoundary(m_geom.periodicity());
78  rho->FillBoundary(m_geom.periodicity());
79  qpr->FillBoundary(m_geom.periodicity());
80  qps->FillBoundary(m_geom.periodicity());
81  qpg->FillBoundary(m_geom.periodicity());
82 
83  // Precompute the vertical component fluxes for the legacy reduced-flux substep rule.
84  for (MFIter mfi(fz, TilingIfNotGPU()); mfi.isValid(); ++mfi) {
85  auto qpr_array = qpr->const_array(mfi);
86  auto qps_array = qps->const_array(mfi);
87  auto qpg_array = qpg->const_array(mfi);
88  auto rho_array = rho->const_array(mfi);
89  auto tabs_array = tabs->const_array(mfi);
90  auto fz_array = fz.array(mfi);
91 
92  const auto& box3d = mfi.tilebox();
93 
94  ParallelFor(box3d, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
95  {
96  const SAMPrecipComponentFaceState face_state =
97  sam_precip_component_face_state(rho_array, tabs_array,
98  qpr_array, qps_array, qpg_array,
99  i, j, k, k_lo, k_hi);
100  const SAMPrecipFluxComponents raw_fluxes =
102  vrain, vsnow, vgrau);
103  const SAMPrecipFluxComponents corrected_fluxes =
105  rho_0,
106  face_state.rho_avg);
107 
108  fz_array(i,j,k,0) = corrected_fluxes.rain;
109  fz_array(i,j,k,1) = corrected_fluxes.snow;
110  fz_array(i,j,k,2) = corrected_fluxes.graupel;
111  });
112  }
113 
114  // Compute the legacy reduced-flux substep count from the maximum
115  // density-corrected sedimentation flux rather than a direct fall speed.
116  Real max_reduced_flux;
117  int n_substep;
118  auto const& ma_fz_arr = fz.const_arrays();
119  GpuTuple<Real> max = ParReduce(TypeList<ReduceOpMax>{},
120  TypeList<Real>{},
121  fz, IntVect(0),
122  [=] AMREX_GPU_DEVICE (int box_no, int i, int j, int k) noexcept
123  -> GpuTuple<Real>
124  {
125  return { ma_fz_arr[box_no](i,j,k,0)
126  + ma_fz_arr[box_no](i,j,k,1)
127  + ma_fz_arr[box_no](i,j,k,2) };
128  });
129  max_reduced_flux = get<0>(max);
130  // ParReduce over a MultiFab gives the local rank maximum here. PrecipFall
131  // needs one global substep count across ranks, including ranks that own no
132  // active tiles for this reduction, so keep the explicit MPI max reduction.
133  ParallelDescriptor::ReduceRealMax(max_reduced_flux);
134  max_reduced_flux += std::numeric_limits<Real>::epsilon();
136  "SAM::PrecipFall: the minimum cell height was never set (Set_dzmin), so the fall substeps cannot be sized");
137  n_substep = sam_substep_count_from_reduced_flux(max_reduced_flux, dtn, m_dzmin);
138  AMREX_ALWAYS_ASSERT(n_substep >= 1);
139  coef /= Real(n_substep);
140  dtn /= Real(n_substep);
141 
142  // Substep the vertical advection
143  for (int nsub(0); nsub<n_substep; ++nsub)
144  {
145  // Refresh ghosts so both sides of a shared z face use the same donor values
146  qpr->FillBoundary(m_geom.periodicity());
147  qps->FillBoundary(m_geom.periodicity());
148  qpg->FillBoundary(m_geom.periodicity());
149  mic_fab_vars[MicVar::rho]->FillBoundary(m_geom.periodicity());
150 
151  for (MFIter mfi(*qp, TileNoZ()); mfi.isValid(); ++mfi) {
152  auto qpr_array = qpr->array(mfi);
153  auto qps_array = qps->array(mfi);
154  auto qpg_array = qpg->array(mfi);
155  auto qp_array = qp->array(mfi);
156  auto rho_array = rho->const_array(mfi);
157  auto tabs_array = tabs->const_array(mfi);
158  auto fz_array = fz.array(mfi);
159 
160  auto rain_accum_array = rain_accum->array(mfi);
161  auto snow_accum_array = snow_accum->array(mfi);
162  auto graupel_accum_array = graupel_accum->array(mfi);
163  auto precip_accum_array = precip_accum->array(mfi);
164  auto sed_array = m_sed.down->array(mfi);
165  const auto inflow_array = (m_sed_inflow) ? m_sed_inflow->const_array(mfi)
166  : Array4<const Real>{};
167  const int inflow_flag = m_sed.inflow_flag_comp();
168  const Real inv_dt = one / dt;
169 
170  const auto dJ_array = (m_detJ_cc) ? m_detJ_cc->const_array(mfi) : Array4<const Real>{};
171 
172  const auto& tbx = mfi.tilebox();
173  const auto& tbz = mfi.tilebox(IntVect(0,0,1),IntVect(0));
174 
175  // Update vertical flux every substep
176  ParallelFor(tbz, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
177  {
178  const SAMPrecipComponentFaceState face_state =
179  sam_precip_component_face_state(rho_array, tabs_array,
180  qpr_array, qps_array, qpg_array,
181  i, j, k, k_lo, k_hi);
182  const SAMPrecipFluxComponents raw_fluxes =
184  vrain, vsnow, vgrau);
185  const SAMPrecipFluxComponents corrected_fluxes =
187  rho_0,
188  face_state.rho_avg);
189  const int donor_k = sam_precip_face_donor_k(k, k_lo, k_hi);
190  const Real detJ_donor = (dJ_array) ? dJ_array(i,j,donor_k) : one;
191 
192  // NOTE: Fz is the sedimentation flux from the advective operator.
193  // In the terrain-following coordinate system, the z-deriv in
194  // the divergence uses the normal velocity (Omega). However,
195  // there are no u/v components to the sedimentation velocity.
196  // Therefore, we simply end up with a division by detJ when
197  // evaluating the source term: dJinv * (flux_hi - flux_lo) * dzinv.
198  fz_array(i,j,k,0) = sam_limit_precip_component_flux(corrected_fluxes.rain,
199  rho_array(i,j,donor_k),
200  qpr_array(i,j,donor_k),
201  detJ_donor,
202  coef);
203  fz_array(i,j,k,1) = sam_limit_precip_component_flux(corrected_fluxes.snow,
204  rho_array(i,j,donor_k),
205  qps_array(i,j,donor_k),
206  detJ_donor,
207  coef);
208  fz_array(i,j,k,2) = sam_limit_precip_component_flux(corrected_fluxes.graupel,
209  rho_array(i,j,donor_k),
210  qpg_array(i,j,donor_k),
211  detJ_donor,
212  coef);
213 
214  // At the top of a fine patch the precipitation coming in is the coarse
215  // level's flux through that face, not one built from interpolated ghost cells
216  if (inflow_array && inflow_array(i,j,k,inflow_flag) > Real(0.5)) {
217  fz_array(i,j,k,0) = inflow_array(i,j,k,0) * inv_dt;
218  fz_array(i,j,k,1) = inflow_array(i,j,k,1) * inv_dt;
219  fz_array(i,j,k,2) = inflow_array(i,j,k,2) * inv_dt;
220  }
221 
222  sed_array(i,j,k,0) += fz_array(i,j,k,0) * dtn;
223  sed_array(i,j,k,1) += fz_array(i,j,k,1) * dtn;
224  sed_array(i,j,k,2) += fz_array(i,j,k,2) * dtn;
225 
226  if (k == k_lo) {
227  const SAMSurfaceAccumulation surface_accum =
229  {fz_array(i,j,k,0), fz_array(i,j,k,1), fz_array(i,j,k,2)},
230  dtn);
231  rain_accum_array(i,j,k) = rain_accum_array(i,j,k) + surface_accum.rain;
232  snow_accum_array(i,j,k) = snow_accum_array(i,j,k) + surface_accum.snow;
233  graupel_accum_array(i,j,k) = graupel_accum_array(i,j,k) + surface_accum.graupel;
234  // The total is in mm of liquid water, whatever the species' own densities
235  precip_accum_array(i,j,k) += (fz_array(i,j,k,0) + fz_array(i,j,k,1) + fz_array(i,j,k,2))
236  * dtn / rhor * Real(1000.0);
237  }
238  });
239 
240  // Update precip every substep
241  ParallelFor(tbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
242  {
243  // Jacobian determinant
244  Real dJinv = (dJ_array) ? one/dJ_array(i,j,k) : one;
245 
246  //==================================================
247  // Precipitating sedimentation (A19)
248  //==================================================
249  const Real dqpr = sam_sedimentation_tendency(fz_array(i,j,k+1,0), fz_array(i,j,k,0),
250  rho_array(i,j,k), dJinv, coef);
251  const Real dqps = sam_sedimentation_tendency(fz_array(i,j,k+1,1), fz_array(i,j,k,1),
252  rho_array(i,j,k), dJinv, coef);
253  const Real dqpg = sam_sedimentation_tendency(fz_array(i,j,k+1,2), fz_array(i,j,k,2),
254  rho_array(i,j,k), dJinv, coef);
255 
256  qpr_array(i,j,k) = std::max(Real(0.0), qpr_array(i,j,k) + dqpr);
257  qps_array(i,j,k) = std::max(Real(0.0), qps_array(i,j,k) + dqps);
258  qpg_array(i,j,k) = std::max(Real(0.0), qpg_array(i,j,k) + dqpg);
259  qp_array(i,j,k) = qpr_array(i,j,k) + qps_array(i,j,k) + qpg_array(i,j,k);
260 
261  // NOTE: Sedimentation does not affect the potential temperature,
262  // but it does affect the liquid/ice static energy.
263  // No source to Theta occurs here.
264  });
265  } // mfi
266  } // nsub
267 }
Real rho_0
Definition: ERF_InitCustomPert_ABL.H:18
constexpr amrex::Real csnow
Definition: ERF_MicrophysicsConstants.H:70
constexpr amrex::Real nzerog
Definition: ERF_MicrophysicsConstants.H:95
constexpr amrex::Real cgrau
Definition: ERF_MicrophysicsConstants.H:71
constexpr amrex::Real a_grau
Definition: ERF_MicrophysicsConstants.H:66
constexpr amrex::Real nzeror
Definition: ERF_MicrophysicsConstants.H:93
constexpr amrex::Real a_rain
Definition: ERF_MicrophysicsConstants.H:62
constexpr amrex::Real nzeros
Definition: ERF_MicrophysicsConstants.H:94
constexpr amrex::Real a_snow
Definition: ERF_MicrophysicsConstants.H:64
constexpr amrex::Real crain
Definition: ERF_MicrophysicsConstants.H:69
constexpr amrex::Real PI
Definition: ERF_NumericalConstants.H:46
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE SAMPrecipFluxComponents sam_precip_component_fluxes_from_face_state(const SAMPrecipComponentFaceState &face_state, const amrex::Real &vrain, const amrex::Real &vsnow, const amrex::Real &vgrau) noexcept
Definition: ERF_SAMUtils.H:1056
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE SAMSurfaceAccumulation sam_surface_accumulation_from_component_fluxes(const SAMPrecipFluxComponents &component_fluxes, const amrex::Real &dtn) noexcept
Definition: ERF_SAMUtils.H:1126
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE SAMPrecipComponentFaceState sam_precip_component_face_state(const amrex::Array4< const amrex::Real > &rho_array, const amrex::Array4< const amrex::Real > &tabs_array, const amrex::Array4< const amrex::Real > &qpr_array, const amrex::Array4< const amrex::Real > &qps_array, const amrex::Array4< const amrex::Real > &qpg_array, const int &i, const int &j, const int &k, const int &k_lo, const int &k_hi) noexcept
Definition: ERF_SAMUtils.H:973
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE SAMPrecipFluxComponents sam_precip_flux_components_density_corrected(const SAMPrecipFluxComponents &precip_fluxes, const amrex::Real &rho_0, const amrex::Real &rho_avg) noexcept
Definition: ERF_SAMUtils.H:1083
@ qp
Definition: ERF_Kessler.H:34
@ rain_accum
Definition: ERF_Kessler.H:36
@ graupel_accum
Definition: ERF_Morrison.H:55
@ qpg
Definition: ERF_Morrison.H:44
@ qps
Definition: ERF_Morrison.H:43
@ qpr
Definition: ERF_Morrison.H:42
@ snow_accum
Definition: ERF_Morrison.H:54
Definition: ERF_SAMUtils.H:125
amrex::Real rho_avg
Definition: ERF_SAMUtils.H:126
Definition: ERF_SAMUtils.H:146
amrex::Real snow
Definition: ERF_SAMUtils.H:148
amrex::Real rain
Definition: ERF_SAMUtils.H:147
amrex::Real graupel
Definition: ERF_SAMUtils.H:149
Definition: ERF_SAMUtils.H:140
amrex::Real rain
Definition: ERF_SAMUtils.H:141
amrex::Real snow
Definition: ERF_SAMUtils.H:142
amrex::Real graupel
Definition: ERF_SAMUtils.H:143

Referenced by Advance().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ Qmoist_Ptr() [1/2]

const amrex::MultiFab* SAM::Qmoist_Ptr ( const int &  varIdx) const
inline
165  {
167  return mic_fab_vars[MicVarMap[varIdx]].get();
168  }
Here is the call graph for this function:

◆ Qmoist_Ptr() [2/2]

amrex::MultiFab* SAM::Qmoist_Ptr ( const int &  varIdx)
inlineoverridevirtual

Reimplemented from NullMoist.

158  {
160  return mic_fab_vars[MicVarMap[varIdx]].get();
161  }
Here is the call graph for this function:

◆ Qmoist_Restart_Vars()

void SAM::Qmoist_Restart_Vars ( const SolverChoice &  ,
std::vector< int > &  a_idx,
std::vector< std::string > &  a_names 
) const
inlineoverridevirtual

Reimplemented from NullMoist.

189  {
190  a_idx.clear();
191  a_names.clear();
192 
193  // NOTE: These are the indices to access into qmoist (not mic_fab_vars)
194  a_idx.push_back(0); a_names.push_back("RainAccum");
195  a_idx.push_back(1); a_names.push_back("SnowAccum");
196  a_idx.push_back(2); a_names.push_back("GraupAccum");
197  a_idx.push_back(3); a_names.push_back("PrecipAccum");
198  }

◆ Qmoist_Size()

int SAM::Qmoist_Size ( )
inlineoverridevirtual

Reimplemented from NullMoist.

177 { return SAM::m_qmoist_size; }

◆ Qstate_Moist_NumConc_Size()

int SAM::Qstate_Moist_NumConc_Size ( )
inlineoverridevirtual

Reimplemented from NullMoist.

int n_qstate_moist_numconc_size
Definition: ERF_SAM.H:355

◆ Qstate_Moist_Size()

int SAM::Qstate_Moist_Size ( )
inlineoverridevirtual

Reimplemented from NullMoist.

180 { return SAM::n_qstate_moist_size; }
int n_qstate_moist_size
Definition: ERF_SAM.H:352

◆ Sed_Flux()

const SedimentationFlux* SAM::Sed_Flux ( ) const
inlineoverridevirtual

The sedimentation face fluxes of the most recent Advance, or null for a scheme that does not sediment (see ERF_SedimentationFlux.H)

Reimplemented from NullMoist.

154 { return &m_sed; }

◆ Set_dzmin()

void SAM::Set_dzmin ( const amrex::Real  dz_min)
inlineoverridevirtual

Reimplemented from NullMoist.

106  {
107  m_dzmin = dz_min;
108  }

◆ Update_Micro_Vars() [1/2]

void SAM::Update_Micro_Vars ( amrex::MultiFab &  cons_in)
inlineoverridevirtual

Reimplemented from NullMoist.

128  {
129  this->Copy_State_to_Micro(cons_in);
131  }
bool m_use_anelastic_reference_pressure
Definition: ERF_NullMoist.H:191
void Copy_State_to_Micro(const amrex::MultiFab &cons_in) override
void Compute_Coefficients(bool use_anelastic_reference_pressure=false)
Definition: ERF_InitSAM.cpp:157
Here is the call graph for this function:

◆ Update_Micro_Vars() [2/2]

void SAM::Update_Micro_Vars ( amrex::MultiFab &  cons_in,
const amrex::MultiFab *  base_state 
)
overridevirtual

Reimplemented from NullMoist.

◆ Update_State_Vars()

void SAM::Update_State_Vars ( amrex::MultiFab &  cons_in,
const amrex::MultiFab &   
)
inlineoverridevirtual

Reimplemented from NullMoist.

136  {
137  this->Copy_Micro_to_State(cons_in);
138  }
void Copy_Micro_to_State(amrex::MultiFab &cons_in) override
Definition: ERF_UpdateSAM.cpp:15
Here is the call graph for this function:

Member Data Documentation

◆ accrgc

amrex::TableData<amrex::Real, 1> SAM::accrgc
private

◆ accrgi

amrex::TableData<amrex::Real, 1> SAM::accrgi
private

◆ accrrc

amrex::TableData<amrex::Real, 1> SAM::accrrc
private

◆ accrsc

amrex::TableData<amrex::Real, 1> SAM::accrsc
private

◆ accrsi

amrex::TableData<amrex::Real, 1> SAM::accrsi
private

◆ CFL_MAX

constexpr amrex::Real SAM::CFL_MAX = myhalf
staticconstexprprivate

◆ coefice

amrex::TableData<amrex::Real, 1> SAM::coefice
private

◆ dt

amrex::Real SAM::dt
private

Referenced by Advance().

◆ evapg1

amrex::TableData<amrex::Real, 1> SAM::evapg1
private

◆ evapg2

amrex::TableData<amrex::Real, 1> SAM::evapg2
private

◆ evapr1

amrex::TableData<amrex::Real, 1> SAM::evapr1
private

◆ evapr2

amrex::TableData<amrex::Real, 1> SAM::evapr2
private

◆ evaps1

amrex::TableData<amrex::Real, 1> SAM::evaps1
private

◆ evaps2

amrex::TableData<amrex::Real, 1> SAM::evaps2
private

◆ m_axis

int SAM::m_axis
private

Referenced by Define().

◆ m_detJ_cc

amrex::MultiFab* SAM::m_detJ_cc
private

◆ m_do_cond

bool SAM::m_do_cond
private

Referenced by Define().

◆ m_dzmin

amrex::Real SAM::m_dzmin {0.0}
private

Referenced by Set_dzmin().

◆ m_fac_cond

amrex::Real SAM::m_fac_cond
private

Referenced by Define().

◆ m_fac_fus

amrex::Real SAM::m_fac_fus
private

Referenced by Define().

◆ m_fac_sub

amrex::Real SAM::m_fac_sub
private

Referenced by Define().

◆ m_geom

amrex::Geometry SAM::m_geom
private

◆ m_moisture_type

MoistureType SAM::m_moisture_type = MoistureType::None
private

◆ m_qmoist_size

int SAM::m_qmoist_size = 4
private

Referenced by Qmoist_Ptr(), and Qmoist_Size().

◆ m_rdOcp

amrex::Real SAM::m_rdOcp {RdoCp}
private

Referenced by Define().

◆ m_sed

SedimentationFlux SAM::m_sed
private

Referenced by Advance(), and Sed_Flux().

◆ m_z_phys_nd

amrex::MultiFab* SAM::m_z_phys_nd
private

◆ mic_fab_vars

amrex::Array<FabPtr, MicVar::NumVars> SAM::mic_fab_vars
private

◆ MicVarMap

amrex::Vector<int> SAM::MicVarMap
private

Referenced by Qmoist_Ptr().

◆ n_qstate_moist_numconc_size

int SAM::n_qstate_moist_numconc_size = 0
private

◆ n_qstate_moist_size

int SAM::n_qstate_moist_size = 6
private

Referenced by Qstate_Moist_Size().

◆ nlev

int SAM::nlev
private

◆ pres1d

amrex::TableData<amrex::Real, 1> SAM::pres1d
private

◆ qn1d

amrex::TableData<amrex::Real, 1> SAM::qn1d
private

◆ qt1d

amrex::TableData<amrex::Real, 1> SAM::qt1d
private

◆ qv1d

amrex::TableData<amrex::Real, 1> SAM::qv1d
private

◆ rho1d

amrex::TableData<amrex::Real, 1> SAM::rho1d
private

◆ tabs1d

amrex::TableData<amrex::Real, 1> SAM::tabs1d
private

◆ zhi

int SAM::zhi
private

◆ zlo

int SAM::zlo
private

The documentation for this class was generated from the following files: