Version: SMASH-3.1
smash::DecayModes Class Reference

#include <decaymodes.h>

The DecayModes class is used to store and update information about decay branches (i.e.

the possible children a mother particle can decay into), including their relative weights.

If you want to find a DecayModes object for a specific particle type use ParticleType::decay_modes().

Definition at line 29 of file decaymodes.h.

Classes

struct  InvalidDecay
 
struct  LoadFailure
 
struct  MissingDecays
 
struct  ReferencedParticleNotFound
 

Public Member Functions

void add_mode (ParticleTypePtr mother, double ratio, int L, ParticleTypePtrList particle_types)
 Add a decay mode using all necessary information. More...
 
void add_mode (DecayBranchPtr branch)
 Add a decay mode from an already existing decay branch. More...
 
bool renormalize (const std::string &name)
 Renormalize the branching ratios to add up to 1. More...
 
bool is_empty () const
 
const DecayBranchList & decay_mode_list () const
 

Static Public Member Functions

static void load_decaymodes (const std::string &input)
 Loads the DecayModes map as described in the input string. More...
 
static DecayTypeget_decay_type (ParticleTypePtr mother, ParticleTypePtrList particle_types, int L)
 Retrieve a decay type. More...
 

Private Attributes

DecayBranchList decay_modes_
 Vector of decay modes. More...
 

Static Private Attributes

static std::vector< DecayModes > * all_decay_modes = nullptr
 A list of all DecayModes objects using the same indexing as all_particle_types. More...
 

Friends

const DecayModesParticleType::decay_modes () const
 allow ParticleType::decay_modes to access all_decay_modes More...
 

Member Function Documentation

◆ add_mode() [1/2]

void smash::DecayModes::add_mode ( ParticleTypePtr  mother,
double  ratio,
int  L,
ParticleTypePtrList  particle_types 
)

Add a decay mode using all necessary information.

Parameters
[in]motherthe particle which decays
[in]ratiothe weight to add to the current mode
[in]Langular momentum
[in]particle_typesa list of the products of the decay

Definition at line 29 of file decaymodes.cc.

30  {
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(std::make_unique<DecayBranch>(*type, ratio));
41 }
DecayBranchList decay_modes_
Vector of decay modes.
Definition: decaymodes.h:123
static DecayType * get_decay_type(ParticleTypePtr mother, ParticleTypePtrList particle_types, int L)
Retrieve a decay type.
Definition: decaymodes.cc:43

◆ add_mode() [2/2]

void smash::DecayModes::add_mode ( DecayBranchPtr  branch)
inline

Add a decay mode from an already existing decay branch.

Parameters
[in]branchthe decay branch to add

Definition at line 47 of file decaymodes.h.

47  {
48  decay_modes_.push_back(std::move(branch));
49  }

◆ renormalize()

bool smash::DecayModes::renormalize ( const std::string &  name)

Renormalize the branching ratios to add up to 1.

Parameters
[in]namethe name of the decaying particle
Returns
if the branching ratios were renormalized by more than 1%

Definition at line 98 of file decaymodes.cc.

