ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
ERF_ComputeTurbulentViscosity.cpp File Reference
#include "ERF_SurfaceLayer.H"
#include "ERF_Constants.H"
#include "ERF_EddyViscosity.H"
#include "ERF_RANSClosure.H"
#include "ERF_Diffusion.H"
#include "ERF_PBLModels.H"
#include "ERF_TileNoZ.H"
#include "ERF_TerrainMetrics.H"
#include "ERF_TerrainDiffusionLimits.H"
#include "ERF_MoistUtils.H"
#include "ERF_RichardsonNumber.H"
Include dependency graph for ERF_ComputeTurbulentViscosity.cpp:

Functions

void ComputeTurbulentViscosityLES (Vector< std::unique_ptr< MultiFab >> &Tau_lev, const MultiFab &cons_in, MultiFab &eddyViscosity, MultiFab &Diss, const Geometry &geom, bool use_terrain_fitted_coords, Vector< std::unique_ptr< MultiFab >> &mapfac, const std::unique_ptr< MultiFab > &z_phys_nd, const TurbChoice &turbChoice, const Real const_grav, std::unique_ptr< SurfaceLayer > &, const MoistureComponentIndices &moisture_indices, const MultiFab *xvel, const MultiFab *yvel)
 
void ComputeTurbulentViscosityLES_EB (Vector< std::unique_ptr< MultiFab >> &Tau_lev, const MultiFab &cons_in, MultiFab &eddyViscosity, MultiFab &Hfx1, MultiFab &Hfx2, MultiFab &Hfx3, const Geometry &geom, Vector< std::unique_ptr< MultiFab >> &mapfac, const TurbChoice &turbChoice, const Real const_grav, [[maybe_unused]] const SolverChoice &solverChoice, std::unique_ptr< SurfaceLayer > &, const MoistureComponentIndices &moisture_indices, const eb_ &ebfact, const MultiFab *xvel, const MultiFab *yvel)
 
void ComputeTurbulentViscosityRANS (int level, const MultiFab &cons_in, const MultiFab &wdist, MultiFab &eddyViscosity, MultiFab &Diss, const Geometry &geom, bool use_terrain_fitted_coords, const std::unique_ptr< MultiFab > &z_phys_nd, const TurbChoice &turbChoice, const Real const_grav, std::unique_ptr< SurfaceLayer > &SurfLayer, const MultiFab *z_0)
 
void ComputeTurbulentViscosity (double dt, const MultiFab &xvel, const MultiFab &yvel, Vector< std::unique_ptr< MultiFab >> &Tau_lev, MultiFab &cons_in, const MultiFab &wdist, MultiFab &eddyViscosity, MultiFab &Hfx1, MultiFab &Hfx2, MultiFab &Hfx3, MultiFab &Diss, const Geometry &geom, Vector< std::unique_ptr< MultiFab >> &mapfac, const std::unique_ptr< MultiFab > &z_phys_nd, const std::unique_ptr< MultiFab > &z_phys_cc, const SolverChoice &solverChoice, std::unique_ptr< SurfaceLayer > &SurfLayer, const MultiFab *z_0, const bool &use_terrain_fitted_coords, const bool &use_moisture, int level, const BCRec *bc_ptr, const eb_ &ebfact, bool vert_only, const MultiFab *qheating_rates, const MultiFab *terrain_blank)
 

Function Documentation

◆ ComputeTurbulentViscosity()

void ComputeTurbulentViscosity ( double  dt,
const MultiFab &  xvel,
const MultiFab &  yvel,
Vector< std::unique_ptr< MultiFab >> &  Tau_lev,
MultiFab &  cons_in,
const MultiFab &  wdist,
MultiFab &  eddyViscosity,
MultiFab &  Hfx1,
MultiFab &  Hfx2,
MultiFab &  Hfx3,
MultiFab &  Diss,
const Geometry &  geom,
Vector< std::unique_ptr< MultiFab >> &  mapfac,
const std::unique_ptr< MultiFab > &  z_phys_nd,
const std::unique_ptr< MultiFab > &  z_phys_cc,
const SolverChoice &  solverChoice,
std::unique_ptr< SurfaceLayer > &  SurfLayer,
const MultiFab *  z_0,
const bool &  use_terrain_fitted_coords,
const bool &  use_moisture,
int  level,
const BCRec *  bc_ptr,
const eb_ &  ebfact,
bool  vert_only,
const MultiFab *  qheating_rates,
const MultiFab *  terrain_blank 
)

Wrapper to compute turbulent viscosity with LES or PBL.

Parameters
[in]dttime step
[in]xvelvelocity in x-dir
[in]yvelvelocity in y-dir
[in]Tau_levstrain at this level
[in]cons_incell center conserved quantities
[in]wdistwall distance
[out]eddyViscosityturbulent viscosity
[out]Hfx1heat flux in x-dir
[out]Hfx2heat flux in y-dir
[out]Hfx3heat flux in z-dir
[out]Dissdissipation of turbulent kinetic energy
[in]geomproblem geometry
[in]mapfacmap factors
[in]z_phys_ndnodal physical z coordinates
[in]z_phys_cccell-centered physical z coordinates
[in]solverChoicecontainer with solver, diffusion, and turbulence parameters
[in]SurfLayeroptional surface-layer model
[in]z_0roughness length
[in]use_terrain_fitted_coordsflag for terrain-fitted coordinates
[in]use_moistureflag for moisture physics
[in]levelAMR level
[in]bc_ptrboundary condition records
[in]ebfactEB factories for cell- and face-centered variables
[in]vert_onlyflag for vertical components of eddyViscosity
[in]qheating_ratesradiation heating rates (SW, LW components)
872 {
873  BL_PROFILE_VAR("ComputeTurbulentViscosity()",ComputeTurbulentViscosity);
874  //
875  // In LES mode, the turbulent viscosity is isotropic (unless mix_isotropic is set to false), so
876  // the LES model sets both horizontal and vertical viscosities
877  //
878  // In PBL mode, the primary purpose of the PBL model is to control vertical transport, so the PBL model sets the vertical viscosity.
879  // Optionally, the PBL model can be run in conjunction with an LES model that sets the horizontal viscosity
880  // (this isn’t truly LES, but the model form is the same as Smagorinsky).
881  //
882  // ComputeTurbulentViscosityLES populates the LES viscosity for both horizontal and vertical components.
883  // ComputeTurbulentViscosityPBL computes the PBL viscosity just for the vertical component.
884  //
885 
886  TurbChoice turbChoice = solverChoice.turbChoice[level];
887  const Real const_grav = solverChoice.gravity;
888 
889  if (!SurfLayer) {
890  AMREX_ALWAYS_ASSERT(!vert_only);
891  }
892 
893  bool impose_phys_bcs = true;
894 
895  if (turbChoice.les_type != LESType::None) {
896  if (solverChoice.terrain_type == TerrainType::EB) {
898  cons_in, eddyViscosity,
899  Hfx1, Hfx2, Hfx3,
900  geom,
901  mapfac, turbChoice, const_grav,
902  solverChoice,
903  SurfLayer, solverChoice.moisture_indices,
904  ebfact, &xvel, &yvel);
905  } else {
907  cons_in, eddyViscosity,
908  Diss,
909  geom, use_terrain_fitted_coords,
910  mapfac, z_phys_nd, turbChoice, const_grav,
911  SurfLayer, solverChoice.moisture_indices,
912  &xvel, &yvel);
913  }
914  }
915 
916  if (turbChoice.rans_type != RANSType::None) {
917  ComputeTurbulentViscosityRANS(level, cons_in, wdist,
918  eddyViscosity,
919  Diss,
920  geom, use_terrain_fitted_coords,
921  z_phys_nd, turbChoice, const_grav,
922  SurfLayer, z_0);
923  }
924 
925  if (turbChoice.pbl_type == PBLType::MYJ) {
926  ComputeDiffusivityMYJ(dt, xvel, yvel, cons_in, eddyViscosity,
927  geom, turbChoice, SurfLayer,
928  use_terrain_fitted_coords, use_moisture,
929  level, bc_ptr, vert_only, z_phys_nd, z_phys_cc,
930  solverChoice.moisture_indices);
931  } else if (turbChoice.pbl_type == PBLType::MYNN25) {
932  ComputeDiffusivityMYNN25(xvel, yvel, cons_in, eddyViscosity,
933  geom, turbChoice, SurfLayer,
934  use_terrain_fitted_coords, use_moisture,
935  level, bc_ptr, vert_only, z_phys_nd, z_phys_cc,
936  solverChoice.moisture_indices);
937  } else if (turbChoice.pbl_type == PBLType::MYNNEDMF) {
938  ComputeDiffusivityMYNNEDMF(xvel, yvel, cons_in, eddyViscosity,
939  geom, turbChoice, SurfLayer,
940  use_terrain_fitted_coords, use_moisture,
941  level, bc_ptr, vert_only, z_phys_nd, z_phys_cc,
942  solverChoice.moisture_indices);
943  } else if (turbChoice.pbl_type == PBLType::YSU) {
944  ComputeDiffusivityYSU(xvel, yvel, cons_in, eddyViscosity,
945  geom, turbChoice, SurfLayer,
946  use_terrain_fitted_coords, use_moisture,
947  level, bc_ptr, vert_only, z_phys_nd, z_phys_cc,
948  solverChoice.moisture_indices);
949  } else if (turbChoice.pbl_type == PBLType::MRF) {
950  ComputeDiffusivityMRF(xvel, yvel, cons_in, eddyViscosity,
951  geom, turbChoice, SurfLayer,
952  use_terrain_fitted_coords, use_moisture,
953  level, bc_ptr, vert_only, z_phys_nd, z_phys_cc,
954  solverChoice.moisture_indices, terrain_blank);
955  } else if (turbChoice.pbl_type == PBLType::YSUNew) {
956  ComputeDiffusivityYSUNew(xvel, yvel, cons_in, eddyViscosity,
957  geom, turbChoice, SurfLayer,
958  use_terrain_fitted_coords, use_moisture,
959  level, bc_ptr, vert_only, z_phys_nd, z_phys_cc,
960  solverChoice.moisture_indices,
961  qheating_rates, terrain_blank);
962  } else if (turbChoice.uses_shoc_family()) {
963  // NOTE: Nothing to do here. The SHOC class handles setting the vertical
964  // components of eddyDiffs in slow RHS pre.
965  }
966 
967  //
968  // At all levels we need to fill values outside the physical boundary for the LES coeffs.
969  // In addition, for all cases, if at level > 0, we want to fill fine ghost cell values that
970  // overlie coarse grid cells (and that are not in another fine valid region) with
971  // extrapolated values from the interior, rather than interpolating from the coarser level,
972  // since we may be using a different turbulence model there.
973  //
974  // Note: here "covered" refers to "covered by valid region of another grid at this level"
975  // Note: here "physbnd" refers to "cells outside the domain if not periodic"
976  // Note: here "interior" refers to "valid cells, i.e. inside 'my' grid"
977  //
978  {
979  int is_covered = 0;
980  int is_notcovered = 1;
981  int is_physbnd = 2;
982  int is_interior = 3;
983  iMultiFab cc_mask(eddyViscosity.boxArray(),eddyViscosity.DistributionMap(),1,1);
984  cc_mask.BuildMask(geom.Domain(), geom.periodicity(), is_covered, is_notcovered, is_physbnd, is_interior);
985 
986  Box domain = geom.Domain();
987  for (int i = 0; i < AMREX_SPACEDIM; ++i) {
988  if (geom.isPeriodic(i)) {
989  domain.grow(i,1);
990  }
991  }
992 
993  eddyViscosity.FillBoundary(geom.periodicity());
994 
995  int ncomp = eddyViscosity.nComp();
996 
997 #ifdef _OPENMP
998 #pragma omp parallel if (Gpu::notInLaunchRegion())
999 #endif
1000  for (MFIter mfi(eddyViscosity); mfi.isValid(); ++mfi)
1001  {
1002  Box vbx = mfi.validbox();
1003 
1004  Box planex_lo = mfi.growntilebox(1); planex_lo.setBig(0, vbx.smallEnd(0)-1);
1005  Box planey_lo = mfi.growntilebox(1); planey_lo.setBig(1, vbx.smallEnd(1)-1);
1006  Box planez_lo = mfi.growntilebox(1); planez_lo.setBig(2, vbx.smallEnd(2)-1);
1007 
1008  Box planex_hi = mfi.growntilebox(1); planex_hi.setSmall(0, vbx.bigEnd(0)+1);
1009  Box planey_hi = mfi.growntilebox(1); planey_hi.setSmall(1, vbx.bigEnd(1)+1);
1010  Box planez_hi = mfi.growntilebox(1); planez_hi.setSmall(2, vbx.bigEnd(2)+1);
1011 
1012  int i_lo = vbx.smallEnd(0); int i_hi = vbx.bigEnd(0);
1013  int j_lo = vbx.smallEnd(1); int j_hi = vbx.bigEnd(1);
1014  int k_lo = vbx.smallEnd(2); int k_hi = vbx.bigEnd(2);
1015 
1016  const Array4<Real>& mu_turb = eddyViscosity.array(mfi);
1017  const Array4<int>& mask_arr = cc_mask.array(mfi);
1018 
1019  auto domlo = lbound(domain);
1020  auto domhi = ubound(domain);
1021 
1022  ParallelFor(planex_lo, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
1023  {
1024  int lj = amrex::min(amrex::max(j, domlo.y), domhi.y);
1025  int lk = amrex::min(amrex::max(k, domlo.z), domhi.z);
1026  if ((mask_arr(i,j,k) == is_notcovered && mask_arr(i_lo,lj,lk) != is_notcovered) ||
1027  (mask_arr(i,j,k) == is_physbnd && i < domlo.x && impose_phys_bcs)) {
1028  for (int n = 0; n < ncomp; n++) {
1029  mu_turb(i,j,k,n) = mu_turb(i_lo,lj,lk,n);
1030  }
1031  }
1032  });
1033  ParallelFor(planex_hi, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
1034  {
1035  int lj = amrex::min(amrex::max(j, domlo.y), domhi.y);
1036  int lk = amrex::min(amrex::max(k, domlo.z), domhi.z);
1037  if ((mask_arr(i,j,k) == is_notcovered && mask_arr(i_hi,lj,lk) != is_notcovered) ||
1038  (mask_arr(i,j,k) == is_physbnd && i > domhi.x && impose_phys_bcs)) {
1039  for (int n = 0; n < ncomp; n++) {
1040  mu_turb(i,j,k,n) = mu_turb(i_hi,lj,lk,n);
1041  }
1042  }
1043  });
1044  ParallelFor(planey_lo, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
1045  {
1046  int lk = amrex::min(amrex::max(k, domlo.z), domhi.z);
1047  if ((mask_arr(i,j,k) == is_notcovered && mask_arr(i,j_lo,lk) != is_notcovered) ||
1048  (mask_arr(i,j,k) == is_physbnd && j < domlo.y && impose_phys_bcs)) {
1049  for (int n = 0; n < ncomp; n++) {
1050  mu_turb(i,j,k,n) = mu_turb(i,j_lo,lk,n);
1051  }
1052  }
1053  });
1054  ParallelFor(planey_hi, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
1055  {
1056  int lk = amrex::min(amrex::max(k, domlo.z), domhi.z);
1057  if ((mask_arr(i,j,k) == is_notcovered && mask_arr(i,j_hi,lk) != is_notcovered)||
1058  (mask_arr(i,j,k) == is_physbnd && j > domhi.y && impose_phys_bcs)) {
1059  for (int n = 0; n < ncomp; n++) {
1060  mu_turb(i,j,k,n) = mu_turb(i,j_hi,lk,n);
1061  }
1062  }
1063  });
1064  ParallelFor(planez_lo, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
1065  {
1066  if ((mask_arr(i,j,k) == is_notcovered && mask_arr(i,j,k_lo) != is_notcovered) ||
1067  (mask_arr(i,j,k) == is_physbnd && k < domlo.z && impose_phys_bcs)) {
1068  for (int n = 0; n < ncomp; n++) {
1069  mu_turb(i,j,k,n) = mu_turb(i,j,k_lo,n);
1070  }
1071  }
1072  });
1073  ParallelFor(planez_hi, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
1074  {
1075  if ((mask_arr(i,j,k) == is_notcovered && mask_arr(i,j,k_hi) != is_notcovered) ||
1076  (mask_arr(i,j,k) == is_physbnd && k > domhi.z && impose_phys_bcs)) {
1077  for (int n = 0; n < ncomp; n++) {
1078  mu_turb(i,j,k,n) = mu_turb(i,j,k_hi,n);
1079  }
1080  }
1081  });
1082  } // mfi
1083 
1084  eddyViscosity.FillBoundary(geom.periodicity());
1085  }
1086 }
void ComputeDiffusivityMRF(const MultiFab &xvel, const MultiFab &yvel, const MultiFab &cons_in, MultiFab &eddyViscosity, const Geometry &geom, const TurbChoice &turbChoice, std::unique_ptr< SurfaceLayer > &SurfLayer, bool use_terrain_fitted_coords, bool use_moisture, int level, const BCRec *bc_ptr, bool, const std::unique_ptr< MultiFab > &z_phys_nd, const std::unique_ptr< MultiFab > &z_phys_cc, const MoistureComponentIndices &moisture_indices, const MultiFab *terrain_blank)
Definition: ERF_ComputeDiffusivityMRF.cpp:34
void ComputeDiffusivityMYJ(double dt, const MultiFab &xvel, const MultiFab &yvel, MultiFab &cons_in, MultiFab &eddyViscosity, const Geometry &geom, const TurbChoice &turbChoice, std::unique_ptr< SurfaceLayer > &SurfLayer, bool use_terrain_fitted_coords, bool use_moisture, int level, const BCRec *bc_ptr, bool, const std::unique_ptr< MultiFab > &z_phys_nd, const std::unique_ptr< MultiFab > &z_phys_cc, const MoistureComponentIndices &moisture_indices)
Definition: ERF_ComputeDiffusivityMYJ.cpp:33
void ComputeDiffusivityMYNN25(const MultiFab &xvel, const MultiFab &yvel, const MultiFab &cons_in, MultiFab &eddyViscosity, const Geometry &geom, const TurbChoice &turbChoice, std::unique_ptr< SurfaceLayer > &SurfLayer, bool use_terrain_fitted_coords, bool use_moisture, int level, const BCRec *bc_ptr, bool, const std::unique_ptr< MultiFab > &z_phys_nd, const std::unique_ptr< MultiFab > &z_phys_cc, const MoistureComponentIndices &moisture_indices)
Definition: ERF_ComputeDiffusivityMYNN25.cpp:31
void ComputeDiffusivityMYNNEDMF(const MultiFab &xvel, const MultiFab &yvel, const MultiFab &cons_in, MultiFab &eddyViscosity, const Geometry &geom, const TurbChoice &turbChoice, std::unique_ptr< SurfaceLayer > &SurfLayer, bool use_terrain_fitted_coords, bool use_moisture, int level, const BCRec *bc_ptr, bool, const std::unique_ptr< MultiFab > &z_phys_nd, const std::unique_ptr< MultiFab > &z_phys_cc, const MoistureComponentIndices &moisture_indices)
Compute eddy viscosity and diffusivity coefficients using the MYNN-EDMF closure.
Definition: ERF_ComputeDiffusivityMYNNEDMF.cpp:4194
void ComputeDiffusivityYSUNew(const MultiFab &xvel, const MultiFab &yvel, const MultiFab &cons_in, MultiFab &eddyViscosity, const Geometry &geom, const TurbChoice &turbChoice, std::unique_ptr< SurfaceLayer > &SurfLayer, bool use_terrain_fitted_coords, bool use_moisture, int level, const BCRec *bc_ptr, bool, const std::unique_ptr< MultiFab > &z_phys_nd, const std::unique_ptr< MultiFab > &z_phys_cc, const MoistureComponentIndices &moisture_indices, const MultiFab *qheating_rates, const MultiFab *terrain_blank)
Definition: ERF_ComputeDiffusivityYSUNew.cpp:43
void ComputeDiffusivityYSU(const MultiFab &xvel, const MultiFab &yvel, const MultiFab &cons_in, MultiFab &eddyViscosity, const Geometry &geom, const TurbChoice &turbChoice, std::unique_ptr< SurfaceLayer > &SurfLayer, bool use_terrain_fitted_coords, bool use_moisture, int level, const BCRec *bc_ptr, bool, const std::unique_ptr< MultiFab > &z_phys_nd, const std::unique_ptr< MultiFab > &z_phys_cc, const MoistureComponentIndices &moisture_indices)
Definition: ERF_ComputeDiffusivityYSU.cpp:31
void ComputeTurbulentViscosityLES_EB(Vector< std::unique_ptr< MultiFab >> &Tau_lev, const MultiFab &cons_in, MultiFab &eddyViscosity, MultiFab &Hfx1, MultiFab &Hfx2, MultiFab &Hfx3, const Geometry &geom, Vector< std::unique_ptr< MultiFab >> &mapfac, const TurbChoice &turbChoice, const Real const_grav, [[maybe_unused]] const SolverChoice &solverChoice, std::unique_ptr< SurfaceLayer > &, const MoistureComponentIndices &moisture_indices, const eb_ &ebfact, const MultiFab *xvel, const MultiFab *yvel)
Definition: ERF_ComputeTurbulentViscosity.cpp:359
void ComputeTurbulentViscosity(double dt, const MultiFab &xvel, const MultiFab &yvel, Vector< std::unique_ptr< MultiFab >> &Tau_lev, MultiFab &cons_in, const MultiFab &wdist, MultiFab &eddyViscosity, MultiFab &Hfx1, MultiFab &Hfx2, MultiFab &Hfx3, MultiFab &Diss, const Geometry &geom, Vector< std::unique_ptr< MultiFab >> &mapfac, const std::unique_ptr< MultiFab > &z_phys_nd, const std::unique_ptr< MultiFab > &z_phys_cc, const SolverChoice &solverChoice, std::unique_ptr< SurfaceLayer > &SurfLayer, const MultiFab *z_0, const bool &use_terrain_fitted_coords, const bool &use_moisture, int level, const BCRec *bc_ptr, const eb_ &ebfact, bool vert_only, const MultiFab *qheating_rates, const MultiFab *terrain_blank)
Definition: ERF_ComputeTurbulentViscosity.cpp:850
void ComputeTurbulentViscosityRANS(int level, const MultiFab &cons_in, const MultiFab &wdist, MultiFab &eddyViscosity, MultiFab &Diss, const Geometry &geom, bool use_terrain_fitted_coords, const std::unique_ptr< MultiFab > &z_phys_nd, const TurbChoice &turbChoice, const Real const_grav, std::unique_ptr< SurfaceLayer > &SurfLayer, const MultiFab *z_0)
Definition: ERF_ComputeTurbulentViscosity.cpp:618
void ComputeTurbulentViscosityLES(Vector< std::unique_ptr< MultiFab >> &Tau_lev, const MultiFab &cons_in, MultiFab &eddyViscosity, MultiFab &Diss, const Geometry &geom, bool use_terrain_fitted_coords, Vector< std::unique_ptr< MultiFab >> &mapfac, const std::unique_ptr< MultiFab > &z_phys_nd, const TurbChoice &turbChoice, const Real const_grav, std::unique_ptr< SurfaceLayer > &, const MoistureComponentIndices &moisture_indices, const MultiFab *xvel, const MultiFab *yvel)
Definition: ERF_ComputeTurbulentViscosity.cpp:34
const bool use_moisture
Definition: ERF_InitCustomPert_ABL.H:71
AMREX_ALWAYS_ASSERT(bx.length()[2]==khi+1)
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::Real Real
Definition: ERF_ShocInterface.H:19
Real z_0
Definition: ERF_UpdateWSubsidence_Bomex.H:10
@ None
Definition: ERF_LSM_Types.H:6
@ xvel
Definition: ERF_IndexDefines.H:218
@ yvel
Definition: ERF_IndexDefines.H:219
amrex::Vector< TurbChoice > turbChoice
Turbulence options for each AMR level.
Definition: ERF_DataStruct.H:2252
amrex::Real gravity
Effective gravitational acceleration.
Definition: ERF_DataStruct.H:2343
static TerrainType terrain_type
Terrain or immersed-boundary representation.
Definition: ERF_DataStruct.H:2224
MoistureComponentIndices moisture_indices
Index map of the moisture data carried by the active scheme: conserved-state components for the speci...
Definition: ERF_DataStruct.H:2575
Definition: ERF_TurbStruct.H:115
RANSType rans_type
Selected RANS closure.
Definition: ERF_TurbStruct.H:828
bool uses_shoc_family() const noexcept
Query whether this level uses any SHOC-family PBL scheme.
Definition: ERF_TurbStruct.H:877
LESType les_type
Selected LES closure.
Definition: ERF_TurbStruct.H:777
PBLType pbl_type
Selected PBL closure.
Definition: ERF_TurbStruct.H:853

