@@ -37,52 +37,46 @@ using namespace o2::framework::expressions;
3737
3838struct phiflowder {
3939
40- // Event selection
4140 Configurable<float > centMin{" centMin" , 0 .f , " Minimum centrality" };
4241 Configurable<float > centMax{" centMax" , 80 .f , " Maximum centrality" };
4342
44- // PID selection from stored bitmask
45- Configurable<bool > usePID{" usePID" , false , " Enable PID selection using stored bitmask" };
46- Configurable<int > pidChoice{" pidChoice" , 2 , " PID choice: 1, 2, 3, 4" };
47- Configurable<bool > pidExclusive{" pidExclusive" , false , " If true require only the chosen PID bit" };
43+ Configurable<bool > useStoredPID{" useStoredPID" , false , " Apply PID-bit selection using stored kaon PID mask" };
44+ Configurable<int > pidChoice{" pidChoice" , 2 , " PID choice: 1, 2, 3, or 4" };
45+ Configurable<bool > pidExclusive{" pidExclusive" , false , " Require exactly the selected PID bit" };
4846
49- // Event mixing
50- Configurable<int > nEvtMixing{" nEvtMixing" , 5 , " Number of events to mix" };
51- ConfigurableAxis cfgVtxBins{" cfgVtxBins" , {5 , -10.0 , 10.0 }, " Mixing bins in z vertex" };
52- ConfigurableAxis cfgMultBins{" cfgMultBins" , {8 , 0.0 , 80.0 }, " Mixing bins in centrality" };
47+ Configurable<bool > applyDeepAngle{" applyDeepAngle" , false , " Apply minimum opening-angle cut for K+K- pairs" };
48+ Configurable<float > deepAngleCut{" deepAngleCut" , 0 .04f , " Minimum K+K- opening angle" };
5349
54- // Histogram axes
55- ConfigurableAxis configAxisInvMass{" configAxisInvMass" , {120 , 0.9 , 1.2 }, " #it{M}_{K^{+}K^{-}} (GeV/#it{c}^{2})" };
56- ConfigurableAxis configAxisPt{" configAxisPt" , {100 , 0.0 , 10.0 }, " #it{p}_{T} (GeV/#it{c})" };
57- ConfigurableAxis configAxisCentrality{" configAxisCentrality" , {8 , 0.0 , 80.0 }, " Centrality (%)" };
50+ Configurable<float > massMin{" massMin" , 0 .9f , " Minimum K+K- mass to fill" };
51+ Configurable<float > massMax{" massMax" , 1 .2f , " Maximum K+K- mass to fill" };
52+
53+ ConfigurableAxis axisInvMass{" axisInvMass" , {120 , 0 .9f , 1 .2f }, " #it{M}_{K^{+}K^{-}} (GeV/#it{c}^{2})" };
54+ ConfigurableAxis axisPhiPt{" axisPhiPt" , {100 , 0 .f , 10 .f }, " #it{p}_{T}^{K^{+}K^{-}} (GeV/#it{c})" };
55+ ConfigurableAxis axisCent{" axisCent" , {80 , 0 .f , 80 .f }, " Centrality (%)" };
56+ ConfigurableAxis axisNKaons{" axisNKaons" , {300 , 0 .f , 300 .f }, " Number of stored kaons per event" };
57+ ConfigurableAxis axisNPairs{" axisNPairs" , {500 , 0 .f , 5000 .f }, " Number of K^{+}K^{-} pairs per event" };
5858
5959 HistogramRegistry histos{" histos" , {}, OutputObjHandlingPolicy::AnalysisObject};
6060
61- // PID bits: same convention as producer
6261 static constexpr uint8_t kPID1 = 1u << 0 ;
6362 static constexpr uint8_t kPID2 = 1u << 1 ;
6463 static constexpr uint8_t kPID3 = 1u << 2 ;
6564 static constexpr uint8_t kPID4 = 1u << 3 ;
6665
67- struct KaonDaughter {
68- float px;
69- float py;
70- float pz;
71- uint8_t pidMask;
72- };
66+ SliceCache cache;
7367
74- void init (o2::framework:: InitContext&)
68+ void init (InitContext const &)
7569 {
76-
77- histos.add (" hCentrality " , " Centrality distribution " , kTH1F , {configAxisCentrality });
78-
79- histos.add (" hSparseSame " , " Same-event sparse " , kTHnSparseF ,
80- {configAxisInvMass, configAxisPt, configAxisCentrality });
81- histos.add (" hSparseMixed " , " Mixed -event sparse " , kTHnSparseF ,
82- {configAxisInvMass, configAxisPt, configAxisCentrality });
70+ histos. add ( " hCentrality " , " Centrality distribution " , kTH1F , {axisCent});
71+ histos.add (" hPhiMassSame " , " Same-event K^{+}K^{-} invariant mass " , kTH1F , {axisInvMass });
72+ histos. add ( " hPhiPtSame " , " Same-event K^{+}K^{-} pT " , kTH1F , {axisPhiPt});
73+ histos.add (" hNplusPerEvent " , " Number of stored K^{+} per event " , kTH1F , {axisNKaons});
74+ histos. add ( " hNminusPerEvent " , " Number of stored K^{-} per event " , kTH1F , {axisNKaons });
75+ histos.add (" hNphiSamePerEvent " , " Number of same -event K^{+}K^{-} pairs per event " , kTH1F , {axisNPairs});
76+ histos. add ( " hSparseSame " , " Same-event K^{+}K^{-};M;pT;centrality " , kTHnSparseF , {axisInvMass, axisPhiPt, axisCent });
8377 }
8478
85- uint8_t requiredPidBit () const
79+ uint8_t getRequiredPidBit () const
8680 {
8781 switch (pidChoice.value ) {
8882 case 1 :
@@ -94,207 +88,121 @@ struct phiflowder {
9488 case 4 :
9589 return kPID4 ;
9690 default :
97- LOGF (warn, " pidChoice=%d is invalid. Using PID2 ." , pidChoice.value );
91+ LOGF (warn, " Invalid pidChoice=%d. PID2 will be used ." , pidChoice.value );
9892 return kPID2 ;
9993 }
10094 }
10195
102- bool passDaughterPID (uint8_t mask) const
96+ template <typename T>
97+ bool passStoredPID (const T& kaon) const
10398 {
104- if (!usePID .value ) {
99+ if (!useStoredPID .value ) {
105100 return true ;
106101 }
107102
108- const uint8_t bit = requiredPidBit ();
103+ const uint16_t mask = kaon.kaonPidMask ();
104+ const uint8_t requiredBit = getRequiredPidBit ();
109105
110106 if (pidExclusive.value ) {
111- return mask == bit ;
107+ return mask == requiredBit ;
112108 }
113109
114- return (mask & bit ) != 0 ;
110+ return (mask & requiredBit ) != 0 ;
115111 }
116112
117- template <typename TPhi>
118- bool passPairPID (const TPhi& phiCand) const
113+ bool passDeepAngle (float px1, float py1, float pz1, float px2, float py2, float pz2) const
119114 {
120- if (!usePID .value ) {
115+ if (!applyDeepAngle .value ) {
121116 return true ;
122117 }
123118
124- const uint16_t pairMask = phiCand.kaonPidMask ();
125-
126- const uint8_t kPlusMask = pairMask & 0xFF ;
127- const uint8_t kMinusMask = (pairMask >> 8 ) & 0xFF ;
128-
129- return passDaughterPID (kPlusMask ) && passDaughterPID (kMinusMask );
130- }
131-
132- void collectUniqueKPlusForEvent (const aod::KaonTracks& kaontracks,
133- int64_t eventId,
134- std::map<int64_t , KaonDaughter>& uniqueKPlus)
135- {
136- uniqueKPlus.clear ();
137-
138- for (const auto & phiCand : kaontracks) {
139- if (phiCand.kaonkaoneventId () != eventId) {
140- continue ;
141- }
119+ const double p1 = std::sqrt (px1 * px1 + py1 * py1 + pz1 * pz1);
120+ const double p2 = std::sqrt (px2 * px2 + py2 * py2 + pz2 * pz2);
142121
143- const uint16_t pairMask = phiCand.kaonPidMask ();
144- const uint8_t kPlusMask = pairMask & 0xFF ;
145-
146- if (!passDaughterPID (kPlusMask )) {
147- continue ;
148- }
149-
150- const int64_t kPlusId = phiCand.kaonIndex1 ();
151-
152- if (uniqueKPlus.find (kPlusId ) == uniqueKPlus.end ()) {
153- uniqueKPlus[kPlusId ] = {
154- phiCand.d1Px (),
155- phiCand.d1Py (),
156- phiCand.d1Pz (),
157- kPlusMask };
158- }
122+ if (p1 <= 0.0 || p2 <= 0.0 ) {
123+ return false ;
159124 }
160- }
161125
162- void collectUniqueKMinusForEvent (const aod::KaonTracks& kaontracks,
163- int64_t eventId,
164- std::map<int64_t , KaonDaughter>& uniqueKMinus)
165- {
166- uniqueKMinus.clear ();
126+ const double cosAngle = std::clamp ((px1 * px2 + py1 * py2 + pz1 * pz2) / (p1 * p2), -1.0 , 1.0 );
127+ const double angle = std::acos (cosAngle);
167128
168- for (const auto & phiCand : kaontracks) {
169- if (phiCand.kaonkaoneventId () != eventId) {
170- continue ;
171- }
129+ return angle >= deepAngleCut.value ;
130+ }
172131
173- const uint16_t pairMask = phiCand.kaonPidMask ();
174- const uint8_t kMinusMask = (pairMask >> 8 ) & 0xFF ;
132+ Filter centralityFilter = (aod::kaonevent::cent >= centMin) && (aod::kaonevent::cent < centMax);
175133
176- if (!passDaughterPID (kMinusMask )) {
177- continue ;
178- }
134+ using EventCandidates = soa::Filtered<aod::KaonEvents>;
179135
180- const int64_t kMinusId = phiCand.kaonIndex2 ();
136+ Partition<aod::KaonTracks> posKaons = aod::kaonpair::charge > int8_t {0 };
137+ Partition<aod::KaonTracks> negKaons = aod::kaonpair::charge < int8_t {0 };
181138
182- if (uniqueKMinus.find (kMinusId ) == uniqueKMinus.end ()) {
183- uniqueKMinus[kMinusId ] = {
184- phiCand.d2Px (),
185- phiCand.d2Py (),
186- phiCand.d2Pz (),
187- kMinusMask };
188- }
189- }
190- }
191-
192- // Filter reduced events
193- Filter centralityFilter = (aod::kaonkaonevent::cent >= centMin &&
194- aod::kaonkaonevent::cent < centMax);
195- using EventCandidates = soa::Filtered<aod::KaonkaonEvents>;
196-
197- void processSameData (EventCandidates::iterator const & collision,
198- aod::KaonTracks const & kaontracks)
139+ void processSameData (EventCandidates::iterator const & collision, aod::KaonTracks const & /* kaontracks*/ )
199140 {
200141 const float centrality = collision.cent ();
201- const int64_t eventId = collision.globalIndex ();
202142
203143 histos.fill (HIST (" hCentrality" ), centrality);
204144
205- for (const auto & phiCand : kaontracks) {
206- if (phiCand.kaonkaoneventId () != eventId) {
207- continue ;
208- }
209-
210- if (!passPairPID (phiCand)) {
211- continue ;
212- }
145+ auto posThisColl = posKaons->sliceByCached (aod::kaonpair::kaoneventId, collision.globalIndex (), cache);
146+ auto negThisColl = negKaons->sliceByCached (aod::kaonpair::kaoneventId, collision.globalIndex (), cache);
213147
214- ROOT ::Math::PxPyPzMVector kPlus (
215- phiCand.d1Px (),
216- phiCand.d1Py (),
217- phiCand.d1Pz (),
218- o2::constants::physics::MassKPlus);
148+ int nPlus = 0 ;
149+ int nMinus = 0 ;
150+ int nPhiSame = 0 ;
219151
220- ROOT ::Math::PxPyPzMVector kMinus (
221- phiCand.d2Px (),
222- phiCand.d2Py (),
223- phiCand.d2Pz (),
224- o2::constants::physics::MassKPlus);
225-
226- auto phi = kPlus + kMinus ;
227-
228- const float phiMass = phi.M ();
229- const float phiPt = phi.Pt ();
230-
231- histos.fill (HIST (" hSparseSame" ), phiMass, phiPt, centrality);
152+ for (const auto & kPlus : posThisColl) {
153+ if (passStoredPID (kPlus )) {
154+ nPlus++;
155+ }
232156 }
233- }
234-
235- PROCESS_SWITCH (phiflowder, processSameData, " Process same-event phi candidates" , true );
236157
237- using BinningType = ColumnBinningPolicy<aod::kaonkaonevent::Posz,
238- aod::kaonkaonevent::Cent>;
239-
240- void processMixedData (EventCandidates const & collisions,
241- aod::KaonTracks const & kaontracks)
242- {
243-
244- BinningType colBinning{{cfgVtxBins, cfgMultBins}, false };
245-
246- std::map<int64_t , KaonDaughter> kPlusEvent1 ;
247- std::map<int64_t , KaonDaughter> kMinusEvent2 ;
248- // int nMixedEventPairs = 0;
158+ for (const auto & kMinus : negThisColl) {
159+ if (passStoredPID (kMinus )) {
160+ nMinus++;
161+ }
162+ }
249163
250- for ( const auto & [collision1, collision2] :
251- selfCombinations (colBinning, nEvtMixing. value , - 1 , collisions, collisions)) {
164+ histos. fill ( HIST ( " hNplusPerEvent " ), nPlus);
165+ histos. fill ( HIST ( " hNminusPerEvent " ), nMinus);
252166
253- if (collision1.globalIndex () == collision2.globalIndex ()) {
167+ for (const auto & kPlus : posThisColl) {
168+ if (!passStoredPID (kPlus )) {
254169 continue ;
255170 }
256- // nMixedEventPairs++;
257-
258- const float centrality = collision1.cent ();
259171
260- collectUniqueKPlusForEvent (kaontracks,
261- collision1.globalIndex (),
262- kPlusEvent1 );
172+ ROOT ::Math::PxPyPzMVector plusVec (kPlus .px (), kPlus .py (), kPlus .pz (), o2::constants::physics::MassKPlus);
263173
264- collectUniqueKMinusForEvent (kaontracks,
265- collision2.globalIndex (),
266- kMinusEvent2 );
174+ for (const auto & kMinus : negThisColl) {
175+ if (!passStoredPID (kMinus )) {
176+ continue ;
177+ }
267178
268- // histos.fill(HIST("hNplusUnique"), kPlusEvent1.size());
269- // histos.fill(HIST("hNminusUnique"), kMinusEvent2.size());
179+ if (!passDeepAngle (kPlus .px (), kPlus .py (), kPlus .pz (), kMinus .px (), kMinus .py (), kMinus .pz ())) {
180+ continue ;
181+ }
270182
271- // const int nMixedKPairs = static_cast<int>(kPlusEvent1.size() * kMinusEvent2.size() );
183+ ROOT ::Math::PxPyPzMVector minusVec ( kMinus . px (), kMinus . py (), kMinus . pz (), o2::constants::physics::MassKPlus );
272184
273- // histos.fill(HIST("hNmixedKPairsPerEventPair"), nMixedKPairs) ;
185+ const auto phiCandidate = plusVec + minusVec ;
274186
275- for (const auto & [kPlusId , kPlus ] : kPlusEvent1 ) {
276- ROOT ::Math::PxPyPzMVector kPlusVec (
277- kPlus .px ,
278- kPlus .py ,
279- kPlus .pz ,
280- o2::constants::physics::MassKPlus);
187+ const float phiMass = phiCandidate.M ();
188+ const float phiPt = phiCandidate.Pt ();
281189
282- for (const auto & [kMinusId , kMinus ] : kMinusEvent2 ) {
283- ROOT ::Math::PxPyPzMVector kMinusVec (
284- kMinus .px ,
285- kMinus .py ,
286- kMinus .pz ,
287- o2::constants::physics::MassKPlus);
190+ if (phiMass < massMin || phiMass > massMax) {
191+ continue ;
192+ }
288193
289- auto phiMix = kPlusVec + kMinusVec ;
194+ histos.fill (HIST (" hPhiMassSame" ), phiMass);
195+ histos.fill (HIST (" hPhiPtSame" ), phiPt);
196+ histos.fill (HIST (" hSparseSame" ), phiMass, phiPt, centrality);
290197
291- histos.fill (HIST (" hSparseMixed" ), phiMix.M (), phiMix.Pt (), centrality);
292- }
198+ nPhiSame++;
293199 }
294200 }
295- // histos.fill(HIST("hNMixEventPairs"), nMixedEventPairs);
201+
202+ histos.fill (HIST (" hNphiSamePerEvent" ), nPhiSame);
296203 }
297- PROCESS_SWITCH (phiflowder, processMixedData, " Process mixed-event phi candidates" , false );
204+
205+ PROCESS_SWITCH (phiflowder, processSameData, " Process same-event K+K- pairs" , true );
298206};
299207
300208WorkflowSpec defineDataProcessing (ConfigContext const & cfgc)
0 commit comments