diff --git a/PWGLF/Tasks/GlobalEventProperties/ptmultCorr.cxx b/PWGLF/Tasks/GlobalEventProperties/ptmultCorr.cxx index 844bacb2867..c224b0e5507 100644 --- a/PWGLF/Tasks/GlobalEventProperties/ptmultCorr.cxx +++ b/PWGLF/Tasks/GlobalEventProperties/ptmultCorr.cxx @@ -16,6 +16,7 @@ /// \since October 01, 2025 #include "Common/CCDB/EventSelectionParams.h" +#include "Common/CCDB/RCTSelectionFlags.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/McCollisionExtra.h" @@ -54,12 +55,13 @@ using namespace o2::framework; using namespace o2::framework::expressions; using namespace o2::aod::track; using namespace o2::aod::evsel; +using namespace o2::aod::rctsel; using ColDataTablePbPb = soa::Join; using ColDataTablepp = soa::Join; using ColMCRecTablePbPb = soa::SmallGroups>; using ColMCRecTablepp = soa::SmallGroups>; -using ColMCTrueTable = soa::Join; +using ColMCTrueTable = soa::Join; using TrackDataTable = soa::Join; using TrackMCRecTable = soa::Join; using TrackMCTrueTable = aod::McParticles; @@ -144,10 +146,7 @@ struct PtmultCorr { Configurable extraphicut3{"extraphicut3", 0.03f, "Extra Phi cut 3"}; Configurable extraphicut4{"extraphicut4", 6.253f, "Extra Phi cut 4"}; - Configurable cfgMinNClusITS{"cfgMinNClusITS", 7, "Minimum ITS clusters"}; Configurable cfgMinNCrossedRows{"cfgMinNCrossedRows", 70, "Minimum TPC crossed rows"}; - Configurable cfgMinNcls{"cfgMinNcls", 130, "Minimum TPC clusters (applied only if cfgApplyNclSel is true)"}; - Configurable cfgApplyNclSel{"cfgApplyNclSel", false, "Enable minimum TPC cluster selection"}; Configurable cfgUseNclsPID{"cfgUseNclsPID", false, "Use NclsPID instead of NclsFound for the Ncls cut"}; Configurable cfgMinChi2ClsTPC{"cfgMinChi2ClsTPC", 0.5f, "Minimum TPC chi2/cls"}; Configurable cfgMaxChi2ClsTPC{"cfgMaxChi2ClsTPC", 4.0f, "Maximum TPC chi2/cls"}; @@ -158,24 +157,38 @@ struct PtmultCorr { ConfigurableAxis centralityBinning{"centralityBinning", {VARIABLE_WIDTH, 0, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100}, ""}; ConfigurableAxis binsImpactPar{"binsImpactPar", {VARIABLE_WIDTH, 0.0, 3.00065, 4.28798, 6.14552, 7.6196, 8.90942, 10.0897, 11.2002, 12.2709, 13.3167, 14.4173, 23.2518}, "Binning of the impact parameter axis"}; - Configurable isApplySameBunchPileup{"isApplySameBunchPileup", false, "Enable SameBunchPileup cut"}; - Configurable isApplyGoodZvtxFT0vsPV{"isApplyGoodZvtxFT0vsPV", false, "Enable GoodZvtxFT0vsPV cut"}; - Configurable isApplyExtraPhiCut{"isApplyExtraPhiCut", false, "Enable extra phi cut"}; - Configurable isApplyNoCollInTimeRangeStandard{"isApplyNoCollInTimeRangeStandard", true, "Enable NoCollInTimeRangeStandard cut"}; - Configurable isApplyNoCollInRofStandard{"isApplyNoCollInRofStandard", false, "Enable NoCollInRofStandard cut"}; - Configurable isApplyNoHighMultCollInPrevRof{"isApplyNoHighMultCollInPrevRof", false, "Enable NoHighMultCollInPrevRof cut"}; - Configurable isApplyFT0CbasedOccupancy{"isApplyFT0CbasedOccupancy", false, "Enable FT0CbasedOccupancy cut"}; - Configurable isApplyInelgt0{"isApplyInelgt0", false, "Enable INEL > 0 condition"}; - Configurable isApplyOccuCut{"isApplyOccuCut", false, "Enable occupancy selection"}; - - // Secondary estimation related configurables - Configurable isApplyDCACuts{"isApplyDCACuts", true, "Enable DCA cuts (set to false for secondary estimation)"}; - Configurable isApplyITSCuts{"isApplyITSCuts", true, "Enable ITS cuts (set to false for secondary estimation)"}; - Configurable isApplyChi2Cuts{"isApplyChi2Cuts", true, "Enable χ² cuts (set to false for secondary estimation)"}; + Configurable isSameBunchPileup{"isSameBunchPileup", false, "Enable SameBunchPileup cut"}; + Configurable isGoodZvtxFT0vsPV{"isGoodZvtxFT0vsPV", false, "Enable GoodZvtxFT0vsPV cut"}; + Configurable applyExtraPhiCut{"applyExtraPhiCut", false, "Enable extra phi cut"}; + Configurable isNoCollInTimeRangeStandard{"isNoCollInTimeRangeStandard", false, "Enable NoCollInTimeRangeStandard cut"}; + Configurable isNoCollInTimeRangeStrict{"isNoCollInTimeRangeStrict", false, "use isNoCollInTimeRangeStrict?"}; + + Configurable selHasBC{"selHasBC", true, "Require has_foundBC"}; + Configurable selHasFT0{"selHasFT0", true, "Require has_foundFT0"}; + + Configurable isNoCollInRofStandard{"isNoCollInRofStandard", false, "Enable NoCollInRofStandard cut"}; + Configurable isNoCollInRofStrict{"isNoCollInRofStrict", false, "use isNoCollInRofStrict?"}; + + Configurable isNoHighMultCollInPrevRof{"isNoHighMultCollInPrevRof", false, "use isNoHighMultCollInPrevRof?"}; + Configurable isNoCollInTimeRangeNarrow{"isNoCollInTimeRangeNarrow", false, "use isNoCollInTimeRangeNarrow?"}; + + Configurable applyFT0CbasedOccupancy{"applyFT0CbasedOccupancy", false, "Enable FT0CbasedOccupancy cut"}; + Configurable applyInelgt0{"applyInelgt0", true, "Enable INEL > 0 condition"}; + Configurable applyOccuCut{"applyOccuCut", true, "Enable occupancy selection"}; + + Configurable rctLabel{"rctLabel", "CBT_hadronPID", "RCT selection flag"}; + Configurable rctCheckZDC{"rctCheckZDC", false, "Check ZDC in RCT"}; + Configurable rctTreatLimitedAcceptanceAsBad{"rctTreatLimitedAcceptanceAsBad", false, "Treat limited acceptance as bad"}; + Configurable requireGoodRct{"requireGoodRct", true, "Apply RCT selection"}; + + // RCT checker instance + RCTFlagsChecker rctChecker; void init(InitContext const&) { - + if (requireGoodRct) { + rctChecker.init(rctLabel.value, rctCheckZDC.value, rctTreatLimitedAcceptanceAsBad.value); + } AxisSpec centAxis = {centralityBinning, "Centrality", "CentralityAxis"}; AxisSpec axisPt = {ptHistBin, "pT", "pTAxis"}; AxisSpec impactParAxis = {binsImpactPar, "Impact Parameter"}; @@ -260,18 +273,36 @@ struct PtmultCorr { kTH2F, {{axisPt}, {500, 0, 500, "Gen N_{ch} |#eta|<0.8"}}); } + if (doprocessEvtLossSigLossMCpp) { + histos.add("hNch_AllRecoEvt", + "All reco collisions passing partial cuts (event split denom pp)", + kTH1F, {{500, 0, 500, "N_{ch} |#eta|<0.8"}}); + histos.add("hNch_WRecoEvtWSelCri", + "Best reco collision passing full selection (event split num pp)", + kTH1F, {{500, 0, 500, "N_{ch} |#eta|<0.8"}}); + } + if (doprocessDatapp || doprocessDataPbPb) { + histos.add("RCTSel", "All=1 | RCT passed=2", kTH1F, {{2, 0.5, 2.5}}); + auto hrct = histos.get(HIST("RCTSel")); + hrct->GetXaxis()->SetBinLabel(1, "All"); + hrct->GetXaxis()->SetBinLabel(2, "RCT passed"); + } auto hstat = histos.get(HIST("EventHist")); auto* x = hstat->GetXaxis(); x->SetBinLabel(1, "All events"); - x->SetBinLabel(2, "sel8"); - x->SetBinLabel(3, "kNoSameBunchPileup"); // reject collisions in case of pileup with another collision in the same foundBC - x->SetBinLabel(4, "kIsGoodZvtxFT0vsPV"); // small difference between z-vertex from PV and from FT0 - x->SetBinLabel(5, "ApplyNoCollInTimeRangeStandard"); - x->SetBinLabel(6, "ApplyNoCollInRofStandard"); - x->SetBinLabel(7, "ApplyNoHighMultCollInPrevRof"); - x->SetBinLabel(8, "INEL > 0"); - x->SetBinLabel(9, "|vz|<10"); - x->SetBinLabel(10, "Occupancy<500"); + x->SetBinLabel(2, "has_foundBC"); + x->SetBinLabel(3, "has_foundFT0"); + x->SetBinLabel(4, "kIsTriggerTVX"); + x->SetBinLabel(5, "kNoITSROFrameBorder"); + x->SetBinLabel(6, "kNoTimeFrameBorder"); + x->SetBinLabel(7, "|vz|SetBinLabel(8, "kIsGoodZvtxFT0vsPV"); + x->SetBinLabel(9, "kNoSameBunchPileup"); + x->SetBinLabel(10, "kNoCollInTimeRangeStrict"); + x->SetBinLabel(11, "kNoCollInRofStrict"); + x->SetBinLabel(12, "kNoHighMultCollInPrevRof"); + x->SetBinLabel(13, "INEL>0"); + x->SetBinLabel(14, "Occupancy"); } template @@ -279,51 +310,74 @@ struct PtmultCorr { { histos.fill(HIST("EventHist"), 1); - if (!col.sel8()) { + if (selHasBC && !col.has_foundBC()) return false; - } - histos.fill(HIST("EventHist"), 2); - - if (isApplySameBunchPileup && !col.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { + if (selHasFT0 && !col.has_foundFT0()) return false; - } - histos.fill(HIST("EventHist"), 3); - if (isApplyGoodZvtxFT0vsPV && !col.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + // TVX trigger (replaces sel8 as primary trigger requirement) + if (!col.selection_bit(o2::aod::evsel::kIsTriggerTVX)) return false; - } histos.fill(HIST("EventHist"), 4); - if (isApplyNoCollInTimeRangeStandard && !col.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { + // ITS ROF border + if (!col.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) return false; - } histos.fill(HIST("EventHist"), 5); - if (isApplyNoCollInRofStandard && !col.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) { + // Time frame border + if (!col.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) return false; - } histos.fill(HIST("EventHist"), 6); - if (isApplyNoHighMultCollInPrevRof && !col.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof)) { + // vtxZ + if (std::abs(col.posZ()) > vtxRange) return false; - } histos.fill(HIST("EventHist"), 7); - if (isApplyInelgt0 && !col.isInelGt0()) { + // Good ZvtxFT0vsPV + if (isGoodZvtxFT0vsPV && + !col.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) return false; - } histos.fill(HIST("EventHist"), 8); - if (std::abs(col.posZ()) >= vtxRange) { + // No same bunch pileup + if (isSameBunchPileup && + !col.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) return false; - } histos.fill(HIST("EventHist"), 9); - auto occu = isApplyFT0CbasedOccupancy ? col.ft0cOccupancyInTimeRange() : col.trackOccupancyInTimeRange(); - if (isApplyOccuCut && occu > occuRange) { + // Time range isolation — Strict (replaces Standard) + if (isNoCollInTimeRangeStrict && + !col.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStrict)) return false; - } histos.fill(HIST("EventHist"), 10); + + // ROF isolation — Strict (replaces Standard) + if (isNoCollInRofStrict && + !col.selection_bit(o2::aod::evsel::kNoCollInRofStrict)) + return false; + histos.fill(HIST("EventHist"), 11); + + // No high mult collision in previous ROF + if (isNoHighMultCollInPrevRof && + !col.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof)) + return false; + histos.fill(HIST("EventHist"), 12); + + // INEL > 0 + if (applyInelgt0 && !col.isInelGt0()) + return false; + histos.fill(HIST("EventHist"), 13); + + // Occupancy + auto occu = applyFT0CbasedOccupancy + ? col.ft0cOccupancyInTimeRange() + : col.trackOccupancyInTimeRange(); + if (applyOccuCut && occu > occuRange) + return false; + histos.fill(HIST("EventHist"), 14); + return true; } @@ -340,59 +394,33 @@ struct PtmultCorr { return false; } - if (track.hasTPC()) { - // ---- Global (ITS+TPC) tracks: + // ITS inner-barrel hit: must fire layer 0 or layer 1 + if (!(track.itsClusterMap() & 0x01) && !(track.itsClusterMap() & 0x02)) { + return false; + } - // ITS cuts: can be disabled for secondary estimation - if (isApplyITSCuts) { - // ITS inner-barrel hit: must fire layer 0 or layer 1 - if (!(track.itsClusterMap() & 0x01) && !(track.itsClusterMap() & 0x02)) { - return false; - } - if (track.itsNCls() < cfgMinNClusITS) { - return false; - } - // ITS chi2 cut (also part of ITS cuts) - if (track.itsChi2NCl() > cfgMaxChi2ClsITS) { - return false; - } - } + // ITS chi2 cut (also part of ITS cuts) + if (track.itsChi2NCl() > cfgMaxChi2ClsITS) { + return false; + } + + if (track.hasTPC()) { - // TPC quality cuts (always applied) if (track.tpcNClsCrossedRows() < cfgMinNCrossedRows) { return false; } - // optional minimum TPC clusters (found or PID, matching piKpRAA applyNclSel) - if (cfgApplyNclSel) { - const int16_t ncl = cfgUseNclsPID ? track.tpcNClsPID() : track.tpcNClsFound(); - if (ncl < cfgMinNcls) { - return false; - } - } - - // --- χ² cuts (can be disabled for secondary estimation) --- - if (isApplyChi2Cuts) { - if (track.tpcChi2NCl() < cfgMinChi2ClsTPC || track.tpcChi2NCl() > cfgMaxChi2ClsTPC) { - return false; - } - } - // pT-dependent DCA cuts (can be disabled for secondary estimation) - if (isApplyDCACuts) { - const float pt = track.pt(); - const double dcaXYcut = (0.0105 + 0.0350 / - std::pow(std::abs(pt), 1.1)); - - const double dcaZcut = cfgDCAz; - if (std::abs(track.dcaZ()) > dcaZcut || std::abs(track.dcaXY()) > dcaXYcut) { - return false; - } + if (track.tpcChi2NCl() < cfgMinChi2ClsTPC || track.tpcChi2NCl() > cfgMaxChi2ClsTPC) { + return false; } } + if (std::abs(track.dcaZ()) > cfgDCAz) + return false; + // --- optional phi cut (applied to all track types) --- histos.fill(HIST("PhiVsEtaHistNoCut"), track.phi(), track.eta()); - if (isApplyExtraPhiCut && ((track.phi() > extraphicut1 && track.phi() < extraphicut2) || track.phi() <= extraphicut3 || track.phi() >= extraphicut4)) { + if (applyExtraPhiCut && ((track.phi() > extraphicut1 && track.phi() < extraphicut2) || track.phi() <= extraphicut3 || track.phi() >= extraphicut4)) { return false; } histos.fill(HIST("PhiVsEtaHistWithCut"), track.phi(), track.eta()); @@ -408,7 +436,7 @@ struct PtmultCorr { if (!track.isPhysicalPrimary()) { return false; } - if (!track.producedByGenerator()) { + if (track.pt() < cfgPtCutMin || track.pt() > cfgPtCutMax) { return false; } auto pdgTrack = pdg->GetParticle(track.pdgCode()); @@ -421,7 +449,7 @@ struct PtmultCorr { if (std::abs(track.eta()) >= etaRange) { return false; } - if (isApplyExtraPhiCut && ((track.phi() > extraphicut1 && track.phi() < extraphicut2) || track.phi() <= extraphicut3 || track.phi() >= extraphicut4)) { + if (applyExtraPhiCut && ((track.phi() > extraphicut1 && track.phi() < extraphicut2) || track.phi() <= extraphicut3 || track.phi() >= extraphicut4)) { return false; } return true; @@ -436,7 +464,7 @@ struct PtmultCorr { template bool isGenChargedTrackSelected(CheckGenTrack const& track) { - if (!track.producedByGenerator()) { + if (track.pt() < cfgPtCutMin || track.pt() > cfgPtCutMax) { return false; } auto pdgTrack = pdg->GetParticle(track.pdgCode()); @@ -449,7 +477,7 @@ struct PtmultCorr { if (std::abs(track.eta()) >= etaRange) { return false; } - if (isApplyExtraPhiCut && ((track.phi() > extraphicut1 && track.phi() < extraphicut2) || track.phi() <= extraphicut3 || track.phi() >= extraphicut4)) { + if (applyExtraPhiCut && ((track.phi() > extraphicut1 && track.phi() < extraphicut2) || track.phi() <= extraphicut3 || track.phi() >= extraphicut4)) { return false; } return true; @@ -457,6 +485,12 @@ struct PtmultCorr { void processDataPbPb(ColDataTablePbPb::iterator const& cols, TrackDataTable const& tracks) { + if (requireGoodRct) { + histos.fill(HIST("RCTSel"), 1); // all events + if (!rctChecker(cols)) + return; + histos.fill(HIST("RCTSel"), 2); // passed RCT + } if (!isEventSelected(cols)) { return; } @@ -479,6 +513,12 @@ struct PtmultCorr { void processDatapp(ColDataTablepp::iterator const& cols, TrackDataTable const& tracks) { + if (requireGoodRct) { + histos.fill(HIST("RCTSel"), 1); // all events + if (!rctChecker(cols)) + return; + histos.fill(HIST("RCTSel"), 2); // passed RCT + } if (!isEventSelected(cols)) { return; } @@ -501,13 +541,23 @@ struct PtmultCorr { { float gencent = -999.f; bool atLeastOne = false; + int bestCollisionIndex = -1; + int biggestNContribs = -1; + for (const auto& RecCol : RecCols) { + if (biggestNContribs < RecCol.numContrib()) { + biggestNContribs = RecCol.numContrib(); + bestCollisionIndex = RecCol.globalIndex(); + } + } for (const auto& RecCol : RecCols) { + if (!isEventSelected(RecCol)) { continue; } - if (RecCol.globalIndex() != mcCollision.bestCollisionIndex()) { + + if (RecCol.globalIndex() != bestCollisionIndex) continue; - } + atLeastOne = true; gencent = RecCol.centFT0C(); } @@ -574,12 +624,14 @@ struct PtmultCorr { } // track (mcgen) loop for (const auto& RecCol : RecCols) { + if (!isEventSelected(RecCol)) { continue; } - if (RecCol.globalIndex() != mcCollision.bestCollisionIndex()) { + + if (RecCol.globalIndex() != bestCollisionIndex) continue; - } + histos.fill(HIST("hPbPbRecMCvtxz"), RecCol.posZ()); histos.fill(HIST("hPbPbRecMCcent"), RecCol.centFT0C()); histos.fill(HIST("hPbPbRecMCvtxzcent"), RecCol.posZ(), RecCol.centFT0C()); @@ -643,13 +695,23 @@ struct PtmultCorr { void processMCeffpp(ColMCTrueTable::iterator const& mcCollision, ColMCRecTablepp const& RecCols, TrackMCTrueTable const& GenParticles, TrackMCRecTable const& RecTracks) { bool atLeastOne = false; + int bestCollisionIndex = -1; + int biggestNContribs = -1; for (const auto& RecCol : RecCols) { + if (biggestNContribs < RecCol.numContrib()) { + biggestNContribs = RecCol.numContrib(); + bestCollisionIndex = RecCol.globalIndex(); + } + } + for (const auto& RecCol : RecCols) { + if (!isEventSelected(RecCol)) { continue; } - if (RecCol.globalIndex() != mcCollision.bestCollisionIndex()) { + + if (RecCol.globalIndex() != bestCollisionIndex) continue; - } + atLeastOne = true; } histos.fill(HIST("hppGenMCvtxz"), mcCollision.posZ()); @@ -713,12 +775,14 @@ struct PtmultCorr { } // track (mcgen) loop for (const auto& RecCol : RecCols) { + if (!isEventSelected(RecCol)) { continue; } - if (RecCol.globalIndex() != mcCollision.bestCollisionIndex()) { + + if (RecCol.globalIndex() != bestCollisionIndex) continue; - } + histos.fill(HIST("hppRecMCvtxz"), RecCol.posZ()); auto recTracksPart = RecTracks.sliceBy(perCollision, RecCol.globalIndex()); std::vector mclabels; @@ -775,16 +839,10 @@ struct PtmultCorr { } // collision loop } - void processEvtLossSigLossMCpp(ColMCTrueTable::iterator const& mcCollision, ColMCRecTablepp const& RecCols, TrackMCTrueTable const& GenParticles) + void processEvtLossSigLossMCpp(ColMCTrueTable::iterator const& /*mcCollision*/, ColMCRecTablepp const& RecCols, TrackMCTrueTable const& GenParticles) { - if (isApplyInelgt0 && !mcCollision.isInelGt0()) { - return; - } - if (std::abs(mcCollision.posZ()) >= vtxRange) { - return; - } - // Count generated Nch in |eta| < 0.8 for CO map and event loss + // ── Count generated Nch in |eta| < etaRange for event loss ────────────── int nChMC = 0; for (const auto& particle : GenParticles) { if (!particle.isPhysicalPrimary()) @@ -794,18 +852,56 @@ struct PtmultCorr { continue; if (std::abs(pdgParticle->Charge()) < KminCharge) continue; - if (std::abs(particle.eta()) < etaRange) // fixed to |eta| < 0.8 for CO map, not etaRange + if (std::abs(particle.eta()) < etaRange) nChMC++; } - bool atLeastOne = false; + for (const auto& RecCol : RecCols) { - if (RecCol.globalIndex() != mcCollision.bestCollisionIndex()) + if (!RecCol.has_foundBC()) + continue; + if (!RecCol.has_foundFT0()) + continue; + if (!RecCol.selection_bit(o2::aod::evsel::kIsTriggerTVX)) + continue; + if (!RecCol.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) + continue; + if (!RecCol.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) + continue; + if (std::fabs(RecCol.posZ()) > vtxRange) + continue; + if (isGoodZvtxFT0vsPV && + !RecCol.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) + continue; + if (isSameBunchPileup && + !RecCol.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) continue; + + histos.fill(HIST("hNch_AllRecoEvt"), nChMC); // denominator for event splitting + } + + // ── Find best collision, apply full selection ──────────────────────────── + // isEventSelected() first, bestCollisionIndex filter second + // matches processMCeffpp cut order + bool atLeastOne = false; + int bestCollisionIndex = -1; + int biggestNContribs = -1; + for (const auto& RecCol : RecCols) { + if (biggestNContribs < RecCol.numContrib()) { + biggestNContribs = RecCol.numContrib(); + bestCollisionIndex = RecCol.globalIndex(); + } + } + for (const auto& RecCol : RecCols) { if (!isEventSelected(RecCol)) continue; + + if (RecCol.globalIndex() != bestCollisionIndex) + continue; atLeastOne = true; + histos.fill(HIST("hNch_WRecoEvtWSelCri"), nChMC); // numerator for event splitting } - // All generated events + + // ── Event loss histograms ──────────────────────────────────────────────── histos.fill(HIST("MCEventHist"), 1); histos.fill(HIST("hNchMC_AllGen"), nChMC); // denominator for event loss @@ -813,16 +909,17 @@ struct PtmultCorr { histos.fill(HIST("MCEventHist"), 2); histos.fill(HIST("hNchMC_WithRecoEvt"), nChMC); // numerator for event loss } + + // ── Signal loss particle loop ──────────────────────────────────────────── for (const auto& particle : GenParticles) { - if (!isGenTrackSelected(particle)) { + if (!isGenTrackSelected(particle)) continue; - } - // All generated particles - histos.fill(HIST("hPtVsNchMC_AllGen"), particle.pt(), nChMC); + + histos.fill(HIST("hPtVsNchMC_AllGen"), particle.pt(), nChMC); // denominator: all generated events histos.fill(HIST("hgenptBeforeEvtSel"), particle.pt()); + if (atLeastOne) { - // All generated particles with at least one reconstructed collision (signal loss estimation) - histos.fill(HIST("hPtVsNchMC_WithRecoEvt"), particle.pt(), nChMC); + histos.fill(HIST("hPtVsNchMC_WithRecoEvt"), particle.pt(), nChMC); // numerator: ≥1 reco collision passing selection histos.fill(HIST("hgenptAfterEvtSel"), particle.pt()); } } @@ -830,14 +927,7 @@ struct PtmultCorr { void processEvtLossSigLossMCPbPb(ColMCTrueTable::iterator const& mcCollision, ColMCRecTablePbPb const& RecCols, TrackMCTrueTable const& GenParticles) { - if (isApplyInelgt0 && !mcCollision.isInelGt0()) { - return; - } - if (std::abs(mcCollision.posZ()) >= vtxRange) { - return; - } - - // count generated Nch in |eta| < 0.8 for CO map and event loss + // ── Count generated Nch in |eta| < etaRange for event loss ────────────── int nChMC = 0; for (const auto& particle : GenParticles) { if (!particle.isPhysicalPrimary()) @@ -851,32 +941,56 @@ struct PtmultCorr { nChMC++; } - // Event splitting denominator : loop over ALL reco collisions passing selection - //(before bestCollisionsIndex filter)- counts N_rec + // ── Event splitting denominator ────────────────────────────────────────── for (const auto& RecCol : RecCols) { + if (!RecCol.has_foundBC()) + continue; + if (!RecCol.has_foundFT0()) + continue; + if (!RecCol.selection_bit(o2::aod::evsel::kIsTriggerTVX)) + continue; + if (!RecCol.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) + continue; + if (!RecCol.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) + continue; + if (std::fabs(RecCol.posZ()) > vtxRange) + continue; + if (isGoodZvtxFT0vsPV && + !RecCol.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) + continue; + if (isSameBunchPileup && + !RecCol.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) + continue; - histos.fill(HIST("hCent_AllRecoEvt"), RecCol.centFT0C()); + histos.fill(HIST("hCent_AllRecoEvt"), RecCol.centFT0C()); // denominator for event splitting } - // Find best collisions + // ── Find best collision, apply full selection ──────────────────────────── + // isEventSelected() first, bestCollisionIndex filter second + // matches processMCeffPbPb cut order bool atLeastOne = false; + int bestCollisionIndex = -1; + int biggestNContribs = -1; + for (const auto& RecCol : RecCols) { + if (biggestNContribs < RecCol.numContrib()) { + biggestNContribs = RecCol.numContrib(); + bestCollisionIndex = RecCol.globalIndex(); + } + } auto centrality = -999.f; for (const auto& RecCol : RecCols) { - - if (RecCol.globalIndex() != mcCollision.bestCollisionIndex()) { + if (!isEventSelected(RecCol)) continue; - } - if (!isEventSelected(RecCol)) { + if (RecCol.globalIndex() != bestCollisionIndex) continue; - } atLeastOne = true; centrality = RecCol.centFT0C(); - histos.fill(HIST("hCent_WRecoEvtWSelCri"), RecCol.centFT0C()); + histos.fill(HIST("hCent_WRecoEvtWSelCri"), RecCol.centFT0C()); // numerator for event splitting } - // Event loss histograms + // ── Event loss histograms ──────────────────────────────────────────────── histos.fill(HIST("MCEventHist"), 1); histos.fill(HIST("hImpactParameterGen"), mcCollision.impactParameter()); histos.fill(HIST("hNchMC_AllGen"), nChMC); // denominator for event loss @@ -888,16 +1002,17 @@ struct PtmultCorr { histos.fill(HIST("hNchMC_WithRecoEvt"), nChMC); // numerator for event loss histos.fill(HIST("hNchVsCent_WithRecoEvt"), centrality, nChMC); // CO map } + + // ── Signal loss particle loop ──────────────────────────────────────────── for (const auto& particle : GenParticles) { - if (!isGenTrackSelected(particle)) { + if (!isGenTrackSelected(particle)) continue; - } - // All generated particles - histos.fill(HIST("hPtVsNchMC_AllGen"), particle.pt(), nChMC); + + histos.fill(HIST("hPtVsNchMC_AllGen"), particle.pt(), nChMC); // denominator: all generated events histos.fill(HIST("hgenptBeforeEvtSelPbPb"), particle.pt(), mcCollision.impactParameter()); + if (atLeastOne) { - // All generated particles with at least one reconstructed collision (signal loss estimation) - histos.fill(HIST("hPtVsNchMC_WithRecoEvt"), particle.pt(), nChMC); + histos.fill(HIST("hPtVsNchMC_WithRecoEvt"), particle.pt(), nChMC); // numerator: ≥1 reco collision passing selection histos.fill(HIST("hgenptAfterEvtSelPbPb"), particle.pt(), mcCollision.impactParameter()); } }