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