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
 
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 ()
 
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
 
bool wrfinput_initialized = 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.

269  {
270  m_updated = false;
271  m_dt = dt;
272  time = t_new;
273  start_time = t_start;
274 
275  if (first_step)
276  {
277  first_step = false;
278 
280  this->init_wrfinput_vars();
281  }
282 
284  }
285 
286  this->AdvanceSLM();
287  m_updated = true;
288 
289  }
amrex::Real start_time
Definition: ERF_SLM.H:852
amrex::Real m_dt
Definition: ERF_SLM.H:696
bool wrfinput_initialized
Definition: ERF_SLM.H:984
amrex::Real time
Definition: ERF_SLM.H:850
void init_wrfinput_vars()
Definition: ERF_SLM.cpp:954
bool first_step
Definition: ERF_SLM.H:853
void AdvanceSLM()
Definition: ERF_SLM.cpp:2895
void initialize_zrefxy()
Definition: ERF_SLM.cpp:1227
bool use_wrfinput
Definition: ERF_SLM.H:983
bool m_updated
Definition: ERF_SLM.H:854
Here is the call graph for this function:

◆ Advance() [2/2]

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

Reimplemented from NullSurf.

261  {
262  Advance(static_cast<amrex::Real>(dt), time + static_cast<amrex::Real>(dt), start_time);
263  }
amrex::Real Real
Definition: ERF_ShocInterface.H:19
void Advance(const double &dt) override
Definition: ERF_SLM.H:260

◆ AdvanceSLM()

void SLM::AdvanceSLM ( )
2896 {
2897  // Expose for GPU copy
2898  Real dt = m_dt;
2899  const int d_khi_lsm = khi_lsm;
2900 
2901  net_rad.setVal(0.0, SLM_NetRad::net_sw1, 1, 0);
2902  net_rad.setVal(0.0, SLM_NetRad::net_sw2, 1, 0);
2903  net_rad.setVal(0.0, SLM_NetRad::net_rad1, 1, 0);
2904  net_rad.setVal(0.0, SLM_NetRad::net_rad2, 1, 0);
2905  mw_inc.setVal(zero);
2906 
2907  // Soil temperature and moisture nudging
2908  for ( MFIter mfi(*lsm_fab_vars[LsmVar_SLM::tsurf], TileNoZ()); mfi.isValid(); ++mfi) {
2909  soil_nudging(mfi);
2910  }
2911 
2912  for ( MFIter mfi(landtype, TileNoZ()); mfi.isValid(); ++mfi) {
2913  auto box = mfi.tilebox();
2914 
2915  auto landmask_arr = landmask.const_array(mfi);
2916 
2917  auto LAI_arr = LAI.const_array(mfi);
2918  auto precip_extinc_arr = precip_extinc.const_array(mfi);
2919 
2920  auto mw_arr = mw.array(mfi);
2921  auto mws_arr = mws.const_array(mfi);
2922  auto mw_mx_arr = mw_mx.const_array(mfi);
2923 
2924  auto mw_inc_arr = mw_inc.array(mfi);
2925 
2926  auto t_cas_arr = t_cas.array(mfi);
2927  auto q_cas_arr = q_cas.array(mfi);
2928 
2929  auto t_sfc_arr = t_sfc.array(mfi);
2930  auto q_sfc_arr = q_sfc.array(mfi);
2931 
2932  auto q_gr_arr = q_gr.array(mfi);
2933 
2934  auto t_canop_arr = t_canop.array(mfi);
2935  auto t_skin_arr = t_skin.const_array(mfi);
2936  auto t_ground_skin_arr = t_ground_skin.const_array(mfi);
2937 
2938  auto soilt_arr = lsm_fab_vars[LsmVar_SLM::soilt]->const_array(mfi);
2939  auto soilw_arr = lsm_fab_vars[LsmVar_SLM::soilw]->const_array(mfi);
2940 
2941  auto m_pot_sat_arr = lsm_fab_vars[LsmVar_SLM::m_pot_sat]->const_array(mfi);
2942  auto Bconst_arr = lsm_fab_vars[LsmVar_SLM::Bconst]->const_array(mfi);
2943 
2944  auto ustar_arr = lsm_fab_vars[LsmVar_SLM::ustar]->const_array(mfi);
2945  auto qstar_arr = lsm_fab_vars[LsmVar_SLM::qstar]->array(mfi);
2946 
2947  auto SAI_arr = SAI.const_array(mfi);
2948  auto cbiom_arr = cbiom.const_array(mfi);
2949 
2950  auto shf_air_arr = shf_air.array(mfi);
2951 
2952  auto evp_air_arr = evp_air.array(mfi);
2953 
2954  auto vegetype_arr = vegetype.const_array(mfi);
2955  auto vege_YES_arr = vege_YES.const_array(mfi);
2956 
2957  auto cp_vege_arr = cp_vege.array(mfi);
2958 
2959  auto sdew_arr = sdew.array(mfi);
2960 
2961  auto tref_arr = lsm_fab_vars[LsmVar_SLM::tref]->const_array(mfi);
2962  auto dref_arr = lsm_fab_vars[LsmVar_SLM::dref]->const_array(mfi);
2963  auto qref_arr = lsm_fab_vars[LsmVar_SLM::qref]->const_array(mfi);
2964  auto pref_arr = lsm_fab_vars[LsmVar_SLM::pref]->const_array(mfi);
2965  auto precip_array = lsm_fab_vars[LsmVar_SLM::precipref]->const_array(mfi);
2966 
2967  auto tsurf_arr = lsm_fab_vars[LsmVar_SLM::tsurf]->array(mfi);
2968  auto tveg_arr = lsm_fab_vars[LsmVar_SLM::tv]->array(mfi);
2969  auto mveg_arr = lsm_fab_vars[LsmVar_SLM::mv]->array(mfi);
2970 
2971  auto r_a_arr = r_a.const_array(mfi);
2972  auto r_b_arr = r_b.const_array(mfi);
2973  auto r_c_arr = r_c.const_array(mfi);
2974  auto r_d_arr = r_d.const_array(mfi);
2975  auto r_soil_arr = r_soil.const_array(mfi);
2976 
2977  auto flbu_arr = lsm_fab_vars[LsmVar_SLM::flbu]->array(mfi);
2978  auto flbv_arr = lsm_fab_vars[LsmVar_SLM::flbv]->array(mfi);
2979  auto flbq_arr = lsm_fab_vars[LsmVar_SLM::flbq]->array(mfi);
2980  auto flbt_arr = lsm_fab_vars[LsmVar_SLM::flbt]->array(mfi);
2981  auto prsfc_arr = lsm_fab_vars[LsmVar_SLM::prsfc]->array(mfi);
2982 
2983  auto wet_canop_arr = wet_canop.const_array(mfi);
2984 
2985  // TODO: Copies for MOST
2986  auto fluxq_arr = lsm_fab_flux[LsmFlux_SLM::q_flux]->array(mfi);
2987  auto fluxt_arr = lsm_fab_flux[LsmFlux_SLM::t_flux]->array(mfi);
2988  auto tau13_arr = lsm_fab_flux[LsmFlux_SLM::tau13]->array(mfi);
2989  auto tau23_arr = lsm_fab_flux[LsmFlux_SLM::tau23]->array(mfi);
2990  auto olen_arr = lsm_fab_flux[LsmFlux_SLM::olen]->array(mfi);
2991 
2992  auto slm_diag_arr = slm_diag.array(mfi);
2993 
2994  // Update LAI and SAI based on current month
2995  UpdateLAI(mfi);
2996 
2997  // NOAHMP radiation calculation
2998  radiation_noahmp(mfi);
2999 
3000  ParallelFor( box, [=] AMREX_GPU_DEVICE (int i, int j, int)
3001  {
3002  if (landmask_arr(i, j, 0) != 1) {
3003  return;
3004  }
3005 
3008  amrex::Real cp_vege_tot, fh;
3009  // precipitation interception rate at canoppy
3010  // For baresoil, precip = 0, as LAI = 0
3011  precip = precip_array(i, j, 0) * (one - std::exp(-precip_extinc_arr(i, j, 0) * LAI_arr(i, j, 0)));
3012 
3013  if (mw_arr(i, j, 0) < mw_mx_arr(i, j, 0))
3014  {
3015  drain = zero;
3016  }
3017  else if(mw_arr(i, j, 0) > mw_mx_arr(i, j, 0))
3018  {
3019  // when water holding storage exceeds its maximum, no precipitation is intercepted
3020  drain = precip;
3021 
3022  // excess water storage gets drained from mw [kg/m^2]
3023  drain += std::max(mw_arr(i, j, 0) - mw_mx_arr(i, j, 0), amrex::Real(zero)) / dt; // mm/s
3024  }
3025 
3026  prsfc_arr(i, j, 0) = precip_array(i, j, 0) - precip + drain;
3027 
3028  // Update output variables
3029  slm_diag_arr(i, j, 0, SLM_Diag::precip) = precip;
3030 
3031  // Original SLM formula:
3032  // cp_vege_arr(i, j, 0) =
3033  // (LAI_arr(i, j, 0) * leaf_thickness * 0.001
3034  // + ztop_arr(i, j, 0) * BAI_arr(i, j, 0) / 43560.) * 900. * 2800.;
3035 
3036  // NoahMP dry canopy heat capacity: CBIOM * effective VAI * volumetric water heat capacity.
3037  const amrex::Real vai = std::min(amrex::Real(6.0), LAI_arr(i, j, 0) + SAI_arr(i, j, 0));
3038  cp_vege_arr(i, j, 0) = cbiom_arr(i, j, 0) * vai * cp_water * vege_YES_arr(i, j, 0);
3039  cp_vege_tot = cp_vege_arr(i, j, 0) + mw_arr(i, j, 0) * 1.e-3 * cp_water;
3040 
3041  // Add to vegetiation moisture increment from SLM::vapor_fluxes()
3042  mw_inc_arr(i, j, 0) += dt * (precip - drain);
3043 
3044  // Update vegetation moisture storage
3045  mw_arr(i, j, 0) += mw_inc_arr(i, j, 0);
3046 
3047  // from gSAM-SLM
3048  // intercepted precip cools the canopy:
3049  // temperature of intercepted rain is the same as reference level
3050  // note it cools even when water storage is full as old water on leaves is replaced by new rain water
3051  if (vegetype_arr(i, j, 0) == 1)
3052  {
3053  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);
3054  }
3055 
3056  // Note:
3057  // Assign appropriate "surface level" values for each land type, for the calculation
3058  //of surface turbulent fluxes
3059  //For baresoil, surface level = soil surface
3060  //For vegetation, surface level = canopy level
3061  if (vegetype_arr(i, j, 0) == 1)
3062  {
3063  t_sfc_arr(i, j, 0) = t_cas_arr(i, j, 0);
3064  q_sfc_arr(i, j, 0) = q_cas_arr(i, j, 0);
3065  }
3066  else
3067  {
3068  t_sfc_arr(i, j, 0) = soilt_arr(i, j, d_khi_lsm);
3069  // Specific humidity at top soil
3070  if (soilt_arr(i, j, d_khi_lsm) > tfriz)
3071  {
3072  erf_qsatw(soilt_arr(i, j, d_khi_lsm), pref_arr(i, j, 0), q_gr_arr(i, j, 0));
3073  sdew_arr(i, j, 0) = one;
3074  if (mws_arr(i, j, 0) == zero)
3075  {
3076  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));
3077  if ( fh > 0.99)
3078  {
3079  sdew_arr(i, j, 0) = one;
3080  }
3081  else
3082  {
3083  sdew_arr(i, j, 0) = zero;
3084  }
3085  q_gr_arr(i, j, 0) *= fh;
3086  }
3087  }
3088  else
3089  {
3090  erf_qsati(soilt_arr(i, j, d_khi_lsm), pref_arr(i, j, 0), q_gr_arr(i, j, 0));
3091  sdew_arr(i, j, 0) = one;
3092  }
3093  q_sfc_arr(i, j, 0) = q_gr_arr(i, j, 0);
3094  }
3095  });
3096 
3097  // Calculate turbulent transfer coeff between reference level and surface
3098  transfer_coeff(mfi);
3099 
3100  // Calculate aerodynamic resistances + stomatal resistance
3101  resistances(mfi);
3102 
3103  //Subcycle in time to avoid swings of canopy temperature
3104  // because of small heat capacity of vegetation in curtain places and seasons
3105  // Of course, the best would be to use implicit scheme, but for now,
3106  // subcycling seems like a good fix.
3107  //from gSAM-SLM
3108  fluxes_canopy(mfi); // compute both sHF and LHF of canopy
3109 
3111 
3112  // Calculate soil moisture increment
3113  soil_water(mfi);
3114 
3115  // Advance the finite-volume soil using only the final skin-to-soil flux.
3116  soil_temperature(mfi);
3117 
3118  ParallelFor( box, [=] AMREX_GPU_DEVICE (int i, int j, int)
3119  {
3120  if (landmask_arr(i, j, 0) != 1) {
3121  return;
3122  }
3123 
3124  amrex::Real q_gr_loc;
3125 
3126  // SAM rhow[nz] = air density at vertical velocity levels, kg/m^3
3127  amrex::Real rhow = dref_arr(i, j, 0); // TODO: double check this
3128  amrex::Real cond_heat, cond_href, cond_hcnp, cond_hundercnp;
3129  amrex::Real cond_vapor, cond_vref, cond_vcnp, cond_vundercnp;
3130  amrex::Real cvh = 2 * LAI_arr(i, j, 0) / r_b_arr(i, j, 0);
3131  if (vegetype_arr(i, j, 0) == 1) {
3132  // Compute diagnostic variables at canopy air space level
3133  // Calculate heat conductances - non-zero only for canopy land type
3134  cond_heat = 1.0 / r_a_arr(i, j, 0) + cvh + 1.0 / r_d_arr(i, j, 0);
3135  cond_href = 1.0 / r_a_arr(i, j, 0) / cond_heat;
3136  cond_hcnp = cvh / cond_heat;
3137  cond_hundercnp = 1.0 / r_d_arr(i, j, 0) / cond_heat;
3138 
3139  // Calculate vapor conductances
3140  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);
3141  cond_vref = 1.0 / r_a_arr(i, j, 0) / cond_vapor;
3142  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);
3143  cond_vundercnp = 1.0 / (r_d_arr(i, j, 0) + r_soil_arr(i, j, 0) + r_litter) / cond_vapor;
3144  }
3145  else
3146  {
3147  cond_heat = 1./r_a_arr(i, j, 0);
3148  cond_href = one;
3149  cond_hcnp = zero;
3150  cond_hundercnp = zero;
3151 
3152  cond_vapor = 1./(r_a_arr(i, j, 0) + r_soil_arr(i, j, 0));
3153  cond_vref = one;
3154  cond_vcnp = zero;
3155  cond_vundercnp = zero;
3156  }
3157 
3158  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;
3159 
3160  amrex::Real qsat_canop;
3161  if (t_canop_arr(i, j, 0) >= tfriz)
3162  {
3163  erf_qsatw(t_canop_arr(i, j, 0), pref_arr(i, j, 0), qsat_canop);
3164  }
3165  else
3166  {
3167  erf_qsati(t_canop_arr(i, j, 0), pref_arr(i, j, 0), qsat_canop);
3168  }
3169 
3170  q_gr_loc = q_gr_arr(i, j, 0);
3171  q_cas_arr(i, j, 0) = qref_arr(i, j, 0) * cond_vref + qsat_canop*cond_vcnp + q_gr_loc*cond_vundercnp;
3172 
3173  // Output variables
3174  tveg_arr(i, j, 0) = t_canop_arr(i, j, 0);
3175  tveg_arr(i, j, d_khi_lsm) = t_canop_arr(i, j, 0);
3176  mveg_arr(i, j, 0) = mw_arr(i, j, 0);
3177  mveg_arr(i, j, d_khi_lsm) = mw_arr(i, j, 0);
3178  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
3179  tsurf_arr(i, j, 0) = tsurf_arr(i, j, d_khi_lsm); // make sure this is set at k=0 for radiation coupling
3180  flbq_arr(i, j, d_khi_lsm) = evp_air_arr(i, j, 0) / rhow; // kg/kg m/s
3181  flbt_arr(i, j, d_khi_lsm) = shf_air_arr(i, j, 0) / (Cp_d*rhow); // Km/s
3182 
3183  qstar_arr(i, j, 0) = -1.0 * flbq_arr(i, j, d_khi_lsm) / ustar_arr(i, j, 0);
3184  qstar_arr(i, j, d_khi_lsm) = qstar_arr(i, j, 0);
3185 
3186  // These terms are multiplied by rho in SurfaceLayer
3187  fluxq_arr(i,j,0) = flbq_arr(i, j, d_khi_lsm);
3188  fluxt_arr(i,j,0) = flbt_arr(i, j, d_khi_lsm);
3189  tau13_arr(i,j,0) = flbu_arr(i, j, d_khi_lsm) / rhow;
3190  tau23_arr(i,j,0) = flbv_arr(i, j, d_khi_lsm) / rhow;
3191 
3192  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));
3193  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));
3194 
3195  });
3196  }
3197 
3198  lsm_fab_vars[LsmVar_SLM::tsurf]->FillBoundary(m_geom.periodicity());
3199  lsm_fab_vars[LsmVar_SLM::ustar]->FillBoundary(m_geom.periodicity());
3200  lsm_fab_vars[LsmVar_SLM::tstar]->FillBoundary(m_geom.periodicity());
3201  lsm_fab_vars[LsmVar_SLM::qstar]->FillBoundary(m_geom.periodicity());
3202  lsm_fab_vars[LsmVar_SLM::flbu]->FillBoundary(m_geom.periodicity());
3203  lsm_fab_vars[LsmVar_SLM::flbv]->FillBoundary(m_geom.periodicity());
3204 
3205  // TODO: fix;
3206  lsm_fab_flux[LsmFlux_SLM::q_flux]->FillBoundary(m_geom.periodicity());
3207  lsm_fab_flux[LsmFlux_SLM::t_flux]->FillBoundary(m_geom.periodicity());
3208  lsm_fab_flux[LsmFlux_SLM::tau13]->FillBoundary(m_geom.periodicity());
3209  lsm_fab_flux[LsmFlux_SLM::tau23]->FillBoundary(m_geom.periodicity());
3210  lsm_fab_flux[LsmFlux_SLM::olen]->FillBoundary(m_geom.periodicity());
3211 }
constexpr amrex::Real KAPPA
Definition: ERF_Constants.H:67
constexpr amrex::Real Cp_d
Definition: ERF_Constants.H:48
constexpr amrex::Real CONST_GRAV
Definition: ERF_Constants.H:68
constexpr amrex::Real R_d
Definition: ERF_Constants.H:46
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:763
amrex::MultiFab t_canop
Definition: ERF_SLM.H:762
amrex::MultiFab evp_air
Definition: ERF_SLM.H:831
amrex::MultiFab q_gr
Definition: ERF_SLM.H:772
amrex::iMultiFab landmask
Definition: ERF_SLM.H:776
amrex::MultiFab mw_mx
Definition: ERF_SLM.H:803
amrex::MultiFab shf_air
Definition: ERF_SLM.H:823
int khi_lsm
Definition: ERF_SLM.H:699
amrex::MultiFab net_rad
Definition: ERF_SLM.H:847
void radiation_noahmp(const amrex::MFIter &mfi)
Definition: ERF_SLM.cpp:5628
amrex::MultiFab mw_inc
Definition: ERF_SLM.H:814
amrex::MultiFab cbiom
Definition: ERF_SLM.H:787
void soil_temperature(const amrex::MFIter &mfi)
Definition: ERF_SLM.cpp:4448
amrex::MultiFab LAI
Definition: ERF_SLM.H:779
amrex::MultiFab wet_canop
Definition: ERF_SLM.H:848
static constexpr amrex::Real cp_water
Definition: ERF_SLM.H:748
amrex::MultiFab q_sfc
Definition: ERF_SLM.H:768
amrex::MultiFab t_sfc
Definition: ERF_SLM.H:767
amrex::MultiFab t_cas
Definition: ERF_SLM.H:769
void soil_nudging(const amrex::MFIter &mfi3d)
Definition: ERF_SLM.cpp:4611
amrex::MultiFab vege_YES
Definition: ERF_SLM.H:785
void fluxes_canopy(const amrex::MFIter &mfi)
Definition: ERF_SLM.cpp:3728
amrex::MultiFab slm_diag
Definition: ERF_SLM.H:950
amrex::Array< FabPtr, LsmVar_SLM::NumVars > lsm_fab_vars
Definition: ERF_SLM.H:704
amrex::MultiFab precip_extinc
Definition: ERF_SLM.H:801
static constexpr amrex::Real tfriz
Definition: ERF_SLM.H:742
amrex::MultiFab t_skin
Definition: ERF_SLM.H:765
void resistances(const amrex::MFIter &mfi)
Definition: ERF_SLM.cpp:3438
amrex::Geometry m_geom
Definition: ERF_SLM.H:679
amrex::MultiFab mws
Definition: ERF_SLM.H:764
amrex::MultiFab r_d
Definition: ERF_SLM.H:842
amrex::iMultiFab landtype
Definition: ERF_SLM.H:777
amrex::MultiFab r_c
Definition: ERF_SLM.H:841
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:4660
void transfer_coeff(const amrex::MFIter &mfi)
Definition: ERF_SLM.cpp:3214
static constexpr amrex::Real r_litter
Definition: ERF_SLM.H:844
void soil_water(const amrex::MFIter &mfi)
Definition: ERF_SLM.cpp:4172
void solve_ground_skin_temperature(const amrex::MFIter &mfi)
Definition: ERF_SLM.cpp:3885
amrex::MultiFab r_a
Definition: ERF_SLM.H:839
amrex::MultiFab t_ground_skin
Definition: ERF_SLM.H:766
amrex::MultiFab r_b
Definition: ERF_SLM.H:840
void UpdateLAI(const amrex::MFIter &mfi)
Definition: ERF_SLM.cpp:2714
amrex::MultiFab q_cas
Definition: ERF_SLM.H:770
amrex::MultiFab sdew
Definition: ERF_SLM.H:773
amrex::MultiFab cp_vege
Definition: ERF_SLM.H:786
amrex::MultiFab r_soil
Definition: ERF_SLM.H:843
amrex::iMultiFab vegetype
Definition: ERF_SLM.H:784
amrex::Array< FabPtr, LsmFlux_SLM::NumVars > lsm_fab_flux
Definition: ERF_SLM.H:707
amrex::MultiFab SAI
Definition: ERF_SLM.H:780
@ 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.

4912 {
4913  for ( amrex::MFIter mfi(cons_in,amrex::TilingIfNotGPU()); mfi.isValid(); ++mfi) {
4914  const auto& box3d = mfi.tilebox();
4915 
4916  // get potential total density, temperature, qt, qp
4917  ParallelFor( box3d, [=] AMREX_GPU_DEVICE (int /*i*/, int /*j*/, int /*k*/)
4918  {
4919  // TODO
4920  /*
4921  states_arr(i,j,k,RhoTheta_comp) = rho_arr(i,j,k)*theta_arr(i,j,k);
4922  states_arr(i,j,k,RhoQ1_comp) = rho_arr(i,j,k)*qv_arr(i,j,k);
4923  states_arr(i,j,k,RhoQ2_comp) = rho_arr(i,j,k)*qc_arr(i,j,k);
4924  states_arr(i,j,k,RhoQ3_comp) = rho_arr(i,j,k)*qp_arr(i,j,k);
4925  */
4926  });
4927  }
4928 
4929  // Fill interior ghost cells and periodic boundaries
4930  cons_in.FillBoundary(m_geom.periodicity());
4931 }

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.

4681 {
4682  int khi = khi_lsm;
4683 
4685 
4686  // Get the temperature, density, pressure at the reference level
4687  for ( MFIter mfi(*tsurf, TileNoZ()); mfi.isValid(); ++mfi) {
4688  const auto& box3d = mfi.tilebox();
4689 
4690  // Create a box with the same i,j bounds, but only at z = 0
4691  amrex::Box b2d = box3d;
4692  b2d.setRange(2, 0);
4693 
4694  auto states_array = cons_in.array(mfi);
4695  auto u_array = u_in.array(mfi);
4696  auto v_array = v_in.array(mfi);
4697 
4698  auto tref_array = lsm_fab_vars[LsmVar_SLM::tref]->array(mfi);
4699  auto rho_array = lsm_fab_vars[LsmVar_SLM::dref]->array(mfi);
4700  auto pres_array = lsm_fab_vars[LsmVar_SLM::pref]->array(mfi);
4701  auto qref_array = lsm_fab_vars[LsmVar_SLM::qref]->array(mfi);
4702  auto slm_u = lsm_fab_vars[LsmVar_SLM::uref]->array(mfi);
4703  auto slm_v = lsm_fab_vars[LsmVar_SLM::vref]->array(mfi);
4704 
4705  auto slm_dir_sw_vis = lsm_fab_vars[LsmVar_SLM::swdsvisxyref]->array(mfi);
4706  auto slm_dir_sw_nir = lsm_fab_vars[LsmVar_SLM::swdsnirxyref]->array(mfi);
4707  auto slm_diff_sw_vis = lsm_fab_vars[LsmVar_SLM::swdsvisdxyref]->array(mfi);
4708  auto slm_diff_sw_nir = lsm_fab_vars[LsmVar_SLM::swdsnirdxyref]->array(mfi);
4709  auto slm_lw = lsm_fab_vars[LsmVar_SLM::lwref]->array(mfi);
4710  auto slm_zenith = lsm_fab_vars[LsmVar_SLM::coszrsxy]->array(mfi);
4711 
4712  ParallelFor(b2d, [=] AMREX_GPU_DEVICE (int i, int j, int k)
4713  {
4714  const Real qv = states_array(i,j,k,RhoQ1_comp)/states_array(i,j,k,Rho_comp);
4715  rho_array(i,j,k) = states_array(i,j,k,Rho_comp);
4716  pres_array(i,j,k) = getPgivenRTh(states_array(i,j,k,RhoTheta_comp), qv)/100.;
4717  tref_array(i,j,k) = getTgivenRandRTh(states_array(i,j,k,Rho_comp),
4718  states_array(i,j,k,RhoTheta_comp),
4719  qv);
4720 
4721  qref_array(i,j,k) = qv;
4722 
4723  amrex::Real u_cc = myhalf * (u_array(i, j, k) + u_array(i + 1, j, k));
4724  amrex::Real v_cc = myhalf * (v_array(i, j, k) + v_array(i, j + 1, k));
4725  slm_u(i, j, k) = u_cc;
4726  slm_v(i, j, k) = v_cc;
4727 
4728  // TODO: this is for plotting purposes.. state arrays are at k=0 which is ghost cell for SLM values
4729  // SLM AMREX plotfile does not write ghost cells, but NetCDF does - fix?
4730  rho_array(i, j, khi) = rho_array(i, j, 0);
4731  pres_array(i, j, khi) = pres_array(i, j, 0);
4732  tref_array(i, j, khi) = tref_array(i, j, 0);
4733  qref_array(i, j, khi) = qref_array(i, j, 0);
4734  slm_u(i, j, khi) = slm_u(i, j, 0);
4735  slm_v(i, j, khi) = slm_v(i, j, 0);
4736 
4737  slm_dir_sw_vis(i, j, khi) = slm_dir_sw_vis(i, j, 0);
4738  slm_dir_sw_nir(i, j, khi) = slm_dir_sw_nir(i, j, 0);
4739  slm_diff_sw_vis(i, j, khi) = slm_diff_sw_vis(i, j, 0);
4740  slm_diff_sw_nir(i, j, khi) = slm_diff_sw_nir(i, j, 0);
4741  slm_lw(i, j, khi) = slm_lw(i, j, 0);
4742  slm_zenith(i, j, khi) = slm_zenith(i, j, 0);
4743  });
4744  }
4745 
4746 #ifdef ERF_USE_NETCDF
4747  if (rad_input_file != "")
4748  {
4749  int tindex = 0;
4750  int tindex_next = 0;
4751  for (int k = 1; k < num_rad_times; k++)
4752  {
4753  if (time > rad_times[k])
4754  {
4755  tindex = k;
4756  }
4757  }
4758 
4759  if (tindex == num_rad_times - 1)
4760  {
4761  tindex_next = tindex;
4762  } else {
4763  tindex_next = tindex + 1;
4764  }
4765 
4766  const amrex::Real t0 = rad_times[tindex];
4767  const amrex::Real t1 = rad_times[tindex_next];
4768 
4769  amrex::Print() << " SLM: time = " << time << ", interpolating fluxes at index " << tindex << " (t0 = " << t0 << " t1 = " << t1 << ")" << std::endl;
4770 
4771  const amrex::Real d_time = time;
4772  for ( MFIter mfi(rad_input_data); mfi.isValid(); ++mfi) {
4773  const auto& box3d = mfi.tilebox();
4774 
4775  // Create a box with the same i,j bounds, but only at z = 0
4776  amrex::Box b2d = box3d;
4777  b2d.setRange(2, 0);
4778 
4779  auto rad_arr = rad_input_data.const_array(mfi);
4780 
4781  auto slm_dir_sw_vis = lsm_fab_vars[LsmVar_SLM::swdsvisxyref]->array(mfi);
4782  auto slm_dir_sw_nir = lsm_fab_vars[LsmVar_SLM::swdsnirxyref]->array(mfi);
4783  auto slm_diff_sw_vis = lsm_fab_vars[LsmVar_SLM::swdsvisdxyref]->array(mfi);
4784  auto slm_diff_sw_nir = lsm_fab_vars[LsmVar_SLM::swdsnirdxyref]->array(mfi);
4785 
4786  auto slm_lw = lsm_fab_vars[LsmVar_SLM::lwref]->array(mfi);
4787  auto slm_zenith = lsm_fab_vars[LsmVar_SLM::coszrsxy]->array(mfi);
4788 
4789  ParallelFor(b2d, [=] AMREX_GPU_DEVICE (int i, int j, int k)
4790  {
4791  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));
4792  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));
4793 
4794  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));
4795  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));
4796 
4797  slm_zenith(i, j, k) = linear_interp(t0, t1, d_time, rad_arr(i, j, tindex, 4), rad_arr(i, j, tindex_next, 4));
4798  slm_lw(i, j, k) = linear_interp(t0, t1, d_time, rad_arr(i, j, tindex, 5), rad_arr(i, j, tindex_next, 5));
4799 
4800 
4801  // TODO: this is for plotting purposes.. state arrays are at k=0 which is ghost cell for SLM values
4802  // SLM AMREX plotfile does not write ghost cells, but NetCDF does - fix?
4803  slm_dir_sw_vis(i, j, khi) = slm_dir_sw_vis(i, j, 0);
4804  slm_dir_sw_nir(i, j, khi) = slm_dir_sw_nir(i, j, 0);
4805  slm_diff_sw_vis(i, j, khi) = slm_diff_sw_vis(i, j, 0);
4806  slm_diff_sw_nir(i, j, khi) = slm_diff_sw_nir(i, j, 0);
4807  slm_lw(i, j, khi) = slm_lw(i, j, 0);
4808  slm_zenith(i, j, khi) = slm_zenith(i, j, 0);
4809  });
4810  }
4811  return;
4812  }
4813 #endif
4814 }
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:42
#define RhoTheta_comp
Definition: ERF_IndexDefines.H:43
#define RhoQ1_comp
Definition: ERF_IndexDefines.H:48
const int khi
Definition: ERF_InitCustomPert_Bubble.H:21
constexpr amrex::Real myhalf
Definition: ERF_NumericalConstants.H:41
std::string rad_input_file
Definition: ERF_SLM.H:719
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:4668
@ 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  // Check if initializing from WRFInput
216  if (sc.init_type == InitType::WRFInput) {
217  amrex::Print() << " SLM is initializing from WRFInput!" << std::endl;
218  use_wrfinput = true;
219  } else {
220  use_wrfinput = false;
221  }
222  }
AMREX_ALWAYS_ASSERT_WITH_MESSAGE(m_cloud_chamber_config.active, "Cloud Chamber: initializer reached without a parsed configuration")
bool use_terrain
Definition: ERF_SLM.H:735
@ None
Definition: ERF_LSM_Types.H:6
static InitType init_type
Initial-condition source selected for the run.
Definition: ERF_DataStruct.H:2218
MoistureType moisture_type
Moisture or microphysics model.
Definition: ERF_DataStruct.H:2524
static TerrainType terrain_type
Terrain or immersed-boundary representation.
Definition: ERF_DataStruct.H:2224
Here is the call graph for this function:

◆ export_to_RRTMGP()

const amrex::Vector<const amrex::MultiFab*>& SLM::export_to_RRTMGP ( )
inline
636  {
637  return slm_to_rad_vars;
638  }
amrex::Vector< const amrex::MultiFab * > slm_to_rad_vars
Definition: ERF_SLM.H:952

◆ 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
4661 {
4662  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));
4663  return std::min(amrex::Real(one), std::exp(moist_pot1*CONST_GRAV/amrex::Real(461.0)/t));
4664 }
@ t
Definition: ERF_WSM6.H:272

◆ fluxes_canopy()

void SLM::fluxes_canopy ( const amrex::MFIter &  mfi)
3729 {
3730  const int d_khi_lsm = khi_lsm;
3731  const int d_nz_lsm = m_nz_lsm;
3732  const Real dt = m_dt;
3733  const int niter = std::max(1, static_cast<int>(std::round(dt)));
3734  const Real dt_iter = dt/niter;
3735 
3736  auto box = mfi.tilebox();
3737 
3738  auto landmask_arr = landmask.const_array(mfi);
3739 
3740  auto LAI_arr = LAI.const_array(mfi);
3741 
3742  auto t_sfc_arr = t_sfc.const_array(mfi);
3743  auto q_sfc_arr = q_sfc.const_array(mfi);
3744 
3745  auto soilw_arr = lsm_fab_vars[LsmVar_SLM::soilw]->const_array(mfi);
3746  auto poro_soil_arr = lsm_fab_vars[LsmVar_SLM::poro_soil]->const_array(mfi);
3747  auto s_depth_arr = lsm_fab_vars[LsmVar_SLM::s_depth]->const_array(mfi);
3748  auto soil_transp_frac_arr = lsm_fab_vars[LsmVar_SLM::soil_transp_frac]->const_array(mfi);
3749  auto w_s_WP_arr = lsm_fab_vars[LsmVar_SLM::w_s_WP]->const_array(mfi);
3750  auto pref_arr = lsm_fab_vars[LsmVar_SLM::pref]->const_array(mfi);
3751 
3752  auto lhf_canop_arr = lhf_canop.array(mfi);
3753  auto evp_canop_arr = evp_canop.array(mfi);
3754  auto shf_canop_arr = shf_canop.array(mfi);
3755 
3756  auto vegetype_arr = vegetype.const_array(mfi);
3757  auto vege_YES_arr = vege_YES.const_array(mfi);
3758 
3759  auto mw_arr = mw.array(mfi);
3760  auto mw_mx_arr = mw_mx.const_array(mfi);
3761  auto mw_inc_arr = mw_inc.array(mfi);
3762  auto cp_vege_arr = cp_vege.const_array(mfi);
3763 
3764  auto dref_arr = lsm_fab_vars[LsmVar_SLM::dref]->const_array(mfi);
3765  auto tr_arr = lsm_fab_vars[LsmVar_SLM::tref]->const_array(mfi);
3766  auto t_canop_arr = t_canop.array(mfi);
3767 
3768  auto r_b_arr = r_b.const_array(mfi);
3769  auto r_c_arr = r_c.const_array(mfi);
3770 
3771  auto wet_canop_arr = wet_canop.array(mfi);
3772  auto evapo_dry_arr = evapo_dry.array(mfi);
3773 
3774  auto slm_diag_arr = slm_diag.array(mfi);
3775 
3776  auto net_rad_arr = net_rad.const_array(mfi);
3777 
3778  auto prsfc_arr = lsm_fab_vars[LsmVar_SLM::prsfc]->array(mfi);
3779 
3780  ParallelFor( box, [=] AMREX_GPU_DEVICE (int i, int j, int)
3781  {
3782  if (landmask_arr(i, j, 0) != 1) {
3783  return;
3784  }
3785  amrex::Real evapo_wet_loc;
3786 
3787  if (vegetype_arr(i, j, 0) == 1)
3788  {
3789  amrex::Real shf0 = zero;
3790  amrex::Real lhf0 = zero;
3791  amrex::Real evp0 = zero;
3792  amrex::Real evapo_wet0 = zero;
3793  amrex::Real drain0 = zero;
3794  // SAM rhow[nz] = air density at vertical velocity levels, kg/m^3
3795  const amrex::Real rhow = dref_arr(i, j, 0); // TODO: double check this
3796  amrex::Real qsat_canop;
3797  amrex::Real cp_vege_tot, t_canop_inc;
3798 
3799  for (int iter = 0; iter < niter; iter++)
3800  {
3801  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);
3802  shf0 += shf_canop_arr(i, j, 0);
3803 
3804  // Evaporation from canopy
3805  // only treat the case where t_canop_arr > tfriz;
3806  AMREX_ALWAYS_ASSERT(t_canop_arr(i, j, 0) > tfriz);
3807 
3808  erf_qsatw(t_canop_arr(i, j, 0), pref_arr(i, j, 0), qsat_canop);
3809 
3810  // direct evaporation from the water held on canopy
3811  // evaporation/dew only possible if canopy temperature is above freezing
3812  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)));
3813 
3814  // increment/decrement of the water amount held on leaves following the direct evaporation/dew formation
3815  const amrex::Real mw_evap_inc = -dt_iter * evapo_wet_loc; // evapo_wet_loc [kg/m2s=mm/s]
3816  mw_inc_arr(i, j, 0) += mw_evap_inc;
3817  mw_arr(i, j, 0) += mw_evap_inc;
3818  wet_canop_arr(i, j, 0) = std::min(amrex::Real(one), mw_arr(i, j, 0)/mw_mx_arr(i, j, 0));
3819 
3820  // Transpiration - only occurs when qsat_canop > qsfc
3821  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));
3822  if (evapo_dry_arr(i, j, 0) > zero) {
3823  amrex::Real max_transpiration = 1.0e30;
3824  bool has_transpiration_source = false;
3825  for (int k = 0; k < d_nz_lsm; k++) {
3826  const int lsm_k = d_khi_lsm - k;
3827  const amrex::Real transp_frac = std::max(amrex::Real(zero), soil_transp_frac_arr(i, j, lsm_k));
3828  if (transp_frac > zero) {
3829  has_transpiration_source = true;
3830  const amrex::Real available_water =
3831  std::max(amrex::Real(zero), soilw_arr(i, j, lsm_k) - w_s_WP_arr(i, j, lsm_k)) *
3832  poro_soil_arr(i, j, lsm_k) *
3833  s_depth_arr(i, j, lsm_k) * 1.0e3;
3834  max_transpiration = std::min(max_transpiration, available_water / (transp_frac * dt));
3835  }
3836  }
3837  evapo_dry_arr(i, j, 0) = has_transpiration_source
3838  ? std::min(evapo_dry_arr(i, j, 0), max_transpiration)
3839  : 0.0;
3840  }
3841 
3842  // Convert evaporation (kg/m2/s) to latent heat flux (W/m2)
3843  lhf_canop_arr(i, j, 0) = lcond*(evapo_wet_loc+evapo_dry_arr(i, j, 0));
3844  evp_canop_arr(i, j, 0) = evapo_wet_loc + evapo_dry_arr(i, j, 0);
3845  lhf0 += lhf_canop_arr(i, j, 0);
3846  evp0 += evp_canop_arr(i, j, 0);
3847  evapo_wet0 += evapo_wet_loc;
3848 
3849  // Update vegetation moisture storage
3850  if (mw_arr(i, j, 0) > mw_mx_arr(i, j, 0))
3851  {
3852  const amrex::Real canopy_drain = (mw_arr(i, j, 0) - mw_mx_arr(i, j, 0)) / dt_iter;
3853  drain0 += canopy_drain; // dripping excess of dew
3854  mw_inc_arr(i, j, 0) -= dt_iter * canopy_drain;
3855  mw_arr(i, j, 0) = mw_mx_arr(i, j, 0);
3856  }
3857 
3858  // Update vegetation temperature
3859  cp_vege_tot = cp_vege_arr(i, j, 0) + mw_arr(i, j, 0) * 1.e-3 * cp_water;
3860  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);
3861  t_canop_arr(i, j, 0) = std::min(amrex::Real(t_canop_max), t_canop_arr(i, j, 0) + t_canop_inc);
3862  }
3863  shf_canop_arr(i, j, 0) = shf0 / static_cast<amrex::Real>(niter);
3864  lhf_canop_arr(i, j, 0) = lhf0 / static_cast<amrex::Real>(niter);
3865  evp_canop_arr(i, j, 0) = evp0 / static_cast<amrex::Real>(niter);
3866  prsfc_arr(i, j, 0) += (drain0 / static_cast<amrex::Real>(niter));
3867  slm_diag_arr(i, j, 0, SLM_Diag::precip_sfc) = prsfc_arr(i, j, 0);
3868  slm_diag_arr(i, j, 0, SLM_Diag::evapo_wet) = evapo_wet0 / static_cast<amrex::Real>(niter);
3869  slm_diag_arr(i, j, 0, SLM_Diag::drain) += (drain0/static_cast<amrex::Real>(niter));
3870  }
3871  else
3872  {
3873  shf_canop_arr(i, j, 0) = zero;
3874  lhf_canop_arr(i, j, 0) = zero;
3875  evp_canop_arr(i, j, 0) = zero;
3876  wet_canop_arr(i, j, 0) = zero;
3877  mw_arr(i, j, 0) = zero;
3878  evapo_wet_loc = zero;
3879  evapo_dry_arr(i, j, 0) = zero;
3880  t_canop_arr(i, j, 0) = tr_arr(i, j, 0);
3881  }
3882  });
3883 }
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:829
static constexpr amrex::Real t_canop_max
Definition: ERF_SLM.H:733
amrex::MultiFab shf_canop
Definition: ERF_SLM.H:821
static constexpr amrex::Real cp
Definition: ERF_SLM.H:747
amrex::MultiFab evapo_dry
Definition: ERF_SLM.H:817
int m_nz_lsm
Definition: ERF_SLM.H:715
amrex::MultiFab lhf_canop
Definition: ERF_SLM.H:826
@ 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
641  {
642  return rad_output_map;
643  }
static std::unordered_map< std::string, std::string > rad_output_map
Definition: ERF_SLM.H:972

◆ Get_Update_Status()

bool SLM::Get_Update_Status ( )
inlineoverridevirtual

Reimplemented from NullSurf.

468 { return m_updated; }

◆ GotoNextLine()

void SLM::GotoNextLine ( std::istream &  is)
static
376 {
377  constexpr std::streamsize bl_ignore_max { 100000 };
378  is.ignore(bl_ignore_max, '\n');
379 }

◆ 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
5214 {
5215  // --------------------------------------------------------------------------------------------------
5216  //input
5217  // ILOC !grid index
5218  // JLOC !grid index
5219  // NSOIL !number of soil layers
5220  // NBAND !number of solar radiation waveband classes
5221  // ICE !value of ist for land ice
5222  // IST !surface type
5223  // FSNO !fraction of surface covered with snow (-)
5224  // TG !ground temperature (k)
5225  // COSZ !cosine solar zenith angle (0-1)
5226  // SMC !volumetric soil water content (m3/m3)
5227  // ALBSND !direct beam snow albedo (vis, nir)
5228  // ALBSNI !diffuse snow albedo (vis, nir)
5229 
5230  //output
5231  // ALBGRD !ground albedo (direct beam: vis, nir)
5232  // ALBGRI !ground albedo (diffuse: vis, nir)
5233 
5234  //local
5235  amrex::Real inc; //soil water correction factor for soil albedo
5236  amrex::Real albsod; //soil albedo (direct)
5237  amrex::Real albsoi; //soil albedo (diffuse)
5238  // --------------------------------------------------------------------------------------------------
5239  constexpr amrex::Real TFRZ = 273.16; // freezing/melting point (k)
5240 
5241  for (int ib = 0; ib < nband; ib++) {
5242  inc = std::max(amrex::Real(0.11) - amrex::Real(0.40)*smc[0], amrex::Real(zero));
5243  if (ist == 1) { //soil
5244  albsod = std::min(albsat[ib]+inc, albdry[ib]);
5245  albsoi = albsod;
5246  } else if (tg > TFRZ) { //unfrozen lake, wetland
5247  albsod = 0.06/(std::max(amrex::Real(0.01),cosz)*std::max(amrex::Real(0.01),cosz)*std::max(amrex::Real(0.01),cosz)*
5248  std::max(amrex::Real(0.01),cosz)*std::max(amrex::Real(0.01),cosz)*std::max(amrex::Real(0.01),cosz)*
5249  std::max(amrex::Real(0.01),cosz) + 0.15);
5250  albsoi = 0.06;
5251  } else { //frozen lake, wetland
5252  albsod = alblak[ib];
5253  albsoi = albsod;
5254  }
5255 
5256  albgrd[ib] = albsod*(1.0-fsno) + albsnd[ib]*fsno;
5257  albgri[ib] = albsoi*(1.0-fsno) + albsni[ib]*fsno;
5258  }
5259 }
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.

28 {
29  m_dt = dt;
30  m_geom = geom;
31  m_geom0 = geom0;
32  m_domain_bcs_type = domain_bcs_type;
33  z_phys_nd = z_phys_nd_in.get();
34 
35  ParmParse pp("slm");
36  pp.query("nsoil", m_nz_lsm);
37  if (m_nz_lsm < 2) {
38  amrex::Abort("SLM: nsoil must be at least 2");
39  }
40  pp.queryarr("soil_dz", m_dz_lsm);
42  static_cast<int>(m_dz_lsm.size()) == m_nz_lsm,
43  "Provided soil thicknesses most match number of soil layers");
44  AMREX_ALWAYS_ASSERT(static_cast<int>(m_dz_lsm.size()) > 0);
45  for (int k = 0; k < static_cast<int>(m_dz_lsm.size()); ++k) {
46  if (!std::isfinite(m_dz_lsm[k]) || m_dz_lsm[k] <= zero) {
47  amrex::Abort("SLM: soil_dz values must be finite and positive");
48  }
49  }
50 
51  Box domain = geom.Domain();
52  khi_lsm = domain.smallEnd(2) - 1; // index of z_r
53  klo_lsm = khi_lsm - m_nz_lsm + 1;
54 
56  LsmDataMap = {
71 
73  LsmDataName = {"tsurf", "ustar", "tstar",
74  "qstar", "tveg", "mv",
75  "tsoil", "wsoil", "sand",
76  "clay", "soil_thickness", "surface_u",
77  "surface_v", "surface_vapor", "surface_heat",
78  "precip_soil", "ref_precip", "SW_dw_dir_vis",
79  "SW_dw_dir_nir", "SW_dw_dif_vis", "SW_dw_dif_nir",
80  "LW_dw", "cos_zenith", "ref_t", "ref_u",
81  "ref_v", "ref_d", "ref_q",
82  "ref_p", "node_z", "soilt_nudge",
83  "soilw_nudge", "lai", "vegtype",
84  "soiltype", "veg_frac", "veg_frac_min", "veg_frac_max", "emis_sfc",
85  "alb_nir_sfc", "alb_vis_sfc", "alb_nir_sfc_diff", "alb_vis_sfc_diff",
86  "soil_transp_frac"};
87 
88  AMREX_ALWAYS_ASSERT(LsmDataMap.size() == LsmDataName.size());
89  AMREX_ALWAYS_ASSERT(static_cast<int>(LsmDataMap.size()) == m_lsm_data_size);
90 
93 
95  LsmFluxName = {"t_flux", "q_flux", "tau13", "tau23", "olen"};
96 
97  AMREX_ALWAYS_ASSERT(LsmFluxMap.size() == LsmFluxName.size());
98  AMREX_ALWAYS_ASSERT(static_cast<int>(LsmFluxMap.size()) == m_lsm_flux_size);
99 
100  // NOTE: All boxes in ba extend from zlo to zhi, so this transform is valid.
101  // If that were to change, the dm and new ba are no longer valid and
102  // direct copying between lsm data/flux vars cannot be done in a parfor.
103 
104  // Set box array for lsm data
105  IntVect ng(1,1,1);
106  BoxArray ba = cons_in.boxArray();
107  DistributionMapping dm = cons_in.DistributionMap();
108  BoxList bl_lsm = ba.boxList();
109  for (auto& b : bl_lsm) {
110  b.setBig(2, khi_lsm); // First point below the surface
111  b.setSmall(2, klo_lsm); // Last point below the surface
112  }
113  BoxArray ba_lsm(std::move(bl_lsm));
114 
115  // Set up lsm geometry
116  const RealBox& dom_rb = m_geom.ProbDomain();
117  RealBox lsm_rb = dom_rb;
118  Real lsm_z_hi = dom_rb.lo(2); // z_r
119  Real lsm_z_lo = lsm_z_hi;
120  for (int k = 0; k < m_nz_lsm; k++)
121  {
122  lsm_z_lo -= m_dz_lsm[k];
123  }
124  lsm_rb.setHi(2,lsm_z_hi); lsm_rb.setLo(2,lsm_z_lo);
125 
126  amrex::Box lsm_dom = m_geom.Domain();
127  lsm_dom.setSmall(2, klo_lsm);
128  lsm_dom.setBig(2, khi_lsm);
129  m_lsm_geom.define(lsm_dom, lsm_rb, m_geom.Coord(), m_geom.isPeriodic());
130 
131  const RealBox& dom_rb0 = m_geom0.ProbDomain();
132  RealBox lsm_rb0 = dom_rb0;
133  Real lsm_z_hi0 = dom_rb0.lo(2);
134  Real lsm_z_lo0 = lsm_z_hi0;
135  for (int k = 0; k < m_nz_lsm; ++k) {
136  lsm_z_lo0 -= m_dz_lsm[k];
137  }
138  lsm_rb0.setHi(2, lsm_z_hi0);
139  lsm_rb0.setLo(2, lsm_z_lo0);
140 
141  Box lsm_dom0 = m_geom0.Domain();
142  const int khi_lsm0 = lsm_dom0.smallEnd(2) - 1;
143  const int klo_lsm0 = khi_lsm0 - m_nz_lsm + 1;
144  lsm_dom0.setSmall(2, klo_lsm0);
145  lsm_dom0.setBig(2, khi_lsm0);
146  m_lsm_geom0.define(lsm_dom0, lsm_rb0, m_geom0.Coord(), m_geom0.isPeriodic());
147 
148  BoxList bl_lsm_2d = ba_lsm.boxList();
149  for (auto& b : bl_lsm_2d) {
150  b.setRange(2, 0, 1);
151  }
152  ba_lsm_2d = BoxArray(std::move(bl_lsm_2d));
153  IntVect ng_2d(0, 0, 0);
154 
155  // Create the data
156  for (auto ivar = 0; ivar < LsmVar_SLM::NumVars; ++ivar) {
157  // State vars are CC
158  lsm_fab_vars[ivar] = std::make_shared<MultiFab>(ba_lsm, dm, 1, ng);
159  lsm_fab_vars[ivar]->setVal(zero);
160  }
161 
162  // Create the fluxes
163  for (auto ivar = 0; ivar < LsmFlux_SLM::NumVars; ++ivar) {
164  // NOTE: Fluxes are CC with ghost cells for averaging
165  lsm_fab_flux[ivar] = std::make_shared<MultiFab>(ba_lsm_2d, dm, 1, IntVect(1,1,0));
166  lsm_fab_flux[ivar]->setVal(zero);
167  }
168 
169  // build list for checkpointing extra variables not mapped to ERF in lsm_fab_vars
170  unmapped_fields.clear();
171  for (int i = 0; i < LsmVar_SLM::NumVars; i++) {
172  int found = 0;
173  for (int j = 0; j < m_lsm_data_size; j++) {
174  if (LsmDataMap[j] == i) {
175  found = 1;
176  break;
177  }
178  }
179  if (!found) {
180  unmapped_fields.push_back(i);
181  }
182  }
183 
184  // Initial olen to neutral condition
185  lsm_fab_flux[LsmFlux_SLM::olen]->setVal(1.0E34);
186 
187  // packed temporary 1D arrays in soil water and soil temperature
188  soilt_vars.define(ba_lsm, dm, SLM_DST::NumVars, 0);
189  soilw_vars.define(ba_lsm, dm, SLM_DSW::NumVars, 0);
190  soilt_vars.setVal(zero);
191  soilw_vars.setVal(zero);
192 
193  // Create local 2D data
194  // TODO: Placeholder landmask array - fix!
195  landmask.define(ba_lsm_2d, dm, 1, ng_2d);
196  landmask.setVal(1);
197 
198  landtype.define(ba_lsm_2d, dm, 1, ng_2d);
199  nroot.define(ba_lsm_2d, dm, 1, ng_2d);
200  LAI.define(ba_lsm_2d, dm, 1, ng_2d);
201  SAI.define(ba_lsm_2d, dm, 1, ng_2d);
202  sstxy.define(ba_lsm_2d, dm, 1, ng_2d);
203 
204  t_canop.define(ba_lsm_2d, dm, 1, ng_2d);
205  mw.define(ba_lsm_2d, dm, 1, ng_2d);
206  mws.define(ba_lsm_2d, dm, 1, ng_2d);
207  t_skin.define(ba_lsm_2d, dm, 1, ng_2d);
208  t_ground_skin.define(ba_lsm_2d, dm, 1, ng_2d);
209  t_cas.define(ba_lsm_2d, dm, 1, ng_2d);
210  q_cas.define(ba_lsm_2d, dm, 1, ng_2d);
211 
212  t_sfc.define(ba_lsm_2d, dm, 1, ng_2d);
213  q_sfc.define(ba_lsm_2d, dm, 1, ng_2d);
214 
215  q_gr.define(ba_lsm_2d, dm, 1, ng_2d);
216  sdew.define(ba_lsm_2d, dm, 1, ng_2d);
217 
218  // NOAHMP radiation state variables
219  albold_noahmp.define(ba_lsm_2d, dm, 1, ng_2d);
220  tauss_noahmp.define(ba_lsm_2d, dm, 1, ng_2d);
221 
222  vegetype.define(ba_lsm_2d, dm, 1, ng_2d);
223  vege_YES.define(ba_lsm_2d, dm, 1, ng_2d);
224  cp_vege.define(ba_lsm_2d, dm, 1, ng_2d);
225  cbiom.define(ba_lsm_2d, dm, 1, ng_2d);
226  dleaf.define(ba_lsm_2d, dm, 1, ng_2d);
227  z0_sfc.define(ba_lsm_2d, dm, 1, ng_2d);
228  Khai_L.define(ba_lsm_2d, dm, 1, ng_2d);
229  phi_1.define(ba_lsm_2d, dm, 1, ng_2d);
230  phi_2.define(ba_lsm_2d, dm, 1, ng_2d);
231  IR_emis_vege.define(ba_lsm_2d, dm, 1, ng_2d);
232  IR_emis_soil.define(ba_lsm_2d, dm, 1, ng_2d);
233  IR_emis_grnd.define(ba_lsm_2d, dm, 1, ng_2d);
234  ztop.define(ba_lsm_2d, dm, 1, ng_2d);
235  disp_hgt.define(ba_lsm_2d, dm, 1, ng_2d);
236  Rgl.define(ba_lsm_2d, dm, 1, ng_2d);
237  Rc_min.define(ba_lsm_2d, dm, 1, ng_2d);
238  hs_rc.define(ba_lsm_2d, dm, 1, ng_2d);
239  precip_extinc.define(ba_lsm_2d, dm, 1, ng_2d);
240  mw_mx.define(ba_lsm_2d, dm, 1, ng_2d);
241  mws_mx.define(ba_lsm_2d, dm, 1, ng_2d);
242  BAI.define(ba_lsm_2d, dm, 1, ng_2d);
243  IMPERV.define(ba_lsm_2d, dm, 1, ng_2d);
244 
245  cp_vege.setVal(zero);
246  cbiom.setVal(0.02);
247  dleaf.setVal(0.04);
248  z0_sfc.setVal(zero);
249  Khai_L.setVal(zero);
250  phi_1.setVal(zero);
251  phi_2.setVal(zero);
252  ztop.setVal(zero);
253  disp_hgt.setVal(zero);
254  Rgl.setVal(zero);
255  Rc_min.setVal(zero);
256  hs_rc.setVal(zero);
257  precip_extinc.setVal(zero);
258  mw_mx.setVal(zero);
259  mws_mx.setVal(zero);
260  BAI.setVal(zero);
261  IMPERV.setVal(zero);
262 
263  mw_inc.define(ba_lsm_2d, dm, 1, ng_2d);
264 
265  evapo_dry.define(ba_lsm_2d, dm, 1, ng_2d);
266 
267  shf_canop.define(ba_lsm_2d, dm, 1, ng_2d);
268  shf_soil.define(ba_lsm_2d, dm, 1, ng_2d);
269  shf_air.define(ba_lsm_2d, dm, 1, ng_2d);
270  lhf_canop.define(ba_lsm_2d, dm, 1, ng_2d);
271  lhf_soil.define(ba_lsm_2d, dm, 1, ng_2d);
272  lhf_air.define(ba_lsm_2d, dm, 1, ng_2d);
273  evp_canop.define(ba_lsm_2d, dm, 1, ng_2d);
274  evp_soil.define(ba_lsm_2d, dm, 1, ng_2d);
275  evp_air.define(ba_lsm_2d, dm, 1, ng_2d);
276 
277  albedovis_v.define(ba_lsm_2d, dm, 1, ng_2d);
278  albedonir_v.define(ba_lsm_2d, dm, 1, ng_2d);
279  albedovis_s.define(ba_lsm_2d, dm, 1, ng_2d);
280  albedonir_s.define(ba_lsm_2d, dm, 1, ng_2d);
281  albedovis_v.setVal(zero);
282  albedovis_s.setVal(zero);
283  albedonir_v.setVal(zero);
284  albedonir_s.setVal(zero);
285  IR_emis_vege.setVal(zero);
286  IR_emis_soil.setVal(0.98);
287  IR_emis_grnd.setVal(zero);
288  vege_YES.setVal(zero);
289 
290  r_a.define(ba_lsm_2d, dm, 1, ng_2d);
291  r_b.define(ba_lsm_2d, dm, 1, ng_2d);
292  r_c.define(ba_lsm_2d, dm, 1, ng_2d);
293  r_d.define(ba_lsm_2d, dm, 1, ng_2d);
294  r_soil.define(ba_lsm_2d, dm, 1, ng_2d);
295 
296  net_rad.define(ba_lsm_2d, dm, SLM_NetRad::NumVars, ng_2d);
297  wet_canop.define(ba_lsm_2d, dm, 1, ng_2d);
298 
299  slm_diag.define(ba_lsm_2d, dm, SLM_Diag::NumVars, ng_2d);
300  slm_diag.setVal(zero);
301 
302  zrefxy.define(ba_lsm_2d, dm, 1, ng_2d);
303  zrefxy.setVal(zero);
304 
305  r_soil.setVal(zero);
306  lhf_air.setVal(zero);
307  lhf_canop.setVal(zero);
308  shf_air.setVal(zero);
309  shf_canop.setVal(zero);
310  evp_air.setVal(zero);
311  evp_canop.setVal(zero);
312  evp_soil.setVal(zero);
313 
314  t_canop.setVal(zero);
315  t_cas.setVal(zero);
316  t_skin.setVal(zero);
317  t_ground_skin.setVal(zero);
318  q_cas.setVal(zero);
319 
320  t_sfc.setVal(zero);
321  q_sfc.setVal(zero);
322  q_gr.setVal(zero);
323  sdew.setVal(zero);
324  wet_canop.setVal(zero);
325 
326  mw.setVal(zero);
327  mws.setVal(zero);
328  mw_inc.setVal(zero);
329  evapo_dry.setVal(zero);
330 
331  r_a.setVal(zero);
332  r_b.setVal(zero);
333  r_c.setVal(zero);
334  r_d.setVal(zero);
335  r_soil.setVal(zero);
336  lsm_fab_vars[LsmVar_SLM::ustar]->setVal(0.1);
339 
340  // Initialize SLM from inputs (for idealized cases, if specified), and load any other parameters
342 
343  if (!use_wrfinput) {
344  // Supply a valid default category when no external soil category is available.
346  }
347 
348  landtype.setVal(landtype0);
349  nroot.setVal(m_nz_lsm);
350  LAI.setVal(LAI0);
351  SAI.setVal(zero);
352  sstxy.setVal(zero);
353  mws_mx.setVal(mws_mx0);
354 
355  net_rad.setVal(zero);
356 
357  // Initialize NOAHMP radiation state variables
358  albold_noahmp.setVal(0.65); // initial snow albedo
359  tauss_noahmp.setVal(zero); // initial snow age
360 
361  // Set a default vegetation fraction of 1.0.
362  // Cells without vegetation (baresoil, urban) are set to 0.0 later on
363  // If using WRFInput, this fraction will be overwritten with those values
365 
366  if (!use_wrfinput) {
367  // Initialize here if not using wrfinput, otherwise it is done at first time step
368  slm_init();
369  }
370 
371  // Following Noah-MP, zref is modified to become ztop (canopy top height) + dz0 (center height of the atmosphere's lowest grid)
372  if (!use_wrfinput) {
373  Real zlo = m_geom.ProbLo(2);
374  Real dz = m_geom.CellSize(2);
375  for ( MFIter mfi(cons_in,TileNoZ()); mfi.isValid(); ++mfi) {
376  const Box& xybx = mfi.growntilebox(0);
377  const Array4<const Real>& z_nd_arr = (use_terrain) ? z_phys_nd->const_array(mfi) : Array4<Real>{};
378  auto ztop_arr = ztop.array(mfi);
379  auto zrefxy_arr = zrefxy.array(mfi);
380 
381  ParallelFor(xybx, [=] AMREX_GPU_DEVICE(int i, int j, int) noexcept {
382  amrex::Real znd = (z_nd_arr) ? Compute_Zrel_AtCellCenter(i, j, 0, z_nd_arr) : zlo + myhalf*dz;
383  zrefxy_arr(i,j,0) = ztop_arr(i,j,0) + znd;
384  });
385  }
386  } else {
387  zrefxy.setVal(zref); // set zrefxy with the value read from inputs file
388  }
389 
390  //slm_to_rad_vars = {Lsm_Data_Ptr(LsmVar_SLM::tsurf), &albedovis_s, &albedovis_v, &albedonir_s, &albedonir_v, &IR_emis_vege, &net_rad};
392  Lsm_Data_Ptr(Lsm_DataIndex("emis_sfc")),
393  Lsm_Data_Ptr(Lsm_DataIndex("alb_vis_sfc")),
394  Lsm_Data_Ptr(Lsm_DataIndex("alb_nir_sfc")),
395  Lsm_Data_Ptr(Lsm_DataIndex("alb_vis_sfc_diff")),
396  Lsm_Data_Ptr(Lsm_DataIndex("alb_nir_sfc_diff"))};
397 }
ParmParse pp("prob")
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:758
amrex::MultiFab z0_sfc
Definition: ERF_SLM.H:789
amrex::Geometry m_geom0
Definition: ERF_SLM.H:682
int Lsm_DataIndex(const std::string &varname) override
Definition: ERF_SLM.H:409
amrex::Vector< amrex::BCRec > m_domain_bcs_type
Definition: ERF_SLM.H:691
int m_lsm_flux_size
Definition: ERF_SLM.H:668
amrex::MultiFab shf_soil
Definition: ERF_SLM.H:822
void init_from_inputs()
Definition: ERF_SLM.cpp:408
amrex::MultiFab Khai_L
Definition: ERF_SLM.H:790
int landtype0
Definition: ERF_SLM.H:722
amrex::Geometry m_lsm_geom0
Definition: ERF_SLM.H:688
amrex::MultiFab phi_2
Definition: ERF_SLM.H:792
amrex::Real mws_mx0
Definition: ERF_SLM.H:743
amrex::MultiFab evp_soil
Definition: ERF_SLM.H:830
amrex::Vector< std::string > LsmFluxName
Definition: ERF_SLM.H:676
amrex::MultiFab IR_emis_grnd
Definition: ERF_SLM.H:795
std::vector< amrex::Real > m_dz_lsm
Definition: ERF_SLM.H:718
amrex::iMultiFab nroot
Definition: ERF_SLM.H:778
amrex::MultiFab ztop
Definition: ERF_SLM.H:796
amrex::MultiFab albedovis_s
Definition: ERF_SLM.H:835
int klo_lsm
Definition: ERF_SLM.H:701
amrex::MultiFab albedonir_s
Definition: ERF_SLM.H:835
amrex::MultiFab albold_noahmp
Definition: ERF_SLM.H:935
void slm_init()
Definition: ERF_SLM.cpp:832
amrex::MultiFab * Lsm_Data_Ptr(const int &varIdx) override
Definition: ERF_SLM.H:297
amrex::MultiFab Rgl
Definition: ERF_SLM.H:798
amrex::MultiFab IR_emis_vege
Definition: ERF_SLM.H:793
amrex::MultiFab sstxy
Definition: ERF_SLM.H:781
amrex::MultiFab IMPERV
Definition: ERF_SLM.H:806
int m_lsm_data_size
Definition: ERF_SLM.H:665
amrex::Vector< std::string > LsmDataName
Definition: ERF_SLM.H:675
amrex::BoxArray ba_lsm_2d
Definition: ERF_SLM.H:693
amrex::Geometry m_lsm_geom
Definition: ERF_SLM.H:685
amrex::MultiFab BAI
Definition: ERF_SLM.H:805
amrex::Real zref
Definition: ERF_SLM.H:755
amrex::MultiFab lhf_soil
Definition: ERF_SLM.H:827
amrex::MultiFab * z_phys_nd
Definition: ERF_SLM.H:947
amrex::MultiFab phi_1
Definition: ERF_SLM.H:791
amrex::Vector< int > LsmDataMap
Definition: ERF_SLM.H:671
amrex::MultiFab albedonir_v
Definition: ERF_SLM.H:834
amrex::MultiFab dleaf
Definition: ERF_SLM.H:788
amrex::Vector< int > unmapped_fields
Definition: ERF_SLM.H:987
amrex::MultiFab lhf_air
Definition: ERF_SLM.H:828
amrex::MultiFab mws_mx
Definition: ERF_SLM.H:804
amrex::MultiFab disp_hgt
Definition: ERF_SLM.H:797
amrex::MultiFab albedovis_v
Definition: ERF_SLM.H:834
amrex::MultiFab tauss_noahmp
Definition: ERF_SLM.H:936
amrex::MultiFab soilt_vars
Definition: ERF_SLM.H:811
amrex::MultiFab hs_rc
Definition: ERF_SLM.H:800
amrex::MultiFab soilw_vars
Definition: ERF_SLM.H:812
amrex::Real LAI0
Definition: ERF_SLM.H:724
amrex::MultiFab Rc_min
Definition: ERF_SLM.H:799
amrex::Vector< int > LsmFluxMap
Definition: ERF_SLM.H:672
amrex::MultiFab zrefxy
Definition: ERF_SLM.H:756
amrex::MultiFab IR_emis_soil
Definition: ERF_SLM.H:794
@ 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:50
@ 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:272
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.
409 {
410  ParmParse pp("slm");
411  pp.query("landtype0", landtype0);
412  pp.query("LAI0", LAI0);
413 
414  auto validate_value = [](const std::string& name, const amrex::Real value,
415  const std::optional<amrex::Real> lower = std::nullopt,
416  const std::optional<amrex::Real> upper = std::nullopt)
417  {
418  if (!std::isfinite(value) || (lower && value < *lower) || (upper && value > *upper)) {
419  amrex::Abort("SLM: invalid value for '" + name + "'");
420  }
421  };
422 
423  auto const get_layer_prop = [&pp, &validate_value](
424  const std::string& name, const int nz, amrex::Vector<amrex::Real>& prop,
425  const std::optional<amrex::Real> lower = std::nullopt,
426  const std::optional<amrex::Real> upper = std::nullopt)
427  {
428  int nval = pp.countval(name.c_str());
429 
430  if (nval == 1)
431  {
432  // read single val and fill prop array over all soil layers
434  pp.query(name.c_str(), tmp);
435  prop.resize(nz);
436  std::fill(prop.begin(), prop.end(), tmp);
437  } else {
438  pp.queryarr(name.c_str(), prop);
439  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(static_cast<int>(prop.size()) == nz, " Expected " + name + " to have " + std::to_string(nz) + " values!");
440  }
441  for (const amrex::Real value : prop) {
442  validate_value(name, value, lower, upper);
443  }
444  };
445 
446  pp.query("soiltnudging", dosoiltnudging);
447  pp.query("soilwnudging", dosoilwnudging);
448  pp.query("tausoil", tausoil);
449 
450  if (!use_wrfinput) {
451  get_layer_prop("clay0", m_nz_lsm, clay0, 0.0, 100.0);
452  get_layer_prop("sand0", m_nz_lsm, sand0, 0.0, 100.0);
453  get_layer_prop("sw0", m_nz_lsm, sw0, 0.0, 1.0);
454  get_layer_prop("st0", m_nz_lsm, st0);
455  }
457  get_layer_prop("relax_hgt", m_nz_lsm, relax_hgt, 0.0, 1.0);
458  } else {
459  relax_hgt.resize(m_nz_lsm);
460  std::fill(relax_hgt.begin(), relax_hgt.end(), 0.0);
461  }
462 
463  if (!use_wrfinput) {
464  for (int i = 0; i < m_nz_lsm; i++)
465  {
466  amrex::Print() << " " << i << ": soilt = " << st0[i] << " soilw = " << sw0[i] << " clay = " << clay0[i] << " sand = " << sand0[i] << " relax = " << relax_hgt[i] << std::endl;
467  }
468  }
469 
470  pp.query("tabs_s", tabs_s);
471  pp.query("t00", t00);
472 
473  pp.query("z0_soil", z0_soil);
474  pp.query("mws_mx0", mws_mx0);
475  pp.query("Rc_max", Rc_max);
476  pp.query("T_opt", T_opt);
477  pp.query("zref", zref);
478 
479  validate_value("tausoil", tausoil, 0.0);
480  validate_value("z0_soil", z0_soil, 0.0);
481  validate_value("Rc_max", Rc_max, 0.0);
482  validate_value("zref", zref, 0.0);
483  validate_value("LAI0", LAI0, 0.0);
484  validate_value("mws_mx0", mws_mx0, 0.0);
485  validate_value("T_opt", T_opt);
486  validate_value("tabs_s", tabs_s);
487  validate_value("t00", t00);
488 
489  // Read NoahmpTable.TBL
490  pp.query("use_parameter_file", use_param_file);
491  pp.query("interpolate_lai", interpolate_lai);
492  pp.query("parameter_file", parameter_file);
493  pp.query("veg_dataset", veg_dataset);
494  pp.query("soil_dataset", soil_dataset);
495  pp.query("use_param_tbl", use_wrf_lai);
496 
497  if (use_param_file) {
498  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(use_wrf_lai != use_param_file, "Cannot use parameter file and parameter table, must choose one method");
499  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(parameter_file != "", "Using parameter file, but file path is empty!");
500  }
501 
502  // Read NoahMP table if provided
503  if (use_param_file && parameter_file != "") {
506 
507  std::string soil_param_key, veg_param_key;
508  const std::string soil_dataset_lower = amrex::toLower(soil_dataset);
509  const std::string veg_dataset_lower = amrex::toLower(veg_dataset);
510  if (soil_dataset_lower == "stas_ruc") {
511  soil_param_key = "noahmp_soil_stas_ruc_parameters";
512  } else if (soil_dataset_lower == "stas") {
513  soil_param_key = "noahmp_soil_stas_parameters";
514  } else {
515  amrex::Abort("Unrecognized soil dataset type, expected 'stas_ruc' or 'stas'");
516  }
517 
518  if (veg_dataset_lower == "usgs") {
519  veg_param_key = "noahmp_usgs_parameters";
520  } else if (veg_dataset_lower == "modis") {
521  veg_param_key = "noahmp_modis_parameters";
522  } else {
523  amrex::Abort("Unrecognized vegetation dataset type, expected 'usgs' or 'modis'");
524  }
525 
526  // Copy soil and vegetation datasets to GPU
527  auto &h_soil_params = full_params.at(soil_param_key);
528  auto &h_veg_params = full_params.at(veg_param_key);
529 
530  for (int i = 0; i < static_cast<int>(h_soil_params.size()); ++i) {
531  const int varsize = static_cast<int>(h_soil_params[i].second.size());
532  if (varsize > 1) {
533  //amrex::Print() << " -- copying soil param '" << h_soil_params[i].first << "' to GPU " << std::endl;
534  auto d_var = std::make_unique<amrex::Gpu::DeviceVector<amrex::Real>>(varsize);
535  auto *d_var_ptr = d_var.get();
536  d_soil_params.insert(std::make_pair(h_soil_params[i].first, std::move(d_var)));
537  Gpu::copyAsync(Gpu::hostToDevice, h_soil_params[i].second.data(), h_soil_params[i].second.data()+varsize, d_var_ptr->data());
538  }
539  }
540 
541  for (int i = 0; i < static_cast<int>(h_veg_params.size()); ++i) {
542  const int varsize = static_cast<int>(h_veg_params[i].second.size());
543  if (h_veg_params[i].first == "nroot") {
544  for (const amrex::Real value : h_veg_params[i].second) {
546  value >= zero && value <= m_nz_lsm && value == std::floor(value),
547  "SLM: NoahMP NROOT must be an integer in [0, slm.nsoil]");
548  }
549  }
550  if (varsize > 1) {
551  //amrex::Print() << " -- copying veg param '" << h_veg_params[i].first << "' to GPU " << std::endl;
552  auto d_var = std::make_unique<amrex::Gpu::DeviceVector<amrex::Real>>(varsize);
553  auto *d_var_ptr = d_var.get();
554  d_veg_params.insert(std::make_pair(h_veg_params[i].first, std::move(d_var)));
555  Gpu::copyAsync(Gpu::hostToDevice, h_veg_params[i].second.data(), h_veg_params[i].second.data()+varsize, d_var_ptr->data());
556  }
557  }
558 
559  Gpu::streamSynchronize();
560 
561  // Read radiation parameters from NoahmpTable.TBL
562  // Read noahmp_rad_parameters block
563  if (full_params.count("noahmp_rad_parameters") > 0) {
564  auto &h_rad_params = full_params.at("noahmp_rad_parameters");
565  for (int i = 0; i < static_cast<int>(h_rad_params.size()); ++i) {
566  std::string pname = h_rad_params[i].first;
567  const int varsize = static_cast<int>(h_rad_params[i].second.size());
568 
569  if (varsize > 1 &&
570  (pname == "albsat_vis" || pname == "albsat_nir" ||
571  pname == "albdry_vis" || pname == "albdry_nir" ||
572  pname == "alblak" || pname == "omegas")) {
573  auto d_var = std::make_unique<amrex::Gpu::DeviceVector<amrex::Real>>(varsize);
574  auto *d_var_ptr = d_var.get();
575  d_rad_params.insert(std::make_pair(pname, std::move(d_var)));
576  Gpu::copyAsync(Gpu::hostToDevice, h_rad_params[i].second.data(),
577  h_rad_params[i].second.data()+varsize, d_var_ptr->data());
578  } else if (pname == "betads" && varsize == 1) {
579  betads_rad = h_rad_params[i].second[0];
580  } else if (pname == "betais" && varsize == 1) {
581  betais_rad = h_rad_params[i].second[0];
582  }
583  }
584  }
585 
586  // Read noahmp_global_parameters block for snow albedo parameters
587  if (full_params.count("noahmp_global_parameters") > 0) {
588  auto &h_global_params = full_params.at("noahmp_global_parameters");
589  for (int i = 0; i < static_cast<int>(h_global_params.size()); ++i) {
590  std::string pname = h_global_params[i].first;
591  const int varsize = static_cast<int>(h_global_params[i].second.size());
592 
593  if (varsize == 1) {
594  if (pname == "tau0") tau0_rad = h_global_params[i].second[0];
595  else if (pname == "grain_growth") grain_growth_rad = h_global_params[i].second[0];
596  else if (pname == "extra_growth") extra_growth_rad = h_global_params[i].second[0];
597  else if (pname == "dirt_soot") dirt_soot_rad = h_global_params[i].second[0];
598  else if (pname == "bats_cosz") bats_cosz_rad = h_global_params[i].second[0];
599  else if (pname == "bats_vis_new") bats_vis_new_rad = h_global_params[i].second[0];
600  else if (pname == "bats_nir_new") bats_nir_new_rad = h_global_params[i].second[0];
601  else if (pname == "bats_vis_age") bats_vis_age_rad = h_global_params[i].second[0];
602  else if (pname == "bats_nir_age") bats_nir_age_rad = h_global_params[i].second[0];
603  else if (pname == "bats_vis_dir") bats_vis_dir_rad = h_global_params[i].second[0];
604  else if (pname == "bats_nir_dir") bats_nir_dir_rad = h_global_params[i].second[0];
605  else if (pname == "swemx") swemx_rad = h_global_params[i].second[0];
606  else if (pname == "snow_emis") snow_emis_rad = h_global_params[i].second[0];
607  else if (pname == "rsurf_exp") rsurf_exp = h_global_params[i].second[0];
608  } else if (varsize == 2) {
609  if (pname == "eg") {
610  eg_soil_rad = h_global_params[i].second[0]; // IST=1 (soil)
611  eg_lake_rad = h_global_params[i].second[1]; // IST=2 (lake)
612  }
613  }
614  }
615  }
616 
617  Gpu::streamSynchronize();
618 
619  amrex::Print() << " SLM: NOAHMP radiation parameters loaded from " << parameter_file << std::endl;
620  } else {
622  }
623 
624  if (interpolate_lai) {
625  pp.gettable("lai", lai_table);
626  pp.gettable("sai", sai_table);
627 
628  // validate LAI and SAI tables
629  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(static_cast<int>(lai_table.size()) == 12, "Invalid LAI table size, expected values for all 12 months");
630  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(static_cast<int>(sai_table.size()) == 12, "Invalid SAI table size, expected values for all 12 months");
631  if (lai_table[0].empty()) {
632  amrex::Abort("SLM: LAI table must contain at least one landtype column");
633  }
634  num_landtypes = static_cast<int>(lai_table[0].size()); // use first entry as size, check all others against
635  for (int i = 0; i < static_cast<int>(lai_table.size()); ++i) {
636  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(static_cast<int>(lai_table[i].size()) == num_landtypes, "Invalid LAI table - inconsistent number of landtype entries between months");
637  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(static_cast<int>(sai_table[i].size()) == num_landtypes, "Invalid SAI table - inconsistent number of landtype entries between months");
638  }
639 
640  d_lai_curr.resize(num_landtypes);
641  d_lai_next.resize(num_landtypes);
642  d_sai_curr.resize(num_landtypes);
643  d_sai_next.resize(num_landtypes);
644  }
645 
646  if (use_wrf_lai) {
647  pp.gettable("vegparam", param_table);
648  if (static_cast<int>(param_table.size()) < 16) {
649  amrex::Abort("SLM: vegparam must contain at least 16 landtype rows");
650  }
651  if (static_cast<int>(param_table[0].size()) < 10) {
652  amrex::Abort("SLM: vegparam must contain at least 10 columns");
653  }
654  amrex::Print() << " param table = " << std::endl;
655 
656  const int nparam = static_cast<int>(param_table[0].size());
657  for (int t = 0; t < static_cast<int>(param_table.size()); ++t) {
658  amrex::Print() << " LANDTYPE " << t << ": ";
659  for (int i = 0; i < static_cast<int>(param_table[t].size()); ++i) {
660  amrex::Print() << param_table[t][i] << " ";
661  }
662  amrex::Print() << std::endl;
663 
664  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(static_cast<int>(param_table[t].size()) == nparam, "Invalid param table, inconsistent number of parameters for landtype");
665  if (param_table[t][0] != t + 1) {
666  amrex::Abort("SLM: vegparam landtype IDs must be sequential");
667  }
668  }
669 
670  // Copy parameter table to GPU
671  d_param_table = TableData<Real, 2>({0, 0}, {static_cast<int>(param_table.size()), nparam});
672 #ifdef AMREX_USE_GPU
673  TableData<Real, 2> h_param_table({0, 0}, {static_cast<int>(param_table.size()), nparam}, amrex::The_Pinned_Arena());
674  auto const &h_tab = h_param_table.table();
675 #else
676  auto const &h_tab = d_param_table.table();
677 #endif
678  for (int t = 0; t < static_cast<int>(param_table.size()); ++t) {
679  for (int i = 0; i < static_cast<int>(param_table[t].size()); ++i) {
680  h_tab(t, i) = param_table[t][i];
681  }
682  }
683 #ifdef AMREX_USE_GPU
684  d_param_table.copy(h_param_table);
685  Gpu::streamSynchronize();
686 #endif
687  }
688 
689  pp.query("rad_input_file", rad_input_file);
690  if (rad_input_file != "") {
691 #ifndef ERF_USE_NETCDF
692  amrex::Abort("ERF needs to be compiled with NetCDF to use the SLM rad_input_file option!");
693 #endif
694 #ifdef ERF_USE_NETCDF
695  ncutils::NCFile rad_forcing_ncf = ncutils::NCFile::open(rad_input_file, NC_NOWRITE);
696 
697  ncutils::NCVar rad_time = rad_forcing_ncf.var("time");
698  num_rad_times = rad_time.shape()[0];
699  rad_times.resize(num_rad_times);
700  rad_time.get(rad_times.dataPtr(), {0}, {static_cast<unsigned long>(num_rad_times)});
701 
702  amrex::Real start_rad_time = rad_times[0];
703  for (int i = 0; i < num_rad_times; i++)
704  {
705  rad_times[i] = (rad_times[i] - start_rad_time) * 86400.0; // shift relative to first time
706  }
707 
708  ncutils::NCDim rad_x = rad_forcing_ncf.dim("x");
709  ncutils::NCDim rad_y = rad_forcing_ncf.dim("y");
710 
711  amrex::Print() << " Read radiation forcing file '" << rad_input_file << "'" << std::endl;
712  amrex::Print() << " rad forcing file has " << num_rad_times << " time values, x = " << rad_x.len() << " y = " << rad_y.len() << std::endl;
713  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!");
714 
715  BoxList bl_rad = ba_lsm_2d.boxList();
716  for (auto& b : bl_rad) {
717  b.setSmall(2, 0);
718  b.setBig(2, num_rad_times - 1);
719  }
720  BoxArray ba_rad = BoxArray(std::move(bl_rad));
721  IntVect ng_2d(0, 0, 0);
722 
723  // rad_input has 6 components: SWVIS, SWNIR, SWVISD, SWNIRD, COSZRS, LWDS
724  rad_input_data.define(ba_rad, landmask.distributionMap, 6, ng_2d);
725  rad_input_data.setVal(zero);
726 
727  amrex::Arena* Arena_Used = amrex::The_Arena();
728 #ifdef AMREX_USE_GPU
729  Arena_Used = amrex::The_Pinned_Arena();
730 #endif
731 
732  for(int comp = 0; comp < rad_input_data.nComp(); ++comp) {
733 
734  ncutils::NCVar rad_var = rad_forcing_ncf.var(rad_names[comp]);
735  amrex::Print() << " Reading radiation input var " << rad_var.name() << std::endl;
736 
737  for ( MFIter mfi(rad_input_data); mfi.isValid(); ++mfi) {
738  const auto& box = mfi.fabbox();
739 
740  FArrayBox tmp(box, 1, Arena_Used);
741  auto *dataPtr = tmp.dataPtr();
742  AMREX_ALWAYS_ASSERT(dataPtr != nullptr);
743 
744  std::vector<size_t> starts = {0,
745  static_cast<unsigned long>(box.loVect()[1]),
746  static_cast<unsigned long>(box.loVect()[0])};
747  std::vector<size_t> counts = {static_cast<unsigned long>(num_rad_times),
748  static_cast<unsigned long>(box.length()[1]),
749  static_cast<unsigned long>(box.length()[0])};
750 
751  amrex::Print() << " -- reading {" << counts[0] << "," << counts[1] << "," << counts[2] << "} at index {" << starts[0] << "," << starts[1] << "," << starts[2] << "}" << std::endl;
752  rad_var.get(dataPtr, starts, counts);
753 
754  auto mf_arr = rad_input_data.array(mfi, comp);
755  auto tmp_arr = tmp.array();
756  ParallelFor(box, [=] AMREX_GPU_DEVICE (int i, int j, int k)
757  {
758  mf_arr(i, j, k) = tmp_arr(i, j, k);
759  });
760  }
761  }
762 
763  /*
764  for ( MFIter mfi(rad_input_data); mfi.isValid(); ++mfi)
765  {
766  const auto& box = mfi.fabbox();
767  auto rad_arr = rad_input_data.const_array(mfi);
768  ParallelFor(box, rad_names.size(), [=] AMREX_GPU_DEVICE (int i, int j, int k, int n)
769  {
770  amrex::Print() << " i = " << i << " j = " << j << " k = " << k << " n = " << n << " RADVAR = " << rad_arr(i, j, k, n) << std::endl;
771  });
772  }
773  */
774 #endif
775  }
776 
777  shf_soil.setVal(zero);
778  lhf_soil.setVal(zero);
779 
780  r_a.setVal(1.0e9);
781  r_b.setVal(1.0e4);
782  r_c.setVal(1.0e4);
783  r_d.setVal(1.0e4);
784 
785  mw.setVal(zero);
786  mws.setVal(zero);
787 
788  mw_inc.setVal(zero);
789  evapo_dry.setVal(zero);
790 }
Arena * Arena_Used
Definition: ERF_Morrison_Advance_F.H:23
std::string name
Definition: ERF_Plotfile2DCatalog.cpp:105
amrex::Vector< amrex::Real > sand0
Definition: ERF_SLM.H:726
amrex::Real snow_emis_rad
Definition: ERF_SLM.H:914
amrex::Real bats_cosz_rad
Definition: ERF_SLM.H:904
amrex::Gpu::DeviceVector< amrex::Real > d_sai_next
Definition: ERF_SLM.H:877
void load_builtin_radiation_parameters()
Definition: ERF_SLM.cpp:795
amrex::Real bats_vis_dir_rad
Definition: ERF_SLM.H:909
std::unordered_map< std::string, std::unique_ptr< amrex::Gpu::DeviceVector< amrex::Real > > > d_veg_params
Definition: ERF_SLM.H:868
amrex::Vector< std::string > param_soil_categories
Definition: ERF_SLM.H:865
amrex::Real tabs_s
Definition: ERF_SLM.H:731
bool use_param_file
Definition: ERF_SLM.H:879
amrex::Real tau0_rad
Definition: ERF_SLM.H:899
amrex::Vector< amrex::Real > sw0
Definition: ERF_SLM.H:727
amrex::Real bats_vis_age_rad
Definition: ERF_SLM.H:907
amrex::Real T_opt
Definition: ERF_SLM.H:754
bool dosoiltnudging
Definition: ERF_SLM.H:738
amrex::Real bats_vis_new_rad
Definition: ERF_SLM.H:905
amrex::Real tausoil
Definition: ERF_SLM.H:739
bool use_wrf_lai
Definition: ERF_SLM.H:881
amrex::Real betads_rad
Definition: ERF_SLM.H:895
SLMParameterTable ReadParameterFile(const std::string &filename, amrex::Vector< std::string > &veg_categories, amrex::Vector< std::string > &soil_categories)
Definition: ERF_SLM.cpp:2216
amrex::Gpu::DeviceVector< amrex::Real > d_lai_next
Definition: ERF_SLM.H:875
std::string veg_dataset
Definition: ERF_SLM.H:857
void validate_parameter_tables()
Definition: ERF_SLM.cpp:2317
amrex::Real dirt_soot_rad
Definition: ERF_SLM.H:902
amrex::Real swemx_rad
Definition: ERF_SLM.H:911
SLMParameterTable full_params
Definition: ERF_SLM.H:863
amrex::Real bats_nir_new_rad
Definition: ERF_SLM.H:906
amrex::Vector< std::string > param_veg_categories
Definition: ERF_SLM.H:864
amrex::Vector< amrex::Real > clay0
Definition: ERF_SLM.H:725
amrex::Real bats_nir_dir_rad
Definition: ERF_SLM.H:910
amrex::Real Rc_max
Definition: ERF_SLM.H:753
std::string parameter_file
Definition: ERF_SLM.H:856
bool dosoilwnudging
Definition: ERF_SLM.H:737
bool interpolate_lai
Definition: ERF_SLM.H:880
std::vector< std::vector< amrex::Real > > sai_table
Definition: ERF_SLM.H:871
amrex::Vector< amrex::Real > st0
Definition: ERF_SLM.H:728
amrex::TableData< amrex::Real, 2 > d_param_table
Definition: ERF_SLM.H:939
amrex::Real extra_growth_rad
Definition: ERF_SLM.H:901
amrex::Gpu::DeviceVector< amrex::Real > d_sai_curr
Definition: ERF_SLM.H:876
amrex::Real grain_growth_rad
Definition: ERF_SLM.H:900
amrex::Gpu::DeviceVector< amrex::Real > d_lai_curr
Definition: ERF_SLM.H:874
int num_landtypes
Definition: ERF_SLM.H:859
std::vector< std::vector< amrex::Real > > lai_table
Definition: ERF_SLM.H:872
amrex::Real eg_lake_rad
Definition: ERF_SLM.H:916
amrex::Real bats_nir_age_rad
Definition: ERF_SLM.H:908
amrex::Real betais_rad
Definition: ERF_SLM.H:896
amrex::Vector< amrex::Real > relax_hgt
Definition: ERF_SLM.H:729
std::unordered_map< std::string, std::unique_ptr< amrex::Gpu::DeviceVector< amrex::Real > > > d_rad_params
Definition: ERF_SLM.H:869
amrex::Real eg_soil_rad
Definition: ERF_SLM.H:915
amrex::Real z0_soil
Definition: ERF_SLM.H:741
std::vector< std::vector< amrex::Real > > param_table
Definition: ERF_SLM.H:938
amrex::Real rsurf_exp
Definition: ERF_SLM.H:903
amrex::Real t00
Definition: ERF_SLM.H:732
std::string soil_dataset
Definition: ERF_SLM.H:858
std::unordered_map< std::string, std::unique_ptr< amrex::Gpu::DeviceVector< amrex::Real > > > d_soil_params
Definition: ERF_SLM.H:867
Definition: ERF_NCInterface.H:344
@ 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:41
Definition: ERF_NCInterface.H:326
Definition: ERF_NCInterface.H:332
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

2531 {
2532  // do nothing if not using the parameter file
2533  if (!use_param_file) {
2534  return;
2535  }
2536 
2537  const int d_khi_lsm = khi_lsm;
2538  const int d_klo_lsm = klo_lsm;
2539 
2540  // Get pointers to GPU soil parameter values
2541  const amrex::Real *d_param_poro = d_soil_params.at("maxsmc")->data();
2542  const amrex::Real *d_param_theta_FC = d_soil_params.at("refsmc")->data();
2543  const amrex::Real *d_param_theta_WP = d_soil_params.at("wltsmc")->data();
2544  const amrex::Real *d_param_m_pot_sat = d_soil_params.at("satpsi")->data();
2545  const amrex::Real *d_param_Bconst = d_soil_params.at("bb")->data();
2546  const amrex::Real *d_param_ks = d_soil_params.at("satdk")->data();
2547 
2548  // Get pointers to GPU vegetation parameter values from NoahMP table
2549  const amrex::Real *d_param_rs = nullptr;
2550  const amrex::Real *d_param_rgl = nullptr;
2551  const amrex::Real *d_param_xl = nullptr;
2552  const amrex::Real *d_param_hs = nullptr;
2553  const amrex::Real *d_param_nroot = nullptr;
2554  const amrex::Real *d_param_hvt = nullptr;
2555  const amrex::Real *d_param_z0mvt = nullptr;
2556  const amrex::Real *d_param_cbiom = nullptr;
2557  const amrex::Real *d_param_dleaf = nullptr;
2558 
2559  // Check if vegetation parameters are available
2560  bool has_rs = false, has_rgl = false, has_xl = false, has_hs = false;
2561  bool has_nroot = false, has_hvt = false, has_z0mvt = false;
2562  bool has_cbiom = false, has_dleaf = false;
2563  if (d_veg_params.find("rs") != d_veg_params.end()) {
2564  d_param_rs = d_veg_params.at("rs")->data();
2565  has_rs = true;
2566  amrex::Print() << " SLM: Using RS (minimum stomatal resistance) from NoahMP parameter file" << std::endl;
2567  }
2568  if (d_veg_params.find("rgl") != d_veg_params.end()) {
2569  d_param_rgl = d_veg_params.at("rgl")->data();
2570  has_rgl = true;
2571  amrex::Print() << " SLM: Using RGL (radiation stress parameter) from NoahMP parameter file" << std::endl;
2572  }
2573  if (d_veg_params.find("xl") != d_veg_params.end()) {
2574  d_param_xl = d_veg_params.at("xl")->data();
2575  has_xl = true;
2576  amrex::Print() << " SLM: Using XL (leaf/stem orientation index) from NoahMP parameter file" << std::endl;
2577  }
2578  if (d_veg_params.find("hs") != d_veg_params.end()) {
2579  d_param_hs = d_veg_params.at("hs")->data();
2580  has_hs = true;
2581  amrex::Print() << " SLM: Using HS (VPD sensitivity parameter) from NoahMP parameter file" << std::endl;
2582  amrex::Print() << " SLM: Note - VPD formula changed to match NoahMP: 1/(1+HS*VPD)" << std::endl;
2583  }
2584  if (d_veg_params.find("nroot") != d_veg_params.end()) {
2585  d_param_nroot = d_veg_params.at("nroot")->data();
2586  has_nroot = true;
2587  amrex::Print() << " SLM: Using NROOT from NoahMP parameter file for the BTR=1 soil-water stress factor" << std::endl;
2588  }
2589  if (d_veg_params.find("hvt") != d_veg_params.end()) {
2590  d_param_hvt = d_veg_params.at("hvt")->data();
2591  has_hvt = true;
2592  amrex::Print() << " SLM: Using HVT (canopy top height) from NoahMP parameter file" << std::endl;
2593  }
2594  if (d_veg_params.find("z0mvt") != d_veg_params.end()) {
2595  d_param_z0mvt = d_veg_params.at("z0mvt")->data();
2596  has_z0mvt = true;
2597  amrex::Print() << " SLM: Using Z0MVT (vegetation momentum roughness) from NoahMP parameter file" << std::endl;
2598  }
2599  if (d_veg_params.find("cbiom") != d_veg_params.end()) {
2600  d_param_cbiom = d_veg_params.at("cbiom")->data();
2601  has_cbiom = true;
2602  amrex::Print() << " SLM: Using CBIOM (canopy biomass heat capacity parameter) from NoahMP parameter file" << std::endl;
2603  }
2604  if (d_veg_params.find("dleaf") != d_veg_params.end()) {
2605  d_param_dleaf = d_veg_params.at("dleaf")->data();
2606  has_dleaf = true;
2607  amrex::Print() << " SLM: Using DLEAF (characteristic leaf dimension) from NoahMP parameter file" << std::endl;
2608  }
2609 
2610  for ( amrex::MFIter mfi(landtype, TileNoZ()); mfi.isValid(); ++mfi) {
2611  amrex::Box bx2d = mfi.tilebox();
2612 
2613  auto landmask_arr = landmask.const_array(mfi);
2614  auto landtype_arr = landtype.const_array(mfi);
2615  auto vegetype_arr = vegetype.const_array(mfi);
2616  auto soiltype_arr = lsm_fab_vars[LsmVar_SLM::soiltype]->const_array(mfi);
2617 
2618  auto poro_soil_arr = lsm_fab_vars[LsmVar_SLM::poro_soil]->array(mfi);
2619  auto theta_FC_arr = lsm_fab_vars[LsmVar_SLM::theta_FC]->array(mfi);
2620  auto theta_WP_arr = lsm_fab_vars[LsmVar_SLM::theta_WP]->array(mfi);
2621  auto m_pot_sat_arr = lsm_fab_vars[LsmVar_SLM::m_pot_sat]->array(mfi);
2622  auto Bconst_arr = lsm_fab_vars[LsmVar_SLM::Bconst]->array(mfi);
2623  auto ks_arr = lsm_fab_vars[LsmVar_SLM::ks]->array(mfi);
2624 
2625  // Get vegetation parameter arrays
2626  auto Rc_min_arr = Rc_min.array(mfi);
2627  auto Rgl_arr = Rgl.array(mfi);
2628  auto Khai_L_arr = Khai_L.array(mfi);
2629  auto hs_rc_arr = hs_rc.array(mfi);
2630  auto nroot_arr = nroot.array(mfi);
2631  auto ztop_arr = ztop.array(mfi);
2632  auto disp_hgt_arr = disp_hgt.array(mfi);
2633  auto z0_sfc_arr = z0_sfc.array(mfi);
2634  auto cbiom_arr = cbiom.array(mfi);
2635  auto dleaf_arr = dleaf.array(mfi);
2636 
2637  amrex::ParallelFor(bx2d, [=] AMREX_GPU_DEVICE(int i, int j, int) noexcept {
2638  if (landmask_arr(i, j, 0) == 1) {
2639 
2640  const int ltype = landtype_arr(i,j,0) - 1; // shift by one to match table index (i.e, types 1-20 -> index 0-19)
2641  const int stype = static_cast<int>(soiltype_arr(i,j,d_khi_lsm)) - 1;
2642 
2643  // Set soil parameters from parameter file
2644  for (int k = d_khi_lsm; k >= d_klo_lsm; k--) {
2645  poro_soil_arr(i, j, k) = d_param_poro[stype];
2646  theta_FC_arr(i, j, k) = d_param_theta_FC[stype];
2647  theta_WP_arr(i, j, k) = d_param_theta_WP[stype];
2648  // NoahMP stores SATPSI as a positive magnitude in meters;
2649  // SLM uses a negative matric potential in millimeters.
2650  m_pot_sat_arr(i, j, k) = -1000.0 * d_param_m_pot_sat[stype];
2651  Bconst_arr(i, j, k) = d_param_Bconst[stype];
2652  // NoahMP SATDK is m/s; SLM soil hydraulics use mm/s.
2653  ks_arr(i, j, k) = 1000.0 * d_param_ks[stype];
2654  }
2655 
2656  // Set vegetation parameters from NoahMP table if available
2657  if (vegetype_arr(i,j,0) == 1) {
2658  if (has_rs && d_param_rs != nullptr) {
2659  // RS = minimum stomatal resistance [s/m]
2660  Rc_min_arr(i, j, 0) = d_param_rs[ltype];
2661  }
2662  if (has_rgl && d_param_rgl != nullptr) {
2663  // RGL = radiation stress parameter [W/m2]
2664  Rgl_arr(i, j, 0) = d_param_rgl[ltype];
2665  }
2666  if (has_xl && d_param_xl != nullptr) {
2667  // XL = leaf/stem orientation index (dimensionless)
2668  // Directly maps to Khai_L in SLM
2669  Khai_L_arr(i, j, 0) = d_param_xl[ltype];
2670  }
2671  if (has_hs && d_param_hs != nullptr) {
2672  // HS = VPD sensitivity parameter (dimensionless)
2673  // NOTE: This only works with the hyperbolic VPD formula: 1/(1+HS*VPD)
2674  // If using exponential formula exp(-hs*VPD), HS values are NOT compatible
2675  hs_rc_arr(i, j, 0) = d_param_hs[ltype];
2676  }
2677  if (has_nroot && d_param_nroot != nullptr) {
2678  nroot_arr(i, j, 0) = static_cast<int>(d_param_nroot[ltype]);
2679  }
2680  if (has_hvt && d_param_hvt != nullptr) {
2681  ztop_arr(i, j, 0) = d_param_hvt[ltype];
2682  disp_hgt_arr(i, j, 0) = 0.65 * ztop_arr(i, j, 0);
2683  }
2684  if (has_z0mvt && d_param_z0mvt != nullptr) {
2685  z0_sfc_arr(i, j, 0) = d_param_z0mvt[ltype];
2686  }
2687  if (has_cbiom && d_param_cbiom != nullptr) {
2688  cbiom_arr(i, j, 0) = d_param_cbiom[ltype];
2689  }
2690  if (has_dleaf && d_param_dleaf != nullptr) {
2691  dleaf_arr(i, j, 0) = d_param_dleaf[ltype];
2692  }
2693  }
2694  }
2695  });
2696  }
2697 
2698  // set a flag to indicate remaining soil variables should be recomputed
2699  params_updated = true;
2700 
2701  // recompute soil variables using updated parameters
2702  init_soil_vars();
2703 
2704  // recompute derived vegetation fields using the updated parameters.
2705  for (amrex::MFIter mfi(landtype, TileNoZ()); mfi.isValid(); ++mfi) {
2706  UpdateLAIParameters(mfi);
2707  }
2708 }
void init_soil_vars()
Definition: ERF_SLM.cpp:1877
bool params_updated
Definition: ERF_SLM.H:882
void UpdateLAIParameters(const amrex::MFIter &mfi)
Definition: ERF_SLM.cpp:2857
@ 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

1340 {
1341  const Real d_z0_soil = z0_soil;
1342 
1343  for ( MFIter mfi(landtype, TileNoZ()); mfi.isValid(); ++mfi) {
1344  const auto& box = mfi.tilebox();
1345 
1346  auto landtype_arr = landtype.const_array(mfi);
1347  auto landmask_arr = landmask.array(mfi);
1348 #ifndef AMREX_USE_GPU
1349  auto LAI_arr = LAI.const_array(mfi);
1350 #endif
1351  auto albedovis_v_arr = albedovis_v.array(mfi);
1352  auto albedonir_v_arr = albedonir_v.array(mfi);
1353  auto albedovis_s_arr = albedovis_s.array(mfi);
1354  auto albedonir_s_arr = albedonir_s.array(mfi);
1355  auto ztop_arr = ztop.array(mfi);
1356  auto disp_hgt_arr = disp_hgt.array(mfi);
1357  auto z0_sfc_arr = z0_sfc.array(mfi);
1358  auto Khai_L_arr = Khai_L.array(mfi);
1359  auto Rc_min_arr = Rc_min.array(mfi);
1360  auto Rgl_arr = Rgl.array(mfi);
1361  auto hs_rc_arr = hs_rc.array(mfi);
1362  auto BAI_arr = BAI.array(mfi);
1363  auto IMPERV_arr = IMPERV.array(mfi);
1364  auto vegetype_arr = vegetype.array(mfi);
1365 
1366  ParallelFor(box, [=] AMREX_GPU_DEVICE (int i, int j, int)
1367  {
1368  switch (landtype_arr(i, j, 0))
1369  {
1370  case 0: // water
1371  // TODO: should this landmask be ERF's lmask_lev variable?
1372  landmask_arr(i, j, 0) = 0;
1373  vegetype_arr(i, j, 0) = 0;
1374  // set albedo for water so that it is populated for RRTMGP
1375  albedovis_v_arr(i, j, 0) = 0.06;
1376  albedonir_v_arr(i, j, 0) = 0.06;
1377  albedovis_s_arr(i, j, 0) = 0.06;
1378  albedonir_s_arr(i, j, 0) = 0.06;
1379  break;
1380  case 1: // evergreen needleleaf forest
1381  albedovis_v_arr(i, j, 0) = 0.094;
1382  albedonir_v_arr(i, j, 0) = 0.161;
1383  albedovis_s_arr(i, j, 0) = 0.208;
1384  albedonir_s_arr(i, j, 0) = 0.344;
1385  ztop_arr(i, j, 0) = 20.;
1386  disp_hgt_arr(i, j, 0) = 0.65 * ztop_arr(i, j, 0);
1387  z0_sfc_arr(i, j, 0) = 1.09;
1388  Khai_L_arr(i, j, 0) = fourth;
1389  Rc_min_arr(i, j, 0) = 250.;
1390  Rgl_arr(i, j, 0) = 120.;
1391  hs_rc_arr(i, j, 0) = 0.03;
1392  BAI_arr(i, j, 0) = 200.;
1393  IMPERV_arr(i, j, 0) = zero;
1394  vegetype_arr(i, j, 0) = 1;
1395  break;
1396  case 2: // evergreen broadleaf forest
1397  albedovis_v_arr(i, j, 0) = 0.086;
1398  albedonir_v_arr(i, j, 0) = 0.146;
1399  albedovis_s_arr(i, j, 0) = 0.208;
1400  albedonir_s_arr(i, j, 0) = 0.344;
1401  ztop_arr(i, j, 0) = 20.;
1402  disp_hgt_arr(i, j, 0) = 0.65 * ztop_arr(i, j, 0);
1403  z0_sfc_arr(i, j, 0) = one;
1404  Khai_L_arr(i, j, 0) = fourth;
1405  Rc_min_arr(i, j, 0) = 250.;
1406  Rgl_arr(i, j, 0) = 120.;
1407  hs_rc_arr(i, j, 0) = 0.03;
1408  BAI_arr(i, j, 0) = 200.;
1409  IMPERV_arr(i, j, 0) = zero;
1410  vegetype_arr(i, j, 0) = 1;
1411  break;
1412  case 3: // deciduous needleaf forest
1413  albedovis_v_arr(i, j, 0) = 0.102;
1414  albedonir_v_arr(i, j, 0) = 0.198;
1415  albedovis_s_arr(i, j, 0) = 0.208;
1416  albedonir_s_arr(i, j, 0) = 0.344;
1417  ztop_arr(i, j, 0) = 20.;
1418  disp_hgt_arr(i, j, 0) = 0.65 * ztop_arr(i, j, 0);
1419  z0_sfc_arr(i, j, 0) = 1.10;
1420  Khai_L_arr(i, j, 0) = fourth;
1421  Rc_min_arr(i, j, 0) = 250.;
1422  Rgl_arr(i, j, 0) = 120.;
1423  hs_rc_arr(i, j, 0) = 0.03;
1424  BAI_arr(i, j, 0) = 200.;
1425  IMPERV_arr(i, j, 0) = zero;
1426  vegetype_arr(i, j, 0) = 1;
1427  break;
1428  case 4: // deciduous broadleaf forest
1429  albedovis_v_arr(i, j, 0) = 0.056;
1430  albedonir_v_arr(i, j, 0) = 0.151;
1431  albedovis_s_arr(i, j, 0) = 0.208;
1432  albedonir_s_arr(i, j, 0) = 0.344;
1433  ztop_arr(i, j, 0) = 20.;
1434  disp_hgt_arr(i, j, 0) = 0.65 * ztop_arr(i, j, 0);
1435  z0_sfc_arr(i, j, 0) = 0.8;
1436  Khai_L_arr(i, j, 0) = fourth;
1437  Rc_min_arr(i, j, 0) = 250.;
1438  Rgl_arr(i, j, 0) = 120.;
1439  hs_rc_arr(i, j, 0) = 0.03;
1440  BAI_arr(i, j, 0) = 200.;
1441  IMPERV_arr(i, j, 0) = zero;
1442  vegetype_arr(i, j, 0) = 1;
1443  break;
1444  case 5: // mixed forest
1445  albedovis_v_arr(i, j, 0) = 0.093;
1446  albedonir_v_arr(i, j, 0) = 0.179;
1447  albedovis_s_arr(i, j, 0) = 0.208;
1448  albedonir_s_arr(i, j, 0) = 0.344;
1449  ztop_arr(i, j, 0) = 20.;
1450  disp_hgt_arr(i, j, 0) = 0.65 * ztop_arr(i, j, 0);
1451  z0_sfc_arr(i, j, 0) = 0.8;
1452  Khai_L_arr(i, j, 0) = fourth;
1453  Rc_min_arr(i, j, 0) = 250.;
1454  Rgl_arr(i, j, 0) = 120.;
1455  hs_rc_arr(i, j, 0) = 0.03;
1456  BAI_arr(i, j, 0) = 200.;
1457  IMPERV_arr(i, j, 0) = zero;
1458  vegetype_arr(i, j, 0) = 1;
1459  break;
1460  case 6: // closed shrublands
1461  albedovis_v_arr(i, j, 0) = 0.069;
1462  albedonir_v_arr(i, j, 0) = 0.121;
1463  albedovis_s_arr(i, j, 0) = 0.208;
1464  albedonir_s_arr(i, j, 0) = 0.344;
1465  ztop_arr(i, j, 0) = one;
1466  disp_hgt_arr(i, j, 0) = 0.65 * ztop_arr(i, j, 0);
1467  z0_sfc_arr(i, j, 0) = 0.1;
1468  Khai_L_arr(i, j, 0) = fourth;
1469  Rc_min_arr(i, j, 0) = 220.;
1470  Rgl_arr(i, j, 0) = 100.;
1471  hs_rc_arr(i, j, 0) = 0.01;
1472  BAI_arr(i, j, 0) = 60.;
1473  IMPERV_arr(i, j, 0) = zero;
1474  vegetype_arr(i, j, 0) = 1;
1475  break;
1476  case 7: // open shrublands
1477  albedovis_v_arr(i, j, 0) = 0.079;
1478  albedonir_v_arr(i, j, 0) = 0.226;
1479  albedovis_s_arr(i, j, 0) = 0.208;
1480  albedonir_s_arr(i, j, 0) = 0.344;
1481  ztop_arr(i, j, 0) = one;
1482  disp_hgt_arr(i, j, 0) = 0.65 * ztop_arr(i, j, 0);
1483  z0_sfc_arr(i, j, 0) = 0.1;
1484  Khai_L_arr(i, j, 0) = fourth;
1485  Rc_min_arr(i, j, 0) = 220.;
1486  Rgl_arr(i, j, 0) = 100.;
1487  hs_rc_arr(i, j, 0) = 0.01;
1488  BAI_arr(i, j, 0) = 60.;
1489  IMPERV_arr(i, j, 0) = zero;
1490  vegetype_arr(i, j, 0) = 1;
1491  break;
1492  case 8: // woody savannas
1493  albedovis_v_arr(i, j, 0) = 0.071;
1494  albedonir_v_arr(i, j, 0) = 0.085;
1495  albedovis_s_arr(i, j, 0) = 0.208;
1496  albedonir_s_arr(i, j, 0) = 0.344;
1497  ztop_arr(i, j, 0) = 5.;
1498  disp_hgt_arr(i, j, 0) = 0.65 * ztop_arr(i, j, 0);
1499  z0_sfc_arr(i, j, 0) = 0.3;
1500  Khai_L_arr(i, j, 0) = fourth;
1501  Rc_min_arr(i, j, 0) = 180.;
1502  Rgl_arr(i, j, 0) = 100.;
1503  hs_rc_arr(i, j, 0) = 0.02;
1504  BAI_arr(i, j, 0) = 100.;
1505  IMPERV_arr(i, j, 0) = zero;
1506  vegetype_arr(i, j, 0) = 1;
1507  break;
1508  case 9: // savannas
1509  albedovis_v_arr(i, j, 0) = 0.061;
1510  albedonir_v_arr(i, j, 0) = 0.227;
1511  albedovis_s_arr(i, j, 0) = 0.208;
1512  albedonir_s_arr(i, j, 0) = 0.344;
1513  ztop_arr(i, j, 0) = 5.;
1514  disp_hgt_arr(i, j, 0) = 0.65 * ztop_arr(i, j, 0);
1515  z0_sfc_arr(i, j, 0) = 0.3;
1516  Khai_L_arr(i, j, 0) = fourth;
1517  Rc_min_arr(i, j, 0) = 100.;
1518  Rgl_arr(i, j, 0) = 100.;
1519  hs_rc_arr(i, j, 0) = 0.01;
1520  BAI_arr(i, j, 0) = 100.;
1521  IMPERV_arr(i, j, 0) = zero;
1522  vegetype_arr(i, j, 0) = 1;
1523  break;
1524  case 10: // grasslands
1525  albedovis_v_arr(i, j, 0) = 0.09;
1526  albedonir_v_arr(i, j, 0) = 0.269;
1527  albedovis_s_arr(i, j, 0) = 0.208;
1528  albedonir_s_arr(i, j, 0) = 0.344;
1529  ztop_arr(i, j, 0) = myhalf;
1530  disp_hgt_arr(i, j, 0) = 0.65 * ztop_arr(i, j, 0);
1531  z0_sfc_arr(i, j, 0) = 0.04;
1532  Khai_L_arr(i, j, 0) = -0.3;
1533  Rc_min_arr(i, j, 0) = 100.;
1534  Rgl_arr(i, j, 0) = 100.;
1535  hs_rc_arr(i, j, 0) = 0.01;
1536  BAI_arr(i, j, 0) = 20.;
1537  IMPERV_arr(i, j, 0) = zero;
1538  vegetype_arr(i, j, 0) = 1;
1539  break;
1540  case 11: // permanent wetlands
1541  albedovis_v_arr(i, j, 0) = 0.081;
1542  albedonir_v_arr(i, j, 0) = 0.18;
1543  albedovis_s_arr(i, j, 0) = 0.208;
1544  albedonir_s_arr(i, j, 0) = 0.344;
1545  ztop_arr(i, j, 0) = myhalf;
1546  disp_hgt_arr(i, j, 0) = 0.65 * ztop_arr(i, j, 0);
1547  z0_sfc_arr(i, j, 0) = 0.2;
1548  Khai_L_arr(i, j, 0) = -0.3;
1549  Rc_min_arr(i, j, 0) = 100.;
1550  Rgl_arr(i, j, 0) = 100.;
1551  hs_rc_arr(i, j, 0) = 0.01;
1552  BAI_arr(i, j, 0) = 20.0;
1553  IMPERV_arr(i, j, 0) = zero;
1554  vegetype_arr(i, j, 0) =1;
1555  break;
1556  case 12: // croplands
1557  albedovis_v_arr(i, j, 0) = 0.084;
1558  albedonir_v_arr(i, j, 0) = 0.193;
1559  albedovis_s_arr(i, j, 0) = 0.208;
1560  albedonir_s_arr(i, j, 0) = 0.344;
1561  ztop_arr(i, j, 0) = myhalf;
1562  disp_hgt_arr(i, j, 0) = 0.65 * ztop_arr(i, j, 0);
1563  z0_sfc_arr(i, j, 0) = 0.03;
1564  Khai_L_arr(i, j, 0) = -0.3;
1565  Rc_min_arr(i, j, 0) = 100.;
1566  Rgl_arr(i, j, 0) = 100.;
1567  hs_rc_arr(i, j, 0) = 0.01;
1568  BAI_arr(i, j, 0) = 60.;
1569  IMPERV_arr(i, j, 0) = zero;
1570  vegetype_arr(i, j, 0) = 1;
1571  break;
1572  case 13: // urban
1573  albedovis_v_arr(i, j, 0) = 0.102;
1574  albedonir_v_arr(i, j, 0) = 0.164;
1575  albedovis_s_arr(i, j, 0) = 0.15;
1576  albedonir_s_arr(i, j, 0) = fourth;
1577  ztop_arr(i, j, 0) = 10.;
1578  disp_hgt_arr(i, j, 0) = 0.65 * ztop_arr(i, j, 0);
1579  z0_sfc_arr(i, j, 0) = one;
1580  Khai_L_arr(i, j, 0) = -0.3;
1581  Rc_min_arr(i, j, 0) = zero;
1582  Rgl_arr(i, j, 0) = zero;
1583  hs_rc_arr(i, j, 0) = zero;
1584  BAI_arr(i, j, 0) = zero;
1585  IMPERV_arr(i, j, 0) = 0.75;
1586  vegetype_arr(i, j, 0) = 0;
1587  break;
1588  case 14: // croplands/natural mozaics
1589  albedovis_v_arr(i, j, 0) = 0.071;
1590  albedonir_v_arr(i, j, 0) = 0.169;
1591  albedovis_s_arr(i, j, 0) = 0.208;
1592  albedonir_s_arr(i, j, 0) = 0.344;
1593  ztop_arr(i, j, 0) = myhalf;
1594  disp_hgt_arr(i, j, 0) = 0.65 * ztop_arr(i, j, 0);
1595  z0_sfc_arr(i, j, 0) = 0.04;
1596  Khai_L_arr(i, j, 0) = -0.3;
1597  Rc_min_arr(i, j, 0) = 100.;
1598  Rgl_arr(i, j, 0) = 100.;
1599  hs_rc_arr(i, j, 0) = 0.01;
1600  BAI_arr(i, j, 0) = 20.;
1601  IMPERV_arr(i, j, 0) = zero;
1602  vegetype_arr(i, j, 0) = 1;
1603  break;
1604  case 15: // snow/ice
1605  albedovis_v_arr(i, j, 0) = zero; // actually depends on moisture content
1606  albedonir_v_arr(i, j, 0) = zero;
1607  albedovis_s_arr(i, j, 0) = 0.91;
1608  albedonir_s_arr(i, j, 0) = 0.65;
1609  ztop_arr(i, j, 0) = zero;
1610  disp_hgt_arr(i, j, 0) = zero;
1611  z0_sfc_arr(i, j, 0) = 0.001;
1612  Khai_L_arr(i, j, 0) = zero;
1613  Rc_min_arr(i, j, 0) = zero;
1614  Rgl_arr(i, j, 0) = zero;
1615  hs_rc_arr(i, j, 0) = zero;
1616  BAI_arr(i, j, 0) = zero;
1617  IMPERV_arr(i, j, 0) = zero;
1618  vegetype_arr(i, j, 0) = 0;
1619  break;
1620  case 16: // baresoil
1621  albedovis_v_arr(i, j, 0) = zero; // actually depends on moisture content
1622  albedonir_v_arr(i, j, 0) = zero;
1623  albedovis_s_arr(i, j, 0) = 0.208;
1624  albedonir_s_arr(i, j, 0) = 0.344;
1625  ztop_arr(i, j, 0) = zero;
1626  disp_hgt_arr(i, j, 0) = zero;
1627  z0_sfc_arr(i, j, 0) = d_z0_soil;
1628  Khai_L_arr(i, j, 0) = zero;
1629  Rc_min_arr(i, j, 0) = zero;
1630  Rgl_arr(i, j, 0) = zero;
1631  hs_rc_arr(i, j, 0) = zero;
1632  BAI_arr(i, j, 0) = zero;
1633  IMPERV_arr(i, j, 0) = zero;
1634  vegetype_arr(i, j, 0) = 0;
1635  break;
1636  default:
1637 // TODO: check if AMReX::Abort can be called inside ParFor?
1638 #ifndef AMREX_USE_GPU
1639  amrex::Abort(
1640  "landtype invalid for i = " + std::to_string(i) +
1641  " j = " + std::to_string(j));
1642 #endif
1643  break;
1644  }
1645 #ifndef AMREX_USE_GPU
1646  if (vegetype_arr(i, j, 0) == 1 && LAI_arr(i, j, 0) == zero)
1647  {
1648  amrex::Abort("LAI is not set for vegetated land point at i = " + std::to_string(i) + " j = " + std::to_string(j));
1649  }
1650 #endif
1651  });
1652  }
1653 }
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

1833 {
1834  const int d_khi_lsm = khi_lsm;
1835  const int d_klo_lsm = klo_lsm;
1836 
1837  const bool wrfinput = use_wrfinput;
1838 
1839  for ( MFIter mfi(landtype, TileNoZ()); mfi.isValid(); ++mfi) {
1840  const auto& box = mfi.tilebox();
1841 
1842  auto s_depth_arr = lsm_fab_vars[LsmVar_SLM::s_depth]->const_array(mfi);
1843 
1844  auto node_z_arr = lsm_fab_vars[LsmVar_SLM::node_z]->array(mfi);
1845  auto interface_z_arr = lsm_fab_vars[LsmVar_SLM::interface_z]->array(mfi);
1846 
1847  ParallelFor(box, [=] AMREX_GPU_DEVICE (int i, int j, int)
1848  {
1849  // k = 0 = reference level
1850  // k = -1 = top of soil layer = khi_lsm
1851  // k = -m_nz_lsm = klo_lsm
1852 
1853  if (!wrfinput) {
1854  node_z_arr(i, j, d_khi_lsm) = myhalf * s_depth_arr(i, j, d_khi_lsm);
1855  for (int k = d_khi_lsm - 1; k >= d_klo_lsm; k--)
1856  {
1857  node_z_arr(i, j, k) = myhalf * s_depth_arr(i, j, k);
1858 
1859  for (int kk = d_khi_lsm; kk >= k + 1; kk--)
1860  {
1861  node_z_arr(i, j, k) = node_z_arr(i, j, k) + s_depth_arr(i, j, kk);
1862  }
1863  }
1864  }
1865 
1866  for (int k = d_khi_lsm; k >= d_klo_lsm; k--)
1867  {
1868  interface_z_arr(i, j, k) = node_z_arr(i, j, k) + myhalf*s_depth_arr(i, j, k);
1869  }
1870  });
1871  }
1872 }
@ interface_z
Definition: ERF_SLM.H:55
Here is the call graph for this function:

◆ init_slm_vars()

void SLM::init_slm_vars ( )
1950 {
1951  const int d_khi_lsm = khi_lsm;
1952 
1953  // Initializes SLM quantities from reference values tr, ts, qr
1954  for ( MFIter mfi(landtype, TileNoZ()); mfi.isValid(); ++mfi) {
1955  auto box = mfi.tilebox();
1956 
1957  auto landmask_arr = landmask.const_array(mfi);
1958 
1959  auto t_cas_arr = t_cas.array(mfi);
1960  auto q_cas_arr = q_cas.array(mfi);
1961  auto q_gr_arr = q_gr.array(mfi);
1962 
1963  auto t_canop_arr = t_canop.array(mfi);
1964  auto t_ground_skin_arr = t_ground_skin.array(mfi);
1965 
1966  auto soilt_arr = lsm_fab_vars[LsmVar_SLM::soilt]->const_array(mfi);
1967  auto soilw_arr = lsm_fab_vars[LsmVar_SLM::soilw]->const_array(mfi);
1968  auto tref_arr = lsm_fab_vars[LsmVar_SLM::tref]->const_array(mfi);
1969  auto tsurf_arr = lsm_fab_vars[LsmVar_SLM::tsurf]->const_array(mfi);
1970  auto qref_arr = lsm_fab_vars[LsmVar_SLM::qref]->const_array(mfi);
1971  auto pref_arr = lsm_fab_vars[LsmVar_SLM::pref]->const_array(mfi);
1972 
1973  //auto landtype_arr = landtype.const_array(mfi);
1974  ParallelFor( box, [=] AMREX_GPU_DEVICE (int i, int j, int)
1975  {
1976  if (landmask_arr(i, j, 0) == 1)
1977  {
1978  amrex::Real q_ground;
1979 
1980  // soil surface specific humidity
1981  if (tsurf_arr(i, j, 0) > tfriz)
1982  {
1983  erf_qsatw(tsurf_arr(i, j, 0), pref_arr(i, j, 0), q_ground);
1984  }
1985  else
1986  {
1987  erf_qsati(tsurf_arr(i, j, 0), pref_arr(i, j, 0), q_ground);
1988  }
1989  q_ground *= soilw_arr(i, j, d_khi_lsm);
1990  q_gr_arr(i, j, 0) = q_ground;
1991 
1992  // canopy temperature: initialize as (ref level temperature + soil surf temperature)/2
1993  //t_canop_arr(i, j, 0) = 0.5*(tref_arr(i, j, 0) + tsurf_arr(i, j, 0));
1994  //t_cas_arr(i, j, 0) = 0.5*(tref_arr(i, j, 0) + tsurf_arr(i, j, 0));
1995 
1996  t_canop_arr(i, j, 0) = tref_arr(i, j, 0);
1997  t_cas_arr(i, j, 0) = tref_arr(i, j, 0);
1998  t_ground_skin_arr(i, j, 0) = soilt_arr(i, j, d_khi_lsm);
1999 
2000  // specific humidity in canopy air space
2001  q_cas_arr(i, j, 0) = myhalf*(qref_arr(i, j, 0) + q_ground);
2002 
2003  //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;
2004 
2005  }
2006  });
2007  }
2008 
2009  // initialize nudging profiles for soil based on the initial soilt and soilw
2010  MultiFab::Copy(*lsm_fab_vars[LsmVar_SLM::soilt_obs], *lsm_fab_vars[LsmVar_SLM::soilt], 0, 0, 1, 0);
2011  MultiFab::Copy(*lsm_fab_vars[LsmVar_SLM::soilw_obs], *lsm_fab_vars[LsmVar_SLM::soilw], 0, 0, 1, 0);
2012 
2014 }
void initialize_surface_outputs()
Definition: ERF_SLM.cpp:2019
@ 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

1659 {
1660  // TODO - read from input file
1661  const Real d_tabs_s = tabs_s;
1662  const Real d_t00 = t00;
1663 
1664  const int d_khi_lsm = khi_lsm;
1665 
1666  const bool wrfinput = use_wrfinput;
1667 
1668  amrex::Gpu::DeviceVector<Real> d_dz_lsm_vec(m_dz_lsm.size());
1669  amrex::Gpu::DeviceVector<Real> d_clay_vec(m_dz_lsm.size());
1670  amrex::Gpu::DeviceVector<Real> d_sand_vec(m_dz_lsm.size());
1671  amrex::Gpu::DeviceVector<Real> d_st0_vec(m_dz_lsm.size());
1672  amrex::Gpu::DeviceVector<Real> d_sw0_vec(m_dz_lsm.size());
1673  amrex::Gpu::DeviceVector<Real> d_relax_vec(m_dz_lsm.size());
1674 
1675  amrex::Gpu::copy(Gpu::hostToDevice, m_dz_lsm.begin(), m_dz_lsm.end(), d_dz_lsm_vec.begin());
1676  amrex::Gpu::copy(Gpu::hostToDevice, clay0.begin(), clay0.end(), d_clay_vec.begin());
1677  amrex::Gpu::copy(Gpu::hostToDevice, sand0.begin(), sand0.end(), d_sand_vec.begin());
1678  amrex::Gpu::copy(Gpu::hostToDevice, st0.begin(), st0.end(), d_st0_vec.begin());
1679  amrex::Gpu::copy(Gpu::hostToDevice, sw0.begin(), sw0.end(), d_sw0_vec.begin());
1680  amrex::Gpu::copy(Gpu::hostToDevice, relax_hgt.begin(), relax_hgt.end(), d_relax_vec.begin());
1681  Real *d_dz_lsm = d_dz_lsm_vec.data();
1682  Real *d_clay0 = d_clay_vec.data();
1683  Real *d_sand0 = d_sand_vec.data();
1684  Real *d_st0 = d_st0_vec.data();
1685  Real *d_sw0 = d_sw0_vec.data();
1686  Real *d_relax = d_relax_vec.data();
1687 
1689  for ( MFIter mfi(*tsurf, TileNoZ()); mfi.isValid(); ++mfi) {
1690  const auto& box3d = mfi.tilebox();
1691 
1692  auto landmask_arr = landmask.const_array(mfi);
1693 
1694  auto soilt_arr = lsm_fab_vars[LsmVar_SLM::soilt]->array(mfi);
1695  auto soilw_arr = lsm_fab_vars[LsmVar_SLM::soilw]->array(mfi);
1696 
1697  auto tsurf_arr = lsm_fab_vars[LsmVar_SLM::tsurf]->array(mfi);
1698  auto lai_arr = lsm_fab_vars[LsmVar_SLM::lai]->array(mfi);
1699  auto vegtype_arr = lsm_fab_vars[LsmVar_SLM::vegtype]->array(mfi);
1700 
1701  auto sand_arr = lsm_fab_vars[LsmVar_SLM::sand]->array(mfi);
1702  auto clay_arr = lsm_fab_vars[LsmVar_SLM::clay]->array(mfi);
1703 
1704  auto s_depth_arr = lsm_fab_vars[LsmVar_SLM::s_depth]->array(mfi);
1705  auto soil_relax_hgt_arr = lsm_fab_vars[LsmVar_SLM::soil_relax_hgt]->array(mfi);
1706 
1707  auto sstxy_arr = sstxy.array(mfi);
1708 
1709  auto soiltype_arr = lsm_fab_vars[LsmVar_SLM::soiltype]->array(mfi);
1710  auto landtype_arr = landtype.const_array(mfi);
1711  auto LAI_local_arr = LAI.const_array(mfi);
1712 
1713  ParallelFor(box3d, [=] AMREX_GPU_DEVICE (int i, int j, int k)
1714  {
1715  if (landmask_arr(i, j, 0) == 1)
1716  {
1717  if (!wrfinput) {
1718  s_depth_arr(i, j, k) = d_dz_lsm[(k*-1)+d_khi_lsm];
1719  clay_arr(i, j, k) = d_clay0[(k*-1)+d_khi_lsm];
1720  sand_arr(i, j, k) = d_sand0[(k*-1)+d_khi_lsm];
1721  soilt_arr(i, j, k) = d_st0[(k*-1)+d_khi_lsm];
1722  soilw_arr(i, j, k) = d_sw0[(k*-1)+d_khi_lsm];
1723  } else {
1724  // initialize sand and clay % based on soil type from WRFInput
1725  switch (static_cast<int>(soiltype_arr(i, j, d_khi_lsm)))
1726  {
1727  case 1: // sand
1728  sand_arr(i,j,k) = 0.92;
1729  clay_arr(i,j,k) = 0.03;
1730  break;
1731  case 2: // loamy sand
1732  sand_arr(i,j,k) = 0.82;
1733  clay_arr(i,j,k) = 0.06;
1734  break;
1735  case 3: // sandy loam
1736  sand_arr(i,j,k) = 0.65;
1737  clay_arr(i,j,k) = 0.10;
1738  break;
1739  case 4: // silt loam
1740  sand_arr(i,j,k) = 0.20;
1741  clay_arr(i,j,k) = 0.15;
1742  break;
1743  case 5: // silt
1744  sand_arr(i,j,k) = 0.08;
1745  clay_arr(i,j,k) = 0.12;
1746  break;
1747  case 6: // loam
1748  sand_arr(i,j,k) = 0.40;
1749  clay_arr(i,j,k) = 0.20;
1750  break;
1751  case 7: // sandy clay loam
1752  sand_arr(i,j,k) = 0.60;
1753  clay_arr(i,j,k) = 0.30;
1754  break;
1755  case 8: // clay loam
1756  sand_arr(i,j,k) = 0.32;
1757  clay_arr(i,j,k) = 0.34;
1758  break;
1759  case 9: // silty clay loam
1760  sand_arr(i,j,k) = 0.20;
1761  clay_arr(i,j,k) = 0.40;
1762  break;
1763  case 10: // sandy clay
1764  sand_arr(i,j,k) = 0.52;
1765  clay_arr(i,j,k) = 0.42;
1766  break;
1767  case 11: // silty clay
1768  sand_arr(i,j,k) = 0.06;
1769  clay_arr(i,j,k) = 0.47;
1770  break;
1771  case 12: // clay
1772  sand_arr(i,j,k) = 0.20;
1773  clay_arr(i,j,k) = 0.60;
1774  break;
1775  case 13: // organic material
1776  //sand_arr(i,j,k) = 0.0;
1777  //clay_arr(i,j,k) = 0.0;
1778  sand_arr(i,j,k) = 0.001;
1779  clay_arr(i,j,k) = 0.001;
1780  break;
1781  case 14: // water
1782  sand_arr(i,j,k) = 0.001;
1783  clay_arr(i,j,k) = 0.001;
1784  break;
1785  case 15: // bedrock
1786  sand_arr(i,j,k) = 0.001;
1787  clay_arr(i,j,k) = 0.001;
1788  break;
1789  case 16: // other (urban/builtup, etc)
1790  sand_arr(i,j,k) = 0.001;
1791  clay_arr(i,j,k) = 0.001;
1792  break;
1793  default:
1794  sand_arr(i,j,k) = -1.0;
1795  clay_arr(i,j,k) = -1.0;
1796  break;
1797  }
1798 
1799  // convert to percentage
1800  sand_arr(i,j,k) *= 100.0;
1801  clay_arr(i,j,k) *= 100.0;
1802  }
1803 
1804  soil_relax_hgt_arr(i, j, k) = d_relax[(k*-1)+d_khi_lsm];
1805 
1806  // keep the 3D wrfinput copies consistent with the local SLM values if not using wrfinput
1807  if (!wrfinput && k == d_khi_lsm) {
1808  tsurf_arr(i, j, d_khi_lsm) = d_st0[0];
1809  tsurf_arr(i, j, 0) = d_st0[0];
1810  lai_arr(i, j, d_khi_lsm) = LAI_local_arr(i, j, 0);
1811  lai_arr(i, j, 0) = LAI_local_arr(i, j, 0);
1812  vegtype_arr(i, j, d_khi_lsm) = landtype_arr(i, j, 0);
1813  vegtype_arr(i, j, 0) = landtype_arr(i, j, 0);
1814  }
1815 
1816  // TODO: nrestart conditional here
1817  // TODO: sstxy - should be ERF surface temp array?
1818  sstxy_arr(i, j, 0) = soilt_arr(i, j, d_khi_lsm) - d_t00;
1819  } else {
1820  sstxy_arr(i, j, 0) = d_tabs_s - d_t00;
1821  }
1822  });
1823  }
1824 
1825  // Calculate node_z (depth of the center of each soil layer)
1827 }
void init_layer_depths()
Definition: ERF_SLM.cpp:1832
@ 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

1878 {
1879  const bool d_param_updated = params_updated;
1880 
1882  for ( MFIter mfi(*tsurf, TileNoZ()); mfi.isValid(); ++mfi) {
1883  const auto& box3d = mfi.tilebox();
1884 
1885  auto landmask_arr = landmask.const_array(mfi);
1886 
1887  auto sand_arr = lsm_fab_vars[LsmVar_SLM::sand]->const_array(mfi);
1888  auto clay_arr = lsm_fab_vars[LsmVar_SLM::clay]->const_array(mfi);
1889 
1890  auto w_s_FC_arr = lsm_fab_vars[LsmVar_SLM::w_s_FC]->array(mfi);
1891  auto w_s_WP_arr = lsm_fab_vars[LsmVar_SLM::w_s_WP]->array(mfi);
1892  auto sst_capa_arr = lsm_fab_vars[LsmVar_SLM::sst_capa]->array(mfi);
1893  auto sst_cond_arr = lsm_fab_vars[LsmVar_SLM::sst_cond]->array(mfi);
1894  auto poro_soil_arr = lsm_fab_vars[LsmVar_SLM::poro_soil]->array(mfi);
1895  auto theta_FC_arr = lsm_fab_vars[LsmVar_SLM::theta_FC]->array(mfi);
1896  auto theta_WP_arr = lsm_fab_vars[LsmVar_SLM::theta_WP]->array(mfi);
1897  auto m_pot_sat_arr = lsm_fab_vars[LsmVar_SLM::m_pot_sat]->array(mfi);
1898  auto Bconst_arr = lsm_fab_vars[LsmVar_SLM::Bconst]->array(mfi);
1899  auto ks_arr = lsm_fab_vars[LsmVar_SLM::ks]->array(mfi);
1900 
1901  ParallelFor(box3d, [=] AMREX_GPU_DEVICE (int i, int j, int k)
1902  {
1903  if (landmask_arr(i, j, 0) == 1)
1904  {
1905  if (!d_param_updated) {
1906  // compute the initial values
1907  const amrex::Real sand_per = sand_arr(i, j, k); // sand percentage
1908 
1909  // soil solids thermal conductivity (Johansen 1975)
1910  // quartz (= SAND content) thermal conductivity = 7.7 W/mK
1911  const amrex::Real mineral_tcond = sand_per > 20.0 ? two : three; // thermal conductivity of other minerals [W/mK]
1912  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));
1913 
1914  // Calculated following Cosby et al. 1984 ( hydraulic properties)
1915  // hydraulic conductivity at satuation , mm/s
1916  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]
1917 
1918  // constant B
1919  Bconst_arr(i, j, k) = 0.159 * clay_arr(i, j, k) + 2.91;
1920 
1921  // porosity (or saturation volumetric water content)
1922  poro_soil_arr(i, j, k) = -0.00126 * sand_per + 0.489; // volume/volume
1923 
1924  // moisture potential at saturation, [mm]
1925  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]
1926 
1927  // soil heat capacity, [J/m^3/K]
1928  // Following de Vries(1963) using SAND=34% CLAY=63%
1929  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));
1930 
1931  // volumetric moisture content at field capacity
1932  // field capacity is assumed to be the occasion when hydraulic conductivity is 0.1mm/d
1933  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));
1934 
1935  // volumetric moisture content at wilting point
1936  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));
1937  }
1938 
1939  // soil wetness at field capacity
1940  w_s_FC_arr(i, j, k) = theta_FC_arr(i, j, k) / poro_soil_arr(i, j, k);
1941 
1942  // soil wetness at wilting point
1943  w_s_WP_arr(i, j, k) = theta_WP_arr(i, j, k) / poro_soil_arr(i, j, k);
1944  }
1945  });
1946  }
1947 }
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.

955 {
956  if (wrfinput_initialized) {
957  return;
958  }
959 
960  // TODO: fix - WRFinput sets 3D MFs in lsm_data - these need to be copied into SLM local 2D MFs
961  const int d_khi_lsm = khi_lsm;
962 
963  const int isnature = 14; // nature cell type to use for urban cells
964  const int isurban = 13; // urban cell type
965  const int islake = 21; // lake cell
966  amrex::ignore_unused(isurban);
967 
968  const auto& input_vegtype = *lsm_fab_vars[LsmVar_SLM::vegtype];
969  const int input_vegtype_min = static_cast<int>(input_vegtype.min(0));
970  const int input_vegtype_max = static_cast<int>(input_vegtype.max(0));
971  if (input_vegtype_min < 0 || input_vegtype_max > 21) {
972  amrex::Abort("SLM: WRF vegetation categories must be in the supported range [0, 21], "
973  "where 0 denotes water/non-land! min = " +
974  std::to_string(input_vegtype_min) + ", max = " +
975  std::to_string(input_vegtype_max));
976  }
977 
978  for (MFIter mfi(landtype, TileNoZ()); mfi.isValid(); ++mfi) {
979  const Box bx2d = mfi.tilebox();
980 
981  auto vegtype_arr = lsm_fab_vars[LsmVar_SLM::vegtype]->array(mfi);
982  auto in_lai_arr = lsm_fab_vars[LsmVar_SLM::lai]->const_array(mfi);
983  auto in_tsk_arr = lsm_fab_vars[LsmVar_SLM::tsurf]->array(mfi);
984  auto in_tsoil_arr = lsm_fab_vars[LsmVar_SLM::soilt]->array(mfi);
985  auto in_vegfrac_arr = lsm_fab_vars[LsmVar_SLM::veg_frac]->array(mfi);
986  auto in_vegfrac_min_arr = lsm_fab_vars[LsmVar_SLM::veg_frac_min]->array(mfi);
987  auto in_vegfrac_max_arr = lsm_fab_vars[LsmVar_SLM::veg_frac_max]->array(mfi);
988 
989  auto emis_sfc_arr = lsm_fab_vars[LsmVar_SLM::emis_sfc]->array(mfi);
990  auto alb_vis_sfc_arr = lsm_fab_vars[LsmVar_SLM::alb_vis_sfc]->array(mfi);
991  auto alb_nir_sfc_arr = lsm_fab_vars[LsmVar_SLM::alb_nir_sfc]->array(mfi);
992  auto alb_vis_sfc_dif_arr = lsm_fab_vars[LsmVar_SLM::alb_vis_sfc_diff]->array(mfi);
993  auto alb_nir_sfc_dif_arr = lsm_fab_vars[LsmVar_SLM::alb_nir_sfc_diff]->array(mfi);
994 
995  auto landmask_arr = landmask.array(mfi);
996  auto landtype_arr = landtype.array(mfi);
997  auto lai_arr = LAI.array(mfi);
998  auto sstxy_arr = sstxy.array(mfi);
999  auto tskin_arr = t_skin.array(mfi);
1000  auto t_ground_skin_arr = t_ground_skin.array(mfi);
1001  auto vegetype_arr = vegetype.array(mfi);
1002  auto vege_YES_arr = vege_YES.array(mfi);
1003 
1004  const bool d_use_wrf_lai = use_wrf_lai;
1005  const Real d_urban_veg_frac = d_use_wrf_lai ? param_table[isnature - 1][1] : 0.0;
1006 
1007  ParallelFor(bx2d, [=] AMREX_GPU_DEVICE(int i, int j, int) noexcept
1008  {
1009  landtype_arr(i, j, 0) = static_cast<int>(vegtype_arr(i, j, d_khi_lsm));
1010  lai_arr(i, j, 0) = in_lai_arr(i, j, d_khi_lsm);
1011 
1012  const amrex::Real tsk = in_tsk_arr(i, j, d_khi_lsm);
1013  sstxy_arr(i, j, 0) = tsk;
1014  in_tsk_arr(i, j, 0) = tsk;
1015  tskin_arr(i, j, 0) = tsk;
1016  t_ground_skin_arr(i, j, 0) = in_tsoil_arr(i, j, d_khi_lsm);
1017 
1018  in_vegfrac_arr(i, j, d_khi_lsm) *= 0.01;
1019  in_vegfrac_arr(i, j, 0) = in_vegfrac_arr(i, j, d_khi_lsm);
1020  in_vegfrac_min_arr(i, j, d_khi_lsm) *= 0.01;
1021  in_vegfrac_min_arr(i, j, 0) = in_vegfrac_min_arr(i, j, d_khi_lsm);
1022  in_vegfrac_max_arr(i, j, d_khi_lsm) *= 0.01;
1023  in_vegfrac_max_arr(i, j, 0) = in_vegfrac_max_arr(i, j, d_khi_lsm);
1024 
1025  if (landtype_arr(i, j, 0) == 0 ||
1026  landtype_arr(i, j, 0) == 17 || landtype_arr(i, j, 0) == islake) {
1027  //amrex::Print() << " -- water cell - setting landtype=0 landmask=0" << std::endl;
1028  landmask_arr(i, j, 0) = 0;
1029  landtype_arr(i, j, 0) = 0;
1030  vegetype_arr(i, j, 0) = 0;
1031  vege_YES_arr(i, j, 0) = zero;
1032  in_tsoil_arr(i, j, d_khi_lsm) = 273.16;
1033 
1034  emis_sfc_arr(i, j, 0) = 0.98;
1035  emis_sfc_arr(i, j, d_khi_lsm) = emis_sfc_arr(i, j, 0);
1036 
1037  alb_vis_sfc_arr(i, j, 0) = 0.06;
1038  alb_nir_sfc_arr(i, j, 0) = 0.06;
1039  alb_vis_sfc_dif_arr(i, j, 0) = 0.06;
1040  alb_nir_sfc_dif_arr(i, j, 0) = 0.06;
1041 
1042  alb_vis_sfc_arr(i, j, d_khi_lsm) = alb_vis_sfc_arr(i, j, 0);
1043  alb_nir_sfc_arr(i, j, d_khi_lsm) = alb_nir_sfc_arr(i, j, 0);
1044  alb_vis_sfc_dif_arr(i, j, d_khi_lsm) = alb_vis_sfc_dif_arr(i, j, 0);
1045  alb_nir_sfc_dif_arr(i, j, d_khi_lsm) = alb_nir_sfc_dif_arr(i, j, 0);
1046 
1047  } else if (landtype_arr(i, j, 0) > 16) {
1048  // amrex::Print() << " -- urban cell (i = " << i << " j = " << j << ") - setting landtype=" << isnature << " (orig landtype = " << landtype_arr(i, j, 0) << ")" << std::endl;
1049  // for urban cells, set as a nature landtype for the non-building part
1050  landtype_arr(i, j, 0) = isnature;
1051  if (d_use_wrf_lai) {
1052  // set the vegetation frac
1053  in_vegfrac_arr(i, j, 0) = d_urban_veg_frac;
1054  }
1055  //landtype_arr(i, j, 0) = isurban;
1056  }
1057 
1058  // Keep the WRF vegetation class synchronized with the normalized SLM class.
1059  vegtype_arr(i, j, d_khi_lsm) = landtype_arr(i, j, 0);
1060  vegtype_arr(i, j, 0) = landtype_arr(i, j, 0);
1061  });
1062  }
1063 
1064  // If using WRFInput, SLM init needs to be delayed here to use correct values set from WRFInput
1065  slm_init();
1066 
1067  // Convert soil moisture from wrfinput to soil wetness for SLM
1068  for (MFIter mfi(*lsm_fab_vars[LsmVar_SLM::soilw], TileNoZ()); mfi.isValid(); ++mfi) {
1069  const Box box = mfi.tilebox();
1070  auto landmask_arr = landmask.const_array(mfi);
1071  auto poro_soil_arr = lsm_fab_vars[LsmVar_SLM::poro_soil]->const_array(mfi);
1072  auto wsoil_arr = lsm_fab_vars[LsmVar_SLM::soilw]->array(mfi);
1073 
1074  ParallelFor(box, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
1075  {
1076  if (landmask_arr(i, j, 0) == 1) {
1077  wsoil_arr(i, j, k) /= poro_soil_arr(i, j, k);
1078  }
1079  });
1080  }
1081 
1082  init_slm_vars();
1083  wrfinput_initialized = true;
1084 }
void init_slm_vars()
Definition: ERF_SLM.cpp:1949

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.

1254 {
1255  const int d_khi_lsm = khi_lsm;
1256  const int islake = 21;
1257 
1258  MultiFab saved_tsurf = lsm_fab_vars[LsmVar_SLM::tsurf]->deepCopy();
1259  MultiFab saved_soilt = lsm_fab_vars[LsmVar_SLM::soilt]->deepCopy();
1260  MultiFab saved_soilw = lsm_fab_vars[LsmVar_SLM::soilw]->deepCopy();
1261  Vector<std::unique_ptr<MultiFab>> saved_data;
1262  Vector<std::unique_ptr<MultiFab>> saved_unmapped;
1263  if (preserve_checkpoint_fields) {
1264  saved_data.resize(m_lsm_data_size);
1265  for (int ivar = 0; ivar < m_lsm_data_size; ++ivar) {
1266  saved_data[ivar] = std::make_unique<MultiFab>(
1267  lsm_fab_vars[LsmDataMap[ivar]]->deepCopy());
1268  }
1269  saved_unmapped.resize(unmapped_fields.size());
1270  for (int ivar = 0; ivar < static_cast<int>(unmapped_fields.size()); ++ivar) {
1271  saved_unmapped[ivar] = std::make_unique<MultiFab>(
1272  lsm_fab_vars[unmapped_fields[ivar]]->deepCopy());
1273  }
1274  }
1275 
1276  for (MFIter mfi(landtype, TileNoZ()); mfi.isValid(); ++mfi) {
1277  const Box box = mfi.tilebox();
1278  auto vegtype_arr = lsm_fab_vars[LsmVar_SLM::vegtype]->const_array(mfi);
1279  auto lai_in_arr = lsm_fab_vars[LsmVar_SLM::lai]->const_array(mfi);
1280  auto tsurf_arr = lsm_fab_vars[LsmVar_SLM::tsurf]->const_array(mfi);
1281 
1282  auto landmask_arr = landmask.array(mfi);
1283  auto landtype_arr = landtype.array(mfi);
1284  auto lai_arr = LAI.array(mfi);
1285  auto sstxy_arr = sstxy.array(mfi);
1286  auto tskin_arr = t_skin.array(mfi);
1287  auto vegetype_arr = vegetype.array(mfi);
1288  auto vege_yes_arr = vege_YES.array(mfi);
1289 
1290  ParallelFor(box, [=] AMREX_GPU_DEVICE (int i, int j, int) noexcept {
1291  const int input_landtype = static_cast<int>(vegtype_arr(i, j, d_khi_lsm));
1292  const bool water = input_landtype <= 0 || input_landtype == 17 || input_landtype == islake;
1293  const int normalized_landtype = (input_landtype > 16) ? 14 : input_landtype;
1294 
1295  landmask_arr(i, j, 0) = water ? 0 : 1;
1296  landtype_arr(i, j, 0) = water ? 0 : normalized_landtype;
1297  lai_arr(i, j, 0) = lai_in_arr(i, j, d_khi_lsm);
1298  sstxy_arr(i, j, 0) = tsurf_arr(i, j, d_khi_lsm);
1299  tskin_arr(i, j, 0) = tsurf_arr(i, j, d_khi_lsm);
1300  vegetype_arr(i, j, 0) = water ? 0 : 1;
1301  vege_yes_arr(i, j, 0) = water ? 0.0 : 1.0;
1302  });
1303  }
1304 
1305  slm_init();
1306 
1307  const IntVect state_ng = lsm_fab_vars[LsmVar_SLM::tsurf]->nGrowVect();
1308  MultiFab::Copy(*lsm_fab_vars[LsmVar_SLM::tsurf], saved_tsurf, 0, 0, 1, state_ng);
1309  MultiFab::Copy(*lsm_fab_vars[LsmVar_SLM::soilt], saved_soilt, 0, 0, 1, state_ng);
1310  MultiFab::Copy(*lsm_fab_vars[LsmVar_SLM::soilw], saved_soilw, 0, 0, 1, state_ng);
1311 
1312  for (MFIter mfi(*lsm_fab_vars[LsmVar_SLM::tsurf], TileNoZ()); mfi.isValid(); ++mfi) {
1313  const Box box = mfi.tilebox().makeSlab(2, 0);
1314  auto tsurf_arr = lsm_fab_vars[LsmVar_SLM::tsurf]->array(mfi);
1315  ParallelFor(box, [=] AMREX_GPU_DEVICE (int i, int j, int) noexcept {
1316  tsurf_arr(i, j, 0) = tsurf_arr(i, j, d_khi_lsm);
1317  });
1318  }
1319 
1320  init_slm_vars();
1321 
1322  if (preserve_checkpoint_fields) {
1323  for (int ivar = 0; ivar < m_lsm_data_size; ++ivar) {
1324  const IntVect ng = lsm_fab_vars[LsmDataMap[ivar]]->nGrowVect();
1325  MultiFab::Copy(*lsm_fab_vars[LsmDataMap[ivar]], *saved_data[ivar],
1326  0, 0, 1, ng);
1327  }
1328  for (int ivar = 0; ivar < static_cast<int>(unmapped_fields.size()); ++ivar) {
1329  const IntVect ng = lsm_fab_vars[unmapped_fields[ivar]]->nGrowVect();
1330  MultiFab::Copy(*lsm_fab_vars[unmapped_fields[ivar]], *saved_unmapped[ivar],
1331  0, 0, 1, ng);
1332  }
1333  }
1334 }
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.

2020 {
2021  const int d_khi_lsm = khi_lsm;
2022 
2023  for (MFIter mfi(landtype, TileNoZ()); mfi.isValid(); ++mfi) {
2024  const auto& box = mfi.tilebox();
2025 
2026  auto landmask_arr = landmask.const_array(mfi);
2027  auto LAI_arr = LAI.const_array(mfi);
2028  auto phi_1_arr = phi_1.const_array(mfi);
2029  auto phi_2_arr = phi_2.const_array(mfi);
2030  auto albedovis_v_arr = albedovis_v.const_array(mfi);
2031  auto albedonir_v_arr = albedonir_v.const_array(mfi);
2032  auto albedovis_s_arr = albedovis_s.const_array(mfi);
2033  auto albedonir_s_arr = albedonir_s.const_array(mfi);
2034  auto vegetype_arr = vegetype.const_array(mfi);
2035  auto IR_emis_vege_arr = IR_emis_vege.const_array(mfi);
2036  auto IR_emis_soil_arr = IR_emis_soil.const_array(mfi);
2037  auto veg_frac_arr = lsm_fab_vars[LsmVar_SLM::veg_frac]->array(mfi);
2038  auto coszrsxy_arr = lsm_fab_vars[LsmVar_SLM::coszrsxy]->const_array(mfi);
2039  auto soilw_arr = lsm_fab_vars[LsmVar_SLM::soilw]->const_array(mfi);
2040  auto t_canop_arr = t_canop.const_array(mfi);
2041  auto mw_arr = mw.const_array(mfi);
2042  auto t_cas_arr = t_cas.const_array(mfi);
2043  auto q_cas_arr = q_cas.const_array(mfi);
2044  auto q_gr_arr = q_gr.const_array(mfi);
2045  auto t_ground_skin_arr = t_ground_skin.const_array(mfi);
2046  auto tsurf_arr = lsm_fab_vars[LsmVar_SLM::tsurf]->array(mfi);
2047  auto t_skin_arr = t_skin.array(mfi);
2048  auto t_sfc_arr = t_sfc.array(mfi);
2049  auto q_sfc_arr = q_sfc.array(mfi);
2050  auto tveg_arr = lsm_fab_vars[LsmVar_SLM::tv]->array(mfi);
2051  auto mveg_arr = lsm_fab_vars[LsmVar_SLM::mv]->array(mfi);
2052  auto emis_sfc_arr = lsm_fab_vars[LsmVar_SLM::emis_sfc]->array(mfi);
2053  auto alb_nir_sfc_arr = lsm_fab_vars[LsmVar_SLM::alb_nir_sfc]->array(mfi);
2054  auto alb_vis_sfc_arr = lsm_fab_vars[LsmVar_SLM::alb_vis_sfc]->array(mfi);
2055  auto alb_nir_sfc_diff_arr = lsm_fab_vars[LsmVar_SLM::alb_nir_sfc_diff]->array(mfi);
2056  auto alb_vis_sfc_diff_arr = lsm_fab_vars[LsmVar_SLM::alb_vis_sfc_diff]->array(mfi);
2057 
2058  ParallelFor(box, [=] AMREX_GPU_DEVICE (int i, int j, int)
2059  {
2060  const amrex::Real cosz = coszrsxy_arr(i, j, 0);
2061  const amrex::Real direct_extinction = phi_1_arr(i, j, 0) / std::max(amrex::Real(0.01), cosz) + phi_2_arr(i, j, 0);
2062  const amrex::Real diffuse_extinction = phi_1_arr(i, j, 0) + phi_2_arr(i, j, 0);
2063  const amrex::Real explai = std::exp(-direct_extinction * LAI_arr(i, j, 0));
2064  const amrex::Real explai0 = std::exp(-diffuse_extinction * LAI_arr(i, j, 0));
2065  const amrex::Real wetfactor = one - myhalf * soilw_arr(i, j, d_khi_lsm);
2066 
2067  const amrex::Real veg_frac = std::min(std::max(veg_frac_arr(i, j, 0), amrex::Real(zero)), amrex::Real(one));
2068  const amrex::Real soil_nir = albedonir_s_arr(i, j, 0) * wetfactor;
2069  const amrex::Real soil_vis = albedovis_s_arr(i, j, 0) * wetfactor;
2070  const amrex::Real canopy_nir = albedonir_v_arr(i, j, 0) * (1.0 - explai) + soil_nir * explai;
2071  const amrex::Real canopy_vis = albedovis_v_arr(i, j, 0) * (1.0 - explai) + soil_vis * explai;
2072  const amrex::Real canopy_nir_diff = albedonir_v_arr(i, j, 0) * (1.0 - explai0) + soil_nir * explai0;
2073  const amrex::Real canopy_vis_diff = albedovis_v_arr(i, j, 0) * (1.0 - explai0) + soil_vis * explai0;
2074  const amrex::Real alb_nir = veg_frac * canopy_nir + (1.0 - veg_frac) * soil_nir;
2075  const amrex::Real alb_vis = veg_frac * canopy_vis + (1.0 - veg_frac) * soil_vis;
2076  const amrex::Real alb_nir_diff = veg_frac * canopy_nir_diff + (1.0 - veg_frac) * soil_nir;
2077  const amrex::Real alb_vis_diff = veg_frac * canopy_vis_diff + (1.0 - veg_frac) * soil_vis;
2078 
2079  t_skin_arr(i, j, 0) = tsurf_arr(i, j, 0);
2080  tsurf_arr(i, j, d_khi_lsm) = tsurf_arr(i, j, 0);
2081  veg_frac_arr(i, j, d_khi_lsm) = veg_frac;
2082  tveg_arr(i, j, 0) = t_canop_arr(i, j, 0);
2083  tveg_arr(i, j, d_khi_lsm) = t_canop_arr(i, j, 0);
2084  mveg_arr(i, j, 0) = mw_arr(i, j, 0);
2085  mveg_arr(i, j, d_khi_lsm) = mw_arr(i, j, 0);
2086 
2087  if (landmask_arr(i, j, 0) == 1 && vegetype_arr(i, j, 0) == 1) {
2088  t_sfc_arr(i, j, 0) = t_cas_arr(i, j, 0);
2089  q_sfc_arr(i, j, 0) = q_cas_arr(i, j, 0);
2090  } else {
2091  t_sfc_arr(i, j, 0) = t_ground_skin_arr(i, j, 0);
2092  q_sfc_arr(i, j, 0) = q_gr_arr(i, j, 0);
2093  }
2094 
2095  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);
2096  alb_nir_sfc_arr(i, j, 0) = alb_nir;
2097  alb_vis_sfc_arr(i, j, 0) = alb_vis;
2098  alb_nir_sfc_diff_arr(i, j, 0) = alb_nir_diff;
2099  alb_vis_sfc_diff_arr(i, j, 0) = alb_vis_diff;
2100 
2101  emis_sfc_arr(i, j, d_khi_lsm) = emis_sfc_arr(i, j, 0);
2102  alb_nir_sfc_arr(i, j, d_khi_lsm) = alb_nir_sfc_arr(i, j, 0);
2103  alb_vis_sfc_arr(i, j, d_khi_lsm) = alb_vis_sfc_arr(i, j, 0);
2104  alb_nir_sfc_diff_arr(i, j, d_khi_lsm) = alb_nir_sfc_diff_arr(i, j, 0);
2105  alb_vis_sfc_diff_arr(i, j, d_khi_lsm) = alb_vis_sfc_diff_arr(i, j, 0);
2106  });
2107  }
2108 }
Here is the call graph for this function:

◆ initialize_zrefxy()

void SLM::initialize_zrefxy ( )

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

1228 {
1229  const Real zlo = m_geom.ProbLo(2);
1230  const Real dz = m_geom.CellSize(2);
1231  for (MFIter mfi(landtype, TileNoZ()); mfi.isValid(); ++mfi) {
1232  const Box& xybx = mfi.growntilebox(0);
1233  const Array4<const Real>& z_nd_arr = use_terrain
1234  ? z_phys_nd->const_array(mfi) : Array4<Real>{};
1235  auto ztop_arr = ztop.array(mfi);
1236  auto zrefxy_arr = zrefxy.array(mfi);
1237  auto landmask_arr = landmask.const_array(mfi);
1238 
1239  ParallelFor(xybx, [=] AMREX_GPU_DEVICE(int i, int j, int) noexcept {
1240  if (landmask_arr(i, j, 0) == 1) {
1241  const Real znd = z_nd_arr ? Compute_Zrel_AtCellCenter(i, j, 0, z_nd_arr)
1242  : zlo + myhalf * dz;
1243  zrefxy_arr(i, j, 0) = ztop_arr(i, j, 0) + znd;
1244  }
1245  });
1246  }
1247 }

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
4670 {
4671  // returns a value that is linearly interpolated between x and y at time t. x
4672  // is at t=t0, y is at t=t1.
4673  if (t0 == t1 || t > t1) {
4674  return y;
4675  }
4676  const amrex::Real dt = (t - t0) / (t1 - t0);
4677  return x + (y - x) * dt;
4678 }

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

796 {
797  auto load_params = [](auto& params, const char* name, const auto& values)
798  {
799  auto d_var = std::make_unique<Gpu::DeviceVector<Real>>(values.size());
800  auto* d_var_ptr = d_var.get();
801  Gpu::copyAsync(Gpu::hostToDevice, values.data(), values.data() + values.size(), d_var_ptr->data());
802  params.emplace(name, std::move(d_var));
803  };
804 
805  load_params(d_veg_params, "cwpvt", SLMDefaultParams::cwpvt);
806  load_params(d_veg_params, "den", SLMDefaultParams::den);
807  load_params(d_veg_params, "hvb", SLMDefaultParams::hvb);
808  load_params(d_veg_params, "rc", SLMDefaultParams::rc);
809  load_params(d_veg_params, "rhol_vis", SLMDefaultParams::rhol_vis);
810  load_params(d_veg_params, "rhol_nir", SLMDefaultParams::rhol_nir);
811  load_params(d_veg_params, "rhos_vis", SLMDefaultParams::rhos_vis);
812  load_params(d_veg_params, "rhos_nir", SLMDefaultParams::rhos_nir);
813  load_params(d_veg_params, "taul_vis", SLMDefaultParams::taul_vis);
814  load_params(d_veg_params, "taul_nir", SLMDefaultParams::taul_nir);
815  load_params(d_veg_params, "taus_vis", SLMDefaultParams::taus_vis);
816  load_params(d_veg_params, "taus_nir", SLMDefaultParams::taus_nir);
817 
818  load_params(d_rad_params, "albsat_vis", SLMDefaultParams::albsat_vis);
819  load_params(d_rad_params, "albsat_nir", SLMDefaultParams::albsat_nir);
820  load_params(d_rad_params, "albdry_vis", SLMDefaultParams::albdry_vis);
821  load_params(d_rad_params, "albdry_nir", SLMDefaultParams::albdry_nir);
822  load_params(d_rad_params, "alblak", SLMDefaultParams::alblak);
823  load_params(d_rad_params, "omegas", SLMDefaultParams::omegas);
824 
825  Gpu::streamSynchronize();
826  amrex::Print() << " SLM: Using built-in Noah-MP radiation parameters" << std::endl;
827 }
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.

323 { return m_geom; }

◆ Lsm_Data_Ptr()

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

Reimplemented from NullSurf.

298  {
299  AMREX_ALWAYS_ASSERT(varIdx < SLM::m_lsm_data_size && varIdx >= 0);
300  int lsmIdx = LsmDataMap[varIdx];
301  AMREX_ALWAYS_ASSERT(lsmIdx < LsmVar_SLM::NumVars && lsmIdx>=0);
302  return lsm_fab_vars[lsmIdx].get();
303  }
Here is the call graph for this function:

◆ Lsm_Data_Size()

int SLM::Lsm_Data_Size ( )
inlineoverridevirtual

Reimplemented from NullSurf.

394 { return SLM::m_lsm_data_size; }

◆ Lsm_DataIndex()

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

Reimplemented from NullSurf.

410  {
411  int varIdx = -1;
412  std::string lc_varname = amrex::toLower(varname);
413  for (int idx(0); idx<SLM::m_lsm_data_size; ++idx) {
414  if (lc_varname == amrex::toLower(Lsm_DataName(idx))) {
415  varIdx = idx;
416  }
417  }
418  AMREX_ASSERT(varIdx != -1); // no name found
419  return varIdx;
420  }
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE int idx(int i, int j, int k, int nx, int ny)
Definition: ERF_InitForEnsemble.cpp:396
std::string Lsm_DataName(const int &varIdx) override
Definition: ERF_SLM.H:402
Here is the call graph for this function:

◆ Lsm_DataName()

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

Reimplemented from NullSurf.

403  {
404  AMREX_ALWAYS_ASSERT(varIdx < SLM::m_lsm_data_size && varIdx >= 0);
405  return LsmDataName[varIdx];
406  }

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.

308  {
309  AMREX_ALWAYS_ASSERT(varIdx < SLM::m_lsm_flux_size && varIdx >= 0);
310  int lsmIdx = LsmFluxMap[varIdx];
311  AMREX_ALWAYS_ASSERT(lsmIdx < LsmFlux_SLM::NumVars && lsmIdx>=0);
312  return lsm_fab_flux[lsmIdx].get();
313  }
Here is the call graph for this function:

◆ Lsm_Flux_Size()

int SLM::Lsm_Flux_Size ( )
inlineoverridevirtual

Reimplemented from NullSurf.

398 { return SLM::m_lsm_flux_size; }

◆ Lsm_FluxIndex()

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

Reimplemented from NullSurf.

432  {
433  int varIdx = -1;
434  std::string lc_varname = amrex::toLower(varname);
435  for (int idx(0); idx<SLM::m_lsm_flux_size; ++idx) {
436  if (lc_varname == amrex::toLower(Lsm_FluxName(idx))) {
437  varIdx = idx;
438  }
439  }
440  AMREX_ALWAYS_ASSERT(varIdx != -1); // no name found
441  return varIdx;
442  }
std::string Lsm_FluxName(const int &varIdx) override
Definition: ERF_SLM.H:424
Here is the call graph for this function:

◆ Lsm_FluxName()

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

Reimplemented from NullSurf.

425  {
426  AMREX_ALWAYS_ASSERT(varIdx < SLM::m_lsm_flux_size && varIdx >= 0);
427  return LsmFluxName[varIdx];
428  }

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.

317 { return m_lsm_geom; }

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

1176 {
1177  AMREX_ALWAYS_ASSERT(static_cast<int>(source_data.size()) == m_lsm_data_size);
1178  AMREX_ALWAYS_ASSERT(static_cast<int>(source_flux.size()) == m_lsm_flux_size);
1180 
1181  const IntVect rr2d(ref_ratio[0], ref_ratio[1], 1);
1182  for (int ivar = 0; ivar < m_lsm_data_size; ++ivar) {
1183  AMREX_ALWAYS_ASSERT(source_data[ivar] != nullptr);
1184  InterpFromCoarseLevel(*lsm_fab_vars[LsmDataMap[ivar]],
1185  lsm_fab_vars[LsmDataMap[ivar]]->nGrowVect(),
1186  IntVect(0,0,0), *source_data[ivar], 0, 0, 1,
1187  source_lsm_geom, m_lsm_geom, rr2d,
1188  &cell_cons_interp, m_domain_bcs_type,
1189  BCVars::cons_bc);
1190  }
1191 
1192  for (int ivar = 0; ivar < m_lsm_flux_size; ++ivar) {
1193  AMREX_ALWAYS_ASSERT(source_flux[ivar] != nullptr);
1194  InterpFromCoarseLevel(*lsm_fab_flux[LsmFluxMap[ivar]],
1195  lsm_fab_flux[LsmFluxMap[ivar]]->nGrowVect(),
1196  IntVect(0,0,0), *source_flux[ivar], 0, 0, 1,
1197  source_geom, m_geom, rr2d,
1198  &cell_cons_interp, m_domain_bcs_type,
1199  BCVars::cons_bc);
1200  }
1201 
1202  for (int ivar = 0; ivar < static_cast<int>(unmapped_fields.size()); ++ivar) {
1203  AMREX_ALWAYS_ASSERT(source_unmapped[ivar] != nullptr);
1204  InterpFromCoarseLevel(*lsm_fab_vars[unmapped_fields[ivar]],
1205  lsm_fab_vars[unmapped_fields[ivar]]->nGrowVect(),
1206  IntVect(0,0,0), *source_unmapped[ivar], 0, 0, 1,
1207  source_lsm_geom, m_lsm_geom, rr2d,
1208  &cell_cons_interp, m_domain_bcs_type,
1209  BCVars::cons_bc);
1210  }
1211 
1212  if (mode == LSMTransferMode::RawInput) {
1213  first_step = true;
1214  wrfinput_initialized = false;
1215  } else {
1218  first_step = false;
1220  }
1221 }
void initialize_processed_state(const bool preserve_checkpoint_fields=false)
Definition: ERF_SLM.cpp:1253
amrex::Vector< amrex::MultiFab * > source_data
Definition: ERF_SLM.H:710
amrex::Vector< amrex::MultiFab * > source_flux
Definition: ERF_SLM.H:711
amrex::Vector< amrex::MultiFab * > source_unmapped
Definition: ERF_SLM.H:712
@ cons_bc
Definition: ERF_IndexDefines.H:92
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.

1105 {
1106  const Geometry old_geom = m_geom;
1107  const Geometry old_lsm_geom = m_lsm_geom;
1108  Vector<std::unique_ptr<MultiFab>> saved_data(m_lsm_data_size);
1109  Vector<std::unique_ptr<MultiFab>> saved_flux(m_lsm_flux_size);
1110  Vector<std::unique_ptr<MultiFab>> saved_unmapped(unmapped_fields.size());
1111 
1112  for (int ivar = 0; ivar < m_lsm_data_size; ++ivar) {
1113  saved_data[ivar] = std::make_unique<MultiFab>(Lsm_Data_Ptr(ivar)->deepCopy());
1114  }
1115  for (int ivar = 0; ivar < m_lsm_flux_size; ++ivar) {
1116  saved_flux[ivar] = std::make_unique<MultiFab>(Lsm_Flux_Ptr(ivar)->deepCopy());
1117  }
1118  for (int ivar = 0; ivar < static_cast<int>(unmapped_fields.size()); ++ivar) {
1119  saved_unmapped[ivar] = std::make_unique<MultiFab>(
1120  lsm_fab_vars[unmapped_fields[ivar]]->deepCopy());
1121  }
1122 
1123  Init(lev, cons_in, u_in, v_in, geom, geom0, domain_bcs_type,
1124  refRatio, dt, z_phys_nd_in, nc_init_file);
1125 
1126  if (has_source) {
1127  Lsm_Interpolate_From_Source(source_geom, source_lsm_geom,
1128  source_ref_ratio, LSMTransferMode::ProcessedState);
1129 
1130  for (int ivar = 0; ivar < m_lsm_data_size; ++ivar) {
1131  lsm_fab_vars[LsmDataMap[ivar]]->ParallelCopy(*saved_data[ivar], 0, 0, 1);
1132  }
1133  for (int ivar = 0; ivar < m_lsm_flux_size; ++ivar) {
1134  lsm_fab_flux[LsmFluxMap[ivar]]->ParallelCopy(*saved_flux[ivar], 0, 0, 1);
1135  }
1136  } else {
1137  source_data.resize(m_lsm_data_size, nullptr);
1138  source_flux.resize(m_lsm_flux_size, nullptr);
1139  source_unmapped.resize(unmapped_fields.size(), nullptr);
1140  for (int ivar = 0; ivar < m_lsm_data_size; ++ivar) {
1141  source_data[ivar] = saved_data[ivar].get();
1142  }
1143  for (int ivar = 0; ivar < m_lsm_flux_size; ++ivar) {
1144  source_flux[ivar] = saved_flux[ivar].get();
1145  }
1146  for (int ivar = 0; ivar < static_cast<int>(unmapped_fields.size()); ++ivar) {
1147  source_unmapped[ivar] = saved_unmapped[ivar].get();
1148  }
1149 
1150  Lsm_Interpolate_From_Source(old_geom, old_lsm_geom,
1151  IntVect(1), LSMTransferMode::ProcessedState);
1152  }
1153 
1154  // ERF checkpoints the mapped fields and this registry of otherwise-private
1155  // SLM fields. Restore the latter after rebuilding the level so a remake
1156  // does not discard state which ERF does not expose through lsm_data.
1157  for (int ivar = 0; ivar < static_cast<int>(unmapped_fields.size()); ++ivar) {
1158  lsm_fab_vars[unmapped_fields[ivar]]->ParallelCopy(*saved_unmapped[ivar], 0, 0, 1);
1159  }
1162 
1163  source_data.clear();
1164  source_flux.clear();
1165  source_unmapped.clear();
1166 }
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:1172
amrex::MultiFab * Lsm_Flux_Ptr(const int &varIdx) override
Definition: ERF_SLM.H:307

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

331  {
332  AMREX_ALWAYS_ASSERT(varIdx >= 0 && varIdx < m_lsm_data_size);
333  if (static_cast<int>(source_data.size()) != m_lsm_data_size) {
334  source_data.resize(m_lsm_data_size, nullptr);
335  }
336  source_data[varIdx] = source_data_ptr;
337  }
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.

345  {
346  AMREX_ALWAYS_ASSERT(varIdx >= 0 && varIdx < m_lsm_flux_size);
347  if (static_cast<int>(source_flux.size()) != m_lsm_flux_size) {
348  source_flux.resize(m_lsm_flux_size, nullptr);
349  }
350  source_flux[varIdx] = source_flux_ptr;
351  }
Here is the call graph for this function:

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

375  {
376  AMREX_ALWAYS_ASSERT(varIdx >= 0 && varIdx < static_cast<int>(unmapped_fields.size()));
377  if (source_unmapped.size() != unmapped_fields.size()) {
378  source_unmapped.resize(unmapped_fields.size(), nullptr);
379  }
380  source_unmapped[varIdx] = source_data_ptr;
381  }
Here is the call graph for this function:

◆ Lsm_Type()

int SLM::Lsm_Type ( )
inlineoverridevirtual

Reimplemented from NullSurf.

484  {
485  return LSM_Type::SLM;
486  }
@ 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.

364  {
365  AMREX_ALWAYS_ASSERT(varIdx >= 0 && varIdx < static_cast<int>(unmapped_fields.size()));
366  return lsm_fab_vars[unmapped_fields[varIdx]].get();
367  }
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.

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

◆ Lsm_WRFInputNames()

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

Reimplemented from NullSurf.

445  {
446  return SLM::wrfinput_map;
447  }
static std::unordered_map< std::string, std::string > wrfinput_map
Definition: ERF_SLM.H:956

◆ radiation_noahmp()

void SLM::radiation_noahmp ( const amrex::MFIter &  mfi)
5629 {
5630  const int d_khi_lsm = khi_lsm;
5631  const int d_opt_rad = opt_rad;
5632  const int d_opt_alb = opt_alb;
5633 
5634  auto box = mfi.tilebox();
5635 
5636  auto landmask_arr = landmask.const_array(mfi);
5637  auto vegtype_arr = lsm_fab_vars[LsmVar_SLM::vegtype]->const_array(mfi);
5638  auto vegetype_arr = vegetype.const_array(mfi);
5639 
5640  // Input arrays
5641  auto swdsvisxyref_arr = lsm_fab_vars[LsmVar_SLM::swdsvisxyref]->const_array(mfi);
5642  auto swdsnirxyref_arr = lsm_fab_vars[LsmVar_SLM::swdsnirxyref]->const_array(mfi);
5643  auto swdsvisdxyref_arr = lsm_fab_vars[LsmVar_SLM::swdsvisdxyref]->const_array(mfi);
5644  auto swdsnirdxyref_arr = lsm_fab_vars[LsmVar_SLM::swdsnirdxyref]->const_array(mfi);
5645  auto coszrsxy_arr = lsm_fab_vars[LsmVar_SLM::coszrsxy]->const_array(mfi);
5646  auto precipref_arr = lsm_fab_vars[LsmVar_SLM::precipref]->const_array(mfi);
5647 
5648  auto LAI_arr = LAI.const_array(mfi);
5649  auto SAI_arr = SAI.const_array(mfi);
5650  auto t_canop_arr = t_canop.const_array(mfi);
5651  auto t_ground_skin_arr = t_ground_skin.const_array(mfi);
5652  auto soilw_arr = lsm_fab_vars[LsmVar_SLM::soilw]->const_array(mfi);
5653  auto poro_soil_arr = lsm_fab_vars[LsmVar_SLM::poro_soil]->const_array(mfi);
5654  auto veg_frac_arr = lsm_fab_vars[LsmVar_SLM::veg_frac]->const_array(mfi);
5655 
5656  // Existing SLM vegetation structure parameters (used instead of reading from NoahmpTable.TBL)
5657  // NOTE: Khai_L is equivalent to NOAHMP's XL (leaf/stem orientation index)
5658  // NOTE: ztop is equivalent to NOAHMP's HVT (top of canopy height in meters)
5659  auto Khai_L_arr = Khai_L.const_array(mfi); // leaf/stem orientation (NOAHMP name: XL)
5660  auto ztop_arr = ztop.const_array(mfi); // canopy height (NOAHMP name: HVT)
5661 
5662  // State variables for NOAHMP radiation
5663  auto tauss_arr = tauss_noahmp.array(mfi);
5664 
5665  // Net radiation arrays for output
5666  auto net_rad_arr = net_rad.array(mfi);
5667  auto t_skin_arr = t_skin.array(mfi);
5668 
5669  // Incoming longwave radiation
5670  auto lwref_arr = lsm_fab_vars[LsmVar_SLM::lwref]->const_array(mfi);
5671 
5672  // Outputs
5673  auto emis_sfc_arr = lsm_fab_vars[LsmVar_SLM::emis_sfc]->array(mfi);
5674  auto alb_nir_sfc_arr = lsm_fab_vars[LsmVar_SLM::alb_nir_sfc]->array(mfi);
5675  auto alb_vis_sfc_arr = lsm_fab_vars[LsmVar_SLM::alb_vis_sfc]->array(mfi);
5676  auto alb_nir_sfc_diff_arr = lsm_fab_vars[LsmVar_SLM::alb_nir_sfc_diff]->array(mfi);
5677  auto alb_vis_sfc_diff_arr = lsm_fab_vars[LsmVar_SLM::alb_vis_sfc_diff]->array(mfi);
5678 
5679  amrex::Real dt_loc = m_dt;
5680 
5681  // Get radiation parameters on device
5682  const amrex::Real* d_rhol_vis = d_veg_params.at("rhol_vis")->data();
5683  const amrex::Real* d_rhol_nir = d_veg_params.at("rhol_nir")->data();
5684  const amrex::Real* d_rhos_vis = d_veg_params.at("rhos_vis")->data();
5685  const amrex::Real* d_rhos_nir = d_veg_params.at("rhos_nir")->data();
5686  const amrex::Real* d_taul_vis = d_veg_params.at("taul_vis")->data();
5687  const amrex::Real* d_taul_nir = d_veg_params.at("taul_nir")->data();
5688  const amrex::Real* d_taus_vis = d_veg_params.at("taus_vis")->data();
5689  const amrex::Real* d_taus_nir = d_veg_params.at("taus_nir")->data();
5690  // NOTE: d_xl uses existing Khai_L_arr, d_hvt uses existing ztop_arr
5691  const amrex::Real* d_rc = d_veg_params.at("rc")->data();
5692  const amrex::Real* d_hvb = d_veg_params.at("hvb")->data();
5693  const amrex::Real* d_den = d_veg_params.at("den")->data();
5694 
5695  const amrex::Real* d_albsat_vis = d_rad_params.at("albsat_vis")->data();
5696  const amrex::Real* d_albsat_nir = d_rad_params.at("albsat_nir")->data();
5697  const amrex::Real* d_albdry_vis = d_rad_params.at("albdry_vis")->data();
5698  const amrex::Real* d_albdry_nir = d_rad_params.at("albdry_nir")->data();
5699  const amrex::Real* d_alblak = d_rad_params.at("alblak")->data();
5700  const amrex::Real* d_omegas = d_rad_params.at("omegas")->data();
5701 
5702  const amrex::Real d_betads = betads_rad;
5703  const amrex::Real d_betais = betais_rad;
5704  const amrex::Real d_tau0 = tau0_rad;
5705  const amrex::Real d_grain_growth = grain_growth_rad;
5706  const amrex::Real d_extra_growth = extra_growth_rad;
5707  const amrex::Real d_dirt_soot = dirt_soot_rad;
5708  const amrex::Real d_swemx = swemx_rad;
5709  const amrex::Real d_bats_cosz = bats_cosz_rad;
5710  const amrex::Real d_bats_vis_new = bats_vis_new_rad;
5711  const amrex::Real d_bats_nir_new = bats_nir_new_rad;
5712  const amrex::Real d_bats_vis_age = bats_vis_age_rad;
5713  const amrex::Real d_bats_nir_age = bats_nir_age_rad;
5714  const amrex::Real d_bats_vis_dir = bats_vis_dir_rad;
5715  const amrex::Real d_bats_nir_dir = bats_nir_dir_rad;
5716 
5717  // Emissivity parameters for longwave radiation
5718  const amrex::Real d_snow_emis = snow_emis_rad;
5719  const amrex::Real d_eg_soil = eg_soil_rad;
5720  const amrex::Real d_eg_lake = eg_lake_rad;
5721 
5722  // Stefan-Boltzmann constant
5723  constexpr amrex::Real SB = 5.67e-08; // W/m²/K⁴
5724 
5725  ParallelFor( box, [=] AMREX_GPU_DEVICE (int i, int j, int)
5726  {
5727  if (landmask_arr(i, j, 0) != 1) {
5728  return;
5729  }
5730 
5731  // --------------------------------------------------------------------------------------------------
5732  // Local variables for radiation calculation (all arrays from NOAHMP RADIATION subroutine)
5733  // --------------------------------------------------------------------------------------------------
5734  constexpr amrex::Real MPE = 1.0E-6;
5735  constexpr int NBAND = 2;
5736  amrex::Real fage; //snow age function (0 - new snow)
5737  amrex::Real albgrd[2]; //ground albedo (direct)
5738  amrex::Real albgri[2]; //ground albedo (diffuse)
5739  amrex::Real albd[2]; //surface albedo (direct)
5740  amrex::Real albi[2]; //surface albedo (diffuse)
5741  amrex::Real fabd[2]; //flux abs by veg (per unit direct flux)
5742  amrex::Real fabi[2]; //flux abs by veg (per unit diffuse flux)
5743  amrex::Real ftdd[2]; //down direct flux below veg (per unit dir flux)
5744  amrex::Real ftid[2]; //down diffuse flux below veg (per unit dir flux)
5745  amrex::Real ftii[2]; //down diffuse flux below veg (per unit dif flux)
5746  amrex::Real frevd[2], frevi[2], fregd[2], fregi[2];
5747  amrex::Real albsnd[2]; //snow albedo (direct)
5748  amrex::Real albsni[2]; //snow albedo (diffuse)
5749  amrex::Real ftdi[2]; //down direct flux below veg per unit dif flux = 0
5750 
5751  amrex::Real fsha; //shaded fraction of canopy
5752  amrex::Real vai; //total LAI + stem area index, one sided
5753  amrex::Real fsun; //sunlit fraction of canopy (-)
5754  amrex::Real laisun; //sunlit leaf area (-)
5755  amrex::Real laisha; //shaded leaf area (-)
5756  amrex::Real parsun; //average absorbed par for sunlit leaves (w/m2)
5757  amrex::Real parsha; //average absorbed par for shaded leaves (w/m2)
5758  amrex::Real sav; //solar radiation absorbed by vegetation (w/m2)
5759  amrex::Real sag; //solar radiation absorbed by ground (w/m2)
5760  amrex::Real fsa; //total absorbed solar radiation (w/m2)
5761  amrex::Real fsr; //total reflected solar radiation (w/m2)
5762  amrex::Real fsrv; //veg. reflected solar radiation (w/m2)
5763  amrex::Real fsrg; //ground reflected solar radiation (w/m2)
5764  amrex::Real bgap, wgap;
5765  amrex::Real gdir; //average projected leaf/stem area in solar direction
5766  amrex::Real ext; //optical depth direct beam per unit leaf + stem area
5767  amrex::Real xl_diag_val, chil_diag_val, phi1_diag_val, phi2_diag_val; //diagnostic outputs
5768 
5769  amrex::Real rho[2]; //leaf/stem reflectance weighted by fraction LAI and SAI
5770  amrex::Real tau[2]; //leaf/stem transmittance weighted by fraction LAI and SAI
5771  amrex::Real wl, ws; //fraction of LAI+SAI that is LAI/SAI
5772 
5773  // Get input values
5774  amrex::Real cosz = coszrsxy_arr(i, j, 0);
5775  amrex::Real elai = LAI_arr(i, j, 0);
5776  amrex::Real esai = SAI_arr(i, j, 0);
5777  amrex::Real tv = t_canop_arr(i, j, 0);
5778  amrex::Real tg = t_ground_skin_arr(i, j, 0);
5779  amrex::Real fveg = veg_frac_arr(i, j, d_khi_lsm);
5780  int vegtyp = static_cast<int>(vegtype_arr(i, j, d_khi_lsm));
5781  int veg_idx = vegtyp - 1; // convert to 0-based index
5782 
5783  // Incoming solar radiation
5784  amrex::Real solad[2], solai[2];
5785  solad[0] = swdsvisxyref_arr(i, j, 0); // direct visible
5786  solad[1] = swdsnirxyref_arr(i, j, 0); // direct NIR
5787  solai[0] = swdsvisdxyref_arr(i, j, 0); // diffuse visible
5788  solai[1] = swdsnirdxyref_arr(i, j, 0); // diffuse NIR
5789 
5790  // Snow variables (set to zero for now - can be added later)
5791  amrex::Real fsno = zero; // snow cover fraction
5792  amrex::Real snowh = zero; // snow height (mm)
5793  amrex::Real sneqvo = zero; // snow mass at last time step (mm)
5794  amrex::Real sneqv = zero; // snow mass (mm)
5795  amrex::Real qsnow = zero; // snowfall (mm/s)
5796  amrex::ignore_unused(snowh, qsnow);
5797 
5798  // Wetness fraction (simplified - using precipitation as indicator)
5799  amrex::Real fwet = (precipref_arr(i, j, 0) > zero) ? 0.1 : zero;
5800 
5801  // Soil moisture for top layer
5802  amrex::Real smc[1];
5803  smc[0] = soilw_arr(i, j, d_khi_lsm) * poro_soil_arr(i, j, d_khi_lsm);
5804 
5805  int nsoil = 1; // using top layer only for albedo calc
5806  int ice = 0; // not ice
5807  int ist = 1; // soil surface type
5808 
5809  // Initialize outputs
5810  bgap = zero;
5811  wgap = zero;
5812  for (int ib = 0; ib < NBAND; ib++) {
5813  albgrd[ib] = zero;
5814  albgri[ib] = zero;
5815  albd[ib] = zero;
5816  albi[ib] = zero;
5817  fabd[ib] = zero;
5818  fabi[ib] = zero;
5819  ftdd[ib] = zero;
5820  ftid[ib] = zero;
5821  ftii[ib] = zero;
5822  albsnd[ib] = zero;
5823  albsni[ib] = zero;
5824  frevd[ib] = zero;
5825  frevi[ib] = zero;
5826  fregd[ib] = zero;
5827  fregi[ib] = zero;
5828  ftdi[ib] = zero;
5829  }
5830  fsun = zero;
5831 
5832  // --------------------------------------------------------------------------------------------------
5833  // ALBEDO CALCULATION (inline to make it device-callable)
5834  // --------------------------------------------------------------------------------------------------
5835 
5836  // snow age (allow nighttime aging)
5837  snow_age_noahmp(dt_loc, tg, sneqvo, sneqv, d_tau0, d_grain_growth, d_extra_growth,
5838  d_dirt_soot, d_swemx, tauss_arr(i,j,0), fage);
5839 
5840  if (cosz > zero) {
5841 
5842  // weight reflectance/transmittance by LAI and SAI
5843  vai = elai + esai;
5844  wl = elai / std::max(vai,MPE);
5845  ws = esai / std::max(vai,MPE);
5846 
5847  // snow albedos: only if COSZ > 0 and FSNO > 0
5848  if(d_opt_alb == 1) {
5849  snowalb_bats_noahmp(NBAND, fsno, cosz, fage,
5850  d_bats_cosz, d_bats_vis_new, d_bats_nir_new,
5851  d_bats_vis_age, d_bats_nir_age,
5852  d_bats_vis_dir, d_bats_nir_dir,
5853  albsnd, albsni);
5854  }
5855 
5856  // ground surface albedo
5857  // Get soil albedo parameters (using soil color index = 4 as default for now)
5858  int soil_color_idx = 3; // index into albedo arrays (0-based for index 4 soil color)
5859  amrex::Real albsat[2], albdry[2], alblak[2];
5860  albsat[0] = d_albsat_vis[soil_color_idx];
5861  albsat[1] = d_albsat_nir[soil_color_idx];
5862  albdry[0] = d_albdry_vis[soil_color_idx];
5863  albdry[1] = d_albdry_nir[soil_color_idx];
5864  alblak[0] = d_alblak[0];
5865  alblak[1] = d_alblak[1];
5866 
5867  groundalb_noahmp(nsoil, NBAND, ice, ist, fsno, smc, albsnd, albsni, cosz, tg,
5868  albsat, albdry, alblak, albgrd, albgri);
5869 
5870  const bool has_canopy = vegetype_arr(i, j, 0) == 1 && fveg > zero && vai > zero;
5871  if (has_canopy) {
5872  // Get parameters for this vegetation type
5873  for (int ib = 0; ib < NBAND; ib++) {
5874  amrex::Real rhol_val = (ib == 0) ? d_rhol_vis[veg_idx] : d_rhol_nir[veg_idx];
5875  amrex::Real rhos_val = (ib == 0) ? d_rhos_vis[veg_idx] : d_rhos_nir[veg_idx];
5876  amrex::Real taul_val = (ib == 0) ? d_taul_vis[veg_idx] : d_taul_nir[veg_idx];
5877  amrex::Real taus_val = (ib == 0) ? d_taus_vis[veg_idx] : d_taus_nir[veg_idx];
5878  rho[ib] = std::max(rhol_val*wl + rhos_val*ws, MPE);
5879  tau[ib] = std::max(taul_val*wl + taus_val*ws, MPE);
5880  }
5881 
5882  // loop over NBAND wavebands to calculate surface albedos and solar
5883  // fluxes for unit incoming direct (IC=0) and diffuse (IC=1)
5884 
5885  for (int ib = 0; ib < NBAND; ib++) {
5886  // direct (IC=0)
5887  twostream_noahmp(ib, 0, vegtyp, cosz, vai, fwet, tv, albgrd, albgri,
5888  rho, tau, fveg, ist,
5889  Khai_L_arr(i,j,0), d_omegas[ib], d_betads, d_betais,
5890  d_opt_rad, d_rc[veg_idx], ztop_arr(i,j,0), d_hvb[veg_idx], d_den[veg_idx],
5891  fabd, albd, ftdd, ftid, gdir, frevd, fregd, bgap, wgap,
5892  xl_diag_val, chil_diag_val, phi1_diag_val, phi2_diag_val);
5893 
5894  // diffuse (IC=1)
5895  twostream_noahmp(ib, 1, vegtyp, cosz, vai, fwet, tv, albgrd, albgri,
5896  rho, tau, fveg, ist,
5897  Khai_L_arr(i,j,0), d_omegas[ib], d_betads, d_betais,
5898  d_opt_rad, d_rc[veg_idx], ztop_arr(i,j,0), d_hvb[veg_idx], d_den[veg_idx],
5899  fabi, albi, ftdi, ftii, gdir, frevi, fregi, bgap, wgap,
5900  xl_diag_val, chil_diag_val, phi1_diag_val, phi2_diag_val);
5901  }
5902 
5903  // sunlit fraction of canopy. set FSUN = 0 if FSUN < 0.01.
5904  ext = gdir/cosz * std::sqrt(one-rho[0]-tau[0]);
5905  fsun = (one-std::exp(-ext*vai)) / std::max(ext*vai,MPE);
5906  ext = fsun;
5907 
5908  if (ext < 0.01) {
5909  wl = zero;
5910  } else {
5911  wl = ext;
5912  }
5913  fsun = wl;
5914  } else {
5915  for (int ib = 0; ib < NBAND; ib++) {
5916  albd[ib] = albgrd[ib];
5917  albi[ib] = albgri[ib];
5918  ftdd[ib] = one;
5919  ftii[ib] = one;
5920  fregd[ib] = albgrd[ib];
5921  fregi[ib] = albgri[ib];
5922  }
5923  }
5924 
5925  } // cosz>0
5926 
5927  // --------------------------------------------------------------------------------------------------
5928  // SURRAD CALCULATION
5929  // --------------------------------------------------------------------------------------------------
5930 
5931  fsha = 1.0-fsun;
5932  laisun = elai*fsun;
5933  laisha = elai*fsha;
5934  vai = elai+ esai;
5935 
5936  surrad_noahmp(MPE, fsun, fsha, elai, vai, laisun, laisha, solad, solai,
5937  fabd, fabi, ftdd, ftid, ftii, albgrd, albgri, albd, albi,
5938  frevd, frevi, fregd, fregi,
5939  parsun, parsha, sav, sag, fsa, fsr, fsrv, fsrg);
5940 
5941  // --------------------------------------------------------------------------------------------------
5942  // LONGWAVE RADIATION CALCULATION (NOAHMP formulation)
5943  // --------------------------------------------------------------------------------------------------
5944  // NOAHMP longwave scheme includes multiple reflections between canopy and ground
5945  // References: NOAHMP module_sf_noahmplsm.F lines 2137-2144 (emissivity)
5946  // lines 3874-3875, 4041 (canopy LW)
5947  // lines 4086-4087, 4104 (ground LW)
5948  // --------------------------------------------------------------------------------------------------
5949 
5950  // Get incoming longwave radiation from atmosphere (W/m²)
5951  // lwdn is positive downward (standard atmospheric convention)
5952  amrex::Real lwdn = lwref_arr(i, j, 0);
5953 
5954  // Calculate emissivities
5955  // Vegetation emissivity (NOAHMP line 2139)
5956  // EMV depends on total vegetation area index (LAI + SAI)
5957  // EMV = 1 for dense canopy, EMV → 0 for sparse/no vegetation
5958  amrex::Real emv = 1.0 - std::exp(-(elai + esai));
5959 
5960  // Ground emissivity (NOAHMP lines 2140-2144)
5961  // EMG is weighted by snow cover fraction (fsno)
5962  // Note: ice = 1 if IST = 2 (lake/ice), for now assume ice = 0 (soil)
5963  // Note: fsno and ist are already declared earlier (lines 5159, 5174)
5964  amrex::Real emg;
5965  if (ist == 1) {
5966  emg = d_eg_soil * (1.0 - fsno) + d_snow_emis * fsno;
5967  } else {
5968  emg = d_eg_lake * (1.0 - fsno) + d_snow_emis * fsno;
5969  }
5970 
5971  // --------------------------------------------------------------------------------------------------
5972  // Canopy net longwave radiation (IRC) - NOAHMP lines 3874-3875, 4041
5973  // --------------------------------------------------------------------------------------------------
5974  // NOAHMP SIGN CONVENTION: IRC is NET UPWARD longwave flux (positive = energy loss)
5975  // IRC = emitted_upward - absorbed_from_atmosphere - absorbed_from_ground
5976  // NOAHMP energy balance: SAV - IRC - SHC - EVC = 0
5977  //
5978  // Physical interpretation of AIR_C term:
5979  // -EMV*LWDN: canopy absorbs atmospheric LW (gain, hence negative)
5980  // -EMV*(1-EMV)*(1-EMG)*LWDN: multiple reflection of atmospheric LW
5981  // (transmitted through canopy, reflected by ground, absorbed by canopy)
5982  // -EMV*EMG*SB*TG^4: canopy absorbs ground emission (gain, hence negative)
5983  //
5984  // Physical interpretation of CIR_C*TV^4 term:
5985  // 2*EMV*SB*TV^4: canopy emits in both directions (up and down)
5986  // -EMV^2*(1-EMG)*SB*TV^4: multiple reflection of canopy downward emission
5987  // (reflected by ground and re-absorbed by canopy)
5988  // --------------------------------------------------------------------------------------------------
5989  amrex::Real air_c = -emv * (1.0 + (1.0 - emv) * (1.0 - emg)) * lwdn
5990  - emv * emg * SB * amrex::Math::powi<4>(tg);
5991  amrex::Real cir_c = (two - emv * (one - emg)) * emv * SB;
5992  amrex::Real irc = air_c + cir_c * amrex::Math::powi<4>(tv);
5993 
5994  // --------------------------------------------------------------------------------------------------
5995  // Ground net longwave radiation (IRG) - NOAHMP lines 4086-4087, 4104
5996  // --------------------------------------------------------------------------------------------------
5997  // NOAHMP SIGN CONVENTION: IRG is NET UPWARD longwave flux (positive = energy loss)
5998  // IRG = emitted_upward - absorbed_from_atmosphere - absorbed_from_canopy
5999  // NOAHMP energy balance: SAG - IRG - SHG - EVG = 0
6000  //
6001  // Physical interpretation of AIR_G term:
6002  // -EMG*(1-EMV)*LWDN: ground absorbs transmitted atmospheric LW (gain, hence negative)
6003  // -EMG*EMV*SB*TV^4: ground absorbs canopy downward emission (gain, hence negative)
6004  //
6005  // Physical interpretation of CIR_G*TG^4 term:
6006  // EMG*SB*TG^4: ground emits upward
6007  // --------------------------------------------------------------------------------------------------
6008  amrex::Real air_g = -emg * (1.0 - emv) * lwdn - emg * emv * SB * amrex::Math::powi<4>(tv);
6009  amrex::Real cir_g = emg * SB;
6010  amrex::Real irg = cir_g * amrex::Math::powi<4>(tg) + air_g;
6011 
6012  amrex::Real tir1 = emv * SB * amrex::Math::powi<4>(tv);
6013  amrex::Real tir2 = emg * SB * amrex::Math::powi<4>(tg);
6014  amrex::Real lwdn1 = lwdn;
6015  amrex::Real lwdn2 = (1.0 - emv) * lwdn + tir1;
6016  amrex::Real lwup2 = tir2 + (1.0 - emg) * lwdn2;
6017  amrex::Real lwup1 = lwdn + irc + irg;
6018 
6019  // --------------------------------------------------------------------------------------------------
6020  // Store shortwave and longwave radiation to net_rad arrays
6021  // --------------------------------------------------------------------------------------------------
6022  // SLM SIGN CONVENTION: net_rad is NET ABSORBED radiation (positive = energy gain)
6023  // net_rad = absorbed_radiation (all sources) - emitted_radiation
6024  // SLM energy balance: net_rad - SH - LH = heat storage change
6025  //
6026  // SIGN CONVERSION: IRC/IRG (net upward) → net_lw (net absorbed)
6027  // net_absorbed = -net_upward
6028  // When IRC > 0 (net upward flux, energy loss), net_lw1 < 0 (energy loss)
6029  // When IRC < 0 (net downward flux, energy gain), net_lw1 > 0 (energy gain)
6030  // --------------------------------------------------------------------------------------------------
6031 
6032  // Shortwave absorbed (W/m²)
6033  net_rad_arr(i, j, 0, SLM_NetRad::net_sw1) = sav; // canopy absorbed solar
6034  net_rad_arr(i, j, 0, SLM_NetRad::net_sw2) = sag; // ground absorbed solar
6035 
6036  // Longwave absorbed (W/m²)
6037  // Sign flip converts NOAHMP convention (net upward) to SLM convention (net absorbed)
6038  net_rad_arr(i, j, 0, SLM_NetRad::tir1) = tir1;
6039  net_rad_arr(i, j, 0, SLM_NetRad::tir2) = tir2;
6040  net_rad_arr(i, j, 0, SLM_NetRad::net_lwup1) = lwup1;
6041  net_rad_arr(i, j, 0, SLM_NetRad::net_lwup2) = lwup2;
6042  net_rad_arr(i, j, 0, SLM_NetRad::net_lwdn1) = lwdn1;
6043  net_rad_arr(i, j, 0, SLM_NetRad::net_lwdn2) = lwdn2;
6044  net_rad_arr(i, j, 0, SLM_NetRad::net_lw1) = -irc; // canopy absorbed longwave
6045  net_rad_arr(i, j, 0, SLM_NetRad::net_lw2) = -irg; // ground absorbed longwave
6046 
6047  // Total net radiation (W/m²)
6048  // net_rad = shortwave_absorbed + longwave_absorbed
6049  // = sav + (-irc) = sav - irc
6050  net_rad_arr(i, j, 0, SLM_NetRad::net_rad1) = sav - irc; // canopy: SW + LW
6051  net_rad_arr(i, j, 0, SLM_NetRad::net_rad2) = sag - irg; // ground: SW + LW
6052 
6053  // Directional shortwave diagnostics use positive physical directions:
6054  // downwelling is positive in net_swdn and upwelling is positive in net_swup.
6055  // net_sw1 and net_sw2 remain positive for absorbed shortwave.
6056  // The surrad terms satisfy SW_in = reflected + canopy absorbed + ground absorbed.
6057  // Downwelling shortwave for stomatal resistance calculation
6058  // Total incoming SW = direct + diffuse for both visible and NIR
6059  const amrex::Real sw_in = solad[0] + solad[1] + solai[0] + solai[1];
6060  net_rad_arr(i, j, 0, SLM_NetRad::net_swdn1) = sw_in;
6061 
6062  // Reflected shortwave at the canopy top.
6063  net_rad_arr(i, j, 0, SLM_NetRad::net_swup1) = fsr;
6064 
6065  // Downwelling SW reaching ground (transmitted through canopy)
6066  amrex::Real sw_down_ground = zero;
6067  for (int ib = 0; ib < NBAND; ib++) {
6068  sw_down_ground += solad[ib] * (ftdd[ib] + ftid[ib])
6069  + solai[ib] * ftii[ib];
6070  }
6071  net_rad_arr(i, j, 0, SLM_NetRad::net_swdn2) = sw_down_ground;
6072  net_rad_arr(i, j, 0, SLM_NetRad::net_swup2) = sw_down_ground - sag;
6073  // --------------------------------------------------------------------------------------------------
6074  // Store albedo outputs
6075  // --------------------------------------------------------------------------------------------------
6076 
6077  alb_vis_sfc_arr(i, j, 0) = albd[0];
6078  alb_nir_sfc_arr(i, j, 0) = albd[1];
6079  alb_vis_sfc_diff_arr(i, j, 0) = albi[0];
6080  alb_nir_sfc_diff_arr(i, j, 0) = albi[1];
6081 
6082  alb_vis_sfc_arr(i, j, d_khi_lsm) = albd[0];
6083  alb_nir_sfc_arr(i, j, d_khi_lsm) = albd[1];
6084  alb_vis_sfc_diff_arr(i, j, d_khi_lsm) = albi[0];
6085  alb_nir_sfc_diff_arr(i, j, d_khi_lsm) = albi[1];
6086 
6087  // Surface emissivity for the vegetated canopy-ground system, including
6088  // the Noah-MP multiple-reflection term.
6089  amrex::Real emiss_sfc = emv + emg * (1.0 - emv)
6090  + emv * (1.0 - emv) * (1.0 - emg);
6091  emis_sfc_arr(i, j, 0) = emiss_sfc;
6092  emis_sfc_arr(i, j, d_khi_lsm) = emis_sfc_arr(i, j, 0);
6093 
6094  t_skin_arr(i, j, 0) = std::pow(
6095  std::max(MPE, (lwup1 - (one - emiss_sfc) * lwdn) /
6096  (emiss_sfc * SB)), fourth);
6097 
6098  }); // End ParallelFor
6099 }
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:5271
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:5508
int opt_rad
Definition: ERF_SLM.H:885
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:5101
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:5154
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:5209
int opt_alb
Definition: ERF_SLM.H:886
@ 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.

684 {
685  auto check_start = amrex::second();
686 
687  amrex::Print() << " Reading SLM checkpoint at level " << lev << std::endl;
688 
689  // Header
690  std::string File(checkpointname + "/Level_" + std::to_string(lev) + "/SLM_Header");
691 
692  VisMF::IO_Buffer io_buffer(VisMF::GetIOBufferSize());
693 
694  Vector<char> fileCharPtr;
695  ParallelDescriptor::ReadAndBcastFile(File, fileCharPtr);
696  std::string fileCharPtrString(fileCharPtr.dataPtr());
697  std::istringstream is(fileCharPtrString, std::istringstream::in);
698 
699  std::string line, word;
700 
701  // read in title line
702  std::getline(is, line);
703 
704  // read in number of soil layers
705  is >> m_nz_lsm;
706 
707  // read in array of dz
708  std::getline(is, line);
709  {
710  std::istringstream lis(line);
711  int i = 0;
712  while (lis >> word) {
713  m_dz_lsm[i++] = std::stod(word);
714  }
715  }
716  GotoNextLine(is);
717 
718  // Read in time
719  is >> m_dt;
720  is >> time;
721  is >> start_time;
722  is >> m_calday;
723  is >> m_orbital_year;
724  is >> m_orbital_mon;
725  is >> m_orbital_day;
726  is >> m_orbital_sec;
727  GotoNextLine(is);
728 
729  // Read in constants
730  is >> tabs_s;
731  is >> t00;
732  is >> LAI0;
733  is >> tausoil;
734  is >> zref;
735  is >> z0_soil;
736  is >> mws_mx0;
737  is >> Rc_max;
738  is >> T_opt;
739  GotoNextLine(is);
740 
741  // Read in flags
742  int reserved_slm_flag = 0;
743  is >> dosoiltnudging;
744  is >> dosoilwnudging;
745  is >> reserved_slm_flag;
746  is >> use_param_file;
747  is >> interpolate_lai;
748  is >> use_wrf_lai;
749  is >> use_wrfinput;
750  amrex::ignore_unused(reserved_slm_flag);
751  GotoNextLine(is);
752 
753  // read in level 'lev' BoxArray from Header
754  BoxArray ba;
755  ba.readFrom(is);
756  GotoNextLine(is);
757 
759 
760  //
761  // Lifecycle flags, if this checkpoint carries them (see WriteCheckpoint). The
762  // defaults are what the reader always did: treat the model as initialized.
763  //
764  // NOTE: the BoxArray read above is already followed by its own GotoNextLine, so
765  // the stream sits at the start of this line -- do not skip another.
766  //
767  int chk_first_step = 0;
768  int chk_wrfinput_initialized = (use_wrfinput) ? 1 : 0;
769  {
770  int a, b;
771  if (is >> a >> b) { chk_first_step = a; chk_wrfinput_initialized = b; }
772  }
773 
774  const std::string prefix = "SLM_";
775 
776  // Use distribution map from data vars, since those are already created and loaded by ERF
777  const DistributionMapping& dm = lsm_fab_vars[0]->DistributionMap();
778  IntVect ng = IntVect(0,0,0);
779  {
780  MultiFab mf(ba_lsm_2d,dm,1,ng);
781  VisMF::Read(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "landmask"));
782  landmask = amrex::cast<iMultiFab>(mf);
783 
784  VisMF::Read(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "landtype"));
785  landtype = amrex::cast<iMultiFab>(mf);
786 
787  VisMF::Read(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "vegetype"));
788  vegetype = amrex::cast<iMultiFab>(mf);
789  }
790 
791  {
792  // DST
793  MultiFab mf(soilt_vars.boxArray(),dm,SLM_DST::NumVars,0);
794  VisMF::Read(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "DST"));
795  MultiFab::Copy(soilt_vars,mf,0,0,SLM_DST::NumVars,ng);
796  }
797 
798  {
799  // DSW
800  MultiFab mf(soilw_vars.boxArray(),dm,SLM_DSW::NumVars,0);
801  VisMF::Read(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "DSW"));
802  MultiFab::Copy(soilw_vars,mf,0,0,SLM_DSW::NumVars,ng);
803  }
804 
805  {
806  // net_rad
807  MultiFab mf(net_rad.boxArray(),dm,SLM_NetRad::NumVars,0);
808  VisMF::Read(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "net_rad"));
809  MultiFab::Copy(net_rad,mf,0,0,SLM_NetRad::NumVars,ng);
810  }
811 
812  {
813  // slm_diag
814  MultiFab mf(slm_diag.boxArray(),dm,SLM_Diag::NumVars,0);
815  VisMF::Read(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "slm_diag"));
816  MultiFab::Copy(slm_diag,mf,0,0,SLM_Diag::NumVars,ng);
817  }
818 
819  MultiFab mf(ba_lsm_2d,dm,1,ng);
820 
821  VisMF::Read(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "LAI"));
822  MultiFab::Copy(LAI,mf,0,0,1,ng);
823 
824  VisMF::Read(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "SAI"));
825  MultiFab::Copy(SAI,mf,0,0,1,ng);
826 
827  VisMF::Read(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "sstxy"));
828  MultiFab::Copy(sstxy,mf,0,0,1,ng);
829 
830  VisMF::Read(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "t_canop"));
831  MultiFab::Copy(t_canop,mf,0,0,1,ng);
832 
833  VisMF::Read(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "mw"));
834  MultiFab::Copy(mw,mf,0,0,1,ng);
835 
836  VisMF::Read(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "mws"));
837  MultiFab::Copy(mws,mf,0,0,1,ng);
838 
839  VisMF::Read(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "t_skin"));
840  MultiFab::Copy(t_skin,mf,0,0,1,ng);
841 
842  VisMF::Read(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "t_ground_skin"));
843  MultiFab::Copy(t_ground_skin,mf,0,0,1,ng);
844 
845  VisMF::Read(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "t_cas"));
846  MultiFab::Copy(t_cas,mf,0,0,1,ng);
847 
848  VisMF::Read(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "q_cas"));
849  MultiFab::Copy(q_cas,mf,0,0,1,ng);
850 
851  VisMF::Read(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "vege_YES"));
852  MultiFab::Copy(vege_YES,mf,0,0,1,ng);
853 
854  VisMF::Read(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "cp_vege"));
855  MultiFab::Copy(cp_vege,mf,0,0,1,ng);
856 
857  VisMF::Read(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "z0_sfc"));
858  MultiFab::Copy(z0_sfc,mf,0,0,1,ng);
859 
860  VisMF::Read(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "Khai_L"));
861  MultiFab::Copy(Khai_L,mf,0,0,1,ng);
862 
863  VisMF::Read(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "phi_1"));
864  MultiFab::Copy(phi_1,mf,0,0,1,ng);
865 
866  VisMF::Read(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "phi_2"));
867  MultiFab::Copy(phi_2,mf,0,0,1,ng);
868 
869  VisMF::Read(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "IR_emis_vege"));
870  MultiFab::Copy(IR_emis_vege,mf,0,0,1,ng);
871 
872  VisMF::Read(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "IR_emis_soil"));
873  MultiFab::Copy(IR_emis_soil,mf,0,0,1,ng);
874 
875  VisMF::Read(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "ztop"));
876  MultiFab::Copy(ztop,mf,0,0,1,ng);
877 
878  VisMF::Read(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "disp_hgt"));
879  MultiFab::Copy(disp_hgt,mf,0,0,1,ng);
880 
881  VisMF::Read(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "Rgl"));
882  MultiFab::Copy(Rgl,mf,0,0,1,ng);
883 
884  VisMF::Read(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "Rc_min"));
885  MultiFab::Copy(Rc_min,mf,0,0,1,ng);
886 
887  VisMF::Read(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "hs_rc"));
888  MultiFab::Copy(hs_rc,mf,0,0,1,ng);
889 
890  VisMF::Read(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "precip_extinc"));
891  MultiFab::Copy(precip_extinc,mf,0,0,1,ng);
892 
893  VisMF::Read(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "mw_mx"));
894  MultiFab::Copy(mw_mx,mf,0,0,1,ng);
895 
896  VisMF::Read(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "mws_mx"));
897  MultiFab::Copy(mws_mx,mf,0,0,1,ng);
898 
899  VisMF::Read(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "BAI"));
900  MultiFab::Copy(BAI,mf,0,0,1,ng);
901 
902  VisMF::Read(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "mw_inc"));
903  MultiFab::Copy(mw_inc,mf,0,0,1,ng);
904 
905  VisMF::Read(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "evapo_dry"));
906  MultiFab::Copy(evapo_dry,mf,0,0,1,ng);
907 
908  VisMF::Read(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "shf_canop"));
909  MultiFab::Copy(shf_canop,mf,0,0,1,ng);
910 
911  VisMF::Read(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "shf_soil"));
912  MultiFab::Copy(shf_soil,mf,0,0,1,ng);
913 
914  VisMF::Read(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "shf_air"));
915  MultiFab::Copy(shf_air,mf,0,0,1,ng);
916 
917  VisMF::Read(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "lhf_canop"));
918  MultiFab::Copy(lhf_canop,mf,0,0,1,ng);
919 
920  VisMF::Read(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "lhf_soil"));
921  MultiFab::Copy(lhf_soil,mf,0,0,1,ng);
922 
923  VisMF::Read(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "lhf_air"));
924  MultiFab::Copy(lhf_air,mf,0,0,1,ng);
925 
926  VisMF::Read(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "albedovis_v"));
927  MultiFab::Copy(albedovis_v,mf,0,0,1,ng);
928 
929  VisMF::Read(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "albedonir_v"));
930  MultiFab::Copy(albedonir_v,mf,0,0,1,ng);
931 
932  VisMF::Read(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "albedovis_s"));
933  MultiFab::Copy(albedovis_s,mf,0,0,1,ng);
934 
935  VisMF::Read(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "albedonir_s"));
936  MultiFab::Copy(albedonir_s,mf,0,0,1,ng);
937 
938  VisMF::Read(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "r_a"));
939  MultiFab::Copy(r_a,mf,0,0,1,ng);
940 
941  VisMF::Read(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "r_b"));
942  MultiFab::Copy(r_b,mf,0,0,1,ng);
943 
944  VisMF::Read(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "r_c"));
945  MultiFab::Copy(r_c,mf,0,0,1,ng);
946 
947  VisMF::Read(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "r_d"));
948  MultiFab::Copy(r_d,mf,0,0,1,ng);
949 
950  VisMF::Read(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "r_soil"));
951  MultiFab::Copy(r_soil,mf,0,0,1,ng);
952 
953  VisMF::Read(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "wet_canop"));
954  MultiFab::Copy(wet_canop,mf,0,0,1,ng);
955 
956  VisMF::Read(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "zrefxy"));
957  MultiFab::Copy(zrefxy,mf,0,0,1,ng);
958 
959  VisMF::Read(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "albold_noahmp"));
960  MultiFab::Copy(albold_noahmp,mf,0,0,1,ng);
961 
962  VisMF::Read(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "tauss_noahmp"));
963  MultiFab::Copy(tauss_noahmp,mf,0,0,1,ng);
964 
965  for (int i = 0; i < static_cast<int>(unmapped_fields.size()); i++) {
966  MultiFab mf_unmapped(lsm_fab_vars[unmapped_fields[i]]->boxArray(),dm,1,IntVect(1,1,1));
967  VisMF::Read(mf_unmapped, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "Data" + std::to_string(unmapped_fields[i])));
968  MultiFab::Copy(*(lsm_fab_vars[unmapped_fields[i]]),mf_unmapped,0,0,1,IntVect(1,1,1));
969  }
970 
971  //
972  // Restore where the model was, rather than asserting it was past its first step.
973  // A checkpoint taken before any step has been advanced leaves first_step set, so
974  // the first Advance after the restart does the deferred wrfinput read and rebuilds
975  // zrefxy -- exactly what the run being continued did at that point.
976  //
977  first_step = (chk_first_step != 0);
978  wrfinput_initialized = (chk_wrfinput_initialized != 0);
979 
980  // Initialize common parameters
982  init_soil_vars();
984 
985  auto check_end = amrex::second() - check_start;
986  ParallelDescriptor::ReduceRealMax(check_end,ParallelDescriptor::IOProcessorNumber());
987  amrex::Print() << " SLM Checkpoint load time = " << check_end << " seconds." << '\n';
988 }
static void GotoNextLine(std::istream &is)
Definition: ERF_SLM_IO.cpp:375
int m_orbital_mon
Definition: ERF_SLM.H:943
amrex::Real m_calday
Definition: ERF_SLM.H:941
int m_orbital_sec
Definition: ERF_SLM.H:945
int m_orbital_year
Definition: ERF_SLM.H:942
int m_orbital_day
Definition: ERF_SLM.H:944
void rebuild_restart_fields()
Definition: ERF_SLM.cpp:2113
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.

2217 {
2218  amrex::Print() << " SLM: Reading parameter file '" << filename << "'..." << std::endl;
2219 
2220  Vector<char> fileCharPtr;
2221  ParallelDescriptor::ReadAndBcastFile(filename, fileCharPtr);
2222  std::string fileCharPtrString(fileCharPtr.dataPtr());
2223  std::istringstream is(fileCharPtrString, std::istringstream::in);
2224 
2225  std::string line;
2226 
2227  std::unordered_map<std::string,
2228  amrex::Vector<std::pair<std::string, amrex::Vector<amrex::Real>>>> table;
2229 
2230  const auto trim = [](std::string value) {
2231  const auto first = value.find_first_not_of(" \t\r\n");
2232  if (first == std::string::npos) { return std::string{}; }
2233  const auto last = value.find_last_not_of(" \t\r\n");
2234  return value.substr(first, last - first + 1);
2235  };
2236 
2237  std::string block_name;
2238  while (std::getline(is, line)) {
2239  const auto comment_pos = line.find('!');
2240  if (comment_pos != std::string::npos) { line.erase(comment_pos); }
2241  line = trim(line);
2242  if (line.empty()) { continue; }
2243 
2244  if (line[0] == '&') {
2245  block_name = amrex::toLower(trim(line.substr(1)));
2246  table.try_emplace(block_name);
2247  continue;
2248  }
2249  if (line[0] == '/') {
2250  block_name.clear();
2251  continue;
2252  }
2253  if (block_name.empty()) { continue; }
2254 
2255  const auto equal_pos = line.find('=');
2256  if (equal_pos == std::string::npos) { continue; }
2257 
2258  const std::string varname = amrex::toLower(trim(line.substr(0, equal_pos)));
2259  std::string values = trim(line.substr(equal_pos + 1));
2260  if (varname == "veg_dataset_description") {
2261  veg_categories.push_back(values);
2262  continue;
2263  }
2264  if (varname == "sltype") {
2265  soil_categories.push_back(values);
2266  continue;
2267  }
2268 
2269  std::replace(values.begin(), values.end(), ',', ' ');
2270  std::replace(values.begin(), values.end(), 'D', 'E');
2271  std::replace(values.begin(), values.end(), 'd', 'e');
2272  std::istringstream value_stream(values);
2273  std::string token;
2274  amrex::Vector<amrex::Real> var_values;
2275  while (value_stream >> token) {
2276  try {
2277  std::size_t parsed = 0;
2278  const amrex::Real value = std::stod(token, &parsed);
2279  if (parsed != token.size()) {
2280  amrex::Abort("SLM: invalid value '" + token + "' for parameter '" +
2281  varname + "' in block '" + block_name + "'");
2282  }
2283  var_values.push_back(value);
2284  } catch (const std::exception&) {
2285  amrex::Abort("SLM: invalid value '" + token + "' for parameter '" +
2286  varname + "' in block '" + block_name + "'");
2287  }
2288  }
2289  if (!var_values.empty()) {
2290  table[block_name].push_back(std::make_pair(varname, var_values));
2291  }
2292  }
2293 
2294 
2295  // Print out all the variables:
2296  amrex::Print() << "---------------------------------------------------" << std::endl;
2297  amrex::Print() << " SLM Parameter File '" << filename << "':" << std::endl;
2298  amrex::Print() << "---------------------------------------------------" << std::endl;
2299  for (auto &block : table) {
2300  amrex::Print() << " '" << block.first << "':" << std::endl;
2301  for (auto &var : block.second) {
2302  amrex::Print() << " - '" << var.first << "': [";
2303  for (int i = 0; i < static_cast<int>(var.second.size()); ++i) {
2304  amrex::Print() << " " << var.second[i];
2305  if (i + 1 < static_cast<int>(var.second.size())) {
2306  amrex::Print() << ",";
2307  }
2308  }
2309  amrex::Print() << " ]" << std::endl;
2310  }
2311  }
2312  amrex::Print() << "---------------------------------------------------" << std::endl;
2313 
2314  return table;
2315 }

◆ rebuild_restart_fields()

void SLM::rebuild_restart_fields ( )

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

2114 {
2115  const int d_khi_lsm = khi_lsm;
2116 
2117  nroot.setVal(m_nz_lsm);
2118  for (MFIter mfi(landtype, TileNoZ()); mfi.isValid(); ++mfi) {
2119  const Box box = mfi.tilebox();
2120 
2121  auto landmask_arr = landmask.const_array(mfi);
2122  auto landtype_arr = landtype.const_array(mfi);
2123  auto vegetype_arr = vegetype.const_array(mfi);
2124  auto nroot_arr = nroot.array(mfi);
2125  auto IR_emis_soil_arr = IR_emis_soil.const_array(mfi);
2126  auto IR_emis_grnd_arr = IR_emis_grnd.array(mfi);
2127  auto IMPERV_arr = IMPERV.array(mfi);
2128 
2129  ParallelFor(box, [=] AMREX_GPU_DEVICE (int i, int j, int) noexcept
2130  {
2131  if (landmask_arr(i, j, 0) == 1) {
2132  IMPERV_arr(i, j, 0) = landtype_arr(i, j, 0) == 13 ? 0.75 : 0.0;
2133  IR_emis_grnd_arr(i, j, 0) = landtype_arr(i, j, 0) == 13
2134  ? IR_emis_urban * IMPERV_arr(i, j, 0)
2135  + IR_emis_soil_arr(i, j, 0) * (1.0 - IMPERV_arr(i, j, 0))
2136  : IR_emis_soil_arr(i, j, 0);
2137  if (vegetype_arr(i, j, 0) == 0) {
2138  nroot_arr(i, j, 0) = 0;
2139  }
2140  } else {
2141  IMPERV_arr(i, j, 0) = zero;
2142  IR_emis_grnd_arr(i, j, 0) = zero;
2143  }
2144  });
2145  }
2146 
2147  // Reapply table-derived parameters without resetting prognostic state.
2148  init_from_params();
2149 
2150  for (MFIter mfi(landtype, TileNoZ()); mfi.isValid(); ++mfi) {
2151  const Box box = mfi.tilebox();
2152 
2153  auto landmask_arr = landmask.const_array(mfi);
2154  auto vegetype_arr = vegetype.const_array(mfi);
2155  auto t_ground_skin_arr = t_ground_skin.const_array(mfi);
2156  auto mws_arr = mws.const_array(mfi);
2157  auto m_pot_sat_arr = lsm_fab_vars[LsmVar_SLM::m_pot_sat]->const_array(mfi);
2158  auto soilw_arr = lsm_fab_vars[LsmVar_SLM::soilw]->const_array(mfi);
2159  auto Bconst_arr = lsm_fab_vars[LsmVar_SLM::Bconst]->const_array(mfi);
2160  auto pref_arr = lsm_fab_vars[LsmVar_SLM::pref]->const_array(mfi);
2161  auto q_gr_arr = q_gr.array(mfi);
2162  auto sdew_arr = sdew.array(mfi);
2163  auto t_sfc_arr = t_sfc.array(mfi);
2164  auto q_sfc_arr = q_sfc.array(mfi);
2165  auto t_cas_arr = t_cas.const_array(mfi);
2166  auto q_cas_arr = q_cas.const_array(mfi);
2167 
2168  ParallelFor(box, [=] AMREX_GPU_DEVICE (int i, int j, int) noexcept
2169  {
2170  if (landmask_arr(i, j, 0) != 1) {
2171  q_gr_arr(i, j, 0) = zero;
2172  sdew_arr(i, j, 0) = zero;
2173  t_sfc_arr(i, j, 0) = zero;
2174  q_sfc_arr(i, j, 0) = zero;
2175  return;
2176  }
2177 
2178  amrex::Real q_ground;
2179  amrex::Real dew_factor = one;
2180  if (t_ground_skin_arr(i, j, 0) >= tfriz) {
2181  erf_qsatw(t_ground_skin_arr(i, j, 0), pref_arr(i, j, 0), q_ground);
2182  if (mws_arr(i, j, 0) == zero) {
2183  const amrex::Real humidity_factor = fh_calc(
2184  t_ground_skin_arr(i, j, 0),
2185  m_pot_sat_arr(i, j, d_khi_lsm),
2186  soilw_arr(i, j, d_khi_lsm),
2187  Bconst_arr(i, j, d_khi_lsm));
2188  dew_factor = humidity_factor > 0.99 ? one : zero;
2189  q_ground *= humidity_factor;
2190  }
2191  } else {
2192  erf_qsati(t_ground_skin_arr(i, j, 0), pref_arr(i, j, 0), q_ground);
2193  }
2194 
2195  q_gr_arr(i, j, 0) = q_ground;
2196  sdew_arr(i, j, 0) = dew_factor;
2197  if (vegetype_arr(i, j, 0) == 1) {
2198  t_sfc_arr(i, j, 0) = t_cas_arr(i, j, 0);
2199  q_sfc_arr(i, j, 0) = q_cas_arr(i, j, 0);
2200  } else {
2201  t_sfc_arr(i, j, 0) = t_ground_skin_arr(i, j, 0);
2202  q_sfc_arr(i, j, 0) = q_ground;
2203  }
2204  });
2205  }
2206 }
void init_from_params()
Definition: ERF_SLM.cpp:2530
static constexpr amrex::Real IR_emis_urban
Definition: ERF_SLM.H:758
Here is the call graph for this function:

◆ resistances()

void SLM::resistances ( const amrex::MFIter &  mfi)
3439 {
3440  const int d_khi_lsm = khi_lsm;
3441  const int d_nz_lsm = m_nz_lsm;
3442  const Real d_z0_soil = z0_soil;
3443  const Real d_T_opt = T_opt;
3444  const Real d_Rc_max = Rc_max;
3445 
3446  auto box = mfi.tilebox();
3447 
3448  auto landmask_arr = landmask.const_array(mfi);
3449  auto landtype_arr = landtype.const_array(mfi);
3450  auto vegetype_arr = vegetype.const_array(mfi);
3451 
3452  auto soilt_arr = lsm_fab_vars[LsmVar_SLM::soilt]->const_array(mfi);
3453  auto soilw_arr = lsm_fab_vars[LsmVar_SLM::soilw]->const_array(mfi);
3454  auto s_depth_arr = lsm_fab_vars[LsmVar_SLM::s_depth]->const_array(mfi);
3455  auto pref_arr = lsm_fab_vars[LsmVar_SLM::pref]->const_array(mfi);
3456 
3457  auto LAI_arr = LAI.const_array(mfi);
3458  auto SAI_arr = SAI.const_array(mfi);
3459  auto t_cas_arr = t_cas.const_array(mfi);
3460  auto q_cas_arr = q_cas.const_array(mfi);
3461  auto ustar_arr = lsm_fab_vars[LsmVar_SLM::ustar]->const_array(mfi);
3462 
3463  auto poro_soil_arr = lsm_fab_vars[LsmVar_SLM::poro_soil]->const_array(mfi);
3464  auto theta_FC_arr = lsm_fab_vars[LsmVar_SLM::theta_FC]->const_array(mfi);
3465  auto theta_WP_arr = lsm_fab_vars[LsmVar_SLM::theta_WP]->const_array(mfi);
3466  auto soil_transp_frac_arr = lsm_fab_vars[LsmVar_SLM::soil_transp_frac]->array(mfi);
3467 
3468  auto ztop_arr = ztop.const_array(mfi);
3469  auto disp_hgt_arr = disp_hgt.const_array(mfi);
3470  auto z0_sfc_arr = z0_sfc.const_array(mfi);
3471  auto Rgl_arr = Rgl.const_array(mfi);
3472  auto Rc_min_arr = Rc_min.const_array(mfi);
3473  auto hs_rc_arr = hs_rc.const_array(mfi);
3474  auto nroot_arr = nroot.const_array(mfi);
3475  auto dleaf_arr = dleaf.const_array(mfi);
3476  auto zrefxy_arr = zrefxy.const_array(mfi);
3477  auto net_rad_arr = net_rad.const_array(mfi);
3478 
3479  auto ur_arr = lsm_fab_vars[LsmVar_SLM::uref]->const_array(mfi);
3480  auto vr_arr = lsm_fab_vars[LsmVar_SLM::vref]->const_array(mfi);
3481 
3482  auto r_a_arr = r_a.const_array(mfi);
3483  auto r_b_arr = r_b.array(mfi);
3484  auto r_c_arr = r_c.array(mfi);
3485  auto r_d_arr = r_d.array(mfi);
3486 
3487  // Get CWPVT parameter (canopy wind extinction factor) if available
3488  const amrex::Real* d_cwpvt = nullptr;
3489  if (d_veg_params.find("cwpvt") != d_veg_params.end()) {
3490  d_cwpvt = d_veg_params.at("cwpvt")->data();
3491  }
3492  const amrex::Real d_cwpvt_default = one; // default value if not from table
3493 
3494  ParallelFor( box, [=] AMREX_GPU_DEVICE (int i, int j, int)
3495  {
3496  if (landmask_arr(i, j, 0) != 1) {
3497  return;
3498  }
3499 
3500  for (int k = 0; k < d_nz_lsm; k++) {
3501  soil_transp_frac_arr(i, j, d_khi_lsm - k) = zero;
3502  }
3503 
3504  if (vegetype_arr(i, j, 0) == 0)
3505  {
3506  // for baresoil, r_d = r_a
3507  // under canopy resistance
3508  r_d_arr(i, j, 0) = r_a_arr(i, j, 0);
3509  }
3510  else
3511  {
3512  // for vegetated land surfaces
3513  amrex::Real rc_fac_rad, rc_fac_vpd, rc_fac_t, rc_fac_sw, d_root, tmp_radf;
3514 
3515  // ===================================================
3516  // Aerodynamic resistance for heat and vapor transfer under canopy space : r_d
3517  // ===================================================
3518  // NoahMP method: ResistanceLeafToGroundMod.F90
3519  // Reference: Niu et al. (2011), He et al. (2023)
3520 
3521  const amrex::Real MPE = 1.0e-6;
3522  const amrex::Real CONST_VON_KARMAN = 0.4;
3523  amrex::Real temp_diff = t_cas_arr(i, j, 0) - soilt_arr(i, j, d_khi_lsm);
3524 
3525  // Get CWPVT (canopy wind extinction parameter) for this vegetation type
3526  int veg_idx = landtype_arr(i, j, 0) - 1; // Convert the vegetation class to a 0-based table index
3527  amrex::Real CanopyWindExtFac = (d_cwpvt != nullptr) ? d_cwpvt[veg_idx] : d_cwpvt_default;
3528 
3529  // Stability correction for undercanopy (MoStabCorrShUndCan)
3530  amrex::Real MoStabCorrShUndCan = one; // Initialize to neutral
3531  // For now, use simple stability correction based on temperature difference
3532  if (temp_diff < zero) {
3533  // Unstable: (1 - 15*z/L)^(-0.25)
3534  amrex::Real zeta = std::min(amrex::Real(zero), amrex::Real(-0.1)); // Assume moderately unstable
3535  MoStabCorrShUndCan = std::pow(one - amrex::Real(15.0) * zeta, -fourth);
3536  } else {
3537  // Stable: 1 + 4.7*z/L
3538  amrex::Real zeta = std::min(amrex::Real(one), amrex::Real(0.1)); // Assume moderately stable
3539  MoStabCorrShUndCan = one + amrex::Real(4.7) * zeta;
3540  }
3541 
3542  // Wind extinction coefficient
3543  amrex::Real VegAreaIndEff = std::min(amrex::Real(6.0), LAI_arr(i, j, 0) + SAI_arr(i, j, 0));
3544  amrex::Real CanopyHeight = ztop_arr(i, j, 0);
3545  amrex::Real WindExtCoeffCanopy = std::sqrt(CanopyWindExtFac * VegAreaIndEff * CanopyHeight * MoStabCorrShUndCan);
3546 
3547  // Roughness lengths
3548  amrex::Real RoughLenShVegGrd = d_z0_soil; // Ground roughness for heat under canopy
3549  amrex::Real RoughLenShCanopy = z0_sfc_arr(i, j, 0); // Canopy roughness for heat
3550  amrex::Real ZeroPlaneDispSfc = disp_hgt_arr(i, j, 0); // Zero plane displacement
3551 
3552  // Exponential wind profile terms
3553  amrex::Real TMP1 = std::exp(-WindExtCoeffCanopy * RoughLenShVegGrd / CanopyHeight);
3554  amrex::Real TMP2 = std::exp(-WindExtCoeffCanopy * (RoughLenShCanopy + ZeroPlaneDispSfc) / CanopyHeight);
3555  amrex::Real TMPRAH2 = CanopyHeight * std::exp(WindExtCoeffCanopy) / WindExtCoeffCanopy * (TMP1 - TMP2);
3556 
3557  // Turbulent transfer coefficient KH
3558  amrex::Real KH = std::max(CONST_VON_KARMAN * ustar_arr(i, j, 0) * (CanopyHeight - ZeroPlaneDispSfc), MPE);
3559 
3560  // Undercanopy aerodynamic resistance
3561  r_d_arr(i, j, 0) = TMPRAH2 / KH;
3562 
3563  // Original SLM method (commented out for comparison)
3564  /*
3565  // temp_diff > 0 : stable undercanopy
3566  // temp_diff < 0 : unstable undercanopy
3567  amrex::Real temp_diff = t_cas_arr(i, j, 0) - soilt_arr(i, j, d_khi_lsm);
3568 
3569  // turbulent transfer coefficient under dense canopy
3570  if (temp_diff < zero)
3571  {
3572  Cs_dense = 0.004;
3573  }
3574  else
3575  {
3576  // rd_correc_fac : undercanopy stability parameter, in effect only for stable undercanopy
3577  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));
3578  // stable undercanopy - Cs_dense becomes smaller than 0.004
3579  Cs_dense = 0.004 / (one + myhalf * std::min(amrex::Real(10.0), rd_correc_fac));
3580  }
3581 
3582  // turbulent transfer coefficient over the exposed topsoil
3583  // typical value of Cs_bare ~0.2
3584  Cs_bare = 0.4 / 0.13 * std::pow((d_z0_soil * ustar_arr(i, j, 0) / (1.5e-5)), -0.45);
3585 
3586  // turbulence transfer coefficient undercanopy
3587  // LAI-weighed sum of Cs_bare and Cs_dense
3588  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)));
3589 
3590  // Undercanopy aerodynamic resistance depends on the weighed sum of the dense canopy covered soil
3591  // and baresoil turbulent transfer coefficient and friction velocity
3592  // Reference: [Oleson et al., 2004] [Zeng et al., 2005]
3593  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
3594  */
3595 
3596  // ===================================================
3597  // Leaf boundary layer resistance : r_b
3598  // ===================================================
3599  // Original SLM formula:
3600  // r_b_arr(i, j, 0) = 0.5 * r_a_arr(i, j, 0);
3601 
3602  // NoahMP leaf boundary resistance from ResistanceLeafToGroundMod.F90.
3603  const amrex::Real wind_ref = std::max(
3604  std::sqrt(ur_arr(i, j, 0) * ur_arr(i, j, 0)
3605  + vr_arr(i, j, 0) * vr_arr(i, j, 0)), MPE);
3606  const amrex::Real wind_canopy_top = std::max(
3607  wind_ref
3608  * std::log((CanopyHeight - ZeroPlaneDispSfc + RoughLenShCanopy)
3609  / RoughLenShCanopy)
3610  / std::log(zrefxy_arr(i, j, 0) / RoughLenShCanopy),
3611  MPE);
3612  const amrex::Real rb_factor = WindExtCoeffCanopy * 50.0
3613  / std::max(one - std::exp(-WindExtCoeffCanopy / two), MPE);
3614  r_b_arr(i, j, 0) = std::min(amrex::Real(50.0), std::max(amrex::Real(5.0),
3615  rb_factor * std::sqrt(dleaf_arr(i, j, 0) / wind_canopy_top)));
3616 
3617  // Original SLM Method 1 (commented out for reference)
3618  // turbulent transfer coefficient between canopy surface and canopy air : Cv = 0.01m/s^-0.5
3619  // characteristic dimension of the leaves in the direction of wind flux : d_leaf = 0.04m
3620  // 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));
3621  // Above equation seems to overestimate LHF
3622 
3623  // ===================================================
3624  // Stomatal resistance : r_c
3625  // ===================================================
3626  // radiation factor (Noah/NoahMP Jarvis bulk formulation)
3627  // Reference: Noah LSM CANRES subroutine, Chen et al. (1996, 2001)
3628  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);
3629  rc_fac_rad = (Rc_min_arr(i, j, 0) / d_Rc_max + RadFac) / (one + RadFac);
3630  rc_fac_rad = std::max(rc_fac_rad, amrex::Real(0.0001));
3631 
3632  /* Original SLM sunlit/shaded approach (commented out to match Noah bulk approach)
3633  // TODO: check if this is the correct downwelling SW to use
3634  //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);
3635  //rc_fac_rad = (Rc_min_arr(i, j, 0) / d_Rc_max + tmp_radf) / (1.0 + tmp_radf);
3636  // modify above to account for the shaded and sunlit part of leaves
3637  k_beer = phi_1_arr(i, j, 0) + phi_2_arr(i, j, 0); // extinction coef
3638  f_shade = 0.1 ; // fraction or radiation reaching shaded leaves (empirical)
3639 
3640  // partition LAI into sunlit and shaded components
3641  lai_sun = std::max(amrex::Real(1.e-6), (one - std::exp(-one * k_beer * LAI_arr(i, j, 0))) / k_beer);
3642  lai_shade = std::max(amrex::Real(zero), LAI_arr(i, j, 0) - lai_sun);
3643 
3644  // Radiation reaching to sunlit and shaded leaves
3645  sw_sun = net_rad_arr(i, j, 0, SLM_NetRad::net_swdn1);
3646  sw_shade = f_shade * sw_sun;
3647 
3648  // compute radiation factors (NoahMP Jarvis form - no LAI division)
3649  tmp_sun = 0.55 * sw_sun * 2. / Rgl_arr(i, j, 0);
3650  tmp_shade = 0.55 * sw_shade * 2. / Rgl_arr(i, j, 0);
3651  rc_fac_sun = (Rc_min_arr(i,j,0) / d_Rc_max + tmp_sun) / (one + tmp_sun);
3652  rc_fac_shade = (Rc_min_arr(i,j,0) / d_Rc_max + tmp_shade) / (one + tmp_shade);
3653 
3654  // combine weighted by LAI
3655  rc_fac_rad = (lai_sun * rc_fac_sun + lai_shade * rc_fac_shade) / LAI_arr(i, j, 0);
3656  *///
3657 
3658 
3659  // vapor pressure deficit factor (Noah/NoahMP Jarvis formulation)
3660  // Noah uses VPD in mixing ratio units (kg/kg), not vapor pressure (hPa)
3661  // Reference: Noah LSM CANRES subroutine, Chen et al. (1996, 2001)
3663  erf_qsatw(t_cas_arr(i, j, 0), pref_arr(i, j, 0), qsatw);
3664  amrex::Real VPD_mixratio = qsatw - q_cas_arr(i, j, 0); // VPD in mixing ratio [kg/kg]
3665  rc_fac_vpd = one / (one + hs_rc_arr(i, j, 0) * VPD_mixratio);
3666  rc_fac_vpd = std::max(rc_fac_vpd, amrex::Real(0.01));
3667 
3668  /* Original SLM VPD calculation using vapor pressure in hPa (commented out)
3669  // Above is modified following changes in gSAM-SLM
3670  amrex::Real e_cas, es_cas;
3671  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)
3672  es_cas = erf_esatw(t_cas_arr(i, j, 0));
3673  // NoahMP Jarvis form: hyperbolic response to VPD
3674  rc_fac_vpd = one / (one + hs_rc_arr(i, j, 0) * (es_cas - e_cas));
3675  *///
3676 
3677  // temperature factor
3678  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)));
3679 
3680  // NoahMP BTR_OPTION=1 soil-moisture stress factor. All rooted
3681  // layers remain in the depth denominator, including dry layers.
3682  const int nroot_cell = nroot_arr(i, j, 0);
3683  d_root = zero;
3684  for (int k = 0; k < nroot_cell; k++) {
3685  const int lsm_k = d_khi_lsm - k;
3686  d_root += s_depth_arr(i, j, lsm_k);
3687  }
3688 
3689  rc_fac_sw = zero;
3690  amrex::Real transp_frac_sum = zero;
3691  for (int k = 0; k < nroot_cell; k++) {
3692  const int lsm_k = d_khi_lsm - k;
3693  const amrex::Real theta_liq = soilw_arr(i, j, lsm_k) * poro_soil_arr(i, j, lsm_k);
3694  const amrex::Real moisture_range = theta_FC_arr(i, j, lsm_k) - theta_WP_arr(i, j, lsm_k);
3695  const amrex::Real soil_wet_fac = std::min(amrex::Real(one), std::max(amrex::Real(zero),
3696  (theta_liq - theta_WP_arr(i, j, lsm_k)) / moisture_range));
3697  const amrex::Real transp_frac =
3698  s_depth_arr(i, j, lsm_k) / std::max(amrex::Real(1.0e-6), d_root) * soil_wet_fac;
3699  soil_transp_frac_arr(i, j, lsm_k) = transp_frac;
3700  transp_frac_sum += transp_frac;
3701  rc_fac_sw += std::max(amrex::Real(1.0e-6), transp_frac);
3702  }
3703 
3704  rc_fac_sw = std::max(amrex::Real(1.0e-6), rc_fac_sw);
3705  for (int k = 0; k < nroot_cell; k++) {
3706  const int lsm_k = d_khi_lsm - k;
3707  if (transp_frac_sum > zero) {
3708  soil_transp_frac_arr(i, j, lsm_k) /= transp_frac_sum;
3709  }
3710  }
3711 
3712  tmp_radf = std::max(amrex::Real(1.0e-6), rc_fac_rad*rc_fac_vpd*rc_fac_t*rc_fac_sw);
3713  // Leaf-level stomatal resistance. LAI is applied once when this
3714  // resistance is converted to canopy transpiration conductance.
3715  r_c_arr(i, j, 0) = std::min(d_Rc_max, Rc_min_arr(i, j, 0) / tmp_radf);
3716 
3717  // ===================================================
3718  // r_litter : litter resistance - not used in this version
3719  // ===================================================
3720  //r_litter_arr(i, j, 0) = 0.0;
3721  // Ref. [Sakaguchi and Zeng 2009]
3722  // Set litter LAI as 0.5
3723  // r_litter_arr(i, j, 0) = 1.0 / 0.004 / ustar_arr(i, j, 0) * (1.0 - std::exp(-0.5));
3724  }
3725  });
3726 }
@ qsatw
Definition: ERF_WSM6.H:340
Here is the call graph for this function:

◆ set_dt()

void SLM::set_dt ( const amrex::Real &  dt)
inline
631  {
632  m_dt = dt;
633  }

◆ set_precip_input()

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

Reimplemented from NullSurf.

4819 {
4820  int khi = khi_lsm;
4821 
4823 
4824  for ( MFIter mfi(*tsurf, TileNoZ()); mfi.isValid(); ++mfi) {
4825  const auto& box3d = mfi.tilebox();
4826 
4827  // Create a box with the same i,j bounds, but only at z = 0
4828  amrex::Box b2d = box3d;
4829  b2d.setRange(2, 0);
4830 
4831  auto precip_array = precip_in->const_array(mfi);
4832 
4833  auto slm_precip = lsm_fab_vars[LsmVar_SLM::precipref]->array(mfi);
4834 
4835  ParallelFor(b2d, [=] AMREX_GPU_DEVICE (int i, int j, int k)
4836  {
4837  slm_precip(i, j, k) = precip_array(i, j, k, 0);
4838 
4839  // TODO: this is for plotting purposes.. state arrays are at k=0 which is ghost cell for SLM values
4840  // SLM AMREX plotfile does not write ghost cells, but NetCDF does - fix?
4841  slm_precip(i, j, khi) = slm_precip(i, j, 0);
4842  });
4843  }
4844 }
@ 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.

4849 {
4850  if (!first_step)
4851  {
4852  return;
4853  }
4855  const int d_khi_lsm = khi_lsm;
4856 
4857  if (lmask_in[0]) {
4858  for ( MFIter mfi(*tsurf, TileNoZ()); mfi.isValid(); ++mfi) {
4859  const auto& box3d = mfi.tilebox();
4860 
4861  // Create a box with the same i,j bounds, but only at z = 0
4862  amrex::Box b2d = box3d;
4863  b2d.setRange(2, 0);
4864 
4865  auto lmask_array = lmask_in[0]->array(mfi);
4866  auto slm_lmask = landmask.array(mfi);
4867  ParallelFor(b2d, [=] AMREX_GPU_DEVICE (int i, int j, int k)
4868  {
4869  slm_lmask(i, j, k) = lmask_array(i, j, k, 0);
4870  });
4871  }
4872  } else {
4873  landmask.setVal(1);
4874  }
4875 
4876  if (!use_wrfinput) {
4877  slm_init();
4878  }
4879 
4880  if (sst_in[0]) {
4881  // Set SLM surface temperature input from ERF.
4882  for ( MFIter mfi(*tsurf, TileNoZ()); mfi.isValid(); ++mfi) {
4883  const auto& box3d = mfi.tilebox();
4884 
4885  // Create a box with the same i,j bounds, but only at z = 0
4886  amrex::Box b2d = box3d;
4887  b2d.setRange(2, 0);
4888 
4889  auto sst_array = sst_in[0]->array(mfi);
4890  auto slm_sst = sstxy.array(mfi);
4891  auto slm_tsk = lsm_fab_vars[LsmVar_SLM::tsurf]->array(mfi);
4892  auto slm_tskin = t_skin.array(mfi);
4893  const bool d_use_wrfinput = use_wrfinput;
4894  ParallelFor(b2d, [=] AMREX_GPU_DEVICE (int i, int j, int k)
4895  {
4896  const amrex::Real tsk = d_use_wrfinput
4897  ? slm_tsk(i, j, d_khi_lsm)
4898  : sst_array(i, j, k, 0);
4899  slm_sst(i, j, k) = tsk;
4900  slm_tsk(i, j, k) = tsk;
4901  slm_tskin(i, j, k) = tsk;
4902  });
4903  }
4904  } else if (!use_wrfinput) {
4905  // If no terrain is setup, initialize SST to reference temperature.
4906  lsm_fab_vars[LsmVar_SLM::tsurf]->setVal(st0[0]);
4907  }
4908 }
Here is the call graph for this function:

◆ slm_init()

void SLM::slm_init ( )

Initialize SLM

833 {
834  // validate the landtype flag
835  const int landtype_min = landtype.min(0);
836  const int landtype_max = landtype.max(0);
837  if (landtype_min < 0 || landtype_max > 16)
838  {
839  amrex::Abort("SLM: landtype values are outside of valid 0-16 range! min = " +
840  std::to_string(landtype_min) + ", max = " + std::to_string(landtype_max));
841  }
842 
843  // validate LAI - check that they are within [0,10]
844  const double lai_min = LAI.min(0);
845  const double lai_max = LAI.max(0);
846  if (lai_min < zero || lai_max > 10.0)
847  {
848  amrex::Abort("SLM: LAI values are outside of valid [0-10] range! min = " +
849  std::to_string(lai_min) + ", max = " + std::to_string(lai_max));
850  }
851 
852  if (use_param_file && landtype_max > num_veg_params)
853  {
854  amrex::Abort("SLM: vegetation category " + std::to_string(landtype_max) +
855  " is outside the selected parameter table range [1, " +
856  std::to_string(num_veg_params) + "]");
857  }
858  if (interpolate_lai && landtype_max > num_landtypes)
859  {
860  amrex::Abort("SLM: vegetation category " + std::to_string(landtype_max) +
861  " is outside the LAI/SAI table range [1, " +
862  std::to_string(num_landtypes) + "]");
863  }
864  if (use_param_file) {
866  const int soiltype_min = static_cast<int>(soiltype.min(0));
867  const int soiltype_max = static_cast<int>(soiltype.max(0));
868  if (soiltype_min < 0 || soiltype_max > num_soil_params)
869  {
870  amrex::Abort("SLM: soil categories are outside the selected parameter table range [1, " +
871  std::to_string(num_soil_params) + "]! min = " +
872  std::to_string(soiltype_min) + ", max = " +
873  std::to_string(soiltype_max));
874  }
875  }
876 
877  // Sets model properties based on the landtype
878  init_landtype();
879 
880  // TODO: VEGYES and set minimum LAI
881  vege_YES.setVal(one);
882 
883  for ( MFIter mfi(landtype, TileNoZ()); mfi.isValid(); ++mfi) {
884  const auto& box = mfi.tilebox();
885 
886  auto vege_YES_arr = vege_YES.array(mfi);
887  auto vegetype_arr = vegetype.const_array(mfi);
888  auto nroot_arr = nroot.array(mfi);
889  auto landtype_arr = landtype.const_array(mfi);
890 
891  auto IR_emis_vege_arr = IR_emis_vege.array(mfi);
892  auto IR_emis_soil_arr = IR_emis_soil.array(mfi);
893  auto IR_emis_grnd_arr = IR_emis_grnd.array(mfi);
894  auto phi_1_arr = phi_1.array(mfi);
895  auto phi_2_arr = phi_2.array(mfi);
896  auto precip_extinc_arr = precip_extinc.array(mfi);
897  auto mw_mx_arr = mw_mx.array(mfi);
898  auto LAI_arr = LAI.array(mfi);
899  auto BAI_arr = BAI.const_array(mfi);
900  auto IMPERV_arr = IMPERV.const_array(mfi);
901  auto ztop_arr = ztop.const_array(mfi);
902  auto Khai_L_arr = Khai_L.array(mfi);
903  auto landmask_arr = landmask.const_array(mfi);
904 
905  auto veg_frac_arr = lsm_fab_vars[LsmVar_SLM::veg_frac]->array(mfi);
906 
907  ParallelFor(box, [=] AMREX_GPU_DEVICE (int i, int j, int)
908  {
909  if (landmask_arr(i, j, 0) == 1) {
910  if (vegetype_arr(i, j, 0) == 0) {
911  vege_YES_arr(i, j, 0) = zero;
912  veg_frac_arr(i, j, 0) = zero;
913  nroot_arr(i, j, 0) = 0;
914  } else {
915  // set minimum LAI for vegetated land
916  LAI_arr(i, j, 0) = std::max(LAI_arr(i, j, 0), amrex::Real(0.001));
917  }
918 
919  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));
920  phi_2_arr(i, j, 0) = 0.877 * (one - two * phi_1_arr(i, j, 0));
921  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)));
922  precip_extinc_arr(i, j, 0) = phi_1_arr(i, j, 0) + phi_2_arr(i, j, 0);
923  //mw_mx_arr(i, j, 0) = 0.1 * LAI_arr(i, j, 0);
924  //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
925  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);
926 
927  if(landtype_arr(i, j, 0) == 13)
928  {
929  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
930  }
931  else
932  {
933  IR_emis_grnd_arr(i, j, 0) = IR_emis_soil_arr(i, j, 0);
934  }
935  } else {
936  vege_YES_arr(i, j, 0) = zero;
937  veg_frac_arr(i, j, 0) = zero;
938  }
939  });
940  }
941 
942  // Initialize soil parameters
943  init_soil_tw();
944 
945  init_soil_vars();
946 
947  // Update any parameters using the values from a parameter file
949 }
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:1658
int num_soil_params
Definition: ERF_SLM.H:861
void init_landtype()
Definition: ERF_SLM.cpp:1339
int num_veg_params
Definition: ERF_SLM.H:860
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
5105 {
5106  //input
5107  // DT !main time step (s)
5108  // TG !ground temperature (k)
5109  // SNEQVO !snow mass at last time step(mm)
5110  // SNEQV !snow water per unit ground area (mm)
5111 
5112  //output
5113  // FAGE !snow age
5114 
5115  //input/output
5116  // TAUSS !non-dimensional snow age
5117 
5118  //local
5119  amrex::Real tage; //total aging effects
5120  amrex::Real age1; //effects of grain growth due to vapor diffusion
5121  amrex::Real age2; //effects of grain growth at freezing of melt water
5122  amrex::Real age3; //effects of soot
5123  amrex::Real dela; //temporary variable
5124  amrex::Real sge; //temporary variable
5125  amrex::Real dels; //temporary variable
5126  amrex::Real dela0; //temporary variable
5127  amrex::Real arg; //temporary variable
5128  // See Yang et al. (1997) J.of Climate for detail.
5129 
5130  constexpr amrex::Real TFRZ = 273.16; // freezing/melting point (k)
5131 
5132  if(sneqv <= zero) {
5133  tauss = zero;
5134  } else {
5135  dela0 = dt/tau0;
5136  arg = grain_growth*(1.0/TFRZ-1.0/tg);
5137  age1 = std::exp(arg);
5138  age2 = std::exp(std::min(amrex::Real(zero), extra_growth*arg));
5139  age3 = dirt_soot;
5140  tage = age1+age2+age3;
5141  dela = dela0*tage;
5142  dels = std::max(amrex::Real(zero), sneqv-sneqvo) / swemx;
5143  sge = (tauss+dela)*(1.0-dels);
5144  tauss = std::max(amrex::Real(zero),sge);
5145  }
5146 
5147  fage= tauss/(tauss+1.0);
5148 }

◆ 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
5159 {
5160  // --------------------------------------------------------------------------------------------------
5161  // input
5162  // NBAND !number of waveband classes
5163  // COSZ !cosine solar zenith angle
5164  // FSNO !snow cover fraction (-)
5165  // FAGE !snow age correction
5166 
5167  // output
5168  // ALBSND !snow albedo for direct(1=vis, 2=nir)
5169  // ALBSNI !snow albedo for diffuse
5170  // ---------------------------------------------------------------------------------------------
5171 
5172  // ------------------------ local variables ----------------------------------------------------
5173  amrex::Real fzen; //zenith angle correction
5174  amrex::Real cf1; //temporary variable
5175  amrex::Real sl2; //2.*SL
5176  amrex::Real sl1; //1/SL
5177  amrex::Real sl; //adjustable parameter
5178  // REAL, PARAMETER :: C1 = 0.2 !default in BATS
5179  // REAL, PARAMETER :: C2 = 0.5 !default in BATS
5180  // REAL, PARAMETER :: C1 = 0.2 * 2. ! double the default to match Sleepers River's
5181  // REAL, PARAMETER :: C2 = 0.5 * 2. ! snow surface albedo (double aging effects)
5182  // ---------------------------------------------------------------------------------------------
5183  // zero albedos for all points
5184 
5185  albsnd[0] = zero;
5186  albsnd[1] = zero;
5187  albsni[0] = zero;
5188  albsni[1] = zero;
5189 
5190  // when cosz > 0
5191 
5192  sl=bats_cosz;
5193  sl1=1.0/sl;
5194  sl2=two*sl;
5195  cf1=((1.0+sl1)/(1.0+sl2*cosz)-sl1);
5196  fzen=std::max(cf1,amrex::Real(zero));
5197 
5198  albsni[0]=bats_vis_new*(1.0-bats_vis_age*fage);
5199  albsni[1]=bats_nir_new*(1.0-bats_nir_age*fage);
5200 
5201  albsnd[0]=albsni[0]+bats_vis_dir*fzen*(1.0-albsni[0]); // vis direct
5202  albsnd[1]=albsni[1]+bats_nir_dir*fzen*(1.0-albsni[1]); // nir direct
5203 }

◆ soil_nudging()

void SLM::soil_nudging ( const amrex::MFIter &  mfi3d)
4612 {
4613  if (!dosoiltnudging && !dosoilwnudging) return;
4614 
4615  const Real dt = m_dt;
4616 
4617  auto box = mfi3d.tilebox();
4618 
4619  auto landmask_arr = landmask.const_array(mfi3d);
4620 
4621  auto soilt_arr = lsm_fab_vars[LsmVar_SLM::soilt]->array(mfi3d);
4622  auto soilw_arr = lsm_fab_vars[LsmVar_SLM::soilw]->array(mfi3d);
4623 
4624  auto soilt_nudge_arr = lsm_fab_vars[LsmVar_SLM::soilt_nudge]->array(mfi3d);
4625  auto soilw_nudge_arr = lsm_fab_vars[LsmVar_SLM::soilw_nudge]->array(mfi3d);
4626  auto soil_relax_hgt_arr = lsm_fab_vars[LsmVar_SLM::soil_relax_hgt]->const_array(mfi3d);
4627 
4628  auto soilt_obs_arr = lsm_fab_vars[LsmVar_SLM::soilt_obs]->const_array(mfi3d);
4629  auto soilw_obs_arr = lsm_fab_vars[LsmVar_SLM::soilw_obs]->const_array(mfi3d);
4630 
4631  const Real d_tau = tausoil;
4632  if (dosoiltnudging)
4633  {
4634  ParallelFor( box, [=] AMREX_GPU_DEVICE (int i, int j, int k)
4635  {
4636  if (landmask_arr(i, j, 0) != 1) {
4637  return;
4638  }
4639 
4640  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);
4641  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;
4642  });
4643  }
4644 
4645  if (dosoilwnudging)
4646  {
4647  ParallelFor( box, [=] AMREX_GPU_DEVICE (int i, int j, int k)
4648  {
4649  if (landmask_arr(i, j, 0) != 1) {
4650  return;
4651  }
4652 
4653  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);
4654  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;
4655  });
4656  }
4657 }
Here is the call graph for this function:

◆ soil_temperature()

void SLM::soil_temperature ( const amrex::MFIter &  mfi)
4449 {
4450  const int d_khi_lsm = khi_lsm;
4451  const int d_klo_lsm = klo_lsm;
4452  const int d_nz_lsm = m_nz_lsm;
4453  const Real dt = m_dt;
4454 
4455  auto box = mfi.tilebox();
4456 
4457  auto landmask_arr = landmask.const_array(mfi);
4458  auto landtype_arr = landtype.const_array(mfi);
4459 
4460  auto soilt_arr = lsm_fab_vars[LsmVar_SLM::soilt]->array(mfi);
4461  auto soilw_arr = lsm_fab_vars[LsmVar_SLM::soilw]->const_array(mfi);
4462 
4463  auto sst_capa_arr = lsm_fab_vars[LsmVar_SLM::sst_capa]->const_array(mfi);
4464  auto sst_cond_arr = lsm_fab_vars[LsmVar_SLM::sst_cond]->const_array(mfi);
4465  auto poro_soil_arr = lsm_fab_vars[LsmVar_SLM::poro_soil]->const_array(mfi);
4466 
4467  auto s_depth_arr = lsm_fab_vars[LsmVar_SLM::s_depth]->const_array(mfi);
4468 
4469  auto slm_diag_arr = slm_diag.array(mfi);
4470 
4471  auto dst_vars = soilt_vars.array(mfi);
4472 
4473  auto mws_arr = mws.array(mfi);
4474 
4475  // TODO: Refactor this whole loop for GPU
4476  ParallelFor( box, [=] AMREX_GPU_DEVICE (int i, int j, int)
4477  {
4478  if (landmask_arr(i, j, 0) != 1) {
4479  return;
4480  }
4481 
4482  amrex::Real temp, k_dry, k_sat, Ke;
4483 
4484  amrex::Real grflux0 = slm_diag_arr(i, j, 0, SLM_Diag::grflux);
4485  slm_diag_arr(i, j, 0, SLM_Diag::grflux) = grflux0;
4486  if (landtype_arr(i, j, 0) == 15) {
4487  // ice
4488  for (int k = 0; k < d_nz_lsm; k++) {
4489  const int lsm_k = d_khi_lsm - k;
4490  dst_vars(i, j, lsm_k, SLM_DST::st_cond) = 1.6; // thermal conductivity of ice
4491  dst_vars(i, j, lsm_k, SLM_DST::st_capa) = 917.0 * 2030.0; // ice heat capacity
4492  }
4493  } else {
4494  for (int k = 0; k < d_nz_lsm; k++) {
4495  const int lsm_k = d_khi_lsm - k;
4496 
4497  temp = (1.0 - poro_soil_arr(i, j, lsm_k)) * 2700.0;
4498 
4499  // dry thermal conductivity [W/mK]
4500  k_dry = (0.135 * temp + 64.7) / (2700.0 - 0.947*temp);
4501 
4502  // saturated soil thermal conductivity
4503  k_sat = std::pow(sst_cond_arr(i, j, lsm_k), one - poro_soil_arr(i, j, lsm_k));
4504  if (soilt_arr(i, j, lsm_k) > tfriz) {
4505  k_sat *= std::pow(amrex::Real(0.57), poro_soil_arr(i, j, lsm_k)); // 0.57 = cond_water
4506  }
4507  else {
4508  k_sat *= std::pow(amrex::Real(1.60), poro_soil_arr(i, j, lsm_k)); // 1.6 = cond_ice
4509  }
4510 
4511  // Weighing factor between dry and saturated soil thermal conductivity
4512  Ke = std::log10(std::max(amrex::Real(0.1), soilw_arr(i, j, lsm_k))) + one;
4513 
4514  // Total soil thermal conductivity at each node_z
4515  dst_vars(i, j, lsm_k, SLM_DST::st_cond) = Ke * (k_sat - k_dry) + k_dry;
4516 
4517  // Soil volumetric heaet capacity at each node_z depth
4518  if (soilt_arr(i, j, lsm_k) > tfriz) {
4519  dst_vars(i, j, lsm_k, SLM_DST::st_capa) =
4520  (1.0 - poro_soil_arr(i, j, lsm_k)) *
4521  sst_capa_arr(i, j, lsm_k) +
4522  (998.0 * 4182.0) * soilw_arr(i, j, lsm_k) *
4523  poro_soil_arr(i, j, lsm_k); // water heat capacity
4524  } else {
4525  dst_vars(i, j, lsm_k, SLM_DST::st_capa) =
4526  (1.0 - poro_soil_arr(i, j, lsm_k)) *
4527  sst_capa_arr(i, j, lsm_k) +
4528  (917.0 * 2030.0) * soilw_arr(i, j, lsm_k) *
4529  poro_soil_arr(i, j, lsm_k); // ice heat capacity
4530  }
4531  }
4532  }
4533 
4534  for (int k = 0; k < d_nz_lsm - 1; k++) {
4535  const int lsm_k = d_khi_lsm - k;
4536 
4537  // calculate effective conductivity at the adjacent soil layer interface
4538  dst_vars(i, j, lsm_k, SLM_DST::st_eff_cond) =
4539  (dst_vars(i, j, lsm_k - 1, SLM_DST::st_cond) * s_depth_arr(i, j, lsm_k - 1) +
4540  dst_vars(i, j, lsm_k, SLM_DST::st_cond) * s_depth_arr(i, j, lsm_k)) /
4541  (s_depth_arr(i, j, lsm_k - 1) + s_depth_arr(i, j, lsm_k));
4542  }
4543 
4544  // from FDE by the implicit, Thomas algorithm is applied
4545  // aa: terms related with T(k-1)
4546  // bb: terms related with T(k)
4547  // cc: terms related with T(k+1)
4548  // dd: current soil temperature + (additional source/sink on soil top)
4549  // For first layer:
4550  amrex::Real aa = zero;
4551  amrex::Real cc =
4552  -two * dst_vars(i, j, d_khi_lsm, SLM_DST::st_eff_cond) * dt /
4553  dst_vars(i, j, d_khi_lsm, SLM_DST::st_capa) /
4554  (s_depth_arr(i, j, d_khi_lsm) *
4555  (s_depth_arr(i, j, d_khi_lsm) + s_depth_arr(i, j, d_khi_lsm - 1)));
4556  amrex::Real bb = 1.0 - cc;
4557  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);
4558 
4559  dst_vars(i, j, d_khi_lsm, SLM_DST::alpha) = cc / bb;
4560  dst_vars(i, j, d_khi_lsm, SLM_DST::beta) = dd / bb;
4561 
4562  // For 2-(nsoil-1) layer:
4563  for (int k = 1; k < d_nz_lsm - 1; k++) {
4564  // NOTE: SLM k-1 indices here are switched from original SAM
4565  // version because our soil level indices are from -1 (top) to
4566  // -nsoil (bottom)
4567  // Index mapping:
4568  // SAM SLM -> ERF SLM
4569  // 1 -> d_khi_lsm
4570  // k - 1 -> k + 1
4571  // k + 1 -> k - 1
4572  // nsoil -> d_klo_lsm
4573  const int lsm_k = d_khi_lsm - k;
4574 
4575  const amrex::Real st_eff_cond_up = dst_vars(i, j, lsm_k + 1, SLM_DST::st_eff_cond);
4576  const amrex::Real st_eff_cond = dst_vars(i, j, lsm_k , SLM_DST::st_eff_cond);
4577  const amrex::Real st_capa = dst_vars(i, j, lsm_k, SLM_DST::st_capa);
4578 
4579  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));
4580  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));
4581  bb = 1.0 - cc - aa;
4582  dd = soilt_arr(i, j, lsm_k); // current time step
4583 
4584  dst_vars(i, j, lsm_k, SLM_DST::alpha) = cc / (bb - aa * dst_vars(i, j, lsm_k + 1, SLM_DST::alpha));
4585  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));
4586  }
4587 
4588  // For bottom layer:
4589  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));
4590  cc = zero;
4591  bb = 1.0 - aa;
4592  dd = soilt_arr(i, j, d_klo_lsm);
4593 
4594  dst_vars(i, j, d_klo_lsm, SLM_DST::alpha) = zero;
4595  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));
4596 
4597  // (n+1) time step soil temperature:
4598  soilt_arr(i, j, d_klo_lsm) = dst_vars(i, j, d_klo_lsm, SLM_DST::beta);
4599  for (int k = d_nz_lsm - 2; k >= 0; k--) {
4600  const int lsm_k = d_khi_lsm - k;
4601  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);
4602  }
4603 
4604  // for wetland, surface water is already implied: gSAM+SLM
4605  if (landtype_arr(i, j, 0) == 11) {
4606  mws_arr(i, j, 0) = zero;
4607  }
4608  });
4609 }
@ 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)
4173 {
4174  const int d_khi_lsm = khi_lsm;
4175  const int d_klo_lsm = klo_lsm;
4176  const int d_nz_lsm = m_nz_lsm;
4177  const Real dt = m_dt;
4178 
4179  auto box = mfi.tilebox();
4180 
4181  auto landmask_arr = landmask.const_array(mfi);
4182  auto landtype_arr = landtype.const_array(mfi);
4183 
4184  auto soilt_arr = lsm_fab_vars[LsmVar_SLM::soilt]->const_array(mfi);
4185  auto soilw_arr = lsm_fab_vars[LsmVar_SLM::soilw]->array(mfi);
4186 
4187  auto poro_soil_arr = lsm_fab_vars[LsmVar_SLM::poro_soil]->const_array(mfi);
4188  auto s_depth_arr = lsm_fab_vars[LsmVar_SLM::s_depth]->const_array(mfi);
4189 
4190  auto IMPERV_arr = IMPERV.const_array(mfi);
4191 
4192  auto mws_arr = mws.array(mfi);
4193  auto mws_mx_arr = mws_mx.const_array(mfi);
4194 
4195  auto evp_soil_arr = evp_soil.const_array(mfi);
4196  auto evapo_dry_arr = evapo_dry.const_array(mfi);
4197  auto prsfc_arr = lsm_fab_vars[LsmVar_SLM::prsfc]->array(mfi);
4198  auto m_pot_sat_arr = lsm_fab_vars[LsmVar_SLM::m_pot_sat]->const_array(mfi);
4199  auto ks_arr = lsm_fab_vars[LsmVar_SLM::ks]->const_array(mfi);
4200  auto Bconst_arr = lsm_fab_vars[LsmVar_SLM::Bconst]->const_array(mfi);
4201  auto w_s_WP_arr = lsm_fab_vars[LsmVar_SLM::w_s_WP]->const_array(mfi);
4202  auto w_s_FC_arr = lsm_fab_vars[LsmVar_SLM::w_s_FC]->const_array(mfi);
4203  auto soil_transp_frac_arr = lsm_fab_vars[LsmVar_SLM::soil_transp_frac]->const_array(mfi);
4204 
4205  auto slm_diag_arr = slm_diag.array(mfi);
4206 
4207  auto dsw_vars = soilw_vars.array(mfi);
4208 
4209  ParallelFor( box, [=] AMREX_GPU_DEVICE (int i, int j, int)
4210  {
4211  if (landmask_arr(i, j, 0) != 1) {
4212  return;
4213  }
4214 
4215  amrex::Real aa, bb, cc, dd;
4217  amrex::Real precip_sfc_loc = prsfc_arr(i, j, 0);
4218 
4219  if (landtype_arr(i, j, 0) == 15) {
4220  // ice
4221  for (int k = 0; k < d_nz_lsm; k++) {
4222  const int lsm_k = d_khi_lsm - k;
4223  soilw_arr(i, j, lsm_k) = zero;
4224  }
4225  } else {
4226  for (int k = 0; k < d_nz_lsm; k++) {
4227  const int lsm_k = d_khi_lsm - k;
4228  dsw_vars(i, j, lsm_k, SLM_DSW::sdepth_mm) = s_depth_arr(i, j, lsm_k) * 1.0e3;
4229  }
4230  const amrex::Real ground_flux = evp_soil_arr(i, j, 0);
4231  const amrex::Real pond_evaporation = std::min(std::max(amrex::Real(zero), ground_flux),
4232  std::max(amrex::Real(zero), mws_arr(i, j, 0)) / dt);
4233  const amrex::Real soil_evaporation =
4234  std::max(amrex::Real(zero), ground_flux - pond_evaporation);
4235  mws_arr(i, j, 0) = std::max(amrex::Real(zero),
4236  mws_arr(i, j, 0) - pond_evaporation * dt
4237  - std::min(amrex::Real(zero), ground_flux) * dt);
4238 
4239  // Calculate precipitation infiltration rate into the first soil layer
4240  bool any_less_than_one = false;
4241  for (int k = 0; k < d_nz_lsm; k++) {
4242  const int lsm_k = d_khi_lsm - k;
4243  if(soilw_arr(i, j, lsm_k) < one) {
4244  any_less_than_one = true;
4245  break;
4246  }
4247  }
4248  if (any_less_than_one && soilt_arr(i, j, d_khi_lsm) >= tfriz)
4249  {
4250  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));
4251  } else {
4252  precip_in = zero;
4253  }
4254  // calculate diffusion coefficient and velocity for soil moisture transfer
4255  // at each interfacial layer(= between adjacent soil layers)
4256  for (int k = 0; k < d_nz_lsm - 1; k++) {
4257  const int lsm_k = d_khi_lsm - k;
4258 
4259  if (soilt_arr(i, j, lsm_k) >= tfriz && soilt_arr(i, j, lsm_k-1) >= tfriz)
4260  {
4261  const amrex::Real sd_lo = dsw_vars(i, j, lsm_k - 1, SLM_DSW::sdepth_mm);
4262  const amrex::Real sd = dsw_vars(i, j, lsm_k , SLM_DSW::sdepth_mm);
4263  const amrex::Real dz = sd + sd_lo;
4264 
4265  dsw_vars(i, j, lsm_k, SLM_DSW::sh_eff_cond) =
4266  (sd * std::pow(
4267  soilw_arr(i, j, lsm_k),
4268  Bconst_arr(i, j, lsm_k) + two) +
4269  sd_lo * std::pow(
4270  soilw_arr(i, j, lsm_k - 1),
4271  Bconst_arr(i, j, lsm_k) + two)) /
4272  dz * ks_arr(i, j, lsm_k) * Bconst_arr(i, j, lsm_k) *
4273  std::abs(m_pot_sat_arr(i, j, lsm_k)) /
4274  poro_soil_arr(i, j, lsm_k);
4275 
4276  dsw_vars(i, j, lsm_k, SLM_DSW::sh_eff_vel) =
4277  (sd * std::pow(
4278  soilw_arr(i, j, lsm_k),
4279  two * Bconst_arr(i, j, lsm_k) + two) +
4280  sd_lo * std::pow(
4281  soilw_arr(i, j, lsm_k - 1),
4282  two * Bconst_arr(i, j, lsm_k) + two)) /
4283  dz * ks_arr(i, j, lsm_k) / poro_soil_arr(i, j, lsm_k);
4284  } else {
4285  // no water movement between two layers, one of which is frozen
4286  dsw_vars(i, j, lsm_k, SLM_DSW::sh_eff_cond) = zero;
4287  dsw_vars(i, j, lsm_k, SLM_DSW::sh_eff_vel) = zero;
4288  }
4289  }
4290 
4291  // make saturated soil for wetlands
4292  if (landtype_arr(i, j, 0) == 11)
4293  {
4294  for (int k = 0; k < d_nz_lsm; k++) {
4295  const int lsm_k = d_khi_lsm - k;
4296  soilw_arr(i, j, lsm_k) = w_s_FC_arr(i, j, lsm_k);
4297  }
4298  }
4299 
4300  // from FDE by the implicit method, Thomas algorithm is applied
4301  // aa: terms related with soilw(k-1)
4302  // bb: terms related with soilw(k)
4303  // cc: terms related with soilw(k+1)
4304  // dd: current soil wetness - sink + source
4305  for (int k = 0; k < d_nz_lsm; k++) {
4306  const int lsm_k = d_khi_lsm - k;
4307  if (soilw_arr(i, j, lsm_k) < w_s_WP_arr(i, j, lsm_k))
4308  {
4309  dsw_vars(i, j, lsm_k, SLM_DSW::sw_wgt) = zero;
4310  } else {
4311  dsw_vars(i, j, lsm_k, SLM_DSW::sw_wgt) = one;
4312  }
4313  }
4314  aa = zero;
4315  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)));
4316  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);
4317  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);
4318 
4319  dsw_vars(i, j, d_khi_lsm, SLM_DSW::alpha) = cc / bb;
4320  dsw_vars(i, j, d_khi_lsm, SLM_DSW::beta) = dd / bb;
4321 
4322  // For 2-(nsoil-1) layer:
4323  for (int k = 1; k < d_nz_lsm - 1; k++) {
4324  // NOTE: SLM k-1 indices here are switched from original SAM
4325  // version because our soil level indices are from -1 (top) to
4326  // -nsoil (bottom)
4327  // Index mapping:
4328  // SAM SLM -> ERF SLM
4329  // 1 -> d_khi_lsm
4330  // k - 1 -> k + 1
4331  // k + 1 -> k - 1
4332  // nsoil -> d_klo_lsm
4333  const int lsm_k = d_khi_lsm - k; // lsm_k = -2 to -nsoil+1
4334 
4335  const amrex::Real sd_lo = dsw_vars(i, j, lsm_k - 1, SLM_DSW::sdepth_mm);
4336  const amrex::Real sd = dsw_vars(i, j, lsm_k , SLM_DSW::sdepth_mm);
4337  const amrex::Real sd_up = dsw_vars(i, j, lsm_k + 1, SLM_DSW::sdepth_mm);
4338 
4339  const amrex::Real sh_eff_cond = dsw_vars(i, j, lsm_k , SLM_DSW::sh_eff_cond);
4340  const amrex::Real sh_eff_cond_up = dsw_vars(i, j, lsm_k + 1, SLM_DSW::sh_eff_cond);
4341 
4342  const amrex::Real sh_eff_vel = dsw_vars(i, j, lsm_k , SLM_DSW::sh_eff_vel);
4343  const amrex::Real sh_eff_vel_up = dsw_vars(i, j, lsm_k + 1, SLM_DSW::sh_eff_vel);
4344 
4345  aa = -one * dt / sd * (sh_eff_vel_up + sh_eff_cond_up * two / (sd_up + sd));
4346  cc = -one * dt / sd * two * sh_eff_cond / (sd + sd_lo);
4347  bb = one - cc + dt / sd * sh_eff_vel + two * dt / sd * sh_eff_cond_up / (sd_up + sd);
4348  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
4349 
4350  dsw_vars(i, j, lsm_k, SLM_DSW::alpha) = cc / (bb - aa * dsw_vars(i, j, lsm_k + 1, SLM_DSW::alpha));
4351  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));
4352  }
4353 
4354  // For bottom layer:
4355  aa = -1.0 * dt / dsw_vars(i, j, d_klo_lsm, SLM_DSW::sdepth_mm) *
4356  (dsw_vars(i, j, d_klo_lsm + 1, SLM_DSW::sh_eff_vel) +
4357  dsw_vars(i, j, d_klo_lsm + 1, SLM_DSW::sh_eff_cond) * two /
4358  (dsw_vars(i, j, d_klo_lsm + 1, SLM_DSW::sdepth_mm) +
4359  dsw_vars(i, j, d_klo_lsm, SLM_DSW::sdepth_mm)));
4360  cc = zero;
4361  bb =
4362  one + (dt / dsw_vars(i, j, d_klo_lsm, SLM_DSW::sdepth_mm) * two *
4363  dsw_vars(i, j, d_klo_lsm + 1, SLM_DSW::sh_eff_cond) /
4364  (dsw_vars(i, j, d_klo_lsm + 1, SLM_DSW::sdepth_mm) +
4365  dsw_vars(i, j, d_klo_lsm, SLM_DSW::sdepth_mm)));
4366 
4367  // drainage when it exceeds 1.0 mm/s
4368  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;
4369  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);
4370 
4371  slm_diag_arr(i, j, 0, SLM_Diag::drain_flux) = drainage_flux;
4372 
4373  dsw_vars(i, j, d_klo_lsm, SLM_DSW::alpha) = zero;
4374  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));
4375 
4376  // (n+1) time step soil wetness:
4377  soilw_arr(i, j, d_klo_lsm) = dsw_vars(i, j, d_klo_lsm, SLM_DSW::beta);
4378  for (int k = d_nz_lsm - 2; k >= 0; k--) {
4379  const int lsm_k = d_khi_lsm - k;
4380  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));
4381  }
4382 
4383  bool fix = false;
4384  for (int k = 0; k < d_nz_lsm; k++) {
4385  const int lsm_k = d_khi_lsm - k;
4386  if (soilw_arr(i, j, lsm_k) > one)
4387  {
4388  fix = true;
4389  break;
4390  }
4391  }
4392 
4393  amrex::Real excess_water = zero;
4394  if (fix) {
4395  // fix the levels where wetness exceeds 1 preserving total water:
4396  // compute the excess
4397  for (int kk = 0; kk < d_nz_lsm; kk++) {
4398  const int lsm_kk = d_khi_lsm - kk;
4399  if (soilw_arr(i, j, lsm_kk) > one)
4400  {
4401  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);
4402  soilw_arr(i, j, lsm_kk) = one;
4403  }
4404  }
4405 
4406  // distribute the excess among layer into deepest layers first:
4407  for (int kk = d_nz_lsm - 1; kk >= 0; kk--) {
4408  const int lsm_kk = d_khi_lsm - kk;
4409  if (soilw_arr(i, j, lsm_kk) < one)
4410  {
4411  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));
4412  soilw_arr(i, j, lsm_kk) += cc / (poro_soil_arr(i, j, lsm_kk)*dsw_vars(i, j, lsm_kk, SLM_DSW::sdepth_mm));
4413  excess_water -= cc;
4414  if (excess_water <= zero)
4415  {
4416  break;
4417  }
4418  }
4419  }
4420  }
4421  // fixing the issue with rain infiltration even if soil is completely saturated
4422  // move all the access to mws and modify the precip_in accordinally
4423  if (excess_water > zero) {
4424  // still some water left after saturating all the soil layers
4425  // modify the precip_in so that the access of water is moved
4426  // to the surface water
4427  precip_in -= excess_water/dt;
4428  }
4429  slm_diag_arr(i, j, 0, SLM_Diag::precip_in) = precip_in;
4430  mws_arr(i, j, 0) = std::max(amrex::Real(zero), mws_arr(i, j, 0) + (precip_sfc_loc - precip_in)*dt);
4431 
4433  if(mws_arr(i, j, 0) > mws_mx_arr(i, j, 0))
4434  {
4435  drain = (mws_arr(i, j, 0) - mws_mx_arr(i, j, 0))/dt;
4436  mws_arr(i, j, 0) = mws_mx_arr(i, j, 0);
4437  }
4438  else
4439  {
4440  drain = zero;
4441  }
4442  slm_diag_arr(i, j, 0, SLM_Diag::drain) = drain;
4443 
4444  }
4445  });
4446 }
@ 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)
3886 {
3887  const int d_khi_lsm = khi_lsm;
3888  const Real dt = m_dt;
3889 
3890  auto box = mfi.tilebox();
3891 
3892  auto landmask_arr = landmask.const_array(mfi);
3893  auto landtype_arr = landtype.const_array(mfi);
3894 
3895  auto IMPERV_arr = IMPERV.const_array(mfi);
3896 
3897  auto q_cas_arr = q_cas.const_array(mfi);
3898  auto t_cas_arr = t_cas.const_array(mfi);
3899  auto t_ground_skin_arr = t_ground_skin.array(mfi);
3900 
3901  auto soilt_arr = lsm_fab_vars[LsmVar_SLM::soilt]->const_array(mfi);
3902  auto soilw_arr = lsm_fab_vars[LsmVar_SLM::soilw]->const_array(mfi);
3903  auto pref_arr = lsm_fab_vars[LsmVar_SLM::pref]->const_array(mfi);
3904  auto s_depth_arr = lsm_fab_vars[LsmVar_SLM::s_depth]->const_array(mfi);
3905  auto sst_cond_arr = lsm_fab_vars[LsmVar_SLM::sst_cond]->const_array(mfi);
3906  auto poro_soil_arr = lsm_fab_vars[LsmVar_SLM::poro_soil]->const_array(mfi);
3907 
3908  auto lhf_canop_arr = lhf_canop.array(mfi);
3909  auto lhf_soil_arr = lhf_soil.array(mfi);
3910  auto lhf_air_arr = lhf_air.array(mfi);
3911 
3912  auto evp_canop_arr = evp_canop.array(mfi);
3913  auto evp_soil_arr = evp_soil.array(mfi);
3914  auto evp_air_arr = evp_air.array(mfi);
3915 
3916  auto shf_soil_arr = shf_soil.array(mfi);
3917  auto shf_canop_arr = shf_canop.array(mfi);
3918  auto shf_air_arr = shf_air.array(mfi);
3919 
3920  auto t_sfc_arr = t_sfc.array(mfi);
3921  auto q_sfc_arr = q_sfc.array(mfi);
3922 
3923  auto q_gr_arr = q_gr.array(mfi);
3924  auto sdew_arr = sdew.array(mfi);
3925 
3926  auto vegetype_arr = vegetype.const_array(mfi);
3927  auto mws_arr = mws.const_array(mfi);
3928 
3929  auto dref_arr = lsm_fab_vars[LsmVar_SLM::dref]->const_array(mfi);
3930  auto qr_arr = lsm_fab_vars[LsmVar_SLM::qref]->const_array(mfi);
3931  auto tr_arr = lsm_fab_vars[LsmVar_SLM::tref]->const_array(mfi);
3932  auto m_pot_sat_arr = lsm_fab_vars[LsmVar_SLM::m_pot_sat]->const_array(mfi);
3933  auto Bconst_arr = lsm_fab_vars[LsmVar_SLM::Bconst]->const_array(mfi);
3934  auto w_s_WP_arr = lsm_fab_vars[LsmVar_SLM::w_s_WP]->const_array(mfi);
3935  auto soil_transp_frac_arr = lsm_fab_vars[LsmVar_SLM::soil_transp_frac]->const_array(mfi);
3936  auto t_canop_arr = t_canop.const_array(mfi);
3937 
3938  auto r_a_arr = r_a.const_array(mfi);
3939  auto r_d_arr = r_d.const_array(mfi);
3940  auto r_soil_arr = r_soil.array(mfi);
3941  auto evapo_dry_arr = evapo_dry.array(mfi);
3942 
3943  auto slm_diag_arr = slm_diag.array(mfi);
3944  auto net_rad_arr = net_rad.array(mfi);
3945  auto t_skin_arr = t_skin.array(mfi);
3946  auto lwref_arr = lsm_fab_vars[LsmVar_SLM::lwref]->const_array(mfi);
3947  auto LAI_arr = LAI.const_array(mfi);
3948  auto SAI_arr = SAI.const_array(mfi);
3949 
3950  const amrex::Real d_eg_soil = eg_soil_rad;
3951  constexpr int max_iterations = 5;
3952  constexpr amrex::Real derivative_step = 0.1;
3953  constexpr amrex::Real correction_limit = 5.0;
3954  constexpr amrex::Real convergence_tolerance = 0.01;
3955  const amrex::Real d_rsurf_exp = rsurf_exp;
3956 
3957 
3958  ParallelFor( box, [=] AMREX_GPU_DEVICE (int i, int j, int)
3959  {
3960  if (landmask_arr(i, j, 0) != 1) {
3961  return;
3962  }
3963 
3964  // SAM rhow[nz] = air density at vertical velocity levels, kg/m^3
3965  const amrex::Real rhow = dref_arr(i, j, 0); // TODO: double check this
3966  const bool vegetated = vegetype_arr(i, j, 0) == 1;
3967  const amrex::Real q_air = vegetated ? q_cas_arr(i, j, 0) : qr_arr(i, j, 0);
3968  const amrex::Real top_soil_water =
3969  std::max(amrex::Real(zero), soilw_arr(i, j, d_khi_lsm)) *
3970  poro_soil_arr(i, j, d_khi_lsm) *
3971  s_depth_arr(i, j, d_khi_lsm) * 1.0e3;
3972  const amrex::Real transpiration_top =
3973  std::max(amrex::Real(zero), soil_transp_frac_arr(i, j, d_khi_lsm)) *
3974  std::max(amrex::Real(zero), evapo_dry_arr(i, j, 0)) * dt;
3975  const amrex::Real available_ground_water =
3976  std::max(amrex::Real(zero), mws_arr(i, j, 0)) +
3977  std::max(amrex::Real(zero), top_soil_water - transpiration_top);
3978  const amrex::Real max_ground_evaporation = available_ground_water / dt;
3979 
3980  /* Legacy SLM soil diffusion and resistance formulation:
3981  amrex::Real q_ground_guess;
3982  if (t_ground_skin_arr(i, j, 0) >= tfriz) {
3983  erf_qsatw(t_ground_skin_arr(i, j, 0), pref_arr(i, j, 0), q_ground_guess);
3984  if (mws_arr(i, j, 0) == zero) {
3985  q_ground_guess *= fh_calc(t_ground_skin_arr(i, j, 0),
3986  m_pot_sat_arr(i, j, d_khi_lsm),
3987  soilw_arr(i, j, d_khi_lsm),
3988  Bconst_arr(i, j, d_khi_lsm));
3989  }
3990  } else {
3991  erf_qsati(t_ground_skin_arr(i, j, 0), pref_arr(i, j, 0), q_ground_guess);
3992  }
3993  amrex::Real soil_diff;
3994  if (soilw_arr(i, j, d_khi_lsm) >= w_s_FC_arr(i,j,d_khi_lsm) ||
3995  q_air > q_ground_guess) {
3996  soil_diff = one;
3997  } else {
3998  soil_diff = fourth * std::pow(one - std::cos(PI *
3999  std::max(amrex::Real(0.01), soilw_arr(i, j, d_khi_lsm)) /
4000  w_s_FC_arr(i, j, d_khi_lsm)), 2);
4001  }
4002  amrex::Real totalR_soil;
4003  if (!vegetated) {
4004  r_soil_arr(i, j, 0) = std::min(amrex::Real(10000.0), std::max(amrex::Real(100.0),
4005  r_a_arr(i, j, 0) * (1.0 / soil_diff - 1.0)));
4006  totalR_soil = r_soil_arr(i, j, 0) + r_a_arr(i, j, 0);
4007  } else {
4008  r_soil_arr(i, j, 0) = std::min(amrex::Real(10000.0), std::max(amrex::Real(50.0),
4009  r_d_arr(i, j, 0) * (1.0 / soil_diff - 1.0)));
4010  totalR_soil = r_soil_arr(i, j, 0) + r_d_arr(i, j, 0) + r_litter;
4011  }
4012  */
4013 
4014  // NoahMP option 1 (Sakaguchi and Zeng, 2009) ground resistance.
4015  const amrex::Real soil_wetness = std::min(amrex::Real(one),
4016  std::max(amrex::Real(zero), soilw_arr(i, j, d_khi_lsm)));
4017  const amrex::Real dry_soil_thickness = s_depth_arr(i, j, d_khi_lsm)
4018  * (std::exp(std::pow(one - soil_wetness, d_rsurf_exp)) - one)
4019  / (std::exp(one) - one);
4020  const amrex::Real soil_b = Bconst_arr(i, j, d_khi_lsm);
4021  const amrex::Real vapor_diffusivity = soil_b > zero
4022  ? 2.2e-5 * poro_soil_arr(i, j, d_khi_lsm)
4023  * poro_soil_arr(i, j, d_khi_lsm)
4024  * std::pow(one - w_s_WP_arr(i, j, d_khi_lsm),
4025  two + three / soil_b)
4026  : 0.0;
4027  amrex::Real soil_resistance = dry_soil_thickness
4028  / std::max(vapor_diffusivity, amrex::Real(1.0e-12));
4029  const amrex::Real theta_liq = soilw_arr(i, j, d_khi_lsm)
4030  * poro_soil_arr(i, j, d_khi_lsm);
4031  if (theta_liq < 0.01 || landtype_arr(i, j, 0) == 13 || soil_b <= zero) {
4032  soil_resistance = 1.0e6;
4033  }
4034  r_soil_arr(i, j, 0) = soil_resistance;
4035 
4036  const amrex::Real aerodynamic_resistance = vegetated
4037  ? r_d_arr(i, j, 0) : r_a_arr(i, j, 0);
4038  const amrex::Real totalR_soil = aerodynamic_resistance + soil_resistance
4039  + (vegetated ? r_litter : 0.0);
4040 
4041  amrex::Real k_dry, k_sat, conductivity;
4042  if (landtype_arr(i, j, 0) == 15) {
4043  conductivity = 1.6;
4044  } else {
4045  const amrex::Real dry_density = (1.0 - poro_soil_arr(i, j, d_khi_lsm)) * 2700.0;
4046  k_dry = (0.135 * dry_density + 64.7) / (2700.0 - 0.947 * dry_density);
4047  k_sat = std::pow(sst_cond_arr(i, j, d_khi_lsm), one - poro_soil_arr(i, j, d_khi_lsm));
4048  k_sat *= std::pow(soilt_arr(i, j, d_khi_lsm) > tfriz ? amrex::Real(0.57) : amrex::Real(1.60),
4049  poro_soil_arr(i, j, d_khi_lsm));
4050  const amrex::Real ke = std::log10(std::max(amrex::Real(0.1), soilw_arr(i, j, d_khi_lsm))) + one;
4051  conductivity = ke * (k_sat - k_dry) + k_dry;
4052  }
4053  const amrex::Real conduction_coeff = two * conductivity / s_depth_arr(i, j, d_khi_lsm);
4054  const amrex::Real emv = one - std::exp(-(LAI_arr(i, j, 0) + SAI_arr(i, j, 0)));
4055  const amrex::Real emg = d_eg_soil;
4056  const amrex::Real net_sw = net_rad_arr(i, j, 0, SLM_NetRad::net_sw2);
4057  const amrex::Real lwdn = lwref_arr(i, j, 0);
4058  const amrex::Real tv = t_canop_arr(i, j, 0);
4059  const amrex::Real tsoil = soilt_arr(i, j, d_khi_lsm);
4060  const amrex::Real potential_factor = std::pow(amrex::Real(1000.0) / pref_arr(i, j, 0), R_d / Cp_d);
4061 
4062  auto residual = [=] AMREX_GPU_DEVICE (amrex::Real tg) noexcept {
4063  amrex::Real q_ground;
4064  amrex::Real dew_factor = one;
4065  if (tg >= tfriz) {
4066  erf_qsatw(tg, pref_arr(i, j, 0), q_ground);
4067  if (mws_arr(i, j, 0) == zero) {
4068  const amrex::Real humidity_factor = fh_calc(tg,
4069  m_pot_sat_arr(i, j, d_khi_lsm), soilw_arr(i, j, d_khi_lsm),
4070  Bconst_arr(i, j, d_khi_lsm));
4071  dew_factor = humidity_factor > 0.99 ? one : zero;
4072  q_ground *= humidity_factor;
4073  }
4074  } else {
4075  erf_qsati(tg, pref_arr(i, j, 0), q_ground);
4076  }
4077  const amrex::Real sensible = vegetated
4078  ? (tg - t_cas_arr(i, j, 0)) * rhow * cp / r_d_arr(i, j, 0)
4079  : (tg - tr_arr(i, j, 0)) * potential_factor * rhow * cp / r_a_arr(i, j, 0);
4080  amrex::Real evaporation = (q_ground - q_air) * rhow / totalR_soil
4081  * (1.0 - IMPERV_arr(i, j, 0));
4082  if (evaporation > zero) {
4083  evaporation = std::min(evaporation, max_ground_evaporation);
4084  }
4085  if (evaporation < zero) evaporation *= dew_factor;
4086  const amrex::Real irg = emg * sigma * amrex::Math::powi<4>(tg)
4087  - emg * (1.0 - emv) * lwdn
4088  - emg * emv * sigma * amrex::Math::powi<4>(tv);
4089  return net_sw - irg - sensible - lcond * evaporation
4090  - conduction_coeff * (tg - tsoil);
4091  };
4092 
4093  amrex::Real tg = t_ground_skin_arr(i, j, 0);
4094  for (int iter = 0; iter < max_iterations; ++iter) {
4095  const amrex::Real f = residual(tg);
4096  const amrex::Real fprime = (residual(tg + derivative_step)
4097  - residual(tg - derivative_step)) / (two * derivative_step);
4098  AMREX_ALWAYS_ASSERT(std::isfinite(f) && std::isfinite(fprime));
4099  if (std::abs(fprime) < 1.0e-12) break;
4100  const amrex::Real delta = std::max(-correction_limit,
4101  std::min(correction_limit, -f / fprime));
4102  tg += delta;
4103  if (std::abs(delta) < convergence_tolerance) {
4104  break;
4105  }
4106  }
4107 
4108  amrex::Real q_ground;
4109  amrex::Real dew_factor = one;
4110  if (tg >= tfriz) {
4111  erf_qsatw(tg, pref_arr(i, j, 0), q_ground);
4112  if (mws_arr(i, j, 0) == zero) {
4113  const amrex::Real humidity_factor = fh_calc(tg,
4114  m_pot_sat_arr(i, j, d_khi_lsm), soilw_arr(i, j, d_khi_lsm),
4115  Bconst_arr(i, j, d_khi_lsm));
4116  dew_factor = humidity_factor > 0.99 ? one : zero;
4117  q_ground *= humidity_factor;
4118  }
4119  } else {
4120  erf_qsati(tg, pref_arr(i, j, 0), q_ground);
4121  }
4122  const amrex::Real sensible = vegetated
4123  ? (tg - t_cas_arr(i, j, 0)) * rhow * cp / r_d_arr(i, j, 0)
4124  : (tg - tr_arr(i, j, 0)) * potential_factor * rhow * cp / r_a_arr(i, j, 0);
4125  amrex::Real evaporation = (q_ground - q_air) * rhow / totalR_soil
4126  * (1.0 - IMPERV_arr(i, j, 0));
4127  if (evaporation > zero) {
4128  evaporation = std::min(evaporation, max_ground_evaporation);
4129  }
4130  if (evaporation < zero) evaporation *= dew_factor;
4131  const amrex::Real irg = emg * sigma * amrex::Math::powi<4>(tg)
4132  - emg * (1.0 - emv) * lwdn
4133  - emg * emv * sigma * amrex::Math::powi<4>(tv);
4134  const amrex::Real ground_conduction = conduction_coeff * (tg - tsoil);
4135 
4136  t_ground_skin_arr(i, j, 0) = tg;
4137  q_gr_arr(i, j, 0) = q_ground;
4138  sdew_arr(i, j, 0) = dew_factor;
4139  shf_soil_arr(i, j, 0) = sensible;
4140  evp_soil_arr(i, j, 0) = evaporation;
4141  lhf_soil_arr(i, j, 0) = lcond * evaporation;
4142  t_sfc_arr(i, j, 0) = vegetated ? t_cas_arr(i, j, 0) : tg;
4143  q_sfc_arr(i, j, 0) = vegetated ? q_cas_arr(i, j, 0) : q_ground;
4144  shf_air_arr(i, j, 0) = vegetated ? shf_canop_arr(i, j, 0) + sensible : sensible;
4145  evp_air_arr(i, j, 0) = vegetated ? evp_canop_arr(i, j, 0) + evaporation : evaporation;
4146  lhf_air_arr(i, j, 0) = vegetated ? lhf_canop_arr(i, j, 0) + lcond * evaporation
4147  : lcond * evaporation;
4148 
4149  const amrex::Real tir1 = emv * sigma * amrex::Math::powi<4>(tv);
4150  const amrex::Real tir2 = emg * sigma * amrex::Math::powi<4>(tg);
4151  const amrex::Real lwdn2 = (1.0 - emv) * lwdn + tir1;
4152  const amrex::Real lwup2 = tir2 + (1.0 - emg) * lwdn2;
4153  const amrex::Real irc = -emv * (1.0 + (1.0 - emv) * (1.0 - emg)) * lwdn
4154  - emv * emg * sigma * amrex::Math::powi<4>(tg)
4155  + (two - emv * (one - emg)) * emv * sigma * amrex::Math::powi<4>(tv);
4156  const amrex::Real lwup1 = lwdn + irc + irg;
4157  net_rad_arr(i, j, 0, SLM_NetRad::tir2) = tir2;
4158  net_rad_arr(i, j, 0, SLM_NetRad::net_lwup2) = lwup2;
4159  net_rad_arr(i, j, 0, SLM_NetRad::net_lwdn2) = lwdn2;
4160  net_rad_arr(i, j, 0, SLM_NetRad::net_lw2) = -irg;
4161  net_rad_arr(i, j, 0, SLM_NetRad::net_rad2) = net_sw - irg;
4162  net_rad_arr(i, j, 0, SLM_NetRad::net_lwup1) = lwup1;
4163  const amrex::Real emiss_sfc = emv + emg * (1.0 - emv)
4164  + emv * (1.0 - emv) * (1.0 - emg);
4165  t_skin_arr(i, j, 0) = std::pow(std::max(amrex::Real(1.0e-6),
4166  (lwup1 - (one - emiss_sfc) * lwdn) / (emiss_sfc * sigma)), fourth);
4167 
4168  slm_diag_arr(i, j, 0, SLM_Diag::grflux) = -ground_conduction;
4169  });
4170 }
static constexpr amrex::Real sigma
Definition: ERF_SLM.H:750
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
5516 {
5517  // --------------------------------------------------------------------------------------------------
5518  // input
5519  // MPE !prevents underflow errors if division by zero
5520  // FSUN !sunlit fraction of canopy
5521  // FSHA !shaded fraction of canopy
5522  // ELAI !leaf area, one-sided
5523  // VAI !leaf + stem area, one-sided
5524  // LAISUN !sunlit leaf area index, one-sided
5525  // LAISHA !shaded leaf area index, one-sided
5526  // SOLAD !incoming direct solar radiation (w/m2)
5527  // SOLAI !incoming diffuse solar radiation (w/m2)
5528  // FABD !flux abs by veg (per unit incoming direct flux)
5529  // FABI !flux abs by veg (per unit incoming diffuse flux)
5530  // FTDD !down dir flux below veg (per incoming dir flux)
5531  // FTID !down dif flux below veg (per incoming dir flux)
5532  // FTII !down dif flux below veg (per incoming dif flux)
5533  // ALBGRD !ground albedo (direct)
5534  // ALBGRI !ground albedo (diffuse)
5535  // ALBD !overall surface albedo (direct)
5536  // ALBI !overall surface albedo (diffuse)
5537  // FREVD !overall surface albedo veg (direct)
5538  // FREVI !overall surface albedo veg (diffuse)
5539  // FREGD !overall surface albedo grd (direct)
5540  // FREGI !overall surface albedo grd (diffuse)
5541 
5542  // output
5543  // PARSUN !average absorbed par for sunlit leaves (w/m2)
5544  // PARSHA !average absorbed par for shaded leaves (w/m2)
5545  // SAV !solar radiation absorbed by vegetation (w/m2)
5546  // SAG !solar radiation absorbed by ground (w/m2)
5547  // FSA !total absorbed solar radiation (w/m2)
5548  // FSR !total reflected solar radiation (w/m2)
5549  // FSRV !reflected solar radiation by vegetation
5550  // FSRG !reflected solar radiation by ground
5551 
5552  // ------------------------ local variables ----------------------------------------------------
5553  constexpr int NBAND = 2; //number of solar radiation waveband classes
5554 
5555  amrex::Real abs; //absorbed solar radiation (w/m2)
5556  amrex::Real rnir; //reflected solar radiation [nir] (w/m2)
5557  amrex::Real rvis; //reflected solar radiation [vis] (w/m2)
5558  amrex::Real laifra; //leaf area fraction of canopy
5559  amrex::Real trd; //transmitted solar radiation: direct (w/m2)
5560  amrex::Real tri; //transmitted solar radiation: diffuse (w/m2)
5561  amrex::Real cad[2]; //direct beam absorbed by canopy (w/m2)
5562  amrex::Real cai[2]; //diffuse radiation absorbed by canopy (w/m2)
5563  // ---------------------------------------------------------------------------------------------
5564 
5565  // zero summed solar fluxes
5566 
5567  sag = zero;
5568  sav = zero;
5569  fsa = zero;
5570 
5571  // loop over nband wavebands
5572 
5573  for (int ib = 0; ib < NBAND; ib++) {
5574 
5575  // absorbed by canopy
5576 
5577  cad[ib] = solad[ib]*fabd[ib];
5578  cai[ib] = solai[ib]*fabi[ib];
5579  sav = sav + cad[ib] + cai[ib];
5580  fsa = fsa + cad[ib] + cai[ib];
5581 
5582  // transmitted solar fluxes incident on ground
5583 
5584  trd = solad[ib]*ftdd[ib];
5585  tri = solad[ib]*ftid[ib] + solai[ib]*ftii[ib];
5586 
5587  // solar radiation absorbed by ground surface
5588 
5589  abs = trd*(1.0-albgrd[ib]) + tri*(1.0-albgri[ib]);
5590  sag = sag + abs;
5591  fsa = fsa + abs;
5592  }
5593 
5594  // partition visible canopy absorption to sunlit and shaded fractions
5595  // to get average absorbed par for sunlit and shaded leaves
5596 
5597  laifra = elai / std::max(vai,mpe);
5598  if (fsun > zero) {
5599  parsun = (cad[0]+fsun*cai[0]) * laifra / std::max(laisun,mpe);
5600  parsha = (fsha*cai[0])*laifra / std::max(laisha,mpe);
5601  } else {
5602  parsun = zero;
5603  parsha = (cad[0]+cai[0])*laifra / std::max(laisha,mpe);
5604  }
5605 
5606  // reflected solar radiation
5607 
5608  rvis = albd[0]*solad[0] + albi[0]*solai[0];
5609  rnir = albd[1]*solad[1] + albi[1]*solai[1];
5610  fsr = rvis + rnir;
5611 
5612  // reflected solar radiation of veg. and ground (combined ground)
5613  fsrv = frevd[0]*solad[0]+frevi[0]*solai[0]+frevd[1]*solad[1]+frevi[1]*solai[1];
5614  fsrg = fregd[0]*solad[0]+fregi[0]*solai[0]+fregd[1]*solad[1]+fregi[1]*solai[1];
5615 }

◆ transfer_coeff()

void SLM::transfer_coeff ( const amrex::MFIter &  mfi)
3215 {
3216  const int d_khi_lsm = khi_lsm;
3217  const Real dt = m_dt;
3218 
3219  auto box = mfi.tilebox();
3220 
3221  auto landmask_arr = landmask.const_array(mfi);
3222 
3223  auto t_sfc_arr = t_sfc.const_array(mfi);
3224  auto q_sfc_arr = q_sfc.const_array(mfi);
3225 
3226  auto disp_hgt_arr = disp_hgt.const_array(mfi);
3227  auto z0_sfc_arr = z0_sfc.const_array(mfi);
3228 
3229  auto qr_arr = lsm_fab_vars[LsmVar_SLM::qref]->const_array(mfi);
3230 
3231  auto ustar_arr = lsm_fab_vars[LsmVar_SLM::ustar]->array(mfi);
3232  auto tstar_arr = lsm_fab_vars[LsmVar_SLM::tstar]->array(mfi);
3233 
3234  auto tref_arr = lsm_fab_vars[LsmVar_SLM::tref]->const_array(mfi);
3235  auto ur_arr = lsm_fab_vars[LsmVar_SLM::uref]->const_array(mfi);
3236  auto vr_arr = lsm_fab_vars[LsmVar_SLM::vref]->const_array(mfi);
3237  auto pref_arr = lsm_fab_vars[LsmVar_SLM::pref]->const_array(mfi);
3238 
3239  auto r_a_arr = r_a.array(mfi);
3240 
3241  auto zref_arr = zrefxy.array(mfi);
3242 
3243  auto flbu_arr = lsm_fab_vars[LsmVar_SLM::flbu]->array(mfi);
3244  auto flbv_arr = lsm_fab_vars[LsmVar_SLM::flbv]->array(mfi);
3245 
3246  constexpr amrex::Real xsim = -1.574;
3247  constexpr amrex::Real xsih = -0.465;
3248  const amrex::Real xm = std::sqrt(std::sqrt(one - amrex::Real(16.0)*xsim));
3249  const amrex::Real xh = std::sqrt(std::sqrt(one - amrex::Real(16.0)*xsih));
3250 
3251  constexpr amrex::Real errormax = 0.01;
3252  constexpr amrex::Real kk = 0.4;
3253  constexpr int nitermax = 10;
3254 
3255  auto xx = [] AMREX_GPU_DEVICE (const amrex::Real &yy) -> amrex::Real {
3256  return std::sqrt(std::sqrt(one - amrex::Real(16.0) * yy));
3257  };
3258  // unstable: -1.574<xsi<0
3259  auto psim1 = [] AMREX_GPU_DEVICE (const amrex::Real &x, const amrex::Real &x0) -> amrex::Real {
3260  return two * std::log((one + x) / (one + x0)) + std::log((one + x*x) / (one + x0*x0)) - two*(std::atan(x)-std::atan(x0));
3261  };
3262  auto psih1 = [] AMREX_GPU_DEVICE (const amrex::Real &x, const amrex::Real &x0) -> amrex::Real {
3263  return two * std::log((one + x*x) / (one + x0*x0));
3264  };
3265  // very unstable: xsi < -1.574
3266  auto const psim2 = [=] AMREX_GPU_DEVICE (const amrex::Real &xsi, const amrex::Real &xsim0, const amrex::Real &xm0) -> amrex::Real {
3267  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)));
3268  };
3269  auto const psih2 = [=] AMREX_GPU_DEVICE (const amrex::Real &xsi, const amrex::Real &xsih0, const amrex::Real &xh0) -> amrex::Real {
3270  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)));
3271  };
3272  // stable: 0 < xsi < 1
3273  auto psim3 = [] AMREX_GPU_DEVICE (const amrex::Real &xsi, const amrex::Real &xsim0) -> amrex::Real {
3274  return -amrex::Real(5.0) * (xsi - xsim0);
3275  };
3276  auto psih3 = [] AMREX_GPU_DEVICE (const amrex::Real &xsi, const amrex::Real &xsih0) -> amrex::Real {
3277  return -amrex::Real(5.0) * (xsi - xsih0);
3278  };
3279  // very stable: 0 < xsi < 1
3280  auto psim4 = [] AMREX_GPU_DEVICE (const amrex::Real &xsi, const amrex::Real &xsim0) -> amrex::Real {
3281  return std::log(amrex::Math::powi<5>(xsi) / xsim0) + amrex::Real(5.0) * (one - xsim0) + xsi - one;
3282  };
3283  auto psih4 = [] AMREX_GPU_DEVICE (const amrex::Real &xsi, const amrex::Real &xsih0) -> amrex::Real {
3284  return std::log(amrex::Math::powi<5>(xsi) / xsih0) + amrex::Real(5.0) * (one - xsih0) + xsi - one;
3285  };
3286 
3287 
3288  ParallelFor( box, [=] AMREX_GPU_DEVICE (int i, int j, int)
3289  {
3290  if (landmask_arr(i, j, 0) != 1) {
3291  return;
3292  }
3293 
3294  // Inputs:
3295  // ts = t_sfc
3296  // th = tref
3297  // qh = qr
3298  // qs = q_sfc
3299  // h = zref = height of ref level
3300  // z0 = z0_sfc = surface roughness length
3301  // disp = disp_hgt
3302 
3303  amrex::Real tsp = t_sfc_arr(i, j, 0) * std::pow(amrex::Real(1000.0)/pref_arr(i, j, 0), R_d / Cp_d);
3304  amrex::Real thp = tref_arr(i, j, 0) * std::pow(amrex::Real(1000.0) / pref_arr(i, j, 0), R_d / Cp_d);
3305 
3306  amrex::Real vel;
3307  // Add additional velocity depending on the stratification
3308  if ((thp - tsp) >= zero)
3309  {
3310  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))));
3311  }
3312  else
3313  {
3314  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))));
3315  }
3316  const Real d_zref = zref_arr(i,j,0);
3317 
3318  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);
3319  r = std::max(amrex::Real(-10.0), std::min(r, amrex::Real(myhalf))); // cap r for stability of iterations
3320  // initial guess
3321  amrex::Real xsi, fm, fh, xsi1;
3322  amrex::Real xsim0, xsih0;
3323  amrex::Real mom_trans_coef, heat_trans_coef;
3324 
3325  //adjust z0 for high wind - gSAM-SLM
3326  amrex::Real z0_adjusted = z0_sfc_arr(i, j, 0) * std::pow(one + vel/amrex::Real(10.0), amrex::Real(-0.6));
3327  amrex::Real zt0 = 0.135 * z0_adjusted; // roughness length for scalars: ln(z0/zt0)=2 (from Garatte BL textbook, p.93)
3328 
3329  amrex::Real z0h = z0_adjusted / (d_zref - disp_hgt_arr(i, j, 0));
3330  // make sure (h - disp) is not negative, otherwise z0dym,z0dyh become nan
3331  AMREX_ALWAYS_ASSERT(d_zref - disp_hgt_arr(i, j, 0) > zero);
3332  amrex::Real zTh = zt0 / (d_zref - disp_hgt_arr(i, j, 0));
3333 
3334  amrex::Real zodym = std::log(one / z0h);
3335  amrex::Real zodyh = std::log(one / zTh);
3336 
3337  // first guess for xsi
3338  if (r > zero)
3339  {
3340  xsi = r * zodym / (one - amrex::Real(5.0) * std::min(amrex::Real(0.19),r));
3341  }
3342  else
3343  {
3344  xsi = r*zodym;
3345  }
3346 
3347  int niter = 0;
3348  amrex::Real error = 1000.0;
3349 
3350  while (error > errormax && niter < nitermax)
3351  {
3352  xsi1 = xsi;
3353  niter++;
3354  xsim0 = z0h * xsi;
3355  xsih0 = zTh * xsi;
3356 
3357  if (xsi < -0.01)
3358  {
3359  if (xsi >= xsim)
3360  {
3361  fm = zodym - psim1(xx(xsi), xx(xsim0));
3362  }
3363  else
3364  {
3365  fm = psim2(xsi, xsim0, xx(xsim0));
3366  }
3367 
3368  if (xsi >= xsih)
3369  {
3370  fh = zodyh - psih1(xx(xsi), xx(xsih0));
3371  }
3372  else
3373  {
3374  fh = psih2(xsi, xsih0, xx(xsih0));
3375  }
3376  }
3377  else if (xsi > 0.01)
3378  {
3379  if (xsi <= one)
3380  {
3381  fm = zodym - psim3(xsi, xsim0);
3382  fh = zodyh - psih3(xsi, xsih0);
3383  }
3384  else
3385  {
3386  fm = psim4(xsi, xsim0);
3387  fh = psih4(xsi, xsih0);
3388  }
3389  }
3390  else
3391  {
3392  fm = zodym;
3393  fh = zodyh;
3394  }
3395 
3396  xsi = r * fm * fm / fh;
3397  error = std::abs(xsi - xsi1);
3398  }
3399 
3400  // limit fm and fh to avoid too large fluxes especially over large surface roughness.
3401  // Basically, make the maximum slowdown of the velocity not bigger than 50% in one timestep
3402  amrex::Real fm0 = std::sqrt((kk*kk)*vel*dt / myhalf / d_zref);
3403  fm = std::max(fm0, fm);
3404  fh = std::max(fh, fh/fm*fm0);
3405 
3406  // drag coefficient C_D = k**2/fm**2
3407  // heat transfer coefficient C_H = k**2/fm/fh
3408  mom_trans_coef = (kk*kk) / (fm*fm);
3409  heat_trans_coef = (kk*kk) / fm / fh;
3410  ustar_arr(i, j, 0) = std::sqrt(mom_trans_coef) * vel;
3411 
3412  // set ustar > 0.2 to avoid too calm conditions at night for the turbulent transfer
3413  //ustar_arr(i, j, 0) = std::max(0.2, ustar_arr(i, j, 0));
3414  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
3415  tstar_arr(i, j, 0) = -kk * (thp - tsp) / fh;
3416 
3417  // recompute Ch and Cd for onsistency
3418  mom_trans_coef = amrex::Math::powi<2>(ustar_arr(i, j, 0)/vel);
3419  heat_trans_coef = (kk/fh) * (ustar_arr(i, j, 0)/vel);
3420  amrex::ignore_unused(heat_trans_coef);
3421 
3422  // aerodynamic resistance between surface and reference level
3423  r_a_arr(i, j, 0) = fh / kk / ustar_arr(i, j, 0);
3424 
3425 
3426  amrex::Real vel_m = vel;
3427  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));
3428  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));
3429 
3430  // Output variables
3431  flbu_arr(i, j, d_khi_lsm) = taux_sfc;
3432  flbv_arr(i, j, d_khi_lsm) = tauy_sfc;
3433  ustar_arr(i, j, d_khi_lsm) = ustar_arr(i, j, 0);
3434  tstar_arr(i, j, d_khi_lsm) = tstar_arr(i, j, 0);
3435  });
3436 }
static constexpr amrex::Real epsv
Definition: ERF_SLM.H:745
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
5280 {
5281  // --------------------------------------------------------------------------------------------------
5282  // input
5283  // IST !surface type
5284  // IB !waveband number
5285  // IC !0=unit incoming direct; 1=unit incoming diffuse
5286  // VEGTYP !vegetation type
5287  // COSZ !cosine of direct zenith angle (0-1)
5288  // VAI !one-sided leaf+stem area index (m2/m2)
5289  // FWET !fraction of lai, sai that is wetted (-)
5290  // T !surface temperature (k)
5291  // ALBGRD !direct albedo of underlying surface (-)
5292  // ALBGRI !diffuse albedo of underlying surface (-)
5293  // RHO !leaf+stem reflectance
5294  // TAU !leaf+stem transmittance
5295  // FVEG !green vegetation fraction [0.0-1.0]
5296 
5297  // output
5298  // FAB !flux abs by veg layer (per unit incoming flux)
5299  // FRE !flux refl above veg layer (per unit incoming flux)
5300  // FTD !down dir flux below veg layer (per unit in flux)
5301  // FTI !down dif flux below veg layer (per unit in flux)
5302  // GDIR !projected leaf+stem area in solar direction
5303  // FREV !flux reflected by veg layer (per unit incoming flux)
5304  // FREG !flux reflected by ground (per unit incoming flux)
5305  // BGAP !between canopy gap fraction for beam (-)
5306  // WGAP !within canopy gap fraction for beam (-)
5307 
5308  // local
5309  amrex::Real omega; //fraction of intercepted radiation that is scattered
5310  amrex::Real omegal; //omega for leaves
5311  amrex::Real betai; //upscatter parameter for diffuse radiation
5312  amrex::Real betail; //betai for leaves
5313  amrex::Real betad; //upscatter parameter for direct beam radiation
5314  amrex::Real betadl; //betad for leaves
5315  amrex::Real ext; //optical depth of direct beam per unit leaf area
5316  amrex::Real avmu; //average diffuse optical depth
5317  amrex::Real coszi; //0.001 <= cosz <= 1.000
5318  amrex::Real asu; //single scattering albedo
5319  amrex::Real chil; // -0.4 <= xl <= 0.6
5320 
5321  amrex::Real tmp0,tmp1,tmp2,tmp3,tmp4,tmp5,tmp6,tmp7,tmp8,tmp9;
5322  amrex::Real p1,p2,p3,p4,s1,s2,u1,u2,u3;
5323  amrex::Real b,c,d,d1,d2,f,h,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10;
5324  amrex::Real phi1,phi2,sigma_ts;
5325  amrex::Real ftds,ftis,fres;
5326  amrex::Real denfveg;
5327  //jref:start
5328  amrex::Real freveg,frebar;
5329  //jref:end
5330 
5331  // variables for the modified two-stream scheme
5332  // Niu and Yang (2004), JGR
5333  constexpr amrex::Real PAI = 3.14159265;
5334  constexpr amrex::Real TFRZ = 273.16; // freezing/melting point (k)
5335  amrex::Real hd; //crown depth (m)
5336  amrex::Real bb; //vertical crown radius (m)
5337  amrex::Real thetap; //angle conversion from SZA
5338  amrex::Real fa; //foliage volume density (m-1)
5339  amrex::Real newvai; //effective LSAI (-)
5340  // Set below for opt_rad = 1, 2 or 3 (the only values); initialised so that no
5341  // path reads them unset.
5342  amrex::Real kopen = one; //gap fraction for diffue light (-)
5343  amrex::Real gap = one; //total gap fraction for beam ( <=1-shafac )
5344 
5345  // -----------------------------------------------------------------
5346  // compute within and between gaps
5347  if(vai == zero) {
5348  gap = one;
5349  kopen = one;
5350  } else {
5351  if(opt_rad == 1) {
5352  denfveg = -std::log(std::max(one-fveg,amrex::Real(0.01)))/(PAI*rc*rc);
5353  hd = hvt - hvb;
5354  bb = myhalf * hd;
5355  thetap = std::atan(bb/rc * std::tan(std::acos(std::max(amrex::Real(0.01),cosz))) );
5356  // BGAP = EXP(-parameters%DEN * PAI * parameters%RC**2/COS(THETAP) )
5357  bgap = std::exp(-denfveg * PAI * rc*rc/std::cos(thetap) );
5358  fa = vai/(1.33 * PAI * rc*rc*rc *(bb/rc)*denfveg);
5359  newvai = hd*fa;
5360  wgap = (one-bgap) * std::exp(-myhalf*newvai/cosz);
5361  gap = std::min(one-fveg, bgap+wgap);
5362 
5363  kopen = 0.05;
5364  }
5365 
5366  if(opt_rad == 2) {
5367  gap = zero;
5368  kopen = zero;
5369  }
5370 
5371  if(opt_rad == 3) {
5372  gap = 1.0-fveg;
5373  kopen = 1.0-fveg;
5374  }
5375  }
5376 
5377  // calculate two-stream parameters OMEGA, BETAD, BETAI, AVMU, GDIR, EXT.
5378  // OMEGA, BETAD, BETAI are adjusted for snow. values for OMEGA*BETAD
5379  // and OMEGA*BETAI are calculated and then divided by the new OMEGA
5380  // because the product OMEGA*BETAI, OMEGA*BETAD is used in solution.
5381  // also, the transmittances and reflectances (TAU, RHO) are linear
5382  // weights of leaf and stem values.
5383 
5384  coszi = std::max(amrex::Real(0.001), cosz);
5385  chil = std::min( std::max(xl, amrex::Real(-0.4)), amrex::Real(0.6));
5386  if (std::abs(chil) <= 0.01) chil = 0.01;
5387  phi1 = myhalf - 0.633*chil - 0.330*chil*chil;
5388  phi2 = 0.877 * (one-two*phi1);
5389  gdir = phi1 + phi2*coszi;
5390 
5391  // Output diagnostics
5392  xl_out = xl;
5393  chil_out = chil;
5394  phi1_out = phi1;
5395  phi2_out = phi2;
5396  ext = gdir/coszi;
5397  avmu = ( one - phi1/phi2 * std::log((phi1+phi2)/phi1) ) / phi2;
5398  omegal = rho[ib] + tau[ib];
5399  tmp0 = gdir + phi2*coszi;
5400  tmp1 = phi1*coszi;
5401  asu = myhalf*omegal*gdir/tmp0 * ( one-tmp1/tmp0*std::log((tmp1+tmp0)/tmp1) );
5402  betadl = (1.0+avmu*ext)/(omegal*avmu*ext)*asu;
5403  betail = myhalf * ( rho[ib]+tau[ib] + (rho[ib]-tau[ib])
5404  * ((one+chil)/two)*((one+chil)/two) ) / omegal;
5405 
5406  // adjust omega, betad, and betai for intercepted snow
5407 
5408  if (t > TFRZ) { //no snow
5409  tmp0 = omegal;
5410  tmp1 = betadl;
5411  tmp2 = betail;
5412  } else {
5413  tmp0 = (1.0-fwet)*omegal + fwet*omegas_param;
5414  tmp1 = ( (1.0-fwet)*omegal*betadl + fwet*omegas_param*betads ) / tmp0;
5415  tmp2 = ( (1.0-fwet)*omegal*betail + fwet*omegas_param*betais ) / tmp0;
5416  }
5417 
5418  omega = tmp0;
5419  betad = tmp1;
5420  betai = tmp2;
5421 
5422  // absorbed, reflected, transmitted fluxes per unit incoming radiation
5423 
5424  b = 1.0 - omega + omega*betai;
5425  c = omega*betai;
5426  tmp0 = avmu*ext;
5427  d = tmp0 * omega*betad;
5428  f = tmp0 * omega*(1.0-betad);
5429  tmp1 = b*b - c*c;
5430  h = std::sqrt(tmp1) / avmu;
5431  sigma_ts = tmp0*tmp0 - tmp1;
5432  if ( std::abs(sigma_ts) < 1.0e-6 ) sigma_ts = (sigma_ts >= zero) ? 1.0e-6 : -1.0e-6;
5433  p1 = b + avmu*h;
5434  p2 = b - avmu*h;
5435  p3 = b + tmp0;
5436  p4 = b - tmp0;
5437  s1 = std::exp(-h*vai);
5438  s2 = std::exp(-ext*vai);
5439  if (ic == 0) {
5440  u1 = b - c/albgrd[ib];
5441  u2 = b - c*albgrd[ib];
5442  u3 = f + c*albgrd[ib];
5443  } else {
5444  u1 = b - c/albgri[ib];
5445  u2 = b - c*albgri[ib];
5446  u3 = f + c*albgri[ib];
5447  }
5448  tmp2 = u1 - avmu*h;
5449  tmp3 = u1 + avmu*h;
5450  d1 = p1*tmp2/s1 - p2*tmp3*s1;
5451  tmp4 = u2 + avmu*h;
5452  tmp5 = u2 - avmu*h;
5453  d2 = tmp4/s1 - tmp5*s1;
5454  h1 = -d*p4 - c*f;
5455  tmp6 = d - h1*p3/sigma_ts;
5456  tmp7 = ( d - c - h1/sigma_ts*(u1+tmp0) ) * s2;
5457  h2 = ( tmp6*tmp2/s1 - p2*tmp7 ) / d1;
5458  h3 = - ( tmp6*tmp3*s1 - p1*tmp7 ) / d1;
5459  h4 = -f*p3 - c*d;
5460  tmp8 = h4/sigma_ts;
5461  tmp9 = ( u3 - tmp8*(u2-tmp0) ) * s2;
5462  h5 = - ( tmp8*tmp4/s1 + tmp9 ) / d2;
5463  h6 = ( tmp8*tmp5*s1 + tmp9 ) / d2;
5464  h7 = (c*tmp2) / (d1*s1);
5465  h8 = (-c*tmp3*s1) / d1;
5466  h9 = tmp4 / (d2*s1);
5467  h10 = (-tmp5*s1) / d2;
5468 
5469  // downward direct and diffuse fluxes below vegetation
5470  // Niu and Yang (2004), JGR.
5471 
5472  if (ic == 0) {
5473  ftds = s2 *(1.0-gap) + gap;
5474  ftis = (h4*s2/sigma_ts + h5*s1 + h6/s1)*(1.0-gap);
5475  } else {
5476  ftds = zero;
5477  ftis = (h9*s1 + h10/s1)*(1.0-kopen) + kopen;
5478  }
5479  ftd[ib] = ftds;
5480  fti[ib] = ftis;
5481 
5482  // flux reflected by the surface (veg. and ground)
5483 
5484  if (ic == 0) {
5485  fres = (h1/sigma_ts + h2 + h3)*(1.0-gap ) + albgrd[ib]*gap;
5486  freveg = (h1/sigma_ts + h2 + h3)*(1.0-gap );
5487  frebar = albgrd[ib]*gap; //jref - separate veg. and ground reflection
5488  } else {
5489  fres = (h7 + h8) *(1.0-kopen) + albgri[ib]*kopen;
5490  freveg = (h7 + h8) *(1.0-kopen) + albgri[ib]*kopen;
5491  frebar = zero; //jref - separate veg. and ground reflection
5492  }
5493  fre[ib] = fres;
5494 
5495  frev[ib] = freveg;
5496  freg[ib] = frebar;
5497 
5498  // flux absorbed by vegetation
5499 
5500  fab[ib] = 1.0 - fre[ib] - (1.0-albgrd[ib])*ftd[ib]
5501  - (1.0-albgri[ib])*fti[ib];
5502 }
@ omega
Definition: ERF_Morrison.H:55
@ xl
Definition: ERF_WSM6.H:324
@ 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.

457  {
458  this->Copy_State_to_Lsm(cons_in, u_in, v_in);
459  if (first_step) {
461  this->init_wrfinput_vars();
462  } else if (!use_wrfinput) {
463  this->init_slm_vars();
464  }
465  }
466  }
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:4680
Here is the call graph for this function:

◆ Update_State_Vars()

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

Reimplemented from NullSurf.

472  {
473  this->Copy_Lsm_to_State(cons_in);
474  }
void Copy_Lsm_to_State(amrex::MultiFab &cons_in) override
Definition: ERF_SLM.cpp:4911
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.

2715 {
2716  if (interpolate_lai) {
2717  Box box = mfi.tilebox();
2718  box.makeSlab(2, 0);
2719 
2720  auto landmask_arr = landmask.const_array(mfi);
2721  auto landtype_arr = landtype.const_array(mfi);
2722  // auto vegetype_arr = vegetype.const_array(mfi);
2723  auto LAI_arr = LAI.array(mfi);
2724  auto SAI_arr = SAI.array(mfi);
2725 
2726  // Update the day and month
2727  time_t timestamp = time_t(time + start_time);
2728  struct tm *timeinfo = gmtime(&timestamp);
2729 
2730  m_orbital_year = timeinfo->tm_year + 1900;
2731  m_orbital_mon = timeinfo->tm_mon + 1;
2732  m_orbital_day = timeinfo->tm_mday;
2733  m_orbital_sec = timeinfo->tm_hour*3600 + timeinfo->tm_min*60 + timeinfo->tm_sec;
2734 
2735  static constexpr double dpy[] = {0.0 , 31.0, 59.0, 90.0, 120.0, 151.0,
2736  181.0, 212.0, 243.0, 273.0, 304.0, 334.0};
2737  bool leap = (m_orbital_year % 4 == 0 && (!(m_orbital_year % 100 == 0) || (m_orbital_year % 400 == 0))) ? true : false;
2738  m_calday = dpy[m_orbital_mon-1] + (m_orbital_day-1.0) + m_orbital_sec/86400.0;
2739  // add extra day if leap year
2740  if (leap) { m_calday += one; }
2741 
2742  int curr_mon = m_orbital_mon - 1;
2743  int next_mon = curr_mon + 1;
2744  Real t0 = dpy[curr_mon];
2745  Real t1;
2746  if (next_mon >= 11) {
2747  next_mon = 0;
2748  t1 = 365.0;
2749  } else {
2750  t1 = dpy[next_mon];
2751  }
2752 
2753  const Real d_calday = m_calday;
2754 
2755  // Setup device interpolation points
2756  Real *d_lai_curr_ptr = d_lai_curr.data();
2757  Real *d_lai_next_ptr = d_lai_next.data();
2758  Real *d_sai_curr_ptr = d_sai_curr.data();
2759  Real *d_sai_next_ptr = d_sai_next.data();
2760  Gpu::copyAsync(Gpu::hostToDevice, lai_table[curr_mon].data(), lai_table[curr_mon].data()+num_landtypes, d_lai_curr_ptr);
2761  Gpu::copyAsync(Gpu::hostToDevice, lai_table[next_mon].data(), lai_table[next_mon].data()+num_landtypes, d_lai_next_ptr);
2762  Gpu::copyAsync(Gpu::hostToDevice, sai_table[curr_mon].data(), sai_table[curr_mon].data()+num_landtypes, d_sai_curr_ptr);
2763  Gpu::copyAsync(Gpu::hostToDevice, sai_table[next_mon].data(), sai_table[next_mon].data()+num_landtypes, d_sai_next_ptr);
2764  Gpu::streamSynchronize();
2765 
2766  // Update LAI and SAI
2767  ParallelFor(box, [=] AMREX_GPU_DEVICE (int i, int j, int)
2768  {
2769  if (landmask_arr(i, j, 0) == 1) {
2770  //if (vegetype_arr(i, j, 0) == 0) { // todo: check, urban cells are not "vegetated", so LAI is incorrect there?
2771  if (landtype_arr(i,j,0) == 16) {
2772  // baresoil point, so no LAI or SAI
2773  LAI_arr(i,j,0) = zero;
2774  SAI_arr(i,j,0) = zero;
2775  //} else if (vegetype_arr(i, j, 0) == 1) {
2776  } else {
2777  // vegetation
2778  const int ltype = landtype_arr(i,j,0) - 1;
2779 
2780  const Real lai_x = d_lai_curr_ptr[ltype];
2781  const Real lai_y = d_lai_next_ptr[ltype];
2782 
2783  const Real sai_x = d_sai_curr_ptr[ltype];
2784  const Real sai_y = d_sai_next_ptr[ltype];
2785 
2786  // Update the LAI and SAI using interpolated values from the monthly tables
2787  // NOTE: LAI + SAI is computed later when needed
2788  LAI_arr(i,j,0) = linear_interp(t0, t1, d_calday, lai_x, lai_y);
2789  SAI_arr(i,j,0) = linear_interp(t0, t1, d_calday, sai_x, sai_y);
2790  }
2791  }
2792  });
2793 
2794  UpdateLAIParameters(mfi);
2795  } else if (use_wrf_lai) {
2796 
2797  Box box = mfi.tilebox();
2798  box.makeSlab(2, 0);
2799 
2800  auto landmask_arr = landmask.const_array(mfi);
2801  auto landtype_arr = landtype.const_array(mfi);
2802  auto veg_frac_arr = lsm_fab_vars[LsmVar_SLM::veg_frac]->array(mfi);
2803  auto veg_frac_min_arr = lsm_fab_vars[LsmVar_SLM::veg_frac_min]->const_array(mfi);
2804  auto veg_frac_max_arr = lsm_fab_vars[LsmVar_SLM::veg_frac_max]->const_array(mfi);
2805 
2806  auto LAI_arr = LAI.array(mfi);
2807 
2808  auto const& d_params = d_param_table.const_table();
2809 
2810  // Update LAI from vegetation fraction.
2811  ParallelFor(box, [=] AMREX_GPU_DEVICE (int i, int j, int)
2812  {
2813  if (landmask_arr(i, j, 0) == 1) {
2814 
2815  const int ltype = landtype_arr(i,j,0) - 1; // shift by one to match table index (i.e, types 1-20 -> 0-19)
2816 
2817  if (ltype + 1 == 13) { // urban cells
2818  veg_frac_arr(i,j,0) = d_params(ltype,1);
2819  }
2820 
2821  // LAI min = col 8, lai max = col 9
2822  // emiss min = col 10, max = col 11
2823  // albedo min = col 12, max = col 13
2824 
2825  if (veg_frac_arr(i,j,0) >= veg_frac_max_arr(i,j,0)) {
2826  //emis_sfc_arr(i,j,0) = emissmax;
2827  LAI_arr(i,j,0) = d_params(ltype, 9);
2828  } else if (veg_frac_arr(i,j,0) <= veg_frac_min_arr(i,j,0)) {
2829  //emis_sfc_arr(i,j,0) = emissmin;
2830  LAI_arr(i,j,0) = d_params(ltype, 8);
2831  } else {
2832  if (veg_frac_max_arr(i,j,0) > veg_frac_min_arr(i,j,0)) {
2833 
2834  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));
2835  interp_frac = std::min(std::max(interp_frac, amrex::Real(zero)), amrex::Real(one)); // bound between 0.0 and 1.0
2836 
2837  // Scale emissivitiy and LAI between min/max by interp_frac
2838  //emis_sfc_arr(i,j,0) = ( (1.0 - interp_frac) * d_params(ltype, 10)) + interp_frac * d_params(ltype, 11);
2839 
2840  LAI_arr(i,j,0) = ( (1.0 - interp_frac) * d_params(ltype, 8)) + interp_frac * d_params(ltype, 9);
2841  } else {
2842  // emis_sfc_arr(i,j,0) = 0.5 * d_params(ltype,10) + 0.5 * d_params(ltype, 11);
2843  LAI_arr(i,j,0) = myhalf * d_params(ltype, 8) + myhalf * d_params(ltype, 9);
2844  }
2845  }
2846 
2847  }
2848  });
2849 
2850  UpdateLAIParameters(mfi);
2851  }
2852 }
Here is the call graph for this function:

◆ UpdateLAIParameters()

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

Updates radiation parameters related to LAI

2858 {
2859  Box box = mfi.tilebox();
2860  box.makeSlab(2, 0);
2861 
2862  auto IR_emis_vege_arr = IR_emis_vege.array(mfi);
2863  auto phi_1_arr = phi_1.array(mfi);
2864  auto phi_2_arr = phi_2.array(mfi);
2865  auto precip_extinc_arr = precip_extinc.array(mfi);
2866  auto mw_mx_arr = mw_mx.array(mfi);
2867  auto LAI_arr = LAI.array(mfi);
2868  auto BAI_arr = BAI.array(mfi);
2869  auto ztop_arr = ztop.array(mfi);
2870  auto Khai_L_arr = Khai_L.array(mfi);
2871  auto landmask_arr = landmask.const_array(mfi);
2872  auto vegetype_arr = vegetype.const_array(mfi);
2873 
2874  ParallelFor(box, [=] AMREX_GPU_DEVICE (int i, int j, int)
2875  {
2876  if (landmask_arr(i, j, 0) == 1) {
2877  if (vegetype_arr(i, j, 0) == 1) {
2878  // set minimum LAI for vegetated land
2879  LAI_arr(i, j, 0) = std::max(LAI_arr(i, j, 0), amrex::Real(0.001));
2880  }
2881 
2882  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));
2883  phi_2_arr(i, j, 0) = 0.877 * (one - two * phi_1_arr(i, j, 0));
2884  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)));
2885  precip_extinc_arr(i, j, 0) = phi_1_arr(i, j, 0) + phi_2_arr(i, j, 0);
2886  //mw_mx_arr(i, j, 0) = 0.1 * LAI_arr(i, j, 0);
2887  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);
2888  }
2889  });
2890 }
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.

2318 {
2319  using ParameterBlock = SLMParameterTable::mapped_type;
2320 
2321  const std::string soil_dataset_lower = amrex::toLower(soil_dataset);
2322  const std::string veg_dataset_lower = amrex::toLower(veg_dataset);
2323 
2324  std::string soil_param_key;
2325  if (soil_dataset_lower == "stas_ruc") {
2326  soil_param_key = "noahmp_soil_stas_ruc_parameters";
2327  } else if (soil_dataset_lower == "stas") {
2328  soil_param_key = "noahmp_soil_stas_parameters";
2329  } else {
2330  amrex::Abort("SLM: unrecognized soil dataset type '" + soil_dataset + "'");
2331  }
2332 
2333  std::string veg_param_key;
2334  std::string veg_category_key;
2335  if (veg_dataset_lower == "usgs") {
2336  veg_param_key = "noahmp_usgs_parameters";
2337  veg_category_key = "noahmp_usgs_veg_categories";
2338  } else if (veg_dataset_lower == "modis") {
2339  veg_param_key = "noahmp_modis_parameters";
2340  veg_category_key = "noahmp_modis_veg_categories";
2341  } else {
2342  amrex::Abort("SLM: unrecognized vegetation dataset type '" + veg_dataset + "'");
2343  }
2344 
2345  auto require_block = [this](const std::string& block_name) -> const ParameterBlock& {
2346  auto block = full_params.find(block_name);
2347  if (block == full_params.end()) {
2348  amrex::Abort("SLM: parameter file is missing block '" + block_name + "'");
2349  }
2350  return block->second;
2351  };
2352 
2353  auto find_parameter = [](const ParameterBlock& block, const std::string& block_name,
2354  const std::string& parameter_name) -> const Vector<Real>*
2355  {
2356  const Vector<Real>* values = nullptr;
2357  for (const auto& parameter : block) {
2358  if (parameter.first == parameter_name) {
2359  if (values != nullptr) {
2360  amrex::Abort("SLM: parameter '" + parameter_name + "' is duplicated in block '" + block_name + "'");
2361  }
2362  values = &parameter.second;
2363  }
2364  }
2365  return values;
2366  };
2367 
2368  auto require_parameter = [&find_parameter](const ParameterBlock& block, const std::string& block_name,
2369  const std::string& parameter_name, int expected_size) -> const Vector<Real>& {
2370  const Vector<Real>* values = find_parameter(block, block_name, parameter_name);
2371  if (values == nullptr || values->empty()) {
2372  amrex::Abort("SLM: parameter file is missing nonempty parameter '" + parameter_name +
2373  "' in block '" + block_name + "'");
2374  }
2375  if (expected_size >= 0 && static_cast<int>(values->size()) != expected_size) {
2376  amrex::Abort("SLM: parameter '" + parameter_name + "' in block '" + block_name +
2377  "' has " + std::to_string(values->size()) +
2378  " values; expected " + std::to_string(expected_size));
2379  }
2380  return *values;
2381  };
2382 
2383  auto validate_parameter_value = [](const std::string& block_name,
2384  const std::string& parameter_name,
2385  const Real value,
2386  const std::optional<Real> lower = std::nullopt,
2387  const std::optional<Real> upper = std::nullopt,
2388  const bool lower_strict = false)
2389  {
2390  if (!std::isfinite(value) ||
2391  (lower && (lower_strict ? value <= *lower : value < *lower)) ||
2392  (upper && value > *upper)) {
2393  amrex::Abort("SLM: invalid value for parameter '" + parameter_name +
2394  "' in block '" + block_name + "'");
2395  }
2396  };
2397 
2398  const ParameterBlock& veg_categories = require_block(veg_category_key);
2399  const Vector<Real>& nveg_values = require_parameter(veg_categories, veg_category_key, "nveg", 1);
2400  if (nveg_values[0] < one || nveg_values[0] != std::floor(nveg_values[0])) {
2401  amrex::Abort("SLM: parameter 'nveg' in block '" + veg_category_key + "' must be a positive integer");
2402  }
2403  num_veg_params = static_cast<int>(nveg_values[0]);
2404 
2405  const ParameterBlock& veg_params = require_block(veg_param_key);
2406  for (const auto& parameter : veg_params) {
2407  if (parameter.second.empty()) {
2408  amrex::Abort("SLM: parameter '" + parameter.first + "' in block '" + veg_param_key + "' is empty");
2409  }
2410  if (parameter.first != "nveg" && static_cast<int>(parameter.second.size()) > 1 &&
2411  static_cast<int>(parameter.second.size()) != num_veg_params) {
2412  amrex::Abort("SLM: parameter '" + parameter.first + "' in block '" + veg_param_key +
2413  "' has " + std::to_string(parameter.second.size()) +
2414  " values; expected " + std::to_string(num_veg_params) +
2415  " vegetation-category values");
2416  }
2417  std::optional<Real> lower;
2418  std::optional<Real> upper;
2419  if (parameter.first == "rhol_vis" || parameter.first == "rhol_nir" ||
2420  parameter.first == "rhos_vis" || parameter.first == "rhos_nir" ||
2421  parameter.first == "taul_vis" || parameter.first == "taul_nir" ||
2422  parameter.first == "taus_vis" || parameter.first == "taus_nir") {
2423  lower = zero;
2424  upper = one;
2425  } else if (parameter.first == "rs" || parameter.first == "rgl" ||
2426  parameter.first == "nroot" || parameter.first == "hvt" ||
2427  parameter.first == "hvb" || parameter.first == "z0mvt" ||
2428  parameter.first == "cbiom" || parameter.first == "dleaf" ||
2429  parameter.first == "rc" || parameter.first == "den" ||
2430  parameter.first == "cwpvt") {
2431  lower = zero;
2432  }
2433  for (const Real value : parameter.second) {
2434  validate_parameter_value(veg_param_key, parameter.first, value, lower, upper);
2435  }
2436  }
2437 
2438  const Vector<Real>* hvt_values = find_parameter(veg_params, veg_param_key, "hvt");
2439  if (hvt_values != nullptr && static_cast<int>(hvt_values->size()) == num_veg_params) {
2440  const Vector<Real>* hvb_values = find_parameter(veg_params, veg_param_key, "hvb");
2441  if (hvb_values != nullptr && static_cast<int>(hvb_values->size()) == num_veg_params) {
2442  for (int i = 0; i < num_veg_params; ++i) {
2443  if ((*hvt_values)[i] > zero && (*hvt_values)[i] <= (*hvb_values)[i]) {
2444  amrex::Abort("SLM: HVT must be greater than HVB for canopy categories in block '" +
2445  veg_param_key + "'");
2446  }
2447  }
2448  }
2449  const Vector<std::string> canopy_parameters = {
2450  "rgl", "z0mvt", "cwpvt", "dleaf", "rc", "den"
2451  };
2452  for (const std::string& parameter_name : canopy_parameters) {
2453  const Vector<Real>* values = find_parameter(veg_params, veg_param_key, parameter_name);
2454  if (values == nullptr || static_cast<int>(values->size()) != num_veg_params) {
2455  continue;
2456  }
2457  for (int i = 0; i < num_veg_params; ++i) {
2458  if ((*hvt_values)[i] > zero) {
2459  validate_parameter_value(veg_param_key, parameter_name, (*values)[i], 0.0,
2460  std::nullopt, true);
2461  }
2462  }
2463  }
2464  }
2465 
2466  const ParameterBlock& soil_categories = require_block("noahmp_stas_soil_categories");
2467  const Vector<Real>& slcats_values = require_parameter(
2468  soil_categories, "noahmp_stas_soil_categories", "slcats", 1);
2469  if (slcats_values[0] < one || slcats_values[0] != std::floor(slcats_values[0])) {
2470  amrex::Abort("SLM: parameter 'slcats' in block 'noahmp_stas_soil_categories' must be a positive integer");
2471  }
2472  num_soil_params = static_cast<int>(slcats_values[0]);
2473 
2474  const ParameterBlock& soil_params = require_block(soil_param_key);
2475  require_parameter(soil_params, soil_param_key, "maxsmc", num_soil_params);
2476 
2477  const Vector<std::string> required_soil_parameters = {
2478  "maxsmc", "refsmc", "wltsmc", "satpsi", "bb", "satdk"
2479  };
2480  for (const std::string& parameter_name : required_soil_parameters) {
2481  require_parameter(soil_params, soil_param_key, parameter_name, num_soil_params);
2482  }
2483  for (const auto& parameter : soil_params) {
2484  if (parameter.second.empty()) {
2485  amrex::Abort("SLM: parameter '" + parameter.first + "' in block '" + soil_param_key + "' is empty");
2486  }
2487  if (static_cast<int>(parameter.second.size()) > 1 && static_cast<int>(parameter.second.size()) != num_soil_params) {
2488  amrex::Abort("SLM: parameter '" + parameter.first + "' in block '" + soil_param_key +
2489  "' has " + std::to_string(parameter.second.size()) +
2490  " values; expected " + std::to_string(num_soil_params) +
2491  " soil-category values");
2492  }
2493  for (const Real value : parameter.second) {
2494  validate_parameter_value(soil_param_key, parameter.first, value, 0.0);
2495  }
2496  }
2497 
2498  const ParameterBlock& rad_params = require_block("noahmp_rad_parameters");
2499  const Vector<std::pair<std::string, int>> required_rad_parameters = {
2500  {"albsat_vis", 8}, {"albsat_nir", 8},
2501  {"albdry_vis", 8}, {"albdry_nir", 8},
2502  {"alblak", 2}, {"omegas", 2},
2503  {"betads", 1}, {"betais", 1}
2504  };
2505  for (const auto& parameter : required_rad_parameters) {
2506  require_parameter(rad_params, "noahmp_rad_parameters", parameter.first, parameter.second);
2507  }
2508  for (const auto& parameter : rad_params) {
2509  if (parameter.second.empty()) {
2510  amrex::Abort("SLM: parameter '" + parameter.first + "' in block 'noahmp_rad_parameters' is empty");
2511  }
2512  for (const Real value : parameter.second) {
2513  validate_parameter_value("noahmp_rad_parameters", parameter.first, value, 0.0, 1.0);
2514  }
2515  }
2516 
2517  const Vector<std::string> required_veg_parameters = {
2518  "rhol_vis", "rhol_nir", "rhos_vis", "rhos_nir",
2519  "taul_vis", "taul_nir", "taus_vis", "taus_nir",
2520  "rc", "hvb", "den"
2521  };
2522  for (const std::string& parameter_name : required_veg_parameters) {
2523  require_parameter(veg_params, veg_param_key, parameter_name, num_veg_params);
2524  }
2525 }

◆ WriteCheckpoint()

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

Reimplemented from NullSurf.

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

◆ 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 
)
4934 {
4935 #ifndef ERF_USE_NETCDF
4936  amrex::ignore_unused(cur_time, plot_prefix, level_step);
4937 #endif
4938  geom.define(amrex::makeSlab(m_lsm_geom.Domain(), 2, 0), m_lsm_geom.ProbDomain(), m_lsm_geom.Coord(), m_lsm_geom.isPeriodic());
4939 
4940  amrex::Vector<amrex::MultiFab*> mf_data;
4941 
4942  mf_data.push_back(&net_rad);
4943 
4944  mf_data.push_back(&mw);
4945  mf_data.push_back(&mws);
4946  mf_data.push_back(&t_canop);
4947  mf_data.push_back(&t_skin);
4948  mf_data.push_back(&t_ground_skin);
4949  mf_data.push_back(&t_cas);
4950  mf_data.push_back(&q_cas);
4951  mf_data.push_back(&mw_inc);
4952 
4953  mf_data.push_back(&evapo_dry);
4954  mf_data.push_back(&shf_air);
4955  mf_data.push_back(&shf_canop);
4956  mf_data.push_back(&shf_soil);
4957  mf_data.push_back(&lhf_air);
4958  mf_data.push_back(&lhf_canop);
4959  mf_data.push_back(&lhf_soil);
4960 
4961  //mf_data.push_back(&ustar);
4962  //mf_data.push_back(&tstar);
4963 
4964  mf_data.push_back(&r_a);
4965  mf_data.push_back(&r_b);
4966  mf_data.push_back(&r_c);
4967  mf_data.push_back(&r_d);
4968  mf_data.push_back(&r_soil);
4969 
4970  mf_data.push_back(&wet_canop);
4971 
4972  mf_data.push_back(&albedovis_v);
4973  mf_data.push_back(&albedovis_s);
4974  mf_data.push_back(&albedonir_v);
4975  mf_data.push_back(&albedonir_s);
4976  mf_data.push_back(&IR_emis_vege);
4977  mf_data.push_back(&IR_emis_soil);
4978  mf_data.push_back(&zrefxy);
4979  mf_data.push_back(&vege_YES);
4980 
4981  mf_data.push_back(&LAI);
4982  mf_data.push_back(&SAI);
4983 
4984  MultiFab tmp_landtype = amrex::ToMultiFab(landtype);
4985  MultiFab tmp_landmask = amrex::ToMultiFab(landmask);
4986 
4987  mf_data.push_back(&tmp_landtype);
4988  mf_data.push_back(&tmp_landmask);
4989 
4990  IntVect ng(0, 0, 0);
4991 
4992  // Total number of output MFs: net_rad components + mf_data size - 1 + diag vars + olen
4993  const int output_size = SLM_NetRad::NumVars + static_cast<int>(mf_data.size()) - 1 + SLM_Diag::NumVars + 1;
4994  fab.define(ba_lsm_2d, net_rad.DistributionMap(), output_size, ng);
4995  MultiFab::Copy(fab, *(mf_data[0]), 0, 0, SLM_NetRad::NumVars, 0);
4996  for (int i = 1; i < static_cast<int>(mf_data.size()); ++i)
4997  {
4998  MultiFab::Copy(fab, *(mf_data[i]), 0, i + SLM_NetRad::NumVars - 1, 1, 0);
4999  }
5000  MultiFab::Copy(fab, slm_diag, 0, output_size - SLM_Diag::NumVars - 1, SLM_Diag::NumVars, 0);
5001  MultiFab::Copy(fab, *(lsm_fab_flux[LsmFlux_SLM::olen]), 0, output_size - 1, 1, 0);
5002 
5003 
5004  varnames = amrex::Vector<std::string>();
5005  // net_rad component names:
5006  varnames.push_back("net_swup1");
5007  varnames.push_back("net_swup2");
5008  varnames.push_back("net_swdn1");
5009  varnames.push_back("net_swdn2");
5010  varnames.push_back("net_sw1");
5011  varnames.push_back("net_sw2");
5012  varnames.push_back("tir1");
5013  varnames.push_back("tir2");
5014  varnames.push_back("net_lwup1");
5015  varnames.push_back("net_lwup2");
5016  varnames.push_back("net_lwdn1");
5017  varnames.push_back("net_lwdn2");
5018  varnames.push_back("net_lw1");
5019  varnames.push_back("net_lw2");
5020  varnames.push_back("net_rad1");
5021  varnames.push_back("net_rad2");
5022  // ----------------------------
5023 
5024  varnames.push_back("mw");
5025  varnames.push_back("mws");
5026  varnames.push_back("t_canop");
5027  varnames.push_back("t_skin");
5028  varnames.push_back("t_ground_skin");
5029  varnames.push_back("t_cas");
5030  varnames.push_back("q_cas");
5031  varnames.push_back("mw_inc");
5032 
5033  varnames.push_back("evapo_dry");
5034  varnames.push_back("shf_air");
5035  varnames.push_back("shf_canop");
5036  varnames.push_back("shf_soil");
5037  varnames.push_back("lhf_air");
5038  varnames.push_back("lhf_canop");
5039  varnames.push_back("lhf_soil");
5040 
5041  //varnames.push_back("ustar");
5042  //varnames.push_back("tstar");
5043 
5044  varnames.push_back("r_a");
5045  varnames.push_back("r_b");
5046  varnames.push_back("r_c");
5047  varnames.push_back("r_d");
5048  varnames.push_back("r_soil");
5049 
5050  varnames.push_back("wet_canop");
5051 
5052  varnames.push_back("albedovis_veg");
5053  varnames.push_back("albedovis_soil");
5054  varnames.push_back("albedonir_veg");
5055  varnames.push_back("albedonir_soil");
5056  varnames.push_back("IR_emis_veg");
5057  varnames.push_back("IR_emis_soil");
5058  varnames.push_back("zrefxy");
5059  varnames.push_back("veg_flag");
5060 
5061  varnames.push_back("LAI");
5062  varnames.push_back("SAI");
5063 
5064  varnames.push_back("vegtype");
5065  varnames.push_back("landmask");
5066 
5067  for (int i = 0; i < static_cast<int>(diag_names.size()); ++i)
5068  {
5069  varnames.push_back(diag_names[i]);
5070  }
5071 
5072  varnames.push_back("olen");
5073 
5074  AMREX_ALWAYS_ASSERT(static_cast<int>(varnames.size()) == output_size);
5075 
5076  if (plotfile_type == PlotFileType::Amrex) {
5077  //amrex::WriteSingleLevelPlotfile(plotfilename, fab, varnames, lsm_2d_geom, cur_time, level_step);
5078 #ifdef ERF_USE_NETCDF
5079  // Temporarily write NetCDF always
5080  //writeSLM_NetCDF(fab, varnames, cur_time, plot_prefix, level_step);
5081 #endif
5082 #ifdef ERF_USE_NETCDF
5083  } else if (plotfile_type == PlotFileType::Netcdf) {
5084  writeSLM_NetCDF(fab, varnames, cur_time, plot_prefix, level_step);
5085 #endif
5086  } else {
5087  Abort("Dont know this plot_filetype");
5088  }
5089 }
static const std::vector< std::string > diag_names
Definition: ERF_SLM.H:1041
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

◆ hs_rc

amrex::MultiFab SLM::hs_rc
private

◆ IMPERV

amrex::MultiFab SLM::IMPERV
private

◆ 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

◆ m_geom

amrex::Geometry SLM::m_geom
private

Referenced by Lsm_Atmos_Geom().

◆ m_geom0

amrex::Geometry SLM::m_geom0
private

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