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