Version: SMASH-3.4
experiment.h
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2013-2026
3  * SMASH Team
4  *
5  * GNU General Public License (GPLv3 or later)
6  */
7 #ifndef SRC_INCLUDE_SMASH_EXPERIMENT_H_
8 #define SRC_INCLUDE_SMASH_EXPERIMENT_H_
9 
10 #include <algorithm>
11 #include <limits>
12 #include <memory>
13 #include <set>
14 #include <string>
15 #include <utility>
16 #include <vector>
17 
18 #include "actionfinderfactory.h"
19 #include "actions.h"
22 #include "chrono.h"
23 #include "decayactionsfinder.h"
25 #include "dynamicfluidfinder.h"
26 #include "energymomentumtensor.h"
27 #include "fields.h"
28 #include "fluidizationaction.h"
29 #include "fourvector.h"
30 #include "grandcan_thermalizer.h"
31 #include "grid.h"
33 #include "icparameters.h"
34 #include "numeric_cast.h"
35 #include "outputparameters.h"
36 #include "pauliblocking.h"
37 #include "potential_globals.h"
38 #include "potentials.h"
39 #include "propagation.h"
40 #include "quantumnumbers.h"
41 #include "scatteractionphoton.h"
42 #include "scatteractionsfinder.h"
43 #include "stringprocess.h"
44 #include "thermalizationaction.h"
45 // Output
46 #include "binaryoutput.h"
47 #ifdef SMASH_USE_HEPMC
48 #include "hepmcoutput.h"
49 #endif
50 #ifdef SMASH_USE_RIVET
51 #include "rivetoutput.h"
52 #endif
53 #include "icoutput.h"
54 #include "oscaroutput.h"
56 #include "thermodynamicoutput.h"
57 #ifdef SMASH_USE_ROOT
58 #include "rootoutput.h"
59 #endif
60 #include "freeforallaction.h"
61 #include "vtkoutput.h"
62 #include "wallcrossingaction.h"
63 
64 namespace std {
65 /**
66  * Print time span in a human readable way:
67  * time < 10 min => seconds
68  * 10 min < time < 3 h => minutes
69  * time > 3h => hours
70  *
71  * \note This operator has to be in the \c std namespace for argument dependent
72  * lookup to find it. If it were in the smash namespace then the code would not
73  * compile since none of its arguments is a type from the smash namespace.
74  */
75 template <typename T, typename Ratio>
76 static ostream &operator<<(ostream &out,
77  const chrono::duration<T, Ratio> &seconds) {
78  using Seconds = chrono::duration<double>;
79  using Minutes = chrono::duration<double, std::ratio<60>>;
80  using Hours = chrono::duration<double, std::ratio<60 * 60>>;
81  constexpr Minutes threshold_for_minutes{10};
82  constexpr Hours threshold_for_hours{3};
83  if (seconds < threshold_for_minutes) {
84  return out << Seconds(seconds).count() << " [s]";
85  }
86  if (seconds < threshold_for_hours) {
87  return out << Minutes(seconds).count() << " [min]";
88  }
89  return out << Hours(seconds).count() << " [h]";
90 }
91 } // namespace std
92 
93 namespace smash {
94 static constexpr int LMain = LogArea::Main::id;
95 static constexpr int LInitialConditions = LogArea::InitialConditions::id;
96 
97 /**
98  * Non-template interface to Experiment<Modus>.
99  *
100  * This class allows to call into the public interface of Experiment<Modus>
101  * without the need to know the specific `Modus`. The interface is meant for
102  * `main()` to set up the experiment and then run takes over.
103  */
105  public:
106  ExperimentBase() = default;
107  /**
108  * The virtual destructor avoids undefined behavior when destroying derived
109  * objects.
110  */
111  virtual ~ExperimentBase() = default;
112 
113  /**
114  * Factory method that creates and initializes a new Experiment<Modus>.
115  *
116  * This function creates a new Experiment object. The Modus template
117  * argument is determined by the \p config argument.
118  *
119  * \param[inout] config The configuration object that sets all initial
120  * conditions of the experiment. \param[in] output_path The directory where
121  * the output files are written.
122  *
123  * \return An owning pointer to the Experiment object, using the
124  * ExperimentBase interface.
125  *
126  * \throws InvalidModusRequest This exception is thrown if the \p
127  * Modus string in the \p config object does not contain a valid
128  * string.
129  *
130  * Most of the Configuration values are read starting from this function. The
131  * configuration itself is documented in \ref doxypage_input_conf_general
132  */
133  static std::unique_ptr<ExperimentBase> create(
134  Configuration &config, const std::filesystem::path &output_path);
135 
136  /**
137  * Runs the experiment.
138  *
139  * The constructor does the setup of the experiment. The run function executes
140  * the complete experiment.
141  */
142  virtual void run() = 0;
143 
144  /**
145  * \ingroup exception
146  * Exception class that is thrown if an invalid modus is requested from the
147  * Experiment factory.
148  */
149  struct InvalidModusRequest : public std::invalid_argument {
150  using std::invalid_argument::invalid_argument;
151  };
152 
153  /**
154  * \ingroup exception
155  * Exception class that is thrown if the requested output path in the
156  * Experiment factory is not existing.
157  */
158  struct NonExistingOutputPathRequest : public std::invalid_argument {
159  using std::invalid_argument::invalid_argument;
160  };
161 };
162 
163 template <typename Modus>
164 class Experiment;
165 template <typename Modus>
166 std::ostream &operator<<(std::ostream &out, const Experiment<Modus> &e);
167 
168 /**
169  * The main class, where the simulation of an experiment is executed.
170  *
171  * The Experiment class owns all data (maybe indirectly) relevant for the
172  * execution of the experiment simulation. The experiment can be conducted in
173  * different running modi. Since the abstraction of these differences should not
174  * incur any overhead, the design is built around the Policy pattern.
175  *
176  * The Policy pattern was defined by Andrei Alexandrescu in his book "Modern C++
177  * Design: Generic Programming and Design Patterns Applied". Addison-Wesley:
178  * > A policy defines a class interface or a class template interface.
179  * > The interface consists of one or all of the following: inner type
180  * > definitions, member functions, and member variables.
181  * The policy pattern can also be understood as a compile-time variant of the
182  * strategy pattern.
183  *
184  * The \p Modus template parameter defines the "policy" of the Experiment class.
185  * It determines several aspects of the experiment execution *at compile time*.
186  * The original strategy pattern would select these differences *at run time*,
187  * thus incurring an overhead. This overhead becomes severe in cases where calls
188  * to strategy/policy functions are done very frequently. Using the policy
189  * pattern, the compiler can fully optimize: It creates a new instance of all
190  * functions in Experiment for all different Modus types.
191  */
192 template <typename Modus>
193 class Experiment : public ExperimentBase {
194  friend class ExperimentBase;
195 
196  public:
197  /**
198  * Runs the experiment.
199  *
200  * The constructor does the setup of the experiment. The run function executes
201  * the complete experiment.
202  */
203  void run() override;
204 
205  /**
206  * Create a new Experiment.
207  *
208  * This constructor is only called from the ExperimentBase::create factory
209  * method.
210  *
211  * \param[inout] config The Configuration object contains all initial setup
212  * of the experiment. It is forwarded to the constructors of member variables
213  * as needed. Note that the object is passed by non-const reference. This is
214  * only necessary for bookkeeping: Values are not only read, but actually
215  * taken out of the object. Thus, all values that remain were not used.
216  * \param[in] output_path The directory where the output files are written.
217  */
218  explicit Experiment(Configuration &config,
219  const std::filesystem::path &output_path);
220 
221  /**
222  * This is called in the beginning of each event. It initializes particles
223  * according to selected modus, resets the clock and saves the initial
224  * conserved quantities for subsequent sanity checks.
225  */
226  void initialize_new_event();
227 
228  /**
229  * Runs the time evolution of an event with fixed-size time steps or without
230  * timesteps, from action to actions.
231  * Within one timestep (fixed) evolution from action to action is invoked.
232  *
233  * \param[in] t_end Time until run_time_evolution is run, in SMASH this is the
234  * configured end_time, but it might differ if SMASH is used
235  * as an external library
236  * \param[in] add_plist A by-default empty particle list which is added to the
237  * current particle content of the system
238  * \param[in] remove_plist A by-default empty particle list which is removed
239  * from the current particle content of the system
240  *
241  * \note
242  * This function is meant to take over ownership of the to-be-added/removed
243  * particle lists and that's why these are passed by rvalue reference.
244  */
245  void run_time_evolution(const double t_end, ParticleList &&add_plist = {},
246  ParticleList &&remove_plist = {});
247 
248  /**
249  * Performs the final decays of an event
250  *
251  * \throws runtime_error if found actions cannot be performed
252  */
253  void do_final_interactions();
254 
255  /// Output at the end of an event
256  void final_output();
257 
258  /**
259  * Provides external access to SMASH particles. This is helpful if SMASH
260  * is used as a 3rd-party library.
261  */
263  /// Getter for all ensembles
264  std::vector<Particles> *all_ensembles() { return &ensembles_; }
265 
266  /**
267  * Provides external access to SMASH calculation modus. This is helpful if
268  * SMASH is used as a 3rd-party library.
269  */
270  Modus *modus() { return &modus_; }
271 
272  /**
273  * Increases the event number by one. This function is helpful if
274  * SMASH is used as a 3rd-party library.
275  */
276  void increase_event_number();
277 
278  private:
279  /**
280  * Perform the given action.
281  *
282  * \param[in] action The action to perform
283  * \param[in] i_ensemble index of ensemble in which action is performed
284  * \param[in] include_pauli_blocking wheter to take Pauli blocking into
285  * account. Skipping Pauli blocking is
286  * useful for example for final decays.
287  * \return False if the action is
288  * rejected either due to invalidity or
289  * Pauli-blocking, or true if it's accepted and performed.
290  */
291  bool perform_action(Action &action, int i_ensemble,
292  bool include_pauli_blocking = true);
293  /**
294  * Create a list of output files
295  *
296  * \param[in] format Format of the output file (e.g. Root, Oscar, Vtk)
297  * \param[in] content Content of the output (e.g. particles, collisions)
298  * \param[in] output_path Path of the output file
299  * \param[in] par Output options.(e.g. Extended)
300  */
301  void create_output(const std::string &format, const std::string &content,
302  const std::filesystem::path &output_path,
303  const OutputParameters &par);
304 
305  /**
306  * Propagate all particles until time to_time without any interactions
307  * and shine dileptons.
308  *
309  * \param[in] to_time Time at the end of propagation [fm]
310  * \param[in, out] particles Particles to be propagated
311  */
312  void propagate_and_shine(double to_time, Particles &particles);
313 
314  /**
315  * Performs all the propagations and actions during a certain time interval
316  * neglecting the influence of the potentials. This function is called in
317  * either the time stepless cases or the cases with time steps.
318  *
319  * \param[in, out] actions Actions occur during a certain time interval.
320  * They provide the ending times of the propagations and
321  * are updated during the time interval.
322  * \param[in] i_ensemble index of ensemble to be evolved
323  * \param[in] end_time_propagation time until propagation should be
324  * performed
325  */
326  void run_time_evolution_timestepless(Actions &actions, int i_ensemble,
327  const double end_time_propagation);
328 
329  /// Intermediate output during an event
330  void intermediate_output();
331 
332  /// Recompute potentials on lattices if necessary.
333  void update_potentials();
334 
335  /**
336  * Calculate the minimal size for the grid cells such that the
337  * ScatterActionsFinder will find all collisions within the maximal
338  * transverse distance (which is determined by the maximal cross section).
339  *
340  * \param[in] dt The current time step size [fm]
341  * \return The minimal required size of cells
342  */
343  double compute_min_cell_length(double dt) const {
346  }
347  return std::sqrt(4 * dt * dt + max_transverse_distance_sqr_);
348  }
349 
350  /// Shortcut for next output time
351  double next_output_time() const {
352  return parameters_.outputclock->next_time();
353  }
354 
355  /**
356  * Counts the number of ensembles in wich interactions took place at the end
357  * of an event
358  */
360 
361  /**
362  * Checks wether the desired number events have been calculated
363  *
364  * \return wether the experiment is is_finished
365  */
366  bool is_finished();
367 
368  /**
369  * Struct of several member variables.
370  * These variables are combined into a struct for efficient input to functions
371  * outside of this class.
372  */
374 
375  /// Structure to precalculate and hold parameters for density computations
377 
378  /**
379  * Instance of the Modus template parameter. May store modus-specific data
380  * and contains modus-specific function implementations.
381  */
382  Modus modus_;
383 
384  /// Complete particle list, all ensembles in one vector
385  std::vector<Particles> ensembles_;
386 
387  /**
388  * An instance of potentials class, that stores parameters of potentials,
389  * calculates them and their gradients.
390  */
391  std::unique_ptr<Potentials> potentials_;
392 
393  /**
394  * An instance of PauliBlocker class that stores parameters needed
395  * for Pauli blocking calculations and computes phase-space density.
396  */
397  std::unique_ptr<PauliBlocker> pauli_blocker_;
398 
399  /**
400  * A list of output formaters. They will be called to write the state of the
401  * particles to file.
402  */
403  OutputsList outputs_;
404 
405  /// The Dilepton output
406  OutputPtr dilepton_output_;
407 
408  /// The Photon output
409  OutputPtr photon_output_;
410 
411  /**
412  * Whether the projectile and the target collided.
413  * One value for each ensemble.
414  */
415  std::vector<bool> projectile_target_interact_;
416 
417  /**
418  * The initial nucleons in the ColliderModus propagate with
419  * beam_momentum_, if Fermi motion is frozen. It's only valid in
420  * the ColliderModus, so is set as an empty vector by default.
421  */
422  std::vector<FourVector> beam_momentum_ = {};
423 
424  /// The Action finder objects
425  std::vector<std::unique_ptr<ActionFinderInterface>> action_finders_;
426 
427  /// The Dilepton Action Finder
428  std::unique_ptr<DecayActionsFinderDilepton> dilepton_finder_;
429 
430  /// The (Scatter) Actions Finder for Direct Photons
431  std::unique_ptr<ActionFinderInterface> photon_finder_;
432 
433  /// Number of fractional photons produced per single reaction
435 
436  /// 4-current for j_QBS lattice output
437  std::unique_ptr<DensityLattice> j_QBS_lat_;
438 
439  /// Baryon density on the lattice
440  std::unique_ptr<DensityLattice> jmu_B_lat_;
441 
442  /// Isospin projection density on the lattice
443  std::unique_ptr<DensityLattice> jmu_I3_lat_;
444 
445  /// Electric charge density on the lattice
446  std::unique_ptr<DensityLattice> jmu_el_lat_;
447 
448  /// Mean-field A^mu on the lattice
449  std::unique_ptr<FieldsLattice> fields_lat_;
450 
451  /**
452  * Custom density on the lattices.
453  * In the config user asks for some kind of density for printout.
454  * Baryon and isospin projection density are anyway needed for potentials.
455  * If user asks for some other density type for printout, it will be handled
456  * using jmu_custom variable.
457  */
458  std::unique_ptr<DensityLattice> jmu_custom_lat_;
459 
460  /// Type of density for lattice printout
462 
463  /**
464  * Lattices for Skyrme or VDF potentials (evaluated in the local rest frame)
465  * times the baryon flow 4-velocity
466  */
467  std::unique_ptr<RectangularLattice<FourVector>> UB_lat_ = nullptr;
468 
469  /**
470  * Lattices for symmetry potentials (evaluated in the local rest frame) times
471  * the isospin flow 4-velocity
472  */
473  std::unique_ptr<RectangularLattice<FourVector>> UI3_lat_ = nullptr;
474 
475  /**
476  * Lattices for the electric and magnetic components of the Skyrme or VDF
477  * force
478  */
479  std::unique_ptr<RectangularLattice<std::pair<ThreeVector, ThreeVector>>>
481 
482  /// Lattices for the electric and magnetic component of the symmetry force
483  std::unique_ptr<RectangularLattice<std::pair<ThreeVector, ThreeVector>>>
485 
486  /// Lattices for electric and magnetic field in fm^-2
487  std::unique_ptr<RectangularLattice<std::pair<ThreeVector, ThreeVector>>>
489 
490  /// Lattices of energy-momentum tensors for printout
491  std::unique_ptr<RectangularLattice<EnergyMomentumTensor>> Tmn_;
492 
493  /// Auxiliary lattice for values of jmu at a time step t0
494  std::unique_ptr<RectangularLattice<FourVector>> old_jmu_auxiliary_;
495  /// Auxiliary lattice for values of jmu at a time step t0 + dt
496  std::unique_ptr<RectangularLattice<FourVector>> new_jmu_auxiliary_;
497  /// Auxiliary lattice for calculating the four-gradient of jmu
498  std::unique_ptr<RectangularLattice<std::array<FourVector, 4>>>
500 
501  /// Auxiliary lattice for values of Amu at a time step t0
502  std::unique_ptr<RectangularLattice<FourVector>> old_fields_auxiliary_;
503  /// Auxiliary lattice for values of Amu at a time step t0 + dt
504  std::unique_ptr<RectangularLattice<FourVector>> new_fields_auxiliary_;
505  /// Auxiliary lattice for calculating the four-gradient of Amu
506  std::unique_ptr<RectangularLattice<std::array<FourVector, 4>>>
508 
509  /// Whether to print the Eckart rest frame density
510  bool printout_rho_eckart_ = false;
511 
512  /// Whether to print the energy-momentum tensor
513  bool printout_tmn_ = false;
514 
515  /// Whether to print the energy-momentum tensor in Landau frame
516  bool printout_tmn_landau_ = false;
517 
518  /// Whether to print the 4-velocity in Landau frame
519  bool printout_v_landau_ = false;
520 
521  /// Whether to print the Q, B, S 4-currents
522  bool printout_j_QBS_ = false;
523 
524  /// Whether to print the thermodynamics quantities evaluated on the lattices
525  bool printout_lattice_td_ = false;
526 
527  /// Whether to print the thermodynamics quantities evaluated on the lattices,
528  /// point by point, in any format
530 
531  /// Whether to write the electric and magnetic fields to VTK files
532  bool printout_coulomb_vtk_ = false;
533 
534  /// Instance of class used for forced thermalization
535  std::unique_ptr<GrandCanThermalizer> thermalizer_;
536 
537  /**
538  * Pointer to the string process class object,
539  * which is used to set the random seed for PYTHIA objects in each event.
540  */
542 
543  /**
544  * Number of events.
545  *
546  * Event is a single simulation of a physical phenomenon:
547  * elementary particle or nucleus-nucleus collision. Result
548  * of a single SMASH event is random (by construction)
549  * as well as result of one collision in nature. To compare
550  * simulation with experiment one has to take ensemble averages,
551  * i.e. perform simulation and real experiment many times
552  * and compare average results.
553  *
554  * nevents_ is number of times single phenomenon (particle
555  * or nucleus-nucleus collision) will be simulated.
556  */
557  int nevents_ = 0;
558 
559  /**
560  * The number of ensembles, in which interactions take place, to be
561  * calculated.
562  *
563  * Can be specified as an inout instead of the number of events. In
564  * this case events will be calculated until this number of ensembles
565  * is reached.
566  */
568 
569  /**
570  * The way in which the number of calculated events is specified.
571  *
572  * Can be either a fixed number of simulated events or a minimum number
573  * of events that contain interactions.
574  */
576 
577  /// Current event
578  int event_ = 0;
579 
580  /// Number of ensembles containing an interaction
582 
583  /**
584  * Maximum number of events to be calculated in order obtain the desired
585  * number of non-empty events using the MinimumNonemptyEnsembles option.
586  */
587  int max_events_ = 0;
588 
589  /// simulation time at which the evolution is stopped.
590  const double end_time_;
591 
592  /**
593  * The clock's timestep size at start up
594  *
595  * Stored here so that the next event will remember this.
596  */
597  const double delta_time_startup_;
598 
599  /// This indicates whether to use the grid.
600  const bool use_grid_;
601 
602  /// This struct contains information on the metric to be used
604 
605  /// This indicates whether dileptons are switched on.
606  const bool dileptons_switch_;
607 
608  /**
609  * This indicates whether dilepton production via bremsstrahlung is
610  * switched on.
611  */
613 
614  /// This indicates whether photons are switched on.
615  const bool photons_switch_;
616 
617  /// This indicates whether bremsstrahlung is switched on.
619 
620  /**
621  * This indicates whether the experiment will be used as initial condition for
622  * hydrodynamics. Currently only the Collider modus can achieve this.
623  */
624  const bool IC_switch_;
625 
626  /// This indicates if the IC is dynamic.
627  const bool IC_dynamic_;
628 
629  /// This indicates whether to use time steps.
631 
632  /**
633  * Maximal distance at which particles can interact in case of the geometric
634  * criterion, squared
635  */
636  double max_transverse_distance_sqr_ = std::numeric_limits<double>::max();
637 
638  /**
639  * The conserved quantities of the system.
640  *
641  * This struct carries the sums of the single particle's various
642  * quantities as measured at the beginning of the evolution and can be
643  * used to regularly check if they are still good.
644  */
646 
647  /**
648  * The initial total mean field energy in the system.
649  * Note: will only be calculated if lattice is on.
650  */
652 
653  /// system starting time of the simulation
654  SystemTimePoint time_start_ = SystemClock::now();
655 
656  /// Type of density to be written to collision headers
658 
659  /**
660  * Total number of interactions for current timestep.
661  * For timestepless mode the whole run time is considered as one timestep.
662  */
663  uint64_t interactions_total_ = 0;
664 
665  /**
666  * Total number of interactions for previous timestep.
667  * For timestepless mode the whole run time is considered as one timestep.
668  */
670 
671  /**
672  * Total number of wall-crossings for current timestep.
673  * For timestepless mode the whole run time is considered as one timestep.
674  */
675  uint64_t wall_actions_total_ = 0;
676 
677  /**
678  * Total number of wall-crossings for previous timestep.
679  * For timestepless mode the whole run time is considered as one timestep.
680  */
682 
683  /**
684  * Total number of Pauli-blockings for current timestep.
685  * For timestepless mode the whole run time is considered as one timestep.
686  */
687  uint64_t total_pauli_blocked_ = 0;
688 
689  /**
690  * Total number of particles removed from the evolution in
691  * hypersurface crossing actions.
692  */
694 
695  /**
696  * Total number of discarded interactions, because they were invalidated
697  * before they could be performed.
698  */
700 
701  /**
702  * Total energy removed from the system in hypersurface crossing actions.
703  */
704  double total_energy_removed_ = 0.0;
705 
706  /**
707  * Total energy violation introduced by Pythia.
708  */
710 
711  /// This indicates whether kinematic cuts are enabled for the IC output
713 
714  /// random seed for the next event.
715  int64_t seed_ = -1;
716 
717  /**
718  * \ingroup logging
719  * Writes the initial state for the Experiment to the output stream.
720  * It automatically appends the output of the current Modus.
721  */
722  friend std::ostream &operator<<<>(std::ostream &out, const Experiment &e);
723 };
724 
725 /// Creates a verbose textual description of the setup of the Experiment.
726 template <typename Modus>
727 std::ostream &operator<<(std::ostream &out, const Experiment<Modus> &e) {
728  out << "End time: " << e.end_time_ << " fm\n";
729  out << e.modus_;
730  return out;
731 }
732 
733 template <typename Modus>
734 void Experiment<Modus>::create_output(const std::string &format,
735  const std::string &content,
736  const std::filesystem::path &output_path,
737  const OutputParameters &out_par) {
738  // Disable output which do not properly work with multiple ensembles
739  if (ensembles_.size() > 1) {
740  auto abort_because_of = [](const std::string &s) {
741  throw std::invalid_argument(
742  s + " output is not available with multiple parallel ensembles.");
743  };
744  if (content == "Initial_Conditions") {
745  abort_because_of("Initial_Conditions");
746  }
747  if ((format == "HepMC") || (format == "HepMC_asciiv3") ||
748  (format == "HepMC_treeroot")) {
749  abort_because_of("HepMC");
750  }
751  if (content == "Rivet") {
752  abort_because_of("Rivet");
753  }
754  if (content == "Collisions") {
755  logg[LExperiment].warn(
756  "Information coming from different ensembles in 'Collisions' output "
757  "is not distinguishable.\nSuch an output with multiple parallel "
758  "ensembles should only be used if later in the data analysis\nit is "
759  "not necessary to trace back which data belongs to which ensemble.");
760  }
761  }
762 
763  if (format == "VTK" && content == "Particles") {
764  outputs_.emplace_back(
765  std::make_unique<VtkOutput>(output_path, content, out_par));
766  } else if (format == "Root") {
767 #ifdef SMASH_USE_ROOT
768  if (content == "Initial_Conditions") {
769  outputs_.emplace_back(
770  std::make_unique<RootOutput>(output_path, "SMASH_IC", out_par));
771  } else {
772  outputs_.emplace_back(
773  std::make_unique<RootOutput>(output_path, content, out_par));
774  }
775 #else
776  logg[LExperiment].error(
777  "Root output requested, but Root support not compiled in");
778 #endif
779  } else if ((format == "Binary" || format == "Oscar2013_bin") &&
780  (content == "Collisions" || content == "Particles" ||
781  content == "Dileptons" || content == "Photons" ||
782  content == "Initial_Conditions")) {
783  outputs_.emplace_back(
784  create_binary_output(format, content, output_path, out_par));
785  } else if (format == "Oscar1999" || format == "Oscar2013") {
786  outputs_.emplace_back(
787  create_oscar_output(format, content, output_path, out_par));
788  } else if (format == "ASCII" &&
789  (content == "Particles" || content == "Collisions" ||
790  content == "Dileptons" || content == "Photons" ||
791  content == "Initial_Conditions")) {
792  outputs_.emplace_back(
793  create_oscar_output(format, content, output_path, out_par));
794  } else if (content == "Thermodynamics" && format == "ASCII") {
795  outputs_.emplace_back(
796  std::make_unique<ThermodynamicOutput>(output_path, content, out_par));
797  } else if (content == "Thermodynamics" &&
798  (format == "Lattice_ASCII" || format == "Lattice_Binary")) {
799  printout_full_lattice_any_td_ = true;
800  outputs_.emplace_back(std::make_unique<ThermodynamicLatticeOutput>(
801  output_path, content, out_par, format == "Lattice_ASCII",
802  format == "Lattice_Binary"));
803  } else if (content == "Thermodynamics" && format == "VTK") {
804  printout_lattice_td_ = true;
805  outputs_.emplace_back(
806  std::make_unique<VtkOutput>(output_path, content, out_par));
807  } else if (content == "Initial_Conditions" && format == "For_vHLLE") {
808  if (IC_dynamic_) {
809  throw std::invalid_argument(
810  "Dynamic initial conditions are only available in Oscar2013 and "
811  "Binary formats.");
812  }
813  outputs_.emplace_back(
814  std::make_unique<ICOutput>(output_path, "SMASH_IC_For_vHLLE", out_par));
815  } else if ((format == "HepMC") || (format == "HepMC_asciiv3") ||
816  (format == "HepMC_treeroot")) {
817 #ifdef SMASH_USE_HEPMC
818  if (content == "Particles") {
819  if ((format == "HepMC") || (format == "HepMC_asciiv3")) {
820  outputs_.emplace_back(std::make_unique<HepMcOutput>(
821  output_path, "SMASH_HepMC_particles", false, "asciiv3"));
822  } else if (format == "HepMC_treeroot") {
823 #ifdef SMASH_USE_HEPMC_ROOTIO
824  outputs_.emplace_back(std::make_unique<HepMcOutput>(
825  output_path, "SMASH_HepMC_particles", false, "root"));
826 #else
827  logg[LExperiment].error(
828  "Requested HepMC_treeroot output not available, "
829  "ROOT or HepMC3_ROOTIO missing or not found by cmake.");
830 #endif
831  }
832  } else if (content == "Collisions") {
833  if ((format == "HepMC") || (format == "HepMC_asciiv3")) {
834  outputs_.emplace_back(std::make_unique<HepMcOutput>(
835  output_path, "SMASH_HepMC_collisions", true, "asciiv3"));
836  } else if (format == "HepMC_treeroot") {
837 #ifdef SMASH_USE_HEPMC_ROOTIO
838  outputs_.emplace_back(std::make_unique<HepMcOutput>(
839  output_path, "SMASH_HepMC_collisions", true, "root"));
840 #else
841  logg[LExperiment].error(
842  "Requested HepMC_treeroot output not available, "
843  "ROOT or HepMC3_ROOTIO missing or not found by cmake.");
844 #endif
845  }
846  } else {
847  logg[LExperiment].error(
848  "HepMC only available for Particles and "
849  "Collisions content. Requested for " +
850  content + ".");
851  }
852 #else
853  logg[LExperiment].error(
854  "HepMC output requested, but HepMC support not compiled in");
855 #endif
856  } else if (content == "Coulomb" && format == "VTK") {
857  printout_coulomb_vtk_ = true;
858  outputs_.emplace_back(
859  std::make_unique<VtkOutput>(output_path, "Fields", out_par));
860  } else if (content == "Rivet") {
861 #ifdef SMASH_USE_RIVET
862  // flag to ensure that the Rivet format has not been already assigned
863  static bool rivet_format_already_selected = false;
864  // if the next check is true, then we are trying to assign the format twice
865  if (rivet_format_already_selected) {
866  logg[LExperiment].warn(
867  "Rivet output format can only be one, either YODA or YODA-full. "
868  "Only your first valid choice will be used.");
869  return;
870  }
871  if (format == "YODA") {
872  outputs_.emplace_back(std::make_unique<RivetOutput>(
873  output_path, "SMASH_Rivet", false, out_par.rivet_parameters));
874  rivet_format_already_selected = true;
875  } else if (format == "YODA-full") {
876  outputs_.emplace_back(std::make_unique<RivetOutput>(
877  output_path, "SMASH_Rivet_full", true, out_par.rivet_parameters));
878  rivet_format_already_selected = true;
879  } else {
880  logg[LExperiment].error("Rivet format " + format +
881  "not one of YODA or YODA-full");
882  }
883 #else
884  logg[LExperiment].error(
885  "Rivet output requested, but Rivet support not compiled in");
886 #endif
887  } else {
888  logg[LExperiment].error()
889  << "Unknown combination of format (" << format << ") and content ("
890  << content << "). Fix the config.";
891  }
892 
893  logg[LExperiment].info() << "Added output " << content << " of format "
894  << format << "\n";
895 }
896 
897 /**
898  * Gathers all general Experiment parameters.
899  *
900  * \param[inout] config Configuration element
901  * \return The ExperimentParameters struct filled with values from the
902  * Configuration
903  */
905 
906 template <typename Modus>
908  const std::filesystem::path &output_path)
909  : parameters_(create_experiment_parameters(config)),
910  density_param_(DensityParameters(parameters_)),
911  modus_(std::invoke([&]() {
912  /* This immediately invoked lambda is a work-around to cope with the
913  * fact that the "Collisions_Within_Nucleus" key belongs to the
914  * "Collider" section, but is used by the ScatterActionsFinder through
915  * the ScatterActionsFinderParameters member. Here that key is taken
916  * from the main configuration and put there back after the "Collider"
917  * section is extracted. If this were not done in this way, the
918  * sub-configuration given to ColliderModus would be deleted not empty
919  * at the end of its constructor and this would throw an exception.*/
921  const bool restore_key = config.has_value(key);
922  const bool temporary_taken_key = config.take(key);
923  auto modus_config =
925  if (restore_key) {
926  config.set_value(key, temporary_taken_key);
927  }
928  return Modus{std::move(modus_config), parameters_};
929  })),
930  ensembles_(parameters_.n_ensembles),
931  end_time_(config.take(InputKeys::gen_endTime)),
932  delta_time_startup_(parameters_.labclock->timestep_duration()),
933  use_grid_(config.take(InputKeys::gen_useGrid)),
934  metric_(config.take(InputKeys::gen_metricType),
936  dileptons_switch_(config.take(InputKeys::collTerm_dileptons_decays)),
937  dileptons_bremsstrahlung_switch_(
939  photons_switch_(
941  photons_bremsstrahlung_switch_(
943  IC_switch_(config.has_section(InputSections::o_initialConditions) &&
944  modus_.is_IC_for_hybrid()),
945  IC_dynamic_(IC_switch_ ? (modus_.IC_parameters().type ==
947  : false),
948  time_step_mode_(config.take(InputKeys::gen_timeStepMode)) {
949  logg[LExperiment].info() << *this;
950 
951  const bool user_wants_nevents = config.has_value(InputKeys::gen_nevents);
952  const bool user_wants_min_nonempty =
954  if (user_wants_nevents == user_wants_min_nonempty) {
955  throw std::invalid_argument(
956  "Please specify either Nevents or Minimum_Nonempty_Ensembles.");
957  }
958  if (user_wants_nevents) {
959  event_counting_ = EventCounting::FixedNumber;
960  nevents_ = config.take(InputKeys::gen_nevents);
961  } else {
962  event_counting_ = EventCounting::MinimumNonEmpty;
963  minimum_nonempty_ensembles_ =
965  int max_ensembles =
967  max_events_ = numeric_cast<int>(std::ceil(
968  static_cast<double>(max_ensembles) / parameters_.n_ensembles));
969  }
970 
971  // covariant derivatives can only be done with covariant smearing
972  if (parameters_.derivatives_mode == DerivativesMode::CovariantGaussian &&
973  parameters_.smearing_mode != SmearingMode::CovariantGaussian) {
974  throw std::invalid_argument(
975  "Covariant Gaussian derivatives only make sense for Covariant Gaussian "
976  "smearing!");
977  }
978 
979  if (parameters_.coll_crit == CollisionCriterion::Stochastic &&
980  (time_step_mode_ != TimeStepMode::Fixed || !use_grid_)) {
981  throw std::invalid_argument(
982  "The stochastic criterion can only be employed for fixed time step "
983  "mode and with a grid!");
984  }
985 
986  if (modus_.is_box() && (time_step_mode_ != TimeStepMode::Fixed)) {
987  throw std::invalid_argument(
988  "The box modus can only be used with the fixed time step mode!");
989  }
990 
991  logg[LExperiment].info("Using ", parameters_.testparticles,
992  " testparticles per particle.");
993  logg[LExperiment].info("Using ", parameters_.n_ensembles,
994  " parallel ensembles.");
995 
996  if (modus_.is_box() && config.read(InputKeys::collTerm_totXsStrategy) !=
998  logg[LExperiment].warn(
999  "To preserve detailed balance in a box simulation, it is recommended\n"
1000  "to use the bottom-up strategy for evaluating total cross sections.\n"
1001  "Consider adding the following line to the 'Collision_Term' section "
1002  "in your configuration file:\n"
1003  " Total_Cross_Section_Strategy: \"BottomUp\"");
1004  }
1005  if (modus_.is_box() && config.read(InputKeys::collTerm_pseudoresonance) !=
1007  logg[LExperiment].warn(
1008  "To preserve detailed balance in a box simulation, it is recommended "
1009  "to not include the pseudoresonances,\nas they artificially increase "
1010  "the resonance production without changing the corresponding "
1011  "decay.\nConsider adding the following line to the 'Collision_Term' "
1012  "section in your configuration file:\n Pseudoresonance: \"None\"");
1013  }
1014 
1015  const bool IC_output = config.has_section(InputSections::o_initialConditions);
1016  if (IC_output != modus_.is_IC_for_hybrid()) {
1017  throw std::invalid_argument(
1018  "The 'Initial_Conditions' subsection must be present in both 'Output' "
1019  "and 'Modi: Collider' sections.");
1020  }
1021 
1022  /* In collider setup with sqrts >= 200 GeV particles don't form continuously
1023  *
1024  * NOTE: This key has to be taken before the ScatterActionsFinder is created
1025  * because there the "String_Parameters" is extracted as sub-config and
1026  * all parameters but this one are taken. If this one is still there
1027  * the configuration temporary object will be destroyed not empty, hence
1028  * throwing an exception.
1029  */
1032  modus_.sqrt_s_NN() >= 200. ? -1. : 1.);
1033 
1034  // create finders
1035  if (dileptons_switch_) {
1036  dilepton_finder_ = std::make_unique<DecayActionsFinderDilepton>();
1037  }
1038  if (photons_switch_ || photons_bremsstrahlung_switch_) {
1039  n_fractional_photons_ =
1041  }
1042  if (parameters_.two_to_one) {
1043  if (parameters_.res_lifetime_factor < really_small) {
1044  logg[LExperiment].warn(
1045  "Resonance lifetime set to zero. Make sure resonances cannot "
1046  "interact inelastically (e.g. resonance chains), else SMASH is known "
1047  "to hang.");
1048  }
1049  action_finders_.emplace_back(
1050  std::make_unique<DecayActionsFinder>(parameters_));
1051  }
1052  bool no_coll = config.take(InputKeys::collTerm_noCollisions);
1053  if ((parameters_.two_to_one || parameters_.included_2to2.any() ||
1054  parameters_.included_multi.any() || parameters_.strings_switch) &&
1055  !no_coll) {
1056  auto scat_finder =
1057  std::make_unique<ScatterActionsFinder>(config, parameters_);
1058  max_transverse_distance_sqr_ =
1059  scat_finder->max_transverse_distance_sqr(parameters_.testparticles);
1060  process_string_ptr_ = scat_finder->get_process_string_ptr();
1061 
1062  /* Initialize Pythia's MPI machinery with a fixed center-of-mass energy
1063  * to ensure reproducible event generation. This prevents the MPI
1064  * initialization from depending on the energy of the current incoming
1065  * hadrons. The factor of 1.2 provides a safety margin for Fermi motion.
1066  *
1067  * TODO: Investigate whether a better choice for the MPI initialization
1068  * energy ceiling can be determined.
1069  */
1070 
1071  if (modus_.is_collider() && process_string_ptr_) {
1072  process_string_ptr_->set_mpi_initialization_sqrts(modus_.sqrt_s_NN() *
1073  1.2);
1074  }
1075  action_finders_.emplace_back(std::move(scat_finder));
1076  } else {
1077  max_transverse_distance_sqr_ =
1078  parameters_.maximum_cross_section / M_PI * fm2_mb;
1079  process_string_ptr_ = NULL;
1080  }
1081  if (modus_.is_box()) {
1082  action_finders_.emplace_back(
1083  std::make_unique<WallCrossActionsFinder>(parameters_.box_length));
1084  }
1085 
1086  if (IC_switch_) {
1087  const InitialConditionParameters &IC_parameters = modus_.IC_parameters();
1088  if (IC_dynamic_) {
1089  // Dynamic fluidization
1090  action_finders_.emplace_back(std::make_unique<DynamicFluidizationFinder>(
1091  modus_.fluid_lattice(), modus_.fluid_background(), IC_parameters));
1092  } else {
1093  // Iso-tau hypersurface
1094  double rapidity_cut = IC_parameters.rapidity_cut.value();
1095 
1096  if (modus_.calculation_frame_is_fixed_target() && rapidity_cut != 0.0) {
1097  throw std::runtime_error(
1098  "Rapidity cut for initial conditions output is not implemented "
1099  "in the fixed target calculation frame. \nPlease use "
1100  "\"center of velocity\" or \"center of mass\" as a "
1101  "\"Calculation_Frame\" instead.");
1102  }
1103 
1104  double pT_cut = IC_parameters.pT_cut.value();
1105  if (rapidity_cut > 0.0 || pT_cut > 0.0) {
1106  kinematic_cuts_for_IC_output_ = true;
1107  }
1108 
1109  const double proper_time = std::invoke([&]() {
1110  if (IC_parameters.proper_time.has_value()) {
1111  return IC_parameters.proper_time.value();
1112  } else {
1113  // Scaling factor applied to the switching time and the lower bound
1114  const double scaling = IC_parameters.proper_time_scaling.value();
1115  // Lower bound for the switching time
1116  const double lower_bound =
1117  IC_parameters.lower_bound.value() * scaling;
1118  // Default proper time is the passing time of the two nuclei
1119  const double default_proper_time =
1120  modus_.nuclei_passing_time() * scaling;
1121  if (default_proper_time >= lower_bound) {
1122  logg[LInitialConditions].info()
1123  << "Nuclei passing time is " << default_proper_time << " fm.";
1124  return default_proper_time;
1125  } else {
1126  logg[LInitialConditions].warn()
1127  << "Nuclei passing time is too short, hypersurface proper time "
1128  << "set to tau = " << lower_bound << " fm.";
1129  return lower_bound;
1130  }
1131  }
1132  });
1133 
1134  action_finders_.emplace_back(
1135  std::make_unique<HyperSurfaceCrossActionsFinder>(
1136  proper_time, rapidity_cut, pT_cut));
1137  }
1138  }
1139 
1141  logg[LExperiment].info() << "Pauli blocking is ON.";
1142  pauli_blocker_ = std::make_unique<PauliBlocker>(
1145  parameters_);
1146  }
1147 
1148  /*!\Userguide
1149  * \page doxypage_output
1150  *
1151  * \section output_directory_ Output directory
1152  *
1153  * Per default, the selected output files will be saved in the directory
1154  * `./data/<run_id>`, where `<run_id>` is an integer number starting from 0.
1155  * At the beginning of a run SMASH checks if the `./data/0` directory exists.
1156  * If it does not exist, it is created and all output files are written there.
1157  * If the directory already exists, SMASH tries for `./data/1`, `./data/2` and
1158  * so on until it finds a free number.
1159  *
1160  * The user can change output directory by a command line option, if
1161  * desired:
1162  * ```console
1163  * ./smash -o <user_output_dir>
1164  * ```
1165  * SMASH, by default, will create the specified folder if not existing or will
1166  * use it if the specified folder exists and is empty. However, if the folder
1167  * exists and is not empty SMASH will abort with an error to avoid overwriting
1168  * existing files.
1169  *
1170  * ---
1171  *
1172  * \section output_contents_ Output content
1173  *
1174  * Output in SMASH is distinguished by _content_ and _format_, where content
1175  * means the physical information contained in the output (e.g. list of
1176  * particles, list of interactions, thermodynamics, etc) and format (e.g.
1177  * ASCII, binary or ROOT). The same content can be printed out in several
1178  * formats _simultaneously_. See \ref doxypage_input_conf_output_examples for
1179  * examples.
1180  *
1181  * These are the possible contents offered by SMASH:
1182  *
1183  * - \b %Particles:
1184  * List of particles at regular time intervals in the computational
1185  * frame or (optionally) only at the event end.
1186  * - Available formats:
1187  * \ref doxypage_output_oscar_particles, \ref doxypage_output_ascii,
1188  * \ref doxypage_output_binary, \ref doxypage_output_root,
1189  * \ref doxypage_output_vtk, \ref doxypage_output_hepmc.
1190  * - \b Collisions:
1191  * List of interactions: collisions, decays, box wall crossings and
1192  * forced thermalizations. Information about incoming, outgoing
1193  * particles and the interaction itself is printed out.
1194  * - Available formats:
1195  * \ref doxypage_output_oscar_collisions, \ref doxypage_output_ascii,
1196  * \ref doxypage_output_binary, \ref doxypage_output_root,
1197  * \ref doxypage_output_hepmc.
1198  * - \b Dileptons:
1199  * Special dilepton output, see \ref doxypage_output_dileptons.
1200  * - Available formats:
1201  * \ref doxypage_output_oscar_collisions, \ref doxypage_output_ascii,
1202  * \ref doxypage_output_binary, \ref doxypage_output_root.
1203  * - \b Photons:
1204  * Special photon output, see \ref doxypage_output_photons.
1205  * - Available formats:
1206  * \ref doxypage_output_oscar_collisions, \ref doxypage_output_ascii,
1207  * \ref doxypage_output_binary, \ref doxypage_output_root.
1208  * - \b Thermodynamics:
1209  * This output allows to print out thermodynamic quantities, see
1210  * \ref input_output_thermodynamics_ "Thermodynamics".
1211  * - Available formats:
1212  * \ref doxypage_output_thermodyn,
1213  * \ref doxypage_output_thermodyn_lattice,
1214  * \ref doxypage_output_vtk.
1215  * - \b Initial_Conditions:
1216  * Special initial conditions output, see
1217  * \ref doxypage_output_initial_conditions for details.
1218  * - Available formats:
1219  * \ref doxypage_output_oscar_particles,
1220  * \ref doxypage_output_initial_conditions.
1221  * - \b Rivet:
1222  * Run Rivet analysis on generated events and output results, see
1223  * \ref doxypage_output_rivet for details.
1224  * - Available formats:
1225  * \ref doxypage_output_rivet.
1226  * - \b Coulomb:
1227  * Electric and magnetic fields, see \ref input_output_coulomb_
1228  * "Coulomb" and \ref doxypage_input_conf_pot_coulomb
1229  * "Coulomb potential" for further information.
1230  * - Available formats:
1231  * \ref doxypage_output_vtk
1232  *
1233  * \attention At the moment, the \b Initial_Conditions and \b Rivet outputs
1234  * content as well as the \b HepMC format cannot be used <u>with multiple
1235  * parallel ensembles</u> and SMASH will abort if the user tries to do so.
1236  * The \b Collisions content, instead, is allowed, although in it collisions
1237  * coming from different ensembles are simply printed all together in an
1238  * effectively unpredictable order and it is not possible to know which one
1239  * belongs to which ensemble. Therefore SMASH warns the user about this fact
1240  * and this setup should only be used if in the data analysis it is not
1241  * necessary to trace back which data belongs to which ensemble.
1242  *
1243  * ---
1244  *
1245  * \section list_of_output_formats Output formats
1246  *
1247  * Every output content can be printed out in several formats:
1248  *
1249  * - \b "ASCII" - a human-readable text-format table of values.
1250  * - For\n
1251  * &emsp;&emsp;`"Particles"` (\ref doxypage_output_oscar_particles),\n
1252  * &emsp;&emsp;`"Collisions"`, `"Dileptons"`, and `"Photons"` (\ref
1253  * doxypage_output_oscar_collisions)\n contents, it uses the \ref
1254  * doxypage_output_oscar "OSCAR block structure".\n In these cases it is
1255  * possible to customize the quantities to be printed into the output file
1256  * (\ref doxypage_output_ascii).
1257  * - For `"Initial_Conditions"` content the output has \ref
1258  * doxypage_output_initial_conditions "a fixed block structure".
1259  * - For `"Thermodynamics"` content the information stored in the output
1260  * file depends on few input keys. Furthermore,
1261  * - using \b "ASCII" as format, the \ref doxypage_output_thermodyn
1262  * "standard thermodynamics output" is produced;
1263  * - using \b "Lattice_ASCII", the \ref doxypage_output_thermodyn_lattice
1264  * "quantities on a lattice" are printed out.
1265  * - \b "Binary" - a binary, not human-readable list of values.
1266  * - The \ref doxypage_output_binary "binary output" is faster to read and
1267  * write than text outputs and all floating point numbers are printed with
1268  * their full precision.
1269  * - For `"Particles"`, `"Collisions"`, `"Dileptons"`, `"Photons"`, and
1270  * `"Initial_Conditions"` contents, it is a binary version equivalent to
1271  * the corresponding ASCII output.\n Also for binary format it is possible
1272  * to customize the quantities to be printed into the file.
1273  * - For the other contents the corresponding documentation pages about the
1274  * ASCII format contain further information.
1275  * - \b "Oscar1999", \b "Oscar2013" - aliases for the \b "ASCII" format with a
1276  * predefined set of quantities.
1277  * - \b "Oscar2013_bin" - alias for the \b "Binary" format with a predefined
1278  * set of quantities.
1279  * - \b "For_vHLLE" - an alias for the \b "ASCII" format exclusive to the
1280  * `"Initial_Conditions"` output content, which produces a file compatible
1281  * with the vHLLE hydrodynamic evolution code (see \ref
1282  * doxypage_output_initial_conditions). This is only available for
1283  * <tt>\ref key_MC_IC_type_ "Constant_Tau"</tt> fluidizations.
1284  * - \b "Root" - binary output in the format used by
1285  * <a href="http://root.cern.ch">the ROOT software</a>
1286  * - Even faster to read and write, requires less disk space
1287  * - Format description: \ref doxypage_output_root
1288  * - \b "VTK" - text output suitable for an easy visualization using
1289  * third-party software
1290  * - There are many different programs that can open a VTK file, although
1291  * their functionality varies.
1292  * - This output can be for example visualized with
1293  * <a href="http://paraview.org/">ParaView</a>. Alternatives are, e.g.,
1294  * <a href=https://docs.enthought.com/mayavi/mayavi/data.html>Mayavi</a>
1295  * or <a href=https://reference.wolfram.com/language/ref/format/VTK.html>
1296  * Mathematica</a>.
1297  * - Visit \ref doxypage_output_vtk for further information
1298  * - \b "HepMC_asciiv3", \b "HepMC_treeroot" - HepMC3 human-readble asciiv3 or
1299  * Tree ROOT format see \ref doxypage_output_hepmc for details
1300  * - \b "YODA", \b "YODA-full" - compact ASCII text format used by the
1301  * Rivet output, see \ref doxypage_output_rivet for details
1302  *
1303  * \note Output of coordinates for the "Collisions" content in
1304  * the periodic box has a feature:
1305  * \ref doxypage_output_collisions_box_modus
1306  */
1307 
1308  /*!\Userguide
1309  * \page doxypage_output_dileptons
1310  * The existence of a dilepton subsection in the collision term section of the
1311  * configuration file enables the dilepton production. In addition, the
1312  * dilepton output also needs to be enabled in the output section and dilepton
1313  * decays have to be uncommented in the used decaymodes.txt file. The output
1314  * file named \a Dileptons (followed by the appropriate suffix) is generated
1315  * when SMASH is executed. It's format is identical to the collision output
1316  * (see \ref doxypage_output_oscar_collisions), it does however only contain
1317  * information about the dilepton decays. \n Further, the block headers differ
1318  * from the usual collision output: <div class="fragment"> <div class="line">
1319  * <span class="preprocessor">
1320  * \# interaction in nin out nout rho density weight shining_weight partial
1321  * part_weight type proc_type </span></div>
1322  * </div>
1323  * where \li \key nin: Number of ingoing
1324  * particles (initial state particles) \li \key nout: Number of outgoing
1325  * particles (finalstate particles) \li \key density: Density at the
1326  * interaction point \li \key shining_weight: Shining weight of the
1327  * interaction. Explanation follows below. \li \key part_weight: The partial
1328  * weight of the interaction. For the dileptons, this coincides with the
1329  * branching ratio. \li \key proc_type: The type of the underlying process.
1330  * See process_type for possible types.
1331  *
1332  * Note, that "interaction", "in", "out", "rho", "weight", "partial" and
1333  * "type" are no variables, but words that are printed. \n
1334  * The dilepton output is available in binary, OSCAR1999, OSCAR2013 and
1335  * OSCAR2013 extended format. \n
1336  *
1337  * \n
1338  * \note
1339  * As dileptons are treated perturbatively, the produced dileptons are
1340  * only written to the dilepton output, but neither to the usual collision
1341  * output, nor to the particle lists.
1342  */
1343 
1344  /*!\Userguide
1345  * \page doxypage_output_photons
1346  * The existence of a photon subsection in the output section of the
1347  * configuration file enables the photon output.
1348  * If photons are enabled, the output file named \a Photons (followed by the
1349  * appropriate suffix) is generated when SMASH is executed. It's format is
1350  * identical to the collision output (see \ref
1351  * doxypage_output_oscar_collisions), it does however only contain information
1352  * about all particles participating in the photon producing interactions at
1353  * each timestep. \n Further, the block headers differ from the usual
1354  * collision output: <div class="fragment"> <div class="line"> <span
1355  * class="preprocessor">
1356  * \# interaction in nin out nout rho density weight photon_weight partial
1357  * part_weight type proc_type </span></div>
1358  * </div>
1359  * where
1360  * \li \key density: Density at the interaction point
1361  * \li \key photon_weight: Weight of the photon process relative to the
1362  * underlying hadronic interaction. Make sure to weigh each photon in your
1363  * analysis with this value. Otherwise the photon production is highly
1364  * overestimated.
1365  * \li \key part_weight: Always 0.0 for photon processes, as they
1366  * are hardcoded.
1367  * \li \key proc_type: The type of the underlying process. See
1368  * \ref doxypage_output_process_types for possible types.
1369  *
1370  * Note, that "interaction", "in", "out", "rho", "weight", "partial" and
1371  * "type" are no variables, but words that are printed. \n
1372  * The photon output is available in binary, OSCAR1999, OSCAR2013 and
1373  * OSCAR2013 extended format. \n
1374  *
1375  */
1376 
1377  /*!\Userguide
1378  * \page doxypage_output_initial_conditions
1379  * Once initial conditions are enabled, the output file named \a SMASH_IC
1380  * (followed by the appropriate suffix) is generated when SMASH is executed.
1381  * \n The output is available in Oscar1999, Oscar2013, ASCII, Oscar2013_bin
1382  * and ROOT format, as well as in an additional "For_vHLLE" format. The latter
1383  * is meant to directly serve as input for the vHLLE hydrodynamics code
1384  * \iref{Karpenko:2013wva}.\n
1385  *
1386  * <h3> Human-readable output </h3> In case of the Oscar1999 and Oscar2013
1387  * format, the structure is identical to the Oscar Particles format (see \ref
1388  * doxypage_output_oscar_particles), and the custom ASCII format is also
1389  * available. \n In contrast to the usual particles output however, the
1390  * initial conditions output provides a **list of all particles removed from
1391  * the evolution** at the time when crossing the hypersurface. This implies
1392  * that neither the initial particle list nor the particle list at each time
1393  * step is printed. \n The general Oscar structure as described in \ref
1394  * doxypage_output_oscar_particles is preserved.\n
1395  *
1396  * <h3>Binary output</h3>
1397  *
1398  * The binary initial conditions output also provides a list of all particles
1399  * removed from the evolution at the time when they cross the hypersurface.
1400  * For each removed particle a 'p' block is created that stores the particle
1401  * data.
1402  *
1403  * Only the particle block header differs from the standard binary output
1404  * structure described in \ref doxypage_output_binary; the individual particle
1405  * lines themselves use exactly the same layout as in the regular binary
1406  * output.
1407  *
1408  * The header has the following structure:
1409  * \code
1410  * char uint32_t
1411  * 'p' n_part_lines
1412  * \endcode
1413  *
1414  * \n Custom particle quantities are also available; their usage is described
1415  * in \ref doxypage_output_binary.
1416  *
1417  * <h3> ROOT output </h3>
1418  * The initial conditions output in shape of a list of all particles removed
1419  * from the SMASH evolution with a \c "Constant_Tau" fluidization criterion
1420  * is also available in ROOT format. Neither the initial nor the final
1421  * particle lists are printed, but the general structure for particle TTrees,
1422  * as described in \ref doxypage_output_root, is preserved.
1423  */
1424 
1425  /*!\Userguide
1426  * \page doxypage_output_spin
1427  * In order to enable spin output, two conditions have to be fulfilled:
1428  * 1. Spin interactions have to be enabled in the collision term section of
1429  * the configuration file.
1430  * 2. The spin components `spin0`, `spinx`, `spiny` and `spinz` have to be
1431  * specified in the `Quantities` list of the %Particles output subsection.
1432  * \see_key{key_output_particles_quantities_}
1433  *
1434  * Spin output is available in OSCAR2013 format. If spins are enabled, the
1435  * resulting output file contains the four components of the mean spin
1436  * (Pauli-Lubanski) vector \f$S^\mu\f$. The components of the mean spin vector
1437  * are set in two different ways, depending on the mode, in which SMASH is
1438  * used:
1439  * #### SMASH Collider Modus
1440  * In the collider mode, the components of the mean spin vector are sampled
1441  * from a gaussian distribution with a mean value of 0 in the particle rest
1442  * frame to ensure that the average polarization vanishes. This strategy
1443  * guarantees that spectators do not contribute with an artificial
1444  * polarization.
1445  * #### SMASH List Mode
1446  * In the list mode, the components of the mean spin vector are read from the
1447  * input file.
1448  */
1449 
1450  // create outputs
1452  " create OutputInterface objects");
1453  dens_type_ = config.take(InputKeys::output_densityType);
1454  logg[LExperiment].debug()
1455  << "Density type printed to headers: " << dens_type_;
1456 
1457  /* Parse configuration about output contents and formats, doing all logical
1458  * checks about specified formats, creating all needed output objects. Note
1459  * that we first extract the output sub configuration without the "Output:"
1460  * enclosing section to easily get all output contents and then we reintroduce
1461  * it for the actual parsing (remember we parse database keys which have
1462  * labels from the top-level only).
1463  */
1464  auto output_conf = config.extract_sub_configuration(
1466  if (output_path == "") {
1467  throw std::invalid_argument(
1468  "Invalid empty output path provided to Experiment constructor.");
1469  } else if (!std::filesystem::exists(output_path)) {
1470  logg[LExperiment].fatal(
1471  "Output path \"" + output_path.string() +
1472  "\" used to create an Experiment object does not exist.");
1473  throw NonExistingOutputPathRequest("Attempt to use not existing path.");
1474  } else if (!std::filesystem::is_directory(output_path)) {
1475  logg[LExperiment].fatal("Output path \"" + output_path.string() +
1476  "\" used to create an Experiment object "
1477  "exists, but it is not a directory.");
1478  throw std::logic_error("Attempt to use invalid existing path.");
1479  }
1480  const std::vector<std::string> output_contents =
1481  output_conf.list_upmost_nodes();
1482  if (output_conf.is_empty()) {
1483  logg[LExperiment].warn() << "No \"Output\" section found in the input "
1484  "file. No output file will be produced.";
1485  } else {
1486  output_conf.enclose_into_section(InputSections::output);
1487  }
1488  auto abort_because_of_invalid_input_file = []() {
1489  throw std::invalid_argument("Invalid configuration input file.");
1490  };
1491  std::vector<std::vector<std::string>> list_of_formats(output_contents.size());
1492  std::transform(
1493  output_contents.cbegin(), output_contents.cend(), list_of_formats.begin(),
1494  [&output_conf, &abort_because_of_invalid_input_file](
1495  const std::string &content) -> std::vector<std::string> {
1496  /* Note that the "Format" key is required and taking it will throw an
1497  * exception if not given by the user. We do here a try and catch to
1498  * give a more informative error message in this case, instead of just
1499  * using the general Configuration::take message.*/
1500  try {
1501  return output_conf.take(InputKeys::get_output_format_key(content));
1502  } catch (const Configuration::RequiredKeyMissing &) {
1503  logg[LExperiment].fatal() << "Unspecified list of formats for "
1504  << std::quoted(content) << " content.";
1505  abort_because_of_invalid_input_file();
1506  /* This is never reached, but it is needed to avoid compiler warnings
1507  * about missing return statement. In C++23 the [[noreturn]] attribute
1508  * can be used on lambda functions after the capturing brackets. */
1509  return {};
1510  }
1511  });
1512  const OutputParameters output_parameters(std::move(output_conf));
1513  for (std::size_t i = 0; i < output_contents.size(); ++i) {
1514  if (output_contents[i] == "Particles" ||
1515  output_contents[i] == "Collisions" ||
1516  output_contents[i] == "Dileptons" || output_contents[i] == "Photons" ||
1517  output_contents[i] == "Initial_Conditions") {
1518  assert(output_parameters.quantities.count(output_contents[i]) > 0);
1519  const bool quantities_given_nonempty =
1520  !output_parameters.quantities.at(output_contents[i]).empty();
1521  auto formats_contains = [&list_of_formats, &i](const std::string &label) {
1522  return std::find(list_of_formats[i].begin(), list_of_formats[i].end(),
1523  label) != list_of_formats[i].end();
1524  };
1525  const bool custom_ascii_requested = formats_contains("ASCII");
1526  const bool custom_binary_requested = formats_contains("Binary");
1527  const bool custom_requested =
1528  custom_ascii_requested || custom_binary_requested;
1529  const bool oscar2013_requested = formats_contains("Oscar2013");
1530  const bool oscar2013_bin_requested = formats_contains("Oscar2013_bin");
1531  const bool is_extended = (output_contents[i] == "Particles")
1532  ? output_parameters.part_extended
1533  : output_parameters.coll_extended;
1534  const auto &default_quantities =
1535  (is_extended) ? OutputDefaultQuantities::oscar2013extended
1536  : OutputDefaultQuantities::oscar2013;
1537  const bool are_given_quantities_oscar2013_ones =
1538  output_parameters.quantities.at(output_contents[i]) ==
1539  default_quantities;
1540  if (quantities_given_nonempty != custom_requested) {
1541  logg[LExperiment].fatal()
1542  << "Non-empty \"Quantities\" and \"ASCII\"/\"Binary\" format have "
1543  << "not been specified both for " << std::quoted(output_contents[i])
1544  << " in config file.";
1545  abort_because_of_invalid_input_file();
1546  }
1547  if (custom_ascii_requested && oscar2013_requested &&
1548  are_given_quantities_oscar2013_ones) {
1549  logg[LExperiment].fatal()
1550  << "The specified \"Quantities\" for the ASCII format are the same "
1551  "as those of the requested \"Oscar2013\"\nformat for "
1552  << std::quoted(output_contents[i])
1553  << " and this would produce the same output file twice.";
1554  abort_because_of_invalid_input_file();
1555  }
1556  if (custom_binary_requested && oscar2013_bin_requested &&
1557  are_given_quantities_oscar2013_ones) {
1558  logg[LExperiment].fatal()
1559  << "The specified \"Quantities\" for the binary format are the "
1560  "same as those of the requested \"Oscar2013_bin\"\nformat for "
1561  << std::quoted(output_contents[i])
1562  << " and this would produce the same output file twice.";
1563  abort_because_of_invalid_input_file();
1564  }
1565  }
1566 
1567  if (std::find(list_of_formats[i].begin(), list_of_formats[i].end(),
1568  "None") != list_of_formats[i].end()) {
1569  if (list_of_formats[i].size() > 1) {
1570  logg[LExperiment].fatal()
1571  << "Use of \"None\" output format together with other formats is "
1572  "not allowed.\nInvalid \"Format\" key for "
1573  << std::quoted(output_contents[i]) << " content.";
1574  abort_because_of_invalid_input_file();
1575  } else {
1576  // Clear vector so that the for below is skipped and no output created
1577  list_of_formats[i].clear();
1578  }
1579  } else if (std::set<std::string> tmp_set(list_of_formats[i].begin(),
1580  list_of_formats[i].end());
1581  list_of_formats[i].size() != tmp_set.size()) {
1582  const std::string old_formats = join(list_of_formats[i], ", "),
1583  new_formats = join(tmp_set, ", ");
1584  logg[LExperiment].warn()
1585  << "Found the same output format multiple times for "
1586  << std::quoted(output_contents[i])
1587  << " content. Duplicates will be ignored:\n 'Format: [" << old_formats
1588  << "] -> [" << new_formats << "]'";
1589  list_of_formats[i].assign(tmp_set.begin(), tmp_set.end());
1590  }
1591  }
1592 
1593  /* Repeat loop over output_contents here to create all outputs after having
1594  * validated all content specifications. This is more user-friendly. */
1595  std::size_t total_number_of_requested_formats = 0;
1596  for (std::size_t i = 0; i < output_contents.size(); ++i) {
1597  for (const auto &format : list_of_formats[i]) {
1598  create_output(format, output_contents[i], output_path, output_parameters);
1599  ++total_number_of_requested_formats;
1600  }
1601  }
1602 
1603  if (outputs_.size() != total_number_of_requested_formats) {
1604  logg[LExperiment].fatal()
1605  << "At least one invalid output format has been provided.";
1606  abort_because_of_invalid_input_file();
1607  }
1608 
1609  /* We can take away the Fermi motion flag, because the collider modus is
1610  * already initialized. We only need it when potentials are enabled, but we
1611  * always have to take it, otherwise SMASH will complain about unused
1612  * options. We have to provide a default value for modi other than Collider.
1613  */
1614  if (config.has_section(InputSections::potentials)) {
1615  if (time_step_mode_ == TimeStepMode::None) {
1616  logg[LExperiment].error() << "Potentials only work with time steps!";
1617  throw std::invalid_argument("Can't use potentials without time steps!");
1618  }
1619  if (modus_.fermi_motion() == FermiMotion::Frozen) {
1620  logg[LExperiment].error()
1621  << "Potentials don't work with frozen Fermi momenta! "
1622  "Use normal Fermi motion instead.";
1623  throw std::invalid_argument(
1624  "Can't use potentials "
1625  "with frozen Fermi momenta!");
1626  }
1627  logg[LExperiment].info() << "Potentials are ON. Timestep is "
1628  << parameters_.labclock->timestep_duration();
1629  // potentials need density calculation parameters from parameters_
1630  potentials_ = std::make_unique<Potentials>(
1632  parameters_);
1633  // make sure that vdf potentials are not used together with Skyrme
1634  // or symmetry potentials
1635  if (potentials_->use_skyrme() && potentials_->use_vdf()) {
1636  throw std::runtime_error(
1637  "Can't use Skyrme and VDF potentials at the same time!");
1638  }
1639  if (potentials_->use_symmetry() && potentials_->use_vdf()) {
1640  throw std::runtime_error(
1641  "Can't use symmetry and VDF potentials at the same time!");
1642  }
1643  if (potentials_->use_skyrme()) {
1644  logg[LExperiment].info() << "Skyrme potentials are:\n";
1645  logg[LExperiment].info()
1646  << "\t\tSkyrme_A [MeV] = " << potentials_->skyrme_a() << "\n";
1647  logg[LExperiment].info()
1648  << "\t\tSkyrme_B [MeV] = " << potentials_->skyrme_b() << "\n";
1649  logg[LExperiment].info()
1650  << "\t\t Skyrme_tau = " << potentials_->skyrme_tau() << "\n";
1651  }
1652  if (potentials_->use_symmetry()) {
1653  logg[LExperiment].info()
1654  << "Symmetry potential is:"
1655  << "\n S_pot [MeV] = " << potentials_->symmetry_S_pot() << "\n";
1656  }
1657  if (potentials_->use_vdf()) {
1658  logg[LExperiment].info() << "VDF potential parameters are:\n";
1659  logg[LExperiment].info() << "\t\tsaturation density [fm^-3] = "
1660  << potentials_->saturation_density() << "\n";
1661  for (int i = 0; i < potentials_->number_of_terms(); i++) {
1662  logg[LExperiment].info()
1663  << "\t\tCoefficient_" << i + 1 << " = "
1664  << 1000.0 * (potentials_->coeffs())[i] << " [MeV] \t Power_"
1665  << i + 1 << " = " << (potentials_->powers())[i] << "\n";
1666  }
1667  }
1668  // if potentials are on, derivatives need to be calculated
1669  if (parameters_.derivatives_mode == DerivativesMode::Off &&
1670  parameters_.field_derivatives_mode == FieldDerivativesMode::ChainRule) {
1671  throw std::invalid_argument(
1672  "Derivatives are necessary for running with potentials.\n"
1673  "Derivatives_Mode: \"Off\" only makes sense for "
1674  "Field_Derivatives_Mode: \"Direct\"!\nUse \"Covariant Gaussian\" or "
1675  "\"Finite difference\".");
1676  }
1677  // for computational efficiency, we want to turn off the derivatives of jmu
1678  // and the rest frame density derivatives if direct derivatives are used
1679  if (parameters_.field_derivatives_mode == FieldDerivativesMode::Direct) {
1680  parameters_.derivatives_mode = DerivativesMode::Off;
1681  parameters_.rho_derivatives_mode = RestFrameDensityDerivativesMode::Off;
1682  }
1683  switch (parameters_.derivatives_mode) {
1685  logg[LExperiment].info() << "Covariant Gaussian derivatives are ON";
1686  break;
1688  logg[LExperiment].info() << "Finite difference derivatives are ON";
1689  break;
1690  case DerivativesMode::Off:
1691  logg[LExperiment].info() << "Gradients of baryon current are OFF";
1692  break;
1693  }
1694  switch (parameters_.rho_derivatives_mode) {
1696  logg[LExperiment].info() << "Rest frame density derivatives are ON";
1697  break;
1699  logg[LExperiment].info() << "Rest frame density derivatives are OFF";
1700  break;
1701  }
1702  // direct or chain rule derivatives only make sense for the VDF potentials
1703  if (potentials_->use_vdf()) {
1704  switch (parameters_.field_derivatives_mode) {
1706  logg[LExperiment].info() << "Chain rule field derivatives are ON";
1707  break;
1709  logg[LExperiment].info() << "Direct field derivatives are ON";
1710  break;
1711  }
1712  }
1713  /*
1714  * Necessary safety checks
1715  */
1716  // VDF potentials need derivatives of rest frame density or fields
1717  if (potentials_->use_vdf() && (parameters_.rho_derivatives_mode ==
1719  parameters_.field_derivatives_mode ==
1721  throw std::runtime_error(
1722  "Can't use VDF potentials without rest frame density derivatives or "
1723  "direct field derivatives!");
1724  }
1725  // potentials require using gradients
1726  if (parameters_.derivatives_mode == DerivativesMode::Off &&
1727  parameters_.field_derivatives_mode == FieldDerivativesMode::ChainRule) {
1728  throw std::runtime_error(
1729  "Can't use potentials without gradients of baryon current (Skyrme, "
1730  "VDF)"
1731  " or direct field derivatives (VDF)!");
1732  }
1733  // direct field derivatives only make sense for the VDF potentials
1734  if (!(potentials_->use_vdf()) &&
1735  parameters_.field_derivatives_mode == FieldDerivativesMode::Direct) {
1736  throw std::invalid_argument(
1737  "Field_Derivatives_Mode: \"Direct\" only makes sense for the VDF "
1738  "potentials!\nUse Field_Derivatives_Mode: \"Chain Rule\" or comment "
1739  "this option out (Chain Rule is default)");
1740  }
1741  }
1742 
1743  // information about the type of smearing
1744  switch (parameters_.smearing_mode) {
1746  logg[LExperiment].info() << "Smearing type: Covariant Gaussian";
1747  break;
1749  logg[LExperiment].info() << "Smearing type: Discrete with weight = "
1750  << parameters_.discrete_weight;
1751  break;
1753  logg[LExperiment].info() << "Smearing type: Triangular with range = "
1754  << parameters_.triangular_range;
1755  break;
1756  }
1757 
1758  // Create lattices
1759  const bool has_lattice = config.has_section(InputSections::lattice);
1760  if (has_lattice) {
1761  const bool automatic = config.take(InputKeys::lattice_automatic);
1762  const bool all_geometrical_properties_specified =
1763  config.has_value(InputKeys::lattice_cellNumber) &&
1764  config.has_value(InputKeys::lattice_origin) &&
1765  config.has_value(InputKeys::lattice_sizes);
1766  if (!automatic && !all_geometrical_properties_specified) {
1767  throw std::invalid_argument(
1768  "The lattice was requested to be manually generated, but some\n"
1769  "lattice geometrical property was not specified. Be sure to provide\n"
1770  "both \"Cell_Number\" and \"Origin\" and \"Sizes\".");
1771  }
1772  if (automatic && all_geometrical_properties_specified) {
1773  throw std::invalid_argument(
1774  "The lattice was requested to be automatically generated, but all\n"
1775  "lattice geometrical properties were specified. In this case you\n"
1776  "need to set \"Automatic: False\".");
1777  }
1778  const bool periodic =
1779  config.take(InputKeys::lattice_periodic, modus_.is_box());
1780  const auto [l, n, origin] = [&config, automatic, this]() {
1781  if (!automatic) {
1782  return std::make_tuple<std::array<double, 3>, std::array<int, 3>,
1783  std::array<double, 3>>(
1784  config.take(InputKeys::lattice_sizes),
1785  config.take(InputKeys::lattice_cellNumber),
1786  config.take(InputKeys::lattice_origin));
1787  } else {
1788  std::array<double, 3> l_default{20., 20., 20.};
1789  std::array<int, 3> n_default{10, 10, 10};
1790  std::array<double, 3> origin_default{-20., -20., -20.};
1791  if (modus_.is_list() && !modus_.is_box()) {
1792  logg[LExperiment].fatal(
1793  "The lattice in List modus should be manually specified.");
1794  throw std::invalid_argument("Invalid Lattice setup.");
1795  } else if (modus_.is_collider()) {
1796  // Estimates on how far particles could get in x, y, z. The
1797  // default lattice is currently not contracted for afterburner runs
1798  const double gamma = modus_.sqrt_s_NN() / (2.0 * nucleon_mass);
1799  const double max_z = 5.0 / gamma + end_time_;
1800  const double estimated_max_transverse_velocity = 0.7;
1801  const double max_xy =
1802  5.0 + estimated_max_transverse_velocity * end_time_;
1803  origin_default = {-max_xy, -max_xy, -max_z};
1804  l_default = {2 * max_xy, 2 * max_xy, 2 * max_z};
1805  // For collider modus only, impose a minimum size of 30fm since the
1806  // heuristic above for determining the lattice expects the end time to
1807  // be large compared to the nucleus size
1808  const double minimum_extension = 30.;
1809  for (auto i = std::size_t{0}; i < l_default.size(); i++) {
1810  if (l_default[i] < minimum_extension) {
1811  logg[LExperiment].debug()
1812  << "Automatic lattice extension in direction " << i
1813  << " heuristically determined as " << l_default[i]
1814  << " fm is smaller than " << minimum_extension
1815  << " fm. Imposing minimum size.";
1816  l_default[i] = minimum_extension;
1817  origin_default[i] = -0.5 * minimum_extension;
1818  }
1819  }
1820  // Go for approximately 0.8 fm cell size and contract lattice in z by
1821  // gamma factor in case of smearing where smearing length is bound to
1822  // the lattice cell length
1823  const int n_xy = numeric_cast<int>(std::ceil(l_default[0] / 0.8));
1824  const bool to_be_contracted =
1825  (parameters_.smearing_mode == SmearingMode::Discrete ||
1826  parameters_.smearing_mode == SmearingMode::Triangular);
1827  const double contraction_factor = (to_be_contracted) ? gamma : 1.0;
1828  const int nz = numeric_cast<int>(
1829  std::ceil(l_default[2] / 0.8 * contraction_factor));
1830  n_default = {n_xy, n_xy, nz};
1831  } else if (modus_.is_box()) {
1832  origin_default = {0., 0., 0.};
1833  const double bl = modus_.length();
1834  l_default = {bl, bl, bl};
1835  const int n_xyz = numeric_cast<int>(std::ceil(bl / 0.5));
1836  n_default = {n_xyz, n_xyz, n_xyz};
1837  } else if (modus_.is_sphere()) {
1838  // Maximal distance from (0, 0, 0) at which a particle
1839  // may be found at the end of the simulation
1840  const double max_d = modus_.radius() + end_time_;
1841  origin_default = {-max_d, -max_d, -max_d};
1842  l_default = {2 * max_d, 2 * max_d, 2 * max_d};
1843  // Go for approximately 0.8 fm cell size
1844  const int n_xyz = numeric_cast<int>(std::ceil(2 * max_d / 0.8));
1845  n_default = {n_xyz, n_xyz, n_xyz};
1846  }
1847  // Take lattice properties from config to assign them to all lattices
1848  return std::make_tuple<std::array<double, 3>, std::array<int, 3>,
1849  std::array<double, 3>>(
1850  config.take(InputKeys::lattice_sizes, l_default),
1851  config.take(InputKeys::lattice_cellNumber, n_default),
1852  config.take(InputKeys::lattice_origin, origin_default));
1853  }
1854  }();
1855 
1856  logg[LExperiment].info()
1857  << "Lattice is ON. Origin = (" << origin[0] << "," << origin[1] << ","
1858  << origin[2] << "), sizes = (" << l[0] << "," << l[1] << "," << l[2]
1859  << "), number of cells = (" << n[0] << "," << n[1] << "," << n[2]
1860  << "), periodic = " << std::boolalpha << periodic;
1861 
1862  if (printout_lattice_td_ || printout_full_lattice_any_td_) {
1863  dens_type_lattice_printout_ = output_parameters.td_dens_type;
1864  printout_rho_eckart_ = output_parameters.td_rho_eckart;
1865  printout_tmn_ = output_parameters.td_tmn;
1866  printout_tmn_landau_ = output_parameters.td_tmn_landau;
1867  printout_v_landau_ = output_parameters.td_v_landau;
1868  printout_j_QBS_ = output_parameters.td_jQBS;
1869  }
1870  if (printout_tmn_ || printout_tmn_landau_ || printout_v_landau_) {
1871  Tmn_ = std::make_unique<RectangularLattice<EnergyMomentumTensor>>(
1872  l, n, origin, periodic, LatticeUpdate::AtOutput);
1873  }
1874  if (printout_j_QBS_) {
1875  j_QBS_lat_ = std::make_unique<DensityLattice>(l, n, origin, periodic,
1876  LatticeUpdate::AtOutput);
1877  }
1878  /* Create baryon and isospin density lattices regardless of config
1879  if potentials are on. This is because they allow to compute
1880  potentials faster */
1881  if (potentials_) {
1882  // Create auxiliary lattices for baryon four-current calculation
1883  old_jmu_auxiliary_ = std::make_unique<RectangularLattice<FourVector>>(
1884  l, n, origin, periodic, LatticeUpdate::EveryTimestep);
1885  new_jmu_auxiliary_ = std::make_unique<RectangularLattice<FourVector>>(
1886  l, n, origin, periodic, LatticeUpdate::EveryTimestep);
1887  four_gradient_auxiliary_ =
1888  std::make_unique<RectangularLattice<std::array<FourVector, 4>>>(
1889  l, n, origin, periodic, LatticeUpdate::EveryTimestep);
1890 
1891  if (potentials_->use_skyrme()) {
1892  jmu_B_lat_ = std::make_unique<DensityLattice>(
1893  l, n, origin, periodic, LatticeUpdate::EveryTimestep);
1894  UB_lat_ = std::make_unique<RectangularLattice<FourVector>>(
1895  l, n, origin, periodic, LatticeUpdate::EveryTimestep);
1896  FB_lat_ = std::make_unique<
1897  RectangularLattice<std::pair<ThreeVector, ThreeVector>>>(
1898  l, n, origin, periodic, LatticeUpdate::EveryTimestep);
1899  }
1900  if (potentials_->use_symmetry()) {
1901  jmu_I3_lat_ = std::make_unique<DensityLattice>(
1902  l, n, origin, periodic, LatticeUpdate::EveryTimestep);
1903  UI3_lat_ = std::make_unique<RectangularLattice<FourVector>>(
1904  l, n, origin, periodic, LatticeUpdate::EveryTimestep);
1905  FI3_lat_ = std::make_unique<
1906  RectangularLattice<std::pair<ThreeVector, ThreeVector>>>(
1907  l, n, origin, periodic, LatticeUpdate::EveryTimestep);
1908  }
1909  if (potentials_->use_coulomb()) {
1910  jmu_el_lat_ = std::make_unique<DensityLattice>(
1911  l, n, origin, periodic, LatticeUpdate::EveryTimestep);
1912  EM_lat_ = std::make_unique<
1913  RectangularLattice<std::pair<ThreeVector, ThreeVector>>>(
1914  l, n, origin, periodic, LatticeUpdate::EveryTimestep);
1915  }
1916  if (potentials_->use_vdf()) {
1917  jmu_B_lat_ = std::make_unique<DensityLattice>(
1918  l, n, origin, periodic, LatticeUpdate::EveryTimestep);
1919  UB_lat_ = std::make_unique<RectangularLattice<FourVector>>(
1920  l, n, origin, periodic, LatticeUpdate::EveryTimestep);
1921  FB_lat_ = std::make_unique<
1922  RectangularLattice<std::pair<ThreeVector, ThreeVector>>>(
1923  l, n, origin, periodic, LatticeUpdate::EveryTimestep);
1924  }
1925  if (parameters_.field_derivatives_mode == FieldDerivativesMode::Direct) {
1926  // Create auxiliary lattices for field calculation
1927  old_fields_auxiliary_ =
1928  std::make_unique<RectangularLattice<FourVector>>(
1929  l, n, origin, periodic, LatticeUpdate::EveryTimestep);
1930  new_fields_auxiliary_ =
1931  std::make_unique<RectangularLattice<FourVector>>(
1932  l, n, origin, periodic, LatticeUpdate::EveryTimestep);
1933  fields_four_gradient_auxiliary_ =
1934  std::make_unique<RectangularLattice<std::array<FourVector, 4>>>(
1935  l, n, origin, periodic, LatticeUpdate::EveryTimestep);
1936 
1937  // Create the fields lattice
1938  fields_lat_ = std::make_unique<FieldsLattice>(
1939  l, n, origin, periodic, LatticeUpdate::EveryTimestep);
1940  }
1941  }
1942  if (dens_type_lattice_printout_ == DensityType::Baryon && !jmu_B_lat_) {
1943  jmu_B_lat_ = std::make_unique<DensityLattice>(l, n, origin, periodic,
1944  LatticeUpdate::AtOutput);
1945  }
1946  if (dens_type_lattice_printout_ == DensityType::BaryonicIsospin &&
1947  !jmu_I3_lat_) {
1948  jmu_I3_lat_ = std::make_unique<DensityLattice>(l, n, origin, periodic,
1949  LatticeUpdate::AtOutput);
1950  }
1951  if (dens_type_lattice_printout_ != DensityType::None &&
1952  dens_type_lattice_printout_ != DensityType::BaryonicIsospin &&
1953  dens_type_lattice_printout_ != DensityType::Baryon) {
1954  jmu_custom_lat_ = std::make_unique<DensityLattice>(
1955  l, n, origin, periodic, LatticeUpdate::AtOutput);
1956  }
1957  }
1958 
1959  // Error messages for missing lattice or coulomb potential config requirements
1960  const bool has_coulomb_potential = potentials_ && potentials_->use_coulomb();
1961  const bool has_lattice_td_output =
1962  printout_lattice_td_ || printout_full_lattice_any_td_;
1963  if (has_lattice_td_output && !has_lattice) {
1964  logg[LExperiment].error(
1965  "If you want Thermodynamic VTK or Lattice output, configure a "
1966  "lattice for it.");
1967  }
1968  if (has_coulomb_potential && !has_lattice) {
1969  logg[LExperiment].error(
1970  "Coulomb potential requires a lattice. Please set it up in the "
1971  "configuration file.");
1972  }
1973  if (printout_coulomb_vtk_) {
1974  if (!has_lattice && !has_coulomb_potential) {
1975  logg[LExperiment].error(
1976  "Coulomb VTK output requires coulomb potential and a lattice. "
1977  "Please add both to the configuration file.");
1978  } else if (!has_lattice) {
1979  logg[LExperiment].error(
1980  "Coulomb VTK output requires a lattice. "
1981  "Please set it up in the configuration file.");
1982  } else if (!has_coulomb_potential) {
1983  logg[LExperiment].error(
1984  "Coulomb VTK output requires coulomb potential. "
1985  "Please add it to the configuration file.");
1986  }
1987  }
1988 
1989  // Warning for the mean field calculation if lattice is not on.
1990  if ((potentials_ != nullptr) && (jmu_B_lat_ == nullptr)) {
1991  logg[LExperiment].warn() << "Lattice is NOT used. Mean-field energy is "
1992  << "not going to be calculated.";
1993  }
1994 
1995  // Store pointers to potential and lattice accessible for Action
1996  if (parameters_.potential_affect_threshold) {
1997  UB_lat_pointer = UB_lat_.get();
1998  UI3_lat_pointer = UI3_lat_.get();
1999  pot_pointer = potentials_.get();
2000  }
2001 
2002  // Throw fatal if DerivativesMode == FiniteDifference and lattice is not on.
2003  if ((parameters_.derivatives_mode == DerivativesMode::FiniteDifference) &&
2004  (jmu_B_lat_ == nullptr)) {
2005  throw std::runtime_error(
2006  "Lattice is necessary to calculate finite difference gradients.");
2007  }
2008 
2009  // Create forced thermalizer
2011  Configuration th_conf = config.extract_complete_sub_configuration(
2013  thermalizer_ = modus_.create_grandcan_thermalizer(th_conf);
2014  }
2015 
2016  /* Take the seed setting only after the configuration was stored to a file
2017  * in smash.cc */
2018  seed_ = config.take(InputKeys::gen_randomseed);
2019 }
2020 
2021 /// String representing a horizontal line.
2022 const std::string hline(113, '-');
2023 
2024 /**
2025  * Generate a string which will be printed to the screen when SMASH is running
2026  *
2027  * \param[in] ensembles The simulated particles: one Particles object per
2028  * ensemble. The information about particles is used to check the
2029  * conservation of the total energy and momentum as well as print
2030  * other useful information.
2031  * \param[in] scatterings_this_interval Number of the scatterings occur within
2032  * the current timestep.
2033  * \param[in] conserved_initial Initial quantum numbers needed to check the
2034  * conservations.
2035  * \param[in] time_start Moment in the REAL WORLD when SMASH starts to run [s].
2036  * \param[in] time Current moment in SMASH [fm].
2037  * \param[in] E_mean_field Value of the mean-field contribution to the total
2038  * energy of the system at the current time.
2039  * \param[in] E_mean_field_initial Value of the mean-field contribution to the
2040  * total energy of the system at t=0.
2041  * \return 'Current time in SMASH [fm]', 'Total kinetic energy in the system
2042  * [GeV]', 'Total mean field energy in the system [GeV]', 'Total energy
2043  * in the system [GeV]', 'Total energy per particle [GeV]', 'Deviation
2044  * of the energy per particle from the initial value [GeV]', 'Number of
2045  * scatterings that occurred within the timestep', 'Total particle
2046  * number', 'Computing time consumed'.
2047  */
2048 std::string format_measurements(const std::vector<Particles> &ensembles,
2049  uint64_t scatterings_this_interval,
2050  const QuantumNumbers &conserved_initial,
2051  SystemTimePoint time_start, double time,
2052  double E_mean_field,
2053  double E_mean_field_initial);
2054 /**
2055  * Calculate the total mean field energy of the system; this will be printed to
2056  * the screen when SMASH is running. Using the baryon density lattice is
2057  * necessary.
2058  *
2059  * \param[in] potentials Parameters of the potentials used in the simulation.
2060  * \param[in] jmu_B_lat Lattice of baryon density and baryon current values as
2061  * well as their gradients at each lattice node.
2062  * \param[in] em_lattice Lattice containing the electric and magnetic field in
2063  * fm^-2 \param[in] parameters Parameters of the experiment, needed for the
2064  * access to the number of testparticles. \return Total mean field energy in the
2065  * Box.
2066  */
2068  const Potentials &potentials,
2070  RectangularLattice<std::pair<ThreeVector, ThreeVector>> *em_lattice,
2071  const ExperimentParameters &parameters);
2072 
2073 /**
2074  * Generate the EventInfo object which is passed to outputs_.
2075  *
2076  * \param[in] ensembles The simulated particles: one Particles object per
2077  * ensemble. Information about all particles (positions, momenta,
2078  * etc.)is passed to the output.
2079  * \param[in] E_mean_field Value of the mean-field contribution to the total
2080  * energy of the system at the current time.
2081  * \param[in] modus_impact_parameter The impact parameter
2082  * \param[in] parameters structure that holds various global parameters
2083  * such as testparticle number, see \ref ExperimentParameters
2084  * \param[in] projectile_target_interact true if there was at least one
2085  * collision
2086  * \param[in] kinematic_cut_for_SMASH_IC true if kinematic cuts in y or pT are
2087  enabled when exracting initial conditions for hydrodynamics
2088  */
2089 EventInfo fill_event_info(const std::vector<Particles> &ensembles,
2090  double E_mean_field, double modus_impact_parameter,
2091  const ExperimentParameters &parameters,
2092  bool projectile_target_interact,
2093  bool kinematic_cut_for_SMASH_IC);
2094 
2095 template <typename Modus>
2097  random::set_seed(seed_);
2098  logg[LExperiment].info() << "random number seed: " << seed_;
2099  /* Set seed for the next event. It has to be positive, so it can be entered
2100  * in the config.
2101  *
2102  * We have to be careful about the minimal integer, whose absolute value
2103  * cannot be represented. */
2104  int64_t r = random::advance();
2105  while (r == INT64_MIN) {
2106  r = random::advance();
2107  }
2108  seed_ = std::abs(r);
2109  /* Set the random seed used in PYTHIA hadronization
2110  * to be same with the SMASH one.
2111  * In this way we ensure that the results are reproducible
2112  * for every event if one knows SMASH random seed. */
2113  if (process_string_ptr_ != NULL) {
2114  process_string_ptr_->init_pythia_hadron_rndm();
2115  }
2116 
2117  for (Particles &particles : ensembles_) {
2118  particles.reset();
2119  }
2120 
2121  // Sample particles according to the initial conditions
2122  double start_time = -1.0;
2123 
2124  // Sample impact parameter only once per all ensembles
2125  // It should be the same for all ensembles
2126  if (modus_.is_collider()) {
2127  modus_.sample_impact();
2128  logg[LExperiment].info("Impact parameter = ", modus_.impact_parameter(),
2129  " fm");
2130  }
2131  for (Particles &particles : ensembles_) {
2132  start_time = modus_.initial_conditions(&particles, parameters_);
2133  }
2134  /* For box modus make sure that particles are in the box. In principle, after
2135  * a correct initialization they should be, so this is just playing it safe.
2136  */
2137  for (Particles &particles : ensembles_) {
2138  modus_.impose_boundary_conditions(&particles, outputs_);
2139  }
2140  // Reset the simulation clock
2141  double timestep = delta_time_startup_;
2142 
2143  switch (time_step_mode_) {
2144  case TimeStepMode::Fixed:
2145  break;
2146  case TimeStepMode::None:
2147  timestep = end_time_ - start_time;
2148  // Take care of the box modus + timestepless propagation
2149  const double max_dt = modus_.max_timestep(max_transverse_distance_sqr_);
2150  if (max_dt > 0. && max_dt < timestep) {
2151  timestep = max_dt;
2152  }
2153  break;
2154  }
2155  std::unique_ptr<UniformClock> clock_for_this_event;
2156  if (modus_.is_list() && (timestep < 0.0)) {
2157  throw std::runtime_error(
2158  "Timestep for the given event is negative. \n"
2159  "This might happen if the formation times of the input particles are "
2160  "larger than the specified end time of the simulation.");
2161  }
2162  clock_for_this_event =
2163  std::make_unique<UniformClock>(start_time, timestep, end_time_);
2164  parameters_.labclock = std::move(clock_for_this_event);
2165 
2166  // Reset the output clock
2167  parameters_.outputclock->reset(start_time, true);
2168  // remove time before starting time in case of custom output times.
2169  parameters_.outputclock->remove_times_in_past(start_time);
2170 
2171  logg[LExperiment].debug(
2172  "Lab clock: t_start = ", parameters_.labclock->current_time(),
2173  ", dt = ", parameters_.labclock->timestep_duration());
2174 
2175  /* Save the initial conserved quantum numbers and total momentum in
2176  * the system for conservation checks */
2177  conserved_initial_ = QuantumNumbers(ensembles_);
2178  wall_actions_total_ = 0;
2179  previous_wall_actions_total_ = 0;
2180  interactions_total_ = 0;
2181  previous_interactions_total_ = 0;
2182  discarded_interactions_total_ = 0;
2183  total_pauli_blocked_ = 0;
2184  projectile_target_interact_.assign(parameters_.n_ensembles, false);
2185  total_hypersurface_crossing_actions_ = 0;
2186  total_energy_removed_ = 0.0;
2187  total_energy_violated_by_Pythia_ = 0.0;
2188  // Print output headers
2189  logg[LExperiment].info() << hline;
2190  logg[LExperiment].info() << "Time[fm] Ekin[GeV] E_MF[GeV] ETotal[GeV] "
2191  << "ETot/N[GeV] D(ETot/N)[GeV] Scatt&Decays "
2192  << "Particles Comp.Time";
2193  logg[LExperiment].info() << hline;
2194  double E_mean_field = 0.0;
2195  if (potentials_) {
2196  // update_potentials();
2197  // if (parameters.outputclock->current_time() == 0.0 )
2198  // using the lattice is necessary
2199  if ((jmu_B_lat_ != nullptr)) {
2200  update_lattice(jmu_B_lat_.get(), old_jmu_auxiliary_.get(),
2201  new_jmu_auxiliary_.get(), four_gradient_auxiliary_.get(),
2202  LatticeUpdate::EveryTimestep, DensityType::Baryon,
2203  density_param_, ensembles_,
2204  parameters_.labclock->timestep_duration(), true);
2205  // Because there was no lattice at t=-Delta_t, the time derivatives
2206  // drho_dt and dj^mu/dt at t=0 are huge, while they shouldn't be; we
2207  // overwrite the time derivative to zero by hand.
2208  for (auto &node : *jmu_B_lat_) {
2209  node.overwrite_drho_dt_to_zero();
2210  node.overwrite_djmu_dt_to_zero();
2211  }
2212  E_mean_field = calculate_mean_field_energy(*potentials_, *jmu_B_lat_,
2213  EM_lat_.get(), parameters_);
2214  }
2215  }
2216  initial_mean_field_energy_ = E_mean_field;
2218  ensembles_, 0u, conserved_initial_, time_start_,
2219  parameters_.labclock->current_time(), E_mean_field,
2220  initial_mean_field_energy_);
2221 
2222  // Output at event start
2223  for (const auto &output : outputs_) {
2224  for (int i_ens = 0; i_ens < parameters_.n_ensembles; i_ens++) {
2225  auto event_info = fill_event_info(
2226  ensembles_, E_mean_field, modus_.impact_parameter(), parameters_,
2227  projectile_target_interact_[i_ens], kinematic_cuts_for_IC_output_);
2228  output->at_eventstart(ensembles_[i_ens], {event_, i_ens}, event_info);
2229  }
2230  // For thermodynamic output
2231  output->at_eventstart(ensembles_, event_);
2232  // For thermodynamic lattice output
2233  if (printout_full_lattice_any_td_) {
2234  if (printout_rho_eckart_) {
2235  switch (dens_type_lattice_printout_) {
2236  case DensityType::Baryon:
2237  output->at_eventstart(event_, ThermodynamicQuantity::EckartDensity,
2238  DensityType::Baryon, *jmu_B_lat_);
2239  break;
2241  output->at_eventstart(event_, ThermodynamicQuantity::EckartDensity,
2242  DensityType::BaryonicIsospin, *jmu_I3_lat_);
2243  break;
2244  case DensityType::None:
2245  break;
2246  default:
2247  output->at_eventstart(event_, ThermodynamicQuantity::EckartDensity,
2249  *jmu_custom_lat_);
2250  }
2251  }
2252  if (printout_tmn_) {
2253  output->at_eventstart(event_, ThermodynamicQuantity::Tmn,
2254  dens_type_lattice_printout_, *Tmn_);
2255  }
2256  if (printout_tmn_landau_) {
2257  output->at_eventstart(event_, ThermodynamicQuantity::TmnLandau,
2258  dens_type_lattice_printout_, *Tmn_);
2259  }
2260  if (printout_v_landau_) {
2261  output->at_eventstart(event_, ThermodynamicQuantity::LandauVelocity,
2262  dens_type_lattice_printout_, *Tmn_);
2263  }
2264  if (printout_j_QBS_) {
2265  output->at_eventstart(event_, ThermodynamicQuantity::j_QBS,
2266  dens_type_lattice_printout_, *j_QBS_lat_);
2267  }
2268  }
2269  }
2270 
2271  /* In the ColliderModus, if Fermi motion is frozen, assign the beam momenta
2272  * to the nucleons in both the projectile and the target. Every ensemble
2273  * gets the same beam momenta, so no need to create beam_momenta_ vector
2274  * for every ensemble.
2275  */
2276  if (modus_.is_collider() && modus_.fermi_motion() == FermiMotion::Frozen) {
2277  for (ParticleData &particle : ensembles_[0]) {
2278  const double m = particle.effective_mass();
2279  double v_beam = 0.0;
2280  if (particle.belongs_to() == BelongsTo::Projectile) {
2281  v_beam = modus_.velocity_projectile();
2282  } else if (particle.belongs_to() == BelongsTo::Target) {
2283  v_beam = modus_.velocity_target();
2284  }
2285  const double gamma = 1.0 / std::sqrt(1.0 - v_beam * v_beam);
2286  beam_momentum_.emplace_back(
2287  FourVector(gamma * m, 0.0, 0.0, gamma * v_beam * m));
2288  } // loop over particles
2289  }
2290 }
2291 
2292 template <typename Modus>
2293 bool Experiment<Modus>::perform_action(Action &action, int i_ensemble,
2294  bool include_pauli_blocking) {
2295  Particles &particles = ensembles_[i_ensemble];
2296  auto &incoming = action.incoming_particles();
2297  // Make sure to skip invalid and Pauli-blocked actions.
2298  if (!action.is_valid(particles)) {
2299  discarded_interactions_total_++;
2300  logg[LExperiment].debug(~einhard::DRed(), "✘ ", action,
2301  " (discarded: invalid)");
2302  return false;
2303  }
2304  const bool core_in_incoming =
2305  std::any_of(incoming.begin(), incoming.end(),
2306  [](const ParticleData &p) { return p.is_core(); });
2307  if (core_in_incoming) {
2308  if (action.get_type() == ProcessType::FluidizationNoRemoval) {
2309  // If the incoming particle is already core, the action should not happen.
2310  logg[LExperiment].debug() << "Discarding " << incoming[0].id();
2311  return false;
2312  } else if (action.get_type() != ProcessType::Elastic) {
2313  /* Only elastic collisions can happen between core and corona particles
2314  * (1→N can still happen) */
2315  const bool all_core_in_incoming =
2316  std::all_of(incoming.begin(), incoming.end(),
2317  [](const ParticleData &p) { return p.is_core(); });
2318  if (!all_core_in_incoming) {
2319  return false;
2320  }
2321  }
2322  }
2323  try {
2324  action.generate_final_state();
2325  } catch (Action::StochasticBelowEnergyThreshold &) {
2326  return false;
2327  }
2328  logg[LExperiment].debug("Process Type is: ", action.get_type());
2329  if (include_pauli_blocking && pauli_blocker_ &&
2330  action.is_pauli_blocked(ensembles_, *pauli_blocker_)) {
2331  total_pauli_blocked_++;
2332  return false;
2333  }
2334 
2335  // Prepare projectile_target_interact_, it's used for output
2336  // to signal that there was some interaction in this event
2337  if (modus_.is_collider()) {
2338  int count_target = 0, count_projectile = 0;
2339  for (const auto &p : incoming) {
2340  if (p.belongs_to() == BelongsTo::Projectile) {
2341  count_projectile++;
2342  } else if (p.belongs_to() == BelongsTo::Target) {
2343  count_target++;
2344  }
2345  }
2346  if (count_target > 0 && count_projectile > 0) {
2347  projectile_target_interact_[i_ensemble] = true;
2348  }
2349  }
2350 
2351  /* Make sure to pick a non-zero integer, because 0 is reserved for "no
2352  * interaction yet". */
2353  const auto id_process = static_cast<uint32_t>(interactions_total_ + 1);
2354  // we perform the action and collect possible energy violations by Pythia
2355  total_energy_violated_by_Pythia_ += action.perform(&particles, id_process);
2356 
2357  interactions_total_++;
2358  if (action.get_type() == ProcessType::Wall) {
2359  wall_actions_total_++;
2360  }
2361  if (action.get_type() == ProcessType::Fluidization) {
2362  total_hypersurface_crossing_actions_++;
2363  total_energy_removed_ += action.incoming_particles()[0].momentum().x0();
2364  }
2365  // Calculate Eckart rest frame density at the interaction point
2366  double rho = 0.0;
2367  if (dens_type_ != DensityType::None) {
2368  const FourVector r_interaction = action.get_interaction_point();
2369  constexpr bool compute_grad = false;
2370  const bool smearing = true;
2371  // todo(oliiny): it's a rough density estimate from a single ensemble.
2372  // It might actually be appropriate for output. Discuss.
2373  rho = std::get<0>(current_eckart(r_interaction.threevec(), particles,
2374  density_param_, dens_type_, compute_grad,
2375  smearing));
2376  }
2377  /*!\Userguide
2378  * \page doxypage_output_collisions_box_modus
2379  * \note When SMASH is running in the box modus, particle coordinates
2380  * in the collision output can be out of the box. This is not an error.
2381  * Box boundary conditions are intentionally not imposed before collision
2382  * output to allow unambiguous finding of the interaction point.
2383  * <I>Example</I>: two particles in the box have x coordinates 0.1 and
2384  * 9.9 fm, while box L = 10 fm. Suppose these particles collide.
2385  * For calculating collision the first one is wrapped to 10.1 fm.
2386  * Then output contains coordinates of 9.9 fm and 10.1 fm.
2387  * From this one can infer interaction point at x = 10 fm.
2388  * Were boundary conditions imposed before output,
2389  * their x coordinates would be 0.1 and 9.9 fm and interaction point
2390  * position could be either at 10 fm or at 5 fm.
2391  */
2392  for (const auto &output : outputs_) {
2393  if (output->is_dilepton_output() || output->is_photon_output()) {
2394  continue;
2395  }
2396  if (output->is_IC_output()) {
2397  if (action.get_type() == ProcessType::Fluidization ||
2398  action.get_type() == ProcessType::FluidizationNoRemoval) {
2399  output->at_interaction(action, rho);
2400  }
2401  } else {
2402  output->at_interaction(action, rho);
2403  }
2404  }
2405 
2406  // At every collision photons can be produced.
2407  // Note: We rely here on the lazy evaluation of the arguments to if.
2408  // It may happen that in a wall-crossing-action sqrt_s raises an exception.
2409  // Therefore we first have to check if the incoming particles can undergo
2410  // an em-interaction.
2411  if (photons_switch_ &&
2412  ScatterActionPhoton::is_photon_reaction(action.incoming_particles()) &&
2413  ScatterActionPhoton::is_kinematically_possible(
2414  action.sqrt_s(), action.incoming_particles())) {
2415  /* Time in the action constructor is relative to
2416  * current time of incoming */
2417  constexpr double action_time = 0.;
2418  ScatterActionPhoton photon_act(
2419  action.incoming_particles(), action_time, n_fractional_photons_,
2420  action.get_total_weight(), parameters_.spin_interaction_type);
2421 
2422  /**
2423  * Add a completely dummy process to the photon action. The only important
2424  * thing is that its cross-section is equal to the cross-section of the
2425  * hadronic action. This can be done, because the photon action is never
2426  * actually performed, only the final state is generated and printed to
2427  * the photon output.
2428  * Note: The cross_section_scaling_factor can be neglected here, since it
2429  * cancels out for the weighting, where a ratio of (unscaled) photon
2430  * cross section and (unscaled) hadronic cross section is taken.
2431  */
2432  photon_act.add_dummy_hadronic_process(action.get_total_weight());
2433 
2434  // Now add the actual photon reaction channel.
2435  photon_act.add_single_process();
2436 
2437  photon_act.perform_photons(outputs_);
2438  }
2439 
2440  if (photons_bremsstrahlung_switch_ &&
2441  BremsstrahlungActionPhoton::is_photon_brems_reaction(
2442  action.incoming_particles())) {
2443  /* Time in the action constructor is relative to
2444  * current time of incoming */
2445  constexpr double action_time = 0.;
2446 
2447  BremsstrahlungActionPhoton photon_brems_act(
2448  action.incoming_particles(), action_time, n_fractional_photons_,
2449  action.get_total_weight(), parameters_.spin_interaction_type);
2450 
2451  /**
2452  * Add a completely dummy process to the bremsstrahlung action. The only
2453  * important thing is that its cross-section is equal to the cross-section
2454  * of the hadronic action. This can be done, because the bremsstrahlung
2455  * action is never actually performed, only the final state is generated and
2456  * printed to the photon output. Note: The cross_section_scaling_factor can
2457  * be neglected here, since it cancels out for the weighting, where a ratio
2458  * of (unscaled) photon cross section and (unscaled) hadronic cross section
2459  * is taken.
2460  */
2461 
2462  photon_brems_act.add_dummy_hadronic_process(action.get_total_weight());
2463 
2464  // Now add the actual bremsstrahlung reaction channel.
2465  photon_brems_act.add_single_process();
2466 
2467  photon_brems_act.perform_bremsstrahlung(outputs_);
2468  }
2469 
2470  if (dileptons_bremsstrahlung_switch_ &&
2471  BremsstrahlungActionDilepton::is_dilepton_brems_reaction(
2472  action.incoming_particles())) {
2473  // Time in the action constructor is relative to current time of incoming
2474  constexpr double action_time = 0.;
2475 
2476  // Create the dilepton bremsstrahlung action with the respective form
2477  // factor.
2478  BremsstrahlungActionDilepton dilepton_brems_act(
2479  action.incoming_particles(), action_time, action.get_total_weight(),
2480  parameters_.dilepton_brems_pion_form_factor_type);
2481 
2482  // Add a dummy process to the dilepton bremsstrahlung action. The
2483  // only important thing is that its cross section is equal to the cross
2484  // section of the hadronic action. The dilepton bremsstrahlung action is
2485  // never performed, only the final state is generated and printed to the
2486  // dilepton output (similar to the photon output).
2487  //
2488  // The add_single_process() logic used in the photon bremsstrahlung
2489  // becomes obsolet since there are no sub-branches leading to the output at
2490  // the moment. Therefore, the very reduced logic from this function is
2491  // incorporated into add_dummy_hadronic_process.
2492  dilepton_brems_act.add_dummy_hadronic_process(action.get_total_weight());
2493 
2494  dilepton_brems_act.perform_dilepton_bremsstrahlung(outputs_);
2495  }
2496 
2497  logg[LExperiment].debug(~einhard::Green(), "✔ ", action);
2498  return true;
2499 }
2500 
2501 /**
2502  * Validate a particle list adjusting each particle to be a valid SMASH
2503  * particle. If the provided particle has an invalid PDG code, it is removed
2504  * from the list and the user warned. If the particles in the list are adjusted,
2505  * the function warns the user only the first time this function is called.
2506  * \see create_valid_smash_particle_matching_provided_quantities for more
2507  * information about which adjustements are made to the particles.
2508  *
2509  * \param[in] particle_list The particle list which should be adjusted
2510  */
2511 void validate_and_adjust_particle_list(ParticleList &particle_list);
2512 
2513 template <typename Modus>
2515  ParticleList &&add_plist,
2516  ParticleList &&remove_plist) {
2517  if (!add_plist.empty() || !remove_plist.empty()) {
2518  if (ensembles_.size() > 1) {
2519  throw std::runtime_error(
2520  "Adding or removing particles from SMASH is only possible when one "
2521  "ensemble is used.");
2522  }
2523  const double action_time = parameters_.labclock->current_time();
2524  /* Use two if statements. The first one is to check if the particles are
2525  * valid. Since this might remove all particles, a second if statement is
2526  * needed to avoid executing the action in that case.*/
2527  if (!add_plist.empty()) {
2529  }
2530  if (!add_plist.empty()) {
2531  // Create and perform action to add particle(s)
2532  auto action_add_particles = std::make_unique<FreeforallAction>(
2533  ParticleList{}, add_plist, action_time);
2534  perform_action(*action_add_particles, 0);
2535  }
2536  // Also here 2 if statements are needed as above.
2537  if (!remove_plist.empty()) {
2538  validate_and_adjust_particle_list(remove_plist);
2539  }
2540  if (!remove_plist.empty()) {
2541  ParticleList found_particles_to_remove;
2542  for (const auto &particle_to_remove : remove_plist) {
2543  const auto iterator_to_particle_to_be_removed_in_ensemble =
2544  std::find_if(
2545  ensembles_[0].begin(), ensembles_[0].end(),
2546  [&particle_to_remove, &action_time](const ParticleData &p) {
2548  particle_to_remove, p, action_time);
2549  });
2550  if (iterator_to_particle_to_be_removed_in_ensemble !=
2551  ensembles_[0].end())
2552  found_particles_to_remove.push_back(
2553  *iterator_to_particle_to_be_removed_in_ensemble);
2554  }
2555  // Sort the particles found to be removed according to their id and look
2556  // for duplicates (sorting is needed to call std::adjacent_find).
2557  std::sort(found_particles_to_remove.begin(),
2558  found_particles_to_remove.end(),
2559  [](const ParticleData &p1, const ParticleData &p2) {
2560  return p1.id() < p2.id();
2561  });
2562  const auto iterator_to_first_duplicate = std::adjacent_find(
2563  found_particles_to_remove.begin(), found_particles_to_remove.end(),
2564  [](const ParticleData &p1, const ParticleData &p2) {
2565  return p1.id() == p2.id();
2566  });
2567  if (iterator_to_first_duplicate != found_particles_to_remove.end()) {
2568  logg[LExperiment].error() << "The same particle has been asked to be "
2569  "removed multiple times:\n"
2570  << *iterator_to_first_duplicate;
2571  throw std::logic_error("Particle cannot be removed twice!");
2572  }
2573  if (auto delta = remove_plist.size() - found_particles_to_remove.size();
2574  delta > 0) {
2575  logg[LExperiment].warn(
2576  "When trying to remove particle(s) at the beginning ",
2577  "of the system evolution,\n", delta,
2578  " particle(s) could not be found and will be ignored.");
2579  }
2580  if (!found_particles_to_remove.empty()) {
2581  [[maybe_unused]] const auto number_particles_before_removal =
2582  ensembles_[0].size();
2583  // Create and perform action to remove particles
2584  auto action_remove_particles = std::make_unique<FreeforallAction>(
2585  found_particles_to_remove, ParticleList{}, action_time);
2586  perform_action(*action_remove_particles, 0);
2587 
2588  assert(number_particles_before_removal -
2589  found_particles_to_remove.size() ==
2590  ensembles_[0].size());
2591  }
2592  }
2593  }
2594 
2595  if (t_end > end_time_) {
2596  logg[LExperiment].fatal()
2597  << "Evolution asked to be run until " << t_end << " > " << end_time_
2598  << " and this cannot be done (because of how the clock works).";
2599  throw std::logic_error(
2600  "Experiment cannot evolve the system beyond End_Time.");
2601  }
2602  while (*(parameters_.labclock) < t_end) {
2603  const double dt = parameters_.labclock->timestep_duration();
2604  logg[LExperiment].debug("Timestepless propagation for next ", dt, " fm.");
2605 
2606  // Perform forced thermalization if required
2607  if (thermalizer_ &&
2608  thermalizer_->is_time_to_thermalize(parameters_.labclock)) {
2609  const bool ignore_cells_under_treshold = true;
2610  // Thermodynamics in thermalizer is computed from all ensembles,
2611  // but thermalization actions act on each ensemble independently
2612  thermalizer_->update_thermalizer_lattice(ensembles_, density_param_,
2613  ignore_cells_under_treshold);
2614  const double current_t = parameters_.labclock->current_time();
2615  for (int i_ens = 0; i_ens < parameters_.n_ensembles; i_ens++) {
2616  thermalizer_->thermalize(ensembles_[i_ens], current_t,
2617  parameters_.testparticles);
2618  ThermalizationAction th_act(*thermalizer_, current_t);
2619  if (th_act.any_particles_thermalized()) {
2620  perform_action(th_act, i_ens);
2621  }
2622  }
2623  }
2624 
2625  if (IC_dynamic_) {
2626  modus_.build_fluidization_lattice(parameters_.labclock->current_time(),
2627  ensembles_, density_param_);
2628  }
2629 
2630  std::vector<Actions> actions(parameters_.n_ensembles);
2631  for (int i_ens = 0; i_ens < parameters_.n_ensembles; i_ens++) {
2632  actions[i_ens].clear();
2633  if (ensembles_[i_ens].size() > 0 && action_finders_.size() > 0) {
2634  /* (1.a) Create grid. */
2635  const double min_cell_length = compute_min_cell_length(dt);
2636  logg[LExperiment].debug("Creating grid with minimal cell length ",
2637  min_cell_length);
2638  /* For the hyper-surface-crossing actions also unformed particles are
2639  * searched and therefore needed on the grid. */
2640  const bool include_unformed_particles = IC_switch_;
2641  const auto &grid =
2642  use_grid_ ? modus_.create_grid(ensembles_[i_ens], min_cell_length,
2643  dt, parameters_.coll_crit,
2644  include_unformed_particles)
2645  : modus_.create_grid(ensembles_[i_ens], min_cell_length,
2646  dt, parameters_.coll_crit,
2647  include_unformed_particles,
2648  CellSizeStrategy::Largest);
2649 
2650  const double gcell_vol = grid.cell_volume();
2651  /* (1.b) Iterate over cells and find actions. */
2652  grid.iterate_cells(
2653  [&](const ParticleList &search_list) {
2654  for (const auto &finder : action_finders_) {
2655  actions[i_ens].insert(finder->find_actions_in_cell(
2656  search_list, dt, gcell_vol, beam_momentum_));
2657  }
2658  },
2659  [&](const ParticleList &search_list,
2660  const ParticleList &neighbors_list) {
2661  for (const auto &finder : action_finders_) {
2662  actions[i_ens].insert(finder->find_actions_with_neighbors(
2663  search_list, neighbors_list, dt, beam_momentum_));
2664  }
2665  });
2666  }
2667  }
2668 
2669  /* \todo (optimizations) Adapt timestep size here */
2670 
2671  /* (2) Propagate from action to action until next output or timestep end */
2672  const double end_timestep_time = parameters_.labclock->next_time();
2673  while (next_output_time() < end_timestep_time) {
2674  for (int i_ens = 0; i_ens < parameters_.n_ensembles; i_ens++) {
2675  run_time_evolution_timestepless(actions[i_ens], i_ens,
2676  next_output_time());
2677  }
2678  ++(*parameters_.outputclock);
2679 
2680  intermediate_output();
2681  }
2682  for (int i_ens = 0; i_ens < parameters_.n_ensembles; i_ens++) {
2683  run_time_evolution_timestepless(actions[i_ens], i_ens, end_timestep_time);
2684  }
2685 
2686  /* (3) Update potentials (if computed on the lattice) and
2687  * compute new momenta according to equations of motion */
2688  if (potentials_) {
2689  update_potentials();
2690  update_momenta(ensembles_, parameters_.labclock->timestep_duration(),
2691  *potentials_, FB_lat_.get(), FI3_lat_.get(), EM_lat_.get(),
2692  jmu_B_lat_.get());
2693  }
2694 
2695  /* (4) Expand universe if non-minkowskian metric; updates
2696  * positions and momenta according to the selected expansion */
2697  if (metric_.mode_ != ExpansionMode::NoExpansion) {
2698  for (Particles &particles : ensembles_) {
2699  expand_space_time(&particles, parameters_, metric_);
2700  }
2701  }
2702 
2703  ++(*parameters_.labclock);
2704 
2705  /* (5) Check conservation laws.
2706  *
2707  * Check conservation of conserved quantities if potentials and string
2708  * fragmentation are off. If potentials are on then momentum is conserved
2709  * only in average. If string fragmentation is on, then energy and
2710  * momentum are only very roughly conserved in high-energy collisions. */
2711  if (!potentials_ && !parameters_.strings_switch &&
2712  metric_.mode_ == ExpansionMode::NoExpansion && !IC_switch_) {
2713  std::string err_msg = conserved_initial_.report_deviations(ensembles_);
2714  if (!err_msg.empty()) {
2715  logg[LExperiment].error() << err_msg;
2716  throw std::runtime_error("Violation of conserved quantities!");
2717  }
2718  }
2719  }
2720 
2721  /* Increment once more the output clock in order to have it prepared for the
2722  * final_output() call. Once close to the end time, the while-loop above to
2723  * produce intermediate output is not entered as the next_output_time() is
2724  * never strictly smaller than end_timestep_time (they are usually equal).
2725  * Since in the final_output() function the current time of the output clock
2726  * is used to produce the output, this has to be incremented before producing
2727  * the final output and it makes sense to do it here.
2728  */
2729  ++(*parameters_.outputclock);
2730 
2731  if (pauli_blocker_) {
2732  logg[LExperiment].info(
2733  "Interactions: Pauli-blocked/performed = ", total_pauli_blocked_, "/",
2734  interactions_total_ - wall_actions_total_);
2735  }
2736 }
2737 
2738 template <typename Modus>
2740  Particles &particles) {
2741  const double dt =
2742  propagate_straight_line(&particles, to_time, beam_momentum_);
2743  if (dilepton_finder_ != nullptr) {
2744  for (const auto &output : outputs_) {
2745  dilepton_finder_->shine(particles, output.get(), dt);
2746  }
2747  }
2748 }
2749 
2750 /**
2751  * Make sure `interactions_total` can be represented as a 32-bit integer.
2752  * This is necessary for converting to a `id_process`. The latter is 32-bit
2753  * integer, because it is written like this to binary output.
2754  *
2755  * \param[in] interactions_total Total interaction number
2756  */
2757 inline void check_interactions_total(uint64_t interactions_total) {
2758  constexpr uint64_t max_uint32 = std::numeric_limits<uint32_t>::max();
2759  if (interactions_total >= max_uint32) {
2760  throw std::runtime_error("Integer overflow in total interaction number!");
2761  }
2762 }
2763 
2764 template <typename Modus>
2766  Actions &actions, int i_ensemble, const double end_time_propagation) {
2767  Particles &particles = ensembles_[i_ensemble];
2768  logg[LExperiment].debug(
2769  "Timestepless propagation: ", "Actions size = ", actions.size(),
2770  ", end time = ", end_time_propagation);
2771 
2772  // iterate over all actions
2773  while (!actions.is_empty()) {
2774  if (actions.earliest_time() > end_time_propagation) {
2775  break;
2776  }
2777  // get next action
2778  ActionPtr act = actions.pop();
2779  if (!act->is_valid(particles)) {
2780  discarded_interactions_total_++;
2781  logg[LExperiment].debug(~einhard::DRed(), "✘ ", act,
2782  " (discarded: invalid)");
2783  continue;
2784  }
2785  logg[LExperiment].debug(~einhard::Green(), "✔ ", act,
2786  ", action time = ", act->time_of_execution());
2787 
2788  /* (1) Propagate to the next action. */
2789  propagate_and_shine(act->time_of_execution(), particles);
2790 
2791  /* (2) Perform action.
2792  *
2793  * Update the positions of the incoming particles, because the information
2794  * in the action object will be outdated as the particles have been
2795  * propagated since the construction of the action. */
2796  act->update_incoming(particles);
2797  const bool performed = perform_action(*act, i_ensemble);
2798 
2799  /* No need to update actions for outgoing particles
2800  * if the action is not performed. */
2801  if (!performed) {
2802  continue;
2803  }
2804 
2805  /* (3) Update actions for newly-produced particles. */
2806 
2807  const double end_time_timestep = parameters_.labclock->next_time();
2808  // New actions are always search until the end of the current timestep
2809  const double time_left = end_time_timestep - act->time_of_execution();
2810  const ParticleList &outgoing_particles = act->outgoing_particles();
2811  // Grid cell volume set to zero, since there is no grid
2812  const double gcell_vol = 0.0;
2813  for (const auto &finder : action_finders_) {
2814  // Outgoing particles can still decay, cross walls...
2815  actions.insert(finder->find_actions_in_cell(outgoing_particles, time_left,
2816  gcell_vol, beam_momentum_));
2817  // ... and collide with other particles.
2818  actions.insert(finder->find_actions_with_surrounding_particles(
2819  outgoing_particles, particles, time_left, beam_momentum_));
2820  }
2821 
2822  check_interactions_total(interactions_total_);
2823  }
2824 
2825  propagate_and_shine(end_time_propagation, particles);
2826 }
2827 
2828 template <typename Modus>
2830  const uint64_t wall_actions_this_interval =
2831  wall_actions_total_ - previous_wall_actions_total_;
2832  previous_wall_actions_total_ = wall_actions_total_;
2833  const uint64_t interactions_this_interval = interactions_total_ -
2834  previous_interactions_total_ -
2835  wall_actions_this_interval;
2836  previous_interactions_total_ = interactions_total_;
2837  double E_mean_field = 0.0;
2838  /// Auxiliary variable to communicate the time in the computational frame
2839  /// at the functions printing the thermodynamics lattice output
2840  double computational_frame_time = 0.0;
2841  if (potentials_) {
2842  // using the lattice is necessary
2843  if ((jmu_B_lat_ != nullptr)) {
2844  E_mean_field = calculate_mean_field_energy(*potentials_, *jmu_B_lat_,
2845  EM_lat_.get(), parameters_);
2846  /*
2847  * Mean field calculated in a box should remain approximately constant if
2848  * the system is in equilibrium, and so deviations from its original value
2849  * may signal a phase transition or other dynamical process. This
2850  * comparison only makes sense in the Box Modus, hence the condition.
2851  */
2852  if (modus_.is_box()) {
2853  double tmp = (E_mean_field - initial_mean_field_energy_) /
2854  (E_mean_field + initial_mean_field_energy_);
2855  /*
2856  * This is displayed when the system evolves away from its initial
2857  * configuration (which is when the total mean field energy in the box
2858  * deviates from its initial value).
2859  */
2860  if (std::abs(tmp) > 0.01) {
2861  logg[LExperiment].info()
2862  << "\n\n\n\t The mean field at t = "
2863  << parameters_.outputclock->current_time()
2864  << " [fm] differs from the mean field at t = 0:"
2865  << "\n\t\t initial_mean_field_energy_ = "
2866  << initial_mean_field_energy_ << " [GeV]"
2867  << "\n\t\t abs[(E_MF - E_MF(t=0))/(E_MF + E_MF(t=0))] = "
2868  << std::abs(tmp)
2869  << "\n\t\t E_MF/E_MF(t=0) = "
2870  << E_mean_field / initial_mean_field_energy_ << "\n\n";
2871  }
2872  }
2873  }
2874  }
2875 
2877  ensembles_, interactions_this_interval, conserved_initial_, time_start_,
2878  parameters_.outputclock->current_time(), E_mean_field,
2879  initial_mean_field_energy_);
2880  const LatticeUpdate lat_upd = LatticeUpdate::AtOutput;
2881 
2882  // save evolution data
2883  if (!(modus_.is_box() && parameters_.outputclock->current_time() <
2884  modus_.equilibration_time())) {
2885  for (const auto &output : outputs_) {
2886  if (output->is_dilepton_output() || output->is_photon_output() ||
2887  output->is_IC_output()) {
2888  continue;
2889  }
2890  for (int i_ens = 0; i_ens < parameters_.n_ensembles; i_ens++) {
2891  auto event_info = fill_event_info(
2892  ensembles_, E_mean_field, modus_.impact_parameter(), parameters_,
2893  projectile_target_interact_[i_ens], kinematic_cuts_for_IC_output_);
2894 
2895  output->at_intermediate_time(ensembles_[i_ens], parameters_.outputclock,
2896  density_param_, {event_, i_ens},
2897  event_info);
2898  computational_frame_time = event_info.current_time;
2899  }
2900  // For thermodynamic output
2901  output->at_intermediate_time(ensembles_, parameters_.outputclock,
2902  density_param_);
2903 
2904  // Thermodynamic output on the lattice versus time
2905  if (printout_rho_eckart_) {
2906  switch (dens_type_lattice_printout_) {
2907  case DensityType::Baryon:
2909  jmu_B_lat_.get(), lat_upd, DensityType::Baryon, density_param_,
2910  ensembles_, false);
2911  output->thermodynamics_output(ThermodynamicQuantity::EckartDensity,
2912  DensityType::Baryon, *jmu_B_lat_);
2913  output->thermodynamics_lattice_output(*jmu_B_lat_,
2914  computational_frame_time);
2915  break;
2918  jmu_I3_lat_.get(), lat_upd, DensityType::BaryonicIsospin,
2919  density_param_, ensembles_, false);
2920  output->thermodynamics_output(ThermodynamicQuantity::EckartDensity,
2922  *jmu_I3_lat_);
2923  output->thermodynamics_lattice_output(*jmu_I3_lat_,
2924  computational_frame_time);
2925  break;
2926  case DensityType::None:
2927  break;
2928  default:
2930  jmu_custom_lat_.get(), lat_upd, dens_type_lattice_printout_,
2931  density_param_, ensembles_, false);
2932  output->thermodynamics_output(ThermodynamicQuantity::EckartDensity,
2933  dens_type_lattice_printout_,
2934  *jmu_custom_lat_);
2935  output->thermodynamics_lattice_output(*jmu_custom_lat_,
2936  computational_frame_time);
2937  }
2938  }
2939  if (printout_tmn_ || printout_tmn_landau_ || printout_v_landau_) {
2941  Tmn_.get(), lat_upd, dens_type_lattice_printout_, density_param_,
2942  ensembles_, false);
2943  if (printout_tmn_) {
2944  output->thermodynamics_output(ThermodynamicQuantity::Tmn,
2945  dens_type_lattice_printout_, *Tmn_);
2946  output->thermodynamics_lattice_output(
2947  ThermodynamicQuantity::Tmn, *Tmn_, computational_frame_time);
2948  }
2949  if (printout_tmn_landau_) {
2950  output->thermodynamics_output(ThermodynamicQuantity::TmnLandau,
2951  dens_type_lattice_printout_, *Tmn_);
2952  output->thermodynamics_lattice_output(
2954  computational_frame_time);
2955  }
2956  if (printout_v_landau_) {
2957  output->thermodynamics_output(ThermodynamicQuantity::LandauVelocity,
2958  dens_type_lattice_printout_, *Tmn_);
2959  output->thermodynamics_lattice_output(
2961  computational_frame_time);
2962  }
2963  }
2964  if (EM_lat_) {
2965  output->fields_output("Efield", "Bfield", *EM_lat_);
2966  }
2967  if (printout_j_QBS_) {
2968  output->thermodynamics_lattice_output(
2969  *j_QBS_lat_, computational_frame_time, ensembles_, density_param_);
2970  }
2971 
2972  if (thermalizer_) {
2973  output->thermodynamics_output(*thermalizer_);
2974  }
2975  }
2976  }
2977 }
2978 
2979 template <typename Modus>
2981  if (potentials_) {
2982  if (potentials_->use_symmetry() && jmu_I3_lat_ != nullptr) {
2983  update_lattice(jmu_I3_lat_.get(), old_jmu_auxiliary_.get(),
2984  new_jmu_auxiliary_.get(), four_gradient_auxiliary_.get(),
2985  LatticeUpdate::EveryTimestep, DensityType::BaryonicIsospin,
2986  density_param_, ensembles_,
2987  parameters_.labclock->timestep_duration(), true);
2988  }
2989  if ((potentials_->use_skyrme() || potentials_->use_symmetry()) &&
2990  jmu_B_lat_ != nullptr) {
2991  update_lattice(jmu_B_lat_.get(), old_jmu_auxiliary_.get(),
2992  new_jmu_auxiliary_.get(), four_gradient_auxiliary_.get(),
2993  LatticeUpdate::EveryTimestep, DensityType::Baryon,
2994  density_param_, ensembles_,
2995  parameters_.labclock->timestep_duration(), true);
2996  const size_t UBlattice_size = UB_lat_->size();
2997  for (size_t i = 0; i < UBlattice_size; i++) {
2998  auto jB = (*jmu_B_lat_)[i];
2999  const FourVector flow_four_velocity_B =
3000  std::abs(jB.rho()) > very_small_double ? jB.jmu_net() / jB.rho()
3001  : FourVector();
3002  double baryon_density = jB.rho();
3003  ThreeVector baryon_grad_j0 = jB.grad_j0();
3004  ThreeVector baryon_dvecj_dt = jB.dvecj_dt();
3005  ThreeVector baryon_curl_vecj = jB.curl_vecj();
3006  if (potentials_->use_skyrme()) {
3007  (*UB_lat_)[i] =
3008  flow_four_velocity_B * potentials_->skyrme_pot(baryon_density);
3009  (*FB_lat_)[i] =
3010  potentials_->skyrme_force(baryon_density, baryon_grad_j0,
3011  baryon_dvecj_dt, baryon_curl_vecj);
3012  }
3013  if (potentials_->use_symmetry() && jmu_I3_lat_ != nullptr) {
3014  auto jI3 = (*jmu_I3_lat_)[i];
3015  const FourVector flow_four_velocity_I3 =
3016  std::abs(jI3.rho()) > very_small_double
3017  ? jI3.jmu_net() / jI3.rho()
3018  : FourVector();
3019  (*UI3_lat_)[i] = flow_four_velocity_I3 *
3020  potentials_->symmetry_pot(jI3.rho(), baryon_density);
3021  (*FI3_lat_)[i] = potentials_->symmetry_force(
3022  jI3.rho(), jI3.grad_j0(), jI3.dvecj_dt(), jI3.curl_vecj(),
3023  baryon_density, baryon_grad_j0, baryon_dvecj_dt,
3024  baryon_curl_vecj);
3025  }
3026  }
3027  }
3028  if (potentials_->use_coulomb()) {
3030  jmu_el_lat_.get(), LatticeUpdate::EveryTimestep, DensityType::Charge,
3031  density_param_, ensembles_, true);
3032  for (size_t i = 0; i < EM_lat_->size(); i++) {
3033  ThreeVector electric_field = {0., 0., 0.};
3034  ThreeVector position = jmu_el_lat_->cell_center(i);
3035  jmu_el_lat_->integrate_volume(electric_field,
3036  Potentials::E_field_integrand,
3037  potentials_->coulomb_r_cut(), position);
3038  ThreeVector magnetic_field = {0., 0., 0.};
3039  jmu_el_lat_->integrate_volume(magnetic_field,
3040  Potentials::B_field_integrand,
3041  potentials_->coulomb_r_cut(), position);
3042  (*EM_lat_)[i] = std::make_pair(electric_field, magnetic_field);
3043  }
3044  } // if ((potentials_->use_skyrme() || ...
3045  if (potentials_->use_vdf() && jmu_B_lat_ != nullptr) {
3046  update_lattice(jmu_B_lat_.get(), old_jmu_auxiliary_.get(),
3047  new_jmu_auxiliary_.get(), four_gradient_auxiliary_.get(),
3048  LatticeUpdate::EveryTimestep, DensityType::Baryon,
3049  density_param_, ensembles_,
3050  parameters_.labclock->timestep_duration(), true);
3051  if (parameters_.field_derivatives_mode == FieldDerivativesMode::Direct) {
3053  fields_lat_.get(), old_fields_auxiliary_.get(),
3054  new_fields_auxiliary_.get(), fields_four_gradient_auxiliary_.get(),
3055  jmu_B_lat_.get(), LatticeUpdate::EveryTimestep, *potentials_,
3056  parameters_.labclock->timestep_duration());
3057  }
3058  const size_t UBlattice_size = UB_lat_->size();
3059  for (size_t i = 0; i < UBlattice_size; i++) {
3060  auto jB = (*jmu_B_lat_)[i];
3061  (*UB_lat_)[i] = potentials_->vdf_pot(jB.rho(), jB.jmu_net());
3062  switch (parameters_.field_derivatives_mode) {
3064  (*FB_lat_)[i] = potentials_->vdf_force(
3065  jB.rho(), jB.drho_dxnu().x0(), jB.drho_dxnu().threevec(),
3066  jB.grad_rho_cross_vecj(), jB.jmu_net().x0(), jB.grad_j0(),
3067  jB.jmu_net().threevec(), jB.dvecj_dt(), jB.curl_vecj());
3068  break;
3070  auto Amu = (*fields_lat_)[i];
3071  (*FB_lat_)[i] = potentials_->vdf_force(
3072  Amu.grad_A0(), Amu.dvecA_dt(), Amu.curl_vecA());
3073  break;
3074  }
3075  } // for (size_t i = 0; i < UBlattice_size; i++)
3076  } // if potentials_->use_vdf()
3077  }
3078 }
3079 
3080 template <typename Modus>
3082  /* At end of time evolution: Force all resonances to decay. In order to handle
3083  * decay chains, we need to loop until no further actions occur. */
3084  bool actions_performed, actions_found;
3085  uint64_t interactions_old;
3086  do {
3087  actions_found = false;
3088  interactions_old = interactions_total_;
3089  for (int i_ens = 0; i_ens < parameters_.n_ensembles; i_ens++) {
3090  Actions actions;
3091  // Dileptons: shining of remaining resonances
3092  if (dilepton_finder_ != nullptr) {
3093  for (const auto &output : outputs_) {
3094  dilepton_finder_->shine_final(ensembles_[i_ens], output.get(), true);
3095  }
3096  }
3097  // Find actions.
3098  for (const auto &finder : action_finders_) {
3099  auto found_actions = finder->find_final_actions(ensembles_[i_ens]);
3100  if (!found_actions.empty()) {
3101  actions.insert(std::move(found_actions));
3102  actions_found = true;
3103  }
3104  }
3105  // Perform actions.
3106  while (!actions.is_empty()) {
3107  perform_action(*actions.pop(), i_ens, false);
3108  }
3109  }
3110  actions_performed = interactions_total_ > interactions_old;
3111  // Throw an error if actions were found but not performed
3112  if (actions_found && !actions_performed) {
3113  throw std::runtime_error("Final actions were found but not performed.");
3114  }
3115  // loop until no more decays occur
3116  } while (actions_performed);
3117 
3118  // Dileptons: shining of stable particles at the end
3119  if (dilepton_finder_ != nullptr) {
3120  for (const auto &output : outputs_) {
3121  for (Particles &particles : ensembles_) {
3122  dilepton_finder_->shine_final(particles, output.get(), false);
3123  }
3124  }
3125  }
3126 }
3127 
3128 template <typename Modus>
3130  /* make sure the experiment actually ran (note: we should compare this
3131  * to the start time, but we don't know that. Therefore, we check that
3132  * the time is positive, which should heuristically be the same). */
3133  double E_mean_field = 0.0;
3134  if (likely(parameters_.labclock > 0)) {
3135  const uint64_t wall_actions_this_interval =
3136  wall_actions_total_ - previous_wall_actions_total_;
3137  const uint64_t interactions_this_interval = interactions_total_ -
3138  previous_interactions_total_ -
3139  wall_actions_this_interval;
3140  if (potentials_) {
3141  // using the lattice is necessary
3142  if ((jmu_B_lat_ != nullptr)) {
3143  E_mean_field = calculate_mean_field_energy(*potentials_, *jmu_B_lat_,
3144  EM_lat_.get(), parameters_);
3145  }
3146  }
3147  if (std::abs(parameters_.labclock->current_time() - end_time_) >
3148  really_small) {
3149  logg[LExperiment].warn()
3150  << "SMASH not propagated until configured end time. Current time = "
3151  << parameters_.labclock->current_time()
3152  << "fm. End time = " << end_time_ << "fm.";
3153  } else {
3155  ensembles_, interactions_this_interval, conserved_initial_,
3156  time_start_, end_time_, E_mean_field, initial_mean_field_energy_);
3157  }
3158  int total_particles = 0;
3159  for (const Particles &particles : ensembles_) {
3160  total_particles += particles.size();
3161  }
3162  if (IC_switch_ && (total_particles == 0)) {
3163  const double initial_system_energy_plus_Pythia_violations =
3164  conserved_initial_.momentum().x0() + total_energy_violated_by_Pythia_;
3165  const double fraction_of_total_system_energy_removed =
3166  initial_system_energy_plus_Pythia_violations / total_energy_removed_;
3167  // Verify there is no more energy in the system if all particles were
3168  // removed when crossing the hypersurface
3169  if (std::fabs(fraction_of_total_system_energy_removed - 1.) >
3170  really_small) {
3171  throw std::runtime_error(
3172  "There is remaining energy in the system although all particles "
3173  "were removed.\n"
3174  "E_remain = " +
3175  std::to_string((initial_system_energy_plus_Pythia_violations -
3176  total_energy_removed_)) +
3177  " [GeV]");
3178  } else {
3179  logg[LExperiment].info() << hline;
3180  logg[LExperiment].info()
3181  << "Time real: " << SystemClock::now() - time_start_;
3182  logg[LExperiment].info()
3183  << "Interactions before reaching hypersurface: "
3184  << interactions_total_ - wall_actions_total_ -
3185  total_hypersurface_crossing_actions_;
3186  logg[LExperiment].info()
3187  << "Total number of particles removed on hypersurface: "
3188  << total_hypersurface_crossing_actions_;
3189  }
3190  } else {
3191  const double precent_discarded =
3192  interactions_total_ > 0
3193  ? static_cast<double>(discarded_interactions_total_) * 100.0 /
3194  interactions_total_
3195  : 0.0;
3196  std::stringstream msg_discarded;
3197  msg_discarded
3198  << "Discarded interaction number: " << discarded_interactions_total_
3199  << " (" << precent_discarded
3200  << "% of the total interaction number including wall crossings)";
3201 
3202  logg[LExperiment].info() << hline;
3203  logg[LExperiment].info()
3204  << "Time real: " << SystemClock::now() - time_start_;
3205  logg[LExperiment].debug() << msg_discarded.str();
3206 
3207  if (parameters_.coll_crit == CollisionCriterion::Stochastic &&
3208  precent_discarded > 1.0) {
3209  // The chosen threshold of 1% is a heuristical value
3210  logg[LExperiment].warn()
3211  << msg_discarded.str()
3212  << "\nThe number of discarded interactions is large, which means "
3213  "the assumption for the stochastic criterion of\n1 interaction "
3214  "per particle per timestep is probably violated. Consider "
3215  "reducing the timestep size.";
3216  }
3217 
3218  logg[LExperiment].info() << "Final interaction number: "
3219  << interactions_total_ - wall_actions_total_;
3220  }
3221 
3222  // Check if there are unformed particles
3223  int unformed_particles_count = 0;
3224  for (const Particles &particles : ensembles_) {
3225  for (const ParticleData &particle : particles) {
3226  if (particle.formation_time() > end_time_) {
3227  unformed_particles_count++;
3228  }
3229  }
3230  }
3231  if (unformed_particles_count > 0) {
3232  logg[LExperiment].warn(
3233  "End time might be too small. ", unformed_particles_count,
3234  " unformed particles were found at the end of the evolution.");
3235  }
3236  }
3237 
3238  // Keep track of how many ensembles had interactions
3239  count_nonempty_ensembles();
3240 
3241  for (const auto &output : outputs_) {
3242  for (int i_ens = 0; i_ens < parameters_.n_ensembles; i_ens++) {
3243  auto event_info = fill_event_info(
3244  ensembles_, E_mean_field, modus_.impact_parameter(), parameters_,
3245  projectile_target_interact_[i_ens], kinematic_cuts_for_IC_output_);
3246  output->at_eventend(ensembles_[i_ens], {event_, i_ens}, event_info);
3247  }
3248  // For thermodynamic output
3249  output->at_eventend(ensembles_, event_);
3250 
3251  // For thermodynamic lattice output
3252  if (printout_rho_eckart_) {
3253  if (dens_type_lattice_printout_ != DensityType::None) {
3255  }
3256  }
3257  if (printout_tmn_) {
3258  output->at_eventend(ThermodynamicQuantity::Tmn);
3259  }
3260  if (printout_tmn_landau_) {
3262  }
3263  if (printout_v_landau_) {
3265  }
3266  if (printout_j_QBS_) {
3267  output->at_eventend(ThermodynamicQuantity::j_QBS);
3268  }
3269  }
3270 }
3271 
3272 template <typename Modus>
3274  for (bool has_interaction : projectile_target_interact_) {
3275  if (has_interaction) {
3276  nonempty_ensembles_++;
3277  }
3278  }
3279 }
3280 
3281 template <typename Modus>
3283  if (event_counting_ == EventCounting::FixedNumber) {
3284  return event_ >= nevents_;
3285  }
3286  if (event_counting_ == EventCounting::MinimumNonEmpty) {
3287  if (nonempty_ensembles_ >= minimum_nonempty_ensembles_) {
3288  return true;
3289  }
3290  if (event_ >= max_events_) {
3291  logg[LExperiment].warn()
3292  << "Maximum number of events (" << max_events_
3293  << ") exceeded. Stopping calculation. "
3294  << "The fraction of empty ensembles is "
3295  << (1.0 - static_cast<double>(nonempty_ensembles_) /
3296  (event_ * parameters_.n_ensembles))
3297  << ". If this fraction is expected, try increasing the "
3298  "Maximum_Ensembles_Run.";
3299  return true;
3300  }
3301  return false;
3302  }
3303  throw std::runtime_error("Event counting option is invalid");
3304  return false;
3305 }
3306 
3307 template <typename Modus>
3309  event_++;
3310 }
3311 
3312 template <typename Modus>
3314  const auto &mainlog = logg[LMain];
3315  for (event_ = 0; !is_finished(); event_++) {
3316  mainlog.info() << "Event " << event_;
3317 
3318  // Sample initial particles, start clock, some printout and book-keeping
3319  initialize_new_event();
3320 
3321  run_time_evolution(end_time_);
3322 
3323  do_final_interactions();
3324 
3325  // Output at event end
3326  final_output();
3327  }
3328 }
3329 
3330 } // namespace smash
3331 
3332 #endif // SRC_INCLUDE_SMASH_EXPERIMENT_H_
Collection of useful type aliases to measure and output the (real) runtime.
A stream modifier that allows to colorize the log output.
Definition: einhard.hpp:147
Action is the base class for a generic process that takes a number of incoming particles and transfor...
Definition: action.h:35
virtual ProcessType get_type() const
Get the process type.
Definition: action.h:131
virtual double get_total_weight() const =0
Return the total weight value, which is mainly used for the weight output entry.
virtual double perform(Particles *particles, uint32_t id_process)
Actually perform the action, e.g.
Definition: action.cc:131
const ParticleList & incoming_particles() const
Get the list of particles that go into the action.
Definition: action.cc:61
virtual void generate_final_state()=0
Generate the final state for this action.
double sqrt_s() const
Determine the total energy in the center-of-mass frame [GeV].
Definition: action.h:271
FourVector get_interaction_point() const
Get the interaction point.
Definition: action.cc:71
bool is_valid(const Particles &particles) const
Check whether the action still applies.
Definition: action.cc:32
bool is_pauli_blocked(const std::vector< Particles > &ensembles, const PauliBlocker &p_bl) const
Check if the action is Pauli-blocked.
Definition: action.cc:38
The Actions class abstracts the storage and manipulation of actions.
Definition: actions.h:29
ActionPtr pop()
Return the first action in the list and removes it from the list.
Definition: actions.h:59
double earliest_time() const
Return time of execution of earliest action.
Definition: actions.h:70
ActionList::size_type size() const
Definition: actions.h:98
void insert(ActionList &&new_acts)
Insert a list of actions into this object.
Definition: actions.h:79
bool is_empty() const
Definition: actions.h:52
Interface to the SMASH configuration files.
void set_value(Key< U > key, T &&value)
Overwrite the value of the YAML node corresponding to the specified key.
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.
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...
bool has_section(const KeyLabels &labels) const
Return whether there is a (possibly empty) section with the given labels.
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.
A class to pre-calculate and store parameters relevant for density calculation.
Definition: density.h:92
Non-template interface to Experiment<Modus>.
Definition: experiment.h:104
static std::unique_ptr< ExperimentBase > create(Configuration &config, const std::filesystem::path &output_path)
Factory method that creates and initializes a new Experiment<Modus>.
Definition: experiment.cc:22
virtual ~ExperimentBase()=default
The virtual destructor avoids undefined behavior when destroying derived objects.
virtual void run()=0
Runs the experiment.
The main class, where the simulation of an experiment is executed.
Definition: experiment.h:193
void propagate_and_shine(double to_time, Particles &particles)
Propagate all particles until time to_time without any interactions and shine dileptons.
Definition: experiment.h:2739
Experiment(Configuration &config, const std::filesystem::path &output_path)
Create a new Experiment.
const ExpansionProperties metric_
This struct contains information on the metric to be used.
Definition: experiment.h:603
std::unique_ptr< ActionFinderInterface > photon_finder_
The (Scatter) Actions Finder for Direct Photons.
Definition: experiment.h:431
double initial_mean_field_energy_
The initial total mean field energy in the system.
Definition: experiment.h:651
void create_output(const std::string &format, const std::string &content, const std::filesystem::path &output_path, const OutputParameters &par)
Create a list of output files.
Definition: experiment.h:734
std::vector< std::unique_ptr< ActionFinderInterface > > action_finders_
The Action finder objects.
Definition: experiment.h:425
bool printout_tmn_
Whether to print the energy-momentum tensor.
Definition: experiment.h:513
QuantumNumbers conserved_initial_
The conserved quantities of the system.
Definition: experiment.h:645
DensityParameters density_param_
Structure to precalculate and hold parameters for density computations.
Definition: experiment.h:376
double next_output_time() const
Shortcut for next output time.
Definition: experiment.h:351
double total_energy_removed_
Total energy removed from the system in hypersurface crossing actions.
Definition: experiment.h:704
DensityType dens_type_lattice_printout_
Type of density for lattice printout.
Definition: experiment.h:461
bool printout_coulomb_vtk_
Whether to write the electric and magnetic fields to VTK files.
Definition: experiment.h:532
double max_transverse_distance_sqr_
Maximal distance at which particles can interact in case of the geometric criterion,...
Definition: experiment.h:636
const bool dileptons_bremsstrahlung_switch_
This indicates whether dilepton production via bremsstrahlung is switched on.
Definition: experiment.h:612
const bool IC_dynamic_
This indicates if the IC is dynamic.
Definition: experiment.h:627
bool printout_j_QBS_
Whether to print the Q, B, S 4-currents.
Definition: experiment.h:522
std::unique_ptr< GrandCanThermalizer > thermalizer_
Instance of class used for forced thermalization.
Definition: experiment.h:535
void count_nonempty_ensembles()
Counts the number of ensembles in wich interactions took place at the end of an event.
Definition: experiment.h:3273
const TimeStepMode time_step_mode_
This indicates whether to use time steps.
Definition: experiment.h:630
std::unique_ptr< DensityLattice > jmu_custom_lat_
Custom density on the lattices.
Definition: experiment.h:458
bool printout_full_lattice_any_td_
Whether to print the thermodynamics quantities evaluated on the lattices, point by point,...
Definition: experiment.h:529
void increase_event_number()
Increases the event number by one.
Definition: experiment.h:3308
void run_time_evolution_timestepless(Actions &actions, int i_ensemble, const double end_time_propagation)
Performs all the propagations and actions during a certain time interval neglecting the influence of ...
Definition: experiment.h:2765
Particles * first_ensemble()
Provides external access to SMASH particles.
Definition: experiment.h:262
void run_time_evolution(const double t_end, ParticleList &&add_plist={}, ParticleList &&remove_plist={})
Runs the time evolution of an event with fixed-size time steps or without timesteps,...
Definition: experiment.h:2514
int minimum_nonempty_ensembles_
The number of ensembles, in which interactions take place, to be calculated.
Definition: experiment.h:567
std::unique_ptr< DensityLattice > jmu_B_lat_
Baryon density on the lattice.
Definition: experiment.h:440
DensityType dens_type_
Type of density to be written to collision headers.
Definition: experiment.h:657
void intermediate_output()
Intermediate output during an event.
Definition: experiment.h:2829
std::vector< FourVector > beam_momentum_
The initial nucleons in the ColliderModus propagate with beam_momentum_, if Fermi motion is frozen.
Definition: experiment.h:422
std::unique_ptr< RectangularLattice< std::pair< ThreeVector, ThreeVector > > > FI3_lat_
Lattices for the electric and magnetic component of the symmetry force.
Definition: experiment.h:484
std::unique_ptr< RectangularLattice< FourVector > > new_jmu_auxiliary_
Auxiliary lattice for values of jmu at a time step t0 + dt.
Definition: experiment.h:496
double compute_min_cell_length(double dt) const
Calculate the minimal size for the grid cells such that the ScatterActionsFinder will find all collis...
Definition: experiment.h:343
void initialize_new_event()
This is called in the beginning of each event.
Definition: experiment.h:2096
const bool photons_bremsstrahlung_switch_
This indicates whether bremsstrahlung is switched on.
Definition: experiment.h:618
bool is_finished()
Checks wether the desired number events have been calculated.
Definition: experiment.h:3282
int n_fractional_photons_
Number of fractional photons produced per single reaction.
Definition: experiment.h:434
const double delta_time_startup_
The clock's timestep size at start up.
Definition: experiment.h:597
std::unique_ptr< RectangularLattice< EnergyMomentumTensor > > Tmn_
Lattices of energy-momentum tensors for printout.
Definition: experiment.h:491
std::vector< Particles > ensembles_
Complete particle list, all ensembles in one vector.
Definition: experiment.h:385
std::unique_ptr< RectangularLattice< FourVector > > new_fields_auxiliary_
Auxiliary lattice for values of Amu at a time step t0 + dt.
Definition: experiment.h:504
SystemTimePoint time_start_
system starting time of the simulation
Definition: experiment.h:654
uint64_t previous_wall_actions_total_
Total number of wall-crossings for previous timestep.
Definition: experiment.h:681
const bool IC_switch_
This indicates whether the experiment will be used as initial condition for hydrodynamics.
Definition: experiment.h:624
std::unique_ptr< RectangularLattice< std::pair< ThreeVector, ThreeVector > > > FB_lat_
Lattices for the electric and magnetic components of the Skyrme or VDF force.
Definition: experiment.h:480
OutputsList outputs_
A list of output formaters.
Definition: experiment.h:403
bool printout_rho_eckart_
Whether to print the Eckart rest frame density.
Definition: experiment.h:510
int event_
Current event.
Definition: experiment.h:578
std::unique_ptr< RectangularLattice< std::array< FourVector, 4 > > > fields_four_gradient_auxiliary_
Auxiliary lattice for calculating the four-gradient of Amu.
Definition: experiment.h:507
std::unique_ptr< PauliBlocker > pauli_blocker_
An instance of PauliBlocker class that stores parameters needed for Pauli blocking calculations and c...
Definition: experiment.h:397
bool printout_v_landau_
Whether to print the 4-velocity in Landau frame.
Definition: experiment.h:519
bool perform_action(Action &action, int i_ensemble, bool include_pauli_blocking=true)
Perform the given action.
std::unique_ptr< RectangularLattice< FourVector > > UI3_lat_
Lattices for symmetry potentials (evaluated in the local rest frame) times the isospin flow 4-velocit...
Definition: experiment.h:473
bool printout_lattice_td_
Whether to print the thermodynamics quantities evaluated on the lattices.
Definition: experiment.h:525
void do_final_interactions()
Performs the final decays of an event.
Definition: experiment.h:3081
std::unique_ptr< RectangularLattice< std::pair< ThreeVector, ThreeVector > > > EM_lat_
Lattices for electric and magnetic field in fm^-2.
Definition: experiment.h:488
std::unique_ptr< FieldsLattice > fields_lat_
Mean-field A^mu on the lattice.
Definition: experiment.h:449
int max_events_
Maximum number of events to be calculated in order obtain the desired number of non-empty events usin...
Definition: experiment.h:587
int nonempty_ensembles_
Number of ensembles containing an interaction.
Definition: experiment.h:581
OutputPtr photon_output_
The Photon output.
Definition: experiment.h:409
EventCounting event_counting_
The way in which the number of calculated events is specified.
Definition: experiment.h:575
const bool dileptons_switch_
This indicates whether dileptons are switched on.
Definition: experiment.h:606
Modus modus_
Instance of the Modus template parameter.
Definition: experiment.h:382
std::unique_ptr< RectangularLattice< FourVector > > old_jmu_auxiliary_
Auxiliary lattice for values of jmu at a time step t0.
Definition: experiment.h:494
std::unique_ptr< DecayActionsFinderDilepton > dilepton_finder_
The Dilepton Action Finder.
Definition: experiment.h:428
std::unique_ptr< DensityLattice > j_QBS_lat_
4-current for j_QBS lattice output
Definition: experiment.h:437
bool printout_tmn_landau_
Whether to print the energy-momentum tensor in Landau frame.
Definition: experiment.h:516
std::unique_ptr< RectangularLattice< std::array< FourVector, 4 > > > four_gradient_auxiliary_
Auxiliary lattice for calculating the four-gradient of jmu.
Definition: experiment.h:499
const bool photons_switch_
This indicates whether photons are switched on.
Definition: experiment.h:615
StringProcess * process_string_ptr_
Pointer to the string process class object, which is used to set the random seed for PYTHIA objects i...
Definition: experiment.h:541
std::unique_ptr< RectangularLattice< FourVector > > UB_lat_
Lattices for Skyrme or VDF potentials (evaluated in the local rest frame) times the baryon flow 4-vel...
Definition: experiment.h:467
ExperimentParameters parameters_
Struct of several member variables.
Definition: experiment.h:373
std::unique_ptr< RectangularLattice< FourVector > > old_fields_auxiliary_
Auxiliary lattice for values of Amu at a time step t0.
Definition: experiment.h:502
std::unique_ptr< DensityLattice > jmu_el_lat_
Electric charge density on the lattice.
Definition: experiment.h:446
std::unique_ptr< Potentials > potentials_
An instance of potentials class, that stores parameters of potentials, calculates them and their grad...
Definition: experiment.h:391
uint64_t wall_actions_total_
Total number of wall-crossings for current timestep.
Definition: experiment.h:675
uint64_t total_hypersurface_crossing_actions_
Total number of particles removed from the evolution in hypersurface crossing actions.
Definition: experiment.h:693
uint64_t interactions_total_
Total number of interactions for current timestep.
Definition: experiment.h:663
const bool use_grid_
This indicates whether to use the grid.
Definition: experiment.h:600
std::unique_ptr< DensityLattice > jmu_I3_lat_
Isospin projection density on the lattice.
Definition: experiment.h:443
int nevents_
Number of events.
Definition: experiment.h:557
uint64_t total_pauli_blocked_
Total number of Pauli-blockings for current timestep.
Definition: experiment.h:687
void final_output()
Output at the end of an event.
Definition: experiment.h:3129
std::vector< bool > projectile_target_interact_
Whether the projectile and the target collided.
Definition: experiment.h:415
Modus * modus()
Provides external access to SMASH calculation modus.
Definition: experiment.h:270
void update_potentials()
Recompute potentials on lattices if necessary.
Definition: experiment.h:2980
OutputPtr dilepton_output_
The Dilepton output.
Definition: experiment.h:406
int64_t seed_
random seed for the next event.
Definition: experiment.h:715
std::vector< Particles > * all_ensembles()
Getter for all ensembles.
Definition: experiment.h:264
uint64_t previous_interactions_total_
Total number of interactions for previous timestep.
Definition: experiment.h:669
uint64_t discarded_interactions_total_
Total number of discarded interactions, because they were invalidated before they could be performed.
Definition: experiment.h:699
void run() override
Runs the experiment.
Definition: experiment.h:3313
bool kinematic_cuts_for_IC_output_
This indicates whether kinematic cuts are enabled for the IC output.
Definition: experiment.h:712
const double end_time_
simulation time at which the evolution is stopped.
Definition: experiment.h:590
double total_energy_violated_by_Pythia_
Total energy violation introduced by Pythia.
Definition: experiment.h:709
The FourVector class holds relevant values in Minkowski spacetime with (+, −, −, −) metric signature.
Definition: fourvector.h:33
ParticleData contains the dynamic information of a certain particle.
Definition: particledata.h:59
static double formation_power_
Power with which the cross section scaling factor grows in time.
Definition: particledata.h:471
The Particles class abstracts the storage and manipulation of particles.
Definition: particles.h:33
size_t size() const
Definition: particles.h:87
A class that stores parameters of potentials, calculates potentials and their gradients.
Definition: potentials.h:36
A container for storing conserved values.
A container class to hold all the arrays on the lattice and access them.
Definition: lattice.h:49
String excitation processes used in SMASH.
Definition: stringprocess.h:46
ThermalizationAction implements forced thermalization as an Action class.
bool any_particles_thermalized() const
This method checks, if there are particles in the region to be thermalized.
The ThreeVector class represents a physical three-vector with the components .
Definition: threevector.h:31
@ Frozen
Use fermi motion without potentials.
@ Dynamic
Dynamic fluidization based on local densities.
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.
@ BottomUp
Sum the existing partial contributions.
@ Stochastic
Stochastic Criteiron.
@ None
No pseudo-resonance is created.
EventCounting
Defines how the number of events is determined.
@ Invalid
Unused, only in the code for internal logic.
@ FixedNumber
The desired number of events is simulated disregarding of whether an interaction took place.
@ MinimumNonEmpty
Events are simulated until there are at least a given number of ensembles in which an interaction too...
DensityType
Allows to choose which kind of density to calculate.
#define SMASH_SOURCE_LOCATION
Hackery that is required to output the location in the source code where the log statement occurs.
Definition: logging.h:153
std::ostream & operator<<(std::ostream &out, const ActionPtr &action)
Convenience: dereferences the ActionPtr to Action.
Definition: action.h:546
std::array< einhard::Logger<>, std::tuple_size< LogArea::AreaTuple >::value > & logg
An array that stores all pre-configured Logger objects.
Definition: logging.h:245
FormattingHelper< T > format(const T &value, const char *unit, int width=-1, int precision=-1)
Acts as a stream modifier for std::ostream to output an object with an optional suffix string and wit...
Definition: logging.h:217
std::unique_ptr< OutputInterface > create_oscar_output(const std::string &format, const std::string &content, const std::filesystem::path &path, const OutputParameters &out_par)
Definition: oscaroutput.cc:926
std::unique_ptr< OutputInterface > create_binary_output(const std::string &format, const std::string &content, const std::filesystem::path &path, const OutputParameters &out_par)
Create a binary output object.
#define likely(x)
Tell the branch predictor that this expression is likely true.
Definition: macros.h:14
constexpr Section c_pauliBlocking
Subsection for the Pauli blocking mechanism.
Definition: input_keys.h:123
constexpr Section output
Section for the output information.
Definition: input_keys.h:205
constexpr Section o_initialConditions
Subsection for the output initial conditions content.
Definition: input_keys.h:213
constexpr Section g_minEnsembles
Subsection for the minimum-nonempty-ensembles mechanism.
Definition: input_keys.h:145
constexpr Section forcedThermalization
Section for the forced thermalization.
Definition: input_keys.h:140
constexpr Section potentials
Section for the potentials information.
Definition: input_keys.h:228
constexpr Section lattice
Section for the lattice.
Definition: input_keys.h:149
constexpr Section modi
Section for the modus specific information.
Definition: input_keys.h:155
constexpr int p
Proton.
constexpr int n
Neutron.
Engine::result_type advance()
Advance the engine's state and return the generated value.
Definition: random.h:81
void set_seed(T &&seed)
Sets the seed of the random number engine.
Definition: random.h:74
Definition: action.h:24
static constexpr int LInitialConditions
Definition: experiment.h:95
void update_momenta(std::vector< Particles > &particles, double dt, const Potentials &pot, RectangularLattice< std::pair< ThreeVector, ThreeVector >> *FB_lat, RectangularLattice< std::pair< ThreeVector, ThreeVector >> *FI3_lat, RectangularLattice< std::pair< ThreeVector, ThreeVector >> *EM_lat, DensityLattice *jB_lat)
Updates the momenta of all particles at the current time step according to the equations of motion:
Definition: propagation.cc:131
EventInfo fill_event_info(const std::vector< Particles > &ensembles, double E_mean_field, double modus_impact_parameter, const ExperimentParameters &parameters, bool projectile_target_interact, bool kinematic_cut_for_SMASH_IC)
Generate the EventInfo object which is passed to outputs_.
Definition: experiment.cc:586
std::string format_measurements(const std::vector< Particles > &ensembles, uint64_t scatterings_this_interval, const QuantumNumbers &conserved_initial, SystemTimePoint time_start, double time, double E_mean_field, double E_mean_field_initial)
Generate a string which will be printed to the screen when SMASH is running.
Definition: experiment.cc:318
void expand_space_time(Particles *particles, const ExperimentParameters &parameters, const ExpansionProperties &metric)
Modifies positions and momentum of all particles to account for space-time deformation.
Definition: propagation.cc:106
void check_interactions_total(uint64_t interactions_total)
Make sure interactions_total can be represented as a 32-bit integer.
Definition: experiment.h:2757
std::tuple< double, FourVector, ThreeVector, ThreeVector, FourVector, FourVector, FourVector, FourVector > current_eckart(const ThreeVector &r, const ParticleList &plist, const DensityParameters &par, DensityType dens_type, bool compute_gradient, bool smearing)
Calculates Eckart rest frame density and 4-current of a given density type and optionally the gradien...
Definition: density.cc:176
double propagate_straight_line(Particles *particles, double to_time, const std::vector< FourVector > &beam_momentum)
Propagates the positions of all particles on a straight line to a given moment.
Definition: propagation.cc:44
ExperimentParameters create_experiment_parameters(Configuration &config)
Gathers all general Experiment parameters.
Definition: experiment.cc:135
constexpr double very_small_double
A very small double, used to avoid division by zero.
Definition: constants.h:44
double calculate_mean_field_energy(const Potentials &potentials, RectangularLattice< smash::DensityOnLattice > &jmu_B_lat, RectangularLattice< std::pair< ThreeVector, ThreeVector >> *em_lattice, const ExperimentParameters &parameters)
Calculate the total mean field energy of the system; this will be printed to the screen when SMASH is...
Definition: experiment.cc:366
static constexpr int LExperiment
void update_fields_lattice(RectangularLattice< FieldsOnLattice > *fields_lat, RectangularLattice< FourVector > *old_fields, RectangularLattice< FourVector > *new_fields, RectangularLattice< std::array< FourVector, 4 >> *fields_four_grad_lattice, DensityLattice *jmu_B_lat, const LatticeUpdate fields_lat_update, const Potentials &potentials, const double time_step)
Updates the contents on the lattice of FieldsOnLattice type.
Definition: fields.cc:14
bool are_particles_identical_at_given_time(const ParticleData &p1, const ParticleData &p2, double time)
Utility function to compare two ParticleData instances with respect to their PDG code,...
void update_lattice_accumulating_ensembles(RectangularLattice< T > *lat, const LatticeUpdate update, const DensityType dens_type, const DensityParameters &par, const std::vector< Particles > &ensembles, const bool compute_gradient)
Updates the contents on the lattice when ensembles are used.
Definition: density.h:654
void validate_and_adjust_particle_list(ParticleList &particle_list)
Validate a particle list adjusting each particle to be a valid SMASH particle.
Definition: experiment.cc:608
bool all_of(Container &&c, UnaryPredicate &&p)
Convenience wrapper for std::all_of that operates on a complete container.
Definition: algorithms.h:80
std::string to_string(ThermodynamicQuantity quantity)
Convert a ThermodynamicQuantity enum value to its corresponding string.
Definition: stringify.cc:26
constexpr double nucleon_mass
Nucleon mass in GeV.
Definition: constants.h:69
void update_lattice(RectangularLattice< DensityOnLattice > *lat, RectangularLattice< FourVector > *old_jmu, RectangularLattice< FourVector > *new_jmu, RectangularLattice< std::array< FourVector, 4 >> *four_grad_lattice, const LatticeUpdate update, const DensityType dens_type, const DensityParameters &par, const std::vector< Particles > &ensembles, const double time_step, const bool compute_gradient)
Updates the contents on the lattice of DensityOnLattice type.
Definition: density.cc:191
LatticeUpdate
Enumerator option for lattice updates.
Definition: lattice.h:38
std::ostream & operator<<(std::ostream &out, const Experiment< Modus > &e)
Creates a verbose textual description of the setup of the Experiment.
Definition: experiment.h:727
Potentials * pot_pointer
Pointer to a Potential class.
static constexpr int LMain
Definition: experiment.h:94
constexpr double really_small
Numerical error tolerance.
Definition: constants.h:41
std::string join(const std::vector< std::string > &v, std::string_view delim)
Join strings using delimiter.
RectangularLattice< FourVector > * UB_lat_pointer
Pointer to the skyrme potential on the lattice.
std::chrono::time_point< std::chrono::system_clock > SystemTimePoint
Type (alias) that is used to store the current time.
Definition: chrono.h:22
const std::string hline(113, '-')
String representing a horizontal line.
RectangularLattice< FourVector > * UI3_lat_pointer
Pointer to the symmmetry potential on the lattice.
constexpr double fm2_mb
mb <-> fm^2 conversion factor.
Definition: constants.h:32
Structure to contain custom data for output.
Struct containing the type of the metric and the expansion parameter of the metric.
Definition: propagation.h:26
Exception class that is thrown if an invalid modus is requested from the Experiment factory.
Definition: experiment.h:149
Exception class that is thrown if the requested output path in the Experiment factory is not existing...
Definition: experiment.h:158
Helper structure for Experiment.
double fixed_min_cell_length
Fixed minimal grid cell length (in fm).
const CollisionCriterion coll_crit
Employed collision criterion.
std::unique_ptr< Clock > outputclock
Output clock to keep track of the next output time.
static const Key< double > collTerm_stringParam_powerParticleFormation
See user guide description for more information.
Definition: input_keys.h:3604
static const Key< bool > collTerm_photons_twoToTwoScatterings
See user guide description for more information.
Definition: input_keys.h:3967
static const Key< DensityType > output_densityType
See user guide description for more information.
Definition: input_keys.h:6146
static const Key< bool > modi_collider_collisionWithinNucleus
See user guide description for more information.
Definition: input_keys.h:4127
static const Key< bool > collTerm_noCollisions
See user guide description for more information.
Definition: input_keys.h:2918
static const Key< int > collTerm_photons_fractionalPhotons
See user guide description for more information.
Definition: input_keys.h:4000
static const Key< int > gen_minNonEmptyEnsembles_number
See user guide description for more information.
Definition: input_keys.h:1427
static const Key< int > gen_minNonEmptyEnsembles_maximumEnsembles
See user guide description for more information.
Definition: input_keys.h:1413
static const Key< double > gen_expansionRate
See user guide description for more information.
Definition: input_keys.h:1563
static const Key< bool > collTerm_dileptons_bremsstrahlung
See user guide description for more information.
Definition: input_keys.h:3925
static const Key< bool > collTerm_dileptons_decays
See user guide description for more information.
Definition: input_keys.h:3907
static const Key< double > gen_endTime
See user guide description for more information.
Definition: input_keys.h:1333
static const Key< bool > collTerm_photons_bremsstrahlung
See user guide description for more information.
Definition: input_keys.h:3983
static const Key< PseudoResonance > collTerm_pseudoresonance
See user guide description for more information.
Definition: input_keys.h:2973
static const Key< int > gen_nevents
See user guide description for more information.
Definition: input_keys.h:1383
static const Key< ExpansionMode > gen_metricType
See user guide description for more information.
Definition: input_keys.h:1656
static const Key< TotalCrossSectionStrategy > collTerm_totXsStrategy
See user guide description for more information.
Definition: input_keys.h:3103
static const Key< bool > gen_useGrid
See user guide description for more information.
Definition: input_keys.h:1831
static const Key< TimeStepMode > gen_timeStepMode
See user guide description for more information.
Definition: input_keys.h:1798
Helper structure for Experiment to hold output options and parameters.
RivetOutputParameters rivet_parameters
Rivet specfic parameters.