ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
ERF_MakeFastCoeffs.cpp File Reference
#include <AMReX.H>
#include "ERF_Constants.H"
#include <ERF_TI_fast_headers.H>
Include dependency graph for ERF_MakeFastCoeffs.cpp:

Functions

void make_fast_coeffs (int, MultiFab &fast_coeffs, Vector< MultiFab > &S_stage_data, const MultiFab &S_stage_prim, const MultiFab &pi_stage, const amrex::Geometry geom, bool l_use_moisture, MeshType mesh_type, Real gravity, Real c_p, std::unique_ptr< MultiFab > &detJ_cc, const double dtau, Real beta_s, amrex::GpuArray< ERF_BC, AMREX_SPACEDIM *2 > &phys_bc_type)
 

Function Documentation

◆ make_fast_coeffs()

void make_fast_coeffs ( int  ,
MultiFab &  fast_coeffs,
Vector< MultiFab > &  S_stage_data,
const MultiFab &  S_stage_prim,
const MultiFab &  pi_stage,
const amrex::Geometry  geom,
bool  l_use_moisture,
MeshType  mesh_type,
Real  gravity,
Real  c_p,
std::unique_ptr< MultiFab > &  detJ_cc,
const double  dtau,
Real  beta_s,
amrex::GpuArray< ERF_BC, AMREX_SPACEDIM *2 > &  phys_bc_type 
)

Function for computing the coefficients for the tridiagonal solver used in the fast integrator (the acoustic substepping).

