30 ParticleTypePtrList particle_types) {
34 if (type == &mode->type()) {
35 mode->set_weight(mode->weight() + ratio);
40 decay_modes_.push_back(make_unique<DecayBranch>(*type, ratio));
44 ParticleTypePtrList particle_types,
50 if (type->has_mother(mother) && type->has_particles(particle_types) &&
51 type->angular_momentum() == L) {
57 switch (particle_types.size()) {
60 particle_types[1]->pdgcode())) {
62 make_unique<TwoBodyDecayDilepton>(particle_types, L));
63 }
else if (particle_types[0]->is_stable() &&
64 particle_types[1]->is_stable()) {
66 make_unique<TwoBodyDecayStable>(particle_types, L));
67 }
else if (particle_types[0]->is_stable() ||
68 particle_types[1]->is_stable()) {
70 make_unique<TwoBodyDecaySemistable>(particle_types, L));
73 make_unique<TwoBodyDecayUnstable>(particle_types, L));
78 particle_types[1]->pdgcode(),
79 particle_types[2]->pdgcode())) {
81 make_unique<ThreeBodyDecayDilepton>(mother, particle_types, L));
84 make_unique<ThreeBodyDecay>(particle_types, L));
89 "DecayModes::get_decay_type was instructed to add a decay mode " 91 std::to_string(particle_types.size()) +
92 " particles. This is an invalid input.");
99 const auto &log = logger<LogArea::DecayModes>();
101 bool is_large_renormalization =
false;
103 sum += mode->weight();
106 log.debug(
"Particle ", name,
107 ": Extremely small renormalization constant: ", sum,
108 "\n=> Skipping the renormalization.");
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;
114 mode->set_weight(mode->weight() / sum);
115 new_sum += mode->weight();
117 log.debug(
"After renormalization sum of ratios is ", new_sum);
119 return is_large_renormalization;
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");
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");
164 const auto &log = logger<LogArea::DecayModes>();
167 static std::vector<DecayTypePtr> decaytypes;
173 static std::vector<DecayModes> decaymodes;
179 ParticleTypePtrList mother_states;
180 std::vector<DecayModes> decay_modes_to_add;
181 int total_large_renormalized = 0;
183 const auto end_of_decaymodes = [&]() {
184 if (isotype_mother ==
nullptr) {
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()) {
191 mother_states[m]->name());
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();
198 decaymodes[
find_offset(pdgcode)] = std::move(decay_modes_to_add[m]);
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();
211 ParticleTypePtrList list = mode->particle_types();
212 for (
auto &type : list) {
213 if (type->has_antiparticle()) {
214 type = type->get_antiparticle();
217 decay_modes_anti.
add_mode(&type_anti, mode->weight(),
218 mode->angular_momentum(), list);
226 uint64_t linenumber = 1;
228 const auto trimmed =
trim(line.text);
229 assert(!trimmed.empty());
231 if (trimmed.find_first_of(
" \t") == std::string::npos) {
234 std::string name =
trim(line.text);
237 decay_modes_to_add.clear();
238 decay_modes_to_add.resize(mother_states.size());
239 log.debug(
"reading decay modes for " + name);
241 for (
size_t m = 0; m < mother_states.size(); m++) {
242 PdgCode pdgcode = mother_states[m]->pdgcode();
245 " in decaymodes.txt:" + std::to_string(linenumber));
249 std::istringstream lineinput(line.text);
250 std::vector<std::string> decay_particles;
251 decay_particles.reserve(3);
258 throw LoadFailure(
"Invalid angular momentum '" + std::to_string(L) +
259 "' in decaymodes.txt:" + std::to_string(line.number) +
260 ": '" + line.text +
"'");
267 decay_particles.emplace_back(name);
269 const bool is_multiplet = isotype;
271 if (!is_multiplet && !is_state) {
274 " is neither an isospin multiplet nor a particle." +
" (line " +
275 std::to_string(linenumber) +
": \"" + trimmed +
"\")");
277 const bool is_hadronic_multiplet =
278 is_multiplet && isotype->get_states()[0]->is_hadron();
279 multi &= is_hadronic_multiplet;
283 bool is_strong_decay;
284 const int s0 = isotype_mother->
spin();
290 switch (decay_particles.size()) {
296 parity = isotype_daughter_1.
parity() * isotype_daughter_2.
parity();
297 is_strong_decay = isotype_daughter_1.
is_hadron() &&
299 const int s1 = isotype_daughter_1.
spin();
300 const int s2 = isotype_daughter_2.
spin();
302 max_L = (s0 + s1 + s2) / 2;
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()) {
310 *daughter1, *daughter2, *mother_states[m]);
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],
319 {daughter1, daughter2});
320 forbidden_by_isospin =
false;
325 if (forbidden_by_isospin) {
327 s <<
",\nwhere isospin mother: " << isotype_mother->
isospin()
328 <<
", daughters: " << isotype_daughter_1.
isospin() <<
" " 329 << isotype_daughter_2.
isospin();
331 " decay mode is forbidden by isospin: \"" +
332 line.text +
"\"" + s.str());
343 parity = isotype_daughter_1.
parity() * isotype_daughter_2.
parity() *
344 isotype_daughter_3.
parity();
345 is_strong_decay = isotype_daughter_1.
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();
352 max_L = (s0 + s1 + s2 + s3) / 2;
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 :
360 *daughter1, *daughter2, *daughter3, *mother_states[m]);
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});
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) +
": \"" +
388 ParticleTypePtrList types;
391 is_strong_decay =
true;
392 for (
auto part : decay_particles) {
395 }
catch (std::runtime_error &e) {
396 throw std::runtime_error(std::string() + e.what() +
" (line " +
397 std::to_string(linenumber) +
": \"" +
400 charge += types.back()->charge();
401 parity *= types.back()->parity();
402 is_strong_decay &= types.back()->is_hadron();
404 if (types.size() == 2) {
405 const int s1 = types[0]->spin();
406 const int s2 = types[1]->spin();
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();
414 max_L = (s0 + s1 + s2 + s3) / 2;
417 " decay mode has an invalid number of particles" 418 " in the final state " +
419 "(line " + std::to_string(linenumber) +
": \"" +
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);
433 " decay mode violates charge conservation " +
434 "(line " + std::to_string(linenumber) +
": \"" +
443 if (is_strong_decay && parity != mother_states[0]->parity()) {
445 " decay mode violates parity conservation " +
446 "(line " + std::to_string(linenumber) +
": \"" +
450 if (L < min_L || L > max_L) {
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 +
"\")");
466 for (
const auto &mother : particles) {
467 if (mother.is_stable()) {
470 const auto &decays = mother.decay_modes().decay_mode_list();
471 for (
const auto &decay : decays) {
472 if (mother.mass() <= decay->threshold()) {
474 s << mother.name() <<
" → ";
475 for (
const auto p : decay->particle_types()) {
478 s <<
" with " << mother.mass() <<
" ≤ " << decay->threshold();
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: " +
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.");
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.
std::string trim(const std::string &s)
Strip leading and trailing whitespaces.
static bool exists(PdgCode pdgcode)
constexpr double really_small
Numerical error tolerance.
PdgCode get_antiparticle() const
Construct the antiparticle to a given PDG code.
bool is_dilepton(const PdgCode pdg1, const PdgCode pdg2)
static void load_decaymodes(const std::string &input)
Loads the DecayModes map as described in the input string.
Collection of useful constants that are known at compile time.
bool has_lepton_pair(const PdgCode pdg1, const PdgCode pdg2, const PdgCode pdg3)
Parity
Represent the parity of a particle type.
static DecayType * get_decay_type(ParticleTypePtr mother, ParticleTypePtrList particle_types, int L)
Retrieve a decay type.
DecayType is the abstract base class for all decay types.
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()
unsigned int spin() const
Returns twice the spin of the multiplet.
static int min_angular_momentum(int s0, int s1, int s2)
Particle type contains the static properties of a particle species.
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.
PdgCode stores a Particle Data Group Particle Numbering Scheme particle type number.
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 ...
std::vector< DecayTypePtr > * all_decay_types
Global pointer to the decay types list.
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.
build_vector_< Line > line_parser(const std::string &input)
Helper function for parsing particles.txt and decaymodes.txt.
std::size_t find_offset(PdgCode pdg)
Passes back the address offset of a particletype in the list of all particles.
A pointer-like interface to global references to ParticleType objects.
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...
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.
Line consists of a line number and the contents of that line.
const std::string & name() const
Returns the name of the multiplet.
const DecayBranchList & decay_mode_list() const