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 <ERF_TerrainMetrics.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, const MultiFab *z_phys_nd, bool use_variable_beta_s, Real beta_s_min, Real beta_s_max, 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,
const MultiFab *  z_phys_nd,
bool  use_variable_beta_s,
Real  beta_s_min,
Real  beta_s_max,
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, and beta_s on the w-faces (component 5)
[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]z_phys_ndHeight coordinate at nodes (null without terrain)
[in]use_variable_beta_sIf true, beta_s = max(beta_s_min, min(beta_s_max, h_xi^2 + h_eta^2)) on each w-face in place of beta_s
[in]beta_s_minLower bound of the spatially varying beta_s
[in]beta_s_maxUpper bound of the spatially varying beta_s
[in]phys_bc_typePhysical boundary condition types
51 {
52  BL_PROFILE_VAR("make_fast_coeffs()",make_fast_coeffs);
53 
54  Real c_v = c_p - R_d;
55 
56  const GpuArray<Real, AMREX_SPACEDIM> dxInv = geom.InvCellSizeArray();
57  Real dzi = dxInv[2];
58 
59  const Box &domain = geom.Domain();
60 
61  MultiFab coeff_A_mf(fast_coeffs, amrex::make_alias, 0, 1);
62  MultiFab coeff_B_mf(fast_coeffs, amrex::make_alias, 1, 1);
63  MultiFab coeff_C_mf(fast_coeffs, amrex::make_alias, 2, 1);
64  MultiFab coeff_P_mf(fast_coeffs, amrex::make_alias, 3, 1);
65  MultiFab coeff_Q_mf(fast_coeffs, amrex::make_alias, 4, 1);
66  MultiFab beta_s_mf(fast_coeffs, amrex::make_alias, 5, 1);
67 
68  const bool has_z_nd = (z_phys_nd != nullptr);
69 
70 
71  // *************************************************************************
72  // Set gravity as a vector
73  const Array<Real,AMREX_SPACEDIM> grav{zero, zero, -gravity};
74  const GpuArray<Real,AMREX_SPACEDIM> grav_gpu{grav[0], grav[1], grav[2]};
75 
76  // *************************************************************************
77  // Define updates in the current RK stage
78  // *************************************************************************
79 #ifdef _OPENMP
80 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
81 #endif
82  {
83 
84  for ( MFIter mfi(S_stage_data[IntVars::cons],TileNoZ()); mfi.isValid(); ++mfi)
85  {
86  Box bx = mfi.tilebox();
87  Box tbz = surroundingNodes(bx,2);
88 
89  const Array4<const Real> & stage_cons = S_stage_data[IntVars::cons].const_array(mfi);
90  const Array4<const Real> & prim = S_stage_prim.const_array(mfi);
91 
92  const Array4<const Real>& detJ = (mesh_type != MeshType::ConstantDz) ?
93  detJ_cc->const_array(mfi) : Array4<const Real>{};
94 
95  const Array4<const Real>& pi_stage_ca = pi_stage.const_array(mfi);
96 
97  FArrayBox gam_fab; gam_fab.resize(surroundingNodes(bx,2),1,The_Async_Arena());
98 
99  auto const& coeffA_a = coeff_A_mf.array(mfi);
100  auto const& coeffB_a = coeff_B_mf.array(mfi);
101  auto const& coeffC_a = coeff_C_mf.array(mfi);
102  auto const& coeffP_a = coeff_P_mf.array(mfi);
103  auto const& coeffQ_a = coeff_Q_mf.array(mfi);
104  auto const& gam_a = gam_fab.array();
105  auto const& beta_s_a = beta_s_mf.array(mfi);
106 
107  const Array4<const Real>& z_nd = (has_z_nd) ? z_phys_nd->const_array(mfi) : Array4<const Real>{};
108 
109  // *********************************************************************
110  // beta_s on the w-faces, which erf_substep_* uses for the time-weighting of the
111  // vertical fluxes and of the vertical momentum equation
112  // *********************************************************************
113  ParallelFor(tbz, [=] AMREX_GPU_DEVICE (int i, int j, int k)
114  {
115  if (use_variable_beta_s) {
116  Real h_xi = (has_z_nd) ? Compute_h_xi_AtKface (i, j, k, dxInv, z_nd) : zero;
117  Real h_eta = (has_z_nd) ? Compute_h_eta_AtKface(i, j, k, dxInv, z_nd) : zero;
118  beta_s_a(i,j,k) = amrex::max(beta_s_min, amrex::min(beta_s_max, h_xi*h_xi + h_eta*h_eta));
119  } else {
120  beta_s_a(i,j,k) = beta_s;
121  }
122  });
123 
124  // *********************************************************************
125  // *********************************************************************
126  // *********************************************************************
127 
128  Box bx_shrunk_in_k = bx;
129  int klo = tbz.smallEnd(2);
130  int khi = tbz.bigEnd(2);
131  bx_shrunk_in_k.setSmall(2,klo+1);
132  bx_shrunk_in_k.setBig(2,khi-1);
133 
134  // Note that the notes use "g" to mean the magnitude of gravity, so it is positive
135  // We set grav_gpu[2] to be the vector component which is negative
136  // We define halfg to match the notes (which is why we take the absolute value)
137  Real halfg = std::abs(myhalf * grav_gpu[2]);
138 
139  //Note we don't act on the bottom or top boundaries of the domain
140  if (mesh_type != MeshType::ConstantDz)
141  {
142  ParallelFor(bx_shrunk_in_k, [=] AMREX_GPU_DEVICE (int i, int j, int k)
143  {
144  Real pi_c = myhalf * (pi_stage_ca(i,j,k-1) + pi_stage_ca(i,j,k));
145 
146  Real detJ_on_kface = myhalf * (detJ(i,j,k) + detJ(i,j,k-1));
147  Real inv_detJ_on_kface = one / detJ_on_kface;
148 
149  Real qv_p = (l_use_moisture) ? prim(i,j,k ,PrimQ1_comp) : zero;
150  Real qv_q = (l_use_moisture) ? prim(i,j,k-1,PrimQ1_comp) : zero;
151 
152  Real Thm_hi = stage_cons(i,j,k ,RhoTheta_comp) * (one + RvoRd*qv_p);
153  Real Thm_lo = stage_cons(i,j,k-1,RhoTheta_comp) * (one + RvoRd*qv_q);
154  Real Thm_grad = dzi * inv_detJ_on_kface * ( Thm_hi - Thm_lo );
155 
156  Real coeff_P = -Gamma * R_d * dzi * inv_detJ_on_kface * pi_c * (one + RvoRd*qv_p)
157  - Gamma * R_d * R_d * Thm_grad * myhalf * pi_stage_ca(i,j,k ) /
158  ( c_v * stage_cons(i,j,k ,RhoTheta_comp) );
159 
160  Real coeff_Q = Gamma * R_d * dzi * inv_detJ_on_kface * pi_c * (one + RvoRd*qv_q)
161  - Gamma * R_d * R_d * Thm_grad * myhalf * pi_stage_ca(i,j,k-1) /
162  ( c_v * stage_cons(i,j,k-1,RhoTheta_comp) );
163 
164  if (l_use_moisture) {
165  Real q = myhalf * ( prim(i,j,k,PrimQ1_comp) + prim(i,j,k-1,PrimQ1_comp)
166  + prim(i,j,k,PrimQ2_comp) + prim(i,j,k-1,PrimQ2_comp) );
167  coeff_P /= (one + q);
168  coeff_Q /= (one + q);
169  }
170 
171  // NOTE: we store the moisture-normalized coefficients so that the explicit
172  // RHS assembled in erf_substep_* uses exactly the same vertical fast
173  // pressure gradient as the implicit tridiagonal system built below
174  coeffP_a(i,j,k) = coeff_P;
175  coeffQ_a(i,j,k) = coeff_Q;
176 
177  Real theta_t_lo = myhalf * ( prim(i,j,k-2,PrimTheta_comp) + prim(i,j,k-1,PrimTheta_comp) );
178  Real theta_t_mid = myhalf * ( prim(i,j,k-1,PrimTheta_comp) + prim(i,j,k ,PrimTheta_comp) );
179  Real theta_t_hi = myhalf * ( prim(i,j,k ,PrimTheta_comp) + prim(i,j,k+1,PrimTheta_comp) );
180 
181  // LHS for tri-diagonal system
182  // NOTE: the w-equation at face k is weighted by beta_2(k), and the vertical
183  // flux at face k' by beta_2(k'), so each entry carries both
184  // (multiplied first, so a uniform beta_s rounds as beta_2 * beta_2 did)
185  Real beta_2_km1 = myhalf * (one + beta_s_a(i,j,k-1));
186  Real beta_2_k = myhalf * (one + beta_s_a(i,j,k ));
187  Real beta_2_kp1 = myhalf * (one + beta_s_a(i,j,k+1));
188  Real D_A = beta_2_km1 * beta_2_k * dzi * static_cast<Real>(dtau * dtau);
189  Real D_B = beta_2_k * beta_2_k * dzi * static_cast<Real>(dtau * dtau);
190  Real D_C = beta_2_k * beta_2_kp1 * dzi * static_cast<Real>(dtau * dtau);
191  coeffA_a(i,j,k) = D_A * (one/detJ(i,j,k-1)) * ( halfg - coeff_Q * theta_t_lo );
192  coeffC_a(i,j,k) = D_C * (one/detJ(i,j,k )) * (-halfg + coeff_P * theta_t_hi );
193 
194  coeffB_a(i,j,k) = one + D_B * ( (coeff_Q/detJ(i,j,k-1) - coeff_P/detJ(i,j,k)) * theta_t_mid
195  + halfg * (one/detJ(i,j,k) - one/detJ(i,j,k-1)) );
196  });
197 
198  } else {
199 
200  ParallelFor(bx_shrunk_in_k, [=] AMREX_GPU_DEVICE (int i, int j, int k)
201  {
202  Real pi_c = myhalf * (pi_stage_ca(i,j,k-1) + pi_stage_ca(i,j,k));
203 
204  Real qv_p = (l_use_moisture) ? prim(i,j,k ,PrimQ1_comp) : zero;
205  Real qv_q = (l_use_moisture) ? prim(i,j,k-1,PrimQ1_comp) : zero;
206 
207  Real Thm_hi = stage_cons(i,j,k ,RhoTheta_comp) * (one + RvoRd*qv_p);
208  Real Thm_lo = stage_cons(i,j,k-1,RhoTheta_comp) * (one + RvoRd*qv_q);
209  Real Thm_grad = dzi * ( Thm_hi - Thm_lo );
210 
211  Real coeff_P = -Gamma * R_d * dzi * pi_c * (one + RvoRd*qv_p)
212  - Gamma * R_d * R_d * Thm_grad * myhalf * pi_stage_ca(i,j,k ) /
213  ( c_v * stage_cons(i,j,k ,RhoTheta_comp) );
214 
215  Real coeff_Q = Gamma * R_d * dzi * pi_c * (one + RvoRd*qv_q)
216  - Gamma * R_d * R_d * Thm_grad * myhalf * pi_stage_ca(i,j,k-1) /
217  ( c_v * stage_cons(i,j,k-1,RhoTheta_comp) );
218 
219  if (l_use_moisture) {
220  Real q = myhalf * ( prim(i,j,k,PrimQ1_comp) + prim(i,j,k-1,PrimQ1_comp)
221  + prim(i,j,k,PrimQ2_comp) + prim(i,j,k-1,PrimQ2_comp) );
222  coeff_P /= (one + q);
223  coeff_Q /= (one + q);
224  }
225 
226  // NOTE: we store the moisture-normalized coefficients so that the explicit
227  // RHS assembled in erf_substep_* uses exactly the same vertical fast
228  // pressure gradient as the implicit tridiagonal system built below
229  coeffP_a(i,j,k) = coeff_P;
230  coeffQ_a(i,j,k) = coeff_Q;
231 
232  Real theta_t_lo = myhalf * ( prim(i,j,k-2,PrimTheta_comp) + prim(i,j,k-1,PrimTheta_comp) );
233  Real theta_t_mid = myhalf * ( prim(i,j,k-1,PrimTheta_comp) + prim(i,j,k ,PrimTheta_comp) );
234  Real theta_t_hi = myhalf * ( prim(i,j,k ,PrimTheta_comp) + prim(i,j,k+1,PrimTheta_comp) );
235 
236  // LHS for tri-diagonal system
237  // NOTE: the w-equation at face k is weighted by beta_2(k), and the vertical
238  // flux at face k' by beta_2(k'), so each entry carries both
239  // (multiplied first, so a uniform beta_s rounds as beta_2 * beta_2 did)
240  Real beta_2_km1 = myhalf * (one + beta_s_a(i,j,k-1));
241  Real beta_2_k = myhalf * (one + beta_s_a(i,j,k ));
242  Real beta_2_kp1 = myhalf * (one + beta_s_a(i,j,k+1));
243  Real D_A = beta_2_km1 * beta_2_k * dzi * static_cast<Real>(dtau * dtau);
244  Real D_B = beta_2_k * beta_2_k * dzi * static_cast<Real>(dtau * dtau);
245  Real D_C = beta_2_k * beta_2_kp1 * dzi * static_cast<Real>(dtau * dtau);
246  coeffA_a(i,j,k) = D_A * ( halfg - coeff_Q * theta_t_lo );
247  coeffC_a(i,j,k) = D_C * (-halfg + coeff_P * theta_t_hi );
248 
249  coeffB_a(i,j,k) = one + D_B * (coeff_Q - coeff_P) * theta_t_mid;
250  });
251  }
252 
253  amrex::Box b2d = tbz; // Copy constructor
254  b2d.setRange(2,0);
255 
256  auto const lo = amrex::lbound(bx);
257  auto const hi = amrex::ubound(bx);
258 
259  auto const domhi = amrex::ubound(domain);
260 
261  {
262  BL_PROFILE("make_coeffs_b2d_loop");
263 #ifdef AMREX_USE_GPU
264  ParallelFor(b2d, [=] AMREX_GPU_DEVICE (int i, int j, int) {
265 
266  // If at the bottom of the grid, we will set w to a specified Dirichlet value
267  coeffA_a(i,j,lo.z) = zero;
268  coeffB_a(i,j,lo.z) = one;
269  coeffC_a(i,j,lo.z) = zero;
270 
271  // If at the top of the grid, we will set w to a specified Dirichlet value
272  coeffA_a(i,j,hi.z+1) = zero;
273  coeffB_a(i,j,hi.z+1) = one;
274  coeffC_a(i,j,hi.z+1) = zero;
275 
276  // UNLESS if at the top of the domain and the boundary is outflow,
277  // we will use a homogeneous Neumann condition
278  if ( (hi.z == domhi.z) &&
279  (phys_bc_type[5] == ERF_BC::outflow or phys_bc_type[5] == ERF_BC::ho_outflow) )
280  {
281  coeffA_a(i,j,hi.z+1) = -one;
282  }
283 
284  // w = specified Dirichlet value at k = lo.z
285  gam_a(i,j,lo.z) = coeffC_a(i,j,lo.z) / coeffB_a(i,j,lo.z);
286  for (int k = lo.z+1; k <= hi.z+1; k++) {
287  coeffB_a(i,j,k) = one / ( coeffB_a(i,j,k) - coeffA_a(i,j,k)*gam_a(i,j,k-1) );
288  gam_a(i,j,k) = coeffC_a(i,j,k) * coeffB_a(i,j,k);
289  }
290  });
291 #else
292  // If at the bottom of the grid, we will set w to a specified Dirichlet value
293  for (int j = lo.y; j <= hi.y; ++j) {
294  AMREX_PRAGMA_SIMD
295  for (int i = lo.x; i <= hi.x; ++i) {
296  coeffA_a(i,j,lo.z) = zero;
297  coeffB_a(i,j,lo.z) = one;
298  coeffC_a(i,j,lo.z) = zero;
299  gam_a(i,j,lo.z) = coeffC_a(i,j,lo.z) / coeffB_a(i,j,lo.z);
300  }
301  }
302  for (int j = lo.y; j <= hi.y; ++j) {
303  AMREX_PRAGMA_SIMD
304  for (int i = lo.x; i <= hi.x; ++i) {
305 
306  // If at the top of the grid, we will set w to a specified Dirichlet value
307  coeffA_a(i,j,hi.z+1) = zero;
308  coeffB_a(i,j,hi.z+1) = one;
309  coeffC_a(i,j,hi.z+1) = zero;
310 
311  // UNLESS if at the top of the domain and the boundary is outflow,
312  // we will use a homogeneous Neumann condition
313  if ( (hi.z == domhi.z) &&
314  (phys_bc_type[5] == ERF_BC::outflow or phys_bc_type[5] == ERF_BC::ho_outflow) )
315  {
316  coeffA_a(i,j,hi.z+1) = -one;
317  }
318  }
319  }
320  for (int k = lo.z+1; k <= hi.z+1; ++k) {
321  for (int j = lo.y; j <= hi.y; ++j) {
322  AMREX_PRAGMA_SIMD
323  for (int i = lo.x; i <= hi.x; ++i) {
324  coeffB_a(i,j,k) = one / ( coeffB_a(i,j,k) - coeffA_a(i,j,k)*gam_a(i,j,k-1) );
325  gam_a(i,j,k) = coeffC_a(i,j,k) * coeffB_a(i,j,k);
326  }
327  }
328  }
329 #endif
330  } // end profile
331  } // mfi
332  } // omp
333 }
constexpr amrex::Real RvoRd
Definition: ERF_Constants.H:59
constexpr amrex::Real R_d
Definition: ERF_Constants.H:49
constexpr amrex::Real Gamma
Definition: ERF_Constants.H:70
#define PrimQ1_comp
Definition: ERF_IndexDefines.H:82
#define PrimQ2_comp
Definition: ERF_IndexDefines.H:83
#define RhoTheta_comp
Definition: ERF_IndexDefines.H:54
#define PrimTheta_comp
Definition: ERF_IndexDefines.H:79
@ ho_outflow
amrex::GpuArray< Real, AMREX_SPACEDIM > dxInv
Definition: ERF_InitCustomPertVels_ParticleTests.H:17
const int khi
Definition: ERF_InitCustomPert_Bubble.H:25
const int klo
Definition: ERF_InitCustomPert_KE.H:9
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, const MultiFab *z_phys_nd, bool use_variable_beta_s, Real beta_s_min, Real beta_s_max, amrex::GpuArray< ERF_BC, AMREX_SPACEDIM *2 > &phys_bc_type)
Definition: ERF_MakeFastCoeffs.cpp:35
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
constexpr amrex::Real myhalf
Definition: ERF_NumericalConstants.H:41
amrex::Real Real
Definition: ERF_ShocInterface.H:19
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real Compute_h_xi_AtKface(const int &i, const int &j, const int &k, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &cellSizeInv, const amrex::Array4< const amrex::Real > &z_nd)
Definition: ERF_TerrainMetrics.H:444
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real Compute_h_eta_AtKface(const int &i, const int &j, const int &k, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &cellSizeInv, const amrex::Array4< const amrex::Real > &z_nd)
Definition: ERF_TerrainMetrics.H:467
AMREX_FORCE_INLINE amrex::IntVect TileNoZ()
Definition: ERF_TileNoZ.H:11
@ cons
Definition: ERF_IndexDefines.H:272
@ q
Definition: ERF_WSM6.H:294
Here is the call graph for this function: