MFEM  v4.5.2
Finite element discretization library
optparser.cpp
Go to the documentation of this file.
1 // Copyright (c) 2010-2023, Lawrence Livermore National Security, LLC. Produced
2 // at the Lawrence Livermore National Laboratory. All Rights reserved. See files
3 // LICENSE and NOTICE for details. LLNL-CODE-806117.
4 //
5 // This file is part of the MFEM library. For more information and source code
6 // availability visit https://mfem.org.
7 //
8 // MFEM is free software; you can redistribute it and/or modify it under the
9 // terms of the BSD-3 license. We welcome feedback and contributions, see file
10 // CONTRIBUTING.md for details.
11 
12 #include "optparser.hpp"
13 #include "../linalg/vector.hpp"
14 #include "../general/communication.hpp"
15 #include <cctype>
16 
17 namespace mfem
18 {
19 
20 using namespace std;
21 
22 int isValidAsInt(char * s)
23 {
24  if ( s == NULL || *s == '\0' )
25  {
26  return 0; // Empty string
27  }
28 
29  if ( *s == '+' || *s == '-' )
30  {
31  ++s;
32  }
33 
34  if ( *s == '\0')
35  {
36  return 0; // sign character only
37  }
38 
39  while (*s)
40  {
41  if ( !isdigit(*s) )
42  {
43  return 0;
44  }
45  ++s;
46  }
47 
48  return 1;
49 }
50 
51 int isValidAsDouble(char * s)
52 {
53  // A valid floating point number for atof using the "C" locale is formed by
54  // - an optional sign character (+ or -),
55  // - followed by a sequence of digits, optionally containing a decimal-point
56  // character (.),
57  // - optionally followed by an exponent part (an e or E character followed by
58  // an optional sign and a sequence of digits).
59 
60  if ( s == NULL || *s == '\0' )
61  {
62  return 0; // Empty string
63  }
64 
65  if ( *s == '+' || *s == '-' )
66  {
67  ++s;
68  }
69 
70  if ( *s == '\0')
71  {
72  return 0; // sign character only
73  }
74 
75  while (*s)
76  {
77  if (!isdigit(*s))
78  {
79  break;
80  }
81  ++s;
82  }
83 
84  if (*s == '\0')
85  {
86  return 1; // s = "123"
87  }
88 
89  if (*s == '.')
90  {
91  ++s;
92  while (*s)
93  {
94  if (!isdigit(*s))
95  {
96  break;
97  }
98  ++s;
99  }
100  if (*s == '\0')
101  {
102  return 1; // this is a fixed point double s = "123." or "123.45"
103  }
104  }
105 
106  if (*s == 'e' || *s == 'E')
107  {
108  ++s;
109  return isValidAsInt(s);
110  }
111  else
112  {
113  return 0; // we have encounter a wrong character
114  }
115 }
116 
117 void parseArray(char * str, Array<int> & var)
118 {
119  var.SetSize(0);
120  std::stringstream input(str);
121  int val;
122  while ( input >> val)
123  {
124  var.Append(val);
125  }
126 }
127 
128 void parseVector(char * str, Vector & var)
129 {
130  int nentries = 0;
131  double val;
132  {
133  std::stringstream input(str);
134  while ( input >> val)
135  {
136  ++nentries;
137  }
138  }
139 
140  var.SetSize(nentries);
141  {
142  nentries = 0;
143  std::stringstream input(str);
144  while ( input >> val)
145  {
146  var(nentries++) = val;
147  }
148  }
149 }
150 
152 {
153  option_check.SetSize(options.Size());
154  option_check = 0;
155  for (int i = 1; i < argc; )
156  {
157  if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0)
158  {
159  // print help message
160  error_type = 1;
161  return;
162  }
163 
164  for (int j = 0; true; j++)
165  {
166  if (j >= options.Size())
167  {
168  // unrecognized option
169  error_type = 2;
170  error_idx = i;
171  return;
172  }
173 
174  if (strcmp(argv[i], options[j].short_name) == 0 ||
175  strcmp(argv[i], options[j].long_name) == 0)
176  {
177  OptionType type = options[j].type;
178 
179  if ( option_check[j] )
180  {
181  error_type = 4;
182  error_idx = j;
183  return;
184  }
185  option_check[j] = 1;
186 
187  i++;
188  if (type != ENABLE && type != DISABLE && i >= argc)
189  {
190  // missing argument
191  error_type = 3;
192  error_idx = j;
193  return;
194  }
195 
196  int isValid = 1;
197  switch (options[j].type)
198  {
199  case INT:
200  isValid = isValidAsInt(argv[i]);
201  *(int *)(options[j].var_ptr) = atoi(argv[i++]);
202  break;
203  case DOUBLE:
204  isValid = isValidAsDouble(argv[i]);
205  *(double *)(options[j].var_ptr) = atof(argv[i++]);
206  break;
207  case STRING:
208  *(const char **)(options[j].var_ptr) = argv[i++];
209  break;
210  case ENABLE:
211  *(bool *)(options[j].var_ptr) = true;
212  option_check[j+1] = 1; // Do not allow the DISABLE Option
213  break;
214  case DISABLE:
215  *(bool *)(options[j].var_ptr) = false;
216  option_check[j-1] = 1; // Do not allow the ENABLE Option
217  break;
218  case ARRAY:
219  parseArray(argv[i++], *(Array<int>*)(options[j].var_ptr) );
220  break;
221  case VECTOR:
222  parseVector(argv[i++], *(Vector*)(options[j].var_ptr) );
223  break;
224  }
225 
226  if (!isValid)
227  {
228  error_type = 5;
229  error_idx = i;
230  return;
231  }
232 
233  break;
234  }
235  }
236  }
237 
238  // check for missing required options
239  for (int i = 0; i < options.Size(); i++)
240  if (options[i].required &&
241  (option_check[i] == 0 ||
242  (options[i].type == ENABLE && option_check[++i] == 0)))
243  {
244  error_type = 6; // required option missing
245  error_idx = i; // for a boolean option i is the index of DISABLE
246  return;
247  }
248 
249  error_type = 0;
250 }
251 
252 void OptionsParser::ParseCheck(std::ostream &os)
253 {
254  Parse();
255  int my_rank = 0;
256 #ifdef MFEM_USE_MPI
257  int mpi_is_initialized = Mpi::IsInitialized();
258  if (mpi_is_initialized) { my_rank = Mpi::WorldRank(); }
259 #endif
260  if (!Good())
261  {
262  if (my_rank == 0) { PrintUsage(os); }
263 #ifdef MFEM_USE_MPI
264  Mpi::Finalize();
265 #endif
266  std::exit(1);
267  }
268  if (my_rank == 0) { PrintOptions(os); }
269 }
270 
271 void OptionsParser::WriteValue(const Option &opt, std::ostream &os)
272 {
273  switch (opt.type)
274  {
275  case INT:
276  os << *(int *)(opt.var_ptr);
277  break;
278 
279  case DOUBLE:
280  os << *(double *)(opt.var_ptr);
281  break;
282 
283  case STRING:
284  os << *(const char **)(opt.var_ptr);
285  break;
286 
287  case ARRAY:
288  {
289  Array<int> &list = *(Array<int>*)(opt.var_ptr);
290  os << '\'';
291  if (list.Size() > 0)
292  {
293  os << list[0];
294  }
295  for (int i = 1; i < list.Size(); i++)
296  {
297  os << ' ' << list[i];
298  }
299  os << '\'';
300  break;
301  }
302 
303  case VECTOR:
304  {
305  Vector &list = *(Vector*)(opt.var_ptr);
306  os << '\'';
307  if (list.Size() > 0)
308  {
309  os << list(0);
310  }
311  for (int i = 1; i < list.Size(); i++)
312  {
313  os << ' ' << list(i);
314  }
315  os << '\'';
316  break;
317  }
318 
319  default: // provide a default to suppress warning
320  break;
321  }
322 }
323 
324 void OptionsParser::PrintOptions(ostream &os) const
325 {
326  static const char *indent = " ";
327 
328  os << "Options used:\n";
329  for (int j = 0; j < options.Size(); j++)
330  {
331  OptionType type = options[j].type;
332 
333  os << indent;
334  if (type == ENABLE)
335  {
336  if (*(bool *)(options[j].var_ptr) == true)
337  {
338  os << options[j].long_name;
339  }
340  else
341  {
342  os << options[j+1].long_name;
343  }
344  j++;
345  }
346  else
347  {
348  os << options[j].long_name << " ";
349  WriteValue(options[j], os);
350  }
351  os << '\n';
352  }
353 }
354 
355 void OptionsParser::PrintError(ostream &os) const
356 {
357  static const char *line_sep = "";
358 
359  os << line_sep;
360  switch (error_type)
361  {
362  case 2:
363  os << "Unrecognized option: " << argv[error_idx] << '\n' << line_sep;
364  break;
365 
366  case 3:
367  os << "Missing argument for the last option: " << argv[argc-1]
368  << '\n' << line_sep;
369  break;
370 
371  case 4:
372  if (options[error_idx].type == ENABLE )
373  os << "Option " << options[error_idx].long_name << " or "
374  << options[error_idx + 1].long_name
375  << " provided multiple times\n" << line_sep;
376  else if (options[error_idx].type == DISABLE)
377  os << "Option " << options[error_idx - 1].long_name << " or "
378  << options[error_idx].long_name
379  << " provided multiple times\n" << line_sep;
380  else
381  os << "Option " << options[error_idx].long_name
382  << " provided multiple times\n" << line_sep;
383  break;
384 
385  case 5:
386  os << "Wrong option format: " << argv[error_idx - 1] << " "
387  << argv[error_idx] << '\n' << line_sep;
388  break;
389 
390  case 6:
391  os << "Missing required option: " << options[error_idx].long_name
392  << '\n' << line_sep;
393  break;
394  }
395  os << endl;
396 }
397 
398 void OptionsParser::PrintHelp(ostream &os) const
399 {
400  static const char *indent = " ";
401  static const char *seprtr = ", ";
402  static const char *descr_sep = "\n\t";
403  static const char *line_sep = "";
404  static const char *types[] = { " <int>", " <double>", " <string>", "", "",
405  " '<int>...'", " '<double>...'"
406  };
407 
408  os << indent << "-h" << seprtr << "--help" << descr_sep
409  << "Print this help message and exit.\n" << line_sep;
410  for (int j = 0; j < options.Size(); j++)
411  {
412  OptionType type = options[j].type;
413 
414  os << indent << options[j].short_name << types[type]
415  << seprtr << options[j].long_name << types[type]
416  << seprtr;
417  if (options[j].required)
418  {
419  os << "(required)";
420  }
421  else
422  {
423  if (type == ENABLE)
424  {
425  j++;
426  os << options[j].short_name << types[type] << seprtr
427  << options[j].long_name << types[type] << seprtr
428  << "current option: ";
429  if (*(bool *)(options[j].var_ptr) == true)
430  {
431  os << options[j-1].long_name;
432  }
433  else
434  {
435  os << options[j].long_name;
436  }
437  }
438  else
439  {
440  os << "current value: ";
441  WriteValue(options[j], os);
442  }
443  }
444  os << descr_sep;
445 
446  if (options[j].description)
447  {
448  os << options[j].description << '\n';
449  }
450  os << line_sep;
451  }
452 }
453 
454 void OptionsParser::PrintUsage(ostream &os) const
455 {
456  static const char *line_sep = "";
457 
458  PrintError(os);
459  os << "Usage: " << argv[0] << " [options] ...\n" << line_sep
460  << "Options:\n" << line_sep;
461  PrintHelp(os);
462 }
463 
464 }
void PrintOptions(std::ostream &out) const
Print the options.
Definition: optparser.cpp:324
static void Finalize()
Finalize MPI (if it has been initialized and not yet already finalized).
void parseVector(char *str, Vector &var)
Definition: optparser.cpp:128
void SetSize(int s)
Resize the vector to size s.
Definition: vector.hpp:512
void PrintUsage(std::ostream &out) const
Print the usage message.
Definition: optparser.cpp:454
void PrintError(std::ostream &out) const
Print the error message.
Definition: optparser.cpp:355
STL namespace.
void Parse()
Parse the command-line options. Note that this function expects all the options provided through the ...
Definition: optparser.cpp:151
int Append(const T &el)
Append element &#39;el&#39; to array, resize if necessary.
Definition: array.hpp:756
static bool IsInitialized()
Return true if MPI has been initialized.
void SetSize(int nsize)
Change the logical size of the array, keep existing entries.
Definition: array.hpp:684
void PrintHelp(std::ostream &out) const
Print the help message.
Definition: optparser.cpp:398
static int WorldRank()
Return the MPI rank in MPI_COMM_WORLD.
int Size() const
Return the logical size of the array.
Definition: array.hpp:141
Vector data type.
Definition: vector.hpp:60
int isValidAsInt(char *s)
Definition: optparser.cpp:22
RefCoord s[3]
void parseArray(char *str, Array< int > &var)
Definition: optparser.cpp:117
int isValidAsDouble(char *s)
Definition: optparser.cpp:51
void ParseCheck(std::ostream &out=mfem::out)
Definition: optparser.cpp:252