ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
ERF_TurbKESources.H File Reference
#include <AMReX.H>
#include <AMReX_BCRec.H>
#include "ERF_DataStruct.H"
#include "ERF_IndexDefines.H"
#include "ERF_NumericalConstants.H"
#include "ERF_SurfaceLayer.H"
#include "ERF_PBLModels.H"
Include dependency graph for ERF_TurbKESources.H:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Functions

template<typename PBLDerivativeDzInvFunctor >
void AddTurbKESources (const amrex::Box &bx, const amrex::Box &domain, const amrex::Array4< const amrex::Real > &u, const amrex::Array4< const amrex::Real > &v, const amrex::Array4< const amrex::Real > &cell_data, const amrex::Array4< const amrex::Real > &cell_prim, const amrex::Array4< amrex::Real > &cell_rhs, const amrex::Array4< const amrex::Real > &SmnSmn_a, const amrex::Array4< const amrex::Real > &hfx_z, const amrex::Array4< const amrex::Real > &qfx1_z, const amrex::Array4< const amrex::Real > &diss, const amrex::Array4< const amrex::Real > &tau13, const amrex::Array4< const amrex::Real > &tau23, const amrex::Array4< const amrex::Real > &mu_turb, const SolverChoice &solverChoice, const int level, const amrex::Array4< const amrex::Real > &tm_arr, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > grav_gpu, const amrex::BCRec *bc_ptr, const bool use_SurfLayer, const amrex::Vector< std::unique_ptr< SurfaceLayer >> &SurfLayer, const PBLDerivativeDzInvFunctor &pbl_derivative_dz_inv)
 

Function Documentation

◆ AddTurbKESources()

template<typename PBLDerivativeDzInvFunctor >
void AddTurbKESources ( const amrex::Box &  bx,
const amrex::Box &  domain,
const amrex::Array4< const amrex::Real > &  u,
const amrex::Array4< const amrex::Real > &  v,
const amrex::Array4< const amrex::Real > &  cell_data,
const amrex::Array4< const amrex::Real > &  cell_prim,
const amrex::Array4< amrex::Real > &  cell_rhs,
const amrex::Array4< const amrex::Real > &  SmnSmn_a,
const amrex::Array4< const amrex::Real > &  hfx_z,
const amrex::Array4< const amrex::Real > &  qfx1_z,
const amrex::Array4< const amrex::Real > &  diss,
const amrex::Array4< const amrex::Real > &  tau13,
const amrex::Array4< const amrex::Real > &  tau23,
const amrex::Array4< const amrex::Real > &  mu_turb,
const SolverChoice &  solverChoice,
const int  level,
const amrex::Array4< const amrex::Real > &  tm_arr,
const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM >  grav_gpu,
const amrex::BCRec *  bc_ptr,
const bool  use_SurfLayer,
const amrex::Vector< std::unique_ptr< SurfaceLayer >> &  SurfLayer,
const PBLDerivativeDzInvFunctor &  pbl_derivative_dz_inv 
)

Add the production and dissipation sources of the subgrid TKE (Deardorff or k-eqn RANS) or of the MYNN QKE to the RhoKE RHS.

These sources consume the face fluxes written by the scalar diffusion of the same RK stage, so they must be called after every DiffusionSrcForState_* call of the stage has returned: the RhoTheta call in erf_slow_rhs_pre (hfx_z) and the RhoQ1 call in erf_slow_rhs_post (qfx1_z).

