ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
SurfaceLayer Class Reference

#include <ERF_SurfaceLayer.H>

Collaboration diagram for SurfaceLayer:

Public Types

enum class  FluxCalcType {
  MOENG = 0 , DONELAN , CUSTOM , BULK_COEFF ,
  ROTATE , RICO
}
 
enum class  ThetaCalcType { ADIABATIC = 0 , HEAT_FLUX , SURFACE_TEMPERATURE }
 
enum class  MoistCalcType { ADIABATIC = 0 , MOISTURE_FLUX , SURFACE_MOISTURE }
 
enum class  RoughCalcType {
  CONSTANT = 0 , CHARNOCK , MODIFIED_CHARNOCK , DONELAN ,
  WAVE_COUPLED
}
 
enum class  PBLHeightCalcType {
  None , MYNN25 , YSU , MRF ,
  YSUNew
}
 

Public Member Functions

 SurfaceLayer (const amrex::Vector< amrex::Geometry > &geom, bool &use_rot_surface_flux, std::string a_pp_prefix, amrex::Vector< std::unique_ptr< amrex::MultiFab >> &Qv_prim, amrex::Vector< std::unique_ptr< amrex::MultiFab >> &z_phys_nd, const MeshType &a_mesh_type, const TerrainType &a_terrain_type, const TurbChoice &a_turb_choice, double start_low_time, double final_low_time, double low_time_interval=0.0, const amrex::Vector< const eb_ * > &eb_vec={})
 
void make_SurfaceLayer_at_level (const int &lev, int nlevs, const amrex::Vector< amrex::MultiFab * > &mfv, std::unique_ptr< amrex::MultiFab > &Theta_prim, std::unique_ptr< amrex::MultiFab > &Qv_prim, std::unique_ptr< amrex::MultiFab > &Qr_prim, std::unique_ptr< amrex::MultiFab > &z_phys_nd, amrex::MultiFab *Hwave, amrex::MultiFab *Lwave, amrex::MultiFab *eddyDiffs, amrex::Vector< amrex::MultiFab * > lsm_data, amrex::Vector< std::string > lsm_data_name, amrex::Vector< amrex::MultiFab * > lsm_flux, amrex::Vector< std::string > lsm_flux_name, amrex::Vector< std::unique_ptr< amrex::MultiFab >> &sst_lev, amrex::Vector< std::unique_ptr< amrex::MultiFab >> &tsk_lev, amrex::Vector< std::unique_ptr< amrex::iMultiFab >> &lmask_lev)
 
void update_fluxes (const int &lev, const double &elapsed_time, const double &elapsed_time_since_start_low, amrex::MultiFab &cons_in, const std::unique_ptr< amrex::MultiFab > &z_phys_nd, const std::unique_ptr< amrex::MultiFab > &walldist, int max_iters=100)
 
template<typename FluxIter >
void compute_fluxes (const int &lev, const int &max_iters, amrex::MultiFab &cons_in, const FluxIter &most_flux, bool is_land)
 
void init_tke_from_ustar (const int &lev, amrex::MultiFab &cons, const std::unique_ptr< amrex::MultiFab > &z_phys_nd, const amrex::Real tkefac=one, const amrex::Real zscale=amrex::Real(700.0))
 
void impose_SurfaceLayer_bcs (const int &lev, amrex::Vector< const amrex::MultiFab * > mfs, amrex::Vector< std::unique_ptr< amrex::MultiFab >> &Tau_lev, amrex::MultiFab *xheat_flux, amrex::MultiFab *yheat_flux, amrex::MultiFab *zheat_flux, amrex::MultiFab *xqv_flux, amrex::MultiFab *yqv_flux, amrex::MultiFab *zqv_flux, const amrex::MultiFab *z_phys)
 
void impose_SurfaceLayer_bcs_EB (const int &lev, amrex::Vector< const amrex::MultiFab * > mfs, amrex::Vector< amrex::Vector< std::unique_ptr< amrex::MultiFab >>> &Tau_lev, amrex::MultiFab *xheat_flux, amrex::MultiFab *yheat_flux, amrex::MultiFab *zheat_flux, amrex::MultiFab *xqv_flux, amrex::MultiFab *yqv_flux, amrex::MultiFab *zqv_flux)
 
template<typename FluxCalc >
void compute_SurfaceLayer_bcs (const int &lev, amrex::Vector< const amrex::MultiFab * > mfs, amrex::Vector< std::unique_ptr< amrex::MultiFab >> &Tau_lev, amrex::MultiFab *xheat_flux, amrex::MultiFab *yheat_flux, amrex::MultiFab *zheat_flux, amrex::MultiFab *xqv_flux, amrex::MultiFab *yqv_flux, amrex::MultiFab *zqv_flux, const amrex::MultiFab *z_phys, const FluxCalc &flux_comp)
 
template<typename FluxCalc >
void compute_SurfaceLayer_bcs_EB (const int &lev, amrex::Vector< const amrex::MultiFab * > mfs, amrex::Vector< amrex::Vector< std::unique_ptr< amrex::MultiFab >>> &Tau_lev, amrex::MultiFab *xheat_flux, amrex::MultiFab *yheat_flux, amrex::MultiFab *zheat_flux, amrex::MultiFab *xqv_flux, amrex::MultiFab *yqv_flux, amrex::MultiFab *zqv_flux, const FluxCalc &flux_comp)
 
void compute_sfc_params_from_lsm_fluxes (const int &lev, amrex::MultiFab &cons_in)
 
void fill_tsurf_with_sst_and_tsk (const int &lev, const double &time)
 
void fill_tsurf_with_sfc_sst (const int &lev, const double &time)
 
void fill_tsurf_with_coupled_sst (const int &lev)
 
void fill_qsurf_with_qsat (const int &lev, const amrex::MultiFab &cons_in, const std::unique_ptr< amrex::MultiFab > &z_phys_nd)
 
void update_sfc_time_index (const amrex::Real &time)
 
amrex::Real interpolate_sfc_column (const amrex::Real &time, int col) const
 
void get_lsm_tsurf (const int &lev)
 
void update_pblh (const int &lev, amrex::Vector< amrex::Vector< amrex::MultiFab >> &vars, amrex::MultiFab *z_phys_cc, const MoistureComponentIndices &moisture_indices)
 
template<typename PBLHeightEstimator >
void compute_pblh (const int &lev, amrex::Vector< amrex::Vector< amrex::MultiFab >> &vars, amrex::MultiFab *z_phys_cc, const PBLHeightEstimator &est, const MoistureComponentIndices &moisture_indice)
 
void read_custom_roughness (const int &lev, const std::string &fname)
 
void update_surf_temp (const double &time)
 
void update_mac_ptrs (const int &lev, amrex::Vector< amrex::Vector< amrex::MultiFab >> &vars_old, amrex::Vector< std::unique_ptr< amrex::MultiFab >> &Theta_prim, amrex::Vector< std::unique_ptr< amrex::MultiFab >> &Qv_prim, amrex::Vector< std::unique_ptr< amrex::MultiFab >> &Qr_prim)
 
amrex::MultiFab * get_u_star (const int &lev)
 
amrex::MultiFab * get_w_star (const int &lev)
 
bool computes_w_star () const
 
bool computes_pblh () const
 
amrex::MultiFab * get_t_star (const int &lev)
 
amrex::MultiFab * get_q_star (const int &lev)
 
amrex::MultiFab * get_olen (const int &lev)
 
amrex::MultiFab * get_pblh (const int &lev)
 
const amrex::MultiFab * get_mac_avg (const int &lev, int comp)
 
bool mac_avg_is_time_averaged () const
 
int get_num_mac_avg () const
 
bool mac_avg_is_initialized (const int &lev) const
 
void set_mac_avg_initialized (const int &lev)
 
amrex::MultiFab * get_mac_avg_ptr (const int &lev, int comp)
 
amrex::Vector< amrex::Realget_mac_plane_avg (const int &lev) const
 
bool set_mac_plane_avg (const int &lev, const amrex::Vector< amrex::Real > &pavg)
 
amrex::MultiFab * get_t_surf (const int &lev)
 
void set_t_surf (const int &lev, const amrex::Real tsurf)
 
amrex::MultiFab * get_q_surf (const int &lev)
 
void set_q_surf (const int &lev, const amrex::Real qsurf)
 
amrex::MultiFab * get_surface_diagnostic_source (const int &lev)
 
amrex::Real get_zref (const int &lev)
 
amrex::MultiFab * get_z0 (const int &lev)
 
bool have_variable_sea_roughness ()
 
amrex::iMultiFab * get_lmask (const int &lev)
 
int lmask_min_reduce (amrex::iMultiFab &lmask, const int &nghost)
 
void update_sst_ptr (const int lev, const int itime, amrex::MultiFab *sst_ptr)
 
void update_tsk_ptr (const int lev, const int itime, amrex::MultiFab *tsk_ptr)
 
void update_coupled_sst_ptr (const int lev, amrex::MultiFab *sst_ptr, amrex::iMultiFab *valid_ptr)
 
void set_coupled_sst_active (const bool active)
 
template<typename FluxIter >
void compute_fluxes (const int &lev, const int &max_iters, MultiFab &cons_in, const FluxIter &most_flux, bool is_land)
 
template<typename FluxCalc >
void compute_SurfaceLayer_bcs (const int &lev, Vector< const MultiFab * > mfs, Vector< std::unique_ptr< MultiFab >> &Tau_lev, MultiFab *xheat_flux, MultiFab *yheat_flux, MultiFab *zheat_flux, MultiFab *xqv_flux, MultiFab *yqv_flux, MultiFab *zqv_flux, const MultiFab *z_phys, const FluxCalc &flux_comp)
 
template<typename FluxCalc >
void compute_SurfaceLayer_bcs_EB (const int &lev, Vector< const MultiFab * > mfs, Vector< Vector< std::unique_ptr< MultiFab >>> &Tau_EB, [[maybe_unused]] MultiFab *xheat_flux, [[maybe_unused]] MultiFab *yheat_flux, MultiFab *Hfx3_EB, [[maybe_unused]] MultiFab *xqv_flux, [[maybe_unused]] MultiFab *yqv_flux, [[maybe_unused]] MultiFab *zqv_flux, const FluxCalc &flux_comp)
 
template<typename PBLHeightEstimator >
void compute_pblh (const int &lev, Vector< Vector< MultiFab >> &vars, MultiFab *z_phys_cc, const PBLHeightEstimator &est, const MoistureComponentIndices &moisture_indices)
 

Static Public Member Functions

static amrex::Vector< amrex::Vector< amrex::Real > > read_cols (const std::string &fname, const int skip_nlines=1)
 

Public Attributes

FluxCalcType flux_type {FluxCalcType::MOENG}
 
ThetaCalcType theta_type {ThetaCalcType::ADIABATIC}
 
MoistCalcType moist_type {MoistCalcType::ADIABATIC}
 
RoughCalcType rough_type_land {RoughCalcType::CONSTANT}
 
RoughCalcType rough_type_sea {RoughCalcType::CHARNOCK}
 
PBLHeightCalcType pblh_type {PBLHeightCalcType::None}
 

Private Attributes

amrex::Vector< amrex::Geometry > m_geom
 
bool m_rotate = false
 
double m_start_low_time
 
double m_final_low_time
 
double m_low_time_interval
 
bool m_include_wstar = false
 
amrex::Real z0_const {amrex::Real(0.1)}
 
amrex::Real default_land_surf_temp {amrex::Real(300.)}
 
amrex::Real surf_temp {amrex::Real(-1.)}
 
amrex::Real surf_heating_rate {0}
 
amrex::Real surf_temp_flux {0}
 
amrex::Real default_land_surf_moist {zero}
 
amrex::Real surf_moist {amrex::Real(-1.)}
 
amrex::Real surf_moist_flux {0}
 
amrex::Real custom_ustar {0}
 
amrex::Real custom_tstar {0}
 
amrex::Real custom_qstar {0}
 
amrex::Real custom_rhosurf {0}
 
bool specified_rho_surf {false}
 
amrex::Real cnk_a {amrex::Real(0.0185)}
 
bool cnk_visc {false}
 
amrex::Real depth {amrex::Real(30.0)}
 
amrex::Vector< amrex::MultiFab > z_0
 
bool m_var_z0 {false}
 
amrex::Real rico_theta_z0 {amrex::Real(298.0)}
 
amrex::Real rico_qsat_z0 {amrex::Real(0.001)}
 
bool use_moisture
 
bool m_has_lsm_fluxes = false
 
bool m_has_lsm_tsurf = false
 
int m_lsm_tsurf_indx = -1
 
bool m_use_sfc_fluxes = false
 
bool m_use_sfc_sst = false
 
int sfc_time_ind = 0
 
amrex::Vector< amrex::Vector< amrex::Real > > sfc
 
amrex::Real sfc_qflux = zero
 
amrex::Real sfc_tflux = zero
 
amrex::Real sfc_ustar = zero
 
bool m_use_coupled_sst = false
 
amrex::Real m_Cd = zero
 
amrex::Real m_Ch = zero
 
amrex::Real m_Cq = zero
 
bool m_ignore_sst = false
 
amrex::Vector< const eb_ * > m_eb_vec
 
TerrainType m_terrain_type
 
MOSTAverage m_ma
 
amrex::Vector< std::unique_ptr< amrex::MultiFab > > u_star
 
amrex::Vector< std::unique_ptr< amrex::MultiFab > > w_star
 
amrex::Vector< std::unique_ptr< amrex::MultiFab > > t_star
 
amrex::Vector< std::unique_ptr< amrex::MultiFab > > q_star
 
amrex::Vector< std::unique_ptr< amrex::MultiFab > > olen
 
amrex::Vector< std::unique_ptr< amrex::MultiFab > > pblh
 
amrex::Vector< std::unique_ptr< amrex::MultiFab > > t_surf
 
amrex::Vector< std::unique_ptr< amrex::MultiFab > > q_surf
 
amrex::Vector< std::unique_ptr< amrex::MultiFab > > surface_diagnostic_source
 
amrex::Vector< amrex::Vector< amrex::MultiFab * > > m_sst_lev
 
amrex::Vector< amrex::Vector< amrex::MultiFab * > > m_tsk_lev
 
amrex::Vector< amrex::Vector< amrex::iMultiFab * > > m_lmask_lev
 
amrex::Vector< amrex::MultiFab * > m_coupled_sst_lev
 
amrex::Vector< amrex::iMultiFab * > m_coupled_sst_valid_lev
 
amrex::Vector< amrex::Vector< amrex::MultiFab * > > m_lsm_data_lev
 
amrex::Vector< amrex::Vector< amrex::MultiFab * > > m_lsm_flux_lev
 
amrex::Vector< std::string > m_lsm_data_name
 
amrex::Vector< std::string > m_lsm_flux_name
 
amrex::Vector< amrex::MultiFab * > m_Hwave_lev
 
amrex::Vector< amrex::MultiFab * > m_Lwave_lev
 
amrex::Vector< amrex::MultiFab * > m_eddyDiffs_lev
 
bool m_update_k_rans = false
 
amrex::Real inv_Cmu2 = zero
 
amrex::Real theta_ref = zero
 

Detailed Description

Abstraction layer for different surface layer schemes (e.g. MOST, Cd)

van der Laan, P., Kelly, M. C., & Sørensen, N. N. (2017). A new k-epsilon model consistent with Monin-Obukhov similarity theory. Wind Energy, 20(3), 479–amrex::Real(489.) https://doi.org/amrex::Real(10.1002)/we.2017

Consistent with Dyer (1974) formulation from page 57, Chapter 2, Modeling the vertical ABL structure in Modelling of Atmospheric Flow Fields, Demetri P Lalas and Corrado F Ratto, January 1996, https://doi.org/amrex::Real(10.1142)/amrex::Real(2975.)

Member Enumeration Documentation

◆ FluxCalcType

Enumerator
MOENG 

Moeng functional form.

DONELAN 

Donelan functional form.

CUSTOM 

Custom constant flux functional form.

BULK_COEFF 

Bulk transfer coefficient functional form.

ROTATE 

Terrain rotation flux functional form.

RICO 
1205  {
1206  MOENG = 0, ///< Moeng functional form
1207  DONELAN, ///< Donelan functional form
1208  CUSTOM, ///< Custom constant flux functional form
1209  BULK_COEFF, ///< Bulk transfer coefficient functional form
1210  ROTATE, ///< Terrain rotation flux functional form
1211  RICO
1212  };

◆ MoistCalcType

Enumerator
ADIABATIC 
MOISTURE_FLUX 

Qv-flux specified.

SURFACE_MOISTURE 

Surface Qv specified.

1220  {
1221  ADIABATIC = 0,
1222  MOISTURE_FLUX, ///< Qv-flux specified
1223  SURFACE_MOISTURE ///< Surface Qv specified
1224  };

◆ PBLHeightCalcType

Enumerator
None 
MYNN25 
YSU 
MRF 
YSUNew 
1234 { None, MYNN25, YSU, MRF, YSUNew };

◆ RoughCalcType

Enumerator
CONSTANT 

Constant z0.

CHARNOCK 
MODIFIED_CHARNOCK 
DONELAN 
WAVE_COUPLED 
1226  {
1227  CONSTANT = 0, ///< Constant z0
1228  CHARNOCK,
1229  MODIFIED_CHARNOCK,
1230  DONELAN,
1231  WAVE_COUPLED
1232  };

◆ ThetaCalcType

Enumerator
ADIABATIC 
HEAT_FLUX 

Heat-flux specified.

SURFACE_TEMPERATURE 

Surface temperature specified.

1214  {
1215  ADIABATIC = 0,
1216  HEAT_FLUX, ///< Heat-flux specified
1217  SURFACE_TEMPERATURE ///< Surface temperature specified
1218  };

Constructor & Destructor Documentation

◆ SurfaceLayer()

SurfaceLayer::SurfaceLayer ( const amrex::Vector< amrex::Geometry > &  geom,
bool &  use_rot_surface_flux,
std::string  a_pp_prefix,
amrex::Vector< std::unique_ptr< amrex::MultiFab >> &  Qv_prim,
amrex::Vector< std::unique_ptr< amrex::MultiFab >> &  z_phys_nd,
const MeshType &  a_mesh_type,
const TerrainType &  a_terrain_type,
const TurbChoice a_turb_choice,
double  start_low_time,
double  final_low_time,
double  low_time_interval = 0.0,
const amrex::Vector< const eb_ * > &  eb_vec = {} 
)
inlineexplicit

Construct the surface-layer interface.

Parameters
[in]geomgeometry for all AMR levels
[in,out]use_rot_surface_fluxwhether to use rotated surface fluxes
[in]a_pp_prefixParmParse prefix used by MOST averages
[in]Qv_primprimitive water-vapor fields by level
[in]z_phys_ndnodal physical-height fields by level
[in]a_mesh_typemesh type
[in]a_terrain_typeterrain representation
[in]a_turb_choiceturbulence-model options
[in]start_low_timefirst available low-boundary-data time
[in]final_low_timefinal available low-boundary-data time
[in]low_time_intervallow-boundary-data time interval
[in]eb_vecoptional embedded-boundary geometry data
69  {})
70  : m_geom(geom),
71  m_rotate(use_rot_surface_flux),
72  m_start_low_time(start_low_time),
73  m_final_low_time(final_low_time),
74  m_low_time_interval(low_time_interval),
75  m_eb_vec(eb_vec),
76  m_terrain_type(a_terrain_type),
77  m_ma(geom, (z_phys_nd[0] != nullptr), a_pp_prefix, a_mesh_type, a_terrain_type, eb_vec)
78  {
79  // We have a moisture model if Qv_prim is a valid pointer
80  use_moisture = (Qv_prim[0].get());
81 
82  // Get roughness
83  amrex::ParmParse pp("erf");
84  pp.queryAdd("most.z0", z0_const);
85 
86  // Specify how to compute the flux
87  if (use_rot_surface_flux) {
89  } else {
90  std::string flux_string_in;
91  std::string flux_string{"moeng"};
92  auto read_flux = pp.queryAdd("surface_layer.flux_type", flux_string_in);
93  if (read_flux) {
94  flux_string = amrex::toLower(flux_string_in);
95  }
96  if (flux_string == "donelan") {
98  } else if (flux_string == "moeng") {
100  } else if (flux_string == "rico") {
102  } else if (flux_string == "bulk_coeff") {
104  } else if (flux_string == "custom") {
106  } else {
107  amrex::Abort("Undefined MOST flux type!");
108  }
109  }
110 
111  // Include w* to handle free convection (Beljaars 1995, QJRMS)
112  pp.queryAdd("most.include_wstar", m_include_wstar);
113 
114  std::string pblh_string_in;
115  std::string pblh_string{"none"};
116  auto read_pblh = pp.queryAdd("most.pblh_calc", pblh_string_in);
117  if (read_pblh) {
118  pblh_string = amrex::toLower(pblh_string_in);
119  }
120  if (pblh_string == "none") {
122  } else if (pblh_string == "mynn25") {
124  } else if (pblh_string == "mynnedmf") {
126  } else if (pblh_string == "ysu") {
128  } else if (pblh_string == "mrf") {
130  } else {
131  amrex::Abort("Undefined PBLH calc type!");
132  }
133 
134  // The w* correction is computed from the PBL height, so it needs a scheme that
135  // actually diagnoses one. With pblh_calc = "none" the pblh MultiFab keeps the
136  // bogus_large_value it was initialized with and calc_wstar turns that into a
137  // convective velocity scale of ~1e50, which destroys the surface fluxes.
139  amrex::Abort("erf.most.include_wstar requires a PBL height: set "
140  "erf.most.pblh_calc (MYNN25 is the only scheme implemented)");
141  }
142 
143  // Get surface temperature. surf_temp and surf_moist are declared with negative
144  // sentinels (see below) so that "did the user set this" is a property of the
145  // value rather than of the queryAdd return value, which only reports whether the
146  // key existed before the call and so stops being meaningful once anything has
147  // parsed the key. Both most.surf_temp and most.surf_moist are also parsed by
148  // ERF_InputSoundingData.H, so the two sites would poison each other otherwise.
149  pp.queryAdd("most.surf_temp", surf_temp);
150  const bool erf_st = (surf_temp > amrex::Real(0));
151  if (erf_st) { default_land_surf_temp = surf_temp; }
152 
153  // Get surface moisture
154  bool erf_sq = false;
155  if (use_moisture) {
156  pp.queryAdd("most.surf_moist", surf_moist);
157  erf_sq = (surf_moist >= amrex::Real(0));
158  }
159  if (erf_sq) { default_land_surf_moist = surf_moist; }
160 
161  // Custom type user must specify the fluxes
166  pp.get("most.ustar", custom_ustar);
167  pp.get("most.tstar", custom_tstar);
168  pp.get("most.qstar", custom_qstar);
169  pp.queryAdd("most.rhosurf", custom_rhosurf);
170  if (custom_qstar != 0) {
172  "Specified custom MOST qv flux without moisture model!");
173  }
174  amrex::Print() << "Using specified ustar, tstar, qstar for MOST = "
175  << custom_ustar << " " << custom_tstar << " "
176  << custom_qstar << std::endl;
177 
178  // Bulk transfer coefficient (must specify coeffs and surface values)
179  } else if (flux_type == FluxCalcType::BULK_COEFF) {
180  pp.get("most.Cd", m_Cd);
181  pp.get("most.Ch", m_Ch);
182  pp.get("most.Cq", m_Cq);
183  pp.get("most.surf_temp", default_land_surf_temp);
184  pp.get("most.surf_moist", default_land_surf_moist);
185  amrex::Print() << "Using specified Cd, Ch, Cq for MOST = "
186  << m_Cd << " " << m_Ch << " "
187  << m_Cq << std::endl;
188 
189  // Specify surface temperature/moisture or surface flux
190  } else {
191  if (erf_st) {
193  pp.queryAdd("most.surf_heating_rate", surf_heating_rate); // [K/h]
194 
195  // Modify rate to be in units of K / s rather than K / hr
196  surf_heating_rate /= amrex::Real(3600.0); // [K/s]
197 
198  if (pp.query("most.surf_temp_flux", surf_temp_flux)) {
199  amrex::Abort("Can only specify one of surf_temp_flux or surf_heating_rate");
200  }
201  } else {
202  pp.queryAdd("most.surf_temp_flux", surf_temp_flux);
203 
204  if (pp.query("most.surf_heating_rate", surf_heating_rate)) {
205  amrex::Abort("Can only specify one of surf_temp_flux or surf_heating_rate");
206  }
207  if (std::abs(surf_temp_flux) >
210  } else {
212  }
213  }
214 
215  if (erf_sq) {
217  } else {
218  pp.queryAdd("most.surf_moist_flux", surf_moist_flux);
219  if (std::abs(surf_moist_flux) >
222  } else {
224  }
225  }
226  }
227 
229  {
230  pp.queryAdd("most.rico.theta_z0", rico_theta_z0);
231  pp.queryAdd("most.rico.qsat_z0", rico_qsat_z0);
232  }
233 
234  // Make sure the inputs file doesn't try to use most.roughness_type
235  std::string bogus_input;
236  if (pp.queryAdd("most.roughness_type", bogus_input) > 0) {
237  amrex::Abort("most.roughness_type is deprecated; use "
238  "most.roughness_type_land and/or most.roughness_type_sea");
239  }
240 
241  // Specify how to compute the surface flux over land (if there is any)
242  std::string rough_land_string_in;
243  std::string rough_land_string{"constant"};
244  auto read_rough_land =
245  pp.queryAdd("most.roughness_type_land", rough_land_string_in);
246  if (read_rough_land) {
247  rough_land_string = amrex::toLower(rough_land_string_in);
248  }
249  if (rough_land_string == "constant") {
251  } else {
252  amrex::Abort("Undefined MOST roughness type for land!");
253  }
254 
255  // Specify how to compute the surface flux over sea (if there is any)
256  std::string rough_sea_string_in;
257  std::string rough_sea_string{"charnock"};
258  auto read_rough_sea = pp.queryAdd("most.roughness_type_sea", rough_sea_string_in);
259  if (read_rough_sea) {
260  rough_sea_string = amrex::toLower(rough_sea_string_in);
261  }
262  if (rough_sea_string == "charnock") {
264  pp.queryAdd("most.charnock_constant", cnk_a);
265  pp.queryAdd("most.charnock_viscosity", cnk_visc);
266  if (cnk_a > 0) {
267  amrex::Print() << "If there is water, Charnock relation with C_a="
268  << cnk_a << (cnk_visc ? " and viscosity" : "")
269  << " will be used" << std::endl;
270  } else {
271  amrex::Print() << "If there is water, Charnock relation with variable "
272  "Charnock parameter (COARE3.0)"
273  << (cnk_visc ? " and viscosity" : "") << " will be used"
274  << std::endl;
275  }
276  } else if (rough_sea_string == "coare3.0") {
278  amrex::Print() << "If there is water, Charnock relation with variable "
279  "Charnock parameter (COARE3.0)"
280  << (cnk_visc ? " and viscosity" : "") << " will be used"
281  << std::endl;
282  cnk_a = -1;
283  } else if (rough_sea_string == "donelan") {
285  } else if (rough_sea_string == "modified_charnock") {
287  pp.queryAdd("most.modified_charnock_depth", depth);
288  } else if (rough_sea_string == "wave_coupled") {
290  } else if (rough_sea_string == "constant") {
292  } else {
293  amrex::Abort("Undefined MOST roughness type for sea!");
294  }
295 
296  // use skin temperature instead of sea-surface temperature
297  // (wrfinput data may have lower resolution SST data)
298  pp.queryAdd("most.ignore_sst", m_ignore_sst);
299 
300  // If we're using the RANS k model, then we need to update the dirichlet
301  // BC based on the instantaneous u* and θ*; the turbulence modeling
302  // choices can vary per level but for now, assume that if specified then
303  // all levels are using the same RANS model.
304  m_update_k_rans = (a_turb_choice.rans_type == RANSType::kEqn &&
305  a_turb_choice.dirichlet_k == true);
306  if (m_update_k_rans) {
307  inv_Cmu2 = one / (a_turb_choice.Cmu0 * a_turb_choice.Cmu0);
308  theta_ref = a_turb_choice.theta_ref;
309  }
310 
311  } // constructor
constexpr amrex::Real one
Definition: ERF_Constants.H:9
ParmParse pp("prob")
amrex::Real Real
Definition: ERF_ShocInterface.H:19
AMREX_ASSERT_WITH_MESSAGE(wbar_cutoff_min > wbar_cutoff_max, "ERROR: wbar_cutoff_min < wbar_cutoff_max")
ThetaCalcType theta_type
Definition: ERF_SurfaceLayer.H:1237
bool m_include_wstar
Definition: ERF_SurfaceLayer.H:1251
bool m_rotate
Definition: ERF_SurfaceLayer.H:1246
PBLHeightCalcType pblh_type
Definition: ERF_SurfaceLayer.H:1241
double m_final_low_time
Definition: ERF_SurfaceLayer.H:1248
bool use_moisture
Definition: ERF_SurfaceLayer.H:1279
amrex::Real m_Cq
Definition: ERF_SurfaceLayer.H:1300
amrex::Vector< const eb_ * > m_eb_vec
Definition: ERF_SurfaceLayer.H:1304
RoughCalcType rough_type_land
Definition: ERF_SurfaceLayer.H:1239
amrex::Real z0_const
Definition: ERF_SurfaceLayer.H:1252
amrex::Real cnk_a
Definition: ERF_SurfaceLayer.H:1270
amrex::Real m_Ch
Definition: ERF_SurfaceLayer.H:1299
amrex::Real surf_temp
Definition: ERF_SurfaceLayer.H:1259
double m_start_low_time
Definition: ERF_SurfaceLayer.H:1247
amrex::Real rico_qsat_z0
Definition: ERF_SurfaceLayer.H:1277
bool m_update_k_rans
Definition: ERF_SurfaceLayer.H:1340
amrex::Real surf_moist_flux
Definition: ERF_SurfaceLayer.H:1264
RoughCalcType rough_type_sea
Definition: ERF_SurfaceLayer.H:1240
amrex::Real surf_moist
Definition: ERF_SurfaceLayer.H:1263
bool m_ignore_sst
Definition: ERF_SurfaceLayer.H:1302
double m_low_time_interval
Definition: ERF_SurfaceLayer.H:1249
amrex::Real custom_qstar
Definition: ERF_SurfaceLayer.H:1267
amrex::Real custom_rhosurf
Definition: ERF_SurfaceLayer.H:1268
@ MOENG
Moeng functional form.
@ BULK_COEFF
Bulk transfer coefficient functional form.
@ CUSTOM
Custom constant flux functional form.
@ ROTATE
Terrain rotation flux functional form.
@ DONELAN
Donelan functional form.
@ SURFACE_MOISTURE
Surface Qv specified.
@ MOISTURE_FLUX
Qv-flux specified.
amrex::Real depth
Definition: ERF_SurfaceLayer.H:1272
amrex::Real default_land_surf_moist
Definition: ERF_SurfaceLayer.H:1262
amrex::Real rico_theta_z0
Definition: ERF_SurfaceLayer.H:1276
amrex::Real surf_temp_flux
Definition: ERF_SurfaceLayer.H:1261
amrex::Vector< amrex::Geometry > m_geom
Definition: ERF_SurfaceLayer.H:1245
amrex::Real theta_ref
Definition: ERF_SurfaceLayer.H:1342
amrex::Real custom_tstar
Definition: ERF_SurfaceLayer.H:1266
bool cnk_visc
Definition: ERF_SurfaceLayer.H:1271
amrex::Real surf_heating_rate
Definition: ERF_SurfaceLayer.H:1260
FluxCalcType flux_type
Definition: ERF_SurfaceLayer.H:1236
MoistCalcType moist_type
Definition: ERF_SurfaceLayer.H:1238
amrex::Real inv_Cmu2
Definition: ERF_SurfaceLayer.H:1341
amrex::Real custom_ustar
Definition: ERF_SurfaceLayer.H:1265
amrex::Real m_Cd
Definition: ERF_SurfaceLayer.H:1298
amrex::Real default_land_surf_temp
Definition: ERF_SurfaceLayer.H:1253
@ SURFACE_TEMPERATURE
Surface temperature specified.
@ HEAT_FLUX
Heat-flux specified.
TerrainType m_terrain_type
Definition: ERF_SurfaceLayer.H:1305
MOSTAverage m_ma
Definition: ERF_SurfaceLayer.H:1306
real(c_double), parameter epsilon
Definition: ERF_module_model_constants.F90:12
RANSType rans_type
Selected RANS closure.
Definition: ERF_TurbStruct.H:604
amrex::Real theta_ref
Reference potential temperature for stable stratification.
Definition: ERF_TurbStruct.H:593
bool dirichlet_k
Whether TKE uses Dirichlet boundary treatment.
Definition: ERF_TurbStruct.H:606
amrex::Real Cmu0
One-equation RANS Cmu0 coefficient.
Definition: ERF_TurbStruct.H:582

Member Function Documentation

◆ compute_fluxes() [1/2]

template<typename FluxIter >
void SurfaceLayer::compute_fluxes ( const int &  lev,
const int &  max_iters,
amrex::MultiFab &  cons_in,
const FluxIter &  most_flux,
bool  is_land 
)

Compute MOST fluxes with a selected flux-iteration functor.

Parameters
[in]levlevel index
[in]max_itersmaximum MOST iteration count
[in,out]cons_inconserved state used by the flux computation
[in]most_fluxflux-iteration functor
[in]is_landwhether the land-surface branch is active

◆ compute_fluxes() [2/2]

template<typename FluxIter >
void SurfaceLayer::compute_fluxes ( const int &  lev,
const int &  max_iters,
MultiFab &  cons_in,
const FluxIter &  most_flux,
bool  is_land 
)

Function to compute the fluxes (u^star and t^star) for Monin Obukhov similarity theory

Parameters
[in]levCurrent level
[in]max_itersMaximum iterations to use
[in]cons_inConserved state whose grids define the surface iteration
[in]most_fluxFlux-iteration functor used to compute ustar, tstar, qstar, and related fields
[in]is_landSelects whether land or sea cells are updated
363 {
364  // Pointers to the computed averages
365  const auto *const tm_ptr = m_ma.get_average(lev,2); // potential temperature
366  const auto *const qvm_ptr = m_ma.get_average(lev,3); // water vapor mixing ratio
367  const auto *const tvm_ptr = m_ma.get_average(lev,4); // virtual potential temperature
368  const auto *const umm_ptr = m_ma.get_average(lev,5); // horizontal velocity magnitude
369  const auto *const zref_ptr = m_ma.get_zref(lev); // reference height
370  const bool l_use_eb = (m_terrain_type == TerrainType::EB);
371 
372  const int klo = m_geom[lev].Domain().smallEnd(2);
373  IntVect ng = u_star[lev]->nGrowVect(); ng[2] = 0;
374 
375  for (MFIter mfi(cons_in); mfi.isValid(); ++mfi)
376  {
377  Box gtbx = mfi.tilebox(IntVect(0),ng);
378 
379  if (!l_use_eb && gtbx.smallEnd(2) != klo) { continue; }
380 
381  if (!l_use_eb) { gtbx.makeSlab(2,klo); }
382 
383  auto u_star_arr = u_star[lev]->array(mfi);
384  auto t_star_arr = t_star[lev]->array(mfi);
385  auto q_star_arr = q_star[lev]->array(mfi);
386  auto t_surf_arr = t_surf[lev]->array(mfi);
387  auto q_surf_arr = q_surf[lev]->array(mfi);
388  auto olen_arr = olen[lev]->array(mfi);
389 
390  const auto tm_arr = tm_ptr->array(mfi);
391  const auto tvm_arr = tvm_ptr->array(mfi);
392  const auto qvm_arr = qvm_ptr->array(mfi);
393  const auto umm_arr = umm_ptr->array(mfi);
394  const auto zref_arr = zref_ptr->array(mfi);
395  const auto z0_arr = z_0[lev].array(mfi);
396 
397  // PBL height if we need to calculate wstar for the Beljaars correction
398  // TODO: can/should we apply this in LES mode?
399  const auto w_star_arr = (m_include_wstar) ? w_star[lev].get()->array(mfi) : Array4<Real> {};
400  const auto pblh_arr = (m_include_wstar) ? pblh[lev].get()->array(mfi) : Array4<Real> {};
401 
402  // Wave properties if they exist
403  const auto Hwave_arr = (m_Hwave_lev[lev]) ? m_Hwave_lev[lev]->array(mfi) : Array4<Real> {};
404  const auto Lwave_arr = (m_Lwave_lev[lev]) ? m_Lwave_lev[lev]->array(mfi) : Array4<Real> {};
405  const auto eta_arr = (m_eddyDiffs_lev[lev]) ? m_eddyDiffs_lev[lev]->array(mfi) : Array4<Real> {};
406 
407  // Land mask array if it exists
408  auto lmask_arr = (m_lmask_lev[lev][0]) ? m_lmask_lev[lev][0]->array(mfi) :
409  Array4<int> {};
410 
411  // Get EB flags if needed
412  const auto flag_arr = (l_use_eb) ? m_eb_vec[lev]->get_const_factory()->getMultiEBCellFlagFab()[mfi].const_array() : Array4<const EBCellFlag>{};
413 
414  if (!l_use_eb) {
415  ParallelFor(gtbx, [=] AMREX_GPU_DEVICE(int i, int j, int ) noexcept
416  {
417  if (( is_land && lmask_arr(i,j,0) == 1) ||
418  (!is_land && lmask_arr(i,j,0) == 0))
419  {
420  // NOTE: All 2D MFs so k index is always 0 from ba2d definition
421  most_flux.iterate_flux(i, j, 0, max_iters,
422  zref_arr, // set in most average
423  z0_arr, // updated if(!is_land)
424  umm_arr, tm_arr, tvm_arr, qvm_arr,
425  u_star_arr, // updated
426  w_star_arr, // updated if(m_include_wstar)
427  t_star_arr, q_star_arr, // updated
428  t_surf_arr, q_surf_arr, olen_arr, // updated
429  pblh_arr, // updated if(m_include_wstar)
430  Hwave_arr, Lwave_arr, eta_arr);
431  }
432  });
433  // EB
434  } else {
435  if (std::is_same<FluxIter, adiabatic_eb>::value ||
436  std::is_same<FluxIter, surface_temp_eb>::value ||
437  std::is_same<FluxIter, surface_flux_eb>::value) {
438  ParallelFor(gtbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
439  {
440  if (( is_land && lmask_arr(i,j,0) == 1) ||
441  (!is_land && lmask_arr(i,j,0) == 0))
442  {
443  if (flag_arr(i,j,k).isSingleValued()) {
444  most_flux.iterate_flux(i, j, k, max_iters,
445  zref_arr, // set in most average
446  z0_arr, // updated if(!is_land)
447  umm_arr, tm_arr, tvm_arr, qvm_arr,
448  u_star_arr, // updated
449  w_star_arr, // updated if(m_include_wstar)
450  t_star_arr, q_star_arr, // updated
451  t_surf_arr, q_surf_arr, olen_arr, // updated
452  pblh_arr, // updated if(m_include_wstar)
453  Hwave_arr, Lwave_arr, eta_arr);
454  }
455  }
456  });
457  } else {
458  amrex::Abort("FluxIter type not supported for EB");
459  }
460  }
461  }
462 }
pp get("wavelength", wavelength)
ParallelFor(fab_box, [=] AMREX_GPU_DEVICE(int i, int j, int k) { qrcuten_arr(i, j, k)=Real(0);qscuten_arr(i, j, k)=Real(0);qicuten_arr(i, j, k)=Real(0);})
amrex::MultiFab * get_zref(const int &lev) const
Definition: ERF_MOSTAverage.H:313
const amrex::MultiFab * get_average(const int &lev, const int &comp) const
Definition: ERF_MOSTAverage.H:235
amrex::Vector< amrex::Vector< amrex::iMultiFab * > > m_lmask_lev
Definition: ERF_SurfaceLayer.H:1322
amrex::Vector< std::unique_ptr< amrex::MultiFab > > t_surf
Definition: ERF_SurfaceLayer.H:1313
amrex::Vector< std::unique_ptr< amrex::MultiFab > > q_star
Definition: ERF_SurfaceLayer.H:1310
amrex::Vector< amrex::MultiFab * > m_Lwave_lev
Definition: ERF_SurfaceLayer.H:1337
amrex::Vector< amrex::MultiFab > z_0
Definition: ERF_SurfaceLayer.H:1273
amrex::Vector< std::unique_ptr< amrex::MultiFab > > w_star
Definition: ERF_SurfaceLayer.H:1308
amrex::Vector< std::unique_ptr< amrex::MultiFab > > u_star
Definition: ERF_SurfaceLayer.H:1307
amrex::Vector< std::unique_ptr< amrex::MultiFab > > q_surf
Definition: ERF_SurfaceLayer.H:1314
amrex::Vector< amrex::MultiFab * > m_Hwave_lev
Definition: ERF_SurfaceLayer.H:1336
amrex::Vector< std::unique_ptr< amrex::MultiFab > > t_star
Definition: ERF_SurfaceLayer.H:1309
amrex::Vector< amrex::MultiFab * > m_eddyDiffs_lev
Definition: ERF_SurfaceLayer.H:1338
amrex::Vector< std::unique_ptr< amrex::MultiFab > > olen
Definition: ERF_SurfaceLayer.H:1311
amrex::Vector< std::unique_ptr< amrex::MultiFab > > pblh
Definition: ERF_SurfaceLayer.H:1312
@ ng
Definition: ERF_Morrison.H:49
Here is the call graph for this function:

◆ compute_pblh() [1/2]

template<typename PBLHeightEstimator >
void SurfaceLayer::compute_pblh ( const int &  lev,
amrex::Vector< amrex::Vector< amrex::MultiFab >> &  vars,
amrex::MultiFab *  z_phys_cc,
const PBLHeightEstimator &  est,
const MoistureComponentIndices moisture_indice 
)

Compute planetary-boundary-layer height with the selected estimator.

Parameters
[in]levlevel index
[in,out]varsstate variables used by the PBL-height calculation
[in]z_phys_cccell-centered physical-height field
[in]estPBL-height estimator functor
[in]moisture_indiceindices for moisture components

◆ compute_pblh() [2/2]

template<typename PBLHeightEstimator >
void SurfaceLayer::compute_pblh ( const int &  lev,
Vector< Vector< MultiFab >> &  vars,
MultiFab *  z_phys_cc,
const PBLHeightEstimator &  est,
const MoistureComponentIndices moisture_indices 
)

Compute PBL height with the supplied estimator.

Parameters
[in]levCurrent level
[in]varsLevel-indexed state MultiFabs passed to the estimator
[in]z_phys_ccCell-centered physical height used by the estimator
[in]estPBL height estimator functor
[in]moisture_indicesMoisture component indices used by the estimator
1524 {
1525  est.compute_pblh(m_geom[lev],z_phys_cc, pblh[lev].get(),
1526  vars[lev][Vars::cons],m_lmask_lev[lev][0],
1527  moisture_indices);
1528 }
@ cons
Definition: ERF_IndexDefines.H:214
Here is the call graph for this function:

◆ compute_sfc_params_from_lsm_fluxes()

void SurfaceLayer::compute_sfc_params_from_lsm_fluxes ( const int &  lev,
amrex::MultiFab &  cons_in 
)

Derive MOST surface parameters from LSM fluxes.

Parameters
[in]levlevel index
[in,out]cons_inconserved state used by the surface-parameter computation

Compute surface-layer parameters from land-surface-model fluxes.

Parameters
[in]levCurrent level
[in]cons_inConserved state used to derive density, theta, and moisture at the surface
1144 {
1146  bool has_moisture = use_moisture;
1147  const int klo = m_geom[lev].Domain().smallEnd(2);
1148  for (MFIter mfi(cons_in); mfi.isValid(); ++mfi) {
1149 
1150  Box vbx = mfi.validbox();
1151  if (vbx.smallEnd(2) != klo) { continue; }
1152  vbx.makeSlab(2,0);
1153 
1154  // Get CC state
1155  const Array4<const Real> cons_arr = cons_in.const_array(mfi);
1156 
1157  // Get SL params
1158  const auto u_star_arr = u_star[lev]->array(mfi);
1159  const auto t_star_arr = t_star[lev]->array(mfi);
1160  const auto q_star_arr = q_star[lev]->array(mfi);
1161  const auto olen_arr = olen[lev]->array(mfi);
1162 
1163  // Get LSM fluxes
1164  auto lmask_arr = (m_lmask_lev[lev][0]) ? m_lmask_lev[lev][0]->array(mfi) :
1165  Array4<int> {};
1166  auto lsm_t_flux_arr = Array4<Real> {};
1167  auto lsm_q_flux_arr = Array4<Real> {};
1168  auto lsm_tau13_arr = Array4<Real> {};
1169  auto lsm_tau23_arr = Array4<Real> {};
1170  // compute_sfc_params_from_lsm_fluxes consumes signed kinematic stress
1171  // components; their vector magnitude determines u_star^2.
1172  for (int n(0); n<m_lsm_flux_lev[lev].size(); ++n) {
1173  if (toLower(m_lsm_flux_name[n]) == "t_flux") { lsm_t_flux_arr = m_lsm_flux_lev[lev][n]->array(mfi); }
1174  if (toLower(m_lsm_flux_name[n]) == "q_flux") { lsm_q_flux_arr = m_lsm_flux_lev[lev][n]->array(mfi); }
1175  if (toLower(m_lsm_flux_name[n]) == "tau13") { lsm_tau13_arr = m_lsm_flux_lev[lev][n]->array(mfi); }
1176  if (toLower(m_lsm_flux_name[n]) == "tau23") { lsm_tau23_arr = m_lsm_flux_lev[lev][n]->array(mfi); }
1177  }
1178 
1179  ParallelFor(vbx, [=] AMREX_GPU_DEVICE(int i, int j, int /*k*/) noexcept
1180  {
1181  int is_land = (lmask_arr) ? lmask_arr(i,j,0) : 1;
1182  // Skip cells the LSM did not have a valid flux (lsm_undefined).
1183  if (is_land && lsm_t_flux_arr && lsm_t_flux_arr(i,j,0) < lsm_undefined) {
1184  Real rho = cons_arr(i,j,klo,Rho_comp);
1185  Real Thd = cons_arr(i,j,klo,RhoTheta_comp) / rho;
1186  Real qv = (has_moisture) ? cons_arr(i,j,klo,RhoQ1_comp) / rho : zero;
1187  Real Thv = Thd * (one + epsv*qv);
1188  Real tau = std::sqrt( lsm_tau13_arr(i,j,0)*lsm_tau13_arr(i,j,0)
1189  + lsm_tau23_arr(i,j,0)*lsm_tau23_arr(i,j,0) );
1190  u_star_arr(i,j,0) = amrex::max(std::sqrt(tau),eps);
1191  if (lsm_t_flux_arr(i,j,0)>=zero) {
1192  t_star_arr(i,j,0) = amrex::min(-lsm_t_flux_arr(i,j,0) / u_star_arr(i,j,0),-eps);
1193  } else {
1194  t_star_arr(i,j,0) = amrex::max(-lsm_t_flux_arr(i,j,0) / u_star_arr(i,j,0),eps);
1195  }
1196  if (lsm_q_flux_arr(i,j,0)>=zero) {
1197  q_star_arr(i,j,0) = amrex::min(-lsm_q_flux_arr(i,j,0) / u_star_arr(i,j,0),-eps);
1198  } else {
1199  q_star_arr(i,j,0) = amrex::max(-lsm_q_flux_arr(i,j,0) / u_star_arr(i,j,0),eps);
1200  }
1201  olen_arr(i,j,0) = ( u_star_arr(i,j,0) * u_star_arr(i,j,0) * Thv ) /
1202  ( KAPPA * CONST_GRAV * t_star_arr(i,j,0) );
1203  }
1204  });
1205  } // mfi
1206 }
constexpr amrex::Real epsv
Definition: ERF_Constants.H:53
constexpr amrex::Real KAPPA
Definition: ERF_Constants.H:63
constexpr amrex::Real zero
Definition: ERF_Constants.H:8
constexpr amrex::Real CONST_GRAV
Definition: ERF_Constants.H:64
constexpr amrex::Real lsm_undefined
Definition: ERF_Constants.H:35
#define Rho_comp
Definition: ERF_IndexDefines.H:39
#define RhoTheta_comp
Definition: ERF_IndexDefines.H:40
#define RhoQ1_comp
Definition: ERF_IndexDefines.H:45
amrex::Vector< amrex::Vector< amrex::MultiFab * > > m_lsm_flux_lev
Definition: ERF_SurfaceLayer.H:1333
amrex::Vector< std::string > m_lsm_flux_name
Definition: ERF_SurfaceLayer.H:1335
@ rho
Definition: ERF_Kessler.H:24
@ qv
Definition: ERF_Kessler.H:30
Here is the call graph for this function:

◆ compute_SurfaceLayer_bcs() [1/2]

template<typename FluxCalc >
void SurfaceLayer::compute_SurfaceLayer_bcs ( const int &  lev,
amrex::Vector< const amrex::MultiFab * >  mfs,
amrex::Vector< std::unique_ptr< amrex::MultiFab >> &  Tau_lev,
amrex::MultiFab *  xheat_flux,
amrex::MultiFab *  yheat_flux,
amrex::MultiFab *  zheat_flux,
amrex::MultiFab *  xqv_flux,
amrex::MultiFab *  yqv_flux,
amrex::MultiFab *  zqv_flux,
const amrex::MultiFab *  z_phys,
const FluxCalc &  flux_comp 
)

Compute planar-terrain surface-layer flux boundary conditions.

Parameters
[in]levlevel index
[in]mfsstate and velocity fields used by the BC computation
[in,out]Tau_levstress fields to fill
[in,out]xheat_fluxx-face heat flux field
[in,out]yheat_fluxy-face heat flux field
[in,out]zheat_fluxz-face heat flux field
[in,out]xqv_fluxx-face moisture flux field
[in,out]yqv_fluxy-face moisture flux field
[in,out]zqv_fluxz-face moisture flux field
[in]z_physphysical-height field
[in]flux_compflux-computation functor

◆ compute_SurfaceLayer_bcs() [2/2]

template<typename FluxCalc >
void SurfaceLayer::compute_SurfaceLayer_bcs ( const int &  lev,
Vector< const MultiFab * >  mfs,
Vector< std::unique_ptr< MultiFab >> &  Tau_lev,
MultiFab *  xheat_flux,
MultiFab *  yheat_flux,
MultiFab *  zheat_flux,
MultiFab *  xqv_flux,
MultiFab *  yqv_flux,
MultiFab *  zqv_flux,
const MultiFab *  z_phys,
const FluxCalc &  flux_comp 
)

Function to calculate MOST fluxes for populating ghost cells.

Parameters
[in]levCurrent level
[in]mfsState MultiFabs used to compute the boundary fluxes
[in,out]Tau_levDiffusive stress MultiFabs populated with surface stresses
[in,out]xheat_fluxx-face heat-flux MultiFab, used when rotated fluxes are enabled
[in,out]yheat_fluxy-face heat-flux MultiFab, used when rotated fluxes are enabled
[in,out]zheat_fluxz-face heat-flux MultiFab populated with vertical surface heat flux
[in,out]xqv_fluxx-face moisture-flux MultiFab, used when rotated fluxes and moisture are enabled
[in,out]yqv_fluxy-face moisture-flux MultiFab, used when rotated fluxes and moisture are enabled
[in,out]zqv_fluxz-face moisture-flux MultiFab populated when moisture is enabled
[in]z_physNodal physical height used to rotate terrain-following fluxes
[in]flux_compFlux-calculation functor used to compute scalar and momentum fluxes
596 {
597  bool rotate = m_rotate;
598  const int klo = m_geom[lev].Domain().smallEnd(2);
599  const auto& dxInv = m_geom[lev].InvCellSizeArray();
600  for (MFIter mfi(*mfs[0]); mfi.isValid(); ++mfi)
601  {
602  // Get field arrays
603  const auto cons_arr = mfs[Vars::cons]->array(mfi);
604  const auto velx_arr = mfs[Vars::xvel]->array(mfi);
605  const auto vely_arr = mfs[Vars::yvel]->array(mfi);
606  const auto velz_arr = mfs[Vars::zvel]->array(mfi);
607 
608  // Diffusive stress vars
609  auto t13_arr = Tau_lev[TauType::tau13]->array(mfi);
610  auto t31_arr = (Tau_lev[TauType::tau31]) ? Tau_lev[TauType::tau31]->array(mfi) : Array4<Real>{};
611 
612  auto t23_arr = Tau_lev[TauType::tau23]->array(mfi);
613  auto t32_arr = (Tau_lev[TauType::tau32]) ? Tau_lev[TauType::tau32]->array(mfi) : Array4<Real>{};
614 
615  auto hfx3_arr = zheat_flux->array(mfi);
616  auto qfx3_arr = (zqv_flux) ? zqv_flux->array(mfi) : Array4<Real>{};
617 
618  auto olen_arr = olen[lev]->array(mfi);
619 
620  // Rotated stress vars
621  auto t11_arr = (m_rotate) ? Tau_lev[TauType::tau11]->array(mfi) : Array4<Real>{};
622  auto t22_arr = (m_rotate) ? Tau_lev[TauType::tau22]->array(mfi) : Array4<Real>{};
623  auto t33_arr = (m_rotate) ? Tau_lev[TauType::tau33]->array(mfi) : Array4<Real>{};
624  auto t12_arr = (m_rotate) ? Tau_lev[TauType::tau12]->array(mfi) : Array4<Real>{};
625  auto t21_arr = (m_rotate) ? Tau_lev[TauType::tau21]->array(mfi) : Array4<Real>{};
626 
627  auto hfx1_arr = (m_rotate) ? xheat_flux->array(mfi) : Array4<Real>{};
628  auto hfx2_arr = (m_rotate) ? yheat_flux->array(mfi) : Array4<Real>{};
629  auto qfx1_arr = (m_rotate && xqv_flux) ? xqv_flux->array(mfi) : Array4<Real>{};
630  auto qfx2_arr = (m_rotate && yqv_flux) ? yqv_flux->array(mfi) : Array4<Real>{};
631 
632  // Terrain
633  const auto zphys_arr = (z_phys) ? z_phys->const_array(mfi) : Array4<const Real>{};
634 
635  // Get average arrays
636  const auto *const u_mean = m_ma.get_average(lev,0);
637  const auto *const v_mean = m_ma.get_average(lev,1);
638  const auto *const t_mean = m_ma.get_average(lev,2);
639  const auto *const q_mean = m_ma.get_average(lev,3);
640  const auto *const u_mag_mean = m_ma.get_average(lev,5);
641 
642  const auto um_arr = u_mean->array(mfi);
643  const auto vm_arr = v_mean->array(mfi);
644  const auto tm_arr = t_mean->array(mfi);
645  const auto qm_arr = q_mean->array(mfi);
646  const auto umm_arr = u_mag_mean->array(mfi);
647 
648  // Get derived arrays
649  const auto u_star_arr = u_star[lev]->array(mfi);
650  const auto t_star_arr = t_star[lev]->array(mfi);
651  const auto q_star_arr = q_star[lev]->array(mfi);
652  const auto t_surf_arr = t_surf[lev]->array(mfi);
653  const auto q_surf_arr = q_surf[lev]->array(mfi);
654  auto surface_source_arr = surface_diagnostic_source[lev]->array(mfi);
655 
656  // Get LSM fluxes
657  auto lmask_arr = (m_lmask_lev[lev][0]) ? m_lmask_lev[lev][0]->array(mfi) :
658  Array4<int> {};
659  auto lsm_t_flux_arr = Array4<Real> {};
660  auto soil_t_flux_arr = Array4<Real> {};
661  auto lsm_q_flux_arr = Array4<Real> {};
662  auto lsm_tau13_arr = Array4<Real> {};
663  auto lsm_tau23_arr = Array4<Real> {};
664  // LSM tau fields are cell-centered kinematic stresses [m2 s-2].
665  // Tau_lev tau13/tau23 are face-centered conservative stresses [N m-2].
666  for (int n(0); n<m_lsm_flux_lev[lev].size(); ++n) {
667  if (toLower(m_lsm_flux_name[n]) == "t_flux") { lsm_t_flux_arr = m_lsm_flux_lev[lev][n]->array(mfi); }
668  if (toLower(m_lsm_flux_name[n]) == "soil_t_flux") { soil_t_flux_arr = m_lsm_flux_lev[lev][n]->array(mfi); }
669  if (toLower(m_lsm_flux_name[n]) == "q_flux") { lsm_q_flux_arr = m_lsm_flux_lev[lev][n]->array(mfi); }
670  if (toLower(m_lsm_flux_name[n]) == "tau13") { lsm_tau13_arr = m_lsm_flux_lev[lev][n]->array(mfi); }
671  if (toLower(m_lsm_flux_name[n]) == "tau23") { lsm_tau23_arr = m_lsm_flux_lev[lev][n]->array(mfi); }
672  }
673 
674  const bool has_lsm_t_flux = static_cast<bool>(lsm_t_flux_arr);
675  const bool is_custom = (flux_type == FluxCalcType::CUSTOM);
676  const bool is_rico = (flux_type == FluxCalcType::RICO);
677 
678 
679  // Rho*Theta flux
680  //============================================================================
681  Box bx = mfi.tilebox();
682  if (bx.smallEnd(2) != klo) { continue; }
683  bx.makeSlab(2,klo);
684  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k)
685  {
686  // Valid theta flux from LSM and over land. The LSM writes the
687  // lsm_undefined sentinel for cells it did not process (sea-ice /
688  // open water); fall back to MOST there instead of applying garbage.
689  Real Tflux;
690  int is_land = (lmask_arr) ? lmask_arr(i,j,0) : 1;
691  const bool lsm_flux_is_valid = (lsm_t_flux_arr) ? (lsm_t_flux_arr(i,j,0) < lsm_undefined) :
692  false;
693  const bool has_land_and_flux = (is_land == 1 && lsm_flux_is_valid);
694  if (lsm_t_flux_arr && has_land_and_flux) {
695  // LSM flux MultiFabs store kinematic fluxes for MOST parameter
696  // updates. The applied hfx array stores the conservative RHS flux.
697  Tflux = cons_arr(i,j,k,Rho_comp) * lsm_t_flux_arr(i,j,0);
698  } else if (is_land == 2) { // no temperature flux within buildings
699  Tflux = zero;
700  } else {
701  Tflux = flux_comp.compute_t_flux(i, j, k,
702  cons_arr, velx_arr, vely_arr,
703  umm_arr, tm_arr, u_star_arr,
704  t_star_arr, t_surf_arr);
705  // NOTE: do NOT write the MOST-fallback flux back into lsm_t_flux_arr.
706  // Doing so flips a sentinel (water/unprocessed) cell to "valid LSM"
707  // on the next step, so a MOST-derived value is re-read as an LSM flux
708  // Only Noah-MP should populate the LSM cache.
709  }
710 
711  if (soil_t_flux_arr && is_land == 1) {
712  soil_t_flux_arr(i,j,k) = Tflux / cons_arr(i,j,k,Rho_comp);
713  }
714 
715  surface_source_arr(i,j,0) = surface_diagnostics::to_plot_value(
717  is_custom, is_rico, is_land, has_lsm_t_flux, lsm_flux_is_valid));
718 
719  // Do scalar flux rotations?
720  if (rotate) {
721  rotate_scalar_flux(i, j, k, Tflux, dxInv, zphys_arr,
722  hfx1_arr, hfx2_arr, hfx3_arr);
723  } else {
724  hfx3_arr(i,j,klo) = Tflux;
725  }
726  });
727 
728  // Rho*Qv flux
729  //============================================================================
730  if (use_moisture) {
731  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k)
732  {
733  // Valid qv flux from LSM and over land (sentinel -> fall back to MOST)
734  Real Qflux;
735  int is_land = (lmask_arr) ? lmask_arr(i,j,0) : 1;
736  const bool lsm_flux_is_valid = (lsm_q_flux_arr) ? (lsm_q_flux_arr(i,j,0) < lsm_undefined) :
737  false;
738  const bool has_land_and_flux = (is_land == 1 && lsm_flux_is_valid);
739  if (lsm_q_flux_arr && has_land_and_flux) {
740  // LSM flux MultiFabs store kinematic fluxes for MOST parameter
741  // updates. The applied qfx array stores the conservative RHS flux.
742  Qflux = cons_arr(i,j,k,Rho_comp) * lsm_q_flux_arr(i,j,0);
743  } else if (is_land == 2) { // no moisture flux within buildings
744  Qflux = zero;
745  } else {
746  Qflux = flux_comp.compute_q_flux(i, j, k,
747  cons_arr, velx_arr, vely_arr,
748  umm_arr, qm_arr, u_star_arr,
749  q_star_arr, q_surf_arr);
750  // NOTE: no writeback into lsm_q_flux_arr -- see the matching
751  // t_flux note above.
752  }
753 
754  // Do scalar flux rotations?
755  if (rotate) {
756  rotate_scalar_flux(i, j, k, Qflux, dxInv, zphys_arr,
757  qfx1_arr, qfx2_arr, qfx3_arr);
758  } else {
759  qfx3_arr(i,j,k) = Qflux;
760  }
761  });
762  } // custom
763 
764  if (!rotate) {
765  // Rho*u flux
766  //============================================================================
767  Box bxx = surroundingNodes(bx,0);
768  ParallelFor(bxx, [=] AMREX_GPU_DEVICE (int i, int j, int k)
769  {
770  // Valid tau13 from LSM and over land. A side that is land but
771  // whose LSM flux is the sentinel (sea-ice / open water) is treated
772  // as non-LSM so that side uses the MOST stress instead.
773  Real stressx;
774  int is_land_hi = (lmask_arr) ? lmask_arr(i ,j,0) : 1;
775  int is_land_lo = (lmask_arr) ? lmask_arr(i-1,j,0) : 1;
776  const bool lsm_hi_flux_is_valid = surface_layer_stress::lsm_flux_is_valid(
777  static_cast<bool>(lsm_tau13_arr), is_land_hi == 1,
778  lsm_tau13_arr ? lsm_tau13_arr(i ,j,0) : zero, lsm_undefined);
779  const bool lsm_lo_flux_is_valid = surface_layer_stress::lsm_flux_is_valid(
780  static_cast<bool>(lsm_tau13_arr), is_land_lo == 1,
781  lsm_tau13_arr ? lsm_tau13_arr(i-1,j,0) : zero, lsm_undefined);
782  const bool has_land_and_flux_hi = (is_land_hi == 1 && lsm_hi_flux_is_valid);
783  const bool has_land_and_flux_lo = (is_land_lo == 1 && lsm_lo_flux_is_valid);
784  if (lsm_tau13_arr && (has_land_and_flux_hi || has_land_and_flux_lo)) {
785  const Real rho_hi = cons_arr(i ,j,k,Rho_comp);
786  const Real rho_lo = cons_arr(i-1,j,k,Rho_comp);
787  const Real most_stress = (!has_land_and_flux_hi || !has_land_and_flux_lo) ?
788  flux_comp.compute_u_flux(i, j, k,
789  cons_arr, velx_arr, vely_arr,
790  umm_arr, um_arr, u_star_arr) : zero;
792  rho_lo, rho_hi, lsm_tau13_arr(i-1,j,0), lsm_tau13_arr(i,j,0),
793  has_land_and_flux_lo, has_land_and_flux_hi, most_stress);
794  stressx = result.face_stress;
795  // NOTE: do NOT write the MOST-fallback stress back into the
796  // cell-centered lsm_tau13_arr. This face-indexed ParallelFor
797  // touches cells (i) and (i-1), so each cell is written by two
798  // adjacent face threads in the same launch -> nondeterministic
799  // write-write race on GPU (ERF #3446). It also spuriously flips
800  // a sentinel (water/unprocessed) cell to "valid LSM" for the
801  // next step. The face stress is fully determined here; the LSM
802  // cache is (re)filled only by Noah-MP. Matches baseline 3ab899d3.
803  } else if (is_land_hi == 2 || is_land_lo == 2) { // no stress within buildings
804  stressx = zero;
805  } else {
806  stressx = flux_comp.compute_u_flux(i, j, k,
807  cons_arr, velx_arr, vely_arr,
808  umm_arr, um_arr, u_star_arr);
809  }
810 
811  t13_arr(i,j,k) = stressx;
812  if (t31_arr) { t31_arr(i,j,k) = stressx; }
813  });
814 
815  // Rho*v flux
816  //============================================================================
817  Box bxy = surroundingNodes(bx,1);
818  ParallelFor(bxy, [=] AMREX_GPU_DEVICE (int i, int j, int k)
819  {
820  // Valid tau23 from LSM and over land (sentinel side -> MOST stress)
821  Real stressy;
822  int is_land_hi = (lmask_arr) ? lmask_arr(i,j ,0) : 1;
823  int is_land_lo = (lmask_arr) ? lmask_arr(i,j-1,0) : 1;
824  const bool lsm_hi_flux_is_valid = surface_layer_stress::lsm_flux_is_valid(
825  static_cast<bool>(lsm_tau23_arr), is_land_hi == 1,
826  lsm_tau23_arr ? lsm_tau23_arr(i,j ,0) : zero, lsm_undefined);
827  const bool lsm_lo_flux_is_valid = surface_layer_stress::lsm_flux_is_valid(
828  static_cast<bool>(lsm_tau23_arr), is_land_lo == 1,
829  lsm_tau23_arr ? lsm_tau23_arr(i,j-1,0) : zero, lsm_undefined);
830  const bool has_land_and_flux_hi = (is_land_hi == 1 && lsm_hi_flux_is_valid);
831  const bool has_land_and_flux_lo = (is_land_lo == 1 && lsm_lo_flux_is_valid);
832  if (lsm_tau23_arr && (has_land_and_flux_hi || has_land_and_flux_lo)) {
833  const Real rho_hi = cons_arr(i,j ,k,Rho_comp);
834  const Real rho_lo = cons_arr(i,j-1,k,Rho_comp);
835  const Real most_stress = (!has_land_and_flux_hi || !has_land_and_flux_lo) ?
836  flux_comp.compute_v_flux(i, j, k,
837  cons_arr, velx_arr, vely_arr,
838  umm_arr, vm_arr, u_star_arr) : zero;
840  rho_lo, rho_hi, lsm_tau23_arr(i,j-1,0), lsm_tau23_arr(i,j,0),
841  has_land_and_flux_lo, has_land_and_flux_hi, most_stress);
842  stressy = result.face_stress;
843  // NOTE: no writeback into cell-centered lsm_tau23_arr -- see the
844  // matching tau13 note above (ERF #3446 write-write race + stale
845  // sentinel-becomes-valid). Face stress is complete here.
846  } else if (is_land_hi == 2 || is_land_lo == 2) { // no stress within buildings
847  stressy = zero;
848  } else {
849  stressy = flux_comp.compute_v_flux(i, j, k,
850  cons_arr, velx_arr, vely_arr,
851  umm_arr, vm_arr, u_star_arr);
852  }
853 
854  t23_arr(i,j,k) = stressy;
855  if (t32_arr) { t32_arr(i,j,k) = stressy; }
856  });
857  } else {
858  // All fluxes with rotation
859  //============================================================================
860  Box bxxy = convert(bx, IntVect(1,1,0));
861  ParallelFor(bxxy, [=] AMREX_GPU_DEVICE (int i, int j, int k)
862  {
863  Real stresst = flux_comp.compute_u_flux(i, j, k,
864  cons_arr, velx_arr, vely_arr,
865  umm_arr, um_arr, u_star_arr);
866  rotate_stress_tensor(i, j, k, stresst, dxInv, zphys_arr,
867  velx_arr, vely_arr, velz_arr,
868  t11_arr, t22_arr, t33_arr,
869  t12_arr, t21_arr,
870  t13_arr, t31_arr,
871  t23_arr, t32_arr);
872  });
873  }
874 
875  // For models that do not do iterations to yield u*/T*/q*,
876  // fill these values from the fluxes that were computed.
877 
878  // NOTE: For LSM, this has been handled in "compute_sfc_params_from_lsm_fluxes"
879  // NOTE: Fluxes here are for conserved quantities, we divide by rho
882  constexpr Real eps = std::numeric_limits<Real>::epsilon();
883  bool l_use_moisture = use_moisture;
884  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int /*k*/)
885  {
886  Real rho = cons_arr(i,j,klo,Rho_comp);
887  Real Thd = cons_arr(i,j,klo,RhoTheta_comp) / rho;
888  Real qv = (l_use_moisture) ? cons_arr(i,j,klo,RhoQ1_comp) / rho : zero;
889  Real Thv = Thd * (one + epsv*qv);
890 
891  Real tau = std::sqrt( t13_arr(i,j,klo)/rho * t13_arr(i,j,klo)/rho
892  + t23_arr(i,j,klo)/rho * t23_arr(i,j,klo)/rho );
893  u_star_arr(i,j,0) = amrex::max(std::sqrt(tau),eps);
894 
895  if (hfx3_arr(i,j,klo)>=zero) {
896  t_star_arr(i,j,0) = amrex::min(-hfx3_arr(i,j,klo) / (rho * u_star_arr(i,j,0)),-eps);
897  } else {
898  t_star_arr(i,j,0) = amrex::max(-hfx3_arr(i,j,klo) / (rho * u_star_arr(i,j,0)),eps);
899  }
900  if (!l_use_moisture) {
901  q_star_arr(i,j,0) = zero;
902  } else if (qfx3_arr(i,j,klo)>=zero) {
903  q_star_arr(i,j,0) = amrex::min(-qfx3_arr(i,j,klo) / (rho * u_star_arr(i,j,0)),-eps);
904  } else {
905  q_star_arr(i,j,0) = amrex::max(-qfx3_arr(i,j,klo) / ( rho * u_star_arr(i,j,0)),eps);
906  }
907  olen_arr(i,j,0) = ( u_star_arr(i,j,0) * u_star_arr(i,j,0) * Thv ) /
908  ( KAPPA * CONST_GRAV * t_star_arr(i,j,0) );
909  });
910  }
911 
912  } // mfiter
913 
914  surface_diagnostic_source[lev]->FillBoundary(m_geom[lev].periodicity());
915 }
@ tau12
Definition: ERF_DataStruct.H:39
@ tau23
Definition: ERF_DataStruct.H:39
@ tau33
Definition: ERF_DataStruct.H:39
@ tau22
Definition: ERF_DataStruct.H:39
@ tau11
Definition: ERF_DataStruct.H:39
@ tau32
Definition: ERF_DataStruct.H:39
@ tau31
Definition: ERF_DataStruct.H:39
@ tau21
Definition: ERF_DataStruct.H:39
@ tau13
Definition: ERF_DataStruct.H:39
amrex::GpuArray< Real, AMREX_SPACEDIM > dxInv
Definition: ERF_InitCustomPertVels_ParticleTests.H:17
AMREX_GPU_DEVICE AMREX_FORCE_INLINE void rotate_scalar_flux(const int &i, const int &j, const int &klo, const amrex::Real &flux, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &dxInv, const amrex::Array4< const amrex::Real > &zphys_arr, const amrex::Array4< amrex::Real > &phi1_arr, const amrex::Array4< amrex::Real > &phi2_arr, const amrex::Array4< amrex::Real > &phi3_arr)
Definition: ERF_TerrainMetrics.H:1035
AMREX_GPU_DEVICE AMREX_FORCE_INLINE void rotate_stress_tensor(const int &i, const int &j, const int &klo, const amrex::Real &flux, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &dxInv, const amrex::Array4< const amrex::Real > &zphys_arr, const amrex::Array4< const amrex::Real > &u_arr, const amrex::Array4< const amrex::Real > &v_arr, const amrex::Array4< const amrex::Real > &w_arr, const amrex::Array4< amrex::Real > &tau11_arr, const amrex::Array4< amrex::Real > &tau22_arr, const amrex::Array4< amrex::Real > &tau33_arr, const amrex::Array4< amrex::Real > &tau12_arr, const amrex::Array4< amrex::Real > &tau21_arr, const amrex::Array4< amrex::Real > &tau13_arr, const amrex::Array4< amrex::Real > &tau31_arr, const amrex::Array4< amrex::Real > &tau23_arr, const amrex::Array4< amrex::Real > &tau32_arr)
Definition: ERF_TerrainMetrics.H:1078
amrex::Vector< std::unique_ptr< amrex::MultiFab > > surface_diagnostic_source
Definition: ERF_SurfaceLayer.H:1318
@ xvel
Definition: ERF_IndexDefines.H:215
@ zvel
Definition: ERF_IndexDefines.H:217
@ yvel
Definition: ERF_IndexDefines.H:216
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real to_plot_value(SurfaceDiagnosticSource source) noexcept
Definition: ERF_SurfaceDiagnosticSource.H:45
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE SurfaceDiagnosticSource classify_scalar_source(bool is_custom, bool is_rico, bool is_land, bool has_lsm_flux, bool lsm_flux_is_valid) noexcept
Definition: ERF_SurfaceDiagnosticSource.H:61
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE bool lsm_flux_is_valid(bool has_flux, bool is_land, amrex::Real flux, amrex::Real undefined)
Definition: ERF_SurfaceLayerStress.H:35
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE FaceStressResult combine_lsm_and_most_stress(amrex::Real rho_low, amrex::Real rho_high, amrex::Real kinematic_low, amrex::Real kinematic_high, bool low_valid, bool high_valid, amrex::Real most_face_stress)
Definition: ERF_SurfaceLayerStress.H:64
Here is the call graph for this function:

◆ compute_SurfaceLayer_bcs_EB() [1/2]

template<typename FluxCalc >
void SurfaceLayer::compute_SurfaceLayer_bcs_EB ( const int &  lev,
amrex::Vector< const amrex::MultiFab * >  mfs,
amrex::Vector< amrex::Vector< std::unique_ptr< amrex::MultiFab >>> &  Tau_lev,
amrex::MultiFab *  xheat_flux,
amrex::MultiFab *  yheat_flux,
amrex::MultiFab *  zheat_flux,
amrex::MultiFab *  xqv_flux,
amrex::MultiFab *  yqv_flux,
amrex::MultiFab *  zqv_flux,
const FluxCalc &  flux_comp 
)

Compute embedded-boundary surface-layer flux boundary conditions.

Parameters
[in]levlevel index
[in]mfsstate and velocity fields used by the BC computation
[in,out]Tau_levEB stress fields to fill
[in,out]xheat_fluxx-face heat flux field
[in,out]yheat_fluxy-face heat flux field
[in,out]zheat_fluxz-face heat flux field
[in,out]xqv_fluxx-face moisture flux field
[in,out]yqv_fluxy-face moisture flux field
[in,out]zqv_fluxz-face moisture flux field
[in]flux_compflux-computation functor

◆ compute_SurfaceLayer_bcs_EB() [2/2]

template<typename FluxCalc >
void SurfaceLayer::compute_SurfaceLayer_bcs_EB ( const int &  lev,
Vector< const MultiFab * >  mfs,
Vector< Vector< std::unique_ptr< MultiFab >>> &  Tau_EB,
[[maybe_unused] ] MultiFab *  xheat_flux,
[[maybe_unused] ] MultiFab *  yheat_flux,
MultiFab *  Hfx3_EB,
[[maybe_unused] ] MultiFab *  xqv_flux,
[[maybe_unused] ] MultiFab *  yqv_flux,
[[maybe_unused] ] MultiFab *  zqv_flux,
const FluxCalc &  flux_comp 
)

Function to calculate MOST fluxes for EB.

Parameters
[in]levCurrent level
[in]mfsState MultiFabs used to compute the EB boundary fluxes
[in,out]Tau_EBEB diffusive stress MultiFabs populated with surface stresses
[in,out]xheat_fluxx-face EB heat-flux MultiFab, currently unused
[in,out]yheat_fluxy-face EB heat-flux MultiFab, currently unused
[in,out]Hfx3_EBEB heat-flux MultiFab populated with scalar surface flux
[in,out]xqv_fluxx-face EB moisture-flux MultiFab, currently unused
[in,out]yqv_fluxy-face EB moisture-flux MultiFab, currently unused
[in,out]zqv_fluxz-face EB moisture-flux MultiFab, currently unused
[in]flux_compEB flux-calculation functor used to compute scalar and momentum fluxes
943 {
944  // Get EB flags for all centerings
945  const auto& cc_factory = m_eb_vec[lev]->get_const_factory();
946  const auto& cc_flags = cc_factory->getMultiEBCellFlagFab();
947  const auto& cc_vfrac = cc_factory->getVolFrac();
948 
949  const auto& u_factory = m_eb_vec[lev]->get_u_const_factory();
950  const auto& u_flags = u_factory->getMultiEBCellFlagFab();
951  const auto& u_vfrac = u_factory->getVolFrac();
952 
953  const auto& v_factory = m_eb_vec[lev]->get_v_const_factory();
954  const auto& v_flags = v_factory->getMultiEBCellFlagFab();
955  const auto& v_vfrac = v_factory->getVolFrac();
956 
957  const auto& w_factory = m_eb_vec[lev]->get_w_const_factory();
958  const auto& w_flags = w_factory->getMultiEBCellFlagFab();
959  const auto& w_vfrac = w_factory->getVolFrac();
960 
961  // EB does not currently have a cell-centered scalar-source classification.
962  // Keep the provenance mask missing rather than inventing face-aware
963  // semantics for the staggered stress path.
964  surface_diagnostic_source[lev]->setVal(
966 
967  for (MFIter mfi(*mfs[0]); mfi.isValid(); ++mfi)
968  {
969  // Get flags for this box (all centerings)
970  const auto& cc_flag = cc_flags[mfi];
971  const auto& u_flag = u_flags[mfi];
972  const auto& v_flag = v_flags[mfi];
973  const auto& w_flag = w_flags[mfi];
974 
975  // Skip boxes that have no cut cells at any centering
976  if (cc_flag.getType() != FabType::singlevalued &&
977  u_flag.getType() != FabType::singlevalued &&
978  v_flag.getType() != FabType::singlevalued &&
979  w_flag.getType() != FabType::singlevalued
980  ) continue;
981 
982  // Get EB flag and volfrac arrays
983  auto const cc_flag_arr = cc_flag.const_array();
984  auto const u_flag_arr = u_flag.const_array();
985  auto const v_flag_arr = v_flag.const_array();
986  auto const w_flag_arr = w_flag.const_array();
987 
988  auto const cc_vfrac_arr = cc_vfrac.const_array(mfi);
989  auto const u_vfrac_arr = u_vfrac.const_array(mfi);
990  auto const v_vfrac_arr = v_vfrac.const_array(mfi);
991  auto const w_vfrac_arr = w_vfrac.const_array(mfi);
992 
993  // Get boundary normals only if cut cells exist
994  auto const bnorm_arr = (cc_flag.getType() == FabType::singlevalued) ?
995  cc_factory->getBndryNormal().const_array(mfi) : Array4<const Real>{};
996  auto const u_bnorm_arr = (u_flag.getType() == FabType::singlevalued) ?
997  u_factory->getBndryNormal().const_array(mfi) : Array4<const Real>{};
998  auto const v_bnorm_arr = (v_flag.getType() == FabType::singlevalued) ?
999  v_factory->getBndryNormal().const_array(mfi) : Array4<const Real>{};
1000  auto const w_bnorm_arr = (w_flag.getType() == FabType::singlevalued) ?
1001  w_factory->getBndryNormal().const_array(mfi) : Array4<const Real>{};
1002 
1003  // Get field arrays
1004  const auto cons_arr = mfs[Vars::cons]->array(mfi);
1005  const auto velx_arr = mfs[Vars::xvel]->array(mfi);
1006  const auto vely_arr = mfs[Vars::yvel]->array(mfi);
1007  const auto velz_arr = mfs[Vars::zvel]->array(mfi);
1008 
1009  // Diffusive stress vars - t13 and t23 components for all grid types
1010  auto u_t13_arr = Tau_EB[EBTauType::tau_eb13][EBGridType::xface]->array(mfi);
1011  auto v_t13_arr = Tau_EB[EBTauType::tau_eb13][EBGridType::yface]->array(mfi);
1012  auto w_t13_arr = Tau_EB[EBTauType::tau_eb13][EBGridType::zface]->array(mfi);
1013 
1014  auto u_t23_arr = Tau_EB[EBTauType::tau_eb23][EBGridType::xface]->array(mfi);
1015  auto v_t23_arr = Tau_EB[EBTauType::tau_eb23][EBGridType::yface]->array(mfi);
1016  auto w_t23_arr = Tau_EB[EBTauType::tau_eb23][EBGridType::zface]->array(mfi);
1017 
1018  auto hfx3_arr = Hfx3_EB->array(mfi);
1019 
1020  // Get average arrays
1021  const auto *const u_mean = m_ma.get_average(lev,0);
1022  const auto *const v_mean = m_ma.get_average(lev,1);
1023  const auto *const t_mean = m_ma.get_average(lev,2);
1024  // const auto *const q_mean = m_ma.get_average(lev,3);
1025  const auto *const u_mag_mean = m_ma.get_average(lev,5);
1026 
1027  const auto um_arr = u_mean->array(mfi);
1028  const auto vm_arr = v_mean->array(mfi);
1029  const auto tm_arr = t_mean->array(mfi);
1030  // const auto qm_arr = q_mean->array(mfi);
1031  const auto umm_arr = u_mag_mean->array(mfi);
1032 
1033  // Get derived arrays
1034  const auto u_star_arr = u_star[lev]->array(mfi);
1035  const auto t_star_arr = t_star[lev]->array(mfi);
1036  const auto t_surf_arr = t_surf[lev]->array(mfi);
1037 
1038  // Rho*Theta flux
1039  //============================================================================
1040  Box bx = mfi.tilebox();
1041  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k)
1042  {
1043  if (cc_flag_arr(i,j,k).isSingleValued()) {
1044  Real Tflux = flux_comp.compute_t_flux(i, j, k,
1045  cons_arr, velx_arr, vely_arr, velz_arr,
1046  umm_arr, tm_arr, u_star_arr,
1047  t_star_arr, t_surf_arr,
1048  u_vfrac_arr, v_vfrac_arr, w_vfrac_arr,
1049  bnorm_arr);
1050  hfx3_arr(i,j,k) = Tflux;
1051  }
1052  });
1053 
1054  // Rho*u flux
1055  //============================================================================
1056  Box bxx = surroundingNodes(bx,0);
1057  Box bxy = surroundingNodes(bx,1);
1058  Box bxz = surroundingNodes(bx,2);
1059  ParallelFor(bxx, [=] AMREX_GPU_DEVICE (int i, int j, int k)
1060  {
1061  if (u_flag_arr(i,j,k).isSingleValued()) {
1062  Real stressx = flux_comp.compute_u_flux(i, j, k,
1063  cons_arr, velx_arr, vely_arr, velz_arr,
1064  umm_arr, um_arr, u_star_arr,
1065  u_vfrac_arr, v_vfrac_arr, w_vfrac_arr,
1066  cc_vfrac_arr, cc_flag_arr,
1067  u_bnorm_arr, 0);
1068  u_t13_arr(i,j,k) = stressx;
1069  }
1070  });
1071  ParallelFor(bxy, [=] AMREX_GPU_DEVICE (int i, int j, int k)
1072  {
1073  if (v_flag_arr(i,j,k).isSingleValued()) {
1074  Real stressx = flux_comp.compute_u_flux(i, j, k,
1075  cons_arr, velx_arr, vely_arr, velz_arr,
1076  umm_arr, um_arr, u_star_arr,
1077  u_vfrac_arr, v_vfrac_arr, w_vfrac_arr,
1078  cc_vfrac_arr, cc_flag_arr,
1079  v_bnorm_arr, 1);
1080  v_t13_arr(i,j,k) = stressx;
1081  }
1082  });
1083  ParallelFor(bxz, [=] AMREX_GPU_DEVICE (int i, int j, int k)
1084  {
1085  if (w_flag_arr(i,j,k).isSingleValued()) {
1086  Real stressx = flux_comp.compute_u_flux(i, j, k,
1087  cons_arr, velx_arr, vely_arr, velz_arr,
1088  umm_arr, um_arr, u_star_arr,
1089  u_vfrac_arr, v_vfrac_arr, w_vfrac_arr,
1090  cc_vfrac_arr, cc_flag_arr,
1091  w_bnorm_arr, 2);
1092  w_t13_arr(i,j,k) = stressx;
1093  }
1094  });
1095 
1096  // Rho*v flux
1097  //============================================================================
1098  ParallelFor(bxx, [=] AMREX_GPU_DEVICE (int i, int j, int k)
1099  {
1100  if (u_flag_arr(i,j,k).isSingleValued()) {
1101  Real stressy = flux_comp.compute_v_flux(i, j, k,
1102  cons_arr, velx_arr, vely_arr, velz_arr,
1103  umm_arr, vm_arr, u_star_arr,
1104  u_vfrac_arr, v_vfrac_arr, w_vfrac_arr,
1105  cc_vfrac_arr, cc_flag_arr, u_bnorm_arr, 0);
1106  u_t23_arr(i,j,k) = stressy;
1107  }
1108  });
1109  ParallelFor(bxy, [=] AMREX_GPU_DEVICE (int i, int j, int k)
1110  {
1111  if (v_flag_arr(i,j,k).isSingleValued()) {
1112  Real stressy = flux_comp.compute_v_flux(i, j, k,
1113  cons_arr, velx_arr, vely_arr, velz_arr,
1114  umm_arr, vm_arr, u_star_arr,
1115  u_vfrac_arr, v_vfrac_arr, w_vfrac_arr,
1116  cc_vfrac_arr, cc_flag_arr, v_bnorm_arr, 1);
1117  v_t23_arr(i,j,k) = stressy;
1118  }
1119  });
1120  ParallelFor(bxz, [=] AMREX_GPU_DEVICE (int i, int j, int k)
1121  {
1122  if (w_flag_arr(i,j,k).isSingleValued()) {
1123  Real stressy = flux_comp.compute_v_flux(i, j, k,
1124  cons_arr, velx_arr, vely_arr, velz_arr,
1125  umm_arr, vm_arr, u_star_arr,
1126  u_vfrac_arr, v_vfrac_arr, w_vfrac_arr,
1127  cc_vfrac_arr, cc_flag_arr, w_bnorm_arr, 2);
1128  w_t23_arr(i,j,k) = stressy;
1129  }
1130  });
1131  } // mfiter
1132 
1133 }
@ tau_eb23
Definition: ERF_EBStruct.H:22
@ tau_eb13
Definition: ERF_EBStruct.H:22
@ yface
Definition: ERF_EBStruct.H:29
@ zface
Definition: ERF_EBStruct.H:29
@ xface
Definition: ERF_EBStruct.H:29
Here is the call graph for this function:

◆ computes_pblh()

bool SurfaceLayer::computes_pblh ( ) const
inline

Do we actually compute the PBL height? If not then the field returned by get_pblh holds only the value it was initialized with, and must not be reported as a diagnostic.

965 { return (pblh_type != PBLHeightCalcType::None); }

◆ computes_w_star()

bool SurfaceLayer::computes_w_star ( ) const
inline

Do we actually compute w*? If not then the field returned by get_w_star holds only the value it was initialized with, and must not be reported as a diagnostic.

959 { return m_include_wstar; }

◆ fill_qsurf_with_qsat()

void SurfaceLayer::fill_qsurf_with_qsat ( const int &  lev,
const amrex::MultiFab &  cons_in,
const std::unique_ptr< amrex::MultiFab > &  z_phys_nd 
)

Fill surface moisture from saturation specific humidity.

Parameters
[in]levlevel index
[in]cons_inconserved state used to evaluate surface pressure
[in]z_phys_ndnodal physical-height field

Fill sea-surface moisture with saturation specific humidity.

Parameters
[in]levCurrent level
[in]cons_inConserved state used to derive pressure at the surface
[in]z_phys_ndNodal physical height used to compute terrain-relative surface height
1342 {
1343  // NOTE: We have already tested a moisture model exists
1344 
1345  // Populate q_surf with qsat over water
1346  auto dz = m_geom[lev].CellSize(2);
1347  const int klo = m_geom[lev].Domain().smallEnd(2);
1348  for (MFIter mfi(*q_surf[lev]); mfi.isValid(); ++mfi)
1349  {
1350  Box gtbx = mfi.growntilebox();
1351 
1352  if (gtbx.smallEnd(2) != klo) { continue; }
1353 
1354  auto t_surf_arr = t_surf[lev]->array(mfi);
1355  auto q_surf_arr = q_surf[lev]->array(mfi);
1356  auto lmask_arr = (m_lmask_lev[lev][0]) ? m_lmask_lev[lev][0]->array(mfi) :
1357  Array4<int> {};
1358  const auto cons_arr = cons_in.const_array(mfi);
1359  const auto z_arr = (z_phys_nd) ? z_phys_nd->const_array(mfi) :
1360  Array4<const Real> {};
1361 
1362  ParallelFor(gtbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
1363  {
1364  int is_land = (lmask_arr) ? lmask_arr(i,j,k) : 1;
1365  if (!is_land) {
1366  auto deltaZ = (z_arr) ? Compute_Zrel_AtCellCenter(i,j,k,z_arr) :
1367  myhalf*dz;
1368  auto Rho = cons_arr(i,j,k,Rho_comp);
1369  auto RTh = cons_arr(i,j,k,RhoTheta_comp);
1370  auto Qv = cons_arr(i,j,k,RhoQ1_comp) / Rho;
1371  auto P_cc = getPgivenRTh(RTh, Qv);
1372  P_cc += Rho*CONST_GRAV*deltaZ;
1373  P_cc *= Real(0.01);
1374  erf_qsatw(t_surf_arr(i,j,k), P_cc, q_surf_arr(i,j,k));
1375  }
1376  });
1377  }
1378  q_surf[lev]->FillBoundary(m_geom[lev].periodicity());
1379 }
constexpr amrex::Real myhalf
Definition: ERF_Constants.H:13
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real getPgivenRTh(const amrex::Real rhotheta, const amrex::Real qv=amrex::Real(0))
Definition: ERF_EOS.H:81
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void erf_qsatw(amrex::Real t, amrex::Real p, amrex::Real &qsatw)
Definition: ERF_MicrophysicsUtils.H:228
AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real Compute_Zrel_AtCellCenter(const int &i, const int &j, const int &k, const amrex::Array4< const amrex::Real > &z_nd)
Definition: ERF_TerrainMetrics.H:740
@ dz
Definition: ERF_AdvanceWDM6.cpp:270
Here is the call graph for this function:

◆ fill_tsurf_with_coupled_sst()

void SurfaceLayer::fill_tsurf_with_coupled_sst ( const int &  lev)

Overwrite surface temperature with coupled ocean SST where the coupler covers the cell.

Runs after fill_tsurf_with_sst_and_tsk so that the lower-boundary data is the base layer: land cells, and water cells with no ocean donor, keep the value written there.

Parameters
[in]levlevel index

Overwrite surface temperature with coupled ocean SST where covered.

Parameters
[in]levCurrent level
1428 {
1429  // No coupler has handed us anything yet. Whatever fill_tsurf_with_sst_and_tsk
1430  // wrote stands, which is the correct answer for one-way and uncoupled runs.
1431  if (m_coupled_sst_lev.empty() || !m_coupled_sst_lev[lev]) { return; }
1432 
1433  // The loop below iterates t_surf and indexes the coupled arrays with the
1434  // same MFIter, so the layouts must agree. They do for planar terrain, where
1435  // t_surf is grids[lev] flattened with setRange(2,0) -- the same construction
1436  // GetOceanToAtmosSurfaceLayout reports. Under EB terrain t_surf keeps the
1437  // full 3D BoxArray and they would not, so fail loudly rather than read the
1438  // wrong fab.
1440  m_coupled_sst_lev[lev]->boxArray() == t_surf[lev]->boxArray() &&
1441  m_coupled_sst_lev[lev]->DistributionMap() == t_surf[lev]->DistributionMap(),
1442  "Coupled SST layout does not match the surface-layer layout.");
1443 
1444  const int klo = m_geom[lev].Domain().smallEnd(2);
1445 
1446  // Absent coverage information we must assume nothing is covered: silently
1447  // treating the whole field as valid is how an uncovered cell ends up holding
1448  // the remap's zero fill.
1449  const bool has_valid = (m_coupled_sst_valid_lev[lev] != nullptr);
1450 
1451  for (MFIter mfi(*t_surf[lev]); mfi.isValid(); ++mfi)
1452  {
1453  Box gtbx = mfi.growntilebox();
1454 
1455  if (gtbx.smallEnd(2) != klo) { continue; }
1456 
1457  // NOTE: the coupled lane does not carry lateral ghost cells, so clamp
1458  // into the valid box exactly as get_lsm_tsurf does. FillBoundary
1459  // in update_fluxes picks up the interior and periodic directions.
1460  Box vbx = mfi.validbox();
1461  int i_lo = vbx.smallEnd(0); int i_hi = vbx.bigEnd(0);
1462  int j_lo = vbx.smallEnd(1); int j_hi = vbx.bigEnd(1);
1463 
1464  auto t_surf_arr = t_surf[lev]->array(mfi);
1465  auto lmask_arr = (m_lmask_lev[lev][0]) ? m_lmask_lev[lev][0]->array(mfi) :
1466  Array4<int> {};
1467  const auto coupled_sst_arr = m_coupled_sst_lev[lev]->const_array(mfi);
1468  auto const& valid_arr = has_valid ? m_coupled_sst_valid_lev[lev]->const_array(mfi)
1469  : Array4<const int>{};
1470 
1471  ParallelFor(gtbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
1472  {
1473  int is_land = (lmask_arr) ? lmask_arr(i,j,k) : 1;
1474  if (is_land) { return; }
1475 
1476  int li = amrex::min(amrex::max(i, i_lo), i_hi);
1477  int lj = amrex::min(amrex::max(j, j_lo), j_hi);
1478 
1479  if (has_valid && valid_arr(li,lj,k) == 0) { return; }
1480 
1481  t_surf_arr(i,j,k) = coupled_sst_arr(li,lj,k);
1482  });
1483  }
1484 }
AMREX_ALWAYS_ASSERT_WITH_MESSAGE(m_cloud_chamber_config.active, "Cloud Chamber: initializer reached without a parsed configuration")
amrex::Vector< amrex::MultiFab * > m_coupled_sst_lev
Definition: ERF_SurfaceLayer.H:1330
amrex::Vector< amrex::iMultiFab * > m_coupled_sst_valid_lev
Definition: ERF_SurfaceLayer.H:1331
Here is the call graph for this function:

◆ fill_tsurf_with_sfc_sst()

void SurfaceLayer::fill_tsurf_with_sfc_sst ( const int &  lev,
const double &  time 
)

Fill surface temperature interpolated from time varying SST file

Parameters
[in]levlevel index
[in]timeinterpolation time
1304 {
1305  update_sfc_time_index(elapsed_time);
1306  const Real sfc_sst = interpolate_sfc_column(elapsed_time, 1);
1307  const int klo = m_geom[lev].Domain().smallEnd(2);
1308 
1309  for (MFIter mfi(*t_surf[lev]); mfi.isValid(); ++mfi)
1310  {
1311  Box gtbx = mfi.growntilebox();
1312 
1313  if (gtbx.smallEnd(2) != klo) { continue; }
1314 
1315  auto t_surf_arr = t_surf[lev]->array(mfi);
1316  auto lmask_arr = (m_lmask_lev[lev][0]) ? m_lmask_lev[lev][0]->array(mfi) :
1317  Array4<int> {};
1318 
1319  ParallelFor(gtbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
1320  {
1321  int is_land = (lmask_arr) ? lmask_arr(i,j,k) : 0;
1322  if (!is_land) {
1323  t_surf_arr(i,j,k) = sfc_sst;
1324  }
1325  });
1326  }
1327 
1328  t_surf[lev]->FillBoundary(m_geom[lev].periodicity());
1329 }
amrex::Real interpolate_sfc_column(const amrex::Real &time, int col) const
Definition: ERF_SurfaceLayer.cpp:324
void update_sfc_time_index(const amrex::Real &time)
Definition: ERF_SurfaceLayer.cpp:307
Here is the call graph for this function:

◆ fill_tsurf_with_sst_and_tsk()

void SurfaceLayer::fill_tsurf_with_sst_and_tsk ( const int &  lev,
const double &  elapsed_time_since_start_low 
)

Fill surface temperature from available SST and TSK data.

Parameters
[in]levlevel index
[in]timeinterpolation time

Fill surface temperature from SST/TSK lower-boundary data.

Parameters
[in]levCurrent level
[in]elapsed_time_since_start_lowTime since the start of the lower-boundary data
1217 {
1218  int n_times_in_sst = static_cast<int>(m_sst_lev[lev].size());
1219 
1220  double dT = m_low_time_interval;
1221 
1222  int n_time_lo, n_time_hi;
1223  Real alpha;
1224 
1225  if (n_times_in_sst > 1) {
1226  n_time_lo = static_cast<int>( elapsed_time_since_start_low / dT);
1227  alpha = static_cast<Real>((elapsed_time_since_start_low - n_time_lo * dT) / dT);
1228 
1229  AMREX_ALWAYS_ASSERT( alpha >= zero && alpha <= one);
1230 
1231  n_time_hi = n_time_lo + 1;
1232 
1233  // Do not over run the last sst file
1234  if (m_start_low_time + elapsed_time_since_start_low >= m_final_low_time) {
1235  n_time_lo = static_cast<int>(m_sst_lev[lev].size())-1;
1236  n_time_hi = n_time_lo;
1237  alpha = zero;
1238  }
1239 
1240  AMREX_ALWAYS_ASSERT( (n_time_lo >= 0) && (n_time_hi < m_sst_lev[lev].size()));
1241  } else {
1242  n_time_lo = 0;
1243  n_time_hi = 0;
1244  alpha = one;
1245  }
1246  AMREX_ALWAYS_ASSERT( alpha >= zero && alpha <= one);
1247 
1248  Real oma = one - alpha;
1249 
1250  // Define a default land surface temperature if we don't read in tsk
1252 
1253  bool use_tsk = (m_tsk_lev[lev][0]);
1254  bool ignore_sst = m_ignore_sst;
1255 
1256  const int klo = m_geom[lev].Domain().smallEnd(2);
1257 
1258  // Populate t_surf
1259  for (MFIter mfi(*t_surf[lev]); mfi.isValid(); ++mfi)
1260  {
1261  Box gtbx = mfi.growntilebox();
1262 
1263  if (gtbx.smallEnd(2) != klo) { continue; }
1264 
1265  auto t_surf_arr = t_surf[lev]->array(mfi);
1266 
1267  const auto sst_lo_arr = m_sst_lev[lev][n_time_lo]->const_array(mfi);
1268  const auto sst_hi_arr = m_sst_lev[lev][n_time_hi]->const_array(mfi);
1269 
1270  auto lmask_arr = (m_lmask_lev[lev][0]) ? m_lmask_lev[lev][0]->array(mfi) :
1271  Array4<int> {};
1272 
1273  if (use_tsk) {
1274  const auto tsk_arr = m_tsk_lev[lev][n_time_lo]->const_array(mfi);
1275  ParallelFor(gtbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
1276  {
1277  int is_land = (lmask_arr) ? lmask_arr(i,j,k) : 1;
1278  if (!is_land && !ignore_sst) {
1279  t_surf_arr(i,j,k) = oma * sst_lo_arr(i,j,k)
1280  + alpha * sst_hi_arr(i,j,k);
1281  } else {
1282  t_surf_arr(i,j,k) = tsk_arr(i,j,k);
1283  }
1284  });
1285  } else {
1286  ParallelFor(gtbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
1287  {
1288  int is_land = (lmask_arr) ? lmask_arr(i,j,k) : 1;
1289  if (!is_land) {
1290  t_surf_arr(i,j,k) = oma * sst_lo_arr(i,j,k)
1291  + alpha * sst_hi_arr(i,j,k);
1292  } else {
1293  t_surf_arr(i,j,k) = lst;
1294  }
1295  });
1296  }
1297  }
1298  t_surf[lev]->FillBoundary(m_geom[lev].periodicity());
1299 }
AMREX_ALWAYS_ASSERT(bx.length()[2]==khi+1)
amrex::Vector< amrex::Vector< amrex::MultiFab * > > m_sst_lev
Definition: ERF_SurfaceLayer.H:1320
amrex::Vector< amrex::Vector< amrex::MultiFab * > > m_tsk_lev
Definition: ERF_SurfaceLayer.H:1321
real(kind=kind_phys), parameter, private alpha
Definition: ERF_module_mp_wdm6.F90:62
Here is the call graph for this function:

◆ get_lmask()

amrex::iMultiFab* SurfaceLayer::get_lmask ( const int &  lev)
inline

Return the land-mask field.

Parameters
[in]levlevel index
1116 { return m_lmask_lev[lev][0]; }

◆ get_lsm_tsurf()

void SurfaceLayer::get_lsm_tsurf ( const int &  lev)

Fill surface temperature from the LSM surface-temperature field.

Parameters
[in]levlevel index

Fill surface temperature from land-surface-model data.

Parameters
[in]levCurrent level
1388 {
1389  const int klo = m_geom[lev].Domain().smallEnd(2);
1390  for (MFIter mfi(*t_surf[lev]); mfi.isValid(); ++mfi)
1391  {
1392  Box gtbx = mfi.growntilebox();
1393 
1394  if (gtbx.smallEnd(2) != klo) { continue; }
1395 
1396  // NOTE: LSM does not carry lateral ghost cells.
1397  // This copies the valid box into the ghost cells.
1398  // Fillboundary is called after this to pick up the
1399  // interior ghost and periodic directions.
1400  Box vbx = mfi.validbox();
1401  int i_lo = vbx.smallEnd(0); int i_hi = vbx.bigEnd(0);
1402  int j_lo = vbx.smallEnd(1); int j_hi = vbx.bigEnd(1);
1403 
1404  auto t_surf_arr = t_surf[lev]->array(mfi);
1405  auto lmask_arr = (m_lmask_lev[lev][0]) ? m_lmask_lev[lev][0]->array(mfi) :
1406  Array4<int> {};
1407  const auto lsm_arr = m_lsm_data_lev[lev][m_lsm_tsurf_indx]->const_array(mfi);
1408 
1409  ParallelFor(gtbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
1410  {
1411  int is_land = (lmask_arr) ? lmask_arr(i,j,k) : 1;
1412  if (is_land) {
1413  int li = amrex::min(amrex::max(i, i_lo), i_hi);
1414  int lj = amrex::min(amrex::max(j, j_lo), j_hi);
1415  t_surf_arr(i,j,k) = lsm_arr(li,lj,k);
1416  }
1417  });
1418  }
1419 }
int m_lsm_tsurf_indx
Definition: ERF_SurfaceLayer.H:1282
amrex::Vector< amrex::Vector< amrex::MultiFab * > > m_lsm_data_lev
Definition: ERF_SurfaceLayer.H:1332
Here is the call graph for this function:

◆ get_mac_avg()

const amrex::MultiFab* SurfaceLayer::get_mac_avg ( const int &  lev,
int  comp 
)
inline

Return a MOST-average field.

Parameters
[in]levlevel index
[in]compcomponent index
1002  {
1003  return m_ma.get_average(lev, comp);
1004  }
Here is the call graph for this function:

◆ get_mac_avg_ptr()

amrex::MultiFab* SurfaceLayer::get_mac_avg_ptr ( const int &  lev,
int  comp 
)
inline

Return a MOST-average field for modification (restart).

Parameters
[in]levlevel index
[in]compcomponent index
1036 { return m_ma.get_average(lev, comp); }
Here is the call graph for this function:

◆ get_mac_plane_avg()

amrex::Vector<amrex::Real> SurfaceLayer::get_mac_plane_avg ( const int &  lev) const
inline

Return the filtered plane averages, which hold the filter state for the plane and EB averaging policies.

Parameters
[in]levlevel index
1044 { return m_ma.get_plane_average(lev); }
amrex::Vector< amrex::Real > get_plane_average(const int &lev) const
Definition: ERF_MOSTAverage.H:285
Here is the call graph for this function:

◆ get_num_mac_avg()

int SurfaceLayer::get_num_mac_avg ( ) const
inline

Return the number of MOST-average components.

1014 { return m_ma.get_navg(); }
int get_navg() const
Definition: ERF_MOSTAverage.H:254
Here is the call graph for this function:

◆ get_olen()

amrex::MultiFab* SurfaceLayer::get_olen ( const int &  lev)
inline

Return the Obukhov length field.

Parameters
[in]levlevel index
986 { return olen[lev].get(); }

◆ get_pblh()

amrex::MultiFab* SurfaceLayer::get_pblh ( const int &  lev)
inline

Return the planetary-boundary-layer-height field.

Parameters
[in]levlevel index
993 { return pblh[lev].get(); }

◆ get_q_star()

amrex::MultiFab* SurfaceLayer::get_q_star ( const int &  lev)
inline

Return the moisture scale field.

Parameters
[in]levlevel index
979 { return q_star[lev].get(); }

◆ get_q_surf()

amrex::MultiFab* SurfaceLayer::get_q_surf ( const int &  lev)
inline

Return the surface-moisture field.

Parameters
[in]levlevel index
1075 { return q_surf[lev].get(); }

◆ get_surface_diagnostic_source()

amrex::MultiFab* SurfaceLayer::get_surface_diagnostic_source ( const int &  lev)
inline

Return the surface-diagnostic provenance field.

Parameters
[in]levlevel index
1090 { return surface_diagnostic_source[lev].get(); }

◆ get_t_star()

amrex::MultiFab* SurfaceLayer::get_t_star ( const int &  lev)
inline

Return the temperature scale field.

Parameters
[in]levlevel index
972 { return t_star[lev].get(); }

◆ get_t_surf()

amrex::MultiFab* SurfaceLayer::get_t_surf ( const int &  lev)
inline

Return the surface-temperature field.

Parameters
[in]levlevel index
1060 { return t_surf[lev].get(); }

◆ get_u_star()

amrex::MultiFab* SurfaceLayer::get_u_star ( const int &  lev)
inline

Return the friction-velocity field.

Parameters
[in]levlevel index
946 { return u_star[lev].get(); }

◆ get_w_star()

amrex::MultiFab* SurfaceLayer::get_w_star ( const int &  lev)
inline

Return the convective velocity scale field.

Parameters
[in]levlevel index
953 { return w_star[lev].get(); }

◆ get_z0()

amrex::MultiFab* SurfaceLayer::get_z0 ( const int &  lev)
inline

Return the roughness-height field.

Parameters
[in]levlevel index
1104 { return &z_0[lev]; }

◆ get_zref()

amrex::Real SurfaceLayer::get_zref ( const int &  lev)
inline

Return the minimum reference height for one level.

Parameters
[in]levlevel index
1097 { return (m_ma.get_zref(lev))->min(0); }
Here is the call graph for this function:

◆ have_variable_sea_roughness()

bool SurfaceLayer::have_variable_sea_roughness ( )
inline

Return whether variable sea roughness is active.

1109 { return m_var_z0; }
bool m_var_z0
Definition: ERF_SurfaceLayer.H:1274

◆ impose_SurfaceLayer_bcs()

void SurfaceLayer::impose_SurfaceLayer_bcs ( const int &  lev,
amrex::Vector< const amrex::MultiFab * >  mfs,
amrex::Vector< std::unique_ptr< amrex::MultiFab >> &  Tau_lev,
amrex::MultiFab *  xheat_flux,
amrex::MultiFab *  yheat_flux,
amrex::MultiFab *  zheat_flux,
amrex::MultiFab *  xqv_flux,
amrex::MultiFab *  yqv_flux,
amrex::MultiFab *  zqv_flux,
const amrex::MultiFab *  z_phys 
)

Impose surface-layer boundary conditions for planar terrain.

Parameters
[in]levlevel index
[in]mfsstate and velocity fields used by the BC computation
[in,out]Tau_levstress fields to fill
[in,out]xheat_fluxx-face heat flux field
[in,out]yheat_fluxy-face heat flux field
[in,out]zheat_fluxz-face heat flux field
[in,out]xqv_fluxx-face moisture flux field
[in,out]yqv_fluxy-face moisture flux field
[in,out]zqv_fluxz-face moisture flux field
[in]z_physphysical-height field

Wrapper to impose Monin Obukhov similarity theory fluxes by populating ghost cells.

Parameters
[in]levCurrent level
[in]mfsState MultiFabs used to compute the boundary fluxes
[in,out]Tau_levDiffusive stress MultiFabs populated with surface stresses
[in,out]xheat_fluxx-face heat-flux MultiFab, used when rotated fluxes are enabled
[in,out]yheat_fluxy-face heat-flux MultiFab, used when rotated fluxes are enabled
[in,out]zheat_fluxz-face heat-flux MultiFab populated with vertical surface heat flux
[in,out]xqv_fluxx-face moisture-flux MultiFab, used when rotated fluxes and moisture are enabled
[in,out]yqv_fluxy-face moisture-flux MultiFab, used when rotated fluxes and moisture are enabled
[in,out]zqv_fluxz-face moisture-flux MultiFab populated when moisture is enabled
[in]z_physNodal physical height used to rotate terrain-following fluxes
490 {
492  moeng_flux flux_comp;
493  compute_SurfaceLayer_bcs(lev, mfs, Tau_lev,
494  xheat_flux, yheat_flux, zheat_flux,
495  xqv_flux, yqv_flux, zqv_flux,
496  z_phys, flux_comp);
497  } else if (flux_type == FluxCalcType::DONELAN) {
498  donelan_flux flux_comp;
499  compute_SurfaceLayer_bcs(lev, mfs, Tau_lev,
500  xheat_flux, yheat_flux, zheat_flux,
501  xqv_flux, yqv_flux, zqv_flux,
502  z_phys, flux_comp);
503  } else if (flux_type == FluxCalcType::ROTATE) {
504  rotate_flux flux_comp;
505  compute_SurfaceLayer_bcs(lev, mfs, Tau_lev,
506  xheat_flux, yheat_flux, zheat_flux,
507  xqv_flux, yqv_flux, zqv_flux,
508  z_phys, flux_comp);
509  } else if (flux_type == FluxCalcType::RICO) {
511  compute_SurfaceLayer_bcs(lev, mfs, Tau_lev,
512  xheat_flux, yheat_flux, zheat_flux,
513  xqv_flux, yqv_flux, zqv_flux,
514  z_phys, flux_comp);
515  } else if (flux_type == FluxCalcType::BULK_COEFF) {
516  bulk_coeff_flux flux_comp(m_Cd, m_Ch, m_Cq);
517  compute_SurfaceLayer_bcs(lev, mfs, Tau_lev,
518  xheat_flux, yheat_flux, zheat_flux,
519  xqv_flux, yqv_flux, zqv_flux,
520  z_phys, flux_comp);
521  } else if (flux_type == FluxCalcType::CUSTOM) {
522  const bool fluxes_include_rho = specified_rho_surf || m_use_sfc_fluxes;
523  custom_flux flux_comp(fluxes_include_rho);
524  compute_SurfaceLayer_bcs(lev, mfs, Tau_lev,
525  xheat_flux, yheat_flux, zheat_flux,
526  xqv_flux, yqv_flux, zqv_flux,
527  z_phys, flux_comp);
528  } else {
529  amrex::Abort("Unknown surface layer flux calculation type");
530  }
531 }
bool specified_rho_surf
Definition: ERF_SurfaceLayer.H:1269
bool m_use_sfc_fluxes
Definition: ERF_SurfaceLayer.H:1285
void compute_SurfaceLayer_bcs(const int &lev, amrex::Vector< const amrex::MultiFab * > mfs, amrex::Vector< std::unique_ptr< amrex::MultiFab >> &Tau_lev, amrex::MultiFab *xheat_flux, amrex::MultiFab *yheat_flux, amrex::MultiFab *zheat_flux, amrex::MultiFab *xqv_flux, amrex::MultiFab *yqv_flux, amrex::MultiFab *zqv_flux, const amrex::MultiFab *z_phys, const FluxCalc &flux_comp)
Definition: ERF_MOSTStress.H:2582
Definition: ERF_MOSTStress.H:2428
Definition: ERF_MOSTStress.H:2264
Definition: ERF_MOSTStress.H:2066
Definition: ERF_MOSTStress.H:2744
Definition: ERF_MOSTStress.H:2910

