ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
SurfaceLayer Class Reference

#include <ERF_SurfaceLayer.H>

Collaboration diagram for SurfaceLayer:

Public Types

enum class  FluxCalcType { MOENG = 0 , DONELAN , CUSTOM , ROTATE }
 
enum class  ThetaCalcType { ADIABATIC = 0 , HEAT_FLUX , SURFACE_TEMPERATURE }
 
enum class  RoughCalcType {
  CONSTANT = 0 , CHARNOCK , MODIFIED_CHARNOCK , DONELAN ,
  WAVE_COUPLED
}
 
enum class  PBLHeightCalcType { None , MYNN25 , YSU }
 

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 TerrainType &a_terrain_type, amrex::Real start_bdy_time=0.0, amrex::Real bdy_time_interval=0.0)
 
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< amrex::MultiFab * > lsm_flux, 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 &time, int max_iters=500)
 
template<typename FluxIter >
void compute_fluxes (const int &lev, const int &max_iters, const FluxIter &most_flux, bool is_land)
 
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)
 
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)
 
void fill_tsurf_with_sst_and_tsk (const int &lev, const amrex::Real &time)
 
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 int RhoQv_comp, const int RhoQc_comp, const int RhoQr_comp)
 
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 int RhoQv_comp, const int RhoQc_comp, const int RhoQr_comp)
 
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)
 
amrex::Real get_zref ()
 
amrex::FArrayBox * 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)
 
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 PBLHeightEstimator >
void compute_pblh (const int &lev, Vector< Vector< MultiFab >> &vars, MultiFab *z_phys_cc, const PBLHeightEstimator &est, int RhoQv_comp, int RhoQc_comp, int RhoQr_comp)
 

Public Attributes

FluxCalcType flux_type {FluxCalcType::MOENG}
 
ThetaCalcType theta_type {ThetaCalcType::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_bdy_time
 
amrex::Real m_bdy_time_interval
 
bool m_include_wstar = false
 
amrex::Real z0_const {0.1}
 
amrex::Real default_land_surf_temp = 300.
 
amrex::Real surf_temp
 
amrex::Real surf_heating_rate {0}
 
amrex::Real surf_temp_flux {0}
 
amrex::Real custom_ustar {0}
 
amrex::Real custom_tstar {0}
 
amrex::Real custom_qstar {0}
 
amrex::Real cnk_a {0.0185}
 
bool cnk_visc {false}
 
amrex::Real depth {30.0}
 
amrex::Vector< amrex::FArrayBox > z_0
 
bool m_var_z0 {false}
 
bool use_moisture
 
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< 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< amrex::MultiFab * > m_Hwave_lev
 
amrex::Vector< amrex::MultiFab * > m_Lwave_lev
 
amrex::Vector< amrex::MultiFab * > m_eddyDiffs_lev
 

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–489. https://doi.org/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/10.1142/2975.

Member Enumeration Documentation

◆ FluxCalcType

Enumerator
MOENG 

Moeng functional form.

DONELAN 

Donelan functional form.

CUSTOM 

Custom constant flux functional form.

ROTATE 

Terrain rotation flux functional form.

501  {
502  MOENG = 0, ///< Moeng functional form
503  DONELAN, ///< Donelan functional form
504  CUSTOM, ///< Custom constant flux functional form
505  ROTATE ///< Terrain rotation flux functional form
506  };

◆ PBLHeightCalcType

Enumerator
None 
MYNN25 
YSU 
522  {
523  None, MYNN25, YSU
524  };

◆ RoughCalcType

Enumerator
CONSTANT 

Constant z0.

CHARNOCK 
MODIFIED_CHARNOCK 
DONELAN 
WAVE_COUPLED 
514  {
515  CONSTANT = 0, ///< Constant z0
516  CHARNOCK,
517  MODIFIED_CHARNOCK,
518  DONELAN,
519  WAVE_COUPLED
520  };

◆ ThetaCalcType

Enumerator
ADIABATIC 
HEAT_FLUX 

Heat-flux specified.

SURFACE_TEMPERATURE 

Surface temperature specified.

508  {
509  ADIABATIC = 0,
510  HEAT_FLUX, ///< Heat-flux specified
511  SURFACE_TEMPERATURE ///< Surface temperature specified
512  };

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 TerrainType &  a_terrain_type,
amrex::Real  start_bdy_time = 0.0,
amrex::Real  bdy_time_interval = 0.0 
)
inlineexplicit
43  : m_geom(geom),
44  m_rotate(use_rot_surface_flux),
45  m_start_bdy_time(start_bdy_time),
46  m_bdy_time_interval(bdy_time_interval),
47  m_ma(geom,(z_phys_nd[0]!=nullptr),a_pp_prefix,a_terrain_type)
48  {
49  // We have a moisture model if Qv_prim is a valid pointer
50  use_moisture = (Qv_prim[0].get());
51 
52  // Get roughness
53  amrex::ParmParse pp("erf");
54  pp.query("most.z0", z0_const);
55 
56  // Specify how to compute the flux
57  if (use_rot_surface_flux) {
59  } else {
60  std::string flux_string_in;
61  std::string flux_string{"moeng"};
62  auto read_flux = pp.query("surface_layer.flux_type", flux_string_in);
63  if (read_flux) { flux_string = amrex::toLower(flux_string_in); }
64  if (flux_string == "donelan") {
66  } else if (flux_string == "moeng") {
68  } else if (flux_string == "custom") {
70  } else {
71  amrex::Abort("Undefined MOST flux type!");
72  }
73  }
74 
75  // Include w* to handle free convection (Beljaars 1995, QJRMS)
76  pp.query("most.include_wstar", m_include_wstar);
77 
78  std::string pblh_string_in;
79  std::string pblh_string{"none"};
80  auto read_pblh = pp.query("most.pblh_calc", pblh_string_in);
81  if (read_pblh) { pblh_string = amrex::toLower(pblh_string_in); }
82  if (pblh_string == "none") {
84  } else if (pblh_string == "mynn25") {
86  } else if (pblh_string == "mynnedmf") {
88  } else if (pblh_string == "ysu") {
90  } else {
91  amrex::Abort("Undefined PBLH calc type!");
92  }
93 
94  // Get surface temperature
95  auto erf_st = pp.query("most.surf_temp", surf_temp);
96  if (erf_st) { default_land_surf_temp = surf_temp; }
97 
98  // Custom type user must specify the fluxes
101  pp.query("most.ustar", custom_ustar);
102  pp.query("most.tstar", custom_tstar);
103  pp.query("most.qstar", custom_qstar);
104  if (custom_qstar != 0) {
105  AMREX_ASSERT_WITH_MESSAGE(use_moisture, "Specified custom MOST qv flux without moisture model!");
106  }
107  amrex::Print() << "Using specified ustar, tstar, qstar for MOST = "
108  << custom_ustar << " "
109  << custom_tstar << " "
110  << custom_qstar << std::endl;
111 
112  // Specify surface temperature or surface flux
113  } else {
114  if (erf_st) {
116  pp.query("most.surf_heating_rate", surf_heating_rate); // [K/h]
117  surf_heating_rate = surf_heating_rate / 3600.0; // [K/s]
118  if (pp.query("most.surf_temp_flux", surf_temp_flux)) {
119  amrex::Abort("Can only specify one of surf_temp_flux or surf_heating_rate");
120  }
121  } else {
122  pp.query("most.surf_temp_flux", surf_temp_flux);
123  if (pp.query("most.surf_heating_rate", surf_heating_rate)) {
124  amrex::Abort("Can only specify one of surf_temp_flux or surf_heating_rate");
125  }
128  } else {
130  }
131  }
132  }
133 
134  // Make sure the inputs file doesn't try to use most.roughness_type
135  std::string bogus_input;
136  if (pp.query("most.roughness_type", bogus_input) > 0) {
137  amrex::Abort("most.roughness_type is deprecated; use most.roughness_type_land and/or most.roughness_type_sea");
138  }
139 
140  // Specify how to compute the surface flux over land (if there is any)
141  std::string rough_land_string_in;
142  std::string rough_land_string{"constant"};
143  auto read_rough_land = pp.query("most.roughness_type_land", rough_land_string_in);
144  if (read_rough_land) { rough_land_string = amrex::toLower(rough_land_string_in); }
145  if (rough_land_string == "constant") {
147  } else {
148  amrex::Abort("Undefined MOST roughness type for land!");
149  }
150 
151  // Specify how to compute the surface flux over sea (if there is any)
152  std::string rough_sea_string_in;
153  std::string rough_sea_string{"charnock"};
154  auto read_rough_sea = pp.query("most.roughness_type_sea", rough_sea_string_in);
155  if (read_rough_sea) { rough_sea_string = amrex::toLower(rough_sea_string_in); }
156  if (rough_sea_string == "charnock") {
158  pp.query("most.charnock_constant",cnk_a);
159  pp.query("most.charnock_viscosity",cnk_visc);
160  if (cnk_a > 0) {
161  amrex::Print() << "If there is water, Charnock relation with C_a=" << cnk_a
162  << (cnk_visc? " and viscosity" : "")
163  << " will be used"
164  << std::endl;
165  } else {
166  amrex::Print() << "If there is water, Charnock relation with variable Charnock parameter (COARE3.0)"
167  << (cnk_visc? " and viscosity" : "")
168  << " will be used"
169  << std::endl;
170  }
171  } else if (rough_sea_string == "coare3.0") {
173  amrex::Print() << "If there is water, Charnock relation with variable Charnock parameter (COARE3.0)"
174  << (cnk_visc? " and viscosity" : "")
175  << " will be used"
176  << std::endl;
177  cnk_a = -1;
178  } else if (rough_sea_string == "donelan") {
180  } else if (rough_sea_string == "modified_charnock") {
182  pp.query("most.modified_charnock_depth",depth);
183  } else if (rough_sea_string == "wave_coupled") {
185  } else if (rough_sea_string == "constant") {
187  } else {
188  amrex::Abort("Undefined MOST roughness type for sea!");
189  }
190  } // constructor
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real pp(amrex::Real y)
Definition: ERF_MicrophysicsUtils.H:219
ThetaCalcType theta_type
Definition: ERF_SurfaceLayer.H:527
bool m_include_wstar
Definition: ERF_SurfaceLayer.H:539
bool m_rotate
Definition: ERF_SurfaceLayer.H:535
PBLHeightCalcType pblh_type
Definition: ERF_SurfaceLayer.H:530
bool use_moisture
Definition: ERF_SurfaceLayer.H:554
RoughCalcType rough_type_land
Definition: ERF_SurfaceLayer.H:528
amrex::Real z0_const
Definition: ERF_SurfaceLayer.H:540
amrex::Real cnk_a
Definition: ERF_SurfaceLayer.H:548
amrex::Real surf_temp
Definition: ERF_SurfaceLayer.H:542
amrex::Real m_start_bdy_time
Definition: ERF_SurfaceLayer.H:536
RoughCalcType rough_type_sea
Definition: ERF_SurfaceLayer.H:529
amrex::Real m_bdy_time_interval
Definition: ERF_SurfaceLayer.H:537
amrex::Real custom_qstar
Definition: ERF_SurfaceLayer.H:547
@ MOENG
Moeng functional form.
@ CUSTOM
Custom constant flux functional form.
@ ROTATE
Terrain rotation flux functional form.
@ DONELAN
Donelan functional form.
amrex::Real depth
Definition: ERF_SurfaceLayer.H:550
amrex::Real surf_temp_flux
Definition: ERF_SurfaceLayer.H:544
amrex::Vector< amrex::Geometry > m_geom
Definition: ERF_SurfaceLayer.H:534
amrex::Real custom_tstar
Definition: ERF_SurfaceLayer.H:546
bool cnk_visc
Definition: ERF_SurfaceLayer.H:549
amrex::Real surf_heating_rate
Definition: ERF_SurfaceLayer.H:543
FluxCalcType flux_type
Definition: ERF_SurfaceLayer.H:526
amrex::Real custom_ustar
Definition: ERF_SurfaceLayer.H:545
amrex::Real default_land_surf_temp
Definition: ERF_SurfaceLayer.H:541
@ SURFACE_TEMPERATURE
Surface temperature specified.
@ HEAT_FLUX
Heat-flux specified.
MOSTAverage m_ma
Definition: ERF_SurfaceLayer.H:556
real(c_double), parameter epsilon
Definition: ERF_module_model_constants.F90:12
Here is the call graph for this function:

Member Function Documentation

◆ compute_fluxes()

template<typename FluxIter >
void SurfaceLayer::compute_fluxes ( const int &  lev,
const int &  max_iters,
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
149 {
150  // Pointers to the computed averages
151  const auto *const tm_ptr = m_ma.get_average(lev,2); // potential temperature
152  const auto *const qvm_ptr = m_ma.get_average(lev,3); // water vapor mixing ratio
153  const auto *const tvm_ptr = m_ma.get_average(lev,4); // virtual potential temperature
154  const auto *const umm_ptr = m_ma.get_average(lev,5); // horizontal velocity magnitude
155 
156  for (MFIter mfi(*u_star[lev]); mfi.isValid(); ++mfi)
157  {
158  Box gtbx = mfi.growntilebox();
159 
160  auto u_star_arr = u_star[lev]->array(mfi);
161  auto t_star_arr = t_star[lev]->array(mfi);
162  auto q_star_arr = q_star[lev]->array(mfi);
163  auto t_surf_arr = t_surf[lev]->array(mfi);
164  auto olen_arr = olen[lev]->array(mfi);
165 
166  const auto tm_arr = tm_ptr->array(mfi);
167  const auto tvm_arr = tvm_ptr->array(mfi);
168  const auto qvm_arr = qvm_ptr->array(mfi);
169  const auto umm_arr = umm_ptr->array(mfi);
170  const auto z0_arr = z_0[lev].array();
171 
172  // PBL height if we need to calculate wstar for the Beljaars correction
173  // TODO: can/should we apply this in LES mode?
174  const auto w_star_arr = (m_include_wstar) ? w_star[lev].get()->array(mfi) : Array4<Real> {};
175  const auto pblh_arr = (m_include_wstar) ? pblh[lev].get()->array(mfi) : Array4<Real> {};
176 
177  // Wave properties if they exist
178  const auto Hwave_arr = (m_Hwave_lev[lev]) ? m_Hwave_lev[lev]->array(mfi) : Array4<Real> {};
179  const auto Lwave_arr = (m_Lwave_lev[lev]) ? m_Lwave_lev[lev]->array(mfi) : Array4<Real> {};
180  const auto eta_arr = (m_eddyDiffs_lev[lev]) ? m_eddyDiffs_lev[lev]->array(mfi) : Array4<Real> {};
181 
182  // Land mask array if it exists
183  auto lmask_arr = (m_lmask_lev[lev][0]) ? m_lmask_lev[lev][0]->array(mfi) :
184  Array4<int> {};
185 
186  ParallelFor(gtbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
187  {
188  if (( is_land && lmask_arr(i,j,k) == 1) ||
189  (!is_land && lmask_arr(i,j,k) == 0))
190  {
191  most_flux.iterate_flux(i, j, k, max_iters,
192  z0_arr, umm_arr, tm_arr, tvm_arr, qvm_arr,
193  u_star_arr, w_star_arr, // to be updated
194  t_star_arr, q_star_arr, // to be updated
195  t_surf_arr, olen_arr, // to be updated
196  pblh_arr, Hwave_arr, Lwave_arr, eta_arr);
197  }
198  });
199  }
200 }
const amrex::MultiFab * get_average(const int &lev, const int &comp) const
Definition: ERF_MOSTAverage.H:101
amrex::Vector< amrex::Vector< amrex::iMultiFab * > > m_lmask_lev
Definition: ERF_SurfaceLayer.H:567
amrex::Vector< std::unique_ptr< amrex::MultiFab > > t_surf
Definition: ERF_SurfaceLayer.H:563
amrex::Vector< amrex::FArrayBox > z_0
Definition: ERF_SurfaceLayer.H:551
amrex::Vector< std::unique_ptr< amrex::MultiFab > > q_star
Definition: ERF_SurfaceLayer.H:560
amrex::Vector< amrex::MultiFab * > m_Lwave_lev
Definition: ERF_SurfaceLayer.H:571
amrex::Vector< std::unique_ptr< amrex::MultiFab > > w_star
Definition: ERF_SurfaceLayer.H:558
amrex::Vector< std::unique_ptr< amrex::MultiFab > > u_star
Definition: ERF_SurfaceLayer.H:557
amrex::Vector< amrex::MultiFab * > m_Hwave_lev
Definition: ERF_SurfaceLayer.H:570
amrex::Vector< std::unique_ptr< amrex::MultiFab > > t_star
Definition: ERF_SurfaceLayer.H:559
amrex::Vector< amrex::MultiFab * > m_eddyDiffs_lev
Definition: ERF_SurfaceLayer.H:572
amrex::Vector< std::unique_ptr< amrex::MultiFab > > olen
Definition: ERF_SurfaceLayer.H:561
amrex::Vector< std::unique_ptr< amrex::MultiFab > > pblh
Definition: ERF_SurfaceLayer.H:562

◆ 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 int  RhoQv_comp,
const int  RhoQc_comp,
const int  RhoQr_comp 
)

◆ 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,
int  RhoQv_comp,
int  RhoQc_comp,
int  RhoQr_comp 
)
548 {
549  est.compute_pblh(m_geom[lev],z_phys_cc, pblh[lev].get(),
550  vars[lev][Vars::cons],m_lmask_lev[lev][0],
551  RhoQv_comp, RhoQc_comp, RhoQr_comp);
552 }
@ cons
Definition: ERF_IndexDefines.H:140

◆ 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
271 {
272  bool rotate = m_rotate;
273  const int klo = m_geom[lev].Domain().smallEnd(2);
274  const auto& dxInv = m_geom[lev].InvCellSizeArray();
275  for (MFIter mfi(*mfs[0]); mfi.isValid(); ++mfi)
276  {
277  // Get field arrays
278  const auto cons_arr = mfs[Vars::cons]->array(mfi);
279  const auto velx_arr = mfs[Vars::xvel]->array(mfi);
280  const auto vely_arr = mfs[Vars::yvel]->array(mfi);
281  const auto velz_arr = mfs[Vars::zvel]->array(mfi);
282 
283  // Diffusive stress vars
284  auto t13_arr = Tau_lev[TauType::tau13]->array(mfi);
285  auto t31_arr = (Tau_lev[TauType::tau31]) ? Tau_lev[TauType::tau31]->array(mfi) : Array4<Real>{};
286 
287  auto t23_arr = Tau_lev[TauType::tau23]->array(mfi);
288  auto t32_arr = (Tau_lev[TauType::tau32]) ? Tau_lev[TauType::tau32]->array(mfi) : Array4<Real>{};
289 
290  auto hfx3_arr = zheat_flux->array(mfi);
291  auto qfx3_arr = (zqv_flux) ? zqv_flux->array(mfi) : Array4<Real>{};
292 
293  // Rotated stress vars
294  auto t11_arr = (m_rotate) ? Tau_lev[TauType::tau11]->array(mfi) : Array4<Real>{};
295  auto t22_arr = (m_rotate) ? Tau_lev[TauType::tau22]->array(mfi) : Array4<Real>{};
296  auto t33_arr = (m_rotate) ? Tau_lev[TauType::tau33]->array(mfi) : Array4<Real>{};
297  auto t12_arr = (m_rotate) ? Tau_lev[TauType::tau12]->array(mfi) : Array4<Real>{};
298  auto t21_arr = (m_rotate) ? Tau_lev[TauType::tau21]->array(mfi) : Array4<Real>{};
299 
300  auto hfx1_arr = (m_rotate) ? xheat_flux->array(mfi) : Array4<Real>{};
301  auto hfx2_arr = (m_rotate) ? yheat_flux->array(mfi) : Array4<Real>{};
302  auto qfx1_arr = (m_rotate && xqv_flux) ? xqv_flux->array(mfi) : Array4<Real>{};
303  auto qfx2_arr = (m_rotate && yqv_flux) ? yqv_flux->array(mfi) : Array4<Real>{};
304 
305  // Terrain
306  const auto zphys_arr = (z_phys) ? z_phys->const_array(mfi) : Array4<const Real>{};
307 
308  // Get average arrays
309  const auto *const u_mean = m_ma.get_average(lev,0);
310  const auto *const v_mean = m_ma.get_average(lev,1);
311  const auto *const t_mean = m_ma.get_average(lev,2);
312  const auto *const q_mean = m_ma.get_average(lev,3);
313  const auto *const u_mag_mean = m_ma.get_average(lev,5);
314 
315  const auto um_arr = u_mean->array(mfi);
316  const auto vm_arr = v_mean->array(mfi);
317  const auto tm_arr = t_mean->array(mfi);
318  const auto qm_arr = q_mean->array(mfi);
319  const auto umm_arr = u_mag_mean->array(mfi);
320 
321  // Get derived arrays
322  const auto u_star_arr = u_star[lev]->array(mfi);
323  const auto t_star_arr = t_star[lev]->array(mfi);
324  const auto q_star_arr = q_star[lev]->array(mfi);
325  const auto t_surf_arr = t_surf[lev]->array(mfi);
326 
327  // Get LSM fluxes
328  auto lmask_arr = (m_lmask_lev[lev][0]) ? m_lmask_lev[lev][0]->array(mfi) :
329  Array4<int> {};
330  auto lsm_flux_arr = (m_lsm_flux_lev[lev][0]) ? m_lsm_flux_lev[lev][0]->array(mfi) :
331  Array4<Real> {};
332 
333  // Rho*Theta flux
334  //============================================================================
335  Box bx = mfi.tilebox();
336  if (bx.smallEnd(2) != klo) { continue; }
337  bx.makeSlab(2,klo);
338  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k)
339  {
340  Real Tflux = flux_comp.compute_t_flux(i, j, k,
341  cons_arr, velx_arr, vely_arr,
342  umm_arr, tm_arr, u_star_arr,
343  t_star_arr, t_surf_arr);
344 
345  if (rotate) {
346  rotate_scalar_flux(i, j, k, Tflux, dxInv, zphys_arr,
347  hfx1_arr, hfx2_arr, hfx3_arr);
348  } else {
349  hfx3_arr(i,j,klo) = Tflux;
350  int is_land = (lmask_arr) ? lmask_arr(i,j,klo) : 1;
351  if (is_land && lsm_flux_arr) {
352  lsm_flux_arr(i,j,k) = Tflux;
353  }
354  }
355  });
356 
357  // Rho*Qv flux
358  //============================================================================
359  // TODO: Generalize MOST q flux
361  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k)
362  {
363  Real Qflux = flux_comp.compute_q_flux(i, j, k,
364  cons_arr, velx_arr, vely_arr,
365  umm_arr, qm_arr, u_star_arr,
366  q_star_arr, t_surf_arr);
367 
368  if (rotate) {
369  rotate_scalar_flux(i, j, k, Qflux, dxInv, zphys_arr,
370  qfx1_arr, qfx2_arr, qfx3_arr);
371  } else {
372  qfx3_arr(i,j,k) = Qflux;
373  }
374  });
375  } // custom
376 
377  // Rho*u flux
378  //============================================================================
379  Box bxx = surroundingNodes(bx,0);
380  ParallelFor(bxx, [=] AMREX_GPU_DEVICE (int i, int j, int k)
381  {
382  Real stressx = flux_comp.compute_u_flux(i, j, k,
383  cons_arr, velx_arr, vely_arr,
384  umm_arr, um_arr, u_star_arr);
385 
386  if (rotate) {
387  rotate_stress_tensor(i, j, k, stressx, dxInv, zphys_arr,
388  velx_arr, vely_arr, velz_arr,
389  t11_arr, t22_arr, t33_arr,
390  t12_arr, t21_arr,
391  t13_arr, t31_arr,
392  t23_arr, t32_arr);
393  } else {
394  t13_arr(i,j,k) = stressx;
395  if (t31_arr) { t31_arr(i,j,k) = stressx; }
396  }
397  });
398 
399  // Rho*v flux
400  //============================================================================
401  Box bxy = surroundingNodes(bx,1);
402  ParallelFor(bxy, [=] AMREX_GPU_DEVICE (int i, int j, int k)
403  {
404  Real stressy = flux_comp.compute_v_flux(i, j, k,
405  cons_arr, velx_arr, vely_arr,
406  umm_arr, vm_arr, u_star_arr);
407 
408  // NOTE: One stress rotation for ALL the stress components
409  if (!rotate) {
410  t23_arr(i,j,k) = stressy;
411  if (t32_arr) { t32_arr(i,j,k) = stressy; }
412  }
413  });
414  } // mfiter
415 }
@ tau12
Definition: ERF_DataStruct.H:30
@ tau23
Definition: ERF_DataStruct.H:30
@ tau33
Definition: ERF_DataStruct.H:30
@ tau22
Definition: ERF_DataStruct.H:30
@ tau11
Definition: ERF_DataStruct.H:30
@ tau32
Definition: ERF_DataStruct.H:30
@ tau31
Definition: ERF_DataStruct.H:30
@ tau21
Definition: ERF_DataStruct.H:30
@ tau13
Definition: ERF_DataStruct.H:30
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:612
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:633
amrex::Vector< amrex::Vector< amrex::MultiFab * > > m_lsm_flux_lev
Definition: ERF_SurfaceLayer.H:569
@ xvel
Definition: ERF_IndexDefines.H:141
@ zvel
Definition: ERF_IndexDefines.H:143
@ yvel
Definition: ERF_IndexDefines.H:142
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 
)
420 {
421  int n_times_in_sst = m_sst_lev[lev].size();
422 
423  int n_time_lo, n_time_hi;
424  Real alpha;
425 
426  if (n_times_in_sst > 1) {
427  // Time interpolation
428  Real dT = m_bdy_time_interval;
429  Real time_since_start = time - m_start_bdy_time;
430  int n_time = static_cast<int>( time_since_start / dT);
431  n_time_lo = n_time;
432  n_time_hi = n_time+1;
433  alpha = (time_since_start - n_time * dT) / dT;
434  AMREX_ALWAYS_ASSERT( (n_time >= 0) && (n_time < (m_sst_lev[lev].size()-1)));
435  } else {
436  n_time_lo = 0;
437  n_time_hi = 0;
438  alpha = 1.0;
439  }
440  AMREX_ALWAYS_ASSERT( alpha >= 0. && alpha <= 1.0);
441 
442  Real oma = 1.0 - alpha;
443 
444  // Define a default land surface temperature if we don't read in tsk
445  Real lst = default_land_surf_temp;
446 
447  bool use_tsk = (m_tsk_lev[lev][0]);
448 
449  // Populate t_surf
450  for (MFIter mfi(*t_surf[lev]); mfi.isValid(); ++mfi)
451  {
452  Box gtbx = mfi.growntilebox();
453 
454  auto t_surf_arr = t_surf[lev]->array(mfi);
455 
456  const auto sst_lo_arr = m_sst_lev[lev][n_time_lo]->const_array(mfi);
457  const auto sst_hi_arr = m_sst_lev[lev][n_time_hi]->const_array(mfi);
458 
459  auto lmask_arr = (m_lmask_lev[lev][0]) ? m_lmask_lev[lev][0]->array(mfi) :
460  Array4<int> {};
461 
462  if (use_tsk) {
463  const auto tsk_arr = m_tsk_lev[lev][n_time_lo]->const_array(mfi);
464  ParallelFor(gtbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
465  {
466  int is_land = (lmask_arr) ? lmask_arr(i,j,k) : 1;
467  if (!is_land) {
468  t_surf_arr(i,j,k) = oma * sst_lo_arr(i,j,k)
469  + alpha * sst_hi_arr(i,j,k);
470  } else {
471  t_surf_arr(i,j,k) = tsk_arr(i,j,k);
472  }
473  });
474  } else {
475  ParallelFor(gtbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
476  {
477  int is_land = (lmask_arr) ? lmask_arr(i,j,k) : 1;
478  if (!is_land) {
479  t_surf_arr(i,j,k) = oma * sst_lo_arr(i,j,k)
480  + alpha * sst_hi_arr(i,j,k);
481  } else {
482  t_surf_arr(i,j,k) = lst;
483  }
484  });
485  }
486  }
487  t_surf[lev]->FillBoundary(m_geom[lev].periodicity());
488 }
amrex::Vector< amrex::Vector< amrex::MultiFab * > > m_sst_lev
Definition: ERF_SurfaceLayer.H:565
amrex::Vector< amrex::Vector< amrex::MultiFab * > > m_tsk_lev
Definition: ERF_SurfaceLayer.H:566

◆ get_lmask()

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

◆ get_lsm_tsurf()

void SurfaceLayer::get_lsm_tsurf ( const int &  lev)
492 {
493  for (MFIter mfi(*t_surf[lev]); mfi.isValid(); ++mfi)
494  {
495  Box gtbx = mfi.growntilebox();
496 
497  // TODO: LSM does not carry lateral ghost cells.
498  // This copies the valid box into the ghost cells.
499  // Fillboundary is called after this to pick up the
500  // interior ghost and periodic directions. Is there
501  // a better approach?
502  Box vbx = mfi.validbox();
503  int i_lo = vbx.smallEnd(0); int i_hi = vbx.bigEnd(0);
504  int j_lo = vbx.smallEnd(1); int j_hi = vbx.bigEnd(1);
505 
506  auto t_surf_arr = t_surf[lev]->array(mfi);
507  auto lmask_arr = (m_lmask_lev[lev][0]) ? m_lmask_lev[lev][0]->array(mfi) :
508  Array4<int> {};
509  const auto lsm_arr = m_lsm_data_lev[lev][0]->const_array(mfi);
510 
511  ParallelFor(gtbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
512  {
513  int is_land = (lmask_arr) ? lmask_arr(i,j,k) : 1;
514  if (is_land) {
515  int li = amrex::min(amrex::max(i, i_lo), i_hi);
516  int lj = amrex::min(amrex::max(j, j_lo), j_hi);
517  t_surf_arr(i,j,k) = lsm_arr(li,lj,k);
518  }
519  });
520  }
521 }
amrex::Vector< amrex::Vector< amrex::MultiFab * > > m_lsm_data_lev
Definition: ERF_SurfaceLayer.H:568

◆ get_mac_avg()

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

◆ get_olen()

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

◆ get_pblh()

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

◆ get_q_star()

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

◆ get_t_star()

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

◆ get_t_surf()

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

◆ get_u_star()

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

◆ get_w_star()

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

◆ get_z0()

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

◆ get_zref()

amrex::Real SurfaceLayer::get_zref ( )
inline
471 { return m_ma.get_zref(); }
amrex::Real get_zref() const
Definition: ERF_MOSTAverage.H:104
Here is the call graph for this function:

◆ have_variable_sea_roughness()

bool SurfaceLayer::have_variable_sea_roughness ( )
inline
476 { return m_var_z0; }
bool m_var_z0
Definition: ERF_SurfaceLayer.H:552

◆ 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
221 {
223  moeng_flux flux_comp;
224  compute_SurfaceLayer_bcs(lev, mfs, Tau_lev,
225  xheat_flux, yheat_flux, zheat_flux,
226  xqv_flux, yqv_flux, zqv_flux,
227  z_phys, flux_comp);
228  } else if (flux_type == FluxCalcType::DONELAN) {
229  donelan_flux flux_comp;
230  compute_SurfaceLayer_bcs(lev, mfs, Tau_lev,
231  xheat_flux, yheat_flux, zheat_flux,
232  xqv_flux, yqv_flux, zqv_flux,
233  z_phys, flux_comp);
234  } else if (flux_type == FluxCalcType::ROTATE) {
235  rotate_flux flux_comp;
236  compute_SurfaceLayer_bcs(lev, mfs, Tau_lev,
237  xheat_flux, yheat_flux, zheat_flux,
238  xqv_flux, yqv_flux, zqv_flux,
239  z_phys, flux_comp);
240  } else {
241  custom_flux flux_comp;
242  compute_SurfaceLayer_bcs(lev, mfs, Tau_lev,
243  xheat_flux, yheat_flux, zheat_flux,
244  xqv_flux, yqv_flux, zqv_flux,
245  z_phys, flux_comp);
246  }
247 }
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:1645
Definition: ERF_MOSTStress.H:1513
Definition: ERF_MOSTStress.H:1367
Definition: ERF_MOSTStress.H:1759

◆ lmask_min_reduce()

int SurfaceLayer::lmask_min_reduce ( amrex::iMultiFab &  lmask,
const int &  nghost 
)
inline
483  {
484  int lmask_min = amrex::ReduceMin(lmask, nghost,
485  [=] AMREX_GPU_HOST_DEVICE (amrex::Box const& bx, amrex::Array4<int const> const& lm_arr) -> int
486  {
487  int locmin = std::numeric_limits<int>::max();
488  const auto lo = lbound(bx);
489  const auto hi = ubound(bx);
490  for (int j = lo.y; j <= hi.y; ++j) {
491  for (int i = lo.x; i <= hi.x; ++i) {
492  locmin = std::min(locmin, lm_arr(i,j,0));
493  }
494  }
495  return locmin;
496  });
497 
498  return lmask_min;
499  }

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< amrex::MultiFab * >  lsm_flux,
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
208  {
209  // Update MOST Average
210  m_ma.make_MOSTAverage_at_level(lev, mfv, Theta_prim, Qv_prim, Qr_prim, z_phys_nd);
211 
212  // Get CC vars
213  amrex::MultiFab& mf = *(mfv[0]);
214 
215  amrex::ParmParse pp("erf");
216 
217  // Do we have a time-varying surface roughness that needs to be saved?
218  if (lev == 0)
219  {
220  const int nghost = 0; // ghost cells not included
221  int lmask_min = lmask_min_reduce(*lmask_lev[0].get(), nghost);
222  amrex::ParallelDescriptor::ReduceIntMin(lmask_min);
223 
224  m_var_z0 = (lmask_min < 1) & (rough_type_sea != RoughCalcType::CONSTANT);
225  if (m_var_z0) {
226  std::string rough_sea_string{"charnock"};
227  pp.query("most.roughness_type_sea", rough_sea_string);
228  amrex::Print() << "Variable sea roughness (type " << rough_sea_string << ")" << std::endl;
229  }
230  }
231 
232  if ( (lev == 0) || (lev > nlevs-1) ) {
233  m_Hwave_lev.resize(nlevs);
234  m_Lwave_lev.resize(nlevs);
235  m_eddyDiffs_lev.resize(nlevs);
236 
237  m_lsm_data_lev.resize(nlevs);
238  m_lsm_flux_lev.resize(nlevs);
239 
240  m_sst_lev.resize(nlevs);
241  m_tsk_lev.resize(nlevs);
242  m_lmask_lev.resize(nlevs);
243 
244  // Size the MOST params for all levels
245  z_0.resize(nlevs);
246  u_star.resize(nlevs);
247  w_star.resize(nlevs);
248  t_star.resize(nlevs);
249  q_star.resize(nlevs);
250  t_surf.resize(nlevs);
251  olen.resize(nlevs);
252  pblh.resize(nlevs);
253  }
254 
255 
256  // Get pointers to SST,TSK and LANDMASK data
257  int nt_tot_sst = sst_lev.size();
258  m_sst_lev[lev].resize(nt_tot_sst);
259  for (int nt(0); nt<nt_tot_sst; ++nt) {
260  m_sst_lev[lev][nt] = sst_lev[nt].get();
261  }
262  int nt_tot_tsk = tsk_lev.size();
263  m_tsk_lev[lev].resize(nt_tot_tsk);
264  for (int nt(0); nt<nt_tot_tsk; ++nt) {
265  m_tsk_lev[lev][nt] = tsk_lev[nt].get();
266  }
267  int nt_tot_lmask = lmask_lev.size();
268  m_lmask_lev[lev].resize(nt_tot_lmask);
269  for (int nt(0); nt<nt_tot_lmask; ++nt) {
270  m_lmask_lev[lev][nt] = lmask_lev[nt].get();
271  }
272 
273  // Get pointers to wave data
274  m_Hwave_lev[lev] = Hwave;
275  m_Lwave_lev[lev] = Lwave;
276  m_eddyDiffs_lev[lev] = eddyDiffs;
277 
278  // Get pointers to LSM data and Fluxes
279  int nvar = lsm_data.size();
280  m_lsm_data_lev[lev].resize(nvar);
281  m_lsm_flux_lev[lev].resize(nvar);
282  for (int n(0); n<nvar; ++n) {
283  m_lsm_data_lev[lev][n] = lsm_data[n];
284  m_lsm_flux_lev[lev][n] = lsm_flux[n];
285  }
286 
287  // Check if there is a user-specified roughness file to be read
288  std::string fname;
289  bool read_z0 = false;
290  if ( (flux_type == FluxCalcType::MOENG) ||
292  read_z0 = pp.query("most.roughness_file_name", fname);
293  }
294 
295  // Attributes for MFs and FABs
296  //--------------------------------------------------------
297  // Create a 2D ba, dm, & ghost cells
298  amrex::BoxArray ba = mf.boxArray();
299  amrex::BoxList bl2d = ba.boxList();
300  for (auto& b : bl2d) {
301  b.setRange(2,0);
302  }
303  amrex::BoxArray ba2d(std::move(bl2d));
304  const amrex::DistributionMapping& dm = mf.DistributionMap();
305  const int ncomp = 1;
306  amrex::IntVect ng = mf.nGrowVect(); ng[2]=0;
307 
308  // Z0 heights FAB
309  //--------------------------------------------------------
310  amrex::Arena* Arena_Used = amrex::The_Arena();
311 #ifdef AMREX_USE_GPU
312  Arena_Used = amrex::The_Pinned_Arena();
313 #endif
314  amrex::Box bx = amrex::grow(m_geom[lev].Domain(),ng);
315  bx.setSmall(2,0);
316  bx.setBig(2,0);
317  z_0[lev].resize(bx,1,Arena_Used);
318  z_0[lev].setVal<amrex::RunOn::Device>(z0_const);
319  if (read_z0) read_custom_roughness(lev,fname);
320 
321  // 2D MFs for U*, T*, T_surf
322  //--------------------------------------------------------
323  u_star[lev] = std::make_unique<amrex::MultiFab>(ba2d,dm,ncomp,ng);
324  u_star[lev]->setVal(1.E34);
325 
326  w_star[lev] = std::make_unique<amrex::MultiFab>(ba2d,dm,ncomp,ng);
327  w_star[lev]->setVal(1.E34);
328 
329  t_star[lev] = std::make_unique<amrex::MultiFab>(ba2d,dm,ncomp,ng);
330  t_star[lev]->setVal(0.0); // default to neutral
331 
332  q_star[lev] = std::make_unique<amrex::MultiFab>(ba2d,dm,ncomp,ng);
333  q_star[lev]->setVal(0.0); // default to dry
334 
335  olen[lev] = std::make_unique<amrex::MultiFab>(ba2d,dm,ncomp,ng);
336  olen[lev]->setVal(1.E34);
337 
338  pblh[lev] = std::make_unique<amrex::MultiFab>(ba2d,dm,ncomp,ng);
339  pblh[lev]->setVal(1.E34);
340 
341  t_surf[lev] = std::make_unique<amrex::MultiFab>(ba2d,dm,ncomp,ng);
342  t_surf[lev]->setVal(default_land_surf_temp);
343 
344  // TODO: Do we want lsm_data to have theta at 0 index always?
345  // Do we want an external enum struct for indexing?
346  if (m_sst_lev[lev][0] || m_tsk_lev[lev][0] || m_lsm_data_lev[lev][0]) {
347  // Valid SST, TSK or LSM data; t_surf set before computing fluxes (avoids extended lambda capture)
348  // Note that land temp will be set from m_tsk_lev
349  // while sea temp will be set from m_sst_lev
351  }
352  }
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:70
int lmask_min_reduce(amrex::iMultiFab &lmask, const int &nghost)
Definition: ERF_SurfaceLayer.H:482
void read_custom_roughness(const int &lev, const std::string &fname)
Definition: ERF_SurfaceLayer.cpp:555
@ ng
Definition: ERF_Morrison.H:48
Here is the call graph for this function:

◆ read_custom_roughness()

void SurfaceLayer::read_custom_roughness ( const int &  lev,
const std::string &  fname 
)
557 {
558  // Read the file if we are on the coarsest level
559  if (lev==0) {
560  // Only the ioproc reads the file and broadcasts
561  if (ParallelDescriptor::IOProcessor()) {
562  Print()<<"Reading MOST roughness file: "<< fname << std::endl;
563  std::ifstream file(fname);
564  Gpu::HostVector<Real> m_x,m_y,m_z0;
565  Real value1,value2,value3;
566  while(file>>value1>>value2>>value3){
567  m_x.push_back(value1);
568  m_y.push_back(value2);
569  m_z0.push_back(value3);
570  }
571  file.close();
572 
573  // Copy data to the GPU
574  int nnode = m_x.size();
575  Gpu::DeviceVector<Real> d_x(nnode),d_y(nnode),d_z0(nnode);
576  Gpu::copy(Gpu::hostToDevice, m_x.begin(), m_x.end(), d_x.begin());
577  Gpu::copy(Gpu::hostToDevice, m_y.begin(), m_y.end(), d_y.begin());
578  Gpu::copy(Gpu::hostToDevice, m_z0.begin(), m_z0.end(), d_z0.begin());
579  Real* xp = d_x.data();
580  Real* yp = d_y.data();
581  Real* z0p = d_z0.data();
582 
583  // Populate z_phys data
584  Real tol = 1.0e-4;
585  auto dx = m_geom[lev].CellSizeArray();
586  auto ProbLoArr = m_geom[lev].ProbLoArray();
587  int ilo = m_geom[lev].Domain().smallEnd(0);
588  int jlo = m_geom[lev].Domain().smallEnd(1);
589  int klo = 0;
590  int ihi = m_geom[lev].Domain().bigEnd(0);
591  int jhi = m_geom[lev].Domain().bigEnd(1);
592 
593  // Grown box with no z range
594  Box xybx = z_0[lev].box();
595  xybx.setRange(2,0);
596 
597  Array4<Real> const& z0_arr = z_0[lev].array();
598  ParallelFor(xybx, [=] AMREX_GPU_DEVICE (int i, int j, int /*k*/)
599  {
600  // Clip indices for ghost-cells
601  int ii = amrex::min(amrex::max(i,ilo),ihi);
602  int jj = amrex::min(amrex::max(j,jlo),jhi);
603 
604  // Location of nodes
605  Real x = ProbLoArr[0] + ii * dx[0];
606  Real y = ProbLoArr[1] + jj * dx[1];
607  int inode = ii + jj * (ihi-ilo+2); // stride is Nx+1
608  if (std::sqrt(std::pow(x-xp[inode],2)+std::pow(y-yp[inode],2)) < tol) {
609  z0_arr(i,j,klo) = z0p[inode];
610  } else {
611  // Unexpected list order, do brute force search
612  Real z0loc = 0.0;
613  bool found = false;
614  for (int n=0; n<nnode; ++n) {
615  Real delta=std::sqrt(std::pow(x-xp[n],2)+std::pow(y-yp[n],2));
616  if (delta < tol) {
617  found = true;
618  z0loc = z0p[n];
619  break;
620  }
621  }
622  AMREX_ASSERT_WITH_MESSAGE(found, "Location read from terrain file does not match the grid!");
623  amrex::ignore_unused(found);
624  z0_arr(i,j,klo) = z0loc;
625  }
626  });
627  } // Is ioproc
628 
629  int ioproc = ParallelDescriptor::IOProcessorNumber();
630  ParallelDescriptor::Barrier();
631  ParallelDescriptor::Bcast(z_0[lev].dataPtr(),z_0[lev].box().numPts(),ioproc);
632  } else {
633  // Create a BC mapper that uses FOEXTRAP at domain bndry
634  Vector<int> bc_lo(3,ERFBCType::foextrap);
635  Vector<int> bc_hi(3,ERFBCType::foextrap);
636  Vector<BCRec> bcr; bcr.push_back(BCRec(bc_lo.data(),bc_hi.data()));
637 
638  // Create ref ratio
639  IntVect ratio;
640  for (int idim = 0; idim < AMREX_SPACEDIM; ++idim) {
641  ratio[idim] = m_geom[lev].Domain().length(idim) / m_geom[0].Domain().length(idim);
642  }
643 
644  // Create interp object and interpolate from the coarsest grid
645  Interpolater* interp = &cell_cons_interp;
646  interp->interp(z_0[0] , 0,
647  z_0[lev], 0,
648  1, z_0[lev].box(),
649  ratio, m_geom[0], m_geom[lev],
650  bcr, 0, 0, RunOn::Gpu);
651  }
652 }
@ m_y
Definition: ERF_DataStruct.H:23
@ m_x
Definition: ERF_DataStruct.H:22
@ foextrap
Definition: ERF_IndexDefines.H:208

Referenced by make_SurfaceLayer_at_level().

Here is the caller graph for this function:

◆ update_fluxes()

void SurfaceLayer::update_fluxes ( const int &  lev,
const amrex::Real &  time,
int  max_iters = 500 
)

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

