diff --git a/include/realizations/catchment/Formulation_Constructors.hpp b/include/realizations/catchment/Formulation_Constructors.hpp index a5cf348f04..0eb22ec3f2 100644 --- a/include/realizations/catchment/Formulation_Constructors.hpp +++ b/include/realizations/catchment/Formulation_Constructors.hpp @@ -45,6 +45,14 @@ namespace realization { #endif // ACTIVATE_PYTHON }; + static std::string valid_formulation_keys(){ + std::string keys = ""; + for(const auto& kv : formulations){ + keys.append(kv.first+" "); + } + return keys; + } + static bool formulation_exists(std::string formulation_type) { return formulations.count(formulation_type) > 0; } @@ -55,6 +63,7 @@ namespace realization { forcing_params &forcing_config, utils::StreamHandler output_stream ) { + std::cout<<"FORMULATION: "< fp; if (forcing_config.provider == "CsvPerFeature" || forcing_config.provider == ""){ @@ -83,13 +92,16 @@ namespace realization { return node.first; } }*/ + std::cout<<"Getting optional name\n"; boost::optional key = tree.get_optional("name"); + std::cout<<"Got name??? "< params; + Routing(const boost::property_tree::ptree& tree){ + params = std::make_shared(tree.get(ROUTING_CONFIG_KEY, "")); + } + }; + + struct Layer{ + Layer():descriptor( {"surface layer", "s", 0, 3600 } ){}; + Layer(const boost::property_tree::ptree& tree){ + auto name = tree.get_optional("name"); + if(!name) missing_keys.push_back("name"); + auto unit = tree.get("time_step_units", "s"); + + auto id = tree.get_optional("id"); + if(!id) missing_keys.push_back("id"); + + auto ts = tree.get_optional("time_step"); + if(missing_keys.empty()){ + descriptor = {*name, unit, *id, *ts};// ngen::LayerDescription( ); + } + } + + const ngen::LayerDescription& get_descriptor(){ + if(!missing_keys.empty()){ + std::string message = "ERROR: Layer cannot be created; the following parameters are missing or invalid: "; + + for (int missing_parameter_index = 0; missing_parameter_index < missing_keys.size(); missing_parameter_index++) { + message += missing_keys[missing_parameter_index]; + + if (missing_parameter_index < missing_keys.size() - 1) { + message += ", "; + } + } + + throw std::runtime_error(message); + } + return descriptor; + } + private: + ngen::LayerDescription descriptor; + std::vector missing_keys; + }; + + struct Time{ + std::string start_time; + std::string end_time; + unsigned int output_interval; + + Time(const boost::property_tree::ptree& tree){ + start_time = tree.get("start_time", std::string()); + end_time = tree.get("end_time", std::string()); + output_interval = tree.get("output_interval", 0); + } + + simulation_time_params make_params(){ + std::vector missing_simulation_time_parameters; + + if (start_time.empty()){ + missing_simulation_time_parameters.push_back("start_time"); + } + + if (end_time.empty()) { + missing_simulation_time_parameters.push_back("end_time"); + } + + if (output_interval == 0) { + missing_simulation_time_parameters.push_back("output_interval"); + } + + if (missing_simulation_time_parameters.size() > 0) { + std::string message = "ERROR: A simulation time parameter cannot be created; the following parameters are missing or invalid: "; + + for (int missing_parameter_index = 0; missing_parameter_index < missing_simulation_time_parameters.size(); missing_parameter_index++) { + message += missing_simulation_time_parameters[missing_parameter_index]; + + if (missing_parameter_index < missing_simulation_time_parameters.size() - 1) { + message += ", "; + } + } + + throw std::runtime_error(message); + } + return simulation_time_params( + start_time, + end_time, + output_interval + ); + } + }; + }; + + struct Formulation_Config{ + + struct Formulation{ + std::string type; + geojson::PropertyMap parameters; + Formulation():type(std::string()), parameters(geojson::PropertyMap()){} + Formulation(std::string& type, geojson::PropertyMap params):type(std::move(type)), parameters(params){} + Formulation(const boost::property_tree::ptree& tree){ + type = tree.get("name"); + std::cout<<"FOUND NAME: "< setting : tree.get_child("params")) { + std::cout<<"FORMULATION: KEY -> "<parameters.emplace( + forcing_parameter.first, + geojson::JSONProperty(forcing_parameter.first, forcing_parameter.second) + ); + } + } + bool has_key(const std::string& key){ + return parameters.count(key) > 0; + } + }; + + Formulation_Config(){}; + Formulation_Config(const boost::property_tree::ptree& tree):formulation_tree(tree){ + + auto possible_forcing = tree.get_child_optional("forcing"); + + if (possible_forcing) { + forcing = Forcing(*possible_forcing); + } + //get first empty key under formulations (corresponds to first json array element) + auto possible_formulation_tree = tree.get_child_optional("formulations.."); + if(possible_formulation_tree){ + formulation = Formulation(*possible_formulation_tree); + } + } + + bool has_formulation(){ + return !(formulation.type.empty() || formulation.parameters.empty()); + } + + Formulation formulation; + boost::property_tree::ptree formulation_tree; + Forcing forcing; + + }; class Formulation_Manager { typedef std::tuple dual_keys; @@ -51,83 +206,34 @@ namespace realization { #endif }; + + + void parse_catchment_formulations(geojson::GeoJSON fabric, utils::StreamHandler& output_stream, simulation_time_params& simulation_time_config){ + + } + virtual void read(geojson::GeoJSON fabric, utils::StreamHandler output_stream) { //TODO seperate the parsing of configuration options like time //and routing and other non feature specific tasks from this main function //which has to iterate the entire hydrofabric. + auto possible_global_config = tree.get_child_optional("global"); if (possible_global_config) { - this->global_formulation_tree = *possible_global_config; - - //get forcing info - for (auto &forcing_parameter : (*possible_global_config).get_child("forcing")) { - this->global_forcing.emplace( - forcing_parameter.first, - geojson::JSONProperty(forcing_parameter.first, forcing_parameter.second) - ); - } - - //get first empty key under formulations (corresponds to first json array element) - auto formulation = (*possible_global_config).get_child("formulations.."); - - for (std::pair global_setting : formulation.get_child("params")) { - this->global_formulation_parameters.emplace( - global_setting.first, - geojson::JSONProperty(global_setting.first, global_setting.second) - ); - } + global_config = Formulation_Config(*possible_global_config); } - /** - * Read simulation time from configuration file - * /// \todo TODO: Separate input_interval from output_interval - */ auto possible_simulation_time = tree.get_child_optional("time"); if (!possible_simulation_time) { throw std::runtime_error("ERROR: No simulation time period defined."); } - - geojson::JSONProperty simulation_time_parameters("time", *possible_simulation_time); - - std::vector missing_simulation_time_parameters; - - if (!simulation_time_parameters.has_key("start_time")) { - missing_simulation_time_parameters.push_back("start_time"); - } - - if (!simulation_time_parameters.has_key("end_time")) { - missing_simulation_time_parameters.push_back("end_time"); - } - - if (!simulation_time_parameters.has_key("output_interval")) { - missing_simulation_time_parameters.push_back("output_interval"); - } - - if (missing_simulation_time_parameters.size() > 0) { - std::string message = "ERROR: A simulation time parameter cannot be created; the following parameters are missing: "; - - for (int missing_parameter_index = 0; missing_parameter_index < missing_simulation_time_parameters.size(); missing_parameter_index++) { - message += missing_simulation_time_parameters[missing_parameter_index]; - - if (missing_parameter_index < missing_simulation_time_parameters.size() - 1) { - message += ", "; - } - } - - throw std::runtime_error(message); - } - - simulation_time_params simulation_time_config( - simulation_time_parameters.at("start_time").as_string(), - simulation_time_parameters.at("end_time").as_string(), - simulation_time_parameters.at("output_interval").as_natural_number() - ); - + config::Time time = config::Time(*possible_simulation_time); + auto simulation_time_config = time.make_params(); /** * Call constructor to construct a Simulation_Time object - */ + */ + this->Simulation_Time_Object = std::make_shared(simulation_time_config); /** @@ -136,51 +242,26 @@ namespace realization { // try to get the json node auto layers_json_array = tree.get_child_optional("layers"); - + config::Layer layer; + // layer description struct + ngen::LayerDescription layer_desc; // 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); + layer_desc = layer.get_descriptor(); + // add the layer to storage + layer_storage.put_layer(layer_desc, layer_desc.id); } else { + std::cout<<"Parsing Layers as Array???\n"; + for (std::pair 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("name"); - layer_desc.id = layer_config.second.get("id"); - layer_desc.time_step = layer_config.second.get("time_step"); - boost::optional layer_units = layer_config.second.get_optional("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(); + std::cout<<"key: "<routing_config = std::make_shared( - routing_parameters.at("t_route_config_file_with_path").as_string() - ); + this->routing_config = (config::Routing(*possible_routing_configs)).params; using_routing = true; #else using_routing = false; @@ -207,7 +284,7 @@ namespace realization { <<", but routing support isn't enabled. No routing will occur."<get_feature(catchment_index); - for (auto &formulation: *formulations) { + // for (auto &formulation: catchment_formulation.formulation_tree) { // Handle single-bmi - decltype(auto) model_params = formulation.second.get_child_optional("params.model_params"); - if (model_params) { - parse_external_model_params(*model_params, catchment_feature); - } - + // decltype(auto) model_params = formulation.second.get_child_optional("params.model_params"); + // if (model_params) { + // parse_external_model_params(*model_params, catchment_feature); + // } + if( catchment_formulation.formulation.parameters.count("model_params") > 0){ + parse_external_model_params(catchment_formulation.formulation.parameters, catchment_feature); + } // Handle multi-bmi // FIXME: this will not handle doubly nested multi-BMI configs, // might need a recursive helper here? - decltype(auto) nested_modules = formulation.second.get_child_optional("params.modules"); - if (nested_modules) { - for (decltype(auto) nested_formulation : *nested_modules) { - decltype(auto) nested_model_params = nested_formulation.second.get_child_optional("params.model_params"); - if (nested_model_params) { - parse_external_model_params(*nested_model_params, catchment_feature); - } - } - } - - this->add_formulation( - this->construct_formulation_from_tree( - simulation_time_config, - catchment_config.first, - catchment_config.second, - formulation.second, - output_stream - ) - ); - break; //only construct one for now FIXME - } //end for formulaitons + // decltype(auto) nested_modules = formulation.second.get_child_optional("params.modules"); + // if (nested_modules) { + // for (decltype(auto) nested_formulation : *nested_modules) { + // decltype(auto) nested_model_params = nested_formulation.second.get_child_optional("params.model_params"); + // if (nested_model_params) { + // parse_external_model_params(*nested_model_params, catchment_feature); + // } + // } + // } + // if( catchment_formulation.formulation.parameters.count("modules") > 0 ){ + // for(auto formulation : catchment_formulation.formulation_tree.get_child("params.modules")){ + // parse_external_model_params(formulation, catchment_feature); + // } + // } + // this->add_formulation( + // this->construct_formulation_from_tree( + // simulation_time_config, + // catchment_config.first, + // catchment_config.second, + // formulation.second, + // output_stream + // ) + // ); + // break; //only construct one for now FIXME + // } //end for formulaitons }//end for catchments @@ -374,28 +456,20 @@ namespace realization { const boost::property_tree::ptree &formulation, utils::StreamHandler output_stream ) { - auto params = formulation.get_child("params"); - std::string formulation_type_key; - try { - formulation_type_key = get_formulation_key(formulation); - } - catch(std::exception& e) { - throw std::runtime_error("Catchment " + identifier + " failed initialization: " + e.what()); + Formulation_Config catchment_formulation(tree); + if(!formulation_exists(catchment_formulation.formulation.type)){ + throw std::runtime_error("Catchment " + identifier + " failed initialization: " + + catchment_formulation.formulation.type + "is not a valid formulation. Options are: "+valid_formulation_keys()); } - boost::property_tree::ptree formulation_config = formulation.get_child("params"); - - auto possible_forcing = tree.get_child_optional("forcing"); - - if (!possible_forcing) { + + if(catchment_formulation.forcing.parameters.empty()){ throw std::runtime_error("No forcing definition was found for " + identifier); } - geojson::JSONProperty forcing_parameters("forcing", *possible_forcing); - std::vector missing_parameters; - if (!forcing_parameters.has_key("path")) { + if (!catchment_formulation.forcing.has_key("path")) { missing_parameters.push_back("path"); } @@ -413,32 +487,24 @@ namespace realization { throw std::runtime_error(message); } - geojson::PropertyMap local_forcing; - for (auto &forcing_parameter : *possible_forcing) { - local_forcing.emplace( - forcing_parameter.first, - geojson::JSONProperty(forcing_parameter.first, forcing_parameter.second) - ); - } - - forcing_params forcing_config = this->get_forcing_params(local_forcing, identifier, simulation_time_config); + forcing_params forcing_config = this->get_forcing_params(catchment_formulation.forcing.parameters, identifier, simulation_time_config); - std::shared_ptr constructed_formulation = construct_formulation(formulation_type_key, identifier, forcing_config, output_stream); + std::shared_ptr constructed_formulation = construct_formulation(catchment_formulation.formulation.type, identifier, forcing_config, output_stream); //, geometry); - constructed_formulation->create_formulation(formulation_config, &global_formulation_parameters); + constructed_formulation->create_formulation(catchment_formulation.formulation.parameters); return constructed_formulation; } std::shared_ptr construct_missing_formulation(geojson::Feature& feature, utils::StreamHandler output_stream, simulation_time_params &simulation_time_config){ const std::string identifier = feature->get_id(); - std::string formulation_type_key = get_formulation_key(global_formulation_tree.get_child("formulations..")); + //std::string formulation_type_key = get_formulation_key(global_config.formulation_tree); - forcing_params forcing_config = this->get_forcing_params(this->global_forcing, identifier, simulation_time_config); - - std::shared_ptr missing_formulation = construct_formulation(formulation_type_key, identifier, forcing_config, output_stream); + forcing_params forcing_config = this->get_forcing_params(global_config.forcing.parameters, identifier, simulation_time_config); + std::cout<<"DOING STUFF\n"; + std::shared_ptr missing_formulation = construct_formulation(global_config.formulation.type, identifier, forcing_config, output_stream); // Need to work with a copy, since it is altered in-place - geojson::PropertyMap global_properties_copy = global_formulation_parameters; + geojson::PropertyMap global_properties_copy = global_config.formulation.parameters; Catchment_Formulation::config_pattern_substitution(global_properties_copy, BMI_REALIZATION_CFG_PARAM_REQ__INIT_CONFIG, "{{id}}", identifier); @@ -737,14 +803,13 @@ namespace realization { boost::property_tree::ptree tree; - boost::property_tree::ptree global_formulation_tree; - - geojson::PropertyMap global_formulation_parameters; - - geojson::PropertyMap global_forcing; + Formulation_Config global_config; std::map> formulations; + //Store global layer formulation pointers + std::map> layer_formulations; + std::shared_ptr routing_config; bool using_routing = false; diff --git a/test/realizations/Formulation_Manager_Test.cpp b/test/realizations/Formulation_Manager_Test.cpp index c062c4717c..b55ec485c6 100644 --- a/test/realizations/Formulation_Manager_Test.cpp +++ b/test/realizations/Formulation_Manager_Test.cpp @@ -602,6 +602,7 @@ const std::string EXAMPLE_5_a = " \"init_config\": \"\"," " \"allow_exceed_end_time\": true," " \"main_output_variable\": \"OUTPUT_VAR_4\"," +" \"uses_forcing_file\": false," " \"modules\": [" " {" " \"name\": \"bmi_c++\"," @@ -942,7 +943,7 @@ TEST_F(Formulation_Manager_Test, read_external_attributes) { }); manager.read(this->fabric, catchment_output); - + std::cout<<"HERERERERE\n"; ASSERT_EQ(manager.get_size(), 3); check_formulation_values(manager, "cat-67", { 1.70352, 10.0 }); check_formulation_values(manager, "cat-52", { 3.14159, 15.0 });