Line data Source code
1 : //
2 : // This is a thin wrapper to the amrex::ParmParse class
3 : // This class exists to add some additional parsing capability,
4 : // e.g. parsing Set::Matrix and Set::Vector data types.
5 : //
6 : // :ref:`IO::ParmParse` uses static :code:`Parse()` functions to
7 : // perform cascading class-based input parsing.
8 : // See the :ref:`autodoc` section for instructions on adding documentation.
9 : //
10 : // :bdg-warning-line:`This is standard infrastructure code; make sure you know what you ard doing before you change it.`
11 : //
12 : // .. _query-directives:
13 : //
14 : // **Query directives**
15 : //
16 : // Alamo uses different query directives to standardize reading inputs and automatic documentation.
17 : // The type of query that is used (query vs query_required, etc) triggers different handlers and different
18 : // automatic documentation procedures.
19 : // For instance, the use of a :code:`query_default` causes a default value to be set and added to the metadata
20 : // file, and it also serves as a flag for the autodoc system to document that a default value is available.
21 : // The following table is a reference for the different kinds of query directives.
22 : //
23 : //
24 : // .. table::
25 : // :widths: 1 99
26 : //
27 : // +---------------------------------+-----------------------------------------------------------------+
28 : // | BC Type | Description |
29 : // +=================================+=================================================================+
30 : // | :bdg-warning:`query` | Standard IO for bool, string, Set::Scalarthat does not enforce |
31 : // | | defaults or required values. Not recommended for general use. |
32 : // +---------------------------------+-----------------------------------------------------------------+
33 : // | :bdg-success:`query_required` | Similar to query, but will abort if no value is specified. |
34 : // | | Required values are indicated by :bdg-danger-line:`required`. |
35 : // +---------------------------------+-----------------------------------------------------------------+
36 : // | :bdg-success:`query_default` | Similar to query, but will fill with default value if no value |
37 : // | | is provided. Will also add default value to metadata. |
38 : // | | Default values are indicated by green badge values, e.g. |
39 : // | | :bdg-success:`0.0`. |
40 : // +---------------------------------+-----------------------------------------------------------------+
41 : // | :bdg-success:`query_validate` | For strings, read in a value and enforce that the value is one |
42 : // | | of a supplied number of values. Optionally make required, or |
43 : // | | set the default value to the first supplied value. |
44 : // | | Acceptable options are indicated by blue badge values, e.g. |
45 : // | | :bdg-primary:`1.0`, :bdg-primary:`2.0`. If a default value is |
46 : // | | available, it is indicated by a green badge, e.g. |
47 : // | | :bdg-success:`1.0`. |
48 : // +---------------------------------+-----------------------------------------------------------------+
49 : // | :bdg-success:`query_file` | Read in a string that defines a file name. |
50 : // | | Check to make sure that the file exists and is a regular file, |
51 : // | | and print an informative error message if not (this can be |
52 : // | | disabled). |
53 : // | | Also, copy the file to the output directory, with the full path |
54 : // | | preserved by replacing / with _. |
55 : // | | (This can also be disabled, but doing so is discouraged.) |
56 : // | | Default values are not allowed. |
57 : // | | File paths are indicated by :bdg-secondary-line:`file path`. |
58 : // +---------------------------------+-----------------------------------------------------------------+
59 : // | :bdg-primary:`queryarr` | Read in an array of numbers into either a standard vector or |
60 : // | | into a :code:`Set::Vector` or :code:`Set::Scalar`. |
61 : // | | No defaults or existence checking is performed. |
62 : // +---------------------------------+-----------------------------------------------------------------+
63 : // | :bdg-primary:`queryclass` | Read a class object with a specified prefix. |
64 : // | | How that class is read in is determined by its :code:`Parse` |
65 : // | | function. |
66 : // +---------------------------------+-----------------------------------------------------------------+
67 : //
68 : // .. _query_locator_macros:
69 : //
70 : // **Query macros**
71 : //
72 : // A set of preprocessor macros are defined so that you can call a query function using :code:`pp_` instead
73 : // of :code:`pp.`.
74 : // For instance, the following two can be used interchangeably:
75 : //
76 : // .. code-block:: cpp
77 : //
78 : // pp.query_required("myvar",myvar); /* function version */
79 : // pp_query_required("myvar",myvar); /* preprocessor macro version - preferred*/
80 : //
81 : // Using the preprocessor macros enables code location information to be passed to the parser, so that
82 : // more informative error messages will be printed out.
83 : // Note that **the ParmParse object must always be called** :code:`pp` **for this to work**.
84 : //
85 : //
86 :
87 : #ifndef IO_PARMPARSE
88 : #define IO_PARMPARSE
89 :
90 : #include <filesystem>
91 : #include <exception>
92 : #include <list>
93 :
94 : #include "Util/Util.H"
95 : #include "Set/Set.H"
96 : #include "AMReX_ParmParse.H"
97 :
98 :
99 : #define pp_query_required(...) pp.query_required(__VA_ARGS__,INFO)
100 : #define pp_query_default(...) pp.query_default(__VA_ARGS__,INFO)
101 : #define pp_query_validate(...) pp.query_validate(__VA_ARGS__,INFO)
102 : #define pp_query_file(...) pp.query_file(__VA_ARGS__,INFO)
103 : #define pp_queryarr(...) pp.queryarr(__VA_ARGS__,INFO)
104 : #define pp_queryarr_required(...) pp.queryarr_required(__VA_ARGS__,INFO)
105 : #define pp_query(...) pp.query(__VA_ARGS__)
106 : #define pp_queryclass(...) pp.queryclass(__VA_ARGS__,INFO)
107 : #define pp_forbid(...) pp.forbid(__VA_ARGS__,INFO)
108 :
109 :
110 : namespace IO
111 : {
112 : class ParmParse : public amrex::ParmParse
113 : {
114 : private:
115 780 : void Define()
116 : {
117 780 : if (checked_for_input_files) return;
118 33 : int k = 0;
119 66 : std::string inputfile = "";
120 33 : while (this->querykth("input",k,inputfile))
121 : {
122 0 : Util::Message(INFO,"Including inputs from "+inputfile);
123 0 : this->addfile(inputfile);
124 0 : k++;
125 : }
126 33 : checked_for_input_files = true;
127 : }
128 : static bool checked_for_input_files;
129 :
130 : public:
131 587 : ParmParse(std::string arg) : amrex::ParmParse::ParmParse(arg) {Define();} ;
132 193 : ParmParse() : amrex::ParmParse::ParmParse() {Define();} ;
133 4654 : std::string getPrefix() const {return m_prefix;};
134 396 : void ignore(std::string name)
135 : {
136 396 : (void)amrex::ParmParse::contains(name.c_str());
137 396 : }
138 :
139 2 : void forbid(std::string name, std::string explanation,
140 : std::string file = "", std::string func = "", int line = -1)
141 : {
142 2 : if (amrex::ParmParse::contains(full(name).c_str()))
143 : {
144 0 : Util::ParmParseException(INFO,file,func,line,full(name),full(name)," forbidden: ", explanation);
145 : }
146 6 : std::set<std::string> subs = amrex::ParmParse::getEntries(full(name));
147 2 : if (subs.size())
148 : {
149 0 : Util::ParmParseException(INFO,file,func,line,full(name),full(name)," forbidden: ", explanation);
150 : }
151 2 : }
152 :
153 1694 : bool contains(std::string name)
154 : {
155 1694 : if (amrex::ParmParse::contains(name.c_str()))
156 546 : return true;
157 1148 : if (amrex::ParmParse::contains(full(name).c_str()))
158 0 : return true;
159 : {
160 2296 : std::set<std::string> subs = amrex::ParmParse::getEntries(name.c_str());
161 1148 : if (subs.size())
162 22 : return true;
163 : }
164 : {
165 2252 : std::set<std::string> subs = amrex::ParmParse::getEntries(full(name).c_str());
166 1126 : if (subs.size())
167 0 : return true;
168 : }
169 1126 : return false;
170 : }
171 :
172 : template<typename T>
173 175 : int query_required( std::string name, T & value,
174 : std::string file = "", std::string func = "", int line = -1)
175 : {
176 175 : if (!contains(name.c_str()))
177 : {
178 0 : Util::ParmParseException(INFO,file,func,line,full(name),"required value for ",full(name)," missing");
179 : }
180 175 : return query(name.c_str(),value);
181 : }
182 :
183 : template<typename T>
184 979 : int query_default( std::string name, T & value, T defaultvalue,
185 : std::string = "", std::string = "", int = -1)
186 : {
187 979 : if (!contains(name.c_str()))
188 : {
189 786 : add(name.c_str(),defaultvalue);
190 : }
191 979 : return query(name.c_str(),value);
192 : }
193 :
194 : int query_validate( std::string name, int & value, std::vector<int> possibleintvals,
195 : std::string file = "", std::string func = "", int line = -1)
196 : {
197 : // First value is accepted by default...
198 :
199 : // set default value
200 : value = possibleintvals[0];
201 :
202 : // get the read value (if it exists)
203 : int retval = query(name.c_str(),value);
204 :
205 : // check to make sure the read value matches one of the inpus
206 : bool ok = false;
207 : for (unsigned int i = 0; i < possibleintvals.size(); i++)
208 : {
209 : if (value == possibleintvals[i]) ok = true;
210 : }
211 :
212 : if (ok) return retval;
213 :
214 : std::stringstream ss;
215 : ss << possibleintvals[0];
216 : for (unsigned int i = 1; i < possibleintvals.size(); i++)
217 : ss << "," << possibleintvals[i];
218 :
219 : Util::ParmParseException(INFO,file,func,line,full(name),"' expected [", ss.str(), "] but got ", value);
220 :
221 : return -1;
222 : }
223 :
224 :
225 49 : int query_validate( std::string name, std::string & value, std::vector<const char *> possiblecharvals, bool firstbydefault,
226 : std::string file = "", std::string func = "", int line = -1)
227 : {
228 : // if not using default values, then the input must be specified
229 49 : if (!firstbydefault)
230 : {
231 0 : if (!contains(name.c_str()))
232 : {
233 0 : Util::ParmParseException(INFO,file,func,line,full(name),"required value for ",full(name)," missing");
234 : }
235 : }
236 :
237 : // set default value
238 49 : value = std::string(possiblecharvals[0]);
239 :
240 : // get the read value (if it exists)
241 49 : int retval = query(name.c_str(),value);
242 :
243 : // check to make sure the read value matches one of the inpus
244 49 : bool ok = false;
245 210 : for (unsigned int i = 0; i < possiblecharvals.size(); i++)
246 : {
247 161 : if (value == std::string(possiblecharvals[i])) ok = true;
248 : }
249 :
250 49 : if (ok) return retval;
251 :
252 0 : std::stringstream ss;
253 0 : ss << possiblecharvals[0];
254 0 : for (unsigned int i = 1; i < possiblecharvals.size(); i++)
255 0 : ss << "," << possiblecharvals[i];
256 :
257 0 : Util::ParmParseException(INFO,file,func,line,full(name),"' expected [", ss.str(), "] but got ", value);
258 :
259 0 : return -1;
260 : }
261 :
262 49 : int query_validate( std::string name, std::string & value, std::vector<const char *> possiblecharvals,
263 : std::string file = "", std::string func = "", int line = -1)
264 : {
265 49 : return query_validate(name,value,possiblecharvals,true,file,func,line);
266 : }
267 :
268 :
269 : // special case for strings
270 31 : int query_default( std::string name, std::string & value, const char *defaultvalue,
271 : std::string file = "", std::string func = "", int line = -1)
272 : {
273 31 : return query_default(name, value, std::string(defaultvalue), file, func, line);
274 : }
275 : // special case for bools
276 6 : int query_default( std::string name, int & value, bool defaultvalue,
277 : std::string file = "", std::string func = "query_default", int line = -1)
278 : {
279 6 : int defaultint = 0;
280 6 : if (defaultvalue) defaultint = 1;
281 6 : return query_default(name, value, defaultint, file, func, line);
282 : }
283 :
284 :
285 :
286 : // validate filenames
287 2 : int query_file( std::string name, std::string & value, bool copyfile, bool checkfile,
288 : std::string file = "", std::string func = "query_file", int line = -1)
289 : {
290 : try
291 : {
292 2 : if (!contains(name.c_str()))
293 : {
294 0 : Util::ParmParseException(INFO,file,func,line,full(name),full(name)," must be specified");
295 : }
296 :
297 2 : int retval = query(name.c_str(),value);
298 :
299 2 : if (amrex::ParallelDescriptor::IOProcessor())
300 : {
301 2 : if ( checkfile && ! std::filesystem::exists(value))
302 : {
303 0 : Util::ParmParseException(INFO,file,func,line,full(name),full(name)," does not exist");
304 : }
305 2 : if ( checkfile && !std::filesystem::is_regular_file(value))
306 : {
307 0 : Util::ParmParseException(INFO,file,func,line,full(name),full(name)," is not a regular file");
308 : }
309 2 : if ( copyfile )
310 : {
311 2 : Util::CopyFileToOutputDir(value, true, full(name));
312 : }
313 : }
314 2 : return retval;
315 : }
316 0 : catch (...)
317 : {
318 0 : Util::ParmParseException(INFO,file,func,line,full(name));
319 : }
320 0 : return -1;
321 : }
322 : int query_file( std::string name, std::string & value, bool copyfile,
323 : std::string file = "", std::string func = "query_file", int line = -1)
324 : {
325 : return query_file(name,value,copyfile,true,file,func,line);
326 : }
327 2 : int query_file( std::string name, std::string & value,
328 : std::string file = "", std::string func = "query_file", int line = -1)
329 : {
330 2 : return query_file(name,value,true,true,file,func,line);
331 : }
332 :
333 :
334 : template<typename T>
335 310 : int queryarr( std::string name, std::vector<T> & value,
336 : std::string /*file*/, std::string /*func*/, int /*line*/)
337 : {
338 310 : return amrex::ParmParse::queryarr(name.c_str(),value);
339 : }
340 9 : int queryarr( std::string name, Set::Vector & value,
341 : std::string file = "", std::string func = "queryarr", int line = -1)
342 : {
343 9 : std::vector<Set::Scalar> vals;
344 9 : amrex::ParmParse::queryarr(name.c_str(), vals);
345 9 : if (vals.size() < AMREX_SPACEDIM)
346 : {
347 0 : Util::ParmParseException(INFO,file,func,line,full(name),full(name)," requires at least ", AMREX_SPACEDIM, " arguments, got ",vals.size());
348 : }
349 28 : for (int i = 0; i < AMREX_SPACEDIM; i++) value(i) = vals[i];
350 18 : return 0;
351 : }
352 6 : int queryarr( std::string name, Set::Matrix & value,
353 : std::string file = "", std::string func = "queryarr", int line = -1)
354 : {
355 6 : std::vector<Set::Scalar> vals;
356 6 : amrex::ParmParse::queryarr(name.c_str(), vals);
357 6 : if (vals.size() == 9)
358 : {
359 : #if AMREX_SPACEDIM==2
360 4 : Util::Warning(file,func,line,"Reading a 3D matrix (",full(name),")into a 2D code - some values will be ignored.");
361 4 : value(0,0) = vals[0]; value(0,1)= vals[1];
362 4 : value(1,0) = vals[3]; value(1,1)= vals[4];
363 : #endif
364 : #if AMREX_SPACEDIM==3
365 2 : value(0,0) = vals[0]; value(0,1)= vals[1]; value(0,2)= vals[2];
366 2 : value(1,0) = vals[3]; value(1,1)= vals[4]; value(1,2)= vals[5];
367 2 : value(2,0) = vals[6]; value(2,1)= vals[7]; value(2,2)= vals[8];
368 : #endif
369 : }
370 0 : else if (vals.size() == 4)
371 : {
372 : #if AMREX_SPACEDIM==2
373 0 : value(0,0) = vals[0]; value(0,1)= vals[1];
374 0 : value(1,0) = vals[2]; value(1,1)= vals[3];
375 : #endif
376 : #if AMREX_SPACEDIM==3
377 0 : Util::Warning(file,func,line,"Reading a 2D matrix (",full(name),")into a 3D code - remaining values will be set to zero.");
378 0 : value(0,0) = vals[0]; value(0,1)= vals[1]; value(0,2)= 0.0;
379 0 : value(1,0) = vals[2]; value(1,1)= vals[3]; value(1,2)= 0.0;
380 0 : value(2,0) = 0.0; value(2,1)= 0.0; value(2,2)= 0.0;
381 : #endif
382 : }
383 : else
384 : {
385 0 : Util::ParmParseException(INFO,file,func,line,full(name),full(name)," needs either 4 or 9 components, but got ",vals.size());
386 : }
387 12 : return 0;
388 : }
389 : template<typename T>
390 9 : int queryarr_required( std::string name, std::vector<T> & value,
391 : std::string file, std::string func, int line)
392 : {
393 9 : if (!contains(name.c_str()))
394 : {
395 0 : Util::ParmParseException(INFO,file,func,line,full(name),"required value for ",full(name)," missing");
396 : }
397 9 : return queryarr(name,value,file,func,line);
398 : }
399 :
400 : int AnyUnusedInputs()
401 : {
402 : int cnt = 0;
403 : for (auto li = m_table->begin(), End = m_table->end(); li != End; ++li)
404 : {
405 : if (!li->second.m_count && li->first.rfind(getPrefix()+".",0) != std::string::npos)
406 : {
407 : Util::Warning(INFO,li->first);
408 : cnt++;
409 : }
410 : }
411 : return cnt;
412 : }
413 :
414 117 : std::vector<std::string> GetUnusedInputs()
415 : {
416 117 : std::vector<std::string> ret;
417 14995 : for (auto li = m_table->begin(), End = m_table->end(); li != End; ++li)
418 : {
419 14878 : if (!li->second.m_count && li->first.rfind(getPrefix()+".",0) != std::string::npos)
420 : {
421 0 : ret.push_back(li->first);
422 : }
423 : }
424 117 : return ret;
425 : }
426 :
427 : static int AllUnusedInputs()
428 : {
429 : ParmParse pp;
430 : int cnt = 0;
431 : for (auto li = pp.m_table->begin(), End = pp.m_table->end(); li != End; ++li)
432 : {
433 : if (!li->second.m_count)
434 : {
435 : Util::Warning(INFO,li->first);
436 : cnt++;
437 : }
438 : }
439 : return cnt;
440 : }
441 0 : std::string prefix ()
442 : {
443 0 : return getPrefix();
444 : }
445 2284 : std::string full (std::string name)
446 : {
447 4568 : std::string prefix = getPrefix();
448 3475 : if (prefix != "") return getPrefix() + "." + name;
449 1093 : else return name;
450 : }
451 :
452 :
453 : using amrex::ParmParse::queryarr;
454 : template<class T>
455 : void queryclass(std::string name, T * value,
456 : std::string file = "", std::string func = "", int line = -1)
457 : {
458 : auto old_prefix = m_prefix;
459 : try
460 : {
461 : if (old_prefix.empty()) m_prefix = name;
462 : else m_prefix.append(".").append(name);
463 : T::Parse(*value, *this);
464 : std::vector<std::string> unused_inputs = GetUnusedInputs();
465 : if (unused_inputs.size())
466 : {
467 : std::stringstream ss;
468 : for (unsigned int i=0; i < unused_inputs.size(); i++)
469 : ss << "\n\t" << unused_inputs[i];
470 : Util::ParmParseException(INFO,file,func,line,name,"The following inputs were specified but not used",ss.str());
471 : }
472 : }
473 : catch (...)
474 : {
475 : m_prefix = old_prefix;
476 : Util::ParmParseException(INFO,file,func,line,full(name));
477 : }
478 : m_prefix = old_prefix;
479 : }
480 : template<class T>
481 117 : void queryclass(std::string name, T & value,
482 : std::string file = "", std::string func = "", int line = __LINE__)
483 : {
484 234 : auto old_prefix = m_prefix;
485 : try
486 : {
487 117 : if (old_prefix.empty()) m_prefix = name;
488 0 : else m_prefix.append(".").append(name);
489 117 : T::Parse(value, *this);
490 234 : std::vector<std::string> unused_inputs = GetUnusedInputs();
491 117 : if (unused_inputs.size())
492 : {
493 0 : std::stringstream ss;
494 0 : for (unsigned int i=0; i < unused_inputs.size(); i++)
495 0 : ss << "\n\t" << unused_inputs[i];
496 0 : Util::ParmParseException(INFO,file,func,line,name,"The following inputs were specified but not used",ss.str());
497 : }
498 : }
499 0 : catch (...)
500 : {
501 0 : m_prefix = old_prefix;
502 0 : Util::ParmParseException(INFO,file,func,line,full(name));
503 : }
504 117 : m_prefix = old_prefix;
505 117 : }
506 : template<class T>
507 : void queryclass(T * value,
508 : std::string file = "", std::string func = "", int line = __LINE__)
509 : {
510 : try
511 : {
512 : T::Parse(*value, *this);
513 : }
514 : catch (...)
515 : {
516 : Util::ParmParseException(INFO,file,func,line,getPrefix());
517 : }
518 : }
519 : template<class T>
520 450 : void queryclass(T & value,
521 : std::string file = "", std::string func = "", int line = __LINE__)
522 : {
523 : try
524 : {
525 450 : T::Parse(value, *this);
526 : }
527 0 : catch (...)
528 : {
529 0 : Util::ParmParseException(INFO,file,func,line,getPrefix());
530 : }
531 450 : }
532 :
533 : template<typename... IC, typename... Args, typename PTRTYPE>
534 45 : void select (std::string name, PTRTYPE *& ic_eta, Args&&... args)
535 : {
536 90 : std::string type = "";
537 :
538 45 : this->query_required(name + ".type", type);
539 :
540 114 : bool matched = ((type == IC::name
541 69 : ? (ic_eta = new IC(std::forward<Args>(args)..., (*this), name + "." + std::string(IC::name))),
542 : true
543 : : false) || ...);
544 45 : if (!matched)
545 0 : Util::Exception(INFO,type," not a valid type for ",name);
546 45 : }
547 : };
548 : }
549 : #endif
|