diff --git a/PWGLF/DataModel/LFHResonanceCorrelationTables.h b/PWGLF/DataModel/LFHResonanceCorrelationTables.h index 2bc281daaf6..3caad772493 100644 --- a/PWGLF/DataModel/LFHResonanceCorrelationTables.h +++ b/PWGLF/DataModel/LFHResonanceCorrelationTables.h @@ -53,8 +53,12 @@ DECLARE_SOA_COLUMN(MCPhysicalPrimary, mcPhysicalPrimary, bool); // true phys DECLARE_SOA_INDEX_COLUMN_FULL(Track, track, int, Tracks, "_Assoc"); //! DECLARE_SOA_COLUMN(MCOriginalPt, mcOriginalPt, float); // true generated pt DECLARE_SOA_COLUMN(PDGCode, pdgCode, int); // pdg code of the MC particle +// Rapidity under the mass hypothesis of the species this entry was actually +// selected as (pion or kaon, from isValidAssocTrack()); -999 for +// the species-agnostic AssocHadron pool, which has no mass hypothesis to use. +DECLARE_SOA_COLUMN(Rapidity, rapidity, float); } // namespace assocHadrons -DECLARE_SOA_TABLE(AssocHadrons, "AOD", "ASSOCHADRONS", o2::soa::Index<>, assocHadrons::CollisionId, assocHadrons::MCPhysicalPrimary, assocHadrons::TrackId, assocHadrons::MCOriginalPt, assocHadrons::PDGCode); +DECLARE_SOA_TABLE(AssocHadrons, "AOD", "ASSOCHADRONS", o2::soa::Index<>, assocHadrons::CollisionId, assocHadrons::MCPhysicalPrimary, assocHadrons::TrackId, assocHadrons::MCOriginalPt, assocHadrons::PDGCode, assocHadrons::Rapidity); /// _________________________________________ /// Table for storing assoc track PID namespace assocPID @@ -86,6 +90,7 @@ DECLARE_SOA_COLUMN(Pt, pt, float); DECLARE_SOA_COLUMN(Eta, eta, float); DECLARE_SOA_COLUMN(Phi, phi, float); DECLARE_SOA_COLUMN(Mass, mass, float); +DECLARE_SOA_COLUMN(Rapidity, rapidity, float); // phi candidate's own rapidity (kaon-mass-pair system) DECLARE_SOA_INDEX_COLUMN_FULL(PosTrackDaughter, posTrackDaughter, int, Tracks, "_PhiPosDaughter"); DECLARE_SOA_INDEX_COLUMN_FULL(NegTrackDaughter, negTrackDaughter, int, Tracks, "_PhiNegDaughter"); @@ -104,6 +109,7 @@ DECLARE_SOA_TABLE( assocPhis::Eta, assocPhis::Phi, assocPhis::Mass, + assocPhis::Rapidity, assocPhis::PosTrackDaughterId, assocPhis::NegTrackDaughterId); @@ -127,6 +133,7 @@ DECLARE_SOA_COLUMN(Pt, pt, float); DECLARE_SOA_COLUMN(Eta, eta, float); DECLARE_SOA_COLUMN(Phi, phi, float); DECLARE_SOA_COLUMN(Mass, mass, float); +DECLARE_SOA_COLUMN(Rapidity, rapidity, float); // K*0 candidate's own rapidity (kaon+pion system) DECLARE_SOA_INDEX_COLUMN_FULL(PosTrackDaughter, posTrackDaughter, int, Tracks, "_KstarPosDaughter"); DECLARE_SOA_INDEX_COLUMN_FULL(NegTrackDaughter, negTrackDaughter, int, Tracks, "_KstarNegDaughter"); @@ -145,6 +152,7 @@ DECLARE_SOA_TABLE( assocKstars::Eta, assocKstars::Phi, assocKstars::Mass, + assocKstars::Rapidity, assocKstars::PosTrackDaughterId, assocKstars::NegTrackDaughterId); } // namespace o2::aod diff --git a/PWGLF/TableProducer/Resonances/hResonanceCorrelationFilter.cxx b/PWGLF/TableProducer/Resonances/hResonanceCorrelationFilter.cxx index 959014c61ce..f35fa0fd078 100644 --- a/PWGLF/TableProducer/Resonances/hResonanceCorrelationFilter.cxx +++ b/PWGLF/TableProducer/Resonances/hResonanceCorrelationFilter.cxx @@ -18,9 +18,6 @@ #include "PWGLF/DataModel/LFHResonanceCorrelationTables.h" #include "Common/CCDB/EventSelectionParams.h" -#include "Common/CCDB/RCTSelectionFlags.h" -#include "Common/Core/Zorro.h" -#include "Common/Core/ZorroSummary.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/Multiplicity.h" @@ -28,7 +25,6 @@ #include "Common/DataModel/PIDResponseTPC.h" #include "Common/DataModel/TrackSelectionTables.h" -#include #include #include #include @@ -56,7 +52,6 @@ using namespace o2; using namespace o2::soa; using namespace o2::constants::math; -using namespace o2::aod::rctsel; using namespace o2::framework; using namespace o2::framework::expressions; @@ -72,64 +67,43 @@ enum PIDCutType { struct HResonanceCorrelationFilter { - Service ccdb; - - RCTFlagsChecker rctChecker; - HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; - // master analysis switches - Configurable doPPAnalysis{"doPPAnalysis", true, "if in pp, set to true"}; - - // Operational - Configurable zorroMask{"zorroMask", "", "zorro trigger class to select on (empty: none)"}; - - Configurable cfgTPCNsigmaKaon{"cfgTPCNsigmaKaon", 3.0f, "TPC Kaon PID"}; - Configurable cfgTOFNsigmaKaon{"cfgTOFNsigmaKaon", 3.0f, "TOF Kaon PID"}; - - Configurable cfgRapidity{"cfgRapidity", 0.5f, "Rapidity cut"}; - - // used for event selections in Pb-Pb - Configurable cfgCutOccupancyHigh{"cfgCutOccupancyHigh", 3000, "High cut on TPC occupancy"}; - Configurable cfgCutOccupancyLow{"cfgCutOccupancyLow", 0, "Low cut on TPC occupancy"}; - - struct : ConfigurableGroup { - std::string prefix = "eventSelections"; - // event filtering - Configurable zVertexCut{"zVertexCut", 10, "Cut on PV position"}; - Configurable selectINELgtZERO{"selectINELgtZERO", true, "select INEL>0 events"}; - Configurable requireAllGoodITSLayers{"requireAllGoodITSLayers", false, " require that in the event all ITS are good"}; - Configurable requireGoodTriggerTVX{"requireGoodTriggerTVX", false, " require acceptable FT0C-FT0A time difference"}; - Configurable requireGoodZvtxFT0vsPV{"requireGoodZvtxFT0vsPV", false, " require small difference between z-vertex from PV and from FT0"}; - Configurable minCentPercent{"minCentPercent", 0, "minimum centrality percentage"}; - Configurable maxCentPercent{"maxCentPercent", 100, "maximum centrality percentage"}; - } eventSelections; - + // Single, shared event-selection cut list used by every process function + // (processTriggers, processAssocPions/Kaons/Hadrons, processPhis, + // processKstars, and their MC counterparts) via isSelectedEvents(), so the + // trigger table and every associated-track/resonance table are always + // built over the exact same set of selected collisions. struct : ConfigurableGroup { + std::string prefix = "configEvents"; Configurable cfgEvtZvtx{"cfgEvtZvtx", 10.0f, "Evt sel: Max. z-Vertex (cm)"}; Configurable cfgEvtTriggerTVXSel{"cfgEvtTriggerTVXSel", true, "Evt sel: triggerTVX selection (MB)"}; Configurable cfgEvtNoTFBorderCut{"cfgEvtNoTFBorderCut", true, "Evt sel: apply TF border cut"}; - Configurable cfgEvtNoITSROFrameBorderCut{"cfgEvtNoITSROFrameBorderCut", false, "Evt sel: apply NoITSRO border cut"}; - Configurable cfgEvtIsRCTFlagpassed{"cfgEvtIsRCTFlagpassed", false, "Evt sel: apply RCT flag selection"}; - Configurable cfgEvtRCTFlagCheckerLabel{"cfgEvtRCTFlagCheckerLabel", "CBT_hadronPID", "Evt sel: RCT flag checker label"}; - Configurable cfgEvtRCTFlagCheckerZDCCheck{"cfgEvtRCTFlagCheckerZDCCheck", false, "Evt sel: RCT flag checker ZDC check"}; - Configurable cfgEvtRCTFlagCheckerLimitAcceptAsBad{"cfgEvtRCTFlagCheckerLimitAcceptAsBad", true, "Evt sel: RCT flag checker treat Limited Acceptance As Bad"}; - Configurable cfgEvtSel8{"cfgEvtSel8", false, "Evt Sel 8 check for offline selection"}; - Configurable cfgEvtIsINELgt0{"cfgEvtIsINELgt0", false, "Evt sel: apply INEL>0 selection"}; + Configurable cfgEvtNoITSROFrameBorderCut{"cfgEvtNoITSROFrameBorderCut", true, "Evt sel: apply NoITSRO border cut"}; + Configurable cfgEvtSel8{"cfgEvtSel8", true, "Evt Sel 8 check for offline selection"}; + Configurable cfgEvtNoSameBunchPileupCut{"cfgEvtNoSameBunchPileupCut", true, "Evt sel: reject collisions associated with the same found-by-T0 bunch crossing"}; + Configurable cfgEvtGoodZvtxFT0vsPVCut{"cfgEvtGoodZvtxFT0vsPVCut", true, "Evt sel: require small difference between z-vertex from PV and from FT0"}; } configEvents; + // Merged from the formerly separate configTracks/generalSelections/ + // trackSelections groups -- all three were "cuts applied to a track or + // track pair", just split by who last touched them. No member names + // collided, so every Configurable below keeps its original key; only the + // "." prefix at each call site changed, to configTracks. struct : ConfigurableGroup { + std::string prefix = "configTracks"; // Pre-selection Track cuts - Configurable trackSelection{"trackSelection", 0, "Track selection: 0 -> No Cut, 1 -> kGlobalTrack, 2 -> kGlobalTrackWoPtEta, 3 -> kGlobalTrackWoDCA, 4 -> kQualityTracks, 5 -> kInAcceptanceTracks"}; Configurable cMinPtcut{"cMinPtcut", 0.15f, "Minimal pT for tracks"}; Configurable cMinTPCNClsFound{"cMinTPCNClsFound", 120, "minimum TPCNClsFound value for good track"}; Configurable cfgCutEta{"cfgCutEta", 0.8f, "Eta range for tracks"}; Configurable cfgCutRapidity{"cfgCutRapidity", 0.5f, "rapidity range for particles"}; Configurable cfgMinCrossedRows{"cfgMinCrossedRows", 70, "min crossed rows for good track"}; + Configurable cfgMaxTPCChi2NCl{"cfgMaxTPCChi2NCl", 4.0f, "max TPC chi2/clusters for good track"}; + Configurable cfgMaxITSChi2NCl{"cfgMaxITSChi2NCl", 36.0f, "max ITS chi2/clusters for good track"}; + Configurable cfgMinTPCCrossedRowsOverFindableCls{"cfgMinTPCCrossedRowsOverFindableCls", 0.8f, "min ratio of TPC crossed rows over findable clusters for good track"}; + Configurable cfgMinITSNCls{"cfgMinITSNCls", 5, "min number of ITS hits (clusters) for good track"}; // DCA Selections - // DCAr to PV - Configurable cMaxDCArToPVcut{"cMaxDCArToPVcut", 0.1f, "Track DCAr cut to PV Maximum"}; // DCAz to PV Configurable cMaxDCAzToPVcut{"cMaxDCAzToPVcut", 0.1f, "Track DCAz cut to PV Maximum"}; @@ -139,64 +113,44 @@ struct HResonanceCorrelationFilter { Configurable cfgGlobalTrack{"cfgGlobalTrack", false, "Global track selection"}; // kGoldenChi2 | kDCAxy | kDCAz Configurable cfgPVContributor{"cfgPVContributor", false, "PV contributor track selection"}; // PV Contriuibutor Configurable cfgHasTOF{"cfgHasTOF", false, "Require TOF"}; - Configurable cfgUseTPCRefit{"cfgUseTPCRefit", false, "Require TPC Refit"}; - Configurable cfgUseITSRefit{"cfgUseITSRefit", false, "Require ITS Refit"}; Configurable cTPCNClsFound{"cTPCNClsFound", false, "Switch to turn on/off TPCNClsFound cut"}; - Configurable cDCAr7SigCut{"cDCAr7SigCut", false, "Track DCAr 7 Sigma cut to PV Maximum"}; - } configTracks; - struct : ConfigurableGroup { - std::string prefix = "generalSelections"; - - // Associated particle selections in phase space + // Associated particle selections in phase space (formerly generalSelections) Configurable assocEtaMin{"assocEtaMin", -0.8, "triggeretamin"}; Configurable assocEtaMax{"assocEtaMax", 0.8, "triggeretamax"}; Configurable assocPtCutMin{"assocPtCutMin", 0.2, "assocptmin"}; Configurable assocPtCutMax{"assocPtCutMax", 10, "assocptmax"}; - // Trigger particle selections in phase space + // Trigger particle selections in phase space (formerly generalSelections) Configurable triggerEtaMin{"triggerEtaMin", -0.8, "triggeretamin"}; Configurable triggerEtaMax{"triggerEtaMax", 0.8, "triggeretamax"}; Configurable triggerPtCutMin{"triggerPtCutMin", 3, "triggerptmin"}; Configurable triggerPtCutMax{"triggerPtCutMax", 20, "triggerptmax"}; - } generalSelections; - struct : ConfigurableGroup { - std::string prefix = "trackSelections"; - // Track quality + // Track quality (formerly trackSelections) Configurable minTPCNCrossedRows{"minTPCNCrossedRows", 70, "Minimum TPC crossed rows"}; Configurable triggerRequireITS{"triggerRequireITS", true, "require ITS signal in trigger tracks"}; Configurable assocRequireITS{"assocRequireITS", true, "require ITS signal in assoc tracks"}; Configurable triggerMaxTPCSharedClusters{"triggerMaxTPCSharedClusters", 200, "maximum number of shared TPC clusters (inclusive)"}; Configurable triggerRequireL0{"triggerRequireL0", false, "require ITS L0 cluster for trigger"}; - Configurable requireClusterInITS{"requireClusterInITS", false, "require cluster in ITS for phi daughter tracks"}; - Configurable minITSClustersForDaughterTracks{"minITSClustersForDaughterTracks", 1, "Minimum number of ITS clusters for phi daughter tracks"}; - - // Associated pion identification - Configurable pionMinBayesProb{"pionMinBayesProb", 0.95, "minimal Bayesian probability for pion ID"}; - Configurable assocPionNSigmaTPCFOF{"assocPionNSigmaTPCFOF", 3, "minimal n sigma in TOF and TPC for Pion ID"}; - Configurable rejectSigma{"rejectSigma", 1, "n sigma for rejecting pion candidates"}; - - // Associated kaon identification (mirrors the pion selection above, with - // the accept/reject roles swapped: accept kaon-consistent tracks, reject - // pion-/proton-consistent ones). Selected via the Species template tag on - // isValidAssocTrack() below -- production path (which table rows - // get made) is still separate per process function, only the PID gate - // logic itself is shared. - Configurable assocKaonNSigmaTPCFOF{"assocKaonNSigmaTPCFOF", 3, "minimal n sigma in TOF and TPC for Kaon ID"}; - // primary particle DCAxy selections + // primary particle DCAxy selections (formerly trackSelections) // formula: |DCAxy| < 0.004f + (0.013f / pt) Configurable dcaXYconstant{"dcaXYconstant", 0.004, "[0] in |DCAxy| < [0]+[1]/pT"}; Configurable dcaXYpTdep{"dcaXYpTdep", 0.013, "[1] in |DCAxy| < [0]+[1]/pT"}; - } trackSelections; + } configTracks; struct : ConfigurableGroup { + std::string prefix = "configPID"; /// PID Selections - Configurable pidnSigmaPreSelectionCut{"pidnSigmaPreSelectionCut", 4.0f, "pidnSigma Cut for pre-selection of tracks"}; Configurable cByPassTOF{"cByPassTOF", false, "By pass TOF PID selection"}; // By pass TOF PID selection Configurable cPIDcutType{"cPIDcutType", 2, "cPIDcutType = 1 for square cut, 2 for circular cut"}; // By pass TOF PID selection Configurable ispTdepPID{"ispTdepPID", false, "enable pT dependent PID"}; + // Were loose top-level Configurables; folded in here as unused (no call + // sites reference them) and logically the same "Kaon PID" kind as the + // kaonTPCPIDcuts/kaonTOFPIDcuts group below. + Configurable cfgTPCNsigmaKaon{"cfgTPCNsigmaKaon", 3.0f, "TPC Kaon PID"}; + Configurable cfgTOFNsigmaKaon{"cfgTOFNsigmaKaon", 3.0f, "TOF Kaon PID"}; // Kaon Configurable> kaonTPCPIDpTintv{"kaonTPCPIDpTintv", {0.5f}, "pT intervals for Kaon TPC PID cuts"}; @@ -213,10 +167,28 @@ struct HResonanceCorrelationFilter { Configurable> pionTOFPIDcuts{"pionTOFPIDcuts", {2}, "nSigma list for Pion TOF PID cuts"}; Configurable> pionTPCTOFCombinedpTintv{"pionTPCTOFCombinedpTintv", {999.0f}, "pT intervals for Pion TPC-TOF PID cuts"}; Configurable> pionTPCTOFCombinedPIDcuts{"pionTPCTOFCombinedPIDcuts", {2}, "nSigma list for Pion TPC-TOF PID cuts"}; + + // Associated kaon identification (formerly trackSelections; mirrors the + // pion selection above, with the accept/reject roles swapped: accept + // kaon-consistent tracks, reject pion-/proton-consistent ones). Selected + // via the Species template tag on isValidAssocTrack() below -- + // production path (which table rows get made) is still separate per + // process function, only the PID gate logic itself is shared. + Configurable assocKaonNSigmaTPCFOF{"assocKaonNSigmaTPCFOF", 3, "minimal n sigma in TOF and TPC for Kaon ID"}; + + // Associated pion identification (formerly trackSelections) + Configurable assocPionNSigmaTPCFOF{"assocPionNSigmaTPCFOF", 3, "minimal n sigma in TOF and TPC for Pion ID"}; + Configurable rejectSigma{"rejectSigma", 1, "n sigma for rejecting pion candidates"}; } configPID; - Configurable ccdburl{"ccdburl", "http://alice-ccdb.cern.ch", "url of the ccdb repository to use"}; - Configurable parameterCCDBPath{"parameterCCDBPath", "Users/k/kcui/LHC25b4a/parameter", "Path of the mean and sigma"}; + // Unused in this file today -- nothing here reads from CCDB any more + // (Zorro was the only consumer and has been removed). Kept (grouped, + // rather than removed) since that's outside today's ask. + struct : ConfigurableGroup { + std::string prefix = "ccdbConfigurations"; + Configurable ccdburl{"ccdburl", "http://alice-ccdb.cern.ch", "url of the ccdb repository to use"}; + Configurable parameterCCDBPath{"parameterCCDBPath", "Users/k/kcui/LHC25b4a/parameter", "Path of the mean and sigma"}; + } ccdbConfigurations; // must include windows for background and peak // @@ -229,7 +201,10 @@ struct HResonanceCorrelationFilter { // mirror the analysis task's own massWindowConfigurationsPhi/Kstar // (sigma = PDG Gamma/2.355 placeholder -- refit from your own peak and // update both files together, they must agree on what "sigma" means). - Configurable maxMassNSigma{"maxMassNSigma", 12.0f, "max mass region to be considered for further analysis, in units of sigma"}; + struct : ConfigurableGroup { + std::string prefix = "massWindowConfigurations"; + Configurable maxMassNSigma{"maxMassNSigma", 12.0f, "max mass region to be considered for further analysis, in units of sigma"}; + } massWindowConfigurations; struct : ConfigurableGroup { std::string prefix = "massWindowConfigurationsPhi"; @@ -254,24 +229,40 @@ struct HResonanceCorrelationFilter { // For extracting strangeness mass QA plots struct : ConfigurableGroup { + std::string prefix = "axesConfigurations"; ConfigurableAxis axisPtQA{"axisPtQA", {VARIABLE_WIDTH, 0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.7f, 0.8f, 0.9f, 1.0f, 1.1f, 1.2f, 1.3f, 1.4f, 1.5f, 1.6f, 1.7f, 1.8f, 1.9f, 2.0f, 2.2f, 2.4f, 2.6f, 2.8f, 3.0f, 3.2f, 3.4f, 3.6f, 3.8f, 4.0f, 4.4f, 4.8f, 5.2f, 5.6f, 6.0f, 6.5f, 7.0f, 7.5f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 17.0f, 19.0f, 21.0f, 23.0f, 25.0f, 30.0f, 35.0f, 40.0f, 50.0f}, "pt axis for QA histograms"}; - ConfigurableAxis axisPhiMass{"axisPhiMass", {200, 0.99f, 1.08f}, "M(K^{+}K^{-})"}; - ConfigurableAxis axisKstarMass{"axisKstarMass", {200, 0.75f, 1.05f}, "M(K#pi)"}; - ConfigurableAxis axisMult{"axisMult", {VARIABLE_WIDTH, 0.0f, 0.01f, 1.0f, 10.0f, 20.0f, 30.0f, 40.0f, 50.0f, 70.0f, 100.0f}, "Centrality percentile bins"}; + // Daughter/trigger-track QA axes (Phi->KK, K*0->Kpi, trigger hadrons): + // each is a 1D QA histogram's own axis, booked in init(). + ConfigurableAxis axisEtaQA{"axisEtaQA", {100, -1.0f, 1.0f}, "#eta"}; + ConfigurableAxis axisNSigmaQA{"axisNSigmaQA", {100, -10.0f, 10.0f}, "n#sigma"}; + ConfigurableAxis axisDCAxyQA{"axisDCAxyQA", {200, -0.5f, 0.5f}, "DCA_{xy} (cm)"}; + ConfigurableAxis axisDCAzQA{"axisDCAzQA", {200, -0.5f, 0.5f}, "DCA_{z} (cm)"}; + ConfigurableAxis axisTPCCrossedRowsQA{"axisTPCCrossedRowsQA", {160, 0, 160}, "TPC crossed rows"}; } axesConfigurations; // QA - Configurable doTrueSelectionInMass{"doTrueSelectionInMass", false, "Fill mass histograms only with true primary Particles for MC"}; - // Do declarative selections for DCAs, if possible - Filter preFilterTracks = nabs(aod::track::dcaXY) < trackSelections.dcaXYconstant + trackSelections.dcaXYpTdep * nabs(aod::track::signed1Pt); + struct : ConfigurableGroup { + std::string prefix = "qaConfigurations"; + Configurable doTrueSelectionInMass{"doTrueSelectionInMass", false, "Fill mass histograms only with true primary Particles for MC"}; + } qaConfigurations; - using FullTracks = soa::Join; - using FullTracksMC = soa::Join; + // Do declarative selections for DCAs, if possible + Filter acceptanceFilter = (nabs(aod::track::eta) < configTracks.cfgCutEta && aod::track::pt > configTracks.cMinPtcut) && + (nabs(aod::track::dcaXY) < configTracks.dcaXYconstant + configTracks.dcaXYpTdep * nabs(aod::track::signed1Pt)) && + (nabs(aod::track::dcaZ) < configTracks.cMaxDCAzToPVcut); + + // All four aliases below join TrackSelection + TrackSelectionExtension so + // that trackCut() -- the shared "is this a good, well-reconstructed track" + // baseline -- is callable (and applied) on trigger and associated-particle + // tracks exactly as it already is on Phi/K*0 daughters (TrackCandidates), + // not just on the latter. + using FullTracks = soa::Join; + using FullTracksMC = soa::Join; using DauTracks = soa::Join; using DauTracksMC = soa::Join; // using IDTracks= soa::Join; // prepared for Bayesian PID - using IDTracks = soa::Join; - using IDTracksMC = soa::Join; + using IDTracks = soa::Join; + using IDTracksMC = soa::Join; Produces triggerTrack; Produces triggerTrackExtra; @@ -287,10 +278,6 @@ struct HResonanceCorrelationFilter { using MCEventCandidates = soa::Join; using MCTrackCandidates = soa::Filtered>; - Zorro zorro; - OutputObj zorroSummary{"zorroSummary"}; - int mRunNumber = -1; - struct TriggCandidate { float pt = 0.f; int collisionId = -1; @@ -304,44 +291,125 @@ struct HResonanceCorrelationFilter { void init(InitContext const&) { - rctChecker.init(configEvents.cfgEvtRCTFlagCheckerLabel, configEvents.cfgEvtRCTFlagCheckerZDCCheck, configEvents.cfgEvtRCTFlagCheckerLimitAcceptAsBad); - histos.add("CollCutCounts", "No. of event after cuts", kTH1I, {{10, 0, 10}}); histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(1, "All Events"); histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(2, "|Vz| < cut"); histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(3, "kIsTriggerTVX"); histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(4, "kNoTimeFrameBorder"); histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(5, "kNoITSROFrameBorder"); - histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(6, "rctChecker"); - histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(7, "sel8"); - histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(8, "IsINELgt0"); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(6, "sel8"); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(7, "kNoSameBunchPileup"); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(8, "kIsGoodZvtxFT0vsPV"); histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(9, "All Passed Events"); - zorroSummary.setObject(zorro.getZorroSummary()); - mRunNumber = -1; - - mMinMassPhi = massWindowConfigurationsPhi.peakMass - maxMassNSigma * massWindowConfigurationsPhi.sigma; - mMaxMassPhi = massWindowConfigurationsPhi.peakMass + maxMassNSigma * massWindowConfigurationsPhi.sigma; - mMinMassKstar = massWindowConfigurationsKstar.peakMass - maxMassNSigma * massWindowConfigurationsKstar.sigma; - mMaxMassKstar = massWindowConfigurationsKstar.peakMass + maxMassNSigma * massWindowConfigurationsKstar.sigma; + mMinMassPhi = massWindowConfigurationsPhi.peakMass - massWindowConfigurations.maxMassNSigma * massWindowConfigurationsPhi.sigma; + mMaxMassPhi = massWindowConfigurationsPhi.peakMass + massWindowConfigurations.maxMassNSigma * massWindowConfigurationsPhi.sigma; + mMinMassKstar = massWindowConfigurationsKstar.peakMass - massWindowConfigurations.maxMassNSigma * massWindowConfigurationsKstar.sigma; + mMaxMassKstar = massWindowConfigurationsKstar.peakMass + massWindowConfigurations.maxMassNSigma * massWindowConfigurationsKstar.sigma; LOGF(info, "Assoc mass pre-cut windows: Phi [%.4f, %.4f] GeV, K*0 [%.4f, %.4f] GeV", mMinMassPhi, mMaxMassPhi, mMinMassKstar, mMaxMassKstar); + + // QA histograms for Phi (K+K-) and K*0 (K-pi) daughter tracks: booked once + // per daughter species (Phi's kaon, Kstar's kaon, Kstar's pion), filled at + // the point each candidate passes all cuts (including the mass window) in + // processPhis/processKstars (and their MC counterparts). + auto bookDaughterQA = [&](const std::string& dir) { + histos.add((dir + "/hPt").c_str(), "p_{T}", kTH1F, {axesConfigurations.axisPtQA}); + histos.add((dir + "/hEta").c_str(), "#eta", kTH1F, {axesConfigurations.axisEtaQA}); + histos.add((dir + "/hTPCNSigma").c_str(), "TPC n#sigma", kTH1F, {axesConfigurations.axisNSigmaQA}); + histos.add((dir + "/hTOFNSigma").c_str(), "TOF n#sigma", kTH1F, {axesConfigurations.axisNSigmaQA}); + histos.add((dir + "/hDCAxy").c_str(), "DCA_{xy}", kTH1F, {axesConfigurations.axisDCAxyQA}); + histos.add((dir + "/hDCAz").c_str(), "DCA_{z}", kTH1F, {axesConfigurations.axisDCAzQA}); + histos.add((dir + "/hTPCCrossedRows").c_str(), "TPC crossed rows", kTH1F, {axesConfigurations.axisTPCCrossedRowsQA}); + // 2D pT-binned QA, same variables as above, to see pT dependence + histos.add((dir + "/hPtVsDCAxy").c_str(), "p_{T} vs DCA_{xy}", kTH2F, {axesConfigurations.axisPtQA, axesConfigurations.axisDCAxyQA}); + histos.add((dir + "/hPtVsDCAz").c_str(), "p_{T} vs DCA_{z}", kTH2F, {axesConfigurations.axisPtQA, axesConfigurations.axisDCAzQA}); + histos.add((dir + "/hPtVsTPCNSigma").c_str(), "p_{T} vs TPC n#sigma", kTH2F, {axesConfigurations.axisPtQA, axesConfigurations.axisNSigmaQA}); + histos.add((dir + "/hPtVsTOFNSigma").c_str(), "p_{T} vs TOF n#sigma", kTH2F, {axesConfigurations.axisPtQA, axesConfigurations.axisNSigmaQA}); + }; + bookDaughterQA("QA/Phi/Kaon"); + bookDaughterQA("QA/Kstar/Kaon"); + bookDaughterQA("QA/Kstar/Pion"); + + // Trigger hadron QA: no PID applied to trigger tracks, so only the 5 + // non-PID variables are booked (pt, eta, dcaXY, dcaZ, TPC crossed rows), + // plus the pT-binned 2D versions of DCAxy/DCAz (no nSigma -- no PID here). + histos.add("QA/TriggerHadron/hPt", "p_{T}", kTH1F, {axesConfigurations.axisPtQA}); + histos.add("QA/TriggerHadron/hEta", "#eta", kTH1F, {axesConfigurations.axisEtaQA}); + histos.add("QA/TriggerHadron/hDCAxy", "DCA_{xy}", kTH1F, {axesConfigurations.axisDCAxyQA}); + histos.add("QA/TriggerHadron/hDCAz", "DCA_{z}", kTH1F, {axesConfigurations.axisDCAzQA}); + histos.add("QA/TriggerHadron/hTPCCrossedRows", "TPC crossed rows", kTH1F, {axesConfigurations.axisTPCCrossedRowsQA}); + histos.add("QA/TriggerHadron/hPtVsDCAxy", "p_{T} vs DCA_{xy}", kTH2F, {axesConfigurations.axisPtQA, axesConfigurations.axisDCAxyQA}); + histos.add("QA/TriggerHadron/hPtVsDCAz", "p_{T} vs DCA_{z}", kTH2F, {axesConfigurations.axisPtQA, axesConfigurations.axisDCAzQA}); } - void initCCDB(aod::BCsWithTimestamps::iterator const& bc) + // HIST() needs a compile-time literal path, so each daughter species gets + // its own explicit fill function rather than one generic/parameterized + // helper. Kaon QA uses the Ka PID accessors, pion QA uses the Pi ones. + template + void fillPhiKaonQA(const Track& track) { - if (mRunNumber == bc.runNumber()) { - return; - } + histos.fill(HIST("QA/Phi/Kaon/hPt"), track.pt()); + histos.fill(HIST("QA/Phi/Kaon/hEta"), track.eta()); + histos.fill(HIST("QA/Phi/Kaon/hTPCNSigma"), track.tpcNSigmaKa()); + histos.fill(HIST("QA/Phi/Kaon/hTOFNSigma"), track.tofNSigmaKa()); + histos.fill(HIST("QA/Phi/Kaon/hDCAxy"), track.dcaXY()); + histos.fill(HIST("QA/Phi/Kaon/hDCAz"), track.dcaZ()); + histos.fill(HIST("QA/Phi/Kaon/hTPCCrossedRows"), track.tpcNClsCrossedRows()); + histos.fill(HIST("QA/Phi/Kaon/hPtVsDCAxy"), track.pt(), track.dcaXY()); + histos.fill(HIST("QA/Phi/Kaon/hPtVsDCAz"), track.pt(), track.dcaZ()); + histos.fill(HIST("QA/Phi/Kaon/hPtVsTPCNSigma"), track.pt(), track.tpcNSigmaKa()); + histos.fill(HIST("QA/Phi/Kaon/hPtVsTOFNSigma"), track.pt(), track.tofNSigmaKa()); + } - zorro.initCCDB(ccdb.service, bc.runNumber(), bc.timestamp(), zorroMask.value); - zorro.populateHistRegistry(histos, bc.runNumber()); + template + void fillKstarKaonQA(const Track& track) + { + histos.fill(HIST("QA/Kstar/Kaon/hPt"), track.pt()); + histos.fill(HIST("QA/Kstar/Kaon/hEta"), track.eta()); + histos.fill(HIST("QA/Kstar/Kaon/hTPCNSigma"), track.tpcNSigmaKa()); + histos.fill(HIST("QA/Kstar/Kaon/hTOFNSigma"), track.tofNSigmaKa()); + histos.fill(HIST("QA/Kstar/Kaon/hDCAxy"), track.dcaXY()); + histos.fill(HIST("QA/Kstar/Kaon/hDCAz"), track.dcaZ()); + histos.fill(HIST("QA/Kstar/Kaon/hTPCCrossedRows"), track.tpcNClsCrossedRows()); + histos.fill(HIST("QA/Kstar/Kaon/hPtVsDCAxy"), track.pt(), track.dcaXY()); + histos.fill(HIST("QA/Kstar/Kaon/hPtVsDCAz"), track.pt(), track.dcaZ()); + histos.fill(HIST("QA/Kstar/Kaon/hPtVsTPCNSigma"), track.pt(), track.tpcNSigmaKa()); + histos.fill(HIST("QA/Kstar/Kaon/hPtVsTOFNSigma"), track.pt(), track.tofNSigmaKa()); + } + + template + void fillKstarPionQA(const Track& track) + { + histos.fill(HIST("QA/Kstar/Pion/hPt"), track.pt()); + histos.fill(HIST("QA/Kstar/Pion/hEta"), track.eta()); + histos.fill(HIST("QA/Kstar/Pion/hTPCNSigma"), track.tpcNSigmaPi()); + histos.fill(HIST("QA/Kstar/Pion/hTOFNSigma"), track.tofNSigmaPi()); + histos.fill(HIST("QA/Kstar/Pion/hDCAxy"), track.dcaXY()); + histos.fill(HIST("QA/Kstar/Pion/hDCAz"), track.dcaZ()); + histos.fill(HIST("QA/Kstar/Pion/hTPCCrossedRows"), track.tpcNClsCrossedRows()); + histos.fill(HIST("QA/Kstar/Pion/hPtVsDCAxy"), track.pt(), track.dcaXY()); + histos.fill(HIST("QA/Kstar/Pion/hPtVsDCAz"), track.pt(), track.dcaZ()); + histos.fill(HIST("QA/Kstar/Pion/hPtVsTPCNSigma"), track.pt(), track.tpcNSigmaPi()); + histos.fill(HIST("QA/Kstar/Pion/hPtVsTOFNSigma"), track.pt(), track.tofNSigmaPi()); + } - mRunNumber = bc.runNumber(); + // Trigger hadrons carry no PID selection, so only the 5 non-PID variables + // are filled here. + template + void fillTriggerHadronQA(const Track& track) + { + histos.fill(HIST("QA/TriggerHadron/hPt"), track.pt()); + histos.fill(HIST("QA/TriggerHadron/hEta"), track.eta()); + histos.fill(HIST("QA/TriggerHadron/hDCAxy"), track.dcaXY()); + histos.fill(HIST("QA/TriggerHadron/hDCAz"), track.dcaZ()); + histos.fill(HIST("QA/TriggerHadron/hTPCCrossedRows"), track.tpcNClsCrossedRows()); + histos.fill(HIST("QA/TriggerHadron/hPtVsDCAxy"), track.pt(), track.dcaXY()); + histos.fill(HIST("QA/TriggerHadron/hPtVsDCAz"), track.pt(), track.dcaZ()); } template - bool isSelected(const Coll& collision, bool fillHist = true) + bool isSelectedEvents(const Coll& collision, bool fillHist = true) { auto applyCut = [&](bool enabled, bool condition, int bin) { if (!enabled) { @@ -379,15 +447,17 @@ struct HResonanceCorrelationFilter { return false; } - if (!applyCut(configEvents.cfgEvtIsRCTFlagpassed, rctChecker(collision), 5)) { + if (!applyCut(configEvents.cfgEvtSel8, collision.sel8(), 5)) { return false; } - if (!applyCut(configEvents.cfgEvtSel8, collision.sel8(), 6)) { + if (!applyCut(configEvents.cfgEvtNoSameBunchPileupCut, + collision.selection_bit(aod::evsel::kNoSameBunchPileup), 6)) { return false; } - if (!applyCut(configEvents.cfgEvtIsINELgt0, collision.isInelGt0(), 7)) { + if (!applyCut(configEvents.cfgEvtGoodZvtxFT0vsPVCut, + collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV), 7)) { return false; } @@ -398,87 +468,35 @@ struct HResonanceCorrelationFilter { return true; } - // this function allows for all event selections to be done in a modular way - template - bool isCollisionSelected(TCollision const& collision) - { - // ________________________________________________ - // Perform basic event selection - if (!collision.sel8()) { - return false; - } - if (std::abs(collision.posZ()) > eventSelections.zVertexCut) { - return false; - } - if (collision.centFT0M() > eventSelections.maxCentPercent || collision.centFT0M() < eventSelections.minCentPercent) { - return false; - } - if (!collision.isInelGt0() && eventSelections.selectINELgtZERO) { - return false; - } - if (!collision.selection_bit(aod::evsel::kIsGoodITSLayersAll) && eventSelections.requireAllGoodITSLayers) { - return false; - } - if (zorroMask.value != "") { - auto bc = collision.template bc_as(); - initCCDB(bc); - bool zorroSelected = zorro.isSelected(collision.template bc_as().globalBC()); /// Just let Zorro do the accounting - if (!zorroSelected) { - return false; - } - } - return true; - } - - // more event selections in Pb-Pb - template - bool isCollisionSelectedPbPb(TCollision const& collision) - { - if (!collision.selection_bit(aod::evsel::kIsTriggerTVX) && eventSelections.requireGoodTriggerTVX) /* FT0 vertex (acceptable FT0C-FT0A time difference) collisions */ - return false; - if (!collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll) && eventSelections.requireAllGoodITSLayers) // cut time intervals with dead ITS staves - return false; - if (!collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV) && eventSelections.requireGoodZvtxFT0vsPV) // removes collisions with large differences between z of PV by tracks and z of PV from FT0 A-C time difference - return false; - auto occupancy = collision.trackOccupancyInTimeRange(); - if (occupancy < cfgCutOccupancyLow || occupancy > cfgCutOccupancyHigh) /* Below min occupancy and Above max occupancy*/ - return false; - if (!collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) // reject collisions close to Time Frame borders - return false; - if (!collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) // reject events affected by the ITS ROF border - return false; - if (!collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) // rejects collisions which are associated with the same "found-by-T0" bunch crossing - return false; - return true; - } - template bool trackCut(const TrackType& track) { // basic track cuts - if (configTracks.cDCAr7SigCut && std::abs(track.dcaXY()) > (0.004f + 0.013f / (track.pt()))) // 7 - Sigma cut - { + // NOTE: pT-dependent |DCAxy| cut is NOT re-applied here -- it's already + // enforced unconditionally, for every track in every process function, + // by acceptanceFilter (Filter on aod::track::dcaXY using the live + // configTracks.dcaXYconstant/dcaXYpTdep). A second check here would be + // either a no-op (same cut) or a stale one (if those configurables are + // ever retuned and this duplicate doesn't track them). + if (configTracks.cfgGlobalWoDCATrack && !track.isGlobalTrackWoDCA()) { return false; } - if (configTracks.cTPCNClsFound && (track.tpcNClsFound() < configTracks.cMinTPCNClsFound)) { + if (configTracks.cfgPVContributor && !track.isPVContributor()) { return false; } if (track.tpcNClsCrossedRows() < configTracks.cfgMinCrossedRows) { return false; } - if (configTracks.cfgHasTOF && !track.hasTOF()) { + if (track.tpcChi2NCl() > configTracks.cfgMaxTPCChi2NCl) { return false; } - if (configTracks.cfgPrimaryTrack && !track.isPrimaryTrack()) { + if (track.itsChi2NCl() > configTracks.cfgMaxITSChi2NCl) { return false; } - if (configTracks.cfgGlobalWoDCATrack && !track.isGlobalTrackWoDCA()) { - return false; - } - if (configTracks.cfgPVContributor && !track.isPVContributor()) { + if (track.tpcCrossedRowsOverFindableCls() < configTracks.cfgMinTPCCrossedRowsOverFindableCls) { return false; } - if (configTracks.cfgGlobalTrack && !track.isGlobalTrack()) { + if (track.itsNCls() < configTracks.cfgMinITSNCls) { return false; } @@ -669,23 +687,28 @@ struct HResonanceCorrelationFilter { template bool isValidTrigger(TTrack const& track) { - if (track.eta() > generalSelections.triggerEtaMax || track.eta() < generalSelections.triggerEtaMin) { + // Shared "good track" baseline, applied identically across trigger, + // associated-particle, and resonance-daughter tracks (see trackCut()). + if (!trackCut(track)) { + return false; + } + if (track.eta() > configTracks.triggerEtaMax || track.eta() < configTracks.triggerEtaMin) { return false; } // if (track.sign()= 1 ) {continue;} - if (track.pt() > generalSelections.triggerPtCutMax || track.pt() < generalSelections.triggerPtCutMin) { + if (track.pt() > configTracks.triggerPtCutMax || track.pt() < configTracks.triggerPtCutMin) { return false; } - if (track.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) { + if (track.tpcNClsCrossedRows() < configTracks.minTPCNCrossedRows) { return false; // crossed rows } - if (!track.hasITS() && trackSelections.triggerRequireITS) { + if (!track.hasITS() && configTracks.triggerRequireITS) { return false; // skip, doesn't have ITS signal (skips lots of TPC-only!) } - if (track.tpcNClsShared() > trackSelections.triggerMaxTPCSharedClusters) { + if (track.tpcNClsShared() > configTracks.triggerMaxTPCSharedClusters) { return false; // skip, has shared clusters } - if (!(BIT_CHECK(track.itsClusterMap(), 0)) && trackSelections.triggerRequireL0) { + if (!(BIT_CHECK(track.itsClusterMap(), 0)) && configTracks.triggerRequireL0) { return false; // skip, doesn't have cluster in ITS L0 } return true; @@ -715,16 +738,21 @@ struct HResonanceCorrelationFilter { { static_assert(Species == AssocPion || Species == AssocKaon || Species == AssocHadron, "isValidAssocTrack: unknown species tag"); - if (assoc.eta() > generalSelections.assocEtaMax || assoc.eta() < generalSelections.assocEtaMin) { + // Shared "good track" baseline, applied identically across trigger, + // associated-particle, and resonance-daughter tracks (see trackCut()). + if (!trackCut(assoc)) { + return false; + } + if (assoc.eta() > configTracks.assocEtaMax || assoc.eta() < configTracks.assocEtaMin) { return false; } - if (assoc.pt() > generalSelections.assocPtCutMax || assoc.pt() < generalSelections.assocPtCutMin) { + if (assoc.pt() > configTracks.assocPtCutMax || assoc.pt() < configTracks.assocPtCutMin) { return false; } - if (assoc.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) { + if (assoc.tpcNClsCrossedRows() < configTracks.minTPCNCrossedRows) { return false; // crossed rows } - if (!assoc.hasITS() && trackSelections.assocRequireITS) { + if (!assoc.hasITS() && configTracks.assocRequireITS) { return false; // skip, doesn't have ITS signal (skips lots of TPC-only!) } @@ -733,26 +761,26 @@ struct HResonanceCorrelationFilter { if constexpr (requires { assoc.tofSignal(); } && !requires { assoc.mcParticle(); }) { if (assoc.tofSignal() > 0) { if constexpr (Species == AssocKaon) { - if (std::sqrt(assoc.tofNSigmaKa() * assoc.tofNSigmaKa() + assoc.tpcNSigmaKa() * assoc.tpcNSigmaKa()) > trackSelections.assocKaonNSigmaTPCFOF) + if (std::sqrt(assoc.tofNSigmaKa() * assoc.tofNSigmaKa() + assoc.tpcNSigmaKa() * assoc.tpcNSigmaKa()) > configPID.assocKaonNSigmaTPCFOF) return false; - if (assoc.tofNSigmaPr() < trackSelections.rejectSigma) + if (assoc.tofNSigmaPr() < configPID.rejectSigma) return false; - if (assoc.tpcNSigmaPr() < trackSelections.rejectSigma) + if (assoc.tpcNSigmaPr() < configPID.rejectSigma) return false; - if (assoc.tofNSigmaPi() < trackSelections.rejectSigma) + if (assoc.tofNSigmaPi() < configPID.rejectSigma) return false; - if (assoc.tpcNSigmaPi() < trackSelections.rejectSigma) + if (assoc.tpcNSigmaPi() < configPID.rejectSigma) return false; } else { - if (std::sqrt(assoc.tofNSigmaPi() * assoc.tofNSigmaPi() + assoc.tpcNSigmaPi() * assoc.tpcNSigmaPi()) > trackSelections.assocPionNSigmaTPCFOF) + if (std::sqrt(assoc.tofNSigmaPi() * assoc.tofNSigmaPi() + assoc.tpcNSigmaPi() * assoc.tpcNSigmaPi()) > configPID.assocPionNSigmaTPCFOF) return false; - if (assoc.tofNSigmaPr() < trackSelections.rejectSigma) + if (assoc.tofNSigmaPr() < configPID.rejectSigma) return false; - if (assoc.tpcNSigmaPr() < trackSelections.rejectSigma) + if (assoc.tpcNSigmaPr() < configPID.rejectSigma) return false; - if (assoc.tofNSigmaKa() < trackSelections.rejectSigma) + if (assoc.tofNSigmaKa() < configPID.rejectSigma) return false; - if (assoc.tpcNSigmaKa() < trackSelections.rejectSigma) + if (assoc.tpcNSigmaKa() < configPID.rejectSigma) return false; } nSigmaTPCTOF[4] = assoc.tofNSigmaPi(); @@ -761,18 +789,18 @@ struct HResonanceCorrelationFilter { nSigmaTPCTOF[7] = assoc.tofNSigmaEl(); } else { if constexpr (Species == AssocKaon) { - if (assoc.tpcNSigmaKa() > trackSelections.assocKaonNSigmaTPCFOF) + if (assoc.tpcNSigmaKa() > configPID.assocKaonNSigmaTPCFOF) return false; - if (assoc.tpcNSigmaPr() < trackSelections.rejectSigma) + if (assoc.tpcNSigmaPr() < configPID.rejectSigma) return false; - if (assoc.tpcNSigmaPi() < trackSelections.rejectSigma) + if (assoc.tpcNSigmaPi() < configPID.rejectSigma) return false; } else { - if (assoc.tpcNSigmaPi() > trackSelections.assocPionNSigmaTPCFOF) + if (assoc.tpcNSigmaPi() > configPID.assocPionNSigmaTPCFOF) return false; - if (assoc.tpcNSigmaPr() < trackSelections.rejectSigma) + if (assoc.tpcNSigmaPr() < configPID.rejectSigma) return false; - if (assoc.tpcNSigmaKa() < trackSelections.rejectSigma) + if (assoc.tpcNSigmaKa() < configPID.rejectSigma) return false; } } @@ -794,12 +822,26 @@ struct HResonanceCorrelationFilter { } } + // Rapidity under the species mass hypothesis; AssocHadron has no mass + // hypothesis to assume, so it's filled with a dummy value instead. + float rapidity = -999.f; + if constexpr (Species == AssocKaon) { + rapidity = LorentzVectorPtEtaPhiMass(assoc.pt(), assoc.eta(), assoc.phi(), + o2::constants::physics::MassKPlus) + .Rapidity(); + } else if constexpr (Species == AssocPion) { + rapidity = LorentzVectorPtEtaPhiMass(assoc.pt(), assoc.eta(), assoc.phi(), + o2::constants::physics::MassPiPlus) + .Rapidity(); + } + assocHadrons( assoc.collisionId(), physicalPrimary, assoc.globalIndex(), origPt, - code); + code, + rapidity); assocPID( nSigmaTPCTOF[0], nSigmaTPCTOF[1], @@ -813,10 +855,10 @@ struct HResonanceCorrelationFilter { } // for real data processing - void processTriggers(soa::Join::iterator const& collision, soa::Filtered const& tracks, aod::BCsWithTimestamps const&) + void processTriggers(soa::Join::iterator const& collision, soa::Filtered const& tracks) { triggerCandidates.clear(); - if (((doPPAnalysis && !isCollisionSelected(collision))) || (!doPPAnalysis && !isCollisionSelectedPbPb(collision))) { + if (!isSelectedEvents(collision)) { return; } @@ -827,6 +869,7 @@ struct HResonanceCorrelationFilter { for (auto const& track : tracks) { if (!isValidTrigger(track)) continue; + fillTriggerHadronQA(track); thisTrigg.pt = track.pt(); thisTrigg.trackId = track.globalIndex(); thisTrigg.collisionId = track.collisionId(); @@ -851,10 +894,10 @@ struct HResonanceCorrelationFilter { } // for MC processing - void processTriggersMC(soa::Join::iterator const& collision, soa::Filtered const& tracks, aod::McParticles const&, aod::BCsWithTimestamps const&) + void processTriggersMC(soa::Join::iterator const& collision, soa::Filtered const& tracks, aod::McParticles const&) { triggerCandidates.clear(); - if (((doPPAnalysis && !isCollisionSelected(collision))) || (!doPPAnalysis && !isCollisionSelectedPbPb(collision))) { + if (!isSelectedEvents(collision)) { return; } @@ -865,6 +908,7 @@ struct HResonanceCorrelationFilter { for (auto const& track : tracks) { if (!isValidTrigger(track)) continue; + fillTriggerHadronQA(track); thisTrigg.pt = track.pt(); thisTrigg.trackId = track.globalIndex(); thisTrigg.collisionId = track.collisionId(); @@ -892,25 +936,12 @@ struct HResonanceCorrelationFilter { } } - void processAssocPions(soa::Join::iterator const& collision, soa::Filtered const& tracks, aod::BCsWithTimestamps const&) + void processAssocPions(soa::Join::iterator const& collision, soa::Filtered const& tracks) { - // Load parameters for sideband subtraction - auto bc = collision.bc_as(); - // Perform basic event selection - if (!collision.sel8()) { + // Perform basic event selection (fillHist=false: processTriggers/processTriggersMC own the shared CollCutCounts fill) + if (!isSelectedEvents(collision, false)) { return; } - // No need to correlate stuff that's in far collisions - if (std::abs(collision.posZ()) > eventSelections.zVertexCut) { - return; - } - if (zorroMask.value != "") { - initCCDB(bc); - bool zorroSelected = zorro.isSelected(collision.bc_as().globalBC()); /// Just let Zorro do the accounting - if (!zorroSelected) { - return; - } - } /// _________________________________________________ /// Step 1: Populate table with trigger tracks @@ -920,25 +951,12 @@ struct HResonanceCorrelationFilter { } } - void processAssocPionsMC(soa::Join::iterator const& collision, soa::Filtered const& tracks, aod::McParticles const&, aod::BCsWithTimestamps const&) + void processAssocPionsMC(soa::Join::iterator const& collision, soa::Filtered const& tracks, aod::McParticles const&) { - // Load parameters for sideband subtraction - auto bc = collision.bc_as(); - // Perform basic event selection - if (!collision.sel8()) { + // Perform basic event selection (fillHist=false: processTriggers/processTriggersMC own the shared CollCutCounts fill) + if (!isSelectedEvents(collision, false)) { return; } - // No need to correlate stuff that's in far collisions - if (std::abs(collision.posZ()) > eventSelections.zVertexCut) { - return; - } - if (zorroMask.value != "") { - initCCDB(bc); - bool zorroSelected = zorro.isSelected(collision.bc_as().globalBC()); /// Just let Zorro do the accounting - if (!zorroSelected) { - return; - } - } /// _________________________________________________ /// Step 1: Populate table with trigger tracks @@ -948,25 +966,12 @@ struct HResonanceCorrelationFilter { } } - void processAssocKaons(soa::Join::iterator const& collision, soa::Filtered const& tracks, aod::BCsWithTimestamps const&) + void processAssocKaons(soa::Join::iterator const& collision, soa::Filtered const& tracks) { - // Load parameters for sideband subtraction - auto bc = collision.bc_as(); - // Perform basic event selection - if (!collision.sel8()) { + // Perform basic event selection (fillHist=false: processTriggers/processTriggersMC own the shared CollCutCounts fill) + if (!isSelectedEvents(collision, false)) { return; } - // No need to correlate stuff that's in far collisions - if (std::abs(collision.posZ()) > eventSelections.zVertexCut) { - return; - } - if (zorroMask.value != "") { - initCCDB(bc); - bool zorroSelected = zorro.isSelected(collision.bc_as().globalBC()); /// Just let Zorro do the accounting - if (!zorroSelected) { - return; - } - } /// _________________________________________________ /// Step 1: Populate table with trigger tracks @@ -976,25 +981,12 @@ struct HResonanceCorrelationFilter { } } - void processAssocKaonsMC(soa::Join::iterator const& collision, soa::Filtered const& tracks, aod::McParticles const&, aod::BCsWithTimestamps const&) + void processAssocKaonsMC(soa::Join::iterator const& collision, soa::Filtered const& tracks, aod::McParticles const&) { - // Load parameters for sideband subtraction - auto bc = collision.bc_as(); - // Perform basic event selection - if (!collision.sel8()) { + // Perform basic event selection (fillHist=false: processTriggers/processTriggersMC own the shared CollCutCounts fill) + if (!isSelectedEvents(collision, false)) { return; } - // No need to correlate stuff that's in far collisions - if (std::abs(collision.posZ()) > eventSelections.zVertexCut) { - return; - } - if (zorroMask.value != "") { - initCCDB(bc); - bool zorroSelected = zorro.isSelected(collision.bc_as().globalBC()); /// Just let Zorro do the accounting - if (!zorroSelected) { - return; - } - } /// _________________________________________________ /// Step 1: Populate table with trigger tracks @@ -1004,25 +996,12 @@ struct HResonanceCorrelationFilter { } } - void processAssocHadrons(soa::Join::iterator const& collision, soa::Filtered const& tracks, aod::BCsWithTimestamps const&) + void processAssocHadrons(soa::Join::iterator const& collision, soa::Filtered const& tracks) { - // Load parameters for sideband subtraction - auto bc = collision.bc_as(); - // Perform basic event selection - if (!collision.sel8()) { - return; - } - // No need to correlate stuff that's in far collisions - if (std::abs(collision.posZ()) > eventSelections.zVertexCut) { + // Perform basic event selection (fillHist=false: processTriggers/processTriggersMC own the shared CollCutCounts fill) + if (!isSelectedEvents(collision, false)) { return; } - if (zorroMask.value != "") { - initCCDB(bc); - bool zorroSelected = zorro.isSelected(collision.bc_as().globalBC()); /// Just let Zorro do the accounting - if (!zorroSelected) { - return; - } - } /// _________________________________________________ /// Step 1: Populate table with trigger tracks @@ -1031,25 +1010,12 @@ struct HResonanceCorrelationFilter { continue; } } - void processAssocHadronsMC(soa::Join::iterator const& collision, soa::Filtered const& tracks, aod::McParticles const&, aod::BCsWithTimestamps const&) + void processAssocHadronsMC(soa::Join::iterator const& collision, soa::Filtered const& tracks, aod::McParticles const&) { - // Load parameters for sideband subtraction - auto bc = collision.bc_as(); - // Perform basic event selection - if (!collision.sel8()) { - return; - } - // No need to correlate stuff that's in far collisions - if (std::abs(collision.posZ()) > eventSelections.zVertexCut) { + // Perform basic event selection (fillHist=false: processTriggers/processTriggersMC own the shared CollCutCounts fill) + if (!isSelectedEvents(collision, false)) { return; } - if (zorroMask.value != "") { - initCCDB(bc); - bool zorroSelected = zorro.isSelected(collision.bc_as().globalBC()); /// Just let Zorro do the accounting - if (!zorroSelected) { - return; - } - } /// _________________________________________________ /// Step 1: Populate table with trigger tracks @@ -1062,7 +1028,7 @@ struct HResonanceCorrelationFilter { void processPhis(EventCandidates::iterator const& collision, TrackCandidates const& tracks) { - if (!isSelected(collision)) { + if (!isSelectedEvents(collision, false)) { return; } @@ -1133,6 +1099,10 @@ struct HResonanceCorrelationFilter { continue; } + // Daughter QA (both legs are kaons for Phi) + fillPhiKaonQA(trk1); + fillPhiKaonQA(trk2); + assocPhis( collision.globalIndex(), false, @@ -1141,6 +1111,7 @@ struct HResonanceCorrelationFilter { phi.Eta(), phi.Phi(), invMass, + phi.Rapidity(), trk1.globalIndex(), trk2.globalIndex()); } @@ -1156,7 +1127,7 @@ struct HResonanceCorrelationFilter { return; } - if (!isSelected(collision)) { + if (!isSelectedEvents(collision, false)) { return; } @@ -1219,6 +1190,10 @@ struct HResonanceCorrelationFilter { continue; } + // Daughter QA (both legs are kaons for Phi) + fillPhiKaonQA(trk1); + fillPhiKaonQA(trk2); + bool mcTruePhi = false; bool mcPhysicalPrimary = false; @@ -1259,6 +1234,7 @@ struct HResonanceCorrelationFilter { phi.Eta(), phi.Phi(), invMass, + phi.Rapidity(), trk1.globalIndex(), trk2.globalIndex()); } @@ -1271,7 +1247,7 @@ struct HResonanceCorrelationFilter { void processKstars(EventCandidates::iterator const& collision, TrackCandidates const& tracks) { - if (!isSelected(collision)) { + if (!isSelectedEvents(collision, false)) { return; } @@ -1332,8 +1308,15 @@ struct HResonanceCorrelationFilter { continue; } + // Daughter QA (kaon leg and pion leg filled separately) + fillKstarKaonQA(kaonTrack); + fillKstarPionQA(pionTrack); + auto const& posTrack = (kaonTrack.sign() > 0) ? kaonTrack : pionTrack; auto const& negTrack = (kaonTrack.sign() > 0) ? pionTrack : kaonTrack; + // Rapidity under whichever mass hypothesis (kaon or pion) was + // actually assigned to that daughter for this hypothesis -- matches + // posTrack/negTrack above, not a fixed species per pos/neg slot. assocKstars( collision.globalIndex(), @@ -1343,6 +1326,7 @@ struct HResonanceCorrelationFilter { kstar.Eta(), kstar.Phi(), invMass, + kstar.Rapidity(), posTrack.globalIndex(), negTrack.globalIndex()); } @@ -1359,7 +1343,7 @@ struct HResonanceCorrelationFilter { return; } - if (!isSelected(collision)) { + if (!isSelectedEvents(collision, false)) { return; } @@ -1413,6 +1397,10 @@ struct HResonanceCorrelationFilter { continue; } + // Daughter QA (kaon leg and pion leg filled separately) + fillKstarKaonQA(kaonTrack); + fillKstarPionQA(pionTrack); + bool mcTrueKstar = false; bool mcPhysicalPrimary = false; @@ -1456,6 +1444,7 @@ struct HResonanceCorrelationFilter { kstar.Eta(), kstar.Phi(), invMass, + kstar.Rapidity(), posTrack.globalIndex(), negTrack.globalIndex()); } diff --git a/PWGLF/Tasks/Resonances/hResonanceCorrelation.cxx b/PWGLF/Tasks/Resonances/hResonanceCorrelation.cxx index 35f54137039..c631ee15932 100644 --- a/PWGLF/Tasks/Resonances/hResonanceCorrelation.cxx +++ b/PWGLF/Tasks/Resonances/hResonanceCorrelation.cxx @@ -97,7 +97,6 @@ struct HResonanceCorrelation { struct : ConfigurableGroup { std::string prefix = "masterConfigurations"; - Configurable doPPAnalysis{"doPPAnalysis", true, "if in pp, set to true"}; Configurable collisionHasTriggOrAssoc{"collisionHasTriggOrAssoc", 0, "require the collisions containing (0:no requirement 1:trig 2:assoc 3:trig or assoc 4:trig and assoc"}; Configurable doFullCorrelationStudy{"doFullCorrelationStudy", true, "if true, do full correlation study by creating all THnSparse histograms for the correlation function"}; Configurable doCorrelationHadron{"doCorrelationHadron", false, "do Hadron correlation"}; @@ -106,11 +105,14 @@ struct HResonanceCorrelation { Configurable doCorrelationPion{"doCorrelationPion", false, "do Pion correlation"}; Configurable doCorrelationKaon{"doCorrelationKaon", false, "do Kaon correlation"}; Configurable doGenEventSelection{"doGenEventSelection", true, "use event selections when performing closure test for the gen events"}; - Configurable selectINELgtZERO{"selectINELgtZERO", true, "select INEL>0 events"}; - Configurable zVertexCut{"zVertexCut", 10, "Cut on PV position"}; - Configurable requireAllGoodITSLayers{"requireAllGoodITSLayers", false, " require that in the event all ITS are good"}; - Configurable requireGoodTriggerTVX{"requireGoodTriggerTVX", false, " require acceptable FT0C-FT0A time difference"}; - Configurable requireGoodZvtxFT0vsPV{"requireGoodZvtxFT0vsPV", false, " require small difference between z-vertex from PV and from FT0"}; + // Used by the generator-level (MC-truth) selection in processPrediction, + // not by isSelectedEvents() below -- that's a different, reconstructed- + // level selection now driven entirely by configEvents.* to match the + // filter task (hResonanceCorrelationFilter.cxx) exactly. + Configurable selectINELgtZERO{"selectINELgtZERO", true, "select INEL>0 events (generator level, see processPrediction)"}; + // Still used directly by the closure-test/prediction blocks (reconstructed + // vs. generated z-vertex checks), independent of isSelectedEvents(). + Configurable zVertexCut{"zVertexCut", 10, "Cut on PV position (closure test / prediction only)"}; Configurable skipUnderOverflowInTHn{"skipUnderOverflowInTHn", false, "skip under/overflow in THns"}; Configurable mixingParameter{"mixingParameter", 10, "how many events are mixed"}; Configurable doMCassociation{"doMCassociation", false, "fill everything only for MC associated"}; @@ -127,7 +129,6 @@ struct HResonanceCorrelation { Configurable doAssocPhysicalPrimaryInGen{"doAssocPhysicalPrimaryInGen", false, "require physical primary for associated particles in Generated Partilces"}; Configurable doAutocorrelationRejection{"doAutocorrelationRejection", true, "reject pairs where trigger Id is the same as daughter particle Id"}; Configurable doMixingQAandEventQA{"doMixingQAandEventQA", true, "if true, add EvnetQA and MixingQA hist to histos"}; - Configurable doITSClustersQA{"doITSClustersQA", true, "if true, add ITSCluster hist to histos"}; Configurable doDeltaPhiStarCheck{"doDeltaPhiStarCheck", false, "if true, create and fill delta phi star histograms"}; Configurable triggerBinToSelect{"triggerBinToSelect", 0, "trigger bin to select on if processSelectEventWithTrigger enabled"}; @@ -136,9 +137,23 @@ struct HResonanceCorrelation { Configurable ySel{"ySel", 0.5, "Selection in rapidity for consistency checks"}; Configurable useTheLeadingParticleAsTrigger{"useTheLeadingParticleAsTrigger", false, "if true, use the leading particle in the event as trigger particle"}; - // used for event selections in Pb-Pb - Configurable cfgCutOccupancyHigh{"cfgCutOccupancyHigh", 3000, "High cut on TPC occupancy"}; - Configurable cfgCutOccupancyLow{"cfgCutOccupancyLow", 0, "Low cut on TPC occupancy"}; + + // Reconstructed-event selection cuts -- deliberately the same cut set and + // names as configEvents in hResonanceCorrelationFilter.cxx, so a collision + // that was selected to produce the trigger/associated/candidate tables is + // selected identically here (previously this task re-derived its own, + // different cut list via isisCollisionSelect()/isisCollisionSelectPbPb(), + // which could disagree with the filter task's selection). + struct : ConfigurableGroup { + std::string prefix = "configEvents"; + Configurable cfgEvtZvtx{"cfgEvtZvtx", 10.0f, "Evt sel: Max. z-Vertex (cm)"}; + Configurable cfgEvtTriggerTVXSel{"cfgEvtTriggerTVXSel", true, "Evt sel: triggerTVX selection (MB)"}; + Configurable cfgEvtNoTFBorderCut{"cfgEvtNoTFBorderCut", true, "Evt sel: apply TF border cut"}; + Configurable cfgEvtNoITSROFrameBorderCut{"cfgEvtNoITSROFrameBorderCut", true, "Evt sel: apply NoITSRO border cut"}; + Configurable cfgEvtSel8{"cfgEvtSel8", true, "Evt Sel 8 check for offline selection"}; + Configurable cfgEvtNoSameBunchPileupCut{"cfgEvtNoSameBunchPileupCut", true, "Evt sel: reject collisions associated with the same found-by-T0 bunch crossing"}; + Configurable cfgEvtGoodZvtxFT0vsPVCut{"cfgEvtGoodZvtxFT0vsPVCut", true, "Evt sel: require small difference between z-vertex from PV and from FT0"}; + } configEvents; // Axes - configurable for smaller sizes struct : ConfigurableGroup { @@ -348,6 +363,11 @@ struct HResonanceCorrelation { static constexpr int IndexHadron = 4; uint16_t doCorrelation = 0; + // Which species' processSameEventH() is responsible for filling the + // shared CollCutCounts/EventQA histograms for a given collision, computed + // once in init() -- see the comment there. -1 means none of the same-event + // process functions are actually enabled (bit + switch), so nothing fills them. + int eventQAOwnerIndex = -1; int mRunNumber = 0; int mRunNumberZorro = 0; @@ -465,6 +485,27 @@ struct HResonanceCorrelation { if (masterConfigurations.doCorrelationHadron) SETBIT(doCorrelation, IndexHadron); + // Pick exactly one "owner" species for the shared CollCutCounts/EventQA + // fills: the highest-priority species (Phi > Kstar > Pion > Kaon > + // Hadron) that will actually run its processSameEventH() (both its + // doCorrelation bit AND its PROCESS_SWITCH on). Every processSameEventH + // calls isSelectedEvents(collision, eventQAOwnerIndex == IndexX) -- only + // the owner's call passes fillHist=true, so a collision examined by + // several same-event process functions in the same run (e.g. Phi and + // K*0 both enabled) is still only counted once, instead of once per + // enabled species. + if (TESTBIT(doCorrelation, IndexPhi) && doprocessSameEventHPhis) { + eventQAOwnerIndex = IndexPhi; + } else if (TESTBIT(doCorrelation, IndexKstar) && doprocessSameEventHKstars) { + eventQAOwnerIndex = IndexKstar; + } else if (TESTBIT(doCorrelation, IndexPion) && doprocessSameEventHPions) { + eventQAOwnerIndex = IndexPion; + } else if (TESTBIT(doCorrelation, IndexKaon) && doprocessSameEventHKaons) { + eventQAOwnerIndex = IndexKaon; + } else if (TESTBIT(doCorrelation, IndexHadron) && doprocessSameEventHHadrons) { + eventQAOwnerIndex = IndexHadron; + } + // Store axis ranges to prevent spurious filling // axis status: // --- Delta-phi is safe -> math forbids insanity @@ -642,13 +683,20 @@ struct HResonanceCorrelation { validCollisions[i].reserve(masterConfigurations.mixingParameter); } } - if (!masterConfigurations.doPPAnalysis) { - // event selections in Pb-Pb - histos.add("hEventSelection", "hEventSelection", kTH1F, {{10, 0, 10}}); - TString eventSelLabel[] = {"all", "sel8", "kIsTriggerTVX", "PV_{z}", "kIsGoodITSLayersAll", "kIsGoodZvtxFT0vsPV", "OccupCut", "kNoTimeFrameBorder", "kNoITSROFrameBorder", "kNoSameBunchPileup "}; - for (int i = 1; i <= histos.get(HIST("hEventSelection"))->GetNbinsX(); i++) { - histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(i, eventSelLabel[i - 1]); - } + // Event-selection cut-flow, same cut set/order/labels as CollCutCounts in + // hResonanceCorrelationFilter.cxx, filled by isSelectedEvents() -- see + // eventQAOwnerIndex above for why only one process function fills it. + if (eventQAOwnerIndex != -1) { + histos.add("CollCutCounts", "No. of event after cuts", kTH1I, {{10, 0, 10}}); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(1, "All Events"); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(2, "|Vz| < cut"); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(3, "kIsTriggerTVX"); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(4, "kNoTimeFrameBorder"); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(5, "kNoITSROFrameBorder"); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(6, "sel8"); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(7, "kNoSameBunchPileup"); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(8, "kIsGoodZvtxFT0vsPV"); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(9, "All Passed Events"); } // ======================================================================== @@ -708,6 +756,24 @@ struct HResonanceCorrelation { doME = doprocessMixedEventHHadrons; } + // Same-/mixed-event invariant-mass cross-check (Phi/K*0 only). Booked + // independent of doFullCorrelationStudy/fillCorrelationHistWithMass + // since these are standalone QA histograms, not part of the + // Signal/LeftBg/RightBg THnF family -- just need doSE/doME (the + // process switch(es) for this species) to actually get filled. + if (i == IndexPhi && doSE) { + histos.add("hPhiInvMass", "Phi candidate invariant mass, same event", kTH3F, {axesConfigurations.axisPhiMass, axesConfigurations.axisPtAssoc, axesConfigurations.axisMult}); + } + if (i == IndexKstar && doSE) { + histos.add("hKstarInvMass", "K*0 candidate invariant mass, same event", kTH3F, {axesConfigurations.axisKstarMass, axesConfigurations.axisPtAssoc, axesConfigurations.axisMult}); + } + if (i == IndexPhi && doME) { + histos.add("hPhiMixedEventInvMass", "Phi candidate invariant mass, mixed event", kTH3F, {axesConfigurations.axisPhiMass, axesConfigurations.axisPtAssoc, axesConfigurations.axisMult}); + } + if (i == IndexKstar && doME) { + histos.add("hKstarMixedEventInvMass", "K*0 candidate invariant mass, mixed event", kTH3F, {axesConfigurations.axisKstarMass, axesConfigurations.axisPtAssoc, axesConfigurations.axisMult}); + } + // Kinematic QA (Only for Phi) if (i < IndexPion && (doSE || doME || doprocessPrediction || doprocessClosureTest)) { if (!efficiencyFlags.applyEffAsFunctionOfMultAndPhi) { @@ -955,115 +1021,92 @@ struct HResonanceCorrelation { } } - // this function allows for all event selections to be done in a modular way + // Single event-selection predicate, cut-for-cut and bin-for-bin the same as + // isSelectedEvents() in hResonanceCorrelationFilter.cxx, so a collision is + // selected identically whether the filter task is producing its tables or + // this task is correlating them. Replaces the former isisCollisionSelect() + // (pp) / isisCollisionSelectPbPb() (Pb-Pb) pair, which carried their own, + // different cut lists (centrality-range, INEL>0, ITS-good-layers, TPC + // occupancy) that could disagree with the filter task's selection. + // + // fillHist controls the CollCutCounts cut-flow and EventQA/hMult+hPvz + // fills: pass true only from the one process function that "owns" this + // collision for QA purposes (see eventQAOwnerIndex in init()) so a + // collision examined by several enabled same-event process functions is + // still only counted once. Mixed-event callers always pass false, since ME + // revisits the same collisions many times per mixing pool and has its own + // separate MixingQA histograms. template - bool isisCollisionSelect(TCollision const& collision) + bool isSelectedEvents(TCollision const& collision, bool fillHist = true) { - // ________________________________________________ - // Perform basic event selection - if (!collision.sel8()) { - return false; - } - if (std::abs(collision.posZ()) > masterConfigurations.zVertexCut) { - return false; - } - if (collision.centFT0M() > axisRanges[5][1] || collision.centFT0M() < axisRanges[5][0]) { - return false; - } - if (!collision.isInelGt0() && masterConfigurations.selectINELgtZERO) { - return false; - } - if (!collision.selection_bit(aod::evsel::kIsGoodITSLayersAll) && masterConfigurations.requireAllGoodITSLayers) { - return false; - } - if (zorroMask.value != "") { - auto bc = collision.template bc_as(); - initZorro(bc); - bool zorroSelected = zorro.isSelected(collision.template bc_as().globalBC()); /// Just let Zorro do the accounting - if (!zorroSelected) { + auto applyCut = [&](bool enabled, bool condition, int bin) { + if (!enabled) { + return true; + } + if (!condition) { return false; } - } - return true; - } - - // event selections in Pb-Pb - template - bool isisCollisionSelectPbPb(TCollision const& collision, bool fillHists) - { - if (fillHists) - histos.fill(HIST("hEventSelection"), 0.5 /* all collisions */); + if (fillHist) { + histos.fill(HIST("CollCutCounts"), bin); + } + return true; + }; - // Perform basic event selection - if (!collision.sel8()) { - return false; + if (fillHist) { + histos.fill(HIST("CollCutCounts"), 0); } - if (fillHists) - histos.fill(HIST("hEventSelection"), 1.5 /* collisions after sel8*/); - if (!collision.selection_bit(aod::evsel::kIsTriggerTVX) && masterConfigurations.requireGoodTriggerTVX) { + if (!applyCut(true, std::abs(collision.posZ()) <= configEvents.cfgEvtZvtx, 1)) { return false; } - if (fillHists) - histos.fill(HIST("hEventSelection"), 2.5 /* FT0 vertex (acceptable FT0C-FT0A time difference) collisions */); - if (std::abs(collision.posZ()) > masterConfigurations.zVertexCut) { + if (!applyCut(configEvents.cfgEvtTriggerTVXSel, + collision.selection_bit(aod::evsel::kIsTriggerTVX), 2)) { return false; } - if (fillHists) - histos.fill(HIST("hEventSelection"), 3.5 /* collisions after sel pvz sel*/); - if (!collision.selection_bit(aod::evsel::kIsGoodITSLayersAll) && masterConfigurations.requireAllGoodITSLayers) { - // cut time intervals with dead ITS staves + if (!applyCut(configEvents.cfgEvtNoTFBorderCut, + collision.selection_bit(aod::evsel::kNoTimeFrameBorder), 3)) { return false; } - if (fillHists) - histos.fill(HIST("hEventSelection"), 4.5 /* collisions after cut time intervals with dead ITS staves*/); - if (!collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV) && masterConfigurations.requireGoodZvtxFT0vsPV) { - // removes collisions with large differences between z of PV by tracks and z of PV from FT0 A-C time difference - // use this cut at low multiplicities with caution + if (!applyCut(configEvents.cfgEvtNoITSROFrameBorderCut, + collision.selection_bit(aod::evsel::kNoITSROFrameBorder), 4)) { return false; } - if (fillHists) - histos.fill(HIST("hEventSelection"), 5.5 /* removes collisions with large differences between z of PV by tracks and z of PV from FT0 A-C time difference*/); - auto occupancy = collision.trackOccupancyInTimeRange(); - if (occupancy < cfgCutOccupancyLow || occupancy > cfgCutOccupancyHigh) + if (!applyCut(configEvents.cfgEvtSel8, collision.sel8(), 5)) { return false; - if (fillHists) - histos.fill(HIST("hEventSelection"), 6.5 /* Below min occupancy and Above max occupancy*/); + } - /* - if (collision.alias_bit(kTVXinTRD)) { - // TRD triggered + if (!applyCut(configEvents.cfgEvtNoSameBunchPileupCut, + collision.selection_bit(aod::evsel::kNoSameBunchPileup), 6)) { return false; } - */ - if (!collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) { - // reject collisions close to Time Frame borders - // O2-4623 + if (!applyCut(configEvents.cfgEvtGoodZvtxFT0vsPVCut, + collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV), 7)) { return false; } - if (fillHists) - histos.fill(HIST("hEventSelection"), 7.5 /* reject collisions close to Time Frame borders*/); - if (!collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) { - // reject events affected by the ITS ROF border - // O2-4309 - return false; + // Zorro trigger-skim accounting: this task's own feature, not part of the + // filter task's cut set (which has no skim awareness) -- opt-in via + // zorroMask, off by default, so it doesn't change the baseline cut set. + if (zorroMask.value != "") { + auto bc = collision.template bc_as(); + initZorro(bc); + bool zorroSelected = zorro.isSelected(collision.template bc_as().globalBC()); /// Just let Zorro do the accounting + if (!zorroSelected) { + return false; + } } - if (fillHists) - histos.fill(HIST("hEventSelection"), 8.5 /* reject events affected by the ITS ROF border*/); - if (!collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { - // rejects collisions which are associated with the same "found-by-T0" bunch crossing - // https://indico.cern.ch/event/1396220/#1-event-selection-with-its-rof - return false; + if (fillHist) { + histos.fill(HIST("CollCutCounts"), 8); + histos.fill(HIST("EventQA/hMult"), collision.centFT0M()); + histos.fill(HIST("EventQA/hPvz"), collision.posZ()); } - if (fillHists) - histos.fill(HIST("hEventSelection"), 9.5 /* rejects collisions which are associated with the same "found-by-T0" bunch crossing*/); + return true; } @@ -2106,7 +2149,7 @@ struct HResonanceCorrelation { for (auto const& collision : collisions) { // ________________________________________________ // Perform basic event selection - if (!isisCollisionSelect(collision)) { + if (!isSelectedEvents(collision, false)) { continue; } @@ -2126,6 +2169,15 @@ struct HResonanceCorrelation { void processSameEventHHadrons(soa::Join::iterator const& collision, aod::AssocHadrons const& assocHadrons, aod::TriggerTracks const& triggerTracks, TracksComplete const&, aod::BCsWithTimestamps const&) { + // Guard against the fatal "histogram not found" crash: the per-species + // histograms this function fills are only booked in init() when + // TESTBIT(doCorrelation, IndexHadron) is also set (see init(), ~line 831) + // -- doCorrelation defaults to 0 (masterConfigurations.doCorrelationHadron + // defaults false) while this PROCESS_SWITCH defaults true, so without this + // guard the default config fills unbooked histograms and crashes. + if (!TESTBIT(doCorrelation, IndexHadron)) { + return; + } LOGF(info, "SameEventHadron: collisions=%d triggers=%zu assocHadrons=%zu", collision.globalIndex(), triggerTracks.size(), assocHadrons.size()); BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; // true is for 'ignore overflows' (true by default). Underflows and overflows will have bin -1. @@ -2144,15 +2196,15 @@ struct HResonanceCorrelation { } // ________________________________________________ - // Perform basic event selection - if (!isisCollisionSelect(collision)) { + // Perform basic event selection. fillHist=true only from the owning + // species (see eventQAOwnerIndex in init()) so CollCutCounts/EventQA + // aren't multi-counted when several same-event species run together. + if (!isSelectedEvents(collision, eventQAOwnerIndex == IndexHadron)) { return; } // ________________________________________________ - if (!doprocessSameEventHPhis && !doprocessSameEventHKstars && !doprocessSameEventHPions && !doprocessSameEventHKaons && doMixingQAandEventQA) { + if (eventQAOwnerIndex == IndexHadron && doMixingQAandEventQA) { histos.fill(HIST("MixingQA/hSECollisionBins"), colBinning.getBin({collision.posZ(), collision.centFT0M()})); - histos.fill(HIST("EventQA/hMult"), collision.centFT0M()); - histos.fill(HIST("EventQA/hPvz"), collision.posZ()); } // Do basic QA @@ -2212,6 +2264,12 @@ struct HResonanceCorrelation { void processSameEventHPions(soa::Join::iterator const& collision, soa::Join const& associatedPions, soa::Join const& triggerTracks, TracksComplete const&, aod::BCsWithTimestamps const&) { + // See the matching comment in processSameEventHHadrons: without this, + // the default config (doCorrelationPion=false, this switch=true) fills + // hPionEtaVsPtAllSelected etc. before they're booked -> fatal. + if (!TESTBIT(doCorrelation, IndexPion)) { + return; + } LOGF(info, "SameEventPion: collisions=%d triggers=%zu assocPions=%zu", collision.globalIndex(), triggerTracks.size(), associatedPions.size()); BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; @@ -2228,15 +2286,14 @@ struct HResonanceCorrelation { } // ________________________________________________ - // Perform basic event selection - if (!isisCollisionSelect(collision)) { + // Perform basic event selection. See the matching comment in + // processSameEventHHadrons for fillHist/eventQAOwnerIndex. + if (!isSelectedEvents(collision, eventQAOwnerIndex == IndexPion)) { return; } // ________________________________________________ - if (!doprocessSameEventHPhis && !doprocessSameEventHKstars && doMixingQAandEventQA) { + if (eventQAOwnerIndex == IndexPion && doMixingQAandEventQA) { histos.fill(HIST("MixingQA/hSECollisionBins"), colBinning.getBin({collision.posZ(), collision.centFT0M()})); - histos.fill(HIST("EventQA/hMult"), collision.centFT0M()); - histos.fill(HIST("EventQA/hPvz"), collision.posZ()); } // Do basic QA for (auto const& pion : associatedPions) { @@ -2277,6 +2334,13 @@ struct HResonanceCorrelation { void processSameEventHKaons(soa::Join::iterator const& collision, soa::Join const& associatedKaons, soa::Join const& triggerTracks, TracksComplete const&, aod::BCsWithTimestamps const&) { + // See the matching comment in processSameEventHHadrons: without this, + // the default config (doCorrelationKaon=false, this switch=true) fills + // hKaonEtaVsPtAllSelected etc. before they're booked -> fatal (this is + // the exact FATAL reported: "hKaonEtaVsPtAllSelected" not found). + if (!TESTBIT(doCorrelation, IndexKaon)) { + return; + } LOGF(info, "SameEventKaon: collisions=%d triggers=%zu assocKaons=%zu", collision.globalIndex(), triggerTracks.size(), associatedKaons.size()); BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; @@ -2293,15 +2357,14 @@ struct HResonanceCorrelation { } // ________________________________________________ - // Perform basic event selection - if (!isisCollisionSelect(collision)) { + // Perform basic event selection. See the matching comment in + // processSameEventHHadrons for fillHist/eventQAOwnerIndex. + if (!isSelectedEvents(collision, eventQAOwnerIndex == IndexKaon)) { return; } // ________________________________________________ - if (!doprocessSameEventHPhis && !doprocessSameEventHKstars && !doprocessSameEventHPions && doMixingQAandEventQA) { + if (eventQAOwnerIndex == IndexKaon && doMixingQAandEventQA) { histos.fill(HIST("MixingQA/hSECollisionBins"), colBinning.getBin({collision.posZ(), collision.centFT0M()})); - histos.fill(HIST("EventQA/hMult"), collision.centFT0M()); - histos.fill(HIST("EventQA/hPvz"), collision.posZ()); } // Do basic QA for (auto const& kaon : associatedKaons) { @@ -2342,6 +2405,12 @@ struct HResonanceCorrelation { void processSameEventHPhis(soa::Join::iterator const& collision, aod::AssocPhis const& associatedPhis, aod::TriggerTracks const& triggerTracks, TracksComplete const&, aod::BCsWithTimestamps const&) { + // See the matching comment in processSameEventHHadrons: without this, + // the default config (doCorrelationPhi=false, this switch=true) fills + // sameEvent/Signal/Phi etc. before they're booked -> fatal. + if (!TESTBIT(doCorrelation, IndexPhi)) { + return; + } LOGF(info, "SameEventPhi: collisions=%d triggers=%zu assocPhi=%zu", collision.globalIndex(), triggerTracks.size(), associatedPhis.size()); BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; @@ -2358,14 +2427,14 @@ struct HResonanceCorrelation { initEfficiencyFromCCDB(bc); } - if (!isisCollisionSelect(collision)) { + // See the matching comment in processSameEventHHadrons for + // fillHist/eventQAOwnerIndex. + if (!isSelectedEvents(collision, eventQAOwnerIndex == IndexPhi)) { return; } - if (doMixingQAandEventQA) { + if (eventQAOwnerIndex == IndexPhi && doMixingQAandEventQA) { histos.fill(HIST("MixingQA/hSECollisionBins"), colBinning.getBin({collision.posZ(), collision.centFT0M()})); - histos.fill(HIST("EventQA/hMult"), collision.centFT0M()); - histos.fill(HIST("EventQA/hPvz"), collision.posZ()); } // ----------------------------- @@ -2391,14 +2460,34 @@ struct HResonanceCorrelation { histos.fill(HIST("hTrackEtaVsPtVsPhi"), track.pt(), track.eta(), track.phi()); } + // Same-event invariant-mass cross-check: raw per-candidate mass, read + // straight off the already-computed Mass column -- no kinematics + // recomputed here. See the matching comment at hPhiMixedEventInvMass's + // booking in init() for why this is the mixed-event counterpart. + for (auto const& cand : associatedPhis) { + histos.fill(HIST("hPhiInvMass"), cand.mass(), cand.pt(), collision.centFT0M()); + } + // ----------------------------- // h-phi correlation // ----------------------------- - fillCorrelationsPhi(triggerTracks, associatedPhis, false, false, collision.posZ(), collision.centFT0M(), bField); + // Guarded by doFullCorrelationStudy to match how sameEvent/Signal/Phi is + // booked in init() (~line 753) -- this call used to be unconditional, + // which would fill that THnF even when doFullCorrelationStudy=false (and + // it was therefore never booked). + if (masterConfigurations.doFullCorrelationStudy) { + fillCorrelationsPhi(triggerTracks, associatedPhis, false, false, collision.posZ(), collision.centFT0M(), bField); + } } void processSameEventHKstars(soa::Join::iterator const& collision, aod::AssocKstars const& associatedKstars, aod::TriggerTracks const& triggerTracks, TracksComplete const&, aod::BCsWithTimestamps const&) { + // See the matching comment in processSameEventHHadrons: without this, + // the default config (doCorrelationKstar=false, this switch=true) fills + // sameEvent/Signal/Kstar0 etc. before they're booked -> fatal. + if (!TESTBIT(doCorrelation, IndexKstar)) { + return; + } LOGF(info, "SameEventKstar: collisions=%d triggers=%zu assocKstar=%zu", collision.globalIndex(), triggerTracks.size(), associatedKstars.size()); BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; @@ -2415,14 +2504,14 @@ struct HResonanceCorrelation { initEfficiencyFromCCDB(bc); } - if (!isisCollisionSelect(collision)) { + // See the matching comment in processSameEventHHadrons for + // fillHist/eventQAOwnerIndex. + if (!isSelectedEvents(collision, eventQAOwnerIndex == IndexKstar)) { return; } - if (doMixingQAandEventQA) { + if (eventQAOwnerIndex == IndexKstar && doMixingQAandEventQA) { histos.fill(HIST("MixingQA/hSECollisionBins"), colBinning.getBin({collision.posZ(), collision.centFT0M()})); - histos.fill(HIST("EventQA/hMult"), collision.centFT0M()); - histos.fill(HIST("EventQA/hPvz"), collision.posZ()); } // ----------------------------- @@ -2448,15 +2537,30 @@ struct HResonanceCorrelation { histos.fill(HIST("hTrackEtaVsPtVsPhi"), track.pt(), track.eta(), track.phi()); } + // Same-event invariant-mass cross-check -- see the matching comment in + // processSameEventHPhis. + for (auto const& cand : associatedKstars) { + histos.fill(HIST("hKstarInvMass"), cand.mass(), cand.pt(), collision.centFT0M()); + } + // ----------------------------- // h-K*0 correlation // ----------------------------- - fillCorrelationsKstar(triggerTracks, associatedKstars, false, false, collision.posZ(), collision.centFT0M(), bField); + // Guarded by doFullCorrelationStudy for the same reason as the Phi call + // above -- matches how sameEvent/Signal/Kstar0 is booked in init(). + if (masterConfigurations.doFullCorrelationStudy) { + fillCorrelationsKstar(triggerTracks, associatedKstars, false, false, collision.posZ(), collision.centFT0M(), bField); + } } void processMixedEventHHadrons(soa::Join const& collisions, aod::AssocHadrons const& assocHadrons, aod::TriggerTracks const& triggerTracks, TracksComplete const&, aod::BCsWithTimestamps const&) { + // See the matching comment in processSameEventHHadrons -- mixedEvent/Signal/Hadron + // is booked only when TESTBIT(doCorrelation, IndexHadron) is set. + if (!TESTBIT(doCorrelation, IndexHadron)) { + return; + } BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; for (auto const& [collision1, collision2] : soa::selfCombinations(colBinning, masterConfigurations.mixingParameter, -1, collisions, collisions)) { auto bc = collision1.bc_as(); @@ -2473,7 +2577,7 @@ struct HResonanceCorrelation { // ________________________________________________ // Perform basic event selection on both collisions - if (!isisCollisionSelect(collision1) || !isisCollisionSelect(collision2)) { + if (!isSelectedEvents(collision1, false) || !isSelectedEvents(collision2, false)) { continue; } if (collision1.centFT0M() > axisRanges[5][1] || collision1.centFT0M() < axisRanges[5][0]) @@ -2501,6 +2605,11 @@ struct HResonanceCorrelation { void processMixedEventHPions(soa::Join const& collisions, soa::Join const& assocPions, soa::Join const& triggerTracks, TracksComplete const&, aod::BCsWithTimestamps const&) { + // See the matching comment in processSameEventHHadrons -- mixedEvent/Signal/Pion + // is booked only when TESTBIT(doCorrelation, IndexPion) is set. + if (!TESTBIT(doCorrelation, IndexPion)) { + return; + } BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; for (auto const& [collision1, collision2] : soa::selfCombinations(colBinning, masterConfigurations.mixingParameter, -1, collisions, collisions)) { auto bc = collision1.bc_as(); @@ -2517,7 +2626,7 @@ struct HResonanceCorrelation { // ________________________________________________ // Perform basic event selection on both collisions - if (!isisCollisionSelect(collision1) || !isisCollisionSelect(collision2)) { + if (!isSelectedEvents(collision1, false) || !isSelectedEvents(collision2, false)) { continue; } if (collision1.centFT0M() > axisRanges[5][1] || collision1.centFT0M() < axisRanges[5][0]) @@ -2545,6 +2654,11 @@ struct HResonanceCorrelation { void processMixedEventHKaons(soa::Join const& collisions, soa::Join const& assocKaons, soa::Join const& triggerTracks, TracksComplete const&, aod::BCsWithTimestamps const&) { + // See the matching comment in processSameEventHHadrons -- mixedEvent/Signal/Kaon + // is booked only when TESTBIT(doCorrelation, IndexKaon) is set. + if (!TESTBIT(doCorrelation, IndexKaon)) { + return; + } BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; for (auto const& [collision1, collision2] : soa::selfCombinations(colBinning, masterConfigurations.mixingParameter, -1, collisions, collisions)) { auto bc = collision1.bc_as(); @@ -2561,7 +2675,7 @@ struct HResonanceCorrelation { // ________________________________________________ // Perform basic event selection on both collisions - if (!isisCollisionSelect(collision1) || !isisCollisionSelect(collision2)) { + if (!isSelectedEvents(collision1, false) || !isSelectedEvents(collision2, false)) { continue; } if (collision1.centFT0M() > axisRanges[5][1] || collision1.centFT0M() < axisRanges[5][0]) @@ -2589,6 +2703,11 @@ struct HResonanceCorrelation { void processMixedEventHPhis(soa::Join const& collisions, aod::AssocPhis const& assocPhis, aod::TriggerTracks const& triggerTracks, TracksComplete const&, aod::BCsWithTimestamps const&) { + // See the matching comment in processSameEventHHadrons -- mixedEvent/Signal/Phi + // is booked only when TESTBIT(doCorrelation, IndexPhi) is set. + if (!TESTBIT(doCorrelation, IndexPhi)) { + return; + } BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; for (auto const& [collision1, collision2] : soa::selfCombinations(colBinning, masterConfigurations.mixingParameter, -1, collisions, collisions)) { @@ -2604,7 +2723,7 @@ struct HResonanceCorrelation { continue; } - if (!isisCollisionSelect(collision1) || !isisCollisionSelect(collision2)) { + if (!isSelectedEvents(collision1, false) || !isSelectedEvents(collision2, false)) { continue; } @@ -2626,6 +2745,14 @@ struct HResonanceCorrelation { auto slicedAssocPhis = assocPhis.sliceBy(collisionSlicePhis, collision2.globalIndex()); + // Mixed-event invariant-mass cross-check: reuses the Mass/Rapidity + // columns already stored by the filter task (no kinematics + // recomputed here) and this same mixing pool, so it's directly + // comparable to the same-event mass QA and to the real ME background. + for (auto const& cand : slicedAssocPhis) { + histos.fill(HIST("hPhiMixedEventInvMass"), cand.mass(), cand.pt(), collision1.centFT0M()); + } + if (masterConfigurations.doFullCorrelationStudy) { // Use the per-collision slices computed above, not the full unsliced // triggerTracks/assocPhis tables -- passing the whole dataframe here @@ -2638,6 +2765,11 @@ struct HResonanceCorrelation { void processMixedEventHKstars(soa::Join const& collisions, aod::AssocKstars const& assocKstars, aod::TriggerTracks const& triggerTracks, TracksComplete const&, aod::BCsWithTimestamps const&) { + // See the matching comment in processSameEventHHadrons -- mixedEvent/Signal/Kstar0 + // is booked only when TESTBIT(doCorrelation, IndexKstar) is set. + if (!TESTBIT(doCorrelation, IndexKstar)) { + return; + } BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; for (auto const& [collision1, collision2] : soa::selfCombinations(colBinning, masterConfigurations.mixingParameter, -1, collisions, collisions)) { @@ -2653,7 +2785,7 @@ struct HResonanceCorrelation { continue; } - if (!isisCollisionSelect(collision1) || !isisCollisionSelect(collision2)) { + if (!isSelectedEvents(collision1, false) || !isSelectedEvents(collision2, false)) { continue; } @@ -2675,6 +2807,12 @@ struct HResonanceCorrelation { auto slicedAssocKstars = assocKstars.sliceBy(collisionSliceKstars, collision2.globalIndex()); + // Mixed-event invariant-mass cross-check -- see the matching comment + // in processMixedEventHPhis. + for (auto const& cand : slicedAssocKstars) { + histos.fill(HIST("hKstarMixedEventInvMass"), cand.mass(), cand.pt(), collision1.centFT0M()); + } + if (masterConfigurations.doFullCorrelationStudy) { // Same reasoning as processMixedEventHPhis above -- use this collision // pair's own slices, not the full unsliced triggerTracks/assocKstars. @@ -2776,7 +2914,11 @@ struct HResonanceCorrelation { bestCollisionFT0Cpercentile = collision.centFT0C(); if (masterConfigurations.applyNewMCSelection) { - isCollisionSelect = (masterConfigurations.doPPAnalysis && isisCollisionSelect(collision)) || (!masterConfigurations.doPPAnalysis && isisCollisionSelectPbPb(collision, false)); + // isSelectedEvents() is the one selection used everywhere in this + // task now (pp and Pb-Pb alike), matching the filter task; see its + // own comment for why -- no more doPPAnalysis branch to a different + // cut set here. + isCollisionSelect = isSelectedEvents(collision, false); } else { bestCollisionSel8 = collision.sel8(); bestCollisionVtxZ = collision.posZ(); @@ -3092,6 +3234,11 @@ struct HResonanceCorrelation { void processMixedEventHPhisInBuffer(soa::Join const& collisions, aod::TriggerTracks const& triggerTracks, aod::AssocPhis const& assocPhis, TracksComplete const&, aod::BCsWithTimestamps const&) { + // See the matching comment in processSameEventHHadrons -- mixedEvent/Signal/Phi + // is booked only when TESTBIT(doCorrelation, IndexPhi) is set. + if (!TESTBIT(doCorrelation, IndexPhi)) { + return; + } for (auto const& collision : collisions) { auto bc = collision.bc_as(); @@ -3104,12 +3251,21 @@ struct HResonanceCorrelation { auto slicedTriggerTracks = triggerTracks.sliceBy(collisionSliceTracks, collision.globalIndex()); auto slicedAssocPhis = assocPhis.sliceBy(collisionSlicePhis, collision.globalIndex()); - fillCorrelationsPhi(slicedTriggerTracks, slicedAssocPhis, true, true, collision.posZ(), collision.centFT0M(), dBz); + // Guarded by doFullCorrelationStudy -- see the matching comment in + // processSameEventHPhis; this call used to be unconditional. + if (masterConfigurations.doFullCorrelationStudy) { + fillCorrelationsPhi(slicedTriggerTracks, slicedAssocPhis, true, true, collision.posZ(), collision.centFT0M(), dBz); + } } } void processMixedEventHKstarsInBuffer(soa::Join const& collisions, aod::TriggerTracks const& triggerTracks, aod::AssocKstars const& assocKstars, TracksComplete const&, aod::BCsWithTimestamps const&) { + // See the matching comment in processSameEventHHadrons -- mixedEvent/Signal/Kstar0 + // is booked only when TESTBIT(doCorrelation, IndexKstar) is set. + if (!TESTBIT(doCorrelation, IndexKstar)) { + return; + } for (auto const& collision : collisions) { auto bc = collision.bc_as(); @@ -3118,7 +3274,10 @@ struct HResonanceCorrelation { auto slicedTriggerTracks = triggerTracks.sliceBy(collisionSliceTracks, collision.globalIndex()); auto slicedAssocKstars = assocKstars.sliceBy(collisionSliceKstars, collision.globalIndex()); - fillCorrelationsKstar(slicedTriggerTracks, slicedAssocKstars, true, true, collision.posZ(), collision.centFT0M(), dBz); + // Guarded by doFullCorrelationStudy -- see the matching comment above. + if (masterConfigurations.doFullCorrelationStudy) { + fillCorrelationsKstar(slicedTriggerTracks, slicedAssocKstars, true, true, collision.posZ(), collision.centFT0M(), dBz); + } } }