ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
ERF_Rebalance.cpp File Reference
#include "ERF_ColumnBands.H"
#include "ERF_Constants.H"
#include "ERF_HSEUtils.H"
#include "ERF_Utils.H"
Include dependency graph for ERF_Rebalance.cpp:

Functions

void rebalance_columns (MultiFab &rho, MultiFab &theta, const MultiFab &qv, const MultiFab &qt, const MultiFab *z_phys, const Geometry &geom, const bool &maintain_Th, bool use_sfc)
 

Function Documentation

◆ rebalance_columns()

void rebalance_columns ( MultiFab &  rho,
MultiFab &  theta,
const MultiFab &  qv,
const MultiFab &  qt,
const MultiFab *  z_phys,
const Geometry &  geom,
const bool &  maintain_Th,
bool  use_sfc 
)

Rebalance density and potential temperature columns to satisfy hydrostatic equilibrium.

Parameters
[in,out]rhoDensity field
[in,out]thetaPotential temperature field
[in]qvWater vapor mixing ratio
[in]qtTotal water mixing ratio
[in]z_physPhysical height field
[in]geomGrid geometry
[in]maintain_ThWhether to maintain the existing potential temperature profile
[in]use_sfcWhether to use a surface boundary condition for initialization
29 {
30 
31 #ifdef AMREX_USE_FLOAT
32  Real tol = Real(1.0e-6);
33 #else
34  Real tol = Real(1.0e-10);
35 #endif
36  Real grav = CONST_GRAV;
37 
38  // int ncomp = cons.nComp();
39  int k_dom_lo = geom.Domain().smallEnd(2);
40 
41  const BoxArray& ba = rho.boxArray();
42 
43  //
44  // This is a bottom-up integration: the value in cell k depends only on cells at or below
45  // k. A box that stops below the top of the domain therefore produces exactly the values
46  // the full-height column would have had in the cells it does contain, so we do NOT require
47  // khi == geom.Domain().bigEnd(2). That matters for a refined level whose patch covers only
48  // the lower part of the domain, which is the normal way to nest an LES region inside a
49  // mesoscale parent.
50  //
51  // A box stacked on top of another box of this level (the BoxArray is split in z, e.g.
52  // amr.max_grid_size below the number of cells in z) must continue the integration of the
53  // box below it. Starting afresh from its own lowest cell would take that cell's density,
54  // which has not been rebalanced, as given, and the base state would then depend on where
55  // the boxes are split. The boxes are therefore integrated in bands of equal lowest index,
56  // bottom up. The state each column reaches in a cell (pressure, theta, qv, qt and the
57  // cell-centre height) is kept in "below", whose z ghost cells are filled from the bands
58  // already integrated before the next band starts. A column whose cell below the box is
59  // not covered by this level (the bottom of the domain, or of a refined patch) starts from
60  // its own lowest cell. With a single band nothing is kept and nothing is filled.
61  //
62  const Vector<int> band_klo = column_bands(ba);
63  const bool multi_band = (band_klo.size() > 1);
64 
65  enum { P_below = 0, Th_below, qv_below, qt_below, z_below, done_below, n_below };
66  MultiFab below;
67  if (multi_band) {
68  below.define(ba, rho.DistributionMap(), n_below, IntVect(0,0,1));
69  below.setVal(zero);
70  }
71 
72  for (const int klo_band : band_klo) {
73 
74  if (klo_band != band_klo[0]) { below.FillBoundary(); }
75 
76  for (MFIter mfi(rho,TileNoZ()); mfi.isValid(); ++mfi) {
77  Box bx = mfi.tilebox();
78  int klo = bx.smallEnd(2);
79  int khi = bx.bigEnd(2);
80 
81  // NOTE: TileNoZ() above guarantees that klo/khi are the *box's* z extent rather
82  // than a tile's, so each column is integrated exactly once.
83  if (klo != klo_band) { continue; }
84 
85  //
86  // The use_sfc seeding marches up from p_0 at z = 0, so it is only meaningful for a
87  // column that reaches the ground. A box whose klo is in the interior can use it
88  // only if every cell below it belongs to a box of this level to continue from.
89  //
90  if (use_sfc && klo > k_dom_lo) {
91  Box slab_below = mfi.validbox();
92  slab_below.setRange(2, klo-1);
93  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(ba.contains(slab_below),
94  "rebalance_columns with use_sfc requires every column "
95  "to reach the bottom of the domain: the integration is "
96  "seeded from p_0 at the surface.");
97  }
98  bx.makeSlab(2,klo);
99 
100  const Array4< Real>& rho_arr = rho.array(mfi);
101  const Array4< Real>& th_arr = theta.array(mfi);
102  const Array4<const Real>& qv_arr = qv.const_array(mfi);
103  const Array4<const Real>& qt_arr = qt.const_array(mfi);
104 
105  const Array4<const Real>& z_arr = z_phys->const_array(mfi);
106 
107  const Array4<Real> below_arr = (multi_band) ? below.array(mfi) : Array4<Real>{};
108  const bool can_continue = multi_band && (klo > k_dom_lo);
109 
110  ParallelFor(bx, [=,RdoCp_d=RdoCp] AMREX_GPU_DEVICE (int i, int j, int /*k*/) noexcept
111  {
112  // Integrate upward from the bottom of this box to its top
113  Real dz, F, C;
114  Real rho_tot_hi, rho_tot_lo;
115  Real z_lo, z_hi;
116  Real R_lo, R_hi;
117  Real qv_lo, qv_hi;
118  Real qt_lo, qt_hi;
119  Real Th_lo, Th_hi;
120  Real T_hi;
121  Real P_lo, P_hi;
122  int k_start;
123 
124  // Continue the column from the cell below this box, in a band already done
125  if (can_continue && below_arr(i,j,klo-1,done_below) > zero) {
126  P_lo = below_arr(i,j,klo-1,P_below);
127  Th_lo = below_arr(i,j,klo-1,Th_below);
128  qv_lo = below_arr(i,j,klo-1,qv_below);
129  qt_lo = below_arr(i,j,klo-1,qt_below);
130  z_lo = below_arr(i,j,klo-1,z_below);
131  P_hi = P_lo;
132  k_start = klo;
133 
134  } else {
135  // Integrate from z=0
136  if (use_sfc) {
137  z_lo = zero; // corresponding to p_0
138  z_hi = Real(0.125) * (z_arr(i,j,klo ) + z_arr(i+1,j,klo ) + z_arr(i,j+1,klo ) + z_arr(i+1,j+1,klo )
139  +z_arr(i,j,klo+1) + z_arr(i+1,j,klo+1) + z_arr(i,j+1,klo+1) + z_arr(i+1,j+1,klo+1));
140  dz = z_hi - z_lo;
141 
142  // Establish known constant
143  qt_lo = qt_arr(i,j,klo);
144  qv_lo = qv_arr(i,j,klo);
145  Th_lo = th_arr(i,j,klo);
146  P_lo = p_0;
147  R_lo = getRhogivenThetaPress(Th_lo, P_lo, RdoCp_d, qv_lo);
148  rho_tot_lo = R_lo * (one + qt_lo);
149  C = -P_lo + myhalf*rho_tot_lo*grav*dz;
150 
151  // Initial guess and residual
152  qt_hi = qt_arr(i,j,klo);
153  qv_hi = qv_arr(i,j,klo);
154  Th_hi = th_arr(i,j,klo);
155  P_hi = p_0;
156  T_hi = getTgivenPandTh(P_hi, Th_hi, RdoCp_d);
157  R_hi = getRhogivenThetaPress(Th_hi, P_hi, RdoCp_d, qv_hi);
158  rho_tot_hi = R_hi * (one + qt_hi);
159  F = P_hi + myhalf*rho_tot_hi*grav*dz + C;
160 
161  // Do iterations
162  HSEutils::Newton_Raphson_hse(tol, RdoCp_d, dz,
163  grav, C, Th_hi, T_hi,
164  qt_hi, qv_hi,
165  P_hi, R_hi, F, maintain_Th);
166 
167  // Assign data
168  rho_arr(i,j,klo) = R_hi;
169  if (!maintain_Th) { th_arr(i,j,klo) = getThgivenTandP(T_hi, P_hi, RdoCp_d); }
170  P_lo = P_hi;
171  z_lo = z_hi;
172 
173  // Use SFC state at first CC
174  } else {
175  z_lo = Real(0.125) * (z_arr(i,j,klo ) + z_arr(i+1,j,klo ) + z_arr(i,j+1,klo ) + z_arr(i+1,j+1,klo )
176  +z_arr(i,j,klo+1) + z_arr(i+1,j,klo+1) + z_arr(i,j+1,klo+1) + z_arr(i+1,j+1,klo+1));
177  P_lo = getPgivenRTh(rho_arr(i,j,klo)*th_arr(i,j,klo),qv_arr(i,j,klo));
178  P_hi = P_lo;
179  }
180 
181  Th_lo = th_arr(i,j,klo);
182  qv_lo = qv_arr(i,j,klo);
183  qt_lo = qt_arr(i,j,klo);
184  k_start = klo+1;
185  }
186 
187  for (int k(klo); k<=khi; ++k)
188  {
189  if (k >= k_start)
190  {
191  z_hi = Real(0.125) * (z_arr(i,j,k ) + z_arr(i+1,j,k ) + z_arr(i,j+1,k ) + z_arr(i+1,j+1,k )
192  +z_arr(i,j,k+1) + z_arr(i+1,j,k+1) + z_arr(i,j+1,k+1) + z_arr(i+1,j+1,k+1));
193  dz = z_hi - z_lo;
194 
195  // Establish known constant (the state in cell k-1)
196  R_lo = getRhogivenThetaPress(Th_lo, P_lo, RdoCp_d, qv_lo);
197  rho_tot_lo = R_lo * (one + qt_lo);
198  C = -P_lo + myhalf*rho_tot_lo*grav*dz;
199 
200  // Initial guess and residual
201  qt_hi = qt_arr(i,j,k);
202  qv_hi = qv_arr(i,j,k);
203  Th_hi = th_arr(i,j,k);
204  T_hi = getTgivenPandTh(P_hi, Th_hi, RdoCp_d);
205  R_hi = getRhogivenThetaPress(Th_hi, P_hi, RdoCp_d, qv_hi);
206  rho_tot_hi = R_hi * (one + qt_hi);
207  F = P_hi + myhalf*rho_tot_hi*grav*dz + C;
208 
209  // Do iterations
210  HSEutils::Newton_Raphson_hse(tol, RdoCp_d, dz,
211  grav, C, Th_hi, T_hi,
212  qt_hi, qv_hi,
213  P_hi, R_hi, F, maintain_Th);
214 
215  // Assign data
216  rho_arr(i,j,k) = R_hi;
217  if (!maintain_Th) { th_arr(i,j,k) = getThgivenTandP(T_hi, P_hi, RdoCp_d); }
218  P_lo = P_hi;
219  z_lo = z_hi;
220  Th_lo = th_arr(i,j,k);
221  qv_lo = qv_hi;
222  qt_lo = qt_hi;
223  }
224 
225  // Keep the state in cell k for a box stacked on this one
226  if (multi_band) {
227  below_arr(i,j,k,P_below) = P_lo;
228  below_arr(i,j,k,Th_below) = Th_lo;
229  below_arr(i,j,k,qv_below) = qv_lo;
230  below_arr(i,j,k,qt_below) = qt_lo;
231  below_arr(i,j,k,z_below) = z_lo;
232  below_arr(i,j,k,done_below) = one;
233  }
234  }
235  });
236  } // mfi
237  } // band
238 } // rebalance_columns
Vector< int > column_bands(const BoxArray &ba)
Definition: ERF_ColumnBands.cpp:13
constexpr amrex::Real p_0
Definition: ERF_Constants.H:53
constexpr amrex::Real CONST_GRAV
Definition: ERF_Constants.H:56
constexpr amrex::Real RdoCp
Definition: ERF_Constants.H:41
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real getRhogivenThetaPress(const amrex::Real th, const amrex::Real p, const amrex::Real rdOcp, const amrex::Real qv=amrex::Real(0))
Definition: ERF_EOS.H:96
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
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
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real getTgivenPandTh(const amrex::Real P, const amrex::Real th, const amrex::Real rdOcp)
Definition: ERF_EOS.H:32
const int klo
Definition: ERF_InitCustomPert_ABL.H:75
const int khi
Definition: ERF_InitCustomPert_Bubble.H:21
AMREX_ALWAYS_ASSERT_WITH_MESSAGE(m_cloud_chamber_config.active, "Cloud Chamber: initializer reached without a parsed configuration")
auto qv_arr
Definition: ERF_InitCustomPert_MultiSpeciesBubble.H:210
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:30
constexpr amrex::Real zero
Definition: ERF_NumericalConstants.H:29
constexpr amrex::Real myhalf
Definition: ERF_NumericalConstants.H:34
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 Newton_Raphson_hse(const Real &m_tol, const Real &RdoCp, const Real &dz, const Real &g, const Real &C, const Real &Th, const Real &T, const Real &qt, const Real &qv, Real &P, Real &rd, Real &F, const bool &maintain_Th)
Definition: ERF_HSEUtils.H:46
@ theta
Definition: ERF_SLM.H:19
@ rho
Definition: ERF_Kessler.H:25
@ qt
Definition: ERF_Kessler.H:30
@ qv
Definition: ERF_Kessler.H:31
@ dz
Definition: ERF_AdvanceWDM6.cpp:272

Referenced by ERF::init_from_input_sounding().

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