Skip to content
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

Merged
merged 4 commits into from Oct 22, 2019

Conversation

gassmoeller
Copy link
Member

This PR extends Utilities::parse_map_to_double_array to allow multiple entries per key using the syntax: key1: value1|value2|value3, key2: value1, key3: value1|value2. We would like to use this syntax to make progress with defining phase transitions for multiple compositional fields (like #2656). The unit tests should cover all cases, so I can make this as a separate PR without actual implementations using it (it will be used by several rheologies, equations of state and material models).

  • I have tested my new feature locally to ensure it is correct.
  • I have created a testcase for the new feature/benchmark in the tests/ directory.

@gassmoeller
Copy link
Member Author

@jdannberg, @naliboff, @mibillen can one of you take a look if this is what we talked about?

Copy link
Contributor

@naliboff naliboff left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Only caught one potential typo, but at first read through looks good!

source/utilities.cc Outdated Show resolved Hide resolved
@gassmoeller
Copy link
Member Author

Thanks for the quick review. I have fixed the comment and added one more functionality we will need. That is to return the actual substructure of the returned vector. As discussed we need this to ensure that all entries actually have the same number of phases for each individual composition (e.g. to distinguish a:1|2,b:3 from a:1,b:2|3).

Copy link
Contributor

@jdannberg jdannberg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very nice! I really like the structure, and the parsing is not that much more complicated.

* is set to 'true' it also allows entries of the form
* "key1: value1|value2|value3, etc", in which case the returned
* vector will have more entries than the provided
* @p list_of_keys.
*
* This function also considers a number of special cases:
* - If the input string consists of only a comma separated
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you want to add comment here that mentions that you can not do this if allow_subentries == true because then the notation does not provide enough information?

* @param [in] allow_subentries If true allow having multiple subentries
* for each key. If false only allow a single entry per key. In either
* case each key is only allowed to appear once.
* @param [out] subentry_structure If handed over this
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To me, this name does not seem as descriptive as it could be. What about something like values_per_key or n_values_per_key? That would also be consistent with the naming of the other input parameters.

INFO("check 10: ");
auto subentry_structure = 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",
Copy link
Contributor

Choose a reason for hiding this comment

The 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?

Copy link
Member Author

Choose a reason for hiding this comment

The 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 split_string_list function, which explicitly allows this case). I think this is fine, likely nobody wants to use it.

// 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",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't this test be more helpful and easier to read if the output format was correct (and only the subentries are not allowed)? This way, how do you know which of the two it is?


for (const auto &entry: return_map)
for (const auto &sub_entry: entry)
return_values.push_back(sub_entry);
}
else if (Patterns::List(Patterns::Double(),1,n_fields).match(input_string))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should make sure that allow_subentries is false in this case.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would not want to do that. Consider the case that somebody wants to allow multiple values per key, but does not know the input structure the user puts into the .prm file. You would still want to allow a simple list of only a single value per key. The parameter is allow_multiple_values_per_key, not enforce_multiple_values_per_key. But you are right, I should document that this format expects exactly one value per key (and we already enforce that, because possibly_extend_from_1_to_N expects either 1 entry, or n_fields entries).

"|")
:
Patterns::Double()
;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move that to the end of the line before?

@@ -146,7 +166,12 @@ namespace aspect

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a space missing in line 162 above.

source/utilities.cc Show resolved Hide resolved
* vector will be filled with as many components as
* keys (+1 if there is a background field). Each value represents
* how many subentries were found for this key. The sum over all
* entries is the length of the returned vector of values.
*
* @return A vector of values that are parsed from the map, provided
* in the order in which the keys appear in the @p list_of_keys argument.
Copy link
Contributor

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).

@gassmoeller
Copy link
Member Author

I think I addressed all comments, and also added the functionality to hand over an existing vector of n_values_per_key that can be used as the expected template for the input parameter. This saves us from implementing Asserts for every parameter to check that its structure is the same as the last one that was parsed. Every change is in the last commit.

@gassmoeller gassmoeller changed the title Extend parse function to allow subentries Extend parse function to allow multiple values per key Oct 21, 2019
Copy link
Contributor

@jdannberg jdannberg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very nice. Please address my two small comments, and then this should be good to go.

@@ -94,27 +94,37 @@ namespace aspect
const std::vector<std::string> &input_field_names,
Copy link
Contributor

Choose a reason for hiding this comment

The 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.

(*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) + "."));
Copy link
Contributor

Choose a reason for hiding this comment

The 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|..."

@gassmoeller
Copy link
Member Author

I addressed the comments. Thanks for the review!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants