ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
ERF_ImplicitDiff_T.cpp File Reference
Include dependency graph for ERF_ImplicitDiff_T.cpp:

Macros

#define INSTANTIATE_IMPLICIT_DIFF_FOR_MOM_LU(STAGDIR)
 

Functions

void ImplicitDiffForStateLU_T (const Box &bx, const Box &domain, const int level, const int n, const double dt_d, const GpuArray< Real, AMREX_SPACEDIM *2 > &bc_neumann_vals, const Array4< Real > &cell_data, const Array4< const Real > &z_nd, const Array4< const Real > &detJ, const GpuArray< Real, AMREX_SPACEDIM > &cellSizeInv, const Array4< const Real > &scalar_zflux, const Array4< const Real > &mu_turb, const SolverChoice &solverChoice, const BCRec *bc_ptr, const bool use_SurfLayer, const Real implicit_fac, const bool use_mrf_countergradient)
 
template<int stagdir>
void ImplicitDiffForMomLU_T (const Box &bx, const Box &, const int level, const double dt_d, const Array4< const Real > &cell_data, const Array4< Real > &face_data, const Array4< const Real > &tau, const Array4< const Real > &tau_corr, const Array4< const Real > &z_nd, const Array4< const Real > &detJ, const GpuArray< Real, AMREX_SPACEDIM > &cellSizeInv, const Array4< const Real > &mu_turb, const SolverChoice &solverChoice, const BCRec *bc_ptr, const bool use_SurfLayer, const Real implicit_fac, const bool use_ysu_mom_countergradient)
 

Macro Definition Documentation

◆ INSTANTIATE_IMPLICIT_DIFF_FOR_MOM_LU

#define INSTANTIATE_IMPLICIT_DIFF_FOR_MOM_LU (   STAGDIR)
Value:
template void ImplicitDiffForMomLU_T<STAGDIR> ( \
const Box&, \
const Box&, \
const int, \
const double, \
const Array4<const Real>&, \
const Array4< Real>&, \
const Array4<const Real>&, \
const Array4<const Real>&, \
const Array4<const Real>&, \
const Array4<const Real>&, \
const GpuArray<Real, AMREX_SPACEDIM>&, \
const Array4<const Real>&, \
const SolverChoice&, \
const BCRec*, \
const bool, \
const Real, \
const bool);
amrex::Real Real
Definition: ERF_ShocInterface.H:19
Definition: ERF_DataStruct.H:634

Function Documentation

◆ ImplicitDiffForMomLU_T()

template<int stagdir>
void ImplicitDiffForMomLU_T ( const Box &  bx,
const Box &  ,
const int  level,
const double  dt_d,
const Array4< const Real > &  cell_data,
const Array4< Real > &  face_data,
const Array4< const Real > &  tau,
const Array4< const Real > &  tau_corr,
const Array4< const Real > &  z_nd,
const Array4< const Real > &  detJ,
const GpuArray< Real, AMREX_SPACEDIM > &  cellSizeInv,
const Array4< const Real > &  mu_turb,
const SolverChoice solverChoice,
const BCRec *  bc_ptr,
const bool  use_SurfLayer,
const Real  implicit_fac,
const bool  use_ysu_mom_countergradient 
)

Function for computing the implicit contribution to the vertical diffusion of momentum, over terrain.

This function (explicitly instantiated below) handles staggering in x, y, or z through the template parameter, stagdir. NOTE: implicit diffusion of w has remains an experimental feature and has not been tested yet with terrain.

Parameters
[in]bxcell-centered box to loop over
[in]levelAMR level
[in]dt_dtime step
[in]cell_dataconserved cell-centered rho
[in,out]face_dataconserved momentum
[in]taustress contribution to momentum
[in]tau_corrstress contribution to momentum that will be corrected by the implicit solve
[in]z_ndnodal array of z
[in]detJJacobian determinant
[in]cellSizeInvinverse cell size array
[in]mu_turbturbulent viscosity
[in]solverChoicecontainer of parameters
[in]bc_ptrcontainer with boundary conditions
[in]use_SurfLayerwhether we have turned on subgrid diffusion
[in]implicit_facif 1 then fully implicit; if 0 then fully explicit
[in]use_ysu_mom_countergradientwhether to include YSU momentum countergradient correction
275 {
276  BL_PROFILE_VAR("ImplicitDiffForMom_T()",ImplicitDiffForMom_T);
277 
278  Real dt = static_cast<Real>(dt_d);
279 
280  // setup quantities for getRhoAlphaAtFaces()
281  DiffChoice dc = solverChoice.diffChoice;
282  TurbChoice tc = solverChoice.turbChoice[level];
283  bool l_consA = (dc.molec_diff_type == MolecDiffType::ConstantAlpha);
284  bool l_turb = tc.use_kturb;
285  // The off-diagonal correction strains for u/v contain a factor of 1/2,
286  // while the diagonal correction strain for w does not.
287  constexpr Real molec_fac = (stagdir == 2) ? two : one;
288  Real mu_eff = (l_consA) ? molec_fac * dc.dynamic_viscosity / dc.rho0_trans
289  : molec_fac * dc.dynamic_viscosity;
290 
291  // g(S*) coefficient
292  // stagdir==0: tau_corr = myhalf * du/dz * mu_tot
293  // stagdir==1: tau_corr = myhalf * dv/dz * mu_tot
294  // stagdir==2: tau_corr = dw/dz * mu_tot
295  constexpr Real gfac = (stagdir == 2) ? two/three : one;
296 
297  // offsets used to average to faces
298  constexpr int ioff = (stagdir == 0) ? 1 : 0;
299  constexpr int joff = (stagdir == 1) ? 1 : 0;
300 
301  // Box bounds
302  int ilo = bx.smallEnd(0);
303  int ihi = bx.bigEnd(0);
304  int jlo = bx.smallEnd(1);
305  int jhi = bx.bigEnd(1);
306  int klo = bx.smallEnd(2);
307  int khi = bx.bigEnd(2);
308  amrex::ignore_unused(ilo, ihi, jlo, jhi);
309 
310  // Temporary FABs for tridiagonal solve (allocated on column)
311  // A[k] * x[k-1] + B[k] * x[k] + C[k+1] = RHS[k]
312  amrex::FArrayBox RHS_fab, soln_fab, coeffG_fab;
313  RHS_fab.resize(bx,1, amrex::The_Async_Arena());
314  soln_fab.resize(bx,1, amrex::The_Async_Arena());
315  coeffG_fab.resize(bx,1, amrex::The_Async_Arena());
316  auto const& RHS_a = RHS_fab.array();
317  auto const& soln_a = soln_fab.array();
318  auto const& coeffG_a = coeffG_fab.array();
319 
320  Real dz_inv = cellSizeInv[2];
321 
322  int bc_comp = BCVars::xvel_bc + stagdir;
323  bool ext_dir_on_zlo = (bc_ptr[bc_comp].lo(2) == ERFBCType::ext_dir ||
324  bc_ptr[bc_comp].lo(2) == ERFBCType::ext_dir_prim);
325  bool ext_dir_on_zhi = (bc_ptr[bc_comp].hi(2) == ERFBCType::ext_dir ||
326  bc_ptr[bc_comp].hi(2) == ERFBCType::ext_dir_prim);
327  bool foextrap_on_zlo = (bc_ptr[bc_comp].lo(2) == ERFBCType::foextrap);
328  bool foextrap_on_zhi = (bc_ptr[bc_comp].hi(2) == ERFBCType::foextrap);
329  amrex::ignore_unused(foextrap_on_zlo,foextrap_on_zhi);
330 
331  AMREX_ASSERT_WITH_MESSAGE(foextrap_on_zlo || ext_dir_on_zlo || use_SurfLayer,
332  "Unexpected lower BC for momentum used with implicit vertical diffusion");
333  AMREX_ASSERT_WITH_MESSAGE(foextrap_on_zhi || ext_dir_on_zhi,
334  "Unexpected upper BC for momentum used with implicit vertical diffusion");
335 
336  Real Fact = implicit_fac * dt * dz_inv;
337 
338 #ifdef AMREX_USE_GPU
339  ParallelFor(makeSlab(bx,2,0), [=] AMREX_GPU_DEVICE (int i, int j, int)
340  {
341 #else
342  for (int j(jlo); j<=jhi; ++j) {
343  for (int i(ilo); i<=ihi; ++i) {
344 #endif
345  // Notes:
346  //
347  // - In DiffusionSrcForMom (e.g., for x-mom)
348  //
349  // Real diffContrib = ...
350  // + (tau13(i,j,k+1) - tau13(i,j,k)) / dzinv
351  // rho_u_rhs(i,j,k) -= diffContrib; // note the negative sign
352  //
353  // - We need to scale the explicit _part_ of `tau13` (for x-mom) by (1 - implicit_fac)
354  // The part that needs to be scaled is stored in `tau_corr`.
355  // E.g., tau13 = myhalf * (du/dz + dw/dx)
356  // tau13_corr = myhalf * du/dz
357  //
358  // - The momentum (`face_data`) was set to `S_old + S_rhs * dt`
359  // prior to including "ERF_Implicit.H". Recall that S_rhs includes
360  // sources from advection and other forcings, not just diffusion.
361  //
362  // - To correct momentum, we need to subtract `implicit_fac * diffContrib_corr`
363  // from S_rhs to recover `(1 - implicit_fac) * diffContrib_corr`,
364  // where `diffContrib_corr = -d(tau_corr)/dz`. The negative sign
365  // comes from our convention for the RHS diffusion source.
366  //
367  // Subtracting a negative gives the += below; multiply by dt to
368  // get the intermediate momentum on the RHS of the tridiagonal
369  // system.
370  //
371  // - With a surface_layer BC, tau13/23 holds the vertical flux -d_z(k*u_i)
372  // directly. We must use tau at klo (not tau_corr) with SL BCs.
373  //
374  // - The detJ for divergence was multiplied through.
375  // Therefore, it doesn't show up in the A/B denominator,
376  // but it does modify B and the RHS.
377  //
378  // - Finally, the terms ~ RHS += (tau_corr_hi - tau_corr_lo) / dz (below)
379  // essentially undo the explicit diffusion update that will be
380  // handled here implicitly.
381 
382  // Bottom boundary coefficients and RHS for L decomp
383  //===================================================
384  Real rhoface, rhoAlpha_lo, rhoAlpha_hi;
385  Real detJface, met_h_zeta_lo, met_h_zeta_hi;
386  Real a_tmp, b_tmp, c_tmp, inv_b2_tmp;
387  {
388  detJface = myhalf * (detJ(i,j,klo) + detJ(i-ioff,j-joff,klo));
389  rhoface = myhalf * (cell_data(i,j,klo,Rho_comp) + cell_data(i-ioff,j-joff,klo,Rho_comp));
390  getRhoAlphaForFaces(i, j, klo, ioff, joff, rhoAlpha_lo, rhoAlpha_hi,
391  cell_data, mu_turb, mu_eff,
392  l_consA, l_turb);
393 
394  met_h_zeta_lo = myhalf * ( Compute_h_zeta_AtKface(i ,j ,klo ,cellSizeInv,z_nd)
395  + Compute_h_zeta_AtKface(i-ioff,j-joff,klo ,cellSizeInv,z_nd) );
396  met_h_zeta_hi = myhalf * ( Compute_h_zeta_AtKface(i ,j ,klo+1,cellSizeInv,z_nd)
397  + Compute_h_zeta_AtKface(i-ioff,j-joff,klo+1,cellSizeInv,z_nd) );
398 
399  a_tmp = zero;
400  c_tmp = -Fact * gfac * rhoAlpha_hi * dz_inv / met_h_zeta_hi;
401 
402  RHS_a(i,j,klo) = detJface * face_data(i,j,klo); // NOTE: this is momenta; solution is velocity
403 
404  // BCs: Dirichlet (u_i = val), slip wall (w = 0), or surface layer (w = 0)
405  if (ext_dir_on_zlo) {
406  RHS_a(i,j,klo) += Fact * gfac * (tau_corr(i,j,klo+1) - tau_corr(i,j,klo));
407  if (stagdir==2) {
408  c_tmp = zero;
409  RHS_a(i,j,klo) = zero;
410  } else {
411  // NOTE: wall is 1/2 dz away (2 dz_inv)
412  a_tmp = -two * Fact * rhoAlpha_lo * dz_inv / met_h_zeta_lo;
413  const Real rho_wall = myhalf * ( cell_data(i ,j ,klo-1,Rho_comp)
414  + cell_data(i-ioff,j-joff,klo-1,Rho_comp) );
415  const Real wall_velocity = face_data(i,j,klo-1) / rho_wall;
416  RHS_a(i,j,klo) -= a_tmp * wall_velocity;
417  }
418  } else if (use_SurfLayer) {
419  // NOTE: tau = -mu*d_z(u_i) w/ SL
420  RHS_a(i,j,klo) += Fact * gfac * (tau_corr(i,j,klo+1) - tau(i,j,klo));
421  RHS_a(i,j,klo) += Fact * tau(i,j,klo);
422  } else {
423  // NOTE: FOEXTRAP has zero lower flux (nothing to add to RHS)
424  RHS_a(i,j,klo) += Fact * gfac * (tau_corr(i,j,klo+1) - tau_corr(i,j,klo));
425  }
426 
427  // Add YSU momentum countergradient correction at bottom boundary.
428  // NOTE: As for the scalars, the lower face at klo carries no
429  // countergradient flux -- the surface stress is supplied by the
430  // surface layer model or the wall BC above. Only the upper face
431  // contributes here.
432  // NOTE: The sign matches the scalar path: tau_i3 = -rho*K*(du_i/dz - gamma_i),
433  // so the countergradient piece of the flux is +rho*K*gamma_i and its
434  // divergence enters the RHS with a minus sign.
435  if (use_ysu_mom_countergradient && stagdir < 2) {
436  const int hgam_comp = (stagdir == 0) ? EddyDiff::HGAMU_v : EddyDiff::HGAMV_v;
437  // Average HGAM* to the staggered face
438  const Real gam_klo = myhalf * (mu_turb(i,j,klo ,hgam_comp) + mu_turb(i-ioff,j-joff,klo ,hgam_comp));
439  const Real gam_kp1 = myhalf * (mu_turb(i,j,klo+1,hgam_comp) + mu_turb(i-ioff,j-joff,klo+1,hgam_comp));
440  const Real gam_hi = myhalf * (gam_klo + gam_kp1);
441  RHS_a(i,j,klo) -= Fact * rhoAlpha_hi * gam_hi;
442  }
443 
444  b_tmp = detJface * rhoface - a_tmp - c_tmp;
445  inv_b2_tmp = one;
446 
447  RHS_a(i,j,klo) /= b_tmp; // NOTE: this is now "rho"
448  coeffG_a(i,j,klo) = c_tmp / b_tmp; // NOTE: this is now "gamma"
449  }
450 
451  // Build the coefficients and RHS for L decomp
452  //===================================================
453  for (int k(klo+1); k < khi; k++) {
454  detJface = myhalf * (detJ(i,j,k) + detJ(i-ioff,j-joff,k));
455  rhoface = myhalf * (cell_data(i,j,k,Rho_comp) + cell_data(i-ioff,j-joff,k,Rho_comp));
456  getRhoAlphaForFaces(i, j, k, ioff, joff, rhoAlpha_lo, rhoAlpha_hi,
457  cell_data, mu_turb, mu_eff,
458  l_consA, l_turb);
459 
460  met_h_zeta_lo = myhalf * ( Compute_h_zeta_AtKface(i ,j ,k ,cellSizeInv,z_nd)
461  + Compute_h_zeta_AtKface(i-ioff,j-joff,k ,cellSizeInv,z_nd) );
462  met_h_zeta_hi = myhalf * ( Compute_h_zeta_AtKface(i ,j ,k+1,cellSizeInv,z_nd)
463  + Compute_h_zeta_AtKface(i-ioff,j-joff,k+1,cellSizeInv,z_nd) );
464 
465  a_tmp = -Fact * rhoAlpha_lo * dz_inv / met_h_zeta_lo;
466  c_tmp = -Fact * rhoAlpha_hi * dz_inv / met_h_zeta_hi;
467  b_tmp = detJface * rhoface - a_tmp - c_tmp;
468  inv_b2_tmp = one / (b_tmp - a_tmp * coeffG_a(i,j,k-1));
469 
470  RHS_a(i,j,k) = detJface * face_data(i,j,k); // NOTE: this is momenta; solution is velocity
471  RHS_a(i,j,k) += Fact * gfac * (tau_corr(i,j,k+1) - tau_corr(i,j,k));
472 
473  // Add YSU momentum countergradient correction
474  if (use_ysu_mom_countergradient && stagdir < 2) {
475  const int hgam_comp = (stagdir == 0) ? EddyDiff::HGAMU_v : EddyDiff::HGAMV_v;
476  // Average HGAM* to the staggered face
477  const Real gam_k = myhalf * (mu_turb(i,j,k ,hgam_comp) + mu_turb(i-ioff,j-joff,k ,hgam_comp));
478  const Real gam_km1 = myhalf * (mu_turb(i,j,k-1,hgam_comp) + mu_turb(i-ioff,j-joff,k-1,hgam_comp));
479  const Real gam_kp1 = myhalf * (mu_turb(i,j,k+1,hgam_comp) + mu_turb(i-ioff,j-joff,k+1,hgam_comp));
480  const Real gam_hi = myhalf * (gam_k + gam_kp1); // at k+1/2
481  const Real gam_lo = myhalf * (gam_k + gam_km1); // at k-1/2
482  RHS_a(i,j,k) -= Fact * (rhoAlpha_hi * gam_hi - rhoAlpha_lo * gam_lo);
483  }
484 
485  RHS_a(i,j,k) = (RHS_a(i,j,k) - a_tmp * RHS_a(i,j,k-1)) * inv_b2_tmp; // NOTE: This is now "rho"
486  coeffG_a(i,j,k) = c_tmp * inv_b2_tmp; // NOTE: this is now "gamma"
487  } // k
488 
489  // Top boundary coefficients and RHS for L decomp
490  //===================================================
491  {
492  detJface = myhalf * (detJ(i,j,khi) + detJ(i-ioff,j-joff,khi));
493  rhoface = myhalf * (cell_data(i,j,khi,Rho_comp) + cell_data(i-ioff,j-joff,khi,Rho_comp));
494  getRhoAlphaForFaces(i, j, khi, ioff, joff, rhoAlpha_lo, rhoAlpha_hi,
495  cell_data, mu_turb, mu_eff,
496  l_consA, l_turb);
497 
498  met_h_zeta_lo = myhalf * ( Compute_h_zeta_AtKface(i ,j ,khi ,cellSizeInv,z_nd)
499  + Compute_h_zeta_AtKface(i-ioff,j-joff,khi ,cellSizeInv,z_nd) );
500  met_h_zeta_hi = myhalf * ( Compute_h_zeta_AtKface(i ,j ,khi+1,cellSizeInv,z_nd)
501  + Compute_h_zeta_AtKface(i-ioff,j-joff,khi+1,cellSizeInv,z_nd) );
502 
503  a_tmp = -Fact * gfac * rhoAlpha_lo * dz_inv / met_h_zeta_lo;
504  c_tmp = zero;
505 
506  RHS_a(i,j,khi) = detJface * face_data(i,j,khi); // NOTE: this is momenta; solution is velocity
507  RHS_a(i,j,khi) += Fact * gfac * (tau_corr(i,j,khi+1) - tau_corr(i,j,khi));
508 
509  // BCs: Dirichlet (u_i = val), slip wall (w = 0)
510  if (ext_dir_on_zhi) {
511  if (stagdir==2) {
512  a_tmp = zero;
513  RHS_a(i,j,khi) = zero;
514  } else {
515  // NOTE: wall is 1/2 dz away (2 dz_inv)
516  c_tmp = -two * Fact * rhoAlpha_hi * dz_inv / met_h_zeta_hi;
517  const Real rho_wall = myhalf * ( cell_data(i ,j ,khi+1,Rho_comp)
518  + cell_data(i-ioff,j-joff,khi+1,Rho_comp) );
519  const Real wall_velocity = face_data(i,j,khi+1) / rho_wall;
520  RHS_a(i,j,khi) -= c_tmp * wall_velocity;
521  }
522  }
523 
524  b_tmp = detJface * rhoface - a_tmp - c_tmp;
525  inv_b2_tmp = one / (b_tmp - a_tmp * coeffG_a(i,j,khi-1));
526 
527  // First solve
528  soln_a(i,j,khi) = (RHS_a(i,j,khi) - a_tmp * RHS_a(i,j,khi-1)) * inv_b2_tmp;
529  }
530 
531  // Back sweep the U decomp solution
532  //===================================================
533  for (int k(khi-1); k>=klo; --k) {
534  soln_a(i,j,k) = RHS_a(i,j,k) - coeffG_a(i,j,k) * soln_a(i,j,k+1);
535  }
536 
537  // Convert back to momenta
538  //===================================================
539  for (int k(klo); k<=khi; ++k) {
540  rhoface = myhalf * (cell_data(i,j,k,Rho_comp) + cell_data(i-ioff,j-joff,k,Rho_comp));
541  face_data(i,j,k) = rhoface * soln_a(i,j,k);
542  }
543 
544 #ifdef AMREX_USE_GPU
545  });
546 #else
547  } // i
548  } // j
549 #endif
550 }
constexpr amrex::Real three
Definition: ERF_Constants.H:11
constexpr amrex::Real two
Definition: ERF_Constants.H:10
constexpr amrex::Real one
Definition: ERF_Constants.H:9
constexpr amrex::Real zero
Definition: ERF_Constants.H:8
constexpr amrex::Real myhalf
Definition: ERF_Constants.H:13
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void getRhoAlphaForFaces(int i, int j, int k, int ioff, int joff, amrex::Real &rhoAlpha_lo, amrex::Real &rhoAlpha_hi, const amrex::Array4< const amrex::Real > &cell_data, const amrex::Array4< const amrex::Real > &mu_turb, const amrex::Real mu_eff, bool l_consA, bool l_turb)
Definition: ERF_GetRhoAlphaForFaces.H:22
#define Rho_comp
Definition: ERF_IndexDefines.H:39
const int khi
Definition: ERF_InitCustomPert_Bubble.H:21
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);})
AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real Compute_h_zeta_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:398
AMREX_ASSERT_WITH_MESSAGE(wbar_cutoff_min > wbar_cutoff_max, "ERROR: wbar_cutoff_min < wbar_cutoff_max")
@ xvel_bc
Definition: ERF_IndexDefines.H:105
@ foextrap
Definition: ERF_IndexDefines.H:291
@ ext_dir
Definition: ERF_IndexDefines.H:292
@ ext_dir_prim
Definition: ERF_IndexDefines.H:295
@ HGAMU_v
Definition: ERF_IndexDefines.H:262
@ HGAMV_v
Definition: ERF_IndexDefines.H:263
Definition: ERF_DiffStruct.H:22
MolecDiffType molec_diff_type
Selected molecular transport model.
Definition: ERF_DiffStruct.H:94
amrex::Real dynamic_viscosity
Dynamic viscosity for momentum diffusion [kg/(m-s)].
Definition: ERF_DiffStruct.H:106
amrex::Real rho0_trans
Reference density used to compute dynamic diffusion coefficients [kg/m3].
Definition: ERF_DiffStruct.H:101
amrex::Vector< TurbChoice > turbChoice
Turbulence options for each AMR level.
Definition: ERF_DataStruct.H:1864
DiffChoice diffChoice
Diffusion-related options.
Definition: ERF_DataStruct.H:1861
Definition: ERF_TurbStruct.H:114
bool use_kturb
Whether any turbulence model is active.
Definition: ERF_TurbStruct.H:665
Here is the call graph for this function:

