Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Layer and DomainLayer Formulation Management #752

Merged
merged 13 commits into from
Apr 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 88 additions & 0 deletions include/core/DomainLayer.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
#ifndef NGEN_DOMAIN_LAYER
#define NGEN_DOMAIN_LAYER

#include "Catchment_Formulation.hpp"
#include "Layer.hpp"
#include "State_Exception.hpp"

namespace ngen
{
class DomainLayer : public Layer
{
public:
DomainLayer() = delete;
/**
* @brief Construct a new Domain Layer object.
*
* Unlike HY_Features types, the feature relationship with a DomainLayer is
* indirect. The @p features collection defines features associated
* (e.g. overlapping) with the Domain. The domain must be further queried
* in order to provide specific information at a particular feature,
* e.g. a catchment which this domain overlaps with, or a nexus location
* the domain may contribute to directly/indirectly via the catchment.
*
* A domain layer associated with a set of catchment features will need to have
* outputs of the domain resampled/aggregated to the catchment.
*
* Currently unsupported, but a future extension of the DomainLayer is interactions
* beetween two or more generic DomainLayers, perhaps each with its own internal grid,
* and resampling between the layers would be required similarly to resampling from
* a DomainLayer to the HY_Features catchment domain.
*
* @param desc LayerDescription for the domain layer
* @param s_t Simulation_Time object associated with the domain layer
* @param features collection of HY_Features associated with the domain
* @param idx index of the layer
* @param formulation Formulation associated with the domain
*/
DomainLayer(
const LayerDescription& desc,
const Simulation_Time& s_t,
feature_type& features,
long idx,
std::shared_ptr<realization::Catchment_Formulation> formulation):
Layer(desc, s_t, features, idx), formulation(formulation)
{
formulation->write_output("Time Step,""Time,"+formulation->get_output_header_line(",")+"\n");
}

/***
* @brief Run one simulation timestep for this model associated with the domain
*
* A domain layer update simply advances the attached domain formulation(s) in time and records
* the BMI accessible outputs of the domain formulation. Since this is NOT a HY_Features
* concept/class, it doesn't directly associate with HY_Features types (e.g. catchments, nexus, ect)
*
* Any required connection to other components, e.g. providing inputs to a catchment feature,
* is not yet implemented in this class.
*/
void update_models() override{
std::string current_timestamp = simulation_time.get_timestamp(output_time_index);
try{
formulation->get_response(output_time_index, simulation_time.get_output_interval_seconds());
}
catch(models::external::State_Exception& e){
std::string msg = e.what();
msg = msg+" at timestep "+std::to_string(output_time_index)
+" ("+current_timestamp+")"
+" at domain layer "+description.name
+" (layer id: "+std::to_string(description.id)+")";
throw models::external::State_Exception(msg);
}
std::string output = std::to_string(output_time_index)+","+current_timestamp+","+
formulation->get_output_line_for_timestep(output_time_index)+"\n";
formulation->write_output(output);
++output_time_index;
if ( output_time_index < simulation_time.get_total_output_times() )
{
simulation_time.advance_timestep();
}

}

private:
std::shared_ptr<realization::Catchment_Formulation> formulation;
};
}

#endif // NGEN_DOMAIN_LAYER
24 changes: 24 additions & 0 deletions include/core/Layer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,27 @@ namespace ngen

}

/**
* @brief Construct a minimum layer object
*
* @param desc
* @param s_t
* @param f
* @param idx
*/
Layer(
const LayerDescription& desc,
const Simulation_Time& s_t,
feature_type& f,
long idx) :
description(desc),
simulation_time(s_t),
features(f),
output_time_index(idx)
{

}

virtual ~Layer() {}

/***
Expand Down Expand Up @@ -138,9 +159,12 @@ namespace ngen
protected:

const LayerDescription description;
//TODO is this really required at the top level?
//See "minimum" constructor above used for DomainLayer impl...
const std::vector<std::string> processing_units;
Simulation_Time simulation_time;
feature_type& features;
//TODO is this really required at the top level? or can this be moved to SurfaceLayer?
const geojson::GeoJSON catchment_data;
long output_time_index;

Expand Down
86 changes: 42 additions & 44 deletions include/realizations/catchment/Formulation_Manager.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,13 @@
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <FeatureBuilder.hpp>
#include "JSONProperty.hpp"
#include "features/Features.hpp"
#include <FeatureCollection.hpp>
#include "Formulation_Constructors.hpp"
#include "LayerData.hpp"
#include "realizations/config/time.hpp"
#include "realizations/config/routing.hpp"
#include "realizations/config/config.hpp"
#include "realizations/config/layer.hpp"

namespace realization {

Expand Down Expand Up @@ -80,56 +79,44 @@ namespace realization {

// try to get the json node
auto layers_json_array = tree.get_child_optional("layers");

// check to see if the node existed
if (!layers_json_array) {
// layer description struct
ngen::LayerDescription layer_desc;

// extract and store layer data from the json
layer_desc.name = "surface layer";
layer_desc.id = 0;
layer_desc.time_step = 3600;
layer_desc.time_step_units = "s";

// add the layer to storage
layer_storage.put_layer(layer_desc, layer_desc.id);
}
else
{
//Create the default surface layer
config::Layer layer;
// layer description struct
ngen::LayerDescription layer_desc;
layer_desc = layer.get_descriptor();
// add the default surface layer to storage
layer_storage.put_layer(layer_desc, layer_desc.id);

if(layers_json_array){

for (std::pair<std::string, boost::property_tree::ptree> layer_config : *layers_json_array)
{
// layer description struct
ngen::LayerDescription layer_desc;

// extract and store layer data from the json
layer_desc.name = layer_config.second.get<std::string>("name");
layer_desc.id = layer_config.second.get<int>("id");
layer_desc.time_step = layer_config.second.get<double>("time_step");
boost::optional<std::string> layer_units = layer_config.second.get_optional<std::string>("time_step_units");
if (*layer_units == "") layer_units = "s";
layer_desc.time_step_units = *layer_units;

// check to see if this layer was allready defined
if (layer_storage.exists(layer_desc.id) )
{
std::string message = "A layer with id = ";
message += std::to_string(layer_desc.id);
message += " was defined more than once";

std::runtime_error r_error(message);

throw r_error;
}
layer = config::Layer(layer_config.second);
layer_desc = layer.get_descriptor();

// add the layer to storage
layer_storage.put_layer(layer_desc, layer_desc.id);

// debuggin print to see parsed data
std::cout << layer_desc.name << ", " << layer_desc.id << ", " << layer_desc.time_step << ", " << layer_desc.time_step_units << "\n";
if(layer.has_formulation() && layer.get_domain()=="catchments"){
double c_value = UnitsHelper::get_converted_value(layer_desc.time_step_units,layer_desc.time_step,"s");
// make a new simulation time object with a different output interval
Simulation_Time sim_time(*Simulation_Time_Object, c_value);
domain_formulations.emplace(
layer_desc.id,
construct_formulation_from_config(simulation_time_config,
"layer-"+std::to_string(layer_desc.id),
layer.formulation,
output_stream
Comment on lines +105 to +108
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is duplicated from formulation initialized just above on line 104

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed duplicate in 581e8e5

)
);
domain_formulations.at(layer_desc.id)->set_output_stream(get_output_root() + layer_desc.name + "_layer_"+std::to_string(layer_desc.id) + ".csv");
}
//TODO for each layer, create deferred providers for use by other layers
//VERY SIMILAR TO NESTED MODULE INIT
}
}

//TODO use the set of layer providers as input for catchments to lookup from

/**
* Read routing configurations from configuration file
*/
Expand Down Expand Up @@ -206,6 +193,14 @@ namespace realization {
return this->formulations.at(id);
}

virtual std::shared_ptr<Catchment_Formulation> get_domain_formulation(long id) const {
return this->domain_formulations.at(id);
}

virtual bool has_domain_formulation(int id) const {
return this->domain_formulations.count( id ) > 0;
}

virtual bool contains(std::string identifier) const {
return this->formulations.count(identifier) > 0;
}
Expand Down Expand Up @@ -619,6 +614,9 @@ namespace realization {

std::map<std::string, std::shared_ptr<Catchment_Formulation>> formulations;

//Store global layer formulation pointers
std::map<int, std::shared_ptr<Catchment_Formulation> > domain_formulations;

std::shared_ptr<routing_params> routing_config;

bool using_routing = false;
Expand Down
99 changes: 99 additions & 0 deletions include/realizations/config/layer.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
#ifndef NGEN_REALIZATION_CONFIG_LAYER_H
#define NGEN_REALIZATION_CONFIG_LAYER_H

#include <boost/property_tree/ptree.hpp>

#include "LayerData.hpp"
#include "config.hpp"

namespace realization{
namespace config{

/**
* @brief Layer configuration information
*
*/
struct Layer{
/**
* @brief Construct a new default surface layer
*
* Default layers are surface layers (0) with 3600 second time steps
*
*/
Layer():descriptor( {"surface layer", "s", 0, 3600 } ){};

/**
* @brief Construct a new Layer from a property tree
*
* @param tree
*/
Layer(const boost::property_tree::ptree& tree):formulation(tree){
std::vector<std::string> missing_keys;
auto name = tree.get_optional<std::string>("name");
if(!name) missing_keys.push_back("name");
auto unit = tree.get<std::string>("time_step_units", "s");

auto id = tree.get_optional<int>("id");
if(!id) missing_keys.push_back("id");

auto ts = tree.get_optional<double>("time_step");
//TODO is time_step required? e.g. need to add to missing_keys?
auto tmp = tree.get_optional<std::string>("domain");
if(tmp) domain = *tmp;
if(missing_keys.empty()){
descriptor = {*name, unit, *id, *ts};// ngen::LayerDescription( );
}
else{
std::string message = "ERROR: Layer cannot be created; the following parameters are missing or invalid: ";

for (const auto& missing : missing_keys) {
message += missing;
message += ", ";
}
//pop off the extra ", "
message.pop_back();
message.pop_back();
throw std::runtime_error(message);
}
}

/**
* @brief Get the descriptor associated with this layer configuration
*
* @return const ngen::LayerDescription&
*/
const ngen::LayerDescription& get_descriptor(){
return descriptor;
}

/**
* @brief Determines if the layer has a valid configured formulation
*
* @return true
* @return false
*/
bool has_formulation(){
return formulation.has_formulation();
}

/**
* @brief Get the domain description
*
* @return const std::string&
*/
const std::string& get_domain(){ return domain; }

/**
* @brief The formulation configuration associated with the layer
*
*/
Config formulation;
private:
std::string domain;
ngen::LayerDescription descriptor;

};

}//end namespace config
}//end namespace realization
#endif //NGEN_REALIZATION_CONFIG_LAYER_H
Loading
Loading