PISM, A Parallel Ice Sheet Model  stable v2.1-1-g6902d5502 committed by Ed Bueler on 2023-12-20 08:38:27 -0800
options.cc
Go to the documentation of this file.
1 /* Copyright (C) 2014, 2015, 2016, 2017, 2018, 2020, 2021, 2023 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 
30 namespace pism {
31 namespace options {
32 
33 String::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 
41 String::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 
51 static const int TEMPORARY_STRING_LENGTH = 32768;
52 
53 int 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 
95 Keyword::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 
136 Integer::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 
159 
160 IntegerList::IntegerList(const std::string& option,
161  const std::string& description,
162  const std::vector<int> &defaults) {
163  std::vector<double> default_value;
164 
165  for (auto v : defaults) {
166  default_value.push_back(v);
167  }
168 
169  RealList input(option, description, default_value);
170  std::vector<int> result;
171 
172  const double eps = 1e-6;
173  for (auto v : input.value()) {
174  if (fabs(v - floor(v)) > eps) {
176  "Can't process '%s': (%f is not an integer).",
177  option.c_str(), v);
178  }
179  result.push_back(static_cast<int>(v));
180  }
181 
182  this->set(result, input.is_set());
183 }
184 
185 const int& IntegerList::operator[](size_t index) const {
186  return m_value[index];
187 }
188 
189 Real::Real(std::shared_ptr<units::System> system,
190  const std::string& option,
191  const std::string& description,
192  const std::string& units,
193  double default_value) {
194 
195  std::string buffer = pism::printf("%f", default_value);
196 
197  String input(option, description, buffer, DONT_ALLOW_EMPTY);
198 
199  if (input.is_set()) {
200  char *endptr = NULL;
201  double result = strtod(input->c_str(), &endptr);
202  if (*endptr != '\0') {
203  // assume that "input" contains units and try converting to "units":
204  try {
205  result = units::convert(system, 1.0, input.value(), units);
206  } catch (RuntimeError &e) {
207  e.add_context("trying to convert '%s' to '%s'",
208  input->c_str(), units.c_str());
209  e.add_context("processing the command-line option %s",
210  option.c_str());
211  throw;
212  }
213  }
214  this->set(result, true);
215  } else {
216  this->set(default_value, false);
217  }
218 }
219 
220 
221 RealList::RealList(const std::string& option,
222  const std::string& description,
223  const std::vector<double> &default_value) {
224  String input(option, description, "", DONT_ALLOW_EMPTY);
225  std::vector<double> result = default_value;
226 
227  if (input.is_set()) {
228  result.clear();
229  for (const auto &p : split(input, ',')) {
230  result.push_back(parse_number(p));
231  }
232  }
233  this->set(result, input.is_set());
234 }
235 
236 const double& RealList::operator[](size_t index) const {
237  return m_value[index];
238 }
239 
240 bool Bool(const std::string& option,
241  const std::string& description) {
242  return String(option, description, "", ALLOW_EMPTY).is_set();
243 }
244 
245 //! Stop if an option `old_name` is set, printing a message that `new_name` should be used instead.
246 void deprecated(const std::string &old_name, const std::string &new_name) {
247 
248  String option(old_name, "no description", "default",
250 
251  if (option.is_set()) {
252  throw RuntimeError::formatted(PISM_ERROR_LOCATION, "command-line option '%s' is deprecated."
253  " Please use '%s' instead.",
254  old_name.c_str(), new_name.c_str());
255  }
256 }
257 
258 //! Print a warning telling the user that an option was ignored.
259 void ignored(const Logger &log, const std::string &name) {
260 
261  String option(name, "no description", "default");
262 
263  if (option.is_set()) {
264  log.message(1, "PISM WARNING: ignoring command-line option '%s'.\n",
265  name.c_str());
266  }
267 }
268 
269 //!Stop if an option `name` is set.
270 void forbidden(const std::string &name) {
271  bool option_is_set = options::Bool(name, "no description");
272 
273  if (option_is_set) {
274  throw RuntimeError::formatted(PISM_ERROR_LOCATION, "command-line option '%s' is not allowed.",
275  name.c_str());
276  }
277 }
278 
279 } // end of namespace options
280 } // 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
IntegerList(const std::string &option, const std::string &description, const std::vector< int > &defaults)
Definition: options.cc:160
const int & operator[](size_t index) const
Definition: options.cc:185
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
RealList(const std::string &option, const std::string &description, const std::vector< double > &default_value)
Definition: options.cc:221
const double & operator[](size_t index) const
Definition: options.cc:236
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:189
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:246
bool Bool(const std::string &option, const std::string &description)
Definition: options.cc:240
void ignored(const Logger &log, const std::string &name)
Print a warning telling the user that an option was ignored.
Definition: options.cc:259
void forbidden(const std::string &name)
Stop if an option name is set.
Definition: options.cc:270
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
double parse_number(const std::string &input)
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)
std::vector< std::string > split(const std::string &input, char separator)
Transform a separator-separated list (a string) into a vector of strings.