From 908b6d4d8a5d8fcdc3eeae4e6f25ffe3ae5c3efa Mon Sep 17 00:00:00 2001 From: Soumyadip Ghosh Date: Thu, 6 Aug 2026 19:40:04 +0530 Subject: [PATCH 1/3] Enhance PID QA with new configurations and histograms Added PID response configurations and updated histograms for Quality Assurance (processPIDQA). --- .../Tasks/longrangecorrDerived.cxx | 143 +++++++++++++++++- 1 file changed, 142 insertions(+), 1 deletion(-) diff --git a/PWGCF/TwoParticleCorrelations/Tasks/longrangecorrDerived.cxx b/PWGCF/TwoParticleCorrelations/Tasks/longrangecorrDerived.cxx index c49559df02c..5eba4376929 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/longrangecorrDerived.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/longrangecorrDerived.cxx @@ -25,6 +25,8 @@ #include "Common/Core/TrackSelectionDefaults.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/McCollisionExtra.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" #include "Common/DataModel/TrackSelectionTables.h" #include @@ -62,6 +64,9 @@ using namespace o2::aod::evsel; using namespace o2::constants::math; auto static constexpr KminCharge = 3.0f; +auto static constexpr KPidMaskPion = 2; +auto static constexpr KPidMaskKaon = 4; +auto static constexpr KPidMaskProton = 8; struct LongrangecorrDerived { @@ -105,14 +110,25 @@ struct LongrangecorrDerived { Configurable cfgRequireFt0aInnerRing{"cfgRequireFt0aInnerRing", false, "Consider FT0A Inner Ring"}; Configurable cfgRequireFt0cOuterRing{"cfgRequireFt0cOuterRing", false, "Consider FT0C Outer Ring"}; Configurable cfgRequireFt0cInnerRing{"cfgRequireFt0cInnerRing", false, "Consider FT0C Inner Ring"}; + + Configurable cfgTofPidPtCut{"cfgTofPidPtCut", 0.4f, "Minimum pt to use TOF N-sigma"}; + Configurable cfgPidNsigmaMax{"cfgPidNsigmaMax", 1.5f, "Maximum n-sigma for PID"}; + Configurable cfgPidNsigmaMin{"cfgPidNsigmaMin", -1.5f, "Minimum n-sigma for PID"}; + Configurable cfgGetNsigmaQA{"cfgGetNsigmaQA", true, "Get QA histograms for PID selection"}; + Configurable cfgGetdEdx{"cfgGetdEdx", true, "Get dEdx histograms for TPC signal"}; + } cfgSel; struct : ConfigurableGroup { ConfigurableAxis axisMultQA{"axisMultQA", {500, -0.5, 499.5}, "multiplicity QA axis"}; - ConfigurableAxis axisMultiplicity{"axisMultiplicity", {VARIABLE_WIDTH, 0, 10, 15, 25, 50, 60, 1000}, "multiplicity axis"}; + ConfigurableAxis axisMultiplicity{"axisMultiplicity", {VARIABLE_WIDTH, 0, 26, 46, 133, 1000}, "multiplicity axis"}; + ConfigurableAxis axisCentrality{"axisCentrality", {VARIABLE_WIDTH, 0, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100}, "Centrality (%)"}; + ConfigurableAxis axisPhi{"axisPhi", {96, 0, TwoPI}, "#phi axis"}; ConfigurableAxis axisEtaTrig{"axisEtaTrig", {40, -1., 1.}, "#eta trig axis"}; ConfigurableAxis axisPtTrigger{"axisPtTrigger", {VARIABLE_WIDTH, 0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 6.0, 10.0}, "pt trigger axis for histograms"}; + ConfigurableAxis axisPtQA{"axisPtQA", {50, 0.0, 8.0}, "pt axis for PID QA histograms"}; + ConfigurableAxis axisVtxZ{"axisVtxZ", {40, -20, 20}, "vertex axis"}; ConfigurableAxis axisEtaAssoc{"axisEtaAssoc", {96, 3.5, 4.9}, "#eta assoc axis"}; ConfigurableAxis axisDeltaPhi{"axisDeltaPhi", {72, -PIHalf, PIHalf * 3}, "delta phi axis for histograms"}; @@ -138,6 +154,11 @@ struct LongrangecorrDerived { ConfigurableAxis axisVertexEfficiency{"axisVertexEfficiency", {10, -10, 10}, "vertex axis for efficiency histograms"}; ConfigurableAxis axisEtaEfficiency{"axisEtaEfficiency", {20, -1.0, 1.0}, "eta axis for efficiency histograms"}; ConfigurableAxis axisPtEfficiency{"axisPtEfficiency", {1, 0.5, 8.0}, "pt axis for efficiency histograms"}; + + ConfigurableAxis axisNsigmaTPC{"axisNsigmaTPC", {80, -5, 5}, "nsigmaTPC axis"}; + ConfigurableAxis axisNsigmaTOF{"axisNsigmaTOF", {80, -5, 5}, "nsigmaTOF axis"}; + ConfigurableAxis axisTpcSignal{"axisTpcSignal", {250, 0, 250}, "dEdx axis for TPC"}; + } cfgAxis; Configurable cfgFv0Cut{"cfgFv0Cut", 50.0f, "FV0A threshold"}; @@ -265,6 +286,39 @@ struct LongrangecorrDerived { myTrackFilter.SetMaxChi2PerClusterTPC(cfgSel.cfgTpcMaxChi2PerCluster); myTrackFilter.SetMaxDcaZ(cfgSel.cfgTpcMaxDcaZ); myTrackFilter.print(); + + if (cfgSel.cfgGetNsigmaQA && (cfgSel.cfgPidMask == KPidMaskPion || cfgSel.cfgPidMask == KPidMaskKaon || cfgSel.cfgPidMask == KPidMaskProton)) { + + if (cfgSel.isUseCentEst) { // CENTRALITY MODE + histos.add("TofTpcNsigma_before", "TPC vs TOF n#sigma Before Cuts;Centrality (%);p_{T} (GeV/c);n#sigma_{TPC};n#sigma_{TOF}", + kTHnSparseD, {cfgAxis.axisCentrality, cfgAxis.axisPtQA, cfgAxis.axisNsigmaTPC, cfgAxis.axisNsigmaTOF}); + + histos.add("TofTpcNsigma_after", "TPC vs TOF n#sigma After Cuts;Centrality (%);p_{T} (GeV/c);n#sigma_{TPC};n#sigma_{TOF}", + kTHnSparseD, {cfgAxis.axisCentrality, cfgAxis.axisPtQA, cfgAxis.axisNsigmaTPC, cfgAxis.axisNsigmaTOF}); + + if (cfgSel.cfgGetdEdx) { + histos.add("TpcdEdx_ptwise", "TPC dE/dx Before Cuts;Centrality (%);p_{T} (GeV/c);TPC dE/dx;n#sigma_{TOF}", + kTHnSparseD, {cfgAxis.axisCentrality, cfgAxis.axisPtQA, cfgAxis.axisTpcSignal, cfgAxis.axisNsigmaTOF}); + + histos.add("TpcdEdx_ptwise_afterCut", "TPC dE/dx After Cuts;Centrality (%);p_{T} (GeV/c);TPC dE/dx;n#sigma_{TOF}", + kTHnSparseD, {cfgAxis.axisCentrality, cfgAxis.axisPtQA, cfgAxis.axisTpcSignal, cfgAxis.axisNsigmaTOF}); + } + } else { // MULTIPLICITY MODE + histos.add("TofTpcNsigma_before", "TPC vs TOF n#sigma Before Cuts;Multiplicity (N_{ch});p_{T} (GeV/c);n#sigma_{TPC};n#sigma_{TOF}", + kTHnSparseD, {cfgAxis.axisMultiplicity, cfgAxis.axisPtQA, cfgAxis.axisNsigmaTPC, cfgAxis.axisNsigmaTOF}); + + histos.add("TofTpcNsigma_after", "TPC vs TOF n#sigma After Cuts;Multiplicity (N_{ch});p_{T} (GeV/c);n#sigma_{TPC};n#sigma_{TOF}", + kTHnSparseD, {cfgAxis.axisMultiplicity, cfgAxis.axisPtQA, cfgAxis.axisNsigmaTPC, cfgAxis.axisNsigmaTOF}); + + if (cfgSel.cfgGetdEdx) { + histos.add("TpcdEdx_ptwise", "TPC dE/dx Before Cuts;Multiplicity (N_{ch});p_{T} (GeV/c);TPC dE/dx;n#sigma_{TOF}", + kTHnSparseD, {cfgAxis.axisMultiplicity, cfgAxis.axisPtQA, cfgAxis.axisTpcSignal, cfgAxis.axisNsigmaTOF}); + + histos.add("TpcdEdx_ptwise_afterCut", "TPC dE/dx After Cuts;Multiplicity (N_{ch});p_{T} (GeV/c);TPC dE/dx;n#sigma_{TOF}", + kTHnSparseD, {cfgAxis.axisMultiplicity, cfgAxis.axisPtQA, cfgAxis.axisTpcSignal, cfgAxis.axisNsigmaTOF}); + } + } + } } void loadEffCorrection(uint64_t timestamp) @@ -1020,6 +1074,92 @@ struct LongrangecorrDerived { } } + using CollStandardTable = soa::Join; + using TrksStandardTable = soa::Join; + + void processPIDQA(CollsTable const& lrCollisions, TrksStandardTable const& standardTracks) + { + for (auto const& col : lrCollisions) { + + if (std::abs(col.posZ()) >= cfgSel.cfgVtxCut) { + continue; + } + + // EXTRACT CENTRALITY/MULTIPLICITY + float multiplicity = 1.0f; + if constexpr (requires { col.centrality(); }) { + if (cfgSel.isUseCentEst) + multiplicity = col.centrality(); + else + multiplicity = col.multiplicity(); + } else { + multiplicity = col.multiplicity(); + } + + auto tracks = standardTracks.sliceByCached(aod::track::collisionId, col.globalIndex(), this->cache); + + for (auto const& track : tracks) { + if (!track.isGlobalTrack()) + continue; + if (!myTrackFilter.IsSelected(track)) + continue; + + float tpcNsig = 0.0f, tofNsig = 0.0f; + if (cfgSel.cfgPidMask & KPidMaskPion) { + tpcNsig = track.tpcNSigmaPi(); + tofNsig = track.tofNSigmaPi(); + } else if (cfgSel.cfgPidMask & KPidMaskKaon) { + tpcNsig = track.tpcNSigmaKa(); + tofNsig = track.tofNSigmaKa(); + } else if (cfgSel.cfgPidMask & KPidMaskProton) { + tpcNsig = track.tpcNSigmaPr(); + tofNsig = track.tofNSigmaPr(); + } + + // FILL BEFORE CUTS + if (cfgSel.cfgGetNsigmaQA && (cfgSel.cfgPidMask == KPidMaskPion || cfgSel.cfgPidMask == KPidMaskKaon || cfgSel.cfgPidMask == KPidMaskProton)) { + histos.fill(HIST("TofTpcNsigma_before"), multiplicity, track.pt(), tpcNsig, tofNsig); + if (cfgSel.cfgGetdEdx) { + histos.fill(HIST("TpcdEdx_ptwise"), multiplicity, track.pt(), track.tpcSignal(), tofNsig); + } + } + + bool isTpcPion = (track.tpcNSigmaPi() > cfgSel.cfgPidNsigmaMin && track.tpcNSigmaPi() < cfgSel.cfgPidNsigmaMax); + bool isTpcKaon = (track.tpcNSigmaKa() > cfgSel.cfgPidNsigmaMin && track.tpcNSigmaKa() < cfgSel.cfgPidNsigmaMax); + bool isTpcProton = (track.tpcNSigmaPr() > cfgSel.cfgPidNsigmaMin && track.tpcNSigmaPr() < cfgSel.cfgPidNsigmaMax); + + bool isTofPion = (track.tofNSigmaPi() > cfgSel.cfgPidNsigmaMin && track.tofNSigmaPi() < cfgSel.cfgPidNsigmaMax); + bool isTofKaon = (track.tofNSigmaKa() > cfgSel.cfgPidNsigmaMin && track.tofNSigmaKa() < cfgSel.cfgPidNsigmaMax); + bool isTofProton = (track.tofNSigmaPr() > cfgSel.cfgPidNsigmaMin && track.tofNSigmaPr() < cfgSel.cfgPidNsigmaMax); + + bool isPion = false, isKaon = false, isProton = false; + if (track.pt() > cfgSel.cfgTofPidPtCut && track.hasTOF()) { + isPion = isTofPion && isTpcPion; + isKaon = isTofKaon && isTpcKaon; + isProton = isTofProton && isTpcProton; + } else if (!(track.pt() > cfgSel.cfgTofPidPtCut && !track.hasTOF())) { + isPion = isTpcPion; + isKaon = isTpcKaon; + isProton = isTpcProton; + } + + if ((isPion && isKaon) || (isPion && isProton) || (isKaon && isProton)) { + isPion = isKaon = isProton = false; + } + + bool isTargetParticle = ((cfgSel.cfgPidMask & KPidMaskPion) && isPion) || ((cfgSel.cfgPidMask & KPidMaskKaon) && isKaon) || ((cfgSel.cfgPidMask & KPidMaskProton) && isProton); + + // FILL AFTER CUTS + if (cfgSel.cfgGetNsigmaQA && isTargetParticle) { + histos.fill(HIST("TofTpcNsigma_after"), multiplicity, track.pt(), tpcNsig, tofNsig); + if (cfgSel.cfgGetdEdx) { + histos.fill(HIST("TpcdEdx_ptwise_afterCut"), multiplicity, track.pt(), track.tpcSignal(), tofNsig); + } + } + } + } + } + PROCESS_SWITCH(LongrangecorrDerived, processTpcft0aSE, "same event TPC vs FT0A", false); PROCESS_SWITCH(LongrangecorrDerived, processTpcft0aME, "mixed event TPC vs FT0A", false); PROCESS_SWITCH(LongrangecorrDerived, processTpcft0cSE, "same event TPC vs FT0C", false); @@ -1067,6 +1207,7 @@ struct LongrangecorrDerived { PROCESS_SWITCH(LongrangecorrDerived, processMcGenFt0aft0cSE, "same MC gen event FT0A vs FT0C", false); PROCESS_SWITCH(LongrangecorrDerived, processMcGenFt0aft0cME, "mixed MC gen event FT0A vs FT0C", false); PROCESS_SWITCH(LongrangecorrDerived, processTPCtrackEff, "process TPC track efficiency", false); + PROCESS_SWITCH(LongrangecorrDerived, processPIDQA, "process PID QA on standard AODs", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) From 9f4727e9a1d573570f6511dd9413da3c38dc2165 Mon Sep 17 00:00:00 2001 From: Soumyadip Ghosh Date: Thu, 6 Aug 2026 20:13:25 +0530 Subject: [PATCH 2/3] Fix static_cast usage in logging statements --- PWGCF/TwoParticleCorrelations/Tasks/longrangecorrDerived.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PWGCF/TwoParticleCorrelations/Tasks/longrangecorrDerived.cxx b/PWGCF/TwoParticleCorrelations/Tasks/longrangecorrDerived.cxx index 5eba4376929..56a324b7ae8 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/longrangecorrDerived.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/longrangecorrDerived.cxx @@ -331,7 +331,7 @@ struct LongrangecorrDerived { if (hTrkEff == nullptr) { LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgSel.cfgEffccdbPath.value.c_str()); } - LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgSel.cfgEffccdbPath.value.c_str(), (void*)hTrkEff); + LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgSel.cfgEffccdbPath.value.c_str(), static_cast(hTrkEff)); } if (cfgSel.cfgAccccdbPath.value.empty() == false) { @@ -339,7 +339,7 @@ struct LongrangecorrDerived { if (hTrkAcc == nullptr) { LOGF(fatal, "Could not load NUA histogram for trigger particles from %s", cfgSel.cfgAccccdbPath.value.c_str()); } - LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgSel.cfgAccccdbPath.value.c_str(), (void*)hTrkAcc); + LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgSel.cfgAccccdbPath.value.c_str(), static_cast(hTrkAcc)); } fLoadTrkEffCorr = true; From e93acc422c0be57dd8b34c80f1e3bd306b42ae3c Mon Sep 17 00:00:00 2001 From: Soumyadip Ghosh Date: Thu, 6 Aug 2026 20:34:24 +0530 Subject: [PATCH 3/3] Fix static_cast syntax in logging statements --- PWGCF/TwoParticleCorrelations/Tasks/longrangecorrDerived.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PWGCF/TwoParticleCorrelations/Tasks/longrangecorrDerived.cxx b/PWGCF/TwoParticleCorrelations/Tasks/longrangecorrDerived.cxx index 56a324b7ae8..7471acec39b 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/longrangecorrDerived.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/longrangecorrDerived.cxx @@ -331,7 +331,7 @@ struct LongrangecorrDerived { if (hTrkEff == nullptr) { LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgSel.cfgEffccdbPath.value.c_str()); } - LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgSel.cfgEffccdbPath.value.c_str(), static_cast(hTrkEff)); + LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgSel.cfgEffccdbPath.value.c_str(), static_cast(hTrkEff)); } if (cfgSel.cfgAccccdbPath.value.empty() == false) { @@ -339,7 +339,7 @@ struct LongrangecorrDerived { if (hTrkAcc == nullptr) { LOGF(fatal, "Could not load NUA histogram for trigger particles from %s", cfgSel.cfgAccccdbPath.value.c_str()); } - LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgSel.cfgAccccdbPath.value.c_str(), static_cast(hTrkAcc)); + LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgSel.cfgAccccdbPath.value.c_str(), static_cast(hTrkAcc)); } fLoadTrkEffCorr = true;