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