◆ impose_SurfaceLayer_bcs_EB()

void SurfaceLayer::impose_SurfaceLayer_bcs_EB ( const int &  lev,
amrex::Vector< const amrex::MultiFab * >  mfs,
amrex::Vector< amrex::Vector< std::unique_ptr< amrex::MultiFab >>> &  Tau_lev,
amrex::MultiFab *  xheat_flux,
amrex::MultiFab *  yheat_flux,
amrex::MultiFab *  zheat_flux,
amrex::MultiFab *  xqv_flux,
amrex::MultiFab *  yqv_flux,
amrex::MultiFab *  zqv_flux 
)

Impose surface-layer boundary conditions for embedded-boundary terrain.

Parameters
[in]levlevel index
[in]mfsstate and velocity fields used by the BC computation
[in,out]Tau_levEB stress fields to fill
[in,out]xheat_fluxx-face heat flux field
[in,out]yheat_fluxy-face heat flux field
[in,out]zheat_fluxz-face heat flux field
[in,out]xqv_fluxx-face moisture flux field
[in,out]yqv_fluxy-face moisture flux field
[in,out]zqv_fluxz-face moisture flux field

Wrapper to impose Monin Obukhov similarity theory fluxes by populating ghost cells.

Parameters
[in]levCurrent level
[in]mfsState MultiFabs used to compute the EB boundary fluxes
[in,out]Tau_EBEB diffusive stress MultiFabs populated with surface stresses
[in,out]xheat_fluxx-face EB heat-flux MultiFab, currently unused
[in,out]yheat_fluxy-face EB heat-flux MultiFab, currently unused
[in,out]Hfx3_EBEB heat-flux MultiFab populated with scalar surface flux
[in,out]xqv_fluxx-face EB moisture-flux MultiFab, currently unused
[in,out]yqv_fluxy-face EB moisture-flux MultiFab, currently unused
[in,out]zqv_fluxz-face EB moisture-flux MultiFab, currently unused
556 {
558  moeng_flux_eb flux_comp;
559  compute_SurfaceLayer_bcs_EB(lev, mfs, Tau_EB,
560  xheat_flux, yheat_flux, Hfx3_EB,
561  xqv_flux, yqv_flux, zqv_flux,
562  flux_comp);
563  } else {
564  amrex::Abort("Not implemented surface layer flux calculation type for EB");
565  }
566 }
void compute_SurfaceLayer_bcs_EB(const int &lev, amrex::Vector< const amrex::MultiFab * > mfs, amrex::Vector< amrex::Vector< std::unique_ptr< amrex::MultiFab >>> &Tau_lev, amrex::MultiFab *xheat_flux, amrex::MultiFab *yheat_flux, amrex::MultiFab *zheat_flux, amrex::MultiFab *xqv_flux, amrex::MultiFab *yqv_flux, amrex::MultiFab *zqv_flux, const FluxCalc &flux_comp)
EB implementation of the Moeng surface-flux formulation.
Definition: ERF_EBMOSTStress.H:316

◆ init_tke_from_ustar()

void SurfaceLayer::init_tke_from_ustar ( const int &  lev,
amrex::MultiFab &  cons,
const std::unique_ptr< amrex::MultiFab > &  z_phys_nd,
const amrex::Real  tkefac = one,
const amrex::Real  zscale = amrex::Real(700.0) 
)

Initialize TKE from the current surface friction velocity.

Parameters
[in]levlevel index
[in,out]consconserved state whose TKE component is initialized
[in]z_phys_ndnodal physical-height field
[in]tkefacscale factor applied to the initialized TKE
[in]zscalevertical decay scale

Initialize TKE from surface-layer friction velocity.

Parameters
[in]levCurrent level
[in,out]consConserved state whose RhoKE component is initialized
[in]z_phys_ndNodal physical height used to compute height above ground
[in]tkefacFactor multiplying ustar squared for the surface TKE value
[in]zscaleScale factor used to taper TKE with height
1545 {
1546  Print() << "Initializing TKE from surface layer ustar on level " << lev << std::endl;
1547 
1548  // Handle vertical decomposition by selectively copying into
1549  // a FArrayBox section on each rank. Then doing a reduce real sum
1550  // and broadcasting to each rank. No mask since all CC data
1551  const int klo = m_geom[lev].Domain().smallEnd(2);
1552  Box bx_lo = u_star[lev]->boxArray().minimalBox();
1553  FArrayBox u_star_lo(bx_lo, 1); u_star_lo.setVal<RunOn::Device>(0);
1554  FArrayBox z_surf_lo(bx_lo, 1); z_surf_lo.setVal<RunOn::Device>(0);
1555  Real* ustar_ptr = u_star_lo.dataPtr();
1556  Real* zsurf_ptr = z_surf_lo.dataPtr();
1557  for (MFIter mfi(cons); mfi.isValid(); ++mfi)
1558  {
1559  Box vbx = mfi.validbox();
1560  if (vbx.smallEnd(2) != klo) { continue; }
1561  vbx.makeSlab(2,0);
1562 
1563  auto const& u_star_arr = u_star[lev]->const_array(mfi);
1564  auto u_star_all = u_star_lo.array();
1565 
1566  auto const& z_phys_arr = z_phys_nd->const_array(mfi);
1567  auto z_surf_all = z_surf_lo.array();
1568 
1569  ParallelFor(vbx, [=] AMREX_GPU_DEVICE(int i, int j, int ) noexcept
1570  {
1571  u_star_all(i,j,0) = u_star_arr(i,j,0);
1572  z_surf_all(i,j,0) = fourth * ( z_phys_arr(i ,j ,klo) + z_phys_arr(i+1,j ,klo)
1573  + z_phys_arr(i ,j+1,klo) + z_phys_arr(i+1,j+1,klo) );
1574  });
1575  }
1576  ParallelDescriptor::ReduceRealSum(ustar_ptr, static_cast<int>(bx_lo.numPts()));
1577  ParallelDescriptor::ReduceRealSum(zsurf_ptr, static_cast<int>(bx_lo.numPts()));
1578 
1579  // Now work on all boxes (ustar has been filled above)
1580  constexpr Real small = Real(0.01);
1581  for (MFIter mfi(cons); mfi.isValid(); ++mfi)
1582  {
1583  Box vbx = mfi.validbox();
1584 
1585  auto const& u_star_arr = u_star_lo.const_array();
1586  auto const& z_surf_arr = z_surf_lo.const_array();
1587  auto const& z_phys_arr = z_phys_nd->const_array(mfi);
1588 
1589  auto const& cons_arr = cons.array(mfi);
1590 
1591  ParallelFor(vbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
1592  {
1593  Real rho = cons_arr(i, j, k, Rho_comp);
1594  Real ust = u_star_arr(i, j, 0);
1595  Real tke0 = tkefac * ust * ust; // surface value
1596  Real zagl = Compute_Z_AtCellCenter(i, j, k, z_phys_arr) - z_surf_arr(i,j,0);
1597 
1598  // linearly tapering profile -- following WRF, approximate top of
1599  // PBL as ustar * zscale
1600  cons_arr(i, j, k, RhoKE_comp) = rho * tke0 * std::max(
1601  (ust * zscale - zagl) / (std::max(ust, small) * zscale),
1602  small);
1603  });
1604  }
1605 }
constexpr amrex::Real fourth
Definition: ERF_Constants.H:14
#define RhoKE_comp
Definition: ERF_IndexDefines.H:41
AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real Compute_Z_AtCellCenter(const int &i, const int &j, const int &k, const amrex::Array4< const amrex::Real > &z_nd)
Definition: ERF_TerrainMetrics.H:694
Here is the call graph for this function:

◆ interpolate_sfc_column()

Real SurfaceLayer::interpolate_sfc_column ( const amrex::Real time,
int  col 
) const

Interpolates the SFC/SST data at the given column and time

Parameters
[in]timeelapsed time
[in]colcolumn index of file data
326 {
327  if (sfc.empty() || sfc[0].empty()) { return zero; }
328  if (sfc[0].size() == 1) { return sfc[col][0]; }
329 
330  const Real t0 = sfc[0][sfc_time_ind];
331  const Real t1 = sfc[0][sfc_time_ind+1];
332  const Real x0 = sfc[col][sfc_time_ind];
333  const Real x1 = sfc[col][sfc_time_ind+1];
334 
335  if (elapsed_time < t0) {
336  return x0;
337  }
338 
339  if (t0 == t1 || elapsed_time > t1) {
340  return x1;
341  }
342 
343  const Real dt = (elapsed_time - t0) / (t1 - t0);
344  return x0 + (x1 - x0) * dt;
345 }
amrex::Vector< amrex::Vector< amrex::Real > > sfc
Definition: ERF_SurfaceLayer.H:1288
int sfc_time_ind
Definition: ERF_SurfaceLayer.H:1287
real(c_double), parameter t0
Definition: ERF_module_model_constants.F90:39

◆ lmask_min_reduce()

int SurfaceLayer::lmask_min_reduce ( amrex::iMultiFab &  lmask,
const int &  nghost 
)
inline

Compute the minimum land-mask value over valid and optional ghost cells.

Parameters
[in]lmaskland-mask field
[in]nghostnumber of ghost cells included in the reduction
1126  {
1127  int lmask_min = amrex::ReduceMin(lmask, nghost, [=] AMREX_GPU_HOST_DEVICE(
1128  amrex::Box const& bx, amrex::Array4<int const> const& lm_arr) -> int
1129  {
1130  int locmin = std::numeric_limits<int>::max();
1131  const auto lo = lbound(bx);
1132  const auto hi = ubound(bx);
1133  for (int j = lo.y; j <= hi.y; ++j) {
1134  for (int i = lo.x; i <= hi.x; ++i) {
1135  locmin = std::min(locmin, lm_arr(i, j, 0));
1136  }
1137  }
1138  return locmin;
1139  });
1140 
1141  return lmask_min;
1142  }

Referenced by make_SurfaceLayer_at_level().

Here is the caller graph for this function:

◆ mac_avg_is_initialized()

bool SurfaceLayer::mac_avg_is_initialized ( const int &  lev) const
inline

Return whether the time filter at this level holds meaningful history.

Parameters
[in]levlevel index
1021 { return m_ma.time_avg_is_initialized(lev); }
bool time_avg_is_initialized(const int &lev) const
Definition: ERF_MOSTAverage.H:262
Here is the call graph for this function:

◆ mac_avg_is_time_averaged()

bool SurfaceLayer::mac_avg_is_time_averaged ( ) const
inline

Return whether the MOST averages are filtered in time.

1009 { return m_ma.do_time_averaging(); }
bool do_time_averaging() const
Definition: ERF_MOSTAverage.H:249
Here is the call graph for this function:

◆ make_SurfaceLayer_at_level()

void SurfaceLayer::make_SurfaceLayer_at_level ( const int &  lev,
int  nlevs,
const amrex::Vector< amrex::MultiFab * > &  mfv,
std::unique_ptr< amrex::MultiFab > &  Theta_prim,
std::unique_ptr< amrex::MultiFab > &  Qv_prim,
std::unique_ptr< amrex::MultiFab > &  Qr_prim,
std::unique_ptr< amrex::MultiFab > &  z_phys_nd,
amrex::MultiFab *  Hwave,
amrex::MultiFab *  Lwave,
amrex::MultiFab *  eddyDiffs,
amrex::Vector< amrex::MultiFab * >  lsm_data,
amrex::Vector< std::string >  lsm_data_name,
amrex::Vector< amrex::MultiFab * >  lsm_flux,
amrex::Vector< std::string >  lsm_flux_name,
amrex::Vector< std::unique_ptr< amrex::MultiFab >> &  sst_lev,
amrex::Vector< std::unique_ptr< amrex::MultiFab >> &  tsk_lev,
amrex::Vector< std::unique_ptr< amrex::iMultiFab >> &  lmask_lev 
)
inline

Allocate and initialize surface-layer data for one AMR level.

Parameters
[in]levlevel index
[in]nlevsnumber of AMR levels
[in]mfvconserved and velocity MultiFabs for this level
[in]Theta_primprimitive potential-temperature field
[in]Qv_primprimitive water-vapor field
[in]Qr_primprimitive rain-water field
[in]z_phys_ndnodal physical-height field
[in]Hwavewave-height field
[in]Lwavewavelength field
[in]eddyDiffseddy-diffusivity field
[in]lsm_dataland-surface-model data fields
[in]lsm_data_namenames for lsm_data entries
[in]lsm_fluxland-surface-model flux fields
[in]lsm_flux_namenames for lsm_flux entries
[in]sst_levsea-surface-temperature data by time
[in]tsk_levskin-temperature data by time
[in]lmask_levland-mask data by time
351  {
352  // Update MOST Average
354  Theta_prim, Qv_prim, Qr_prim,
355  z_phys_nd);
356 
357  // Get CC vars
358  amrex::MultiFab& mf = *(mfv[0]);
359 
360  amrex::ParmParse pp("erf");
361 
362  // Do we have a time-varying surface roughness that needs to be saved?
363  if (lev == 0) {
364  const int nghost = 0; // ghost cells not included
365  int lmask_min = lmask_min_reduce(*lmask_lev[0].get(), nghost);
366  amrex::ParallelDescriptor::ReduceIntMin(lmask_min);
367 
368  m_var_z0 = (lmask_min < 1) & (rough_type_sea != RoughCalcType::CONSTANT);
369  if (m_var_z0) {
370  std::string rough_sea_string{"charnock"};
371  pp.queryAdd("most.roughness_type_sea", rough_sea_string);
372  amrex::Print() << "Variable sea roughness (type " << rough_sea_string
373  << ")" << std::endl;
374  }
375  }
376 
377  if (m_eddyDiffs_lev.size() < lev+1) {
378  m_Hwave_lev.resize(nlevs);
379  m_Lwave_lev.resize(nlevs);
380  m_eddyDiffs_lev.resize(nlevs);
381 
382  m_lsm_data_lev.resize(nlevs);
383  m_lsm_flux_lev.resize(nlevs);
384 
385  m_sst_lev.resize(nlevs);
386  m_tsk_lev.resize(nlevs);
387  m_lmask_lev.resize(nlevs);
388 
389  m_coupled_sst_lev.resize(nlevs, nullptr);
390  m_coupled_sst_valid_lev.resize(nlevs, nullptr);
391 
392  // Size the MOST params for all levels
393  z_0.resize(nlevs);
394  u_star.resize(nlevs);
395  w_star.resize(nlevs);
396  t_star.resize(nlevs);
397  q_star.resize(nlevs);
398  t_surf.resize(nlevs);
399  q_surf.resize(nlevs);
400  surface_diagnostic_source.resize(nlevs);
401  olen.resize(nlevs);
402  pblh.resize(nlevs);
403  }
404 
405  // Get pointers to SST,TSK and LANDMASK data
406  int nt_tot_sst = sst_lev.size();
407  m_sst_lev[lev].resize(nt_tot_sst);
408  for (int nt(0); nt < nt_tot_sst; ++nt) {
409  m_sst_lev[lev][nt] = sst_lev[nt].get();
410  }
411  int nt_tot_tsk = static_cast<int>(tsk_lev.size());
412  m_tsk_lev[lev].resize(nt_tot_tsk);
413  for (int nt(0); nt < nt_tot_tsk; ++nt) {
414  m_tsk_lev[lev][nt] = tsk_lev[nt].get();
415  }
416  int nt_tot_lmask = static_cast<int>(lmask_lev.size());
417  m_lmask_lev[lev].resize(nt_tot_lmask);
418  for (int nt(0); nt < nt_tot_lmask; ++nt) {
419  m_lmask_lev[lev][nt] = lmask_lev[nt].get();
420  }
421 
422  // Get pointers to wave data
423  m_Hwave_lev[lev] = Hwave;
424  m_Lwave_lev[lev] = Lwave;
425  m_eddyDiffs_lev[lev] = eddyDiffs;
426 
427  // Text-file driven surface forcing modes. The file always contains at
428  // least time(day) and sst(K); prescribed-flux mode also uses H, LE, USTAR.
429  pp.queryAdd("most.use_sfc_fluxes", m_use_sfc_fluxes);
430  pp.queryAdd("most.use_sfc_sst", m_use_sfc_sst);
432  amrex::Abort("Only one of most.use_sfc_fluxes and most.use_sfc_sst may be enabled");
433  }
435  if (m_terrain_type == TerrainType::EB) {
436  amrex::Abort("Text-file surface forcing is not supported with EB terrain");
437  }
438 
439  // load sensible and latent heat fluxes from sfc to prescribe
440  std::string sfc_file = "";
441  pp.queryAdd("most.sfc_file", sfc_file);
442  if (sfc_file.empty()) {
443  amrex::Abort("most.sfc_file must be set when using text-file surface forcing");
444  }
445 
446  // sfc contains: time(day) sst(K) H(W/m2) LE(W/m2) TAU(m2/s2)
447  sfc = read_cols(sfc_file, 1);
448 
449  const int min_cols = m_use_sfc_fluxes ? 5 : 2;
450  if (static_cast<int>(sfc.size()) < min_cols) {
451  amrex::Abort("Surface forcing file does not contain the required number of columns");
452  }
453 
454  // shift time column in days to be relative to current elapsed time
455  const amrex::Real start_day = sfc[0][0];
456  for (int i = 0; i < static_cast<int>(sfc[0].size()); ++i) {
457  sfc[0][i] = 86400.0 * (sfc[0][i] - start_day);
458  }
459 
460  if (m_use_sfc_sst) {
462  amrex::Abort("most.use_sfc_sst cannot be combined with prescribed heat flux or surf_heating_rate");
463  }
465  amrex::Abort("most.use_sfc_sst cannot be combined with prescribed moisture flux");
466  }
468  amrex::Print() << "Using MOST with prescribed SST from most.sfc_file '" << sfc_file << "' over sea" << std::endl;
469  }
470 
471  if (m_use_sfc_fluxes) {
473  amrex::Print() << "Using MOST with prescribed time-varying surface fluxes from '" << sfc_file << "'" << std::endl;
474  }
475  }
476 
477  // Get pointers to LSM data and Fluxes
478  int ndata = static_cast<int>(lsm_data.size());
479  int nflux = static_cast<int>(lsm_flux.size());
480  m_lsm_data_name.resize(ndata);
481  m_lsm_data_lev[lev].resize(ndata);
482  m_lsm_flux_name.resize(nflux);
483  m_lsm_flux_lev[lev].resize(nflux);
484  for (int n(0); n < ndata; ++n) {
485  m_lsm_data_name[n] = lsm_data_name[n];
486  m_lsm_data_lev[lev][n] = lsm_data[n];
487  const std::string lc_name = amrex::toLower(lsm_data_name[n]);
488  if (lc_name == "theta" || lc_name == "t_surf") {
489  m_has_lsm_tsurf = true;
490  m_lsm_tsurf_indx = n;
491  }
492  }
494  amrex::Abort("most.use_sfc_sst cannot be combined with an ocean LSM t_surf input");
495  }
496  int n_valid_lsm_flux = 0;
497  bool has_soil_t_flux = false;
498  for (int n(0); n < nflux; ++n) {
499  m_lsm_flux_name[n] = lsm_flux_name[n];
500  m_lsm_flux_lev[lev][n] = lsm_flux[n];
501  if (m_lsm_flux_lev[lev][n]) { ++n_valid_lsm_flux; }
502  if (amrex::toLower(m_lsm_flux_name[n]) == "soil_t_flux") {
503  has_soil_t_flux = true;
504  }
505  }
506  AMREX_ALWAYS_ASSERT((n_valid_lsm_flux==0 || n_valid_lsm_flux>=4 ||
507  (n_valid_lsm_flux==1 && has_soil_t_flux)));
508  if (n_valid_lsm_flux>=4) { m_has_lsm_fluxes = true; }
509 
510  // Check if there is a user-specified roughness file to be read
511  std::string fname;
512  bool read_z0 = false;
513  if ( (flux_type == FluxCalcType::MOENG) ||
515  int count = pp.countval("most.roughness_file_name");
516  if (count > 1) {
517  AMREX_ALWAYS_ASSERT(count >= lev+1);
518  pp.query("most.roughness_file_name", fname, lev);
519  read_z0 = true;
520  } else if (count == 1) {
521  if (lev == 0) {
522  pp.queryAdd("most.roughness_file_name", fname);
523  } else {
524  // we will interpolate from the coarsest level
525  fname = "";
526  }
527  read_z0 = true;
528  }
529  // else use z0_const
530  }
531 
532  // Attributes for MFs and FABs
533  //--------------------------------------------------------
534  // Create a 2D ba for planar terrain, 3D for EB terrain
535  amrex::BoxArray ba = mf.boxArray();
536  amrex::BoxArray ba_flux;
537  amrex::IntVect ng{1,1,0};
538 
539  if (m_terrain_type == TerrainType::EB) {
540  // Use full 3D BoxArray for EB terrain
541  ba_flux = ba;
542  ng = amrex::IntVect{1,1,1}; // Include z ghost cells
543  } else {
544  // Collapse to 2D for planar terrain
545  amrex::BoxList bl2d = ba.boxList();
546  for (auto& b : bl2d) { b.setRange(2,0); }
547  ba_flux = amrex::BoxArray(std::move(bl2d));
548  }
549 
550  const amrex::DistributionMapping& dm = mf.DistributionMap();
551  const int ncomp = 1;
552 
553  // Z0 heights FAB
554  //--------------------------------------------------------
555  z_0[lev].define(ba_flux, dm, ncomp, ng);
556  z_0[lev].setVal(z0_const);
557  if (read_z0) {
558  read_custom_roughness(lev, fname);
559  }
560 
561  // 2D MFs for U*, T*, T_surf
562  //--------------------------------------------------------
563  u_star[lev] = std::make_unique<amrex::MultiFab>(ba_flux, dm, ncomp, ng);
564  u_star[lev]->setVal(bogus_large_value);
565 
566  w_star[lev] = std::make_unique<amrex::MultiFab>(ba_flux, dm, ncomp, ng);
567  w_star[lev]->setVal(bogus_large_value);
568 
569  t_star[lev] = std::make_unique<amrex::MultiFab>(ba_flux, dm, ncomp, ng);
570  t_star[lev]->setVal(zero); // default to neutral
571 
572  q_star[lev] = std::make_unique<amrex::MultiFab>(ba_flux, dm, ncomp, ng);
573  q_star[lev]->setVal(zero); // default to dry
574 
575  olen[lev] = std::make_unique<amrex::MultiFab>(ba_flux, dm, ncomp, ng);
576  olen[lev]->setVal(bogus_large_value);
577 
578  pblh[lev] = std::make_unique<amrex::MultiFab>(ba_flux, dm, ncomp, ng);
579  pblh[lev]->setVal(bogus_large_value);
580 
581  t_surf[lev] = std::make_unique<amrex::MultiFab>(ba_flux, dm, ncomp, ng);
582  t_surf[lev]->setVal(default_land_surf_temp);
583 
584  q_surf[lev] = std::make_unique<amrex::MultiFab>(ba_flux, dm, ncomp, ng);
585  q_surf[lev]->setVal(default_land_surf_moist);
586 
587  surface_diagnostic_source[lev] = std::make_unique<amrex::MultiFab>(ba_flux, dm, ncomp, ng);
588  surface_diagnostic_source[lev]->setVal(
590 
591  // TODO: Do we want an enum struct for indexing?
592 
593  bool use_sst = (!m_sst_lev[lev].empty() && m_sst_lev[lev][0]);
594  bool use_tsk = (!m_tsk_lev[lev].empty() && m_tsk_lev[lev][0]);
595  if (use_sst || use_tsk || m_has_lsm_tsurf || m_use_coupled_sst) {
596  // Valid SST, TSK, LSM or coupled-ocean data; t_surf set before computing
597  // fluxes (avoids extended lambda capture) Note that land temp will be set
598  // from m_tsk_lev while sea temp will be set from m_sst_lev
600 
601  // Pathways in fill_tsurf_with_sst_and_tsk
602  amrex::Print() << "Using MOST with specified surface temperature ";
603  if (m_has_lsm_tsurf && !use_sst && !use_tsk) {
604  amrex::Print() << "(LSM: " << m_lsm_data_name[m_lsm_tsurf_indx] << ")";
605  } else if (!use_sst && !use_tsk) {
606  amrex::Print() << "(land: T0, sea: none)";
607  } else {
608  // NOTE: SST from the LOW file populates TSK in update_sst_tsk.
609  // So if we have TSK, it contains everything and has been
610  // sanity checked for valid SST values.
611  if (use_tsk) { m_ignore_sst = true; }
612  if (use_tsk) {
613  amrex::Print() << "(land: TSK, ";
614  } else {
615  amrex::Print() << "(land: T0, ";
616  }
617  if (use_tsk && !use_sst) {
618  amrex::Print() << "sea: TSK)";
619  } else {
620  amrex::Print() << "sea: SST)";
622  }
623  }
624  // The coupler is layered on top of whatever the above selected: it
625  // overwrites only the water cells it actually covers, so the pathway
626  // named above remains the value for land and for uncovered water.
627  if (m_use_coupled_sst) {
628  amrex::Print() << " + coupled ocean SST where covered";
629  }
630  amrex::Print() << std::endl;
631  }
632  }
constexpr amrex::Real bogus_large_value
Definition: ERF_Constants.H:26
void make_MOSTAverage_at_level(const int &lev, const amrex::Vector< amrex::MultiFab * > &vars_old, std::unique_ptr< amrex::MultiFab > &Theta_prim, std::unique_ptr< amrex::MultiFab > &Qv_prim, std::unique_ptr< amrex::MultiFab > &Qr_prim, std::unique_ptr< amrex::MultiFab > &z_phys_nd)
Definition: ERF_MOSTAverage.cpp:111
int lmask_min_reduce(amrex::iMultiFab &lmask, const int &nghost)
Definition: ERF_SurfaceLayer.H:1124
amrex::Vector< std::string > m_lsm_data_name
Definition: ERF_SurfaceLayer.H:1334
bool m_has_lsm_tsurf
Definition: ERF_SurfaceLayer.H:1281
bool m_has_lsm_fluxes
Definition: ERF_SurfaceLayer.H:1280
static amrex::Vector< amrex::Vector< amrex::Real > > read_cols(const std::string &fname, const int skip_nlines=1)
Definition: ERF_SurfaceLayer.cpp:1743
bool m_use_sfc_sst
Definition: ERF_SurfaceLayer.H:1286
bool m_use_coupled_sst
Definition: ERF_SurfaceLayer.H:1296
void read_custom_roughness(const int &lev, const std::string &fname)
Definition: ERF_SurfaceLayer.cpp:1615
Here is the call graph for this function:

