HILA
Loading...
Searching...
No Matches
input.h
Go to the documentation of this file.
1#ifndef HILA_PARAM_INPUT_H
2#define HILA_PARAM_INPUT_H
3
4/** @file input.h */
5
6#include <string>
7#include "defs.h"
8#include "lattice.h"
9#include "plumbing/com_mpi.h"
10
11namespace hila {
12
13/**
14 * @brief hila::input - Class for parsing runtime parameter files.
15 *
16 * @details Input files consist normally of "key <value>" -pairs.
17 *
18 * It is important to note that the data structure employed by the input class is not a dictionary,
19 * but technically a stack. This means that the values are read in sequentially, thus they must be
20 * in the file in the order they are read in.
21 *
22 * In examples of other methods we will refer to the input object as f. Creating the input object is
23 * done with:
24 *
25 * \code{.cpp}
26 * hila::input f("filename"); // initialization with filename opens the file for input
27 * \endcode
28 *
29 * One can also initialize the input object without specifying the filename:
30 *
31 * \code{.cpp}
32 * hila::input f; // initialization with filename opens the file for input
33 * \endcode
34 *
35 * in which case the `hila::input::open` method needs to be called separately.
36 *
37 * __Comment character '#'__: everything after # in input file is a comment to the end of the line.
38 *
39 * __NOTE__: methods which broadcast to all nodes (default) must be called from all nodes
40 * synchronously. These include open(), get(), get_value() with bcast=true, get_item with
41 * bcast=true.
42 *
43 * Thus:
44 * \code{.cpp}
45 * if (hila::myrank() == 0) {
46 * double v = f.get("a value");
47 * ...
48 * }
49 * \endcode
50 * deadlocks (if there are more than 1 rank). Method `f.get_value(v,"a value",false)` can be used in
51 * this context.
52 */
53class input {
54
55 private:
56 std::ifstream inputfile;
57 bool is_initialized = false;
58 std::string filename;
59 bool use_cin;
60 int file_number; // running file index
61
62 std::string linebuffer;
63 size_t lb_start = 0; // linebuffer start index
64 bool is_line_printed;
65 bool speaking = true; // false does not print information
66
67 std::vector<std::string> cmdline_p;
68
69 public:
70 input() {}
71 ~input() {
72 close();
73 }
74 input(const std::string &fname) {
75 open(fname);
76 }
77
78 /**
79 * @brief Open file that parameters are read from
80 * @details If input class is initialized with path to file that needs to be opened,
81 * then the file will be opened automatically, and this method does not need to be called.
82 *
83 * If no argument is given then filename will be interperated as default input file name
84 *
85 * In the case that use_cmdline is True and if no argument is given and filename is given with
86 * -i {alternative_file} cmdline argument then {alternative_file} will be the specified file to
87 * be opened.
88 *
89 * @param fname Path to file that needs to be read
90 * @param use_cmdline Default True
91 * @param exit_on_error If true exit on error. Return value is passed to all MPI nodes. Default
92 * True
93 * @return true
94 * @return false
95 */
96 bool open(const std::string &fname, bool use_cmdline = true, bool exit_on_error = true);
97
98 /** @brief Closes input parameter file
99 * @details After `f.close()` the file can be reopened with `f.open("file")` and the stack is
100 * reset. File is also closed when variable "f" goes out of scope.
101 */
102 void close();
103
104 /**
105 * @brief Silence print output during file reading
106 * @details
107 * \code{.cpp}
108 * f.quiet(); // don't print read items to hila::out
109 * f.quiet(false); // re-enable printing
110 * \endcode
111 * By default hila::input methods print everything read to hila::out0 for logging. `f.quiet()`
112 * disables this.
113 *
114 * @param really
115 */
116 void quiet(bool really = true) {
117 speaking = !really;
118 }
119
120 /**
121 * @brief returntype is a special class for resolving get("label") return type
122 */
124 public:
125 std::string label;
126 input *parent;
127
128 returntype(const std::string &str, input *a) : label(str), parent(a) {}
129
130 /// cast operator does the conversion - disable nullptr_t cast used
131 /// in hila types
132 /// disable type "char" because then c++ does not know how to initialize strings
133 template <typename T, std::enable_if_t<(hila::is_complex_or_arithmetic<T>::value &&
134 !std::is_same<T, char>::value) ||
135 std::is_same<T, std::string>::value,
136 int> = 0>
137 operator T() {
138 T val;
139 if (!parent->get_value(val, label, true))
141 return val;
142 }
143
144 template <typename T, int n>
145 operator Vector<n, T>() {
146 Vector<n, T> val;
147 if (!parent->get_value(val, label, true))
149 return val;
150 }
151
152 operator CoordinateVector() {
154 if (!parent->get_value(val, label, true))
156 return val;
157 }
158
159 template <typename T, std::enable_if_t<hila::is_complex_or_arithmetic<T>::value ||
160 std::is_same<T, std::string>::value,
161 int> = 0>
162 operator std::vector<T>() {
163 std::vector<T> val;
164 if (!parent->get_value(val, label, true))
166 return val;
167 }
168 };
169
170 /**
171 * @brief Get next value in stack of read in input string from parameters file.
172 * @details Use as
173 * \code{.cpp}
174 * var = f.get("key");
175 * \endcode
176 * reads in a key-value pair
177 * \code{.txt}
178 * key <value(s)>
179 * \endcode
180 * from the input file f, and returns the value of type of variable var.
181 * The value is broadcast to all MPI nodes. The method infers
182 * the type of the returned variable from the type of the assignment.
183 *
184 * Key is an alphanumeric string, which may contain words separated by whitespace.
185 *
186 * The get method simply traverses the parsed in stack of input values in the given
187 * parameters file. A functionality of the method is that the given argument key is skipped and
188 * the value that this key is assigned to is then fetched and returned.
189 *
190 * Technically the key's can also be read in if no argument is given to get. The main
191 * functionality of the key argument is to book keep by the user that all parameters are read
192 * in. If the key that is wanting to be read doesn't exist, then get throws an error and the
193 * program is stopped.
194 *
195 * With this logic one could construct a parameters file of only values:
196 *
197 * \code {.txt}
198 * value_1
199 * value_2
200 * .
201 * .
202 * .
203 * value_n
204 * \endcode
205 *
206 * and read in the values in a loop by simply calling hila::input::get() consecutively, but this
207 * is not advised.
208 *
209 * The type of the value to be read in is inferred by the variable the value is fetched into. If
210 * the value cannot be casted into the variable, then the parser will throw an error and crash.
211 *
212 * ## Types which the parser supports with examples
213 *
214 * Multiple items are separated by commas, whitespace is not significant.
215 *
216 * ### Any arithmetic type (ints/floats)
217 * @code
218 * int i = f.get("number of cats");
219 * @endcode
220 * __matches__ " number of cats 5 "
221 *
222 * ### Complex<float/double>
223 * @code{.cpp}
224 * Complex<double> phase = f.get("complex phase");
225 * @endcode
226 * __matches__ "complex phase (0.4, 0.5)"
227 *
228 * complex values are given in pairs within ( , )
229 *
230 * ### std::string
231 * @code{.cpp}
232 * std::string s = f.get("key");
233 * @endcode
234 * __matches__ "key <string value>" where string value is either
235 *
236 * - sequence of non-whitespace chars, delimited by whitespace, eol, ','
237 * or '#'.
238 *
239 * - characters enclosed by quotes "..". These have to pair
240 * within the same line. Quote marks are removed.
241 *
242 * ### CoordinateVector
243 * @code{.cpp}
244 * CoordinateVector v;
245 * v = f.get("lattice size");
246 * @endcode
247 * __matches__ "lattice size 32, 32, 32, 32" (if NDIM == 4).
248 *
249 * ### Vector<T,int>
250 * @code{.cpp}
251 * Vector<5,int> vec = f.get("initial vector")
252 * @endcode
253 * __matches__ "initial vector 1, 2, 3, 4, 5"
254 *
255 * ### std::vector<T>
256 * @code{.cpp}
257 * std::vector<double> dvec = f.get("vec");
258 * @endcode
259 * __matches__ "vec 3,4, 5.5, 7.8, 4" and returns a vector of double values.
260 * The numbers are read until they are not followed by a comma. If comma
261 * is the last non-whitespace character on the line, reading continues to
262 * the next line.
263 *
264 * T is one of the other supported types.
265 *
266 * __NOTE__: The parser will crash if during reading in a multi valued line (Vector,
267 * std::vector) the line ends with a ",". The parser will interpret that there is data on the
268 * next line to read in and will fail during the next get call.
269 *
270 * @param key Parameter to be read in.
271 * @return returntype Value corresponding to input key
272 */
273 inline returntype get(const std::string &key) {
274 return returntype(key, this);
275 }
276
277 inline returntype get() {
278 return returntype("", this);
279 }
280
281 /**
282 * @brief Read input (alternative to get())
283 * @details
284 * Val can be any value used in get()-method above. If broadcast==false,
285 * the value is not broadcast to other nodes. The return value is false if
286 * the value could not be read successfully, true otherwise.
287 * This method does not exit on error (but an error message may be printed)
288 * Example:
289 * \code{.cpp}
290 * int i,j;
291 * bool success;
292 * success = get_value(i, "key", false); // only node 0 gets i
293 * success = get_value(j,"key2"); // all nodes get j
294 *\endcode
295 * __NOTE__: if broadcast == false the return value is correct only on node 0.
296 *
297 * Supported types same as for hila::input::get
298 * @tparam T
299 * @param val variable to store gotten value in. Infers the type for the fetched value
300 * @param label key to fetch value for
301 * @param bcast default true. If true the value will be broadcasted to all nodes
302 * @return true Returns true on success
303 * @return false Returns false if return value is correct only on node 0.
304 */
305 template <typename T>
306 bool get_value(T &val, const std::string &label, bool bcast = true) {
307 val = {};
308 bool no_error = handle_key(label); // removes whitespace
309
310 if (hila::myrank() == 0 && no_error) {
311 std::string tok;
312 if (!(get_token(tok) && is_value(tok, val))) {
313
314 if (speaking)
315 hila::out << "Error: expecting a value of type '" << type_id<T>() << "' after '"
316 << label << "'\n";
317
318 no_error = false;
319 }
320 }
321
322 if (bcast) {
323 // string has to be treated separately
324 if constexpr (std::is_same<T, std::string>::value) {
325 hila::broadcast(val);
326 hila::broadcast(no_error);
327 } else {
328 hila::broadcast2(val, no_error);
329 }
330 }
331
332 return no_error;
333 }
334
335 // get_value for Complex<T>
336 template <typename T>
337 bool get_value(Complex<T> &val, const std::string &label, bool bcast = true) {
338 val = 0;
339 bool no_error = handle_key(label); // removes whitespace
340
341 if (hila::myrank() == 0 && no_error) {
342 std::string tok;
343 T re, im;
344
345 no_error =
346 (match_token("(") && get_token(tok) && is_value(tok, re) && match_token(",") &&
347 get_token(tok) && is_value(tok, im) && match_token(")"));
348 if (!no_error && speaking) {
349 hila::out << "Error: expecting complex value '(re,im)' after '" << label << "'\n";
350 }
351
352 val = Complex<T>(re, im);
353 }
354
355 if (bcast) {
356 hila::broadcast2(val, no_error);
357 }
358 return no_error;
359 }
360 // get_value for #Vector<n,T>
361 template <int n, typename T>
362 bool get_value(Vector<n, T> &val, const std::string &label, bool bcast = true) {
363 val = 0;
364 bool no_error = true;
365
366 if (hila::myrank() == 0) {
367 no_error = get_value(val[0], label, false);
368 for (int i = 1; i < n && no_error; i++) {
369 no_error = get_value(val[i], ",", false);
370 }
371
372 if (!no_error && speaking) {
373 hila::out << "Error: expecting " << n << " comma-separated " << type_id<T>()
374 << "s after '" << label << "'\n";
375 }
376 }
377
378 if (bcast) {
379 hila::broadcast2(val, no_error);
380 }
381 return no_error;
382 }
383
384 // get_value for #CoordinateVector
385 template <int n = NDIM>
386 bool get_value(CoordinateVector &val, const std::string &label, bool bcast = true) {
388 bool b = get_value(iv, label, bcast);
389 val = iv;
390 return b;
391 }
392
393 // get_value for std::vector<T>
394 template <typename T>
395 bool get_value(std::vector<T> &val, const std::string &label, bool bcast = true) {
396 val = {};
397 bool no_error = true;
398
399 if (hila::myrank() == 0) {
400 T v;
401 no_error = get_value(v, label, false);
402 val.push_back(v);
403 while (no_error && match_token(",")) {
404 no_error = get_value(v, "", false);
405 val.push_back(v);
406 }
407
408 if (!no_error && speaking) {
409 hila::out << "Error: expecting a comma-separated list of " << type_id<T>()
410 << "s after '" << label << "'\n";
411 }
412 }
413
414 if (bcast) {
415 hila::broadcast(val);
416 hila::broadcast(no_error);
417 }
418
419 return no_error;
420 }
421 /** @} */
422
423 /**
424 * @brief Identify item from a list
425 * @details"items" contains the allowed entries. Return value is the
426 * index of the item found in input file.
427 *
428 * If the value of the optional bool parameter broadcast is:
429 * - true (default): the result is broadcast to all nodes
430 * and the program exits if no matches found.
431 *
432 * - false: result is not broadcast, and if no match found returns -1.
433 *
434 * Special item values:
435 *
436 * - "%f" matches a float or double value
437 * - "%i" matches an int or long
438 * - "%s" matches any string value
439 *
440 * If one of these is matched, it has to be read again with corresponding
441 * get() or get_value() -method. get_item doesn't progress the stack, so get() will fetch the
442 * next value which the item result corresponds to
443 *
444 * Examples:
445 * \code{.cpp}
446 * i = f.get_item("colour", {"red","green","blue"});
447 * \endcode
448 * will return value 1 if f contains "colour green", and quits the
449 * program if none of the 3 alternatives are found.
450 * \code{.cpp}
451 * double clover;
452 * int i = f.get_item("c_sw", {"tree","perturbative","%f"} );
453 * if (i == 2)
454 * clover = f.get();
455 * else { ...
456 * \endcode
457 * If file contains:
458 * - c_sw perturbative get_item() returns 1
459 * - c_sw 1.343 get_item() returns 2 and subsequent get() sets c_sw = 1.343
460 * - c_sw abcd error message and quit
461 *
462 * __NOTE__: "%s" matches anything. It should be the last item in the list.
463 * (The items are tested in order and first to match is returned.)
464 *
465 * @param label key to match in parameters file
466 * @param items list of items to identify with
467 * @param bcast Default true, if true broadcast to all MPI ranks
468 * @return int index of identified item in users defined list
469 */
470 int get_item(const std::string &label, const std::vector<std::string> &items,
471 bool bcast = true);
472
473 private:
474 /// a helper method to give type name
475 template <typename T>
476 inline const char *type_id() {
477 return nullptr;
478 }
479
480 bool peek_token(std::string &tok);
481 bool get_token(std::string &tok);
482 bool match_token(const std::string &tok);
483
484 void scan_cmdline(const std::string &key, int &end_of_key);
485
486 bool scan_string(std::string &val);
487
488 template <typename T, std::enable_if_t<std::is_arithmetic<T>::value, int> = 0>
489 bool is_value(const std::string &s, T &val) {
490 std::istringstream ss(s);
491 ss >> val;
492 if (ss.fail() || ss.bad())
493 return false;
494 else
495 return true;
496 }
497
498 bool is_value(const std::string &s, std::string &val);
499
500 bool contains_word_list(const std::string &list, int &end_of_key);
501
502 std::string remove_quotes(const std::string &val);
503
504 void print_linebuf(int eok);
505
506 bool get_line();
507 bool handle_key(const std::string &c);
508
509 bool remove_whitespace();
510};
511
512/// give here specializations of the type_id helper
513
514template <>
515inline const char *input::type_id<int>() {
516 return "int";
517}
518template <>
519inline const char *input::type_id<long>() {
520 return "long";
521}
522template <>
523inline const char *input::type_id<long long>() {
524 return "long long";
525}
526template <>
527inline const char *input::type_id<unsigned int>() {
528 return "unsigned int";
529}
530template <>
531inline const char *input::type_id<unsigned long>() {
532 return "unsigned long";
533}
534template <>
535inline const char *input::type_id<unsigned long long>() {
536 return "unsigned long long";
537}
538
539template <>
540inline const char *input::type_id<float>() {
541 return "float";
542}
543template <>
544inline const char *input::type_id<double>() {
545 return "double";
546}
547template <>
548inline const char *input::type_id<std::string>() {
549 return "string";
550}
551template <>
552inline const char *input::type_id<Complex<float>>() {
553 return "complex value";
554}
555template <>
556inline const char *input::type_id<Complex<double>>() {
557 return "complex value";
558}
559
560
561} // namespace hila
562
563#endif
Complex definition.
Definition cmplx.h:58
Matrix class which defines matrix operations.
Definition matrix.h:1742
returntype is a special class for resolving get("label") return type
Definition input.h:123
hila::input - Class for parsing runtime parameter files.
Definition input.h:53
void close()
Closes input parameter file.
Definition input.cpp:94
returntype get(const std::string &key)
Get next value in stack of read in input string from parameters file.
Definition input.h:273
bool get_value(T &val, const std::string &label, bool bcast=true)
Read input (alternative to get())
Definition input.h:306
bool open(const std::string &fname, bool use_cmdline=true, bool exit_on_error=true)
Open file that parameters are read from.
Definition input.cpp:27
void quiet(bool really=true)
Silence print output during file reading.
Definition input.h:116
int get_item(const std::string &label, const std::vector< std::string > &items, bool bcast=true)
Identify item from a list.
Definition input.cpp:361
CoordinateVector_t< int > CoordinateVector
CoordinateVector alias for CoordinateVector_t.
This file defines all includes for HILA.
Implement hila::swap for gauge fields.
Definition array.h:982
int myrank()
rank of this node
Definition com_mpi.cpp:237
std::ostream out
this is our default output file stream
T broadcast(T &var, int rank=0)
Broadcast the value of var to all MPI ranks from rank (default=0).
Definition com_mpi.h:170
void finishrun()
Normal, controlled exit - all nodes must call this. Prints timing information and information about c...
void broadcast2(T &t, U &u, int rank=0)
and broadcast with two values
Definition com_mpi.h:263