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

Interface between ERF and land-surface and urban models. More...

#include <ERF_SurfaceModel.H>

Collaboration diagram for SurfaceModel:

Classes

struct  Field
 Describes a registered land/urban field mapping. More...
 
struct  RadiationField
 

Public Member Functions

 SurfaceModel (int nlevs, const amrex::Vector< amrex::BoxArray > &ba, const amrex::Vector< amrex::Geometry > &geom, const amrex::Vector< amrex::DistributionMapping > &dm, const SolverChoice &, amrex::Vector< amrex::Vector< std::unique_ptr< amrex::iMultiFab >>> &)
 Constructs a surface-model interface for the supplied AMR levels. More...
 
void initialize_for_level (int lev, const amrex::BoxArray &ba, const amrex::Geometry &geom, const amrex::DistributionMapping &dm, const amrex::Vector< std::unique_ptr< amrex::iMultiFab >> &lmask_lev, const amrex::Vector< amrex::BCRec > &domain_bcs_type, const amrex::Vector< amrex::IntVect > &refRatio)
 Initializes storage and boundary data for an AMR level. More...
 
void set_model_data (const int lev, const amrex::Vector< amrex::MultiFab * > model_data, const amrex::Vector< std::string > &data_names, SurfaceModelType type)
 Registers model variables used in weighted averaging. More...
 
void set_model_fluxes (const int lev, const amrex::Vector< amrex::MultiFab * > model_fluxes, const amrex::Vector< std::string > &flux_names, SurfaceModelType type)
 Registers model fluxes as additional weighted-average inputs. More...
 
void set_model_fields (SurfaceModelType type, const amrex::Vector< int > &field_indices, const bool use_fluxes=true)
 Selects the model fields that participate in weighted averaging. More...
 
void apply_weight_average (int lev, const amrex::MultiFab *lsm_data, amrex::MultiFab *lsm_weighted, const amrex::MultiFab *urban_data, amrex::MultiFab *urban_weighted)
 Applies land and urban weights into separate output fields. More...
 
void calculate_weight_average (int lev, amrex::MultiFab *const urban_frac, bool update_derived=true)
 Computes land and urban weights and applies them to registered fields. More...
 
bool are_fluxes ()
 Returns whether the interface exports fluxes rather than MOST variables. More...
 
void request_surface_outputs (const bool persistent=true)
 Enables and materializes the surface outputs consumed by ERF. More...
 
void request_surface_layer_outputs ()
 Enables blended surface outputs needed by the SurfaceLayer. More...
 
void mark_fields_valid ()
 Marks the registered surface fields as filled by an actual model advance. More...
 
bool fields_are_valid () const
 Returns whether the surface models have advanced at least once. More...
 
amrex::MultiFab * get_ustar (const int &lev)
 Returns the weighted horizontal momentum output for a level. More...
 
amrex::MultiFab * get_tstar (const int &lev)
 Returns the weighted thermal output for a level. More...
 
amrex::MultiFab * get_qstar (const int &lev)
 Returns the weighted moisture output for a level. More...
 
amrex::MultiFab * get_tsurf (const int &lev)
 Returns the weighted surface-temperature output for a level. More...
 
SurfaceFluxView get_surface_flux_view (const int lev, const amrex::MFIter &mfi) const
 Returns provider or blended surface-flux views for one tile. More...
 
const amrex::MultiFab * get_weighted_model_data (const int lev, SurfaceModelType type, const int field_idx) const
 Returns weighted data for a selected model field. More...
 
amrex::MultiFab * get_wavg_factors (const int &lev)
 Returns the land and urban weighting factors for a level. More...
 
void activate_all_field_maps (const bool persistent=true)
 Activates all registered mapped fields for an output consumer. More...
 
void write_output (const int &finest_lev, const amrex::Real &time, const std::string &plot_prefix, const amrex::Vector< int > &level_steps, const amrex::Vector< amrex::IntVect > &ref_ratio)
 Writes registered surface-model fields to plotfile output. More...
 
void register_field_map (std::string name, const std::pair< int, int > &lsm_urb_map, bool fill_boundary=false)
 Registers a field map using land and urban field indices. More...
 
void register_field_map (std::string name, amrex::Vector< amrex::MultiFab * > &lsm_lev_mf, amrex::Vector< amrex::MultiFab * > &urb_lev_mf, bool fill_boundary=false)
 Registers a field map using per-level land and urban pointers. More...
 
void set_field_map_pointers (const std::string &name, int lev, amrex::MultiFab *lsm_mf, amrex::MultiFab *urban_mf)
 Updates a pointer-based field mapping for one level. More...
 
void register_radiation_input (const std::string &name, const std::pair< int, int > &lsm_urb_map)
 Registers a canonical radiation input mapping. More...
 
void register_radiation_inputs (const std::unordered_map< std::string, std::pair< int, int >> &input_map)
 Registers canonical radiation input mappings. More...
 
void register_radiation_output (const std::string &name, const std::pair< int, int > &lsm_urb_map)
 Registers a canonical radiation output mapping. More...
 
void register_radiation_outputs (const std::unordered_map< std::string, std::pair< int, int >> &output_map)
 Registers canonical radiation output mappings. More...
 
amrex::MultiFab * get_field (const std::string &name, int lev=0)
 Retrieves a registered field by its common name. More...
 
const amrex::Vector< const amrex::MultiFab * > get_radiation_fields (int lev=0)
 Returns fields registered under the RRTMGP radiation names. More...
 
const amrex::Vector< amrex::MultiFab * > get_radiation_output_fields (int lev=0)
 Returns canonical radiation output destinations for one level. More...
 
amrex::MultiFab * get_radiation_output_field (int lev, const std::string &name)
 Returns the destination of one canonical radiation output by name. More...
 
void distribute_radiation_outputs (int lev)
 Distributes updated canonical radiation outputs to other providers. More...
 
void distribute_radiation_output (int lev, int output_index)
 Distributes one updated canonical radiation output to other providers. More...
 
void WriteCheckpoint (const std::string &checkpointname)
 Writes surface-model state to a checkpoint file. More...
 
void ReadCheckpoint (const std::string &checkpointname)
 Restores surface-model state from a checkpoint file. More...
 
void validate_radiation_output_layout (int lev, const amrex::MultiFab *mf) const
 Validates the layout contract for a canonical radiation output. More...
 
void calculate_simple_average (int lev, amrex::MultiFab *const urban_frac)
 Computes simple land and urban weights from the urban fraction. More...
 
void build_radiation_fieldlist (int lev)
 Builds the ordered radiation-field list for one AMR level. More...
 
void weight_average_fields (int lev, amrex::MultiFab *const)
 Applies the current land and urban weights to selected model fields. More...
 
void activate_field_map (const std::string &name, const bool persistent=true)
 Activates a mapped field and allocates its storage on all levels. More...
 
void deactivate_transient_field_maps ()
 Disables and releases mapped fields used only by the last output. More...
 
bool is_field_mapped (int lev, SurfaceModelType type, int field_idx, const amrex::MultiFab *mf) const
 Checks whether a model field participates in a registered mapping. More...
 
void weight_model_field (int lev, const amrex::MultiFab *source, amrex::MultiFab *weighted, SurfaceModelType type)
 Writes a weighted copy of a model field. More...
 

Static Public Member Functions

static void GotoNextLine (std::istream &is)
 Advances an input stream to the next line. More...
 

Private Member Functions

void update_provider_mode (const int lev)
 Updates the provider mode after model data are registered. More...
 
void ensure_weight_factors (const int lev)
 Creates weighting factors when an external caller requests them. More...
 
void ensure_surface_outputs (const int lev)
 Allocates surface-output fields for one level. More...
 
void release_transient_surface_outputs ()
 Releases surface outputs that have no persistent consumer. More...
 

Private Attributes

int m_nlevs
 
bool m_use_urban = false
 
bool m_use_land = false
 
bool m_export_fluxes = true
 
bool m_export_fluxes_set = false
 
amrex::GpuArray< bool, 2 > m_output_fields_registered {{false, false}}
 
bool m_fields_are_valid = false
 
bool m_weights_updated = false
 
amrex::Vector< SurfaceProviderMode > m_provider_mode
 
amrex::Vector< amrex::BoxArray > m_ba
 
amrex::Vector< amrex::BoxArray > m_ba2d
 
amrex::Vector< amrex::Geometry > m_geom
 
amrex::Vector< amrex::DistributionMapping > m_dmap
 
amrex::Vector< amrex::Geometry > m_geom2d
 
amrex::Vector< amrex::iMultiFab * > m_lmask
 
amrex::Vector< amrex::MultiFab * > urban_frac_lev
 
amrex::Vector< amrex::Vector< amrex::MultiFab * > > lsm_data_lev
 
amrex::Vector< amrex::Vector< amrex::MultiFab * > > urban_data_lev
 
amrex::Vector< amrex::Vector< std::unique_ptr< amrex::MultiFab > > > weighted_lsm_data_lev
 
amrex::Vector< amrex::Vector< std::unique_ptr< amrex::MultiFab > > > weighted_urban_data_lev
 
amrex::Vector< std::unique_ptr< amrex::MultiFab > > u_star
 
amrex::Vector< std::unique_ptr< amrex::MultiFab > > t_star
 
amrex::Vector< std::unique_ptr< amrex::MultiFab > > q_star
 
amrex::Vector< std::unique_ptr< amrex::MultiFab > > t_surf
 
amrex::Vector< int > m_surface_outputs_requested
 
bool m_surface_outputs_enabled = false
 
bool m_surface_layer_outputs_enabled = false
 
amrex::Vector< amrex::MultiFab * > m_last_urban_frac
 
amrex::Vector< std::unique_ptr< amrex::MultiFab > > wavg
 
amrex::Vector< int > lsm_fields
 
amrex::Vector< int > urban_fields
 
amrex::Vector< amrex::Vector< std::unique_ptr< amrex::MultiFab > > > fields
 
std::unordered_map< std::string, Field > fieldmap
 
amrex::Vector< std::string > m_lsm_names
 
amrex::Vector< std::string > m_urban_names
 
amrex::Vector< amrex::Vector< const amrex::MultiFab * > > rad_fields
 
amrex::Vector< amrex::Vector< amrex::MultiFab * > > rad_output_fields
 
std::unordered_map< std::string, RadiationField > radiation_input_map
 
std::unordered_map< std::string, RadiationField > radiation_output_map
 

Static Private Attributes

static const std::vector< std::string > radnames = {"tskin", "emiss", "albedo_vis", "albedo_nir", "albedo_vis_diff", "albedo_nir_diff"}
 
static const std::vector< std::string > rad_output_names
 
static const std::vector< std::string > field_names
 

Detailed Description

Interface between ERF and land-surface and urban models.

SurfaceModel collects data and fluxes from the LSM and urban models, performs the land-urban weight averaging and exposes the resulting fields for use by ERF boundary-condition and physics components.

Models may register data and mappings independently, without requiring a fixed ordering or one-to-one correspondence between their fields.

Model-specific field names can also be registered under a common name for consumers such as RRTMGP and MOST.

This class supports its own checkpoint state, ensuring the mappings are restored correctly on restart. This class also provides a surface plotfile for weight-averaged model data for the fields registered.

Constructor & Destructor Documentation

◆ SurfaceModel()

SurfaceModel::SurfaceModel ( int  nlevs,
const amrex::Vector< amrex::BoxArray > &  ba,
const amrex::Vector< amrex::Geometry > &  geom,
const amrex::Vector< amrex::DistributionMapping > &  dm,
const SolverChoice &  ,
amrex::Vector< amrex::Vector< std::unique_ptr< amrex::iMultiFab >>> &   
)
inlineexplicit

Constructs a surface-model interface for the supplied AMR levels.

Parameters
nlevsNumber of AMR levels initially available.
baCell-centered box arrays for each level.
geomGeometry for each level.
dmDistribution mappings for each level.
solverChoiceERF solver configuration.
lmask_levLand-mask data for each level.
66  : m_nlevs(nlevs), m_ba(ba), m_geom(geom), m_dmap(dm) {
67 
68  u_star.resize(m_nlevs);
69  t_star.resize(m_nlevs);
70  q_star.resize(m_nlevs);
71  t_surf.resize(m_nlevs);
72 
73  wavg.resize(m_nlevs);
75  m_output_fields_registered = {false, false};
76 
77  m_ba2d.resize(m_nlevs);
78  m_geom2d.resize(m_nlevs);
79  m_lmask.resize(m_nlevs);
80 
81 
82  urban_frac_lev.resize(m_nlevs);
83  lsm_data_lev.resize(m_nlevs);
84  urban_data_lev.resize(m_nlevs);
87 
88  rad_fields.resize(m_nlevs);
89  rad_output_fields.resize(m_nlevs);
90  m_surface_outputs_requested.resize(m_nlevs, false);
91  m_last_urban_frac.resize(m_nlevs, nullptr);
92 
93  // set initial fields for each model to none
94  lsm_fields = amrex::Vector<int>(5, -1);
95  urban_fields = amrex::Vector<int>(5, -1);
96  }
amrex::Vector< amrex::MultiFab * > urban_frac_lev
Definition: ERF_SurfaceModel.H:1036
int m_nlevs
Definition: ERF_SurfaceModel.H:1009
amrex::Vector< amrex::BoxArray > m_ba2d
Definition: ERF_SurfaceModel.H:1028
amrex::Vector< amrex::Geometry > m_geom
Definition: ERF_SurfaceModel.H:1029
amrex::Vector< amrex::Vector< amrex::MultiFab * > > rad_output_fields
Definition: ERF_SurfaceModel.H:1097
amrex::Vector< amrex::MultiFab * > m_last_urban_frac
Definition: ERF_SurfaceModel.H:1052
amrex::Vector< std::unique_ptr< amrex::MultiFab > > q_star
Definition: ERF_SurfaceModel.H:1046
amrex::Vector< amrex::BoxArray > m_ba
Definition: ERF_SurfaceModel.H:1027
amrex::Vector< int > lsm_fields
Definition: ERF_SurfaceModel.H:1060
amrex::Vector< std::unique_ptr< amrex::MultiFab > > t_surf
Definition: ERF_SurfaceModel.H:1047
amrex::Vector< SurfaceProviderMode > m_provider_mode
Definition: ERF_SurfaceModel.H:1025
amrex::Vector< amrex::Vector< std::unique_ptr< amrex::MultiFab > > > weighted_urban_data_lev
Definition: ERF_SurfaceModel.H:1040
amrex::Vector< amrex::Vector< std::unique_ptr< amrex::MultiFab > > > weighted_lsm_data_lev
Definition: ERF_SurfaceModel.H:1039
amrex::GpuArray< bool, 2 > m_output_fields_registered
Definition: ERF_SurfaceModel.H:1016
amrex::Vector< std::unique_ptr< amrex::MultiFab > > u_star
Definition: ERF_SurfaceModel.H:1044
amrex::Vector< std::unique_ptr< amrex::MultiFab > > t_star
Definition: ERF_SurfaceModel.H:1045
amrex::Vector< amrex::Geometry > m_geom2d
Definition: ERF_SurfaceModel.H:1032
amrex::Vector< int > urban_fields
Definition: ERF_SurfaceModel.H:1061
amrex::Vector< amrex::Vector< amrex::MultiFab * > > lsm_data_lev
Definition: ERF_SurfaceModel.H:1037
amrex::Vector< int > m_surface_outputs_requested
Definition: ERF_SurfaceModel.H:1049
amrex::Vector< amrex::Vector< const amrex::MultiFab * > > rad_fields
Definition: ERF_SurfaceModel.H:1096
amrex::Vector< std::unique_ptr< amrex::MultiFab > > wavg
Definition: ERF_SurfaceModel.H:1055
amrex::Vector< amrex::iMultiFab * > m_lmask
Definition: ERF_SurfaceModel.H:1033
amrex::Vector< amrex::Vector< amrex::MultiFab * > > urban_data_lev
Definition: ERF_SurfaceModel.H:1038
amrex::Vector< amrex::DistributionMapping > m_dmap
Definition: ERF_SurfaceModel.H:1030

Member Function Documentation

◆ activate_all_field_maps()

void SurfaceModel::activate_all_field_maps ( const bool  persistent = true)

Activates all registered mapped fields for an output consumer.

660 {
661  for (const auto& entry : fieldmap) {
662  activate_field_map(entry.first, persistent);
663  }
664 }
void activate_field_map(const std::string &name, const bool persistent=true)
Activates a mapped field and allocates its storage on all levels.
Definition: ERF_SurfaceModel.cpp:637
std::unordered_map< std::string, Field > fieldmap
Definition: ERF_SurfaceModel.H:1091

◆ activate_field_map()

void SurfaceModel::activate_field_map ( const std::string &  name,
const bool  persistent = true 
)

Activates a mapped field and allocates its storage on all levels.

Parameters
nameCommon mapped field name.
638 {
639  auto field_it = fieldmap.find(name);
640  if (field_it == fieldmap.end()) { return; }
641 
642  Field& field = field_it->second;
643  field.active = true;
644  field.persistent_consumer = field.persistent_consumer || persistent;
645  if (field.mf_ind == -1) {
646  field.mf_ind = static_cast<int>(fields.size());
647  fields.emplace_back(m_nlevs);
648  }
649 
650  for (int lev = 0; lev < m_nlevs; ++lev) {
651  if (fields[field.mf_ind][lev] == nullptr) {
652  fields[field.mf_ind][lev] = std::make_unique<amrex::MultiFab>(
653  m_ba2d[lev], m_dmap[lev], 1, IntVect(1,1,0));
654  fields[field.mf_ind][lev]->setVal(0.0);
655  }
656  }
657 }
std::string name
Definition: ERF_Plotfile2DCatalog.cpp:105
amrex::Vector< amrex::Vector< std::unique_ptr< amrex::MultiFab > > > fields
Definition: ERF_SurfaceModel.H:1090

Referenced by get_field(), and initialize_for_level().

Here is the caller graph for this function:

◆ apply_weight_average()

void SurfaceModel::apply_weight_average ( int  lev,
const amrex::MultiFab *  lsm_data,
amrex::MultiFab *  lsm_weighted,
const amrex::MultiFab *  urban_data,
amrex::MultiFab *  urban_weighted 
)

Applies land and urban weights into separate output fields.

A null land or urban source is skipped, but both sources cannot be null. The source fields are not modified and the fields may use different underlying grids.

Parameters
levAMR level whose weights are used.
lsm_dataLand-model source data to weight.
lsm_weightedDestination for weighted land data.
urban_dataUrban-model source data to weight.
urban_weightedDestination for weighted urban data.
16 {
17  bool valid_land = (lsm_data != nullptr);
18  bool valid_urban = (urban_data != nullptr);
19 
20  AMREX_ASSERT_WITH_MESSAGE(valid_land || valid_urban, "Need at least one pointer to apply weights");
21  AMREX_ASSERT_WITH_MESSAGE(!valid_land || lsm_weighted != nullptr,
22  "Need a destination for weighted land data");
23  AMREX_ASSERT_WITH_MESSAGE(!valid_urban || urban_weighted != nullptr,
24  "Need a destination for weighted urban data");
25  AMREX_ASSERT_WITH_MESSAGE(lsm_data == nullptr || lsm_data != lsm_weighted,
26  "Weighted land data must use separate storage");
27  AMREX_ASSERT_WITH_MESSAGE(urban_data == nullptr || urban_data != urban_weighted,
28  "Weighted urban data must use separate storage");
29 
30  // make sure the weights have been calculated before applying them
32 
33  // Apply weights separately to support different underlying grids between lsm_data and urban_data
34  if (valid_land) {
35  weight_model_field(lev, lsm_data, lsm_weighted, SurfaceModelType::LAND);
36  }
37 
38  if (valid_urban) {
39  weight_model_field(lev, urban_data, urban_weighted, SurfaceModelType::URBAN);
40  }
41 }
AMREX_ALWAYS_ASSERT(bx.length()[2]==khi+1)
@ LAND
Definition: ERF_SurfaceModel.H:15
@ URBAN
Definition: ERF_SurfaceModel.H:15
AMREX_ASSERT_WITH_MESSAGE(wbar_cutoff_min > wbar_cutoff_max, "ERROR: wbar_cutoff_min < wbar_cutoff_max")
bool m_weights_updated
Definition: ERF_SurfaceModel.H:1023
void weight_model_field(int lev, const amrex::MultiFab *source, amrex::MultiFab *weighted, SurfaceModelType type)
Writes a weighted copy of a model field.
Definition: ERF_SurfaceModel.cpp:43
Here is the call graph for this function:

◆ are_fluxes()

bool SurfaceModel::are_fluxes ( )
inline

Returns whether the interface exports fluxes rather than MOST variables.

Returns
True when weighted outputs are fluxes.
471 { return m_export_fluxes; }
bool m_export_fluxes
Definition: ERF_SurfaceModel.H:1014

◆ build_radiation_fieldlist()

void SurfaceModel::build_radiation_fieldlist ( int  lev)
inline

Builds the ordered radiation-field list for one AMR level.

Parameters
levAMR level to update.
816  {
817  amrex::Print() << " --- building radiation field list" << std::endl;
818 
819  // loop over fields and check against names of radiation variables
820  for (int li = 0; li < static_cast<int>(radnames.size()); ++li) {
821  amrex::Print() << " - Checking for radiation variable '" << radnames[li] << "'" << std::endl;
822 
823  amrex::MultiFab* mf = nullptr;
824  auto rad_it = radiation_input_map.find(radnames[li]);
825  if (rad_it != radiation_input_map.end()) {
826  const int land_idx = rad_it->second.map.first;
827  const int urban_idx = rad_it->second.map.second;
828  const SurfaceProviderMode mode = m_provider_mode[lev];
829  const bool use_land = mode == SurfaceProviderMode::LandOnly ||
831  const bool use_urban = mode == SurfaceProviderMode::UrbanOnly ||
833  const bool valid_land = use_land && land_idx >= 0 &&
834  lev < static_cast<int>(lsm_data_lev.size()) &&
835  land_idx < static_cast<int>(lsm_data_lev[lev].size()) &&
836  lsm_data_lev[lev][land_idx] != nullptr;
837  const bool valid_urban = use_urban && urban_idx >= 0 &&
838  lev < static_cast<int>(urban_data_lev.size()) &&
839  urban_idx < static_cast<int>(urban_data_lev[lev].size()) &&
840  urban_data_lev[lev][urban_idx] != nullptr;
841  if (valid_land && valid_urban) {
842  if (!rad_it->second.weighted[lev]) {
843  rad_it->second.weighted[lev] = std::make_unique<amrex::MultiFab>(
844  m_ba2d[lev], m_dmap[lev], 1, amrex::IntVect(1,1,0));
845  }
846  auto& weighted = *rad_it->second.weighted[lev];
847  const auto& land = *lsm_data_lev[lev][land_idx];
848  const auto& urban = *urban_data_lev[lev][urban_idx];
849  for (amrex::MFIter mfi(weighted); mfi.isValid(); ++mfi) {
850  const amrex::Box bx = mfi.tilebox();
851  const auto weights = wavg[lev]->const_array(mfi);
852  const auto land_arr = land.const_array(mfi);
853  const auto urban_arr = urban.const_array(mfi);
854  // LSM surface fields may be stored at k <= 0; use the top valid LSM
855  // plane, which matches k=0 when the LSM keeps those fields synchronized
856  // at the surface (such as SLM). Urban surface fields are stored at k=0.
857  const int land_k = land.box(mfi.index()).bigEnd(2);
858  const int urban_k = urban.box(mfi.index()).smallEnd(2);
859  auto output = weighted.array(mfi);
860  amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) {
861  output(i,j,k) = land_arr(i,j,land_k) *
862  weights(i,j,0,SurfaceModelType::LAND) +
863  urban_arr(i,j,urban_k) *
864  weights(i,j,0,SurfaceModelType::URBAN);
865  });
866  }
867  weighted.FillBoundary(m_geom[lev].periodicity());
868  mf = rad_it->second.weighted[lev].get();
869  } else if (valid_land) {
870  mf = lsm_data_lev[lev][land_idx];
871  } else if (valid_urban) {
872  mf = urban_data_lev[lev][urban_idx];
873  }
874  }
875  if (mf) {
876  rad_fields[lev].push_back(mf);
877  } else {
878  amrex::Print() << " - WARNING: did not find radiation variable, setting to null to use default RRTMGP value!" << std::endl;
879  rad_fields[lev].push_back(nullptr);
880  }
881  }
883  static_cast<int>(rad_fields[lev].size()) == static_cast<int>(radnames.size()),
884  "Did not find all radiation fields");
885  }
AMREX_ALWAYS_ASSERT_WITH_MESSAGE(m_cloud_chamber_config.active, "Cloud Chamber: initializer reached without a parsed configuration")
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);})
SurfaceProviderMode
Definition: ERF_SurfaceModel.H:17
std::unordered_map< std::string, RadiationField > radiation_input_map
Definition: ERF_SurfaceModel.H:1108
static const std::vector< std::string > radnames
Definition: ERF_SurfaceModel.H:1064

Referenced by get_radiation_fields().

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

◆ calculate_simple_average()

void SurfaceModel::calculate_simple_average ( int  lev,
amrex::MultiFab *const  urban_frac 
)

Computes simple land and urban weights from the urban fraction.

Parameters
levAMR level to update.
urban_fracUrban fraction used to compute the weights.
528 {
529  const SurfaceProviderMode mode = m_provider_mode[lev];
530 
531  // If no urban fraction is available, use the only enabled model as the
532  // complete surface. A missing fraction is ambiguous when both models
533  // are enabled, so fail explicitly rather than silently choosing a model.
534  if (urban_frac == nullptr) {
537  "Urban fraction is required when both land and urban models are enabled");
538  }
539 
540  if (mode != SurfaceProviderMode::Both) {
541  m_weights_updated = true;
542  return;
543  }
544 
545  for (MFIter mfi(*wavg[lev], TileNoZ()); mfi.isValid(); ++mfi)
546  {
547  Box tbx = mfi.tilebox();
548  // weights are defined only on k = 0
549  tbx = tbx.makeSlab(2, 0);
550 
551  auto urban_frac_arr = (urban_frac != nullptr) ? urban_frac->const_array(mfi) : Array4<const Real>{};
552  auto weights_arr = wavg[lev]->array(mfi);
553 
554  ParallelFor(tbx, [=] AMREX_GPU_DEVICE (int i, int j, int /*k*/)
555  {
556  // Weights are proportional to urban fraction coverage in the current cell.
557  weights_arr(i, j, 0, SurfaceModelType::URBAN) = urban_frac_arr(i, j, 0);
558  weights_arr(i, j, 0, SurfaceModelType::LAND) = 1.0 - urban_frac_arr(i, j, 0);
559  });
560  }
561 
562  m_weights_updated = true;
563 }
AMREX_FORCE_INLINE amrex::IntVect TileNoZ()
Definition: ERF_TileNoZ.H:11
Here is the call graph for this function:

◆ calculate_weight_average()

void SurfaceModel::calculate_weight_average ( int  lev,
amrex::MultiFab *const  urban_frac,
bool  update_derived = true 
)

Computes land and urban weights and applies them to registered fields.

Parameters
levAMR level to update.
urban_fracUrban fraction used to compute the weights.
73 {
74  m_last_urban_frac[lev] = urban_frac;
75  const SurfaceProviderMode mode = m_provider_mode[lev];
76  // An urban model can be enabled on only a subset of AMR levels. Those
77  // inactive levels have no surface-provider data to update.
78  if (mode == SurfaceProviderMode::None) {
79  return;
80  }
81 
82  // Calculate weighted averages between Land and Urban. Output consumers
83  // can reuse the weights computed during the timestep update.
84  if (update_derived || !m_weights_updated) {
85  calculate_simple_average(lev, urban_frac);
86  }
87 
88  const bool use_urban = mode == SurfaceProviderMode::UrbanOnly ||
90  const bool use_land = mode == SurfaceProviderMode::LandOnly ||
92  const bool use_weights = mode == SurfaceProviderMode::Both;
93  const bool use_fluxes = m_export_fluxes;
94  const bool output_fields_registered =
97 
98  amrex::MultiFab* const outputs[] = {u_star[lev].get(), t_star[lev].get(), q_star[lev].get(), t_surf[lev].get()};
99 
100  const int nfields = (m_export_fluxes) ? 5 : 4;
101 
102  if (m_surface_outputs_requested[lev] && output_fields_registered && !use_weights) {
103  const auto& provider_fields = use_land ? lsm_fields : urban_fields;
104  const auto& provider_data = use_land ? lsm_data_lev[lev] : urban_data_lev[lev];
105  auto source_for = [&] (const int field) -> const amrex::MultiFab* {
106  if (field >= static_cast<int>(provider_fields.size()) ||
107  provider_fields[field] < 0 ||
108  provider_fields[field] >= static_cast<int>(provider_data.size())) {
109  return nullptr;
110  }
111  return provider_data[provider_fields[field]];
112  };
113 
114  const amrex::MultiFab* source0 = source_for(0);
115  const amrex::MultiFab* source1 = source_for(1);
116  const amrex::MultiFab* source2 = source_for(2);
117  const amrex::MultiFab* source3 = source_for(3);
118  const amrex::MultiFab* source4 = source_for(4);
119 
120  for (MFIter mfi(*u_star[lev], TileNoZ()); mfi.isValid(); ++mfi)
121  {
122  const Box tbx = mfi.tilebox();
123  const auto source0_arr = source0 ? source0->const_array(mfi) : Array4<const Real>{};
124  const auto source1_arr = source1 ? source1->const_array(mfi) : Array4<const Real>{};
125  const auto source2_arr = source2 ? source2->const_array(mfi) : Array4<const Real>{};
126  const auto source3_arr = source3 ? source3->const_array(mfi) : Array4<const Real>{};
127  const auto source4_arr = source4 ? source4->const_array(mfi) : Array4<const Real>{};
128  const int source0_k = source0 ? (use_land ? source0->box(mfi.index()).bigEnd(2)
129  : source0->box(mfi.index()).smallEnd(2)) : 0;
130  const int source1_k = source1 ? (use_land ? source1->box(mfi.index()).bigEnd(2)
131  : source1->box(mfi.index()).smallEnd(2)) : 0;
132  const int source2_k = source2 ? (use_land ? source2->box(mfi.index()).bigEnd(2)
133  : source2->box(mfi.index()).smallEnd(2)) : 0;
134  const int source3_k = source3 ? (use_land ? source3->box(mfi.index()).bigEnd(2)
135  : source3->box(mfi.index()).smallEnd(2)) : 0;
136  const int source4_k = source4 ? (use_land ? source4->box(mfi.index()).bigEnd(2)
137  : source4->box(mfi.index()).smallEnd(2)) : 0;
138 
139  auto u_arr = u_star[lev]->array(mfi);
140  auto t_arr = t_star[lev]->array(mfi);
141  auto q_arr = q_star[lev]->array(mfi);
142  auto tsurf_arr = t_surf[lev]->array(mfi);
143  ParallelFor(tbx, [=] AMREX_GPU_DEVICE (int i, int j, int k)
144  {
145  if (use_fluxes) {
146  u_arr(i, j, k, 0) = source0_arr ? source0_arr(i, j, source0_k) : 0.0;
147  u_arr(i, j, k, 1) = source1_arr ? source1_arr(i, j, source1_k) : 0.0;
148  t_arr(i, j, k) = source2_arr ? source2_arr(i, j, source2_k) : 0.0;
149  q_arr(i, j, k) = source3_arr ? source3_arr(i, j, source3_k) : 0.0;
150  tsurf_arr(i, j, k) = source4_arr ? source4_arr(i, j, source4_k) : 0.0;
151  } else {
152  u_arr(i, j, k, 0) = source0_arr ? source0_arr(i, j, source0_k) : 0.0;
153  u_arr(i, j, k, 1) = 0.0;
154  t_arr(i, j, k) = source1_arr ? source1_arr(i, j, source1_k) : 0.0;
155  q_arr(i, j, k) = source2_arr ? source2_arr(i, j, source2_k) : 0.0;
156  tsurf_arr(i, j, k) = source3_arr ? source3_arr(i, j, source3_k) : 0.0;
157  }
158  });
159  }
160 
161  u_star[lev]->FillBoundary(m_geom[lev].periodicity());
162  t_star[lev]->FillBoundary(m_geom[lev].periodicity());
163  q_star[lev]->FillBoundary(m_geom[lev].periodicity());
164  t_surf[lev]->FillBoundary(m_geom[lev].periodicity());
165  }
166 
167  // Output weighted surface fluxes into ustar, tstar, qstar and surface temperature into tsurf
168  // TODO: make sure grids of urban and LSM inputs match
169  if (m_surface_outputs_requested[lev] && output_fields_registered && use_weights) {
170  if (!use_fluxes) {
171  u_star[lev]->setVal(0.0, 1, 1, 0);
172  }
173  for (int field = 0; field < nfields; ++field)
174  {
175  int output_field = (use_fluxes && field > 0) ? field - 1 : field;
176  int comp = (use_fluxes && field < 2) ? field : 0;
177 
178  // whether we have a valid LSM multifab
179  bool valid_land = (use_land &&
180  lsm_fields[field] != -1 &&
181  lsm_data_lev[lev][lsm_fields[field]]);
182 
183  // whether we have a valid urban multifab
184  bool valid_urban = (use_urban &&
185  urban_fields[field] != -1 &&
186  urban_data_lev[lev][urban_fields[field]]);
187 
188  for (MFIter mfi(*u_star[lev], TileNoZ()); mfi.isValid(); ++mfi)
189  {
190  Box tbx = mfi.tilebox();
191 
192  const auto weights_arr = use_weights
193  ? wavg[lev]->const_array(mfi) : Array4<const Real>{};
194 
195  // Outputs for surface boundary condition
196  auto output_arr = outputs[output_field]->array(mfi);
197  const amrex::MultiFab* lsm_mf = valid_land ? lsm_data_lev[lev][lsm_fields[field]] : nullptr;
198  const amrex::MultiFab* urban_mf = valid_urban ? urban_data_lev[lev][urban_fields[field]] : nullptr;
199  const int lsm_khi = valid_land ? lsm_mf->box(mfi.index()).bigEnd(2) : 0;
200  const int urban_klo = valid_urban ? urban_mf->box(mfi.index()).smallEnd(2) : 0;
201  auto lsm_data_arr = valid_land ? lsm_mf->const_array(mfi) : Array4<const Real>{};
202  auto urban_data_arr = valid_urban ? urban_mf->const_array(mfi) : Array4<const Real>{};
203 
204  ParallelFor(tbx, [=] AMREX_GPU_DEVICE (int i, int j, int k)
205  {
206  if (valid_land && use_land && !use_urban) {
207  output_arr(i, j, k, comp) = lsm_data_arr(i, j, lsm_khi);
208  } else if (valid_urban && use_urban && !use_land) {
209  output_arr(i, j, k, comp) = urban_data_arr(i, j, urban_klo);
210  } else {
211  Real land = (lsm_data_arr) ? lsm_data_arr(i, j, lsm_khi) * weights_arr(i, j, 0, SurfaceModelType::LAND) : 0.0;
212  Real urb = (urban_data_arr) ? urban_data_arr(i, j, urban_klo) * weights_arr(i, j, 0, SurfaceModelType::URBAN) : 0.0;
213  output_arr(i, j, k, comp) = land + urb;
214  }
215 
216  });
217  }
218 
219  outputs[output_field]->FillBoundary(comp, 1, m_geom[lev].periodicity());
220  }
221  }
222 
223  // Write weighted copies only when both providers contribute. With one
224  // provider, get_weighted_model_data() returns the provider-owned source.
225  if (update_derived && use_weights && use_land) {
226  for (int field = nfields; field < static_cast<int>(lsm_fields.size()); ++field) {
227  if (lsm_fields[field] == -1) continue;
229  lsm_data_lev[lev][lsm_fields[field]])) continue;
230  const int field_idx = lsm_fields[field];
231  if (static_cast<int>(weighted_lsm_data_lev[lev].size()) <= field_idx) {
232  weighted_lsm_data_lev[lev].resize(lsm_data_lev[lev].size());
233  }
234  if (weighted_lsm_data_lev[lev][field_idx] == nullptr) {
235  const auto* source = lsm_data_lev[lev][field_idx];
236  weighted_lsm_data_lev[lev][field_idx] = std::make_unique<MultiFab>(
237  source->boxArray(), source->DistributionMap(), source->nComp(),
238  source->nGrowVect());
239  }
240  weight_model_field(lev, lsm_data_lev[lev][field_idx],
241  weighted_lsm_data_lev[lev][field_idx].get(),
243  }
244  }
245 
246  if (update_derived && use_weights && use_urban) {
247  for (int field = nfields; field < static_cast<int>(urban_fields.size()); ++field) {
248  if (urban_fields[field] == -1) continue;
250  urban_data_lev[lev][urban_fields[field]])) continue;
251  const int field_idx = urban_fields[field];
252  if (static_cast<int>(weighted_urban_data_lev[lev].size()) <= field_idx) {
253  weighted_urban_data_lev[lev].resize(urban_data_lev[lev].size());
254  }
255  if (weighted_urban_data_lev[lev][field_idx] == nullptr) {
256  const auto* source = urban_data_lev[lev][field_idx];
257  weighted_urban_data_lev[lev][field_idx] = std::make_unique<MultiFab>(
258  source->boxArray(), source->DistributionMap(), source->nComp(),
259  source->nGrowVect());
260  }
261  weight_model_field(lev, urban_data_lev[lev][field_idx],
262  weighted_urban_data_lev[lev][field_idx].get(),
264  }
265  }
266 
267  weight_average_fields(lev, urban_frac);
268 
269  if (!update_derived) { return; }
270 
271  for (auto& entry : radiation_input_map) {
272  const int land_idx = entry.second.map.first;
273  const int urban_idx = entry.second.map.second;
274  const bool valid_land = use_land && land_idx >= 0 &&
275  land_idx < static_cast<int>(lsm_data_lev[lev].size()) &&
276  lsm_data_lev[lev][land_idx] != nullptr;
277  const bool valid_urban = use_urban && urban_idx >= 0 &&
278  urban_idx < static_cast<int>(urban_data_lev[lev].size()) &&
279  urban_data_lev[lev][urban_idx] != nullptr;
280  if (!(valid_land && valid_urban)) { continue; }
281 
282  if (entry.second.weighted[lev] == nullptr) {
283  entry.second.weighted[lev] = std::make_unique<MultiFab>(
284  m_ba2d[lev], m_dmap[lev], 1, IntVect(1,1,0));
285  }
286  MultiFab& output = *entry.second.weighted[lev];
287  const MultiFab& land = *lsm_data_lev[lev][land_idx];
288  const MultiFab& urban = *urban_data_lev[lev][urban_idx];
289  for (MFIter mfi(output, TileNoZ()); mfi.isValid(); ++mfi) {
290  const Box bx = mfi.tilebox();
291  const auto weights = wavg[lev]->const_array(mfi);
292  const auto land_arr = land.const_array(mfi);
293  const auto urban_arr = urban.const_array(mfi);
294  // LSM surface fields may be stored at k <= 0; use the top valid LSM
295  // plane, which matches k=0 when the LSM keeps those fields synchronized
296  // at the surface (such as SLM). Urban surface fields are stored at k=0.
297  const int land_k = land.box(mfi.index()).bigEnd(2);
298  const int urban_k = urban.box(mfi.index()).smallEnd(2);
299  auto out = output.array(mfi);
300  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) {
301  out(i,j,k) = land_arr(i,j,land_k) * weights(i,j,0,SurfaceModelType::LAND) +
302  urban_arr(i,j,urban_k) * weights(i,j,0,SurfaceModelType::URBAN);
303  });
304  }
305  output.FillBoundary(m_geom[lev].periodicity());
306  }
307 }
pp get("wavelength", wavelength)
amrex::Real Real
Definition: ERF_ShocInterface.H:19
bool is_field_mapped(int lev, SurfaceModelType type, int field_idx, const amrex::MultiFab *mf) const
Checks whether a model field participates in a registered mapping.
Definition: ERF_SurfaceModel.cpp:492
void calculate_simple_average(int lev, amrex::MultiFab *const urban_frac)
Computes simple land and urban weights from the urban fraction.
Definition: ERF_SurfaceModel.cpp:527
void weight_average_fields(int lev, amrex::MultiFab *const)
Applies the current land and urban weights to selected model fields.
Definition: ERF_SurfaceModel.cpp:679
Here is the call graph for this function:

◆ deactivate_transient_field_maps()

void SurfaceModel::deactivate_transient_field_maps ( )

Disables and releases mapped fields used only by the last output.

667 {
668  for (auto& entry : fieldmap) {
669  Field& field = entry.second;
670  if (field.persistent_consumer) { continue; }
671  field.active = false;
672  if (field.mf_ind == -1) { continue; }
673  for (auto& mf : fields[field.mf_ind]) {
674  mf.reset();
675  }
676  }
677 }

◆ distribute_radiation_output()

void SurfaceModel::distribute_radiation_output ( int  lev,
int  output_index 
)

Distributes one updated canonical radiation output to other providers.

Parameters
levAMR level to update.
output_indexIndex in the canonical radiation output list.
447 {
448  AMREX_ALWAYS_ASSERT(output_index >= 0 &&
449  output_index < static_cast<int>(rad_output_names.size()));
450  auto it = radiation_output_map.find(rad_output_names[output_index]);
451  if (it == radiation_output_map.end()) { return; }
452 
453  const int land_idx = it->second.map.first;
454  const int urban_idx = it->second.map.second;
455  const SurfaceProviderMode mode = m_provider_mode[lev];
456  const bool use_land = mode == SurfaceProviderMode::LandOnly ||
458  const bool use_urban = mode == SurfaceProviderMode::UrbanOnly ||
460  MultiFab* lsm = (use_land && land_idx >= 0 && land_idx < static_cast<int>(lsm_data_lev[lev].size()))
461  ? lsm_data_lev[lev][land_idx] : nullptr;
462  MultiFab* urban = (use_urban && urban_idx >= 0 && urban_idx < static_cast<int>(urban_data_lev[lev].size()))
463  ? urban_data_lev[lev][urban_idx] : nullptr;
464 
465  // The LSM destination is always primary when it is available. Urban
466  // receives the updated surface plane only when both destinations exist.
467  if (lsm && urban && lsm != urban) {
468  for (MFIter mfi(*urban, TileNoZ()); mfi.isValid(); ++mfi) {
469  const Box& source_box = (*lsm)[mfi].box();
470  const Box& target_box = (*urban)[mfi].box();
472  source_box.smallEnd(2) <= 0 && source_box.bigEnd(2) >= 0 &&
473  target_box.smallEnd(2) <= 0 && target_box.bigEnd(2) >= 0,
474  "Radiation output destinations must contain the k=0 surface plane");
475  //
476  // These are the *grown* boxes, so the two destinations need not carry the same
477  // ghost vector. Loop over the intersection of the two surface planes rather than
478  // over the target alone: indexing the source at the target's indices would read
479  // out of bounds wherever the urban halo reaches past the LSM one.
480  //
481  const Box copy_slab = makeSlab(source_box, 2, 0) & makeSlab(target_box, 2, 0);
482  if (copy_slab.isEmpty()) { continue; }
483  const auto source = (*lsm)[mfi].array();
484  auto target = (*urban)[mfi].array();
485  amrex::ParallelFor(copy_slab, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept {
486  target(i, j, k) = source(i, j, k);
487  });
488  }
489  }
490 }
std::unordered_map< std::string, RadiationField > radiation_output_map
Definition: ERF_SurfaceModel.H:1109
static const std::vector< std::string > rad_output_names
Definition: ERF_SurfaceModel.H:1099
Here is the call graph for this function:

◆ distribute_radiation_outputs()

void SurfaceModel::distribute_radiation_outputs ( int  lev)

Distributes updated canonical radiation outputs to other providers.

Parameters
levAMR level to update.
440 {
441  for (int i = 0; i < static_cast<int>(rad_output_names.size()); ++i) {
443  }
444 }
void distribute_radiation_output(int lev, int output_index)
Distributes one updated canonical radiation output to other providers.
Definition: ERF_SurfaceModel.cpp:446

◆ ensure_surface_outputs()

void SurfaceModel::ensure_surface_outputs ( const int  lev)
inlineprivate

Allocates surface-output fields for one level.

Parameters
levAMR level whose outputs should be materialized.
976  {
977  if (u_star[lev] != nullptr) { return; }
978 
979  const amrex::IntVect ng(1,1,0);
980  u_star[lev] = std::make_unique<amrex::MultiFab>(m_ba2d[lev], m_dmap[lev], 2, ng);
981  t_star[lev] = std::make_unique<amrex::MultiFab>(m_ba2d[lev], m_dmap[lev], 1, ng);
982  q_star[lev] = std::make_unique<amrex::MultiFab>(m_ba2d[lev], m_dmap[lev], 1, ng);
983  t_surf[lev] = std::make_unique<amrex::MultiFab>(m_ba2d[lev], m_dmap[lev], 1, ng);
984  u_star[lev]->setVal(0.0);
985  t_star[lev]->setVal(0.0);
986  q_star[lev]->setVal(0.0);
987  t_surf[lev]->setVal(300.0);
988  }
@ ng
Definition: ERF_Morrison.H:50

Referenced by initialize_for_level(), request_surface_layer_outputs(), request_surface_outputs(), and update_provider_mode().

Here is the caller graph for this function:

◆ ensure_weight_factors()

void SurfaceModel::ensure_weight_factors ( const int  lev)
inlineprivate

Creates weighting factors when an external caller requests them.

Parameters
levAMR level whose weighting factors should be materialized.
959  {
960  if (wavg[lev] != nullptr) { return; }
961 
962  const amrex::IntVect ng(1,1,0);
963  wavg[lev] = std::make_unique<amrex::MultiFab>(m_ba2d[lev], m_dmap[lev], 2, ng);
964  const SurfaceProviderMode mode = m_provider_mode[lev];
965  wavg[lev]->setVal(mode != SurfaceProviderMode::UrbanOnly ? 1.0 : 0.0,
966  SurfaceModelType::LAND, 1, 0);
967  wavg[lev]->setVal(mode == SurfaceProviderMode::UrbanOnly ? 1.0 : 0.0,
969  }

Referenced by get_wavg_factors(), and update_provider_mode().

Here is the caller graph for this function:

◆ fields_are_valid()

bool SurfaceModel::fields_are_valid ( ) const
inline

Returns whether the surface models have advanced at least once.

Returns
False until the first advance, while the registered fields still hold their initial values rather than anything the models computed.
509 { return m_fields_are_valid; }
bool m_fields_are_valid
Definition: ERF_SurfaceModel.H:1020

◆ get_field()

amrex::MultiFab* SurfaceModel::get_field ( const std::string &  name,
int  lev = 0 
)
inline

Retrieves a registered field by its common name.

Parameters
nameRegistered field name.
levAMR level to query.
Returns
Pointer to the registered field, or nullptr if it is unknown.
722  {
723  if (auto field = fieldmap.find(name); field != fieldmap.end()) {
725  return fields[field->second.mf_ind][lev].get();
726  } else {
727  return nullptr;
728  }
729  }
Here is the call graph for this function:

◆ get_qstar()

amrex::MultiFab* SurfaceModel::get_qstar ( const int &  lev)
inline

Returns the weighted moisture output for a level.

Parameters
levAMR level to query.
Returns
Pointer to the weighted q-star or vapor-flux field.
534  {
535  return q_star[lev].get();
536  }

◆ get_radiation_fields()

const amrex::Vector<const amrex::MultiFab*> SurfaceModel::get_radiation_fields ( int  lev = 0)
inline

Returns fields registered under the RRTMGP radiation names.

The resulting map is cached after the first call for reuse on subsequent timesteps. When an expected field is missing, it is marked nullptr for the RRTMGP implementation to apply its fallback values.

Parameters
levAMR level to query.
Returns
Radiation fields in the order expected by RRTMGP.
741  {
742  // check if we need to build the radiation list (for first time step,)
743  if (rad_fields[lev].size() == 0) {
745  }
746  return rad_fields[lev];
747  }
void build_radiation_fieldlist(int lev)
Builds the ordered radiation-field list for one AMR level.
Definition: ERF_SurfaceModel.H:816
Here is the call graph for this function:

◆ get_radiation_output_field()

MultiFab * SurfaceModel::get_radiation_output_field ( int  lev,
const std::string &  name 
)

Returns the destination of one canonical radiation output by name.

Parameters
levAMR level to query.
nameCanonical radiation output name (aborts on an unknown name).
Returns
The registered destination, or nullptr when none is registered.
412 {
413  const auto it = std::find(rad_output_names.begin(), rad_output_names.end(), name);
415  "Unknown canonical radiation output: " + name);
416  return get_radiation_output_fields(lev)[static_cast<int>(it - rad_output_names.begin())];
417 }
const amrex::Vector< amrex::MultiFab * > get_radiation_output_fields(int lev=0)
Returns canonical radiation output destinations for one level.
Definition: ERF_SurfaceModel.cpp:378
Here is the call graph for this function:

◆ get_radiation_output_fields()

const Vector< MultiFab * > SurfaceModel::get_radiation_output_fields ( int  lev = 0)

Returns canonical radiation output destinations for one level.

Parameters
levAMR level to query.
Returns
One destination per canonical radiation output.
379 {
380  if (!rad_output_fields[lev].empty()) { return rad_output_fields[lev]; }
381  rad_output_fields[lev].resize(rad_output_names.size(), nullptr);
382  const SurfaceProviderMode mode = m_provider_mode[lev];
383  const bool use_land = mode == SurfaceProviderMode::LandOnly ||
385  const bool use_urban = mode == SurfaceProviderMode::UrbanOnly ||
387  for (int i = 0; i < static_cast<int>(rad_output_names.size()); ++i) {
388  auto it = radiation_output_map.find(rad_output_names[i]);
389  if (it == radiation_output_map.end()) { continue; }
390  const int land_idx = it->second.map.first;
391  const int urban_idx = it->second.map.second;
392  const bool valid_land = use_land && land_idx >= 0 &&
393  land_idx < static_cast<int>(lsm_data_lev[lev].size()) &&
394  lsm_data_lev[lev][land_idx];
395  const bool valid_urban = use_urban && urban_idx >= 0 &&
396  urban_idx < static_cast<int>(urban_data_lev[lev].size()) &&
397  urban_data_lev[lev][urban_idx];
398  if (valid_land) {
399  validate_radiation_output_layout(lev, lsm_data_lev[lev][land_idx]);
400  rad_output_fields[lev][i] = lsm_data_lev[lev][land_idx];
401  } else if (valid_urban) {
402  rad_output_fields[lev][i] = urban_data_lev[lev][urban_idx];
403  }
404  if (valid_urban) {
405  validate_radiation_output_layout(lev, urban_data_lev[lev][urban_idx]);
406  }
407  }
408  return rad_output_fields[lev];
409 }
void validate_radiation_output_layout(int lev, const amrex::MultiFab *mf) const
Validates the layout contract for a canonical radiation output.
Definition: ERF_SurfaceModel.cpp:419

◆ get_surface_flux_view()

SurfaceFluxView SurfaceModel::get_surface_flux_view ( const int  lev,
const amrex::MFIter &  mfi 
) const
inline

Returns provider or blended surface-flux views for one tile.

Parameters
levAMR level to query.
mfiTile whose flux arrays are requested.
Returns
Read-only flux views selected for the level's provider mode.
554  {
556 
557  const SurfaceProviderMode mode = m_provider_mode[lev];
558  if (mode == SurfaceProviderMode::Both) {
559  AMREX_ALWAYS_ASSERT(u_star[lev] != nullptr);
560  AMREX_ALWAYS_ASSERT(t_star[lev] != nullptr);
561  AMREX_ALWAYS_ASSERT(q_star[lev] != nullptr);
562  return {u_star[lev]->const_array(mfi, 0),
563  u_star[lev]->const_array(mfi, 1),
564  t_star[lev]->const_array(mfi),
565  q_star[lev]->const_array(mfi)};
566  }
567 
568  const auto& provider_fields = (mode == SurfaceProviderMode::LandOnly)
570  const auto& provider_data = (mode == SurfaceProviderMode::LandOnly)
571  ? lsm_data_lev[lev] : urban_data_lev[lev];
572  auto get_provider_field = [&] (const int field) -> amrex::Array4<const amrex::Real> {
573  if (field >= static_cast<int>(provider_fields.size())) {
574  return {};
575  }
576  const int index = provider_fields[field];
577  if (index < 0 || index >= static_cast<int>(provider_data.size()) ||
578  provider_data[index] == nullptr) {
579  return {};
580  }
581  return provider_data[index]->const_array(mfi);
582  };
583 
584  return {get_provider_field(0), get_provider_field(1),
585  get_provider_field(2), get_provider_field(3)};
586  }
Here is the call graph for this function:

◆ get_tstar()

amrex::MultiFab* SurfaceModel::get_tstar ( const int &  lev)
inline

Returns the weighted thermal output for a level.

Parameters
levAMR level to query.
Returns
Pointer to the weighted t-star or heat-flux field.
525  {
526  return t_star[lev].get();
527  }

◆ get_tsurf()

amrex::MultiFab* SurfaceModel::get_tsurf ( const int &  lev)
inline

Returns the weighted surface-temperature output for a level.

Parameters
levAMR level to query.
Returns
Pointer to the weighted surface-temperature field.
543  {
544  return t_surf[lev].get();
545  }

◆ get_ustar()

amrex::MultiFab* SurfaceModel::get_ustar ( const int &  lev)
inline

Returns the weighted horizontal momentum output for a level.

Parameters
levAMR level to query.
Returns
Pointer to the weighted u-star or momentum-flux field.
516  {
517  return u_star[lev].get();
518  }

◆ get_wavg_factors()

amrex::MultiFab* SurfaceModel::get_wavg_factors ( const int &  lev)
inline

Returns the land and urban weighting factors for a level.

Parameters
levAMR level to query.
Returns
Pointer to the weighting-factor field.
639  {
641  return wavg[lev].get();
642  }
void ensure_weight_factors(const int lev)
Creates weighting factors when an external caller requests them.
Definition: ERF_SurfaceModel.H:958
Here is the call graph for this function:

◆ get_weighted_model_data()

const amrex::MultiFab* SurfaceModel::get_weighted_model_data ( const int  lev,
SurfaceModelType  type,
const int  field_idx 
) const
inline

Returns weighted data for a selected model field.

Parameters
levAMR level to query.
typeProvider type owning the field.
field_idxProvider field index.
Returns
Weighted field or the source field for a single provider, or nullptr if it has not been selected.
597  {
598  const SurfaceProviderMode mode = m_provider_mode[lev];
599  const bool single_provider =
602  if (single_provider) {
603  const auto& selected = (type == SurfaceModelType::LAND)
605  const int first_output_field = m_export_fluxes ? 5 : 4;
606  bool is_weighted_field = false;
607  for (int field = first_output_field;
608  field < static_cast<int>(selected.size()); ++field) {
609  if (selected[field] == field_idx) {
610  is_weighted_field = true;
611  break;
612  }
613  }
614  if (!is_weighted_field) {
615  return nullptr;
616  }
617 
618  const auto& source = (type == SurfaceModelType::LAND)
619  ? lsm_data_lev[lev] : urban_data_lev[lev];
620  if (field_idx < 0 || field_idx >= static_cast<int>(source.size())) {
621  return nullptr;
622  }
623  return source[field_idx];
624  }
625 
626  const auto& weighted = (type == SurfaceModelType::LAND)
628  if (field_idx < 0 || field_idx >= static_cast<int>(weighted.size())) {
629  return nullptr;
630  }
631  return weighted[field_idx].get();
632  }

◆ GotoNextLine()

void SurfaceModel::GotoNextLine ( std::istream &  is)
static

Advances an input stream to the next line.

taken from ERF_Checkpoint, reuse?

Parameters
isInput stream to advance.
825 {
826  constexpr std::streamsize bl_ignore_max { 100000 };
827  is.ignore(bl_ignore_max, '\n');
828 }

◆ initialize_for_level()

void SurfaceModel::initialize_for_level ( int  lev,
const amrex::BoxArray &  ba,
const amrex::Geometry &  geom,
const amrex::DistributionMapping &  dm,
const amrex::Vector< std::unique_ptr< amrex::iMultiFab >> &  lmask_lev,
const amrex::Vector< amrex::BCRec > &  domain_bcs_type,
const amrex::Vector< amrex::IntVect > &  refRatio 
)
inline

Initializes storage and boundary data for an AMR level.

Existing coarser-level data are interpolated when a finer level is initialized.

Parameters
levAMR level to initialize.
baCell-centered box array for the level.
geomGeometry for the level.
dmDistribution mapping for the level.
lmask_levLand-mask data used by the level.
domain_bcs_typeBoundary conditions for interpolation.
refRatioRefinement ratios between adjacent levels.
113  {
114  if (lev >= m_nlevs) {
115  m_nlevs = lev+1;
116 
117  u_star.resize(m_nlevs);
118  t_star.resize(m_nlevs);
119  q_star.resize(m_nlevs);
120  t_surf.resize(m_nlevs);
121 
122  wavg.resize(m_nlevs);
124 
125  m_dmap.resize(m_nlevs);
126  m_ba.resize(m_nlevs);
127  m_ba2d.resize(m_nlevs);
128  m_geom.resize(m_nlevs);
129  m_geom2d.resize(m_nlevs);
130  m_lmask.resize(m_nlevs);
131 
132  urban_frac_lev.resize(m_nlevs);
133  lsm_data_lev.resize(m_nlevs);
134  urban_data_lev.resize(m_nlevs);
137 
138  rad_fields.resize(m_nlevs);
139  rad_output_fields.resize(m_nlevs);
141  m_last_urban_frac.resize(m_nlevs, nullptr);
142  }
143 
144  m_ba[lev] = ba;
145  m_geom[lev] = geom;
146  m_dmap[lev] = dm;
147 
148  // Create a 2D ba, dm, & ghost cells
149  amrex::BoxList bl2d = ba.boxList();
150  for (auto& b : bl2d) {
151  b.setRange(2, 0);
152  }
153  m_ba2d[lev] = amrex::BoxArray(std::move(bl2d));
154 
155  amrex::RealBox dom2d = geom.ProbDomain();
156  dom2d.setHi(2, geom.CellSize(2));
157  m_geom2d[lev].define(makeSlab(m_geom[lev].Domain(), 2, 0), dom2d, geom.Coord(), geom.isPeriodic());
158 
159  m_lmask[lev] = lmask_lev[0].get();
160 
161  weighted_lsm_data_lev[lev].clear();
162  weighted_urban_data_lev[lev].clear();
163 
164  u_star[lev].reset();
165  t_star[lev].reset();
166  q_star[lev].reset();
167  t_surf[lev].reset();
168  wavg[lev].reset();
169  if (m_surface_outputs_requested[lev]) {
171  }
173 
174  // Recreate registered fields on the current level layout. This is also
175  // needed when an existing AMR level is remade with a new grid layout.
176  for (int field = 0; field < static_cast<int>(fields.size()); ++field) {
177  if (static_cast<int>(fields[field].size()) < m_nlevs) {
178  fields[field].resize(m_nlevs);
179  }
180  fields[field][lev].reset();
181  }
182  for (const auto& entry : fieldmap) {
183  if (entry.second.active) {
184  activate_field_map(entry.first);
185  }
186  }
187  for (auto& entry : radiation_input_map) {
188  if (static_cast<int>(entry.second.weighted.size()) < m_nlevs) {
189  entry.second.weighted.resize(m_nlevs);
190  }
191  entry.second.weighted[lev].reset();
192  }
193 
194  // Cached radiation fields point into the registered storage above.
195  // Rebuild the cache when this level's fields have been replaced.
196  rad_fields[lev].clear();
197  rad_output_fields[lev].clear();
198 
199  // Interpolate the new finer level using coarse data
200  if (lev > 0) {
201  amrex::Real time_for_fp = zero; // This is not actually used
202  amrex::Vector<amrex::Real> ftime = {time_for_fp, time_for_fp};
203  amrex::Vector<amrex::Real> ctime = {time_for_fp, time_for_fp};
204  amrex::IntVect ng_od(0, 0, 0); // ng outside domain
205  amrex::Interpolater* mapper = &amrex::cell_cons_interp;
206  amrex::Vector<amrex::MultiFab*> fmf;
207  amrex::Vector<amrex::MultiFab*> cmf;
208  if (u_star[lev] != nullptr && u_star[lev-1] != nullptr) {
209  amrex::InterpFromCoarseLevel(*u_star[lev], u_star[lev-1]->nGrowVect(), ng_od,
210  *u_star[lev-1], 0, 0, 2,
211  m_geom[lev-1], m_geom[lev],
212  refRatio[lev-1], &amrex::cell_cons_interp,
213  domain_bcs_type, BCVars::cons_bc);
214  amrex::InterpFromCoarseLevel(*t_star[lev], t_star[lev-1]->nGrowVect(), ng_od,
215  *t_star[lev-1], 0, 0, 1,
216  m_geom[lev-1], m_geom[lev],
217  refRatio[lev-1], &amrex::cell_cons_interp,
218  domain_bcs_type, BCVars::cons_bc);
219  amrex::InterpFromCoarseLevel(*q_star[lev], q_star[lev-1]->nGrowVect(), ng_od,
220  *q_star[lev-1], 0, 0, 1,
221  m_geom[lev-1], m_geom[lev],
222  refRatio[lev-1], &amrex::cell_cons_interp,
223  domain_bcs_type, BCVars::cons_bc);
224  amrex::InterpFromCoarseLevel(*t_surf[lev], t_surf[lev-1]->nGrowVect(), ng_od,
225  *t_surf[lev-1], 0, 0, 1,
226  m_geom[lev-1], m_geom[lev],
227  refRatio[lev-1], &amrex::cell_cons_interp,
228  domain_bcs_type, BCVars::cons_bc);
229 
230  fmf = {u_star[lev ].get(), u_star[lev ].get()};
231  cmf = {u_star[lev-1].get(), u_star[lev-1].get()};
232  amrex::FillPatchTwoLevels(*u_star[lev].get(), u_star[lev]->nGrowVect(), ng_od,
233  time_for_fp, cmf, ctime, fmf, ftime,
234  0, 0, 2, m_geom[lev-1], m_geom[lev],
235  refRatio[lev-1], mapper, domain_bcs_type,
237  fmf = {t_star[lev ].get(), t_star[lev ].get()};
238  cmf = {t_star[lev-1].get(), t_star[lev-1].get()};
239  amrex::FillPatchTwoLevels(*t_star[lev].get(), t_star[lev]->nGrowVect(), ng_od,
240  time_for_fp, cmf, ctime, fmf, ftime,
241  0, 0, 1, m_geom[lev-1], m_geom[lev],
242  refRatio[lev-1], mapper, domain_bcs_type,
244  fmf = {q_star[lev ].get(), q_star[lev ].get()};
245  cmf = {q_star[lev-1].get(), q_star[lev-1].get()};
246  amrex::FillPatchTwoLevels(*q_star[lev].get(), q_star[lev]->nGrowVect(), ng_od,
247  time_for_fp, cmf, ctime, fmf, ftime,
248  0, 0, 1, m_geom[lev-1], m_geom[lev],
249  refRatio[lev-1], mapper, domain_bcs_type,
251  fmf = {t_surf[lev ].get(), t_surf[lev ].get()};
252  cmf = {t_surf[lev-1].get(), t_surf[lev-1].get()};
253  amrex::FillPatchTwoLevels(*t_surf[lev].get(), t_surf[lev]->nGrowVect(), ng_od,
254  time_for_fp, cmf, ctime, fmf, ftime,
255  0, 0, 1, m_geom[lev-1], m_geom[lev],
256  refRatio[lev-1], mapper, domain_bcs_type,
258  }
259 
260  for (int field = 0; field < static_cast<int>(fields.size()); ++field) {
261  if (fields[field][lev] == nullptr || fields[field][lev-1] == nullptr) { continue; }
262  amrex::IntVect ngv = fields[field][lev-1]->nGrowVect(); ngv[2] = 0;
263  amrex::InterpFromCoarseLevel(*fields[field][lev], ngv, ng_od,
264  *fields[field][lev-1], 0, 0, 1,
265  m_geom[lev-1], m_geom[lev],
266  refRatio[lev-1], &amrex::cell_cons_interp,
267  domain_bcs_type, BCVars::cons_bc);
268  fmf = {fields[field][lev ].get(), fields[field][lev ].get()};
269  cmf = {fields[field][lev-1].get(), fields[field][lev-1].get()};
270  amrex::FillPatchTwoLevels(*fields[field][lev].get(), ngv, ng_od,
271  time_for_fp, cmf, ctime, fmf, ftime,
272  0, 0, 1, m_geom[lev-1], m_geom[lev],
273  refRatio[lev-1], mapper, domain_bcs_type,
275  }
276  }
277  }
constexpr amrex::Real zero
Definition: ERF_NumericalConstants.H:36
void ensure_surface_outputs(const int lev)
Allocates surface-output fields for one level.
Definition: ERF_SurfaceModel.H:975
bool m_surface_outputs_enabled
Definition: ERF_SurfaceModel.H:1050
@ cons_bc
Definition: ERF_IndexDefines.H:92
Here is the call graph for this function:

◆ is_field_mapped()

bool SurfaceModel::is_field_mapped ( int  lev,
SurfaceModelType  type,
int  field_idx,
const amrex::MultiFab *  mf 
) const

Checks whether a model field participates in a registered mapping.

Parameters
levAMR level to query.
typeLand or urban model owning the field.
field_idxIndex of the model field.
mfField pointer to test.
Returns
True when the field is part of a registered mapping.
494 {
495  for (const auto& entry : fieldmap) {
496  const Field& field = entry.second;
497 
498  const int mapped_idx = (type == SurfaceModelType::LAND)
499  ? field.map.first : field.map.second;
500  if (mapped_idx == field_idx) {
501  return true;
502  }
503 
504  // Pointer-based mappings have no model-field index. Match the
505  // registered pointer for the current level instead.
506  if (field.map.first == -1 && field.map.second == -1 && mf != nullptr) {
507  const auto& ptrs = (type == SurfaceModelType::LAND)
508  ? field.lsm_ptr : field.urb_ptr;
509  if (lev < static_cast<int>(ptrs.size()) && ptrs[lev] == mf) {
510  return true;
511  }
512  }
513  }
514 
515  for (const auto& entry : radiation_input_map) {
516  const auto& map = entry.second.map;
517  const int mapped_idx = (type == SurfaceModelType::LAND)
518  ? map.first : map.second;
519  if (mapped_idx == field_idx) {
520  return true;
521  }
522  }
523 
524  return false;
525 }
if(l_use_mynn &&start_comp<=RhoKE_comp &&end_comp >=RhoKE_comp)
Definition: ERF_AddQKESources.H:2
real(c_double), private map
Definition: ERF_module_mp_morr_two_moment.F90:222
Here is the call graph for this function:

◆ mark_fields_valid()

void SurfaceModel::mark_fields_valid ( )
inline

Marks the registered surface fields as filled by an actual model advance.

502 { m_fields_are_valid = true; }

◆ ReadCheckpoint()

void SurfaceModel::ReadCheckpoint ( const std::string &  checkpointname)

Restores surface-model state from a checkpoint file.

Parameters
checkpointnameCheckpoint file name.
948 {
951  auto check_start = amrex::second();
952 
953  amrex::Print() << " Reading SurfaceModel checkpoint " << std::endl;
954 
955  auto checkpoint_error = [&] (const std::string& message) {
956  amrex::Abort("SurfaceModel checkpoint '" + checkpointname + "': " + message);
957  };
958 
959  // Header
960  const std::string File(checkpointname + "/SurfaceModel_Header");
961  if (!amrex::FileExists(File)) {
962  checkpoint_error("missing SurfaceModel_Header");
963  }
964 
965  Vector<char> fileCharPtr;
966  ParallelDescriptor::ReadAndBcastFile(File, fileCharPtr);
967  std::string fileCharPtrString(fileCharPtr.dataPtr(), fileCharPtr.size());
968  std::istringstream is(fileCharPtrString, std::istringstream::in);
969 
970  std::string line;
971 
972  // read in title line
973  if (!std::getline(is, line) || line != "Checkpoint file for SurfaceModel") {
974  checkpoint_error("invalid or truncated header title");
975  }
976 
977  // read in number of levels
978  int chk_nlevs = -1;
979  if (!(is >> chk_nlevs) || chk_nlevs < 1) {
980  checkpoint_error("invalid number of levels");
981  }
982 
983  // read in flags
984  bool chk_use_urban = false;
985  bool chk_use_land = false;
986  bool chk_export_fluxes = false;
987  bool chk_weights_updated = false;
988  bool chk_fields_are_valid = false;
989  if (!(is >> chk_use_urban >> chk_use_land >> chk_export_fluxes >> chk_weights_updated
990  >> chk_fields_are_valid)) {
991  checkpoint_error("invalid flags in header");
992  }
993  GotoNextLine(is);
994 
995  if (chk_nlevs != m_nlevs) {
996  checkpoint_error("level count mismatch: checkpoint has " + std::to_string(chk_nlevs) +
997  ", current SurfaceModel has " + std::to_string(m_nlevs));
998  }
999  if (chk_use_urban != m_use_urban || chk_use_land != m_use_land ||
1000  chk_export_fluxes != m_export_fluxes) {
1001  checkpoint_error("model configuration flags do not match the current SurfaceModel");
1002  }
1003 
1004  // Read box arrays into temporaries so current runtime geometry is not
1005  // overwritten before it has been validated.
1006  amrex::Vector<amrex::BoxArray> chk_ba(chk_nlevs);
1007  amrex::Vector<amrex::BoxArray> chk_ba2d(chk_nlevs);
1008  for (int lev = 0; lev < chk_nlevs; ++lev) {
1009  chk_ba[lev].readFrom(is);
1010  if (!is) checkpoint_error("invalid 3D BoxArray for level " + std::to_string(lev));
1011  GotoNextLine(is);
1012  }
1013  GotoNextLine(is);
1014  for (int lev = 0; lev < chk_nlevs; ++lev) {
1015  chk_ba2d[lev].readFrom(is);
1016  if (!is) checkpoint_error("invalid 2D BoxArray for level " + std::to_string(lev));
1017  GotoNextLine(is);
1018  }
1019  GotoNextLine(is);
1020 
1021  // The checkpointed fields are read back on the decomposition they were written with and
1022  // then redistributed onto the live one, so the two BoxArrays need not match box for box.
1023  // They do have to cover the same index space: ERF turns regridding of level 0 on by itself
1024  // when a restart uses more ranks than level 0 has boxes (see ERF::restart), which changes
1025  // the boxes but not the region they tile.
1026  const std::string coverage_hint =
1027  "; the SurfaceModel checkpoint can be read back on a different decomposition, but not "
1028  "on a different domain -- check that the restart uses the same grid extents and "
1029  "refinement as the run that wrote the checkpoint";
1030  amrex::Vector<amrex::DistributionMapping> chk_dmap(chk_nlevs);
1031  for (int lev = 0; lev < chk_nlevs; ++lev) {
1032  if (chk_ba[lev].minimalBox() != m_ba[lev].minimalBox()) {
1033  checkpoint_error("3D domain coverage mismatch at level " + std::to_string(lev) +
1034  coverage_hint);
1035  }
1036  if (chk_ba2d[lev].minimalBox() != m_ba2d[lev].minimalBox()) {
1037  checkpoint_error("2D domain coverage mismatch at level " + std::to_string(lev) +
1038  coverage_hint);
1039  }
1040  chk_dmap[lev].define(chk_ba2d[lev]);
1041  }
1042 
1043  // Read in LSM fields
1044  if (!std::getline(is, line)) {
1045  checkpoint_error("missing LSM field list");
1046  }
1047  amrex::Vector<int> chk_lsm_fields;
1048  std::istringstream lsm_stream(line);
1049  int field_idx = -1;
1050  while (lsm_stream >> field_idx) chk_lsm_fields.push_back(field_idx);
1051 
1052  // Read in Urban fields
1053  if (!std::getline(is, line)) {
1054  checkpoint_error("missing urban field list");
1055  }
1056  amrex::Vector<int> chk_urban_fields;
1057  std::istringstream urban_stream(line);
1058  while (urban_stream >> field_idx) chk_urban_fields.push_back(field_idx);
1059  GotoNextLine(is);
1060 
1061  if (chk_lsm_fields != lsm_fields || chk_urban_fields != urban_fields) {
1062  checkpoint_error("model field lists do not match the current SurfaceModel");
1063  }
1064 
1065  // Read number of mapped fields
1066  int nfields = 0;
1067  if (!(is >> nfields) || nfields < 0) {
1068  checkpoint_error("invalid mapped-field count");
1069  }
1070  GotoNextLine(is);
1071 
1072  // Read any mapped fields
1073  if (nfields != static_cast<int>(fieldmap.size())) {
1074  checkpoint_error("mapped-field count does not match the current SurfaceModel");
1075  }
1076 
1077  // The field mapping should already be created before the restart, but verify consistency with the file
1078  amrex::Vector<std::string> checkpoint_field_names;
1079  for (int i = 0; i < nfields; i++) {
1080  if (!std::getline(is, line)) {
1081  checkpoint_error("missing mapped-field entry " + std::to_string(i));
1082  }
1083  std::istringstream lis(line);
1084 
1085  std::string field_name;
1086  int chk_mf_ind = -1;
1087  int lsm_ind = -1;
1088  int urb_ind = -1;
1089  int fill_bound_int = -1;
1090  if (!(lis >> field_name >> chk_mf_ind >> lsm_ind >> urb_ind >> fill_bound_int) ||
1091  chk_mf_ind < 0 || lsm_ind < -1 || urb_ind < -1 ||
1092  (fill_bound_int != 0 && fill_bound_int != 1)) {
1093  checkpoint_error("invalid mapped-field entry for '" + field_name + "'");
1094  }
1095  const bool fill_bound = (fill_bound_int != 0);
1096 
1097  auto field_it = fieldmap.find(field_name);
1098  if (field_it == fieldmap.end()) {
1099  checkpoint_error("checkpoint mapping '" + field_name + "' is not registered");
1100  }
1101  for (const auto& seen_name : checkpoint_field_names) {
1102  if (seen_name == field_name) {
1103  checkpoint_error("duplicate mapped-field entry for '" + field_name + "'");
1104  }
1105  }
1106  checkpoint_field_names.push_back(field_name);
1107  const auto &field = field_it->second;
1108  if (field.mf_ind < 0 || field.mf_ind >= static_cast<int>(fields.size())) {
1109  checkpoint_error("current mapped-field storage is invalid for '" + field_name + "'");
1110  }
1111  if (static_cast<int>(fields[field.mf_ind].size()) != m_nlevs) {
1112  checkpoint_error("current mapped-field level storage is invalid for '" + field_name + "'");
1113  }
1114  if (field.map.first != lsm_ind || field.map.second != urb_ind ||
1115  field.fill_bound != fill_bound) {
1116  checkpoint_error("mapped-field metadata mismatch for '" + field_name + "'");
1117  }
1118  if (lsm_ind == -1 && urb_ind == -1) {
1119  if (static_cast<int>(field.lsm_ptr.size()) != m_nlevs ||
1120  static_cast<int>(field.urb_ptr.size()) != m_nlevs) {
1121  checkpoint_error("pointer mapping has the wrong number of levels for '" + field_name + "'");
1122  }
1123  for (int lev = 0; lev < m_nlevs; ++lev) {
1124  if (fields[field.mf_ind][lev] == nullptr) {
1125  checkpoint_error("mapped-field storage is missing at level " +
1126  std::to_string(lev) + " for '" + field_name + "'");
1127  }
1128  }
1129  } else {
1130  for (int lev = 0; lev < m_nlevs; ++lev) {
1131  const bool invalid_land_index =
1132  !lsm_data_lev[lev].empty() &&
1133  lsm_ind >= static_cast<int>(lsm_data_lev[lev].size());
1134  const bool invalid_urban_index =
1135  !urban_data_lev[lev].empty() &&
1136  urb_ind >= static_cast<int>(urban_data_lev[lev].size());
1137  if (invalid_land_index || invalid_urban_index) {
1138  checkpoint_error("mapped-field index is out of range for '" + field_name + "'");
1139  }
1140  if (fields[field.mf_ind][lev] == nullptr) {
1141  checkpoint_error("mapped-field storage is missing at level " +
1142  std::to_string(lev) + " for '" + field_name + "'");
1143  }
1144  }
1145  }
1146  amrex::ignore_unused(chk_mf_ind);
1147  }
1148 
1149  const std::string prefix = "SurfaceModel_";
1150 
1151  IntVect ng = IntVect(1,1,0);
1152  auto validate_multifab_header = [&] (const std::string& name, int lev, int ncomp) {
1153  const std::string header_name = name + "_H";
1154  if (!amrex::FileExists(header_name)) {
1155  checkpoint_error("missing MultiFab header '" + header_name + "'");
1156  }
1157 
1158  Vector<char> header_data;
1159  ParallelDescriptor::ReadAndBcastFile(header_name, header_data);
1160  std::istringstream header_stream(
1161  std::string(header_data.dataPtr(), header_data.size()), std::istringstream::in);
1162  VisMF::Header header;
1163  if (!(header_stream >> header)) {
1164  checkpoint_error("invalid MultiFab header '" + header_name + "'");
1165  }
1166  if (header.m_ncomp != ncomp || header.m_ngrow != ng ||
1167  !(header.m_ba == chk_ba2d[lev])) {
1168  checkpoint_error("MultiFab layout mismatch for level " + std::to_string(lev) +
1169  " in '" + name + "'");
1170  }
1171  if (static_cast<amrex::Long>(header.m_fod.size()) != chk_ba2d[lev].size()) {
1172  checkpoint_error("MultiFab FAB count mismatch for level " + std::to_string(lev) +
1173  " in '" + name + "'");
1174  }
1175  const std::string data_dir = VisMF::DirName(name);
1176  for (const auto& fab_file : header.m_fod) {
1177  if (!amrex::FileExists(data_dir + fab_file.m_name)) {
1178  checkpoint_error("missing MultiFab data file '" + data_dir + fab_file.m_name + "'");
1179  }
1180  }
1181  };
1182 
1183  // Validate all required data headers before changing any runtime fields.
1184  for (int lev = 0; lev < m_nlevs; ++lev) {
1185  validate_multifab_header(MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "ustar"), lev, 2);
1186  validate_multifab_header(MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "wavg"), lev, 2);
1187  validate_multifab_header(MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "tstar"), lev, 1);
1188  validate_multifab_header(MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "qstar"), lev, 1);
1189  validate_multifab_header(MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "tsurf"), lev, 1);
1190  for (const auto &field : fieldmap) {
1191  validate_multifab_header(
1192  MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "_f_" + field.first), lev, 1);
1193  }
1194  }
1195 
1196  //
1197  // Move a field that was read on the checkpointed layout onto the live one. When the two
1198  // layouts are identical this is the same local copy it always was, so an unchanged restart
1199  // stays bitwise identical; otherwise the data is redistributed.
1200  //
1201  // The redistribution is done in two passes because the checkpoint carries ghost cells and
1202  // a single ParallelCopy over grown source boxes would leave the outcome up to the order in
1203  // which overlapping sources happen to be applied. The first pass seeds everything,
1204  // including the ghost cells outside a non-periodic physical boundary, which no amount of
1205  // valid-region copying can reach. The second pass then lays the valid data over the top,
1206  // so wherever a cell is covered by a valid source cell that value wins.
1207  //
1208  auto redistribute = [&] (MultiFab& dst, const MultiFab& src, int ncomp, int lev)
1209  {
1210  if (dst.boxArray() == src.boxArray() && dst.DistributionMap() == src.DistributionMap()) {
1211  MultiFab::Copy(dst, src, 0, 0, ncomp, ng);
1212  return;
1213  }
1214  dst.ParallelCopy(src, 0, 0, ncomp, ng, ng);
1215  dst.ParallelCopy(src, 0, 0, ncomp, IntVect(0), ng, m_geom2d[lev].periodicity());
1216  };
1217 
1218  for (int lev = 0; lev < m_nlevs; lev++) {
1219  {
1220  MultiFab mf(chk_ba2d[lev],chk_dmap[lev],2,ng);
1221  VisMF::Read(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "ustar"));
1222  redistribute(*(u_star[lev]),mf,2,lev);
1223 
1224  VisMF::Read(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "wavg"));
1225  ensure_weight_factors(lev);
1226  redistribute(*wavg[lev],mf,2,lev);
1227  }
1228 
1229  MultiFab mf(chk_ba2d[lev],chk_dmap[lev],1,ng);
1230 
1231  VisMF::Read(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "tstar"));
1232  redistribute(*t_star[lev],mf,1,lev);
1233 
1234  VisMF::Read(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "qstar"));
1235  redistribute(*q_star[lev],mf,1,lev);
1236 
1237  VisMF::Read(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "tsurf"));
1238  redistribute(*t_surf[lev],mf,1,lev);
1239 
1240  for (auto &field : fieldmap) {
1241  VisMF::Read(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "_f_" + field.first));
1242  redistribute(*(fields[field.second.mf_ind][lev]), mf, 1, lev);
1243  }
1244  }
1245 
1246  m_weights_updated = chk_weights_updated;
1247  // Restore whether the surface models had already integrated a step when the checkpoint
1248  // was written. Without this a restart would spend its first step treating the
1249  // checkpointed u*/t*/q* as unfilled and fall back to the MOST values instead.
1250  m_fields_are_valid = chk_fields_are_valid;
1251 
1254 
1255  auto check_end = amrex::second() - check_start;
1256  ParallelDescriptor::ReduceRealMax(check_end,ParallelDescriptor::IOProcessorNumber());
1257  amrex::Print() << " SurfaceModel Checkpoint load time = " << check_end << " seconds." << '\n';
1258 }
static void GotoNextLine(std::istream &is)
Advances an input stream to the next line.
Definition: ERF_SurfaceModel.cpp:824
void release_transient_surface_outputs()
Releases surface outputs that have no persistent consumer.
Definition: ERF_SurfaceModel.H:993
bool m_use_urban
Definition: ERF_SurfaceModel.H:1011
void activate_all_field_maps(const bool persistent=true)
Activates all registered mapped fields for an output consumer.
Definition: ERF_SurfaceModel.cpp:659
void request_surface_outputs(const bool persistent=true)
Enables and materializes the surface outputs consumed by ERF.
Definition: ERF_SurfaceModel.H:476
void deactivate_transient_field_maps()
Disables and releases mapped fields used only by the last output.
Definition: ERF_SurfaceModel.cpp:666
bool m_use_land
Definition: ERF_SurfaceModel.H:1012

◆ register_field_map() [1/2]

void SurfaceModel::register_field_map ( std::string  name,
amrex::Vector< amrex::MultiFab * > &  lsm_lev_mf,
amrex::Vector< amrex::MultiFab * > &  urb_lev_mf,
bool  fill_boundary = false 
)

Registers a field map using per-level land and urban pointers.

Parameters
nameCommon name used to retrieve the mapped field.
lsm_lev_mfLand-model fields, indexed by AMR level.
urb_lev_mfUrban-model fields, indexed by AMR level.
fill_boundaryWhether to fill mapped-field boundaries.
591 {
592  // Register a field using explicit data pointers, rather than indices into lsm_data and urban_data
593  AMREX_ALWAYS_ASSERT(lsm_lev_mf.size() > 0);
594  amrex::Print() << " adding mapping between LSM<->Urban mfs <"<<lsm_lev_mf[0] << "," << urb_lev_mf[0] << "> to common surface name " << name << std::endl;
595 
596  AMREX_ALWAYS_ASSERT(lsm_lev_mf.size() == urb_lev_mf.size());
597  if (fieldmap.find(name) == fieldmap.end()) {
598 
599  Field field;
600  field.map = std::pair<int,int>(-1,-1);
601  field.mf_ind = -1;
602  field.fill_bound = fill_boundary;
603  field.lsm_ptr = lsm_lev_mf;
604  field.urb_ptr = urb_lev_mf;
605 
606  // Storage is allocated when a consumer activates this mapping.
607  amrex::Vector<std::unique_ptr<amrex::MultiFab>> mf_lev(m_nlevs);
608  fields.push_back(std::move(mf_lev));
609  field.mf_ind = fields.size() - 1;
610 
611  amrex::Print() << " -- created at ind = " << field.mf_ind << std::endl;
612 
613  fieldmap.insert({name, field});
614  } else {
615  return;
616  }
617 }
Here is the call graph for this function:

◆ register_field_map() [2/2]

void SurfaceModel::register_field_map ( std::string  name,
const std::pair< int, int > &  lsm_urb_map,
bool  fill_boundary = false 
)

Registers a field map using land and urban field indices.

Parameters
nameCommon name used to retrieve the mapped field.
lsm_urb_mapPair of land and urban field indices.
fill_boundaryWhether to fill mapped-field boundaries.
566 {
567  amrex::Print() << " adding mapping between LSM<->Urban fields <"<<lsm_urb_map.first << "," << lsm_urb_map.second << "> to common surface name " << name << std::endl;
568 
569  AMREX_ALWAYS_ASSERT(!(lsm_urb_map.first == -1 && lsm_urb_map.second == -1));
570  if (fieldmap.find(name) == fieldmap.end()) {
571 
572  Field field;
573  field.map = lsm_urb_map;
574  field.mf_ind = -1;
575  field.fill_bound = fill_boundary;
576 
577  // Storage is allocated when a consumer activates this mapping.
578  amrex::Vector<std::unique_ptr<amrex::MultiFab>> mf_lev(m_nlevs);
579  fields.push_back(std::move(mf_lev));
580  field.mf_ind = fields.size() - 1;
581 
582  amrex::Print() << " -- created at ind = " << field.mf_ind << std::endl;
583 
584  fieldmap.insert({name, field});
585  } else {
586  return;
587  }
588 }
Here is the call graph for this function:

◆ register_radiation_input()

void SurfaceModel::register_radiation_input ( const std::string &  name,
const std::pair< int, int > &  lsm_urb_map 
)

Registers a canonical radiation input mapping.

Parameters
nameCanonical radiation input name.
lsm_urb_mapPair of land and urban model field indices.
311 {
312  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(!(map.first == -1 && map.second == -1),
313  "A radiation input must have a provider mapping");
315  std::find(radnames.begin(), radnames.end(), name) != radnames.end(),
316  "Unknown canonical radiation input: " + name);
317  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(map.first >= -1 && map.second >= -1,
318  "Radiation input indices must be -1 or nonnegative");
319  if (map.first >= 0) {
320  AMREX_ALWAYS_ASSERT(map.first < static_cast<int>(m_lsm_names.size()));
321  }
322  if (map.second >= 0) {
323  AMREX_ALWAYS_ASSERT(map.second < static_cast<int>(m_urban_names.size()));
324  }
325  RadiationField field;
326  field.map = map;
327  field.weighted.resize(m_nlevs);
328  radiation_input_map[name] = std::move(field);
329  for (auto& fields_at_level : rad_fields) { fields_at_level.clear(); }
330 }
amrex::Vector< std::string > m_lsm_names
Definition: ERF_SurfaceModel.H:1093
amrex::Vector< std::string > m_urban_names
Definition: ERF_SurfaceModel.H:1094
Here is the call graph for this function:

◆ register_radiation_inputs()

void SurfaceModel::register_radiation_inputs ( const std::unordered_map< std::string, std::pair< int, int >> &  input_map)

Registers canonical radiation input mappings.

Parameters
input_mapCanonical names mapped to land and urban indices.
334 {
335  for (const auto& entry : input_map) {
336  register_radiation_input(entry.first, entry.second);
337  }
338 }
void register_radiation_input(const std::string &name, const std::pair< int, int > &lsm_urb_map)
Registers a canonical radiation input mapping.
Definition: ERF_SurfaceModel.cpp:309

◆ register_radiation_output()

void SurfaceModel::register_radiation_output ( const std::string &  name,
const std::pair< int, int > &  lsm_urb_map 
)

Registers a canonical radiation output mapping.

Parameters
nameCanonical radiation output name.
lsm_urb_mapPair of land and urban model field indices.
342 {
343  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(!(map.first == -1 && map.second == -1),
344  "A radiation output must have a provider mapping");
346  std::find(rad_output_names.begin(), rad_output_names.end(), name) != rad_output_names.end(),
347  "Unknown canonical radiation output: " + name);
348  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(map.first >= -1 && map.second >= -1,
349  "Radiation output indices must be -1 or nonnegative");
350  if (map.first >= 0) {
351  AMREX_ALWAYS_ASSERT(map.first < static_cast<int>(m_lsm_names.size()));
352  }
353  if (map.second >= 0) {
354  AMREX_ALWAYS_ASSERT(map.second < static_cast<int>(m_urban_names.size()));
355  }
356  for (int lev = 0; lev < m_nlevs; ++lev) {
357  if (map.first >= 0 && map.first < static_cast<int>(lsm_data_lev[lev].size())) {
359  }
360  if (map.second >= 0 && map.second < static_cast<int>(urban_data_lev[lev].size())) {
362  }
363  }
364  RadiationField field;
365  field.map = map;
366  radiation_output_map[name] = std::move(field);
367  for (auto& fields_at_level : rad_output_fields) { fields_at_level.clear(); }
368 }
Here is the call graph for this function:

◆ register_radiation_outputs()

void SurfaceModel::register_radiation_outputs ( const std::unordered_map< std::string, std::pair< int, int >> &  output_map)

Registers canonical radiation output mappings.

Parameters
output_mapCanonical names mapped to land and urban indices.
372 {
373  for (const auto& entry : output_map) {
374  register_radiation_output(entry.first, entry.second);
375  }
376 }
void register_radiation_output(const std::string &name, const std::pair< int, int > &lsm_urb_map)
Registers a canonical radiation output mapping.
Definition: ERF_SurfaceModel.cpp:340

◆ release_transient_surface_outputs()

void SurfaceModel::release_transient_surface_outputs ( )
inlineprivate

Releases surface outputs that have no persistent consumer.

994  {
995  if (m_surface_outputs_enabled) { return; }
996  for (int lev = 0; lev < m_nlevs; ++lev) {
999  continue;
1000  }
1001  m_surface_outputs_requested[lev] = false;
1002  u_star[lev].reset();
1003  t_star[lev].reset();
1004  q_star[lev].reset();
1005  t_surf[lev].reset();
1006  }
1007  }
bool m_surface_layer_outputs_enabled
Definition: ERF_SurfaceModel.H:1051

◆ request_surface_layer_outputs()

void SurfaceModel::request_surface_layer_outputs ( )
inline

Enables blended surface outputs needed by the SurfaceLayer.

489  {
491  for (int lev = 0; lev < m_nlevs; ++lev) {
493  m_surface_outputs_requested[lev] = true;
495  }
496  }
497  }
Here is the call graph for this function:

◆ request_surface_outputs()

void SurfaceModel::request_surface_outputs ( const bool  persistent = true)
inline

Enables and materializes the surface outputs consumed by ERF.

477  {
479  for (int lev = 0; lev < m_nlevs; ++lev) {
480  m_surface_outputs_requested[lev] = true;
482  }
483  }
Here is the call graph for this function:

◆ set_field_map_pointers()

void SurfaceModel::set_field_map_pointers ( const std::string &  name,
int  lev,
amrex::MultiFab *  lsm_mf,
amrex::MultiFab *  urban_mf 
)

Updates a pointer-based field mapping for one level.

Parameters
nameCommon name of the mapped field.
levAMR level to update.
lsm_mfLand-model field pointer, or nullptr.
urban_mfUrban-model field pointer, or nullptr.
622 {
623  auto field_it = fieldmap.find(name);
624  AMREX_ALWAYS_ASSERT(field_it != fieldmap.end());
625 
626  Field& field = field_it->second;
627  AMREX_ALWAYS_ASSERT(field.map.first == -1 && field.map.second == -1);
628  if (static_cast<int>(field.lsm_ptr.size()) < m_nlevs) {
629  field.lsm_ptr.resize(m_nlevs, nullptr);
630  field.urb_ptr.resize(m_nlevs, nullptr);
631  }
633  field.lsm_ptr[lev] = lsm_mf;
634  field.urb_ptr[lev] = urban_mf;
635 }
Here is the call graph for this function:

◆ set_model_data()

void SurfaceModel::set_model_data ( const int  lev,
const amrex::Vector< amrex::MultiFab * >  model_data,
const amrex::Vector< std::string > &  data_names,
SurfaceModelType  type 
)
inline

Registers model variables used in weighted averaging.

Parameters
levAMR level associated with the data.
model_dataPointers to the model variables.
data_namesNames corresponding to model_data.
typeWhether the data belong to the land or urban model.
288  {
289  if (type == SurfaceModelType::LAND) {
290  lsm_data_lev[lev].resize(model_data.size());
291  weighted_lsm_data_lev[lev].clear();
292  weighted_lsm_data_lev[lev].resize(model_data.size());
293  if (lev == 0) {
294  // only set names on first level, since they are the same for all levels
295  m_lsm_names.resize(model_data.size());
296  AMREX_ALWAYS_ASSERT(data_names.size() == model_data.size());
297  for (int i = 0; i < static_cast<int>(data_names.size()); ++i) {
298  m_lsm_names[i] = data_names[i];
299  }
300  }
301 
302  for (int i = 0; i < static_cast<int>(lsm_data_lev[lev].size()); ++i) {
303  AMREX_ALWAYS_ASSERT(model_data[i]);
304  lsm_data_lev[lev][i] = model_data[i];
305  }
306  m_use_land = true;
308  for (auto& fields_at_level : rad_fields) { fields_at_level.clear(); }
309  for (auto& fields_at_level : rad_output_fields) { fields_at_level.clear(); }
310  for (auto& entry : radiation_input_map) {
311  if (lev < static_cast<int>(entry.second.weighted.size())) {
312  entry.second.weighted[lev].reset();
313  }
314  }
315  } else if (type == SurfaceModelType::URBAN) {
316  urban_data_lev[lev].resize(model_data.size());
317  weighted_urban_data_lev[lev].clear();
318  weighted_urban_data_lev[lev].resize(model_data.size());
319  if (lev == 0) {
320  // only set names on first level, since they are the same for all levels
321  m_urban_names.resize(model_data.size());
322  AMREX_ALWAYS_ASSERT(data_names.size() == model_data.size());
323  for (int i = 0; i < static_cast<int>(data_names.size()); ++i) {
324  m_urban_names[i] = data_names[i];
325  }
326  }
327  for (int i = 0; i < static_cast<int>(urban_data_lev[lev].size()); ++i) {
328  AMREX_ALWAYS_ASSERT(model_data[i]);
329  urban_data_lev[lev][i] = model_data[i];
330  }
331  m_use_urban = true;
333  for (auto& fields_at_level : rad_fields) { fields_at_level.clear(); }
334  for (auto& fields_at_level : rad_output_fields) { fields_at_level.clear(); }
335  for (auto& entry : radiation_input_map) {
336  if (lev < static_cast<int>(entry.second.weighted.size())) {
337  entry.second.weighted[lev].reset();
338  }
339  }
340  }
341  }
void update_provider_mode(const int lev)
Updates the provider mode after model data are registered.
Definition: ERF_SurfaceModel.H:932
Here is the call graph for this function:

◆ set_model_fields()

void SurfaceModel::set_model_fields ( SurfaceModelType  type,
const amrex::Vector< int > &  field_indices,
const bool  use_fluxes = true 
)
inline

Selects the model fields that participate in weighted averaging.

When using fluxes, the first five elements map to u-stress, v-stress, heat flux, vapor flux, and surface temperature. Otherwise, the first four elements map to ustar, tstar, qstar, and surface temperature. A field index of -1 skips that field.

Parameters
typeWhether the fields belong to the land or urban model.
field_indicesIndices of the fields to average.
use_fluxesWhether the selected fields are fluxes.
397  {
398  const int nfields = static_cast<int>(field_indices.size());
399  if (!use_fluxes) {
400  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(nfields >= 4, "Must have at least four fields to average (ustar, tstar, qstar, tsurf)");
401  } else {
402  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(nfields >= 5, "Must have at least five fields to average (u-stress, v-stress, hfx, qfx, tsurf)");
403  }
404 
405  // Latch the export mode off the first model that registers, then make sure every
406  // subsequent model agrees: either all of them supply fluxes, or all of them supply
407  // u*, t*, q* directly. Taking the mode from the caller rather than assuming the
408  // default is what makes the non-flux path reachable at all.
409  if (!m_export_fluxes_set) {
410  m_export_fluxes = use_fluxes;
411  m_export_fluxes_set = true;
412  }
414  "All registered surface models must export either fluxes or u*/t*/q*, not a mix");
415 
416  const int nregistered = (type == SurfaceModelType::LAND)
417  ? static_cast<int>(m_lsm_names.size())
418  : static_cast<int>(m_urban_names.size());
419  for (const int field : field_indices) {
421  field == -1 || (field >= 0 && field < nregistered),
422  "Model field index must be -1 or within the registered provider data range");
423  }
424 
425  if (type == SurfaceModelType::LAND) {
426  lsm_fields = amrex::Vector<int>(field_indices);
428  for (auto& fields_at_level : weighted_lsm_data_lev) {
429  fields_at_level.clear();
430  }
431  } else if (type == SurfaceModelType::URBAN) {
432  urban_fields = amrex::Vector<int>(field_indices);
434  for (auto& fields_at_level : weighted_urban_data_lev) {
435  fields_at_level.clear();
436  }
437  }
438  }
bool m_export_fluxes_set
Definition: ERF_SurfaceModel.H:1015
Here is the call graph for this function:

◆ set_model_fluxes()

void SurfaceModel::set_model_fluxes ( const int  lev,
const amrex::Vector< amrex::MultiFab * >  model_fluxes,
const amrex::Vector< std::string > &  flux_names,
SurfaceModelType  type 
)
inline

Registers model fluxes as additional weighted-average inputs.

Parameters
levAMR level associated with the fluxes.
model_fluxesPointers to the model flux fields.
flux_namesNames corresponding to model_fluxes.
typeWhether the fluxes belong to the land or urban model.
353  {
354  amrex::Vector<amrex::MultiFab*>* model_data = nullptr;
355  amrex::Vector<std::string>* model_names = nullptr;
356 
357  if (type == SurfaceModelType::LAND) {
358  model_data = &lsm_data_lev[lev];
359  model_names = &m_lsm_names;
360  } else if (type == SurfaceModelType::URBAN) {
361  model_data = &urban_data_lev[lev];
362  model_names = &m_urban_names;
363  }
364 
365  AMREX_ALWAYS_ASSERT(model_data != nullptr);
366  AMREX_ALWAYS_ASSERT(model_names != nullptr);
367  AMREX_ALWAYS_ASSERT(flux_names.size() == model_fluxes.size());
368 
369  const int data_size = static_cast<int>(model_data->size());
370  model_data->resize(data_size + static_cast<int>(model_fluxes.size()));
371  if (lev == 0) {
372  model_names->resize(data_size + static_cast<int>(model_fluxes.size()));
373  }
374 
375  for (int i = 0; i < static_cast<int>(model_fluxes.size()); ++i) {
376  AMREX_ALWAYS_ASSERT(model_fluxes[i]);
377  (*model_data)[data_size + i] = model_fluxes[i];
378  if (lev == 0) {
379  (*model_names)[data_size + i] = flux_names[i];
380  }
381  }
382  }
Here is the call graph for this function:

◆ update_provider_mode()

void SurfaceModel::update_provider_mode ( const int  lev)
inlineprivate

Updates the provider mode after model data are registered.

Parameters
levAMR level whose provider mode should be updated.
933  {
934  const bool has_land = !lsm_data_lev[lev].empty();
935  const bool has_urban = !urban_data_lev[lev].empty();
936  if (has_land && has_urban) {
940  m_surface_outputs_requested[lev] = true;
942  }
943  } else if (has_land) {
945  wavg[lev].reset();
946  } else if (has_urban) {
948  wavg[lev].reset();
949  } else {
951  }
952  }

Referenced by set_model_data().

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

◆ validate_radiation_output_layout()

void SurfaceModel::validate_radiation_output_layout ( int  lev,
const amrex::MultiFab *  mf 
) const

Validates the layout contract for a canonical radiation output.

Parameters
levAMR level associated with the destination.
mfRadiation output destination to validate.
420 {
421  if (mf == nullptr) { return; }
422  BoxList horizontal_boxes = mf->boxArray().boxList();
423  // include the ghost cells here for SLM: surface values are exchanged at
424  // k = 0 which is a ghost cell for SLM.
425  const int k_grow = mf->nGrowVect()[2];
426  for (Box& box : horizontal_boxes) {
428  box.smallEnd(2) <= 0 && box.bigEnd(2) + k_grow >= 0,
429  "Radiation output destination must contain the k=0 surface plane "
430  "in its valid or ghost region");
431  box.setRange(2, 0);
432  }
433  const BoxArray horizontal_ba(std::move(horizontal_boxes));
435  horizontal_ba == m_ba2d[lev] && mf->DistributionMap() == m_dmap[lev],
436  "Radiation output destination must match the level horizontal layout");
437 }
Here is the call graph for this function:

◆ weight_average_fields()

void SurfaceModel::weight_average_fields ( int  lev,
amrex::MultiFab * const   
)

Applies the current land and urban weights to selected model fields.

Parameters
levAMR level to update.
urban_fracUrban fraction used to compute the weights.
680 {
681  const SurfaceProviderMode mode = m_provider_mode[lev];
682  const bool use_land = mode == SurfaceProviderMode::LandOnly ||
684  const bool use_urban = mode == SurfaceProviderMode::UrbanOnly ||
686 
687  for (auto &field : fieldmap)
688  {
689  if (!field.second.active) { continue; }
690  int mf_idx = field.second.mf_ind;
691  AMREX_ASSERT(mf_idx != -1);
692 
693  int lsm_idx = field.second.map.first;
694  int urb_idx = field.second.map.second;
695 
696  // whether we have a valid LSM multifab
697  bool valid_land = (use_land &&
698  lsm_idx != -1 &&
699  lsm_data_lev[lev][lsm_idx]);
700 
701  // whether we have a valid urban multifab
702  bool valid_urban = (use_urban &&
703  urb_idx != -1 &&
704  urban_data_lev[lev][urb_idx]);
705 
706  bool use_mf = false;
707  if (lsm_idx == -1 && urb_idx == -1) {
708  // use explicit MF ptrs rather than indices
709  use_mf = true;
710  valid_land = (use_land && field.second.lsm_ptr[lev]);
711  valid_urban = (use_urban && field.second.urb_ptr[lev]);
712  }
713 
714  for (MFIter mfi(*fields[mf_idx][lev], TileNoZ()); mfi.isValid(); ++mfi)
715  {
716  Box tbx = mfi.tilebox();
717  const auto weights_arr = (mode == SurfaceProviderMode::Both)
718  ? wavg[lev]->const_array(mfi) : Array4<const Real>{};
719 
720  // Calculate weight average into output
721  auto output_arr = fields[mf_idx][lev]->array(mfi);
722  const amrex::MultiFab *lsm_mf = (use_mf) ? field.second.lsm_ptr[lev] : (lsm_idx != -1 ? lsm_data_lev[lev][lsm_idx] : nullptr);
723  const amrex::MultiFab *urb_mf = (use_mf) ? field.second.urb_ptr[lev] : (urb_idx != -1 ? urban_data_lev[lev][urb_idx] : nullptr);
724 
725  auto lsm_data_arr = (valid_land) ? lsm_mf->const_array(mfi) : Array4<const Real>{};
726  auto urban_data_arr = (valid_urban) ? urb_mf->const_array(mfi) : Array4<const Real>{};
727 
728  if (valid_land && !valid_urban) {
729  // use solely land value
730  ParallelFor(tbx, [=] AMREX_GPU_DEVICE (int i, int j, int k)
731  {
732  output_arr(i, j, k) = lsm_data_arr(i, j, k);
733  });
734  } else if (!valid_land && valid_urban) {
735  // use solely urban value
736  ParallelFor(tbx, [=] AMREX_GPU_DEVICE (int i, int j, int k)
737  {
738  output_arr(i, j, k) = urban_data_arr(i, j, k);
739  });
740  } else {
741  ParallelFor(tbx, [=] AMREX_GPU_DEVICE (int i, int j, int k)
742  {
743  Real land = (lsm_data_arr) ? lsm_data_arr(i, j, k) * weights_arr(i, j, 0, SurfaceModelType::LAND) : 0.0;
744  Real urb = (urban_data_arr) ? urban_data_arr(i, j, k) * weights_arr(i, j, 0, SurfaceModelType::URBAN) : 0.0;
745 
746  output_arr(i, j, k) = land + urb;
747  });
748  }
749  }
750  if (field.second.fill_bound) {
751  fields[mf_idx][lev]->FillBoundary(m_geom[lev].periodicity());
752  }
753  //outputs[output_field]->FillBoundary(comp, 1, m_geom[lev].periodicity());
754  }
755 }
Here is the call graph for this function:

◆ weight_model_field()

void SurfaceModel::weight_model_field ( int  lev,
const amrex::MultiFab *  source,
amrex::MultiFab *  weighted,
SurfaceModelType  type 
)

Writes a weighted copy of a model field.

Parameters
levAMR level whose weights are used.
sourceModel-owned source field.
weightedSurfaceModel-owned destination field.
typeProvider type owning the source field.
46 {
47  AMREX_ASSERT(source != nullptr);
48  AMREX_ASSERT(weighted != nullptr);
49 
50  for (MFIter mfi(*source, TileNoZ()); mfi.isValid(); ++mfi)
51  {
52  Box tbx = mfi.tilebox();
53  const auto source_arr = source->const_array(mfi);
54  auto weighted_arr = weighted->array(mfi);
55  const bool use_weights = m_provider_mode[lev] == SurfaceProviderMode::Both;
56  const auto weights_arr = use_weights
57  ? wavg[lev]->const_array(mfi) : Array4<const Real>{};
58 
59  ParallelFor(tbx, [=] AMREX_GPU_DEVICE (int i, int j, int k)
60  {
61  weighted_arr(i, j, k) = use_weights
62  ? source_arr(i, j, k) * weights_arr(i, j, 0, type)
63  : source_arr(i, j, k);
64  });
65  }
66 
67  weighted->FillBoundary(m_geom[lev].periodicity());
68 }
Here is the call graph for this function:

◆ write_output()

void SurfaceModel::write_output ( const int &  finest_lev,
const amrex::Real &  time,
const std::string &  plot_prefix,
const amrex::Vector< int > &  level_steps,
const amrex::Vector< amrex::IntVect > &  ref_ratio 
)

Writes registered surface-model fields to plotfile output.

Parameters
finest_levFinest AMR level to write.
timeSimulation time associated with the output.
plot_prefixOutput plotfile prefix.
level_stepsNumber of steps represented at each level.
ref_ratioRefinement ratios between adjacent levels.
759 {
762  std::string plotfilename = amrex::Concatenate(plot_prefix + "2D_", level_steps[0], 5);
763 
764  const int nfields = (m_export_fluxes) ? 5 : 4;
765  // Surface outputs, land mask, urban fraction, and mapped fields.
766  const int noutput = nfields + 2 + fieldmap.size();
767  IntVect ng(0, 0, 0);
768 
769  amrex::Vector<std::string> varnames(nfields);
770 
771  amrex::Vector<amrex::MultiFab> fab(finest_lev+1);
772  for (int lev = 0; lev <= finest_lev; lev++) {
774  amrex::MultiFab* const outputs[] = {u_star[lev].get(), t_star[lev].get(), q_star[lev].get(), t_surf[lev].get()};
775  fab[lev].define(outputs[0]->boxArray(), m_dmap[lev], noutput, ng);
776 
777  //fab[lev].setVal(0.0);
778 
779  // Output weighted surface fluxes into ustar, tstar, qstar and surface temperature into tsurf
780  // TODO: make sure grids of urban and LSM inputs match
781  for (int field=0; field < nfields; field++)
782  {
783  int output_field = (m_export_fluxes && field > 0) ? field - 1 : field;
784  int comp = (m_export_fluxes && field < 2) ? field : 0;
785 
786  MultiFab::Copy(fab[lev], *(outputs[output_field]), comp, field, 1, ng);
787 
788  varnames[field] = field_names[field];
789  }
790 
791  int nout = nfields;
792 
793  MultiFab lmask_tmp = amrex::ToMultiFab(*m_lmask[lev]); // iMultiFab -> MultiFab
794  MultiFab::Copy(fab[lev], lmask_tmp, 0, nout, 1, ng);
795  nout++;
796  MultiFab::Copy(fab[lev], *get_wavg_factors(lev), SurfaceModelType::URBAN, nout, 1, ng);
797  nout++;
798 
799  if (lev == 0) {
800  varnames.push_back("lmask");
801  varnames.push_back("urb_frac");
802  }
803 
804  // Add any mapped fields
805  for (auto &field : fieldmap) {
806  MultiFab::Copy(fab[lev], *(fields[field.second.mf_ind][lev]), 0, nout, 1, ng);
807  if (lev == 0) {
808  varnames.push_back(field.first);
809  }
810  nout++;
811  }
812 
813  AMREX_ALWAYS_ASSERT(varnames.size() == noutput);
814  }
815 
816  amrex::WriteMultiLevelPlotfile(plotfilename, finest_lev+1, GetVecOfConstPtrs(fab), varnames, m_geom2d, time, level_steps, ref_ratio);
819 }
amrex::MultiFab * get_wavg_factors(const int &lev)
Returns the land and urban weighting factors for a level.
Definition: ERF_SurfaceModel.H:639
void calculate_weight_average(int lev, amrex::MultiFab *const urban_frac, bool update_derived=true)
Computes land and urban weights and applies them to registered fields.
Definition: ERF_SurfaceModel.cpp:71
static const std::vector< std::string > field_names
Definition: ERF_SurfaceModel.H:1112
Here is the call graph for this function:

◆ WriteCheckpoint()

void SurfaceModel::WriteCheckpoint ( const std::string &  checkpointname)

Writes surface-model state to a checkpoint file.

Parameters
checkpointnameCheckpoint file name.
831 {
834  auto check_start = amrex::second();
835 
836  // write header
837  if (ParallelDescriptor::IOProcessor()) {
838 
839  amrex::Print() << " Writing SurfaceModel checkpoint " << std::endl;
840 
841  std::string HeaderFileName(checkpointname + "/SurfaceModel_Header");
842  VisMF::IO_Buffer io_buffer(VisMF::IO_Buffer_Size);
843  std::ofstream HeaderFile;
844  HeaderFile.rdbuf()->pubsetbuf(io_buffer.dataPtr(), io_buffer.size());
845  HeaderFile.open(HeaderFileName.c_str(), std::ofstream::out |
846  std::ofstream::trunc |
847  std::ofstream::binary);
848  if(! HeaderFile.good()) {
849  FileOpenFailed(HeaderFileName);
850  }
851 
852  HeaderFile.precision(17);
853 
854  // write out title line
855  HeaderFile << "Checkpoint file for SurfaceModel\n";
856 
857  // write out number of levels
858  HeaderFile << m_nlevs << "\n";
859 
860  // write out flags
861  HeaderFile << m_use_urban << "\n";
862  HeaderFile << m_use_land << "\n";
863  HeaderFile << m_export_fluxes << "\n";
864  HeaderFile << m_weights_updated << "\n";
865  HeaderFile << m_fields_are_valid << "\n";
866  HeaderFile << "\n";
867 
868  // Write box arrays
869  for (int lev = 0; lev < m_nlevs; lev++) {
870  m_ba[lev].writeOn(HeaderFile);
871  HeaderFile << '\n';
872 
873  }
874  HeaderFile << '\n';
875  for (int lev = 0; lev < m_nlevs; lev++) {
876  m_ba2d[lev].writeOn(HeaderFile);
877  HeaderFile << '\n';
878  }
879  HeaderFile << '\n';
880 
881  // Write out fields
882  // LSM fields
883  for (int i = 0; i < static_cast<int>(lsm_fields.size()); ++i) {
884  HeaderFile << lsm_fields[i] << " ";
885  }
886  HeaderFile << '\n';
887 
888  // Urban fields
889  for (int i = 0; i < static_cast<int>(urban_fields.size()); ++i) {
890  HeaderFile << urban_fields[i] << " ";
891  }
892  HeaderFile << '\n';
893 
894  // Field mapping
895  HeaderFile << '\n';
896  HeaderFile << fieldmap.size() << "\n";
897  for (auto &field : fieldmap) {
898  // pointer fields are reconstructed at load
899  HeaderFile << field.first << " " << field.second.mf_ind << " " << field.second.map.first << " " << field.second.map.second << " " << field.second.fill_bound << '\n';
900  }
901  HeaderFile << '\n';
902  }
903 
904  amrex::ParallelDescriptor::Barrier();
905 
906  const std::string prefix = "SurfaceModel_";
907 
908  // Radiation input fields are derived from provider data and are intentionally
909  // rebuilt after restart rather than written to the checkpoint.
910  for (int lev = 0; lev < m_nlevs; lev++) {
911  IntVect ng(1,1,0);
913 
914  {
915  MultiFab mf(m_ba2d[lev],m_dmap[lev],2,ng);
916  MultiFab::Copy(mf,*u_star[lev],0,0,2,ng);
917  VisMF::Write(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "ustar"));
918 
919  MultiFab::Copy(mf,*get_wavg_factors(lev),0,0,2,ng);
920  VisMF::Write(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "wavg"));
921  }
922 
923  MultiFab mf(m_ba2d[lev],m_dmap[lev],1,ng);
924 
925  MultiFab::Copy(mf,*t_star[lev],0,0,1,ng);
926  VisMF::Write(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "tstar"));
927 
928  MultiFab::Copy(mf,*q_star[lev],0,0,1,ng);
929  VisMF::Write(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "qstar"));
930 
931  MultiFab::Copy(mf,*t_surf[lev],0,0,1,ng);
932  VisMF::Write(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "tsurf"));
933 
934  for (auto &field : fieldmap) {
935  MultiFab::Copy(mf, *(fields[field.second.mf_ind][lev]), 0, 0, 1, ng);
936  VisMF::Write(mf, MultiFabFileFullPrefix(lev, checkpointname, "Level_", prefix + "_f_" + field.first));
937  }
938  }
939 
940  auto check_end = amrex::second() - check_start;
943  ParallelDescriptor::ReduceRealMax(check_end,ParallelDescriptor::IOProcessorNumber());
944  amrex::Print() << " SurfaceModel Checkpoint write time = " << check_end << " seconds." << '\n';
945 }

Member Data Documentation

◆ field_names

const std::vector<std::string> SurfaceModel::field_names
inlinestaticprivate
Initial value:
= {
"uflux",
"vflux",
"hfx",
"qfx",
"tskin",
}

◆ fieldmap

std::unordered_map<std::string, Field> SurfaceModel::fieldmap
private

Referenced by get_field(), and initialize_for_level().

◆ fields

amrex::Vector<amrex::Vector<std::unique_ptr<amrex::MultiFab> > > SurfaceModel::fields
private

Referenced by get_field(), and initialize_for_level().

◆ lsm_data_lev

amrex::Vector<amrex::Vector<amrex::MultiFab*> > SurfaceModel::lsm_data_lev
private

◆ lsm_fields

amrex::Vector<int> SurfaceModel::lsm_fields
private

◆ m_ba

amrex::Vector<amrex::BoxArray> SurfaceModel::m_ba
private

Referenced by initialize_for_level().

◆ m_ba2d

amrex::Vector<amrex::BoxArray> SurfaceModel::m_ba2d
private

◆ m_dmap

amrex::Vector<amrex::DistributionMapping> SurfaceModel::m_dmap
private

◆ m_export_fluxes

bool SurfaceModel::m_export_fluxes = true
private

◆ m_export_fluxes_set

bool SurfaceModel::m_export_fluxes_set = false
private

Referenced by set_model_fields().

◆ m_fields_are_valid

bool SurfaceModel::m_fields_are_valid = false
private

◆ m_geom

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

◆ m_geom2d

amrex::Vector<amrex::Geometry> SurfaceModel::m_geom2d
private

◆ m_last_urban_frac

amrex::Vector<amrex::MultiFab*> SurfaceModel::m_last_urban_frac
private

◆ m_lmask

amrex::Vector<amrex::iMultiFab*> SurfaceModel::m_lmask
private

◆ m_lsm_names

amrex::Vector<std::string> SurfaceModel::m_lsm_names
private

◆ m_nlevs

◆ m_output_fields_registered

amrex::GpuArray<bool, 2> SurfaceModel::m_output_fields_registered {{false, false}}
private

Referenced by set_model_fields(), and SurfaceModel().

◆ m_provider_mode

◆ m_surface_layer_outputs_enabled

bool SurfaceModel::m_surface_layer_outputs_enabled = false
private

◆ m_surface_outputs_enabled

bool SurfaceModel::m_surface_outputs_enabled = false
private

◆ m_surface_outputs_requested

amrex::Vector<int> SurfaceModel::m_surface_outputs_requested
private

◆ m_urban_names

amrex::Vector<std::string> SurfaceModel::m_urban_names
private

◆ m_use_land

bool SurfaceModel::m_use_land = false
private

Referenced by set_model_data().

◆ m_use_urban

bool SurfaceModel::m_use_urban = false
private

Referenced by set_model_data().

◆ m_weights_updated

bool SurfaceModel::m_weights_updated = false
private

◆ q_star

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

◆ rad_fields

amrex::Vector<amrex::Vector<const amrex::MultiFab*> > SurfaceModel::rad_fields
private

◆ rad_output_fields

amrex::Vector<amrex::Vector<amrex::MultiFab*> > SurfaceModel::rad_output_fields
private

◆ rad_output_names

const std::vector<std::string> SurfaceModel::rad_output_names
inlinestaticprivate
Initial value:
= {
"cos_zenith_angle", "sw_flux_dn", "sw_flux_dn_dir_vis",
"sw_flux_dn_dir_nir", "sw_flux_dn_dif_vis", "sw_flux_dn_dif_nir",
"lw_flux_dn"}

◆ radiation_input_map

std::unordered_map<std::string, RadiationField> SurfaceModel::radiation_input_map
private

◆ radiation_output_map

std::unordered_map<std::string, RadiationField> SurfaceModel::radiation_output_map
private

◆ radnames

const std::vector<std::string> SurfaceModel::radnames = {"tskin", "emiss", "albedo_vis", "albedo_nir", "albedo_vis_diff", "albedo_nir_diff"}
inlinestaticprivate

◆ t_star

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

◆ t_surf

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

◆ u_star

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

◆ urban_data_lev

amrex::Vector<amrex::Vector<amrex::MultiFab*> > SurfaceModel::urban_data_lev
private

◆ urban_fields

amrex::Vector<int> SurfaceModel::urban_fields
private

◆ urban_frac_lev

amrex::Vector<amrex::MultiFab*> SurfaceModel::urban_frac_lev
private

◆ wavg

amrex::Vector<std::unique_ptr<amrex::MultiFab> > SurfaceModel::wavg
private

◆ weighted_lsm_data_lev

amrex::Vector<amrex::Vector<std::unique_ptr<amrex::MultiFab> > > SurfaceModel::weighted_lsm_data_lev
private

◆ weighted_urban_data_lev

amrex::Vector<amrex::Vector<std::unique_ptr<amrex::MultiFab> > > SurfaceModel::weighted_urban_data_lev
private

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