2222#include " Framework/ASoAHelpers.h"
2323#include " Framework/AnalysisDataModel.h"
2424#include " Framework/AnalysisTask.h"
25+ #include " Framework/BinningPolicy.h"
2526#include " Framework/StepTHn.h"
2627#include " Framework/runDataProcessing.h"
2728#include < Framework/Configurable.h>
3435
3536#include < fairlogger/Logger.h>
3637
38+ #include < cmath> // for std::fabs
39+ #include < deque>
3740#include < iostream>
3841#include < iterator>
42+ #include < set> // <<< CHANGED: for dedup sets
3943#include < string>
44+ #include < type_traits>
45+ #include < unordered_map> // <<< CHANGED: for seenMap
46+ #include < utility>
4047#include < vector>
4148
4249// o2 includes.
@@ -49,7 +56,7 @@ using namespace o2::framework::expressions;
4956using namespace o2 ::soa;
5057
5158struct lambdaspincorrderived {
52-
59+ // BinningType colBinning;
5360 struct : ConfigurableGroup {
5461 Configurable<std::string> cfgURL{" cfgURL" , " http://alice-ccdb.cern.ch" , " Address of the CCDB to browse" };
5562 Configurable<int64_t > nolaterthan{" ccdb-no-later-than" , std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now ().time_since_epoch ()).count (), " Latest acceptable timestamp of creation for the object" };
@@ -71,6 +78,7 @@ struct lambdaspincorrderived {
7178 Configurable<float > centMax{" centMax" , 80 , " Maximum Centrality" };
7279
7380 // Lambda selection ////////////
81+ Configurable<unsigned > harmonic{" harmonic" , 1 , " Harmonic delta phi" };
7482 Configurable<bool > useweight{" useweight" , 1 , " Use weight" };
7583 Configurable<bool > usePDGM{" usePDGM" , 1 , " Use PDG mass" };
7684 Configurable<bool > checkDoubleStatus{" checkDoubleStatus" , 0 , " Check Double status" };
@@ -183,7 +191,7 @@ struct lambdaspincorrderived {
183191 if (std::abs (candidate1.lambdaEta () - candidate2.lambdaEta ()) > etaMix) {
184192 return false ;
185193 }
186- if (std::abs (RecoDecay::constrainAngle (candidate1.lambdaPhi (), 0 .0F ) - RecoDecay::constrainAngle (candidate2.lambdaPhi (), 0 .0F )) > phiMix) {
194+ if (std::abs (RecoDecay::constrainAngle (candidate1.lambdaPhi (), 0 .0F , harmonic ) - RecoDecay::constrainAngle (candidate2.lambdaPhi (), 0 .0F , harmonic )) > phiMix) {
187195 return false ;
188196 }
189197 if (std::abs (candidate1.lambdaMass () - candidate2.lambdaMass ()) > massMix) {
@@ -195,7 +203,7 @@ struct lambdaspincorrderived {
195203 void fillHistograms (int tag1, int tag2,
196204 const ROOT::Math::PtEtaPhiMVector& particle1, const ROOT::Math::PtEtaPhiMVector& particle2,
197205 const ROOT::Math::PtEtaPhiMVector& daughpart1, const ROOT::Math::PtEtaPhiMVector& daughpart2,
198- double centrality, int datatype)
206+ double centrality, int datatype, float mixpairweight )
199207 {
200208
201209 auto lambda1Mass = 0.0 ;
@@ -230,41 +238,42 @@ struct lambdaspincorrderived {
230238
231239 auto cosThetaDiff = -999.0 ;
232240 cosThetaDiff = proton1LambdaRF.Vect ().Unit ().Dot (proton2LambdaRF.Vect ().Unit ());
233- double deltaPhi = std::abs (RecoDecay::constrainAngle (particle1Dummy.Phi (), 0 .0F ) - RecoDecay::constrainAngle (particle2Dummy.Phi (), 0 .0F ));
241+ double deltaPhi = std::abs (RecoDecay::constrainAngle (particle1Dummy.Phi (), 0 .0F , harmonic ) - RecoDecay::constrainAngle (particle2Dummy.Phi (), 0 .0F , harmonic ));
234242 double deltaEta = particle1Dummy.Eta () - particle2Dummy.Eta ();
235243 double deltaR = TMath::Sqrt (deltaEta * deltaEta + deltaPhi * deltaPhi);
236244
237245 if (datatype == 0 ) {
238- histos.fill (HIST (" hPtYSame" ), particle1.Pt (), particle1.Rapidity ());
246+ mixpairweight = 1.0 ;
247+ histos.fill (HIST (" hPtYSame" ), particle1.Pt (), particle1.Rapidity (), mixpairweight);
239248 if (tag1 == 0 && tag2 == 0 ) {
240- histos.fill (HIST (" hSparseLambdaLambda" ), particle1.M (), particle2.M (), cosThetaDiff, centrality, deltaR);
241- histos.fill (HIST (" hLambdaSameForLL" ), particle1.Pt (), particle1.Eta (), RecoDecay::constrainAngle (particle1.Phi (), 0 .0F ) );
249+ histos.fill (HIST (" hSparseLambdaLambda" ), particle1.M (), particle2.M (), cosThetaDiff, centrality, deltaR, mixpairweight );
250+ histos.fill (HIST (" hLambdaSameForLL" ), particle1.Pt (), particle1.Eta (), RecoDecay::constrainAngle (particle1.Phi (), 0 .0F , harmonic), mixpairweight );
242251 } else if ((tag1 == 0 && tag2 == 1 ) || (tag1 == 1 && tag2 == 0 )) {
243- histos.fill (HIST (" hSparseLambdaAntiLambda" ), particle1.M (), particle2.M (), cosThetaDiff, centrality, deltaR);
244- histos.fill (HIST (" hLambdaSameForLAL" ), particle1.Pt (), particle1.Eta (), RecoDecay::constrainAngle (particle1.Phi (), 0 .0F ) );
252+ histos.fill (HIST (" hSparseLambdaAntiLambda" ), particle1.M (), particle2.M (), cosThetaDiff, centrality, deltaR, mixpairweight );
253+ histos.fill (HIST (" hLambdaSameForLAL" ), particle1.Pt (), particle1.Eta (), RecoDecay::constrainAngle (particle1.Phi (), 0 .0F , harmonic), mixpairweight );
245254 } else if (tag1 == 1 && tag2 == 1 ) {
246- histos.fill (HIST (" hSparseAntiLambdaAntiLambda" ), particle1.M (), particle2.M (), cosThetaDiff, centrality, deltaR);
247- histos.fill (HIST (" hAntiLambdaSameForALAL" ), particle1.Pt (), particle1.Eta (), RecoDecay::constrainAngle (particle1.Phi (), 0 .0F ) );
255+ histos.fill (HIST (" hSparseAntiLambdaAntiLambda" ), particle1.M (), particle2.M (), cosThetaDiff, centrality, deltaR, mixpairweight );
256+ histos.fill (HIST (" hAntiLambdaSameForALAL" ), particle1.Pt (), particle1.Eta (), RecoDecay::constrainAngle (particle1.Phi (), 0 .0F , harmonic), mixpairweight );
248257 }
249258 } else if (datatype == 1 ) {
250- double weight1 = 1.0 ;
251- double weight2 = 1.0 ;
252- double weight3 = 1.0 ;
259+ double weight1 = mixpairweight ;
260+ double weight2 = mixpairweight ;
261+ double weight3 = mixpairweight ;
253262 if (useweight) {
254- weight1 = hweight1->GetBinContent (hweight1->FindBin (particle1.Pt (), particle1.Eta (), particle1.Phi ()));
255- weight2 = hweight2->GetBinContent (hweight2->FindBin (particle1.Pt (), particle1.Eta (), particle1.Phi ()));
256- weight3 = hweight3->GetBinContent (hweight3->FindBin (particle1.Pt (), particle1.Eta (), particle1.Phi ()));
263+ weight1 = mixpairweight * hweight1->GetBinContent (hweight1->FindBin (particle1.Pt (), particle1.Eta (), particle1.Phi ()));
264+ weight2 = mixpairweight * hweight2->GetBinContent (hweight2->FindBin (particle1.Pt (), particle1.Eta (), particle1.Phi ()));
265+ weight3 = mixpairweight * hweight3->GetBinContent (hweight3->FindBin (particle1.Pt (), particle1.Eta (), particle1.Phi ()));
257266 }
258267 histos.fill (HIST (" hPtYMix" ), particle1.Pt (), particle1.Rapidity ());
259268 if (tag1 == 0 && tag2 == 0 ) {
260269 histos.fill (HIST (" hSparseLambdaLambdaMixed" ), particle1.M (), particle2.M (), cosThetaDiff, centrality, deltaR, weight1);
261- histos.fill (HIST (" hLambdaMixForLL" ), particle1.Pt (), particle1.Eta (), RecoDecay::constrainAngle (particle1.Phi (), 0 .0F ), weight1);
270+ histos.fill (HIST (" hLambdaMixForLL" ), particle1.Pt (), particle1.Eta (), RecoDecay::constrainAngle (particle1.Phi (), 0 .0F , harmonic ), weight1);
262271 } else if ((tag1 == 0 && tag2 == 1 ) || (tag1 == 1 && tag2 == 0 )) {
263272 histos.fill (HIST (" hSparseLambdaAntiLambdaMixed" ), particle1.M (), particle2.M (), cosThetaDiff, centrality, deltaR, weight2);
264- histos.fill (HIST (" hLambdaMixForLAL" ), particle1.Pt (), particle1.Eta (), RecoDecay::constrainAngle (particle1.Phi (), 0 .0F ), weight2);
273+ histos.fill (HIST (" hLambdaMixForLAL" ), particle1.Pt (), particle1.Eta (), RecoDecay::constrainAngle (particle1.Phi (), 0 .0F , harmonic ), weight2);
265274 } else if (tag1 == 1 && tag2 == 1 ) {
266275 histos.fill (HIST (" hSparseAntiLambdaAntiLambdaMixed" ), particle1.M (), particle2.M (), cosThetaDiff, centrality, deltaR, weight3);
267- histos.fill (HIST (" hAntiLambdaMixForALAL" ), particle1.Pt (), particle1.Eta (), RecoDecay::constrainAngle (particle1.Phi (), 0 .0F ), weight3);
276+ histos.fill (HIST (" hAntiLambdaMixForALAL" ), particle1.Pt (), particle1.Eta (), RecoDecay::constrainAngle (particle1.Phi (), 0 .0F , harmonic ), weight3);
268277 }
269278 }
270279 }
@@ -310,24 +319,24 @@ struct lambdaspincorrderived {
310319 }
311320 proton2 = ROOT::Math::PtEtaPhiMVector (v02.protonPt (), v02.protonEta (), v02.protonPhi (), o2::constants::physics::MassProton);
312321 lambda2 = ROOT::Math::PtEtaPhiMVector (v02.lambdaPt (), v02.lambdaEta (), v02.lambdaPhi (), v02.lambdaMass ());
313- histos.fill (HIST (" deltaPhiSame" ), std::abs (RecoDecay::constrainAngle (v0.lambdaPhi (), 0 .0F ) - RecoDecay::constrainAngle (v02.lambdaPhi (), 0 .0F )));
322+ histos.fill (HIST (" deltaPhiSame" ), std::abs (RecoDecay::constrainAngle (v0.lambdaPhi (), 0 .0F , harmonic ) - RecoDecay::constrainAngle (v02.lambdaPhi (), 0 .0F , harmonic )));
314323 if (std::abs (lambda2.Rapidity ()) > rapidity) {
315324 continue ;
316325 }
317326 if (std::abs (lambda2.Eta ()) > v0eta) {
318327 continue ;
319328 }
320329 if (v0.v0Status () == 0 && v02.v0Status () == 0 ) {
321- fillHistograms (0 , 0 , lambda, lambda2, proton, proton2, centrality, 0 );
330+ fillHistograms (0 , 0 , lambda, lambda2, proton, proton2, centrality, 0 , 1.0 );
322331 }
323332 if (v0.v0Status () == 0 && v02.v0Status () == 1 ) {
324- fillHistograms (0 , 1 , lambda, lambda2, proton, proton2, centrality, 0 );
333+ fillHistograms (0 , 1 , lambda, lambda2, proton, proton2, centrality, 0 , 1.0 );
325334 }
326335 if (v0.v0Status () == 1 && v02.v0Status () == 0 ) {
327- fillHistograms (1 , 0 , lambda2, lambda, proton2, proton, centrality, 0 );
336+ fillHistograms (1 , 0 , lambda2, lambda, proton2, proton, centrality, 0 , 1.0 );
328337 }
329338 if (v0.v0Status () == 1 && v02.v0Status () == 1 ) {
330- fillHistograms (1 , 1 , lambda, lambda2, proton, proton2, centrality, 0 );
339+ fillHistograms (1 , 1 , lambda, lambda2, proton, proton2, centrality, 0 , 1.0 );
331340 }
332341 }
333342 }
@@ -388,7 +397,7 @@ struct lambdaspincorrderived {
388397 lambda = ROOT::Math::PtEtaPhiMVector (t3.lambdaPt (), t3.lambdaEta (), t3.lambdaPhi (), t3.lambdaMass ());
389398 proton2 = ROOT::Math::PtEtaPhiMVector (t2.protonPt (), t2.protonEta (), t2.protonPhi (), o2::constants::physics::MassProton);
390399 lambda2 = ROOT::Math::PtEtaPhiMVector (t2.lambdaPt (), t2.lambdaEta (), t2.lambdaPhi (), t2.lambdaMass ());
391- histos.fill (HIST (" deltaPhiMix" ), std::abs (RecoDecay::constrainAngle (t3.lambdaPhi (), 0 .0F ) - RecoDecay::constrainAngle (t2.lambdaPhi (), 0 .0F )));
400+ histos.fill (HIST (" deltaPhiMix" ), std::abs (RecoDecay::constrainAngle (t3.lambdaPhi (), 0 .0F , harmonic ) - RecoDecay::constrainAngle (t2.lambdaPhi (), 0 .0F , harmonic )));
392401 if (std::abs (lambda.Rapidity ()) > rapidity) {
393402 continue ;
394403 }
@@ -402,22 +411,109 @@ struct lambdaspincorrderived {
402411 continue ;
403412 }
404413 if (t3.v0Status () == 0 && t2.v0Status () == 0 ) {
405- fillHistograms (0 , 0 , lambda, lambda2, proton, proton2, centrality, 1 );
414+ fillHistograms (0 , 0 , lambda, lambda2, proton, proton2, centrality, 1 , 1.0 );
406415 }
407416 if (t3.v0Status () == 0 && t2.v0Status () == 1 ) {
408- fillHistograms (0 , 1 , lambda, lambda2, proton, proton2, centrality, 1 );
417+ fillHistograms (0 , 1 , lambda, lambda2, proton, proton2, centrality, 1 , 1.0 );
409418 }
410419 if (t3.v0Status () == 1 && t2.v0Status () == 0 ) {
411- fillHistograms (1 , 0 , lambda2, lambda, proton2, proton, centrality, 1 );
420+ fillHistograms (1 , 0 , lambda2, lambda, proton2, proton, centrality, 1 , 1.0 );
412421 }
413422 if (t3.v0Status () == 1 && t2.v0Status () == 1 ) {
414- fillHistograms (1 , 1 , lambda, lambda2, proton, proton2, centrality, 1 );
423+ fillHistograms (1 , 1 , lambda, lambda2, proton, proton2, centrality, 1 , 1.0 );
415424 }
416425 }
417426 } // replacement track pair
418427 } // collision pair
419428 }
420429 PROCESS_SWITCH (lambdaspincorrderived, processME, " Process data ME" , false );
430+
431+ void processMEV2 (EventCandidates const & collisions, AllTrackCandidates const & V0s)
432+ {
433+ auto nBins = colBinning.getAllBinsCount ();
434+ std::vector<std::deque<std::pair<int , AllTrackCandidates>>> eventPools (nBins);
435+
436+ for (auto & collision1 : collisions) {
437+ int bin = colBinning.getBin (std::make_tuple (collision1.posz (), collision1.cent ()));
438+ auto poolA = V0s.sliceBy (tracksPerCollisionV0, collision1.index ());
439+ float centrality = collision1.cent ();
440+
441+ // <<< CHANGED: map old collision index → set of (t2.idx, t3.idx) we've already filled
442+ std::unordered_map<int , std::set<std::pair<int , int >>> seenMap;
443+
444+ for (auto & [t1, t2] : soa::combinations (o2::soa::CombinationsFullIndexPolicy (poolA, poolA))) {
445+ if (!selectionV0 (t1) || !selectionV0 (t2))
446+ continue ;
447+ if (t2.index () <= t1.index ())
448+ continue ;
449+ if (t1.protonIndex () == t2.protonIndex ())
450+ continue ;
451+ if (t1.pionIndex () == t2.pionIndex ())
452+ continue ;
453+
454+ int mixes = 0 ;
455+ for (auto it = eventPools[bin].rbegin (); it != eventPools[bin].rend () && mixes < nEvtMixing; ++it, ++mixes) {
456+ int collision2idx = it->first ;
457+ AllTrackCandidates& poolB = it->second ;
458+
459+ int nRepl = 0 ;
460+ for (auto & t3 : poolB) {
461+ if (selectionV0 (t3) && checkKinematics (t1, t3)) {
462+ ++nRepl;
463+ }
464+ }
465+ if (nRepl == 0 )
466+ continue ;
467+ float invN = 1 .0f / static_cast <float >(nRepl);
468+
469+ for (auto & t3 : poolB) {
470+ if (!(selectionV0 (t3) && checkKinematics (t1, t3))) {
471+ continue ;
472+ }
473+ if (collision1.index () == collision2idx) {
474+ continue ;
475+ }
476+
477+ // <<< CHANGED: dedupe (t2, t3) pairs per prior collision
478+ auto key = std::make_pair (t2.index (), t3.index ());
479+ auto & seen = seenMap[collision2idx];
480+ if (!seen.insert (key).second ) {
481+ continue ;
482+ }
483+
484+ // reconstruct 4-vectors
485+ auto proton = ROOT::Math::PtEtaPhiMVector (t3.protonPt (), t3.protonEta (), t3.protonPhi (), o2::constants::physics::MassProton);
486+ auto lambda = ROOT::Math::PtEtaPhiMVector (t3.lambdaPt (), t3.lambdaEta (), t3.lambdaPhi (), t3.lambdaMass ());
487+ auto proton2 = ROOT::Math::PtEtaPhiMVector (t2.protonPt (), t2.protonEta (), t2.protonPhi (), o2::constants::physics::MassProton);
488+ auto lambda2 = ROOT::Math::PtEtaPhiMVector (t2.lambdaPt (), t2.lambdaEta (), t2.lambdaPhi (), t2.lambdaMass ());
489+
490+ float dPhi = std::fabs (RecoDecay::constrainAngle (lambda.Phi (), 0 .0F , harmonic) - RecoDecay::constrainAngle (lambda2.Phi (), 0 .0F , harmonic));
491+ histos.fill (HIST (" deltaPhiMix" ), dPhi, invN);
492+
493+ if (t3.v0Status () == 0 && t2.v0Status () == 0 ) {
494+ fillHistograms (0 , 0 , lambda, lambda2, proton, proton2, centrality, 1 , invN);
495+ }
496+ if (t3.v0Status () == 0 && t2.v0Status () == 1 ) {
497+ fillHistograms (0 , 1 , lambda, lambda2, proton, proton2, centrality, 1 , invN);
498+ }
499+ if (t3.v0Status () == 1 && t2.v0Status () == 0 ) {
500+ fillHistograms (1 , 0 , lambda2, lambda, proton2, proton, centrality, 1 , invN);
501+ }
502+ if (t3.v0Status () == 1 && t2.v0Status () == 1 ) {
503+ fillHistograms (1 , 1 , lambda, lambda2, proton, proton2, centrality, 1 , invN);
504+ }
505+ }
506+ } // end mixing-event loop
507+ } // end same-event pair loop
508+
509+ auto sliced = V0s.sliceBy (tracksPerCollisionV0, collision1.index ());
510+ eventPools[bin].emplace_back (collision1.index (), std::move (sliced));
511+ if (static_cast <int >(eventPools[bin].size ()) > nEvtMixing) {
512+ eventPools[bin].pop_front ();
513+ }
514+ } // end primary-event loop
515+ }
516+ PROCESS_SWITCH (lambdaspincorrderived, processMEV2, " Process data ME" , false );
421517};
422518WorkflowSpec defineDataProcessing (ConfigContext const & cfgc)
423519{
0 commit comments