Parameters
[in]bxcell center box to loop over
[in]domainbox of the whole domain
[in]uvelocity in x-dir
[in]vvelocity in y-dir
[in]cell_dataconserved cell center vars
[in]cell_primprimitive cell center vars
[in,out]cell_rhsRHS for cell center vars
[in]SmnSmn_astrain rate magnitude
[in]hfx_zrho<w'theta'> on z-faces
[in]qfx1_zrho<w'qv'> on z-faces (may be null)
[in]dissdissipation of TKE
[in]tau13rho<u'w'>; holds the surface-layer stress on the zlo face
[in]tau23rho<v'w'>; holds the surface-layer stress on the zlo face
[in]mu_turbturbulent viscosity
[in]solverChoicecontainer of solver and diffusion parameters
[in]levelAMR level
[in]tm_arrtheta mean array
[in]grav_gpugravity vector
[in]bc_ptrcontainer with boundary conditions
[in]use_SurfLayerwhether surface-layer fluxes are applied in the diffusion
[in]SurfLayersurface layer objects, one per face
[in]pbl_derivative_dz_invinverse vertical spacings for the mesh type
68 {
69  BL_PROFILE("AddTurbKESources()");
70 
71  const TurbChoice& turbChoice = solverChoice.turbChoice[level];
72 
73  const bool l_use_keqn = turbChoice.use_keqn;
74  const bool l_implicit_diss = turbChoice.use_keqn && turbChoice.implicit_tke_dissipation;
75  const bool l_use_mynn = turbChoice.use_pbl_tke;
76 
77  const bool SurfLayer_zlo = use_SurfLayer && SurfLayer[amrex::Orientation::zlo()] != nullptr;
78 
79  // Clamp the divisor to the smallest normal Real before the select: an optimised
80  // build if-converts the select into an unconditional 1/theta_ref (also when
81  // the select is on the divisor), which trips fpe_trap_zero with the default
82  // theta_ref = 0. The clamp is exact for any normal theta_ref > 0 and its
83  // reciprocal is finite in single and double precision.
84  const bool use_ref_theta = (turbChoice.theta_ref > 0);
85  const amrex::Real inv_theta_ref = one / amrex::max(turbChoice.theta_ref,
86  std::numeric_limits<amrex::Real>::min());
87  const amrex::Real l_inv_theta0 = (use_ref_theta) ? inv_theta_ref : one;
88  const amrex::Real l_abs_g = std::abs(grav_gpu[2]);
89 
90  // Using Deardorff (see Sullivan et al 1994)
91  // or k-eqn RANS (see Axell & Liungman 2001)
92  //
93  // Note: At this point, the thermal diffusivity ("Khv" field in ERF) and the
94  // subgrid dissipation ("diss" here) have been updated by the closure
95  // at the beginning of each timestep.
96  // The strain rate magnitude is updated at the beginning of the first
97  // RK stage only, therefore the shear production term also does not
98  // change between RK stages.
99  // hfx_z (Hfx3) is z-nodal, (0,0,1): it lives at the w-faces, not at the
100  // cell centre, so a single hfx_z(i,j,k) is the flux at the LOWER face of
101  // the cell and using it as the cell-centred value is not formally correct.
102  // hfx_z holds the face fluxes of the theta diffusion of this stage
103  // (the full flux, independent of the implicit/explicit split); at the
104  // bottom face of the domain that is the surface-layer flux when a
105  // surface layer is used. The buoyancy production therefore averages the
106  // two faces of every cell, the first cell included, for the cell-centred
107  // vertical heat flux. The first cell is not special-cased to the surface
108  // flux alone: that would again place a w-face value at the cell centre
109  // (a MOST-profile gradient evaluated at the first cell centre would be
110  // the consistent alternative).
111  // On a terrain-fitted mesh hfx_z holds the flux along zeta only: the slope
112  // corrections and the Jacobian are applied where the fluxes enter the RHS, so
113  // over sloping terrain this source omits those terms (as does the hfx3 output).
114  // REQUIRED WRITER: the RhoTheta call of DiffusionSrcForState_{N,S,T}
115  // in erf_slow_rhs_pre, on the same RK stage, which writes every z-face
116  // of each valid box (zbx = surroundingNodes(bx,2), top face included).
117  // Running the RhoKE source without that same-stage theta pass would
118  // read stale faces.
119  if (l_use_keqn) {
120  int qty_index = RhoKE_comp;
121  // The source reads hfx_z at k+1: the z-faces of bx must be in hfx_z
122  AMREX_ASSERT(hfx_z.contains(bx.smallEnd(0), bx.smallEnd(1), bx.smallEnd(2)) &&
123  hfx_z.contains(bx.bigEnd(0), bx.bigEnd(1), bx.bigEnd(2)+1));
124  amrex::ParallelFor(bx,[=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
125  {
126  // Add Buoyancy Source
127  // where the SGS buoyancy flux tau_{theta,i} = -KH * dtheta/dx_i,
128  // such that for dtheta/dz < 0, there is a positive (upward) heat
129  // flux; the TKE buoyancy production is then
130  // B = g/theta_0 * tau_{theta,w}
131  // for a dry atmosphere.
132  // TODO: To account for moisture, the Brunt-Vaisala frequency,
133  // N^2 = g[1/theta * dtheta/dz + ...]
134  // **should** be a function of the water vapor and total water
135  // mixing ratios, depending on whether conditions are saturated or
136  // not (see the WRF model description, Skamarock et al 2019).
137  // NOTE: reads hfx_z at k+1, one face above bx; Hfx3 is z-nodal and
138  // the theta diffusion writes every face of its valid box, top included.
139  const amrex::Real hfx_cc = amrex::Real(0.5) * ( hfx_z(i,j,k) + hfx_z(i,j,k+1) );
140  amrex::Real buoyancy_src = l_abs_g * l_inv_theta0 * hfx_cc;
141  if (!use_ref_theta) {
142  // l_inv_theta0 == 1, divide by actual theta
143  buoyancy_src /= cell_prim(i,j,k,PrimTheta_comp);
144  }
145  cell_rhs(i,j,k,qty_index) += buoyancy_src;
146 
147  // TKE shear production
148  // P = -tau_ij * S_ij = 2 * mu_turb * S_ij * S_ij
149  // Note: This assumes that the horizontal and vertical diffusivities
150  // of momentum are equal
151  cell_rhs(i,j,k,qty_index) += two*mu_turb(i,j,k,EddyDiff::Mom_v) * SmnSmn_a(i,j,k);
152 
153  // TKE dissipation (explicit); with erf.implicit_tke_dissipation the
154  // sink is folded into the update in erf_slow_rhs_post instead
155  if (!l_implicit_diss) {
156  cell_rhs(i,j,k,qty_index) -= diss(i,j,k);
157  }
158  });
159  }
160 
161  // Using PBL
162  if (l_use_mynn) {
163  int qty_index = RhoKE_comp;
164  auto pbl_mynn_B1_l = turbChoice.pbl_mynn.B1;
165 
166  // At k = klo the resolved vertical gradients are contaminated by the
167  // foextrap/hoextrap ghost cells (ERF #4037). When a SurfaceLayer owns the
168  // zlo face, ComputeQKESourceTerms builds the production terms there from
169  // the surface stresses and scalar fluxes that are actually applied to the
170  // momentum, heat and moisture equations instead.
171  const bool l_surface_layer_on_zlo = SurfLayer_zlo;
172 
173  amrex::ParallelFor(bx,[=,moist_indices=solverChoice.moisture_indices] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
174  {
175  bool c_ext_dir_on_zlo = ( (bc_ptr[BCVars::cons_bc].lo(2) == ERFBCType::ext_dir) );
176  bool c_ext_dir_on_zhi = ( (bc_ptr[BCVars::cons_bc].hi(2) == ERFBCType::ext_dir) );
177  bool u_ext_dir_on_zlo = ( (bc_ptr[BCVars::xvel_bc].lo(2) == ERFBCType::ext_dir) );
178  bool u_ext_dir_on_zhi = ( (bc_ptr[BCVars::xvel_bc].hi(2) == ERFBCType::ext_dir) );
179  bool v_ext_dir_on_zlo = ( (bc_ptr[BCVars::yvel_bc].lo(2) == ERFBCType::ext_dir) );
180  bool v_ext_dir_on_zhi = ( (bc_ptr[BCVars::yvel_bc].hi(2) == ERFBCType::ext_dir) );
181 
182  const auto pbl_dz_inv = pbl_derivative_dz_inv(i,j,k);
183 
184  // This computes shear production, buoyancy production, and dissipation terms only.
185  cell_rhs(i, j, k, qty_index) += ComputeQKESourceTerms(i,j,k,u,v,cell_data,cell_prim,
186  mu_turb,domain,pbl_dz_inv,
187  pbl_mynn_B1_l,tm_arr(i,j,0),
188  moist_indices,
189  c_ext_dir_on_zlo, c_ext_dir_on_zhi,
190  u_ext_dir_on_zlo, u_ext_dir_on_zhi,
191  v_ext_dir_on_zlo, v_ext_dir_on_zhi,
192  tau13, tau23, hfx_z, qfx1_z,
193  l_surface_layer_on_zlo);
194  });
195  }
196 }
@ tau23
Definition: ERF_DataStruct.H:48
@ tau13
Definition: ERF_DataStruct.H:48
#define PrimTheta_comp
Definition: ERF_IndexDefines.H:61
#define RhoKE_comp
Definition: ERF_IndexDefines.H:44
ParallelFor(fab_box, [=] AMREX_GPU_DEVICE(int i, int j, int k) { qrcuten_arr(i, j, k)=Real(0);qscuten_arr(i, j, k)=Real(0);qicuten_arr(i, j, k)=Real(0);})
constexpr amrex::Real two
Definition: ERF_NumericalConstants.H:38
constexpr amrex::Real one
Definition: ERF_NumericalConstants.H:37
AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real ComputeQKESourceTerms(int i, int j, int k, const amrex::Array4< const amrex::Real > &uvel, const amrex::Array4< const amrex::Real > &vvel, const amrex::Array4< const amrex::Real > &cell_data, const amrex::Array4< const amrex::Real > &cell_prim, const amrex::Array4< const amrex::Real > &K_turb, const amrex::Box &domain, const PBLDerivativeDzInv dz_inv, amrex::Real pbl_mynn_B1_l, const amrex::Real theta_mean, const MoistureComponentIndices &moisture_indices, bool c_ext_dir_on_zlo, bool c_ext_dir_on_zhi, bool u_ext_dir_on_zlo, bool u_ext_dir_on_zhi, bool v_ext_dir_on_zlo, bool v_ext_dir_on_zhi, const amrex::Array4< const amrex::Real > &tau13, const amrex::Array4< const amrex::Real > &tau23, const amrex::Array4< const amrex::Real > &hfx_z, const amrex::Array4< const amrex::Real > &qfx1_z, bool surface_layer_on_zlo)
Definition: ERF_PBLModels.H:457
amrex::Real Real
Definition: ERF_ShocInterface.H:19
@ yvel_bc
Definition: ERF_IndexDefines.H:109
@ cons_bc
Definition: ERF_IndexDefines.H:92
@ xvel_bc
Definition: ERF_IndexDefines.H:108
@ ext_dir
Definition: ERF_IndexDefines.H:300
@ Mom_v
Definition: ERF_IndexDefines.H:252
amrex::Real B1
Definition: ERF_MYNNStruct.H:52
amrex::Vector< TurbChoice > turbChoice
Turbulence options for each AMR level.
Definition: ERF_DataStruct.H:2252
MoistureComponentIndices moisture_indices
Index map of the moisture data carried by the active scheme: conserved-state components for the speci...
Definition: ERF_DataStruct.H:2575
Definition: ERF_TurbStruct.H:115
bool implicit_tke_dissipation
Definition: ERF_TurbStruct.H:839
amrex::Real theta_ref
Reference potential temperature for stable stratification.
Definition: ERF_TurbStruct.H:815
bool use_keqn
Whether a microscale TKE closure is active.
Definition: ERF_TurbStruct.H:922
MYNNLevel25 pbl_mynn
MYNN level-2.5 closure coefficients.
Definition: ERF_TurbStruct.H:917
bool use_pbl_tke
Whether a mesoscale PBL TKE closure is active.
Definition: ERF_TurbStruct.H:925

Referenced by erf_slow_rhs_post().

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