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