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