1+ #ifndef O2_COALESCENCE_H
2+ #define O2_COALESCENCE_H
3+
4+ #include " Pythia8/Pythia.h"
5+ #include " fairlogger/Logger.h"
6+ #include " TParticlePDG.h"
7+ #include " TDatabasePDG.h"
8+ #include " TSystem.h"
9+ #include " TMath.h"
10+ #include < cmath>
11+ #include < vector>
12+ #include < fstream>
13+ #include < string>
14+ using namespace Pythia8 ;
15+
16+ namespace o2
17+ {
18+ // / Coalescence afterburner for Pythia8
19+ // / Utility to compute naive coalescence afterburner as done in PRL 126, 101101 (2021)
20+
21+ enum NucleiBits {
22+ kDeuteron = 0 ,
23+ kTriton = 1 ,
24+ kHe3 = 2 ,
25+ kHyperTriton = 3 ,
26+ kHe4 = 4 ,
27+ };
28+
29+ std::vector<unsigned int > pdgList = {10010010 , 1000010030 , 1000020030 , 1010010030 , 1000020040 };
30+ std::vector<float > massList = {1.875612 , 2.80892113298 , 2.808391 , 2.991134 , 3.727379 };
31+
32+ bool coalPythia8 (Pythia8::Event& event, int charge, int pdgCode, float mass, double coalescenceRadius, int iD1, int iD2, int iD3 = -1 , int iD4 = -1 )
33+ {
34+ std::vector<int > nucleonIDs = std::vector<int >{iD1, iD2};
35+ // add A=3 and A=4 nuclei if enabled
36+ if (iD3 > 0 ) {
37+ nucleonIDs.push_back (iD3);
38+ }
39+ if (iD4 > 0 ) {
40+ nucleonIDs.push_back (iD4);
41+ }
42+ // coalescence afterburner
43+ Pythia8::Vec4 p;
44+ for (auto nID : nucleonIDs) {
45+ if (event[nID].status () < 0 ) {
46+ return false ;
47+ }
48+ p += event[nID].p ();
49+ }
50+ bool isCoalescence = true ;
51+ for (auto nID : nucleonIDs) {
52+ auto pN = event[nID].p ();
53+ pN.bstback (p);
54+ if (pN.pAbs () > coalescenceRadius) {
55+ isCoalescence = false ;
56+ break ;
57+ }
58+ }
59+ if (!isCoalescence) {
60+ return false ;
61+ }
62+ p.e (std::hypot (p.pAbs (), mass));
63+ // / In order to avoid the transport of the mother particles, but to still keep them in the stack, we set the status to negative and we mark the nucleus status as 94 (decay product)
64+ event.append ((charge * 2 - 1 ) * pdgCode, 94 , 0 , 0 , 0 , 0 , 0 , 0 , p.px (), p.py (), p.pz (), p.e (), mass);
65+ for (auto nID : nucleonIDs) {
66+ event[nID].statusNeg ();
67+ event[nID].daughter1 (event.size () - 1 );
68+ }
69+ fmt::printf (" >> Adding a %i with p = %f, %f, %f, E = %f\n " , (charge * 2 - 1 ) * pdgCode, p.px (), p.py (), p.pz (), p.e ());
70+ return true ;
71+ }
72+
73+ bool CoalAfterburner (Pythia8::Event& event, std::vector<unsigned int > inputPdgList = {}, double coalMomentum = 0.4 , int firstDauID = -1 , int lastDauId = -1 )
74+ {
75+ const double coalescenceRadius{0.5 * 1.122462 * coalMomentum};
76+
77+ // if coalescence from a heavy hadron, loop only between firstDauID and lastDauID
78+ int loopStart = firstDauID > 0 ? firstDauID : 0 ;
79+ int loopEnd = lastDauId > 0 ? lastDauId : event.size ();
80+ // fill the nuclear mask
81+ uint8_t nuclearMask = 0 ;
82+ for (auto nuclPdg : inputPdgList) {
83+ if (nuclPdg == pdgList[NucleiBits::kDeuteron ]) {
84+ nuclearMask |= (1 << kDeuteron );
85+ } else if (nuclPdg == pdgList[NucleiBits::kTriton ]) {
86+ nuclearMask |= (1 << kTriton );
87+ } else if (nuclPdg == pdgList[NucleiBits::kHe3 ]) {
88+ nuclearMask |= (1 << kHe3 );
89+ } else if (nuclPdg == pdgList[NucleiBits::kHyperTriton ]) {
90+ nuclearMask |= (1 << kHyperTriton );
91+ } else if (nuclPdg == pdgList[NucleiBits::kHe4 ]) {
92+ nuclearMask |= (1 << kHe4 );
93+ } else {
94+ LOG (fatal) << " Unknown pdg code for coalescence generator: " << nuclPdg;
95+ return false ;
96+ }
97+ }
98+ // fill nucleon pools
99+ std::vector<int > protons[2 ], neutrons[2 ], lambdas[2 ];
100+ for (auto iPart{loopStart}; iPart < loopEnd; ++iPart) {
101+ if (std::abs (event[iPart].y ()) > 1 .) // skip particles with y > 1
102+ {
103+ continue ;
104+ }
105+ if (std::abs (event[iPart].id ()) == 2212 ) {
106+ protons[event[iPart].id () > 0 ].push_back (iPart);
107+ } else if (std::abs (event[iPart].id ()) == 2112 ) {
108+ neutrons[event[iPart].id () > 0 ].push_back (iPart);
109+ } else if (std::abs (event[iPart].id ()) == 3122 && (nuclearMask & (1 << kHyperTriton ))) {
110+ lambdas[event[iPart].id () > 0 ].push_back (iPart);
111+ }
112+ }
113+ // run coalescence
114+ bool coalHappened = false ;
115+ for (int iC{0 }; iC < 2 ; ++iC) {
116+ for (int iP{0 }; iP < protons[iC].size (); ++iP) {
117+ for (int iN{0 }; iN < neutrons[iC].size (); ++iN) {
118+ if (nuclearMask & (1 << kDeuteron )) {
119+ coalHappened |= coalPythia8 (event, iC, pdgList[kDeuteron ], massList[kDeuteron ], coalescenceRadius, protons[iC][iP], neutrons[iC][iN]);
120+ }
121+ if (nuclearMask & (1 << kTriton )) {
122+ for (int iN2{iN + 1 }; iN2 < neutrons[iC].size (); ++iN2) {
123+ coalHappened |= coalPythia8 (event, iC, pdgList[kTriton ], massList[kTriton ], coalescenceRadius, protons[iC][iP], neutrons[iC][iN], neutrons[iC][iN2]);
124+ }
125+ }
126+ if (nuclearMask & (1 << kHe3 )) {
127+ for (int iP2{iP + 1 }; iP2 < protons[iC].size (); ++iP2) {
128+ coalHappened |= coalPythia8 (event, iC, pdgList[kHe3 ], massList[kHe3 ], coalescenceRadius, protons[iC][iP], protons[iC][iP2], neutrons[iC][iN]);
129+ }
130+ }
131+ if (nuclearMask & (1 << kHyperTriton )) {
132+ for (int iL{0 }; iL < lambdas[iC].size (); ++iL) {
133+ coalHappened |= coalPythia8 (event, iC, pdgList[kHyperTriton ], massList[kHyperTriton ], coalescenceRadius, protons[iC][iP], neutrons[iC][iN], lambdas[iC][iL]);
134+ }
135+ }
136+ if (nuclearMask & (1 << kHe4 )) {
137+ for (int iP2{iP + 1 }; iP2 < protons[iC].size (); ++iP2) {
138+ for (int iN2{iN + 1 }; iN2 < neutrons[iC].size (); ++iN2) {
139+ coalHappened |= coalPythia8 (event, iC, pdgList[kHe4 ], massList[kHe4 ], coalescenceRadius, protons[iC][iP], protons[iC][iP2], neutrons[iC][iN], neutrons[iC][iN2]);
140+ }
141+ }
142+ }
143+ }
144+ }
145+ }
146+ return coalHappened;
147+ }
148+
149+ } // namespace o2
150+
151+ #endif // O2_COALESCENCE_H
0 commit comments