Parameters
[in]levellevel of refinement
[out]fast_coeffsthe coefficients for the tridiagonal solver computed here
[in]S_stage_datasolution at the last stage
[in]S_stage_primprimitive variables (i.e. conserved variables divided by density) at the last stage
[in]pi_stageExner function at the last stage
[in]geomContainer for geometric information
[in]l_use_moistureAre we evolving moisture?
[in]mesh_typeDo we have constant dz?
[in]gravityMagnitude of gravity
[in]c_pCoefficient at constant pressure
[in]detJ_ccJacobian of the metric transformation at cell centers
[in]dtauFast time step
[in]beta_sCoefficient which determines how implicit vs explicit the solve is
[in]phys_bc_typePhysical boundary condition types
41 {
42  BL_PROFILE_VAR("make_fast_coeffs()",make_fast_coeffs);
43 
44  Real beta_2 = myhalf * (one + beta_s); // multiplies implicit terms
45 
46  Real c_v = c_p - R_d;
47 
48  const GpuArray<Real, AMREX_SPACEDIM> dxInv = geom.InvCellSizeArray();
49  Real dzi = dxInv[2];
50 
51  const Box &domain = geom.Domain();
52 
53  MultiFab coeff_A_mf(fast_coeffs, amrex::make_alias, 0, 1);
54  MultiFab coeff_B_mf(fast_coeffs, amrex::make_alias, 1, 1);
55  MultiFab coeff_C_mf(fast_coeffs, amrex::make_alias, 2, 1);
56  MultiFab coeff_P_mf(fast_coeffs, amrex::make_alias, 3, 1);
57  MultiFab coeff_Q_mf(fast_coeffs, amrex::make_alias, 4, 1);
58 
59 
60  // *************************************************************************
61  // Set gravity as a vector
62  const Array<Real,AMREX_SPACEDIM> grav{zero, zero, -gravity};
63  const GpuArray<Real,AMREX_SPACEDIM> grav_gpu{grav[0], grav[1], grav[2]};
64 
65  // *************************************************************************
66  // Define updates in the current RK stage
67  // *************************************************************************
68 #ifdef _OPENMP
69 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
70 #endif
71  {
72 
73  for ( MFIter mfi(S_stage_data[IntVars::cons],TileNoZ()); mfi.isValid(); ++mfi)
74  {
75  Box bx = mfi.tilebox();
76  Box tbz = surroundingNodes(bx,2);
77 
78  const Array4<const Real> & stage_cons = S_stage_data[IntVars::cons].const_array(mfi);
79  const Array4<const Real> & prim = S_stage_prim.const_array(mfi);
80 
81  const Array4<const Real>& detJ = (mesh_type != MeshType::ConstantDz) ?
82  detJ_cc->const_array(mfi) : Array4<const Real>{};
83 
84  const Array4<const Real>& pi_stage_ca = pi_stage.const_array(mfi);
85 
86  FArrayBox gam_fab; gam_fab.resize(surroundingNodes(bx,2),1,The_Async_Arena());
87 
88  auto const& coeffA_a = coeff_A_mf.array(mfi);
89  auto const& coeffB_a = coeff_B_mf.array(mfi);
90  auto const& coeffC_a = coeff_C_mf.array(mfi);
91  auto const& coeffP_a = coeff_P_mf.array(mfi);
92  auto const& coeffQ_a = coeff_Q_mf.array(mfi);
93  auto const& gam_a = gam_fab.array();
94 
95  // *********************************************************************
96  // *********************************************************************
97  // *********************************************************************
98 
99  Box bx_shrunk_in_k = bx;
100  int klo = tbz.smallEnd(2);
101  int khi = tbz.bigEnd(2);
102  bx_shrunk_in_k.setSmall(2,klo+1);
103  bx_shrunk_in_k.setBig(2,khi-1);
104 
105  // Note that the notes use "g" to mean the magnitude of gravity, so it is positive
106  // We set grav_gpu[2] to be the vector component which is negative
107  // We define halfg to match the notes (which is why we take the absolute value)
108  Real halfg = std::abs(myhalf * grav_gpu[2]);
109 
110  //Note we don't act on the bottom or top boundaries of the domain
111  if (mesh_type != MeshType::ConstantDz)
112  {
113  ParallelFor(bx_shrunk_in_k, [=] AMREX_GPU_DEVICE (int i, int j, int k)
114  {
115  Real pi_c = myhalf * (pi_stage_ca(i,j,k-1) + pi_stage_ca(i,j,k));
116 
117  Real detJ_on_kface = myhalf * (detJ(i,j,k) + detJ(i,j,k-1));
118  Real inv_detJ_on_kface = one / detJ_on_kface;
119 
120  Real qv_p = (l_use_moisture) ? prim(i,j,k ,PrimQ1_comp) : zero;
121  Real qv_q = (l_use_moisture) ? prim(i,j,k-1,PrimQ1_comp) : zero;
122 
123  Real Thm_hi = stage_cons(i,j,k ,RhoTheta_comp) * (one + RvoRd*qv_p);
124  Real Thm_lo = stage_cons(i,j,k-1,RhoTheta_comp) * (one + RvoRd*qv_q);
125  Real Thm_grad = dzi * inv_detJ_on_kface * ( Thm_hi - Thm_lo );
126 
127  Real coeff_P = -Gamma * R_d * dzi * inv_detJ_on_kface * pi_c * (one + RvoRd*qv_p)
128  - Gamma * R_d * R_d * Thm_grad * myhalf * pi_stage_ca(i,j,k ) /
129  ( c_v * stage_cons(i,j,k ,RhoTheta_comp) );
130 
131  Real coeff_Q = Gamma * R_d * dzi * inv_detJ_on_kface * pi_c * (one + RvoRd*qv_q)
132  - Gamma * R_d * R_d * Thm_grad * myhalf * pi_stage_ca(i,j,k-1) /
133  ( c_v * stage_cons(i,j,k-1,RhoTheta_comp) );
134 
135  if (l_use_moisture) {
136  Real q = myhalf * ( prim(i,j,k,PrimQ1_comp) + prim(i,j,k-1,PrimQ1_comp)
137  + prim(i,j,k,PrimQ2_comp) + prim(i,j,k-1,PrimQ2_comp) );
138  coeff_P /= (one + q);
139  coeff_Q /= (one + q);
140  }
141 
142  // NOTE: we store the moisture-normalized coefficients so that the explicit
143  // RHS assembled in erf_substep_* uses exactly the same vertical fast
144  // pressure gradient as the implicit tridiagonal system built below
145  coeffP_a(i,j,k) = coeff_P;
146  coeffQ_a(i,j,k) = coeff_Q;
147 
148  Real theta_t_lo = myhalf * ( prim(i,j,k-2,PrimTheta_comp) + prim(i,j,k-1,PrimTheta_comp) );
149  Real theta_t_mid = myhalf * ( prim(i,j,k-1,PrimTheta_comp) + prim(i,j,k ,PrimTheta_comp) );
150  Real theta_t_hi = myhalf * ( prim(i,j,k ,PrimTheta_comp) + prim(i,j,k+1,PrimTheta_comp) );
151 
152  // LHS for tri-diagonal system
153  Real D = beta_2 * beta_2 * dzi * static_cast<Real>(dtau * dtau);
154  coeffA_a(i,j,k) = D * (one/detJ(i,j,k-1)) * ( halfg - coeff_Q * theta_t_lo );
155  coeffC_a(i,j,k) = D * (one/detJ(i,j,k )) * (-halfg + coeff_P * theta_t_hi );
156 
157  coeffB_a(i,j,k) = one + D * ( (coeff_Q/detJ(i,j,k-1) - coeff_P/detJ(i,j,k)) * theta_t_mid
158  + halfg * (one/detJ(i,j,k) - one/detJ(i,j,k-1)) );
159  });
160 
161  } else {
162 
163  ParallelFor(bx_shrunk_in_k, [=] AMREX_GPU_DEVICE (int i, int j, int k)
164  {
165  Real pi_c = myhalf * (pi_stage_ca(i,j,k-1) + pi_stage_ca(i,j,k));
166 
167  Real qv_p = (l_use_moisture) ? prim(i,j,k ,PrimQ1_comp) : zero;
168  Real qv_q = (l_use_moisture) ? prim(i,j,k-1,PrimQ1_comp) : zero;
169 
170  Real Thm_hi = stage_cons(i,j,k ,RhoTheta_comp) * (one + RvoRd*qv_p);
171  Real Thm_lo = stage_cons(i,j,k-1,RhoTheta_comp) * (one + RvoRd*qv_q);
172  Real Thm_grad = dzi * ( Thm_hi - Thm_lo );
173 
174  Real coeff_P = -Gamma * R_d * dzi * pi_c * (one + RvoRd*qv_p)
175  - Gamma * R_d * R_d * Thm_grad * myhalf * pi_stage_ca(i,j,k ) /
176  ( c_v * stage_cons(i,j,k ,RhoTheta_comp) );
177 
178  Real coeff_Q = Gamma * R_d * dzi * pi_c * (one + RvoRd*qv_q)
179  - Gamma * R_d * R_d * Thm_grad * myhalf * pi_stage_ca(i,j,k-1) /
180  ( c_v * stage_cons(i,j,k-1,RhoTheta_comp) );
181 
182  if (l_use_moisture) {
183  Real q = myhalf * ( prim(i,j,k,PrimQ1_comp) + prim(i,j,k-1,PrimQ1_comp)
184  + prim(i,j,k,PrimQ2_comp) + prim(i,j,k-1,PrimQ2_comp) );
185  coeff_P /= (one + q);
186  coeff_Q /= (one + q);
187  }
188 
189  // NOTE: we store the moisture-normalized coefficients so that the explicit
190  // RHS assembled in erf_substep_* uses exactly the same vertical fast
191  // pressure gradient as the implicit tridiagonal system built below
192  coeffP_a(i,j,k) = coeff_P;
193  coeffQ_a(i,j,k) = coeff_Q;
194 
195  Real theta_t_lo = myhalf * ( prim(i,j,k-2,PrimTheta_comp) + prim(i,j,k-1,PrimTheta_comp) );
196  Real theta_t_mid = myhalf * ( prim(i,j,k-1,PrimTheta_comp) + prim(i,j,k ,PrimTheta_comp) );
197  Real theta_t_hi = myhalf * ( prim(i,j,k ,PrimTheta_comp) + prim(i,j,k+1,PrimTheta_comp) );
198 
199  // LHS for tri-diagonal system
200  Real D = beta_2 * beta_2 * dzi * static_cast<Real>(dtau * dtau);
201  coeffA_a(i,j,k) = D * ( halfg - coeff_Q * theta_t_lo );
202  coeffC_a(i,j,k) = D * (-halfg + coeff_P * theta_t_hi );
203 
204  coeffB_a(i,j,k) = one + D * (coeff_Q - coeff_P) * theta_t_mid;
205  });
206  }
207 
208  amrex::Box b2d = tbz; // Copy constructor
209  b2d.setRange(2,0);
210 
211  auto const lo = amrex::lbound(bx);
212  auto const hi = amrex::ubound(bx);
213 
214  auto const domhi = amrex::ubound(domain);
215 
216  {
217  BL_PROFILE("make_coeffs_b2d_loop");
218 #ifdef AMREX_USE_GPU
219  ParallelFor(b2d, [=] AMREX_GPU_DEVICE (int i, int j, int) {
220 
221  // If at the bottom of the grid, we will set w to a specified Dirichlet value
222  coeffA_a(i,j,lo.z) = zero;
223  coeffB_a(i,j,lo.z) = one;
224  coeffC_a(i,j,lo.z) = zero;
225 
226  // If at the top of the grid, we will set w to a specified Dirichlet value
227  coeffA_a(i,j,hi.z+1) = zero;
228  coeffB_a(i,j,hi.z+1) = one;
229  coeffC_a(i,j,hi.z+1) = zero;
230 
231  // UNLESS if at the top of the domain and the boundary is outflow,
232  // we will use a homogeneous Neumann condition
233  if ( (hi.z == domhi.z) &&
234  (phys_bc_type[5] == ERF_BC::outflow or phys_bc_type[5] == ERF_BC::ho_outflow) )
235  {
236  coeffA_a(i,j,hi.z+1) = -one;
237  }
238 
239  // w = specified Dirichlet value at k = lo.z
240  gam_a(i,j,lo.z) = coeffC_a(i,j,lo.z) / coeffB_a(i,j,lo.z);
241  for (int k = lo.z+1; k <= hi.z+1; k++) {
242  coeffB_a(i,j,k) = one / ( coeffB_a(i,j,k) - coeffA_a(i,j,k)*gam_a(i,j,k-1) );
243  gam_a(i,j,k) = coeffC_a(i,j,k) * coeffB_a(i,j,k);
244  }
245  });
246 #else
247  // If at the bottom of the grid, we will set w to a specified Dirichlet value
248  for (int j = lo.y; j <= hi.y; ++j) {
249  AMREX_PRAGMA_SIMD
250  for (int i = lo.x; i <= hi.x; ++i) {
251  coeffA_a(i,j,lo.z) = zero;
252  coeffB_a(i,j,lo.z) = one;
253  coeffC_a(i,j,lo.z) = zero;
254  gam_a(i,j,lo.z) = coeffC_a(i,j,lo.z) / coeffB_a(i,j,lo.z);
255  }
256  }
257  for (int j = lo.y; j <= hi.y; ++j) {
258  AMREX_PRAGMA_SIMD
259  for (int i = lo.x; i <= hi.x; ++i) {
260 
261  // If at the top of the grid, we will set w to a specified Dirichlet value
262  coeffA_a(i,j,hi.z+1) = zero;
263  coeffB_a(i,j,hi.z+1) = one;
264  coeffC_a(i,j,hi.z+1) = zero;
265 
266  // UNLESS if at the top of the domain and the boundary is outflow,
267  // we will use a homogeneous Neumann condition
268  if ( (hi.z == domhi.z) &&
269  (phys_bc_type[5] == ERF_BC::outflow or phys_bc_type[5] == ERF_BC::ho_outflow) )
270  {
271  coeffA_a(i,j,hi.z+1) = -one;
272  }
273  }
274  }
275  for (int k = lo.z+1; k <= hi.z+1; ++k) {
276  for (int j = lo.y; j <= hi.y; ++j) {
277  AMREX_PRAGMA_SIMD
278  for (int i = lo.x; i <= hi.x; ++i) {
279  coeffB_a(i,j,k) = one / ( coeffB_a(i,j,k) - coeffA_a(i,j,k)*gam_a(i,j,k-1) );
280  gam_a(i,j,k) = coeffC_a(i,j,k) * coeffB_a(i,j,k);
281  }
282  }
283  }
284 #endif
285  } // end profile
286  } // mfi
287  } // omp
288 }
constexpr amrex::Real RvoRd
Definition: ERF_Constants.H:43
constexpr amrex::Real R_d
Definition: ERF_Constants.H:34
constexpr amrex::Real Gamma
Definition: ERF_Constants.H:54
#define PrimQ1_comp
Definition: ERF_IndexDefines.H:61
#define PrimQ2_comp
Definition: ERF_IndexDefines.H:62
#define RhoTheta_comp
Definition: ERF_IndexDefines.H:40
#define PrimTheta_comp
Definition: ERF_IndexDefines.H:58
@ ho_outflow
amrex::GpuArray< Real, AMREX_SPACEDIM > dxInv
Definition: ERF_InitCustomPertVels_ParticleTests.H:17
const int klo
Definition: ERF_InitCustomPert_ABL.H:75
const int khi
Definition: ERF_InitCustomPert_Bubble.H:21
void make_fast_coeffs(int, MultiFab &fast_coeffs, Vector< MultiFab > &S_stage_data, const MultiFab &S_stage_prim, const MultiFab &pi_stage, const amrex::Geometry geom, bool l_use_moisture, MeshType mesh_type, Real gravity, Real c_p, std::unique_ptr< MultiFab > &detJ_cc, const double dtau, Real beta_s, amrex::GpuArray< ERF_BC, AMREX_SPACEDIM *2 > &phys_bc_type)
Definition: ERF_MakeFastCoeffs.cpp:28
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
@ cons
Definition: ERF_IndexDefines.H:232
@ q
Definition: ERF_WSM6.H:273
Here is the call graph for this function: