ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
ERF_PBLModels.H File Reference
#include <AMReX_Periodicity.H>
#include "ERF_Constants.H"
#include "ERF_DataStruct.H"
#include "ERF_MOSTUtils.H"
Include dependency graph for ERF_PBLModels.H:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Classes

struct  PBLDerivativeDzInv
 
struct  PBLSurfaceLayerGradient
 
struct  PBLDerivativeDzInv_N
 Functor for inverse vertical spacings with constant grid spacing. More...
 
struct  PBLDerivativeDzInv_S
 Functor for inverse vertical spacings for stretched grids using a spacing array. More...
 
struct  PBLDerivativeDzInv_T
 Functor for inverse vertical spacings for terrain-following grids using cell-center heights. More...
 

Functions

void ComputeDiffusivityMYJ (double dt, const amrex::MultiFab &xvel, const amrex::MultiFab &yvel, amrex::MultiFab &cons_in, amrex::MultiFab &eddyViscosity, const amrex::Geometry &geom, const TurbChoice &turbChoice, std::unique_ptr< SurfaceLayer > &SurfLayer, bool use_terrain_fitted_coords, bool use_moisture, int level, const amrex::BCRec *bc_ptr, bool, const std::unique_ptr< amrex::MultiFab > &z_phys_nd, const std::unique_ptr< amrex::MultiFab > &z_phys_cc, const MoistureComponentIndices &moisture_indices)
 
void ComputeDiffusivityMYNN25 (const amrex::MultiFab &xvel, const amrex::MultiFab &yvel, const amrex::MultiFab &cons_in, amrex::MultiFab &eddyViscosity, const amrex::Geometry &geom, const TurbChoice &turbChoice, std::unique_ptr< SurfaceLayer > &SurfLayer, bool use_terrain_fitted_coords, bool use_moisture, int level, const amrex::BCRec *bc_ptr, bool, const std::unique_ptr< amrex::MultiFab > &z_phys_nd, const std::unique_ptr< amrex::MultiFab > &z_phys_cc, const MoistureComponentIndices &moisture_indices)
 
void ComputeDiffusivityMYNNEDMF (const amrex::MultiFab &xvel, const amrex::MultiFab &yvel, const amrex::MultiFab &cons_in, amrex::MultiFab &eddyViscosity, const amrex::Geometry &geom, const TurbChoice &turbChoice, std::unique_ptr< SurfaceLayer > &SurfLayer, bool use_terrain_fitted_coords, bool use_moisture, int level, const amrex::BCRec *bc_ptr, bool, const std::unique_ptr< amrex::MultiFab > &z_phys_nd, const std::unique_ptr< amrex::MultiFab > &z_phys_cc, const MoistureComponentIndices &moisture_indices)
 
void ComputeDiffusivityYSU (const amrex::MultiFab &xvel, const amrex::MultiFab &yvel, const amrex::MultiFab &cons_in, amrex::MultiFab &eddyViscosity, const amrex::Geometry &geom, const TurbChoice &turbChoice, std::unique_ptr< SurfaceLayer > &SurfLayer, bool use_terrain_fitted_coords, bool use_moisture, int level, const amrex::BCRec *bc_ptr, bool, const std::unique_ptr< amrex::MultiFab > &z_phys_nd, const std::unique_ptr< amrex::MultiFab > &z_phys_cc, const MoistureComponentIndices &moisture_indices)
 
void ComputeDiffusivityMRF (const amrex::MultiFab &xvel, const amrex::MultiFab &yvel, const amrex::MultiFab &cons_in, amrex::MultiFab &eddyViscosity, const amrex::Geometry &geom, const TurbChoice &turbChoice, std::unique_ptr< SurfaceLayer > &SurfLayer, bool use_terrain_fitted_coords, bool use_moisture, int level, const amrex::BCRec *bc_ptr, bool, const std::unique_ptr< amrex::MultiFab > &z_phys_nd, const std::unique_ptr< amrex::MultiFab > &z_phys_cc, const MoistureComponentIndices &moisture_indices, const amrex::MultiFab *terrain_blank=nullptr)
 
void ComputeDiffusivityYSUNew (const amrex::MultiFab &xvel, const amrex::MultiFab &yvel, const amrex::MultiFab &cons_in, amrex::MultiFab &eddyViscosity, const amrex::Geometry &geom, const TurbChoice &turbChoice, std::unique_ptr< SurfaceLayer > &SurfLayer, bool use_terrain_fitted_coords, bool use_moisture, int level, const amrex::BCRec *bc_ptr, bool, const std::unique_ptr< amrex::MultiFab > &z_phys_nd, const std::unique_ptr< amrex::MultiFab > &z_phys_cc, const MoistureComponentIndices &moisture_indices, const amrex::MultiFab *qheating_rates=nullptr, const amrex::MultiFab *terrain_blank=nullptr)
 
AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real GetPBLStratVar (int i, int j, int k, const StratType strat, const amrex::Array4< const amrex::Real > &cell_data, const MoistureComponentIndices &moisture_indices)
 
AMREX_GPU_DEVICE AMREX_FORCE_INLINE void ComputeVerticalDerivativesPBL (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 int izmin, const int izmax, const PBLDerivativeDzInv &dz_inv, const bool c_ext_dir_on_zlo, const bool c_ext_dir_on_zhi, const bool u_ext_dir_on_zlo, const bool u_ext_dir_on_zhi, const bool v_ext_dir_on_zlo, const bool v_ext_dir_on_zhi, amrex::Real &dthetadz, amrex::Real &dudz, amrex::Real &dvdz, const MoistureComponentIndices &moisture_indices, const StratType strat=StratType::thetav)
 
AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real ComputeVirtualTStarPBL (amrex::Real t_star, amrex::Real q_star, amrex::Real theta, amrex::Real qv, bool use_moisture)
 
AMREX_GPU_DEVICE AMREX_FORCE_INLINE void ApplySurfaceLayerGradientsPBL (const PBLSurfaceLayerGradient &sl, amrex::Real &dthetadz, amrex::Real &dudz, amrex::Real &dvdz)
 
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)
 
void ApplyPBLHSmoothing (amrex::FArrayBox &pblh_fab, const amrex::Box &xybx_valid, const amrex::Real weight, const int passes, const amrex::Box &domain, const amrex::Periodicity &periodicity)
 Apply spatial smoothing to PBLH field using 5-point stencil. More...
 

Function Documentation

◆ ApplyPBLHSmoothing()

void ApplyPBLHSmoothing ( amrex::FArrayBox &  pblh_fab,
const amrex::Box &  xybx_valid,
const amrex::Real  weight,
const int  passes,
const amrex::Box &  domain,
const amrex::Periodicity &  periodicity 
)
inline

Apply spatial smoothing to PBLH field using 5-point stencil.

Applies a simple 5-point spatial averaging to reduce grid-to-grid noise in the diagnosed PBLH field. This is standard post-processing practice for discrete Rib-crossing detection as described in Seibert et al. (2000): "Review and intercomparison of operational methods for the determination of the mixing height", Atmospheric Environment, 34, 1001-1027.

Stencil: PBLH_smooth(i,j) = w * PBLH(i,j) + (1-w)/4 * [PBLH(i+-1,j) + PBLH(i,j+-1)]

The stencil reads one column outside the region it writes, so the result on a cell depends on columns that a single tile – or a single box – does not own. The caller therefore names the region it wants smoothed (xybx_valid, normally the planar tile box) inside a work array carrying passes columns of halo around it, filled from the same inputs. Pass p writes the region grown by passes-1-p and reads the one grown by passes-p, so each pass consumes one column of halo and the last lands exactly on xybx_valid. Every read stays inside the array, and the result does not depend on how the domain is divided into boxes or tiles.

At a non-periodic domain edge there is no column to read, so the stencil reuses the cell's own value (reflective/Neumann). Across a periodic edge the halo holds the wrapped columns and is read directly, which is why the periodicity has to be passed in: clamping there would fold an edge into a domain that has none.

Parameters
[in,out]pblh_fabFArrayBox holding the 2D PBLH field (component 0), sized to contain xybx_valid grown by passes in x and y
[in]xybx_validRegion to smooth (x,y indices only)
[in]weightCenter cell weight in stencil (must be in [0,1])
[in]passesNumber of smoothing iterations
[in]domainDomain box, for the reflective edge condition
[in]periodicityDomain periodicity, so periodic edges read the halo
723 {
724  if (passes <= 0) { return; }
725 
726  // The caller must supply the halo the passes will consume; see the note above.
728  pblh_fab.box().contains(amrex::grow(xybx_valid, amrex::IntVect(passes,passes,0))),
729  "ApplyPBLHSmoothing: the PBLH work array needs `passes` columns of halo "
730  "around the region being smoothed");
731 
732  // Use a temporary to hold intermediate results
733  amrex::FArrayBox pblh_temp(pblh_fab.box(), 1, amrex::The_Async_Arena());
734  auto pblh = pblh_fab.array();
735  auto pblh_tmp = pblh_temp.array();
736 
737  const auto& dom_lo = amrex::lbound(domain);
738  const auto& dom_hi = amrex::ubound(domain);
739 
740  const bool per_x = periodicity.isPeriodic(0);
741  const bool per_y = periodicity.isPeriodic(1);
742 
743  const amrex::Real wt_side = (one - weight) / four; // Weight for each of 4 neighbors
744 
745  // Apply smoothing passes
746  for (int pass = 0; pass < passes; ++pass) {
747  const int halo = passes - 1 - pass;
748  const amrex::Box bx = amrex::grow(xybx_valid, amrex::IntVect(halo,halo,0));
749 
750  amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int) noexcept
751  {
752  // Reflective (Neumann) at a non-periodic domain edge: reuse the cell's
753  // own value. Everywhere else -- including across a periodic edge, where
754  // the halo holds the wrapped column -- read the neighbour.
755  const int i_xlo = (per_x || i > dom_lo.x) ? (i - 1) : i;
756  const int i_xhi = (per_x || i < dom_hi.x) ? (i + 1) : i;
757  const int j_ylo = (per_y || j > dom_lo.y) ? (j - 1) : j;
758  const int j_yhi = (per_y || j < dom_hi.y) ? (j + 1) : j;
759 
760  // 5-point stencil
761  pblh_tmp(i, j, 0) = weight * pblh(i, j, 0)
762  + wt_side * (pblh(i_xlo, j, 0) + pblh(i_xhi, j, 0) +
763  pblh(i, j_ylo, 0) + pblh(i, j_yhi, 0));
764  });
765 
766  // Copy back only what this pass computed: outside bx the array still holds
767  // the previous pass, which no later pass reads.
768  pblh_fab.copy<amrex::RunOn::Device>(pblh_temp, bx, 0, bx, 0, 1);
769  }
770 }
AMREX_ALWAYS_ASSERT_WITH_MESSAGE(m_cloud_chamber_config.active, "Cloud Chamber: initializer reached without a parsed configuration")
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 four
Definition: ERF_NumericalConstants.H:40
constexpr amrex::Real one
Definition: ERF_NumericalConstants.H:37
amrex::Real Real
Definition: ERF_ShocInterface.H:19

Referenced by ComputeDiffusivityMRF(), and ComputeDiffusivityYSUNew().

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

◆ ApplySurfaceLayerGradientsPBL()

AMREX_GPU_DEVICE AMREX_FORCE_INLINE void ApplySurfaceLayerGradientsPBL ( const PBLSurfaceLayerGradient &  sl,
amrex::Real &  dthetadz,
amrex::Real &  dudz,
amrex::Real &  dvdz 
)

Overwrite the vertical gradients in the first cell above the surface with the MOST profile gradients, |dU/dz| = u_* phi_m(zeta) / (kappa z) dthetav/dz = thetav_* phi_h(zeta) / (kappa z)

With zlo.type = surface_layer the state carries foextrap and the velocities hoextrap at the lower boundary, so the centered stencil in ComputeVerticalDerivativesPBL reads ghost values that hold no surface information there: the potential-temperature gradient comes out half the one-sided value and the velocity gradient has no dependence on the friction velocity at all (ERF #4037). The MOST profile used here is the one the surface stress and heat flux were themselves derived from.

Every PBL scheme that consumes these gradients uses only the shear magnitude dudz^2 + dvdz^2, so that magnitude is carried in dudz and dvdz is left at zero rather than being split by wind direction.

Parameters
[in]slMonin-Obukhov inputs for this column
[out]dthetadzvertical derivative of virtual potential temperature
[out]dudzvertical derivative of x-velocity (carries the shear magnitude)
[out]dvdzvertical derivative of y-velocity (set to zero)
427 {
428  const similarity_funs sfuns{};
429  const amrex::Real inv_kappa_z = one / (KAPPA * sl.zval);
430  dudz = sl.u_star * sfuns.calc_phi_m(sl.zeta) * inv_kappa_z;
431  dvdz = zero;
432  dthetadz = sl.tstar_v * sfuns.calc_phi_h(sl.zeta) * inv_kappa_z;
433 }
constexpr amrex::Real KAPPA
Definition: ERF_Constants.H:67
constexpr amrex::Real zero
Definition: ERF_NumericalConstants.H:36
amrex::Real tstar_v
virtual temperature scale
Definition: ERF_PBLModels.H:368
amrex::Real zeta
stability coordinate z/L
Definition: ERF_PBLModels.H:370
amrex::Real u_star
friction velocity
Definition: ERF_PBLModels.H:367
amrex::Real zval
height of the cell center above the surface
Definition: ERF_PBLModels.H:369
Definition: ERF_MOSTUtils.H:37

Referenced by ComputeDiffusivityMYJ(), ComputeDiffusivityMYNN25(), ComputeDiffusivityMYNNEDMF(), ComputeDiffusivityYSU(), and ComputeDiffusivityYSUNew().

Here is the caller graph for this function:

◆ ComputeDiffusivityMRF()

void ComputeDiffusivityMRF ( const amrex::MultiFab &  xvel,
const amrex::MultiFab &  yvel,
const amrex::MultiFab &  cons_in,
amrex::MultiFab &  eddyViscosity,
const amrex::Geometry &  geom,
const TurbChoice &  turbChoice,
std::unique_ptr< SurfaceLayer > &  SurfLayer,
bool  use_terrain_fitted_coords,
bool  use_moisture,
int  level,
const amrex::BCRec *  bc_ptr,
bool  ,
const std::unique_ptr< amrex::MultiFab > &  z_phys_nd,
const std::unique_ptr< amrex::MultiFab > &  z_phys_cc,
const MoistureComponentIndices &  moisture_indices,
const amrex::MultiFab *  terrain_blank = nullptr 
)

Compute eddy diffusivities of momentum (eddy viscosity) and heat using the Medium Range Forecast (MRF) boundary layer scheme based on Hong and Pan (1996). Reference: Hong, S. Y., and H.-L. Pan, 1996: Nonlocal Boundary Layer Vertical Diffusion in a Medium-Range Forecast Model. Monthly Weather Review, 124, 2322-2339. https://doi.org/10.1175/1520-0493(1996)124<2322:NBLVDI>2.0.CO;2

Parameters
[in]xvelVelocity in x-dir
[in]yvelVelocity in y-dir
[in]cons_inCell center conserved quantities
[out]eddyViscosityHolds turbulent viscosity
[in]geomProblem geometry
[in]turbChoiceContainer with turbulence parameters
[in]SurfLayerPointer to Monin-Obukhov class if instantiated
[in]use_moistureIf we have microphysics enabled
[in]levelCurrent level
[in]bc_ptrPointer to array with boundary condition info
[in]vert_onlyOnly compute vertical eddy diffusivities
[in]z_phys_ndPhysical location of grid nodes, if terrain (or grid stretching) is enabled
[in]z_phys_ccPhysical location of cell centers

◆ ComputeDiffusivityMYJ()

void ComputeDiffusivityMYJ ( double  dt,
const amrex::MultiFab &  xvel,
const amrex::MultiFab &  yvel,
amrex::MultiFab &  cons_in,
amrex::MultiFab &  eddyViscosity,
const amrex::Geometry &  geom,
const TurbChoice &  turbChoice,
std::unique_ptr< SurfaceLayer > &  SurfLayer,
bool  use_terrain_fitted_coords,
bool  use_moisture,
int  level,
const amrex::BCRec *  bc_ptr,
bool  ,
const std::unique_ptr< amrex::MultiFab > &  z_phys_nd,
const std::unique_ptr< amrex::MultiFab > &  z_phys_cc,
const MoistureComponentIndices &  moisture_indices 
)

Compute eddy diffusivities of momentum (eddy viscosity) and heat using the Mellor-Yamada-Janjić (MYJ) boundary layer scheme. Reference: Janjić, Z. I., 1994: The Step-Mountain Eta Coordinate Model: Further Developments of the Convection, Viscous Sublayer, and Turbulence Closure Schemes. Monthly Weather Review, 122, 927–945. https://doi.org/10.1175/1520-0493(1994)122<0927:TSMECM>2.0.CO;2

Parameters
[in]xvelVelocity in x-dir
[in]yvelVelocity in y-dir
[in]cons_inCell center conserved quantities
[out]eddyViscosityHolds turbulent viscosity
[in]geomProblem geometry
[in]turbChoiceContainer with turbulence parameters
[in]SurfLayerPointer to Monin-Obukhov class if instantiated
[in]use_moistureIf we have microphysics enabled
[in]levelCurrent level
[in]bc_ptrPointer to array with boundary condition info
[in]vert_onlyOnly compute vertical eddy diffusivities
[in]z_phys_ndPhysical location of grid nodes, if terrain (or grid stretching) is enabled
[in]z_phys_ccPhysical location of cell centers

◆ ComputeDiffusivityMYNN25()

void ComputeDiffusivityMYNN25 ( const amrex::MultiFab &  xvel,
const amrex::MultiFab &  yvel,
const amrex::MultiFab &  cons_in,
amrex::MultiFab &  eddyViscosity,
const amrex::Geometry &  geom,
const TurbChoice &  turbChoice,
std::unique_ptr< SurfaceLayer > &  SurfLayer,
bool  use_terrain_fitted_coords,
bool  use_moisture,
int  level,
const amrex::BCRec *  bc_ptr,
bool  ,
const std::unique_ptr< amrex::MultiFab > &  z_phys_nd,
const std::unique_ptr< amrex::MultiFab > &  z_phys_cc,
const MoistureComponentIndices &  moisture_indices 
)

Compute eddy diffusivities of momentum (eddy viscosity) and heat using the Mellor-Yamada-Nakanishi-Niino Level amrex::Real(2.5) PBL scheme

Parameters
[in]xvelVelocity in x-dir
[in]yvelVelocity in y-dir
[in]cons_inCell center conserved quantities
[out]eddyViscosityHolds turbulent viscosity
[in]geomProblem geometry
[in]turbChoiceContainer with turbulence parameters
[in]SurfLayerPointer to Monin-Obukhov class if instantiated
[in]use_moistureIf we have microphysics enabled
[in]levelCurrent level
[in]bc_ptrPointer to array with boundary condition info
[in]vert_onlyOnly compute vertical eddy diffusivities
[in]z_phys_ndPhysical location of grid nodes, if terrain (or grid stretching) is enabled
[in]z_phys_ccPhysical location of cell centers

◆ ComputeDiffusivityMYNNEDMF()

void ComputeDiffusivityMYNNEDMF ( const amrex::MultiFab &  xvel,
const amrex::MultiFab &  yvel,
const amrex::MultiFab &  cons_in,
amrex::MultiFab &  eddyViscosity,
const amrex::Geometry &  geom,
const TurbChoice &  turbChoice,
std::unique_ptr< SurfaceLayer > &  SurfLayer,
bool  use_terrain_fitted_coords,
bool  use_moisture,
int  level,
const amrex::BCRec *  bc_ptr,
bool  ,
const std::unique_ptr< amrex::MultiFab > &  z_phys_nd,
const std::unique_ptr< amrex::MultiFab > &  z_phys_cc,
const MoistureComponentIndices &  moisture_indices 
)

Compute eddy diffusivities of momentum (eddy viscosity) and heat using the Mellor-Yamada-Nakanishi-Niino Level amrex::Real(2.5) PBL scheme

Parameters
[in]xvelVelocity in x-dir
[in]yvelVelocity in y-dir
[in]cons_inCell center conserved quantities
[out]eddyViscosityHolds turbulent viscosity
[in]geomProblem geometry
[in]turbChoiceContainer with turbulence parameters
[in]SurfLayerPointer to Monin-Obukhov class if instantiated
[in]use_moistureIf we have microphysics enabled
[in]levelCurrent level
[in]bc_ptrPointer to array with boundary condition info
[in]vert_onlyOnly compute vertical eddy diffusivities
[in]z_phys_ndPhysical location of grid nodes, if terrain (or grid stretching) is enabled
[in]z_phys_ccPhysical location of cell centers

◆ ComputeDiffusivityYSU()

void ComputeDiffusivityYSU ( const amrex::MultiFab &  xvel,
const amrex::MultiFab &  yvel,
const amrex::MultiFab &  cons_in,
amrex::MultiFab &  eddyViscosity,
const amrex::Geometry &  geom,
const TurbChoice &  turbChoice,
std::unique_ptr< SurfaceLayer > &  SurfLayer,
bool  use_terrain_fitted_coords,
bool  use_moisture,
int  level,
const amrex::BCRec *  bc_ptr,
bool  ,
const std::unique_ptr< amrex::MultiFab > &  z_phys_nd,
const std::unique_ptr< amrex::MultiFab > &  z_phys_cc,
const MoistureComponentIndices &  moisture_indices 
)

Compute eddy diffusivities of momentum (eddy viscosity) and heat using the Yonsei University PBL scheme

Parameters
[in]xvelVelocity in x-dir
[in]yvelVelocity in y-dir
[in]cons_inCell center conserved quantities
[out]eddyViscosityHolds turbulent viscosity
[in]geomProblem geometry
[in]turbChoiceContainer with turbulence parameters
[in]SurfLayerPointer to Monin-Obukhov class if instantiated
[in]use_moistureIf we have microphysics enabled
[in]levelCurrent level
[in]bc_ptrPointer to array with boundary condition info
[in]vert_onlyOnly compute vertical eddy diffusivities
[in]z_phys_ndPhysical location of grid nodes, if terrain (or grid stretching) is enabled
[in]z_phys_ccPhysical location of cell centers

◆ ComputeDiffusivityYSUNew()

void ComputeDiffusivityYSUNew ( const amrex::MultiFab &  xvel,
const amrex::MultiFab &  yvel,
const amrex::MultiFab &  cons_in,
amrex::MultiFab &  eddyViscosity,
const amrex::Geometry &  geom,
const TurbChoice &  turbChoice,
std::unique_ptr< SurfaceLayer > &  SurfLayer,
bool  use_terrain_fitted_coords,
bool  use_moisture,
int  level,
const amrex::BCRec *  bc_ptr,
bool  ,
const std::unique_ptr< amrex::MultiFab > &  z_phys_nd,
const std::unique_ptr< amrex::MultiFab > &  z_phys_cc,
const MoistureComponentIndices &  moisture_indices,
const amrex::MultiFab *  qheating_rates = nullptr,
const amrex::MultiFab *  terrain_blank = nullptr 
)

Compute eddy diffusivities of momentum (eddy viscosity) and heat using the WRF Yonsei University (YSU) PBL scheme.

Based on: Hong, Noh & Dudhia (2006), MWR, https://doi.org/10.1175/MWR3250.1 Hong (2010), QJRMS, https://doi.org/10.1002/qj.665

Features: three-pass bulk-Richardson PBLH diagnosis, nonlocal countergradient fluxes, explicit entrainment at PBL top, and grid-adaptive Richardson-number mixing in free atmosphere.

Parameters
[in]xvelx-direction velocity
[in]yvely-direction velocity
[in]cons_incell-center conserved quantities
[out]eddyViscosityturbulent eddy viscosity
[in]geomproblem geometry
[in]turbChoiceturbulence parameter container
[in]SurfLayerMonin-Obukhov surface layer object
[in]use_terrain_fitted_coordsflag for terrain coordinates
[in]use_moistureflag for moisture active
[in]levelAMR level
[in]bc_ptrboundary condition array
[in]vert_onlycompute vertical diffusivity only
[in]z_phys_ndphysical node heights (terrain)
[in]z_phys_ccphysical cell-center heights
[in]moisture_indicescomponent index mapping for moisture variables
[in]qheating_ratesradiation heating rates (SW, LW components); optional

◆ ComputeQKESourceTerms()

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 
)

Function for computing the QKE source terms (NN09, Eqn. 5).

Parameters
[in]uvelocity in x-dir
[in]vvelocity in y-dir
[in]cell_dataconserved cell center vars
[in]cell_primprimitive cell center vars
[in]K_turbturbulent viscosity
[in]domainbox of the whole domain
[in]dz_invinverse spacings for interior and Dirichlet-boundary differences
[in]pbl_mynn_B1_la parameter
[in]theta_meanaverage theta
[in]moisture_indicesmoisture variable component indices
[in]tau13rho<u'w'> on z-faces; the surface-layer stress at the zlo face
[in]tau23rho<v'w'> on z-faces; the surface-layer stress at the zlo face
[in]hfx_zrho<w'theta'> on z-faces; the surface-layer heat flux at the zlo face
[in]qfx1_zrho<w'qv'> on z-faces; the surface-layer moisture flux at the zlo face
[in]surface_layer_on_zloa SurfaceLayer supplies the fluxes through the zlo face
479 {
480  amrex::Real source_term = zero;
481 
482  int izmin = domain.smallEnd(2);
483  int izmax = domain.bigEnd(2);
484 
485  const amrex::Real rho = cell_data(i,j,k,Rho_comp);
486 
487  // Notes:
488  // - We transport TKE = myhalf*QKE rather than QKE, so the RHS terms do not
489  // have a factor of two.
490  // - Transport terms due to turbulence and pressure are included when
491  // DiffusionSrcForState_* is called from ERF_slow_rhs_post.
492  // - Eddy diffusivities are updated at the beginning of each time step only.
493 
494  const bool use_surface_fluxes = ( surface_layer_on_zlo && (k == izmin) &&
495  tau13 && tau23 && hfx_z );
496 
497  if (use_surface_fluxes)
498  {
499  // The first cell center lies inside the unresolved surface layer. With
500  // zlo.type = surface_layer the state carries foextrap and the velocities
501  // hoextrap at the lower boundary, so the stencil used below reads ghost
502  // values that hold no surface information: the resolved gradients there
503  // are not a discretization of anything (ERF #4037). Build the production
504  // terms from the fluxes the model actually carries instead:
505  //
506  // tau13, tau23 = rho<u'w'>, rho<v'w'> (z-faces, staggered in x,y)
507  // hfx_z, qfx1_z = rho<w'theta'>, rho<w'qv'> (z-faces)
508  //
509  // Two different heights are wanted here, so the two are kept apart:
510  // - u_*, thetav_* and L are surface-layer parameters and have no
511  // cell-centered counterpart, so they come from the zlo face alone;
512  // - the production terms belong at the cell center, so their flux
513  // factors average the two z-faces bounding this cell.
514  // Only the profile shape, phi(zeta_1)/(kappa z_1), ties the two together.
515 
516  const amrex::Real theta = cell_data(i,j,k,RhoTheta_comp) / rho;
517  const amrex::Real qv = (moisture_indices.qv >= 0) ?
518  cell_data(i,j,k,moisture_indices.qv) / rho : zero;
519 
520  // ---- Surface-layer parameters, on the zlo face ----
521 
522  // |tau|/rho = u_*^2
523  const amrex::Real t13_s = myhalf * ( tau13(i,j,k) + tau13(i+1,j,k) );
524  const amrex::Real t23_s = myhalf * ( tau23(i,j,k) + tau23(i,j+1,k) );
525  const amrex::Real tau_s = std::sqrt(t13_s*t13_s + t23_s*t23_s);
526  const amrex::Real ustar2 = tau_s / rho;
527  const amrex::Real ustar = std::sqrt(ustar2);
528 
529  // Surface virtual heat flux rho<w'theta_v'> (Stull Eqn. 4.4.5d)
530  amrex::Real hfxv_s = hfx_z(i,j,k) * (one + epsv*qv);
531  if (qfx1_z) { hfxv_s += epsv * theta * qfx1_z(i,j,k); }
532 
533  // Height of this cell center above the surface. dz_inv.dirichlet_lo is by
534  // construction the inverse of the boundary-to-first-center distance, so it
535  // carries the right height for uniform, stretched and terrain-fitted grids.
536  const amrex::Real z1 = one / dz_inv.dirichlet_lo;
537 
538  // Obukhov length, from the surface fluxes. A vanishing stress (a building
539  // face, say) leaves the shear production zero whatever zeta is, so the
540  // neutral value is the safe one to carry into phi_m there.
541  const amrex::Real thetav = theta * (one + epsv*qv);
543  amrex::Real zeta = zero;
544  if (std::abs(hfxv_s) > eps && ustar > eps) {
545  const amrex::Real l_obukhov = -( thetav * rho * ustar2 * ustar )
546  / ( KAPPA * CONST_GRAV * hfxv_s );
547  zeta = z1 / l_obukhov;
548  }
549 
550  // phi_m, the companion of the psi_m that SurfaceLayer uses
551  const similarity_funs sfuns{};
552  const amrex::Real phi_m = sfuns.calc_phi_m(zeta);
553 
554  // ---- Production terms, at the cell center ----
555 
556  // Cell-centered stress: average the two z-faces bounding this cell
557  const amrex::Real t13_c = fourth * ( tau13(i,j,k ) + tau13(i+1,j,k )
558  + tau13(i,j,k+1) + tau13(i+1,j,k+1) );
559  const amrex::Real t23_c = fourth * ( tau23(i,j ,k) + tau23(i,j ,k+1)
560  + tau23(i,j+1,k) + tau23(i,j+1,k+1) );
561 
562  // Cell-centered virtual heat flux. Both faces are from this stage: the
563  // QKE sources are added after RhoQ1 is diffused (see AddTurbKESources).
564  amrex::Real hfxv_c = myhalf * ( hfx_z(i,j,k) + hfx_z(i,j,k+1) ) * (one + epsv*qv);
565  if (qfx1_z) {
566  hfxv_c += epsv * theta * myhalf * ( qfx1_z(i,j,k) + qfx1_z(i,j,k+1) );
567  }
568 
569  // Shear production, -<u'w'> dU/dz - <v'w'> dV/dz. MOST sets the direction
570  // of the mean shear anti-parallel to the surface stress and its magnitude
571  // to u_* phi_m/(kappa z_1), so the dot product is the cell-centered stress
572  // projected onto the surface stress. This reduces to |tau| u_* phi_m /
573  // (kappa z_1) when the stress does not turn across the cell.
574  if (tau_s > eps) {
575  const amrex::Real tau_proj = (t13_c*t13_s + t23_c*t23_s) / tau_s;
576  source_term += std::max(tau_proj,amrex::Real(0.)) * ustar * phi_m / (KAPPA * z1);
577  }
578 
579  // Buoyancy production, which needs no gradient at all
580  source_term += (CONST_GRAV/theta_mean) * hfxv_c;
581  }
582  else
583  {
584  // Compute some relevant derivatives
585  amrex::Real dthetadz, dudz, dvdz;
587  uvel, vvel, cell_data, izmin, izmax, dz_inv,
588  c_ext_dir_on_zlo, c_ext_dir_on_zhi,
589  u_ext_dir_on_zlo, u_ext_dir_on_zhi,
590  v_ext_dir_on_zlo, v_ext_dir_on_zhi,
591  dthetadz, dudz, dvdz,
592  moisture_indices);
593 
594  // Second-order turbulent fluxes, e.g.:
595  // -<uw> = L q SM dU/dz (NN09, Eqn. 18)
596  // = Kmv/rho dU/dz
597 
598  // Shear Production
599  source_term += K_turb(i,j,k,EddyDiff::Mom_v) * (dudz*dudz + dvdz*dvdz);
600 
601  // Buoyancy Production
602  source_term -= (CONST_GRAV/theta_mean)*K_turb(i,j,k,EddyDiff::Theta_v)*dthetadz;
603  }
604 
605  // Dissipation (NN09, Eqn. 12)
606  amrex::Real qke = two * cell_prim(i,j,k,PrimKE_comp);
607  if (std::abs(qke) > zero) {
608  source_term -= rho * std::pow(qke,amrex::Real(1.5)) /
609  (pbl_mynn_B1_l * K_turb(i,j,k,EddyDiff::Turb_lengthscale));
610  }
611 
612  return source_term;
613 }
constexpr amrex::Real epsv
Definition: ERF_Constants.H:52
constexpr amrex::Real CONST_GRAV
Definition: ERF_Constants.H:68
@ tau23
Definition: ERF_DataStruct.H:48
@ tau13
Definition: ERF_DataStruct.H:48
#define Rho_comp
Definition: ERF_IndexDefines.H:42
#define RhoTheta_comp
Definition: ERF_IndexDefines.H:43
#define PrimKE_comp
Definition: ERF_IndexDefines.H:62
constexpr amrex::Real two
Definition: ERF_NumericalConstants.H:38
constexpr amrex::Real fourth
Definition: ERF_NumericalConstants.H:42
constexpr amrex::Real myhalf
Definition: ERF_NumericalConstants.H:41
AMREX_GPU_DEVICE AMREX_FORCE_INLINE void ComputeVerticalDerivativesPBL(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 int izmin, const int izmax, const PBLDerivativeDzInv &dz_inv, const bool c_ext_dir_on_zlo, const bool c_ext_dir_on_zhi, const bool u_ext_dir_on_zlo, const bool u_ext_dir_on_zhi, const bool v_ext_dir_on_zlo, const bool v_ext_dir_on_zhi, amrex::Real &dthetadz, amrex::Real &dudz, amrex::Real &dvdz, const MoistureComponentIndices &moisture_indices, const StratType strat=StratType::thetav)
Definition: ERF_PBLModels.H:304
@ Theta_v
Definition: ERF_IndexDefines.H:253
@ Turb_lengthscale
Definition: ERF_IndexDefines.H:257
@ Mom_v
Definition: ERF_IndexDefines.H:252
@ ustar
Definition: ERF_SLM.H:28
@ rho
Definition: ERF_Kessler.H:25
@ theta
Definition: ERF_Kessler.H:26
@ qv
Definition: ERF_Kessler.H:31
real(c_double), parameter epsilon
Definition: ERF_module_model_constants.F90:12
int qv
water vapor
Definition: ERF_DataStruct.H:249
amrex::Real dirichlet_lo
Definition: ERF_PBLModels.H:252

