298 const std::vector<bool>& nucleon_has_interacted,
int N_tot,
int N_proj)
299 : coll_crit_(parameters.coll_crit),
301 config.take({
"Collision_Term",
"Elastic_Cross_Section"}, -1.)),
302 testparticles_(parameters.testparticles),
303 isotropic_(config.take({
"Collision_Term",
"Isotropic"},
false)),
304 two_to_one_(parameters.two_to_one),
305 incl_set_(parameters.included_2to2),
306 incl_multi_set_(parameters.included_multi),
307 scale_xs_(parameters.scale_xs),
308 additional_el_xs_(parameters.additional_el_xs),
309 low_snn_cut_(parameters.low_snn_cut),
310 strings_switch_(parameters.strings_switch),
311 use_AQM_(parameters.use_AQM),
312 strings_with_probability_(parameters.strings_with_probability),
313 nnbar_treatment_(parameters.nnbar_treatment),
314 box_length_(parameters.box_length),
315 nucleon_has_interacted_(nucleon_has_interacted),
318 string_formation_time_(config.take(
319 {
"Collision_Term",
"String_Parameters",
"Formation_Time"}, 1.)),
320 maximum_cross_section_(parameters.maximum_cross_section),
321 only_warn_for_high_prob_(config.take(
322 {
"Collision_Term",
"Only_Warn_For_High_Probability"},
false)) {
323 if (is_constant_elastic_isotropic()) {
325 "Constant elastic isotropic cross-section mode:",
" using ",
326 elastic_parameter_,
" mb as maximal cross-section.");
329 throw std::invalid_argument(
330 "Multi-body reactions (like e.g. 3->1 or 3->2) are only possible with "
333 "criterion. Change your config accordingly.");
339 throw std::invalid_argument(
340 "To prevent double counting it is not possible to enable deuteron 3->2 "
341 "reactions\nand reactions involving the d' at the same time\ni.e. to "
342 "include `Deuteron_3to2` in `Multi_Particle_Reactions` and\n "
343 "\"PiDeuteron_to_pidprime\" "
344 "or \"NDeuteron_to_Ndprime\" in `Included_2to2` at the same time.\n"
345 "Change your config accordingly.");
348 if (strings_switch_) {
349 auto subconfig = config[
"Collision_Term"][
"String_Parameters"];
350 string_process_interface_ = make_unique<StringProcess>(
351 subconfig.take({
"String_Tension"}, 1.0), string_formation_time_,
352 subconfig.take({
"Gluon_Beta"}, 0.5),
353 subconfig.take({
"Gluon_Pmin"}, 0.001),
354 subconfig.take({
"Quark_Alpha"}, 2.0),
355 subconfig.take({
"Quark_Beta"}, 7.0),
356 subconfig.take({
"Strange_Supp"}, 0.16),
357 subconfig.take({
"Diquark_Supp"}, 0.036),
358 subconfig.take({
"Sigma_Perp"}, 0.42),
359 subconfig.take({
"StringZ_A_Leading"}, 0.2),
360 subconfig.take({
"StringZ_B_Leading"}, 2.0),
361 subconfig.take({
"StringZ_A"}, 2.0), subconfig.take({
"StringZ_B"}, 0.55),
362 subconfig.take({
"String_Sigma_T"}, 0.5),
363 subconfig.take({
"Form_Time_Factor"}, 1.0),
364 subconfig.take({
"Mass_Dependent_Formation_Times"},
false),
365 subconfig.take({
"Prob_proton_to_d_uu"}, 1. / 3.),
366 subconfig.take({
"Separate_Fragment_Baryon"},
true),
367 subconfig.take({
"Popcorn_Rate"}, 0.15));
373 const std::vector<FourVector>& beam_momentum,
374 const double gcell_vol)
const {
380 assert(data_a.
id() >= 0);
381 assert(data_b.
id() >= 0);
397 const double time_until_collision =
401 if (time_until_collision < 0. || time_until_collision >= dt) {
406 ScatterActionPtr act = make_unique<ScatterAction>(
411 act->set_stochastic_pos_idx();
419 const double distance_squared =
421 ? act->transverse_distance_sqr()
423 ? act->cov_transverse_distance_sqr()
447 const double v_rel = act->relative_velocity();
450 const double prob = xs * v_rel * dt / gcell_vol;
453 "Stochastic collison criterion parameters (2-particles):\nprob = ",
454 prob,
", xs = ", xs,
", v_rel = ", v_rel,
", dt = ", dt,
455 ", gcell_vol = ", gcell_vol,
", testparticles = ",
testparticles_);
458 std::stringstream err;
459 err <<
"Probability larger than 1 for stochastic rates. ( P_22 = " << prob
460 <<
" )\nConsider using smaller timesteps.";
464 throw std::runtime_error(err.str());
470 if (random_no > prob) {
487 const double cross_section_criterion = xs * M_1_PI;
490 if (distance_squared >= cross_section_criterion) {
495 "\n ", data_a,
"\n<-> ", data_b);
500 return std::move(act);
504 const ParticleList& plist,
double dt,
const double gcell_vol)
const {
512 plist.begin(), plist.end(),
513 [&](
const ParticleData& data) { return data.id() < N_tot_; }) &&
515 plist.begin(), plist.end(),
516 [&](
const ParticleData& data) { return data.id() < N_proj_; }) ||
518 plist.begin(), plist.end(),
519 [&](
const ParticleData& data) { return data.id() >= N_proj_; })) &&
520 std::none_of(plist.begin(), plist.end(), [&](
const ParticleData& data) {
521 return nucleon_has_interacted_[data.id()];
538 ScatterActionMultiPtr act =
539 make_unique<ScatterActionMulti>(plist, time_until_collision);
541 act->set_stochastic_pos_idx();
550 act->get_total_weight() / std::pow(
testparticles_, plist.size() - 1);
554 std::stringstream err;
555 err <<
"Probability larger than 1 for stochastic rates. ( P_nm = " << prob
556 <<
" )\nConsider using smaller timesteps.";
560 throw std::runtime_error(err.str());
566 if (random_no > prob) {
570 return std::move(act);
574 const ParticleList& search_list,
double dt,
const double gcell_vol,
575 const std::vector<FourVector>& beam_momentum)
const {
576 std::vector<ActionPtr> actions;
580 if (p1.id() < p2.id()) {
584 actions.push_back(std::move(act));
590 if (p1.id() < p2.id() && p2.id() < p3.id()) {
594 actions.push_back(std::move(act));
606 const ParticleList& search_list,
const ParticleList& neighbors_list,
607 double dt,
const std::vector<FourVector>& beam_momentum)
const {
608 std::vector<ActionPtr> actions;
615 assert(p1.id() != p2.id());
619 actions.push_back(std::move(act));
627 const ParticleList& search_list,
const Particles& surrounding_list,
628 double dt,
const std::vector<FourVector>& beam_momentum)
const {
629 std::vector<ActionPtr> actions;
637 auto result = std::find_if(
638 search_list.begin(), search_list.end(),
640 if (result != search_list.end()) {
647 actions.push_back(std::move(act));
655 constexpr
double time = 0.0;
658 const size_t N_pairs = N_isotypes * (N_isotypes - 1) / 2;
660 std::cout << N_isotypes <<
" iso-particle types." << std::endl;
661 std::cout <<
"They can make " << N_pairs <<
" pairs." << std::endl;
662 std::vector<double> momentum_scan_list = {0.1, 0.3, 0.5, 1.0,
663 2.0, 3.0, 5.0, 10.0};
666 if (&A_isotype > &B_isotype) {
669 bool any_nonzero_cs =
false;
670 std::vector<std::string> r_list;
673 if (A_type > B_type) {
677 for (
auto mom : momentum_scan_list) {
678 A.set_4momentum(A.pole_mass(), mom, 0.0, 0.0);
680 ScatterActionPtr act = make_unique<ScatterAction>(
685 act->add_all_scatterings(
690 const double total_cs = act->cross_section();
691 if (total_cs <= 0.0) {
694 any_nonzero_cs =
true;
695 for (
const auto& channel : act->collision_channels()) {
696 const auto type = channel->get_type();
700 r = A_type->name() + B_type->name() + std::string(
" → strings");
704 ? std::string(
" (el)")
706 ? std::string(
" (inel)")
707 : std::string(
" (?)");
708 r = A_type->name() + B_type->name() + std::string(
" → ") +
709 channel->particle_types()[0]->name() +
710 channel->particle_types()[1]->name() + r_type;
718 std::sort(r_list.begin(), r_list.end());
719 r_list.erase(std::unique(r_list.begin(), r_list.end()), r_list.end());
720 if (any_nonzero_cs) {
721 for (
auto r : r_list) {
723 if (r_list.back() != r) {
727 std::cout << std::endl;
757 namespace decaytree {
805 Node(
const std::string& name,
double weight,
806 ParticleTypePtrList&& initial_particles,
807 ParticleTypePtrList&& final_particles, ParticleTypePtrList&& state,
808 std::vector<Node>&& children)
828 ParticleTypePtrList&& initial_particles,
829 ParticleTypePtrList&& final_particles) {
831 ParticleTypePtrList state(
state_);
832 for (
const auto&
p : initial_particles) {
833 state.erase(std::find(state.begin(), state.end(),
p));
835 for (
const auto&
p : final_particles) {
839 std::sort(state.begin(), state.end(),
841 return a->name() < b->name();
844 Node new_node(name, weight, std::move(initial_particles),
845 std::move(final_particles), std::move(state), {});
846 children_.emplace_back(std::move(new_node));
857 std::vector<FinalStateCrossSection> result;
870 for (uint64_t i = 0; i < depth; i++) {
875 child.print_helper(depth + 1);
891 uint64_t depth, std::vector<FinalStateCrossSection>& result,
892 const std::string& name,
double weight,
893 bool show_intermediate_states =
false)
const {
901 std::string new_name;
904 if (show_intermediate_states) {
906 if (!new_name.empty()) {
914 for (
const auto& s :
state_) {
915 new_name += s->name();
918 if (show_intermediate_states) {
927 child.final_state_cross_sections_helper(depth + 1, result, new_name,
928 weight, show_intermediate_states);
942 const DecayBranchPtr& decay,
943 ParticleTypePtrList& final_state) {
944 std::stringstream name;
945 name <<
"[" << res_name <<
"->";
946 for (
const auto&
p : decay->particle_types()) {
948 final_state.push_back(
p);
972 uint32_t n_unstable = 0;
973 double sqrts_minus_masses = sqrts;
978 sqrts_minus_masses -= ptype->
mass();
981 n_unstable != 0 ? 1. /
static_cast<double>(n_unstable) : 1.;
985 const double sqrts_decay = sqrts_minus_masses + ptype->
mass();
986 bool can_decay =
false;
991 double final_state_mass = 0.;
992 for (
const auto&
p : decay->particle_types()) {
993 final_state_mass +=
p->mass();
995 if (final_state_mass > sqrts_decay) {
1000 ParticleTypePtrList parts;
1002 auto& new_node = node.
add_action(name, norm * decay->weight(), {ptype},
1023 static void deduplicate(std::vector<FinalStateCrossSection>& final_state_xs) {
1024 std::sort(final_state_xs.begin(), final_state_xs.end(),
1027 auto current = final_state_xs.begin();
1028 while (current != final_state_xs.end()) {
1029 auto adjacent = std::adjacent_find(
1030 current, final_state_xs.end(),
1032 return a.name_ == b.name_;
1035 if (adjacent != final_state_xs.end()) {
1036 adjacent->cross_section_ += (adjacent + 1)->cross_section_;
1037 final_state_xs.erase(adjacent + 1);
1044 bool final_state, std::vector<double>& plab)
const {
1045 typedef std::vector<std::pair<double, double>> xs_saver;
1046 std::map<std::string, xs_saver> xs_dump;
1047 std::map<std::string, double> outgoing_total_mass;
1050 int n_momentum_points = 200;
1051 constexpr
double momentum_step = 0.02;
1058 if (plab.size() > 0) {
1059 n_momentum_points = plab.size();
1061 std::sort(plab.begin(), plab.end());
1062 plab.erase(std::unique(plab.begin(), plab.end()), plab.end());
1064 for (
int i = 0; i < n_momentum_points; i++) {
1066 if (plab.size() > 0) {
1069 momentum = momentum_step * (i + 1);
1071 a_data.set_4momentum(m_a, momentum, 0.0, 0.0);
1073 const double sqrts = (a_data.momentum() + b_data.
momentum()).abs();
1074 const ParticleList incoming = {a_data, b_data};
1075 ScatterActionPtr act = make_unique<ScatterAction>(
1085 {&a, &b}, {&a, &b}, {});
1086 const CollisionBranchList& processes = act->collision_channels();
1087 for (
const auto& process : processes) {
1088 const double xs = process->weight();
1093 std::stringstream process_description_stream;
1094 process_description_stream << *process;
1095 const std::string& description = process_description_stream.str();
1097 for (
const auto& ptype : process->particle_types()) {
1098 m_tot += ptype->mass();
1100 outgoing_total_mass[description] = m_tot;
1101 if (!xs_dump[description].empty() &&
1102 std::abs(xs_dump[description].back().first - sqrts) <
1104 xs_dump[description].back().second += xs;
1106 xs_dump[description].push_back(std::make_pair(sqrts, xs));
1109 std::stringstream process_description_stream;
1110 process_description_stream << *process;
1111 const std::string& description = process_description_stream.str();
1112 ParticleTypePtrList initial_particles = {&a, &b};
1113 ParticleTypePtrList final_particles = process->particle_types();
1114 auto& process_node =
1115 tree.
add_action(description, xs, std::move(initial_particles),
1116 std::move(final_particles));
1120 xs_dump[
"total"].push_back(std::make_pair(sqrts, act->cross_section()));
1122 outgoing_total_mass[
"total"] = -1.0;
1127 for (
const auto&
p : final_state_xs) {
1132 if (
p.name_ ==
"") {
1135 outgoing_total_mass[
p.name_] =
p.mass_;
1136 xs_dump[
p.name_].push_back(std::make_pair(sqrts,
p.cross_section_));
1143 for (
auto it = begin(xs_dump); it != end(xs_dump);) {
1145 const xs_saver& xs = (*it).second;
1147 for (
const auto&
p : xs) {
1151 it = xs_dump.erase(it);
1158 std::vector<std::string> all_channels;
1159 for (
const auto& channel : xs_dump) {
1160 all_channels.push_back(channel.first);
1162 std::sort(all_channels.begin(), all_channels.end(),
1163 [&](
const std::string& str_a,
const std::string& str_b) {
1164 return outgoing_total_mass[str_a] < outgoing_total_mass[str_b];
1168 std::cout <<
"# Dumping partial " << a.
name() << b.
name()
1169 <<
" cross-sections in mb, energies in GeV" << std::endl;
1170 std::cout <<
" sqrt_s";
1174 for (
const auto& channel : all_channels) {
1177 std::cout << std::endl;
1180 for (
int i = 0; i < n_momentum_points; i++) {
1182 if (plab.size() > 0) {
1185 momentum = momentum_step * (i + 1);
1187 a_data.set_4momentum(m_a, momentum, 0.0, 0.0);
1189 const double sqrts = (a_data.momentum() + b_data.
momentum()).abs();
1190 std::printf(
"%9.6f", sqrts);
1191 for (
const auto& channel : all_channels) {
1192 const xs_saver energy_and_xs = xs_dump[channel];
1194 for (; j < energy_and_xs.size() && energy_and_xs[j].first < sqrts; j++) {
1197 if (j < energy_and_xs.size() &&
1198 std::abs(energy_and_xs[j].first - sqrts) <
really_small) {
1199 xs = energy_and_xs[j].second;
1201 std::printf(
"%16.6f", xs);