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

Macros

#define INSTANTIATE_IMPLICIT_DIFF_FOR_MOM_LU(STAGDIR)
 

Functions

void ImplicitDiffForStateLU_N (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 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, const Array4< const Real > &wall_face, const Array4< const Real > &wall_pin)
 
template<int stagdir>
void ImplicitDiffForMomLU_N (const Box &bx, const Box &domain, const int level, const double dt_d, const Array4< const int > &col_kext, const Array4< const Real > &cell_data, const Array4< Real > &face_data, const Array4< const Real > &tau, const Array4< const Real > &tau_corr, 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, const Array4< const Real > &wall_face)
 
void ImplicitVerticalFluxForReflux_N (const Box &bx, const Box &domain, const int level, const int n, const Real implicit_fac, const Array4< const Real > &cell_data, const GpuArray< Real, AMREX_SPACEDIM > &cellSizeInv, const Array4< const Real > &mu_turb, const SolverChoice &solverChoice, const Array4< const Real > &mf_mx, const Array4< const Real > &mf_my, const bool use_mrf_countergradient, const Array4< Real > &zflux)
 

Macro Definition Documentation

◆ INSTANTIATE_IMPLICIT_DIFF_FOR_MOM_LU

#define INSTANTIATE_IMPLICIT_DIFF_FOR_MOM_LU (   STAGDIR)
Value:
template void ImplicitDiffForMomLU_N<STAGDIR> ( \
const Box&, \
const Box&, \
const int, \
const double, \
const Array4<const int >&, \
const Array4<const Real>&, \
const Array4< 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, \
const Array4<const Real>&);
amrex::Real Real
Definition: ERF_ShocInterface.H:19
Definition: ERF_DataStruct.H:715

Function Documentation

◆ ImplicitDiffForMomLU_N()

template<int stagdir>
void ImplicitDiffForMomLU_N ( const Box &  bx,
const Box &  domain,
const int  level,
const double  dt_d,
const Array4< const int > &  col_kext,
const Array4< const Real > &  cell_data,
const Array4< Real > &  face_data,
const Array4< const Real > &  tau,
const Array4< const Real > &  tau_corr,
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,
const Array4< const Real > &  wall_face 
)

Function for computing the implicit contribution to the vertical diffusion of momentum, with a uniform grid and no terrain.

