ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
ERF_ComputeTurbulentViscosity.cpp File Reference
#include "ERF_SurfaceLayer.H"
#include "ERF_EddyViscosity.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 &Hfx1, MultiFab &Hfx2, MultiFab &Hfx3, 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 ComputeTurbulentViscosityRANS (Vector< std::unique_ptr< MultiFab >> &, const MultiFab &cons_in, const MultiFab &wdist, MultiFab &eddyViscosity, MultiFab &Hfx1, MultiFab &Hfx2, MultiFab &Hfx3, MultiFab &Diss, const Geometry &geom, bool use_terrain_fitted_coords, Vector< std::unique_ptr< MultiFab >> &, 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 (Real 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 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, bool vert_only)
 

Function Documentation

◆ ComputeTurbulentViscosity()

void ComputeTurbulentViscosity ( Real  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 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,
bool  vert_only 
)

Wrapper to compute turbulent viscosity with LES or PBL.

Parameters
[in]xvelvelocity in x-dir
[in]yvelvelocity in y-dir
[in]Tau_levstrain at this level
[in]cons_incell center conserved quantities
[out]eddyViscosityturbulent viscosity
[in]Hfx1heat flux in x-dir
[in]Hfx2heat flux in y-dir
[in]Hfx3heat flux in z-dir
[in]Dissdissipation of turbulent kinetic energy
[in]geomproblem geometry
[in]mapfacmap factors
[in]turbChoicecontainer with turbulence parameters
[in]mostpointer to Monin-Obukhov class if instantiated
[in]vert_onlyflag for vertical components of eddyViscosity
588 {
589  BL_PROFILE_VAR("ComputeTurbulentViscosity()",ComputeTurbulentViscosity);
590  //
591  // In LES mode, the turbulent viscosity is isotropic (unless mix_isotropic is set to false), so
592  // the LES model sets both horizontal and vertical viscosities
593  //
594  // In PBL mode, the primary purpose of the PBL model is to control vertical transport, so the PBL model sets the vertical viscosity.
595  // Optionally, the PBL model can be run in conjunction with an LES model that sets the horizontal viscosity
596  // (this isn’t truly LES, but the model form is the same as Smagorinsky).
597  //
598  // ComputeTurbulentViscosityLES populates the LES viscosity for both horizontal and vertical components.
599  // ComputeTurbulentViscosityPBL computes the PBL viscosity just for the vertical component.
600  //
601 
602  TurbChoice turbChoice = solverChoice.turbChoice[level];
603  const Real const_grav = solverChoice.gravity;
604 
605  if (!SurfLayer) {
606  AMREX_ALWAYS_ASSERT(!vert_only);
607  }
608 
609  bool impose_phys_bcs = false;
610 
611  if (turbChoice.les_type != LESType::None) {
612  impose_phys_bcs = true;
614  cons_in, eddyViscosity,
615  Hfx1, Hfx2, Hfx3, Diss,
616  geom, use_terrain_fitted_coords,
617  mapfac, z_phys_nd, turbChoice, const_grav,
618  SurfLayer, solverChoice.moisture_indices,
619  &xvel, &yvel);
620  }
621 
622  if (turbChoice.rans_type != RANSType::None) {
623  impose_phys_bcs = true;
625  cons_in, wdist,
626  eddyViscosity,
627  Hfx1, Hfx2, Hfx3, Diss,
628  geom, use_terrain_fitted_coords,
629  mapfac, z_phys_nd, turbChoice, const_grav,
630  SurfLayer, z_0);
631  }
632 
633  if (turbChoice.pbl_type == PBLType::MYJ) {
634  ComputeDiffusivityMYJ(dt, xvel, yvel, cons_in, eddyViscosity,
635  geom, turbChoice, SurfLayer,
636  use_terrain_fitted_coords, use_moisture,
637  level, bc_ptr, vert_only, z_phys_nd,
638  solverChoice.moisture_indices);
639  } else if (turbChoice.pbl_type == PBLType::MYNN25) {
640  ComputeDiffusivityMYNN25(xvel, yvel, cons_in, eddyViscosity,
641  geom, turbChoice, SurfLayer,
642  use_terrain_fitted_coords, use_moisture,
643  level, bc_ptr, vert_only, z_phys_nd,
644  solverChoice.moisture_indices);
645  } else if (turbChoice.pbl_type == PBLType::MYNNEDMF) {
646  ComputeDiffusivityMYNNEDMF(xvel, yvel, cons_in, eddyViscosity,
647  geom, turbChoice, SurfLayer,
648  use_terrain_fitted_coords, use_moisture,
649  level, bc_ptr, vert_only, z_phys_nd,
650  solverChoice.moisture_indices);
651  } else if (turbChoice.pbl_type == PBLType::YSU) {
652  ComputeDiffusivityYSU(xvel, yvel, cons_in, eddyViscosity,
653  geom, turbChoice, SurfLayer,
654  use_terrain_fitted_coords, use_moisture,
655  level, bc_ptr, vert_only, z_phys_nd,
656  solverChoice.moisture_indices);
657  } else if (turbChoice.pbl_type == PBLType::MRF) {
658  ComputeDiffusivityMRF(xvel, yvel, cons_in, eddyViscosity,
659  geom, turbChoice, SurfLayer,
660  use_terrain_fitted_coords, use_moisture,
661  level, bc_ptr, vert_only, z_phys_nd,
662  solverChoice.moisture_indices);
663  } else if (turbChoice.pbl_type == PBLType::SHOC) {
664  // NOTE: Nothing to do here. The SHOC class handles setting the vertical
665  // components of eddyDiffs in slow RHS pre.
666  }
667 
668  //
669  // At all levels we need to fill values outside the physical boundary for the LES coeffs.
670  // In addition, for all cases, if at level > 0, we want to fill fine ghost cell values that
671  // overlie coarse grid cells (and that are not in another fine valid region) with
672  // extrapolated values from the interior, rather than interpolating from the coarser level,
673  // since we may be using a different turbulence model there.
674  //
675  // Note: here "covered" refers to "covered by valid region of another grid at this level"
676  // Note: here "physbnd" refers to "cells outside the domain if not periodic"
677  // Note: here "interior" refers to "valid cells, i.e. inside 'my' grid"
678  //
679  {
680  int is_covered = 0;
681  int is_notcovered = 1;
682  int is_physbnd = 2;
683  int is_interior = 3;
684  iMultiFab cc_mask(eddyViscosity.boxArray(),eddyViscosity.DistributionMap(),1,1);
685  cc_mask.BuildMask(geom.Domain(), geom.periodicity(), is_covered, is_notcovered, is_physbnd, is_interior);
686 
687  Box domain = geom.Domain();
688  for (int i = 0; i < AMREX_SPACEDIM; ++i) {
689  if (geom.isPeriodic(i)) {
690  domain.grow(i,1);
691  }
692  }
693 
694  eddyViscosity.FillBoundary(geom.periodicity());
695 
696  int ncomp = eddyViscosity.nComp();
697 
698 #ifdef _OPENMP
699 #pragma omp parallel if (Gpu::notInLaunchRegion())
700 #endif
701  for (MFIter mfi(eddyViscosity); mfi.isValid(); ++mfi)
702  {
703  Box vbx = mfi.validbox();
704 
705  Box planex_lo = mfi.growntilebox(1); planex_lo.setBig(0, vbx.smallEnd(0)-1);
706  Box planey_lo = mfi.growntilebox(1); planey_lo.setBig(1, vbx.smallEnd(1)-1);
707  Box planez_lo = mfi.growntilebox(1); planez_lo.setBig(2, vbx.smallEnd(2)-1);
708 
709  Box planex_hi = mfi.growntilebox(1); planex_hi.setSmall(0, vbx.bigEnd(0)+1);
710  Box planey_hi = mfi.growntilebox(1); planey_hi.setSmall(1, vbx.bigEnd(1)+1);
711  Box planez_hi = mfi.growntilebox(1); planez_hi.setSmall(2, vbx.bigEnd(2)+1);
712 
713  int i_lo = vbx.smallEnd(0); int i_hi = vbx.bigEnd(0);
714  int j_lo = vbx.smallEnd(1); int j_hi = vbx.bigEnd(1);
715  int k_lo = vbx.smallEnd(2); int k_hi = vbx.bigEnd(2);
716 
717  const Array4<Real>& mu_turb = eddyViscosity.array(mfi);
718  const Array4<int>& mask_arr = cc_mask.array(mfi);
719 
720  auto domlo = lbound(domain);
721  auto domhi = ubound(domain);
722 
723  ParallelFor(planex_lo, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
724  {
725  int lj = amrex::min(amrex::max(j, domlo.y), domhi.y);
726  int lk = amrex::min(amrex::max(k, domlo.z), domhi.z);
727  if ((mask_arr(i,j,k) == is_notcovered && mask_arr(i_lo,lj,lk) != is_notcovered) ||
728  (mask_arr(i,j,k) == is_physbnd && i < domlo.x && impose_phys_bcs)) {
729  for (int n = 0; n < ncomp; n++) {
730  mu_turb(i,j,k,n) = mu_turb(i_lo,lj,lk,n);
731  }
732  }
733  });
734  ParallelFor(planex_hi, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
735  {
736  int lj = amrex::min(amrex::max(j, domlo.y), domhi.y);
737  int lk = amrex::min(amrex::max(k, domlo.z), domhi.z);
738  if ((mask_arr(i,j,k) == is_notcovered && mask_arr(i_hi,lj,lk) != is_notcovered) ||
739  (mask_arr(i,j,k) == is_physbnd && i > domhi.x && impose_phys_bcs)) {
740  for (int n = 0; n < ncomp; n++) {
741  mu_turb(i,j,k,n) = mu_turb(i_hi,lj,lk,n);
742  }
743  }
744  });
745  ParallelFor(planey_lo, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
746  {
747  int lk = amrex::min(amrex::max(k, domlo.z), domhi.z);
748  if ((mask_arr(i,j,k) == is_notcovered && mask_arr(i,j_lo,lk) != is_notcovered) ||
749  (mask_arr(i,j,k) == is_physbnd && j < domlo.y && impose_phys_bcs)) {
750  for (int n = 0; n < ncomp; n++) {
751  mu_turb(i,j,k,n) = mu_turb(i,j_lo,lk,n);
752  }
753  }
754  });
755  ParallelFor(planey_hi, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
756  {
757  int lk = amrex::min(amrex::max(k, domlo.z), domhi.z);
758  if ((mask_arr(i,j,k) == is_notcovered && mask_arr(i,j_hi,lk) != is_notcovered)||
759  (mask_arr(i,j,k) == is_physbnd && j > domhi.y && impose_phys_bcs)) {
760  for (int n = 0; n < ncomp; n++) {
761  mu_turb(i,j,k,n) = mu_turb(i,j_hi,lk,n);
762  }
763  }
764  });
765  ParallelFor(planez_lo, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
766  {
767  if ((mask_arr(i,j,k) == is_notcovered && mask_arr(i,j,k_lo) != is_notcovered) ||
768  (mask_arr(i,j,k) == is_physbnd && k < domlo.z && impose_phys_bcs)) {
769  if (mask_arr(i,j,k) == is_physbnd) {
770  }
771  for (int n = 0; n < ncomp; n++) {
772  mu_turb(i,j,k,n) = mu_turb(i,j,k_lo,n);
773  }
774  }
775  });
776  ParallelFor(planez_hi, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
777  {
778  if ((mask_arr(i,j,k) == is_notcovered && mask_arr(i,j,k_hi) != is_notcovered) ||
779  (mask_arr(i,j,k) == is_physbnd && k > domhi.z && impose_phys_bcs)) {
780  for (int n = 0; n < ncomp; n++) {
781  mu_turb(i,j,k,n) = mu_turb(i,j,k_hi,n);
782  }
783  }
784  });
785  } // mfi
786  eddyViscosity.FillBoundary(geom.periodicity());
787  }
788 }
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, int level, const BCRec *bc_ptr, bool, const std::unique_ptr< MultiFab > &z_phys_nd, const MoistureComponentIndices &moisture_indices)
Definition: ERF_ComputeDiffusivityMRF.cpp:12
void ComputeDiffusivityMYJ(Real 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 MoistureComponentIndices &moisture_indices)
Definition: ERF_ComputeDiffusivityMYJ.cpp:13
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 MoistureComponentIndices &moisture_indices)
Definition: ERF_ComputeDiffusivityMYNN25.cpp:13
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 MoistureComponentIndices &moisture_indices)
Definition: ERF_ComputeDiffusivityMYNNEDMF.cpp:4177
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 MoistureComponentIndices &moisture_indices)
Definition: ERF_ComputeDiffusivityYSU.cpp:11
void ComputeTurbulentViscosityRANS(Vector< std::unique_ptr< MultiFab >> &, const MultiFab &cons_in, const MultiFab &wdist, MultiFab &eddyViscosity, MultiFab &Hfx1, MultiFab &Hfx2, MultiFab &Hfx3, MultiFab &Diss, const Geometry &geom, bool use_terrain_fitted_coords, Vector< std::unique_ptr< MultiFab >> &, 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:349
void ComputeTurbulentViscosityLES(Vector< std::unique_ptr< MultiFab >> &Tau_lev, const MultiFab &cons_in, MultiFab &eddyViscosity, MultiFab &Hfx1, MultiFab &Hfx2, MultiFab &Hfx3, 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:30
void ComputeTurbulentViscosity(Real 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 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, bool vert_only)
Definition: ERF_ComputeTurbulentViscosity.cpp:570
for(int ori=0;ori< 2 *AMREX_SPACEDIM;ori++)
Definition: ERF_Implicit.H:9
TurbChoice turbChoice
Definition: ERF_SetupVertDiff.H:5
amrex::Real Real
Definition: ERF_ShocInterface.H:19
@ xvel
Definition: ERF_IndexDefines.H:141
@ yvel
Definition: ERF_IndexDefines.H:142
amrex::Real gravity
Definition: ERF_DataStruct.H:973
amrex::Vector< TurbChoice > turbChoice
Definition: ERF_DataStruct.H:905
MoistureComponentIndices moisture_indices
Definition: ERF_DataStruct.H:1039
Definition: ERF_TurbStruct.H:41
PBLType pbl_type
Definition: ERF_TurbStruct.H:390
RANSType rans_type
Definition: ERF_TurbStruct.H:387
LESType les_type
Definition: ERF_TurbStruct.H:345

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 &  Hfx1,
MultiFab &  Hfx2,
MultiFab &  Hfx3,
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
[in]Hfx1heat flux in x-dir
[in]Hfx2heat flux in y-dir
[in]Hfx3heat flux in z-dir
[in]Dissdissipation of turbulent kinetic energy
[in]geomproblem geometry
[in]mapfacmap factors
[in]turbChoicecontainer with turbulence parameters
[in]xvelx-direction velocity (for moist Ri correction)
[in]yvely-direction velocity (for moist Ri correction)
41 {
42  const GpuArray<Real, AMREX_SPACEDIM> cellSizeInv = geom.InvCellSizeArray();
43  const Box& domain = geom.Domain();
44 
45  Real inv_Pr_t = turbChoice.Pr_t_inv;
46  Real inv_Sc_t = turbChoice.Sc_t_inv;
47  Real inv_sigma_k = 1.0 / turbChoice.sigma_k;
48 
49  bool use_thetav_grad = (turbChoice.strat_type == StratType::thetav);
50  bool use_thetal_grad = (turbChoice.strat_type == StratType::thetal);
51 
52  bool isotropic = turbChoice.mix_isotropic;
53 
54  // SMAGORINSKY: Fill Kturb for momentum in horizontal and vertical
55  //***********************************************************************************
56  if (turbChoice.les_type == LESType::Smagorinsky)
57  {
58  Real Cs = turbChoice.Cs;
59  bool smag2d = turbChoice.smag2d;
60 
61  // Define variables required inside device lambdas (scalars only)
62  Real l_abs_g = const_grav;
63  Real l_Ri_crit = turbChoice.Ri_crit;
64  bool l_use_Ri_corr = turbChoice.use_Ri_correction;
65  bool l_has_xvel = (xvel != nullptr);
66  bool l_has_yvel = (yvel != nullptr);
67 
68 #ifdef _OPENMP
69 #pragma omp parallel if (Gpu::notInLaunchRegion())
70 #endif
71  for (MFIter mfi(eddyViscosity,TilingIfNotGPU()); mfi.isValid(); ++mfi)
72  {
73  Box bxcc = mfi.growntilebox(1) & domain;
74  const Array4<Real>& mu_turb = eddyViscosity.array(mfi);
75  const Array4<Real>& hfx_x = Hfx1.array(mfi);
76  const Array4<Real>& hfx_y = Hfx2.array(mfi);
77  const Array4<Real>& hfx_z = Hfx3.array(mfi);
78  const Array4<Real const >& cell_data = cons_in.array(mfi);
79  Array4<Real const> tau11 = Tau_lev[TauType::tau11]->array(mfi);
80  Array4<Real const> tau22 = Tau_lev[TauType::tau22]->array(mfi);
81  Array4<Real const> tau33 = Tau_lev[TauType::tau33]->array(mfi);
82  Array4<Real const> tau12 = Tau_lev[TauType::tau12]->array(mfi);
83  Array4<Real const> tau13 = Tau_lev[TauType::tau13]->array(mfi);
84  Array4<Real const> tau23 = Tau_lev[TauType::tau23]->array(mfi);
85  Array4<Real const> mf_u = mapfac[MapFacType::u_x]->const_array(mfi);
86  Array4<Real const> mf_v = mapfac[MapFacType::v_y]->const_array(mfi);
87  Array4<Real const> z_nd_arr = z_phys_nd->const_array(mfi);
88 
89  Array4<Real const> u_arr = (l_has_xvel) ? xvel->const_array(mfi) : Array4<Real const>{};
90  Array4<Real const> v_arr = (l_has_yvel) ? yvel->const_array(mfi) : Array4<Real const>{};
91 
92  ParallelFor(bxcc, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
93  {
94  // =====================================================================
95  // 1. STRAIN RATE MAGNITUDE CALCULATION
96  // =====================================================================
97  Real SmnSmn;
98  if (smag2d) {
99  SmnSmn = ComputeSmnSmn2D(i,j,k,tau11,tau22,tau12);
100  } else {
101  SmnSmn = ComputeSmnSmn(i,j,k,tau11,tau22,tau33,tau12,tau13,tau23);
102  }
103  Real strain_rate_magnitude = std::sqrt(2.0 * SmnSmn);
104 
105  // =====================================================================
106  // 2. GRID SCALE CALCULATION (filter width Δ)
107  // =====================================================================
108  Real dxInv = cellSizeInv[0];
109  Real dyInv = cellSizeInv[1];
110  Real dzInv = cellSizeInv[2];
111  if (use_terrain_fitted_coords) {
112  dzInv /= Compute_h_zeta_AtCellCenter(i,j,k, cellSizeInv, z_nd_arr);
113  }
114 
115  Real Delta;
116  Real DeltaH;
117  if (isotropic) {
118  Real cellVolMsf = 1.0 / (dxInv * mf_u(i,j,0) * dyInv * mf_v(i,j,0) * dzInv);
119  Delta = std::cbrt(cellVolMsf);
120  DeltaH = Delta;
121  } else {
122  Delta = 1.0 / dzInv;
123  DeltaH = std::sqrt(1.0 / (dxInv * mf_u(i,j,0) * dyInv * mf_v(i,j,0)));
124  }
125 
126  Real rho = cell_data(i, j, k, Rho_comp);
127  Real CsDeltaSqr_h = Cs * Cs * DeltaH * DeltaH;
128  Real CsDeltaSqr_v = Cs * Cs * Delta * Delta;
129 
130  Real nu_turb_base_h = CsDeltaSqr_h * strain_rate_magnitude;
131  Real nu_turb_base_v = CsDeltaSqr_v * strain_rate_magnitude;
132 
133  Real stability_factor = 1.0;
134 
135  if (l_use_Ri_corr && l_has_xvel && l_has_yvel) {
136  Real N2 = ComputeN2(i, j, k, dzInv, l_abs_g, cell_data, moisture_indices);
137  Real S2_vert = ComputeVerticalShear2(i, j, k, dzInv, u_arr, v_arr);
138  Real Ri = ComputeRichardson(N2, S2_vert);
139  stability_factor = StabilityFunction(Ri, l_Ri_crit);
140  }
141 
142  if (isotropic) {
143  mu_turb(i, j, k, EddyDiff::Mom_h) = rho * nu_turb_base_h * stability_factor;
144  mu_turb(i, j, k, EddyDiff::Mom_v) = rho * nu_turb_base_v * stability_factor;
145  } else {
146  mu_turb(i, j, k, EddyDiff::Mom_h) = rho * nu_turb_base_h;
147  mu_turb(i, j, k, EddyDiff::Mom_v) = rho * nu_turb_base_v * stability_factor;
148  }
149 
150  Real dtheta_dz = 0.5 * ( cell_data(i,j,k+1,RhoTheta_comp)/cell_data(i,j,k+1,Rho_comp)
151  - cell_data(i,j,k-1,RhoTheta_comp)/cell_data(i,j,k-1,Rho_comp) )*dzInv;
152  hfx_x(i,j,k) = 0.0;
153  hfx_y(i,j,k) = 0.0;
154  hfx_z(i,j,k) = -inv_Pr_t * mu_turb(i,j,k,EddyDiff::Mom_v) * dtheta_dz;
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(0.0, l_C_e - 1.9*l_C_k);
166  const Real l_abs_g = const_grav;
167 
168  const bool use_ref_theta = (turbChoice.theta_ref > 0);
169  const Real l_inv_theta0 = (use_ref_theta) ? 1.0 / turbChoice.theta_ref : 1.0;
170 
171 #ifdef _OPENMP
172 #pragma omp parallel if (Gpu::notInLaunchRegion())
173 #endif
174  for ( MFIter mfi(eddyViscosity,TilingIfNotGPU()); mfi.isValid(); ++mfi)
175  {
176  Box bxcc = mfi.tilebox();
177 
178  const Array4<Real>& mu_turb = eddyViscosity.array(mfi);
179  const Array4<Real>& hfx_x = Hfx1.array(mfi);
180  const Array4<Real>& hfx_y = Hfx2.array(mfi);
181  const Array4<Real>& hfx_z = Hfx3.array(mfi);
182  const Array4<Real>& diss = Diss.array(mfi);
183 
184  const Array4<Real const > &cell_data = cons_in.array(mfi);
185 
186  Array4<Real const> mf_u = mapfac[MapFacType::u_x]->const_array(mfi);
187  Array4<Real const> mf_v = mapfac[MapFacType::v_y]->const_array(mfi);
188 
189  Array4<Real const> z_nd_arr = z_phys_nd->const_array(mfi);
190 
191  ParallelFor(bxcc, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
192  {
193  Real dxInv = cellSizeInv[0];
194  Real dyInv = cellSizeInv[1];
195  Real dzInv = cellSizeInv[2];
196  if (use_terrain_fitted_coords) {
197  // the terrain grid is only deformed in z for now
198  dzInv /= Compute_h_zeta_AtCellCenter(i,j,k, cellSizeInv, z_nd_arr);
199  }
200  Real Delta;
201  if (isotropic) {
202  Real cellVolMsf = 1.0 / (dxInv * mf_u(i,j,0) * dyInv * mf_v(i,j,0) * dzInv);
203  Delta = std::cbrt(cellVolMsf);
204  } else {
205  Delta = 1.0 / dzInv;
206  }
207 
208  Real dtheta_dz;
209  if (use_thetav_grad) {
210  dtheta_dz = 0.5 * ( GetThetav(i, j, k+1, cell_data, moisture_indices)
211  - GetThetav(i, j, k-1, cell_data, moisture_indices) )*dzInv;
212  } else if (use_thetal_grad) {
213  dtheta_dz = 0.5 * ( GetThetal(i, j, k+1, cell_data, moisture_indices)
214  - GetThetal(i, j, k-1, cell_data, moisture_indices) )*dzInv;
215  } else {
216  dtheta_dz = 0.5 * ( cell_data(i, j, k+1, RhoTheta_comp) / cell_data(i, j, k+1, Rho_comp)
217  - cell_data(i, j, k-1, RhoTheta_comp) / cell_data(i, j, k-1, Rho_comp) )*dzInv;
218  }
219 
220  // Calculate stratification-dependent mixing length (Deardorff 1980, Eqn. 10a)
221  Real E = amrex::max(cell_data(i,j,k,RhoKE_comp)/cell_data(i,j,k,Rho_comp),Real(0.0));
222  Real stratification = l_abs_g * dtheta_dz * l_inv_theta0;
223  if (!use_ref_theta) {
224  // l_inv_theta0 == 1, divide by actual theta
225  stratification *= cell_data(i,j,k,Rho_comp) /
226  cell_data(i,j,k,RhoTheta_comp);
227  }
228 
229  // Following WRF, the stratification effects are applied to the vertical length scales
230  // in the case of anistropic mixing
231  Real length;
233  if (stratification <= eps) {
234  length = Delta; // cbrt(dx*dy*dz) -or- dz
235  } else {
236  length = 0.76 * std::sqrt(E / amrex::max(stratification,eps));
237  // mixing length should be _reduced_ for stable stratification
238  length = amrex::min(length, Delta);
239  // following WRF, make sure the mixing length isn't too small
240  length = amrex::max(length, 0.001 * Delta);
241  }
242 
243  Real DeltaH = (isotropic) ? length : std::sqrt(1.0 / (dxInv * mf_u(i,j,0) * dyInv * mf_v(i,j,0)));
244 
245  Real Pr_inv_v = (1. + 2.*length/Delta);
246  Real Pr_inv_h = (isotropic) ? Pr_inv_v : inv_Pr_t;
247 
248  // Calculate eddy diffusivities
249  // K = rho * C_k * l * KE^(1/2)
250  mu_turb(i,j,k,EddyDiff::Mom_h) = cell_data(i,j,k,Rho_comp) * l_C_k * DeltaH * std::sqrt(E);
251  mu_turb(i,j,k,EddyDiff::Mom_v) = cell_data(i,j,k,Rho_comp) * l_C_k * length * std::sqrt(E);
252  // KH = (1 + 2*l/delta) * mu_turb
253  mu_turb(i,j,k,EddyDiff::Theta_h) = Pr_inv_h * mu_turb(i,j,k,EddyDiff::Mom_h);
254  mu_turb(i,j,k,EddyDiff::Theta_v) = Pr_inv_v * mu_turb(i,j,k,EddyDiff::Mom_v);
255  // Store lengthscale for TKE source terms
256  mu_turb(i,j,k,EddyDiff::Turb_lengthscale) = length;
257 
258  // Calculate SFS quantities
259  // - dissipation
260  Real Ce;
261  if ((l_C_e_wall > 0) && (k==0)) {
262  Ce = l_C_e_wall;
263  } else {
264  Ce = 1.9*l_C_k + Ce_lcoeff*length / Delta;
265  }
266  diss(i,j,k) = cell_data(i,j,k,Rho_comp) * Ce * std::pow(E,1.5) / length;
267 
268  // - heat flux
269  // (Note: If using SurfaceLayer, the value at k=0 will
270  // be overwritten)
271  hfx_x(i,j,k) = 0.0;
272  hfx_y(i,j,k) = 0.0;
273  hfx_z(i,j,k) = -mu_turb(i,j,k,EddyDiff::Theta_v) * dtheta_dz; // (rho*w)' theta' [kg m^-2 s^-1 K]
274  });
275  }
276  }
277 
278  // Extrapolate Kturb in x/y, fill remaining elements (relevant to lev==0)
279  //***********************************************************************************
280  int ngc(1);
281  // EddyDiff mapping : Theta_h KE_h Scalar_h Q_h
282  Vector<Real> Factors = {inv_Pr_t, inv_sigma_k, inv_Sc_t, inv_Sc_t}; // alpha = mu/Pr
283  Gpu::AsyncVector<Real> d_Factors; d_Factors.resize(Factors.size());
284  Gpu::copy(Gpu::hostToDevice, Factors.begin(), Factors.end(), d_Factors.begin());
285  Real* fac_ptr = d_Factors.data();
286 
287  const bool use_KE = ( turbChoice.les_type == LESType::Deardorff );
288 
289 #ifdef _OPENMP
290 #pragma omp parallel if (Gpu::notInLaunchRegion())
291 #endif
292  for ( MFIter mfi(eddyViscosity,TilingIfNotGPU()); mfi.isValid(); ++mfi)
293  {
294  Box bxcc = mfi.tilebox();
295  Box planex = bxcc; planex.setSmall(0, 1); planex.setBig(0, ngc); planex.grow(1,1);
296  Box planey = bxcc; planey.setSmall(1, 1); planey.setBig(1, ngc); planey.grow(0,1);
297  bxcc.growLo(0,ngc); bxcc.growHi(0,ngc);
298  bxcc.growLo(1,ngc); bxcc.growHi(1,ngc);
299 
300  const Array4<Real>& mu_turb = eddyViscosity.array(mfi);
301 
302  for (auto n = 1; n < (EddyDiff::NumDiffs-1)/2; ++n) {
303  int offset = (EddyDiff::NumDiffs-1)/2;
304  switch (n)
305  {
306  case EddyDiff::KE_h:
307  if (use_KE) {
308  ParallelFor(bxcc, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
309  {
310  int indx = n;
311  int indx_v = indx + offset;
312  mu_turb(i,j,k,indx) = mu_turb(i,j,k,EddyDiff::Mom_h) * fac_ptr[indx-1];
313  mu_turb(i,j,k,indx_v) = mu_turb(i,j,k,indx);
314  });
315  }
316  break;
317  default:
318  ParallelFor(bxcc, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
319  {
320  int indx = n;
321  int indx_v = indx + offset;
322 
323  // NOTE: Theta_h, Theta_v have already been set for Deardorff
324  if (!(indx_v == EddyDiff::Theta_v && use_KE)) {
325  mu_turb(i,j,k,indx) = mu_turb(i,j,k,EddyDiff::Mom_h) * fac_ptr[indx-1];
326  mu_turb(i,j,k,indx_v) = mu_turb(i,j,k,indx);
327  }
328  });
329  break;
330  }
331  }
332  }
333 }
if(l_use_mynn &&start_comp<=RhoKE_comp &&end_comp >=RhoKE_comp)
Definition: ERF_AddQKESources.H:2
@ tau12
Definition: ERF_DataStruct.H:30
@ tau23
Definition: ERF_DataStruct.H:30
@ tau33
Definition: ERF_DataStruct.H:30
@ tau22
Definition: ERF_DataStruct.H:30
@ tau11
Definition: ERF_DataStruct.H:30
@ tau13
Definition: ERF_DataStruct.H:30
@ v_y
Definition: ERF_DataStruct.H:23
@ u_x
Definition: ERF_DataStruct.H:22
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:90
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:63
#define Rho_comp
Definition: ERF_IndexDefines.H:36
#define RhoTheta_comp
Definition: ERF_IndexDefines.H:37
#define RhoKE_comp
Definition: ERF_IndexDefines.H:38
AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real GetThetal(int i, int j, int k, const amrex::Array4< amrex::Real const > &cell_data, const MoistureComponentIndices &moisture_indices)
Definition: ERF_MoistUtils.H:101
AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real GetThetav(int i, int j, int k, const amrex::Array4< amrex::Real const > &cell_data, const MoistureComponentIndices &moisture_indices)
Definition: ERF_MoistUtils.H:72
AMREX_FORCE_INLINE IntVect offset(const int face_dir, const int normal)
Definition: ERF_ReadBndryPlanes.cpp:28
AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real StabilityFunction(amrex::Real Ri, amrex::Real Ri_crit)
Definition: ERF_RichardsonNumber.H:246
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:201
AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real ComputeRichardson(amrex::Real N2_moist, amrex::Real S2_vert)
Definition: ERF_RichardsonNumber.H:230
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:126
Real l_inv_theta0
Definition: ERF_SetupDiff.H:17
AMREX_FORCE_INLINE AMREX_GPU_DEVICE 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:53
@ Theta_v
Definition: ERF_IndexDefines.H:176
@ Turb_lengthscale
Definition: ERF_IndexDefines.H:180
@ Mom_h
Definition: ERF_IndexDefines.H:170
@ Mom_v
Definition: ERF_IndexDefines.H:175
@ NumDiffs
Definition: ERF_IndexDefines.H:181
@ Theta_h
Definition: ERF_IndexDefines.H:171
@ KE_h
Definition: ERF_IndexDefines.H:172
@ rho
Definition: ERF_Kessler.H:22
real(c_double), parameter epsilon
Definition: ERF_module_model_constants.F90:12
bool smag2d
Definition: ERF_TurbStruct.H:357
amrex::Real sigma_k
Definition: ERF_TurbStruct.H:373
amrex::Real Sc_t_inv
Definition: ERF_TurbStruct.H:353
amrex::Real Ri_crit
Definition: ERF_TurbStruct.H:384
StratType strat_type
Definition: ERF_TurbStruct.H:378
amrex::Real Ck
Definition: ERF_TurbStruct.H:362
bool use_Ri_correction
Definition: ERF_TurbStruct.H:383
amrex::Real Cs
Definition: ERF_TurbStruct.H:356
amrex::Real Pr_t_inv
Definition: ERF_TurbStruct.H:349
amrex::Real Ce_wall
Definition: ERF_TurbStruct.H:361
amrex::Real Ce
Definition: ERF_TurbStruct.H:360
amrex::Real theta_ref
Definition: ERF_TurbStruct.H:376
bool mix_isotropic
Definition: ERF_TurbStruct.H:381

Referenced by ComputeTurbulentViscosity().

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

◆ ComputeTurbulentViscosityRANS()

void ComputeTurbulentViscosityRANS ( Vector< std::unique_ptr< MultiFab >> &  ,
const MultiFab &  cons_in,
const MultiFab &  wdist,
MultiFab &  eddyViscosity,
MultiFab &  Hfx1,
MultiFab &  Hfx2,
MultiFab &  Hfx3,
MultiFab &  Diss,
const Geometry &  geom,
bool  use_terrain_fitted_coords,
Vector< std::unique_ptr< MultiFab >> &  ,
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]Tau_levstrain at this level
[in]cons_incell center conserved quantities
[out]eddyViscosityturbulent viscosity
[in]Hfx1heat flux in x-dir
[in]Hfx2heat flux in y-dir
[in]Hfx3heat flux in z-dir
[in]Dissdissipation of turbulent kinetic energy
[in]geomproblem geometry
[in]mapfacmap factor
[in]turbChoicecontainer with turbulence parameters
365 {
366  const GpuArray<Real, AMREX_SPACEDIM> cellSizeInv = geom.InvCellSizeArray();
367  const bool use_SurfLayer = (SurfLayer != nullptr);
368 
369  Real inv_Pr_t = turbChoice.Pr_t_inv;
370  Real inv_Sc_t = turbChoice.Sc_t_inv;
371  Real inv_sigma_k = 1.0 / turbChoice.sigma_k;
372 
373  // One-Equation k model (Axell & Liungman 2001, Environ Fluid Mech)
374  //***********************************************************************************
375  if (turbChoice.rans_type == RANSType::kEqn)
376  {
377  const Real Cmu0 = turbChoice.Cmu0;
378  const Real Cmu0_pow3 = Cmu0 * Cmu0 * Cmu0;
379  const Real inv_Cb_sq = 1.0 / (turbChoice.Cb * turbChoice.Cb);
380  const Real Rt_crit = turbChoice.Rt_crit;
381  const Real Rt_min = turbChoice.Rt_min;
382  const Real l_g_max = turbChoice.l_g_max;
383  const Real abs_g = const_grav;
384 
385  const bool use_ref_theta = (turbChoice.theta_ref > 0);
386  const Real inv_theta0 = (use_ref_theta) ? 1.0 / turbChoice.theta_ref : 1.0;
387 
388 #ifdef _OPENMP
389 #pragma omp parallel if (Gpu::notInLaunchRegion())
390 #endif
391  for ( MFIter mfi(eddyViscosity,TilingIfNotGPU()); mfi.isValid(); ++mfi)
392  {
393  Box bxcc = mfi.tilebox();
394 
395  const Array4<Real const>& d_arr = wdist.const_array(mfi);
396  const Array4<Real const>& z0_arr = (use_SurfLayer) ? z_0->const_array(mfi) : Array4<Real const>{};
397 
398  const Array4<Real>& mu_turb = eddyViscosity.array(mfi);
399  const Array4<Real>& hfx_x = Hfx1.array(mfi);
400  const Array4<Real>& hfx_y = Hfx2.array(mfi);
401  const Array4<Real>& hfx_z = Hfx3.array(mfi);
402  const Array4<Real>& diss = Diss.array(mfi);
403 
404  const Array4<Real const>& cell_data = cons_in.array(mfi);
405 
406  const Array4<Real const>& z_nd_arr = z_phys_nd->const_array(mfi);
407 
408  ParallelFor(bxcc, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
409  {
411  Real tke = amrex::max(cell_data(i,j,k,RhoKE_comp)/cell_data(i,j,k,Rho_comp), eps);
412 
413  // Estimate stratification
414  Real dzInv = cellSizeInv[2];
415  if (use_terrain_fitted_coords) {
416  // the terrain grid is only deformed in z for now
417  dzInv /= Compute_h_zeta_AtCellCenter(i,j,k, cellSizeInv, z_nd_arr);
418  }
419  Real dtheta_dz = 0.5 * ( cell_data(i,j,k+1,RhoTheta_comp)/cell_data(i,j,k+1,Rho_comp)
420  - cell_data(i,j,k-1,RhoTheta_comp)/cell_data(i,j,k-1,Rho_comp) )*dzInv;
421  Real N2 = abs_g * inv_theta0 * dtheta_dz; // Brunt–Väisälä frequency squared
422  if (!use_ref_theta) {
423  // inv_theta0 == 1, divide by actual theta
424  N2 *= cell_data(i,j,k,Rho_comp) /
425  cell_data(i,j,k,RhoTheta_comp);
426  }
427 
428  // Geometric length scale (AL01, Eqn. 22)
429  Real l_g = (z0_arr) ? KAPPA * (d_arr(i, j, k) + z0_arr(i, j, 0))
430  : KAPPA * d_arr(i, j, k);
431 
432  // Enforce a maximum value
433  l_g = l_g_max * l_g / (l_g_max + l_g);
434 
435  // Turbulent Richardson number (AL01, Eqn. 29)
436  // using the old dissipation value
437  Real diss0 = max(diss(i, j, k) / cell_data(i, j, k, Rho_comp),
438  eps);
439  Real Rt = tke*tke * N2 / diss0;
440 
441  // Turbulent length scale
442  Real length;
443  if (std::abs(N2) <= eps) {
444  length = l_g;
445  } else if (N2 > eps) {
446  // Stable (AL01, Eqn. 26)
447  length = std::sqrt(1.0 /
448  (1.0 / (l_g * l_g) + inv_Cb_sq * N2 / tke));
449  } else {
450  // Unstable (AL01, Eqn. 28)
451  length = l_g * std::sqrt(1.0 - Cmu0_pow3*Cmu0_pow3 * inv_Cb_sq * Rt);
452  }
453  mu_turb(i, j, k, EddyDiff::Turb_lengthscale) = length;
454 
455  // Burchard & Petersen smoothing function
456  Rt = (Rt >= Rt_crit) ? Rt
457  : std::max(Rt,
458  Rt - std::pow(Rt - Rt_crit, 2) /
459  (Rt + Rt_min - 2*Rt_crit));
460 
461  // Stability functions
462  // Note: These use the smoothed turbulent Richardson number
463  Real cmu = (Cmu0 + 0.108*Rt)
464  / (1.0 + 0.308*Rt + 0.00837*Rt*Rt); // (AL01, Eqn. 31)
465  Real cmu_prime = Cmu0 / (1 + 0.277*Rt); // (AL01, Eqn. 32)
466 
467  // Calculate eddy diffusivities
468  // K = rho * nu_t = rho * c_mu * tke^(1/2) * length
469  Real nut = cmu * std::sqrt(tke) * length; // eddy viscosity
470  Real nut_prime = cmu_prime / cmu * nut; // eddy diffusivity
471  mu_turb(i, j, k, EddyDiff::Mom_h) = cell_data(i, j, k, Rho_comp) * nut;
472  mu_turb(i, j, k, EddyDiff::Mom_v) = mu_turb(i, j, k, EddyDiff::Mom_h);
473  mu_turb(i, j, k, EddyDiff::Theta_v) = cell_data(i, j, k, Rho_comp) * nut_prime;
474 
475  // Calculate SFS quantities
476  // - dissipation (AL01 Eqn. 19)
477  diss(i, j, k) = cell_data(i, j, k, Rho_comp) * Cmu0_pow3 * std::pow(tke,1.5) / length;
478 
479  // - heat flux
480  // (Note: If using SurfaceLayer, the value at k=0 will
481  // be overwritten)
482  hfx_x(i, j, k) = 0.0;
483  hfx_y(i, j, k) = 0.0;
484  // Note: buoyant production = g/theta0 * hfx == -nut_prime * N^2 (c.f. AL01 Eqn. 15)
485  // = nut_prime * g/theta0 * dtheta/dz
486  // ==> hfx = nut_prime * dtheta/dz
487  // Our convention is such that dtheta/dz < 0 gives a positive
488  // (upward) heat flux.
489  hfx_z(i, j, k) = -mu_turb(i, j, k, EddyDiff::Theta_v) * dtheta_dz; // (rho*w)' theta' [kg m^-2 s^-1 K]
490  });
491  }
492  }
493 
494  // Extrapolate Kturb in x/y, fill remaining elements (relevant to lev==0)
495  //***********************************************************************************
496  int ngc(1);
497  // EddyDiff mapping : Theta_h KE_h Scalar_h Q_h
498  Vector<Real> Factors = {inv_Pr_t, inv_sigma_k, inv_Sc_t, inv_Sc_t}; // alpha = mu/Pr
499  Gpu::AsyncVector<Real> d_Factors; d_Factors.resize(Factors.size());
500  Gpu::copy(Gpu::hostToDevice, Factors.begin(), Factors.end(), d_Factors.begin());
501  Real* fac_ptr = d_Factors.data();
502 
503  const bool use_KE = ( turbChoice.rans_type == RANSType::kEqn );
504 
505 #ifdef _OPENMP
506 #pragma omp parallel if (Gpu::notInLaunchRegion())
507 #endif
508  for ( MFIter mfi(eddyViscosity,TilingIfNotGPU()); mfi.isValid(); ++mfi)
509  {
510  Box bxcc = mfi.tilebox();
511  Box planex = bxcc; planex.setSmall(0, 1); planex.setBig(0, ngc); planex.grow(1,1);
512  Box planey = bxcc; planey.setSmall(1, 1); planey.setBig(1, ngc); planey.grow(0,1);
513  bxcc.growLo(0,ngc); bxcc.growHi(0,ngc);
514  bxcc.growLo(1,ngc); bxcc.growHi(1,ngc);
515 
516  const Array4<Real>& mu_turb = eddyViscosity.array(mfi);
517 
518  for (auto n = 1; n < (EddyDiff::NumDiffs-1)/2; ++n) {
519  int offset = (EddyDiff::NumDiffs-1)/2;
520  switch (n)
521  {
522  case EddyDiff::KE_h:
523  if (use_KE) {
524  ParallelFor(bxcc, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
525  {
526  int indx = n;
527  int indx_v = indx + offset;
528  mu_turb(i,j,k,indx) = mu_turb(i,j,k,EddyDiff::Mom_h) * fac_ptr[indx-1];
529  mu_turb(i,j,k,indx_v) = mu_turb(i,j,k,indx);
530  });
531  }
532  break;
533  default:
534  ParallelFor(bxcc, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
535  {
536  int indx = n;
537  int indx_v = indx + offset;
538 
539  mu_turb(i,j,k,indx) = mu_turb(i,j,k,EddyDiff::Mom_h) * fac_ptr[indx-1];
540 
541  // NOTE: Theta_v has already been set for Deardorff
542  if (!(indx_v == EddyDiff::Theta_v && use_KE)) {
543  mu_turb(i,j,k,indx_v) = mu_turb(i,j,k,indx);
544  }
545  });
546  break;
547  }
548  }
549  }
550 }
constexpr amrex::Real KAPPA
Definition: ERF_Constants.H:20
amrex::Real Rt_min
Definition: ERF_TurbStruct.H:368
amrex::Real Cmu0
Definition: ERF_TurbStruct.H:365
amrex::Real Cb
Definition: ERF_TurbStruct.H:366
amrex::Real Rt_crit
Definition: ERF_TurbStruct.H:367
amrex::Real l_g_max
Definition: ERF_TurbStruct.H:369

Referenced by ComputeTurbulentViscosity().

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