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