Parameters
[in]levCurrent level
[in]max_itersmaximum iterations to use
15 {
16  // Update SST data if we have a valid pointer
17  if (m_sst_lev[lev][0]) fill_tsurf_with_sst_and_tsk(lev, time);
18 
19  // TODO: we want 0 index to always be theta?
20  // Update land surface temp if we have a valid pointer
21  if (m_lsm_data_lev[lev][0]) get_lsm_tsurf(lev);
22 
23  // Fill interior ghost cells
24  t_surf[lev]->FillBoundary(m_geom[lev].periodicity());
25 
26  // Compute plane averages for all vars (regardless of flux type)
28 
29  // ***************************************************************
30  // Iterate the fluxes if moeng type
31  // First iterate over land -- the only model for surface roughness
32  // over land is RoughCalcType::CONSTANT
33  // ***************************************************************
36  bool is_land = true;
40  compute_fluxes(lev, max_iters, most_flux, is_land);
41  } else {
42  amrex::Abort("Unknown value for rough_type_land");
43  }
45  update_surf_temp(time);
48  compute_fluxes(lev, max_iters, most_flux, is_land);
49  } else {
50  amrex::Abort("Unknown value for rough_type_land");
51  }
52  } else if (theta_type == ThetaCalcType::ADIABATIC) {
54  adiabatic most_flux(m_ma.get_zref(), surf_temp_flux);
55  compute_fluxes(lev, max_iters, most_flux, is_land);
56  } else {
57  amrex::Abort("Unknown value for rough_type_land");
58  }
59  } else {
60  amrex::Abort("Unknown value for theta_type");
61  }
62  } // MOENG -- LAND
63 
64  // ***************************************************************
65  // Iterate the fluxes if moeng type
66  // Next iterate over sea -- the models for surface roughness
67  // over sea are CHARNOCK, DONELAN, MODIFIED_CHARNOCK or WAVE_COUPLED
68  // ***************************************************************
71  bool is_land = false;
75  compute_fluxes(lev, max_iters, most_flux, is_land);
78  compute_fluxes(lev, max_iters, most_flux, is_land);
79  } else if (rough_type_sea == RoughCalcType::DONELAN) {
81  compute_fluxes(lev, max_iters, most_flux, is_land);
84  compute_fluxes(lev, max_iters, most_flux, is_land);
85  } else {
86  amrex::Abort("Unknown value for rough_type_sea");
87  }
88 
90  update_surf_temp(time);
93  compute_fluxes(lev, max_iters, most_flux, is_land);
96  compute_fluxes(lev, max_iters, most_flux, is_land);
97  } else if (rough_type_sea == RoughCalcType::DONELAN) {
99  compute_fluxes(lev, max_iters, most_flux, is_land);
102  compute_fluxes(lev, max_iters, most_flux, is_land);
103  } else {
104  amrex::Abort("Unknown value for rough_type_sea");
105  }
106 
107  } else if (theta_type == ThetaCalcType::ADIABATIC) {
110  compute_fluxes(lev, max_iters, most_flux, is_land);
113  compute_fluxes(lev, max_iters, most_flux, is_land);
114  } else if (rough_type_sea == RoughCalcType::DONELAN) {
116  compute_fluxes(lev, max_iters, most_flux, is_land);
119  compute_fluxes(lev, max_iters, most_flux, is_land);
120  } else {
121  amrex::Abort("Unknown value for rough_type_sea");
122  }
123  } else {
124  amrex::Abort("Unknown value for theta_type");
125  }
126 
127  } // MOENG -- SEA
128 
130  u_star[lev]->setVal(custom_ustar);
131  t_star[lev]->setVal(custom_tstar);
132  q_star[lev]->setVal(custom_qstar);
133  }
134 }
void compute_averages(const int &lev)
Definition: ERF_MOSTAverage.cpp:679
void update_surf_temp(const amrex::Real &time)
Definition: ERF_SurfaceLayer.H:423
void get_lsm_tsurf(const int &lev)
Definition: ERF_SurfaceLayer.cpp:491
void compute_fluxes(const int &lev, const int &max_iters, const FluxIter &most_flux, bool is_land)
Definition: ERF_SurfaceLayer.cpp:145
void fill_tsurf_with_sst_and_tsk(const int &lev, const amrex::Real &time)
Definition: ERF_SurfaceLayer.cpp:418
Definition: ERF_MOSTStress.H:136
Definition: ERF_MOSTStress.H:270
Definition: ERF_MOSTStress.H:207
Definition: ERF_MOSTStress.H:333
Definition: ERF_MOSTStress.H:90
Definition: ERF_MOSTStress.H:487
Definition: ERF_MOSTStress.H:677
Definition: ERF_MOSTStress.H:586
Definition: ERF_MOSTStress.H:765
Definition: ERF_MOSTStress.H:402
Definition: ERF_MOSTStress.H:956
Definition: ERF_MOSTStress.H:1164
Definition: ERF_MOSTStress.H:1064
Definition: ERF_MOSTStress.H:1261
Definition: ERF_MOSTStress.H:862

◆ 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
444  { m_ma.update_field_ptrs(lev,vars_old,Theta_prim,Qv_prim,Qr_prim); }
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:246
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 int  RhoQv_comp,
const int  RhoQc_comp,
const int  RhoQr_comp 
)
530 {
532  MYNNPBLH estimator;
533  compute_pblh(lev, vars, z_phys_cc, estimator, RhoQv_comp, RhoQc_comp, RhoQr_comp);
534  } else if (pblh_type == PBLHeightCalcType::YSU) {
535  amrex::Error("YSU PBLH calc not implemented yet");
536  }
537 }
void compute_pblh(const int &lev, amrex::Vector< amrex::Vector< amrex::MultiFab >> &vars, amrex::MultiFab *z_phys_cc, const PBLHeightEstimator &est, const int RhoQv_comp, const int RhoQc_comp, const int RhoQr_comp)
Definition: ERF_PBLHeight.H:8

◆ update_surf_temp()

void SurfaceLayer::update_surf_temp ( const amrex::Real &  time)
inline
424  {
425  if (surf_heating_rate != 0) {
426  int nlevs = m_geom.size();
427  for (int lev = 0; lev < nlevs; lev++)
428  {
429  t_surf[lev]->setVal(surf_temp + surf_heating_rate*time);
430  amrex::Print() << "Surface temp at t=" << time
431  << ": "
432  << surf_temp + surf_heating_rate*time
433  << std::endl;
434  }
435  }
436  }

Member Data Documentation

◆ cnk_a

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

Referenced by SurfaceLayer().

◆ cnk_visc

bool SurfaceLayer::cnk_visc {false}
private

Referenced by SurfaceLayer().

◆ custom_qstar

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

Referenced by SurfaceLayer().

◆ custom_tstar

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

Referenced by SurfaceLayer().

◆ custom_ustar

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

Referenced by SurfaceLayer().

◆ default_land_surf_temp

amrex::Real SurfaceLayer::default_land_surf_temp = 300.
private

◆ depth

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

Referenced by SurfaceLayer().

◆ flux_type

FluxCalcType SurfaceLayer::flux_type {FluxCalcType::MOENG}

◆ m_bdy_time_interval

amrex::Real SurfaceLayer::m_bdy_time_interval
private

◆ m_eddyDiffs_lev

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

◆ m_geom

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

◆ m_Hwave_lev

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

◆ m_include_wstar

bool SurfaceLayer::m_include_wstar = false
private

Referenced by SurfaceLayer().

◆ m_lmask_lev

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

◆ m_lsm_data_lev

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

◆ m_lsm_flux_lev

amrex::Vector<amrex::Vector<amrex::MultiFab*> > SurfaceLayer::m_lsm_flux_lev
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_bdy_time

amrex::Real SurfaceLayer::m_start_bdy_time
private

◆ m_tsk_lev

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

◆ m_var_z0

bool SurfaceLayer::m_var_z0 {false}
private

◆ olen

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

◆ pblh

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

◆ pblh_type

PBLHeightCalcType SurfaceLayer::pblh_type {PBLHeightCalcType::None}

Referenced by SurfaceLayer().

◆ q_star

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

◆ rough_type_land

RoughCalcType SurfaceLayer::rough_type_land {RoughCalcType::CONSTANT}

Referenced by SurfaceLayer().

◆ rough_type_sea

RoughCalcType SurfaceLayer::rough_type_sea {RoughCalcType::CHARNOCK}

◆ surf_heating_rate

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

Referenced by SurfaceLayer(), and update_surf_temp().

◆ surf_temp

amrex::Real SurfaceLayer::surf_temp
private

Referenced by SurfaceLayer(), and update_surf_temp().

◆ surf_temp_flux

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

Referenced by SurfaceLayer().

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

◆ u_star

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

◆ use_moisture

bool SurfaceLayer::use_moisture
private

Referenced by SurfaceLayer().

◆ w_star

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

◆ z0_const

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

◆ z_0

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

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