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