Referenced by AddTurbKESources().

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

◆ ComputeVerticalDerivativesPBL()

AMREX_GPU_DEVICE AMREX_FORCE_INLINE void ComputeVerticalDerivativesPBL ( 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 int  izmin,
const int  izmax,
const PBLDerivativeDzInv &  dz_inv,
const bool  c_ext_dir_on_zlo,
const bool  c_ext_dir_on_zhi,
const bool  u_ext_dir_on_zlo,
const bool  u_ext_dir_on_zhi,
const bool  v_ext_dir_on_zlo,
const bool  v_ext_dir_on_zhi,
amrex::Real &  dthetadz,
amrex::Real &  dudz,
amrex::Real &  dvdz,
const MoistureComponentIndices &  moisture_indices,
const StratType  strat = StratType::thetav 
)
322 {
323  // ext_dir ghost values hold the value at the physical boundary, which is
324  // half a ghost-center spacing from the adjacent cell center. Use a
325  // two-point, first-order boundary-to-center difference with inverse
326  // distance 2/dz.
327  if ( k==izmax && c_ext_dir_on_zhi ) {
328  dthetadz = ( GetPBLStratVar(i,j,k+1,strat,cell_data,moisture_indices)
329  -GetPBLStratVar(i,j,k ,strat,cell_data,moisture_indices) )*dz_inv.dirichlet_hi;
330  } else if ( k==izmin && c_ext_dir_on_zlo ) {
331  dthetadz = ( GetPBLStratVar(i,j,k ,strat,cell_data,moisture_indices)
332  -GetPBLStratVar(i,j,k-1,strat,cell_data,moisture_indices) )*dz_inv.dirichlet_lo;
333  } else {
334  dthetadz = ( GetPBLStratVar(i,j,k+1,strat,cell_data,moisture_indices)
335  -GetPBLStratVar(i,j,k-1,strat,cell_data,moisture_indices) )*dz_inv.interior;
336  }
337 
338  if ( k==izmax && u_ext_dir_on_zhi ) {
339  dudz = myhalf*( uvel(i ,j,k+1) - uvel(i ,j,k)
340  +uvel(i+1,j,k+1) - uvel(i+1,j,k) )*dz_inv.dirichlet_hi;
341  } else if ( k==izmin && u_ext_dir_on_zlo ) {
342  dudz = myhalf*( uvel(i ,j,k) - uvel(i ,j,k-1)
343  +uvel(i+1,j,k) - uvel(i+1,j,k-1) )*dz_inv.dirichlet_lo;
344  } else {
345  dudz = myhalf*( uvel(i ,j,k+1) - uvel(i ,j,k-1)
346  +uvel(i+1,j,k+1) - uvel(i+1,j,k-1) )*dz_inv.interior;
347  }
348 
349  if ( k==izmax && v_ext_dir_on_zhi ) {
350  dvdz = myhalf*( vvel(i,j ,k+1) - vvel(i,j ,k)
351  +vvel(i,j+1,k+1) - vvel(i,j+1,k) )*dz_inv.dirichlet_hi;
352  } else if ( k==izmin && v_ext_dir_on_zlo ) {
353  dvdz = myhalf*( vvel(i,j ,k) - vvel(i,j ,k-1)
354  +vvel(i,j+1,k) - vvel(i,j+1,k-1) )*dz_inv.dirichlet_lo;
355  } else {
356  dvdz = myhalf*( vvel(i,j ,k+1) - vvel(i,j ,k-1)
357  +vvel(i,j+1,k+1) - vvel(i,j+1,k-1) )*dz_inv.interior;
358  }
359 }
AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real GetPBLStratVar(int i, int j, int k, const StratType strat, const amrex::Array4< const amrex::Real > &cell_data, const MoistureComponentIndices &moisture_indices)
Definition: ERF_PBLModels.H:288
amrex::Real dirichlet_hi
Definition: ERF_PBLModels.H:253
amrex::Real interior
Definition: ERF_PBLModels.H:251

