1919#ifndef ALICE3_CORE_DECAYER_H_
2020#define ALICE3_CORE_DECAYER_H_
2121
22+ #include " ALICE3/Core/ConfigurationParser.h"
2223#include " ALICE3/Core/OTFParticle.h"
2324#include " ALICE3/Core/TrackUtilities.h"
2425
26+ #include < CCDB/BasicCCDBManager.h>
2527#include < CommonConstants/PhysicsConstants.h>
28+ #include < Framework/Logger.h>
2629#include < MathUtils/Primitive2D.h>
2730#include < ReconstructionDataFormats/Track.h>
2831
3437#include < array>
3538#include < cmath>
3639#include < cstddef>
40+ #include < map>
41+ #include < sstream>
42+ #include < string>
43+ #include < unordered_map>
3744#include < vector>
3845
3946namespace o2 ::upgrade
@@ -45,6 +52,53 @@ class Decayer
4552 // Default constructor
4653 Decayer () = default ;
4754
55+ static constexpr double BranchingRatioTolerance = 0.01 ;
56+
57+ struct DecayChannel {
58+ double branchingRatio{};
59+ std::vector<int > daughters;
60+ };
61+
62+ template <typename TDatabase>
63+ void loadDecayTable (const std::string& path, const TDatabase& pdgDB, o2::ccdb::BasicCCDBManager* ccdb = nullptr )
64+ {
65+ std::string fileName = o2::fastsim::ConfigurationParser::accessFile (path, " ./.ALICE3/Configuration/" , ccdb);
66+ std::vector<std::string> mothers;
67+ const std::map<std::string, std::map<std::string, std::string>> config = o2::fastsim::ConfigurationParser::parseTEnvConfiguration (fileName, mothers);
68+
69+ mDecayTable .clear ();
70+ for (const auto & [motherName, channels] : config) {
71+ const int motherPdg = std::stoi (motherName);
72+ if (!pdgDB->GetParticle (motherPdg)) {
73+ LOG (fatal) << " Decay table: unknown mother PDG code " << motherPdg;
74+ }
75+ for (const auto & [channelName, value] : channels) {
76+ DecayChannel channel;
77+ std::istringstream stream (value);
78+ stream >> channel.branchingRatio ;
79+ int daughterPdg{};
80+ while (stream >> daughterPdg) {
81+ if (!pdgDB->GetParticle (daughterPdg)) {
82+ LOG (fatal) << " Decay table: unknown daughter PDG code " << daughterPdg << " in " << motherName << " ." << channelName;
83+ }
84+ channel.daughters .push_back (daughterPdg);
85+ }
86+ if (!stream.eof () || channel.branchingRatio <= 0 . || channel.daughters .size () < 2 ) {
87+ LOG (fatal) << " Decay table: invalid channel " << motherName << " ." << channelName << " : \" " << value << " \" " ;
88+ }
89+ mDecayTable [motherPdg].push_back (channel);
90+ }
91+ double brTotal = 0 .;
92+ for (const auto & channel : mDecayTable [motherPdg]) {
93+ brTotal += channel.branchingRatio ;
94+ }
95+ if (std::abs (brTotal - 1 .) > BranchingRatioTolerance) {
96+ LOG (fatal) << " Decay table: branching ratios of PDG " << motherPdg << " sum to " << brTotal << " , expected 1 within " << BranchingRatioTolerance;
97+ }
98+ LOG (info) << " Decay table: overriding " << mDecayTable [motherPdg].size () << " decay channel(s) of PDG " << motherPdg;
99+ }
100+ }
101+
48102 template <typename TDatabase>
49103 std::vector<o2::upgrade::OTFParticle> decayParticle (const OTFParticle& particle, const TDatabase& pdgDB)
50104 {
@@ -69,21 +123,22 @@ class Decayer
69123 py = particle.py () * std::cos (mTheta ) + particle.px () * std::sin (mTheta );
70124 }
71125
72- double brTotal = 0 .;
73126 e = std::sqrt (mass * mass + px * px + py * py + particle.pz () * particle.pz ());
74- for (int ch = 0 ; ch < particleInfo->NDecayChannels (); ++ch) {
75- brTotal += particleInfo->DecayChannel (ch)->BranchingRatio ();
127+ const std::vector<DecayChannel> channels = getDecayChannels (particle.pdgCode (), particleInfo);
128+
129+ double brTotal = 0 .;
130+ for (const auto & channel : channels) {
131+ brTotal += channel.branchingRatio ;
76132 }
77133
78134 double brSum = 0 .;
79135 std::vector<double > dauMasses;
80136 std::vector<int > pdgCodesDaughters;
81137 const double randomChannel = mRand3 .Uniform (0 ., brTotal);
82- for (int ch = 0 ; ch < particleInfo-> NDecayChannels (); ++ch ) {
83- brSum += particleInfo-> DecayChannel (ch)-> BranchingRatio () ;
138+ for (const auto & channel : channels ) {
139+ brSum += channel. branchingRatio ;
84140 if (randomChannel < brSum) {
85- for (int dau = 0 ; dau < particleInfo->DecayChannel (ch)->NDaughters (); ++dau) {
86- const int pdgDau = particleInfo->DecayChannel (ch)->DaughterPdgCode (dau);
141+ for (const int pdgDau : channel.daughters ) {
87142 pdgCodesDaughters.push_back (pdgDau);
88143 const auto & dauInfo = pdgDB->GetParticle (pdgDau);
89144 dauMasses.push_back (dauInfo->Mass ());
@@ -166,6 +221,27 @@ class Decayer
166221 [[nodiscard]] float getDecayRadius () const { return static_cast <float >(std::hypot (mVx , mVy )); }
167222
168223 private:
224+ // / Decay channels from the loaded decay table if the mother is listed there, otherwise from TDatabasePDG
225+ template <typename TParticleInfo>
226+ std::vector<DecayChannel> getDecayChannels (const int pdgCode, const TParticleInfo& particleInfo) const
227+ {
228+ if (const auto it = mDecayTable .find (pdgCode); it != mDecayTable .end ()) {
229+ return it->second ;
230+ }
231+
232+ std::vector<DecayChannel> channels;
233+ for (int ch = 0 ; ch < particleInfo->NDecayChannels (); ++ch) {
234+ DecayChannel channel;
235+ channel.branchingRatio = particleInfo->DecayChannel (ch)->BranchingRatio ();
236+ for (int dau = 0 ; dau < particleInfo->DecayChannel (ch)->NDaughters (); ++dau) {
237+ channel.daughters .push_back (particleInfo->DecayChannel (ch)->DaughterPdgCode (dau));
238+ }
239+ channels.push_back (channel);
240+ }
241+ return channels;
242+ }
243+
244+ std::unordered_map<int , std::vector<DecayChannel>> mDecayTable ;
169245 double mBz {20 .}; // kG
170246 double mVx {-1 .}, mVy {-1 .}, mVz {-1 .};
171247 double mTheta {};
0 commit comments