66#include "Pythia8/Pythia.h"
77#include "TRandom3.h"
88#include "TMath.h"
9- // #include "TF1.h"
109#include "TParticle.h"
1110#include "TSystem.h"
1211#if __has_include ("SimulationDataFormat/MCGenStatus.h" )
1918#endif
2019#include <cmath>
2120#include <string>
21+ #include <vector>
2222#endif
2323
24- // Double_t FuncLavy(Double_t *x, Double_t *par)
25- // {
26-
27- // Double_t p = (par[0] - 1) * (par[0] - 2) * par[1] * x[0] / (((pow((1 + (((sqrt((par[2] * par[2]) + (x[0] * x[0]))) - par[2]) / (par[0] * par[3]))), par[0]) * (par[0] * par[3] * ((par[0] * par[3]) + (par[2] * (par[0] - 2)))))));
28- // return (p);
29- // }
30-
3124class GeneratorPhiResonance : public o2 ::eventgen ::GeneratorPythia8
3225{
3326public :
3427 GeneratorPhiResonance (int resoPDG = 999999 ,
35- float ptMin = 0.0 , float ptMax = 50.0 ,
28+ int customPhiPDG = 888888 ,
29+ float ptMin = 0.0 , float ptMax = 50.0 , float ptMaxPhi = 100.0 ,
3630 float yMin = -1.0 , float yMax = 1.0 ,
3731 std ::string pythiaCfgMb = "${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_inel_136tev.cfg" ,
3832 int signalInterval = 3 )
39- : GeneratorPythia8 (), mResoPDG (resoPDG ), mPtMin (ptMin ), mPtMaxPhiPhi (ptMax ), mYMin (yMin ), mYMax (yMax ), mSignalInterval (signalInterval )
33+ : GeneratorPythia8 (), mResoPDG (resoPDG ), mCustomPhiPDG ( customPhiPDG ), mPtMin (ptMin ), mPtMaxPhiPhi (ptMax ), mPtMaxPhi ( ptMaxPhi ), mYMin (yMin ), mYMax (yMax ), mSignalInterval (signalInterval )
4034 {
41- // 1. Initialize Gun Pythia object & define custom resonance
42- // # id::all = name antiName spinType chargeType colType m0 mWidth mMin mMax tau0
35+ // 1. Define Custom Directly Injected Phi (PDG: 888888) with mass, width, and decay to kaons
36+ std ::string createCustomPhi = std ::to_string (mCustomPhiPDG ) + ":new = custom_phi custom_phi 3 0 0 1.019461 0.004249 0.980 1.100 0.0" ;
37+ std ::string customPhiMayDecay = std ::to_string (mCustomPhiPDG ) + ":mayDecay = on" ;
38+ std ::string addPhiDecayKPlusKMinus = std ::to_string (mCustomPhiPDG ) + ":addChannel = 1 0.492 0 321 -321" ;
39+
40+ // 2. Define Custom Signal Resonance (PDG: 999999) decay into standard Phis (333 333)
4341 std ::string createReso = std ::to_string (mResoPDG ) + ":new = f2_Custom void 5 0 0 2.714 0.012 2.05 3.50 0.0" ;
44- mPythiaGun .readString (createReso );
45- mPythiaGun .readString (std ::to_string (mResoPDG ) + ":mayDecay = on" );
46- // id:addChannel = onMode bRatio meMode product1 product2, (onMode = 1: allow decay, bRatio = branching ratio, meMode = matrix element mode where 0 is isotropic decay)
47- std ::string addDecay = std ::to_string (mResoPDG ) + ":addChannel = 1 1.0 0 333 333" ;
48- mPythiaGun .readString (addDecay );
42+ std ::string resoMayDecay = std ::to_string (mResoPDG ) + ":mayDecay = on" ;
43+ std ::string addResoDecay = std ::to_string (mResoPDG ) + ":addChannel = 1 1.0 0 333 333" ;
44+
45+ // Helper lambda to load custom particle definitions across ALL Pythia engines
46+ auto applyCustomParticles = [& ](Pythia8 ::Pythia & pythiaInst )
47+ {
48+ pythiaInst .readString (createCustomPhi );
49+ pythiaInst .readString (customPhiMayDecay );
50+ pythiaInst .readString (addPhiDecayKPlusKMinus );
51+ pythiaInst .readString (createReso );
52+ pythiaInst .readString (resoMayDecay );
53+ pythiaInst .readString (addResoDecay );
54+ };
55+
56+ // 1: Apply particle definitions to mPythia, mPythiaGun, and pythiaObjectMinimumBias
57+ applyCustomParticles (mPythia );
58+ applyCustomParticles (mPythiaGun );
4959
5060 mPythiaGun .readString ("ProcessLevel:all off" );
5161 mPythiaGun .readString ("Random:setSeed = on" );
5262 mPythiaGun .readString ("Random:seed = " + std ::to_string (1 + gRandom -> Integer (900000000 )));
5363 mPythiaGun .init ();
5464
55- // 2 . Initialize Minimum Bias Pythia Engine
65+ // 3 . Initialize Minimum Bias Pythia Engine
5666 if (pythiaCfgMb .empty ())
5767 {
5868 auto & param = o2 ::eventgen ::GeneratorPythia8Param ::Instance ();
@@ -67,25 +77,8 @@ public:
6777 pythiaObjectMinimumBias .readString ("Random:setSeed = on" );
6878 pythiaObjectMinimumBias .readString ("Random:seed = " + std ::to_string (1 + gRandom -> Integer (900000000 )));
6979
70- // Add custom particle definition to MB instance so particle table matches
71- pythiaObjectMinimumBias .readString (createReso );
72- pythiaObjectMinimumBias .readString (std ::to_string (mResoPDG ) + ":mayDecay = on" );
73- pythiaObjectMinimumBias .readString (addDecay );
74-
80+ applyCustomParticles (pythiaObjectMinimumBias );
7581 pythiaObjectMinimumBias .init ();
76-
77- // // Thermal pT distribution for phi-phi resonance
78- // mThermal = new TF1("mThermal", "x*sqrt(x*x+[0]*[0])*exp(-sqrt(x*x+[0]*[0])/[1])", mPtMin, mPtMaxPhiPhi);
79-
80- // // Lévy-Tsallis pT distribution for direct phi
81- // mLevyTsallis = new TF1("mLevyTsallis", FuncLavy, mPtMin, 100.0, 4);
82-
83- // mLevyTsallis->SetParameters(
84- // 7.60279, // n
85- // 0.0374237, // dN/dy
86- // 1.01946, // mass
87- // 0.338379 // T
88- // );
8982 }
9083
9184 Bool_t generateEvent () override
@@ -99,69 +92,77 @@ public:
9992 mbOK = pythiaObjectMinimumBias .next ();
10093 }
10194
102- // Copy MB event into mPythia
103- mPythia . event = pythiaObjectMinimumBias . event ;
95+ // 2: Copy MB event using exact pointer binding from generator_pythia8_LF_rapidity_width.C
96+ copyMinimumBiasEventForInjection () ;
10497
10598 // 2. Clear Gun event container
10699 mPythiaGun .event .reset ();
107100
108101 // 3. Inject Signal Gun Particles into mPythiaGun
109102 if (mEventCounter % mSignalInterval == 0 )
110103 {
111- // Theramal distribution
104+ // Resonant signal -> Decays into 333 333 (Standard Phis)
112105 injectParticle (mResoPDG , 1 , true);
113106 }
114107 else
115108 {
116- // From published
117- injectParticle (333 , 2 , false);
109+ // Directly injected uncorrelated Phi -> Uses Custom PDG 888888
110+ injectParticle (mCustomPhiPDG , 2 , false);
118111 }
119112
120113 // 4. Force Decay of injected particles using Pythia's Decayer
121- for (int i = 1 ; i < mPythiaGun .event .size (); ++ i )
122- {
123- if (mPythiaGun .event [i ].status () > 0 )
124- { // Active injected particles
125- mPythiaGun .particleData .mayDecay (mPythiaGun .event [i ].id (), true);
126- mPythiaGun .moreDecays ();
127- }
128- }
114+ mPythiaGun .moreDecays ();
115+ mPythiaGun .next ();
129116
130- // 5. Merge mPythiaGun event into mPythia.event
117+ // 3: Index Mapping during Event Merging
131118 int offset = mPythia .event .size ();
119+ std ::vector < int > indexMap (mPythiaGun .event .size (), 0 );
132120
133121 for (int i = 1 ; i < mPythiaGun .event .size (); ++ i )
134- { // Skip system particle 0
122+ {
123+ indexMap [i ] = mPythia .event .size ();
135124 Pythia8 ::Particle p = mPythiaGun .event [i ];
125+ mPythia .event .append (p );
126+ }
136127
137- // Adjust history indices accurately
138- int mother1 = ( p . mother1 () > 0 ) ? p . mother1 () + offset - 1 : p . mother1 ();
139- int mother2 = ( p . mother2 () > 0 ) ? p . mother2 () + offset - 1 : p . mother2 ();
140- int daughter1 = ( p . daughter1 () > 0 ) ? p . daughter1 () + offset - 1 : p . daughter1 () ;
141- int daughter2 = ( p . daughter2 () > 0 ) ? p . daughter2 () + offset - 1 : p . daughter2 () ;
128+ // Re-link mother and daughter index relationships accurately
129+ for ( int i = 1 ; i < mPythiaGun . event . size (); ++ i )
130+ {
131+ int newIdx = indexMap [ i ] ;
132+ Pythia8 :: Particle & p = mPythia . event [ newIdx ] ;
142133
143- p .mothers (mother1 , mother2 );
144- p .daughters (daughter1 , daughter2 );
134+ int m1 = p .mother1 ();
135+ int m2 = p .mother2 ();
136+ int d1 = p .daughter1 ();
137+ int d2 = p .daughter2 ();
145138
146- mPythia .event .append (p );
139+ p .mothers ((m1 > 0 && m1 < (int )indexMap .size ()) ? indexMap [m1 ] : 0 ,
140+ (m2 > 0 && m2 < (int )indexMap .size ()) ? indexMap [m2 ] : 0 );
141+
142+ p .daughters ((d1 > 0 && d1 < (int )indexMap .size ()) ? indexMap [d1 ] : 0 ,
143+ (d2 > 0 && d2 < (int )indexMap .size ()) ? indexMap [d2 ] : 0 );
147144 }
148145
149- // 6. CRITICAL : Restore Pythia particleData pointers for O2 exporter
146+ // 4 : Restore Pythia particleData pointers for O2 exporter
150147 mPythia .event .restorePtrs ();
151148
152- // 7. Invoke base generator hooks to sync O2 event record
153- // return GeneratorPythia8::generateEvent();
154- return true; // Skip base generator processing to avoid overwriting injected particles
149+ return true;
155150 }
156151
157152private :
153+ void copyMinimumBiasEventForInjection ()
154+ {
155+ mPythia .event = pythiaObjectMinimumBias .event ;
156+ mPythia .event .init ("Minimum-bias event with injected particles" , & mPythia .particleData );
157+ mPythia .event .restorePtrs ();
158+ }
159+
158160 void injectParticle (int pdg , int nParticles , bool thermalPt )
159161 {
160162 const double phiMass = 1.019461 ;
161163
162164 for (int i = 0 ; i < nParticles ; ++ i )
163165 {
164- // const double pt = gRandom->Uniform(mPtMin, mPtMaxPhiPhi);
165166 const double y = gRandom -> Uniform (mYMin , mYMax );
166167 const double phi = gRandom -> Uniform (0 , TMath ::TwoPi ());
167168
@@ -175,26 +176,17 @@ private:
175176 }
176177 else
177178 {
178- mass = mPythiaGun .particleData .mSel (pdg );
179+ mass = mPythiaGun .particleData .mSel (333 ); // Use standard phi mass for directly injected custom phi
179180 }
180181
181182 double pt ;
182-
183183 if (thermalPt )
184184 {
185- // const double T = 0.160;
186-
187- // mThermal->SetParameter(0, mass);
188- // mThermal->SetParameter(1, T);
189-
190- // pt = mThermal->GetRandom();
191-
192- pt = gRandom -> Uniform (mPtMin , mPtMaxPhiPhi ); // Falling back to flat pT due to low statistics in high pT
185+ pt = gRandom -> Uniform (mPtMin , mPtMaxPhiPhi );
193186 }
194187 else
195188 {
196- // pt = mLevyTsallis->GetRandom();
197- pt = gRandom -> Uniform (mPtMin , 100.0 ); // Falling back to flat pT due to low statistics in high pT
189+ pt = gRandom -> Uniform (mPtMin , mPtMaxPhi );
198190 }
199191
200192 const double px = pt * std ::cos (phi );
@@ -215,24 +207,23 @@ private:
215207 particle .yProd (0. );
216208 particle .zProd (0. );
217209
210+ mPythiaGun .particleData .mayDecay (pdg , true);
218211 mPythiaGun .event .append (particle );
219212 }
220213 }
221214
222215 int mEventCounter = 0 ;
223216 int mResoPDG ;
217+ int mCustomPhiPDG ;
224218 int mSignalInterval ;
225- float mPtMin , mPtMaxPhiPhi , mYMin , mYMax ;
219+ float mPtMin , mPtMaxPhiPhi , mPtMaxPhi , mYMin , mYMax ;
226220
227221 Pythia8 ::Pythia mPythiaGun ;
228222 Pythia8 ::Pythia pythiaObjectMinimumBias ;
229-
230- // TF1 *mThermal;
231- // TF1 *mLevyTsallis;
232223};
233224
234225/// Entry point for o2-sim
235- FairGenerator * generatePhiResonanceGun (int resoPDG = 999999 , float ptMin = 0.0 , float ptMax = 50.0 , float yMin = -1.0 , float yMax = 1.0 , std ::string pythiaCfgMb = "" , int signalInterval = 3 )
226+ FairGenerator * generatePhiResonanceGun (int resoPDG = 999999 , int customPhiPDG = 888888 , float ptMin = 0.0 , float ptMax = 50.0 , float ptMaxPhi = 100 .0 , float yMin = -1.0 , float yMax = 1.0 , std ::string pythiaCfgMb = "" , int signalInterval = 3 )
236227{
237- return new GeneratorPhiResonance (resoPDG , ptMin , ptMax , yMin , yMax , pythiaCfgMb , signalInterval );
228+ return new GeneratorPhiResonance (resoPDG , customPhiPDG , ptMin , ptMax , ptMaxPhi , yMin , yMax , pythiaCfgMb , signalInterval );
238229}
0 commit comments