Loading [MathJax]/extensions/tex2jax.js
PISM, A Parallel Ice Sheet Model 2.2.2-d6b3a29ca committed by Constantine Khrulev on 2025-03-28
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
options.cc
Go to the documentation of this file.
1/* Copyright (C) 2014, 2015, 2016, 2017, 2018, 2020, 2021, 2023, 2024 PISM Authors
2 *
3 * This file is part of PISM.
4 *
5 * PISM is free software; you can redistribute it and/or modify it under the
6 * terms of the GNU General Public License as published by the Free Software
7 * Foundation; either version 3 of the License, or (at your option) any later
8 * version.
9 *
10 * PISM is distributed in the hope that it will be useful, but WITHOUT ANY
11 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
13 * details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with PISM; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20#include <cstring> // memset
21
22#include <petscsys.h>
23
24#include "pism/util/error_handling.hh"
25#include "pism/util/Logger.hh"
26#include "pism/util/Units.hh"
27#include "pism/util/pism_utilities.hh"
28#include "pism/util/pism_options.hh"
29
30namespace pism {
31namespace options {
32
33String::String(const std::string& option,
34 const std::string& description) {
35 int errcode = process(option, description, "", DONT_ALLOW_EMPTY);
36 if (errcode != 0) {
37 throw RuntimeError::formatted(PISM_ERROR_LOCATION, "failed to process option %s", option.c_str());
38 }
39}
40
41String::String(const std::string& option,
42 const std::string& description,
43 const std::string& default_value,
44 ArgumentFlag argument_flag) {
45 int errcode = process(option, description, default_value, argument_flag);
46 if (errcode != 0) {
47 throw RuntimeError::formatted(PISM_ERROR_LOCATION, "failed to process option %s", option.c_str());
48 }
49}
50
51static const int TEMPORARY_STRING_LENGTH = 32768;
52
53int String::process(const std::string& option,
54 const std::string& description,
55 const std::string& default_value,
56 ArgumentFlag argument_flag) {
57
58 char string[TEMPORARY_STRING_LENGTH];
59 memset(string, 0, TEMPORARY_STRING_LENGTH);
60
61 PetscBool flag = PETSC_FALSE;
62
63 PetscErrorCode ierr;
64 ierr = PetscOptionsGetString(NULL, // default option database
65 NULL, // no prefix
66 option.c_str(),
67 string,
69 &flag);
70 PISM_CHK(ierr, "PetscOptionsGetString");
71
72 std::string result = string;
73
74 if (flag == PETSC_TRUE) {
75 if (result.empty()) {
76 if (argument_flag == ALLOW_EMPTY) {
77 this->set("", true);
78 } else {
80 "command line option '%s'\n"
81 "(%s)\n"
82 "requires an argument.",
83 option.c_str(), description.c_str());
84 }
85 } else {
86 this->set(result, true);
87 }
88 } else {
89 this->set(default_value, false);
90 }
91
92 return 0;
93}
94
95Keyword::Keyword(const std::string& option,
96 const std::string& description,
97 const std::string& choices,
98 const std::string& default_value) {
99
100 if (choices.empty()) {
101 throw RuntimeError::formatted(PISM_ERROR_LOCATION, "empty choices argument");
102 }
103
104 std::string list = "[" + choices + "]";
105 std::string long_description = description + " Choose one of " + list;
106
107 String input(option, long_description, default_value, DONT_ALLOW_EMPTY);
108
109 // use the default value if the option was not set
110 if (not input.is_set()) {
111 this->set(input, input.is_set());
112 return;
113 }
114
115 std::string word = input;
116 // find ":" and discard everything that goes after
117 size_t n = word.find(':');
118 if (n != std::string::npos) {
119 word.resize(n);
120 }
121
122 // transform a comma-separated list of choices into a set of
123 // choices:
124 auto choices_set = set_split(choices, ',');
125
126 // use the choice if it is valid and stop if it is not
127 if (choices_set.find(word) != choices_set.end()) {
128 this->set(word, true);
129 } else {
131 "invalid %s argument: '%s'. Please choose one of %s.\n",
132 option.c_str(), word.c_str(), list.c_str());
133 }
134}
135
136Integer::Integer(const std::string& option,
137 const std::string& description,
138 int default_value) {
139
140 String input(option, description,
141 pism::printf("%d", default_value),
143
144 if (input.is_set()) {
145 long int result = 0;
146 try {
147 result = parse_integer(input);
148 } catch (RuntimeError &e) {
149 e.add_context("processing command-line option '%s %s'",
150 option.c_str(), input->c_str());
151 throw;
152 }
153 this->set(static_cast<int>(result), true);
154 } else {
155 this->set(static_cast<int>(default_value), false);
156 }
157}
158
159Real::Real(std::shared_ptr<units::System> system,
160 const std::string& option,
161 const std::string& description,
162 const std::string& units,
163 double default_value) {
164
165 std::string buffer = pism::printf("%f", default_value);
166
167 String input(option, description, buffer, DONT_ALLOW_EMPTY);
168
169 if (input.is_set()) {
170 char *endptr = NULL;
171 double result = strtod(input->c_str(), &endptr);
172 if (*endptr != '\0') {
173 // assume that "input" contains units and try converting to "units":
174 try {
175 result = units::convert(system, 1.0, input.value(), units);
176 } catch (RuntimeError &e) {
177 e.add_context("trying to convert '%s' to '%s'",
178 input->c_str(), units.c_str());
179 e.add_context("processing the command-line option %s",
180 option.c_str());
181 throw;
182 }
183 }
184 this->set(result, true);
185 } else {
186 this->set(default_value, false);
187 }
188}
189
190bool Bool(const std::string& option,
191 const std::string& description) {
192 return String(option, description, "", ALLOW_EMPTY).is_set();
193}
194
195//! Stop if an option `old_name` is set, printing a message that `new_name` should be used instead.
196void deprecated(const std::string &old_name, const std::string &new_name) {
197
198 String option(old_name, "no description", "default",
200
201 if (option.is_set()) {
202 throw RuntimeError::formatted(PISM_ERROR_LOCATION, "command-line option '%s' is deprecated."
203 " Please use '%s' instead.",
204 old_name.c_str(), new_name.c_str());
205 }
206}
207
208//! Print a warning telling the user that an option was ignored.
209void ignored(const Logger &log, const std::string &name) {
210
211 String option(name, "no description", "default");
212
213 if (option.is_set()) {
214 log.message(1, "PISM WARNING: ignoring command-line option '%s'.\n",
215 name.c_str());
216 }
217}
218
219//!Stop if an option `name` is set.
220void forbidden(const std::string &name) {
221 bool option_is_set = options::Bool(name, "no description");
222
223 if (option_is_set) {
224 throw RuntimeError::formatted(PISM_ERROR_LOCATION, "command-line option '%s' is not allowed.",
225 name.c_str());
226 }
227}
228
229} // end of namespace options
230} // end of namespace pism
void message(int threshold, const char format[],...) const __attribute__((format(printf
Print a message to the log.
Definition Logger.cc:49
A basic logging class.
Definition Logger.hh:40
void add_context(const std::string &message)
Add a message providing some context. This way we can (sort of) get a stack trace even though C++ exc...
static RuntimeError formatted(const ErrorLocation &location, const char format[],...) __attribute__((format(printf
build a RuntimeError with a formatted message
Integer(const std::string &option, const std::string &description, int default_value)
Definition options.cc:136
Keyword(const std::string &option, const std::string &description, const std::string &choices, const std::string &default_value)
Definition options.cc:95
bool is_set() const
Definition options.hh:35
void set(std::string new_value, bool new_flag)
Definition options.hh:56
operator std::string() const
Definition options.hh:41
Real(std::shared_ptr< units::System > system, const std::string &option, const std::string &description, const std::string &units, double default_value)
Definition options.cc:159
String(const std::string &option, const std::string &description)
Definition options.cc:33
int process(const std::string &option, const std::string &description, const std::string &default_value, ArgumentFlag flag)
Definition options.cc:53
#define PISM_CHK(errcode, name)
#define PISM_ERROR_LOCATION
#define n
Definition exactTestM.c:37
static const int TEMPORARY_STRING_LENGTH
Definition options.cc:51
void deprecated(const std::string &old_name, const std::string &new_name)
Stop if an option old_name is set, printing a message that new_name should be used instead.
Definition options.cc:196
bool Bool(const std::string &option, const std::string &description)
Definition options.cc:190
void ignored(const Logger &log, const std::string &name)
Print a warning telling the user that an option was ignored.
Definition options.cc:209
void forbidden(const std::string &name)
Stop if an option name is set.
Definition options.cc:220
double convert(System::Ptr system, double input, const std::string &spec1, const std::string &spec2)
Convert a quantity from unit1 to unit2.
Definition Units.cc:70
std::string printf(const char *format,...)
std::set< std::string > set_split(const std::string &input, char separator)
Transform a separator-separated list (a string) into a set of strings.
long int parse_integer(const std::string &input)