Referenced by ComputeDiffusivityMRF(), ComputeDiffusivityMYJ(), ComputeDiffusivityMYNN25(), ComputeDiffusivityMYNNEDMF(), ComputeDiffusivityYSU(), ComputeDiffusivityYSUNew(), and ComputeQKESourceTerms().

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

◆ ComputeVirtualTStarPBL()

AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real ComputeVirtualTStarPBL ( amrex::Real  t_star,
amrex::Real  q_star,
amrex::Real  theta,
amrex::Real  qv,
bool  use_moisture 
)

Virtual temperature scale, thetav_* = theta_*(1 + epsv qv) + epsv theta q_*. The surface virtual heat flux is <w'thetav'> = -u_* thetav_*.

Parameters
[in]t_startemperature scale
[in]q_starmoisture scale
[in]thetapotential temperature
[in]qvwater vapor mixing ratio
[in]use_moisturewhether the moisture term is active
391 {
392  amrex::Real tstar_v = t_star * (one + epsv*qv);
393  if (use_moisture) { tstar_v += epsv * theta * q_star; }
394  return tstar_v;
395 }
const bool use_moisture
Definition: ERF_InitCustomPert_ABL.H:71

Referenced by ComputeDiffusivityMYJ(), ComputeDiffusivityMYNN25(), ComputeDiffusivityMYNNEDMF(), ComputeDiffusivityYSU(), and ComputeDiffusivityYSUNew().

