]> https://gitweb.dealii.org/ - dealii.git/commitdiff
Introduce ways to define aliases for parameters.
authorWolfgang Bangerth <bangerth@math.tamu.edu>
Sun, 22 Mar 2015 22:46:32 +0000 (17:46 -0500)
committerWolfgang Bangerth <bangerth@math.tamu.edu>
Tue, 24 Mar 2015 12:33:29 +0000 (07:33 -0500)
doc/news/changes.h
include/deal.II/base/parameter_handler.h
source/base/parameter_handler.cc
tests/bits/parameter_handler_3_with_alias_01.cc [new file with mode: 0644]
tests/bits/parameter_handler_3_with_alias_01.output [new file with mode: 0644]
tests/bits/parameter_handler_3_with_alias_02.cc [new file with mode: 0644]
tests/bits/parameter_handler_3_with_alias_02.output [new file with mode: 0644]

index c544c4042874f261e67799f2467f1796f330dc08..14c559f78c2c17f143e0b2c5af829bad1672dc9d 100644 (file)
@@ -411,6 +411,14 @@ inconvenience this causes.
   (Wolfgang Bangerth, 2015/03/23)
   </li>
 
+  <li> New: ParameterHandler::declare_alias() allows to define
+  alternate names for parameters. This is primarily intended to allow
+  for backward compatible ways of changing the names of parameters
+  to applications.
+  <br>
+  (Wolfgang Bangerth, 2015/03/22)
+  </li>
+
   <li> New: GridGenerator::create_triangulation_with_removed_cells() creates
   a new mesh out of an existing one by dropping individual cells.
   <br>
index 55b9e56ca27b9caf217dcaab169731255f3906fd..5b5e1b3e33d6d3a79bc2f9faecebb242a2b4498b 100644 (file)
@@ -1,6 +1,6 @@
 // ---------------------------------------------------------------------
 //
-// Copyright (C) 1998 - 2014 by the deal.II authors
+// Copyright (C) 1998 - 2015 by the deal.II authors
 //
 // This file is part of the deal.II library.
 //
@@ -1647,6 +1647,47 @@ public:
                       const Patterns::PatternBase &pattern = Patterns::Anything(),
                       const std::string           &documentation = std::string());
 
+  /**
+   * Create an alias for an existing entry. This provides a way to refer
+   * to a parameter in the input file using an alternate name. The
+   * alias will be in the current section, and the referenced entry needs
+   * to be an existing entry in the current section.
+   *
+   * The primary purpose of this function is to allow for a backward
+   * compatible way of changing names in input files of applications
+   * for which backward compatibility is important. This can be
+   * achieved by changing the name of the parameter in the call to
+   * declare_entry(), and then creating an alias that maps the old
+   * name to the new name. This way, old input files can continue
+   * to refer to parameters under the old name, and they will
+   * automatically be mapped to the new parameter name.
+   *
+   * It is valid to reference in a parameter file to the same parameter
+   * multiple times. The value that will ultimately be chosen in such
+   * cases is simply the last value set. This rule also applies to
+   * aliases, where the final value of a parameter is the last value
+   * set either through the current name of the parameter or through
+   * any of its possible multiple aliases. For example, if you have
+   * an input file that looks like
+   * @code
+   *   set parm1       = 1
+   *   set parm1_alias = 2
+   * @endcode
+   * where <code>parm1_alias</code> is an alias declared via
+   * @code
+   *   prm.declare_alias ("parm1", "parm1_alias");
+   * @endcode
+   * then the final value for the parameter called <code>parm1</code>
+   * will be 2, not 1.
+   *
+   * @param existing_entry_name The name of an existing parameter
+   *   in the current section that the alias should refer to.
+   * @param alias_name An alternate name for the parameter referenced
+   *   by the first argument.
+   */
+  void declare_alias (const std::string &existing_entry_name,
+                      const std::string &alias_name);
+
   /**
    * Enter a subsection. If it does not yet exist, create it.
    */
@@ -1922,11 +1963,6 @@ private:
    */
   static std::string demangle (const std::string &s);
 
-  /**
-   * Return whether a given node is a parameter node or a subsection node.
-   */
-  static bool is_parameter_node (const boost::property_tree::ptree &);
-
   /**
    * Path of presently selected subsections; empty list means top level
    */
index 7144a4106594a71c8a3ea03dd4d5c84d3737e2f8..a246f59ff6cfdd3b6bb00279b942094dd2aa4b4f 100644 (file)
@@ -1,6 +1,6 @@
 // ---------------------------------------------------------------------
 //
-// Copyright (C) 1998 - 2014 by the deal.II authors
+// Copyright (C) 1998 - 2015 by the deal.II authors
 //
 // This file is part of the deal.II library.
 //
@@ -1271,13 +1271,30 @@ ParameterHandler::demangle (const std::string &s)
 
 
 
-bool
-ParameterHandler::is_parameter_node (const boost::property_tree::ptree &p)
+namespace
 {
-  return static_cast<bool>(p.get_optional<std::string>("value"));
-}
+  /**
+   * Return whether a given node is a parameter node (as opposed
+   * to being a subsection or alias node)
+   */
+  bool
+  is_parameter_node (const boost::property_tree::ptree &p)
+  {
+    return static_cast<bool>(p.get_optional<std::string>("value"));
+  }
 
 
+  /**
+   * Return whether a given node is a alias node (as opposed
+   * to being a subsection or parameter node)
+   */
+  bool
+  is_alias_node (const boost::property_tree::ptree &p)
+  {
+    return static_cast<bool>(p.get_optional<std::string>("alias"));
+  }
+}
+
 
 std::string
 ParameterHandler::get_current_path () const
@@ -1486,6 +1503,13 @@ namespace
                 return false;
               }
           }
+        else if (p->second.get_optional<std::string>("alias"))
+          {
+            // it is an alias node. alias nodes are static and
+            // there is nothing to do here (but the same applies as
+            // mentioned in the comment above about the static
+            // nodes inside parameter nodes
+          }
         else
           {
             // it must be a subsection
@@ -1610,6 +1634,52 @@ ParameterHandler::declare_entry (const std::string           &entry,
 
 
 
+void
+ParameterHandler::declare_alias(const std::string &existing_entry_name,
+                                const std::string &alias_name)
+{
+  // see if there is anything to refer to already
+  Assert (entries->get_optional<std::string>(get_current_full_path(existing_entry_name)),
+          ExcMessage ("You are trying to declare an alias entry <"
+                      + alias_name +
+                      "> that references an entry <"
+                      + existing_entry_name +
+                      ">, but the latter does not exist."));
+
+  // now also make sure that if the alias has already been
+  // declared, that it is also an alias and refers to the same
+  // entry
+  if (entries->get_optional<std::string>(get_current_full_path(alias_name)))
+    {
+      Assert (entries->get_optional<std::string> (get_current_full_path(alias_name) + path_separator + "alias"),
+              ExcMessage ("You are trying to declare an alias entry <"
+                          + alias_name +
+                          "> but a non-alias entry already exists in this "
+                          "subsection (i.e., there is either a preexisting "
+                          "further subsection, or a parameter entry, with "
+                          "the same name as the alias)."));
+      Assert (entries->get<std::string> (get_current_full_path(alias_name) + path_separator + "alias")
+              ==
+              existing_entry_name,
+              ExcMessage ("You are trying to declare an alias entry <"
+                          + alias_name +
+                          "> but an alias entry already exists in this "
+                          "subsection and this existing alias references a "
+                          "different parameter entry. Specifically, "
+                          "you are trying to reference the entry <"
+                          + existing_entry_name +
+                          "> whereas the existing alias references "
+                          "the entry <"
+                          + entries->get<std::string> (get_current_full_path(alias_name) + path_separator + "alias") +
+                          ">."));
+    }
+
+  entries->put (get_current_full_path(alias_name) + path_separator + "alias",
+                existing_entry_name);
+}
+
+
+
 void ParameterHandler::enter_subsection (const std::string &subsection)
 {
   const std::string current_path = get_current_path ();
@@ -1717,21 +1787,24 @@ void
 ParameterHandler::set (const std::string &entry_string,
                        const std::string &new_value)
 {
-  // assert that the entry is indeed
-  // declared
-  if (entries->get_optional<std::string>
-      (get_current_full_path(entry_string) + path_separator + "value"))
+  // resolve aliases before looking up the correct entry
+  std::string path = get_current_full_path(entry_string);
+  if (entries->get_optional<std::string>(path + path_separator + "alias"))
+    path = get_current_full_path(entries->get<std::string>(path + path_separator + "alias"));
+
+  // assert that the entry is indeed declared
+  if (entries->get_optional<std::string>(path + path_separator + "value"))
     {
       const unsigned int pattern_index
-        = entries->get<unsigned int> (get_current_full_path(entry_string) + path_separator + "pattern");
+        = entries->get<unsigned int> (path + path_separator + "pattern");
       AssertThrow (patterns[pattern_index]->match(new_value),
                    ExcValueDoesNotMatchPattern (new_value,
                                                 entries->get<std::string>
-                                                (get_current_full_path(entry_string) +
+                                                (path +
                                                  path_separator +
                                                  "pattern_description")));
 
-      entries->put (get_current_full_path(entry_string) + path_separator + "value",
+      entries->put (path + path_separator + "value",
                     new_value);
     }
   else
@@ -2219,7 +2292,7 @@ ParameterHandler::print_parameters_section (std::ostream      &out,
            p != current_section.end(); ++p)
         if (is_parameter_node (p->second) == true)
           ++n_parameters;
-        else
+        else if (is_alias_node (p->second) == false)
           ++n_sections;
 
       if ((style != Description)
@@ -2236,7 +2309,9 @@ ParameterHandler::print_parameters_section (std::ostream      &out,
       for (boost::property_tree::ptree::const_assoc_iterator
            p = current_section.ordered_begin();
            p != current_section.not_found(); ++p)
-        if (is_parameter_node (p->second) == false)
+        if ((is_parameter_node (p->second) == false)
+            &&
+            (is_alias_node (p->second) == false))
           {
             // first print the subsection header
             switch (style)
@@ -2482,11 +2557,14 @@ ParameterHandler::scan_line (std::string         line,
       std::string entry_name  (line, 0, line.find('='));
       std::string entry_value (line, line.find('=')+1, std::string::npos);
 
-      const std::string current_path = get_current_path ();
+      // resolve aliases before we look up the entry
+      std::string path = get_current_full_path(entry_name);
+      if (entries->get_optional<std::string>(path + path_separator + "alias"))
+        path = get_current_full_path(entries->get<std::string>(path + path_separator + "alias"));
 
       // assert that the entry is indeed
       // declared
-      if (entries->get_optional<std::string> (get_current_full_path(entry_name) + path_separator + "value"))
+      if (entries->get_optional<std::string> (path + path_separator + "value"))
         {
           // if entry was declared:
           // does it match the regex? if not,
@@ -2497,7 +2575,7 @@ ParameterHandler::scan_line (std::string         line,
           if (entry_value.find ('{') == std::string::npos)
             {
               const unsigned int pattern_index
-                = entries->get<unsigned int> (get_current_full_path(entry_name) + path_separator + "pattern");
+                = entries->get<unsigned int> (path + path_separator + "pattern");
               if (!patterns[pattern_index]->match(entry_value))
                 {
                   std::cerr << "Line <" << lineno
@@ -2514,7 +2592,7 @@ ParameterHandler::scan_line (std::string         line,
                 }
             }
 
-          entries->put (get_current_full_path(entry_name) + path_separator + "value",
+          entries->put (path + path_separator + "value",
                         entry_value);
           return true;
         }
diff --git a/tests/bits/parameter_handler_3_with_alias_01.cc b/tests/bits/parameter_handler_3_with_alias_01.cc
new file mode 100644 (file)
index 0000000..7083f24
--- /dev/null
@@ -0,0 +1,86 @@
+// ---------------------------------------------------------------------
+//
+// Copyright (C) 2003 - 2015 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.
+//
+// ---------------------------------------------------------------------
+
+
+
+// like _03, but use an alias to reference two parameters using the same name.
+// in the _with_alias_01 testcase, the alias doesn't do anything (we just
+// declare it, but the input file still references the original name) whereas
+// the _with_alias_02 does it the other way around
+
+#include "../tests.h"
+#include <deal.II/base/logstream.h>
+#include <deal.II/base/parameter_handler.h>
+#include <fstream>
+#include <iomanip>
+
+
+int main ()
+{
+  try
+    {
+      std::ofstream logfile("output");
+      deallog.attach(logfile);
+      deallog.depth_console(0);
+      deallog.threshold_double(1.e-10);
+
+      ParameterHandler prm;
+      prm.enter_subsection ("Testing");
+      prm.declare_entry ("string list",
+                         "a",
+                         Patterns::List(Patterns::Selection("a|b|c|d|e|f|g|h")),
+                         "docs 1");
+      prm.declare_entry ("int",
+                         "1",
+                         Patterns::Integer());
+      prm.declare_entry ("double",
+                         "3.1415926",
+                         Patterns::Double(),
+                         "docs 3");
+      prm.declare_alias ("int",
+                        "int_alias");
+      prm.leave_subsection ();
+
+      // read and then write parameters
+      prm.read_input(SOURCE_DIR "/prm/parameter_handler_3.prm");
+      prm.print_parameters (logfile, ParameterHandler::Text);
+    }
+  catch (std::exception &exc)
+    {
+      deallog << std::endl << std::endl
+              << "----------------------------------------------------"
+              << std::endl;
+      deallog << "Exception on processing: " << std::endl
+              << exc.what() << std::endl
+              << "Aborting!" << std::endl
+              << "----------------------------------------------------"
+              << std::endl;
+
+      return 1;
+    }
+  catch (...)
+    {
+      deallog << std::endl << std::endl
+              << "----------------------------------------------------"
+              << std::endl;
+      deallog << "Unknown exception!" << std::endl
+              << "Aborting!" << std::endl
+              << "----------------------------------------------------"
+              << std::endl;
+      return 1;
+    };
+
+  return 0;
+}
diff --git a/tests/bits/parameter_handler_3_with_alias_01.output b/tests/bits/parameter_handler_3_with_alias_01.output
new file mode 100644 (file)
index 0000000..cf97eae
--- /dev/null
@@ -0,0 +1,13 @@
+
+# Listing of Parameters
+# ---------------------
+subsection Testing
+  # docs 3
+  set double      = 3.1415926
+  set int         = 3         # default: 1
+
+  # docs 1
+  set string list = a, b, c   # default: a
+end
+
+
diff --git a/tests/bits/parameter_handler_3_with_alias_02.cc b/tests/bits/parameter_handler_3_with_alias_02.cc
new file mode 100644 (file)
index 0000000..08d7b5e
--- /dev/null
@@ -0,0 +1,86 @@
+// ---------------------------------------------------------------------
+//
+// Copyright (C) 2003 - 2015 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.
+//
+// ---------------------------------------------------------------------
+
+
+
+// like _03, but use an alias to reference two parameters using the same name
+// in the _with_alias_01 testcase, the alias doesn't do anything (we just
+// declare it, but the input file still references the original name) whereas
+// the _with_alias_02 does it the other way around
+
+#include "../tests.h"
+#include <deal.II/base/logstream.h>
+#include <deal.II/base/parameter_handler.h>
+#include <fstream>
+#include <iomanip>
+
+
+int main ()
+{
+  try
+    {
+      std::ofstream logfile("output");
+      deallog.attach(logfile);
+      deallog.depth_console(0);
+      deallog.threshold_double(1.e-10);
+
+      ParameterHandler prm;
+      prm.enter_subsection ("Testing");
+      prm.declare_entry ("string list",
+                         "a",
+                         Patterns::List(Patterns::Selection("a|b|c|d|e|f|g|h")),
+                         "docs 1");
+      prm.declare_entry ("int_alias",
+                         "1",
+                         Patterns::Integer());
+      prm.declare_entry ("double",
+                         "3.1415926",
+                         Patterns::Double(),
+                         "docs 3");
+      prm.declare_alias ("int_alias",
+                        "int");
+      prm.leave_subsection ();
+
+      // read and then write parameters
+      prm.read_input(SOURCE_DIR "/prm/parameter_handler_3.prm");
+      prm.print_parameters (logfile, ParameterHandler::Text);
+    }
+  catch (std::exception &exc)
+    {
+      deallog << std::endl << std::endl
+              << "----------------------------------------------------"
+              << std::endl;
+      deallog << "Exception on processing: " << std::endl
+              << exc.what() << std::endl
+              << "Aborting!" << std::endl
+              << "----------------------------------------------------"
+              << std::endl;
+
+      return 1;
+    }
+  catch (...)
+    {
+      deallog << std::endl << std::endl
+              << "----------------------------------------------------"
+              << std::endl;
+      deallog << "Unknown exception!" << std::endl
+              << "Aborting!" << std::endl
+              << "----------------------------------------------------"
+              << std::endl;
+      return 1;
+    };
+
+  return 0;
+}
diff --git a/tests/bits/parameter_handler_3_with_alias_02.output b/tests/bits/parameter_handler_3_with_alias_02.output
new file mode 100644 (file)
index 0000000..4d123c1
--- /dev/null
@@ -0,0 +1,13 @@
+
+# Listing of Parameters
+# ---------------------
+subsection Testing
+  # docs 3
+  set double      = 3.1415926
+  set int_alias   = 3         # default: 1
+
+  # docs 1
+  set string list = a, b, c   # default: a
+end
+
+

In the beginning the Universe was created. This has made a lot of people very angry and has been widely regarded as a bad move.

Douglas Adams


Typeset in Trocchi and Trocchi Bold Sans Serif.