◆ ImplicitDiffForStateLU_T()

void ImplicitDiffForStateLU_T ( const Box &  bx,
const Box &  domain,
const int  level,
const int  n,
const double  dt_d,
const GpuArray< Real, AMREX_SPACEDIM *2 > &  bc_neumann_vals,
const Array4< Real > &  cell_data,
const Array4< const Real > &  z_nd,
const Array4< const Real > &  detJ,
const GpuArray< Real, AMREX_SPACEDIM > &  cellSizeInv,
const Array4< const Real > &  scalar_zflux,
const Array4< const Real > &  mu_turb,
const SolverChoice solverChoice,
const BCRec *  bc_ptr,
const bool  use_SurfLayer,
const Real  implicit_fac,
const bool  use_mrf_countergradient 
)

Function for computing the implicit contribution to the vertical diffusion of theta, with terrain.

Parameters
[in]bxcell-centered box to loop over
[in]domainbox of the whole domain
[in]levelAMR level
[in]nconserved component index
[in]dt_dtime step
[in]bc_neumann_valsvalues of derivatives if bc_type == Neumann
[in,out]cell_dataconserved cell-centered rho, rho theta
[in]z_ndnodal array of z
[in]detJJacobian determinant
[in]cellSizeInvinverse cell size array
[in]scalar_zfluxscalar vertical flux in z-dir
[in]mu_turbturbulent viscosity
[in]solverChoicecontainer of parameters
[in]bc_ptrcontainer with boundary conditions
[in]use_SurfLayerwhether we have turned on subgrid diffusion
[in]implicit_facif 1 then fully implicit; if 0 then fully explicit
[in]use_mrf_countergradientwhether to include MRF countergradient correction
49 {
50  BL_PROFILE_VAR("ImplicitDiffForState_T()",ImplicitDiffForState_T);
51 
52  Real dt = static_cast<Real>(dt_d);
53 
54  // setup quantities for getRhoAlpha()
55 #include "ERF_SetupVertDiff.H"
56  const int qty_index = n;
57  const int prim_index = qty_index - 1;
58  const int prim_scal_index = (qty_index >= RhoScalar_comp && qty_index < RhoScalar_comp+NSCALARS) ? PrimScalar_comp : prim_index;
59 
60  // Box bounds
61  int ilo = bx.smallEnd(0);
62  int ihi = bx.bigEnd(0);
63  int jlo = bx.smallEnd(1);
64  int jhi = bx.bigEnd(1);
65  int klo = bx.smallEnd(2);
66  int khi = bx.bigEnd(2);
67  amrex::ignore_unused(ilo, ihi, jlo, jhi);
68 
69  // Temporary FABs for tridiagonal solve (allocated on column)
70  // A[k] * x[k-1] + B[k] * x[k] + C[k+1] = RHS[k]
71 
72  // With LU decomposition, M * x = r is written as L * U * x = r with U * x = rho
73  // We then first have L * rho = r and U * x = rho
74  amrex::FArrayBox RHS_fab, soln_fab, coeffG_fab;
75  RHS_fab.resize(bx,1, amrex::The_Async_Arena());
76  soln_fab.resize(bx,1, amrex::The_Async_Arena());
77  coeffG_fab.resize(bx,1, amrex::The_Async_Arena());
78  auto const& RHS_a = RHS_fab.array();
79  auto const& soln_a = soln_fab.array();
80  auto const& coeffG_a = coeffG_fab.array();
81 
82  Real dz_inv = cellSizeInv[2];
83 
84  int bc_comp = qty_index;
85  bool foextrap_on_zlo = (bc_ptr[bc_comp].lo(2) == ERFBCType::foextrap);
86  bool foextrap_on_zhi = (bc_ptr[bc_comp].hi(2) == ERFBCType::foextrap);
87  bool neumann_on_zlo = (bc_ptr[bc_comp].lo(2) == ERFBCType::neumann);
88  bool neumann_on_zhi = (bc_ptr[bc_comp].hi(2) == ERFBCType::neumann);
89  amrex::ignore_unused(foextrap_on_zlo, foextrap_on_zhi);
90 
91  AMREX_ASSERT_WITH_MESSAGE(foextrap_on_zlo || neumann_on_zlo || use_SurfLayer,
92  "Unexpected lower BC for scalars used with implicit vertical diffusion");
93  AMREX_ASSERT_WITH_MESSAGE(foextrap_on_zhi || neumann_on_zhi,
94  "Unexpected upper BC for scalars used with implicit vertical diffusion");
95 
96  Real Fact = implicit_fac * dt * dz_inv;
97 
98 #ifdef AMREX_USE_GPU
99  ParallelFor(makeSlab(bx,2,0), [=] AMREX_GPU_DEVICE (int i, int j, int)
100  {
101 #else
102  for (int j(jlo); j<=jhi; ++j) {
103  for (int i(ilo); i<=ihi; ++i) {
104 #endif
105  // Notes: The detJ for divergence was multiplied through.
106  // Therefore, it doesn't show up in the A/B denominator,
107  // but it does modify B and the RHS.
108 
109  // Bottom boundary coefficients and RHS for L decomp
110  //===================================================
111  Real rhoAlpha_lo, rhoAlpha_hi;
112  Real met_h_zeta_lo, met_h_zeta_hi;
113  Real a_tmp, b_tmp, c_tmp, inv_b2_tmp;
114  {
115  getRhoAlpha(i, j, klo, rhoAlpha_lo, rhoAlpha_hi,
116  cell_data, mu_turb, d_alpha_eff, d_eddy_diff_idz,
117  prim_index, prim_scal_index, l_consA, l_turb);
118 
119  met_h_zeta_hi = Compute_h_zeta_AtKface(i,j,klo+1,cellSizeInv,z_nd);
120 
121  a_tmp = zero;
122  c_tmp = -Fact * rhoAlpha_hi * dz_inv / met_h_zeta_hi;
123  b_tmp = detJ(i,j,klo) * cell_data(i,j,klo,Rho_comp) - a_tmp - c_tmp;
124  inv_b2_tmp = one;
125 
126  RHS_a(i,j,klo) = detJ(i,j,klo) * cell_data(i,j,klo,n); // NOTE: this is rho*phi; solution is phi
127  if (use_SurfLayer && scalar_zflux) {
128  RHS_a(i,j,klo) += Fact * scalar_zflux(i,j,klo); // NOTE: scalar_zflux = -K*d_z(\phi)
129  } else if (neumann_on_zlo) {
130  RHS_a(i,j,klo) += -Fact * rhoAlpha_lo * bc_neumann_vals[2]; // NOTE: N_val = d_z(\phi)
131  }
132 
133  // Add countergradient correction to RHS at bottom boundary.
134  // NOTE: The lower face at klo carries no countergradient flux -- the
135  // total surface flux is supplied by the surface layer model or the
136  // Neumann BC, while gamma represents nonlocal transport interior to
137  // the PBL. Only the upper face contributes here.
138  if (use_mrf_countergradient && (n == RhoTheta_comp || n == RhoQ1_comp)) {
139  const int gam_comp = (n == RhoTheta_comp) ? EddyDiff::HGAMT_v : EddyDiff::HGAMQ_v;
140  const Real gam_hi = myhalf * (mu_turb(i, j, klo, gam_comp) + mu_turb(i, j, klo+1, gam_comp));
141  RHS_a(i,j,klo) -= Fact * rhoAlpha_hi * gam_hi;
142  }
143 
144  RHS_a(i,j,klo) /= b_tmp; // NOTE: this is now "rho"
145  coeffG_a(i,j,klo) = c_tmp / b_tmp; // NOTE: this is now "gamma"
146  }
147 
148  // Build the coefficients and RHS for L decomp
149  //===================================================
150  for (int k(klo+1); k < khi; k++) {
151  getRhoAlpha(i, j, k, rhoAlpha_lo, rhoAlpha_hi,
152  cell_data, mu_turb, d_alpha_eff, d_eddy_diff_idz,
153  prim_index, prim_scal_index, l_consA, l_turb);
154 
155  met_h_zeta_lo = Compute_h_zeta_AtKface(i,j,k ,cellSizeInv,z_nd);
156  met_h_zeta_hi = Compute_h_zeta_AtKface(i,j,k+1,cellSizeInv,z_nd);
157 
158  a_tmp = -Fact * rhoAlpha_lo * dz_inv / met_h_zeta_lo;
159  c_tmp = -Fact * rhoAlpha_hi * dz_inv / met_h_zeta_hi;
160  b_tmp = detJ(i,j,k) * cell_data(i,j,k,Rho_comp) - a_tmp - c_tmp;
161  inv_b2_tmp = one / (b_tmp - a_tmp * coeffG_a(i,j,k-1));
162 
163  RHS_a(i,j,k) = detJ(i,j,k) * cell_data(i,j,k,n); // NOTE: this is rho*phi; solution is phi
164 
165  // Add countergradient correction to RHS in interior
166  if (use_mrf_countergradient && (n == RhoTheta_comp || n == RhoQ1_comp)) {
167  const int gam_comp = (n == RhoTheta_comp) ? EddyDiff::HGAMT_v : EddyDiff::HGAMQ_v;
168  const Real gam_k = mu_turb(i, j, k, gam_comp);
169  const Real gam_km1 = mu_turb(i, j, k-1, gam_comp);
170  const Real gam_kp1 = mu_turb(i, j, k+1, gam_comp);
171  const Real gam_hi = myhalf * (gam_k + gam_kp1); // at k+½
172  const Real gam_lo = myhalf * (gam_k + gam_km1); // at k-½
173  // Countergradient flux divergence (implicit contribution to RHS):
174  // -Fact * [ρα_{k+½}·γ_{k+½} - ρα_{k-½}·γ_{k-½}]
175  // NOTE: no 1/met_h_zeta here. met_h_zeta appears in a_tmp/c_tmp only
176  // because the diffusive flux contains an inner vertical
177  // derivative d(phi)/dz ~ dz_inv/h_zeta * delta_phi. The
178  // countergradient piece contains no derivative, so it takes only
179  // the outer dz_inv already carried in Fact.
180  RHS_a(i,j,k) -= Fact * (rhoAlpha_hi * gam_hi - rhoAlpha_lo * gam_lo);
181  }
182 
183  RHS_a(i,j,k) = (RHS_a(i,j,k) - a_tmp * RHS_a(i,j,k-1)) * inv_b2_tmp; // NOTE: This is now "rho"
184  coeffG_a(i,j,k) = c_tmp * inv_b2_tmp; // NOTE: this is now "gamma"
185  } // k
186 
187  // Top boundary coefficients and RHS for L decomp
188  //===================================================
189  {
190  getRhoAlpha(i, j, khi, rhoAlpha_lo, rhoAlpha_hi,
191  cell_data, mu_turb, d_alpha_eff, d_eddy_diff_idz,
192  prim_index, prim_scal_index, l_consA, l_turb);
193 
194  // Lower-face metric shared with row khi-1.
195  met_h_zeta_lo = Compute_h_zeta_AtKface(i,j,khi ,cellSizeInv,z_nd);
196 
197  a_tmp = -Fact * rhoAlpha_lo * dz_inv / met_h_zeta_lo;
198  c_tmp = zero;
199  b_tmp = detJ(i,j,khi) * cell_data(i,j,khi,Rho_comp) - a_tmp - c_tmp;
200  inv_b2_tmp = one / (b_tmp - a_tmp * coeffG_a(i,j,khi-1));
201 
202  RHS_a(i,j,khi) = detJ(i,j,khi) * cell_data(i,j,khi,n); // NOTE: this is rho*phi; solution is phi
203  if (neumann_on_zhi) {
204  RHS_a(i,j,khi) -= -Fact * rhoAlpha_hi * bc_neumann_vals[5]; // NOTE: N_val = d_z(\phi)
205  }
206 
207  // First solve
208  soln_a(i,j,khi) = (RHS_a(i,j,khi) - a_tmp * RHS_a(i,j,khi-1)) * inv_b2_tmp;
209  }
210 
211  // Back sweep the U decomp solution
212  //===================================================
213  for (int k(khi-1); k>=klo; --k) {
214  soln_a(i,j,k) = RHS_a(i,j,k) - coeffG_a(i,j,k) * soln_a(i,j,k+1);
215  }
216 
217  // Convert back to rho*theta
218  //===================================================
219  for (int k(klo); k<=khi; ++k) {
220  cell_data(i,j,k,n) = cell_data(i,j,k,Rho_comp) * soln_a(i,j,k);
221  }
222 
223 #ifdef AMREX_USE_GPU
224  });
225 #else
226  } // i
227  } // j
228 #endif
229 }
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void getRhoAlpha(int i, int j, int k, amrex::Real &rhoAlpha_lo, amrex::Real &rhoAlpha_hi, const amrex::Array4< const amrex::Real > &cell_data, const amrex::Array4< const amrex::Real > &mu_turb, const amrex::Real *d_alpha_eff, const int *d_eddy_diff_idz, int prim_index, int prim_scal_index, bool l_consA, bool l_turb)
Definition: ERF_GetRhoAlpha.H:20
#define RhoScalar_comp
Definition: ERF_IndexDefines.H:43
#define RhoTheta_comp
Definition: ERF_IndexDefines.H:40
#define NSCALARS
Definition: ERF_IndexDefines.H:16
#define RhoQ1_comp
Definition: ERF_IndexDefines.H:45
#define PrimScalar_comp
Definition: ERF_IndexDefines.H:60
@ neumann
Definition: ERF_IndexDefines.H:297
@ HGAMQ_v
Definition: ERF_IndexDefines.H:261
@ HGAMT_v
Definition: ERF_IndexDefines.H:260
Here is the call graph for this function: