Version: SMASH-3.4
particletype.cc
Go to the documentation of this file.
1 /*
2  *
3  * Copyright (c) 2014-2020,2022-2024,2026
4  * SMASH Team
5  *
6  * GNU General Public License (GPLv3 or later)
7  *
8  */
9 
10 #include "smash/particletype.h"
11 
12 #include <assert.h>
13 
14 #include <algorithm>
15 #include <map>
16 #include <vector>
17 
18 #include "smash/constants.h"
19 #include "smash/decaymodes.h"
20 #include "smash/distributions.h"
21 #include "smash/formfactors.h"
22 #include "smash/inputfunctions.h"
23 #include "smash/integrate.h"
24 #include "smash/iomanipulators.h"
25 #include "smash/isoparticletype.h"
26 #include "smash/logging.h"
28 #include "smash/stringfunctions.h"
29 
30 namespace smash {
31 static constexpr int LParticleType = LogArea::ParticleType::id;
32 static constexpr int LResonances = LogArea::Resonances::id;
33 
34 namespace {
35 /// Global pointer to the Particle Type list.
36 const ParticleTypeList *all_particle_types = nullptr;
37 /// Global pointer to the Particle Type list of nucleons
38 ParticleTypePtrList nucleons_list;
39 /// Global pointer to the Particle Type list of anti-nucleons
40 ParticleTypePtrList anti_nucs_list;
41 /// Global pointer to the Particle Type list of deltas
42 ParticleTypePtrList deltas_list;
43 /// Global pointer to the Particle Type list of anti-deltas
44 ParticleTypePtrList anti_deltas_list;
45 /// Global pointer to the Particle Type list of baryon resonances
46 ParticleTypePtrList baryon_resonances_list;
47 /// Global pointer to the Particle Type list of light nuclei
48 ParticleTypePtrList light_nuclei_list;
49 } // unnamed namespace
50 
51 const ParticleTypeList &ParticleType::list_all() {
52  assert(all_particle_types);
53  return *all_particle_types;
54 }
55 
56 // For performance reasons one might want to inline this function.
58  // Calculate the offset via pointer subtraction:
59  const auto offset = this - std::addressof(list_all()[0]);
60  // Since we're using uint16_t for storing the index better be safe than sorry:
61  // The offset must fit into the data type. If this ever fails we got a lot
62  // more particle types than initially expected and you have to increase the
63  // ParticleTypePtr storage to uint32_t.
64  assert(offset >= 0 && offset < 0xffff);
65  // After the assertion above the down-cast to uint16_t is safe:
66  return ParticleTypePtr(static_cast<uint16_t>(offset));
67 }
68 
69 ParticleTypePtrList &ParticleType::list_nucleons() { return nucleons_list; }
70 
71 ParticleTypePtrList &ParticleType::list_anti_nucleons() {
72  return anti_nucs_list;
73 }
74 
75 ParticleTypePtrList &ParticleType::list_Deltas() { return deltas_list; }
76 
77 ParticleTypePtrList &ParticleType::list_anti_Deltas() {
78  return anti_deltas_list;
79 }
80 
81 ParticleTypePtrList &ParticleType::list_baryon_resonances() {
83 }
84 
85 ParticleTypePtrList &ParticleType::list_light_nuclei() {
86  return light_nuclei_list;
87 }
88 
90  const auto found = std::lower_bound(
92  [](const ParticleType &l, const PdgCode &r) { return l.pdgcode() < r; });
93  if (found == all_particle_types->end() || found->pdgcode() != pdgcode) {
94  return {}; // The default constructor creates an invalid pointer.
95  }
96  return &*found;
97 }
98 
100  const auto found = ParticleType::try_find(pdgcode);
101  if (!found) {
102  throw PdgNotFoundFailure("PDG code " + pdgcode.string() + " not found!");
103  }
104  return *found;
105 }
106 
108  const auto found = ParticleType::try_find(pdgcode);
109  return found;
110 }
111 
112 bool ParticleType::exists(const std::string &name) {
113  const auto found =
114  std::find_if(all_particle_types->begin(), all_particle_types->end(),
115  [&](const ParticleType &p) { return p.name() == name; });
116  if (found == all_particle_types->end()) {
117  return false;
118  }
119  return true;
120 }
121 
122 ParticleType::ParticleType(std::string n, double m, double w, Parity p,
123  PdgCode id)
124  : name_(n),
125  mass_(m),
126  width_(w),
127  parity_(p),
128  pdgcode_(id),
129  min_mass_kinematic_(-1.),
130  min_mass_spectral_(-1.),
131  charge_(pdgcode_.charge()),
132  isospin_(-1),
133  I3_(pdgcode_.isospin3()) {}
134 
135 /**
136  * Construct an antiparticle name-string from the given name-string for the
137  * particle and its PDG code.
138  *
139  * \param[in] name the name-string of the particle to convert
140  * \param[in] code the pdgcode of the particle to convert
141  * \return the name-string of the converted antiparticle
142  */
143 static std::string antiname(const std::string &name, PdgCode code) {
144  std::string basename, charge;
145 
146  if (name.find("⁺⁺") != std::string::npos) {
147  basename = name.substr(0, name.length() - sizeof("⁺⁺") + 1);
148  charge = "⁻⁻";
149  } else if (name.find("⁺") != std::string::npos) {
150  basename = name.substr(0, name.length() - sizeof("⁺") + 1);
151  charge = "⁻";
152  } else if (name.find("⁻⁻") != std::string::npos) {
153  basename = name.substr(0, name.length() - sizeof("⁻⁻") + 1);
154  charge = "⁺⁺";
155  } else if (name.find("⁻") != std::string::npos) {
156  basename = name.substr(0, name.length() - sizeof("⁻") + 1);
157  charge = "⁺";
158  } else if (name.find("⁰") != std::string::npos) {
159  basename = name.substr(0, name.length() - sizeof("⁰") + 1);
160  charge = "⁰";
161  } else {
162  basename = name;
163  charge = "";
164  }
165 
166  // baryons & strange mesons: insert a bar
167  if (code.baryon_number() != 0 || code.strangeness() != 0 ||
168  code.charmness() != 0 || code.is_neutrino()) {
169  constexpr char bar[] = "\u0305";
170  basename.insert(utf8::sequence_length(basename.begin()), bar);
171  }
172 
173  return basename + charge;
174 }
175 
176 /**
177  * Construct a charge string, given the charge as integer.
178  *
179  * \param[in] charge charge of a particle
180  * \return the corresponding string to write out this charge
181  * \throw runtime_error if the charge is not an integer between -2 and 2
182  */
183 static std::string chargestr(int charge) {
184  switch (charge) {
185  case 2:
186  return "⁺⁺";
187  case 1:
188  return "⁺";
189  case 0:
190  return "⁰";
191  case -1:
192  return "⁻";
193  case -2:
194  return "⁻⁻";
195  default:
196  throw std::runtime_error("Invalid charge " + std::to_string(charge));
197  }
198 }
199 
200 void ParticleType::create_type_list(const std::string &input) { // {{{
201  static ParticleTypeList type_list;
202  type_list.clear(); // in case LoadFailure was thrown and caught and we should
203  // try again
204  for (const Line &line : line_parser(input)) {
205  std::istringstream lineinput(line.text);
206  std::string name;
207  double mass, width;
208  std::string parity_string;
209  std::vector<std::string> pdgcode_strings;
210  // We expect at most 4 PDG codes per multiplet.
211  pdgcode_strings.reserve(4);
212  lineinput >> name >> mass >> width >> parity_string;
213  Parity parity;
214  bool fail = false;
215  if (parity_string == "+") {
217  } else if (parity_string == "-") {
219  } else {
220  fail = true;
221  }
222  if (lineinput.fail() || fail) {
224  "While loading the ParticleType data:\nFailed to convert the input "
225  "string to the expected data types.",
226  line));
227  }
228  // read additional PDG codes (if present)
229  while (!lineinput.eof()) {
230  pdgcode_strings.push_back("");
231  lineinput >> pdgcode_strings.back();
232  if (lineinput.fail()) {
234  "While loading the ParticleType data:\nFailed to convert the input "
235  "string to the expected data types.",
236  line));
237  }
238  }
239  if (pdgcode_strings.size() < 1) {
241  "While loading the ParticleType data:\nFailed to convert the input "
242  "string due to missing PDG code.",
243  line));
244  }
245  std::vector<PdgCode> pdgcode;
246  pdgcode.resize(pdgcode_strings.size());
247  std::transform(pdgcode_strings.begin(), pdgcode_strings.end(),
248  pdgcode.begin(),
249  [](const std::string &s) { return PdgCode(s); });
250  ensure_all_read(lineinput, line);
251 
252  // Check if provided masses are the same as hardcoded ones, if present
254  throw std::runtime_error("Nucleon mass in input file different from " +
256  }
257  if (pdgcode[0].is_pion() && !almost_equal(mass, pion_mass)) {
258  throw std::runtime_error("Pion mass in input file different from " +
260  }
261  if (pdgcode[0].is_kaon() && !almost_equal(mass, kaon_mass)) {
262  throw std::runtime_error("Kaon mass in input file different from " +
264  }
265  if (pdgcode[0].is_omega() && !almost_equal(mass, omega_mass)) {
266  throw std::runtime_error("Omega mass in input file different from " +
268  }
269  if (pdgcode[0].is_Delta() && !almost_equal(mass, delta_mass)) {
270  throw std::runtime_error("Delta mass in input file different from " +
272  }
274  throw std::runtime_error("Deuteron mass in input file different from " +
276  }
277 
278  // add all states to type list
279  for (size_t i = 0; i < pdgcode.size(); i++) {
280  std::string full_name = name;
281  if (pdgcode.size() > 1) {
282  // for multiplets: add charge string to name
283  full_name += chargestr(pdgcode[i].charge());
284  }
285  type_list.emplace_back(full_name, mass, width, parity, pdgcode[i]);
286  logg[LParticleType].debug()
287  << "Setting particle type: " << type_list.back();
288  if (pdgcode[i].has_antiparticle()) {
289  /* add corresponding antiparticle */
290  PdgCode anti = pdgcode[i].get_antiparticle();
291  // For bosons the parity does not change, for fermions it gets inverted.
292  const auto anti_parity = (anti.spin() % 2 == 0) ? parity : -parity;
293  full_name = antiname(full_name, pdgcode[i]);
294  type_list.emplace_back(full_name, mass, width, anti_parity, anti);
295  logg[LParticleType].debug()
296  << "Setting antiparticle type: " << type_list.back();
297  }
298  }
299  }
300  type_list.shrink_to_fit();
301 
302  /* Sort the type list by PDG code. */
303  std::sort(type_list.begin(), type_list.end());
304 
305  /* Look for duplicates. */
306  PdgCode prev_pdg = 0;
307  for (const auto &t : type_list) {
308  if (t.pdgcode() == prev_pdg) {
309  throw ParticleType::LoadFailure("Duplicate PdgCode in particles.txt: " +
310  t.pdgcode().string());
311  }
312  prev_pdg = t.pdgcode();
313  }
314 
315  if (all_particle_types != nullptr) {
316  throw std::runtime_error("Error: Type list was already built!");
317  }
318  all_particle_types = &type_list; // note that type_list is a function-local
319  // static and thus will live on until after
320  // main().
321 
322  // create all isospin multiplets
323  for (const auto &t : type_list) {
325  }
326  // link the multiplets to the types
327  for (auto &t : type_list) {
328  t.iso_multiplet_ = IsoParticleType::find(t);
329  }
330 
331  // Create nucleons/anti-nucleons list
332  if (IsoParticleType::exists("N")) {
333  for (const auto &state : IsoParticleType::find("N").get_states()) {
334  nucleons_list.push_back(state);
335  anti_nucs_list.push_back(state->get_antiparticle());
336  }
337  }
338 
339  // Create deltas list
340  if (IsoParticleType::exists("Δ")) {
341  for (const auto &state : IsoParticleType::find("Δ").get_states()) {
342  deltas_list.push_back(state);
343  anti_deltas_list.push_back(state->get_antiparticle());
344  }
345  }
346 
347  // Create baryon resonances list
348  for (const ParticleType &type_resonance : ParticleType::list_all()) {
349  /* Only loop over baryon resonances. */
350  if (type_resonance.is_stable() ||
351  type_resonance.pdgcode().baryon_number() != 1) {
352  continue;
353  }
354  baryon_resonances_list.push_back(&type_resonance);
355  baryon_resonances_list.push_back(type_resonance.get_antiparticle());
356  }
357 
358  for (const ParticleType &type : ParticleType::list_all()) {
359  if (type.is_nucleus()) {
360  light_nuclei_list.push_back(&type);
361  }
362  }
363 } /*}}}*/
364 
366  if (unlikely(min_mass_kinematic_ < 0.)) {
367  /* If the particle is stable, min. mass is just the mass. */
369  /* Otherwise, find the lowest mass value needed in any decay mode */
370  if (!is_stable()) {
371  for (const auto &mode : decay_modes().decay_mode_list()) {
372  min_mass_kinematic_ = std::min(min_mass_kinematic_, mode->threshold());
373  }
374  }
375  }
376  return min_mass_kinematic_;
377 }
378 
380  if (unlikely(min_mass_spectral_ < 0.)) {
381  /* If the particle is stable or it has a non-zero spectral function value at
382  * the minimum mass that is allowed by kinematics, min_mass_spectral is just
383  * the min_mass_kinetic. */
385  /* Otherwise, find the lowest mass value where spectral function has a
386  * non-zero value by bisection.*/
387  if (!is_stable() &&
389  // find a right bound that has non-zero spectral function for bisection
390  const double m_step = 0.01;
391  double right_bound_bis;
392  for (unsigned int i = 0;; i++) {
393  right_bound_bis = min_mass_kinematic() + m_step * i;
394  if (full_spectral_function(right_bound_bis) > really_small) {
395  break;
396  }
397  }
398  // bisection
399  const double precision = 1E-6;
400  double left_bound_bis = right_bound_bis - m_step;
401  while (right_bound_bis - left_bound_bis > precision) {
402  const double mid = (left_bound_bis + right_bound_bis) / 2.0;
404  right_bound_bis = mid;
405  } else {
406  left_bound_bis = mid;
407  }
408  }
409  min_mass_spectral_ = right_bound_bis;
410  }
411  }
412  return min_mass_spectral_;
413 }
414 
416  if (isospin_ < 0) {
419  : 0;
420  }
421  return isospin_;
422 }
423 
424 double ParticleType::partial_width(const double m,
425  const DecayBranch *mode) const {
426  if (m < mode->threshold()) {
427  return 0.;
428  }
429  double partial_width_at_pole = width_at_pole() * mode->weight();
430  return mode->type().width(mass(), partial_width_at_pole, m);
431 }
432 
434  const auto offset = this - std::addressof(list_all()[0]);
435  const auto &modes = (*DecayModes::all_decay_modes)[offset];
436  assert(is_stable() || !modes.is_empty());
437  return modes;
438 }
439 
440 double ParticleType::total_width(const double m) const {
441  double w = 0.;
442  if (is_stable()) {
443  return w;
444  }
445  /* Loop over decay modes and sum up all partial widths. */
446  const auto &modes = decay_modes().decay_mode_list();
447  for (unsigned int i = 0; i < modes.size(); i++) {
448  w = w + partial_width(m, modes[i].get());
449  }
450  if (w < width_cutoff) {
451  return 0.;
452  }
453  return w;
454 }
455 
457  for (const ParticleType &ptype : ParticleType::list_all()) {
458  if (!ptype.is_stable() && ptype.decay_modes().is_empty()) {
459  throw std::runtime_error(
460  "Unstable particle " + ptype.name() +
461  " has no decay chanels! Either add one to it in decaymodes file or "
462  "set it's width to 0 in particles file.");
463  }
464  if (ptype.is_dprime() && !ParticleType::try_find(pdg::deuteron)) {
465  throw std::runtime_error(
466  "d' cannot be used without deuteron. Modify input particles file "
467  "accordingly.");
468  }
469  }
470 }
471 
473  WhichDecaymodes wh) const {
474  switch (wh) {
475  case WhichDecaymodes::All: {
476  return true;
477  }
479  return !t.is_dilepton_decay();
480  }
482  return t.is_dilepton_decay();
483  }
484  default:
485  throw std::runtime_error(
486  "Problem in selecting decaymodes in wanted_decaymode()");
487  }
488 }
489 
491  const ThreeVector x,
492  WhichDecaymodes wh) const {
493  const auto &decay_mode_list = decay_modes().decay_mode_list();
494  /* Determine whether the decay is affected by the potentials. If it's
495  * affected, read the values of the potentials at the position of the
496  * particle */
497  FourVector UB = FourVector();
498  FourVector UI3 = FourVector();
499  if (UB_lat_pointer != nullptr) {
500  UB_lat_pointer->value_at(x, UB);
501  }
502  if (UI3_lat_pointer != nullptr) {
503  UI3_lat_pointer->value_at(x, UI3);
504  }
505  /* Loop over decay modes and calculate all partial widths. */
506  DecayBranchList partial;
507  partial.reserve(decay_mode_list.size());
508  for (unsigned int i = 0; i < decay_mode_list.size(); i++) {
509  /* Calculate the square root s of the final state particles. */
510  const auto FinalTypes = decay_mode_list[i]->type().particle_types();
511  double scale_B = 0.0;
512  double scale_I3 = 0.0;
513  if (pot_pointer != nullptr) {
514  scale_B += pot_pointer->force_scale(*this).first;
515  scale_I3 += pot_pointer->force_scale(*this).second * isospin3_rel();
516  for (const auto &finaltype : FinalTypes) {
517  scale_B -= pot_pointer->force_scale(*finaltype).first;
518  scale_I3 -= pot_pointer->force_scale(*finaltype).second *
519  finaltype->isospin3_rel();
520  }
521  }
522  double sqrt_s = (p + UB * scale_B + UI3 * scale_I3).abs();
523 
524  const double w = partial_width(sqrt_s, decay_mode_list[i].get());
525  if (w > 0.) {
526  if (wanted_decaymode(decay_mode_list[i]->type(), wh)) {
527  partial.push_back(
528  std::make_unique<DecayBranch>(decay_mode_list[i]->type(), w));
529  }
530  }
531  }
532  return partial;
533 }
534 
535 double ParticleType::get_partial_width(const double m,
536  const ParticleTypePtrList dlist) const {
537  /* Get all decay modes. */
538  const auto &decaymodes = decay_modes().decay_mode_list();
539 
540  /* Find the right one(s) and add up corresponding widths. */
541  double w = 0.;
542  for (const auto &mode : decaymodes) {
543  double partial_width_at_pole = width_at_pole() * mode->weight();
544  if (mode->type().has_particles(dlist)) {
545  w += mode->type().width(mass(), partial_width_at_pole, m);
546  }
547  }
548  return w;
549 }
550 
552  const ParticleData &p_a,
553  const ParticleData &p_b) const {
554  /* Get all decay modes. */
555  const auto &decaymodes = decay_modes().decay_mode_list();
556 
557  /* Find the right one(s) and add up corresponding widths. */
558  double w = 0.;
559  for (const auto &mode : decaymodes) {
560  double partial_width_at_pole = width_at_pole() * mode->weight();
561  const ParticleTypePtrList l = {&p_a.type(), &p_b.type()};
562  if (mode->type().has_particles(l)) {
563  w += mode->type().in_width(mass(), partial_width_at_pole, m,
564  p_a.effective_mass(), p_b.effective_mass());
565  }
566  }
567  return w;
568 }
569 
570 double ParticleType::full_spectral_function(double m) const {
571  if (norm_factor_ < 0.) {
572  /* Initialize the normalization factor
573  * by integrating over the unnormalized spectral function. */
574  static /*thread_local (see commit 897d0b8)*/ Integrator integrate;
575  const double width = width_at_pole();
576  const double m_pole = mass();
577  // We transform the integral using m = m_min + width_pole * tan(x), to
578  // make it definite and to avoid numerical issues.
579  const double x_min = std::atan((min_mass_kinematic() - m_pole) / width);
580  norm_factor_ = 1. / integrate(x_min, M_PI / 2., [&](double x) {
581  const double tanx = std::tan(x);
582  const double m_x = m_pole + width * tanx;
583  const double jacobian = width * (1.0 + tanx * tanx);
584  return no_norm_spectral_function(m_x) * jacobian;
585  });
586  }
588 }
589 
591  /* The spectral function is a relativistic Breit-Wigner function
592  * with mass-dependent width. Here: without normalization factor. */
593  const double resonance_width = total_width(m);
594  if (resonance_width < ParticleType::width_cutoff) {
595  return 0.;
596  }
597  return breit_wigner(m, mass(), resonance_width);
598 }
599 
602 }
603 
605  if (is_stable()) {
607  return;
608  }
609  /*
610  * These values are arbitrary but sufficed at the time of writing. Since
611  * the performance cost of making this more precise is (probably) not large,
612  * the resolution can in principle be increased without significant impact.
613  */
614  constexpr double step_size = 0.02;
615  constexpr double safety_factor = 1.01;
617  for (double m = mass_limit_; m > mass_ - width_; m -= step_size) {
618  double current_value = ratio_spectral_full_to_breit_wigner(m);
619  if (current_value > max_ratio) {
620  max_ratio = current_value;
621  }
622  }
624  std::max(1.0, safety_factor * max_ratio);
625 }
626 
628  return breit_wigner_nonrel(m, mass(), width_at_pole());
629 }
630 
632  double energy) const {
634  energy);
635 }
636 
637 double ParticleType::sample_full_spectral_function(double energy) const {
638  if (is_stable()) {
639  return mass();
640  }
641  double mass, acceptance = 0;
642  double sf_ratio_max = std::max(max_ratio_spectral_full_to_breit_wigner(),
644  while (true) {
645  do {
648  } while (acceptance < random::uniform(0., sf_ratio_max));
649  if (unlikely(acceptance - sf_ratio_max > really_small)) {
650  logg[LResonances].debug(
651  "Warning: maximum ratio between full spectral function and Breit-",
652  "Wigner increased in sample_full_spectral_function: ", sf_ratio_max,
653  " to ", acceptance, " for ", name(), ". Sampled mass is ", mass,
654  " GeV with ", energy,
655  " GeV available.\n This might happen rarely at"
656  " the edges of the spectral function.");
657  // increase fudge factor
658  sf_ratio_max *= acceptance / sf_ratio_max;
660  } else {
661  break;
662  }
663  }
664  return mass;
665 }
666 
667 /* Resonance mass sampling for 2-particle final state */
668 double ParticleType::sample_resonance_mass(const double mass_stable,
669  const double cms_energy,
670  int L) const {
671  /* largest possible mass: Use 'nextafter' to make sure it is not above the
672  * physical limit by numerical error. */
673  const double max_mass = std::nextafter(cms_energy - mass_stable, 0.);
674 
675  // smallest possible mass to find non-zero spectral function contributions
676  const double min_mass = min_mass_spectral();
677 
678  // largest possible cm momentum (from smallest mass)
679  const double pcm_max = pCM(cms_energy, mass_stable, min_mass);
680  /* The maximum of the spectral-function ratio 'usually' happens at the
681  * largest mass. However, this is not always the case, therefore we need
682  * and additional fudge factor (determined automatically). Additionally,
683  * a heuristic knowledge is used that usually such mass exist that
684  * full_spectral_function(m) > breit_wigner_spectral_function(m). */
685  const double sf_ratio_max =
688 
689  int max_trials = 10;
690  double mass_res, acceptance, fudge_factor = 1;
691  double max = sf_ratio_max * pcm_max * blatt_weisskopf_sqr(pcm_max, L);
692  do {
693  max *= fudge_factor;
694  do {
695  // sample mass from a simple Breit-Wigner (aka Cauchy) distribution
696  mass_res = sample_breit_wigner_spectral_function(max_mass);
697  // determine cm momentum for this case
698  const double pcm = pCM(cms_energy, mass_stable, mass_res);
699  acceptance = ratio_spectral_full_to_breit_wigner(mass_res) * pcm *
700  blatt_weisskopf_sqr(pcm, L);
701  } while (acceptance < random::uniform(0., max));
702  fudge_factor *= acceptance / max;
703  --max_trials;
704  } while (acceptance > max && max_trials > 0);
705 
706  if (acceptance > max && max_trials == 0) {
707  logg[LResonances].fatal(
708  "Maximum acceptance should be increased in sample_resonance_mass: ",
709  sf_ratio_max, " ", acceptance / max, " ", pdgcode(), " ", mass_stable,
710  " ", cms_energy, " ", mass_res);
711  throw std::runtime_error(
712  "Precomputation of maximum spectral function is not working properly. "
713  "Please contact the developers.");
714  }
715  return mass_res;
716 }
717 
718 /* Resonance mass sampling for 2-particle final state with two resonances. */
719 std::pair<double, double> sample_two_resonance_masses(const ParticleType &t1,
720  const ParticleType &t2,
721  const double cms_energy,
722  int L) {
723  /* Sample resonance mass from the distribution
724  * used for calculating the cross section. */
725  const double max_mass_1 =
726  std::nextafter(cms_energy - t2.min_mass_spectral(), 0.);
727  const double max_mass_2 =
728  std::nextafter(cms_energy - t1.min_mass_spectral(), 0.);
729  // largest possible cm momentum (from smallest mass)
730  const double pcm_max =
731  pCM(cms_energy, t1.min_mass_spectral(), t2.min_mass_spectral());
732  const double sf_ratio_max =
734  t1.ratio_spectral_full_to_breit_wigner(max_mass_1)) *
736  t2.ratio_spectral_full_to_breit_wigner(max_mass_2));
737 
738  int max_trials_outer = 10, max_trials_inner = 20;
739  double mass_1, mass_2, acceptance, fudge_factor = 1;
740  double max_acceptance =
741  sf_ratio_max * pcm_max * blatt_weisskopf_sqr(pcm_max, L);
742  // Rejection sampling
743  do {
744  max_acceptance *= fudge_factor;
745  do {
746  // sample mass from a simple Breit-Wigner (aka Cauchy) distribution
747  mass_1 = t1.sample_breit_wigner_spectral_function(max_mass_1);
748  mass_2 = t2.sample_breit_wigner_spectral_function(max_mass_2);
749  // determine cm momentum for this case
750  const double pcm = pCM(cms_energy, mass_1, mass_2);
751  const double sf_ratio = t1.ratio_spectral_full_to_breit_wigner(mass_1) *
753  // determine ratios of full to simple spectral function
754  acceptance = sf_ratio * pcm * blatt_weisskopf_sqr(pcm, L);
755  } while (acceptance < random::uniform(0., max_acceptance) &&
756  --max_trials_inner > 0);
757  fudge_factor *= acceptance / max_acceptance;
758  } while (acceptance > max_acceptance && --max_trials_outer > 0);
759 
760  if (max_trials_inner == 0) {
761  /*
762  * This fallback is necessary for when the inner loop fails to find an
763  * acceptable mass combination. This can happen when the cms_energy is much
764  * smaller than the sum of pole masses of the two resonances.
765  *
766  */
767  logg[LResonances].debug(
768  "Could not sample a valid mass combination for the resonances ",
769  t1.name(), " and ", t2.name(), " with energy ", cms_energy,
770  " GeV. Using bisection fallback to find valid masses.");
771  while (mass_1 + mass_2 > cms_energy) {
772  mass_1 = (t1.min_mass_spectral() + mass_1) / 2;
773  mass_2 = (t2.min_mass_spectral() + mass_2) / 2;
774  }
775  }
776 
777  if (acceptance > max_acceptance && max_trials_outer == 0) {
778  logg[LResonances].fatal(
779  "Maximum acceptance should be increased in sample_resonance_masses: ",
780  acceptance / max_acceptance, " ", t1.pdgcode(), " ", t2.pdgcode(), " ",
781  cms_energy, " ", mass_1, " ", mass_2);
782  throw std::runtime_error(
783  "Precomputation of maximum spectral function is not working properly. "
784  "Please contact the developers.");
785  }
786  return {mass_1, mass_2};
787 }
788 
790  if (is_stable()) {
791  std::stringstream err;
792  err << "Particle " << *this << " is stable, so it makes no"
793  << " sense to print its spectral function, etc.";
794  throw std::runtime_error(err.str());
795  }
796 
797  double rightmost_pole = 0.0;
798  const auto &decaymodes = decay_modes().decay_mode_list();
799  for (const auto &mode : decaymodes) {
800  double pole_mass_sum = 0.0;
801  for (const ParticleTypePtr p : mode->type().particle_types()) {
802  pole_mass_sum += p->mass();
803  }
804  if (pole_mass_sum > rightmost_pole) {
805  rightmost_pole = pole_mass_sum;
806  }
807  }
808 
809  std::cout << "# mass m[GeV], width w(m) [GeV],"
810  << " spectral function(m^2)*m [GeV^-1] of " << *this << std::endl;
811  constexpr double m_step = 0.02;
812  const double m_min = min_mass_spectral();
813  // An empirical value used to stop the printout. Assumes that spectral
814  // function decays at high mass, which is true for all known resonances.
815  constexpr double spectral_function_threshold = 8.e-3;
816  std::cout << std::fixed << std::setprecision(5);
817  for (unsigned int i = 0;; i++) {
818  const double m = m_min + m_step * i;
819  const double w = total_width(m), sf = full_spectral_function(m);
820  if (m > rightmost_pole * 2 && sf < spectral_function_threshold) {
821  break;
822  }
823  std::cout << m << " " << w << " " << sf << std::endl;
824  }
825 }
826 
827 std::ostream &operator<<(std::ostream &out, const ParticleType &type) {
828  const PdgCode &pdg = type.pdgcode();
829  return out << type.name() << std::setfill(' ') << std::right
830  << "[ mass:" << field<6> << type.mass()
831  << ", width:" << field<6> << type.width_at_pole()
832  << ", PDG:" << field<6> << pdg
833  << ", charge:" << field<3> << pdg.charge()
834  << ", spin:" << field<2> << pdg.spin() << "/2 ]";
835 }
836 
837 /*
838  * This is valid for two particles of the same species because the comparison
839  * operator for smart pointers compares the pointed object. In this case, the
840  * `std::set<ParticleTypePtr> incoming` will contain one element instead of two.
841  */
842 ParticleTypePtrList list_possible_resonances(const ParticleTypePtr type_a,
843  const ParticleTypePtr type_b) {
844  static std::map<std::set<ParticleTypePtr>, ParticleTypePtrList>
845  map_possible_resonances_of;
846  std::set<ParticleTypePtr> incoming{type_a, type_b};
847  const ParticleTypePtrList incoming_types = {type_a, type_b};
848  // Fill map if set is not yet present
849  if (map_possible_resonances_of.count(incoming) == 0) {
850  logg[LResonances].debug()
851  << "Filling map of compatible resonances for ptypes " << type_a->name()
852  << " " << type_b->name();
853  ParticleTypePtrList resonance_list{};
854  // The tests below are redundant as the decay modes already obey them, but
855  // they are quicker to check and so improve performance.
856  for (const ParticleType &resonance : ParticleType::list_all()) {
857  /* Not a resonance, go to next type of particle */
858  if (resonance.is_stable()) {
859  continue;
860  }
861  // Same resonance as in the beginning, ignore
862  if ((resonance.pdgcode() == type_a->pdgcode()) ||
863  (resonance.pdgcode() == type_b->pdgcode())) {
864  continue;
865  }
866  // Check for charge conservation.
867  if (resonance.charge() != type_a->charge() + type_b->charge()) {
868  continue;
869  }
870  // Check for baryon-number conservation.
871  if (resonance.baryon_number() !=
872  type_a->baryon_number() + type_b->baryon_number()) {
873  continue;
874  }
875  // Check for strangeness conservation.
876  if (resonance.strangeness() !=
877  type_a->strangeness() + type_b->strangeness()) {
878  continue;
879  }
880  const auto &decaymodes = resonance.decay_modes().decay_mode_list();
881  for (const auto &mode : decaymodes) {
882  if (mode->type().has_particles(incoming_types)) {
883  resonance_list.push_back(&resonance);
884  break;
885  }
886  }
887  }
888  // Here `resonance_list` can be empty, corresponding to the case where there
889  // are no possible resonances.
890  map_possible_resonances_of[incoming] = resonance_list;
891  }
892 
893  return map_possible_resonances_of[incoming];
894 }
895 
896 } // namespace smash
DecayBranch is a derivative of ProcessBranch, which is used to represent decay channels.
const DecayType & type() const
The DecayModes class is used to store and update information about decay branches (i....
Definition: decaymodes.h:29
const DecayBranchList & decay_mode_list() const
Definition: decaymodes.h:63
DecayType is the abstract base class for all decay types.
Definition: decaytype.h:23
virtual bool is_dilepton_decay() const
Definition: decaytype.h:82
virtual double width(double m0, double G0, double m) const =0
The FourVector class holds relevant values in Minkowski spacetime with (+, −, −, −) metric signature.
Definition: fourvector.h:33
A C++ interface for numerical integration in one dimension with the GSL CQUAD integration functions.
Definition: integrate.h:106
static bool exists(const std::string &name)
Returns whether the ParticleType with the given pdgcode exists.
static const IsoParticleType & find(const std::string &name)
Returns the IsoParticleType object for the given name.
int isospin() const
Returns twice the total isospin of the multiplet.
static void create_multiplet(const ParticleType &type)
Add a new multiplet to the global list of IsoParticleTypes, which contains type.
ParticleData contains the dynamic information of a certain particle.
Definition: particledata.h:59
const ParticleType & type() const
Get the type of the particle.
Definition: particledata.h:132
double effective_mass() const
Get the particle's effective mass.
Definition: particledata.cc:25
A pointer-like interface to global references to ParticleType objects.
Definition: particletype.h:731
Particle type contains the static properties of a particle species.
Definition: particletype.h:100
double min_mass_spectral_
minimum mass, where the spectral function is non-zero Mutable, because it is initialized at first cal...
Definition: particletype.h:681
double sample_full_spectral_function(double energy=mass_limit_) const
Sample mass from the full spectral function.
bool is_pion() const
Definition: particletype.h:221
double min_mass_spectral() const
The minimum mass of the resonance, where the spectral function is non-zero.
IsoParticleType * iso_multiplet_
Container for the isospin multiplet information.
Definition: particletype.h:693
double breit_wigner_spectral_function(double m) const
This one is the most simple form of the spectral function, using a Cauchy distribution (non-relativis...
const DecayModes & decay_modes() const
double sample_resonance_mass(const double mass_stable, const double cms_energy, int L=0) const
Resonance mass sampling for 2-particle final state with one resonance (type given by 'this') and one ...
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...
int strangeness() const
Definition: particletype.h:215
static const ParticleTypePtr try_find(PdgCode pdgcode)
Returns the ParticleTypePtr for the given pdgcode.
Definition: particletype.cc:89
void dump_width_and_spectral_function() const
Prints out width and spectral function versus mass to the standard output.
double total_width(const double m) const
Get the mass-dependent total width of a particle with mass m.
bool wanted_decaymode(const DecayType &t, WhichDecaymodes wh) const
Helper Function that containes the if-statement logic that decides if a decay mode is either a hadron...
void calculate_max_ratio_spectral_full_to_breit_wigner() const
Calculates the maximum ratio between full spectral function and simple one.
static const ParticleType & find(PdgCode pdgcode)
Returns the ParticleType object for the given pdgcode.
Definition: particletype.cc:99
double min_mass_kinematic() const
The minimum mass of the resonance that is kinematically allowed.
PdgCode pdgcode() const
Definition: particletype.h:159
static bool exists(PdgCode pdgcode)
double full_spectral_function(double m) const
Full spectral function of the resonance (relativistic Breit-Wigner distribution with mass-dependent ...
static void check_consistency()
const std::string & name() const
Definition: particletype.h:144
double min_mass_kinematic_
minimum kinematically allowed mass of the particle Mutable, because it is initialized at first call o...
Definition: particletype.h:674
int32_t charge() const
The charge of the particle.
Definition: particletype.h:191
static ParticleTypePtrList & list_nucleons()
Definition: particletype.cc:69
static ParticleTypePtrList & list_anti_nucleons()
Definition: particletype.cc:71
double max_ratio_spectral_full_to_breit_wigner() const
Getter used in the resonance mass sampling functions.
Definition: particletype.h:442
static const ParticleTypeList & list_all()
Definition: particletype.cc:51
bool is_stable() const
Definition: particletype.h:251
int isospin_
Isospin of the particle; filled automatically from pdgcode_.
Definition: particletype.h:688
double isospin3_rel() const
Definition: particletype.h:182
double width_
width of the particle
Definition: particletype.h:663
bool is_Delta() const
Definition: particletype.h:227
double width_at_pole() const
Definition: particletype.h:153
static ParticleTypePtrList & list_anti_Deltas()
Definition: particletype.cc:77
PdgCode pdgcode_
PDG Code of the particle.
Definition: particletype.h:667
bool has_antiparticle() const
Definition: particletype.h:162
ParticleType(std::string n, double m, double w, Parity p, PdgCode id)
Creates a fully initialized ParticleType object.
bool is_nucleon() const
Definition: particletype.h:218
double mass() const
Definition: particletype.h:147
static ParticleTypePtrList & list_baryon_resonances()
Definition: particletype.cc:81
static constexpr double width_cutoff
Decay width cutoff for considering a particle as stable.
Definition: particletype.h:110
DecayBranchList get_partial_widths(const FourVector p, const ThreeVector x, WhichDecaymodes wh) const
Get all the mass-dependent partial decay widths of a particle with mass m.
static ParticleTypePtrList & list_Deltas()
Definition: particletype.cc:75
bool is_deuteron() const
Definition: particletype.h:257
double ratio_spectral_full_to_breit_wigner(double m) const
Calculate the ratio between the full spectral function and simple one.
double get_partial_in_width(const double m, const ParticleData &p_a, const ParticleData &p_b) const
Get the mass-dependent partial in-width of a resonance with mass m, decaying into two given daughter ...
double sample_breit_wigner_spectral_function(double energy=mass_limit_) const
Sample mass from the simple spectral function (Breit-Wigner/Cauchy distribution).
bool is_kaon() const
Definition: particletype.h:224
static void create_type_list(const std::string &particles)
Initialize the global ParticleType list (list_all) from the given input data.
int isospin() const
Returns twice the isospin vector length .
double norm_factor_
This normalization factor ensures that the spectral function is normalized to unity,...
Definition: particletype.h:684
int baryon_number() const
Definition: particletype.h:212
ParticleTypePtr operator&() const
Returns an object that acts like a pointer, except that it requires only 2 bytes and inhibits pointer...
Definition: particletype.cc:57
std::optional< double > max_ratio_spectral_full_to_breit_wigner_
Maximum ratio between full spectral function and the mass-independent Breit-Wigner.
Definition: particletype.h:699
static constexpr double mass_limit_
Limit for sampling the resonance masses.
Definition: particletype.h:657
double no_norm_spectral_function(double m) const
Full spectral function without normalization factor.
static ParticleTypePtrList & list_light_nuclei()
Definition: particletype.cc:85
double partial_width(const double m, const DecayBranch *mode) const
Get the mass-dependent partial decay width of a particle with mass m in a particular decay mode.
Parity parity() const
Definition: particletype.h:156
double mass_
pole mass of the particle
Definition: particletype.h:661
PdgCode stores a Particle Data Group Particle Numbering Scheme particle type number.
Definition: pdgcode.h:108
int baryon_number() const
Definition: pdgcode.h:388
unsigned int spin() const
Definition: pdgcode.h:691
int charmness() const
Definition: pdgcode.h:633
int strangeness() const
Definition: pdgcode.h:626
std::string string() const
Definition: pdgcode.h:322
PdgCode get_antiparticle() const
Construct the antiparticle to a given PDG code.
Definition: pdgcode.h:329
bool is_hadron() const
Definition: pdgcode.h:367
bool is_neutrino() const
Definition: pdgcode.h:378
int charge() const
The charge of the particle.
Definition: pdgcode.h:650
static std::pair< double, int > force_scale(const ParticleType &data)
Evaluates the scaling factor of the forces acting on the particles.
Definition: potentials.cc:152
double weight() const
The ThreeVector class represents a physical three-vector with the components .
Definition: threevector.h:31
Collection of useful constants that are known at compile time.
std::ostream & operator<<(std::ostream &out, const ActionPtr &action)
Convenience: dereferences the ActionPtr to Action.
Definition: action.h:546
std::array< einhard::Logger<>, std::tuple_size< LogArea::AreaTuple >::value > & logg
An array that stores all pre-configured Logger objects.
Definition: logging.h:245
#define unlikely(x)
Tell the branch predictor that this expression is likely false.
Definition: macros.h:16
ParticleTypePtrList deltas_list
Global pointer to the Particle Type list of deltas.
Definition: particletype.cc:42
ParticleTypePtrList baryon_resonances_list
Global pointer to the Particle Type list of baryon resonances.
Definition: particletype.cc:46
const ParticleTypeList * all_particle_types
Global pointer to the Particle Type list.
Definition: particletype.cc:36
ParticleTypePtrList anti_deltas_list
Global pointer to the Particle Type list of anti-deltas.
Definition: particletype.cc:44
ParticleTypePtrList light_nuclei_list
Global pointer to the Particle Type list of light nuclei.
Definition: particletype.cc:48
ParticleTypePtrList anti_nucs_list
Global pointer to the Particle Type list of anti-nucleons.
Definition: particletype.cc:40
ParticleTypePtrList nucleons_list
Global pointer to the Particle Type list of nucleons.
Definition: particletype.cc:38
constexpr int p
Proton.
constexpr int n
Neutron.
constexpr int64_t deuteron
Deuteron.
T uniform(T min, T max)
Definition: random.h:91
T cauchy(T pole, T width, T min, T max)
Draws a random number from a Cauchy distribution (sometimes also called Lorentz or non-relativistic B...
Definition: random.h:351
std::iterator_traits< octet_iterator >::difference_type sequence_length(octet_iterator lead_it)
Given an iterator to the beginning of a UTF-8 sequence, return the length of the next UTF-8 code poin...
Definition: action.h:24
constexpr double delta_mass
Delta mass in GeV.
Definition: constants.h:97
T pCM(const T sqrts, const T mass_a, const T mass_b) noexcept
Definition: kinematics.h:79
bool almost_equal(const N x, const N y)
Checks whether two floating-point numbers are almost equal.
Definition: numerics.h:89
ParticleTypePtrList list_possible_resonances(const ParticleTypePtr type_a, const ParticleTypePtr type_b)
Lists the possible resonances that decay into two particles.
void ensure_all_read(std::istream &input, const Line &line)
Makes sure that nothing is left to read from this line.
static Integrator integrate
Definition: decaytype.cc:143
double breit_wigner(double m, double pole, double width)
Returns a relativistic Breit-Wigner distribution.
static std::string antiname(const std::string &name, PdgCode code)
Construct an antiparticle name-string from the given name-string for the particle and its PDG code.
build_vector_< Line > line_parser(const std::string &input)
Helper function for parsing particles.txt and decaymodes.txt.
Parity
Represent the parity of a particle type.
Definition: particletype.h:27
@ Neg
Negative parity.
@ Pos
Positive parity.
std::string to_string(ThermodynamicQuantity quantity)
Convert a ThermodynamicQuantity enum value to its corresponding string.
Definition: stringify.cc:26
constexpr double deuteron_mass
Deuteron mass in GeV.
Definition: constants.h:103
constexpr double nucleon_mass
Nucleon mass in GeV.
Definition: constants.h:69
double blatt_weisskopf_sqr(const double p_ab, const int L)
Definition: formfactors.h:36
static constexpr int LResonances
Potentials * pot_pointer
Pointer to a Potential class.
constexpr double pion_mass
Pion mass in GeV.
Definition: constants.h:76
constexpr double really_small
Numerical error tolerance.
Definition: constants.h:41
std::string build_error_string(std::string message, const Line &line)
Builds a meaningful error message.
constexpr double kaon_mass
Kaon mass in GeV.
Definition: constants.h:83
RectangularLattice< FourVector > * UB_lat_pointer
Pointer to the skyrme potential on the lattice.
double breit_wigner_nonrel(double m, double pole, double width)
Returns a non-relativistic Breit-Wigner distribution, which is essentially a Cauchy distribution with...
static std::string chargestr(int charge)
Construct a charge string, given the charge as integer.
constexpr double omega_mass
omega mass in GeV.
Definition: constants.h:90
std::pair< double, double > sample_two_resonance_masses(const ParticleType &t1, const ParticleType &t2, const double cms_energy, int L=0)
Resonance mass sampling for 2-particle final state with two resonances.
RectangularLattice< FourVector > * UI3_lat_pointer
Pointer to the symmmetry potential on the lattice.
WhichDecaymodes
Decide which decay mode widths are returned in get partical widths.
Definition: particletype.h:35
@ Hadronic
Ignore dilepton decay modes widths.
@ Dileptons
Only return dilepton decays widths.
@ All
All decay mode widths.
static constexpr int LParticleType
Line consists of a line number and the contents of that line.