98  {
99  double sum = 0.;
100  bool is_large_renormalization = false;
101  for (auto &mode : decay_modes_) {
102  sum += mode->weight();
103  }
104  if (std::abs(sum - 1.) < really_small) {
105  logg[LDecayModes].debug("Particle ", name,
106  ": Extremely small renormalization constant: ", sum,
107  "\n=> Skipping the renormalization.");
108  } else {
109  is_large_renormalization = (std::abs(sum - 1.) > 0.01);
110  logg[LDecayModes].debug("Particle ", name,
111  ": 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  logg[LDecayModes].debug("After renormalization sum of ratios is ", new_sum);
118  }
119  return is_large_renormalization;
120 }
std::array< einhard::Logger<>, std::tuple_size< LogArea::AreaTuple >::value > logg
An array that stores all pre-configured Logger objects.
Definition: logging.cc:39
constexpr double really_small
Numerical error tolerance.
Definition: constants.h:37
static constexpr int LDecayModes
Definition: decayaction.cc:17

◆ is_empty()

bool smash::DecayModes::is_empty ( ) const
inline
Returns
true if empty (i.e. no decay modes)

Definition at line 60 of file decaymodes.h.

60 { return decay_modes_.empty(); }

◆ decay_mode_list()

const DecayBranchList& smash::DecayModes::decay_mode_list ( ) const
inline
Returns
pass out the decay modes list

Definition at line 63 of file decaymodes.h.

63 { return decay_modes_; }

◆ load_decaymodes()

void smash::DecayModes::load_decaymodes ( const std::string &  input)
static

Loads the DecayModes map as described in the input string.

It does sanity checking - that the particles it talks about are in the ParticleType map.

Parameters
[in]inputthe full decaymodes input file, as a string
Exceptions
MissingDecaysif there are no decays specified for an unstable particle
LoadFailureif there are duplicate entries detailing decaymodes for the same particle, or if the angular momentum is smaller than 0 or larger than 4
InvalidDecayif product of the decay does not exist in the particle list or as an isospin multiplet, or if decay is forbidden by isospin or charge conservation, or if sum of the minimum mass of products is less than the pole mass of the mother particle
runtime_errorif there are less than 2 or more than 3 products to a given decay branch, or if a branch cannot be found to be part of an isospin multiplet

Definition at line 163 of file decaymodes.cc.

163  {
164  // create the DecayType vector first, then it outlives the DecayModes vector,
165  // which references the DecayType objects.
166  static std::vector<DecayTypePtr> decaytypes;
167  decaytypes.clear(); // in case an exception was thrown and should try again
168  // ten decay types per decay mode should be a good guess.
169  decaytypes.reserve(10 * ParticleType::list_all().size());
170  all_decay_types = &decaytypes;
171 
172  static std::vector<DecayModes> decaymodes;
173  decaymodes.clear(); // in case an exception was thrown and should try again
174  decaymodes.resize(ParticleType::list_all().size());
175  all_decay_modes = &decaymodes;
176 
177  const IsoParticleType *isotype_mother = nullptr;
178  ParticleTypePtrList mother_states;
179  std::vector<DecayModes> decay_modes_to_add; // one for each mother state
180  int total_large_renormalized = 0;
181 
182  const auto end_of_decaymodes = [&]() {
183  if (isotype_mother == nullptr) { // at the start of the file
184  return;
185  }
186  // Loop over all states in the mother multiplet and add modes
187  for (size_t m = 0; m < mother_states.size(); m++) {
188  if (decay_modes_to_add[m].is_empty() && !mother_states[m]->is_stable()) {
189  throw MissingDecays("No decay modes found for particle " +
190  mother_states[m]->name());
191  }
192  bool is_large_renorm =
193  decay_modes_to_add[m].renormalize(mother_states[m]->name());
194  total_large_renormalized += is_large_renorm;
195  PdgCode pdgcode = mother_states[m]->pdgcode();
196  /* Add the list of decay modes for this particle type */
197  decaymodes[find_offset(pdgcode)] = std::move(decay_modes_to_add[m]);
198  }
199  if (isotype_mother->has_anti_multiplet()) {
200  /* Construct the decay modes for the anti-multiplet. */
201  logg[LDecayModes].debug("generating decay modes for anti-multiplet: " +
202  isotype_mother->name());
203  for (const auto &state : mother_states) {
204  PdgCode pdg = state->pdgcode();
205  PdgCode pdg_anti = pdg.get_antiparticle();
206  const ParticleType &type_anti = ParticleType::find(pdg_anti);
207  DecayModes &decay_modes_orig = decaymodes[find_offset(pdg)];
208  DecayModes &decay_modes_anti = decaymodes[find_offset(pdg_anti)];
209  for (const auto &mode : decay_modes_orig.decay_mode_list()) {
210  ParticleTypePtrList list = mode->particle_types();
211  for (auto &type : list) {
212  if (type->has_antiparticle()) {
213  type = type->get_antiparticle();
214  }
215  }
216  decay_modes_anti.add_mode(&type_anti, mode->weight(),
217  mode->angular_momentum(), list);
218  }
219  }
220  }
221  };
222 
223  // Track the line number for better error messages.
224  // FIXME: At the moment this does not include comments and empty lines.
225  uint64_t linenumber = 1;
226  for (const Line &line : line_parser(input)) {
227  const auto trimmed = trim(line.text);
228  assert(!trimmed.empty()); // trim(line.text) is never empty,
229  // else line_parser is broken
230  if (trimmed.find_first_of(" \t") == std::string::npos) {
231  // a single record on one line signifies a new decay mode section
232  end_of_decaymodes();
233  std::string name = trim(line.text);
234  isotype_mother = &IsoParticleType::find(name);
235  mother_states = isotype_mother->get_states();
236  decay_modes_to_add.clear();
237  decay_modes_to_add.resize(mother_states.size());
238  logg[LDecayModes].debug("reading decay modes for " + name);
239  // check if any of the states have decay modes already
240  for (size_t m = 0; m < mother_states.size(); m++) {
241  PdgCode pdgcode = mother_states[m]->pdgcode();
242  if (!decaymodes[find_offset(pdgcode)].is_empty()) {
243  throw LoadFailure("Duplicate entry for " + name +
244  " in decaymodes.txt:" + std::to_string(linenumber));
245  }
246  }
247  } else {
248  std::istringstream lineinput(line.text);
249  std::vector<std::string> decay_particles;
250  decay_particles.reserve(3);
251  double ratio;
252  lineinput >> ratio;
253 
254  int L;
255  lineinput >> L;
256  if (L < 0) {
257  throw LoadFailure("Invalid angular momentum '" + std::to_string(L) +
258  "' in decaymodes.txt:" + std::to_string(line.number) +
259  ": '" + line.text + "'");
260  }
261 
262  std::string name;
263  lineinput >> name;
264  bool multi = true;
265  while (lineinput) {
266  decay_particles.emplace_back(name);
267  const auto isotype = IsoParticleType::try_find(name);
268  const bool is_multiplet = isotype;
269  const bool is_state = ParticleType::exists(name);
270  if (!is_multiplet && !is_state) {
271  throw InvalidDecay(
272  "Daughter " + name +
273  " is neither an isospin multiplet nor a particle." + " (line " +
274  std::to_string(linenumber) + ": \"" + trimmed + "\")");
275  }
276  const bool is_hadronic_multiplet =
277  is_multiplet && isotype->get_states()[0]->is_hadron();
278  multi &= is_hadronic_multiplet;
279  lineinput >> name;
280  }
281  Parity parity;
282  const int s0 = isotype_mother->spin();
283  int min_L = 0;
284  int max_L = 0;
285  if (multi) {
286  /* References to isospin multiplets: Automatically determine all valid
287  * combinations and calculate Clebsch-Gordan factors */
288  switch (decay_particles.size()) {
289  case 2: {
290  const IsoParticleType &isotype_daughter_1 =
291  IsoParticleType::find(decay_particles[0]);
292  const IsoParticleType &isotype_daughter_2 =
293  IsoParticleType::find(decay_particles[1]);
294  parity = isotype_daughter_1.parity() * isotype_daughter_2.parity();
295  const int s1 = isotype_daughter_1.spin();
296  const int s2 = isotype_daughter_2.spin();
297  min_L = min_angular_momentum(s0, s1, s2);
298  max_L = (s0 + s1 + s2) / 2;
299  // loop through multiplets
300  bool forbidden_by_isospin = true;
301  for (size_t m = 0; m < mother_states.size(); m++) {
302  for (const auto &daughter1 : isotype_daughter_1.get_states()) {
303  for (const auto &daughter2 : isotype_daughter_2.get_states()) {
304  // calculate Clebsch-Gordan factor
305  const double cg_sqr = isospin_clebsch_gordan_sqr_2to1(
306  *daughter1, *daughter2, *mother_states[m]);
307  if (cg_sqr > 0.) {
308  // add mode
309  logg[LDecayModes].debug(
310  "decay mode generated: " + mother_states[m]->name() +
311  " -> " + daughter1->name() + " " + daughter2->name() +
312  " (" + std::to_string(ratio * cg_sqr) + ")");
313  decay_modes_to_add[m].add_mode(mother_states[m],
314  ratio * cg_sqr, L,
315  {daughter1, daughter2});
316  forbidden_by_isospin = false;
317  }
318  }
319  }
320  }
321  if (forbidden_by_isospin) {
322  std::stringstream s;
323  s << ",\nwhere isospin mother: " << isotype_mother->isospin()
324  << ", daughters: " << isotype_daughter_1.isospin() << " "
325  << isotype_daughter_2.isospin();
326  throw InvalidDecay(isotype_mother->name() +
327  " decay mode is forbidden by isospin: \"" +
328  line.text + "\"" + s.str());
329  }
330  break;
331  }
332  case 3: {
333  const IsoParticleType &isotype_daughter_1 =
334  IsoParticleType::find(decay_particles[0]);
335  const IsoParticleType &isotype_daughter_2 =
336  IsoParticleType::find(decay_particles[1]);
337  const IsoParticleType &isotype_daughter_3 =
338  IsoParticleType::find(decay_particles[2]);
339  parity = isotype_daughter_1.parity() * isotype_daughter_2.parity() *
340  isotype_daughter_3.parity();
341  const int s1 = isotype_daughter_1.spin();
342  const int s2 = isotype_daughter_2.spin();
343  const int s3 = isotype_daughter_2.spin();
344  min_L = min_angular_momentum(s0, s1, s2, s3);
345  max_L = (s0 + s1 + s2 + s3) / 2;
346  // loop through multiplets
347  for (size_t m = 0; m < mother_states.size(); m++) {
348  for (const auto &daughter1 : isotype_daughter_1.get_states()) {
349  for (const auto &daughter2 : isotype_daughter_2.get_states()) {
350  for (const auto &daughter3 :
351  isotype_daughter_3.get_states()) {
352  const double cg_sqr = isospin_clebsch_gordan_sqr_3to1(
353  *daughter1, *daughter2, *daughter3, *mother_states[m]);
354  if (cg_sqr > 0.) {
355  // add mode
356  logg[LDecayModes].debug(
357  "decay mode generated: " + mother_states[m]->name() +
358  " -> " + daughter1->name() + " " + daughter2->name() +
359  " " + daughter3->name() + " (" +
360  std::to_string(ratio * cg_sqr) + ")");
361  decay_modes_to_add[m].add_mode(
362  mother_states[m], ratio * cg_sqr, L,
363  {daughter1, daughter2, daughter3});
364  }
365  }
366  }
367  }
368  }
369  break;
370  }
371  default:
372  throw std::runtime_error(
373  "References to isospin multiplets only "
374  "allowed in two-body or three-body decays: " +
375  line.text + " (line " + std::to_string(linenumber) + ": \"" +
376  trimmed + "\")");
377  }
378  } else {
379  /* References to specific states, not multiplets:
380  * Loop over all mother states and check charge conservation. */
381  ParticleTypePtrList types;
382  int charge = 0;
383  parity = Parity::Pos;
384  for (auto part : decay_particles) {
385  try {
386  types.push_back(IsoParticleType::find_state(part));
387  } catch (std::runtime_error &e) {
388  throw std::runtime_error(std::string() + e.what() + " (line " +
389  std::to_string(linenumber) + ": \"" +
390  trimmed + "\")");
391  }
392  charge += types.back()->charge();
393  parity *= types.back()->parity();
394  }
395  if (types.size() == 2) {
396  const int s1 = types[0]->spin();
397  const int s2 = types[1]->spin();
398  min_L = min_angular_momentum(s0, s1, s2);
399  max_L = (s0 + s1 + s2) / 2;
400  } else if (types.size() == 3) {
401  const int s1 = types[0]->spin();
402  const int s2 = types[1]->spin();
403  const int s3 = types[2]->spin();
404  min_L = min_angular_momentum(s0, s1, s2, s3);
405  max_L = (s0 + s1 + s2 + s3) / 2;
406  } else {
407  throw InvalidDecay(isotype_mother->name() +
408  " decay mode has an invalid number of particles"
409  " in the final state " +
410  "(line " + std::to_string(linenumber) + ": \"" +
411  trimmed + "\")");
412  }
413  bool no_decays = true;
414  for (size_t m = 0; m < mother_states.size(); m++) {
415  if (mother_states[m]->charge() == charge) {
416  logg[LDecayModes].debug(
417  "decay mode found: ", mother_states[m]->name(), " -> ",
418  decay_particles.size());
419  decay_modes_to_add[m].add_mode(mother_states[m], ratio, L, types);
420  no_decays = false;
421  }
422  }
423  if (no_decays) {
424  throw InvalidDecay(isotype_mother->name() +
425  " decay mode violates charge conservation " +
426  "(line " + std::to_string(linenumber) + ": \"" +
427  trimmed + "\")");
428  }
429  }
430  // Take angular momentum into account.
431  // FIXME: At the moment this is not correct for 3-body decays (see #517),
432  // therefore only check paritiy for 2-body decays below.
433  if (L % 2 == 1) {
434  parity = -parity;
435  }
436  // Make sure the decay has the correct parity for 2-body decays
437  if (decay_particles.size() == 2 && parity != mother_states[0]->parity()) {
438  throw InvalidDecay(mother_states[0]->name() +
439  " decay mode violates parity conservation " +
440  "(line " + std::to_string(linenumber) + ": \"" +
441  trimmed + "\")");
442  }
443  // Make sure the decay has a correct angular momentum.
444  if (L < min_L || L > max_L) {
445  throw InvalidDecay(
446  mother_states[0]->name() +
447  " decay mode violates angular momentum conservation: " +
448  std::to_string(L) + " not in [" + std::to_string(min_L) + ", " +
449  std::to_string(max_L) + "] (line " + std::to_string(linenumber) +
450  ": \"" + trimmed + "\")");
451  }
452  }
453  linenumber++;
454  }
455  end_of_decaymodes();
456 
457  // Check whether the mother's pole mass is strictly larger than the minimal
458  // masses of the daughters. This is required by the Manley-Saleski ansatz.
459  const auto &particles = ParticleType::list_all();
460  for (const auto &mother : particles) {
461  if (mother.is_stable()) {
462  continue;
463  }
464  const auto &decays = mother.decay_modes().decay_mode_list();
465  for (const auto &decay : decays) {
466  if (mother.mass() <= decay->threshold()) {
467  std::stringstream s;
468  s << mother.name() << " → ";
469  for (const auto &p : decay->particle_types()) {
470  s << p->name();
471  }
472  s << " with " << mother.mass() << " ≤ " << decay->threshold();
473  throw InvalidDecay(
474  "For all decays, the minimum mass of daughters"
475  "must be smaller\nthan the mother's pole mass "
476  "(Manley-Saleski Ansatz)\n"
477  "Violated by the following decay: " +
478  s.str());
479  }
480  }
481  }
482  if (total_large_renormalized > 0) {
483  logg[LDecayModes].warn(
484  "Branching ratios of ", total_large_renormalized,
485  " hadrons were renormalized by more than 1% to have sum 1.");
486  }
487 }
bool is_empty() const
Definition: decaymodes.h:60
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
static const IsoParticleType * try_find(const std::string &name)
Returns the IsoParticleType pointer for the given name.
static const IsoParticleType & find(const std::string &name)
Returns the IsoParticleType object 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...
static const ParticleType & find(PdgCode pdgcode)
Returns the ParticleType object for the given pdgcode.
Definition: particletype.cc:99
static bool exists(PdgCode pdgcode)
static const ParticleTypeList & list_all()
Definition: particletype.cc:51
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
constexpr int p
Proton.
static int min_angular_momentum(int s0, int s1, int s2)
Definition: decaymodes.cc:138
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...
build_vector_< Line > line_parser(const std::string &input)
Helper function for parsing particles.txt and decaymodes.txt.
std::string trim(const std::string &s)
Strip leading and trailing whitespaces.
Parity
Represent the parity of a particle type.
Definition: particletype.h:25
@ Pos
Positive parity.
std::vector< DecayTypePtr > * all_decay_types
Global pointer to the decay types list.
Definition: decaymodes.cc:27
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:26

◆ get_decay_type()

DecayType * smash::DecayModes::get_decay_type ( ParticleTypePtr  mother,
ParticleTypePtrList  particle_types,
int  L 
)
static

Retrieve a decay type.

Parameters
[in]motherthe decaying particle
[in]particle_typesthe products of the decay
[in]Lthe angular momentum
Returns
the corresponding DecayType object
Exceptions
InvalidDecayif there are less than 2 or more than 3 products

Definition at line 43 of file decaymodes.cc.

45  {
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  std::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  std::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  std::make_unique<TwoBodyDecaySemistable>(particle_types, L));
71  } else {
72  all_decay_types->emplace_back(
73  std::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(std::make_unique<ThreeBodyDecayDilepton>(
81  mother, particle_types, L));
82  } else {
83  all_decay_types->emplace_back(
84  std::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 }
bool is_dilepton(const PdgCode pdg1, const PdgCode pdg2)
Definition: pdgcode.h:1117
bool has_lepton_pair(const PdgCode pdg1, const PdgCode pdg2, const PdgCode pdg3)
Definition: pdgcode.h:1129

Friends And Related Function Documentation

◆ ParticleType::decay_modes

const DecayModes& ParticleType::decay_modes ( ) const
friend

allow ParticleType::decay_modes to access all_decay_modes

Member Data Documentation

◆ decay_modes_

DecayBranchList smash::DecayModes::decay_modes_
private

Vector of decay modes.

Each mode consists of a vector of the pdg codes of decay products and a ratio of this decay mode compared to all possible modes

Definition at line 123 of file decaymodes.h.

◆ all_decay_modes

std::vector< DecayModes > * smash::DecayModes::all_decay_modes = nullptr
staticprivate

A list of all DecayModes objects using the same indexing as all_particle_types.

Global pointer to the decay modes list.

Definition at line 132 of file decaymodes.h.


The documentation for this class was generated from the following files: