MFEM  v4.6.0
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 STD_STRING:
211  *(std::string *)(options[j].var_ptr) = argv[i++];
212  break;
213  case ENABLE:
214  *(bool *)(options[j].var_ptr) = true;
215  option_check[j+1] = 1; // Do not allow the DISABLE Option
216  break;
217  case DISABLE:
218  *(bool *)(options[j].var_ptr) = false;
219  option_check[j-1] = 1; // Do not allow the ENABLE Option
220  break;
221  case ARRAY:
222  parseArray(argv[i++], *(Array<int>*)(options[j].var_ptr) );
223  break;
224  case VECTOR:
225  parseVector(argv[i++], *(Vector*)(options[j].var_ptr) );
226  break;
227  }
228 
229  if (!isValid)
230  {
231  error_type = 5;
232  error_idx = i;
233  return;
234  }
235 
236  break;
237  }
238  }
239  }
240 
241  // check for missing required options
242  for (int i = 0; i < options.Size(); i++)
243  if (options[i].required &&
244  (option_check[i] == 0 ||
245  (options[i].type == ENABLE && option_check[++i] == 0)))
246  {
247  error_type = 6; // required option missing
248  error_idx = i; // for a boolean option i is the index of DISABLE
249  return;
250  }
251 
252  error_type = 0;
253 }
254 
255 void OptionsParser::ParseCheck(std::ostream &os)
256 {
257  Parse();
258  int my_rank = 0;
259 #ifdef MFEM_USE_MPI
260  int mpi_is_initialized = Mpi::IsInitialized();
261  if (mpi_is_initialized) { my_rank = Mpi::WorldRank(); }
262 #endif
263  if (!Good())
264  {
265  if (my_rank == 0) { PrintUsage(os); }
266 #ifdef MFEM_USE_MPI
267  Mpi::Finalize();
268 #endif
269  std::exit(1);
270  }
271  if (my_rank == 0) { PrintOptions(os); }
272 }
273 
274 void OptionsParser::WriteValue(const Option &opt, std::ostream &os)
275 {
276  switch (opt.type)
277  {
278  case INT:
279  os << *(int *)(opt.var_ptr);
280  break;
281 
282  case DOUBLE:
283  os << *(double *)(opt.var_ptr);
284  break;
285 
286  case STRING:
287  os << *(const char **)(opt.var_ptr);
288  break;
289 
290  case STD_STRING:
291  out << *(std::string *)(opt.var_ptr);
292  break;
293 
294  case ARRAY:
295  {
296  Array<int> &list = *(Array<int>*)(opt.var_ptr);
297  os << '\'';
298  if (list.Size() > 0)
299  {
300  os << list[0];
301  }
302  for (int i = 1; i < list.Size(); i++)
303  {
304  os << ' ' << list[i];
305  }
306  os << '\'';
307  break;
308  }
309 
310  case VECTOR:
311  {
312  Vector &list = *(Vector*)(opt.var_ptr);
313  os << '\'';
314  if (list.Size() > 0)
315  {
316  os << list(0);
317  }
318  for (int i = 1; i < list.Size(); i++)
319  {
320  os << ' ' << list(i);
321  }
322  os << '\'';
323  break;
324  }
325 
326  default: // provide a default to suppress warning
327  break;
328  }
329 }
330 
331 void OptionsParser::PrintOptions(ostream &os) const
332 {
333  static const char *indent = " ";
334 
335  os << "Options used:\n";
336  for (int j = 0; j < options.Size(); j++)
337  {
338  OptionType type = options[j].type;
339 
340  os << indent;
341  if (type == ENABLE)
342  {
343  if (*(bool *)(options[j].var_ptr) == true)
344  {
345  os << options[j].long_name;
346  }
347  else
348  {
349  os << options[j+1].long_name;
350  }
351  j++;
352  }
353  else
354  {
355  os << options[j].long_name << " ";
356  WriteValue(options[j], os);
357  }
358  os << '\n';
359  }
360 }
361 
362 void OptionsParser::PrintError(ostream &os) const
363 {
364  static const char *line_sep = "";
365 
366  os << line_sep;
367  switch (error_type)
368  {
369  case 2:
370  os << "Unrecognized option: " << argv[error_idx] << '\n' << line_sep;
371  break;
372 
373  case 3:
374  os << "Missing argument for the last option: " << argv[argc-1]
375  << '\n' << line_sep;
376  break;
377 
378  case 4:
379  if (options[error_idx].type == ENABLE )
380  os << "Option " << options[error_idx].long_name << " or "
381  << options[error_idx + 1].long_name
382  << " provided multiple times\n" << line_sep;
383  else if (options[error_idx].type == DISABLE)
384  os << "Option " << options[error_idx - 1].long_name << " or "
385  << options[error_idx].long_name
386  << " provided multiple times\n" << line_sep;
387  else
388  os << "Option " << options[error_idx].long_name
389  << " provided multiple times\n" << line_sep;
390  break;
391 
392  case 5:
393  os << "Wrong option format: " << argv[error_idx - 1] << " "
394  << argv[error_idx] << '\n' << line_sep;
395  break;
396 
397  case 6:
398  os << "Missing required option: " << options[error_idx].long_name
399  << '\n' << line_sep;
400  break;
401  }
402  os << endl;
403 }
404 
405 void OptionsParser::PrintHelp(ostream &os) const
406 {
407  static const char *indent = " ";
408  static const char *seprtr = ", ";
409  static const char *descr_sep = "\n\t";
410  static const char *line_sep = "";
411  static const char *types[] = { " <int>", " <double>", " <string>",
412  " <string>", "", "", " '<int>...'",
413  " '<double>...'"
414  };
415 
416  os << indent << "-h" << seprtr << "--help" << descr_sep
417  << "Print this help message and exit.\n" << line_sep;
418  for (int j = 0; j < options.Size(); j++)
419  {
420  OptionType type = options[j].type;
421 
422  os << indent << options[j].short_name << types[type]
423  << seprtr << options[j].long_name << types[type]
424  << seprtr;
425  if (options[j].required)
426  {
427  os << "(required)";
428  }
429  else
430  {
431  if (type == ENABLE)
432  {
433  j++;
434  os << options[j].short_name << types[type] << seprtr
435  << options[j].long_name << types[type] << seprtr
436  << "current option: ";
437  if (*(bool *)(options[j].var_ptr) == true)
438  {
439  os << options[j-1].long_name;
440  }
441  else
442  {
443  os << options[j].long_name;
444  }
445  }
446  else
447  {
448  os << "current value: ";
449  WriteValue(options[j], os);
450  }
451  }
452  os << descr_sep;
453 
454  if (options[j].description)
455  {
456  os << options[j].description << '\n';
457  }
458  os << line_sep;
459  }
460 }
461 
462 void OptionsParser::PrintUsage(ostream &os) const
463 {
464  static const char *line_sep = "";
465 
466  PrintError(os);
467  os << "Usage: " << argv[0] << " [options] ...\n" << line_sep
468  << "Options:\n" << line_sep;
469  PrintHelp(os);
470 }
471 
472 }
void PrintOptions(std::ostream &out) const
Print the options.
Definition: optparser.cpp:331
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:517
void PrintUsage(std::ostream &out) const
Print the usage message.
Definition: optparser.cpp:462
void PrintError(std::ostream &out) const
Print the error message.
Definition: optparser.cpp:362
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:759
static bool IsInitialized()
Return true if MPI has been initialized.
OutStream out(std::cout)
Global stream used by the library for standard output. Initially it uses the same std::streambuf as s...
Definition: globals.hpp:66
void SetSize(int nsize)
Change the logical size of the array, keep existing entries.
Definition: array.hpp:687
void PrintHelp(std::ostream &out) const
Print the help message.
Definition: optparser.cpp:405
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:58
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:255