diff --git a/data/bmi/c/cfe/cat-27_bmi_config.ini b/data/bmi/c/cfe/cat-27_bmi_config.ini index 491f97f11f..fb32061b1e 100644 --- a/data/bmi/c/cfe/cat-27_bmi_config.ini +++ b/data/bmi/c/cfe/cat-27_bmi_config.ini @@ -19,3 +19,4 @@ K_lf=0.01 K_nash=0.03 nash_storage=0.0,0.0 giuh_ordinates=0.06,0.51,0.28,0.12,0.03 +num_timesteps=720 diff --git a/extern/test_bmi_c/include/test_bmi_c.h b/extern/test_bmi_c/include/test_bmi_c.h index 071a7cd5fc..fa990d1ebb 100644 --- a/extern/test_bmi_c/include/test_bmi_c.h +++ b/extern/test_bmi_c/include/test_bmi_c.h @@ -27,6 +27,10 @@ struct test_bmi_c_model { double* output_var_1; double* output_var_2; + + int param_var_1; + double param_var_2; + double* param_var_3; }; typedef struct test_bmi_c_model test_bmi_c_model; diff --git a/extern/test_bmi_c/src/bmi_test_bmi_c.c b/extern/test_bmi_c/src/bmi_test_bmi_c.c index bf92676367..71e11c62eb 100644 --- a/extern/test_bmi_c/src/bmi_test_bmi_c.c +++ b/extern/test_bmi_c/src/bmi_test_bmi_c.c @@ -8,7 +8,7 @@ #define INPUT_VAR_NAME_COUNT 2 #define OUTPUT_VAR_NAME_COUNT 2 - +#define PARAM_VAR_NAME_COUNT 3 // Don't forget to update Get_value/Get_value_at_indices (and setter) implementation if these are adjusted static const char *output_var_names[OUTPUT_VAR_NAME_COUNT] = { "OUTPUT_VAR_1", "OUTPUT_VAR_2" }; @@ -26,6 +26,13 @@ static const int input_var_item_count[INPUT_VAR_NAME_COUNT] = { 1, 1 }; static const char *input_var_grids[INPUT_VAR_NAME_COUNT] = { 0, 0 }; static const char *input_var_locations[INPUT_VAR_NAME_COUNT] = { "node", "node" }; +// Don't forget to update Get_value/Get_value_at_indices (and setter) implementation if these are adjusted +static const char *param_var_names[PARAM_VAR_NAME_COUNT] = { "PARAM_VAR_1", "PARAM_VAR_2", "PARAM_VAR_3" }; +static const char *param_var_types[PARAM_VAR_NAME_COUNT] = { "int", "double", "double" }; +static const char *param_var_units[PARAM_VAR_NAME_COUNT] = { "m", "m/s", "m"}; +static const int param_var_item_count[PARAM_VAR_NAME_COUNT] = { 1, 1, 2 }; +static const char *param_var_grids[PARAM_VAR_NAME_COUNT] = { 0, 0, 0 }; +static const char *param_var_locations[PARAM_VAR_NAME_COUNT] = { "node", "node", "node" }; static int Finalize (Bmi *self) { @@ -40,6 +47,8 @@ static int Finalize (Bmi *self) free(model->output_var_1); if( model->output_var_2 != NULL ) free(model->output_var_2); + if (model->param_var_3 != NULL ) + free(model->param_var_3); free(self->data); } @@ -272,9 +281,28 @@ static int Get_time_units (Bmi *self, char * units) static int Get_value (Bmi *self, const char *name, void *dest) { - // Since all the variables are scalar, use nested call to "by index" version, with just index 0 - int inds[] = {0}; - return self->get_value_at_indices(self, name, dest, inds, 1); + int i = 0; + int item_count = -1; + for (i = 0; i < PARAM_VAR_NAME_COUNT; i++) { + if (strcmp(name, param_var_names[i]) == 0) { + item_count = param_var_item_count[i]; + break; + } + } + + if( item_count < 1 ){ + // Since all the variables are scalar, use nested call to "by index" version, with just index 0 + int inds[] = {0}; + return self->get_value_at_indices(self, name, dest, inds, 1); + } + else{ + //All linear indicies + int inds[item_count]; + for(i = 0; i < item_count; i++){ + inds[i] = i; + } + return self->get_value_at_indices(self, name, dest, inds, item_count); + } } @@ -345,6 +373,20 @@ static int Get_value_ptr (Bmi *self, const char *name, void **dest) return BMI_SUCCESS; } + if (strcmp (name, "PARAM_VAR_1") == 0) { + *dest = &((test_bmi_c_model *)(self->data))->param_var_1; + return BMI_SUCCESS; + } + + if (strcmp (name, "PARAM_VAR_2") == 0) { + *dest = &((test_bmi_c_model *)(self->data))->param_var_2; + return BMI_SUCCESS; + } + + if (strcmp (name, "PARAM_VAR_3") == 0) { + *dest = ((test_bmi_c_model *)(self->data))->param_var_3; + return BMI_SUCCESS; + } return BMI_FAILURE; } @@ -433,6 +475,14 @@ static int Get_var_nbytes (Bmi *self, const char *name, int * nbytes) } } } + if (item_count < 1) { + for (i = 0; i < PARAM_VAR_NAME_COUNT; i++) { + if (strcmp(name, param_var_names[i]) == 0) { + item_count = param_var_item_count[i]; + break; + } + } + } if (item_count < 1) item_count = ((test_bmi_c_model *) self->data)->num_time_steps; @@ -458,6 +508,13 @@ static int Get_var_type (Bmi *self, const char *name, char * type) return BMI_SUCCESS; } } + // Finally check to see if in param array + for (i = 0; i < PARAM_VAR_NAME_COUNT; i++) { + if (strcmp(name, param_var_names[i]) == 0) { + strncpy(type, param_var_types[i], BMI_MAX_TYPE_NAME); + return BMI_SUCCESS; + } + } // If we get here, it means the variable name wasn't recognized type[0] = '\0'; return BMI_FAILURE; @@ -529,6 +586,12 @@ static int Initialize (Bmi *self, const char *file) model->output_var_1 = malloc(sizeof(double)); model->output_var_2 = malloc(sizeof(double)); + model->param_var_1 = 0; + model->param_var_2 = 0.0; + model->param_var_3 = malloc(2*sizeof(double)); + model->param_var_3[0] = 0.0; + model->param_var_3[1] = 0.0; + return BMI_SUCCESS; } diff --git a/include/geojson/JSONProperty.hpp b/include/geojson/JSONProperty.hpp index f48825df0a..72142dff6c 100644 --- a/include/geojson/JSONProperty.hpp +++ b/include/geojson/JSONProperty.hpp @@ -8,9 +8,45 @@ #include #include +#include namespace geojson { class JSONProperty; + //Forward declare variant types + struct List; + struct Object; + using PropertyVariant = boost::variant, + boost::recursive_wrapper + >; + + /** + * @brief Struct wrapping a vector of \ref PropertyVariant representing a JSON list. + * + */ + struct List{ + std::vector values; + friend std::ostream& operator<<(std::ostream& os, const List& obj){ + os<<"LIST"; + return os; + } + }; + + /** + * @brief Struct wrapping a map of nested \ref PropertyVariant representing a JSON object. + * + */ + struct Object{ + std::map values; + friend std::ostream& operator<<(std::ostream& os, const Object& obj){ + os<<"OBJECT"; + return os; + } + }; /** * Defines the different types of properties that are stored within a JSON property @@ -64,12 +100,14 @@ namespace geojson { if (value == "true" || value == "false") { type = PropertyType::Boolean; boolean = value == "true"; + data = boolean; } else { //Try natural number first since double cant cast to int/long try{ natural_number = boost::lexical_cast(value); type = PropertyType::Natural; + data = natural_number; } catch (boost::bad_lexical_cast &e) { @@ -77,26 +115,37 @@ namespace geojson { //Try to cast to double/real next real_number = boost::lexical_cast(value); type = PropertyType::Real; + data = real_number; } catch (boost::bad_lexical_cast & e) { //At this point, we are left with string option string = value; type = PropertyType::String; + data = string; } } } } else { // This isn't a terminal node, therefore represents an object or array + List tmp_list; + Object tmp_obj; + //TODO unit test these construction paths... for (auto &property : property_tree) { if (property.first.empty()) { type = PropertyType::List; - value_list.push_back(JSONProperty(value_key, property.second)); + JSONProperty tmp = JSONProperty(value_key, property.second); + value_list.push_back(tmp); + tmp_list.values.push_back(tmp.data); + data = tmp_list; } else { type = PropertyType::Object; - values.emplace(property.first, JSONProperty(property.first, property.second)); + JSONProperty tmp = JSONProperty(property.first, property.second); + values.emplace(property.first, tmp); + tmp_obj.values.emplace(property.first, tmp.data); + data = tmp_obj; } } } @@ -111,7 +160,8 @@ namespace geojson { JSONProperty(std::string value_key, short value) : type(PropertyType::Natural), key(value_key), - natural_number(long(value)) + natural_number(long(value)), + data(long(value)) {} /** @@ -123,7 +173,8 @@ namespace geojson { JSONProperty(std::string value_key, int value) : type(PropertyType::Natural), key(value_key), - natural_number(long(value)) + natural_number(long(value)), + data(long(value)) {} /** @@ -135,7 +186,8 @@ namespace geojson { JSONProperty(std::string value_key, long value) : type(PropertyType::Natural), key(value_key), - natural_number(value) + natural_number(value), + data(value) {} /** @@ -147,7 +199,8 @@ namespace geojson { JSONProperty(std::string value_key, float value) : type(PropertyType::Real), key(value_key), - real_number(double(value)) + real_number(double(value)), + data(double(value)) {} /** @@ -159,7 +212,8 @@ namespace geojson { JSONProperty(std::string value_key, double value) : type(PropertyType::Real), key(value_key), - real_number(value) + real_number(value), + data(value) {} /** @@ -172,6 +226,7 @@ namespace geojson { type = PropertyType::String; key = value_key; string = value; + data = string; } /** @@ -187,6 +242,7 @@ namespace geojson { if (value == "true" || value == "false") { type = PropertyType::Boolean; boolean = value == "true"; + data = boolean; } else { bool is_numeric = true; @@ -222,25 +278,34 @@ namespace geojson { if (is_numeric) { type = PropertyType::Natural; natural_number = std::stol(value); + data = natural_number; } else if (is_real) { type = PropertyType::Real; real_number = std::stod(value); + data = real_number; } else { // Otherwise we'll store everything as a raw string type = PropertyType::String; string = value; + data = string; } } } - JSONProperty(std::string value_key, std::vector properties) : type(PropertyType::List), key(value_key), value_list(properties) {} + JSONProperty(std::string value_key, std::vector properties) : type(PropertyType::List), key(value_key), value_list(properties) { + List tmp; + for(const auto& p : properties){ + tmp.values.push_back(p.data); + } + data = tmp; + } JSONProperty(const JSONProperty &original) { type = original.type; key = original.key; - + data = original.data; switch (type) { case PropertyType::Boolean: boolean = original.boolean; @@ -281,6 +346,7 @@ namespace geojson { type = PropertyType::Boolean; key = value_key; boolean = value; + data = boolean; } /** @@ -293,7 +359,13 @@ namespace geojson { : type(PropertyType::Object), key(value_key), values(value) - {} + { + Object tmp; + for(auto const& property : value){ + tmp.values.emplace(property.first, property.second.data); + } + data = tmp; + } /** * Get the type of the property (Natural, Real, String, etc) @@ -315,6 +387,39 @@ namespace geojson { bool as_boolean() const; + /** + * @brief Populates a std::vector with PropertyVariant values. + * + * Scalar properties will yeild a vector of size 1. + * + * List properties will yield a vector of compatible types. + * E.g. a List with [1, 2.1, 3] can upcast the Natural number 1 and 3 iff + * a container is provided with sufficient datatype (double) + * + * std::vector double_vec; + * as_vector(double_vec); + * + * Will give a vector of doubles = {1.0, 2.1, 3.0}. + * + * However, if a vector of long is used, only the Natural numbers will be extracted + * + * std::vector long_vec; + * as_vector(long_vec); + * + * Will give a vector of longs = {1, 3}. + * + * Other than this caveat, as_vector effetively filters the property list for types + * representable by T. + * + * @tparam T + * @param vector + */ + template + void as_vector(std::vector& vector) const{ + PropertyVisitor visitor(vector); + boost::apply_visitor(visitor, data); + } + std::vector as_list() const; std::vector as_natural_vector() const; @@ -373,6 +478,54 @@ namespace geojson { bool boolean; PropertyMap values; std::vector value_list; + //boost::variant to hold the parsed data + //can be one of boost::blank, long, double, bool, string, List, Object + //Defaults to boost::blank + //TODO port this entire class to use this variant + //TODO make sure all construction paths for `data` are unit tested + PropertyVariant data; + + /** + * @brief Visitor to filter boost variants by type T into a vector of type T + * + * A specialization is defined in JSONProperty.cpp which allows + * a vistitor to coerce long type variants into double types. + * See \ref as_vector for some explanation. + * + * TODO rename this visitor to be a litte more clear on its semantics. + * @tparam T + */ + template + struct PropertyVisitor : public boost::static_visitor<> + { + //PropertyVisitor takes a templated vector and stores the vector + //reference in the struct + PropertyVisitor(std::vector& v) : vec(v) {} + //Vistor operators, first is generic template operator + template + void operator () (const Variant& value) { + //This is a no-op for all types that are not T + } + + //Visitor operator for type T, adds T types to vector + void operator () (const T& value) + { + vec.push_back(value); + } + + /* Can overload these with custom implementation and/or errors + void operator () (const boost::blank&){} + void operator () (const Object&) {} + */ + void operator () (const List& values) { + //Recurse through the list to filter for types T + std::for_each(values.values.begin(), values.values.end(), boost::apply_visitor(*this)); + } + + private: + //Storage for filtered items + std::vector& vec; + }; }; } #endif // GEOJSON_JSONPROPERTY_H diff --git a/include/realizations/catchment/Bmi_Module_Formulation.hpp b/include/realizations/catchment/Bmi_Module_Formulation.hpp index 1d19bffcdd..a03ddcb0f3 100644 --- a/include/realizations/catchment/Bmi_Module_Formulation.hpp +++ b/include/realizations/catchment/Bmi_Module_Formulation.hpp @@ -640,7 +640,11 @@ namespace realization { // Do this next, since after checking whether other input variables are present in the properties, we can // now construct the adapter and init the model set_bmi_model(construct_model(properties)); - + + //Check if any parameter values need to be set on the BMI model, + //and set them before it is run + set_initial_bmi_parameters(properties); + // Make sure that this is able to interpret model time and convert to real time, since BMI model time is // usually starting at 0 and just counting up determine_model_time_offset(); @@ -688,6 +692,110 @@ namespace realization { model_initialized = get_bmi_model()->is_model_initialized(); } + /** + * @brief Check configuration properties for `model_params` and attempt to set them in the bmi model. + * + * This checks for a key named `model_params` in the parsed properties, and for each property + * it will attempt to call `SetValue` using the property's key as the BMI variable + * and the property's value as the value to set. + * + * This function should only be called once @p bmi_model is properly constructed. + * If @p bmi_model is a nullptr, this function becomes a no-op. + * + */ + void set_initial_bmi_parameters(geojson::PropertyMap properties){ + auto model = get_bmi_model(); + if( model == nullptr ) return; + //Now that the model is ready, we can set some intial parameters passed in the config + auto model_params = properties.find("model_params"); + + if (model_params != properties.end() ){ + + geojson::PropertyMap params = model_params->second.get_values(); + //Declare/init the possible vectors here + //reuse them for each loop iteration, make sure to clear them + std::vector long_vec; + std::vector double_vec; + //not_supported + //std::vector str_vec; + //std::vector bool_vec; + std::shared_ptr value_ptr; + for (auto& param : params) { + //Get some basic BMI info for this param + int varItemSize = get_bmi_model()->GetVarItemsize(param.first); + int totalBytes = get_bmi_model()->GetVarNbytes(param.first); + + //Figure out the c++ type to convert data to + std::string type = get_bmi_model()->get_analogous_cxx_type(get_bmi_model()->GetVarType(param.first), + varItemSize); + //TODO might consider refactoring as_vector and get_values_as_type + //(and by extension, as_c_array) into the JSONProperty class + //then instead of the PropertyVariant visitor filling vectors + //it could fill the c-like array and avoid another copy. + switch( param.second.get_type() ){ + case geojson::PropertyType::Natural: + param.second.as_vector(long_vec); + value_ptr = get_values_as_type(type, long_vec.begin(), long_vec.end()); + std::cout<<"NAT VALUE: "<SetValue(param.first, value_ptr.get()); + } + catch (const std::exception &e) + { + std::cout<<"Exception setting parameter value: "<< e.what()<) overloads??? + //implment the overloads in each adapter + //ensure proper type is prepared before setting value + } + /** * Test whether backing model has fixed time step size. * @@ -762,6 +870,84 @@ namespace realization { model_initialized = is_initialized; } + /** + * @brief Template function for copying iterator range into contiguous array. + * + * This function will iterate the range and cast the iterator value to type T + * and copy that value into a C-like array of contiguous, dynamically allocated memory. + * This array is stored in a smart pointer with a custom array deleter. + * + * @tparam T + * @tparam Iterator + * @param begin + * @param end + * @return std::shared_ptr + */ + template + std::shared_ptr as_c_array(Iterator begin, Iterator end){ + //Make a shared pointer large enough to hold all elements + //This is a CONTIGUOUS array of type T + //Must provide a custom deleter to delete the array + std::shared_ptr ptr( new T[std::distance(begin, end)], [](T *p) { delete[] p; } ); + Iterator it = begin; + int i = 0; + while(it != end){ + //Be safe and cast the input to the desired type + ptr.get()[i] = static_cast(*it); + ++it; + ++i; + } + return ptr; + } + + /** + * @brief Gets values in iterator range, casted based on @p type then returned as typeless (void) pointer. + * + * @tparam Iterator + * @param type + * @param begin + * @param end + * @return std::shared_ptr + */ + template + std::shared_ptr get_values_as_type(std::string type, Iterator begin, Iterator end) + { + //Use std::vector range constructor to ensure contiguous storage of values + //Return the pointer to the contiguous storage + if (type == "double" || type == "double precision") + return as_c_array(begin, end); + + if (type == "float" || type == "real") + return as_c_array(begin, end); + + if (type == "short" || type == "short int" || type == "signed short" || type == "signed short int") + return as_c_array(begin, end); + + if (type == "unsigned short" || type == "unsigned short int") + return as_c_array(begin, end); + + if (type == "int" || type == "signed" || type == "signed int" || type == "integer") + return as_c_array(begin, end); + + if (type == "unsigned" || type == "unsigned int") + return as_c_array(begin, end); + + if (type == "long" || type == "long int" || type == "signed long" || type == "signed long int") + return as_c_array(begin, end); + + if (type == "unsigned long" || type == "unsigned long int") + return as_c_array(begin, end); + + if (type == "long long" || type == "long long int" || type == "signed long long" || type == "signed long long int") + return as_c_array(begin, end); + + if (type == "unsigned long long" || type == "unsigned long long int") + return as_c_array(begin, end); + + throw std::runtime_error("Unable to get values of iterable as type" + type + " from " + get_model_type_name() + + " : no logic for converting values to variable's type."); + } + // TODO: need to modify this to support arrays properly, since in general that's what BMI modules deal with template std::shared_ptr get_value_as_type(std::string type, T value) diff --git a/src/geojson/JSONProperty.cpp b/src/geojson/JSONProperty.cpp index a77908feac..67ccc5dcc7 100644 --- a/src/geojson/JSONProperty.cpp +++ b/src/geojson/JSONProperty.cpp @@ -192,3 +192,10 @@ bool JSONProperty::has_key(std::string key) const { std::string JSONProperty::get_key() const { return key; } + +namespace geojson{ + //Template specialization + template<> template<> void JSONProperty::PropertyVisitor::operator ()(const long& value){ + vec.push_back(static_cast(value)); + } +} \ No newline at end of file diff --git a/test/geojson/JSONProperty_Test.cpp b/test/geojson/JSONProperty_Test.cpp index 154270995e..be0eb85f6e 100644 --- a/test/geojson/JSONProperty_Test.cpp +++ b/test/geojson/JSONProperty_Test.cpp @@ -185,4 +185,114 @@ TEST_F(JSONProperty_Test, ptree_test) { ASSERT_EQ(properties[2].get_type(), geojson::PropertyType::Boolean); ASSERT_EQ(properties[3].get_type(), geojson::PropertyType::Natural); ASSERT_EQ(properties[4].get_type(), geojson::PropertyType::Real); +} + +TEST_F(JSONProperty_Test, test_as_vector_natural){ + geojson::JSONProperty natural_property("natural", 4); + ASSERT_EQ(natural_property.get_type(), geojson::PropertyType::Natural); + ASSERT_EQ(natural_property.as_natural_number(), 4); + std::vector vec; + natural_property.as_vector(vec); + ASSERT_EQ(vec.size(), 1); + ASSERT_EQ(vec[0], 4); +} + +TEST_F(JSONProperty_Test, test_as_vector_real){ + geojson::JSONProperty natural_property("real", 4.2); + ASSERT_EQ(natural_property.get_type(), geojson::PropertyType::Real); + ASSERT_EQ(natural_property.as_real_number(), 4.2); + std::vector vec; + natural_property.as_vector(vec); + ASSERT_EQ(vec.size(), 1); + ASSERT_EQ(vec[0], 4.2); +} + +TEST_F(JSONProperty_Test, test_as_vector_string){ + geojson::JSONProperty natural_property("string", "test"); + ASSERT_EQ(natural_property.get_type(), geojson::PropertyType::String); + ASSERT_EQ(natural_property.as_string(), "test"); + std::vector vec; + natural_property.as_vector(vec); + ASSERT_EQ(vec.size(), 1); + ASSERT_EQ(vec[0], "test"); +} + +TEST_F(JSONProperty_Test, test_as_vector_natural_list){ + std::vector test_list = {1,2,3,4}; + std::vector properties; + for(auto const num : test_list){ + properties.push_back(geojson::JSONProperty("", num)); + } + + geojson::JSONProperty natural_property("natural_list", properties); + ASSERT_EQ(natural_property.get_type(), geojson::PropertyType::List); + ASSERT_EQ(natural_property.as_natural_vector(), test_list); + std::vector vec; + natural_property.as_vector(vec); + ASSERT_EQ(vec.size(), 4); + ASSERT_EQ(vec, test_list); +} + +TEST_F(JSONProperty_Test, test_as_vector_real_list){ + std::vector test_list = {1.1,2.2,3.3,4.4}; + std::vector properties; + for(auto const num : test_list){ + properties.push_back(geojson::JSONProperty("", num)); + } + + geojson::JSONProperty real_property("real_list", properties); + ASSERT_EQ(real_property.get_type(), geojson::PropertyType::List); + ASSERT_EQ(real_property.as_real_vector(), test_list); + std::vector vec; + real_property.as_vector(vec); + ASSERT_EQ(vec.size(), 4); + ASSERT_EQ(vec, test_list); +} + +TEST_F(JSONProperty_Test, test_as_vector_mixed_list){ + //Test to ensure a mixed list of natural and real numbers + //can be converted to a single vector of real (double) + std::vector test_list_real = {1.1,2.2,3.3,4.4}; + std::vector test_list_natural = {1,2,3,4}; + std::vector properties; + for(auto const num : test_list_real){ + properties.push_back(geojson::JSONProperty("", num)); + } + for(auto const num : test_list_natural){ + properties.push_back(geojson::JSONProperty("", num)); + } + geojson::JSONProperty mixed_property("mixed_list", properties); + std::vector vec; + mixed_property.as_vector(vec); + ASSERT_EQ(vec.size(), 8); + std::vector all = {1.1,2.2,3.3,4.4,1,2,3,4}; + ASSERT_EQ(vec, all); +} + +TEST_F(JSONProperty_Test, test_as_vector_mixed_list_0a){ + std::vector test_list_real = {1.1,2.2,3.3,4.4}; + std::vector test_list_natural = {1,2,3,4}; + std::vector test_list_str = {"hello", "world"}; + std::vector properties; + for(auto const num : test_list_real){ + properties.push_back(geojson::JSONProperty("", num)); + } + for(auto const num : test_list_natural){ + properties.push_back(geojson::JSONProperty("", num)); + } + for(auto const str : test_list_str){ + properties.push_back(geojson::JSONProperty("", str)); + } + geojson::JSONProperty mixed_property("mixed_list", properties); + std::vector vec; + mixed_property.as_vector(vec); + ASSERT_EQ(vec.size(), 8); + std::vector all = {1.1,2.2,3.3,4.4,1,2,3,4}; + ASSERT_EQ(vec, all); + + std::vector vec2; + mixed_property.as_vector(vec2); + ASSERT_EQ(vec2.size(), 2); + ASSERT_EQ(vec2, test_list_str); + } \ No newline at end of file diff --git a/test/realizations/catchments/Bmi_C_Formulation_Test.cpp b/test/realizations/catchments/Bmi_C_Formulation_Test.cpp index a7f5b079ae..070bf28377 100644 --- a/test/realizations/catchments/Bmi_C_Formulation_Test.cpp +++ b/test/realizations/catchments/Bmi_C_Formulation_Test.cpp @@ -206,7 +206,8 @@ void Bmi_C_Formulation_Test::SetUp() { " }," " \"registration_function\": \"" + registration_functions[i] + "\"," + variables_line + - " \"uses_forcing_file\": " + (uses_forcing_file[i] ? "true" : "false") + "" + " \"uses_forcing_file\": " + (uses_forcing_file[i] ? "true" : "false") + "," + " \"model_params\": { \"PARAM_VAR_1\": 42, \"PARAM_VAR_2\": 4.2, \"PARAM_VAR_3\": [4, 2]}" + " }," " \"forcing\": { \"path\": \"" + forcing_file[i] + "\", \"provider\": \"CsvPerFeature\"}" " }" @@ -266,6 +267,23 @@ TEST_F(Bmi_C_Formulation_Test, GetResponse_0_a) { ASSERT_EQ(response, 00); } +/** Simple test of initial parameter setting. */ +TEST_F(Bmi_C_Formulation_Test, set_initial_parameters_0_a) { + int ex_index = 0; + + Bmi_C_Formulation formulation(catchment_ids[ex_index], std::make_unique(*forcing_params_examples[ex_index]), utils::StreamHandler()); + formulation.create_formulation(config_prop_ptree[ex_index]); + + int param = get_friend_bmi_model(formulation)->GetValue("PARAM_VAR_1")[0]; + ASSERT_EQ(param, 42); + double param2 = get_friend_bmi_model(formulation)->GetValue("PARAM_VAR_2")[0]; + ASSERT_EQ(param2, 4.2); + std::vector param3 = get_friend_bmi_model(formulation)->GetValue("PARAM_VAR_3"); + ASSERT_EQ(param3.size(), 2); + ASSERT_EQ(param3[0], 4.0); + ASSERT_EQ(param3[1], 2.0); +} + /** Test of get response after several iterations. */ TEST_F(Bmi_C_Formulation_Test, GetResponse_0_b) { int ex_index = 0;