ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
ERF_InitDensityHSE.H
Go to the documentation of this file.
1 /**
2  * Initialize hydrostatically balanced density
3  *
4  * Calls init_isentropic_hse_terrain() or init_isentropic_hse() for cases with
5  * and without terrain, respectively. Hydrostatic equilibrium (HSE) is satisfied discretely.
6  * Note that these routines presume that qv==0 when evaluating the EOS. Both
7  * density and pressure in HSE are calculated but only the density is used at
8  * this point.
9 */
10 
11 #include "ERF_HSEUtils.H"
12 
13 /**
14  * @brief Initialize density in hydrostatic equilibrium for dry cases.
15  *
16  * @param[out] rho_hse MultiFab to be filled with HSE density.
17  * @param z_phys_nd Unused nodal physical height MultiFab.
18  * @param[in] z_phys_cc Cell-centered physical height MultiFab.
19  * @param[in] geom Geometry defining the domain.
20  * @param[in] stretched_dz_h Vector of stretched grid spacings.
21  * @param[in] is_constant_dz Whether the grid spacing is constant.
22  * @param[in] is_stretched_dz Whether the grid spacing is stretched.
23  */
24 void
25 erf_init_dens_hse_dry (amrex::MultiFab& rho_hse,
26  std::unique_ptr<amrex::MultiFab>& /*z_phys_nd*/,
27  std::unique_ptr<amrex::MultiFab>& z_phys_cc,
28  amrex::Geometry const& geom,
29  const amrex::Vector<amrex::Real>& stretched_dz_h,
30  bool is_constant_dz, bool is_stretched_dz) override
31 {
32  const amrex::Real T_sfc = amrex::Real(300.);
33  const amrex::Real rho_sfc = p_0 / (R_d*T_sfc);
34  const amrex::Real Thetabar = T_sfc;
35 
36  if (!is_constant_dz && !is_stretched_dz) {
37 
38  const int domlo_z = geom.Domain().smallEnd(2);
39  const int domhi_z = geom.Domain().bigEnd(2);
40  if (domhi_z > 255) amrex::Abort("1D Arrays are hard-wired to only 256 high");
41 
42  //
43  // A column is integrated upward from the bottom of its box. A box stacked on another box
44  // of this level (the BoxArray is split in z, e.g. amr.max_grid_size below the number of
45  // cells in z) must continue from the density that box reached, not start afresh from
46  // the ghost cell below it. The boxes are therefore integrated in bands of equal lowest
47  // index, bottom up, and before each band the cells just below it are filled from the
48  // bands already done. With a single band nothing is filled.
49  //
50  const amrex::Vector<int> bands = column_bands(rho_hse.boxArray());
51 
52  for (const int klo_band : bands)
53  {
54  if (klo_band != bands[0]) {
55  fill_below_band(rho_hse, 0, 1, klo_band, amrex::IntVect(1,1,0), geom);
56  }
57 
58  for ( amrex::MFIter mfi(rho_hse, TileNoZ()); mfi.isValid(); ++mfi )
59  {
60  amrex::Array4<amrex::Real > rho_arr = rho_hse.array(mfi);
61  amrex::Array4<amrex::Real const> z_cc_arr = z_phys_cc->const_array(mfi);
62 
63  // Create a flat box with same horizontal extent but only one cell in vertical
64  const amrex::Box& tbz = mfi.nodaltilebox(2);
65  amrex::Box b2d = tbz; // Copy constructor
66  b2d.grow(0,1); b2d.grow(1,1); // Grow by one in the lateral directions
67  b2d.setRange(2,0);
68 
69  const int klo = tbz.smallEnd(2);
70  const int khi = tbz.bigEnd(2)-1;
71 
72  if (klo != klo_band) { continue; }
73 
74  amrex::ParallelFor(b2d, [=] AMREX_GPU_DEVICE (int i, int j, int)
75  {
76  //
77  // The density we start each column's integration from. If this box does
78  // not reach the bottom of the domain then that is the density in the
79  // ghost cell below the box: the density the box of this level below it
80  // reached or, where there is no such box, the one interpolated from the
81  // coarser level.
82  //
83  // NOTE: we must do this lookup here, inside the kernel, for two reasons:
84  // rho_arr is device memory, so reading it on the host is invalid in
85  // a GPU build, and -- more importantly -- each column has its own
86  // surface density, so we must not use the value at one corner of
87  // the box for every column.
88  //
89  const amrex::Real rho_local_sfc = (klo == 0) ? rho_sfc : rho_arr(i,j,klo-1);
90 
91  amrex::Array1D<amrex::Real,0,255> r;
92  amrex::Array1D<amrex::Real,0,255> p;
93  HSEutils::init_isentropic_hse_terrain(i,j,rho_local_sfc,Thetabar,&(r(0)),&(p(0)),z_cc_arr,klo,khi);
94 
95  for (int k = klo; k <= khi; k++) {
96  rho_arr(i,j,k) = r(k);
97  }
98 
99  // Impose Neumann conditions at bottom and top of domain boundary
100  if (klo == domlo_z) {
101  rho_arr(i,j,domlo_z-1) = rho_arr(i,j,domlo_z);
102  }
103  if (khi == domhi_z) {
104  rho_arr(i,j,domhi_z+1) = rho_arr(i,j,domhi_z);
105  }
106  });
107  } // mfi
108  } // band
109 
110  } else {
111 
112 
113  // Note that this integrates over the entire domain height,
114  // but only copies into each box as appropriate.
115  // There is no assumption that any one box spans the whole vertical height.
116  const int klo = geom.Domain().smallEnd(2);
117  const int khi = geom.Domain().bigEnd(2);
118 
119  // These are at cell centers (unstaggered)
120  amrex::Vector<amrex::Real> h_r(khi+2);
121  amrex::Vector<amrex::Real> h_p(khi+2);
122 
123  amrex::Gpu::DeviceVector<amrex::Real> d_r(khi+2);
124  amrex::Gpu::DeviceVector<amrex::Real> d_p(khi+2);
125 
126  if (is_constant_dz) {
127  const amrex::Real dz = geom.CellSize()[2];
128  HSEutils::init_isentropic_hse_constant_dz(rho_sfc,Thetabar,h_r.data(),h_p.data(),dz,geom.ProbLo(2),klo,khi);
129  } else { // stretched_dz
130  HSEutils::init_isentropic_hse_stretched_dz(rho_sfc,Thetabar,h_r.data(),h_p.data(),stretched_dz_h.data(),geom.ProbLo(2),klo,khi);
131  }
132 
133  amrex::Gpu::copyAsync(amrex::Gpu::hostToDevice, h_r.begin(), h_r.end(), d_r.begin());
134  amrex::Gpu::copyAsync(amrex::Gpu::hostToDevice, h_p.begin(), h_p.end(), d_p.begin());
135 
136  amrex::Real* r = d_r.data();
137 
138 #ifdef _OPENMP
139 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
140 #endif
141  for ( amrex::MFIter mfi(rho_hse, amrex::TilingIfNotGPU()); mfi.isValid(); ++mfi )
142  {
143  const amrex::Box& bx = mfi.growntilebox(1);
144  const amrex::Array4<amrex::Real> rho_hse_arr = rho_hse[mfi].array();
145  amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
146  {
147  int kk = std::max(k,0);
148  rho_hse_arr(i,j,k) = r[kk];
149  });
150  } // mfi
151 
152  // The kernels above read d_r; wait for them before d_r is freed
153  amrex::Gpu::streamSynchronize();
154  }
155 }
156 
157 /**
158  * @brief Initialize density in hydrostatic equilibrium for moist cases.
159  *
160  * @param[out] rho_hse MultiFab to be filled with HSE density.
161  * @param z_phys_nd Unused nodal physical height MultiFab.
162  * @param[in] geom Geometry defining the domain.
163  * @param[in] zlevels_stag Staggered vertical levels of this level; the heights on a mesh
164  * other than ConstantDz, where CellSize(2) is not physical.
165  */
166 void
167 erf_init_dens_hse_moist (amrex::MultiFab& rho_hse,
168  std::unique_ptr<amrex::MultiFab>& /*z_phys_nd*/,
169  amrex::Geometry const& geom,
170  const amrex::Vector<amrex::Real>& zlevels_stag) override
171 {
172  const amrex::Real dz = geom.CellSize()[2];
173  const int khi = geom.Domain().bigEnd()[2];
174 
175  // These are at cell centers (unstaggered)
176  amrex::Vector<amrex::Real> h_r(khi+2);
177  amrex::Vector<amrex::Real> h_p(khi+2);
178  amrex::Vector<amrex::Real> h_t(khi+2);
179  amrex::Vector<amrex::Real> h_q_v(khi+2);
180 
181  amrex::ParmParse pp("prob");
182  amrex::Real q_t = amrex::Real(0.02);
183  pp.query("qt_init",q_t);
184 
186  pp.query("eq_pot_temp",eq_pot_temp);
187 
188  bool use_empirical = false;
189  pp.query("use_empirical_psat",use_empirical);
190 
191  amrex::Real z_tr_1 = -one;
192  amrex::Real z_tr_2 = -one;
193  pp.query("height", z_tr_1);
194  pp.query("z_tr", z_tr_2);
195 
196  // Used only with T_from_theta_in_moist_init, but kept at physical values:
197  // zeros raise an FPE in the speculatively evaluated compute_theta arm.
199 
200  bool T_from_theta = false;
201  pp.query("T_from_theta_in_moist_init", T_from_theta);
202 
203  if (T_from_theta) {
204  pp.get("theta_tr",theta_tr); pp.get("theta_0",theta_0);
205  pp.get("T_tr",T_tr);
206  }
207 
208  if (use_physical_z()) {
209  HSEutils::init_isentropic_hse_no_terrain( h_t.data(), h_r.data(), h_p.data(),
210  h_q_v.data(), zlevels_stag.data(), khi, q_t, eq_pot_temp,
211  use_empirical, T_from_theta, z_tr_1, z_tr_2,
212  theta_0, theta_tr, T_tr);
213  } else {
214  HSEutils::init_isentropic_hse_no_terrain( h_t.data(), h_r.data(), h_p.data(),
215  h_q_v.data(), dz, geom.ProbLo(2), khi, q_t, eq_pot_temp, use_empirical,
216  T_from_theta, z_tr_1, z_tr_2,
217  theta_0, theta_tr, T_tr);
218  }
219 
220  amrex::Gpu::DeviceVector<amrex::Real> d_r(khi+2);
221  amrex::Gpu::copy(amrex::Gpu::hostToDevice, h_r.begin(), h_r.end(), d_r.begin());
222  amrex::Real* r = d_r.data();
223 
224 #ifdef _OPENMP
225 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
226 #endif
227  for ( amrex::MFIter mfi(rho_hse,amrex::TilingIfNotGPU()); mfi.isValid(); ++mfi)
228  {
229  const amrex::Box& bx = mfi.growntilebox(1);
230  const amrex::Array4<amrex::Real> rho_hse_arr = rho_hse[mfi].array();
231  amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
232  {
233  int kk = std::max(k,0);
234  rho_hse_arr(i,j,k) = r[kk];
235  });
236  } // mfi
237 
238  // The kernels above read d_r; wait for them before d_r is freed
239  amrex::Gpu::streamSynchronize();
240 }
241 
242 /**
243  * @brief Initialize HSE density as a constant value.
244  *
245  * @param[out] rho_hse MultiFab to be filled with constant HSE density.
246  */
247 void
248 erf_init_const_dens_hse (amrex::MultiFab& rho_hse) override
249 {
250  amrex::Real rho_0 = base_parms.rho_0;
251  for ( amrex::MFIter mfi(rho_hse, amrex::TilingIfNotGPU()); mfi.isValid(); ++mfi )
252  {
253  amrex::Array4<amrex::Real> rho_hse_arr = rho_hse.array(mfi);
254  const amrex::Box& gbx = mfi.growntilebox(1);
255  ParallelFor(gbx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
256  {
257  rho_hse_arr(i,j,k) = rho_0;
258  });
259  }
260 }
261 /**
262  * @brief Initialize constant density and potential temperature in HSE.
263  *
264  * @param[out] rho_hse MultiFab for HSE density.
265  * @param[out] p_hse MultiFab for HSE pressure.
266  * @param[out] pi_hse MultiFab for HSE internal energy.
267  * @param[out] th_hse MultiFab for HSE potential temperature.
268  * @param[out] qv_hse MultiFab for HSE water vapor mixing ratio.
269  * @param[in] l_rdOcp Constant R_d/Cp.
270  */
271 void
272 erf_init_const_dens_and_th_hse (amrex::MultiFab& rho_hse, amrex::MultiFab& p_hse,
273  amrex::MultiFab& pi_hse, amrex::MultiFab& th_hse,
274  amrex::MultiFab& qv_hse, amrex::Real l_rdOcp) override
275 {
276  amrex::Real rho_0 = base_parms.rho_0;
277  amrex::Real T_0 = base_parms.T_0;
278 
279  amrex::ParmParse pp_prob("prob");
280  amrex::Real dtheta_dz = zero; pp_prob.query("dtheta_dz",dtheta_dz);
281 
282  rho_hse.setVal(rho_0);
283  th_hse.setVal(T_0);
284 
285  amrex::Real rt0 = rho_0 * T_0;
286 
287  amrex::Real p0 = getPgivenRTh(rt0);
288  p_hse.setVal(p0);
289 
290  amrex::Real pi0 = getExnergivenRTh(rt0, l_rdOcp);
291 
292  pi_hse.setVal(pi0);
293 
294  // Default to zero background moisture
295  qv_hse.setVal(0.0);
296 }
297 
298 /**
299  * @brief Initialize constant density and linearly varying potential temperature in HSE.
300  *
301  * @param[out] rho_hse MultiFab for HSE density.
302  * @param[out] p_hse MultiFab for HSE pressure.
303  * @param[out] pi_hse MultiFab for HSE internal energy.
304  * @param[out] th_hse MultiFab for HSE potential temperature.
305  * @param[out] qv_hse MultiFab for HSE water vapor mixing ratio.
306  * @param[in] l_rdOcp Constant R_d/Cp.
307  * @param[in] z_phys_cc Cell-centered physical height MultiFab.
308  */
309 void
310 erf_init_const_dens_and_linear_th_hse (amrex::MultiFab& rho_hse, amrex::MultiFab& p_hse,
311  amrex::MultiFab& pi_hse, amrex::MultiFab& th_hse,
312  amrex::MultiFab& qv_hse, amrex::Real l_rdOcp,
313  std::unique_ptr<amrex::MultiFab>& z_phys_cc) override
314 {
315  amrex::Real rho_0 = base_parms.rho_0;
316  amrex::Real T_0 = base_parms.T_0;
317 
318  amrex::ParmParse pp_prob("prob");
319  amrex::Real dtheta_dz = zero; pp_prob.query("dtheta_dz",dtheta_dz);
320 
321  rho_hse.setVal(rho_0);
322 
323  for ( amrex::MFIter mfi(th_hse, amrex::TilingIfNotGPU()); mfi.isValid(); ++mfi )
324  {
325  amrex::Array4<amrex::Real> th_hse_arr = th_hse.array(mfi);
326  amrex::Array4<amrex::Real> p_hse_arr = p_hse.array(mfi);
327  amrex::Array4<amrex::Real> pi_hse_arr = pi_hse.array(mfi);
328  amrex::Array4<amrex::Real const> z_arr = z_phys_cc->array(mfi);
329  const amrex::Box& gbx = mfi.growntilebox(1);
330  ParallelFor(gbx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
331  {
332  th_hse_arr(i,j,k) = T_0 + dtheta_dz * (z_arr(i,j,k)- z_arr(i,j,0));
333  p_hse_arr(i,j,k) = getPgivenRTh(rho_0*th_hse_arr(i,j,k));
334  pi_hse_arr(i,j,k) = getExnergivenRTh(rho_0*th_hse_arr(i,j,k), l_rdOcp);
335  });
336  } // mfi
337 
338  // Default to zero background moisture
339  qv_hse.setVal(0.0);
340 }
Vector< int > column_bands(const BoxArray &ba)
Definition: ERF_ColumnBands.cpp:13
void fill_below_band(MultiFab &mf, int icomp, int ncomp, int klo_band, const IntVect &lateral_ng, const Geometry &geom)
Definition: ERF_ColumnBands.cpp:23
constexpr amrex::Real p_0
Definition: ERF_Constants.H:69
constexpr amrex::Real R_d
Definition: ERF_Constants.H:49
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real getExnergivenRTh(const amrex::Real rhotheta, const amrex::Real rdOcp, const amrex::Real qv=amrex::Real(0))
Definition: ERF_EOS.H:156
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
Real rho_0
Definition: ERF_InitCustomPert_ABL.H:18
ParmParse pp_prob("prob")
Real T_0
Definition: ERF_InitCustomPert_ABL.H:19
ParmParse pp("prob")
const int khi
Definition: ERF_InitCustomPert_Bubble.H:25
const int klo
Definition: ERF_InitCustomPert_KE.H:9
Real eq_pot_temp
Definition: ERF_InitCustomPert_MultiSpeciesBubble.H:27
bool use_empirical
Definition: ERF_InitCustomPert_MultiSpeciesBubble.H:29
Vector< Real > h_t(khi+2)
Real T_tr
Definition: ERF_InitCustomPert_SquallLine.H:47
Real q_t
Definition: ERF_InitCustomPert_SquallLine.H:28
Vector< Real > h_q_v(khi+2)
Gpu::DeviceVector< Real > d_p(khi+2)
Vector< Real > h_r(khi+2)
Real theta_tr
Definition: ERF_InitCustomPert_SquallLine.H:51
Vector< Real > h_p(khi+2)
Gpu::DeviceVector< Real > d_r(khi+2)
Real theta_0
Definition: ERF_InitCustomPert_SquallLine.H:50
void erf_init_const_dens_hse(amrex::MultiFab &rho_hse) override
Initialize HSE density as a constant value.
Definition: ERF_InitDensityHSE.H:248
void erf_init_dens_hse_moist(amrex::MultiFab &rho_hse, std::unique_ptr< amrex::MultiFab > &, amrex::Geometry const &geom, const amrex::Vector< amrex::Real > &zlevels_stag) override
Initialize density in hydrostatic equilibrium for moist cases.
Definition: ERF_InitDensityHSE.H:167
void erf_init_const_dens_and_linear_th_hse(amrex::MultiFab &rho_hse, amrex::MultiFab &p_hse, amrex::MultiFab &pi_hse, amrex::MultiFab &th_hse, amrex::MultiFab &qv_hse, amrex::Real l_rdOcp, std::unique_ptr< amrex::MultiFab > &z_phys_cc) override
Initialize constant density and linearly varying potential temperature in HSE.
Definition: ERF_InitDensityHSE.H:310
void erf_init_const_dens_and_th_hse(amrex::MultiFab &rho_hse, amrex::MultiFab &p_hse, amrex::MultiFab &pi_hse, amrex::MultiFab &th_hse, amrex::MultiFab &qv_hse, amrex::Real l_rdOcp) override
Initialize constant density and potential temperature in HSE.
Definition: ERF_InitDensityHSE.H:272
void erf_init_dens_hse_dry(amrex::MultiFab &rho_hse, std::unique_ptr< amrex::MultiFab > &, std::unique_ptr< amrex::MultiFab > &z_phys_cc, amrex::Geometry const &geom, const amrex::Vector< amrex::Real > &stretched_dz_h, bool is_constant_dz, bool is_stretched_dz) override
Initialize density in hydrostatic equilibrium for dry cases.
Definition: ERF_InitDensityHSE.H:25
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::Real Real
Definition: ERF_ShocInterface.H:19
AMREX_FORCE_INLINE amrex::IntVect TileNoZ()
Definition: ERF_TileNoZ.H:11
auto rho_arr
Definition: ERF_UpdateWSubsidence_SineMassFlux.H:3
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void init_isentropic_hse_constant_dz(const amrex::Real &r_sfc, const amrex::Real &theta, amrex::Real *r, amrex::Real *p, const amrex::Real &dz, const amrex::Real &z_bot, const int klo, const int khi)
Definition: ERF_HSEUtils.H:112
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void init_isentropic_hse_stretched_dz(const amrex::Real &r_sfc, const amrex::Real &theta, amrex::Real *r, amrex::Real *p, const amrex::Real *stretched_dz, const amrex::Real &z_bot, const int klo, const int khi)
Definition: ERF_HSEUtils.H:226
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void init_isentropic_hse_terrain(int i, int j, const amrex::Real &r_sfc, const amrex::Real &theta, amrex::Real *r, amrex::Real *p, const amrex::Array4< amrex::Real const > z_cc, const int &klo, const int &khi)
Definition: ERF_HSEUtils.H:342
AMREX_FORCE_INLINE AMREX_GPU_HOST_DEVICE void init_isentropic_hse_no_terrain(Real *theta, Real *r, Real *p, Real *q_v, const Real &dz, const Real &z_bot, const int &khi, const Real q_t, const Real eq_pot_temp, const bool use_empirical, const bool T_from_theta=false, const Real z_tr_1=-one, const Real z_tr_2=-one, const Real theta_0=amrex::Real(300), const Real theta_tr=amrex::Real(300), const Real T_tr=amrex::Real(300))
Definition: ERF_HSEUtils.H:769
@ dz
Definition: ERF_AdvanceWDM6.cpp:273
@ p
Definition: ERF_WSM6.H:301
real(c_double), parameter p0
Definition: ERF_module_model_constants.F90:40