generated from NOAA-OWP/owp-open-source-project-template
-
Notifications
You must be signed in to change notification settings - Fork 64
/
Copy pathformulation.hpp
147 lines (132 loc) · 6.04 KB
/
formulation.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
#ifndef NGEN_REALIZATION_CONFIG_FORMULATION_H
#define NGEN_REALIZATION_CONFIG_FORMULATION_H
#include <NGenConfig.h>
#include <boost/property_tree/ptree.hpp>
#include <string>
#include "JSONProperty.hpp"
#if NGEN_WITH_MPI
#include <mpi.h>
#endif
namespace realization{
namespace config{
struct Formulation{
std::string type;
//Formulation parameters, object as a PropertyMap
geojson::PropertyMap parameters;
//List of nested formulations (used for multi bmi representations)
std::vector<Formulation> nested;
/**
* @brief Construct a new default Formulation object
*
* Default objects have an "" type and and empty property map.
*/
Formulation() = default;
/**
* @brief Construct a new Formulation object
*
* @param type formulation type represented
* @param params formulation parameter mapping
*/
Formulation(std::string type, geojson::PropertyMap params):type(std::move(type)), parameters(params){}
/**
* @brief Construct a new Formulation object from a boost property tree
*
* The tree should have a "name" key corresponding to the formulation type
* as well as a "params" key to build the property map from
*
* @param tree property tree to build Formulation from
*/
Formulation(const boost::property_tree::ptree& tree){
type = tree.get<std::string>("name");
for (std::pair<std::string, boost::property_tree::ptree> setting : tree.get_child("params")) {
//Construct the geoJSON PropertyMap from each key, value pair in "params"
parameters.emplace(
setting.first,
geojson::JSONProperty(setting.first, setting.second)
);
}
if(type=="bmi_multi"){
for(auto& module : tree.get_child("params.modules")){
//Create the nested formulations in order of definition
nested.push_back(Formulation(module.second));
}
// If running MPI job, output with only one processor
#if NGEN_WITH_MPI
int mpi_rank;
MPI_Comm_rank(MPI_COMM_WORLD, &mpi_rank);
if (mpi_rank == 0)
#endif
{
geojson::JSONProperty::print_property(parameters.at("modules"));
}
}
}
/**
* @brief Link formulation parameters to hydrofabric data held in feature
*
* @param feature Hydrofabric feature with properties to assign to formulation
* model params
*/
void link_external(geojson::Feature feature){
if(type == "bmi_multi"){
std::vector<geojson::JSONProperty> tmp;
for(auto& n : nested){
//Iterate and link any nested modules with this feature
if(n.parameters.count("model_params")){
n.link_external(feature);
}
//Need a temporary map to hold the updated formulation properties in
geojson::PropertyMap map = {};
map.emplace("name", geojson::JSONProperty("name", n.type));
map.emplace("params", geojson::JSONProperty("", n.parameters));
tmp.push_back(geojson::JSONProperty("", map));
}
//Reset the bmi_multi modules with the now linked module definitions
parameters.at("modules") = geojson::JSONProperty("modules", tmp);
return;
}
//Short circut
if(parameters.count("model_params") < 1 ) return;
//Have some model params, check to see if any should be linked to the hyrdofabric feature
geojson::PropertyMap attr = parameters.at("model_params").get_values();
for (decltype(auto) param : attr) {
// Check for type to short-circuit. If param.second is not an object, `.has_key()` will throw
if (param.second.get_type() != geojson::PropertyType::Object || !param.second.has_key("source")) {
attr.emplace(param.first, param.second);
continue;
}
decltype(auto) param_source = param.second.at("source");
decltype(auto) param_source_name = param_source.as_string();
if (param_source_name != "hydrofabric") {
// TODO: 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.");
}
// Property name in the feature properties is either
// the value of key "from", or has the same name as
// the expected model parameter key
decltype(auto) param_name = param.second.has_key("from")
? param.second.at("from").as_string()
: param.first;
if (feature->has_property(param_name)) {
auto catchment_attribute = feature->get_property(param_name);
// Use param.first in the `.emplace` calls instead of param_name since
// the expected name is given by the key of the model_params values.
switch (catchment_attribute.get_type()) {
case geojson::PropertyType::List:
case geojson::PropertyType::Object:
// TODO: Should list/object values be passed to model parameters?
// Typically, feature properties *should* be scalars.
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" << '\n';
break;
default:
attr.at(param.first) = geojson::JSONProperty(param.first, catchment_attribute);
}
}
}
parameters.at("model_params") = geojson::JSONProperty("model_params", attr);
}
};
}//end namespace config
}//end namespace realization
#endif //NGEN_REALIZATION_CONFIG_FORMULATION_H