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_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)
840 {
841  BL_PROFILE_VAR("ComputeTurbulentViscosity()",ComputeTurbulentViscosity);
842  //
843  // In LES mode, the turbulent viscosity is isotropic (unless mix_isotropic is set to false), so
844  // the LES model sets both horizontal and vertical viscosities
845  //
846  // In PBL mode, the primary purpose of the PBL model is to control vertical transport, so the PBL model sets the vertical viscosity.
847  // Optionally, the PBL model can be run in conjunction with an LES model that sets the horizontal viscosity
848  // (this isn’t truly LES, but the model form is the same as Smagorinsky).
849  //
850  // ComputeTurbulentViscosityLES populates the LES viscosity for both horizontal and vertical components.
851  // ComputeTurbulentViscosityPBL computes the PBL viscosity just for the vertical component.
852  //
853 
854  TurbChoice turbChoice = solverChoice.turbChoice[level];
855  const Real const_grav = solverChoice.gravity;
856 
857  if (!SurfLayer) {
858  AMREX_ALWAYS_ASSERT(!vert_only);
859  }
860 
861  bool impose_phys_bcs = true;
862 
863  if (turbChoice.les_type != LESType::None) {
864  if (solverChoice.terrain_type == TerrainType::EB) {
866  cons_in, eddyViscosity,
867  Hfx1, Hfx2, Hfx3,
868  geom,
869  mapfac, turbChoice, const_grav,
870  solverChoice,
871  SurfLayer, solverChoice.moisture_indices,
872  ebfact, &xvel, &yvel);
873  } else {
875  cons_in, eddyViscosity,
876  Diss,
877  geom, use_terrain_fitted_coords,
878  mapfac, z_phys_nd, turbChoice, const_grav,
879  SurfLayer, solverChoice.moisture_indices,
880  &xvel, &yvel);
881  }
882  }
883 
884  if (turbChoice.rans_type != RANSType::None) {
885  ComputeTurbulentViscosityRANS(level, cons_in, wdist,
886  eddyViscosity,
887  Diss,
888  geom, use_terrain_fitted_coords,
889  z_phys_nd, turbChoice, const_grav,
890  SurfLayer, z_0);
891  }
892 
893  if (turbChoice.pbl_type == PBLType::MYJ) {
894  ComputeDiffusivityMYJ(dt, xvel, yvel, cons_in, eddyViscosity,
895  geom, turbChoice, SurfLayer,
896  use_terrain_fitted_coords, use_moisture,
897  level, bc_ptr, vert_only, z_phys_nd, z_phys_cc,
898  solverChoice.moisture_indices);
899  } else if (turbChoice.pbl_type == PBLType::MYNN25) {
900  ComputeDiffusivityMYNN25(xvel, yvel, cons_in, eddyViscosity,
901  geom, turbChoice, SurfLayer,
902  use_terrain_fitted_coords, use_moisture,
903  level, bc_ptr, vert_only, z_phys_nd, z_phys_cc,
904  solverChoice.moisture_indices);
905  } else if (turbChoice.pbl_type == PBLType::MYNNEDMF) {
906  ComputeDiffusivityMYNNEDMF(xvel, yvel, cons_in, eddyViscosity,
907  geom, turbChoice, SurfLayer,
908  use_terrain_fitted_coords, use_moisture,
909  level, bc_ptr, vert_only, z_phys_nd, z_phys_cc,
910  solverChoice.moisture_indices);
911  } else if (turbChoice.pbl_type == PBLType::YSU) {
912  ComputeDiffusivityYSU(xvel, yvel, cons_in, eddyViscosity,
913  geom, turbChoice, SurfLayer,
914  use_terrain_fitted_coords, use_moisture,
915  level, bc_ptr, vert_only, z_phys_nd, z_phys_cc,
916  solverChoice.moisture_indices);
917  } else if (turbChoice.pbl_type == PBLType::MRF) {
918  ComputeDiffusivityMRF(xvel, yvel, cons_in, eddyViscosity,
919  geom, turbChoice, SurfLayer,
920  use_terrain_fitted_coords, use_moisture,
921  level, bc_ptr, vert_only, z_phys_nd, z_phys_cc,
922  solverChoice.moisture_indices, terrain_blank);
923  } else if (turbChoice.pbl_type == PBLType::YSUNew) {
924  ComputeDiffusivityYSUNew(xvel, yvel, cons_in, eddyViscosity,
925  geom, turbChoice, SurfLayer,
926  use_terrain_fitted_coords, use_moisture,
927  level, bc_ptr, vert_only, z_phys_nd, z_phys_cc,
928  solverChoice.moisture_indices,
929  qheating_rates, terrain_blank);
930  } else if (turbChoice.uses_shoc_family()) {
931  // NOTE: Nothing to do here. The SHOC class handles setting the vertical
932  // components of eddyDiffs in slow RHS pre.
933  }
934 
935  //
936  // At all levels we need to fill values outside the physical boundary for the LES coeffs.
937  // In addition, for all cases, if at level > 0, we want to fill fine ghost cell values that
938  // overlie coarse grid cells (and that are not in another fine valid region) with
939  // extrapolated values from the interior, rather than interpolating from the coarser level,
940  // since we may be using a different turbulence model there.
941  //
942  // Note: here "covered" refers to "covered by valid region of another grid at this level"
943  // Note: here "physbnd" refers to "cells outside the domain if not periodic"
944  // Note: here "interior" refers to "valid cells, i.e. inside 'my' grid"
945  //
946  {
947  int is_covered = 0;
948  int is_notcovered = 1;
949  int is_physbnd = 2;
950  int is_interior = 3;
951  iMultiFab cc_mask(eddyViscosity.boxArray(),eddyViscosity.DistributionMap(),1,1);
952  cc_mask.BuildMask(geom.Domain(), geom.periodicity(), is_covered, is_notcovered, is_physbnd, is_interior);
953 
954  Box domain = geom.Domain();
955  for (int i = 0; i < AMREX_SPACEDIM; ++i) {
956  if (geom.isPeriodic(i)) {
957  domain.grow(i,1);
958  }
959  }
960 
961  eddyViscosity.FillBoundary(geom.periodicity());
962 
963  int ncomp = eddyViscosity.nComp();
964 
965 #ifdef _OPENMP
966 #pragma omp parallel if (Gpu::notInLaunchRegion())
967 #endif
968  for (MFIter mfi(eddyViscosity); mfi.isValid(); ++mfi)
969  {
970  Box vbx = mfi.validbox();
971 
972  Box planex_lo = mfi.growntilebox(1); planex_lo.setBig(0, vbx.smallEnd(0)-1);
973  Box planey_lo = mfi.growntilebox(1); planey_lo.setBig(1, vbx.smallEnd(1)-1);
974  Box planez_lo = mfi.growntilebox(1); planez_lo.setBig(2, vbx.smallEnd(2)-1);
975 
976  Box planex_hi = mfi.growntilebox(1); planex_hi.setSmall(0, vbx.bigEnd(0)+1);
977  Box planey_hi = mfi.growntilebox(1); planey_hi.setSmall(1, vbx.bigEnd(1)+1);
978  Box planez_hi = mfi.growntilebox(1); planez_hi.setSmall(2, vbx.bigEnd(2)+1);
979 
980  int i_lo = vbx.smallEnd(0); int i_hi = vbx.bigEnd(0);
981  int j_lo = vbx.smallEnd(1); int j_hi = vbx.bigEnd(1);
982  int k_lo = vbx.smallEnd(2); int k_hi = vbx.bigEnd(2);
983 
984  const Array4<Real>& mu_turb = eddyViscosity.array(mfi);
985  const Array4<int>& mask_arr = cc_mask.array(mfi);
986 
987  auto domlo = lbound(domain);
988  auto domhi = ubound(domain);
989 
990  ParallelFor(planex_lo, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
991  {
992  int lj = amrex::min(amrex::max(j, domlo.y), domhi.y);
993  int lk = amrex::min(amrex::max(k, domlo.z), domhi.z);
994  if ((mask_arr(i,j,k) == is_notcovered && mask_arr(i_lo,lj,lk) != is_notcovered) ||
995  (mask_arr(i,j,k) == is_physbnd && i < domlo.x && impose_phys_bcs)) {
996  for (int n = 0; n < ncomp; n++) {
997  mu_turb(i,j,k,n) = mu_turb(i_lo,lj,lk,n);
998  }
999  }
1000  });
1001  ParallelFor(planex_hi, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
1002  {
1003  int lj = amrex::min(amrex::max(j, domlo.y), domhi.y);
1004  int lk = amrex::min(amrex::max(k, domlo.z), domhi.z);
1005  if ((mask_arr(i,j,k) == is_notcovered && mask_arr(i_hi,lj,lk) != is_notcovered) ||
1006  (mask_arr(i,j,k) == is_physbnd && i > domhi.x && impose_phys_bcs)) {
1007  for (int n = 0; n < ncomp; n++) {
1008  mu_turb(i,j,k,n) = mu_turb(i_hi,lj,lk,n);
1009  }
1010  }
1011  });
1012  ParallelFor(planey_lo, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
1013  {
1014  int lk = amrex::min(amrex::max(k, domlo.z), domhi.z);
1015  if ((mask_arr(i,j,k) == is_notcovered && mask_arr(i,j_lo,lk) != is_notcovered) ||
1016  (mask_arr(i,j,k) == is_physbnd && j < domlo.y && impose_phys_bcs)) {
1017  for (int n = 0; n < ncomp; n++) {
1018  mu_turb(i,j,k,n) = mu_turb(i,j_lo,lk,n);
1019  }
1020  }
1021  });
1022  ParallelFor(planey_hi, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
1023  {
1024  int lk = amrex::min(amrex::max(k, domlo.z), domhi.z);
1025  if ((mask_arr(i,j,k) == is_notcovered && mask_arr(i,j_hi,lk) != is_notcovered)||
1026  (mask_arr(i,j,k) == is_physbnd && j > domhi.y && impose_phys_bcs)) {
1027  for (int n = 0; n < ncomp; n++) {
1028  mu_turb(i,j,k,n) = mu_turb(i,j_hi,lk,n);
1029  }
1030  }
1031  });
1032  ParallelFor(planez_lo, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
1033  {
1034  if ((mask_arr(i,j,k) == is_notcovered && mask_arr(i,j,k_lo) != is_notcovered) ||
1035  (mask_arr(i,j,k) == is_physbnd && k < domlo.z && impose_phys_bcs)) {
1036  for (int n = 0; n < ncomp; n++) {
1037  mu_turb(i,j,k,n) = mu_turb(i,j,k_lo,n);
1038  }
1039  }
1040  });
1041  ParallelFor(planez_hi, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
1042  {
1043  if ((mask_arr(i,j,k) == is_notcovered && mask_arr(i,j,k_hi) != is_notcovered) ||
1044  (mask_arr(i,j,k) == is_physbnd && k > domhi.z && impose_phys_bcs)) {
1045  for (int n = 0; n < ncomp; n++) {
1046  mu_turb(i,j,k,n) = mu_turb(i,j,k_hi,n);
1047  }
1048  }
1049  });
1050  } // mfi
1051 
1052  eddyViscosity.FillBoundary(geom.periodicity());
1053  }
1054 }
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:36
void ComputeDiffusivityMYJ(double dt, const MultiFab &xvel, const MultiFab &yvel, MultiFab &cons_in, MultiFab &eddyViscosity, const Geometry &geom, const TurbChoice &, std::unique_ptr< SurfaceLayer > &, bool use_terrain_fitted_coords, bool, int, 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, 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:29
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:340
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:818
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:586
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:33
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
@ xvel
Definition: ERF_IndexDefines.H:215
@ yvel
Definition: ERF_IndexDefines.H:216
amrex::Vector< TurbChoice > turbChoice
Turbulence options for each AMR level.
Definition: ERF_DataStruct.H:1974
amrex::Real gravity
Effective gravitational acceleration.
Definition: ERF_DataStruct.H:2060
static TerrainType terrain_type
Terrain or immersed-boundary representation.
Definition: ERF_DataStruct.H:1949
MoistureComponentIndices moisture_indices
Index map of the moisture data carried by the active scheme: conserved-state components for the speci...
Definition: ERF_DataStruct.H:2263
Definition: ERF_TurbStruct.H:115
RANSType rans_type
Selected RANS closure.
Definition: ERF_TurbStruct.H:754
bool uses_shoc_family() const noexcept
Query whether this level uses any SHOC-family PBL scheme.
Definition: ERF_TurbStruct.H:803
LESType les_type
Selected LES closure.
Definition: ERF_TurbStruct.H:712
PBLType pbl_type
Selected PBL closure.
Definition: ERF_TurbStruct.H:779

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

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 
)
352 {
353  const GpuArray<Real, AMREX_SPACEDIM> cellSizeInv = geom.InvCellSizeArray();
354  Real dxInv = cellSizeInv[0];
355  Real dyInv = cellSizeInv[1];
356  Real dzInv = cellSizeInv[2];
357 
358  const Box& domain = geom.Domain();
359 
360  Real inv_Pr_t = turbChoice.Pr_t_inv;
361  Real inv_Sc_t = turbChoice.Sc_t_inv;
362  Real inv_sigma_k = one / turbChoice.sigma_k;
363 
364  bool isotropic = turbChoice.mix_isotropic;
365 
366  AMREX_ASSERT(turbChoice.les_type == LESType::Smagorinsky);
367 
368  // SMAGORINSKY: Fill Kturb for momentum in horizontal and vertical
369  //***********************************************************************************
370  if (turbChoice.les_type == LESType::Smagorinsky)
371  {
372  Real Cs = turbChoice.Cs;
373 
374  // Define variables required inside device lambdas (scalars only)
375  Real l_abs_g = const_grav;
376  Real l_Ri_crit = turbChoice.Ri_crit;
377  bool l_use_Ri_corr = turbChoice.use_Ri_correction;
378  bool l_has_xvel = (xvel != nullptr);
379  bool l_has_yvel = (yvel != nullptr);
380 
381 #ifdef _OPENMP
382 #pragma omp parallel if (Gpu::notInLaunchRegion())
383 #endif
384  for (MFIter mfi(eddyViscosity,TilingIfNotGPU()); mfi.isValid(); ++mfi)
385  {
386  Box bxcc = mfi.growntilebox(1) & domain;
387  const Array4<Real>& mu_turb = eddyViscosity.array(mfi);
388  const Array4<Real>& hfx_x = Hfx1.array(mfi);
389  const Array4<Real>& hfx_y = Hfx2.array(mfi);
390  const Array4<Real>& hfx_z = Hfx3.array(mfi);
391  const Array4<Real const >& cell_data = cons_in.array(mfi);
392  Array4<Real const> tau11 = Tau_lev[TauType::tau11]->array(mfi);
393  Array4<Real const> tau22 = Tau_lev[TauType::tau22]->array(mfi);
394  Array4<Real const> tau33 = Tau_lev[TauType::tau33]->array(mfi);
395  Array4<Real const> tau12 = Tau_lev[TauType::tau12]->array(mfi);
396  Array4<Real const> tau13 = Tau_lev[TauType::tau13]->array(mfi);
397  Array4<Real const> tau23 = Tau_lev[TauType::tau23]->array(mfi);
398  Array4<Real const> mf_u = mapfac[MapFacType::u_x]->const_array(mfi);
399  Array4<Real const> mf_v = mapfac[MapFacType::v_y]->const_array(mfi);
400 
401  Array4<Real const> u_arr = (l_has_xvel) ? xvel->const_array(mfi) : Array4<Real const>{};
402  Array4<Real const> v_arr = (l_has_yvel) ? yvel->const_array(mfi) : Array4<Real const>{};
403 
404  Array4<const EBCellFlag> c_cflag = (ebfact.get_const_factory())->getMultiEBCellFlagFab()[mfi].const_array();
405  Array4<const EBCellFlag> u_cflag = (ebfact.get_u_const_factory())->getMultiEBCellFlagFab()[mfi].const_array();
406  Array4<const EBCellFlag> v_cflag = (ebfact.get_v_const_factory())->getMultiEBCellFlagFab()[mfi].const_array();
407  Array4<const EBCellFlag> w_cflag = (ebfact.get_w_const_factory())->getMultiEBCellFlagFab()[mfi].const_array();
408  Array4<const Real > u_vfrac = (ebfact.get_u_const_factory())->getVolFrac().const_array(mfi);
409  Array4<const Real > v_vfrac = (ebfact.get_v_const_factory())->getVolFrac().const_array(mfi);
410 
411  ParallelFor(bxcc, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
412  {
413  if (!c_cflag(i,j,k).isCovered()) {
414  // =====================================================================
415  // 1. STRAIN RATE MAGNITUDE CALCULATION
416  // =====================================================================
417  Real SmnSmn = zero;
418  if (c_cflag(i,j,k).isRegular()) {
419  SmnSmn = ComputeSmnSmn(i,j,k,tau11,tau22,tau33,tau12,tau13,tau23);
420  } else if (c_cflag(i,j,k).isSingleValued()) {
421  SmnSmn = ComputeSmnSmn_EB(i,j,k,tau11,tau22,tau33,tau12,tau13,tau23,c_cflag,u_cflag,v_cflag,w_cflag);
422  }
423  Real strain_rate_magnitude = std::sqrt(two * SmnSmn);
424 
425  // =====================================================================
426  // 2. GRID SCALE CALCULATION (filter width Δ)
427  // =====================================================================
428  Real Delta;
429  Real DeltaH;
430  if (isotropic) {
431  Real cellVolMsf = one / (dxInv * mf_u(i,j,0) * dyInv * mf_v(i,j,0) * dzInv);
432  Delta = std::cbrt(cellVolMsf);
433  DeltaH = Delta;
434  } else {
435  Delta = one / dzInv;
436  DeltaH = std::sqrt(one / (dxInv * mf_u(i,j,0) * dyInv * mf_v(i,j,0)));
437  }
438 
439  Real rho = cell_data(i, j, k, Rho_comp);
440  Real CsDeltaSqr_h = Cs * Cs * DeltaH * DeltaH;
441  Real CsDeltaSqr_v = Cs * Cs * Delta * Delta;
442 
443  Real nu_turb_base_h = CsDeltaSqr_h * strain_rate_magnitude;
444  Real nu_turb_base_v = CsDeltaSqr_v * strain_rate_magnitude;
445 
446  Real stability_factor = one;
447 
448  if (l_use_Ri_corr && l_has_xvel && l_has_yvel) {
449  Real N2 = zero;
450  Real S2_vert = zero;
451  if (c_cflag(i,j,k).isRegular()) {
452  N2 = ComputeN2(i, j, k, dzInv, l_abs_g, cell_data, moisture_indices);
453  S2_vert = ComputeVerticalShear2(i, j, k, dzInv, u_arr, v_arr);
454  } else if (c_cflag(i,j,k).isSingleValued()) {
455  N2 = ComputeN2_EB(i, j, k, c_cflag, dzInv, l_abs_g, cell_data, moisture_indices);
456  S2_vert = ComputeVerticalShear2_EB(i, j, k, c_cflag, u_vfrac, v_vfrac, dzInv, u_arr, v_arr);
457  }
458  Real Ri = ComputeRichardson(N2, S2_vert);
459  stability_factor = StabilityFunction(Ri, l_Ri_crit);
460  }
461 
462  if (isotropic) {
463  mu_turb(i, j, k, EddyDiff::Mom_h) = rho * nu_turb_base_h * stability_factor;
464  mu_turb(i, j, k, EddyDiff::Mom_v) = rho * nu_turb_base_v * stability_factor;
465  } else {
466  mu_turb(i, j, k, EddyDiff::Mom_h) = rho * nu_turb_base_h;
467  mu_turb(i, j, k, EddyDiff::Mom_v) = rho * nu_turb_base_v * stability_factor;
468  }
469 
470  Real dtheta_dz = zero;
471 
472  if (c_cflag(i,j,k).isSingleValued() && c_cflag(i,j,k+1).isCovered()) {
473  amrex::Real theta = cell_data(i, j, k, RhoTheta_comp) / rho;
474  amrex::Real theta_km1 = cell_data(i,j,k-1,RhoTheta_comp)/cell_data(i,j,k-1,Rho_comp);
475  amrex::Real theta_km2 = cell_data(i,j,k-2,RhoTheta_comp)/cell_data(i,j,k-2,Rho_comp);
476  dtheta_dz = (three*theta - Real(4.)*theta_km1 + theta_km2) * myhalf * dzInv;
477  } else if (c_cflag(i,j,k).isSingleValued() && c_cflag(i,j,k-1).isCovered()) {
478  amrex::Real theta = cell_data(i, j, k, RhoTheta_comp) / rho;
479  amrex::Real theta_kp1 = cell_data(i,j,k+1,RhoTheta_comp)/cell_data(i,j,k+1,Rho_comp);
480  amrex::Real theta_kp2 = cell_data(i,j,k+2,RhoTheta_comp)/cell_data(i,j,k+2,Rho_comp);
481  dtheta_dz = (-theta_kp2 + Real(4.)*theta_kp1 - three*theta) * myhalf * dzInv;
482  } else if (!c_cflag(i,j,k).isCovered()) {
483  amrex::Real theta_km1 = cell_data(i,j,k-1,RhoTheta_comp)/cell_data(i,j,k-1,Rho_comp);
484  amrex::Real theta_kp1 = cell_data(i,j,k+1,RhoTheta_comp)/cell_data(i,j,k+1,Rho_comp);
485  dtheta_dz = myhalf * (theta_kp1 - theta_km1) * dzInv;
486  }
487 
488  // The EB diffusion does not write hfx_z, so on EB terrain this is
489  // the only writer of the SGS heat flux output (the surface layer
490  // overwrites the bottom). Elsewhere the theta diffusion writes the
491  // face fluxes and the closures do not store a heat flux. This write
492  // keeps the z-nodal/cell-centred mismatch noted above; moving the EB
493  // heat-flux output to the EB face fluxes belongs with that diffusion.
494  hfx_x(i,j,k) = zero;
495  hfx_y(i,j,k) = zero;
496  hfx_z(i,j,k) = -inv_Pr_t * mu_turb(i,j,k,EddyDiff::Mom_v) * dtheta_dz;
497 
498  } else {
499 
500  hfx_x(i,j,k) = zero;
501  hfx_y(i,j,k) = zero;
502  hfx_z(i,j,k) = zero;
503  }// End of if not covered
504  });
505  }
506  }
507 
508  // Extrapolate Kturb in x/y, fill remaining elements (relevant to lev==0)
509  //***********************************************************************************
510  int ngc(1);
511  // EddyDiff mapping : Theta_h KE_h Scalar_h Q_h
512  Vector<Real> Factors = {inv_Pr_t, inv_sigma_k, inv_Sc_t, inv_Sc_t}; // alpha = mu/Pr
513  Gpu::AsyncVector<Real> d_Factors; d_Factors.resize(Factors.size());
514  Gpu::copy(Gpu::hostToDevice, Factors.begin(), Factors.end(), d_Factors.begin());
515  Real* fac_ptr = d_Factors.data();
516 
517  const bool use_KE = ( turbChoice.les_type == LESType::Deardorff );
518 
519 #ifdef _OPENMP
520 #pragma omp parallel if (Gpu::notInLaunchRegion())
521 #endif
522  for ( MFIter mfi(eddyViscosity,TilingIfNotGPU()); mfi.isValid(); ++mfi)
523  {
524  Box bxcc = mfi.tilebox();
525  Box planex = bxcc; planex.setSmall(0, 1); planex.setBig(0, ngc); planex.grow(1,1);
526  Box planey = bxcc; planey.setSmall(1, 1); planey.setBig(1, ngc); planey.grow(0,1);
527  //
528  // NOTE: growntilebox, not the tilebox grown by hand. Growing by hand pushes every tile
529  // ngc cells past its own share of the grid, so once the grid is tiled two tiles
530  // store to the same mu_turb cells -- a data race under OpenMP, even though both
531  // store the same value. The union of the tiles, and the values stored, are
532  // unchanged, and this is identical to the old expression with one tile per grid.
533  //
534  bxcc = mfi.growntilebox(IntVect(ngc,ngc,0));
535 
536  const Array4<Real>& mu_turb = eddyViscosity.array(mfi);
537 
538  for (auto n = 1; n < EddyDiff::Mom_v; ++n) {
539  int offset = EddyDiff::Mom_v;
540  switch (n)
541  {
542  case EddyDiff::KE_h:
543  if (use_KE) {
544  ParallelFor(bxcc, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
545  {
546  int indx = n;
547  int indx_v = indx + offset;
548  mu_turb(i,j,k,indx) = mu_turb(i,j,k,EddyDiff::Mom_h) * fac_ptr[indx-1];
549  mu_turb(i,j,k,indx_v) = mu_turb(i,j,k,EddyDiff::Mom_v) * fac_ptr[indx-1];
550  });
551  }
552  break;
553  default:
554  ParallelFor(bxcc, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
555  {
556  int indx = n;
557  int indx_v = indx + offset;
558 
559  // NOTE: Theta_h, Theta_v have already been set for Deardorff
560  if (!(indx_v == EddyDiff::Theta_v && use_KE)) {
561  mu_turb(i,j,k,indx) = mu_turb(i,j,k,EddyDiff::Mom_h) * fac_ptr[indx-1];
562  mu_turb(i,j,k,indx_v) = mu_turb(i,j,k,EddyDiff::Mom_v) * fac_ptr[indx-1];
563  }
564  });
565  break;
566  }
567  }
568  }
569 }
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:32
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)
Definition: ERF_RichardsonNumber.H:336
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
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_SLM.H:19

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

Referenced by ComputeTurbulentViscosity().

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