Teuchos: Modified parameter lists
Created by: danielsjensen1
@trilinos/Teuchos
enhancement
Expectations
Parameter lists currently don't handle validation of dependent parameters, optional sublists/parameters, and template-like expansion of sublists/parameters.
Current Behavior
Examples of limitations:
Validation of dependent parameters
In the time-stepping block below there are three interdependent parameters. The current validation method (validateParametersAndSetDefaults) can ensure that all of the values are valid floats and even positive values but there is not an obvious way to ensure that they are consistent without doing a postprocessing reconciliation step. We would like to attach a reconciliation method to parameter lists to handle such cases.
Time Stepping:
Final Time: 1.e-0
Time Step: 1.e-3
Number of Timesteps: ?
Optional sublists/parameters
In the following Equations block there could be three types of equations specified with different optional parameters. In order to validate these sublists a valid parameter list (let's call it valid_pl) with the correct structure and valid defaults needs to be created and have only the sublists that the user specifies. This is not possible to do without first looking at the user's input parameter list to see which of the optional sublists are included before validation. We propose the attachment of a modifier method to parameter lists that can do this modification to valid_pl before the validation method is run.
Equations:
Laguerre:
Order: 1
Hermite:
Order: 2
Hypergeometric:
Order: 0
Expanding sublists
Often we would like to expand a sublist based on a template and base name as in the example below with Laguerre 1 and Laguerre 2 having the same structure. This also requires parsing the user's input parameter list and creating a new valid parameter list based on the sublists discovered. The modifier method described above can handle this situation in a similar fashion to the 'Optional sublists/parameters' issue described above.
Equations:
Laguerre 1:
Order: 1
Laguerre 2:
Order: 2
Motivation and Context
In scientific programs of significant complexity, the current parameter list validation implementation is not sufficient and significant amounts of code are added to achieve the desired functionality. We propose that these modification and reconciliation methods be standardized and added to the current parameter list functionality.
The pull request https://github.com/trilinos/Trilinos/pull/4834 allows one to attach a modifier to any given parameter (sub)list similar to the way that validators are attached to parameters. (The name "modifier" could be changed maybe even to "validator" if desired.) Each modifier can have a modify and reconcile method. The modify method looks at the user's input parameter list (input_pl) and modifies the validation parameter list (valid_pl) to contain the same structure by handling optional parameters/sublists. After calling the modify method (usually recursively) one would typically call the validateParametersAndSetDefaults method. After all of the parameters exist and are of the correct type the reconcile method can be called (usually recursively) to check that all interdependent parameters are valid. A simple example of this process is shown below. The new tests mentioned in "Possible Solution" contain additional examples.
using Teuchos::ParameterListModifier;
using Teuchos::ParameterList;
class TimeSteppingModifier: public ParameterListModifier{
public:
TimeSteppingModifier(): ParameterListModifier("Time Stepping Modifier"){}
void reconcile(ParameterList &pl) const{
double &tf = pl.get<double>("Final Time");
double &dt = pl.get<double>("Time Step");
int &num_dt = pl.get<int>("Number of Time Steps");
double tf_new, dt_new;
int num_dt_new;
if (tf > 0.0 and dt > 0.0){
num_dt_new = std::ceil(tf / dt);
if (num_dt > 0){
TEUCHOS_ASSERT_EQUALITY(num_dt, num_dt_new);
} else{
num_dt = num_dt_new;
}
} else if (dt > 0.0 and num_dt > 0){
tf_new = num_dt * dt;
if (tf > 0.0){
TEUCHOS_ASSERT_EQUALITY(tf, tf_new);
} else{
tf = tf_new;
}
} else if (tf > 0.0 and num_dt > 0){
dt_new = tf / static_cast<double>(num_dt);
if (dt > 0.0){
TEUCHOS_ASSERT_EQUALITY(dt, dt_new);
} else{
dt = dt_new;
}
} else{
throw std::logic_error("Less than two positive time parameters, "
"(""Final Time"", ""Time Step"", ""Number of Timesteps""), given.");
}
}
};
class EquationsModifier: public ParameterListModifier{
public:
EquationsModifier(): ParameterListModifier("Equations Modifier"){}
void modify(ParameterList &pl, ParameterList &valid_pl) const{
int num_expanded = 0;
num_expanded += expandSublistsUsingBaseName("Laguerre", pl, valid_pl);
num_expanded += expandSublistsUsingBaseName("Hermite", pl, valid_pl);
num_expanded += expandSublistsUsingBaseName("Hypergeometric", pl, valid_pl);
if (num_expanded < 1){
throw std::logic_error("There must be at least one equation.");
}
}
};
RCP<TimeSteppingModifier> time_stepping_modifier = rcp(new TimeSteppingModifier());
RCP<EquationsModifier> equations_modifier = rcp(new EquationsModifier());
ParameterList valid_pl = ParameterList("valid_pl");
valid_pl.sublist("Time Stepping", time_stepping_modifier);
valid_pl.sublist("Time Stepping").set("Final Time", 0.0);
valid_pl.sublist("Time Stepping").set("Time Step", 0.0);
valid_pl.sublist("Time Stepping").set("Number of Time Steps", 0);
valid_pl.sublist("Equations", equations_modifier);
valid_pl.sublist("Equations").sublist("Laguerre").set("Order", 0);
valid_pl.sublist("Equations").sublist("Hermite").set("Order", 0);
valid_pl.sublist("Equations").sublist("Hypergeometric").set("Order", 0);
ParameterList pl = ParameterList("pl");
pl.sublist("Time Stepping").set("Final Time", 1.0);
pl.sublist("Time Stepping").set("Time Step", 1e-3);
// If "Number of Time Steps" is set below then it must be consistent with
// "Final Time" and "Time Step" above.
// pl.sublist("Time Stepping").set("Number of Time Steps", 10);
pl.sublist("Equations").sublist("Laguerre 1").set("Order", 3);
pl.sublist("Equations").sublist("Laguerre 2").set("Order", 5);
pl.sublist("Equations").sublist("Hermite 1").set("Order", 0);
pl.modifyParameterList(valid_pl);
pl.validateParametersAndSetDefaults(valid_pl);
pl.reconcileParameterList(valid_pl);
pl.print();
Output:
Time Stepping ->
Final Time = 1
Time Step = 0.001
Number of Time Steps = 1000 [default]
Equations ->
Laguerre 1 ->
Order = 3 [unused]
Laguerre 2 ->
Order = 5 [unused]
Hermite 1 ->
Order = 0 [unused]
Definition of Done
-
Unit tests added for all additional modification and reconciliation methods -
Documentation added with usage examples as well -
Updates to tutorials?
Possible Solution
A merge request will be attached to this issue with an initial implementation, unit tests, and documentation.
Steps to Reproduce
Your Environment
- Relevant repo SHA1s:
- Relevant configure flags or configure script:
- Operating system and version:
- Compiler and TPL versions:
Related Issues
- Blocks
- Is blocked by
- Follows
- Precedes
- Related to
- Part of
- Composed of