From e9be0625484caedacc8abafb6d6a704a9fdd9f5f Mon Sep 17 00:00:00 2001 From: suyoupeng <109774812+15071832337@users.noreply.github.com> Date: Sun, 26 Jul 2026 21:02:50 +0800 Subject: [PATCH 1/5] Add files via upload From 2babda474d75637ee14de02f08e6fbad6c51f262 Mon Sep 17 00:00:00 2001 From: suyoupeng <109774812+15071832337@users.noreply.github.com> Date: Sun, 26 Jul 2026 21:04:29 +0800 Subject: [PATCH 2/5] Add files via upload --- .../Strangeness/lambdaspincorrelation.cxx | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/PWGLF/TableProducer/Strangeness/lambdaspincorrelation.cxx b/PWGLF/TableProducer/Strangeness/lambdaspincorrelation.cxx index b557053295c..fcb3bf21aff 100644 --- a/PWGLF/TableProducer/Strangeness/lambdaspincorrelation.cxx +++ b/PWGLF/TableProducer/Strangeness/lambdaspincorrelation.cxx @@ -298,6 +298,7 @@ struct lambdaspincorrelation { std::vector positiveIndex = {}; std::vector negativeIndex = {}; std::vector dcaBetweenDaughter = {}; + std::vector dcaV0ToPV = {}; int numbV0 = 0; // LOGF(info, "event collisions: (%d)", collision.index()); auto centrality = collision.centFT0C(); @@ -355,6 +356,7 @@ struct lambdaspincorrelation { positiveIndex.push_back(postrack1.globalIndex()); negativeIndex.push_back(negtrack1.globalIndex()); v0Cospa.push_back(v0.v0cosPA()); + dcaV0ToPV.push_back(std::abs(v0.dcav0topv())); v0Radius.push_back(v0.v0radius()); dcaPositive.push_back(std::abs(v0.dcapostopv())); dcaNegative.push_back(std::abs(v0.dcanegtopv())); @@ -391,7 +393,7 @@ struct lambdaspincorrelation { lambdaDummy = lambdaMother.at(i5); protonDummy = protonDaughter.at(i5); pionDummy = pionDaughter.at(i5); - lambdaPair(indexEvent, v0Status.at(i5), doubleStatus.at(i5), v0Cospa.at(i5), v0Radius.at(i5), dcaPositive.at(i5), dcaNegative.at(i5), dcaBetweenDaughter.at(i5), lambdaDummy.Pt(), lambdaDummy.Eta(), lambdaDummy.Phi(), lambdaDummy.M(), protonDummy.Pt(), protonDummy.Eta(), protonDummy.Phi(), positiveIndex.at(i5), negativeIndex.at(i5)); + lambdaPair(indexEvent, v0Status.at(i5), doubleStatus.at(i5), v0Cospa.at(i5), v0Radius.at(i5), dcaPositive.at(i5), dcaNegative.at(i5), dcaBetweenDaughter.at(i5), lambdaDummy.Pt(), lambdaDummy.Eta(), lambdaDummy.Phi(), lambdaDummy.M(), protonDummy.Pt(), protonDummy.Eta(), protonDummy.Phi(), positiveIndex.at(i5), negativeIndex.at(i5), dcaV0ToPV.at(i5)); } } } @@ -410,6 +412,7 @@ struct lambdaspincorrelation { std::vector positiveIndex = {}; std::vector negativeIndex = {}; std::vector dcaBetweenDaughter = {}; + std::vector dcaV0ToPV = {}; int numbV0 = 0; // LOGF(info, "event collisions: (%d)", collision.index()); auto centrality = collision.centFT0C(); @@ -470,6 +473,7 @@ struct lambdaspincorrelation { dcaPositive.push_back(std::abs(v0.dcapostopv())); dcaNegative.push_back(std::abs(v0.dcanegtopv())); dcaBetweenDaughter.push_back(std::abs(v0.dcaV0daughters())); + dcaV0ToPV.push_back(std::abs(v0.dcav0topv())); if (lambdaTag) { v0Status.push_back(0); proton = ROOT::Math::PxPyPzMVector(v0.pxpos(), v0.pypos(), v0.pzpos(), o2::constants::physics::MassProton); @@ -502,7 +506,7 @@ struct lambdaspincorrelation { lambdaDummy = lambdaMother.at(i5); protonDummy = protonDaughter.at(i5); pionDummy = pionDaughter.at(i5); - lambdaPairmc(indexEvent, v0Status.at(i5), doubleStatus.at(i5), v0Cospa.at(i5), v0Radius.at(i5), dcaPositive.at(i5), dcaNegative.at(i5), dcaBetweenDaughter.at(i5), lambdaDummy.Pt(), lambdaDummy.Eta(), lambdaDummy.Phi(), lambdaDummy.M(), protonDummy.Pt(), protonDummy.Eta(), protonDummy.Phi(), positiveIndex.at(i5), negativeIndex.at(i5)); + lambdaPairmc(indexEvent, v0Status.at(i5), doubleStatus.at(i5), v0Cospa.at(i5), v0Radius.at(i5), dcaPositive.at(i5), dcaNegative.at(i5), dcaBetweenDaughter.at(i5), lambdaDummy.Pt(), lambdaDummy.Eta(), lambdaDummy.Phi(), lambdaDummy.M(), protonDummy.Pt(), protonDummy.Eta(), protonDummy.Phi(), positiveIndex.at(i5), negativeIndex.at(i5), dcaV0ToPV.at(i5)); } } } @@ -530,6 +534,7 @@ struct lambdaspincorrelation { std::vector positiveIndex = {}; std::vector negativeIndex = {}; std::vector dcaBetweenDaughter = {}; + std::vector dcaV0ToPV = {}; int numbV0 = 0; auto centrality = collision.centFT0C(); @@ -610,6 +615,7 @@ struct lambdaspincorrelation { negativeIndex.push_back(negtrack1.globalIndex()); v0Cospa.push_back(v0.v0cosPA()); + dcaV0ToPV.push_back(std::abs(v0.dcav0topv())); v0Radius.push_back(v0.v0radius()); dcaPositive.push_back(std::abs(v0.dcapostopv())); dcaNegative.push_back(std::abs(v0.dcanegtopv())); @@ -695,7 +701,8 @@ struct lambdaspincorrelation { protonDummy.Eta(), protonDummy.Phi(), positiveIndex.at(i5), - negativeIndex.at(i5)); + negativeIndex.at(i5), + dcaV0ToPV.at(i5)); } } } From 6ee37d1d7242a5f990254d477c5b3bee197bcdbc Mon Sep 17 00:00:00 2001 From: suyoupeng <109774812+15071832337@users.noreply.github.com> Date: Sun, 26 Jul 2026 21:06:00 +0800 Subject: [PATCH 3/5] Add files via upload --- PWGLF/Tasks/Strangeness/lambdaspincorrderived.cxx | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/PWGLF/Tasks/Strangeness/lambdaspincorrderived.cxx b/PWGLF/Tasks/Strangeness/lambdaspincorrderived.cxx index 76b9d788c63..3fccb5235f3 100644 --- a/PWGLF/Tasks/Strangeness/lambdaspincorrderived.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaspincorrderived.cxx @@ -170,6 +170,13 @@ static inline int piIdx(const T& t) { return t.pionIndexmc(); } + +template +static inline int dcaV0ToPV(const T& t) +{ + return t.dcaV0ToPVmc(); +} + } // namespace mcacc // Optional fixed-leg correction pointers kept outside the task struct. @@ -273,6 +280,7 @@ struct lambdaspincorrderived { Configurable rapidity{"rapidity", 0.5, "Rapidity cut on lambda"}; Configurable v0etaMixBuffer{"v0etaMixBuffer", 0.8, "Eta cut on mix event buffer"}; Configurable v0eta{"v0eta", 0.8, "Eta cut on lambda"}; + Configurable dcaV0ToPV{"dcaV0ToPV", 1.2, "DCA V0 to PV cut on lambda"}; // Event Mixing Configurable cosDef{"cosDef", 1, "Defination of cos"}; @@ -586,6 +594,9 @@ struct lambdaspincorrderived { if (candidate.dcaBetweenDaughter() > dcaDaughters) { return false; } + if (candidate.dcaV0ToPV() > dcaV0ToPV) { + return false; + } if (candidate.v0Status() == 0 && (std::abs(candidate.dcaPositive()) < dcaProton || std::abs(candidate.dcaNegative()) < dcaPion)) { return false; } @@ -1647,6 +1658,9 @@ struct lambdaspincorrderived { if (mcacc::dcaDau(candidate) > dcaDaughters) { return false; } + if (mcacc::dcaV0ToPV(candidate) > dcaV0ToPV) { + return false; + } if (mcacc::v0Status(candidate) == 0 && (std::abs(mcacc::dcaPos(candidate)) < dcaProton || std::abs(mcacc::dcaNeg(candidate)) < dcaPion)) { return false; } From f535a74ffe6a2c9d3c177b6a46daaec1d3c34264 Mon Sep 17 00:00:00 2001 From: suyoupeng <109774812+15071832337@users.noreply.github.com> Date: Sun, 26 Jul 2026 21:17:52 +0800 Subject: [PATCH 4/5] Add files via upload --- PWGLF/DataModel/LFSpincorrelationTables.h | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/PWGLF/DataModel/LFSpincorrelationTables.h b/PWGLF/DataModel/LFSpincorrelationTables.h index 571a3413549..d4fdffd2442 100644 --- a/PWGLF/DataModel/LFSpincorrelationTables.h +++ b/PWGLF/DataModel/LFSpincorrelationTables.h @@ -52,6 +52,8 @@ DECLARE_SOA_COLUMN(ProtonEta, protonEta, float); //! Proton Et DECLARE_SOA_COLUMN(ProtonPhi, protonPhi, float); //! Proton Phi DECLARE_SOA_COLUMN(ProtonIndex, protonIndex, int); //! Proton index DECLARE_SOA_COLUMN(PionIndex, pionIndex, int); //! Pion index +DECLARE_SOA_COLUMN(DcaV0ToPV, dcaV0ToPV, float); //! DCA of V0 to primary vertex + } // namespace lambdapair DECLARE_SOA_TABLE(LambdaPairs, "AOD", "LAMBDAPAIR", o2::soa::Index<>, @@ -71,7 +73,8 @@ DECLARE_SOA_TABLE(LambdaPairs, "AOD", "LAMBDAPAIR", lambdapair::ProtonEta, lambdapair::ProtonPhi, lambdapair::ProtonIndex, - lambdapair::PionIndex); + lambdapair::PionIndex, + lambdapair::DcaV0ToPV); using LambdaPair = LambdaPairs::iterator; @@ -105,6 +108,7 @@ DECLARE_SOA_COLUMN(ProtonEtamc, protonEtamc, float); //! Proto DECLARE_SOA_COLUMN(ProtonPhimc, protonPhimc, float); //! Proton Phi in montecarlo DECLARE_SOA_COLUMN(ProtonIndexmc, protonIndexmc, int); //! Proton index in montecarlo DECLARE_SOA_COLUMN(PionIndexmc, pionIndexmc, int); //! Pion index in montecarlo +DECLARE_SOA_COLUMN(DcaV0ToPVmc, dcaV0ToPVmc, float); //! DCA of V0 to primary vertex } // namespace lambdapairmc DECLARE_SOA_TABLE(LambdaPairmcs, "AOD", "LAMBDAPAIRMC", o2::soa::Index<>, @@ -124,7 +128,8 @@ DECLARE_SOA_TABLE(LambdaPairmcs, "AOD", "LAMBDAPAIRMC", lambdapairmc::ProtonEtamc, lambdapairmc::ProtonPhimc, lambdapairmc::ProtonIndexmc, - lambdapairmc::PionIndexmc); + lambdapairmc::PionIndexmc, + lambdapairmc::DcaV0ToPVmc); using LambdaPairmc = LambdaPairmcs::iterator; From 48b94ec87a8c733f381059aa3d8e9ff880fa99b5 Mon Sep 17 00:00:00 2001 From: suyoupeng <109774812+15071832337@users.noreply.github.com> Date: Sun, 26 Jul 2026 23:19:17 +0800 Subject: [PATCH 5/5] Add files via upload --- .../Strangeness/lambdaspincorrderived.cxx | 421 ++++++++++++++++-- 1 file changed, 393 insertions(+), 28 deletions(-) diff --git a/PWGLF/Tasks/Strangeness/lambdaspincorrderived.cxx b/PWGLF/Tasks/Strangeness/lambdaspincorrderived.cxx index 3fccb5235f3..d923663c5ce 100644 --- a/PWGLF/Tasks/Strangeness/lambdaspincorrderived.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaspincorrderived.cxx @@ -47,8 +47,6 @@ #include #include #include -#include -#include #include #include #include @@ -170,13 +168,6 @@ static inline int piIdx(const T& t) { return t.pionIndexmc(); } - -template -static inline int dcaV0ToPV(const T& t) -{ - return t.dcaV0ToPVmc(); -} - } // namespace mcacc // Optional fixed-leg correction pointers kept outside the task struct. @@ -228,19 +219,24 @@ struct lambdaspincorrderived { Configurable useNUA{"useNUA", false, "Apply single-candidate NUA weight in (phi,eta)"}; Configurable ConfNUAPathLambda{"ConfNUAPathLambda", "", "CCDB path for Lambda NUA TH2D(phi,eta)"}; Configurable ConfNUAPathAntiLambda{"ConfNUAPathAntiLambda", "", "CCDB path for AntiLambda NUA TH2D(phi,eta)"}; + + // Mixing selection and binning Configurable> massMixEdges{"massMixEdges", {1.09f, 1.108f, 1.122f, 1.14f}, "Mass-mixing region edges: [SB low | signal | SB high]"}; Configurable cfgMixLegMode{"cfgMixLegMode", 0, "0=replace leg-1 only, 1=replace leg-2 only, 2=do both one-leg replacements"}; Configurable cfgV5MassBins{"cfgV5MassBins", 5, "Number of fixed mass bins for V5 mixing"}; Configurable cfgV5NeighborPt{"cfgV5NeighborPt", 0, "v5: neighbor bins in pT (use symmetric ±N, edge-safe)"}; Configurable cfgV5NeighborEta{"cfgV5NeighborEta", 0, "v5: neighbor bins in eta (use symmetric ±N, edge-safe)"}; Configurable cfgV5NeighborPhi{"cfgV5NeighborPhi", 0, "v5: neighbor bins in phi (use symmetric ±N, periodic wrap)"}; - Configurable usePairKineMatch{"usePairKineMatch", true, "Require pair-level matching between (A,B) and (C,B)"}; Configurable cfgV5MaxMatches{"cfgV5MaxMatches", 50, "v5: max ME replacements per SE pair (after all cuts)"}; Configurable cfgMixSeed{"cfgMixSeed", 0xdecafbadULL, "RNG seed for downsampling matches (deterministic)"}; + Configurable cfgV6CarryUnmatched{"cfgV6CarryUnmatched", false, "Carry data and MC V6 replacement branches with zero matches to later data frames"}; + Configurable cfgV6MaxPendingBranches{"cfgV6MaxPendingBranches", 0, "Maximum retained V6 branches per data or MC buffer; <=0 means unlimited"}; + Configurable cfgV6MaxPendingAge{"cfgV6MaxPendingAge", 0, "Maximum later data frames searched by a retained V6 branch; <=0 means unlimited"}; + Configurable cfgV6LogPending{"cfgV6LogPending", false, "Print one V6 pending-branch summary per data frame"}; + + // Event and candidate mixing cuts Configurable centMin{"centMin", 0, "Minimum Centrality"}; Configurable centMax{"centMax", 80, "Maximum Centrality"}; - Configurable rngSeed{"rngSeed", 12345, "Seed for random mixing (reproducible)"}; - std::mt19937 rng{12345}; Configurable nEvtMixing{"nEvtMixing", 10, "Number of events to mix"}; ConfigurableAxis CfgVtxBins{"CfgVtxBins", {VARIABLE_WIDTH, -10, -8, -6, -4, -2, 0, 2, 4, 6, 8, 10}, "Mixing bins - z-vertex"}; ConfigurableAxis CfgMultBins{"CfgMultBins", {VARIABLE_WIDTH, 0, 110}, "Mixing bins - centrality"}; @@ -280,7 +276,6 @@ struct lambdaspincorrderived { Configurable rapidity{"rapidity", 0.5, "Rapidity cut on lambda"}; Configurable v0etaMixBuffer{"v0etaMixBuffer", 0.8, "Eta cut on mix event buffer"}; Configurable v0eta{"v0eta", 0.8, "Eta cut on lambda"}; - Configurable dcaV0ToPV{"dcaV0ToPV", 1.2, "DCA V0 to PV cut on lambda"}; // Event Mixing Configurable cosDef{"cosDef", 1, "Defination of cos"}; @@ -486,7 +481,6 @@ struct lambdaspincorrderived { histos.add("hSparsePairMassAntiLambdaLambdaMixed", "hSparsePairMassAntiLambdaLambdaMixed", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisPairMass}, true); histos.add("hSparsePairMassAntiLambdaAntiLambdaMixed", "hSparsePairMassAntiLambdaAntiLambdaMixed", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisPairMass}, true); } - rng.seed(static_cast(rngSeed.value)); ccdb->setURL(cfgCcdbParam.cfgURL); ccdbApi.init("http://alice-ccdb.cern.ch"); ccdb->setCaching(true); @@ -594,9 +588,6 @@ struct lambdaspincorrderived { if (candidate.dcaBetweenDaughter() > dcaDaughters) { return false; } - if (candidate.dcaV0ToPV() > dcaV0ToPV) { - return false; - } if (candidate.v0Status() == 0 && (std::abs(candidate.dcaPositive()) < dcaProton || std::abs(candidate.dcaNegative()) < dcaPion)) { return false; } @@ -1598,8 +1589,6 @@ struct lambdaspincorrderived { inline int ptBin(float pt) const { return binFromValue(pt, ptMin, ptStep, nPt_); } inline int etaBin(float eta) const { return binFromValue(eta, etaMin, etaStep, nEta_); } inline int phiBin(float phi) const { return binFromValue(phi, phiMin, phiStep, nPhi_); } - inline int massBin(float m) const { return binFromValue(m, mMin, mStep, nM_); } - inline int radiusBin(float r) const { if (!std::isfinite(r) || nR_ <= 0) { @@ -1620,6 +1609,210 @@ struct lambdaspincorrderived { uint16_t ptBin, etaBin, phiBin, mBin, rBin; }; + struct StoredV6Candidate { + int64_t collisionIdx = -1; + int64_t globalIdx = -1; + int status = -1; + bool isDouble = false; + float cospa = 0.f; + float radius = 0.f; + float dcaPos = 0.f; + float dcaNeg = 0.f; + float dcaDau = 0.f; + float lPt = 0.f; + float lEta = 0.f; + float lPhi = 0.f; + float lMass = 0.f; + float pPt = 0.f; + float pEta = 0.f; + float pPhi = 0.f; + int64_t pIndex = -1; + int64_t piIndex = -1; + + int v0Status() const { return status; } + bool doubleStatus() const { return isDouble; } + float v0Cospa() const { return cospa; } + float v0Radius() const { return radius; } + float dcaPositive() const { return dcaPos; } + float dcaNegative() const { return dcaNeg; } + float dcaBetweenDaughter() const { return dcaDau; } + float lambdaPt() const { return lPt; } + float lambdaEta() const { return lEta; } + float lambdaPhi() const { return lPhi; } + float lambdaMass() const { return lMass; } + float protonPt() const { return pPt; } + float protonEta() const { return pEta; } + float protonPhi() const { return pPhi; } + int64_t protonIndex() const { return pIndex; } + int64_t pionIndex() const { return piIndex; } + int64_t globalIndex() const { return globalIdx; } + }; + + struct PendingV6Branch { + StoredV6Candidate target; + StoredV6Candidate fixed; + int colBin = -1; + int replacedLeg = 1; + int age = 0; + uint64_t seed = 0; + }; + + template + StoredV6Candidate storeV6Candidate(T const& t, int64_t collisionIdx) const + { + return {collisionIdx, static_cast(t.globalIndex()), static_cast(t.v0Status()), static_cast(t.doubleStatus()), + t.v0Cospa(), t.v0Radius(), t.dcaPositive(), t.dcaNegative(), t.dcaBetweenDaughter(), + t.lambdaPt(), t.lambdaEta(), t.lambdaPhi(), t.lambdaMass(), + t.protonPt(), t.protonEta(), t.protonPhi(), + static_cast(t.protonIndex()), static_cast(t.pionIndex())}; + } + + template + void fillV6MixedBranch(TRep const& replacement, TFixed const& fixed, int replacedLeg, float controlWeight, float mixWeight) + { + const auto repProton = ROOT::Math::PtEtaPhiMVector(replacement.protonPt(), replacement.protonEta(), replacement.protonPhi(), o2::constants::physics::MassProton); + const auto repLambda = ROOT::Math::PtEtaPhiMVector(replacement.lambdaPt(), replacement.lambdaEta(), replacement.lambdaPhi(), replacement.lambdaMass()); + const auto fixedProton = ROOT::Math::PtEtaPhiMVector(fixed.protonPt(), fixed.protonEta(), fixed.protonPhi(), o2::constants::physics::MassProton); + const auto fixedLambda = ROOT::Math::PtEtaPhiMVector(fixed.lambdaPt(), fixed.lambdaEta(), fixed.lambdaPhi(), fixed.lambdaMass()); + const int repStatus = replacement.v0Status(); + const int fixedStatus = fixed.v0Status(); + + if (replacedLeg == 1) { + fillReplacementControlMap(repStatus, fixedStatus, 1, false, repLambda, controlWeight); + fillFixedLegControlMap(repStatus, fixedStatus, 1, false, fixedLambda, controlWeight); + if ((repStatus == 0 && fixedStatus == 1) || (repStatus == 1 && fixedStatus == 0)) { + if (fillBasicQAHistos) { + histos.fill(HIST("deltaPhiMix"), deltaPhiMinusPiToPi((float)repLambda.Phi(), (float)fixedLambda.Phi()), mixWeight); + } + } + if (repStatus == 0 && fixedStatus == 1) { + fillHistograms(0, 1, repLambda, fixedLambda, repProton, fixedProton, 1, mixWeight, 1, 1); + } else if (repStatus == 1 && fixedStatus == 0) { + fillHistograms(0, 1, fixedLambda, repLambda, fixedProton, repProton, 1, mixWeight, 2, 1); + } else { + fillHistograms(repStatus, fixedStatus, repLambda, fixedLambda, repProton, fixedProton, 1, mixWeight, 1, 1); + } + return; + } + + fillReplacementControlMap(fixedStatus, repStatus, 2, false, repLambda, controlWeight); + fillFixedLegControlMap(fixedStatus, repStatus, 2, false, fixedLambda, controlWeight); + if (fillBasicQAHistos) { + histos.fill(HIST("deltaPhiMix"), deltaPhiMinusPiToPi((float)fixedLambda.Phi(), (float)repLambda.Phi()), mixWeight); + } + if (fixedStatus == 0 && repStatus == 1) { + fillHistograms(0, 1, fixedLambda, repLambda, fixedProton, repProton, 1, mixWeight, 2, 2); + } else if (fixedStatus == 1 && repStatus == 0) { + fillHistograms(0, 1, repLambda, fixedLambda, repProton, fixedProton, 1, mixWeight, 1, 2); + } else { + fillHistograms(fixedStatus, repStatus, fixedLambda, repLambda, fixedProton, repProton, 1, mixWeight, 2, 2); + } + } + + struct StoredV6CandidateMC { + int64_t globalIdx = -1; + int status = -1; + bool isDouble = false; + float cospa = 0.f; + float radius = 0.f; + float dcaPos = 0.f; + float dcaNeg = 0.f; + float dcaDau = 0.f; + float lPt = 0.f; + float lEta = 0.f; + float lPhi = 0.f; + float lMass = 0.f; + float pPt = 0.f; + float pEta = 0.f; + float pPhi = 0.f; + int64_t pIndex = -1; + int64_t piIndex = -1; + + int v0Statusmc() const { return status; } + bool doubleStatusmc() const { return isDouble; } + float v0Cospamc() const { return cospa; } + float v0Radiusmc() const { return radius; } + float dcaPositivemc() const { return dcaPos; } + float dcaNegativemc() const { return dcaNeg; } + float dcaBetweenDaughtermc() const { return dcaDau; } + float lambdaPtmc() const { return lPt; } + float lambdaEtamc() const { return lEta; } + float lambdaPhimc() const { return lPhi; } + float lambdaMassmc() const { return lMass; } + float protonPtmc() const { return pPt; } + float protonEtamc() const { return pEta; } + float protonPhimc() const { return pPhi; } + int64_t protonIndexmc() const { return pIndex; } + int64_t pionIndexmc() const { return piIndex; } + int64_t globalIndex() const { return globalIdx; } + }; + + struct PendingV6BranchMC { + StoredV6CandidateMC target; + StoredV6CandidateMC fixed; + int colBin = -1; + int replacedLeg = 1; + int age = 0; + uint64_t seed = 0; + }; + + struct V6PendingState { + std::deque data; + std::deque mc; + }; + + V6PendingState v6Pending; + + template + StoredV6CandidateMC storeV6CandidateMC(T const& t) const + { + return {static_cast(t.globalIndex()), mcacc::v0Status(t), mcacc::doubleStatus(t), + mcacc::v0CosPA(t), mcacc::v0Radius(t), mcacc::dcaPos(t), mcacc::dcaNeg(t), mcacc::dcaDau(t), + mcacc::lamPt(t), mcacc::lamEta(t), mcacc::lamPhi(t), mcacc::lamMass(t), + mcacc::prPt(t), mcacc::prEta(t), mcacc::prPhi(t), + static_cast(mcacc::prIdx(t)), static_cast(mcacc::piIdx(t))}; + } + + template + void fillV6MixedBranchMC(TRep const& replacement, TFixed const& fixed, int replacedLeg, float controlWeight, float mixWeight) + { + const auto repProton = ROOT::Math::PtEtaPhiMVector(mcacc::prPt(replacement), mcacc::prEta(replacement), mcacc::prPhi(replacement), o2::constants::physics::MassProton); + const auto repLambda = ROOT::Math::PtEtaPhiMVector(mcacc::lamPt(replacement), mcacc::lamEta(replacement), mcacc::lamPhi(replacement), mcacc::lamMass(replacement)); + const auto fixedProton = ROOT::Math::PtEtaPhiMVector(mcacc::prPt(fixed), mcacc::prEta(fixed), mcacc::prPhi(fixed), o2::constants::physics::MassProton); + const auto fixedLambda = ROOT::Math::PtEtaPhiMVector(mcacc::lamPt(fixed), mcacc::lamEta(fixed), mcacc::lamPhi(fixed), mcacc::lamMass(fixed)); + const int repStatus = mcacc::v0Status(replacement); + const int fixedStatus = mcacc::v0Status(fixed); + + if (replacedLeg == 1) { + fillReplacementControlMap(repStatus, fixedStatus, 1, false, repLambda, controlWeight); + fillFixedLegControlMap(repStatus, fixedStatus, 1, false, fixedLambda, controlWeight); + if (fillBasicQAHistos) { + histos.fill(HIST("deltaPhiMix"), deltaPhiMinusPiToPi((float)repLambda.Phi(), (float)fixedLambda.Phi()), mixWeight); + } + if (repStatus == 0 && fixedStatus == 1) { + fillHistograms(0, 1, repLambda, fixedLambda, repProton, fixedProton, 1, mixWeight, 1, 1); + } else if (repStatus == 1 && fixedStatus == 0) { + fillHistograms(0, 1, fixedLambda, repLambda, fixedProton, repProton, 1, mixWeight, 2, 1); + } else { + fillHistograms(repStatus, fixedStatus, repLambda, fixedLambda, repProton, fixedProton, 1, mixWeight, 1, 1); + } + return; + } + + fillReplacementControlMap(fixedStatus, repStatus, 2, false, repLambda, controlWeight); + fillFixedLegControlMap(fixedStatus, repStatus, 2, false, fixedLambda, controlWeight); + if (fillBasicQAHistos) { + histos.fill(HIST("deltaPhiMix"), deltaPhiMinusPiToPi((float)fixedLambda.Phi(), (float)repLambda.Phi()), mixWeight); + } + if (fixedStatus == 0 && repStatus == 1) { + fillHistograms(0, 1, fixedLambda, repLambda, fixedProton, repProton, 1, mixWeight, 2, 2); + } else if (fixedStatus == 1 && repStatus == 0) { + fillHistograms(0, 1, repLambda, fixedLambda, repProton, fixedProton, 1, mixWeight, 1, 2); + } else { + fillHistograms(fixedStatus, repStatus, fixedLambda, repLambda, fixedProton, repProton, 1, mixWeight, 2, 2); + } + } + static inline size_t linearKeyR(int colBin, int statBin, int ptBin, int etaBin, int phiBin, int mBin, int rBin, int nStatus, int nPt, int nEta, int nPhi, int nM, int nR) @@ -1658,9 +1851,6 @@ struct lambdaspincorrderived { if (mcacc::dcaDau(candidate) > dcaDaughters) { return false; } - if (mcacc::dcaV0ToPV(candidate) > dcaV0ToPV) { - return false; - } if (mcacc::v0Status(candidate) == 0 && (std::abs(mcacc::dcaPos(candidate)) < dcaProton || std::abs(mcacc::dcaNeg(candidate)) < dcaPion)) { return false; } @@ -2143,6 +2333,68 @@ struct lambdaspincorrderived { } }; + const size_t pendingAtStart = v6Pending.data.size(); + size_t pendingMatched = 0; + size_t pendingExpired = 0; + size_t pendingAdded = 0; + if (!cfgV6CarryUnmatched) { + v6Pending.data.clear(); + } else { + for (auto it = v6Pending.data.begin(); it != v6Pending.data.end();) { + auto& pending = *it; + ++pending.age; + if (cfgV6MaxPendingAge.value > 0 && pending.age > cfgV6MaxPendingAge.value) { + ++pendingExpired; + it = v6Pending.data.erase(it); + continue; + } + + auto& matches = pending.replacedLeg == 1 ? matches1 : matches2; + collectMatchesForReplacedLeg(pending.target, pending.fixed, pending.colBin, -1, matches); + limitMatchesToNEvents(matches, nEvtMixing.value); + downsampleMatches(matches, pending.seed ^ splitmix64(static_cast(pending.age))); + + int nAccepted = 0; + for (auto const& m : matches) { + auto replacement = V0s.iteratorAt(static_cast(m.rowIndex)); + if (!selectionV0(replacement) || !checkKinematics(pending.target, replacement)) { + continue; + } + if (replacement.globalIndex() == pending.target.globalIndex() || replacement.globalIndex() == pending.fixed.globalIndex()) { + continue; + } + if (hasSharedDaughters(replacement, pending.target) || hasSharedDaughters(replacement, pending.fixed)) { + continue; + } + ++nAccepted; + } + + if (nAccepted == 0) { + ++it; + continue; + } + + const float controlWeight = 1.0f / static_cast(nAccepted); + const float branchNorm = cfgMixLegMode.value == 2 ? 0.5f : 1.0f; + const float mixWeight = branchNorm * controlWeight; + for (auto const& m : matches) { + auto replacement = V0s.iteratorAt(static_cast(m.rowIndex)); + if (!selectionV0(replacement) || !checkKinematics(pending.target, replacement)) { + continue; + } + if (replacement.globalIndex() == pending.target.globalIndex() || replacement.globalIndex() == pending.fixed.globalIndex()) { + continue; + } + if (hasSharedDaughters(replacement, pending.target) || hasSharedDaughters(replacement, pending.fixed)) { + continue; + } + fillV6MixedBranch(replacement, pending.fixed, pending.replacedLeg, controlWeight, mixWeight); + } + ++pendingMatched; + it = v6Pending.data.erase(it); + } + } + // -------- PASS 2: configurable one-leg / two-leg mixing -------- for (auto const& col1 : collisions) { const int colBin = colBinning.getBin(std::make_tuple(col1.posz(), col1.cent())); @@ -2249,6 +2501,22 @@ struct lambdaspincorrderived { } } + if (cfgV6CarryUnmatched) { + const auto hasPendingSpace = [&]() { + return cfgV6MaxPendingBranches.value <= 0 || static_cast(v6Pending.data.size()) < cfgV6MaxPendingBranches.value; + }; + if (doMixLeg1 && nFill1 == 0 && hasPendingSpace()) { + v6Pending.data.push_back({storeV6Candidate(t1, curColIdx), storeV6Candidate(t2, curColIdx), colBin, 1, 0, + static_cast(t1.globalIndex()) ^ splitmix64(static_cast(t2.globalIndex())) ^ splitmix64(static_cast(curColIdx))}); + ++pendingAdded; + } + if (doMixLeg2 && nFill2 == 0 && hasPendingSpace()) { + v6Pending.data.push_back({storeV6Candidate(t2, curColIdx), storeV6Candidate(t1, curColIdx), colBin, 2, 0, + static_cast(t2.globalIndex()) ^ splitmix64(static_cast(t1.globalIndex())) ^ splitmix64(static_cast(curColIdx))}); + ++pendingAdded; + } + } + if (nFill1 <= 0 && nFill2 <= 0) { continue; } @@ -2260,9 +2528,10 @@ struct lambdaspincorrderived { const int nActiveMixBranches = ((doMixLeg1 && nFill1 > 0) ? 1 : 0) + ((doMixLeg2 && nFill2 > 0) ? 1 : 0); - const float branchNorm = (cfgMixLegMode.value == 2 && nActiveMixBranches > 0) - ? 1.0f / static_cast(nActiveMixBranches) - : 1.0f; + float branchNorm = 1.0f; + if (cfgMixLegMode.value == 2) { + branchNorm = cfgV6CarryUnmatched ? 0.5f : 1.0f / static_cast(nActiveMixBranches); + } const float finalMixWeightLeg1 = branchNorm * wSELeg1; const float finalMixWeightLeg2 = branchNorm * wSELeg2; @@ -2370,6 +2639,10 @@ struct lambdaspincorrderived { } } } + if (cfgV6LogPending) { + LOGF(info, "MEV6 data pending branches: carriedIn=%zu matched=%zu expired=%zu newlyPropagated=%zu carriedToNext=%zu", + pendingAtStart, pendingMatched, pendingExpired, pendingAdded, v6Pending.data.size()); + } } PROCESS_SWITCH(lambdaspincorrderived, processMEV6, "Process data ME v6 with radius buffer", false); @@ -2606,6 +2879,68 @@ struct lambdaspincorrderived { } }; + const size_t pendingAtStartMC = v6Pending.mc.size(); + size_t pendingMatchedMC = 0; + size_t pendingExpiredMC = 0; + size_t pendingAddedMC = 0; + if (!cfgV6CarryUnmatched) { + v6Pending.mc.clear(); + } else { + for (auto it = v6Pending.mc.begin(); it != v6Pending.mc.end();) { + auto& pending = *it; + ++pending.age; + if (cfgV6MaxPendingAge.value > 0 && pending.age > cfgV6MaxPendingAge.value) { + ++pendingExpiredMC; + it = v6Pending.mc.erase(it); + continue; + } + + auto& matches = pending.replacedLeg == 1 ? matches1 : matches2; + collectMatchesForReplacedLeg(pending.target, pending.fixed, pending.colBin, -1, matches); + limitMatchesToNEvents(matches, nEvtMixing.value); + downsampleMatches(matches, pending.seed ^ splitmix64(static_cast(pending.age))); + + int nAccepted = 0; + for (auto const& m : matches) { + auto replacement = V0sMC.iteratorAt(static_cast(m.rowIndex)); + if (!selectionV0MC(replacement) || !checkKinematicsMC(pending.target, replacement)) { + continue; + } + if (replacement.globalIndex() == pending.target.globalIndex() || replacement.globalIndex() == pending.fixed.globalIndex()) { + continue; + } + if (hasSharedDaughtersMC(replacement, pending.target) || hasSharedDaughtersMC(replacement, pending.fixed)) { + continue; + } + ++nAccepted; + } + + if (nAccepted == 0) { + ++it; + continue; + } + + const float controlWeight = 1.0f / static_cast(nAccepted); + const float branchNorm = cfgMixLegMode.value == 2 ? 0.5f : 1.0f; + const float mixWeight = branchNorm * controlWeight; + for (auto const& m : matches) { + auto replacement = V0sMC.iteratorAt(static_cast(m.rowIndex)); + if (!selectionV0MC(replacement) || !checkKinematicsMC(pending.target, replacement)) { + continue; + } + if (replacement.globalIndex() == pending.target.globalIndex() || replacement.globalIndex() == pending.fixed.globalIndex()) { + continue; + } + if (hasSharedDaughtersMC(replacement, pending.target) || hasSharedDaughtersMC(replacement, pending.fixed)) { + continue; + } + fillV6MixedBranchMC(replacement, pending.fixed, pending.replacedLeg, controlWeight, mixWeight); + } + ++pendingMatchedMC; + it = v6Pending.mc.erase(it); + } + } + // -------- PASS 2: configurable one-leg / two-leg mixing -------- for (auto const& col1 : collisions) { const int colBin = colBinning.getBin(std::make_tuple(mcacc::posz(col1), mcacc::cent(col1))); @@ -2713,6 +3048,22 @@ struct lambdaspincorrderived { } } + if (cfgV6CarryUnmatched) { + const auto hasPendingSpace = [&]() { + return cfgV6MaxPendingBranches.value <= 0 || static_cast(v6Pending.mc.size()) < cfgV6MaxPendingBranches.value; + }; + if (doMixLeg1 && nFill1 == 0 && hasPendingSpace()) { + v6Pending.mc.push_back({storeV6CandidateMC(t1), storeV6CandidateMC(t2), colBin, 1, 0, + static_cast(t1.globalIndex()) ^ splitmix64(static_cast(t2.globalIndex())) ^ splitmix64(static_cast(curColIdx))}); + ++pendingAddedMC; + } + if (doMixLeg2 && nFill2 == 0 && hasPendingSpace()) { + v6Pending.mc.push_back({storeV6CandidateMC(t2), storeV6CandidateMC(t1), colBin, 2, 0, + static_cast(t2.globalIndex()) ^ splitmix64(static_cast(t1.globalIndex())) ^ splitmix64(static_cast(curColIdx))}); + ++pendingAddedMC; + } + } + if (nFill1 <= 0 && nFill2 <= 0) { continue; } @@ -2722,9 +3073,10 @@ struct lambdaspincorrderived { const int nActiveMixBranches = ((doMixLeg1 && nFill1 > 0) ? 1 : 0) + ((doMixLeg2 && nFill2 > 0) ? 1 : 0); - const float branchNorm = (cfgMixLegMode.value == 2 && nActiveMixBranches > 0) - ? 1.0f / static_cast(nActiveMixBranches) - : 1.0f; + float branchNorm = 1.0f; + if (cfgMixLegMode.value == 2) { + branchNorm = cfgV6CarryUnmatched ? 0.5f : 1.0f / static_cast(nActiveMixBranches); + } const float finalMixWeightLeg1 = branchNorm * wSELeg1; const float finalMixWeightLeg2 = branchNorm * wSELeg2; @@ -2840,8 +3192,21 @@ struct lambdaspincorrderived { } } } + if (cfgV6LogPending) { + LOGF(info, "MEV6 MC pending branches: carriedIn=%zu matched=%zu expired=%zu newlyPropagated=%zu carriedToNext=%zu", + pendingAtStartMC, pendingMatchedMC, pendingExpiredMC, pendingAddedMC, v6Pending.mc.size()); + } } PROCESS_SWITCH(lambdaspincorrderived, processMCMEV6, "Process MC ME v6 with radius buffer", false); + + void endOfStream(EndOfStreamContext&) + { + if (cfgV6LogPending) { + LOGF(info, "MEV6 end of stream: unmatchedData=%zu unmatchedMC=%zu", v6Pending.data.size(), v6Pending.mc.size()); + } + v6Pending.data.clear(); + v6Pending.mc.clear(); + } }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) {