Version: SMASH-3.3
scatteractionmulti.cc
Go to the documentation of this file.
1 /*
2  *
3  * Copyright (c) 2020-2025
4  * SMASH Team
5  *
6  * GNU General Public License (GPLv3 or later)
7  *
8  */
9 
11 
12 #include <map>
13 
14 #include "gsl/gsl_sf_ellint.h"
15 
16 #include "smash/crosssections.h"
17 #include "smash/integrate.h"
18 #include "smash/logging.h"
19 #include "smash/parametrizations.h"
20 #include "smash/pow.h"
21 
22 namespace smash {
23 static constexpr int LScatterActionMulti = LogArea::ScatterActionMulti::id;
24 
26  const ParticleList& in_plist, double time,
27  const SpinInteractionType spin_interaction_type)
28  : Action(in_plist, time),
29  total_probability_(0.),
30  spin_interaction_type_(spin_interaction_type) {}
31 
32 void ScatterActionMulti::add_reaction(CollisionBranchPtr p) {
33  add_process<CollisionBranch>(p, reaction_channels_, total_probability_);
34 }
35 
36 void ScatterActionMulti::add_reactions(CollisionBranchList pv) {
37  add_processes<CollisionBranch>(std::move(pv), reaction_channels_,
39 }
40 
42  double xsec_scaling = 1.0;
43  for (const ParticleData& in_part : incoming_particles_) {
44  xsec_scaling *= in_part.xsec_scaling_factor();
45  }
46  return total_probability_ * xsec_scaling;
47 }
48 
50  double xsec_scaling = 1.0;
51  for (const ParticleData& in_part : incoming_particles_) {
52  xsec_scaling *= in_part.xsec_scaling_factor();
53  }
54  return partial_probability_ * xsec_scaling;
55 }
56 
58  double dt, const double gcell_vol,
59  const MultiParticleReactionsBitSet incl_multi) {
60  // 3 -> m
61  if (incoming_particles_.size() == 3) {
62  // 3 -> 1
63  if (incl_multi[IncludedMultiParticleReactions::Meson_3to1] == 1) {
65  incoming_particles_[2])) {
66  // 3pi -> omega
67  const ParticleTypePtr type_omega = ParticleType::try_find(0x223);
68  if (type_omega) {
69  add_reaction(std::make_unique<CollisionBranch>(
70  *type_omega,
71  probability_three_to_one(*type_omega, dt, gcell_vol,
72  type_omega->spin_degeneracy()),
74  }
75  // 3pi -> phi
76  const ParticleTypePtr type_phi = ParticleType::try_find(0x333);
77  if (type_phi) {
78  add_reaction(std::make_unique<CollisionBranch>(
79  *type_phi,
80  probability_three_to_one(*type_phi, dt, gcell_vol,
81  type_phi->spin_degeneracy()),
83  }
85  incoming_particles_[2])) {
86  // eta2pi -> eta-prime
87  const ParticleTypePtr type_eta_prime = ParticleType::try_find(0x331);
88 
89  int sym_factor_in = 1;
90  if (incoming_particles_[0].type() == incoming_particles_[1].type() ||
91  incoming_particles_[1].type() == incoming_particles_[2].type() ||
92  incoming_particles_[2].type() == incoming_particles_[0].type()) {
93  sym_factor_in = 2; // 2 factorial
94  }
95 
96  if (type_eta_prime) {
97  add_reaction(std::make_unique<CollisionBranch>(
98  *type_eta_prime,
100  *type_eta_prime, dt, gcell_vol,
101  sym_factor_in * type_eta_prime->spin_degeneracy()),
103  }
104  }
105  }
106  // 3 -> 2
107  if (incl_multi[IncludedMultiParticleReactions::Deuteron_3to2] == 1) {
108  const PdgCode pdg_a = incoming_particles_[0].pdgcode();
109  const PdgCode pdg_b = incoming_particles_[1].pdgcode();
110  const PdgCode pdg_c = incoming_particles_[2].pdgcode();
111  const ParticleTypePtr type_deuteron =
113  const ParticleTypePtr type_anti_deuteron =
115 
116  const int spin_factor_inc = pdg_a.spin_degeneracy() *
117  pdg_b.spin_degeneracy() *
118  pdg_c.spin_degeneracy();
119 
120  if (type_deuteron && type_anti_deuteron) {
121  // πpn → πd
122  if ((pdg_a.is_pion() && pdg_b == pdg::p && pdg_c == pdg::n) ||
123  (pdg_a.is_pion() && pdg_b == pdg::n && pdg_c == pdg::p) ||
124  (pdg_a == pdg::p && pdg_b.is_pion() && pdg_c == pdg::n) ||
125  (pdg_a == pdg::n && pdg_b.is_pion() && pdg_c == pdg::p) ||
126  (pdg_a == pdg::p && pdg_b == pdg::n && pdg_c.is_pion()) ||
127  (pdg_a == pdg::n && pdg_b == pdg::p && pdg_c.is_pion())) {
128  // Get type of incoming π
129  ParticleList::iterator it = std::find_if(
131  [](ParticleData x) { return x.is_pion(); });
132  const ParticleType& type_pi = it->type();
133 
134  const double spin_degn =
135  react_degen_factor(spin_factor_inc, type_pi.spin_degeneracy(),
136  type_deuteron->spin_degeneracy());
137 
138  add_reaction(std::make_unique<CollisionBranch>(
139  type_pi, *type_deuteron,
140  probability_three_to_two(type_pi, *type_deuteron, dt, gcell_vol,
141  spin_degn),
143  }
144 
145  // πp̅n̅ → πd̅
146  if ((pdg_a.is_pion() && pdg_b == -pdg::p && pdg_c == -pdg::n) ||
147  (pdg_a.is_pion() && pdg_b == -pdg::n && pdg_c == -pdg::p) ||
148  (pdg_a == -pdg::p && pdg_b.is_pion() && pdg_c == -pdg::n) ||
149  (pdg_a == -pdg::n && pdg_b.is_pion() && pdg_c == -pdg::p) ||
150  (pdg_a == -pdg::p && pdg_b == -pdg::n && pdg_c.is_pion()) ||
151  (pdg_a == -pdg::n && pdg_b == -pdg::p && pdg_c.is_pion())) {
152  // Get type of incoming π
153  ParticleList::iterator it = std::find_if(
155  [](ParticleData x) { return x.is_pion(); });
156  const ParticleType& type_pi = it->type();
157 
158  const double spin_degn =
159  react_degen_factor(spin_factor_inc, type_pi.spin_degeneracy(),
160  type_anti_deuteron->spin_degeneracy());
161 
162  add_reaction(std::make_unique<CollisionBranch>(
163  type_pi, *type_anti_deuteron,
164  probability_three_to_two(type_pi, *type_anti_deuteron, dt,
165  gcell_vol, spin_degn),
167  }
168 
169  // Nnp → Nd, N̅np → N̅d
170  if ((pdg_a.is_nucleon() && pdg_b == pdg::p && pdg_c == pdg::n) ||
171  (pdg_a.is_nucleon() && pdg_b == pdg::n && pdg_c == pdg::p) ||
172  (pdg_a == pdg::p && pdg_b.is_nucleon() && pdg_c == pdg::n) ||
173  (pdg_a == pdg::n && pdg_b.is_nucleon() && pdg_c == pdg::p) ||
174  (pdg_a == pdg::p && pdg_b == pdg::n && pdg_c.is_nucleon()) ||
175  (pdg_a == pdg::n && pdg_b == pdg::p && pdg_c.is_nucleon())) {
176  int symmetry_factor = 1; // already true for N̅np → N̅d case
177 
178  ParticleList::iterator it =
179  std::find_if(incoming_particles_.begin(),
180  incoming_particles_.end(), [](ParticleData x) {
181  return x.pdgcode().antiparticle_sign() == -1;
182  });
183  if (it == incoming_particles_.end()) {
184  /* Meaning no anti-N found by find_if,
185  * therefore not N̅np → N̅d, but Nnp → Nd. */
186  symmetry_factor = 2; // for Nnp → Nd (2 factorial)
187  // It is already clear here that we have a double of two N
188  if (pdg_a == pdg_b) {
189  it = incoming_particles_.begin();
190  } else {
191  // If a and b are not the double, then c has to be part of it
192  it = incoming_particles_.begin() + 2;
193  }
194  }
195  const ParticleType& type_N = it->type();
196 
197  const double spin_degn =
198  react_degen_factor(spin_factor_inc, type_N.spin_degeneracy(),
199  type_deuteron->spin_degeneracy());
200 
201  add_reaction(std::make_unique<CollisionBranch>(
202  type_N, *type_deuteron,
203  probability_three_to_two(type_N, *type_deuteron, dt, gcell_vol,
204  symmetry_factor * spin_degn),
206  }
207 
208  // Np̅n̅ → Nd̅, N̅p̅n̅ → N̅d̅
209  if ((pdg_a.is_nucleon() && pdg_b == -pdg::p && pdg_c == -pdg::n) ||
210  (pdg_a.is_nucleon() && pdg_b == -pdg::n && pdg_c == -pdg::p) ||
211  (pdg_a == -pdg::p && pdg_b.is_nucleon() && pdg_c == -pdg::n) ||
212  (pdg_a == -pdg::n && pdg_b.is_nucleon() && pdg_c == -pdg::p) ||
213  (pdg_a == -pdg::p && pdg_b == -pdg::n && pdg_c.is_nucleon()) ||
214  (pdg_a == -pdg::n && pdg_b == -pdg::p && pdg_c.is_nucleon())) {
215  int symmetry_factor = 1; // already true for Np̅n̅ → Nd̅ case
216 
217  ParticleList::iterator it =
218  std::find_if(incoming_particles_.begin(),
219  incoming_particles_.end(), [](ParticleData x) {
220  return x.pdgcode().antiparticle_sign() == 1;
221  });
222  if (it == incoming_particles_.end()) {
223  /* Meaning no N found by find_if,
224  * therefore not Np̅n̅ → Nd̅, but N̅p̅n̅ → N̅d̅. */
225  symmetry_factor = 2; // for N̅p̅n̅ → N̅d̅ (2 factorial)
226  // It is already clear here that we have a double of two N̅
227  if (pdg_a == pdg_b) {
228  it = incoming_particles_.begin();
229  } else {
230  // If a and b are not the double, then c has to be part of it
231  it = incoming_particles_.begin() + 2;
232  }
233  }
234  const ParticleType& type_N = it->type();
235 
236  const double spin_degn =
237  react_degen_factor(spin_factor_inc, type_N.spin_degeneracy(),
238  type_anti_deuteron->spin_degeneracy());
239 
240  add_reaction(std::make_unique<CollisionBranch>(
241  type_N, *type_anti_deuteron,
242  probability_three_to_two(type_N, *type_anti_deuteron, dt,
243  gcell_vol, symmetry_factor * spin_degn),
245  }
246  }
247  }
248  }
249  // 4 -> 2
250  if (incoming_particles_.size() == 4 &&
252  std::map<PdgCode, int> c; // counts incoming PdgCodes
253  int spin_factor_inc = 1;
254  for (const ParticleData& data : incoming_particles_) {
255  c[data.pdgcode()]++;
256  spin_factor_inc *= data.pdgcode().spin_degeneracy();
257  }
258  // Nucleons, antinucleons, and pions can catalyze
259  const int n_possible_catalysts_incoming =
260  c[pdg::n] + c[pdg::p] + c[-pdg::p] + c[-pdg::n] + c[pdg::pi_p] +
261  c[pdg::pi_z] + c[pdg::pi_m];
262 
263  for (PdgCode pdg_nucleus :
266  const ParticleTypePtr type_nucleus = ParticleType::try_find(pdg_nucleus);
267  // Nucleus can be formed if and only if:
268  // 1) Incoming particles contain enough components (like p, n, Lambda)
269  // 2) In (incoming particles - components) there is still a catalyst
270  // This is including the situation like nnpp. Can be that t(nnp) is formed
271  // and p is catalyst, can be that he-3(ppn) is formed and n is catalyst.
272  // Both reactions should be added.
273  const int n_nucleus_components_that_can_be_catalysts =
274  pdg_nucleus.nucleus_p() + pdg_nucleus.nucleus_ap() +
275  pdg_nucleus.nucleus_n() + pdg_nucleus.nucleus_an();
276  const bool incoming_contain_nucleus_components =
277  c[pdg::p] >= pdg_nucleus.nucleus_p() &&
278  c[-pdg::p] >= pdg_nucleus.nucleus_ap() &&
279  c[pdg::n] >= pdg_nucleus.nucleus_n() &&
280  c[-pdg::n] >= pdg_nucleus.nucleus_an() &&
281  c[pdg::Lambda] >= pdg_nucleus.nucleus_La() &&
282  c[-pdg::Lambda] >= pdg_nucleus.nucleus_aLa();
283  const bool can_form_nucleus =
284  type_nucleus && incoming_contain_nucleus_components &&
285  n_possible_catalysts_incoming -
286  n_nucleus_components_that_can_be_catalysts ==
287  1;
288 
289  if (!can_form_nucleus) {
290  continue;
291  }
292  // Find the catalyst
293  std::map<PdgCode, int> catalyst_count = c;
294  catalyst_count[pdg::p] -= pdg_nucleus.nucleus_p();
295  catalyst_count[-pdg::p] -= pdg_nucleus.nucleus_ap();
296  catalyst_count[pdg::n] -= pdg_nucleus.nucleus_n();
297  catalyst_count[-pdg::n] -= pdg_nucleus.nucleus_an();
298  catalyst_count[pdg::Lambda] -= pdg_nucleus.nucleus_La();
299  catalyst_count[-pdg::Lambda] -= pdg_nucleus.nucleus_aLa();
300  PdgCode pdg_catalyst = PdgCode::invalid();
301  for (const auto i : catalyst_count) {
302  if (i.second == 1) {
303  pdg_catalyst = i.first;
304  break;
305  }
306  }
307  if (pdg_catalyst == PdgCode::invalid()) {
308  logg[LScatterActionMulti].error("Something went wrong while forming",
309  pdg_nucleus, " from ",
311  }
312  const ParticleTypePtr type_catalyst =
313  ParticleType::try_find(pdg_catalyst);
314  const double spin_degn =
315  react_degen_factor(spin_factor_inc, type_catalyst->spin_degeneracy(),
316  type_nucleus->spin_degeneracy());
317  double symmetry_factor = 1.0;
318  for (const auto i : c) {
319  symmetry_factor *= (i.second == 3) ? 6.0 // 3!
320  : (i.second == 2) ? 2.0 // 2!
321  : 1.0;
322  if (i.second > 3 || i.second < 0) {
323  logg[LScatterActionMulti].error("4<->2 error, incoming particles ",
325  }
326  }
327 
328  add_reaction(std::make_unique<CollisionBranch>(
329  *type_catalyst, *type_nucleus,
330  probability_four_to_two(*type_catalyst, *type_nucleus, dt, gcell_vol,
331  symmetry_factor * spin_degn),
333  }
334  }
335  // 5 -> 2
336  if (incoming_particles_.size() == 5) {
337  if (incl_multi[IncludedMultiParticleReactions::NNbar_5to2] == 1) {
339  const int spin_factor_inc =
340  incoming_particles_[0].pdgcode().spin_degeneracy() *
341  incoming_particles_[1].pdgcode().spin_degeneracy() *
342  incoming_particles_[2].pdgcode().spin_degeneracy() *
343  incoming_particles_[3].pdgcode().spin_degeneracy() *
344  incoming_particles_[4].pdgcode().spin_degeneracy();
345  const double symmetry_factor = 4.0; // 2! * 2!
346 
348  const ParticleTypePtr type_anti_p = ParticleType::try_find(-pdg::p);
350  const ParticleTypePtr type_anti_n = ParticleType::try_find(-pdg::n);
351 
352  const double spin_degn =
353  react_degen_factor(spin_factor_inc, type_p->spin_degeneracy(),
354  type_anti_p->spin_degeneracy());
355 
356  if (type_p && type_n) {
357  const double prob = probability_five_to_two(
358  type_p->mass(), dt, gcell_vol,
359  symmetry_factor * spin_degn); // same for ppbar and nnbar
360  add_reaction(std::make_unique<CollisionBranch>(
361  *type_p, *type_anti_p, prob,
363  add_reaction(std::make_unique<CollisionBranch>(
364  *type_n, *type_anti_n, prob,
366  }
367  }
368  }
369  }
370 }
371 
373  logg[LScatterActionMulti].debug("Incoming particles for ScatterActionMulti: ",
375 
376  /* Decide for a particular final state. */
377  const CollisionBranch* proc =
378  choose_channel<CollisionBranch>(reaction_channels_, total_probability_);
379  process_type_ = proc->get_type();
381  partial_probability_ = proc->weight();
382 
383  logg[LScatterActionMulti].debug("Chosen channel: ", process_type_,
385 
386  switch (process_type_) {
388  /* n->1 annihilation */
389  annihilation();
390  break;
394  /* n->2 scattering */
395  n_to_two();
396  break;
397  default:
399  "ScatterActionMulti::generate_final_state: Invalid process type " +
400  std::to_string(static_cast<int>(process_type_)) + " was requested.");
401  }
402 
403  /* The production point of the new particles. */
404  FourVector middle_point = get_interaction_point();
405 
406  for (ParticleData& new_particle : outgoing_particles_) {
407  // Boost to the computational frame
408  new_particle.boost_momentum(
410  /* Set positions of the outgoing particles */
411  new_particle.set_4position(middle_point);
412  }
413 }
414 
415 double ScatterActionMulti::calculate_I3(const double sqrts) const {
416  const double m1 = incoming_particles_[0].type().mass();
417  const double m2 = incoming_particles_[1].type().mass();
418  const double m3 = incoming_particles_[2].type().mass();
419 
420  if (sqrts < m1 + m2 + m3) {
421  return 0.0;
422  }
423  const double x1 = (m1 - m2) * (m1 - m2), x2 = (m1 + m2) * (m1 + m2),
424  x3 = (sqrts - m3) * (sqrts - m3),
425  x4 = (sqrts + m3) * (sqrts + m3);
426  const double qmm = x3 - x1, qmp = x3 - x2, qpm = x4 - x1, qpp = x4 - x2;
427  const double kappa = std::sqrt(qpm * qmp / (qpp * qmm));
428  const double tmp = std::sqrt(qmm * qpp);
429  const double c1 =
430  4.0 * m1 * m2 * std::sqrt(qmm / qpp) * (x4 - m3 * sqrts + m1 * m2);
431  const double c2 = 0.5 * (m1 * m1 + m2 * m2 + m3 * m3 + sqrts * sqrts) * tmp;
432  const double c3 = 8 * m1 * m2 / tmp *
433  ((m1 * m1 + m2 * m2) * (m3 * m3 + sqrts * sqrts) -
434  2 * m1 * m1 * m2 * m2 - 2 * m3 * m3 * sqrts * sqrts);
435  const double c4 =
436  -8 * m1 * m2 / tmp * smash::pow_int(sqrts * sqrts - m3 * m3, 2);
437  const double res =
438  c1 * gsl_sf_ellint_Kcomp(kappa, GSL_PREC_DOUBLE) +
439  c2 * gsl_sf_ellint_Ecomp(kappa, GSL_PREC_DOUBLE) +
440  c3 * gsl_sf_ellint_Pcomp(kappa, -qmp / qmm, GSL_PREC_DOUBLE) +
441  c4 * gsl_sf_ellint_Pcomp(kappa, -x1 * qmp / (x2 * qmm), GSL_PREC_DOUBLE);
442  return res;
443 }
444 
446  const ParticleType& type_out, double dt, const double gcell_vol,
447  const int degen_sym_factor) const {
448  const double e1 = incoming_particles_[0].momentum().x0();
449  const double e2 = incoming_particles_[1].momentum().x0();
450  const double e3 = incoming_particles_[2].momentum().x0();
451  const double sqrts = sqrt_s();
452 
453  const double gamma_decay = type_out.get_partial_width(
454  sqrts, {&incoming_particles_[0].type(), &incoming_particles_[1].type(),
455  &incoming_particles_[2].type()});
456 
457  const double I_3 = calculate_I3(sqrts);
458  const double ph_sp_3 =
459  1. / (8 * M_PI * M_PI * M_PI) * 1. / (16 * sqrts * sqrts) * I_3;
460 
461  const double spec_f_val = type_out.spectral_function(sqrts);
462 
463  return dt / (gcell_vol * gcell_vol) * M_PI / (4. * e1 * e2 * e3) *
464  gamma_decay / ph_sp_3 * spec_f_val * std::pow(hbarc, 5.0) *
465  degen_sym_factor;
466 }
467 
469  const ParticleType& type_out1, const ParticleType& type_out2, double dt,
470  const double gcell_vol, const double degen_sym_factor) const {
471  const double e1 = incoming_particles_[0].momentum().x0();
472  const double e2 = incoming_particles_[1].momentum().x0();
473  const double e3 = incoming_particles_[2].momentum().x0();
474  const double m4 = type_out1.mass();
475  const double m5 = type_out2.mass();
476 
477  const double sqrts = sqrt_s();
478  const double xs =
479  CrossSections::two_to_three_xs(type_out1, type_out2, sqrts) / gev2_mb;
480  const double lamb = lambda_tilde(sqrts * sqrts, m4 * m4, m5 * m5);
481 
482  const double I_3 = calculate_I3(sqrts);
483  const double ph_sp_3 =
484  1. / (8 * M_PI * M_PI * M_PI) * 1. / (16 * sqrts * sqrts) * I_3;
485 
486  return dt / (gcell_vol * gcell_vol) * 1. / (4. * e1 * e2 * e3) * lamb /
487  (ph_sp_3 * 8 * M_PI * sqrts * sqrts) * xs * std::pow(hbarc, 5.0) *
488  degen_sym_factor;
489 }
490 
492  const ParticleType& type_out1, const ParticleType& type_out2, double dt,
493  const double gcell_vol, const double degen_sym_factor) const {
494  const double e1 = incoming_particles_[0].momentum().x0();
495  const double e2 = incoming_particles_[1].momentum().x0();
496  const double e3 = incoming_particles_[2].momentum().x0();
497  const double e4 = incoming_particles_[3].momentum().x0();
498  const double m5 = type_out1.mass();
499  const double m6 = type_out2.mass();
500 
501  const double man_s = sqrt_s() * sqrt_s();
502  const double xs =
503  CrossSections::two_to_four_xs(type_out1, type_out2, sqrt_s()) / gev2_mb;
504  const double lamb = lambda_tilde(man_s, m5 * m5, m6 * m6);
505  const double ph_sp_4 = parametrizaton_phi4(man_s);
506 
507  return dt / std::pow(gcell_vol, 3.0) * 1. / (16. * e1 * e2 * e3 * e4) * xs /
508  (4. * M_PI * man_s) * lamb / ph_sp_4 * std::pow(hbarc, 8.0) *
509  degen_sym_factor;
510 }
511 
512 double ScatterActionMulti::parametrizaton_phi4(const double man_s) const {
513  int n_nucleons = 0, n_pions = 0, n_lambdas = 0;
514  double sum_m = 0.0, prod_m = 1.0;
515  for (const ParticleData& data : incoming_particles_) {
516  const PdgCode pdg = data.type().pdgcode();
517  n_nucleons += pdg.is_nucleon(); // including anti-nucleons
518  n_pions += pdg.is_pion();
519  n_lambdas += pdg.is_Lambda(); // including anti-Lambda
520  sum_m += data.type().mass();
521  prod_m *= data.type().mass();
522  }
523  const double x = 1.0 - sum_m / std::sqrt(man_s);
524  const double x2 = x * x;
525  const double x3 = x2 * x;
526  double g = -1.0;
527 
528  if (n_nucleons == 3 && n_pions == 1) { // NNNpi
529  g = (1.0 + 0.862432 * x - 3.4853 * x2 + 1.70259 * x3) /
530  (1.0 + 0.387376 * x - 1.34128 * x2 + 0.154489 * x3);
531  } else if (n_nucleons == 4) { // NNNN
532  g = (1.0 - 1.72285 * x + 0.728331 * x2) /
533  (1.0 - 0.967146 * x - 0.0103633 * x2);
534  } else if (n_nucleons == 2 && n_lambdas == 1 && n_pions == 1) { // LaNNpi
535  g = (1.0 + 0.937064 * x - 3.56864 * x2 + 1.721 * x3) /
536  (1.0 + 0.365202 * x - 1.2854 * x2 + 0.138444 * x3);
537  } else if (n_nucleons == 3 && n_lambdas == 1) { // LaNNN
538  g = (1.0 + 0.882401 * x - 3.4074 * x2 + 1.62454 * x3) /
539  (1.0 + 1.61741 * x - 2.12543 * x2 - 0.0902067 * x3);
540  }
541 
542  if (g > 0.0) {
543  return (std::sqrt(prod_m) * sum_m * sum_m * std::pow(x, 3.5) * g) /
544  (840. * std::sqrt(2) * std::pow(M_PI, 4.0) * std::pow(1 - x, 4.0));
545  } else {
546  logg[LScatterActionMulti].error("parametrizaton_phi4: no parametrization ",
547  "available for ", incoming_particles_);
548  return 0.0;
549  }
550 }
551 
553  const double mout, double dt, const double gcell_vol,
554  const double degen_sym_factor) const {
555  const double e1 = incoming_particles_[0].momentum().x0();
556  const double e2 = incoming_particles_[1].momentum().x0();
557  const double e3 = incoming_particles_[2].momentum().x0();
558  const double e4 = incoming_particles_[3].momentum().x0();
559  const double e5 = incoming_particles_[4].momentum().x0();
560 
561  const double man_s = sqrt_s() * sqrt_s();
562  const double lamb = lambda_tilde(man_s, mout * mout, mout * mout);
563  const double ph_sp_5 = parametrizaton_phi5_pions(man_s);
564 
565  // Matching the NNbar anihilation cross section defintion for 2-to-5
566  const double xs =
567  std::max(0., ppbar_total(man_s) - ppbar_elastic(man_s)) / gev2_mb;
568 
569  return dt / std::pow(gcell_vol, 4.0) * 1. / (32. * e1 * e2 * e3 * e4 * e5) *
570  xs / (4. * M_PI * man_s) * lamb / ph_sp_5 * std::pow(hbarc, 11.0) *
571  degen_sym_factor;
572 }
573 
574 double ScatterActionMulti::parametrizaton_phi5_pions(const double man_s) const {
575  // see function documentation for parameter naming
576  const double s_zero = 25 * pion_mass * pion_mass;
577  const double fit_a = 2.1018e-10;
578  const double fit_alpha = 1.982;
579  return fit_a * std::pow(man_s - s_zero, 5.0) *
580  std::pow(1 + man_s / s_zero, -fit_alpha);
581 }
582 
584  if (outgoing_particles_.size() != 1) {
585  std::string s =
586  "Annihilation: "
587  "Incorrect number of particles in final state: ";
588  s += std::to_string(outgoing_particles_.size()) + ".";
589  throw InvalidScatterActionMulti(s);
590  }
591  // Set the momentum of the formed particle in its rest frame.
592  outgoing_particles_[0].set_4momentum(
593  total_momentum_of_outgoing_particles().abs(), 0., 0., 0.);
594  // Make sure to assign formation times before boost to the computational frame
598  }
599 
600  logg[LScatterActionMulti].debug("Momentum of the new particle: ",
601  outgoing_particles_[0].momentum());
602 }
603 
606  // Make sure to assign formation times before boost to the computational frame
610  }
612  "->2 scattering:", incoming_particles_,
613  " -> ", outgoing_particles_);
614 }
615 
617  const ParticleData& data_a, const ParticleData& data_b,
618  const ParticleData& data_c) const {
619  // We want a combination of pi+, pi- and pi0
620  const PdgCode pdg_a = data_a.pdgcode();
621  const PdgCode pdg_b = data_b.pdgcode();
622  const PdgCode pdg_c = data_c.pdgcode();
623 
624  return (pdg_a.is_pion() && pdg_b.is_pion() && pdg_c.is_pion()) &&
625  (pdg_a != pdg_b && pdg_b != pdg_c && pdg_c != pdg_a);
626 }
627 
629  const ParticleData& data_b,
630  const ParticleData& data_c) const {
631  // We want a combination of pi0, pi0 and eta or pi+, pi- and eta
632  const PdgCode pdg_a = data_a.pdgcode();
633  const PdgCode pdg_b = data_b.pdgcode();
634  const PdgCode pdg_c = data_c.pdgcode();
635 
636  return (pdg_a == pdg::pi_z && pdg_b == pdg::pi_z && pdg_c == pdg::eta) ||
637  (pdg_a == pdg::pi_z && pdg_b == pdg::eta && pdg_c == pdg::pi_z) ||
638  (pdg_a == pdg::eta && pdg_b == pdg::pi_z && pdg_c == pdg::pi_z) ||
639 
640  (pdg_a == pdg::eta && pdg_b == pdg::pi_m && pdg_c == pdg::pi_p) ||
641  (pdg_a == pdg::eta && pdg_b == pdg::pi_p && pdg_c == pdg::pi_m) ||
642  (pdg_a == pdg::pi_m && pdg_b == pdg::pi_p && pdg_c == pdg::eta) ||
643  (pdg_a == pdg::pi_m && pdg_b == pdg::eta && pdg_c == pdg::pi_p) ||
644  (pdg_a == pdg::pi_p && pdg_b == pdg::pi_m && pdg_c == pdg::eta) ||
645  (pdg_a == pdg::pi_p && pdg_b == pdg::eta && pdg_c == pdg::pi_m);
646 }
647 
650  const bool all_inc_pi =
652  [](const ParticleData& data) { return data.is_pion(); });
653  const int no_of_piz = std::count_if(
655  [](const ParticleData& data) { return data.pdgcode() == pdg::pi_z; });
656 
657  int total_state_charge = 0;
658  for (const ParticleData& part : incoming_particles_) {
659  total_state_charge += part.pdgcode().charge();
660  }
661 
662  return (all_inc_pi && total_state_charge == 0 && no_of_piz == 1);
663 }
664 
665 void ScatterActionMulti::format_debug_output(std::ostream& out) const {
666  out << "MultiParticleScatter of " << incoming_particles_;
667  if (outgoing_particles_.empty()) {
668  out << " (not performed)";
669  } else {
670  out << " to " << outgoing_particles_;
671  }
672 }
673 
674 } // namespace smash
Action is the base class for a generic process that takes a number of incoming particles and transfor...
Definition: action.h:35
virtual void sample_2body_phasespace()
Sample the full 2-body phase-space (masses, momenta, angles) in the center-of-mass frame for the fina...
Definition: action.cc:305
FourVector total_momentum_of_outgoing_particles() const
Calculate the total kinetic momentum of the outgoing particles.
Definition: action.cc:160
void assign_formation_time_to_outgoing_particles()
Assign the formation time to the outgoing particles.
Definition: action.cc:191
ParticleList outgoing_particles_
Initially this stores only the PDG codes of final-state particles.
Definition: action.h:372
void assign_unpolarized_spin_vector_to_outgoing_particles()
Assign an unpolarized spin vector to all outgoing particles.
Definition: action.cc:478
double sqrt_s() const
Determine the total energy in the center-of-mass frame [GeV].
Definition: action.h:271
ParticleList incoming_particles_
List with data of incoming particles.
Definition: action.h:364
FourVector get_interaction_point() const
Get the interaction point.
Definition: action.cc:68
static double lambda_tilde(double a, double b, double c)
Little helper function that calculates the lambda function (sometimes written with a tilde to better ...
Definition: action.h:315
ProcessType process_type_
type of process
Definition: action.h:381
CollisionBranch is a derivative of ProcessBranch, which is used to represent particular final-state c...
ProcessType get_type() const override
static double two_to_three_xs(const ParticleType &type_in1, const ParticleType &type_in2, double sqrts)
Determine 2->3 cross section for the scattering of the given particle types.
static double two_to_four_xs(const ParticleType &type_in1, const ParticleType &type_in2, double sqrts)
Determine 2->4 cross section for the scattering of the given particle types.
The FourVector class holds relevant values in Minkowski spacetime with (+, −, −, −) metric signature.
Definition: fourvector.h:33
ParticleData contains the dynamic information of a certain particle.
Definition: particledata.h:59
PdgCode pdgcode() const
Get the pdgcode of the particle.
Definition: particledata.h:88
A pointer-like interface to global references to ParticleType objects.
Definition: particletype.h:682
Particle type contains the static properties of a particle species.
Definition: particletype.h:98
double spectral_function(double m) const
Full spectral function of the resonance (relativistic Breit-Wigner distribution with mass-dependent ...
double get_partial_width(const double m, const ParticleTypePtrList dlist) const
Get the mass-dependent partial width of a resonance with mass m, decaying into two given daughter par...
static const ParticleTypePtr try_find(PdgCode pdgcode)
Returns the ParticleTypePtr for the given pdgcode.
Definition: particletype.cc:89
unsigned int spin_degeneracy() const
Definition: particletype.h:195
double mass() const
Definition: particletype.h:145
PdgCode stores a Particle Data Group Particle Numbering Scheme particle type number.
Definition: pdgcode.h:108
bool is_pion() const
Definition: pdgcode.h:471
bool is_nucleon() const
Definition: pdgcode.h:404
static PdgCode invalid()
PdgCode 0x0 is guaranteed not to be valid by the PDG standard, but it passes all tests here,...
Definition: pdgcode.h:826
bool is_Lambda() const
Definition: pdgcode.h:448
unsigned int spin_degeneracy() const
Definition: pdgcode.h:712
ParticleList particle_list() const
double weight() const
Thrown when ScatterActionMulti is called to perform with unknown combination of incoming and outgoing...
double probability_four_to_two(const ParticleType &type_out1, const ParticleType &type_out2, double dt, const double gcell_vol, const double degen_sym_factor=1.0) const
Calculate the probability for a 4-to-2 reaction according to the stochastic collision criterion as gi...
double total_probability_
Total probability of reaction.
bool two_pions_eta(const ParticleData &data_a, const ParticleData &data_b, const ParticleData &data_c) const
Check wether the three incoming particles are π⁺,π⁻,η or π⁰,π⁰,η in any order.
double probability_three_to_one(const ParticleType &type_out, double dt, const double gcell_vol, const int degen_sym_factor=1) const
Calculate the probability for a 3m-to-1 reaction according to the stochastic collision criterion as g...
bool all_incoming_particles_are_pions_have_zero_charge_only_one_piz() const
Check if 5 incoming particles match intial pion state for 5-to-2, which is pi+ pi- pi+ pi- pi0 in ord...
void generate_final_state() override
Generate the final-state of the multi-particle scattering process.
double get_partial_weight() const override
Get the partial probability for the chosen channel (scaled with the cross section scaling factors of ...
double react_degen_factor(const int spin_factor_inc, const int spin_degen_out1, const int spin_degen_out2) const
Determine the spin degeneracy factor ( ) for the N->2 reaction.
void format_debug_output(std::ostream &out) const override
Writes information about this action to the out stream.
void add_reactions(CollisionBranchList pv)
Add several new reaction channels at once.
void add_possible_reactions(double dt, const double gcell_vol, const MultiParticleReactionsBitSet incl_multi)
Add all possible multi-particle reactions for the given incoming particles.
double probability_five_to_two(const double m_out, double dt, const double gcell_vol, const double degen_sym_factor=1.0) const
Calculate the probability for a 5-to-2 reaction according to the stochastic collision criterion as gi...
CollisionBranchList reaction_channels_
List of possible collisions.
double probability_three_to_two(const ParticleType &type_out1, const ParticleType &type_out2, double dt, const double gcell_vol, const double degen_sym_factor=1.0) const
Calculate the probability for a 3-to-2 reaction according to the stochastic collision criterion as gi...
double calculate_I3(const double sqrts) const
Calculate the integration necessary for the three-body phase space.
ScatterActionMulti(const ParticleList &in_plist, double time, const SpinInteractionType spin_interaction_type=SpinInteractionType::Off)
Construct a ScatterActionMulti object.
double parametrizaton_phi5_pions(const double man_s) const
Calculate the parametrized 5-pion phase space.
double get_total_weight() const override
Get the total probability for the reaction (scaled with the cross section scaling factors of the inco...
SpinInteractionType spin_interaction_type_
Spin interaction type.
bool three_different_pions(const ParticleData &data_a, const ParticleData &data_b, const ParticleData &data_c) const
Check wether the three incoming particles are π⁺,π⁻,π⁰ in any order.
void add_reaction(CollisionBranchPtr p)
Add a new reaction channel.
double partial_probability_
Partial probability of the chosen outgoing channel.
void n_to_two()
Perform a n->2 process.
void annihilation()
Perform a n->1 annihilation process.
double parametrizaton_phi4(const double man_s) const
Calculate the parametrized 4-body relativistic phase space integral.
std::bitset< 4 > MultiParticleReactionsBitSet
Container for the n to m reactions in the code.
@ NNbar_5to2
@ A3_Nuclei_4to2
@ Deuteron_3to2
@ Meson_3to1
SpinInteractionType
Possible spin interaction types.
@ Off
No spin interactions.
std::array< einhard::Logger<>, std::tuple_size< LogArea::AreaTuple >::value > logg
An array that stores all pre-configured Logger objects.
Definition: logging.cc:40
constexpr int pi_p
π⁺.
constexpr int64_t antideuteron
Anti-deuteron in decimal digits.
constexpr int64_t triton
Triton.
constexpr int64_t antihe3
Anti-He-3.
constexpr int p
Proton.
constexpr int64_t hypertriton
Hypertriton.
constexpr int64_t antihypertriton
Anti-Hypertriton.
constexpr int eta
η.
constexpr int64_t antitriton
Anti-triton.
constexpr int pi_z
π⁰.
constexpr int n
Neutron.
constexpr int64_t deuteron
Deuteron.
constexpr int Lambda
Λ.
constexpr int pi_m
π⁻.
constexpr int64_t he3
He-3.
Definition: action.h:24
constexpr double gev2_mb
GeV^-2 <-> mb conversion factor.
Definition: constants.h:35
double ppbar_total(double mandelstam_s)
ppbar total cross section parametrization Source: Bass:1998ca
static constexpr int LScatterActionMulti
double ppbar_elastic(double mandelstam_s)
ppbar elastic cross section parametrization Source: Bass:1998ca
@ MultiParticleThreeToTwo
See here for a short description.
@ MultiParticleThreeMesonsToOne
See here for a short description.
@ MultiParticleFourToTwo
See here for a short description.
@ MultiParticleFiveToTwo
See here for a short description.
bool all_of(Container &&c, UnaryPredicate &&p)
Convenience wrapper for std::all_of that operates on a complete container.
Definition: algorithms.h:80
std::string to_string(ThermodynamicQuantity quantity)
Convert a ThermodynamicQuantity enum value to its corresponding string.
Definition: stringify.cc:26
constexpr T pow_int(const T base, unsigned const exponent)
Efficient template for calculating integer powers using squaring.
Definition: pow.h:23
constexpr double pion_mass
Pion mass in GeV.
Definition: constants.h:69
constexpr double hbarc
GeV <-> fm conversion factor.
Definition: constants.h:29