MFEM v4.7.0
Finite element discretization library
Loading...
Searching...
No Matches
optparser.cpp
Go to the documentation of this file.
1// Copyright (c) 2010-2024, 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"
15#include <cctype>
16
17namespace mfem
18{
19
20using namespace std;
21
22int 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
51int 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
117void 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
128void parseVector(char * str, Vector & var)
129{
130 int nentries = 0;
131 real_t 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 *(real_t *)(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
255void 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
268#endif
269 std::exit(1);
270 }
271 if (my_rank == 0) { PrintOptions(os); }
272}
273
274void 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 << *(real_t *)(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
331void 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
362void 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
405void 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
462void 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 SetSize(int nsize)
Change the logical size of the array, keep existing entries.
Definition array.hpp:697
int Size() const
Return the logical size of the array.
Definition array.hpp:144
int Append(const T &el)
Append element 'el' to array, resize if necessary.
Definition array.hpp:769
static void Finalize()
Finalize MPI (if it has been initialized and not yet already finalized).
static bool IsInitialized()
Return true if MPI has been initialized.
static int WorldRank()
Return the MPI rank in MPI_COMM_WORLD.
void ParseCheck(std::ostream &out=mfem::out)
void Parse()
Parse the command-line options. Note that this function expects all the options provided through the ...
void PrintUsage(std::ostream &out) const
Print the usage message.
void PrintOptions(std::ostream &out) const
Print the options.
void PrintHelp(std::ostream &out) const
Print the help message.
bool Good() const
Return true if the command line options were parsed successfully.
void PrintError(std::ostream &out) const
Print the error message.
Vector data type.
Definition vector.hpp:80
void SetSize(int s)
Resize the vector to size s.
Definition vector.hpp:538
int isValidAsInt(char *s)
Definition optparser.cpp:22
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)
float real_t
Definition config.hpp:43
void parseVector(char *str, Vector &var)
int isValidAsDouble(char *s)
Definition optparser.cpp:51
RefCoord s[3]