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
4046#include < vector>
4147
4248// o2 includes.
@@ -49,7 +55,7 @@ using namespace o2::framework::expressions;
4955using namespace o2 ::soa;
5056
5157struct lambdaspincorrderived {
52-
58+ // BinningType colBinning;
5359 struct : ConfigurableGroup {
5460 Configurable<std::string> cfgURL{" cfgURL" , " http://alice-ccdb.cern.ch" , " Address of the CCDB to browse" };
5561 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 +77,7 @@ struct lambdaspincorrderived {
7177 Configurable<float > centMax{" centMax" , 80 , " Maximum Centrality" };
7278
7379 // Lambda selection ////////////
80+ Configurable<unsigned > harmonic{" harmonic" , 1 , " Harmonic delta phi" };
7481 Configurable<bool > useweight{" useweight" , 1 , " Use weight" };
7582 Configurable<bool > usePDGM{" usePDGM" , 1 , " Use PDG mass" };
7683 Configurable<bool > checkDoubleStatus{" checkDoubleStatus" , 0 , " Check Double status" };
@@ -183,7 +190,7 @@ struct lambdaspincorrderived {
183190 if (std::abs (candidate1.lambdaEta () - candidate2.lambdaEta ()) > etaMix) {
184191 return false ;
185192 }
186- if (std::abs (RecoDecay::constrainAngle (candidate1.lambdaPhi (), 0 .0F ) - RecoDecay::constrainAngle (candidate2.lambdaPhi (), 0 .0F )) > phiMix) {
193+ if (std::abs (RecoDecay::constrainAngle (candidate1.lambdaPhi (), 0 .0F , harmonic ) - RecoDecay::constrainAngle (candidate2.lambdaPhi (), 0 .0F , harmonic )) > phiMix) {
187194 return false ;
188195 }
189196 if (std::abs (candidate1.lambdaMass () - candidate2.lambdaMass ()) > massMix) {
@@ -195,7 +202,7 @@ struct lambdaspincorrderived {
195202 void fillHistograms (int tag1, int tag2,
196203 const ROOT::Math::PtEtaPhiMVector& particle1, const ROOT::Math::PtEtaPhiMVector& particle2,
197204 const ROOT::Math::PtEtaPhiMVector& daughpart1, const ROOT::Math::PtEtaPhiMVector& daughpart2,
198- double centrality, int datatype)
205+ double centrality, int datatype, float mixpairweight )
199206 {
200207
201208 auto lambda1Mass = 0.0 ;
@@ -230,41 +237,42 @@ struct lambdaspincorrderived {
230237
231238 auto cosThetaDiff = -999.0 ;
232239 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 ));
240+ double deltaPhi = std::abs (RecoDecay::constrainAngle (particle1Dummy.Phi (), 0 .0F , harmonic ) - RecoDecay::constrainAngle (particle2Dummy.Phi (), 0 .0F , harmonic ));
234241 double deltaEta = particle1Dummy.Eta () - particle2Dummy.Eta ();
235242 double deltaR = TMath::Sqrt (deltaEta * deltaEta + deltaPhi * deltaPhi);
236243
237244 if (datatype == 0 ) {
238- histos.fill (HIST (" hPtYSame" ), particle1.Pt (), particle1.Rapidity ());
245+ mixpairweight = 1.0 ;
246+ histos.fill (HIST (" hPtYSame" ), particle1.Pt (), particle1.Rapidity (), mixpairweight);
239247 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 ) );
248+ histos.fill (HIST (" hSparseLambdaLambda" ), particle1.M (), particle2.M (), cosThetaDiff, centrality, deltaR, mixpairweight );
249+ histos.fill (HIST (" hLambdaSameForLL" ), particle1.Pt (), particle1.Eta (), RecoDecay::constrainAngle (particle1.Phi (), 0 .0F , harmonic), mixpairweight );
242250 } 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 ) );
251+ histos.fill (HIST (" hSparseLambdaAntiLambda" ), particle1.M (), particle2.M (), cosThetaDiff, centrality, deltaR, mixpairweight );
252+ histos.fill (HIST (" hLambdaSameForLAL" ), particle1.Pt (), particle1.Eta (), RecoDecay::constrainAngle (particle1.Phi (), 0 .0F , harmonic), mixpairweight );
245253 } 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 ) );
254+ histos.fill (HIST (" hSparseAntiLambdaAntiLambda" ), particle1.M (), particle2.M (), cosThetaDiff, centrality, deltaR, mixpairweight );
255+ histos.fill (HIST (" hAntiLambdaSameForALAL" ), particle1.Pt (), particle1.Eta (), RecoDecay::constrainAngle (particle1.Phi (), 0 .0F , harmonic), mixpairweight );
248256 }
249257 } else if (datatype == 1 ) {
250- double weight1 = 1.0 ;
251- double weight2 = 1.0 ;
252- double weight3 = 1.0 ;
258+ double weight1 = mixpairweight ;
259+ double weight2 = mixpairweight ;
260+ double weight3 = mixpairweight ;
253261 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 ()));
262+ weight1 = mixpairweight * hweight1->GetBinContent (hweight1->FindBin (particle1.Pt (), particle1.Eta (), particle1.Phi ()));
263+ weight2 = mixpairweight * hweight2->GetBinContent (hweight2->FindBin (particle1.Pt (), particle1.Eta (), particle1.Phi ()));
264+ weight3 = mixpairweight * hweight3->GetBinContent (hweight3->FindBin (particle1.Pt (), particle1.Eta (), particle1.Phi ()));
257265 }
258266 histos.fill (HIST (" hPtYMix" ), particle1.Pt (), particle1.Rapidity ());
259267 if (tag1 == 0 && tag2 == 0 ) {
260268 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);
269+ histos.fill (HIST (" hLambdaMixForLL" ), particle1.Pt (), particle1.Eta (), RecoDecay::constrainAngle (particle1.Phi (), 0 .0F , harmonic ), weight1);
262270 } else if ((tag1 == 0 && tag2 == 1 ) || (tag1 == 1 && tag2 == 0 )) {
263271 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);
272+ histos.fill (HIST (" hLambdaMixForLAL" ), particle1.Pt (), particle1.Eta (), RecoDecay::constrainAngle (particle1.Phi (), 0 .0F , harmonic ), weight2);
265273 } else if (tag1 == 1 && tag2 == 1 ) {
266274 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);
275+ histos.fill (HIST (" hAntiLambdaMixForALAL" ), particle1.Pt (), particle1.Eta (), RecoDecay::constrainAngle (particle1.Phi (), 0 .0F , harmonic ), weight3);
268276 }
269277 }
270278 }
@@ -310,24 +318,24 @@ struct lambdaspincorrderived {
310318 }
311319 proton2 = ROOT::Math::PtEtaPhiMVector (v02.protonPt (), v02.protonEta (), v02.protonPhi (), o2::constants::physics::MassProton);
312320 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 )));
321+ histos.fill (HIST (" deltaPhiSame" ), std::abs (RecoDecay::constrainAngle (v0.lambdaPhi (), 0 .0F , harmonic ) - RecoDecay::constrainAngle (v02.lambdaPhi (), 0 .0F , harmonic )));
314322 if (std::abs (lambda2.Rapidity ()) > rapidity) {
315323 continue ;
316324 }
317325 if (std::abs (lambda2.Eta ()) > v0eta) {
318326 continue ;
319327 }
320328 if (v0.v0Status () == 0 && v02.v0Status () == 0 ) {
321- fillHistograms (0 , 0 , lambda, lambda2, proton, proton2, centrality, 0 );
329+ fillHistograms (0 , 0 , lambda, lambda2, proton, proton2, centrality, 0 , 1.0 );
322330 }
323331 if (v0.v0Status () == 0 && v02.v0Status () == 1 ) {
324- fillHistograms (0 , 1 , lambda, lambda2, proton, proton2, centrality, 0 );
332+ fillHistograms (0 , 1 , lambda, lambda2, proton, proton2, centrality, 0 , 1.0 );
325333 }
326334 if (v0.v0Status () == 1 && v02.v0Status () == 0 ) {
327- fillHistograms (1 , 0 , lambda2, lambda, proton2, proton, centrality, 0 );
335+ fillHistograms (1 , 0 , lambda2, lambda, proton2, proton, centrality, 0 , 1.0 );
328336 }
329337 if (v0.v0Status () == 1 && v02.v0Status () == 1 ) {
330- fillHistograms (1 , 1 , lambda, lambda2, proton, proton2, centrality, 0 );
338+ fillHistograms (1 , 1 , lambda, lambda2, proton, proton2, centrality, 0 , 1.0 );
331339 }
332340 }
333341 }
@@ -388,7 +396,7 @@ struct lambdaspincorrderived {
388396 lambda = ROOT::Math::PtEtaPhiMVector (t3.lambdaPt (), t3.lambdaEta (), t3.lambdaPhi (), t3.lambdaMass ());
389397 proton2 = ROOT::Math::PtEtaPhiMVector (t2.protonPt (), t2.protonEta (), t2.protonPhi (), o2::constants::physics::MassProton);
390398 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 )));
399+ histos.fill (HIST (" deltaPhiMix" ), std::abs (RecoDecay::constrainAngle (t3.lambdaPhi (), 0 .0F , harmonic ) - RecoDecay::constrainAngle (t2.lambdaPhi (), 0 .0F , harmonic )));
392400 if (std::abs (lambda.Rapidity ()) > rapidity) {
393401 continue ;
394402 }
@@ -402,22 +410,109 @@ struct lambdaspincorrderived {
402410 continue ;
403411 }
404412 if (t3.v0Status () == 0 && t2.v0Status () == 0 ) {
405- fillHistograms (0 , 0 , lambda, lambda2, proton, proton2, centrality, 1 );
413+ fillHistograms (0 , 0 , lambda, lambda2, proton, proton2, centrality, 1 , 1.0 );
406414 }
407415 if (t3.v0Status () == 0 && t2.v0Status () == 1 ) {
408- fillHistograms (0 , 1 , lambda, lambda2, proton, proton2, centrality, 1 );
416+ fillHistograms (0 , 1 , lambda, lambda2, proton, proton2, centrality, 1 , 1.0 );
409417 }
410418 if (t3.v0Status () == 1 && t2.v0Status () == 0 ) {
411- fillHistograms (1 , 0 , lambda2, lambda, proton2, proton, centrality, 1 );
419+ fillHistograms (1 , 0 , lambda2, lambda, proton2, proton, centrality, 1 , 1.0 );
412420 }
413421 if (t3.v0Status () == 1 && t2.v0Status () == 1 ) {
414- fillHistograms (1 , 1 , lambda, lambda2, proton, proton2, centrality, 1 );
422+ fillHistograms (1 , 1 , lambda, lambda2, proton, proton2, centrality, 1 , 1.0 );
415423 }
416424 }
417425 } // replacement track pair
418426 } // collision pair
419427 }
420428 PROCESS_SWITCH (lambdaspincorrderived, processME, " Process data ME" , false );
429+
430+ void processMEV2 (EventCandidates const & collisions, AllTrackCandidates const & V0s)
431+ {
432+ auto nBins = colBinning.getAllBinsCount ();
433+ std::vector<std::deque<std::pair<int , AllTrackCandidates>>> eventPools (nBins);
434+
435+ for (auto & collision1 : collisions) {
436+ int bin = colBinning.getBin (std::make_tuple (collision1.posz (), collision1.cent ()));
437+ auto poolA = V0s.sliceBy (tracksPerCollisionV0, collision1.index ());
438+ float centrality = collision1.cent ();
439+
440+ // <<< CHANGED: map old collision index → set of (t2.idx, t3.idx) we've already filled
441+ std::unordered_map<int , std::set<std::pair<int , int >>> seenMap;
442+
443+ for (auto & [t1, t2] : soa::combinations (o2::soa::CombinationsFullIndexPolicy (poolA, poolA))) {
444+ if (!selectionV0 (t1) || !selectionV0 (t2))
445+ continue ;
446+ if (t2.index () <= t1.index ())
447+ continue ;
448+ if (t1.protonIndex () == t2.protonIndex ())
449+ continue ;
450+ if (t1.pionIndex () == t2.pionIndex ())
451+ continue ;
452+
453+ int mixes = 0 ;
454+ for (auto it = eventPools[bin].rbegin (); it != eventPools[bin].rend () && mixes < nEvtMixing; ++it, ++mixes) {
455+ int collision2idx = it->first ;
456+ AllTrackCandidates& poolB = it->second ;
457+
458+ int nRepl = 0 ;
459+ for (auto & t3 : poolB) {
460+ if (selectionV0 (t3) && checkKinematics (t1, t3)) {
461+ ++nRepl;
462+ }
463+ }
464+ if (nRepl == 0 )
465+ continue ;
466+ float invN = 1 .0f / float (nRepl);
467+
468+ for (auto & t3 : poolB) {
469+ if (!(selectionV0 (t3) && checkKinematics (t1, t3))) {
470+ continue ;
471+ }
472+ if (collision1.index () == collision2idx) {
473+ continue ;
474+ }
475+
476+ // <<< CHANGED: dedupe (t2, t3) pairs per prior collision
477+ auto key = std::make_pair (t2.index (), t3.index ());
478+ auto & seen = seenMap[collision2idx];
479+ if (!seen.insert (key).second ) {
480+ continue ;
481+ }
482+
483+ // reconstruct 4-vectors
484+ auto proton = ROOT::Math::PtEtaPhiMVector (t3.protonPt (), t3.protonEta (), t3.protonPhi (), o2::constants::physics::MassProton);
485+ auto lambda = ROOT::Math::PtEtaPhiMVector (t3.lambdaPt (), t3.lambdaEta (), t3.lambdaPhi (), t3.lambdaMass ());
486+ auto proton2 = ROOT::Math::PtEtaPhiMVector (t2.protonPt (), t2.protonEta (), t2.protonPhi (), o2::constants::physics::MassProton);
487+ auto lambda2 = ROOT::Math::PtEtaPhiMVector (t2.lambdaPt (), t2.lambdaEta (), t2.lambdaPhi (), t2.lambdaMass ());
488+
489+ float dPhi = std::fabs (RecoDecay::constrainAngle (lambda.Phi (), 0 .0F , harmonic) - RecoDecay::constrainAngle (lambda2.Phi (), 0 .0F , harmonic));
490+ histos.fill (HIST (" deltaPhiMix" ), dPhi, invN);
491+
492+ if (t3.v0Status () == 0 && t2.v0Status () == 0 ) {
493+ fillHistograms (0 , 0 , lambda, lambda2, proton, proton2, centrality, 1 , invN);
494+ }
495+ if (t3.v0Status () == 0 && t2.v0Status () == 1 ) {
496+ fillHistograms (0 , 1 , lambda, lambda2, proton, proton2, centrality, 1 , invN);
497+ }
498+ if (t3.v0Status () == 1 && t2.v0Status () == 0 ) {
499+ fillHistograms (1 , 0 , lambda2, lambda, proton2, proton, centrality, 1 , invN);
500+ }
501+ if (t3.v0Status () == 1 && t2.v0Status () == 1 ) {
502+ fillHistograms (1 , 1 , lambda, lambda2, proton, proton2, centrality, 1 , invN);
503+ }
504+ }
505+ } // end mixing-event loop
506+ } // end same-event pair loop
507+
508+ auto sliced = V0s.sliceBy (tracksPerCollisionV0, collision1.index ());
509+ eventPools[bin].emplace_back (collision1.index (), std::move (sliced));
510+ if ((int )eventPools[bin].size () > nEvtMixing) {
511+ eventPools[bin].pop_front ();
512+ }
513+ } // end primary-event loop
514+ }
515+ PROCESS_SWITCH (lambdaspincorrderived, processMEV2, " Process data ME" , false );
421516};
422517WorkflowSpec defineDataProcessing (ConfigContext const & cfgc)
423518{
0 commit comments