Version: SMASH-3.4
configuration.h
Go to the documentation of this file.
1 /*
2  *
3  * Copyright (c) 2014-2026
4  * SMASH Team
5  *
6  * GNU General Public License (GPLv3 or later)
7  *
8  */
9 
10 #ifndef SRC_INCLUDE_SMASH_CONFIGURATION_H_
11 #define SRC_INCLUDE_SMASH_CONFIGURATION_H_
12 
13 #include <algorithm>
14 #include <array>
15 #include <exception>
16 #include <filesystem>
17 #include <iostream>
18 #include <map>
19 #include <optional>
20 #include <set>
21 #include <stdexcept>
22 #include <string>
23 #include <utility>
24 #include <vector>
25 
26 #include "yaml-cpp/yaml.h"
27 
28 #include "cxx17compat.h"
29 #include "forwarddeclarations.h"
30 #include "key.h"
31 #include "stringify.h"
32 
33 namespace YAML {
34 
35 /**
36  * Convert from YAML::Node to SMASH-readable (C++) format and vice versa.
37  *
38  * \tparam T Type of the values (could be any data type that
39  * needs conversion).
40  */
41 template <typename T>
42 struct convert {
43  /**
44  * Serialization: Converts x (of any type) to a YAML::Node. To do this,
45  * the type of x needs first be cast to a string.
46  *
47  * \param[in] x Value that is to be converted to a YAML::Node.
48  * \return YAML node
49  */
50  static Node encode(const T &x) {
51  if constexpr (std::is_convertible_v<T, std::string>) {
52  return Node{static_cast<std::string>(x)};
53  } else {
54  static_assert(smash::has_to_string_v<T>,
55  "Encoding type T to YAML::Node requires an overload of "
56  "smash::to_string(T) to convert T to an std::string.");
57  return Node{smash::to_string(x)};
58  }
59  }
60 
61  /**
62  * Deserialization: Converts a YAML::Node to any SMASH-readable data type and
63  * returns whether or not this operation was successful.
64  *
65  * \param[in] node YAML::Node that is to be converted.
66  * \param[in] x Value that the YAML:Node is cast to.
67  * \return True in case conversion was successful.
68  */
69  static bool decode(const Node &node, T &x) {
70  if (!node.IsScalar()) {
71  return false;
72  } else {
73  x = static_cast<T>(node.Scalar());
74  return true;
75  }
76  }
77 };
78 } // namespace YAML
79 
80 namespace smash {
81 
82 /*!\Userguide
83  * \page doxypage_input_particles
84  *
85  * <h3>How the particles file is used</h3>
86  * The particles available to SMASH are defined in the *input/particles.txt*
87  * file. The content of this file is internally copied by CMake to the
88  * ***build*** directory when running the `cmake` command **for the first time**
89  * to set up SMASH. If you want to modify the particles file, you are encouraged
90  * to copy the provided one to a wished location, which has then to be passed to
91  * SMASH via the `-p` option. For example, assuming to have a
92  * *custom_particles.txt* file in the ***build*** folder, the SMASH executable
93  * can be run from there and instructed to use the own particles file via
94  * ```console
95  * ./smash -p custom_particles.txt
96  * ```
97  *
98  * <h3>The particle file format</h3>
99  * %Particles are specified as a table with particles properties in different
100  * columns, which may be separated by an arbitrary number of spaces:
101  * ```
102  * <name> <mass in GeV> <width in GeV> <parity> <PDG codes>
103  * ```
104  * The name has to be a unique UTF-8 string. Conventionally, unicode names are
105  * used in SMASH to make the file more readable and generate prettier output. It
106  * is possible to only specify the isospin multiplet and SMASH will fill in the
107  * properties of the components of the multiplet assuming isospin symmetry. The
108  * names generated this way will have the charges appended to the multiplet name
109  * using the unicode characters `⁻`, `⁰` and `⁺`. This is appropriate for almost
110  * all particles. Anti particles do not have to be specified explicitly.
111  *
112  * The pole mass and the on-shell width of the particle or multiplet have to be
113  * specified as floating point numbers in GeV.
114  *
115  * The parity has to be either `+` or `-`.
116  *
117  * The PDG codes are following the [numbering
118  * scheme](http://pdg.lbl.gov/2018/mcdata/mc_particle_id_contents.html)
119  * specified by the PDG, which depends on the quantum numbers of the particles.
120  * For SMASH, it is important that the quark content in the PDG code is
121  * correctly specified. Other than that, deviations from the numbering scheme
122  * have no effect in SMASH. If the name represents a multiplet, there has to be
123  * a PDG code for all multiplet members, except for anti particles.
124  *
125  * For example, to define all three pions (π⁻, π⁰, π⁺), it is sufficient to
126  * specify the π multiplet using the following line in *particles.txt*, where
127  * the 4th column contains the PDG number of the neutral and the 5th PDG number
128  * of the charged state:
129  * ```
130  * π 0.138 7.7e-9 111 211
131  * ```
132  *
133  * It is also possible to only specify a specific member of the multiplet. In
134  * this case, the charge has to be given as a suffix in the name using the UTF-8
135  * unicode characters `⁻`, `⁰` and `⁺`. For example, the properties of the
136  * electron can be specified like this:
137  * ```
138  * e⁻ 0.000511 0 11
139  * ```
140  *
141  * Comments can be added to the particles file using the `#` character.
142  * Everything after `#` until the end of the line is ignored.
143  *
144  * <hr>
145  * \attention
146  * -# If you specify an incorrect value, SMASH will print an error similar to
147  * the following:
148  * ```
149  * Failed to convert the input string to the expected data types.
150  * ```
151  * -# SMASH validates (up to some small numeric precision) the mass of some
152  * particles (e.g. N, π, K, ω, Δ, D). Therefore, totally nonphysical mass
153  * values cannot be used and SMASH will abort with a message error like e.g.
154  * the following
155  * ```
156  * Nucleon mass in input file different from 0.938000
157  * ```
158  * if a mass discrepancy for one of the validated particle is detected. This
159  * constraint is due to internal assumptions in SMASH and is needed to
160  * guarantee the correctness of the results. If you really need to use SMASH
161  * with nonphysical mass values, feel free to contact us or open an issue.
162  * -# Related to the previous point, it is important to mention that all hadrons
163  * belonging to the same isospin multiplet must have the same mass and this
164  * is enforced by SMASH, which will fail otherwise. Feel free to get in touch
165  * with us, if this restriction represents a problem for you.
166  * -# Some reactions in SMASH are parametrized and require specific particles in
167  * the final state. When such a reaction happens and the required particle is
168  * not defined, SMASH will crash.
169  * -# When running a box simulation in which detailed balance is expected to be
170  * conserved, the particles file will need to be modified. See \ref
171  * modi_box_usage_remark "this remark about the box modus" for further
172  * information.
173  */
174 
175 /*!\Userguide
176  * \page doxypage_input_decaymodes
177  *
178  * All possible decays and resonance formations in SMASH are provided by the
179  * `decaymodes.txt` file, which is
180  * located in '$SMASH_SRC_DIRECTORY/input'. If you want to modify and use this
181  * file to set up SMASH, execute
182  * ```console
183  * ./smash -d $SMASH_SRC_DIRECTORY/input/decaymodes.txt
184  * ```
185  * in the '$SMASH_SRC_DIRECTORY/build' directory. \n
186  *
187  * The decaymodes are formatted in blocks of the following format:
188  * ```
189  * <name of decaying particle>
190  * <branching ratio> <angular momentum L> <names of decay products>
191  * <branching ratio> <angular momentum L> <names of decay products>
192  * ...
193  * ```
194  * The blocks have to be separated by at least one empty line.
195  *
196  * The names have to be the ones defined in *particles.txt* (see \ref
197  * doxypage_input_particles). If multiplet names are used, the other branching
198  * ratios are generated by SMASH assuming isospin symmetry. Note that currently
199  * decay channels can only be specified for whole multiplets; individual
200  * particles can however still be used in a decay channel as specific daughters.
201  *
202  * The branching ratios are given as a floating point number. If the branching
203  * ratios in one block do not add up to 1, they are automatically normalized by
204  * SMASH.
205  *
206  * The angular momentum of the decay channel has to be specified as an integer.
207  *
208  * The names of two or three decay products have to be given for each channel.
209  * Note that the SMASH defaults avoid three-body decays, because they break
210  * detailed balance due to the lack of 3-to-1 reactions in SMASH.
211  *
212  * For example, the following lines are enough to specify all possible decays of
213  * the N(1440) resonance multiplet:
214  * ```
215  * N(1440)
216  * 0.60 1 N π
217  * 0.24 1 Δ π
218  * 0.16 0 N σ
219  * ```
220  * For decays violating isospin symmetry, it is possible to specify the members
221  * of the multiplets in the final state explicitely:
222  * ```
223  * φ
224  * 0.489 1 K⁺ K̅⁻
225  * 0.342 1 K⁰ K̅⁰
226  * ```
227  *
228  * It is possible to add comments to `decaymodes.txt` using the `#` character.
229  * Everything after `#` until the end of the line is ignored.
230  *
231  * \note
232  * Note that SMASH has an internal width cutoff (currently 10 keV), below which
233  * particles cannot decay, even if decays are specified in `decaymodes.txt`.
234  * This only changes when `Ignore_Minimum_Decay_Width_For_Decays_At_The_End` is
235  * enabled, then all decays are considered in the final decays, no matter their
236  * decay width.
237  *
238  * \note
239  * Note further, that the decay modes file will need to be modified when running
240  * a box simulation in which detailed balance is expected to be conserved. See
241  * \ref doxypage_input_conf_modi_box for further information.
242  */
243 
244 /**
245  * Interface to the SMASH configuration files.
246  *
247  * The configuration is created from a %YAML file and then stores a nested map
248  * of maps (normally a tree, but %YAML allows it to be cyclic - even though we
249  * don't want that feature). Since the resource owned by the object is a
250  * \c YAML::Node that handle memory in a similar way as a pointer does, it is
251  * forbidden (nor should it be needed) to copy instances of this class, while
252  * moving is fine (see special members documentation for more information).
253  *
254  * The typical usage of a Configuration is to create it, consume (i.e.
255  * <tt>take</tt>) all its values and let it being destructed. Since this is the
256  * contact point with SMASH input file, the class is meant to be strict in its
257  * usage, so that it is possible to help the inexpert user, who might being
258  * using a wrong input file and/or e.g. specify an unused key hoping in an
259  * effect that indeed does not occur. Therefore, it is imposed that <b>all keys
260  * must be parsed before an instance gets destroyed</b>. If this is not the
261  * case, an exception will be thrown.
262  *
263  * For the typical usage in SMASH one needs to read the value once. In that
264  * case, use the Configuration::take function, for example:
265  * \code
266  * Key<double> key{{"General", "SIGMA"}, {"1.0"}};
267  * double value = config.take(key);
268  * \endcode
269  * Note that only Key objects can be taken. This is done on purpose, because all
270  * SMASH allowed keys are gathered as static members of the InputKeys class.
271  * This allows calls like \c config.take(InputKeys::key_name) (where of course
272  * an existing member should be used).
273  *
274  * The opposite operation of \c take is the Configuration::set_value method,
275  * which has a similar syntax, but needs the new value to be assigned, e.g.
276  * \code
277  * config.set_value(key, 3.1415);
278  * \endcode
279  *
280  * If you need to delegate parsing of a section to some object, you can use the
281  * Configuration::extract_complete_sub_configuration method, which is taking a
282  * full section and returning a new, distinct Configuration instance.
283  *
284  * Last but not least, the Configuration::validate method is used by SMASH to
285  * check that all given keys are allowed in the present version of the codebase.
286  * This is achieved by querying the "database" InputKeys class.
287  *
288  * \attention As the Configuration is implemented, it does not make sense in
289  * practice to have constant instances, because their keys could not be taken
290  * and their destruction would lead to an exception being thrown. However, it
291  * still makes perfectly sense to have constant methods (think e.g. of a
292  * <tt>const %Configuration&</tt> being passed to a function).
293  */
295  public:
296  /**
297  * \ingroup exception
298  * Thrown when the types in the config file and C++ don't match.
299  */
300  struct IncorrectTypeInAssignment : public std::runtime_error {
301  using std::runtime_error::runtime_error;
302  };
303  /**
304  * \ingroup exception
305  * Thrown for YAML parse errors.
306  */
307  struct ParseError : public std::runtime_error {
308  using std::runtime_error::runtime_error;
309  };
310  /**
311  * \ingroup exception
312  * Thrown if the file does not exist.
313  */
314  struct FileDoesNotExist : public std::runtime_error {
315  using std::runtime_error::runtime_error;
316  };
317  /**
318  * \ingroup exception
319  * Thrown if a Key is taken twice.
320  */
321  struct TakeSameKeyTwice : public std::logic_error {
322  using std::logic_error::logic_error;
323  };
324  /**
325  * \ingroup exception
326  * Thrown if a required Key is taken/read but is missing.
327  */
328  struct RequiredKeyMissing : public std::invalid_argument {
329  using std::invalid_argument::invalid_argument;
330  };
331  /**
332  * \ingroup exception
333  * Thrown if a Key has an invalid value.
334  */
335  struct InvalidKeyValue : public std::invalid_argument {
336  using std::invalid_argument::invalid_argument;
337  };
338 
339  /**
340  * Flag to mark initialization with a YAML formatted string.
341  */
342  static const char InitializeFromYAMLString = 'S';
343 
344  /**
345  * Flag to tune method(s) behavior such that it is descriptive from the
346  * caller side. For example, see \ref extract_sub_configuration.
347  */
348  enum class GetEmpty { Yes, No };
349 
350  /**
351  * Return type of Configuration::validate which conveys more information that
352  * simply a two-state boolean variable.
353  */
354  enum class Is { Invalid, Deprecated, Valid };
355 
356  /**
357  * Read config.yaml from the specified path.
358  *
359  * \param[in] path The directory where the SMASH config files are located.
360  */
361  explicit Configuration(const std::filesystem::path &path);
362 
363  /**
364  * Read a YAML config file from the specified path.
365  *
366  * \param[in] path The directory where the SMASH config files are located.
367  * \param[in] filename The filename (without path) of the YAML config file, in
368  * case you don't want the default "config.yaml".
369  */
370  explicit Configuration(const std::filesystem::path &path,
371  const std::filesystem::path &filename);
372 
373  /**
374  * Initialize configuration with a YAML formatted string. This is
375  * useful in 3-rd party application where we may not be able or
376  * willing to read in external files.
377  *
378  * \param[in] yaml YAML formatted configuration data.
379  * \param[in] sflag control flag InitializeFromYAMLString.
380  */
381  explicit Configuration(const char *yaml, const char sflag) {
382  if (sflag == InitializeFromYAMLString) {
383  merge_yaml(yaml);
384  } else {
385  throw std::runtime_error(
386  "Unknown control flag in Configuration constructor"
387  " with a YAML formatted string. Please, use"
388  " Configuration::InitializeFromYAMLString.");
389  }
390  }
391 
392 #ifdef BUILD_TESTS
393  /**
394  * \mocking
395  * Unit tests can use this constructor to get a Configuration object from a
396  * built-in string.
397  * This function is only available to tests and should never be used/needed in
398  * actual SMASH code. The intention is to avoid creating a mock object for
399  * Configuration to test other classes of SMASH.
400  */
401  explicit Configuration(const char *yaml) : root_node_(YAML::Load(yaml)) {
402  if (root_node_.IsNull())
403  root_node_ = YAML::Node{YAML::NodeType::Map};
404  }
405 #endif
406 
407  /**
408  * Prevent Configuration objects from being copied.
409  *
410  * Underneath, the resource is a \c YAML::Node and since this handles memory
411  * in a similar way as a pointer would do, copying an object would make
412  * several instances point to the same memory and it would make it difficult
413  * to use this object correctly. Therefore, copies are not allowed.
414  */
415  Configuration(const Configuration &) = delete;
416  /**
417  * Prevent Configuration objects from being copy-assigned.
418  *
419  * See copy constructor Configuration(const Configuration &) for more
420  * information.
421  */
423 
424  /**
425  * Provide class with move constructor.
426  *
427  * In contrast to copying, moving is fine, since this keeps the owner
428  * of the resource unique.
429  *
430  * \note Since the class has the peculiar behavior that all keys must be
431  * parsed before it gets destroyed (otherwise an exception is thrown),
432  * it is important to manually implement the move operations, in order to
433  * ensure that objects that are moved from result cleared and their
434  * destruction is not leading to any throw. This is not guaranteed if the
435  * special members are defaulted to the compiler generated versions.
436  */
438 
439  /**
440  * Provide class with move assignment operator.
441  *
442  * See move constructor Configuration(Configuration &&) for more information.
443  */
445 
446  /**
447  * Destroy the object, optionally throwing if not all keys were taken.
448  *
449  * This is a way to enforce that the object has to be consumed (i.e.
450  * completely parsed) during its lifetime. Since this object might be
451  * destructed during stack unwinding, the destructor has to throw only
452  * if it is safe to do so and the uncaught_exceptions_ member is used
453  * to properly implement this behavior.
454  */
455  ~Configuration() noexcept(false);
456 
457  /**
458  * Merge the configuration in \p yaml into the existing tree.
459  *
460  * The function parses the string in \p yaml into its internal tree
461  * representation. Then it merges the nodes from the new tree into the
462  * existing tree.
463  * The merge resolves conflicts by taking the value from \p yaml.
464  *
465  * \param[in] yaml A string with YAML (or JSON) content that is to be merged.
466  */
467  void merge_yaml(const std::string &yaml);
468 
469  /// Lists all YAML::Nodes from the configuration setup.
470  std::vector<std::string> list_upmost_nodes();
471 
472  /**
473  * The default interface for SMASH to read configuration values.
474  *
475  * The function returns the value at the specified \p Key and removes its
476  * labels from the Configuration object. Therefore, a subsequent call to the
477  * \c take or \c has_value methods with the same \p Key throws or returns
478  * \c false respectively. By removing the value, the Configuration object
479  * keeps track which keys were never taken.
480  *
481  * \attention If a not existent Key is taken, its default value is returned,
482  * if any exists. If a not existing required key is taken, an error
483  * will be given.
484  *
485  * \note If taking a key leaves the parent key without a value, then this is
486  * in turn removed and so on. From a performance point of view, it might
487  * be argued that this is not needed to be checked and done at every
488  * \c take operation and it might be done once for all. However, on one
489  * hand it is a natural behaviour to expect and on the other hand this
490  * is hardly going to be an application bottle-neck.
491  *
492  * \warning Since \c take returns the default value when the key is not
493  * present in the configuration, it is important to make it throw if
494  * an existing key is attempted to be taken twice. Otherwise it would
495  * happen that taking any existing key would return the user-defined
496  * value the first time and taking it again would return the key
497  * default value. This is a misleading behaviour we want to avoid.
498  * However, a key can be taken several times if it exists at take
499  * time. For example, taking a key, setting its value and taking it
500  * again is a valid behaviour.
501  *
502  * \param[in] key The input key that should be taken. This is usually one of
503  * the \c InputKeys static members, i.e. one of the allowed keys. Of course,
504  * any \c Key would work. For example, given
505  \verbatim
506  Group:
507  Key: 42
508  \endverbatim
509  * then
510  \verbatim
511  Key key<int>{{"Group", "Key"}, {"1.0"}};
512  string value = config.take(key);
513  \endverbatim
514  * will take the value. This will make the key \c "Group" also be removed from
515  * the configuration, since it remains without any value.
516  *
517  * \return The value of the taken key if present, its default value otherwise.
518  *
519  * \throw TakeSameKeyTwice If a key was already previously taken.
520  * \throw std::logic_error If the taken key has not an \c std::map type but
521  * refers to a section in the configuration file.
522  * \throw std::invalid_argument If a key without a default is taken but it is
523  * absent in the configuration.
524  * \throw std::invalid_argument If the taken key has an invalid value w.r.t.
525  * its validator.
526  */
527  template <typename T>
528  T take(const Key<T> &key) {
529  if (has_value(key)) {
530  // The following return statement converts a Value into T
532  } else if (has_section(key.labels())) {
533  // In this case, if the Key type is a map, we take it, otherwise fails
534  if constexpr (isMap<typename Key<T>::type>::value) {
536  } else {
537  throw std::logic_error(
538  "Key " + std::string{key} + // NOLINT(whitespace/braces)
539  " was taken, but its value is not a map, although there is a "
540  "section in the configuration with its labels.");
541  }
542  } else if (did_key_exist_and_was_it_already_taken(key.labels())) {
543  throw TakeSameKeyTwice("Attempt to take key " +
544  std::string{key} + // NOLINT(whitespace/braces)
545  " twice.");
546  } else {
547  try {
548  /* Note that a key with invalid default value cannot be constructed,
549  hence we do not validate its default value here. */
550  return key.default_value();
551  } catch (std::bad_optional_access &) {
552  throw RequiredKeyMissing(
553  "Key " + std::string{key} + // NOLINT(whitespace/braces)
554  " without default value taken, but missing in configuration.");
555  }
556  }
557  }
558 
559  /**
560  * Alternative method to take a key value, specifying the default value.
561  * \see take which is used in one branch of this method (e.g. for possible
562  * thrown exceptions).
563  *
564  * @tparam T The type of the key to be taken
565  * @param key The key to be taken
566  * @param default_value The default value to be returned if the key is not
567  * present in the configuration
568  * @return The value of the key
569  *
570  * \throw std::logic_error If the key has not a default value declared as
571  * dependent on external entities.
572  * \throw std::logic_error If the passed default value is invalid w.r.t. the
573  * key validator.
574  */
575  template <typename T>
576  T take(const Key<T> &key, T default_value) {
577  if (!key.has_dependent_default()) {
578  throw std::logic_error(
579  "An input Key without dependent default cannot be taken specifying a "
580  "default value! Either define the key as having a dependent default "
581  "or take it without a default value (which is a Key property).");
582  }
583  if (!key.validate(default_value)) {
584  throw std::logic_error("Invalid default value passed when taking " +
585  static_cast<std::string>(key) + " key.");
586  }
587  if (has_value(key)) {
588  return take(key);
589  }
590  return default_value;
591  }
592 
593  /**
594  * Additional interface for SMASH to read configuration values without
595  * removing them.
596  *
597  * The function returns the value of the specified \c Key but does not remove
598  * it from the Configuration object. Semantically, this means the value was
599  * not used.
600  *
601  * Also this method returns the default value of the key (or an error if none
602  * is available), if the key is not present in the configuration. \see take
603  *
604  * \note Since reading a key does not remove it from the configuration, it is
605  * not necessary to store the key read to avoid taking it multiple
606  * times. Actually, doing so is safe and will return the same value.
607  *
608  * \param[in] key The input key that should be read.
609  *
610  * \return The value of the taken key if present, its default value otherwise.
611  *
612  * \throw std::logic_error If the read key has not an \c std::map type but
613  * refers to a section in the configuration file.
614  * \throw std::invalid_argument If a key without a default is read but it is
615  * absent in the configuration.
616  * \throw std::invalid_argument If the read key has an invalid value w.r.t.
617  * its validator.
618  */
619  template <typename T>
620  T read(const Key<T> &key) const {
621  if (has_value(key)) {
622  // The following return statement converts a Value into T
624  } else if (has_section(key.labels())) {
625  // In this case, if the Key type is a map, we take it, otherwise fails
626  if constexpr (isMap<typename Key<T>::type>::value) {
628  } else {
629  throw std::logic_error(
630  "Key " + std::string{key} + // NOLINT(whitespace/braces)
631  " was read, but its value is not a map, although there is a "
632  "section in the configuration with its labels.");
633  }
634  } else {
635  try {
636  /* Note that a key with invalid default value cannot be constructed,
637  hence we do not validate its default value here. */
638  return key.default_value();
639  } catch (std::bad_optional_access &) {
640  throw RequiredKeyMissing(
641  "Key " + std::string{key} + // NOLINT(whitespace/braces)
642  " without default value read, but missing in configuration.");
643  }
644  }
645  }
646 
647  /**
648  * Alternative method to read a key value, specifying the default value.
649  * \see read which is used in one branch of this method (e.g. for possible
650  * thrown exceptions).
651  *
652  * @tparam T The type of the key to be read
653  * @param key The key to be read
654  * @param default_value The default value to be returned if the key is not
655  * present in the configuration
656  * @return The value of the key
657  *
658  * \throw std::logic_error If the key has not a default value declared as
659  * dependent on external entities.
660  * \throw std::logic_error If the passed default value is invalid w.r.t. the
661  * key validator.
662  */
663  template <typename T>
664  T read(const Key<T> &key, T default_value) const {
665  if (!key.has_dependent_default()) {
666  throw std::logic_error(
667  "An input Key without dependent default cannot be read specifying a "
668  "default value! Either define the key as having a dependent default "
669  "or read it without a default value (which is a Key property).");
670  }
671  if (!key.validate(default_value)) {
672  throw std::logic_error("Invalid default value passed when reading " +
673  static_cast<std::string>(key) + " key.");
674  }
675  if (has_value(key)) {
676  return read(key);
677  }
678  return default_value;
679  }
680 
681  /**
682  * Overwrite the value of the YAML node corresponding to the specified key.
683  *
684  * \param[in] key The input key that should be changed.
685  * \param[in] value An arbitrary value that yaml-cpp can convert into YAML
686  * representation. Any builtin type, strings, maps, and
687  * vectors can be used here. Of course, this has to match the
688  * Key type passed as first argument.
689  *
690  * \tparam T The type of the value to be assigned to the Key.
691  * \tparam U The type of the key value. This is by default \c T but
692  * it has been allowed to be different from it, as long as it is
693  * convertible to T. This enables e.g. to set a key with a string
694  * value using a <tt>const char*</tt> second argument.
695  *
696  * \attention This method creates a new entry in the configuration if the
697  * passed key is not yet existing in it.
698  *
699  * \note Removing qualifiers and the reference in the default value of the
700  * second template argument is needed because the Key type is a plain
701  * type and \c T might be deduced to a constant and/or reference type.
702  */
703  template <typename T, typename U = remove_cvref_t<T>,
704  typename std::enable_if_t<std::is_convertible_v<T, U>, bool> = true>
705  void set_value(Key<U> key, T &&value) {
707  {key.labels().begin(), key.labels().end()});
708  node = std::forward<T>(value);
709  }
710 
711  /**
712  * Remove all entries in the given section except for \p key.
713  *
714  * \param[in] key The key of the map entry to keep.
715  * \param[in] section You can pass an arbitrary number of keys inside curly
716  * braces, following the nesting structure in the config
717  * file, in order to specify the section where to delete
718  * entries. Omitting the \c section is equivalent to
719  * specifying \c {} and the top-level section is
720  * understood.
721  */
722  void remove_all_entries_in_section_but_one(const std::string &key,
723  KeyLabels section = {});
724 
725  /**
726  * Create a new configuration from a then-removed section of the present
727  * object. This method is meant to be used to deal with sections only, i.e.
728  * it will throw if used to extract a key value that is not a section (namely
729  * a map in YAML language). Use \ref take for that purpose, instead.
730  *
731  * \param[in] section You can pass an arbitrary number of keys inside curly
732  * braces, following the nesting structure in the config file.
733  * \param[in] empty_if_not_existing
734  * Specify \c Configuration::GetEmpty::Yes if you want an empty
735  * Configuration in case the requested section does not exist.
736  *
737  * \throw std::runtime_error if the method is used
738  * - to access a scalar or sequence value;
739  * - to access a key that has no value or is an empty map;
740  * - to access a not existing key (unless explicitly allowed).
741  *
742  * \return A new \c Configuration containing the chosen section.
743  */
745  KeyLabels section, Configuration::GetEmpty empty_if_not_existing =
747 
748  /**
749  * Alternative method to extract a sub-configuration, which retains the labels
750  * from the top-level in the returned object instead of dropping them.
751  *
752  * \see extract_sub_configuration
753  */
755  KeyLabels section, Configuration::GetEmpty empty_if_not_existing =
757 
758  /**
759  * Enclose the configuration into the given section.
760  *
761  * @param section The section in which to enclose the configuration.
762  */
763  void enclose_into_section(KeyLabels section);
764 
765  /**
766  * Return whether the configuration has a (possibly empty) non-map key.
767  * Although %YAML keys can have maps as value, we rather refer to those as
768  * sections and we do not consider them as key in the SMASH database sense. A
769  * key has then either a scalar or sequence value.
770  *
771  * \param[in] key The key to be checked for.
772  */
773  template <typename T>
774  bool has_key(const Key<T> &key) const {
775  const auto found_node =
776  find_existing_node({key.labels().begin(), key.labels().end()});
777  return found_node.has_value() && !(found_node.value().IsMap());
778  }
779 
780  /**
781  * Return whether there is a \b non-empty value behind the requested \p key
782  * (which is supposed not to refer to a section). If there is a section with
783  * the same labels as the provided key has, this function returns \c false .
784  *
785  * \param[in] key The key to be checked for.
786  */
787  template <typename T>
788  bool has_value(const Key<T> &key) const {
789  const auto found_node =
790  find_existing_node({key.labels().begin(), key.labels().end()});
791  return found_node.has_value() && !(found_node.value().IsNull()) &&
792  !(found_node.value().IsMap());
793  }
794 
795  /**
796  * Return whether there is a (possibly empty) section with the given labels.
797  *
798  * \param[in] labels The labels of the section to be checked for.
799  */
800  bool has_section(const KeyLabels &labels) const {
801  const auto found_node = find_existing_node({labels.begin(), labels.end()});
802  return found_node.has_value() && found_node.value().IsMap();
803  }
804 
805  /**
806  * @return \c true if the object is empty;
807  * @return \c false if at least one key exists.
808  */
809  bool is_empty() const { return root_node_.size() == 0; }
810 
811  /**
812  * Return a \c string of the current YAML tree.
813  */
814  std::string to_string() const;
815 
816  /**
817  * Erase the Configuration content.
818  *
819  * This function is useful e.g. in tests to clean up not taken keys
820  * that would trigger an exception being thrown at by the destructor.
821  */
822  void clear() { root_node_.reset(); }
823 
824  /**
825  * Validate content of configuration in terms of YAML keys.
826  *
827  * A warning or error message is printed for deprecated or invalid keys,
828  * respectively, together with information about SMASH versions, if possible.
829  *
830  * \note Here a full validation is done by default and all keys are checked,
831  * although the validation might be shortened by returning \c false as soon as
832  * an invalid key is found. However, a full validation is more user-friendly,
833  * since as much information as possible about the input file is provided.
834  *
835  * \param[in] full_validation Whether all keys are checked or not.
836  *
837  * \return \c Is::Valid if the object contains valid keys only;
838  * \return \c Is::Deprecated if the object is valid but has deprecated key(s);
839  * \return \c Is::Invalid if the object contains at least one invalid key.
840  */
841  Is validate(bool full_validation = true) const;
842 
843  private:
844  /**
845  * Proxy object to be used when taking or reading keys in the configuration.
846  * This type automatically converts to the target type e.g. on assignment. An
847  * object of this type is returned by the private \c take and \c read methods
848  * and it is constructed from the \c YAML::Node to be taken or read.
849  *
850  * This class is an implementation detail of Configuration and can be ignored
851  * by users of Configuration.
852  */
853  class Value {
854  friend class Configuration;
855 
856  /// a YAML leaf node \todo(steinberg) What is that?
857  const YAML::Node node_;
858  /// The key to be interpreted
859  const char *const key_;
860 
861  /**
862  * Construct the Value wrapper from a YAML::Node.
863  *
864  * \note This constructor must be implicit, otherwise it's impossible to
865  * return an rvalue Value object - because the copy constructor is deleted.
866  */
867  Value(const YAML::Node &n, const char *key) : node_(n), key_(key) {
868  if (!(n.IsScalar() || n.IsSequence() || n.IsMap())) {
869  std::stringstream err;
870  err << "Configuration value for \"" << key
871  << "\" is missing or invalid";
872  throw std::runtime_error(err.str());
873  }
874  }
875 
876  public:
877  /// If you want to copy this you're doing it wrong
878  Value(const Value &) = delete;
879  /// If you want to copy this you're doing it wrong
880  Value &operator=(const Value &) = delete;
881 
882  /**
883  * This function determines the type it is assigned to and calls
884  * YAML::Node::as<T>() with this type.
885  *
886  * This makes reading values more convenient than calling as<type>()
887  * explicitly.
888  * \throw IncorrectTypeInAssignment
889  */
890  template <typename T>
891  operator T() const {
892  try {
893  return node_.as<T>();
894  } catch (YAML::TypedBadConversion<T> &e) {
896  "The value for key \"" + std::string(key_) +
897  "\" cannot be converted to the requested type.");
898  }
899  }
900 
901  /**
902  * Check conversion exceptions.
903  *
904  * \throw IncorrectTypeInAssignment in case type conversion failed.
905  */
906  template <typename T>
907  operator std::vector<T>() const {
908  try {
909  return node_.as<std::vector<T>>();
910  } catch (YAML::TypedBadConversion<T> &e) {
912  "One of the values in the sequence for key \"" + std::string(key_) +
913  "\" failed to convert to the requested type. E.g. [1 2] is a "
914  "sequence of one string \"1 2\" and [1, 2] is a sequence of two "
915  "integers. Often there is just a comma missing in the config "
916  "file.");
917  } catch (YAML::TypedBadConversion<std::vector<T>> &e) {
919  "The value for key \"" + std::string(key_) +
920  "\" cannot be converted to the requested type. A sequence was "
921  "expected but apparently not found.");
922  }
923  }
924 
925  /**
926  * Cast array of keys to a std::array of length N.
927  *
928  * \return Array of std::array type.
929  * \throw IncorrectTypeInAssignment in case the number of keys does not
930  * match the length of the newly generated array.
931  */
932  template <typename T, size_t N>
933  operator std::array<T, N>() const {
934  const std::vector<T> vec = operator std::vector<T>();
935  const size_t n_read = vec.size();
936  // Alert if size does not match
937  if (n_read != N) {
938  throw IncorrectTypeInAssignment("Wrong number of values in array \"" +
939  std::string(key_) + "\". Expected " +
940  std::to_string(N) +
941  " values,"
942  " found " +
943  std::to_string(n_read) + ".");
944  }
945  std::array<T, N> arr;
946  std::copy_n(vec.begin(), N, arr.begin());
947  return arr;
948  }
949 
950  /**
951  * Set ReactionBitSet from configuration values.
952  *
953  * \return ReactionBitSet with all included reaction types.
954  * \throw IncorrectTypeInAssignment in case a reaction type that is not
955  * available is provided as a configuration value.
956  */
957  operator ReactionsBitSet() const {
958  const std::vector<std::string> v = operator std::vector<std::string>();
959  ReactionsBitSet s;
960  for (const auto &x : v) {
961  if (x == "All") {
962  s.set();
963  break;
964  } else if (x == "Elastic") {
966  } else if (x == "NN_to_NR") {
968  } else if (x == "NN_to_DR") {
970  } else if (x == "KN_to_KN") {
972  } else if (x == "KN_to_KDelta") {
974  } else if (x == "Strangeness_exchange") {
976  } else if (x == "NNbar") {
978  } else if (x == "PiDeuteron_to_NN") {
980  } else if (x == "PiDeuteron_to_pidprime") {
982  } else if (x == "NDeuteron_to_Ndprime") {
984  } else if (x == "Charm_T-matrix") {
986  } else {
988  "The value for key \"" + std::string(key_) +
989  "\" should be \"All\", \"Elastic\", \"NN_to_NR\", \"NN_to_DR\","
990  "\"KN_to_KN\", \"KN_to_KDelta\", \"PiDeuteron_to_NN\", "
991  "\"PiDeuteron_to_pidprime\", \"NDeuteron_to_Ndprime\", "
992  "\"Strangeness_exchange\", \"NNbar\", or \"Charm_T-matrix\" "
993  "or any combination of these.");
994  }
995  }
996  return s;
997  }
998 
999  /**
1000  * Set MultiParticleReactionsBitSet from configuration values.
1001  *
1002  * \return MultiParticleReactionsBitSet with all included reaction types.
1003  * \throw IncorrectTypeInAssignment in case a reaction type that is not
1004  * available is provided as a configuration value.
1005  */
1006  operator MultiParticleReactionsBitSet() const {
1007  const std::vector<std::string> v = operator std::vector<std::string>();
1009  for (const auto &x : v) {
1010  if (x == "All") {
1011  s.set();
1012  break;
1013  } else if (x == "Meson_3to1") {
1015  } else if (x == "Deuteron_3to2") {
1017  } else if (x == "NNbar_5to2") {
1019  } else if (x == "A3_Nuclei_4to2") {
1021  } else {
1023  "The value for key \"" + std::string(key_) +
1024  "\" should be \"All\", \"Meson_3to1\", "
1025  "\"Deuteron_3to2\" or \"NNbar_5to2\", "
1026  "\"A3_Nuclei_4to2\", or any combination of "
1027  "these.");
1028  }
1029  }
1030  return s;
1031  }
1032 
1033  /**
1034  * Set thermodynamic quantity from configuration values.
1035  *
1036  * \return Set of thermodynamic quantity.
1037  * \throw IncorrectTypeInAssignment in case a thermodynamic quantity that is
1038  * not available is provided as a configuration value.
1039  */
1040  operator std::set<ThermodynamicQuantity>() const {
1041  const std::vector<std::string> v = operator std::vector<std::string>();
1042  std::set<ThermodynamicQuantity> s;
1043  for (const auto &x : v) {
1044  if (x == "rho_eckart") {
1046  } else if (x == "tmn") {
1047  s.insert(ThermodynamicQuantity::Tmn);
1048  } else if (x == "tmn_landau") {
1050  } else if (x == "landau_velocity") {
1052  } else if (x == "j_QBS") {
1053  s.insert(ThermodynamicQuantity::j_QBS);
1054  } else {
1056  "The value for key \"" + std::string(key_) +
1057  "\" should be \"rho_eckart\", \"tmn\""
1058  ", \"tmn_landau\", \"landau_velocity\" or \"j_QBS\".");
1059  }
1060  }
1061  return s;
1062  }
1063 
1064  /**
1065  * Set calculation frame from configuration values.
1066  *
1067  * \return string of calculation frame.
1068  * \throw IncorrectTypeInAssignment in case a calculation frame that is
1069  * not available is provided as a configuration value.
1070  */
1071  operator CalculationFrame() const {
1072  const std::string s = operator std::string();
1073  if (s == "center of velocity") {
1075  }
1076  if (s == "center of mass") {
1078  }
1079  if (s == "fixed target") {
1081  }
1083  "The value for key \"" + std::string(key_) +
1084  "\" should be \"center of velocity\" or \"center of mass\" "
1085  "or \"fixed target\".");
1086  }
1087 
1088  /**
1089  * Set form factor from configuration values.
1090  *
1091  * \return Dilepton pion form factor type.
1092  * \throw IncorrectTypeInAssignment in case a form factor value that is
1093  * not available is provided as a configuration value.
1094  */
1095  operator DileptonBremsPionFormFactor() const {
1096  const std::string s = operator std::string();
1097  if (s == "FF1") {
1099  }
1100  if (s == "FF2") {
1102  }
1103  if (s == "Off") {
1105  }
1107  "The value for key \"" + std::string(key_) +
1108  "\" should be \"FF1\" or \"FF2\" or \"Off\".");
1109  }
1110 
1111  /**
1112  * (De-)Activate Fermi motion from configuration values.
1113  *
1114  * \return Fermi motion setup.
1115  * \throw IncorrectTypeInAssignment in case a Fermi motion value that is
1116  * not available is provided as a configuration value.
1117  */
1118  operator FermiMotion() const {
1119  const std::string s = operator std::string();
1120  if (s == "off") {
1121  return FermiMotion::Off;
1122  }
1123  if (s == "on") {
1124  return FermiMotion::On;
1125  }
1126  if (s == "frozen") {
1127  return FermiMotion::Frozen;
1128  }
1130  "The value for key \"" + std::string(key_) +
1131  "\" should be \"off\" or \"on\" or \"frozen\".");
1132  }
1133 
1134  /**
1135  * Set density type from configuration values.
1136  *
1137  * \return Density type.
1138  * \throw IncorrectTypeInAssignment in case a density type that is
1139  * not available is provided as a configuration value.
1140  */
1141  operator DensityType() const {
1142  const std::string s = operator std::string();
1143  if (s == "hadron") {
1144  return DensityType::Hadron;
1145  }
1146  if (s == "baryon") {
1147  return DensityType::Baryon;
1148  }
1149  if (s == "baryonic isospin") {
1151  }
1152  if (s == "pion") {
1153  return DensityType::Pion;
1154  }
1155  if (s == "total isospin") {
1157  }
1158  if (s == "none") {
1159  return DensityType::None;
1160  }
1161  throw IncorrectTypeInAssignment("The value for key \"" +
1162  std::string(key_) +
1163  "\" should be \"hadron\" or \"baryon\" "
1164  "or \"baryonic isospin\" or \"pion\" "
1165  "or \"none\".");
1166  }
1167 
1168  /**
1169  * Set expansion mode from configuration values.
1170  *
1171  * \return Expansion mode.
1172  * \throw IncorrectTypeInAssignment in case an expansion mode that is
1173  * not available is provided as a configuration value.
1174  */
1175  operator ExpansionMode() const {
1176  const std::string s = operator std::string();
1177  if (s == "NoExpansion") {
1179  }
1180  if (s == "MasslessFRW") {
1182  }
1183  if (s == "MassiveFRW") {
1185  }
1186  if (s == "Exponential") {
1188  }
1190  "The value for key \"" + std::string(key_) +
1191  "\" should be \"NoExpansion\", \"MasslessFRW\"," +
1192  "\"MassiveFRW\" or \"Exponential\".");
1193  }
1194 
1195  /**
1196  * Set DerivativesMode.
1197  */
1198  operator DerivativesMode() const {
1199  const std::string s = operator std::string();
1200  if (s == "Covariant Gaussian") {
1202  }
1203  if (s == "Finite difference") {
1205  }
1206  if (s == "Off") {
1207  return DerivativesMode::Off;
1208  }
1210  "The value for key \"" + std::string(key_) +
1211  "\" should be \"Covariant Gaussian\", \"Finite difference\"," +
1212  " or \"Off\".");
1213  }
1214 
1215  /**
1216  * Set FieldDerivatives mode.
1217  */
1218  operator FieldDerivativesMode() const {
1219  const std::string s = operator std::string();
1220  if (s == "Chain Rule") {
1222  }
1223  if (s == "Direct") {
1225  }
1227  "The value for key \"" + std::string(key_) +
1228  "\" should be \"Chain Rule\" or \"Direct\".");
1229  }
1230 
1231  /**
1232  * Set SmearingMode.
1233  */
1234  operator SmearingMode() const {
1235  const std::string s = operator std::string();
1236  if (s == "Covariant Gaussian") {
1238  }
1239  if (s == "Discrete") {
1240  return SmearingMode::Discrete;
1241  }
1242  if (s == "Triangular") {
1243  return SmearingMode::Triangular;
1244  }
1246  "The value for key \"" + std::string(key_) +
1247  "\" should be \"Covariant Gaussian\", \"Discrete\"," +
1248  " or \"Triangular\".");
1249  }
1250 
1251  /**
1252  * Set time step mode from configuration values.
1253  *
1254  * \return time step mode.
1255  * \throw IncorrectTypeInAssignment in case a time step mode that is
1256  * not available is provided as a configuration value.
1257  */
1258  operator TimeStepMode() const {
1259  const std::string s = operator std::string();
1260  if (s == "None") {
1261  return TimeStepMode::None;
1262  }
1263  if (s == "Fixed") {
1264  return TimeStepMode::Fixed;
1265  }
1266  throw IncorrectTypeInAssignment("The value for key \"" +
1267  std::string(key_) +
1268  "\" should be \"None\" or \"Fixed\".");
1269  }
1270 
1271  /**
1272  * Set initial condition for box setup from configuration values.
1273  *
1274  * \return Initial condition for box setup.
1275  * \throw IncorrectTypeInAssignment in case an initial conditions that is
1276  * not available is provided as a configuration value.
1277  */
1278  operator BoxInitialCondition() const {
1279  const std::string s = operator std::string();
1280  if (s == "thermal momenta") {
1282  }
1283  if (s == "thermal momenta quantum") {
1285  }
1286  if (s == "peaked momenta") {
1288  }
1290  "The value for key \"" + std::string(key_) +
1291  "\" should be \"thermal momenta\", \"thermal momenta quantum\", " +
1292  "or \"peaked momenta\".");
1293  }
1294 
1295  /**
1296  * Set initial condition for sphere setup from configuration values.
1297  *
1298  * \return Initial condition for sphere setup.
1299  * \throw IncorrectTypeInAssignment in case an initial conditions that is
1300  * not available is provided as a configuration value.
1301  */
1302  operator SphereInitialCondition() const {
1303  const std::string s = operator std::string();
1304  if (s == "thermal momenta") {
1306  }
1307  if (s == "thermal momenta quantum") {
1309  }
1310  if (s == "IC_ES") {
1312  }
1313  if (s == "IC_1M") {
1315  }
1316  if (s == "IC_2M") {
1318  }
1319  if (s == "IC_Massive") {
1321  }
1323  "The value for key \"" + std::string(key_) +
1324  "\" should be \"thermal momenta\", \"thermal momenta quantum\", " +
1325  "\"IC_ES\", \"IC_1M\", \"IC_2M\" or" + "\"IC_Massive\".");
1326  }
1327 
1328  /**
1329  * Set treatment of N-Nbar reactions from configuration values.
1330  *
1331  * \return N-Nbar treatment.
1332  * \throw IncorrectTypeInAssignment in case an N-Nbar treatment that is
1333  * not available is provided as a configuration value.
1334  */
1335  operator NNbarTreatment() const {
1336  const std::string s = operator std::string();
1337  if (s == "no annihilation") {
1339  }
1340  if (s == "resonances") {
1342  }
1343  if (s == "two to five") {
1345  }
1346  if (s == "strings") {
1347  return NNbarTreatment::Strings;
1348  }
1350  "The value for key \"" + std::string(key_) + "\" should be " +
1351  "\"no annihilation\", \"resonances\", \"two to five\" or " +
1352  " \"strings\".");
1353  }
1354 
1355  /**
1356  * Set cross-section sampling method from configuration values.
1357  *
1358  * \return Sampling method of cross-section.
1359  * \throw IncorrectTypeInAssignment in case a sampling method that is
1360  * not available is provided as a configuration value.
1361  */
1362  operator Sampling() const {
1363  const std::string s = operator std::string();
1364  if (s == "quadratic") {
1365  return Sampling::Quadratic;
1366  }
1367  if (s == "custom") {
1368  return Sampling::Custom;
1369  }
1370  if (s == "uniform") {
1371  return Sampling::Uniform;
1372  }
1374  "The value for key \"" + std::string(key_) +
1375  "\" should be \"quadratic\", \"uniform\" or \"custom\".");
1376  }
1377 
1378  /**
1379  * Set algorithm for forced thermalization from configuration values.
1380  *
1381  * \return Algorithm for forced thermalization.
1382  * \throw IncorrectTypeInAssignment in case a thermalization algorithm that
1383  * is not available is provided as a configuration value.
1384  */
1385  operator ThermalizationAlgorithm() const {
1386  const std::string s = operator std::string();
1387  if (s == "mode sampling") {
1389  }
1390  if (s == "biased BF") {
1392  }
1393  if (s == "unbiased BF") {
1395  }
1397  "The value for key \"" + std::string(key_) +
1398  "\" should be \"mode sampling\", \"biased BF\" or \"unbiased BF\".");
1399  }
1400 
1401  /**
1402  * Set collision criterion from configuration values.
1403  *
1404  * \return CollisionCriterion.
1405  * \throw IncorrectTypeInAssignment in case a collision criterion that is
1406  * not available is provided as a configuration value.
1407  */
1408  operator CollisionCriterion() const {
1409  const std::string s = operator std::string();
1410  if (s == "Geometric") {
1412  }
1413  if (s == "Stochastic") {
1415  }
1416  if (s == "Covariant") {
1418  }
1420  "The value for key \"" + std::string(key_) + "\" should be " +
1421  "\"Geometric\", \"Stochastic\" " + "or \"Covariant\".");
1422  }
1423 
1424  /**
1425  * Set charm rescattering method from configuration values.
1426  *
1427  * \return CharmRescattering.
1428  * \throw IncorrectTypeInAssignment in case a charm rescattering method that
1429  * is not available is provided as a configuration value.
1430  */
1431  operator CharmRescattering() const {
1432  const std::string c = operator std::string();
1433  if (c == "T-matrix") {
1435  }
1436  if (c == "resonances") {
1438  }
1439  if (c == "none") {
1440  return CharmRescattering::None;
1441  }
1443  "The value for key \"" + std::string(key_) + "\" should be " +
1444  "\"T-matrix\", \"resonances\", " + "or \"none\".");
1445  }
1446 
1447  /**
1448  * Set spin interaction type from configuration values.
1449  *
1450  * \return SpinInteractionType.
1451  * \throw IncorrectTypeInAssignment in case a spin interaction type that is
1452  * not available is provided as a configuration value.
1453  */
1454  operator SpinInteractionType() const {
1455  const std::string s = operator std::string();
1456  if (s == "On")
1457  return SpinInteractionType::On;
1458  if (s == "Off")
1459  return SpinInteractionType::Off;
1460  throw IncorrectTypeInAssignment("The value for key \"" +
1461  std::string(key_) + "\" should be " +
1462  "\"On\" or \"Off\".");
1463  }
1464 
1465  /**
1466  * Set hard string transition mode from configuration values.
1467  *
1468  * \return HardStringTransitionMode.
1469  * \throw IncorrectTypeInAssignment in case a mode that is not available is
1470  * provided as a configuration value.
1471  */
1472  operator HardStringTransitionMode() const {
1473  const std::string s = operator std::string();
1474 
1475  if (s == "Exponential") {
1477  }
1478  if (s == "Custom_Range") {
1480  }
1481 
1482  throw IncorrectTypeInAssignment("The value for key \"" +
1483  std::string(key_) +
1484  "\" should be "
1485  "\"Exponential\" or \"Custom_Range\".");
1486  }
1487  /**
1488  * Set total cross section strategy from configuration values.
1489  *
1490  * \return TotalCrossSectionStrategy.
1491  * \throw IncorrectTypeInAssignment in case a strategy that is
1492  * not available is provided as a configuration value.
1493  */
1494  operator TotalCrossSectionStrategy() const {
1495  const std::string s = operator std::string();
1496  if (s == "BottomUp") {
1498  }
1499  if (s == "TopDown") {
1501  }
1502  if (s == "TopDownMeasured") {
1504  }
1506  "The value for key \"" + std::string(key_) + "\" should be " +
1507  "\"BottomUp\", \"TopDown\" " + "or \"TopDownMeasured\".");
1508  }
1509 
1510  /**
1511  * Set how pseudo-resonances are used from configuration values.
1512  *
1513  * \return PseudoResonance.
1514  * \throw IncorrectTypeInAssignment in case a key that is
1515  * not available is provided as a configuration value.
1516  */
1517  operator PseudoResonance() const {
1518  const std::string s = operator std::string();
1519  if (s == "None") {
1520  return PseudoResonance::None;
1521  }
1522  if (s == "Largest") {
1523  return PseudoResonance::Largest;
1524  }
1525  if (s == "Closest") {
1526  return PseudoResonance::Closest;
1527  }
1528  if (s == "LargestFromUnstable") {
1530  }
1531  if (s == "ClosestFromUnstable") {
1533  }
1535  "The value for key \"" + std::string(key_) + "\" should be " +
1536  "\"None\", \"Largest\", \"Closest\", \"LargestFromUnstable\", or "
1537  "\"ClosestFromUnstable\".");
1538  }
1539 
1540  /**
1541  * Set condition of fluidization for hydrodynamic initial
1542  * conditions.
1543  *
1544  * \return FluidizationType.
1545  * \throw IncorrectTypeInAssignment in case a key that is
1546  * not available is provided as a configuration value.
1547  */
1548  operator FluidizationType() const {
1549  const std::string s = operator std::string();
1550  if (s == "Constant_Tau") {
1552  } else if (s == "Dynamic") {
1554  }
1555  throw IncorrectTypeInAssignment("The value for key \"" +
1556  std::string(key_) + "\" should be " +
1557  "\"Constant_Tau\" or \"Dynamic\".");
1558  }
1559 
1560  /**
1561  * Set OutputOnlyFinal for particles output from configuration values.
1562  *
1563  * \return OutputOnlyFinal.
1564  * \throw IncorrectTypeInAssignment in case only_final value that is
1565  * not available is provided as a configuration value.
1566  */
1567  operator OutputOnlyFinal() const {
1568  const std::string s = operator std::string();
1569  if (s == "Yes") {
1570  return OutputOnlyFinal::Yes;
1571  }
1572  if (s == "No") {
1573  return OutputOnlyFinal::No;
1574  }
1575  if (s == "IfNotEmpty") {
1577  }
1578  throw IncorrectTypeInAssignment("The value for key \"" +
1579  std::string(key_) + "\" should be " +
1580  "\"Yes\", \"No\" or \"IfNotEmpty\".");
1581  }
1582 
1583  /**
1584  * Set FluidizableProcessesBitSet from configuration values.
1585  *
1586  * \return FluidizableProcessesBitSet with all included reaction types.
1587  * \throw IncorrectTypeInAssignment in case a reaction type that is not
1588  * available is provided as a configuration value.
1589  */
1590  operator FluidizableProcessesBitSet() const {
1591  const std::vector<std::string> v = operator std::vector<std::string>();
1593  for (const auto &x : v) {
1594  if (x == "All") {
1595  s.set();
1596  break;
1597  } else if (x == "Elastic") {
1599  } else if (x == "Decay") {
1601  } else if (x == "Inelastic") {
1603  } else if (x == "SoftString") {
1605  } else if (x == "HardString") {
1607  } else {
1609  "The value for key \"" + std::string(key_) +
1610  "\" should be \"All\", \"Elastic\", \"Decay\", "
1611  "\"Inelastic\", \"SoftString\", \"HardString\", "
1612  "or any combination of these.");
1613  }
1614  }
1615  return s;
1616  }
1617  };
1618 
1619  /**
1620  * Create a sub-object that has its root node at the given node.
1621  *
1622  * \note This constructor is not explicit because it can be called only from
1623  * inside Configuration and by making it explicit a return would require the
1624  * copy constructor.
1625  */
1626  Configuration(const YAML::Node &node) // NOLINT(runtime/explicit) : see above
1627  : root_node_(YAML::Clone(node)) {}
1628 
1629  /**
1630  * Descend in and if needed modify the YAML tree from the given node using the
1631  * provided keys.
1632  *
1633  * After this call nodes corresponding to the passed keys are guaranteed to
1634  * exist in the tree.
1635  *
1636  * \param[in] keys Keys that will be possibly added to the YAML tree.
1637  *
1638  * \return Node in the tree reached by using the provided keys.
1639  */
1640  YAML::Node find_node_creating_it_if_not_existing(KeyLabels keys) const;
1641 
1642  /**
1643  * Descend in the YAML tree from the given node using the provided keys.
1644  *
1645  * This function \b must not use the YAML::Node subscript operator, which is
1646  * at the very bottom level creating an undefined node in the %YAML tree,
1647  * hence "wasting" some memory. Note that the fact that this method is marked
1648  * as const does not forbid to use the access operator on <tt>root_node_</tt>,
1649  * because of how the %YAML library works. We want the tree to be completely
1650  * untouched by this method.
1651  *
1652  * \param[in] keys Keys that will be used to descend the YAML tree.
1653  *
1654  * \return \c std::optional<YAML::Node> containing the node in the tree
1655  * reached by using the provided keys, if it exists;
1656  * \return \c std::nullopt otherwise.
1657  *
1658  * \note It has been decided to return an optional value rather than throwing
1659  * an exception because this method is going to be used in other methods
1660  * like \c has_value and putting there a try-catch block would probably
1661  * cause a performance cost that can be avoided (exceptions on the
1662  * exceptional path are expensive).
1663  */
1664  std::optional<YAML::Node> find_existing_node(KeyLabels keys) const;
1665 
1666  /**
1667  * Utility type trait (general case) for the take and read public methods.
1668  */
1669  template <class T>
1670  struct isMap : std::false_type {};
1671  /**
1672  * Utility type trait (special case) for the take and read public methods.
1673  */
1674  template <class Key, class Value>
1675  struct isMap<std::map<Key, Value>> : std::true_type {};
1676 
1677  /**
1678  * Validate and return the passed key value
1679  *
1680  * @tparam T The type of the key whose value should be validated.
1681  * @param key The key whose value should be validated.
1682  * @param value The value of the key to be validated.
1683  *
1684  * @return The validated value of the key.
1685  *
1686  * @throw std::invalid_argument If the value is invalid w.r.t. the key
1687  * validator.
1688  */
1689  template <typename T>
1690  T get_validated_key_value(const Key<T> &key, const T &value) const {
1691  if (key.validate(value)) {
1692  return value;
1693  } else {
1694  throw InvalidKeyValue("Invalid value detected in configuration file:\n " +
1695  key.as_yaml(value));
1696  }
1697  }
1698 
1699  /**
1700  * Take a key unconditionally, validate and return its value.
1701  *
1702  * \tparam T The type of the key to be taken.
1703  * \param key The key to be taken.
1704  *
1705  * \return T The validated value of the taken key.
1706  *
1707  * \note This method internally calls the private \c take method and you
1708  * should refer to it e.g. for possible exceptions thrown.
1709  */
1710  template <typename T>
1712  // The following assignment converts a Configuration::Value into T
1713  T value = take({key.labels().begin(), key.labels().end()});
1714  return get_validated_key_value(key, value);
1715  }
1716 
1717  /**
1718  * Read a key unconditionally, validate and return its value.
1719  *
1720  * \tparam T The type of the key to be taken.
1721  * \param key The key to be taken.
1722  *
1723  * \return T The validated value of the key.
1724  *
1725  * \note This method internally calls the private \c read method and you
1726  * should refer to it e.g. for possible exceptions thrown.
1727  */
1728  template <typename T>
1730  // The following assignment converts a Configuration::Value into T
1731  T value = read({key.labels().begin(), key.labels().end()});
1732  return get_validated_key_value(key, value);
1733  }
1734 
1735  /**
1736  * This is the implementation detail to take a key. Having a non-templated
1737  * method for it it allows for defining the method in the source file, which
1738  * isn't possible for a template.
1739  *
1740  * \param[in] labels the labels of the key to be taken.
1741  *
1742  * \return The \c Value of the key.
1743  *
1744  * \throw std::runtime_error if the key does not exist.
1745  */
1746  Value take(KeyLabels labels);
1747 
1748  /**
1749  * This is the implementation detail to read a key. Having a non-templated
1750  * method for it it allows for defining the method in the source file, which
1751  * isn't possible for a template.
1752  *
1753  * \param[in] labels the labels of the key to be read.
1754  *
1755  * \return The \c Value of the key.
1756  *
1757  * \throw std::runtime_error if the key does not exist.
1758  */
1759  Value read(KeyLabels labels) const;
1760 
1761  /**
1762  * Find out whether a key has been already taken.
1763  *
1764  * \param labels The labels of the key to be checked.
1765  *
1766  * \return \c true if the key was already taken,
1767  * \return \c false otherwise.
1768  */
1770  return std::find(existing_keys_already_taken_.begin(),
1772  labels) != existing_keys_already_taken_.end();
1773  }
1774 
1775  /// The general_config.yaml contents - fully parsed
1776  YAML::Node root_node_{YAML::NodeType::Map};
1777 
1778  /// Counter to be able to optionally throw in destructor
1779  int uncaught_exceptions_{std::uncaught_exceptions()};
1780 
1781  /// List of taken keys to throw on taking same key twice
1782  std::vector<KeyLabels> existing_keys_already_taken_{};
1783 };
1784 
1785 } // namespace smash
1786 
1787 #endif // SRC_INCLUDE_SMASH_CONFIGURATION_H_
Proxy object to be used when taking or reading keys in the configuration.
Value(const YAML::Node &n, const char *key)
Construct the Value wrapper from a YAML::Node.
Value(const Value &)=delete
If you want to copy this you're doing it wrong.
const char *const key_
The key to be interpreted.
const YAML::Node node_
a YAML leaf node
Value & operator=(const Value &)=delete
If you want to copy this you're doing it wrong.
Interface to the SMASH configuration files.
Is
Return type of Configuration::validate which conveys more information that simply a two-state boolean...
YAML::Node find_node_creating_it_if_not_existing(KeyLabels keys) const
Descend in and if needed modify the YAML tree from the given node using the provided keys.
T take(const Key< T > &key, T default_value)
Alternative method to take a key value, specifying the default value.
void set_value(Key< U > key, T &&value)
Overwrite the value of the YAML node corresponding to the specified key.
Configuration(const Configuration &)=delete
Prevent Configuration objects from being copied.
void merge_yaml(const std::string &yaml)
Merge the configuration in yaml into the existing tree.
std::string to_string() const
Return a string of the current YAML tree.
Configuration extract_sub_configuration(KeyLabels section, Configuration::GetEmpty empty_if_not_existing=Configuration::GetEmpty::No)
Create a new configuration from a then-removed section of the present object.
T read(const Key< T > &key) const
Additional interface for SMASH to read configuration values without removing them.
T unconditionally_read_and_validate(const Key< T > &key) const
Read a key unconditionally, validate and return its value.
bool is_empty() const
Configuration(const std::filesystem::path &path)
Read config.yaml from the specified path.
T unconditionally_take_and_validate(const Key< T > &key)
Take a key unconditionally, validate and return its value.
std::optional< YAML::Node > find_existing_node(KeyLabels keys) const
Descend in the YAML tree from the given node using the provided keys.
void enclose_into_section(KeyLabels section)
Enclose the configuration into the given section.
YAML::Node root_node_
The general_config.yaml contents - fully parsed.
Configuration(const YAML::Node &node)
Create a sub-object that has its root node at the given node.
int uncaught_exceptions_
Counter to be able to optionally throw in destructor.
bool did_key_exist_and_was_it_already_taken(const KeyLabels &labels) const
Find out whether a key has been already taken.
Configuration(const char *yaml)
bool has_value(const Key< T > &key) const
Return whether there is a non-empty value behind the requested key (which is supposed not to refer to...
void clear()
Erase the Configuration content.
Configuration(const char *yaml, const char sflag)
Initialize configuration with a YAML formatted string.
bool has_key(const Key< T > &key) const
Return whether the configuration has a (possibly empty) non-map key.
bool has_section(const KeyLabels &labels) const
Return whether there is a (possibly empty) section with the given labels.
Is validate(bool full_validation=true) const
Validate content of configuration in terms of YAML keys.
~Configuration() noexcept(false)
Destroy the object, optionally throwing if not all keys were taken.
GetEmpty
Flag to tune method(s) behavior such that it is descriptive from the caller side.
static const char InitializeFromYAMLString
Flag to mark initialization with a YAML formatted string.
Configuration extract_complete_sub_configuration(KeyLabels section, Configuration::GetEmpty empty_if_not_existing=Configuration::GetEmpty::No)
Alternative method to extract a sub-configuration, which retains the labels from the top-level in the...
T take(const Key< T > &key)
The default interface for SMASH to read configuration values.
void remove_all_entries_in_section_but_one(const std::string &key, KeyLabels section={})
Remove all entries in the given section except for key.
std::vector< std::string > list_upmost_nodes()
Lists all YAML::Nodes from the configuration setup.
std::vector< KeyLabels > existing_keys_already_taken_
List of taken keys to throw on taking same key twice.
T read(const Key< T > &key, T default_value) const
Alternative method to read a key value, specifying the default value.
T get_validated_key_value(const Key< T > &key, const T &value) const
Validate and return the passed key value.
Configuration & operator=(const Configuration &)=delete
Prevent Configuration objects from being copy-assigned.
Object to store a YAML input file key together with metadata associated to it.
Definition: key.h:126
bool has_dependent_default() const noexcept
Ask whether the default value depends on other other keys.
Definition: key.h:225
bool validate(const default_type &value) const noexcept
Get whether the given key value is valid.
Definition: key.h:282
default_type default_value() const
Get the default value of the key.
Definition: key.h:217
const KeyLabels & labels() const
Method to access the Key labels.
Definition: key.h:365
std::string as_yaml([[maybe_unused]] std::optional< default_type > value=std::nullopt) const noexcept
Build and return a YAML-formatted string in the compact form (using braces as single line).
Definition: key.h:328
default_type type
Let the clients of this class have access to the key type.
Definition: key.h:208
SmearingMode
Modes of smearing.
DileptonBremsPionFormFactor
Option to use form factors in dilepton bremsstrahlung as described in Shyam:2010vr .
@ FF2
Photon couples 40% directly to intrinsice quark structure of pion and 60% indirectly via meson.
@ FF1
Photon couples to pion only via meson.
@ Off
Don't use form factors, i.e. multiply by 1.
FermiMotion
Option to use Fermi Motion.
@ On
Use fermi motion in combination with potentials.
@ Frozen
Use fermi motion without potentials.
@ Off
Don't use fermi motion.
ThermalizationAlgorithm
Defines the algorithm used for the forced thermalization.
FluidizationType
Possible methods to convert SMASH particle into fluid cells.
@ ConstantTau
Hypersurface crossed at a fixed proper time.
@ Dynamic
Dynamic fluidization based on local densities.
NNbarTreatment
Treatment of N Nbar Annihilation.
@ NoAnnihilation
No Annihilation.
@ TwoToFive
Directly create 5 pions, use with multi-particle reactions.
@ Resonances
Use intermediate Resonances.
@ Strings
Use string fragmentation.
CharmRescattering
Possible charm scattering options.
@ T_Matrix
Charm interactions via T-matrix approach.
@ Resonances
Charm interactions via resonances.
@ None
Disable charm interactions.
TimeStepMode
The time step mode.
@ Fixed
Use fixed time step.
@ None
Don't use time steps; propagate from action to action.
@ EckartDensity
Density in the Eckart frame.
@ Tmn
Energy-momentum tensor in lab frame.
@ LandauVelocity
Velocity of the Landau rest frame.
@ j_QBS
Electric (Q), baryonic (B) and strange (S) currents.
@ TmnLandau
Energy-momentum tensor in Landau rest frame.
HardStringTransitionMode
Select the model used for the transition from soft to hard string excitation.
@ Custom_Range
Smooth transition within a user-defined invariant energy range.
@ Exponential
Legacy exponential splitting based on the hard string cross section.
std::bitset< 5 > FluidizableProcessesBitSet
TotalCrossSectionStrategy
Determine how total cross sections for collision finding should be computed.
@ TopDownMeasured
Mix the two above, using the parametrizations only for measured processes, and summing up partials fo...
@ TopDown
Use parametrizations based on existing data, rescaling with AQM for unmeasured processes.
@ BottomUp
Sum the existing partial contributions.
Sampling
Possible methods of impact parameter sampling.
@ Quadratic
Sample from areal / quadratic distribution.
@ Custom
Sample from custom, user-defined distribution.
@ Uniform
Sample from uniform distribution.
@ From_HardString
@ From_Inelastic
@ From_Elastic
@ From_SoftString
@ From_Decay
std::bitset< 4 > MultiParticleReactionsBitSet
Container for the n to m reactions in the code.
CalculationFrame
The calculation frame.
@ NNbar_5to2
@ A3_Nuclei_4to2
@ Deuteron_3to2
@ Meson_3to1
DerivativesMode
Modes of calculating the gradients.
CollisionCriterion
Criteria used to check collisions.
@ Stochastic
Stochastic Criteiron.
@ Geometric
Geometric criterion.
@ Covariant
Covariant Criterion.
FieldDerivativesMode
Modes of calculating the field gradients: chain rule or direct.
SphereInitialCondition
Initial condition for a particle in a sphere.
@ ThermalMomentaBoltzmann
A thermalized ensemble is generated, with momenta sampled from a Maxwell-Boltzmann distribution.
@ IC_ES
Off-equilibrium distribution used in massless comparisons of SMASH to the extended universe metric.
@ ThermalMomentaQuantum
A thermalized ensemble is generated, with momenta of baryons(mesons) sampled from a Fermi(Bose) distr...
@ IC_Massive
A generalization of IC_ES for the non-zero mass case; note that there is currently no analytical comp...
@ IC_2M
Off-equilibrium distribution used in massless comparisons of SMASH to the extended universe metric.
@ IC_1M
Off-equilibrium distribution used in massless comparisons of SMASH to the extended universe metric.
PseudoResonance
Which pseudo-resonance fills the inelastic gap in the transition to string region of cross sections.
@ Closest
Resonance with the pole mass closest from the invariant mass of incoming particles for all processes.
@ ClosestFromUnstable
Closest resonance for a given mass from processes with at least one resonance in the incoming particl...
@ None
No pseudo-resonance is created.
@ LargestFromUnstable
Heaviest possible resonance from processes with at least one resonance in the incoming particles.
@ Largest
Resonance of largest mass for all processes.
@ KN_to_KDelta
@ KN_to_KN
@ NN_to_NR
@ PiDeuteron_to_pidprime
@ NDeuteron_to_Ndprime
@ Strangeness_exchange
@ Charm_T_matrix
@ PiDeuteron_to_NN
@ NN_to_DR
std::bitset< 11 > ReactionsBitSet
Container for the 2 to 2 reactions in the code.
DensityType
Allows to choose which kind of density to calculate.
OutputOnlyFinal
Whether and when only final state particles should be printed.
@ IfNotEmpty
Print only final-state particles, and those only if the event is not empty.
@ Yes
Print only final-state particles.
@ No
Print initial, intermediate and final-state particles.
SpinInteractionType
Possible spin interaction types.
@ On
All spin interactions.
@ Off
No spin interactions.
BoxInitialCondition
Initial condition for a particle in a box.
@ ThermalMomentaBoltzmann
A thermalized ensemble is generated, with momenta sampled from a Maxwell-Boltzmann distribution.
@ ThermalMomentaQuantum
A thermalized ensemble is generated, with momenta of baryons(mesons) sampled from a Fermi(Bose) distr...
@ PeakedMomenta
All particles have the same momentum with T being the temperature.
ExpansionMode
Defines properties of expansion for the metric (e.g.
constexpr int n
Neutron.
Definition: action.h:24
@ Value
Normal default with a value associated to it.
std::vector< std::string_view > KeyLabels
Descriptive alias for storing key labels, i.e.
Definition: key.h:46
std::string to_string(ThermodynamicQuantity quantity)
Convert a ThermodynamicQuantity enum value to its corresponding string.
Definition: stringify.cc:26
Convert from YAML::Node to SMASH-readable (C++) format and vice versa.
Definition: configuration.h:42
static bool decode(const Node &node, T &x)
Deserialization: Converts a YAML::Node to any SMASH-readable data type and returns whether or not thi...
Definition: configuration.h:69
static Node encode(const T &x)
Serialization: Converts x (of any type) to a YAML::Node.
Definition: configuration.h:50
Thrown if the file does not exist.
Thrown when the types in the config file and C++ don't match.
Thrown if a Key has an invalid value.
Thrown for YAML parse errors.
Thrown if a required Key is taken/read but is missing.
Thrown if a Key is taken twice.
Utility type trait (general case) for the take and read public methods.