New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Extend parse function to allow multiple values per key #3267
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -94,27 +94,37 @@ namespace aspect | |
const std::vector<std::string> &input_field_names, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For some reason, the names of the first two function arguments are different in the declaration and in the implementation here. |
||
const bool has_background_field, | ||
const std::string &property_name, | ||
const bool allow_subentries, | ||
std::shared_ptr<std::vector<unsigned int> > subentry_structure) | ||
const bool allow_multiple_values_per_key, | ||
std::shared_ptr<std::vector<unsigned int> > n_values_per_key) | ||
{ | ||
std::vector<std::string> field_names = input_field_names; | ||
if (has_background_field) | ||
field_names.insert(field_names.begin(),"background"); | ||
|
||
const unsigned int n_fields = field_names.size(); | ||
std::vector<double> return_values; | ||
if (subentry_structure) | ||
subentry_structure->resize(n_fields,0); | ||
|
||
const auto key_pattern = (allow_subentries) | ||
const bool check_structure = (n_values_per_key && n_values_per_key->size() != 0); | ||
const bool store_structure = (n_values_per_key && n_values_per_key->size() == 0); | ||
|
||
if (store_structure) | ||
n_values_per_key->resize(n_fields,0); | ||
|
||
if (check_structure) | ||
AssertThrow(n_values_per_key->size() == n_fields, | ||
ExcMessage("When providing an expected structure for input parameter " + property_name + " you need to provide " | ||
+ "as many entries in the structure vector as there are input field names (+1 if there is a background field). " | ||
+ "The current structure vector has " + std::to_string(n_values_per_key->size()) + " entries, but there are " | ||
+ std::to_string(n_fields) + " field names." )); | ||
|
||
const auto key_pattern = (allow_multiple_values_per_key) | ||
? | ||
Patterns::List(Patterns::Double(), | ||
1, | ||
std::numeric_limits<unsigned int>::max(), | ||
"|") | ||
: | ||
Patterns::Double() | ||
; | ||
Patterns::Double(); | ||
|
||
// Parse the string depending on what Pattern we are dealing with | ||
if (Patterns::Map(Patterns::Anything(), | ||
|
@@ -159,18 +169,29 @@ namespace aspect | |
ExcMessage ("There is only one " | ||
+ property_name | ||
+ " value given. The keyword `all' is " | ||
"expected but is not found. Please" | ||
"expected but is not found. Please " | ||
"check your " | ||
+ property_name | ||
+ " list.")); | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is a space missing in line 162 above. |
||
const std::vector<std::string> values = dealii::Utilities::split_string_list(key_and_value[1], '|'); | ||
|
||
// Assign all the elements to the "all" value | ||
for (unsigned int field_index=0; field_index<n_fields; ++field_index) | ||
{ | ||
return_map[field_index].push_back(Utilities::string_to_double(key_and_value[1])); | ||
for (const auto &value : values) | ||
{ | ||
return_map[field_index].push_back(Utilities::string_to_double(value)); | ||
|
||
if (store_structure) | ||
++(*n_values_per_key)[field_index]; | ||
} | ||
|
||
if (subentry_structure) | ||
(*subentry_structure)[field_index] = 1; | ||
if (check_structure) | ||
AssertThrow((*n_values_per_key)[field_index] == values.size(), | ||
ExcMessage("The key <" + key_and_value[0] + "> in <"+ property_name + "> does not have " | ||
+ "the expected number of values. It expects " + std::to_string((*n_values_per_key)[field_index]) | ||
+ " values, but we found " + std::to_string(values.size()) + " values.")); | ||
} | ||
gassmoeller marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
// Handle lists of multiple entries | ||
|
@@ -237,15 +258,21 @@ namespace aspect | |
"is not set, the default names are " | ||
"C_1, C_2, ..., C_n.")); | ||
|
||
const std::vector<std::string> subfield_entries = dealii::Utilities::split_string_list(key_and_value[1], '|'); | ||
const std::vector<std::string> values = dealii::Utilities::split_string_list(key_and_value[1], '|'); | ||
|
||
for (const auto &entry : subfield_entries) | ||
for (const auto &value : values) | ||
{ | ||
return_map[field_index].push_back(Utilities::string_to_double(entry)); | ||
return_map[field_index].push_back(Utilities::string_to_double(value)); | ||
|
||
if (subentry_structure) | ||
++(*subentry_structure)[field_index]; | ||
if (store_structure) | ||
++(*n_values_per_key)[field_index]; | ||
} | ||
|
||
if (check_structure) | ||
AssertThrow((*n_values_per_key)[field_index] == values.size(), | ||
ExcMessage("The key <" + key_and_value[0] + "> in <"+ property_name + "> does not have " | ||
+ "the expected number of values. It expects " + std::to_string((*n_values_per_key)[field_index]) | ||
+ " values, but we found " + std::to_string(values.size()) + " values.")); | ||
} | ||
} | ||
|
||
|
@@ -260,10 +287,18 @@ namespace aspect | |
n_fields, | ||
property_name); | ||
|
||
if (subentry_structure) | ||
if (store_structure) | ||
{ | ||
for (unsigned int i=0; i<n_fields; ++i) | ||
(*n_values_per_key)[i] = 1; | ||
} | ||
|
||
if (check_structure) | ||
{ | ||
for (unsigned int i=0; i<n_fields; ++i) | ||
(*subentry_structure)[i] = 1; | ||
AssertThrow((*n_values_per_key)[i] == 1, | ||
ExcMessage("The input parameter " + property_name + " does not have " | ||
+ "the expected number of values for field index " + std::to_string(i) + ".")); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I find this error message a bit confusing. If this error is thrown, it means someone specified a list of doubles in the input file with one value per key, but some other parameter was specified with multiple values per key, provided in the key:value format (or there is some other reason why the function expects more than one value per key). So to help the user a bit more, I would tell the user how many entries were expected, and tell them something like: "To specify more than one value per field for this input parameter, you need to use the format :value1|value2|..." |
||
} | ||
} | ||
else | ||
|
@@ -273,7 +308,8 @@ namespace aspect | |
ExcMessage ("The required format for field <" | ||
+ property_name | ||
+ "> was not found. Specify a comma separated " | ||
"list of `<double>' or `<id> : <double>|<double>|...'.")); | ||
+ "list of `<double>' or `<key1> : <double>|<double>|..., " | ||
+ "<key2> : <double>|... , ... '.")); | ||
} | ||
return return_values; | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -84,32 +84,78 @@ TEST_CASE("Utilities::parse_map_to_double_array") | |
|
||
{ | ||
INFO("check 9: "); | ||
auto subentry_structure = std::make_shared<std::vector<unsigned int>>(); | ||
auto n_values_per_key = std::make_shared<std::vector<unsigned int>>(); | ||
|
||
compare_vectors_approx(aspect::Utilities::parse_map_to_double_array ("C1:100, C2:200|100, C3:300, C4:400, C5:500, background:0", | ||
{"C1","C2","C3","C4","C5"}, | ||
true, | ||
"TestField", | ||
true, | ||
subentry_structure), | ||
n_values_per_key), | ||
{0.0,100.0,200.0,100.0,300.0,400.0,500.0}); | ||
|
||
REQUIRE(*subentry_structure == std::vector<unsigned int>({1,1,2,1,1,1})); | ||
REQUIRE(*n_values_per_key == std::vector<unsigned int>({1,1,2,1,1,1})); | ||
} | ||
|
||
{ | ||
INFO("check 10: "); | ||
auto subentry_structure = std::make_shared<std::vector<unsigned int>>(); | ||
auto n_values_per_key = std::make_shared<std::vector<unsigned int>>(); | ||
|
||
compare_vectors_approx(aspect::Utilities::parse_map_to_double_array ("C1:100, C2:200|, C3:300, C4:400, C5:500", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh, we actually allow to use the | but then not have an entry? Is that intended? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That is allowed by deal.II (it depends on the implementation of the |
||
{"C1","C2","C3","C4","C5"}, | ||
false, | ||
"TestField", | ||
true, | ||
subentry_structure), | ||
n_values_per_key), | ||
{100.0,200.0,300.0,400.0,500.0}); | ||
|
||
REQUIRE(*subentry_structure == std::vector<unsigned int>({1,1,1,1,1})); | ||
REQUIRE(*n_values_per_key == std::vector<unsigned int>({1,1,1,1,1})); | ||
} | ||
|
||
{ | ||
INFO("check 11: "); | ||
auto n_values_per_key = std::make_shared<std::vector<unsigned int>>(); | ||
|
||
compare_vectors_approx(aspect::Utilities::parse_map_to_double_array ("C1:100, C2:200|300, C3:300, C4:400, C5:500", | ||
{"C1","C2","C3","C4","C5"}, | ||
false, | ||
"TestField", | ||
true, | ||
n_values_per_key), | ||
{100.0,200.0,300.0,300.0,400.0,500.0}); | ||
|
||
REQUIRE(*n_values_per_key == std::vector<unsigned int>({1,2,1,1,1})); | ||
|
||
compare_vectors_approx(aspect::Utilities::parse_map_to_double_array ("C1:100, C2:200|300, C3:300, C4:400, C5:500", | ||
{"C1","C2","C3","C4","C5"}, | ||
false, | ||
"TestField", | ||
true, | ||
n_values_per_key), | ||
{100.0,200.0,300.0,300.0,400.0,500.0}); | ||
} | ||
|
||
{ | ||
INFO("check 12: "); | ||
auto n_values_per_key = std::make_shared<std::vector<unsigned int>>(); | ||
|
||
compare_vectors_approx(aspect::Utilities::parse_map_to_double_array ("all:300|400", | ||
{"C1","C2"}, | ||
false, | ||
"TestField", | ||
true, | ||
n_values_per_key), | ||
{300.0,400.0,300.0,400.0}); | ||
|
||
REQUIRE(*n_values_per_key == std::vector<unsigned int> ({2,2})); | ||
|
||
compare_vectors_approx(aspect::Utilities::parse_map_to_double_array ("all:100|200", | ||
{"C1","C2"}, | ||
false, | ||
"TestField", | ||
true, | ||
n_values_per_key), | ||
{100.0,200.0,100.0,200.0}); | ||
} | ||
|
||
INFO("check complete"); | ||
|
@@ -169,13 +215,13 @@ TEST_CASE("Utilities::parse_map_to_double_array FAIL ON PURPOSE") | |
// Subentries not allowed | ||
INFO("check fail 7: "); | ||
REQUIRE_THROWS_WITH( | ||
aspect::Utilities::parse_map_to_double_array ("C1:100, C2:|200, C3:300, C4:400, C5:500", | ||
aspect::Utilities::parse_map_to_double_array ("C1:100, C2:100|200, C3:300, C4:400, C5:500", | ||
{"C1","C2","C3","C4","C5"}, | ||
false, | ||
"TestField"), Contains("The required format for field")); | ||
|
||
// Wrong subentry format | ||
INFO("check fail 7: "); | ||
INFO("check fail 8: "); | ||
REQUIRE_THROWS_WITH( | ||
aspect::Utilities::parse_map_to_double_array ("C1:100, C2:|200, C3:300, C4:400, C5:500", | ||
{"C1","C2","C3","C4","C5"}, | ||
|
@@ -184,13 +230,60 @@ TEST_CASE("Utilities::parse_map_to_double_array FAIL ON PURPOSE") | |
true), Contains("The required format for field")); | ||
|
||
// No subentries | ||
INFO("check fail 8: "); | ||
INFO("check fail 9: "); | ||
REQUIRE_THROWS_WITH( | ||
aspect::Utilities::parse_map_to_double_array ("C1:100, C2:|, C3:300, C4:400, C5:500", | ||
{"C1","C2","C3","C4","C5"}, | ||
false, | ||
"TestField", | ||
true), Contains("The required format for field")); | ||
|
||
// Wrong input structure | ||
{ | ||
INFO("check fail 10: "); | ||
auto n_values_per_key = std::make_shared<std::vector<unsigned int>>(); | ||
|
||
compare_vectors_approx(aspect::Utilities::parse_map_to_double_array ("C1:100, C2:200|300, C3:300, C4:400, C5:500", | ||
{"C1","C2","C3","C4","C5"}, | ||
false, | ||
"TestField", | ||
true, | ||
n_values_per_key), | ||
{100.0,200.0,300.0,300.0,400.0,500.0}); | ||
|
||
REQUIRE(*n_values_per_key == std::vector<unsigned int>({1,2,1,1,1})); | ||
|
||
REQUIRE_THROWS_WITH(aspect::Utilities::parse_map_to_double_array ("C1:100|200, C2:300, C3:300, C4:400, C5:500", | ||
{"C1","C2","C3","C4","C5"}, | ||
false, | ||
"TestField", | ||
true, | ||
n_values_per_key), | ||
Contains("the expected number of entries")); | ||
} | ||
|
||
{ | ||
INFO("check fail 11: "); | ||
auto n_values_per_key = std::make_shared<std::vector<unsigned int>>(); | ||
|
||
compare_vectors_approx(aspect::Utilities::parse_map_to_double_array ("all:300|400", | ||
{"C1","C2"}, | ||
false, | ||
"TestField", | ||
true, | ||
n_values_per_key), | ||
{300.0,400.0,300.0,400.0}); | ||
|
||
REQUIRE(*n_values_per_key == std::vector<unsigned int>({2,2})); | ||
|
||
REQUIRE_THROWS_WITH(aspect::Utilities::parse_map_to_double_array ("all:100|200|300", | ||
{"C1","C2"}, | ||
false, | ||
"TestField", | ||
true, | ||
n_values_per_key), | ||
Contains("the expected number of entries")); | ||
} | ||
} | ||
|
||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should add some documentation here that explains the new structure with subentries. That could also be a good place to explain why the
subentry_structure
is useful (it means whoever calls the function can figure out which value belongs to which key).