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

#include <ERF_SLM.H>

Inheritance diagram for SLM:
Collaboration diagram for SLM:

Public Member Functions

 SLM ()
 
virtual ~SLM ()=default
 
void Define (SolverChoice &sc) override
 
void Init (const int &, const amrex::MultiFab &cons_in, const amrex::MultiFab &u_in, const amrex::MultiFab &v_in, const amrex::Geometry &geom, const amrex::Geometry &geom0, amrex::Vector< amrex::BCRec > &domain_bcs_type, amrex::IntVect &refRatio, const amrex::Real &dt, std::unique_ptr< amrex::MultiFab > &z_phys_nd_in, amrex::Vector< amrex::Vector< std::string >> &nc_init_file) override
 
void Lsm_Regrid_Level (const int &lev, const amrex::MultiFab &cons_in, const amrex::MultiFab &u_in, const amrex::MultiFab &v_in, const amrex::Geometry &geom, const amrex::Geometry &geom0, amrex::Vector< amrex::BCRec > &domain_bcs_type, amrex::IntVect &refRatio, const amrex::Real &dt, std::unique_ptr< amrex::MultiFab > &z_phys_nd_in, amrex::Vector< amrex::Vector< std::string >> &nc_init_file, const amrex::Geometry &source_geom, const amrex::Geometry &source_lsm_geom, const amrex::IntVect &source_ref_ratio, const bool has_source) override
 
void Advance (const double &dt) override
 
void Advance (const amrex::Real &dt, const amrex::Real &t_new, const amrex::Real &t_start) override
 
void AdvanceSLM ()
 
amrex::MultiFab * Lsm_Data_Ptr (const int &varIdx) override
 
amrex::MultiFab * Lsm_Flux_Ptr (const int &varIdx) override
 
amrex::Geometry Lsm_Geom () override
 
amrex::Geometry Lsm_Atmos_Geom () override
 
void Lsm_Set_Source_Data_Ptr (const int &varIdx, amrex::MultiFab *source_data_ptr) override
 
void Lsm_Set_Source_Flux_Ptr (const int &varIdx, amrex::MultiFab *source_flux_ptr) override
 
int Lsm_Unmapped_Size () override
 
amrex::MultiFab * Lsm_Unmapped_Ptr (const int &varIdx) override
 
void Lsm_Set_Source_Unmapped_Ptr (const int &varIdx, amrex::MultiFab *source_data_ptr) override
 
bool Lsm_Has_Atmos_State () override
 
void Lsm_Set_Source_Has_Atmos_State (bool source_has_atmos) override
 
void Lsm_Interpolate_From_Source (const amrex::Geometry &source_geom, const amrex::Geometry &source_lsm_geom, const amrex::IntVect &ref_ratio, LSMTransferMode mode) override
 
int Lsm_Data_Size () override
 
int Lsm_Flux_Size () override
 
std::string Lsm_DataName (const int &varIdx) override
 
int Lsm_DataIndex (const std::string &varname) override
 
std::string Lsm_FluxName (const int &varIdx) override
 
int Lsm_FluxIndex (const std::string &varname) override
 
std::unordered_map< std::string, std::string > & Lsm_WRFInputNames () override
 
void Copy_State_to_Lsm (const amrex::MultiFab &cons_in, const amrex::MultiFab &u_in, const amrex::MultiFab &v_in) override
 
void Copy_Lsm_to_State (amrex::MultiFab &cons_in) override
 
void Update_Lsm_Vars (amrex::MultiFab &cons_in, amrex::MultiFab &u_in, amrex::MultiFab &v_in) override
 
bool Get_Update_Status () override
 
void Update_State_Vars (amrex::MultiFab &cons_in) override
 
void set_precip_input (const amrex::MultiFab *precip_in) override
 
void set_terrain_inputs (const amrex::Vector< std::unique_ptr< amrex::MultiFab >> &sst_in, const amrex::Vector< std::unique_ptr< amrex::iMultiFab >> &lmask_in) override
 
int Lsm_Type () override
 
void writeSLM_Data (const PlotFileType plotfile_type, const amrex::Real cur_time, const std::string plot_prefix, const int level_step, const int lev, const int finest_lev, amrex::MultiFab &fab, amrex::Geometry &geom, amrex::Vector< std::string > &varnames)
 
void init_from_inputs ()
 
void slm_init ()
 
void init_layer_depths ()
 
void init_soil_vars ()
 
void init_landtype ()
 
void init_soil_tw ()
 
void init_slm_vars ()
 
void init_wrfinput_vars ()
 
void initialize_surface_outputs ()
 
void rebuild_restart_fields ()
 
SLMParameterTable ReadParameterFile (const std::string &filename, amrex::Vector< std::string > &veg_categories, amrex::Vector< std::string > &soil_categories)
 
void validate_parameter_tables ()
 
void init_from_params ()
 
void load_builtin_radiation_parameters ()
 
void UpdateLAI (const amrex::MFIter &mfi)
 
void UpdateLAIParameters (const amrex::MFIter &mfi)
 
void radiation_noahmp (const amrex::MFIter &mfi)
 
void albedo_noahmp (const amrex::MFIter &mfi, int i, int j, amrex::Real dt, amrex::Real cosz, amrex::Real snowh, amrex::Real fsno, amrex::Real fwet, amrex::Real sneqvo, amrex::Real sneqv, amrex::Real qsnow, amrex::Real tg, amrex::Real tv, amrex::Real elai, amrex::Real esai, const amrex::Real *smc, int nsoil, amrex::Real fveg, amrex::Real &albold, amrex::Real &tauss, amrex::Real *albgrd, amrex::Real *albgri, amrex::Real *albd, amrex::Real *albi, amrex::Real *fabd, amrex::Real *fabi, amrex::Real *ftdd, amrex::Real *ftid, amrex::Real *ftii, amrex::Real &fsun, amrex::Real *frevd, amrex::Real *frevi, amrex::Real *fregd, amrex::Real *fregi, amrex::Real &bgap, amrex::Real &wgap, amrex::Real *albsnd, amrex::Real *albsni)
 
void transfer_coeff (const amrex::MFIter &mfi)
 
void resistances (const amrex::MFIter &mfi)
 
void fluxes_canopy (const amrex::MFIter &mfi)
 
void solve_ground_skin_temperature (const amrex::MFIter &mfi)
 
void soil_water (const amrex::MFIter &mfi)
 
void soil_temperature (const amrex::MFIter &mfi)
 
void soil_nudging (const amrex::MFIter &mfi3d)
 
void set_dt (const amrex::Real &dt)
 
const amrex::Vector< const amrex::MultiFab * > & export_to_RRTMGP ()
 
std::unordered_map< std::string, std::string > & get_rad_output_map ()
 
const std::vector< amrex::Real > & get_soil_dz () const
 
void WriteCheckpoint (const int &lev, const std::string &checkpointname) const override
 
void ReadCheckpoint (const int &lev, const std::string &checkpointname) override
 
void initialize_zrefxy ()
 
void initialize_processed_state (const bool preserve_checkpoint_fields=false)
 
virtual void Init (const int &, const amrex::MultiFab &, const amrex::Geometry &, const amrex::Geometry &, amrex::Vector< amrex::BCRec > &, amrex::IntVect &, const amrex::Real &, amrex::Vector< amrex::Vector< std::string >> &)
 
virtual void Init (const int &lev, const amrex::MultiFab &cons_in, const amrex::MultiFab &, const amrex::MultiFab &, const amrex::Geometry &geom, const amrex::Geometry &geom0, amrex::Vector< amrex::BCRec > &domain_bcs_type, amrex::IntVect &refRatio, const amrex::Real &dt_advance, std::unique_ptr< amrex::MultiFab > &, amrex::Vector< amrex::Vector< std::string >> &nc_init_file)
 
- Public Member Functions inherited from NullSurf
 NullSurf ()
 
virtual ~NullSurf ()=default
 
virtual void Init (const int &, const amrex::MultiFab &, const amrex::Geometry &, const amrex::Geometry &, amrex::Vector< amrex::BCRec > &, amrex::IntVect &, const amrex::Real &, amrex::Vector< amrex::Vector< std::string >> &)
 
virtual void Advance_With_State (const int &, amrex::MultiFab &, amrex::MultiFab &, amrex::MultiFab &, amrex::MultiFab *, amrex::MultiFab *, const SurfacePrecipAccumulationSources &, const amrex::Real &, const amrex::Real &, const int &, const bool, amrex::MultiFab *)
 
virtual void Plot_Landfile (const int &)
 
virtual void Update_Micro_Vars (amrex::MultiFab &)
 
virtual void Copy_State_to_Micro (const amrex::MultiFab &)
 
virtual void Copy_Micro_to_State (amrex::MultiFab &)
 
virtual void Lsm_Set_Lev0_Data_Ptr (const int &, amrex::MultiFab *)
 
virtual void Lsm_Set_Lev0_Flux_Ptr (const int &, amrex::MultiFab *)
 
virtual bool Runs_Own_Land_Driver () const
 
virtual int Get_LSM_Step () const
 
virtual void Set_LSM_Step (int)
 
virtual void Write_Lsm_Restart (const std::string &) const
 
virtual void Read_Lsm_Restart (const std::string &)
 

Static Public Member Functions

AMREX_GPU_HOST_DEVICE static AMREX_FORCE_INLINE void surrad_noahmp (amrex::Real mpe, amrex::Real fsun, amrex::Real fsha, amrex::Real elai, amrex::Real vai, amrex::Real laisun, amrex::Real laisha, const amrex::Real *solad, const amrex::Real *solai, const amrex::Real *fabd, const amrex::Real *fabi, const amrex::Real *ftdd, const amrex::Real *ftid, const amrex::Real *ftii, const amrex::Real *albgrd, const amrex::Real *albgri, const amrex::Real *albd, const amrex::Real *albi, const amrex::Real *frevd, const amrex::Real *frevi, const amrex::Real *fregd, const amrex::Real *fregi, amrex::Real &parsun, amrex::Real &parsha, amrex::Real &sav, amrex::Real &sag, amrex::Real &fsa, amrex::Real &fsr, amrex::Real &fsrv, amrex::Real &fsrg)
 
AMREX_GPU_HOST_DEVICE static AMREX_FORCE_INLINE void snow_age_noahmp (amrex::Real dt, amrex::Real tg, amrex::Real sneqvo, amrex::Real sneqv, amrex::Real tau0, amrex::Real grain_growth, amrex::Real extra_growth, amrex::Real dirt_soot, amrex::Real swemx, amrex::Real &tauss, amrex::Real &fage)
 
AMREX_GPU_HOST_DEVICE static AMREX_FORCE_INLINE void snowalb_bats_noahmp (int nband, amrex::Real fsno, amrex::Real cosz, amrex::Real fage, amrex::Real bats_cosz, amrex::Real bats_vis_new, amrex::Real bats_nir_new, amrex::Real bats_vis_age, amrex::Real bats_nir_age, amrex::Real bats_vis_dir, amrex::Real bats_nir_dir, amrex::Real *albsnd, amrex::Real *albsni)
 
AMREX_GPU_HOST_DEVICE static AMREX_FORCE_INLINE void groundalb_noahmp (int nsoil, int nband, int ice, int ist, amrex::Real fsno, const amrex::Real *smc, const amrex::Real *albsnd, const amrex::Real *albsni, amrex::Real cosz, amrex::Real tg, const amrex::Real *albsat, const amrex::Real *albdry, const amrex::Real *alblak, amrex::Real *albgrd, amrex::Real *albgri)
 
AMREX_GPU_HOST_DEVICE static AMREX_FORCE_INLINE void twostream_noahmp (int ib, int ic, int vegtyp, amrex::Real cosz, amrex::Real vai, amrex::Real fwet, amrex::Real t, const amrex::Real *albgrd, const amrex::Real *albgri, const amrex::Real *rho, const amrex::Real *tau, amrex::Real fveg, int ist, amrex::Real xl, amrex::Real omegas_param, amrex::Real betads, amrex::Real betais, int opt_rad, amrex::Real rc, amrex::Real hvt, amrex::Real hvb, amrex::Real den, amrex::Real *fab, amrex::Real *fre, amrex::Real *ftd, amrex::Real *fti, amrex::Real &gdir, amrex::Real *frev, amrex::Real *freg, amrex::Real &bgap, amrex::Real &wgap, amrex::Real &xl_out, amrex::Real &chil_out, amrex::Real &phi1_out, amrex::Real &phi2_out)
 
AMREX_GPU_HOST_DEVICE static AMREX_FORCE_INLINE amrex::Real fh_calc (const amrex::Real &t, const amrex::Real &mps, const amrex::Real &sw, const amrex::Real &B)
 
AMREX_GPU_HOST_DEVICE static AMREX_FORCE_INLINE amrex::Real linear_interp (const amrex::Real t0, const amrex::Real t1, const amrex::Real t, const amrex::Real x, const amrex::Real y)
 
static void GotoNextLine (std::istream &is)
 

Private Types

using FabPtr = std::shared_ptr< amrex::MultiFab >
 

Private Attributes

int m_lsm_data_size = 44
 
int m_lsm_flux_size = 5
 
amrex::Vector< int > LsmDataMap
 
amrex::Vector< int > LsmFluxMap
 
amrex::Vector< std::string > LsmDataName
 
amrex::Vector< std::string > LsmFluxName
 
amrex::Geometry m_geom
 
amrex::Geometry m_geom0
 
amrex::Geometry m_lsm_geom
 
amrex::Geometry m_lsm_geom0
 
amrex::Vector< amrex::BCRec > m_domain_bcs_type
 
amrex::BoxArray ba_lsm_2d
 
amrex::Real m_dt
 
int khi_lsm
 
int klo_lsm
 
amrex::Array< FabPtr, LsmVar_SLM::NumVars > lsm_fab_vars
 
amrex::Array< FabPtr, LsmFlux_SLM::NumVars > lsm_fab_flux
 
amrex::Vector< amrex::MultiFab * > source_data
 
amrex::Vector< amrex::MultiFab * > source_flux
 
amrex::Vector< amrex::MultiFab * > source_unmapped
 
int m_nz_lsm = 7
 
std::vector< amrex::Real > m_dz_lsm
 
std::string rad_input_file
 
int landtype0 = 16
 
amrex::Real LAI0 = zero
 
amrex::Vector< amrex::Real > clay0
 
amrex::Vector< amrex::Real > sand0
 
amrex::Vector< amrex::Real > sw0
 
amrex::Vector< amrex::Real > st0
 
amrex::Vector< amrex::Real > relax_hgt
 
amrex::Real tabs_s = zero
 
amrex::Real t00 = 300.0
 
bool use_terrain = false
 
bool dosoilwnudging = false
 
bool dosoiltnudging = false
 
amrex::Real tausoil = 86400.0
 
amrex::Real z0_soil = 0.01
 
amrex::Real mws_mx0 = 50.0
 
amrex::Real Rc_max = 5000.0
 
amrex::Real T_opt = 298.0
 
amrex::Real zref = myhalf
 
amrex::MultiFab zrefxy
 
amrex::MultiFab t_canop
 
amrex::MultiFab mw
 
amrex::MultiFab mws
 
amrex::MultiFab t_skin
 
amrex::MultiFab t_ground_skin
 
amrex::MultiFab t_sfc
 
amrex::MultiFab q_sfc
 
amrex::MultiFab t_cas
 
amrex::MultiFab q_cas
 
amrex::MultiFab q_gr
 
amrex::MultiFab sdew
 
amrex::iMultiFab landmask
 
amrex::iMultiFab landtype
 
amrex::iMultiFab nroot
 
amrex::MultiFab LAI
 
amrex::MultiFab SAI
 
amrex::MultiFab sstxy
 
amrex::iMultiFab vegetype
 
amrex::MultiFab vege_YES
 
amrex::MultiFab cp_vege
 
amrex::MultiFab cbiom
 
amrex::MultiFab dleaf
 
amrex::MultiFab z0_sfc
 
amrex::MultiFab Khai_L
 
amrex::MultiFab phi_1
 
amrex::MultiFab phi_2
 
amrex::MultiFab IR_emis_vege
 
amrex::MultiFab IR_emis_soil
 
amrex::MultiFab IR_emis_grnd
 
amrex::MultiFab ztop
 
amrex::MultiFab disp_hgt
 
amrex::MultiFab Rgl
 
amrex::MultiFab Rc_min
 
amrex::MultiFab hs_rc
 
amrex::MultiFab precip_extinc
 
amrex::MultiFab precip_sfc
 
amrex::MultiFab mw_mx
 
amrex::MultiFab mws_mx
 
amrex::MultiFab BAI
 
amrex::MultiFab IMPERV
 
amrex::MultiFab soilt_vars
 
amrex::MultiFab soilw_vars
 
amrex::MultiFab mw_inc
 
amrex::MultiFab evapo_dry
 
amrex::MultiFab evapo_wet
 
amrex::MultiFab shf_canop
 
amrex::MultiFab shf_soil
 
amrex::MultiFab shf_air
 
amrex::MultiFab lhf_canop
 
amrex::MultiFab lhf_soil
 
amrex::MultiFab lhf_air
 
amrex::MultiFab evp_canop
 
amrex::MultiFab evp_soil
 
amrex::MultiFab evp_air
 
amrex::MultiFab albedovis_v
 
amrex::MultiFab albedonir_v
 
amrex::MultiFab albedovis_s
 
amrex::MultiFab albedonir_s
 
amrex::MultiFab r_a
 
amrex::MultiFab r_b
 
amrex::MultiFab r_c
 
amrex::MultiFab r_d
 
amrex::MultiFab r_soil
 
amrex::MultiFab net_rad
 
amrex::MultiFab wet_canop
 
amrex::Real time = zero
 
amrex::Real start_time = -1.0
 
bool first_step = true
 
bool m_updated = false
 
std::string parameter_file = "NoahmpTable.TBL"
 
std::string veg_dataset = "modis"
 
std::string soil_dataset = "stas"
 
int num_landtypes = -1
 
int num_veg_params = -1
 
int num_soil_params = -1
 
SLMParameterTable full_params
 
amrex::Vector< std::string > param_veg_categories
 
amrex::Vector< std::string > param_soil_categories
 
std::unordered_map< std::string, std::unique_ptr< amrex::Gpu::DeviceVector< amrex::Real > > > d_soil_params
 
std::unordered_map< std::string, std::unique_ptr< amrex::Gpu::DeviceVector< amrex::Real > > > d_veg_params
 
std::unordered_map< std::string, std::unique_ptr< amrex::Gpu::DeviceVector< amrex::Real > > > d_rad_params
 
std::vector< std::vector< amrex::Real > > sai_table
 
std::vector< std::vector< amrex::Real > > lai_table
 
amrex::Gpu::DeviceVector< amrex::Real > d_lai_curr
 
amrex::Gpu::DeviceVector< amrex::Real > d_lai_next
 
amrex::Gpu::DeviceVector< amrex::Real > d_sai_curr
 
amrex::Gpu::DeviceVector< amrex::Real > d_sai_next
 
bool use_param_file = false
 
bool interpolate_lai = false
 
bool use_wrf_lai = false
 
bool params_updated = false
 
int opt_rad = 1
 
int opt_alb = 1
 
amrex::Real betads_rad = myhalf
 
amrex::Real betais_rad = myhalf
 
amrex::Real tau0_rad = 1.0e6
 
amrex::Real grain_growth_rad = 5000.
 
amrex::Real extra_growth_rad = 10.
 
amrex::Real dirt_soot_rad = 0.3
 
amrex::Real rsurf_exp = 5.0
 
amrex::Real bats_cosz_rad = two
 
amrex::Real bats_vis_new_rad = 0.95
 
amrex::Real bats_nir_new_rad = 0.65
 
amrex::Real bats_vis_age_rad = 0.2
 
amrex::Real bats_nir_age_rad = myhalf
 
amrex::Real bats_vis_dir_rad = 0.4
 
amrex::Real bats_nir_dir_rad = 0.4
 
amrex::Real swemx_rad = one
 
amrex::Real snow_emis_rad = 0.95
 
amrex::Real eg_soil_rad = 0.97
 
amrex::Real eg_lake_rad = 0.98
 
amrex::MultiFab albold_noahmp
 
amrex::MultiFab tauss_noahmp
 
std::vector< std::vector< amrex::Real > > param_table
 
amrex::TableData< amrex::Real, 2 > d_param_table
 
amrex::Real m_calday = -1.0
 
int m_orbital_year = -1
 
int m_orbital_mon = -1
 
int m_orbital_day = -1
 
int m_orbital_sec = -1
 
amrex::MultiFab * z_phys_nd
 
amrex::MultiFab slm_diag
 
amrex::Vector< const amrex::MultiFab * > slm_to_rad_vars
 
bool use_wrfinput = false
 
InitType m_init_type = InitType::None
 
bool wrfinput_initialized = false
 
bool has_atmos_state = false
 
bool source_has_atmos_state = false
 
bool init_from_atmos_pending = false
 
amrex::Vector< int > unmapped_fields
 

Static Private Attributes

static constexpr amrex::Real t_canop_max = 343.
 
static constexpr amrex::Real tfriz = 273.15
 
static constexpr amrex::Real epsv = 0.61
 
static constexpr amrex::Real cp = 1004.0
 
static constexpr amrex::Real cp_water = 4.1796e6
 
static constexpr amrex::Real sigma = 5.67e-8
 
static constexpr amrex::Real IR_emis_urban = 0.9
 
static constexpr amrex::Real r_litter = zero
 
static std::unordered_map< std::string, std::string > wrfinput_map
 
static std::unordered_map< std::string, std::string > rad_output_map
 
static const std::unordered_set< int > surf_vars
 
static constexpr const char * LsmVarName_Full []
 
static const std::vector< std::string > diag_names
 

Detailed Description

The Simplified Land-surface Model (SLM) supplies lower-boundary sensible-heat, latent-heat, and momentum fluxes for land cells.

The original formulation and evaluation of SLM are documented in Lee and Khairoutdinov (2015), "A Simplified Land Model (SLM) for use in cloud-resolving models: Formulation and evaluation," Journal of Advances in Modeling Earth Systems, 7, 1368–1392, https://doi.org/10.1002/2014MS000419.

SLM includes selected formulations adapted from NoahMP, updates inherited from gSAM-SLM, and SLM-specific corrections to the surface energy and water budgets. It remains a simplified land model and is not a complete implementation of NoahMP.

Member Typedef Documentation

◆ FabPtr

using SLM::FabPtr = std::shared_ptr<amrex::MultiFab>
private

Constructor & Destructor Documentation

◆ SLM()

SLM::SLM ( )
inline
193 {}

◆ ~SLM()

virtual SLM::~SLM ( )
virtualdefault

Member Function Documentation

◆ Advance() [1/2]

void SLM::Advance ( const amrex::Real &  dt,
const amrex::Real &  t_new,
const amrex::Real &  t_start 
)
inlineoverridevirtual

Reimplemented from NullSurf.

275  {
276  m_updated = false;
277  m_dt = dt;
278  time = t_new;
279  start_time = t_start;
280 
281  if (first_step)
282  {
283  first_step = false;
284 
286  this->init_wrfinput_vars();
287  }
288 
290  }
291 
292  this->AdvanceSLM();
293  m_updated = true;
294 
295  }
amrex::Real start_time
Definition: ERF_SLM.H:881
amrex::Real m_dt
Definition: ERF_SLM.H:725
bool wrfinput_initialized
Definition: ERF_SLM.H:1014
amrex::Real time
Definition: ERF_SLM.H:879
void init_wrfinput_vars()
Definition: ERF_SLM.cpp:1061
bool first_step
Definition: ERF_SLM.H:882
void AdvanceSLM()
Definition: ERF_SLM.cpp:3043
void initialize_zrefxy()
Definition: ERF_SLM.cpp:1369
bool use_wrfinput
Definition: ERF_SLM.H:1012
bool m_updated
Definition: ERF_SLM.H:883
Here is the call graph for this function:

◆ Advance() [2/2]

void SLM::Advance ( const double &  dt)
inlineoverridevirtual

Reimplemented from NullSurf.

267  {
268  Advance(static_cast<amrex::Real>(dt), time + static_cast<amrex::Real>(dt), start_time);
269  }
amrex::Real Real
Definition: ERF_ShocInterface.H:19
void Advance(const double &dt) override
Definition: ERF_SLM.H:266

◆ AdvanceSLM()

void SLM::AdvanceSLM ( )
3044 {
3045  // Expose for GPU copy
3046  Real dt = m_dt;
3047  const int d_khi_lsm = khi_lsm;
3048 
3049  net_rad.setVal(0.0, SLM_NetRad::net_sw1, 1, 0);
3050  net_rad.setVal(0.0, SLM_NetRad::net_sw2, 1, 0);
3051  net_rad.setVal(0.0, SLM_NetRad::net_rad1, 1, 0);
3052  net_rad.setVal(0.0, SLM_NetRad::net_rad2, 1, 0);
3053  mw_inc.setVal(zero);
3054 
3055  // Soil temperature and moisture nudging
3056  for ( MFIter mfi(*lsm_fab_vars[LsmVar_SLM::tsurf], TileNoZ()); mfi.isValid(); ++mfi) {
3057  soil_nudging(mfi);
3058  }
3059 
3060  for ( MFIter mfi(landtype, TileNoZ()); mfi.isValid(); ++mfi) {
3061  auto box = mfi.tilebox();
3062 
3063  auto landmask_arr = landmask.const_array(mfi);
3064 
3065  auto LAI_arr = LAI.const_array(mfi);
3066  auto precip_extinc_arr = precip_extinc.const_array(mfi);
3067 
3068  auto mw_arr = mw.array(mfi);
3069  auto mws_arr = mws.const_array(mfi);
3070  auto mw_mx_arr = mw_mx.const_array(mfi);
3071 
3072  auto mw_inc_arr = mw_inc.array(mfi);
3073 
3074  auto t_cas_arr = t_cas.array(mfi);
3075  auto q_cas_arr = q_cas.array(mfi);
3076 
3077  auto t_sfc_arr = t_sfc.array(mfi);
3078  auto q_sfc_arr = q_sfc.array(mfi);
3079 
3080  auto q_gr_arr = q_gr.array(mfi);
3081 
3082  auto t_canop_arr = t_canop.array(mfi);
3083  auto t_skin_arr = t_skin.const_array(mfi);
3084  auto t_ground_skin_arr = t_ground_skin.const_array(mfi);
3085 
3086  auto soilt_arr = lsm_fab_vars[LsmVar_SLM::soilt]->const_array(mfi);
3087  auto soilw_arr = lsm_fab_vars[LsmVar_SLM::soilw]->const_array(mfi);
3088 
3089  auto m_pot_sat_arr = lsm_fab_vars[LsmVar_SLM::m_pot_sat]->const_array(mfi);
3090  auto Bconst_arr = lsm_fab_vars[LsmVar_SLM::Bconst]->const_array(mfi);
3091 
3092  auto ustar_arr = lsm_fab_vars[LsmVar_SLM::ustar]->const_array(mfi);
3093  auto qstar_arr = lsm_fab_vars[LsmVar_SLM::qstar]->array(mfi);
3094 
3095  auto SAI_arr = SAI.const_array(mfi);
3096  auto cbiom_arr = cbiom.const_array(mfi);
3097 
3098  auto shf_air_arr = shf_air.array(mfi);
3099 
3100  auto evp_air_arr = evp_air.array(mfi);
3101 
3102  auto vegetype_arr = vegetype.const_array(mfi);
3103  auto vege_YES_arr = vege_YES.const_array(mfi);
3104 
3105  auto cp_vege_arr = cp_vege.array(mfi);
3106 
3107  auto sdew_arr = sdew.array(mfi);
3108 
3109  auto tref_arr = lsm_fab_vars[LsmVar_SLM::tref]->const_array(mfi);
3110  auto dref_arr = lsm_fab_vars[LsmVar_SLM::dref]->const_array(mfi);
3111  auto qref_arr = lsm_fab_vars[LsmVar_SLM::qref]->const_array(mfi);
3112  auto pref_arr = lsm_fab_vars[LsmVar_SLM::pref]->const_array(mfi);
3113  auto precip_array = lsm_fab_vars[LsmVar_SLM::precipref]->const_array(mfi);
3114 
3115  auto tsurf_arr = lsm_fab_vars[LsmVar_SLM::tsurf]->array(mfi);
3116  auto tveg_arr = lsm_fab_vars[LsmVar_SLM::tv]->array(mfi);
3117  auto mveg_arr = lsm_fab_vars[LsmVar_SLM::mv]->array(mfi);
3118 
3119  auto r_a_arr = r_a.const_array(mfi);
3120  auto r_b_arr = r_b.const_array(mfi);
3121  auto r_c_arr = r_c.const_array(mfi);
3122  auto r_d_arr = r_d.const_array(mfi);
3123  auto r_soil_arr = r_soil.const_array(mfi);
3124 
3125  auto flbu_arr = lsm_fab_vars[LsmVar_SLM::flbu]->array(mfi);
3126  auto flbv_arr = lsm_fab_vars[LsmVar_SLM::flbv]->array(mfi);
3127  auto flbq_arr = lsm_fab_vars[LsmVar_SLM::flbq]->array(mfi);
3128  auto flbt_arr = lsm_fab_vars[LsmVar_SLM::flbt]->array(mfi);
3129  auto prsfc_arr = lsm_fab_vars[LsmVar_SLM::prsfc]->array(mfi);
3130 
3131  auto wet_canop_arr = wet_canop.const_array(mfi);
3132 
3133  // TODO: Copies for MOST
3134  auto fluxq_arr = lsm_fab_flux[LsmFlux_SLM::q_flux]->array(mfi);
3135  auto fluxt_arr = lsm_fab_flux[LsmFlux_SLM::t_flux]->array(mfi);
3136  auto tau13_arr = lsm_fab_flux[LsmFlux_SLM::tau13]->array(mfi);
3137  auto tau23_arr = lsm_fab_flux[LsmFlux_SLM::tau23]->array(mfi);
3138  auto olen_arr = lsm_fab_flux[LsmFlux_SLM::olen]->array(mfi);
3139 
3140  auto slm_diag_arr = slm_diag.array(mfi);
3141 
3142  // Update LAI and SAI based on current month
3143  UpdateLAI(mfi);
3144 
3145  // NOAHMP radiation calculation
3146  radiation_noahmp(mfi);
3147 
3148  ParallelFor( box, [=] AMREX_GPU_DEVICE (int i, int j, int)
3149  {
3150  if (landmask_arr(i, j, 0) != 1) {
3151  return;
3152  }
3153 
3156  amrex::Real cp_vege_tot, fh;
3157  // precipitation interception rate at canoppy
3158  // For baresoil, precip = 0, as LAI = 0
3159  precip = precip_array(i, j, 0) * (one - std::exp(-precip_extinc_arr(i, j, 0) * LAI_arr(i, j, 0)));
3160 
3161  if (mw_arr(i, j, 0) < mw_mx_arr(i, j, 0))
3162  {
3163  drain = zero;
3164  }
3165  else if(mw_arr(i, j, 0) > mw_mx_arr(i, j, 0))
3166  {
3167  // when water holding storage exceeds its maximum, no precipitation is intercepted
3168  drain = precip;
3169 
3170  // excess water storage gets drained from mw [kg/m^2]
3171  drain += std::max(mw_arr(i, j, 0) - mw_mx_arr(i, j, 0), amrex::Real(zero)) / dt; // mm/s
3172  }
3173 
3174  prsfc_arr(i, j, 0) = precip_array(i, j, 0) - precip + drain;
3175 
3176  // Update output variables
3177  slm_diag_arr(i, j, 0, SLM_Diag::precip) = precip;
3178 
3179  // Original SLM formula:
3180  // cp_vege_arr(i, j, 0) =
3181  // (LAI_arr(i, j, 0) * leaf_thickness * 0.001
3182  // + ztop_arr(i, j, 0) * BAI_arr(i, j, 0) / 43560.) * 900. * 2800.;
3183 
3184  // NoahMP dry canopy heat capacity: CBIOM * effective VAI * volumetric water heat capacity.
3185  const amrex::Real vai = std::min(amrex::Real(6.0), LAI_arr(i, j, 0) + SAI_arr(i, j, 0));
3186  cp_vege_arr(i, j, 0) = cbiom_arr(i, j, 0) * vai * cp_water * vege_YES_arr(i, j, 0);
3187  cp_vege_tot = cp_vege_arr(i, j, 0) + mw_arr(i, j, 0) * 1.e-3 * cp_water;
3188 
3189  // Add to vegetiation moisture increment from SLM::vapor_fluxes()
3190  mw_inc_arr(i, j, 0) += dt * (precip - drain);
3191 
3192  // Update vegetation moisture storage
3193  mw_arr(i, j, 0) += mw_inc_arr(i, j, 0);
3194 
3195  // from gSAM-SLM
3196  // intercepted precip cools the canopy:
3197  // temperature of intercepted rain is the same as reference level
3198  // note it cools even when water storage is full as old water on leaves is replaced by new rain water
3199  if (vegetype_arr(i, j, 0) == 1)
3200  {
3201  t_canop_arr(i, j, 0) = (cp_vege_tot * t_canop_arr(i, j, 0) + tref_arr(i, j, 0) * precip * dt * 1.e-3 * cp_water) / (cp_vege_tot + precip * dt * 1.e-3 * cp_water);
3202  }
3203 
3204  // Note:
3205  // Assign appropriate "surface level" values for each land type, for the calculation
3206  //of surface turbulent fluxes
3207  //For baresoil, surface level = soil surface
3208  //For vegetation, surface level = canopy level
3209  if (vegetype_arr(i, j, 0) == 1)
3210  {
3211  t_sfc_arr(i, j, 0) = t_cas_arr(i, j, 0);
3212  q_sfc_arr(i, j, 0) = q_cas_arr(i, j, 0);
3213  }
3214  else
3215  {
3216  t_sfc_arr(i, j, 0) = soilt_arr(i, j, d_khi_lsm);
3217  // Specific humidity at top soil
3218  if (soilt_arr(i, j, d_khi_lsm) > tfriz)
3219  {
3220  erf_qsatw(soilt_arr(i, j, d_khi_lsm), pref_arr(i, j, 0), q_gr_arr(i, j, 0));
3221  sdew_arr(i, j, 0) = one;
3222  if (mws_arr(i, j, 0) == zero)
3223  {
3224  fh = fh_calc(soilt_arr(i, j, d_khi_lsm), m_pot_sat_arr(i, j, d_khi_lsm), soilw_arr(i, j, d_khi_lsm), Bconst_arr(i, j, d_khi_lsm));
3225  if ( fh > 0.99)
3226  {
3227  sdew_arr(i, j, 0) = one;
3228  }
3229  else
3230  {
3231  sdew_arr(i, j, 0) = zero;
3232  }
3233  q_gr_arr(i, j, 0) *= fh;
3234  }
3235  }
3236  else
3237  {
3238  erf_qsati(soilt_arr(i, j, d_khi_lsm), pref_arr(i, j, 0), q_gr_arr(i, j, 0));
3239  sdew_arr(i, j, 0) = one;
3240  }
3241  q_sfc_arr(i, j, 0) = q_gr_arr(i, j, 0);
3242  }
3243  });
3244 
3245  // Calculate turbulent transfer coeff between reference level and surface
3246  transfer_coeff(mfi);
3247 
3248  // Calculate aerodynamic resistances + stomatal resistance
3249  resistances(mfi);
3250 
3251  //Subcycle in time to avoid swings of canopy temperature
3252  // because of small heat capacity of vegetation in curtain places and seasons
3253  // Of course, the best would be to use implicit scheme, but for now,
3254  // subcycling seems like a good fix.
3255  //from gSAM-SLM
3256  fluxes_canopy(mfi); // compute both sHF and LHF of canopy
3257 
3259 
3260  // Calculate soil moisture increment
3261  soil_water(mfi);
3262 
3263  // Advance the finite-volume soil using only the final skin-to-soil flux.
3264  soil_temperature(mfi);
3265 
3266  ParallelFor( box, [=] AMREX_GPU_DEVICE (int i, int j, int)
3267  {
3268  if (landmask_arr(i, j, 0) != 1) {
3269  return;
3270  }
3271 
3272  amrex::Real q_gr_loc;
3273 
3274  // SAM rhow[nz] = air density at vertical velocity levels, kg/m^3
3275  amrex::Real rhow = dref_arr(i, j, 0); // TODO: double check this
3276  amrex::Real cond_heat, cond_href, cond_hcnp, cond_hundercnp;
3277  amrex::Real cond_vapor, cond_vref, cond_vcnp, cond_vundercnp;
3278  amrex::Real cvh = 2 * LAI_arr(i, j, 0) / r_b_arr(i, j, 0);
3279  if (vegetype_arr(i, j, 0) == 1) {
3280  // Compute diagnostic variables at canopy air space level
3281  // Calculate heat conductances - non-zero only for canopy land type
3282  cond_heat = 1.0 / r_a_arr(i, j, 0) + cvh + 1.0 / r_d_arr(i, j, 0);
3283  cond_href = 1.0 / r_a_arr(i, j, 0) / cond_heat;
3284  cond_hcnp = cvh / cond_heat;
3285  cond_hundercnp = 1.0 / r_d_arr(i, j, 0) / cond_heat;
3286 
3287  // Calculate vapor conductances
3288  cond_vapor = 1.0 / r_a_arr(i, j, 0) + wet_canop_arr(i, j, 0) * LAI_arr(i, j, 0) / r_b_arr(i, j, 0) + (1.0 - wet_canop_arr(i, j, 0)) * LAI_arr(i, j, 0) /(r_b_arr(i, j, 0) + r_c_arr(i, j, 0)) + 1.0 / (r_d_arr(i, j, 0) + r_soil_arr(i, j, 0) + r_litter);
3289  cond_vref = 1.0 / r_a_arr(i, j, 0) / cond_vapor;
3290  cond_vcnp = (wet_canop_arr(i, j, 0) * LAI_arr(i, j, 0) / r_b_arr(i, j, 0) / cond_vapor + (1.0 - wet_canop_arr(i, j, 0)) * LAI_arr(i, j, 0) / (r_b_arr(i, j, 0) + r_c_arr(i, j, 0)) / cond_vapor);
3291  cond_vundercnp = 1.0 / (r_d_arr(i, j, 0) + r_soil_arr(i, j, 0) + r_litter) / cond_vapor;
3292  }
3293  else
3294  {
3295  cond_heat = 1./r_a_arr(i, j, 0);
3296  cond_href = one;
3297  cond_hcnp = zero;
3298  cond_hundercnp = zero;
3299 
3300  cond_vapor = 1./(r_a_arr(i, j, 0) + r_soil_arr(i, j, 0));
3301  cond_vref = one;
3302  cond_vcnp = zero;
3303  cond_vundercnp = zero;
3304  }
3305 
3306  t_cas_arr(i, j, 0) = tref_arr(i, j, 0)*cond_href + t_canop_arr(i, j, 0)*cond_hcnp + t_ground_skin_arr(i, j, 0)*cond_hundercnp;
3307 
3308  amrex::Real qsat_canop;
3309  if (t_canop_arr(i, j, 0) >= tfriz)
3310  {
3311  erf_qsatw(t_canop_arr(i, j, 0), pref_arr(i, j, 0), qsat_canop);
3312  }
3313  else
3314  {
3315  erf_qsati(t_canop_arr(i, j, 0), pref_arr(i, j, 0), qsat_canop);
3316  }
3317 
3318  q_gr_loc = q_gr_arr(i, j, 0);
3319  q_cas_arr(i, j, 0) = qref_arr(i, j, 0) * cond_vref + qsat_canop*cond_vcnp + q_gr_loc*cond_vundercnp;
3320 
3321  // Output variables
3322  tveg_arr(i, j, 0) = t_canop_arr(i, j, 0);
3323  tveg_arr(i, j, d_khi_lsm) = t_canop_arr(i, j, 0);
3324  mveg_arr(i, j, 0) = mw_arr(i, j, 0);
3325  mveg_arr(i, j, d_khi_lsm) = mw_arr(i, j, 0);
3326  tsurf_arr(i, j, d_khi_lsm) = t_skin_arr(i, j, 0); // TODO: ts in SLM is input and output - check how this should be coupled back to ERF
3327  tsurf_arr(i, j, 0) = tsurf_arr(i, j, d_khi_lsm); // make sure this is set at k=0 for radiation coupling
3328  flbq_arr(i, j, d_khi_lsm) = evp_air_arr(i, j, 0) / rhow; // kg/kg m/s
3329  flbt_arr(i, j, d_khi_lsm) = shf_air_arr(i, j, 0) / (Cp_d*rhow); // Km/s
3330 
3331  qstar_arr(i, j, 0) = -1.0 * flbq_arr(i, j, d_khi_lsm) / ustar_arr(i, j, 0);
3332  qstar_arr(i, j, d_khi_lsm) = qstar_arr(i, j, 0);
3333 
3334  // These terms are multiplied by rho in SurfaceLayer
3335  fluxq_arr(i,j,0) = flbq_arr(i, j, d_khi_lsm);
3336  fluxt_arr(i,j,0) = flbt_arr(i, j, d_khi_lsm);
3337  tau13_arr(i,j,0) = flbu_arr(i, j, d_khi_lsm) / rhow;
3338  tau23_arr(i,j,0) = flbv_arr(i, j, d_khi_lsm) / rhow;
3339 
3340  amrex::Real tvm = getThgivenRandT(rhow, tref_arr(i,j,0), R_d / Cp_d, qref_arr(i,j,0)) * (1.0 + 0.61 * qref_arr(i,j,0));
3341  olen_arr(i,j,0) = -ustar_arr(i,j,0) * ustar_arr(i,j,0) * ustar_arr(i,j,0) * tvm / (KAPPA * CONST_GRAV * flbt_arr(i,j,d_khi_lsm));
3342 
3343  });
3344  }
3345 
3346  lsm_fab_vars[LsmVar_SLM::tsurf]->FillBoundary(m_geom.periodicity());
3347  lsm_fab_vars[LsmVar_SLM::ustar]->FillBoundary(m_geom.periodicity());
3348  lsm_fab_vars[LsmVar_SLM::tstar]->FillBoundary(m_geom.periodicity());
3349  lsm_fab_vars[LsmVar_SLM::qstar]->FillBoundary(m_geom.periodicity());
3350  lsm_fab_vars[LsmVar_SLM::flbu]->FillBoundary(m_geom.periodicity());
3351  lsm_fab_vars[LsmVar_SLM::flbv]->FillBoundary(m_geom.periodicity());
3352 
3353  // TODO: fix;
3354  lsm_fab_flux[LsmFlux_SLM::q_flux]->FillBoundary(m_geom.periodicity());
3355  lsm_fab_flux[LsmFlux_SLM::t_flux]->FillBoundary(m_geom.periodicity());
3356  lsm_fab_flux[LsmFlux_SLM::tau13]->FillBoundary(m_geom.periodicity());
3357  lsm_fab_flux[LsmFlux_SLM::tau23]->FillBoundary(m_geom.periodicity());
3358  lsm_fab_flux[LsmFlux_SLM::olen]->FillBoundary(m_geom.periodicity());
3359 }
constexpr amrex::Real KAPPA
Definition: ERF_Constants.H:71
constexpr amrex::Real Cp_d
Definition: ERF_Constants.H:51
constexpr amrex::Real CONST_GRAV
Definition: ERF_Constants.H:72
constexpr amrex::Real R_d
Definition: ERF_Constants.H:49
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real getThgivenRandT(const amrex::Real rho, const amrex::Real T, const amrex::Real rdOcp, const amrex::Real qv=amrex::Real(0))
Definition: ERF_EOS.H:64
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void erf_qsatw(amrex::Real t, amrex::Real p, amrex::Real &qsatw)
Definition: ERF_MicrophysicsUtils.H:264
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void erf_qsati(amrex::Real t, amrex::Real p, amrex::Real &qsati)
Definition: ERF_MicrophysicsUtils.H:254
ParallelFor(fab_box, [=] AMREX_GPU_DEVICE(int i, int j, int k) { qrcuten_arr(i, j, k)=Real(0);qscuten_arr(i, j, k)=Real(0);qicuten_arr(i, j, k)=Real(0);})
constexpr amrex::Real one
Definition: ERF_NumericalConstants.H:37
constexpr amrex::Real zero
Definition: ERF_NumericalConstants.H:36
AMREX_FORCE_INLINE amrex::IntVect TileNoZ()
Definition: ERF_TileNoZ.H:11
amrex::MultiFab mw
Definition: ERF_SLM.H:792
amrex::MultiFab t_canop
Definition: ERF_SLM.H:791
amrex::MultiFab evp_air
Definition: ERF_SLM.H:860
amrex::MultiFab q_gr
Definition: ERF_SLM.H:801
amrex::iMultiFab landmask
Definition: ERF_SLM.H:805
amrex::MultiFab mw_mx
Definition: ERF_SLM.H:832
amrex::MultiFab shf_air
Definition: ERF_SLM.H:852
int khi_lsm
Definition: ERF_SLM.H:728
amrex::MultiFab net_rad
Definition: ERF_SLM.H:876
void radiation_noahmp(const amrex::MFIter &mfi)
Definition: ERF_SLM.cpp:5786
amrex::MultiFab mw_inc
Definition: ERF_SLM.H:843
amrex::MultiFab cbiom
Definition: ERF_SLM.H:816
void soil_temperature(const amrex::MFIter &mfi)
Definition: ERF_SLM.cpp:4605
amrex::MultiFab LAI
Definition: ERF_SLM.H:808
amrex::MultiFab wet_canop
Definition: ERF_SLM.H:877
static constexpr amrex::Real cp_water
Definition: ERF_SLM.H:777
amrex::MultiFab q_sfc
Definition: ERF_SLM.H:797
amrex::MultiFab t_sfc
Definition: ERF_SLM.H:796
amrex::MultiFab t_cas
Definition: ERF_SLM.H:798
void soil_nudging(const amrex::MFIter &mfi3d)
Definition: ERF_SLM.cpp:4768
amrex::MultiFab vege_YES
Definition: ERF_SLM.H:814
void fluxes_canopy(const amrex::MFIter &mfi)
Definition: ERF_SLM.cpp:3885
amrex::MultiFab slm_diag
Definition: ERF_SLM.H:979
amrex::Array< FabPtr, LsmVar_SLM::NumVars > lsm_fab_vars
Definition: ERF_SLM.H:733
amrex::MultiFab precip_extinc
Definition: ERF_SLM.H:830
static constexpr amrex::Real tfriz
Definition: ERF_SLM.H:771
amrex::MultiFab t_skin
Definition: ERF_SLM.H:794
void resistances(const amrex::MFIter &mfi)
Definition: ERF_SLM.cpp:3595
amrex::Geometry m_geom
Definition: ERF_SLM.H:708
amrex::MultiFab mws
Definition: ERF_SLM.H:793
amrex::MultiFab r_d
Definition: ERF_SLM.H:871
amrex::iMultiFab landtype
Definition: ERF_SLM.H:806
amrex::MultiFab r_c
Definition: ERF_SLM.H:870
AMREX_GPU_HOST_DEVICE static AMREX_FORCE_INLINE amrex::Real fh_calc(const amrex::Real &t, const amrex::Real &mps, const amrex::Real &sw, const amrex::Real &B)
Definition: ERF_SLM.cpp:4817
void transfer_coeff(const amrex::MFIter &mfi)
Definition: ERF_SLM.cpp:3362
static constexpr amrex::Real r_litter
Definition: ERF_SLM.H:873
void soil_water(const amrex::MFIter &mfi)
Definition: ERF_SLM.cpp:4329
void solve_ground_skin_temperature(const amrex::MFIter &mfi)
Definition: ERF_SLM.cpp:4042
amrex::MultiFab r_a
Definition: ERF_SLM.H:868
amrex::MultiFab t_ground_skin
Definition: ERF_SLM.H:795
amrex::MultiFab r_b
Definition: ERF_SLM.H:869
void UpdateLAI(const amrex::MFIter &mfi)
Definition: ERF_SLM.cpp:2858
amrex::MultiFab q_cas
Definition: ERF_SLM.H:799
amrex::MultiFab sdew
Definition: ERF_SLM.H:802
amrex::MultiFab cp_vege
Definition: ERF_SLM.H:815
amrex::MultiFab r_soil
Definition: ERF_SLM.H:872
amrex::iMultiFab vegetype
Definition: ERF_SLM.H:813
amrex::Array< FabPtr, LsmFlux_SLM::NumVars > lsm_fab_flux
Definition: ERF_SLM.H:736
amrex::MultiFab SAI
Definition: ERF_SLM.H:809
@ t_flux
Definition: ERF_SLM.H:100
@ tau23
Definition: ERF_SLM.H:103
@ tau13
Definition: ERF_SLM.H:102
@ q_flux
Definition: ERF_SLM.H:101
@ olen
Definition: ERF_SLM.H:104
@ mv
Definition: ERF_SLM.H:47
@ pref
Definition: ERF_SLM.H:34
@ ustar
Definition: ERF_SLM.H:28
@ flbu
Definition: ERF_SLM.H:83
@ flbv
Definition: ERF_SLM.H:84
@ qref
Definition: ERF_SLM.H:33
@ soilw
Definition: ERF_SLM.H:49
@ m_pot_sat
Definition: ERF_SLM.H:72
@ tref
Definition: ERF_SLM.H:32
@ flbq
Definition: ERF_SLM.H:85
@ Bconst
Definition: ERF_SLM.H:73
@ flbt
Definition: ERF_SLM.H:86
@ dref
Definition: ERF_SLM.H:35
@ tv
Definition: ERF_SLM.H:46
@ qstar
Definition: ERF_SLM.H:30
@ tsurf
Definition: ERF_SLM.H:27
@ prsfc
Definition: ERF_SLM.H:87
@ soilt
Definition: ERF_SLM.H:48
@ precipref
Definition: ERF_SLM.H:38
@ tstar
Definition: ERF_SLM.H:29
@ drain
Definition: ERF_SLM.H:137
@ precip
Definition: ERF_SLM.H:133
@ net_sw1
Definition: ERF_SLM.H:115
@ net_rad2
Definition: ERF_SLM.H:126
@ net_sw2
Definition: ERF_SLM.H:116
@ net_rad1
Definition: ERF_SLM.H:125
real(c_double), private rhow
Definition: ERF_module_mp_morr_two_moment.F90:187

Referenced by Advance().

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

◆ albedo_noahmp()

void SLM::albedo_noahmp ( const amrex::MFIter &  mfi,
int  i,
int  j,
amrex::Real  dt,
amrex::Real  cosz,
amrex::Real  snowh,
amrex::Real  fsno,
amrex::Real  fwet,
amrex::Real  sneqvo,
amrex::Real  sneqv,
amrex::Real  qsnow,
amrex::Real  tg,
amrex::Real  tv,
amrex::Real  elai,
amrex::Real  esai,
const amrex::Real *  smc,
int  nsoil,
amrex::Real  fveg,
amrex::Real &  albold,
amrex::Real &  tauss,
amrex::Real *  albgrd,
amrex::Real *  albgri,
amrex::Real *  albd,
amrex::Real *  albi,
amrex::Real *  fabd,
amrex::Real *  fabi,
amrex::Real *  ftdd,
amrex::Real *  ftid,
amrex::Real *  ftii,
amrex::Real &  fsun,
amrex::Real *  frevd,
amrex::Real *  frevi,
amrex::Real *  fregd,
amrex::Real *  fregi,
amrex::Real &  bgap,
amrex::Real &  wgap,
amrex::Real *  albsnd,
amrex::Real *  albsni 
)

◆ Copy_Lsm_to_State()

void SLM::Copy_Lsm_to_State ( amrex::MultiFab &  cons_in)
overridevirtual

Reimplemented from NullSurf.

5070 {
5071  for ( amrex::MFIter mfi(cons_in,amrex::TilingIfNotGPU()); mfi.isValid(); ++mfi) {
5072  const auto& box3d = mfi.tilebox();
5073 
5074  // get potential total density, temperature, qt, qp
5075  ParallelFor( box3d, [=] AMREX_GPU_DEVICE (int /*i*/, int /*j*/, int /*k*/)
5076  {
5077  // TODO
5078  /*
5079  states_arr(i,j,k,RhoTheta_comp) = rho_arr(i,j,k)*theta_arr(i,j,k);
5080  states_arr(i,j,k,RhoQ1_comp) = rho_arr(i,j,k)*qv_arr(i,j,k);
5081  states_arr(i,j,k,RhoQ2_comp) = rho_arr(i,j,k)*qc_arr(i,j,k);
5082  states_arr(i,j,k,RhoQ3_comp) = rho_arr(i,j,k)*qp_arr(i,j,k);
5083  */
5084  });
5085  }
5086 
5087  // Fill interior ghost cells and periodic boundaries
5088  cons_in.FillBoundary(m_geom.periodicity());
5089 }

Referenced by Update_State_Vars().

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

◆ Copy_State_to_Lsm()

void SLM::Copy_State_to_Lsm ( const amrex::MultiFab &  cons_in,
const amrex::MultiFab &  u_in,
const amrex::MultiFab &  v_in 
)
overridevirtual

Reimplemented from NullSurf.

4838 {
4839  int khi = khi_lsm;
4840 
4842 
4843  // Get the temperature, density, pressure at the reference level
4844  for ( MFIter mfi(*tsurf, TileNoZ()); mfi.isValid(); ++mfi) {
4845  const auto& box3d = mfi.tilebox();
4846 
4847  // Create a box with the same i,j bounds, but only at z = 0
4848  amrex::Box b2d = box3d;
4849  b2d.setRange(2, 0);
4850 
4851  auto states_array = cons_in.array(mfi);
4852  auto u_array = u_in.array(mfi);
4853  auto v_array = v_in.array(mfi);
4854 
4855  auto tref_array = lsm_fab_vars[LsmVar_SLM::tref]->array(mfi);
4856  auto rho_array = lsm_fab_vars[LsmVar_SLM::dref]->array(mfi);
4857  auto pres_array = lsm_fab_vars[LsmVar_SLM::pref]->array(mfi);
4858  auto qref_array = lsm_fab_vars[LsmVar_SLM::qref]->array(mfi);
4859  auto slm_u = lsm_fab_vars[LsmVar_SLM::uref]->array(mfi);
4860  auto slm_v = lsm_fab_vars[LsmVar_SLM::vref]->array(mfi);
4861 
4862  auto slm_dir_sw_vis = lsm_fab_vars[LsmVar_SLM::swdsvisxyref]->array(mfi);
4863  auto slm_dir_sw_nir = lsm_fab_vars[LsmVar_SLM::swdsnirxyref]->array(mfi);
4864  auto slm_diff_sw_vis = lsm_fab_vars[LsmVar_SLM::swdsvisdxyref]->array(mfi);
4865  auto slm_diff_sw_nir = lsm_fab_vars[LsmVar_SLM::swdsnirdxyref]->array(mfi);
4866  auto slm_lw = lsm_fab_vars[LsmVar_SLM::lwref]->array(mfi);
4867  auto slm_zenith = lsm_fab_vars[LsmVar_SLM::coszrsxy]->array(mfi);
4868 
4869  ParallelFor(b2d, [=] AMREX_GPU_DEVICE (int i, int j, int k)
4870  {
4871  const Real qv = states_array(i,j,k,RhoQ1_comp)/states_array(i,j,k,Rho_comp);
4872  rho_array(i,j,k) = states_array(i,j,k,Rho_comp);
4873  pres_array(i,j,k) = getPgivenRTh(states_array(i,j,k,RhoTheta_comp), qv)/100.;
4874  tref_array(i,j,k) = getTgivenRandRTh(states_array(i,j,k,Rho_comp),
4875  states_array(i,j,k,RhoTheta_comp),
4876  qv);
4877 
4878  qref_array(i,j,k) = qv;
4879 
4880  amrex::Real u_cc = myhalf * (u_array(i, j, k) + u_array(i + 1, j, k));
4881  amrex::Real v_cc = myhalf * (v_array(i, j, k) + v_array(i, j + 1, k));
4882  slm_u(i, j, k) = u_cc;
4883  slm_v(i, j, k) = v_cc;
4884 
4885  // TODO: this is for plotting purposes.. state arrays are at k=0 which is ghost cell for SLM values
4886  // SLM AMREX plotfile does not write ghost cells, but NetCDF does - fix?
4887  rho_array(i, j, khi) = rho_array(i, j, 0);
4888  pres_array(i, j, khi) = pres_array(i, j, 0);
4889  tref_array(i, j, khi) = tref_array(i, j, 0);
4890  qref_array(i, j, khi) = qref_array(i, j, 0);
4891  slm_u(i, j, khi) = slm_u(i, j, 0);
4892  slm_v(i, j, khi) = slm_v(i, j, 0);
4893 
4894  slm_dir_sw_vis(i, j, khi) = slm_dir_sw_vis(i, j, 0);
4895  slm_dir_sw_nir(i, j, khi) = slm_dir_sw_nir(i, j, 0);
4896  slm_diff_sw_vis(i, j, khi) = slm_diff_sw_vis(i, j, 0);
4897  slm_diff_sw_nir(i, j, khi) = slm_diff_sw_nir(i, j, 0);
4898  slm_lw(i, j, khi) = slm_lw(i, j, 0);
4899  slm_zenith(i, j, khi) = slm_zenith(i, j, 0);
4900  });
4901  }
4902  has_atmos_state = true;
4903 
4904 #ifdef ERF_USE_NETCDF
4905  if (rad_input_file != "")
4906  {
4907  int tindex = 0;
4908  int tindex_next = 0;
4909  for (int k = 1; k < num_rad_times; k++)
4910  {
4911  if (time > rad_times[k])
4912  {
4913  tindex = k;
4914  }
4915  }
4916 
4917  if (tindex == num_rad_times - 1)
4918  {
4919  tindex_next = tindex;
4920  } else {
4921  tindex_next = tindex + 1;
4922  }
4923 
4924  const amrex::Real t0 = rad_times[tindex];
4925  const amrex::Real t1 = rad_times[tindex_next];
4926 
4927  amrex::Print() << " SLM: time = " << time << ", interpolating fluxes at index " << tindex << " (t0 = " << t0 << " t1 = " << t1 << ")" << std::endl;
4928 
4929  const amrex::Real d_time = time;
4930  for ( MFIter mfi(rad_input_data); mfi.isValid(); ++mfi) {
4931  const auto& box3d = mfi.tilebox();
4932 
4933  // Create a box with the same i,j bounds, but only at z = 0
4934  amrex::Box b2d = box3d;
4935  b2d.setRange(2, 0);
4936 
4937  auto rad_arr = rad_input_data.const_array(mfi);
4938 
4939  auto slm_dir_sw_vis = lsm_fab_vars[LsmVar_SLM::swdsvisxyref]->array(mfi);
4940  auto slm_dir_sw_nir = lsm_fab_vars[LsmVar_SLM::swdsnirxyref]->array(mfi);
4941  auto slm_diff_sw_vis = lsm_fab_vars[LsmVar_SLM::swdsvisdxyref]->array(mfi);
4942  auto slm_diff_sw_nir = lsm_fab_vars[LsmVar_SLM::swdsnirdxyref]->array(mfi);
4943 
4944  auto slm_lw = lsm_fab_vars[LsmVar_SLM::lwref]->array(mfi);
4945  auto slm_zenith = lsm_fab_vars[LsmVar_SLM::coszrsxy]->array(mfi);
4946 
4947  ParallelFor(b2d, [=] AMREX_GPU_DEVICE (int i, int j, int k)
4948  {
4949  slm_dir_sw_vis(i, j, k) = linear_interp(t0, t1, d_time, rad_arr(i, j, tindex, 0), rad_arr(i, j, tindex_next, 0));
4950  slm_dir_sw_nir(i, j, k) = linear_interp(t0, t1, d_time, rad_arr(i, j, tindex, 1), rad_arr(i, j, tindex_next, 1));
4951 
4952  slm_diff_sw_vis(i, j, k) = linear_interp(t0, t1, d_time, rad_arr(i, j, tindex, 2), rad_arr(i, j, tindex_next, 2));
4953  slm_diff_sw_nir(i, j, k) = linear_interp(t0, t1, d_time, rad_arr(i, j, tindex, 3), rad_arr(i, j, tindex_next, 3));
4954 
4955  slm_zenith(i, j, k) = linear_interp(t0, t1, d_time, rad_arr(i, j, tindex, 4), rad_arr(i, j, tindex_next, 4));
4956  slm_lw(i, j, k) = linear_interp(t0, t1, d_time, rad_arr(i, j, tindex, 5), rad_arr(i, j, tindex_next, 5));
4957 
4958 
4959  // TODO: this is for plotting purposes.. state arrays are at k=0 which is ghost cell for SLM values
4960  // SLM AMREX plotfile does not write ghost cells, but NetCDF does - fix?
4961  slm_dir_sw_vis(i, j, khi) = slm_dir_sw_vis(i, j, 0);
4962  slm_dir_sw_nir(i, j, khi) = slm_dir_sw_nir(i, j, 0);
4963  slm_diff_sw_vis(i, j, khi) = slm_diff_sw_vis(i, j, 0);
4964  slm_diff_sw_nir(i, j, khi) = slm_diff_sw_nir(i, j, 0);
4965  slm_lw(i, j, khi) = slm_lw(i, j, 0);
4966  slm_zenith(i, j, khi) = slm_zenith(i, j, 0);
4967  });
4968  }
4969  return;
4970  }
4971 #endif
4972 }
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real getTgivenRandRTh(const amrex::Real rho, const amrex::Real rhotheta, const amrex::Real qv=amrex::Real(0))
Definition: ERF_EOS.H:46
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
#define Rho_comp
Definition: ERF_IndexDefines.H:53
#define RhoTheta_comp
Definition: ERF_IndexDefines.H:54
#define RhoQ1_comp
Definition: ERF_IndexDefines.H:66
const int khi
Definition: ERF_InitCustomPert_Bubble.H:25
constexpr amrex::Real myhalf
Definition: ERF_NumericalConstants.H:41
std::string rad_input_file
Definition: ERF_SLM.H:748
AMREX_GPU_HOST_DEVICE static AMREX_FORCE_INLINE amrex::Real linear_interp(const amrex::Real t0, const amrex::Real t1, const amrex::Real t, const amrex::Real x, const amrex::Real y)
Definition: ERF_SLM.cpp:4825
bool has_atmos_state
Definition: ERF_SLM.H:1019
@ uref
Definition: ERF_SLM.H:36
@ swdsvisxyref
Definition: ERF_SLM.H:39
@ vref
Definition: ERF_SLM.H:37
@ swdsnirxyref
Definition: ERF_SLM.H:40
@ swdsvisdxyref
Definition: ERF_SLM.H:41
@ coszrsxy
Definition: ERF_SLM.H:44
@ swdsnirdxyref
Definition: ERF_SLM.H:42
@ lwref
Definition: ERF_SLM.H:43
@ qv
Definition: ERF_Kessler.H:31
real(c_double), parameter t0
Definition: ERF_module_model_constants.F90:39

Referenced by Update_Lsm_Vars().

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

◆ Define()

void SLM::Define ( SolverChoice &  sc)
inlineoverridevirtual

Reimplemented from NullSurf.

201  {
202  // SLM exchanges water vapor with the atmosphere unconditionally: Copy_State_to_Lsm
203  // reads RhoQ1_comp out of the state and Copy_Lsm_to_State writes it back. Without a
204  // moisture model that component is not part of the state, so those accesses run off
205  // the end of the conserved-variable FAB.
208  "The SLM land surface model requires a moisture model: it exchanges water vapor "
209  "with the atmospheric state, which has no RhoQ1 component when "
210  "erf.moisture_model = None. Set erf.moisture_model, "
211  "or choose a different erf.land_surface_model.");
212 
214 
215  // Where the initial data come from, which can supply the soil layers (see Init)
216  m_init_type = sc.init_type;
217 
218  // Check if initializing from WRFInput, or from metgrid files, for which ERF builds
219  // the same fields real.exe writes to wrfinput (ERF::init_lsm_from_metgrid)
220  if (sc.init_type == InitType::WRFInput || sc.init_type == InitType::Metgrid) {
221  amrex::Print() << " SLM is initializing from "
222  << ((sc.init_type == InitType::WRFInput) ? "WRFInput" : "metgrid")
223  << " data!" << std::endl;
224  use_wrfinput = true;
225  } else {
226  use_wrfinput = false;
227  }
228  }
AMREX_ALWAYS_ASSERT_WITH_MESSAGE(axis >=0 &&axis< AMREX_SPACEDIM, "prob.axis must be 0, 1 or 2")
bool use_terrain
Definition: ERF_SLM.H:764
InitType m_init_type
Definition: ERF_SLM.H:1013
@ None
Definition: ERF_LSM_Types.H:6
static InitType init_type
Initial-condition source selected for the run.
Definition: ERF_DataStruct.H:2583
MoistureType moisture_type
Moisture or microphysics model.
Definition: ERF_DataStruct.H:2942
static TerrainType terrain_type
Terrain or immersed-boundary representation.
Definition: ERF_DataStruct.H:2589
Here is the call graph for this function:

◆ export_to_RRTMGP()

const amrex::Vector<const amrex::MultiFab*>& SLM::export_to_RRTMGP ( )
inline
663  {
664  return slm_to_rad_vars;
665  }
amrex::Vector< const amrex::MultiFab * > slm_to_rad_vars
Definition: ERF_SLM.H:981

◆ fh_calc()

AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real SLM::fh_calc ( const amrex::Real &  t,
const amrex::Real &  mps,
const amrex::Real &  sw,
const amrex::Real &  B 
)
static
4818 {
4819  amrex::Real moist_pot1 = std::max(amrex::Real(-100000.0), mps / (std::pow(std::max(amrex::Real(0.0001), sw), B)) / amrex::Real(1000.0));
4820  return std::min(amrex::Real(one), std::exp(moist_pot1*CONST_GRAV/amrex::Real(461.0)/t));
4821 }
@ t
Definition: ERF_WSM6.H:293

◆ fluxes_canopy()

void SLM::fluxes_canopy ( const amrex::MFIter &  mfi)
3886 {
3887  const int d_khi_lsm = khi_lsm;
3888  const int d_nz_lsm = m_nz_lsm;
3889  const Real dt = m_dt;
3890  const int niter = std::max(1, static_cast<int>(std::round(dt)));
3891  const Real dt_iter = dt/niter;
3892 
3893  auto box = mfi.tilebox();
3894 
3895  auto landmask_arr = landmask.const_array(mfi);
3896 
3897  auto LAI_arr = LAI.const_array(mfi);
3898 
3899  auto t_sfc_arr = t_sfc.const_array(mfi);
3900  auto q_sfc_arr = q_sfc.const_array(mfi);
3901 
3902  auto soilw_arr = lsm_fab_vars[LsmVar_SLM::soilw]->const_array(mfi);
3903  auto poro_soil_arr = lsm_fab_vars[LsmVar_SLM::poro_soil]->const_array(mfi);
3904  auto s_depth_arr = lsm_fab_vars[LsmVar_SLM::s_depth]->const_array(mfi);
3905  auto soil_transp_frac_arr = lsm_fab_vars[LsmVar_SLM::soil_transp_frac]->const_array(mfi);
3906  auto w_s_WP_arr = lsm_fab_vars[LsmVar_SLM::w_s_WP]->const_array(mfi);
3907  auto pref_arr = lsm_fab_vars[LsmVar_SLM::pref]->const_array(mfi);
3908 
3909  auto lhf_canop_arr = lhf_canop.array(mfi);
3910  auto evp_canop_arr = evp_canop.array(mfi);
3911  auto shf_canop_arr = shf_canop.array(mfi);
3912 
3913  auto vegetype_arr = vegetype.const_array(mfi);
3914  auto vege_YES_arr = vege_YES.const_array(mfi);
3915 
3916  auto mw_arr = mw.array(mfi);
3917  auto mw_mx_arr = mw_mx.const_array(mfi);
3918  auto mw_inc_arr = mw_inc.array(mfi);
3919  auto cp_vege_arr = cp_vege.const_array(mfi);
3920 
3921  auto dref_arr = lsm_fab_vars[LsmVar_SLM::dref]->const_array(mfi);
3922  auto tr_arr = lsm_fab_vars[LsmVar_SLM::tref]->const_array(mfi);
3923  auto t_canop_arr = t_canop.array(mfi);
3924 
3925  auto r_b_arr = r_b.const_array(mfi);
3926  auto r_c_arr = r_c.const_array(mfi);
3927 
3928  auto wet_canop_arr = wet_canop.array(mfi);
3929  auto evapo_dry_arr = evapo_dry.array(mfi);
3930 
3931  auto slm_diag_arr = slm_diag.array(mfi);
3932 
3933  auto net_rad_arr = net_rad.const_array(mfi);
3934 
3935  auto prsfc_arr = lsm_fab_vars[LsmVar_SLM::prsfc]->array(mfi);
3936 
3937  ParallelFor( box, [=] AMREX_GPU_DEVICE (int i, int j, int)
3938  {
3939  if (landmask_arr(i, j, 0) != 1) {
3940  return;
3941  }
3942  amrex::Real evapo_wet_loc;
3943 
3944  if (vegetype_arr(i, j, 0) == 1)
3945  {
3946  amrex::Real shf0 = zero;
3947  amrex::Real lhf0 = zero;
3948  amrex::Real evp0 = zero;
3949  amrex::Real evapo_wet0 = zero;
3950  amrex::Real drain0 = zero;
3951  // SAM rhow[nz] = air density at vertical velocity levels, kg/m^3
3952  const amrex::Real rhow = dref_arr(i, j, 0); // TODO: double check this
3953  amrex::Real qsat_canop;
3954  amrex::Real cp_vege_tot, t_canop_inc;
3955 
3956  for (int iter = 0; iter < niter; iter++)
3957  {
3958  shf_canop_arr(i, j, 0) = (t_canop_arr(i, j, 0) - t_sfc_arr(i, j, 0)) * rhow * cp * two * LAI_arr(i, j, 0) / r_b_arr(i, j, 0);
3959  shf0 += shf_canop_arr(i, j, 0);
3960 
3961  // Evaporation from canopy
3962  // only treat the case where t_canop_arr > tfriz;
3963  AMREX_ALWAYS_ASSERT(t_canop_arr(i, j, 0) > tfriz);
3964 
3965  erf_qsatw(t_canop_arr(i, j, 0), pref_arr(i, j, 0), qsat_canop);
3966 
3967  // direct evaporation from the water held on canopy
3968  // evaporation/dew only possible if canopy temperature is above freezing
3969  evapo_wet_loc = std::min(mw_arr(i, j, 0)/dt_iter, ((qsat_canop - q_sfc_arr(i, j, 0)) * rhow * LAI_arr(i, j, 0) / (r_b_arr(i, j, 0))*vege_YES_arr(i, j, 0)));
3970 
3971  // increment/decrement of the water amount held on leaves following the direct evaporation/dew formation
3972  const amrex::Real mw_evap_inc = -dt_iter * evapo_wet_loc; // evapo_wet_loc [kg/m2s=mm/s]
3973  mw_inc_arr(i, j, 0) += mw_evap_inc;
3974  mw_arr(i, j, 0) += mw_evap_inc;
3975  wet_canop_arr(i, j, 0) = std::min(amrex::Real(one), mw_arr(i, j, 0)/mw_mx_arr(i, j, 0));
3976 
3977  // Transpiration - only occurs when qsat_canop > qsfc
3978  evapo_dry_arr(i, j, 0) = std::max(amrex::Real(zero),(qsat_canop - q_sfc_arr(i, j, 0))*rhow*(one - wet_canop_arr(i, j, 0))*LAI_arr(i, j, 0)/(r_b_arr(i, j, 0) + r_c_arr(i, j, 0))*vege_YES_arr(i, j, 0));
3979  if (evapo_dry_arr(i, j, 0) > zero) {
3980  amrex::Real max_transpiration = 1.0e30;
3981  bool has_transpiration_source = false;
3982  for (int k = 0; k < d_nz_lsm; k++) {
3983  const int lsm_k = d_khi_lsm - k;
3984  const amrex::Real transp_frac = std::max(amrex::Real(zero), soil_transp_frac_arr(i, j, lsm_k));
3985  if (transp_frac > zero) {
3986  has_transpiration_source = true;
3987  const amrex::Real available_water =
3988  std::max(amrex::Real(zero), soilw_arr(i, j, lsm_k) - w_s_WP_arr(i, j, lsm_k)) *
3989  poro_soil_arr(i, j, lsm_k) *
3990  s_depth_arr(i, j, lsm_k) * 1.0e3;
3991  max_transpiration = std::min(max_transpiration, available_water / (transp_frac * dt));
3992  }
3993  }
3994  evapo_dry_arr(i, j, 0) = has_transpiration_source
3995  ? std::min(evapo_dry_arr(i, j, 0), max_transpiration)
3996  : 0.0;
3997  }
3998 
3999  // Convert evaporation (kg/m2/s) to latent heat flux (W/m2)
4000  lhf_canop_arr(i, j, 0) = lcond*(evapo_wet_loc+evapo_dry_arr(i, j, 0));
4001  evp_canop_arr(i, j, 0) = evapo_wet_loc + evapo_dry_arr(i, j, 0);
4002  lhf0 += lhf_canop_arr(i, j, 0);
4003  evp0 += evp_canop_arr(i, j, 0);
4004  evapo_wet0 += evapo_wet_loc;
4005 
4006  // Update vegetation moisture storage
4007  if (mw_arr(i, j, 0) > mw_mx_arr(i, j, 0))
4008  {
4009  const amrex::Real canopy_drain = (mw_arr(i, j, 0) - mw_mx_arr(i, j, 0)) / dt_iter;
4010  drain0 += canopy_drain; // dripping excess of dew
4011  mw_inc_arr(i, j, 0) -= dt_iter * canopy_drain;
4012  mw_arr(i, j, 0) = mw_mx_arr(i, j, 0);
4013  }
4014 
4015  // Update vegetation temperature
4016  cp_vege_tot = cp_vege_arr(i, j, 0) + mw_arr(i, j, 0) * 1.e-3 * cp_water;
4017  t_canop_inc = dt_iter / std::max(amrex::Real(1.0e-3), cp_vege_tot)*(net_rad_arr(i, j, 0, SLM_NetRad::net_rad1) - shf_canop_arr(i, j, 0) - lhf_canop_arr(i, j, 0)) * vege_YES_arr(i, j, 0);
4018  t_canop_arr(i, j, 0) = std::min(amrex::Real(t_canop_max), t_canop_arr(i, j, 0) + t_canop_inc);
4019  }
4020  shf_canop_arr(i, j, 0) = shf0 / static_cast<amrex::Real>(niter);
4021  lhf_canop_arr(i, j, 0) = lhf0 / static_cast<amrex::Real>(niter);
4022  evp_canop_arr(i, j, 0) = evp0 / static_cast<amrex::Real>(niter);
4023  prsfc_arr(i, j, 0) += (drain0 / static_cast<amrex::Real>(niter));
4024  slm_diag_arr(i, j, 0, SLM_Diag::precip_sfc) = prsfc_arr(i, j, 0);
4025  slm_diag_arr(i, j, 0, SLM_Diag::evapo_wet) = evapo_wet0 / static_cast<amrex::Real>(niter);
4026  slm_diag_arr(i, j, 0, SLM_Diag::drain) += (drain0/static_cast<amrex::Real>(niter));
4027  }
4028  else
4029  {
4030  shf_canop_arr(i, j, 0) = zero;
4031  lhf_canop_arr(i, j, 0) = zero;
4032  evp_canop_arr(i, j, 0) = zero;
4033  wet_canop_arr(i, j, 0) = zero;
4034  mw_arr(i, j, 0) = zero;
4035  evapo_wet_loc = zero;
4036  evapo_dry_arr(i, j, 0) = zero;
4037  t_canop_arr(i, j, 0) = tr_arr(i, j, 0);
4038  }
4039  });
4040 }
AMREX_ALWAYS_ASSERT(bx.length()[2]==khi+1)
constexpr amrex::Real lcond
Definition: ERF_MicrophysicsConstants.H:109
constexpr amrex::Real two
Definition: ERF_NumericalConstants.H:38
amrex::MultiFab evp_canop
Definition: ERF_SLM.H:858
static constexpr amrex::Real t_canop_max
Definition: ERF_SLM.H:762
amrex::MultiFab shf_canop
Definition: ERF_SLM.H:850
static constexpr amrex::Real cp
Definition: ERF_SLM.H:776
amrex::MultiFab evapo_dry
Definition: ERF_SLM.H:846
int m_nz_lsm
Definition: ERF_SLM.H:744
amrex::MultiFab lhf_canop
Definition: ERF_SLM.H:855
@ poro_soil
Definition: ERF_SLM.H:69
@ s_depth
Definition: ERF_SLM.H:53
@ w_s_WP
Definition: ERF_SLM.H:65
@ soil_transp_frac
Definition: ERF_SLM.H:63
@ evapo_wet
Definition: ERF_SLM.H:139
@ precip_sfc
Definition: ERF_SLM.H:135
Here is the call graph for this function:

◆ get_rad_output_map()

std::unordered_map<std::string, std::string>& SLM::get_rad_output_map ( )
inline
668  {
669  return rad_output_map;
670  }
static std::unordered_map< std::string, std::string > rad_output_map
Definition: ERF_SLM.H:1001

◆ get_soil_dz()

const std::vector<amrex::Real>& SLM::get_soil_dz ( ) const
inline
672 { return m_dz_lsm; }
std::vector< amrex::Real > m_dz_lsm
Definition: ERF_SLM.H:747

◆ Get_Update_Status()

bool SLM::Get_Update_Status ( )
inlineoverridevirtual

Reimplemented from NullSurf.

495 { return m_updated; }

◆ GotoNextLine()

void SLM::GotoNextLine ( std::istream &  is)
static
383 {
384  constexpr std::streamsize bl_ignore_max { 100000 };
385  is.ignore(bl_ignore_max, '\n');
386 }

◆ groundalb_noahmp()

AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void SLM::groundalb_noahmp ( int  nsoil,
int  nband,
int  ice,
int  ist,
amrex::Real  fsno,
const amrex::Real *  smc,
const amrex::Real *  albsnd,
const amrex::Real *  albsni,
amrex::Real  cosz,
amrex::Real  tg,
const amrex::Real *  albsat,
const amrex::Real *  albdry,
const amrex::Real *  alblak,
amrex::Real *  albgrd,
amrex::Real *  albgri 
)
static
5372 {
5373  // --------------------------------------------------------------------------------------------------
5374  //input
5375  // ILOC !grid index
5376  // JLOC !grid index
5377  // NSOIL !number of soil layers
5378  // NBAND !number of solar radiation waveband classes
5379  // ICE !value of ist for land ice
5380  // IST !surface type
5381  // FSNO !fraction of surface covered with snow (-)
5382  // TG !ground temperature (k)
5383  // COSZ !cosine solar zenith angle (0-1)
5384  // SMC !volumetric soil water content (m3/m3)
5385  // ALBSND !direct beam snow albedo (vis, nir)
5386  // ALBSNI !diffuse snow albedo (vis, nir)
5387 
5388  //output
5389  // ALBGRD !ground albedo (direct beam: vis, nir)
5390  // ALBGRI !ground albedo (diffuse: vis, nir)
5391 
5392  //local
5393  amrex::Real inc; //soil water correction factor for soil albedo
5394  amrex::Real albsod; //soil albedo (direct)
5395  amrex::Real albsoi; //soil albedo (diffuse)
5396  // --------------------------------------------------------------------------------------------------
5397  constexpr amrex::Real TFRZ = 273.16; // freezing/melting point (k)
5398 
5399  for (int ib = 0; ib < nband; ib++) {
5400  inc = std::max(amrex::Real(0.11) - amrex::Real(0.40)*smc[0], amrex::Real(zero));
5401  if (ist == 1) { //soil
5402  albsod = std::min(albsat[ib]+inc, albdry[ib]);
5403  albsoi = albsod;
5404  } else if (tg > TFRZ) { //unfrozen lake, wetland
5405  albsod = 0.06/(std::max(amrex::Real(0.01),cosz)*std::max(amrex::Real(0.01),cosz)*std::max(amrex::Real(0.01),cosz)*
5406  std::max(amrex::Real(0.01),cosz)*std::max(amrex::Real(0.01),cosz)*std::max(amrex::Real(0.01),cosz)*
5407  std::max(amrex::Real(0.01),cosz) + 0.15);
5408  albsoi = 0.06;
5409  } else { //frozen lake, wetland
5410  albsod = alblak[ib];
5411  albsoi = albsod;
5412  }
5413 
5414  albgrd[ib] = albsod*(1.0-fsno) + albsnd[ib]*fsno;
5415  albgri[ib] = albsoi*(1.0-fsno) + albsni[ib]*fsno;
5416  }
5417 }
constexpr std::array< amrex::Real, 2 > alblak
Definition: ERF_SLM_DefaultParams.H:58

◆ Init() [1/3]

virtual void NullSurf::Init
inline
34  { }

◆ Init() [2/3]

void SLM::Init ( const int &  ,
const amrex::MultiFab &  cons_in,
const amrex::MultiFab &  u_in,
const amrex::MultiFab &  v_in,
const amrex::Geometry &  geom,
const amrex::Geometry &  geom0,
amrex::Vector< amrex::BCRec > &  domain_bcs_type,
amrex::IntVect &  refRatio,
const amrex::Real &  dt,
std::unique_ptr< amrex::MultiFab > &  z_phys_nd_in,
amrex::Vector< amrex::Vector< std::string >> &  nc_init_file 
)
overridevirtual

Reimplemented from NullSurf.

82 {
83  m_dt = dt;
84  m_geom = geom;
85  m_geom0 = geom0;
86  m_domain_bcs_type = domain_bcs_type;
87  z_phys_nd = z_phys_nd_in.get();
88 
89  ParmParse pp("slm");
90  const bool have_nsoil = pp.contains("nsoil");
91  const bool have_dz = pp.contains("soil_dz");
92  pp.query("nsoil", m_nz_lsm);
93  m_dz_lsm.clear();
94  pp.queryarr("soil_dz", m_dz_lsm);
95 
96  // The soil layers of the initial data, where they have them: the Noah layers SLM's
97  // state is interpolated to from met_em files, or the file's own DZS for wrfinput, which
98  // is what SLM's soil physics then uses. Without slm.soil_dz SLM takes these; with it
99  // the two must agree, since slm.soil_dz sets the geometry the soil is plotted on.
100  Vector<Real> data_dz;
101  std::string data_source;
102 #ifdef ERF_USE_NETCDF
103  if (m_init_type == InitType::Metgrid) {
105  data_source = "the Noah layers SLM is initialized on from met_em files";
106  } else if (m_init_type == InitType::WRFInput &&
107  !nc_init_file.empty() && !nc_init_file[0].empty()) {
108  data_dz = read_wrfinput_soil_dz(nc_init_file[0][0]);
109  data_source = "DZS in " + nc_init_file[0][0];
110  }
111 #else
112  amrex::ignore_unused(nc_init_file);
113 #endif
114 
115  if (!data_dz.empty()) {
116  const int nsoil_data = static_cast<int>(data_dz.size());
117  if (!have_dz) {
118  if (have_nsoil && m_nz_lsm != nsoil_data) {
119  amrex::Abort("SLM: slm.nsoil = " + std::to_string(m_nz_lsm) + " but " + data_source
120  + " has " + std::to_string(nsoil_data) + " layers ("
121  + soil_dz_string(data_dz) + " m); leave slm.nsoil and slm.soil_dz unset to use them");
122  }
123  m_nz_lsm = nsoil_data;
124  m_dz_lsm = data_dz;
125  amrex::Print() << "SLM: " << m_nz_lsm << " soil layers from " << data_source << ": "
126  << soil_dz_string(m_dz_lsm) << " m" << std::endl;
127  } else {
128  bool match = (static_cast<int>(m_dz_lsm.size()) == nsoil_data);
129  for (int k = 0; match && k < nsoil_data; ++k) {
130  match = std::abs(m_dz_lsm[k] - data_dz[k]) <= Real(1.e-6) * data_dz[k];
131  }
132  if (!match) {
133  amrex::Abort("SLM: slm.soil_dz = " + soil_dz_string(m_dz_lsm) + " differs from "
134  + data_source + ", " + soil_dz_string(data_dz)
135  + " m; set it to those, or leave slm.nsoil and slm.soil_dz unset to use them");
136  }
137  }
138  } else if (!have_dz) {
139  amrex::Abort("SLM: slm.soil_dz is required -- the thicknesses in m of the slm.nsoil soil "
140  "layers (" + std::to_string(m_nz_lsm) + " unless set), from the top down");
141  }
142 
143  if (m_nz_lsm < 2) {
144  amrex::Abort("SLM: nsoil must be at least 2");
145  }
146  if (static_cast<int>(m_dz_lsm.size()) != m_nz_lsm) {
147  amrex::Abort("SLM: slm.soil_dz has " + std::to_string(m_dz_lsm.size())
148  + " values but slm.nsoil is " + std::to_string(m_nz_lsm));
149  }
150  for (int k = 0; k < static_cast<int>(m_dz_lsm.size()); ++k) {
151  if (!std::isfinite(m_dz_lsm[k]) || m_dz_lsm[k] <= zero) {
152  amrex::Abort("SLM: soil_dz values must be finite and positive");
153  }
154  }
155 
156  Box domain = geom.Domain();
157  khi_lsm = domain.smallEnd(2) - 1; // index of z_r
158  klo_lsm = khi_lsm - m_nz_lsm + 1;
159 
160  LsmDataMap.resize(m_lsm_data_size);
161  LsmDataMap = {
176 
178  LsmDataName = {"tsurf", "ustar", "tstar",
179  "qstar", "tveg", "mv",
180  "tsoil", "wsoil", "sand",
181  "clay", "soil_thickness", "surface_u",
182  "surface_v", "surface_vapor", "surface_heat",
183  "precip_soil", "ref_precip", "SW_dw_dir_vis",
184  "SW_dw_dir_nir", "SW_dw_dif_vis", "SW_dw_dif_nir",
185  "LW_dw", "cos_zenith", "ref_t", "ref_u",
186  "ref_v", "ref_d", "ref_q",
187  "ref_p", "node_z", "soilt_nudge",
188  "soilw_nudge", "lai", "vegtype",
189  "soiltype", "veg_frac", "veg_frac_min", "veg_frac_max", "emis_sfc",
190  "alb_nir_sfc", "alb_vis_sfc", "alb_nir_sfc_diff", "alb_vis_sfc_diff",
191  "soil_transp_frac"};
192 
193  AMREX_ALWAYS_ASSERT(LsmDataMap.size() == LsmDataName.size());
194  AMREX_ALWAYS_ASSERT(static_cast<int>(LsmDataMap.size()) == m_lsm_data_size);
195 
196  LsmFluxMap.resize(m_lsm_flux_size);
198 
200  LsmFluxName = {"t_flux", "q_flux", "tau13", "tau23", "olen"};
201 
202  AMREX_ALWAYS_ASSERT(LsmFluxMap.size() == LsmFluxName.size());
203  AMREX_ALWAYS_ASSERT(static_cast<int>(LsmFluxMap.size()) == m_lsm_flux_size);
204 
205  // NOTE: All boxes in ba extend from zlo to zhi, so this transform is valid.
206  // If that were to change, the dm and new ba are no longer valid and
207  // direct copying between lsm data/flux vars cannot be done in a parfor.
208 
209  // Set box array for lsm data
210  IntVect ng(1,1,1);
211  BoxArray ba = cons_in.boxArray();
212  DistributionMapping dm = cons_in.DistributionMap();
213  BoxList bl_lsm = ba.boxList();
214  for (auto& b : bl_lsm) {
215  b.setBig(2, khi_lsm); // First point below the surface
216  b.setSmall(2, klo_lsm); // Last point below the surface
217  }
218  BoxArray ba_lsm(std::move(bl_lsm));
219 
220  // Set up lsm geometry
221  const RealBox& dom_rb = m_geom.ProbDomain();
222  RealBox lsm_rb = dom_rb;
223  Real lsm_z_hi = dom_rb.lo(2); // z_r
224  Real lsm_z_lo = lsm_z_hi;
225  for (int k = 0; k < m_nz_lsm; k++)
226  {
227  lsm_z_lo -= m_dz_lsm[k];
228  }
229  lsm_rb.setHi(2,lsm_z_hi); lsm_rb.setLo(2,lsm_z_lo);
230 
231  amrex::Box lsm_dom = m_geom.Domain();
232  lsm_dom.setSmall(2, klo_lsm);
233  lsm_dom.setBig(2, khi_lsm);
234  m_lsm_geom.define(lsm_dom, lsm_rb, m_geom.Coord(), m_geom.isPeriodic());
235 
236  const RealBox& dom_rb0 = m_geom0.ProbDomain();
237  RealBox lsm_rb0 = dom_rb0;
238  Real lsm_z_hi0 = dom_rb0.lo(2);
239  Real lsm_z_lo0 = lsm_z_hi0;
240  for (int k = 0; k < m_nz_lsm; ++k) {
241  lsm_z_lo0 -= m_dz_lsm[k];
242  }
243  lsm_rb0.setHi(2, lsm_z_hi0);
244  lsm_rb0.setLo(2, lsm_z_lo0);
245 
246  Box lsm_dom0 = m_geom0.Domain();
247  const int khi_lsm0 = lsm_dom0.smallEnd(2) - 1;
248  const int klo_lsm0 = khi_lsm0 - m_nz_lsm + 1;
249  lsm_dom0.setSmall(2, klo_lsm0);
250  lsm_dom0.setBig(2, khi_lsm0);
251  m_lsm_geom0.define(lsm_dom0, lsm_rb0, m_geom0.Coord(), m_geom0.isPeriodic());
252 
253  BoxList bl_lsm_2d = ba_lsm.boxList();
254  for (auto& b : bl_lsm_2d) {
255  b.setRange(2, 0, 1);
256  }
257  ba_lsm_2d = BoxArray(std::move(bl_lsm_2d));
258  IntVect ng_2d(0, 0, 0);
259 
260  // Create the data
261  for (auto ivar = 0; ivar < LsmVar_SLM::NumVars; ++ivar) {
262  // State vars are CC
263  lsm_fab_vars[ivar] = std::make_shared<MultiFab>(ba_lsm, dm, 1, ng);
264  lsm_fab_vars[ivar]->setVal(zero);
265  }
266 
267  // Create the fluxes
268  for (auto ivar = 0; ivar < LsmFlux_SLM::NumVars; ++ivar) {
269  // NOTE: Fluxes are CC with ghost cells for averaging
270  lsm_fab_flux[ivar] = std::make_shared<MultiFab>(ba_lsm_2d, dm, 1, IntVect(1,1,0));
271  lsm_fab_flux[ivar]->setVal(zero);
272  }
273 
274  // build list for checkpointing extra variables not mapped to ERF in lsm_fab_vars
275  unmapped_fields.clear();
276  for (int i = 0; i < LsmVar_SLM::NumVars; i++) {
277  int found = 0;
278  for (int j = 0; j < m_lsm_data_size; j++) {
279  if (LsmDataMap[j] == i) {
280  found = 1;
281  break;
282  }
283  }
284  if (!found) {
285  unmapped_fields.push_back(i);
286  }
287  }
288 
289  // Initial olen to neutral condition
290  lsm_fab_flux[LsmFlux_SLM::olen]->setVal(1.0E34);
291 
292  // packed temporary 1D arrays in soil water and soil temperature
293  soilt_vars.define(ba_lsm, dm, SLM_DST::NumVars, 0);
294  soilw_vars.define(ba_lsm, dm, SLM_DSW::NumVars, 0);
295  soilt_vars.setVal(zero);
296  soilw_vars.setVal(zero);
297 
298  // Create local 2D data
299  // TODO: Placeholder landmask array - fix!
300  landmask.define(ba_lsm_2d, dm, 1, ng_2d);
301  landmask.setVal(1);
302 
303  landtype.define(ba_lsm_2d, dm, 1, ng_2d);
304  nroot.define(ba_lsm_2d, dm, 1, ng_2d);
305  LAI.define(ba_lsm_2d, dm, 1, ng_2d);
306  SAI.define(ba_lsm_2d, dm, 1, ng_2d);
307  sstxy.define(ba_lsm_2d, dm, 1, ng_2d);
308 
309  t_canop.define(ba_lsm_2d, dm, 1, ng_2d);
310  mw.define(ba_lsm_2d, dm, 1, ng_2d);
311  mws.define(ba_lsm_2d, dm, 1, ng_2d);
312  t_skin.define(ba_lsm_2d, dm, 1, ng_2d);
313  t_ground_skin.define(ba_lsm_2d, dm, 1, ng_2d);
314  t_cas.define(ba_lsm_2d, dm, 1, ng_2d);
315  q_cas.define(ba_lsm_2d, dm, 1, ng_2d);
316 
317  t_sfc.define(ba_lsm_2d, dm, 1, ng_2d);
318  q_sfc.define(ba_lsm_2d, dm, 1, ng_2d);
319 
320  q_gr.define(ba_lsm_2d, dm, 1, ng_2d);
321  sdew.define(ba_lsm_2d, dm, 1, ng_2d);
322 
323  // NOAHMP radiation state variables
324  albold_noahmp.define(ba_lsm_2d, dm, 1, ng_2d);
325  tauss_noahmp.define(ba_lsm_2d, dm, 1, ng_2d);
326 
327  vegetype.define(ba_lsm_2d, dm, 1, ng_2d);
328  vege_YES.define(ba_lsm_2d, dm, 1, ng_2d);
329  cp_vege.define(ba_lsm_2d, dm, 1, ng_2d);
330  cbiom.define(ba_lsm_2d, dm, 1, ng_2d);
331  dleaf.define(ba_lsm_2d, dm, 1, ng_2d);
332  z0_sfc.define(ba_lsm_2d, dm, 1, ng_2d);
333  Khai_L.define(ba_lsm_2d, dm, 1, ng_2d);
334  phi_1.define(ba_lsm_2d, dm, 1, ng_2d);
335  phi_2.define(ba_lsm_2d, dm, 1, ng_2d);
336  IR_emis_vege.define(ba_lsm_2d, dm, 1, ng_2d);
337  IR_emis_soil.define(ba_lsm_2d, dm, 1, ng_2d);
338  IR_emis_grnd.define(ba_lsm_2d, dm, 1, ng_2d);
339  ztop.define(ba_lsm_2d, dm, 1, ng_2d);
340  disp_hgt.define(ba_lsm_2d, dm, 1, ng_2d);
341  Rgl.define(ba_lsm_2d, dm, 1, ng_2d);
342  Rc_min.define(ba_lsm_2d, dm, 1, ng_2d);
343  hs_rc.define(ba_lsm_2d, dm, 1, ng_2d);
344  precip_extinc.define(ba_lsm_2d, dm, 1, ng_2d);
345  mw_mx.define(ba_lsm_2d, dm, 1, ng_2d);
346  mws_mx.define(ba_lsm_2d, dm, 1, ng_2d);
347  BAI.define(ba_lsm_2d, dm, 1, ng_2d);
348  IMPERV.define(ba_lsm_2d, dm, 1, ng_2d);
349 
350  cp_vege.setVal(zero);
351  cbiom.setVal(0.02);
352  dleaf.setVal(0.04);
353  z0_sfc.setVal(zero);
354  Khai_L.setVal(zero);
355  phi_1.setVal(zero);
356  phi_2.setVal(zero);
357  ztop.setVal(zero);
358  disp_hgt.setVal(zero);
359  Rgl.setVal(zero);
360  Rc_min.setVal(zero);
361  hs_rc.setVal(zero);
362  precip_extinc.setVal(zero);
363  mw_mx.setVal(zero);
364  mws_mx.setVal(zero);
365  BAI.setVal(zero);
366  IMPERV.setVal(zero);
367 
368  mw_inc.define(ba_lsm_2d, dm, 1, ng_2d);
369 
370  evapo_dry.define(ba_lsm_2d, dm, 1, ng_2d);
371 
372  shf_canop.define(ba_lsm_2d, dm, 1, ng_2d);
373  shf_soil.define(ba_lsm_2d, dm, 1, ng_2d);
374  shf_air.define(ba_lsm_2d, dm, 1, ng_2d);
375  lhf_canop.define(ba_lsm_2d, dm, 1, ng_2d);
376  lhf_soil.define(ba_lsm_2d, dm, 1, ng_2d);
377  lhf_air.define(ba_lsm_2d, dm, 1, ng_2d);
378  evp_canop.define(ba_lsm_2d, dm, 1, ng_2d);
379  evp_soil.define(ba_lsm_2d, dm, 1, ng_2d);
380  evp_air.define(ba_lsm_2d, dm, 1, ng_2d);
381 
382  albedovis_v.define(ba_lsm_2d, dm, 1, ng_2d);
383  albedonir_v.define(ba_lsm_2d, dm, 1, ng_2d);
384  albedovis_s.define(ba_lsm_2d, dm, 1, ng_2d);
385  albedonir_s.define(ba_lsm_2d, dm, 1, ng_2d);
386  albedovis_v.setVal(zero);
387  albedovis_s.setVal(zero);
388  albedonir_v.setVal(zero);
389  albedonir_s.setVal(zero);
390  IR_emis_vege.setVal(zero);
391  IR_emis_soil.setVal(0.98);
392  IR_emis_grnd.setVal(zero);
393  vege_YES.setVal(zero);
394 
395  r_a.define(ba_lsm_2d, dm, 1, ng_2d);
396  r_b.define(ba_lsm_2d, dm, 1, ng_2d);
397  r_c.define(ba_lsm_2d, dm, 1, ng_2d);
398  r_d.define(ba_lsm_2d, dm, 1, ng_2d);
399  r_soil.define(ba_lsm_2d, dm, 1, ng_2d);
400 
401  net_rad.define(ba_lsm_2d, dm, SLM_NetRad::NumVars, ng_2d);
402  wet_canop.define(ba_lsm_2d, dm, 1, ng_2d);
403 
404  slm_diag.define(ba_lsm_2d, dm, SLM_Diag::NumVars, ng_2d);
405  slm_diag.setVal(zero);
406 
407  zrefxy.define(ba_lsm_2d, dm, 1, ng_2d);
408  zrefxy.setVal(zero);
409 
410  r_soil.setVal(zero);
411  lhf_air.setVal(zero);
412  lhf_canop.setVal(zero);
413  shf_air.setVal(zero);
414  shf_canop.setVal(zero);
415  evp_air.setVal(zero);
416  evp_canop.setVal(zero);
417  evp_soil.setVal(zero);
418 
419  t_canop.setVal(zero);
420  t_cas.setVal(zero);
421  t_skin.setVal(zero);
422  t_ground_skin.setVal(zero);
423  q_cas.setVal(zero);
424 
425  t_sfc.setVal(zero);
426  q_sfc.setVal(zero);
427  q_gr.setVal(zero);
428  sdew.setVal(zero);
429  wet_canop.setVal(zero);
430 
431  mw.setVal(zero);
432  mws.setVal(zero);
433  mw_inc.setVal(zero);
434  evapo_dry.setVal(zero);
435 
436  r_a.setVal(zero);
437  r_b.setVal(zero);
438  r_c.setVal(zero);
439  r_d.setVal(zero);
440  r_soil.setVal(zero);
441  lsm_fab_vars[LsmVar_SLM::ustar]->setVal(0.1);
444 
445  // Initialize SLM from inputs (for idealized cases, if specified), and load any other parameters
447 
448  if (!use_wrfinput) {
449  // Supply a valid default category when no external soil category is available.
451  }
452 
453  landtype.setVal(landtype0);
454  nroot.setVal(m_nz_lsm);
455  LAI.setVal(LAI0);
456  SAI.setVal(zero);
457  sstxy.setVal(zero);
458  mws_mx.setVal(mws_mx0);
459 
460  net_rad.setVal(zero);
461 
462  // Initialize NOAHMP radiation state variables
463  albold_noahmp.setVal(0.65); // initial snow albedo
464  tauss_noahmp.setVal(zero); // initial snow age
465 
466  // Set a default vegetation fraction of 1.0.
467  // Cells without vegetation (baresoil, urban) are set to 0.0 later on
468  // If using WRFInput, this fraction will be overwritten with those values
470 
471  if (!use_wrfinput) {
472  // Initialize here if not using wrfinput, otherwise it is done at first time step
473  slm_init();
474  }
475 
476  // Following Noah-MP, zref is modified to become ztop (canopy top height) + dz0 (height above the ground of the center of the atmosphere's lowest cell)
477  if (!use_wrfinput) {
478  Real dz = m_geom.CellSize(2);
479  // The ground nodes under every box, including those that start above the ground
480  const auto z_ground = (use_terrain) ? copy_ground_plane(*z_phys_nd, m_geom.Domain().smallEnd(2))
481  : std::unique_ptr<MultiFab>{};
482  for ( MFIter mfi(cons_in,TileNoZ()); mfi.isValid(); ++mfi) {
483  const Box& xybx = mfi.growntilebox(0);
484  const Array4<const Real>& z_nd_arr = (use_terrain) ? z_ground->const_array(mfi) : Array4<Real>{};
485  auto ztop_arr = ztop.array(mfi);
486  auto zrefxy_arr = zrefxy.array(mfi);
487 
488  ParallelFor(xybx, [=] AMREX_GPU_DEVICE(int i, int j, int) noexcept {
489  amrex::Real znd = (z_nd_arr) ? Compute_Zrel_AtCellCenter(i, j, 0, z_nd_arr) : myhalf*dz;
490  zrefxy_arr(i,j,0) = ztop_arr(i,j,0) + znd;
491  });
492  }
493  } else {
494  zrefxy.setVal(zref); // set zrefxy with the value read from inputs file
495  }
496 
497  //slm_to_rad_vars = {Lsm_Data_Ptr(LsmVar_SLM::tsurf), &albedovis_s, &albedovis_v, &albedonir_s, &albedonir_v, &IR_emis_vege, &net_rad};
499  Lsm_Data_Ptr(Lsm_DataIndex("emis_sfc")),
500  Lsm_Data_Ptr(Lsm_DataIndex("alb_vis_sfc")),
501  Lsm_Data_Ptr(Lsm_DataIndex("alb_nir_sfc")),
502  Lsm_Data_Ptr(Lsm_DataIndex("alb_vis_sfc_diff")),
503  Lsm_Data_Ptr(Lsm_DataIndex("alb_nir_sfc_diff"))};
504 }
ParmParse pp("prob")
std::unique_ptr< MultiFab > copy_ground_plane(const MultiFab &src, int klo)
Definition: ERF_PlanarBoundary.cpp:136
AMREX_GPU_HOST_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:762
amrex::MultiFab z0_sfc
Definition: ERF_SLM.H:818
amrex::Geometry m_geom0
Definition: ERF_SLM.H:711
int Lsm_DataIndex(const std::string &varname) override
Definition: ERF_SLM.H:430
amrex::Vector< amrex::BCRec > m_domain_bcs_type
Definition: ERF_SLM.H:720
int m_lsm_flux_size
Definition: ERF_SLM.H:697
amrex::MultiFab shf_soil
Definition: ERF_SLM.H:851
void init_from_inputs()
Definition: ERF_SLM.cpp:515
amrex::MultiFab Khai_L
Definition: ERF_SLM.H:819
int landtype0
Definition: ERF_SLM.H:751
amrex::Geometry m_lsm_geom0
Definition: ERF_SLM.H:717
amrex::MultiFab phi_2
Definition: ERF_SLM.H:821
amrex::Real mws_mx0
Definition: ERF_SLM.H:772
amrex::MultiFab evp_soil
Definition: ERF_SLM.H:859
amrex::Vector< std::string > LsmFluxName
Definition: ERF_SLM.H:705
amrex::MultiFab IR_emis_grnd
Definition: ERF_SLM.H:824
amrex::iMultiFab nroot
Definition: ERF_SLM.H:807
amrex::MultiFab ztop
Definition: ERF_SLM.H:825
amrex::MultiFab albedovis_s
Definition: ERF_SLM.H:864
int klo_lsm
Definition: ERF_SLM.H:730
amrex::MultiFab albedonir_s
Definition: ERF_SLM.H:864
amrex::MultiFab albold_noahmp
Definition: ERF_SLM.H:964
void slm_init()
Definition: ERF_SLM.cpp:939
amrex::MultiFab * Lsm_Data_Ptr(const int &varIdx) override
Definition: ERF_SLM.H:303
amrex::MultiFab Rgl
Definition: ERF_SLM.H:827
amrex::MultiFab IR_emis_vege
Definition: ERF_SLM.H:822
amrex::MultiFab sstxy
Definition: ERF_SLM.H:810
amrex::MultiFab IMPERV
Definition: ERF_SLM.H:835
int m_lsm_data_size
Definition: ERF_SLM.H:694
amrex::Vector< std::string > LsmDataName
Definition: ERF_SLM.H:704
amrex::BoxArray ba_lsm_2d
Definition: ERF_SLM.H:722
amrex::Geometry m_lsm_geom
Definition: ERF_SLM.H:714
amrex::MultiFab BAI
Definition: ERF_SLM.H:834
amrex::Real zref
Definition: ERF_SLM.H:784
amrex::MultiFab lhf_soil
Definition: ERF_SLM.H:856
amrex::MultiFab * z_phys_nd
Definition: ERF_SLM.H:976
amrex::MultiFab phi_1
Definition: ERF_SLM.H:820
amrex::Vector< int > LsmDataMap
Definition: ERF_SLM.H:700
amrex::MultiFab albedonir_v
Definition: ERF_SLM.H:863
amrex::MultiFab dleaf
Definition: ERF_SLM.H:817
amrex::Vector< int > unmapped_fields
Definition: ERF_SLM.H:1027
amrex::MultiFab lhf_air
Definition: ERF_SLM.H:857
amrex::MultiFab mws_mx
Definition: ERF_SLM.H:833
amrex::MultiFab disp_hgt
Definition: ERF_SLM.H:826
amrex::MultiFab albedovis_v
Definition: ERF_SLM.H:863
amrex::MultiFab tauss_noahmp
Definition: ERF_SLM.H:965
amrex::MultiFab soilt_vars
Definition: ERF_SLM.H:840
amrex::MultiFab hs_rc
Definition: ERF_SLM.H:829
amrex::MultiFab soilw_vars
Definition: ERF_SLM.H:841
amrex::Real LAI0
Definition: ERF_SLM.H:753
amrex::MultiFab Rc_min
Definition: ERF_SLM.H:828
amrex::Vector< int > LsmFluxMap
Definition: ERF_SLM.H:701
amrex::MultiFab zrefxy
Definition: ERF_SLM.H:785
amrex::MultiFab IR_emis_soil
Definition: ERF_SLM.H:823
@ NumVars
Definition: ERF_SLM.H:105
@ alb_nir_sfc_diff
Definition: ERF_SLM.H:91
@ emis_sfc
Definition: ERF_SLM.H:88
@ NumVars
Definition: ERF_SLM.H:93
@ clay
Definition: ERF_SLM.H:52
@ vegtype
Definition: ERF_SLM.H:77
@ soiltype
Definition: ERF_SLM.H:78
@ soilw_nudge
Definition: ERF_SLM.H:56
@ soilt_nudge
Definition: ERF_SLM.H:57
@ alb_vis_sfc_diff
Definition: ERF_SLM.H:92
@ node_z
Definition: ERF_SLM.H:54
@ veg_frac
Definition: ERF_SLM.H:79
@ veg_frac_max
Definition: ERF_SLM.H:81
@ alb_vis_sfc
Definition: ERF_SLM.H:90
@ veg_frac_min
Definition: ERF_SLM.H:80
@ lai
Definition: ERF_SLM.H:76
@ alb_nir_sfc
Definition: ERF_SLM.H:89
@ sand
Definition: ERF_SLM.H:51
@ ng
Definition: ERF_Morrison.H:51
@ NumVars
Definition: ERF_SLM.H:153
@ NumVars
Definition: ERF_SLM.H:164
@ NumVars
Definition: ERF_SLM.H:141
@ NumVars
Definition: ERF_SLM.H:127
@ dz
Definition: ERF_AdvanceWDM6.cpp:273
constexpr int nsoil_noah
Definition: ERF_MetgridLandUtils.H:19
constexpr amrex::Real dzs_noah[nsoil_noah]
Definition: ERF_MetgridLandUtils.H:20
Here is the call graph for this function:

◆ Init() [3/3]

virtual void NullSurf::Init
inline
46  {
47  Init(lev, cons_in, geom, geom0,
48  domain_bcs_type, refRatio, dt_advance,
49  nc_init_file);
50  }
virtual void Init(const int &, const amrex::MultiFab &, const amrex::Geometry &, const amrex::Geometry &, amrex::Vector< amrex::BCRec > &, amrex::IntVect &, const amrex::Real &, amrex::Vector< amrex::Vector< std::string >> &)
Definition: ERF_NullSurf.H:27

◆ init_from_inputs()

void SLM::init_from_inputs ( )

Initialize SLM from inputs.

Initializes for idealized cases if provided:

  • Reads uniform land, vegetation, soil, and surface-property inputs.
  • Reads layer-dependent soil properties when not using WRFInput. Initializes other options:
  • Configures soil nudging and relaxation-height values.
  • Loads optional Noah-MP parameter tables and radiation settings.
516 {
517  ParmParse pp("slm");
518  pp.query("landtype0", landtype0);
519  pp.query("LAI0", LAI0);
520 
521  auto validate_value = [](const std::string& name, const amrex::Real value,
522  const std::optional<amrex::Real> lower = std::nullopt,
523  const std::optional<amrex::Real> upper = std::nullopt)
524  {
525  if (!std::isfinite(value) || (lower && value < *lower) || (upper && value > *upper)) {
526  amrex::Abort("SLM: invalid value for '" + name + "'");
527  }
528  };
529 
530  auto const get_layer_prop = [&pp, &validate_value](
531  const std::string& name, const int nz, amrex::Vector<amrex::Real>& prop,
532  const std::optional<amrex::Real> lower = std::nullopt,
533  const std::optional<amrex::Real> upper = std::nullopt)
534  {
535  int nval = pp.countval(name.c_str());
536 
537  if (nval == 1)
538  {
539  // read single val and fill prop array over all soil layers
541  pp.query(name.c_str(), tmp);
542  prop.resize(nz);
543  std::fill(prop.begin(), prop.end(), tmp);
544  } else {
545  pp.queryarr(name.c_str(), prop);
546  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(static_cast<int>(prop.size()) == nz, " Expected " + name + " to have " + std::to_string(nz) + " values!");
547  }
548  for (const amrex::Real value : prop) {
549  validate_value(name, value, lower, upper);
550  }
551  };
552 
553  pp.query("soiltnudging", dosoiltnudging);
554  pp.query("soilwnudging", dosoilwnudging);
555  pp.query("tausoil", tausoil);
556 
557  if (!use_wrfinput) {
558  get_layer_prop("clay0", m_nz_lsm, clay0, 0.0, 100.0);
559  get_layer_prop("sand0", m_nz_lsm, sand0, 0.0, 100.0);
560  get_layer_prop("sw0", m_nz_lsm, sw0, 0.0, 1.0);
561  get_layer_prop("st0", m_nz_lsm, st0);
562  }
564  get_layer_prop("relax_hgt", m_nz_lsm, relax_hgt, 0.0, 1.0);
565  } else {
566  relax_hgt.resize(m_nz_lsm);
567  std::fill(relax_hgt.begin(), relax_hgt.end(), 0.0);
568  }
569 
570  if (!use_wrfinput) {
571  for (int i = 0; i < m_nz_lsm; i++)
572  {
573  amrex::Print() << " " << i << ": soilt = " << st0[i] << " soilw = " << sw0[i] << " clay = " << clay0[i] << " sand = " << sand0[i] << " relax = " << relax_hgt[i] << std::endl;
574  }
575  }
576 
577  pp.query("tabs_s", tabs_s);
578  pp.query("t00", t00);
579 
580  pp.query("z0_soil", z0_soil);
581  pp.query("mws_mx0", mws_mx0);
582  pp.query("Rc_max", Rc_max);
583  pp.query("T_opt", T_opt);
584  pp.query("zref", zref);
585 
586  validate_value("tausoil", tausoil, 0.0);
587  validate_value("z0_soil", z0_soil, 0.0);
588  validate_value("Rc_max", Rc_max, 0.0);
589  validate_value("zref", zref, 0.0);
590  validate_value("LAI0", LAI0, 0.0);
591  validate_value("mws_mx0", mws_mx0, 0.0);
592  validate_value("T_opt", T_opt);
593  validate_value("tabs_s", tabs_s);
594  validate_value("t00", t00);
595 
596  // Read NoahmpTable.TBL
597  pp.query("use_parameter_file", use_param_file);
598  pp.query("interpolate_lai", interpolate_lai);
599  pp.query("parameter_file", parameter_file);
600  pp.query("veg_dataset", veg_dataset);
601  pp.query("soil_dataset", soil_dataset);
602  pp.query("use_param_tbl", use_wrf_lai);
603 
604  if (use_param_file) {
605  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(use_wrf_lai != use_param_file, "Cannot use parameter file and parameter table, must choose one method");
606  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(parameter_file != "", "Using parameter file, but file path is empty!");
607  }
608 
609  // Read NoahMP table if provided
610  if (use_param_file && parameter_file != "") {
613 
614  std::string soil_param_key, veg_param_key;
615  const std::string soil_dataset_lower = amrex::toLower(soil_dataset);
616  const std::string veg_dataset_lower = amrex::toLower(veg_dataset);
617  if (soil_dataset_lower == "stas_ruc") {
618  soil_param_key = "noahmp_soil_stas_ruc_parameters";
619  } else if (soil_dataset_lower == "stas") {
620  soil_param_key = "noahmp_soil_stas_parameters";
621  } else {
622  amrex::Abort("Unrecognized soil dataset type, expected 'stas_ruc' or 'stas'");
623  }
624 
625  if (veg_dataset_lower == "usgs") {
626  veg_param_key = "noahmp_usgs_parameters";
627  } else if (veg_dataset_lower == "modis") {
628  veg_param_key = "noahmp_modis_parameters";
629  } else {
630  amrex::Abort("Unrecognized vegetation dataset type, expected 'usgs' or 'modis'");
631  }
632 
633  // Copy soil and vegetation datasets to GPU
634  auto &h_soil_params = full_params.at(soil_param_key);
635  auto &h_veg_params = full_params.at(veg_param_key);
636 
637  for (int i = 0; i < static_cast<int>(h_soil_params.size()); ++i) {
638  const int varsize = static_cast<int>(h_soil_params[i].second.size());
639  if (varsize > 1) {
640  //amrex::Print() << " -- copying soil param '" << h_soil_params[i].first << "' to GPU " << std::endl;
641  auto d_var = std::make_unique<amrex::Gpu::DeviceVector<amrex::Real>>(varsize);
642  auto *d_var_ptr = d_var.get();
643  d_soil_params.insert(std::make_pair(h_soil_params[i].first, std::move(d_var)));
644  Gpu::copyAsync(Gpu::hostToDevice, h_soil_params[i].second.data(), h_soil_params[i].second.data()+varsize, d_var_ptr->data());
645  }
646  }
647 
648  for (int i = 0; i < static_cast<int>(h_veg_params.size()); ++i) {
649  const int varsize = static_cast<int>(h_veg_params[i].second.size());
650  if (h_veg_params[i].first == "nroot") {
651  for (const amrex::Real value : h_veg_params[i].second) {
653  value >= zero && value <= m_nz_lsm && value == std::floor(value),
654  "SLM: NoahMP NROOT must be an integer in [0, slm.nsoil]");
655  }
656  }
657  if (varsize > 1) {
658  //amrex::Print() << " -- copying veg param '" << h_veg_params[i].first << "' to GPU " << std::endl;
659  auto d_var = std::make_unique<amrex::Gpu::DeviceVector<amrex::Real>>(varsize);
660  auto *d_var_ptr = d_var.get();
661  d_veg_params.insert(std::make_pair(h_veg_params[i].first, std::move(d_var)));
662  Gpu::copyAsync(Gpu::hostToDevice, h_veg_params[i].second.data(), h_veg_params[i].second.data()+varsize, d_var_ptr->data());
663  }
664  }
665 
666  Gpu::streamSynchronize();
667 
668  // Read radiation parameters from NoahmpTable.TBL
669  // Read noahmp_rad_parameters block
670  if (full_params.count("noahmp_rad_parameters") > 0) {
671  auto &h_rad_params = full_params.at("noahmp_rad_parameters");
672  for (int i = 0; i < static_cast<int>(h_rad_params.size()); ++i) {
673  std::string pname = h_rad_params[i].first;
674  const int varsize = static_cast<int>(h_rad_params[i].second.size());
675 
676  if (varsize > 1 &&
677  (pname == "albsat_vis" || pname == "albsat_nir" ||
678  pname == "albdry_vis" || pname == "albdry_nir" ||
679  pname == "alblak" || pname == "omegas")) {
680  auto d_var = std::make_unique<amrex::Gpu::DeviceVector<amrex::Real>>(varsize);
681  auto *d_var_ptr = d_var.get();
682  d_rad_params.insert(std::make_pair(pname, std::move(d_var)));
683  Gpu::copyAsync(Gpu::hostToDevice, h_rad_params[i].second.data(),
684  h_rad_params[i].second.data()+varsize, d_var_ptr->data());
685  } else if (pname == "betads" && varsize == 1) {
686  betads_rad = h_rad_params[i].second[0];
687  } else if (pname == "betais" && varsize == 1) {
688  betais_rad = h_rad_params[i].second[0];
689  }
690  }
691  }
692 
693  // Read noahmp_global_parameters block for snow albedo parameters
694  if (full_params.count("noahmp_global_parameters") > 0) {
695  auto &h_global_params = full_params.at("noahmp_global_parameters");
696  for (int i = 0; i < static_cast<int>(h_global_params.size()); ++i) {
697  std::string pname = h_global_params[i].first;
698  const int varsize = static_cast<int>(h_global_params[i].second.size());
699 
700  if (varsize == 1) {
701  if (pname == "tau0") tau0_rad = h_global_params[i].second[0];
702  else if (pname == "grain_growth") grain_growth_rad = h_global_params[i].second[0];
703  else if (pname == "extra_growth") extra_growth_rad = h_global_params[i].second[0];
704  else if (pname == "dirt_soot") dirt_soot_rad = h_global_params[i].second[0];
705  else if (pname == "bats_cosz") bats_cosz_rad = h_global_params[i].second[0];
706  else if (pname == "bats_vis_new") bats_vis_new_rad = h_global_params[i].second[0];
707  else if (pname == "bats_nir_new") bats_nir_new_rad = h_global_params[i].second[0];
708  else if (pname == "bats_vis_age") bats_vis_age_rad = h_global_params[i].second[0];
709  else if (pname == "bats_nir_age") bats_nir_age_rad = h_global_params[i].second[0];
710  else if (pname == "bats_vis_dir") bats_vis_dir_rad = h_global_params[i].second[0];
711  else if (pname == "bats_nir_dir") bats_nir_dir_rad = h_global_params[i].second[0];
712  else if (pname == "swemx") swemx_rad = h_global_params[i].second[0];
713  else if (pname == "snow_emis") snow_emis_rad = h_global_params[i].second[0];
714  else if (pname == "rsurf_exp") rsurf_exp = h_global_params[i].second[0];
715  } else if (varsize == 2) {
716  if (pname == "eg") {
717  eg_soil_rad = h_global_params[i].second[0]; // IST=1 (soil)
718  eg_lake_rad = h_global_params[i].second[1]; // IST=2 (lake)
719  }
720  }
721  }
722  }
723 
724  Gpu::streamSynchronize();
725 
726  amrex::Print() << " SLM: NOAHMP radiation parameters loaded from " << parameter_file << std::endl;
727  } else {
729  }
730 
731  if (interpolate_lai) {
732  pp.gettable("lai", lai_table);
733  pp.gettable("sai", sai_table);
734 
735  // validate LAI and SAI tables
736  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(static_cast<int>(lai_table.size()) == 12, "Invalid LAI table size, expected values for all 12 months");
737  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(static_cast<int>(sai_table.size()) == 12, "Invalid SAI table size, expected values for all 12 months");
738  if (lai_table[0].empty()) {
739  amrex::Abort("SLM: LAI table must contain at least one landtype column");
740  }
741  num_landtypes = static_cast<int>(lai_table[0].size()); // use first entry as size, check all others against
742  for (int i = 0; i < static_cast<int>(lai_table.size()); ++i) {
743  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(static_cast<int>(lai_table[i].size()) == num_landtypes, "Invalid LAI table - inconsistent number of landtype entries between months");
744  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(static_cast<int>(sai_table[i].size()) == num_landtypes, "Invalid SAI table - inconsistent number of landtype entries between months");
745  }
746 
747  d_lai_curr.resize(num_landtypes);
748  d_lai_next.resize(num_landtypes);
749  d_sai_curr.resize(num_landtypes);
750  d_sai_next.resize(num_landtypes);
751  }
752 
753  if (use_wrf_lai) {
754  pp.gettable("vegparam", param_table);
755  if (static_cast<int>(param_table.size()) < 16) {
756  amrex::Abort("SLM: vegparam must contain at least 16 landtype rows");
757  }
758  if (static_cast<int>(param_table[0].size()) < 10) {
759  amrex::Abort("SLM: vegparam must contain at least 10 columns");
760  }
761  amrex::Print() << " param table = " << std::endl;
762 
763  const int nparam = static_cast<int>(param_table[0].size());
764  for (int t = 0; t < static_cast<int>(param_table.size()); ++t) {
765  amrex::Print() << " LANDTYPE " << t << ": ";
766  for (int i = 0; i < static_cast<int>(param_table[t].size()); ++i) {
767  amrex::Print() << param_table[t][i] << " ";
768  }
769  amrex::Print() << std::endl;
770 
771  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(static_cast<int>(param_table[t].size()) == nparam, "Invalid param table, inconsistent number of parameters for landtype");
772  if (param_table[t][0] != t + 1) {
773  amrex::Abort("SLM: vegparam landtype IDs must be sequential");
774  }
775  }
776 
777  // Copy parameter table to GPU
778  d_param_table = TableData<Real, 2>({0, 0}, {static_cast<int>(param_table.size()), nparam});
779 #ifdef AMREX_USE_GPU
780  TableData<Real, 2> h_param_table({0, 0}, {static_cast<int>(param_table.size()), nparam}, amrex::The_Pinned_Arena());
781  auto const &h_tab = h_param_table.table();
782 #else
783  auto const &h_tab = d_param_table.table();
784 #endif
785  for (int t = 0; t < static_cast<int>(param_table.size()); ++t) {
786  for (int i = 0; i < static_cast<int>(param_table[t].size()); ++i) {
787  h_tab(t, i) = param_table[t][i];
788  }
789  }
790 #ifdef AMREX_USE_GPU
791  d_param_table.copy(h_param_table);
792  Gpu::streamSynchronize();
793 #endif
794  }
795 
796  pp.query("rad_input_file", rad_input_file);
797  if (rad_input_file != "") {
798 #ifndef ERF_USE_NETCDF
799  amrex::Abort("ERF needs to be compiled with NetCDF to use the SLM rad_input_file option!");
800 #endif
801 #ifdef ERF_USE_NETCDF
802  ncutils::NCFile rad_forcing_ncf = ncutils::NCFile::open(rad_input_file, NC_NOWRITE);
803 
804  ncutils::NCVar rad_time = rad_forcing_ncf.var("time");
805  num_rad_times = rad_time.shape()[0];
806  rad_times.resize(num_rad_times);
807  rad_time.get(rad_times.dataPtr(), {0}, {static_cast<unsigned long>(num_rad_times)});
808 
809  amrex::Real start_rad_time = rad_times[0];
810  for (int i = 0; i < num_rad_times; i++)
811  {
812  rad_times[i] = (rad_times[i] - start_rad_time) * 86400.0; // shift relative to first time
813  }
814 
815  ncutils::NCDim rad_x = rad_forcing_ncf.dim("x");
816  ncutils::NCDim rad_y = rad_forcing_ncf.dim("y");
817 
818  amrex::Print() << " Read radiation forcing file '" << rad_input_file << "'" << std::endl;
819  amrex::Print() << " rad forcing file has " << num_rad_times << " time values, x = " << rad_x.len() << " y = " << rad_y.len() << std::endl;
820  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(m_lsm_geom.Domain().length(0) == static_cast<int>(rad_x.len()) && m_lsm_geom.Domain().length(1) == static_cast<int>(rad_y.len()), "Radiation input file must have same X and Y dimensions!");
821 
822  BoxList bl_rad = ba_lsm_2d.boxList();
823  for (auto& b : bl_rad) {
824  b.setSmall(2, 0);
825  b.setBig(2, num_rad_times - 1);
826  }
827  BoxArray ba_rad = BoxArray(std::move(bl_rad));
828  IntVect ng_2d(0, 0, 0);
829 
830  // rad_input has 6 components: SWVIS, SWNIR, SWVISD, SWNIRD, COSZRS, LWDS
831  rad_input_data.define(ba_rad, landmask.distributionMap, 6, ng_2d);
832  rad_input_data.setVal(zero);
833 
834  amrex::Arena* Arena_Used = amrex::The_Arena();
835 #ifdef AMREX_USE_GPU
836  Arena_Used = amrex::The_Pinned_Arena();
837 #endif
838 
839  for(int comp = 0; comp < rad_input_data.nComp(); ++comp) {
840 
841  ncutils::NCVar rad_var = rad_forcing_ncf.var(rad_names[comp]);
842  amrex::Print() << " Reading radiation input var " << rad_var.name() << std::endl;
843 
844  for ( MFIter mfi(rad_input_data); mfi.isValid(); ++mfi) {
845  const auto& box = mfi.fabbox();
846 
847  FArrayBox tmp(box, 1, Arena_Used);
848  auto *dataPtr = tmp.dataPtr();
849  AMREX_ALWAYS_ASSERT(dataPtr != nullptr);
850 
851  std::vector<size_t> starts = {0,
852  static_cast<unsigned long>(box.loVect()[1]),
853  static_cast<unsigned long>(box.loVect()[0])};
854  std::vector<size_t> counts = {static_cast<unsigned long>(num_rad_times),
855  static_cast<unsigned long>(box.length()[1]),
856  static_cast<unsigned long>(box.length()[0])};
857 
858  amrex::Print() << " -- reading {" << counts[0] << "," << counts[1] << "," << counts[2] << "} at index {" << starts[0] << "," << starts[1] << "," << starts[2] << "}" << std::endl;
859  rad_var.get(dataPtr, starts, counts);
860 
861  auto mf_arr = rad_input_data.array(mfi, comp);
862  auto tmp_arr = tmp.array();
863  ParallelFor(box, [=] AMREX_GPU_DEVICE (int i, int j, int k)
864  {
865  mf_arr(i, j, k) = tmp_arr(i, j, k);
866  });
867  }
868  }
869 
870  /*
871  for ( MFIter mfi(rad_input_data); mfi.isValid(); ++mfi)
872  {
873  const auto& box = mfi.fabbox();
874  auto rad_arr = rad_input_data.const_array(mfi);
875  ParallelFor(box, rad_names.size(), [=] AMREX_GPU_DEVICE (int i, int j, int k, int n)
876  {
877  amrex::Print() << " i = " << i << " j = " << j << " k = " << k << " n = " << n << " RADVAR = " << rad_arr(i, j, k, n) << std::endl;
878  });
879  }
880  */
881 #endif
882  }
883 
884  shf_soil.setVal(zero);
885  lhf_soil.setVal(zero);
886 
887  r_a.setVal(1.0e9);
888  r_b.setVal(1.0e4);
889  r_c.setVal(1.0e4);
890  r_d.setVal(1.0e4);
891 
892  mw.setVal(zero);
893  mws.setVal(zero);
894 
895  mw_inc.setVal(zero);
896  evapo_dry.setVal(zero);
897 }
Arena * Arena_Used
Definition: ERF_Morrison_Advance_F.H:23
std::string name
Definition: ERF_Plotfile2DCatalog.cpp:116
amrex::Vector< amrex::Real > sand0
Definition: ERF_SLM.H:755
amrex::Real snow_emis_rad
Definition: ERF_SLM.H:943
amrex::Real bats_cosz_rad
Definition: ERF_SLM.H:933
amrex::Gpu::DeviceVector< amrex::Real > d_sai_next
Definition: ERF_SLM.H:906
void load_builtin_radiation_parameters()
Definition: ERF_SLM.cpp:902
amrex::Real bats_vis_dir_rad
Definition: ERF_SLM.H:938
std::unordered_map< std::string, std::unique_ptr< amrex::Gpu::DeviceVector< amrex::Real > > > d_veg_params
Definition: ERF_SLM.H:897
amrex::Vector< std::string > param_soil_categories
Definition: ERF_SLM.H:894
amrex::Real tabs_s
Definition: ERF_SLM.H:760
bool use_param_file
Definition: ERF_SLM.H:908
amrex::Real tau0_rad
Definition: ERF_SLM.H:928
amrex::Vector< amrex::Real > sw0
Definition: ERF_SLM.H:756
amrex::Real bats_vis_age_rad
Definition: ERF_SLM.H:936
amrex::Real T_opt
Definition: ERF_SLM.H:783
bool dosoiltnudging
Definition: ERF_SLM.H:767
amrex::Real bats_vis_new_rad
Definition: ERF_SLM.H:934
amrex::Real tausoil
Definition: ERF_SLM.H:768
bool use_wrf_lai
Definition: ERF_SLM.H:910
amrex::Real betads_rad
Definition: ERF_SLM.H:924
SLMParameterTable ReadParameterFile(const std::string &filename, amrex::Vector< std::string > &veg_categories, amrex::Vector< std::string > &soil_categories)
Definition: ERF_SLM.cpp:2360
amrex::Gpu::DeviceVector< amrex::Real > d_lai_next
Definition: ERF_SLM.H:904
std::string veg_dataset
Definition: ERF_SLM.H:886
void validate_parameter_tables()
Definition: ERF_SLM.cpp:2461
amrex::Real dirt_soot_rad
Definition: ERF_SLM.H:931
amrex::Real swemx_rad
Definition: ERF_SLM.H:940
SLMParameterTable full_params
Definition: ERF_SLM.H:892
amrex::Real bats_nir_new_rad
Definition: ERF_SLM.H:935
amrex::Vector< std::string > param_veg_categories
Definition: ERF_SLM.H:893
amrex::Vector< amrex::Real > clay0
Definition: ERF_SLM.H:754
amrex::Real bats_nir_dir_rad
Definition: ERF_SLM.H:939
amrex::Real Rc_max
Definition: ERF_SLM.H:782
std::string parameter_file
Definition: ERF_SLM.H:885
bool dosoilwnudging
Definition: ERF_SLM.H:766
bool interpolate_lai
Definition: ERF_SLM.H:909
std::vector< std::vector< amrex::Real > > sai_table
Definition: ERF_SLM.H:900
amrex::Vector< amrex::Real > st0
Definition: ERF_SLM.H:757
amrex::TableData< amrex::Real, 2 > d_param_table
Definition: ERF_SLM.H:968
amrex::Real extra_growth_rad
Definition: ERF_SLM.H:930
amrex::Gpu::DeviceVector< amrex::Real > d_sai_curr
Definition: ERF_SLM.H:905
amrex::Real grain_growth_rad
Definition: ERF_SLM.H:929
amrex::Gpu::DeviceVector< amrex::Real > d_lai_curr
Definition: ERF_SLM.H:903
int num_landtypes
Definition: ERF_SLM.H:888
std::vector< std::vector< amrex::Real > > lai_table
Definition: ERF_SLM.H:901
amrex::Real eg_lake_rad
Definition: ERF_SLM.H:945
amrex::Real bats_nir_age_rad
Definition: ERF_SLM.H:937
amrex::Real betais_rad
Definition: ERF_SLM.H:925
amrex::Vector< amrex::Real > relax_hgt
Definition: ERF_SLM.H:758
std::unordered_map< std::string, std::unique_ptr< amrex::Gpu::DeviceVector< amrex::Real > > > d_rad_params
Definition: ERF_SLM.H:898
amrex::Real eg_soil_rad
Definition: ERF_SLM.H:944
amrex::Real z0_soil
Definition: ERF_SLM.H:770
std::vector< std::vector< amrex::Real > > param_table
Definition: ERF_SLM.H:967
amrex::Real rsurf_exp
Definition: ERF_SLM.H:932
amrex::Real t00
Definition: ERF_SLM.H:761
std::string soil_dataset
Definition: ERF_SLM.H:887
std::unordered_map< std::string, std::unique_ptr< amrex::Gpu::DeviceVector< amrex::Real > > > d_soil_params
Definition: ERF_SLM.H:896
Definition: ERF_NCInterface.H:351
@ tmp
Definition: ERF_AdvanceWSM6.cpp:116
void fill(MultiFab &dst, int temperature_comp, int mixing_ratio_comp, int source_comp, const Sources &sources, Real missing_value)
Definition: ERF_NearSurfaceDiagnostics.cpp:42
Definition: ERF_NCInterface.H:333
Definition: ERF_NCInterface.H:339
Here is the call graph for this function:

◆ init_from_params()

void SLM::init_from_params ( )

Overwrites any default SLM variables from values set in the parameter file

2675 {
2676  // do nothing if not using the parameter file
2677  if (!use_param_file) {
2678  return;
2679  }
2680 
2681  const int d_khi_lsm = khi_lsm;
2682  const int d_klo_lsm = klo_lsm;
2683 
2684  // Get pointers to GPU soil parameter values
2685  const amrex::Real *d_param_poro = d_soil_params.at("maxsmc")->data();
2686  const amrex::Real *d_param_theta_FC = d_soil_params.at("refsmc")->data();
2687  const amrex::Real *d_param_theta_WP = d_soil_params.at("wltsmc")->data();
2688  const amrex::Real *d_param_m_pot_sat = d_soil_params.at("satpsi")->data();
2689  const amrex::Real *d_param_Bconst = d_soil_params.at("bb")->data();
2690  const amrex::Real *d_param_ks = d_soil_params.at("satdk")->data();
2691 
2692  // Get pointers to GPU vegetation parameter values from NoahMP table
2693  const amrex::Real *d_param_rs = nullptr;
2694  const amrex::Real *d_param_rgl = nullptr;
2695  const amrex::Real *d_param_xl = nullptr;
2696  const amrex::Real *d_param_hs = nullptr;
2697  const amrex::Real *d_param_nroot = nullptr;
2698  const amrex::Real *d_param_hvt = nullptr;
2699  const amrex::Real *d_param_z0mvt = nullptr;
2700  const amrex::Real *d_param_cbiom = nullptr;
2701  const amrex::Real *d_param_dleaf = nullptr;
2702 
2703  // Check if vegetation parameters are available
2704  bool has_rs = false, has_rgl = false, has_xl = false, has_hs = false;
2705  bool has_nroot = false, has_hvt = false, has_z0mvt = false;
2706  bool has_cbiom = false, has_dleaf = false;
2707  if (d_veg_params.find("rs") != d_veg_params.end()) {
2708  d_param_rs = d_veg_params.at("rs")->data();
2709  has_rs = true;
2710  amrex::Print() << " SLM: Using RS (minimum stomatal resistance) from NoahMP parameter file" << std::endl;
2711  }
2712  if (d_veg_params.find("rgl") != d_veg_params.end()) {
2713  d_param_rgl = d_veg_params.at("rgl")->data();
2714  has_rgl = true;
2715  amrex::Print() << " SLM: Using RGL (radiation stress parameter) from NoahMP parameter file" << std::endl;
2716  }
2717  if (d_veg_params.find("xl") != d_veg_params.end()) {
2718  d_param_xl = d_veg_params.at("xl")->data();
2719  has_xl = true;
2720  amrex::Print() << " SLM: Using XL (leaf/stem orientation index) from NoahMP parameter file" << std::endl;
2721  }
2722  if (d_veg_params.find("hs") != d_veg_params.end()) {
2723  d_param_hs = d_veg_params.at("hs")->data();
2724  has_hs = true;
2725  amrex::Print() << " SLM: Using HS (VPD sensitivity parameter) from NoahMP parameter file" << std::endl;
2726  amrex::Print() << " SLM: Note - VPD formula changed to match NoahMP: 1/(1+HS*VPD)" << std::endl;
2727  }
2728  if (d_veg_params.find("nroot") != d_veg_params.end()) {
2729  d_param_nroot = d_veg_params.at("nroot")->data();
2730  has_nroot = true;
2731  amrex::Print() << " SLM: Using NROOT from NoahMP parameter file for the BTR=1 soil-water stress factor" << std::endl;
2732  }
2733  if (d_veg_params.find("hvt") != d_veg_params.end()) {
2734  d_param_hvt = d_veg_params.at("hvt")->data();
2735  has_hvt = true;
2736  amrex::Print() << " SLM: Using HVT (canopy top height) from NoahMP parameter file" << std::endl;
2737  }
2738  if (d_veg_params.find("z0mvt") != d_veg_params.end()) {
2739  d_param_z0mvt = d_veg_params.at("z0mvt")->data();
2740  has_z0mvt = true;
2741  amrex::Print() << " SLM: Using Z0MVT (vegetation momentum roughness) from NoahMP parameter file" << std::endl;
2742  }
2743  if (d_veg_params.find("cbiom") != d_veg_params.end()) {
2744  d_param_cbiom = d_veg_params.at("cbiom")->data();
2745  has_cbiom = true;
2746  amrex::Print() << " SLM: Using CBIOM (canopy biomass heat capacity parameter) from NoahMP parameter file" << std::endl;
2747  }
2748  if (d_veg_params.find("dleaf") != d_veg_params.end()) {
2749  d_param_dleaf = d_veg_params.at("dleaf")->data();
2750  has_dleaf = true;
2751  amrex::Print() << " SLM: Using DLEAF (characteristic leaf dimension) from NoahMP parameter file" << std::endl;
2752  }
2753 
2754  for ( amrex::MFIter mfi(landtype, TileNoZ()); mfi.isValid(); ++mfi) {
2755  amrex::Box bx2d = mfi.tilebox();
2756 
2757  auto landmask_arr = landmask.const_array(mfi);
2758  auto landtype_arr = landtype.const_array(mfi);
2759  auto vegetype_arr = vegetype.const_array(mfi);
2760  auto soiltype_arr = lsm_fab_vars[LsmVar_SLM::soiltype]->const_array(mfi);
2761 
2762  auto poro_soil_arr = lsm_fab_vars[LsmVar_SLM::poro_soil]->array(mfi);
2763  auto theta_FC_arr = lsm_fab_vars[LsmVar_SLM::theta_FC]->array(mfi);
2764  auto theta_WP_arr = lsm_fab_vars[LsmVar_SLM::theta_WP]->array(mfi);
2765  auto m_pot_sat_arr = lsm_fab_vars[LsmVar_SLM::m_pot_sat]->array(mfi);
2766  auto Bconst_arr = lsm_fab_vars[LsmVar_SLM::Bconst]->array(mfi);
2767  auto ks_arr = lsm_fab_vars[LsmVar_SLM::ks]->array(mfi);
2768 
2769  // Get vegetation parameter arrays
2770  auto Rc_min_arr = Rc_min.array(mfi);
2771  auto Rgl_arr = Rgl.array(mfi);
2772  auto Khai_L_arr = Khai_L.array(mfi);
2773  auto hs_rc_arr = hs_rc.array(mfi);
2774  auto nroot_arr = nroot.array(mfi);
2775  auto ztop_arr = ztop.array(mfi);
2776  auto disp_hgt_arr = disp_hgt.array(mfi);
2777  auto z0_sfc_arr = z0_sfc.array(mfi);
2778  auto cbiom_arr = cbiom.array(mfi);
2779  auto dleaf_arr = dleaf.array(mfi);
2780 
2781  amrex::ParallelFor(bx2d, [=] AMREX_GPU_DEVICE(int i, int j, int) noexcept {
2782  if (landmask_arr(i, j, 0) == 1) {
2783 
2784  const int ltype = landtype_arr(i,j,0) - 1; // shift by one to match table index (i.e, types 1-20 -> index 0-19)
2785  const int stype = static_cast<int>(soiltype_arr(i,j,d_khi_lsm)) - 1;
2786 
2787  // Set soil parameters from parameter file
2788  for (int k = d_khi_lsm; k >= d_klo_lsm; k--) {
2789  poro_soil_arr(i, j, k) = d_param_poro[stype];
2790  theta_FC_arr(i, j, k) = d_param_theta_FC[stype];
2791  theta_WP_arr(i, j, k) = d_param_theta_WP[stype];
2792  // NoahMP stores SATPSI as a positive magnitude in meters;
2793  // SLM uses a negative matric potential in millimeters.
2794  m_pot_sat_arr(i, j, k) = -1000.0 * d_param_m_pot_sat[stype];
2795  Bconst_arr(i, j, k) = d_param_Bconst[stype];
2796  // NoahMP SATDK is m/s; SLM soil hydraulics use mm/s.
2797  ks_arr(i, j, k) = 1000.0 * d_param_ks[stype];
2798  }
2799 
2800  // Set vegetation parameters from NoahMP table if available
2801  if (vegetype_arr(i,j,0) == 1) {
2802  if (has_rs && d_param_rs != nullptr) {
2803  // RS = minimum stomatal resistance [s/m]
2804  Rc_min_arr(i, j, 0) = d_param_rs[ltype];
2805  }
2806  if (has_rgl && d_param_rgl != nullptr) {
2807  // RGL = radiation stress parameter [W/m2]
2808  Rgl_arr(i, j, 0) = d_param_rgl[ltype];
2809  }
2810  if (has_xl && d_param_xl != nullptr) {
2811  // XL = leaf/stem orientation index (dimensionless)
2812  // Directly maps to Khai_L in SLM
2813  Khai_L_arr(i, j, 0) = d_param_xl[ltype];
2814  }
2815  if (has_hs && d_param_hs != nullptr) {
2816  // HS = VPD sensitivity parameter (dimensionless)
2817  // NOTE: This only works with the hyperbolic VPD formula: 1/(1+HS*VPD)
2818  // If using exponential formula exp(-hs*VPD), HS values are NOT compatible
2819  hs_rc_arr(i, j, 0) = d_param_hs[ltype];
2820  }
2821  if (has_nroot && d_param_nroot != nullptr) {
2822  nroot_arr(i, j, 0) = static_cast<int>(d_param_nroot[ltype]);
2823  }
2824  if (has_hvt && d_param_hvt != nullptr) {
2825  ztop_arr(i, j, 0) = d_param_hvt[ltype];
2826  disp_hgt_arr(i, j, 0) = 0.65 * ztop_arr(i, j, 0);
2827  }
2828  if (has_z0mvt && d_param_z0mvt != nullptr) {
2829  z0_sfc_arr(i, j, 0) = d_param_z0mvt[ltype];
2830  }
2831  if (has_cbiom && d_param_cbiom != nullptr) {
2832  cbiom_arr(i, j, 0) = d_param_cbiom[ltype];
2833  }
2834  if (has_dleaf && d_param_dleaf != nullptr) {
2835  dleaf_arr(i, j, 0) = d_param_dleaf[ltype];
2836  }
2837  }
2838  }
2839  });
2840  }
2841 
2842  // set a flag to indicate remaining soil variables should be recomputed
2843  params_updated = true;
2844 
2845  // recompute soil variables using updated parameters
2846  init_soil_vars();
2847 
2848  // recompute derived vegetation fields using the updated parameters.
2849  for (amrex::MFIter mfi(landtype, TileNoZ()); mfi.isValid(); ++mfi) {
2850  UpdateLAIParameters(mfi);
2851  }
2852 }
void init_soil_vars()
Definition: ERF_SLM.cpp:2021
bool params_updated
Definition: ERF_SLM.H:911
void UpdateLAIParameters(const amrex::MFIter &mfi)
Definition: ERF_SLM.cpp:3005
@ theta_FC
Definition: ERF_SLM.H:70
@ ks
Definition: ERF_SLM.H:74
@ theta_WP
Definition: ERF_SLM.H:71
Here is the call graph for this function:

◆ init_landtype()

void SLM::init_landtype ( )

Helper function to set properties according to the IGBP class

1484 {
1485  const Real d_z0_soil = z0_soil;
1486 
1487  for ( MFIter mfi(landtype, TileNoZ()); mfi.isValid(); ++mfi) {
1488  const auto& box = mfi.tilebox();
1489 
1490  auto landtype_arr = landtype.const_array(mfi);
1491  auto landmask_arr = landmask.array(mfi);
1492 #ifndef AMREX_USE_GPU
1493  auto LAI_arr = LAI.const_array(mfi);
1494 #endif
1495  auto albedovis_v_arr = albedovis_v.array(mfi);
1496  auto albedonir_v_arr = albedonir_v.array(mfi);
1497  auto albedovis_s_arr = albedovis_s.array(mfi);
1498  auto albedonir_s_arr = albedonir_s.array(mfi);
1499  auto ztop_arr = ztop.array(mfi);
1500  auto disp_hgt_arr = disp_hgt.array(mfi);
1501  auto z0_sfc_arr = z0_sfc.array(mfi);
1502  auto Khai_L_arr = Khai_L.array(mfi);
1503  auto Rc_min_arr = Rc_min.array(mfi);
1504  auto Rgl_arr = Rgl.array(mfi);
1505  auto hs_rc_arr = hs_rc.array(mfi);
1506  auto BAI_arr = BAI.array(mfi);
1507  auto IMPERV_arr = IMPERV.array(mfi);
1508  auto vegetype_arr = vegetype.array(mfi);
1509 
1510  ParallelFor(box, [=] AMREX_GPU_DEVICE (int i, int j, int)
1511  {
1512  switch (landtype_arr(i, j, 0))
1513  {
1514  case 0: // water
1515  // TODO: should this landmask be ERF's lmask_lev variable?
1516  landmask_arr(i, j, 0) = 0;
1517  vegetype_arr(i, j, 0) = 0;
1518  // set albedo for water so that it is populated for RRTMGP
1519  albedovis_v_arr(i, j, 0) = 0.06;
1520  albedonir_v_arr(i, j, 0) = 0.06;
1521  albedovis_s_arr(i, j, 0) = 0.06;
1522  albedonir_s_arr(i, j, 0) = 0.06;
1523  break;
1524  case 1: // evergreen needleleaf forest
1525  albedovis_v_arr(i, j, 0) = 0.094;
1526  albedonir_v_arr(i, j, 0) = 0.161;
1527  albedovis_s_arr(i, j, 0) = 0.208;
1528  albedonir_s_arr(i, j, 0) = 0.344;
1529  ztop_arr(i, j, 0) = 20.;
1530  disp_hgt_arr(i, j, 0) = 0.65 * ztop_arr(i, j, 0);
1531  z0_sfc_arr(i, j, 0) = 1.09;
1532  Khai_L_arr(i, j, 0) = fourth;
1533  Rc_min_arr(i, j, 0) = 250.;
1534  Rgl_arr(i, j, 0) = 120.;
1535  hs_rc_arr(i, j, 0) = 0.03;
1536  BAI_arr(i, j, 0) = 200.;
1537  IMPERV_arr(i, j, 0) = zero;
1538  vegetype_arr(i, j, 0) = 1;
1539  break;
1540  case 2: // evergreen broadleaf forest
1541  albedovis_v_arr(i, j, 0) = 0.086;
1542  albedonir_v_arr(i, j, 0) = 0.146;
1543  albedovis_s_arr(i, j, 0) = 0.208;
1544  albedonir_s_arr(i, j, 0) = 0.344;
1545  ztop_arr(i, j, 0) = 20.;
1546  disp_hgt_arr(i, j, 0) = 0.65 * ztop_arr(i, j, 0);
1547  z0_sfc_arr(i, j, 0) = one;
1548  Khai_L_arr(i, j, 0) = fourth;
1549  Rc_min_arr(i, j, 0) = 250.;
1550  Rgl_arr(i, j, 0) = 120.;
1551  hs_rc_arr(i, j, 0) = 0.03;
1552  BAI_arr(i, j, 0) = 200.;
1553  IMPERV_arr(i, j, 0) = zero;
1554  vegetype_arr(i, j, 0) = 1;
1555  break;
1556  case 3: // deciduous needleaf forest
1557  albedovis_v_arr(i, j, 0) = 0.102;
1558  albedonir_v_arr(i, j, 0) = 0.198;
1559  albedovis_s_arr(i, j, 0) = 0.208;
1560  albedonir_s_arr(i, j, 0) = 0.344;
1561  ztop_arr(i, j, 0) = 20.;
1562  disp_hgt_arr(i, j, 0) = 0.65 * ztop_arr(i, j, 0);
1563  z0_sfc_arr(i, j, 0) = 1.10;
1564  Khai_L_arr(i, j, 0) = fourth;
1565  Rc_min_arr(i, j, 0) = 250.;
1566  Rgl_arr(i, j, 0) = 120.;
1567  hs_rc_arr(i, j, 0) = 0.03;
1568  BAI_arr(i, j, 0) = 200.;
1569  IMPERV_arr(i, j, 0) = zero;
1570  vegetype_arr(i, j, 0) = 1;
1571  break;
1572  case 4: // deciduous broadleaf forest
1573  albedovis_v_arr(i, j, 0) = 0.056;
1574  albedonir_v_arr(i, j, 0) = 0.151;
1575  albedovis_s_arr(i, j, 0) = 0.208;
1576  albedonir_s_arr(i, j, 0) = 0.344;
1577  ztop_arr(i, j, 0) = 20.;
1578  disp_hgt_arr(i, j, 0) = 0.65 * ztop_arr(i, j, 0);
1579  z0_sfc_arr(i, j, 0) = 0.8;
1580  Khai_L_arr(i, j, 0) = fourth;
1581  Rc_min_arr(i, j, 0) = 250.;
1582  Rgl_arr(i, j, 0) = 120.;
1583  hs_rc_arr(i, j, 0) = 0.03;
1584  BAI_arr(i, j, 0) = 200.;
1585  IMPERV_arr(i, j, 0) = zero;
1586  vegetype_arr(i, j, 0) = 1;
1587  break;
1588  case 5: // mixed forest
1589  albedovis_v_arr(i, j, 0) = 0.093;
1590  albedonir_v_arr(i, j, 0) = 0.179;
1591  albedovis_s_arr(i, j, 0) = 0.208;
1592  albedonir_s_arr(i, j, 0) = 0.344;
1593  ztop_arr(i, j, 0) = 20.;
1594  disp_hgt_arr(i, j, 0) = 0.65 * ztop_arr(i, j, 0);
1595  z0_sfc_arr(i, j, 0) = 0.8;
1596  Khai_L_arr(i, j, 0) = fourth;
1597  Rc_min_arr(i, j, 0) = 250.;
1598  Rgl_arr(i, j, 0) = 120.;
1599  hs_rc_arr(i, j, 0) = 0.03;
1600  BAI_arr(i, j, 0) = 200.;
1601  IMPERV_arr(i, j, 0) = zero;
1602  vegetype_arr(i, j, 0) = 1;
1603  break;
1604  case 6: // closed shrublands
1605  albedovis_v_arr(i, j, 0) = 0.069;
1606  albedonir_v_arr(i, j, 0) = 0.121;
1607  albedovis_s_arr(i, j, 0) = 0.208;
1608  albedonir_s_arr(i, j, 0) = 0.344;
1609  ztop_arr(i, j, 0) = one;
1610  disp_hgt_arr(i, j, 0) = 0.65 * ztop_arr(i, j, 0);
1611  z0_sfc_arr(i, j, 0) = 0.1;
1612  Khai_L_arr(i, j, 0) = fourth;
1613  Rc_min_arr(i, j, 0) = 220.;
1614  Rgl_arr(i, j, 0) = 100.;
1615  hs_rc_arr(i, j, 0) = 0.01;
1616  BAI_arr(i, j, 0) = 60.;
1617  IMPERV_arr(i, j, 0) = zero;
1618  vegetype_arr(i, j, 0) = 1;
1619  break;
1620  case 7: // open shrublands
1621  albedovis_v_arr(i, j, 0) = 0.079;
1622  albedonir_v_arr(i, j, 0) = 0.226;
1623  albedovis_s_arr(i, j, 0) = 0.208;
1624  albedonir_s_arr(i, j, 0) = 0.344;
1625  ztop_arr(i, j, 0) = one;
1626  disp_hgt_arr(i, j, 0) = 0.65 * ztop_arr(i, j, 0);
1627  z0_sfc_arr(i, j, 0) = 0.1;
1628  Khai_L_arr(i, j, 0) = fourth;
1629  Rc_min_arr(i, j, 0) = 220.;
1630  Rgl_arr(i, j, 0) = 100.;
1631  hs_rc_arr(i, j, 0) = 0.01;
1632  BAI_arr(i, j, 0) = 60.;
1633  IMPERV_arr(i, j, 0) = zero;
1634  vegetype_arr(i, j, 0) = 1;
1635  break;
1636  case 8: // woody savannas
1637  albedovis_v_arr(i, j, 0) = 0.071;
1638  albedonir_v_arr(i, j, 0) = 0.085;
1639  albedovis_s_arr(i, j, 0) = 0.208;
1640  albedonir_s_arr(i, j, 0) = 0.344;
1641  ztop_arr(i, j, 0) = 5.;
1642  disp_hgt_arr(i, j, 0) = 0.65 * ztop_arr(i, j, 0);
1643  z0_sfc_arr(i, j, 0) = 0.3;
1644  Khai_L_arr(i, j, 0) = fourth;
1645  Rc_min_arr(i, j, 0) = 180.;
1646  Rgl_arr(i, j, 0) = 100.;
1647  hs_rc_arr(i, j, 0) = 0.02;
1648  BAI_arr(i, j, 0) = 100.;
1649  IMPERV_arr(i, j, 0) = zero;
1650  vegetype_arr(i, j, 0) = 1;
1651  break;
1652  case 9: // savannas
1653  albedovis_v_arr(i, j, 0) = 0.061;
1654  albedonir_v_arr(i, j, 0) = 0.227;
1655  albedovis_s_arr(i, j, 0) = 0.208;
1656  albedonir_s_arr(i, j, 0) = 0.344;
1657  ztop_arr(i, j, 0) = 5.;
1658  disp_hgt_arr(i, j, 0) = 0.65 * ztop_arr(i, j, 0);
1659  z0_sfc_arr(i, j, 0) = 0.3;
1660  Khai_L_arr(i, j, 0) = fourth;
1661  Rc_min_arr(i, j, 0) = 100.;
1662  Rgl_arr(i, j, 0) = 100.;
1663  hs_rc_arr(i, j, 0) = 0.01;
1664  BAI_arr(i, j, 0) = 100.;
1665  IMPERV_arr(i, j, 0) = zero;
1666  vegetype_arr(i, j, 0) = 1;
1667  break;
1668  case 10: // grasslands
1669  albedovis_v_arr(i, j, 0) = 0.09;
1670  albedonir_v_arr(i, j, 0) = 0.269;
1671  albedovis_s_arr(i, j, 0) = 0.208;
1672  albedonir_s_arr(i, j, 0) = 0.344;
1673  ztop_arr(i, j, 0) = myhalf;
1674  disp_hgt_arr(i, j, 0) = 0.65 * ztop_arr(i, j, 0);
1675  z0_sfc_arr(i, j, 0) = 0.04;
1676  Khai_L_arr(i, j, 0) = -0.3;
1677  Rc_min_arr(i, j, 0) = 100.;
1678  Rgl_arr(i, j, 0) = 100.;
1679  hs_rc_arr(i, j, 0) = 0.01;
1680  BAI_arr(i, j, 0) = 20.;
1681  IMPERV_arr(i, j, 0) = zero;
1682  vegetype_arr(i, j, 0) = 1;
1683  break;
1684  case 11: // permanent wetlands
1685  albedovis_v_arr(i, j, 0) = 0.081;
1686  albedonir_v_arr(i, j, 0) = 0.18;
1687  albedovis_s_arr(i, j, 0) = 0.208;
1688  albedonir_s_arr(i, j, 0) = 0.344;
1689  ztop_arr(i, j, 0) = myhalf;
1690  disp_hgt_arr(i, j, 0) = 0.65 * ztop_arr(i, j, 0);
1691  z0_sfc_arr(i, j, 0) = 0.2;
1692  Khai_L_arr(i, j, 0) = -0.3;
1693  Rc_min_arr(i, j, 0) = 100.;
1694  Rgl_arr(i, j, 0) = 100.;
1695  hs_rc_arr(i, j, 0) = 0.01;
1696  BAI_arr(i, j, 0) = 20.0;
1697  IMPERV_arr(i, j, 0) = zero;
1698  vegetype_arr(i, j, 0) =1;
1699  break;
1700  case 12: // croplands
1701  albedovis_v_arr(i, j, 0) = 0.084;
1702  albedonir_v_arr(i, j, 0) = 0.193;
1703  albedovis_s_arr(i, j, 0) = 0.208;
1704  albedonir_s_arr(i, j, 0) = 0.344;
1705  ztop_arr(i, j, 0) = myhalf;
1706  disp_hgt_arr(i, j, 0) = 0.65 * ztop_arr(i, j, 0);
1707  z0_sfc_arr(i, j, 0) = 0.03;
1708  Khai_L_arr(i, j, 0) = -0.3;
1709  Rc_min_arr(i, j, 0) = 100.;
1710  Rgl_arr(i, j, 0) = 100.;
1711  hs_rc_arr(i, j, 0) = 0.01;
1712  BAI_arr(i, j, 0) = 60.;
1713  IMPERV_arr(i, j, 0) = zero;
1714  vegetype_arr(i, j, 0) = 1;
1715  break;
1716  case 13: // urban
1717  albedovis_v_arr(i, j, 0) = 0.102;
1718  albedonir_v_arr(i, j, 0) = 0.164;
1719  albedovis_s_arr(i, j, 0) = 0.15;
1720  albedonir_s_arr(i, j, 0) = fourth;
1721  ztop_arr(i, j, 0) = 10.;
1722  disp_hgt_arr(i, j, 0) = 0.65 * ztop_arr(i, j, 0);
1723  z0_sfc_arr(i, j, 0) = one;
1724  Khai_L_arr(i, j, 0) = -0.3;
1725  Rc_min_arr(i, j, 0) = zero;
1726  Rgl_arr(i, j, 0) = zero;
1727  hs_rc_arr(i, j, 0) = zero;
1728  BAI_arr(i, j, 0) = zero;
1729  IMPERV_arr(i, j, 0) = 0.75;
1730  vegetype_arr(i, j, 0) = 0;
1731  break;
1732  case 14: // croplands/natural mozaics
1733  albedovis_v_arr(i, j, 0) = 0.071;
1734  albedonir_v_arr(i, j, 0) = 0.169;
1735  albedovis_s_arr(i, j, 0) = 0.208;
1736  albedonir_s_arr(i, j, 0) = 0.344;
1737  ztop_arr(i, j, 0) = myhalf;
1738  disp_hgt_arr(i, j, 0) = 0.65 * ztop_arr(i, j, 0);
1739  z0_sfc_arr(i, j, 0) = 0.04;
1740  Khai_L_arr(i, j, 0) = -0.3;
1741  Rc_min_arr(i, j, 0) = 100.;
1742  Rgl_arr(i, j, 0) = 100.;
1743  hs_rc_arr(i, j, 0) = 0.01;
1744  BAI_arr(i, j, 0) = 20.;
1745  IMPERV_arr(i, j, 0) = zero;
1746  vegetype_arr(i, j, 0) = 1;
1747  break;
1748  case 15: // snow/ice
1749  albedovis_v_arr(i, j, 0) = zero; // actually depends on moisture content
1750  albedonir_v_arr(i, j, 0) = zero;
1751  albedovis_s_arr(i, j, 0) = 0.91;
1752  albedonir_s_arr(i, j, 0) = 0.65;
1753  ztop_arr(i, j, 0) = zero;
1754  disp_hgt_arr(i, j, 0) = zero;
1755  z0_sfc_arr(i, j, 0) = 0.001;
1756  Khai_L_arr(i, j, 0) = zero;
1757  Rc_min_arr(i, j, 0) = zero;
1758  Rgl_arr(i, j, 0) = zero;
1759  hs_rc_arr(i, j, 0) = zero;
1760  BAI_arr(i, j, 0) = zero;
1761  IMPERV_arr(i, j, 0) = zero;
1762  vegetype_arr(i, j, 0) = 0;
1763  break;
1764  case 16: // baresoil
1765  albedovis_v_arr(i, j, 0) = zero; // actually depends on moisture content
1766  albedonir_v_arr(i, j, 0) = zero;
1767  albedovis_s_arr(i, j, 0) = 0.208;
1768  albedonir_s_arr(i, j, 0) = 0.344;
1769  ztop_arr(i, j, 0) = zero;
1770  disp_hgt_arr(i, j, 0) = zero;
1771  z0_sfc_arr(i, j, 0) = d_z0_soil;
1772  Khai_L_arr(i, j, 0) = zero;
1773  Rc_min_arr(i, j, 0) = zero;
1774  Rgl_arr(i, j, 0) = zero;
1775  hs_rc_arr(i, j, 0) = zero;
1776  BAI_arr(i, j, 0) = zero;
1777  IMPERV_arr(i, j, 0) = zero;
1778  vegetype_arr(i, j, 0) = 0;
1779  break;
1780  default:
1781 // TODO: check if AMReX::Abort can be called inside ParFor?
1782 #ifndef AMREX_USE_GPU
1783  amrex::Abort(
1784  "landtype invalid for i = " + std::to_string(i) +
1785  " j = " + std::to_string(j));
1786 #endif
1787  break;
1788  }
1789 #ifndef AMREX_USE_GPU
1790  if (vegetype_arr(i, j, 0) == 1 && LAI_arr(i, j, 0) == zero)
1791  {
1792  amrex::Abort("LAI is not set for vegetated land point at i = " + std::to_string(i) + " j = " + std::to_string(j));
1793  }
1794 #endif
1795  });
1796  }
1797 }
constexpr amrex::Real fourth
Definition: ERF_NumericalConstants.H:42
Here is the call graph for this function:

◆ init_layer_depths()

void SLM::init_layer_depths ( )

Calculates center and face depths for each soil layer

1977 {
1978  const int d_khi_lsm = khi_lsm;
1979  const int d_klo_lsm = klo_lsm;
1980 
1981  const bool wrfinput = use_wrfinput;
1982 
1983  for ( MFIter mfi(landtype, TileNoZ()); mfi.isValid(); ++mfi) {
1984  const auto& box = mfi.tilebox();
1985 
1986  auto s_depth_arr = lsm_fab_vars[LsmVar_SLM::s_depth]->const_array(mfi);
1987 
1988  auto node_z_arr = lsm_fab_vars[LsmVar_SLM::node_z]->array(mfi);
1989  auto interface_z_arr = lsm_fab_vars[LsmVar_SLM::interface_z]->array(mfi);
1990 
1991  ParallelFor(box, [=] AMREX_GPU_DEVICE (int i, int j, int)
1992  {
1993  // k = 0 = reference level
1994  // k = -1 = top of soil layer = khi_lsm
1995  // k = -m_nz_lsm = klo_lsm
1996 
1997  if (!wrfinput) {
1998  node_z_arr(i, j, d_khi_lsm) = myhalf * s_depth_arr(i, j, d_khi_lsm);
1999  for (int k = d_khi_lsm - 1; k >= d_klo_lsm; k--)
2000  {
2001  node_z_arr(i, j, k) = myhalf * s_depth_arr(i, j, k);
2002 
2003  for (int kk = d_khi_lsm; kk >= k + 1; kk--)
2004  {
2005  node_z_arr(i, j, k) = node_z_arr(i, j, k) + s_depth_arr(i, j, kk);
2006  }
2007  }
2008  }
2009 
2010  for (int k = d_khi_lsm; k >= d_klo_lsm; k--)
2011  {
2012  interface_z_arr(i, j, k) = node_z_arr(i, j, k) + myhalf*s_depth_arr(i, j, k);
2013  }
2014  });
2015  }
2016 }
@ interface_z
Definition: ERF_SLM.H:55
Here is the call graph for this function:

◆ init_slm_vars()

void SLM::init_slm_vars ( )
2094 {
2095  const int d_khi_lsm = khi_lsm;
2096 
2097  // Initializes SLM quantities from reference values tr, ts, qr
2098  for ( MFIter mfi(landtype, TileNoZ()); mfi.isValid(); ++mfi) {
2099  auto box = mfi.tilebox();
2100 
2101  auto landmask_arr = landmask.const_array(mfi);
2102 
2103  auto t_cas_arr = t_cas.array(mfi);
2104  auto q_cas_arr = q_cas.array(mfi);
2105  auto q_gr_arr = q_gr.array(mfi);
2106 
2107  auto t_canop_arr = t_canop.array(mfi);
2108  auto t_ground_skin_arr = t_ground_skin.array(mfi);
2109 
2110  auto soilt_arr = lsm_fab_vars[LsmVar_SLM::soilt]->const_array(mfi);
2111  auto soilw_arr = lsm_fab_vars[LsmVar_SLM::soilw]->const_array(mfi);
2112  auto tref_arr = lsm_fab_vars[LsmVar_SLM::tref]->const_array(mfi);
2113  auto tsurf_arr = lsm_fab_vars[LsmVar_SLM::tsurf]->const_array(mfi);
2114  auto qref_arr = lsm_fab_vars[LsmVar_SLM::qref]->const_array(mfi);
2115  auto pref_arr = lsm_fab_vars[LsmVar_SLM::pref]->const_array(mfi);
2116 
2117  //auto landtype_arr = landtype.const_array(mfi);
2118  ParallelFor( box, [=] AMREX_GPU_DEVICE (int i, int j, int)
2119  {
2120  if (landmask_arr(i, j, 0) == 1)
2121  {
2122  amrex::Real q_ground;
2123 
2124  // soil surface specific humidity
2125  if (tsurf_arr(i, j, 0) > tfriz)
2126  {
2127  erf_qsatw(tsurf_arr(i, j, 0), pref_arr(i, j, 0), q_ground);
2128  }
2129  else
2130  {
2131  erf_qsati(tsurf_arr(i, j, 0), pref_arr(i, j, 0), q_ground);
2132  }
2133  q_ground *= soilw_arr(i, j, d_khi_lsm);
2134  q_gr_arr(i, j, 0) = q_ground;
2135 
2136  // canopy temperature: initialize as (ref level temperature + soil surf temperature)/2
2137  //t_canop_arr(i, j, 0) = 0.5*(tref_arr(i, j, 0) + tsurf_arr(i, j, 0));
2138  //t_cas_arr(i, j, 0) = 0.5*(tref_arr(i, j, 0) + tsurf_arr(i, j, 0));
2139 
2140  t_canop_arr(i, j, 0) = tref_arr(i, j, 0);
2141  t_cas_arr(i, j, 0) = tref_arr(i, j, 0);
2142  t_ground_skin_arr(i, j, 0) = soilt_arr(i, j, d_khi_lsm);
2143 
2144  // specific humidity in canopy air space
2145  q_cas_arr(i, j, 0) = myhalf*(qref_arr(i, j, 0) + q_ground);
2146 
2147  //amrex::Print() << " i = " << i << " j = " << j << " k = 0 : t_canop = " << t_canop_arr(i, j, 0) << " t_cas = " << t_cas_arr(i, j, 0) << " tsurf = " << tsurf_arr(i, j, 0) << " qcas = " << q_cas_arr(i, j, 0) << " qref = " << qref_arr(i, j, 0) << std::endl;
2148 
2149  }
2150  });
2151  }
2152 
2153  // initialize nudging profiles for soil based on the initial soilt and soilw
2154  MultiFab::Copy(*lsm_fab_vars[LsmVar_SLM::soilt_obs], *lsm_fab_vars[LsmVar_SLM::soilt], 0, 0, 1, 0);
2155  MultiFab::Copy(*lsm_fab_vars[LsmVar_SLM::soilw_obs], *lsm_fab_vars[LsmVar_SLM::soilw], 0, 0, 1, 0);
2156 
2158 }
void initialize_surface_outputs()
Definition: ERF_SLM.cpp:2163
@ soilt_obs
Definition: ERF_SLM.H:60
@ soilw_obs
Definition: ERF_SLM.H:61

Referenced by Update_Lsm_Vars().

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

◆ init_soil_tw()

void SLM::init_soil_tw ( )

Reads soil input file, initializes soil wetness and temperature

1803 {
1804  // TODO - read from input file
1805  const Real d_tabs_s = tabs_s;
1806  const Real d_t00 = t00;
1807 
1808  const int d_khi_lsm = khi_lsm;
1809 
1810  const bool wrfinput = use_wrfinput;
1811 
1812  amrex::Gpu::DeviceVector<Real> d_dz_lsm_vec(m_dz_lsm.size());
1813  amrex::Gpu::DeviceVector<Real> d_clay_vec(m_dz_lsm.size());
1814  amrex::Gpu::DeviceVector<Real> d_sand_vec(m_dz_lsm.size());
1815  amrex::Gpu::DeviceVector<Real> d_st0_vec(m_dz_lsm.size());
1816  amrex::Gpu::DeviceVector<Real> d_sw0_vec(m_dz_lsm.size());
1817  amrex::Gpu::DeviceVector<Real> d_relax_vec(m_dz_lsm.size());
1818 
1819  amrex::Gpu::copy(Gpu::hostToDevice, m_dz_lsm.begin(), m_dz_lsm.end(), d_dz_lsm_vec.begin());
1820  amrex::Gpu::copy(Gpu::hostToDevice, clay0.begin(), clay0.end(), d_clay_vec.begin());
1821  amrex::Gpu::copy(Gpu::hostToDevice, sand0.begin(), sand0.end(), d_sand_vec.begin());
1822  amrex::Gpu::copy(Gpu::hostToDevice, st0.begin(), st0.end(), d_st0_vec.begin());
1823  amrex::Gpu::copy(Gpu::hostToDevice, sw0.begin(), sw0.end(), d_sw0_vec.begin());
1824  amrex::Gpu::copy(Gpu::hostToDevice, relax_hgt.begin(), relax_hgt.end(), d_relax_vec.begin());
1825  Real *d_dz_lsm = d_dz_lsm_vec.data();
1826  Real *d_clay0 = d_clay_vec.data();
1827  Real *d_sand0 = d_sand_vec.data();
1828  Real *d_st0 = d_st0_vec.data();
1829  Real *d_sw0 = d_sw0_vec.data();
1830  Real *d_relax = d_relax_vec.data();
1831 
1833  for ( MFIter mfi(*tsurf, TileNoZ()); mfi.isValid(); ++mfi) {
1834  const auto& box3d = mfi.tilebox();
1835 
1836  auto landmask_arr = landmask.const_array(mfi);
1837 
1838  auto soilt_arr = lsm_fab_vars[LsmVar_SLM::soilt]->array(mfi);
1839  auto soilw_arr = lsm_fab_vars[LsmVar_SLM::soilw]->array(mfi);
1840 
1841  auto tsurf_arr = lsm_fab_vars[LsmVar_SLM::tsurf]->array(mfi);
1842  auto lai_arr = lsm_fab_vars[LsmVar_SLM::lai]->array(mfi);
1843  auto vegtype_arr = lsm_fab_vars[LsmVar_SLM::vegtype]->array(mfi);
1844 
1845  auto sand_arr = lsm_fab_vars[LsmVar_SLM::sand]->array(mfi);
1846  auto clay_arr = lsm_fab_vars[LsmVar_SLM::clay]->array(mfi);
1847 
1848  auto s_depth_arr = lsm_fab_vars[LsmVar_SLM::s_depth]->array(mfi);
1849  auto soil_relax_hgt_arr = lsm_fab_vars[LsmVar_SLM::soil_relax_hgt]->array(mfi);
1850 
1851  auto sstxy_arr = sstxy.array(mfi);
1852 
1853  auto soiltype_arr = lsm_fab_vars[LsmVar_SLM::soiltype]->array(mfi);
1854  auto landtype_arr = landtype.const_array(mfi);
1855  auto LAI_local_arr = LAI.const_array(mfi);
1856 
1857  ParallelFor(box3d, [=] AMREX_GPU_DEVICE (int i, int j, int k)
1858  {
1859  if (landmask_arr(i, j, 0) == 1)
1860  {
1861  if (!wrfinput) {
1862  s_depth_arr(i, j, k) = d_dz_lsm[(k*-1)+d_khi_lsm];
1863  clay_arr(i, j, k) = d_clay0[(k*-1)+d_khi_lsm];
1864  sand_arr(i, j, k) = d_sand0[(k*-1)+d_khi_lsm];
1865  soilt_arr(i, j, k) = d_st0[(k*-1)+d_khi_lsm];
1866  soilw_arr(i, j, k) = d_sw0[(k*-1)+d_khi_lsm];
1867  } else {
1868  // initialize sand and clay % based on soil type from WRFInput
1869  switch (static_cast<int>(soiltype_arr(i, j, d_khi_lsm)))
1870  {
1871  case 1: // sand
1872  sand_arr(i,j,k) = 0.92;
1873  clay_arr(i,j,k) = 0.03;
1874  break;
1875  case 2: // loamy sand
1876  sand_arr(i,j,k) = 0.82;
1877  clay_arr(i,j,k) = 0.06;
1878  break;
1879  case 3: // sandy loam
1880  sand_arr(i,j,k) = 0.65;
1881  clay_arr(i,j,k) = 0.10;
1882  break;
1883  case 4: // silt loam
1884  sand_arr(i,j,k) = 0.20;
1885  clay_arr(i,j,k) = 0.15;
1886  break;
1887  case 5: // silt
1888  sand_arr(i,j,k) = 0.08;
1889  clay_arr(i,j,k) = 0.12;
1890  break;
1891  case 6: // loam
1892  sand_arr(i,j,k) = 0.40;
1893  clay_arr(i,j,k) = 0.20;
1894  break;
1895  case 7: // sandy clay loam
1896  sand_arr(i,j,k) = 0.60;
1897  clay_arr(i,j,k) = 0.30;
1898  break;
1899  case 8: // clay loam
1900  sand_arr(i,j,k) = 0.32;
1901  clay_arr(i,j,k) = 0.34;
1902  break;
1903  case 9: // silty clay loam
1904  sand_arr(i,j,k) = 0.20;
1905  clay_arr(i,j,k) = 0.40;
1906  break;
1907  case 10: // sandy clay
1908  sand_arr(i,j,k) = 0.52;
1909  clay_arr(i,j,k) = 0.42;
1910  break;
1911  case 11: // silty clay
1912  sand_arr(i,j,k) = 0.06;
1913  clay_arr(i,j,k) = 0.47;
1914  break;
1915  case 12: // clay
1916  sand_arr(i,j,k) = 0.20;
1917  clay_arr(i,j,k) = 0.60;
1918  break;
1919  case 13: // organic material
1920  //sand_arr(i,j,k) = 0.0;
1921  //clay_arr(i,j,k) = 0.0;
1922  sand_arr(i,j,k) = 0.001;
1923  clay_arr(i,j,k) = 0.001;
1924  break;
1925  case 14: // water
1926  sand_arr(i,j,k) = 0.001;
1927  clay_arr(i,j,k) = 0.001;
1928  break;
1929  case 15: // bedrock
1930  sand_arr(i,j,k) = 0.001;
1931  clay_arr(i,j,k) = 0.001;
1932  break;
1933  case 16: // other (urban/builtup, etc)
1934  sand_arr(i,j,k) = 0.001;
1935  clay_arr(i,j,k) = 0.001;
1936  break;
1937  default:
1938  sand_arr(i,j,k) = -1.0;
1939  clay_arr(i,j,k) = -1.0;
1940  break;
1941  }
1942 
1943  // convert to percentage
1944  sand_arr(i,j,k) *= 100.0;
1945  clay_arr(i,j,k) *= 100.0;
1946  }
1947 
1948  soil_relax_hgt_arr(i, j, k) = d_relax[(k*-1)+d_khi_lsm];
1949 
1950  // keep the 3D wrfinput copies consistent with the local SLM values if not using wrfinput
1951  if (!wrfinput && k == d_khi_lsm) {
1952  tsurf_arr(i, j, d_khi_lsm) = d_st0[0];
1953  tsurf_arr(i, j, 0) = d_st0[0];
1954  lai_arr(i, j, d_khi_lsm) = LAI_local_arr(i, j, 0);
1955  lai_arr(i, j, 0) = LAI_local_arr(i, j, 0);
1956  vegtype_arr(i, j, d_khi_lsm) = landtype_arr(i, j, 0);
1957  vegtype_arr(i, j, 0) = landtype_arr(i, j, 0);
1958  }
1959 
1960  // TODO: nrestart conditional here
1961  // TODO: sstxy - should be ERF surface temp array?
1962  sstxy_arr(i, j, 0) = soilt_arr(i, j, d_khi_lsm) - d_t00;
1963  } else {
1964  sstxy_arr(i, j, 0) = d_tabs_s - d_t00;
1965  }
1966  });
1967  }
1968 
1969  // Calculate node_z (depth of the center of each soil layer)
1971 }
void init_layer_depths()
Definition: ERF_SLM.cpp:1976
@ soil_relax_hgt
Definition: ERF_SLM.H:59
Here is the call graph for this function:

◆ init_soil_vars()

void SLM::init_soil_vars ( )

Initializes soil properties based on clay and sand percentages

2022 {
2023  const bool d_param_updated = params_updated;
2024 
2026  for ( MFIter mfi(*tsurf, TileNoZ()); mfi.isValid(); ++mfi) {
2027  const auto& box3d = mfi.tilebox();
2028 
2029  auto landmask_arr = landmask.const_array(mfi);
2030 
2031  auto sand_arr = lsm_fab_vars[LsmVar_SLM::sand]->const_array(mfi);
2032  auto clay_arr = lsm_fab_vars[LsmVar_SLM::clay]->const_array(mfi);
2033 
2034  auto w_s_FC_arr = lsm_fab_vars[LsmVar_SLM::w_s_FC]->array(mfi);
2035  auto w_s_WP_arr = lsm_fab_vars[LsmVar_SLM::w_s_WP]->array(mfi);
2036  auto sst_capa_arr = lsm_fab_vars[LsmVar_SLM::sst_capa]->array(mfi);
2037  auto sst_cond_arr = lsm_fab_vars[LsmVar_SLM::sst_cond]->array(mfi);
2038  auto poro_soil_arr = lsm_fab_vars[LsmVar_SLM::poro_soil]->array(mfi);
2039  auto theta_FC_arr = lsm_fab_vars[LsmVar_SLM::theta_FC]->array(mfi);
2040  auto theta_WP_arr = lsm_fab_vars[LsmVar_SLM::theta_WP]->array(mfi);
2041  auto m_pot_sat_arr = lsm_fab_vars[LsmVar_SLM::m_pot_sat]->array(mfi);
2042  auto Bconst_arr = lsm_fab_vars[LsmVar_SLM::Bconst]->array(mfi);
2043  auto ks_arr = lsm_fab_vars[LsmVar_SLM::ks]->array(mfi);
2044 
2045  ParallelFor(box3d, [=] AMREX_GPU_DEVICE (int i, int j, int k)
2046  {
2047  if (landmask_arr(i, j, 0) == 1)
2048  {
2049  if (!d_param_updated) {
2050  // compute the initial values
2051  const amrex::Real sand_per = sand_arr(i, j, k); // sand percentage
2052 
2053  // soil solids thermal conductivity (Johansen 1975)
2054  // quartz (= SAND content) thermal conductivity = 7.7 W/mK
2055  const amrex::Real mineral_tcond = sand_per > 20.0 ? two : three; // thermal conductivity of other minerals [W/mK]
2056  sst_cond_arr(i, j, k) = std::pow(amrex::Real(7.7), sand_per * amrex::Real(0.01)) * std::pow(mineral_tcond, one - sand_per * amrex::Real(0.01));
2057 
2058  // Calculated following Cosby et al. 1984 ( hydraulic properties)
2059  // hydraulic conductivity at satuation , mm/s
2060  ks_arr(i, j, k) = std::pow(amrex::Real(10.0), amrex::Real(0.0153)*sand_per - amrex::Real(0.884)) * (amrex::Real(25.4) / amrex::Real(3600.0)); // [mm/s] from [inch/hr]
2061 
2062  // constant B
2063  Bconst_arr(i, j, k) = 0.159 * clay_arr(i, j, k) + 2.91;
2064 
2065  // porosity (or saturation volumetric water content)
2066  poro_soil_arr(i, j, k) = -0.00126 * sand_per + 0.489; // volume/volume
2067 
2068  // moisture potential at saturation, [mm]
2069  m_pot_sat_arr(i, j, k) = std::min(amrex::Real(-150.0), -amrex::Real(10.0) * std::pow(amrex::Real(10.0), amrex::Real(1.88) - amrex::Real(0.0131)*sand_per)); // [mm] from [cm]
2070 
2071  // soil heat capacity, [J/m^3/K]
2072  // Following de Vries(1963) using SAND=34% CLAY=63%
2073  sst_capa_arr(i, j, k) = 1.0e6 * (2.128*sand_per + 2.385*clay_arr(i, j, k)) / (sand_per + clay_arr(i, j, k));
2074 
2075  // volumetric moisture content at field capacity
2076  // field capacity is assumed to be the occasion when hydraulic conductivity is 0.1mm/d
2077  theta_FC_arr(i, j, k) = poro_soil_arr(i, j, k) * std::pow((amrex::Real(0.1) / amrex::Real(86400.0) / ks_arr(i, j, k)), one / (two * Bconst_arr(i, j, k) + three));
2078 
2079  // volumetric moisture content at wilting point
2080  theta_WP_arr(i, j, k) = poro_soil_arr(i, j, k) * std::pow((amrex::Real(-150000.0) / m_pot_sat_arr(i, j, k)), -one / Bconst_arr(i, j, k));
2081  }
2082 
2083  // soil wetness at field capacity
2084  w_s_FC_arr(i, j, k) = theta_FC_arr(i, j, k) / poro_soil_arr(i, j, k);
2085 
2086  // soil wetness at wilting point
2087  w_s_WP_arr(i, j, k) = theta_WP_arr(i, j, k) / poro_soil_arr(i, j, k);
2088  }
2089  });
2090  }
2091 }
constexpr amrex::Real three
Definition: ERF_NumericalConstants.H:39
@ sst_cond
Definition: ERF_SLM.H:68
@ sst_capa
Definition: ERF_SLM.H:67
@ w_s_FC
Definition: ERF_SLM.H:64
Here is the call graph for this function:

◆ init_wrfinput_vars()

void SLM::init_wrfinput_vars ( )

Initializes local SLM fields from WRFInput data.

1062 {
1063  if (wrfinput_initialized) {
1064  return;
1065  }
1066 
1067  // TODO: fix - WRFinput sets 3D MFs in lsm_data - these need to be copied into SLM local 2D MFs
1068  const int d_khi_lsm = khi_lsm;
1069 
1070  const int isnature = 14; // nature cell type to use for urban cells
1071  const int isurban = 13; // urban cell type
1072  const int islake = 21; // lake cell
1073  amrex::ignore_unused(isurban);
1074 
1075  const auto& input_vegtype = *lsm_fab_vars[LsmVar_SLM::vegtype];
1076  const int input_vegtype_min = static_cast<int>(input_vegtype.min(0));
1077  const int input_vegtype_max = static_cast<int>(input_vegtype.max(0));
1078  if (input_vegtype_min < 0 || input_vegtype_max > 21) {
1079  amrex::Abort("SLM: WRF vegetation categories must be in the supported range [0, 21], "
1080  "where 0 denotes water/non-land! min = " +
1081  std::to_string(input_vegtype_min) + ", max = " +
1082  std::to_string(input_vegtype_max));
1083  }
1084 
1085  for (MFIter mfi(landtype, TileNoZ()); mfi.isValid(); ++mfi) {
1086  const Box bx2d = mfi.tilebox();
1087 
1088  auto vegtype_arr = lsm_fab_vars[LsmVar_SLM::vegtype]->array(mfi);
1089  auto in_lai_arr = lsm_fab_vars[LsmVar_SLM::lai]->const_array(mfi);
1090  auto in_tsk_arr = lsm_fab_vars[LsmVar_SLM::tsurf]->array(mfi);
1091  auto in_tsoil_arr = lsm_fab_vars[LsmVar_SLM::soilt]->array(mfi);
1092  auto in_vegfrac_arr = lsm_fab_vars[LsmVar_SLM::veg_frac]->array(mfi);
1093  auto in_vegfrac_min_arr = lsm_fab_vars[LsmVar_SLM::veg_frac_min]->array(mfi);
1094  auto in_vegfrac_max_arr = lsm_fab_vars[LsmVar_SLM::veg_frac_max]->array(mfi);
1095 
1096  auto emis_sfc_arr = lsm_fab_vars[LsmVar_SLM::emis_sfc]->array(mfi);
1097  auto alb_vis_sfc_arr = lsm_fab_vars[LsmVar_SLM::alb_vis_sfc]->array(mfi);
1098  auto alb_nir_sfc_arr = lsm_fab_vars[LsmVar_SLM::alb_nir_sfc]->array(mfi);
1099  auto alb_vis_sfc_dif_arr = lsm_fab_vars[LsmVar_SLM::alb_vis_sfc_diff]->array(mfi);
1100  auto alb_nir_sfc_dif_arr = lsm_fab_vars[LsmVar_SLM::alb_nir_sfc_diff]->array(mfi);
1101 
1102  auto landmask_arr = landmask.array(mfi);
1103  auto landtype_arr = landtype.array(mfi);
1104  auto lai_arr = LAI.array(mfi);
1105  auto sstxy_arr = sstxy.array(mfi);
1106  auto tskin_arr = t_skin.array(mfi);
1107  auto t_ground_skin_arr = t_ground_skin.array(mfi);
1108  auto vegetype_arr = vegetype.array(mfi);
1109  auto vege_YES_arr = vege_YES.array(mfi);
1110 
1111  const bool d_use_wrf_lai = use_wrf_lai;
1112  const Real d_urban_veg_frac = d_use_wrf_lai ? param_table[isnature - 1][1] : 0.0;
1113 
1114  ParallelFor(bx2d, [=] AMREX_GPU_DEVICE(int i, int j, int) noexcept
1115  {
1116  landtype_arr(i, j, 0) = static_cast<int>(vegtype_arr(i, j, d_khi_lsm));
1117  lai_arr(i, j, 0) = in_lai_arr(i, j, d_khi_lsm);
1118 
1119  const amrex::Real tsk = in_tsk_arr(i, j, d_khi_lsm);
1120  sstxy_arr(i, j, 0) = tsk;
1121  in_tsk_arr(i, j, 0) = tsk;
1122  tskin_arr(i, j, 0) = tsk;
1123  t_ground_skin_arr(i, j, 0) = in_tsoil_arr(i, j, d_khi_lsm);
1124 
1125  in_vegfrac_arr(i, j, d_khi_lsm) *= 0.01;
1126  in_vegfrac_arr(i, j, 0) = in_vegfrac_arr(i, j, d_khi_lsm);
1127  in_vegfrac_min_arr(i, j, d_khi_lsm) *= 0.01;
1128  in_vegfrac_min_arr(i, j, 0) = in_vegfrac_min_arr(i, j, d_khi_lsm);
1129  in_vegfrac_max_arr(i, j, d_khi_lsm) *= 0.01;
1130  in_vegfrac_max_arr(i, j, 0) = in_vegfrac_max_arr(i, j, d_khi_lsm);
1131 
1132  if (landtype_arr(i, j, 0) == 0 ||
1133  landtype_arr(i, j, 0) == 17 || landtype_arr(i, j, 0) == islake) {
1134  //amrex::Print() << " -- water cell - setting landtype=0 landmask=0" << std::endl;
1135  landmask_arr(i, j, 0) = 0;
1136  landtype_arr(i, j, 0) = 0;
1137  vegetype_arr(i, j, 0) = 0;
1138  vege_YES_arr(i, j, 0) = zero;
1139  in_tsoil_arr(i, j, d_khi_lsm) = 273.16;
1140 
1141  emis_sfc_arr(i, j, 0) = 0.98;
1142  emis_sfc_arr(i, j, d_khi_lsm) = emis_sfc_arr(i, j, 0);
1143 
1144  alb_vis_sfc_arr(i, j, 0) = 0.06;
1145  alb_nir_sfc_arr(i, j, 0) = 0.06;
1146  alb_vis_sfc_dif_arr(i, j, 0) = 0.06;
1147  alb_nir_sfc_dif_arr(i, j, 0) = 0.06;
1148 
1149  alb_vis_sfc_arr(i, j, d_khi_lsm) = alb_vis_sfc_arr(i, j, 0);
1150  alb_nir_sfc_arr(i, j, d_khi_lsm) = alb_nir_sfc_arr(i, j, 0);
1151  alb_vis_sfc_dif_arr(i, j, d_khi_lsm) = alb_vis_sfc_dif_arr(i, j, 0);
1152  alb_nir_sfc_dif_arr(i, j, d_khi_lsm) = alb_nir_sfc_dif_arr(i, j, 0);
1153 
1154  } else if (landtype_arr(i, j, 0) > 16) {
1155  // amrex::Print() << " -- urban cell (i = " << i << " j = " << j << ") - setting landtype=" << isnature << " (orig landtype = " << landtype_arr(i, j, 0) << ")" << std::endl;
1156  // for urban cells, set as a nature landtype for the non-building part
1157  landtype_arr(i, j, 0) = isnature;
1158  if (d_use_wrf_lai) {
1159  // set the vegetation frac
1160  in_vegfrac_arr(i, j, 0) = d_urban_veg_frac;
1161  }
1162  //landtype_arr(i, j, 0) = isurban;
1163  }
1164 
1165  // Keep the WRF vegetation class synchronized with the normalized SLM class.
1166  vegtype_arr(i, j, d_khi_lsm) = landtype_arr(i, j, 0);
1167  vegtype_arr(i, j, 0) = landtype_arr(i, j, 0);
1168  });
1169  }
1170 
1171  // If using WRFInput, SLM init needs to be delayed here to use correct values set from WRFInput
1172  slm_init();
1173 
1174  // Convert soil moisture from wrfinput to soil wetness for SLM
1175  for (MFIter mfi(*lsm_fab_vars[LsmVar_SLM::soilw], TileNoZ()); mfi.isValid(); ++mfi) {
1176  const Box box = mfi.tilebox();
1177  auto landmask_arr = landmask.const_array(mfi);
1178  auto poro_soil_arr = lsm_fab_vars[LsmVar_SLM::poro_soil]->const_array(mfi);
1179  auto wsoil_arr = lsm_fab_vars[LsmVar_SLM::soilw]->array(mfi);
1180 
1181  ParallelFor(box, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
1182  {
1183  if (landmask_arr(i, j, 0) == 1) {
1184  wsoil_arr(i, j, k) /= poro_soil_arr(i, j, k);
1185  }
1186  });
1187  }
1188 
1189  init_slm_vars();
1190  wrfinput_initialized = true;
1191 }
void init_slm_vars()
Definition: ERF_SLM.cpp:2093

Referenced by Advance(), and Update_Lsm_Vars().

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

◆ initialize_processed_state()

void SLM::initialize_processed_state ( const bool  preserve_checkpoint_fields = false)

Rebuilds SLM-local fields after transferring an evolved state.

Reconstructs local SLM fields after a processed-state transfer.

1398 {
1399  const int d_khi_lsm = khi_lsm;
1400  const int islake = 21;
1401 
1402  MultiFab saved_tsurf = lsm_fab_vars[LsmVar_SLM::tsurf]->deepCopy();
1403  MultiFab saved_soilt = lsm_fab_vars[LsmVar_SLM::soilt]->deepCopy();
1404  MultiFab saved_soilw = lsm_fab_vars[LsmVar_SLM::soilw]->deepCopy();
1405  Vector<std::unique_ptr<MultiFab>> saved_data;
1406  Vector<std::unique_ptr<MultiFab>> saved_unmapped;
1407  if (preserve_checkpoint_fields) {
1408  saved_data.resize(m_lsm_data_size);
1409  for (int ivar = 0; ivar < m_lsm_data_size; ++ivar) {
1410  saved_data[ivar] = std::make_unique<MultiFab>(
1411  lsm_fab_vars[LsmDataMap[ivar]]->deepCopy());
1412  }
1413  saved_unmapped.resize(unmapped_fields.size());
1414  for (int ivar = 0; ivar < static_cast<int>(unmapped_fields.size()); ++ivar) {
1415  saved_unmapped[ivar] = std::make_unique<MultiFab>(
1416  lsm_fab_vars[unmapped_fields[ivar]]->deepCopy());
1417  }
1418  }
1419 
1420  for (MFIter mfi(landtype, TileNoZ()); mfi.isValid(); ++mfi) {
1421  const Box box = mfi.tilebox();
1422  auto vegtype_arr = lsm_fab_vars[LsmVar_SLM::vegtype]->const_array(mfi);
1423  auto lai_in_arr = lsm_fab_vars[LsmVar_SLM::lai]->const_array(mfi);
1424  auto tsurf_arr = lsm_fab_vars[LsmVar_SLM::tsurf]->const_array(mfi);
1425 
1426  auto landmask_arr = landmask.array(mfi);
1427  auto landtype_arr = landtype.array(mfi);
1428  auto lai_arr = LAI.array(mfi);
1429  auto sstxy_arr = sstxy.array(mfi);
1430  auto tskin_arr = t_skin.array(mfi);
1431  auto vegetype_arr = vegetype.array(mfi);
1432  auto vege_yes_arr = vege_YES.array(mfi);
1433 
1434  ParallelFor(box, [=] AMREX_GPU_DEVICE (int i, int j, int) noexcept {
1435  const int input_landtype = static_cast<int>(vegtype_arr(i, j, d_khi_lsm));
1436  const bool water = input_landtype <= 0 || input_landtype == 17 || input_landtype == islake;
1437  const int normalized_landtype = (input_landtype > 16) ? 14 : input_landtype;
1438 
1439  landmask_arr(i, j, 0) = water ? 0 : 1;
1440  landtype_arr(i, j, 0) = water ? 0 : normalized_landtype;
1441  lai_arr(i, j, 0) = lai_in_arr(i, j, d_khi_lsm);
1442  sstxy_arr(i, j, 0) = tsurf_arr(i, j, d_khi_lsm);
1443  tskin_arr(i, j, 0) = tsurf_arr(i, j, d_khi_lsm);
1444  vegetype_arr(i, j, 0) = water ? 0 : 1;
1445  vege_yes_arr(i, j, 0) = water ? 0.0 : 1.0;
1446  });
1447  }
1448 
1449  slm_init();
1450 
1451  const IntVect state_ng = lsm_fab_vars[LsmVar_SLM::tsurf]->nGrowVect();
1452  MultiFab::Copy(*lsm_fab_vars[LsmVar_SLM::tsurf], saved_tsurf, 0, 0, 1, state_ng);
1453  MultiFab::Copy(*lsm_fab_vars[LsmVar_SLM::soilt], saved_soilt, 0, 0, 1, state_ng);
1454  MultiFab::Copy(*lsm_fab_vars[LsmVar_SLM::soilw], saved_soilw, 0, 0, 1, state_ng);
1455 
1456  for (MFIter mfi(*lsm_fab_vars[LsmVar_SLM::tsurf], TileNoZ()); mfi.isValid(); ++mfi) {
1457  const Box box = mfi.tilebox().makeSlab(2, 0);
1458  auto tsurf_arr = lsm_fab_vars[LsmVar_SLM::tsurf]->array(mfi);
1459  ParallelFor(box, [=] AMREX_GPU_DEVICE (int i, int j, int) noexcept {
1460  tsurf_arr(i, j, 0) = tsurf_arr(i, j, d_khi_lsm);
1461  });
1462  }
1463 
1464  init_slm_vars();
1465 
1466  if (preserve_checkpoint_fields) {
1467  for (int ivar = 0; ivar < m_lsm_data_size; ++ivar) {
1468  const IntVect ng = lsm_fab_vars[LsmDataMap[ivar]]->nGrowVect();
1469  MultiFab::Copy(*lsm_fab_vars[LsmDataMap[ivar]], *saved_data[ivar],
1470  0, 0, 1, ng);
1471  }
1472  for (int ivar = 0; ivar < static_cast<int>(unmapped_fields.size()); ++ivar) {
1473  const IntVect ng = lsm_fab_vars[unmapped_fields[ivar]]->nGrowVect();
1474  MultiFab::Copy(*lsm_fab_vars[unmapped_fields[ivar]], *saved_unmapped[ivar],
1475  0, 0, 1, ng);
1476  }
1477  }
1478 }
Here is the call graph for this function:

◆ initialize_surface_outputs()

void SLM::initialize_surface_outputs ( )

Initializes diagnostics and radiation coupling fields from the SLM state.

2164 {
2165  const int d_khi_lsm = khi_lsm;
2166 
2167  for (MFIter mfi(landtype, TileNoZ()); mfi.isValid(); ++mfi) {
2168  const auto& box = mfi.tilebox();
2169 
2170  auto landmask_arr = landmask.const_array(mfi);
2171  auto LAI_arr = LAI.const_array(mfi);
2172  auto phi_1_arr = phi_1.const_array(mfi);
2173  auto phi_2_arr = phi_2.const_array(mfi);
2174  auto albedovis_v_arr = albedovis_v.const_array(mfi);
2175  auto albedonir_v_arr = albedonir_v.const_array(mfi);
2176  auto albedovis_s_arr = albedovis_s.const_array(mfi);
2177  auto albedonir_s_arr = albedonir_s.const_array(mfi);
2178  auto vegetype_arr = vegetype.const_array(mfi);
2179  auto IR_emis_vege_arr = IR_emis_vege.const_array(mfi);
2180  auto IR_emis_soil_arr = IR_emis_soil.const_array(mfi);
2181  auto veg_frac_arr = lsm_fab_vars[LsmVar_SLM::veg_frac]->array(mfi);
2182  auto coszrsxy_arr = lsm_fab_vars[LsmVar_SLM::coszrsxy]->const_array(mfi);
2183  auto soilw_arr = lsm_fab_vars[LsmVar_SLM::soilw]->const_array(mfi);
2184  auto t_canop_arr = t_canop.const_array(mfi);
2185  auto mw_arr = mw.const_array(mfi);
2186  auto t_cas_arr = t_cas.const_array(mfi);
2187  auto q_cas_arr = q_cas.const_array(mfi);
2188  auto q_gr_arr = q_gr.const_array(mfi);
2189  auto t_ground_skin_arr = t_ground_skin.const_array(mfi);
2190  auto tsurf_arr = lsm_fab_vars[LsmVar_SLM::tsurf]->array(mfi);
2191  auto t_skin_arr = t_skin.array(mfi);
2192  auto t_sfc_arr = t_sfc.array(mfi);
2193  auto q_sfc_arr = q_sfc.array(mfi);
2194  auto tveg_arr = lsm_fab_vars[LsmVar_SLM::tv]->array(mfi);
2195  auto mveg_arr = lsm_fab_vars[LsmVar_SLM::mv]->array(mfi);
2196  auto emis_sfc_arr = lsm_fab_vars[LsmVar_SLM::emis_sfc]->array(mfi);
2197  auto alb_nir_sfc_arr = lsm_fab_vars[LsmVar_SLM::alb_nir_sfc]->array(mfi);
2198  auto alb_vis_sfc_arr = lsm_fab_vars[LsmVar_SLM::alb_vis_sfc]->array(mfi);
2199  auto alb_nir_sfc_diff_arr = lsm_fab_vars[LsmVar_SLM::alb_nir_sfc_diff]->array(mfi);
2200  auto alb_vis_sfc_diff_arr = lsm_fab_vars[LsmVar_SLM::alb_vis_sfc_diff]->array(mfi);
2201 
2202  ParallelFor(box, [=] AMREX_GPU_DEVICE (int i, int j, int)
2203  {
2204  const amrex::Real cosz = coszrsxy_arr(i, j, 0);
2205  const amrex::Real direct_extinction = phi_1_arr(i, j, 0) / std::max(amrex::Real(0.01), cosz) + phi_2_arr(i, j, 0);
2206  const amrex::Real diffuse_extinction = phi_1_arr(i, j, 0) + phi_2_arr(i, j, 0);
2207  const amrex::Real explai = std::exp(-direct_extinction * LAI_arr(i, j, 0));
2208  const amrex::Real explai0 = std::exp(-diffuse_extinction * LAI_arr(i, j, 0));
2209  const amrex::Real wetfactor = one - myhalf * soilw_arr(i, j, d_khi_lsm);
2210 
2211  const amrex::Real veg_frac = std::min(std::max(veg_frac_arr(i, j, 0), amrex::Real(zero)), amrex::Real(one));
2212  const amrex::Real soil_nir = albedonir_s_arr(i, j, 0) * wetfactor;
2213  const amrex::Real soil_vis = albedovis_s_arr(i, j, 0) * wetfactor;
2214  const amrex::Real canopy_nir = albedonir_v_arr(i, j, 0) * (1.0 - explai) + soil_nir * explai;
2215  const amrex::Real canopy_vis = albedovis_v_arr(i, j, 0) * (1.0 - explai) + soil_vis * explai;
2216  const amrex::Real canopy_nir_diff = albedonir_v_arr(i, j, 0) * (1.0 - explai0) + soil_nir * explai0;
2217  const amrex::Real canopy_vis_diff = albedovis_v_arr(i, j, 0) * (1.0 - explai0) + soil_vis * explai0;
2218  const amrex::Real alb_nir = veg_frac * canopy_nir + (1.0 - veg_frac) * soil_nir;
2219  const amrex::Real alb_vis = veg_frac * canopy_vis + (1.0 - veg_frac) * soil_vis;
2220  const amrex::Real alb_nir_diff = veg_frac * canopy_nir_diff + (1.0 - veg_frac) * soil_nir;
2221  const amrex::Real alb_vis_diff = veg_frac * canopy_vis_diff + (1.0 - veg_frac) * soil_vis;
2222 
2223  t_skin_arr(i, j, 0) = tsurf_arr(i, j, 0);
2224  tsurf_arr(i, j, d_khi_lsm) = tsurf_arr(i, j, 0);
2225  veg_frac_arr(i, j, d_khi_lsm) = veg_frac;
2226  tveg_arr(i, j, 0) = t_canop_arr(i, j, 0);
2227  tveg_arr(i, j, d_khi_lsm) = t_canop_arr(i, j, 0);
2228  mveg_arr(i, j, 0) = mw_arr(i, j, 0);
2229  mveg_arr(i, j, d_khi_lsm) = mw_arr(i, j, 0);
2230 
2231  if (landmask_arr(i, j, 0) == 1 && vegetype_arr(i, j, 0) == 1) {
2232  t_sfc_arr(i, j, 0) = t_cas_arr(i, j, 0);
2233  q_sfc_arr(i, j, 0) = q_cas_arr(i, j, 0);
2234  } else {
2235  t_sfc_arr(i, j, 0) = t_ground_skin_arr(i, j, 0);
2236  q_sfc_arr(i, j, 0) = q_gr_arr(i, j, 0);
2237  }
2238 
2239  emis_sfc_arr(i, j, 0) = IR_emis_vege_arr(i, j, 0) * veg_frac + IR_emis_soil_arr(i, j, 0) * (1.0 - veg_frac);
2240  alb_nir_sfc_arr(i, j, 0) = alb_nir;
2241  alb_vis_sfc_arr(i, j, 0) = alb_vis;
2242  alb_nir_sfc_diff_arr(i, j, 0) = alb_nir_diff;
2243  alb_vis_sfc_diff_arr(i, j, 0) = alb_vis_diff;
2244 
2245  emis_sfc_arr(i, j, d_khi_lsm) = emis_sfc_arr(i, j, 0);
2246  alb_nir_sfc_arr(i, j, d_khi_lsm) = alb_nir_sfc_arr(i, j, 0);
2247  alb_vis_sfc_arr(i, j, d_khi_lsm) = alb_vis_sfc_arr(i, j, 0);
2248  alb_nir_sfc_diff_arr(i, j, d_khi_lsm) = alb_nir_sfc_diff_arr(i, j, 0);
2249  alb_vis_sfc_diff_arr(i, j, d_khi_lsm) = alb_vis_sfc_diff_arr(i, j, 0);
2250  });
2251  }
2252 }
Here is the call graph for this function:

◆ initialize_zrefxy()

void SLM::initialize_zrefxy ( )

Initializes the canopy-adjusted reference height on the current grid.

1370 {
1371  const Real dz = m_geom.CellSize(2);
1372  // The ground nodes under every box, including those that start above the ground
1373  const auto z_ground = use_terrain ? copy_ground_plane(*z_phys_nd, m_geom.Domain().smallEnd(2))
1374  : std::unique_ptr<MultiFab>{};
1375  for (MFIter mfi(landtype, TileNoZ()); mfi.isValid(); ++mfi) {
1376  const Box& xybx = mfi.growntilebox(0);
1377  const Array4<const Real>& z_nd_arr = use_terrain
1378  ? z_ground->const_array(mfi) : Array4<Real>{};
1379  auto ztop_arr = ztop.array(mfi);
1380  auto zrefxy_arr = zrefxy.array(mfi);
1381  auto landmask_arr = landmask.const_array(mfi);
1382 
1383  ParallelFor(xybx, [=] AMREX_GPU_DEVICE(int i, int j, int) noexcept {
1384  if (landmask_arr(i, j, 0) == 1) {
1385  const Real znd = z_nd_arr ? Compute_Zrel_AtCellCenter(i, j, 0, z_nd_arr)
1386  : myhalf * dz;
1387  zrefxy_arr(i, j, 0) = ztop_arr(i, j, 0) + znd;
1388  }
1389  });
1390  }
1391 }

Referenced by Advance().

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

◆ linear_interp()

AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real SLM::linear_interp ( const amrex::Real  t0,
const amrex::Real  t1,
const amrex::Real  t,
const amrex::Real  x,
const amrex::Real  y 
)
static
4827 {
4828  // returns a value that is linearly interpolated between x and y at time t. x
4829  // is at t=t0, y is at t=t1.
4830  if (t0 == t1 || t > t1) {
4831  return y;
4832  }
4833  const amrex::Real dt = (t - t0) / (t1 - t0);
4834  return x + (y - x) * dt;
4835 }

◆ load_builtin_radiation_parameters()

void SLM::load_builtin_radiation_parameters ( )

Loads built-in Noah-MP radiation lookup values for idealized SLM cases.

Loads the Noah-MP radiation lookup values used when no parameter file is supplied.

903 {
904  auto load_params = [](auto& params, const char* name, const auto& values)
905  {
906  auto d_var = std::make_unique<Gpu::DeviceVector<Real>>(values.size());
907  auto* d_var_ptr = d_var.get();
908  Gpu::copyAsync(Gpu::hostToDevice, values.data(), values.data() + values.size(), d_var_ptr->data());
909  params.emplace(name, std::move(d_var));
910  };
911 
912  load_params(d_veg_params, "cwpvt", SLMDefaultParams::cwpvt);
913  load_params(d_veg_params, "den", SLMDefaultParams::den);
914  load_params(d_veg_params, "hvb", SLMDefaultParams::hvb);
915  load_params(d_veg_params, "rc", SLMDefaultParams::rc);
916  load_params(d_veg_params, "rhol_vis", SLMDefaultParams::rhol_vis);
917  load_params(d_veg_params, "rhol_nir", SLMDefaultParams::rhol_nir);
918  load_params(d_veg_params, "rhos_vis", SLMDefaultParams::rhos_vis);
919  load_params(d_veg_params, "rhos_nir", SLMDefaultParams::rhos_nir);
920  load_params(d_veg_params, "taul_vis", SLMDefaultParams::taul_vis);
921  load_params(d_veg_params, "taul_nir", SLMDefaultParams::taul_nir);
922  load_params(d_veg_params, "taus_vis", SLMDefaultParams::taus_vis);
923  load_params(d_veg_params, "taus_nir", SLMDefaultParams::taus_nir);
924 
925  load_params(d_rad_params, "albsat_vis", SLMDefaultParams::albsat_vis);
926  load_params(d_rad_params, "albsat_nir", SLMDefaultParams::albsat_nir);
927  load_params(d_rad_params, "albdry_vis", SLMDefaultParams::albdry_vis);
928  load_params(d_rad_params, "albdry_nir", SLMDefaultParams::albdry_nir);
929  load_params(d_rad_params, "alblak", SLMDefaultParams::alblak);
930  load_params(d_rad_params, "omegas", SLMDefaultParams::omegas);
931 
932  Gpu::streamSynchronize();
933  amrex::Print() << " SLM: Using built-in Noah-MP radiation parameters" << std::endl;
934 }
constexpr std::array< amrex::Real, num_veg_params > rhos_nir
Definition: ERF_SLM_DefaultParams.H:34
constexpr std::array< amrex::Real, num_veg_params > taul_vis
Definition: ERF_SLM_DefaultParams.H:37
constexpr std::array< amrex::Real, num_veg_params > taus_vis
Definition: ERF_SLM_DefaultParams.H:43
constexpr std::array< amrex::Real, 8 > albdry_vis
Definition: ERF_SLM_DefaultParams.H:54
constexpr std::array< amrex::Real, num_veg_params > rhos_vis
Definition: ERF_SLM_DefaultParams.H:31
constexpr std::array< amrex::Real, num_veg_params > taul_nir
Definition: ERF_SLM_DefaultParams.H:40
constexpr std::array< amrex::Real, 2 > omegas
Definition: ERF_SLM_DefaultParams.H:59
constexpr std::array< amrex::Real, 8 > albsat_nir
Definition: ERF_SLM_DefaultParams.H:52
constexpr std::array< amrex::Real, num_veg_params > den
Definition: ERF_SLM_DefaultParams.H:16
constexpr std::array< amrex::Real, num_veg_params > rhol_vis
Definition: ERF_SLM_DefaultParams.H:25
constexpr std::array< amrex::Real, num_veg_params > rc
Definition: ERF_SLM_DefaultParams.H:22
constexpr std::array< amrex::Real, num_veg_params > hvb
Definition: ERF_SLM_DefaultParams.H:19
constexpr std::array< amrex::Real, num_veg_params > cwpvt
Definition: ERF_SLM_DefaultParams.H:13
constexpr std::array< amrex::Real, 8 > albsat_vis
Definition: ERF_SLM_DefaultParams.H:50
constexpr std::array< amrex::Real, 8 > albdry_nir
Definition: ERF_SLM_DefaultParams.H:56
constexpr std::array< amrex::Real, num_veg_params > rhol_nir
Definition: ERF_SLM_DefaultParams.H:28
constexpr std::array< amrex::Real, num_veg_params > taus_nir
Definition: ERF_SLM_DefaultParams.H:46

◆ Lsm_Atmos_Geom()

amrex::Geometry SLM::Lsm_Atmos_Geom ( )
inlineoverridevirtual

Returns the atmospheric geometry used by SLM coupling fields.

Reimplemented from NullSurf.

329 { return m_geom; }

◆ Lsm_Data_Ptr()

amrex::MultiFab* SLM::Lsm_Data_Ptr ( const int &  varIdx)
inlineoverridevirtual

Reimplemented from NullSurf.

304  {
305  AMREX_ALWAYS_ASSERT(varIdx < SLM::m_lsm_data_size && varIdx >= 0);
306  int lsmIdx = LsmDataMap[varIdx];
307  AMREX_ALWAYS_ASSERT(lsmIdx < LsmVar_SLM::NumVars && lsmIdx>=0);
308  return lsm_fab_vars[lsmIdx].get();
309  }
Here is the call graph for this function:

◆ Lsm_Data_Size()

int SLM::Lsm_Data_Size ( )
inlineoverridevirtual

Reimplemented from NullSurf.

415 { return SLM::m_lsm_data_size; }

◆ Lsm_DataIndex()

int SLM::Lsm_DataIndex ( const std::string &  varname)
inlineoverridevirtual

Reimplemented from NullSurf.

431  {
432  int varIdx = -1;
433  std::string lc_varname = amrex::toLower(varname);
434  for (int idx(0); idx<SLM::m_lsm_data_size; ++idx) {
435  if (lc_varname == amrex::toLower(Lsm_DataName(idx))) {
436  varIdx = idx;
437  }
438  }
439  AMREX_ASSERT(varIdx != -1); // no name found
440  return varIdx;
441  }
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE int idx(int i, int j, int k, int nx, int ny)
Definition: ERF_InitForEnsemble.cpp:404
std::string Lsm_DataName(const int &varIdx) override
Definition: ERF_SLM.H:423
Here is the call graph for this function:

◆ Lsm_DataName()

std::string SLM::Lsm_DataName ( const int &  varIdx)
inlineoverridevirtual

Reimplemented from NullSurf.

424  {
425  AMREX_ALWAYS_ASSERT(varIdx < SLM::m_lsm_data_size && varIdx >= 0);
426  return LsmDataName[varIdx];
427  }

Referenced by Lsm_DataIndex().

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

◆ Lsm_Flux_Ptr()

amrex::MultiFab* SLM::Lsm_Flux_Ptr ( const int &  varIdx)
inlineoverridevirtual

Reimplemented from NullSurf.

314  {
315  AMREX_ALWAYS_ASSERT(varIdx < SLM::m_lsm_flux_size && varIdx >= 0);
316  int lsmIdx = LsmFluxMap[varIdx];
317  AMREX_ALWAYS_ASSERT(lsmIdx < LsmFlux_SLM::NumVars && lsmIdx>=0);
318  return lsm_fab_flux[lsmIdx].get();
319  }
Here is the call graph for this function:

◆ Lsm_Flux_Size()

int SLM::Lsm_Flux_Size ( )
inlineoverridevirtual

Reimplemented from NullSurf.

419 { return SLM::m_lsm_flux_size; }

◆ Lsm_FluxIndex()

int SLM::Lsm_FluxIndex ( const std::string &  varname)
inlineoverridevirtual

Reimplemented from NullSurf.

453  {
454  int varIdx = -1;
455  std::string lc_varname = amrex::toLower(varname);
456  for (int idx(0); idx<SLM::m_lsm_flux_size; ++idx) {
457  if (lc_varname == amrex::toLower(Lsm_FluxName(idx))) {
458  varIdx = idx;
459  }
460  }
461  AMREX_ALWAYS_ASSERT(varIdx != -1); // no name found
462  return varIdx;
463  }
std::string Lsm_FluxName(const int &varIdx) override
Definition: ERF_SLM.H:445
Here is the call graph for this function:

◆ Lsm_FluxName()

std::string SLM::Lsm_FluxName ( const int &  varIdx)
inlineoverridevirtual

Reimplemented from NullSurf.

446  {
447  AMREX_ALWAYS_ASSERT(varIdx < SLM::m_lsm_flux_size && varIdx >= 0);
448  return LsmFluxName[varIdx];
449  }

Referenced by Lsm_FluxIndex().

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

◆ Lsm_Geom()

amrex::Geometry SLM::Lsm_Geom ( )
inlineoverridevirtual

Reimplemented from NullSurf.

323 { return m_lsm_geom; }

◆ Lsm_Has_Atmos_State()

bool SLM::Lsm_Has_Atmos_State ( )
inlineoverridevirtual

Returns whether Copy_State_to_Lsm has filled tref, qref and pref.

Reimplemented from NullSurf.

393 { return has_atmos_state; }

◆ Lsm_Interpolate_From_Source()

void SLM::Lsm_Interpolate_From_Source ( const amrex::Geometry &  source_geom,
const amrex::Geometry &  source_lsm_geom,
const amrex::IntVect &  ref_ratio,
LSMTransferMode  mode 
)
overridevirtual

Transfers exposed state from a source AMR level.

Transfers SLM fields from a source AMR level.

Reimplemented from NullSurf.

1284 {
1285  AMREX_ALWAYS_ASSERT(static_cast<int>(source_data.size()) == m_lsm_data_size);
1286  AMREX_ALWAYS_ASSERT(static_cast<int>(source_flux.size()) == m_lsm_flux_size);
1288 
1289  const IntVect rr2d(ref_ratio[0], ref_ratio[1], 1);
1290 
1291  // This InterpFromCoarseLevel reads the coarse ghost cells as they are, and SLM
1292  // never fills its fields' ghost cells, so they still hold the zeros of Init.
1293  // Interpolate from a copy whose ghost cells between boxes (and across periodic
1294  // boundaries) are filled; otherwise the fine cells next to a coarse box
1295  // boundary get zeros (vegtype = 0, i.e. water). The copy leaves the source
1296  // level's own ghost cells, which SLM writes to its checkpoints, as they were.
1297  auto with_filled_ghosts = [] (const MultiFab& src, const Geometry& geom)
1298  {
1299  MultiFab tmp(src.boxArray(), src.DistributionMap(), src.nComp(), src.nGrowVect());
1300  MultiFab::Copy(tmp, src, 0, 0, src.nComp(), src.nGrowVect());
1301  tmp.FillBoundary(geom.periodicity());
1302  return tmp;
1303  };
1304 
1305  // The vegetation and soil types are categories: a fine cell takes its coarse cell's,
1306  // since a linear interpolation between two of them (17, water, and 5, say, along a
1307  // coastline) gives a third (12, cropland) once SLM truncates it to an integer
1308  auto interp_for = [] (int var) -> Interpolater*
1309  {
1310  return (var == LsmVar_SLM::vegtype || var == LsmVar_SLM::soiltype)
1311  ? static_cast<Interpolater*>(&pc_interp) : static_cast<Interpolater*>(&cell_cons_interp);
1312  };
1313 
1314  for (int ivar = 0; ivar < m_lsm_data_size; ++ivar) {
1315  AMREX_ALWAYS_ASSERT(source_data[ivar] != nullptr);
1316  const MultiFab src = with_filled_ghosts(*source_data[ivar], source_lsm_geom);
1317  InterpFromCoarseLevel(*lsm_fab_vars[LsmDataMap[ivar]],
1318  lsm_fab_vars[LsmDataMap[ivar]]->nGrowVect(),
1319  IntVect(0,0,0), src, 0, 0, 1,
1320  source_lsm_geom, m_lsm_geom, rr2d,
1321  interp_for(LsmDataMap[ivar]), m_domain_bcs_type,
1322  BCVars::cons_bc);
1323  }
1324 
1325  for (int ivar = 0; ivar < m_lsm_flux_size; ++ivar) {
1326  AMREX_ALWAYS_ASSERT(source_flux[ivar] != nullptr);
1327  const MultiFab src = with_filled_ghosts(*source_flux[ivar], source_geom);
1328  InterpFromCoarseLevel(*lsm_fab_flux[LsmFluxMap[ivar]],
1329  lsm_fab_flux[LsmFluxMap[ivar]]->nGrowVect(),
1330  IntVect(0,0,0), src, 0, 0, 1,
1331  source_geom, m_geom, rr2d,
1332  &cell_cons_interp, m_domain_bcs_type,
1333  BCVars::cons_bc);
1334  }
1335 
1336  for (int ivar = 0; ivar < static_cast<int>(unmapped_fields.size()); ++ivar) {
1337  AMREX_ALWAYS_ASSERT(source_unmapped[ivar] != nullptr);
1338  const MultiFab src = with_filled_ghosts(*source_unmapped[ivar], source_lsm_geom);
1339  InterpFromCoarseLevel(*lsm_fab_vars[unmapped_fields[ivar]],
1340  lsm_fab_vars[unmapped_fields[ivar]]->nGrowVect(),
1341  IntVect(0,0,0), src, 0, 0, 1,
1342  source_lsm_geom, m_lsm_geom, rr2d,
1343  interp_for(unmapped_fields[ivar]), m_domain_bcs_type,
1344  BCVars::cons_bc);
1345  }
1346 
1347  // tref, qref and pref were interpolated from the source, so they are only as
1348  // good as the source's. A level made at initialization, before any
1349  // Update_Lsm_Vars, gets zeros, and init_slm_vars must then run again once
1350  // this level's own Update_Lsm_Vars has filled them.
1352 
1353  if (mode == LSMTransferMode::RawInput) {
1354  first_step = true;
1355  wrfinput_initialized = false;
1356  } else {
1359  first_step = false;
1362  }
1363 }
void initialize_processed_state(const bool preserve_checkpoint_fields=false)
Definition: ERF_SLM.cpp:1397
amrex::Vector< amrex::MultiFab * > source_data
Definition: ERF_SLM.H:739
bool init_from_atmos_pending
Definition: ERF_SLM.H:1024
amrex::Vector< amrex::MultiFab * > source_flux
Definition: ERF_SLM.H:740
amrex::Vector< amrex::MultiFab * > source_unmapped
Definition: ERF_SLM.H:741
bool source_has_atmos_state
Definition: ERF_SLM.H:1020
@ cons_bc
Definition: ERF_IndexDefines.H:110
Here is the call graph for this function:

◆ Lsm_Regrid_Level()

void SLM::Lsm_Regrid_Level ( const int &  lev,
const amrex::MultiFab &  cons_in,
const amrex::MultiFab &  u_in,
const amrex::MultiFab &  v_in,
const amrex::Geometry &  geom,
const amrex::Geometry &  geom0,
amrex::Vector< amrex::BCRec > &  domain_bcs_type,
amrex::IntVect &  refRatio,
const amrex::Real &  dt,
std::unique_ptr< amrex::MultiFab > &  z_phys_nd_in,
amrex::Vector< amrex::Vector< std::string >> &  nc_init_file,
const amrex::Geometry &  source_geom,
const amrex::Geometry &  source_lsm_geom,
const amrex::IntVect &  source_ref_ratio,
const bool  has_source 
)
overridevirtual

Rebuilds SLM arrays on a remade atmospheric grid and transfers state.

Reimplemented from NullSurf.

1212 {
1213  const Geometry old_geom = m_geom;
1214  const Geometry old_lsm_geom = m_lsm_geom;
1215  Vector<std::unique_ptr<MultiFab>> saved_data(m_lsm_data_size);
1216  Vector<std::unique_ptr<MultiFab>> saved_flux(m_lsm_flux_size);
1217  Vector<std::unique_ptr<MultiFab>> saved_unmapped(unmapped_fields.size());
1218 
1219  for (int ivar = 0; ivar < m_lsm_data_size; ++ivar) {
1220  saved_data[ivar] = std::make_unique<MultiFab>(Lsm_Data_Ptr(ivar)->deepCopy());
1221  }
1222  for (int ivar = 0; ivar < m_lsm_flux_size; ++ivar) {
1223  saved_flux[ivar] = std::make_unique<MultiFab>(Lsm_Flux_Ptr(ivar)->deepCopy());
1224  }
1225  for (int ivar = 0; ivar < static_cast<int>(unmapped_fields.size()); ++ivar) {
1226  saved_unmapped[ivar] = std::make_unique<MultiFab>(
1227  lsm_fab_vars[unmapped_fields[ivar]]->deepCopy());
1228  }
1229 
1230  Init(lev, cons_in, u_in, v_in, geom, geom0, domain_bcs_type,
1231  refRatio, dt, z_phys_nd_in, nc_init_file);
1232 
1233  if (has_source) {
1234  Lsm_Interpolate_From_Source(source_geom, source_lsm_geom,
1235  source_ref_ratio, LSMTransferMode::ProcessedState);
1236 
1237  for (int ivar = 0; ivar < m_lsm_data_size; ++ivar) {
1238  lsm_fab_vars[LsmDataMap[ivar]]->ParallelCopy(*saved_data[ivar], 0, 0, 1);
1239  }
1240  for (int ivar = 0; ivar < m_lsm_flux_size; ++ivar) {
1241  lsm_fab_flux[LsmFluxMap[ivar]]->ParallelCopy(*saved_flux[ivar], 0, 0, 1);
1242  }
1243  } else {
1244  source_data.resize(m_lsm_data_size, nullptr);
1245  source_flux.resize(m_lsm_flux_size, nullptr);
1246  source_unmapped.resize(unmapped_fields.size(), nullptr);
1247  for (int ivar = 0; ivar < m_lsm_data_size; ++ivar) {
1248  source_data[ivar] = saved_data[ivar].get();
1249  }
1250  for (int ivar = 0; ivar < m_lsm_flux_size; ++ivar) {
1251  source_flux[ivar] = saved_flux[ivar].get();
1252  }
1253  for (int ivar = 0; ivar < static_cast<int>(unmapped_fields.size()); ++ivar) {
1254  source_unmapped[ivar] = saved_unmapped[ivar].get();
1255  }
1257 
1258  Lsm_Interpolate_From_Source(old_geom, old_lsm_geom,
1259  IntVect(1), LSMTransferMode::ProcessedState);
1260  }
1261 
1262  // ERF checkpoints the mapped fields and this registry of otherwise-private
1263  // SLM fields. Restore the latter after rebuilding the level so a remake
1264  // does not discard state which ERF does not expose through lsm_data.
1265  for (int ivar = 0; ivar < static_cast<int>(unmapped_fields.size()); ++ivar) {
1266  lsm_fab_vars[unmapped_fields[ivar]]->ParallelCopy(*saved_unmapped[ivar], 0, 0, 1);
1267  }
1270 
1271  source_data.clear();
1272  source_flux.clear();
1273  source_unmapped.clear();
1274 }
void Lsm_Interpolate_From_Source(const amrex::Geometry &source_geom, const amrex::Geometry &source_lsm_geom, const amrex::IntVect &ref_ratio, LSMTransferMode mode) override
Definition: ERF_SLM.cpp:1280
amrex::MultiFab * Lsm_Flux_Ptr(const int &varIdx) override
Definition: ERF_SLM.H:313

◆ Lsm_Set_Source_Data_Ptr()

void SLM::Lsm_Set_Source_Data_Ptr ( const int &  varIdx,
amrex::MultiFab *  source_data_ptr 
)
inlineoverridevirtual

Registers an exposed source data field.

Reimplemented from NullSurf.

337  {
338  AMREX_ALWAYS_ASSERT(varIdx >= 0 && varIdx < m_lsm_data_size);
339  if (static_cast<int>(source_data.size()) != m_lsm_data_size) {
340  source_data.resize(m_lsm_data_size, nullptr);
341  }
342  source_data[varIdx] = source_data_ptr;
343  }
Here is the call graph for this function:

◆ Lsm_Set_Source_Flux_Ptr()

void SLM::Lsm_Set_Source_Flux_Ptr ( const int &  varIdx,
amrex::MultiFab *  source_flux_ptr 
)
inlineoverridevirtual

Registers an exposed source flux field.

Reimplemented from NullSurf.

351  {
352  AMREX_ALWAYS_ASSERT(varIdx >= 0 && varIdx < m_lsm_flux_size);
353  if (static_cast<int>(source_flux.size()) != m_lsm_flux_size) {
354  source_flux.resize(m_lsm_flux_size, nullptr);
355  }
356  source_flux[varIdx] = source_flux_ptr;
357  }
Here is the call graph for this function:

◆ Lsm_Set_Source_Has_Atmos_State()

void SLM::Lsm_Set_Source_Has_Atmos_State ( bool  source_has_atmos)
inlineoverridevirtual

Registers whether the source level's tref, qref and pref have been filled.

Reimplemented from NullSurf.

400  {
401  source_has_atmos_state = source_has_atmos;
402  }

◆ Lsm_Set_Source_Unmapped_Ptr()

void SLM::Lsm_Set_Source_Unmapped_Ptr ( const int &  varIdx,
amrex::MultiFab *  source_data_ptr 
)
inlineoverridevirtual

Registers a private source SLM state field.

Reimplemented from NullSurf.

381  {
382  AMREX_ALWAYS_ASSERT(varIdx >= 0 && varIdx < static_cast<int>(unmapped_fields.size()));
383  if (source_unmapped.size() != unmapped_fields.size()) {
384  source_unmapped.resize(unmapped_fields.size(), nullptr);
385  }
386  source_unmapped[varIdx] = source_data_ptr;
387  }
Here is the call graph for this function:

◆ Lsm_Type()

int SLM::Lsm_Type ( )
inlineoverridevirtual

Reimplemented from NullSurf.

511  {
512  return LSM_Type::SLM;
513  }
@ SLM
Definition: ERF_LSM_Types.H:8

◆ Lsm_Unmapped_Ptr()

amrex::MultiFab* SLM::Lsm_Unmapped_Ptr ( const int &  varIdx)
inlineoverridevirtual

Returns a private SLM state field.

Reimplemented from NullSurf.

370  {
371  AMREX_ALWAYS_ASSERT(varIdx >= 0 && varIdx < static_cast<int>(unmapped_fields.size()));
372  return lsm_fab_vars[unmapped_fields[varIdx]].get();
373  }
Here is the call graph for this function:

◆ Lsm_Unmapped_Size()

int SLM::Lsm_Unmapped_Size ( )
inlineoverridevirtual

Returns the number of private SLM state fields.

Reimplemented from NullSurf.

363 { return static_cast<int>(unmapped_fields.size()); }

◆ Lsm_WRFInputNames()

std::unordered_map<std::string, std::string>& SLM::Lsm_WRFInputNames ( )
inlineoverridevirtual

Reimplemented from NullSurf.

466  {
467  return SLM::wrfinput_map;
468  }
static std::unordered_map< std::string, std::string > wrfinput_map
Definition: ERF_SLM.H:985

◆ radiation_noahmp()

void SLM::radiation_noahmp ( const amrex::MFIter &  mfi)
5787 {
5788  const int d_khi_lsm = khi_lsm;
5789  const int d_opt_rad = opt_rad;
5790  const int d_opt_alb = opt_alb;
5791 
5792  auto box = mfi.tilebox();
5793 
5794  auto landmask_arr = landmask.const_array(mfi);
5795  auto vegtype_arr = lsm_fab_vars[LsmVar_SLM::vegtype]->const_array(mfi);
5796  auto vegetype_arr = vegetype.const_array(mfi);
5797 
5798  // Input arrays
5799  auto swdsvisxyref_arr = lsm_fab_vars[LsmVar_SLM::swdsvisxyref]->const_array(mfi);
5800  auto swdsnirxyref_arr = lsm_fab_vars[LsmVar_SLM::swdsnirxyref]->const_array(mfi);
5801  auto swdsvisdxyref_arr = lsm_fab_vars[LsmVar_SLM::swdsvisdxyref]->const_array(mfi);
5802  auto swdsnirdxyref_arr = lsm_fab_vars[LsmVar_SLM::swdsnirdxyref]->const_array(mfi);
5803  auto coszrsxy_arr = lsm_fab_vars[LsmVar_SLM::coszrsxy]->const_array(mfi);
5804  auto precipref_arr = lsm_fab_vars[LsmVar_SLM::precipref]->const_array(mfi);
5805 
5806  auto LAI_arr = LAI.const_array(mfi);
5807  auto SAI_arr = SAI.const_array(mfi);
5808  auto t_canop_arr = t_canop.const_array(mfi);
5809  auto t_ground_skin_arr = t_ground_skin.const_array(mfi);
5810  auto soilw_arr = lsm_fab_vars[LsmVar_SLM::soilw]->const_array(mfi);
5811  auto poro_soil_arr = lsm_fab_vars[LsmVar_SLM::poro_soil]->const_array(mfi);
5812  auto veg_frac_arr = lsm_fab_vars[LsmVar_SLM::veg_frac]->const_array(mfi);
5813 
5814  // Existing SLM vegetation structure parameters (used instead of reading from NoahmpTable.TBL)
5815  // NOTE: Khai_L is equivalent to NOAHMP's XL (leaf/stem orientation index)
5816  // NOTE: ztop is equivalent to NOAHMP's HVT (top of canopy height in meters)
5817  auto Khai_L_arr = Khai_L.const_array(mfi); // leaf/stem orientation (NOAHMP name: XL)
5818  auto ztop_arr = ztop.const_array(mfi); // canopy height (NOAHMP name: HVT)
5819 
5820  // State variables for NOAHMP radiation
5821  auto tauss_arr = tauss_noahmp.array(mfi);
5822 
5823  // Net radiation arrays for output
5824  auto net_rad_arr = net_rad.array(mfi);
5825  auto t_skin_arr = t_skin.array(mfi);
5826 
5827  // Incoming longwave radiation
5828  auto lwref_arr = lsm_fab_vars[LsmVar_SLM::lwref]->const_array(mfi);
5829 
5830  // Outputs
5831  auto emis_sfc_arr = lsm_fab_vars[LsmVar_SLM::emis_sfc]->array(mfi);
5832  auto alb_nir_sfc_arr = lsm_fab_vars[LsmVar_SLM::alb_nir_sfc]->array(mfi);
5833  auto alb_vis_sfc_arr = lsm_fab_vars[LsmVar_SLM::alb_vis_sfc]->array(mfi);
5834  auto alb_nir_sfc_diff_arr = lsm_fab_vars[LsmVar_SLM::alb_nir_sfc_diff]->array(mfi);
5835  auto alb_vis_sfc_diff_arr = lsm_fab_vars[LsmVar_SLM::alb_vis_sfc_diff]->array(mfi);
5836 
5837  amrex::Real dt_loc = m_dt;
5838 
5839  // Get radiation parameters on device
5840  const amrex::Real* d_rhol_vis = d_veg_params.at("rhol_vis")->data();
5841  const amrex::Real* d_rhol_nir = d_veg_params.at("rhol_nir")->data();
5842  const amrex::Real* d_rhos_vis = d_veg_params.at("rhos_vis")->data();
5843  const amrex::Real* d_rhos_nir = d_veg_params.at("rhos_nir")->data();
5844  const amrex::Real* d_taul_vis = d_veg_params.at("taul_vis")->data();
5845  const amrex::Real* d_taul_nir = d_veg_params.at("taul_nir")->data();
5846  const amrex::Real* d_taus_vis = d_veg_params.at("taus_vis")->data();
5847  const amrex::Real* d_taus_nir = d_veg_params.at("taus_nir")->data();
5848  // NOTE: d_xl uses existing Khai_L_arr, d_hvt uses existing ztop_arr
5849  const amrex::Real* d_rc = d_veg_params.at("rc")->data();
5850  const amrex::Real* d_hvb = d_veg_params.at("hvb")->data();
5851  const amrex::Real* d_den = d_veg_params.at("den")->data();
5852 
5853  const amrex::Real* d_albsat_vis = d_rad_params.at("albsat_vis")->data();
5854  const amrex::Real* d_albsat_nir = d_rad_params.at("albsat_nir")->data();
5855  const amrex::Real* d_albdry_vis = d_rad_params.at("albdry_vis")->data();
5856  const amrex::Real* d_albdry_nir = d_rad_params.at("albdry_nir")->data();
5857  const amrex::Real* d_alblak = d_rad_params.at("alblak")->data();
5858  const amrex::Real* d_omegas = d_rad_params.at("omegas")->data();
5859 
5860  const amrex::Real d_betads = betads_rad;
5861  const amrex::Real d_betais = betais_rad;
5862  const amrex::Real d_tau0 = tau0_rad;
5863  const amrex::Real d_grain_growth = grain_growth_rad;
5864  const amrex::Real d_extra_growth = extra_growth_rad;
5865  const amrex::Real d_dirt_soot = dirt_soot_rad;
5866  const amrex::Real d_swemx = swemx_rad;
5867  const amrex::Real d_bats_cosz = bats_cosz_rad;
5868  const amrex::Real d_bats_vis_new = bats_vis_new_rad;
5869  const amrex::Real d_bats_nir_new = bats_nir_new_rad;
5870  const amrex::Real d_bats_vis_age = bats_vis_age_rad;
5871  const amrex::Real d_bats_nir_age = bats_nir_age_rad;
5872  const amrex::Real d_bats_vis_dir = bats_vis_dir_rad;
5873  const amrex::Real d_bats_nir_dir = bats_nir_dir_rad;
5874 
5875  // Emissivity parameters for longwave radiation
5876  const amrex::Real d_snow_emis = snow_emis_rad;
5877  const amrex::Real d_eg_soil = eg_soil_rad;
5878  const amrex::Real d_eg_lake = eg_lake_rad;
5879 
5880  // Stefan-Boltzmann constant
5881  constexpr amrex::Real SB = 5.67e-08; // W/m²/K⁴
5882 
5883  ParallelFor( box, [=] AMREX_GPU_DEVICE (int i, int j, int)
5884  {
5885  if (landmask_arr(i, j, 0) != 1) {
5886  return;
5887  }
5888 
5889  // --------------------------------------------------------------------------------------------------
5890  // Local variables for radiation calculation (all arrays from NOAHMP RADIATION subroutine)
5891  // --------------------------------------------------------------------------------------------------
5892  constexpr amrex::Real MPE = 1.0E-6;
5893  constexpr int NBAND = 2;
5894  amrex::Real fage; //snow age function (0 - new snow)
5895  amrex::Real albgrd[2]; //ground albedo (direct)
5896  amrex::Real albgri[2]; //ground albedo (diffuse)
5897  amrex::Real albd[2]; //surface albedo (direct)
5898  amrex::Real albi[2]; //surface albedo (diffuse)
5899  amrex::Real fabd[2]; //flux abs by veg (per unit direct flux)
5900  amrex::Real fabi[2]; //flux abs by veg (per unit diffuse flux)
5901  amrex::Real ftdd[2]; //down direct flux below veg (per unit dir flux)
5902  amrex::Real ftid[2]; //down diffuse flux below veg (per unit dir flux)
5903  amrex::Real ftii[2]; //down diffuse flux below veg (per unit dif flux)
5904  amrex::Real frevd[2], frevi[2], fregd[2], fregi[2];
5905  amrex::Real albsnd[2]; //snow albedo (direct)
5906  amrex::Real albsni[2]; //snow albedo (diffuse)
5907  amrex::Real ftdi[2]; //down direct flux below veg per unit dif flux = 0
5908 
5909  amrex::Real fsha; //shaded fraction of canopy
5910  amrex::Real vai; //total LAI + stem area index, one sided
5911  amrex::Real fsun; //sunlit fraction of canopy (-)
5912  amrex::Real laisun; //sunlit leaf area (-)
5913  amrex::Real laisha; //shaded leaf area (-)
5914  amrex::Real parsun; //average absorbed par for sunlit leaves (w/m2)
5915  amrex::Real parsha; //average absorbed par for shaded leaves (w/m2)
5916  amrex::Real sav; //solar radiation absorbed by vegetation (w/m2)
5917  amrex::Real sag; //solar radiation absorbed by ground (w/m2)
5918  amrex::Real fsa; //total absorbed solar radiation (w/m2)
5919  amrex::Real fsr; //total reflected solar radiation (w/m2)
5920  amrex::Real fsrv; //veg. reflected solar radiation (w/m2)
5921  amrex::Real fsrg; //ground reflected solar radiation (w/m2)
5922  amrex::Real bgap, wgap;
5923  amrex::Real gdir; //average projected leaf/stem area in solar direction
5924  amrex::Real ext; //optical depth direct beam per unit leaf + stem area
5925  amrex::Real xl_diag_val, chil_diag_val, phi1_diag_val, phi2_diag_val; //diagnostic outputs
5926 
5927  amrex::Real rho[2]; //leaf/stem reflectance weighted by fraction LAI and SAI
5928  amrex::Real tau[2]; //leaf/stem transmittance weighted by fraction LAI and SAI
5929  amrex::Real wl, ws; //fraction of LAI+SAI that is LAI/SAI
5930 
5931  // Get input values
5932  amrex::Real cosz = coszrsxy_arr(i, j, 0);
5933  amrex::Real elai = LAI_arr(i, j, 0);
5934  amrex::Real esai = SAI_arr(i, j, 0);
5935  amrex::Real tv = t_canop_arr(i, j, 0);
5936  amrex::Real tg = t_ground_skin_arr(i, j, 0);
5937  amrex::Real fveg = veg_frac_arr(i, j, d_khi_lsm);
5938  int vegtyp = static_cast<int>(vegtype_arr(i, j, d_khi_lsm));
5939  int veg_idx = vegtyp - 1; // convert to 0-based index
5940 
5941  // Incoming solar radiation
5942  amrex::Real solad[2], solai[2];
5943  solad[0] = swdsvisxyref_arr(i, j, 0); // direct visible
5944  solad[1] = swdsnirxyref_arr(i, j, 0); // direct NIR
5945  solai[0] = swdsvisdxyref_arr(i, j, 0); // diffuse visible
5946  solai[1] = swdsnirdxyref_arr(i, j, 0); // diffuse NIR
5947 
5948  // Snow variables (set to zero for now - can be added later)
5949  amrex::Real fsno = zero; // snow cover fraction
5950  amrex::Real snowh = zero; // snow height (mm)
5951  amrex::Real sneqvo = zero; // snow mass at last time step (mm)
5952  amrex::Real sneqv = zero; // snow mass (mm)
5953  amrex::Real qsnow = zero; // snowfall (mm/s)
5954  amrex::ignore_unused(snowh, qsnow);
5955 
5956  // Wetness fraction (simplified - using precipitation as indicator)
5957  amrex::Real fwet = (precipref_arr(i, j, 0) > zero) ? 0.1 : zero;
5958 
5959  // Soil moisture for top layer
5960  amrex::Real smc[1];
5961  smc[0] = soilw_arr(i, j, d_khi_lsm) * poro_soil_arr(i, j, d_khi_lsm);
5962 
5963  int nsoil = 1; // using top layer only for albedo calc
5964  int ice = 0; // not ice
5965  int ist = 1; // soil surface type
5966 
5967  // Initialize outputs
5968  bgap = zero;
5969  wgap = zero;
5970  for (int ib = 0; ib < NBAND; ib++) {
5971  albgrd[ib] = zero;
5972  albgri[ib] = zero;
5973  albd[ib] = zero;
5974  albi[ib] = zero;
5975  fabd[ib] = zero;
5976  fabi[ib] = zero;
5977  ftdd[ib] = zero;
5978  ftid[ib] = zero;
5979  ftii[ib] = zero;
5980  albsnd[ib] = zero;
5981  albsni[ib] = zero;
5982  frevd[ib] = zero;
5983  frevi[ib] = zero;
5984  fregd[ib] = zero;
5985  fregi[ib] = zero;
5986  ftdi[ib] = zero;
5987  }
5988  fsun = zero;
5989 
5990  // --------------------------------------------------------------------------------------------------
5991  // ALBEDO CALCULATION (inline to make it device-callable)
5992  // --------------------------------------------------------------------------------------------------
5993 
5994  // snow age (allow nighttime aging)
5995  snow_age_noahmp(dt_loc, tg, sneqvo, sneqv, d_tau0, d_grain_growth, d_extra_growth,
5996  d_dirt_soot, d_swemx, tauss_arr(i,j,0), fage);
5997 
5998  if (cosz > zero) {
5999 
6000  // weight reflectance/transmittance by LAI and SAI
6001  vai = elai + esai;
6002  wl = elai / std::max(vai,MPE);
6003  ws = esai / std::max(vai,MPE);
6004 
6005  // snow albedos: only if COSZ > 0 and FSNO > 0
6006  if(d_opt_alb == 1) {
6007  snowalb_bats_noahmp(NBAND, fsno, cosz, fage,
6008  d_bats_cosz, d_bats_vis_new, d_bats_nir_new,
6009  d_bats_vis_age, d_bats_nir_age,
6010  d_bats_vis_dir, d_bats_nir_dir,
6011  albsnd, albsni);
6012  }
6013 
6014  // ground surface albedo
6015  // Get soil albedo parameters (using soil color index = 4 as default for now)
6016  int soil_color_idx = 3; // index into albedo arrays (0-based for index 4 soil color)
6017  amrex::Real albsat[2], albdry[2], alblak[2];
6018  albsat[0] = d_albsat_vis[soil_color_idx];
6019  albsat[1] = d_albsat_nir[soil_color_idx];
6020  albdry[0] = d_albdry_vis[soil_color_idx];
6021  albdry[1] = d_albdry_nir[soil_color_idx];
6022  alblak[0] = d_alblak[0];
6023  alblak[1] = d_alblak[1];
6024 
6025  groundalb_noahmp(nsoil, NBAND, ice, ist, fsno, smc, albsnd, albsni, cosz, tg,
6026  albsat, albdry, alblak, albgrd, albgri);
6027 
6028  const bool has_canopy = vegetype_arr(i, j, 0) == 1 && fveg > zero && vai > zero;
6029  if (has_canopy) {
6030  // Get parameters for this vegetation type
6031  for (int ib = 0; ib < NBAND; ib++) {
6032  amrex::Real rhol_val = (ib == 0) ? d_rhol_vis[veg_idx] : d_rhol_nir[veg_idx];
6033  amrex::Real rhos_val = (ib == 0) ? d_rhos_vis[veg_idx] : d_rhos_nir[veg_idx];
6034  amrex::Real taul_val = (ib == 0) ? d_taul_vis[veg_idx] : d_taul_nir[veg_idx];
6035  amrex::Real taus_val = (ib == 0) ? d_taus_vis[veg_idx] : d_taus_nir[veg_idx];
6036  rho[ib] = std::max(rhol_val*wl + rhos_val*ws, MPE);
6037  tau[ib] = std::max(taul_val*wl + taus_val*ws, MPE);
6038  }
6039 
6040  // loop over NBAND wavebands to calculate surface albedos and solar
6041  // fluxes for unit incoming direct (IC=0) and diffuse (IC=1)
6042 
6043  for (int ib = 0; ib < NBAND; ib++) {
6044  // direct (IC=0)
6045  twostream_noahmp(ib, 0, vegtyp, cosz, vai, fwet, tv, albgrd, albgri,
6046  rho, tau, fveg, ist,
6047  Khai_L_arr(i,j,0), d_omegas[ib], d_betads, d_betais,
6048  d_opt_rad, d_rc[veg_idx], ztop_arr(i,j,0), d_hvb[veg_idx], d_den[veg_idx],
6049  fabd, albd, ftdd, ftid, gdir, frevd, fregd, bgap, wgap,
6050  xl_diag_val, chil_diag_val, phi1_diag_val, phi2_diag_val);
6051 
6052  // diffuse (IC=1)
6053  twostream_noahmp(ib, 1, vegtyp, cosz, vai, fwet, tv, albgrd, albgri,
6054  rho, tau, fveg, ist,
6055  Khai_L_arr(i,j,0), d_omegas[ib], d_betads, d_betais,
6056  d_opt_rad, d_rc[veg_idx], ztop_arr(i,j,0), d_hvb[veg_idx], d_den[veg_idx],
6057  fabi, albi, ftdi, ftii, gdir, frevi, fregi, bgap, wgap,
6058  xl_diag_val, chil_diag_val, phi1_diag_val, phi2_diag_val);
6059  }
6060 
6061  // sunlit fraction of canopy. set FSUN = 0 if FSUN < 0.01.
6062  ext = gdir/cosz * std::sqrt(one-rho[0]-tau[0]);
6063  fsun = (one-std::exp(-ext*vai)) / std::max(ext*vai,MPE);
6064  ext = fsun;
6065 
6066  if (ext < 0.01) {
6067  wl = zero;
6068  } else {
6069  wl = ext;
6070  }
6071  fsun = wl;
6072  } else {
6073  for (int ib = 0; ib < NBAND; ib++) {
6074  albd[ib] = albgrd[ib];
6075  albi[ib] = albgri[ib];
6076  ftdd[ib] = one;
6077  ftii[ib] = one;
6078  fregd[ib] = albgrd[ib];
6079  fregi[ib] = albgri[ib];
6080  }
6081  }
6082 
6083  } // cosz>0
6084 
6085  // --------------------------------------------------------------------------------------------------
6086  // SURRAD CALCULATION
6087  // --------------------------------------------------------------------------------------------------
6088 
6089  fsha = 1.0-fsun;
6090  laisun = elai*fsun;
6091  laisha = elai*fsha;
6092  vai = elai+ esai;
6093 
6094  surrad_noahmp(MPE, fsun, fsha, elai, vai, laisun, laisha, solad, solai,
6095  fabd, fabi, ftdd, ftid, ftii, albgrd, albgri, albd, albi,
6096  frevd, frevi, fregd, fregi,
6097  parsun, parsha, sav, sag, fsa, fsr, fsrv, fsrg);
6098 
6099  // --------------------------------------------------------------------------------------------------
6100  // LONGWAVE RADIATION CALCULATION (NOAHMP formulation)
6101  // --------------------------------------------------------------------------------------------------
6102  // NOAHMP longwave scheme includes multiple reflections between canopy and ground
6103  // References: NOAHMP module_sf_noahmplsm.F lines 2137-2144 (emissivity)
6104  // lines 3874-3875, 4041 (canopy LW)
6105  // lines 4086-4087, 4104 (ground LW)
6106  // --------------------------------------------------------------------------------------------------
6107 
6108  // Get incoming longwave radiation from atmosphere (W/m²)
6109  // lwdn is positive downward (standard atmospheric convention)
6110  amrex::Real lwdn = lwref_arr(i, j, 0);
6111 
6112  // Calculate emissivities
6113  // Vegetation emissivity (NOAHMP line 2139)
6114  // EMV depends on total vegetation area index (LAI + SAI)
6115  // EMV = 1 for dense canopy, EMV → 0 for sparse/no vegetation
6116  amrex::Real emv = 1.0 - std::exp(-(elai + esai));
6117 
6118  // Ground emissivity (NOAHMP lines 2140-2144)
6119  // EMG is weighted by snow cover fraction (fsno)
6120  // Note: ice = 1 if IST = 2 (lake/ice), for now assume ice = 0 (soil)
6121  // Note: fsno and ist are already declared earlier (lines 5159, 5174)
6122  amrex::Real emg;
6123  if (ist == 1) {
6124  emg = d_eg_soil * (1.0 - fsno) + d_snow_emis * fsno;
6125  } else {
6126  emg = d_eg_lake * (1.0 - fsno) + d_snow_emis * fsno;
6127  }
6128 
6129  // --------------------------------------------------------------------------------------------------
6130  // Canopy net longwave radiation (IRC) - NOAHMP lines 3874-3875, 4041
6131  // --------------------------------------------------------------------------------------------------
6132  // NOAHMP SIGN CONVENTION: IRC is NET UPWARD longwave flux (positive = energy loss)
6133  // IRC = emitted_upward - absorbed_from_atmosphere - absorbed_from_ground
6134  // NOAHMP energy balance: SAV - IRC - SHC - EVC = 0
6135  //
6136  // Physical interpretation of AIR_C term:
6137  // -EMV*LWDN: canopy absorbs atmospheric LW (gain, hence negative)
6138  // -EMV*(1-EMV)*(1-EMG)*LWDN: multiple reflection of atmospheric LW
6139  // (transmitted through canopy, reflected by ground, absorbed by canopy)
6140  // -EMV*EMG*SB*TG^4: canopy absorbs ground emission (gain, hence negative)
6141  //
6142  // Physical interpretation of CIR_C*TV^4 term:
6143  // 2*EMV*SB*TV^4: canopy emits in both directions (up and down)
6144  // -EMV^2*(1-EMG)*SB*TV^4: multiple reflection of canopy downward emission
6145  // (reflected by ground and re-absorbed by canopy)
6146  // --------------------------------------------------------------------------------------------------
6147  amrex::Real air_c = -emv * (1.0 + (1.0 - emv) * (1.0 - emg)) * lwdn
6148  - emv * emg * SB * amrex::Math::powi<4>(tg);
6149  amrex::Real cir_c = (two - emv * (one - emg)) * emv * SB;
6150  amrex::Real irc = air_c + cir_c * amrex::Math::powi<4>(tv);
6151 
6152  // --------------------------------------------------------------------------------------------------
6153  // Ground net longwave radiation (IRG) - NOAHMP lines 4086-4087, 4104
6154  // --------------------------------------------------------------------------------------------------
6155  // NOAHMP SIGN CONVENTION: IRG is NET UPWARD longwave flux (positive = energy loss)
6156  // IRG = emitted_upward - absorbed_from_atmosphere - absorbed_from_canopy
6157  // NOAHMP energy balance: SAG - IRG - SHG - EVG = 0
6158  //
6159  // Physical interpretation of AIR_G term:
6160  // -EMG*(1-EMV)*LWDN: ground absorbs transmitted atmospheric LW (gain, hence negative)
6161  // -EMG*EMV*SB*TV^4: ground absorbs canopy downward emission (gain, hence negative)
6162  //
6163  // Physical interpretation of CIR_G*TG^4 term:
6164  // EMG*SB*TG^4: ground emits upward
6165  // --------------------------------------------------------------------------------------------------
6166  amrex::Real air_g = -emg * (1.0 - emv) * lwdn - emg * emv * SB * amrex::Math::powi<4>(tv);
6167  amrex::Real cir_g = emg * SB;
6168  amrex::Real irg = cir_g * amrex::Math::powi<4>(tg) + air_g;
6169 
6170  amrex::Real tir1 = emv * SB * amrex::Math::powi<4>(tv);
6171  amrex::Real tir2 = emg * SB * amrex::Math::powi<4>(tg);
6172  amrex::Real lwdn1 = lwdn;
6173  amrex::Real lwdn2 = (1.0 - emv) * lwdn + tir1;
6174  amrex::Real lwup2 = tir2 + (1.0 - emg) * lwdn2;
6175  amrex::Real lwup1 = lwdn + irc + irg;
6176 
6177  // --------------------------------------------------------------------------------------------------
6178  // Store shortwave and longwave radiation to net_rad arrays
6179  // --------------------------------------------------------------------------------------------------
6180  // SLM SIGN CONVENTION: net_rad is NET ABSORBED radiation (positive = energy gain)
6181  // net_rad = absorbed_radiation (all sources) - emitted_radiation
6182  // SLM energy balance: net_rad - SH - LH = heat storage change
6183  //
6184  // SIGN CONVERSION: IRC/IRG (net upward) → net_lw (net absorbed)
6185  // net_absorbed = -net_upward
6186  // When IRC > 0 (net upward flux, energy loss), net_lw1 < 0 (energy loss)
6187  // When IRC < 0 (net downward flux, energy gain), net_lw1 > 0 (energy gain)
6188  // --------------------------------------------------------------------------------------------------
6189 
6190  // Shortwave absorbed (W/m²)
6191  net_rad_arr(i, j, 0, SLM_NetRad::net_sw1) = sav; // canopy absorbed solar
6192  net_rad_arr(i, j, 0, SLM_NetRad::net_sw2) = sag; // ground absorbed solar
6193 
6194  // Longwave absorbed (W/m²)
6195  // Sign flip converts NOAHMP convention (net upward) to SLM convention (net absorbed)
6196  net_rad_arr(i, j, 0, SLM_NetRad::tir1) = tir1;
6197  net_rad_arr(i, j, 0, SLM_NetRad::tir2) = tir2;
6198  net_rad_arr(i, j, 0, SLM_NetRad::net_lwup1) = lwup1;
6199  net_rad_arr(i, j, 0, SLM_NetRad::net_lwup2) = lwup2;
6200  net_rad_arr(i, j, 0, SLM_NetRad::net_lwdn1) = lwdn1;
6201  net_rad_arr(i, j, 0, SLM_NetRad::net_lwdn2) = lwdn2;
6202  net_rad_arr(i, j, 0, SLM_NetRad::net_lw1) = -irc; // canopy absorbed longwave
6203  net_rad_arr(i, j, 0, SLM_NetRad::net_lw2) = -irg; // ground absorbed longwave
6204 
6205  // Total net radiation (W/m²)
6206  // net_rad = shortwave_absorbed + longwave_absorbed
6207  // = sav + (-irc) = sav - irc
6208  net_rad_arr(i, j, 0, SLM_NetRad::net_rad1) = sav - irc; // canopy: SW + LW
6209  net_rad_arr(i, j, 0, SLM_NetRad::net_rad2) = sag - irg; // ground: SW + LW
6210 
6211  // Directional shortwave diagnostics use positive physical directions:
6212  // downwelling is positive in net_swdn and upwelling is positive in net_swup.
6213  // net_sw1 and net_sw2 remain positive for absorbed shortwave.
6214  // The surrad terms satisfy SW_in = reflected + canopy absorbed + ground absorbed.
6215  // Downwelling shortwave for stomatal resistance calculation
6216  // Total incoming SW = direct + diffuse for both visible and NIR
6217  const amrex::Real sw_in = solad[0] + solad[1] + solai[0] + solai[1];
6218  net_rad_arr(i, j, 0, SLM_NetRad::net_swdn1) = sw_in;
6219 
6220  // Reflected shortwave at the canopy top.
6221  net_rad_arr(i, j, 0, SLM_NetRad::net_swup1) = fsr;
6222 
6223  // Downwelling SW reaching ground (transmitted through canopy)
6224  amrex::Real sw_down_ground = zero;
6225  for (int ib = 0; ib < NBAND; ib++) {
6226  sw_down_ground += solad[ib] * (ftdd[ib] + ftid[ib])
6227  + solai[ib] * ftii[ib];
6228  }
6229  net_rad_arr(i, j, 0, SLM_NetRad::net_swdn2) = sw_down_ground;
6230  net_rad_arr(i, j, 0, SLM_NetRad::net_swup2) = sw_down_ground - sag;
6231  // --------------------------------------------------------------------------------------------------
6232  // Store albedo outputs
6233  // --------------------------------------------------------------------------------------------------
6234 
6235  alb_vis_sfc_arr(i, j, 0) = albd[0];
6236  alb_nir_sfc_arr(i, j, 0) = albd[1];
6237  alb_vis_sfc_diff_arr(i, j, 0) = albi[0];
6238  alb_nir_sfc_diff_arr(i, j, 0) = albi[1];
6239 
6240  alb_vis_sfc_arr(i, j, d_khi_lsm) = albd[0];
6241  alb_nir_sfc_arr(i, j, d_khi_lsm) = albd[1];
6242  alb_vis_sfc_diff_arr(i, j, d_khi_lsm) = albi[0];
6243  alb_nir_sfc_diff_arr(i, j, d_khi_lsm) = albi[1];
6244 
6245  // Surface emissivity for the vegetated canopy-ground system, including
6246  // the Noah-MP multiple-reflection term.
6247  amrex::Real emiss_sfc = emv + emg * (1.0 - emv)
6248  + emv * (1.0 - emv) * (1.0 - emg);
6249  emis_sfc_arr(i, j, 0) = emiss_sfc;
6250  emis_sfc_arr(i, j, d_khi_lsm) = emis_sfc_arr(i, j, 0);
6251 
6252  t_skin_arr(i, j, 0) = std::pow(
6253  std::max(MPE, (lwup1 - (one - emiss_sfc) * lwdn) /
6254  (emiss_sfc * SB)), fourth);
6255 
6256  }); // End ParallelFor
6257 }
AMREX_GPU_HOST_DEVICE static AMREX_FORCE_INLINE void twostream_noahmp(int ib, int ic, int vegtyp, amrex::Real cosz, amrex::Real vai, amrex::Real fwet, amrex::Real t, const amrex::Real *albgrd, const amrex::Real *albgri, const amrex::Real *rho, const amrex::Real *tau, amrex::Real fveg, int ist, amrex::Real xl, amrex::Real omegas_param, amrex::Real betads, amrex::Real betais, int opt_rad, amrex::Real rc, amrex::Real hvt, amrex::Real hvb, amrex::Real den, amrex::Real *fab, amrex::Real *fre, amrex::Real *ftd, amrex::Real *fti, amrex::Real &gdir, amrex::Real *frev, amrex::Real *freg, amrex::Real &bgap, amrex::Real &wgap, amrex::Real &xl_out, amrex::Real &chil_out, amrex::Real &phi1_out, amrex::Real &phi2_out)
Definition: ERF_SLM.cpp:5429
AMREX_GPU_HOST_DEVICE static AMREX_FORCE_INLINE void surrad_noahmp(amrex::Real mpe, amrex::Real fsun, amrex::Real fsha, amrex::Real elai, amrex::Real vai, amrex::Real laisun, amrex::Real laisha, const amrex::Real *solad, const amrex::Real *solai, const amrex::Real *fabd, const amrex::Real *fabi, const amrex::Real *ftdd, const amrex::Real *ftid, const amrex::Real *ftii, const amrex::Real *albgrd, const amrex::Real *albgri, const amrex::Real *albd, const amrex::Real *albi, const amrex::Real *frevd, const amrex::Real *frevi, const amrex::Real *fregd, const amrex::Real *fregi, amrex::Real &parsun, amrex::Real &parsha, amrex::Real &sav, amrex::Real &sag, amrex::Real &fsa, amrex::Real &fsr, amrex::Real &fsrv, amrex::Real &fsrg)
Definition: ERF_SLM.cpp:5666
int opt_rad
Definition: ERF_SLM.H:914
AMREX_GPU_HOST_DEVICE static AMREX_FORCE_INLINE void snow_age_noahmp(amrex::Real dt, amrex::Real tg, amrex::Real sneqvo, amrex::Real sneqv, amrex::Real tau0, amrex::Real grain_growth, amrex::Real extra_growth, amrex::Real dirt_soot, amrex::Real swemx, amrex::Real &tauss, amrex::Real &fage)
Definition: ERF_SLM.cpp:5259
AMREX_GPU_HOST_DEVICE static AMREX_FORCE_INLINE void snowalb_bats_noahmp(int nband, amrex::Real fsno, amrex::Real cosz, amrex::Real fage, amrex::Real bats_cosz, amrex::Real bats_vis_new, amrex::Real bats_nir_new, amrex::Real bats_vis_age, amrex::Real bats_nir_age, amrex::Real bats_vis_dir, amrex::Real bats_nir_dir, amrex::Real *albsnd, amrex::Real *albsni)
Definition: ERF_SLM.cpp:5312
AMREX_GPU_HOST_DEVICE static AMREX_FORCE_INLINE void groundalb_noahmp(int nsoil, int nband, int ice, int ist, amrex::Real fsno, const amrex::Real *smc, const amrex::Real *albsnd, const amrex::Real *albsni, amrex::Real cosz, amrex::Real tg, const amrex::Real *albsat, const amrex::Real *albdry, const amrex::Real *alblak, amrex::Real *albgrd, amrex::Real *albgri)
Definition: ERF_SLM.cpp:5367
int opt_alb
Definition: ERF_SLM.H:915
@ rho
Definition: ERF_Kessler.H:25
@ net_lwdn1
Definition: ERF_SLM.H:121
@ net_swdn1
Definition: ERF_SLM.H:113
@ net_lw2
Definition: ERF_SLM.H:124
@ net_swdn2
Definition: ERF_SLM.H:114
@ tir2
Definition: ERF_SLM.H:118
@ net_lwdn2
Definition: ERF_SLM.H:122
@ net_lwup1
Definition: ERF_SLM.H:119
@ net_lw1
Definition: ERF_SLM.H:123
@ net_swup1
Definition: ERF_SLM.H:111
@ tir1
Definition: ERF_SLM.H:117
@ net_swup2
Definition: ERF_SLM.H:112
@ net_lwup2
Definition: ERF_SLM.H:120
Here is the call graph for this function:

◆ ReadCheckpoint()

void SLM::ReadCheckpoint ( const int &  lev,
const std::string &  checkpointname 
)
overridevirtual

Reimplemented from NullSurf.

691 {
692  auto check_start = amrex::second();
693 
694  amrex::Print() << " Reading SLM checkpoint at level " << lev << std::endl;
695 
696  // Header
697  std::string File(checkpointname + "/Level_" + std::to_string(lev) + "/SLM_Header");
698 
699  VisMF::IO_Buffer io_buffer(VisMF::GetIOBufferSize());
700 
701  Vector<char> fileCharPtr;
702  ParallelDescriptor::ReadAndBcastFile(File, fileCharPtr);
703  std::string fileCharPtrString(fileCharPtr.dataPtr());
704  std::istringstream is(fileCharPtrString, std::istringstream::in);
705 
706  std::string line, word;
707 
708  // read in title line
709  std::getline(is, line);
710 
711  // read in number of soil layers and the array of dz. Init has already built the
712  // soil geometry from slm.nsoil / slm.soil_dz, so these are checked against it
713  // rather than adopted.
714  int nz_chk = 0;
715  is >> nz_chk;
716  GotoNextLine(is);
717 
718  Vector<Real> dz_chk;
719  std::getline(is, line);
720  {
721  std::istringstream lis(line);
722  while (lis >> word) {
723  dz_chk.push_back(static_cast<Real>(std::stod(word)));
724  }
725  }
726 
728  nz_chk == m_nz_lsm && static_cast<int>(dz_chk.size()) == m_nz_lsm,
729  "SLM::ReadCheckpoint: the checkpoint's number of soil layers differs from slm.nsoil");
730  for (int k = 0; k < m_nz_lsm; ++k) {
732  std::abs(dz_chk[k] - m_dz_lsm[k]) <= Real(1.e-6) * m_dz_lsm[k],
733  "SLM::ReadCheckpoint: the checkpoint's soil layer thicknesses differ from slm.soil_dz");
734  }
735 
736  // Read in time
737  is >> m_dt;
738  is >> time;
739  is >> start_time;
740  is >> m_calday;
741  is >> m_orbital_year;
742  is >> m_orbital_mon;
743  is >> m_orbital_day;
744  is >> m_orbital_sec;
745  GotoNextLine(is);
746 
747  // Read in constants
748  is >> tabs_s;
749  is >> t00;
750  is >> LAI0;
751  is >> tausoil;
752  is >> zref;
753  is >> z0_soil;
754  is >> mws_mx0;
755  is >> Rc_max;
756  is >> T_opt;
757  GotoNextLine(is);
758 
759  // Read in flags
760  int reserved_slm_flag = 0;
761  is >> dosoiltnudging;
762  is >> dosoilwnudging;
763  is >> reserved_slm_flag;
764  is >> use_param_file;
765  is >> interpolate_lai;
766  is >> use_wrf_lai;
767  is >> use_wrfinput;
768  amrex::ignore_unused(reserved_slm_flag);
769  GotoNextLine(is);
770 
771  // read in level 'lev' BoxArray from Header
772  BoxArray ba;
773  ba.readFrom(is);
774  GotoNextLine(is);
775 
776  //
777  // The grids the checkpoint was written on. They need not be this run's: a restart that
778  // re-makes the level-0 grids (issue 4225) builds the model on the new ones, and every
779  // field below is read on the grids in the file and redistributed onto those.
780  //
781  amrex::ignore_unused(ba);
782 
783  //
784  // Lifecycle flags, if this checkpoint carries them (see WriteCheckpoint). The
785  // defaults are what the reader always did: treat the model as initialized.
786  //
787  // NOTE: the BoxArray read above is already followed by its own GotoNextLine, so
788  // the stream sits at the start of this line -- do not skip another.
789  //
790  int chk_first_step = 0;
791  int chk_wrfinput_initialized = (use_wrfinput) ? 1 : 0;
792  {
793  int a, b;
794  if (is >> a >> b) { chk_first_step = a; chk_wrfinput_initialized = b; }
795  }
796 
797  const std::string prefix = "SLM_";
798 
799  // Every field is planar or a column of soil layers: periodic laterally as the domain is,
800  // never in z
801  const Periodicity period = erf_checkpoint::lateral_periodicity(m_geom);
802  auto read_field = [&] (MultiFab& dst, const std::string& name, int ncomp,
803  const IntVect& ng_read, bool src_ghosts)
804  {
806  dst, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + name),
807  0, 0, ncomp, ng_read, src_ghosts, period);
808  };
809 
810  // Use distribution map from data vars, since those are already created and loaded by ERF
811  const DistributionMapping& dm = lsm_fab_vars[0]->DistributionMap();
813  MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "landmask"), period);
815  MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "landtype"), period);
817  MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "vegetype"), period);
818 
819  // DST
820  read_field(soilt_vars, "DST", SLM_DST::NumVars, IntVect(0), false);
821 
822  // DSW
823  read_field(soilw_vars, "DSW", SLM_DSW::NumVars, IntVect(0), false);
824 
825  // net_rad
826  read_field(net_rad, "net_rad", SLM_NetRad::NumVars, IntVect(0), false);
827 
828  // slm_diag
829  read_field(slm_diag, "slm_diag", SLM_Diag::NumVars, IntVect(0), false);
830 
831  read_field(LAI, "LAI", 1, IntVect(0), false);
832 
833  read_field(SAI, "SAI", 1, IntVect(0), false);
834 
835  read_field(sstxy, "sstxy", 1, IntVect(0), false);
836 
837  read_field(t_canop, "t_canop", 1, IntVect(0), false);
838 
839  read_field(mw, "mw", 1, IntVect(0), false);
840 
841  read_field(mws, "mws", 1, IntVect(0), false);
842 
843  read_field(t_skin, "t_skin", 1, IntVect(0), false);
844 
845  read_field(t_ground_skin, "t_ground_skin", 1, IntVect(0), false);
846 
847  read_field(t_cas, "t_cas", 1, IntVect(0), false);
848 
849  read_field(q_cas, "q_cas", 1, IntVect(0), false);
850 
851  read_field(vege_YES, "vege_YES", 1, IntVect(0), false);
852 
853  read_field(cp_vege, "cp_vege", 1, IntVect(0), false);
854 
855  read_field(z0_sfc, "z0_sfc", 1, IntVect(0), false);
856 
857  read_field(Khai_L, "Khai_L", 1, IntVect(0), false);
858 
859  read_field(phi_1, "phi_1", 1, IntVect(0), false);
860 
861  read_field(phi_2, "phi_2", 1, IntVect(0), false);
862 
863  read_field(IR_emis_vege, "IR_emis_vege", 1, IntVect(0), false);
864 
865  read_field(IR_emis_soil, "IR_emis_soil", 1, IntVect(0), false);
866 
867  read_field(ztop, "ztop", 1, IntVect(0), false);
868 
869  read_field(disp_hgt, "disp_hgt", 1, IntVect(0), false);
870 
871  read_field(Rgl, "Rgl", 1, IntVect(0), false);
872 
873  read_field(Rc_min, "Rc_min", 1, IntVect(0), false);
874 
875  read_field(hs_rc, "hs_rc", 1, IntVect(0), false);
876 
877  read_field(precip_extinc, "precip_extinc", 1, IntVect(0), false);
878 
879  read_field(mw_mx, "mw_mx", 1, IntVect(0), false);
880 
881  read_field(mws_mx, "mws_mx", 1, IntVect(0), false);
882 
883  read_field(BAI, "BAI", 1, IntVect(0), false);
884 
885  read_field(mw_inc, "mw_inc", 1, IntVect(0), false);
886 
887  read_field(evapo_dry, "evapo_dry", 1, IntVect(0), false);
888 
889  read_field(shf_canop, "shf_canop", 1, IntVect(0), false);
890 
891  read_field(shf_soil, "shf_soil", 1, IntVect(0), false);
892 
893  read_field(shf_air, "shf_air", 1, IntVect(0), false);
894 
895  read_field(lhf_canop, "lhf_canop", 1, IntVect(0), false);
896 
897  read_field(lhf_soil, "lhf_soil", 1, IntVect(0), false);
898 
899  read_field(lhf_air, "lhf_air", 1, IntVect(0), false);
900 
901  read_field(albedovis_v, "albedovis_v", 1, IntVect(0), false);
902 
903  read_field(albedonir_v, "albedonir_v", 1, IntVect(0), false);
904 
905  read_field(albedovis_s, "albedovis_s", 1, IntVect(0), false);
906 
907  read_field(albedonir_s, "albedonir_s", 1, IntVect(0), false);
908 
909  read_field(r_a, "r_a", 1, IntVect(0), false);
910 
911  read_field(r_b, "r_b", 1, IntVect(0), false);
912 
913  read_field(r_c, "r_c", 1, IntVect(0), false);
914 
915  read_field(r_d, "r_d", 1, IntVect(0), false);
916 
917  read_field(r_soil, "r_soil", 1, IntVect(0), false);
918 
919  read_field(wet_canop, "wet_canop", 1, IntVect(0), false);
920 
921  read_field(zrefxy, "zrefxy", 1, IntVect(0), false);
922 
923  read_field(albold_noahmp, "albold_noahmp", 1, IntVect(0), false);
924 
925  read_field(tauss_noahmp, "tauss_noahmp", 1, IntVect(0), false);
926 
927  for (int i = 0; i < static_cast<int>(unmapped_fields.size()); i++) {
928  read_field(*(lsm_fab_vars[unmapped_fields[i]]), "Data" + std::to_string(unmapped_fields[i]),
929  1, IntVect(1,1,1), true);
930  }
931 
932  //
933  // Restore where the model was, rather than asserting it was past its first step.
934  // A checkpoint taken before any step has been advanced leaves first_step set, so
935  // the first Advance after the restart does the deferred wrfinput read and rebuilds
936  // zrefxy -- exactly what the run being continued did at that point.
937  //
938  first_step = (chk_first_step != 0);
939  wrfinput_initialized = (chk_wrfinput_initialized != 0);
940 
941  // A checkpoint is written after Update_Lsm_Vars, so tref, qref and pref hold the state
942  has_atmos_state = true;
943  init_from_atmos_pending = false;
944 
945  // Initialize common parameters
947  init_soil_vars();
949 
950  auto check_end = amrex::second() - check_start;
951  ParallelDescriptor::ReduceRealMax(check_end,ParallelDescriptor::IOProcessorNumber());
952  amrex::Print() << " SLM Checkpoint load time = " << check_end << " seconds." << '\n';
953 }
static void GotoNextLine(std::istream &is)
Definition: ERF_SLM_IO.cpp:382
int m_orbital_mon
Definition: ERF_SLM.H:972
amrex::Real m_calday
Definition: ERF_SLM.H:970
int m_orbital_sec
Definition: ERF_SLM.H:974
int m_orbital_year
Definition: ERF_SLM.H:971
int m_orbital_day
Definition: ERF_SLM.H:973
void rebuild_restart_fields()
Definition: ERF_SLM.cpp:2257
void read_checkpoint_imultifab(amrex::iMultiFab &dst, const amrex::BoxArray &ba, const amrex::DistributionMapping &dm, const std::string &mf_name, const amrex::Periodicity &period)
Definition: ERF_ReadCheckpointMF.H:120
amrex::Periodicity lateral_periodicity(const amrex::Geometry &geom)
Definition: ERF_ReadCheckpointMF.H:20
void read_checkpoint_multifab(amrex::MultiFab &dst, const std::string &mf_name, int scomp, int dcomp, int ncomp, const amrex::IntVect &dst_ghosts, bool src_ghosts, const amrex::Periodicity &period)
Definition: ERF_ReadCheckpointMF.H:52
Here is the call graph for this function:

◆ ReadParameterFile()

SLMParameterTable SLM::ReadParameterFile ( const std::string &  filename,
amrex::Vector< std::string > &  veg_categories,
amrex::Vector< std::string > &  soil_categories 
)

Loads and parses the given .TBL file (such as NoahMPTable.TBL)

Returns an unordered map containing the name of each parameter block, where each block contains a list of variables, with each variable containing one or more values.

Returns the vegetation and soil category names loaded from file as a vector of names.

2361 {
2362  amrex::Print() << " SLM: Reading parameter file '" << filename << "'..." << std::endl;
2363 
2364  Vector<char> fileCharPtr;
2365  ParallelDescriptor::ReadAndBcastFile(filename, fileCharPtr);
2366  std::string fileCharPtrString(fileCharPtr.dataPtr());
2367  std::istringstream is(fileCharPtrString, std::istringstream::in);
2368 
2369  std::string line;
2370 
2371  std::unordered_map<std::string,
2372  amrex::Vector<std::pair<std::string, amrex::Vector<amrex::Real>>>> table;
2373 
2374  const auto trim = [](std::string value) {
2375  const auto first = value.find_first_not_of(" \t\r\n");
2376  if (first == std::string::npos) { return std::string{}; }
2377  const auto last = value.find_last_not_of(" \t\r\n");
2378  return value.substr(first, last - first + 1);
2379  };
2380 
2381  std::string block_name;
2382  while (std::getline(is, line)) {
2383  const auto comment_pos = line.find('!');
2384  if (comment_pos != std::string::npos) { line.erase(comment_pos); }
2385  line = trim(line);
2386  if (line.empty()) { continue; }
2387 
2388  if (line[0] == '&') {
2389  block_name = amrex::toLower(trim(line.substr(1)));
2390  table.try_emplace(block_name);
2391  continue;
2392  }
2393  if (line[0] == '/') {
2394  block_name.clear();
2395  continue;
2396  }
2397  if (block_name.empty()) { continue; }
2398 
2399  const auto equal_pos = line.find('=');
2400  if (equal_pos == std::string::npos) { continue; }
2401 
2402  const std::string varname = amrex::toLower(trim(line.substr(0, equal_pos)));
2403  std::string values = trim(line.substr(equal_pos + 1));
2404  if (varname == "veg_dataset_description") {
2405  veg_categories.push_back(values);
2406  continue;
2407  }
2408  if (varname == "sltype") {
2409  soil_categories.push_back(values);
2410  continue;
2411  }
2412 
2413  std::replace(values.begin(), values.end(), ',', ' ');
2414  std::replace(values.begin(), values.end(), 'D', 'E');
2415  std::replace(values.begin(), values.end(), 'd', 'e');
2416  std::istringstream value_stream(values);
2417  std::string token;
2418  amrex::Vector<amrex::Real> var_values;
2419  while (value_stream >> token) {
2420  try {
2421  std::size_t parsed = 0;
2422  const amrex::Real value = std::stod(token, &parsed);
2423  if (parsed != token.size()) {
2424  amrex::Abort("SLM: invalid value '" + token + "' for parameter '" +
2425  varname + "' in block '" + block_name + "'");
2426  }
2427  var_values.push_back(value);
2428  } catch (const std::exception&) {
2429  amrex::Abort("SLM: invalid value '" + token + "' for parameter '" +
2430  varname + "' in block '" + block_name + "'");
2431  }
2432  }
2433  if (!var_values.empty()) {
2434  table[block_name].push_back(std::make_pair(varname, var_values));
2435  }
2436  }
2437 
2438 
2439  // Print out all the variables:
2440  amrex::Print() << "---------------------------------------------------" << std::endl;
2441  amrex::Print() << " SLM Parameter File '" << filename << "':" << std::endl;
2442  amrex::Print() << "---------------------------------------------------" << std::endl;
2443  for (auto &block : table) {
2444  amrex::Print() << " '" << block.first << "':" << std::endl;
2445  for (auto &var : block.second) {
2446  amrex::Print() << " - '" << var.first << "': [";
2447  for (int i = 0; i < static_cast<int>(var.second.size()); ++i) {
2448  amrex::Print() << " " << var.second[i];
2449  if (i + 1 < static_cast<int>(var.second.size())) {
2450  amrex::Print() << ",";
2451  }
2452  }
2453  amrex::Print() << " ]" << std::endl;
2454  }
2455  }
2456  amrex::Print() << "---------------------------------------------------" << std::endl;
2457 
2458  return table;
2459 }

◆ rebuild_restart_fields()

void SLM::rebuild_restart_fields ( )

Rebuilds restart fields that can be derived from the checkpoint state.

2258 {
2259  const int d_khi_lsm = khi_lsm;
2260 
2261  nroot.setVal(m_nz_lsm);
2262  for (MFIter mfi(landtype, TileNoZ()); mfi.isValid(); ++mfi) {
2263  const Box box = mfi.tilebox();
2264 
2265  auto landmask_arr = landmask.const_array(mfi);
2266  auto landtype_arr = landtype.const_array(mfi);
2267  auto vegetype_arr = vegetype.const_array(mfi);
2268  auto nroot_arr = nroot.array(mfi);
2269  auto IR_emis_soil_arr = IR_emis_soil.const_array(mfi);
2270  auto IR_emis_grnd_arr = IR_emis_grnd.array(mfi);
2271  auto IMPERV_arr = IMPERV.array(mfi);
2272 
2273  ParallelFor(box, [=] AMREX_GPU_DEVICE (int i, int j, int) noexcept
2274  {
2275  if (landmask_arr(i, j, 0) == 1) {
2276  IMPERV_arr(i, j, 0) = landtype_arr(i, j, 0) == 13 ? 0.75 : 0.0;
2277  IR_emis_grnd_arr(i, j, 0) = landtype_arr(i, j, 0) == 13
2278  ? IR_emis_urban * IMPERV_arr(i, j, 0)
2279  + IR_emis_soil_arr(i, j, 0) * (1.0 - IMPERV_arr(i, j, 0))
2280  : IR_emis_soil_arr(i, j, 0);
2281  if (vegetype_arr(i, j, 0) == 0) {
2282  nroot_arr(i, j, 0) = 0;
2283  }
2284  } else {
2285  IMPERV_arr(i, j, 0) = zero;
2286  IR_emis_grnd_arr(i, j, 0) = zero;
2287  }
2288  });
2289  }
2290 
2291  // Reapply table-derived parameters without resetting prognostic state.
2292  init_from_params();
2293 
2294  for (MFIter mfi(landtype, TileNoZ()); mfi.isValid(); ++mfi) {
2295  const Box box = mfi.tilebox();
2296 
2297  auto landmask_arr = landmask.const_array(mfi);
2298  auto vegetype_arr = vegetype.const_array(mfi);
2299  auto t_ground_skin_arr = t_ground_skin.const_array(mfi);
2300  auto mws_arr = mws.const_array(mfi);
2301  auto m_pot_sat_arr = lsm_fab_vars[LsmVar_SLM::m_pot_sat]->const_array(mfi);
2302  auto soilw_arr = lsm_fab_vars[LsmVar_SLM::soilw]->const_array(mfi);
2303  auto Bconst_arr = lsm_fab_vars[LsmVar_SLM::Bconst]->const_array(mfi);
2304  auto pref_arr = lsm_fab_vars[LsmVar_SLM::pref]->const_array(mfi);
2305  auto q_gr_arr = q_gr.array(mfi);
2306  auto sdew_arr = sdew.array(mfi);
2307  auto t_sfc_arr = t_sfc.array(mfi);
2308  auto q_sfc_arr = q_sfc.array(mfi);
2309  auto t_cas_arr = t_cas.const_array(mfi);
2310  auto q_cas_arr = q_cas.const_array(mfi);
2311 
2312  ParallelFor(box, [=] AMREX_GPU_DEVICE (int i, int j, int) noexcept
2313  {
2314  if (landmask_arr(i, j, 0) != 1) {
2315  q_gr_arr(i, j, 0) = zero;
2316  sdew_arr(i, j, 0) = zero;
2317  t_sfc_arr(i, j, 0) = zero;
2318  q_sfc_arr(i, j, 0) = zero;
2319  return;
2320  }
2321 
2322  amrex::Real q_ground;
2323  amrex::Real dew_factor = one;
2324  if (t_ground_skin_arr(i, j, 0) >= tfriz) {
2325  erf_qsatw(t_ground_skin_arr(i, j, 0), pref_arr(i, j, 0), q_ground);
2326  if (mws_arr(i, j, 0) == zero) {
2327  const amrex::Real humidity_factor = fh_calc(
2328  t_ground_skin_arr(i, j, 0),
2329  m_pot_sat_arr(i, j, d_khi_lsm),
2330  soilw_arr(i, j, d_khi_lsm),
2331  Bconst_arr(i, j, d_khi_lsm));
2332  dew_factor = humidity_factor > 0.99 ? one : zero;
2333  q_ground *= humidity_factor;
2334  }
2335  } else {
2336  erf_qsati(t_ground_skin_arr(i, j, 0), pref_arr(i, j, 0), q_ground);
2337  }
2338 
2339  q_gr_arr(i, j, 0) = q_ground;
2340  sdew_arr(i, j, 0) = dew_factor;
2341  if (vegetype_arr(i, j, 0) == 1) {
2342  t_sfc_arr(i, j, 0) = t_cas_arr(i, j, 0);
2343  q_sfc_arr(i, j, 0) = q_cas_arr(i, j, 0);
2344  } else {
2345  t_sfc_arr(i, j, 0) = t_ground_skin_arr(i, j, 0);
2346  q_sfc_arr(i, j, 0) = q_ground;
2347  }
2348  });
2349  }
2350 }
void init_from_params()
Definition: ERF_SLM.cpp:2674
static constexpr amrex::Real IR_emis_urban
Definition: ERF_SLM.H:787
Here is the call graph for this function:

◆ resistances()

void SLM::resistances ( const amrex::MFIter &  mfi)
3596 {
3597  const int d_khi_lsm = khi_lsm;
3598  const int d_nz_lsm = m_nz_lsm;
3599  const Real d_z0_soil = z0_soil;
3600  const Real d_T_opt = T_opt;
3601  const Real d_Rc_max = Rc_max;
3602 
3603  auto box = mfi.tilebox();
3604 
3605  auto landmask_arr = landmask.const_array(mfi);
3606  auto landtype_arr = landtype.const_array(mfi);
3607  auto vegetype_arr = vegetype.const_array(mfi);
3608 
3609  auto soilt_arr = lsm_fab_vars[LsmVar_SLM::soilt]->const_array(mfi);
3610  auto soilw_arr = lsm_fab_vars[LsmVar_SLM::soilw]->const_array(mfi);
3611  auto s_depth_arr = lsm_fab_vars[LsmVar_SLM::s_depth]->const_array(mfi);
3612  auto pref_arr = lsm_fab_vars[LsmVar_SLM::pref]->const_array(mfi);
3613 
3614  auto LAI_arr = LAI.const_array(mfi);
3615  auto SAI_arr = SAI.const_array(mfi);
3616  auto t_cas_arr = t_cas.const_array(mfi);
3617  auto q_cas_arr = q_cas.const_array(mfi);
3618  auto ustar_arr = lsm_fab_vars[LsmVar_SLM::ustar]->const_array(mfi);
3619 
3620  auto poro_soil_arr = lsm_fab_vars[LsmVar_SLM::poro_soil]->const_array(mfi);
3621  auto theta_FC_arr = lsm_fab_vars[LsmVar_SLM::theta_FC]->const_array(mfi);
3622  auto theta_WP_arr = lsm_fab_vars[LsmVar_SLM::theta_WP]->const_array(mfi);
3623  auto soil_transp_frac_arr = lsm_fab_vars[LsmVar_SLM::soil_transp_frac]->array(mfi);
3624 
3625  auto ztop_arr = ztop.const_array(mfi);
3626  auto disp_hgt_arr = disp_hgt.const_array(mfi);
3627  auto z0_sfc_arr = z0_sfc.const_array(mfi);
3628  auto Rgl_arr = Rgl.const_array(mfi);
3629  auto Rc_min_arr = Rc_min.const_array(mfi);
3630  auto hs_rc_arr = hs_rc.const_array(mfi);
3631  auto nroot_arr = nroot.const_array(mfi);
3632  auto dleaf_arr = dleaf.const_array(mfi);
3633  auto zrefxy_arr = zrefxy.const_array(mfi);
3634  auto net_rad_arr = net_rad.const_array(mfi);
3635 
3636  auto ur_arr = lsm_fab_vars[LsmVar_SLM::uref]->const_array(mfi);
3637  auto vr_arr = lsm_fab_vars[LsmVar_SLM::vref]->const_array(mfi);
3638 
3639  auto r_a_arr = r_a.const_array(mfi);
3640  auto r_b_arr = r_b.array(mfi);
3641  auto r_c_arr = r_c.array(mfi);
3642  auto r_d_arr = r_d.array(mfi);
3643 
3644  // Get CWPVT parameter (canopy wind extinction factor) if available
3645  const amrex::Real* d_cwpvt = nullptr;
3646  if (d_veg_params.find("cwpvt") != d_veg_params.end()) {
3647  d_cwpvt = d_veg_params.at("cwpvt")->data();
3648  }
3649  const amrex::Real d_cwpvt_default = one; // default value if not from table
3650 
3651  ParallelFor( box, [=] AMREX_GPU_DEVICE (int i, int j, int)
3652  {
3653  if (landmask_arr(i, j, 0) != 1) {
3654  return;
3655  }
3656 
3657  for (int k = 0; k < d_nz_lsm; k++) {
3658  soil_transp_frac_arr(i, j, d_khi_lsm - k) = zero;
3659  }
3660 
3661  if (vegetype_arr(i, j, 0) == 0)
3662  {
3663  // for baresoil, r_d = r_a
3664  // under canopy resistance
3665  r_d_arr(i, j, 0) = r_a_arr(i, j, 0);
3666  }
3667  else
3668  {
3669  // for vegetated land surfaces
3670  amrex::Real rc_fac_rad, rc_fac_vpd, rc_fac_t, rc_fac_sw, d_root, tmp_radf;
3671 
3672  // ===================================================
3673  // Aerodynamic resistance for heat and vapor transfer under canopy space : r_d
3674  // ===================================================
3675  // NoahMP method: ResistanceLeafToGroundMod.F90
3676  // Reference: Niu et al. (2011), He et al. (2023)
3677 
3678  const amrex::Real MPE = 1.0e-6;
3679  const amrex::Real CONST_VON_KARMAN = 0.4;
3680  amrex::Real temp_diff = t_cas_arr(i, j, 0) - soilt_arr(i, j, d_khi_lsm);
3681 
3682  // Get CWPVT (canopy wind extinction parameter) for this vegetation type
3683  int veg_idx = landtype_arr(i, j, 0) - 1; // Convert the vegetation class to a 0-based table index
3684  amrex::Real CanopyWindExtFac = (d_cwpvt != nullptr) ? d_cwpvt[veg_idx] : d_cwpvt_default;
3685 
3686  // Stability correction for undercanopy (MoStabCorrShUndCan)
3687  amrex::Real MoStabCorrShUndCan = one; // Initialize to neutral
3688  // For now, use simple stability correction based on temperature difference
3689  if (temp_diff < zero) {
3690  // Unstable: (1 - 15*z/L)^(-0.25)
3691  amrex::Real zeta = std::min(amrex::Real(zero), amrex::Real(-0.1)); // Assume moderately unstable
3692  MoStabCorrShUndCan = std::pow(one - amrex::Real(15.0) * zeta, -fourth);
3693  } else {
3694  // Stable: 1 + 4.7*z/L
3695  amrex::Real zeta = std::min(amrex::Real(one), amrex::Real(0.1)); // Assume moderately stable
3696  MoStabCorrShUndCan = one + amrex::Real(4.7) * zeta;
3697  }
3698 
3699  // Wind extinction coefficient
3700  amrex::Real VegAreaIndEff = std::min(amrex::Real(6.0), LAI_arr(i, j, 0) + SAI_arr(i, j, 0));
3701  amrex::Real CanopyHeight = ztop_arr(i, j, 0);
3702  amrex::Real WindExtCoeffCanopy = std::sqrt(CanopyWindExtFac * VegAreaIndEff * CanopyHeight * MoStabCorrShUndCan);
3703 
3704  // Roughness lengths
3705  amrex::Real RoughLenShVegGrd = d_z0_soil; // Ground roughness for heat under canopy
3706  amrex::Real RoughLenShCanopy = z0_sfc_arr(i, j, 0); // Canopy roughness for heat
3707  amrex::Real ZeroPlaneDispSfc = disp_hgt_arr(i, j, 0); // Zero plane displacement
3708 
3709  // Exponential wind profile terms
3710  amrex::Real TMP1 = std::exp(-WindExtCoeffCanopy * RoughLenShVegGrd / CanopyHeight);
3711  amrex::Real TMP2 = std::exp(-WindExtCoeffCanopy * (RoughLenShCanopy + ZeroPlaneDispSfc) / CanopyHeight);
3712  amrex::Real TMPRAH2 = CanopyHeight * std::exp(WindExtCoeffCanopy) / WindExtCoeffCanopy * (TMP1 - TMP2);
3713 
3714  // Turbulent transfer coefficient KH
3715  amrex::Real KH = std::max(CONST_VON_KARMAN * ustar_arr(i, j, 0) * (CanopyHeight - ZeroPlaneDispSfc), MPE);
3716 
3717  // Undercanopy aerodynamic resistance
3718  r_d_arr(i, j, 0) = TMPRAH2 / KH;
3719 
3720  // Original SLM method (commented out for comparison)
3721  /*
3722  // temp_diff > 0 : stable undercanopy
3723  // temp_diff < 0 : unstable undercanopy
3724  amrex::Real temp_diff = t_cas_arr(i, j, 0) - soilt_arr(i, j, d_khi_lsm);
3725 
3726  // turbulent transfer coefficient under dense canopy
3727  if (temp_diff < zero)
3728  {
3729  Cs_dense = 0.004;
3730  }
3731  else
3732  {
3733  // rd_correc_fac : undercanopy stability parameter, in effect only for stable undercanopy
3734  amrex::Real rd_correc_fac = CONST_GRAV * ztop_arr(i, j, 0) * std::max(amrex::Real(zero), temp_diff) / soilt_arr(i, j, d_khi_lsm) / (std::pow(ustar_arr(i, j, 0), 2));
3735  // stable undercanopy - Cs_dense becomes smaller than 0.004
3736  Cs_dense = 0.004 / (one + myhalf * std::min(amrex::Real(10.0), rd_correc_fac));
3737  }
3738 
3739  // turbulent transfer coefficient over the exposed topsoil
3740  // typical value of Cs_bare ~0.2
3741  Cs_bare = 0.4 / 0.13 * std::pow((d_z0_soil * ustar_arr(i, j, 0) / (1.5e-5)), -0.45);
3742 
3743  // turbulence transfer coefficient undercanopy
3744  // LAI-weighed sum of Cs_bare and Cs_dense
3745  Cs = Cs_bare * std::exp(-1.0 * LAI_arr(i, j, 0)) + Cs_dense * (1.0 - std::exp(-1.0 * LAI_arr(i, j, 0)));
3746 
3747  // Undercanopy aerodynamic resistance depends on the weighed sum of the dense canopy covered soil
3748  // and baresoil turbulent transfer coefficient and friction velocity
3749  // Reference: [Oleson et al., 2004] [Zeng et al., 2005]
3750  r_d_arr(i, j, 0) = std::min(amrex::Real(400.0), 1.0 / ustar_arr(i, j, 0) / Cs); // prevent r_d from getting too large under stable condition
3751  */
3752 
3753  // ===================================================
3754  // Leaf boundary layer resistance : r_b
3755  // ===================================================
3756  // Original SLM formula:
3757  // r_b_arr(i, j, 0) = 0.5 * r_a_arr(i, j, 0);
3758 
3759  // NoahMP leaf boundary resistance from ResistanceLeafToGroundMod.F90.
3760  const amrex::Real wind_ref = std::max(
3761  std::sqrt(ur_arr(i, j, 0) * ur_arr(i, j, 0)
3762  + vr_arr(i, j, 0) * vr_arr(i, j, 0)), MPE);
3763  const amrex::Real wind_canopy_top = std::max(
3764  wind_ref
3765  * std::log((CanopyHeight - ZeroPlaneDispSfc + RoughLenShCanopy)
3766  / RoughLenShCanopy)
3767  / std::log(zrefxy_arr(i, j, 0) / RoughLenShCanopy),
3768  MPE);
3769  const amrex::Real rb_factor = WindExtCoeffCanopy * 50.0
3770  / std::max(one - std::exp(-WindExtCoeffCanopy / two), MPE);
3771  r_b_arr(i, j, 0) = std::min(amrex::Real(50.0), std::max(amrex::Real(5.0),
3772  rb_factor * std::sqrt(dleaf_arr(i, j, 0) / wind_canopy_top)));
3773 
3774  // Original SLM Method 1 (commented out for reference)
3775  // turbulent transfer coefficient between canopy surface and canopy air : Cv = 0.01m/s^-0.5
3776  // characteristic dimension of the leaves in the direction of wind flux : d_leaf = 0.04m
3777  // r_b_arr(i, j, 0) = 1.0 / 0.01 * std::pow( ustar_arr(i, j, 0) / 0.04, -0.5) / std::max(0.1, LAI_arr(i, j, 0));
3778  // Above equation seems to overestimate LHF
3779 
3780  // ===================================================
3781  // Stomatal resistance : r_c
3782  // ===================================================
3783  // radiation factor (Noah/NoahMP Jarvis bulk formulation)
3784  // Reference: Noah LSM CANRES subroutine, Chen et al. (1996, 2001)
3785  amrex::Real RadFac = 0.55 * net_rad_arr(i, j, 0, SLM_NetRad::net_swdn1) * two / Rgl_arr(i, j, 0) / LAI_arr(i, j, 0);
3786  rc_fac_rad = (Rc_min_arr(i, j, 0) / d_Rc_max + RadFac) / (one + RadFac);
3787  rc_fac_rad = std::max(rc_fac_rad, amrex::Real(0.0001));
3788 
3789  /* Original SLM sunlit/shaded approach (commented out to match Noah bulk approach)
3790  // TODO: check if this is the correct downwelling SW to use
3791  //amrex::Real tmp_radf = 0.55 * net_rad_arr(i, j, 0, SLM_NetRad::net_swdn1) * 2.0 / Rgl_arr(i, j, 0) / LAI_arr(i, j, 0);
3792  //rc_fac_rad = (Rc_min_arr(i, j, 0) / d_Rc_max + tmp_radf) / (1.0 + tmp_radf);
3793  // modify above to account for the shaded and sunlit part of leaves
3794  k_beer = phi_1_arr(i, j, 0) + phi_2_arr(i, j, 0); // extinction coef
3795  f_shade = 0.1 ; // fraction or radiation reaching shaded leaves (empirical)
3796 
3797  // partition LAI into sunlit and shaded components
3798  lai_sun = std::max(amrex::Real(1.e-6), (one - std::exp(-one * k_beer * LAI_arr(i, j, 0))) / k_beer);
3799  lai_shade = std::max(amrex::Real(zero), LAI_arr(i, j, 0) - lai_sun);
3800 
3801  // Radiation reaching to sunlit and shaded leaves
3802  sw_sun = net_rad_arr(i, j, 0, SLM_NetRad::net_swdn1);
3803  sw_shade = f_shade * sw_sun;
3804 
3805  // compute radiation factors (NoahMP Jarvis form - no LAI division)
3806  tmp_sun = 0.55 * sw_sun * 2. / Rgl_arr(i, j, 0);
3807  tmp_shade = 0.55 * sw_shade * 2. / Rgl_arr(i, j, 0);
3808  rc_fac_sun = (Rc_min_arr(i,j,0) / d_Rc_max + tmp_sun) / (one + tmp_sun);
3809  rc_fac_shade = (Rc_min_arr(i,j,0) / d_Rc_max + tmp_shade) / (one + tmp_shade);
3810 
3811  // combine weighted by LAI
3812  rc_fac_rad = (lai_sun * rc_fac_sun + lai_shade * rc_fac_shade) / LAI_arr(i, j, 0);
3813  *///
3814 
3815 
3816  // vapor pressure deficit factor (Noah/NoahMP Jarvis formulation)
3817  // Noah uses VPD in mixing ratio units (kg/kg), not vapor pressure (hPa)
3818  // Reference: Noah LSM CANRES subroutine, Chen et al. (1996, 2001)
3820  erf_qsatw(t_cas_arr(i, j, 0), pref_arr(i, j, 0), qsatw);
3821  amrex::Real VPD_mixratio = qsatw - q_cas_arr(i, j, 0); // VPD in mixing ratio [kg/kg]
3822  rc_fac_vpd = one / (one + hs_rc_arr(i, j, 0) * VPD_mixratio);
3823  rc_fac_vpd = std::max(rc_fac_vpd, amrex::Real(0.01));
3824 
3825  /* Original SLM VPD calculation using vapor pressure in hPa (commented out)
3826  // Above is modified following changes in gSAM-SLM
3827  amrex::Real e_cas, es_cas;
3828  e_cas = q_cas_arr(i, j, 0) * pref_arr(i, j ,0)/(0.622+0.388*q_cas_arr(i, j, 0)); //vapor pressure in hPa (mb)
3829  es_cas = erf_esatw(t_cas_arr(i, j, 0));
3830  // NoahMP Jarvis form: hyperbolic response to VPD
3831  rc_fac_vpd = one / (one + hs_rc_arr(i, j, 0) * (es_cas - e_cas));
3832  *///
3833 
3834  // temperature factor
3835  rc_fac_t = std::max(amrex::Real(zero), one - amrex::Real(0.0016) * amrex::Math::powi<2>(d_T_opt - t_cas_arr(i, j, 0)));
3836 
3837  // NoahMP BTR_OPTION=1 soil-moisture stress factor. All rooted
3838  // layers remain in the depth denominator, including dry layers.
3839  const int nroot_cell = nroot_arr(i, j, 0);
3840  d_root = zero;
3841  for (int k = 0; k < nroot_cell; k++) {
3842  const int lsm_k = d_khi_lsm - k;
3843  d_root += s_depth_arr(i, j, lsm_k);
3844  }
3845 
3846  rc_fac_sw = zero;
3847  amrex::Real transp_frac_sum = zero;
3848  for (int k = 0; k < nroot_cell; k++) {
3849  const int lsm_k = d_khi_lsm - k;
3850  const amrex::Real theta_liq = soilw_arr(i, j, lsm_k) * poro_soil_arr(i, j, lsm_k);
3851  const amrex::Real moisture_range = theta_FC_arr(i, j, lsm_k) - theta_WP_arr(i, j, lsm_k);
3852  const amrex::Real soil_wet_fac = std::min(amrex::Real(one), std::max(amrex::Real(zero),
3853  (theta_liq - theta_WP_arr(i, j, lsm_k)) / moisture_range));
3854  const amrex::Real transp_frac =
3855  s_depth_arr(i, j, lsm_k) / std::max(amrex::Real(1.0e-6), d_root) * soil_wet_fac;
3856  soil_transp_frac_arr(i, j, lsm_k) = transp_frac;
3857  transp_frac_sum += transp_frac;
3858  rc_fac_sw += std::max(amrex::Real(1.0e-6), transp_frac);
3859  }
3860 
3861  rc_fac_sw = std::max(amrex::Real(1.0e-6), rc_fac_sw);
3862  for (int k = 0; k < nroot_cell; k++) {
3863  const int lsm_k = d_khi_lsm - k;
3864  if (transp_frac_sum > zero) {
3865  soil_transp_frac_arr(i, j, lsm_k) /= transp_frac_sum;
3866  }
3867  }
3868 
3869  tmp_radf = std::max(amrex::Real(1.0e-6), rc_fac_rad*rc_fac_vpd*rc_fac_t*rc_fac_sw);
3870  // Leaf-level stomatal resistance. LAI is applied once when this
3871  // resistance is converted to canopy transpiration conductance.
3872  r_c_arr(i, j, 0) = std::min(d_Rc_max, Rc_min_arr(i, j, 0) / tmp_radf);
3873 
3874  // ===================================================
3875  // r_litter : litter resistance - not used in this version
3876  // ===================================================
3877  //r_litter_arr(i, j, 0) = 0.0;
3878  // Ref. [Sakaguchi and Zeng 2009]
3879  // Set litter LAI as 0.5
3880  // r_litter_arr(i, j, 0) = 1.0 / 0.004 / ustar_arr(i, j, 0) * (1.0 - std::exp(-0.5));
3881  }
3882  });
3883 }
@ qsatw
Definition: ERF_WSM6.H:361
Here is the call graph for this function:

◆ set_dt()

void SLM::set_dt ( const amrex::Real &  dt)
inline
658  {
659  m_dt = dt;
660  }

◆ set_precip_input()

void SLM::set_precip_input ( const amrex::MultiFab *  precip_in)
overridevirtual

Reimplemented from NullSurf.

4977 {
4978  int khi = khi_lsm;
4979 
4981 
4982  for ( MFIter mfi(*tsurf, TileNoZ()); mfi.isValid(); ++mfi) {
4983  const auto& box3d = mfi.tilebox();
4984 
4985  // Create a box with the same i,j bounds, but only at z = 0
4986  amrex::Box b2d = box3d;
4987  b2d.setRange(2, 0);
4988 
4989  auto precip_array = precip_in->const_array(mfi);
4990 
4991  auto slm_precip = lsm_fab_vars[LsmVar_SLM::precipref]->array(mfi);
4992 
4993  ParallelFor(b2d, [=] AMREX_GPU_DEVICE (int i, int j, int k)
4994  {
4995  slm_precip(i, j, k) = precip_array(i, j, k, 0);
4996 
4997  // TODO: this is for plotting purposes.. state arrays are at k=0 which is ghost cell for SLM values
4998  // SLM AMREX plotfile does not write ghost cells, but NetCDF does - fix?
4999  slm_precip(i, j, khi) = slm_precip(i, j, 0);
5000  });
5001  }
5002 }
@ precip_in
Definition: ERF_SLM.H:134
Here is the call graph for this function:

◆ set_terrain_inputs()

void SLM::set_terrain_inputs ( const amrex::Vector< std::unique_ptr< amrex::MultiFab >> &  sst_in,
const amrex::Vector< std::unique_ptr< amrex::iMultiFab >> &  lmask_in 
)
overridevirtual

Reimplemented from NullSurf.

5007 {
5008  if (!first_step)
5009  {
5010  return;
5011  }
5013  const int d_khi_lsm = khi_lsm;
5014 
5015  if (lmask_in[0]) {
5016  for ( MFIter mfi(*tsurf, TileNoZ()); mfi.isValid(); ++mfi) {
5017  const auto& box3d = mfi.tilebox();
5018 
5019  // Create a box with the same i,j bounds, but only at z = 0
5020  amrex::Box b2d = box3d;
5021  b2d.setRange(2, 0);
5022 
5023  auto lmask_array = lmask_in[0]->array(mfi);
5024  auto slm_lmask = landmask.array(mfi);
5025  ParallelFor(b2d, [=] AMREX_GPU_DEVICE (int i, int j, int k)
5026  {
5027  slm_lmask(i, j, k) = lmask_array(i, j, k, 0);
5028  });
5029  }
5030  } else {
5031  landmask.setVal(1);
5032  }
5033 
5034  if (!use_wrfinput) {
5035  slm_init();
5036  }
5037 
5038  if (sst_in[0]) {
5039  // Set SLM surface temperature input from ERF.
5040  for ( MFIter mfi(*tsurf, TileNoZ()); mfi.isValid(); ++mfi) {
5041  const auto& box3d = mfi.tilebox();
5042 
5043  // Create a box with the same i,j bounds, but only at z = 0
5044  amrex::Box b2d = box3d;
5045  b2d.setRange(2, 0);
5046 
5047  auto sst_array = sst_in[0]->array(mfi);
5048  auto slm_sst = sstxy.array(mfi);
5049  auto slm_tsk = lsm_fab_vars[LsmVar_SLM::tsurf]->array(mfi);
5050  auto slm_tskin = t_skin.array(mfi);
5051  const bool d_use_wrfinput = use_wrfinput;
5052  ParallelFor(b2d, [=] AMREX_GPU_DEVICE (int i, int j, int k)
5053  {
5054  const amrex::Real tsk = d_use_wrfinput
5055  ? slm_tsk(i, j, d_khi_lsm)
5056  : sst_array(i, j, k, 0);
5057  slm_sst(i, j, k) = tsk;
5058  slm_tsk(i, j, k) = tsk;
5059  slm_tskin(i, j, k) = tsk;
5060  });
5061  }
5062  } else if (!use_wrfinput) {
5063  // If no terrain is setup, initialize SST to reference temperature.
5064  lsm_fab_vars[LsmVar_SLM::tsurf]->setVal(st0[0]);
5065  }
5066 }
Here is the call graph for this function:

◆ slm_init()

void SLM::slm_init ( )

Initialize SLM

940 {
941  // validate the landtype flag
942  const int landtype_min = landtype.min(0);
943  const int landtype_max = landtype.max(0);
944  if (landtype_min < 0 || landtype_max > 16)
945  {
946  amrex::Abort("SLM: landtype values are outside of valid 0-16 range! min = " +
947  std::to_string(landtype_min) + ", max = " + std::to_string(landtype_max));
948  }
949 
950  // validate LAI - check that they are within [0,10]
951  const double lai_min = LAI.min(0);
952  const double lai_max = LAI.max(0);
953  if (lai_min < zero || lai_max > 10.0)
954  {
955  amrex::Abort("SLM: LAI values are outside of valid [0-10] range! min = " +
956  std::to_string(lai_min) + ", max = " + std::to_string(lai_max));
957  }
958 
959  if (use_param_file && landtype_max > num_veg_params)
960  {
961  amrex::Abort("SLM: vegetation category " + std::to_string(landtype_max) +
962  " is outside the selected parameter table range [1, " +
963  std::to_string(num_veg_params) + "]");
964  }
965  if (interpolate_lai && landtype_max > num_landtypes)
966  {
967  amrex::Abort("SLM: vegetation category " + std::to_string(landtype_max) +
968  " is outside the LAI/SAI table range [1, " +
969  std::to_string(num_landtypes) + "]");
970  }
971  if (use_param_file) {
973  const int soiltype_min = static_cast<int>(soiltype.min(0));
974  const int soiltype_max = static_cast<int>(soiltype.max(0));
975  if (soiltype_min < 0 || soiltype_max > num_soil_params)
976  {
977  amrex::Abort("SLM: soil categories are outside the selected parameter table range [1, " +
978  std::to_string(num_soil_params) + "]! min = " +
979  std::to_string(soiltype_min) + ", max = " +
980  std::to_string(soiltype_max));
981  }
982  }
983 
984  // Sets model properties based on the landtype
985  init_landtype();
986 
987  // TODO: VEGYES and set minimum LAI
988  vege_YES.setVal(one);
989 
990  for ( MFIter mfi(landtype, TileNoZ()); mfi.isValid(); ++mfi) {
991  const auto& box = mfi.tilebox();
992 
993  auto vege_YES_arr = vege_YES.array(mfi);
994  auto vegetype_arr = vegetype.const_array(mfi);
995  auto nroot_arr = nroot.array(mfi);
996  auto landtype_arr = landtype.const_array(mfi);
997 
998  auto IR_emis_vege_arr = IR_emis_vege.array(mfi);
999  auto IR_emis_soil_arr = IR_emis_soil.array(mfi);
1000  auto IR_emis_grnd_arr = IR_emis_grnd.array(mfi);
1001  auto phi_1_arr = phi_1.array(mfi);
1002  auto phi_2_arr = phi_2.array(mfi);
1003  auto precip_extinc_arr = precip_extinc.array(mfi);
1004  auto mw_mx_arr = mw_mx.array(mfi);
1005  auto LAI_arr = LAI.array(mfi);
1006  auto BAI_arr = BAI.const_array(mfi);
1007  auto IMPERV_arr = IMPERV.const_array(mfi);
1008  auto ztop_arr = ztop.const_array(mfi);
1009  auto Khai_L_arr = Khai_L.array(mfi);
1010  auto landmask_arr = landmask.const_array(mfi);
1011 
1012  auto veg_frac_arr = lsm_fab_vars[LsmVar_SLM::veg_frac]->array(mfi);
1013 
1014  ParallelFor(box, [=] AMREX_GPU_DEVICE (int i, int j, int)
1015  {
1016  if (landmask_arr(i, j, 0) == 1) {
1017  if (vegetype_arr(i, j, 0) == 0) {
1018  vege_YES_arr(i, j, 0) = zero;
1019  veg_frac_arr(i, j, 0) = zero;
1020  nroot_arr(i, j, 0) = 0;
1021  } else {
1022  // set minimum LAI for vegetated land
1023  LAI_arr(i, j, 0) = std::max(LAI_arr(i, j, 0), amrex::Real(0.001));
1024  }
1025 
1026  phi_1_arr(i, j, 0) = myhalf - 0.633 * Khai_L_arr(i, j, 0) - 0.33 * amrex::Math::powi<2>(Khai_L_arr(i, j, 0));
1027  phi_2_arr(i, j, 0) = 0.877 * (one - two * phi_1_arr(i, j, 0));
1028  IR_emis_vege_arr(i, j, 0) = 0.97 * (one - std::exp(-(phi_1_arr(i, j, 0) + phi_2_arr(i, j, 0)) * LAI_arr(i, j, 0)));
1029  precip_extinc_arr(i, j, 0) = phi_1_arr(i, j, 0) + phi_2_arr(i, j, 0);
1030  //mw_mx_arr(i, j, 0) = 0.1 * LAI_arr(i, j, 0);
1031  //add the basal area to mw_mx. circumference of trunks per unit area = sqrt(4*PI*basal area), which then is converted from sq.feet/sq acre to m2/m2 by diving by 43560
1032  mw_mx_arr(i, j, 0) = 0.1 * LAI_arr(i, j, 0) + ztop_arr(i, j, 0) * std::pow(four * PI * BAI_arr(i, j, 0) / amrex::Real(43560.0), myhalf);
1033 
1034  if(landtype_arr(i, j, 0) == 13)
1035  {
1036  IR_emis_grnd_arr(i, j, 0) = IR_emis_urban * IMPERV_arr(i, j, 0) + IR_emis_soil_arr(i, j, 0) * (1.-IMPERV_arr(i, j, 0)); // urban IR emissivity
1037  }
1038  else
1039  {
1040  IR_emis_grnd_arr(i, j, 0) = IR_emis_soil_arr(i, j, 0);
1041  }
1042  } else {
1043  vege_YES_arr(i, j, 0) = zero;
1044  veg_frac_arr(i, j, 0) = zero;
1045  }
1046  });
1047  }
1048 
1049  // Initialize soil parameters
1050  init_soil_tw();
1051 
1052  init_soil_vars();
1053 
1054  // Update any parameters using the values from a parameter file
1055  init_from_params();
1056 }
constexpr amrex::Real four
Definition: ERF_NumericalConstants.H:40
constexpr amrex::Real PI
Definition: ERF_NumericalConstants.H:46
void init_soil_tw()
Definition: ERF_SLM.cpp:1802
int num_soil_params
Definition: ERF_SLM.H:890
void init_landtype()
Definition: ERF_SLM.cpp:1483
int num_veg_params
Definition: ERF_SLM.H:889
Here is the call graph for this function:

◆ snow_age_noahmp()

AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void SLM::snow_age_noahmp ( amrex::Real  dt,
amrex::Real  tg,
amrex::Real  sneqvo,
amrex::Real  sneqv,
amrex::Real  tau0,
amrex::Real  grain_growth,
amrex::Real  extra_growth,
amrex::Real  dirt_soot,
amrex::Real  swemx,
amrex::Real &  tauss,
amrex::Real &  fage 
)
static
5263 {
5264  //input
5265  // DT !main time step (s)
5266  // TG !ground temperature (k)
5267  // SNEQVO !snow mass at last time step(mm)
5268  // SNEQV !snow water per unit ground area (mm)
5269 
5270  //output
5271  // FAGE !snow age
5272 
5273  //input/output
5274  // TAUSS !non-dimensional snow age
5275 
5276  //local
5277  amrex::Real tage; //total aging effects
5278  amrex::Real age1; //effects of grain growth due to vapor diffusion
5279  amrex::Real age2; //effects of grain growth at freezing of melt water
5280  amrex::Real age3; //effects of soot
5281  amrex::Real dela; //temporary variable
5282  amrex::Real sge; //temporary variable
5283  amrex::Real dels; //temporary variable
5284  amrex::Real dela0; //temporary variable
5285  amrex::Real arg; //temporary variable
5286  // See Yang et al. (1997) J.of Climate for detail.
5287 
5288  constexpr amrex::Real TFRZ = 273.16; // freezing/melting point (k)
5289 
5290  if(sneqv <= zero) {
5291  tauss = zero;
5292  } else {
5293  dela0 = dt/tau0;
5294  arg = grain_growth*(1.0/TFRZ-1.0/tg);
5295  age1 = std::exp(arg);
5296  age2 = std::exp(std::min(amrex::Real(zero), extra_growth*arg));
5297  age3 = dirt_soot;
5298  tage = age1+age2+age3;
5299  dela = dela0*tage;
5300  dels = std::max(amrex::Real(zero), sneqv-sneqvo) / swemx;
5301  sge = (tauss+dela)*(1.0-dels);
5302  tauss = std::max(amrex::Real(zero),sge);
5303  }
5304 
5305  fage= tauss/(tauss+1.0);
5306 }

◆ snowalb_bats_noahmp()

AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void SLM::snowalb_bats_noahmp ( int  nband,
amrex::Real  fsno,
amrex::Real  cosz,
amrex::Real  fage,
amrex::Real  bats_cosz,
amrex::Real  bats_vis_new,
amrex::Real  bats_nir_new,
amrex::Real  bats_vis_age,
amrex::Real  bats_nir_age,
amrex::Real  bats_vis_dir,
amrex::Real  bats_nir_dir,
amrex::Real *  albsnd,
amrex::Real *  albsni 
)
static
5317 {
5318  // --------------------------------------------------------------------------------------------------
5319  // input
5320  // NBAND !number of waveband classes
5321  // COSZ !cosine solar zenith angle
5322  // FSNO !snow cover fraction (-)
5323  // FAGE !snow age correction
5324 
5325  // output
5326  // ALBSND !snow albedo for direct(1=vis, 2=nir)
5327  // ALBSNI !snow albedo for diffuse
5328  // ---------------------------------------------------------------------------------------------
5329 
5330  // ------------------------ local variables ----------------------------------------------------
5331  amrex::Real fzen; //zenith angle correction
5332  amrex::Real cf1; //temporary variable
5333  amrex::Real sl2; //2.*SL
5334  amrex::Real sl1; //1/SL
5335  amrex::Real sl; //adjustable parameter
5336  // REAL, PARAMETER :: C1 = 0.2 !default in BATS
5337  // REAL, PARAMETER :: C2 = 0.5 !default in BATS
5338  // REAL, PARAMETER :: C1 = 0.2 * 2. ! double the default to match Sleepers River's
5339  // REAL, PARAMETER :: C2 = 0.5 * 2. ! snow surface albedo (double aging effects)
5340  // ---------------------------------------------------------------------------------------------
5341  // zero albedos for all points
5342 
5343  albsnd[0] = zero;
5344  albsnd[1] = zero;
5345  albsni[0] = zero;
5346  albsni[1] = zero;
5347 
5348  // when cosz > 0
5349 
5350  sl=bats_cosz;
5351  sl1=1.0/sl;
5352  sl2=two*sl;
5353  cf1=((1.0+sl1)/(1.0+sl2*cosz)-sl1);
5354  fzen=std::max(cf1,amrex::Real(zero));
5355 
5356  albsni[0]=bats_vis_new*(1.0-bats_vis_age*fage);
5357  albsni[1]=bats_nir_new*(1.0-bats_nir_age*fage);
5358 
5359  albsnd[0]=albsni[0]+bats_vis_dir*fzen*(1.0-albsni[0]); // vis direct
5360  albsnd[1]=albsni[1]+bats_nir_dir*fzen*(1.0-albsni[1]); // nir direct
5361 }

◆ soil_nudging()

void SLM::soil_nudging ( const amrex::MFIter &  mfi3d)
4769 {
4770  if (!dosoiltnudging && !dosoilwnudging) return;
4771 
4772  const Real dt = m_dt;
4773 
4774  auto box = mfi3d.tilebox();
4775 
4776  auto landmask_arr = landmask.const_array(mfi3d);
4777 
4778  auto soilt_arr = lsm_fab_vars[LsmVar_SLM::soilt]->array(mfi3d);
4779  auto soilw_arr = lsm_fab_vars[LsmVar_SLM::soilw]->array(mfi3d);
4780 
4781  auto soilt_nudge_arr = lsm_fab_vars[LsmVar_SLM::soilt_nudge]->array(mfi3d);
4782  auto soilw_nudge_arr = lsm_fab_vars[LsmVar_SLM::soilw_nudge]->array(mfi3d);
4783  auto soil_relax_hgt_arr = lsm_fab_vars[LsmVar_SLM::soil_relax_hgt]->const_array(mfi3d);
4784 
4785  auto soilt_obs_arr = lsm_fab_vars[LsmVar_SLM::soilt_obs]->const_array(mfi3d);
4786  auto soilw_obs_arr = lsm_fab_vars[LsmVar_SLM::soilw_obs]->const_array(mfi3d);
4787 
4788  const Real d_tau = tausoil;
4789  if (dosoiltnudging)
4790  {
4791  ParallelFor( box, [=] AMREX_GPU_DEVICE (int i, int j, int k)
4792  {
4793  if (landmask_arr(i, j, 0) != 1) {
4794  return;
4795  }
4796 
4797  soilt_arr(i, j, k) -= (soilt_arr(i, j, k) - soilt_obs_arr(i, j, k))*dt/d_tau*soil_relax_hgt_arr(i, j, k);
4798  soilt_nudge_arr(i, j, k) = (soilt_arr(i, j, k) - soilt_obs_arr(i, j, k))*soil_relax_hgt_arr(i, j, k) / d_tau;
4799  });
4800  }
4801 
4802  if (dosoilwnudging)
4803  {
4804  ParallelFor( box, [=] AMREX_GPU_DEVICE (int i, int j, int k)
4805  {
4806  if (landmask_arr(i, j, 0) != 1) {
4807  return;
4808  }
4809 
4810  soilw_arr(i, j, k) -= (soilw_arr(i, j, k) - soilw_obs_arr(i, j, k))*dt/d_tau*soil_relax_hgt_arr(i, j, k);
4811  soilw_nudge_arr(i, j, k) = (soilw_arr(i, j, k) - soilw_obs_arr(i, j, k))*soil_relax_hgt_arr(i, j, k) / d_tau;
4812  });
4813  }
4814 }
Here is the call graph for this function:

◆ soil_temperature()

void SLM::soil_temperature ( const amrex::MFIter &  mfi)
4606 {
4607  const int d_khi_lsm = khi_lsm;
4608  const int d_klo_lsm = klo_lsm;
4609  const int d_nz_lsm = m_nz_lsm;
4610  const Real dt = m_dt;
4611 
4612  auto box = mfi.tilebox();
4613 
4614  auto landmask_arr = landmask.const_array(mfi);
4615  auto landtype_arr = landtype.const_array(mfi);
4616 
4617  auto soilt_arr = lsm_fab_vars[LsmVar_SLM::soilt]->array(mfi);
4618  auto soilw_arr = lsm_fab_vars[LsmVar_SLM::soilw]->const_array(mfi);
4619 
4620  auto sst_capa_arr = lsm_fab_vars[LsmVar_SLM::sst_capa]->const_array(mfi);
4621  auto sst_cond_arr = lsm_fab_vars[LsmVar_SLM::sst_cond]->const_array(mfi);
4622  auto poro_soil_arr = lsm_fab_vars[LsmVar_SLM::poro_soil]->const_array(mfi);
4623 
4624  auto s_depth_arr = lsm_fab_vars[LsmVar_SLM::s_depth]->const_array(mfi);
4625 
4626  auto slm_diag_arr = slm_diag.array(mfi);
4627 
4628  auto dst_vars = soilt_vars.array(mfi);
4629 
4630  auto mws_arr = mws.array(mfi);
4631 
4632  // TODO: Refactor this whole loop for GPU
4633  ParallelFor( box, [=] AMREX_GPU_DEVICE (int i, int j, int)
4634  {
4635  if (landmask_arr(i, j, 0) != 1) {
4636  return;
4637  }
4638 
4639  amrex::Real temp, k_dry, k_sat, Ke;
4640 
4641  amrex::Real grflux0 = slm_diag_arr(i, j, 0, SLM_Diag::grflux);
4642  slm_diag_arr(i, j, 0, SLM_Diag::grflux) = grflux0;
4643  if (landtype_arr(i, j, 0) == 15) {
4644  // ice
4645  for (int k = 0; k < d_nz_lsm; k++) {
4646  const int lsm_k = d_khi_lsm - k;
4647  dst_vars(i, j, lsm_k, SLM_DST::st_cond) = 1.6; // thermal conductivity of ice
4648  dst_vars(i, j, lsm_k, SLM_DST::st_capa) = 917.0 * 2030.0; // ice heat capacity
4649  }
4650  } else {
4651  for (int k = 0; k < d_nz_lsm; k++) {
4652  const int lsm_k = d_khi_lsm - k;
4653 
4654  temp = (1.0 - poro_soil_arr(i, j, lsm_k)) * 2700.0;
4655 
4656  // dry thermal conductivity [W/mK]
4657  k_dry = (0.135 * temp + 64.7) / (2700.0 - 0.947*temp);
4658 
4659  // saturated soil thermal conductivity
4660  k_sat = std::pow(sst_cond_arr(i, j, lsm_k), one - poro_soil_arr(i, j, lsm_k));
4661  if (soilt_arr(i, j, lsm_k) > tfriz) {
4662  k_sat *= std::pow(amrex::Real(0.57), poro_soil_arr(i, j, lsm_k)); // 0.57 = cond_water
4663  }
4664  else {
4665  k_sat *= std::pow(amrex::Real(1.60), poro_soil_arr(i, j, lsm_k)); // 1.6 = cond_ice
4666  }
4667 
4668  // Weighing factor between dry and saturated soil thermal conductivity
4669  Ke = std::log10(std::max(amrex::Real(0.1), soilw_arr(i, j, lsm_k))) + one;
4670 
4671  // Total soil thermal conductivity at each node_z
4672  dst_vars(i, j, lsm_k, SLM_DST::st_cond) = Ke * (k_sat - k_dry) + k_dry;
4673 
4674  // Soil volumetric heaet capacity at each node_z depth
4675  if (soilt_arr(i, j, lsm_k) > tfriz) {
4676  dst_vars(i, j, lsm_k, SLM_DST::st_capa) =
4677  (1.0 - poro_soil_arr(i, j, lsm_k)) *
4678  sst_capa_arr(i, j, lsm_k) +
4679  (998.0 * 4182.0) * soilw_arr(i, j, lsm_k) *
4680  poro_soil_arr(i, j, lsm_k); // water heat capacity
4681  } else {
4682  dst_vars(i, j, lsm_k, SLM_DST::st_capa) =
4683  (1.0 - poro_soil_arr(i, j, lsm_k)) *
4684  sst_capa_arr(i, j, lsm_k) +
4685  (917.0 * 2030.0) * soilw_arr(i, j, lsm_k) *
4686  poro_soil_arr(i, j, lsm_k); // ice heat capacity
4687  }
4688  }
4689  }
4690 
4691  for (int k = 0; k < d_nz_lsm - 1; k++) {
4692  const int lsm_k = d_khi_lsm - k;
4693 
4694  // calculate effective conductivity at the adjacent soil layer interface
4695  dst_vars(i, j, lsm_k, SLM_DST::st_eff_cond) =
4696  (dst_vars(i, j, lsm_k - 1, SLM_DST::st_cond) * s_depth_arr(i, j, lsm_k - 1) +
4697  dst_vars(i, j, lsm_k, SLM_DST::st_cond) * s_depth_arr(i, j, lsm_k)) /
4698  (s_depth_arr(i, j, lsm_k - 1) + s_depth_arr(i, j, lsm_k));
4699  }
4700 
4701  // from FDE by the implicit, Thomas algorithm is applied
4702  // aa: terms related with T(k-1)
4703  // bb: terms related with T(k)
4704  // cc: terms related with T(k+1)
4705  // dd: current soil temperature + (additional source/sink on soil top)
4706  // For first layer:
4707  amrex::Real aa = zero;
4708  amrex::Real cc =
4709  -two * dst_vars(i, j, d_khi_lsm, SLM_DST::st_eff_cond) * dt /
4710  dst_vars(i, j, d_khi_lsm, SLM_DST::st_capa) /
4711  (s_depth_arr(i, j, d_khi_lsm) *
4712  (s_depth_arr(i, j, d_khi_lsm) + s_depth_arr(i, j, d_khi_lsm - 1)));
4713  amrex::Real bb = 1.0 - cc;
4714  amrex::Real dd = soilt_arr(i, j, d_khi_lsm) - grflux0 * dt / s_depth_arr(i, j, d_khi_lsm) / dst_vars(i, j, d_khi_lsm, SLM_DST::st_capa);
4715 
4716  dst_vars(i, j, d_khi_lsm, SLM_DST::alpha) = cc / bb;
4717  dst_vars(i, j, d_khi_lsm, SLM_DST::beta) = dd / bb;
4718 
4719  // For 2-(nsoil-1) layer:
4720  for (int k = 1; k < d_nz_lsm - 1; k++) {
4721  // NOTE: SLM k-1 indices here are switched from original SAM
4722  // version because our soil level indices are from -1 (top) to
4723  // -nsoil (bottom)
4724  // Index mapping:
4725  // SAM SLM -> ERF SLM
4726  // 1 -> d_khi_lsm
4727  // k - 1 -> k + 1
4728  // k + 1 -> k - 1
4729  // nsoil -> d_klo_lsm
4730  const int lsm_k = d_khi_lsm - k;
4731 
4732  const amrex::Real st_eff_cond_up = dst_vars(i, j, lsm_k + 1, SLM_DST::st_eff_cond);
4733  const amrex::Real st_eff_cond = dst_vars(i, j, lsm_k , SLM_DST::st_eff_cond);
4734  const amrex::Real st_capa = dst_vars(i, j, lsm_k, SLM_DST::st_capa);
4735 
4736  aa = -two * st_eff_cond_up * dt / s_depth_arr(i, j, lsm_k) / st_capa / (s_depth_arr(i, j, lsm_k+1) + s_depth_arr(i, j, lsm_k));
4737  cc = -two * st_eff_cond * dt / s_depth_arr(i, j, lsm_k) / st_capa / (s_depth_arr(i, j, lsm_k) + s_depth_arr(i, j, lsm_k-1));
4738  bb = 1.0 - cc - aa;
4739  dd = soilt_arr(i, j, lsm_k); // current time step
4740 
4741  dst_vars(i, j, lsm_k, SLM_DST::alpha) = cc / (bb - aa * dst_vars(i, j, lsm_k + 1, SLM_DST::alpha));
4742  dst_vars(i, j, lsm_k, SLM_DST::beta) = (dd - aa * dst_vars(i, j, lsm_k + 1, SLM_DST::beta)) / (bb - aa * dst_vars(i, j, lsm_k + 1, SLM_DST::alpha));
4743  }
4744 
4745  // For bottom layer:
4746  aa = -two * dst_vars(i, j, d_klo_lsm + 1, SLM_DST::st_eff_cond) * dt / s_depth_arr(i, j, d_klo_lsm) / dst_vars(i, j, d_klo_lsm, SLM_DST::st_capa) / (s_depth_arr(i, j, d_klo_lsm + 1) + s_depth_arr(i, j, d_klo_lsm));
4747  cc = zero;
4748  bb = 1.0 - aa;
4749  dd = soilt_arr(i, j, d_klo_lsm);
4750 
4751  dst_vars(i, j, d_klo_lsm, SLM_DST::alpha) = zero;
4752  dst_vars(i, j, d_klo_lsm, SLM_DST::beta) = (dd - aa * dst_vars(i, j, d_klo_lsm + 1, SLM_DST::beta)) / (bb - aa * dst_vars(i, j, d_klo_lsm + 1, SLM_DST::alpha));
4753 
4754  // (n+1) time step soil temperature:
4755  soilt_arr(i, j, d_klo_lsm) = dst_vars(i, j, d_klo_lsm, SLM_DST::beta);
4756  for (int k = d_nz_lsm - 2; k >= 0; k--) {
4757  const int lsm_k = d_khi_lsm - k;
4758  soilt_arr(i, j, lsm_k) = dst_vars(i, j, lsm_k, SLM_DST::beta) - dst_vars(i, j, lsm_k, SLM_DST::alpha) * soilt_arr(i, j, lsm_k - 1);
4759  }
4760 
4761  // for wetland, surface water is already implied: gSAM+SLM
4762  if (landtype_arr(i, j, 0) == 11) {
4763  mws_arr(i, j, 0) = zero;
4764  }
4765  });
4766 }
@ st_eff_cond
Definition: ERF_SLM.H:150
@ beta
Definition: ERF_SLM.H:152
@ alpha
Definition: ERF_SLM.H:151
@ st_capa
Definition: ERF_SLM.H:149
@ st_cond
Definition: ERF_SLM.H:148
@ grflux
Definition: ERF_SLM.H:140
Here is the call graph for this function:

◆ soil_water()

void SLM::soil_water ( const amrex::MFIter &  mfi)
4330 {
4331  const int d_khi_lsm = khi_lsm;
4332  const int d_klo_lsm = klo_lsm;
4333  const int d_nz_lsm = m_nz_lsm;
4334  const Real dt = m_dt;
4335 
4336  auto box = mfi.tilebox();
4337 
4338  auto landmask_arr = landmask.const_array(mfi);
4339  auto landtype_arr = landtype.const_array(mfi);
4340 
4341  auto soilt_arr = lsm_fab_vars[LsmVar_SLM::soilt]->const_array(mfi);
4342  auto soilw_arr = lsm_fab_vars[LsmVar_SLM::soilw]->array(mfi);
4343 
4344  auto poro_soil_arr = lsm_fab_vars[LsmVar_SLM::poro_soil]->const_array(mfi);
4345  auto s_depth_arr = lsm_fab_vars[LsmVar_SLM::s_depth]->const_array(mfi);
4346 
4347  auto IMPERV_arr = IMPERV.const_array(mfi);
4348 
4349  auto mws_arr = mws.array(mfi);
4350  auto mws_mx_arr = mws_mx.const_array(mfi);
4351 
4352  auto evp_soil_arr = evp_soil.const_array(mfi);
4353  auto evapo_dry_arr = evapo_dry.const_array(mfi);
4354  auto prsfc_arr = lsm_fab_vars[LsmVar_SLM::prsfc]->array(mfi);
4355  auto m_pot_sat_arr = lsm_fab_vars[LsmVar_SLM::m_pot_sat]->const_array(mfi);
4356  auto ks_arr = lsm_fab_vars[LsmVar_SLM::ks]->const_array(mfi);
4357  auto Bconst_arr = lsm_fab_vars[LsmVar_SLM::Bconst]->const_array(mfi);
4358  auto w_s_WP_arr = lsm_fab_vars[LsmVar_SLM::w_s_WP]->const_array(mfi);
4359  auto w_s_FC_arr = lsm_fab_vars[LsmVar_SLM::w_s_FC]->const_array(mfi);
4360  auto soil_transp_frac_arr = lsm_fab_vars[LsmVar_SLM::soil_transp_frac]->const_array(mfi);
4361 
4362  auto slm_diag_arr = slm_diag.array(mfi);
4363 
4364  auto dsw_vars = soilw_vars.array(mfi);
4365 
4366  ParallelFor( box, [=] AMREX_GPU_DEVICE (int i, int j, int)
4367  {
4368  if (landmask_arr(i, j, 0) != 1) {
4369  return;
4370  }
4371 
4372  amrex::Real aa, bb, cc, dd;
4374  amrex::Real precip_sfc_loc = prsfc_arr(i, j, 0);
4375 
4376  if (landtype_arr(i, j, 0) == 15) {
4377  // ice
4378  for (int k = 0; k < d_nz_lsm; k++) {
4379  const int lsm_k = d_khi_lsm - k;
4380  soilw_arr(i, j, lsm_k) = zero;
4381  }
4382  } else {
4383  for (int k = 0; k < d_nz_lsm; k++) {
4384  const int lsm_k = d_khi_lsm - k;
4385  dsw_vars(i, j, lsm_k, SLM_DSW::sdepth_mm) = s_depth_arr(i, j, lsm_k) * 1.0e3;
4386  }
4387  const amrex::Real ground_flux = evp_soil_arr(i, j, 0);
4388  const amrex::Real pond_evaporation = std::min(std::max(amrex::Real(zero), ground_flux),
4389  std::max(amrex::Real(zero), mws_arr(i, j, 0)) / dt);
4390  const amrex::Real soil_evaporation =
4391  std::max(amrex::Real(zero), ground_flux - pond_evaporation);
4392  mws_arr(i, j, 0) = std::max(amrex::Real(zero),
4393  mws_arr(i, j, 0) - pond_evaporation * dt
4394  - std::min(amrex::Real(zero), ground_flux) * dt);
4395 
4396  // Calculate precipitation infiltration rate into the first soil layer
4397  bool any_less_than_one = false;
4398  for (int k = 0; k < d_nz_lsm; k++) {
4399  const int lsm_k = d_khi_lsm - k;
4400  if(soilw_arr(i, j, lsm_k) < one) {
4401  any_less_than_one = true;
4402  break;
4403  }
4404  }
4405  if (any_less_than_one && soilt_arr(i, j, d_khi_lsm) >= tfriz)
4406  {
4407  precip_in = (1.-IMPERV_arr(i, j, 0))*std::min(precip_sfc_loc + mws_arr(i, j, 0)/dt, ks_arr(i, j, d_khi_lsm));
4408  } else {
4409  precip_in = zero;
4410  }
4411  // calculate diffusion coefficient and velocity for soil moisture transfer
4412  // at each interfacial layer(= between adjacent soil layers)
4413  for (int k = 0; k < d_nz_lsm - 1; k++) {
4414  const int lsm_k = d_khi_lsm - k;
4415 
4416  if (soilt_arr(i, j, lsm_k) >= tfriz && soilt_arr(i, j, lsm_k-1) >= tfriz)
4417  {
4418  const amrex::Real sd_lo = dsw_vars(i, j, lsm_k - 1, SLM_DSW::sdepth_mm);
4419  const amrex::Real sd = dsw_vars(i, j, lsm_k , SLM_DSW::sdepth_mm);
4420  const amrex::Real dz = sd + sd_lo;
4421 
4422  dsw_vars(i, j, lsm_k, SLM_DSW::sh_eff_cond) =
4423  (sd * std::pow(
4424  soilw_arr(i, j, lsm_k),
4425  Bconst_arr(i, j, lsm_k) + two) +
4426  sd_lo * std::pow(
4427  soilw_arr(i, j, lsm_k - 1),
4428  Bconst_arr(i, j, lsm_k) + two)) /
4429  dz * ks_arr(i, j, lsm_k) * Bconst_arr(i, j, lsm_k) *
4430  std::abs(m_pot_sat_arr(i, j, lsm_k)) /
4431  poro_soil_arr(i, j, lsm_k);
4432 
4433  dsw_vars(i, j, lsm_k, SLM_DSW::sh_eff_vel) =
4434  (sd * std::pow(
4435  soilw_arr(i, j, lsm_k),
4436  two * Bconst_arr(i, j, lsm_k) + two) +
4437  sd_lo * std::pow(
4438  soilw_arr(i, j, lsm_k - 1),
4439  two * Bconst_arr(i, j, lsm_k) + two)) /
4440  dz * ks_arr(i, j, lsm_k) / poro_soil_arr(i, j, lsm_k);
4441  } else {
4442  // no water movement between two layers, one of which is frozen
4443  dsw_vars(i, j, lsm_k, SLM_DSW::sh_eff_cond) = zero;
4444  dsw_vars(i, j, lsm_k, SLM_DSW::sh_eff_vel) = zero;
4445  }
4446  }
4447 
4448  // make saturated soil for wetlands
4449  if (landtype_arr(i, j, 0) == 11)
4450  {
4451  for (int k = 0; k < d_nz_lsm; k++) {
4452  const int lsm_k = d_khi_lsm - k;
4453  soilw_arr(i, j, lsm_k) = w_s_FC_arr(i, j, lsm_k);
4454  }
4455  }
4456 
4457  // from FDE by the implicit method, Thomas algorithm is applied
4458  // aa: terms related with soilw(k-1)
4459  // bb: terms related with soilw(k)
4460  // cc: terms related with soilw(k+1)
4461  // dd: current soil wetness - sink + source
4462  for (int k = 0; k < d_nz_lsm; k++) {
4463  const int lsm_k = d_khi_lsm - k;
4464  if (soilw_arr(i, j, lsm_k) < w_s_WP_arr(i, j, lsm_k))
4465  {
4466  dsw_vars(i, j, lsm_k, SLM_DSW::sw_wgt) = zero;
4467  } else {
4468  dsw_vars(i, j, lsm_k, SLM_DSW::sw_wgt) = one;
4469  }
4470  }
4471  aa = zero;
4472  cc = -two * dsw_vars(i, j, d_khi_lsm, SLM_DSW::sh_eff_cond)*dt/(dsw_vars(i, j, d_khi_lsm, SLM_DSW::sdepth_mm)*(dsw_vars(i, j, d_khi_lsm, SLM_DSW::sdepth_mm) + dsw_vars(i, j, d_khi_lsm - 1, SLM_DSW::sdepth_mm)));
4473  bb = 1.0 - cc + dsw_vars(i, j, d_khi_lsm, SLM_DSW::sh_eff_vel)*dt/dsw_vars(i, j, d_khi_lsm, SLM_DSW::sdepth_mm);
4474  dd = soilw_arr(i, j, d_khi_lsm) - (soil_evaporation + soil_transp_frac_arr(i, j, d_khi_lsm)*evapo_dry_arr(i, j, 0) - precip_in)*dt / poro_soil_arr(i, j, d_khi_lsm) / dsw_vars(i, j, d_khi_lsm, SLM_DSW::sdepth_mm);
4475 
4476  dsw_vars(i, j, d_khi_lsm, SLM_DSW::alpha) = cc / bb;
4477  dsw_vars(i, j, d_khi_lsm, SLM_DSW::beta) = dd / bb;
4478 
4479  // For 2-(nsoil-1) layer:
4480  for (int k = 1; k < d_nz_lsm - 1; k++) {
4481  // NOTE: SLM k-1 indices here are switched from original SAM
4482  // version because our soil level indices are from -1 (top) to
4483  // -nsoil (bottom)
4484  // Index mapping:
4485  // SAM SLM -> ERF SLM
4486  // 1 -> d_khi_lsm
4487  // k - 1 -> k + 1
4488  // k + 1 -> k - 1
4489  // nsoil -> d_klo_lsm
4490  const int lsm_k = d_khi_lsm - k; // lsm_k = -2 to -nsoil+1
4491 
4492  const amrex::Real sd_lo = dsw_vars(i, j, lsm_k - 1, SLM_DSW::sdepth_mm);
4493  const amrex::Real sd = dsw_vars(i, j, lsm_k , SLM_DSW::sdepth_mm);
4494  const amrex::Real sd_up = dsw_vars(i, j, lsm_k + 1, SLM_DSW::sdepth_mm);
4495 
4496  const amrex::Real sh_eff_cond = dsw_vars(i, j, lsm_k , SLM_DSW::sh_eff_cond);
4497  const amrex::Real sh_eff_cond_up = dsw_vars(i, j, lsm_k + 1, SLM_DSW::sh_eff_cond);
4498 
4499  const amrex::Real sh_eff_vel = dsw_vars(i, j, lsm_k , SLM_DSW::sh_eff_vel);
4500  const amrex::Real sh_eff_vel_up = dsw_vars(i, j, lsm_k + 1, SLM_DSW::sh_eff_vel);
4501 
4502  aa = -one * dt / sd * (sh_eff_vel_up + sh_eff_cond_up * two / (sd_up + sd));
4503  cc = -one * dt / sd * two * sh_eff_cond / (sd + sd_lo);
4504  bb = one - cc + dt / sd * sh_eff_vel + two * dt / sd * sh_eff_cond_up / (sd_up + sd);
4505  dd = soilw_arr(i, j, lsm_k) - soil_transp_frac_arr(i, j, lsm_k) * evapo_dry_arr(i, j, 0) * dt / poro_soil_arr(i, j, lsm_k) / sd; // current time step
4506 
4507  dsw_vars(i, j, lsm_k, SLM_DSW::alpha) = cc / (bb - aa * dsw_vars(i, j, lsm_k + 1, SLM_DSW::alpha));
4508  dsw_vars(i, j, lsm_k, SLM_DSW::beta) = (dd - aa * dsw_vars(i, j, lsm_k + 1, SLM_DSW::beta)) / (bb - aa * dsw_vars(i, j, lsm_k + 1, SLM_DSW::alpha));
4509  }
4510 
4511  // For bottom layer:
4512  aa = -1.0 * dt / dsw_vars(i, j, d_klo_lsm, SLM_DSW::sdepth_mm) *
4513  (dsw_vars(i, j, d_klo_lsm + 1, SLM_DSW::sh_eff_vel) +
4514  dsw_vars(i, j, d_klo_lsm + 1, SLM_DSW::sh_eff_cond) * two /
4515  (dsw_vars(i, j, d_klo_lsm + 1, SLM_DSW::sdepth_mm) +
4516  dsw_vars(i, j, d_klo_lsm, SLM_DSW::sdepth_mm)));
4517  cc = zero;
4518  bb =
4519  one + (dt / dsw_vars(i, j, d_klo_lsm, SLM_DSW::sdepth_mm) * two *
4520  dsw_vars(i, j, d_klo_lsm + 1, SLM_DSW::sh_eff_cond) /
4521  (dsw_vars(i, j, d_klo_lsm + 1, SLM_DSW::sdepth_mm) +
4522  dsw_vars(i, j, d_klo_lsm, SLM_DSW::sdepth_mm)));
4523 
4524  // drainage when it exceeds 1.0 mm/s
4525  amrex::Real drainage_flux = std::max(soilw_arr(i, j, d_klo_lsm) - one, amrex::Real(zero))*poro_soil_arr(i, j, d_klo_lsm)*dsw_vars(i, j, d_klo_lsm, SLM_DSW::sdepth_mm)/dt;
4526  dd = soilw_arr(i, j, d_klo_lsm) - (soil_transp_frac_arr(i, j, d_klo_lsm)*evapo_dry_arr(i, j, 0) + drainage_flux) * dt / poro_soil_arr(i, j, d_klo_lsm) / dsw_vars(i, j, d_klo_lsm, SLM_DSW::sdepth_mm);
4527 
4528  slm_diag_arr(i, j, 0, SLM_Diag::drain_flux) = drainage_flux;
4529 
4530  dsw_vars(i, j, d_klo_lsm, SLM_DSW::alpha) = zero;
4531  dsw_vars(i, j, d_klo_lsm, SLM_DSW::beta) = (dd - aa * dsw_vars(i, j, d_klo_lsm + 1, SLM_DSW::beta)) / (bb - aa * dsw_vars(i, j, d_klo_lsm + 1, SLM_DSW::alpha));
4532 
4533  // (n+1) time step soil wetness:
4534  soilw_arr(i, j, d_klo_lsm) = dsw_vars(i, j, d_klo_lsm, SLM_DSW::beta);
4535  for (int k = d_nz_lsm - 2; k >= 0; k--) {
4536  const int lsm_k = d_khi_lsm - k;
4537  soilw_arr(i, j, lsm_k) = std::max(amrex::Real(zero), dsw_vars(i, j, lsm_k, SLM_DSW::beta) - dsw_vars(i, j, lsm_k, SLM_DSW::alpha) * soilw_arr(i, j, lsm_k - 1));
4538  }
4539 
4540  bool fix = false;
4541  for (int k = 0; k < d_nz_lsm; k++) {
4542  const int lsm_k = d_khi_lsm - k;
4543  if (soilw_arr(i, j, lsm_k) > one)
4544  {
4545  fix = true;
4546  break;
4547  }
4548  }
4549 
4550  amrex::Real excess_water = zero;
4551  if (fix) {
4552  // fix the levels where wetness exceeds 1 preserving total water:
4553  // compute the excess
4554  for (int kk = 0; kk < d_nz_lsm; kk++) {
4555  const int lsm_kk = d_khi_lsm - kk;
4556  if (soilw_arr(i, j, lsm_kk) > one)
4557  {
4558  excess_water += (soilw_arr(i, j, lsm_kk) - 1.0)*poro_soil_arr(i, j, lsm_kk)*dsw_vars(i, j, lsm_kk, SLM_DSW::sdepth_mm);
4559  soilw_arr(i, j, lsm_kk) = one;
4560  }
4561  }
4562 
4563  // distribute the excess among layer into deepest layers first:
4564  for (int kk = d_nz_lsm - 1; kk >= 0; kk--) {
4565  const int lsm_kk = d_khi_lsm - kk;
4566  if (soilw_arr(i, j, lsm_kk) < one)
4567  {
4568  cc = std::min(excess_water, (one - soilw_arr(i, j, lsm_kk))*poro_soil_arr(i, j, lsm_kk)*dsw_vars(i, j, lsm_kk, SLM_DSW::sdepth_mm));
4569  soilw_arr(i, j, lsm_kk) += cc / (poro_soil_arr(i, j, lsm_kk)*dsw_vars(i, j, lsm_kk, SLM_DSW::sdepth_mm));
4570  excess_water -= cc;
4571  if (excess_water <= zero)
4572  {
4573  break;
4574  }
4575  }
4576  }
4577  }
4578  // fixing the issue with rain infiltration even if soil is completely saturated
4579  // move all the access to mws and modify the precip_in accordinally
4580  if (excess_water > zero) {
4581  // still some water left after saturating all the soil layers
4582  // modify the precip_in so that the access of water is moved
4583  // to the surface water
4584  precip_in -= excess_water/dt;
4585  }
4586  slm_diag_arr(i, j, 0, SLM_Diag::precip_in) = precip_in;
4587  mws_arr(i, j, 0) = std::max(amrex::Real(zero), mws_arr(i, j, 0) + (precip_sfc_loc - precip_in)*dt);
4588 
4590  if(mws_arr(i, j, 0) > mws_mx_arr(i, j, 0))
4591  {
4592  drain = (mws_arr(i, j, 0) - mws_mx_arr(i, j, 0))/dt;
4593  mws_arr(i, j, 0) = mws_mx_arr(i, j, 0);
4594  }
4595  else
4596  {
4597  drain = zero;
4598  }
4599  slm_diag_arr(i, j, 0, SLM_Diag::drain) = drain;
4600 
4601  }
4602  });
4603 }
@ sw_wgt
Definition: ERF_SLM.H:158
@ sh_eff_cond
Definition: ERF_SLM.H:159
@ beta
Definition: ERF_SLM.H:162
@ alpha
Definition: ERF_SLM.H:161
@ sh_eff_vel
Definition: ERF_SLM.H:160
@ sdepth_mm
Definition: ERF_SLM.H:163
@ drain_flux
Definition: ERF_SLM.H:138
Here is the call graph for this function:

◆ solve_ground_skin_temperature()

void SLM::solve_ground_skin_temperature ( const amrex::MFIter &  mfi)
4043 {
4044  const int d_khi_lsm = khi_lsm;
4045  const Real dt = m_dt;
4046 
4047  auto box = mfi.tilebox();
4048 
4049  auto landmask_arr = landmask.const_array(mfi);
4050  auto landtype_arr = landtype.const_array(mfi);
4051 
4052  auto IMPERV_arr = IMPERV.const_array(mfi);
4053 
4054  auto q_cas_arr = q_cas.const_array(mfi);
4055  auto t_cas_arr = t_cas.const_array(mfi);
4056  auto t_ground_skin_arr = t_ground_skin.array(mfi);
4057 
4058  auto soilt_arr = lsm_fab_vars[LsmVar_SLM::soilt]->const_array(mfi);
4059  auto soilw_arr = lsm_fab_vars[LsmVar_SLM::soilw]->const_array(mfi);
4060  auto pref_arr = lsm_fab_vars[LsmVar_SLM::pref]->const_array(mfi);
4061  auto s_depth_arr = lsm_fab_vars[LsmVar_SLM::s_depth]->const_array(mfi);
4062  auto sst_cond_arr = lsm_fab_vars[LsmVar_SLM::sst_cond]->const_array(mfi);
4063  auto poro_soil_arr = lsm_fab_vars[LsmVar_SLM::poro_soil]->const_array(mfi);
4064 
4065  auto lhf_canop_arr = lhf_canop.array(mfi);
4066  auto lhf_soil_arr = lhf_soil.array(mfi);
4067  auto lhf_air_arr = lhf_air.array(mfi);
4068 
4069  auto evp_canop_arr = evp_canop.array(mfi);
4070  auto evp_soil_arr = evp_soil.array(mfi);
4071  auto evp_air_arr = evp_air.array(mfi);
4072 
4073  auto shf_soil_arr = shf_soil.array(mfi);
4074  auto shf_canop_arr = shf_canop.array(mfi);
4075  auto shf_air_arr = shf_air.array(mfi);
4076 
4077  auto t_sfc_arr = t_sfc.array(mfi);
4078  auto q_sfc_arr = q_sfc.array(mfi);
4079 
4080  auto q_gr_arr = q_gr.array(mfi);
4081  auto sdew_arr = sdew.array(mfi);
4082 
4083  auto vegetype_arr = vegetype.const_array(mfi);
4084  auto mws_arr = mws.const_array(mfi);
4085 
4086  auto dref_arr = lsm_fab_vars[LsmVar_SLM::dref]->const_array(mfi);
4087  auto qr_arr = lsm_fab_vars[LsmVar_SLM::qref]->const_array(mfi);
4088  auto tr_arr = lsm_fab_vars[LsmVar_SLM::tref]->const_array(mfi);
4089  auto m_pot_sat_arr = lsm_fab_vars[LsmVar_SLM::m_pot_sat]->const_array(mfi);
4090  auto Bconst_arr = lsm_fab_vars[LsmVar_SLM::Bconst]->const_array(mfi);
4091  auto w_s_WP_arr = lsm_fab_vars[LsmVar_SLM::w_s_WP]->const_array(mfi);
4092  auto soil_transp_frac_arr = lsm_fab_vars[LsmVar_SLM::soil_transp_frac]->const_array(mfi);
4093  auto t_canop_arr = t_canop.const_array(mfi);
4094 
4095  auto r_a_arr = r_a.const_array(mfi);
4096  auto r_d_arr = r_d.const_array(mfi);
4097  auto r_soil_arr = r_soil.array(mfi);
4098  auto evapo_dry_arr = evapo_dry.array(mfi);
4099 
4100  auto slm_diag_arr = slm_diag.array(mfi);
4101  auto net_rad_arr = net_rad.array(mfi);
4102  auto t_skin_arr = t_skin.array(mfi);
4103  auto lwref_arr = lsm_fab_vars[LsmVar_SLM::lwref]->const_array(mfi);
4104  auto LAI_arr = LAI.const_array(mfi);
4105  auto SAI_arr = SAI.const_array(mfi);
4106 
4107  const amrex::Real d_eg_soil = eg_soil_rad;
4108  constexpr int max_iterations = 5;
4109  constexpr amrex::Real derivative_step = 0.1;
4110  constexpr amrex::Real correction_limit = 5.0;
4111  constexpr amrex::Real convergence_tolerance = 0.01;
4112  const amrex::Real d_rsurf_exp = rsurf_exp;
4113 
4114 
4115  ParallelFor( box, [=] AMREX_GPU_DEVICE (int i, int j, int)
4116  {
4117  if (landmask_arr(i, j, 0) != 1) {
4118  return;
4119  }
4120 
4121  // SAM rhow[nz] = air density at vertical velocity levels, kg/m^3
4122  const amrex::Real rhow = dref_arr(i, j, 0); // TODO: double check this
4123  const bool vegetated = vegetype_arr(i, j, 0) == 1;
4124  const amrex::Real q_air = vegetated ? q_cas_arr(i, j, 0) : qr_arr(i, j, 0);
4125  const amrex::Real top_soil_water =
4126  std::max(amrex::Real(zero), soilw_arr(i, j, d_khi_lsm)) *
4127  poro_soil_arr(i, j, d_khi_lsm) *
4128  s_depth_arr(i, j, d_khi_lsm) * 1.0e3;
4129  const amrex::Real transpiration_top =
4130  std::max(amrex::Real(zero), soil_transp_frac_arr(i, j, d_khi_lsm)) *
4131  std::max(amrex::Real(zero), evapo_dry_arr(i, j, 0)) * dt;
4132  const amrex::Real available_ground_water =
4133  std::max(amrex::Real(zero), mws_arr(i, j, 0)) +
4134  std::max(amrex::Real(zero), top_soil_water - transpiration_top);
4135  const amrex::Real max_ground_evaporation = available_ground_water / dt;
4136 
4137  /* Legacy SLM soil diffusion and resistance formulation:
4138  amrex::Real q_ground_guess;
4139  if (t_ground_skin_arr(i, j, 0) >= tfriz) {
4140  erf_qsatw(t_ground_skin_arr(i, j, 0), pref_arr(i, j, 0), q_ground_guess);
4141  if (mws_arr(i, j, 0) == zero) {
4142  q_ground_guess *= fh_calc(t_ground_skin_arr(i, j, 0),
4143  m_pot_sat_arr(i, j, d_khi_lsm),
4144  soilw_arr(i, j, d_khi_lsm),
4145  Bconst_arr(i, j, d_khi_lsm));
4146  }
4147  } else {
4148  erf_qsati(t_ground_skin_arr(i, j, 0), pref_arr(i, j, 0), q_ground_guess);
4149  }
4150  amrex::Real soil_diff;
4151  if (soilw_arr(i, j, d_khi_lsm) >= w_s_FC_arr(i,j,d_khi_lsm) ||
4152  q_air > q_ground_guess) {
4153  soil_diff = one;
4154  } else {
4155  soil_diff = fourth * std::pow(one - std::cos(PI *
4156  std::max(amrex::Real(0.01), soilw_arr(i, j, d_khi_lsm)) /
4157  w_s_FC_arr(i, j, d_khi_lsm)), 2);
4158  }
4159  amrex::Real totalR_soil;
4160  if (!vegetated) {
4161  r_soil_arr(i, j, 0) = std::min(amrex::Real(10000.0), std::max(amrex::Real(100.0),
4162  r_a_arr(i, j, 0) * (1.0 / soil_diff - 1.0)));
4163  totalR_soil = r_soil_arr(i, j, 0) + r_a_arr(i, j, 0);
4164  } else {
4165  r_soil_arr(i, j, 0) = std::min(amrex::Real(10000.0), std::max(amrex::Real(50.0),
4166  r_d_arr(i, j, 0) * (1.0 / soil_diff - 1.0)));
4167  totalR_soil = r_soil_arr(i, j, 0) + r_d_arr(i, j, 0) + r_litter;
4168  }
4169  */
4170 
4171  // NoahMP option 1 (Sakaguchi and Zeng, 2009) ground resistance.
4172  const amrex::Real soil_wetness = std::min(amrex::Real(one),
4173  std::max(amrex::Real(zero), soilw_arr(i, j, d_khi_lsm)));
4174  const amrex::Real dry_soil_thickness = s_depth_arr(i, j, d_khi_lsm)
4175  * (std::exp(std::pow(one - soil_wetness, d_rsurf_exp)) - one)
4176  / (std::exp(one) - one);
4177  const amrex::Real soil_b = Bconst_arr(i, j, d_khi_lsm);
4178  const amrex::Real vapor_diffusivity = soil_b > zero
4179  ? 2.2e-5 * poro_soil_arr(i, j, d_khi_lsm)
4180  * poro_soil_arr(i, j, d_khi_lsm)
4181  * std::pow(one - w_s_WP_arr(i, j, d_khi_lsm),
4182  two + three / soil_b)
4183  : 0.0;
4184  amrex::Real soil_resistance = dry_soil_thickness
4185  / std::max(vapor_diffusivity, amrex::Real(1.0e-12));
4186  const amrex::Real theta_liq = soilw_arr(i, j, d_khi_lsm)
4187  * poro_soil_arr(i, j, d_khi_lsm);
4188  if (theta_liq < 0.01 || landtype_arr(i, j, 0) == 13 || soil_b <= zero) {
4189  soil_resistance = 1.0e6;
4190  }
4191  r_soil_arr(i, j, 0) = soil_resistance;
4192 
4193  const amrex::Real aerodynamic_resistance = vegetated
4194  ? r_d_arr(i, j, 0) : r_a_arr(i, j, 0);
4195  const amrex::Real totalR_soil = aerodynamic_resistance + soil_resistance
4196  + (vegetated ? r_litter : 0.0);
4197 
4198  amrex::Real k_dry, k_sat, conductivity;
4199  if (landtype_arr(i, j, 0) == 15) {
4200  conductivity = 1.6;
4201  } else {
4202  const amrex::Real dry_density = (1.0 - poro_soil_arr(i, j, d_khi_lsm)) * 2700.0;
4203  k_dry = (0.135 * dry_density + 64.7) / (2700.0 - 0.947 * dry_density);
4204  k_sat = std::pow(sst_cond_arr(i, j, d_khi_lsm), one - poro_soil_arr(i, j, d_khi_lsm));
4205  k_sat *= std::pow(soilt_arr(i, j, d_khi_lsm) > tfriz ? amrex::Real(0.57) : amrex::Real(1.60),
4206  poro_soil_arr(i, j, d_khi_lsm));
4207  const amrex::Real ke = std::log10(std::max(amrex::Real(0.1), soilw_arr(i, j, d_khi_lsm))) + one;
4208  conductivity = ke * (k_sat - k_dry) + k_dry;
4209  }
4210  const amrex::Real conduction_coeff = two * conductivity / s_depth_arr(i, j, d_khi_lsm);
4211  const amrex::Real emv = one - std::exp(-(LAI_arr(i, j, 0) + SAI_arr(i, j, 0)));
4212  const amrex::Real emg = d_eg_soil;
4213  const amrex::Real net_sw = net_rad_arr(i, j, 0, SLM_NetRad::net_sw2);
4214  const amrex::Real lwdn = lwref_arr(i, j, 0);
4215  const amrex::Real tv = t_canop_arr(i, j, 0);
4216  const amrex::Real tsoil = soilt_arr(i, j, d_khi_lsm);
4217  const amrex::Real potential_factor = std::pow(amrex::Real(1000.0) / pref_arr(i, j, 0), R_d / Cp_d);
4218 
4219  auto residual = [=] AMREX_GPU_DEVICE (amrex::Real tg) noexcept {
4220  amrex::Real q_ground;
4221  amrex::Real dew_factor = one;
4222  if (tg >= tfriz) {
4223  erf_qsatw(tg, pref_arr(i, j, 0), q_ground);
4224  if (mws_arr(i, j, 0) == zero) {
4225  const amrex::Real humidity_factor = fh_calc(tg,
4226  m_pot_sat_arr(i, j, d_khi_lsm), soilw_arr(i, j, d_khi_lsm),
4227  Bconst_arr(i, j, d_khi_lsm));
4228  dew_factor = humidity_factor > 0.99 ? one : zero;
4229  q_ground *= humidity_factor;
4230  }
4231  } else {
4232  erf_qsati(tg, pref_arr(i, j, 0), q_ground);
4233  }
4234  const amrex::Real sensible = vegetated
4235  ? (tg - t_cas_arr(i, j, 0)) * rhow * cp / r_d_arr(i, j, 0)
4236  : (tg - tr_arr(i, j, 0)) * potential_factor * rhow * cp / r_a_arr(i, j, 0);
4237  amrex::Real evaporation = (q_ground - q_air) * rhow / totalR_soil
4238  * (1.0 - IMPERV_arr(i, j, 0));
4239  if (evaporation > zero) {
4240  evaporation = std::min(evaporation, max_ground_evaporation);
4241  }
4242  if (evaporation < zero) evaporation *= dew_factor;
4243  const amrex::Real irg = emg * sigma * amrex::Math::powi<4>(tg)
4244  - emg * (1.0 - emv) * lwdn
4245  - emg * emv * sigma * amrex::Math::powi<4>(tv);
4246  return net_sw - irg - sensible - lcond * evaporation
4247  - conduction_coeff * (tg - tsoil);
4248  };
4249 
4250  amrex::Real tg = t_ground_skin_arr(i, j, 0);
4251  for (int iter = 0; iter < max_iterations; ++iter) {
4252  const amrex::Real f = residual(tg);
4253  const amrex::Real fprime = (residual(tg + derivative_step)
4254  - residual(tg - derivative_step)) / (two * derivative_step);
4255  AMREX_ALWAYS_ASSERT(std::isfinite(f) && std::isfinite(fprime));
4256  if (std::abs(fprime) < 1.0e-12) break;
4257  const amrex::Real delta = std::max(-correction_limit,
4258  std::min(correction_limit, -f / fprime));
4259  tg += delta;
4260  if (std::abs(delta) < convergence_tolerance) {
4261  break;
4262  }
4263  }
4264 
4265  amrex::Real q_ground;
4266  amrex::Real dew_factor = one;
4267  if (tg >= tfriz) {
4268  erf_qsatw(tg, pref_arr(i, j, 0), q_ground);
4269  if (mws_arr(i, j, 0) == zero) {
4270  const amrex::Real humidity_factor = fh_calc(tg,
4271  m_pot_sat_arr(i, j, d_khi_lsm), soilw_arr(i, j, d_khi_lsm),
4272  Bconst_arr(i, j, d_khi_lsm));
4273  dew_factor = humidity_factor > 0.99 ? one : zero;
4274  q_ground *= humidity_factor;
4275  }
4276  } else {
4277  erf_qsati(tg, pref_arr(i, j, 0), q_ground);
4278  }
4279  const amrex::Real sensible = vegetated
4280  ? (tg - t_cas_arr(i, j, 0)) * rhow * cp / r_d_arr(i, j, 0)
4281  : (tg - tr_arr(i, j, 0)) * potential_factor * rhow * cp / r_a_arr(i, j, 0);
4282  amrex::Real evaporation = (q_ground - q_air) * rhow / totalR_soil
4283  * (1.0 - IMPERV_arr(i, j, 0));
4284  if (evaporation > zero) {
4285  evaporation = std::min(evaporation, max_ground_evaporation);
4286  }
4287  if (evaporation < zero) evaporation *= dew_factor;
4288  const amrex::Real irg = emg * sigma * amrex::Math::powi<4>(tg)
4289  - emg * (1.0 - emv) * lwdn
4290  - emg * emv * sigma * amrex::Math::powi<4>(tv);
4291  const amrex::Real ground_conduction = conduction_coeff * (tg - tsoil);
4292 
4293  t_ground_skin_arr(i, j, 0) = tg;
4294  q_gr_arr(i, j, 0) = q_ground;
4295  sdew_arr(i, j, 0) = dew_factor;
4296  shf_soil_arr(i, j, 0) = sensible;
4297  evp_soil_arr(i, j, 0) = evaporation;
4298  lhf_soil_arr(i, j, 0) = lcond * evaporation;
4299  t_sfc_arr(i, j, 0) = vegetated ? t_cas_arr(i, j, 0) : tg;
4300  q_sfc_arr(i, j, 0) = vegetated ? q_cas_arr(i, j, 0) : q_ground;
4301  shf_air_arr(i, j, 0) = vegetated ? shf_canop_arr(i, j, 0) + sensible : sensible;
4302  evp_air_arr(i, j, 0) = vegetated ? evp_canop_arr(i, j, 0) + evaporation : evaporation;
4303  lhf_air_arr(i, j, 0) = vegetated ? lhf_canop_arr(i, j, 0) + lcond * evaporation
4304  : lcond * evaporation;
4305 
4306  const amrex::Real tir1 = emv * sigma * amrex::Math::powi<4>(tv);
4307  const amrex::Real tir2 = emg * sigma * amrex::Math::powi<4>(tg);
4308  const amrex::Real lwdn2 = (1.0 - emv) * lwdn + tir1;
4309  const amrex::Real lwup2 = tir2 + (1.0 - emg) * lwdn2;
4310  const amrex::Real irc = -emv * (1.0 + (1.0 - emv) * (1.0 - emg)) * lwdn
4311  - emv * emg * sigma * amrex::Math::powi<4>(tg)
4312  + (two - emv * (one - emg)) * emv * sigma * amrex::Math::powi<4>(tv);
4313  const amrex::Real lwup1 = lwdn + irc + irg;
4314  net_rad_arr(i, j, 0, SLM_NetRad::tir2) = tir2;
4315  net_rad_arr(i, j, 0, SLM_NetRad::net_lwup2) = lwup2;
4316  net_rad_arr(i, j, 0, SLM_NetRad::net_lwdn2) = lwdn2;
4317  net_rad_arr(i, j, 0, SLM_NetRad::net_lw2) = -irg;
4318  net_rad_arr(i, j, 0, SLM_NetRad::net_rad2) = net_sw - irg;
4319  net_rad_arr(i, j, 0, SLM_NetRad::net_lwup1) = lwup1;
4320  const amrex::Real emiss_sfc = emv + emg * (1.0 - emv)
4321  + emv * (1.0 - emv) * (1.0 - emg);
4322  t_skin_arr(i, j, 0) = std::pow(std::max(amrex::Real(1.0e-6),
4323  (lwup1 - (one - emiss_sfc) * lwdn) / (emiss_sfc * sigma)), fourth);
4324 
4325  slm_diag_arr(i, j, 0, SLM_Diag::grflux) = -ground_conduction;
4326  });
4327 }
static constexpr amrex::Real sigma
Definition: ERF_SLM.H:779
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real aerodynamic_resistance(const amrex::Real z_ref, const amrex::Real z0, const amrex::Real psi_h, const amrex::Real kappa, const amrex::Real u_star)
Definition: ERF_SurfaceMoisture.H:116
Here is the call graph for this function:

◆ surrad_noahmp()

AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void SLM::surrad_noahmp ( amrex::Real  mpe,
amrex::Real  fsun,
amrex::Real  fsha,
amrex::Real  elai,
amrex::Real  vai,
amrex::Real  laisun,
amrex::Real  laisha,
const amrex::Real *  solad,
const amrex::Real *  solai,
const amrex::Real *  fabd,
const amrex::Real *  fabi,
const amrex::Real *  ftdd,
const amrex::Real *  ftid,
const amrex::Real *  ftii,
const amrex::Real *  albgrd,
const amrex::Real *  albgri,
const amrex::Real *  albd,
const amrex::Real *  albi,
const amrex::Real *  frevd,
const amrex::Real *  frevi,
const amrex::Real *  fregd,
const amrex::Real *  fregi,
amrex::Real &  parsun,
amrex::Real &  parsha,
amrex::Real &  sav,
amrex::Real &  sag,
amrex::Real &  fsa,
amrex::Real &  fsr,
amrex::Real &  fsrv,
amrex::Real &  fsrg 
)
static
5674 {
5675  // --------------------------------------------------------------------------------------------------
5676  // input
5677  // MPE !prevents underflow errors if division by zero
5678  // FSUN !sunlit fraction of canopy
5679  // FSHA !shaded fraction of canopy
5680  // ELAI !leaf area, one-sided
5681  // VAI !leaf + stem area, one-sided
5682  // LAISUN !sunlit leaf area index, one-sided
5683  // LAISHA !shaded leaf area index, one-sided
5684  // SOLAD !incoming direct solar radiation (w/m2)
5685  // SOLAI !incoming diffuse solar radiation (w/m2)
5686  // FABD !flux abs by veg (per unit incoming direct flux)
5687  // FABI !flux abs by veg (per unit incoming diffuse flux)
5688  // FTDD !down dir flux below veg (per incoming dir flux)
5689  // FTID !down dif flux below veg (per incoming dir flux)
5690  // FTII !down dif flux below veg (per incoming dif flux)
5691  // ALBGRD !ground albedo (direct)
5692  // ALBGRI !ground albedo (diffuse)
5693  // ALBD !overall surface albedo (direct)
5694  // ALBI !overall surface albedo (diffuse)
5695  // FREVD !overall surface albedo veg (direct)
5696  // FREVI !overall surface albedo veg (diffuse)
5697  // FREGD !overall surface albedo grd (direct)
5698  // FREGI !overall surface albedo grd (diffuse)
5699 
5700  // output
5701  // PARSUN !average absorbed par for sunlit leaves (w/m2)
5702  // PARSHA !average absorbed par for shaded leaves (w/m2)
5703  // SAV !solar radiation absorbed by vegetation (w/m2)
5704  // SAG !solar radiation absorbed by ground (w/m2)
5705  // FSA !total absorbed solar radiation (w/m2)
5706  // FSR !total reflected solar radiation (w/m2)
5707  // FSRV !reflected solar radiation by vegetation
5708  // FSRG !reflected solar radiation by ground
5709 
5710  // ------------------------ local variables ----------------------------------------------------
5711  constexpr int NBAND = 2; //number of solar radiation waveband classes
5712 
5713  amrex::Real abs; //absorbed solar radiation (w/m2)
5714  amrex::Real rnir; //reflected solar radiation [nir] (w/m2)
5715  amrex::Real rvis; //reflected solar radiation [vis] (w/m2)
5716  amrex::Real laifra; //leaf area fraction of canopy
5717  amrex::Real trd; //transmitted solar radiation: direct (w/m2)
5718  amrex::Real tri; //transmitted solar radiation: diffuse (w/m2)
5719  amrex::Real cad[2]; //direct beam absorbed by canopy (w/m2)
5720  amrex::Real cai[2]; //diffuse radiation absorbed by canopy (w/m2)
5721  // ---------------------------------------------------------------------------------------------
5722 
5723  // zero summed solar fluxes
5724 
5725  sag = zero;
5726  sav = zero;
5727  fsa = zero;
5728 
5729  // loop over nband wavebands
5730 
5731  for (int ib = 0; ib < NBAND; ib++) {
5732 
5733  // absorbed by canopy
5734 
5735  cad[ib] = solad[ib]*fabd[ib];
5736  cai[ib] = solai[ib]*fabi[ib];
5737  sav = sav + cad[ib] + cai[ib];
5738  fsa = fsa + cad[ib] + cai[ib];
5739 
5740  // transmitted solar fluxes incident on ground
5741 
5742  trd = solad[ib]*ftdd[ib];
5743  tri = solad[ib]*ftid[ib] + solai[ib]*ftii[ib];
5744 
5745  // solar radiation absorbed by ground surface
5746 
5747  abs = trd*(1.0-albgrd[ib]) + tri*(1.0-albgri[ib]);
5748  sag = sag + abs;
5749  fsa = fsa + abs;
5750  }
5751 
5752  // partition visible canopy absorption to sunlit and shaded fractions
5753  // to get average absorbed par for sunlit and shaded leaves
5754 
5755  laifra = elai / std::max(vai,mpe);
5756  if (fsun > zero) {
5757  parsun = (cad[0]+fsun*cai[0]) * laifra / std::max(laisun,mpe);
5758  parsha = (fsha*cai[0])*laifra / std::max(laisha,mpe);
5759  } else {
5760  parsun = zero;
5761  parsha = (cad[0]+cai[0])*laifra / std::max(laisha,mpe);
5762  }
5763 
5764  // reflected solar radiation
5765 
5766  rvis = albd[0]*solad[0] + albi[0]*solai[0];
5767  rnir = albd[1]*solad[1] + albi[1]*solai[1];
5768  fsr = rvis + rnir;
5769 
5770  // reflected solar radiation of veg. and ground (combined ground)
5771  fsrv = frevd[0]*solad[0]+frevi[0]*solai[0]+frevd[1]*solad[1]+frevi[1]*solai[1];
5772  fsrg = fregd[0]*solad[0]+fregi[0]*solai[0]+fregd[1]*solad[1]+fregi[1]*solai[1];
5773 }

◆ transfer_coeff()

void SLM::transfer_coeff ( const amrex::MFIter &  mfi)
3363 {
3364  const int d_khi_lsm = khi_lsm;
3365  const Real dt = m_dt;
3366 
3367  auto box = mfi.tilebox();
3368 
3369  auto landmask_arr = landmask.const_array(mfi);
3370 
3371  auto t_sfc_arr = t_sfc.const_array(mfi);
3372  auto q_sfc_arr = q_sfc.const_array(mfi);
3373 
3374  auto disp_hgt_arr = disp_hgt.const_array(mfi);
3375  auto z0_sfc_arr = z0_sfc.const_array(mfi);
3376 
3377  auto qr_arr = lsm_fab_vars[LsmVar_SLM::qref]->const_array(mfi);
3378 
3379  auto ustar_arr = lsm_fab_vars[LsmVar_SLM::ustar]->array(mfi);
3380  auto tstar_arr = lsm_fab_vars[LsmVar_SLM::tstar]->array(mfi);
3381 
3382  auto tref_arr = lsm_fab_vars[LsmVar_SLM::tref]->const_array(mfi);
3383  auto ur_arr = lsm_fab_vars[LsmVar_SLM::uref]->const_array(mfi);
3384  auto vr_arr = lsm_fab_vars[LsmVar_SLM::vref]->const_array(mfi);
3385  auto pref_arr = lsm_fab_vars[LsmVar_SLM::pref]->const_array(mfi);
3386 
3387  auto r_a_arr = r_a.array(mfi);
3388 
3389  auto zref_arr = zrefxy.array(mfi);
3390 
3391  auto flbu_arr = lsm_fab_vars[LsmVar_SLM::flbu]->array(mfi);
3392  auto flbv_arr = lsm_fab_vars[LsmVar_SLM::flbv]->array(mfi);
3393 
3394  constexpr amrex::Real xsim = -1.574;
3395  constexpr amrex::Real xsih = -0.465;
3396  const amrex::Real xm = std::sqrt(std::sqrt(one - amrex::Real(16.0)*xsim));
3397  const amrex::Real xh = std::sqrt(std::sqrt(one - amrex::Real(16.0)*xsih));
3398 
3399  constexpr amrex::Real errormax = 0.01;
3400  constexpr amrex::Real kk = 0.4;
3401  constexpr int nitermax = 10;
3402 
3403  auto xx = [] AMREX_GPU_DEVICE (const amrex::Real &yy) -> amrex::Real {
3404  return std::sqrt(std::sqrt(one - amrex::Real(16.0) * yy));
3405  };
3406  // unstable: -1.574<xsi<0
3407  auto psim1 = [] AMREX_GPU_DEVICE (const amrex::Real &x, const amrex::Real &x0) -> amrex::Real {
3408  return two * std::log((one + x) / (one + x0)) + std::log((one + x*x) / (one + x0*x0)) - two*(std::atan(x)-std::atan(x0));
3409  };
3410  auto psih1 = [] AMREX_GPU_DEVICE (const amrex::Real &x, const amrex::Real &x0) -> amrex::Real {
3411  return two * std::log((one + x*x) / (one + x0*x0));
3412  };
3413  // very unstable: xsi < -1.574
3414  auto const psim2 = [=] AMREX_GPU_DEVICE (const amrex::Real &xsi, const amrex::Real &xsim0, const amrex::Real &xm0) -> amrex::Real {
3415  return std::log(xsim / xsim0) - psim1(xm, xm0) + amrex::Real(1.14)*(std::pow(-xsi, amrex::Real(0.3333)) - std::pow(-xsim, amrex::Real(0.3333)));
3416  };
3417  auto const psih2 = [=] AMREX_GPU_DEVICE (const amrex::Real &xsi, const amrex::Real &xsih0, const amrex::Real &xh0) -> amrex::Real {
3418  return std::log(xsih / xsih0) - psih1(xh, xh0) + amrex::Real(0.8)*(std::pow(-xsi, amrex::Real(0.3333)) - std::pow(-xsih, amrex::Real(0.3333)));
3419  };
3420  // stable: 0 < xsi < 1
3421  auto psim3 = [] AMREX_GPU_DEVICE (const amrex::Real &xsi, const amrex::Real &xsim0) -> amrex::Real {
3422  return -amrex::Real(5.0) * (xsi - xsim0);
3423  };
3424  auto psih3 = [] AMREX_GPU_DEVICE (const amrex::Real &xsi, const amrex::Real &xsih0) -> amrex::Real {
3425  return -amrex::Real(5.0) * (xsi - xsih0);
3426  };
3427  // very stable: 0 < xsi < 1
3428  auto psim4 = [] AMREX_GPU_DEVICE (const amrex::Real &xsi, const amrex::Real &xsim0) -> amrex::Real {
3429  return std::log(amrex::Math::powi<5>(xsi) / xsim0) + amrex::Real(5.0) * (one - xsim0) + xsi - one;
3430  };
3431  auto psih4 = [] AMREX_GPU_DEVICE (const amrex::Real &xsi, const amrex::Real &xsih0) -> amrex::Real {
3432  return std::log(amrex::Math::powi<5>(xsi) / xsih0) + amrex::Real(5.0) * (one - xsih0) + xsi - one;
3433  };
3434 
3435 
3436  ParallelFor( box, [=] AMREX_GPU_DEVICE (int i, int j, int)
3437  {
3438  if (landmask_arr(i, j, 0) != 1) {
3439  return;
3440  }
3441 
3442  // Inputs:
3443  // ts = t_sfc
3444  // th = tref
3445  // qh = qr
3446  // qs = q_sfc
3447  // h = zref = height of ref level
3448  // z0 = z0_sfc = surface roughness length
3449  // disp = disp_hgt
3450 
3451  amrex::Real tsp = t_sfc_arr(i, j, 0) * std::pow(amrex::Real(1000.0)/pref_arr(i, j, 0), R_d / Cp_d);
3452  amrex::Real thp = tref_arr(i, j, 0) * std::pow(amrex::Real(1000.0) / pref_arr(i, j, 0), R_d / Cp_d);
3453 
3454  amrex::Real vel;
3455  // Add additional velocity depending on the stratification.
3456  // The two branches are intentionally identical (see issue #4487): following
3457  // SLM in gSAM (the global version of SAM), vel does not depend on the
3458  // stratification. The original SAM SLM added a stratification-dependent
3459  // velocity scale (U_c = 0.1 m/s if stable, U_c = 1 m/s if unstable); gSAM
3460  // instead uses vel = max(0.5, |U|). The if-else is kept so that a
3461  // stratification-dependent term can be added back here if needed.
3462  if ((thp - tsp) >= zero)
3463  {
3464  vel = std::max(amrex::Real(myhalf), std::sqrt(amrex::Math::powi<2>(ur_arr(i, j, 0)) + amrex::Math::powi<2>(vr_arr(i, j, 0))));
3465  }
3466  else
3467  {
3468  vel = std::max(amrex::Real(myhalf), std::sqrt(amrex::Math::powi<2>(ur_arr(i, j, 0)) + amrex::Math::powi<2>(vr_arr(i, j, 0))));
3469  }
3470  const Real d_zref = zref_arr(i,j,0);
3471 
3472  amrex::Real r = 9.81 / tsp * (thp * (1.0 + epsv * qr_arr(i, j, 0)) - tsp * (1.0 + epsv * q_sfc_arr(i, j, 0))) * (d_zref - disp_hgt_arr(i, j, 0)) / (vel*vel);
3473  // Cap the bulk Richardson number for stability of the iterations. The upper
3474  // cap of 0.5 follows SLM in gSAM; the original SAM SLM used 0.19 (0.2 in older
3475  // versions). See issue #4487.
3476  r = std::max(amrex::Real(-10.0), std::min(r, amrex::Real(myhalf)));
3477  // initial guess
3478  amrex::Real xsi, fm, fh, xsi1;
3479  amrex::Real xsim0, xsih0;
3480  amrex::Real mom_trans_coef, heat_trans_coef;
3481 
3482  //adjust z0 for high wind - gSAM-SLM
3483  amrex::Real z0_adjusted = z0_sfc_arr(i, j, 0) * std::pow(one + vel/amrex::Real(10.0), amrex::Real(-0.6));
3484  amrex::Real zt0 = 0.135 * z0_adjusted; // roughness length for scalars: ln(z0/zt0)=2 (from Garatte BL textbook, p.93)
3485 
3486  amrex::Real z0h = z0_adjusted / (d_zref - disp_hgt_arr(i, j, 0));
3487  // make sure (h - disp) is not negative, otherwise z0dym,z0dyh become nan
3488  AMREX_ALWAYS_ASSERT(d_zref - disp_hgt_arr(i, j, 0) > zero);
3489  amrex::Real zTh = zt0 / (d_zref - disp_hgt_arr(i, j, 0));
3490 
3491  amrex::Real zodym = std::log(one / z0h);
3492  amrex::Real zodyh = std::log(one / zTh);
3493 
3494  // first guess for xsi
3495  if (r > zero)
3496  {
3497  xsi = r * zodym / (one - amrex::Real(5.0) * std::min(amrex::Real(0.19),r));
3498  }
3499  else
3500  {
3501  xsi = r*zodym;
3502  }
3503 
3504  int niter = 0;
3505  amrex::Real error = 1000.0;
3506 
3507  while (error > errormax && niter < nitermax)
3508  {
3509  xsi1 = xsi;
3510  niter++;
3511  xsim0 = z0h * xsi;
3512  xsih0 = zTh * xsi;
3513 
3514  if (xsi < -0.01)
3515  {
3516  if (xsi >= xsim)
3517  {
3518  fm = zodym - psim1(xx(xsi), xx(xsim0));
3519  }
3520  else
3521  {
3522  fm = psim2(xsi, xsim0, xx(xsim0));
3523  }
3524 
3525  if (xsi >= xsih)
3526  {
3527  fh = zodyh - psih1(xx(xsi), xx(xsih0));
3528  }
3529  else
3530  {
3531  fh = psih2(xsi, xsih0, xx(xsih0));
3532  }
3533  }
3534  else if (xsi > 0.01)
3535  {
3536  if (xsi <= one)
3537  {
3538  fm = zodym - psim3(xsi, xsim0);
3539  fh = zodyh - psih3(xsi, xsih0);
3540  }
3541  else
3542  {
3543  fm = psim4(xsi, xsim0);
3544  fh = psih4(xsi, xsih0);
3545  }
3546  }
3547  else
3548  {
3549  fm = zodym;
3550  fh = zodyh;
3551  }
3552 
3553  xsi = r * fm * fm / fh;
3554  error = std::abs(xsi - xsi1);
3555  }
3556 
3557  // limit fm and fh to avoid too large fluxes especially over large surface roughness.
3558  // Basically, make the maximum slowdown of the velocity not bigger than 50% in one timestep
3559  amrex::Real fm0 = std::sqrt((kk*kk)*vel*dt / myhalf / d_zref);
3560  fm = std::max(fm0, fm);
3561  fh = std::max(fh, fh/fm*fm0);
3562 
3563  // drag coefficient C_D = k**2/fm**2
3564  // heat transfer coefficient C_H = k**2/fm/fh
3565  mom_trans_coef = (kk*kk) / (fm*fm);
3566  heat_trans_coef = (kk*kk) / fm / fh;
3567  ustar_arr(i, j, 0) = std::sqrt(mom_trans_coef) * vel;
3568 
3569  // set ustar > 0.2 to avoid too calm conditions at night for the turbulent transfer
3570  //ustar_arr(i, j, 0) = std::max(0.2, ustar_arr(i, j, 0));
3571  ustar_arr(i, j, 0) = std::sqrt(ustar_arr(i, j, 0) * ustar_arr(i, j, 0)+amrex::Real(0.05)*amrex::Real(0.05)); // following gSAM-SLM
3572  tstar_arr(i, j, 0) = -kk * (thp - tsp) / fh;
3573 
3574  // recompute Ch and Cd for onsistency
3575  mom_trans_coef = amrex::Math::powi<2>(ustar_arr(i, j, 0)/vel);
3576  heat_trans_coef = (kk/fh) * (ustar_arr(i, j, 0)/vel);
3577  amrex::ignore_unused(heat_trans_coef);
3578 
3579  // aerodynamic resistance between surface and reference level
3580  r_a_arr(i, j, 0) = fh / kk / ustar_arr(i, j, 0);
3581 
3582 
3583  amrex::Real vel_m = vel;
3584  amrex::Real taux_sfc = -1.0 * mom_trans_coef * vel_m * ur_arr(i, j, 0) * (100.0 * pref_arr(i, j, 0) / 287.0 / tref_arr(i, j, 0));
3585  amrex::Real tauy_sfc = -1.0 * mom_trans_coef * vel_m * vr_arr(i, j, 0) * (100.0 * pref_arr(i, j, 0) / 287.0 / tref_arr(i, j, 0));
3586 
3587  // Output variables
3588  flbu_arr(i, j, d_khi_lsm) = taux_sfc;
3589  flbv_arr(i, j, d_khi_lsm) = tauy_sfc;
3590  ustar_arr(i, j, d_khi_lsm) = ustar_arr(i, j, 0);
3591  tstar_arr(i, j, d_khi_lsm) = tstar_arr(i, j, 0);
3592  });
3593 }
static constexpr amrex::Real epsv
Definition: ERF_SLM.H:774
Here is the call graph for this function:

◆ twostream_noahmp()

AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void SLM::twostream_noahmp ( int  ib,
int  ic,
int  vegtyp,
amrex::Real  cosz,
amrex::Real  vai,
amrex::Real  fwet,
amrex::Real  t,
const amrex::Real *  albgrd,
const amrex::Real *  albgri,
const amrex::Real *  rho,
const amrex::Real *  tau,
amrex::Real  fveg,
int  ist,
amrex::Real  xl,
amrex::Real  omegas_param,
amrex::Real  betads,
amrex::Real  betais,
int  opt_rad,
amrex::Real  rc,
amrex::Real  hvt,
amrex::Real  hvb,
amrex::Real  den,
amrex::Real *  fab,
amrex::Real *  fre,
amrex::Real *  ftd,
amrex::Real *  fti,
amrex::Real &  gdir,
amrex::Real *  frev,
amrex::Real *  freg,
amrex::Real &  bgap,
amrex::Real &  wgap,
amrex::Real &  xl_out,
amrex::Real &  chil_out,
amrex::Real &  phi1_out,
amrex::Real &  phi2_out 
)
static
5438 {
5439  // --------------------------------------------------------------------------------------------------
5440  // input
5441  // IST !surface type
5442  // IB !waveband number
5443  // IC !0=unit incoming direct; 1=unit incoming diffuse
5444  // VEGTYP !vegetation type
5445  // COSZ !cosine of direct zenith angle (0-1)
5446  // VAI !one-sided leaf+stem area index (m2/m2)
5447  // FWET !fraction of lai, sai that is wetted (-)
5448  // T !surface temperature (k)
5449  // ALBGRD !direct albedo of underlying surface (-)
5450  // ALBGRI !diffuse albedo of underlying surface (-)
5451  // RHO !leaf+stem reflectance
5452  // TAU !leaf+stem transmittance
5453  // FVEG !green vegetation fraction [0.0-1.0]
5454 
5455  // output
5456  // FAB !flux abs by veg layer (per unit incoming flux)
5457  // FRE !flux refl above veg layer (per unit incoming flux)
5458  // FTD !down dir flux below veg layer (per unit in flux)
5459  // FTI !down dif flux below veg layer (per unit in flux)
5460  // GDIR !projected leaf+stem area in solar direction
5461  // FREV !flux reflected by veg layer (per unit incoming flux)
5462  // FREG !flux reflected by ground (per unit incoming flux)
5463  // BGAP !between canopy gap fraction for beam (-)
5464  // WGAP !within canopy gap fraction for beam (-)
5465 
5466  // local
5467  amrex::Real omega; //fraction of intercepted radiation that is scattered
5468  amrex::Real omegal; //omega for leaves
5469  amrex::Real betai; //upscatter parameter for diffuse radiation
5470  amrex::Real betail; //betai for leaves
5471  amrex::Real betad; //upscatter parameter for direct beam radiation
5472  amrex::Real betadl; //betad for leaves
5473  amrex::Real ext; //optical depth of direct beam per unit leaf area
5474  amrex::Real avmu; //average diffuse optical depth
5475  amrex::Real coszi; //0.001 <= cosz <= 1.000
5476  amrex::Real asu; //single scattering albedo
5477  amrex::Real chil; // -0.4 <= xl <= 0.6
5478 
5479  amrex::Real tmp0,tmp1,tmp2,tmp3,tmp4,tmp5,tmp6,tmp7,tmp8,tmp9;
5480  amrex::Real p1,p2,p3,p4,s1,s2,u1,u2,u3;
5481  amrex::Real b,c,d,d1,d2,f,h,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10;
5482  amrex::Real phi1,phi2,sigma_ts;
5483  amrex::Real ftds,ftis,fres;
5484  amrex::Real denfveg;
5485  //jref:start
5486  amrex::Real freveg,frebar;
5487  //jref:end
5488 
5489  // variables for the modified two-stream scheme
5490  // Niu and Yang (2004), JGR
5491  constexpr amrex::Real PAI = 3.14159265;
5492  constexpr amrex::Real TFRZ = 273.16; // freezing/melting point (k)
5493  amrex::Real hd; //crown depth (m)
5494  amrex::Real bb; //vertical crown radius (m)
5495  amrex::Real thetap; //angle conversion from SZA
5496  amrex::Real fa; //foliage volume density (m-1)
5497  amrex::Real newvai; //effective LSAI (-)
5498  // Set below for opt_rad = 1, 2 or 3 (the only values); initialised so that no
5499  // path reads them unset.
5500  amrex::Real kopen = one; //gap fraction for diffue light (-)
5501  amrex::Real gap = one; //total gap fraction for beam ( <=1-shafac )
5502 
5503  // -----------------------------------------------------------------
5504  // compute within and between gaps
5505  if(vai == zero) {
5506  gap = one;
5507  kopen = one;
5508  } else {
5509  if(opt_rad == 1) {
5510  denfveg = -std::log(std::max(one-fveg,amrex::Real(0.01)))/(PAI*rc*rc);
5511  hd = hvt - hvb;
5512  bb = myhalf * hd;
5513  thetap = std::atan(bb/rc * std::tan(std::acos(std::max(amrex::Real(0.01),cosz))) );
5514  // BGAP = EXP(-parameters%DEN * PAI * parameters%RC**2/COS(THETAP) )
5515  bgap = std::exp(-denfveg * PAI * rc*rc/std::cos(thetap) );
5516  fa = vai/(1.33 * PAI * rc*rc*rc *(bb/rc)*denfveg);
5517  newvai = hd*fa;
5518  wgap = (one-bgap) * std::exp(-myhalf*newvai/cosz);
5519  gap = std::min(one-fveg, bgap+wgap);
5520 
5521  kopen = 0.05;
5522  }
5523 
5524  if(opt_rad == 2) {
5525  gap = zero;
5526  kopen = zero;
5527  }
5528 
5529  if(opt_rad == 3) {
5530  gap = 1.0-fveg;
5531  kopen = 1.0-fveg;
5532  }
5533  }
5534 
5535  // calculate two-stream parameters OMEGA, BETAD, BETAI, AVMU, GDIR, EXT.
5536  // OMEGA, BETAD, BETAI are adjusted for snow. values for OMEGA*BETAD
5537  // and OMEGA*BETAI are calculated and then divided by the new OMEGA
5538  // because the product OMEGA*BETAI, OMEGA*BETAD is used in solution.
5539  // also, the transmittances and reflectances (TAU, RHO) are linear
5540  // weights of leaf and stem values.
5541 
5542  coszi = std::max(amrex::Real(0.001), cosz);
5543  chil = std::min( std::max(xl, amrex::Real(-0.4)), amrex::Real(0.6));
5544  if (std::abs(chil) <= 0.01) chil = 0.01;
5545  phi1 = myhalf - 0.633*chil - 0.330*chil*chil;
5546  phi2 = 0.877 * (one-two*phi1);
5547  gdir = phi1 + phi2*coszi;
5548 
5549  // Output diagnostics
5550  xl_out = xl;
5551  chil_out = chil;
5552  phi1_out = phi1;
5553  phi2_out = phi2;
5554  ext = gdir/coszi;
5555  avmu = ( one - phi1/phi2 * std::log((phi1+phi2)/phi1) ) / phi2;
5556  omegal = rho[ib] + tau[ib];
5557  tmp0 = gdir + phi2*coszi;
5558  tmp1 = phi1*coszi;
5559  asu = myhalf*omegal*gdir/tmp0 * ( one-tmp1/tmp0*std::log((tmp1+tmp0)/tmp1) );
5560  betadl = (1.0+avmu*ext)/(omegal*avmu*ext)*asu;
5561  betail = myhalf * ( rho[ib]+tau[ib] + (rho[ib]-tau[ib])
5562  * ((one+chil)/two)*((one+chil)/two) ) / omegal;
5563 
5564  // adjust omega, betad, and betai for intercepted snow
5565 
5566  if (t > TFRZ) { //no snow
5567  tmp0 = omegal;
5568  tmp1 = betadl;
5569  tmp2 = betail;
5570  } else {
5571  tmp0 = (1.0-fwet)*omegal + fwet*omegas_param;
5572  tmp1 = ( (1.0-fwet)*omegal*betadl + fwet*omegas_param*betads ) / tmp0;
5573  tmp2 = ( (1.0-fwet)*omegal*betail + fwet*omegas_param*betais ) / tmp0;
5574  }
5575 
5576  omega = tmp0;
5577  betad = tmp1;
5578  betai = tmp2;
5579 
5580  // absorbed, reflected, transmitted fluxes per unit incoming radiation
5581 
5582  b = 1.0 - omega + omega*betai;
5583  c = omega*betai;
5584  tmp0 = avmu*ext;
5585  d = tmp0 * omega*betad;
5586  f = tmp0 * omega*(1.0-betad);
5587  tmp1 = b*b - c*c;
5588  h = std::sqrt(tmp1) / avmu;
5589  sigma_ts = tmp0*tmp0 - tmp1;
5590  if ( std::abs(sigma_ts) < 1.0e-6 ) sigma_ts = (sigma_ts >= zero) ? 1.0e-6 : -1.0e-6;
5591  p1 = b + avmu*h;
5592  p2 = b - avmu*h;
5593  p3 = b + tmp0;
5594  p4 = b - tmp0;
5595  s1 = std::exp(-h*vai);
5596  s2 = std::exp(-ext*vai);
5597  if (ic == 0) {
5598  u1 = b - c/albgrd[ib];
5599  u2 = b - c*albgrd[ib];
5600  u3 = f + c*albgrd[ib];
5601  } else {
5602  u1 = b - c/albgri[ib];
5603  u2 = b - c*albgri[ib];
5604  u3 = f + c*albgri[ib];
5605  }
5606  tmp2 = u1 - avmu*h;
5607  tmp3 = u1 + avmu*h;
5608  d1 = p1*tmp2/s1 - p2*tmp3*s1;
5609  tmp4 = u2 + avmu*h;
5610  tmp5 = u2 - avmu*h;
5611  d2 = tmp4/s1 - tmp5*s1;
5612  h1 = -d*p4 - c*f;
5613  tmp6 = d - h1*p3/sigma_ts;
5614  tmp7 = ( d - c - h1/sigma_ts*(u1+tmp0) ) * s2;
5615  h2 = ( tmp6*tmp2/s1 - p2*tmp7 ) / d1;
5616  h3 = - ( tmp6*tmp3*s1 - p1*tmp7 ) / d1;
5617  h4 = -f*p3 - c*d;
5618  tmp8 = h4/sigma_ts;
5619  tmp9 = ( u3 - tmp8*(u2-tmp0) ) * s2;
5620  h5 = - ( tmp8*tmp4/s1 + tmp9 ) / d2;
5621  h6 = ( tmp8*tmp5*s1 + tmp9 ) / d2;
5622  h7 = (c*tmp2) / (d1*s1);
5623  h8 = (-c*tmp3*s1) / d1;
5624  h9 = tmp4 / (d2*s1);
5625  h10 = (-tmp5*s1) / d2;
5626 
5627  // downward direct and diffuse fluxes below vegetation
5628  // Niu and Yang (2004), JGR.
5629 
5630  if (ic == 0) {
5631  ftds = s2 *(1.0-gap) + gap;
5632  ftis = (h4*s2/sigma_ts + h5*s1 + h6/s1)*(1.0-gap);
5633  } else {
5634  ftds = zero;
5635  ftis = (h9*s1 + h10/s1)*(1.0-kopen) + kopen;
5636  }
5637  ftd[ib] = ftds;
5638  fti[ib] = ftis;
5639 
5640  // flux reflected by the surface (veg. and ground)
5641 
5642  if (ic == 0) {
5643  fres = (h1/sigma_ts + h2 + h3)*(1.0-gap ) + albgrd[ib]*gap;
5644  freveg = (h1/sigma_ts + h2 + h3)*(1.0-gap );
5645  frebar = albgrd[ib]*gap; //jref - separate veg. and ground reflection
5646  } else {
5647  fres = (h7 + h8) *(1.0-kopen) + albgri[ib]*kopen;
5648  freveg = (h7 + h8) *(1.0-kopen) + albgri[ib]*kopen;
5649  frebar = zero; //jref - separate veg. and ground reflection
5650  }
5651  fre[ib] = fres;
5652 
5653  frev[ib] = freveg;
5654  freg[ib] = frebar;
5655 
5656  // flux absorbed by vegetation
5657 
5658  fab[ib] = 1.0 - fre[ib] - (1.0-albgrd[ib])*ftd[ib]
5659  - (1.0-albgri[ib])*fti[ib];
5660 }
@ omega
Definition: ERF_Morrison.H:57
@ xl
Definition: ERF_WSM6.H:345
@ tmp3
Definition: ERF_AdvanceWSM6.cpp:119
@ tmp1
Definition: ERF_AdvanceWSM6.cpp:117
@ tmp2
Definition: ERF_AdvanceWSM6.cpp:118

◆ Update_Lsm_Vars()

void SLM::Update_Lsm_Vars ( amrex::MultiFab &  cons_in,
amrex::MultiFab &  u_in,
amrex::MultiFab &  v_in 
)
inlineoverridevirtual

Reimplemented from NullSurf.

478  {
479  this->Copy_State_to_Lsm(cons_in, u_in, v_in);
480  if (first_step) {
482  this->init_wrfinput_vars();
483  } else if (!use_wrfinput) {
484  this->init_slm_vars();
485  }
486  } else if (init_from_atmos_pending) {
487  // This level was made from a source level whose reference-level fields
488  // had not been filled yet, so t_canop, t_cas and q_cas were set from
489  // zeros; set them again now that tref, qref and pref hold the state.
490  this->init_slm_vars();
491  }
492  init_from_atmos_pending = false;
493  }
void Copy_State_to_Lsm(const amrex::MultiFab &cons_in, const amrex::MultiFab &u_in, const amrex::MultiFab &v_in) override
Definition: ERF_SLM.cpp:4837
Here is the call graph for this function:

◆ Update_State_Vars()

void SLM::Update_State_Vars ( amrex::MultiFab &  cons_in)
inlineoverridevirtual

Reimplemented from NullSurf.

499  {
500  this->Copy_Lsm_to_State(cons_in);
501  }
void Copy_Lsm_to_State(amrex::MultiFab &cons_in) override
Definition: ERF_SLM.cpp:5069
Here is the call graph for this function:

◆ UpdateLAI()

void SLM::UpdateLAI ( const amrex::MFIter &  mfi)

Updates LAI and SAI based on the current simulation time and monthly values from the LAI and SAI tables.

2859 {
2860  if (interpolate_lai) {
2861  Box box = mfi.tilebox();
2862  box.makeSlab(2, 0);
2863 
2864  auto landmask_arr = landmask.const_array(mfi);
2865  auto landtype_arr = landtype.const_array(mfi);
2866  // auto vegetype_arr = vegetype.const_array(mfi);
2867  auto LAI_arr = LAI.array(mfi);
2868  auto SAI_arr = SAI.array(mfi);
2869 
2870  // Update the day and month
2871  time_t timestamp = time_t(time + start_time);
2872  struct tm *timeinfo = gmtime(&timestamp);
2873 
2874  m_orbital_year = timeinfo->tm_year + 1900;
2875  m_orbital_mon = timeinfo->tm_mon + 1;
2876  m_orbital_day = timeinfo->tm_mday;
2877  m_orbital_sec = timeinfo->tm_hour*3600 + timeinfo->tm_min*60 + timeinfo->tm_sec;
2878 
2879  static constexpr double dpy[] = {0.0 , 31.0, 59.0, 90.0, 120.0, 151.0,
2880  181.0, 212.0, 243.0, 273.0, 304.0, 334.0};
2881  bool leap = (m_orbital_year % 4 == 0 && (!(m_orbital_year % 100 == 0) || (m_orbital_year % 400 == 0))) ? true : false;
2882  // In a leap year, 29 February shifts the start of every month from March on by
2883  // one day; the dates, the month boundaries and the year end must all agree on it.
2884  const Real leap_day = (leap) ? one : zero;
2885  auto month_start = [&] (const int m) { return dpy[m] + ((m >= 2) ? leap_day : zero); };
2886 
2887  int curr_mon = m_orbital_mon - 1;
2888  int next_mon = curr_mon + 1;
2889  m_calday = month_start(curr_mon) + (m_orbital_day-1.0) + m_orbital_sec/86400.0;
2890 
2891  Real t0 = month_start(curr_mon);
2892  Real t1;
2893  if (next_mon > 11) {
2894  // December interpolates toward the following January
2895  next_mon = 0;
2896  t1 = 365.0 + leap_day;
2897  } else {
2898  t1 = month_start(next_mon);
2899  }
2900 
2901  const Real d_calday = m_calday;
2902 
2903  // Setup device interpolation points
2904  Real *d_lai_curr_ptr = d_lai_curr.data();
2905  Real *d_lai_next_ptr = d_lai_next.data();
2906  Real *d_sai_curr_ptr = d_sai_curr.data();
2907  Real *d_sai_next_ptr = d_sai_next.data();
2908  Gpu::copyAsync(Gpu::hostToDevice, lai_table[curr_mon].data(), lai_table[curr_mon].data()+num_landtypes, d_lai_curr_ptr);
2909  Gpu::copyAsync(Gpu::hostToDevice, lai_table[next_mon].data(), lai_table[next_mon].data()+num_landtypes, d_lai_next_ptr);
2910  Gpu::copyAsync(Gpu::hostToDevice, sai_table[curr_mon].data(), sai_table[curr_mon].data()+num_landtypes, d_sai_curr_ptr);
2911  Gpu::copyAsync(Gpu::hostToDevice, sai_table[next_mon].data(), sai_table[next_mon].data()+num_landtypes, d_sai_next_ptr);
2912  Gpu::streamSynchronize();
2913 
2914  // Update LAI and SAI
2915  ParallelFor(box, [=] AMREX_GPU_DEVICE (int i, int j, int)
2916  {
2917  if (landmask_arr(i, j, 0) == 1) {
2918  //if (vegetype_arr(i, j, 0) == 0) { // todo: check, urban cells are not "vegetated", so LAI is incorrect there?
2919  if (landtype_arr(i,j,0) == 16) {
2920  // baresoil point, so no LAI or SAI
2921  LAI_arr(i,j,0) = zero;
2922  SAI_arr(i,j,0) = zero;
2923  //} else if (vegetype_arr(i, j, 0) == 1) {
2924  } else {
2925  // vegetation
2926  const int ltype = landtype_arr(i,j,0) - 1;
2927 
2928  const Real lai_x = d_lai_curr_ptr[ltype];
2929  const Real lai_y = d_lai_next_ptr[ltype];
2930 
2931  const Real sai_x = d_sai_curr_ptr[ltype];
2932  const Real sai_y = d_sai_next_ptr[ltype];
2933 
2934  // Update the LAI and SAI using interpolated values from the monthly tables
2935  // NOTE: LAI + SAI is computed later when needed
2936  LAI_arr(i,j,0) = linear_interp(t0, t1, d_calday, lai_x, lai_y);
2937  SAI_arr(i,j,0) = linear_interp(t0, t1, d_calday, sai_x, sai_y);
2938  }
2939  }
2940  });
2941 
2942  UpdateLAIParameters(mfi);
2943  } else if (use_wrf_lai) {
2944 
2945  Box box = mfi.tilebox();
2946  box.makeSlab(2, 0);
2947 
2948  auto landmask_arr = landmask.const_array(mfi);
2949  auto landtype_arr = landtype.const_array(mfi);
2950  auto veg_frac_arr = lsm_fab_vars[LsmVar_SLM::veg_frac]->array(mfi);
2951  auto veg_frac_min_arr = lsm_fab_vars[LsmVar_SLM::veg_frac_min]->const_array(mfi);
2952  auto veg_frac_max_arr = lsm_fab_vars[LsmVar_SLM::veg_frac_max]->const_array(mfi);
2953 
2954  auto LAI_arr = LAI.array(mfi);
2955 
2956  auto const& d_params = d_param_table.const_table();
2957 
2958  // Update LAI from vegetation fraction.
2959  ParallelFor(box, [=] AMREX_GPU_DEVICE (int i, int j, int)
2960  {
2961  if (landmask_arr(i, j, 0) == 1) {
2962 
2963  const int ltype = landtype_arr(i,j,0) - 1; // shift by one to match table index (i.e, types 1-20 -> 0-19)
2964 
2965  if (ltype + 1 == 13) { // urban cells
2966  veg_frac_arr(i,j,0) = d_params(ltype,1);
2967  }
2968 
2969  // LAI min = col 8, lai max = col 9
2970  // emiss min = col 10, max = col 11
2971  // albedo min = col 12, max = col 13
2972 
2973  if (veg_frac_arr(i,j,0) >= veg_frac_max_arr(i,j,0)) {
2974  //emis_sfc_arr(i,j,0) = emissmax;
2975  LAI_arr(i,j,0) = d_params(ltype, 9);
2976  } else if (veg_frac_arr(i,j,0) <= veg_frac_min_arr(i,j,0)) {
2977  //emis_sfc_arr(i,j,0) = emissmin;
2978  LAI_arr(i,j,0) = d_params(ltype, 8);
2979  } else {
2980  if (veg_frac_max_arr(i,j,0) > veg_frac_min_arr(i,j,0)) {
2981 
2982  Real interp_frac = (veg_frac_arr(i,j,0) - veg_frac_min_arr(i,j,0)) / (veg_frac_max_arr(i,j,0) - veg_frac_min_arr(i,j,0));
2983  interp_frac = std::min(std::max(interp_frac, amrex::Real(zero)), amrex::Real(one)); // bound between 0.0 and 1.0
2984 
2985  // Scale emissivitiy and LAI between min/max by interp_frac
2986  //emis_sfc_arr(i,j,0) = ( (1.0 - interp_frac) * d_params(ltype, 10)) + interp_frac * d_params(ltype, 11);
2987 
2988  LAI_arr(i,j,0) = ( (1.0 - interp_frac) * d_params(ltype, 8)) + interp_frac * d_params(ltype, 9);
2989  } else {
2990  // emis_sfc_arr(i,j,0) = 0.5 * d_params(ltype,10) + 0.5 * d_params(ltype, 11);
2991  LAI_arr(i,j,0) = myhalf * d_params(ltype, 8) + myhalf * d_params(ltype, 9);
2992  }
2993  }
2994 
2995  }
2996  });
2997 
2998  UpdateLAIParameters(mfi);
2999  }
3000 }
Here is the call graph for this function:

◆ UpdateLAIParameters()

void SLM::UpdateLAIParameters ( const amrex::MFIter &  mfi)

Updates radiation parameters related to LAI

3006 {
3007  Box box = mfi.tilebox();
3008  box.makeSlab(2, 0);
3009 
3010  auto IR_emis_vege_arr = IR_emis_vege.array(mfi);
3011  auto phi_1_arr = phi_1.array(mfi);
3012  auto phi_2_arr = phi_2.array(mfi);
3013  auto precip_extinc_arr = precip_extinc.array(mfi);
3014  auto mw_mx_arr = mw_mx.array(mfi);
3015  auto LAI_arr = LAI.array(mfi);
3016  auto BAI_arr = BAI.array(mfi);
3017  auto ztop_arr = ztop.array(mfi);
3018  auto Khai_L_arr = Khai_L.array(mfi);
3019  auto landmask_arr = landmask.const_array(mfi);
3020  auto vegetype_arr = vegetype.const_array(mfi);
3021 
3022  ParallelFor(box, [=] AMREX_GPU_DEVICE (int i, int j, int)
3023  {
3024  if (landmask_arr(i, j, 0) == 1) {
3025  if (vegetype_arr(i, j, 0) == 1) {
3026  // set minimum LAI for vegetated land
3027  LAI_arr(i, j, 0) = std::max(LAI_arr(i, j, 0), amrex::Real(0.001));
3028  }
3029 
3030  phi_1_arr(i, j, 0) = myhalf - 0.633 * Khai_L_arr(i, j, 0) - 0.33 * amrex::Math::powi<2>(Khai_L_arr(i, j, 0));
3031  phi_2_arr(i, j, 0) = 0.877 * (one - two * phi_1_arr(i, j, 0));
3032  IR_emis_vege_arr(i, j, 0) = 0.97 * (one - std::exp(-(phi_1_arr(i, j, 0) + phi_2_arr(i, j, 0)) * LAI_arr(i, j, 0)));
3033  precip_extinc_arr(i, j, 0) = phi_1_arr(i, j, 0) + phi_2_arr(i, j, 0);
3034  //mw_mx_arr(i, j, 0) = 0.1 * LAI_arr(i, j, 0);
3035  mw_mx_arr(i, j, 0) = 0.1 * LAI_arr(i, j, 0) + ztop_arr(i, j, 0) * std::pow(four * PI * BAI_arr(i, j, 0) / amrex::Real(43560.0), myhalf);
3036  }
3037  });
3038 }
Here is the call graph for this function:

◆ validate_parameter_tables()

void SLM::validate_parameter_tables ( )

Validates the selected Noah-MP parameter blocks before copying them to the device.

2462 {
2463  using ParameterBlock = SLMParameterTable::mapped_type;
2464 
2465  const std::string soil_dataset_lower = amrex::toLower(soil_dataset);
2466  const std::string veg_dataset_lower = amrex::toLower(veg_dataset);
2467 
2468  std::string soil_param_key;
2469  if (soil_dataset_lower == "stas_ruc") {
2470  soil_param_key = "noahmp_soil_stas_ruc_parameters";
2471  } else if (soil_dataset_lower == "stas") {
2472  soil_param_key = "noahmp_soil_stas_parameters";
2473  } else {
2474  amrex::Abort("SLM: unrecognized soil dataset type '" + soil_dataset + "'");
2475  }
2476 
2477  std::string veg_param_key;
2478  std::string veg_category_key;
2479  if (veg_dataset_lower == "usgs") {
2480  veg_param_key = "noahmp_usgs_parameters";
2481  veg_category_key = "noahmp_usgs_veg_categories";
2482  } else if (veg_dataset_lower == "modis") {
2483  veg_param_key = "noahmp_modis_parameters";
2484  veg_category_key = "noahmp_modis_veg_categories";
2485  } else {
2486  amrex::Abort("SLM: unrecognized vegetation dataset type '" + veg_dataset + "'");
2487  }
2488 
2489  auto require_block = [this](const std::string& block_name) -> const ParameterBlock& {
2490  auto block = full_params.find(block_name);
2491  if (block == full_params.end()) {
2492  amrex::Abort("SLM: parameter file is missing block '" + block_name + "'");
2493  }
2494  return block->second;
2495  };
2496 
2497  auto find_parameter = [](const ParameterBlock& block, const std::string& block_name,
2498  const std::string& parameter_name) -> const Vector<Real>*
2499  {
2500  const Vector<Real>* values = nullptr;
2501  for (const auto& parameter : block) {
2502  if (parameter.first == parameter_name) {
2503  if (values != nullptr) {
2504  amrex::Abort("SLM: parameter '" + parameter_name + "' is duplicated in block '" + block_name + "'");
2505  }
2506  values = &parameter.second;
2507  }
2508  }
2509  return values;
2510  };
2511 
2512  auto require_parameter = [&find_parameter](const ParameterBlock& block, const std::string& block_name,
2513  const std::string& parameter_name, int expected_size) -> const Vector<Real>& {
2514  const Vector<Real>* values = find_parameter(block, block_name, parameter_name);
2515  if (values == nullptr || values->empty()) {
2516  amrex::Abort("SLM: parameter file is missing nonempty parameter '" + parameter_name +
2517  "' in block '" + block_name + "'");
2518  }
2519  if (expected_size >= 0 && static_cast<int>(values->size()) != expected_size) {
2520  amrex::Abort("SLM: parameter '" + parameter_name + "' in block '" + block_name +
2521  "' has " + std::to_string(values->size()) +
2522  " values; expected " + std::to_string(expected_size));
2523  }
2524  return *values;
2525  };
2526 
2527  auto validate_parameter_value = [](const std::string& block_name,
2528  const std::string& parameter_name,
2529  const Real value,
2530  const std::optional<Real> lower = std::nullopt,
2531  const std::optional<Real> upper = std::nullopt,
2532  const bool lower_strict = false)
2533  {
2534  if (!std::isfinite(value) ||
2535  (lower && (lower_strict ? value <= *lower : value < *lower)) ||
2536  (upper && value > *upper)) {
2537  amrex::Abort("SLM: invalid value for parameter '" + parameter_name +
2538  "' in block '" + block_name + "'");
2539  }
2540  };
2541 
2542  const ParameterBlock& veg_categories = require_block(veg_category_key);
2543  const Vector<Real>& nveg_values = require_parameter(veg_categories, veg_category_key, "nveg", 1);
2544  if (nveg_values[0] < one || nveg_values[0] != std::floor(nveg_values[0])) {
2545  amrex::Abort("SLM: parameter 'nveg' in block '" + veg_category_key + "' must be a positive integer");
2546  }
2547  num_veg_params = static_cast<int>(nveg_values[0]);
2548 
2549  const ParameterBlock& veg_params = require_block(veg_param_key);
2550  for (const auto& parameter : veg_params) {
2551  if (parameter.second.empty()) {
2552  amrex::Abort("SLM: parameter '" + parameter.first + "' in block '" + veg_param_key + "' is empty");
2553  }
2554  if (parameter.first != "nveg" && static_cast<int>(parameter.second.size()) > 1 &&
2555  static_cast<int>(parameter.second.size()) != num_veg_params) {
2556  amrex::Abort("SLM: parameter '" + parameter.first + "' in block '" + veg_param_key +
2557  "' has " + std::to_string(parameter.second.size()) +
2558  " values; expected " + std::to_string(num_veg_params) +
2559  " vegetation-category values");
2560  }
2561  std::optional<Real> lower;
2562  std::optional<Real> upper;
2563  if (parameter.first == "rhol_vis" || parameter.first == "rhol_nir" ||
2564  parameter.first == "rhos_vis" || parameter.first == "rhos_nir" ||
2565  parameter.first == "taul_vis" || parameter.first == "taul_nir" ||
2566  parameter.first == "taus_vis" || parameter.first == "taus_nir") {
2567  lower = zero;
2568  upper = one;
2569  } else if (parameter.first == "rs" || parameter.first == "rgl" ||
2570  parameter.first == "nroot" || parameter.first == "hvt" ||
2571  parameter.first == "hvb" || parameter.first == "z0mvt" ||
2572  parameter.first == "cbiom" || parameter.first == "dleaf" ||
2573  parameter.first == "rc" || parameter.first == "den" ||
2574  parameter.first == "cwpvt") {
2575  lower = zero;
2576  }
2577  for (const Real value : parameter.second) {
2578  validate_parameter_value(veg_param_key, parameter.first, value, lower, upper);
2579  }
2580  }
2581 
2582  const Vector<Real>* hvt_values = find_parameter(veg_params, veg_param_key, "hvt");
2583  if (hvt_values != nullptr && static_cast<int>(hvt_values->size()) == num_veg_params) {
2584  const Vector<Real>* hvb_values = find_parameter(veg_params, veg_param_key, "hvb");
2585  if (hvb_values != nullptr && static_cast<int>(hvb_values->size()) == num_veg_params) {
2586  for (int i = 0; i < num_veg_params; ++i) {
2587  if ((*hvt_values)[i] > zero && (*hvt_values)[i] <= (*hvb_values)[i]) {
2588  amrex::Abort("SLM: HVT must be greater than HVB for canopy categories in block '" +
2589  veg_param_key + "'");
2590  }
2591  }
2592  }
2593  const Vector<std::string> canopy_parameters = {
2594  "rgl", "z0mvt", "cwpvt", "dleaf", "rc", "den"
2595  };
2596  for (const std::string& parameter_name : canopy_parameters) {
2597  const Vector<Real>* values = find_parameter(veg_params, veg_param_key, parameter_name);
2598  if (values == nullptr || static_cast<int>(values->size()) != num_veg_params) {
2599  continue;
2600  }
2601  for (int i = 0; i < num_veg_params; ++i) {
2602  if ((*hvt_values)[i] > zero) {
2603  validate_parameter_value(veg_param_key, parameter_name, (*values)[i], 0.0,
2604  std::nullopt, true);
2605  }
2606  }
2607  }
2608  }
2609 
2610  const ParameterBlock& soil_categories = require_block("noahmp_stas_soil_categories");
2611  const Vector<Real>& slcats_values = require_parameter(
2612  soil_categories, "noahmp_stas_soil_categories", "slcats", 1);
2613  if (slcats_values[0] < one || slcats_values[0] != std::floor(slcats_values[0])) {
2614  amrex::Abort("SLM: parameter 'slcats' in block 'noahmp_stas_soil_categories' must be a positive integer");
2615  }
2616  num_soil_params = static_cast<int>(slcats_values[0]);
2617 
2618  const ParameterBlock& soil_params = require_block(soil_param_key);
2619  require_parameter(soil_params, soil_param_key, "maxsmc", num_soil_params);
2620 
2621  const Vector<std::string> required_soil_parameters = {
2622  "maxsmc", "refsmc", "wltsmc", "satpsi", "bb", "satdk"
2623  };
2624  for (const std::string& parameter_name : required_soil_parameters) {
2625  require_parameter(soil_params, soil_param_key, parameter_name, num_soil_params);
2626  }
2627  for (const auto& parameter : soil_params) {
2628  if (parameter.second.empty()) {
2629  amrex::Abort("SLM: parameter '" + parameter.first + "' in block '" + soil_param_key + "' is empty");
2630  }
2631  if (static_cast<int>(parameter.second.size()) > 1 && static_cast<int>(parameter.second.size()) != num_soil_params) {
2632  amrex::Abort("SLM: parameter '" + parameter.first + "' in block '" + soil_param_key +
2633  "' has " + std::to_string(parameter.second.size()) +
2634  " values; expected " + std::to_string(num_soil_params) +
2635  " soil-category values");
2636  }
2637  for (const Real value : parameter.second) {
2638  validate_parameter_value(soil_param_key, parameter.first, value, 0.0);
2639  }
2640  }
2641 
2642  const ParameterBlock& rad_params = require_block("noahmp_rad_parameters");
2643  const Vector<std::pair<std::string, int>> required_rad_parameters = {
2644  {"albsat_vis", 8}, {"albsat_nir", 8},
2645  {"albdry_vis", 8}, {"albdry_nir", 8},
2646  {"alblak", 2}, {"omegas", 2},
2647  {"betads", 1}, {"betais", 1}
2648  };
2649  for (const auto& parameter : required_rad_parameters) {
2650  require_parameter(rad_params, "noahmp_rad_parameters", parameter.first, parameter.second);
2651  }
2652  for (const auto& parameter : rad_params) {
2653  if (parameter.second.empty()) {
2654  amrex::Abort("SLM: parameter '" + parameter.first + "' in block 'noahmp_rad_parameters' is empty");
2655  }
2656  for (const Real value : parameter.second) {
2657  validate_parameter_value("noahmp_rad_parameters", parameter.first, value, 0.0, 1.0);
2658  }
2659  }
2660 
2661  const Vector<std::string> required_veg_parameters = {
2662  "rhol_vis", "rhol_nir", "rhos_vis", "rhos_nir",
2663  "taul_vis", "taul_nir", "taus_vis", "taus_nir",
2664  "rc", "hvb", "den"
2665  };
2666  for (const std::string& parameter_name : required_veg_parameters) {
2667  require_parameter(veg_params, veg_param_key, parameter_name, num_veg_params);
2668  }
2669 }

◆ WriteCheckpoint()

void SLM::WriteCheckpoint ( const int &  lev,
const std::string &  checkpointname 
) const
overridevirtual

Reimplemented from NullSurf.

389 {
390  auto check_start = amrex::second();
391 
392  // write SLM header
393  if (ParallelDescriptor::IOProcessor()) {
394 
395  amrex::Print() << " Writing SLM checkpoint at level " << lev << std::endl;
396 
397  std::string HeaderFileName(checkpointname + "/Level_" + std::to_string(lev) + "/SLM_Header");
398  VisMF::IO_Buffer io_buffer(VisMF::IO_Buffer_Size);
399  std::ofstream HeaderFile;
400  HeaderFile.rdbuf()->pubsetbuf(io_buffer.dataPtr(), io_buffer.size());
401  HeaderFile.open(HeaderFileName.c_str(), std::ofstream::out |
402  std::ofstream::trunc |
403  std::ofstream::binary);
404  if(! HeaderFile.good()) {
405  FileOpenFailed(HeaderFileName);
406  }
407 
408  HeaderFile.precision(17);
409 
410  // write out title line
411  HeaderFile << "Checkpoint file for SLM\n";
412 
413  // write out number of soil layers
414  HeaderFile << m_nz_lsm << "\n";
415 
416  // write out array of dz
417  for (int i = 0; i < static_cast<int>(m_dz_lsm.size()); ++i) {
418  HeaderFile << m_dz_lsm[i] << " ";
419  }
420  HeaderFile << "\n";
421  HeaderFile << "\n";
422 
423  // write out timestep and cal day
424  HeaderFile << m_dt << "\n";
425  HeaderFile << time << "\n";
426  HeaderFile << start_time << "\n";
427  HeaderFile << m_calday << "\n";
428  HeaderFile << m_orbital_year << "\n";
429  HeaderFile << m_orbital_mon << "\n";
430  HeaderFile << m_orbital_day << "\n";
431  HeaderFile << m_orbital_sec << "\n";
432  HeaderFile << "\n";
433 
434  // write out constants
435  // - tabs_s
436  // - t00
437  // - LAI0
438  // - tausoil
439  // - zref
440  // - z0_soil
441  // - mws_mx0
442  HeaderFile << tabs_s << "\n";
443  HeaderFile << t00 << "\n";
444  HeaderFile << LAI0 << "\n";
445  HeaderFile << tausoil << "\n";
446  HeaderFile << zref << "\n";
447  HeaderFile << z0_soil << "\n";
448  HeaderFile << mws_mx0 << "\n";
449  HeaderFile << Rc_max << "\n";
450  HeaderFile << T_opt << "\n";
451  HeaderFile << "\n";
452 
453  // write out flags
454  // - dosoiltnudge
455  // - dosoilwnudge
456  // - reserved compatibility slot for the removed set_from_file flag
457  // - use_param_file
458  // - interpolate_lai
459  // - use_wrf_lai
460  // - use_wrfinput
461  HeaderFile << dosoiltnudging << "\n";
462  HeaderFile << dosoilwnudging << "\n";
463  HeaderFile << 0 << "\n";
464  HeaderFile << use_param_file << "\n";
465  HeaderFile << interpolate_lai << "\n";
466  HeaderFile << use_wrf_lai << "\n";
467  HeaderFile << use_wrfinput << "\n";
468  HeaderFile << "\n";
469 
470  // Write box arrays for SLM
471  ba_lsm_2d.writeOn(HeaderFile);
472  HeaderFile << '\n';
473 
474  //
475  // Where SLM is in its lifecycle. With a wrfinput run the surface fields and
476  // the canopy-adjusted reference height are not built in Init -- they need the
477  // file, and are deferred to the first Advance -- so a checkpoint written
478  // before any step has been taken holds zrefxy at the scalar slm.zref
479  // placeholder rather than ztop + dz0. The reader used to declare the model
480  // initialized unconditionally, which is right for every later checkpoint and
481  // wrong for that one. Written last so a checkpoint from before this simply
482  // ends after the BoxArray and the reader keeps its old default.
483  //
484  HeaderFile << first_step << " " << wrfinput_initialized << "\n";
485  }
486 
487  amrex::ParallelDescriptor::Barrier();
488 
489  const std::string prefix = "SLM_";
490 
491  // Use distribution map from data vars, since those are already created and loaded by ERF
492  const DistributionMapping& dm = lsm_fab_vars[0]->DistributionMap();
493  IntVect ng = IntVect(0,0,0);
494  {
495  MultiFab imf_lm = amrex::ToMultiFab(landmask);
496  VisMF::Write(imf_lm, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "landmask"));
497 
498  MultiFab imf_lt = amrex::ToMultiFab(landtype);
499  VisMF::Write(imf_lt, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "landtype"));
500 
501  MultiFab imf_vt = amrex::ToMultiFab(vegetype);
502  VisMF::Write(imf_vt, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "vegetype"));
503  }
504 
505  {
506  // DST
507  MultiFab mf(soilt_vars.boxArray(),dm,SLM_DST::NumVars,0);
508  MultiFab::Copy(mf,soilt_vars,0,0,SLM_DST::NumVars,ng);
509  VisMF::Write(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "DST"));
510  }
511 
512  {
513  // DSW
514  MultiFab mf(soilw_vars.boxArray(),dm,SLM_DSW::NumVars,0);
515  MultiFab::Copy(mf,soilw_vars,0,0,SLM_DSW::NumVars,ng);
516  VisMF::Write(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "DSW"));
517  }
518 
519  {
520  // net_rad
521  MultiFab mf(net_rad.boxArray(),dm,SLM_NetRad::NumVars,0);
522  MultiFab::Copy(mf,net_rad,0,0,SLM_NetRad::NumVars,ng);
523  VisMF::Write(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "net_rad"));
524  }
525 
526  {
527  // slm_diag
528  MultiFab mf(slm_diag.boxArray(),dm,SLM_Diag::NumVars,0);
529  MultiFab::Copy(mf,slm_diag,0,0,SLM_Diag::NumVars,ng);
530  VisMF::Write(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "slm_diag"));
531  }
532 
533  MultiFab mf(ba_lsm_2d,dm,1,ng);
534 
535  MultiFab::Copy(mf,LAI,0,0,1,ng);
536  VisMF::Write(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "LAI"));
537 
538  MultiFab::Copy(mf,SAI,0,0,1,ng);
539  VisMF::Write(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "SAI"));
540 
541  MultiFab::Copy(mf,sstxy,0,0,1,ng);
542  VisMF::Write(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "sstxy"));
543 
544  MultiFab::Copy(mf,t_canop,0,0,1,ng);
545  VisMF::Write(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "t_canop"));
546 
547  MultiFab::Copy(mf,mw,0,0,1,ng);
548  VisMF::Write(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "mw"));
549 
550  MultiFab::Copy(mf,mws,0,0,1,ng);
551  VisMF::Write(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "mws"));
552 
553  MultiFab::Copy(mf,t_skin,0,0,1,ng);
554  VisMF::Write(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "t_skin"));
555 
556  MultiFab::Copy(mf,t_ground_skin,0,0,1,ng);
557  VisMF::Write(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "t_ground_skin"));
558 
559  MultiFab::Copy(mf,t_cas,0,0,1,ng);
560  VisMF::Write(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "t_cas"));
561 
562  MultiFab::Copy(mf,q_cas,0,0,1,ng);
563  VisMF::Write(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "q_cas"));
564 
565  MultiFab::Copy(mf,vege_YES,0,0,1,ng);
566  VisMF::Write(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "vege_YES"));
567 
568  MultiFab::Copy(mf,cp_vege,0,0,1,ng);
569  VisMF::Write(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "cp_vege"));
570 
571  MultiFab::Copy(mf,z0_sfc,0,0,1,ng);
572  VisMF::Write(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "z0_sfc"));
573 
574  MultiFab::Copy(mf,Khai_L,0,0,1,ng);
575  VisMF::Write(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "Khai_L"));
576 
577  MultiFab::Copy(mf,phi_1,0,0,1,ng);
578  VisMF::Write(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "phi_1"));
579 
580  MultiFab::Copy(mf,phi_2,0,0,1,ng);
581  VisMF::Write(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "phi_2"));
582 
583  MultiFab::Copy(mf,IR_emis_vege,0,0,1,ng);
584  VisMF::Write(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "IR_emis_vege"));
585 
586  MultiFab::Copy(mf,IR_emis_soil,0,0,1,ng);
587  VisMF::Write(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "IR_emis_soil"));
588 
589  MultiFab::Copy(mf,ztop,0,0,1,ng);
590  VisMF::Write(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "ztop"));
591 
592  MultiFab::Copy(mf,disp_hgt,0,0,1,ng);
593  VisMF::Write(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "disp_hgt"));
594 
595  MultiFab::Copy(mf,Rgl,0,0,1,ng);
596  VisMF::Write(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "Rgl"));
597 
598  MultiFab::Copy(mf,Rc_min,0,0,1,ng);
599  VisMF::Write(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "Rc_min"));
600 
601  MultiFab::Copy(mf,hs_rc,0,0,1,ng);
602  VisMF::Write(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "hs_rc"));
603 
604  MultiFab::Copy(mf,precip_extinc,0,0,1,ng);
605  VisMF::Write(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "precip_extinc"));
606 
607  MultiFab::Copy(mf,mw_mx,0,0,1,ng);
608  VisMF::Write(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "mw_mx"));
609 
610  MultiFab::Copy(mf,mws_mx,0,0,1,ng);
611  VisMF::Write(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "mws_mx"));
612 
613  MultiFab::Copy(mf,BAI,0,0,1,ng);
614  VisMF::Write(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "BAI"));
615 
616  MultiFab::Copy(mf,mw_inc,0,0,1,ng);
617  VisMF::Write(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "mw_inc"));
618 
619  MultiFab::Copy(mf,evapo_dry,0,0,1,ng);
620  VisMF::Write(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "evapo_dry"));
621 
622  MultiFab::Copy(mf,shf_canop,0,0,1,ng);
623  VisMF::Write(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "shf_canop"));
624 
625  MultiFab::Copy(mf,shf_soil,0,0,1,ng);
626  VisMF::Write(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "shf_soil"));
627 
628  MultiFab::Copy(mf,shf_air,0,0,1,ng);
629  VisMF::Write(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "shf_air"));
630 
631  MultiFab::Copy(mf,lhf_canop,0,0,1,ng);
632  VisMF::Write(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "lhf_canop"));
633 
634  MultiFab::Copy(mf,lhf_soil,0,0,1,ng);
635  VisMF::Write(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "lhf_soil"));
636 
637  MultiFab::Copy(mf,lhf_air,0,0,1,ng);
638  VisMF::Write(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "lhf_air"));
639 
640  MultiFab::Copy(mf,albedovis_v,0,0,1,ng);
641  VisMF::Write(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "albedovis_v"));
642 
643  MultiFab::Copy(mf,albedonir_v,0,0,1,ng);
644  VisMF::Write(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "albedonir_v"));
645 
646  MultiFab::Copy(mf,albedovis_s,0,0,1,ng);
647  VisMF::Write(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "albedovis_s"));
648 
649  MultiFab::Copy(mf,albedonir_s,0,0,1,ng);
650  VisMF::Write(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "albedonir_s"));
651 
652  MultiFab::Copy(mf,r_a,0,0,1,ng);
653  VisMF::Write(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "r_a"));
654 
655  MultiFab::Copy(mf,r_b,0,0,1,ng);
656  VisMF::Write(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "r_b"));
657 
658  MultiFab::Copy(mf,r_c,0,0,1,ng);
659  VisMF::Write(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "r_c"));
660 
661  MultiFab::Copy(mf,r_d,0,0,1,ng);
662  VisMF::Write(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "r_d"));
663 
664  MultiFab::Copy(mf,r_soil,0,0,1,ng);
665  VisMF::Write(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "r_soil"));
666 
667  MultiFab::Copy(mf,wet_canop,0,0,1,ng);
668  VisMF::Write(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "wet_canop"));
669 
670  MultiFab::Copy(mf,zrefxy,0,0,1,ng);
671  VisMF::Write(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "zrefxy"));
672 
673  MultiFab::Copy(mf,albold_noahmp,0,0,1,ng);
674  VisMF::Write(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "albold_noahmp"));
675 
676  MultiFab::Copy(mf,tauss_noahmp,0,0,1,ng);
677  VisMF::Write(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "tauss_noahmp"));
678 
679  for (int i = 0; i < static_cast<int>(unmapped_fields.size()); i++) {
680  MultiFab mf_unmapped(lsm_fab_vars[unmapped_fields[i]]->boxArray(),dm,1,IntVect(1,1,1));
681  MultiFab::Copy(mf_unmapped,*(lsm_fab_vars[unmapped_fields[i]]),0,0,1,IntVect(1,1,1));
682  VisMF::Write(mf_unmapped, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "Data" + std::to_string(unmapped_fields[i])));
683  }
684 
685  auto check_end = amrex::second() - check_start;
686  ParallelDescriptor::ReduceRealMax(check_end,ParallelDescriptor::IOProcessorNumber());
687  amrex::Print() << " SLM Checkpoint write time = " << check_end << " seconds." << '\n';
688 }

◆ writeSLM_Data()

void SLM::writeSLM_Data ( const PlotFileType  plotfile_type,
const amrex::Real  cur_time,
const std::string  plot_prefix,
const int  level_step,
const int  lev,
const int  finest_lev,
amrex::MultiFab &  fab,
amrex::Geometry &  geom,
amrex::Vector< std::string > &  varnames 
)
5092 {
5093 #ifndef ERF_USE_NETCDF
5094  amrex::ignore_unused(cur_time, plot_prefix, level_step);
5095 #endif
5096  geom.define(amrex::makeSlab(m_lsm_geom.Domain(), 2, 0), m_lsm_geom.ProbDomain(), m_lsm_geom.Coord(), m_lsm_geom.isPeriodic());
5097 
5098  amrex::Vector<amrex::MultiFab*> mf_data;
5099 
5100  mf_data.push_back(&net_rad);
5101 
5102  mf_data.push_back(&mw);
5103  mf_data.push_back(&mws);
5104  mf_data.push_back(&t_canop);
5105  mf_data.push_back(&t_skin);
5106  mf_data.push_back(&t_ground_skin);
5107  mf_data.push_back(&t_cas);
5108  mf_data.push_back(&q_cas);
5109  mf_data.push_back(&mw_inc);
5110 
5111  mf_data.push_back(&evapo_dry);
5112  mf_data.push_back(&shf_air);
5113  mf_data.push_back(&shf_canop);
5114  mf_data.push_back(&shf_soil);
5115  mf_data.push_back(&lhf_air);
5116  mf_data.push_back(&lhf_canop);
5117  mf_data.push_back(&lhf_soil);
5118 
5119  //mf_data.push_back(&ustar);
5120  //mf_data.push_back(&tstar);
5121 
5122  mf_data.push_back(&r_a);
5123  mf_data.push_back(&r_b);
5124  mf_data.push_back(&r_c);
5125  mf_data.push_back(&r_d);
5126  mf_data.push_back(&r_soil);
5127 
5128  mf_data.push_back(&wet_canop);
5129 
5130  mf_data.push_back(&albedovis_v);
5131  mf_data.push_back(&albedovis_s);
5132  mf_data.push_back(&albedonir_v);
5133  mf_data.push_back(&albedonir_s);
5134  mf_data.push_back(&IR_emis_vege);
5135  mf_data.push_back(&IR_emis_soil);
5136  mf_data.push_back(&zrefxy);
5137  mf_data.push_back(&vege_YES);
5138 
5139  mf_data.push_back(&LAI);
5140  mf_data.push_back(&SAI);
5141 
5142  MultiFab tmp_landtype = amrex::ToMultiFab(landtype);
5143  MultiFab tmp_landmask = amrex::ToMultiFab(landmask);
5144 
5145  mf_data.push_back(&tmp_landtype);
5146  mf_data.push_back(&tmp_landmask);
5147 
5148  IntVect ng(0, 0, 0);
5149 
5150  // Total number of output MFs: net_rad components + mf_data size - 1 + diag vars + olen
5151  const int output_size = SLM_NetRad::NumVars + static_cast<int>(mf_data.size()) - 1 + SLM_Diag::NumVars + 1;
5152  fab.define(ba_lsm_2d, net_rad.DistributionMap(), output_size, ng);
5153  MultiFab::Copy(fab, *(mf_data[0]), 0, 0, SLM_NetRad::NumVars, 0);
5154  for (int i = 1; i < static_cast<int>(mf_data.size()); ++i)
5155  {
5156  MultiFab::Copy(fab, *(mf_data[i]), 0, i + SLM_NetRad::NumVars - 1, 1, 0);
5157  }
5158  MultiFab::Copy(fab, slm_diag, 0, output_size - SLM_Diag::NumVars - 1, SLM_Diag::NumVars, 0);
5159  MultiFab::Copy(fab, *(lsm_fab_flux[LsmFlux_SLM::olen]), 0, output_size - 1, 1, 0);
5160 
5161 
5162  varnames = amrex::Vector<std::string>();
5163  // net_rad component names:
5164  varnames.push_back("net_swup1");
5165  varnames.push_back("net_swup2");
5166  varnames.push_back("net_swdn1");
5167  varnames.push_back("net_swdn2");
5168  varnames.push_back("net_sw1");
5169  varnames.push_back("net_sw2");
5170  varnames.push_back("tir1");
5171  varnames.push_back("tir2");
5172  varnames.push_back("net_lwup1");
5173  varnames.push_back("net_lwup2");
5174  varnames.push_back("net_lwdn1");
5175  varnames.push_back("net_lwdn2");
5176  varnames.push_back("net_lw1");
5177  varnames.push_back("net_lw2");
5178  varnames.push_back("net_rad1");
5179  varnames.push_back("net_rad2");
5180  // ----------------------------
5181 
5182  varnames.push_back("mw");
5183  varnames.push_back("mws");
5184  varnames.push_back("t_canop");
5185  varnames.push_back("t_skin");
5186  varnames.push_back("t_ground_skin");
5187  varnames.push_back("t_cas");
5188  varnames.push_back("q_cas");
5189  varnames.push_back("mw_inc");
5190 
5191  varnames.push_back("evapo_dry");
5192  varnames.push_back("shf_air");
5193  varnames.push_back("shf_canop");
5194  varnames.push_back("shf_soil");
5195  varnames.push_back("lhf_air");
5196  varnames.push_back("lhf_canop");
5197  varnames.push_back("lhf_soil");
5198 
5199  //varnames.push_back("ustar");
5200  //varnames.push_back("tstar");
5201 
5202  varnames.push_back("r_a");
5203  varnames.push_back("r_b");
5204  varnames.push_back("r_c");
5205  varnames.push_back("r_d");
5206  varnames.push_back("r_soil");
5207 
5208  varnames.push_back("wet_canop");
5209 
5210  varnames.push_back("albedovis_veg");
5211  varnames.push_back("albedovis_soil");
5212  varnames.push_back("albedonir_veg");
5213  varnames.push_back("albedonir_soil");
5214  varnames.push_back("IR_emis_veg");
5215  varnames.push_back("IR_emis_soil");
5216  varnames.push_back("zrefxy");
5217  varnames.push_back("veg_flag");
5218 
5219  varnames.push_back("LAI");
5220  varnames.push_back("SAI");
5221 
5222  varnames.push_back("vegtype");
5223  varnames.push_back("landmask");
5224 
5225  for (int i = 0; i < static_cast<int>(diag_names.size()); ++i)
5226  {
5227  varnames.push_back(diag_names[i]);
5228  }
5229 
5230  varnames.push_back("olen");
5231 
5232  AMREX_ALWAYS_ASSERT(static_cast<int>(varnames.size()) == output_size);
5233 
5234  if (plotfile_type == PlotFileType::Amrex) {
5235  //amrex::WriteSingleLevelPlotfile(plotfilename, fab, varnames, lsm_2d_geom, cur_time, level_step);
5236 #ifdef ERF_USE_NETCDF
5237  // Temporarily write NetCDF always
5238  //writeSLM_NetCDF(fab, varnames, cur_time, plot_prefix, level_step);
5239 #endif
5240 #ifdef ERF_USE_NETCDF
5241  } else if (plotfile_type == PlotFileType::Netcdf) {
5242  writeSLM_NetCDF(fab, varnames, cur_time, plot_prefix, level_step);
5243 #endif
5244  } else {
5245  Abort("Dont know this plot_filetype");
5246  }
5247 }
static const std::vector< std::string > diag_names
Definition: ERF_SLM.H:1081
Here is the call graph for this function:

Member Data Documentation

◆ albedonir_s

amrex::MultiFab SLM::albedonir_s
private

◆ albedonir_v

amrex::MultiFab SLM::albedonir_v
private

◆ albedovis_s

amrex::MultiFab SLM::albedovis_s
private

◆ albedovis_v

amrex::MultiFab SLM::albedovis_v
private

◆ albold_noahmp

amrex::MultiFab SLM::albold_noahmp
private

◆ ba_lsm_2d

amrex::BoxArray SLM::ba_lsm_2d
private

◆ BAI

amrex::MultiFab SLM::BAI
private

◆ bats_cosz_rad

amrex::Real SLM::bats_cosz_rad = two
private

◆ bats_nir_age_rad

amrex::Real SLM::bats_nir_age_rad = myhalf
private

◆ bats_nir_dir_rad

amrex::Real SLM::bats_nir_dir_rad = 0.4
private

◆ bats_nir_new_rad

amrex::Real SLM::bats_nir_new_rad = 0.65
private

◆ bats_vis_age_rad

amrex::Real SLM::bats_vis_age_rad = 0.2
private

◆ bats_vis_dir_rad

amrex::Real SLM::bats_vis_dir_rad = 0.4
private

◆ bats_vis_new_rad

amrex::Real SLM::bats_vis_new_rad = 0.95
private

◆ betads_rad

amrex::Real SLM::betads_rad = myhalf
private

◆ betais_rad

amrex::Real SLM::betais_rad = myhalf
private

◆ cbiom

amrex::MultiFab SLM::cbiom
private

◆ clay0

amrex::Vector<amrex::Real> SLM::clay0
private

◆ cp

constexpr amrex::Real SLM::cp = 1004.0
staticconstexprprivate

◆ cp_vege

amrex::MultiFab SLM::cp_vege
private

◆ cp_water

constexpr amrex::Real SLM::cp_water = 4.1796e6
staticconstexprprivate

◆ d_lai_curr

amrex::Gpu::DeviceVector<amrex::Real> SLM::d_lai_curr
private

◆ d_lai_next

amrex::Gpu::DeviceVector<amrex::Real> SLM::d_lai_next
private

◆ d_param_table

amrex::TableData<amrex::Real, 2> SLM::d_param_table
private

◆ d_rad_params

std::unordered_map<std::string, std::unique_ptr<amrex::Gpu::DeviceVector<amrex::Real> > > SLM::d_rad_params
private

◆ d_sai_curr

amrex::Gpu::DeviceVector<amrex::Real> SLM::d_sai_curr
private

◆ d_sai_next

amrex::Gpu::DeviceVector<amrex::Real> SLM::d_sai_next
private

◆ d_soil_params

std::unordered_map<std::string, std::unique_ptr<amrex::Gpu::DeviceVector<amrex::Real> > > SLM::d_soil_params
private

◆ d_veg_params

std::unordered_map<std::string, std::unique_ptr<amrex::Gpu::DeviceVector<amrex::Real> > > SLM::d_veg_params
private

◆ diag_names

const std::vector<std::string> SLM::diag_names
inlinestaticprivate
Initial value:
= {
"precip",
"precip_in",
"precip_sfc",
"runoff_sfc",
"drain",
"drain_flux",
"evapo_wet",
"grflux"
}

◆ dirt_soot_rad

amrex::Real SLM::dirt_soot_rad = 0.3
private

◆ disp_hgt

amrex::MultiFab SLM::disp_hgt
private

◆ dleaf

amrex::MultiFab SLM::dleaf
private

◆ dosoiltnudging

bool SLM::dosoiltnudging = false
private

◆ dosoilwnudging

bool SLM::dosoilwnudging = false
private

◆ eg_lake_rad

amrex::Real SLM::eg_lake_rad = 0.98
private

◆ eg_soil_rad

amrex::Real SLM::eg_soil_rad = 0.97
private

◆ epsv

constexpr amrex::Real SLM::epsv = 0.61
staticconstexprprivate

◆ evapo_dry

amrex::MultiFab SLM::evapo_dry
private

◆ evapo_wet

amrex::MultiFab SLM::evapo_wet
private

◆ evp_air

amrex::MultiFab SLM::evp_air
private

◆ evp_canop

amrex::MultiFab SLM::evp_canop
private

◆ evp_soil

amrex::MultiFab SLM::evp_soil
private

◆ extra_growth_rad

amrex::Real SLM::extra_growth_rad = 10.
private

◆ first_step

bool SLM::first_step = true
private

Referenced by Advance(), and Update_Lsm_Vars().

◆ full_params

SLMParameterTable SLM::full_params
private

◆ grain_growth_rad

amrex::Real SLM::grain_growth_rad = 5000.
private

◆ has_atmos_state

bool SLM::has_atmos_state = false
private

Referenced by Lsm_Has_Atmos_State().

◆ hs_rc

amrex::MultiFab SLM::hs_rc
private

◆ IMPERV

amrex::MultiFab SLM::IMPERV
private

◆ init_from_atmos_pending

bool SLM::init_from_atmos_pending = false
private

Referenced by Update_Lsm_Vars().

◆ interpolate_lai

bool SLM::interpolate_lai = false
private

◆ IR_emis_grnd

amrex::MultiFab SLM::IR_emis_grnd
private

◆ IR_emis_soil

amrex::MultiFab SLM::IR_emis_soil
private

◆ IR_emis_urban

constexpr amrex::Real SLM::IR_emis_urban = 0.9
staticconstexprprivate

◆ IR_emis_vege

amrex::MultiFab SLM::IR_emis_vege
private

◆ Khai_L

amrex::MultiFab SLM::Khai_L
private

◆ khi_lsm

int SLM::khi_lsm
private

◆ klo_lsm

int SLM::klo_lsm
private

◆ LAI

amrex::MultiFab SLM::LAI
private

◆ LAI0

amrex::Real SLM::LAI0 = zero
private

◆ lai_table

std::vector<std::vector<amrex::Real> > SLM::lai_table
private

◆ landmask

amrex::iMultiFab SLM::landmask
private

◆ landtype

amrex::iMultiFab SLM::landtype
private

◆ landtype0

int SLM::landtype0 = 16
private

◆ lhf_air

amrex::MultiFab SLM::lhf_air
private

◆ lhf_canop

amrex::MultiFab SLM::lhf_canop
private

◆ lhf_soil

amrex::MultiFab SLM::lhf_soil
private

◆ lsm_fab_flux

amrex::Array<FabPtr, LsmFlux_SLM::NumVars> SLM::lsm_fab_flux
private

Referenced by Lsm_Flux_Ptr().

◆ lsm_fab_vars

amrex::Array<FabPtr, LsmVar_SLM::NumVars> SLM::lsm_fab_vars
private

Referenced by Lsm_Data_Ptr(), and Lsm_Unmapped_Ptr().

◆ LsmDataMap

amrex::Vector<int> SLM::LsmDataMap
private

Referenced by Lsm_Data_Ptr().

◆ LsmDataName

amrex::Vector<std::string> SLM::LsmDataName
private

Referenced by Lsm_DataName().

◆ LsmFluxMap

amrex::Vector<int> SLM::LsmFluxMap
private

Referenced by Lsm_Flux_Ptr().

◆ LsmFluxName

amrex::Vector<std::string> SLM::LsmFluxName
private

Referenced by Lsm_FluxName().

◆ LsmVarName_Full

constexpr const char* SLM::LsmVarName_Full[]
staticconstexprprivate
Initial value:
= {
"tsurf", "ustar", "tstar", "qstar", "ref_t", "ref_q", "ref_p", "ref_d", "ref_u", "ref_v", "ref_precip",
"SW_dw_dir_vis", "SW_dw_dir_nir", "SW_dw_dif_vis", "SW_dw_dif_nir", "LW_dw", "zenith",
"tveg", "mv", "tsoil", "wsoil", "sand", "clay", "soil_thickness", "node_z", "interface_z",
"soilw_nudge", "soilt_nudge", "sw_over_sat", "soil_relax_hgt", "soilt_obs", "soilw_obs", "soil_transp_frac", "w_s_FC", "w_s_WP",
"sst_capa", "sst_cond", "poro_soil", "theta_FC", "theta_WP", "m_pot_sat", "Bconst", "ks","lai","vegtype","soiltype",
"veg_frac", "veg_frac_min", "veg_frac_max", "surface_u", "surface_v", "surface_vapor", "surface_heat", "precip_soil", "emis_sfc", "alb_nir_sfc", "alb_vis_sfc", "alb_nir_sfc_diff", "alb_vis_sfc_diff"
}

◆ m_calday

amrex::Real SLM::m_calday = -1.0
private

◆ m_domain_bcs_type

amrex::Vector<amrex::BCRec> SLM::m_domain_bcs_type
private

◆ m_dt

amrex::Real SLM::m_dt
private

Referenced by Advance(), and set_dt().

◆ m_dz_lsm

std::vector<amrex::Real> SLM::m_dz_lsm
private

Referenced by get_soil_dz().

◆ m_geom

amrex::Geometry SLM::m_geom
private

Referenced by Lsm_Atmos_Geom().

◆ m_geom0

amrex::Geometry SLM::m_geom0
private

◆ m_init_type

InitType SLM::m_init_type = InitType::None
private

Referenced by Define().

◆ m_lsm_data_size

int SLM::m_lsm_data_size = 44
private

◆ m_lsm_flux_size

int SLM::m_lsm_flux_size = 5
private

◆ m_lsm_geom

amrex::Geometry SLM::m_lsm_geom
private

Referenced by Lsm_Geom().

◆ m_lsm_geom0

amrex::Geometry SLM::m_lsm_geom0
private

◆ m_nz_lsm

int SLM::m_nz_lsm = 7
private

◆ m_orbital_day

int SLM::m_orbital_day = -1
private

◆ m_orbital_mon

int SLM::m_orbital_mon = -1
private

◆ m_orbital_sec

int SLM::m_orbital_sec = -1
private

◆ m_orbital_year

int SLM::m_orbital_year = -1
private

◆ m_updated

bool SLM::m_updated = false
private

Referenced by Advance(), and Get_Update_Status().

◆ mw

amrex::MultiFab SLM::mw
private

◆ mw_inc

amrex::MultiFab SLM::mw_inc
private

◆ mw_mx

amrex::MultiFab SLM::mw_mx
private

◆ mws

amrex::MultiFab SLM::mws
private

◆ mws_mx

amrex::MultiFab SLM::mws_mx
private

◆ mws_mx0

amrex::Real SLM::mws_mx0 = 50.0
private

◆ net_rad

amrex::MultiFab SLM::net_rad
private

◆ nroot

amrex::iMultiFab SLM::nroot
private

◆ num_landtypes

int SLM::num_landtypes = -1
private

◆ num_soil_params

int SLM::num_soil_params = -1
private

◆ num_veg_params

int SLM::num_veg_params = -1
private

◆ opt_alb

int SLM::opt_alb = 1
private

◆ opt_rad

int SLM::opt_rad = 1
private

◆ param_soil_categories

amrex::Vector<std::string> SLM::param_soil_categories
private

◆ param_table

std::vector<std::vector<amrex::Real> > SLM::param_table
private

◆ param_veg_categories

amrex::Vector<std::string> SLM::param_veg_categories
private

◆ parameter_file

std::string SLM::parameter_file = "NoahmpTable.TBL"
private

◆ params_updated

bool SLM::params_updated = false
private

◆ phi_1

amrex::MultiFab SLM::phi_1
private

◆ phi_2

amrex::MultiFab SLM::phi_2
private

◆ precip_extinc

amrex::MultiFab SLM::precip_extinc
private

◆ precip_sfc

amrex::MultiFab SLM::precip_sfc
private

◆ q_cas

amrex::MultiFab SLM::q_cas
private

◆ q_gr

amrex::MultiFab SLM::q_gr
private

◆ q_sfc

amrex::MultiFab SLM::q_sfc
private

◆ r_a

amrex::MultiFab SLM::r_a
private

◆ r_b

amrex::MultiFab SLM::r_b
private

◆ r_c

amrex::MultiFab SLM::r_c
private

◆ r_d

amrex::MultiFab SLM::r_d
private

◆ r_litter

constexpr amrex::Real SLM::r_litter = zero
staticconstexprprivate

◆ r_soil

amrex::MultiFab SLM::r_soil
private

◆ rad_input_file

std::string SLM::rad_input_file
private

◆ rad_output_map

std::unordered_map<std::string, std::string> SLM::rad_output_map
inlinestaticprivate
Initial value:
= {
{"cos_zenith_angle", "cos_zenith"},
{"sw_flux_dn", ""},
{"sw_flux_dn_dir_vis", "SW_dw_dir_vis"},
{"sw_flux_dn_dir_nir", "SW_dw_dir_nir"},
{"sw_flux_dn_dif_vis", "SW_dw_dif_vis"},
{"sw_flux_dn_dif_nir", "SW_dw_dif_nir"},
{"lw_flux_dn", "LW_dw"}
}

Referenced by get_rad_output_map().

◆ Rc_max

amrex::Real SLM::Rc_max = 5000.0
private

◆ Rc_min

amrex::MultiFab SLM::Rc_min
private

◆ relax_hgt

amrex::Vector<amrex::Real> SLM::relax_hgt
private

◆ Rgl

amrex::MultiFab SLM::Rgl
private

◆ rsurf_exp

amrex::Real SLM::rsurf_exp = 5.0
private

◆ SAI

amrex::MultiFab SLM::SAI
private

◆ sai_table

std::vector<std::vector<amrex::Real> > SLM::sai_table
private

◆ sand0

amrex::Vector<amrex::Real> SLM::sand0
private

◆ sdew

amrex::MultiFab SLM::sdew
private

◆ shf_air

amrex::MultiFab SLM::shf_air
private

◆ shf_canop

amrex::MultiFab SLM::shf_canop
private

◆ shf_soil

amrex::MultiFab SLM::shf_soil
private

◆ sigma

constexpr amrex::Real SLM::sigma = 5.67e-8
staticconstexprprivate

◆ slm_diag

amrex::MultiFab SLM::slm_diag
private

◆ slm_to_rad_vars

amrex::Vector<const amrex::MultiFab*> SLM::slm_to_rad_vars
private

Referenced by export_to_RRTMGP().

◆ snow_emis_rad

amrex::Real SLM::snow_emis_rad = 0.95
private

◆ soil_dataset

std::string SLM::soil_dataset = "stas"
private

◆ soilt_vars

amrex::MultiFab SLM::soilt_vars
private

◆ soilw_vars

amrex::MultiFab SLM::soilw_vars
private

◆ source_data

amrex::Vector<amrex::MultiFab*> SLM::source_data
private

Referenced by Lsm_Set_Source_Data_Ptr().

◆ source_flux

amrex::Vector<amrex::MultiFab*> SLM::source_flux
private

Referenced by Lsm_Set_Source_Flux_Ptr().

◆ source_has_atmos_state

bool SLM::source_has_atmos_state = false
private

◆ source_unmapped

amrex::Vector<amrex::MultiFab*> SLM::source_unmapped
private

◆ sstxy

amrex::MultiFab SLM::sstxy
private

◆ st0

amrex::Vector<amrex::Real> SLM::st0
private

◆ start_time

amrex::Real SLM::start_time = -1.0
private

Referenced by Advance().

◆ surf_vars

const std::unordered_set<int> SLM::surf_vars
inlinestaticprivate

◆ sw0

amrex::Vector<amrex::Real> SLM::sw0
private

◆ swemx_rad

amrex::Real SLM::swemx_rad = one
private

◆ t00

amrex::Real SLM::t00 = 300.0
private

◆ t_canop

amrex::MultiFab SLM::t_canop
private

◆ t_canop_max

constexpr amrex::Real SLM::t_canop_max = 343.
staticconstexprprivate

◆ t_cas

amrex::MultiFab SLM::t_cas
private

◆ t_ground_skin

amrex::MultiFab SLM::t_ground_skin
private

◆ T_opt

amrex::Real SLM::T_opt = 298.0
private

◆ t_sfc

amrex::MultiFab SLM::t_sfc
private

◆ t_skin

amrex::MultiFab SLM::t_skin
private

◆ tabs_s

amrex::Real SLM::tabs_s = zero
private

◆ tau0_rad

amrex::Real SLM::tau0_rad = 1.0e6
private

◆ tausoil

amrex::Real SLM::tausoil = 86400.0
private

◆ tauss_noahmp

amrex::MultiFab SLM::tauss_noahmp
private

◆ tfriz

constexpr amrex::Real SLM::tfriz = 273.15
staticconstexprprivate

◆ time

amrex::Real SLM::time = zero
private

Referenced by Advance().

◆ unmapped_fields

amrex::Vector<int> SLM::unmapped_fields
private

◆ use_param_file

bool SLM::use_param_file = false
private

◆ use_terrain

bool SLM::use_terrain = false
private

Referenced by Define().

◆ use_wrf_lai

bool SLM::use_wrf_lai = false
private

◆ use_wrfinput

bool SLM::use_wrfinput = false
private

Referenced by Advance(), Define(), and Update_Lsm_Vars().

◆ veg_dataset

std::string SLM::veg_dataset = "modis"
private

◆ vege_YES

amrex::MultiFab SLM::vege_YES
private

◆ vegetype

amrex::iMultiFab SLM::vegetype
private

◆ wet_canop

amrex::MultiFab SLM::wet_canop
private

◆ wrfinput_initialized

bool SLM::wrfinput_initialized = false
private

Referenced by Advance(), and Update_Lsm_Vars().

◆ wrfinput_map

std::unordered_map<std::string, std::string> SLM::wrfinput_map
inlinestaticprivate
Initial value:
= {
{"DZS", "soil_thickness"},
{"ZS", "node_z"},
{"TSLB", "tsoil"},
{"SMOIS", "wsoil"},
{"LAI", "lai"},
{"IVGTYP", "vegtype"},
{"ISLTYP", "soiltype"},
{"TSK", "tsurf"},
{"VEGFRA", "veg_frac"},
{"SHDMIN", "veg_frac_min"},
{"SHDMAX", "veg_frac_max"},
}

Referenced by Lsm_WRFInputNames().

◆ z0_sfc

amrex::MultiFab SLM::z0_sfc
private

◆ z0_soil

amrex::Real SLM::z0_soil = 0.01
private

◆ z_phys_nd

amrex::MultiFab* SLM::z_phys_nd
private

◆ zref

amrex::Real SLM::zref = myhalf
private

◆ zrefxy

amrex::MultiFab SLM::zrefxy
private

◆ ztop

amrex::MultiFab SLM::ztop
private

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