This function (explicitly instantiated below) handles staggering in x or y through the template parameter, stagdir.

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]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
329 {
330  // Fraction-stress immersed wall law: wall_face is zero on the bottom face of the wall cell
331  // of each face column (xz or yz edges), which then carries no diffusive coupling; the wall
332  // stress is a source term (ERF_ImmersedWallCell.H). Empty when not in use.
333  const bool has_wall_face = static_cast<bool>(wall_face);
334 
335  BL_PROFILE_VAR("ImplicitDiffForMom_N()",ImplicitDiffForMom_N);
336 
337  Real dt = static_cast<Real>(dt_d);
338 
339  // setup quantities for getRhoAlphaAtFaces()
340  DiffChoice dc = solverChoice.diffChoice;
341  TurbChoice tc = solverChoice.turbChoice[level];
342  bool l_consA = (dc.molec_diff_type == MolecDiffType::ConstantAlpha);
343  bool l_turb = tc.use_kturb;
344  // The correction strains for u/v contain a factor of 1/2, so mu_eff does not carry
345  // the factor of 2 of the stresses
346  Real mu_eff = (l_consA) ? dc.dynamic_viscosity / dc.rho0_trans
347  : dc.dynamic_viscosity;
348 
349  // offsets used to average to faces
350  constexpr int ioff = (stagdir == 0) ? 1 : 0;
351  constexpr int joff = (stagdir == 1) ? 1 : 0;
352 
353  // Box bounds
354  int ilo = bx.smallEnd(0);
355  int ihi = bx.bigEnd(0);
356  int jlo = bx.smallEnd(1);
357  int jhi = bx.bigEnd(1);
358  int box_klo = bx.smallEnd(2);
359  int box_khi = bx.bigEnd(2);
360  amrex::ignore_unused(ilo, ihi, jlo, jhi);
361 
362  // The vertical extent of the domain. A column whose top or bottom is not the domain
363  // boundary ends at a coarse/fine boundary instead, and must not be given a physical BC row.
364  const int dom_klo = domain.smallEnd(2);
365  const int dom_khi = domain.bigEnd(2);
366 
367  // Temporary FABs for tridiagonal solve (allocated on column)
368  // A[k] * x[k-1] + B[k] * x[k] + C[k+1] = RHS[k]
369  amrex::FArrayBox RHS_fab, soln_fab, coeffG_fab;
370  RHS_fab.resize(bx,1, amrex::The_Async_Arena());
371  soln_fab.resize(bx,1, amrex::The_Async_Arena());
372  coeffG_fab.resize(bx,1, amrex::The_Async_Arena());
373  auto const& RHS_a = RHS_fab.array();
374  auto const& soln_a = soln_fab.array();
375  auto const& coeffG_a = coeffG_fab.array();
376 
377  Real dz_inv = cellSizeInv[2];
378 
379  int bc_comp = BCVars::xvel_bc + stagdir;
380  bool ext_dir_on_zlo = (bc_ptr[bc_comp].lo(2) == ERFBCType::ext_dir ||
381  bc_ptr[bc_comp].lo(2) == ERFBCType::ext_dir_prim);
382  bool ext_dir_on_zhi = (bc_ptr[bc_comp].hi(2) == ERFBCType::ext_dir ||
383  bc_ptr[bc_comp].hi(2) == ERFBCType::ext_dir_prim);
384  bool foextrap_on_zlo = (bc_ptr[bc_comp].lo(2) == ERFBCType::foextrap);
385  bool foextrap_on_zhi = (bc_ptr[bc_comp].hi(2) == ERFBCType::foextrap);
386  amrex::ignore_unused(foextrap_on_zlo,foextrap_on_zhi);
387 
388  AMREX_ASSERT_WITH_MESSAGE(foextrap_on_zlo || ext_dir_on_zlo || use_SurfLayer,
389  "Unexpected lower BC for momentum used with implicit vertical diffusion");
390  AMREX_ASSERT_WITH_MESSAGE(foextrap_on_zhi || ext_dir_on_zhi,
391  "Unexpected upper BC for momentum used with implicit vertical diffusion");
392 
393  Real Fact = implicit_fac * dt * dz_inv;
394 
395 #ifdef AMREX_USE_GPU
396  ParallelFor(makeSlab(bx,2,0), [=] AMREX_GPU_DEVICE (int i, int j, int)
397  {
398 #else
399  for (int j(jlo); j<=jhi; ++j) {
400  for (int i(ilo); i<=ihi; ++i) {
401 #endif
402 
403  // The face column at (i,j) is shared by the two cell columns on either side of it,
404  // and so by the box on either side of a grid seam. Solve only over the vertical
405  // range that both of those cell columns cover: the boxes on the two sides then
406  // build the same tridiagonal system and get the same answer, so the duplicated
407  // faces along the seam stay consistent. Faces outside that range sit on a lateral
408  // coarse/fine boundary and are set from the coarse level, so it is consistent to
409  // leave them to the explicit update.
410  const int klo = amrex::max(box_klo, col_kext(i-ioff,j-joff,0,0),
411  col_kext(i ,j ,0,0));
412  const int khi = amrex::min(box_khi, col_kext(i-ioff,j-joff,0,1),
413  col_kext(i ,j ,0,1));
414 
415  // Fewer than two faces in common -- nothing to invert, leave the column explicit
416 #ifdef AMREX_USE_GPU
417  if (khi <= klo) { return; }
418 #else
419  if (khi <= klo) { continue; }
420 #endif
421  // Notes:
422  //
423  // - In DiffusionSrcForMom (e.g., for x-mom)
424  //
425  // Real diffContrib = ...
426  // + (tau13(i,j,k+1) - tau13(i,j,k)) / dzinv
427  // rho_u_rhs(i,j,k) -= diffContrib; // note the negative sign
428  //
429  // - We need to scale the explicit _part_ of `tau13` (for x-mom) by (1 - implicit_fac)
430  // The part that needs to be scaled is stored in `tau_corr`.
431  // E.g., tau13 = 0.5 * (du/dz + dw/dx)
432  // tau13_corr = 0.5 * du/dz
433  //
434  // - The momentum (`face_data`) was set to `S_old + S_rhs * dt`
435  // prior to including "ERF_Implicit.H". Recall that S_rhs includes
436  // sources from advection and other forcings, not just diffusion.
437  //
438  // - To correct momentum, we need to subtract `implicit_fac * diffContrib_corr`
439  // from S_rhs to recover `(1 - implicit_fac) * diffContrib_corr`,
440  // where `diffContrib_corr = -d(tau_corr)/dz`. The negative sign
441  // comes from our convention for the RHS diffusion source.
442  //
443  // Subtracting a negative gives the += below; multiply by dt to
444  // get the intermediate momentum on the RHS of the tridiagonal
445  // system.
446  //
447  // - With a surface_layer BC, tau13/23 holds the vertical flux -d_z(k*u_i)
448  // directly. We must use tau at klo (not tau_corr) with SL BCs.
449  //
450  // - Finally, the terms ~ RHS += (tau_corr_hi - tau_corr_lo) / dz (below)
451  // essentially undo the explicit diffusion update that will be
452  // handled here implicitly.
453 
454  // Bottom boundary coefficients and RHS for L decomp
455  //===================================================
456  Real rhoface, rhoAlpha_lo, rhoAlpha_hi;
457  Real a_tmp, b_tmp, c_tmp, inv_b2_tmp;
458  {
459  rhoface = myhalf * (cell_data(i,j,klo,Rho_comp) + cell_data(i-ioff,j-joff,klo,Rho_comp));
460  getRhoAlphaForFaces(i, j, klo, ioff, joff, rhoAlpha_lo, rhoAlpha_hi,
461  cell_data, mu_turb, mu_eff,
462  l_consA, l_turb);
463 
464  a_tmp = zero;
465  c_tmp = -Fact * rhoAlpha_hi * dz_inv;
466  if (has_wall_face) { c_tmp *= wall_face(i,j,klo+1); }
467 
468  RHS_a(i,j,klo) = face_data(i,j,klo); // NOTE: this is momenta; solution is velocity
469 
470  // BCs: Dirichlet (u_i = val), slip wall (w = 0), or surface layer (w = 0) --
471  // but only where the bottom of this column really is the domain boundary
472  if (klo != dom_klo) {
473  // Coarse/fine (or grid) boundary: close the system with the known face value
474  // below instead of imposing a physical BC at an interior height
475  a_tmp = -Fact * rhoAlpha_lo * dz_inv;
476  RHS_a(i,j,klo) += Fact * (tau_corr(i,j,klo+1) - tau_corr(i,j,klo));
477  const Real rho_below = myhalf * ( cell_data(i ,j ,klo-1,Rho_comp)
478  + cell_data(i-ioff,j-joff,klo-1,Rho_comp) );
479  RHS_a(i,j,klo) -= a_tmp * (face_data(i,j,klo-1) / rho_below);
480  } else if (ext_dir_on_zlo) {
481  RHS_a(i,j,klo) += Fact * (tau_corr(i,j,klo+1) - tau_corr(i,j,klo));
482  // Wall stress with the one-sided gradient of ComputeStrain_N,
483  // (c_wall u_wall + c_0 u_0 + c_1 u_1) * dz_inv: a_tmp multiplies the
484  // wall value, and c_1 adds to the coupling with the cell above
486  a_tmp = Fact * rhoAlpha_lo * dz_inv * wc.c_wall;
487  c_tmp += Fact * rhoAlpha_lo * dz_inv * wc.c_1;
488  const Real rho_wall = myhalf * ( cell_data(i ,j ,klo-1,Rho_comp)
489  + cell_data(i-ioff,j-joff,klo-1,Rho_comp) );
490  const Real wall_velocity = face_data(i,j,klo-1) / rho_wall;
491  RHS_a(i,j,klo) -= a_tmp * wall_velocity;
492  } else if (use_SurfLayer) {
493  // NOTE: tau = -mu*d_z(u_i) w/ SL
494  RHS_a(i,j,klo) += Fact * (tau_corr(i,j,klo+1) - tau(i,j,klo));
495  RHS_a(i,j,klo) += Fact * tau(i,j,klo);
496  } else {
497  // NOTE: FOEXTRAP has zero lower flux (nothing to add to RHS)
498  RHS_a(i,j,klo) += Fact * (tau_corr(i,j,klo+1) - tau_corr(i,j,klo));
499  }
500 
501  // Add YSU momentum countergradient correction at bottom boundary.
502  // NOTE: As for the scalars, the lower face at the domain bottom carries no
503  // countergradient flux -- the surface stress is supplied by the
504  // surface layer model or the wall BC above. At a coarse/fine boundary
505  // the lower face is an interior face and carries it, as in the
506  // interior rows (erf-model/ERF#4822).
507  // NOTE: The sign matches the scalar path: tau_i3 = -rho*K*(du_i/dz - gamma_i),
508  // so the countergradient piece of the flux is +rho*K*gamma_i and its
509  // divergence enters the RHS with a minus sign.
510  if (use_ysu_mom_countergradient) {
511  const int hgam_comp = (stagdir == 0) ? EddyDiff::HGAMU_v : EddyDiff::HGAMV_v;
512  // Average HGAM* to the staggered face
513  const Real gam_klo = myhalf * (mu_turb(i,j,klo ,hgam_comp) + mu_turb(i-ioff,j-joff,klo ,hgam_comp));
514  const Real gam_kp1 = myhalf * (mu_turb(i,j,klo+1,hgam_comp) + mu_turb(i-ioff,j-joff,klo+1,hgam_comp));
515  const Real gam_hi = myhalf * (gam_klo + gam_kp1);
516  RHS_a(i,j,klo) -= Fact * rhoAlpha_hi * gam_hi;
517  if (klo != dom_klo) {
518  const Real gam_km1 = myhalf * (mu_turb(i,j,klo-1,hgam_comp) + mu_turb(i-ioff,j-joff,klo-1,hgam_comp));
519  const Real gam_lo = myhalf * (gam_klo + gam_km1);
520  RHS_a(i,j,klo) += Fact * rhoAlpha_lo * gam_lo;
521  }
522  }
523 
524  // the bottom face of a wall cell at a coarse/fine boundary carries no coupling either
525  if (has_wall_face) { a_tmp *= wall_face(i,j,klo); }
526  b_tmp = rhoface - a_tmp - c_tmp;
527  inv_b2_tmp = one;
528 
529  RHS_a(i,j,klo) /= b_tmp; // NOTE: this is now "rho"
530  coeffG_a(i,j,klo) = c_tmp / b_tmp; // NOTE: this is now "gamma"
531  }
532 
533  // Build the coefficients and RHS for L decomp
534  //===================================================
535  for (int k(klo+1); k < khi; k++) {
536  rhoface = myhalf * (cell_data(i,j,k,Rho_comp) + cell_data(i-ioff,j-joff,k,Rho_comp));
537  getRhoAlphaForFaces(i, j, k, ioff, joff, rhoAlpha_lo, rhoAlpha_hi,
538  cell_data, mu_turb, mu_eff,
539  l_consA, l_turb);
540 
541  a_tmp = -Fact * rhoAlpha_lo * dz_inv;
542  c_tmp = -Fact * rhoAlpha_hi * dz_inv;
543  if (has_wall_face) {
544  a_tmp *= wall_face(i,j,k);
545  c_tmp *= wall_face(i,j,k+1);
546  }
547  b_tmp = rhoface - a_tmp - c_tmp;
548  inv_b2_tmp = one/ (b_tmp - a_tmp * coeffG_a(i,j,k-1));
549 
550  RHS_a(i,j,k) = face_data(i,j,k); // NOTE: this is momenta; solution is velocity
551  RHS_a(i,j,k) += Fact * (tau_corr(i,j,k+1) - tau_corr(i,j,k));
552 
553  // Add YSU momentum countergradient correction
554  if (use_ysu_mom_countergradient) {
555  const int hgam_comp = (stagdir == 0) ? EddyDiff::HGAMU_v : EddyDiff::HGAMV_v;
556  // Average HGAM* to the staggered face
557  const Real gam_k = myhalf * (mu_turb(i,j,k ,hgam_comp) + mu_turb(i-ioff,j-joff,k ,hgam_comp));
558  const Real gam_km1 = myhalf * (mu_turb(i,j,k-1,hgam_comp) + mu_turb(i-ioff,j-joff,k-1,hgam_comp));
559  const Real gam_kp1 = myhalf * (mu_turb(i,j,k+1,hgam_comp) + mu_turb(i-ioff,j-joff,k+1,hgam_comp));
560  const Real gam_hi = myhalf * (gam_k + gam_kp1); // at k+1/2
561  const Real gam_lo = myhalf * (gam_k + gam_km1); // at k-1/2
562  RHS_a(i,j,k) -= Fact * (rhoAlpha_hi * gam_hi - rhoAlpha_lo * gam_lo);
563  }
564 
565  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"
566  coeffG_a(i,j,k) = c_tmp * inv_b2_tmp; // NOTE: this is now "gamma"
567  } // k
568 
569  // Top boundary coefficients and RHS for L decomp
570  //===================================================
571  {
572  rhoface = myhalf * (cell_data(i,j,khi,Rho_comp) + cell_data(i-ioff,j-joff,khi,Rho_comp));
573  getRhoAlphaForFaces(i, j, khi, ioff, joff, rhoAlpha_lo, rhoAlpha_hi,
574  cell_data, mu_turb, mu_eff,
575  l_consA, l_turb);
576 
577  a_tmp = -Fact * rhoAlpha_lo * dz_inv;
578  c_tmp = zero;
579  if (has_wall_face) { a_tmp *= wall_face(i,j,khi); }
580 
581  RHS_a(i,j,khi) = face_data(i,j,khi); // NOTE: this is momenta; solution is velocity
582  RHS_a(i,j,khi) += Fact * (tau_corr(i,j,khi+1) - tau_corr(i,j,khi));
583 
584  // BCs: Dirichlet (u_i = val), slip wall (w = 0) --
585  // but only where the top of this column really is the domain boundary
586  if (khi != dom_khi) {
587  // Coarse/fine (or grid) boundary: close the system with the known face value
588  // above instead of imposing a physical BC at an interior height
589  c_tmp = -Fact * rhoAlpha_hi * dz_inv;
590  const Real rho_above = myhalf * ( cell_data(i ,j ,khi+1,Rho_comp)
591  + cell_data(i-ioff,j-joff,khi+1,Rho_comp) );
592  RHS_a(i,j,khi) -= c_tmp * (face_data(i,j,khi+1) / rho_above);
593  } else if (ext_dir_on_zhi) {
594  // Wall stress with the one-sided gradient of ComputeStrain_N: c_tmp
595  // multiplies the wall value, and c_1 adds to the coupling with the
596  // cell below
598  c_tmp = Fact * rhoAlpha_hi * dz_inv * wc.c_wall;
599  a_tmp += Fact * rhoAlpha_hi * dz_inv * wc.c_1;
600  const Real rho_wall = myhalf * ( cell_data(i ,j ,khi+1,Rho_comp)
601  + cell_data(i-ioff,j-joff,khi+1,Rho_comp) );
602  const Real wall_velocity = face_data(i,j,khi+1) / rho_wall;
603  RHS_a(i,j,khi) -= c_tmp * wall_velocity;
604  }
605 
606  // Add YSU momentum countergradient correction at top boundary.
607  // NOTE: The lower face at khi is the interior face shared with row khi-1, which
608  // has already taken this flux out of the cell below, so it always carries
609  // it; without it here the flux left the column (erf-model/ERF#4822). The
610  // upper face carries it only at a coarse/fine boundary -- the domain top
611  // is a lid for gamma, as for the scalars.
612  if (use_ysu_mom_countergradient) {
613  const int hgam_comp = (stagdir == 0) ? EddyDiff::HGAMU_v : EddyDiff::HGAMV_v;
614  // Average HGAM* to the staggered face
615  const Real gam_khi = myhalf * (mu_turb(i,j,khi ,hgam_comp) + mu_turb(i-ioff,j-joff,khi ,hgam_comp));
616  const Real gam_km1 = myhalf * (mu_turb(i,j,khi-1,hgam_comp) + mu_turb(i-ioff,j-joff,khi-1,hgam_comp));
617  const Real gam_lo = myhalf * (gam_khi + gam_km1);
618  RHS_a(i,j,khi) += Fact * rhoAlpha_lo * gam_lo;
619  if (khi != dom_khi) {
620  const Real gam_kp1 = myhalf * (mu_turb(i,j,khi+1,hgam_comp) + mu_turb(i-ioff,j-joff,khi+1,hgam_comp));
621  const Real gam_hi = myhalf * (gam_khi + gam_kp1);
622  RHS_a(i,j,khi) -= Fact * rhoAlpha_hi * gam_hi;
623  }
624  }
625 
626  b_tmp = rhoface - a_tmp - c_tmp;
627  inv_b2_tmp = one/ (b_tmp - a_tmp * coeffG_a(i,j,khi-1));
628 
629  // First solve
630  soln_a(i,j,khi) = (RHS_a(i,j,khi) - a_tmp * RHS_a(i,j,khi-1)) * inv_b2_tmp;
631  }
632 
633  // Back sweep the U decomp solution
634  //===================================================
635  for (int k(khi-1); k>=klo; --k) {
636  soln_a(i,j,k) = RHS_a(i,j,k) - coeffG_a(i,j,k) * soln_a(i,j,k+1);
637  }
638 
639  // Convert back to momenta
640  //===================================================
641  for (int k(klo); k<=khi; ++k) {
642  rhoface = myhalf * (cell_data(i,j,k,Rho_comp) + cell_data(i-ioff,j-joff,k,Rho_comp));
643  face_data(i,j,k) = rhoface * soln_a(i,j,k);
644  }
645 
646 #ifdef AMREX_USE_GPU
647  });
648 #else
649  } // i
650  } // j
651 #endif
652 }
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, const int eddy_comp=EddyDiff::Mom_v)
Definition: ERF_GetRhoAlphaForFaces.H:23
#define Rho_comp
Definition: ERF_IndexDefines.H:53
const int khi
Definition: ERF_InitCustomPert_Bubble.H:25
const int klo
Definition: ERF_InitCustomPert_KE.H:9
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_ASSERT_WITH_MESSAGE(wbar_cutoff_min > wbar_cutoff_max, "ERROR: wbar_cutoff_min < wbar_cutoff_max")
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE WallGradCoeffs WallGradientCoeffsUniform() noexcept
Definition: ERF_WallGradientCoeffs.H:46
@ xvel_bc
Definition: ERF_IndexDefines.H:126
@ foextrap
Definition: ERF_IndexDefines.H:339
@ ext_dir
Definition: ERF_IndexDefines.H:340
@ ext_dir_prim
Definition: ERF_IndexDefines.H:343
@ HGAMU_v
Definition: ERF_IndexDefines.H:302
@ HGAMV_v
Definition: ERF_IndexDefines.H:303
Definition: ERF_DiffStruct.H:24
MolecDiffType molec_diff_type
Selected molecular transport model.
Definition: ERF_DiffStruct.H:96
amrex::Real dynamic_viscosity
Dynamic viscosity for momentum diffusion [kg/(m-s)].
Definition: ERF_DiffStruct.H:108
amrex::Real rho0_trans
Reference density used to compute dynamic diffusion coefficients [kg/m3].
Definition: ERF_DiffStruct.H:103
amrex::Vector< TurbChoice > turbChoice
Turbulence options for each AMR level.
Definition: ERF_DataStruct.H:2620
DiffChoice diffChoice
Diffusion-related options.
Definition: ERF_DataStruct.H:2617
Definition: ERF_TurbStruct.H:119
bool use_kturb
Whether any turbulence model is active.
Definition: ERF_TurbStruct.H:977
Definition: ERF_WallGradientCoeffs.H:25
amrex::Real c_wall
Definition: ERF_WallGradientCoeffs.H:26
amrex::Real c_1
Definition: ERF_WallGradientCoeffs.H:28
Here is the call graph for this function:

◆ ImplicitDiffForStateLU_N()

void ImplicitDiffForStateLU_N ( 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 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,
const Array4< const Real > &  wall_face,
const Array4< const Real > &  wall_pin 
)

Function for computing the implicit contribution to the vertical diffusion of theta, with a uniform grid, no terrain, and LU decomposition.

Parameters
[in]bxcell-centered box to loop over
[in]levelAMR level
[in]domainbox of the whole domain
[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]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
50 {
51  // Fraction-stress immersed wall law: wall_face is zero on the bottom face of the wall cell of
52  // each column (z faces), which then carries no diffusive coupling. Empty when not in use.
53  const bool has_wall_face = static_cast<bool>(wall_face);
54  // ... and wall_pin (the TKE solve with erf.if_wall_tke_time_factor = 1) holds the wall value
55  // of the TKE where it is >= 0: that row is a Dirichlet row, like the pinned first cell of flat
56  // ground, and the row above sees it as a Dirichlet neighbour
57  const bool has_wall_pin = static_cast<bool>(wall_pin);
58 
59  BL_PROFILE_VAR("ImplicitDiffForState_N()",ImplicitDiffForState_N);
60 
61  Real dt = static_cast<Real>(dt_d);
62 
63  // setup quantities for getRhoAlpha()
64 #include "ERF_SetupVertDiff.H"
65  const int qty_index = n;
66  const int prim_index = qty_index - 1;
67  const int prim_scal_index = (qty_index >= RhoScalar_comp && qty_index < RhoScalar_comp+NSCALARS) ? PrimScalar_comp : prim_index;
68 
69  // Box bounds
70  int ilo = bx.smallEnd(0);
71  int ihi = bx.bigEnd(0);
72  int jlo = bx.smallEnd(1);
73  int jhi = bx.bigEnd(1);
74  int klo = bx.smallEnd(2);
75  int khi = bx.bigEnd(2);
76  amrex::ignore_unused(ilo, ihi, jlo, jhi);
77 
78  // Temporary FABs for tridiagonal solve (allocated on column)
79  // A[k] * x[k-1] + B[k] * x[k] + C[k+1] = RHS[k]
80 
81  // With LU decomposition, M * x = r is written as L * U * x = r with U * x = rho
82  // We then first have L * rho = r and U * x = rho
83  amrex::FArrayBox RHS_fab, soln_fab, coeffG_fab;
84  RHS_fab.resize(bx,1, amrex::The_Async_Arena());
85  soln_fab.resize(bx,1, amrex::The_Async_Arena());
86  coeffG_fab.resize(bx,1, amrex::The_Async_Arena());
87  auto const& RHS_a = RHS_fab.array();
88  auto const& soln_a = soln_fab.array();
89  auto const& coeffG_a = coeffG_fab.array();
90 
91  Real dz_inv = cellSizeInv[2];
92 
93  // BC types are stored by BC variable, not by state component
94  const int bc_comp = ResolveNativeScalarDiffusionPolicy(qty_index, solverChoice.diffChoice).bc_comp;
95  bool foextrap_on_zlo = (bc_ptr[bc_comp].lo(2) == ERFBCType::foextrap);
96  bool foextrap_on_zhi = (bc_ptr[bc_comp].hi(2) == ERFBCType::foextrap);
97  bool neumann_on_zlo = (bc_ptr[bc_comp].lo(2) == ERFBCType::neumann);
98  bool neumann_on_zhi = (bc_ptr[bc_comp].hi(2) == ERFBCType::neumann);
99  amrex::ignore_unused(foextrap_on_zlo, foextrap_on_zhi);
100 
101  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(foextrap_on_zlo || neumann_on_zlo || use_SurfLayer,
102  "Unexpected lower BC for scalars used with implicit vertical diffusion");
103  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(foextrap_on_zhi || neumann_on_zhi,
104  "Unexpected upper BC for scalars used with implicit vertical diffusion");
105 
106  // k-eqn RANS with a Dirichlet wall value of k: the first cell is held at
107  // the value set by the surface layer, so its row reduces to x(klo) = phi(klo)
108  // and the row above sees it as a Dirichlet neighbour.
109  // NOTE: init_bcs requires zlo.type = surface_layer whenever dirichlet_k is set,
110  // so this matches the l_dirichlet_k pin in erf_slow_rhs_post, which also
111  // requires a surface layer. Do not relax one without the other.
112  const bool pin_klo = (qty_index == RhoKE_comp) && (klo == domain.smallEnd(2)) &&
113  (solverChoice.turbChoice[level].rans_type == RANSType::kEqn) &&
114  solverChoice.turbChoice[level].dirichlet_k;
115 
116  // A column whose bottom or top is not the domain boundary ends at a coarse/fine boundary
117  // instead. There the system must be closed with the value on the other side of that
118  // boundary rather than given a physical BC row at an interior height, so that the answer
119  // does not depend on where the grid happens to end in the vertical.
120  const bool at_zlo = (klo == domain.smallEnd(2));
121  const bool at_zhi = (khi == domain.bigEnd(2));
122 
123  Real Fact = implicit_fac * dt * dz_inv;
124 
125 #ifdef AMREX_USE_GPU
126  ParallelFor(makeSlab(bx,2,0), [=] AMREX_GPU_DEVICE (int i, int j, int)
127  {
128 #else
129  for (int j(jlo); j<=jhi; ++j) {
130  for (int i(ilo); i<=ihi; ++i) {
131 #endif
132  // Bottom boundary coefficients and RHS for L decomp
133  //===================================================
134  Real rhoAlpha_lo, rhoAlpha_hi;
135  Real a_tmp, b_tmp, c_tmp, inv_b2_tmp;
136  {
137  getRhoAlpha(i, j, klo, rhoAlpha_lo, rhoAlpha_hi,
138  cell_data, mu_turb, d_alpha_eff, d_eddy_diff_idz,
139  prim_scal_index, l_consA, l_turb);
140 
141  a_tmp = zero;
142  if (!at_zlo) { a_tmp = -Fact * rhoAlpha_lo * dz_inv; }
143  c_tmp = (pin_klo) ? zero : -Fact * rhoAlpha_hi * dz_inv;
144  if (has_wall_face) {
145  a_tmp *= wall_face(i,j,klo);
146  c_tmp *= wall_face(i,j,klo+1);
147  }
148  const bool pin_here_lo = has_wall_pin && (wall_pin(i,j,klo) >= zero);
149  if (pin_here_lo) { a_tmp = zero; c_tmp = zero; }
150  b_tmp = cell_data(i,j,klo,Rho_comp) - a_tmp - c_tmp;
151  inv_b2_tmp = one;
152 
153  RHS_a(i,j,klo) = cell_data(i,j,klo,n); // NOTE: this is rho*phi; solution is phi
154  if (pin_here_lo) {
155  // Dirichlet row: no flux terms
156  } else if (!at_zlo) {
157  // Coarse/fine boundary: close the system with the known value below
158  RHS_a(i,j,klo) -= a_tmp * (cell_data(i,j,klo-1,n) / cell_data(i,j,klo-1,Rho_comp));
159  } else if (pin_klo) {
160  // Dirichlet row: no flux terms
161  } else if (use_SurfLayer && scalar_zflux && !(has_wall_face && wall_face(i,j,klo) == zero)) {
162  // (a wall cell at klo takes its wall flux from the source, not from the surface layer)
163  RHS_a(i,j,klo) += Fact * scalar_zflux(i,j,klo); // NOTE: scalar_zflux = -K*d_z(\phi)
164  } else if (neumann_on_zlo) {
165  RHS_a(i,j,klo) += -Fact * rhoAlpha_lo * bc_neumann_vals[2]; // NOTE: N_val = d_z(\phi)
166  }
167 
168  // Add countergradient correction to RHS at bottom boundary.
169  // NOTE: The lower face at klo carries no countergradient flux -- the
170  // total surface flux is supplied by the surface layer model or the
171  // Neumann BC, while gamma represents nonlocal transport interior to
172  // the PBL. Only the upper face contributes here.
173  if (use_mrf_countergradient && (n == RhoTheta_comp || n == RhoQ1_comp)) {
174  const int gam_comp = (n == RhoTheta_comp) ? EddyDiff::HGAMT_v : EddyDiff::HGAMQ_v;
175  const Real gam_hi = myhalf * (mu_turb(i, j, klo, gam_comp) + mu_turb(i, j, klo+1, gam_comp));
176  RHS_a(i,j,klo) -= Fact * rhoAlpha_hi * gam_hi;
177  if (!at_zlo) {
178  const Real gam_lo = myhalf * (mu_turb(i, j, klo, gam_comp) + mu_turb(i, j, klo-1, gam_comp));
179  RHS_a(i,j,klo) += Fact * rhoAlpha_lo * gam_lo;
180  }
181  }
182 
183  RHS_a(i,j,klo) /= b_tmp; // NOTE: this is now "rho"
184  coeffG_a(i,j,klo) = c_tmp / b_tmp; // NOTE: this is now "gamma"
185  }
186 
187  // Build the coefficients and RHS for L decomp
188  //===================================================
189  for (int k(klo+1); k < khi; k++) {
190  getRhoAlpha(i, j, k, rhoAlpha_lo, rhoAlpha_hi,
191  cell_data, mu_turb, d_alpha_eff, d_eddy_diff_idz,
192  prim_scal_index, l_consA, l_turb);
193 
194  a_tmp = -Fact * rhoAlpha_lo * dz_inv;
195  c_tmp = -Fact * rhoAlpha_hi * dz_inv;
196  if (has_wall_face) {
197  a_tmp *= wall_face(i,j,k);
198  c_tmp *= wall_face(i,j,k+1);
199  }
200  const bool pin_here = has_wall_pin && (wall_pin(i,j,k) >= zero);
201  if (pin_here) { a_tmp = zero; c_tmp = zero; }
202  b_tmp = cell_data(i,j,k,Rho_comp) - a_tmp - c_tmp;
203  inv_b2_tmp = one / (b_tmp - a_tmp * coeffG_a(i,j,k-1));
204 
205  RHS_a(i,j,k) = cell_data(i,j,k,n); // NOTE: this is rho*phi; solution is phi
206 
207  // Add countergradient correction to RHS in interior
208  if (use_mrf_countergradient && (n == RhoTheta_comp || n == RhoQ1_comp)) {
209  const int gam_comp = (n == RhoTheta_comp) ? EddyDiff::HGAMT_v : EddyDiff::HGAMQ_v;
210  const Real gam_k = mu_turb(i, j, k, gam_comp);
211  const Real gam_km1 = mu_turb(i, j, k-1, gam_comp);
212  const Real gam_kp1 = mu_turb(i, j, k+1, gam_comp);
213  const Real gam_hi = myhalf * (gam_k + gam_kp1); // at k+½
214  const Real gam_lo = myhalf * (gam_k + gam_km1); // at k-½
215  // Countergradient flux divergence (implicit contribution to RHS):
216  // -Fact * [ρα_{k+½}·γ_{k+½} - ρα_{k-½}·γ_{k-½}]
217  RHS_a(i,j,k) -= Fact * (rhoAlpha_hi * gam_hi - rhoAlpha_lo * gam_lo);
218  }
219 
220  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"
221  coeffG_a(i,j,k) = c_tmp * inv_b2_tmp; // NOTE: this is now "gamma"
222  } // k
223 
224  // Top boundary coefficients and RHS for L decomp
225  //===================================================
226  {
227  getRhoAlpha(i, j, khi, rhoAlpha_lo, rhoAlpha_hi,
228  cell_data, mu_turb, d_alpha_eff, d_eddy_diff_idz,
229  prim_scal_index, l_consA, l_turb);
230 
231  a_tmp = -Fact * rhoAlpha_lo * dz_inv;
232  c_tmp = zero;
233  if (!at_zhi) { c_tmp = -Fact * rhoAlpha_hi * dz_inv; }
234  if (has_wall_face) {
235  a_tmp *= wall_face(i,j,khi);
236  if (!at_zhi) { c_tmp *= wall_face(i,j,khi+1); }
237  }
238  b_tmp = cell_data(i,j,khi,Rho_comp) - a_tmp - c_tmp;
239  inv_b2_tmp = one / (b_tmp - a_tmp * coeffG_a(i,j,khi-1));
240 
241  RHS_a(i,j,khi) = cell_data(i,j,khi,n); // NOTE: this is rho*phi; solution is phi
242  if (!at_zhi) {
243  // Coarse/fine boundary: close the system with the known value above
244  RHS_a(i,j,khi) -= c_tmp * (cell_data(i,j,khi+1,n) / cell_data(i,j,khi+1,Rho_comp));
245  } else if (neumann_on_zhi) {
246  RHS_a(i,j,khi) -= -Fact * rhoAlpha_hi * bc_neumann_vals[5]; // NOTE: N_val = d_z(\phi)
247  }
248 
249  // Add countergradient correction to RHS at top boundary.
250  // NOTE: The lower face at khi always carries the countergradient flux, which
251  // row khi-1 has already taken out of the cell below; without it here
252  // that flux would leave the column. The upper face carries it only at
253  // a coarse/fine boundary -- the domain top is a lid for gamma.
254  if (use_mrf_countergradient && (n == RhoTheta_comp || n == RhoQ1_comp)) {
255  const int gam_comp = (n == RhoTheta_comp) ? EddyDiff::HGAMT_v : EddyDiff::HGAMQ_v;
256  const Real gam_lo = myhalf * (mu_turb(i, j, khi, gam_comp) + mu_turb(i, j, khi-1, gam_comp));
257  RHS_a(i,j,khi) += Fact * rhoAlpha_lo * gam_lo;
258  if (!at_zhi) {
259  const Real gam_hi = myhalf * (mu_turb(i, j, khi, gam_comp) + mu_turb(i, j, khi+1, gam_comp));
260  RHS_a(i,j,khi) -= Fact * rhoAlpha_hi * gam_hi;
261  }
262  }
263 
264  // First solve
265  soln_a(i,j,khi) = (RHS_a(i,j,khi) - a_tmp * RHS_a(i,j,khi-1)) * inv_b2_tmp;
266  }
267 
268  // Back sweep the U decomp solution
269  //===================================================
270  for (int k(khi-1); k>=klo; --k) {
271  soln_a(i,j,k) = RHS_a(i,j,k) - coeffG_a(i,j,k) * soln_a(i,j,k+1);
272  }
273 
274  // Convert back to rho*theta
275  //===================================================
276  for (int k(klo); k<=khi; ++k) {
277  cell_data(i,j,k,n) = cell_data(i,j,k,Rho_comp) * soln_a(i,j,k);
278  }
279 
280 #ifdef AMREX_USE_GPU
281  });
282 #else
283  } // i
284  } // j
285 #endif
286 }
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_scal_index, bool l_consA, bool l_turb)
Definition: ERF_GetRhoAlpha.H:20
#define RhoScalar_comp
Definition: ERF_IndexDefines.H:57
#define RhoTheta_comp
Definition: ERF_IndexDefines.H:54
#define NSCALARS
Definition: ERF_IndexDefines.H:26
#define RhoQ1_comp
Definition: ERF_IndexDefines.H:66
#define PrimScalar_comp
Definition: ERF_IndexDefines.H:81
#define RhoKE_comp
Definition: ERF_IndexDefines.H:55
AMREX_ALWAYS_ASSERT_WITH_MESSAGE(axis >=0 &&axis< AMREX_SPACEDIM, "prob.axis must be 0, 1 or 2")
NativeScalarDiffusionPolicy ResolveNativeScalarDiffusionPolicy(const int qty_comp, const DiffChoice &choice)
Definition: ERF_NativeScalarDiffusion.H:26
@ neumann
Definition: ERF_IndexDefines.H:345
@ HGAMQ_v
Definition: ERF_IndexDefines.H:301
@ HGAMT_v
Definition: ERF_IndexDefines.H:300
int bc_comp
Definition: ERF_NativeScalarDiffusion.H:16
Here is the call graph for this function:

◆ ImplicitVerticalFluxForReflux_N()

void ImplicitVerticalFluxForReflux_N ( const Box &  bx,
const Box &  domain,
const int  level,
const int  n,
const Real  implicit_fac,
const Array4< const Real > &  cell_data,
const GpuArray< Real, AMREX_SPACEDIM > &  cellSizeInv,
const Array4< const Real > &  mu_turb,
const SolverChoice &  solverChoice,
const Array4< const Real > &  mf_mx,
const Array4< const Real > &  mf_my,
const bool  use_mrf_countergradient,
const Array4< Real > &  zflux 
)

The implicit part of the vertical diffusive flux of component n on the z faces of bx, as the matching ImplicitDiffForStateLU_N solve applied it, for refluxing: in the convention of the advective fluxes the flux registers hold (see AddScalarDiffusionRefluxFluxes_N), i.e. the vertical transfer divided by m_x m_y.

cell_data must hold the solution of that solve, including the ghost values a column that ends at a coarse/fine boundary was closed with. Faces on the domain boundary are set to zero: they are never refluxed.

Parameters
[in]bxcell-centered box
[in]domainbox of the whole domain
[in]levelAMR level
[in]nconserved component index
[in]implicit_facthe vert_implicit_fac the solve used
[in]cell_dataconserved state after the solve
[in]use_mrf_countergradientwhether the solve added the MRF countergradient flux
[out]zfluxface flux of component n, in component 0
710 {
711 #include "ERF_SetupVertDiff.H"
712  const int qty_index = n;
713  const int prim_index = qty_index - 1;
714  const int prim_scal_index = (qty_index >= RhoScalar_comp && qty_index < RhoScalar_comp+NSCALARS) ? PrimScalar_comp : prim_index;
715  const int khi = bx.bigEnd(2);
716  const Real dz_inv = cellSizeInv[2];
717 
718  ParallelFor(surroundingNodes(bx,2), [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
719  {
720  if (k == dom_lo.z || k == dom_hi.z+1) { zflux(i,j,k) = zero; return; }
721  // The coefficient on this face, from the cell above it (or below, at the top of bx)
722  Real rhoAlpha_lo, rhoAlpha_hi;
723  const int kc = (k <= khi) ? k : k-1;
724  getRhoAlpha(i, j, kc, rhoAlpha_lo, rhoAlpha_hi, cell_data, mu_turb, d_alpha_eff, d_eddy_diff_idz,
725  prim_scal_index, l_consA, l_turb);
726  const Real rhoAlpha = (k <= khi) ? rhoAlpha_lo : rhoAlpha_hi;
727  const Real phi_hi = cell_data(i,j,k ,n) / cell_data(i,j,k ,Rho_comp);
728  const Real phi_lo = cell_data(i,j,k-1,n) / cell_data(i,j,k-1,Rho_comp);
729  Real G = -implicit_fac * rhoAlpha * dz_inv * (phi_hi - phi_lo);
730  // The countergradient flux the solve adds through the face, fac rho*alpha gamma
731  if (use_mrf_countergradient) {
732  const int gam_comp = (n == RhoTheta_comp) ? EddyDiff::HGAMT_v : EddyDiff::HGAMQ_v;
733  G += implicit_fac * rhoAlpha * myhalf * (mu_turb(i,j,k,gam_comp) + mu_turb(i,j,k-1,gam_comp));
734  }
735  zflux(i,j,k) = G / (mf_mx(i,j,0) * mf_my(i,j,0));
736  });
737 }
Here is the call graph for this function: