22#include "Generators/GeneratorPythia8.h"
33#include "Pythia8/Pythia.h"
44#include "TRandom.h"
5+ #include "TDatabasePDG.h"
56#include <fairlogger/Logger.h>
67
78#include <string>
@@ -16,7 +17,7 @@ public:
1617 GeneratorPythia8GapTriggeredHF () = default ;
1718
1819 /// constructor
19- GeneratorPythia8GapTriggeredHF (int inputTriggerRatio = 5 , std ::vector < int > quarkPdgList = {}, std ::vector < int > hadronPdgList = {})
20+ GeneratorPythia8GapTriggeredHF (int inputTriggerRatio = 5 , std ::vector < int > quarkPdgList = {}, std ::vector < int > hadronPdgList = {}, std :: vector < std :: array < int , 2 >> partPdgToReplaceList = {}, std :: vector < float > freqReplaceList = {} )
2021 {
2122
2223 mGeneratedEvents = 0 ;
@@ -29,8 +30,22 @@ public:
2930 mHadronPdg = 0 ;
3031 mQuarkPdgList = quarkPdgList ;
3132 mHadronPdgList = hadronPdgList ;
33+ mPartPdgToReplaceList = partPdgToReplaceList ;
34+ mFreqReplaceList = freqReplaceList ;
35+ // Ds1*(2700), Ds1*(2860), Ds3*(2860), Xic(3055), Xic(3080)
36+ mCustomPartPdgs = {30433 , 40433 , 437 , 4325 , 4326 };
37+ mCustomPartMasses [30433 ] = 2.714f ;
38+ mCustomPartMasses [40433 ] = 2.859f ;
39+ mCustomPartMasses [437 ] = 2.860f ;
40+ mCustomPartMasses [4325 ] = 3.0559f ;
41+ mCustomPartMasses [4326 ] = 3.0772f ;
42+ mCustomPartWidths [30433 ] = 0.122f ;
43+ mCustomPartWidths [40433 ] = 0.160f ;
44+ mCustomPartWidths [437 ] = 0.053f ;
45+ mCustomPartWidths [4325 ] = 0.0078f ;
46+ mCustomPartWidths [4326 ] = 0.0036f ;
3247 Print ();
33- }
48+ }
3449
3550 /// Destructor
3651 ~GeneratorPythia8GapTriggeredHF () = default ;
@@ -54,6 +69,11 @@ public:
5469 {
5570 LOG (info )<<Form ("* %d ", pdg );
5671 }
72+ LOG (info )<<Form ("* Replacements : ");
73+ for (auto iRepl {0u }; iRepl < mPartPdgToReplaceList .size (); ++ iRepl )
74+ {
75+ LOGP (info , "* {} -> {} (freq. {})" , mPartPdgToReplaceList [iRepl ].at (0 ), mPartPdgToReplaceList [iRepl ].at (1 ), mFreqReplaceList [iRepl ]);
76+ }
5777 LOG (info )<<"***********************************************************************" ;
5878 }
5979
@@ -62,6 +82,21 @@ public:
6282 addSubGenerator (0 , "Minimum bias" );
6383 addSubGenerator (4 , "Charm injected" );
6484 addSubGenerator (5 , "Beauty injected" );
85+
86+ std ::vector < int > pdgToReplace {};
87+ for (auto iRepl {0u }; iRepl < mPartPdgToReplaceList .size () ; ++ iRepl )
88+ {
89+ for (auto iPdgRep {0u }; iPdgRep < pdgToReplace .size () ; ++ iPdgRep ) {
90+ if (mPartPdgToReplaceList [iRepl ].at (0 ) == pdgToReplace [iPdgRep ]) {
91+ mFreqReplaceList [iRepl ] += mFreqReplaceList [iPdgRep ];
92+ }
93+ }
94+ if (mFreqReplaceList [iRepl ] > 1.f ) {
95+ LOGP (fatal , "Replacing more than 100% of a particle!" );
96+ }
97+ pdgToReplace .push_back (mPartPdgToReplaceList [iRepl ].at (0 ));
98+ }
99+
65100 return o2 ::eventgen ::GeneratorPythia8 ::Init ();
66101 }
67102
@@ -115,7 +150,7 @@ protected:
115150 {
116151 if (GeneratorPythia8 ::generateEvent ())
117152 {
118- genOk = selectEvent (mPythia . event );
153+ genOk = selectEvent ();
119154 }
120155 }
121156 notifySubGenerator (mQuarkPdg );
@@ -136,34 +171,35 @@ protected:
136171 return true;
137172 }
138173
139- bool selectEvent (const Pythia8 :: Event & event )
174+ bool selectEvent ()
140175 {
141176
142177 bool isGoodAtPartonLevel {mQuarkPdgList .size () == 0 };
143178 bool isGoodAtHadronLevel {mHadronPdgList .size () == 0 };
179+ bool anyPartToReplace {mPartPdgToReplaceList .size () > 0 };
144180
145- for (auto iPart {0 }; iPart < event .size (); ++ iPart )
181+ for (auto iPart {0 }; iPart < mPythia . event .size (); ++ iPart )
146182 {
147183
148184 // search for Q-Qbar mother with at least one Q in rapidity window
149185 if (!isGoodAtPartonLevel )
150186 {
151- auto daughterList = event [iPart ].daughterList ();
187+ auto daughterList = mPythia . event [iPart ].daughterList ();
152188 bool hasQ = false, hasQbar = false, atSelectedY = false;
153189 for (auto iDau : daughterList )
154190 {
155- if (event [iDau ].id () == mQuarkPdg )
191+ if (mPythia . event [iDau ].id () == mQuarkPdg )
156192 {
157193 hasQ = true;
158- if ((event [iDau ].y () > mQuarkRapidityMin ) && (event [iDau ].y () < mQuarkRapidityMax ))
194+ if ((mPythia . event [iDau ].y () > mQuarkRapidityMin ) && (mPythia . event [iDau ].y () < mQuarkRapidityMax ))
159195 {
160196 atSelectedY = true;
161197 }
162198 }
163- if (event [iDau ].id () == - mQuarkPdg )
199+ if (mPythia . event [iDau ].id () == - mQuarkPdg )
164200 {
165201 hasQbar = true;
166- if ((event [iDau ].y () > mQuarkRapidityMin ) && (event [iDau ].y () < mQuarkRapidityMax ))
202+ if ((mPythia . event [iDau ].y () > mQuarkRapidityMin ) && (mPythia . event [iDau ].y () < mQuarkRapidityMax ))
167203 {
168204 atSelectedY = true;
169205 }
@@ -178,26 +214,89 @@ protected:
178214 // search for hadron in rapidity window
179215 if (!isGoodAtHadronLevel )
180216 {
181- int id = std ::abs (event [iPart ].id ());
182- float rap = event [iPart ].y ();
217+ int id = std ::abs (mPythia . event [iPart ].id ());
218+ float rap = mPythia . event [iPart ].y ();
183219 if (id == mHadronPdg && rap > mHadRapidityMin && rap < mHadRapidityMax )
184220 {
185221 isGoodAtHadronLevel = true;
186222 }
187223 }
188224
189- // we send the trigger
190- if (isGoodAtPartonLevel && isGoodAtHadronLevel )
225+ // if requested, we replace the particle
226+ const double pseudoRndm = mPythia .event [iPart ].pT () * 1000. - (int64_t )(mPythia .event [iPart ].pT () * 1000 );
227+ for (auto iPartToReplace {0u }; iPartToReplace < mPartPdgToReplaceList .size (); ++ iPartToReplace ) {
228+ if (std ::abs (mPythia .event [iPart ].id ()) == mPartPdgToReplaceList [iPartToReplace ][0 ] && pseudoRndm < mFreqReplaceList [iPartToReplace ]) {
229+ LOGP (debug , "REPLACING PARTICLE {} WITH {}, BEING RNDM {}" , mPartPdgToReplaceList [iPartToReplace ][0 ], mPartPdgToReplaceList [iPartToReplace ][1 ], pseudoRndm );
230+ replaceParticle (iPart , mPartPdgToReplaceList [iPartToReplace ][1 ]);
231+ break ;
232+ }
233+ }
234+
235+ // we send the trigger immediately (if there are no particles to replace, that can be different from the trigger ones)
236+ if (isGoodAtPartonLevel && isGoodAtHadronLevel && !anyPartToReplace )
191237 {
192- LOG (debug )<<"EVENT SELECTED: Found particle " <<event [iPart ].id ()<<" at rapidity " <<event [iPart ].y ()<<"\n" ;
238+ LOG (debug )<<"EVENT SELECTED: Found particle " <<mPythia . event [iPart ].id ()<<" at rapidity " <<mPythia . event [iPart ].y ()<<"\n" ;
193239 return true;
194240 }
195241 }
196242
243+ // we send the trigger
244+ if (isGoodAtPartonLevel && isGoodAtHadronLevel ) {
245+ return true;
246+ }
247+
197248 return false;
198249 };
199250
200- private :
251+ bool replaceParticle (int iPartToReplace , int pdgCodeNew ) {
252+
253+ std ::array < int , 2 > mothers = {mPythia .event [iPartToReplace ].mother1 (), mPythia .event [iPartToReplace ].mother2 ()};
254+
255+ std ::array < int , 25 > pdgDiquarks = {1103 , 2101 , 2103 , 2203 , 3101 , 3103 , 3201 , 3203 , 3303 , 4101 , 4103 , 4201 , 4203 , 4301 , 4303 , 4403 , 5101 , 5103 , 5201 , 5203 , 5301 , 5303 , 5401 , 5403 , 5503 };
256+
257+ for (auto const & mother : mothers ) {
258+ auto pdgMother = std ::abs (mPythia .event [mother ].id ());
259+ if (pdgMother > 100 && std ::find (pdgDiquarks .begin (), pdgDiquarks .end (), pdgMother ) == pdgDiquarks .end ()) {
260+ return false;
261+ }
262+ }
263+
264+ int charge = mPythia .event [iPartToReplace ].id () / std ::abs (mPythia .event [iPartToReplace ].id ());
265+ int status = mPythia .event [iPartToReplace ].status ();
266+ float px = mPythia .event [iPartToReplace ].px ();
267+ float py = mPythia .event [iPartToReplace ].py ();
268+ float pz = mPythia .event [iPartToReplace ].pz ();
269+ float mass = 0.f ;
270+
271+ if (std ::find (mCustomPartPdgs .begin (), mCustomPartPdgs .end (), pdgCodeNew ) == mCustomPartPdgs .end ()) { // not a custom particle
272+ float width = TDatabasePDG ::Instance ()-> GetParticle (pdgCodeNew )-> Width ();
273+ float massRest = TDatabasePDG ::Instance ()-> GetParticle (pdgCodeNew )-> Mass ();
274+ if (width > 0.f ) {
275+ mass = gRandom -> BreitWigner (massRest , width );
276+ } else {
277+ mass = massRest ;
278+ }
279+ } else {
280+ if (mCustomPartWidths [pdgCodeNew ] > 0.f ) {
281+ mass = gRandom -> BreitWigner (mCustomPartMasses [pdgCodeNew ], mCustomPartWidths [pdgCodeNew ]);
282+ } else {
283+ mass = mCustomPartMasses [pdgCodeNew ];
284+ }
285+ }
286+ float energy = std ::sqrt (px * px + py * py + pz * pz + mass * mass );
287+
288+ mPythia .event .remove (iPartToReplace , iPartToReplace , true); // we remove the original particle
289+ mPythia .event .append (charge * pdgCodeNew , status , 0 , 0 , 0 , 0 , 0 , 0 , px , py , pz , energy , mass );
290+ mPythia .event [mPythia .event .size ()- 1 ].mother1 (mothers [0 ]);
291+ mPythia .event [mPythia .event .size ()- 1 ].mother2 (mothers [1 ]);
292+ mPythia .moreDecays (); // we need to decay the new particle
293+
294+ return true;
295+ }
296+
297+ // id:all = name antiName spinType chargeType colType m0 mWidth mMin mMax tau0
298+
299+ private :
201300 // Interface to override import particles
202301 Pythia8 ::Event mOutputEvent ;
203302
@@ -209,6 +308,11 @@ private:
209308 float mHadRapidityMin ;
210309 float mHadRapidityMax ;
211310 unsigned int mUsedSeed ;
311+ std ::vector < std ::array < int , 2 >> mPartPdgToReplaceList ;
312+ std ::vector < float > mFreqReplaceList ;
313+ std ::vector < int > mCustomPartPdgs ;
314+ std ::map < int , float > mCustomPartMasses ;
315+ std ::map < int , float > mCustomPartWidths ;
212316
213317 // Control gap-triggering
214318 unsigned long long mGeneratedEvents ;
@@ -223,9 +327,9 @@ private:
223327
224328// Predefined generators:
225329// Charm-enriched
226- FairGenerator * GeneratorPythia8GapTriggeredCharm (int inputTriggerRatio , float yQuarkMin = -1.5 , float yQuarkMax = 1.5 , float yHadronMin = -1.5 , float yHadronMax = 1.5 , std ::vector < int > hadronPdgList = {})
330+ FairGenerator * GeneratorPythia8GapTriggeredCharm (int inputTriggerRatio , float yQuarkMin = -1.5 , float yQuarkMax = 1.5 , float yHadronMin = -1.5 , float yHadronMax = 1.5 , std ::vector < int > hadronPdgList = {}, std :: vector < std :: array < int , 2 >> partPdgToReplaceList = {}, std :: vector < float > freqReplaceList = {} )
227331{
228- auto myGen = new GeneratorPythia8GapTriggeredHF (inputTriggerRatio , std ::vector < int > {4 }, hadronPdgList );
332+ auto myGen = new GeneratorPythia8GapTriggeredHF (inputTriggerRatio , std ::vector < int > {4 }, hadronPdgList , partPdgToReplaceList , freqReplaceList );
229333 auto seed = (gRandom -> TRandom ::GetSeed () % 900000000 );
230334 myGen -> setUsedSeed (seed );
231335 myGen -> readString ("Random :setSeed on ");
@@ -239,9 +343,9 @@ FairGenerator *GeneratorPythia8GapTriggeredCharm(int inputTriggerRatio, float yQ
239343}
240344
241345// Beauty-enriched
242- FairGenerator * GeneratorPythia8GapTriggeredBeauty (int inputTriggerRatio , float yQuarkMin = -1.5 , float yQuarkMax = 1.5 , float yHadronMin = -1.5 , float yHadronMax = 1.5 , std ::vector < int > hadronPdgList = {})
346+ FairGenerator * GeneratorPythia8GapTriggeredBeauty (int inputTriggerRatio , float yQuarkMin = -1.5 , float yQuarkMax = 1.5 , float yHadronMin = -1.5 , float yHadronMax = 1.5 , std ::vector < int > hadronPdgList = {}, std :: vector < std :: array < int , 2 >> partPdgToReplaceList = {}, std :: vector < float > freqReplaceList = {} )
243347{
244- auto myGen = new GeneratorPythia8GapTriggeredHF (inputTriggerRatio , std ::vector < int > {5 }, hadronPdgList );
348+ auto myGen = new GeneratorPythia8GapTriggeredHF (inputTriggerRatio , std ::vector < int > {5 }, hadronPdgList , partPdgToReplaceList , freqReplaceList );
245349 auto seed = (gRandom -> TRandom ::GetSeed () % 900000000 );
246350 myGen -> setUsedSeed (seed );
247351 myGen -> readString ("Random:setSeed on" );
@@ -255,9 +359,9 @@ FairGenerator *GeneratorPythia8GapTriggeredBeauty(int inputTriggerRatio, float y
255359}
256360
257361// Charm and beauty enriched (with same ratio)
258- FairGenerator * GeneratorPythia8GapTriggeredCharmAndBeauty (int inputTriggerRatio , float yQuarkMin = -1.5 , float yQuarkMax = 1.5 , float yHadronMin = -1.5 , float yHadronMax = 1.5 , std ::vector < int > hadronPdgList = {})
362+ FairGenerator * GeneratorPythia8GapTriggeredCharmAndBeauty (int inputTriggerRatio , float yQuarkMin = -1.5 , float yQuarkMax = 1.5 , float yHadronMin = -1.5 , float yHadronMax = 1.5 , std ::vector < int > hadronPdgList = {}, std :: vector < std :: array < int , 2 >> partPdgToReplaceList = {}, std :: vector < float > freqReplaceList = {} )
259363{
260- auto myGen = new GeneratorPythia8GapTriggeredHF (inputTriggerRatio , std ::vector < int > {4 , 5 }, hadronPdgList );
364+ auto myGen = new GeneratorPythia8GapTriggeredHF (inputTriggerRatio , std ::vector < int > {4 , 5 }, hadronPdgList , partPdgToReplaceList , freqReplaceList );
261365 auto seed = (gRandom -> TRandom ::GetSeed () % 900000000 );
262366 myGen -> setUsedSeed (seed );
263367 myGen -> readString ("Random:setSeed on" );
@@ -270,13 +374,13 @@ FairGenerator *GeneratorPythia8GapTriggeredCharmAndBeauty(int inputTriggerRatio,
270374 return myGen ;
271375}
272376
273- FairGenerator * GeneratorPythia8GapHF (int inputTriggerRatio , float yQuarkMin = -1.5 , float yQuarkMax = 1.5 , float yHadronMin = -1.5 , float yHadronMax = 1.5 , std ::vector < int > quarkPdgList = {}, std ::vector < int > hadronPdgList = {})
377+ FairGenerator * GeneratorPythia8GapHF (int inputTriggerRatio , float yQuarkMin = -1.5 , float yQuarkMax = 1.5 , float yHadronMin = -1.5 , float yHadronMax = 1.5 , std ::vector < int > quarkPdgList = {}, std ::vector < int > hadronPdgList = {}, std :: vector < std :: array < int , 2 >> partPdgToReplaceList = {}, std :: vector < float > freqReplaceList = {} )
274378{
275379 if (hadronPdgList .size () == 0 && quarkPdgList .size () == 0 )
276380 {
277381 LOG (fatal ) << "GeneratorPythia8GapHF: At least one quark or hadron PDG code must be specified" ;
278382 }
279- auto myGen = new GeneratorPythia8GapTriggeredHF (inputTriggerRatio , quarkPdgList , hadronPdgList );
383+ auto myGen = new GeneratorPythia8GapTriggeredHF (inputTriggerRatio , quarkPdgList , hadronPdgList , partPdgToReplaceList , freqReplaceList );
280384 auto seed = (gRandom -> TRandom ::GetSeed () % 900000000 );
281385 myGen -> setUsedSeed (seed );
282386 myGen -> readString ("Random:setSeed on" );
0 commit comments