Version: SMASH-1.5
decaymodes.cc
Go to the documentation of this file.
1 /*
2  *
3  * Copyright (c) 2014-2018
4  * SMASH Team
5  *
6  * GNU General Public License (GPLv3 or later)
7  *
8  */
9 
10 #include "smash/decaymodes.h"
11 
12 #include <vector>
13 
14 #include "smash/clebschgordan.h"
15 #include "smash/constants.h"
16 #include "smash/cxx14compat.h"
17 #include "smash/inputfunctions.h"
18 #include "smash/isoparticletype.h"
19 #include "smash/logging.h"
20 #include "smash/stringfunctions.h"
21 
22 namespace smash {
23 
25 std::vector<DecayModes> *DecayModes::all_decay_modes = nullptr;
27 std::vector<DecayTypePtr> *all_decay_types = nullptr;
28 
29 void DecayModes::add_mode(ParticleTypePtr mother, double ratio, int L,
30  ParticleTypePtrList particle_types) {
31  DecayType *type = get_decay_type(mother, particle_types, L);
32  // Check if mode already exists: if yes, add weight.
33  for (auto &mode : decay_modes_) {
34  if (type == &mode->type()) {
35  mode->set_weight(mode->weight() + ratio);
36  return;
37  }
38  }
39  // Add new mode.
40  decay_modes_.push_back(make_unique<DecayBranch>(*type, ratio));
41 }
42 
44  ParticleTypePtrList particle_types,
45  int L) {
46  assert(all_decay_types != nullptr);
47 
48  // check if the decay type already exisits
49  for (const auto &type : *all_decay_types) {
50  if (type->has_mother(mother) && type->has_particles(particle_types) &&
51  type->angular_momentum() == L) {
52  return type.get();
53  }
54  }
55 
56  // if the type does not exist yet, create a new one
57  switch (particle_types.size()) {
58  case 2:
59  if (is_dilepton(particle_types[0]->pdgcode(),
60  particle_types[1]->pdgcode())) {
61  all_decay_types->emplace_back(
62  make_unique<TwoBodyDecayDilepton>(particle_types, L));
63  } else if (particle_types[0]->is_stable() &&
64  particle_types[1]->is_stable()) {
65  all_decay_types->emplace_back(
66  make_unique<TwoBodyDecayStable>(particle_types, L));
67  } else if (particle_types[0]->is_stable() ||
68  particle_types[1]->is_stable()) {
69  all_decay_types->emplace_back(
70  make_unique<TwoBodyDecaySemistable>(particle_types, L));
71  } else {
72  all_decay_types->emplace_back(
73  make_unique<TwoBodyDecayUnstable>(particle_types, L));
74  }
75  break;
76  case 3:
77  if (has_lepton_pair(particle_types[0]->pdgcode(),
78  particle_types[1]->pdgcode(),
79  particle_types[2]->pdgcode())) {
80  all_decay_types->emplace_back(
81  make_unique<ThreeBodyDecayDilepton>(mother, particle_types, L));
82  } else {
83  all_decay_types->emplace_back(
84  make_unique<ThreeBodyDecay>(particle_types, L));
85  }
86  break;
87  default:
88  throw InvalidDecay(
89  "DecayModes::get_decay_type was instructed to add a decay mode "
90  "with " +
91  std::to_string(particle_types.size()) +
92  " particles. This is an invalid input.");
93  }
94 
95  return all_decay_types->back().get();
96 }
97 
98 bool DecayModes::renormalize(const std::string &name) {
99  const auto &log = logger<LogArea::DecayModes>();
100  double sum = 0.;
101  bool is_large_renormalization = false;
102  for (auto &mode : decay_modes_) {
103  sum += mode->weight();
104  }
105  if (std::abs(sum - 1.) < really_small) {
106  log.debug("Particle ", name,
107  ": Extremely small renormalization constant: ", sum,
108  "\n=> Skipping the renormalization.");
109  } else {
110  is_large_renormalization = (std::abs(sum - 1.) > 0.01);
111  log.debug("Particle ", name, ": Renormalizing decay modes with ", sum);
112  double new_sum = 0.0;
113  for (auto &mode : decay_modes_) {
114  mode->set_weight(mode->weight() / sum);
115  new_sum += mode->weight();
116  }
117  log.debug("After renormalization sum of ratios is ", new_sum);
118  }
119  return is_large_renormalization;
120 }
121 
122 /* unnamed namespace; its content is accessible only in this file.
123  * This is identically the same as have a static function, but if one
124  * wanted to specify a file-local type, this would be the way to go. */
125 namespace {
132 inline std::size_t find_offset(PdgCode pdg) {
133  return std::addressof(ParticleType::find(pdg)) -
134  std::addressof(ParticleType::list_all()[0]);
135 }
136 } // unnamed namespace
137 
138 static int min_angular_momentum(int s0, int s1, int s2) {
139  int min_L = std::min(std::abs(s0 - s1 - s2), std::abs(s0 - s1 + s2));
140  min_L = std::min(min_L, std::abs(s0 + s1 - s2));
141  if (min_L % 2 != 0) {
142  throw std::runtime_error(
143  "min_angular_momentum: sum of spins should be integer");
144  }
145  return min_L / 2.;
146 }
147 
148 static int min_angular_momentum(int s0, int s1, int s2, int s3) {
149  int min_L =
150  std::min(std::abs(s0 - s1 + s2 + s3), std::abs(s0 + s1 - s2 + s3));
151  min_L = std::min(min_L, std::abs(s0 + s1 + s2 - s3));
152  min_L = std::min(min_L, std::abs(s0 - s1 - s2 + s3));
153  min_L = std::min(min_L, std::abs(s0 - s1 + s2 - s3));
154  min_L = std::min(min_L, std::abs(s0 + s1 - s2 - s3));
155  min_L = std::min(min_L, std::abs(s0 - s1 - s2 - s3));
156  if (min_L % 2 != 0) {
157  throw std::runtime_error(
158  "min_angular_momentum: sum of spins should be integer");
159  }
160  return min_L / 2.;
161 }
162 
163 void DecayModes::load_decaymodes(const std::string &input) {
164  const auto &log = logger<LogArea::DecayModes>();
165  // create the DecayType vector first, then it outlives the DecayModes vector,
166  // which references the DecayType objects.
167  static std::vector<DecayTypePtr> decaytypes;
168  decaytypes.clear(); // in case an exception was thrown and should try again
169  // ten decay types per decay mode should be a good guess.
170  decaytypes.reserve(10 * ParticleType::list_all().size());
171  all_decay_types = &decaytypes;
172 
173  static std::vector<DecayModes> decaymodes;
174  decaymodes.clear(); // in case an exception was thrown and should try again
175  decaymodes.resize(ParticleType::list_all().size());
176  all_decay_modes = &decaymodes;
177 
178  const IsoParticleType *isotype_mother = nullptr;
179  ParticleTypePtrList mother_states;
180  std::vector<DecayModes> decay_modes_to_add; // one for each mother state
181  int total_large_renormalized = 0;
182 
183  const auto end_of_decaymodes = [&]() {
184  if (isotype_mother == nullptr) { // at the start of the file
185  return;
186  }
187  // Loop over all states in the mother multiplet and add modes
188  for (size_t m = 0; m < mother_states.size(); m++) {
189  if (decay_modes_to_add[m].is_empty() && !mother_states[m]->is_stable()) {
190  throw MissingDecays("No decay modes found for particle " +
191  mother_states[m]->name());
192  }
193  bool is_large_renorm =
194  decay_modes_to_add[m].renormalize(mother_states[m]->name());
195  total_large_renormalized += is_large_renorm;
196  PdgCode pdgcode = mother_states[m]->pdgcode();
197  /* Add the list of decay modes for this particle type */
198  decaymodes[find_offset(pdgcode)] = std::move(decay_modes_to_add[m]);
199  }
200  if (isotype_mother->has_anti_multiplet()) {
201  /* Construct the decay modes for the anti-multiplet. */
202  log.debug("generating decay modes for anti-multiplet: " +
203  isotype_mother->name());
204  for (const auto &state : mother_states) {
205  PdgCode pdg = state->pdgcode();
206  PdgCode pdg_anti = pdg.get_antiparticle();
207  const ParticleType &type_anti = ParticleType::find(pdg_anti);
208  DecayModes &decay_modes_orig = decaymodes[find_offset(pdg)];
209  DecayModes &decay_modes_anti = decaymodes[find_offset(pdg_anti)];
210  for (const auto &mode : decay_modes_orig.decay_mode_list()) {
211  ParticleTypePtrList list = mode->particle_types();
212  for (auto &type : list) {
213  if (type->has_antiparticle()) {
214  type = type->get_antiparticle();
215  }
216  }
217  decay_modes_anti.add_mode(&type_anti, mode->weight(),
218  mode->angular_momentum(), list);
219  }
220  }
221  }
222  };
223 
224  // Track the line number for better error messages.
225  // FIXME: At the moment this does not include comments and empty lines.
226  uint64_t linenumber = 1;
227  for (const Line &line : line_parser(input)) {
228  const auto trimmed = trim(line.text);
229  assert(!trimmed.empty()); // trim(line.text) is never empty,
230  // else line_parser is broken
231  if (trimmed.find_first_of(" \t") == std::string::npos) {
232  // a single record on one line signifies a new decay mode section
233  end_of_decaymodes();
234  std::string name = trim(line.text);
235  isotype_mother = &IsoParticleType::find(name);
236  mother_states = isotype_mother->get_states();
237  decay_modes_to_add.clear();
238  decay_modes_to_add.resize(mother_states.size());
239  log.debug("reading decay modes for " + name);
240  // check if any of the states have decay modes already
241  for (size_t m = 0; m < mother_states.size(); m++) {
242  PdgCode pdgcode = mother_states[m]->pdgcode();
243  if (!decaymodes[find_offset(pdgcode)].is_empty()) {
244  throw LoadFailure("Duplicate entry for " + name +
245  " in decaymodes.txt:" + std::to_string(linenumber));
246  }
247  }
248  } else {
249  std::istringstream lineinput(line.text);
250  std::vector<std::string> decay_particles;
251  decay_particles.reserve(3);
252  double ratio;
253  lineinput >> ratio;
254 
255  int L;
256  lineinput >> L;
257  if (L < 0) {
258  throw LoadFailure("Invalid angular momentum '" + std::to_string(L) +
259  "' in decaymodes.txt:" + std::to_string(line.number) +
260  ": '" + line.text + "'");
261  }
262 
263  std::string name;
264  lineinput >> name;
265  bool multi = true;
266  while (lineinput) {
267  decay_particles.emplace_back(name);
268  const auto isotype = IsoParticleType::try_find(name);
269  const bool is_multiplet = isotype;
270  const bool is_state = ParticleType::exists(name);
271  if (!is_multiplet && !is_state) {
272  throw InvalidDecay(
273  "Daughter " + name +
274  " is neither an isospin multiplet nor a particle." + " (line " +
275  std::to_string(linenumber) + ": \"" + trimmed + "\")");
276  }
277  const bool is_hadronic_multiplet =
278  is_multiplet && isotype->get_states()[0]->is_hadron();
279  multi &= is_hadronic_multiplet;
280  lineinput >> name;
281  }
282  Parity parity;
283  bool is_strong_decay;
284  const int s0 = isotype_mother->spin();
285  int min_L = 0;
286  int max_L = 0;
287  if (multi) {
288  /* References to isospin multiplets: Automatically determine all valid
289  * combinations and calculate Clebsch-Gordan factors */
290  switch (decay_particles.size()) {
291  case 2: {
292  const IsoParticleType &isotype_daughter_1 =
293  IsoParticleType::find(decay_particles[0]);
294  const IsoParticleType &isotype_daughter_2 =
295  IsoParticleType::find(decay_particles[1]);
296  parity = isotype_daughter_1.parity() * isotype_daughter_2.parity();
297  is_strong_decay = isotype_daughter_1.is_hadron() &&
298  isotype_daughter_2.is_hadron();
299  const int s1 = isotype_daughter_1.spin();
300  const int s2 = isotype_daughter_2.spin();
301  min_L = min_angular_momentum(s0, s1, s2);
302  max_L = (s0 + s1 + s2) / 2;
303  // loop through multiplets
304  bool forbidden_by_isospin = true;
305  for (size_t m = 0; m < mother_states.size(); m++) {
306  for (const auto &daughter1 : isotype_daughter_1.get_states()) {
307  for (const auto &daughter2 : isotype_daughter_2.get_states()) {
308  // calculate Clebsch-Gordan factor
309  const double cg_sqr = isospin_clebsch_gordan_sqr_2to1(
310  *daughter1, *daughter2, *mother_states[m]);
311  if (cg_sqr > 0.) {
312  // add mode
313  log.debug(
314  "decay mode generated: " + mother_states[m]->name() +
315  " -> " + daughter1->name() + " " + daughter2->name() +
316  " (" + std::to_string(ratio * cg_sqr) + ")");
317  decay_modes_to_add[m].add_mode(mother_states[m],
318  ratio * cg_sqr, L,
319  {daughter1, daughter2});
320  forbidden_by_isospin = false;
321  }
322  }
323  }
324  }
325  if (forbidden_by_isospin) {
326  std::stringstream s;
327  s << ",\nwhere isospin mother: " << isotype_mother->isospin()
328  << ", daughters: " << isotype_daughter_1.isospin() << " "
329  << isotype_daughter_2.isospin();
330  throw InvalidDecay(isotype_mother->name() +
331  " decay mode is forbidden by isospin: \"" +
332  line.text + "\"" + s.str());
333  }
334  break;
335  }
336  case 3: {
337  const IsoParticleType &isotype_daughter_1 =
338  IsoParticleType::find(decay_particles[0]);
339  const IsoParticleType &isotype_daughter_2 =
340  IsoParticleType::find(decay_particles[1]);
341  const IsoParticleType &isotype_daughter_3 =
342  IsoParticleType::find(decay_particles[2]);
343  parity = isotype_daughter_1.parity() * isotype_daughter_2.parity() *
344  isotype_daughter_3.parity();
345  is_strong_decay = isotype_daughter_1.is_hadron() &&
346  isotype_daughter_2.is_hadron() &&
347  isotype_daughter_3.is_hadron();
348  const int s1 = isotype_daughter_1.spin();
349  const int s2 = isotype_daughter_2.spin();
350  const int s3 = isotype_daughter_2.spin();
351  min_L = min_angular_momentum(s0, s1, s2, s3);
352  max_L = (s0 + s1 + s2 + s3) / 2;
353  // loop through multiplets
354  for (size_t m = 0; m < mother_states.size(); m++) {
355  for (const auto &daughter1 : isotype_daughter_1.get_states()) {
356  for (const auto &daughter2 : isotype_daughter_2.get_states()) {
357  for (const auto &daughter3 :
358  isotype_daughter_3.get_states()) {
359  const double cg_sqr = isospin_clebsch_gordan_sqr_3to1(
360  *daughter1, *daughter2, *daughter3, *mother_states[m]);
361  if (cg_sqr > 0.) {
362  // add mode
363  log.debug(
364  "decay mode generated: " + mother_states[m]->name() +
365  " -> " + daughter1->name() + " " + daughter2->name() +
366  " " + daughter3->name() + " (" +
367  std::to_string(ratio * cg_sqr) + ")");
368  decay_modes_to_add[m].add_mode(
369  mother_states[m], ratio * cg_sqr, L,
370  {daughter1, daughter2, daughter3});
371  }
372  }
373  }
374  }
375  }
376  break;
377  }
378  default:
379  throw std::runtime_error(
380  "References to isospin multiplets only "
381  "allowed in two-body or three-body decays: " +
382  line.text + " (line " + std::to_string(linenumber) + ": \"" +
383  trimmed + "\")");
384  }
385  } else {
386  /* References to specific states, not multiplets:
387  * Loop over all mother states and check charge conservation. */
388  ParticleTypePtrList types;
389  int charge = 0;
390  parity = Parity::Pos;
391  is_strong_decay = true;
392  for (auto part : decay_particles) {
393  try {
394  types.push_back(IsoParticleType::find_state(part));
395  } catch (std::runtime_error &e) {
396  throw std::runtime_error(std::string() + e.what() + " (line " +
397  std::to_string(linenumber) + ": \"" +
398  trimmed + "\")");
399  }
400  charge += types.back()->charge();
401  parity *= types.back()->parity();
402  is_strong_decay &= types.back()->is_hadron();
403  }
404  if (types.size() == 2) {
405  const int s1 = types[0]->spin();
406  const int s2 = types[1]->spin();
407  min_L = min_angular_momentum(s0, s1, s2);
408  max_L = (s0 + s1 + s2) / 2;
409  } else if (types.size() == 3) {
410  const int s1 = types[0]->spin();
411  const int s2 = types[1]->spin();
412  const int s3 = types[2]->spin();
413  min_L = min_angular_momentum(s0, s1, s2, s3);
414  max_L = (s0 + s1 + s2 + s3) / 2;
415  } else {
416  throw InvalidDecay(isotype_mother->name() +
417  " decay mode has an invalid number of particles"
418  " in the final state " +
419  "(line " + std::to_string(linenumber) + ": \"" +
420  trimmed + "\")");
421  }
422  bool no_decays = true;
423  for (size_t m = 0; m < mother_states.size(); m++) {
424  if (mother_states[m]->charge() == charge) {
425  log.debug("decay mode found: " + mother_states[m]->name() + " -> " +
426  std::to_string(decay_particles.size()));
427  decay_modes_to_add[m].add_mode(mother_states[m], ratio, L, types);
428  no_decays = false;
429  }
430  }
431  if (no_decays) {
432  throw InvalidDecay(isotype_mother->name() +
433  " decay mode violates charge conservation " +
434  "(line " + std::to_string(linenumber) + ": \"" +
435  trimmed + "\")");
436  }
437  }
438  // Take angular momentum into account.
439  if (L % 2 == 1) {
440  parity = -parity;
441  }
442  // Make sure the decay has the correct parity.
443  if (is_strong_decay && parity != mother_states[0]->parity()) {
444  throw InvalidDecay(mother_states[0]->name() +
445  " decay mode violates parity conservation " +
446  "(line " + std::to_string(linenumber) + ": \"" +
447  trimmed + "\")");
448  }
449  // Make sure the decay has a correct angular momentum.
450  if (L < min_L || L > max_L) {
451  throw InvalidDecay(
452  mother_states[0]->name() +
453  " decay mode violates angular momentum conservation: " +
454  std::to_string(L) + " not in [" + std::to_string(min_L) + ", " +
455  std::to_string(max_L) + "] (line " + std::to_string(linenumber) +
456  ": \"" + trimmed + "\")");
457  }
458  }
459  linenumber++;
460  }
461  end_of_decaymodes();
462 
463  // Check whether the mother's pole mass is strictly larger than the minimal
464  // masses of the daughters. This is required by the Manley-Saleski ansatz.
465  const auto &particles = ParticleType::list_all();
466  for (const auto &mother : particles) {
467  if (mother.is_stable()) {
468  continue;
469  }
470  const auto &decays = mother.decay_modes().decay_mode_list();
471  for (const auto &decay : decays) {
472  if (mother.mass() <= decay->threshold()) {
473  std::stringstream s;
474  s << mother.name() << " → ";
475  for (const auto p : decay->particle_types()) {
476  s << p->name();
477  }
478  s << " with " << mother.mass() << " ≤ " << decay->threshold();
479  throw InvalidDecay(
480  "For all decays, the minimum mass of daughters"
481  "must be smaller\nthan the mother's pole mass "
482  "(Manley-Saleski Ansatz)\n"
483  "Violated by the following decay: " +
484  s.str());
485  }
486  }
487  }
488  if (total_large_renormalized > 0) {
489  log.warn("Branching ratios of ", total_large_renormalized,
490  " hadrons were renormalized by more than 1% to have sum 1.");
491  }
492 }
493 
494 } // namespace smash
static const IsoParticleType & find(const std::string &name)
Returns the IsoParticleType object for the given name.
ParticleTypePtrList get_states() const
Returns list of states that form part of the multiplet.
bool renormalize(const std::string &name)
Renormalize the branching ratios to add up to 1.
Definition: decaymodes.cc:98
std::string trim(const std::string &s)
Strip leading and trailing whitespaces.
static bool exists(PdgCode pdgcode)
constexpr double really_small
Numerical error tolerance.
Definition: constants.h:34
PdgCode get_antiparticle() const
Construct the antiparticle to a given PDG code.
Definition: pdgcode.h:259
bool is_dilepton(const PdgCode pdg1, const PdgCode pdg2)
Definition: pdgcode.h:980
static void load_decaymodes(const std::string &input)
Loads the DecayModes map as described in the input string.
Definition: decaymodes.cc:163
Collection of useful constants that are known at compile time.
bool has_lepton_pair(const PdgCode pdg1, const PdgCode pdg2, const PdgCode pdg3)
Definition: pdgcode.h:992
Parity
Represent the parity of a particle type.
Definition: particletype.h:24
static DecayType * get_decay_type(ParticleTypePtr mother, ParticleTypePtrList particle_types, int L)
Retrieve a decay type.
Definition: decaymodes.cc:43
bool is_empty() const
Definition: decaymodes.h:60
DecayType is the abstract base class for all decay types.
Definition: decaytype.h:23
double isospin_clebsch_gordan_sqr_3to1(const ParticleType &p_a, const ParticleType &p_b, const ParticleType &p_c, const ParticleType &Res)
Calculate the squared isospin Clebsch-Gordan coefficient for three particles p_a, p_b and p_c couplin...
static const ParticleType & find(PdgCode pdgcode)
Returns the ParticleType object for the given pdgcode.
static const ParticleTypeList & list_all()
Definition: particletype.cc:55
unsigned int spin() const
Returns twice the spin of the multiplet.
static int min_angular_momentum(int s0, int s1, int s2)
Definition: decaymodes.cc:138
Particle type contains the static properties of a particle species.
Definition: particletype.h:87
IsoParticleType is a class to represent isospin multiplets.
void add_mode(ParticleTypePtr mother, double ratio, int L, ParticleTypePtrList particle_types)
Add a decay mode using all necessary information.
Definition: decaymodes.cc:29
PdgCode stores a Particle Data Group Particle Numbering Scheme particle type number.
Definition: pdgcode.h:108
double isospin_clebsch_gordan_sqr_2to1(const ParticleType &p_a, const ParticleType &p_b, const ParticleType &Res)
Calculate the squared isospin Clebsch-Gordan coefficient for two particles p_a and p_b coupling to a ...
Definition: clebschgordan.h:42
std::vector< DecayTypePtr > * all_decay_types
Global pointer to the decay types list.
Definition: decaymodes.cc:27
static const IsoParticleType * try_find(const std::string &name)
Returns the IsoParticleType pointer for the given name.
static const ParticleTypePtr find_state(const std::string &name)
Returns the ParticleType object for the given name, by first finding the correct multiplet and then l...
DecayBranchList decay_modes_
Vector of decay modes.
Definition: decaymodes.h:123
build_vector_< Line > line_parser(const std::string &input)
Helper function for parsing particles.txt and decaymodes.txt.
constexpr int p
Proton.
std::size_t find_offset(PdgCode pdg)
Passes back the address offset of a particletype in the list of all particles.
Definition: decaymodes.cc:132
A pointer-like interface to global references to ParticleType objects.
Definition: particletype.h:660
bool has_anti_multiplet() const
Check if there is a multiplet of antiparticles, which is different from the original multiplet...
The DecayModes class is used to store and update information about decay branches (i...
Definition: decaymodes.h:29
int isospin() const
Returns twice the total isospin of the multiplet.
static std::vector< DecayModes > * all_decay_modes
A list of all DecayModes objects using the same indexing as all_particle_types.
Definition: decaymodes.h:132
Line consists of a line number and the contents of that line.
const std::string & name() const
Returns the name of the multiplet.
Positive parity.
Definition: action.h:24
const DecayBranchList & decay_mode_list() const
Definition: decaymodes.h:63