diff --git a/PWGLF/DataModel/LFPhiFlowTables.h b/PWGLF/DataModel/LFPhiFlowTables.h index a1efbbcb7a7..139d0ee763c 100644 --- a/PWGLF/DataModel/LFPhiFlowTables.h +++ b/PWGLF/DataModel/LFPhiFlowTables.h @@ -25,7 +25,7 @@ namespace o2::aod { -namespace kaonkaonevent +namespace kaonevent { DECLARE_SOA_COLUMN(Cent, cent, float); DECLARE_SOA_COLUMN(Posz, posz, float); @@ -33,45 +33,37 @@ DECLARE_SOA_COLUMN(QxA, qxA, float); DECLARE_SOA_COLUMN(QxC, qxC, float); DECLARE_SOA_COLUMN(QyA, qyA, float); DECLARE_SOA_COLUMN(QyC, qyC, float); -} // namespace kaonkaonevent -DECLARE_SOA_TABLE(KaonkaonEvents, "AOD", "KAONKAONEVENT", +} // namespace kaonevent +DECLARE_SOA_TABLE(KaonEvents, "AOD", "KAONEVENT", o2::soa::Index<>, - kaonkaonevent::Cent, - kaonkaonevent::Posz, - kaonkaonevent::QxA, - kaonkaonevent::QxC, - kaonkaonevent::QyA, - kaonkaonevent::QyC) -using KaonkaonEvent = KaonkaonEvents::iterator; + kaonevent::Cent, + kaonevent::Posz, + kaonevent::QxA, + kaonevent::QxC, + kaonevent::QyA, + kaonevent::QyC) +using KaonEvent = KaonEvents::iterator; -namespace kaonkaonpair +namespace kaonpair { -DECLARE_SOA_INDEX_COLUMN(KaonkaonEvent, kaonkaonevent); -DECLARE_SOA_COLUMN(D1Px, d1Px, float); //! Bachelor Kaon Px -DECLARE_SOA_COLUMN(D1Py, d1Py, float); //! Bachelor Kaon Py -DECLARE_SOA_COLUMN(D1Pz, d1Pz, float); //! Bachelor Kaon Pz -DECLARE_SOA_COLUMN(D2Px, d2Px, float); //! Bachelor Kaon Px -DECLARE_SOA_COLUMN(D2Py, d2Py, float); //! Bachelor Kaon Py -DECLARE_SOA_COLUMN(D2Pz, d2Pz, float); //! Bachelor Kaon Pz -DECLARE_SOA_COLUMN(PhiM, phiM, float); //! Phi Mass -DECLARE_SOA_COLUMN(KaonIndex1, kaonIndex1, int64_t); //! Daughter Kaon index1 -DECLARE_SOA_COLUMN(KaonIndex2, kaonIndex2, int64_t); //! Daughter Kaon index2 +DECLARE_SOA_INDEX_COLUMN(KaonEvent, kaonevent); +DECLARE_SOA_COLUMN(Px, px, float); //! Bachelor Kaon Px +DECLARE_SOA_COLUMN(Py, py, float); //! Bachelor Kaon Py +DECLARE_SOA_COLUMN(Pz, pz, float); //! Bachelor Kaon Pz +DECLARE_SOA_COLUMN(Charge, charge, int8_t); //! Charge +DECLARE_SOA_COLUMN(KaonIndex, kaonIndex, int64_t); //! Daughter Kaon index1 DECLARE_SOA_COLUMN(KaonPidMask, kaonPidMask, uint16_t); //! bitmask for PID selections -} // namespace kaonkaonpair +} // namespace kaonpair DECLARE_SOA_TABLE(KaonTracks, "AOD", "KAONTRACK", o2::soa::Index<>, - kaonkaonpair::KaonkaonEventId, - kaonkaonpair::D1Px, - kaonkaonpair::D1Py, - kaonkaonpair::D1Pz, - kaonkaonpair::D2Px, - kaonkaonpair::D2Py, - kaonkaonpair::D2Pz, - kaonkaonpair::PhiM, - kaonkaonpair::KaonIndex1, - kaonkaonpair::KaonIndex2, - kaonkaonpair::KaonPidMask); + kaonpair::KaonEventId, + kaonpair::Px, + kaonpair::Py, + kaonpair::Pz, + kaonpair::Charge, + kaonpair::KaonIndex, + kaonpair::KaonPidMask); using KaonTrack = KaonTracks::iterator; } // namespace o2::aod diff --git a/PWGLF/TableProducer/Resonances/phiflow.cxx b/PWGLF/TableProducer/Resonances/phiflow.cxx index 764ff028cae..84708fde922 100644 --- a/PWGLF/TableProducer/Resonances/phiflow.cxx +++ b/PWGLF/TableProducer/Resonances/phiflow.cxx @@ -54,7 +54,7 @@ using namespace o2::aod::rctsel; struct phiflow { - Produces kaonkaonEvent; + Produces kaonEvent; Produces kaonTrack; struct : ConfigurableGroup { @@ -96,8 +96,6 @@ struct phiflow { Configurable nsigmaCutTPCKaMeson{"nsigmaCutTPCKaMeson", 3.0, "Maximum nsigma cut TPC for kaon meson track"}; Configurable nsigmaCutTOFKaMeson{"nsigmaCutTOFKaMeson", 3.0, "Maximum nsigma cut TOF for kaon meson track"}; Configurable cutTOFBetaKaMeson{"cutTOFBetaKaMeson", 1.0, "Maximum beta cut for kaon meson track"}; - Configurable isDeepAngle{"isDeepAngle", true, "Deep Angle cut"}; - Configurable cutDeepAngle{"cutDeepAngle", 0.04, "Deep Angle cut value"}; } grpKaon; enum KaonPidBits : uint8_t { @@ -107,26 +105,15 @@ struct phiflow { kPID4 = 1u << 3 // selectionPID4 }; - // configurable for chargedkstar - Configurable cfgPhiMassMin{"cfgPhiMassMin", 0.9f, "Phi mass min"}; - Configurable cfgPhiMassMax{"cfgPhiMassMax", 1.2f, "Phi mass max"}; - - Configurable iMNbins{"iMNbins", 120, "Number of bins in invariant mass"}; - Configurable lbinIM{"lbinIM", 0.9, "lower bin value in IM histograms"}; - Configurable hbinIM{"hbinIM", 1.2, "higher bin value in IM histograms"}; - HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; RCTFlagsChecker rctChecker; void init(o2::framework::InitContext&) { rctChecker.init(rctCut.cfgEvtRCTFlagCheckerLabel, rctCut.cfgEvtRCTFlagCheckerZDCCheck, rctCut.cfgEvtRCTFlagCheckerLimitAcceptAsBad); - AxisSpec thnAxisInvMass{iMNbins, lbinIM, hbinIM, "#it{M} (GeV/#it{c}^{2})"}; histos.add("hCent", "hCent", kTH1F, {{8, 0, 80.0}}); histos.add("hEvtSelInfo", "hEvtSelInfo", kTH1F, {{5, 0, 5.0}}); histos.add("hTrkSelInfo", "hTrkSelInfo", kTH1F, {{10, 0, 10.0}}); - histos.add("hPhiMass", "hPhiMass", kTH1F, {thnAxisInvMass}); - histos.add("hInfo", "hInfo", kTH1F, {{5, 0, 5.0}}); } template @@ -191,28 +178,23 @@ struct phiflow { template bool selectionPID4(const T& candidate) { - const float px = candidate.px(); - const float py = candidate.py(); - // const float pz = candidate.pz(); - const float pt = std::sqrt(px * px + py * py); - - constexpr float pSwitch = 0.5f; // GeV/c + constexpr float pSwitch = 0.5f; + const float pt = candidate.pt(); const float nTPC = candidate.tpcNSigmaKa(); - // Low momentum: TPC-only, TOF not required and not used if (pt < pSwitch) { - return std::abs(nTPC) < grpKaon.nsigmaCutTPCKaMeson; // e.g. 3 + return std::abs(nTPC) < grpKaon.nsigmaCutTPCKaMeson; } - // High momentum: TOF hit mandatory + separate 3σ cuts if (!candidate.hasTOF()) { return false; } const float nTOF = candidate.tofNSigmaKa(); - return (std::abs(nTPC) < grpKaon.nsigmaCutTPCKaMeson) && - (std::abs(nTOF) < grpKaon.nsigmaCutTOFKaMeson); + + return std::abs(nTPC) < grpKaon.nsigmaCutTPCKaMeson && + std::abs(nTOF) < grpKaon.nsigmaCutTOFKaMeson; } template @@ -235,26 +217,14 @@ struct phiflow { return m; } - // deep angle cut on pair to remove photon conversion - template - bool selectionPair(const T1& candidate1, const T2& candidate2) - { - const double pt1 = candidate1.pt(); - const double pt2 = candidate2.pt(); - const double pz1 = candidate1.pz(); - const double pz2 = candidate2.pz(); - const double p1 = candidate1.p(); - const double p2 = candidate2.p(); - - if (p1 <= 0.0 || p2 <= 0.0) { - return false; - } - - const double cosAngle = std::clamp((pt1 * pt2 + pz1 * pz2) / (p1 * p2), -1.0, 1.0); - const double angle = std::acos(cosAngle); - - return !(grpKaon.isDeepAngle && angle < grpKaon.cutDeepAngle); - } + struct StoredKaon { + float px; + float py; + float pz; + int8_t charge; + int64_t trackId; + uint8_t pidMask; + }; Filter collisionFilter = nabs(aod::collision::posZ) < cfgCutVertex; Filter centralityFilter = (aod::cent::centFT0C < cfgCutCentralityMax && aod::cent::centFT0C > cfgCutCentralityMin); @@ -268,8 +238,6 @@ struct phiflow { Partition posTracks = aod::track::signed1Pt > cfgCutCharge; Partition negTracks = aod::track::signed1Pt < cfgCutCharge; - static constexpr double massKa = o2::constants::physics::MassKPlus; - void processData(EventCandidates::iterator const& collision, AllTrackCandidates const& /*tracks*/) { @@ -279,19 +247,16 @@ struct phiflow { const auto vz = collision.posZ(); const int occupancy = collision.trackOccupancyInTimeRange(); - const auto qxZDCA = collision.qxZDCA(); - const auto qxZDCC = collision.qxZDCC(); - const auto qyZDCA = collision.qyZDCA(); - const auto qyZDCC = collision.qyZDCC(); - histos.fill(HIST("hEvtSelInfo"), 0.5); if (!((!rctCut.requireRCTFlagChecker || rctChecker(collision)) && collision.selection_bit(aod::evsel::kNoSameBunchPileup) && collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV) && - (!useNoCollInTimeRangeStandard || collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) && + (!useNoCollInTimeRangeStandard || + collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) && collision.sel8() && - (!useGoodITSLayersAll || collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) && + (!useGoodITSLayersAll || + collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) && occupancy < cfgCutOccupancy)) { return; } @@ -299,21 +264,20 @@ struct phiflow { histos.fill(HIST("hEvtSelInfo"), 1.5); histos.fill(HIST("hCent"), centrality); - std::vector phiresonance; - std::vector phiresonanced1; - std::vector phiresonanced2; - std::vector Phid1Index; - std::vector Phid2Index; - std::vector PhiPairPidMask; + std::vector selectedKaons; - auto posThisColl = posTracks->sliceByCached(aod::track::collisionId, - collision.globalIndex(), - cache); + // Same slicing pattern that worked in your old producer. + auto posThisColl = posTracks->sliceByCached( + aod::track::collisionId, + collision.globalIndex(), + cache); - auto negThisColl = negTracks->sliceByCached(aod::track::collisionId, - collision.globalIndex(), - cache); + auto negThisColl = negTracks->sliceByCached( + aod::track::collisionId, + collision.globalIndex(), + cache); + // ---------------- Positive kaons ---------------- for (const auto& track1 : posThisColl) { histos.fill(HIST("hTrkSelInfo"), 0.5); @@ -323,7 +287,9 @@ struct phiflow { histos.fill(HIST("hTrkSelInfo"), 1.5); - const float nSigmaITS1 = itsResponse.nSigmaITS(track1); + const float nSigmaITS1 = + itsResponse.nSigmaITS(track1); + if (grpKaon.itsPIDSelection && (nSigmaITS1 <= grpKaon.lowITSPIDNsigma || nSigmaITS1 >= grpKaon.highITSPIDNsigma)) { @@ -332,95 +298,87 @@ struct phiflow { histos.fill(HIST("hTrkSelInfo"), 2.5); - const uint8_t mask = kaonPidMask(track1); - if (grpKaon.usePID && mask == 0) { + const uint8_t mask1 = kaonPidMask(track1); + + if (grpKaon.usePID && mask1 == 0) { continue; } histos.fill(HIST("hTrkSelInfo"), 3.5); - const auto track1ID = track1.globalIndex(); - - for (const auto& track2 : negThisColl) { - histos.fill(HIST("hTrkSelInfo"), 4.5); - - if (!selectionTrack(track2)) { - continue; - } - - histos.fill(HIST("hTrkSelInfo"), 5.5); - - const float nSigmaITS2 = itsResponse.nSigmaITS(track2); - if (grpKaon.itsPIDSelection && - (nSigmaITS2 <= grpKaon.lowITSPIDNsigma || - nSigmaITS2 >= grpKaon.highITSPIDNsigma)) { - continue; - } - - histos.fill(HIST("hTrkSelInfo"), 6.5); - - const uint8_t mask2 = kaonPidMask(track2); - if (grpKaon.usePID && mask2 == 0) { - continue; - } + selectedKaons.push_back({track1.px(), + track1.py(), + track1.pz(), + +1, + static_cast(track1.globalIndex()), + mask1}); + } - histos.fill(HIST("hTrkSelInfo"), 7.5); + // ---------------- Negative kaons ---------------- + for (const auto& track2 : negThisColl) { + histos.fill(HIST("hTrkSelInfo"), 4.5); - const auto track2ID = track2.globalIndex(); + if (!selectionTrack(track2)) { + continue; + } - if (track2ID == track1ID) { - continue; - } + histos.fill(HIST("hTrkSelInfo"), 5.5); - if (!selectionPair(track1, track2)) { - continue; - } + const float nSigmaITS2 = + itsResponse.nSigmaITS(track2); - ROOT::Math::PxPyPzMVector KaonPlus(track1.px(), track1.py(), track1.pz(), massKa); - ROOT::Math::PxPyPzMVector KaonMinus(track2.px(), track2.py(), track2.pz(), massKa); - auto PhiMesonMother = KaonPlus + KaonMinus; + if (grpKaon.itsPIDSelection && + (nSigmaITS2 <= grpKaon.lowITSPIDNsigma || + nSigmaITS2 >= grpKaon.highITSPIDNsigma)) { + continue; + } - histos.fill(HIST("hPhiMass"), PhiMesonMother.M()); + histos.fill(HIST("hTrkSelInfo"), 6.5); - ROOT::Math::PtEtaPhiMVector temp1(track1.pt(), track1.eta(), track1.phi(), massKa); - ROOT::Math::PtEtaPhiMVector temp2(track2.pt(), track2.eta(), track2.phi(), massKa); - ROOT::Math::PtEtaPhiMVector temp3(PhiMesonMother.pt(), PhiMesonMother.eta(), PhiMesonMother.phi(), PhiMesonMother.M()); + const uint8_t mask2 = kaonPidMask(track2); - phiresonanced1.push_back(temp1); - phiresonanced2.push_back(temp2); - phiresonance.push_back(temp3); + if (grpKaon.usePID && mask2 == 0) { + continue; + } - Phid1Index.push_back(track1ID); - Phid2Index.push_back(track2ID); + histos.fill(HIST("hTrkSelInfo"), 7.5); - const uint16_t pairPidMask = (static_cast(mask2) << 8) | mask; - PhiPairPidMask.push_back(pairPidMask); - } + selectedKaons.push_back({track2.px(), + track2.py(), + track2.pz(), + -1, + static_cast(track2.globalIndex()), + mask2}); } - // Optional: if no accepted K+K- pair, do not write this event - if (phiresonance.empty()) { + // No selected K+ or K- in this collision: + // not writing a reduced event row. + if (selectedKaons.empty()) { return; } - kaonkaonEvent(centrality, vz, qxZDCA, qxZDCC, qyZDCA, qyZDCC); - const auto indexEvent = kaonkaonEvent.lastIndex(); + histos.fill(HIST("hEvtSelInfo"), 2.5); - for (size_t i = 0; i < phiresonance.size(); ++i) { - const auto& phiCand = phiresonance[i]; - const auto& d1 = phiresonanced1[i]; - const auto& d2 = phiresonanced2[i]; + kaonEvent(centrality, + vz, + collision.qxZDCA(), + collision.qxZDCC(), + collision.qyZDCA(), + collision.qyZDCC()); + const int64_t indexEvent = kaonEvent.lastIndex(); + + // One table row per selected kaon. + for (const auto& kaon : selectedKaons) { kaonTrack(indexEvent, - d1.Px(), d1.Py(), d1.Pz(), - d2.Px(), d2.Py(), d2.Pz(), - phiCand.M(), - Phid1Index[i], - Phid2Index[i], - PhiPairPidMask[i]); + kaon.px, + kaon.py, + kaon.pz, + kaon.charge, + kaon.trackId, + kaon.pidMask); } } - PROCESS_SWITCH(phiflow, processData, "Process data", true); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) diff --git a/PWGLF/Tasks/Resonances/phiflowder.cxx b/PWGLF/Tasks/Resonances/phiflowder.cxx index 73db0d04207..1cc19684a49 100644 --- a/PWGLF/Tasks/Resonances/phiflowder.cxx +++ b/PWGLF/Tasks/Resonances/phiflowder.cxx @@ -37,52 +37,46 @@ using namespace o2::framework::expressions; struct phiflowder { - // Event selection Configurable centMin{"centMin", 0.f, "Minimum centrality"}; Configurable centMax{"centMax", 80.f, "Maximum centrality"}; - // PID selection from stored bitmask - Configurable usePID{"usePID", false, "Enable PID selection using stored bitmask"}; - Configurable pidChoice{"pidChoice", 2, "PID choice: 1, 2, 3, 4"}; - Configurable pidExclusive{"pidExclusive", false, "If true require only the chosen PID bit"}; + Configurable useStoredPID{"useStoredPID", false, "Apply PID-bit selection using stored kaon PID mask"}; + Configurable pidChoice{"pidChoice", 2, "PID choice: 1, 2, 3, or 4"}; + Configurable pidExclusive{"pidExclusive", false, "Require exactly the selected PID bit"}; - // Event mixing - Configurable nEvtMixing{"nEvtMixing", 5, "Number of events to mix"}; - ConfigurableAxis cfgVtxBins{"cfgVtxBins", {5, -10.0, 10.0}, "Mixing bins in z vertex"}; - ConfigurableAxis cfgMultBins{"cfgMultBins", {8, 0.0, 80.0}, "Mixing bins in centrality"}; + Configurable applyDeepAngle{"applyDeepAngle", false, "Apply minimum opening-angle cut for K+K- pairs"}; + Configurable deepAngleCut{"deepAngleCut", 0.04f, "Minimum K+K- opening angle"}; - // Histogram axes - ConfigurableAxis configAxisInvMass{"configAxisInvMass", {120, 0.9, 1.2}, "#it{M}_{K^{+}K^{-}} (GeV/#it{c}^{2})"}; - ConfigurableAxis configAxisPt{"configAxisPt", {100, 0.0, 10.0}, "#it{p}_{T} (GeV/#it{c})"}; - ConfigurableAxis configAxisCentrality{"configAxisCentrality", {8, 0.0, 80.0}, "Centrality (%)"}; + Configurable massMin{"massMin", 0.9f, "Minimum K+K- mass to fill"}; + Configurable massMax{"massMax", 1.2f, "Maximum K+K- mass to fill"}; + + ConfigurableAxis axisInvMass{"axisInvMass", {120, 0.9f, 1.2f}, "#it{M}_{K^{+}K^{-}} (GeV/#it{c}^{2})"}; + ConfigurableAxis axisPhiPt{"axisPhiPt", {100, 0.f, 10.f}, "#it{p}_{T}^{K^{+}K^{-}} (GeV/#it{c})"}; + ConfigurableAxis axisCent{"axisCent", {80, 0.f, 80.f}, "Centrality (%)"}; + ConfigurableAxis axisNKaons{"axisNKaons", {300, 0.f, 300.f}, "Number of stored kaons per event"}; + ConfigurableAxis axisNPairs{"axisNPairs", {500, 0.f, 5000.f}, "Number of K^{+}K^{-} pairs per event"}; HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; - // PID bits: same convention as producer static constexpr uint8_t kPID1 = 1u << 0; static constexpr uint8_t kPID2 = 1u << 1; static constexpr uint8_t kPID3 = 1u << 2; static constexpr uint8_t kPID4 = 1u << 3; - struct KaonDaughter { - float px; - float py; - float pz; - uint8_t pidMask; - }; + SliceCache cache; - void init(o2::framework::InitContext&) + void init(InitContext const&) { - - histos.add("hCentrality", "Centrality distribution", kTH1F, {configAxisCentrality}); - - histos.add("hSparseSame", "Same-event sparse", kTHnSparseF, - {configAxisInvMass, configAxisPt, configAxisCentrality}); - histos.add("hSparseMixed", "Mixed-event sparse", kTHnSparseF, - {configAxisInvMass, configAxisPt, configAxisCentrality}); + histos.add("hCentrality", "Centrality distribution", kTH1F, {axisCent}); + histos.add("hPhiMassSame", "Same-event K^{+}K^{-} invariant mass", kTH1F, {axisInvMass}); + histos.add("hPhiPtSame", "Same-event K^{+}K^{-} pT", kTH1F, {axisPhiPt}); + histos.add("hNplusPerEvent", "Number of stored K^{+} per event", kTH1F, {axisNKaons}); + histos.add("hNminusPerEvent", "Number of stored K^{-} per event", kTH1F, {axisNKaons}); + histos.add("hNphiSamePerEvent", "Number of same-event K^{+}K^{-} pairs per event", kTH1F, {axisNPairs}); + histos.add("hSparseSame", "Same-event K^{+}K^{-};M;pT;centrality", kTHnSparseF, {axisInvMass, axisPhiPt, axisCent}); } - uint8_t requiredPidBit() const + uint8_t getRequiredPidBit() const { switch (pidChoice.value) { case 1: @@ -94,207 +88,121 @@ struct phiflowder { case 4: return kPID4; default: - LOGF(warn, "pidChoice=%d is invalid. Using PID2.", pidChoice.value); + LOGF(warn, "Invalid pidChoice=%d. PID2 will be used.", pidChoice.value); return kPID2; } } - bool passDaughterPID(uint8_t mask) const + template + bool passStoredPID(const T& kaon) const { - if (!usePID.value) { + if (!useStoredPID.value) { return true; } - const uint8_t bit = requiredPidBit(); + const uint16_t mask = kaon.kaonPidMask(); + const uint8_t requiredBit = getRequiredPidBit(); if (pidExclusive.value) { - return mask == bit; + return mask == requiredBit; } - return (mask & bit) != 0; + return (mask & requiredBit) != 0; } - template - bool passPairPID(const TPhi& phiCand) const + bool passDeepAngle(float px1, float py1, float pz1, float px2, float py2, float pz2) const { - if (!usePID.value) { + if (!applyDeepAngle.value) { return true; } - const uint16_t pairMask = phiCand.kaonPidMask(); - - const uint8_t kPlusMask = pairMask & 0xFF; - const uint8_t kMinusMask = (pairMask >> 8) & 0xFF; - - return passDaughterPID(kPlusMask) && passDaughterPID(kMinusMask); - } - - void collectUniqueKPlusForEvent(const aod::KaonTracks& kaontracks, - int64_t eventId, - std::map& uniqueKPlus) - { - uniqueKPlus.clear(); - - for (const auto& phiCand : kaontracks) { - if (phiCand.kaonkaoneventId() != eventId) { - continue; - } + const double p1 = std::sqrt(px1 * px1 + py1 * py1 + pz1 * pz1); + const double p2 = std::sqrt(px2 * px2 + py2 * py2 + pz2 * pz2); - const uint16_t pairMask = phiCand.kaonPidMask(); - const uint8_t kPlusMask = pairMask & 0xFF; - - if (!passDaughterPID(kPlusMask)) { - continue; - } - - const int64_t kPlusId = phiCand.kaonIndex1(); - - if (uniqueKPlus.find(kPlusId) == uniqueKPlus.end()) { - uniqueKPlus[kPlusId] = { - phiCand.d1Px(), - phiCand.d1Py(), - phiCand.d1Pz(), - kPlusMask}; - } + if (p1 <= 0.0 || p2 <= 0.0) { + return false; } - } - void collectUniqueKMinusForEvent(const aod::KaonTracks& kaontracks, - int64_t eventId, - std::map& uniqueKMinus) - { - uniqueKMinus.clear(); + const double cosAngle = std::clamp((px1 * px2 + py1 * py2 + pz1 * pz2) / (p1 * p2), -1.0, 1.0); + const double angle = std::acos(cosAngle); - for (const auto& phiCand : kaontracks) { - if (phiCand.kaonkaoneventId() != eventId) { - continue; - } + return angle >= deepAngleCut.value; + } - const uint16_t pairMask = phiCand.kaonPidMask(); - const uint8_t kMinusMask = (pairMask >> 8) & 0xFF; + Filter centralityFilter = (aod::kaonevent::cent >= centMin) && (aod::kaonevent::cent < centMax); - if (!passDaughterPID(kMinusMask)) { - continue; - } + using EventCandidates = soa::Filtered; - const int64_t kMinusId = phiCand.kaonIndex2(); + Partition posKaons = aod::kaonpair::charge > int8_t{0}; + Partition negKaons = aod::kaonpair::charge < int8_t{0}; - if (uniqueKMinus.find(kMinusId) == uniqueKMinus.end()) { - uniqueKMinus[kMinusId] = { - phiCand.d2Px(), - phiCand.d2Py(), - phiCand.d2Pz(), - kMinusMask}; - } - } - } - - // Filter reduced events - Filter centralityFilter = (aod::kaonkaonevent::cent >= centMin && - aod::kaonkaonevent::cent < centMax); - using EventCandidates = soa::Filtered; - - void processSameData(EventCandidates::iterator const& collision, - aod::KaonTracks const& kaontracks) + void processSameData(EventCandidates::iterator const& collision, aod::KaonTracks const& /*kaontracks*/) { const float centrality = collision.cent(); - const int64_t eventId = collision.globalIndex(); histos.fill(HIST("hCentrality"), centrality); - for (const auto& phiCand : kaontracks) { - if (phiCand.kaonkaoneventId() != eventId) { - continue; - } - - if (!passPairPID(phiCand)) { - continue; - } + auto posThisColl = posKaons->sliceByCached(aod::kaonpair::kaoneventId, collision.globalIndex(), cache); + auto negThisColl = negKaons->sliceByCached(aod::kaonpair::kaoneventId, collision.globalIndex(), cache); - ROOT::Math::PxPyPzMVector kPlus( - phiCand.d1Px(), - phiCand.d1Py(), - phiCand.d1Pz(), - o2::constants::physics::MassKPlus); + int nPlus = 0; + int nMinus = 0; + int nPhiSame = 0; - ROOT::Math::PxPyPzMVector kMinus( - phiCand.d2Px(), - phiCand.d2Py(), - phiCand.d2Pz(), - o2::constants::physics::MassKPlus); - - auto phi = kPlus + kMinus; - - const float phiMass = phi.M(); - const float phiPt = phi.Pt(); - - histos.fill(HIST("hSparseSame"), phiMass, phiPt, centrality); + for (const auto& kPlus : posThisColl) { + if (passStoredPID(kPlus)) { + nPlus++; + } } - } - - PROCESS_SWITCH(phiflowder, processSameData, "Process same-event phi candidates", true); - using BinningType = ColumnBinningPolicy; - - void processMixedData(EventCandidates const& collisions, - aod::KaonTracks const& kaontracks) - { - - BinningType colBinning{{cfgVtxBins, cfgMultBins}, false}; - - std::map kPlusEvent1; - std::map kMinusEvent2; - // int nMixedEventPairs = 0; + for (const auto& kMinus : negThisColl) { + if (passStoredPID(kMinus)) { + nMinus++; + } + } - for (const auto& [collision1, collision2] : - selfCombinations(colBinning, nEvtMixing.value, -1, collisions, collisions)) { + histos.fill(HIST("hNplusPerEvent"), nPlus); + histos.fill(HIST("hNminusPerEvent"), nMinus); - if (collision1.globalIndex() == collision2.globalIndex()) { + for (const auto& kPlus : posThisColl) { + if (!passStoredPID(kPlus)) { continue; } - // nMixedEventPairs++; - - const float centrality = collision1.cent(); - collectUniqueKPlusForEvent(kaontracks, - collision1.globalIndex(), - kPlusEvent1); + ROOT::Math::PxPyPzMVector plusVec(kPlus.px(), kPlus.py(), kPlus.pz(), o2::constants::physics::MassKPlus); - collectUniqueKMinusForEvent(kaontracks, - collision2.globalIndex(), - kMinusEvent2); + for (const auto& kMinus : negThisColl) { + if (!passStoredPID(kMinus)) { + continue; + } - // histos.fill(HIST("hNplusUnique"), kPlusEvent1.size()); - // histos.fill(HIST("hNminusUnique"), kMinusEvent2.size()); + if (!passDeepAngle(kPlus.px(), kPlus.py(), kPlus.pz(), kMinus.px(), kMinus.py(), kMinus.pz())) { + continue; + } - // const int nMixedKPairs = static_cast(kPlusEvent1.size() * kMinusEvent2.size()); + ROOT::Math::PxPyPzMVector minusVec(kMinus.px(), kMinus.py(), kMinus.pz(), o2::constants::physics::MassKPlus); - // histos.fill(HIST("hNmixedKPairsPerEventPair"), nMixedKPairs); + const auto phiCandidate = plusVec + minusVec; - for (const auto& [kPlusId, kPlus] : kPlusEvent1) { - ROOT::Math::PxPyPzMVector kPlusVec( - kPlus.px, - kPlus.py, - kPlus.pz, - o2::constants::physics::MassKPlus); + const float phiMass = phiCandidate.M(); + const float phiPt = phiCandidate.Pt(); - for (const auto& [kMinusId, kMinus] : kMinusEvent2) { - ROOT::Math::PxPyPzMVector kMinusVec( - kMinus.px, - kMinus.py, - kMinus.pz, - o2::constants::physics::MassKPlus); + if (phiMass < massMin || phiMass > massMax) { + continue; + } - auto phiMix = kPlusVec + kMinusVec; + histos.fill(HIST("hPhiMassSame"), phiMass); + histos.fill(HIST("hPhiPtSame"), phiPt); + histos.fill(HIST("hSparseSame"), phiMass, phiPt, centrality); - histos.fill(HIST("hSparseMixed"), phiMix.M(), phiMix.Pt(), centrality); - } + nPhiSame++; } } - // histos.fill(HIST("hNMixEventPairs"), nMixedEventPairs); + + histos.fill(HIST("hNphiSamePerEvent"), nPhiSame); } - PROCESS_SWITCH(phiflowder, processMixedData, "Process mixed-event phi candidates", false); + + PROCESS_SWITCH(phiflowder, processSameData, "Process same-event K+K- pairs", true); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc)