--- /dev/null
+//-----------------------------------------------------------
+//
+// Copyright (C) 2014 - 2016 by the deal.II authors
+//
+// This file is part of the deal.II library.
+//
+// The deal.II library is free software; you can use it, redistribute
+// it, and/or modify it under the terms of the GNU Lesser General
+// Public License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+// The full text of the license can be found in the file LICENSE at
+// the top level of the deal.II distribution.
+//
+//-----------------------------------------------------------
+
+#ifndef dealii_base_parameter_acceptor_h
+#define dealii_base_parameter_acceptor_h
+
+#include <deal.II/base/config.h>
+#include <deal.II/base/parameter_handler.h>
+#include <deal.II/base/smartpointer.h>
+#include <deal.II/base/logstream.h>
+#include <deal.II/base/exceptions.h>
+#include <boost/signals2/signal.hpp>
+#include <typeinfo>
+
+DEAL_II_NAMESPACE_OPEN
+
+/**
+ * A parameter acceptor base class. This class is used to define a public
+ * interface for classes wich want to use a single global ParameterHandler to
+ * handle parameters. This class declares one static ParameterHandler, and two
+ * static functions (declare_all_parameters() and parse_all_parameters()) that
+ * manage all of the derived classes.
+ *
+ * The basic interface provides two subscription mechanisms: a **global
+ * subscription mechanism** and a **local subscription mechanism**.
+ *
+ * The global subscription mechanism is such that whenever a class that was
+ * derived by ParameterAcceptor is constructed, a static registry
+ * (ParameterAcceptor::class_list) in the base class is updated with a pointer
+ * to the derived class, and a path in the parameter file. Such registry is
+ * traversed upon invocation of the single function
+ * ParameterAcceptor::initialize(file.prm) which in turn calls the method
+ * ParameterAcceptor::declare_parameters() for each of the registered classes,
+ * reads the file `file.prm` and subsequently calls the method
+ * ParameterAcceptor::parse_parameters(), again for each of the registered
+ * classes. The method log_info() can be used to extract informations about the
+ * classes that have been derived from ParameterAcceptor, and that will be
+ * parsed when calling ParameterAcceptor::initialize().
+ *
+ * ParameterAcceptor can be used in two different ways: by overloading the
+ * ParameterAcceptor::declare_parameters() and
+ * ParameterAcceptor::parse_parameters() methods, or by calling its
+ * ParameterAcceptor::add_parameter() method for each parameter we want to
+ * have. This in turns makes sure that the given parameter is registered in the
+ * global parameter handler (by calling ParameterHandler::add_parameter()), at
+ * the correct path. If you define all your parameters using the
+ * ParameterAcceptor::add_parameter() method, then you don't need to overload
+ * any of the virtual methods of this class.
+ *
+ * If some post processing is required on the parsed values, the user can
+ * attach a signal to ParameterAcceptor::parse_parameters_call_back can be
+ * overridden, which is called just after the parse_parameters() function of
+ * each class.
+ *
+ * A typical usage of this class is the following:
+ *
+ * @code
+ * // This is your own class, derived from ParameterAcceptor
+ * class MyClass : public ParameterAcceptor {
+ *
+ * // The constructor of ParameterAcceptor requires a std::string,
+ * // which defines the section name where the parameters of MyClass
+ * // will be stored.
+ *
+ * MyClass(std::string name) :
+ * ParameterAcceptor(name)
+ * {
+ * add_parameter("A param", member_var);
+ * }
+ *
+ * private:
+ * std::vector<unsigned int> member_var;
+ * ...
+ * };
+ *
+ * int main() {
+ * // Make sure you build your class BEFORE calling
+ * // ParameterAcceptor::initialize()
+ * MyClass class;
+ *
+ * // With this call, all derived classes will have their
+ * // parameters initialized
+ * ParameterAcceptor::initialize("file.prm");
+ * }
+ * @endcode
+ *
+ * An implementation that uses user defined declare and parse functions is given
+ * by the following example:
+ *
+ * @code
+ * // Again your own class, derived from ParameterAcceptor
+ * class MyClass : public ParameterAcceptor {
+ *
+ * MyClass(std::string name) :
+ * ParameterAcceptor(name)
+ * {}
+ *
+ * virtual void declare_parameters(ParameterHandler &prm) {
+ * ...
+ * }
+ *
+ * virtual void parse_parameters(ParameterHandler &prm) {
+ * ...
+ * }
+ * };
+ *
+ * int main() {
+ * // Make sure you build your class BEFORE calling
+ * // ParameterAcceptor::initialize()
+ * MyClass class;
+ * ParameterAcceptor::initialize("file.prm");
+ * class.run();
+ * }
+ * @endcode
+ *
+ *
+ * Parameter files can be organised into section/subsection/subsubsection.
+ * To do so, the std::string passed to ParameterAcceptor within the
+ * constructor of the derived class needs to contain the separator "/".
+ * In fact, "first/second/third/My Class" will organize the parameters
+ * as follows
+ *
+ * @code
+ * subsection first
+ * subsection second
+ * subsection third
+ * subsection My Class
+ * ... # all the parameters
+ * end
+ * end
+ * end
+ * end
+ * @endcode
+ *
+ * In the following examles, we propose some use cases with increasing
+ * complexities.
+ *
+ * MyClass is derived from ParameterAcceptor and has a
+ * member object that is derived itself from ParameterAcceptor.
+ * @code
+ * class MyClass : public ParameterAcceptor
+ * {
+ * MyClass (std::string name);
+ * virtual void declare_parameters(ParameterHandler &prm);
+ * private:
+ * SomeParsedClass<dim> my_subclass;
+ * ...
+ * };
+ *
+ * MyClass::MyClass(std::string name)
+ * :
+ * ParameterAcceptor(name),
+ * my_subclass("Forcing term")
+ * {}
+ *
+ * void MyClass::declare_parmeters(ParameterHandler &prm)
+ * {
+ * // many add_parameter(...);
+ * }
+ *
+ * ...
+ *
+ * int main()
+ * {
+ * MyClass mc("My Class");
+ *
+ * ParameterAcceptor::initialize("file.prm");
+ * }
+ * @endcode
+ *
+ * In this case, the structure of the parameters will be
+ * @code
+ * subsection Forcing term
+ * ... #parameters of SomeParsedClass
+ * end
+ * subsection My class
+ * ... #all the parameters of MyClass defined in declare_parameters
+ * end
+ * @endcode
+ * Note that the sections are alphabetically sorted.
+ *
+ * Now suppose that in the main file we need two or more objects of MyClass
+ * @code
+ * int main()
+ * {
+ * MyClass ca("Class A");
+ * MyClass cb("Class B");
+ * ParameterAcceptor::initialize("file.prm");
+ * }
+ * @endcode
+ *
+ * What we will read in the parameter file looks like
+ * @code
+ * subsection Class A
+ * ...
+ * end
+ * subsection Class B
+ * ...
+ * end
+ * subsection Forcing term
+ * ...
+ * end
+ * @endcode
+ * Note that there is only one section "Forcing term", this is because
+ * both objects have defined the same name for the section of their
+ * SomeParsedClass. There are two strategies to manage this issue. The
+ * first one (not recommended) would be to change the name of the section
+ * of SomeParsedClass such that it contains also the string passed to
+ * the constructor of MyClass:
+ * @code
+ * MyClass::MyClass(std::string name)
+ * :
+ * ParameterAcceptor(name),
+ * my_subclass(name+" --- forcing term")
+ * {}
+ * @endcode
+ *
+ * The other way to proceed (recommended) is to use exploit the /section/subsection
+ * approach **in the main class**.
+ * @code
+ * int main()
+ * {
+ * MyClass ca("/Class A/Class");
+ * MyClass cb("/Class B/Class");
+ * ParameterAcceptor::initialize("file.prm");
+ * }
+ * @endcode
+ * Now, in the parameter file we can find
+ * @code
+ * subsection Class A
+ * subsection Class
+ * ...
+ * end
+ * subsection Forcing term
+ * ...
+ * end
+ * end
+ * subsection Class B
+ * subsection Class
+ * ...
+ * end
+ * subsection Forcing term
+ * ...
+ * end
+ * end
+ * @endcode
+ * Note the "/" at the begin of the string name. This is interpreted by
+ * ParameterAcceptor like the root folder in Unix systems. This means
+ * that the sections "Class A" and "Class B" will not be nested under any
+ * section. On the other hand, if the string does not begin with a "/"
+ * as in the previous cases (and for the ParsedFunction also in this last
+ * example) the section will be created **under the current path**, which
+ * depends on the previously defined sections/subsections/subsubsections.
+ * Indeed, the section "Forcing term" is nested under "Class A" or "Class B".
+ * To make things more clear. let's consider the following two examples
+ * @code
+ * int main()
+ * {
+ * MyClass ca("/Class A/Class");
+ * MyClass cb("Class B/Class");
+ * ParameterAcceptor::initialize("file.prm");
+ * }
+ * @endcode
+ * The parameter file will have the following structure
+ * @code
+ * subsection Class A
+ * subsection Class
+ * ...
+ * end
+ * subsection Forcing term
+ * ...
+ * end
+ * subsection Class B
+ * subsection Class
+ * ...
+ * end
+ * subsection Forcing term
+ * ...
+ * end
+ * end
+ * end
+ * @endcode
+ *
+ * If instead one of the paths ends with "/" instead of just
+ * a name of the class, subsequent classes will be declared
+ * under the full path, as if the class name should be interpreted
+ * as a directory:
+ * @code
+ * int main()
+ * {
+ * MyClass ca("/Class A/Class/");
+ * MyClass cb("Class B/Class");
+ * ParameterAcceptor::initialize("file.prm");
+ * }
+ * @endcode
+ * The parameter file will have the following structure
+ * @code
+ * subsection Class A
+ * subsection Class
+ * ...
+ * subsection Forcing term
+ * ...
+ * end
+ * subsection Class B
+ * subsection Class
+ * ...
+ * end
+ * subsection Forcing term
+ * ...
+ * end
+ * end
+ * end
+ * end
+ * @endcode
+ *
+ * As a final remark, in order to allow a proper management of all the
+ * sections/subsections, the instantiation of objects and the call to
+ * ParameterAcceptor::initialize() **cannot be done in multithread**.
+ *
+ * If you pass an empty name, the boost::core::demangle() function is used to
+ * fill the section name with a human readable version of the class name
+ * itself.
+ *
+ * @author Luca Heltai, 2017.
+ */
+class ParameterAcceptor : public Subscriptor
+{
+public:
+ /**
+ * The constructor adds derived classes to the list of acceptors. If
+ * a section name is specified, then this is used to scope the
+ * parameters in the given section, otherwise a pretty printed
+ * version of the derived class is used.
+ */
+ ParameterAcceptor(const std::string section_name="");
+
+ /**
+ * The destructor sets to zero the pointer relative to this index,
+ * so that it is safe to destroy the mother class.
+ */
+ virtual ~ParameterAcceptor();
+
+ /**
+ * Call declare_all_parameters(), read filename (if it is present as input
+ * parameter) and parse_all_parameters() on the static member prm. If
+ * outfilename is not the emtpy string, then write the content that was read
+ * in to the outfilename. The format of both input and output files are
+ * selected using the extensions of the files themselves. This can be either
+ * `prm` or `xml`. If the output format is `prm`, then the
+ * `output_style_for_prm_format` is used to decide wether we write the full
+ * documentation as well, or only the parameters.
+ *
+ * If the input file does not exist, a default one with the same name is created
+ * for you, and an exception is thrown.
+ *
+ * @param filename Input file name
+ * @param output_filename Output file name
+ * @param output_style_for_prm_format How to write the output file if format is `prm`
+ */
+ static void initialize(const std::string filename,
+ const std::string output_filename="",
+ const ParameterHandler::OutputStyle output_style_for_prm_format=ParameterHandler::ShortText);
+
+ /**
+ * Clear class list and global parameter file.
+ */
+ static void clear();
+
+ /**
+ * Declare parameter entries of the derived class.
+ */
+ virtual void declare_parameters(ParameterHandler &prm);
+
+ /**
+ * Declare parameter call back. This function is called at the end of
+ * declare_all_parameters, to allow users to process their parameters right
+ * after they have been parsed. The default implementation is empty.
+ *
+ * You can use this function, for example, to create a quadrature
+ * rule after you have read how many quadrature points you wanted
+ * to use from the parameter file.
+ */
+ boost::signals2::signal<void()> declare_parameters_call_back;
+
+ /**
+ * Parse the (derived class) parameters.
+ */
+ virtual void parse_parameters(ParameterHandler &prm);
+
+ /**
+ * Parse parameter call back. This function is called at the end of
+ * parse_all_parameters, to allow users to process their parameters right
+ * after they have been parsed. The default implementation is empty.
+ *
+ * You can use this function, for example, to create a quadrature
+ * rule after you have read how many quadrature points you wanted
+ * to use from the parameter file.
+ */
+ boost::signals2::signal<void()> parse_parameters_call_back;
+
+ /**
+ * Parse the given ParameterHandler. This function enters the
+ * subsection returned by get_section_name() for each derived class,
+ * and parses all parameters that were added using add_parameter().
+ */
+ static void parse_all_parameters(ParameterHandler &prm=ParameterAcceptor::prm);
+
+
+ /**
+ * Print information to deallog about all stored classes.
+ */
+ static void log_info();
+
+
+ /**
+ * Initialize the global ParameterHandler with all derived classes
+ * parameters.This function enters the subsection returned by
+ * get_section_name() for each derived class, and declares all parameters
+ * that were added using add_parameter().
+ */
+ static void declare_all_parameters(ParameterHandler &prm=ParameterAcceptor::prm);
+
+
+ /**
+ * Return the section name of this class. If a name was provided
+ * at construction time, then that name is returned, otherwise it
+ * returns the name of this class, pretty printed.
+ */
+ std::string get_section_name() const;
+
+ /**
+ * Travers all registered classes, and figure out what
+ * subsections we need to enter.
+ */
+ std::vector<std::string> get_section_path() const;
+
+ /**
+ * Add a parameter in the correct path. This method forwards all arguments to
+ * the ParameterHandler::add_parameter() method, after entering the correct
+ * section path.
+ *
+ * See the documentation of ParameterHandler::add_parameter() for more
+ * information.
+ */
+ template <class ParameterType>
+ void add_parameter(const std::string &entry,
+ ParameterType ¶meter,
+ const std::string &documentation = std::string(),
+ const Patterns::PatternBase &pattern =
+ *Patterns::Tools::Convert<ParameterType>::to_pattern());
+
+ /**
+ * The global parameter handler.
+ */
+ static ParameterHandler prm;
+
+private:
+ /**
+ * Make sure we enter the right subsection of the global parameter file.
+ */
+ void enter_my_subsection(ParameterHandler &prm);
+
+ /**
+ * This function undoes what the enter_my_subsection() function did. It only
+ * makes sense if enter_my_subsection() is called before this one.
+ */
+ void leave_my_subsection(ParameterHandler &prm);
+
+ /**
+ * A list containing all constructed classes of type
+ * ParameterAcceptor.
+ */
+ static std::vector<SmartPointer<ParameterAcceptor> > class_list;
+
+ /** The index of this specific class within the class list. */
+ const unsigned int acceptor_id;
+
+ /**
+ * Separator between section and subsection.
+ */
+ static const char sep = '/';
+
+protected:
+ /** The subsection name for this class. */
+ const std::string section_name;
+};
+
+
+// Inline and template functions
+template<class ParameterType>
+void ParameterAcceptor::add_parameter(const std::string& entry,
+ ParameterType& parameter,
+ const std::string& documentation,
+ const Patterns::PatternBase& pattern)
+{
+ enter_my_subsection(prm);
+ prm.add_parameter(entry, parameter, documentation, pattern);
+ leave_my_subsection(prm);
+}
+
+
+DEAL_II_NAMESPACE_CLOSE
+
+#endif
+
--- /dev/null
+//-----------------------------------------------------------
+//
+// Copyright (C) 2015 - 2016 by the deal.II authors
+//
+// This file is part of the deal.II library.
+//
+// The deal.II library is free software; you can use it, redistribute
+// it, and/or modify it under the terms of the GNU Lesser General
+// Public License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+// The full text of the license can be found in the file LICENSE at
+// the top level of the deal.II distribution.
+//
+//-----------------------------------------------------------
+
+#include <deal.II/base/parameter_acceptor.h>
+#include <deal.II/base/utilities.h>
+#include <deal.II/base/revision.h>
+#include <deal.II/base/path_search.h>
+#include <boost/core/demangle.hpp>
+#include <fstream>
+
+DEAL_II_NAMESPACE_OPEN
+
+
+// Static empty class list
+std::vector<SmartPointer<ParameterAcceptor> > ParameterAcceptor::class_list;
+// Static parameter handler
+ParameterHandler ParameterAcceptor::prm;
+
+ParameterAcceptor::ParameterAcceptor(const std::string name) :
+ acceptor_id(class_list.size()),
+ section_name(name)
+{
+ SmartPointer<ParameterAcceptor> pt(this, boost::core::demangle(typeid(*this).name()).c_str());
+ class_list.push_back(pt);
+}
+
+
+ParameterAcceptor::~ParameterAcceptor()
+{
+ class_list[acceptor_id] = 0;
+}
+
+std::string ParameterAcceptor::get_section_name() const
+{
+ return (section_name != "" ? section_name : boost::core::demangle(typeid(*this).name()));
+}
+
+
+void
+ParameterAcceptor::initialize(const std::string filename,
+ const std::string output_filename,
+ const ParameterHandler::OutputStyle output_style_for_prm_format)
+{
+ declare_all_parameters();
+ // check the extension of input file
+ if (filename.substr(filename.find_last_of(".") + 1) == "prm")
+ {
+ try
+ {
+ prm.parse_input(filename);
+ }
+ catch (dealii::PathSearch::ExcFileNotFound)
+ {
+ std::ofstream out(filename);
+ Assert(out, ExcIO());
+ prm.print_parameters(out, ParameterHandler::Text);
+ out.close();
+ AssertThrow(false, ExcMessage("You specified "+filename+" as input "+
+ "parameter file, but it does not exist. " +
+ "We created one for you."));
+ }
+ }
+ else if (filename.substr(filename.find_last_of(".") + 1) == "xml")
+ {
+ std::ifstream is(filename);
+ if (!is)
+ {
+ std::ofstream out(filename);
+ Assert(out, ExcIO());
+ prm.print_parameters(out, ParameterHandler::XML);
+ out.close();
+ is.clear();
+ AssertThrow(false, ExcMessage("You specified "+filename+" as input "+
+ "parameter file, but it does not exist. " +
+ "We created one for you."));
+ }
+ prm.parse_input_from_xml(is);
+ }
+ else
+ AssertThrow(false, ExcMessage("Invalid extension of parameter file. Please use .prm or .xml"));
+
+ parse_all_parameters();
+ if (output_filename != "")
+ {
+ std::ofstream outfile(output_filename.c_str());
+ Assert(outfile, ExcIO());
+ std::string extension = output_filename.substr(output_filename.find_last_of(".") + 1);
+
+ if ( extension == "prm")
+ {
+ outfile << "# Parameter file generated with " << std::endl
+ << "# DEAL_II_GIT_BRANCH= " << DEAL_II_GIT_BRANCH << std::endl
+ << "# DEAL_II_GIT_SHORTREV= " << DEAL_II_GIT_SHORTREV << std::endl;
+ Assert(output_style_for_prm_format == ParameterHandler::Text ||
+ output_style_for_prm_format == ParameterHandler::ShortText,
+ ExcMessage("Only Text or ShortText can be specified in output_style_for_prm_format."))
+ prm.print_parameters(outfile, output_style_for_prm_format);
+ }
+ else if (extension == "xml")
+ prm.print_parameters(outfile, ParameterHandler::XML);
+ else if (extension == "latex" || extension == "tex")
+ prm.print_parameters(outfile, ParameterHandler::LaTeX);
+ else
+ AssertThrow(false,ExcNotImplemented());
+ }
+}
+
+void
+ParameterAcceptor::clear()
+{
+ class_list.clear();
+ prm.clear();
+}
+
+
+
+void ParameterAcceptor::declare_parameters(ParameterHandler &prm)
+{}
+
+
+
+void ParameterAcceptor::parse_parameters(ParameterHandler &prm)
+{}
+
+
+
+void
+ParameterAcceptor::log_info()
+{
+ deallog.push("ParameterAcceptor");
+ for (unsigned int i=0; i<class_list.size(); ++i)
+ {
+ deallog << "Class " << i << ":";
+ if (class_list[i])
+ deallog << class_list[i]->get_section_name() << std::endl;
+ else
+ deallog << " NULL" << std::endl;
+ }
+ deallog.pop();
+}
+
+void ParameterAcceptor::parse_all_parameters(ParameterHandler &prm)
+{
+ for (unsigned int i=0; i< class_list.size(); ++i)
+ if (class_list[i] != NULL)
+ {
+ class_list[i]->enter_my_subsection(prm);
+ class_list[i]->parse_parameters(prm);
+ class_list[i]->parse_parameters_call_back();
+ class_list[i]->leave_my_subsection(prm);
+ }
+}
+
+void ParameterAcceptor::declare_all_parameters(ParameterHandler &prm)
+{
+ for (unsigned int i=0; i< class_list.size(); ++i)
+ if (class_list[i] != NULL)
+ {
+ class_list[i]->enter_my_subsection(prm);
+ class_list[i]->declare_parameters(prm);
+ class_list[i]->declare_parameters_call_back();
+ class_list[i]->leave_my_subsection(prm);
+ }
+}
+
+
+std::vector<std::string>
+ParameterAcceptor::get_section_path() const
+{
+ Assert(acceptor_id < class_list.size(), ExcInternalError());
+ std::vector<std::string> sections =
+ Utilities::split_string_list(class_list[acceptor_id]->get_section_name(), sep);
+ bool is_absolute = false;
+ if (sections.size() > 1)
+ {
+ // Handle the cases of a leading "/"
+ if (sections[0] == "")
+ {
+ is_absolute = true;
+ sections.erase(sections.begin());
+ }
+ }
+ if (is_absolute == false)
+ {
+ // In all other cases, we scan for earlier classes, and prepend the
+ // first absolute path (in reverse order) we find to ours
+ for (int i=acceptor_id-1; i>=0; --i)
+ if (class_list[i] != NULL)
+ if (class_list[i]->get_section_name().front() == sep)
+ {
+ bool has_trailing = class_list[i]->get_section_name().back() == sep;
+ // Absolute path found
+ auto secs = Utilities::split_string_list(class_list[i]->get_section_name(), sep);
+ Assert(secs[0] == "", ExcInternalError());
+ // Insert all sections except first and last
+ sections.insert(sections.begin(), secs.begin()+1, secs.end()-(has_trailing ? 0 : 1));
+ // exit from for cycle
+ break;
+ }
+ }
+ return sections;
+}
+
+void ParameterAcceptor::enter_my_subsection(ParameterHandler &prm=ParameterAcceptor::prm)
+{
+ std::vector<std::string> sections = get_section_path();
+ for (auto sec : sections)
+ {
+ prm.enter_subsection(sec);
+ }
+}
+
+void ParameterAcceptor::leave_my_subsection(ParameterHandler &prm=ParameterAcceptor::prm)
+{
+ std::vector<std::string> sections = get_section_path();
+ for (auto sec : sections)
+ {
+ prm.leave_subsection();
+ }
+}
+
+
+
+DEAL_II_NAMESPACE_CLOSE
+