Reference documentation for deal.II version 9.2.0
\(\newcommand{\dealvcentcolon}{\mathrel{\mathop{:}}}\) \(\newcommand{\dealcoloneq}{\dealvcentcolon\mathrel{\mkern-1.2mu}=}\) \(\newcommand{\jump}[1]{\left[\!\left[ #1 \right]\!\right]}\) \(\newcommand{\average}[1]{\left\{\!\left\{ #1 \right\}\!\right\}}\)
parameter_handler.cc
Go to the documentation of this file.
1 // ---------------------------------------------------------------------
2 //
3 // Copyright (C) 1998 - 2020 by the deal.II authors
4 //
5 // This file is part of the deal.II library.
6 //
7 // The deal.II library is free software; you can use it, redistribute
8 // it, and/or modify it under the terms of the GNU Lesser General
9 // Public License as published by the Free Software Foundation; either
10 // version 2.1 of the License, or (at your option) any later version.
11 // The full text of the license can be found in the file LICENSE.md at
12 // the top level directory of deal.II.
13 //
14 // ---------------------------------------------------------------------
15 
16 
17 #include <deal.II/base/logstream.h>
21 #include <deal.II/base/utilities.h>
22 
24 #define BOOST_BIND_GLOBAL_PLACEHOLDERS
25 #include <boost/algorithm/string.hpp>
26 #include <boost/io/ios_state.hpp>
27 #include <boost/property_tree/json_parser.hpp>
28 #include <boost/property_tree/ptree.hpp>
29 #include <boost/property_tree/xml_parser.hpp>
30 #undef BOOST_BIND_GLOBAL_PLACEHOLDERS
32 
33 #include <algorithm>
34 #include <cctype>
35 #include <cstdlib>
36 #include <cstring>
37 #include <fstream>
38 #include <iomanip>
39 #include <iostream>
40 #include <limits>
41 #include <sstream>
42 
43 
45 
46 
48  : entries(new boost::property_tree::ptree())
49 {}
50 
51 
52 namespace
53 {
54  std::string
55  mangle(const std::string &s)
56  {
57  std::string u;
58 
59  // reserve the minimum number of characters we will need. it may
60  // be more but this is the least we can do
61  u.reserve(s.size());
62 
63  // see if the name is special and if so mangle the whole thing
64  const bool mangle_whole_string = (s == "value");
65 
66  // for all parts of the string, see if it is an allowed character or not
67  for (const char c : s)
68  {
69  static const std::string allowed_characters(
70  "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789");
71 
72  if ((!mangle_whole_string) &&
73  (allowed_characters.find(c) != std::string::npos))
74  u.push_back(c);
75  else
76  {
77  u.push_back('_');
78  static const char hex[16] = {'0',
79  '1',
80  '2',
81  '3',
82  '4',
83  '5',
84  '6',
85  '7',
86  '8',
87  '9',
88  'a',
89  'b',
90  'c',
91  'd',
92  'e',
93  'f'};
94  u.push_back(hex[static_cast<unsigned char>(c) / 16]);
95  u.push_back(hex[static_cast<unsigned char>(c) % 16]);
96  }
97  }
98 
99  return u;
100  }
101 
102 
103 
104  std::string
105  demangle(const std::string &s)
106  {
107  std::string u;
108  u.reserve(s.size());
109 
110  for (unsigned int i = 0; i < s.size(); ++i)
111  if (s[i] != '_')
112  u.push_back(s[i]);
113  else
114  {
115  Assert(i + 2 < s.size(),
116  ExcMessage("Trying to demangle an invalid string."));
117 
118  unsigned char c = 0;
119  switch (s[i + 1])
120  {
121  case '0':
122  c = 0 * 16;
123  break;
124  case '1':
125  c = 1 * 16;
126  break;
127  case '2':
128  c = 2 * 16;
129  break;
130  case '3':
131  c = 3 * 16;
132  break;
133  case '4':
134  c = 4 * 16;
135  break;
136  case '5':
137  c = 5 * 16;
138  break;
139  case '6':
140  c = 6 * 16;
141  break;
142  case '7':
143  c = 7 * 16;
144  break;
145  case '8':
146  c = 8 * 16;
147  break;
148  case '9':
149  c = 9 * 16;
150  break;
151  case 'a':
152  c = 10 * 16;
153  break;
154  case 'b':
155  c = 11 * 16;
156  break;
157  case 'c':
158  c = 12 * 16;
159  break;
160  case 'd':
161  c = 13 * 16;
162  break;
163  case 'e':
164  c = 14 * 16;
165  break;
166  case 'f':
167  c = 15 * 16;
168  break;
169  default:
170  Assert(false, ExcInternalError());
171  }
172  switch (s[i + 2])
173  {
174  case '0':
175  c += 0;
176  break;
177  case '1':
178  c += 1;
179  break;
180  case '2':
181  c += 2;
182  break;
183  case '3':
184  c += 3;
185  break;
186  case '4':
187  c += 4;
188  break;
189  case '5':
190  c += 5;
191  break;
192  case '6':
193  c += 6;
194  break;
195  case '7':
196  c += 7;
197  break;
198  case '8':
199  c += 8;
200  break;
201  case '9':
202  c += 9;
203  break;
204  case 'a':
205  c += 10;
206  break;
207  case 'b':
208  c += 11;
209  break;
210  case 'c':
211  c += 12;
212  break;
213  case 'd':
214  c += 13;
215  break;
216  case 'e':
217  c += 14;
218  break;
219  case 'f':
220  c += 15;
221  break;
222  default:
223  Assert(false, ExcInternalError());
224  }
225 
226  u.push_back(static_cast<char>(c));
227 
228  // skip the two characters
229  i += 2;
230  }
231 
232  return u;
233  }
234 
239  bool
240  is_parameter_node(const boost::property_tree::ptree &p)
241  {
242  return static_cast<bool>(p.get_optional<std::string>("value"));
243  }
244 
245 
250  bool
251  is_alias_node(const boost::property_tree::ptree &p)
252  {
253  return static_cast<bool>(p.get_optional<std::string>("alias"));
254  }
255 
262  std::string
263  collate_path_string(const char separator,
264  const std::vector<std::string> &subsection_path)
265  {
266  if (subsection_path.size() > 0)
267  {
268  std::string p = mangle(subsection_path[0]);
269  for (unsigned int i = 1; i < subsection_path.size(); ++i)
270  {
271  p += separator;
272  p += mangle(subsection_path[i]);
273  }
274  return p;
275  }
276  else
277  return "";
278  }
279 
285  void
286  recursively_sort_parameters(
287  const char separator,
288  const std::vector<std::string> &target_subsection_path,
289  boost::property_tree::ptree & tree)
290  {
291  boost::property_tree::ptree &current_section =
292  tree.get_child(collate_path_string(separator, target_subsection_path));
293 
294  // Custom comparator to ensure that the order of sorting is:
295  // - sorted parameters and aliases;
296  // - sorted subsections.
297  static auto compare =
298  [](const std::pair<std::string, boost::property_tree::ptree> &a,
299  const std::pair<std::string, boost::property_tree::ptree> &b) {
300  bool a_is_param =
301  (is_parameter_node(a.second) || is_alias_node(a.second));
302 
303  bool b_is_param =
304  (is_parameter_node(b.second) || is_alias_node(b.second));
305 
306  // If a is a parameter/alias and b is a subsection,
307  // a should go first, and viceversa.
308  if (a_is_param && !b_is_param)
309  return true;
310 
311  if (!a_is_param && b_is_param)
312  return false;
313 
314  // Otherwise, compare a and b.
315  return a.first < b.first;
316  };
317 
318  current_section.sort(compare);
319 
320  // Now transverse subsections tree recursively.
321  for (auto &p : current_section)
322  {
323  if ((is_parameter_node(p.second) == false) &&
324  (is_alias_node(p.second) == false))
325  {
326  const std::string subsection = demangle(p.first);
327 
328  std::vector<std::string> subsection_path = target_subsection_path;
329  subsection_path.emplace_back(subsection);
330 
331  recursively_sort_parameters(separator, subsection_path, tree);
332  }
333  }
334  }
335 
339  void
340  assert_validity_of_output_style(const ParameterHandler::OutputStyle style)
341  {
342  AssertThrow(
343  (((style & ParameterHandler::XML) != 0) +
344  ((style & ParameterHandler::JSON) != 0) +
345  ((style & ParameterHandler::PRM) != 0) +
346  ((style & ParameterHandler::Description) != 0) +
347  ((style & ParameterHandler::LaTeX) != 0)) == 1,
348  ExcMessage(
349  "You have chosen either no or multiple style formats. You can choose "
350  "between: PRM, Description, LaTeX, XML, JSON."));
351  }
352 
353 } // namespace
354 
355 
356 
357 std::string
359 {
360  return collate_path_string(path_separator, subsection_path);
361 }
362 
363 
364 
365 std::string
366 ParameterHandler::get_current_full_path(const std::string &name) const
367 {
368  std::string path = get_current_path();
369  if (path.empty() == false)
370  path += path_separator;
371 
372  path += mangle(name);
373 
374  return path;
375 }
376 
377 
378 
379 std::string
381  const std::vector<std::string> &sub_path,
382  const std::string & name) const
383 {
384  std::string path = get_current_path();
385  if (path.empty() == false)
386  path += path_separator;
387 
388  if (sub_path.empty() == false)
389  path += collate_path_string(path_separator, sub_path) + path_separator;
390 
391  path += mangle(name);
392 
393  return path;
394 }
395 
396 
397 
398 void
399 ParameterHandler::parse_input(std::istream & input,
400  const std::string &filename,
401  const std::string &last_line,
402  const bool skip_undefined)
403 {
404  AssertThrow(input, ExcIO());
405 
406  // store subsections we are currently in
407  const std::vector<std::string> saved_path = subsection_path;
408 
409  std::string input_line;
410  std::string fully_concatenated_line;
411  bool is_concatenated = false;
412  // Maintain both the current line number and the current logical line
413  // number, where the latter refers to the line number where (possibly) the
414  // current line continuation started.
415  unsigned int current_line_n = 0;
416  unsigned int current_logical_line_n = 0;
417 
418  // define an action that tries to scan a line.
419  //
420  // if that fails, i.e., if scan_line throws
421  // an exception either because a parameter doesn't match its
422  // pattern or because an associated action throws an exception,
423  // then try to rewind the set of subsections to the same
424  // point where we were when the current function was called.
425  // this at least allows to read parameters from a predictable
426  // state, rather than leave the subsection stack in some
427  // unknown state.
428  //
429  // after unwinding the subsection stack, just re-throw the exception
430  auto scan_line_or_cleanup = [this,
431  &skip_undefined,
432  &saved_path](const std::string &line,
433  const std::string &filename,
434  const unsigned int line_number) {
435  try
436  {
437  scan_line(line, filename, line_number, skip_undefined);
438  }
439  catch (...)
440  {
441  while ((saved_path != subsection_path) && (subsection_path.size() > 0))
443 
444  throw;
445  }
446  };
447 
448 
449  while (std::getline(input, input_line))
450  {
451  ++current_line_n;
452  if (!is_concatenated)
453  current_logical_line_n = current_line_n;
454  // Trim the whitespace at the ends of the line here instead of in
455  // scan_line. This makes the continuation line logic a lot simpler.
456  input_line = Utilities::trim(input_line);
457 
458  // If we see the line which is the same as @p last_line ,
459  // terminate the parsing.
460  if (last_line.length() != 0 && input_line == last_line)
461  break;
462 
463  // Check whether or not the current line should be joined with the next
464  // line before calling scan_line.
465  if (input_line.length() != 0 &&
466  input_line.find_last_of('\\') == input_line.length() - 1)
467  {
468  input_line.erase(input_line.length() - 1); // remove the last '\'
469  is_concatenated = true;
470 
471  fully_concatenated_line += input_line;
472  }
473  // If the previous line ended in a '\' but the current did not, then we
474  // should proceed to scan_line.
475  else if (is_concatenated)
476  {
477  fully_concatenated_line += input_line;
478  is_concatenated = false;
479  }
480  // Finally, if neither the previous nor current lines are continuations,
481  // then the current input line is entirely concatenated.
482  else
483  {
484  fully_concatenated_line = input_line;
485  }
486 
487  if (!is_concatenated)
488  {
489  scan_line_or_cleanup(fully_concatenated_line,
490  filename,
491  current_logical_line_n);
492 
493  fully_concatenated_line.clear();
494  }
495  }
496 
497  // While it does not make much sense for anyone to actually do this, allow
498  // the last line to end in a backslash. To do so, we need to parse
499  // whatever was left in the stash of concatenated lines
500  if (is_concatenated)
501  scan_line_or_cleanup(fully_concatenated_line, filename, current_line_n);
502 
503  if (saved_path != subsection_path)
504  {
505  std::stringstream paths_message;
506  if (saved_path.size() > 0)
507  {
508  paths_message << "Path before loading input:\n";
509  for (unsigned int i = 0; i < subsection_path.size(); ++i)
510  {
511  paths_message << std::setw(i * 2 + 4) << " "
512  << "subsection " << saved_path[i] << '\n';
513  }
514  paths_message << "Current path:\n";
515  for (unsigned int i = 0; i < subsection_path.size(); ++i)
516  {
517  paths_message << std::setw(i * 2 + 4) << " "
518  << "subsection " << subsection_path[i]
519  << (i == subsection_path.size() - 1 ? "" : "\n");
520  }
521  }
522  // restore subsection we started with before throwing the exception:
523  subsection_path = saved_path;
524  AssertThrow(false,
525  ExcUnbalancedSubsections(filename, paths_message.str()));
526  }
527 }
528 
529 
530 
531 void
532 ParameterHandler::parse_input(const std::string &filename,
533  const std::string &last_line,
534  const bool skip_undefined,
535  const bool assert_mandatory_entries_are_found)
536 {
537  std::ifstream is(filename);
538  AssertThrow(is, PathSearch::ExcFileNotFound(filename, "ParameterHandler"));
539 
540  std::string file_ending = filename.substr(filename.find_last_of('.') + 1);
541  boost::algorithm::to_lower(file_ending);
542  if (file_ending == "prm")
543  parse_input(is, filename, last_line, skip_undefined);
544  else if (file_ending == "xml")
545  parse_input_from_xml(is, skip_undefined);
546  else if (file_ending == "json")
547  parse_input_from_json(is, skip_undefined);
548  else
549  AssertThrow(false,
550  ExcMessage("Unknown input file name extension. Supported types "
551  "are .prm, .xml, and .json."));
552 
553  if (assert_mandatory_entries_are_found)
555 }
556 
557 
558 
559 void
561  const std::string &last_line,
562  const bool skip_undefined)
563 {
564  std::istringstream input_stream(s);
565  parse_input(input_stream, "input string", last_line, skip_undefined);
566 }
567 
568 
569 
570 namespace
571 {
572  // Recursively go through the 'source' tree and see if we can find
573  // corresponding entries in the 'destination' tree. If not, error out
574  // (i.e. we have just read an XML file that has entries that weren't
575  // declared in the ParameterHandler object); if so, copy the value of these
576  // nodes into the destination object
577  void
578  read_xml_recursively(
579  const boost::property_tree::ptree &source,
580  const std::string & current_path,
581  const char path_separator,
582  const std::vector<std::unique_ptr<const Patterns::PatternBase>> &patterns,
583  const bool skip_undefined,
584  ParameterHandler &prm)
585  {
586  for (const auto &p : source)
587  {
588  // a sub-tree must either be a parameter node or a subsection
589  if (p.second.empty())
590  {
591  // set the found parameter in the destination argument
592  if (skip_undefined)
593  {
594  try
595  {
596  prm.set(demangle(p.first), p.second.data());
597  }
599  {
600  // ignore undeclared entry assert
601  }
602  }
603  else
604  prm.set(demangle(p.first), p.second.data());
605  }
606  else if (p.second.get_optional<std::string>("value"))
607  {
608  // set the found parameter in the destination argument
609  if (skip_undefined)
610  {
611  try
612  {
613  prm.set(demangle(p.first),
614  p.second.get<std::string>("value"));
615  }
617  {
618  // ignore undeclared entry assert
619  }
620  }
621  else
622  prm.set(demangle(p.first), p.second.get<std::string>("value"));
623 
624  // this node might have sub-nodes in addition to "value", such as
625  // "default_value", "documentation", etc. we might at some point
626  // in the future want to make sure that if they exist that they
627  // match the ones in the 'destination' tree
628  }
629  else if (p.second.get_optional<std::string>("alias"))
630  {
631  // it is an alias node. alias nodes are static and there is
632  // nothing to do here (but the same applies as mentioned in the
633  // comment above about the static nodes inside parameter nodes
634  }
635  else
636  {
637  // it must be a subsection
638  prm.enter_subsection(demangle(p.first));
639  read_xml_recursively(p.second,
640  (current_path.empty() ?
641  p.first :
642  current_path + path_separator + p.first),
643  path_separator,
644  patterns,
645  skip_undefined,
646  prm);
647  prm.leave_subsection();
648  }
649  }
650  }
651 
652  // Recursively go through the @p source tree and collapse the nodes of the
653  // format:
654  //
655  //"key":
656  // {
657  // "value" : "val",
658  // "default_value" : "...",
659  // "documentation" : "...",
660  // "pattern" : "...",
661  // "pattern_description": "..."
662  // },
663  //
664  // to
665  //
666  // "key" : "val";
667  //
668  // As an example a JSON file is shown. However, this function also works for
669  // XML since both formats are build around the same BOOST data structures.
670  //
671  // This function is strongly based on read_xml_recursively().
672  void
673  recursively_compress_tree(boost::property_tree::ptree &source)
674  {
675  for (auto &p : source)
676  {
677  if (p.second.get_optional<std::string>("value"))
678  {
679  // save the value in a temporal variable
680  const auto temp = p.second.get<std::string>("value");
681  // clear node (children and value)
682  p.second.clear();
683  // set the correct value
684  p.second.put_value<std::string>(temp);
685  }
686  else if (p.second.get_optional<std::string>("alias"))
687  {}
688  else
689  {
690  // it must be a subsection
691  recursively_compress_tree(p.second);
692  }
693  }
694  }
695 } // namespace
696 
697 
698 
699 void
701  const bool skip_undefined)
702 {
703  AssertThrow(in, ExcIO());
704  // read the XML tree assuming that (as we
705  // do in print_parameters(XML) it has only
706  // a single top-level node called
707  // "ParameterHandler"
708  boost::property_tree::ptree single_node_tree;
709  // This boost function will raise an exception if this is not a valid XML
710  // file.
711  read_xml(in, single_node_tree);
712 
713  // make sure there is a single top-level element
714  // called "ParameterHandler"
715  AssertThrow(single_node_tree.get_optional<std::string>("ParameterHandler"),
716  ExcInvalidXMLParameterFile("There is no top-level XML element "
717  "called \"ParameterHandler\"."));
718 
719  const std::size_t n_top_level_elements =
720  std::distance(single_node_tree.begin(), single_node_tree.end());
721  if (n_top_level_elements != 1)
722  {
723  std::ostringstream top_level_message;
724  top_level_message << "The ParameterHandler input parser found "
725  << n_top_level_elements
726  << " top level elements while reading\n "
727  << " an XML format input file, but there should be"
728  << " exactly one top level element.\n"
729  << " The top level elements are:\n";
730 
731  unsigned int entry_n = 0;
732  for (boost::property_tree::ptree::iterator it = single_node_tree.begin();
733  it != single_node_tree.end();
734  ++it, ++entry_n)
735  {
736  top_level_message
737  << " " << it->first
738  << (entry_n != n_top_level_elements - 1 ? "\n" : "");
739  }
740 
741  // repeat assertion condition to make the printed version easier to read
742  AssertThrow(n_top_level_elements == 1,
743  ExcInvalidXMLParameterFile(top_level_message.str()));
744  }
745 
746  // read the child elements recursively
747  const boost::property_tree::ptree &my_entries =
748  single_node_tree.get_child("ParameterHandler");
749 
750  read_xml_recursively(
751  my_entries, "", path_separator, patterns, skip_undefined, *this);
752 }
753 
754 
755 void
757  const bool skip_undefined)
758 {
759  AssertThrow(in, ExcIO());
760 
761  boost::property_tree::ptree node_tree;
762  // This boost function will raise an exception if this is not a valid JSON
763  // file.
764  read_json(in, node_tree);
765 
766  // The xml function is reused to read in the xml into the parameter file.
767  // This means that only mangled files can be read.
768  read_xml_recursively(
769  node_tree, "", path_separator, patterns, skip_undefined, *this);
770 }
771 
772 
773 
774 void
776 {
777  entries = std_cxx14::make_unique<boost::property_tree::ptree>();
778  entries_set_status.clear();
779 }
780 
781 
782 
783 void
784 ParameterHandler::declare_entry(const std::string & entry,
785  const std::string & default_value,
786  const Patterns::PatternBase &pattern,
787  const std::string & documentation,
788  const bool has_to_be_set)
789 {
790  entries->put(get_current_full_path(entry) + path_separator + "value",
791  default_value);
792  entries->put(get_current_full_path(entry) + path_separator + "default_value",
793  default_value);
794  entries->put(get_current_full_path(entry) + path_separator + "documentation",
795  documentation);
796 
797  // initialize with false
798  const std::pair<bool, bool> set_status =
799  std::pair<bool, bool>(has_to_be_set, false);
800  entries_set_status.insert(
801  std::pair<std::string, std::pair<bool, bool>>(get_current_full_path(entry),
802  set_status));
803 
804  patterns.reserve(patterns.size() + 1);
805  patterns.emplace_back(pattern.clone());
806  entries->put(get_current_full_path(entry) + path_separator + "pattern",
807  static_cast<unsigned int>(patterns.size() - 1));
808  // also store the description of
809  // the pattern. we do so because we
810  // may wish to export the whole
811  // thing as XML or any other format
812  // so that external tools can work
813  // on the parameter file; in that
814  // case, they will have to be able
815  // to re-create the patterns as far
816  // as possible
818  "pattern_description",
819  patterns.back()->description());
820 
821  // as documented, do the default value checking at the very end
822  AssertThrow(pattern.match(default_value),
823  ExcValueDoesNotMatchPattern(default_value,
824  pattern.description()));
825 }
826 
827 
828 
829 void
831  const std::string & entry,
832  const std::function<void(const std::string &)> &action)
833 {
834  actions.push_back(action);
835 
836  // get the current list of actions, if any
837  boost::optional<std::string> current_actions =
838  entries->get_optional<std::string>(get_current_full_path(entry) +
839  path_separator + "actions");
840 
841  // if there were actions already associated with this parameter, add
842  // the current one to it; otherwise, create a one-item list and use
843  // that
844  if (current_actions)
845  {
846  const std::string all_actions =
847  current_actions.get() + "," +
848  Utilities::int_to_string(actions.size() - 1);
849  entries->put(get_current_full_path(entry) + path_separator + "actions",
850  all_actions);
851  }
852  else
853  entries->put(get_current_full_path(entry) + path_separator + "actions",
854  Utilities::int_to_string(actions.size() - 1));
855 
856 
857  // as documented, run the action on the default value at the very end
858  const std::string default_value = entries->get<std::string>(
859  get_current_full_path(entry) + path_separator + "default_value");
860  action(default_value);
861 }
862 
863 
864 
865 void
866 ParameterHandler::declare_alias(const std::string &existing_entry_name,
867  const std::string &alias_name,
868  const bool alias_is_deprecated)
869 {
870  // see if there is anything to refer to already
871  Assert(entries->get_optional<std::string>(
872  get_current_full_path(existing_entry_name)),
873  ExcMessage("You are trying to declare an alias entry <" + alias_name +
874  "> that references an entry <" + existing_entry_name +
875  ">, but the latter does not exist."));
876  // then also make sure that what is being referred to is in
877  // fact a parameter (not an alias or subsection)
878  Assert(entries->get_optional<std::string>(
879  get_current_full_path(existing_entry_name) + path_separator +
880  "value"),
881  ExcMessage("You are trying to declare an alias entry <" + alias_name +
882  "> that references an entry <" + existing_entry_name +
883  ">, but the latter does not seem to be a "
884  "parameter declaration."));
885 
886 
887  // now also make sure that if the alias has already been
888  // declared, that it is also an alias and refers to the same
889  // entry
890  if (entries->get_optional<std::string>(get_current_full_path(alias_name)))
891  {
892  Assert(entries->get_optional<std::string>(
893  get_current_full_path(alias_name) + path_separator + "alias"),
894  ExcMessage("You are trying to declare an alias entry <" +
895  alias_name +
896  "> but a non-alias entry already exists in this "
897  "subsection (i.e., there is either a preexisting "
898  "further subsection, or a parameter entry, with "
899  "the same name as the alias)."));
900  Assert(entries->get<std::string>(get_current_full_path(alias_name) +
901  path_separator + "alias") ==
902  existing_entry_name,
903  ExcMessage(
904  "You are trying to declare an alias entry <" + alias_name +
905  "> but an alias entry already exists in this "
906  "subsection and this existing alias references a "
907  "different parameter entry. Specifically, "
908  "you are trying to reference the entry <" +
909  existing_entry_name +
910  "> whereas the existing alias references "
911  "the entry <" +
912  entries->get<std::string>(get_current_full_path(alias_name) +
913  path_separator + "alias") +
914  ">."));
915  }
916 
917  entries->put(get_current_full_path(alias_name) + path_separator + "alias",
918  existing_entry_name);
919  entries->put(get_current_full_path(alias_name) + path_separator +
920  "deprecation_status",
921  (alias_is_deprecated ? "true" : "false"));
922 }
923 
924 
925 
926 void
927 ParameterHandler::enter_subsection(const std::string &subsection)
928 {
929  // if necessary create subsection
930  if (!entries->get_child_optional(get_current_full_path(subsection)))
931  entries->add_child(get_current_full_path(subsection),
932  boost::property_tree::ptree());
933 
934  // then enter it
935  subsection_path.push_back(subsection);
936 }
937 
938 
939 
940 void
942 {
943  // assert there is a subsection that
944  // we may leave
946 
947  if (subsection_path.size() > 0)
948  subsection_path.pop_back();
949 }
950 
951 
952 
953 bool
955  const std::vector<std::string> &sub_path) const
956 {
957  // Get full path to sub_path (i.e. prepend subsection_path to sub_path).
958  std::vector<std::string> full_path(subsection_path);
959  full_path.insert(full_path.end(), sub_path.begin(), sub_path.end());
960 
961  boost::optional<const boost::property_tree::ptree &> subsection(
962  entries->get_child_optional(
963  collate_path_string(path_separator, full_path)));
964 
965  // If subsection is boost::null (i.e. it does not exist)
966  // or it exists as a parameter/alias node, return false.
967  // Otherwise (i.e. it exists as a subsection node), return true.
968  return !(!subsection || is_parameter_node(subsection.get()) ||
969  is_alias_node(subsection.get()));
970 }
971 
972 
973 
974 std::string
975 ParameterHandler::get(const std::string &entry_string) const
976 {
977  // assert that the entry is indeed
978  // declared
979  if (boost::optional<std::string> value = entries->get_optional<std::string>(
980  get_current_full_path(entry_string) + path_separator + "value"))
981  return value.get();
982  else
983  {
984  Assert(false, ExcEntryUndeclared(entry_string));
985  return "";
986  }
987 }
988 
989 
990 
991 std::string
992 ParameterHandler::get(const std::vector<std::string> &entry_subsection_path,
993  const std::string & entry_string) const
994 {
995  // assert that the entry is indeed
996  // declared
997  if (boost::optional<std::string> value = entries->get_optional<std::string>(
998  get_current_full_path(entry_subsection_path, entry_string) +
999  path_separator + "value"))
1000  return value.get();
1001  else
1002  {
1003  Assert(false,
1004  ExcEntryUndeclared(demangle(
1005  get_current_full_path(entry_subsection_path, entry_string))));
1006  return "";
1007  }
1008 }
1009 
1010 
1011 
1012 long int
1013 ParameterHandler::get_integer(const std::string &entry_string) const
1014 {
1015  try
1016  {
1017  return Utilities::string_to_int(get(entry_string));
1018  }
1019  catch (...)
1020  {
1021  AssertThrow(false,
1022  ExcMessage("Can't convert the parameter value <" +
1023  get(entry_string) + "> for entry <" +
1024  entry_string + "> to an integer."));
1025  return 0;
1026  }
1027 }
1028 
1029 
1030 
1031 long int
1033  const std::vector<std::string> &entry_subsection_path,
1034  const std::string & entry_string) const
1035 {
1036  try
1037  {
1038  return Utilities::string_to_int(get(entry_subsection_path, entry_string));
1039  }
1040  catch (...)
1041  {
1042  AssertThrow(false,
1043  ExcMessage(
1044  "Can't convert the parameter value <" +
1045  get(entry_subsection_path, entry_string) + "> for entry <" +
1046  demangle(get_current_full_path(entry_subsection_path,
1047  entry_string)) +
1048  "> to an integer."));
1049  return 0;
1050  }
1051 }
1052 
1053 
1054 
1055 double
1056 ParameterHandler::get_double(const std::string &entry_string) const
1057 {
1058  try
1059  {
1060  return Utilities::string_to_double(get(entry_string));
1061  }
1062  catch (...)
1063  {
1064  AssertThrow(false,
1065  ExcMessage("Can't convert the parameter value <" +
1066  get(entry_string) + "> for entry <" +
1067  entry_string +
1068  "> to a double precision variable."));
1069  return 0;
1070  }
1071 }
1072 
1073 
1074 
1075 double
1077  const std::vector<std::string> &entry_subsection_path,
1078  const std::string & entry_string) const
1079 {
1080  try
1081  {
1083  get(entry_subsection_path, entry_string));
1084  }
1085  catch (...)
1086  {
1087  AssertThrow(false,
1088  ExcMessage(
1089  "Can't convert the parameter value <" +
1090  get(entry_subsection_path, entry_string) + "> for entry <" +
1091  demangle(get_current_full_path(entry_subsection_path,
1092  entry_string)) +
1093  "> to a double precision variable."));
1094  return 0;
1095  }
1096 }
1097 
1098 
1099 
1100 bool
1101 ParameterHandler::get_bool(const std::string &entry_string) const
1102 {
1103  const std::string s = get(entry_string);
1104 
1105  AssertThrow((s == "true") || (s == "false") || (s == "yes") || (s == "no"),
1106  ExcMessage("Can't convert the parameter value <" +
1107  get(entry_string) + "> for entry <" + entry_string +
1108  "> to a boolean."));
1109  if (s == "true" || s == "yes")
1110  return true;
1111  else
1112  return false;
1113 }
1114 
1115 
1116 
1117 bool
1119  const std::vector<std::string> &entry_subsection_path,
1120  const std::string & entry_string) const
1121 {
1122  const std::string s = get(entry_subsection_path, entry_string);
1123 
1124  AssertThrow((s == "true") || (s == "false") || (s == "yes") || (s == "no"),
1125  ExcMessage("Can't convert the parameter value <" +
1126  get(entry_subsection_path, entry_string) +
1127  "> for entry <" +
1128  demangle(get_current_full_path(entry_subsection_path,
1129  entry_string)) +
1130  "> to a boolean."));
1131  if (s == "true" || s == "yes")
1132  return true;
1133  else
1134  return false;
1135 }
1136 
1137 
1138 
1139 void
1140 ParameterHandler::set(const std::string &entry_string,
1141  const std::string &new_value)
1142 {
1143  // resolve aliases before looking up the correct entry
1144  std::string path = get_current_full_path(entry_string);
1145  if (entries->get_optional<std::string>(path + path_separator + "alias"))
1146  path = get_current_full_path(
1147  entries->get<std::string>(path + path_separator + "alias"));
1148 
1149  // get the node for the entry. if it doesn't exist, then we end up
1150  // in the else-branch below, which asserts that the entry is indeed
1151  // declared
1152  if (entries->get_optional<std::string>(path + path_separator + "value"))
1153  {
1154  // verify that the new value satisfies the provided pattern
1155  const unsigned int pattern_index =
1156  entries->get<unsigned int>(path + path_separator + "pattern");
1157  AssertThrow(patterns[pattern_index]->match(new_value),
1158  ExcValueDoesNotMatchPattern(new_value,
1159  entries->get<std::string>(
1160  path + path_separator +
1161  "pattern_description")));
1162 
1163  // then also execute the actions associated with this
1164  // parameter (if any have been provided)
1165  const boost::optional<std::string> action_indices_as_string =
1166  entries->get_optional<std::string>(path + path_separator + "actions");
1167  if (action_indices_as_string)
1168  {
1169  std::vector<int> action_indices = Utilities::string_to_int(
1170  Utilities::split_string_list(action_indices_as_string.get()));
1171  for (const unsigned int index : action_indices)
1172  if (actions.size() >= index + 1)
1173  actions[index](new_value);
1174  }
1175 
1176  // finally write the new value into the database
1177  entries->put(path + path_separator + "value", new_value);
1178 
1179  auto map_iter = entries_set_status.find(path);
1180  if (map_iter != entries_set_status.end())
1181  map_iter->second = std::pair<bool, bool>(map_iter->second.first, true);
1182  else
1183  AssertThrow(false,
1184  ExcMessage("Could not find parameter " + path +
1185  " in map entries_set_status."));
1186  }
1187  else
1188  AssertThrow(false, ExcEntryUndeclared(entry_string));
1189 }
1190 
1191 
1192 void
1193 ParameterHandler::set(const std::string &entry_string, const char *new_value)
1194 {
1195  // simply forward
1196  set(entry_string, std::string(new_value));
1197 }
1198 
1199 
1200 void
1201 ParameterHandler::set(const std::string &entry_string, const double new_value)
1202 {
1203  std::ostringstream s;
1204  s << std::setprecision(16);
1205  s << new_value;
1206 
1207  // hand this off to the function that
1208  // actually sets the value as a string
1209  set(entry_string, s.str());
1210 }
1211 
1212 
1213 
1214 void
1215 ParameterHandler::set(const std::string &entry_string, const long int new_value)
1216 {
1217  std::ostringstream s;
1218  s << new_value;
1219 
1220  // hand this off to the function that
1221  // actually sets the value as a string
1222  set(entry_string, s.str());
1223 }
1224 
1225 
1226 
1227 void
1228 ParameterHandler::set(const std::string &entry_string, const bool new_value)
1229 {
1230  // hand this off to the function that
1231  // actually sets the value as a string
1232  set(entry_string, (new_value ? "true" : "false"));
1233 }
1234 
1235 
1236 
1237 std::ostream &
1239  const OutputStyle style) const
1240 {
1241  AssertThrow(out, ExcIO());
1242 
1243  assert_validity_of_output_style(style);
1244 
1245  // Create entries copy and sort it, if needed.
1246  // In this way we ensure that the class state is never
1247  // modified by this function.
1248  boost::property_tree::ptree current_entries = *entries.get();
1249 
1250  // Sort parameters alphabetically, if needed.
1251  if (!(style & KeepDeclarationOrder))
1252  {
1253  // Dive recursively into the subsections,
1254  // starting from the top level.
1255  recursively_sort_parameters(path_separator,
1256  std::vector<std::string>(),
1257  current_entries);
1258  }
1259 
1260  // we'll have to print some text that is padded with spaces;
1261  // set the appropriate fill character, but also make sure that
1262  // we will restore the previous setting (and all other stream
1263  // flags) when we exit this function
1264  boost::io::ios_flags_saver restore_flags(out);
1265  boost::io::basic_ios_fill_saver<char> restore_fill_state(out);
1266  out.fill(' ');
1267 
1268  // we treat XML and JSON is one step via BOOST, whereas all of the others are
1269  // done recursively in our own code. take care of the two special formats
1270  // first
1271 
1272  // explicitly compress the tree if requested
1273  if ((style & Short) && (style & (XML | JSON)))
1274  {
1275  // modify the copy of the tree
1276  recursively_compress_tree(current_entries);
1277  }
1278 
1279  if (style & XML)
1280  {
1281  // call the writer function and exit as there is nothing
1282  // further to do down in this function
1283  //
1284  // XML has a requirement that there can only be one
1285  // single top-level entry, but we may have multiple
1286  // entries and sections. we work around this by
1287  // creating a tree just for this purpose with the
1288  // single top-level node "ParameterHandler" and
1289  // assign the existing tree under it
1290  boost::property_tree::ptree single_node_tree;
1291  single_node_tree.add_child("ParameterHandler", current_entries);
1292 
1293  write_xml(out, single_node_tree);
1294  return out;
1295  }
1296 
1297  if (style & JSON)
1298  {
1299  write_json(out, current_entries);
1300  return out;
1301  }
1302 
1303  // for all of the other formats, print a preamble:
1304  if ((style & Short) && (style & Text))
1305  {
1306  // nothing to do
1307  }
1308  else if (style & Text)
1309  {
1310  out << "# Listing of Parameters" << std::endl
1311  << "# ---------------------" << std::endl;
1312  }
1313  else if (style & LaTeX)
1314  {
1315  out << "\\subsection{Global parameters}" << std::endl;
1316  out << "\\label{parameters:global}" << std::endl;
1317  out << std::endl << std::endl;
1318  }
1319  else if (style & Description)
1320  {
1321  out << "Listing of Parameters:" << std::endl << std::endl;
1322  }
1323  else
1324  {
1325  Assert(false, ExcNotImplemented());
1326  }
1327 
1328  // dive recursively into the subsections
1330  current_entries,
1331  std::vector<std::string>(), // start at the top level
1332  style,
1333  0,
1334  out);
1335 
1336  return out;
1337 }
1338 
1339 
1340 
1341 void
1343  const std::string & filename,
1344  const ParameterHandler::OutputStyle style) const
1345 {
1346  std::string extension = filename.substr(filename.find_last_of('.') + 1);
1347  boost::algorithm::to_lower(extension);
1348 
1349  ParameterHandler::OutputStyle output_style = style;
1350  if (extension == "prm")
1351  output_style = style | PRM;
1352  else if (extension == "xml")
1353  output_style = style | XML;
1354  else if (extension == "json")
1355  output_style = style | JSON;
1356  else if (extension == "tex")
1357  output_style = style | LaTeX;
1358 
1359  std::ofstream out(filename);
1360  AssertThrow(out, ExcIO());
1361  print_parameters(out, output_style);
1362 }
1363 
1364 
1365 
1366 void
1368  const boost::property_tree::ptree &tree,
1369  const std::vector<std::string> & target_subsection_path,
1370  const OutputStyle style,
1371  const unsigned int indent_level,
1372  std::ostream & out) const
1373 {
1374  AssertThrow(out, ExcIO());
1375 
1376  // this function should not be necessary for XML or JSON output...
1377  Assert(!(style & (XML | JSON)), ExcInternalError());
1378 
1379  const boost::property_tree::ptree &current_section =
1380  tree.get_child(collate_path_string(path_separator, target_subsection_path));
1381 
1382  unsigned int overall_indent_level = indent_level;
1383 
1384  const bool is_short = style & Short;
1385 
1386  if (style & Text)
1387  {
1388  // first find out the longest entry name to be able to align the
1389  // equal signs to do this loop over all nodes of the current
1390  // tree, select the parameter nodes (and discard sub-tree nodes)
1391  // and take the maximum of their lengths
1392  //
1393  // likewise find the longest actual value string to make sure we
1394  // can align the default and documentation strings
1395  std::size_t longest_name = 0;
1396  std::size_t longest_value = 0;
1397  for (const auto &p : current_section)
1398  if (is_parameter_node(p.second) == true)
1399  {
1400  longest_name = std::max(longest_name, demangle(p.first).length());
1401  longest_value =
1402  std::max(longest_value,
1403  p.second.get<std::string>("value").length());
1404  }
1405 
1406  // print entries one by one
1407  bool first_entry = true;
1408  for (const auto &p : current_section)
1409  if (is_parameter_node(p.second) == true)
1410  {
1411  const std::string value = p.second.get<std::string>("value");
1412 
1413  // if there is documentation, then add an empty line
1414  // (unless this is the first entry in a subsection), print
1415  // the documentation, and then the actual entry; break the
1416  // documentation into readable chunks such that the whole
1417  // thing is at most 78 characters wide
1418  if (!is_short &&
1419  !p.second.get<std::string>("documentation").empty())
1420  {
1421  if (first_entry == false)
1422  out << '\n';
1423  else
1424  first_entry = false;
1425 
1426  const std::vector<std::string> doc_lines =
1428  p.second.get<std::string>("documentation"),
1429  78 - overall_indent_level * 2 - 2);
1430 
1431  for (const auto &doc_line : doc_lines)
1432  out << std::setw(overall_indent_level * 2) << ""
1433  << "# " << doc_line << '\n';
1434  }
1435 
1436  // print name and value of this entry
1437  out << std::setw(overall_indent_level * 2) << ""
1438  << "set " << demangle(p.first)
1439  << std::setw(longest_name - demangle(p.first).length() + 1)
1440  << " "
1441  << "= " << value;
1442 
1443  // finally print the default value, but only if it differs
1444  // from the actual value
1445  if (!is_short &&
1446  value != p.second.get<std::string>("default_value"))
1447  {
1448  out << std::setw(longest_value - value.length() + 1) << ' '
1449  << "# ";
1450  out << "default: "
1451  << p.second.get<std::string>("default_value");
1452  }
1453 
1454  out << '\n';
1455  }
1456  }
1457  else if (style & LaTeX)
1458  {
1459  auto escape = [](const std::string &input) {
1461  };
1462 
1463  // if there are any parameters in this section then print them
1464  // as an itemized list
1465  const bool parameters_exist_here =
1466  std::any_of(current_section.begin(),
1467  current_section.end(),
1468  [](const boost::property_tree::ptree::value_type &p) {
1469  return is_parameter_node(p.second) ||
1470  is_alias_node(p.second);
1471  });
1472  if (parameters_exist_here)
1473  {
1474  out << "\\begin{itemize}" << '\n';
1475 
1476  // print entries one by one
1477  for (const auto &p : current_section)
1478  if (is_parameter_node(p.second) == true)
1479  {
1480  const std::string value = p.second.get<std::string>("value");
1481 
1482  // print name
1483  out << "\\item {\\it Parameter name:} {\\tt "
1484  << escape(demangle(p.first)) << "}\n"
1485  << "\\phantomsection";
1486  {
1487  // create label: labels are not to be escaped but
1488  // mangled
1489  std::string label = "parameters:";
1490  for (const auto &path : target_subsection_path)
1491  {
1492  label.append(mangle(path));
1493  label.append("/");
1494  }
1495  label.append(p.first);
1496  // Backwards-compatibility. Output the label with and
1497  // without escaping whitespace:
1498  if (label.find("_20") != std::string::npos)
1499  out << "\\label{"
1500  << Utilities::replace_in_string(label, "_20", " ")
1501  << "}\n";
1502  out << "\\label{" << label << "}\n";
1503  }
1504  out << "\n\n";
1505 
1506  out << "\\index[prmindex]{" << escape(demangle(p.first))
1507  << "}\n";
1508  out << "\\index[prmindexfull]{";
1509  for (const auto &path : target_subsection_path)
1510  out << escape(path) << "!";
1511  out << escape(demangle(p.first)) << "}\n";
1512 
1513  // finally print value and default
1514  out << "{\\it Value:} " << escape(value) << "\n\n"
1515  << '\n'
1516  << "{\\it Default:} "
1517  << escape(p.second.get<std::string>("default_value"))
1518  << "\n\n"
1519  << '\n';
1520 
1521  // if there is a documenting string, print it as well but
1522  // don't escape to allow formatting/formulas
1523  if (!is_short &&
1524  !p.second.get<std::string>("documentation").empty())
1525  out << "{\\it Description:} "
1526  << p.second.get<std::string>("documentation") << "\n\n"
1527  << '\n';
1528  if (!is_short)
1529  {
1530  // also output possible values, do not escape because the
1531  // description internally will use LaTeX formatting
1532  const unsigned int pattern_index =
1533  p.second.get<unsigned int>("pattern");
1534  const std::string desc_str =
1535  patterns[pattern_index]->description(
1537  out << "{\\it Possible values:} " << desc_str << '\n';
1538  }
1539  }
1540  else if (is_alias_node(p.second) == true)
1541  {
1542  const std::string alias = p.second.get<std::string>("alias");
1543 
1544  // print name
1545  out << "\\item {\\it Parameter name:} {\\tt "
1546  << escape(demangle(p.first)) << "}\n"
1547  << "\\phantomsection";
1548  {
1549  // create label: labels are not to be escaped but
1550  // mangled
1551  std::string label = "parameters:";
1552  for (const auto &path : target_subsection_path)
1553  {
1554  label.append(mangle(path));
1555  label.append("/");
1556  }
1557  label.append(p.first);
1558  // Backwards-compatibility. Output the label with and
1559  // without escaping whitespace:
1560  if (label.find("_20") != std::string::npos)
1561  out << "\\label{"
1562  << Utilities::replace_in_string(label, "_20", " ")
1563  << "}\n";
1564  out << "\\label{" << label << "}\n";
1565  }
1566  out << "\n\n";
1567 
1568  out << "\\index[prmindex]{" << escape(demangle(p.first))
1569  << "}\n";
1570  out << "\\index[prmindexfull]{";
1571  for (const auto &path : target_subsection_path)
1572  out << escape(path) << "!";
1573  out << escape(demangle(p.first)) << "}\n";
1574 
1575  // finally print alias and indicate if it is deprecated
1576  out
1577  << "This parameter is an alias for the parameter ``\\texttt{"
1578  << escape(alias) << "}''."
1579  << (p.second.get<std::string>("deprecation_status") ==
1580  "true" ?
1581  " Its use is deprecated." :
1582  "")
1583  << "\n\n"
1584  << '\n';
1585  }
1586  out << "\\end{itemize}" << '\n';
1587  }
1588  }
1589  else if (style & Description)
1590  {
1591  // first find out the longest entry name to be able to align the
1592  // equal signs
1593  std::size_t longest_name = 0;
1594  for (const auto &p : current_section)
1595  if (is_parameter_node(p.second) == true)
1596  longest_name = std::max(longest_name, demangle(p.first).length());
1597 
1598  // print entries one by one
1599  for (const auto &p : current_section)
1600  if (is_parameter_node(p.second) == true)
1601  {
1602  // print name and value
1603  out << std::setw(overall_indent_level * 2) << ""
1604  << "set " << demangle(p.first)
1605  << std::setw(longest_name - demangle(p.first).length() + 1)
1606  << " "
1607  << " = ";
1608 
1609  // print possible values:
1610  const unsigned int pattern_index =
1611  p.second.get<unsigned int>("pattern");
1612  const std::string full_desc_str =
1613  patterns[pattern_index]->description(Patterns::PatternBase::Text);
1614  const std::vector<std::string> description_str =
1616  full_desc_str, 78 - overall_indent_level * 2 - 2, '|');
1617  if (description_str.size() > 1)
1618  {
1619  out << '\n';
1620  for (const auto &description : description_str)
1621  out << std::setw(overall_indent_level * 2 + 6) << ""
1622  << description << '\n';
1623  }
1624  else if (description_str.empty() == false)
1625  out << " " << description_str[0] << '\n';
1626  else
1627  out << '\n';
1628 
1629  // if there is a documenting string, print it as well
1630  if (!is_short &&
1631  p.second.get<std::string>("documentation").length() != 0)
1632  out << std::setw(overall_indent_level * 2 + longest_name + 10)
1633  << ""
1634  << "(" << p.second.get<std::string>("documentation") << ")"
1635  << '\n';
1636  }
1637  }
1638  else
1639  {
1640  Assert(false, ExcNotImplemented());
1641  }
1642 
1643 
1644  // if there was text before and there are sections to come, put two
1645  // newlines between the last entry and the first subsection
1646  {
1647  unsigned int n_parameters = 0;
1648  unsigned int n_sections = 0;
1649  for (const auto &p : current_section)
1650  if (is_parameter_node(p.second) == true)
1651  ++n_parameters;
1652  else if (is_alias_node(p.second) == false)
1653  ++n_sections;
1654 
1655  if (!(style & Description) && (!((style & Text) && is_short)) &&
1656  (n_parameters != 0) && (n_sections != 0))
1657  out << "\n\n";
1658  }
1659 
1660  // now transverse subsections tree
1661  for (const auto &p : current_section)
1662  if ((is_parameter_node(p.second) == false) &&
1663  (is_alias_node(p.second) == false))
1664  {
1665  // first print the subsection header
1666  if ((style & Text) || (style & Description))
1667  {
1668  out << std::setw(overall_indent_level * 2) << ""
1669  << "subsection " << demangle(p.first) << '\n';
1670  }
1671  else if (style & LaTeX)
1672  {
1673  auto escape = [](const std::string &input) {
1674  return Patterns::internal::escape(input,
1676  };
1677 
1678  out << '\n' << "\\subsection{Parameters in section \\tt ";
1679 
1680  // find the path to the current section so that we can
1681  // print it in the \subsection{...} heading
1682  for (const auto &path : target_subsection_path)
1683  out << escape(path) << "/";
1684  out << escape(demangle(p.first));
1685 
1686  out << "}" << '\n';
1687  out << "\\label{parameters:";
1688  for (const auto &path : target_subsection_path)
1689  out << mangle(path) << "/";
1690  out << p.first << "}";
1691  out << '\n';
1692 
1693  out << '\n';
1694  }
1695  else
1696  {
1697  Assert(false, ExcNotImplemented());
1698  }
1699 
1700  // then the contents of the subsection
1701  const std::string subsection = demangle(p.first);
1702  std::vector<std::string> directory_path = target_subsection_path;
1703  directory_path.emplace_back(subsection);
1704 
1706  tree, directory_path, style, overall_indent_level + 1, out);
1707 
1708  if (is_short && (style & Text))
1709  {
1710  // write end of subsection.
1711  out << std::setw(overall_indent_level * 2) << ""
1712  << "end" << '\n';
1713  }
1714  else if (style & Text)
1715  {
1716  // write end of subsection. one blank line after each
1717  // subsection
1718  out << std::setw(overall_indent_level * 2) << ""
1719  << "end" << '\n'
1720  << '\n';
1721 
1722  // if this is a toplevel subsection, then have two
1723  // newlines
1724  if (overall_indent_level == 0)
1725  out << '\n';
1726  }
1727  else if (style & Description)
1728  {
1729  // nothing to do
1730  }
1731  else if (style & LaTeX)
1732  {
1733  // nothing to do
1734  }
1735  else
1736  {
1737  Assert(false, ExcNotImplemented());
1738  }
1739  }
1740 }
1741 
1742 
1743 
1744 void
1746 {
1747  out.push("parameters");
1748  // dive recursively into the subsections
1749  log_parameters_section(out, style);
1750 
1751  out.pop();
1752 }
1753 
1754 
1755 void
1757  const OutputStyle style)
1758 {
1759  // Create entries copy and sort it, if needed.
1760  // In this way we ensure that the class state is never
1761  // modified by this function.
1762  boost::property_tree::ptree sorted_entries;
1763  boost::property_tree::ptree *current_entries = entries.get();
1764 
1765  // Sort parameters alphabetically, if needed.
1766  if (!(style & KeepDeclarationOrder))
1767  {
1768  sorted_entries = *entries;
1769  current_entries = &sorted_entries;
1770 
1771  // Dive recursively into the subsections,
1772  // starting from the current level.
1773  recursively_sort_parameters(path_separator,
1775  sorted_entries);
1776  }
1777 
1778  const boost::property_tree::ptree &current_section =
1779  current_entries->get_child(get_current_path());
1780 
1781  // print entries one by one
1782  for (const auto &p : current_section)
1783  if (is_parameter_node(p.second) == true)
1784  out << demangle(p.first) << ": " << p.second.get<std::string>("value")
1785  << std::endl;
1786 
1787  // now transverse subsections tree
1788  for (const auto &p : current_section)
1789  if (is_parameter_node(p.second) == false)
1790  {
1791  out.push(demangle(p.first));
1792  enter_subsection(demangle(p.first));
1793  log_parameters_section(out, style);
1794  leave_subsection();
1795  out.pop();
1796  }
1797 }
1798 
1799 
1800 
1801 void
1803  const std::string &input_filename,
1804  const unsigned int current_line_n,
1805  const bool skip_undefined)
1806 {
1807  // save a copy for some error messages
1808  const std::string original_line = line;
1809 
1810  // if there is a comment, delete it
1811  if (line.find('#') != std::string::npos)
1812  line.erase(line.find('#'), std::string::npos);
1813 
1814  // replace \t by space:
1815  while (line.find('\t') != std::string::npos)
1816  line.replace(line.find('\t'), 1, " ");
1817 
1818  // trim start and end:
1819  line = Utilities::trim(line);
1820 
1821  // if line is now empty: leave
1822  if (line.length() == 0)
1823  {
1824  return;
1825  }
1826  // enter subsection
1827  else if (Utilities::match_at_string_start(line, "SUBSECTION ") ||
1828  Utilities::match_at_string_start(line, "subsection "))
1829  {
1830  // delete this prefix
1831  line.erase(0, std::string("subsection").length() + 1);
1832 
1833  const std::string subsection = Utilities::trim(line);
1834 
1835  // check whether subsection exists
1836  AssertThrow(skip_undefined || entries->get_child_optional(
1837  get_current_full_path(subsection)),
1838  ExcNoSubsection(current_line_n,
1839  input_filename,
1840  demangle(get_current_full_path(subsection))));
1841 
1842  // subsection exists
1843  subsection_path.push_back(subsection);
1844  }
1845  // exit subsection
1846  else if (Utilities::match_at_string_start(line, "END") ||
1847  Utilities::match_at_string_start(line, "end"))
1848  {
1849  line.erase(0, 3);
1850  while ((line.size() > 0) && (std::isspace(line[0])))
1851  line.erase(0, 1);
1852 
1853  AssertThrow(
1854  line.size() == 0,
1855  ExcCannotParseLine(current_line_n,
1856  input_filename,
1857  "Invalid content after 'end' or 'END' statement."));
1858  AssertThrow(subsection_path.size() != 0,
1859  ExcCannotParseLine(current_line_n,
1860  input_filename,
1861  "There is no subsection to leave here."));
1862  leave_subsection();
1863  }
1864  // regular entry
1865  else if (Utilities::match_at_string_start(line, "SET ") ||
1866  Utilities::match_at_string_start(line, "set "))
1867  {
1868  // erase "set" statement
1869  line.erase(0, 4);
1870 
1871  std::string::size_type pos = line.find('=');
1872  AssertThrow(
1873  pos != std::string::npos,
1874  ExcCannotParseLine(current_line_n,
1875  input_filename,
1876  "Invalid format of 'set' or 'SET' statement."));
1877 
1878  // extract entry name and value and trim
1879  std::string entry_name = Utilities::trim(std::string(line, 0, pos));
1880  std::string entry_value =
1881  Utilities::trim(std::string(line, pos + 1, std::string::npos));
1882 
1883  // resolve aliases before we look up the entry. if necessary, print
1884  // a warning that the alias is deprecated
1885  std::string path = get_current_full_path(entry_name);
1886  if (entries->get_optional<std::string>(path + path_separator + "alias"))
1887  {
1888  if (entries->get<std::string>(path + path_separator +
1889  "deprecation_status") == "true")
1890  {
1891  std::cerr << "Warning in line <" << current_line_n
1892  << "> of file <" << input_filename
1893  << ">: You are using the deprecated spelling <"
1894  << entry_name << "> of the parameter <"
1895  << entries->get<std::string>(path + path_separator +
1896  "alias")
1897  << ">." << std::endl;
1898  }
1899  path = get_current_full_path(
1900  entries->get<std::string>(path + path_separator + "alias"));
1901  }
1902 
1903  // get the node for the entry. if it doesn't exist, then we end up
1904  // in the else-branch below, which asserts that the entry is indeed
1905  // declared
1906  if (entries->get_optional<std::string>(path + path_separator + "value"))
1907  {
1908  // if entry was declared: does it match the regex? if not, don't enter
1909  // it into the database exception: if it contains characters which
1910  // specify it as a multiple loop entry, then ignore content
1911  if (entry_value.find('{') == std::string::npos)
1912  {
1913  // verify that the new value satisfies the provided pattern
1914  const unsigned int pattern_index =
1915  entries->get<unsigned int>(path + path_separator + "pattern");
1916  AssertThrow(patterns[pattern_index]->match(entry_value),
1918  current_line_n,
1919  input_filename,
1920  entry_value,
1921  entry_name,
1922  patterns[pattern_index]->description()));
1923 
1924  // then also execute the actions associated with this
1925  // parameter (if any have been provided)
1926  const boost::optional<std::string> action_indices_as_string =
1927  entries->get_optional<std::string>(path + path_separator +
1928  "actions");
1929  if (action_indices_as_string)
1930  {
1931  std::vector<int> action_indices =
1933  action_indices_as_string.get()));
1934  for (const unsigned int index : action_indices)
1935  if (actions.size() >= index + 1)
1936  actions[index](entry_value);
1937  }
1938  }
1939 
1940  // finally write the new value into the database
1941  entries->put(path + path_separator + "value", entry_value);
1942  }
1943  else
1944  {
1945  AssertThrow(
1946  skip_undefined,
1947  ExcCannotParseLine(current_line_n,
1948  input_filename,
1949  ("No entry with name <" + entry_name +
1950  "> was declared in the current subsection.")));
1951  }
1952  }
1953  // an include statement?
1954  else if (Utilities::match_at_string_start(line, "include ") ||
1955  Utilities::match_at_string_start(line, "INCLUDE "))
1956  {
1957  // erase "include " statement and eliminate spaces
1958  line.erase(0, 7);
1959  while ((line.size() > 0) && (line[0] == ' '))
1960  line.erase(0, 1);
1961 
1962  // the remainder must then be a filename
1963  AssertThrow(line.size() != 0,
1964  ExcCannotParseLine(current_line_n,
1965  input_filename,
1966  "The current line is an 'include' or "
1967  "'INCLUDE' statement, but it does not "
1968  "name a file for inclusion."));
1969 
1970  std::ifstream input(line.c_str());
1971  AssertThrow(input,
1972  ExcCannotOpenIncludeStatementFile(current_line_n,
1973  input_filename,
1974  line));
1975  parse_input(input, line, "", skip_undefined);
1976  }
1977  else
1978  {
1979  AssertThrow(
1980  false,
1981  ExcCannotParseLine(current_line_n,
1982  input_filename,
1983  "The line\n\n"
1984  " <" +
1985  original_line +
1986  ">\n\n"
1987  "could not be parsed: please check to "
1988  "make sure that the file is not missing a "
1989  "'set', 'include', 'subsection', or 'end' "
1990  "statement."));
1991  }
1992 }
1993 
1994 
1995 
1996 std::size_t
1998 {
1999  // TODO: add to this an estimate of the memory in the property_tree
2001 }
2002 
2003 
2004 
2005 bool
2007 {
2008  if (patterns.size() != prm2.patterns.size())
2009  return false;
2010 
2011  for (unsigned int j = 0; j < patterns.size(); ++j)
2012  if (patterns[j]->description() != prm2.patterns[j]->description())
2013  return false;
2014 
2015  // instead of walking through all
2016  // the nodes of the two trees
2017  // entries and prm2.entries and
2018  // comparing them for equality,
2019  // simply dump the content of the
2020  // entire structure into a string
2021  // and compare those for equality
2022  std::ostringstream o1, o2;
2023  write_json(o1, *entries);
2024  write_json(o2, *prm2.entries);
2025  return (o1.str() == o2.str());
2026 }
2027 
2028 
2029 
2030 std::set<std::string>
2032 {
2033  std::set<std::string> entries_wrongly_not_set;
2034 
2035  for (const auto &it : entries_set_status)
2036  if (it.second.first == true && it.second.second == false)
2037  entries_wrongly_not_set.insert(it.first);
2038 
2039  return entries_wrongly_not_set;
2040 }
2041 
2042 
2043 
2044 void
2046 {
2047  const std::set<std::string> entries_wrongly_not_set =
2049 
2050  if (entries_wrongly_not_set.size() > 0)
2051  {
2052  std::string list_of_missing_parameters = "\n\n";
2053  for (const auto &it : entries_wrongly_not_set)
2054  list_of_missing_parameters += " " + it + "\n";
2055  list_of_missing_parameters += "\n";
2056 
2057  AssertThrow(
2058  entries_wrongly_not_set.size() == 0,
2059  ExcMessage(
2060  "Not all entries of the parameter handler that were declared with "
2061  "`has_to_be_set = true` have been set. The following parameters " +
2062  list_of_missing_parameters +
2063  " have not been set. "
2064  "A possible reason might be that you did not add these parameter to "
2065  "the input file or that their spelling is not correct."));
2066  }
2067 }
2068 
2069 
2070 
2072  : n_branches(0)
2073 {}
2074 
2075 
2076 
2077 void
2079  const std::string &filename,
2080  const std::string &last_line,
2081  const bool skip_undefined)
2082 {
2083  AssertThrow(input, ExcIO());
2084 
2085  // Note that (to avoid infinite recursion) we have to explicitly call the
2086  // base class version of parse_input and *not* a wrapper (which may be
2087  // virtual and lead us back here)
2088  ParameterHandler::parse_input(input, filename, last_line, skip_undefined);
2089  init_branches();
2090 }
2091 
2092 
2093 
2094 void
2096 {
2097  for (unsigned int run_no = 0; run_no < n_branches; ++run_no)
2098  {
2099  // give create_new one-based numbers
2100  uc.create_new(run_no + 1);
2101  fill_entry_values(run_no);
2102  uc.run(*this);
2103  }
2104 }
2105 
2106 
2107 
2108 void
2110 {
2111  multiple_choices.clear();
2113 
2114  // split up different values
2115  for (auto &multiple_choice : multiple_choices)
2116  multiple_choice.split_different_values();
2117 
2118  // finally calculate number of branches
2119  n_branches = 1;
2120  for (const auto &multiple_choice : multiple_choices)
2121  if (multiple_choice.type == Entry::variant)
2122  n_branches *= multiple_choice.different_values.size();
2123 
2124  // check whether array entries have the correct
2125  // number of entries
2126  for (const auto &multiple_choice : multiple_choices)
2127  if (multiple_choice.type == Entry::array)
2128  if (multiple_choice.different_values.size() != n_branches)
2129  std::cerr << " The entry value" << std::endl
2130  << " " << multiple_choice.entry_value << std::endl
2131  << " for the entry named" << std::endl
2132  << " " << multiple_choice.entry_name << std::endl
2133  << " does not have the right number of entries for the "
2134  << std::endl
2135  << " " << n_branches
2136  << " variant runs that will be performed." << std::endl;
2137 
2138 
2139  // do a first run on filling the values to
2140  // check for the conformance with the regexp
2141  // (later on, this will be lost in the whole
2142  // other output)
2143  for (unsigned int i = 0; i < n_branches; ++i)
2144  fill_entry_values(i);
2145 }
2146 
2147 
2148 
2149 void
2151 {
2152  const boost::property_tree::ptree &current_section =
2153  entries->get_child(get_current_path());
2154 
2155  // check all entries in the present
2156  // subsection whether they are
2157  // multiple entries
2158  for (const auto &p : current_section)
2159  if (is_parameter_node(p.second) == true)
2160  {
2161  const std::string value = p.second.get<std::string>("value");
2162  if (value.find('{') != std::string::npos)
2163  multiple_choices.emplace_back(subsection_path,
2164  demangle(p.first),
2165  value);
2166  }
2167 
2168  // then loop over all subsections
2169  for (const auto &p : current_section)
2170  if (is_parameter_node(p.second) == false)
2171  {
2172  enter_subsection(demangle(p.first));
2174  leave_subsection();
2175  }
2176 }
2177 
2178 
2179 
2180 void
2182 {
2183  unsigned int possibilities = 1;
2184 
2185  std::vector<Entry>::iterator choice;
2186  for (choice = multiple_choices.begin(); choice != multiple_choices.end();
2187  ++choice)
2188  {
2189  const unsigned int selection =
2190  (run_no / possibilities) % choice->different_values.size();
2191  std::string entry_value;
2192  if (choice->type == Entry::variant)
2193  entry_value = choice->different_values[selection];
2194  else
2195  {
2196  if (run_no >= choice->different_values.size())
2197  {
2198  std::cerr
2199  << "The given array for entry <" << choice->entry_name
2200  << "> does not contain enough elements! Taking empty string instead."
2201  << std::endl;
2202  entry_value = "";
2203  }
2204  else
2205  entry_value = choice->different_values[run_no];
2206  }
2207 
2208  // temporarily enter the
2209  // subsection tree of this
2210  // multiple entry, set the
2211  // value, and get out
2212  // again. the set() operation
2213  // also tests for the
2214  // correctness of the value
2215  // with regard to the pattern
2216  subsection_path.swap(choice->subsection_path);
2217  set(choice->entry_name, entry_value);
2218  subsection_path.swap(choice->subsection_path);
2219 
2220  // move ahead if it was a variant entry
2221  if (choice->type == Entry::variant)
2222  possibilities *= choice->different_values.size();
2223  }
2224 }
2225 
2226 
2227 
2228 std::size_t
2230 {
2231  std::size_t mem = ParameterHandler::memory_consumption();
2232  for (const auto &multiple_choice : multiple_choices)
2233  mem += multiple_choice.memory_consumption();
2234 
2235  return mem;
2236 }
2237 
2238 
2239 
2240 MultipleParameterLoop::Entry::Entry(const std::vector<std::string> &ssp,
2241  const std::string & Name,
2242  const std::string & Value)
2243  : subsection_path(ssp)
2244  , entry_name(Name)
2245  , entry_value(Value)
2246  , type(Entry::array)
2247 {}
2248 
2249 
2250 
2251 void
2253 {
2254  // split string into three parts:
2255  // part before the opening "{",
2256  // the selection itself, final
2257  // part after "}"
2258  std::string prefix(entry_value, 0, entry_value.find('{'));
2259  std::string multiple(entry_value,
2260  entry_value.find('{') + 1,
2261  entry_value.rfind('}') - entry_value.find('{') - 1);
2262  std::string postfix(entry_value,
2263  entry_value.rfind('}') + 1,
2264  std::string::npos);
2265  // if array entry {{..}}: delete inner
2266  // pair of braces
2267  if (multiple[0] == '{')
2268  multiple.erase(0, 1);
2269  if (multiple[multiple.size() - 1] == '}')
2270  multiple.erase(multiple.size() - 1, 1);
2271  // erase leading and trailing spaces
2272  // in multiple
2273  while (std::isspace(multiple[0]))
2274  multiple.erase(0, 1);
2275  while (std::isspace(multiple[multiple.size() - 1]))
2276  multiple.erase(multiple.size() - 1, 1);
2277 
2278  // delete spaces around '|'
2279  while (multiple.find(" |") != std::string::npos)
2280  multiple.replace(multiple.find(" |"), 2, "|");
2281  while (multiple.find("| ") != std::string::npos)
2282  multiple.replace(multiple.find("| "), 2, "|");
2283 
2284  while (multiple.find('|') != std::string::npos)
2285  {
2286  different_values.push_back(
2287  prefix + std::string(multiple, 0, multiple.find('|')) + postfix);
2288  multiple.erase(0, multiple.find('|') + 1);
2289  }
2290  // make up the last selection ("while" broke
2291  // because there was no '|' any more
2292  different_values.push_back(prefix + multiple + postfix);
2293  // finally check whether this was a variant
2294  // entry ({...}) or an array ({{...}})
2295  if ((entry_value.find("{{") != std::string::npos) &&
2296  (entry_value.find("}}") != std::string::npos))
2297  type = Entry::array;
2298  else
2299  type = Entry::variant;
2300 }
2301 
2302 
2303 std::size_t
2305 {
2310  sizeof(type));
2311 }
2312 
std::vector< std::string > split_string_list(const std::string &s, const std::string &delimiter=",")
Definition: utilities.cc:705
std::vector< Entry > multiple_choices
long int get_integer(const std::string &entry_string) const
void pop()
Definition: logstream.cc:316
static const char path_separator
std::string escape(const std::string &input, const PatternBase::OutputStyle style)
Definition: patterns.cc:55
void assert_that_entries_have_been_set() const
void loop(UserClass &uc)
types::global_dof_index size_type
Definition: cuda_kernels.h:45
static ::ExceptionBase & ExcCannotOpenIncludeStatementFile(int arg1, std::string arg2, std::string arg3)
static ::ExceptionBase & ExcIO()
virtual void run(ParameterHandler &prm)=0
std::string get_current_path() const
static ::ExceptionBase & ExcEntryUndeclared(std::string arg1)
std::string trim(const std::string &input)
Definition: utilities.cc:532
std::string get(const std::string &entry_string) const
static ::ExceptionBase & ExcUnbalancedSubsections(std::string arg1, std::string arg2)
std::ostream & print_parameters(std::ostream &out, const OutputStyle style) const
std::vector< std::string > break_text_into_lines(const std::string &original_text, const unsigned int width, const char delimiter=' ')
Definition: utilities.cc:759
virtual void parse_input(std::istream &input, const std::string &filename="input file", const std::string &last_line="", const bool skip_undefined=false) override
virtual void parse_input_from_string(const std::string &s, const std::string &last_line="", const bool skip_undefined=false)
virtual std::unique_ptr< PatternBase > clone() const =0
void set(const std::string &entry_name, const std::string &new_value)
void log_parameters(LogStream &out, const OutputStyle style=DefaultStyle)
std::vector< std::string > subsection_path
#define AssertThrow(cond, exc)
Definition: exceptions.h:1531
void scan_line(std::string line, const std::string &input_filename, const unsigned int current_line_n, const bool skip_undefined)
static ::ExceptionBase & ExcValueDoesNotMatchPattern(std::string arg1, std::string arg2)
std::vector< std::string > subsection_path
double string_to_double(const std::string &s)
Definition: utilities.cc:657
#define DEAL_II_DISABLE_EXTRA_DIAGNOSTICS
Definition: config.h:372
bool subsection_path_exists(const std::vector< std::string > &sub_path) const
void enter_subsection(const std::string &subsection)
static ::ExceptionBase & ExcFileNotFound(std::string arg1, std::string arg2)
static ::ExceptionBase & ExcInvalidXMLParameterFile()
static ::ExceptionBase & ExcMessage(std::string arg1)
virtual void create_new(const unsigned int run_no)=0
static ::ExceptionBase & ExcCannotParseLine(int arg1, std::string arg2, std::string arg3)
double get_double(const std::string &entry_name) const
std::size_t memory_consumption() const
void declare_entry(const std::string &entry, const std::string &default_value, const Patterns::PatternBase &pattern=Patterns::Anything(), const std::string &documentation="", const bool has_to_be_set=false)
virtual void parse_input_from_xml(std::istream &input, const bool skip_undefined=false)
#define Assert(cond, exc)
Definition: exceptions.h:1419
std::string get_current_full_path(const std::string &name) const
#define DEAL_II_NAMESPACE_CLOSE
Definition: config.h:359
bool match_at_string_start(const std::string &name, const std::string &pattern)
Definition: utilities.cc:839
std::unique_ptr< boost::property_tree::ptree > entries
void fill_entry_values(const unsigned int run_no)
virtual void parse_input(std::istream &input, const std::string &filename="input file", const std::string &last_line="", const bool skip_undefined=false)
bool get_bool(const std::string &entry_name) const
static ::ExceptionBase & ExcAlreadyAtTopLevel()
std::string int_to_string(const unsigned int value, const unsigned int digits=numbers::invalid_unsigned_int)
Definition: utilities.cc:474
std::string replace_in_string(const std::string &input, const std::string &from, const std::string &to)
Definition: utilities.cc:513
std::vector< std::unique_ptr< const Patterns::PatternBase > > patterns
virtual bool match(const std::string &test_string) const =0
SymmetricTensor< 2, dim, Number > b(const Tensor< 2, dim, Number > &F)
bool operator==(const ParameterHandler &prm2) const
void log_parameters_section(LogStream &out, const OutputStyle style=DefaultStyle)
virtual void parse_input_from_json(std::istream &input, const bool skip_undefined=false)
std::set< std::string > get_entries_wrongly_not_set() const
std::size_t memory_consumption() const
void push(const std::string &text)
Definition: logstream.cc:302
virtual std::string description(const OutputStyle style=Machine) const =0
void add_action(const std::string &entry, const std::function< void(const std::string &value)> &action)
#define DEAL_II_ENABLE_EXTRA_DIAGNOSTICS
Definition: config.h:409
#define DEAL_II_NAMESPACE_OPEN
Definition: config.h:358
int string_to_int(const std::string &s)
Definition: utilities.cc:609
static ::ExceptionBase & ExcNotImplemented()
std::vector< std::string > different_values
std::size_t memory_consumption() const
static ::ExceptionBase & ExcInvalidEntryForPattern(int arg1, std::string arg2, std::string arg3, std::string arg4, std::string arg5)
void recursively_print_parameters(const boost::property_tree::ptree &tree, const std::vector< std::string > &target_subsection_path, const ParameterHandler::OutputStyle style, const unsigned int indent_level, std::ostream &out) const
std::vector< std::function< void(const std::string &)> > actions
static ::ExceptionBase & ExcNoSubsection(int arg1, std::string arg2, std::string arg3)
static const bool value
void declare_alias(const std::string &existing_entry_name, const std::string &alias_name, const bool alias_is_deprecated=false)
T max(const T &t, const MPI_Comm &mpi_communicator)
std::map< std::string, std::pair< bool, bool > > entries_set_status
std::enable_if< std::is_fundamental< T >::value, std::size_t >::type memory_consumption(const T &t)
static ::ExceptionBase & ExcInternalError()