7 #ifndef SRC_INCLUDE_SMASH_EXPERIMENT_H_
8 #define SRC_INCLUDE_SMASH_EXPERIMENT_H_
47 #ifdef SMASH_USE_HEPMC
50 #ifdef SMASH_USE_RIVET
75 template <
typename T,
typename Ratio>
77 const chrono::duration<T, Ratio> &seconds) {
78 using Seconds = chrono::duration<double>;
79 using Minutes = chrono::duration<double, std::ratio<60>>;
80 using Hours = chrono::duration<double, std::ratio<60 * 60>>;
81 constexpr Minutes threshold_for_minutes{10};
82 constexpr Hours threshold_for_hours{3};
83 if (seconds < threshold_for_minutes) {
84 return out << Seconds(seconds).count() <<
" [s]";
86 if (seconds < threshold_for_hours) {
87 return out << Minutes(seconds).count() <<
" [min]";
89 return out << Hours(seconds).count() <<
" [h]";
94 static constexpr
int LMain = LogArea::Main::id;
133 static std::unique_ptr<ExperimentBase>
create(
134 Configuration &config,
const std::filesystem::path &output_path);
150 using std::invalid_argument::invalid_argument;
159 using std::invalid_argument::invalid_argument;
163 template <
typename Modus>
165 template <
typename Modus>
192 template <
typename Modus>
219 const std::filesystem::path &output_path);
246 ParticleList &&remove_plist = {});
292 bool include_pauli_blocking =
true);
302 const std::filesystem::path &output_path,
327 const double end_time_propagation);
467 std::unique_ptr<RectangularLattice<FourVector>>
UB_lat_ =
nullptr;
473 std::unique_ptr<RectangularLattice<FourVector>>
UI3_lat_ =
nullptr;
479 std::unique_ptr<RectangularLattice<std::pair<ThreeVector, ThreeVector>>>
483 std::unique_ptr<RectangularLattice<std::pair<ThreeVector, ThreeVector>>>
487 std::unique_ptr<RectangularLattice<std::pair<ThreeVector, ThreeVector>>>
491 std::unique_ptr<RectangularLattice<EnergyMomentumTensor>>
Tmn_;
498 std::unique_ptr<RectangularLattice<std::array<FourVector, 4>>>
506 std::unique_ptr<RectangularLattice<std::array<FourVector, 4>>>
722 friend std::ostream &operator<<<>(std::ostream &out,
const Experiment &e);
726 template <
typename Modus>
728 out <<
"End time: " << e.
end_time_ <<
" fm\n";
733 template <
typename Modus>
735 const std::string &content,
736 const std::filesystem::path &output_path,
739 if (ensembles_.size() > 1) {
740 auto abort_because_of = [](
const std::string &s) {
741 throw std::invalid_argument(
742 s +
" output is not available with multiple parallel ensembles.");
744 if (content ==
"Initial_Conditions") {
745 abort_because_of(
"Initial_Conditions");
747 if ((
format ==
"HepMC") || (
format ==
"HepMC_asciiv3") ||
748 (
format ==
"HepMC_treeroot")) {
749 abort_because_of(
"HepMC");
751 if (content ==
"Rivet") {
752 abort_because_of(
"Rivet");
754 if (content ==
"Collisions") {
756 "Information coming from different ensembles in 'Collisions' output "
757 "is not distinguishable.\nSuch an output with multiple parallel "
758 "ensembles should only be used if later in the data analysis\nit is "
759 "not necessary to trace back which data belongs to which ensemble.");
763 if (
format ==
"VTK" && content ==
"Particles") {
764 outputs_.emplace_back(
765 std::make_unique<VtkOutput>(output_path, content, out_par));
766 }
else if (
format ==
"Root") {
767 #ifdef SMASH_USE_ROOT
768 if (content ==
"Initial_Conditions") {
769 outputs_.emplace_back(
770 std::make_unique<RootOutput>(output_path,
"SMASH_IC", out_par));
772 outputs_.emplace_back(
773 std::make_unique<RootOutput>(output_path, content, out_par));
777 "Root output requested, but Root support not compiled in");
779 }
else if ((
format ==
"Binary" ||
format ==
"Oscar2013_bin") &&
780 (content ==
"Collisions" || content ==
"Particles" ||
781 content ==
"Dileptons" || content ==
"Photons" ||
782 content ==
"Initial_Conditions")) {
783 outputs_.emplace_back(
785 }
else if (
format ==
"Oscar1999" ||
format ==
"Oscar2013") {
786 outputs_.emplace_back(
788 }
else if (
format ==
"ASCII" &&
789 (content ==
"Particles" || content ==
"Collisions" ||
790 content ==
"Dileptons" || content ==
"Photons" ||
791 content ==
"Initial_Conditions")) {
792 outputs_.emplace_back(
794 }
else if (content ==
"Thermodynamics" &&
format ==
"ASCII") {
795 outputs_.emplace_back(
796 std::make_unique<ThermodynamicOutput>(output_path, content, out_par));
797 }
else if (content ==
"Thermodynamics" &&
798 (
format ==
"Lattice_ASCII" ||
format ==
"Lattice_Binary")) {
799 printout_full_lattice_any_td_ =
true;
800 outputs_.emplace_back(std::make_unique<ThermodynamicLatticeOutput>(
801 output_path, content, out_par,
format ==
"Lattice_ASCII",
802 format ==
"Lattice_Binary"));
803 }
else if (content ==
"Thermodynamics" &&
format ==
"VTK") {
804 printout_lattice_td_ =
true;
805 outputs_.emplace_back(
806 std::make_unique<VtkOutput>(output_path, content, out_par));
807 }
else if (content ==
"Initial_Conditions" &&
format ==
"For_vHLLE") {
809 throw std::invalid_argument(
810 "Dynamic initial conditions are only available in Oscar2013 and "
813 outputs_.emplace_back(
814 std::make_unique<ICOutput>(output_path,
"SMASH_IC_For_vHLLE", out_par));
815 }
else if ((
format ==
"HepMC") || (
format ==
"HepMC_asciiv3") ||
816 (
format ==
"HepMC_treeroot")) {
817 #ifdef SMASH_USE_HEPMC
818 if (content ==
"Particles") {
819 if ((
format ==
"HepMC") || (
format ==
"HepMC_asciiv3")) {
820 outputs_.emplace_back(std::make_unique<HepMcOutput>(
821 output_path,
"SMASH_HepMC_particles",
false,
"asciiv3"));
822 }
else if (
format ==
"HepMC_treeroot") {
823 #ifdef SMASH_USE_HEPMC_ROOTIO
824 outputs_.emplace_back(std::make_unique<HepMcOutput>(
825 output_path,
"SMASH_HepMC_particles",
false,
"root"));
828 "Requested HepMC_treeroot output not available, "
829 "ROOT or HepMC3_ROOTIO missing or not found by cmake.");
832 }
else if (content ==
"Collisions") {
833 if ((
format ==
"HepMC") || (
format ==
"HepMC_asciiv3")) {
834 outputs_.emplace_back(std::make_unique<HepMcOutput>(
835 output_path,
"SMASH_HepMC_collisions",
true,
"asciiv3"));
836 }
else if (
format ==
"HepMC_treeroot") {
837 #ifdef SMASH_USE_HEPMC_ROOTIO
838 outputs_.emplace_back(std::make_unique<HepMcOutput>(
839 output_path,
"SMASH_HepMC_collisions",
true,
"root"));
842 "Requested HepMC_treeroot output not available, "
843 "ROOT or HepMC3_ROOTIO missing or not found by cmake.");
848 "HepMC only available for Particles and "
849 "Collisions content. Requested for " +
854 "HepMC output requested, but HepMC support not compiled in");
856 }
else if (content ==
"Coulomb" &&
format ==
"VTK") {
857 printout_coulomb_vtk_ =
true;
858 outputs_.emplace_back(
859 std::make_unique<VtkOutput>(output_path,
"Fields", out_par));
860 }
else if (content ==
"Rivet") {
861 #ifdef SMASH_USE_RIVET
863 static bool rivet_format_already_selected =
false;
865 if (rivet_format_already_selected) {
867 "Rivet output format can only be one, either YODA or YODA-full. "
868 "Only your first valid choice will be used.");
872 outputs_.emplace_back(std::make_unique<RivetOutput>(
874 rivet_format_already_selected =
true;
875 }
else if (
format ==
"YODA-full") {
876 outputs_.emplace_back(std::make_unique<RivetOutput>(
878 rivet_format_already_selected =
true;
881 "not one of YODA or YODA-full");
885 "Rivet output requested, but Rivet support not compiled in");
889 <<
"Unknown combination of format (" <<
format <<
") and content ("
890 << content <<
"). Fix the config.";
893 logg[
LExperiment].info() <<
"Added output " << content <<
" of format "
906 template <
typename Modus>
908 const std::filesystem::path &output_path)
911 modus_(std::invoke([&]() {
921 const bool restore_key = config.
has_value(key);
922 const bool temporary_taken_key = config.
take(key);
926 config.
set_value(key, temporary_taken_key);
928 return Modus{std::move(modus_config),
parameters_};
930 ensembles_(parameters_.n_ensembles),
932 delta_time_startup_(parameters_.labclock->timestep_duration()),
937 dileptons_bremsstrahlung_switch_(
941 photons_bremsstrahlung_switch_(
944 modus_.is_IC_for_hybrid()),
945 IC_dynamic_(IC_switch_ ? (modus_.IC_parameters().type ==
952 const bool user_wants_min_nonempty =
954 if (user_wants_nevents == user_wants_min_nonempty) {
955 throw std::invalid_argument(
956 "Please specify either Nevents or Minimum_Nonempty_Ensembles.");
958 if (user_wants_nevents) {
963 minimum_nonempty_ensembles_ =
967 max_events_ = numeric_cast<int>(std::ceil(
968 static_cast<double>(max_ensembles) / parameters_.n_ensembles));
974 throw std::invalid_argument(
975 "Covariant Gaussian derivatives only make sense for Covariant Gaussian "
981 throw std::invalid_argument(
982 "The stochastic criterion can only be employed for fixed time step "
983 "mode and with a grid!");
987 throw std::invalid_argument(
988 "The box modus can only be used with the fixed time step mode!");
992 " testparticles per particle.");
994 " parallel ensembles.");
999 "To preserve detailed balance in a box simulation, it is recommended\n"
1000 "to use the bottom-up strategy for evaluating total cross sections.\n"
1001 "Consider adding the following line to the 'Collision_Term' section "
1002 "in your configuration file:\n"
1003 " Total_Cross_Section_Strategy: \"BottomUp\"");
1008 "To preserve detailed balance in a box simulation, it is recommended "
1009 "to not include the pseudoresonances,\nas they artificially increase "
1010 "the resonance production without changing the corresponding "
1011 "decay.\nConsider adding the following line to the 'Collision_Term' "
1012 "section in your configuration file:\n Pseudoresonance: \"None\"");
1016 if (IC_output != modus_.is_IC_for_hybrid()) {
1017 throw std::invalid_argument(
1018 "The 'Initial_Conditions' subsection must be present in both 'Output' "
1019 "and 'Modi: Collider' sections.");
1032 modus_.sqrt_s_NN() >= 200. ? -1. : 1.);
1035 if (dileptons_switch_) {
1036 dilepton_finder_ = std::make_unique<DecayActionsFinderDilepton>();
1038 if (photons_switch_ || photons_bremsstrahlung_switch_) {
1039 n_fractional_photons_ =
1042 if (parameters_.two_to_one) {
1045 "Resonance lifetime set to zero. Make sure resonances cannot "
1046 "interact inelastically (e.g. resonance chains), else SMASH is known "
1049 action_finders_.emplace_back(
1050 std::make_unique<DecayActionsFinder>(parameters_));
1053 if ((parameters_.two_to_one || parameters_.included_2to2.any() ||
1054 parameters_.included_multi.any() || parameters_.strings_switch) &&
1057 std::make_unique<ScatterActionsFinder>(config, parameters_);
1058 max_transverse_distance_sqr_ =
1059 scat_finder->max_transverse_distance_sqr(parameters_.testparticles);
1060 process_string_ptr_ = scat_finder->get_process_string_ptr();
1071 if (modus_.is_collider() && process_string_ptr_) {
1072 process_string_ptr_->set_mpi_initialization_sqrts(modus_.sqrt_s_NN() *
1075 action_finders_.emplace_back(std::move(scat_finder));
1077 max_transverse_distance_sqr_ =
1078 parameters_.maximum_cross_section / M_PI *
fm2_mb;
1079 process_string_ptr_ = NULL;
1081 if (modus_.is_box()) {
1082 action_finders_.emplace_back(
1083 std::make_unique<WallCrossActionsFinder>(parameters_.box_length));
1087 const InitialConditionParameters &IC_parameters = modus_.IC_parameters();
1090 action_finders_.emplace_back(std::make_unique<DynamicFluidizationFinder>(
1091 modus_.fluid_lattice(), modus_.fluid_background(), IC_parameters));
1094 double rapidity_cut = IC_parameters.rapidity_cut.value();
1096 if (modus_.calculation_frame_is_fixed_target() && rapidity_cut != 0.0) {
1097 throw std::runtime_error(
1098 "Rapidity cut for initial conditions output is not implemented "
1099 "in the fixed target calculation frame. \nPlease use "
1100 "\"center of velocity\" or \"center of mass\" as a "
1101 "\"Calculation_Frame\" instead.");
1104 double pT_cut = IC_parameters.pT_cut.value();
1105 if (rapidity_cut > 0.0 || pT_cut > 0.0) {
1106 kinematic_cuts_for_IC_output_ =
true;
1109 const double proper_time = std::invoke([&]() {
1110 if (IC_parameters.proper_time.has_value()) {
1111 return IC_parameters.proper_time.value();
1114 const double scaling = IC_parameters.proper_time_scaling.value();
1116 const double lower_bound =
1117 IC_parameters.lower_bound.value() * scaling;
1119 const double default_proper_time =
1120 modus_.nuclei_passing_time() * scaling;
1121 if (default_proper_time >= lower_bound) {
1122 logg[LInitialConditions].info()
1123 <<
"Nuclei passing time is " << default_proper_time <<
" fm.";
1124 return default_proper_time;
1126 logg[LInitialConditions].warn()
1127 <<
"Nuclei passing time is too short, hypersurface proper time "
1128 <<
"set to tau = " << lower_bound <<
" fm.";
1134 action_finders_.emplace_back(
1135 std::make_unique<HyperSurfaceCrossActionsFinder>(
1136 proper_time, rapidity_cut, pT_cut));
1142 pauli_blocker_ = std::make_unique<PauliBlocker>(
1452 " create OutputInterface objects");
1455 <<
"Density type printed to headers: " << dens_type_;
1466 if (output_path ==
"") {
1467 throw std::invalid_argument(
1468 "Invalid empty output path provided to Experiment constructor.");
1469 }
else if (!std::filesystem::exists(output_path)) {
1471 "Output path \"" + output_path.string() +
1472 "\" used to create an Experiment object does not exist.");
1473 throw NonExistingOutputPathRequest(
"Attempt to use not existing path.");
1474 }
else if (!std::filesystem::is_directory(output_path)) {
1476 "\" used to create an Experiment object "
1477 "exists, but it is not a directory.");
1478 throw std::logic_error(
"Attempt to use invalid existing path.");
1480 const std::vector<std::string> output_contents =
1481 output_conf.list_upmost_nodes();
1482 if (output_conf.is_empty()) {
1483 logg[
LExperiment].warn() <<
"No \"Output\" section found in the input "
1484 "file. No output file will be produced.";
1488 auto abort_because_of_invalid_input_file = []() {
1489 throw std::invalid_argument(
"Invalid configuration input file.");
1491 std::vector<std::vector<std::string>> list_of_formats(output_contents.size());
1493 output_contents.cbegin(), output_contents.cend(), list_of_formats.begin(),
1494 [&output_conf, &abort_because_of_invalid_input_file](
1495 const std::string &content) -> std::vector<std::string> {
1501 return output_conf.take(InputKeys::get_output_format_key(content));
1502 } catch (
const Configuration::RequiredKeyMissing &) {
1504 << std::quoted(content) <<
" content.";
1505 abort_because_of_invalid_input_file();
1512 const OutputParameters output_parameters(std::move(output_conf));
1513 for (std::size_t i = 0; i < output_contents.size(); ++i) {
1514 if (output_contents[i] ==
"Particles" ||
1515 output_contents[i] ==
"Collisions" ||
1516 output_contents[i] ==
"Dileptons" || output_contents[i] ==
"Photons" ||
1517 output_contents[i] ==
"Initial_Conditions") {
1518 assert(output_parameters.quantities.count(output_contents[i]) > 0);
1519 const bool quantities_given_nonempty =
1520 !output_parameters.quantities.at(output_contents[i]).empty();
1521 auto formats_contains = [&list_of_formats, &i](
const std::string &label) {
1522 return std::find(list_of_formats[i].begin(), list_of_formats[i].end(),
1523 label) != list_of_formats[i].end();
1525 const bool custom_ascii_requested = formats_contains(
"ASCII");
1526 const bool custom_binary_requested = formats_contains(
"Binary");
1527 const bool custom_requested =
1528 custom_ascii_requested || custom_binary_requested;
1529 const bool oscar2013_requested = formats_contains(
"Oscar2013");
1530 const bool oscar2013_bin_requested = formats_contains(
"Oscar2013_bin");
1531 const bool is_extended = (output_contents[i] ==
"Particles")
1532 ? output_parameters.part_extended
1533 : output_parameters.coll_extended;
1534 const auto &default_quantities =
1535 (is_extended) ? OutputDefaultQuantities::oscar2013extended
1536 : OutputDefaultQuantities::oscar2013;
1537 const bool are_given_quantities_oscar2013_ones =
1538 output_parameters.quantities.at(output_contents[i]) ==
1540 if (quantities_given_nonempty != custom_requested) {
1542 <<
"Non-empty \"Quantities\" and \"ASCII\"/\"Binary\" format have "
1543 <<
"not been specified both for " << std::quoted(output_contents[i])
1544 <<
" in config file.";
1545 abort_because_of_invalid_input_file();
1547 if (custom_ascii_requested && oscar2013_requested &&
1548 are_given_quantities_oscar2013_ones) {
1550 <<
"The specified \"Quantities\" for the ASCII format are the same "
1551 "as those of the requested \"Oscar2013\"\nformat for "
1552 << std::quoted(output_contents[i])
1553 <<
" and this would produce the same output file twice.";
1554 abort_because_of_invalid_input_file();
1556 if (custom_binary_requested && oscar2013_bin_requested &&
1557 are_given_quantities_oscar2013_ones) {
1559 <<
"The specified \"Quantities\" for the binary format are the "
1560 "same as those of the requested \"Oscar2013_bin\"\nformat for "
1561 << std::quoted(output_contents[i])
1562 <<
" and this would produce the same output file twice.";
1563 abort_because_of_invalid_input_file();
1567 if (std::find(list_of_formats[i].begin(), list_of_formats[i].end(),
1568 "None") != list_of_formats[i].end()) {
1569 if (list_of_formats[i].size() > 1) {
1571 <<
"Use of \"None\" output format together with other formats is "
1572 "not allowed.\nInvalid \"Format\" key for "
1573 << std::quoted(output_contents[i]) <<
" content.";
1574 abort_because_of_invalid_input_file();
1577 list_of_formats[i].clear();
1579 }
else if (std::set<std::string> tmp_set(list_of_formats[i].begin(),
1580 list_of_formats[i].end());
1581 list_of_formats[i].size() != tmp_set.size()) {
1582 const std::string old_formats =
join(list_of_formats[i],
", "),
1583 new_formats =
join(tmp_set,
", ");
1585 <<
"Found the same output format multiple times for "
1586 << std::quoted(output_contents[i])
1587 <<
" content. Duplicates will be ignored:\n 'Format: [" << old_formats
1588 <<
"] -> [" << new_formats <<
"]'";
1589 list_of_formats[i].assign(tmp_set.begin(), tmp_set.end());
1595 std::size_t total_number_of_requested_formats = 0;
1596 for (std::size_t i = 0; i < output_contents.size(); ++i) {
1597 for (
const auto &
format : list_of_formats[i]) {
1598 create_output(
format, output_contents[i], output_path, output_parameters);
1599 ++total_number_of_requested_formats;
1603 if (outputs_.size() != total_number_of_requested_formats) {
1605 <<
"At least one invalid output format has been provided.";
1606 abort_because_of_invalid_input_file();
1617 throw std::invalid_argument(
"Can't use potentials without time steps!");
1621 <<
"Potentials don't work with frozen Fermi momenta! "
1622 "Use normal Fermi motion instead.";
1623 throw std::invalid_argument(
1624 "Can't use potentials "
1625 "with frozen Fermi momenta!");
1628 << parameters_.labclock->timestep_duration();
1630 potentials_ = std::make_unique<Potentials>(
1635 if (potentials_->use_skyrme() && potentials_->use_vdf()) {
1636 throw std::runtime_error(
1637 "Can't use Skyrme and VDF potentials at the same time!");
1639 if (potentials_->use_symmetry() && potentials_->use_vdf()) {
1640 throw std::runtime_error(
1641 "Can't use symmetry and VDF potentials at the same time!");
1643 if (potentials_->use_skyrme()) {
1646 <<
"\t\tSkyrme_A [MeV] = " << potentials_->skyrme_a() <<
"\n";
1648 <<
"\t\tSkyrme_B [MeV] = " << potentials_->skyrme_b() <<
"\n";
1650 <<
"\t\t Skyrme_tau = " << potentials_->skyrme_tau() <<
"\n";
1652 if (potentials_->use_symmetry()) {
1654 <<
"Symmetry potential is:"
1655 <<
"\n S_pot [MeV] = " << potentials_->symmetry_S_pot() <<
"\n";
1657 if (potentials_->use_vdf()) {
1660 << potentials_->saturation_density() <<
"\n";
1661 for (
int i = 0; i < potentials_->number_of_terms(); i++) {
1663 <<
"\t\tCoefficient_" << i + 1 <<
" = "
1664 << 1000.0 * (potentials_->coeffs())[i] <<
" [MeV] \t Power_"
1665 << i + 1 <<
" = " << (potentials_->powers())[i] <<
"\n";
1671 throw std::invalid_argument(
1672 "Derivatives are necessary for running with potentials.\n"
1673 "Derivatives_Mode: \"Off\" only makes sense for "
1674 "Field_Derivatives_Mode: \"Direct\"!\nUse \"Covariant Gaussian\" or "
1675 "\"Finite difference\".");
1683 switch (parameters_.derivatives_mode) {
1694 switch (parameters_.rho_derivatives_mode) {
1703 if (potentials_->use_vdf()) {
1704 switch (parameters_.field_derivatives_mode) {
1717 if (potentials_->use_vdf() && (parameters_.rho_derivatives_mode ==
1719 parameters_.field_derivatives_mode ==
1721 throw std::runtime_error(
1722 "Can't use VDF potentials without rest frame density derivatives or "
1723 "direct field derivatives!");
1728 throw std::runtime_error(
1729 "Can't use potentials without gradients of baryon current (Skyrme, "
1731 " or direct field derivatives (VDF)!");
1734 if (!(potentials_->use_vdf()) &&
1736 throw std::invalid_argument(
1737 "Field_Derivatives_Mode: \"Direct\" only makes sense for the VDF "
1738 "potentials!\nUse Field_Derivatives_Mode: \"Chain Rule\" or comment "
1739 "this option out (Chain Rule is default)");
1744 switch (parameters_.smearing_mode) {
1750 << parameters_.discrete_weight;
1754 << parameters_.triangular_range;
1761 const bool automatic = config.
take(InputKeys::lattice_automatic);
1762 const bool all_geometrical_properties_specified =
1763 config.
has_value(InputKeys::lattice_cellNumber) &&
1764 config.
has_value(InputKeys::lattice_origin) &&
1765 config.
has_value(InputKeys::lattice_sizes);
1766 if (!automatic && !all_geometrical_properties_specified) {
1767 throw std::invalid_argument(
1768 "The lattice was requested to be manually generated, but some\n"
1769 "lattice geometrical property was not specified. Be sure to provide\n"
1770 "both \"Cell_Number\" and \"Origin\" and \"Sizes\".");
1772 if (automatic && all_geometrical_properties_specified) {
1773 throw std::invalid_argument(
1774 "The lattice was requested to be automatically generated, but all\n"
1775 "lattice geometrical properties were specified. In this case you\n"
1776 "need to set \"Automatic: False\".");
1778 const bool periodic =
1779 config.
take(InputKeys::lattice_periodic, modus_.is_box());
1780 const auto [l,
n, origin] = [&config, automatic,
this]() {
1782 return std::make_tuple<std::array<double, 3>, std::array<int, 3>,
1783 std::array<double, 3>>(
1784 config.
take(InputKeys::lattice_sizes),
1785 config.
take(InputKeys::lattice_cellNumber),
1786 config.
take(InputKeys::lattice_origin));
1788 std::array<double, 3> l_default{20., 20., 20.};
1789 std::array<int, 3> n_default{10, 10, 10};
1790 std::array<double, 3> origin_default{-20., -20., -20.};
1791 if (modus_.is_list() && !modus_.is_box()) {
1793 "The lattice in List modus should be manually specified.");
1794 throw std::invalid_argument(
"Invalid Lattice setup.");
1795 }
else if (modus_.is_collider()) {
1798 const double gamma = modus_.sqrt_s_NN() / (2.0 *
nucleon_mass);
1799 const double max_z = 5.0 / gamma + end_time_;
1800 const double estimated_max_transverse_velocity = 0.7;
1801 const double max_xy =
1802 5.0 + estimated_max_transverse_velocity * end_time_;
1803 origin_default = {-max_xy, -max_xy, -max_z};
1804 l_default = {2 * max_xy, 2 * max_xy, 2 * max_z};
1808 const double minimum_extension = 30.;
1809 for (
auto i = std::size_t{0}; i < l_default.size(); i++) {
1810 if (l_default[i] < minimum_extension) {
1812 <<
"Automatic lattice extension in direction " << i
1813 <<
" heuristically determined as " << l_default[i]
1814 <<
" fm is smaller than " << minimum_extension
1815 <<
" fm. Imposing minimum size.";
1816 l_default[i] = minimum_extension;
1817 origin_default[i] = -0.5 * minimum_extension;
1823 const int n_xy = numeric_cast<int>(std::ceil(l_default[0] / 0.8));
1824 const bool to_be_contracted =
1827 const double contraction_factor = (to_be_contracted) ? gamma : 1.0;
1828 const int nz = numeric_cast<int>(
1829 std::ceil(l_default[2] / 0.8 * contraction_factor));
1830 n_default = {n_xy, n_xy, nz};
1831 }
else if (modus_.is_box()) {
1832 origin_default = {0., 0., 0.};
1833 const double bl = modus_.length();
1834 l_default = {bl, bl, bl};
1835 const int n_xyz = numeric_cast<int>(std::ceil(bl / 0.5));
1836 n_default = {n_xyz, n_xyz, n_xyz};
1837 }
else if (modus_.is_sphere()) {
1840 const double max_d = modus_.radius() + end_time_;
1841 origin_default = {-max_d, -max_d, -max_d};
1842 l_default = {2 * max_d, 2 * max_d, 2 * max_d};
1844 const int n_xyz = numeric_cast<int>(std::ceil(2 * max_d / 0.8));
1845 n_default = {n_xyz, n_xyz, n_xyz};
1848 return std::make_tuple<std::array<double, 3>, std::array<int, 3>,
1849 std::array<double, 3>>(
1850 config.
take(InputKeys::lattice_sizes, l_default),
1851 config.
take(InputKeys::lattice_cellNumber, n_default),
1852 config.
take(InputKeys::lattice_origin, origin_default));
1857 <<
"Lattice is ON. Origin = (" << origin[0] <<
"," << origin[1] <<
","
1858 << origin[2] <<
"), sizes = (" << l[0] <<
"," << l[1] <<
"," << l[2]
1859 <<
"), number of cells = (" <<
n[0] <<
"," <<
n[1] <<
"," <<
n[2]
1860 <<
"), periodic = " << std::boolalpha << periodic;
1862 if (printout_lattice_td_ || printout_full_lattice_any_td_) {
1863 dens_type_lattice_printout_ = output_parameters.td_dens_type;
1864 printout_rho_eckart_ = output_parameters.td_rho_eckart;
1865 printout_tmn_ = output_parameters.td_tmn;
1866 printout_tmn_landau_ = output_parameters.td_tmn_landau;
1867 printout_v_landau_ = output_parameters.td_v_landau;
1868 printout_j_QBS_ = output_parameters.td_jQBS;
1870 if (printout_tmn_ || printout_tmn_landau_ || printout_v_landau_) {
1871 Tmn_ = std::make_unique<RectangularLattice<EnergyMomentumTensor>>(
1872 l,
n, origin, periodic, LatticeUpdate::AtOutput);
1874 if (printout_j_QBS_) {
1875 j_QBS_lat_ = std::make_unique<DensityLattice>(l,
n, origin, periodic,
1876 LatticeUpdate::AtOutput);
1883 old_jmu_auxiliary_ = std::make_unique<RectangularLattice<FourVector>>(
1884 l,
n, origin, periodic, LatticeUpdate::EveryTimestep);
1885 new_jmu_auxiliary_ = std::make_unique<RectangularLattice<FourVector>>(
1886 l,
n, origin, periodic, LatticeUpdate::EveryTimestep);
1887 four_gradient_auxiliary_ =
1888 std::make_unique<RectangularLattice<std::array<FourVector, 4>>>(
1889 l,
n, origin, periodic, LatticeUpdate::EveryTimestep);
1891 if (potentials_->use_skyrme()) {
1892 jmu_B_lat_ = std::make_unique<DensityLattice>(
1893 l,
n, origin, periodic, LatticeUpdate::EveryTimestep);
1894 UB_lat_ = std::make_unique<RectangularLattice<FourVector>>(
1895 l,
n, origin, periodic, LatticeUpdate::EveryTimestep);
1896 FB_lat_ = std::make_unique<
1897 RectangularLattice<std::pair<ThreeVector, ThreeVector>>>(
1898 l,
n, origin, periodic, LatticeUpdate::EveryTimestep);
1900 if (potentials_->use_symmetry()) {
1901 jmu_I3_lat_ = std::make_unique<DensityLattice>(
1902 l,
n, origin, periodic, LatticeUpdate::EveryTimestep);
1903 UI3_lat_ = std::make_unique<RectangularLattice<FourVector>>(
1904 l,
n, origin, periodic, LatticeUpdate::EveryTimestep);
1905 FI3_lat_ = std::make_unique<
1906 RectangularLattice<std::pair<ThreeVector, ThreeVector>>>(
1907 l,
n, origin, periodic, LatticeUpdate::EveryTimestep);
1909 if (potentials_->use_coulomb()) {
1910 jmu_el_lat_ = std::make_unique<DensityLattice>(
1911 l,
n, origin, periodic, LatticeUpdate::EveryTimestep);
1912 EM_lat_ = std::make_unique<
1913 RectangularLattice<std::pair<ThreeVector, ThreeVector>>>(
1914 l,
n, origin, periodic, LatticeUpdate::EveryTimestep);
1916 if (potentials_->use_vdf()) {
1917 jmu_B_lat_ = std::make_unique<DensityLattice>(
1918 l,
n, origin, periodic, LatticeUpdate::EveryTimestep);
1919 UB_lat_ = std::make_unique<RectangularLattice<FourVector>>(
1920 l,
n, origin, periodic, LatticeUpdate::EveryTimestep);
1921 FB_lat_ = std::make_unique<
1922 RectangularLattice<std::pair<ThreeVector, ThreeVector>>>(
1923 l,
n, origin, periodic, LatticeUpdate::EveryTimestep);
1927 old_fields_auxiliary_ =
1928 std::make_unique<RectangularLattice<FourVector>>(
1929 l,
n, origin, periodic, LatticeUpdate::EveryTimestep);
1930 new_fields_auxiliary_ =
1931 std::make_unique<RectangularLattice<FourVector>>(
1932 l,
n, origin, periodic, LatticeUpdate::EveryTimestep);
1933 fields_four_gradient_auxiliary_ =
1934 std::make_unique<RectangularLattice<std::array<FourVector, 4>>>(
1935 l,
n, origin, periodic, LatticeUpdate::EveryTimestep);
1938 fields_lat_ = std::make_unique<FieldsLattice>(
1939 l,
n, origin, periodic, LatticeUpdate::EveryTimestep);
1943 jmu_B_lat_ = std::make_unique<DensityLattice>(l,
n, origin, periodic,
1944 LatticeUpdate::AtOutput);
1948 jmu_I3_lat_ = std::make_unique<DensityLattice>(l,
n, origin, periodic,
1949 LatticeUpdate::AtOutput);
1954 jmu_custom_lat_ = std::make_unique<DensityLattice>(
1955 l,
n, origin, periodic, LatticeUpdate::AtOutput);
1960 const bool has_coulomb_potential = potentials_ && potentials_->use_coulomb();
1961 const bool has_lattice_td_output =
1962 printout_lattice_td_ || printout_full_lattice_any_td_;
1963 if (has_lattice_td_output && !has_lattice) {
1965 "If you want Thermodynamic VTK or Lattice output, configure a "
1968 if (has_coulomb_potential && !has_lattice) {
1970 "Coulomb potential requires a lattice. Please set it up in the "
1971 "configuration file.");
1973 if (printout_coulomb_vtk_) {
1974 if (!has_lattice && !has_coulomb_potential) {
1976 "Coulomb VTK output requires coulomb potential and a lattice. "
1977 "Please add both to the configuration file.");
1978 }
else if (!has_lattice) {
1980 "Coulomb VTK output requires a lattice. "
1981 "Please set it up in the configuration file.");
1982 }
else if (!has_coulomb_potential) {
1984 "Coulomb VTK output requires coulomb potential. "
1985 "Please add it to the configuration file.");
1990 if ((potentials_ !=
nullptr) && (jmu_B_lat_ ==
nullptr)) {
1991 logg[
LExperiment].warn() <<
"Lattice is NOT used. Mean-field energy is "
1992 <<
"not going to be calculated.";
1996 if (parameters_.potential_affect_threshold) {
2004 (jmu_B_lat_ ==
nullptr)) {
2005 throw std::runtime_error(
2006 "Lattice is necessary to calculate finite difference gradients.");
2013 thermalizer_ = modus_.create_grandcan_thermalizer(th_conf);
2018 seed_ = config.
take(InputKeys::gen_randomseed);
2049 uint64_t scatterings_this_interval,
2052 double E_mean_field,
2053 double E_mean_field_initial);
2090 double E_mean_field,
double modus_impact_parameter,
2092 bool projectile_target_interact,
2093 bool kinematic_cut_for_SMASH_IC);
2095 template <
typename Modus>
2105 while (r == INT64_MIN) {
2108 seed_ = std::abs(r);
2113 if (process_string_ptr_ != NULL) {
2114 process_string_ptr_->init_pythia_hadron_rndm();
2117 for (
Particles &particles : ensembles_) {
2122 double start_time = -1.0;
2126 if (modus_.is_collider()) {
2127 modus_.sample_impact();
2128 logg[
LExperiment].info(
"Impact parameter = ", modus_.impact_parameter(),
2131 for (
Particles &particles : ensembles_) {
2132 start_time = modus_.initial_conditions(&particles, parameters_);
2137 for (
Particles &particles : ensembles_) {
2138 modus_.impose_boundary_conditions(&particles, outputs_);
2141 double timestep = delta_time_startup_;
2143 switch (time_step_mode_) {
2147 timestep = end_time_ - start_time;
2149 const double max_dt = modus_.max_timestep(max_transverse_distance_sqr_);
2150 if (max_dt > 0. && max_dt < timestep) {
2155 std::unique_ptr<UniformClock> clock_for_this_event;
2156 if (modus_.is_list() && (timestep < 0.0)) {
2157 throw std::runtime_error(
2158 "Timestep for the given event is negative. \n"
2159 "This might happen if the formation times of the input particles are "
2160 "larger than the specified end time of the simulation.");
2162 clock_for_this_event =
2163 std::make_unique<UniformClock>(start_time, timestep, end_time_);
2164 parameters_.labclock = std::move(clock_for_this_event);
2167 parameters_.outputclock->reset(start_time,
true);
2169 parameters_.outputclock->remove_times_in_past(start_time);
2172 "Lab clock: t_start = ", parameters_.labclock->current_time(),
2173 ", dt = ", parameters_.labclock->timestep_duration());
2178 wall_actions_total_ = 0;
2179 previous_wall_actions_total_ = 0;
2180 interactions_total_ = 0;
2181 previous_interactions_total_ = 0;
2182 discarded_interactions_total_ = 0;
2183 total_pauli_blocked_ = 0;
2184 projectile_target_interact_.assign(parameters_.n_ensembles,
false);
2185 total_hypersurface_crossing_actions_ = 0;
2186 total_energy_removed_ = 0.0;
2187 total_energy_violated_by_Pythia_ = 0.0;
2190 logg[
LExperiment].info() <<
"Time[fm] Ekin[GeV] E_MF[GeV] ETotal[GeV] "
2191 <<
"ETot/N[GeV] D(ETot/N)[GeV] Scatt&Decays "
2192 <<
"Particles Comp.Time";
2194 double E_mean_field = 0.0;
2199 if ((jmu_B_lat_ !=
nullptr)) {
2201 new_jmu_auxiliary_.get(), four_gradient_auxiliary_.get(),
2203 density_param_, ensembles_,
2204 parameters_.labclock->timestep_duration(),
true);
2208 for (
auto &node : *jmu_B_lat_) {
2209 node.overwrite_drho_dt_to_zero();
2210 node.overwrite_djmu_dt_to_zero();
2213 EM_lat_.get(), parameters_);
2216 initial_mean_field_energy_ = E_mean_field;
2218 ensembles_, 0u, conserved_initial_, time_start_,
2219 parameters_.labclock->current_time(), E_mean_field,
2220 initial_mean_field_energy_);
2223 for (
const auto &
output : outputs_) {
2224 for (
int i_ens = 0; i_ens < parameters_.n_ensembles; i_ens++) {
2226 ensembles_, E_mean_field, modus_.impact_parameter(), parameters_,
2227 projectile_target_interact_[i_ens], kinematic_cuts_for_IC_output_);
2228 output->at_eventstart(ensembles_[i_ens], {event_, i_ens}, event_info);
2231 output->at_eventstart(ensembles_, event_);
2233 if (printout_full_lattice_any_td_) {
2234 if (printout_rho_eckart_) {
2235 switch (dens_type_lattice_printout_) {
2252 if (printout_tmn_) {
2254 dens_type_lattice_printout_, *Tmn_);
2256 if (printout_tmn_landau_) {
2258 dens_type_lattice_printout_, *Tmn_);
2260 if (printout_v_landau_) {
2262 dens_type_lattice_printout_, *Tmn_);
2264 if (printout_j_QBS_) {
2266 dens_type_lattice_printout_, *j_QBS_lat_);
2278 const double m = particle.effective_mass();
2279 double v_beam = 0.0;
2280 if (particle.belongs_to() == BelongsTo::Projectile) {
2281 v_beam = modus_.velocity_projectile();
2282 }
else if (particle.belongs_to() == BelongsTo::Target) {
2283 v_beam = modus_.velocity_target();
2285 const double gamma = 1.0 / std::sqrt(1.0 - v_beam * v_beam);
2286 beam_momentum_.emplace_back(
2287 FourVector(gamma * m, 0.0, 0.0, gamma * v_beam * m));
2292 template <
typename Modus>
2294 bool include_pauli_blocking) {
2295 Particles &particles = ensembles_[i_ensemble];
2299 discarded_interactions_total_++;
2301 " (discarded: invalid)");
2304 const bool core_in_incoming =
2305 std::any_of(incoming.begin(), incoming.end(),
2306 [](
const ParticleData &
p) { return p.is_core(); });
2307 if (core_in_incoming) {
2308 if (action.
get_type() == ProcessType::FluidizationNoRemoval) {
2315 const bool all_core_in_incoming =
2317 [](
const ParticleData &
p) { return p.is_core(); });
2318 if (!all_core_in_incoming) {
2325 }
catch (Action::StochasticBelowEnergyThreshold &) {
2329 if (include_pauli_blocking && pauli_blocker_ &&
2331 total_pauli_blocked_++;
2337 if (modus_.is_collider()) {
2338 int count_target = 0, count_projectile = 0;
2339 for (
const auto &
p : incoming) {
2340 if (
p.belongs_to() == BelongsTo::Projectile) {
2342 }
else if (
p.belongs_to() == BelongsTo::Target) {
2346 if (count_target > 0 && count_projectile > 0) {
2347 projectile_target_interact_[i_ensemble] =
true;
2353 const auto id_process =
static_cast<uint32_t
>(interactions_total_ + 1);
2355 total_energy_violated_by_Pythia_ += action.
perform(&particles, id_process);
2357 interactions_total_++;
2358 if (action.
get_type() == ProcessType::Wall) {
2359 wall_actions_total_++;
2361 if (action.
get_type() == ProcessType::Fluidization) {
2362 total_hypersurface_crossing_actions_++;
2369 constexpr
bool compute_grad =
false;
2370 const bool smearing =
true;
2373 rho = std::get<0>(
current_eckart(r_interaction.threevec(), particles,
2374 density_param_, dens_type_, compute_grad,
2392 for (
const auto &
output : outputs_) {
2393 if (
output->is_dilepton_output() ||
output->is_photon_output()) {
2396 if (
output->is_IC_output()) {
2397 if (action.
get_type() == ProcessType::Fluidization ||
2398 action.
get_type() == ProcessType::FluidizationNoRemoval) {
2399 output->at_interaction(action, rho);
2402 output->at_interaction(action, rho);
2411 if (photons_switch_ &&
2413 ScatterActionPhoton::is_kinematically_possible(
2417 constexpr
double action_time = 0.;
2418 ScatterActionPhoton photon_act(
2435 photon_act.add_single_process();
2437 photon_act.perform_photons(outputs_);
2440 if (photons_bremsstrahlung_switch_ &&
2441 BremsstrahlungActionPhoton::is_photon_brems_reaction(
2445 constexpr
double action_time = 0.;
2447 BremsstrahlungActionPhoton photon_brems_act(
2465 photon_brems_act.add_single_process();
2467 photon_brems_act.perform_bremsstrahlung(outputs_);
2470 if (dileptons_bremsstrahlung_switch_ &&
2471 BremsstrahlungActionDilepton::is_dilepton_brems_reaction(
2474 constexpr
double action_time = 0.;
2478 BremsstrahlungActionDilepton dilepton_brems_act(
2480 parameters_.dilepton_brems_pion_form_factor_type);
2494 dilepton_brems_act.perform_dilepton_bremsstrahlung(outputs_);
2513 template <
typename Modus>
2515 ParticleList &&add_plist,
2516 ParticleList &&remove_plist) {
2517 if (!add_plist.empty() || !remove_plist.empty()) {
2518 if (ensembles_.size() > 1) {
2519 throw std::runtime_error(
2520 "Adding or removing particles from SMASH is only possible when one "
2521 "ensemble is used.");
2523 const double action_time = parameters_.labclock->current_time();
2527 if (!add_plist.empty()) {
2530 if (!add_plist.empty()) {
2532 auto action_add_particles = std::make_unique<FreeforallAction>(
2533 ParticleList{}, add_plist, action_time);
2534 perform_action(*action_add_particles, 0);
2537 if (!remove_plist.empty()) {
2540 if (!remove_plist.empty()) {
2541 ParticleList found_particles_to_remove;
2542 for (
const auto &particle_to_remove : remove_plist) {
2543 const auto iterator_to_particle_to_be_removed_in_ensemble =
2545 ensembles_[0].begin(), ensembles_[0].end(),
2546 [&particle_to_remove, &action_time](
const ParticleData &
p) {
2548 particle_to_remove,
p, action_time);
2550 if (iterator_to_particle_to_be_removed_in_ensemble !=
2551 ensembles_[0].end())
2552 found_particles_to_remove.push_back(
2553 *iterator_to_particle_to_be_removed_in_ensemble);
2557 std::sort(found_particles_to_remove.begin(),
2558 found_particles_to_remove.end(),
2560 return p1.id() < p2.id();
2562 const auto iterator_to_first_duplicate = std::adjacent_find(
2563 found_particles_to_remove.begin(), found_particles_to_remove.end(),
2565 return p1.id() == p2.id();
2567 if (iterator_to_first_duplicate != found_particles_to_remove.end()) {
2568 logg[
LExperiment].error() <<
"The same particle has been asked to be "
2569 "removed multiple times:\n"
2570 << *iterator_to_first_duplicate;
2571 throw std::logic_error(
"Particle cannot be removed twice!");
2573 if (
auto delta = remove_plist.size() - found_particles_to_remove.size();
2576 "When trying to remove particle(s) at the beginning ",
2577 "of the system evolution,\n", delta,
2578 " particle(s) could not be found and will be ignored.");
2580 if (!found_particles_to_remove.empty()) {
2581 [[maybe_unused]]
const auto number_particles_before_removal =
2582 ensembles_[0].size();
2584 auto action_remove_particles = std::make_unique<FreeforallAction>(
2585 found_particles_to_remove, ParticleList{}, action_time);
2586 perform_action(*action_remove_particles, 0);
2588 assert(number_particles_before_removal -
2589 found_particles_to_remove.size() ==
2590 ensembles_[0].size());
2595 if (t_end > end_time_) {
2597 <<
"Evolution asked to be run until " << t_end <<
" > " << end_time_
2598 <<
" and this cannot be done (because of how the clock works).";
2599 throw std::logic_error(
2600 "Experiment cannot evolve the system beyond End_Time.");
2602 while (*(parameters_.labclock) < t_end) {
2603 const double dt = parameters_.labclock->timestep_duration();
2604 logg[
LExperiment].debug(
"Timestepless propagation for next ", dt,
" fm.");
2608 thermalizer_->is_time_to_thermalize(parameters_.labclock)) {
2609 const bool ignore_cells_under_treshold =
true;
2612 thermalizer_->update_thermalizer_lattice(ensembles_, density_param_,
2613 ignore_cells_under_treshold);
2614 const double current_t = parameters_.labclock->current_time();
2615 for (
int i_ens = 0; i_ens < parameters_.n_ensembles; i_ens++) {
2616 thermalizer_->thermalize(ensembles_[i_ens], current_t,
2617 parameters_.testparticles);
2620 perform_action(th_act, i_ens);
2626 modus_.build_fluidization_lattice(parameters_.labclock->current_time(),
2627 ensembles_, density_param_);
2630 std::vector<Actions> actions(parameters_.n_ensembles);
2631 for (
int i_ens = 0; i_ens < parameters_.n_ensembles; i_ens++) {
2632 actions[i_ens].clear();
2633 if (ensembles_[i_ens].size() > 0 && action_finders_.size() > 0) {
2635 const double min_cell_length = compute_min_cell_length(dt);
2640 const bool include_unformed_particles = IC_switch_;
2642 use_grid_ ? modus_.create_grid(ensembles_[i_ens], min_cell_length,
2643 dt, parameters_.coll_crit,
2644 include_unformed_particles)
2645 : modus_.create_grid(ensembles_[i_ens], min_cell_length,
2646 dt, parameters_.coll_crit,
2647 include_unformed_particles,
2648 CellSizeStrategy::Largest);
2650 const double gcell_vol = grid.cell_volume();
2653 [&](
const ParticleList &search_list) {
2654 for (
const auto &finder : action_finders_) {
2655 actions[i_ens].insert(finder->find_actions_in_cell(
2656 search_list, dt, gcell_vol, beam_momentum_));
2659 [&](
const ParticleList &search_list,
2660 const ParticleList &neighbors_list) {
2661 for (
const auto &finder : action_finders_) {
2662 actions[i_ens].insert(finder->find_actions_with_neighbors(
2663 search_list, neighbors_list, dt, beam_momentum_));
2672 const double end_timestep_time = parameters_.labclock->next_time();
2673 while (next_output_time() < end_timestep_time) {
2674 for (
int i_ens = 0; i_ens < parameters_.n_ensembles; i_ens++) {
2675 run_time_evolution_timestepless(actions[i_ens], i_ens,
2676 next_output_time());
2678 ++(*parameters_.outputclock);
2680 intermediate_output();
2682 for (
int i_ens = 0; i_ens < parameters_.n_ensembles; i_ens++) {
2683 run_time_evolution_timestepless(actions[i_ens], i_ens, end_timestep_time);
2689 update_potentials();
2690 update_momenta(ensembles_, parameters_.labclock->timestep_duration(),
2691 *potentials_, FB_lat_.get(), FI3_lat_.get(), EM_lat_.get(),
2698 for (
Particles &particles : ensembles_) {
2703 ++(*parameters_.labclock);
2711 if (!potentials_ && !parameters_.strings_switch &&
2713 std::string err_msg = conserved_initial_.report_deviations(ensembles_);
2714 if (!err_msg.empty()) {
2716 throw std::runtime_error(
"Violation of conserved quantities!");
2729 ++(*parameters_.outputclock);
2731 if (pauli_blocker_) {
2733 "Interactions: Pauli-blocked/performed = ", total_pauli_blocked_,
"/",
2734 interactions_total_ - wall_actions_total_);
2738 template <
typename Modus>
2743 if (dilepton_finder_ !=
nullptr) {
2744 for (
const auto &
output : outputs_) {
2745 dilepton_finder_->shine(particles,
output.get(), dt);
2758 constexpr uint64_t max_uint32 = std::numeric_limits<uint32_t>::max();
2759 if (interactions_total >= max_uint32) {
2760 throw std::runtime_error(
"Integer overflow in total interaction number!");
2764 template <
typename Modus>
2766 Actions &actions,
int i_ensemble,
const double end_time_propagation) {
2767 Particles &particles = ensembles_[i_ensemble];
2769 "Timestepless propagation: ",
"Actions size = ", actions.
size(),
2770 ", end time = ", end_time_propagation);
2778 ActionPtr act = actions.
pop();
2779 if (!act->is_valid(particles)) {
2780 discarded_interactions_total_++;
2782 " (discarded: invalid)");
2786 ", action time = ", act->time_of_execution());
2789 propagate_and_shine(act->time_of_execution(), particles);
2796 act->update_incoming(particles);
2797 const bool performed = perform_action(*act, i_ensemble);
2807 const double end_time_timestep = parameters_.labclock->next_time();
2809 const double time_left = end_time_timestep - act->time_of_execution();
2810 const ParticleList &outgoing_particles = act->outgoing_particles();
2812 const double gcell_vol = 0.0;
2813 for (
const auto &finder : action_finders_) {
2815 actions.
insert(finder->find_actions_in_cell(outgoing_particles, time_left,
2816 gcell_vol, beam_momentum_));
2818 actions.
insert(finder->find_actions_with_surrounding_particles(
2819 outgoing_particles, particles, time_left, beam_momentum_));
2825 propagate_and_shine(end_time_propagation, particles);
2828 template <
typename Modus>
2830 const uint64_t wall_actions_this_interval =
2831 wall_actions_total_ - previous_wall_actions_total_;
2832 previous_wall_actions_total_ = wall_actions_total_;
2833 const uint64_t interactions_this_interval = interactions_total_ -
2834 previous_interactions_total_ -
2835 wall_actions_this_interval;
2836 previous_interactions_total_ = interactions_total_;
2837 double E_mean_field = 0.0;
2840 double computational_frame_time = 0.0;
2843 if ((jmu_B_lat_ !=
nullptr)) {
2845 EM_lat_.get(), parameters_);
2852 if (modus_.is_box()) {
2853 double tmp = (E_mean_field - initial_mean_field_energy_) /
2854 (E_mean_field + initial_mean_field_energy_);
2860 if (std::abs(tmp) > 0.01) {
2862 <<
"\n\n\n\t The mean field at t = "
2863 << parameters_.outputclock->current_time()
2864 <<
" [fm] differs from the mean field at t = 0:"
2865 <<
"\n\t\t initial_mean_field_energy_ = "
2866 << initial_mean_field_energy_ <<
" [GeV]"
2867 <<
"\n\t\t abs[(E_MF - E_MF(t=0))/(E_MF + E_MF(t=0))] = "
2869 <<
"\n\t\t E_MF/E_MF(t=0) = "
2870 << E_mean_field / initial_mean_field_energy_ <<
"\n\n";
2877 ensembles_, interactions_this_interval, conserved_initial_, time_start_,
2878 parameters_.outputclock->current_time(), E_mean_field,
2879 initial_mean_field_energy_);
2883 if (!(modus_.is_box() && parameters_.outputclock->current_time() <
2884 modus_.equilibration_time())) {
2885 for (
const auto &
output : outputs_) {
2886 if (
output->is_dilepton_output() ||
output->is_photon_output() ||
2887 output->is_IC_output()) {
2890 for (
int i_ens = 0; i_ens < parameters_.n_ensembles; i_ens++) {
2892 ensembles_, E_mean_field, modus_.impact_parameter(), parameters_,
2893 projectile_target_interact_[i_ens], kinematic_cuts_for_IC_output_);
2895 output->at_intermediate_time(ensembles_[i_ens], parameters_.outputclock,
2896 density_param_, {event_, i_ens},
2898 computational_frame_time = event_info.current_time;
2901 output->at_intermediate_time(ensembles_, parameters_.outputclock,
2905 if (printout_rho_eckart_) {
2906 switch (dens_type_lattice_printout_) {
2913 output->thermodynamics_lattice_output(*jmu_B_lat_,
2914 computational_frame_time);
2919 density_param_, ensembles_,
false);
2923 output->thermodynamics_lattice_output(*jmu_I3_lat_,
2924 computational_frame_time);
2930 jmu_custom_lat_.get(), lat_upd, dens_type_lattice_printout_,
2931 density_param_, ensembles_,
false);
2933 dens_type_lattice_printout_,
2935 output->thermodynamics_lattice_output(*jmu_custom_lat_,
2936 computational_frame_time);
2939 if (printout_tmn_ || printout_tmn_landau_ || printout_v_landau_) {
2941 Tmn_.get(), lat_upd, dens_type_lattice_printout_, density_param_,
2943 if (printout_tmn_) {
2945 dens_type_lattice_printout_, *Tmn_);
2946 output->thermodynamics_lattice_output(
2949 if (printout_tmn_landau_) {
2951 dens_type_lattice_printout_, *Tmn_);
2952 output->thermodynamics_lattice_output(
2954 computational_frame_time);
2956 if (printout_v_landau_) {
2958 dens_type_lattice_printout_, *Tmn_);
2959 output->thermodynamics_lattice_output(
2961 computational_frame_time);
2965 output->fields_output(
"Efield",
"Bfield", *EM_lat_);
2967 if (printout_j_QBS_) {
2968 output->thermodynamics_lattice_output(
2969 *j_QBS_lat_, computational_frame_time, ensembles_, density_param_);
2973 output->thermodynamics_output(*thermalizer_);
2979 template <
typename Modus>
2982 if (potentials_->use_symmetry() && jmu_I3_lat_ !=
nullptr) {
2984 new_jmu_auxiliary_.get(), four_gradient_auxiliary_.get(),
2986 density_param_, ensembles_,
2987 parameters_.labclock->timestep_duration(),
true);
2989 if ((potentials_->use_skyrme() || potentials_->use_symmetry()) &&
2990 jmu_B_lat_ !=
nullptr) {
2992 new_jmu_auxiliary_.get(), four_gradient_auxiliary_.get(),
2994 density_param_, ensembles_,
2995 parameters_.labclock->timestep_duration(),
true);
2996 const size_t UBlattice_size = UB_lat_->size();
2997 for (
size_t i = 0; i < UBlattice_size; i++) {
2998 auto jB = (*jmu_B_lat_)[i];
3002 double baryon_density = jB.rho();
3006 if (potentials_->use_skyrme()) {
3008 flow_four_velocity_B * potentials_->skyrme_pot(baryon_density);
3010 potentials_->skyrme_force(baryon_density, baryon_grad_j0,
3011 baryon_dvecj_dt, baryon_curl_vecj);
3013 if (potentials_->use_symmetry() && jmu_I3_lat_ !=
nullptr) {
3014 auto jI3 = (*jmu_I3_lat_)[i];
3017 ? jI3.jmu_net() / jI3.rho()
3019 (*UI3_lat_)[i] = flow_four_velocity_I3 *
3020 potentials_->symmetry_pot(jI3.rho(), baryon_density);
3021 (*FI3_lat_)[i] = potentials_->symmetry_force(
3022 jI3.rho(), jI3.grad_j0(), jI3.dvecj_dt(), jI3.curl_vecj(),
3023 baryon_density, baryon_grad_j0, baryon_dvecj_dt,
3028 if (potentials_->use_coulomb()) {
3031 density_param_, ensembles_,
true);
3032 for (
size_t i = 0; i < EM_lat_->size(); i++) {
3034 ThreeVector position = jmu_el_lat_->cell_center(i);
3035 jmu_el_lat_->integrate_volume(electric_field,
3036 Potentials::E_field_integrand,
3037 potentials_->coulomb_r_cut(), position);
3039 jmu_el_lat_->integrate_volume(magnetic_field,
3040 Potentials::B_field_integrand,
3041 potentials_->coulomb_r_cut(), position);
3042 (*EM_lat_)[i] = std::make_pair(electric_field, magnetic_field);
3045 if (potentials_->use_vdf() && jmu_B_lat_ !=
nullptr) {
3047 new_jmu_auxiliary_.get(), four_gradient_auxiliary_.get(),
3049 density_param_, ensembles_,
3050 parameters_.labclock->timestep_duration(),
true);
3053 fields_lat_.get(), old_fields_auxiliary_.get(),
3054 new_fields_auxiliary_.get(), fields_four_gradient_auxiliary_.get(),
3055 jmu_B_lat_.get(), LatticeUpdate::EveryTimestep, *potentials_,
3056 parameters_.labclock->timestep_duration());
3058 const size_t UBlattice_size = UB_lat_->size();
3059 for (
size_t i = 0; i < UBlattice_size; i++) {
3060 auto jB = (*jmu_B_lat_)[i];
3061 (*UB_lat_)[i] = potentials_->vdf_pot(jB.rho(), jB.jmu_net());
3062 switch (parameters_.field_derivatives_mode) {
3064 (*FB_lat_)[i] = potentials_->vdf_force(
3065 jB.rho(), jB.drho_dxnu().x0(), jB.drho_dxnu().threevec(),
3066 jB.grad_rho_cross_vecj(), jB.jmu_net().x0(), jB.grad_j0(),
3067 jB.jmu_net().threevec(), jB.dvecj_dt(), jB.curl_vecj());
3070 auto Amu = (*fields_lat_)[i];
3071 (*FB_lat_)[i] = potentials_->vdf_force(
3072 Amu.grad_A0(), Amu.dvecA_dt(), Amu.curl_vecA());
3080 template <
typename Modus>
3084 bool actions_performed, actions_found;
3085 uint64_t interactions_old;
3087 actions_found =
false;
3088 interactions_old = interactions_total_;
3089 for (
int i_ens = 0; i_ens < parameters_.n_ensembles; i_ens++) {
3092 if (dilepton_finder_ !=
nullptr) {
3093 for (
const auto &
output : outputs_) {
3094 dilepton_finder_->shine_final(ensembles_[i_ens],
output.get(),
true);
3098 for (
const auto &finder : action_finders_) {
3099 auto found_actions = finder->find_final_actions(ensembles_[i_ens]);
3100 if (!found_actions.empty()) {
3101 actions.
insert(std::move(found_actions));
3102 actions_found =
true;
3107 perform_action(*actions.
pop(), i_ens,
false);
3110 actions_performed = interactions_total_ > interactions_old;
3112 if (actions_found && !actions_performed) {
3113 throw std::runtime_error(
"Final actions were found but not performed.");
3116 }
while (actions_performed);
3119 if (dilepton_finder_ !=
nullptr) {
3120 for (
const auto &
output : outputs_) {
3121 for (
Particles &particles : ensembles_) {
3122 dilepton_finder_->shine_final(particles,
output.get(),
false);
3128 template <
typename Modus>
3133 double E_mean_field = 0.0;
3134 if (
likely(parameters_.labclock > 0)) {
3135 const uint64_t wall_actions_this_interval =
3136 wall_actions_total_ - previous_wall_actions_total_;
3137 const uint64_t interactions_this_interval = interactions_total_ -
3138 previous_interactions_total_ -
3139 wall_actions_this_interval;
3142 if ((jmu_B_lat_ !=
nullptr)) {
3144 EM_lat_.get(), parameters_);
3147 if (std::abs(parameters_.labclock->current_time() - end_time_) >
3150 <<
"SMASH not propagated until configured end time. Current time = "
3151 << parameters_.labclock->current_time()
3152 <<
"fm. End time = " << end_time_ <<
"fm.";
3155 ensembles_, interactions_this_interval, conserved_initial_,
3156 time_start_, end_time_, E_mean_field, initial_mean_field_energy_);
3158 int total_particles = 0;
3159 for (
const Particles &particles : ensembles_) {
3160 total_particles += particles.
size();
3162 if (IC_switch_ && (total_particles == 0)) {
3163 const double initial_system_energy_plus_Pythia_violations =
3164 conserved_initial_.momentum().x0() + total_energy_violated_by_Pythia_;
3165 const double fraction_of_total_system_energy_removed =
3166 initial_system_energy_plus_Pythia_violations / total_energy_removed_;
3169 if (std::fabs(fraction_of_total_system_energy_removed - 1.) >
3171 throw std::runtime_error(
3172 "There is remaining energy in the system although all particles "
3176 total_energy_removed_)) +
3181 <<
"Time real: " << SystemClock::now() - time_start_;
3183 <<
"Interactions before reaching hypersurface: "
3184 << interactions_total_ - wall_actions_total_ -
3185 total_hypersurface_crossing_actions_;
3187 <<
"Total number of particles removed on hypersurface: "
3188 << total_hypersurface_crossing_actions_;
3191 const double precent_discarded =
3192 interactions_total_ > 0
3193 ?
static_cast<double>(discarded_interactions_total_) * 100.0 /
3196 std::stringstream msg_discarded;
3198 <<
"Discarded interaction number: " << discarded_interactions_total_
3199 <<
" (" << precent_discarded
3200 <<
"% of the total interaction number including wall crossings)";
3204 <<
"Time real: " << SystemClock::now() - time_start_;
3208 precent_discarded > 1.0) {
3211 << msg_discarded.str()
3212 <<
"\nThe number of discarded interactions is large, which means "
3213 "the assumption for the stochastic criterion of\n1 interaction "
3214 "per particle per timestep is probably violated. Consider "
3215 "reducing the timestep size.";
3219 << interactions_total_ - wall_actions_total_;
3223 int unformed_particles_count = 0;
3224 for (
const Particles &particles : ensembles_) {
3226 if (particle.formation_time() > end_time_) {
3227 unformed_particles_count++;
3231 if (unformed_particles_count > 0) {
3233 "End time might be too small. ", unformed_particles_count,
3234 " unformed particles were found at the end of the evolution.");
3239 count_nonempty_ensembles();
3241 for (
const auto &
output : outputs_) {
3242 for (
int i_ens = 0; i_ens < parameters_.n_ensembles; i_ens++) {
3244 ensembles_, E_mean_field, modus_.impact_parameter(), parameters_,
3245 projectile_target_interact_[i_ens], kinematic_cuts_for_IC_output_);
3246 output->at_eventend(ensembles_[i_ens], {event_, i_ens}, event_info);
3249 output->at_eventend(ensembles_, event_);
3252 if (printout_rho_eckart_) {
3257 if (printout_tmn_) {
3260 if (printout_tmn_landau_) {
3263 if (printout_v_landau_) {
3266 if (printout_j_QBS_) {
3272 template <
typename Modus>
3274 for (
bool has_interaction : projectile_target_interact_) {
3275 if (has_interaction) {
3276 nonempty_ensembles_++;
3281 template <
typename Modus>
3284 return event_ >= nevents_;
3287 if (nonempty_ensembles_ >= minimum_nonempty_ensembles_) {
3290 if (event_ >= max_events_) {
3292 <<
"Maximum number of events (" << max_events_
3293 <<
") exceeded. Stopping calculation. "
3294 <<
"The fraction of empty ensembles is "
3295 << (1.0 -
static_cast<double>(nonempty_ensembles_) /
3296 (event_ * parameters_.n_ensembles))
3297 <<
". If this fraction is expected, try increasing the "
3298 "Maximum_Ensembles_Run.";
3303 throw std::runtime_error(
"Event counting option is invalid");
3307 template <
typename Modus>
3312 template <
typename Modus>
3315 for (event_ = 0; !is_finished(); event_++) {
3316 mainlog.info() <<
"Event " << event_;
3319 initialize_new_event();
3321 run_time_evolution(end_time_);
3323 do_final_interactions();
Collection of useful type aliases to measure and output the (real) runtime.
A stream modifier that allows to colorize the log output.
Action is the base class for a generic process that takes a number of incoming particles and transfor...
virtual ProcessType get_type() const
Get the process type.
virtual double get_total_weight() const =0
Return the total weight value, which is mainly used for the weight output entry.
virtual double perform(Particles *particles, uint32_t id_process)
Actually perform the action, e.g.
const ParticleList & incoming_particles() const
Get the list of particles that go into the action.
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].
FourVector get_interaction_point() const
Get the interaction point.
bool is_valid(const Particles &particles) const
Check whether the action still applies.
bool is_pauli_blocked(const std::vector< Particles > &ensembles, const PauliBlocker &p_bl) const
Check if the action is Pauli-blocked.
The Actions class abstracts the storage and manipulation of actions.
ActionPtr pop()
Return the first action in the list and removes it from the list.
double earliest_time() const
Return time of execution of earliest action.
ActionList::size_type size() const
void insert(ActionList &&new_acts)
Insert a list of actions into this object.
Interface to the SMASH configuration files.
void set_value(Key< U > key, T &&value)
Overwrite the value of the YAML node corresponding to the specified key.
Configuration extract_sub_configuration(KeyLabels section, Configuration::GetEmpty empty_if_not_existing=Configuration::GetEmpty::No)
Create a new configuration from a then-removed section of the present object.
T read(const Key< T > &key) const
Additional interface for SMASH to read configuration values without removing them.
bool has_value(const Key< T > &key) const
Return whether there is a non-empty value behind the requested key (which is supposed not to refer to...
bool has_section(const KeyLabels &labels) const
Return whether there is a (possibly empty) section with the given labels.
Configuration extract_complete_sub_configuration(KeyLabels section, Configuration::GetEmpty empty_if_not_existing=Configuration::GetEmpty::No)
Alternative method to extract a sub-configuration, which retains the labels from the top-level in the...
T take(const Key< T > &key)
The default interface for SMASH to read configuration values.
A class to pre-calculate and store parameters relevant for density calculation.
Non-template interface to Experiment<Modus>.
static std::unique_ptr< ExperimentBase > create(Configuration &config, const std::filesystem::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.
void propagate_and_shine(double to_time, Particles &particles)
Propagate all particles until time to_time without any interactions and shine dileptons.
Experiment(Configuration &config, const std::filesystem::path &output_path)
Create a new Experiment.
const ExpansionProperties metric_
This struct contains information on the metric to be used.
std::unique_ptr< ActionFinderInterface > photon_finder_
The (Scatter) Actions Finder for Direct Photons.
double initial_mean_field_energy_
The initial total mean field energy in the system.
void create_output(const std::string &format, const std::string &content, const std::filesystem::path &output_path, const OutputParameters &par)
Create a list of output files.
std::vector< std::unique_ptr< ActionFinderInterface > > action_finders_
The Action finder objects.
bool printout_tmn_
Whether to print the energy-momentum tensor.
QuantumNumbers conserved_initial_
The conserved quantities of the system.
DensityParameters density_param_
Structure to precalculate and hold parameters for density computations.
double next_output_time() const
Shortcut for next output time.
double total_energy_removed_
Total energy removed from the system in hypersurface crossing actions.
DensityType dens_type_lattice_printout_
Type of density for lattice printout.
bool printout_coulomb_vtk_
Whether to write the electric and magnetic fields to VTK files.
double max_transverse_distance_sqr_
Maximal distance at which particles can interact in case of the geometric criterion,...
const bool dileptons_bremsstrahlung_switch_
This indicates whether dilepton production via bremsstrahlung is switched on.
const bool IC_dynamic_
This indicates if the IC is dynamic.
bool printout_j_QBS_
Whether to print the Q, B, S 4-currents.
std::unique_ptr< GrandCanThermalizer > thermalizer_
Instance of class used for forced thermalization.
void count_nonempty_ensembles()
Counts the number of ensembles in wich interactions took place at the end of an event.
const TimeStepMode time_step_mode_
This indicates whether to use time steps.
std::unique_ptr< DensityLattice > jmu_custom_lat_
Custom density on the lattices.
bool printout_full_lattice_any_td_
Whether to print the thermodynamics quantities evaluated on the lattices, point by point,...
void increase_event_number()
Increases the event number by one.
void run_time_evolution_timestepless(Actions &actions, int i_ensemble, const double end_time_propagation)
Performs all the propagations and actions during a certain time interval neglecting the influence of ...
Particles * first_ensemble()
Provides external access to SMASH particles.
void run_time_evolution(const double t_end, ParticleList &&add_plist={}, ParticleList &&remove_plist={})
Runs the time evolution of an event with fixed-size time steps or without timesteps,...
int minimum_nonempty_ensembles_
The number of ensembles, in which interactions take place, to be calculated.
std::unique_ptr< DensityLattice > jmu_B_lat_
Baryon density on the lattice.
DensityType dens_type_
Type of density to be written to collision headers.
void intermediate_output()
Intermediate output during an event.
std::vector< FourVector > beam_momentum_
The initial nucleons in the ColliderModus propagate with beam_momentum_, if Fermi motion is frozen.
std::unique_ptr< RectangularLattice< std::pair< ThreeVector, ThreeVector > > > FI3_lat_
Lattices for the electric and magnetic component of the symmetry force.
std::unique_ptr< RectangularLattice< FourVector > > new_jmu_auxiliary_
Auxiliary lattice for values of jmu at a time step t0 + dt.
double compute_min_cell_length(double dt) const
Calculate the minimal size for the grid cells such that the ScatterActionsFinder will find all collis...
void initialize_new_event()
This is called in the beginning of each event.
const bool photons_bremsstrahlung_switch_
This indicates whether bremsstrahlung is switched on.
bool is_finished()
Checks wether the desired number events have been calculated.
int n_fractional_photons_
Number of fractional photons produced per single reaction.
const double delta_time_startup_
The clock's timestep size at start up.
std::unique_ptr< RectangularLattice< EnergyMomentumTensor > > Tmn_
Lattices of energy-momentum tensors for printout.
std::vector< Particles > ensembles_
Complete particle list, all ensembles in one vector.
std::unique_ptr< RectangularLattice< FourVector > > new_fields_auxiliary_
Auxiliary lattice for values of Amu at a time step t0 + dt.
SystemTimePoint time_start_
system starting time of the simulation
uint64_t previous_wall_actions_total_
Total number of wall-crossings for previous timestep.
const bool IC_switch_
This indicates whether the experiment will be used as initial condition for hydrodynamics.
std::unique_ptr< RectangularLattice< std::pair< ThreeVector, ThreeVector > > > FB_lat_
Lattices for the electric and magnetic components of the Skyrme or VDF force.
OutputsList outputs_
A list of output formaters.
bool printout_rho_eckart_
Whether to print the Eckart rest frame density.
std::unique_ptr< RectangularLattice< std::array< FourVector, 4 > > > fields_four_gradient_auxiliary_
Auxiliary lattice for calculating the four-gradient of Amu.
std::unique_ptr< PauliBlocker > pauli_blocker_
An instance of PauliBlocker class that stores parameters needed for Pauli blocking calculations and c...
bool printout_v_landau_
Whether to print the 4-velocity in Landau frame.
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...
bool printout_lattice_td_
Whether to print the thermodynamics quantities evaluated on the lattices.
void do_final_interactions()
Performs the final decays of an event.
std::unique_ptr< RectangularLattice< std::pair< ThreeVector, ThreeVector > > > EM_lat_
Lattices for electric and magnetic field in fm^-2.
std::unique_ptr< FieldsLattice > fields_lat_
Mean-field A^mu on the lattice.
int max_events_
Maximum number of events to be calculated in order obtain the desired number of non-empty events usin...
int nonempty_ensembles_
Number of ensembles containing an interaction.
OutputPtr photon_output_
The Photon output.
EventCounting event_counting_
The way in which the number of calculated events is specified.
const bool dileptons_switch_
This indicates whether dileptons are switched on.
Modus modus_
Instance of the Modus template parameter.
std::unique_ptr< RectangularLattice< FourVector > > old_jmu_auxiliary_
Auxiliary lattice for values of jmu at a time step t0.
std::unique_ptr< DecayActionsFinderDilepton > dilepton_finder_
The Dilepton Action Finder.
std::unique_ptr< DensityLattice > j_QBS_lat_
4-current for j_QBS lattice output
bool printout_tmn_landau_
Whether to print the energy-momentum tensor in Landau frame.
std::unique_ptr< RectangularLattice< std::array< FourVector, 4 > > > four_gradient_auxiliary_
Auxiliary lattice for calculating the four-gradient of jmu.
const bool photons_switch_
This indicates whether photons are switched on.
StringProcess * process_string_ptr_
Pointer to the string process class object, which is used to set the random seed for PYTHIA objects i...
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...
ExperimentParameters parameters_
Struct of several member variables.
std::unique_ptr< RectangularLattice< FourVector > > old_fields_auxiliary_
Auxiliary lattice for values of Amu at a time step t0.
std::unique_ptr< DensityLattice > jmu_el_lat_
Electric charge density on the lattice.
std::unique_ptr< Potentials > potentials_
An instance of potentials class, that stores parameters of potentials, calculates them and their grad...
uint64_t wall_actions_total_
Total number of wall-crossings for current timestep.
uint64_t total_hypersurface_crossing_actions_
Total number of particles removed from the evolution in hypersurface crossing actions.
uint64_t interactions_total_
Total number of interactions for current timestep.
const bool use_grid_
This indicates whether to use the grid.
std::unique_ptr< DensityLattice > jmu_I3_lat_
Isospin projection density on the lattice.
int nevents_
Number of events.
uint64_t total_pauli_blocked_
Total number of Pauli-blockings for current timestep.
void final_output()
Output at the end of an event.
std::vector< bool > projectile_target_interact_
Whether the projectile and the target collided.
Modus * modus()
Provides external access to SMASH calculation modus.
void update_potentials()
Recompute potentials on lattices if necessary.
OutputPtr dilepton_output_
The Dilepton output.
int64_t seed_
random seed for the next event.
std::vector< Particles > * all_ensembles()
Getter for all ensembles.
uint64_t previous_interactions_total_
Total number of interactions for previous timestep.
uint64_t discarded_interactions_total_
Total number of discarded interactions, because they were invalidated before they could be performed.
void run() override
Runs the experiment.
bool kinematic_cuts_for_IC_output_
This indicates whether kinematic cuts are enabled for the IC output.
const double end_time_
simulation time at which the evolution is stopped.
double total_energy_violated_by_Pythia_
Total energy violation introduced by Pythia.
The FourVector class holds relevant values in Minkowski spacetime with (+, −, −, −) metric signature.
ParticleData contains the dynamic information of a certain particle.
static double formation_power_
Power with which the cross section scaling factor grows in time.
The Particles class abstracts the storage and manipulation of particles.
A class that stores parameters of potentials, calculates potentials and their gradients.
A container for storing conserved values.
A container class to hold all the arrays on the lattice and access them.
String excitation processes used in SMASH.
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 .
@ Frozen
Use fermi motion without potentials.
@ Dynamic
Dynamic fluidization based on local densities.
TimeStepMode
The time step mode.
@ Fixed
Use fixed time step.
@ None
Don't use time steps; propagate from action to action.
@ EckartDensity
Density in the Eckart frame.
@ Tmn
Energy-momentum tensor in lab frame.
@ LandauVelocity
Velocity of the Landau rest frame.
@ j_QBS
Electric (Q), baryonic (B) and strange (S) currents.
@ TmnLandau
Energy-momentum tensor in Landau rest frame.
@ BottomUp
Sum the existing partial contributions.
@ Stochastic
Stochastic Criteiron.
@ None
No pseudo-resonance is created.
EventCounting
Defines how the number of events is determined.
@ Invalid
Unused, only in the code for internal logic.
@ FixedNumber
The desired number of events is simulated disregarding of whether an interaction took place.
@ MinimumNonEmpty
Events are simulated until there are at least a given number of ensembles in which an interaction too...
DensityType
Allows to choose which kind of density to calculate.
#define SMASH_SOURCE_LOCATION
Hackery that is required to output the location in the source code where the log statement occurs.
std::ostream & operator<<(std::ostream &out, const ActionPtr &action)
Convenience: dereferences the ActionPtr to Action.
std::array< einhard::Logger<>, std::tuple_size< LogArea::AreaTuple >::value > & logg
An array that stores all pre-configured Logger objects.
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...
std::unique_ptr< OutputInterface > create_oscar_output(const std::string &format, const std::string &content, const std::filesystem::path &path, const OutputParameters &out_par)
std::unique_ptr< OutputInterface > create_binary_output(const std::string &format, const std::string &content, const std::filesystem::path &path, const OutputParameters &out_par)
Create a binary output object.
#define likely(x)
Tell the branch predictor that this expression is likely true.
Engine::result_type advance()
Advance the engine's state and return the generated value.
void set_seed(T &&seed)
Sets the seed of the random number engine.
static constexpr int LInitialConditions
void update_momenta(std::vector< Particles > &particles, double dt, const Potentials &pot, RectangularLattice< std::pair< ThreeVector, ThreeVector >> *FB_lat, RectangularLattice< std::pair< ThreeVector, ThreeVector >> *FI3_lat, RectangularLattice< std::pair< ThreeVector, ThreeVector >> *EM_lat, DensityLattice *jB_lat)
Updates the momenta of all particles at the current time step according to the equations of motion:
EventInfo fill_event_info(const std::vector< Particles > &ensembles, double E_mean_field, double modus_impact_parameter, const ExperimentParameters ¶meters, bool projectile_target_interact, bool kinematic_cut_for_SMASH_IC)
Generate the EventInfo object which is passed to outputs_.
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.
void expand_space_time(Particles *particles, const ExperimentParameters ¶meters, const ExpansionProperties &metric)
Modifies positions and momentum of all particles to account for space-time deformation.
void check_interactions_total(uint64_t interactions_total)
Make sure interactions_total can be represented as a 32-bit integer.
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...
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.
ExperimentParameters create_experiment_parameters(Configuration &config)
Gathers all general Experiment parameters.
constexpr double very_small_double
A very small double, used to avoid division by zero.
double calculate_mean_field_energy(const Potentials &potentials, RectangularLattice< smash::DensityOnLattice > &jmu_B_lat, RectangularLattice< std::pair< ThreeVector, ThreeVector >> *em_lattice, const ExperimentParameters ¶meters)
Calculate the total mean field energy of the system; this will be printed to the screen when SMASH is...
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.
bool are_particles_identical_at_given_time(const ParticleData &p1, const ParticleData &p2, double time)
Utility function to compare two ParticleData instances with respect to their PDG code,...
void update_lattice_accumulating_ensembles(RectangularLattice< T > *lat, const LatticeUpdate update, const DensityType dens_type, const DensityParameters &par, const std::vector< Particles > &ensembles, const bool compute_gradient)
Updates the contents on the lattice when ensembles are used.
void validate_and_adjust_particle_list(ParticleList &particle_list)
Validate a particle list adjusting each particle to be a valid SMASH particle.
bool all_of(Container &&c, UnaryPredicate &&p)
Convenience wrapper for std::all_of that operates on a complete container.
std::string to_string(ThermodynamicQuantity quantity)
Convert a ThermodynamicQuantity enum value to its corresponding string.
constexpr double nucleon_mass
Nucleon mass in GeV.
void update_lattice(RectangularLattice< DensityOnLattice > *lat, RectangularLattice< FourVector > *old_jmu, RectangularLattice< FourVector > *new_jmu, RectangularLattice< std::array< FourVector, 4 >> *four_grad_lattice, const LatticeUpdate update, const DensityType dens_type, const DensityParameters &par, const std::vector< Particles > &ensembles, const double time_step, const bool compute_gradient)
Updates the contents on the lattice of DensityOnLattice type.
LatticeUpdate
Enumerator option for lattice updates.
std::ostream & operator<<(std::ostream &out, const Experiment< Modus > &e)
Creates a verbose textual description of the setup of the Experiment.
Potentials * pot_pointer
Pointer to a Potential class.
static constexpr int LMain
constexpr double really_small
Numerical error tolerance.
std::string join(const std::vector< std::string > &v, std::string_view delim)
Join strings using delimiter.
RectangularLattice< FourVector > * UB_lat_pointer
Pointer to the skyrme potential on the lattice.
std::chrono::time_point< std::chrono::system_clock > SystemTimePoint
Type (alias) that is used to store the current time.
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.
Structure to contain custom data for output.
Struct containing the type of the metric and the expansion parameter of the metric.
Exception class that is thrown if an invalid modus is requested from the Experiment factory.
Exception class that is thrown if the requested output path in the Experiment factory is not existing...
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.
RivetOutputParameters rivet_parameters
Rivet specfic parameters.