From c3c31eca30ea9f00e0716165c4261840a3237ccc Mon Sep 17 00:00:00 2001 From: skundu692 Date: Fri, 1 May 2026 15:59:11 +0200 Subject: [PATCH 1/8] Fix Lambda spin correlation selection --- .../Strangeness/lambdaspincorrderived.cxx | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/PWGLF/Tasks/Strangeness/lambdaspincorrderived.cxx b/PWGLF/Tasks/Strangeness/lambdaspincorrderived.cxx index 012685039f0..12fbd831149 100644 --- a/PWGLF/Tasks/Strangeness/lambdaspincorrderived.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaspincorrderived.cxx @@ -856,7 +856,8 @@ struct lambdaspincorrderived { continue; } - auto poolA = V0s.sliceBy(tracksPerCollisionV0, collision1.index()); + // auto poolA = V0s.sliceBy(tracksPerCollisionV0, collision1.index()); + auto poolA = V0s.sliceBy(tracksPerCollisionV0, collision1.globalIndex()); // if pool empty, push and continue if (eventPools[bin].empty()) { @@ -1007,7 +1008,8 @@ struct lambdaspincorrderived { } // push current event into pool - auto sliced = V0s.sliceBy(tracksPerCollisionV0, collision1.index()); + // auto sliced = V0s.sliceBy(tracksPerCollisionV0, collision1.index()); + auto sliced = V0s.sliceBy(tracksPerCollisionV0, collision1.globalIndex()); eventPools[bin].emplace_back(collision1.index(), std::move(sliced)); if ((int)eventPools[bin].size() > nEvtMixing) { eventPools[bin].pop_front(); @@ -1457,7 +1459,8 @@ struct lambdaspincorrderived { continue; } - auto slice = V0s.sliceBy(tracksPerCollisionV0, col.index()); + // auto slice = V0s.sliceBy(tracksPerCollisionV0, col.index()); + auto slice = V0s.sliceBy(tracksPerCollisionV0, col.globalIndex()); for (auto const& t : slice) { if (!selectionV0(t)) { @@ -1660,7 +1663,8 @@ struct lambdaspincorrderived { } const int64_t curColIdx = static_cast(col1.index()); - auto poolA = V0s.sliceBy(tracksPerCollisionV0, col1.index()); + // auto poolA = V0s.sliceBy(tracksPerCollisionV0, col1.index()); + auto poolA = V0s.sliceBy(tracksPerCollisionV0, col1.globalIndex()); for (auto const& [t1, t2] : soa::combinations(o2::soa::CombinationsFullIndexPolicy(poolA, poolA))) { if (!selectionV0(t1) || !selectionV0(t2)) { @@ -1869,7 +1873,8 @@ struct lambdaspincorrderived { continue; } - auto slice = V0sMC.sliceBy(tracksPerCollisionV0mc, col.index()); + // auto slice = V0sMC.sliceBy(tracksPerCollisionV0mc, col.index()); + auto slice = V0sMC.sliceBy(tracksPerCollisionV0mc, col.globalIndex()); for (auto const& t : slice) { if (!selectionV0MC(t)) { @@ -2078,7 +2083,8 @@ struct lambdaspincorrderived { } const int64_t curColIdx = static_cast(col1.index()); - auto poolA = V0sMC.sliceBy(tracksPerCollisionV0mc, col1.index()); + // auto poolA = V0sMC.sliceBy(tracksPerCollisionV0mc, col1.index()); + auto poolA = V0sMC.sliceBy(tracksPerCollisionV0mc, col1.globalIndex()); for (auto const& [t1, t2] : soa::combinations(o2::soa::CombinationsFullIndexPolicy(poolA, poolA))) { if (!selectionV0MC(t1) || !selectionV0MC(t2)) { From 18d8d3de839709230115c1063f7a03abf1be4f6d Mon Sep 17 00:00:00 2001 From: skundu692 Date: Fri, 1 May 2026 16:07:49 +0200 Subject: [PATCH 2/8] Fix Lambda spin correlation selection --- .../Strangeness/lambdaspincorrderived.cxx | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/PWGLF/Tasks/Strangeness/lambdaspincorrderived.cxx b/PWGLF/Tasks/Strangeness/lambdaspincorrderived.cxx index 12fbd831149..012685039f0 100644 --- a/PWGLF/Tasks/Strangeness/lambdaspincorrderived.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaspincorrderived.cxx @@ -856,8 +856,7 @@ struct lambdaspincorrderived { continue; } - // auto poolA = V0s.sliceBy(tracksPerCollisionV0, collision1.index()); - auto poolA = V0s.sliceBy(tracksPerCollisionV0, collision1.globalIndex()); + auto poolA = V0s.sliceBy(tracksPerCollisionV0, collision1.index()); // if pool empty, push and continue if (eventPools[bin].empty()) { @@ -1008,8 +1007,7 @@ struct lambdaspincorrderived { } // push current event into pool - // auto sliced = V0s.sliceBy(tracksPerCollisionV0, collision1.index()); - auto sliced = V0s.sliceBy(tracksPerCollisionV0, collision1.globalIndex()); + auto sliced = V0s.sliceBy(tracksPerCollisionV0, collision1.index()); eventPools[bin].emplace_back(collision1.index(), std::move(sliced)); if ((int)eventPools[bin].size() > nEvtMixing) { eventPools[bin].pop_front(); @@ -1459,8 +1457,7 @@ struct lambdaspincorrderived { continue; } - // auto slice = V0s.sliceBy(tracksPerCollisionV0, col.index()); - auto slice = V0s.sliceBy(tracksPerCollisionV0, col.globalIndex()); + auto slice = V0s.sliceBy(tracksPerCollisionV0, col.index()); for (auto const& t : slice) { if (!selectionV0(t)) { @@ -1663,8 +1660,7 @@ struct lambdaspincorrderived { } const int64_t curColIdx = static_cast(col1.index()); - // auto poolA = V0s.sliceBy(tracksPerCollisionV0, col1.index()); - auto poolA = V0s.sliceBy(tracksPerCollisionV0, col1.globalIndex()); + auto poolA = V0s.sliceBy(tracksPerCollisionV0, col1.index()); for (auto const& [t1, t2] : soa::combinations(o2::soa::CombinationsFullIndexPolicy(poolA, poolA))) { if (!selectionV0(t1) || !selectionV0(t2)) { @@ -1873,8 +1869,7 @@ struct lambdaspincorrderived { continue; } - // auto slice = V0sMC.sliceBy(tracksPerCollisionV0mc, col.index()); - auto slice = V0sMC.sliceBy(tracksPerCollisionV0mc, col.globalIndex()); + auto slice = V0sMC.sliceBy(tracksPerCollisionV0mc, col.index()); for (auto const& t : slice) { if (!selectionV0MC(t)) { @@ -2083,8 +2078,7 @@ struct lambdaspincorrderived { } const int64_t curColIdx = static_cast(col1.index()); - // auto poolA = V0sMC.sliceBy(tracksPerCollisionV0mc, col1.index()); - auto poolA = V0sMC.sliceBy(tracksPerCollisionV0mc, col1.globalIndex()); + auto poolA = V0sMC.sliceBy(tracksPerCollisionV0mc, col1.index()); for (auto const& [t1, t2] : soa::combinations(o2::soa::CombinationsFullIndexPolicy(poolA, poolA))) { if (!selectionV0MC(t1) || !selectionV0MC(t2)) { From 8e5c01af42847fa792c0555c39b3307448d657aa Mon Sep 17 00:00:00 2001 From: skundu692 Date: Tue, 12 May 2026 16:32:53 +0200 Subject: [PATCH 3/8] Fix event selection in MC table for spin correlation and add new process function for high mass resonance ananlysis --- PWGLF/Tasks/Resonances/doublephimeson.cxx | 1 + 1 file changed, 1 insertion(+) diff --git a/PWGLF/Tasks/Resonances/doublephimeson.cxx b/PWGLF/Tasks/Resonances/doublephimeson.cxx index 61b181646cf..f5c7f353be1 100644 --- a/PWGLF/Tasks/Resonances/doublephimeson.cxx +++ b/PWGLF/Tasks/Resonances/doublephimeson.cxx @@ -16,6 +16,7 @@ #include "PWGLF/DataModel/ReducedDoublePhiTables.h" #include + #include #include #include From 8c5f0fdbd907e80c75e612d29aa43034f39a5330 Mon Sep 17 00:00:00 2001 From: skundu692 Date: Wed, 13 May 2026 12:16:26 +0200 Subject: [PATCH 4/8] Fix bug in v0 loop for MC --- PWGLF/TableProducer/Strangeness/lambdaspincorrelation.cxx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/PWGLF/TableProducer/Strangeness/lambdaspincorrelation.cxx b/PWGLF/TableProducer/Strangeness/lambdaspincorrelation.cxx index 33561ee14cd..b557053295c 100644 --- a/PWGLF/TableProducer/Strangeness/lambdaspincorrelation.cxx +++ b/PWGLF/TableProducer/Strangeness/lambdaspincorrelation.cxx @@ -283,6 +283,7 @@ struct lambdaspincorrelation { using EventCandidates = soa::Filtered>; using AllTrackCandidates = soa::Join; using ResoV0s = aod::V0Datas; + Preslice perCollisionV0s = aod::v0data::collisionId; using EventCandidatesMC = soa::Join; using AllTrackCandidatesMC = soa::Join; void processData(EventCandidates::iterator const& collision, AllTrackCandidates const&, ResoV0s const& V0s) @@ -555,8 +556,9 @@ struct lambdaspincorrelation { occupancy < cfgCutOccupancy) { histos.fill(HIST("hEvtSelInfo"), 2.5); + auto groupedV0s = V0s.sliceBy(perCollisionV0s, collision.globalIndex()); - for (const auto& v0 : V0s) { + for (const auto& v0 : groupedV0s) { histos.fill(HIST("hEvtSelInfo"), 3.5); // all V0s seen auto [lambdaTag, aLambdaTag, isValid] = getLambdaTagsMC(v0, collision); From 48c56904fb633e0bdea542e6019d4b117295e920 Mon Sep 17 00:00:00 2001 From: skundu692 Date: Wed, 13 May 2026 12:23:50 +0200 Subject: [PATCH 5/8] Fix clang-format --- PWGLF/Tasks/Resonances/doublephimeson.cxx | 1 - 1 file changed, 1 deletion(-) diff --git a/PWGLF/Tasks/Resonances/doublephimeson.cxx b/PWGLF/Tasks/Resonances/doublephimeson.cxx index f5c7f353be1..61b181646cf 100644 --- a/PWGLF/Tasks/Resonances/doublephimeson.cxx +++ b/PWGLF/Tasks/Resonances/doublephimeson.cxx @@ -16,7 +16,6 @@ #include "PWGLF/DataModel/ReducedDoublePhiTables.h" #include - #include #include #include From 5070dc4f3d517291fad80dac8a39733f1c67c6c2 Mon Sep 17 00:00:00 2001 From: skundu692 Date: Sun, 14 Jun 2026 10:22:49 +0200 Subject: [PATCH 6/8] PWGLF: add pT-dependent double-phi mass parameters and CCDB residual weights for Lambda spin correlations --- PWGLF/Tasks/Resonances/doublephimeson.cxx | 610 +++++++++++- .../Strangeness/lambdaspincorrderived.cxx | 931 ++++++++++++++---- 2 files changed, 1305 insertions(+), 236 deletions(-) diff --git a/PWGLF/Tasks/Resonances/doublephimeson.cxx b/PWGLF/Tasks/Resonances/doublephimeson.cxx index 61b181646cf..178fe725b95 100644 --- a/PWGLF/Tasks/Resonances/doublephimeson.cxx +++ b/PWGLF/Tasks/Resonances/doublephimeson.cxx @@ -61,6 +61,7 @@ struct doublephimeson { Configurable maxPhiPt{"maxPhiPt", 100, "Maximum phi Pt"}; Configurable minPhiMass2{"minPhiMass2", 1.01, "Minimum phi mass2"}; Configurable maxPhiMass2{"maxPhiMass2", 1.03, "Maximum phi mass2"}; + Configurable minExoticPt{"minExoticPt", 6.0, "Minimum Exotic Pt"}; Configurable minExoticMass{"minExoticMass", 2.0, "Minimum Exotic mass"}; Configurable maxExoticMass{"maxExoticMass", 3.6, "Maximum Exotic mass"}; Configurable additionalEvsel{"additionalEvsel", false, "Additional event selection"}; @@ -70,6 +71,72 @@ struct doublephimeson { Configurable cutNsigmaTOF{"cutNsigmaTOF", 3.0, "nsigma cut TOF"}; Configurable momTOFCut{"momTOFCut", 1.8, "minimum pT cut for madnatory TOF"}; Configurable maxKaonPt{"maxKaonPt", 100.0, "maximum kaon pt cut"}; + + // ------------------------------------------------------------ + // pT-dependent phi mass peak and width from single-phi BW fits + // + // DeltaM = sqrt( ((m1 - mean(pt1))/width(pt1))^2 + // + ((m2 - mean(pt2))/width(pt2))^2 ) + // + // Units: + // pT : GeV/c + // mean : GeV/c^2 + // width : GeV/c^2 + // + // 35 pT-bin edges -> 34 mean/width values. + // ------------------------------------------------------------ + + Configurable> cfgPhiPtBins{ + "cfgPhiPtBins", + std::vector{ + 0.4, 0.5, 0.6, 0.8, 0.9, + 1.0, 1.1, 1.2, 1.4, 1.6, + 1.8, 2.0, 2.2, 2.4, 2.6, + 2.8, 3.0, 3.5, 4.0, 4.5, + 5.0, 6.0, 7.0, 8.0, 9.0, + 10.0, 12.0, 14.0, 16.0, 18.0, + 20.0, 25.0, 30.0, 40.0, 100.0}, + "pT bin edges for phi mass peak and width calibration"}; + + Configurable> cfgPhiMeanVsPt{ + "cfgPhiMeanVsPt", + std::vector{ + 1.01779, 1.01826, 1.01897, 1.01924, 1.01934, + 1.01938, 1.01942, 1.01945, 1.01946, 1.01947, + 1.01953, 1.01960, 1.01965, 1.01967, 1.01971, + 1.01972, 1.01974, 1.01977, 1.01980, 1.01983, + 1.01987, 1.01988, 1.01989, 1.01990, 1.01992, + 1.01992, 1.01990, 1.01990, 1.01990, 1.01990, + 1.01990, 1.01990, 1.01990, 1.01990}, + "phi mass peak vs pT from single-phi Breit-Wigner fit"}; + + Configurable> cfgPhiSigmaVsPt{ + "cfgPhiSigmaVsPt", + std::vector{ + 0.00507421, 0.00554344, 0.00554447, 0.00562718, 0.00548766, + 0.00541166, 0.00539718, 0.00539168, 0.00532416, 0.00534485, + 0.00547586, 0.00567904, 0.00579755, 0.00583511, 0.00587245, + 0.00600147, 0.00600020, 0.00610160, 0.00628558, 0.00646913, + 0.00678283, 0.00720555, 0.00753636, 0.00784207, 0.00814550, + 0.00854121, 0.00890474, 0.00981538, 0.01011060, 0.01077820, + 0.01112900, 0.01203120, 0.01372720, 0.02443550}, + "phi Breit-Wigner width vs pT from single-phi fit"}; + + Configurable cfgDefaultPhiMean{ + "cfgDefaultPhiMean", + 1.019461f, + "default phi mass peak if pT is outside calibration range"}; + + Configurable cfgDefaultPhiSigma{ + "cfgDefaultPhiSigma", + 0.0055f, + "default phi width if pT is outside calibration range"}; + + Configurable cfgMinAllowedPhiSigma{ + "cfgMinAllowedPhiSigma", + 1.0e-6f, + "protection against zero or negative phi width"}; + // Event Mixing Configurable nEvtMixing{"nEvtMixing", 1, "Number of events to mix"}; ConfigurableAxis CfgVtxBins{"CfgVtxBins", {10, -10, 10}, "Mixing bins - z-vertex"}; @@ -77,13 +144,15 @@ struct doublephimeson { // THnsparse bining ConfigurableAxis configThnAxisPtCorr{"configThnAxisPtCorr", {1000, 0.0, 100}, "#it{M} (GeV/#it{c}^{2})"}; - ConfigurableAxis configThnAxisInvMass{"configThnAxisInvMass", {1500, 2.0, 3.5}, "#it{M} (GeV/#it{c}^{2})"}; + ConfigurableAxis configThnAxisInvMass{"configThnAxisInvMass", {300, 2.4, 3.0}, "#it{M} (GeV/#it{c}^{2})"}; ConfigurableAxis configThnAxisInvMassPhi{"configThnAxisInvMassPhi", {20, 1.01, 1.03}, "#it{M} (GeV/#it{c}^{2})"}; ConfigurableAxis configThnAxisInvMassDeltaPhi{"configThnAxisInvMassDeltaPhi", {80, 0.0, 0.08}, "#it{M} (GeV/#it{c}^{2})"}; + ConfigurableAxis configThnAxisInvMassDeltaPhiSigma{"configThnAxisInvMassDeltaPhiSigma", {100, 0.0, 10}, "#it{M} (GeV/#it{c}^{2}) sigma"}; ConfigurableAxis configThnAxisDaugherPt{"configThnAxisDaugherPt", {25, 0.0, 50.}, "#it{p}_{T} (GeV/#it{c})"}; ConfigurableAxis configThnAxisPt{"configThnAxisPt", {40, 0.0, 20.}, "#it{p}_{T} (GeV/#it{c})"}; + ConfigurableAxis configThnAxisDaughterPt{"configThnAxisDaughterPt", {100, 0.0, 100.}, "daughter #it{p}_{T} (GeV/#it{c})"}; ConfigurableAxis configThnAxisKstar{"configThnAxisKstar", {200, 0.0, 2.0}, "#it{k}^{*} (GeV/#it{c})"}; - ConfigurableAxis configThnAxisDeltaR{"configThnAxisDeltaR", {200, 0.0, 2.0}, "#it{k}^{*} (GeV/#it{c})"}; + ConfigurableAxis configThnAxisDeltaR{"configThnAxisDeltaR", {VARIABLE_WIDTH, 0.0, 0.0001, 0.0003, 0.0005, 0.0007, 0.001, 0.002, 0.003, 0.004, 0.005, 0.006, 0.007, 0.008, 0.009, 0.01, 0.02, 0.03, 0.04, 0.05, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 2.0, 10.0}, "#it{k}^{*} (GeV/#it{c})"}; ConfigurableAxis configThnAxisCosTheta{"configThnAxisCosTheta", {160, 0.0, 3.2}, "cos #theta{*}"}; ConfigurableAxis configThnAxisNumPhi{"configThnAxisNumPhi", {101, -0.5, 100.5}, "cos #theta{*}"}; ConfigurableAxis configThnAxisDeltaPt{"configThnAxisDeltaPt", {100, 0.0, 1.0}, "delta pt"}; @@ -103,7 +172,9 @@ struct doublephimeson { histos.add("hnsigmaTPCKaonPlus", "hnsigmaTPCKaonPlus", kTH2F, {{1000, -3.0, 3.0f}, {100, 0.0f, 10.0f}}); histos.add("hnsigmaTPCKaonMinus", "hnsigmaTPCKaonMinus", kTH2F, {{1000, -3.0, 3.0f}, {100, 0.0f, 10.0f}}); histos.add("hnsigmaTPCTOFKaon", "hnsigmaTPCTOFKaon", kTH3F, {{500, -3.0, 3.0f}, {500, -3.0, 3.0f}, {100, 0.0f, 10.0f}}); - histos.add("hPhiMass", "hPhiMass", kTH3F, {{40, 1.0, 1.04f}, {40, 1.0, 1.04f}, {100, 0.0f, 10.0f}}); + histos.add("hPhiMassVsPt", "hPhiMassVsPt", kTH2F, {{40, 1.0, 1.04f}, {1000, 0.0f, 100.0f}}); + histos.add("hPhiMass", "hPhiMass", kTH3F, {{40, 1.0, 1.04f}, {40, 1.0, 1.04f}, {250, 0.0f, 100.0f}}); + histos.add("hPhiMassNormalized", "hPhiMassNormalized", kTH3F, {{100, -10.0, 10.0f}, {100, -10.0, 10.0f}, {250, 0.0f, 100.0f}}); histos.add("hPhiMass2", "hPhiMass2", kTH2F, {{40, 1.0, 1.04f}, {40, 1.0f, 1.04f}}); histos.add("hkPlusDeltaetaDeltaPhi", "hkPlusDeltaetaDeltaPhi", kTH2F, {{400, -2.0, 2.0}, {640, -2.0 * TMath::Pi(), 2.0 * TMath::Pi()}}); histos.add("hkMinusDeltaetaDeltaPhi", "hkMinusDeltaetaDeltaPhi", kTH2F, {{400, -2.0, 2.0}, {640, -2.0 * TMath::Pi(), 2.0 * TMath::Pi()}}); @@ -111,10 +182,12 @@ struct doublephimeson { histos.add("hDeltaRkaonminus", "hDeltaRkaonminus", kTH1F, {{800, 0.0, 8.0}}); histos.add("hPtCorrelation", "hPtCorrelation", kTH2F, {{400, 0.0, 40.0}, {5000, 0.0, 100.0}}); const AxisSpec thnAxisdeltapt{configThnAxisDeltaPt, "Delta pt"}; + const AxisSpec thnAxisdaughterpt{configThnAxisDaughterPt, "Daughter pt"}; const AxisSpec thnAxisInvMass{configThnAxisInvMass, "#it{M} (GeV/#it{c}^{2})"}; const AxisSpec thnAxisPt{configThnAxisPt, "#it{p}_{T} (GeV/#it{c})"}; const AxisSpec thnAxisInvMassPhi{configThnAxisInvMassPhi, "#it{M} (GeV/#it{c}^{2})"}; const AxisSpec thnAxisInvMassDeltaPhi{configThnAxisInvMassDeltaPhi, "#it{M} (GeV/#it{c}^{2})"}; + const AxisSpec thnAxisInvMassDeltaPhiSigma{configThnAxisInvMassDeltaPhiSigma, "#it{M} (GeV/#it{c}^{2}) sigma"}; const AxisSpec thnAxisDeltaR{configThnAxisDeltaR, "#Delta R)"}; const AxisSpec thnAxisCosTheta{configThnAxisCosTheta, "cos #theta"}; const AxisSpec thnAxisNumPhi{configThnAxisNumPhi, "Number of phi meson"}; @@ -129,14 +202,27 @@ struct doublephimeson { histos.add("SEMassUnlike_DeltaRZA", "SEMassUnlike_DeltaRZA", HistType::kTHnSparseF, {thnAxisInvMass, thnAxisPt, thnAxisDeltaRPhi, thnAxisZ, thnAxisA, thnAxisInvMassDeltaPhi}); histos.add("MEMassUnlike_DeltaRZA", "MEMassUnlike_DeltaRZA", HistType::kTHnSparseF, {thnAxisInvMass, thnAxisPt, thnAxisDeltaRPhi, thnAxisZ, thnAxisA, thnAxisInvMassDeltaPhi}); histos.add("SEMassUnlike_AllVars", "SEMassUnlike_AllVars", HistType::kTHnSparseF, + {thnAxisInvMass, // M(phi-phi) + thnAxisPt, // pT(phi-phi) + thnAxisDeltaRPhi, // DeltaR(phi,phi) + thnAxisDeltaR, // min DeltaR(K,K) + thnAxisInvMassPhi, + thnAxisInvMassPhi, + thnAxisInvMassDeltaPhi, + thnAxisInvMassDeltaPhiSigma, + thnAxisPtCorr, + thnAxisNumPhi}); // pT correlation variable + + histos.add("MEMassUnlike_AllVars", "MEMassUnlike_AllVars", HistType::kTHnSparseF, {thnAxisInvMass, // M(phi-phi) thnAxisPt, // pT(phi-phi) thnAxisDeltaRPhi, // DeltaR(phi,phi) thnAxisDeltaR, // min DeltaR(K,K) - thnAxisZ, // z = pT1 / (pT1 + pT2) - thnAxisA, // A = |pT1 - pT2| / (pT1 + pT2) - thnAxisInvMassDeltaPhi, // DeltaM(phi1,phi2) - thnAxisPtCorr}); // pT correlation variable + thnAxisInvMassPhi, // m(phi1) + thnAxisInvMassPhi, // m(phi2) + thnAxisInvMassDeltaPhi, // DeltaM_phi + thnAxisPtCorr, + thnAxisNumPhi}); // pT correlation variable } // get kstar @@ -238,7 +324,10 @@ struct doublephimeson { } if (PIDStrategy == 1003) { - if (ptcand < 0.5 && nsigmaTPC > -2.0 && nsigmaTPC < 3.0) { + if (ptcand < 0.5 && TOFHit != 1 && nsigmaTPC > -2.0 && nsigmaTPC < 2.0) { + return true; + } + if (ptcand < 0.5 && TOFHit == 1 && std::sqrt(nsigmaTOF * nsigmaTOF + nsigmaTPC * nsigmaTPC) < 2.5) { return true; } if (ptcand >= 0.5) { @@ -251,6 +340,32 @@ struct doublephimeson { } } + if (PIDStrategy == 1008) { + if (ptcand < 0.5 && TOFHit != 1 && nsigmaTPC > -2.0 && nsigmaTPC < 2.0) { + return true; + } + if (ptcand < 0.5 && TOFHit == 1 && std::sqrt(nsigmaTOF * nsigmaTOF + nsigmaTPC * nsigmaTPC) < 2.5) { + return true; + } + if (ptcand >= 0.5) { + if (TOFHit != 1 && ptcand >= 0.5 && ptcand < 0.6 && nsigmaTPC > -1.5 && nsigmaTPC < 2.0) { + return true; + } + if (TOFHit != 1 && ptcand >= 0.6 && ptcand < 0.7 && nsigmaTPC > -1.0 && nsigmaTPC < 2.0) { + return true; + } + if (TOFHit != 1 && ptcand >= 0.7 && ptcand < 1.0 && nsigmaTPC > 0.0 && nsigmaTPC < 2.0) { + return true; + } + if (TOFHit != 1 && ptcand >= 1.0 && nsigmaTPC > -2.0 && nsigmaTPC < 2.0) { + return true; + } + if (TOFHit == 1 && std::sqrt(nsigmaTOF * nsigmaTOF + nsigmaTPC * nsigmaTPC) < 2.5) { + return true; + } + } + } + if (PIDStrategy == 1005) { // low pT: TPC-only if (ptcand < 0.5 && nsigmaTPC > -2.0 && nsigmaTPC < 3.0) { @@ -304,6 +419,35 @@ struct doublephimeson { } } + if (PIDStrategy == 1007) { + + const bool hasTOF = (TOFHit == 1); + const bool passTPC_low = (nsigmaTPC > -2.0 && nsigmaTPC < 3.0); + const bool passTPC_midNoTOF = (nsigmaTPC > -1.5 && nsigmaTPC < 3.0); + const bool passTPC_highNoTOF = (nsigmaTPC > -2.0 && nsigmaTPC < 2.0); + + const bool passTPC_TOF = + hasTOF && std::sqrt(nsigmaTOF * nsigmaTOF + nsigmaTPC * nsigmaTPC) < 2.5; + + if (ptcand < 0.5) { + if (passTPC_low) + return true; + } else if (ptcand < 0.7) { + if (hasTOF && passTPC_TOF) + return true; + if (!hasTOF && passTPC_midNoTOF) + return true; + } else if (ptcand < 1.1) { + if (passTPC_TOF) + return true; + } else { + if (hasTOF && passTPC_TOF) + return true; + if (!hasTOF && passTPC_highNoTOF) + return true; + } + } + if (PIDStrategy == 100) { if (ptcand < 1.2) { if (TOFHit == 1 && std::sqrt(nsigmaTOF * nsigmaTOF + nsigmaTPC * nsigmaTPC) < 2.5) { @@ -517,6 +661,91 @@ struct doublephimeson { TLorentzVector Phi1kaonplus, Phi1kaonminus, Phi2kaonplus, Phi2kaonminus; // TLorentzVector exoticRot, Phid1Rot; + int getPhiPtCalibBin(float pt) + { + const auto& bins = cfgPhiPtBins.value; + + if (bins.size() < 2) { + return -1; + } + + if (pt < bins.front()) { + return -1; + } + + for (size_t i = 0; i + 1 < bins.size(); ++i) { + if (pt >= bins[i] && pt < bins[i + 1]) { + return static_cast(i); + } + } + + if (pt >= bins.back()) { + return static_cast(bins.size()) - 2; + } + + return -1; + } + + float getPhiMassPeakVsPt(float pt) + { + const int ibin = getPhiPtCalibBin(pt); + const auto& means = cfgPhiMeanVsPt.value; + + if (ibin >= 0 && ibin < static_cast(means.size())) { + return means[ibin]; + } + + return cfgDefaultPhiMean.value; + } + + float getPhiWidthVsPt(float pt) + { + const int ibin = getPhiPtCalibBin(pt); + const auto& widths = cfgPhiSigmaVsPt.value; + + float width = cfgDefaultPhiSigma.value; + + if (ibin >= 0 && ibin < static_cast(widths.size())) { + width = widths[ibin]; + } + + if (width < cfgMinAllowedPhiSigma.value) { + width = cfgDefaultPhiSigma.value; + } + + return width; + } + + float getNormalizedDeltaMPhi(float m1, float pt1, float m2, float pt2) + { + const float mean1 = getPhiMassPeakVsPt(pt1); + const float mean2 = getPhiMassPeakVsPt(pt2); + + const float width1 = getPhiWidthVsPt(pt1); + const float width2 = getPhiWidthVsPt(pt2); + + return TMath::Sqrt( + TMath::Power((m1 - mean1) / width1, 2.0) + + TMath::Power((m2 - mean2) / width2, 2.0)); + } + + float getDeltaMPhi(float m1, float pt1, float m2, float pt2) + { + const float mean1 = getPhiMassPeakVsPt(pt1); + const float mean2 = getPhiMassPeakVsPt(pt2); + + return TMath::Sqrt( + TMath::Power((m1 - mean1), 2.0) + + TMath::Power((m2 - mean2), 2.0)); + } + + float getNormalizedMPhi(float m1, float pt1) + { + const float mean1 = getPhiMassPeakVsPt(pt1); + const float width1 = getPhiWidthVsPt(pt1); + return ((m1 - mean1) / width1); + } + void processSE(aod::RedPhiEvents::iterator const& collision, aod::PhiTracks const& phitracks) { if (additionalEvsel && (collision.numPos() < 2 || collision.numNeg() < 2)) { @@ -1231,9 +1460,8 @@ struct doublephimeson { const double minDR = minDRV[i]; // (optional but recommended) protect ptcorr from blow-ups - const double denom = (pair.Pt() - p1.Pt()); - if (std::abs(denom) < 1e-9) - continue; + const double denom = std::abs(pair.Pt() - p1.Pt()); + const double ptcorr = p1.Pt() / denom; histos.fill(HIST("hPtCorrelation"), pair.Pt(), ptcorr); @@ -1269,6 +1497,7 @@ struct doublephimeson { // --- phi multiplicity with PID --- int phimult = 0; + int nBestPairingRejected = 0; for (auto const& t : phitracks) { const double kpluspt = std::hypot(t.phid1Px(), t.phid1Py()); const double kminuspt = std::hypot(t.phid2Px(), t.phid2Py()); @@ -1280,6 +1509,11 @@ struct doublephimeson { if (t.phiMass() < minPhiMass1 || t.phiMass() > maxPhiMass1) { continue; } + TLorentzVector phi1; + phi1.SetXYZM(t.phiPx(), t.phiPy(), t.phiPz(), t.phiMass()); + if (phi1.Pt() < minPhiPt || phi1.Pt() > maxPhiPt) { + continue; + } if (kpluspt > maxKaonPt || kminuspt > maxKaonPt) { continue; } @@ -1293,6 +1527,7 @@ struct doublephimeson { histos.fill(HIST("hnsigmaTPCTOFKaon"), t.phid1TPC(), t.phid1TOF(), kpluspt); histos.fill(HIST("hnsigmaTPCKaonPlus"), t.phid1TPC(), kpluspt); histos.fill(HIST("hnsigmaTPCKaonMinus"), t.phid2TPC(), kminuspt); + histos.fill(HIST("hPhiMassVsPt"), t.phiMass(), phi1.Pt()); ++phimult; } @@ -1304,11 +1539,12 @@ struct doublephimeson { constexpr double mPhiPDG = o2::constants::physics::MassPhi; // GeV/c^2 constexpr double mKPDG = o2::constants::physics::MassKPlus; // GeV/c^2 - const auto deltaMPhi = [=](double m1, double m2) { + const auto deltaMPhiNominal = [=](double m1, double m2) { const double d1 = m1 - mPhiPDG; const double d2 = m2 - mPhiPDG; return std::sqrt(d1 * d1 + d2 * d2); }; + const auto deltaR = [](double phi1, double eta1, double phi2, double eta2) { const double dphi = std::abs(TVector2::Phi_mpi_pi(phi1 - phi2)); const double deta = eta1 - eta2; @@ -1389,14 +1625,6 @@ struct doublephimeson { continue; } - // reject any shared daughter between the two phi candidates - if (t1.phid1Index() == t2.phid1Index() || - t1.phid1Index() == t2.phid2Index() || - t1.phid2Index() == t2.phid1Index() || - t1.phid2Index() == t2.phid2Index()) { - continue; - } - TLorentzVector phi2; TLorentzVector k2p; TLorentzVector k2m; @@ -1412,17 +1640,46 @@ struct doublephimeson { continue; } - const double dM = deltaMPhi(phi1.M(), phi2.M()); - if (dM > maxDeltaMPhi) { + TLorentzVector pair = phi1 + phi2; + if (pair.Pt() < minExoticPt) { continue; } - - TLorentzVector pair = phi1 + phi2; if (pair.M() < minExoticMass || pair.M() > maxExoticMass) { continue; } + // reject any shared daughter between the two phi candidates + if (t1.phid1Index() == t2.phid1Index() || + t1.phid1Index() == t2.phid2Index() || + t1.phid2Index() == t2.phid1Index() || + t1.phid2Index() == t2.phid2Index()) { + // LOGF(info,"track share",t1.phid1Index(),t1.phid2Index(),t2.phid1Index(),t2.phid2Index()); + continue; + } + const double dMNominal = deltaMPhiNominal(phi1.M(), phi2.M()); + const double mCross12 = (k1p + k2m).M(); // K+ from phi1 + K- from phi2 + const double mCross21 = (k2p + k1m).M(); // K+ from phi2 + K- from phi1 + const double dMCross = deltaMPhiNominal(mCross12, mCross21); + // Reject this candidate only if the crossed assignment is more phi-like + if (dMCross > 1.01 && dMCross < 1.03) { + ++nBestPairingRejected; + /* + LOGF(info, + "Best-pairing rejected: dMNominal = %.6f, dMCross = %.6f, " + "mPhi1 = %.6f, mPhi2 = %.6f, mCross12 = %.6f, mCross21 = %.6f", + dMNominal, + dMCross, + phi1.M(), + phi2.M(), + mCross12, + mCross21, + pair.Pt(), + pair.M()); + */ + continue; + } histos.fill(HIST("hPhiMass"), phi1.M(), phi2.M(), pair.Pt()); + histos.fill(HIST("hPhiMassNormalized"), getNormalizedMPhi(phi1.M(), phi1.Pt()), getNormalizedMPhi(phi2.M(), phi2.Pt()), pair.Pt()); ROOT::Math::PtEtaPhiMVector k1pV(k1p.Pt(), k1p.Eta(), k1p.Phi(), mKPDG); ROOT::Math::PtEtaPhiMVector k1mV(k1m.Pt(), k1m.Eta(), k1m.Phi(), mKPDG); @@ -1455,12 +1712,9 @@ struct doublephimeson { const double pairPt = pair.Pt(); const double dRphi = deltaR(p1.Phi(), p1.Eta(), p2.Phi(), p2.Eta()); const double minDR = minDRV[i]; - const double dM = deltaMPhi(p1.M(), p2.M()); - - const double denom = pairPt - p1.Pt(); - if (std::abs(denom) < 1e-9) { - continue; - } + const double dMNominal = getDeltaMPhi(p1.M(), p1.Pt(), p2.M(), p2.Pt()); + const double dMNominalNsigma = getNormalizedDeltaMPhi(p1.M(), p1.Pt(), p2.M(), p2.Pt()); + const double denom = std::abs(pairPt - p1.Pt()); const double ptcorr = p1.Pt() / denom; @@ -1470,29 +1724,20 @@ struct doublephimeson { if (ptsum <= 0.0) { continue; } - - const double z = pt1 / ptsum; - const double A = std::abs(pt1 - pt2) / ptsum; - - histos.fill(HIST("hPtCorrelation"), pairPt, ptcorr); - - histos.fill(HIST("SEMassUnlike"), - M, - minDR, - pairPt, - dRphi, - dM, - ptcorr); - - histos.fill(HIST("SEMassUnlike_AllVars"), - M, - pairPt, - dRphi, - minDR, - z, - A, - dM, - ptcorr); + if (pairPt > minExoticPt) { + histos.fill(HIST("hPtCorrelation"), pairPt, ptcorr); + histos.fill(HIST("SEMassUnlike_AllVars"), + M, + pairPt, + dRphi, + minDR, + p1.M(), + p2.M(), + dMNominal, + dMNominalNsigma, + ptcorr, + pairV.size()); + } } } PROCESS_SWITCH(doublephimeson, processopti5, "Process Optimized same event with all variables", true); @@ -1500,6 +1745,265 @@ struct doublephimeson { SliceCache cache; using BinningTypeVertexContributor = ColumnBinningPolicy; + void processMixedEventopti5(aod::RedPhiEvents& collisions, aod::PhiTracks& phitracks) + { + auto tracksTuple = std::make_tuple(phitracks); + + BinningTypeVertexContributor binningOnPositions{{CfgVtxBins, CfgMultBins}, true}; + + SameKindPair pair{ + binningOnPositions, nEvtMixing, -1, collisions, tracksTuple, &cache}; + + constexpr double mKPDG = o2::constants::physics::MassKPlus; // GeV/c^2 + + const auto deltaR = [](double phi1, double eta1, double phi2, double eta2) { + const double dphi = std::abs(TVector2::Phi_mpi_pi(phi1 - phi2)); + const double deta = eta1 - eta2; + return std::sqrt(dphi * dphi + deta * deta); + }; + + const auto minKaonDeltaR = + [&](const ROOT::Math::PtEtaPhiMVector& kplus1, + const ROOT::Math::PtEtaPhiMVector& kminus1, + const ROOT::Math::PtEtaPhiMVector& kplus2, + const ROOT::Math::PtEtaPhiMVector& kminus2) { + const double dRkplus = + deltaR(kplus1.Phi(), kplus1.Eta(), kplus2.Phi(), kplus2.Eta()); + + const double dRkminus = + deltaR(kminus1.Phi(), kminus1.Eta(), kminus2.Phi(), kminus2.Eta()); + + histos.fill(HIST("hDeltaRkaonplus"), dRkplus); + histos.fill(HIST("hDeltaRkaonminus"), dRkminus); + + double minDR = dRkplus; + minDR = std::min(minDR, dRkminus); + + minDR = std::min(minDR, + deltaR(kplus1.Phi(), kplus1.Eta(), + kminus1.Phi(), kminus1.Eta())); + + minDR = std::min(minDR, + deltaR(kplus1.Phi(), kplus1.Eta(), + kminus2.Phi(), kminus2.Eta())); + + minDR = std::min(minDR, + deltaR(kplus2.Phi(), kplus2.Eta(), + kminus1.Phi(), kminus1.Eta())); + + minDR = std::min(minDR, + deltaR(kplus2.Phi(), kplus2.Eta(), + kminus2.Phi(), kminus2.Eta())); + + return minDR; + }; + + struct PhiCand { + ROOT::Math::PtEtaPhiMVector phi; + ROOT::Math::PtEtaPhiMVector kplus; + ROOT::Math::PtEtaPhiMVector kminus; + }; + + for (auto& [collision1, tracks1, collision2, tracks2] : pair) { + + if (collision1.index() == collision2.index()) { + continue; + } + + if (additionalEvsel) { + if (collision1.numPos() < 2 || collision1.numNeg() < 2) { + continue; + } + if (collision2.numPos() < 2 || collision2.numNeg() < 2) { + continue; + } + } + + std::vector cands1; + std::vector cands2; + + // ====================================================== + // Build phi candidates from event 1 + // ====================================================== + for (auto const& t1 : tracks1) { + const double kplus1pt = std::hypot(t1.phid1Px(), t1.phid1Py()); + const double kminus1pt = std::hypot(t1.phid2Px(), t1.phid2Py()); + + if (kplus1pt > maxKaonPt || kminus1pt > maxKaonPt) { + continue; + } + + if (!selectionPID(t1.phid1TPC(), t1.phid1TOF(), t1.phid1TOFHit(), + strategyPID1, kplus1pt)) { + continue; + } + + if (!selectionPID(t1.phid2TPC(), t1.phid2TOF(), t1.phid2TOFHit(), + strategyPID2, kminus1pt)) { + continue; + } + + if (t1.phiMass() < minPhiMass1 || t1.phiMass() > maxPhiMass1) { + continue; + } + + TLorentzVector phi1; + TLorentzVector k1p; + TLorentzVector k1m; + + phi1.SetXYZM(t1.phiPx(), t1.phiPy(), t1.phiPz(), t1.phiMass()); + k1p.SetXYZM(t1.phid1Px(), t1.phid1Py(), t1.phid1Pz(), mKPDG); + k1m.SetXYZM(t1.phid2Px(), t1.phid2Py(), t1.phid2Pz(), mKPDG); + + if (phi1.Pt() < minPhiPt || phi1.Pt() > maxPhiPt) { + continue; + } + + PhiCand cand; + cand.phi = ROOT::Math::PtEtaPhiMVector(phi1.Pt(), phi1.Eta(), phi1.Phi(), phi1.M()); + cand.kplus = ROOT::Math::PtEtaPhiMVector(k1p.Pt(), k1p.Eta(), k1p.Phi(), mKPDG); + cand.kminus = ROOT::Math::PtEtaPhiMVector(k1m.Pt(), k1m.Eta(), k1m.Phi(), mKPDG); + + cands1.emplace_back(std::move(cand)); + } + + // ====================================================== + // Build phi candidates from event 2 + // ====================================================== + for (auto const& t2 : tracks2) { + const double kplus2pt = std::hypot(t2.phid1Px(), t2.phid1Py()); + const double kminus2pt = std::hypot(t2.phid2Px(), t2.phid2Py()); + + if (kplus2pt > maxKaonPt || kminus2pt > maxKaonPt) { + continue; + } + + if (!selectionPID(t2.phid1TPC(), t2.phid1TOF(), t2.phid1TOFHit(), + strategyPID1, kplus2pt)) { + continue; + } + + if (!selectionPID(t2.phid2TPC(), t2.phid2TOF(), t2.phid2TOFHit(), + strategyPID2, kminus2pt)) { + continue; + } + + if (t2.phiMass() < minPhiMass2 || t2.phiMass() > maxPhiMass2) { + continue; + } + + TLorentzVector phi2; + TLorentzVector k2p; + TLorentzVector k2m; + + phi2.SetXYZM(t2.phiPx(), t2.phiPy(), t2.phiPz(), t2.phiMass()); + k2p.SetXYZM(t2.phid1Px(), t2.phid1Py(), t2.phid1Pz(), mKPDG); + k2m.SetXYZM(t2.phid2Px(), t2.phid2Py(), t2.phid2Pz(), mKPDG); + + if (phi2.Pt() < minPhiPt || phi2.Pt() > maxPhiPt) { + continue; + } + + PhiCand cand; + cand.phi = ROOT::Math::PtEtaPhiMVector(phi2.Pt(), phi2.Eta(), phi2.Phi(), phi2.M()); + cand.kplus = ROOT::Math::PtEtaPhiMVector(k2p.Pt(), k2p.Eta(), k2p.Phi(), mKPDG); + cand.kminus = ROOT::Math::PtEtaPhiMVector(k2m.Pt(), k2m.Eta(), k2m.Phi(), mKPDG); + + cands2.emplace_back(std::move(cand)); + } + + if (cands1.empty() || cands2.empty()) { + continue; + } + + // ====================================================== + // Build mixed-event phi-phi pairs + // ====================================================== + for (auto const& c1 : cands1) { + + TLorentzVector phi1; + TLorentzVector k1p; + TLorentzVector k1m; + + phi1.SetPtEtaPhiM(c1.phi.Pt(), c1.phi.Eta(), c1.phi.Phi(), c1.phi.M()); + k1p.SetPtEtaPhiM(c1.kplus.Pt(), c1.kplus.Eta(), c1.kplus.Phi(), mKPDG); + k1m.SetPtEtaPhiM(c1.kminus.Pt(), c1.kminus.Eta(), c1.kminus.Phi(), mKPDG); + + for (auto const& c2 : cands2) { + + TLorentzVector phi2; + TLorentzVector k2p; + TLorentzVector k2m; + + phi2.SetPtEtaPhiM(c2.phi.Pt(), c2.phi.Eta(), c2.phi.Phi(), c2.phi.M()); + k2p.SetPtEtaPhiM(c2.kplus.Pt(), c2.kplus.Eta(), c2.kplus.Phi(), mKPDG); + k2m.SetPtEtaPhiM(c2.kminus.Pt(), c2.kminus.Eta(), c2.kminus.Phi(), mKPDG); + + const double dM = getDeltaMPhi(phi1.M(), phi1.Pt(), + phi2.M(), phi2.Pt()); + + TLorentzVector pairPhiPhi = phi1 + phi2; + + if (pairPhiPhi.Pt() < minExoticPt) { + continue; + } + + const double minDR = + minKaonDeltaR(c1.kplus, c1.kminus, c2.kplus, c2.kminus); + + const double dRphi = + deltaR(phi1.Phi(), phi1.Eta(), phi2.Phi(), phi2.Eta()); + + const double denom = std::abs(pairPhiPhi.Pt() - phi1.Pt()); + if (denom < 1e-9) { + continue; + } + + const double ptcorr = phi1.Pt() / denom; + + const double pt1 = phi1.Pt(); + const double pt2 = phi2.Pt(); + const double ptsum = pt1 + pt2; + + if (ptsum <= 0.0) { + continue; + } + + const double z = pt1 / ptsum; + const double A = std::abs(pt1 - pt2) / ptsum; + + histos.fill(HIST("MEMassUnlike"), + pairPhiPhi.M(), + minDR, + pairPhiPhi.Pt(), + dRphi, + dM, + ptcorr); + + histos.fill(HIST("MEMassUnlike_DeltaRZA"), + pairPhiPhi.M(), + pairPhiPhi.Pt(), + pairPhiPhi.Pt() * dRphi, + z, + A, + dM); + + histos.fill(HIST("MEMassUnlike_AllVars"), + pairPhiPhi.M(), + pairPhiPhi.Pt(), + dRphi, + minDR, + phi1.M(), + phi2.M(), + dM, + ptcorr); + } + } + } + } + PROCESS_SWITCH(doublephimeson, processMixedEventopti5, + "Process EventMixing for combinatorial background", false); + void processMixedEvent(aod::RedPhiEvents& collisions, aod::PhiTracks& phitracks) { auto tracksTuple = std::make_tuple(phitracks); diff --git a/PWGLF/Tasks/Strangeness/lambdaspincorrderived.cxx b/PWGLF/Tasks/Strangeness/lambdaspincorrderived.cxx index 012685039f0..954a1b11c13 100644 --- a/PWGLF/Tasks/Strangeness/lambdaspincorrderived.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaspincorrderived.cxx @@ -35,7 +35,7 @@ #include #include -#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include #include #include #include @@ -172,15 +172,33 @@ static inline int piIdx(const T& t) } } // namespace mcacc +// Optional fixed-leg correction pointers kept outside the task struct. +static TH3D* gFixedLLRep1 = nullptr; +static TH3D* gFixedULRep1 = nullptr; +static TH3D* gFixedALALRep1 = nullptr; + +static TH3D* gFixedLLRep2 = nullptr; +static TH3D* gFixedULRep2 = nullptr; +static TH3D* gFixedALALRep2 = nullptr; + struct lambdaspincorrderived { // BinningType colBinning; struct : ConfigurableGroup { Configurable cfgURL{"cfgURL", "http://alice-ccdb.cern.ch", "Address of the CCDB to browse"}; Configurable nolaterthan{"ccdb-no-later-than", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "Latest acceptable timestamp of creation for the object"}; + Configurable useFixedWeight{"useFixedWeight", false, "Use additional fixed-leg correction with useweight"}; + Configurable> ConfWeightPaths{ + "ConfWeightPaths", + std::vector{}, + "Replaced-leg CCDB paths in order: REP_LL_leg1,REP_UL_leg1,REP_ALAL_leg1,REP_LL_leg2,REP_UL_leg2,REP_ALAL_leg2"}; + Configurable> ConfFixedWeightPaths{ + "ConfFixedWeightPaths", + std::vector{}, + "Fixed-leg CCDB paths in order: FIX_LL_forRepLeg1,FIX_UL_forRepLeg1,FIX_ALAL_forRepLeg1,FIX_LL_forRepLeg2,FIX_UL_forRepLeg2,FIX_ALAL_forRepLeg2"}; } cfgCcdbParam; struct : ConfigurableGroup { - ConfigurableAxis cfgMixRadiusBins{"cfgMixRadiusBins", {VARIABLE_WIDTH, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 10.0, 12.0, 15.0, 20.0, 25.0, 30.0}, "Radius bins for V6 radius buffer"}; + ConfigurableAxis cfgMixRadiusBins{"cfgMixRadiusBins", {VARIABLE_WIDTH, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 10.0, 12.0, 15.0, 20.0, 25.0, 33.0}, "Radius bins for V6 radius buffer"}; } cfgMixRadiusParam; // Enable access to the CCDB for the offset and correction constants and save them in dedicated variables. @@ -200,14 +218,6 @@ struct lambdaspincorrderived { TH2D* hNUALambda = nullptr; TH2D* hNUAAntiLambda = nullptr; - Configurable ConfWeightPathLL{"ConfWeightPathLL", "Users/s/skundu/My/Object/spincorr/cent010LL", "Weight path"}; - Configurable ConfWeightPathALAL{"ConfWeightPathALAL", "Users/s/skundu/My/Object/spincorr/cent010LL", "Weight path"}; - Configurable ConfWeightPathLAL{"ConfWeightPathLAL", "Users/s/skundu/My/Object/spincorr/cent010LL", "Weight path"}; - Configurable ConfWeightPathALL{"ConfWeightPathALL", "Users/s/skundu/My/Object/spincorr/cent010LL", "Weight path"}; - Configurable ConfWeightPathLL2{"ConfWeightPathLL2", "Users/s/skundu/My/Object/spincorr/cent010LL", "Weight path 2"}; - Configurable ConfWeightPathALAL2{"ConfWeightPathALAL2", "Users/s/skundu/My/Object/spincorr/cent010LL", "Weight path 2"}; - Configurable ConfWeightPathLAL2{"ConfWeightPathLAL2", "Users/s/skundu/My/Object/spincorr/cent010LL", "Weight path 2"}; - Configurable ConfWeightPathALL2{"ConfWeightPathALL2", "Users/s/skundu/My/Object/spincorr/cent010LL", "Weight path 2"}; 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)"}; @@ -238,7 +248,17 @@ struct lambdaspincorrderived { Configurable harmonicDphi{"harmonicDphi", 2, "Harmonic delta phi"}; Configurable useweight{"useweight", 0, "Use weight"}; Configurable usePDGM{"usePDGM", 1, "Use PDG mass"}; - Configurable useAdditionalHisto{"useAdditionalHisto", 0, "Use additional histogram"}; + Configurable useAdditionalHisto{"useAdditionalHisto", 0, "Backward-compatible switch for extra Rap/Phi/PairMass THnSparse"}; + + // Output-control switches. Keep the final mass-mass-costheta sparse on by default, + // but allow heavy QA/additional sparses to be disabled in production. + Configurable fillBasicQAHistos{"fillBasicQAHistos", true, "Fill light QA histograms: pT-y, phi-eta, centrality, dphi, pt/eta vs cent"}; + Configurable fillReplacementQAHistos{"fillReplacementQAHistos", true, "Fill raw TGT/REP single-candidate replacement QA maps"}; + Configurable fillFixedLegQAHistos{"fillFixedLegQAHistos", false, "Fill fixed-leg TGT/SUC QA maps"}; + Configurable fillWeightQAHistos{"fillWeightQAHistos", false, "Fill weighted/final-weighted REP/FIX QA maps"}; + Configurable fillAnalysisSparses{"fillAnalysisSparses", false, "Fill extra deltaR/deltaRap/deltaPhi Analysis THnSparse objects"}; + Configurable fillAdditionalSparses{"fillAdditionalSparses", false, "Fill extra rapidity/dphi/pair-mass THnSparse objects"}; + Configurable checkDoubleStatus{"checkDoubleStatus", 0, "Check Double status"}; Configurable cosPA{"cosPA", 0.995, "Cosine Pointing Angle"}; Configurable radiusMin{"radiusMin", 3, "Minimum V0 radius"}; @@ -264,6 +284,8 @@ struct lambdaspincorrderived { // THnsparse bining ConfigurableAxis configThnAxisInvMass{"configThnAxisInvMass", {50, 1.09, 1.14}, "#it{M} (GeV/#it{c}^{2})"}; ConfigurableAxis configThnAxisR{"configThnAxisR", {VARIABLE_WIDTH, 0.0, 8.0}, "#it{R}"}; + ConfigurableAxis configThnAxisPt{"configThnAxisPt", {40, 0.5, 4.5}, "#it{R}"}; + ConfigurableAxis configThnAxisRap{"configThnAxisRap", {16, -0.8, 0.8}, "#it{R}"}; ConfigurableAxis configThnAxisPol{"configThnAxisPol", {VARIABLE_WIDTH, 0.0, 8.0}, "cos#it{#theta *}"}; ConfigurableAxis configThnAxisCentrality{"configThnAxisCentrality", {VARIABLE_WIDTH, 0.0, 80.0}, "Centrality"}; ConfigurableAxis configThnAxisRapidity{"configThnAxisRapidity", {VARIABLE_WIDTH, 0.0, 1.0}, "Rapidity"}; @@ -278,16 +300,80 @@ struct lambdaspincorrderived { void init(o2::framework::InitContext&) { - histos.add("hPtRadiusV0", "V0 QA;#it{p}_{T}^{V0} (GeV/#it{c});V0 decay radius (cm)", kTH2F, {{100, 0.0, 10.0}, {120, 0.0, 45.0}}); - histos.add("hPtYSame", "hPtYSame", kTH2F, {{100, 0.0, 10.0}, {200, -1.0, 1.0}}); - histos.add("hPtYMix", "hPtYMix", kTH2F, {{100, 0.0, 10.0}, {200, -1.0, 1.0}}); - histos.add("hPhiEtaSame", "hPhiEtaSame", kTH2F, {{720, 0.0, 2.0 * TMath::Pi()}, {200, -1.0, 1.0}}); - histos.add("hPhiEtaMix", "hPhiEtaMix", kTH2F, {{720, 0.0, 2.0 * TMath::Pi()}, {200, -1.0, 1.0}}); - histos.add("hCentrality", "Centrality distribution", kTH1F, {{configThnAxisCentrality}}); - histos.add("deltaPhiSame", "deltaPhiSame", HistType::kTH1D, {{72, -TMath::Pi(), TMath::Pi()}}, true); - histos.add("deltaPhiMix", "deltaPhiMix", HistType::kTH1D, {{72, -TMath::Pi(), TMath::Pi()}}, true); - histos.add("ptCent", "ptCent", HistType::kTH2D, {{100, 0.0, 10.0}, {8, 0.0, 80.0}}, true); - histos.add("etaCent", "etaCent", HistType::kTH2D, {{32, -0.8, 0.8}, {8, 0.0, 80.0}}, true); + if (fillBasicQAHistos) { + histos.add("hPtRadiusV0", "V0 QA;#it{p}_{T}^{V0} (GeV/#it{c});V0 decay radius (cm)", kTH2F, {{100, 0.0, 10.0}, {120, 0.0, 45.0}}); + histos.add("hPtYSame", "hPtYSame", kTH2F, {{100, 0.0, 10.0}, {200, -1.0, 1.0}}); + histos.add("hPtYMix", "hPtYMix", kTH2F, {{100, 0.0, 10.0}, {200, -1.0, 1.0}}); + histos.add("hPhiEtaSame", "hPhiEtaSame", kTH2F, {{720, 0.0, 2.0 * TMath::Pi()}, {200, -1.0, 1.0}}); + histos.add("hPhiEtaMix", "hPhiEtaMix", kTH2F, {{720, 0.0, 2.0 * TMath::Pi()}, {200, -1.0, 1.0}}); + histos.add("hCentrality", "Centrality distribution", kTH1F, {{configThnAxisCentrality}}); + histos.add("deltaPhiSame", "deltaPhiSame", HistType::kTH1D, {{72, -TMath::Pi(), TMath::Pi()}}, true); + histos.add("deltaPhiMix", "deltaPhiMix", HistType::kTH1D, {{72, -TMath::Pi(), TMath::Pi()}}, true); + histos.add("ptCent", "ptCent", HistType::kTH2D, {{100, 0.0, 10.0}, {8, 0.0, 80.0}}, true); + histos.add("etaCent", "etaCent", HistType::kTH2D, {{32, -0.8, 0.8}, {8, 0.0, 80.0}}, true); + } + + if (fillWeightQAHistos) { + // QA for weighting + histos.add("REP_LL_leg1_weighted", + "Weighted repl LL leg1;#phi;y/#eta;p_{T}", + HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + + histos.add("REP_LAL_leg1_weighted", + "Weighted repl LAL leg1;#phi;y/#eta;p_{T}", + HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + + histos.add("REP_ALL_leg1_weighted", + "Weighted repl ALL leg1;#phi;y/#eta;p_{T}", + HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + + histos.add("REP_ALAL_leg1_weighted", + "Weighted repl ALAL leg1;#phi;y/#eta;p_{T}", + HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + + histos.add("REP_LL_leg2_weighted", + "Weighted repl LL leg2;#phi;y/#eta;p_{T}", + HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + + histos.add("REP_LAL_leg2_weighted", + "Weighted repl LAL leg2;#phi;y/#eta;p_{T}", + HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + + histos.add("REP_ALL_leg2_weighted", + "Weighted repl ALL leg2;#phi;y/#eta;p_{T}", + HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + + histos.add("REP_ALAL_leg2_weighted", + "Weighted repl ALAL leg2;#phi;y/#eta;p_{T}", + HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + } + if (fillFixedLegQAHistos) { + // Fixed-leg QA for replacement-leg bias check. + // For repLeg1: leg1 is replaced, fixed leg is original leg2. + // For repLeg2: leg2 is replaced, fixed leg is original leg1. + + // Target fixed-leg maps: all requested SE pairs + histos.add("TGT_FIX_LL_forRepLeg1", "Target fixed LL for rep leg1;#phi;y/#eta;p_{T}", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + histos.add("TGT_FIX_LAL_forRepLeg1", "Target fixed LAL for rep leg1;#phi;y/#eta;p_{T}", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + histos.add("TGT_FIX_ALL_forRepLeg1", "Target fixed ALL for rep leg1;#phi;y/#eta;p_{T}", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + histos.add("TGT_FIX_ALAL_forRepLeg1", "Target fixed ALAL for rep leg1;#phi;y/#eta;p_{T}", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + + histos.add("TGT_FIX_LL_forRepLeg2", "Target fixed LL for rep leg2;#phi;y/#eta;p_{T}", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + histos.add("TGT_FIX_LAL_forRepLeg2", "Target fixed LAL for rep leg2;#phi;y/#eta;p_{T}", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + histos.add("TGT_FIX_ALL_forRepLeg2", "Target fixed ALL for rep leg2;#phi;y/#eta;p_{T}", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + histos.add("TGT_FIX_ALAL_forRepLeg2", "Target fixed ALAL for rep leg2;#phi;y/#eta;p_{T}", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + + // Successful fixed-leg maps: only pairs where replacement was found + histos.add("SUC_FIX_LL_forRepLeg1", "Successful fixed LL for rep leg1;#phi;y/#eta;p_{T}", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + histos.add("SUC_FIX_LAL_forRepLeg1", "Successful fixed LAL for rep leg1;#phi;y/#eta;p_{T}", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + histos.add("SUC_FIX_ALL_forRepLeg1", "Successful fixed ALL for rep leg1;#phi;y/#eta;p_{T}", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + histos.add("SUC_FIX_ALAL_forRepLeg1", "Successful fixed ALAL for rep leg1;#phi;y/#eta;p_{T}", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + + histos.add("SUC_FIX_LL_forRepLeg2", "Successful fixed LL for rep leg2;#phi;y/#eta;p_{T}", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + histos.add("SUC_FIX_LAL_forRepLeg2", "Successful fixed LAL for rep leg2;#phi;y/#eta;p_{T}", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + histos.add("SUC_FIX_ALL_forRepLeg2", "Successful fixed ALL for rep leg2;#phi;y/#eta;p_{T}", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + histos.add("SUC_FIX_ALAL_forRepLeg2", "Successful fixed ALAL for rep leg2;#phi;y/#eta;p_{T}", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + } histos.add("hEtaPhiLambdaRaw", "Lambda raw;#phi;#eta", kTH2D, {{360, 0.0, 2.0 * TMath::Pi()}, {32, -0.8, 0.8}}); @@ -297,27 +383,48 @@ struct lambdaspincorrderived { histos.add("hNUAWeightLambda", "Lambda NUA weight", kTH1D, {{200, 0.0, 5.0}}); histos.add("hNUAWeightAntiLambda", "AntiLambda NUA weight", kTH1D, {{200, 0.0, 5.0}}); - // --- target/replacement single-leg occupancy maps for replacement correction - histos.add("TGT_LL_leg1", "Target LL leg1", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); - histos.add("TGT_LAL_leg1", "Target LAL leg1", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); - histos.add("TGT_ALL_leg1", "Target ALL leg1", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); - histos.add("TGT_ALAL_leg1", "Target ALAL leg1", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); - - histos.add("REP_LL_leg1", "Repl LL leg1", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); - histos.add("REP_LAL_leg1", "Repl LAL leg1", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); - histos.add("REP_ALL_leg1", "Repl ALL leg1", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); - histos.add("REP_ALAL_leg1", "Repl ALAL leg1", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); - - histos.add("TGT_LL_leg2", "Target LL leg2", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); - histos.add("TGT_LAL_leg2", "Target LAL leg2", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); - histos.add("TGT_ALL_leg2", "Target ALL leg2", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); - histos.add("TGT_ALAL_leg2", "Target ALAL leg2", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); - - histos.add("REP_LL_leg2", "Repl LL leg2", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); - histos.add("REP_LAL_leg2", "Repl LAL leg2", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); - histos.add("REP_ALL_leg2", "Repl ALL leg2", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); - histos.add("REP_ALAL_leg2", "Repl ALAL leg2", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); - + if (fillReplacementQAHistos) { + // --- target/replacement single-leg occupancy maps for replacement correction + histos.add("TGT_LL_leg1", "Target LL leg1", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + histos.add("TGT_LAL_leg1", "Target LAL leg1", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + histos.add("TGT_ALL_leg1", "Target ALL leg1", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + histos.add("TGT_ALAL_leg1", "Target ALAL leg1", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + + histos.add("REP_LL_leg1", "Repl LL leg1", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + histos.add("REP_LAL_leg1", "Repl LAL leg1", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + histos.add("REP_ALL_leg1", "Repl ALL leg1", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + histos.add("REP_ALAL_leg1", "Repl ALAL leg1", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + + histos.add("TGT_LL_leg2", "Target LL leg2", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + histos.add("TGT_LAL_leg2", "Target LAL leg2", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + histos.add("TGT_ALL_leg2", "Target ALL leg2", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + histos.add("TGT_ALAL_leg2", "Target ALAL leg2", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + + histos.add("REP_LL_leg2", "Repl LL leg2", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + histos.add("REP_LAL_leg2", "Repl LAL leg2", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + histos.add("REP_ALL_leg2", "Repl ALL leg2", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + histos.add("REP_ALAL_leg2", "Repl ALAL leg2", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + } + if (fillWeightQAHistos) { + // Final-weighted QA: filled with the exact same mixing correction weight + // used for the mixed-event sparse, excluding NUA. + // Correction categories are LL, UL=(LAL+ALL), ALAL. + histos.add("REP_LL_leg1_finalWeighted", "Final weighted REP LL leg1;#phi;y/#eta;p_{T}", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + histos.add("REP_UL_leg1_finalWeighted", "Final weighted REP UL leg1;#phi;y/#eta;p_{T}", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + histos.add("REP_ALAL_leg1_finalWeighted", "Final weighted REP ALAL leg1;#phi;y/#eta;p_{T}", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + + histos.add("REP_LL_leg2_finalWeighted", "Final weighted REP LL leg2;#phi;y/#eta;p_{T}", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + histos.add("REP_UL_leg2_finalWeighted", "Final weighted REP UL leg2;#phi;y/#eta;p_{T}", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + histos.add("REP_ALAL_leg2_finalWeighted", "Final weighted REP ALAL leg2;#phi;y/#eta;p_{T}", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + + histos.add("SUC_FIX_LL_forRepLeg1_finalWeighted", "Final weighted fixed LL for rep leg1;#phi;y/#eta;p_{T}", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + histos.add("SUC_FIX_UL_forRepLeg1_finalWeighted", "Final weighted fixed UL for rep leg1;#phi;y/#eta;p_{T}", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + histos.add("SUC_FIX_ALAL_forRepLeg1_finalWeighted", "Final weighted fixed ALAL for rep leg1;#phi;y/#eta;p_{T}", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + + histos.add("SUC_FIX_LL_forRepLeg2_finalWeighted", "Final weighted fixed LL for rep leg2;#phi;y/#eta;p_{T}", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + histos.add("SUC_FIX_UL_forRepLeg2_finalWeighted", "Final weighted fixed UL for rep leg2;#phi;y/#eta;p_{T}", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + histos.add("SUC_FIX_ALAL_forRepLeg2_finalWeighted", "Final weighted fixed ALAL for rep leg2;#phi;y/#eta;p_{T}", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + } histos.add("hSparseLambdaLambda", "hSparseLambdaLambda", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisR}, true); histos.add("hSparseLambdaAntiLambda", "hSparseLambdaAntiLambda", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisR}, true); histos.add("hSparseAntiLambdaLambda", "hSparseAntiLambdLambda", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisR}, true); @@ -328,17 +435,19 @@ struct lambdaspincorrderived { histos.add("hSparseAntiLambdaLambdaMixed", "hSparseAntiLambdaLambdaMixed", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisR}, true); histos.add("hSparseAntiLambdaAntiLambdaMixed", "hSparseAntiLambdaAntiLambdaMixed", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisR}, true); - histos.add("hSparseLambdaLambdaAnalysis", "hSparseLambdaLambdaAnalysis", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisDeltaR, configThnAxisDeltaRap, configThnAxisDeltaPhi}, true); - histos.add("hSparseLambdaAntiLambdaAnalysis", "hSparseLambdaAntiLambdaAnalysis", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisDeltaR, configThnAxisDeltaRap, configThnAxisDeltaPhi}, true); - histos.add("hSparseAntiLambdaLambdaAnalysis", "hSparseAntiLambdLambdaAnalysis", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisDeltaR, configThnAxisDeltaRap, configThnAxisDeltaPhi}, true); - histos.add("hSparseAntiLambdaAntiLambdaAnalysis", "hSparseAntiLambdaAntiLambdaAnalysis", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisDeltaR, configThnAxisDeltaRap, configThnAxisDeltaPhi}, true); + if (fillAnalysisSparses) { + histos.add("hSparseLambdaLambdaAnalysis", "hSparseLambdaLambdaAnalysis", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisDeltaR, configThnAxisDeltaRap, configThnAxisDeltaPhi}, true); + histos.add("hSparseLambdaAntiLambdaAnalysis", "hSparseLambdaAntiLambdaAnalysis", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisDeltaR, configThnAxisDeltaRap, configThnAxisDeltaPhi}, true); + histos.add("hSparseAntiLambdaLambdaAnalysis", "hSparseAntiLambdLambdaAnalysis", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisDeltaR, configThnAxisDeltaRap, configThnAxisDeltaPhi}, true); + histos.add("hSparseAntiLambdaAntiLambdaAnalysis", "hSparseAntiLambdaAntiLambdaAnalysis", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisDeltaR, configThnAxisDeltaRap, configThnAxisDeltaPhi}, true); - histos.add("hSparseLambdaLambdaMixedAnalysis", "hSparseLambdaLambdaMixedAnalysis", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisDeltaR, configThnAxisDeltaRap, configThnAxisDeltaPhi}, true); - histos.add("hSparseLambdaAntiLambdaMixedAnalysis", "hSparseLambdaAntiLambdaMixedAnalysis", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisDeltaR, configThnAxisDeltaRap, configThnAxisDeltaPhi}, true); - histos.add("hSparseAntiLambdaLambdaMixedAnalysis", "hSparseAntiLambdaLambdaMixedAnalysis", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisDeltaR, configThnAxisDeltaRap, configThnAxisDeltaPhi}, true); - histos.add("hSparseAntiLambdaAntiLambdaMixedAnalysis", "hSparseAntiLambdaAntiLambdaMixedAnalysis", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisDeltaR, configThnAxisDeltaRap, configThnAxisDeltaPhi}, true); + histos.add("hSparseLambdaLambdaMixedAnalysis", "hSparseLambdaLambdaMixedAnalysis", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisDeltaR, configThnAxisDeltaRap, configThnAxisDeltaPhi}, true); + histos.add("hSparseLambdaAntiLambdaMixedAnalysis", "hSparseLambdaAntiLambdaMixedAnalysis", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisDeltaR, configThnAxisDeltaRap, configThnAxisDeltaPhi}, true); + histos.add("hSparseAntiLambdaLambdaMixedAnalysis", "hSparseAntiLambdaLambdaMixedAnalysis", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisDeltaR, configThnAxisDeltaRap, configThnAxisDeltaPhi}, true); + histos.add("hSparseAntiLambdaAntiLambdaMixedAnalysis", "hSparseAntiLambdaAntiLambdaMixedAnalysis", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisDeltaR, configThnAxisDeltaRap, configThnAxisDeltaPhi}, true); + } - if (useAdditionalHisto) { + if (useAdditionalHisto || fillAdditionalSparses) { histos.add("hSparseRapLambdaLambda", "hSparseRapLambdaLambda", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisRapidity}, true); histos.add("hSparseRapLambdaAntiLambda", "hSparseRapLambdaAntiLambda", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisRapidity}, true); histos.add("hSparseRapAntiLambdaLambda", "hSparseRapAntiLambdLambda", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisRapidity}, true); @@ -374,18 +483,70 @@ struct lambdaspincorrderived { ccdbApi.init("http://alice-ccdb.cern.ch"); ccdb->setCaching(true); ccdb->setLocalObjectValidityChecking(); - ccdb->setCreatedNotAfter(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()); - LOGF(info, "Getting alignment offsets from the CCDB..."); + ccdb->setCreatedNotAfter(cfgCcdbParam.nolaterthan.value); + LOGF(info, "Getting alignment offsets from the CCDB (check carefully)..."); if (useweight) { - hweight1 = ccdb->getForTimeStamp(ConfWeightPathLL.value, cfgCcdbParam.nolaterthan.value); - hweight2 = ccdb->getForTimeStamp(ConfWeightPathLAL.value, cfgCcdbParam.nolaterthan.value); - hweight3 = ccdb->getForTimeStamp(ConfWeightPathALL.value, cfgCcdbParam.nolaterthan.value); - hweight4 = ccdb->getForTimeStamp(ConfWeightPathALAL.value, cfgCcdbParam.nolaterthan.value); + const auto& repPaths = cfgCcdbParam.ConfWeightPaths.value; + if (repPaths.size() < 6) { + LOGF(fatal, "ConfWeightPaths must contain 6 paths: REP_LL_leg1, REP_UL_leg1, REP_ALAL_leg1, REP_LL_leg2, REP_UL_leg2, REP_ALAL_leg2. Found %zu", repPaths.size()); + } + for (size_t i = 0; i < repPaths.size(); ++i) { + LOGF(info, "ConfWeightPaths[%zu] = '%s'", i, repPaths[i].c_str()); + } - hweight12 = ccdb->getForTimeStamp(ConfWeightPathLL2.value, cfgCcdbParam.nolaterthan.value); - hweight22 = ccdb->getForTimeStamp(ConfWeightPathLAL2.value, cfgCcdbParam.nolaterthan.value); - hweight32 = ccdb->getForTimeStamp(ConfWeightPathALL2.value, cfgCcdbParam.nolaterthan.value); - hweight42 = ccdb->getForTimeStamp(ConfWeightPathALAL2.value, cfgCcdbParam.nolaterthan.value); + auto loadRep = [&](int idx, const char* name) -> TH3D* { + if (idx < 0 || idx >= static_cast(repPaths.size()) || repPaths[idx].empty()) { + LOGF(fatal, "Missing replaced-leg path index %d for %s", idx, name); + return nullptr; + } + auto* h = ccdb->getForTimeStamp(repPaths[idx], cfgCcdbParam.nolaterthan.value); + if (!h) { + LOGF(fatal, "Could not load replaced-leg weight %s from path %s", name, repPaths[idx].c_str()); + } + return h; + }; + + hweight1 = loadRep(0, "REP_LL_leg1"); + hweight2 = loadRep(1, "REP_UL_leg1"); + hweight4 = loadRep(2, "REP_ALAL_leg1"); + hweight12 = loadRep(3, "REP_LL_leg2"); + hweight22 = loadRep(4, "REP_UL_leg2"); + hweight42 = loadRep(5, "REP_ALAL_leg2"); + + // Keep the old ALL pointers as aliases to the UL maps to avoid accidental null access. + hweight3 = hweight2; + hweight32 = hweight22; + + if (cfgCcdbParam.useFixedWeight) { + const auto& fixPaths = cfgCcdbParam.ConfFixedWeightPaths.value; + if (fixPaths.size() < 6) { + LOGF(fatal, "ConfFixedWeightPaths must contain 6 paths: FIX_LL_forRepLeg1, FIX_UL_forRepLeg1, FIX_ALAL_forRepLeg1, FIX_LL_forRepLeg2, FIX_UL_forRepLeg2, FIX_ALAL_forRepLeg2. Found %zu", fixPaths.size()); + } + for (size_t i = 0; i < fixPaths.size(); ++i) { + LOGF(info, "ConfFixedWeightPaths[%zu] = '%s'", i, fixPaths[i].c_str()); + } + + auto loadFixed = [&](int idx, const char* name) -> TH3D* { + if (idx < 0 || idx >= static_cast(fixPaths.size()) || fixPaths[idx].empty()) { + LOGF(fatal, "Missing fixed-leg path index %d for %s", idx, name); + return nullptr; + } + auto* h = ccdb->getForTimeStamp(fixPaths[idx], cfgCcdbParam.nolaterthan.value); + if (!h) { + LOGF(fatal, "Could not load fixed-leg weight %s from path %s", name, fixPaths[idx].c_str()); + } + return h; + }; + + gFixedLLRep1 = loadFixed(0, "FIX_LL_forRepLeg1"); + gFixedULRep1 = loadFixed(1, "FIX_UL_forRepLeg1"); + gFixedALALRep1 = loadFixed(2, "FIX_ALAL_forRepLeg1"); + gFixedLLRep2 = loadFixed(3, "FIX_LL_forRepLeg2"); + gFixedULRep2 = loadFixed(4, "FIX_UL_forRepLeg2"); + gFixedALALRep2 = loadFixed(5, "FIX_ALAL_forRepLeg2"); + + LOGF(info, "Fixed-leg weights enabled. Loaded paths: %zu", fixPaths.size()); + } } if (useNUA) { hNUALambda = ccdb->getForTimeStamp(ConfNUAPathLambda.value, cfgCcdbParam.nolaterthan.value); @@ -495,10 +656,112 @@ struct lambdaspincorrderived { return w; } + int getWeightCategory(int tag1, int tag2) const + { + // Correction categories: + // 0 = LL, 1 = UL = LAL + ALL, 2 = ALAL + if (tag1 == 0 && tag2 == 0) { + return 0; + } + if (tag1 == 1 && tag2 == 1) { + return 2; + } + return 1; + } + + template + void fillFinalWeightedMixingQA(int tag1, int tag2, + int mapLeg, + int replacedPos, + LV const& particle1, + LV const& particle2, + double weight) + { + // This QA is filled with the exact same mixing-correction weight used + // for the mixed-event sparse, before the NUA factor is applied. + // mapLeg = physical replacement branch used for the CCDB map (1 or 2) + // replacedPos = position of the replaced candidate after possible L#bar{L} reordering (1 or 2) + if (!fillWeightQAHistos) { + return; + } + if (!std::isfinite(weight) || weight <= 0.0) { + return; + } + if (mapLeg != 1 && mapLeg != 2) { + return; + } + + auto getEtaOrY = [&](LV const& p) { + return userapidity ? p.Rapidity() : p.Eta(); + }; + + const LV& rep = (replacedPos == 2) ? particle2 : particle1; + const LV& fix = (replacedPos == 2) ? particle1 : particle2; + + const double ptRep = rep.Pt(); + const double phiRep = RecoDecay::constrainAngle(rep.Phi(), 0.0F, harmonic); + const double etaRep = getEtaOrY(rep); + + const double ptFix = fix.Pt(); + const double phiFix = RecoDecay::constrainAngle(fix.Phi(), 0.0F, harmonic); + const double etaFix = getEtaOrY(fix); + + // Exact four pair tags are filled for the legacy REP_*_weighted maps. + // These are useful because UL can be formed later as LAL+ALL. + if (mapLeg == 1) { + if (tag1 == 0 && tag2 == 0) { + histos.fill(HIST("REP_LL_leg1_weighted"), phiRep, etaRep, ptRep, weight); + } else if (tag1 == 0 && tag2 == 1) { + histos.fill(HIST("REP_LAL_leg1_weighted"), phiRep, etaRep, ptRep, weight); + } else if (tag1 == 1 && tag2 == 0) { + histos.fill(HIST("REP_ALL_leg1_weighted"), phiRep, etaRep, ptRep, weight); + } else if (tag1 == 1 && tag2 == 1) { + histos.fill(HIST("REP_ALAL_leg1_weighted"), phiRep, etaRep, ptRep, weight); + } + } else { // mapLeg == 2 + if (tag1 == 0 && tag2 == 0) { + histos.fill(HIST("REP_LL_leg2_weighted"), phiRep, etaRep, ptRep, weight); + } else if (tag1 == 0 && tag2 == 1) { + histos.fill(HIST("REP_LAL_leg2_weighted"), phiRep, etaRep, ptRep, weight); + } else if (tag1 == 1 && tag2 == 0) { + histos.fill(HIST("REP_ALL_leg2_weighted"), phiRep, etaRep, ptRep, weight); + } else if (tag1 == 1 && tag2 == 1) { + histos.fill(HIST("REP_ALAL_leg2_weighted"), phiRep, etaRep, ptRep, weight); + } + } + + // Combined categories are filled for the final-weighted QA maps. + const int wcat = getWeightCategory(tag1, tag2); // 0=LL, 1=UL, 2=ALAL + + if (mapLeg == 1) { + if (wcat == 0) { + histos.fill(HIST("REP_LL_leg1_finalWeighted"), phiRep, etaRep, ptRep, weight); + histos.fill(HIST("SUC_FIX_LL_forRepLeg1_finalWeighted"), phiFix, etaFix, ptFix, weight); + } else if (wcat == 1) { + histos.fill(HIST("REP_UL_leg1_finalWeighted"), phiRep, etaRep, ptRep, weight); + histos.fill(HIST("SUC_FIX_UL_forRepLeg1_finalWeighted"), phiFix, etaFix, ptFix, weight); + } else if (wcat == 2) { + histos.fill(HIST("REP_ALAL_leg1_finalWeighted"), phiRep, etaRep, ptRep, weight); + histos.fill(HIST("SUC_FIX_ALAL_forRepLeg1_finalWeighted"), phiFix, etaFix, ptFix, weight); + } + } else { // mapLeg == 2 + if (wcat == 0) { + histos.fill(HIST("REP_LL_leg2_finalWeighted"), phiRep, etaRep, ptRep, weight); + histos.fill(HIST("SUC_FIX_LL_forRepLeg2_finalWeighted"), phiFix, etaFix, ptFix, weight); + } else if (wcat == 1) { + histos.fill(HIST("REP_UL_leg2_finalWeighted"), phiRep, etaRep, ptRep, weight); + histos.fill(HIST("SUC_FIX_UL_forRepLeg2_finalWeighted"), phiFix, etaFix, ptFix, weight); + } else if (wcat == 2) { + histos.fill(HIST("REP_ALAL_leg2_finalWeighted"), phiRep, etaRep, ptRep, weight); + histos.fill(HIST("SUC_FIX_ALAL_forRepLeg2_finalWeighted"), phiFix, etaFix, ptFix, weight); + } + } + } + void fillHistograms(int tag1, int tag2, const ROOT::Math::PtEtaPhiMVector& particle1, const ROOT::Math::PtEtaPhiMVector& particle2, const ROOT::Math::PtEtaPhiMVector& daughpart1, const ROOT::Math::PtEtaPhiMVector& daughpart2, - int datatype, float mixpairweight, int replacedLeg = 1) + int datatype, float mixpairweight, int replacedLeg = 1, int weightMapLeg = -1) { auto lambda1Mass = 0.0; auto lambda2Mass = 0.0; @@ -576,57 +839,131 @@ struct lambdaspincorrderived { double deltaRap = std::abs(particle1.Rapidity() - particle2.Rapidity()); double deltaR = TMath::Sqrt(deltaRap * deltaRap + dphi_pair * dphi_pair); - double epsWeight1 = 1.0; - double epsWeight2 = 1.0; + // only for weight lookup; must match fillReplacementControlMap() + double yOrEta1_forWeight = deta1; + double yOrEta2_forWeight = deta2; + + if (userapidity) { + yOrEta1_forWeight = particle1.Rapidity(); + yOrEta2_forWeight = particle2.Rapidity(); + } + + // `replacedLeg` is the position of the replaced candidate in the ordered pair + // passed to fillHistograms: 1 -> particle1, 2 -> particle2. + // `weightMapLeg` is the physical replacement branch used to select the CCDB map: + // 1 -> REP_*_leg1, 2 -> REP_*_leg2. This distinction is needed for unlike-sign + // pairs where the pair is reordered to Lambda-AntiLambda before filling. + const int replacedPos = replacedLeg; + const int ccdbMapLeg = (weightMapLeg > 0) ? weightMapLeg : replacedLeg; + + double epsWeightReplaced = 1.0; + double epsWeightFixed = 1.0; if (useweight && datatype == 1) { - if (tag1 == 0 && tag2 == 0) { - epsWeight1 = hweight1->GetBinContent(hweight1->FindBin(dphi1, deta1, pt1)); - epsWeight2 = hweight12->GetBinContent(hweight12->FindBin(dphi2, deta2, pt2)); - } else if (tag1 == 0 && tag2 == 1) { - epsWeight1 = hweight2->GetBinContent(hweight2->FindBin(dphi1, deta1, pt1)); - epsWeight2 = hweight22->GetBinContent(hweight22->FindBin(dphi2, deta2, pt2)); - } else if (tag1 == 1 && tag2 == 0) { - epsWeight1 = hweight3->GetBinContent(hweight3->FindBin(dphi1, deta1, pt1)); - epsWeight2 = hweight32->GetBinContent(hweight32->FindBin(dphi2, deta2, pt2)); - } else if (tag1 == 1 && tag2 == 1) { - epsWeight1 = hweight4->GetBinContent(hweight4->FindBin(dphi1, deta1, pt1)); - epsWeight2 = hweight42->GetBinContent(hweight42->FindBin(dphi2, deta2, pt2)); + const int wcat = getWeightCategory(tag1, tag2); + + auto getRepEps = [&](int mapLeg, double phi, double yOrEta, double pt) -> double { + TH3D* h = nullptr; + if (mapLeg == 1) { + if (wcat == 0) + h = hweight1; + else if (wcat == 1) + h = hweight2; + else if (wcat == 2) + h = hweight4; + } else if (mapLeg == 2) { + if (wcat == 0) + h = hweight12; + else if (wcat == 1) + h = hweight22; + else if (wcat == 2) + h = hweight42; + } + if (!h) { + return 1.0; + } + return h->GetBinContent(h->FindBin(phi, yOrEta, pt)); + }; + + auto getFixedEps = [&](int mapLeg, double phi, double yOrEta, double pt) -> double { + TH3D* h = nullptr; + if (mapLeg == 1) { + if (wcat == 0) + h = gFixedLLRep1; + else if (wcat == 1) + h = gFixedULRep1; + else if (wcat == 2) + h = gFixedALALRep1; + } else if (mapLeg == 2) { + if (wcat == 0) + h = gFixedLLRep2; + else if (wcat == 1) + h = gFixedULRep2; + else if (wcat == 2) + h = gFixedALALRep2; + } + if (!h) { + return 1.0; + } + return h->GetBinContent(h->FindBin(phi, yOrEta, pt)); + }; + + const double phiRep = (replacedPos == 2) ? dphi2 : dphi1; + const double yRep = (replacedPos == 2) ? yOrEta2_forWeight : yOrEta1_forWeight; + const double ptRep = (replacedPos == 2) ? pt2 : pt1; + epsWeightReplaced = getRepEps(ccdbMapLeg, phiRep, yRep, ptRep); + + if (cfgCcdbParam.useFixedWeight) { + const int fixedPos = (replacedPos == 2) ? 1 : 2; + const double phiFix = (fixedPos == 2) ? dphi2 : dphi1; + const double yFix = (fixedPos == 2) ? yOrEta2_forWeight : yOrEta1_forWeight; + const double ptFix = (fixedPos == 2) ? pt2 : pt1; + epsWeightFixed = getFixedEps(ccdbMapLeg, phiFix, yFix, ptFix); } } if (datatype == 0) { const double weight = pairNUAWeight; if (tag1 == 0 && tag2 == 0) { - histos.fill(HIST("hPtYSame"), particle1.Pt(), particle1.Rapidity(), nuaWeight1); - histos.fill(HIST("hPhiEtaSame"), dphi1, particle1.Eta(), nuaWeight1); + if (fillBasicQAHistos) + histos.fill(HIST("hPtYSame"), particle1.Pt(), particle1.Rapidity(), nuaWeight1); + if (fillBasicQAHistos) + histos.fill(HIST("hPhiEtaSame"), dphi1, particle1.Eta(), nuaWeight1); histos.fill(HIST("hSparseLambdaLambda"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, weight); - histos.fill(HIST("hSparseLambdaLambdaAnalysis"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, deltaRap, std::abs(dphi_pair), weight); - if (useAdditionalHisto) { + if (fillAnalysisSparses) { + histos.fill(HIST("hSparseLambdaLambdaAnalysis"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, deltaRap, std::abs(dphi_pair), weight); + } + if (useAdditionalHisto || fillAdditionalSparses) { histos.fill(HIST("hSparseRapLambdaLambda"), particle1.M(), particle2.M(), cosThetaDiff, deltaRap, weight); histos.fill(HIST("hSparsePhiLambdaLambda"), particle1.M(), particle2.M(), cosThetaDiff, dphi_pair, weight); histos.fill(HIST("hSparsePairMassLambdaLambda"), particle1.M(), particle2.M(), cosThetaDiff, pairDummy.M(), weight); } } else if (tag1 == 0 && tag2 == 1) { histos.fill(HIST("hSparseLambdaAntiLambda"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, weight); - histos.fill(HIST("hSparseLambdaAntiLambdaAnalysis"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, deltaRap, std::abs(dphi_pair), weight); - if (useAdditionalHisto) { + if (fillAnalysisSparses) { + histos.fill(HIST("hSparseLambdaAntiLambdaAnalysis"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, deltaRap, std::abs(dphi_pair), weight); + } + if (useAdditionalHisto || fillAdditionalSparses) { histos.fill(HIST("hSparseRapLambdaAntiLambda"), particle1.M(), particle2.M(), cosThetaDiff, deltaRap, weight); histos.fill(HIST("hSparsePhiLambdaAntiLambda"), particle1.M(), particle2.M(), cosThetaDiff, dphi_pair, weight); histos.fill(HIST("hSparsePairMassLambdaAntiLambda"), particle1.M(), particle2.M(), cosThetaDiff, pairDummy.M(), weight); } } else if (tag1 == 1 && tag2 == 0) { histos.fill(HIST("hSparseAntiLambdaLambda"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, weight); - histos.fill(HIST("hSparseAntiLambdaLambdaAnalysis"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, deltaRap, std::abs(dphi_pair), weight); - if (useAdditionalHisto) { + if (fillAnalysisSparses) { + histos.fill(HIST("hSparseAntiLambdaLambdaAnalysis"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, deltaRap, std::abs(dphi_pair), weight); + } + if (useAdditionalHisto || fillAdditionalSparses) { histos.fill(HIST("hSparseRapAntiLambdaLambda"), particle1.M(), particle2.M(), cosThetaDiff, deltaRap, weight); histos.fill(HIST("hSparsePhiAntiLambdaLambda"), particle1.M(), particle2.M(), cosThetaDiff, dphi_pair, weight); histos.fill(HIST("hSparsePairMassAntiLambdaLambda"), particle1.M(), particle2.M(), cosThetaDiff, pairDummy.M(), weight); } } else if (tag1 == 1 && tag2 == 1) { histos.fill(HIST("hSparseAntiLambdaAntiLambda"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, weight); - histos.fill(HIST("hSparseAntiLambdaAntiLambdaAnalysis"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, deltaRap, std::abs(dphi_pair), weight); - if (useAdditionalHisto) { + if (fillAnalysisSparses) { + histos.fill(HIST("hSparseAntiLambdaAntiLambdaAnalysis"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, deltaRap, std::abs(dphi_pair), weight); + } + if (useAdditionalHisto || fillAdditionalSparses) { histos.fill(HIST("hSparseRapAntiLambdaAntiLambda"), particle1.M(), particle2.M(), cosThetaDiff, deltaRap, weight); histos.fill(HIST("hSparsePhiAntiLambdaAntiLambda"), particle1.M(), particle2.M(), cosThetaDiff, dphi_pair, weight); histos.fill(HIST("hSparsePairMassAntiLambdaAntiLambda"), particle1.M(), particle2.M(), cosThetaDiff, pairDummy.M(), weight); @@ -637,25 +974,39 @@ struct lambdaspincorrderived { double weight = mixpairweight; if (useweight) { - const double epsWeightReplaced = (replacedLeg == 2) ? epsWeight2 : epsWeight1; - if (!std::isfinite(epsWeightReplaced) || epsWeightReplaced <= 0.0) { + const double epsWeightTotal = epsWeightReplaced * epsWeightFixed; + + if (!std::isfinite(epsWeightTotal) || epsWeightTotal <= 0.0) { return; } - weight = mixpairweight / epsWeightReplaced; + weight = mixpairweight / epsWeightTotal; + } + + // This is the pure mixing-correction weight. + // Do not include NUA here, because TGT/REP/FIX QA maps were filled without NUA. + const double weightMixingQA = weight; + + if (useweight) { + fillFinalWeightedMixingQA(tag1, tag2, ccdbMapLeg, replacedPos, particle1, particle2, weightMixingQA); } + weight *= pairNUAWeight; if (!std::isfinite(weight) || weight <= 0.0) { return; } if (tag1 == 0 && tag2 == 0) { - if (replacedLeg == 1) { - histos.fill(HIST("hPtYMix"), particle1.Pt(), particle1.Rapidity(), nuaWeight1 * mixpairweight); - histos.fill(HIST("hPhiEtaMix"), dphi1, particle1.Eta(), nuaWeight1 * mixpairweight); + if (replacedLeg == 1 || replacedLeg == 2) { + if (fillBasicQAHistos) + histos.fill(HIST("hPtYMix"), particle1.Pt(), particle1.Rapidity(), nuaWeight1 * mixpairweight); + if (fillBasicQAHistos) + histos.fill(HIST("hPhiEtaMix"), dphi1, particle1.Eta(), nuaWeight1 * mixpairweight); } histos.fill(HIST("hSparseLambdaLambdaMixed"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, weight); - histos.fill(HIST("hSparseLambdaLambdaMixedAnalysis"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, deltaRap, std::abs(dphi_pair), weight); - if (useAdditionalHisto) { + if (fillAnalysisSparses) { + histos.fill(HIST("hSparseLambdaLambdaMixedAnalysis"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, deltaRap, std::abs(dphi_pair), weight); + } + if (useAdditionalHisto || fillAdditionalSparses) { histos.fill(HIST("hSparseRapLambdaLambdaMixed"), particle1.M(), particle2.M(), cosThetaDiff, deltaRap, weight); histos.fill(HIST("hSparsePhiLambdaLambdaMixed"), particle1.M(), particle2.M(), cosThetaDiff, dphi_pair, weight); histos.fill(HIST("hSparsePairMassLambdaLambdaMixed"), particle1.M(), particle2.M(), cosThetaDiff, pairDummy.M(), weight); @@ -663,24 +1014,30 @@ struct lambdaspincorrderived { } else if (tag1 == 0 && tag2 == 1) { histos.fill(HIST("hSparseLambdaAntiLambdaMixed"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, weight); - histos.fill(HIST("hSparseLambdaAntiLambdaMixedAnalysis"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, deltaRap, std::abs(dphi_pair), weight); - if (useAdditionalHisto) { + if (fillAnalysisSparses) { + histos.fill(HIST("hSparseLambdaAntiLambdaMixedAnalysis"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, deltaRap, std::abs(dphi_pair), weight); + } + if (useAdditionalHisto || fillAdditionalSparses) { histos.fill(HIST("hSparseRapLambdaAntiLambdaMixed"), particle1.M(), particle2.M(), cosThetaDiff, deltaRap, weight); histos.fill(HIST("hSparsePhiLambdaAntiLambdaMixed"), particle1.M(), particle2.M(), cosThetaDiff, dphi_pair, weight); histos.fill(HIST("hSparsePairMassLambdaAntiLambdaMixed"), particle1.M(), particle2.M(), cosThetaDiff, pairDummy.M(), weight); } } else if (tag1 == 1 && tag2 == 0) { histos.fill(HIST("hSparseAntiLambdaLambdaMixed"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, weight); - histos.fill(HIST("hSparseAntiLambdaLambdaMixedAnalysis"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, deltaRap, std::abs(dphi_pair), weight); - if (useAdditionalHisto) { + if (fillAnalysisSparses) { + histos.fill(HIST("hSparseAntiLambdaLambdaMixedAnalysis"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, deltaRap, std::abs(dphi_pair), weight); + } + if (useAdditionalHisto || fillAdditionalSparses) { histos.fill(HIST("hSparseRapAntiLambdaLambdaMixed"), particle1.M(), particle2.M(), cosThetaDiff, deltaRap, weight); histos.fill(HIST("hSparsePhiAntiLambdaLambdaMixed"), particle1.M(), particle2.M(), cosThetaDiff, dphi_pair, weight); histos.fill(HIST("hSparsePairMassAntiLambdaLambdaMixed"), particle1.M(), particle2.M(), cosThetaDiff, pairDummy.M(), weight); } } else if (tag1 == 1 && tag2 == 1) { histos.fill(HIST("hSparseAntiLambdaAntiLambdaMixed"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, weight); - histos.fill(HIST("hSparseAntiLambdaAntiLambdaMixedAnalysis"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, deltaRap, std::abs(dphi_pair), weight); - if (useAdditionalHisto) { + if (fillAnalysisSparses) { + histos.fill(HIST("hSparseAntiLambdaAntiLambdaMixedAnalysis"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, deltaRap, std::abs(dphi_pair), weight); + } + if (useAdditionalHisto || fillAdditionalSparses) { histos.fill(HIST("hSparseRapAntiLambdaAntiLambdaMixed"), particle1.M(), particle2.M(), cosThetaDiff, deltaRap, weight); histos.fill(HIST("hSparsePhiAntiLambdaAntiLambdaMixed"), particle1.M(), particle2.M(), cosThetaDiff, dphi_pair, weight); histos.fill(HIST("hSparsePairMassAntiLambdaAntiLambdaMixed"), particle1.M(), particle2.M(), cosThetaDiff, pairDummy.M(), weight); @@ -735,9 +1092,15 @@ struct lambdaspincorrderived { if (!selectionV0(v0)) { continue; } - histos.fill(HIST("hPtRadiusV0"), v0.lambdaPt(), v0.v0Radius()); - histos.fill(HIST("ptCent"), v0.lambdaPt(), centrality); - histos.fill(HIST("etaCent"), v0.lambdaEta(), centrality); + if (fillBasicQAHistos) { + histos.fill(HIST("hPtRadiusV0"), v0.lambdaPt(), v0.v0Radius()); + } + if (fillBasicQAHistos) { + histos.fill(HIST("ptCent"), v0.lambdaPt(), centrality); + } + if (fillBasicQAHistos) { + histos.fill(HIST("etaCent"), v0.lambdaEta(), centrality); + } proton = ROOT::Math::PtEtaPhiMVector(v0.protonPt(), v0.protonEta(), v0.protonPhi(), o2::constants::physics::MassProton); lambda = ROOT::Math::PtEtaPhiMVector(v0.lambdaPt(), v0.lambdaEta(), v0.lambdaPhi(), v0.lambdaMass()); const double phi = RecoDecay::constrainAngle(v0.lambdaPhi(), 0.0F, harmonic); @@ -760,7 +1123,8 @@ struct lambdaspincorrderived { proton2 = ROOT::Math::PtEtaPhiMVector(v02.protonPt(), v02.protonEta(), v02.protonPhi(), o2::constants::physics::MassProton); lambda2 = ROOT::Math::PtEtaPhiMVector(v02.lambdaPt(), v02.lambdaEta(), v02.lambdaPhi(), v02.lambdaMass()); if ((v0.v0Status() == 0 && v02.v0Status() == 1) || (v0.v0Status() == 1 && v02.v0Status() == 0)) - histos.fill(HIST("deltaPhiSame"), RecoDecay::constrainAngle(v0.lambdaPhi() - v02.lambdaPhi(), -TMath::Pi(), harmonicDphi)); + if (fillBasicQAHistos) + histos.fill(HIST("deltaPhiSame"), RecoDecay::constrainAngle(v0.lambdaPhi() - v02.lambdaPhi(), -TMath::Pi(), harmonicDphi)); // const int ptype = pairTypeCode(v0.v0Status(), v02.v0Status()); if (v0.v0Status() == 0 && v02.v0Status() == 0) { fillHistograms(0, 0, lambda, lambda2, proton, proton2, 0, 1.0); @@ -782,6 +1146,9 @@ struct lambdaspincorrderived { template void fillReplacementControlMap(int tag1, int tag2, int leg, bool isTarget, LV const& particle, float weight) { + if (!fillReplacementQAHistos) { + return; + } const double pt = particle.Pt(); const double phi = RecoDecay::constrainAngle(particle.Phi(), 0.0F, harmonic); @@ -803,6 +1170,8 @@ struct lambdaspincorrderived { } if (leg == 1 && !isTarget) { + + // Raw REP map: used to produce CCDB weight. if (tag1 == 0 && tag2 == 0) histos.fill(HIST("REP_LL_leg1"), phi, etaOrY, pt, weight); else if (tag1 == 0 && tag2 == 1) @@ -811,6 +1180,7 @@ struct lambdaspincorrderived { histos.fill(HIST("REP_ALL_leg1"), phi, etaOrY, pt, weight); else if (tag1 == 1 && tag2 == 1) histos.fill(HIST("REP_ALAL_leg1"), phi, etaOrY, pt, weight); + // Correct weighted REP QA is filled in fillHistograms(), after the exact final weight is computed. return; } @@ -827,6 +1197,8 @@ struct lambdaspincorrderived { } if (leg == 2 && !isTarget) { + + // Raw REP map: used to produce CCDB weight. if (tag1 == 0 && tag2 == 0) histos.fill(HIST("REP_LL_leg2"), phi, etaOrY, pt, weight); else if (tag1 == 0 && tag2 == 1) @@ -835,10 +1207,76 @@ struct lambdaspincorrderived { histos.fill(HIST("REP_ALL_leg2"), phi, etaOrY, pt, weight); else if (tag1 == 1 && tag2 == 1) histos.fill(HIST("REP_ALAL_leg2"), phi, etaOrY, pt, weight); + // Correct weighted REP QA is filled in fillHistograms(), after the exact final weight is computed. return; } } + template + void fillFixedLegControlMap(int tag1, int tag2, + int repLeg, + bool isTarget, + LV const& fixedParticle, + float weight) + { + if (!fillFixedLegQAHistos) { + return; + } + const double pt = fixedParticle.Pt(); + const double phi = RecoDecay::constrainAngle(fixedParticle.Phi(), 0.0F, harmonic); + double etaOrY = fixedParticle.Eta(); + if (userapidity) { + etaOrY = fixedParticle.Rapidity(); + } + + if (repLeg == 1) { + // leg1 is replaced, fixed leg is original leg2 + if (isTarget) { + if (tag1 == 0 && tag2 == 0) + histos.fill(HIST("TGT_FIX_LL_forRepLeg1"), phi, etaOrY, pt, weight); + else if (tag1 == 0 && tag2 == 1) + histos.fill(HIST("TGT_FIX_LAL_forRepLeg1"), phi, etaOrY, pt, weight); + else if (tag1 == 1 && tag2 == 0) + histos.fill(HIST("TGT_FIX_ALL_forRepLeg1"), phi, etaOrY, pt, weight); + else if (tag1 == 1 && tag2 == 1) + histos.fill(HIST("TGT_FIX_ALAL_forRepLeg1"), phi, etaOrY, pt, weight); + } else { + if (tag1 == 0 && tag2 == 0) + histos.fill(HIST("SUC_FIX_LL_forRepLeg1"), phi, etaOrY, pt, weight); + else if (tag1 == 0 && tag2 == 1) + histos.fill(HIST("SUC_FIX_LAL_forRepLeg1"), phi, etaOrY, pt, weight); + else if (tag1 == 1 && tag2 == 0) + histos.fill(HIST("SUC_FIX_ALL_forRepLeg1"), phi, etaOrY, pt, weight); + else if (tag1 == 1 && tag2 == 1) + histos.fill(HIST("SUC_FIX_ALAL_forRepLeg1"), phi, etaOrY, pt, weight); + } + return; + } + + if (repLeg == 2) { + // leg2 is replaced, fixed leg is original leg1 + if (isTarget) { + if (tag1 == 0 && tag2 == 0) + histos.fill(HIST("TGT_FIX_LL_forRepLeg2"), phi, etaOrY, pt, weight); + else if (tag1 == 0 && tag2 == 1) + histos.fill(HIST("TGT_FIX_LAL_forRepLeg2"), phi, etaOrY, pt, weight); + else if (tag1 == 1 && tag2 == 0) + histos.fill(HIST("TGT_FIX_ALL_forRepLeg2"), phi, etaOrY, pt, weight); + else if (tag1 == 1 && tag2 == 1) + histos.fill(HIST("TGT_FIX_ALAL_forRepLeg2"), phi, etaOrY, pt, weight); + } else { + if (tag1 == 0 && tag2 == 0) + histos.fill(HIST("SUC_FIX_LL_forRepLeg2"), phi, etaOrY, pt, weight); + else if (tag1 == 0 && tag2 == 1) + histos.fill(HIST("SUC_FIX_LAL_forRepLeg2"), phi, etaOrY, pt, weight); + else if (tag1 == 1 && tag2 == 0) + histos.fill(HIST("SUC_FIX_ALL_forRepLeg2"), phi, etaOrY, pt, weight); + else if (tag1 == 1 && tag2 == 1) + histos.fill(HIST("SUC_FIX_ALAL_forRepLeg2"), phi, etaOrY, pt, weight); + } + return; + } + } // Processing Event Mixing SliceCache cache; using BinningType = ColumnBinningPolicy; @@ -879,17 +1317,6 @@ struct lambdaspincorrderived { const bool doMixLeg1 = (cfgMixLegMode.value == 0 || cfgMixLegMode.value == 2); const bool doMixLeg2 = (cfgMixLegMode.value == 1 || cfgMixLegMode.value == 2); - if (doMixLeg1) { - fillReplacementControlMap(t1.v0Status(), t2.v0Status(), 1, true, - ROOT::Math::PtEtaPhiMVector(t1.lambdaPt(), t1.lambdaEta(), t1.lambdaPhi(), t1.lambdaMass()), - 1.0f); - } - if (doMixLeg2) { - fillReplacementControlMap(t1.v0Status(), t2.v0Status(), 2, true, - ROOT::Math::PtEtaPhiMVector(t2.lambdaPt(), t2.lambdaEta(), t2.lambdaPhi(), t2.lambdaMass()), - 1.0f); - } - struct PV { AllTrackCandidates* pool; int nRepl1 = 0; @@ -898,6 +1325,8 @@ struct lambdaspincorrderived { std::vector usable; int totalRepl = 0; + int totalRepl1 = 0; + int totalRepl2 = 0; int mixes = 0; for (auto it = eventPools[bin].rbegin(); it != eventPools[bin].rend() && mixes < nEvtMixing; ++it, ++mixes) { @@ -917,13 +1346,15 @@ struct lambdaspincorrderived { } if (doMixLeg1) { - if (checkKinematics(t1, tX)) { + // Single-track replacement: replace a candidate only by the same species. + if (tX.v0Status() == t1.v0Status() && checkKinematics(t1, tX)) { ++nRepl1; } } if (doMixLeg2) { - if (checkKinematics(t2, tX)) { + // Single-track replacement: replace a candidate only by the same species. + if (tX.v0Status() == t2.v0Status() && checkKinematics(t2, tX)) { ++nRepl2; } } @@ -932,6 +1363,8 @@ struct lambdaspincorrderived { if (nRepl1 > 0 || nRepl2 > 0) { usable.push_back(PV{&poolB, nRepl1, nRepl2}); totalRepl += nRepl1 + nRepl2; + totalRepl1 += nRepl1; + totalRepl2 += nRepl2; } } @@ -941,6 +1374,28 @@ struct lambdaspincorrderived { const float wBase = 1.0f / static_cast(totalRepl); + // Single-track replacement target must use the same branch normalization + // as the actually used replacement candidates. Per SE pair: + // sum REP_leg1 weights = totalRepl1 / totalRepl + // sum REP_leg2 weights = totalRepl2 / totalRepl + // Therefore TGT leg1/leg2 are filled with the same weights. + if (doMixLeg1 && totalRepl1 > 0) { + fillReplacementControlMap(t1.v0Status(), t2.v0Status(), 1, true, + ROOT::Math::PtEtaPhiMVector(t1.lambdaPt(), t1.lambdaEta(), t1.lambdaPhi(), t1.lambdaMass()), + static_cast(totalRepl1) * wBase); + fillFixedLegControlMap(t1.v0Status(), t2.v0Status(), 1, true, + ROOT::Math::PtEtaPhiMVector(t2.lambdaPt(), t2.lambdaEta(), t2.lambdaPhi(), t2.lambdaMass()), + static_cast(totalRepl1) * wBase); + } + if (doMixLeg2 && totalRepl2 > 0) { + fillReplacementControlMap(t1.v0Status(), t2.v0Status(), 2, true, + ROOT::Math::PtEtaPhiMVector(t2.lambdaPt(), t2.lambdaEta(), t2.lambdaPhi(), t2.lambdaMass()), + static_cast(totalRepl2) * wBase); + fillFixedLegControlMap(t1.v0Status(), t2.v0Status(), 2, true, + ROOT::Math::PtEtaPhiMVector(t1.lambdaPt(), t1.lambdaEta(), t1.lambdaPhi(), t1.lambdaMass()), + static_cast(totalRepl2) * wBase); + } + for (auto& pv : usable) { auto& poolB = *pv.pool; @@ -951,7 +1406,7 @@ struct lambdaspincorrderived { // -------- leg-1 replacement: (tX, t2) if (doMixLeg1) { - if (checkKinematics(t1, tX)) { + if (tX.v0Status() == t1.v0Status() && checkKinematics(t1, tX)) { fillReplacementControlMap(tX.v0Status(), t2.v0Status(), 1, false, ROOT::Math::PtEtaPhiMVector(tX.lambdaPt(), tX.lambdaEta(), tX.lambdaPhi(), tX.lambdaMass()), wBase); @@ -969,7 +1424,8 @@ struct lambdaspincorrderived { RecoDecay::constrainAngle(lambda2.Phi(), 0.0F, harmonic), -TMath::Pi(), harmonicDphi); - histos.fill(HIST("deltaPhiMix"), dPhi, wBase); + if (fillBasicQAHistos) + histos.fill(HIST("deltaPhiMix"), dPhi, wBase); fillHistograms(tX.v0Status(), t2.v0Status(), lambda, lambda2, proton, proton2, 1, wBase, 1); @@ -978,7 +1434,7 @@ struct lambdaspincorrderived { // -------- leg-2 replacement: (t1, tX) if (doMixLeg2) { - if (checkKinematics(t2, tX)) { + if (tX.v0Status() == t2.v0Status() && checkKinematics(t2, tX)) { fillReplacementControlMap(t1.v0Status(), tX.v0Status(), 2, false, ROOT::Math::PtEtaPhiMVector(tX.lambdaPt(), tX.lambdaEta(), tX.lambdaPhi(), tX.lambdaMass()), wBase); @@ -996,7 +1452,8 @@ struct lambdaspincorrderived { RecoDecay::constrainAngle(lambda2.Phi(), 0.0F, harmonic), -TMath::Pi(), harmonicDphi); - histos.fill(HIST("deltaPhiMix"), dPhi, wBase); + if (fillBasicQAHistos) + histos.fill(HIST("deltaPhiMix"), dPhi, wBase); fillHistograms(t1.v0Status(), tX.v0Status(), lambda, lambda2, proton, proton2, 1, wBase, 2); @@ -1270,9 +1727,15 @@ struct lambdaspincorrderived { if (!selectionV0MC(v0)) { continue; } - histos.fill(HIST("hPtRadiusV0"), mcacc::lamPt(v0), mcacc::v0Radius(v0)); - histos.fill(HIST("ptCent"), mcacc::lamPt(v0), centrality); - histos.fill(HIST("etaCent"), mcacc::lamEta(v0), centrality); + if (fillBasicQAHistos) { + histos.fill(HIST("hPtRadiusV0"), mcacc::lamPt(v0), mcacc::v0Radius(v0)); + } + if (fillBasicQAHistos) { + histos.fill(HIST("ptCent"), mcacc::lamPt(v0), centrality); + } + if (fillBasicQAHistos) { + histos.fill(HIST("etaCent"), mcacc::lamEta(v0), centrality); + } proton = ROOT::Math::PtEtaPhiMVector(mcacc::prPt(v0), mcacc::prEta(v0), mcacc::prPhi(v0), o2::constants::physics::MassProton); @@ -1337,17 +1800,31 @@ struct lambdaspincorrderived { return std::abs(deltaPhiMinusPiToPi(phiA, phiB)); } - // symmetric neighbors for phi: no wrap at edge + // symmetric neighbors for phi with periodic wrap at 0/2pi static inline void collectNeighborBinsPhi(int b, int nPhi, int nNeighbor, std::vector& out) { out.clear(); + if (nPhi <= 0 || b < 0 || b >= nPhi) { + return; + } + + if (2 * nNeighbor + 1 >= nPhi) { + out.reserve(nPhi); + for (int bb = 0; bb < nPhi; ++bb) { + out.push_back(bb); + } + return; + } + out.reserve(2 * nNeighbor + 1); for (int d = -nNeighbor; d <= nNeighbor; ++d) { - const int bb = b + d; - if (bb >= 0 && bb < nPhi) { - out.push_back(bb); + int bb = (b + d) % nPhi; + if (bb < 0) { + bb += nPhi; } + out.push_back(bb); } + std::sort(out.begin(), out.end()); out.erase(std::unique(out.begin(), out.end()), out.end()); } @@ -1674,6 +2151,26 @@ struct lambdaspincorrderived { const bool doMixLeg1 = (cfgMixLegMode.value == 0 || cfgMixLegMode.value == 2); const bool doMixLeg2 = (cfgMixLegMode.value == 1 || cfgMixLegMode.value == 2); + // Fill TGT maps before searching for replacements. This makes TGT the + // true same-event target phase space for the selected pair, matching the + // hPtYSame definition. REP below is filled only for accepted replacements. + if (doMixLeg1) { + fillReplacementControlMap(t1.v0Status(), t2.v0Status(), 1, true, + ROOT::Math::PtEtaPhiMVector(t1.lambdaPt(), t1.lambdaEta(), t1.lambdaPhi(), t1.lambdaMass()), + 1.0f); + fillFixedLegControlMap(t1.v0Status(), t2.v0Status(), 1, true, + ROOT::Math::PtEtaPhiMVector(t2.lambdaPt(), t2.lambdaEta(), t2.lambdaPhi(), t2.lambdaMass()), + 1.0f); + } + if (doMixLeg2) { + fillReplacementControlMap(t1.v0Status(), t2.v0Status(), 2, true, + ROOT::Math::PtEtaPhiMVector(t2.lambdaPt(), t2.lambdaEta(), t2.lambdaPhi(), t2.lambdaMass()), + 1.0f); + fillFixedLegControlMap(t1.v0Status(), t2.v0Status(), 2, true, + ROOT::Math::PtEtaPhiMVector(t1.lambdaPt(), t1.lambdaEta(), t1.lambdaPhi(), t1.lambdaMass()), + 1.0f); + } + if (doMixLeg1) { collectMatchesForReplacedLeg(t1, t2, colBin, curColIdx, matches1); limitMatchesToNEvents(matches1, nEvtMixing.value); @@ -1690,18 +2187,8 @@ struct lambdaspincorrderived { matches2.clear(); } - // --- fill target distributions for the original SE leg that is to be replaced - if (doMixLeg1) { - fillReplacementControlMap(t1.v0Status(), t2.v0Status(), 1, true, - ROOT::Math::PtEtaPhiMVector(t1.lambdaPt(), t1.lambdaEta(), t1.lambdaPhi(), t1.lambdaMass()), - 1.0f); - } - - if (doMixLeg2) { - fillReplacementControlMap(t1.v0Status(), t2.v0Status(), 2, true, - ROOT::Math::PtEtaPhiMVector(t2.lambdaPt(), t2.lambdaEta(), t2.lambdaPhi(), t2.lambdaMass()), - 1.0f); - } + // Do not fill TGT here. TGT has already been filled above as the + // selected same-event target phase space, independent of replacement success. int nFill1 = 0; int nFill2 = 0; // count actual accepted fills for leg-1 replacement @@ -1710,6 +2197,8 @@ struct lambdaspincorrderived { auto tX = V0s.iteratorAt(static_cast(m.rowIndex)); if (!selectionV0(tX)) continue; + if (tX.v0Status() != t1.v0Status()) + continue; if (!checkKinematics(t1, tX)) continue; if (tX.globalIndex() == t1.globalIndex()) @@ -1728,10 +2217,12 @@ struct lambdaspincorrderived { if (doMixLeg2) { for (auto const& m : matches2) { auto tY = V0s.iteratorAt(static_cast(m.rowIndex)); - if (!checkKinematics(t2, tY)) - continue; if (!selectionV0(tY)) continue; + if (tY.v0Status() != t2.v0Status()) + continue; + if (!checkKinematics(t2, tY)) + continue; if (tY.globalIndex() == t2.globalIndex()) continue; if (tY.globalIndex() == t1.globalIndex()) @@ -1747,8 +2238,23 @@ struct lambdaspincorrderived { if (nFill1 <= 0 && nFill2 <= 0) { continue; } - const int nUse = nFill1 + nFill2; - const float wSE = (nUse > 0) ? 1.0f / static_cast(nUse) : 0.0f; + // Residual-weight QA needs a leg-specific normalization: + // TGT_leg is filled once per same-event target candidate with weight 1. + // REP_leg is the average replacement distribution for that same target. + const float wSELeg1 = (nFill1 > 0) ? 1.0f / static_cast(nFill1) : 0.0f; + const float wSELeg2 = (nFill2 > 0) ? 1.0f / static_cast(nFill2) : 0.0f; + + 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; + const float finalMixWeightLeg1 = branchNorm * wSELeg1; + const float finalMixWeightLeg2 = branchNorm * wSELeg2; + + // Do not fill target maps here: TGT has already been filled above, + // before searching for replacements, so that TGT represents all selected + // same-event targets rather than only targets with successful replacements. if (doMixLeg1 && nFill1 > 0) { for (auto const& m : matches1) { @@ -1756,6 +2262,8 @@ struct lambdaspincorrderived { if (!selectionV0(tX)) { continue; } + if (tX.v0Status() != t1.v0Status()) + continue; if (!checkKinematics(t1, tX)) continue; if (tX.globalIndex() == t1.globalIndex()) @@ -1768,7 +2276,12 @@ struct lambdaspincorrderived { continue; fillReplacementControlMap(tX.v0Status(), t2.v0Status(), 1, false, ROOT::Math::PtEtaPhiMVector(tX.lambdaPt(), tX.lambdaEta(), tX.lambdaPhi(), tX.lambdaMass()), - wSE); + wSELeg1); + // Fixed leg for leg1 replacement is original t2. + // Fill only for successful replacement. + fillFixedLegControlMap(tX.v0Status(), t2.v0Status(), 1, false, + ROOT::Math::PtEtaPhiMVector(t2.lambdaPt(), t2.lambdaEta(), t2.lambdaPhi(), t2.lambdaMass()), + wSELeg1); auto proton = ROOT::Math::PtEtaPhiMVector(tX.protonPt(), tX.protonEta(), tX.protonPhi(), o2::constants::physics::MassProton); auto lambda = ROOT::Math::PtEtaPhiMVector(tX.lambdaPt(), tX.lambdaEta(), tX.lambdaPhi(), tX.lambdaMass()); auto proton2 = ROOT::Math::PtEtaPhiMVector(t2.protonPt(), t2.protonEta(), t2.protonPhi(), o2::constants::physics::MassProton); @@ -1776,19 +2289,20 @@ struct lambdaspincorrderived { // const int ptype = pairTypeCode(tX.v0Status(), t2.v0Status()); - const float meWeight = wSE; + const float meWeight = finalMixWeightLeg1; const float dPhi = deltaPhiMinusPiToPi((float)lambda.Phi(), (float)lambda2.Phi()); if ((tX.v0Status() == 0 && t2.v0Status() == 1) || (tX.v0Status() == 1 && t2.v0Status() == 0)) - histos.fill(HIST("deltaPhiMix"), dPhi, wSE); + if (fillBasicQAHistos) + histos.fill(HIST("deltaPhiMix"), dPhi, meWeight); const int s1 = tX.v0Status(); const int s2 = t2.v0Status(); if (s1 == 0 && s2 == 1) { - fillHistograms(0, 1, lambda, lambda2, proton, proton2, 1, meWeight, 1); + fillHistograms(0, 1, lambda, lambda2, proton, proton2, 1, meWeight, 1, 1); } else if (s1 == 1 && s2 == 0) { - fillHistograms(0, 1, lambda2, lambda, proton2, proton, 1, meWeight, 2); + fillHistograms(0, 1, lambda2, lambda, proton2, proton, 1, meWeight, 2, 1); } else { - fillHistograms(s1, s2, lambda, lambda2, proton, proton2, 1, meWeight, 1); + fillHistograms(s1, s2, lambda, lambda2, proton, proton2, 1, meWeight, 1, 1); } } } @@ -1799,6 +2313,8 @@ struct lambdaspincorrderived { if (!selectionV0(tY)) { continue; } + if (tY.v0Status() != t2.v0Status()) + continue; if (!checkKinematics(t2, tY)) continue; if (tY.globalIndex() == t2.globalIndex()) @@ -1811,24 +2327,30 @@ struct lambdaspincorrderived { continue; fillReplacementControlMap(t1.v0Status(), tY.v0Status(), 2, false, ROOT::Math::PtEtaPhiMVector(tY.lambdaPt(), tY.lambdaEta(), tY.lambdaPhi(), tY.lambdaMass()), - wSE); + wSELeg2); + // Fixed leg for leg2 replacement is original t1. + // Fill only for successful replacement. + fillFixedLegControlMap(t1.v0Status(), tY.v0Status(), 2, false, + ROOT::Math::PtEtaPhiMVector(t1.lambdaPt(), t1.lambdaEta(), t1.lambdaPhi(), t1.lambdaMass()), + wSELeg2); auto proton = ROOT::Math::PtEtaPhiMVector(t1.protonPt(), t1.protonEta(), t1.protonPhi(), o2::constants::physics::MassProton); auto lambda = ROOT::Math::PtEtaPhiMVector(t1.lambdaPt(), t1.lambdaEta(), t1.lambdaPhi(), t1.lambdaMass()); auto proton2 = ROOT::Math::PtEtaPhiMVector(tY.protonPt(), tY.protonEta(), tY.protonPhi(), o2::constants::physics::MassProton); auto lambda2 = ROOT::Math::PtEtaPhiMVector(tY.lambdaPt(), tY.lambdaEta(), tY.lambdaPhi(), tY.lambdaMass()); // const int ptype = pairTypeCode(t1.v0Status(), tY.v0Status()); - const float meWeight = wSE; + const float meWeight = finalMixWeightLeg2; const float dPhi = deltaPhiMinusPiToPi((float)lambda.Phi(), (float)lambda2.Phi()); - histos.fill(HIST("deltaPhiMix"), dPhi, wSE); + if (fillBasicQAHistos) + histos.fill(HIST("deltaPhiMix"), dPhi, meWeight); const int s1 = t1.v0Status(); const int s2 = tY.v0Status(); if (s1 == 0 && s2 == 1) { - fillHistograms(0, 1, lambda, lambda2, proton, proton2, 1, meWeight, 2); + fillHistograms(0, 1, lambda, lambda2, proton, proton2, 1, meWeight, 2, 2); } else if (s1 == 1 && s2 == 0) { - fillHistograms(0, 1, lambda2, lambda, proton2, proton, 1, meWeight, 1); + fillHistograms(0, 1, lambda2, lambda, proton2, proton, 1, meWeight, 1, 2); } else { - fillHistograms(s1, s2, lambda, lambda2, proton, proton2, 1, meWeight, 2); + fillHistograms(s1, s2, lambda, lambda2, proton, proton2, 1, meWeight, 2, 2); } } } @@ -2092,6 +2614,25 @@ struct lambdaspincorrderived { const bool doMixLeg1 = (cfgMixLegMode.value == 0 || cfgMixLegMode.value == 2); const bool doMixLeg2 = (cfgMixLegMode.value == 1 || cfgMixLegMode.value == 2); + // Fill MC TGT maps before searching for replacements, same definition as data: + // TGT is the selected same-event target phase space, independent of replacement success. + if (doMixLeg1) { + fillReplacementControlMap(mcacc::v0Status(t1), mcacc::v0Status(t2), 1, true, + ROOT::Math::PtEtaPhiMVector(mcacc::lamPt(t1), mcacc::lamEta(t1), mcacc::lamPhi(t1), mcacc::lamMass(t1)), + 1.0f); + fillFixedLegControlMap(mcacc::v0Status(t1), mcacc::v0Status(t2), 1, true, + ROOT::Math::PtEtaPhiMVector(mcacc::lamPt(t2), mcacc::lamEta(t2), mcacc::lamPhi(t2), mcacc::lamMass(t2)), + 1.0f); + } + if (doMixLeg2) { + fillReplacementControlMap(mcacc::v0Status(t1), mcacc::v0Status(t2), 2, true, + ROOT::Math::PtEtaPhiMVector(mcacc::lamPt(t2), mcacc::lamEta(t2), mcacc::lamPhi(t2), mcacc::lamMass(t2)), + 1.0f); + fillFixedLegControlMap(mcacc::v0Status(t1), mcacc::v0Status(t2), 2, true, + ROOT::Math::PtEtaPhiMVector(mcacc::lamPt(t1), mcacc::lamEta(t1), mcacc::lamPhi(t1), mcacc::lamMass(t1)), + 1.0f); + } + if (doMixLeg1) { collectMatchesForReplacedLeg(t1, t2, colBin, curColIdx, matches1); limitMatchesToNEvents(matches1, nEvtMixing.value); @@ -2106,18 +2647,9 @@ struct lambdaspincorrderived { } else { matches2.clear(); } - if (doMixLeg1) { - fillReplacementControlMap(mcacc::v0Status(t1), mcacc::v0Status(t2), 1, true, - ROOT::Math::PtEtaPhiMVector(mcacc::lamPt(t1), mcacc::lamEta(t1), mcacc::lamPhi(t1), mcacc::lamMass(t1)), - 1.0f); - } - - if (doMixLeg2) { - fillReplacementControlMap(mcacc::v0Status(t1), mcacc::v0Status(t2), 2, true, - ROOT::Math::PtEtaPhiMVector(mcacc::lamPt(t2), mcacc::lamEta(t2), mcacc::lamPhi(t2), mcacc::lamMass(t2)), - 1.0f); - } + // Do not fill TGT here. TGT has already been filled above as the + // selected same-event target phase space, independent of replacement success. int nFill1 = 0; int nFill2 = 0; @@ -2128,6 +2660,8 @@ struct lambdaspincorrderived { if (!selectionV0MC(tX)) continue; + if (mcacc::v0Status(tX) != mcacc::v0Status(t1)) + continue; if (!checkKinematicsMC(t1, tX)) continue; if (tX.globalIndex() == t1.globalIndex()) @@ -2149,6 +2683,8 @@ struct lambdaspincorrderived { if (!selectionV0MC(tY)) continue; + if (mcacc::v0Status(tY) != mcacc::v0Status(t2)) + continue; if (!checkKinematicsMC(t2, tY)) continue; if (tY.globalIndex() == t2.globalIndex()) @@ -2166,8 +2702,17 @@ struct lambdaspincorrderived { if (nFill1 <= 0 && nFill2 <= 0) { continue; } - const int nUse = nFill1 + nFill2; - const float wSE = (nUse > 0) ? 1.0f / static_cast(nUse) : 0.0f; + // Residual-weight QA needs the same leg-specific REP normalization in MC. + const float wSELeg1 = (nFill1 > 0) ? 1.0f / static_cast(nFill1) : 0.0f; + const float wSELeg2 = (nFill2 > 0) ? 1.0f / static_cast(nFill2) : 0.0f; + + 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; + const float finalMixWeightLeg1 = branchNorm * wSELeg1; + const float finalMixWeightLeg2 = branchNorm * wSELeg2; if (doMixLeg1 && nFill1 > 0) { for (auto const& m : matches1) { @@ -2175,6 +2720,8 @@ struct lambdaspincorrderived { if (!selectionV0MC(tX)) continue; + if (mcacc::v0Status(tX) != mcacc::v0Status(t1)) + continue; if (!checkKinematicsMC(t1, tX)) continue; if (tX.globalIndex() == t1.globalIndex()) @@ -2187,8 +2734,15 @@ struct lambdaspincorrderived { continue; fillReplacementControlMap(mcacc::v0Status(tX), mcacc::v0Status(t2), 1, false, ROOT::Math::PtEtaPhiMVector(mcacc::lamPt(tX), mcacc::lamEta(tX), mcacc::lamPhi(tX), mcacc::lamMass(tX)), - wSE); - + wSELeg1); + // Fixed leg for MC leg1 replacement is original t2. + // Fill only for successful replacement. + fillFixedLegControlMap(mcacc::v0Status(tX), mcacc::v0Status(t2), 1, false, + ROOT::Math::PtEtaPhiMVector(mcacc::lamPt(t2), + mcacc::lamEta(t2), + mcacc::lamPhi(t2), + mcacc::lamMass(t2)), + wSELeg1); auto pX = ROOT::Math::PtEtaPhiMVector(mcacc::prPt(tX), mcacc::prEta(tX), mcacc::prPhi(tX), o2::constants::physics::MassProton); auto lX = ROOT::Math::PtEtaPhiMVector(mcacc::lamPt(tX), mcacc::lamEta(tX), mcacc::lamPhi(tX), @@ -2199,18 +2753,19 @@ struct lambdaspincorrderived { mcacc::lamMass(t2)); // const int ptype = pairTypeCode(mcacc::v0Status(tX), mcacc::v0Status(t2)); - const float meWeight = wSE; + const float meWeight = finalMixWeightLeg1; const float dPhi = deltaPhiMinusPiToPi((float)lX.Phi(), (float)l2.Phi()); - histos.fill(HIST("deltaPhiMix"), dPhi, wSE); + if (fillBasicQAHistos) + histos.fill(HIST("deltaPhiMix"), dPhi, meWeight); const int s1 = mcacc::v0Status(tX); const int s2 = mcacc::v0Status(t2); if (s1 == 0 && s2 == 1) { - fillHistograms(0, 1, lX, l2, pX, p2, 1, meWeight, 1); + fillHistograms(0, 1, lX, l2, pX, p2, 1, meWeight, 1, 1); } else if (s1 == 1 && s2 == 0) { - fillHistograms(0, 1, l2, lX, p2, pX, 1, meWeight, 2); + fillHistograms(0, 1, l2, lX, p2, pX, 1, meWeight, 2, 1); } else { - fillHistograms(s1, s2, lX, l2, pX, p2, 1, meWeight, 1); + fillHistograms(s1, s2, lX, l2, pX, p2, 1, meWeight, 1, 1); } } } @@ -2220,6 +2775,8 @@ struct lambdaspincorrderived { if (!selectionV0MC(tY)) continue; + if (mcacc::v0Status(tY) != mcacc::v0Status(t2)) + continue; if (!checkKinematicsMC(t2, tY)) continue; if (tY.globalIndex() == t2.globalIndex()) @@ -2232,8 +2789,15 @@ struct lambdaspincorrderived { continue; fillReplacementControlMap(mcacc::v0Status(t1), mcacc::v0Status(tY), 2, false, ROOT::Math::PtEtaPhiMVector(mcacc::lamPt(tY), mcacc::lamEta(tY), mcacc::lamPhi(tY), mcacc::lamMass(tY)), - wSE); - + wSELeg2); + // Fixed leg for MC leg2 replacement is original t1. + // Fill only for successful replacement. + fillFixedLegControlMap(mcacc::v0Status(t1), mcacc::v0Status(tY), 2, false, + ROOT::Math::PtEtaPhiMVector(mcacc::lamPt(t1), + mcacc::lamEta(t1), + mcacc::lamPhi(t1), + mcacc::lamMass(t1)), + wSELeg2); auto p1 = ROOT::Math::PtEtaPhiMVector(mcacc::prPt(t1), mcacc::prEta(t1), mcacc::prPhi(t1), o2::constants::physics::MassProton); auto l1 = ROOT::Math::PtEtaPhiMVector(mcacc::lamPt(t1), mcacc::lamEta(t1), mcacc::lamPhi(t1), @@ -2244,18 +2808,19 @@ struct lambdaspincorrderived { mcacc::lamMass(tY)); // const int ptype = pairTypeCode(mcacc::v0Status(t1), mcacc::v0Status(tY)); - const float meWeight = wSE; + const float meWeight = finalMixWeightLeg2; const float dPhi = deltaPhiMinusPiToPi((float)l1.Phi(), (float)lY.Phi()); - histos.fill(HIST("deltaPhiMix"), dPhi, wSE); + if (fillBasicQAHistos) + histos.fill(HIST("deltaPhiMix"), dPhi, meWeight); const int s1 = mcacc::v0Status(t1); const int s2 = mcacc::v0Status(tY); if (s1 == 0 && s2 == 1) { - fillHistograms(0, 1, l1, lY, p1, pY, 1, meWeight, 2); + fillHistograms(0, 1, l1, lY, p1, pY, 1, meWeight, 2, 2); } else if (s1 == 1 && s2 == 0) { - fillHistograms(0, 1, lY, l1, pY, p1, 1, meWeight, 1); + fillHistograms(0, 1, lY, l1, pY, p1, 1, meWeight, 1, 2); } else { - fillHistograms(s1, s2, l1, lY, p1, pY, 1, meWeight, 2); + fillHistograms(s1, s2, l1, lY, p1, pY, 1, meWeight, 2, 2); } } } From 8e9628abfaeedc1b852764969112c5a9e2e970e5 Mon Sep 17 00:00:00 2001 From: skundu692 Date: Sun, 14 Jun 2026 10:40:59 +0200 Subject: [PATCH 7/8] remove unused variable --- PWGLF/Tasks/Resonances/doublephimeson.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PWGLF/Tasks/Resonances/doublephimeson.cxx b/PWGLF/Tasks/Resonances/doublephimeson.cxx index 178fe725b95..8f680d0e99a 100644 --- a/PWGLF/Tasks/Resonances/doublephimeson.cxx +++ b/PWGLF/Tasks/Resonances/doublephimeson.cxx @@ -1655,7 +1655,7 @@ struct doublephimeson { // LOGF(info,"track share",t1.phid1Index(),t1.phid2Index(),t2.phid1Index(),t2.phid2Index()); continue; } - const double dMNominal = deltaMPhiNominal(phi1.M(), phi2.M()); + // const double dMNominal = deltaMPhiNominal(phi1.M(), phi2.M()); const double mCross12 = (k1p + k2m).M(); // K+ from phi1 + K- from phi2 const double mCross21 = (k2p + k1m).M(); // K+ from phi2 + K- from phi1 const double dMCross = deltaMPhiNominal(mCross12, mCross21); From eb369fe2ce5849a27c0e143573aebe01b9db2409 Mon Sep 17 00:00:00 2001 From: skundu692 Date: Sun, 14 Jun 2026 11:04:52 +0200 Subject: [PATCH 8/8] remove unused variable --- PWGLF/Tasks/Resonances/doublephimeson.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PWGLF/Tasks/Resonances/doublephimeson.cxx b/PWGLF/Tasks/Resonances/doublephimeson.cxx index 8f680d0e99a..74c3c2d9c74 100644 --- a/PWGLF/Tasks/Resonances/doublephimeson.cxx +++ b/PWGLF/Tasks/Resonances/doublephimeson.cxx @@ -1497,7 +1497,7 @@ struct doublephimeson { // --- phi multiplicity with PID --- int phimult = 0; - int nBestPairingRejected = 0; + // int nBestPairingRejected = 0; for (auto const& t : phitracks) { const double kpluspt = std::hypot(t.phid1Px(), t.phid1Py()); const double kminuspt = std::hypot(t.phid2Px(), t.phid2Py()); @@ -1661,7 +1661,7 @@ struct doublephimeson { const double dMCross = deltaMPhiNominal(mCross12, mCross21); // Reject this candidate only if the crossed assignment is more phi-like if (dMCross > 1.01 && dMCross < 1.03) { - ++nBestPairingRejected; + // ++nBestPairingRejected; /* LOGF(info, "Best-pairing rejected: dMNominal = %.6f, dMCross = %.6f, "