Skip to content

Commit 4700788

Browse files
committed
NOAA-OWP#554: catchment-specific parameter parsing
1 parent 11bdacb commit 4700788

File tree

2 files changed

+148
-20
lines changed

2 files changed

+148
-20
lines changed

include/realizations/catchment/Formulation_Manager.hpp

+24-20
Original file line numberDiff line numberDiff line change
@@ -170,19 +170,21 @@ namespace realization {
170170
continue;
171171
}
172172

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

179178
// Parse catchment-specific model_params
180-
auto model_params = formulations->get_child_optional("params.model_params");
181-
if (model_params) {
182-
parse_external_model_params(*model_params, catchment_feature);
183-
}
179+
auto catchment_feature = fabric->get_feature(catchment_index);
184180

185-
for (const auto &formulation: *formulations) {
181+
for (auto &formulation: *formulations) {
182+
decltype(auto) model_params = formulation.second.get_child_optional("params.model_params");
183+
if (model_params) {
184+
std::cerr << "Checking for external model_params\n";
185+
parse_external_model_params(*model_params, catchment_feature);
186+
}
187+
186188
this->add_formulation(
187189
this->construct_formulation_from_tree(
188190
simulation_time_config,
@@ -479,49 +481,51 @@ namespace realization {
479481
* @param catchment_feature Associated catchment feature
480482
*/
481483
void parse_external_model_params(boost::property_tree::ptree& model_params, const geojson::Feature catchment_feature) {
482-
for (auto& param : model_params) {
483-
auto param_source = param.second.get_child_optional("source");
484-
if (!param_source) {
484+
boost::property_tree::ptree attr {};
485+
for (decltype(auto) param : model_params) {
486+
if (param.second.count("source") == 0) {
487+
attr.put_child(param.first, param.second);
485488
continue;
486489
}
487-
488-
auto param_source_name = param_source->get_value<std::string>();
490+
491+
decltype(auto) param_source = param.second.get_child("source");
492+
decltype(auto) param_source_name = param_source.get_value<std::string>();
489493
if (param_source_name != "hydrofabric") {
490494
// temporary until the logic for alternative sources is designed
491495
throw std::logic_error("ERROR: 'model_params' source `" + param_source_name + "` not currently supported. Only `hydrofabric` is supported.");
492496
}
493497

494-
auto param_name = param.second.get_child_optional("from")
498+
decltype(auto) param_name = param.second.get_child_optional("from")
495499
? param.second.get_child("from").get_value<std::string>()
496500
: param.first;
497501

498-
model_params.erase(param.first);
499-
500502
if (catchment_feature->has_property(param_name)) {
501503
auto catchment_attribute = catchment_feature->get_property(param_name);
502504
switch (catchment_attribute.get_type()) {
503505
case geojson::PropertyType::Natural:
504-
model_params.put(param_name, catchment_attribute.as_natural_number());
506+
attr.put(param_name, catchment_attribute.as_natural_number());
505507
break;
506508
case geojson::PropertyType::Boolean:
507-
model_params.put(param_name, catchment_attribute.as_boolean());
509+
attr.put(param_name, catchment_attribute.as_boolean());
508510
break;
509511
case geojson::PropertyType::Real:
510-
model_params.put(param_name, catchment_attribute.as_real_number());
512+
attr.put(param_name, catchment_attribute.as_real_number());
511513
break;
512514
case geojson::PropertyType::String:
513-
model_params.put(param_name, catchment_attribute.as_string());
515+
attr.put(param_name, catchment_attribute.as_string());
514516
break;
515517

516518
case geojson::PropertyType::List:
517519
case geojson::PropertyType::Object:
518520
default:
519521
std::cerr << "WARNING: property type " << static_cast<int>(catchment_attribute.get_type()) << " not allowed as model parameter. "
520-
<< "Must be one of: Natural (int), Real (double), Boolean, or String" << std::endl;
522+
<< "Must be one of: Natural (int), Real (double), Boolean, or String" << '\n';
521523
break;
522524
}
523525
}
524526
}
527+
528+
model_params.swap(attr);
525529
}
526530

527531
boost::property_tree::ptree tree;

test/realizations/Formulation_Manager_Test.cpp

+124
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#include "FileChecker.h"
2+
#include "StreamHandler.hpp"
13
#include "gtest/gtest.h"
24
#include <Formulation_Manager.hpp>
35
#include <Catchment_Formulation.hpp>
@@ -67,6 +69,30 @@ class Formulation_Manager_Test : public ::testing::Test {
6769
fabric->add_feature(feature);
6870
}
6971

72+
void add_feature(const std::string& id, geojson::PropertyMap properties)
73+
{
74+
geojson::three_dimensional_coordinates three_dimensions {
75+
{
76+
{1.0, 2.0},
77+
{3.0, 4.0},
78+
{5.0, 6.0}
79+
},
80+
{
81+
{7.0, 8.0},
82+
{9.0, 10.0},
83+
{11.0, 12.0}
84+
}
85+
};
86+
87+
geojson::Feature feature = std::make_shared<geojson::PolygonFeature>(geojson::PolygonFeature(
88+
geojson::polygon(three_dimensions),
89+
id,
90+
properties
91+
));
92+
93+
fabric->add_feature(feature);
94+
}
95+
7096
std::vector<std::string> path_options = {
7197
"",
7298
"../",
@@ -638,6 +664,60 @@ const std::string EXAMPLE_4 = "{ "
638664
"} "
639665
"}";
640666

667+
const std::string EXAMPLE_5 =
668+
"{"
669+
" \"time\": {"
670+
" \"start_time\": \"2015-12-01 00:00:00\","
671+
" \"end_time\": \"2015-12-30 23:00:00\","
672+
" \"output_interval\": 3600"
673+
" },"
674+
" \"catchments\": {"
675+
" \"cat-67\": {"
676+
" \"formulations\": ["
677+
" {"
678+
" \"name\": \"bmi_c++\","
679+
" \"params\": {"
680+
" \"model_type_name\": \"bmi_c++_sloth\","
681+
" \"library_file\": \"" +
682+
utils::FileChecker::find_first_readable({
683+
"../../extern/sloth/cmake_build/libslothmodel.so",
684+
"../extern/sloth/cmake_build/libslothmodel.so",
685+
"./extern/sloth/cmake_build/libslothmodel.so",
686+
"../../extern/sloth/build/libslothmodel.so",
687+
"../extern/sloth/build/libslothmodel.so",
688+
"./extern/sloth/build/libslothmodel.so",
689+
})+
690+
"\","
691+
" \"init_config\": \"/dev/null\","
692+
" \"allow_exceed_end_time\": true,"
693+
" \"main_output_variable\": \"Klf\","
694+
" \"uses_forcing_file\": false,"
695+
" \"model_params\": {"
696+
" \"Klf\": {"
697+
" \"source\": \"hydrofabric\""
698+
" },"
699+
" \"Kn\": {"
700+
" \"source\": \"hydrofabric\""
701+
" },"
702+
" \"nash_n\": {"
703+
" \"source\": \"hydrofabric\","
704+
" \"from\": \"n\""
705+
" },"
706+
" \"Cgw\": {"
707+
" \"source\": \"hydrofabric\""
708+
" },"
709+
" \"static_var\": 0.0"
710+
" }"
711+
" }"
712+
" }"
713+
" ],"
714+
" \"forcing\": {"
715+
" \"path\": \"./data/forcing/cat-67_2015-12-01 00_00_00_2015-12-30 23_00_00.csv\""
716+
" }"
717+
" }"
718+
" }"
719+
"}";
720+
641721
TEST_F(Formulation_Manager_Test, basic_reading_1) {
642722
std::stringstream stream;
643723

@@ -828,3 +908,47 @@ TEST_F(Formulation_Manager_Test, forcing_provider_specification) {
828908
}
829909
}
830910

911+
TEST_F(Formulation_Manager_Test, read_external_attributes) {
912+
std::stringstream stream;
913+
stream << fix_paths(EXAMPLE_5);
914+
915+
std::ostream* ptr = &std::cout;
916+
std::shared_ptr<std::ostream> s_ptr(ptr, [](void*) {});
917+
utils::StreamHandler catchment_output(s_ptr);
918+
919+
auto manager = realization::Formulation_Manager(stream);
920+
this->add_feature("cat-67", geojson::PropertyMap{
921+
{ "Klf", geojson::JSONProperty{"Klf", 1.70352 } },
922+
{ "Kn", geojson::JSONProperty{"Kn", 0.03 } },
923+
{ "n", geojson::JSONProperty{"n", 2 } }, // nash_n
924+
{ "Cgw", geojson::JSONProperty{"Cgw", 0.01 } }
925+
});
926+
927+
auto feature = this->fabric->get_feature("cat-67");
928+
ASSERT_TRUE(feature->has_property("Klf"));
929+
ASSERT_TRUE(feature->has_property("Kn"));
930+
ASSERT_TRUE(feature->has_property("n"));
931+
ASSERT_TRUE(feature->has_property("Cgw"));
932+
933+
manager.read(this->fabric, catchment_output);
934+
935+
ASSERT_EQ(manager.get_size(), 1);
936+
ASSERT_TRUE(manager.contains("cat-67"));
937+
938+
pdm03_struct pdm_et_data;
939+
pdm_et_data.scaled_distribution_fn_shape_parameter = 1.3;
940+
pdm_et_data.vegetation_adjustment = 0.99;
941+
pdm_et_data.model_time_step = 0.0;
942+
pdm_et_data.max_height_soil_moisture_storerage_tank = 400.0;
943+
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);
944+
std::shared_ptr<pdm03_struct> et_params_ptr = std::make_shared<pdm03_struct>(pdm_et_data);
945+
946+
auto formulation = manager.get_formulation("cat-67");
947+
formulation->set_et_params(et_params_ptr);
948+
double result = formulation->get_response(0, 3600);
949+
950+
951+
ASSERT_NEAR(result, 1.70352, 1e-5);
952+
ASSERT_EQ(formulation->get_output_header_line(","), "Cgw,Klf,Kn,n,static_var");
953+
ASSERT_EQ(formulation->get_output_line_for_timestep(0), "0.010000,1.703520,0.030000,2.000000,0.000000");
954+
}

0 commit comments

Comments
 (0)