Reference documentation for deal.II version GIT relicensing-31-gf283a0fc39 2024-02-29 06:50:02+00:00
\(\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// SPDX-License-Identifier: LGPL-2.1-or-later
4// Copyright (C) 1998 - 2024 by the deal.II authors
5//
6// This file is part of the deal.II library.
7//
8// Part of the source code is dual licensed under Apache-2.0 WITH
9// LLVM-exception OR LGPL-2.1-or-later. Detailed license information
10// governing the source code and code contributions can be found in
11// LICENSE.md and CONTRIBUTING.md at the top level directory of deal.II.
12//
13// ------------------------------------------------------------------------
14
15
21
22#define BOOST_BIND_GLOBAL_PLACEHOLDERS
23#include <boost/algorithm/string.hpp>
24#include <boost/io/ios_state.hpp>
25#include <boost/property_tree/json_parser.hpp>
26#include <boost/property_tree/ptree.hpp>
27#include <boost/property_tree/xml_parser.hpp>
28#undef BOOST_BIND_GLOBAL_PLACEHOLDERS
29
30#include <algorithm>
31#include <cctype>
32#include <cstdlib>
33#include <cstring>
34#include <fstream>
35#include <iomanip>
36#include <iostream>
37#include <limits>
38#include <sstream>
39
40
42
43
45 : entries(new boost::property_tree::ptree())
46{}
47
48
49namespace
50{
51 std::string
52 mangle(const std::string &s)
53 {
54 std::string u;
55
56 // reserve the minimum number of characters we will need. it may
57 // be more but this is the least we can do
58 u.reserve(s.size());
59
60 // see if the name is special and if so mangle the whole thing
61 const bool mangle_whole_string = (s == "value");
62
63 // for all parts of the string, see if it is an allowed character or not
64 for (const char c : s)
65 {
66 static const std::string allowed_characters(
67 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789");
68
69 if ((!mangle_whole_string) &&
70 (allowed_characters.find(c) != std::string::npos))
71 u.push_back(c);
72 else
73 {
74 u.push_back('_');
75 static const char hex[16] = {'0',
76 '1',
77 '2',
78 '3',
79 '4',
80 '5',
81 '6',
82 '7',
83 '8',
84 '9',
85 'a',
86 'b',
87 'c',
88 'd',
89 'e',
90 'f'};
91 u.push_back(hex[static_cast<unsigned char>(c) / 16]);
92 u.push_back(hex[static_cast<unsigned char>(c) % 16]);
93 }
94 }
95
96 return u;
97 }
98
99
100
101 std::string
102 demangle(const std::string &s)
103 {
104 std::string u;
105 u.reserve(s.size());
106
107 for (unsigned int i = 0; i < s.size(); ++i)
108 if (s[i] != '_')
109 u.push_back(s[i]);
110 else
111 {
112 Assert(i + 2 < s.size(),
113 ExcMessage("Trying to demangle an invalid string."));
114
115 unsigned char c = 0;
116 switch (s[i + 1])
117 {
118 case '0':
119 c = 0 * 16;
120 break;
121 case '1':
122 c = 1 * 16;
123 break;
124 case '2':
125 c = 2 * 16;
126 break;
127 case '3':
128 c = 3 * 16;
129 break;
130 case '4':
131 c = 4 * 16;
132 break;
133 case '5':
134 c = 5 * 16;
135 break;
136 case '6':
137 c = 6 * 16;
138 break;
139 case '7':
140 c = 7 * 16;
141 break;
142 case '8':
143 c = 8 * 16;
144 break;
145 case '9':
146 c = 9 * 16;
147 break;
148 case 'a':
149 c = 10 * 16;
150 break;
151 case 'b':
152 c = 11 * 16;
153 break;
154 case 'c':
155 c = 12 * 16;
156 break;
157 case 'd':
158 c = 13 * 16;
159 break;
160 case 'e':
161 c = 14 * 16;
162 break;
163 case 'f':
164 c = 15 * 16;
165 break;
166 default:
168 }
169 switch (s[i + 2])
170 {
171 case '0':
172 c += 0;
173 break;
174 case '1':
175 c += 1;
176 break;
177 case '2':
178 c += 2;
179 break;
180 case '3':
181 c += 3;
182 break;
183 case '4':
184 c += 4;
185 break;
186 case '5':
187 c += 5;
188 break;
189 case '6':
190 c += 6;
191 break;
192 case '7':
193 c += 7;
194 break;
195 case '8':
196 c += 8;
197 break;
198 case '9':
199 c += 9;
200 break;
201 case 'a':
202 c += 10;
203 break;
204 case 'b':
205 c += 11;
206 break;
207 case 'c':
208 c += 12;
209 break;
210 case 'd':
211 c += 13;
212 break;
213 case 'e':
214 c += 14;
215 break;
216 case 'f':
217 c += 15;
218 break;
219 default:
221 }
222
223 u.push_back(static_cast<char>(c));
224
225 // skip the two characters
226 i += 2;
227 }
228
229 return u;
230 }
231
236 bool
237 is_parameter_node(const boost::property_tree::ptree &p)
238 {
239 return static_cast<bool>(p.get_optional<std::string>("value"));
240 }
241
242
247 bool
248 is_alias_node(const boost::property_tree::ptree &p)
249 {
250 return static_cast<bool>(p.get_optional<std::string>("alias"));
251 }
252
259 std::string
260 collate_path_string(const char separator,
261 const std::vector<std::string> &subsection_path)
262 {
263 if (subsection_path.size() > 0)
264 {
265 std::string p = mangle(subsection_path[0]);
266 for (unsigned int i = 1; i < subsection_path.size(); ++i)
267 {
268 p += separator;
269 p += mangle(subsection_path[i]);
270 }
271 return p;
272 }
273 else
274 return "";
275 }
276
282 void
283 recursively_sort_parameters(
284 const char separator,
285 const std::vector<std::string> &target_subsection_path,
286 boost::property_tree::ptree &tree)
287 {
288 boost::property_tree::ptree &current_section =
289 tree.get_child(collate_path_string(separator, target_subsection_path));
290
291 // Custom comparator to ensure that the order of sorting is:
292 // - sorted parameters and aliases;
293 // - sorted subsections.
294 static auto compare =
295 [](const std::pair<std::string, boost::property_tree::ptree> &a,
296 const std::pair<std::string, boost::property_tree::ptree> &b) {
297 bool a_is_param =
298 (is_parameter_node(a.second) || is_alias_node(a.second));
299
300 bool b_is_param =
301 (is_parameter_node(b.second) || is_alias_node(b.second));
302
303 // If a is a parameter/alias and b is a subsection,
304 // a should go first, and vice-versa.
305 if (a_is_param && !b_is_param)
306 return true;
307
308 if (!a_is_param && b_is_param)
309 return false;
310
311 // Otherwise, compare a and b.
312 return a.first < b.first;
313 };
314
315 current_section.sort(compare);
316
317 // Now transverse subsections tree recursively.
318 for (auto &p : current_section)
319 {
320 if ((is_parameter_node(p.second) == false) &&
321 (is_alias_node(p.second) == false))
322 {
323 const std::string subsection = demangle(p.first);
324
325 std::vector<std::string> subsection_path = target_subsection_path;
326 subsection_path.emplace_back(subsection);
327
328 recursively_sort_parameters(separator, subsection_path, tree);
329 }
330 }
331 }
332
339 void
340 recursively_demangle(const boost::property_tree::ptree &tree_in,
341 boost::property_tree::ptree &tree_out,
342 const bool is_parameter_or_alias_node = false)
343 {
344 for (const auto &p : tree_in)
345 {
346 if (is_parameter_or_alias_node)
347 {
348 tree_out.put_child(p.first, p.second);
349 }
350 else
351 {
352 boost::property_tree::ptree temp;
353
354 if (const auto val = p.second.get_value_optional<std::string>())
355 temp.put_value<std::string>(*val);
356
357 recursively_demangle(p.second,
358 temp,
359 is_parameter_node(p.second) ||
360 is_alias_node(p.second));
361 tree_out.put_child(demangle(p.first), temp);
362 }
363 }
364 }
365
369 void
370 assert_validity_of_output_style(const ParameterHandler::OutputStyle style)
371 {
373 (((style & ParameterHandler::XML) != 0) +
374 ((style & ParameterHandler::JSON) != 0) +
375 ((style & ParameterHandler::PRM) != 0) +
376 ((style & ParameterHandler::Description) != 0) +
377 ((style & ParameterHandler::LaTeX) != 0)) == 1,
379 "You have chosen either no or multiple style formats. You can choose "
380 "between: PRM, Description, LaTeX, XML, JSON."));
381 }
382
383} // namespace
384
385
386
387std::string
389{
390 return collate_path_string(path_separator, subsection_path);
391}
392
393
394
395std::string
396ParameterHandler::get_current_full_path(const std::string &name) const
397{
398 std::string path = get_current_path();
399 if (path.empty() == false)
400 path += path_separator;
401
402 path += mangle(name);
403
404 return path;
405}
406
407
408
409std::string
411 const std::vector<std::string> &sub_path,
412 const std::string &name) const
413{
414 std::string path = get_current_path();
415 if (path.empty() == false)
416 path += path_separator;
417
418 if (sub_path.empty() == false)
419 path += collate_path_string(path_separator, sub_path) + path_separator;
420
421 path += mangle(name);
422
423 return path;
424}
425
426
427
428void
430 const std::string &filename,
431 const std::string &last_line,
432 const bool skip_undefined)
433{
434 AssertThrow(input.fail() == false, ExcIO());
435
436 // store subsections we are currently in
437 const std::vector<std::string> saved_path = subsection_path;
438
439 std::string input_line;
440 std::string fully_concatenated_line;
441 bool is_concatenated = false;
442 // Maintain both the current line number and the current logical line
443 // number, where the latter refers to the line number where (possibly) the
444 // current line continuation started.
445 unsigned int current_line_n = 0;
446 unsigned int current_logical_line_n = 0;
447
448 // define an action that tries to scan a line.
449 //
450 // if that fails, i.e., if scan_line throws
451 // an exception either because a parameter doesn't match its
452 // pattern or because an associated action throws an exception,
453 // then try to rewind the set of subsections to the same
454 // point where we were when the current function was called.
455 // this at least allows to read parameters from a predictable
456 // state, rather than leave the subsection stack in some
457 // unknown state.
458 //
459 // after unwinding the subsection stack, just re-throw the exception
460 auto scan_line_or_cleanup = [this,
461 &skip_undefined,
462 &saved_path](const std::string &line,
463 const std::string &filename,
464 const unsigned int line_number) {
465 try
466 {
467 scan_line(line, filename, line_number, skip_undefined);
468 }
469 catch (...)
470 {
471 while ((saved_path != subsection_path) && (subsection_path.size() > 0))
473
474 throw;
475 }
476 };
477
478
479 while (std::getline(input, input_line))
480 {
481 ++current_line_n;
482 if (!is_concatenated)
483 current_logical_line_n = current_line_n;
484 // Trim the whitespace at the ends of the line here instead of in
485 // scan_line. This makes the continuation line logic a lot simpler.
486 input_line = Utilities::trim(input_line);
487
488 // If we see the line which is the same as @p last_line ,
489 // terminate the parsing.
490 if (last_line.size() != 0 && input_line == last_line)
491 break;
492
493 // Check whether or not the current line should be joined with the next
494 // line before calling scan_line.
495 if (input_line.size() != 0 &&
496 input_line.find_last_of('\\') == input_line.size() - 1)
497 {
498 input_line.erase(input_line.size() - 1); // remove the last '\'
499 is_concatenated = true;
500
501 fully_concatenated_line += input_line;
502 }
503 // If the previous line ended in a '\' but the current did not, then we
504 // should proceed to scan_line.
505 else if (is_concatenated)
506 {
507 fully_concatenated_line += input_line;
508 is_concatenated = false;
509 }
510 // Finally, if neither the previous nor current lines are continuations,
511 // then the current input line is entirely concatenated.
512 else
513 {
514 fully_concatenated_line = input_line;
515 }
516
517 if (!is_concatenated)
518 {
519 scan_line_or_cleanup(fully_concatenated_line,
520 filename,
521 current_logical_line_n);
522
523 fully_concatenated_line.clear();
524 }
525 }
526
527 // While it does not make much sense for anyone to actually do this, allow
528 // the last line to end in a backslash. To do so, we need to parse
529 // whatever was left in the stash of concatenated lines
530 if (is_concatenated)
531 scan_line_or_cleanup(fully_concatenated_line, filename, current_line_n);
532
533 if (saved_path != subsection_path)
534 {
535 std::stringstream paths_message;
536 if (saved_path.size() > 0)
537 {
538 paths_message << "Path before loading input:\n";
539 for (unsigned int i = 0; i < subsection_path.size(); ++i)
540 {
541 paths_message << std::setw(i * 2 + 4) << " "
542 << "subsection " << saved_path[i] << '\n';
543 }
544 paths_message << "Current path:\n";
545 for (unsigned int i = 0; i < subsection_path.size(); ++i)
546 {
547 paths_message << std::setw(i * 2 + 4) << " "
548 << "subsection " << subsection_path[i]
549 << (i == subsection_path.size() - 1 ? "" : "\n");
550 }
551 }
552 // restore subsection we started with before throwing the exception:
553 subsection_path = saved_path;
554 AssertThrow(false,
555 ExcUnbalancedSubsections(filename, paths_message.str()));
556 }
557}
558
559
560
561void
562ParameterHandler::parse_input(const std::string &filename,
563 const std::string &last_line,
564 const bool skip_undefined,
565 const bool assert_mandatory_entries_are_found)
566{
567 std::ifstream is(filename);
568 AssertThrow(is, PathSearch::ExcFileNotFound(filename, "ParameterHandler"));
569
570 std::string file_ending = filename.substr(filename.find_last_of('.') + 1);
571 boost::algorithm::to_lower(file_ending);
572 if (file_ending == "prm")
573 parse_input(is, filename, last_line, skip_undefined);
574 else if (file_ending == "xml")
575 parse_input_from_xml(is, skip_undefined);
576 else if (file_ending == "json")
577 parse_input_from_json(is, skip_undefined);
578 else
579 AssertThrow(false,
580 ExcMessage("Unknown input file name extension. Supported types "
581 "are .prm, .xml, and .json."));
582
583 if (assert_mandatory_entries_are_found)
585}
586
587
588
589void
591 const std::string &last_line,
592 const bool skip_undefined)
593{
594 std::istringstream input_stream(s);
595 parse_input(input_stream, "input string", last_line, skip_undefined);
596}
597
598
599
600namespace
601{
602 // Recursively go through the 'source' tree and see if we can find
603 // corresponding entries in the 'destination' tree. If not, error out
604 // (i.e. we have just read an XML file that has entries that weren't
605 // declared in the ParameterHandler object); if so, copy the value of these
606 // nodes into the destination object
607 void
608 read_xml_recursively(
609 const boost::property_tree::ptree &source,
610 const std::string &current_path,
611 const char path_separator,
612 const std::vector<std::unique_ptr<const Patterns::PatternBase>> &patterns,
613 const bool skip_undefined,
614 ParameterHandler &prm)
615 {
616 for (const auto &p : source)
617 {
618 // a sub-tree must either be a parameter node or a subsection
619 if (p.second.empty())
620 {
621 // set the found parameter in the destination argument
622 if (skip_undefined)
623 {
624 try
625 {
626 prm.set(demangle(p.first), p.second.data());
627 }
629 {
630 // ignore undeclared entry assert
631 }
632 }
633 else
634 prm.set(demangle(p.first), p.second.data());
635 }
636 else if (p.second.get_optional<std::string>("value"))
637 {
638 // set the found parameter in the destination argument
639 if (skip_undefined)
640 {
641 try
642 {
643 prm.set(demangle(p.first),
644 p.second.get<std::string>("value"));
645 }
647 {
648 // ignore undeclared entry assert
649 }
650 }
651 else
652 prm.set(demangle(p.first), p.second.get<std::string>("value"));
653
654 // this node might have sub-nodes in addition to "value", such as
655 // "default_value", "documentation", etc. we might at some point
656 // in the future want to make sure that if they exist that they
657 // match the ones in the 'destination' tree
658 }
659 else if (p.second.get_optional<std::string>("alias"))
660 {
661 // it is an alias node. alias nodes are static and there is
662 // nothing to do here (but the same applies as mentioned in the
663 // comment above about the static nodes inside parameter nodes
664 }
665 else
666 {
667 // it must be a subsection
668 prm.enter_subsection(demangle(p.first));
669 read_xml_recursively(p.second,
670 (current_path.empty() ?
671 p.first :
672 current_path + path_separator + p.first),
673 path_separator,
674 patterns,
675 skip_undefined,
676 prm);
677 prm.leave_subsection();
678 }
679 }
680 }
681
682 // Recursively go through the @p source tree and collapse the nodes of the
683 // format:
684 //
685 //"key":
686 // {
687 // "value" : "val",
688 // "default_value" : "...",
689 // "documentation" : "...",
690 // "pattern" : "...",
691 // "pattern_description": "..."
692 // },
693 //
694 // to
695 //
696 // "key" : "val";
697 //
698 // As an example a JSON file is shown. However, this function also works for
699 // XML since both formats are build around the same BOOST data structures.
700 //
701 // This function is strongly based on read_xml_recursively().
702 void
703 recursively_compress_tree(boost::property_tree::ptree &source)
704 {
705 for (auto &p : source)
706 {
707 if (p.second.get_optional<std::string>("value"))
708 {
709 // save the value in a temporal variable
710 const auto temp = p.second.get<std::string>("value");
711 // clear node (children and value)
712 p.second.clear();
713 // set the correct value
714 p.second.put_value<std::string>(temp);
715 }
716 else if (p.second.get_optional<std::string>("alias"))
717 {
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 }
1054
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 {
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 {
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 {
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 {
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.empty())
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.empty(),
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);
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.empty(),
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
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
2366
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
constexpr void clear()
#define DEAL_II_NAMESPACE_OPEN
Definition config.h:502
#define DEAL_II_NAMESPACE_CLOSE
Definition config.h:503
Point< 2 > first
Definition grid_out.cc:4613
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()
#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)
#define DEAL_II_ASSERT_UNREACHABLE()
#define DEAL_II_NOT_IMPLEMENTED()
spacedim const Point< spacedim > & p
Definition grid_tools.h:981
std::enable_if_t< std::is_fundamental_v< T >, std::size_t > memory_consumption(const T &t)
std::string escape(const std::string &input, const PatternBase::OutputStyle style)
Definition patterns.cc:52
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:701
std::string replace_in_string(const std::string &input, const std::string &from, const std::string &to)
Definition utilities.cc:509
std::vector< std::string > break_text_into_lines(const std::string &original_text, const unsigned int width, const char delimiter=' ')
Definition utilities.cc:755
std::string int_to_string(const unsigned int value, const unsigned int digits=numbers::invalid_unsigned_int)
Definition utilities.cc:470
bool match_at_string_start(const std::string &name, const std::string &pattern)
Definition utilities.cc:832
double string_to_double(const std::string &s)
Definition utilities.cc:653
std::string trim(const std::string &input)
Definition utilities.cc:528
int string_to_int(const std::string &s)
Definition utilities.cc:605
::VectorizedArray< Number, width > max(const ::VectorizedArray< Number, width > &, const ::VectorizedArray< Number, width > &)