◆ read_cols()

amrex::Vector< amrex::Vector< amrex::Real > > SurfaceLayer::read_cols ( const std::string &  fname,
const int  skip_nlines = 1 
)
static

Reads columns of data from a text file, returning each column in a vector.

Parameters
[in]fnamepath to text file
[in]skip_nlinesnumber of lines to skip before reading data (e.g, header lines)
Returns
Vector containing each column in the file as a vector
1744 {
1745  std::ifstream ifs(fname);
1746  if (!ifs.is_open())
1747  {
1748  amrex::Error("Error opening input file " + fname);
1749  }
1750 
1751  amrex::Vector<amrex::Vector<amrex::Real>> col_data;
1752  std::string line;
1753  int nlines = 0;
1754  int ncols = -1;
1755 
1756  const auto print_err = [](const std::string &err_fname, int lineno, int cols, int expected_cols) {
1757  amrex::Error("Error reading file '" + err_fname + "': expected line " +
1758  std::to_string(lineno) + " to have " + std::to_string(expected_cols) +
1759  " columns, but got " + std::to_string(cols));
1760  };
1761 
1762  while (std::getline(ifs, line))
1763  {
1764  nlines++;
1765  if (nlines <= skip_nlines) continue;
1766  if (line.empty()) continue;
1767 
1768  std::istringstream iss(line);
1769 
1770  amrex::Real tmp;
1771  // Get the number of columns in the file
1772  if (ncols == -1) {
1773  int j = 0;
1774  while (iss >> tmp) {
1775  col_data.push_back(amrex::Vector<amrex::Real>());
1776  j+= 1;
1777  }
1778 
1779  ncols = j;
1780  iss = std::istringstream(line);
1781  }
1782 
1783  int j = 0;
1784  while (iss >> tmp) {
1785  // verify each line has the same number of columns
1786  if (j >= ncols) {
1787  print_err(fname, nlines, j+1, ncols);
1788  }
1789  col_data[j].push_back(tmp);
1790  j+= 1;
1791  }
1792 
1793  // throw error if there are fewer columns in the line than expected
1794  if (j != ncols) {
1795  print_err(fname, nlines, j, ncols);
1796  }
1797  }
1798 
1799  ifs.close();
1800 
1801  return col_data;
1802 }
@ tmp
Definition: ERF_AdvanceWSM6.cpp:114

Referenced by make_SurfaceLayer_at_level().

Here is the caller graph for this function:

◆ read_custom_roughness()

void SurfaceLayer::read_custom_roughness ( const int &  lev,
const std::string &  fname 
)

Read custom roughness data for one level.

Parameters
[in]levlevel index
[in]fnameroughness-data file name

Read or interpolate custom roughness length data.

Parameters
[in]levCurrent level
[in]fnameRoughness file name; an empty name interpolates from level 0
1617 {
1618  // Read the file if we have it
1619  if (!fname.empty()) {
1620  // Only the ioproc reads the file
1621  Gpu::HostVector<Real> m_x,m_y,m_z0;
1622  if (ParallelDescriptor::IOProcessor()) {
1623  Print()<<"Reading MOST roughness file at level " << lev << " : " << fname << std::endl;
1624  std::ifstream file(fname);
1625  Real value1,value2,value3;
1626  while(file>>value1>>value2>>value3){
1627  m_x.push_back(value1);
1628  m_y.push_back(value2);
1629  m_z0.push_back(value3);
1630  }
1631  file.close();
1632 
1633  AMREX_ALWAYS_ASSERT(m_x.size() == m_y.size());
1634  AMREX_ALWAYS_ASSERT(m_x.size() == m_z0.size());
1635  }
1636 
1637  // Broadcast the whole domain to every rank
1638  int ioproc = ParallelDescriptor::IOProcessorNumber();
1639  int nnode = static_cast<int>(m_x.size());
1640  ParallelDescriptor::Bcast(&nnode, 1, ioproc);
1641 
1642  if (!ParallelDescriptor::IOProcessor()) {
1643  m_x.resize(nnode);
1644  m_y.resize(nnode);
1645  m_z0.resize(nnode);
1646  }
1647  ParallelDescriptor::Bcast(m_x.data() , nnode, ioproc);
1648  ParallelDescriptor::Bcast(m_y.data() , nnode, ioproc);
1649  ParallelDescriptor::Bcast(m_z0.data(), nnode, ioproc);
1650 
1651  // Copy data to the GPU
1652  Gpu::DeviceVector<Real> d_x(nnode),d_y(nnode),d_z0(nnode);
1653  Gpu::copy(Gpu::hostToDevice, m_x.begin(), m_x.end(), d_x.begin());
1654  Gpu::copy(Gpu::hostToDevice, m_y.begin(), m_y.end(), d_y.begin());
1655  Gpu::copy(Gpu::hostToDevice, m_z0.begin(), m_z0.end(), d_z0.begin());
1656  Real* xp = d_x.data();
1657  Real* yp = d_y.data();
1658  Real* z0p = d_z0.data();
1659 
1660  // Each rank populates it's z_0[lev] MultiFab
1661  const int klo = m_geom[lev].Domain().smallEnd(2);
1662  for (MFIter mfi(z_0[lev]); mfi.isValid(); ++mfi)
1663  {
1664  Box gtbx = mfi.growntilebox();
1665 
1666  if (gtbx.smallEnd(2) != klo) { continue; }
1667 
1668  // Populate z_phys data
1669  Real tol = Real(1.0e-4);
1670  auto dx = m_geom[lev].CellSizeArray();
1671  auto ProbLoArr = m_geom[lev].ProbLoArray();
1672  int ilo = m_geom[lev].Domain().smallEnd(0);
1673  int jlo = m_geom[lev].Domain().smallEnd(1);
1674  int ihi = m_geom[lev].Domain().bigEnd(0);
1675  int jhi = m_geom[lev].Domain().bigEnd(1);
1676 
1677  Array4<Real> const& z0_arr = z_0[lev].array(mfi);
1678  ParallelFor(gtbx, [=] AMREX_GPU_DEVICE (int i, int j, int /*k*/)
1679  {
1680  // Clip indices for ghost-cells
1681  int ii = amrex::min(amrex::max(i,ilo),ihi);
1682  int jj = amrex::min(amrex::max(j,jlo),jhi);
1683 
1684  // Location of nodes
1685  Real x = ProbLoArr[0] + ii * dx[0];
1686  Real y = ProbLoArr[1] + jj * dx[1];
1687  int inode = ii + jj * (ihi-ilo+2); // stride is Nx+1
1688  if (std::sqrt(amrex::Math::powi<2>(x-xp[inode])+amrex::Math::powi<2>(y-yp[inode])) < tol) {
1689  z0_arr(i,j,klo) = z0p[inode];
1690  } else {
1691  // Unexpected list order, do brute force search
1692  Real z0loc = zero;
1693  bool found = false;
1694  for (int n=0; n<nnode; ++n) {
1695  Real delta=std::sqrt(amrex::Math::powi<2>(x-xp[n])+amrex::Math::powi<2>(y-yp[n]));
1696  if (delta < tol) {
1697  found = true;
1698  z0loc = z0p[n];
1699  break;
1700  }
1701  }
1702  AMREX_ASSERT_WITH_MESSAGE(found, "Location read from terrain file does not match the grid!");
1703  amrex::ignore_unused(found);
1704  z0_arr(i,j,klo) = z0loc;
1705  }
1706  });
1707  } // mfi
1708  } else {
1709  AMREX_ALWAYS_ASSERT(lev > 0);
1710 
1711  Print()<<"Interpolating MOST roughness at level " << lev << std::endl;
1712 
1713  // Create a BC mapper that uses FOEXTRAP at domain bndry
1714  Vector<int> bc_lo(3,ERFBCType::foextrap);
1715  Vector<int> bc_hi(3,ERFBCType::foextrap);
1716  Vector<BCRec> bcr; bcr.push_back(BCRec(bc_lo.data(),bc_hi.data()));
1717 
1718  // Create ref ratio
1719  IntVect ratio;
1720  for (int idim = 0; idim < AMREX_SPACEDIM; ++idim) {
1721  ratio[idim] = m_geom[lev].Domain().length(idim) / m_geom[0].Domain().length(idim);
1722  }
1723 
1724  // Create interp object and interpolate from the coarsest grid
1725  MFInterpolater* interp = &mf_cell_cons_interp;
1726  interp->interp(z_0[0] , 0,
1727  z_0[lev], 0,
1728  1, z_0[lev].nGrowVect(),
1729  m_geom[0], m_geom[lev],
1730  m_geom[lev].Domain(),ratio,
1731  bcr, 0);
1732  }
1733 }
@ m_y
Definition: ERF_DataStruct.H:29
@ m_x
Definition: ERF_DataStruct.H:28
const Real dx
Definition: ERF_InitCustomPert_ABL.H:44
@ foextrap
Definition: ERF_IndexDefines.H:291

Referenced by make_SurfaceLayer_at_level().

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

◆ set_coupled_sst_active()

void SurfaceLayer::set_coupled_sst_active ( const bool  active)
inline

Declare that an ocean coupler will supply SST for this run.

Must be called before make_SurfaceLayer_at_level, which uses it to select ThetaCalcType::SURFACE_TEMPERATURE.

Parameters
[in]activewhether coupled SST is configured
1193 { m_use_coupled_sst = active; }

◆ set_mac_avg_initialized()

void SurfaceLayer::set_mac_avg_initialized ( const int &  lev)
inline

Declare the time filter at this level to hold meaningful history (restart).

Parameters
[in]levlevel index
void set_time_avg_initialized(const int &lev)
Definition: ERF_MOSTAverage.H:273
Here is the call graph for this function:

◆ set_mac_plane_avg()

bool SurfaceLayer::set_mac_plane_avg ( const int &  lev,
const amrex::Vector< amrex::Real > &  pavg 
)
inline

Restore the filtered plane averages from a checkpoint; returns false if the checkpoint does not hold what this run expects.

Parameters
[in]levlevel index
[in]pavgfiltered plane averages, one per average component
1053 { return m_ma.set_plane_average(lev, pavg); }
bool set_plane_average(const int &lev, const amrex::Vector< amrex::Real > &pavg)
Definition: ERF_MOSTAverage.H:299
Here is the call graph for this function:

◆ set_q_surf()

void SurfaceLayer::set_q_surf ( const int &  lev,
const amrex::Real  qsurf 
)
inline

Set the surface-moisture field to a constant value.

Parameters
[in]levlevel index
[in]qsurfsurface moisture
1083 { q_surf[lev]->setVal(qsurf); }

◆ set_t_surf()

void SurfaceLayer::set_t_surf ( const int &  lev,
const amrex::Real  tsurf 
)
inline

Set the surface-temperature field to a constant value.

Parameters
[in]levlevel index
[in]tsurfsurface temperature
1068 { t_surf[lev]->setVal(tsurf); }

◆ update_coupled_sst_ptr()

void SurfaceLayer::update_coupled_sst_ptr ( const int  lev,
amrex::MultiFab *  sst_ptr,
amrex::iMultiFab *  valid_ptr 
)
inline

Hand over the coupled sea-surface temperature and its per-cell coverage flag.

Both pointers stay owned by the caller and must remain valid, and on the same layout, until replaced. Passing a null sst_ptr retracts the coupled lane, so the lower-boundary SST/TSK value stands everywhere again.

Parameters
[in]levlevel index
[in]sst_ptrcoupled sea-surface temperature [K]
[in]valid_ptrper-cell coverage flag; nonzero where the coupler supplied a value. Null means no cell is covered.
1180  {
1181  m_coupled_sst_lev[lev] = sst_ptr;
1182  m_coupled_sst_valid_lev[lev] = valid_ptr;
1183  }

◆ update_fluxes()

void SurfaceLayer::update_fluxes ( const int &  lev,
const double &  elapsed_time,
const double &  elapsed_time_since_start_low,
amrex::MultiFab &  cons_in,
const std::unique_ptr< amrex::MultiFab > &  z_phys_nd,
const std::unique_ptr< amrex::MultiFab > &  walldist,
int  max_iters = 100 
)

Update surface fluxes and related surface-layer state.

Parameters
[in]levlevel index
[in]elapsed_timecurrent elapsed simulation time
[in]elapsed_time_since_start_lowelapsed time relative to low-data start
[in,out]cons_inconserved state used by the flux update
[in]z_phys_ndnodal physical-height field
[in]walldistwall-distance field
[in]max_itersmaximum MOST iteration count

Wrapper to update ustar and tstar for Monin Obukhov similarity theory.

Parameters
[in]levCurrent level
[in]elapsed_timeCurrent simulation time
[in]elapsed_time_since_start_lowTime since the start of the lower-boundary data
[in,out]cons_inConserved state, updated when RANS TKE is initialized from surface-layer data
[in]z_phys_ndNodal physical height used by terrain-aware surface calculations
[in]walldistWall distance used when updating boundary TKE
[in]max_itersMaximum iterations to use in the MOST flux solve
25 {
26  // Update with SST/TSK data if we have a valid pointer.
27  //
28  // This runs even when an ocean coupler is active: it is the only writer of
29  // t_surf over land, and it is the fallback for the water cells the coupler
30  // does not cover. Coupled SST is applied below and only where the coupler
31  // actually supplied a value, so the lower-boundary data is the base layer
32  // rather than an alternative to it.
33  if (!m_sst_lev[lev].empty() && m_sst_lev[lev][0]) {
34  fill_tsurf_with_sst_and_tsk(lev, elapsed_time_since_start_low);
35  }
36  if (m_use_sfc_sst) {
37  // Set tsurf to time varying SST from sfc file
38  fill_tsurf_with_sfc_sst(lev, elapsed_time);
39  }
40 
41  // Apply heating rate if needed
43  update_surf_temp(elapsed_time_since_start_low);
44  }
45 
46  // Overwrite the covered water cells with coupled ocean SST. This must come
47  // after update_surf_temp, which is a whole-domain setVal, and before
48  // fill_qsurf_with_qsat, which derives sea-surface humidity from t_surf.
50 
51  // Update qsurf with qsat over sea
52  if (use_moisture) {
53  fill_qsurf_with_qsat(lev, cons_in, z_phys_nd);
54  }
55 
56  // Update land surface temp if we have a valid pointer
57  if (m_has_lsm_tsurf) { get_lsm_tsurf(lev); }
58 
59  // Fill interior ghost cells
60  t_surf[lev]->FillBoundary(m_geom[lev].periodicity());
61 
62  // Compute plane averages for all vars (regardless of flux type)
64 
65  // NOTE: Do iterations to seed variables on the first step (LSM called post step)
66  // as well as compute values where invalid LSM fluxes may reside
67  //*******************************************************************************
68  // ***************************************************************
69  // Iterate the fluxes if moeng type
70  // First iterate over land -- the only model for surface roughness
71  // over land is RoughCalcType::CONSTANT
72  // ***************************************************************
75  bool is_land = true;
76  // Do we have a constant flux for moisture over land?
77  bool cons_qflux = ( (moist_type == MoistCalcType::MOISTURE_FLUX) ||
79  if (m_terrain_type != TerrainType::EB) {
82  surface_flux most_flux(surf_temp_flux, surf_moist_flux, cons_qflux);
83  compute_fluxes(lev, max_iters, cons_in, most_flux, is_land);
84  } else {
85  amrex::Abort("Unknown value for rough_type_land");
86  }
89  surface_temp most_flux(surf_temp_flux, surf_moist_flux, cons_qflux);
90  compute_fluxes(lev, max_iters, cons_in, most_flux, is_land);
91  } else {
92  amrex::Abort("Unknown value for rough_type_land");
93  }
94  } else if ((theta_type == ThetaCalcType::ADIABATIC) &&
98  compute_fluxes(lev, max_iters, cons_in, most_flux, is_land);
99  } else {
100  amrex::Abort("Unknown value for rough_type_land");
101  }
102  } else {
103  amrex::Abort("Unknown value for theta_type");
104  }
105  // EB
106  } else {
109  surface_flux_eb most_flux(surf_temp_flux, surf_moist_flux, cons_qflux);
110  compute_fluxes(lev, max_iters, cons_in, most_flux, is_land);
111  } else {
112  amrex::Abort("Unknown value for rough_type_land");
113  }
116  surface_temp_eb most_flux(surf_temp_flux, surf_moist_flux, cons_qflux);
117  compute_fluxes(lev, max_iters, cons_in, most_flux, is_land);
118  } else {
119  amrex::Abort("Unknown value for rough_type_land");
120  }
121  } else if ((theta_type == ThetaCalcType::ADIABATIC) &&
125  compute_fluxes(lev, max_iters, cons_in, most_flux, is_land);
126  } else {
127  amrex::Abort("Unknown value for rough_type_land");
128  }
129  } else {
130  amrex::Abort("Unknown value for theta_type");
131  }
132  } // EB
133  } // MOENG -- LAND
134 
135  // Update u*/T*/q*/L over land (iterations or from LSM fluxes)
136  if (m_has_lsm_fluxes && elapsed_time > zero) {
138  }
139 
140  // ***************************************************************
141  // Iterate the fluxes if moeng type
142  // Next iterate over sea -- the models for surface roughness
143  // over sea are CHARNOCK, DONELAN, MODIFIED_CHARNOCK or WAVE_COUPLED
144  // NOTE: Sea surface fluxes are not supported for EB terrain
145  // ***************************************************************
146  if ((flux_type == FluxCalcType::MOENG ||
148  m_terrain_type != TerrainType::EB) {
149  bool is_land = false;
150  // NOTE: Do not allow default to adiabatic over sea (we have Qvs at surface)
151  // Do we have a constant flux for moisture over sea?
152  bool cons_qflux = (moist_type == MoistCalcType::MOISTURE_FLUX);
156  cnk_a, cnk_visc, cons_qflux);
157  compute_fluxes(lev, max_iters, cons_in, most_flux, is_land);
160  depth, cons_qflux);
161  compute_fluxes(lev, max_iters, cons_in, most_flux, is_land);
162  } else if (rough_type_sea == RoughCalcType::DONELAN) {
164  cons_qflux);
165  compute_fluxes(lev, max_iters, cons_in, most_flux, is_land);
168  cons_qflux);
169  compute_fluxes(lev, max_iters, cons_in, most_flux, is_land);
170  } else {
171  amrex::Abort("Unknown value for rough_type_sea");
172  }
173 
177  cnk_a, cnk_visc, cons_qflux);
178  compute_fluxes(lev, max_iters, cons_in, most_flux, is_land);
181  depth, cons_qflux);
182  compute_fluxes(lev, max_iters, cons_in, most_flux, is_land);
183  } else if (rough_type_sea == RoughCalcType::DONELAN) {
185  cons_qflux);
186  compute_fluxes(lev, max_iters, cons_in, most_flux, is_land);
189  cons_qflux);
190  compute_fluxes(lev, max_iters, cons_in, most_flux, is_land);
191  } else {
192  amrex::Abort("Unknown value for rough_type_sea");
193  }
194 
195  } else if ((theta_type == ThetaCalcType::ADIABATIC) &&
199  cnk_a, cnk_visc);
200  compute_fluxes(lev, max_iters, cons_in, most_flux, is_land);
203  depth);
204  compute_fluxes(lev, max_iters, cons_in, most_flux, is_land);
205  } else if (rough_type_sea == RoughCalcType::DONELAN) {
207  compute_fluxes(lev, max_iters, cons_in, most_flux, is_land);
210  compute_fluxes(lev, max_iters, cons_in, most_flux, is_land);
211  } else {
212  amrex::Abort("Unknown value for rough_type_sea");
213  }
214  } else {
215  amrex::Abort("Unknown value for theta_type");
216  }
217  } // MOENG -- SEA
218 
220  if (m_use_sfc_fluxes) {
221  // update custom surface fluxes interpolated from file
222  update_sfc_time_index(elapsed_time);
223  sfc_tflux = interpolate_sfc_column(elapsed_time, 2);
224  sfc_qflux = interpolate_sfc_column(elapsed_time, 3);
225  sfc_ustar = interpolate_sfc_column(elapsed_time, 4);
226 
227  amrex::Print() << " ABLMOST: Interpolating SHF and LHF at time "
228  << elapsed_time
229  << ": SHF = " << sfc_tflux
230  << " (W/m^2) LHF = " << sfc_qflux
231  << " (W/m^2) TAU = " << sfc_ustar
232  << " (m^2/s^2)" << std::endl;
233 
234  // overwrite the custom_ustar/tstar/qstar values with the new values and
235  // use the existing pathway to set u*,t*,q* with or without a custom_rhosurf
236  // note - when m_use_sfc_fluxes=true, custom_flux has specified_rho_surf=true,
237  // so there is no rho factor here
238  custom_ustar = std::sqrt(sfc_ustar); // convert tau from file to u*
241  }
242 
243  if (custom_rhosurf > 0) {
244  specified_rho_surf = true;
245  u_star[lev]->setVal(std::sqrt(custom_rhosurf) * custom_ustar);
246  t_star[lev]->setVal(custom_rhosurf * custom_tstar);
247  q_star[lev]->setVal(custom_rhosurf * custom_qstar);
248  } else {
249  u_star[lev]->setVal(custom_ustar);
250  t_star[lev]->setVal(custom_tstar);
251  q_star[lev]->setVal(custom_qstar);
252  }
253  }
254 
255  if (m_update_k_rans) {
256  const bool use_ref_theta = (theta_ref > 0);
257  const Real l_inv_theta0 = (use_ref_theta) ? one / theta_ref : one;
258  const Real l_inv_Cmu2 = inv_Cmu2;
259  const int klo = m_geom[lev].Domain().smallEnd(2);
260  IntVect ng = u_star[lev]->nGrowVect(); ng[2] = 0;
261 
262  for (MFIter mfi(cons_in); mfi.isValid(); ++mfi)
263  {
264  Box gpbx = mfi.tilebox(IntVect(0),ng);
265 
266  if (gpbx.smallEnd(2) != klo) { continue; }
267 
268  gpbx.makeSlab(2,klo);
269 
270  auto cons_arr = cons_in.array(mfi);
271  const auto& u_star_arr = u_star[lev]->const_array(mfi);
272  const auto& t_star_arr = t_star[lev]->const_array(mfi);
273  const auto& dist_arr = walldist->const_array(mfi);
274 
275  ParallelFor(gpbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
276  {
277  Real rho = cons_arr(i,j,k,Rho_comp);
278  if (t_star_arr(i,j,0) < -1e-8) {
279  // Only destabilizing buoyancy flux affects the boundary k
280  // tstar < 0 ==> B > 0
281  Real B = -CONST_GRAV * l_inv_theta0 * u_star_arr(i,j,0) * t_star_arr(i,j,0);
282  if (!use_ref_theta) {
283  B *= cons_arr(i,j,k,Rho_comp) /
284  cons_arr(i,j,k,RhoTheta_comp);
285  }
286 
287  // Axell & Liungman 2001, Eqn. 16
288  cons_arr(i,j,k,RhoKE_comp) = rho * l_inv_Cmu2 *
289  std::pow(
290  u_star_arr(i,j,0) * u_star_arr(i,j,0) * u_star_arr(i,j,0)
291  + KAPPA * B * dist_arr(i,j,k),
292  two/three);
293  } else {
294  cons_arr(i,j,k,RhoKE_comp) = rho * l_inv_Cmu2 * u_star_arr(i,j,0) * u_star_arr(i,j,0);
295  }
296  });
297  }
298  }
299 
300  u_star[lev]->FillBoundary(m_geom[lev].periodicity());
301  t_star[lev]->FillBoundary(m_geom[lev].periodicity());
302  q_star[lev]->FillBoundary(m_geom[lev].periodicity());
303  olen[lev]->FillBoundary(m_geom[lev].periodicity());
304 }
constexpr amrex::Real three
Definition: ERF_Constants.H:11
constexpr amrex::Real Cp_d
Definition: ERF_Constants.H:49
constexpr amrex::Real two
Definition: ERF_Constants.H:10
constexpr amrex::Real L_v
Definition: ERF_Constants.H:59
void compute_averages(const int &lev)
Definition: ERF_MOSTAverage.cpp:970
amrex::Real sfc_tflux
Definition: ERF_SurfaceLayer.H:1290
void fill_tsurf_with_sfc_sst(const int &lev, const double &time)
Definition: ERF_SurfaceLayer.cpp:1302
amrex::Real sfc_qflux
Definition: ERF_SurfaceLayer.H:1289
void get_lsm_tsurf(const int &lev)
Definition: ERF_SurfaceLayer.cpp:1387
void fill_qsurf_with_qsat(const int &lev, const amrex::MultiFab &cons_in, const std::unique_ptr< amrex::MultiFab > &z_phys_nd)
Definition: ERF_SurfaceLayer.cpp:1339
void compute_fluxes(const int &lev, const int &max_iters, amrex::MultiFab &cons_in, const FluxIter &most_flux, bool is_land)
amrex::Real sfc_ustar
Definition: ERF_SurfaceLayer.H:1291
void update_surf_temp(const double &time)
Definition: ERF_SurfaceLayer.H:903
void fill_tsurf_with_sst_and_tsk(const int &lev, const double &time)
Definition: ERF_SurfaceLayer.cpp:1215
void fill_tsurf_with_coupled_sst(const int &lev)
Definition: ERF_SurfaceLayer.cpp:1427
void compute_sfc_params_from_lsm_fluxes(const int &lev, amrex::MultiFab &cons_in)
Definition: ERF_SurfaceLayer.cpp:1142
Definition: ERF_MOSTStress.H:226
Definition: ERF_MOSTStress.H:437
EB surface-layer model for adiabatic constant-roughness fluxes.
Definition: ERF_EBMOSTStress.H:14
Definition: ERF_MOSTStress.H:336
Definition: ERF_MOSTStress.H:522
Definition: ERF_MOSTStress.H:162
Definition: ERF_MOSTStress.H:738
Definition: ERF_MOSTStress.H:1003
EB surface-layer model with prescribed surface fluxes and constant roughness.
Definition: ERF_EBMOSTStress.H:207
Definition: ERF_MOSTStress.H:875
Definition: ERF_MOSTStress.H:1127
Definition: ERF_MOSTStress.H:617
Definition: ERF_MOSTStress.H:1414
Definition: ERF_MOSTStress.H:1785
EB surface-layer model with prescribed surface temperature and constant roughness.
Definition: ERF_EBMOSTStress.H:67
Definition: ERF_MOSTStress.H:1604
Definition: ERF_MOSTStress.H:1919
Definition: ERF_MOSTStress.H:1264
Here is the call graph for this function:

◆ update_mac_ptrs()

void SurfaceLayer::update_mac_ptrs ( const int &  lev,
amrex::Vector< amrex::Vector< amrex::MultiFab >> &  vars_old,
amrex::Vector< std::unique_ptr< amrex::MultiFab >> &  Theta_prim,
amrex::Vector< std::unique_ptr< amrex::MultiFab >> &  Qv_prim,
amrex::Vector< std::unique_ptr< amrex::MultiFab >> &  Qr_prim 
)
inline

Update MOST-average field pointers.

Parameters
[in]levlevel index
[in]vars_oldold-time state variables
[in]Theta_primprimitive potential-temperature fields by level
[in]Qv_primprimitive water-vapor fields by level
[in]Qr_primprimitive rain-water fields by level
937  {
938  m_ma.update_field_ptrs(lev, vars_old, Theta_prim, Qv_prim, Qr_prim);
939  }
void update_field_ptrs(const int &lev, amrex::Vector< amrex::Vector< amrex::MultiFab >> &vars_old, amrex::Vector< std::unique_ptr< amrex::MultiFab >> &Theta_prim, amrex::Vector< std::unique_ptr< amrex::MultiFab >> &Qv_prim, amrex::Vector< std::unique_ptr< amrex::MultiFab >> &Qr_prim)
Definition: ERF_MOSTAverage.cpp:308
Here is the call graph for this function:

◆ update_pblh()

void SurfaceLayer::update_pblh ( const int &  lev,
amrex::Vector< amrex::Vector< amrex::MultiFab >> &  vars,
amrex::MultiFab *  z_phys_cc,
const MoistureComponentIndices moisture_indices 
)

Wrapper around compute_pblh.

Parameters
[in]levlevel index
[in,out]varsstate variables used by the PBL-height calculation
[in]z_phys_cccell-centered physical-height field
[in]moisture_indicesindices for moisture components

Update PBL height using the configured estimator.

Parameters
[in]levCurrent level
[in]varsLevel-indexed state MultiFabs passed to the PBL height estimator
[in]z_phys_ccCell-centered physical height used by the PBL height estimator
[in]moisture_indicesMoisture component indices used by the PBL height estimator
1499 {
1501  MYNNPBLH estimator;
1502  compute_pblh(lev, vars, z_phys_cc, estimator, moisture_indices);
1504  amrex::Error("YSU/MRF PBLH calc not implemented yet");
1505  }
1506 }
void compute_pblh(const int &lev, amrex::Vector< amrex::Vector< amrex::MultiFab >> &vars, amrex::MultiFab *z_phys_cc, const PBLHeightEstimator &est, const MoistureComponentIndices &moisture_indice)
Diagnostic utility for the planetary boundary layer height.
Definition: ERF_PBLHeight.H:11

◆ update_sfc_time_index()

void SurfaceLayer::update_sfc_time_index ( const amrex::Real time)

Updates current time index for interpolating data from SFC/SST file.

Parameters
[in]timeelapsed time
308 {
309  if (sfc.empty() || sfc[0].size() < 2) { return; }
310 
311  Real t1 = sfc[0][sfc_time_ind+1];
312  while (elapsed_time >= t1)
313  {
314  int prev_index = sfc_time_ind;
315  sfc_time_ind = std::min(sfc_time_ind + 1, int(sfc[0].size() - 2));
316  t1 = sfc[0][sfc_time_ind+1];
317  if (prev_index == sfc_time_ind) {
318  break;
319  }
320  }
321 }

◆ update_sst_ptr()

void SurfaceLayer::update_sst_ptr ( const int  lev,
const int  itime,
amrex::MultiFab *  sst_ptr 
)
inline

Update one stored sea-surface-temperature pointer.

Parameters
[in]levlevel index
[in]itimetime-slice index
[in]sst_ptrsea-surface-temperature field pointer
1151  {
1152  m_sst_lev[lev][itime] = sst_ptr;
1153  }

◆ update_surf_temp()

void SurfaceLayer::update_surf_temp ( const double &  time)
inline

Update prescribed surface temperature from the configured heating rate.

Parameters
[in]timeelapsed simulation time
904  {
905  // NOTE: this is a whole-domain setVal, so it overwrites the SST/TSK fill
906  // done earlier in update_fluxes. Coupled SST is applied after this
907  // call and therefore still wins on the water cells it covers.
908  if (surf_heating_rate != 0) {
909  // Use the actual size of t_surf, not m_geom.size(), which is always
910  // max_level+1 and so runs past the levels that exist. t_surf is sized for
911  // all levels up front but filled one level at a time, so we also have to
912  // skip the entries that have not been allocated yet.
913  int nlevs = static_cast<int>(t_surf.size());
914  for (int lev = 0; lev < nlevs; lev++) {
915  if (!t_surf[lev]) { continue; }
916  t_surf[lev]->setVal(surf_temp + surf_heating_rate * static_cast<amrex::Real>(time));
917  amrex::Print() << "Surface temp at t=" << time << ": "
918  << surf_temp + surf_heating_rate * time << std::endl;
919  }
920  }
921  }

◆ update_tsk_ptr()

void SurfaceLayer::update_tsk_ptr ( const int  lev,
const int  itime,
amrex::MultiFab *  tsk_ptr 
)
inline

Update one stored skin-temperature pointer.

Parameters
[in]levlevel index
[in]itimetime-slice index
[in]tsk_ptrskin-temperature field pointer
1162  {
1163  m_tsk_lev[lev][itime] = tsk_ptr;
1164  }

Member Data Documentation

◆ cnk_a

amrex::Real SurfaceLayer::cnk_a {amrex::Real(0.0185)}
private

◆ cnk_visc

bool SurfaceLayer::cnk_visc {false}
private

◆ custom_qstar

amrex::Real SurfaceLayer::custom_qstar {0}
private

◆ custom_rhosurf

amrex::Real SurfaceLayer::custom_rhosurf {0}
private

◆ custom_tstar

amrex::Real SurfaceLayer::custom_tstar {0}
private

◆ custom_ustar

amrex::Real SurfaceLayer::custom_ustar {0}
private

◆ default_land_surf_moist

amrex::Real SurfaceLayer::default_land_surf_moist {zero}
private

◆ default_land_surf_temp

amrex::Real SurfaceLayer::default_land_surf_temp {amrex::Real(300.)}
private

◆ depth

amrex::Real SurfaceLayer::depth {amrex::Real(30.0)}
private

◆ flux_type

FluxCalcType SurfaceLayer::flux_type {FluxCalcType::MOENG}

◆ inv_Cmu2

amrex::Real SurfaceLayer::inv_Cmu2 = zero
private

◆ m_Cd

amrex::Real SurfaceLayer::m_Cd = zero
private

◆ m_Ch

amrex::Real SurfaceLayer::m_Ch = zero
private

◆ m_coupled_sst_lev

amrex::Vector<amrex::MultiFab*> SurfaceLayer::m_coupled_sst_lev
private

◆ m_coupled_sst_valid_lev

amrex::Vector<amrex::iMultiFab*> SurfaceLayer::m_coupled_sst_valid_lev
private

◆ m_Cq

amrex::Real SurfaceLayer::m_Cq = zero
private

◆ m_eb_vec

amrex::Vector<const eb_*> SurfaceLayer::m_eb_vec
private

◆ m_eddyDiffs_lev

amrex::Vector<amrex::MultiFab*> SurfaceLayer::m_eddyDiffs_lev
private

◆ m_final_low_time

double SurfaceLayer::m_final_low_time
private

◆ m_geom

amrex::Vector<amrex::Geometry> SurfaceLayer::m_geom
private

◆ m_has_lsm_fluxes

bool SurfaceLayer::m_has_lsm_fluxes = false
private

◆ m_has_lsm_tsurf

bool SurfaceLayer::m_has_lsm_tsurf = false
private

◆ m_Hwave_lev

amrex::Vector<amrex::MultiFab*> SurfaceLayer::m_Hwave_lev
private

◆ m_ignore_sst

bool SurfaceLayer::m_ignore_sst = false
private

◆ m_include_wstar

bool SurfaceLayer::m_include_wstar = false
private

Referenced by computes_w_star().

◆ m_lmask_lev

amrex::Vector<amrex::Vector<amrex::iMultiFab*> > SurfaceLayer::m_lmask_lev
private

◆ m_low_time_interval

double SurfaceLayer::m_low_time_interval
private

◆ m_lsm_data_lev

amrex::Vector<amrex::Vector<amrex::MultiFab*> > SurfaceLayer::m_lsm_data_lev
private

◆ m_lsm_data_name

amrex::Vector<std::string> SurfaceLayer::m_lsm_data_name
private

◆ m_lsm_flux_lev

amrex::Vector<amrex::Vector<amrex::MultiFab*> > SurfaceLayer::m_lsm_flux_lev
private

◆ m_lsm_flux_name

amrex::Vector<std::string> SurfaceLayer::m_lsm_flux_name
private

◆ m_lsm_tsurf_indx

int SurfaceLayer::m_lsm_tsurf_indx = -1
private

◆ m_Lwave_lev

amrex::Vector<amrex::MultiFab*> SurfaceLayer::m_Lwave_lev
private

◆ m_ma

◆ m_rotate

bool SurfaceLayer::m_rotate = false
private

◆ m_sst_lev

amrex::Vector<amrex::Vector<amrex::MultiFab*> > SurfaceLayer::m_sst_lev
private

◆ m_start_low_time

double SurfaceLayer::m_start_low_time
private

◆ m_terrain_type

TerrainType SurfaceLayer::m_terrain_type
private

◆ m_tsk_lev

amrex::Vector<amrex::Vector<amrex::MultiFab*> > SurfaceLayer::m_tsk_lev
private

◆ m_update_k_rans

bool SurfaceLayer::m_update_k_rans = false
private

◆ m_use_coupled_sst

bool SurfaceLayer::m_use_coupled_sst = false
private

◆ m_use_sfc_fluxes

bool SurfaceLayer::m_use_sfc_fluxes = false
private

◆ m_use_sfc_sst

bool SurfaceLayer::m_use_sfc_sst = false
private

◆ m_var_z0

bool SurfaceLayer::m_var_z0 {false}
private

◆ moist_type

MoistCalcType SurfaceLayer::moist_type {MoistCalcType::ADIABATIC}

◆ olen

amrex::Vector<std::unique_ptr<amrex::MultiFab> > SurfaceLayer::olen
private

◆ pblh

amrex::Vector<std::unique_ptr<amrex::MultiFab> > SurfaceLayer::pblh
private

◆ pblh_type

PBLHeightCalcType SurfaceLayer::pblh_type {PBLHeightCalcType::None}

Referenced by computes_pblh().

◆ q_star

amrex::Vector<std::unique_ptr<amrex::MultiFab> > SurfaceLayer::q_star
private

◆ q_surf

amrex::Vector<std::unique_ptr<amrex::MultiFab> > SurfaceLayer::q_surf
private

◆ rico_qsat_z0

amrex::Real SurfaceLayer::rico_qsat_z0 {amrex::Real(0.001)}
private

◆ rico_theta_z0

amrex::Real SurfaceLayer::rico_theta_z0 {amrex::Real(298.0)}
private

◆ rough_type_land

RoughCalcType SurfaceLayer::rough_type_land {RoughCalcType::CONSTANT}

◆ rough_type_sea

RoughCalcType SurfaceLayer::rough_type_sea {RoughCalcType::CHARNOCK}

◆ sfc

amrex::Vector<amrex::Vector<amrex::Real> > SurfaceLayer::sfc
private

◆ sfc_qflux

amrex::Real SurfaceLayer::sfc_qflux = zero
private

◆ sfc_tflux

amrex::Real SurfaceLayer::sfc_tflux = zero
private

◆ sfc_time_ind

int SurfaceLayer::sfc_time_ind = 0
private

◆ sfc_ustar

amrex::Real SurfaceLayer::sfc_ustar = zero
private

◆ specified_rho_surf

bool SurfaceLayer::specified_rho_surf {false}
private

◆ surf_heating_rate

amrex::Real SurfaceLayer::surf_heating_rate {0}
private

◆ surf_moist

amrex::Real SurfaceLayer::surf_moist {amrex::Real(-1.)}
private

◆ surf_moist_flux

amrex::Real SurfaceLayer::surf_moist_flux {0}
private

◆ surf_temp

amrex::Real SurfaceLayer::surf_temp {amrex::Real(-1.)}
private

Referenced by update_surf_temp().

◆ surf_temp_flux

amrex::Real SurfaceLayer::surf_temp_flux {0}
private

◆ surface_diagnostic_source

amrex::Vector<std::unique_ptr<amrex::MultiFab> > SurfaceLayer::surface_diagnostic_source
private

◆ t_star

amrex::Vector<std::unique_ptr<amrex::MultiFab> > SurfaceLayer::t_star
private

◆ t_surf

amrex::Vector<std::unique_ptr<amrex::MultiFab> > SurfaceLayer::t_surf
private

◆ theta_ref

amrex::Real SurfaceLayer::theta_ref = zero
private

◆ theta_type

ThetaCalcType SurfaceLayer::theta_type {ThetaCalcType::ADIABATIC}

◆ u_star

amrex::Vector<std::unique_ptr<amrex::MultiFab> > SurfaceLayer::u_star
private

◆ use_moisture

bool SurfaceLayer::use_moisture
private

◆ w_star

amrex::Vector<std::unique_ptr<amrex::MultiFab> > SurfaceLayer::w_star
private

◆ z0_const

amrex::Real SurfaceLayer::z0_const {amrex::Real(0.1)}
private

◆ z_0

amrex::Vector<amrex::MultiFab> SurfaceLayer::z_0
private

The documentation for this class was generated from the following files: