Version: SMASH-2.2
experiment.h
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2013-2022
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 
151  struct NonExistingOutputPathRequest : public std::invalid_argument {
152  using std::invalid_argument::invalid_argument;
153  };
154 };
155 
156 template <typename Modus>
157 class Experiment;
158 template <typename Modus>
159 std::ostream &operator<<(std::ostream &out, const Experiment<Modus> &e);
160 
185 template <typename Modus>
186 class Experiment : public ExperimentBase {
187  friend class ExperimentBase;
188 
189  public:
196  void run() override;
197 
211  explicit Experiment(Configuration config, const bf::path &output_path);
212 
218  void initialize_new_event();
219 
230  void run_time_evolution(const double t_end);
231 
237  void do_final_decays();
238 
240  void final_output();
241 
246  // todo(oliiny): this should be made compatible with JetScape on
247  // the JetScape side
250  std::vector<Particles> *all_ensembles() { return &ensembles_; }
251 
256  Modus *modus() { return &modus_; }
257 
258  private:
271  bool perform_action(Action &action, int i_ensemble,
272  bool include_pauli_blocking = true);
281  void create_output(const std::string &format, const std::string &content,
282  const bf::path &output_path, const OutputParameters &par);
283 
291  void propagate_and_shine(double to_time, Particles &particles);
292 
306  void run_time_evolution_timestepless(Actions &actions, int i_ensemble,
307  const double end_time_propagation,
308  const double end_time_run);
309 
311  void intermediate_output();
312 
314  void update_potentials();
315 
324  double compute_min_cell_length(double dt) const {
327  }
328  return std::sqrt(4 * dt * dt + max_transverse_distance_sqr_);
329  }
330 
332  double next_output_time() const {
333  return parameters_.outputclock->next_time();
334  }
335 
341 
347  bool is_finished();
348 
355 
358 
363  Modus modus_;
364 
366  std::vector<Particles> ensembles_;
367 
372  std::unique_ptr<Potentials> potentials_;
373 
378  std::unique_ptr<PauliBlocker> pauli_blocker_;
379 
384  OutputsList outputs_;
385 
387  OutputPtr dilepton_output_;
388 
390  OutputPtr photon_output_;
391 
396  std::vector<bool> projectile_target_interact_;
397 
403  std::vector<FourVector> beam_momentum_ = {};
404 
406  std::vector<std::unique_ptr<ActionFinderInterface>> action_finders_;
407 
409  std::unique_ptr<DecayActionsFinderDilepton> dilepton_finder_;
410 
412  std::unique_ptr<ActionFinderInterface> photon_finder_;
413 
416 
418  std::unique_ptr<DensityLattice> j_QBS_lat_;
419 
421  std::unique_ptr<DensityLattice> jmu_B_lat_;
422 
424  std::unique_ptr<DensityLattice> jmu_I3_lat_;
425 
427  std::unique_ptr<DensityLattice> jmu_el_lat_;
428 
430  std::unique_ptr<FieldsLattice> fields_lat_;
431 
439  std::unique_ptr<DensityLattice> jmu_custom_lat_;
440 
443 
448  std::unique_ptr<RectangularLattice<FourVector>> UB_lat_ = nullptr;
449 
454  std::unique_ptr<RectangularLattice<FourVector>> UI3_lat_ = nullptr;
455 
460  std::unique_ptr<RectangularLattice<std::pair<ThreeVector, ThreeVector>>>
462 
464  std::unique_ptr<RectangularLattice<std::pair<ThreeVector, ThreeVector>>>
466 
468  std::unique_ptr<RectangularLattice<std::pair<ThreeVector, ThreeVector>>>
470 
472  std::unique_ptr<RectangularLattice<EnergyMomentumTensor>> Tmn_;
473 
475  std::unique_ptr<RectangularLattice<FourVector>> old_jmu_auxiliary_;
477  std::unique_ptr<RectangularLattice<FourVector>> new_jmu_auxiliary_;
479  std::unique_ptr<RectangularLattice<std::array<FourVector, 4>>>
481 
483  std::unique_ptr<RectangularLattice<FourVector>> old_fields_auxiliary_;
485  std::unique_ptr<RectangularLattice<FourVector>> new_fields_auxiliary_;
487  std::unique_ptr<RectangularLattice<std::array<FourVector, 4>>>
489 
491  bool printout_tmn_ = false;
492 
494  bool printout_tmn_landau_ = false;
495 
497  bool printout_v_landau_ = false;
498 
500  bool printout_j_QBS_ = false;
501 
503  bool printout_lattice_td_ = false;
504 
508 
512 
516 
518  std::unique_ptr<GrandCanThermalizer> thermalizer_;
519 
525 
540  const int nevents_ = 0;
541 
551 
559 
561  int event_ = 0;
562 
565 
570  int max_events_ = 0;
571 
573  const double end_time_;
574 
580  const double delta_time_startup_;
581 
586  const bool force_decays_;
587 
589  const bool use_grid_;
590 
593 
595  const bool dileptons_switch_;
596 
598  const bool photons_switch_;
599 
602 
604  const bool IC_output_switch_;
605 
608 
613  double max_transverse_distance_sqr_ = std::numeric_limits<double>::max();
614 
623 
629 
631  SystemTimePoint time_start_ = SystemClock::now();
632 
635 
640  uint64_t interactions_total_ = 0;
641 
647 
652  uint64_t wall_actions_total_ = 0;
653 
659 
664  uint64_t total_pauli_blocked_ = 0;
665 
671 
677 
682  double total_energy_removed_ = 0.0;
683 
686 
688  int64_t seed_ = -1;
689 
695  friend std::ostream &operator<<<>(std::ostream &out, const Experiment &e);
696 };
697 
699 template <typename Modus>
700 std::ostream &operator<<(std::ostream &out, const Experiment<Modus> &e) {
701  out << "End time: " << e.end_time_ << " fm/c\n";
702  out << e.modus_;
703  return out;
704 }
705 
706 template <typename Modus>
707 void Experiment<Modus>::create_output(const std::string &format,
708  const std::string &content,
709  const bf::path &output_path,
710  const OutputParameters &out_par) {
711  logg[LExperiment].info() << "Adding output " << content << " of format "
712  << format << std::endl;
713 
714  if (format == "VTK" && content == "Particles") {
715  outputs_.emplace_back(
716  make_unique<VtkOutput>(output_path, content, out_par));
717  } else if (format == "Root") {
718 #ifdef SMASH_USE_ROOT
719  if (content == "Initial_Conditions") {
720  outputs_.emplace_back(
721  make_unique<RootOutput>(output_path, "SMASH_IC", out_par));
722  } else {
723  outputs_.emplace_back(
724  make_unique<RootOutput>(output_path, content, out_par));
725  }
726 #else
727  logg[LExperiment].error(
728  "Root output requested, but Root support not compiled in");
729 #endif
730  } else if (format == "Binary") {
731  if (content == "Collisions" || content == "Dileptons" ||
732  content == "Photons") {
733  outputs_.emplace_back(
734  make_unique<BinaryOutputCollisions>(output_path, content, out_par));
735  } else if (content == "Particles") {
736  outputs_.emplace_back(
737  make_unique<BinaryOutputParticles>(output_path, content, out_par));
738  } else if (content == "Initial_Conditions") {
739  outputs_.emplace_back(make_unique<BinaryOutputInitialConditions>(
740  output_path, content, out_par));
741  }
742  } else if (format == "Oscar1999" || format == "Oscar2013") {
743  outputs_.emplace_back(
744  create_oscar_output(format, content, output_path, out_par));
745  } else if (content == "Thermodynamics" && format == "ASCII") {
746  outputs_.emplace_back(
747  make_unique<ThermodynamicOutput>(output_path, content, out_par));
748  } else if (content == "Thermodynamics" &&
749  (format == "Lattice_ASCII" || format == "Lattice_Binary")) {
750  printout_full_lattice_any_td_ = true;
751  if (format == "Lattice_ASCII") {
752  printout_full_lattice_ascii_td_ = true;
753  }
754  if (format == "Lattice_Binary") {
755  printout_full_lattice_binary_td_ = true;
756  }
757  outputs_.emplace_back(make_unique<ThermodynamicLatticeOutput>(
758  output_path, content, out_par, printout_full_lattice_ascii_td_,
759  printout_full_lattice_binary_td_));
760  } else if (content == "Thermodynamics" && format == "VTK") {
761  printout_lattice_td_ = true;
762  outputs_.emplace_back(
763  make_unique<VtkOutput>(output_path, content, out_par));
764  } else if (content == "Initial_Conditions" && format == "ASCII") {
765  outputs_.emplace_back(
766  make_unique<ICOutput>(output_path, "SMASH_IC", out_par));
767  } else if ((format == "HepMC") || (format == "HepMC_asciiv3") ||
768  (format == "HepMC_treeroot")) {
769 #ifdef SMASH_USE_HEPMC
770  if (content == "Particles") {
771  if ((format == "HepMC") || (format == "HepMC_asciiv3")) {
772  outputs_.emplace_back(make_unique<HepMcOutput>(
773  output_path, "SMASH_HepMC_particles", false, "asciiv3"));
774  } else if (format == "HepMC_treeroot") {
775 #ifdef SMASH_USE_HEPMC_ROOTIO
776  outputs_.emplace_back(make_unique<HepMcOutput>(
777  output_path, "SMASH_HepMC_particles", false, "root"));
778 #else
779  logg[LExperiment].error(
780  "Requested HepMC_treeroot output not available, "
781  "ROOT or HepMC3 ROOTIO missing or not found by cmake.");
782 #endif
783  }
784  } else if (content == "Collisions") {
785  if ((format == "HepMC") || (format == "HepMC_asciiv3")) {
786  outputs_.emplace_back(make_unique<HepMcOutput>(
787  output_path, "SMASH_HepMC_collisions", true, "asciiv3"));
788  } else if (format == "HepMC_treeroot") {
789 #ifdef SMASH_USE_HEPMC_ROOTIO
790  outputs_.emplace_back(make_unique<HepMcOutput>(
791  output_path, "SMASH_HepMC_collisions", true, "root"));
792 #else
793  logg[LExperiment].error(
794  "Requested HepMC_treeroot output not available, "
795  "ROOT or HepMC3 ROOTIO missing or not found by cmake.");
796 #endif
797  }
798  } else {
799  logg[LExperiment].error(
800  "HepMC only available for Particles and "
801  "Collisions content. Requested for " +
802  content + ".");
803  }
804 #else
805  logg[LExperiment].error(
806  "HepMC output requested, but HepMC support not compiled in");
807 #endif
808  } else if (content == "Coulomb" && format == "VTK") {
809  outputs_.emplace_back(
810  make_unique<VtkOutput>(output_path, "Fields", out_par));
811  } else if (content == "Rivet") {
812 #ifdef SMASH_USE_RIVET
813  // flag to ensure that the Rivet format has not been already assigned
814  static bool rivet_format_already_selected = false;
815  // if the next check is true, then we are trying to assign the format twice
816  if (rivet_format_already_selected) {
817  logg[LExperiment].warn(
818  "Rivet output format can only be one, either YODA or YODA-full. "
819  "Only your first valid choice will be used.");
820  return;
821  }
822  if (format == "YODA") {
823  outputs_.emplace_back(
824  make_unique<RivetOutput>(output_path, "SMASH_Rivet", false, out_par));
825  rivet_format_already_selected = true;
826  } else if (format == "YODA-full") {
827  outputs_.emplace_back(make_unique<RivetOutput>(
828  output_path, "SMASH_Rivet_full", true, out_par));
829  rivet_format_already_selected = true;
830  } else {
831  logg[LExperiment].error("Rivet format " + format +
832  "not one of YODA or YODA-full");
833  }
834 #else
835  logg[LExperiment].error(
836  "Rivet output requested, but Rivet support not compiled in");
837 #endif
838  } else {
839  logg[LExperiment].error()
840  << "Unknown combination of format (" << format << ") and content ("
841  << content << "). Fix the config.";
842  }
843 }
844 
853 
1072 template <typename Modus>
1073 Experiment<Modus>::Experiment(Configuration config, const bf::path &output_path)
1074  : parameters_(create_experiment_parameters(config)),
1075  density_param_(DensityParameters(parameters_)),
1076  modus_(config["Modi"], parameters_),
1077  ensembles_(parameters_.n_ensembles),
1078  nevents_(config.take({"General", "Nevents"}, 0)),
1079  end_time_(config.take({"General", "End_Time"})),
1080  delta_time_startup_(parameters_.labclock->timestep_duration()),
1081  force_decays_(
1082  config.take({"Collision_Term", "Force_Decays_At_End"}, true)),
1083  use_grid_(config.take({"General", "Use_Grid"}, true)),
1084  metric_(
1085  config.take({"General", "Metric_Type"}, ExpansionMode::NoExpansion),
1086  config.take({"General", "Expansion_Rate"}, 0.1)),
1087  dileptons_switch_(
1088  config.take({"Collision_Term", "Dileptons", "Decays"}, false)),
1089  photons_switch_(config.take(
1090  {"Collision_Term", "Photons", "2to2_Scatterings"}, false)),
1091  bremsstrahlung_switch_(
1092  config.take({"Collision_Term", "Photons", "Bremsstrahlung"}, false)),
1093  IC_output_switch_(config.has_value({"Output", "Initial_Conditions"})),
1094  time_step_mode_(
1095  config.take({"General", "Time_Step_Mode"}, TimeStepMode::Fixed)) {
1096  logg[LExperiment].info() << *this;
1097 
1098  if (config.has_value({"General", "Minimum_Nonempty_Ensembles"})) {
1099  if (config.has_value({"General", "Nevents"})) {
1100  throw std::invalid_argument(
1101  "Please specify either Nevents or Minimum_Nonempty_Ensembles.");
1102  }
1103  event_counting_ = EventCounting::MinimumNonEmpty;
1104  minimum_nonempty_ensembles_ =
1105  config.take({"General", "Minimum_Nonempty_Ensembles", "Number"});
1106  int max_ensembles = config.take(
1107  {"General", "Minimum_Nonempty_Ensembles", "Maximum_Ensembles_Run"});
1108  max_events_ =
1109  std::ceil(static_cast<double>(max_ensembles) / parameters_.n_ensembles);
1110  } else {
1111  event_counting_ = EventCounting::FixedNumber;
1112  }
1113 
1114  // covariant derivatives can only be done with covariant smearing
1115  if (parameters_.derivatives_mode == DerivativesMode::CovariantGaussian &&
1116  parameters_.smearing_mode != SmearingMode::CovariantGaussian) {
1117  throw std::invalid_argument(
1118  "Covariant Gaussian derivatives only make sense for Covariant Gaussian "
1119  "smearing!");
1120  }
1121 
1122  // for triangular smearing:
1123  // the weight needs to be larger than 1./7. for the center cell to contribute
1124  // more than the surrounding cells
1125  if (parameters_.smearing_mode == SmearingMode::Discrete &&
1126  parameters_.discrete_weight < (1. / 7.)) {
1127  throw std::invalid_argument(
1128  "The central weight for discrete smearing should be >= 1./7.");
1129  }
1130 
1131  if (parameters_.coll_crit == CollisionCriterion::Stochastic &&
1132  (time_step_mode_ != TimeStepMode::Fixed || !use_grid_)) {
1133  throw std::invalid_argument(
1134  "The stochastic criterion can only be employed for fixed time step "
1135  "mode and with a grid!");
1136  }
1137 
1138  logg[LExperiment].info("Using ", parameters_.testparticles,
1139  " testparticles per particle.");
1140  logg[LExperiment].info("Using ", parameters_.n_ensembles,
1141  " parallel ensembles.");
1142 
1143  // create finders
1144  if (dileptons_switch_) {
1145  dilepton_finder_ = make_unique<DecayActionsFinderDilepton>();
1146  }
1147  if (photons_switch_ || bremsstrahlung_switch_) {
1148  n_fractional_photons_ =
1149  config.take({"Collision_Term", "Photons", "Fractional_Photons"}, 100);
1150  }
1151  if (parameters_.two_to_one) {
1152  if (parameters_.res_lifetime_factor < 0.) {
1153  throw std::invalid_argument(
1154  "Resonance lifetime modifier cannot be negative!");
1155  }
1156  if (parameters_.res_lifetime_factor < really_small) {
1157  logg[LExperiment].warn(
1158  "Resonance lifetime set to zero. Make sure resonances cannot "
1159  "interact",
1160  "inelastically (e.g. resonance chains), else SMASH is known to "
1161  "hang.");
1162  }
1163  action_finders_.emplace_back(make_unique<DecayActionsFinder>(
1164  parameters_.res_lifetime_factor, parameters_.do_weak_decays));
1165  }
1166  bool no_coll = config.take({"Collision_Term", "No_Collisions"}, false);
1167  if ((parameters_.two_to_one || parameters_.included_2to2.any() ||
1168  parameters_.included_multi.any() || parameters_.strings_switch) &&
1169  !no_coll) {
1170  auto scat_finder = make_unique<ScatterActionsFinder>(config, parameters_);
1171  max_transverse_distance_sqr_ =
1172  scat_finder->max_transverse_distance_sqr(parameters_.testparticles);
1173  process_string_ptr_ = scat_finder->get_process_string_ptr();
1174  action_finders_.emplace_back(std::move(scat_finder));
1175  } else {
1176  max_transverse_distance_sqr_ =
1177  parameters_.maximum_cross_section / M_PI * fm2_mb;
1178  process_string_ptr_ = NULL;
1179  }
1180  if (modus_.is_box()) {
1181  action_finders_.emplace_back(
1182  make_unique<WallCrossActionsFinder>(parameters_.box_length));
1183  }
1184  if (IC_output_switch_) {
1185  if (!modus_.is_collider()) {
1186  throw std::runtime_error(
1187  "Initial conditions can only be extracted in collider modus.");
1188  }
1189  double proper_time;
1190  if (config.has_value({"Output", "Initial_Conditions", "Proper_Time"})) {
1191  // Read in proper time from config
1192  proper_time =
1193  config.take({"Output", "Initial_Conditions", "Proper_Time"});
1194  } else {
1195  // Default proper time is the passing time of the two nuclei
1196  double default_proper_time = modus_.nuclei_passing_time();
1197  double lower_bound =
1198  config.take({"Output", "Initial_Conditions", "Lower_Bound"}, 0.5);
1199  if (default_proper_time >= lower_bound) {
1200  proper_time = default_proper_time;
1201  } else {
1202  logg[LInitialConditions].warn()
1203  << "Nuclei passing time is too short, hypersurface proper time set "
1204  "to tau = "
1205  << lower_bound << " fm.";
1206  proper_time = lower_bound;
1207  }
1208  }
1209 
1210  double rapidity_cut = 0.0;
1211  if (config.has_value({"Output", "Initial_Conditions", "Rapidity_Cut"})) {
1212  rapidity_cut =
1213  config.take({"Output", "Initial_Conditions", "Rapidity_Cut"});
1214  if (rapidity_cut <= 0.0) {
1215  logg[LInitialConditions].fatal()
1216  << "Rapidity cut for initial conditions configured as abs(y) < "
1217  << rapidity_cut << " is unreasonable. \nPlease choose a positive, "
1218  << "non-zero value or employ SMASH without rapidity cut.";
1219  throw std::runtime_error(
1220  "Kinematic cut for initial conditions malconfigured.");
1221  }
1222  }
1223 
1224  if (modus_.calculation_frame_is_fixed_target() && rapidity_cut != 0.0) {
1225  throw std::runtime_error(
1226  "Rapidity cut for initial conditions output is not implemented "
1227  "in the fixed target calculation frame. \nPlease use "
1228  "\"center of velocity\" or \"center of mass\" as a "
1229  "\"Calculation_Frame\" instead.");
1230  }
1231 
1232  double transverse_momentum_cut = 0.0;
1233  if (config.has_value({"Output", "Initial_Conditions", "pT_Cut"})) {
1234  transverse_momentum_cut =
1235  config.take({"Output", "Initial_Conditions", "pT_Cut"});
1236  if (transverse_momentum_cut <= 0.0) {
1237  logg[LInitialConditions].fatal()
1238  << "transverse momentum cut for initial conditions configured as "
1239  "pT < "
1240  << rapidity_cut << " is unreasonable. \nPlease choose a positive, "
1241  << "non-zero value or employ SMASH without pT cut.";
1242  throw std::runtime_error(
1243  "Kinematic cut for initial conditions malconfigured.");
1244  }
1245  }
1246 
1247  if (rapidity_cut > 0.0 || transverse_momentum_cut > 0.0) {
1248  kinematic_cuts_for_IC_output_ = true;
1249  }
1250 
1251  if (rapidity_cut > 0.0 && transverse_momentum_cut > 0.0) {
1252  logg[LInitialConditions].info()
1253  << "Extracting initial conditions in kinematic range: "
1254  << -rapidity_cut << " <= y <= " << rapidity_cut
1255  << "; pT <= " << transverse_momentum_cut << " GeV.";
1256  } else if (rapidity_cut > 0.0) {
1257  logg[LInitialConditions].info()
1258  << "Extracting initial conditions in kinematic range: "
1259  << -rapidity_cut << " <= y <= " << rapidity_cut << ".";
1260  } else if (transverse_momentum_cut > 0.0) {
1261  logg[LInitialConditions].info()
1262  << "Extracting initial conditions in kinematic range: pT <= "
1263  << transverse_momentum_cut << " GeV.";
1264  } else {
1265  logg[LInitialConditions].info()
1266  << "Extracting initial conditions without kinematic cuts.";
1267  }
1268 
1269  action_finders_.emplace_back(make_unique<HyperSurfaceCrossActionsFinder>(
1270  proper_time, rapidity_cut, transverse_momentum_cut));
1271  }
1272 
1273  if (config.has_value({"Collision_Term", "Pauli_Blocking"})) {
1274  logg[LExperiment].info() << "Pauli blocking is ON.";
1275  pauli_blocker_ = make_unique<PauliBlocker>(
1276  config["Collision_Term"]["Pauli_Blocking"], parameters_);
1277  }
1278  // In collider setup with sqrts >= 200 GeV particles don't form continuously
1280  config.take({"Collision_Term", "Power_Particle_Formation"},
1281  modus_.sqrt_s_NN() >= 200. ? -1. : 1.);
1282 
1315  // create outputs
1317  " create OutputInterface objects");
1318 
1319  auto output_conf = config["Output"];
1547  dens_type_ = config.take({"Output", "Density_Type"}, DensityType::None);
1548  logg[LExperiment].debug()
1549  << "Density type printed to headers: " << dens_type_;
1550 
1551  const OutputParameters output_parameters(std::move(output_conf));
1552 
1553  std::vector<std::string> output_contents = output_conf.list_upmost_nodes();
1554  for (const auto &content : output_contents) {
1555  auto this_output_conf = output_conf[content.c_str()];
1556  const std::vector<std::string> formats = this_output_conf.take({"Format"});
1557  if (output_path == "") {
1558  continue;
1559  }
1560  for (const auto &format : formats) {
1561  create_output(format, content, output_path, output_parameters);
1562  }
1563  }
1564 
1565  /* We can take away the Fermi motion flag, because the collider modus is
1566  * already initialized. We only need it when potentials are enabled, but we
1567  * always have to take it, otherwise SMASH will complain about unused
1568  * options. We have to provide a default value for modi other than Collider.
1569  */
1570  const FermiMotion motion =
1571  config.take({"Modi", "Collider", "Fermi_Motion"}, FermiMotion::Off);
1572  if (config.has_value({"Potentials"})) {
1573  if (time_step_mode_ == TimeStepMode::None) {
1574  logg[LExperiment].error() << "Potentials only work with time steps!";
1575  throw std::invalid_argument("Can't use potentials without time steps!");
1576  }
1577  if (motion == FermiMotion::Frozen) {
1578  logg[LExperiment].error()
1579  << "Potentials don't work with frozen Fermi momenta! "
1580  "Use normal Fermi motion instead.";
1581  throw std::invalid_argument(
1582  "Can't use potentials "
1583  "with frozen Fermi momenta!");
1584  }
1585  logg[LExperiment].info() << "Potentials are ON. Timestep is "
1586  << parameters_.labclock->timestep_duration();
1587  // potentials need density calculation parameters from parameters_
1588  potentials_ = make_unique<Potentials>(config["Potentials"], parameters_);
1589  // make sure that vdf potentials are not used together with Skyrme
1590  // or symmetry potentials
1591  if (potentials_->use_skyrme() && potentials_->use_vdf()) {
1592  throw std::runtime_error(
1593  "Can't use Skyrme and VDF potentials at the same time!");
1594  }
1595  if (potentials_->use_symmetry() && potentials_->use_vdf()) {
1596  throw std::runtime_error(
1597  "Can't use symmetry and VDF potentials at the same time!");
1598  }
1599  if (potentials_->use_skyrme()) {
1600  logg[LExperiment].info() << "Skyrme potentials are:\n";
1601  logg[LExperiment].info()
1602  << "\t\tSkyrme_A [MeV] = " << potentials_->skyrme_a() << "\n";
1603  logg[LExperiment].info()
1604  << "\t\tSkyrme_B [MeV] = " << potentials_->skyrme_b() << "\n";
1605  logg[LExperiment].info()
1606  << "\t\t Skyrme_tau = " << potentials_->skyrme_tau() << "\n";
1607  }
1608  if (potentials_->use_symmetry()) {
1609  logg[LExperiment].info()
1610  << "Symmetry potential is:"
1611  << "\n S_pot [MeV] = " << potentials_->symmetry_S_pot() << "\n";
1612  }
1613  if (potentials_->use_vdf()) {
1614  logg[LExperiment].info() << "VDF potential parameters are:\n";
1615  logg[LExperiment].info() << "\t\tsaturation density [fm^-3] = "
1616  << potentials_->saturation_density() << "\n";
1617  for (int i = 0; i < potentials_->number_of_terms(); i++) {
1618  logg[LExperiment].info()
1619  << "\t\tCoefficient_" << i + 1 << " = "
1620  << 1000.0 * (potentials_->coeffs())[i] << " [MeV] \t Power_"
1621  << i + 1 << " = " << (potentials_->powers())[i] << "\n";
1622  }
1623  }
1624  // if potentials are on, derivatives need to be calculated
1625  if (parameters_.derivatives_mode == DerivativesMode::Off &&
1626  parameters_.field_derivatives_mode == FieldDerivativesMode::ChainRule) {
1627  throw std::invalid_argument(
1628  "Derivatives are necessary for running with potentials.\n"
1629  "Derivatives_Mode: \"Off\" only makes sense for "
1630  "Field_Derivatives_Mode: \"Direct\"!\nUse \"Covariant Gaussian\" or "
1631  "\"Finite difference\".");
1632  }
1633  // for computational efficiency, we want to turn off the derivatives of jmu
1634  // and the rest frame density derivatives if direct derivatives are used
1635  if (parameters_.field_derivatives_mode == FieldDerivativesMode::Direct) {
1636  parameters_.derivatives_mode = DerivativesMode::Off;
1637  parameters_.rho_derivatives_mode = RestFrameDensityDerivativesMode::Off;
1638  }
1639  switch (parameters_.derivatives_mode) {
1641  logg[LExperiment].info() << "Covariant Gaussian derivatives are ON";
1642  break;
1644  logg[LExperiment].info() << "Finite difference derivatives are ON";
1645  break;
1646  case DerivativesMode::Off:
1647  logg[LExperiment].info() << "Gradients of baryon current are OFF";
1648  break;
1649  }
1650  switch (parameters_.rho_derivatives_mode) {
1652  logg[LExperiment].info() << "Rest frame density derivatives are ON";
1653  break;
1655  logg[LExperiment].info() << "Rest frame density derivatives are OFF";
1656  break;
1657  }
1658  // direct or chain rule derivatives only make sense for the VDF potentials
1659  if (potentials_->use_vdf()) {
1660  switch (parameters_.field_derivatives_mode) {
1662  logg[LExperiment].info() << "Chain rule field derivatives are ON";
1663  break;
1665  logg[LExperiment].info() << "Direct field derivatives are ON";
1666  break;
1667  }
1668  }
1669  /*
1670  * Necessary safety checks
1671  */
1672  // VDF potentials need derivatives of rest frame density or fields
1673  if (potentials_->use_vdf() && (parameters_.rho_derivatives_mode ==
1675  parameters_.field_derivatives_mode ==
1677  throw std::runtime_error(
1678  "Can't use VDF potentials without rest frame density derivatives or "
1679  "direct field derivatives!");
1680  }
1681  // potentials require using gradients
1682  if (parameters_.derivatives_mode == DerivativesMode::Off &&
1683  parameters_.field_derivatives_mode == FieldDerivativesMode::ChainRule) {
1684  throw std::runtime_error(
1685  "Can't use potentials without gradients of baryon current (Skyrme, "
1686  "VDF)"
1687  " or direct field derivatives (VDF)!");
1688  }
1689  // direct field derivatives only make sense for the VDF potentials
1690  if (!(potentials_->use_vdf()) &&
1691  parameters_.field_derivatives_mode == FieldDerivativesMode::Direct) {
1692  throw std::invalid_argument(
1693  "Field_Derivatives_Mode: \"Direct\" only makes sense for the VDF "
1694  "potentials!\nUse Field_Derivatives_Mode: \"Chain Rule\" or comment "
1695  "this option out (Chain Rule is default)");
1696  }
1697  }
1698 
1699  // information about the type of smearing
1700  switch (parameters_.smearing_mode) {
1702  logg[LExperiment].info() << "Smearing type: Covariant Gaussian";
1703  break;
1705  logg[LExperiment].info() << "Smearing type: Discrete with weight = "
1706  << parameters_.discrete_weight;
1707  break;
1709  logg[LExperiment].info() << "Smearing type: Triangular with range = "
1710  << parameters_.triangular_range;
1711  break;
1712  }
1713 
1793  // Create lattices
1794  if (config.has_value({"Lattice"})) {
1795  std::array<double, 3> l_default{20., 20., 20.};
1796  std::array<int, 3> n_default{10, 10, 10};
1797  std::array<double, 3> origin_default{-20., -20., -20.};
1798  bool periodic_default = false;
1799  if (modus_.is_collider()) {
1800  // Estimates on how far particles could get in x, y, z
1801  const double gam = modus_.sqrt_s_NN() / (2.0 * nucleon_mass);
1802  const double max_z = 5.0 / gam + end_time_;
1803  const double estimated_max_transverse_velocity = 0.7;
1804  const double max_xy = 5.0 + estimated_max_transverse_velocity * end_time_;
1805  origin_default = {-max_xy, -max_xy, -max_z};
1806  l_default = {2 * max_xy, 2 * max_xy, 2 * max_z};
1807  // Go for approximately 0.8 fm cell size and contract
1808  // lattice in z by gamma factor
1809  const int n_xy = std::ceil(2 * max_xy / 0.8);
1810  int nz = std::ceil(2 * max_z / 0.8);
1811  // Contract lattice by gamma factor in case of smearing where
1812  // smearing length is bound to the lattice cell length
1813  if (parameters_.smearing_mode == SmearingMode::Discrete ||
1814  parameters_.smearing_mode == SmearingMode::Triangular) {
1815  nz = static_cast<int>(std::ceil(2 * max_z / 0.8 * gam));
1816  }
1817  n_default = {n_xy, n_xy, nz};
1818  } else if (modus_.is_box()) {
1819  periodic_default = true;
1820  origin_default = {0., 0., 0.};
1821  const double bl = modus_.length();
1822  l_default = {bl, bl, bl};
1823  const int n_xyz = std::ceil(bl / 0.5);
1824  n_default = {n_xyz, n_xyz, n_xyz};
1825  } else if (modus_.is_sphere()) {
1826  // Maximal distance from (0, 0, 0) at which a particle
1827  // may be found at the end of the simulation
1828  const double max_d = modus_.radius() + end_time_;
1829  origin_default = {-max_d, -max_d, -max_d};
1830  l_default = {2 * max_d, 2 * max_d, 2 * max_d};
1831  // Go for approximately 0.8 fm cell size
1832  const int n_xyz = std::ceil(2 * max_d / 0.8);
1833  n_default = {n_xyz, n_xyz, n_xyz};
1834  }
1835  // Take lattice properties from config to assign them to all lattices
1836  const std::array<double, 3> l =
1837  config.take({"Lattice", "Sizes"}, l_default);
1838  const std::array<int, 3> n =
1839  config.take({"Lattice", "Cell_Number"}, n_default);
1840  const std::array<double, 3> origin =
1841  config.take({"Lattice", "Origin"}, origin_default);
1842  const bool periodic =
1843  config.take({"Lattice", "Periodic"}, periodic_default);
1844 
1845  logg[LExperiment].info()
1846  << "Lattice is ON. Origin = (" << origin[0] << "," << origin[1] << ","
1847  << origin[2] << "), sizes = (" << l[0] << "," << l[1] << "," << l[2]
1848  << "), number of cells = (" << n[0] << "," << n[1] << "," << n[2]
1849  << ")";
1850 
1851  if (printout_lattice_td_ || printout_full_lattice_any_td_) {
1852  dens_type_lattice_printout_ = output_parameters.td_dens_type;
1853  printout_tmn_ = output_parameters.td_tmn;
1854  printout_tmn_landau_ = output_parameters.td_tmn_landau;
1855  printout_v_landau_ = output_parameters.td_v_landau;
1856  printout_j_QBS_ = output_parameters.td_jQBS;
1857  }
1858  if (printout_tmn_ || printout_tmn_landau_ || printout_v_landau_) {
1859  Tmn_ = make_unique<RectangularLattice<EnergyMomentumTensor>>(
1860  l, n, origin, periodic, LatticeUpdate::AtOutput);
1861  }
1862  if (printout_j_QBS_) {
1863  j_QBS_lat_ = make_unique<DensityLattice>(l, n, origin, periodic,
1865  }
1866  /* Create baryon and isospin density lattices regardless of config
1867  if potentials are on. This is because they allow to compute
1868  potentials faster */
1869  if (potentials_) {
1870  // Create auxiliary lattices for baryon four-current calculation
1871  old_jmu_auxiliary_ = make_unique<RectangularLattice<FourVector>>(
1872  l, n, origin, periodic, LatticeUpdate::EveryTimestep);
1873  new_jmu_auxiliary_ = make_unique<RectangularLattice<FourVector>>(
1874  l, n, origin, periodic, LatticeUpdate::EveryTimestep);
1875  four_gradient_auxiliary_ =
1876  make_unique<RectangularLattice<std::array<FourVector, 4>>>(
1877  l, n, origin, periodic, LatticeUpdate::EveryTimestep);
1878 
1879  if (potentials_->use_skyrme()) {
1880  jmu_B_lat_ = make_unique<DensityLattice>(l, n, origin, periodic,
1882  UB_lat_ = make_unique<RectangularLattice<FourVector>>(
1883  l, n, origin, periodic, LatticeUpdate::EveryTimestep);
1884  FB_lat_ = make_unique<
1885  RectangularLattice<std::pair<ThreeVector, ThreeVector>>>(
1886  l, n, origin, periodic, LatticeUpdate::EveryTimestep);
1887  }
1888  if (potentials_->use_symmetry()) {
1889  jmu_I3_lat_ = make_unique<DensityLattice>(l, n, origin, periodic,
1891  UI3_lat_ = make_unique<RectangularLattice<FourVector>>(
1892  l, n, origin, periodic, LatticeUpdate::EveryTimestep);
1893  FI3_lat_ = make_unique<
1894  RectangularLattice<std::pair<ThreeVector, ThreeVector>>>(
1895  l, n, origin, periodic, LatticeUpdate::EveryTimestep);
1896  }
1897  if (potentials_->use_coulomb()) {
1898  jmu_el_lat_ = make_unique<DensityLattice>(l, n, origin, periodic,
1900  EM_lat_ = make_unique<
1901  RectangularLattice<std::pair<ThreeVector, ThreeVector>>>(
1902  l, n, origin, periodic, LatticeUpdate::EveryTimestep);
1903  }
1904  if (potentials_->use_vdf()) {
1905  jmu_B_lat_ = make_unique<DensityLattice>(l, n, origin, periodic,
1907  UB_lat_ = make_unique<RectangularLattice<FourVector>>(
1908  l, n, origin, periodic, LatticeUpdate::EveryTimestep);
1909  FB_lat_ = make_unique<
1910  RectangularLattice<std::pair<ThreeVector, ThreeVector>>>(
1911  l, n, origin, periodic, LatticeUpdate::EveryTimestep);
1912  }
1913  if (parameters_.field_derivatives_mode == FieldDerivativesMode::Direct) {
1914  // Create auxiliary lattices for field calculation
1915  old_fields_auxiliary_ = make_unique<RectangularLattice<FourVector>>(
1916  l, n, origin, periodic, LatticeUpdate::EveryTimestep);
1917  new_fields_auxiliary_ = make_unique<RectangularLattice<FourVector>>(
1918  l, n, origin, periodic, LatticeUpdate::EveryTimestep);
1919  fields_four_gradient_auxiliary_ =
1920  make_unique<RectangularLattice<std::array<FourVector, 4>>>(
1921  l, n, origin, periodic, LatticeUpdate::EveryTimestep);
1922 
1923  // Create the fields lattice
1924  fields_lat_ = make_unique<FieldsLattice>(l, n, origin, periodic,
1926  }
1927  } else {
1928  if (dens_type_lattice_printout_ == DensityType::Baryon) {
1929  jmu_B_lat_ = make_unique<DensityLattice>(l, n, origin, periodic,
1931  }
1932  if (dens_type_lattice_printout_ == DensityType::BaryonicIsospin) {
1933  jmu_I3_lat_ = make_unique<DensityLattice>(l, n, origin, periodic,
1935  }
1936  }
1937  if (dens_type_lattice_printout_ != DensityType::None &&
1938  dens_type_lattice_printout_ != DensityType::BaryonicIsospin &&
1939  dens_type_lattice_printout_ != DensityType::Baryon) {
1940  jmu_custom_lat_ = make_unique<DensityLattice>(l, n, origin, periodic,
1942  }
1943  } else if (printout_lattice_td_ || printout_full_lattice_any_td_) {
1944  logg[LExperiment].error(
1945  "If you want Therm. VTK or Lattice output, configure a lattice for "
1946  "it.");
1947  } else if (potentials_ && potentials_->use_coulomb()) {
1948  logg[LExperiment].error(
1949  "Coulomb potential requires a lattice. Please add one to the "
1950  "configuration");
1951  }
1952 
1953  // Warning for the mean field calculation if lattice is not on.
1954  if ((potentials_ != nullptr) && (jmu_B_lat_ == nullptr)) {
1955  logg[LExperiment].warn() << "Lattice is NOT used. Mean-field energy is "
1956  << "not going to be calculated.";
1957  }
1958 
1959  // Store pointers to potential and lattice accessible for Action
1960  if (parameters_.potential_affect_threshold) {
1961  UB_lat_pointer = UB_lat_.get();
1962  UI3_lat_pointer = UI3_lat_.get();
1963  pot_pointer = potentials_.get();
1964  }
1965 
1966  // Throw fatal if DerivativesMode == FiniteDifference and lattice is not on.
1967  if ((parameters_.derivatives_mode == DerivativesMode::FiniteDifference) &&
1968  (jmu_B_lat_ == nullptr)) {
1969  throw std::runtime_error(
1970  "Lattice is necessary to calculate finite difference gradients.");
1971  }
1972 
1973  // Create forced thermalizer
1974  if (config.has_value({"Forced_Thermalization"})) {
1975  Configuration &&th_conf = config["Forced_Thermalization"];
1976  thermalizer_ = modus_.create_grandcan_thermalizer(th_conf);
1977  }
1978 
1979  /* Take the seed setting only after the configuration was stored to a file
1980  * in smash.cc */
1981  seed_ = config.take({"General", "Randomseed"});
1982 }
1983 
1985 const std::string hline(113, '-');
1986 
2011 std::string format_measurements(const std::vector<Particles> &ensembles,
2012  uint64_t scatterings_this_interval,
2013  const QuantumNumbers &conserved_initial,
2014  SystemTimePoint time_start, double time,
2015  double E_mean_field,
2016  double E_mean_field_initial);
2031  const Potentials &potentials,
2033  RectangularLattice<std::pair<ThreeVector, ThreeVector>> *em_lattice,
2034  const ExperimentParameters &parameters);
2035 
2052 EventInfo fill_event_info(const std::vector<Particles> &ensembles,
2053  double E_mean_field, double modus_impact_parameter,
2054  const ExperimentParameters &parameters,
2055  bool projectile_target_interact,
2056  bool kinematic_cut_for_SMASH_IC);
2057 
2058 template <typename Modus>
2060  random::set_seed(seed_);
2061  logg[LExperiment].info() << "random number seed: " << seed_;
2062  /* Set seed for the next event. It has to be positive, so it can be entered
2063  * in the config.
2064  *
2065  * We have to be careful about the minimal integer, whose absolute value
2066  * cannot be represented. */
2067  int64_t r = random::advance();
2068  while (r == INT64_MIN) {
2069  r = random::advance();
2070  }
2071  seed_ = std::abs(r);
2072  /* Set the random seed used in PYTHIA hadronization
2073  * to be same with the SMASH one.
2074  * In this way we ensure that the results are reproducible
2075  * for every event if one knows SMASH random seed. */
2076  if (process_string_ptr_ != NULL) {
2077  process_string_ptr_->init_pythia_hadron_rndm();
2078  }
2079 
2080  for (Particles &particles : ensembles_) {
2081  particles.reset();
2082  }
2083 
2084  // Sample particles according to the initial conditions
2085  double start_time = -1.0;
2086 
2087  // Sample impact parameter only once per all ensembles
2088  // It should be the same for all ensembles
2089  if (modus_.is_collider()) {
2090  modus_.sample_impact();
2091  logg[LExperiment].info("Impact parameter = ", modus_.impact_parameter(),
2092  " fm");
2093  }
2094  for (Particles &particles : ensembles_) {
2095  start_time = modus_.initial_conditions(&particles, parameters_);
2096  }
2097  /* For box modus make sure that particles are in the box. In principle, after
2098  * a correct initialization they should be, so this is just playing it safe.
2099  */
2100  for (Particles &particles : ensembles_) {
2101  modus_.impose_boundary_conditions(&particles, outputs_);
2102  }
2103  // Reset the simulation clock
2104  double timestep = delta_time_startup_;
2105 
2106  switch (time_step_mode_) {
2107  case TimeStepMode::Fixed:
2108  break;
2109  case TimeStepMode::None:
2110  timestep = end_time_ - start_time;
2111  // Take care of the box modus + timestepless propagation
2112  const double max_dt = modus_.max_timestep(max_transverse_distance_sqr_);
2113  if (max_dt > 0. && max_dt < timestep) {
2114  timestep = max_dt;
2115  }
2116  break;
2117  }
2118  std::unique_ptr<UniformClock> clock_for_this_event;
2119  if (modus_.is_list() && (timestep < 0.0)) {
2120  throw std::runtime_error(
2121  "Timestep for the given event is negative. \n"
2122  "This might happen if the formation times of the input particles are "
2123  "larger than the specified end time of the simulation.");
2124  }
2125  clock_for_this_event = make_unique<UniformClock>(start_time, timestep);
2126  parameters_.labclock = std::move(clock_for_this_event);
2127 
2128  // Reset the output clock
2129  parameters_.outputclock->reset(start_time, true);
2130  // remove time before starting time in case of custom output times.
2131  parameters_.outputclock->remove_times_in_past(start_time);
2132 
2133  logg[LExperiment].debug(
2134  "Lab clock: t_start = ", parameters_.labclock->current_time(),
2135  ", dt = ", parameters_.labclock->timestep_duration());
2136 
2137  /* Save the initial conserved quantum numbers and total momentum in
2138  * the system for conservation checks */
2139  conserved_initial_ = QuantumNumbers(ensembles_);
2140  wall_actions_total_ = 0;
2141  previous_wall_actions_total_ = 0;
2142  interactions_total_ = 0;
2143  previous_interactions_total_ = 0;
2144  discarded_interactions_total_ = 0;
2145  total_pauli_blocked_ = 0;
2146  projectile_target_interact_.assign(parameters_.n_ensembles, false);
2147  total_hypersurface_crossing_actions_ = 0;
2148  total_energy_removed_ = 0.0;
2149  // Print output headers
2150  logg[LExperiment].info() << hline;
2151  logg[LExperiment].info() << "Time[fm] Ekin[GeV] E_MF[GeV] ETotal[GeV] "
2152  << "ETot/N[GeV] D(ETot/N)[GeV] Scatt&Decays "
2153  << "Particles Comp.Time";
2154  logg[LExperiment].info() << hline;
2155  double E_mean_field = 0.0;
2156  if (potentials_) {
2157  // update_potentials();
2158  // if (parameters.outputclock->current_time() == 0.0 )
2159  // using the lattice is necessary
2160  if ((jmu_B_lat_ != nullptr)) {
2161  update_lattice(jmu_B_lat_.get(), old_jmu_auxiliary_.get(),
2162  new_jmu_auxiliary_.get(), four_gradient_auxiliary_.get(),
2164  density_param_, ensembles_,
2165  parameters_.labclock->timestep_duration(), true);
2166  // Because there was no lattice at t=-Delta_t, the time derivatives
2167  // drho_dt and dj^mu/dt at t=0 are huge, while they shouldn't be; we
2168  // overwrite the time derivative to zero by hand.
2169  for (auto &node : *jmu_B_lat_) {
2170  node.overwrite_drho_dt_to_zero();
2171  node.overwrite_djmu_dt_to_zero();
2172  }
2173  E_mean_field = calculate_mean_field_energy(*potentials_, *jmu_B_lat_,
2174  EM_lat_.get(), parameters_);
2175  }
2176  }
2177  initial_mean_field_energy_ = E_mean_field;
2179  ensembles_, 0u, conserved_initial_, time_start_,
2180  parameters_.labclock->current_time(), E_mean_field,
2181  initial_mean_field_energy_);
2182 
2183  // Output at event start
2184  for (const auto &output : outputs_) {
2185  for (int i_ens = 0; i_ens < parameters_.n_ensembles; i_ens++) {
2186  auto event_info = fill_event_info(
2187  ensembles_, E_mean_field, modus_.impact_parameter(), parameters_,
2188  projectile_target_interact_[i_ens], kinematic_cuts_for_IC_output_);
2189  output->at_eventstart(ensembles_[i_ens],
2190  // Pretend each ensemble is an independent event
2191  event_ * parameters_.n_ensembles + i_ens,
2192  event_info);
2193  }
2194  // For thermodynamic output
2195  output->at_eventstart(ensembles_, event_);
2196  // For thermodynamic lattice output
2197  if (printout_full_lattice_any_td_) {
2198  switch (dens_type_lattice_printout_) {
2199  case DensityType::Baryon:
2200  output->at_eventstart(event_, ThermodynamicQuantity::EckartDensity,
2201  DensityType::Baryon, *jmu_B_lat_);
2202  break;
2204  output->at_eventstart(event_, ThermodynamicQuantity::EckartDensity,
2205  DensityType::BaryonicIsospin, *jmu_I3_lat_);
2206  break;
2207  case DensityType::None:
2208  break;
2209  default:
2210  output->at_eventstart(event_, ThermodynamicQuantity::EckartDensity,
2211  DensityType::BaryonicIsospin, *jmu_custom_lat_);
2212  }
2213  if (printout_tmn_) {
2214  output->at_eventstart(event_, ThermodynamicQuantity::Tmn,
2215  dens_type_lattice_printout_, *Tmn_);
2216  }
2217  if (printout_tmn_landau_) {
2218  output->at_eventstart(event_, ThermodynamicQuantity::TmnLandau,
2219  dens_type_lattice_printout_, *Tmn_);
2220  }
2221  if (printout_v_landau_) {
2222  output->at_eventstart(event_, ThermodynamicQuantity::LandauVelocity,
2223  dens_type_lattice_printout_, *Tmn_);
2224  }
2225  if (printout_j_QBS_) {
2226  output->at_eventstart(event_, ThermodynamicQuantity::j_QBS,
2227  dens_type_lattice_printout_, *j_QBS_lat_);
2228  }
2229  }
2230  }
2231 
2232  /* In the ColliderModus, if Fermi motion is frozen, assign the beam momenta
2233  * to the nucleons in both the projectile and the target. Every ensemble
2234  * gets the same beam momenta, so no need to create beam_momenta_ vector
2235  * for every ensemble.
2236  */
2237  if (modus_.is_collider() && modus_.fermi_motion() == FermiMotion::Frozen) {
2238  for (ParticleData &particle : ensembles_[0]) {
2239  const double m = particle.effective_mass();
2240  double v_beam = 0.0;
2241  if (particle.belongs_to() == BelongsTo::Projectile) {
2242  v_beam = modus_.velocity_projectile();
2243  } else if (particle.belongs_to() == BelongsTo::Target) {
2244  v_beam = modus_.velocity_target();
2245  }
2246  const double gamma = 1.0 / std::sqrt(1.0 - v_beam * v_beam);
2247  beam_momentum_.emplace_back(
2248  FourVector(gamma * m, 0.0, 0.0, gamma * v_beam * m));
2249  } // loop over particles
2250  }
2251 }
2252 
2253 template <typename Modus>
2254 bool Experiment<Modus>::perform_action(Action &action, int i_ensemble,
2255  bool include_pauli_blocking) {
2256  Particles &particles = ensembles_[i_ensemble];
2257  // Make sure to skip invalid and Pauli-blocked actions.
2258  if (!action.is_valid(particles)) {
2259  discarded_interactions_total_++;
2260  logg[LExperiment].debug(~einhard::DRed(), "✘ ", action,
2261  " (discarded: invalid)");
2262  return false;
2263  }
2264  action.generate_final_state();
2265  logg[LExperiment].debug("Process Type is: ", action.get_type());
2266  if (include_pauli_blocking && pauli_blocker_ &&
2267  action.is_pauli_blocked(ensembles_, *pauli_blocker_)) {
2268  total_pauli_blocked_++;
2269  return false;
2270  }
2271 
2272  // Prepare projectile_target_interact_, it's used for output
2273  // to signal that there was some interaction in this event
2274  if (modus_.is_collider()) {
2275  int count_target = 0, count_projectile = 0;
2276  for (const auto &incoming : action.incoming_particles()) {
2277  if (incoming.belongs_to() == BelongsTo::Projectile) {
2278  count_projectile++;
2279  } else if (incoming.belongs_to() == BelongsTo::Target) {
2280  count_target++;
2281  }
2282  }
2283  if (count_target > 0 && count_projectile > 0) {
2284  projectile_target_interact_[i_ensemble] = true;
2285  }
2286  }
2287 
2288  /* Make sure to pick a non-zero integer, because 0 is reserved for "no
2289  * interaction yet". */
2290  const auto id_process = static_cast<uint32_t>(interactions_total_ + 1);
2291  action.perform(&particles, id_process);
2292  interactions_total_++;
2293  if (action.get_type() == ProcessType::Wall) {
2294  wall_actions_total_++;
2295  }
2296  if (action.get_type() == ProcessType::HyperSurfaceCrossing) {
2297  total_hypersurface_crossing_actions_++;
2298  total_energy_removed_ += action.incoming_particles()[0].momentum().x0();
2299  }
2300  // Calculate Eckart rest frame density at the interaction point
2301  double rho = 0.0;
2302  if (dens_type_ != DensityType::None) {
2303  const FourVector r_interaction = action.get_interaction_point();
2304  constexpr bool compute_grad = false;
2305  const bool smearing = true;
2306  // todo(oliiny): it's a rough density estimate from a single ensemble.
2307  // It might actually be appropriate for output. Discuss.
2308  rho = std::get<0>(current_eckart(r_interaction.threevec(), particles,
2309  density_param_, dens_type_, compute_grad,
2310  smearing));
2311  }
2327  for (const auto &output : outputs_) {
2328  if (!output->is_dilepton_output() && !output->is_photon_output()) {
2329  if (output->is_IC_output() &&
2331  output->at_interaction(action, rho);
2332  } else if (!output->is_IC_output()) {
2333  output->at_interaction(action, rho);
2334  }
2335  }
2336  }
2337 
2338  // At every collision photons can be produced.
2339  // Note: We rely here on the lazy evaluation of the arguments to if.
2340  // It may happen that in a wall-crossing-action sqrt_s raises an exception.
2341  // Therefore we first have to check if the incoming particles can undergo
2342  // an em-interaction.
2343  if (photons_switch_ &&
2346  action.sqrt_s(), action.incoming_particles())) {
2347  /* Time in the action constructor is relative to
2348  * current time of incoming */
2349  constexpr double action_time = 0.;
2350  ScatterActionPhoton photon_act(action.incoming_particles(), action_time,
2351  n_fractional_photons_,
2352  action.get_total_weight());
2353 
2364  photon_act.add_dummy_hadronic_process(action.get_total_weight());
2365 
2366  // Now add the actual photon reaction channel.
2367  photon_act.add_single_process();
2368 
2369  photon_act.perform_photons(outputs_);
2370  }
2371 
2372  if (bremsstrahlung_switch_ &&
2374  action.incoming_particles())) {
2375  /* Time in the action constructor is relative to
2376  * current time of incoming */
2377  constexpr double action_time = 0.;
2378 
2379  BremsstrahlungAction brems_act(action.incoming_particles(), action_time,
2380  n_fractional_photons_,
2381  action.get_total_weight());
2382 
2394  brems_act.add_dummy_hadronic_process(action.get_total_weight());
2395 
2396  // Now add the actual bremsstrahlung reaction channel.
2397  brems_act.add_single_process();
2398 
2399  brems_act.perform_bremsstrahlung(outputs_);
2400  }
2401 
2402  logg[LExperiment].debug(~einhard::Green(), "✔ ", action);
2403  return true;
2404 }
2405 
2406 template <typename Modus>
2407 void Experiment<Modus>::run_time_evolution(const double t_end) {
2408  while (parameters_.labclock->current_time() < t_end) {
2409  const double t = parameters_.labclock->current_time();
2410  const double dt =
2411  std::min(parameters_.labclock->timestep_duration(), t_end - t);
2412  logg[LExperiment].debug("Timestepless propagation for next ", dt, " fm/c.");
2413 
2414  // Perform forced thermalization if required
2415  if (thermalizer_ &&
2416  thermalizer_->is_time_to_thermalize(parameters_.labclock)) {
2417  const bool ignore_cells_under_treshold = true;
2418  // Thermodynamics in thermalizer is computed from all ensembles,
2419  // but thermalization actions act on each ensemble independently
2420  thermalizer_->update_thermalizer_lattice(ensembles_, density_param_,
2421  ignore_cells_under_treshold);
2422  const double current_t = parameters_.labclock->current_time();
2423  for (int i_ens = 0; i_ens < parameters_.n_ensembles; i_ens++) {
2424  thermalizer_->thermalize(ensembles_[i_ens], current_t,
2425  parameters_.testparticles);
2426  ThermalizationAction th_act(*thermalizer_, current_t);
2427  if (th_act.any_particles_thermalized()) {
2428  perform_action(th_act, i_ens);
2429  }
2430  }
2431  }
2432 
2433  std::vector<Actions> actions(parameters_.n_ensembles);
2434  for (int i_ens = 0; i_ens < parameters_.n_ensembles; i_ens++) {
2435  actions[i_ens].clear();
2436  if (ensembles_[i_ens].size() > 0 && action_finders_.size() > 0) {
2437  /* (1.a) Create grid. */
2438  const double min_cell_length = compute_min_cell_length(dt);
2439  logg[LExperiment].debug("Creating grid with minimal cell length ",
2440  min_cell_length);
2441  /* For the hyper-surface-crossing actions also unformed particles are
2442  * searched and therefore needed on the grid. */
2443  const bool include_unformed_particles = IC_output_switch_;
2444  const auto &grid =
2445  use_grid_ ? modus_.create_grid(ensembles_[i_ens], min_cell_length,
2446  dt, parameters_.coll_crit,
2447  include_unformed_particles)
2448  : modus_.create_grid(ensembles_[i_ens], min_cell_length,
2449  dt, parameters_.coll_crit,
2450  include_unformed_particles,
2452 
2453  const double gcell_vol = grid.cell_volume();
2454  /* (1.b) Iterate over cells and find actions. */
2455  grid.iterate_cells(
2456  [&](const ParticleList &search_list) {
2457  for (const auto &finder : action_finders_) {
2458  actions[i_ens].insert(finder->find_actions_in_cell(
2459  search_list, dt, gcell_vol, beam_momentum_));
2460  }
2461  },
2462  [&](const ParticleList &search_list,
2463  const ParticleList &neighbors_list) {
2464  for (const auto &finder : action_finders_) {
2465  actions[i_ens].insert(finder->find_actions_with_neighbors(
2466  search_list, neighbors_list, dt, beam_momentum_));
2467  }
2468  });
2469  }
2470  }
2471 
2472  /* \todo (optimizations) Adapt timestep size here */
2473 
2474  /* (2) Propagate from action to action until next output or timestep end */
2475  const double end_timestep_time =
2476  std::min(parameters_.labclock->next_time(), t_end);
2477  while (next_output_time() <= end_timestep_time) {
2478  for (int i_ens = 0; i_ens < parameters_.n_ensembles; i_ens++) {
2479  run_time_evolution_timestepless(actions[i_ens], i_ens,
2480  next_output_time(), t_end);
2481  }
2482  ++(*parameters_.outputclock);
2483 
2484  // Avoid duplication of final output
2485  if (parameters_.outputclock->current_time() < t_end) {
2486  intermediate_output();
2487  }
2488  }
2489  for (int i_ens = 0; i_ens < parameters_.n_ensembles; i_ens++) {
2490  run_time_evolution_timestepless(actions[i_ens], i_ens, end_timestep_time,
2491  t_end);
2492  }
2493 
2494  /* (3) Update potentials (if computed on the lattice) and
2495  * compute new momenta according to equations of motion */
2496  if (potentials_) {
2497  update_potentials();
2498  update_momenta(ensembles_, parameters_.labclock->timestep_duration(),
2499  *potentials_, FB_lat_.get(), FI3_lat_.get(),
2500  EM_lat_.get());
2501  }
2502 
2503  /* (4) Expand universe if non-minkowskian metric; updates
2504  * positions and momenta according to the selected expansion */
2505  if (metric_.mode_ != ExpansionMode::NoExpansion) {
2506  for (Particles &particles : ensembles_) {
2507  expand_space_time(&particles, parameters_, metric_);
2508  }
2509  }
2510 
2511  ++(*parameters_.labclock);
2512 
2513  /* (5) Check conservation laws.
2514  *
2515  * Check conservation of conserved quantities if potentials and string
2516  * fragmentation are off. If potentials are on then momentum is conserved
2517  * only in average. If string fragmentation is on, then energy and
2518  * momentum are only very roughly conserved in high-energy collisions. */
2519  if (!potentials_ && !parameters_.strings_switch &&
2520  metric_.mode_ == ExpansionMode::NoExpansion && !IC_output_switch_) {
2521  std::string err_msg = conserved_initial_.report_deviations(ensembles_);
2522  if (!err_msg.empty()) {
2523  logg[LExperiment].error() << err_msg;
2524  throw std::runtime_error("Violation of conserved quantities!");
2525  }
2526  }
2527  }
2528 
2529  if (pauli_blocker_) {
2530  logg[LExperiment].info(
2531  "Interactions: Pauli-blocked/performed = ", total_pauli_blocked_, "/",
2532  interactions_total_ - wall_actions_total_);
2533  }
2534 }
2535 
2536 template <typename Modus>
2538  Particles &particles) {
2539  const double dt =
2540  propagate_straight_line(&particles, to_time, beam_momentum_);
2541  if (dilepton_finder_ != nullptr) {
2542  for (const auto &output : outputs_) {
2543  dilepton_finder_->shine(particles, output.get(), dt);
2544  }
2545  }
2546 }
2547 
2555 inline void check_interactions_total(uint64_t interactions_total) {
2556  constexpr uint64_t max_uint32 = std::numeric_limits<uint32_t>::max();
2557  if (interactions_total >= max_uint32) {
2558  throw std::runtime_error("Integer overflow in total interaction number!");
2559  }
2560 }
2561 
2562 template <typename Modus>
2564  Actions &actions, int i_ensemble, const double end_time_propagation,
2565  const double end_time_run) {
2566  Particles &particles = ensembles_[i_ensemble];
2567  logg[LExperiment].debug(
2568  "Timestepless propagation: ", "Actions size = ", actions.size(),
2569  ", end time = ", end_time_propagation);
2570 
2571  // iterate over all actions
2572  while (!actions.is_empty()) {
2573  if (actions.earliest_time() > end_time_propagation) {
2574  break;
2575  }
2576  // get next action
2577  ActionPtr act = actions.pop();
2578  if (!act->is_valid(particles)) {
2579  discarded_interactions_total_++;
2580  logg[LExperiment].debug(~einhard::DRed(), "✘ ", act,
2581  " (discarded: invalid)");
2582  continue;
2583  }
2584  logg[LExperiment].debug(~einhard::Green(), "✔ ", act,
2585  ", action time = ", act->time_of_execution());
2586 
2587  /* (1) Propagate to the next action. */
2588  propagate_and_shine(act->time_of_execution(), particles);
2589 
2590  /* (2) Perform action.
2591  *
2592  * Update the positions of the incoming particles, because the information
2593  * in the action object will be outdated as the particles have been
2594  * propagated since the construction of the action. */
2595  act->update_incoming(particles);
2596  const bool performed = perform_action(*act, i_ensemble);
2597 
2598  /* No need to update actions for outgoing particles
2599  * if the action is not performed. */
2600  if (!performed) {
2601  continue;
2602  }
2603 
2604  /* (3) Update actions for newly-produced particles. */
2605 
2606  const double end_time_timestep =
2607  std::min(parameters_.labclock->next_time(), end_time_run);
2608  assert(!(end_time_propagation > end_time_timestep));
2609  // New actions are always search until the end of the current timestep
2610  const double time_left = end_time_timestep - act->time_of_execution();
2611  const ParticleList &outgoing_particles = act->outgoing_particles();
2612  // Grid cell volume set to zero, since there is no grid
2613  const double gcell_vol = 0.0;
2614  for (const auto &finder : action_finders_) {
2615  // Outgoing particles can still decay, cross walls...
2616  actions.insert(finder->find_actions_in_cell(outgoing_particles, time_left,
2617  gcell_vol, beam_momentum_));
2618  // ... and collide with other particles.
2619  actions.insert(finder->find_actions_with_surrounding_particles(
2620  outgoing_particles, particles, time_left, beam_momentum_));
2621  }
2622 
2623  check_interactions_total(interactions_total_);
2624  }
2625 
2626  propagate_and_shine(end_time_propagation, particles);
2627 }
2628 
2629 template <typename Modus>
2631  const uint64_t wall_actions_this_interval =
2632  wall_actions_total_ - previous_wall_actions_total_;
2633  previous_wall_actions_total_ = wall_actions_total_;
2634  const uint64_t interactions_this_interval = interactions_total_ -
2635  previous_interactions_total_ -
2636  wall_actions_this_interval;
2637  previous_interactions_total_ = interactions_total_;
2638  double E_mean_field = 0.0;
2641  double computational_frame_time = 0.0;
2642  if (potentials_) {
2643  // using the lattice is necessary
2644  if ((jmu_B_lat_ != nullptr)) {
2645  E_mean_field = calculate_mean_field_energy(*potentials_, *jmu_B_lat_,
2646  EM_lat_.get(), parameters_);
2647  /*
2648  * Mean field calculated in a box should remain approximately constant if
2649  * the system is in equilibrium, and so deviations from its original value
2650  * may signal a phase transition or other dynamical process. This
2651  * comparison only makes sense in the Box Modus, hence the condition.
2652  */
2653  if (modus_.is_box()) {
2654  double tmp = (E_mean_field - initial_mean_field_energy_) /
2655  (E_mean_field + initial_mean_field_energy_);
2656  /*
2657  * This is displayed when the system evolves away from its initial
2658  * configuration (which is when the total mean field energy in the box
2659  * deviates from its initial value).
2660  */
2661  if (std::abs(tmp) > 0.01) {
2662  logg[LExperiment].info()
2663  << "\n\n\n\t The mean field at t = "
2664  << parameters_.outputclock->current_time()
2665  << " [fm/c] differs from the mean field at t = 0:"
2666  << "\n\t\t initial_mean_field_energy_ = "
2667  << initial_mean_field_energy_ << " [GeV]"
2668  << "\n\t\t abs[(E_MF - E_MF(t=0))/(E_MF + E_MF(t=0))] = "
2669  << std::abs(tmp)
2670  << "\n\t\t E_MF/E_MF(t=0) = "
2671  << E_mean_field / initial_mean_field_energy_ << "\n\n";
2672  }
2673  }
2674  }
2675  }
2676 
2678  ensembles_, interactions_this_interval, conserved_initial_, time_start_,
2679  parameters_.outputclock->current_time(), E_mean_field,
2680  initial_mean_field_energy_);
2681  const LatticeUpdate lat_upd = LatticeUpdate::AtOutput;
2682 
2683  // save evolution data
2684  if (!(modus_.is_box() && parameters_.outputclock->current_time() <
2685  modus_.equilibration_time())) {
2686  for (const auto &output : outputs_) {
2687  if (output->is_dilepton_output() || output->is_photon_output() ||
2688  output->is_IC_output()) {
2689  continue;
2690  }
2691  for (int i_ens = 0; i_ens < parameters_.n_ensembles; i_ens++) {
2692  auto event_info = fill_event_info(
2693  ensembles_, E_mean_field, modus_.impact_parameter(), parameters_,
2694  projectile_target_interact_[i_ens], kinematic_cuts_for_IC_output_);
2695 
2696  output->at_intermediate_time(ensembles_[i_ens], parameters_.outputclock,
2697  density_param_, event_info);
2698  computational_frame_time = event_info.current_time;
2699  }
2700  // For thermodynamic output
2701  output->at_intermediate_time(ensembles_, parameters_.outputclock,
2702  density_param_);
2703 
2704  // Thermodynamic output on the lattice versus time
2705  switch (dens_type_lattice_printout_) {
2706  case DensityType::Baryon:
2707  update_lattice(jmu_B_lat_.get(), lat_upd, DensityType::Baryon,
2708  density_param_, ensembles_, false);
2709  output->thermodynamics_output(ThermodynamicQuantity::EckartDensity,
2710  DensityType::Baryon, *jmu_B_lat_);
2711  output->thermodynamics_lattice_output(*jmu_B_lat_,
2712  computational_frame_time);
2713  break;
2715  update_lattice(jmu_I3_lat_.get(), lat_upd,
2716  DensityType::BaryonicIsospin, density_param_,
2717  ensembles_, false);
2718  output->thermodynamics_output(ThermodynamicQuantity::EckartDensity,
2720  *jmu_I3_lat_);
2721  output->thermodynamics_lattice_output(*jmu_I3_lat_,
2722  computational_frame_time);
2723  break;
2724  case DensityType::None:
2725  break;
2726  default:
2727  update_lattice(jmu_custom_lat_.get(), lat_upd,
2728  dens_type_lattice_printout_, density_param_,
2729  ensembles_, false);
2730  output->thermodynamics_output(ThermodynamicQuantity::EckartDensity,
2731  dens_type_lattice_printout_,
2732  *jmu_custom_lat_);
2733  output->thermodynamics_lattice_output(*jmu_custom_lat_,
2734  computational_frame_time);
2735  }
2736  if (printout_tmn_ || printout_tmn_landau_ || printout_v_landau_) {
2737  update_lattice(Tmn_.get(), lat_upd, dens_type_lattice_printout_,
2738  density_param_, ensembles_, false);
2739  if (printout_tmn_) {
2740  output->thermodynamics_output(ThermodynamicQuantity::Tmn,
2741  dens_type_lattice_printout_, *Tmn_);
2742  output->thermodynamics_lattice_output(
2743  ThermodynamicQuantity::Tmn, *Tmn_, computational_frame_time);
2744  }
2745  if (printout_tmn_landau_) {
2746  output->thermodynamics_output(ThermodynamicQuantity::TmnLandau,
2747  dens_type_lattice_printout_, *Tmn_);
2748  output->thermodynamics_lattice_output(
2750  computational_frame_time);
2751  }
2752  if (printout_v_landau_) {
2753  output->thermodynamics_output(ThermodynamicQuantity::LandauVelocity,
2754  dens_type_lattice_printout_, *Tmn_);
2755  output->thermodynamics_lattice_output(
2757  computational_frame_time);
2758  }
2759  }
2760  if (EM_lat_) {
2761  output->fields_output("Efield", "Bfield", *EM_lat_);
2762  }
2763  if (printout_j_QBS_) {
2764  output->thermodynamics_lattice_output(
2765  *j_QBS_lat_, computational_frame_time, ensembles_, density_param_);
2766  }
2767 
2768  if (thermalizer_) {
2769  output->thermodynamics_output(*thermalizer_);
2770  }
2771  }
2772  }
2773 }
2774 
2775 template <typename Modus>
2777  if (potentials_) {
2778  if (potentials_->use_symmetry() && jmu_I3_lat_ != nullptr) {
2779  update_lattice(jmu_I3_lat_.get(), old_jmu_auxiliary_.get(),
2780  new_jmu_auxiliary_.get(), four_gradient_auxiliary_.get(),
2782  density_param_, ensembles_,
2783  parameters_.labclock->timestep_duration(), true);
2784  }
2785  if ((potentials_->use_skyrme() || potentials_->use_symmetry()) &&
2786  jmu_B_lat_ != nullptr) {
2787  update_lattice(jmu_B_lat_.get(), old_jmu_auxiliary_.get(),
2788  new_jmu_auxiliary_.get(), four_gradient_auxiliary_.get(),
2790  density_param_, ensembles_,
2791  parameters_.labclock->timestep_duration(), true);
2792  const size_t UBlattice_size = UB_lat_->size();
2793  for (size_t i = 0; i < UBlattice_size; i++) {
2794  auto jB = (*jmu_B_lat_)[i];
2795  const FourVector flow_four_velocity_B =
2796  std::abs(jB.rho()) > very_small_double ? jB.jmu_net() / jB.rho()
2797  : FourVector();
2798  double baryon_density = jB.rho();
2799  ThreeVector baryon_grad_j0 = jB.grad_j0();
2800  ThreeVector baryon_dvecj_dt = jB.dvecj_dt();
2801  ThreeVector baryon_curl_vecj = jB.curl_vecj();
2802  if (potentials_->use_skyrme()) {
2803  (*UB_lat_)[i] =
2804  flow_four_velocity_B * potentials_->skyrme_pot(baryon_density);
2805  (*FB_lat_)[i] =
2806  potentials_->skyrme_force(baryon_density, baryon_grad_j0,
2807  baryon_dvecj_dt, baryon_curl_vecj);
2808  }
2809  if (potentials_->use_symmetry() && jmu_I3_lat_ != nullptr) {
2810  auto jI3 = (*jmu_I3_lat_)[i];
2811  const FourVector flow_four_velocity_I3 =
2812  std::abs(jI3.rho()) > very_small_double
2813  ? jI3.jmu_net() / jI3.rho()
2814  : FourVector();
2815  (*UI3_lat_)[i] = flow_four_velocity_I3 *
2816  potentials_->symmetry_pot(jI3.rho(), baryon_density);
2817  (*FI3_lat_)[i] = potentials_->symmetry_force(
2818  jI3.rho(), jI3.grad_j0(), jI3.dvecj_dt(), jI3.curl_vecj(),
2819  baryon_density, baryon_grad_j0, baryon_dvecj_dt,
2820  baryon_curl_vecj);
2821  }
2822  }
2823  }
2824  if (potentials_->use_coulomb()) {
2825  update_lattice(jmu_el_lat_.get(), LatticeUpdate::EveryTimestep,
2826  DensityType::Charge, density_param_, ensembles_, true);
2827  for (size_t i = 0; i < EM_lat_->size(); i++) {
2828  ThreeVector electric_field = {0., 0., 0.};
2829  ThreeVector position = jmu_el_lat_->cell_center(i);
2830  jmu_el_lat_->integrate_volume(electric_field,
2832  potentials_->coulomb_r_cut(), position);
2833  ThreeVector magnetic_field = {0., 0., 0.};
2834  jmu_el_lat_->integrate_volume(magnetic_field,
2836  potentials_->coulomb_r_cut(), position);
2837  (*EM_lat_)[i] = std::make_pair(electric_field, magnetic_field);
2838  }
2839  } // if ((potentials_->use_skyrme() || ...
2840  if (potentials_->use_vdf() && jmu_B_lat_ != nullptr) {
2841  update_lattice(jmu_B_lat_.get(), old_jmu_auxiliary_.get(),
2842  new_jmu_auxiliary_.get(), four_gradient_auxiliary_.get(),
2844  density_param_, ensembles_,
2845  parameters_.labclock->timestep_duration(), true);
2846  if (parameters_.field_derivatives_mode == FieldDerivativesMode::Direct) {
2848  fields_lat_.get(), old_fields_auxiliary_.get(),
2849  new_fields_auxiliary_.get(), fields_four_gradient_auxiliary_.get(),
2850  jmu_B_lat_.get(), LatticeUpdate::EveryTimestep, *potentials_,
2851  parameters_.labclock->timestep_duration());
2852  }
2853  const size_t UBlattice_size = UB_lat_->size();
2854  for (size_t i = 0; i < UBlattice_size; i++) {
2855  auto jB = (*jmu_B_lat_)[i];
2856  (*UB_lat_)[i] = potentials_->vdf_pot(jB.rho(), jB.jmu_net());
2857  switch (parameters_.field_derivatives_mode) {
2859  (*FB_lat_)[i] = potentials_->vdf_force(
2860  jB.rho(), jB.drho_dxnu().x0(), jB.drho_dxnu().threevec(),
2861  jB.grad_rho_cross_vecj(), jB.jmu_net().x0(), jB.grad_j0(),
2862  jB.jmu_net().threevec(), jB.dvecj_dt(), jB.curl_vecj());
2863  break;
2865  auto Amu = (*fields_lat_)[i];
2866  (*FB_lat_)[i] = potentials_->vdf_force(
2867  Amu.grad_A0(), Amu.dvecA_dt(), Amu.curl_vecA());
2868  break;
2869  }
2870  } // for (size_t i = 0; i < UBlattice_size; i++)
2871  } // if potentials_->use_vdf()
2872  }
2873 }
2874 
2875 template <typename Modus>
2877  /* At end of time evolution: Force all resonances to decay. In order to handle
2878  * decay chains, we need to loop until no further actions occur. */
2879  bool actions_performed, decays_found;
2880  uint64_t interactions_old;
2881  do {
2882  decays_found = false;
2883  interactions_old = interactions_total_;
2884  for (int i_ens = 0; i_ens < parameters_.n_ensembles; i_ens++) {
2885  Actions actions;
2886 
2887  // Dileptons: shining of remaining resonances
2888  if (dilepton_finder_ != nullptr) {
2889  for (const auto &output : outputs_) {
2890  dilepton_finder_->shine_final(ensembles_[i_ens], output.get(), true);
2891  }
2892  }
2893  // Find actions.
2894  for (const auto &finder : action_finders_) {
2895  auto found_actions = finder->find_final_actions(ensembles_[i_ens]);
2896  if (!found_actions.empty()) {
2897  actions.insert(std::move(found_actions));
2898  decays_found = true;
2899  }
2900  }
2901  // Perform actions.
2902  while (!actions.is_empty()) {
2903  perform_action(*actions.pop(), i_ens, false);
2904  }
2905  }
2906  actions_performed = interactions_total_ > interactions_old;
2907  // Throw an error if actions were found but not performed
2908  if (decays_found && !actions_performed) {
2909  throw std::runtime_error("Final decays were found but not performed.");
2910  }
2911  // loop until no more decays occur
2912  } while (actions_performed);
2913 
2914  // Dileptons: shining of stable particles at the end
2915  if (dilepton_finder_ != nullptr) {
2916  for (const auto &output : outputs_) {
2917  for (Particles &particles : ensembles_) {
2918  dilepton_finder_->shine_final(particles, output.get(), false);
2919  }
2920  }
2921  }
2922 }
2923 
2924 template <typename Modus>
2926  /* make sure the experiment actually ran (note: we should compare this
2927  * to the start time, but we don't know that. Therefore, we check that
2928  * the time is positive, which should heuristically be the same). */
2929  double E_mean_field = 0.0;
2930  if (likely(parameters_.labclock > 0)) {
2931  const uint64_t wall_actions_this_interval =
2932  wall_actions_total_ - previous_wall_actions_total_;
2933  const uint64_t interactions_this_interval = interactions_total_ -
2934  previous_interactions_total_ -
2935  wall_actions_this_interval;
2936  if (potentials_) {
2937  // using the lattice is necessary
2938  if ((jmu_B_lat_ != nullptr)) {
2939  E_mean_field = calculate_mean_field_energy(*potentials_, *jmu_B_lat_,
2940  EM_lat_.get(), parameters_);
2941  }
2942  }
2944  ensembles_, interactions_this_interval, conserved_initial_, time_start_,
2945  end_time_, E_mean_field, initial_mean_field_energy_);
2946  int total_particles = 0;
2947  for (const Particles &particles : ensembles_) {
2948  total_particles += particles.size();
2949  }
2950  if (IC_output_switch_ && (total_particles == 0)) {
2951  // Verify there is no more energy in the system if all particles were
2952  // removed when crossing the hypersurface
2953  const double remaining_energy =
2954  conserved_initial_.momentum().x0() - total_energy_removed_;
2955  if (remaining_energy > really_small) {
2956  throw std::runtime_error(
2957  "There is remaining energy in the system although all particles "
2958  "were removed.\n"
2959  "E_remain = " +
2960  std::to_string(remaining_energy) + " [GeV]");
2961  } else {
2962  logg[LExperiment].info() << hline;
2963  logg[LExperiment].info()
2964  << "Time real: " << SystemClock::now() - time_start_;
2965  logg[LExperiment].info()
2966  << "Interactions before reaching hypersurface: "
2967  << interactions_total_ - wall_actions_total_ -
2968  total_hypersurface_crossing_actions_;
2969  logg[LExperiment].info()
2970  << "Total number of particles removed on hypersurface: "
2971  << total_hypersurface_crossing_actions_;
2972  }
2973  } else {
2974  const double precent_discarded =
2975  interactions_total_ > 0
2976  ? static_cast<double>(discarded_interactions_total_) * 100.0 /
2977  interactions_total_
2978  : 0.0;
2979  std::stringstream msg_discarded;
2980  msg_discarded
2981  << "Discarded interaction number: " << discarded_interactions_total_
2982  << " (" << precent_discarded
2983  << "% of the total interaction number including wall crossings)";
2984 
2985  logg[LExperiment].info() << hline;
2986  logg[LExperiment].info()
2987  << "Time real: " << SystemClock::now() - time_start_;
2988  logg[LExperiment].debug() << msg_discarded.str();
2989 
2990  if (parameters_.coll_crit == CollisionCriterion::Stochastic &&
2991  precent_discarded > 1.0) {
2992  // The choosen threshold of 1% is a heuristical value
2993  logg[LExperiment].warn()
2994  << msg_discarded.str()
2995  << "\nThe number of discarded interactions is large, which means "
2996  "the assumption for the stochastic criterion of\n1 interaction "
2997  "per particle per timestep is probably violated. Consider "
2998  "reducing the timestep size.";
2999  }
3000 
3001  logg[LExperiment].info() << "Final interaction number: "
3002  << interactions_total_ - wall_actions_total_;
3003  }
3004 
3005  // Check if there are unformed particles
3006  int unformed_particles_count = 0;
3007  for (const Particles &particles : ensembles_) {
3008  for (const ParticleData &particle : particles) {
3009  if (particle.formation_time() > end_time_) {
3010  unformed_particles_count++;
3011  }
3012  }
3013  }
3014  if (unformed_particles_count > 0) {
3015  logg[LExperiment].warn(
3016  "End time might be too small. ", unformed_particles_count,
3017  " unformed particles were found at the end of the evolution.");
3018  }
3019  }
3020 
3021  // Keep track of how many ensembles had interactions
3022  count_nonempty_ensembles();
3023 
3024  for (const auto &output : outputs_) {
3025  for (int i_ens = 0; i_ens < parameters_.n_ensembles; i_ens++) {
3026  auto event_info = fill_event_info(
3027  ensembles_, E_mean_field, modus_.impact_parameter(), parameters_,
3028  projectile_target_interact_[i_ens], kinematic_cuts_for_IC_output_);
3029  output->at_eventend(ensembles_[i_ens],
3030  // Pretend each ensemble is an independent event
3031  event_ * parameters_.n_ensembles + i_ens, event_info);
3032  }
3033  // For thermodynamic output
3034  output->at_eventend(ensembles_, event_);
3035 
3036  // For thermodynamic lattice output
3037  if (dens_type_lattice_printout_ != DensityType::None) {
3038  output->at_eventend(event_, ThermodynamicQuantity::EckartDensity,
3039  dens_type_lattice_printout_);
3040  output->at_eventend(ThermodynamicQuantity::EckartDensity);
3041  }
3042  if (printout_tmn_) {
3043  output->at_eventend(event_, ThermodynamicQuantity::Tmn,
3044  dens_type_lattice_printout_);
3045  output->at_eventend(ThermodynamicQuantity::Tmn);
3046  }
3047  if (printout_tmn_landau_) {
3048  output->at_eventend(event_, ThermodynamicQuantity::TmnLandau,
3049  dens_type_lattice_printout_);
3050  output->at_eventend(ThermodynamicQuantity::TmnLandau);
3051  }
3052  if (printout_v_landau_) {
3053  output->at_eventend(event_, ThermodynamicQuantity::LandauVelocity,
3054  dens_type_lattice_printout_);
3055  output->at_eventend(ThermodynamicQuantity::LandauVelocity);
3056  }
3057  if (printout_j_QBS_) {
3058  output->at_eventend(ThermodynamicQuantity::j_QBS);
3059  }
3060  }
3061 }
3062 
3063 template <typename Modus>
3065  for (bool has_interaction : projectile_target_interact_) {
3066  if (has_interaction) {
3067  nonempty_ensembles_++;
3068  }
3069  }
3070 }
3071 
3072 template <typename Modus>
3074  if (event_counting_ == EventCounting::FixedNumber) {
3075  return event_ >= nevents_;
3076  }
3077  if (event_counting_ == EventCounting::MinimumNonEmpty) {
3078  if (nonempty_ensembles_ >= minimum_nonempty_ensembles_) {
3079  return true;
3080  }
3081  if (event_ >= max_events_) {
3082  logg[LExperiment].warn()
3083  << "Maximum number of events (" << max_events_
3084  << ") exceeded. Stopping calculation. "
3085  << "The fraction of empty ensembles is "
3086  << (1.0 - static_cast<double>(nonempty_ensembles_) /
3087  (event_ * parameters_.n_ensembles))
3088  << ". If this fraction is expected, try increasing the "
3089  "Maximum_Ensembles_Run.";
3090  return true;
3091  }
3092  return false;
3093  }
3094  throw std::runtime_error("Event counting option is invalid");
3095  return false;
3096 }
3097 
3098 template <typename Modus>
3100  const auto &mainlog = logg[LMain];
3101  for (event_ = 0; !is_finished(); event_++) {
3102  mainlog.info() << "Event " << event_;
3103 
3104  // Sample initial particles, start clock, some printout and book-keeping
3105  initialize_new_event();
3106 
3107  run_time_evolution(end_time_);
3108 
3109  if (force_decays_) {
3110  do_final_decays();
3111  }
3112 
3113  // Output at event end
3114  final_output();
3115  }
3116 }
3117 
3118 } // namespace smash
3119 
3120 #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:127
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:186
const bool bremsstrahlung_switch_
This indicates whether bremsstrahlung is switched on.
Definition: experiment.h:601
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:2537
const ExpansionProperties metric_
This struct contains information on the metric to be used.
Definition: experiment.h:592
std::unique_ptr< ActionFinderInterface > photon_finder_
The (Scatter) Actions Finder for Direct Photons.
Definition: experiment.h:412
const bool force_decays_
This indicates whether we force all resonances to decay in the last timestep.
Definition: experiment.h:586
double initial_mean_field_energy_
The initial total mean field energy in the system.
Definition: experiment.h:628
std::vector< std::unique_ptr< ActionFinderInterface > > action_finders_
The Action finder objects.
Definition: experiment.h:406
bool printout_tmn_
Whether to print the energy-momentum tensor.
Definition: experiment.h:491
QuantumNumbers conserved_initial_
The conserved quantities of the system.
Definition: experiment.h:622
DensityParameters density_param_
Structure to precalculate and hold parameters for density computations.
Definition: experiment.h:357
double next_output_time() const
Shortcut for next output time.
Definition: experiment.h:332
bool printout_full_lattice_ascii_td_
Whether to print the thermodynamics quantities evaluated on the lattices, point by point,...
Definition: experiment.h:507
double total_energy_removed_
Total energy removed from the system in hypersurface crossing actions.
Definition: experiment.h:682
DensityType dens_type_lattice_printout_
Type of density for lattice printout.
Definition: experiment.h:442
double max_transverse_distance_sqr_
Maximal distance at which particles can interact in case of the geometric criterion,...
Definition: experiment.h:613
bool printout_j_QBS_
Whether to print the Q, B, S 4-currents.
Definition: experiment.h:500
std::unique_ptr< GrandCanThermalizer > thermalizer_
Instance of class used for forced thermalization.
Definition: experiment.h:518
void count_nonempty_ensembles()
Counts the number of ensembles in wich interactions took place at the end of an event.
Definition: experiment.h:3064
const TimeStepMode time_step_mode_
This indicates whether to use time steps.
Definition: experiment.h:607
std::unique_ptr< DensityLattice > jmu_custom_lat_
Custom density on the lattices.
Definition: experiment.h:439
bool printout_full_lattice_any_td_
Whether to print the thermodynamics quantities evaluated on the lattices, point by point,...
Definition: experiment.h:515
Particles * first_ensemble()
Provides external access to SMASH particles.
Definition: experiment.h:248
int minimum_nonempty_ensembles_
The number of ensembles, in which interactions take place, to be calculated.
Definition: experiment.h:550
void run_time_evolution_timestepless(Actions &actions, int i_ensemble, const double end_time_propagation, const double end_time_run)
Performs all the propagations and actions during a certain time interval neglecting the influence of ...
Definition: experiment.h:2563
std::unique_ptr< DensityLattice > jmu_B_lat_
Baryon density on the lattice.
Definition: experiment.h:421
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:707
DensityType dens_type_
Type of density to be written to collision headers.
Definition: experiment.h:634
const int nevents_
Number of events.
Definition: experiment.h:540
void intermediate_output()
Intermediate output during an event.
Definition: experiment.h:2630
std::vector< FourVector > beam_momentum_
The initial nucleons in the ColliderModus propagate with beam_momentum_, if Fermi motion is frozen.
Definition: experiment.h:403
std::unique_ptr< RectangularLattice< std::pair< ThreeVector, ThreeVector > > > FI3_lat_
Lattices for the electric and magnetic component of the symmetry force.
Definition: experiment.h:465
std::unique_ptr< RectangularLattice< FourVector > > new_jmu_auxiliary_
Auxiliary lattice for values of jmu at a time step t0 + dt.
Definition: experiment.h:477
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:324
void initialize_new_event()
This is called in the beginning of each event.
Definition: experiment.h:2059
bool is_finished()
Checks wether the desired number events have been calculated.
Definition: experiment.h:3073
int n_fractional_photons_
Number of fractional photons produced per single reaction.
Definition: experiment.h:415
const double delta_time_startup_
The clock's timestep size at start up.
Definition: experiment.h:580
std::unique_ptr< RectangularLattice< EnergyMomentumTensor > > Tmn_
Lattices of energy-momentum tensors for printout.
Definition: experiment.h:472
std::vector< Particles > ensembles_
Complete particle list, all ensembles in one vector.
Definition: experiment.h:366
std::unique_ptr< RectangularLattice< FourVector > > new_fields_auxiliary_
Auxiliary lattice for values of Amu at a time step t0 + dt.
Definition: experiment.h:485
SystemTimePoint time_start_
system starting time of the simulation
Definition: experiment.h:631
uint64_t previous_wall_actions_total_
Total number of wall-crossings for previous timestep.
Definition: experiment.h:658
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:461
OutputsList outputs_
A list of output formaters.
Definition: experiment.h:384
int event_
Current event.
Definition: experiment.h:561
std::unique_ptr< RectangularLattice< std::array< FourVector, 4 > > > fields_four_gradient_auxiliary_
Auxiliary lattice for calculating the four-gradient of Amu.
Definition: experiment.h:488
void do_final_decays()
Performs the final decays of an event.
Definition: experiment.h:2876
std::unique_ptr< PauliBlocker > pauli_blocker_
An instance of PauliBlocker class that stores parameters needed for Pauli blocking calculations and c...
Definition: experiment.h:378
bool printout_v_landau_
Whether to print the 4-velocity in Landau frame.
Definition: experiment.h:497
bool perform_action(Action &action, int i_ensemble, bool include_pauli_blocking=true)
Perform the given action.
std::unique_ptr< RectangularLattice< FourVector > > UI3_lat_
Lattices for symmetry potentials (evaluated in the local rest frame) times the isospin flow 4-velocit...
Definition: experiment.h:454
bool printout_lattice_td_
Whether to print the thermodynamics quantities evaluated on the lattices.
Definition: experiment.h:503
std::unique_ptr< RectangularLattice< std::pair< ThreeVector, ThreeVector > > > EM_lat_
Lattices for electric and magnetic field in fm^-2.
Definition: experiment.h:469
std::unique_ptr< FieldsLattice > fields_lat_
Mean-field A^mu on the lattice.
Definition: experiment.h:430
int max_events_
Maximum number of events to be calculated in order obtain the desired number of non-empty events usin...
Definition: experiment.h:570
int nonempty_ensembles_
Number of ensembles containing an interaction.
Definition: experiment.h:564
OutputPtr photon_output_
The Photon output.
Definition: experiment.h:390
EventCounting event_counting_
The way in which the number of calculated events is specified.
Definition: experiment.h:558
const bool dileptons_switch_
This indicates whether dileptons are switched on.
Definition: experiment.h:595
Modus modus_
Instance of the Modus template parameter.
Definition: experiment.h:363
std::unique_ptr< RectangularLattice< FourVector > > old_jmu_auxiliary_
Auxiliary lattice for values of jmu at a time step t0.
Definition: experiment.h:475
std::unique_ptr< DecayActionsFinderDilepton > dilepton_finder_
The Dilepton Action Finder.
Definition: experiment.h:409
std::unique_ptr< DensityLattice > j_QBS_lat_
4-current for j_QBS lattice output
Definition: experiment.h:418
bool printout_tmn_landau_
Whether to print the energy-momentum tensor in Landau frame.
Definition: experiment.h:494
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:511
std::unique_ptr< RectangularLattice< std::array< FourVector, 4 > > > four_gradient_auxiliary_
Auxiliary lattice for calculating the four-gradient of jmu.
Definition: experiment.h:480
const bool photons_switch_
This indicates whether photons are switched on.
Definition: experiment.h:598
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:524
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:448
ExperimentParameters parameters_
Struct of several member variables.
Definition: experiment.h:354
std::unique_ptr< RectangularLattice< FourVector > > old_fields_auxiliary_
Auxiliary lattice for values of Amu at a time step t0.
Definition: experiment.h:483
void run_time_evolution(const double t_end)
Runs the time evolution of an event with fixed-sized time steps or without timesteps,...
Definition: experiment.h:2407
std::unique_ptr< DensityLattice > jmu_el_lat_
Electric charge density on the lattice.
Definition: experiment.h:427
std::unique_ptr< Potentials > potentials_
An instance of potentials class, that stores parameters of potentials, calculates them and their grad...
Definition: experiment.h:372
uint64_t wall_actions_total_
Total number of wall-crossings for current timestep.
Definition: experiment.h:652
uint64_t total_hypersurface_crossing_actions_
Total number of particles removed from the evolution in hypersurface crossing actions.
Definition: experiment.h:670
uint64_t interactions_total_
Total number of interactions for current timestep.
Definition: experiment.h:640
const bool use_grid_
This indicates whether to use the grid.
Definition: experiment.h:589
std::unique_ptr< DensityLattice > jmu_I3_lat_
Isospin projection density on the lattice.
Definition: experiment.h:424
uint64_t total_pauli_blocked_
Total number of Pauli-blockings for current timestep.
Definition: experiment.h:664
void final_output()
Output at the end of an event.
Definition: experiment.h:2925
std::vector< bool > projectile_target_interact_
Whether the projectile and the target collided.
Definition: experiment.h:396
Modus * modus()
Provides external access to SMASH calculation modus.
Definition: experiment.h:256
void update_potentials()
Recompute potentials on lattices if necessary.
Definition: experiment.h:2776
const bool IC_output_switch_
This indicates whether the IC output is enabled.
Definition: experiment.h:604
OutputPtr dilepton_output_
The Dilepton output.
Definition: experiment.h:387
int64_t seed_
random seed for the next event.
Definition: experiment.h:688
std::vector< Particles > * all_ensembles()
Getter for all ensembles.
Definition: experiment.h:250
uint64_t previous_interactions_total_
Total number of interactions for previous timestep.
Definition: experiment.h:646
uint64_t discarded_interactions_total_
Total number of discarded interactions, because they were invalidated before they could be performed.
Definition: experiment.h:676
void run() override
Runs the experiment.
Definition: experiment.h:3099
bool kinematic_cuts_for_IC_output_
This indicates whether kinematic cuts are enabled for the IC output.
Definition: experiment.h:685
const double end_time_
simulation time at which the evolution is stopped.
Definition: experiment.h:573
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.
EventCounting
Defines how the number of events is determined.
#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:532
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
EventInfo fill_event_info(const std::vector< Particles > &ensembles, double E_mean_field, double modus_impact_parameter, const ExperimentParameters &parameters, bool projectile_target_interact, bool kinematic_cut_for_SMASH_IC)
Generate the EventInfo object which is passed to outputs_.
Definition: experiment.cc:968
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:700
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:535
void check_interactions_total(uint64_t interactions_total)
Make sure interactions_total can be represented as a 32-bit integer.
Definition: experiment.h:2555
@ 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)
Calculates Eckart rest frame density and 4-current of a given density type and optionally the gradien...
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:748
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:700
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:494
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
Exception class that is thrown if the requested output path in the Experiment factory is not existing...
Definition: experiment.h:151
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.