Here is the caller graph for this function:

◆ GetPBLStratVar()

AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real GetPBLStratVar ( int  i,
int  j,
int  k,
const StratType  strat,
const amrex::Array4< const amrex::Real > &  cell_data,
const MoistureComponentIndices &  moisture_indices 
)

Compute vertical derivatives of potential temperature and velocity for PBL models.

Parameters
ix-index
jy-index
kz-index
[in]uvelx-velocity
[in]vvely-velocity
[in]cell_dataconserved cell-center variables
[in]izminlower z-index limit
[in]izmaxupper z-index limit
[in]dz_invinverse physical distance spacings
[in]c_ext_dir_on_zloDirichlet condition on z-low face for cell data
[in]c_ext_dir_on_zhiDirichlet condition on z-high face for cell data
[in]u_ext_dir_on_zloDirichlet condition on z-low face for u-velocity
[in]u_ext_dir_on_zhiDirichlet condition on z-high face for u-velocity
[in]v_ext_dir_on_zloDirichlet condition on z-low face for v-velocity
[in]v_ext_dir_on_zhiDirichlet condition on z-high face for v-velocity
[out]dthetadzcomputed vertical derivative of potential temperature
[out]dudzcomputed vertical derivative of x-velocity
[out]dvdzcomputed vertical derivative of y-velocity
[in]moisture_indicesmoisture variable component indices Thermodynamic variable whose vertical gradient the PBL schemes use for buoyancy, selected by erf.thermal_stratification.
[in]stratstratification variable to return
292 {
293  if (strat == StratType::theta) {
294  return cell_data(i,j,k,RhoTheta_comp) / cell_data(i,j,k,Rho_comp);
295  } else if (strat == StratType::thetal) {
296  return GetThetal(i,j,k,cell_data,moisture_indices);
297  }
298  return GetThetav(i,j,k,cell_data,moisture_indices);
299 }
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

Referenced by ComputeVerticalDerivativesPBL().

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