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 }
 

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, amrex::Real start_low_time, amrex::Real final_low_time, amrex::Real low_time_interval=zero, 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 amrex::Real &elapsed_time, const amrex::Real &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 amrex::Real &time)
 
void fill_qsurf_with_qsat (const int &lev, const amrex::MultiFab &cons_in, const std::unique_ptr< amrex::MultiFab > &z_phys_nd)
 
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 amrex::Real &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)
 
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)
 
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)
 
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)
 

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
 
amrex::Real m_start_low_time
 
amrex::Real m_final_low_time
 
amrex::Real 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 surf_heating_rate {0}
 
amrex::Real surf_temp_flux {0}
 
amrex::Real default_land_surf_moist {zero}
 
amrex::Real surf_moist
 
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
 
bool m_has_ocean_lsm_tsurf = false
 
int m_lsm_tsurf_indx = -1
 
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::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 
686  {
687  MOENG = 0, ///< Moeng functional form
688  DONELAN, ///< Donelan functional form
689  CUSTOM, ///< Custom constant flux functional form
690  BULK_COEFF, ///< Bulk transfer coefficient functional form
691  ROTATE, ///< Terrain rotation flux functional form
692  RICO
693  };

◆ MoistCalcType

Enumerator
ADIABATIC 
MOISTURE_FLUX 

Qv-flux specified.

SURFACE_MOISTURE 

Surface Qv specified.

701  {
702  ADIABATIC = 0,
703  MOISTURE_FLUX, ///< Qv-flux specified
704  SURFACE_MOISTURE ///< Surface Qv specified
705  };

◆ PBLHeightCalcType

Enumerator
None 
MYNN25 
YSU 
MRF 
715 { None, MYNN25, YSU, MRF };

◆ RoughCalcType

Enumerator
CONSTANT 

Constant z0.

CHARNOCK 
MODIFIED_CHARNOCK 
DONELAN 
WAVE_COUPLED 
707  {
708  CONSTANT = 0, ///< Constant z0
709  CHARNOCK,
710  MODIFIED_CHARNOCK,
711  DONELAN,
712  WAVE_COUPLED
713  };

◆ ThetaCalcType

Enumerator
ADIABATIC 
HEAT_FLUX 

Heat-flux specified.

SURFACE_TEMPERATURE 

Surface temperature specified.

695  {
696  ADIABATIC = 0,
697  HEAT_FLUX, ///< Heat-flux specified
698  SURFACE_TEMPERATURE ///< Surface temperature specified
699  };

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,
amrex::Real  start_low_time,
amrex::Real  final_low_time,
amrex::Real  low_time_interval = zero,
const amrex::Vector< const eb_ * > &  eb_vec = {} 
)
inlineexplicit
49  {})
50  : m_geom(geom),
51  m_rotate(use_rot_surface_flux),
52  m_start_low_time(start_low_time),
53  m_final_low_time(final_low_time),
54  m_low_time_interval(low_time_interval),
55  m_eb_vec(eb_vec),
56  m_terrain_type(a_terrain_type),
57  m_ma(geom, (z_phys_nd[0] != nullptr), a_pp_prefix, a_mesh_type, a_terrain_type, eb_vec)
58  {
59  // We have a moisture model if Qv_prim is a valid pointer
60  use_moisture = (Qv_prim[0].get());
61 
62  // Get roughness
63  amrex::ParmParse pp("erf");
64  pp.query("most.z0", z0_const);
65 
66  // Specify how to compute the flux
67  if (use_rot_surface_flux) {
69  } else {
70  std::string flux_string_in;
71  std::string flux_string{"moeng"};
72  auto read_flux = pp.query("surface_layer.flux_type", flux_string_in);
73  if (read_flux) {
74  flux_string = amrex::toLower(flux_string_in);
75  }
76  if (flux_string == "donelan") {
78  } else if (flux_string == "moeng") {
80  } else if (flux_string == "rico") {
82  } else if (flux_string == "bulk_coeff") {
84  } else if (flux_string == "custom") {
86  } else {
87  amrex::Abort("Undefined MOST flux type!");
88  }
89  }
90 
91  // Include w* to handle free convection (Beljaars 1995, QJRMS)
92  pp.query("most.include_wstar", m_include_wstar);
93 
94  std::string pblh_string_in;
95  std::string pblh_string{"none"};
96  auto read_pblh = pp.query("most.pblh_calc", pblh_string_in);
97  if (read_pblh) {
98  pblh_string = amrex::toLower(pblh_string_in);
99  }
100  if (pblh_string == "none") {
102  } else if (pblh_string == "mynn25") {
104  } else if (pblh_string == "mynnedmf") {
106  } else if (pblh_string == "ysu") {
108  } else if (pblh_string == "mrf") {
110  } else {
111  amrex::Abort("Undefined PBLH calc type!");
112  }
113 
114  // Get surface temperature
115  auto erf_st = pp.query("most.surf_temp", surf_temp);
116  if (erf_st) { default_land_surf_temp = surf_temp; }
117 
118  // Get surface moisture
119  bool erf_sq = false;
120  if (use_moisture) { erf_sq = pp.query("most.surf_moist", surf_moist); }
121  if (erf_sq) { default_land_surf_moist = surf_moist; }
122 
123  // Custom type user must specify the fluxes
128  pp.get("most.ustar", custom_ustar);
129  pp.get("most.tstar", custom_tstar);
130  pp.get("most.qstar", custom_qstar);
131  pp.query("most.rhosurf", custom_rhosurf);
132  if (custom_qstar != 0) {
134  "Specified custom MOST qv flux without moisture model!");
135  }
136  amrex::Print() << "Using specified ustar, tstar, qstar for MOST = "
137  << custom_ustar << " " << custom_tstar << " "
138  << custom_qstar << std::endl;
139 
140  // Bulk transfer coefficient (must specify coeffs and surface values)
141  } else if (flux_type == FluxCalcType::BULK_COEFF) {
142  pp.get("most.Cd", m_Cd);
143  pp.get("most.Ch", m_Ch);
144  pp.get("most.Cq", m_Cq);
145  pp.get("most.surf_temp", default_land_surf_temp);
146  pp.get("most.surf_moist", default_land_surf_moist);
147  amrex::Print() << "Using specified Cd, Ch, Cq for MOST = "
148  << m_Cd << " " << m_Ch << " "
149  << m_Cq << std::endl;
150 
151  // Specify surface temperature/moisture or surface flux
152  } else {
153  if (erf_st) {
155  pp.query("most.surf_heating_rate", surf_heating_rate); // [K/h]
156 
157  // Modify rate to be in units of K / s rather than K / hr
158  surf_heating_rate /= amrex::Real(3600.0); // [K/s]
159 
160  if (pp.query("most.surf_temp_flux", surf_temp_flux)) {
161  amrex::Abort("Can only specify one of surf_temp_flux or surf_heating_rate");
162  }
163  } else {
164  pp.query("most.surf_temp_flux", surf_temp_flux);
165 
166  if (pp.query("most.surf_heating_rate", surf_heating_rate)) {
167  amrex::Abort("Can only specify one of surf_temp_flux or surf_heating_rate");
168  }
169  if (std::abs(surf_temp_flux) >
172  } else {
174  }
175  }
176 
177  if (erf_sq) {
179  } else {
180  pp.query("most.surf_moist_flux", surf_moist_flux);
181  if (std::abs(surf_moist_flux) >
184  } else {
186  }
187  }
188  }
189 
191  {
192  pp.query("most.rico.theta_z0", rico_theta_z0);
193  pp.query("most.rico.qsat_z0", rico_qsat_z0);
194  }
195 
196  // Make sure the inputs file doesn't try to use most.roughness_type
197  std::string bogus_input;
198  if (pp.query("most.roughness_type", bogus_input) > 0) {
199  amrex::Abort("most.roughness_type is deprecated; use "
200  "most.roughness_type_land and/or most.roughness_type_sea");
201  }
202 
203  // Specify how to compute the surface flux over land (if there is any)
204  std::string rough_land_string_in;
205  std::string rough_land_string{"constant"};
206  auto read_rough_land =
207  pp.query("most.roughness_type_land", rough_land_string_in);
208  if (read_rough_land) {
209  rough_land_string = amrex::toLower(rough_land_string_in);
210  }
211  if (rough_land_string == "constant") {
213  } else {
214  amrex::Abort("Undefined MOST roughness type for land!");
215  }
216 
217  // Specify how to compute the surface flux over sea (if there is any)
218  std::string rough_sea_string_in;
219  std::string rough_sea_string{"charnock"};
220  auto read_rough_sea = pp.query("most.roughness_type_sea", rough_sea_string_in);
221  if (read_rough_sea) {
222  rough_sea_string = amrex::toLower(rough_sea_string_in);
223  }
224  if (rough_sea_string == "charnock") {
226  pp.query("most.charnock_constant", cnk_a);
227  pp.query("most.charnock_viscosity", cnk_visc);
228  if (cnk_a > 0) {
229  amrex::Print() << "If there is water, Charnock relation with C_a="
230  << cnk_a << (cnk_visc ? " and viscosity" : "")
231  << " will be used" << std::endl;
232  } else {
233  amrex::Print() << "If there is water, Charnock relation with variable "
234  "Charnock parameter (COARE3.0)"
235  << (cnk_visc ? " and viscosity" : "") << " will be used"
236  << std::endl;
237  }
238  } else if (rough_sea_string == "coare3.0") {
240  amrex::Print() << "If there is water, Charnock relation with variable "
241  "Charnock parameter (COARE3.0)"
242  << (cnk_visc ? " and viscosity" : "") << " will be used"
243  << std::endl;
244  cnk_a = -1;
245  } else if (rough_sea_string == "donelan") {
247  } else if (rough_sea_string == "modified_charnock") {
249  pp.query("most.modified_charnock_depth", depth);
250  } else if (rough_sea_string == "wave_coupled") {
252  } else if (rough_sea_string == "constant") {
254  } else {
255  amrex::Abort("Undefined MOST roughness type for sea!");
256  }
257 
258  // use skin temperature instead of sea-surface temperature
259  // (wrfinput data may have lower resolution SST data)
260  pp.query("most.ignore_sst", m_ignore_sst);
261 
262  // If we're using the RANS k model, then we need to update the dirichlet
263  // BC based on the instantaneous u* and θ*; the turbulence modeling
264  // choices can vary per level but for now, assume that if specified then
265  // all levels are using the same RANS model.
266  m_update_k_rans = (a_turb_choice.rans_type == RANSType::kEqn &&
267  a_turb_choice.dirichlet_k == true);
268  if (m_update_k_rans) {
269  inv_Cmu2 = one / (a_turb_choice.Cmu0 * a_turb_choice.Cmu0);
270  theta_ref = a_turb_choice.theta_ref;
271  }
272 
273  } // 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:718
bool m_include_wstar
Definition: ERF_SurfaceLayer.H:732
bool m_rotate
Definition: ERF_SurfaceLayer.H:727
PBLHeightCalcType pblh_type
Definition: ERF_SurfaceLayer.H:722
bool use_moisture
Definition: ERF_SurfaceLayer.H:755
amrex::Real m_Cq
Definition: ERF_SurfaceLayer.H:763
amrex::Vector< const eb_ * > m_eb_vec
Definition: ERF_SurfaceLayer.H:767
RoughCalcType rough_type_land
Definition: ERF_SurfaceLayer.H:720
amrex::Real z0_const
Definition: ERF_SurfaceLayer.H:733
amrex::Real cnk_a
Definition: ERF_SurfaceLayer.H:746
amrex::Real m_Ch
Definition: ERF_SurfaceLayer.H:762
amrex::Real m_start_low_time
Definition: ERF_SurfaceLayer.H:728
amrex::Real surf_temp
Definition: ERF_SurfaceLayer.H:735
amrex::Real rico_qsat_z0
Definition: ERF_SurfaceLayer.H:753
bool m_update_k_rans
Definition: ERF_SurfaceLayer.H:794
amrex::Real surf_moist_flux
Definition: ERF_SurfaceLayer.H:740
RoughCalcType rough_type_sea
Definition: ERF_SurfaceLayer.H:721
amrex::Real surf_moist
Definition: ERF_SurfaceLayer.H:739
bool m_ignore_sst
Definition: ERF_SurfaceLayer.H:765
amrex::Real custom_qstar
Definition: ERF_SurfaceLayer.H:743
amrex::Real custom_rhosurf
Definition: ERF_SurfaceLayer.H:744
@ 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:748
amrex::Real default_land_surf_moist
Definition: ERF_SurfaceLayer.H:738
amrex::Real rico_theta_z0
Definition: ERF_SurfaceLayer.H:752
amrex::Real surf_temp_flux
Definition: ERF_SurfaceLayer.H:737
amrex::Vector< amrex::Geometry > m_geom
Definition: ERF_SurfaceLayer.H:726
amrex::Real theta_ref
Definition: ERF_SurfaceLayer.H:796
amrex::Real custom_tstar
Definition: ERF_SurfaceLayer.H:742
amrex::Real m_final_low_time
Definition: ERF_SurfaceLayer.H:729
bool cnk_visc
Definition: ERF_SurfaceLayer.H:747
amrex::Real surf_heating_rate
Definition: ERF_SurfaceLayer.H:736
FluxCalcType flux_type
Definition: ERF_SurfaceLayer.H:717
MoistCalcType moist_type
Definition: ERF_SurfaceLayer.H:719
amrex::Real inv_Cmu2
Definition: ERF_SurfaceLayer.H:795
amrex::Real custom_ustar
Definition: ERF_SurfaceLayer.H:741
amrex::Real m_Cd
Definition: ERF_SurfaceLayer.H:761
amrex::Real default_land_surf_temp
Definition: ERF_SurfaceLayer.H:734
@ SURFACE_TEMPERATURE
Surface temperature specified.
@ HEAT_FLUX
Heat-flux specified.
TerrainType m_terrain_type
Definition: ERF_SurfaceLayer.H:768
amrex::Real m_low_time_interval
Definition: ERF_SurfaceLayer.H:730
MOSTAverage m_ma
Definition: ERF_SurfaceLayer.H:769
real(c_double), parameter epsilon
Definition: ERF_module_model_constants.F90:12
RANSType rans_type
Definition: ERF_TurbStruct.H:473
bool dirichlet_k
Definition: ERF_TurbStruct.H:475
amrex::Real Cmu0
Definition: ERF_TurbStruct.H:451
amrex::Real theta_ref
Definition: ERF_TurbStruct.H:462

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_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]most_fluxstructure to iteratively compute ustar and tstar
277 {
278  // Pointers to the computed averages
279  const auto *const tm_ptr = m_ma.get_average(lev,2); // potential temperature
280  const auto *const qvm_ptr = m_ma.get_average(lev,3); // water vapor mixing ratio
281  const auto *const tvm_ptr = m_ma.get_average(lev,4); // virtual potential temperature
282  const auto *const umm_ptr = m_ma.get_average(lev,5); // horizontal velocity magnitude
283  const auto *const zref_ptr = m_ma.get_zref(lev); // reference height
284  const bool l_use_eb = (m_terrain_type == TerrainType::EB);
285 
286  const int klo = m_geom[lev].Domain().smallEnd(2);
287  IntVect ng = u_star[lev]->nGrowVect(); ng[2] = 0;
288 
289  for (MFIter mfi(cons_in); mfi.isValid(); ++mfi)
290  {
291  Box gtbx = mfi.tilebox(IntVect(0),ng);
292 
293  if (!l_use_eb && gtbx.smallEnd(2) != klo) { continue; }
294 
295  if (!l_use_eb) { gtbx.makeSlab(2,klo); }
296 
297  auto u_star_arr = u_star[lev]->array(mfi);
298  auto t_star_arr = t_star[lev]->array(mfi);
299  auto q_star_arr = q_star[lev]->array(mfi);
300  auto t_surf_arr = t_surf[lev]->array(mfi);
301  auto q_surf_arr = q_surf[lev]->array(mfi);
302  auto olen_arr = olen[lev]->array(mfi);
303 
304  const auto tm_arr = tm_ptr->array(mfi);
305  const auto tvm_arr = tvm_ptr->array(mfi);
306  const auto qvm_arr = qvm_ptr->array(mfi);
307  const auto umm_arr = umm_ptr->array(mfi);
308  const auto zref_arr = zref_ptr->array(mfi);
309  const auto z0_arr = z_0[lev].array(mfi);
310 
311  // PBL height if we need to calculate wstar for the Beljaars correction
312  // TODO: can/should we apply this in LES mode?
313  const auto w_star_arr = (m_include_wstar) ? w_star[lev].get()->array(mfi) : Array4<Real> {};
314  const auto pblh_arr = (m_include_wstar) ? pblh[lev].get()->array(mfi) : Array4<Real> {};
315 
316  // Wave properties if they exist
317  const auto Hwave_arr = (m_Hwave_lev[lev]) ? m_Hwave_lev[lev]->array(mfi) : Array4<Real> {};
318  const auto Lwave_arr = (m_Lwave_lev[lev]) ? m_Lwave_lev[lev]->array(mfi) : Array4<Real> {};
319  const auto eta_arr = (m_eddyDiffs_lev[lev]) ? m_eddyDiffs_lev[lev]->array(mfi) : Array4<Real> {};
320 
321  // Land mask array if it exists
322  auto lmask_arr = (m_lmask_lev[lev][0]) ? m_lmask_lev[lev][0]->array(mfi) :
323  Array4<int> {};
324 
325  // Get EB flags if needed
326  const auto flag_arr = (l_use_eb) ? m_eb_vec[lev]->get_const_factory()->getMultiEBCellFlagFab()[mfi].const_array() : Array4<const EBCellFlag>{};
327 
328  if (!l_use_eb) {
329  ParallelFor(gtbx, [=] AMREX_GPU_DEVICE(int i, int j, int ) noexcept
330  {
331  if (( is_land && lmask_arr(i,j,0) == 1) ||
332  (!is_land && lmask_arr(i,j,0) == 0))
333  {
334  // NOTE: All 2D MFs so k index is always 0 from ba2d definition
335  most_flux.iterate_flux(i, j, 0, max_iters,
336  zref_arr, // set in most average
337  z0_arr, // updated if(!is_land)
338  umm_arr, tm_arr, tvm_arr, qvm_arr,
339  u_star_arr, // updated
340  w_star_arr, // updated if(m_include_wstar)
341  t_star_arr, q_star_arr, // updated
342  t_surf_arr, q_surf_arr, olen_arr, // updated
343  pblh_arr, // updated if(m_include_wstar)
344  Hwave_arr, Lwave_arr, eta_arr);
345  }
346  });
347  // EB
348  } else {
352  ParallelFor(gtbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
353  {
354  if (( is_land && lmask_arr(i,j,0) == 1) ||
355  (!is_land && lmask_arr(i,j,0) == 0))
356  {
357  if (flag_arr(i,j,k).isSingleValued()) {
358  most_flux.iterate_flux(i, j, k, max_iters,
359  zref_arr, // set in most average
360  z0_arr, // updated if(!is_land)
361  umm_arr, tm_arr, tvm_arr, qvm_arr,
362  u_star_arr, // updated
363  w_star_arr, // updated if(m_include_wstar)
364  t_star_arr, q_star_arr, // updated
365  t_surf_arr, q_surf_arr, olen_arr, // updated
366  pblh_arr, // updated if(m_include_wstar)
367  Hwave_arr, Lwave_arr, eta_arr);
368  }
369  }
370  });
371  } else {
372  amrex::Abort("FluxIter type not supported for EB");
373  }
374  }
375  }
376 }
amrex::Real value
Definition: ERF_HurricaneDiagnostics.H:20
pp get("wavelength", wavelength)
ParallelFor(grown_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:116
const amrex::MultiFab * get_average(const int &lev, const int &comp) const
Definition: ERF_MOSTAverage.H:113
amrex::Vector< amrex::Vector< amrex::iMultiFab * > > m_lmask_lev
Definition: ERF_SurfaceLayer.H:785
amrex::Vector< std::unique_ptr< amrex::MultiFab > > t_surf
Definition: ERF_SurfaceLayer.H:776
amrex::Vector< std::unique_ptr< amrex::MultiFab > > q_star
Definition: ERF_SurfaceLayer.H:773
amrex::Vector< amrex::MultiFab * > m_Lwave_lev
Definition: ERF_SurfaceLayer.H:791
amrex::Vector< amrex::MultiFab > z_0
Definition: ERF_SurfaceLayer.H:749
amrex::Vector< std::unique_ptr< amrex::MultiFab > > w_star
Definition: ERF_SurfaceLayer.H:771
amrex::Vector< std::unique_ptr< amrex::MultiFab > > u_star
Definition: ERF_SurfaceLayer.H:770
amrex::Vector< std::unique_ptr< amrex::MultiFab > > q_surf
Definition: ERF_SurfaceLayer.H:777
amrex::Vector< amrex::MultiFab * > m_Hwave_lev
Definition: ERF_SurfaceLayer.H:790
amrex::Vector< std::unique_ptr< amrex::MultiFab > > t_star
Definition: ERF_SurfaceLayer.H:772
amrex::Vector< amrex::MultiFab * > m_eddyDiffs_lev
Definition: ERF_SurfaceLayer.H:792
amrex::Vector< std::unique_ptr< amrex::MultiFab > > olen
Definition: ERF_SurfaceLayer.H:774
amrex::Vector< std::unique_ptr< amrex::MultiFab > > pblh
Definition: ERF_SurfaceLayer.H:775
@ ng
Definition: ERF_Morrison.H:48
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_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 
)
1197 {
1198  est.compute_pblh(m_geom[lev],z_phys_cc, pblh[lev].get(),
1199  vars[lev][Vars::cons],m_lmask_lev[lev][0],
1200  moisture_indices);
1201 }
@ cons
Definition: ERF_IndexDefines.H:174
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 
)
942 {
944  bool has_moisture = use_moisture;
945  const int klo = m_geom[lev].Domain().smallEnd(2);
946  for (MFIter mfi(cons_in); mfi.isValid(); ++mfi) {
947 
948  Box vbx = mfi.validbox();
949  if (vbx.smallEnd(2) != klo) { continue; }
950  vbx.makeSlab(2,0);
951 
952  // Get CC state
953  const Array4<const Real> cons_arr = cons_in.const_array(mfi);
954 
955  // Get SL params
956  const auto u_star_arr = u_star[lev]->array(mfi);
957  const auto t_star_arr = t_star[lev]->array(mfi);
958  const auto q_star_arr = q_star[lev]->array(mfi);
959  const auto olen_arr = olen[lev]->array(mfi);
960 
961  // Get LSM fluxes
962  auto lmask_arr = (m_lmask_lev[lev][0]) ? m_lmask_lev[lev][0]->array(mfi) :
963  Array4<int> {};
964  auto lsm_t_flux_arr = Array4<Real> {};
965  auto lsm_q_flux_arr = Array4<Real> {};
966  auto lsm_tau13_arr = Array4<Real> {};
967  auto lsm_tau23_arr = Array4<Real> {};
968  for (int n(0); n<m_lsm_flux_lev[lev].size(); ++n) {
969  if (toLower(m_lsm_flux_name[n]) == "t_flux") { lsm_t_flux_arr = m_lsm_flux_lev[lev][n]->array(mfi); }
970  if (toLower(m_lsm_flux_name[n]) == "q_flux") { lsm_q_flux_arr = m_lsm_flux_lev[lev][n]->array(mfi); }
971  if (toLower(m_lsm_flux_name[n]) == "tau13") { lsm_tau13_arr = m_lsm_flux_lev[lev][n]->array(mfi); }
972  if (toLower(m_lsm_flux_name[n]) == "tau23") { lsm_tau23_arr = m_lsm_flux_lev[lev][n]->array(mfi); }
973  }
974 
975  ParallelFor(vbx, [=] AMREX_GPU_DEVICE(int i, int j, int /*k*/) noexcept
976  {
977  int is_land = (lmask_arr) ? lmask_arr(i,j,0) : 1;
978  // Skip cells the LSM did not flux (sea-ice / open water -> the LSM
979  // wrote the lsm_flux_undefined sentinel). They keep the u*/T*/q*/L
980  // from the MOST iteration (computed from the surface temperature).
981  // Using the sentinel here would give u_star ~ sqrt(bogus_large_value) and blow up
982  // the PBL on the next step.
983  if (is_land && lsm_t_flux_arr && lsm_t_flux_arr(i,j,0) < lsm_flux_undefined) {
984  Real rho = cons_arr(i,j,klo,Rho_comp);
985  Real Thd = cons_arr(i,j,klo,RhoTheta_comp) / rho;
986  Real qv = (has_moisture) ? cons_arr(i,j,klo,RhoQ1_comp) / rho : zero;
987  Real Thv = Thd * (one + (R_v/R_d - one)*qv);
988  Real tau = std::sqrt( lsm_tau13_arr(i,j,0)*lsm_tau13_arr(i,j,0)
989  + lsm_tau23_arr(i,j,0)*lsm_tau23_arr(i,j,0) );
990  u_star_arr(i,j,0) = amrex::max(std::sqrt(tau),eps);
991  if (lsm_t_flux_arr(i,j,0)>=zero) {
992  t_star_arr(i,j,0) = amrex::min(-lsm_t_flux_arr(i,j,0) / u_star_arr(i,j,0),-eps);
993  } else {
994  t_star_arr(i,j,0) = amrex::max(-lsm_t_flux_arr(i,j,0) / u_star_arr(i,j,0),eps);
995  }
996  if (lsm_q_flux_arr(i,j,0)>=zero) {
997  q_star_arr(i,j,0) = amrex::min(-lsm_q_flux_arr(i,j,0) / u_star_arr(i,j,0),-eps);
998  } else {
999  q_star_arr(i,j,0) = amrex::max(-lsm_q_flux_arr(i,j,0) / u_star_arr(i,j,0),eps);
1000  }
1001  olen_arr(i,j,0) = ( u_star_arr(i,j,0) * u_star_arr(i,j,0) * Thv ) /
1002  ( KAPPA * CONST_GRAV * t_star_arr(i,j,0) );
1003  }
1004  });
1005  } // mfi
1006 }
constexpr amrex::Real R_v
Definition: ERF_Constants.H:43
constexpr amrex::Real KAPPA
Definition: ERF_Constants.H:52
constexpr amrex::Real zero
Definition: ERF_Constants.H:8
constexpr amrex::Real lsm_flux_undefined
Definition: ERF_Constants.H:35
constexpr amrex::Real CONST_GRAV
Definition: ERF_Constants.H:53
constexpr amrex::Real R_d
Definition: ERF_Constants.H:42
#define Rho_comp
Definition: ERF_IndexDefines.H:36
#define RhoTheta_comp
Definition: ERF_IndexDefines.H:37
#define RhoQ1_comp
Definition: ERF_IndexDefines.H:42
rho
Definition: ERF_InitCustomPert_Bubble.H:106
amrex::Vector< amrex::Vector< amrex::MultiFab * > > m_lsm_flux_lev
Definition: ERF_SurfaceLayer.H:787
amrex::Vector< std::string > m_lsm_flux_name
Definition: ERF_SurfaceLayer.H:789
@ qv
Definition: ERF_Kessler.H:29
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_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,out]mfsMultiFabs to populate
[in]eddyDiffsDiffusion coefficients from turbulence model
[in]flux_compstructure to compute fluxes
489 {
490  bool rotate = m_rotate;
491  const int klo = m_geom[lev].Domain().smallEnd(2);
492  const auto& dxInv = m_geom[lev].InvCellSizeArray();
493  for (MFIter mfi(*mfs[0]); mfi.isValid(); ++mfi)
494  {
495  // Get field arrays
496  const auto cons_arr = mfs[Vars::cons]->array(mfi);
497  const auto velx_arr = mfs[Vars::xvel]->array(mfi);
498  const auto vely_arr = mfs[Vars::yvel]->array(mfi);
499  const auto velz_arr = mfs[Vars::zvel]->array(mfi);
500 
501  // Diffusive stress vars
502  auto t13_arr = Tau_lev[TauType::tau13]->array(mfi);
503  auto t31_arr = (Tau_lev[TauType::tau31]) ? Tau_lev[TauType::tau31]->array(mfi) : Array4<Real>{};
504 
505  auto t23_arr = Tau_lev[TauType::tau23]->array(mfi);
506  auto t32_arr = (Tau_lev[TauType::tau32]) ? Tau_lev[TauType::tau32]->array(mfi) : Array4<Real>{};
507 
508  auto hfx3_arr = zheat_flux->array(mfi);
509  auto qfx3_arr = (zqv_flux) ? zqv_flux->array(mfi) : Array4<Real>{};
510 
511  // Rotated stress vars
512  auto t11_arr = (m_rotate) ? Tau_lev[TauType::tau11]->array(mfi) : Array4<Real>{};
513  auto t22_arr = (m_rotate) ? Tau_lev[TauType::tau22]->array(mfi) : Array4<Real>{};
514  auto t33_arr = (m_rotate) ? Tau_lev[TauType::tau33]->array(mfi) : Array4<Real>{};
515  auto t12_arr = (m_rotate) ? Tau_lev[TauType::tau12]->array(mfi) : Array4<Real>{};
516  auto t21_arr = (m_rotate) ? Tau_lev[TauType::tau21]->array(mfi) : Array4<Real>{};
517 
518  auto hfx1_arr = (m_rotate) ? xheat_flux->array(mfi) : Array4<Real>{};
519  auto hfx2_arr = (m_rotate) ? yheat_flux->array(mfi) : Array4<Real>{};
520  auto qfx1_arr = (m_rotate && xqv_flux) ? xqv_flux->array(mfi) : Array4<Real>{};
521  auto qfx2_arr = (m_rotate && yqv_flux) ? yqv_flux->array(mfi) : Array4<Real>{};
522 
523  // Terrain
524  const auto zphys_arr = (z_phys) ? z_phys->const_array(mfi) : Array4<const Real>{};
525 
526  // Get average arrays
527  const auto *const u_mean = m_ma.get_average(lev,0);
528  const auto *const v_mean = m_ma.get_average(lev,1);
529  const auto *const t_mean = m_ma.get_average(lev,2);
530  const auto *const q_mean = m_ma.get_average(lev,3);
531  const auto *const u_mag_mean = m_ma.get_average(lev,5);
532 
533  const auto um_arr = u_mean->array(mfi);
534  const auto vm_arr = v_mean->array(mfi);
535  const auto tm_arr = t_mean->array(mfi);
536  const auto qm_arr = q_mean->array(mfi);
537  const auto umm_arr = u_mag_mean->array(mfi);
538 
539  // Get derived arrays
540  const auto u_star_arr = u_star[lev]->array(mfi);
541  const auto t_star_arr = t_star[lev]->array(mfi);
542  const auto q_star_arr = q_star[lev]->array(mfi);
543  const auto t_surf_arr = t_surf[lev]->array(mfi);
544  const auto q_surf_arr = q_surf[lev]->array(mfi);
545  auto surface_source_arr = surface_diagnostic_source[lev]->array(mfi);
546 
547  // Get LSM fluxes
548  auto lmask_arr = (m_lmask_lev[lev][0]) ? m_lmask_lev[lev][0]->array(mfi) :
549  Array4<int> {};
550  auto lsm_t_flux_arr = Array4<Real> {};
551  auto lsm_q_flux_arr = Array4<Real> {};
552  auto lsm_tau13_arr = Array4<Real> {};
553  auto lsm_tau23_arr = Array4<Real> {};
554  for (int n(0); n<m_lsm_flux_lev[lev].size(); ++n) {
555  if (toLower(m_lsm_flux_name[n]) == "t_flux") { lsm_t_flux_arr = m_lsm_flux_lev[lev][n]->array(mfi); }
556  if (toLower(m_lsm_flux_name[n]) == "q_flux") { lsm_q_flux_arr = m_lsm_flux_lev[lev][n]->array(mfi); }
557  if (toLower(m_lsm_flux_name[n]) == "tau13") { lsm_tau13_arr = m_lsm_flux_lev[lev][n]->array(mfi); }
558  if (toLower(m_lsm_flux_name[n]) == "tau23") { lsm_tau23_arr = m_lsm_flux_lev[lev][n]->array(mfi); }
559  }
560 
561  const bool has_lsm_t_flux = static_cast<bool>(lsm_t_flux_arr);
562  const bool is_custom = (flux_type == FluxCalcType::CUSTOM);
563  const bool is_rico = (flux_type == FluxCalcType::RICO);
564 
565 
566  // Rho*Theta flux
567  //============================================================================
568  Box bx = mfi.tilebox();
569  if (bx.smallEnd(2) != klo) { continue; }
570  bx.makeSlab(2,klo);
571  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k)
572  {
573  // Valid theta flux from LSM and over land. The LSM writes the
574  // lsm_flux_undefined sentinel for cells it did not process (sea-ice /
575  // open water); fall back to MOST there instead of applying garbage.
576  Real Tflux;
577  int is_land = (lmask_arr) ? lmask_arr(i,j,0) : 1;
578  const bool lsm_flux_is_valid = has_lsm_t_flux &&
579  lsm_t_flux_arr(i,j,0) < lsm_flux_undefined;
580  if (has_lsm_t_flux && is_land && lsm_flux_is_valid) {
581  const Real rho = cons_arr(i,j,k,Rho_comp);
582  // LSM flux MultiFabs store kinematic fluxes for MOST parameter
583  // updates. The applied hfx array stores the conservative RHS flux.
584  Tflux = rho * lsm_t_flux_arr(i,j,0);
585  } else {
586  Tflux = flux_comp.compute_t_flux(i, j, k,
587  cons_arr, velx_arr, vely_arr,
588  umm_arr, tm_arr, u_star_arr,
589  t_star_arr, t_surf_arr);
590  }
591 
592  surface_source_arr(i,j,0) = surface_diagnostics::to_plot_value(
594  is_custom, is_rico, is_land, has_lsm_t_flux, lsm_flux_is_valid));
595 
596  // Do scalar flux rotations?
597  if (rotate) {
598  rotate_scalar_flux(i, j, k, Tflux, dxInv, zphys_arr,
599  hfx1_arr, hfx2_arr, hfx3_arr);
600  } else {
601  hfx3_arr(i,j,klo) = Tflux;
602  }
603  });
604 
605  // Rho*Qv flux
606  //============================================================================
607  if (use_moisture) {
608  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k)
609  {
610  // Valid qv flux from LSM and over land (sentinel -> fall back to MOST)
611  Real Qflux;
612  int is_land = (lmask_arr) ? lmask_arr(i,j,0) : 1;
613  if (lsm_q_flux_arr && is_land && lsm_q_flux_arr(i,j,0) < lsm_flux_undefined) {
614  const Real rho = cons_arr(i,j,k,Rho_comp);
615  // LSM flux MultiFabs store kinematic fluxes for MOST parameter
616  // updates. The applied qfx array stores the conservative RHS flux.
617  Qflux = rho * lsm_q_flux_arr(i,j,0);
618  } else {
619  Qflux = flux_comp.compute_q_flux(i, j, k,
620  cons_arr, velx_arr, vely_arr,
621  umm_arr, qm_arr, u_star_arr,
622  q_star_arr, q_surf_arr);
623  }
624 
625  // Do scalar flux rotations?
626  if (rotate) {
627  rotate_scalar_flux(i, j, k, Qflux, dxInv, zphys_arr,
628  qfx1_arr, qfx2_arr, qfx3_arr);
629  } else {
630  qfx3_arr(i,j,k) = Qflux;
631  }
632  });
633  } // custom
634 
635  if (!rotate) {
636  // Rho*u flux
637  //============================================================================
638  Box bxx = surroundingNodes(bx,0);
639  ParallelFor(bxx, [=] AMREX_GPU_DEVICE (int i, int j, int k)
640  {
641  // Valid tau13 from LSM and over land. A side that is land but
642  // whose LSM flux is the sentinel (sea-ice / open water) is treated
643  // as non-LSM so that side uses the MOST stress instead.
644  Real stressx;
645  int is_land_hi = ((lmask_arr) ? lmask_arr(i ,j,0) : 1)
646  && (!lsm_tau13_arr || lsm_tau13_arr(i ,j,0) < lsm_flux_undefined);
647  int is_land_lo = ((lmask_arr) ? lmask_arr(i-1,j,0) : 1)
648  && (!lsm_tau13_arr || lsm_tau13_arr(i-1,j,0) < lsm_flux_undefined);
649  if (lsm_tau13_arr && (is_land_hi || is_land_lo)) {
650  stressx = zero;
651  if (!is_land_hi || !is_land_lo) {
652  stressx += myhalf * flux_comp.compute_u_flux(i, j, k,
653  cons_arr, velx_arr, vely_arr,
654  umm_arr, um_arr, u_star_arr);
655  }
656  if (is_land_hi) {
657  const Real rho_hi = cons_arr(i ,j,k,Rho_comp);
658  // LSM tau13 is kinematic for u_star; Tau stores conservative stress.
659  stressx += myhalf * rho_hi * lsm_tau13_arr(i ,j,0);
660  }
661  if (is_land_lo) {
662  const Real rho_lo = cons_arr(i-1,j,k,Rho_comp);
663  // LSM tau13 is kinematic for u_star; Tau stores conservative stress.
664  stressx += myhalf * rho_lo * lsm_tau13_arr(i-1,j,0);
665  }
666  } else {
667  stressx = flux_comp.compute_u_flux(i, j, k,
668  cons_arr, velx_arr, vely_arr,
669  umm_arr, um_arr, u_star_arr);
670  }
671 
672  t13_arr(i,j,k) = stressx;
673  if (t31_arr) { t31_arr(i,j,k) = stressx; }
674  });
675 
676  // Rho*v flux
677  //============================================================================
678  Box bxy = surroundingNodes(bx,1);
679  ParallelFor(bxy, [=] AMREX_GPU_DEVICE (int i, int j, int k)
680  {
681  // Valid tau23 from LSM and over land (sentinel side -> MOST stress)
682  Real stressy;
683  int is_land_hi = ((lmask_arr) ? lmask_arr(i,j ,0) : 1)
684  && (!lsm_tau23_arr || lsm_tau23_arr(i,j ,0) < lsm_flux_undefined);
685  int is_land_lo = ((lmask_arr) ? lmask_arr(i,j-1,0) : 1)
686  && (!lsm_tau23_arr || lsm_tau23_arr(i,j-1,0) < lsm_flux_undefined);
687  if (lsm_tau23_arr && (is_land_hi || is_land_lo)) {
688  stressy = zero;
689  if (!is_land_hi || !is_land_lo) {
690  stressy += myhalf * flux_comp.compute_v_flux(i, j, k,
691  cons_arr, velx_arr, vely_arr,
692  umm_arr, vm_arr, u_star_arr);
693  }
694  if (is_land_hi) {
695  const Real rho_hi = cons_arr(i,j ,k,Rho_comp);
696  // LSM tau23 is kinematic for u_star; Tau stores conservative stress.
697  stressy += myhalf * rho_hi * lsm_tau23_arr(i,j ,0);
698  }
699  if (is_land_lo) {
700  const Real rho_lo = cons_arr(i,j-1,k,Rho_comp);
701  // LSM tau23 is kinematic for u_star; Tau stores conservative stress.
702  stressy += myhalf * rho_lo * lsm_tau23_arr(i,j-1,0);
703  }
704  } else {
705  stressy = flux_comp.compute_v_flux(i, j, k,
706  cons_arr, velx_arr, vely_arr,
707  umm_arr, vm_arr, u_star_arr);
708  }
709 
710  t23_arr(i,j,k) = stressy;
711  if (t32_arr) { t32_arr(i,j,k) = stressy; }
712  });
713  } else {
714  // All fluxes with rotation
715  //============================================================================
716  Box bxxy = convert(bx, IntVect(1,1,0));
717  ParallelFor(bxxy, [=] AMREX_GPU_DEVICE (int i, int j, int k)
718  {
719  Real stresst = flux_comp.compute_u_flux(i, j, k,
720  cons_arr, velx_arr, vely_arr,
721  umm_arr, um_arr, u_star_arr);
722  rotate_stress_tensor(i, j, k, stresst, dxInv, zphys_arr,
723  velx_arr, vely_arr, velz_arr,
724  t11_arr, t22_arr, t33_arr,
725  t12_arr, t21_arr,
726  t13_arr, t31_arr,
727  t23_arr, t32_arr);
728  });
729  }
730  } // mfiter
731 
732  surface_diagnostic_source[lev]->FillBoundary(m_geom[lev].periodicity());
733 }
constexpr amrex::Real myhalf
Definition: ERF_Constants.H:13
@ tau12
Definition: ERF_DataStruct.H:32
@ tau23
Definition: ERF_DataStruct.H:32
@ tau33
Definition: ERF_DataStruct.H:32
@ tau22
Definition: ERF_DataStruct.H:32
@ tau11
Definition: ERF_DataStruct.H:32
@ tau32
Definition: ERF_DataStruct.H:32
@ tau31
Definition: ERF_DataStruct.H:32
@ tau21
Definition: ERF_DataStruct.H:32
@ tau13
Definition: ERF_DataStruct.H:32
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:611
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:632
amrex::Vector< std::unique_ptr< amrex::MultiFab > > surface_diagnostic_source
Definition: ERF_SurfaceLayer.H:781
@ xvel
Definition: ERF_IndexDefines.H:175
@ zvel
Definition: ERF_IndexDefines.H:177
@ yvel
Definition: ERF_IndexDefines.H:176
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real to_plot_value(SurfaceDiagnosticSource source) noexcept
Definition: ERF_SurfaceDiagnosticSource.H:35
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:42
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_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,out]mfsMultiFabs to populate
[in]eddyDiffsDiffusion coefficients from turbulence model
[in]flux_compstructure to compute fluxes
755 {
756  // Get EB flags for all centerings
757  const auto& cc_flags = m_eb_vec[lev]->get_const_factory()->getMultiEBCellFlagFab();
758  const auto& u_flags = m_eb_vec[lev]->get_u_const_factory()->getMultiEBCellFlagFab();
759  const auto& v_flags = m_eb_vec[lev]->get_v_const_factory()->getMultiEBCellFlagFab();
760  const auto& w_flags = m_eb_vec[lev]->get_w_const_factory()->getMultiEBCellFlagFab();
761 
762  const auto& cc_vfrac = m_eb_vec[lev]->get_const_factory()->getVolFrac();
763  const auto& u_vfrac = m_eb_vec[lev]->get_u_const_factory()->getVolFrac();
764  const auto& v_vfrac = m_eb_vec[lev]->get_v_const_factory()->getVolFrac();
765  const auto& w_vfrac = m_eb_vec[lev]->get_w_const_factory()->getVolFrac();
766  const auto& cc_bnorm = m_eb_vec[lev]->get_const_factory()->getBndryNormal();
767  const auto& u_bnorm = m_eb_vec[lev]->get_u_const_factory()->getBndryNorm();
768  const auto& v_bnorm = m_eb_vec[lev]->get_v_const_factory()->getBndryNorm();
769  const auto& w_bnorm = m_eb_vec[lev]->get_w_const_factory()->getBndryNorm();
770 
771  // EB does not currently have a cell-centered scalar-source classification.
772  // Keep the provenance mask missing rather than inventing face-aware
773  // semantics for the staggered stress path.
774  surface_diagnostic_source[lev]->setVal(
776 
777  for (MFIter mfi(*mfs[0]); mfi.isValid(); ++mfi)
778  {
779  // Get flags for this box (all centerings)
780  const auto& cc_flag = cc_flags[mfi];
781  const auto& u_flag = u_flags[mfi];
782  const auto& v_flag = v_flags[mfi];
783  const auto& w_flag = w_flags[mfi];
784 
785  // Skip boxes that have no cut cells at any centering
786  if (cc_flag.getType() != FabType::singlevalued &&
787  u_flag.getType() != FabType::singlevalued &&
788  v_flag.getType() != FabType::singlevalued &&
789  w_flag.getType() != FabType::singlevalued
790  ) continue;
791 
792  // Get EB flag and volfrac arrays
793  auto const cc_flag_arr = cc_flag.const_array();
794  auto const u_flag_arr = u_flag.const_array();
795  auto const v_flag_arr = v_flag.const_array();
796  auto const w_flag_arr = w_flag.const_array();
797 
798  auto const cc_vfrac_arr = cc_vfrac.const_array(mfi);
799  auto const u_vfrac_arr = u_vfrac.const_array(mfi);
800  auto const v_vfrac_arr = v_vfrac.const_array(mfi);
801  auto const w_vfrac_arr = w_vfrac.const_array(mfi);
802  auto const bnorm_arr = cc_bnorm.const_array(mfi);
803  auto const u_bnorm_arr = u_bnorm.const_array(mfi);
804  auto const v_bnorm_arr = v_bnorm.const_array(mfi);
805  auto const w_bnorm_arr = w_bnorm.const_array(mfi);
806 
807  // Get field arrays
808  const auto cons_arr = mfs[Vars::cons]->array(mfi);
809  const auto velx_arr = mfs[Vars::xvel]->array(mfi);
810  const auto vely_arr = mfs[Vars::yvel]->array(mfi);
811  const auto velz_arr = mfs[Vars::zvel]->array(mfi);
812 
813  // Diffusive stress vars - t13 and t23 components for all grid types
814  auto u_t13_arr = Tau_EB[EBTauType::tau_eb13][EBGridType::xface]->array(mfi);
815  auto v_t13_arr = Tau_EB[EBTauType::tau_eb13][EBGridType::yface]->array(mfi);
816  auto w_t13_arr = Tau_EB[EBTauType::tau_eb13][EBGridType::zface]->array(mfi);
817 
818  auto u_t23_arr = Tau_EB[EBTauType::tau_eb23][EBGridType::xface]->array(mfi);
819  auto v_t23_arr = Tau_EB[EBTauType::tau_eb23][EBGridType::yface]->array(mfi);
820  auto w_t23_arr = Tau_EB[EBTauType::tau_eb23][EBGridType::zface]->array(mfi);
821 
822  auto hfx3_arr = Hfx3_EB->array(mfi);
823 
824  // Get average arrays
825  const auto *const u_mean = m_ma.get_average(lev,0);
826  const auto *const v_mean = m_ma.get_average(lev,1);
827  const auto *const t_mean = m_ma.get_average(lev,2);
828  // const auto *const q_mean = m_ma.get_average(lev,3);
829  const auto *const u_mag_mean = m_ma.get_average(lev,5);
830 
831  const auto um_arr = u_mean->array(mfi);
832  const auto vm_arr = v_mean->array(mfi);
833  const auto tm_arr = t_mean->array(mfi);
834  // const auto qm_arr = q_mean->array(mfi);
835  const auto umm_arr = u_mag_mean->array(mfi);
836 
837  // Get derived arrays
838  const auto u_star_arr = u_star[lev]->array(mfi);
839  const auto t_star_arr = t_star[lev]->array(mfi);
840  const auto t_surf_arr = t_surf[lev]->array(mfi);
841 
842  // Rho*Theta flux
843  //============================================================================
844  Box bx = mfi.tilebox();
845  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k)
846  {
847  if (cc_flag_arr(i,j,k).isSingleValued()) {
848  Real Tflux = flux_comp.compute_t_flux(i, j, k,
849  cons_arr, velx_arr, vely_arr, velz_arr,
850  umm_arr, tm_arr, u_star_arr,
851  t_star_arr, t_surf_arr,
852  u_vfrac_arr, v_vfrac_arr, w_vfrac_arr,
853  bnorm_arr);
854  hfx3_arr(i,j,k) = Tflux;
855  }
856  });
857 
858  // Rho*u flux
859  //============================================================================
860  Box bxx = surroundingNodes(bx,0);
861  Box bxy = surroundingNodes(bx,1);
862  Box bxz = surroundingNodes(bx,2);
863  ParallelFor(bxx, [=] AMREX_GPU_DEVICE (int i, int j, int k)
864  {
865  if (u_flag_arr(i,j,k).isSingleValued()) {
866  Real stressx = flux_comp.compute_u_flux(i, j, k,
867  cons_arr, velx_arr, vely_arr, velz_arr,
868  umm_arr, um_arr, u_star_arr,
869  u_vfrac_arr, v_vfrac_arr, w_vfrac_arr,
870  cc_vfrac_arr, cc_flag_arr,
871  u_bnorm_arr, 0);
872  u_t13_arr(i,j,k) = stressx;
873  }
874  });
875  ParallelFor(bxy, [=] AMREX_GPU_DEVICE (int i, int j, int k)
876  {
877  if (v_flag_arr(i,j,k).isSingleValued()) {
878  Real stressx = flux_comp.compute_u_flux(i, j, k,
879  cons_arr, velx_arr, vely_arr, velz_arr,
880  umm_arr, um_arr, u_star_arr,
881  u_vfrac_arr, v_vfrac_arr, w_vfrac_arr,
882  cc_vfrac_arr, cc_flag_arr,
883  v_bnorm_arr, 1);
884  v_t13_arr(i,j,k) = stressx;
885  }
886  });
887  ParallelFor(bxz, [=] AMREX_GPU_DEVICE (int i, int j, int k)
888  {
889  if (u_flag_arr(i,j,k).isSingleValued()) {
890  Real stressx = flux_comp.compute_u_flux(i, j, k,
891  cons_arr, velx_arr, vely_arr, velz_arr,
892  umm_arr, um_arr, u_star_arr,
893  u_vfrac_arr, v_vfrac_arr, w_vfrac_arr,
894  cc_vfrac_arr, cc_flag_arr,
895  w_bnorm_arr, 2);
896  w_t13_arr(i,j,k) = stressx;
897  }
898  });
899 
900  // Rho*v flux
901  //============================================================================
902  ParallelFor(bxx, [=] AMREX_GPU_DEVICE (int i, int j, int k)
903  {
904  if (u_flag_arr(i,j,k).isSingleValued()) {
905  Real stressy = flux_comp.compute_v_flux(i, j, k,
906  cons_arr, velx_arr, vely_arr, velz_arr,
907  umm_arr, vm_arr, u_star_arr,
908  u_vfrac_arr, v_vfrac_arr, w_vfrac_arr,
909  cc_vfrac_arr, cc_flag_arr, u_bnorm_arr, 0);
910  u_t23_arr(i,j,k) = stressy;
911  }
912  });
913  ParallelFor(bxy, [=] AMREX_GPU_DEVICE (int i, int j, int k)
914  {
915  if (v_flag_arr(i,j,k).isSingleValued()) {
916  Real stressy = flux_comp.compute_v_flux(i, j, k,
917  cons_arr, velx_arr, vely_arr, velz_arr,
918  umm_arr, vm_arr, u_star_arr,
919  u_vfrac_arr, v_vfrac_arr, w_vfrac_arr,
920  cc_vfrac_arr, cc_flag_arr, v_bnorm_arr, 1);
921  v_t23_arr(i,j,k) = stressy;
922  }
923  });
924  ParallelFor(bxz, [=] AMREX_GPU_DEVICE (int i, int j, int k)
925  {
926  if (w_flag_arr(i,j,k).isSingleValued()) {
927  Real stressy = flux_comp.compute_v_flux(i, j, k,
928  cons_arr, velx_arr, vely_arr, velz_arr,
929  umm_arr, vm_arr, u_star_arr,
930  u_vfrac_arr, v_vfrac_arr, w_vfrac_arr,
931  cc_vfrac_arr, cc_flag_arr, w_bnorm_arr, 2);
932  w_t23_arr(i,j,k) = stressy;
933  }
934  });
935  } // mfiter
936 
937 }
@ tau_eb23
Definition: ERF_EBStruct.H:16
@ tau_eb13
Definition: ERF_EBStruct.H:16
@ yface
Definition: ERF_EBStruct.H:20
@ zface
Definition: ERF_EBStruct.H:20
@ xface
Definition: ERF_EBStruct.H:20
Here is the call graph for this function:

◆ 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 
)
1099 {
1100  // NOTE: We have already tested a moisture model exists
1101 
1102  // Populate q_surf with qsat over water
1103  auto dz = m_geom[lev].CellSize(2);
1104  const int klo = m_geom[lev].Domain().smallEnd(2);
1105  for (MFIter mfi(*q_surf[lev]); mfi.isValid(); ++mfi)
1106  {
1107  Box gtbx = mfi.growntilebox();
1108 
1109  if (gtbx.smallEnd(2) != klo) { continue; }
1110 
1111  auto t_surf_arr = t_surf[lev]->array(mfi);
1112  auto q_surf_arr = q_surf[lev]->array(mfi);
1113  auto lmask_arr = (m_lmask_lev[lev][0]) ? m_lmask_lev[lev][0]->array(mfi) :
1114  Array4<int> {};
1115  const auto cons_arr = cons_in.const_array(mfi);
1116  const auto z_arr = (z_phys_nd) ? z_phys_nd->const_array(mfi) :
1117  Array4<const Real> {};
1118 
1119  ParallelFor(gtbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
1120  {
1121  int is_land = (lmask_arr) ? lmask_arr(i,j,k) : 1;
1122  if (!is_land) {
1123  auto deltaZ = (z_arr) ? Compute_Zrel_AtCellCenter(i,j,k,z_arr) :
1124  myhalf*dz;
1125  auto Rho = cons_arr(i,j,k,Rho_comp);
1126  auto RTh = cons_arr(i,j,k,RhoTheta_comp);
1127  auto Qv = cons_arr(i,j,k,RhoQ1_comp) / Rho;
1128  auto P_cc = getPgivenRTh(RTh, Qv);
1129  P_cc += Rho*CONST_GRAV*deltaZ;
1130  P_cc *= Real(0.01);
1131  erf_qsatw(t_surf_arr(i,j,k), P_cc, q_surf_arr(i,j,k));
1132  }
1133  });
1134  }
1135  q_surf[lev]->FillBoundary(m_geom[lev].periodicity());
1136 }
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:389
@ dz
Definition: ERF_AdvanceWSM6.cpp:104
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 amrex::Real time 
)
1011 {
1012  int n_times_in_sst = static_cast<int>(m_sst_lev[lev].size());
1013 
1015 
1016  int n_time_lo, n_time_hi;
1017  Real alpha;
1018 
1019  if (n_times_in_sst > 1) {
1020  n_time_lo = static_cast<int>( elapsed_time_since_start_low / dT);
1021  alpha = (elapsed_time_since_start_low - n_time_lo * dT) / dT;
1022 
1023  AMREX_ALWAYS_ASSERT( alpha >= zero && alpha <= one);
1024 
1025  n_time_hi = n_time_lo + 1;
1026 
1027  // Do not over run the last sst file
1028  if (m_start_low_time + elapsed_time_since_start_low >= m_final_low_time) {
1029  n_time_lo = static_cast<int>(m_sst_lev[lev].size())-1;
1030  n_time_hi = n_time_lo;
1031  alpha = zero;
1032  }
1033 
1034  AMREX_ALWAYS_ASSERT( (n_time_lo >= 0) && (n_time_hi < m_sst_lev[lev].size()));
1035  } else {
1036  n_time_lo = 0;
1037  n_time_hi = 0;
1038  alpha = one;
1039  }
1040  AMREX_ALWAYS_ASSERT( alpha >= zero && alpha <= one);
1041 
1042  Real oma = one - alpha;
1043 
1044  // Define a default land surface temperature if we don't read in tsk
1046 
1047  bool use_tsk = (m_tsk_lev[lev][0]);
1048  bool ignore_sst = m_ignore_sst;
1049 
1050  const int klo = m_geom[lev].Domain().smallEnd(2);
1051 
1052  // Populate t_surf
1053  for (MFIter mfi(*t_surf[lev]); mfi.isValid(); ++mfi)
1054  {
1055  Box gtbx = mfi.growntilebox();
1056 
1057  if (gtbx.smallEnd(2) != klo) { continue; }
1058 
1059  auto t_surf_arr = t_surf[lev]->array(mfi);
1060 
1061  const auto sst_lo_arr = m_sst_lev[lev][n_time_lo]->const_array(mfi);
1062  const auto sst_hi_arr = m_sst_lev[lev][n_time_hi]->const_array(mfi);
1063 
1064  auto lmask_arr = (m_lmask_lev[lev][0]) ? m_lmask_lev[lev][0]->array(mfi) :
1065  Array4<int> {};
1066 
1067  if (use_tsk) {
1068  const auto tsk_arr = m_tsk_lev[lev][n_time_lo]->const_array(mfi);
1069  ParallelFor(gtbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
1070  {
1071  int is_land = (lmask_arr) ? lmask_arr(i,j,k) : 1;
1072  if (!is_land && !ignore_sst) {
1073  t_surf_arr(i,j,k) = oma * sst_lo_arr(i,j,k)
1074  + alpha * sst_hi_arr(i,j,k);
1075  } else {
1076  t_surf_arr(i,j,k) = tsk_arr(i,j,k);
1077  }
1078  });
1079  } else {
1080  ParallelFor(gtbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
1081  {
1082  int is_land = (lmask_arr) ? lmask_arr(i,j,k) : 1;
1083  if (!is_land) {
1084  t_surf_arr(i,j,k) = oma * sst_lo_arr(i,j,k)
1085  + alpha * sst_hi_arr(i,j,k);
1086  } else {
1087  t_surf_arr(i,j,k) = lst;
1088  }
1089  });
1090  }
1091  }
1092  t_surf[lev]->FillBoundary(m_geom[lev].periodicity());
1093 }
AMREX_ALWAYS_ASSERT(bx.length()[2]==khi+1)
amrex::Vector< amrex::Vector< amrex::MultiFab * > > m_sst_lev
Definition: ERF_SurfaceLayer.H:783
amrex::Vector< amrex::Vector< amrex::MultiFab * > > m_tsk_lev
Definition: ERF_SurfaceLayer.H:784
real(kind=kind_phys), parameter, public alpha
Definition: ERF_module_mp_wsm6.F90:44
Here is the call graph for this function:

◆ get_lmask()

amrex::iMultiFab* SurfaceLayer::get_lmask ( const int &  lev)
inline
656 { return m_lmask_lev[lev][0]; }

◆ get_lsm_tsurf()

void SurfaceLayer::get_lsm_tsurf ( const int &  lev)
1140 {
1141  const int klo = m_geom[lev].Domain().smallEnd(2);
1142  const bool has_sea_tsurf = (m_has_ocean_lsm_tsurf &&
1143  amrex::toLower(m_lsm_data_name[m_lsm_tsurf_indx]) == "t_surf");
1144  for (MFIter mfi(*t_surf[lev]); mfi.isValid(); ++mfi)
1145  {
1146  Box gtbx = mfi.growntilebox();
1147 
1148  if (gtbx.smallEnd(2) != klo) { continue; }
1149 
1150  // NOTE: LSM does not carry lateral ghost cells.
1151  // This copies the valid box into the ghost cells.
1152  // Fillboundary is called after this to pick up the
1153  // interior ghost and periodic directions.
1154  Box vbx = mfi.validbox();
1155  int i_lo = vbx.smallEnd(0); int i_hi = vbx.bigEnd(0);
1156  int j_lo = vbx.smallEnd(1); int j_hi = vbx.bigEnd(1);
1157 
1158  auto t_surf_arr = t_surf[lev]->array(mfi);
1159  auto lmask_arr = (m_lmask_lev[lev][0]) ? m_lmask_lev[lev][0]->array(mfi) :
1160  Array4<int> {};
1161  const auto lsm_arr = m_lsm_data_lev[lev][m_lsm_tsurf_indx]->const_array(mfi);
1162 
1163  ParallelFor(gtbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
1164  {
1165  int is_land = (lmask_arr) ? lmask_arr(i,j,k) : 1;
1166  if ((!has_sea_tsurf && is_land) ||
1167  (has_sea_tsurf && !is_land)) {
1168  int li = amrex::min(amrex::max(i, i_lo), i_hi);
1169  int lj = amrex::min(amrex::max(j, j_lo), j_hi);
1170  t_surf_arr(i,j,k) = lsm_arr(li,lj,k);
1171  }
1172  });
1173  }
1174 }
amrex::Vector< std::string > m_lsm_data_name
Definition: ERF_SurfaceLayer.H:788
int m_lsm_tsurf_indx
Definition: ERF_SurfaceLayer.H:759
amrex::Vector< amrex::Vector< amrex::MultiFab * > > m_lsm_data_lev
Definition: ERF_SurfaceLayer.H:786
bool m_has_ocean_lsm_tsurf
Definition: ERF_SurfaceLayer.H:758
Here is the call graph for this function:

◆ get_mac_avg()

const amrex::MultiFab* SurfaceLayer::get_mac_avg ( const int &  lev,
int  comp 
)
inline
638  {
639  return m_ma.get_average(lev, comp);
640  }
Here is the call graph for this function:

◆ get_olen()

amrex::MultiFab* SurfaceLayer::get_olen ( const int &  lev)
inline
633 { return olen[lev].get(); }

◆ get_pblh()

amrex::MultiFab* SurfaceLayer::get_pblh ( const int &  lev)
inline
635 { return pblh[lev].get(); }

◆ get_q_star()

amrex::MultiFab* SurfaceLayer::get_q_star ( const int &  lev)
inline
631 { return q_star[lev].get(); }

◆ get_q_surf()

amrex::MultiFab* SurfaceLayer::get_q_surf ( const int &  lev)
inline
645 { return q_surf[lev].get(); }

◆ get_surface_diagnostic_source()

amrex::MultiFab* SurfaceLayer::get_surface_diagnostic_source ( const int &  lev)
inline
648 { return surface_diagnostic_source[lev].get(); }

◆ get_t_star()

amrex::MultiFab* SurfaceLayer::get_t_star ( const int &  lev)
inline
629 { return t_star[lev].get(); }

◆ get_t_surf()

amrex::MultiFab* SurfaceLayer::get_t_surf ( const int &  lev)
inline
642 { return t_surf[lev].get(); }

◆ get_u_star()

amrex::MultiFab* SurfaceLayer::get_u_star ( const int &  lev)
inline
625 { return u_star[lev].get(); }

◆ get_w_star()

amrex::MultiFab* SurfaceLayer::get_w_star ( const int &  lev)
inline
627 { return w_star[lev].get(); }

◆ get_z0()

amrex::MultiFab* SurfaceLayer::get_z0 ( const int &  lev)
inline
652 { return &z_0[lev]; }

◆ get_zref()

amrex::Real SurfaceLayer::get_zref ( const int &  lev)
inline
650 { 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
654 { return m_var_z0; }
bool m_var_z0
Definition: ERF_SurfaceLayer.H:750

◆ 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 
)

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

Parameters
[in]levCurrent level
[in,out]mfsMultiFabs to populate
[in]eddyDiffsDiffusion coefficients from turbulence model
397 {
399  moeng_flux flux_comp;
400  compute_SurfaceLayer_bcs(lev, mfs, Tau_lev,
401  xheat_flux, yheat_flux, zheat_flux,
402  xqv_flux, yqv_flux, zqv_flux,
403  z_phys, flux_comp);
404  } else if (flux_type == FluxCalcType::DONELAN) {
405  donelan_flux flux_comp;
406  compute_SurfaceLayer_bcs(lev, mfs, Tau_lev,
407  xheat_flux, yheat_flux, zheat_flux,
408  xqv_flux, yqv_flux, zqv_flux,
409  z_phys, flux_comp);
410  } else if (flux_type == FluxCalcType::ROTATE) {
411  rotate_flux flux_comp;
412  compute_SurfaceLayer_bcs(lev, mfs, Tau_lev,
413  xheat_flux, yheat_flux, zheat_flux,
414  xqv_flux, yqv_flux, zqv_flux,
415  z_phys, flux_comp);
416  } else if (flux_type == FluxCalcType::RICO) {
418  compute_SurfaceLayer_bcs(lev, mfs, Tau_lev,
419  xheat_flux, yheat_flux, zheat_flux,
420  xqv_flux, yqv_flux, zqv_flux,
421  z_phys, flux_comp);
422  } else if (flux_type == FluxCalcType::BULK_COEFF) {
423  bulk_coeff_flux flux_comp(m_Cd, m_Ch, m_Cq);
424  compute_SurfaceLayer_bcs(lev, mfs, Tau_lev,
425  xheat_flux, yheat_flux, zheat_flux,
426  xqv_flux, yqv_flux, zqv_flux,
427  z_phys, flux_comp);
428  } else if (flux_type == FluxCalcType::CUSTOM) {
429  custom_flux flux_comp(specified_rho_surf);
430  compute_SurfaceLayer_bcs(lev, mfs, Tau_lev,
431  xheat_flux, yheat_flux, zheat_flux,
432  xqv_flux, yqv_flux, zqv_flux,
433  z_phys, flux_comp);
434  } else {
435  amrex::Abort("Unknown surface layer flux calculation type");
436  }
437 }
bool specified_rho_surf
Definition: ERF_SurfaceLayer.H:745
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:2221
Definition: ERF_MOSTStress.H:2100
Definition: ERF_MOSTStress.H:1967
Definition: ERF_MOSTStress.H:1800
Definition: ERF_MOSTStress.H:2348
Definition: ERF_MOSTStress.H:2480

◆ 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 
)

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

Parameters
[in]levCurrent level
[in,out]mfsMultiFabs to populate
[in]eddyDiffsDiffusion coefficients from turbulence model
456 {
458  moeng_flux_eb flux_comp;
459  compute_SurfaceLayer_bcs_EB(lev, mfs, Tau_EB,
460  xheat_flux, yheat_flux, Hfx3_EB,
461  xqv_flux, yqv_flux, zqv_flux,
462  flux_comp);
463  } else {
464  amrex::Abort("Not implemented surface layer flux calculation type for EB");
465  }
466 }
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)
Definition: ERF_EBMOSTStress.H:300

◆ 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) 
)
1209 {
1210  Print() << "Initializing TKE from surface layer ustar on level " << lev << std::endl;
1211 
1212  // Handle vertical decomposition by selectively copying into
1213  // a FArrayBox section on each rank. Then doing a reduce real sum
1214  // and broadcasting to each rank. No mask since all CC data
1215  const int klo = m_geom[lev].Domain().smallEnd(2);
1216  Box bx_lo = u_star[lev]->boxArray().minimalBox();
1217  FArrayBox u_star_lo(bx_lo, 1); u_star_lo.setVal<RunOn::Device>(0);
1218  FArrayBox z_surf_lo(bx_lo, 1); z_surf_lo.setVal<RunOn::Device>(0);
1219  Real* ustar_ptr = u_star_lo.dataPtr();
1220  Real* zsurf_ptr = z_surf_lo.dataPtr();
1221  for (MFIter mfi(cons); mfi.isValid(); ++mfi)
1222  {
1223  Box vbx = mfi.validbox();
1224  if (vbx.smallEnd(2) != klo) { continue; }
1225  vbx.makeSlab(2,0);
1226 
1227  auto const& u_star_arr = u_star[lev]->const_array(mfi);
1228  auto u_star_all = u_star_lo.array();
1229 
1230  auto const& z_phys_arr = z_phys_nd->const_array(mfi);
1231  auto z_surf_all = z_surf_lo.array();
1232 
1233  ParallelFor(vbx, [=] AMREX_GPU_DEVICE(int i, int j, int ) noexcept
1234  {
1235  u_star_all(i,j,0) = u_star_arr(i,j,0);
1236  z_surf_all(i,j,0) = fourth * ( z_phys_arr(i ,j ,klo) + z_phys_arr(i+1,j ,klo)
1237  + z_phys_arr(i ,j+1,klo) + z_phys_arr(i+1,j+1,klo) );
1238  });
1239  }
1240  ParallelDescriptor::ReduceRealSum(ustar_ptr, static_cast<int>(bx_lo.numPts()));
1241  ParallelDescriptor::ReduceRealSum(zsurf_ptr, static_cast<int>(bx_lo.numPts()));
1242 
1243  // Now work on all boxes (ustar has been filled above)
1244  constexpr Real small = Real(0.01);
1245  for (MFIter mfi(cons); mfi.isValid(); ++mfi)
1246  {
1247  Box vbx = mfi.validbox();
1248 
1249  auto const& u_star_arr = u_star_lo.const_array();
1250  auto const& z_surf_arr = z_surf_lo.const_array();
1251  auto const& z_phys_arr = z_phys_nd->const_array(mfi);
1252 
1253  auto const& cons_arr = cons.array(mfi);
1254 
1255  ParallelFor(vbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
1256  {
1257  Real rho = cons_arr(i, j, k, Rho_comp);
1258  Real ust = u_star_arr(i, j, 0);
1259  Real tke0 = tkefac * ust * ust; // surface value
1260  Real zagl = Compute_Z_AtCellCenter(i, j, k, z_phys_arr) - z_surf_arr(i,j,0);
1261 
1262  // linearly tapering profile -- following WRF, approximate top of
1263  // PBL as ustar * zscale
1264  cons_arr(i, j, k, RhoKE_comp) = rho * tke0 * std::max(
1265  (ust * zscale - zagl) / (std::max(ust, small) * zscale),
1266  small);
1267  });
1268  }
1269 }
constexpr amrex::Real fourth
Definition: ERF_Constants.H:14
#define RhoKE_comp
Definition: ERF_IndexDefines.H:38
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:361
Here is the call graph for this function:

◆ lmask_min_reduce()

int SurfaceLayer::lmask_min_reduce ( amrex::iMultiFab &  lmask,
const int &  nghost 
)
inline
660  {
661  int lmask_min = amrex::ReduceMin(lmask, nghost, [=] AMREX_GPU_HOST_DEVICE(
662  amrex::Box const& bx, amrex::Array4<int const> const& lm_arr) -> int
663  {
664  int locmin = std::numeric_limits<int>::max();
665  const auto lo = lbound(bx);
666  const auto hi = ubound(bx);
667  for (int j = lo.y; j <= hi.y; ++j) {
668  for (int i = lo.x; i <= hi.x; ++i) {
669  locmin = std::min(locmin, lm_arr(i, j, 0));
670  }
671  }
672  return locmin;
673  });
674 
675  return lmask_min;
676  }

Referenced by make_SurfaceLayer_at_level().

Here is the caller 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
292  {
293  // Update MOST Average
295  Theta_prim, Qv_prim, Qr_prim,
296  z_phys_nd);
297 
298  // Get CC vars
299  amrex::MultiFab& mf = *(mfv[0]);
300 
301  amrex::ParmParse pp("erf");
302 
303  // Do we have a time-varying surface roughness that needs to be saved?
304  if (lev == 0) {
305  const int nghost = 0; // ghost cells not included
306  int lmask_min = lmask_min_reduce(*lmask_lev[0].get(), nghost);
307  amrex::ParallelDescriptor::ReduceIntMin(lmask_min);
308 
309  m_var_z0 = (lmask_min < 1) & (rough_type_sea != RoughCalcType::CONSTANT);
310  if (m_var_z0) {
311  std::string rough_sea_string{"charnock"};
312  pp.query("most.roughness_type_sea", rough_sea_string);
313  amrex::Print() << "Variable sea roughness (type " << rough_sea_string
314  << ")" << std::endl;
315  }
316  }
317 
318  if (m_eddyDiffs_lev.size() < lev+1) {
319  m_Hwave_lev.resize(nlevs);
320  m_Lwave_lev.resize(nlevs);
321  m_eddyDiffs_lev.resize(nlevs);
322 
323  m_lsm_data_lev.resize(nlevs);
324  m_lsm_flux_lev.resize(nlevs);
325 
326  m_sst_lev.resize(nlevs);
327  m_tsk_lev.resize(nlevs);
328  m_lmask_lev.resize(nlevs);
329 
330  // Size the MOST params for all levels
331  z_0.resize(nlevs);
332  u_star.resize(nlevs);
333  w_star.resize(nlevs);
334  t_star.resize(nlevs);
335  q_star.resize(nlevs);
336  t_surf.resize(nlevs);
337  q_surf.resize(nlevs);
338  surface_diagnostic_source.resize(nlevs);
339  olen.resize(nlevs);
340  pblh.resize(nlevs);
341  }
342 
343  // Get pointers to SST,TSK and LANDMASK data
344  int nt_tot_sst = sst_lev.size();
345  m_sst_lev[lev].resize(nt_tot_sst);
346  for (int nt(0); nt < nt_tot_sst; ++nt) {
347  m_sst_lev[lev][nt] = sst_lev[nt].get();
348  }
349  int nt_tot_tsk = static_cast<int>(tsk_lev.size());
350  m_tsk_lev[lev].resize(nt_tot_tsk);
351  for (int nt(0); nt < nt_tot_tsk; ++nt) {
352  m_tsk_lev[lev][nt] = tsk_lev[nt].get();
353  }
354  int nt_tot_lmask = static_cast<int>(lmask_lev.size());
355  m_lmask_lev[lev].resize(nt_tot_lmask);
356  for (int nt(0); nt < nt_tot_lmask; ++nt) {
357  m_lmask_lev[lev][nt] = lmask_lev[nt].get();
358  }
359 
360  // Get pointers to wave data
361  m_Hwave_lev[lev] = Hwave;
362  m_Lwave_lev[lev] = Lwave;
363  m_eddyDiffs_lev[lev] = eddyDiffs;
364 
365  // Get pointers to LSM data and Fluxes
366  int ndata = static_cast<int>(lsm_data.size());
367  int nflux = static_cast<int>(lsm_flux.size());
368  m_lsm_data_name.resize(ndata);
369  m_lsm_data_lev[lev].resize(ndata);
370  m_lsm_flux_name.resize(nflux);
371  m_lsm_flux_lev[lev].resize(nflux);
372  for (int n(0); n < ndata; ++n) {
373  m_lsm_data_name[n] = lsm_data_name[n];
374  m_lsm_data_lev[lev][n] = lsm_data[n];
375  const std::string lc_name = amrex::toLower(lsm_data_name[n]);
376  if (lc_name == "theta" || lc_name == "t_surf") {
377  m_has_lsm_tsurf = true;
378  m_lsm_tsurf_indx = n;
379  m_has_ocean_lsm_tsurf = (lc_name == "t_surf");
380  }
381  }
382  int n_valid_lsm_flux = 0;
383  for (int n(0); n < nflux; ++n) {
384  m_lsm_flux_name[n] = lsm_flux_name[n];
385  m_lsm_flux_lev[lev][n] = lsm_flux[n];
386  if (m_lsm_flux_lev[lev][n]) { ++n_valid_lsm_flux; }
387  }
388  AMREX_ALWAYS_ASSERT((n_valid_lsm_flux==0 || n_valid_lsm_flux>=4));
389  if (n_valid_lsm_flux>=4) { m_has_lsm_fluxes = true; }
390 
391  // Check if there is a user-specified roughness file to be read
392  std::string fname;
393  bool read_z0 = false;
394  if ( (flux_type == FluxCalcType::MOENG) ||
396  int count = pp.countval("most.roughness_file_name");
397  if (count > 1) {
398  AMREX_ALWAYS_ASSERT(count >= lev+1);
399  pp.query("most.roughness_file_name", fname, lev);
400  read_z0 = true;
401  } else if (count == 1) {
402  if (lev == 0) {
403  pp.query("most.roughness_file_name", fname);
404  } else {
405  // we will interpolate from the coarsest level
406  fname = "";
407  }
408  read_z0 = true;
409  }
410  // else use z0_const
411  }
412 
413  // Attributes for MFs and FABs
414  //--------------------------------------------------------
415  // Create a 2D ba for planar terrain, 3D for EB terrain
416  amrex::BoxArray ba = mf.boxArray();
417  amrex::BoxArray ba_flux;
418  amrex::IntVect ng{1,1,0};
419 
420  if (m_terrain_type == TerrainType::EB) {
421  // Use full 3D BoxArray for EB terrain
422  ba_flux = ba;
423  ng = amrex::IntVect{1,1,1}; // Include z ghost cells
424  } else {
425  // Collapse to 2D for planar terrain
426  amrex::BoxList bl2d = ba.boxList();
427  for (auto& b : bl2d) { b.setRange(2,0); }
428  ba_flux = amrex::BoxArray(std::move(bl2d));
429  }
430 
431  const amrex::DistributionMapping& dm = mf.DistributionMap();
432  const int ncomp = 1;
433 
434  // Z0 heights FAB
435  //--------------------------------------------------------
436  z_0[lev].define(ba_flux, dm, ncomp, ng);
437  z_0[lev].setVal(z0_const);
438  if (read_z0) {
439  read_custom_roughness(lev, fname);
440  }
441 
442  // 2D MFs for U*, T*, T_surf
443  //--------------------------------------------------------
444  u_star[lev] = std::make_unique<amrex::MultiFab>(ba_flux, dm, ncomp, ng);
445  u_star[lev]->setVal(bogus_large_value);
446 
447  w_star[lev] = std::make_unique<amrex::MultiFab>(ba_flux, dm, ncomp, ng);
448  w_star[lev]->setVal(bogus_large_value);
449 
450  t_star[lev] = std::make_unique<amrex::MultiFab>(ba_flux, dm, ncomp, ng);
451  t_star[lev]->setVal(zero); // default to neutral
452 
453  q_star[lev] = std::make_unique<amrex::MultiFab>(ba_flux, dm, ncomp, ng);
454  q_star[lev]->setVal(zero); // default to dry
455 
456  olen[lev] = std::make_unique<amrex::MultiFab>(ba_flux, dm, ncomp, ng);
457  olen[lev]->setVal(bogus_large_value);
458 
459  pblh[lev] = std::make_unique<amrex::MultiFab>(ba_flux, dm, ncomp, ng);
460  pblh[lev]->setVal(bogus_large_value);
461 
462  t_surf[lev] = std::make_unique<amrex::MultiFab>(ba_flux, dm, ncomp, ng);
463  t_surf[lev]->setVal(default_land_surf_temp);
464 
465  q_surf[lev] = std::make_unique<amrex::MultiFab>(ba_flux, dm, ncomp, ng);
466  q_surf[lev]->setVal(default_land_surf_moist);
467 
468  surface_diagnostic_source[lev] = std::make_unique<amrex::MultiFab>(ba_flux, dm, ncomp, ng);
469  surface_diagnostic_source[lev]->setVal(
471 
472  // TODO: Do we want an enum struct for indexing?
473 
474  bool use_sst = (!m_sst_lev[lev].empty() && m_sst_lev[lev][0]);
475  bool use_tsk = (!m_tsk_lev[lev].empty() && m_tsk_lev[lev][0]);
476  if (use_sst || use_tsk || m_has_lsm_tsurf) {
477  // Valid SST, TSK or LSM data; t_surf set before computing fluxes (avoids
478  // extended lambda capture) Note that land temp will be set from m_tsk_lev
479  // while sea temp will be set from m_sst_lev
481 
482  // Pathways in fill_tsurf_with_sst_and_tsk
483  amrex::Print() << "Using MOST with specified surface temperature ";
484  if (m_has_ocean_lsm_tsurf) {
485  amrex::Print() << "(OceanSurf: t_surf)" << std::endl;
486  } else {
487  // NOTE: SST from the LOW file populates TSK in update_sst_tsk.
488  // So if we have TSK, it contains everything and has been
489  // sanity checked for valid SST values.
490  if (use_tsk) { m_ignore_sst = true; }
491  if (use_tsk) {
492  amrex::Print() << "(land: TSK, ";
493  } else {
494  amrex::Print() << "(land: T0, ";
495  }
496  if (use_tsk && !use_sst) {
497  amrex::Print() << "sea: TSK)" << std::endl;
498  } else {
499  amrex::Print() << "sea: SST)" << std::endl;
501  }
502  }
503  }
504  }
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:90
int lmask_min_reduce(amrex::iMultiFab &lmask, const int &nghost)
Definition: ERF_SurfaceLayer.H:658
bool m_has_lsm_tsurf
Definition: ERF_SurfaceLayer.H:757
bool m_has_lsm_fluxes
Definition: ERF_SurfaceLayer.H:756
void read_custom_roughness(const int &lev, const std::string &fname)
Definition: ERF_SurfaceLayer.cpp:1273
Here is the call graph for this function:

◆ read_custom_roughness()

void SurfaceLayer::read_custom_roughness ( const int &  lev,
const std::string &  fname 
)
1275 {
1276  // Read the file if we have it
1277  if (!fname.empty()) {
1278  // Only the ioproc reads the file
1279  Gpu::HostVector<Real> m_x,m_y,m_z0;
1280  if (ParallelDescriptor::IOProcessor()) {
1281  Print()<<"Reading MOST roughness file at level " << lev << " : " << fname << std::endl;
1282  std::ifstream file(fname);
1283  Real value1,value2,value3;
1284  while(file>>value1>>value2>>value3){
1285  m_x.push_back(value1);
1286  m_y.push_back(value2);
1287  m_z0.push_back(value3);
1288  }
1289  file.close();
1290 
1291  AMREX_ALWAYS_ASSERT(m_x.size() == m_y.size());
1292  AMREX_ALWAYS_ASSERT(m_x.size() == m_z0.size());
1293  }
1294 
1295  // Broadcast the whole domain to every rank
1296  int ioproc = ParallelDescriptor::IOProcessorNumber();
1297  int nnode = static_cast<int>(m_x.size());
1298  ParallelDescriptor::Bcast(&nnode, 1, ioproc);
1299 
1300  if (!ParallelDescriptor::IOProcessor()) {
1301  m_x.resize(nnode);
1302  m_y.resize(nnode);
1303  m_z0.resize(nnode);
1304  }
1305  ParallelDescriptor::Bcast(m_x.data() , nnode, ioproc);
1306  ParallelDescriptor::Bcast(m_y.data() , nnode, ioproc);
1307  ParallelDescriptor::Bcast(m_z0.data(), nnode, ioproc);
1308 
1309  // Copy data to the GPU
1310  Gpu::DeviceVector<Real> d_x(nnode),d_y(nnode),d_z0(nnode);
1311  Gpu::copy(Gpu::hostToDevice, m_x.begin(), m_x.end(), d_x.begin());
1312  Gpu::copy(Gpu::hostToDevice, m_y.begin(), m_y.end(), d_y.begin());
1313  Gpu::copy(Gpu::hostToDevice, m_z0.begin(), m_z0.end(), d_z0.begin());
1314  Real* xp = d_x.data();
1315  Real* yp = d_y.data();
1316  Real* z0p = d_z0.data();
1317 
1318  // Each rank populates it's z_0[lev] MultiFab
1319  const int klo = m_geom[lev].Domain().smallEnd(2);
1320  for (MFIter mfi(z_0[lev]); mfi.isValid(); ++mfi)
1321  {
1322  Box gtbx = mfi.growntilebox();
1323 
1324  if (gtbx.smallEnd(2) != klo) { continue; }
1325 
1326  // Populate z_phys data
1327  Real tol = Real(1.0e-4);
1328  auto dx = m_geom[lev].CellSizeArray();
1329  auto ProbLoArr = m_geom[lev].ProbLoArray();
1330  int ilo = m_geom[lev].Domain().smallEnd(0);
1331  int jlo = m_geom[lev].Domain().smallEnd(1);
1332  int ihi = m_geom[lev].Domain().bigEnd(0);
1333  int jhi = m_geom[lev].Domain().bigEnd(1);
1334 
1335  Array4<Real> const& z0_arr = z_0[lev].array(mfi);
1336  ParallelFor(gtbx, [=] AMREX_GPU_DEVICE (int i, int j, int /*k*/)
1337  {
1338  // Clip indices for ghost-cells
1339  int ii = amrex::min(amrex::max(i,ilo),ihi);
1340  int jj = amrex::min(amrex::max(j,jlo),jhi);
1341 
1342  // Location of nodes
1343  Real x = ProbLoArr[0] + ii * dx[0];
1344  Real y = ProbLoArr[1] + jj * dx[1];
1345  int inode = ii + jj * (ihi-ilo+2); // stride is Nx+1
1346  if (std::sqrt(amrex::Math::powi<2>(x-xp[inode])+amrex::Math::powi<2>(y-yp[inode])) < tol) {
1347  z0_arr(i,j,klo) = z0p[inode];
1348  } else {
1349  // Unexpected list order, do brute force search
1350  Real z0loc = zero;
1351  bool found = false;
1352  for (int n=0; n<nnode; ++n) {
1353  Real delta=std::sqrt(amrex::Math::powi<2>(x-xp[n])+amrex::Math::powi<2>(y-yp[n]));
1354  if (delta < tol) {
1355  found = true;
1356  z0loc = z0p[n];
1357  break;
1358  }
1359  }
1360  AMREX_ASSERT_WITH_MESSAGE(found, "Location read from terrain file does not match the grid!");
1361  amrex::ignore_unused(found);
1362  z0_arr(i,j,klo) = z0loc;
1363  }
1364  });
1365  } // mfi
1366  } else {
1367  AMREX_ALWAYS_ASSERT(lev > 0);
1368 
1369  Print()<<"Interpolating MOST roughness at level " << lev << std::endl;
1370 
1371  // Create a BC mapper that uses FOEXTRAP at domain bndry
1372  Vector<int> bc_lo(3,ERFBCType::foextrap);
1373  Vector<int> bc_hi(3,ERFBCType::foextrap);
1374  Vector<BCRec> bcr; bcr.push_back(BCRec(bc_lo.data(),bc_hi.data()));
1375 
1376  // Create ref ratio
1377  IntVect ratio;
1378  for (int idim = 0; idim < AMREX_SPACEDIM; ++idim) {
1379  ratio[idim] = m_geom[lev].Domain().length(idim) / m_geom[0].Domain().length(idim);
1380  }
1381 
1382  // Create interp object and interpolate from the coarsest grid
1383  MFInterpolater* interp = &mf_cell_cons_interp;
1384  interp->interp(z_0[0] , 0,
1385  z_0[lev], 0,
1386  1, z_0[lev].nGrowVect(),
1387  m_geom[0], m_geom[lev],
1388  m_geom[lev].Domain(),ratio,
1389  bcr, 0);
1390  }
1391 }
@ m_y
Definition: ERF_DataStruct.H:25
@ m_x
Definition: ERF_DataStruct.H:24
const Real dx
Definition: ERF_InitCustomPert_ABL.H:23
@ foextrap
Definition: ERF_IndexDefines.H:242

Referenced by make_SurfaceLayer_at_level().

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

◆ set_q_surf()

void SurfaceLayer::set_q_surf ( const int &  lev,
const amrex::Real  qsurf 
)
inline
646 { q_surf[lev]->setVal(qsurf); }

◆ set_t_surf()

void SurfaceLayer::set_t_surf ( const int &  lev,
const amrex::Real  tsurf 
)
inline
643 { t_surf[lev]->setVal(tsurf); }

◆ update_fluxes()

void SurfaceLayer::update_fluxes ( const int &  lev,
const amrex::Real elapsed_time,
const amrex::Real 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 
)

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

Parameters
[in]levCurrent level
[in]max_itersmaximum iterations to use
19 {
20  // Update with SST/TSK data if we have a valid pointer
21  if (!m_has_ocean_lsm_tsurf &&
22  !m_sst_lev[lev].empty() && m_sst_lev[lev][0]) {
23  fill_tsurf_with_sst_and_tsk(lev, elapsed_time_since_start_low);
24  }
25 
26  // Apply heating rate if needed
29  update_surf_temp(elapsed_time_since_start_low);
30  }
31 
32  // Update qsurf with qsat over sea
33  if (use_moisture) {
34  fill_qsurf_with_qsat(lev, cons_in, z_phys_nd);
35  }
36 
37  // Update land surface temp if we have a valid pointer
38  if (m_has_lsm_tsurf) { get_lsm_tsurf(lev); }
39 
40  // Fill interior ghost cells
41  t_surf[lev]->FillBoundary(m_geom[lev].periodicity());
42 
43  // Compute plane averages for all vars (regardless of flux type)
45 
46  // NOTE: Do iterations to seed variables on the first step (LSM called post step)
47  //*******************************************************************************
48  // Update u*/T*/q*/L over land (iterations or from LSM fluxes)
49  if (m_has_lsm_fluxes && elapsed_time > zero) {
51  } else {
52  // ***************************************************************
53  // Iterate the fluxes if moeng type
54  // First iterate over land -- the only model for surface roughness
55  // over land is RoughCalcType::CONSTANT
56  // ***************************************************************
59  bool is_land = true;
60  // Do we have a constant flux for moisture over land?
61  bool cons_qflux = ( (moist_type == MoistCalcType::MOISTURE_FLUX) ||
63  if (m_terrain_type != TerrainType::EB) {
66  surface_flux most_flux(surf_temp_flux, surf_moist_flux, cons_qflux);
67  compute_fluxes(lev, max_iters, cons_in, most_flux, is_land);
68  } else {
69  amrex::Abort("Unknown value for rough_type_land");
70  }
73  surface_temp most_flux(surf_temp_flux, surf_moist_flux, cons_qflux);
74  compute_fluxes(lev, max_iters, cons_in, most_flux, is_land);
75  } else {
76  amrex::Abort("Unknown value for rough_type_land");
77  }
78  } else if ((theta_type == ThetaCalcType::ADIABATIC) &&
82  compute_fluxes(lev, max_iters, cons_in, most_flux, is_land);
83  } else {
84  amrex::Abort("Unknown value for rough_type_land");
85  }
86  } else {
87  amrex::Abort("Unknown value for theta_type");
88  }
89  // EB
90  } else {
93  surface_flux_eb most_flux(surf_temp_flux, surf_moist_flux, cons_qflux);
94  compute_fluxes(lev, max_iters, cons_in, most_flux, is_land);
95  } else {
96  amrex::Abort("Unknown value for rough_type_land");
97  }
100  surface_temp_eb most_flux(surf_temp_flux, surf_moist_flux, cons_qflux);
101  compute_fluxes(lev, max_iters, cons_in, most_flux, is_land);
102  } else {
103  amrex::Abort("Unknown value for rough_type_land");
104  }
105  } else if ((theta_type == ThetaCalcType::ADIABATIC) &&
109  compute_fluxes(lev, max_iters, cons_in, most_flux, is_land);
110  } else {
111  amrex::Abort("Unknown value for rough_type_land");
112  }
113  } else {
114  amrex::Abort("Unknown value for theta_type");
115  }
116  }
117  } // MOENG -- LAND
118  } // has_lsm_fluxes
119 
120  // ***************************************************************
121  // Iterate the fluxes if moeng type
122  // Next iterate over sea -- the models for surface roughness
123  // over sea are CHARNOCK, DONELAN, MODIFIED_CHARNOCK or WAVE_COUPLED
124  // NOTE: Sea surface fluxes are not supported for EB terrain
125  // ***************************************************************
126  if ((flux_type == FluxCalcType::MOENG ||
128  m_terrain_type != TerrainType::EB) {
129  bool is_land = false;
130  // NOTE: Do not allow default to adiabatic over sea (we have Qvs at surface)
131  // Do we have a constant flux for moisture over sea?
132  bool cons_qflux = (moist_type == MoistCalcType::MOISTURE_FLUX);
136  cnk_a, cnk_visc, cons_qflux);
137  compute_fluxes(lev, max_iters, cons_in, most_flux, is_land);
140  depth, cons_qflux);
141  compute_fluxes(lev, max_iters, cons_in, most_flux, is_land);
142  } else if (rough_type_sea == RoughCalcType::DONELAN) {
144  cons_qflux);
145  compute_fluxes(lev, max_iters, cons_in, most_flux, is_land);
148  cons_qflux);
149  compute_fluxes(lev, max_iters, cons_in, most_flux, is_land);
150  } else {
151  amrex::Abort("Unknown value for rough_type_sea");
152  }
153 
157  cnk_a, cnk_visc, cons_qflux);
158  compute_fluxes(lev, max_iters, cons_in, most_flux, is_land);
161  depth, cons_qflux);
162  compute_fluxes(lev, max_iters, cons_in, most_flux, is_land);
163  } else if (rough_type_sea == RoughCalcType::DONELAN) {
165  cons_qflux);
166  compute_fluxes(lev, max_iters, cons_in, most_flux, is_land);
169  cons_qflux);
170  compute_fluxes(lev, max_iters, cons_in, most_flux, is_land);
171  } else {
172  amrex::Abort("Unknown value for rough_type_sea");
173  }
174 
175  } else if ((theta_type == ThetaCalcType::ADIABATIC) &&
179  cnk_a, cnk_visc);
180  compute_fluxes(lev, max_iters, cons_in, most_flux, is_land);
183  depth);
184  compute_fluxes(lev, max_iters, cons_in, most_flux, is_land);
185  } else if (rough_type_sea == RoughCalcType::DONELAN) {
187  compute_fluxes(lev, max_iters, cons_in, most_flux, is_land);
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  } else {
195  amrex::Abort("Unknown value for theta_type");
196  }
197  } // MOENG -- SEA
198 
200  if (custom_rhosurf > 0) {
201  specified_rho_surf = true;
202  u_star[lev]->setVal(std::sqrt(custom_rhosurf) * custom_ustar);
203  t_star[lev]->setVal(custom_rhosurf * custom_tstar);
204  q_star[lev]->setVal(custom_rhosurf * custom_qstar);
205  } else {
206  u_star[lev]->setVal(custom_ustar);
207  t_star[lev]->setVal(custom_tstar);
208  q_star[lev]->setVal(custom_qstar);
209  }
210  }
211 
212  if (m_update_k_rans) {
213  const bool use_ref_theta = (theta_ref > 0);
215  const Real l_inv_Cmu2 = inv_Cmu2;
216  const int klo = m_geom[lev].Domain().smallEnd(2);
217  IntVect ng = u_star[lev]->nGrowVect(); ng[2] = 0;
218 
219  for (MFIter mfi(cons_in); mfi.isValid(); ++mfi)
220  {
221  Box gpbx = mfi.tilebox(IntVect(0),ng);
222 
223  if (gpbx.smallEnd(2) != klo) { continue; }
224 
225  gpbx.makeSlab(2,klo);
226 
227  auto cons_arr = cons_in.array(mfi);
228  const auto& u_star_arr = u_star[lev]->const_array(mfi);
229  const auto& t_star_arr = t_star[lev]->const_array(mfi);
230  const auto& dist_arr = walldist->const_array(mfi);
231 
232  ParallelFor(gpbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
233  {
234  Real rho = cons_arr(i,j,k,Rho_comp);
235  if (t_star_arr(i,j,0) < -1e-8) {
236  // Only destabilizing buoyancy flux affects the boundary k
237  // tstar < 0 ==> B > 0
238  Real B = -CONST_GRAV * l_inv_theta0 * u_star_arr(i,j,0) * t_star_arr(i,j,0);
239  if (!use_ref_theta) {
240  B *= cons_arr(i,j,k,Rho_comp) /
241  cons_arr(i,j,k,RhoTheta_comp);
242  }
243 
244  // Axell & Liungman 2001, Eqn. 16
245  cons_arr(i,j,k,RhoKE_comp) = rho * l_inv_Cmu2 *
246  std::pow(
247  u_star_arr(i,j,0) * u_star_arr(i,j,0) * u_star_arr(i,j,0)
248  + KAPPA * B * dist_arr(i,j,k),
249  two/three);
250  } else {
251  cons_arr(i,j,k,RhoKE_comp) = rho * l_inv_Cmu2 * u_star_arr(i,j,0) * u_star_arr(i,j,0);
252  }
253  });
254  }
255  }
256 
257  u_star[lev]->FillBoundary(m_geom[lev].periodicity());
258  t_star[lev]->FillBoundary(m_geom[lev].periodicity());
259  q_star[lev]->FillBoundary(m_geom[lev].periodicity());
260  olen[lev]->FillBoundary(m_geom[lev].periodicity());
261 }
constexpr amrex::Real three
Definition: ERF_Constants.H:11
constexpr amrex::Real two
Definition: ERF_Constants.H:10
const bool use_ref_theta
Definition: ERF_SetupDiff.H:16
const Real l_inv_theta0
Definition: ERF_SetupDiff.H:17
void compute_averages(const int &lev)
Definition: ERF_MOSTAverage.cpp:900
void update_surf_temp(const amrex::Real &time)
Definition: ERF_SurfaceLayer.H:601
void get_lsm_tsurf(const int &lev)
Definition: ERF_SurfaceLayer.cpp:1139
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:1096
void compute_fluxes(const int &lev, const int &max_iters, amrex::MultiFab &cons_in, const FluxIter &most_flux, bool is_land)
void fill_tsurf_with_sst_and_tsk(const int &lev, const amrex::Real &time)
Definition: ERF_SurfaceLayer.cpp:1009
void compute_sfc_params_from_lsm_fluxes(const int &lev, amrex::MultiFab &cons_in)
Definition: ERF_SurfaceLayer.cpp:940
Definition: ERF_MOSTStress.H:189
Definition: ERF_MOSTStress.H:367
Definition: ERF_EBMOSTStress.H:12
Definition: ERF_MOSTStress.H:282
Definition: ERF_MOSTStress.H:437
Definition: ERF_MOSTStress.H:140
Definition: ERF_MOSTStress.H:622
Definition: ERF_MOSTStress.H:852
Definition: ERF_EBMOSTStress.H:196
Definition: ERF_MOSTStress.H:741
Definition: ERF_MOSTStress.H:960
Definition: ERF_MOSTStress.H:517
Definition: ERF_MOSTStress.H:1215
Definition: ERF_MOSTStress.H:1551
Definition: ERF_EBMOSTStress.H:61
Definition: ERF_MOSTStress.H:1387
Definition: ERF_MOSTStress.H:1669
Definition: ERF_MOSTStress.H:1081
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
621  {
622  m_ma.update_field_ptrs(lev, vars_old, Theta_prim, Qv_prim, Qr_prim);
623  }
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:279
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 
)
1181 {
1183  MYNNPBLH estimator;
1184  compute_pblh(lev, vars, z_phys_cc, estimator, moisture_indices);
1186  amrex::Error("YSU/MRF PBLH calc not implemented yet");
1187  }
1188 }
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)
Definition: ERF_PBLHeight.H:8

◆ update_sst_ptr()

void SurfaceLayer::update_sst_ptr ( const int  lev,
const int  itime,
amrex::MultiFab *  sst_ptr 
)
inline
678  {
679  m_sst_lev[lev][itime] = sst_ptr;
680  }

◆ update_surf_temp()

void SurfaceLayer::update_surf_temp ( const amrex::Real time)
inline
602  {
603  if (m_has_ocean_lsm_tsurf) {
604  return;
605  }
606  if (surf_heating_rate != 0) {
607  int nlevs = static_cast<int>(m_geom.size());
608  for (int lev = 0; lev < nlevs; lev++) {
609  t_surf[lev]->setVal(surf_temp + surf_heating_rate * time);
610  amrex::Print() << "Surface temp at t=" << time << ": "
611  << surf_temp + surf_heating_rate * time << std::endl;
612  }
613  }
614  }

◆ update_tsk_ptr()

void SurfaceLayer::update_tsk_ptr ( const int  lev,
const int  itime,
amrex::MultiFab *  tsk_ptr 
)
inline
682  {
683  m_tsk_lev[lev][itime] = tsk_ptr;
684  }

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_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

amrex::Real SurfaceLayer::m_final_low_time
private

◆ m_geom

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

Referenced by update_surf_temp().

◆ 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_has_ocean_lsm_tsurf

bool SurfaceLayer::m_has_ocean_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

◆ m_lmask_lev

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

◆ m_low_time_interval

amrex::Real 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

MOSTAverage SurfaceLayer::m_ma
private

◆ 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

amrex::Real 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_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}

◆ 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}

◆ specified_rho_surf

bool SurfaceLayer::specified_rho_surf {false}
private

◆ surf_heating_rate

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

Referenced by update_surf_temp().

◆ surf_moist

amrex::Real SurfaceLayer::surf_moist
private

◆ surf_moist_flux

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

◆ surf_temp

amrex::Real SurfaceLayer::surf_temp
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: