Skip to content

Commit

Permalink
NOAA-OWP#554: catchment-specific parameter parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
program-- committed Jul 26, 2023
1 parent 788318f commit 63ecc45
Show file tree
Hide file tree
Showing 2 changed files with 148 additions and 20 deletions.
44 changes: 24 additions & 20 deletions include/realizations/catchment/Formulation_Manager.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -170,19 +170,21 @@ namespace realization {
continue;
}

auto catchment_feature = fabric->get_feature(catchment_index);
auto formulations = catchment_config.second.get_child_optional("formulations");
decltype(auto) formulations = catchment_config.second.get_child_optional("formulations");
if( !formulations ) {
throw std::runtime_error("ERROR: No formulations defined for "+catchment_config.first+".");
}

// Parse catchment-specific model_params
auto model_params = formulations->get_child_optional("params.model_params");
if (model_params) {
parse_external_model_params(*model_params, catchment_feature);
}
auto catchment_feature = fabric->get_feature(catchment_index);

for (const auto &formulation: *formulations) {
for (auto &formulation: *formulations) {
decltype(auto) model_params = formulation.second.get_child_optional("params.model_params");
if (model_params) {
std::cerr << "Checking for external model_params\n";
parse_external_model_params(*model_params, catchment_feature);
}

this->add_formulation(
this->construct_formulation_from_tree(
simulation_time_config,
Expand Down Expand Up @@ -479,49 +481,51 @@ namespace realization {
* @param catchment_feature Associated catchment feature
*/
void parse_external_model_params(boost::property_tree::ptree& model_params, const geojson::Feature catchment_feature) {
for (auto& param : model_params) {
auto param_source = param.second.get_child_optional("source");
if (!param_source) {
boost::property_tree::ptree attr {};
for (decltype(auto) param : model_params) {
if (param.second.count("source") == 0) {
attr.put_child(param.first, param.second);
continue;
}

auto param_source_name = param_source->get_value<std::string>();

decltype(auto) param_source = param.second.get_child("source");
decltype(auto) param_source_name = param_source.get_value<std::string>();
if (param_source_name != "hydrofabric") {
// temporary until the logic for alternative sources is designed
throw std::logic_error("ERROR: 'model_params' source `" + param_source_name + "` not currently supported. Only `hydrofabric` is supported.");
}

auto param_name = param.second.get_child_optional("from")
decltype(auto) param_name = param.second.get_child_optional("from")
? param.second.get_child("from").get_value<std::string>()
: param.first;

model_params.erase(param.first);

if (catchment_feature->has_property(param_name)) {
auto catchment_attribute = catchment_feature->get_property(param_name);
switch (catchment_attribute.get_type()) {
case geojson::PropertyType::Natural:
model_params.put(param_name, catchment_attribute.as_natural_number());
attr.put(param_name, catchment_attribute.as_natural_number());
break;
case geojson::PropertyType::Boolean:
model_params.put(param_name, catchment_attribute.as_boolean());
attr.put(param_name, catchment_attribute.as_boolean());
break;
case geojson::PropertyType::Real:
model_params.put(param_name, catchment_attribute.as_real_number());
attr.put(param_name, catchment_attribute.as_real_number());
break;
case geojson::PropertyType::String:
model_params.put(param_name, catchment_attribute.as_string());
attr.put(param_name, catchment_attribute.as_string());
break;

case geojson::PropertyType::List:
case geojson::PropertyType::Object:
default:
std::cerr << "WARNING: property type " << static_cast<int>(catchment_attribute.get_type()) << " not allowed as model parameter. "
<< "Must be one of: Natural (int), Real (double), Boolean, or String" << std::endl;
<< "Must be one of: Natural (int), Real (double), Boolean, or String" << '\n';
break;
}
}
}

model_params.swap(attr);
}

boost::property_tree::ptree tree;
Expand Down
124 changes: 124 additions & 0 deletions test/realizations/Formulation_Manager_Test.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#include "FileChecker.h"
#include "StreamHandler.hpp"
#include "gtest/gtest.h"
#include <Formulation_Manager.hpp>
#include <Catchment_Formulation.hpp>
Expand Down Expand Up @@ -67,6 +69,30 @@ class Formulation_Manager_Test : public ::testing::Test {
fabric->add_feature(feature);
}

void add_feature(const std::string& id, geojson::PropertyMap properties)
{
geojson::three_dimensional_coordinates three_dimensions {
{
{1.0, 2.0},
{3.0, 4.0},
{5.0, 6.0}
},
{
{7.0, 8.0},
{9.0, 10.0},
{11.0, 12.0}
}
};

geojson::Feature feature = std::make_shared<geojson::PolygonFeature>(geojson::PolygonFeature(
geojson::polygon(three_dimensions),
id,
properties
));

fabric->add_feature(feature);
}

std::vector<std::string> path_options = {
"",
"../",
Expand Down Expand Up @@ -638,6 +664,60 @@ const std::string EXAMPLE_4 = "{ "
"} "
"}";

const std::string EXAMPLE_5 =
"{"
" \"time\": {"
" \"start_time\": \"2015-12-01 00:00:00\","
" \"end_time\": \"2015-12-30 23:00:00\","
" \"output_interval\": 3600"
" },"
" \"catchments\": {"
" \"cat-67\": {"
" \"formulations\": ["
" {"
" \"name\": \"bmi_c++\","
" \"params\": {"
" \"model_type_name\": \"bmi_c++_sloth\","
" \"library_file\": \"" +
utils::FileChecker::find_first_readable({
"../../extern/sloth/cmake_build/libslothmodel.so",
"../extern/sloth/cmake_build/libslothmodel.so",
"./extern/sloth/cmake_build/libslothmodel.so",
"../../extern/sloth/build/libslothmodel.so",
"../extern/sloth/build/libslothmodel.so",
"./extern/sloth/build/libslothmodel.so",
})+
"\","
" \"init_config\": \"/dev/null\","
" \"allow_exceed_end_time\": true,"
" \"main_output_variable\": \"Klf\","
" \"uses_forcing_file\": false,"
" \"model_params\": {"
" \"Klf\": {"
" \"source\": \"hydrofabric\""
" },"
" \"Kn\": {"
" \"source\": \"hydrofabric\""
" },"
" \"nash_n\": {"
" \"source\": \"hydrofabric\","
" \"from\": \"n\""
" },"
" \"Cgw\": {"
" \"source\": \"hydrofabric\""
" },"
" \"static_var\": 0.0"
" }"
" }"
" }"
" ],"
" \"forcing\": {"
" \"path\": \"./data/forcing/cat-67_2015-12-01 00_00_00_2015-12-30 23_00_00.csv\""
" }"
" }"
" }"
"}";

TEST_F(Formulation_Manager_Test, basic_reading_1) {
std::stringstream stream;

Expand Down Expand Up @@ -828,3 +908,47 @@ TEST_F(Formulation_Manager_Test, forcing_provider_specification) {
}
}

TEST_F(Formulation_Manager_Test, read_external_attributes) {
std::stringstream stream;
stream << fix_paths(EXAMPLE_5);

std::ostream* ptr = &std::cout;
std::shared_ptr<std::ostream> s_ptr(ptr, [](void*) {});
utils::StreamHandler catchment_output(s_ptr);

auto manager = realization::Formulation_Manager(stream);
this->add_feature("cat-67", geojson::PropertyMap{
{ "Klf", geojson::JSONProperty{"Klf", 1.70352 } },
{ "Kn", geojson::JSONProperty{"Kn", 0.03 } },
{ "n", geojson::JSONProperty{"n", 2 } }, // nash_n
{ "Cgw", geojson::JSONProperty{"Cgw", 0.01 } }
});

auto feature = this->fabric->get_feature("cat-67");
ASSERT_TRUE(feature->has_property("Klf"));
ASSERT_TRUE(feature->has_property("Kn"));
ASSERT_TRUE(feature->has_property("n"));
ASSERT_TRUE(feature->has_property("Cgw"));

manager.read(this->fabric, catchment_output);

ASSERT_EQ(manager.get_size(), 1);
ASSERT_TRUE(manager.contains("cat-67"));

pdm03_struct pdm_et_data;
pdm_et_data.scaled_distribution_fn_shape_parameter = 1.3;
pdm_et_data.vegetation_adjustment = 0.99;
pdm_et_data.model_time_step = 0.0;
pdm_et_data.max_height_soil_moisture_storerage_tank = 400.0;
pdm_et_data.maximum_combined_contents = pdm_et_data.max_height_soil_moisture_storerage_tank / (1.0+pdm_et_data.scaled_distribution_fn_shape_parameter);
std::shared_ptr<pdm03_struct> et_params_ptr = std::make_shared<pdm03_struct>(pdm_et_data);

auto formulation = manager.get_formulation("cat-67");
formulation->set_et_params(et_params_ptr);
double result = formulation->get_response(0, 3600);


ASSERT_NEAR(result, 1.70352, 1e-5);
ASSERT_EQ(formulation->get_output_header_line(","), "Cgw,Klf,Kn,n,static_var");
ASSERT_EQ(formulation->get_output_line_for_timestep(0), "0.010000,1.703520,0.030000,2.000000,0.000000");
}

0 comments on commit 63ecc45

Please sign in to comment.