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