164 const auto &log = logger<LogArea::DecayModes>();
167 static std::vector<DecayTypePtr> decaytypes;
173 static std::vector<DecayModes> decaymodes;
178 const IsoParticleType *isotype_mother =
nullptr;
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()) {
190 throw MissingDecays(
"No decay modes found for particle " +
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]);
200 if (isotype_mother->has_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();
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();
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);
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);
241 for (
size_t m = 0; m < mother_states.size(); m++) {
242 PdgCode pdgcode = mother_states[m]->pdgcode();
244 throw LoadFailure(
"Duplicate entry for " + name +
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()) {
292 const IsoParticleType &isotype_daughter_1 =
294 const IsoParticleType &isotype_daughter_2 =
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();
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();
330 throw InvalidDecay(isotype_mother->name() +
331 " decay mode is forbidden by isospin: \"" +
332 line.text +
"\"" + s.str());
337 const IsoParticleType &isotype_daughter_1 =
339 const IsoParticleType &isotype_daughter_2 =
341 const IsoParticleType &isotype_daughter_3 =
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();
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 :
358 isotype_daughter_3.get_states()) {
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;
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) +
": \"" +
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);
432 throw InvalidDecay(isotype_mother->name() +
433 " decay mode violates charge conservation " +
434 "(line " + std::to_string(linenumber) +
": \"" +
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) +
": \"" +
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.
std::string trim(const std::string &s)
Strip leading and trailing whitespaces.
static bool exists(PdgCode pdgcode)
Parity
Represent the parity of a particle type.
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()
static int min_angular_momentum(int s0, int s1, int s2)
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...
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.
static std::vector< DecayModes > * all_decay_modes
A list of all DecayModes objects using the same indexing as all_particle_types.