parametersystem.hh
Go to the documentation of this file.
1// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2// vi: set et ts=4 sw=4 sts=4:
3/*
4 This file is part of the Open Porous Media project (OPM).
5
6 OPM is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 2 of the License, or
9 (at your option) any later version.
10
11 OPM is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with OPM. If not, see <http://www.gnu.org/licenses/>.
18
19 Consult the COPYING file in the top-level source directory of this
20 module for the precise wording of the license and the list of
21 copyright holders.
22*/
32#ifndef OPM_PARAMETER_SYSTEM_HH
33#define OPM_PARAMETER_SYSTEM_HH
34
35#if HAVE_QUAD
36#include <opm/material/common/quad.hpp>
37#endif // HAVE_QUAD
38
39#include <dune/common/classname.hh>
40#include <dune/common/parametertree.hh>
41
42#include <charconv>
43#include <cstdlib>
44#include <fstream>
45#include <iostream>
46#include <list>
47#include <map>
48#include <set>
49#include <sstream>
50#include <stdexcept>
51#include <string>
52#include <tuple>
53#include <type_traits>
54
55#include <unistd.h>
56#include <sys/ioctl.h>
57
58namespace Opm::Parameters {
59
60namespace detail {
61
62template <typename, class = void>
63struct has_name : public std::false_type {};
64
65template <typename T>
66struct has_name<T, std::void_t<decltype(std::declval<T>().name)>>
67: public std::true_type {};
68
70template<class Parameter>
72{
73 if constexpr (has_name<Parameter>::value) {
74 return Parameter::name;
75 } else {
76 std::string paramName = Dune::className<Parameter>();
77 paramName.replace(0, std::strlen("Opm::Parameters::"), "");
78 const auto pos = paramName.find_first_of('<');
79 if (pos != std::string::npos) {
80 paramName.erase(pos);
81 }
82 return paramName;
83 }
84}
85
86}
87
89{
90 std::string paramName;
91 std::string paramTypeName;
92 std::string typeTagName;
93 std::string usageString;
94 std::string defaultValue;
96
97 bool operator==(const ParamInfo& other) const
98 {
99 return other.paramName == paramName
100 && other.paramTypeName == paramTypeName
101 && other.typeTagName == typeTagName
102 && other.usageString == usageString;
103 }
104};
105
121template <class Param>
122auto Get(bool errorIfNotRegistered = true);
123
138template <class Param>
139auto SetDefault(decltype(Param::value) new_value);
140
142{
143public:
145 {}
146 virtual void retrieve() = 0;
147};
148
149template <class Param>
151{
152public:
153 void retrieve() override
154 {
155 // retrieve the parameter once to make sure that its value does
156 // not contain a syntax error.
157 std::ignore = Get<Param>(/*errorIfNotRegistered=*/true);
158 }
159};
160
162{
163 using type = Dune::ParameterTree;
164
165 static Dune::ParameterTree& tree()
166 { return *storage_().tree; }
167
168 static std::map<std::string, ParamInfo>& mutableRegistry()
169 { return storage_().registry; }
170
171 static const std::map<std::string, ParamInfo>& registry()
172 { return storage_().registry; }
173
174 static std::list<std::unique_ptr<ParamRegFinalizerBase_>> &registrationFinalizers()
175 { return storage_().finalizers; }
176
177 static bool& registrationOpen()
178 { return storage_().registrationOpen; }
179
180 static void clear()
181 {
182 storage_().tree = std::make_unique<Dune::ParameterTree>();
183 storage_().finalizers.clear();
184 storage_().registrationOpen = true;
185 storage_().registry.clear();
186 }
187
188private:
189 // this is not pretty, but handling these attributes as static variables inside
190 // member functions of the ParameterMetaData property class triggers a bug in clang
191 // 3.5's address sanitizer which causes these variables to be initialized multiple
192 // times...
193 struct Storage_
194 {
195 Storage_()
196 {
197 tree = std::make_unique<Dune::ParameterTree>();
198 registrationOpen = true;
199 }
200
201 std::unique_ptr<Dune::ParameterTree> tree;
202 std::map<std::string, ParamInfo> registry;
203 std::list<std::unique_ptr<ParamRegFinalizerBase_>> finalizers;
204 bool registrationOpen;
205 };
206
207 static Storage_& storage_()
208 {
209 static Storage_ obj;
210 return obj;
211 }
212};
213
214// function prototype declarations
215void printParamUsage_(std::ostream& os, const ParamInfo& paramInfo);
216void getFlattenedKeyList_(std::list<std::string>& dest,
217 const Dune::ParameterTree& tree,
218 const std::string& prefix = "");
219
220inline std::string breakLines_(const std::string& msg,
221 int indentWidth,
222 int maxWidth)
223{
224 std::string result;
225 int startInPos = 0;
226 int inPos = 0;
227 int lastBreakPos = 0;
228 int ttyPos = 0;
229 for (; inPos < int(msg.size()); ++ inPos, ++ ttyPos) {
230 if (msg[inPos] == '\n') {
231 result += msg.substr(startInPos, inPos - startInPos + 1);
232 startInPos = inPos + 1;
233 lastBreakPos = startInPos + 1;
234
235 // we need to use -1 here because ttyPos is incremented after the loop body
236 ttyPos = -1;
237 continue;
238 }
239
240 if (std::isspace(msg[inPos]))
241 lastBreakPos = inPos;
242
243 if (ttyPos >= maxWidth) {
244 if (lastBreakPos > startInPos) {
245 result += msg.substr(startInPos, lastBreakPos - startInPos);
246 startInPos = lastBreakPos + 1;
247 lastBreakPos = startInPos;
248 inPos = startInPos;
249 }
250 else {
251 result += msg.substr(startInPos, inPos - startInPos);
252 startInPos = inPos;
253 lastBreakPos = startInPos;
254 inPos = startInPos;
255 }
256
257 result += "\n";
258 for (int i = 0; i < indentWidth; ++i)
259 result += " ";
260 ttyPos = indentWidth;
261 }
262 }
263
264 result += msg.substr(startInPos);
265
266 return result;
267}
268
269inline int getTtyWidth_()
270{
271 int ttyWidth = 10*1000; // effectively do not break lines at all.
272 if (isatty(STDOUT_FILENO)) {
273#if defined TIOCGWINSZ
274 // This is a bit too linux specific, IMO. let's do it anyway
275 struct winsize ttySize;
276 ioctl(STDOUT_FILENO, TIOCGWINSZ, &ttySize);
277 ttyWidth = std::max<int>(80, ttySize.ws_col);
278#else
279 // default for systems that do not implement the TIOCGWINSZ ioctl
280 ttyWidth = 100;
281#endif
282 }
283
284 return ttyWidth;
285}
286
287inline void printParamUsage_(std::ostream& os, const ParamInfo& paramInfo)
288{
289 std::string paramMessage, paramType, paramDescription;
290
291 int ttyWidth = getTtyWidth_();
292
293 // convert the CamelCase name to a command line --parameter-name.
294 std::string cmdLineName = "-";
295 const std::string camelCaseName = paramInfo.paramName;
296 for (unsigned i = 0; i < camelCaseName.size(); ++i) {
297 if (isupper(camelCaseName[i]))
298 cmdLineName += "-";
299 cmdLineName += static_cast<char>(std::tolower(camelCaseName[i]));
300 }
301
302 // assemble the printed output
303 paramMessage = " ";
304 paramMessage += cmdLineName;
305
306 // add the =VALUE_TYPE part
307 bool isString = false;
308 if (paramInfo.paramTypeName == Dune::className<std::string>()
309 || paramInfo.paramTypeName == "const char *")
310 {
311 paramMessage += "=STRING";
312 isString = true;
313 }
314 else if (paramInfo.paramTypeName == Dune::className<float>()
315 || paramInfo.paramTypeName == Dune::className<double>()
316 || paramInfo.paramTypeName == Dune::className<long double>()
317#if HAVE_QUAD
318 || paramInfo.paramTypeName == Dune::className<quad>()
319#endif // HAVE_QUAD
320 )
321 paramMessage += "=SCALAR";
322 else if (paramInfo.paramTypeName == Dune::className<int>()
323 || paramInfo.paramTypeName == Dune::className<unsigned int>()
324 || paramInfo.paramTypeName == Dune::className<short>()
325 || paramInfo.paramTypeName == Dune::className<unsigned short>())
326 paramMessage += "=INTEGER";
327 else if (paramInfo.paramTypeName == Dune::className<bool>())
328 paramMessage += "=BOOLEAN";
329 else if (paramInfo.paramTypeName.empty()) {
330 // the parameter is a flag. Do nothing!
331 }
332 else {
333 // unknown type
334 paramMessage += "=VALUE";
335 }
336
337 // fill up the up help string to the 50th character
338 paramMessage += " ";
339 while (paramMessage.size() < 50)
340 paramMessage += " ";
341
342
343 // append the parameter usage string.
344 paramMessage += paramInfo.usageString;
345
346 // add the default value
347 if (!paramInfo.paramTypeName.empty()) {
348 if (paramMessage.back() != '.')
349 paramMessage += '.';
350 paramMessage += " Default: ";
351 if (paramInfo.paramTypeName == "bool") {
352 if (paramInfo.defaultValue == "0")
353 paramMessage += "false";
354 else
355 paramMessage += "true";
356 }
357 else if (isString) {
358 paramMessage += "\"";
359 paramMessage += paramInfo.defaultValue;
360 paramMessage += "\"";
361 }
362 else
363 paramMessage += paramInfo.defaultValue;
364 }
365
366 paramMessage = breakLines_(paramMessage, /*indent=*/52, ttyWidth);
367 paramMessage += "\n";
368
369 // print everything
370 os << paramMessage;
371}
372
373inline void getFlattenedKeyList_(std::list<std::string>& dest,
374 const Dune::ParameterTree& tree,
375 const std::string& prefix)
376{
377 // add the keys of the current sub-structure
378 for (const auto& valueKey : tree.getValueKeys()) {
379 std::string newKey(prefix + valueKey);
380 dest.push_back(newKey);
381 }
382
383 // recursively add all substructure keys
384 for (const auto& subKey : tree.getSubKeys()) {
385 std::string newPrefix(prefix + subKey + '.');
386 getFlattenedKeyList_(dest, tree.sub(subKey), newPrefix);
387 }
388}
389
390// print the values of a list of parameters
391inline void printParamList_(std::ostream& os,
392 const std::list<std::string>& keyList,
393 bool printDefaults = false)
394{
395 const Dune::ParameterTree& tree = MetaData::tree();
396
397 for (const auto& key : keyList) {
398 const auto& paramInfo = MetaData::registry().at(key);
399 const std::string& defaultValue = paramInfo.defaultValue;
400 std::string value = defaultValue;
401 if (tree.hasKey(key))
402 value = tree.get(key, "");
403 os << key << "=\"" << value << "\"";
404 if (printDefaults)
405 os << " # default: \"" << defaultValue << "\"";
406 os << "\n";
407 }
408}
409
411
421inline void printUsage(const std::string& helpPreamble,
422 const std::string& errorMsg = "",
423 std::ostream& os = std::cerr,
424 const bool showAll = false)
425{
426 if (!errorMsg.empty()) {
427 os << errorMsg << "\n\n";
428 }
429
430 os << breakLines_(helpPreamble, /*indent=*/2, /*maxWidth=*/getTtyWidth_());
431 os << "\n";
432
433 os << "Recognized options:\n";
434
435 if (!helpPreamble.empty()) {
436 ParamInfo pInfo;
437 pInfo.paramName = "h,--help";
438 pInfo.usageString = "Print this help message and exit";
439 printParamUsage_(os, pInfo);
440 pInfo.paramName = "-help-all";
441 pInfo.usageString = "Print all parameters, including obsolete, hidden and deprecated ones.";
442 printParamUsage_(os, pInfo);
443 }
444
445 for (const auto& param : MetaData::registry()) {
446 if (showAll || !param.second.isHidden)
447 printParamUsage_(os, param.second);
448 }
449}
450
452inline int noPositionalParameters_(std::set<std::string>&,
453 std::string& errorMsg,
454 int,
455 const char** argv,
456 int paramIdx,
457 int)
458{
459 errorMsg = std::string("Illegal parameter \"")+argv[paramIdx]+"\".";
460 return 0;
461}
462
464
465
466inline void removeLeadingSpace_(std::string& s)
467{
468 unsigned i;
469 for (i = 0; i < s.size(); ++ i)
470 if (!std::isspace(s[i]))
471 break;
472 s = s.substr(i);
473}
474
475inline std::string transformKey_(const std::string& s,
476 bool capitalizeFirstLetter = true,
477 const std::string& errorPrefix = "")
478{
479 std::string result;
480
481 if (s.empty())
482 throw std::runtime_error(errorPrefix+"Empty parameter names are invalid");
483
484 if (!std::isalpha(s[0]))
485 throw std::runtime_error(errorPrefix+"Parameter name '" + s + "' is invalid: First character must be a letter");
486
487 if (capitalizeFirstLetter)
488 result += static_cast<char>(std::toupper(s[0]));
489 else
490 result += s[0];
491
492 for (unsigned i = 1; i < s.size(); ++i) {
493 if (s[i] == '-') {
494 ++ i;
495 if (s.size() <= i || !std::isalpha(s[i]))
496 throw std::runtime_error(errorPrefix+"Invalid parameter name '" + s + "'");
497 result += static_cast<char>(std::toupper(s[i]));
498 }
499 else if (!std::isalnum(s[i]))
500 throw std::runtime_error(errorPrefix+"Invalid parameter name '" + s + "'");
501 else
502 result += s[i];
503 }
504
505 return result;
506}
507
508inline std::string parseKey_(std::string& s)
509{
510 unsigned i;
511 for (i = 0; i < s.size(); ++ i)
512 if (std::isspace(s[i]) || s[i] == '=')
513 break;
514
515 std::string ret = s.substr(0, i);
516 s = s.substr(i);
517 return ret;
518}
519
520// parse a quoted string
521inline std::string parseQuotedValue_(std::string& s, const std::string& errorPrefix)
522{
523 if (s.empty() || s[0] != '"')
524 throw std::runtime_error(errorPrefix+"Expected quoted string");
525
526 std::string result;
527 unsigned i = 1;
528 for (; i < s.size(); ++i) {
529 // handle escape characters
530 if (s[i] == '\\') {
531 ++ i;
532 if (s.size() <= i)
533 throw std::runtime_error(errorPrefix+"Unexpected end of quoted string");
534
535 if (s[i] == 'n')
536 result += '\n';
537 else if (s[i] == 'r')
538 result += '\r';
539 else if (s[i] == 't')
540 result += '\t';
541 else if (s[i] == '"')
542 result += '"';
543 else if (s[i] == '\\')
544 result += '\\';
545 else
546 throw std::runtime_error(errorPrefix+"Unknown escape character '\\" + s[i] + "'");
547 }
548 else if (s[i] == '"')
549 break;
550 else
551 result += s[i];
552 }
553
554 s = s.substr(i+1);
555 return result;
556}
557
558inline std::string parseUnquotedValue_(std::string& s, const std::string&)
559{
560 unsigned i;
561 for (i = 0; i < s.size(); ++ i)
562 if (std::isspace(s[i]))
563 break;
564
565 std::string ret = s.substr(0, i);
566 s = s.substr(i);
567 return ret;
568}
569
586template <class PositionalArgumentCallback>
587std::string parseCommandLineOptions(int argc,
588 const char **argv,
589 const std::string& helpPreamble = "",
590 const PositionalArgumentCallback& posArgCallback = noPositionalParameters_)
591{
592 // handle the "--help" parameter
593 if (!helpPreamble.empty()) {
594 for (int i = 1; i < argc; ++i) {
595 if (std::string("-h") == argv[i]
596 || std::string("--help") == argv[i]) {
597 printUsage(helpPreamble, /*errorMsg=*/"", std::cout);
598 return "Help called";
599 }
600 if (std::string("--help-all") == argv[i]) {
601 printUsage(helpPreamble, /*errorMsg=*/"", std::cout, true);
602 return "Help called";
603 }
604 }
605 }
606
607 std::set<std::string> seenKeys;
608 int numPositionalParams = 0;
609 for (int i = 1; i < argc; ++i) {
610 // All non-positional command line options need to start with '-'
611 if (strlen(argv[i]) < 4
612 || argv[i][0] != '-'
613 || argv[i][1] != '-')
614 {
615 std::string errorMsg;
616 int numHandled = posArgCallback(seenKeys, errorMsg, argc, argv,
617 i, numPositionalParams);
618
619 if (numHandled < 1) {
620 std::ostringstream oss;
621
622 if (!helpPreamble.empty())
623 printUsage(helpPreamble, errorMsg, std::cerr);
624
625 return errorMsg;
626 }
627 else {
628 ++ numPositionalParams;
629 i += numHandled - 1;
630 continue;
631 }
632 }
633
634 std::string paramName, paramValue;
635
636 // read a --my-opt=abc option. This gets transformed
637 // into the parameter "MyOpt" with the value being
638 // "abc"
639
640 // There is nothing after the '-'
641 if (argv[i][2] == 0 || !std::isalpha(argv[i][2])) {
642 std::ostringstream oss;
643 oss << "Parameter name of argument " << i
644 << " ('" << argv[i] << "') "
645 << "is invalid because it does not start with a letter.";
646
647 if (!helpPreamble.empty())
648 printUsage(helpPreamble, oss.str(), std::cerr);
649
650 return oss.str();
651 }
652
653 // copy everything after the "--" into a separate string
654 std::string s(argv[i] + 2);
655
656 // parse argument
657 paramName = transformKey_(parseKey_(s), /*capitalizeFirst=*/true);
658 if (seenKeys.count(paramName) > 0) {
659 std::string msg =
660 std::string("Parameter '")+paramName+"' specified multiple times as a "
661 "command line parameter";
662
663 if (!helpPreamble.empty())
664 printUsage(helpPreamble, msg, std::cerr);
665 return msg;
666 }
667 seenKeys.insert(paramName);
668
669 if (s.empty() || s[0] != '=') {
670 std::string msg =
671 std::string("Parameter '")+paramName+"' is missing a value. "
672 +" Please use "+argv[i]+"=value.";
673
674 if (!helpPreamble.empty())
675 printUsage(helpPreamble, msg, std::cerr);
676 return msg;
677 }
678
679 paramValue = s.substr(1);
680
681 // Put the key=value pair into the parameter tree
682 MetaData::tree()[paramName] = paramValue;
683 }
684 return "";
685}
686
693inline void parseParameterFile(const std::string& fileName, bool overwrite = true)
694{
695 std::set<std::string> seenKeys;
696 std::ifstream ifs(fileName);
697 unsigned curLineNum = 0;
698 while (ifs) {
699 // string and file processing in c++ is quite blunt!
700 std::string curLine;
701 std::getline(ifs, curLine);
702 curLineNum += 1;
703 std::string errorPrefix = fileName+":"+std::to_string(curLineNum)+": ";
704
705 // strip leading white space
706 removeLeadingSpace_(curLine);
707
708 // ignore empty and comment lines
709 if (curLine.empty() || curLine[0] == '#' || curLine[0] == ';')
710 continue;
711
712 // TODO (?): support for parameter groups.
713
714 // find the "key" of the key=value pair
715 std::string key = parseKey_(curLine);
716 std::string canonicalKey = transformKey_(key, /*capitalizeFirst=*/true, errorPrefix);
717
718 if (seenKeys.count(canonicalKey) > 0)
719 throw std::runtime_error(errorPrefix+"Parameter '"+canonicalKey+"' seen multiple times in the same file");
720 seenKeys.insert(canonicalKey);
721
722 // deal with the equals sign
723 removeLeadingSpace_(curLine);
724 if (curLine.empty() || curLine[0] != '=')
725 std::runtime_error(errorPrefix+"Syntax error, expecting 'key=value'");
726
727 curLine = curLine.substr(1);
728 removeLeadingSpace_(curLine);
729
730 if (curLine.empty() || curLine[0] == '#' || curLine[0] == ';')
731 std::runtime_error(errorPrefix+"Syntax error, expecting 'key=value'");
732
733 // get the value
734 std::string value;
735 if (curLine[0] == '"')
736 value = parseQuotedValue_(curLine, errorPrefix);
737 else
738 value = parseUnquotedValue_(curLine, errorPrefix);
739
740 // ignore trailing comments
741 removeLeadingSpace_(curLine);
742 if (!curLine.empty() && curLine[0] != '#' && curLine[0] != ';')
743 std::runtime_error(errorPrefix+"Syntax error, expecting 'key=value'");
744
745 // all went well, add the parameter to the database object
746 if (overwrite || !MetaData::tree().hasKey(canonicalKey)) {
747 MetaData::tree()[canonicalKey] = value;
748 }
749 }
750}
751
758inline void printValues(std::ostream& os = std::cout)
759{
760 std::list<std::string> runTimeAllKeyList;
761 std::list<std::string> runTimeKeyList;
762 std::list<std::string> unknownKeyList;
763
764 getFlattenedKeyList_(runTimeAllKeyList, MetaData::tree());
765 for (const auto& key : runTimeAllKeyList) {
766 if (MetaData::registry().find(key) == MetaData::registry().end()) {
767 // key was not registered by the program!
768 unknownKeyList.push_back(key);
769 }
770 else {
771 // the key was specified at run-time
772 runTimeKeyList.push_back(key);
773 }
774 }
775
776 // loop over all registered parameters
777 std::list<std::string> compileTimeKeyList;
778 for (const auto& reg : MetaData::registry()) {
779 // check whether the key was specified at run-time
780 if (MetaData::tree().hasKey(reg.first)) {
781 continue;
782 } else {
783 compileTimeKeyList.push_back(reg.first);
784 }
785 }
786
787 // report the values of all registered (and unregistered)
788 // parameters
789 if (runTimeKeyList.size() > 0) {
790 os << "# [known parameters which were specified at run-time]\n";
791 printParamList_(os, runTimeKeyList, /*printDefaults=*/true);
792 }
793
794 if (compileTimeKeyList.size() > 0) {
795 os << "# [parameters which were specified at compile-time]\n";
796 printParamList_(os, compileTimeKeyList, /*printDefaults=*/false);
797 }
798
799 if (unknownKeyList.size() > 0) {
800 os << "# [unused run-time specified parameters]\n";
801 for (const auto& unused : unknownKeyList) {
802 os << unused << "=\"" << MetaData::tree().get(unused, "") << "\"\n" << std::flush;
803 }
804 }
805}
806
815inline bool printUnused(std::ostream& os = std::cout)
816{
817 std::list<std::string> runTimeAllKeyList;
818 std::list<std::string> unknownKeyList;
819
820 getFlattenedKeyList_(runTimeAllKeyList, MetaData::tree());
821 for (const auto& key : runTimeAllKeyList) {
822 if (MetaData::registry().find(key) == MetaData::registry().end()) {
823 // key was not registered by the program!
824 unknownKeyList.push_back(key);
825 }
826 }
827
828 if (unknownKeyList.size() > 0) {
829 os << "# [unused run-time specified parameters]\n";
830 for (const auto& unused : unknownKeyList) {
831 os << unused << "=\""
832 << MetaData::tree().get(unused, "") << "\"\n" << std::flush;
833 }
834 return true;
835 }
836 return false;
837}
838
839template <class Param>
840auto Get(bool errorIfNotRegistered)
841{
842 const std::string paramName = detail::getParamName<Param>();
843 if (errorIfNotRegistered) {
845 throw std::runtime_error("Parameters can only retrieved after _all_ of them have "
846 "been registered.");
847
848 if (MetaData::registry().find(paramName) == MetaData::registry().end()) {
849 throw std::runtime_error("Accessing parameter " + paramName
850 +" without prior registration is not allowed.");
851 }
852 }
853
854 using ParamType = std::conditional_t<std::is_same_v<decltype(Param::value),
855 const char* const>, std::string,
856 std::remove_const_t<decltype(Param::value)>>;
857 ParamType defaultValue = Param::value;
858
859 const std::string& defVal = MetaData::mutableRegistry()[paramName].defaultValue;
860 if constexpr (std::is_same_v<ParamType, std::string>) {
861 defaultValue = defVal;
862 }
863 else if constexpr (std::is_same_v<ParamType, bool>) {
864 defaultValue = defVal == "1";
865 }
866#if HAVE_QUAD
867 else if constexpr (std::is_same_v<ParamType, quad>) {
868 defaultValue = std::strtold(defVal.data(), nullptr);
869 }
870#endif
871#if !HAVE_FLOATING_POINT_FROM_CHARS
872 else if constexpr (std::is_floating_point_v<ParamType>) {
873 defaultValue = std::strtod(defVal.c_str(), nullptr);
874 }
875#endif // !HAVE_FLOATING_POINT_FROM_CHARS
876 else {
877 std::from_chars(defVal.data(), defVal.data() + defVal.size(), defaultValue);
878 }
879
880 // prefix the parameter name by the model's GroupName. E.g. If
881 // the model specifies its group name to be 'Stokes', in an
882 // INI file this would result in something like:
883 //
884 // [Stokes]
885 // NewtonWriteConvergence = true
886 // retrieve actual parameter from the parameter tree
887 return MetaData::tree().template get<ParamType>(paramName, defaultValue);
888}
889
890template <class Param>
891auto SetDefault(decltype(Param::value) new_value)
892{
893 const std::string paramName = detail::getParamName<Param>();
894 if (MetaData::registry().find(paramName) == MetaData::registry().end()) {
895 throw std::runtime_error("Accessing parameter " + paramName +
896 " without prior registration is not allowed.");
897 }
898 std::ostringstream oss;
899 oss << new_value;
900 MetaData::mutableRegistry()[paramName].defaultValue = oss.str();
901}
902
909template <class Container>
910void getLists(Container& usedParams, Container& unusedParams)
911{
912 usedParams.clear();
913 unusedParams.clear();
914
916 throw std::runtime_error("Parameter lists can only retrieved after _all_ of them have "
917 "been registered.");
918 }
919
920 // get all parameter keys
921 std::list<std::string> allKeysList;
922 getFlattenedKeyList_(allKeysList, MetaData::tree());
923
924 for (const auto& key : allKeysList) {
925 if (MetaData::registry().find(key) == MetaData::registry().end()) {
926 // key was not registered
927 unusedParams.emplace_back(key, MetaData::tree()[key]);
928 }
929 else {
930 // key was registered
931 usedParams.emplace_back(key, MetaData::tree()[key]);
932 }
933 }
934}
935
936inline void reset()
937{
939}
940
947template <class Param>
948bool IsSet(bool errorIfNotRegistered = true)
949{
950 const std::string paramName = detail::getParamName<Param>();
951
952 if (errorIfNotRegistered) {
954 throw std::runtime_error("Parameters can only checked after _all_ of them have "
955 "been registered.");
956 }
957
958 if (MetaData::registry().find(paramName) == MetaData::registry().end())
959 throw std::runtime_error("Accessing parameter " + std::string(paramName) +
960 " without prior registration is not allowed.");
961 }
962
963 // check whether the parameter is in the parameter tree
964 return MetaData::tree().hasKey(paramName);
965}
966
983template <class Param>
984void Register(const char* usageString)
985{
986 const std::string paramName = detail::getParamName<Param>();
988 throw std::logic_error("Parameter registration was already closed before "
989 "the parameter '" + paramName + "' was registered.");
990 }
991
992 const auto defaultValue = Param::value;
993 using ParamType = std::conditional_t<std::is_same_v<decltype(defaultValue),
994 const char* const>, std::string,
995 std::remove_const_t<decltype(defaultValue)>>;
997 std::make_unique<ParamRegFinalizer_<Param>>());
998
999 ParamInfo paramInfo;
1000 paramInfo.paramName = paramName;
1001 paramInfo.paramTypeName = Dune::className<ParamType>();
1002 paramInfo.usageString = usageString;
1003 std::ostringstream oss;
1004 oss << defaultValue;
1005 paramInfo.defaultValue = oss.str();
1006 paramInfo.isHidden = false;
1007 if (MetaData::registry().find(paramName) != MetaData::registry().end()) {
1008 // allow to register a parameter twice, but only if the
1009 // parameter name, type and usage string are exactly the same.
1010 if (MetaData::registry().at(paramName) == paramInfo) {
1011 return;
1012 }
1013 throw std::logic_error("Parameter " + paramName
1014 +" registered twice with non-matching characteristics.");
1015 }
1016
1017 MetaData::mutableRegistry()[paramName] = paramInfo;
1018}
1019
1020
1026template <class Param>
1027void Hide()
1028{
1029 const std::string paramName = detail::getParamName<Param>();
1031 throw std::logic_error("Parameter '" +paramName + "' declared as hidden"
1032 " when parameter registration was already closed.");
1033 }
1034
1035 auto paramInfoIt = MetaData::mutableRegistry().find(paramName);
1036 if (paramInfoIt == MetaData::mutableRegistry().end()) {
1037 throw std::logic_error("Tried to declare unknown parameter '"
1038 + paramName + "' hidden.");
1039 }
1040
1041 auto& paramInfo = paramInfoIt->second;
1042 paramInfo.isHidden = true;
1043}
1044
1052inline void endRegistration()
1053{
1055 throw std::logic_error("Parameter registration was already closed. It is only possible "
1056 "to close it once.");
1057 }
1058
1060
1061 // loop over all parameters and retrieve their values to make sure
1062 // that there is no syntax error
1063 for (const auto& param : MetaData::registrationFinalizers()) {
1064 param->retrieve();
1065 }
1067}
1069
1070} // namespace Opm::Parameters
1071
1072#endif // OPM_PARAMETER_SYSTEM_HH
Definition: parametersystem.hh:151
void retrieve() override
Definition: parametersystem.hh:153
Definition: parametersystem.hh:142
virtual ~ParamRegFinalizerBase_()
Definition: parametersystem.hh:144
bool printUnused(std::ostream &os=std::cout)
Print the list of unused run-time parameters.
Definition: parametersystem.hh:815
auto SetDefault(decltype(Param::value) new_value)
Set a runtime parameter.
Definition: parametersystem.hh:891
void printUsage(const std::string &helpPreamble, const std::string &errorMsg="", std::ostream &os=std::cerr, const bool showAll=false)
Print a usage message for all run-time parameters.
Definition: parametersystem.hh:421
void Register(const char *usageString)
Register a run-time parameter.
Definition: parametersystem.hh:984
void parseParameterFile(const std::string &fileName, bool overwrite=true)
Read the parameters from an INI-style file.
Definition: parametersystem.hh:693
std::string parseCommandLineOptions(int argc, const char **argv, const std::string &helpPreamble="", const PositionalArgumentCallback &posArgCallback=noPositionalParameters_)
Parse the parameters provided on the command line.
Definition: parametersystem.hh:587
void printValues(std::ostream &os=std::cout)
Print values of the run-time parameters.
Definition: parametersystem.hh:758
auto Get(bool errorIfNotRegistered=true)
Retrieve a runtime parameter.
Definition: parametersystem.hh:840
auto getParamName()
get the name data member of a parameter
Definition: parametersystem.hh:71
Definition: blackoilnewtonmethodparameters.hh:31
std::string parseUnquotedValue_(std::string &s, const std::string &)
Definition: parametersystem.hh:558
void removeLeadingSpace_(std::string &s)
Definition: parametersystem.hh:466
int getTtyWidth_()
Definition: parametersystem.hh:269
bool IsSet(bool errorIfNotRegistered=true)
Returns true if a parameter has been specified at runtime, false otherwise.
Definition: parametersystem.hh:948
void printParamList_(std::ostream &os, const std::list< std::string > &keyList, bool printDefaults=false)
Definition: parametersystem.hh:391
std::string parseQuotedValue_(std::string &s, const std::string &errorPrefix)
Definition: parametersystem.hh:521
void printParamUsage_(std::ostream &os, const ParamInfo &paramInfo)
Definition: parametersystem.hh:287
void endRegistration()
Indicate that all parameters are registered for a given type tag.
Definition: parametersystem.hh:1052
std::string breakLines_(const std::string &msg, int indentWidth, int maxWidth)
Definition: parametersystem.hh:220
void reset()
Definition: parametersystem.hh:936
std::string transformKey_(const std::string &s, bool capitalizeFirstLetter=true, const std::string &errorPrefix="")
Definition: parametersystem.hh:475
void Hide()
Indicate that a given parameter should not be mentioned in the help message.
Definition: parametersystem.hh:1027
void getLists(Container &usedParams, Container &unusedParams)
Retrieves the lists of parameters specified at runtime and their values.
Definition: parametersystem.hh:910
std::string parseKey_(std::string &s)
Definition: parametersystem.hh:508
void getFlattenedKeyList_(std::list< std::string > &dest, const Dune::ParameterTree &tree, const std::string &prefix="")
Definition: parametersystem.hh:373
Definition: parametersystem.hh:162
static std::map< std::string, ParamInfo > & mutableRegistry()
Definition: parametersystem.hh:168
static void clear()
Definition: parametersystem.hh:180
static Dune::ParameterTree & tree()
Definition: parametersystem.hh:165
static std::list< std::unique_ptr< ParamRegFinalizerBase_ > > & registrationFinalizers()
Definition: parametersystem.hh:174
Dune::ParameterTree type
Definition: parametersystem.hh:163
static const std::map< std::string, ParamInfo > & registry()
Definition: parametersystem.hh:171
static bool & registrationOpen()
Definition: parametersystem.hh:177
Definition: parametersystem.hh:89
bool operator==(const ParamInfo &other) const
Definition: parametersystem.hh:97
std::string usageString
Definition: parametersystem.hh:93
std::string typeTagName
Definition: parametersystem.hh:92
bool isHidden
Definition: parametersystem.hh:95
std::string paramTypeName
Definition: parametersystem.hh:91
std::string paramName
Definition: parametersystem.hh:90
std::string defaultValue
Definition: parametersystem.hh:94
Definition: parametersystem.hh:63