Referenced by ERF::advance_dycore().

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

◆ ComputeTurbulentViscosityLES()

void ComputeTurbulentViscosityLES ( Vector< std::unique_ptr< MultiFab >> &  Tau_lev,
const MultiFab &  cons_in,
MultiFab &  eddyViscosity,
MultiFab &  Diss,
const Geometry &  geom,
bool  use_terrain_fitted_coords,
Vector< std::unique_ptr< MultiFab >> &  mapfac,
const std::unique_ptr< MultiFab > &  z_phys_nd,
const TurbChoice &  turbChoice,
const Real  const_grav,
std::unique_ptr< SurfaceLayer > &  ,
const MoistureComponentIndices &  moisture_indices,
const MultiFab *  xvel,
const MultiFab *  yvel 
)

Function for computing the turbulent viscosity with LES.

Parameters
[in]Tau_levstrain at this level
[in]cons_incell center conserved quantities
[out]eddyViscosityturbulent viscosity
[out]Dissdissipation of turbulent kinetic energy
[in]geomproblem geometry
[in]use_terrain_fitted_coordsflag for terrain-fitted coordinates
[in]mapfacmap factors
[in]z_phys_ndnodal physical z coordinates
[in]turbChoicecontainer with turbulence parameters
[in]const_gravgravitational acceleration
[in]moisture_indicesmoisture component indices
[in]xvelx-direction velocity (for moist Ri correction)
[in]yvely-direction velocity (for moist Ri correction)
45 {
46  const GpuArray<Real, AMREX_SPACEDIM> cellSizeInv = geom.InvCellSizeArray();
47  const Box& domain = geom.Domain();
48 
49  Real inv_Pr_t = turbChoice.Pr_t_inv;
50  Real inv_Sc_t = turbChoice.Sc_t_inv;
51  Real inv_sigma_k = one / turbChoice.sigma_k;
52 
53  bool use_thetav_grad = (turbChoice.strat_type == StratType::thetav);
54  bool use_thetal_grad = (turbChoice.strat_type == StratType::thetal);
55 
56  bool isotropic = turbChoice.mix_isotropic;
57 
58  // SMAGORINSKY: Fill Kturb for momentum in horizontal and vertical
59  //***********************************************************************************
60  if (turbChoice.les_type == LESType::Smagorinsky)
61  {
62  Real Cs = turbChoice.Cs;
63  bool smag2d = turbChoice.smag2d;
64 
65  // Opt-in limits of WRF's smag2d_km on the horizontal viscosity (see
66  // Smag2DLimitedViscosity). The slope limiter needs the terrain slope, so it acts
67  // on terrain-fitted meshes only; SolverChoice aborts if it is asked for elsewhere.
68  bool l_smag2d_slope = smag2d && turbChoice.smag2d_slope_limiter && use_terrain_fitted_coords;
69  Real l_smag2d_cap = (smag2d) ? turbChoice.smag2d_kh_cap : zero;
70  bool l_smag2d_limit = l_smag2d_slope || (l_smag2d_cap > zero);
71 
72  // Define variables required inside device lambdas (scalars only)
73  Real l_abs_g = const_grav;
74  Real l_Ri_crit = turbChoice.Ri_crit;
75  bool l_use_Ri_corr = turbChoice.use_Ri_correction;
76  bool l_has_xvel = (xvel != nullptr);
77  bool l_has_yvel = (yvel != nullptr);
78 
79 #ifdef _OPENMP
80 #pragma omp parallel if (Gpu::notInLaunchRegion())
81 #endif
82  for (MFIter mfi(eddyViscosity,TilingIfNotGPU()); mfi.isValid(); ++mfi)
83  {
84  Box bxcc = mfi.growntilebox(1) & domain;
85  // NOTE: the closures deliberately store no subgrid heat flux. hfx_z (Hfx3)
86  // is z-nodal, (0,0,1), while the closure's -K dtheta/dz is cell-centred,
87  // and the theta diffusion of every RK stage overwrites all z-faces (the
88  // surface layer the bottom face) before anything reads them, so the value
89  // written here was both misplaced and unused. The TKE buoyancy source
90  // reads the face fluxes of that diffusion (ERF_TurbKESources.H).
91  const Array4<Real>& mu_turb = eddyViscosity.array(mfi);
92  const Array4<Real const >& cell_data = cons_in.array(mfi);
93  Array4<Real const> tau11 = Tau_lev[TauType::tau11]->array(mfi);
94  Array4<Real const> tau22 = Tau_lev[TauType::tau22]->array(mfi);
95  Array4<Real const> tau33 = Tau_lev[TauType::tau33]->array(mfi);
96  Array4<Real const> tau12 = Tau_lev[TauType::tau12]->array(mfi);
97  Array4<Real const> tau13 = Tau_lev[TauType::tau13]->array(mfi);
98  Array4<Real const> tau23 = Tau_lev[TauType::tau23]->array(mfi);
99  Array4<Real const> mf_u = mapfac[MapFacType::u_x]->const_array(mfi);
100  Array4<Real const> mf_v = mapfac[MapFacType::v_y]->const_array(mfi);
101  Array4<Real const> z_nd_arr = z_phys_nd->const_array(mfi);
102 
103  Array4<Real const> u_arr = (l_has_xvel) ? xvel->const_array(mfi) : Array4<Real const>{};
104  Array4<Real const> v_arr = (l_has_yvel) ? yvel->const_array(mfi) : Array4<Real const>{};
105 
106  ParallelFor(bxcc, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
107  {
108  // =====================================================================
109  // 1. STRAIN RATE MAGNITUDE CALCULATION
110  // =====================================================================
111  Real SmnSmn;
112  if (smag2d) {
113  SmnSmn = ComputeSmnSmn2D(i,j,k,tau11,tau22,tau12);
114  } else {
115  SmnSmn = ComputeSmnSmn(i,j,k,tau11,tau22,tau33,tau12,tau13,tau23);
116  }
117  Real strain_rate_magnitude = std::sqrt(two * SmnSmn);
118 
119  // =====================================================================
120  // 2. GRID SCALE CALCULATION (filter width Δ)
121  // =====================================================================
122  Real dxInv = cellSizeInv[0];
123  Real dyInv = cellSizeInv[1];
124  Real dzInv = cellSizeInv[2];
125  if (use_terrain_fitted_coords) {
126  dzInv /= Compute_h_zeta_AtCellCenter(i,j,k, cellSizeInv, z_nd_arr);
127  }
128 
129  Real Delta;
130  Real DeltaH;
131  if (isotropic) {
132  Real cellVolMsf = one / (dxInv * mf_u(i,j,0) * dyInv * mf_v(i,j,0) * dzInv);
133  Delta = std::cbrt(cellVolMsf);
134  DeltaH = Delta;
135  } else {
136  Delta = one / dzInv;
137  DeltaH = std::sqrt(one / (dxInv * mf_u(i,j,0) * dyInv * mf_v(i,j,0)));
138  }
139 
140  Real rho = cell_data(i, j, k, Rho_comp);
141  Real CsDeltaSqr_h = Cs * Cs * DeltaH * DeltaH;
142  Real CsDeltaSqr_v = Cs * Cs * Delta * Delta;
143 
144  Real nu_turb_base_h = CsDeltaSqr_h * strain_rate_magnitude;
145  Real nu_turb_base_v = CsDeltaSqr_v * strain_rate_magnitude;
146 
147  // WRF smag2d_km limits on K_h only, applied before the scalar diffusivities
148  // (Theta_h, Scalar_h, Q_h) are derived from Mom_h below; Mom_v is unchanged.
149  if (l_smag2d_limit) {
150  Real alpha = one;
151  if (l_smag2d_slope) {
153  }
154  nu_turb_base_h = Smag2DLimitedViscosity(nu_turb_base_h, strain_rate_magnitude,
155  DeltaH, alpha, l_smag2d_cap, l_smag2d_slope);
156  }
157 
158  Real stability_factor = one;
159 
160  if (l_use_Ri_corr && l_has_xvel && l_has_yvel) {
161  Real N2 = ComputeN2(i, j, k, dzInv, l_abs_g, cell_data, moisture_indices);
162  Real S2_vert = ComputeVerticalShear2(i, j, k, dzInv, u_arr, v_arr);
163  Real Ri = ComputeRichardson(N2, S2_vert);
164  stability_factor = StabilityFunction(Ri, l_Ri_crit);
165  }
166 
167  if (isotropic) {
168  mu_turb(i, j, k, EddyDiff::Mom_h) = rho * nu_turb_base_h * stability_factor;
169  mu_turb(i, j, k, EddyDiff::Mom_v) = rho * nu_turb_base_v * stability_factor;
170  } else {
171  mu_turb(i, j, k, EddyDiff::Mom_h) = rho * nu_turb_base_h;
172  mu_turb(i, j, k, EddyDiff::Mom_v) = rho * nu_turb_base_v * stability_factor;
173  }
174  });
175  }
176  }
177  // DEARDORFF: Fill Kturb for momentum in horizontal and vertical
178  //***********************************************************************************
179  else if (turbChoice.les_type == LESType::Deardorff)
180  {
181  const Real l_C_k = turbChoice.Ck;
182  const Real l_C_e = turbChoice.Ce;
183  const Real l_C_e_wall = turbChoice.Ce_wall;
184  const Real Ce_lcoeff = amrex::max(amrex::Real(zero), l_C_e - Real(1.9)*l_C_k);
185  const Real l_abs_g = const_grav;
186 
187  // Clamped divisor: the select is if-converted, so 1/theta_ref runs even
188  // when theta_ref = 0 and would trip fpe_trap_zero (see ERF_SetupDiff.H)
189  const bool use_ref_theta = (turbChoice.theta_ref > 0);
190  const Real inv_theta_ref = one / amrex::max(turbChoice.theta_ref, std::numeric_limits<Real>::min());
191  const Real l_inv_theta0 = (use_ref_theta) ? inv_theta_ref : one;
192 
193 #ifdef _OPENMP
194 #pragma omp parallel if (Gpu::notInLaunchRegion())
195 #endif
196  for ( MFIter mfi(eddyViscosity,TilingIfNotGPU()); mfi.isValid(); ++mfi)
197  {
198  Box bxcc = mfi.tilebox();
199 
200  // NOTE: the closures deliberately store no subgrid heat flux. hfx_z (Hfx3)
201  // is z-nodal, (0,0,1), while the closure's -K dtheta/dz is cell-centred,
202  // and the theta diffusion of every RK stage overwrites all z-faces (the
203  // surface layer the bottom face) before anything reads them, so the value
204  // written here was both misplaced and unused. The TKE buoyancy source
205  // reads the face fluxes of that diffusion (ERF_TurbKESources.H).
206  const Array4<Real>& mu_turb = eddyViscosity.array(mfi);
207  const Array4<Real>& diss = Diss.array(mfi);
208 
209  const Array4<Real const > &cell_data = cons_in.array(mfi);
210 
211  Array4<Real const> mf_u = mapfac[MapFacType::u_x]->const_array(mfi);
212  Array4<Real const> mf_v = mapfac[MapFacType::v_y]->const_array(mfi);
213 
214  Array4<Real const> z_nd_arr = z_phys_nd->const_array(mfi);
215 
216  ParallelFor(bxcc, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
217  {
218  Real dxInv = cellSizeInv[0];
219  Real dyInv = cellSizeInv[1];
220  Real dzInv = cellSizeInv[2];
221  if (use_terrain_fitted_coords) {
222  // the terrain grid is only deformed in z for now
223  dzInv /= Compute_h_zeta_AtCellCenter(i,j,k, cellSizeInv, z_nd_arr);
224  }
225  Real Delta;
226  if (isotropic) {
227  Real cellVolMsf = one / (dxInv * mf_u(i,j,0) * dyInv * mf_v(i,j,0) * dzInv);
228  Delta = std::cbrt(cellVolMsf);
229  } else {
230  Delta = one / dzInv;
231  }
232 
233  Real dtheta_dz;
234  if (use_thetav_grad) {
235  dtheta_dz = myhalf * ( GetThetav(i, j, k+1, cell_data, moisture_indices)
236  -GetThetav(i, j, k-1, cell_data, moisture_indices) )*dzInv;
237  } else if (use_thetal_grad) {
238  dtheta_dz = myhalf * ( GetThetal(i, j, k+1, cell_data, moisture_indices)
239  -GetThetal(i, j, k-1, cell_data, moisture_indices) )*dzInv;
240  } else {
241  dtheta_dz = myhalf * ( cell_data(i, j, k+1, RhoTheta_comp) / cell_data(i, j, k+1, Rho_comp)
242  -cell_data(i, j, k-1, RhoTheta_comp) / cell_data(i, j, k-1, Rho_comp) )*dzInv;
243  }
244 
245  // Calculate stratification-dependent mixing length (Deardorff 1980, Eqn. 10a)
246  Real E = amrex::max(cell_data(i,j,k,RhoKE_comp)/cell_data(i,j,k,Rho_comp),Real(0.0));
247  Real stratification = l_abs_g * dtheta_dz * l_inv_theta0;
248  if (!use_ref_theta) {
249  // l_inv_theta0 == 1, divide by actual theta
250  stratification *= cell_data(i,j,k,Rho_comp) /
251  cell_data(i,j,k,RhoTheta_comp);
252  }
253 
254  // Following WRF, the stratification effects are applied to the vertical length scales
255  // in the case of anisotropic mixing
256  Real length;
258  if (stratification <= eps) {
259  length = Delta; // cbrt(dx*dy*dz) -or- dz
260  } else {
261  length = Real(0.76) * std::sqrt(E / amrex::max(stratification,eps));
262  // mixing length should be _reduced_ for stable stratification
263  length = amrex::min(length, Delta);
264  // following WRF, make sure the mixing length isn't too small
265  length = amrex::max(length, Real(0.001) * Delta);
266  }
267 
268  Real DeltaH = (isotropic) ? length : std::sqrt(one / (dxInv * mf_u(i,j,0) * dyInv * mf_v(i,j,0)));
269 
270  Real Pr_inv_v = (one + two*length/Delta);
271  Real Pr_inv_h = (isotropic) ? Pr_inv_v : inv_Pr_t;
272 
273  // Calculate eddy diffusivities
274  // K = rho * C_k * l * KE^(1/2)
275  mu_turb(i,j,k,EddyDiff::Mom_h) = cell_data(i,j,k,Rho_comp) * l_C_k * DeltaH * std::sqrt(E);
276  mu_turb(i,j,k,EddyDiff::Mom_v) = cell_data(i,j,k,Rho_comp) * l_C_k * length * std::sqrt(E);
277  // KH = (1 + 2*l/delta) * mu_turb
278  mu_turb(i,j,k,EddyDiff::Theta_h) = Pr_inv_h * mu_turb(i,j,k,EddyDiff::Mom_h);
279  mu_turb(i,j,k,EddyDiff::Theta_v) = Pr_inv_v * mu_turb(i,j,k,EddyDiff::Mom_v);
280  // Store lengthscale for TKE source terms
281  mu_turb(i,j,k,EddyDiff::Turb_lengthscale) = length;
282 
283  // Calculate SFS quantities
284  // - dissipation
285  Real Ce;
286  if ((l_C_e_wall > 0) && (k==0)) {
287  Ce = l_C_e_wall;
288  } else {
289  Ce = Real(1.9)*l_C_k + Ce_lcoeff*length / Delta;
290  }
291  diss(i,j,k) = cell_data(i,j,k,Rho_comp) * Ce * std::pow(E,Real(1.5)) / length;
292  });
293  }
294  }
295 
296  // Extrapolate Kturb in x/y, fill remaining elements (relevant to lev==0)
297  //***********************************************************************************
298  int ngc(1);
299  // EddyDiff mapping : Theta_h KE_h Scalar_h Q_h
300  Vector<Real> Factors = {inv_Pr_t, inv_sigma_k, inv_Sc_t, inv_Sc_t}; // alpha = mu/Pr
301  Gpu::AsyncVector<Real> d_Factors; d_Factors.resize(Factors.size());
302  Gpu::copy(Gpu::hostToDevice, Factors.begin(), Factors.end(), d_Factors.begin());
303  Real* fac_ptr = d_Factors.data();
304 
305  const bool use_KE = ( turbChoice.les_type == LESType::Deardorff );
306 
307 #ifdef _OPENMP
308 #pragma omp parallel if (Gpu::notInLaunchRegion())
309 #endif
310  for ( MFIter mfi(eddyViscosity,TilingIfNotGPU()); mfi.isValid(); ++mfi)
311  {
312  Box bxcc = mfi.tilebox();
313  Box planex = bxcc; planex.setSmall(0, 1); planex.setBig(0, ngc); planex.grow(1,1);
314  Box planey = bxcc; planey.setSmall(1, 1); planey.setBig(1, ngc); planey.grow(0,1);
315  //
316  // NOTE: growntilebox, not the tilebox grown by hand. Growing by hand pushes every tile
317  // ngc cells past its own share of the grid, so once the grid is tiled two tiles
318  // store to the same mu_turb cells -- a data race under OpenMP, even though both
319  // store the same value. The union of the tiles, and the values stored, are
320  // unchanged, and this is identical to the old expression with one tile per grid.
321  //
322  bxcc = mfi.growntilebox(IntVect(ngc,ngc,0));
323 
324  const Array4<Real>& mu_turb = eddyViscosity.array(mfi);
325 
326  for (auto n = 1; n < EddyDiff::Mom_v; ++n) {
327  int offset = EddyDiff::Mom_v;
328  switch (n)
329  {
330  case EddyDiff::KE_h:
331  if (use_KE) {
332  ParallelFor(bxcc, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
333  {
334  int indx = n;
335  int indx_v = indx + offset;
336  mu_turb(i,j,k,indx) = mu_turb(i,j,k,EddyDiff::Mom_h) * fac_ptr[indx-1];
337  mu_turb(i,j,k,indx_v) = mu_turb(i,j,k,EddyDiff::Mom_v) * fac_ptr[indx-1];
338  });
339  }
340  break;
341  default:
342  ParallelFor(bxcc, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
343  {
344  int indx = n;
345  int indx_v = indx + offset;
346 
347  // NOTE: Theta_h, Theta_v have already been set for Deardorff
348  if (!(indx_v == EddyDiff::Theta_v && use_KE)) {
349  mu_turb(i,j,k,indx) = mu_turb(i,j,k,EddyDiff::Mom_h) * fac_ptr[indx-1];
350  mu_turb(i,j,k,indx_v) = mu_turb(i,j,k,EddyDiff::Mom_v) * fac_ptr[indx-1];
351  }
352  });
353  break;
354  }
355  }
356  }
357 }
@ tau12
Definition: ERF_DataStruct.H:48
@ tau23
Definition: ERF_DataStruct.H:48
@ tau33
Definition: ERF_DataStruct.H:48
@ tau22
Definition: ERF_DataStruct.H:48
@ tau11
Definition: ERF_DataStruct.H:48
@ tau13
Definition: ERF_DataStruct.H:48
@ v_y
Definition: ERF_DataStruct.H:38
@ u_x
Definition: ERF_DataStruct.H:37
AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real ComputeSmnSmn2D(int &i, int &j, int &k, const amrex::Array4< amrex::Real const > &tau11, const amrex::Array4< amrex::Real const > &tau22, const amrex::Array4< amrex::Real const > &tau12)
Definition: ERF_EddyViscosity.H:254
AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real ComputeSmnSmn(int &i, int &j, int &k, const amrex::Array4< amrex::Real const > &tau11, const amrex::Array4< amrex::Real const > &tau22, const amrex::Array4< amrex::Real const > &tau33, const amrex::Array4< amrex::Real const > &tau12, const amrex::Array4< amrex::Real const > &tau13, const amrex::Array4< amrex::Real const > &tau23)
Definition: ERF_EddyViscosity.H:104
#define Rho_comp
Definition: ERF_IndexDefines.H:42
#define RhoTheta_comp
Definition: ERF_IndexDefines.H:43
#define RhoKE_comp
Definition: ERF_IndexDefines.H:44
amrex::GpuArray< Real, AMREX_SPACEDIM > dxInv
Definition: ERF_InitCustomPertVels_ParticleTests.H:17
const Real length
Definition: ERF_InitCustomPert_AnelasticWallDiffusion.H:14
AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real GetThetav(const int &i, const int &j, const int &k, const amrex::Array4< amrex::Real const > &cell_data, const MoistureComponentIndices &moisture_indices)
Definition: ERF_MoistUtils.H:74
AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real GetThetal(const int &i, const int &j, const int &k, const amrex::Array4< amrex::Real const > &cell_data, const MoistureComponentIndices &moisture_indices)
Definition: ERF_MoistUtils.H:130
constexpr amrex::Real two
Definition: ERF_NumericalConstants.H:38
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_FORCE_INLINE IntVect offset(const int face_dir, const int normal)
Definition: ERF_ReadBndryPlanes.cpp:32
AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real StabilityFunction(amrex::Real Ri, amrex::Real Ri_crit)
Definition: ERF_RichardsonNumber.H:522
AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real ComputeVerticalShear2(int i, int j, int k, amrex::Real dzInv, const amrex::Array4< const amrex::Real > &u, const amrex::Array4< const amrex::Real > &v)
Definition: ERF_RichardsonNumber.H:366
AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real ComputeRichardson(amrex::Real N2_moist, amrex::Real S2_vert)
Definition: ERF_RichardsonNumber.H:506
AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real ComputeN2(int i, int j, int k, amrex::Real dzInv, amrex::Real const_grav, const amrex::Array4< const amrex::Real > &cell_data, const MoistureComponentIndices &moisture_indices)
Definition: ERF_RichardsonNumber.H:130
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real Smag2DLimitedViscosity(amrex::Real nu, amrex::Real strain, amrex::Real DeltaH, amrex::Real alpha, amrex::Real kh_cap, bool use_slope) noexcept
Definition: ERF_TerrainDiffusionLimits.H:110
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real TerrainSlopeFactor(const TerrainCellDrops &d) noexcept
Definition: ERF_TerrainDiffusionLimits.H:78
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE TerrainCellDrops ComputeTerrainCellDrops(int i, int j, int k, const amrex::Array4< const amrex::Real > &z_nd) noexcept
Definition: ERF_TerrainDiffusionLimits.H:44
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real Compute_h_zeta_AtCellCenter(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:197
@ Theta_v
Definition: ERF_IndexDefines.H:253
@ Turb_lengthscale
Definition: ERF_IndexDefines.H:257
@ Mom_h
Definition: ERF_IndexDefines.H:247
@ Mom_v
Definition: ERF_IndexDefines.H:252
@ Theta_h
Definition: ERF_IndexDefines.H:248
@ KE_h
Definition: ERF_IndexDefines.H:249
@ rho
Definition: ERF_Kessler.H:25
@ alpha
Definition: ERF_SLM.H:151
real(c_double), parameter epsilon
Definition: ERF_module_model_constants.F90:12
StratType strat_type
Thermodynamic variable used for stability stratification.
Definition: ERF_TurbStruct.H:817
amrex::Real Ce
Deardorff dissipation coefficient.
Definition: ERF_TurbStruct.H:799
amrex::Real sigma_k
TKE diffusivity coefficient denominator.
Definition: ERF_TurbStruct.H:812
bool use_Ri_correction
Whether Richardson-number correction is applied.
Definition: ERF_TurbStruct.H:824
amrex::Real Ck
Deardorff eddy-viscosity coefficient.
Definition: ERF_TurbStruct.H:801
amrex::Real Ce_wall
Wall value for the Deardorff dissipation coefficient.
Definition: ERF_TurbStruct.H:800
amrex::Real theta_ref
Reference potential temperature for stable stratification.
Definition: ERF_TurbStruct.H:815
amrex::Real Sc_t_inv
Inverse turbulent Schmidt number.
Definition: ERF_TurbStruct.H:785
bool mix_isotropic
Whether subgrid mixing uses isotropic length scales.
Definition: ERF_TurbStruct.H:822
amrex::Real smag2d_kh_cap
Cap K_h <= smag2d_kh_cap * Delta_h for Smagorinsky-2D (m/s); 0 means no cap.
Definition: ERF_TurbStruct.H:793
amrex::Real Ri_crit
Critical Richardson number for stability correction.
Definition: ERF_TurbStruct.H:825
amrex::Real Cs
Smagorinsky model coefficient.
Definition: ERF_TurbStruct.H:788
bool smag2d_slope_limiter
Apply WRF's Smagorinsky-2D slope limiter (K_h / alpha or / alpha^2) on terrain-fitted meshes.
Definition: ERF_TurbStruct.H:792
amrex::Real Pr_t_inv
Inverse turbulent Prandtl number.
Definition: ERF_TurbStruct.H:781
bool smag2d
Whether the 2-D Smagorinsky formulation is used.
Definition: ERF_TurbStruct.H:789

Referenced by ComputeTurbulentViscosity().

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

◆ ComputeTurbulentViscosityLES_EB()

void ComputeTurbulentViscosityLES_EB ( Vector< std::unique_ptr< MultiFab >> &  Tau_lev,
const MultiFab &  cons_in,
MultiFab &  eddyViscosity,
MultiFab &  Hfx1,
MultiFab &  Hfx2,
MultiFab &  Hfx3,
const Geometry &  geom,
Vector< std::unique_ptr< MultiFab >> &  mapfac,
const TurbChoice &  turbChoice,
const Real  const_grav,
[[maybe_unused] ] const SolverChoice &  solverChoice,
std::unique_ptr< SurfaceLayer > &  ,
const MoistureComponentIndices &  moisture_indices,
const eb_ &  ebfact,
const MultiFab *  xvel,
const MultiFab *  yvel 
)
371 {
372  const GpuArray<Real, AMREX_SPACEDIM> cellSizeInv = geom.InvCellSizeArray();
373  Real dxInv = cellSizeInv[0];
374  Real dyInv = cellSizeInv[1];
375  Real dzInv = cellSizeInv[2];
376 
377  const Box& domain = geom.Domain();
378 
379  Real inv_Pr_t = turbChoice.Pr_t_inv;
380  Real inv_Sc_t = turbChoice.Sc_t_inv;
381  Real inv_sigma_k = one / turbChoice.sigma_k;
382 
383  bool isotropic = turbChoice.mix_isotropic;
384 
385  AMREX_ASSERT(turbChoice.les_type == LESType::Smagorinsky);
386 
387  // SMAGORINSKY: Fill Kturb for momentum in horizontal and vertical
388  //***********************************************************************************
389  if (turbChoice.les_type == LESType::Smagorinsky)
390  {
391  Real Cs = turbChoice.Cs;
392 
393  // Define variables required inside device lambdas (scalars only)
394  Real l_abs_g = const_grav;
395  Real l_Ri_crit = turbChoice.Ri_crit;
396  bool l_use_Ri_corr = turbChoice.use_Ri_correction;
397  bool l_has_xvel = (xvel != nullptr);
398  bool l_has_yvel = (yvel != nullptr);
399 
400 #ifdef _OPENMP
401 #pragma omp parallel if (Gpu::notInLaunchRegion())
402 #endif
403  for (MFIter mfi(eddyViscosity,TilingIfNotGPU()); mfi.isValid(); ++mfi)
404  {
405  Box bxcc = mfi.growntilebox(1) & domain;
406  const Array4<Real>& mu_turb = eddyViscosity.array(mfi);
407  const Array4<Real>& hfx_x = Hfx1.array(mfi);
408  const Array4<Real>& hfx_y = Hfx2.array(mfi);
409  const Array4<Real>& hfx_z = Hfx3.array(mfi);
410  const Array4<Real const >& cell_data = cons_in.array(mfi);
411  Array4<Real const> tau11 = Tau_lev[TauType::tau11]->array(mfi);
412  Array4<Real const> tau22 = Tau_lev[TauType::tau22]->array(mfi);
413  Array4<Real const> tau33 = Tau_lev[TauType::tau33]->array(mfi);
414  Array4<Real const> tau12 = Tau_lev[TauType::tau12]->array(mfi);
415  Array4<Real const> tau13 = Tau_lev[TauType::tau13]->array(mfi);
416  Array4<Real const> tau23 = Tau_lev[TauType::tau23]->array(mfi);
417  Array4<Real const> mf_u = mapfac[MapFacType::u_x]->const_array(mfi);
418  Array4<Real const> mf_v = mapfac[MapFacType::v_y]->const_array(mfi);
419 
420  Array4<Real const> u_arr = (l_has_xvel) ? xvel->const_array(mfi) : Array4<Real const>{};
421  Array4<Real const> v_arr = (l_has_yvel) ? yvel->const_array(mfi) : Array4<Real const>{};
422 
423  Array4<const EBCellFlag> c_cflag = (ebfact.get_const_factory())->getMultiEBCellFlagFab()[mfi].const_array();
424  Array4<const EBCellFlag> u_cflag = (ebfact.get_u_const_factory())->getMultiEBCellFlagFab()[mfi].const_array();
425  Array4<const EBCellFlag> v_cflag = (ebfact.get_v_const_factory())->getMultiEBCellFlagFab()[mfi].const_array();
426  Array4<const EBCellFlag> w_cflag = (ebfact.get_w_const_factory())->getMultiEBCellFlagFab()[mfi].const_array();
427  Array4<const Real > u_vfrac = (ebfact.get_u_const_factory())->getVolFrac().const_array(mfi);
428  Array4<const Real > v_vfrac = (ebfact.get_v_const_factory())->getVolFrac().const_array(mfi);
429 
430  ParallelFor(bxcc, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
431  {
432  if (!c_cflag(i,j,k).isCovered()) {
433  // =====================================================================
434  // 1. STRAIN RATE MAGNITUDE CALCULATION
435  // =====================================================================
436  Real SmnSmn = zero;
437  if (c_cflag(i,j,k).isRegular()) {
438  SmnSmn = ComputeSmnSmn(i,j,k,tau11,tau22,tau33,tau12,tau13,tau23);
439  } else if (c_cflag(i,j,k).isSingleValued()) {
440  SmnSmn = ComputeSmnSmn_EB(i,j,k,tau11,tau22,tau33,tau12,tau13,tau23,c_cflag,u_cflag,v_cflag,w_cflag);
441  }
442  Real strain_rate_magnitude = std::sqrt(two * SmnSmn);
443 
444  // =====================================================================
445  // 2. GRID SCALE CALCULATION (filter width Δ)
446  // =====================================================================
447  Real Delta;
448  Real DeltaH;
449  if (isotropic) {
450  Real cellVolMsf = one / (dxInv * mf_u(i,j,0) * dyInv * mf_v(i,j,0) * dzInv);
451  Delta = std::cbrt(cellVolMsf);
452  DeltaH = Delta;
453  } else {
454  Delta = one / dzInv;
455  DeltaH = std::sqrt(one / (dxInv * mf_u(i,j,0) * dyInv * mf_v(i,j,0)));
456  }
457 
458  Real rho = cell_data(i, j, k, Rho_comp);
459  Real CsDeltaSqr_h = Cs * Cs * DeltaH * DeltaH;
460  Real CsDeltaSqr_v = Cs * Cs * Delta * Delta;
461 
462  Real nu_turb_base_h = CsDeltaSqr_h * strain_rate_magnitude;
463  Real nu_turb_base_v = CsDeltaSqr_v * strain_rate_magnitude;
464 
465  Real stability_factor = one;
466 
467  if (l_use_Ri_corr && l_has_xvel && l_has_yvel) {
468  Real N2 = zero;
469  Real S2_vert = zero;
470  // Covered and multi-valued cells fall through both branches below
471  // with no shear computed, so start from "not valid" and let the
472  // branch that does the work say otherwise.
473  bool shear_is_valid = false;
474  if (c_cflag(i,j,k).isRegular()) {
475  N2 = ComputeN2(i, j, k, dzInv, l_abs_g, cell_data, moisture_indices);
476  S2_vert = ComputeVerticalShear2(i, j, k, dzInv, u_arr, v_arr);
477  shear_is_valid = true;
478  } else if (c_cflag(i,j,k).isSingleValued()) {
479  N2 = ComputeN2_EB(i, j, k, c_cflag, dzInv, l_abs_g, cell_data, moisture_indices);
480  S2_vert = ComputeVerticalShear2_EB(i, j, k, c_cflag, u_vfrac, v_vfrac, dzInv, u_arr, v_arr, shear_is_valid);
481  }
482  // When the EB geometry leaves no usable shear stencil we keep
483  // stability_factor = 1, i.e. no stratification damping: without a
484  // trustworthy gradient we would rather leave the Smagorinsky
485  // viscosity alone than suppress it on the strength of a Ri we do
486  // not believe. This is a deliberate choice -- damping to 0 instead
487  // would be just as defensible -- so change it knowingly.
488  if (shear_is_valid) {
489  Real Ri = ComputeRichardson(N2, S2_vert);
490  stability_factor = StabilityFunction(Ri, l_Ri_crit);
491  }
492  }
493 
494  if (isotropic) {
495  mu_turb(i, j, k, EddyDiff::Mom_h) = rho * nu_turb_base_h * stability_factor;
496  mu_turb(i, j, k, EddyDiff::Mom_v) = rho * nu_turb_base_v * stability_factor;
497  } else {
498  mu_turb(i, j, k, EddyDiff::Mom_h) = rho * nu_turb_base_h;
499  mu_turb(i, j, k, EddyDiff::Mom_v) = rho * nu_turb_base_v * stability_factor;
500  }
501 
502  Real dtheta_dz = zero;
503 
504  if (c_cflag(i,j,k).isSingleValued() && c_cflag(i,j,k+1).isCovered()) {
505  amrex::Real theta = cell_data(i, j, k, RhoTheta_comp) / rho;
506  amrex::Real theta_km1 = cell_data(i,j,k-1,RhoTheta_comp)/cell_data(i,j,k-1,Rho_comp);
507  amrex::Real theta_km2 = cell_data(i,j,k-2,RhoTheta_comp)/cell_data(i,j,k-2,Rho_comp);
508  dtheta_dz = (three*theta - Real(4.)*theta_km1 + theta_km2) * myhalf * dzInv;
509  } else if (c_cflag(i,j,k).isSingleValued() && c_cflag(i,j,k-1).isCovered()) {
510  amrex::Real theta = cell_data(i, j, k, RhoTheta_comp) / rho;
511  amrex::Real theta_kp1 = cell_data(i,j,k+1,RhoTheta_comp)/cell_data(i,j,k+1,Rho_comp);
512  amrex::Real theta_kp2 = cell_data(i,j,k+2,RhoTheta_comp)/cell_data(i,j,k+2,Rho_comp);
513  dtheta_dz = (-theta_kp2 + Real(4.)*theta_kp1 - three*theta) * myhalf * dzInv;
514  } else if (!c_cflag(i,j,k).isCovered()) {
515  amrex::Real theta_km1 = cell_data(i,j,k-1,RhoTheta_comp)/cell_data(i,j,k-1,Rho_comp);
516  amrex::Real theta_kp1 = cell_data(i,j,k+1,RhoTheta_comp)/cell_data(i,j,k+1,Rho_comp);
517  dtheta_dz = myhalf * (theta_kp1 - theta_km1) * dzInv;
518  }
519 
520  // The EB diffusion does not write hfx_z, so on EB terrain this is
521  // the only writer of the SGS heat flux output (the surface layer
522  // overwrites the bottom). Elsewhere the theta diffusion writes the
523  // face fluxes and the closures do not store a heat flux. This write
524  // keeps the z-nodal/cell-centred mismatch noted above; moving the EB
525  // heat-flux output to the EB face fluxes belongs with that diffusion.
526  hfx_x(i,j,k) = zero;
527  hfx_y(i,j,k) = zero;
528  hfx_z(i,j,k) = -inv_Pr_t * mu_turb(i,j,k,EddyDiff::Mom_v) * dtheta_dz;
529 
530  } else {
531 
532  hfx_x(i,j,k) = zero;
533  hfx_y(i,j,k) = zero;
534  hfx_z(i,j,k) = zero;
535  }// End of if not covered
536  });
537  }
538  }
539 
540  // Extrapolate Kturb in x/y, fill remaining elements (relevant to lev==0)
541  //***********************************************************************************
542  int ngc(1);
543  // EddyDiff mapping : Theta_h KE_h Scalar_h Q_h
544  Vector<Real> Factors = {inv_Pr_t, inv_sigma_k, inv_Sc_t, inv_Sc_t}; // alpha = mu/Pr
545  Gpu::AsyncVector<Real> d_Factors; d_Factors.resize(Factors.size());
546  Gpu::copy(Gpu::hostToDevice, Factors.begin(), Factors.end(), d_Factors.begin());
547  Real* fac_ptr = d_Factors.data();
548 
549  const bool use_KE = ( turbChoice.les_type == LESType::Deardorff );
550 
551 #ifdef _OPENMP
552 #pragma omp parallel if (Gpu::notInLaunchRegion())
553 #endif
554  for ( MFIter mfi(eddyViscosity,TilingIfNotGPU()); mfi.isValid(); ++mfi)
555  {
556  Box bxcc = mfi.tilebox();
557  Box planex = bxcc; planex.setSmall(0, 1); planex.setBig(0, ngc); planex.grow(1,1);
558  Box planey = bxcc; planey.setSmall(1, 1); planey.setBig(1, ngc); planey.grow(0,1);
559  //
560  // NOTE: growntilebox, not the tilebox grown by hand. Growing by hand pushes every tile
561  // ngc cells past its own share of the grid, so once the grid is tiled two tiles
562  // store to the same mu_turb cells -- a data race under OpenMP, even though both
563  // store the same value. The union of the tiles, and the values stored, are
564  // unchanged, and this is identical to the old expression with one tile per grid.
565  //
566  bxcc = mfi.growntilebox(IntVect(ngc,ngc,0));
567 
568  const Array4<Real>& mu_turb = eddyViscosity.array(mfi);
569 
570  for (auto n = 1; n < EddyDiff::Mom_v; ++n) {
571  int offset = EddyDiff::Mom_v;
572  switch (n)
573  {
574  case EddyDiff::KE_h:
575  if (use_KE) {
576  ParallelFor(bxcc, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
577  {
578  int indx = n;
579  int indx_v = indx + offset;
580  mu_turb(i,j,k,indx) = mu_turb(i,j,k,EddyDiff::Mom_h) * fac_ptr[indx-1];
581  mu_turb(i,j,k,indx_v) = mu_turb(i,j,k,EddyDiff::Mom_v) * fac_ptr[indx-1];
582  });
583  }
584  break;
585  default:
586  ParallelFor(bxcc, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
587  {
588  int indx = n;
589  int indx_v = indx + offset;
590 
591  // NOTE: Theta_h, Theta_v have already been set for Deardorff
592  if (!(indx_v == EddyDiff::Theta_v && use_KE)) {
593  mu_turb(i,j,k,indx) = mu_turb(i,j,k,EddyDiff::Mom_h) * fac_ptr[indx-1];
594  mu_turb(i,j,k,indx_v) = mu_turb(i,j,k,EddyDiff::Mom_v) * fac_ptr[indx-1];
595  }
596  });
597  break;
598  }
599  }
600  }
601 }
AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real ComputeSmnSmn_EB(int &i, int &j, int &k, const amrex::Array4< amrex::Real const > &tau11, const amrex::Array4< amrex::Real const > &tau22, const amrex::Array4< amrex::Real const > &tau33, const amrex::Array4< amrex::Real const > &tau12, const amrex::Array4< amrex::Real const > &tau13, const amrex::Array4< amrex::Real const > &tau23, const amrex::Array4< const amrex::EBCellFlag > &c_cflag, const amrex::Array4< const amrex::EBCellFlag > &u_cflag, const amrex::Array4< const amrex::EBCellFlag > &v_cflag, const amrex::Array4< const amrex::EBCellFlag > &w_cflag)
Definition: ERF_EddyViscosity.H:149
constexpr amrex::Real three
Definition: ERF_NumericalConstants.H:39
AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real ComputeN2_EB(int i, int j, int k, const amrex::Array4< const amrex::EBCellFlag > &c_cflag, amrex::Real dzInv, amrex::Real const_grav, const amrex::Array4< const amrex::Real > &cell_data, const MoistureComponentIndices &moisture_indices)
Definition: ERF_RichardsonNumber.H:192
AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real ComputeVerticalShear2_EB(int i, int j, int k, const amrex::Array4< const amrex::EBCellFlag > &c_cflag, const amrex::Array4< const amrex::Real > &u_vfrac, const amrex::Array4< const amrex::Real > &v_vfrac, amrex::Real dzInv, const amrex::Array4< const amrex::Real > &u, const amrex::Array4< const amrex::Real > &v, bool &shear_is_valid)
Definition: ERF_RichardsonNumber.H:403
eb_aux_ const * get_w_const_factory() const noexcept
Return the ERF auxiliary z-face EB factory.
Definition: ERF_EB.H:133
const std::unique_ptr< amrex::EBFArrayBoxFactory > & get_const_factory() const noexcept
Return the cell-centered EB factory.
Definition: ERF_EB.H:102
eb_aux_ const * get_v_const_factory() const noexcept
Return the ERF auxiliary y-face EB factory.
Definition: ERF_EB.H:131
eb_aux_ const * get_u_const_factory() const noexcept
Return the ERF auxiliary x-face EB factory.
Definition: ERF_EB.H:129
@ theta
Definition: ERF_Kessler.H:26

Referenced by ComputeTurbulentViscosity().

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

◆ ComputeTurbulentViscosityRANS()

void ComputeTurbulentViscosityRANS ( int  level,
const MultiFab &  cons_in,
const MultiFab &  wdist,
MultiFab &  eddyViscosity,
MultiFab &  Diss,
const Geometry &  geom,
bool  use_terrain_fitted_coords,
const std::unique_ptr< MultiFab > &  z_phys_nd,
const TurbChoice &  turbChoice,
const Real  const_grav,
std::unique_ptr< SurfaceLayer > &  SurfLayer,
const MultiFab *  z_0 
)

Function for computing the eddy viscosity with RANS.

Parameters
[in]cons_incell center conserved quantities
[in]wdistwall distance
[out]eddyViscosityturbulent viscosity
[out]Dissdissipation of turbulent kinetic energy
[in]geomproblem geometry
[in]use_terrain_fitted_coordsflag for terrain-fitted coordinates
[in]z_phys_ndnodal physical z coordinates
[in]turbChoicecontainer with turbulence parameters
[in]const_gravgravitational acceleration
[in]SurfLayeroptional surface-layer model
[in]z_0roughness length
630 {
631  const GpuArray<Real, AMREX_SPACEDIM> cellSizeInv = geom.InvCellSizeArray();
632  const bool use_SurfLayer = (SurfLayer != nullptr);
633 
634  // Optional cap of the geometric length from the diagnosed PBL height
635  const bool lscale_from_pblh = turbChoice.rans_lscale_from_pblh;
636  if (lscale_from_pblh) {
637  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(use_SurfLayer && SurfLayer->computes_pblh(),
638  "erf.rans_lscale_from_pblh needs zlo.type = surface_layer and erf.most.pblh_calc = MYNN25");
639  }
640  const MultiFab* pblh_mf = (lscale_from_pblh) ? SurfLayer->get_pblh(level) : nullptr;
641 
642  Real inv_Pr_t = turbChoice.Pr_t_inv;
643  Real inv_Sc_t = turbChoice.Sc_t_inv;
644  Real inv_sigma_k = one / turbChoice.sigma_k;
645 
646  // One-Equation k model (Axell & Liungman 2001, Environ Fluid Mech)
647  //***********************************************************************************
648  if (turbChoice.rans_type == RANSType::kEqn)
649  {
650  const Real Cmu0 = turbChoice.Cmu0;
651  const Real Cmu0_pow3 = Cmu0 * Cmu0 * Cmu0;
652  const Real inv_Cb_sq = one / (turbChoice.Cb * turbChoice.Cb);
653  const Real Rt_crit = turbChoice.Rt_crit;
654  const Real Rt_min = turbChoice.Rt_min;
655  const Real l_g_max = turbChoice.l_g_max;
656  const Real l_min = turbChoice.rans_lscale_min;
657  const Real abs_g = const_grav;
658  // floor on k: erf.tke_floor if set, otherwise machine epsilon
659  const Real tke_floor = amrex::max(turbChoice.tke_floor, std::numeric_limits<Real>::epsilon());
660 
661  // Clamped divisor: the select is if-converted, so 1/theta_ref runs even
662  // when theta_ref = 0 and would trip fpe_trap_zero (see ERF_SetupDiff.H)
663  const bool use_ref_theta = (turbChoice.theta_ref > 0);
664  const Real inv_theta_ref = one / amrex::max(turbChoice.theta_ref, std::numeric_limits<Real>::min());
665  const Real inv_theta0 = (use_ref_theta) ? inv_theta_ref : one;
666 
667 #ifdef _OPENMP
668 #pragma omp parallel if (Gpu::notInLaunchRegion())
669 #endif
670  for ( MFIter mfi(eddyViscosity,TilingIfNotGPU()); mfi.isValid(); ++mfi)
671  {
672  Box bxcc = mfi.tilebox();
673 
674  const Array4<Real const>& d_arr = wdist.const_array(mfi);
675  const Array4<Real const>& z0_arr = (use_SurfLayer) ? z_0->const_array(mfi) : Array4<Real const>{};
676  const Array4<Real const>& pblh_arr = (pblh_mf) ? pblh_mf->const_array(mfi) : Array4<Real const>{};
677 
678  // NOTE: the closures deliberately store no subgrid heat flux. hfx_z (Hfx3)
679  // is z-nodal, (0,0,1), while the closure's -K dtheta/dz is cell-centred,
680  // and the theta diffusion of every RK stage overwrites all z-faces (the
681  // surface layer the bottom face) before anything reads them, so the value
682  // written here was both misplaced and unused. The TKE buoyancy source
683  // reads the face fluxes of that diffusion (ERF_TurbKESources.H).
684  const Array4<Real>& mu_turb = eddyViscosity.array(mfi);
685  const Array4<Real>& diss = Diss.array(mfi);
686 
687  const Array4<Real const>& cell_data = cons_in.array(mfi);
688 
689  const Array4<Real const>& z_nd_arr = z_phys_nd->const_array(mfi);
690 
691  ParallelFor(bxcc, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
692  {
694  Real tke = amrex::max(cell_data(i,j,k,RhoKE_comp)/cell_data(i,j,k,Rho_comp), tke_floor);
695 
696  // Estimate stratification
697  Real dzInv = cellSizeInv[2];
698  if (use_terrain_fitted_coords) {
699  // the terrain grid is only deformed in z for now
700  dzInv /= Compute_h_zeta_AtCellCenter(i,j,k, cellSizeInv, z_nd_arr);
701  }
702  Real dtheta_dz = myhalf * ( cell_data(i,j,k+1,RhoTheta_comp)/cell_data(i,j,k+1,Rho_comp)
703  - cell_data(i,j,k-1,RhoTheta_comp)/cell_data(i,j,k-1,Rho_comp) )*dzInv;
704  Real N2 = abs_g * inv_theta0 * dtheta_dz; // Brunt–Väisälä frequency squared
705  if (!use_ref_theta) {
706  // inv_theta0 == 1, divide by actual theta
707  N2 *= cell_data(i,j,k,Rho_comp) /
708  cell_data(i,j,k,RhoTheta_comp);
709  }
710 
711  // Geometric length scale (AL01, Eqn. 22)
712  Real l_g = (z0_arr) ? KAPPA * (d_arr(i, j, k) + z0_arr(i, j, 0))
713  : KAPPA * d_arr(i, j, k);
714 
715  // Enforce a maximum value: fixed, or kappa * 0.1 * zi clamped to
716  // [rans_lscale_min, max_geom_lscale] when the PBL height is diagnosed
717  Real l_cap = l_g_max;
718  if (pblh_arr) {
719  l_cap = amrex::min(l_g_max, amrex::max(l_min, KAPPA * Real(0.1) * pblh_arr(i,j,0)));
720  }
721  l_g = AL01::geom_length(l_g, l_cap);
722 
723  // Turbulent length scale (neutral / stable Eq. 26 / unstable Eq. 28,
724  // the latter bounded through the smoothed Rt)
725  Real length = AL01::turb_length(l_g, N2, tke, Cmu0_pow3, inv_Cb_sq, Rt_crit, Rt_min, eps);
726  mu_turb(i, j, k, EddyDiff::Turb_lengthscale) = length;
727 
728  // Dissipation rate (AL01, Eqn. 19)
729  diss(i, j, k) = AL01::dissipation(cell_data(i, j, k, Rho_comp), Cmu0_pow3, tke, length);
730 
731  // Turbulent Richardson number (AL01, Eqn. 29 combined with Eqn. 19),
732  // smoothed below Rt_crit (Burchard & Petersen)
733  Real Rt = AL01::smooth_Rt(AL01::richardson(length, N2, tke, Cmu0_pow3), Rt_crit, Rt_min);
734 
735  // Stability functions (AL01, Eqns. 31 and 32), using the smoothed Rt
736  Real cmu = AL01::cmu(Rt, Cmu0);
737  Real cmu_prime = AL01::cmu_prime(Rt, Cmu0);
738  mu_turb(i, j, k, EddyDiff::RANS_Rt) = Rt;
739  mu_turb(i, j, k, EddyDiff::RANS_cmu) = cmu;
740  mu_turb(i, j, k, EddyDiff::RANS_cmu_prime) = cmu_prime;
741 
742  // Calculate eddy diffusivities
743  // K = rho * nu_t = rho * c_mu * tke^(1/2) * length
744  Real nut = cmu * std::sqrt(tke) * length; // eddy viscosity
745  Real nut_prime = cmu_prime / cmu * nut; // eddy diffusivity
746  mu_turb(i, j, k, EddyDiff::Mom_h) = cell_data(i, j, k, Rho_comp) * nut;
747  mu_turb(i, j, k, EddyDiff::Mom_v) = mu_turb(i, j, k, EddyDiff::Mom_h);
748  mu_turb(i, j, k, EddyDiff::Theta_v) = cell_data(i, j, k, Rho_comp) * nut_prime;
749  });
750  }
751  }
752 
753  // Fill the remaining eddy-diffusivity components from Mom_h. Ghost cells
754  // are filled afterwards by the FillBoundary and physical-boundary
755  // extrapolation in ComputeTurbulentViscosity, so only the valid cells are
756  // set here.
757  //***********************************************************************************
758  // EddyDiff mapping : Theta_h KE_h Scalar_h Q_h
759  Vector<Real> Factors = {inv_Pr_t, inv_sigma_k, inv_Sc_t, inv_Sc_t}; // alpha = mu/Pr
760  Gpu::AsyncVector<Real> d_Factors; d_Factors.resize(Factors.size());
761  Gpu::copy(Gpu::hostToDevice, Factors.begin(), Factors.end(), d_Factors.begin());
762  Real* fac_ptr = d_Factors.data();
763 
764  const bool use_KE = ( turbChoice.rans_type == RANSType::kEqn );
765  // With erf.rans_consistent_diffusivities every heat, scalar and moisture
766  // diffusivity is rho * cmu' * sqrt(k) * L, i.e. the Theta_v value set by
767  // the closure; without it the horizontal heat and all scalar diffusivities
768  // are the eddy viscosity over Pr_t / Sc_t (the historical behaviour).
769  const bool consistent = use_KE && turbChoice.rans_consistent_diffusivities;
770 
771 #ifdef _OPENMP
772 #pragma omp parallel if (Gpu::notInLaunchRegion())
773 #endif
774  for ( MFIter mfi(eddyViscosity,TilingIfNotGPU()); mfi.isValid(); ++mfi)
775  {
776  Box bxcc = mfi.tilebox();
777 
778  const Array4<Real>& mu_turb = eddyViscosity.array(mfi);
779 
780  for (auto n = 1; n < EddyDiff::Mom_v; ++n) {
781  int offset = EddyDiff::Mom_v;
782  switch (n)
783  {
784  case EddyDiff::KE_h:
785  if (use_KE) {
786  ParallelFor(bxcc, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
787  {
788  int indx = n;
789  int indx_v = indx + offset;
790  mu_turb(i,j,k,indx) = mu_turb(i,j,k,EddyDiff::Mom_h) * fac_ptr[indx-1];
791  mu_turb(i,j,k,indx_v) = mu_turb(i,j,k,indx);
792  });
793  }
794  break;
795  default:
796  ParallelFor(bxcc, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
797  {
798  int indx = n;
799  int indx_v = indx + offset;
800 
801  if (consistent) {
802  mu_turb(i,j,k,indx) = mu_turb(i,j,k,EddyDiff::Theta_v);
803  if (indx_v != EddyDiff::Theta_v) {
804  mu_turb(i,j,k,indx_v) = mu_turb(i,j,k,EddyDiff::Theta_v);
805  }
806  } else {
807  mu_turb(i,j,k,indx) = mu_turb(i,j,k,EddyDiff::Mom_h) * fac_ptr[indx-1];
808 
809  // NOTE: Theta_v has already been set for the closure
810  if (!(indx_v == EddyDiff::Theta_v && use_KE)) {
811  mu_turb(i,j,k,indx_v) = mu_turb(i,j,k,indx);
812  }
813  }
814  });
815  break;
816  }
817  }
818  }
819 }
constexpr amrex::Real KAPPA
Definition: ERF_Constants.H:67
AMREX_ALWAYS_ASSERT_WITH_MESSAGE(m_cloud_chamber_config.active, "Cloud Chamber: initializer reached without a parsed configuration")
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real cmu_prime(amrex::Real Rt, amrex::Real Cmu0)
Definition: ERF_RANSClosure.H:127
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real turb_length(amrex::Real l_g, amrex::Real N2, amrex::Real tke, amrex::Real Cmu0_pow3, amrex::Real inv_Cb_sq, amrex::Real Rt_crit, amrex::Real Rt_min, amrex::Real eps)
Definition: ERF_RANSClosure.H:69
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real richardson(amrex::Real length, amrex::Real N2, amrex::Real tke, amrex::Real Cmu0_pow3)
Definition: ERF_RANSClosure.H:110
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real cmu(amrex::Real Rt, amrex::Real Cmu0)
Definition: ERF_RANSClosure.H:119
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real smooth_Rt(amrex::Real Rt, amrex::Real Rt_crit, amrex::Real Rt_min)
Definition: ERF_RANSClosure.H:39
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real geom_length(amrex::Real l_g_raw, amrex::Real l_g_max)
Definition: ERF_RANSClosure.H:24
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real dissipation(amrex::Real rho, amrex::Real Cmu0_pow3, amrex::Real tke, amrex::Real length)
Definition: ERF_RANSClosure.H:101
@ RANS_Rt
Definition: ERF_IndexDefines.H:269
@ RANS_cmu
Definition: ERF_IndexDefines.H:270
@ RANS_cmu_prime
Definition: ERF_IndexDefines.H:271
bool rans_consistent_diffusivities
Definition: ERF_TurbStruct.H:844
amrex::Real l_g_max
Maximum geometric length scale.
Definition: ERF_TurbStruct.H:808
amrex::Real tke_floor
Definition: ERF_TurbStruct.H:835
bool rans_lscale_from_pblh
Definition: ERF_TurbStruct.H:849
amrex::Real rans_lscale_min
Definition: ERF_TurbStruct.H:850
amrex::Real Rt_min
Minimum turbulent Reynolds number.
Definition: ERF_TurbStruct.H:807
amrex::Real Cb
One-equation RANS buoyancy coefficient.
Definition: ERF_TurbStruct.H:805
amrex::Real Rt_crit
Critical turbulent Reynolds number.
Definition: ERF_TurbStruct.H:806
amrex::Real Cmu0
One-equation RANS Cmu0 coefficient.
Definition: ERF_TurbStruct.H:804

Referenced by ComputeTurbulentViscosity().

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