234 const std::vector<bool>& nucleon_has_interacted,
int N_tot,
int N_proj)
235 : coll_crit_(config.take({
"Collision_Term",
"Collision_Criterion"},
238 config.take({
"Collision_Term",
"Elastic_Cross_Section"}, -1.)),
239 testparticles_(parameters.testparticles),
240 isotropic_(config.take({
"Collision_Term",
"Isotropic"},
false)),
241 two_to_one_(parameters.two_to_one),
242 incl_set_(parameters.included_2to2),
243 low_snn_cut_(parameters.low_snn_cut),
244 strings_switch_(parameters.strings_switch),
245 use_AQM_(parameters.use_AQM),
246 strings_with_probability_(parameters.strings_with_probability),
247 nnbar_treatment_(parameters.nnbar_treatment),
248 nucleon_has_interacted_(nucleon_has_interacted),
251 string_formation_time_(config.take(
252 {
"Collision_Term",
"String_Parameters",
"Formation_Time"}, 1.)) {
254 !(is_constant_elastic_isotropic())) {
255 throw std::invalid_argument(
256 "The stochastic collision criterion is only supported for elastic (and "
257 "isotropic)\n2-to-2 reactions of one particle species. Change you "
258 "config accordingly.");
260 if (is_constant_elastic_isotropic()) {
262 "Constant elastic isotropic cross-section mode:",
" using ",
263 elastic_parameter_,
" mb as maximal cross-section.");
265 if (strings_switch_) {
266 auto subconfig = config[
"Collision_Term"][
"String_Parameters"];
267 string_process_interface_ = make_unique<StringProcess>(
268 subconfig.take({
"String_Tension"}, 1.0), string_formation_time_,
269 subconfig.take({
"Gluon_Beta"}, 0.5),
270 subconfig.take({
"Gluon_Pmin"}, 0.001),
271 subconfig.take({
"Quark_Alpha"}, 2.0),
272 subconfig.take({
"Quark_Beta"}, 7.0),
273 subconfig.take({
"Strange_Supp"}, 0.16),
274 subconfig.take({
"Diquark_Supp"}, 0.036),
275 subconfig.take({
"Sigma_Perp"}, 0.42),
276 subconfig.take({
"StringZ_A_Leading"}, 0.2),
277 subconfig.take({
"StringZ_B_Leading"}, 2.0),
278 subconfig.take({
"StringZ_A"}, 2.0), subconfig.take({
"StringZ_B"}, 0.55),
279 subconfig.take({
"String_Sigma_T"}, 0.5),
280 subconfig.take({
"Form_Time_Factor"}, 1.0),
281 subconfig.take({
"Mass_Dependent_Formation_Times"},
false),
282 subconfig.take({
"Prob_proton_to_d_uu"}, 1. / 3.),
283 subconfig.take({
"Separate_Fragment_Baryon"},
true),
284 subconfig.take({
"Popcorn_Rate"}, 0.15));
290 const std::vector<FourVector>& beam_momentum,
const double cell_vol)
const {
296 assert(data_a.
id() >= 0);
297 assert(data_b.
id() >= 0);
307 const double time_until_collision =
311 if (time_until_collision < 0. || time_until_collision >= dt) {
316 ScatterActionPtr act = make_unique<ScatterAction>(
334 const double xs = act->cross_section() *
fm2_mb;
337 const double m1 = act->incoming_particles()[0].effective_mass();
338 const double m1_sqr = m1 * m1;
339 const double m2 = act->incoming_particles()[1].effective_mass();
340 const double m2_sqr = m2 * m2;
341 const double e1 = act->incoming_particles()[0].momentum().x0();
342 const double e2 = act->incoming_particles()[1].momentum().x0();
343 const double m_s = act->mandelstam_s();
344 const double lambda = (m_s - m1_sqr - m2_sqr) * (m_s - m1_sqr - m2_sqr) -
345 4. * m1_sqr * m2_sqr;
346 const double v_rel = std::sqrt(lambda) / (2. * e1 * e2);
349 const double p_22 = xs * v_rel * dt / (cell_vol *
testparticles_);
352 "Stochastic collison criterion parameters:\np_22 = ", p_22,
353 ", xs = ", xs,
", v_rel = ", v_rel,
", dt = ", dt,
357 std::stringstream err;
358 err <<
"Probability larger than 1 for stochastic rates. ( P = " << p_22
359 <<
" )\nUse smaller timesteps.";
360 throw std::runtime_error(err.str());
365 if (random_no > p_22) {
380 const double distance_squared = act->transverse_distance_sqr();
393 double cross_section_criterion = act->cross_section() *
fm2_mb * M_1_PI /
401 if (distance_squared >= cross_section_criterion) {
406 "\n ", data_a,
"\n<-> ", data_b);
411 return std::move(act);
415 const ParticleList& search_list,
double dt,
const double cell_vol,
416 const std::vector<FourVector>& beam_momentum)
const {
417 std::vector<ActionPtr> actions;
420 if (p1.id() < p2.id()) {
424 actions.push_back(std::move(act));
433 const ParticleList& search_list,
const ParticleList& neighbors_list,
434 double dt,
const std::vector<FourVector>& beam_momentum)
const {
435 std::vector<ActionPtr> actions;
442 assert(p1.id() != p2.id());
446 actions.push_back(std::move(act));
454 const ParticleList& search_list,
const Particles& surrounding_list,
455 double dt,
const std::vector<FourVector>& beam_momentum)
const {
456 std::vector<ActionPtr> actions;
464 auto result = std::find_if(
465 search_list.begin(), search_list.end(),
467 if (result != search_list.end()) {
474 actions.push_back(std::move(act));
482 constexpr
double time = 0.0;
485 const size_t N_pairs = N_isotypes * (N_isotypes - 1) / 2;
487 std::cout << N_isotypes <<
" iso-particle types." << std::endl;
488 std::cout <<
"They can make " << N_pairs <<
" pairs." << std::endl;
489 std::vector<double> momentum_scan_list = {0.1, 0.3, 0.5, 1.0,
490 2.0, 3.0, 5.0, 10.0};
493 if (&A_isotype > &B_isotype) {
496 bool any_nonzero_cs =
false;
497 std::vector<std::string> r_list;
500 if (A_type > B_type) {
504 for (
auto mom : momentum_scan_list) {
505 A.set_4momentum(A.pole_mass(), mom, 0.0, 0.0);
507 ScatterActionPtr act = make_unique<ScatterAction>(
516 const double total_cs = act->cross_section();
517 if (total_cs <= 0.0) {
520 any_nonzero_cs =
true;
521 for (
const auto& channel : act->collision_channels()) {
522 const auto type = channel->get_type();
526 r = A_type->name() + B_type->name() + std::string(
" → strings");
530 ? std::string(
" (el)")
532 ? std::string(
" (inel)")
533 : std::string(
" (?)");
534 r = A_type->name() + B_type->name() + std::string(
" → ") +
535 channel->particle_types()[0]->name() +
536 channel->particle_types()[1]->name() + r_type;
544 std::sort(r_list.begin(), r_list.end());
545 r_list.erase(std::unique(r_list.begin(), r_list.end()), r_list.end());
546 if (any_nonzero_cs) {
547 for (
auto r : r_list) {
549 if (r_list.back() != r) {
553 std::cout << std::endl;
583 namespace decaytree {
631 Node(
const std::string& name,
double weight,
632 ParticleTypePtrList&& initial_particles,
633 ParticleTypePtrList&& final_particles, ParticleTypePtrList&& state,
634 std::vector<Node>&& children)
654 ParticleTypePtrList&& initial_particles,
655 ParticleTypePtrList&& final_particles) {
657 ParticleTypePtrList state(
state_);
658 for (
const auto&
p : initial_particles) {
659 state.erase(std::find(state.begin(), state.end(),
p));
661 for (
const auto&
p : final_particles) {
665 std::sort(state.begin(), state.end(),
667 return a->
name() < b->name();
670 Node new_node(name, weight, std::move(initial_particles),
671 std::move(final_particles), std::move(state), {});
672 children_.emplace_back(std::move(new_node));
683 std::vector<FinalStateCrossSection> result;
696 for (uint64_t i = 0; i < depth; i++) {
701 child.print_helper(depth + 1);
717 uint64_t depth, std::vector<FinalStateCrossSection>& result,
718 const std::string& name,
double weight,
719 bool show_intermediate_states =
false)
const {
727 std::string new_name;
730 if (show_intermediate_states) {
732 if (!new_name.empty()) {
740 for (
const auto& s :
state_) {
741 new_name += s->name();
744 if (show_intermediate_states) {
753 child.final_state_cross_sections_helper(depth + 1, result, new_name,
754 weight, show_intermediate_states);
768 const DecayBranchPtr& decay,
769 ParticleTypePtrList& final_state) {
770 std::stringstream name;
771 name <<
"[" << res_name <<
"->";
772 for (
const auto&
p : decay->particle_types()) {
774 final_state.push_back(
p);
798 uint32_t n_unstable = 0;
799 double sqrts_minus_masses = sqrts;
804 sqrts_minus_masses -= ptype->
mass();
807 n_unstable != 0 ? 1. / static_cast<double>(n_unstable) : 1.;
811 const double sqrts_decay = sqrts_minus_masses + ptype->
mass();
812 bool can_decay =
false;
817 double final_state_mass = 0.;
818 for (
const auto&
p : decay->particle_types()) {
819 final_state_mass +=
p->mass();
821 if (final_state_mass > sqrts_decay) {
826 ParticleTypePtrList parts;
828 auto& new_node = node.
add_action(name, norm * decay->weight(), {ptype},
849 static void deduplicate(std::vector<FinalStateCrossSection>& final_state_xs) {
850 std::sort(final_state_xs.begin(), final_state_xs.end(),
853 auto current = final_state_xs.begin();
854 while (current != final_state_xs.end()) {
855 auto adjacent = std::adjacent_find(
856 current, final_state_xs.end(),
858 return a.
name_ == b.name_;
861 if (adjacent != final_state_xs.end()) {
862 adjacent->cross_section_ += (adjacent + 1)->cross_section_;
863 final_state_xs.erase(adjacent + 1);
870 bool final_state, std::vector<double>& plab)
const {
871 typedef std::vector<std::pair<double, double>> xs_saver;
872 std::map<std::string, xs_saver> xs_dump;
873 std::map<std::string, double> outgoing_total_mass;
876 int n_momentum_points = 200;
877 constexpr
double momentum_step = 0.02;
884 if (plab.size() > 0) {
885 n_momentum_points = plab.size();
887 std::sort(plab.begin(), plab.end());
888 plab.erase(std::unique(plab.begin(), plab.end()), plab.end());
890 for (
int i = 0; i < n_momentum_points; i++) {
892 if (plab.size() > 0) {
895 momentum = momentum_step * (i + 1);
897 a_data.set_4momentum(m_a, momentum, 0.0, 0.0);
899 const double sqrts = (a_data.momentum() + b_data.
momentum()).abs();
900 const ParticleList incoming = {a_data, b_data};
901 ScatterActionPtr act = make_unique<ScatterAction>(
910 {&a, &b}, {&a, &b}, {});
911 const CollisionBranchList& processes = act->collision_channels();
912 for (
const auto& process : processes) {
913 const double xs = process->weight();
918 std::stringstream process_description_stream;
919 process_description_stream << *process;
920 const std::string& description = process_description_stream.str();
922 for (
const auto& ptype : process->particle_types()) {
923 m_tot += ptype->mass();
925 outgoing_total_mass[description] = m_tot;
926 if (!xs_dump[description].empty() &&
927 std::abs(xs_dump[description].back().first - sqrts) <
929 xs_dump[description].back().second += xs;
931 xs_dump[description].push_back(std::make_pair(sqrts, xs));
934 std::stringstream process_description_stream;
935 process_description_stream << *process;
936 const std::string& description = process_description_stream.str();
937 ParticleTypePtrList initial_particles = {&a, &b};
938 ParticleTypePtrList final_particles = process->particle_types();
940 tree.add_action(description, xs, std::move(initial_particles),
941 std::move(final_particles));
945 xs_dump[
"total"].push_back(std::make_pair(sqrts, act->cross_section()));
947 outgoing_total_mass[
"total"] = -1.0;
950 auto final_state_xs = tree.final_state_cross_sections();
952 for (
const auto&
p : final_state_xs) {
960 outgoing_total_mass[
p.name_] =
p.mass_;
961 xs_dump[
p.name_].push_back(std::make_pair(sqrts,
p.cross_section_));
968 for (
auto it = begin(xs_dump); it != end(xs_dump);) {
970 const xs_saver& xs = (*it).second;
972 for (
const auto&
p : xs) {
976 it = xs_dump.erase(it);
983 std::vector<std::string> all_channels;
984 for (
const auto channel : xs_dump) {
985 all_channels.push_back(channel.first);
987 std::sort(all_channels.begin(), all_channels.end(),
988 [&](
const std::string& str_a,
const std::string& str_b) {
989 return outgoing_total_mass[str_a] < outgoing_total_mass[str_b];
993 std::cout <<
"# Dumping partial " << a.
name() << b.
name()
994 <<
" cross-sections in mb, energies in GeV" << std::endl;
995 std::cout <<
" sqrt_s";
999 for (
const auto channel : all_channels) {
1002 std::cout << std::endl;
1005 for (
int i = 0; i < n_momentum_points; i++) {
1007 if (plab.size() > 0) {
1010 momentum = momentum_step * (i + 1);
1012 a_data.set_4momentum(m_a, momentum, 0.0, 0.0);
1014 const double sqrts = (a_data.momentum() + b_data.
momentum()).abs();
1015 std::printf(
"%9.6f", sqrts);
1016 for (
const auto channel : all_channels) {
1017 const xs_saver energy_and_xs = xs_dump[channel];
1019 for (; j < energy_and_xs.size() && energy_and_xs[j].first < sqrts; j++) {
1022 if (j < energy_and_xs.size() &&
1023 std::abs(energy_and_xs[j].first - sqrts) <
really_small) {
1024 xs = energy_and_xs[j].second;
1026 std::printf(
"%16.6f", xs);