ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
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 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, 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 > &)
 
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, 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 FArrayBox *z_0)
 
void ComputeTurbulentViscosity (const MultiFab &xvel, const MultiFab &yvel, Vector< std::unique_ptr< MultiFab >> &Tau_lev, const 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 FArrayBox *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 ( const MultiFab &  xvel,
const MultiFab &  yvel,
Vector< std::unique_ptr< MultiFab >> &  Tau_lev,
const 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 FArrayBox *  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
533 {
534  BL_PROFILE_VAR("ComputeTurbulentViscosity()",ComputeTurbulentViscosity);
535  //
536  // In LES mode, the turbulent viscosity is isotropic (unless mix_isotropic is set to false), so
537  // the LES model sets both horizontal and vertical viscosities
538  //
539  // In PBL mode, the primary purpose of the PBL model is to control vertical transport, so the PBL model sets the vertical viscosity.
540  // Optionally, the PBL model can be run in conjunction with an LES model that sets the horizontal viscosity
541  // (this isn’t truly LES, but the model form is the same as Smagorinsky).
542  //
543  // ComputeTurbulentViscosityLES populates the LES viscosity for both horizontal and vertical components.
544  // ComputeTurbulentViscosityPBL computes the PBL viscosity just for the vertical component.
545  //
546 
547  TurbChoice turbChoice = solverChoice.turbChoice[level];
548  const Real const_grav = solverChoice.gravity;
549 
550  if (!SurfLayer) {
551  AMREX_ALWAYS_ASSERT(!vert_only);
552  }
553 
554  bool impose_phys_bcs = false;
555 
556  if (turbChoice.les_type != LESType::None) {
557  impose_phys_bcs = true;
559  cons_in, eddyViscosity,
560  Hfx1, Hfx2, Hfx3, Diss,
561  geom, use_terrain_fitted_coords,
562  mapfac, z_phys_nd, turbChoice, const_grav,
563  SurfLayer);
564  }
565 
566  if (turbChoice.rans_type != RANSType::None) {
567  impose_phys_bcs = true;
569  cons_in, wdist,
570  eddyViscosity,
571  Hfx1, Hfx2, Hfx3, Diss,
572  geom, use_terrain_fitted_coords,
573  mapfac, z_phys_nd, turbChoice, const_grav,
574  SurfLayer, z_0);
575  }
576 
577  if (turbChoice.pbl_type == PBLType::MYNN25) {
578  ComputeDiffusivityMYNN25(xvel, yvel, cons_in, eddyViscosity,
579  geom, turbChoice, SurfLayer,
580  use_terrain_fitted_coords, use_moisture,
581  level, bc_ptr, vert_only, z_phys_nd,
582  solverChoice.RhoQv_comp, solverChoice.RhoQc_comp,
583  solverChoice.RhoQr_comp);
584  } else if (turbChoice.pbl_type == PBLType::MYNNEDMF) {
585  ComputeDiffusivityMYNNEDMF(xvel, yvel, cons_in, eddyViscosity,
586  geom, turbChoice, SurfLayer,
587  use_terrain_fitted_coords, use_moisture,
588  level, bc_ptr, vert_only, z_phys_nd,
589  solverChoice.RhoQv_comp, solverChoice.RhoQc_comp,
590  solverChoice.RhoQr_comp);
591  } else if (turbChoice.pbl_type == PBLType::YSU) {
592  ComputeDiffusivityYSU(xvel, yvel, cons_in, eddyViscosity,
593  geom, turbChoice, SurfLayer,
594  use_terrain_fitted_coords, use_moisture,
595  level, bc_ptr, vert_only, z_phys_nd);
596  }
597 
598  //
599  // At all levels we need to fill values outside the physical boundary for the LES coeffs.
600  // In addition, for all cases, if at level > 0, we want to fill fine ghost cell values that
601  // overlie coarse grid cells (and that are not in another fine valid region) with
602  // extrapolated values from the interior, rather than interpolating from the coarser level,
603  // since we may be using a different turbulence model there.
604  //
605  // Note: here "covered" refers to "covered by valid region of another grid at this level"
606  // Note: here "physbnd" refers to "cells outside the domain if not periodic"
607  // Note: here "interior" refers to "valid cells, i.e. inside 'my' grid"
608  //
609  {
610  int is_covered = 0;
611  int is_notcovered = 1;
612  int is_physbnd = 2;
613  int is_interior = 3;
614  iMultiFab cc_mask(eddyViscosity.boxArray(),eddyViscosity.DistributionMap(),1,1);
615  cc_mask.BuildMask(geom.Domain(), geom.periodicity(), is_covered, is_notcovered, is_physbnd, is_interior);
616 
617  Box domain = geom.Domain();
618  for (int i = 0; i < AMREX_SPACEDIM; ++i) {
619  if (geom.isPeriodic(i)) {
620  domain.grow(i,1);
621  }
622  }
623 
624  eddyViscosity.FillBoundary(geom.periodicity());
625 
626  int ncomp = eddyViscosity.nComp();
627 
628 #ifdef _OPENMP
629 #pragma omp parallel if (Gpu::notInLaunchRegion())
630 #endif
631  for (MFIter mfi(eddyViscosity); mfi.isValid(); ++mfi)
632  {
633  Box vbx = mfi.validbox();
634 
635  Box planex_lo = mfi.growntilebox(1); planex_lo.setBig(0, vbx.smallEnd(0)-1);
636  Box planey_lo = mfi.growntilebox(1); planey_lo.setBig(1, vbx.smallEnd(1)-1);
637  Box planez_lo = mfi.growntilebox(1); planez_lo.setBig(2, vbx.smallEnd(2)-1);
638 
639  Box planex_hi = mfi.growntilebox(1); planex_hi.setSmall(0, vbx.bigEnd(0)+1);
640  Box planey_hi = mfi.growntilebox(1); planey_hi.setSmall(1, vbx.bigEnd(1)+1);
641  Box planez_hi = mfi.growntilebox(1); planez_hi.setSmall(2, vbx.bigEnd(2)+1);
642 
643  int i_lo = vbx.smallEnd(0); int i_hi = vbx.bigEnd(0);
644  int j_lo = vbx.smallEnd(1); int j_hi = vbx.bigEnd(1);
645  int k_lo = vbx.smallEnd(2); int k_hi = vbx.bigEnd(2);
646 
647  const Array4<Real>& mu_turb = eddyViscosity.array(mfi);
648  const Array4<int>& mask_arr = cc_mask.array(mfi);
649 
650  auto domlo = lbound(domain);
651  auto domhi = ubound(domain);
652 
653  ParallelFor(planex_lo, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
654  {
655  int lj = amrex::min(amrex::max(j, domlo.y), domhi.y);
656  int lk = amrex::min(amrex::max(k, domlo.z), domhi.z);
657  if ((mask_arr(i,j,k) == is_notcovered) ||
658  (mask_arr(i,j,k) == is_physbnd && i < domlo.x && impose_phys_bcs)) {
659  for (int n = 0; n < ncomp; n++) {
660  mu_turb(i,j,k,n) = mu_turb(i_lo,lj,lk,n);
661  }
662  }
663  });
664  ParallelFor(planex_hi, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
665  {
666  int lj = amrex::min(amrex::max(j, domlo.y), domhi.y);
667  int lk = amrex::min(amrex::max(k, domlo.z), domhi.z);
668  if ((mask_arr(i,j,k) == is_notcovered) ||
669  (mask_arr(i,j,k) == is_physbnd && i > domhi.x && impose_phys_bcs)) {
670  for (int n = 0; n < ncomp; n++) {
671  mu_turb(i,j,k,n) = mu_turb(i_hi,lj,lk,n);
672  }
673  }
674  });
675  ParallelFor(planey_lo, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
676  {
677  int lk = amrex::min(amrex::max(k, domlo.z), domhi.z);
678  if ((mask_arr(i,j,k) == is_notcovered) ||
679  (mask_arr(i,j,k) == is_physbnd && j < domlo.y && impose_phys_bcs)) {
680  for (int n = 0; n < ncomp; n++) {
681  mu_turb(i,j,k,n) = mu_turb(i,j_lo,lk,n);
682  }
683  }
684  });
685  ParallelFor(planey_hi, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
686  {
687  int lk = amrex::min(amrex::max(k, domlo.z), domhi.z);
688  if ((mask_arr(i,j,k) == is_notcovered) ||
689  (mask_arr(i,j,k) == is_physbnd && j > domhi.y && impose_phys_bcs)) {
690  for (int n = 0; n < ncomp; n++) {
691  mu_turb(i,j,k,n) = mu_turb(i,j_hi,lk,n);
692  }
693  }
694  });
695  ParallelFor(planez_lo, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
696  {
697  if ((mask_arr(i,j,k) == is_notcovered) ||
698  (mask_arr(i,j,k) == is_physbnd && k < domlo.z && impose_phys_bcs)) {
699  if (mask_arr(i,j,k) == is_physbnd) {
700  }
701  for (int n = 0; n < ncomp; n++) {
702  mu_turb(i,j,k,n) = mu_turb(i,j,k_lo,n);
703  }
704  }
705  });
706  ParallelFor(planez_hi, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
707  {
708  if ((mask_arr(i,j,k) == is_notcovered) ||
709  (mask_arr(i,j,k) == is_physbnd && k > domhi.z && impose_phys_bcs)) {
710  for (int n = 0; n < ncomp; n++) {
711  mu_turb(i,j,k,n) = mu_turb(i,j,k_hi,n);
712  }
713  }
714  });
715  } // mfi
716  eddyViscosity.FillBoundary(geom.periodicity());
717  }
718 }
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 int RhoQv_comp, const int RhoQc_comp, const int RhoQr_comp)
Definition: ERF_ComputeDiffusivityMYNN25.cpp:11
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 int RhoQv_comp, const int RhoQc_comp, const int RhoQr_comp)
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)
Definition: ERF_ComputeDiffusivityYSU.cpp:11
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, 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 > &)
Definition: ERF_ComputeTurbulentViscosity.cpp:26
void ComputeTurbulentViscosity(const MultiFab &xvel, const MultiFab &yvel, Vector< std::unique_ptr< MultiFab >> &Tau_lev, const 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 FArrayBox *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:516
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, 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 FArrayBox *z_0)
Definition: ERF_ComputeTurbulentViscosity.cpp:301
@ xvel
Definition: ERF_IndexDefines.H:141
@ yvel
Definition: ERF_IndexDefines.H:142
int RhoQr_comp
Definition: ERF_DataStruct.H:786
amrex::Real gravity
Definition: ERF_DataStruct.H:714
int RhoQc_comp
Definition: ERF_DataStruct.H:780
int RhoQv_comp
Definition: ERF_DataStruct.H:779
amrex::Vector< TurbChoice > turbChoice
Definition: ERF_DataStruct.H:676
Definition: ERF_TurbStruct.H:31
PBLType pbl_type
Definition: ERF_TurbStruct.H:276
RANSType rans_type
Definition: ERF_TurbStruct.H:273
LESType les_type
Definition: ERF_TurbStruct.H:236

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,
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 > &   
)

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
34 {
35  const GpuArray<Real, AMREX_SPACEDIM> cellSizeInv = geom.InvCellSizeArray();
36  const Box& domain = geom.Domain();
37 
38  Real inv_Pr_t = turbChoice.Pr_t_inv;
39  Real inv_Sc_t = turbChoice.Sc_t_inv;
40  Real inv_sigma_k = 1.0 / turbChoice.sigma_k;
41 
42  bool isotropic = turbChoice.mix_isotropic;
43 
44  // SMAGORINSKY: Fill Kturb for momentum in horizontal and vertical
45  //***********************************************************************************
46  if (turbChoice.les_type == LESType::Smagorinsky)
47  {
48  Real Cs = turbChoice.Cs;
49  bool smag2d = turbChoice.smag2d;
50 
51 #ifdef _OPENMP
52 #pragma omp parallel if (Gpu::notInLaunchRegion())
53 #endif
54  for (MFIter mfi(eddyViscosity,TilingIfNotGPU()); mfi.isValid(); ++mfi)
55  {
56  // NOTE: This gets us the lateral ghost cells for lev>0; which
57  // have been filled from FP Two Levels.
58  Box bxcc = mfi.growntilebox(1) & domain;
59 
60  const Array4<Real>& mu_turb = eddyViscosity.array(mfi);
61  const Array4<Real>& hfx_x = Hfx1.array(mfi);
62  const Array4<Real>& hfx_y = Hfx2.array(mfi);
63  const Array4<Real>& hfx_z = Hfx3.array(mfi);
64  const Array4<Real const > &cell_data = cons_in.array(mfi);
65 
66  Array4<Real const> tau11 = Tau_lev[TauType::tau11]->array(mfi);
67  Array4<Real const> tau22 = Tau_lev[TauType::tau22]->array(mfi);
68  Array4<Real const> tau33 = Tau_lev[TauType::tau33]->array(mfi);
69  Array4<Real const> tau12 = Tau_lev[TauType::tau12]->array(mfi);
70  Array4<Real const> tau13 = Tau_lev[TauType::tau13]->array(mfi);
71  Array4<Real const> tau23 = Tau_lev[TauType::tau23]->array(mfi);
72 
73  Array4<Real const> mf_u = mapfac[MapFacType::u_x]->const_array(mfi);
74  Array4<Real const> mf_v = mapfac[MapFacType::v_x]->const_array(mfi);
75 
76  Array4<Real const> z_nd_arr = z_phys_nd->const_array(mfi);
77 
78  ParallelFor(bxcc, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
79  {
80  Real SmnSmn;
81  if (smag2d) {
82  SmnSmn = ComputeSmnSmn2D(i,j,k,tau11,tau22,tau12);
83  } else {
84  SmnSmn = ComputeSmnSmn(i,j,k,tau11,tau22,tau33,tau12,tau13,tau23);
85  }
86  Real dxInv = cellSizeInv[0];
87  Real dyInv = cellSizeInv[1];
88  Real dzInv = cellSizeInv[2];
89  if (use_terrain) {
90  // the terrain grid is only deformed in z for now
91  dzInv /= Compute_h_zeta_AtCellCenter(i,j,k, cellSizeInv, z_nd_arr);
92  }
93 
94  if (isotropic) {
95  Real cellVolMsf = 1.0 / (dxInv * mf_u(i,j,0) * dyInv * mf_v(i,j,0) * dzInv);
96  Real Delta = std::cbrt(cellVolMsf);
97  Real CsDeltaSqr = Cs*Cs*Delta*Delta;
98 
99  mu_turb(i, j, k, EddyDiff::Mom_h) = CsDeltaSqr * cell_data(i, j, k, Rho_comp) * std::sqrt(2.0*SmnSmn);
100  mu_turb(i, j, k, EddyDiff::Mom_v) = mu_turb(i, j, k, EddyDiff::Mom_h);
101  } else {
102  Real DeltaH = std::sqrt(1.0 / (dxInv * mf_u(i,j,0) * dyInv * mf_v(i,j,0)));
103  Real Kmh = Cs*Cs*DeltaH*DeltaH * cell_data(i, j, k, Rho_comp) * std::sqrt(2.0*SmnSmn);
104  Kmh = amrex::min(Kmh, 10*DeltaH);
105  mu_turb(i, j, k, EddyDiff::Mom_h) = Kmh;
106  if (smag2d) {
107  mu_turb(i, j, k, EddyDiff::Mom_v) = 0.0;
108  } else {
109  Real DeltaV = 1.0 / dzInv;
110  mu_turb(i, j, k, EddyDiff::Mom_v) = Cs*Cs*DeltaV*DeltaV * cell_data(i, j, k, Rho_comp) * std::sqrt(2.0*SmnSmn);
111  }
112  }
113 
114  // Calculate SFS quantities
115  Real dtheta_dz = 0.5 * ( cell_data(i,j,k+1,RhoTheta_comp)/cell_data(i,j,k+1,Rho_comp)
116  - cell_data(i,j,k-1,RhoTheta_comp)/cell_data(i,j,k-1,Rho_comp) )*dzInv;
117 
118  // - heat flux
119  // (Note: If using ERF_EXPLICIT_MOST_STRESS, the value at k=0 will
120  // be overwritten when BCs are applied)
121  hfx_x(i,j,k) = 0.0;
122  hfx_y(i,j,k) = 0.0;
123  hfx_z(i,j,k) = -inv_Pr_t*mu_turb(i,j,k,EddyDiff::Mom_v) * dtheta_dz; // (rho*w)' theta' [kg m^-2 s^-1 K]
124  });
125  }
126  }
127  // DEARDORFF: Fill Kturb for momentum in horizontal and vertical
128  //***********************************************************************************
129  else if (turbChoice.les_type == LESType::Deardorff)
130  {
131  const Real l_C_k = turbChoice.Ck;
132  const Real l_C_e = turbChoice.Ce;
133  const Real l_C_e_wall = turbChoice.Ce_wall;
134  const Real Ce_lcoeff = amrex::max(0.0, l_C_e - 1.9*l_C_k);
135  const Real l_abs_g = const_grav;
136  const Real l_inv_theta0 = 1.0 / turbChoice.theta_ref;
137 
138 #ifdef _OPENMP
139 #pragma omp parallel if (Gpu::notInLaunchRegion())
140 #endif
141  for ( MFIter mfi(eddyViscosity,TilingIfNotGPU()); mfi.isValid(); ++mfi)
142  {
143  Box bxcc = mfi.tilebox();
144 
145  const Array4<Real>& mu_turb = eddyViscosity.array(mfi);
146  const Array4<Real>& hfx_x = Hfx1.array(mfi);
147  const Array4<Real>& hfx_y = Hfx2.array(mfi);
148  const Array4<Real>& hfx_z = Hfx3.array(mfi);
149  const Array4<Real>& diss = Diss.array(mfi);
150 
151  const Array4<Real const > &cell_data = cons_in.array(mfi);
152 
153  Array4<Real const> mf_u = mapfac[MapFacType::u_x]->const_array(mfi);
154  Array4<Real const> mf_v = mapfac[MapFacType::v_x]->const_array(mfi);
155 
156  Array4<Real const> z_nd_arr = z_phys_nd->const_array(mfi);
157 
158  ParallelFor(bxcc, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
159  {
160  Real dxInv = cellSizeInv[0];
161  Real dyInv = cellSizeInv[1];
162  Real dzInv = cellSizeInv[2];
163  if (use_terrain) {
164  // the terrain grid is only deformed in z for now
165  dzInv /= Compute_h_zeta_AtCellCenter(i,j,k, cellSizeInv, z_nd_arr);
166  }
167  Real Delta;
168  if (isotropic) {
169  Real cellVolMsf = 1.0 / (dxInv * mf_u(i,j,0) * dyInv * mf_v(i,j,0) * dzInv);
170  Delta = std::cbrt(cellVolMsf);
171  } else {
172  Delta = 1.0 / dzInv;
173  }
174 
175  // Calculate stratification-dependent mixing length (Deardorff 1980)
177  Real dtheta_dz = 0.5 * ( cell_data(i,j,k+1,RhoTheta_comp)/cell_data(i,j,k+1,Rho_comp)
178  - cell_data(i,j,k-1,RhoTheta_comp)/cell_data(i,j,k-1,Rho_comp) )*dzInv;
179  Real E = amrex::max(cell_data(i,j,k,RhoKE_comp)/cell_data(i,j,k,Rho_comp),Real(0.0));
180  Real stratification = l_abs_g * dtheta_dz * l_inv_theta0;
181 
182  // Following WRF, the stratification effects are applied to the vertical length scales
183  // in the case of anistropic mixing
184  Real length;
185  if (stratification <= eps) {
186  length = Delta; // cbrt(dx*dy*dz) -or- dz
187  } else {
188  length = 0.76 * std::sqrt(E / amrex::max(stratification,eps));
189  // mixing length should be _reduced_ for stable stratification
190  length = amrex::min(length, Delta);
191  // following WRF, make sure the mixing length isn't too small
192  length = amrex::max(length, 0.001 * Delta);
193  }
194 
195  Real DeltaH = (isotropic) ? length : std::sqrt(1.0 / (dxInv * mf_u(i,j,0) * dyInv * mf_v(i,j,0)));
196 
197  Real Pr_inv_v = (1. + 2.*length/Delta);
198  Real Pr_inv_h = (isotropic) ? Pr_inv_v : inv_Pr_t;
199 
200  // Calculate eddy diffusivities
201  // K = rho * C_k * l * KE^(1/2)
202  mu_turb(i,j,k,EddyDiff::Mom_h) = cell_data(i,j,k,Rho_comp) * l_C_k * DeltaH * std::sqrt(E);
203  mu_turb(i,j,k,EddyDiff::Mom_v) = cell_data(i,j,k,Rho_comp) * l_C_k * length * std::sqrt(E);
204  // KH = (1 + 2*l/delta) * mu_turb
205  mu_turb(i,j,k,EddyDiff::Theta_h) = Pr_inv_h * mu_turb(i,j,k,EddyDiff::Mom_h);
206  mu_turb(i,j,k,EddyDiff::Theta_v) = Pr_inv_v * mu_turb(i,j,k,EddyDiff::Mom_v);
207  // Store lengthscale for TKE source terms
208  mu_turb(i,j,k,EddyDiff::Turb_lengthscale) = length;
209 
210  // Calculate SFS quantities
211  // - dissipation
212  Real Ce;
213  if ((l_C_e_wall > 0) && (k==0)) {
214  Ce = l_C_e_wall;
215  } else {
216  Ce = 1.9*l_C_k + Ce_lcoeff*length / Delta;
217  }
218  diss(i,j,k) = cell_data(i,j,k,Rho_comp) * Ce * std::pow(E,1.5) / length;
219 
220  // - heat flux
221  // (Note: If using SurfaceLayer, the value at k=0 will
222  // be overwritten)
223  hfx_x(i,j,k) = 0.0;
224  hfx_y(i,j,k) = 0.0;
225  hfx_z(i,j,k) = -mu_turb(i,j,k,EddyDiff::Theta_v) * dtheta_dz; // (rho*w)' theta' [kg m^-2 s^-1 K]
226  });
227  }
228  }
229 
230  // Extrapolate Kturb in x/y, fill remaining elements (relevant to lev==0)
231  //***********************************************************************************
232  int ngc(1);
233  // EddyDiff mapping : Theta_h KE_h Scalar_h Q_h
234  Vector<Real> Factors = {inv_Pr_t, inv_sigma_k, inv_Sc_t, inv_Sc_t}; // alpha = mu/Pr
235  Gpu::AsyncVector<Real> d_Factors; d_Factors.resize(Factors.size());
236  Gpu::copy(Gpu::hostToDevice, Factors.begin(), Factors.end(), d_Factors.begin());
237  Real* fac_ptr = d_Factors.data();
238 
239  const bool use_KE = ( turbChoice.les_type == LESType::Deardorff );
240 
241 #ifdef _OPENMP
242 #pragma omp parallel if (Gpu::notInLaunchRegion())
243 #endif
244  for ( MFIter mfi(eddyViscosity,TilingIfNotGPU()); mfi.isValid(); ++mfi)
245  {
246  Box bxcc = mfi.tilebox();
247  Box planex = bxcc; planex.setSmall(0, 1); planex.setBig(0, ngc); planex.grow(1,1);
248  Box planey = bxcc; planey.setSmall(1, 1); planey.setBig(1, ngc); planey.grow(0,1);
249  bxcc.growLo(0,ngc); bxcc.growHi(0,ngc);
250  bxcc.growLo(1,ngc); bxcc.growHi(1,ngc);
251 
252  const Array4<Real>& mu_turb = eddyViscosity.array(mfi);
253 
254  for (auto n = 1; n < (EddyDiff::NumDiffs-1)/2; ++n) {
255  int offset = (EddyDiff::NumDiffs-1)/2;
256  switch (n)
257  {
258  case EddyDiff::KE_h:
259  if (use_KE) {
260  ParallelFor(bxcc, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
261  {
262  int indx = n;
263  int indx_v = indx + offset;
264  mu_turb(i,j,k,indx) = mu_turb(i,j,k,EddyDiff::Mom_h) * fac_ptr[indx-1];
265  mu_turb(i,j,k,indx_v) = mu_turb(i,j,k,indx);
266  });
267  }
268  break;
269  default:
270  ParallelFor(bxcc, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
271  {
272  int indx = n;
273  int indx_v = indx + offset;
274 
275  // NOTE: Theta_h, Theta_v have already been set for Deardorff
276  if (!(indx_v == EddyDiff::Theta_v && use_KE)) {
277  mu_turb(i,j,k,indx) = mu_turb(i,j,k,EddyDiff::Mom_h) * fac_ptr[indx-1];
278  mu_turb(i,j,k,indx_v) = mu_turb(i,j,k,indx);
279  }
280  });
281  break;
282  }
283  }
284  }
285 }
@ tau12
Definition: ERF_DataStruct.H:26
@ tau23
Definition: ERF_DataStruct.H:26
@ tau33
Definition: ERF_DataStruct.H:26
@ tau22
Definition: ERF_DataStruct.H:26
@ tau11
Definition: ERF_DataStruct.H:26
@ tau13
Definition: ERF_DataStruct.H:26
@ v_x
Definition: ERF_DataStruct.H:21
@ u_x
Definition: ERF_DataStruct.H:21
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:64
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:37
#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_FORCE_INLINE IntVect offset(const int face_dir, const int normal)
Definition: ERF_ReadBndryPlanes.cpp:28
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:47
@ 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
real(c_double), parameter epsilon
Definition: ERF_module_model_constants.F90:12
bool smag2d
Definition: ERF_TurbStruct.H:248
amrex::Real sigma_k
Definition: ERF_TurbStruct.H:264
amrex::Real Sc_t_inv
Definition: ERF_TurbStruct.H:244
amrex::Real Ck
Definition: ERF_TurbStruct.H:253
amrex::Real Cs
Definition: ERF_TurbStruct.H:247
amrex::Real Pr_t_inv
Definition: ERF_TurbStruct.H:240
amrex::Real Ce_wall
Definition: ERF_TurbStruct.H:252
amrex::Real Ce
Definition: ERF_TurbStruct.H:251
amrex::Real theta_ref
Definition: ERF_TurbStruct.H:267
bool mix_isotropic
Definition: ERF_TurbStruct.H:270

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,
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 FArrayBox *  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
317 {
318  const GpuArray<Real, AMREX_SPACEDIM> cellSizeInv = geom.InvCellSizeArray();
319  const bool use_SurfLayer = (SurfLayer != nullptr);
320 
321  Real inv_Pr_t = turbChoice.Pr_t_inv;
322  Real inv_Sc_t = turbChoice.Sc_t_inv;
323  Real inv_sigma_k = 1.0 / turbChoice.sigma_k;
324 
325  // One-Equation k model (Axell & Liungman 2001, Environ Fluid Mech)
326  //***********************************************************************************
327  if (turbChoice.rans_type == RANSType::kEqn)
328  {
329  const Real Cmu0 = turbChoice.Cmu0;
330  const Real Cmu0_pow3 = Cmu0 * Cmu0 * Cmu0;
331  const Real inv_Cb_sq = 1.0 / (turbChoice.Cb * turbChoice.Cb);
332  const Real Rt_crit = turbChoice.Rt_crit;
333  const Real Rt_min = turbChoice.Rt_min;
334  const Real l_g_max = turbChoice.l_g_max;
335 
336  const Real abs_g = const_grav;
337  const Real inv_theta0 = 1.0 / turbChoice.theta_ref;
338 
339 #ifdef _OPENMP
340 #pragma omp parallel if (Gpu::notInLaunchRegion())
341 #endif
342  for ( MFIter mfi(eddyViscosity,TilingIfNotGPU()); mfi.isValid(); ++mfi)
343  {
344  Box bxcc = mfi.tilebox();
345 
346  const Array4<Real const>& d_arr = wdist.const_array(mfi);
347  const Array4<Real const>& z0_arr = (use_SurfLayer) ? z_0->const_array() : Array4<Real const>{};
348 
349  const Array4<Real>& mu_turb = eddyViscosity.array(mfi);
350  const Array4<Real>& hfx_x = Hfx1.array(mfi);
351  const Array4<Real>& hfx_y = Hfx2.array(mfi);
352  const Array4<Real>& hfx_z = Hfx3.array(mfi);
353  const Array4<Real>& diss = Diss.array(mfi);
354 
355  const Array4<Real const>& cell_data = cons_in.array(mfi);
356 
357  const Array4<Real const>& z_nd_arr = z_phys_nd->const_array(mfi);
358 
359  ParallelFor(bxcc, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
360  {
362  Real tke = amrex::max(cell_data(i,j,k,RhoKE_comp)/cell_data(i,j,k,Rho_comp), eps);
363 
364  // Estimate stratification
365  Real dzInv = cellSizeInv[2];
366  if (use_terrain) {
367  // the terrain grid is only deformed in z for now
368  dzInv /= Compute_h_zeta_AtCellCenter(i,j,k, cellSizeInv, z_nd_arr);
369  }
370  Real dtheta_dz = 0.5 * ( cell_data(i,j,k+1,RhoTheta_comp)/cell_data(i,j,k+1,Rho_comp)
371  - cell_data(i,j,k-1,RhoTheta_comp)/cell_data(i,j,k-1,Rho_comp) )*dzInv;
372  Real N2 = abs_g * inv_theta0 * dtheta_dz; // Brunt–Väisälä frequency squared
373 
374  // Geometric length scale (AL01, Eqn. 22)
375  Real l_g = (z0_arr) ? KAPPA * (d_arr(i, j, k) + z0_arr(i, j, 0))
376  : KAPPA * d_arr(i, j, k);
377 
378  // Enforce a maximum value
379  l_g = l_g_max * l_g / (l_g_max + l_g);
380 
381  // Turbulent Richardson number (AL01, Eqn. 29)
382  // using the old dissipation value
383  Real diss0 = max(diss(i, j, k) / cell_data(i, j, k, Rho_comp),
384  eps);
385  Real Rt = tke*tke * N2 / diss0;
386 
387  // Turbulent length scale
388  Real length;
389  if (std::abs(N2) <= eps) {
390  length = l_g;
391  } else if (N2 > eps) {
392  // Stable (AL01, Eqn. 26)
393  length = std::sqrt(1.0 /
394  (1.0 / (l_g * l_g) + inv_Cb_sq * N2 / tke));
395  } else {
396  // Unstable (AL01, Eqn. 28)
397  length = l_g * std::sqrt(1.0 - Cmu0_pow3*Cmu0_pow3 * inv_Cb_sq * Rt);
398  }
399  mu_turb(i, j, k, EddyDiff::Turb_lengthscale) = length;
400 
401  // Burchard & Petersen smoothing function
402  Rt = (Rt >= Rt_crit) ? Rt
403  : std::max(Rt,
404  Rt - std::pow(Rt - Rt_crit, 2) /
405  (Rt + Rt_min - 2*Rt_crit));
406 
407  // Stability functions
408  // Note: These use the smoothed turbulent Richardson number
409  Real cmu = (Cmu0 + 0.108*Rt)
410  / (1.0 + 0.308*Rt + 0.00837*Rt*Rt); // (AL01, Eqn. 31)
411  Real cmu_prime = Cmu0 / (1 + 0.277*Rt); // (AL01, Eqn. 32)
412 
413  // Calculate eddy diffusivities
414  // K = rho * nu_t = rho * c_mu * tke^(1/2) * length
415  Real nut = cmu * std::sqrt(tke) * length; // eddy viscosity
416  Real nut_prime = cmu_prime / cmu * nut; // eddy diffusivity
417  mu_turb(i, j, k, EddyDiff::Mom_h) = cell_data(i, j, k, Rho_comp) * nut;
418  mu_turb(i, j, k, EddyDiff::Mom_v) = mu_turb(i, j, k, EddyDiff::Mom_h);
419  mu_turb(i, j, k, EddyDiff::Theta_v) = cell_data(i, j, k, Rho_comp) * nut_prime;
420 
421  // Calculate SFS quantities
422  // - dissipation (AL01 Eqn. 19)
423  diss(i, j, k) = cell_data(i, j, k, Rho_comp) * Cmu0_pow3 * std::pow(tke,1.5) / length;
424 
425  // - heat flux
426  // (Note: If using SurfaceLayer, the value at k=0 will
427  // be overwritten)
428  hfx_x(i, j, k) = 0.0;
429  hfx_y(i, j, k) = 0.0;
430  // Note: buoyant production = g/theta0 * hfx == -nut_prime * N^2 (c.f. AL01 Eqn. 15)
431  // = nut_prime * g/theta0 * dtheta/dz
432  // ==> hfx = nut_prime * dtheta/dz
433  // Our convention is such that dtheta/dz < 0 gives a positive
434  // (upward) heat flux.
435  hfx_z(i, j, k) = -mu_turb(i, j, k, EddyDiff::Theta_v) * dtheta_dz; // (rho*w)' theta' [kg m^-2 s^-1 K]
436  });
437  }
438  }
439 
440  // Extrapolate Kturb in x/y, fill remaining elements (relevant to lev==0)
441  //***********************************************************************************
442  int ngc(1);
443  // EddyDiff mapping : Theta_h KE_h Scalar_h Q_h
444  Vector<Real> Factors = {inv_Pr_t, inv_sigma_k, inv_Sc_t, inv_Sc_t}; // alpha = mu/Pr
445  Gpu::AsyncVector<Real> d_Factors; d_Factors.resize(Factors.size());
446  Gpu::copy(Gpu::hostToDevice, Factors.begin(), Factors.end(), d_Factors.begin());
447  Real* fac_ptr = d_Factors.data();
448 
449  const bool use_KE = ( turbChoice.rans_type == RANSType::kEqn );
450 
451 #ifdef _OPENMP
452 #pragma omp parallel if (Gpu::notInLaunchRegion())
453 #endif
454  for ( MFIter mfi(eddyViscosity,TilingIfNotGPU()); mfi.isValid(); ++mfi)
455  {
456  Box bxcc = mfi.tilebox();
457  Box planex = bxcc; planex.setSmall(0, 1); planex.setBig(0, ngc); planex.grow(1,1);
458  Box planey = bxcc; planey.setSmall(1, 1); planey.setBig(1, ngc); planey.grow(0,1);
459  bxcc.growLo(0,ngc); bxcc.growHi(0,ngc);
460  bxcc.growLo(1,ngc); bxcc.growHi(1,ngc);
461 
462  const Array4<Real>& mu_turb = eddyViscosity.array(mfi);
463 
464  for (auto n = 1; n < (EddyDiff::NumDiffs-1)/2; ++n) {
465  int offset = (EddyDiff::NumDiffs-1)/2;
466  switch (n)
467  {
468  case EddyDiff::KE_h:
469  if (use_KE) {
470  ParallelFor(bxcc, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
471  {
472  int indx = n;
473  int indx_v = indx + offset;
474  mu_turb(i,j,k,indx) = mu_turb(i,j,k,EddyDiff::Mom_h) * fac_ptr[indx-1];
475  mu_turb(i,j,k,indx_v) = mu_turb(i,j,k,indx);
476  });
477  }
478  break;
479  default:
480  ParallelFor(bxcc, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
481  {
482  int indx = n;
483  int indx_v = indx + offset;
484 
485  mu_turb(i,j,k,indx) = mu_turb(i,j,k,EddyDiff::Mom_h) * fac_ptr[indx-1];
486 
487  // NOTE: Theta_v has already been set for Deardorff
488  if (!(indx_v == EddyDiff::Theta_v && use_KE)) {
489  mu_turb(i,j,k,indx_v) = mu_turb(i,j,k,indx);
490  }
491  });
492  break;
493  }
494  }
495  }
496 }
constexpr amrex::Real KAPPA
Definition: ERF_Constants.H:20
amrex::Real Rt_min
Definition: ERF_TurbStruct.H:259
amrex::Real Cmu0
Definition: ERF_TurbStruct.H:256
amrex::Real Cb
Definition: ERF_TurbStruct.H:257
amrex::Real Rt_crit
Definition: ERF_TurbStruct.H:258
amrex::Real l_g_max
Definition: ERF_TurbStruct.H:260

Referenced by ComputeTurbulentViscosity().

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