Version: SMASH-2.1
experiment.h
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2013-2021
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 <string>
14 #include <utility>
15 #include <vector>
16 
17 #include "actionfinderfactory.h"
18 #include "actions.h"
19 #include "bremsstrahlungaction.h"
20 #include "chrono.h"
21 #include "decayactionsfinder.h"
23 #include "energymomentumtensor.h"
24 #include "fields.h"
25 #include "fourvector.h"
26 #include "grandcan_thermalizer.h"
27 #include "grid.h"
29 #include "outputparameters.h"
30 #include "pauliblocking.h"
31 #include "potential_globals.h"
32 #include "potentials.h"
33 #include "propagation.h"
34 #include "quantumnumbers.h"
35 #include "scatteractionphoton.h"
36 #include "scatteractionsfinder.h"
37 #include "stringprocess.h"
38 #include "thermalizationaction.h"
39 // Output
40 #include "binaryoutput.h"
41 #ifdef SMASH_USE_HEPMC
42 #include "hepmcoutput.h"
43 #endif
44 #ifdef SMASH_USE_RIVET
45 #include "rivetoutput.h"
46 #endif
47 #include "icoutput.h"
48 #include "oscaroutput.h"
50 #include "thermodynamicoutput.h"
51 #ifdef SMASH_USE_ROOT
52 #include "rootoutput.h"
53 #endif
54 #include "vtkoutput.h"
55 #include "wallcrossingaction.h"
56 
57 namespace std {
68 template <typename T, typename Ratio>
69 static ostream &operator<<(ostream &out,
70  const chrono::duration<T, Ratio> &seconds) {
71  using Seconds = chrono::duration<double>;
72  using Minutes = chrono::duration<double, std::ratio<60>>;
73  using Hours = chrono::duration<double, std::ratio<60 * 60>>;
74  constexpr Minutes threshold_for_minutes{10};
75  constexpr Hours threshold_for_hours{3};
76  if (seconds < threshold_for_minutes) {
77  return out << Seconds(seconds).count() << " [s]";
78  }
79  if (seconds < threshold_for_hours) {
80  return out << Minutes(seconds).count() << " [min]";
81  }
82  return out << Hours(seconds).count() << " [h]";
83 }
84 } // namespace std
85 
86 namespace smash {
87 static constexpr int LMain = LogArea::Main::id;
88 static constexpr int LInitialConditions = LogArea::InitialConditions::id;
89 
98  public:
99  ExperimentBase() = default;
104  virtual ~ExperimentBase() = default;
105 
126  static std::unique_ptr<ExperimentBase> create(Configuration config,
127  const bf::path &output_path);
128 
135  virtual void run() = 0;
136 
142  struct InvalidModusRequest : public std::invalid_argument {
143  using std::invalid_argument::invalid_argument;
144  };
145 };
146 
147 template <typename Modus>
148 class Experiment;
149 template <typename Modus>
150 std::ostream &operator<<(std::ostream &out, const Experiment<Modus> &e);
151 
176 template <typename Modus>
177 class Experiment : public ExperimentBase {
178  friend class ExperimentBase;
179 
180  public:
187  void run() override;
188 
202  explicit Experiment(Configuration config, const bf::path &output_path);
203 
209  void initialize_new_event();
210 
217  void run_time_evolution();
218 
220  void do_final_decays();
221 
223  void final_output();
224 
229  // todo(oliiny): this should be made compatible with JetScape on
230  // the JetScape side
233  std::vector<Particles> *all_ensembles() { return &ensembles_; }
234 
239  Modus *modus() { return &modus_; }
240 
241  private:
251  bool perform_action(Action &action, int i_ensemble);
260  void create_output(const std::string &format, const std::string &content,
261  const bf::path &output_path, const OutputParameters &par);
262 
270  void propagate_and_shine(double to_time, Particles &particles);
271 
284  void run_time_evolution_timestepless(Actions &actions, int i_ensemble,
285  double end_time_propagation);
286 
288  void intermediate_output();
289 
291  void update_potentials();
292 
301  double compute_min_cell_length(double dt) const {
304  }
305  return std::sqrt(4 * dt * dt + max_transverse_distance_sqr_);
306  }
307 
309  double next_output_time() const {
310  return parameters_.outputclock->next_time();
311  }
312 
319 
322 
327  Modus modus_;
328 
330  std::vector<Particles> ensembles_;
331 
336  std::unique_ptr<Potentials> potentials_;
337 
342  std::unique_ptr<PauliBlocker> pauli_blocker_;
343 
348  OutputsList outputs_;
349 
351  OutputPtr dilepton_output_;
352 
354  OutputPtr photon_output_;
355 
360  std::vector<bool> projectile_target_interact_;
361 
367  std::vector<FourVector> beam_momentum_ = {};
368 
370  std::vector<std::unique_ptr<ActionFinderInterface>> action_finders_;
371 
373  std::unique_ptr<DecayActionsFinderDilepton> dilepton_finder_;
374 
376  std::unique_ptr<ActionFinderInterface> photon_finder_;
377 
380 
382  std::unique_ptr<DensityLattice> j_QBS_lat_;
383 
385  std::unique_ptr<DensityLattice> jmu_B_lat_;
386 
388  std::unique_ptr<DensityLattice> jmu_I3_lat_;
389 
391  std::unique_ptr<DensityLattice> jmu_el_lat_;
392 
394  std::unique_ptr<FieldsLattice> fields_lat_;
395 
403  std::unique_ptr<DensityLattice> jmu_custom_lat_;
404 
407 
412  std::unique_ptr<RectangularLattice<FourVector>> UB_lat_ = nullptr;
413 
418  std::unique_ptr<RectangularLattice<FourVector>> UI3_lat_ = nullptr;
419 
424  std::unique_ptr<RectangularLattice<std::pair<ThreeVector, ThreeVector>>>
426 
428  std::unique_ptr<RectangularLattice<std::pair<ThreeVector, ThreeVector>>>
430 
432  std::unique_ptr<RectangularLattice<std::pair<ThreeVector, ThreeVector>>>
434 
436  std::unique_ptr<RectangularLattice<EnergyMomentumTensor>> Tmn_;
437 
439  std::unique_ptr<RectangularLattice<FourVector>> old_jmu_auxiliary_;
441  std::unique_ptr<RectangularLattice<FourVector>> new_jmu_auxiliary_;
443  std::unique_ptr<RectangularLattice<std::array<FourVector, 4>>>
445 
447  std::unique_ptr<RectangularLattice<FourVector>> old_fields_auxiliary_;
449  std::unique_ptr<RectangularLattice<FourVector>> new_fields_auxiliary_;
451  std::unique_ptr<RectangularLattice<std::array<FourVector, 4>>>
453 
455  bool printout_tmn_ = false;
456 
458  bool printout_tmn_landau_ = false;
459 
461  bool printout_v_landau_ = false;
462 
464  bool printout_j_QBS_ = false;
465 
467  bool printout_lattice_td_ = false;
468 
472 
476 
480 
482  std::unique_ptr<GrandCanThermalizer> thermalizer_;
483 
489 
504  const int nevents_;
505 
507  int event_ = 0;
508 
510  const double end_time_;
511 
517  const double delta_time_startup_;
518 
523  const bool force_decays_;
524 
526  const bool use_grid_;
527 
530 
532  const bool dileptons_switch_;
533 
535  const bool photons_switch_;
536 
539 
541  const bool IC_output_switch_;
542 
545 
550  double max_transverse_distance_sqr_ = std::numeric_limits<double>::max();
551 
560 
566 
568  SystemTimePoint time_start_ = SystemClock::now();
569 
572 
577  uint64_t interactions_total_ = 0;
578 
584 
589  uint64_t wall_actions_total_ = 0;
590 
596 
601  uint64_t total_pauli_blocked_ = 0;
602 
608 
614 
619  double total_energy_removed_ = 0.0;
620 
622  int64_t seed_ = -1;
623 
629  friend std::ostream &operator<<<>(std::ostream &out, const Experiment &e);
630 };
631 
633 template <typename Modus>
634 std::ostream &operator<<(std::ostream &out, const Experiment<Modus> &e) {
635  out << "End time: " << e.end_time_ << " fm/c\n";
636  out << e.modus_;
637  return out;
638 }
639 
640 template <typename Modus>
641 void Experiment<Modus>::create_output(const std::string &format,
642  const std::string &content,
643  const bf::path &output_path,
644  const OutputParameters &out_par) {
645  logg[LExperiment].info() << "Adding output " << content << " of format "
646  << format << std::endl;
647 
648  if (format == "VTK" && content == "Particles") {
649  outputs_.emplace_back(
650  make_unique<VtkOutput>(output_path, content, out_par));
651  } else if (format == "Root") {
652 #ifdef SMASH_USE_ROOT
653  if (content == "Initial_Conditions") {
654  outputs_.emplace_back(
655  make_unique<RootOutput>(output_path, "SMASH_IC", out_par));
656  } else {
657  outputs_.emplace_back(
658  make_unique<RootOutput>(output_path, content, out_par));
659  }
660 #else
661  logg[LExperiment].error(
662  "Root output requested, but Root support not compiled in");
663 #endif
664  } else if (format == "Binary") {
665  if (content == "Collisions" || content == "Dileptons" ||
666  content == "Photons") {
667  outputs_.emplace_back(
668  make_unique<BinaryOutputCollisions>(output_path, content, out_par));
669  } else if (content == "Particles") {
670  outputs_.emplace_back(
671  make_unique<BinaryOutputParticles>(output_path, content, out_par));
672  } else if (content == "Initial_Conditions") {
673  outputs_.emplace_back(make_unique<BinaryOutputInitialConditions>(
674  output_path, content, out_par));
675  }
676  } else if (format == "Oscar1999" || format == "Oscar2013") {
677  outputs_.emplace_back(
678  create_oscar_output(format, content, output_path, out_par));
679  } else if (content == "Thermodynamics" && format == "ASCII") {
680  outputs_.emplace_back(
681  make_unique<ThermodynamicOutput>(output_path, content, out_par));
682  } else if (content == "Thermodynamics" &&
683  (format == "Lattice_ASCII" || format == "Lattice_Binary")) {
684  printout_full_lattice_any_td_ = true;
685  if (format == "Lattice_ASCII") {
686  printout_full_lattice_ascii_td_ = true;
687  }
688  if (format == "Lattice_Binary") {
689  printout_full_lattice_binary_td_ = true;
690  }
691  outputs_.emplace_back(make_unique<ThermodynamicLatticeOutput>(
692  output_path, content, out_par, printout_full_lattice_ascii_td_,
693  printout_full_lattice_binary_td_));
694  } else if (content == "Thermodynamics" && format == "VTK") {
695  printout_lattice_td_ = true;
696  outputs_.emplace_back(
697  make_unique<VtkOutput>(output_path, content, out_par));
698  } else if (content == "Initial_Conditions" && format == "ASCII") {
699  outputs_.emplace_back(
700  make_unique<ICOutput>(output_path, "SMASH_IC", out_par));
701  } else if (format == "HepMC") {
702 #ifdef SMASH_USE_HEPMC
703  if (content == "Particles") {
704  outputs_.emplace_back(make_unique<HepMcOutput>(
705  output_path, "SMASH_HepMC_particles", false));
706  } else if (content == "Collisions") {
707  outputs_.emplace_back(make_unique<HepMcOutput>(
708  output_path, "SMASH_HepMC_collisions", true));
709  } else {
710  logg[LExperiment].error(
711  "HepMC only available for Particles and "
712  "Collisions content. Requested for " +
713  content + ".");
714  }
715 #else
716  logg[LExperiment].error(
717  "HepMC output requested, but HepMC support not compiled in");
718 #endif
719  } else if (content == "Coulomb" && format == "VTK") {
720  outputs_.emplace_back(
721  make_unique<VtkOutput>(output_path, "Fields", out_par));
722  } else if (content == "Rivet") {
723 #ifdef SMASH_USE_RIVET
724  // flag to ensure that the Rivet format has not been already assigned
725  static bool rivet_format_already_selected = false;
726  // if the next check is true, then we are trying to assign the format twice
727  if (rivet_format_already_selected) {
728  logg[LExperiment].warn(
729  "Rivet output format can only be one, either YODA or YODA-full. "
730  "Only your first valid choice will be used.");
731  return;
732  }
733  if (format == "YODA") {
734  outputs_.emplace_back(
735  make_unique<RivetOutput>(output_path, "SMASH_Rivet", false, out_par));
736  rivet_format_already_selected = true;
737  } else if (format == "YODA-full") {
738  outputs_.emplace_back(make_unique<RivetOutput>(
739  output_path, "SMASH_Rivet_full", true, out_par));
740  rivet_format_already_selected = true;
741  } else {
742  logg[LExperiment].error("Rivet format " + format +
743  "not one of YODA or YODA-full");
744  }
745 #else
746  logg[LExperiment].error(
747  "Rivet output requested, but Rivet support not compiled in");
748 #endif
749  } else {
750  logg[LExperiment].error()
751  << "Unknown combination of format (" << format << ") and content ("
752  << content << "). Fix the config.";
753  }
754 }
755 
764 
932 template <typename Modus>
933 Experiment<Modus>::Experiment(Configuration config, const bf::path &output_path)
934  : parameters_(create_experiment_parameters(config)),
935  density_param_(DensityParameters(parameters_)),
936  modus_(config["Modi"], parameters_),
937  ensembles_(parameters_.n_ensembles),
938  nevents_(config.take({"General", "Nevents"})),
939  end_time_(config.take({"General", "End_Time"})),
940  delta_time_startup_(parameters_.labclock->timestep_duration()),
941  force_decays_(
942  config.take({"Collision_Term", "Force_Decays_At_End"}, true)),
943  use_grid_(config.take({"General", "Use_Grid"}, true)),
944  metric_(
945  config.take({"General", "Metric_Type"}, ExpansionMode::NoExpansion),
946  config.take({"General", "Expansion_Rate"}, 0.1)),
947  dileptons_switch_(
948  config.take({"Collision_Term", "Dileptons", "Decays"}, false)),
949  photons_switch_(config.take(
950  {"Collision_Term", "Photons", "2to2_Scatterings"}, false)),
951  bremsstrahlung_switch_(
952  config.take({"Collision_Term", "Photons", "Bremsstrahlung"}, false)),
953  IC_output_switch_(config.has_value({"Output", "Initial_Conditions"})),
954  time_step_mode_(
955  config.take({"General", "Time_Step_Mode"}, TimeStepMode::Fixed)) {
956  logg[LExperiment].info() << *this;
957 
958  // covariant derivatives can only be done with covariant smearing
959  if (parameters_.derivatives_mode == DerivativesMode::CovariantGaussian &&
960  parameters_.smearing_mode != SmearingMode::CovariantGaussian) {
961  throw std::invalid_argument(
962  "Covariant Gaussian derivatives only make sense for Covariant Gaussian "
963  "smearing!");
964  }
965 
966  // for triangular smearing:
967  // the weight needs to be larger than 1./7. for the center cell to contribute
968  // more than the surrounding cells
969  if (parameters_.smearing_mode == SmearingMode::Discrete &&
970  parameters_.discrete_weight < (1. / 7.)) {
971  throw std::invalid_argument(
972  "The central weight for discrete smearing should be >= 1./7.");
973  }
974 
975  if (parameters_.coll_crit == CollisionCriterion::Stochastic &&
976  (time_step_mode_ != TimeStepMode::Fixed || !use_grid_)) {
977  throw std::invalid_argument(
978  "The stochastic criterion can only be employed for fixed time step "
979  "mode and with a grid!");
980  }
981 
982  logg[LExperiment].info("Using ", parameters_.testparticles,
983  " testparticles per particle.");
984  logg[LExperiment].info("Using ", parameters_.n_ensembles,
985  " parallel ensembles.");
986 
987  // create finders
988  if (dileptons_switch_) {
989  dilepton_finder_ = make_unique<DecayActionsFinderDilepton>();
990  }
991  if (photons_switch_ || bremsstrahlung_switch_) {
992  n_fractional_photons_ =
993  config.take({"Collision_Term", "Photons", "Fractional_Photons"}, 100);
994  }
995  if (parameters_.two_to_one) {
996  if (parameters_.res_lifetime_factor < 0.) {
997  throw std::invalid_argument(
998  "Resonance lifetime modifier cannot be negative!");
999  }
1000  if (parameters_.res_lifetime_factor < really_small) {
1001  logg[LExperiment].warn(
1002  "Resonance lifetime set to zero. Make sure resonances cannot "
1003  "interact",
1004  "inelastically (e.g. resonance chains), else SMASH is known to "
1005  "hang.");
1006  }
1007  action_finders_.emplace_back(
1008  make_unique<DecayActionsFinder>(parameters_.res_lifetime_factor));
1009  }
1010  bool no_coll = config.take({"Collision_Term", "No_Collisions"}, false);
1011  if ((parameters_.two_to_one || parameters_.included_2to2.any() ||
1012  parameters_.included_multi.any() || parameters_.strings_switch) &&
1013  !no_coll) {
1014  auto scat_finder = make_unique<ScatterActionsFinder>(config, parameters_);
1015  max_transverse_distance_sqr_ =
1016  scat_finder->max_transverse_distance_sqr(parameters_.testparticles);
1017  process_string_ptr_ = scat_finder->get_process_string_ptr();
1018  action_finders_.emplace_back(std::move(scat_finder));
1019  } else {
1020  max_transverse_distance_sqr_ =
1021  parameters_.maximum_cross_section / M_PI * fm2_mb;
1022  process_string_ptr_ = NULL;
1023  }
1024  if (modus_.is_box()) {
1025  action_finders_.emplace_back(
1026  make_unique<WallCrossActionsFinder>(parameters_.box_length));
1027  }
1028  if (IC_output_switch_) {
1029  if (!modus_.is_collider()) {
1030  throw std::runtime_error(
1031  "Initial conditions can only be extracted in collider modus.");
1032  }
1033  double proper_time;
1034  if (config.has_value({"Output", "Initial_Conditions", "Proper_Time"})) {
1035  // Read in proper time from config
1036  proper_time =
1037  config.take({"Output", "Initial_Conditions", "Proper_Time"});
1038  } else {
1039  // Default proper time is the passing time of the two nuclei
1040  double default_proper_time = modus_.nuclei_passing_time();
1041  double lower_bound =
1042  config.take({"Output", "Initial_Conditions", "Lower_Bound"}, 0.5);
1043  if (default_proper_time >= lower_bound) {
1044  proper_time = default_proper_time;
1045  } else {
1046  logg[LInitialConditions].warn()
1047  << "Nuclei passing time is too short, hypersurface proper time set "
1048  "to tau = "
1049  << lower_bound << " fm.";
1050  proper_time = lower_bound;
1051  }
1052  }
1053  action_finders_.emplace_back(
1054  make_unique<HyperSurfaceCrossActionsFinder>(proper_time));
1055  }
1056 
1057  if (config.has_value({"Collision_Term", "Pauli_Blocking"})) {
1058  logg[LExperiment].info() << "Pauli blocking is ON.";
1059  pauli_blocker_ = make_unique<PauliBlocker>(
1060  config["Collision_Term"]["Pauli_Blocking"], parameters_);
1061  }
1062  // In collider setup with sqrts >= 200 GeV particles don't form continuously
1064  config.take({"Collision_Term", "Power_Particle_Formation"},
1065  modus_.sqrt_s_NN() >= 200. ? -1. : 1.);
1066 
1099  // create outputs
1101  " create OutputInterface objects");
1102 
1103  auto output_conf = config["Output"];
1331  dens_type_ = config.take({"Output", "Density_Type"}, DensityType::None);
1332  logg[LExperiment].debug()
1333  << "Density type printed to headers: " << dens_type_;
1334 
1335  const OutputParameters output_parameters(std::move(output_conf));
1336 
1337  std::vector<std::string> output_contents = output_conf.list_upmost_nodes();
1338  for (const auto &content : output_contents) {
1339  auto this_output_conf = output_conf[content.c_str()];
1340  const std::vector<std::string> formats = this_output_conf.take({"Format"});
1341  if (output_path == "") {
1342  continue;
1343  }
1344  for (const auto &format : formats) {
1345  create_output(format, content, output_path, output_parameters);
1346  }
1347  }
1348 
1349  /* We can take away the Fermi motion flag, because the collider modus is
1350  * already initialized. We only need it when potentials are enabled, but we
1351  * always have to take it, otherwise SMASH will complain about unused
1352  * options. We have to provide a default value for modi other than Collider.
1353  */
1354  const FermiMotion motion =
1355  config.take({"Modi", "Collider", "Fermi_Motion"}, FermiMotion::Off);
1356  if (config.has_value({"Potentials"})) {
1357  if (time_step_mode_ == TimeStepMode::None) {
1358  logg[LExperiment].error() << "Potentials only work with time steps!";
1359  throw std::invalid_argument("Can't use potentials without time steps!");
1360  }
1361  if (motion == FermiMotion::Frozen) {
1362  logg[LExperiment].error()
1363  << "Potentials don't work with frozen Fermi momenta! "
1364  "Use normal Fermi motion instead.";
1365  throw std::invalid_argument(
1366  "Can't use potentials "
1367  "with frozen Fermi momenta!");
1368  }
1369  logg[LExperiment].info() << "Potentials are ON. Timestep is "
1370  << parameters_.labclock->timestep_duration();
1371  // potentials need density calculation parameters from parameters_
1372  potentials_ = make_unique<Potentials>(config["Potentials"], parameters_);
1373  // make sure that vdf potentials are not used together with Skyrme
1374  // or symmetry potentials
1375  if (potentials_->use_skyrme() && potentials_->use_vdf()) {
1376  throw std::runtime_error(
1377  "Can't use Skyrme and VDF potentials at the same time!");
1378  }
1379  if (potentials_->use_symmetry() && potentials_->use_vdf()) {
1380  throw std::runtime_error(
1381  "Can't use symmetry and VDF potentials at the same time!");
1382  }
1383  if (potentials_->use_skyrme()) {
1384  logg[LExperiment].info() << "Skyrme potentials are:\n";
1385  logg[LExperiment].info()
1386  << "\t\tSkyrme_A [MeV] = " << potentials_->skyrme_a() << "\n";
1387  logg[LExperiment].info()
1388  << "\t\tSkyrme_B [MeV] = " << potentials_->skyrme_b() << "\n";
1389  logg[LExperiment].info()
1390  << "\t\t Skyrme_tau = " << potentials_->skyrme_tau() << "\n";
1391  }
1392  if (potentials_->use_symmetry()) {
1393  logg[LExperiment].info()
1394  << "Symmetry potential is:"
1395  << "\n S_pot [MeV] = " << potentials_->symmetry_S_pot() << "\n";
1396  }
1397  if (potentials_->use_vdf()) {
1398  logg[LExperiment].info() << "VDF potential parameters are:\n";
1399  logg[LExperiment].info() << "\t\tsaturation density [fm^-3] = "
1400  << potentials_->saturation_density() << "\n";
1401  for (int i = 0; i < potentials_->number_of_terms(); i++) {
1402  logg[LExperiment].info()
1403  << "\t\tCoefficient_" << i + 1 << " = "
1404  << 1000.0 * (potentials_->coeffs())[i] << " [MeV] \t Power_"
1405  << i + 1 << " = " << (potentials_->powers())[i] << "\n";
1406  }
1407  }
1408  // if potentials are on, derivatives need to be calculated
1409  if (parameters_.derivatives_mode == DerivativesMode::Off &&
1410  parameters_.field_derivatives_mode == FieldDerivativesMode::ChainRule) {
1411  throw std::invalid_argument(
1412  "Derivatives are necessary for running with potentials.\n"
1413  "Derivatives_Mode: \"Off\" only makes sense for "
1414  "Field_Derivatives_Mode: \"Direct\"!\nUse \"Covariant Gaussian\" or "
1415  "\"Finite difference\".");
1416  }
1417  // for computational efficiency, we want to turn off the derivatives of jmu
1418  // and the rest frame density derivatives if direct derivatives are used
1419  if (parameters_.field_derivatives_mode == FieldDerivativesMode::Direct) {
1420  parameters_.derivatives_mode = DerivativesMode::Off;
1421  parameters_.rho_derivatives_mode = RestFrameDensityDerivativesMode::Off;
1422  }
1423  switch (parameters_.derivatives_mode) {
1425  logg[LExperiment].info() << "Covariant Gaussian derivatives are ON";
1426  break;
1428  logg[LExperiment].info() << "Finite difference derivatives are ON";
1429  break;
1430  case DerivativesMode::Off:
1431  logg[LExperiment].info() << "Gradients of baryon current are OFF";
1432  break;
1433  }
1434  switch (parameters_.rho_derivatives_mode) {
1436  logg[LExperiment].info() << "Rest frame density derivatives are ON";
1437  break;
1439  logg[LExperiment].info() << "Rest frame density derivatives are OFF";
1440  break;
1441  }
1442  // direct or chain rule derivatives only make sense for the VDF potentials
1443  if (potentials_->use_vdf()) {
1444  switch (parameters_.field_derivatives_mode) {
1446  logg[LExperiment].info() << "Chain rule field derivatives are ON";
1447  break;
1449  logg[LExperiment].info() << "Direct field derivatives are ON";
1450  break;
1451  }
1452  }
1453  /*
1454  * Necessary safety checks
1455  */
1456  // VDF potentials need derivatives of rest frame density or fields
1457  if (potentials_->use_vdf() && (parameters_.rho_derivatives_mode ==
1459  parameters_.field_derivatives_mode ==
1461  throw std::runtime_error(
1462  "Can't use VDF potentials without rest frame density derivatives or "
1463  "direct field derivatives!");
1464  }
1465  // potentials require using gradients
1466  if (parameters_.derivatives_mode == DerivativesMode::Off &&
1467  parameters_.field_derivatives_mode == FieldDerivativesMode::ChainRule) {
1468  throw std::runtime_error(
1469  "Can't use potentials without gradients of baryon current (Skyrme, "
1470  "VDF)"
1471  " or direct field derivatives (VDF)!");
1472  }
1473  // direct field derivatives only make sense for the VDF potentials
1474  if (!(potentials_->use_vdf()) &&
1475  parameters_.field_derivatives_mode == FieldDerivativesMode::Direct) {
1476  throw std::invalid_argument(
1477  "Field_Derivatives_Mode: \"Direct\" only makes sense for the VDF "
1478  "potentials!\nUse Field_Derivatives_Mode: \"Chain Rule\" or comment "
1479  "this option out (Chain Rule is default)");
1480  }
1481  }
1482 
1483  // information about the type of smearing
1484  switch (parameters_.smearing_mode) {
1486  logg[LExperiment].info() << "Smearing type: Covariant Gaussian";
1487  break;
1489  logg[LExperiment].info() << "Smearing type: Discrete with weight = "
1490  << parameters_.discrete_weight;
1491  break;
1493  logg[LExperiment].info() << "Smearing type: Triangular with range = "
1494  << parameters_.triangular_range;
1495  break;
1496  }
1497 
1577  // Create lattices
1578  if (config.has_value({"Lattice"})) {
1579  std::array<double, 3> l_default{20., 20., 20.};
1580  std::array<int, 3> n_default{10, 10, 10};
1581  std::array<double, 3> origin_default{-20., -20., -20.};
1582  bool periodic_default = false;
1583  if (modus_.is_collider()) {
1584  // Estimates on how far particles could get in x, y, z
1585  const double gam = modus_.sqrt_s_NN() / (2.0 * nucleon_mass);
1586  const double max_z = 5.0 / gam + end_time_;
1587  const double estimated_max_transverse_velocity = 0.7;
1588  const double max_xy = 5.0 + estimated_max_transverse_velocity * end_time_;
1589  origin_default = {-max_xy, -max_xy, -max_z};
1590  l_default = {2 * max_xy, 2 * max_xy, 2 * max_z};
1591  // Go for approximately 0.8 fm cell size and contract
1592  // lattice in z by gamma factor
1593  const int n_xy = std::ceil(2 * max_xy / 0.8);
1594  int nz = std::ceil(2 * max_z / 0.8);
1595  // Contract lattice by gamma factor in case of smearing where
1596  // smearing length is bound to the lattice cell length
1597  if (parameters_.smearing_mode == SmearingMode::Discrete ||
1598  parameters_.smearing_mode == SmearingMode::Triangular) {
1599  nz = static_cast<int>(std::ceil(2 * max_z / 0.8 * gam));
1600  }
1601  n_default = {n_xy, n_xy, nz};
1602  } else if (modus_.is_box()) {
1603  periodic_default = true;
1604  origin_default = {0., 0., 0.};
1605  const double bl = modus_.length();
1606  l_default = {bl, bl, bl};
1607  const int n_xyz = std::ceil(bl / 0.5);
1608  n_default = {n_xyz, n_xyz, n_xyz};
1609  } else if (modus_.is_sphere()) {
1610  // Maximal distance from (0, 0, 0) at which a particle
1611  // may be found at the end of the simulation
1612  const double max_d = modus_.radius() + end_time_;
1613  origin_default = {-max_d, -max_d, -max_d};
1614  l_default = {2 * max_d, 2 * max_d, 2 * max_d};
1615  // Go for approximately 0.8 fm cell size
1616  const int n_xyz = std::ceil(2 * max_d / 0.8);
1617  n_default = {n_xyz, n_xyz, n_xyz};
1618  }
1619  // Take lattice properties from config to assign them to all lattices
1620  const std::array<double, 3> l =
1621  config.take({"Lattice", "Sizes"}, l_default);
1622  const std::array<int, 3> n =
1623  config.take({"Lattice", "Cell_Number"}, n_default);
1624  const std::array<double, 3> origin =
1625  config.take({"Lattice", "Origin"}, origin_default);
1626  const bool periodic =
1627  config.take({"Lattice", "Periodic"}, periodic_default);
1628 
1629  logg[LExperiment].info()
1630  << "Lattice is ON. Origin = (" << origin[0] << "," << origin[1] << ","
1631  << origin[2] << "), sizes = (" << l[0] << "," << l[1] << "," << l[2]
1632  << "), number of cells = (" << n[0] << "," << n[1] << "," << n[2]
1633  << ")";
1634 
1635  if (printout_lattice_td_ || printout_full_lattice_any_td_) {
1636  dens_type_lattice_printout_ = output_parameters.td_dens_type;
1637  printout_tmn_ = output_parameters.td_tmn;
1638  printout_tmn_landau_ = output_parameters.td_tmn_landau;
1639  printout_v_landau_ = output_parameters.td_v_landau;
1640  printout_j_QBS_ = output_parameters.td_jQBS;
1641  }
1642  if (printout_tmn_ || printout_tmn_landau_ || printout_v_landau_) {
1643  Tmn_ = make_unique<RectangularLattice<EnergyMomentumTensor>>(
1644  l, n, origin, periodic, LatticeUpdate::AtOutput);
1645  }
1646  if (printout_j_QBS_) {
1647  j_QBS_lat_ = make_unique<DensityLattice>(l, n, origin, periodic,
1649  }
1650  /* Create baryon and isospin density lattices regardless of config
1651  if potentials are on. This is because they allow to compute
1652  potentials faster */
1653  if (potentials_) {
1654  // Create auxiliary lattices for baryon four-current calculation
1655  old_jmu_auxiliary_ = make_unique<RectangularLattice<FourVector>>(
1656  l, n, origin, periodic, LatticeUpdate::EveryTimestep);
1657  new_jmu_auxiliary_ = make_unique<RectangularLattice<FourVector>>(
1658  l, n, origin, periodic, LatticeUpdate::EveryTimestep);
1659  four_gradient_auxiliary_ =
1660  make_unique<RectangularLattice<std::array<FourVector, 4>>>(
1661  l, n, origin, periodic, LatticeUpdate::EveryTimestep);
1662 
1663  if (potentials_->use_skyrme()) {
1664  jmu_B_lat_ = make_unique<DensityLattice>(l, n, origin, periodic,
1666  UB_lat_ = make_unique<RectangularLattice<FourVector>>(
1667  l, n, origin, periodic, LatticeUpdate::EveryTimestep);
1668  FB_lat_ = make_unique<
1669  RectangularLattice<std::pair<ThreeVector, ThreeVector>>>(
1670  l, n, origin, periodic, LatticeUpdate::EveryTimestep);
1671  }
1672  if (potentials_->use_symmetry()) {
1673  jmu_I3_lat_ = make_unique<DensityLattice>(l, n, origin, periodic,
1675  UI3_lat_ = make_unique<RectangularLattice<FourVector>>(
1676  l, n, origin, periodic, LatticeUpdate::EveryTimestep);
1677  FI3_lat_ = make_unique<
1678  RectangularLattice<std::pair<ThreeVector, ThreeVector>>>(
1679  l, n, origin, periodic, LatticeUpdate::EveryTimestep);
1680  }
1681  if (potentials_->use_coulomb()) {
1682  jmu_el_lat_ = make_unique<DensityLattice>(l, n, origin, periodic,
1684  EM_lat_ = make_unique<
1685  RectangularLattice<std::pair<ThreeVector, ThreeVector>>>(
1686  l, n, origin, periodic, LatticeUpdate::EveryTimestep);
1687  }
1688  if (potentials_->use_vdf()) {
1689  jmu_B_lat_ = make_unique<DensityLattice>(l, n, origin, periodic,
1691  UB_lat_ = make_unique<RectangularLattice<FourVector>>(
1692  l, n, origin, periodic, LatticeUpdate::EveryTimestep);
1693  FB_lat_ = make_unique<
1694  RectangularLattice<std::pair<ThreeVector, ThreeVector>>>(
1695  l, n, origin, periodic, LatticeUpdate::EveryTimestep);
1696  }
1697  if (parameters_.field_derivatives_mode == FieldDerivativesMode::Direct) {
1698  // Create auxiliary lattices for field calculation
1699  old_fields_auxiliary_ = make_unique<RectangularLattice<FourVector>>(
1700  l, n, origin, periodic, LatticeUpdate::EveryTimestep);
1701  new_fields_auxiliary_ = make_unique<RectangularLattice<FourVector>>(
1702  l, n, origin, periodic, LatticeUpdate::EveryTimestep);
1703  fields_four_gradient_auxiliary_ =
1704  make_unique<RectangularLattice<std::array<FourVector, 4>>>(
1705  l, n, origin, periodic, LatticeUpdate::EveryTimestep);
1706 
1707  // Create the fields lattice
1708  fields_lat_ = make_unique<FieldsLattice>(l, n, origin, periodic,
1710  }
1711  } else {
1712  if (dens_type_lattice_printout_ == DensityType::Baryon) {
1713  jmu_B_lat_ = make_unique<DensityLattice>(l, n, origin, periodic,
1715  }
1716  if (dens_type_lattice_printout_ == DensityType::BaryonicIsospin) {
1717  jmu_I3_lat_ = make_unique<DensityLattice>(l, n, origin, periodic,
1719  }
1720  }
1721  if (dens_type_lattice_printout_ != DensityType::None &&
1722  dens_type_lattice_printout_ != DensityType::BaryonicIsospin &&
1723  dens_type_lattice_printout_ != DensityType::Baryon) {
1724  jmu_custom_lat_ = make_unique<DensityLattice>(l, n, origin, periodic,
1726  }
1727  } else if (printout_lattice_td_ || printout_full_lattice_any_td_) {
1728  logg[LExperiment].error(
1729  "If you want Therm. VTK or Lattice output, configure a lattice for "
1730  "it.");
1731  } else if (potentials_ && potentials_->use_coulomb()) {
1732  logg[LExperiment].error(
1733  "Coulomb potential requires a lattice. Please add one to the "
1734  "configuration");
1735  }
1736 
1737  // Warning for the mean field calculation if lattice is not on.
1738  if ((potentials_ != nullptr) && (jmu_B_lat_ == nullptr)) {
1739  logg[LExperiment].warn() << "Lattice is NOT used. Mean-field energy is "
1740  << "not going to be calculated.";
1741  }
1742 
1743  // Store pointers to potential and lattice accessible for Action
1744  if (parameters_.potential_affect_threshold) {
1745  UB_lat_pointer = UB_lat_.get();
1746  UI3_lat_pointer = UI3_lat_.get();
1747  pot_pointer = potentials_.get();
1748  }
1749 
1750  // Throw fatal if DerivativesMode == FiniteDifference and lattice is not on.
1751  if ((parameters_.derivatives_mode == DerivativesMode::FiniteDifference) &&
1752  (jmu_B_lat_ == nullptr)) {
1753  throw std::runtime_error(
1754  "Lattice is necessary to calculate finite difference gradients.");
1755  }
1756 
1757  // Create forced thermalizer
1758  if (config.has_value({"Forced_Thermalization"})) {
1759  Configuration &&th_conf = config["Forced_Thermalization"];
1760  thermalizer_ = modus_.create_grandcan_thermalizer(th_conf);
1761  }
1762 
1763  /* Take the seed setting only after the configuration was stored to a file
1764  * in smash.cc */
1765  seed_ = config.take({"General", "Randomseed"});
1766 }
1767 
1769 const std::string hline(113, '-');
1770 
1795 std::string format_measurements(const std::vector<Particles> &ensembles,
1796  uint64_t scatterings_this_interval,
1797  const QuantumNumbers &conserved_initial,
1798  SystemTimePoint time_start, double time,
1799  double E_mean_field,
1800  double E_mean_field_initial);
1815  const Potentials &potentials,
1817  RectangularLattice<std::pair<ThreeVector, ThreeVector>> *em_lattice,
1818  const ExperimentParameters &parameters);
1819 
1834 EventInfo fill_event_info(const std::vector<Particles> &ensembles,
1835  double E_mean_field, double modus_impact_parameter,
1836  const ExperimentParameters &parameters,
1837  bool projectile_target_interact);
1838 
1839 template <typename Modus>
1841  random::set_seed(seed_);
1842  logg[LExperiment].info() << "random number seed: " << seed_;
1843  /* Set seed for the next event. It has to be positive, so it can be entered
1844  * in the config.
1845  *
1846  * We have to be careful about the minimal integer, whose absolute value
1847  * cannot be represented. */
1848  int64_t r = random::advance();
1849  while (r == INT64_MIN) {
1850  r = random::advance();
1851  }
1852  seed_ = std::abs(r);
1853  /* Set the random seed used in PYTHIA hadronization
1854  * to be same with the SMASH one.
1855  * In this way we ensure that the results are reproducible
1856  * for every event if one knows SMASH random seed. */
1857  if (process_string_ptr_ != NULL) {
1858  process_string_ptr_->init_pythia_hadron_rndm();
1859  }
1860 
1861  for (Particles &particles : ensembles_) {
1862  particles.reset();
1863  }
1864 
1865  // Sample particles according to the initial conditions
1866  double start_time = -1.0;
1867 
1868  // Sample impact parameter only once per all ensembles
1869  // It should be the same for all ensembles
1870  if (modus_.is_collider()) {
1871  modus_.sample_impact();
1872  logg[LExperiment].info("Impact parameter = ", modus_.impact_parameter(),
1873  " fm");
1874  }
1875  for (Particles &particles : ensembles_) {
1876  start_time = modus_.initial_conditions(&particles, parameters_);
1877  }
1878  /* For box modus make sure that particles are in the box. In principle, after
1879  * a correct initialization they should be, so this is just playing it safe.
1880  */
1881  for (Particles &particles : ensembles_) {
1882  modus_.impose_boundary_conditions(&particles, outputs_);
1883  }
1884  // Reset the simulation clock
1885  double timestep = delta_time_startup_;
1886 
1887  switch (time_step_mode_) {
1888  case TimeStepMode::Fixed:
1889  break;
1890  case TimeStepMode::None:
1891  timestep = end_time_ - start_time;
1892  // Take care of the box modus + timestepless propagation
1893  const double max_dt = modus_.max_timestep(max_transverse_distance_sqr_);
1894  if (max_dt > 0. && max_dt < timestep) {
1895  timestep = max_dt;
1896  }
1897  break;
1898  }
1899  std::unique_ptr<UniformClock> clock_for_this_event;
1900  if (modus_.is_list() && (timestep < 0.0)) {
1901  throw std::runtime_error(
1902  "Timestep for the given event is negative. \n"
1903  "This might happen if the formation times of the input particles are "
1904  "larger than the specified end time of the simulation.");
1905  }
1906  clock_for_this_event = make_unique<UniformClock>(start_time, timestep);
1907  parameters_.labclock = std::move(clock_for_this_event);
1908 
1909  // Reset the output clock
1910  parameters_.outputclock->reset(start_time, true);
1911  // remove time before starting time in case of custom output times.
1912  parameters_.outputclock->remove_times_in_past(start_time);
1913 
1914  logg[LExperiment].debug(
1915  "Lab clock: t_start = ", parameters_.labclock->current_time(),
1916  ", dt = ", parameters_.labclock->timestep_duration());
1917 
1918  /* Save the initial conserved quantum numbers and total momentum in
1919  * the system for conservation checks */
1920  conserved_initial_ = QuantumNumbers(ensembles_);
1921  wall_actions_total_ = 0;
1922  previous_wall_actions_total_ = 0;
1923  interactions_total_ = 0;
1924  previous_interactions_total_ = 0;
1925  discarded_interactions_total_ = 0;
1926  total_pauli_blocked_ = 0;
1927  projectile_target_interact_.resize(parameters_.n_ensembles, false);
1928  total_hypersurface_crossing_actions_ = 0;
1929  total_energy_removed_ = 0.0;
1930  // Print output headers
1931  logg[LExperiment].info() << hline;
1932  logg[LExperiment].info() << "Time[fm] Ekin[GeV] E_MF[GeV] ETotal[GeV] "
1933  << "ETot/N[GeV] D(ETot/N)[GeV] Scatt&Decays "
1934  << "Particles Comp.Time";
1935  logg[LExperiment].info() << hline;
1936  double E_mean_field = 0.0;
1937  if (potentials_) {
1938  // update_potentials();
1939  // if (parameters.outputclock->current_time() == 0.0 )
1940  // using the lattice is necessary
1941  if ((jmu_B_lat_ != nullptr)) {
1942  update_lattice(jmu_B_lat_.get(), old_jmu_auxiliary_.get(),
1943  new_jmu_auxiliary_.get(), four_gradient_auxiliary_.get(),
1945  density_param_, ensembles_,
1946  parameters_.labclock->timestep_duration(), true);
1947  // Because there was no lattice at t=-Delta_t, the time derivatives
1948  // drho_dt and dj^mu/dt at t=0 are huge, while they shouldn't be; we
1949  // overwrite the time derivative to zero by hand.
1950  for (auto &node : *jmu_B_lat_) {
1951  node.overwrite_drho_dt_to_zero();
1952  node.overwrite_djmu_dt_to_zero();
1953  }
1954  E_mean_field = calculate_mean_field_energy(*potentials_, *jmu_B_lat_,
1955  EM_lat_.get(), parameters_);
1956  }
1957  }
1958  initial_mean_field_energy_ = E_mean_field;
1960  ensembles_, 0u, conserved_initial_, time_start_,
1961  parameters_.labclock->current_time(), E_mean_field,
1962  initial_mean_field_energy_);
1963 
1964  // Output at event start
1965  for (const auto &output : outputs_) {
1966  for (int i_ens = 0; i_ens < parameters_.n_ensembles; i_ens++) {
1967  auto event_info =
1968  fill_event_info(ensembles_, E_mean_field, modus_.impact_parameter(),
1969  parameters_, projectile_target_interact_[i_ens]);
1970  output->at_eventstart(ensembles_[i_ens],
1971  // Pretend each ensemble is an independent event
1972  event_ * parameters_.n_ensembles + i_ens,
1973  event_info);
1974  }
1975  // For thermodynamic output
1976  output->at_eventstart(ensembles_, event_);
1977  // For thermodynamic lattice output
1978  if (printout_full_lattice_any_td_) {
1979  switch (dens_type_lattice_printout_) {
1980  case DensityType::Baryon:
1981  output->at_eventstart(event_, ThermodynamicQuantity::EckartDensity,
1982  DensityType::Baryon, *jmu_B_lat_);
1983  break;
1985  output->at_eventstart(event_, ThermodynamicQuantity::EckartDensity,
1986  DensityType::BaryonicIsospin, *jmu_I3_lat_);
1987  break;
1988  case DensityType::None:
1989  break;
1990  default:
1991  output->at_eventstart(event_, ThermodynamicQuantity::EckartDensity,
1992  DensityType::BaryonicIsospin, *jmu_custom_lat_);
1993  }
1994  if (printout_tmn_) {
1995  output->at_eventstart(event_, ThermodynamicQuantity::Tmn,
1996  dens_type_lattice_printout_, *Tmn_);
1997  }
1998  if (printout_tmn_landau_) {
1999  output->at_eventstart(event_, ThermodynamicQuantity::TmnLandau,
2000  dens_type_lattice_printout_, *Tmn_);
2001  }
2002  if (printout_v_landau_) {
2003  output->at_eventstart(event_, ThermodynamicQuantity::LandauVelocity,
2004  dens_type_lattice_printout_, *Tmn_);
2005  }
2006  if (printout_j_QBS_) {
2007  output->at_eventstart(event_, ThermodynamicQuantity::j_QBS,
2008  dens_type_lattice_printout_, *j_QBS_lat_);
2009  }
2010  }
2011  }
2012 
2013  /* In the ColliderModus, if Fermi motion is frozen, assign the beam momenta
2014  * to the nucleons in both the projectile and the target. Every ensemble
2015  * gets the same beam momenta, so no need to create beam_momenta_ vector
2016  * for every ensemble.
2017  */
2018  if (modus_.is_collider() && modus_.fermi_motion() == FermiMotion::Frozen) {
2019  for (ParticleData &particle : ensembles_[0]) {
2020  const double m = particle.effective_mass();
2021  double v_beam = 0.0;
2022  if (particle.belongs_to() == BelongsTo::Projectile) {
2023  v_beam = modus_.velocity_projectile();
2024  } else if (particle.belongs_to() == BelongsTo::Target) {
2025  v_beam = modus_.velocity_target();
2026  }
2027  const double gamma = 1.0 / std::sqrt(1.0 - v_beam * v_beam);
2028  beam_momentum_.emplace_back(
2029  FourVector(gamma * m, 0.0, 0.0, gamma * v_beam * m));
2030  } // loop over particles
2031  }
2032 }
2033 
2034 template <typename Modus>
2035 bool Experiment<Modus>::perform_action(Action &action, int i_ensemble) {
2036  Particles &particles = ensembles_[i_ensemble];
2037  // Make sure to skip invalid and Pauli-blocked actions.
2038  if (!action.is_valid(particles)) {
2039  discarded_interactions_total_++;
2040  logg[LExperiment].debug(~einhard::DRed(), "✘ ", action,
2041  " (discarded: invalid)");
2042  return false;
2043  }
2044  action.generate_final_state();
2045  logg[LExperiment].debug("Process Type is: ", action.get_type());
2046  if (pauli_blocker_ && action.is_pauli_blocked(ensembles_, *pauli_blocker_)) {
2047  total_pauli_blocked_++;
2048  return false;
2049  }
2050 
2051  // Prepare projectile_target_interact_, it's used for output
2052  // to signal that there was some interaction in this event
2053  if (modus_.is_collider()) {
2054  int count_target = 0, count_projectile = 0;
2055  for (const auto &incoming : action.incoming_particles()) {
2056  if (incoming.belongs_to() == BelongsTo::Projectile) {
2057  count_projectile++;
2058  } else if (incoming.belongs_to() == BelongsTo::Target) {
2059  count_target++;
2060  }
2061  }
2062  if (count_target > 0 && count_projectile > 0) {
2063  projectile_target_interact_[i_ensemble] = true;
2064  }
2065  }
2066 
2067  /* Make sure to pick a non-zero integer, because 0 is reserved for "no
2068  * interaction yet". */
2069  const auto id_process = static_cast<uint32_t>(interactions_total_ + 1);
2070  action.perform(&particles, id_process);
2071  interactions_total_++;
2072  if (action.get_type() == ProcessType::Wall) {
2073  wall_actions_total_++;
2074  }
2075  if (action.get_type() == ProcessType::HyperSurfaceCrossing) {
2076  total_hypersurface_crossing_actions_++;
2077  total_energy_removed_ += action.incoming_particles()[0].momentum().x0();
2078  }
2079  // Calculate Eckart rest frame density at the interaction point
2080  double rho = 0.0;
2081  if (dens_type_ != DensityType::None) {
2082  const FourVector r_interaction = action.get_interaction_point();
2083  constexpr bool compute_grad = false;
2084  const bool smearing = true;
2085  // todo(oliiny): it's a rough density estimate from a single ensemble.
2086  // It might actually be appropriate for output. Discuss.
2087  rho = std::get<0>(current_eckart(r_interaction.threevec(), particles,
2088  density_param_, dens_type_, compute_grad,
2089  smearing));
2090  }
2106  for (const auto &output : outputs_) {
2107  if (!output->is_dilepton_output() && !output->is_photon_output()) {
2108  if (output->is_IC_output() &&
2110  output->at_interaction(action, rho);
2111  } else if (!output->is_IC_output()) {
2112  output->at_interaction(action, rho);
2113  }
2114  }
2115  }
2116 
2117  // At every collision photons can be produced.
2118  // Note: We rely here on the lazy evaluation of the arguments to if.
2119  // It may happen that in a wall-crossing-action sqrt_s raises an exception.
2120  // Therefore we first have to check if the incoming particles can undergo
2121  // an em-interaction.
2122  if (photons_switch_ &&
2125  action.sqrt_s(), action.incoming_particles())) {
2126  /* Time in the action constructor is relative to
2127  * current time of incoming */
2128  constexpr double action_time = 0.;
2129  ScatterActionPhoton photon_act(action.incoming_particles(), action_time,
2130  n_fractional_photons_,
2131  action.get_total_weight());
2132 
2143  photon_act.add_dummy_hadronic_process(action.get_total_weight());
2144 
2145  // Now add the actual photon reaction channel.
2146  photon_act.add_single_process();
2147 
2148  photon_act.perform_photons(outputs_);
2149  }
2150 
2151  if (bremsstrahlung_switch_ &&
2153  action.incoming_particles())) {
2154  /* Time in the action constructor is relative to
2155  * current time of incoming */
2156  constexpr double action_time = 0.;
2157 
2158  BremsstrahlungAction brems_act(action.incoming_particles(), action_time,
2159  n_fractional_photons_,
2160  action.get_total_weight());
2161 
2173  brems_act.add_dummy_hadronic_process(action.get_total_weight());
2174 
2175  // Now add the actual bremsstrahlung reaction channel.
2176  brems_act.add_single_process();
2177 
2178  brems_act.perform_bremsstrahlung(outputs_);
2179  }
2180 
2181  logg[LExperiment].debug(~einhard::Green(), "✔ ", action);
2182  return true;
2183 }
2184 
2185 template <typename Modus>
2187  while (parameters_.labclock->current_time() < end_time_) {
2188  const double t = parameters_.labclock->current_time();
2189  const double dt =
2190  std::min(parameters_.labclock->timestep_duration(), end_time_ - t);
2191  logg[LExperiment].debug("Timestepless propagation for next ", dt, " fm/c.");
2192 
2193  // Perform forced thermalization if required
2194  if (thermalizer_ &&
2195  thermalizer_->is_time_to_thermalize(parameters_.labclock)) {
2196  const bool ignore_cells_under_treshold = true;
2197  // Thermodynamics in thermalizer is computed from all ensembles,
2198  // but thermalization actions act on each ensemble independently
2199  thermalizer_->update_thermalizer_lattice(ensembles_, density_param_,
2200  ignore_cells_under_treshold);
2201  const double current_t = parameters_.labclock->current_time();
2202  for (int i_ens = 0; i_ens < parameters_.n_ensembles; i_ens++) {
2203  thermalizer_->thermalize(ensembles_[i_ens], current_t,
2204  parameters_.testparticles);
2205  ThermalizationAction th_act(*thermalizer_, current_t);
2206  if (th_act.any_particles_thermalized()) {
2207  perform_action(th_act, i_ens);
2208  }
2209  }
2210  }
2211 
2212  std::vector<Actions> actions(parameters_.n_ensembles);
2213  for (int i_ens = 0; i_ens < parameters_.n_ensembles; i_ens++) {
2214  actions[i_ens].clear();
2215  if (ensembles_[i_ens].size() > 0 && action_finders_.size() > 0) {
2216  /* (1.a) Create grid. */
2217  const double min_cell_length = compute_min_cell_length(dt);
2218  logg[LExperiment].debug("Creating grid with minimal cell length ",
2219  min_cell_length);
2220  const auto &grid =
2221  use_grid_ ? modus_.create_grid(ensembles_[i_ens], min_cell_length,
2222  dt, parameters_.coll_crit)
2223  : modus_.create_grid(ensembles_[i_ens], min_cell_length,
2224  dt, parameters_.coll_crit,
2226 
2227  const double gcell_vol = grid.cell_volume();
2228  /* (1.b) Iterate over cells and find actions. */
2229  grid.iterate_cells(
2230  [&](const ParticleList &search_list) {
2231  for (const auto &finder : action_finders_) {
2232  actions[i_ens].insert(finder->find_actions_in_cell(
2233  search_list, dt, gcell_vol, beam_momentum_));
2234  }
2235  },
2236  [&](const ParticleList &search_list,
2237  const ParticleList &neighbors_list) {
2238  for (const auto &finder : action_finders_) {
2239  actions[i_ens].insert(finder->find_actions_with_neighbors(
2240  search_list, neighbors_list, dt, beam_momentum_));
2241  }
2242  });
2243  }
2244  }
2245 
2246  /* \todo (optimizations) Adapt timestep size here */
2247 
2248  /* (2) Propagate from action to action until next output or timestep end */
2249  const double end_timestep_time =
2250  std::min(parameters_.labclock->next_time(), end_time_);
2251  while (next_output_time() <= end_timestep_time) {
2252  for (int i_ens = 0; i_ens < parameters_.n_ensembles; i_ens++) {
2253  run_time_evolution_timestepless(actions[i_ens], i_ens,
2254  next_output_time());
2255  }
2256  ++(*parameters_.outputclock);
2257 
2258  // Avoid duplication of final output
2259  if (parameters_.outputclock->current_time() < end_time_) {
2260  intermediate_output();
2261  }
2262  }
2263  for (int i_ens = 0; i_ens < parameters_.n_ensembles; i_ens++) {
2264  run_time_evolution_timestepless(actions[i_ens], i_ens, end_timestep_time);
2265  }
2266 
2267  /* (3) Update potentials (if computed on the lattice) and
2268  * compute new momenta according to equations of motion */
2269  if (potentials_) {
2270  update_potentials();
2271  update_momenta(ensembles_, parameters_.labclock->timestep_duration(),
2272  *potentials_, FB_lat_.get(), FI3_lat_.get(),
2273  EM_lat_.get());
2274  }
2275 
2276  /* (4) Expand universe if non-minkowskian metric; updates
2277  * positions and momenta according to the selected expansion */
2278  if (metric_.mode_ != ExpansionMode::NoExpansion) {
2279  for (Particles &particles : ensembles_) {
2280  expand_space_time(&particles, parameters_, metric_);
2281  }
2282  }
2283 
2284  ++(*parameters_.labclock);
2285 
2286  /* (5) Check conservation laws.
2287  *
2288  * Check conservation of conserved quantities if potentials and string
2289  * fragmentation are off. If potentials are on then momentum is conserved
2290  * only in average. If string fragmentation is on, then energy and
2291  * momentum are only very roughly conserved in high-energy collisions. */
2292  if (!potentials_ && !parameters_.strings_switch &&
2293  metric_.mode_ == ExpansionMode::NoExpansion && !IC_output_switch_) {
2294  std::string err_msg = conserved_initial_.report_deviations(ensembles_);
2295  if (!err_msg.empty()) {
2296  logg[LExperiment].error() << err_msg;
2297  throw std::runtime_error("Violation of conserved quantities!");
2298  }
2299  }
2300  }
2301 
2302  if (pauli_blocker_) {
2303  logg[LExperiment].info(
2304  "Interactions: Pauli-blocked/performed = ", total_pauli_blocked_, "/",
2305  interactions_total_ - wall_actions_total_);
2306  }
2307 }
2308 
2309 template <typename Modus>
2311  Particles &particles) {
2312  const double dt =
2313  propagate_straight_line(&particles, to_time, beam_momentum_);
2314  if (dilepton_finder_ != nullptr) {
2315  for (const auto &output : outputs_) {
2316  dilepton_finder_->shine(particles, output.get(), dt);
2317  }
2318  }
2319 }
2320 
2328 inline void check_interactions_total(uint64_t interactions_total) {
2329  constexpr uint64_t max_uint32 = std::numeric_limits<uint32_t>::max();
2330  if (interactions_total >= max_uint32) {
2331  throw std::runtime_error("Integer overflow in total interaction number!");
2332  }
2333 }
2334 
2335 template <typename Modus>
2337  Actions &actions, int i_ensemble, double end_time_propagation) {
2338  Particles &particles = ensembles_[i_ensemble];
2339  logg[LExperiment].debug(
2340  "Timestepless propagation: ", "Actions size = ", actions.size(),
2341  ", end time = ", end_time_propagation);
2342 
2343  // iterate over all actions
2344  while (!actions.is_empty()) {
2345  if (actions.earliest_time() > end_time_propagation) {
2346  break;
2347  }
2348  // get next action
2349  ActionPtr act = actions.pop();
2350  if (!act->is_valid(particles)) {
2351  discarded_interactions_total_++;
2352  logg[LExperiment].debug(~einhard::DRed(), "✘ ", act,
2353  " (discarded: invalid)");
2354  continue;
2355  }
2356  logg[LExperiment].debug(~einhard::Green(), "✔ ", act,
2357  ", action time = ", act->time_of_execution());
2358 
2359  /* (1) Propagate to the next action. */
2360  propagate_and_shine(act->time_of_execution(), particles);
2361 
2362  /* (2) Perform action.
2363  *
2364  * Update the positions of the incoming particles, because the information
2365  * in the action object will be outdated as the particles have been
2366  * propagated since the construction of the action. */
2367  act->update_incoming(particles);
2368  const bool performed = perform_action(*act, i_ensemble);
2369 
2370  /* No need to update actions for outgoing particles
2371  * if the action is not performed. */
2372  if (!performed) {
2373  continue;
2374  }
2375 
2376  /* (3) Update actions for newly-produced particles. */
2377 
2378  const double end_time_timestep =
2379  std::min(parameters_.labclock->next_time(), end_time_);
2380  assert(!(end_time_propagation > end_time_timestep));
2381  // New actions are always search until the end of the current timestep
2382  const double time_left = end_time_timestep - act->time_of_execution();
2383  const ParticleList &outgoing_particles = act->outgoing_particles();
2384  // Grid cell volume set to zero, since there is no grid
2385  const double gcell_vol = 0.0;
2386  for (const auto &finder : action_finders_) {
2387  // Outgoing particles can still decay, cross walls...
2388  actions.insert(finder->find_actions_in_cell(outgoing_particles, time_left,
2389  gcell_vol, beam_momentum_));
2390  // ... and collide with other particles.
2391  actions.insert(finder->find_actions_with_surrounding_particles(
2392  outgoing_particles, particles, time_left, beam_momentum_));
2393  }
2394 
2395  check_interactions_total(interactions_total_);
2396  }
2397 
2398  propagate_and_shine(end_time_propagation, particles);
2399 }
2400 
2401 template <typename Modus>
2403  const uint64_t wall_actions_this_interval =
2404  wall_actions_total_ - previous_wall_actions_total_;
2405  previous_wall_actions_total_ = wall_actions_total_;
2406  const uint64_t interactions_this_interval = interactions_total_ -
2407  previous_interactions_total_ -
2408  wall_actions_this_interval;
2409  previous_interactions_total_ = interactions_total_;
2410  double E_mean_field = 0.0;
2413  double computational_frame_time = 0.0;
2414  if (potentials_) {
2415  // using the lattice is necessary
2416  if ((jmu_B_lat_ != nullptr)) {
2417  E_mean_field = calculate_mean_field_energy(*potentials_, *jmu_B_lat_,
2418  EM_lat_.get(), parameters_);
2419  /*
2420  * Mean field calculated in a box should remain approximately constant if
2421  * the system is in equilibrium, and so deviations from its original value
2422  * may signal a phase transition or other dynamical process. This
2423  * comparison only makes sense in the Box Modus, hence the condition.
2424  */
2425  if (modus_.is_box()) {
2426  double tmp = (E_mean_field - initial_mean_field_energy_) /
2427  (E_mean_field + initial_mean_field_energy_);
2428  /*
2429  * This is displayed when the system evolves away from its initial
2430  * configuration (which is when the total mean field energy in the box
2431  * deviates from its initial value).
2432  */
2433  if (std::abs(tmp) > 0.01) {
2434  logg[LExperiment].info()
2435  << "\n\n\n\t The mean field at t = "
2436  << parameters_.outputclock->current_time()
2437  << " [fm/c] differs from the mean field at t = 0:"
2438  << "\n\t\t initial_mean_field_energy_ = "
2439  << initial_mean_field_energy_ << " [GeV]"
2440  << "\n\t\t abs[(E_MF - E_MF(t=0))/(E_MF + E_MF(t=0))] = "
2441  << std::abs(tmp)
2442  << "\n\t\t E_MF/E_MF(t=0) = "
2443  << E_mean_field / initial_mean_field_energy_ << "\n\n";
2444  }
2445  }
2446  }
2447  }
2448 
2450  ensembles_, interactions_this_interval, conserved_initial_, time_start_,
2451  parameters_.outputclock->current_time(), E_mean_field,
2452  initial_mean_field_energy_);
2453  const LatticeUpdate lat_upd = LatticeUpdate::AtOutput;
2454 
2455  // save evolution data
2456  if (!(modus_.is_box() && parameters_.outputclock->current_time() <
2457  modus_.equilibration_time())) {
2458  for (const auto &output : outputs_) {
2459  if (output->is_dilepton_output() || output->is_photon_output() ||
2460  output->is_IC_output()) {
2461  continue;
2462  }
2463  for (int i_ens = 0; i_ens < parameters_.n_ensembles; i_ens++) {
2464  auto event_info =
2465  fill_event_info(ensembles_, E_mean_field, modus_.impact_parameter(),
2466  parameters_, projectile_target_interact_[i_ens]);
2467 
2468  output->at_intermediate_time(ensembles_[i_ens], parameters_.outputclock,
2469  density_param_, event_info);
2470  computational_frame_time = event_info.current_time;
2471  }
2472  // For thermodynamic output
2473  output->at_intermediate_time(ensembles_, parameters_.outputclock,
2474  density_param_);
2475 
2476  // Thermodynamic output on the lattice versus time
2477  switch (dens_type_lattice_printout_) {
2478  case DensityType::Baryon:
2479  update_lattice(jmu_B_lat_.get(), lat_upd, DensityType::Baryon,
2480  density_param_, ensembles_, false);
2481  output->thermodynamics_output(ThermodynamicQuantity::EckartDensity,
2482  DensityType::Baryon, *jmu_B_lat_);
2483  output->thermodynamics_lattice_output(*jmu_B_lat_,
2484  computational_frame_time);
2485  break;
2487  update_lattice(jmu_I3_lat_.get(), lat_upd,
2488  DensityType::BaryonicIsospin, density_param_,
2489  ensembles_, false);
2490  output->thermodynamics_output(ThermodynamicQuantity::EckartDensity,
2492  *jmu_I3_lat_);
2493  output->thermodynamics_lattice_output(*jmu_I3_lat_,
2494  computational_frame_time);
2495  break;
2496  case DensityType::None:
2497  break;
2498  default:
2499  update_lattice(jmu_custom_lat_.get(), lat_upd,
2500  dens_type_lattice_printout_, density_param_,
2501  ensembles_, false);
2502  output->thermodynamics_output(ThermodynamicQuantity::EckartDensity,
2503  dens_type_lattice_printout_,
2504  *jmu_custom_lat_);
2505  output->thermodynamics_lattice_output(*jmu_custom_lat_,
2506  computational_frame_time);
2507  }
2508  if (printout_tmn_ || printout_tmn_landau_ || printout_v_landau_) {
2509  update_lattice(Tmn_.get(), lat_upd, dens_type_lattice_printout_,
2510  density_param_, ensembles_, false);
2511  if (printout_tmn_) {
2512  output->thermodynamics_output(ThermodynamicQuantity::Tmn,
2513  dens_type_lattice_printout_, *Tmn_);
2514  output->thermodynamics_lattice_output(
2515  ThermodynamicQuantity::Tmn, *Tmn_, computational_frame_time);
2516  }
2517  if (printout_tmn_landau_) {
2518  output->thermodynamics_output(ThermodynamicQuantity::TmnLandau,
2519  dens_type_lattice_printout_, *Tmn_);
2520  output->thermodynamics_lattice_output(
2522  computational_frame_time);
2523  }
2524  if (printout_v_landau_) {
2525  output->thermodynamics_output(ThermodynamicQuantity::LandauVelocity,
2526  dens_type_lattice_printout_, *Tmn_);
2527  output->thermodynamics_lattice_output(
2529  computational_frame_time);
2530  }
2531  }
2532  if (EM_lat_) {
2533  output->fields_output("Efield", "Bfield", *EM_lat_);
2534  }
2535  if (printout_j_QBS_) {
2536  output->thermodynamics_lattice_output(
2537  *j_QBS_lat_, computational_frame_time, ensembles_, density_param_);
2538  }
2539 
2540  if (thermalizer_) {
2541  output->thermodynamics_output(*thermalizer_);
2542  }
2543  }
2544  }
2545 }
2546 
2547 template <typename Modus>
2549  if (potentials_) {
2550  if (potentials_->use_symmetry() && jmu_I3_lat_ != nullptr) {
2551  update_lattice(jmu_I3_lat_.get(), old_jmu_auxiliary_.get(),
2552  new_jmu_auxiliary_.get(), four_gradient_auxiliary_.get(),
2554  density_param_, ensembles_,
2555  parameters_.labclock->timestep_duration(), true);
2556  }
2557  if ((potentials_->use_skyrme() || potentials_->use_symmetry()) &&
2558  jmu_B_lat_ != nullptr) {
2559  update_lattice(jmu_B_lat_.get(), old_jmu_auxiliary_.get(),
2560  new_jmu_auxiliary_.get(), four_gradient_auxiliary_.get(),
2562  density_param_, ensembles_,
2563  parameters_.labclock->timestep_duration(), true);
2564  const size_t UBlattice_size = UB_lat_->size();
2565  for (size_t i = 0; i < UBlattice_size; i++) {
2566  auto jB = (*jmu_B_lat_)[i];
2567  const FourVector flow_four_velocity_B =
2568  std::abs(jB.rho()) > very_small_double ? jB.jmu_net() / jB.rho()
2569  : FourVector();
2570  double baryon_density = jB.rho();
2571  ThreeVector baryon_grad_j0 = jB.grad_j0();
2572  ThreeVector baryon_dvecj_dt = jB.dvecj_dt();
2573  ThreeVector baryon_curl_vecj = jB.curl_vecj();
2574  if (potentials_->use_skyrme()) {
2575  (*UB_lat_)[i] =
2576  flow_four_velocity_B * potentials_->skyrme_pot(baryon_density);
2577  (*FB_lat_)[i] =
2578  potentials_->skyrme_force(baryon_density, baryon_grad_j0,
2579  baryon_dvecj_dt, baryon_curl_vecj);
2580  }
2581  if (potentials_->use_symmetry() && jmu_I3_lat_ != nullptr) {
2582  auto jI3 = (*jmu_I3_lat_)[i];
2583  const FourVector flow_four_velocity_I3 =
2584  std::abs(jI3.rho()) > very_small_double
2585  ? jI3.jmu_net() / jI3.rho()
2586  : FourVector();
2587  (*UI3_lat_)[i] = flow_four_velocity_I3 *
2588  potentials_->symmetry_pot(jI3.rho(), baryon_density);
2589  (*FI3_lat_)[i] = potentials_->symmetry_force(
2590  jI3.rho(), jI3.grad_j0(), jI3.dvecj_dt(), jI3.curl_vecj(),
2591  baryon_density, baryon_grad_j0, baryon_dvecj_dt,
2592  baryon_curl_vecj);
2593  }
2594  }
2595  }
2596  if (potentials_->use_coulomb()) {
2597  update_lattice(jmu_el_lat_.get(), LatticeUpdate::EveryTimestep,
2598  DensityType::Charge, density_param_, ensembles_, true);
2599  for (size_t i = 0; i < EM_lat_->size(); i++) {
2600  ThreeVector electric_field = {0., 0., 0.};
2601  ThreeVector position = jmu_el_lat_->cell_center(i);
2602  jmu_el_lat_->integrate_volume(electric_field,
2604  potentials_->coulomb_r_cut(), position);
2605  ThreeVector magnetic_field = {0., 0., 0.};
2606  jmu_el_lat_->integrate_volume(magnetic_field,
2608  potentials_->coulomb_r_cut(), position);
2609  (*EM_lat_)[i] = std::make_pair(electric_field, magnetic_field);
2610  }
2611  } // if ((potentials_->use_skyrme() || ...
2612  if (potentials_->use_vdf() && jmu_B_lat_ != nullptr) {
2613  update_lattice(jmu_B_lat_.get(), old_jmu_auxiliary_.get(),
2614  new_jmu_auxiliary_.get(), four_gradient_auxiliary_.get(),
2616  density_param_, ensembles_,
2617  parameters_.labclock->timestep_duration(), true);
2618  if (parameters_.field_derivatives_mode == FieldDerivativesMode::Direct) {
2620  fields_lat_.get(), old_fields_auxiliary_.get(),
2621  new_fields_auxiliary_.get(), fields_four_gradient_auxiliary_.get(),
2622  jmu_B_lat_.get(), LatticeUpdate::EveryTimestep, *potentials_,
2623  parameters_.labclock->timestep_duration());
2624  }
2625  const size_t UBlattice_size = UB_lat_->size();
2626  for (size_t i = 0; i < UBlattice_size; i++) {
2627  auto jB = (*jmu_B_lat_)[i];
2628  (*UB_lat_)[i] = potentials_->vdf_pot(jB.rho(), jB.jmu_net());
2629  switch (parameters_.field_derivatives_mode) {
2631  (*FB_lat_)[i] = potentials_->vdf_force(
2632  jB.rho(), jB.drho_dxnu().x0(), jB.drho_dxnu().threevec(),
2633  jB.grad_rho_cross_vecj(), jB.jmu_net().x0(), jB.grad_j0(),
2634  jB.jmu_net().threevec(), jB.dvecj_dt(), jB.curl_vecj());
2635  break;
2637  auto Amu = (*fields_lat_)[i];
2638  (*FB_lat_)[i] = potentials_->vdf_force(
2639  Amu.grad_A0(), Amu.dvecA_dt(), Amu.curl_vecA());
2640  break;
2641  }
2642  } // for (size_t i = 0; i < UBlattice_size; i++)
2643  } // if potentials_->use_vdf()
2644  }
2645 }
2646 
2647 template <typename Modus>
2649  /* At end of time evolution: Force all resonances to decay. In order to handle
2650  * decay chains, we need to loop until no further actions occur. */
2651  uint64_t interactions_old = 0;
2652  do {
2653  interactions_old = interactions_total_;
2654 
2655  for (int i_ens = 0; i_ens < parameters_.n_ensembles; i_ens++) {
2656  Actions actions;
2657 
2658  // Dileptons: shining of remaining resonances
2659  if (dilepton_finder_ != nullptr) {
2660  for (const auto &output : outputs_) {
2661  dilepton_finder_->shine_final(ensembles_[i_ens], output.get(), true);
2662  }
2663  }
2664  // Find actions.
2665  for (const auto &finder : action_finders_) {
2666  actions.insert(finder->find_final_actions(ensembles_[i_ens]));
2667  }
2668  // Perform actions.
2669  while (!actions.is_empty()) {
2670  perform_action(*actions.pop(), i_ens);
2671  }
2672  }
2673  // loop until no more decays occur
2674  } while (interactions_total_ > interactions_old);
2675 
2676  // Dileptons: shining of stable particles at the end
2677  if (dilepton_finder_ != nullptr) {
2678  for (const auto &output : outputs_) {
2679  for (Particles &particles : ensembles_) {
2680  dilepton_finder_->shine_final(particles, output.get(), false);
2681  }
2682  }
2683  }
2684 }
2685 
2686 template <typename Modus>
2688  /* make sure the experiment actually ran (note: we should compare this
2689  * to the start time, but we don't know that. Therefore, we check that
2690  * the time is positive, which should heuristically be the same). */
2691  double E_mean_field = 0.0;
2692  if (likely(parameters_.labclock > 0)) {
2693  const uint64_t wall_actions_this_interval =
2694  wall_actions_total_ - previous_wall_actions_total_;
2695  const uint64_t interactions_this_interval = interactions_total_ -
2696  previous_interactions_total_ -
2697  wall_actions_this_interval;
2698  if (potentials_) {
2699  // using the lattice is necessary
2700  if ((jmu_B_lat_ != nullptr)) {
2701  E_mean_field = calculate_mean_field_energy(*potentials_, *jmu_B_lat_,
2702  EM_lat_.get(), parameters_);
2703  }
2704  }
2706  ensembles_, interactions_this_interval, conserved_initial_, time_start_,
2707  end_time_, E_mean_field, initial_mean_field_energy_);
2708  int total_particles = 0;
2709  for (const Particles &particles : ensembles_) {
2710  total_particles += particles.size();
2711  }
2712  if (IC_output_switch_ && (total_particles == 0)) {
2713  // Verify there is no more energy in the system if all particles were
2714  // removed when crossing the hypersurface
2715  const double remaining_energy =
2716  conserved_initial_.momentum().x0() - total_energy_removed_;
2717  if (remaining_energy > really_small) {
2718  throw std::runtime_error(
2719  "There is remaining energy in the system although all particles "
2720  "were removed.\n"
2721  "E_remain = " +
2722  std::to_string(remaining_energy) + " [GeV]");
2723  } else {
2724  logg[LExperiment].info() << hline;
2725  logg[LExperiment].info()
2726  << "Time real: " << SystemClock::now() - time_start_;
2727  logg[LExperiment].info()
2728  << "Interactions before reaching hypersurface: "
2729  << interactions_total_ - wall_actions_total_ -
2730  total_hypersurface_crossing_actions_;
2731  logg[LExperiment].info()
2732  << "Total number of particles removed on hypersurface: "
2733  << total_hypersurface_crossing_actions_;
2734  }
2735  } else {
2736  const double precent_discarded =
2737  interactions_total_ > 0
2738  ? static_cast<double>(discarded_interactions_total_) * 100.0 /
2739  interactions_total_
2740  : 0.0;
2741  std::stringstream msg_discarded;
2742  msg_discarded
2743  << "Discarded interaction number: " << discarded_interactions_total_
2744  << " (" << precent_discarded
2745  << "% of the total interaction number including wall crossings)";
2746 
2747  logg[LExperiment].info() << hline;
2748  logg[LExperiment].info()
2749  << "Time real: " << SystemClock::now() - time_start_;
2750  logg[LExperiment].debug() << msg_discarded.str();
2751 
2752  if (parameters_.coll_crit == CollisionCriterion::Stochastic &&
2753  precent_discarded > 1.0) {
2754  // The choosen threshold of 1% is a heuristical value
2755  logg[LExperiment].warn()
2756  << msg_discarded.str()
2757  << "\nThe number of discarded interactions is large, which means "
2758  "the assumption for the stochastic criterion of\n1 interaction "
2759  "per particle per timestep is probably violated. Consider "
2760  "reducing the timestep size.";
2761  }
2762 
2763  logg[LExperiment].info() << "Final interaction number: "
2764  << interactions_total_ - wall_actions_total_;
2765  }
2766 
2767  // Check if there are unformed particles
2768  int unformed_particles_count = 0;
2769  for (const Particles &particles : ensembles_) {
2770  for (const ParticleData &particle : particles) {
2771  if (particle.formation_time() > end_time_) {
2772  unformed_particles_count++;
2773  }
2774  }
2775  }
2776  if (unformed_particles_count > 0) {
2777  logg[LExperiment].warn(
2778  "End time might be too small. ", unformed_particles_count,
2779  " unformed particles were found at the end of the evolution.");
2780  }
2781  }
2782 
2783  for (const auto &output : outputs_) {
2784  for (int i_ens = 0; i_ens < parameters_.n_ensembles; i_ens++) {
2785  auto event_info =
2786  fill_event_info(ensembles_, E_mean_field, modus_.impact_parameter(),
2787  parameters_, projectile_target_interact_[i_ens]);
2788  output->at_eventend(ensembles_[i_ens],
2789  // Pretend each ensemble is an independent event
2790  event_ * parameters_.n_ensembles + i_ens, event_info);
2791  }
2792  // For thermodynamic output
2793  output->at_eventend(ensembles_, event_);
2794 
2795  // For thermodynamic lattice output
2796  if (dens_type_lattice_printout_ != DensityType::None) {
2797  output->at_eventend(event_, ThermodynamicQuantity::EckartDensity,
2798  dens_type_lattice_printout_);
2799  output->at_eventend(ThermodynamicQuantity::EckartDensity);
2800  }
2801  if (printout_tmn_) {
2802  output->at_eventend(event_, ThermodynamicQuantity::Tmn,
2803  dens_type_lattice_printout_);
2804  output->at_eventend(ThermodynamicQuantity::Tmn);
2805  }
2806  if (printout_tmn_landau_) {
2807  output->at_eventend(event_, ThermodynamicQuantity::TmnLandau,
2808  dens_type_lattice_printout_);
2809  output->at_eventend(ThermodynamicQuantity::TmnLandau);
2810  }
2811  if (printout_v_landau_) {
2812  output->at_eventend(event_, ThermodynamicQuantity::LandauVelocity,
2813  dens_type_lattice_printout_);
2814  output->at_eventend(ThermodynamicQuantity::LandauVelocity);
2815  }
2816  if (printout_j_QBS_) {
2817  output->at_eventend(ThermodynamicQuantity::j_QBS);
2818  }
2819  }
2820 }
2821 
2822 template <typename Modus>
2824  const auto &mainlog = logg[LMain];
2825  for (event_ = 0; event_ < nevents_; event_++) {
2826  mainlog.info() << "Event " << event_;
2827 
2828  // Sample initial particles, start clock, some printout and book-keeping
2829  initialize_new_event();
2830 
2831  run_time_evolution();
2832 
2833  if (force_decays_) {
2834  do_final_decays();
2835  }
2836 
2837  // Output at event end
2838  final_output();
2839  }
2840 }
2841 
2842 } // namespace smash
2843 
2844 #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:143
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.
const ParticleList & incoming_particles() const
Get the list of particles that go into the action.
Definition: action.cc:57
virtual void perform(Particles *particles, uint32_t id_process)
Actually perform the action, e.g.
Definition: action.cc:124
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:266
FourVector get_interaction_point() const
Get the interaction point.
Definition: action.cc:67
bool is_valid(const Particles &particles) const
Check whether the action still applies.
Definition: action.cc:28
bool is_pauli_blocked(const std::vector< Particles > &ensembles, const PauliBlocker &p_bl) const
Check if the action is Pauli-blocked.
Definition: action.cc:34
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
static bool is_bremsstrahlung_reaction(const ParticleList &in)
Check if particles can undergo an implemented photon process.
Interface to the SMASH configuration files.
A class to pre-calculate and store parameters relevant for density calculation.
Definition: density.h:108
Non-template interface to Experiment<Modus>.
Definition: experiment.h:97
static std::unique_ptr< ExperimentBase > create(Configuration config, const bf::path &output_path)
Factory method that creates and initializes a new Experiment<Modus>.
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:177
const bool bremsstrahlung_switch_
This indicates whether bremsstrahlung is switched on.
Definition: experiment.h:538
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:2310
const ExpansionProperties metric_
This struct contains information on the metric to be used.
Definition: experiment.h:529
std::unique_ptr< ActionFinderInterface > photon_finder_
The (Scatter) Actions Finder for Direct Photons.
Definition: experiment.h:376
const bool force_decays_
This indicates whether we force all resonances to decay in the last timestep.
Definition: experiment.h:523
double initial_mean_field_energy_
The initial total mean field energy in the system.
Definition: experiment.h:565
std::vector< std::unique_ptr< ActionFinderInterface > > action_finders_
The Action finder objects.
Definition: experiment.h:370
bool printout_tmn_
Whether to print the energy-momentum tensor.
Definition: experiment.h:455
QuantumNumbers conserved_initial_
The conserved quantities of the system.
Definition: experiment.h:559
DensityParameters density_param_
Structure to precalculate and hold parameters for density computations.
Definition: experiment.h:321
double next_output_time() const
Shortcut for next output time.
Definition: experiment.h:309
bool printout_full_lattice_ascii_td_
Whether to print the thermodynamics quantities evaluated on the lattices, point by point,...
Definition: experiment.h:471
double total_energy_removed_
Total energy removed from the system in hypersurface crossing actions.
Definition: experiment.h:619
DensityType dens_type_lattice_printout_
Type of density for lattice printout.
Definition: experiment.h:406
double max_transverse_distance_sqr_
Maximal distance at which particles can interact in case of the geometric criterion,...
Definition: experiment.h:550
bool printout_j_QBS_
Whether to print the Q, B, S 4-currents.
Definition: experiment.h:464
std::unique_ptr< GrandCanThermalizer > thermalizer_
Instance of class used for forced thermalization.
Definition: experiment.h:482
bool perform_action(Action &action, int i_ensemble)
Perform the given action.
const TimeStepMode time_step_mode_
This indicates whether to use time steps.
Definition: experiment.h:544
std::unique_ptr< DensityLattice > jmu_custom_lat_
Custom density on the lattices.
Definition: experiment.h:403
bool printout_full_lattice_any_td_
Whether to print the thermodynamics quantities evaluated on the lattices, point by point,...
Definition: experiment.h:479
Particles * first_ensemble()
Provides external access to SMASH particles.
Definition: experiment.h:231
std::unique_ptr< DensityLattice > jmu_B_lat_
Baryon density on the lattice.
Definition: experiment.h:385
void create_output(const std::string &format, const std::string &content, const bf::path &output_path, const OutputParameters &par)
Create a list of output files.
Definition: experiment.h:641
DensityType dens_type_
Type of density to be written to collision headers.
Definition: experiment.h:571
const int nevents_
Number of events.
Definition: experiment.h:504
void intermediate_output()
Intermediate output during an event.
Definition: experiment.h:2402
std::vector< FourVector > beam_momentum_
The initial nucleons in the ColliderModus propagate with beam_momentum_, if Fermi motion is frozen.
Definition: experiment.h:367
std::unique_ptr< RectangularLattice< std::pair< ThreeVector, ThreeVector > > > FI3_lat_
Lattices for the electric and magnetic component of the symmetry force.
Definition: experiment.h:429
std::unique_ptr< RectangularLattice< FourVector > > new_jmu_auxiliary_
Auxiliary lattice for values of jmu at a time step t0 + dt.
Definition: experiment.h:441
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:301
void initialize_new_event()
This is called in the beginning of each event.
Definition: experiment.h:1840
int n_fractional_photons_
Number of fractional photons produced per single reaction.
Definition: experiment.h:379
const double delta_time_startup_
The clock's timestep size at start up.
Definition: experiment.h:517
std::unique_ptr< RectangularLattice< EnergyMomentumTensor > > Tmn_
Lattices of energy-momentum tensors for printout.
Definition: experiment.h:436
std::vector< Particles > ensembles_
Complete particle list, all ensembles in one vector.
Definition: experiment.h:330
std::unique_ptr< RectangularLattice< FourVector > > new_fields_auxiliary_
Auxiliary lattice for values of Amu at a time step t0 + dt.
Definition: experiment.h:449
SystemTimePoint time_start_
system starting time of the simulation
Definition: experiment.h:568
void run_time_evolution()
Runs the time evolution of an event with fixed-sized time steps or without timesteps,...
Definition: experiment.h:2186
uint64_t previous_wall_actions_total_
Total number of wall-crossings for previous timestep.
Definition: experiment.h:595
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:425
OutputsList outputs_
A list of output formaters.
Definition: experiment.h:348
int event_
Current event.
Definition: experiment.h:507
std::unique_ptr< RectangularLattice< std::array< FourVector, 4 > > > fields_four_gradient_auxiliary_
Auxiliary lattice for calculating the four-gradient of Amu.
Definition: experiment.h:452
void do_final_decays()
Performs the final decays of an event.
Definition: experiment.h:2648
std::unique_ptr< PauliBlocker > pauli_blocker_
An instance of PauliBlocker class that stores parameters needed for Pauli blocking calculations and c...
Definition: experiment.h:342
bool printout_v_landau_
Whether to print the 4-velocity in Landau frame.
Definition: experiment.h:461
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:418
bool printout_lattice_td_
Whether to print the thermodynamics quantities evaluated on the lattices.
Definition: experiment.h:467
std::unique_ptr< RectangularLattice< std::pair< ThreeVector, ThreeVector > > > EM_lat_
Lattices for electric and magnetic field in fm^-2.
Definition: experiment.h:433
std::unique_ptr< FieldsLattice > fields_lat_
Mean-field A^mu on the lattice.
Definition: experiment.h:394
OutputPtr photon_output_
The Photon output.
Definition: experiment.h:354
void run_time_evolution_timestepless(Actions &actions, int i_ensemble, double end_time_propagation)
Performs all the propagations and actions during a certain time interval neglecting the influence of ...
Definition: experiment.h:2336
const bool dileptons_switch_
This indicates whether dileptons are switched on.
Definition: experiment.h:532
Modus modus_
Instance of the Modus template parameter.
Definition: experiment.h:327
std::unique_ptr< RectangularLattice< FourVector > > old_jmu_auxiliary_
Auxiliary lattice for values of jmu at a time step t0.
Definition: experiment.h:439
std::unique_ptr< DecayActionsFinderDilepton > dilepton_finder_
The Dilepton Action Finder.
Definition: experiment.h:373
std::unique_ptr< DensityLattice > j_QBS_lat_
4-current for j_QBS lattice output
Definition: experiment.h:382
bool printout_tmn_landau_
Whether to print the energy-momentum tensor in Landau frame.
Definition: experiment.h:458
Experiment(Configuration config, const bf::path &output_path)
Create a new Experiment.
bool printout_full_lattice_binary_td_
Whether to print the thermodynamics quantities evaluated on the lattices, point by point,...
Definition: experiment.h:475
std::unique_ptr< RectangularLattice< std::array< FourVector, 4 > > > four_gradient_auxiliary_
Auxiliary lattice for calculating the four-gradient of jmu.
Definition: experiment.h:444
const bool photons_switch_
This indicates whether photons are switched on.
Definition: experiment.h:535
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:488
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:412
ExperimentParameters parameters_
Struct of several member variables.
Definition: experiment.h:318
std::unique_ptr< RectangularLattice< FourVector > > old_fields_auxiliary_
Auxiliary lattice for values of Amu at a time step t0.
Definition: experiment.h:447
std::unique_ptr< DensityLattice > jmu_el_lat_
Electric charge density on the lattice.
Definition: experiment.h:391
std::unique_ptr< Potentials > potentials_
An instance of potentials class, that stores parameters of potentials, calculates them and their grad...
Definition: experiment.h:336
uint64_t wall_actions_total_
Total number of wall-crossings for current timestep.
Definition: experiment.h:589
uint64_t total_hypersurface_crossing_actions_
Total number of particles removed from the evolution in hypersurface crossing actions.
Definition: experiment.h:607
uint64_t interactions_total_
Total number of interactions for current timestep.
Definition: experiment.h:577
const bool use_grid_
This indicates whether to use the grid.
Definition: experiment.h:526
std::unique_ptr< DensityLattice > jmu_I3_lat_
Isospin projection density on the lattice.
Definition: experiment.h:388
uint64_t total_pauli_blocked_
Total number of Pauli-blockings for current timestep.
Definition: experiment.h:601
void final_output()
Output at the end of an event.
Definition: experiment.h:2687
std::vector< bool > projectile_target_interact_
Whether the projectile and the target collided.
Definition: experiment.h:360
Modus * modus()
Provides external access to SMASH calculation modus.
Definition: experiment.h:239
void update_potentials()
Recompute potentials on lattices if necessary.
Definition: experiment.h:2548
const bool IC_output_switch_
This indicates whether the IC output is enabled.
Definition: experiment.h:541
OutputPtr dilepton_output_
The Dilepton output.
Definition: experiment.h:351
int64_t seed_
random seed for the next event.
Definition: experiment.h:622
std::vector< Particles > * all_ensembles()
Getter for all ensembles.
Definition: experiment.h:233
uint64_t previous_interactions_total_
Total number of interactions for previous timestep.
Definition: experiment.h:583
uint64_t discarded_interactions_total_
Total number of discarded interactions, because they were invalidated before they could be performed.
Definition: experiment.h:613
void run() override
Runs the experiment.
Definition: experiment.h:2823
const double end_time_
simulation time at which the evolution is stopped.
Definition: experiment.h:510
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:58
static double formation_power_
Power with which the cross section scaling factor grows in time.
Definition: particledata.h:389
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:32
static ThreeVector B_field_integrand(ThreeVector pos, DensityOnLattice &charge_density, ThreeVector point)
Integrand for calculating the magnetic field using the Biot-Savart formula.
Definition: potentials.h:222
static ThreeVector E_field_integrand(ThreeVector pos, DensityOnLattice &charge_density, ThreeVector point)
Integrand for calculating the electric field.
Definition: potentials.h:204
A container for storing conserved values.
A container class to hold all the arrays on the lattice and access them.
Definition: lattice.h:47
static bool is_photon_reaction(const ParticleList &in)
Check if particles can undergo an implemented photon process.
static bool is_kinematically_possible(const double s_sqrt, const ParticleList &in)
Check if CM-energy is sufficient to produce hadron in final state.
String excitation processes used in SMASH.
Definition: stringprocess.h:45
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
FermiMotion
Option to use Fermi Motion.
@ Frozen
Use fermi motion without potentials.
@ Off
Don't use fermi motion.
TimeStepMode
The time step mode.
@ Fixed
Use fixed time step.
@ None
Don't use time steps; propagate from action to action.
@ Stochastic
Stochastic Criteiron.
#define SMASH_SOURCE_LOCATION
Hackery that is required to output the location in the source code where the log statement occurs.
Definition: logging.h:243
std::ostream & operator<<(std::ostream &out, const ActionPtr &action)
Convenience: dereferences the ActionPtr to Action.
Definition: action.h:529
std::array< einhard::Logger<>, std::tuple_size< LogArea::AreaTuple >::value > logg
An array that stores all pre-configured Logger objects.
Definition: logging.cc:39
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:307
std::unique_ptr< OutputInterface > create_oscar_output(const std::string &format, const std::string &content, const bf::path &path, const OutputParameters &out_par)
Definition: oscaroutput.cc:769
#define likely(x)
Tell the branch predictor that this expression is likely true.
Definition: macros.h:14
constexpr int n
Neutron.
Engine::result_type advance()
Advance the engine's state and return the generated value.
Definition: random.h:78
void set_seed(T &&seed)
Sets the seed of the random number engine.
Definition: random.h:71
Definition: action.h:24
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)
Updates the momenta of all particles at the current time step according to the equations of motion:
Definition: propagation.cc:111
static constexpr int LInitialConditions
Definition: experiment.h:88
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:679
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:86
void update_lattice(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.
Definition: density.h:536
void check_interactions_total(uint64_t interactions_total)
Make sure interactions_total can be represented as a 32-bit integer.
Definition: experiment.h:2328
@ Wall
box wall crossing
@ HyperSurfaceCrossing
Hypersurface crossing Particles are removed from the evolution and printed to a separate output to se...
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)
Definition: density.cc:167
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
constexpr double very_small_double
A very small double, used to avoid division by zero.
Definition: constants.h:40
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:727
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
constexpr double nucleon_mass
Nucleon mass in GeV.
Definition: constants.h:58
LatticeUpdate
Enumerator option for lattice updates.
Definition: lattice.h:36
std::ostream & operator<<(std::ostream &out, const Experiment< Modus > &e)
Creates a verbose textual description of the setup of the Experiment.
Definition: experiment.h:634
EventInfo fill_event_info(const std::vector< Particles > &ensembles, double E_mean_field, double modus_impact_parameter, const ExperimentParameters &parameters, bool projectile_target_interact)
Generate the EventInfo object which is passed to outputs_.
Definition: experiment.cc:947
Potentials * pot_pointer
Pointer to a Potential class.
static constexpr int LMain
Definition: experiment.h:87
constexpr double really_small
Numerical error tolerance.
Definition: constants.h:37
DensityType
Allows to choose which kind of density to calculate.
Definition: density.h:36
RectangularLattice< FourVector > * UB_lat_pointer
Pointer to the skyrme potential on the lattice.
ExperimentParameters create_experiment_parameters(Configuration config)
Gathers all general Experiment parameters.
Definition: experiment.cc:474
std::unique_ptr< T > make_unique(Args &&... args)
Definition for make_unique Is in C++14's standard library; necessary for older compilers.
Definition: cxx14compat.h:26
@ Largest
Make cells as large as possible.
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:28
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:142
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.
Helper structure for Experiment to hold output options and parameters.