Classes | |
class | ExcAlreadyAtTopLevel |
class | ExcConversionError |
class | ExcDefaultDoesNotMatchPattern |
class | ExcEntryAlreadyExists |
class | ExcEntryUndeclared |
struct | Section |
Public Types | |
enum | OutputStyle { Text = 1, LaTeX = 2, Description = 3, ShortText = 193 } |
Public Member Functions | |
ParameterHandler () | |
virtual | ~ParameterHandler () |
virtual bool | read_input (std::istream &input) |
virtual bool | read_input (const std::string &filename, const bool optional=false, const bool write_stripped_file=false) |
virtual bool | read_input_from_string (const char *s) |
void | clear () |
void | declare_entry (const std::string &entry, const std::string &default_value, const Patterns::PatternBase &pattern=Patterns::Anything(), const std::string &documentation=std::string()) |
void | enter_subsection (const std::string &subsection) |
bool | leave_subsection () |
const std::string & | get (const std::string &entry_string) const |
long int | get_integer (const std::string &entry_string) const |
double | get_double (const std::string &entry_name) const |
bool | get_bool (const std::string &entry_name) const |
void | set (const std::string &entry_name, const std::string &new_value) |
void | set (const std::string &entry_name, const char *new_value) |
void | set (const std::string &entry_name, const long int &new_value) |
void | set (const std::string &entry_name, const double &new_value) |
void | set (const std::string &entry_name, const bool &new_value) |
std::ostream & | print_parameters (std::ostream &out, const OutputStyle style) |
void | print_parameters_section (std::ostream &out, const OutputStyle style, const unsigned int indent_level) |
void | log_parameters (LogStream &out) |
void | log_parameters_section (LogStream &out) |
unsigned int | memory_consumption () const |
Private Member Functions | |
ParameterHandler (const ParameterHandler &) | |
ParameterHandler & | operator= (const ParameterHandler &) |
bool | scan_line (std::string line, const unsigned int lineno) |
Section * | get_present_defaults_subsection () |
const Section * | get_present_defaults_subsection () const |
Section * | get_present_changed_subsection () |
const Section * | get_present_changed_subsection () const |
Private Attributes | |
std::vector< std::string > | subsection_path |
Section | defaults |
Section | changed_entries |
Friends | |
class | MultipleParameterLoop |
* set Time step size = 0.3 * set Geometry = [0,1]x[0,3] *
The ParameterHandler class is discussed in detail in the step-19 example program, and is used in more realistic situations in step-29, step-33 and step-34.
In order to use the facilities of a ParameterHandler object, one first has to make known the different entries the input file may or may not contain. This is done in the following way:
... ParameterHandler prm; prm.declare_entry ("Time step size", "0.2", Patterns::Double(), "Some documentation"); prm.declare_entry ("Geometry", "[0,1]x[0,1]", Patterns::Anything()); ...
Entries may be located in subsections which form a kind of input tree. For example input parameters for linear solver routines should be classified in a subsection named Linear solver
or any other suitable name. This is accomplished in the following way:
... LinEq eq; eq.declare_parameters (prm); ... void LinEq::declare_parameters (ParameterHandler &prm) { prm.enter_subsection("Linear solver"); prm.declare_entry ("Solver", "CG", Patterns::Selection("CG|GMRES|GaussElim"), "Name of a linear solver for the inner iteration"); prm.declare_entry ("Maximum number of iterations", "20", ParameterHandler::RegularExpressions::Integer()); ... prm.leave_subsection (); }
Subsections may be nested. For example a nonlinear solver may have a linear solver as member object. Then the function call tree would be something like (if the class NonLinEq
has a member variables eq
of type LinEq
):
void NonLinEq::declare_parameters (ParameterHandler &prm) { prm.enter_subsection ("Nonlinear solver"); prm.declare_entry ("Nonlinear method", "Newton-Raphson", ParameterHandler::RegularExpressions::Anything()); eq.declare_parameters (prm); prm.leave_subsection (); }
For class member functions which declare the different entries we propose to use the common name declare_parameters
. In normal cases this method can be static
since the entries will not depend on any previous knowledge. Classes for which entries should logically be grouped into subsections should declare these subsections themselves. If a class has two or more member variables of the same type both of which should have their own parameters, this parent class' method declare_parameters
is responsible to group them into different subsections:
void NonLinEq::declare_parameters (ParameterHandler &prm) { prm.enter_subsection ("Nonlinear solver"); prm.enter_subsection ("Linear solver 1"); eq1.declare_parameters (prm); prm.leave_subsection (); prm.enter_subsection ("Linear solver 2"); eq2.declare_parameters (prm); prm.leave_subsection (); prm.leave_subsection (); }
For the first example above the input file would look like the following:
* ... * subsection Nonlinear solver * set Nonlinear method = Gradient * subsection Linear solver * set Solver = CG * set Maxmimum number of iterations = 30 * end * end * ... # other stuff *
subsection
, set
and end
may be either written in lowercase or uppercase letters. Leading and trailing whitespace is removed, multiple whitespace is condensed into only one. Since the latter applies also to the name of an entry, an entry name will not be recognised if in the declaration multiple whitespace is used.
In entry names and values the following characters are not allowed: #
, {
, }
, |
. Their use is reserved for the MultipleParameterLoop class.
Comments starting with # are skipped.
We propose to use the following scheme to name entries: start the first word with a capital letter and use lowercase letters further on. The same applies to the possible entry values to the right of the =
sign.
In order to read input you can use three possibilities: reading from an std::istream
object, reading from a file of which the name is given and reading from a string in memory in which the lines are separated by \n
characters. These possibilites are used as follows:
ParameterHandler prm; ... // declaration of entries ... prm.read_input (cin); // read input from standard in, // or prm.read_input ("simulation.in"); // or char *in = "set Time step size = 0.3 \n ..."; prm.read_input_from_string (in); ...
.prm
.You should not try to declare entries using declare_entry() and enter_subsection() with as yet unknown subsection names after using read_input(). The results in this case are unspecified.
If an error occurs upon reading the input, error messages are written to std::cerr
.
Each class gets its data out of a ParameterHandler object by calling the get() member functions like this:
void NonLinEq::get_parameters (ParameterHandler &prm) { prm.enter_subsection ("Nonlinear solver"); std::string method = prm.get ("Nonlinear method"); eq.get_parameters (prm); prm.leave_subsection (); }
It is guaranteed that only entries matching the given regular expression are returned, i.e. an input entry value which does not match the regular expression is not stored.
You can use get() to retrieve the parameter in text form, get_integer() to get an integer or get_double() to get a double. You can also use get_bool(). It will cause an internal error if the string could not be converted to an integer, double or a bool. This should, though, not happen if you correctly specified the regular expression for this entry; you should not try to get out an integer or a double from an entry for which no according regular expression was set. The internal error is raised through the Assert() macro family which only works in debug mode.
If you want to print out all user selectable features, use the print_parameters() function. It is generally a good idea to print all parameters at the beginning of a log file, since this way input and output are together in one file which makes matching at a later time easier. Additionally, the function also print those entries which have not been modified in the input file und are thus set to default values; since default values may change in the process of program development, you cannot know the values of parameters not specified in the input file.
We propose that every class which gets data out of a ParameterHandler object provides a function named get_parameters
. This should be declared virtual
. get_parameters
functions in derived classes should call the BaseClass::get_parameters
function.
Experience has shown that in programs defining larger numbers of parameters (more than, say, fifty) it is advantageous to define an additional class holding these parameters. This class is more like a C-style structure, having a large number of variables, usually public. It then has at least two functions, which declare and parse the parameters. In the main program, the main class has an object of this parameter class and delegates declaration and parsing of parameters to this object.
The advantage of this approach is that you can keep out the technical details (declaration and parsing) out of the main class and additionally don't clutter up your main class with dozens or more variables denoting the parameters.
This is the code:
#include <iostream> #include "../include/parameter_handler.h" DEAL_II_NAMESPACE_OPEN class LinEq { public: static void declare_parameters (ParameterHandler &prm); void get_parameters (ParameterHandler &prm); private: std::string Method; int MaxIterations; }; class Problem { private: LinEq eq1, eq2; std::string Matrix1, Matrix2; std::string outfile; public: static void declare_parameters (ParameterHandler &prm); void get_parameters (ParameterHandler &prm); }; void LinEq::declare_parameters (ParameterHandler &prm) { // declare parameters for the linear // solver in a subsection prm.enter_subsection ("Linear solver"); prm.declare_entry ("Solver", "CG", Patterns::Selection("CG|BiCGStab|GMRES"), "Name of a linear solver for the inner iteration"); prm.declare_entry ("Maximum number of iterations", "20", Patterns::Integer()); prm.leave_subsection (); } void LinEq::get_parameters (ParameterHandler &prm) { prm.enter_subsection ("Linear solver"); Method = prm.get ("Solver"); MaxIterations = prm.get_integer ("Maximum number of iterations"); prm.leave_subsection (); std::cout << " LinEq: Method=" << Method << ", MaxIterations=" << MaxIterations << std::endl; } void Problem::declare_parameters (ParameterHandler &prm) { // first some global parameter entries prm.declare_entry ("Output file", "out", Patterns::Anything(), "Name of the output file, either relative to the present" "path or absolute"); prm.declare_entry ("Equation 1", "Laplace", Patterns::Anything(), "String identifying the equation we want to solve"); prm.declare_entry ("Equation 2", "Elasticity", Patterns::Anything()); // declare parameters for the // first equation prm.enter_subsection ("Equation 1"); prm.declare_entry ("Matrix type", "Sparse", Patterns::Selection("Full|Sparse|Diagonal"), "Type of the matrix to be used, either full," "sparse, or diagonal"); LinEq::declare_parameters (prm); // for eq1 prm.leave_subsection (); // declare parameters for the // second equation prm.enter_subsection ("Equation 2"); prm.declare_entry ("Matrix type", "Sparse", Patterns::Selection("Full|Sparse|Diagonal")); LinEq::declare_parameters (prm); // for eq2 prm.leave_subsection (); } void Problem::get_parameters (ParameterHandler &prm) { // entries of the problem class outfile = prm.get ("Output file"); std::string equation1 = prm.get ("Equation 1"), equation2 = prm.get ("Equation 2"); // get parameters for the // first equation prm.enter_subsection ("Equation 1"); Matrix1 = prm.get ("Matrix type"); eq1.get_parameters (prm); // for eq1 prm.leave_subsection (); // get parameters for the // second equation prm.enter_subsection ("Equation 2"); Matrix2 = prm.get ("Matrix type"); eq2.get_parameters (prm); // for eq2 prm.leave_subsection (); std::cout << " Problem: outfile=" << outfile << std::endl << " eq1=" << equation1 << ", eq2=" << equation2 << std::endl << " Matrix1=" << Matrix1 << ", Matrix2=" << Matrix2 << std::endl; } void main () { ParameterHandler prm; Problem p; p.declare_parameters (prm); // read input from "prmtest.prm"; giving // argv[1] would also be a good idea prm.read_input ("prmtest.prm"); // print parameters to std::cout as ASCII text std::cout << std::endl << std::endl; prm.print_parameters (std::cout, ParameterHandler::Text); // get parameters into the program std::cout << std::endl << std::endl << "Getting parameters:" << std::endl; p.get_parameters (prm); // now run the program with these // input parameters p.do_something (); }
This is the input file (named "prmtest.prm"):
* # first declare the types of equations * set Equation 1 = Poisson * set Equation 2 = Navier-Stokes * * subsection Equation 1 * set Matrix type = Sparse * subsection Linear solver # parameters for linear solver 1 * set Solver = Gauss-Seidel * set Maximum number of iterations = 40 * end * end * * subsection Equation 2 * set Matrix type = Full * subsection Linear solver * set Solver = CG * set Maximum number of iterations = 100 * end * end *
And here is the ouput of the program:
* Line 8: * The entry value * Gauss-Seidel * for the entry named * Solver * does not match the given regular expression * CG|BiCGStab|GMRES * * * Listing of Parameters * --------------------- * set Equation 1 = Poisson # Laplace * set Equation 2 = Navier-Stokes # Elasticity * set Output file = out * subsection Equation 1 * set Matrix type = Sparse # Sparse * subsection Linear solver * set Maximum number of iterations = 40 # 20 * set Solver = CG * end * end * subsection Equation 2 * set Matrix type = Full # Sparse * subsection Linear solver * set Maximum number of iterations = 100 # 20 * set Solver = CG # CG * end * end * * * Getting parameters: * LinEq: Method=CG, MaxIterations=40 * LinEq: Method=CG, MaxIterations=100 * Problem: outfile=out * eq1=Poisson, eq2=Navier-Stokes * Matrix1=Sparse, Matrix2=Full *
This class is inspired by the MenuSystem
class of DiffPack
.
List of possible output formats.
The formats down the list with prefix Short and bit 6 and 7 set reproduce the old behavior of not writing comments or original values to the files.
Text | Write human readable output suitable to be read by ParameterHandler again. |
LaTeX | Write paramteters as a LaTeX table. |
Description | Write out declared parameters with description and possible values. |
ShortText | Write input for ParameterHandler without comments or changed default values. |
ParameterHandler::ParameterHandler | ( | const ParameterHandler & | ) | [private] |
Inhibit automatic CopyConstructor.
ParameterHandler::ParameterHandler | ( | ) |
Constructor.
virtual ParameterHandler::~ParameterHandler | ( | ) | [virtual] |
Destructor. Declare this only to have a virtual destructor, which is safer as we have virtual functions. It actually does nothing spectacular.
ParameterHandler& ParameterHandler::operator= | ( | const ParameterHandler & | ) | [private] |
Inhibit automatic assignment operator.
virtual bool ParameterHandler::read_input | ( | std::istream & | input | ) | [virtual] |
Read input from a stream until stream returns eof
condition or error.
Return whether the read was successful.
Reimplemented in MultipleParameterLoop.
virtual bool ParameterHandler::read_input | ( | const std::string & | filename, | |
const bool | optional = false , |
|||
const bool | write_stripped_file = false | |||
) | [virtual] |
Read input from a file the name of which is given. The PathSearch class "PARAMETERS" is used to find the file.
Return whether the read was successful.
Unless optional
is true
, this function will automatically generate the requested file with default values if the file did not exist. This file will not contain additional comments if write_stripped_file
is true
.
Reimplemented in MultipleParameterLoop.
virtual bool ParameterHandler::read_input_from_string | ( | const char * | s | ) | [virtual] |
Read input from a string in memory. The lines in memory have to be separated by \n
characters.
Return whether the read was successful.
Reimplemented in MultipleParameterLoop.
void ParameterHandler::clear | ( | ) |
Clear all contents.
void ParameterHandler::declare_entry | ( | const std::string & | entry, | |
const std::string & | default_value, | |||
const Patterns::PatternBase & | pattern = Patterns::Anything() , |
|||
const std::string & | documentation = std::string() | |||
) |
Declare a new entry with name entry
, default and for which any input has to match the pattern
(default: any pattern).
The last parameter defaulting to an empty string is used to add a documenting text to each entry which will be printed as a comment when this class is asked to write out all declarations to a stream using the print_parameters() function.
The function generates an exception if the default value doesn't match the given pattern. An entry can be declared more than once without generating an error, for example to override an earlier default value.
void ParameterHandler::enter_subsection | ( | const std::string & | subsection | ) |
Enter a subsection; if not yet existent, declare it.
bool ParameterHandler::leave_subsection | ( | ) |
Leave present subsection. Return false
if there is no subsection to leave; true
otherwise.
const std::string& ParameterHandler::get | ( | const std::string & | entry_string | ) | const |
Return value of entry entry_string
. If the entry was changed, then the changed value is returned, otherwise the default value. If the value of an undeclared entry is required, an empty string is returned and assert
is used to check whether this entry was declared (therefore an exception may be thrown).
long int ParameterHandler::get_integer | ( | const std::string & | entry_string | ) | const |
Return value of entry entry_string
as long int
. (A long int is chosen so that even very large unsigned values can be returned by this function.)
double ParameterHandler::get_double | ( | const std::string & | entry_name | ) | const |
Return value of entry entry_name
as double
.
bool ParameterHandler::get_bool | ( | const std::string & | entry_name | ) | const |
Return value of entry entry_name
as bool
. The entry may be "true" or "yes" for true
, "false" or "no" for false
respectively.
void ParameterHandler::set | ( | const std::string & | entry_name, | |
const std::string & | new_value | |||
) |
Change the value presently stored for entry_name
to the one given in the second argument.
The parameter must already exist in the present subsection.
void ParameterHandler::set | ( | const std::string & | entry_name, | |
const char * | new_value | |||
) |
Same as above, but an overload where the second argument is a character pointer. This is necessary, since otherwise the call to set("abc","def")
will be mapped to the function taking one string and a bool as arguments, which is certainly not what is most often intended.
void ParameterHandler::set | ( | const std::string & | entry_name, | |
const long int & | new_value | |||
) |
Change the value presently stored for entry_name
to the one given in the second argument.
The parameter must already exist in the present subsection.
void ParameterHandler::set | ( | const std::string & | entry_name, | |
const double & | new_value | |||
) |
Change the value presently stored for entry_name
to the one given in the second argument.
The parameter must already exist in the present subsection.
For internal purposes, the new value needs to be converted to a string. This is done using 16 digits of accuracy, so the set value and the one you can get back out using get_double() may differ in the 16th digit.
void ParameterHandler::set | ( | const std::string & | entry_name, | |
const bool & | new_value | |||
) |
Change the value presently stored for entry_name
to the one given in the second argument.
The parameter must already exist in the present subsection.
std::ostream& ParameterHandler::print_parameters | ( | std::ostream & | out, | |
const OutputStyle | style | |||
) |
Print all parameters with the given style to out
. Presently only Text
and LaTeX
are implemented.
In Text
format, the output is formatted in such a way that it is possible to use it for later input again. This is most useful to record the parameters for a specific run, since if you output the parameters using this function into a log file, you can always recover the results by simply copying the output to your input file.
Besides the name and value of each entry, the output also contains the default value of entries if it is different from the actual value, as well as the documenting string given to the declare_entry() function if available.
void ParameterHandler::print_parameters_section | ( | std::ostream & | out, | |
const OutputStyle | style, | |||
const unsigned int | indent_level | |||
) |
Print out the parameters of the present subsection as given by the subsection_path
member variable. This variable is controlled by entering and leaving subsections through the enter_subsection() and leave_subsection() functions.
In most cases, you will not want to use this function directly, but have it called recursively by the previous function.
void ParameterHandler::log_parameters | ( | LogStream & | out | ) |
Print parameters to a logstream. This function allows to print all parameters into a log-file. Sections will be indented in the usual log-file style.
void ParameterHandler::log_parameters_section | ( | LogStream & | out | ) |
Log parameters in the present subsection. The subsection is determined by the subsection_path
member variable. This variable is controlled by entering and leaving subsections through the enter_subsection() and leave_subsection() functions.
In most cases, you will not want to use this function directly, but have it called recursively by the previous function.
Determine an estimate for the memory consumption (in bytes) of this object.
Reimplemented in MultipleParameterLoop.
Scan one line of input. lineno
is the number of the line presently scanned (for the logs if there are messages). Return false
if line contained stuff that could not be understood, the uppermost subsection was to be left by an END
or end
statement, a value for a non-declared entry was given or teh entry value did not match the regular expression. true
otherwise.
The function modifies its argument, but also takes it by value, so the caller's variable is not changed.
Section* ParameterHandler::get_present_defaults_subsection | ( | ) | [private] |
Get a pointer to the Section
structure in the Defaults
tree for the subsection we are presently in.
const Section* ParameterHandler::get_present_defaults_subsection | ( | ) | const [private] |
Same, const
version.
Section* ParameterHandler::get_present_changed_subsection | ( | ) | [private] |
Get a pointer to the Section structure in the changed_entries
tree for the subsection we are presently in.
const Section* ParameterHandler::get_present_changed_subsection | ( | ) | const [private] |
Same, const
version.
friend class MultipleParameterLoop [friend] |
std::vector<std::string> ParameterHandler::subsection_path [private] |
Path of presently selected subsections; empty list means top level
Section ParameterHandler::defaults [private] |
List of default values organized as a tree of subsections
Section ParameterHandler::changed_entries [private] |
Analogue list of changed entries. The tree of subsections is there even if there are no changed entry values in a subsection; therefore enter_subsection() has to create the tree in both Defaults
and changed_entries
.