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_queryarr_default(...) pp.queryarr_default(__VA_ARGS__,INFO)
106 : #define pp_query(...) pp.query(__VA_ARGS__)
107 : #define pp_queryclass(...) pp.queryclass(__VA_ARGS__,INFO)
108 : #define pp_forbid(...) pp.forbid(__VA_ARGS__,INFO)
109 :
110 :
111 : namespace IO
112 : {
113 : class ParmParse : public amrex::ParmParse
114 : {
115 : private:
116 917 : void Define()
117 : {
118 917 : if (checked_for_input_files) return;
119 38 : int k = 0;
120 38 : std::string inputfile = "";
121 38 : while (this->querykth("input",k,inputfile))
122 : {
123 0 : Util::Message(INFO,"Including inputs from "+inputfile);
124 0 : this->addfile(inputfile);
125 0 : k++;
126 : }
127 38 : checked_for_input_files = true;
128 38 : }
129 : static bool checked_for_input_files;
130 :
131 : public:
132 648 : ParmParse(std::string arg) : amrex::ParmParse::ParmParse(arg) {Define();} ;
133 269 : ParmParse() : amrex::ParmParse::ParmParse() {Define();} ;
134 8216 : std::string getPrefix() const {return m_prefix;};
135 396 : void ignore(std::string name)
136 : {
137 396 : (void)amrex::ParmParse::contains(name.c_str());
138 396 : }
139 :
140 6 : void pushPrefix(const std::string prefix)
141 : {
142 6 : if (m_prefix.length())
143 3 : m_prefix = m_prefix + "." + prefix;
144 : else
145 3 : m_prefix = prefix;
146 6 : }
147 :
148 6 : void popPrefix()
149 : {
150 6 : size_t pos = m_prefix.rfind('.');
151 6 : if (pos == std::string::npos)
152 3 : m_prefix = "";
153 : else
154 3 : m_prefix = m_prefix.substr(0, pos);
155 6 : }
156 :
157 :
158 275 : void forbid(std::string name, std::string explanation,
159 : std::string file = "", std::string func = "", int line = -1)
160 : {
161 275 : if (amrex::ParmParse::contains(full(name).c_str()))
162 : {
163 0 : Util::ParmParseException(INFO,file,func,line,full(name),full(name)," forbidden: ", explanation);
164 : }
165 275 : std::set<std::string> subs = amrex::ParmParse::getEntries(full(name));
166 275 : if (subs.size())
167 : {
168 0 : Util::ParmParseException(INFO,file,func,line,full(name),full(name)," forbidden: ", explanation);
169 : }
170 275 : }
171 :
172 2738 : bool contains(std::string name)
173 : {
174 2738 : if (amrex::ParmParse::contains(name.c_str()))
175 1049 : return true;
176 1689 : if (amrex::ParmParse::contains(full(name).c_str()))
177 0 : return true;
178 : {
179 1689 : std::set<std::string> subs = amrex::ParmParse::getEntries(name.c_str());
180 1689 : if (subs.size())
181 52 : return true;
182 1689 : }
183 : {
184 1637 : std::set<std::string> subs = amrex::ParmParse::getEntries(full(name).c_str());
185 1637 : if (subs.size())
186 0 : return true;
187 1637 : }
188 1637 : return false;
189 : }
190 :
191 : template<typename T>
192 264 : int query_required( std::string name, T & value,
193 : std::string file = "", std::string func = "", int line = -1)
194 : {
195 528 : if (!contains(name.c_str()))
196 : {
197 0 : Util::ParmParseException(INFO,file,func,line,full(name),"required value for ",full(name)," missing");
198 : }
199 264 : return query(name.c_str(),value);
200 : }
201 :
202 : template<typename T>
203 1302 : int query_default( std::string name, T & value, T defaultvalue,
204 : std::string = "", std::string = "", int = -1)
205 : {
206 2604 : if (!contains(name.c_str()))
207 : {
208 1015 : add(name.c_str(),defaultvalue);
209 : }
210 1302 : return query(name.c_str(),value);
211 : }
212 :
213 1 : int query_validate( std::string name, int & value, std::vector<int> possibleintvals,
214 : std::string file = "", std::string func = "", int line = -1)
215 : {
216 : // First value is accepted by default...
217 :
218 : // set default value
219 1 : value = possibleintvals[0];
220 :
221 : // get the read value (if it exists)
222 1 : int retval = query(name.c_str(),value);
223 :
224 : // check to make sure the read value matches one of the inpus
225 1 : bool ok = false;
226 4 : for (unsigned int i = 0; i < possibleintvals.size(); i++)
227 : {
228 3 : if (value == possibleintvals[i]) ok = true;
229 : }
230 :
231 1 : if (ok) return retval;
232 :
233 0 : std::stringstream ss;
234 0 : ss << possibleintvals[0];
235 0 : for (unsigned int i = 1; i < possibleintvals.size(); i++)
236 0 : ss << "," << possibleintvals[i];
237 :
238 0 : Util::ParmParseException(INFO,file,func,line,full(name),"' expected [", ss.str(), "] but got ", value);
239 :
240 0 : return -1;
241 0 : }
242 :
243 :
244 94 : int query_validate( std::string name, std::string & value, std::vector<const char *> possiblecharvals, bool firstbydefault,
245 : std::string file = "", std::string func = "", int line = -1)
246 : {
247 : // if not using default values, then the input must be specified
248 94 : if (!firstbydefault)
249 : {
250 0 : if (!contains(name.c_str()))
251 : {
252 0 : Util::ParmParseException(INFO,file,func,line,full(name),"required value for ",full(name)," missing");
253 : }
254 : }
255 :
256 : // set default value
257 94 : value = std::string(possiblecharvals[0]);
258 :
259 : // get the read value (if it exists)
260 94 : int retval = query(name.c_str(),value);
261 :
262 : // check to make sure the read value matches one of the inpus
263 94 : bool ok = false;
264 370 : for (unsigned int i = 0; i < possiblecharvals.size(); i++)
265 : {
266 552 : if (value == std::string(possiblecharvals[i])) ok = true;
267 : }
268 :
269 94 : if (ok) return retval;
270 :
271 0 : std::stringstream ss;
272 0 : ss << possiblecharvals[0];
273 0 : for (unsigned int i = 1; i < possiblecharvals.size(); i++)
274 0 : ss << "," << possiblecharvals[i];
275 :
276 0 : Util::ParmParseException(INFO,file,func,line,full(name),"' expected [", ss.str(), "] but got ", value);
277 :
278 0 : return -1;
279 0 : }
280 :
281 94 : int query_validate( std::string name, std::string & value, std::vector<const char *> possiblecharvals,
282 : std::string file = "", std::string func = "", int line = -1)
283 : {
284 94 : return query_validate(name,value,possiblecharvals,true,file,func,line);
285 : }
286 :
287 :
288 : // special case for strings
289 163 : int query_default( std::string name, std::string & value, const char *defaultvalue,
290 : std::string file = "", std::string func = "", int line = -1)
291 : {
292 489 : return query_default(name, value, std::string(defaultvalue), file, func, line);
293 : }
294 : // special case for bools
295 61 : int query_default( std::string name, int & value, bool defaultvalue,
296 : std::string file = "", std::string func = "query_default", int line = -1)
297 : {
298 61 : int defaultint = 0;
299 61 : if (defaultvalue) defaultint = 1;
300 61 : return query_default(name, value, defaultint, file, func, line);
301 : }
302 :
303 :
304 :
305 : // validate filenames
306 1 : int query_file( std::string name, std::string & value, bool copyfile, bool checkfile,
307 : std::string file = "", std::string func = "query_file", int line = -1)
308 : {
309 : try
310 : {
311 2 : if (!contains(name.c_str()))
312 : {
313 0 : Util::ParmParseException(INFO,file,func,line,full(name),full(name)," must be specified");
314 : }
315 :
316 1 : int retval = query(name.c_str(),value);
317 :
318 1 : if (amrex::ParallelDescriptor::IOProcessor())
319 : {
320 1 : if ( checkfile && ! std::filesystem::exists(value))
321 : {
322 0 : Util::ParmParseException(INFO,file,func,line,full(name),full(name)," does not exist");
323 : }
324 1 : if ( checkfile && !std::filesystem::is_regular_file(value))
325 : {
326 0 : Util::ParmParseException(INFO,file,func,line,full(name),full(name)," is not a regular file");
327 : }
328 1 : if ( copyfile )
329 : {
330 1 : Util::CopyFileToOutputDir(value, true, full(name));
331 : }
332 : }
333 1 : return retval;
334 : }
335 0 : catch (...)
336 : {
337 0 : Util::ParmParseException(INFO,file,func,line,full(name));
338 0 : }
339 0 : return -1;
340 : }
341 : int query_file( std::string name, std::string & value, bool copyfile,
342 : std::string file = "", std::string func = "query_file", int line = -1)
343 : {
344 : return query_file(name,value,copyfile,true,file,func,line);
345 : }
346 1 : int query_file( std::string name, std::string & value,
347 : std::string file = "", std::string func = "query_file", int line = -1)
348 : {
349 1 : return query_file(name,value,true,true,file,func,line);
350 : }
351 :
352 :
353 : template<typename T>
354 180 : int queryarr( std::string name, std::vector<T> & value,
355 : std::string /*file*/, std::string /*func*/, int /*line*/)
356 : {
357 180 : return amrex::ParmParse::queryarr(name.c_str(),value);
358 : }
359 9 : int queryarr( std::string name, Set::Vector & value,
360 : std::string file = "", std::string func = "queryarr", int line = -1)
361 : {
362 9 : std::vector<Set::Scalar> vals;
363 9 : amrex::ParmParse::queryarr(name.c_str(), vals);
364 9 : if (vals.size() < AMREX_SPACEDIM)
365 : {
366 0 : Util::ParmParseException(INFO,file,func,line,full(name),full(name)," requires at least ", AMREX_SPACEDIM, " arguments, got ",vals.size());
367 : }
368 28 : for (int i = 0; i < AMREX_SPACEDIM; i++) value(i) = vals[i];
369 9 : return 0;
370 9 : }
371 6 : int queryarr( std::string name, Set::Matrix & value,
372 : std::string file = "", std::string func = "queryarr", int line = -1)
373 : {
374 6 : std::vector<Set::Scalar> vals;
375 6 : amrex::ParmParse::queryarr(name.c_str(), vals);
376 6 : if (vals.size() == 9)
377 : {
378 : #if AMREX_SPACEDIM==2
379 4 : Util::Warning(file,func,line,"Reading a 3D matrix (",full(name),")into a 2D code - some values will be ignored.");
380 4 : value(0,0) = vals[0]; value(0,1)= vals[1];
381 4 : value(1,0) = vals[3]; value(1,1)= vals[4];
382 : #endif
383 : #if AMREX_SPACEDIM==3
384 2 : value(0,0) = vals[0]; value(0,1)= vals[1]; value(0,2)= vals[2];
385 2 : value(1,0) = vals[3]; value(1,1)= vals[4]; value(1,2)= vals[5];
386 2 : value(2,0) = vals[6]; value(2,1)= vals[7]; value(2,2)= vals[8];
387 : #endif
388 : }
389 0 : else if (vals.size() == 4)
390 : {
391 : #if AMREX_SPACEDIM==2
392 0 : value(0,0) = vals[0]; value(0,1)= vals[1];
393 0 : value(1,0) = vals[2]; value(1,1)= vals[3];
394 : #endif
395 : #if AMREX_SPACEDIM==3
396 0 : Util::Warning(file,func,line,"Reading a 2D matrix (",full(name),")into a 3D code - remaining values will be set to zero.");
397 0 : value(0,0) = vals[0]; value(0,1)= vals[1]; value(0,2)= 0.0;
398 0 : value(1,0) = vals[2]; value(1,1)= vals[3]; value(1,2)= 0.0;
399 0 : value(2,0) = 0.0; value(2,1)= 0.0; value(2,2)= 0.0;
400 : #endif
401 : }
402 : else
403 : {
404 0 : Util::ParmParseException(INFO,file,func,line,full(name),full(name)," needs either 4 or 9 components, but got ",vals.size());
405 : }
406 6 : return 0;
407 6 : }
408 : template<typename T>
409 47 : int queryarr_required( std::string name, std::vector<T> & value,
410 : std::string file, std::string func, int line)
411 : {
412 94 : if (!contains(name.c_str()))
413 : {
414 0 : Util::ParmParseException(INFO,file,func,line,full(name),"required value for ",full(name)," missing");
415 : }
416 47 : return queryarr(name,value,file,func,line);
417 : }
418 :
419 :
420 456 : int queryarr_default( std::string name, std::vector<std::string> & value, std::string defaultvalue,
421 : std::string = "", std::string = "", int = -1)
422 : {
423 912 : if (!contains(name.c_str()))
424 : {
425 224 : add(name.c_str(),defaultvalue);
426 : }
427 456 : return queryarr(name.c_str(),value);
428 : }
429 :
430 :
431 :
432 : template <typename T>
433 : int
434 18 : queryclass_enumerate(std::string a_name, std::vector<T> &value, int number = 1,
435 : std::string file = "", std::string func = "", int line = __LINE__)
436 : {
437 18 : value.clear();
438 :
439 : //
440 : // If only one is present with no subscript, then read
441 : // it only and return.
442 : //
443 18 : std::string name = a_name;
444 18 : if (this->contains(name))
445 : {
446 8 : for (int n = 0; n < number; n++)
447 : {
448 4 : T tmp;
449 4 : this->queryclass<T>(name, tmp, file, func, line);
450 4 : value.push_back(tmp);
451 : }
452 4 : return 0;
453 : }
454 :
455 : //
456 : // Some logic to determine whether we are starting with zero
457 : // (model0, model1, model2, ...)
458 : // or one
459 : // (model1, model2, model3, ...)
460 : // since both are supported
461 : //
462 14 : int start = -1;
463 14 : std::string name0 = a_name + std::to_string(0);
464 14 : std::string name1 = a_name + std::to_string(1);
465 28 : if (this->contains(name0.c_str()))
466 : {
467 0 : start = 0;
468 0 : name = name0;
469 : }
470 28 : else if (this->contains(name1.c_str()))
471 : {
472 14 : start = 1;
473 14 : name = name1;
474 : }
475 : else
476 : {
477 0 : Util::Exception(INFO,"Enumerations must begin with 0 or 1");
478 : }
479 :
480 : //
481 : // Iterate over items called (model0), model1, model2, etc
482 : // until no more are found then exit.
483 : //
484 76 : for (int cntr = start; this->contains(name.c_str()); cntr++)
485 : {
486 34 : if (this->contains(name.c_str()))
487 : {
488 17 : T tmp;
489 17 : this->queryclass<T>(name, tmp, file, func, line);
490 17 : value.push_back(tmp);
491 17 : }
492 17 : name = a_name + std::to_string(cntr+1);
493 : }
494 :
495 14 : return 0;
496 18 : }
497 :
498 :
499 : template <typename T>
500 : int
501 22 : query_enumerate(std::string a_name, std::vector<T> &value, int number = 1,
502 : std::string file = "", std::string func = "", int line = __LINE__)
503 : {
504 22 : value.clear();
505 :
506 : //
507 : // If only one is present with no subscript, then read
508 : // it only and return.
509 : //
510 22 : std::string name = a_name;
511 22 : if (this->contains(name))
512 : {
513 0 : for (int n = 0; n < number; n++)
514 : {
515 0 : T tmp;
516 0 : this->query_required(name, tmp, file, func, line);
517 0 : value.push_back(tmp);
518 : }
519 0 : return 0;
520 : }
521 :
522 : //
523 : // Some logic to determine whether we are starting with zero
524 : // (model0, model1, model2, ...)
525 : // or one
526 : // (model1, model2, model3, ...)
527 : // since both are supported
528 : //
529 22 : int start = -1;
530 22 : std::string name0 = a_name + std::to_string(0);
531 22 : std::string name1 = a_name + std::to_string(1);
532 44 : if (this->contains(name0.c_str()))
533 : {
534 22 : start = 0;
535 22 : name = name0;
536 : }
537 0 : else if (this->contains(name1.c_str()))
538 : {
539 0 : start = 1;
540 0 : name = name1;
541 : }
542 : else
543 : {
544 0 : Util::Exception(INFO,"Enumerations must begin with 0 or 1");
545 : }
546 :
547 : //
548 : // Iterate over items called (model0), model1, model2, etc
549 : // until no more are found then exit.
550 : //
551 124 : for (int cntr = start; this->contains(name.c_str()); cntr++)
552 : {
553 58 : if (this->contains(name.c_str()))
554 : {
555 29 : T tmp;
556 29 : this->query_required(name, tmp, file, func, line);
557 29 : value.push_back(tmp);
558 29 : }
559 29 : name = a_name + std::to_string(cntr+1);
560 : }
561 :
562 22 : return 0;
563 22 : }
564 :
565 :
566 :
567 9 : int AnyUnusedInputs(bool inscopeonly = true, bool verbose = false)
568 : {
569 9 : int cnt = 0;
570 1390 : for (auto li = m_table->begin(), End = m_table->end(); li != End; ++li)
571 : {
572 1381 : if (!li->second.m_count)
573 : {
574 0 : if (inscopeonly && getPrefix() != "")
575 : {
576 0 : if (li->first.rfind(getPrefix()+".",0) != std::string::npos)
577 : {
578 0 : if (verbose) Util::Warning(INFO,li->first);
579 0 : cnt++;
580 : }
581 : }
582 : else
583 : {
584 0 : if (verbose) Util::Warning(INFO,li->first);
585 0 : cnt++;
586 : }
587 : }
588 : }
589 9 : return cnt;
590 : }
591 :
592 199 : std::vector<std::string> GetUnusedInputs()
593 : {
594 199 : std::vector<std::string> ret;
595 25197 : for (auto li = m_table->begin(), End = m_table->end(); li != End; ++li)
596 : {
597 24998 : if (!li->second.m_count && li->first.rfind(getPrefix()+".",0) != std::string::npos)
598 : {
599 0 : ret.push_back(li->first);
600 : }
601 : }
602 199 : return ret;
603 0 : }
604 :
605 0 : static int AllUnusedInputs()
606 : {
607 0 : ParmParse pp;
608 0 : int cnt = 0;
609 0 : for (auto li = pp.m_table->begin(), End = pp.m_table->end(); li != End; ++li)
610 : {
611 0 : if (!li->second.m_count)
612 : {
613 0 : Util::Warning(INFO,li->first);
614 0 : cnt++;
615 : }
616 : }
617 0 : return cnt;
618 0 : }
619 0 : std::string prefix ()
620 : {
621 0 : return getPrefix();
622 : }
623 3881 : std::string full (std::string name)
624 : {
625 3881 : std::string prefix = getPrefix();
626 3881 : if (prefix != "") return getPrefix() + "." + name;
627 1854 : else return name;
628 3881 : }
629 :
630 :
631 : using amrex::ParmParse::queryarr;
632 : template<class T>
633 : void queryclass(std::string name, T * value,
634 : std::string file = "", std::string func = "", int line = -1)
635 : {
636 : auto old_prefix = m_prefix;
637 : try
638 : {
639 : if (old_prefix.empty()) m_prefix = name;
640 : else m_prefix.append(".").append(name);
641 : T::Parse(*value, *this);
642 : std::vector<std::string> unused_inputs = GetUnusedInputs();
643 : if (unused_inputs.size())
644 : {
645 : std::stringstream ss;
646 : for (unsigned int i=0; i < unused_inputs.size(); i++)
647 : ss << "\n\t" << unused_inputs[i];
648 : Util::ParmParseException(INFO,file,func,line,name,"The following inputs were specified but not used",ss.str());
649 : }
650 : }
651 : catch (...)
652 : {
653 : m_prefix = old_prefix;
654 : Util::ParmParseException(INFO,file,func,line,full(name));
655 : }
656 : m_prefix = old_prefix;
657 : }
658 : template<class T>
659 199 : void queryclass(std::string name, T & value,
660 : std::string file = "", std::string func = "", int line = __LINE__)
661 : {
662 199 : auto old_prefix = m_prefix;
663 : try
664 : {
665 199 : if (old_prefix.empty()) m_prefix = name;
666 0 : else m_prefix.append(".").append(name);
667 199 : T::Parse(value, *this);
668 199 : std::vector<std::string> unused_inputs = GetUnusedInputs();
669 199 : if (unused_inputs.size())
670 : {
671 0 : std::stringstream ss;
672 0 : for (unsigned int i=0; i < unused_inputs.size(); i++)
673 0 : ss << "\n\t" << unused_inputs[i];
674 0 : Util::ParmParseException(INFO,file,func,line,name,"The following inputs were specified but not used",ss.str());
675 0 : }
676 199 : }
677 0 : catch (...)
678 : {
679 0 : m_prefix = old_prefix;
680 0 : Util::ParmParseException(INFO,file,func,line,full(name));
681 : }
682 199 : m_prefix = old_prefix;
683 199 : }
684 :
685 : template<class T>
686 : void queryclass(T * value,
687 : std::string file = "", std::string func = "", int line = __LINE__)
688 : {
689 : try
690 : {
691 : T::Parse(*value, *this);
692 : }
693 : catch (...)
694 : {
695 : Util::ParmParseException(INFO,file,func,line,getPrefix());
696 : }
697 : }
698 : template<class T>
699 459 : void queryclass(T & value,
700 : std::string file = "", std::string func = "", int line = __LINE__)
701 : {
702 : try
703 : {
704 459 : T::Parse(value, *this);
705 : }
706 0 : catch (...)
707 : {
708 0 : Util::ParmParseException(INFO,file,func,line,getPrefix());
709 : }
710 459 : }
711 :
712 : //
713 : // Variadic template parsing operator to assign a pointer to
714 : // one of a set of possible class objects, then call that method's
715 : // Parse function.
716 : //
717 : // If there is more than one instantiating class, then type must be set.
718 : // Otherwise, no type is necessary.
719 : //
720 : template<typename... IC, typename... Args, typename PTRTYPE>
721 44 : void select (std::string name, PTRTYPE *& ic_eta, Args&&... args)
722 : {
723 : // if there is only one IC arg provided, we don't need to check the type - assume that
724 : // it is the default.
725 : if constexpr (sizeof...(IC) == 0)
726 : {
727 : using first_IC = std::tuple_element_t<0, std::tuple<IC...>>;
728 : ic_eta = new first_IC(std::forward<Args>(args)..., (*this), name + "." + std::string(first_IC::name));
729 : }
730 : // otherwise, check the type.
731 : else
732 : {
733 88 : std::string type = "";
734 132 : this->query_required(name + ".type", type);
735 48 : bool matched = (( type == IC::name
736 157 : ? (ic_eta = new IC(std::forward<Args>(args)..., (*this), name + "." + std::string(IC::name))),
737 : true
738 114 : : false) || ...);
739 44 : if (!matched)
740 0 : Util::Exception(INFO, type, " not a valid type for ", name);
741 44 : }
742 44 : }
743 :
744 : //
745 : // Identical to the above, except sets the type automatically to the
746 : // firs specified
747 : //
748 : template<typename FirstIC, typename... IC, typename... Args, typename PTRTYPE>
749 106 : void select_default (std::string name, PTRTYPE *& ic_eta, Args&&... args)
750 : {
751 212 : std::string type = "";
752 :
753 318 : this->query_default(name + ".type", type, FirstIC::name);
754 :
755 0 : bool matched =
756 212 : (( type == FirstIC::name
757 266 : ? (ic_eta = new FirstIC(std::forward<Args>(args)..., (*this), name + "." + std::string(FirstIC::name))),
758 : true : false))
759 212 : ||
760 26 : (( type == IC::name
761 180 : ? (ic_eta = new IC(std::forward<Args>(args)..., (*this), name + "." + std::string(IC::name))),
762 : true : false) || ...);
763 :
764 :
765 106 : if (!matched)
766 0 : Util::Exception(INFO,type," not a valid type for ",name);
767 106 : }
768 :
769 :
770 : //
771 : //
772 : template<typename... IC, typename... Args, typename PTRTYPE>
773 : void select_enumerate (std::string a_name, std::vector<PTRTYPE*> & value, Args&&... args)
774 : {
775 :
776 : value.clear();
777 :
778 : //
779 : // If only one is present with no subscript, then read
780 : // it only and return.
781 : //
782 : std::string name = a_name;
783 : if (this->contains(name))
784 : {
785 : PTRTYPE *tmp;
786 : this->select<IC...>(a_name, tmp, args...);
787 : value.push_back(tmp);
788 : return;
789 : }
790 :
791 : //
792 : // Some logic to determine whether we are starting with zero
793 : // (model0, model1, model2, ...)
794 : // or one
795 : // (model1, model2, model3, ...)
796 : // since both are supported
797 : //
798 : int start = -1;
799 : std::string name0 = a_name + std::to_string(0);
800 : std::string name1 = a_name + std::to_string(1);
801 : if (this->contains(name0.c_str()))
802 : {
803 : start = 0;
804 : name = name0;
805 : }
806 : else if (this->contains(name1.c_str()))
807 : {
808 : start = 1;
809 : name = name1;
810 : }
811 : else
812 : {
813 : Util::Exception(INFO,"Enumerations must begin with 0 or 1");
814 : }
815 :
816 : //
817 : // Iterate over items called (model0), model1, model2, etc
818 : // until no more are found then exit.
819 : //
820 : for (int cntr = start; this->contains(name.c_str()); cntr++)
821 : {
822 : if (this->contains(name.c_str()))
823 : {
824 : PTRTYPE *tmp;
825 : this->select<IC...>(name, tmp, args...);
826 : value.push_back(tmp);
827 : }
828 : name = a_name + std::to_string(cntr+1);
829 : }
830 : }
831 :
832 :
833 :
834 : //
835 : // Similar to select but specialized for main functions
836 : //
837 : template<typename... INTEGRATOR, typename... Args, typename PTRTYPE>
838 : void select_main (PTRTYPE *& ic_eta, Args&&... args)
839 : {
840 : std::string type = "";
841 :
842 : this->query_required("alamo.program", type);
843 :
844 : bool matched = ((type == INTEGRATOR::name
845 : ? (ic_eta = new INTEGRATOR(std::forward<Args>(args)..., (*this))),
846 : true
847 : : false) || ...);
848 : if (!matched)
849 : Util::Exception(INFO,type," not a valid type for ",type);
850 : }
851 :
852 : //
853 : // Similar to select_main but works for one function only
854 : // and doesn't require a type specifier.
855 : //
856 :
857 : // with variadic arguments
858 : template<typename INTEGRATOR, typename Args, typename PTRTYPE>
859 : void select_only (PTRTYPE *& ic_eta, Args&& args)
860 : {
861 : ic_eta = new INTEGRATOR(std::forward<Args>(args), (*this));
862 : }
863 : // without variadic arguments
864 : template<typename INTEGRATOR, typename PTRTYPE>
865 36 : void select_only (PTRTYPE *& ic_eta)
866 : {
867 36 : ic_eta = new INTEGRATOR((*this));
868 36 : }
869 :
870 :
871 : //
872 : // functionally simlar to other kinds of select, execpt works on
873 : // template-based static dispatch.
874 : //
875 :
876 : template <typename... OBJ, typename CLASS>
877 3 : void select(std::string name, CLASS& value)
878 : {
879 3 : pushPrefix(name);
880 3 : static_polymorphism_parser<CLASS, 0, OBJ...>(value);
881 3 : popPrefix();
882 3 : }
883 :
884 : private:
885 : //
886 : // This function is a virtual "swtich / case" block for static
887 : // dispatch. It works on a faux-virtual class "CLASS" that must
888 : // contain a tuple of "obj" with each "obj" being a derived type.
889 : // Recursion is used to unroll the list, identify the correct class
890 : // based on "name/type", set the "selected" index, then call the
891 : // appropriate class' Parse function.
892 : //
893 : // This is only for use by the "select" functions.
894 : //
895 : template <typename CLASS, int N, typename... OBJ>
896 4 : void static_polymorphism_parser(CLASS& value)
897 : {
898 : if constexpr (N == 0)
899 : {
900 3 : std::string type;
901 15 : query_default("type",type,value.names[0]);
902 :
903 12 : for (unsigned int i = 0; i < sizeof...(OBJ); i++)
904 9 : if (type == value.names[i])
905 3 : value.selected = i;
906 :
907 3 : if (value.selected < 0)
908 0 : Util::Exception(INFO,
909 : "Error reading " + getPrefix() +
910 : ", invalid type " + type);
911 :
912 :
913 3 : pushPrefix(type);
914 3 : }
915 :
916 : if constexpr (N < sizeof...(OBJ))
917 : {
918 4 : if (value.selected == N)
919 : {
920 3 : std::get<N>(value.obj).Parse(std::get<N>(value.obj), *this);
921 3 : popPrefix();
922 3 : return;
923 : }
924 : else
925 1 : return static_polymorphism_parser<CLASS, N+1, OBJ...>(value);
926 : }
927 0 : else Util::Abort(INFO);
928 0 : }
929 :
930 : public:
931 :
932 : template <int N>
933 17 : void query_exactly(std::vector<std::string> names, std::pair<std::string, Set::Scalar> values[N])
934 : {
935 17 : int cnt = 0;
936 17 : std::vector<std::string> read;
937 102 : for (unsigned int n = 0; n < names.size(); n++)
938 : {
939 85 : if (amrex::ParmParse::contains(names[n].c_str()))
940 : {
941 34 : read.push_back(names[n]);
942 34 : cnt++;
943 : }
944 : }
945 136 : Util::AssertException( INFO, TEST(cnt == N), "incorrect number of values specified: only ", N,
946 : " values are allowed, but received ",Util::String::Join(read));
947 :
948 :
949 17 : cnt = 0;
950 102 : for (unsigned int n = 0; n < names.size(); n++)
951 : {
952 85 : if (amrex::ParmParse::contains(names[n].c_str()))
953 : {
954 34 : values[cnt].first = names[n];
955 102 : query_required(names[n],values[cnt].second);
956 34 : cnt++;
957 : }
958 : }
959 17 : }
960 :
961 :
962 :
963 :
964 : };
965 : }
966 : #endif
|