From d8ce85010cc9b314b486a1b133984703c71196e5 Mon Sep 17 00:00:00 2001 From: Dushmanta Sahu Date: Tue, 20 Jan 2026 08:44:40 -0600 Subject: [PATCH 01/10] =?UTF-8?q?=E2=80=9CCOMENTARIO=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PWGLF/Tasks/Nuspex/CMakeLists.txt | 5 + PWGLF/Tasks/Nuspex/multiplicitypt.cxx | 960 ++++++++++++++++++++++++++ 2 files changed, 965 insertions(+) create mode 100644 PWGLF/Tasks/Nuspex/multiplicitypt.cxx diff --git a/PWGLF/Tasks/Nuspex/CMakeLists.txt b/PWGLF/Tasks/Nuspex/CMakeLists.txt index 0247878bf85..a4ba69bfb88 100644 --- a/PWGLF/Tasks/Nuspex/CMakeLists.txt +++ b/PWGLF/Tasks/Nuspex/CMakeLists.txt @@ -180,4 +180,9 @@ o2physics_add_dpl_workflow(chargedparticle-raa PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(multiplicity-pt + SOURCES multiplicitypt.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + endif() diff --git a/PWGLF/Tasks/Nuspex/multiplicitypt.cxx b/PWGLF/Tasks/Nuspex/multiplicitypt.cxx new file mode 100644 index 00000000000..d3d64510028 --- /dev/null +++ b/PWGLF/Tasks/Nuspex/multiplicitypt.cxx @@ -0,0 +1,960 @@ +#include "Common/Core/TrackSelection.h" +#include "Common/Core/TrackSelectionDefaults.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/TrackSelectionTables.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/PIDResponse.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/PIDResponseTOF.h" + +#include "Framework/ASoAHelpers.h" +#include "Framework/AnalysisDataModel.h" +#include "Framework/AnalysisTask.h" +#include "Framework/HistogramRegistry.h" +#include "Framework/O2DatabasePDGPlugin.h" +#include "Framework/StaticFor.h" +#include "Framework/runDataProcessing.h" +#include "ReconstructionDataFormats/Track.h" +#include "PWGLF/Utils/inelGt.h" + +#include "TDatabasePDG.h" +#include +#include +#include +#include +#include + +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using BCsRun3 = soa::Join; + +struct multiplicitypt { + + // Service + Service pdg; + + // Configurables - Matching spectraTOF approach + Configurable isRun3{"isRun3", true, "is Run3 dataset"}; + + // Event selection configurables (spectraTOF style) + Configurable cfgCutVertex{"cfgCutVertex", 10.0f, "Accepted z-vertex range"}; + Configurable cfgINELCut{"cfgINELCut", 0, "INEL event selection: 0 no sel, 1 INEL>0, 2 INEL>1"}; + Configurable askForCustomTVX{"askForCustomTVX", false, "Ask for custom TVX rather than sel8"}; + + // SET ALL THESE TO FALSE LIKE SPECTRATOF: + Configurable removeITSROFrameBorder{"removeITSROFrameBorder", false, "Remove ITS Read-Out Frame border"}; + Configurable removeNoSameBunchPileup{"removeNoSameBunchPileup", false, "Remove no same bunch pileup"}; + Configurable requireIsGoodZvtxFT0vsPV{"requireIsGoodZvtxFT0vsPV", false, "Require good Z vertex FT0 vs PV"}; + Configurable requireIsVertexITSTPC{"requireIsVertexITSTPC", false, "Require vertex ITSTPC"}; + Configurable removeNoTimeFrameBorder{"removeNoTimeFrameBorder", false, "Remove no time frame border"}; + + // Track selection configurables (spectraTOF style) + Configurable cfgCutEtaMax{"cfgCutEtaMax", 0.8f, "Max eta range for tracks"}; + Configurable cfgCutEtaMin{"cfgCutEtaMin", -0.8f, "Min eta range for tracks"}; + Configurable cfgCutY{"cfgCutY", 0.5f, "Y range for tracks"}; + Configurable cfgCutNsigma{"cfgCutNsigma", 3.0f, "nsigma cut range for tracks"}; + Configurable lastRequiredTrdCluster{"lastRequiredTrdCluster", -1, "Last cluster to require in TRD"}; + Configurable requireTrdOnly{"requireTrdOnly", false, "Require only tracks from TRD"}; + Configurable requireNoTrd{"requireNoTrd", false, "Require tracks without TRD"}; + + // Multiplicity estimator (like spectraTOF) + Configurable multiplicityEstimator{"multiplicityEstimator", 6, + "Multiplicity estimator: 0=NoMult, 1=MultFV0M, 2=MultFT0M, 3=MultFDDM, 4=MultTracklets, 5=MultTPC, 6=MultNTracksPV, 7=MultNTracksPVeta1, 8=CentFT0C, 9=CentFT0M, 10=CentFV0A"}; + + // Analysis switches + Configurable enableDCAHistograms{"enableDCAHistograms", false, "Enable DCA histograms"}; + Configurable enablePIDHistograms{"enablePIDHistograms", true, "Enable PID histograms"}; + + // Track cut configurables (spectraTOF style) + Configurable useCustomTrackCuts{"useCustomTrackCuts", true, "Flag to use custom track cuts"}; + Configurable itsPattern{"itsPattern", 0, "0 = Run3ITSibAny, 1 = Run3ITSallAny, 2 = Run3ITSall7Layers, 3 = Run3ITSibTwo"}; + Configurable requireITS{"requireITS", true, "Additional cut on the ITS requirement"}; + Configurable requireTPC{"requireTPC", true, "Additional cut on the TPC requirement"}; + Configurable requireGoldenChi2{"requireGoldenChi2", true, "Additional cut on the GoldenChi2"}; + Configurable minNCrossedRowsTPC{"minNCrossedRowsTPC", 70.f, "Additional cut on the minimum number of crossed rows in the TPC"}; + Configurable minNCrossedRowsOverFindableClustersTPC{"minNCrossedRowsOverFindableClustersTPC", 0.8f, "Additional cut on the minimum value of the ratio between crossed rows and findable clusters in the TPC"}; + Configurable maxChi2PerClusterTPC{"maxChi2PerClusterTPC", 4.f, "Additional cut on the maximum value of the chi2 per cluster in the TPC"}; + Configurable minChi2PerClusterTPC{"minChi2PerClusterTPC", 0.5f, "Additional cut on the minimum value of the chi2 per cluster in the TPC"}; + Configurable maxChi2PerClusterITS{"maxChi2PerClusterITS", 36.f, "Additional cut on the maximum value of the chi2 per cluster in the ITS"}; + Configurable maxDcaXYFactor{"maxDcaXYFactor", 1.f, "Additional cut on the maximum value of the DCA xy (multiplicative factor)"}; + Configurable maxDcaZ{"maxDcaZ", 0.1f, "Additional cut on the maximum value of the DCA z"}; + Configurable minTPCNClsFound{"minTPCNClsFound", 100.f, "Additional cut on the minimum value of the number of found clusters in the TPC"}; + Configurable min_ITS_nClusters{"min_ITS_nClusters", 5, "minimum number of found ITS clusters"}; + + // Basic track cuts + Configurable cfgTrkEtaCut{"cfgTrkEtaCut", 0.8f, "Eta range for tracks"}; + Configurable cfgTrkLowPtCut{"cfgTrkLowPtCut", 0.15f, "Minimum constituent pT"}; + + // Custom track cuts matching spectraTOF + TrackSelection customTrackCuts; + + // Histogram Registry + HistogramRegistry ue; + + // Table definitions - EXACT spectraTOF approach + using CollisionTableData = soa::Join; + using CollisionTableMC = soa::Join; + + // Track tables - TPC PID only + using TrackTableData = soa::Join; + using TrackTableMC = soa::Join; + + // MC tables - EXACT spectraTOF approach + using CollisionTableMCTrue = aod::McCollisions; + using ParticleTableMC = aod::McParticles; + + // Preslice for MC particles (like spectraTOF) + Preslice perMCCol = aod::mcparticle::mcCollisionId; + + // Multiplicity estimator enum (like spectraTOF) + enum MultCodes : int { + kNoMultiplicity = 0, + kMultFV0M = 1, + kMultFT0M = 2, + kMultFDDM = 3, + kMultTracklets = 4, + kMultTPC = 5, + kMultNTracksPV = 6, + kMultNTracksPVeta1 = 7, + kCentralityFT0C = 8, + kCentralityFT0M = 9, + kCentralityFV0A = 10 + }; + + // Particle species enum (from spectraTOF) + enum ParticleSpecies : int { + kPion = 0, + kKaon = 1, + kProton = 2, + kNSpecies = 3 + }; + + // PDG codes + static constexpr int PDGPion = 211; + static constexpr int PDGKaon = 321; + static constexpr int PDGProton = 2212; + + // ======================================================================== + // PROCESS FUNCTION DECLARATIONS - SPECTRATOF STYLE + // ======================================================================== + + // Data processing + void processData(CollisionTableData::iterator const& collision, + TrackTableData const& tracks); + PROCESS_SWITCH(multiplicitypt, processData, "process data", false); + + // MC processing - EXACT spectraTOF approach + void processMC(TrackTableMC const& tracks, + aod::McParticles const& particles, + CollisionTableMCTrue const& mcCollisions, + CollisionTableMC const& collisions); + PROCESS_SWITCH(multiplicitypt, processMC, "process MC", true); + + // True MC processing - EXACT spectraTOF approach + void processTrue(CollisionTableMCTrue const& mcCollisions, + ParticleTableMC const& particles); + PROCESS_SWITCH(multiplicitypt, processTrue, "process true MC", true); + + // ======================================================================== + // TRACK SELECTION FUNCTIONS - MATCHING spectraTOF + // ======================================================================== + + template + bool passesCutWoDCA(TrackType const& track) const + { + if (useCustomTrackCuts.value) { + for (int i = 0; i < static_cast(TrackSelection::TrackCuts::kNCuts); i++) { + if (i == static_cast(TrackSelection::TrackCuts::kDCAxy) || + i == static_cast(TrackSelection::TrackCuts::kDCAz)) { + continue; + } + if (!customTrackCuts.IsSelected(track, static_cast(i))) { + return false; + } + } + return true; + } + return track.isGlobalTrackWoDCA(); + } + + template + bool passesDCAxyCut(TrackType const& track) const + { + if (useCustomTrackCuts.value) { + if (!passesCutWoDCA(track)) { + return false; + } + const float maxDcaXY = maxDcaXYFactor.value * (0.0105f + 0.0350f / std::pow(track.pt(), 1.1f)); + if (std::abs(track.dcaXY()) > maxDcaXY) { + return false; + } + return true; + } + return track.isGlobalTrack(); + } + + template + bool passesTrackSelection(TrackType const& track) const + { + if (track.eta() < cfgCutEtaMin.value || track.eta() > cfgCutEtaMax.value) + return false; + + if (track.tpcChi2NCl() < minChi2PerClusterTPC.value || track.tpcChi2NCl() > maxChi2PerClusterTPC.value) + return false; + + if (!passesCutWoDCA(track)) + return false; + + return passesDCAxyCut(track); + } + + // ======================================================================== + // PID SELECTION FUNCTIONS - TPC ONLY (OLD NON-EXCLUSIVE METHOD) + // ======================================================================== + + template + bool passesPIDSelection(TrackType const& track) const + { + float nsigmaTPC = 0.f; + + if constexpr (species == kPion) { + nsigmaTPC = track.tpcNSigmaPi(); + } else if constexpr (species == kKaon) { + nsigmaTPC = track.tpcNSigmaKa(); + } else if constexpr (species == kProton) { + nsigmaTPC = track.tpcNSigmaPr(); + } + + // TPC-only PID (works for all pT, but better at low pT < 1 GeV/c) + return (std::abs(nsigmaTPC) < cfgCutNsigma.value); + } + + // ======================================================================== + // EXCLUSIVE PID SELECTION - Returns best hypothesis for a track + // ======================================================================== + + template + int getBestPIDHypothesis(TrackType const& track) const + { + // Return values: -1 = no ID, 0 = pion, 1 = kaon, 2 = proton + + float nsigmaPi = std::abs(track.tpcNSigmaPi()); + float nsigmaKa = std::abs(track.tpcNSigmaKa()); + float nsigmaPr = std::abs(track.tpcNSigmaPr()); + + // Find the hypothesis with smallest |nσ| that passes the cut + float minNSigma = 999.0f; + int bestSpecies = -1; + + if (nsigmaPi < cfgCutNsigma.value && nsigmaPi < minNSigma) { + minNSigma = nsigmaPi; + bestSpecies = kPion; + } + if (nsigmaKa < cfgCutNsigma.value && nsigmaKa < minNSigma) { + minNSigma = nsigmaKa; + bestSpecies = kKaon; + } + if (nsigmaPr < cfgCutNsigma.value && nsigmaPr < minNSigma) { + minNSigma = nsigmaPr; + bestSpecies = kProton; + } + + return bestSpecies; + } + + // ======================================================================== + // EVENT SELECTION FUNCTION - EXACT spectraTOF + // ======================================================================== + + template + bool isEventSelected(CollisionType const& collision) + { + if constexpr (fillHistograms) { + ue.fill(HIST("evsel"), 1.f); + if (collision.isInelGt0()) ue.fill(HIST("evsel"), 2.f); + if (collision.isInelGt1()) ue.fill(HIST("evsel"), 3.f); + } + + if (askForCustomTVX.value) { + if (!collision.selection_bit(aod::evsel::kIsTriggerTVX)) return false; + } else { + if (!collision.sel8()) return false; + } + + if constexpr (fillHistograms) ue.fill(HIST("evsel"), 4.f); + + if (removeITSROFrameBorder.value && !collision.selection_bit(aod::evsel::kNoITSROFrameBorder)) return false; + if constexpr (fillHistograms) ue.fill(HIST("evsel"), 5.f); + + if (removeNoSameBunchPileup.value && !collision.selection_bit(aod::evsel::kNoSameBunchPileup)) return false; + if constexpr (fillHistograms) ue.fill(HIST("evsel"), 6.f); + + if (requireIsGoodZvtxFT0vsPV.value && !collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV)) return false; + if constexpr (fillHistograms) ue.fill(HIST("evsel"), 7.f); + + if (requireIsVertexITSTPC.value && !collision.selection_bit(aod::evsel::kIsVertexITSTPC)) return false; + if constexpr (fillHistograms) ue.fill(HIST("evsel"), 8.f); + + if (removeNoTimeFrameBorder.value && !collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) return false; + if constexpr (fillHistograms) ue.fill(HIST("evsel"), 9.f); + + if (std::abs(collision.posZ()) > cfgCutVertex.value) return false; + + if constexpr (fillHistograms) { + ue.fill(HIST("evsel"), 13.f); + if (collision.isInelGt0()) ue.fill(HIST("evsel"), 14.f); + if (collision.isInelGt1()) ue.fill(HIST("evsel"), 15.f); + } + + if (cfgINELCut.value == 1 && !collision.isInelGt0()) return false; + if (cfgINELCut.value == 2 && !collision.isInelGt1()) return false; + + return true; + } + + // ======================================================================== + // PRIMARY SELECTION - MATCHING spectraTOF + // ======================================================================== + + template + bool isGoodPrimary(ParticleType const& particle) const + { + auto pdgParticle = pdg->GetParticle(particle.pdgCode()); + if (!pdgParticle || pdgParticle->Charge() == 0.) + return false; + + if (!particle.isPhysicalPrimary()) + return false; + + if (std::abs(particle.eta()) >= cfgCutEtaMax.value) + return false; + if (particle.pt() < cfgTrkLowPtCut.value) + return false; + + if (std::abs(particle.y()) > cfgCutY.value) + return false; + + return true; + } + + // Particle-specific primary selection + template + bool isGoodPrimarySpecies(ParticleType const& particle) const + { + int pdgCode = std::abs(particle.pdgCode()); + int expectedPDG = 0; + + if constexpr (species == kPion) expectedPDG = PDGPion; + else if constexpr (species == kKaon) expectedPDG = PDGKaon; + else if constexpr (species == kProton) expectedPDG = PDGProton; + + if (pdgCode != expectedPDG) return false; + + return isGoodPrimary(particle); + } + + void init(InitContext const&); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} + +void multiplicitypt::init(InitContext const&) +{ + // ======================================================================== + // CUSTOM TRACK CUTS INITIALIZATION - MATCHING spectraTOF + // ======================================================================== + + if (useCustomTrackCuts.value) { + LOG(info) << "Using custom track cuts matching spectraTOF approach"; + customTrackCuts = getGlobalTrackSelectionRun3ITSMatch(itsPattern.value); + + customTrackCuts.SetRequireITSRefit(requireITS.value); + customTrackCuts.SetRequireTPCRefit(requireTPC.value); + customTrackCuts.SetMinNClustersITS(min_ITS_nClusters.value); + customTrackCuts.SetRequireGoldenChi2(requireGoldenChi2.value); + customTrackCuts.SetMaxChi2PerClusterTPC(maxChi2PerClusterTPC.value); + customTrackCuts.SetMaxChi2PerClusterITS(maxChi2PerClusterITS.value); + customTrackCuts.SetMinNCrossedRowsTPC(minNCrossedRowsTPC.value); + customTrackCuts.SetMinNClustersTPC(minTPCNClsFound.value); + customTrackCuts.SetMinNCrossedRowsOverFindableClustersTPC(minNCrossedRowsOverFindableClustersTPC.value); + customTrackCuts.SetMaxDcaXYPtDep([](float /*pt*/) { return 10000.f; }); + customTrackCuts.SetMaxDcaZ(maxDcaZ.value); + + customTrackCuts.print(); + } + + // ======================================================================== + // AXIS DEFINITIONS + // ======================================================================== + + ConfigurableAxis ptBinning{ + "ptBinning", + {0.0, 0.1, 0.15, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, + 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2, 1.3, 1.4, + 1.5, 1.6, 1.7, 1.8, 1.9, 2.0, 2.2, 2.4, 2.6, 2.8, + 3.0, 3.5, 4.0, 4.5, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, + 12.0, 14.0, 16.0, 18.0, 20.0, 25.0, 30.0, 40.0, 50.0}, + "pT bin limits" + }; + AxisSpec ptAxis = {ptBinning, "#it{p}_{T} (GeV/#it{c})"}; + + // ======================================================================== + // HISTOGRAM REGISTRY - INCLUSIVE + PARTICLE-SPECIFIC + // ======================================================================== + + // Event counting - EXACT spectraTOF approach + ue.add("MC/GenRecoCollisions", "Generated and Reconstructed MC Collisions", HistType::kTH1D, {{10, 0.5, 10.5}}); + auto hColl = ue.get(HIST("MC/GenRecoCollisions")); + hColl->GetXaxis()->SetBinLabel(1, "Collisions generated"); + hColl->GetXaxis()->SetBinLabel(2, "Collisions reconstructed"); + + // CRITICAL: Complete event counting system + ue.add("hEventsAllGen", "All generated events", HistType::kTH1F, {{1, 0.5, 1.5}}); + ue.add("hEventsPassPhysicsSelection", "Events passing physics selection", HistType::kTH1F, {{1, 0.5, 1.5}}); + ue.add("hEventsReconstructable", "Physics-selected events with reconstruction", HistType::kTH1F, {{1, 0.5, 1.5}}); + ue.add("hEventsSelectedReco", "Selected reconstructed events", HistType::kTH1F, {{1, 0.5, 1.5}}); + + // Event loss breakdown histogram + ue.add("hEventLossBreakdown", "Event loss breakdown", HistType::kTH1D, {{4, 0.5, 4.5}}); + auto hLoss = ue.get(HIST("hEventLossBreakdown")); + hLoss->GetXaxis()->SetBinLabel(1, "Physics selected"); + hLoss->GetXaxis()->SetBinLabel(2, "Reconstructed"); + hLoss->GetXaxis()->SetBinLabel(3, "Selected"); + hLoss->GetXaxis()->SetBinLabel(4, "Final efficiency"); + + // ======================================================================== + // INCLUSIVE CHARGED PARTICLE HISTOGRAMS + // ======================================================================== + + // ALL generated primaries (before any physics selection) + ue.add("Inclusive/hPtPrimGenAll", "All generated primaries (no cuts);#it{p}_{T} (GeV/#it{c});Counts", + HistType::kTH1D, {ptAxis}); + + // Generated primaries AFTER physics selection + ue.add("Inclusive/hPtPrimGen", "Generated primaries (after physics selection);#it{p}_{T} (GeV/#it{c});Counts", + HistType::kTH1D, {ptAxis}); + + // Tracking Efficiency + ue.add("Inclusive/hPtNumEff", "Tracking efficiency numerator;#it{p}_{T} (GeV/#it{c});Counts", + HistType::kTH1D, {ptAxis}); + ue.add("Inclusive/hPtDenEff", "Tracking efficiency denominator;#it{p}_{T} (GeV/#it{c});Counts", + HistType::kTH1D, {ptAxis}); + + // Primary Fraction + ue.add("Inclusive/hPtAllReco", "All reconstructed tracks;#it{p}_{T} (GeV/#it{c});Counts", + HistType::kTH1D, {ptAxis}); + ue.add("Inclusive/hPtPrimReco", "Reconstructed primaries;#it{p}_{T} (GeV/#it{c});Counts", + HistType::kTH1D, {ptAxis}); + ue.add("Inclusive/hPtSecReco", "Reconstructed secondaries;#it{p}_{T} (GeV/#it{c});Counts", + HistType::kTH1D, {ptAxis}); + + // Measured spectra + ue.add("Inclusive/hPtMeasured", "All measured tracks;#it{p}_{T} (GeV/#it{c});Counts", + HistType::kTH1D, {ptAxis}); + + // ======================================================================== + // PARTICLE-SPECIFIC HISTOGRAMS (Pions, Kaons, Protons) + // ======================================================================== + + const std::array particleNames = {"Pion", "Kaon", "Proton"}; + const std::array particleSymbols = {"#pi^{#pm}", "K^{#pm}", "p+#bar{p}"}; + + for (int iSpecies = 0; iSpecies < kNSpecies; ++iSpecies) { + const auto& name = particleNames[iSpecies]; + const auto& symbol = particleSymbols[iSpecies]; + + // Generated histograms + ue.add(Form("%s/hPtPrimGenAll", name.c_str()), + Form("All generated %s (no cuts);#it{p}_{T} (GeV/#it{c});Counts", symbol.c_str()), + HistType::kTH1D, {ptAxis}); + + ue.add(Form("%s/hPtPrimGen", name.c_str()), + Form("Generated %s (after physics selection);#it{p}_{T} (GeV/#it{c});Counts", symbol.c_str()), + HistType::kTH1D, {ptAxis}); + + // Tracking efficiency + ue.add(Form("%s/hPtNumEff", name.c_str()), + Form("%s tracking efficiency numerator;#it{p}_{T} (GeV/#it{c});Counts", symbol.c_str()), + HistType::kTH1D, {ptAxis}); + + ue.add(Form("%s/hPtDenEff", name.c_str()), + Form("%s tracking efficiency denominator;#it{p}_{T} (GeV/#it{c});Counts", symbol.c_str()), + HistType::kTH1D, {ptAxis}); + + // Reconstructed histograms + ue.add(Form("%s/hPtAllReco", name.c_str()), + Form("All reconstructed %s;#it{p}_{T} (GeV/#it{c});Counts", symbol.c_str()), + HistType::kTH1D, {ptAxis}); + + ue.add(Form("%s/hPtPrimReco", name.c_str()), + Form("Reconstructed primary %s;#it{p}_{T} (GeV/#it{c});Counts", symbol.c_str()), + HistType::kTH1D, {ptAxis}); + + ue.add(Form("%s/hPtSecReco", name.c_str()), + Form("Reconstructed secondary %s;#it{p}_{T} (GeV/#it{c});Counts", symbol.c_str()), + HistType::kTH1D, {ptAxis}); + + // Measured spectra + ue.add(Form("%s/hPtMeasured", name.c_str()), + Form("Measured %s;#it{p}_{T} (GeV/#it{c});Counts", symbol.c_str()), + HistType::kTH1D, {ptAxis}); + + // PID quality histograms - TPC ONLY + if (enablePIDHistograms) { + ue.add(Form("%s/hNsigmaTPC", name.c_str()), + Form("TPC n#sigma %s;#it{p}_{T} (GeV/#it{c});n#sigma_{TPC}", symbol.c_str()), + HistType::kTH2D, {ptAxis, {200, -10, 10}}); + } + } + + // ======================================================================== + // MONITORING HISTOGRAMS + // ======================================================================== + ue.add("evsel", "Event selection", HistType::kTH1D, {{20, 0.5, 20.5}}); + auto h = ue.get(HIST("evsel")); + h->GetXaxis()->SetBinLabel(1, "Events read"); + h->GetXaxis()->SetBinLabel(2, "INEL>0"); + h->GetXaxis()->SetBinLabel(3, "INEL>1"); + h->GetXaxis()->SetBinLabel(4, "Trigger passed"); + h->GetXaxis()->SetBinLabel(5, "NoITSROFrameBorder"); + h->GetXaxis()->SetBinLabel(6, "NoSameBunchPileup"); + h->GetXaxis()->SetBinLabel(7, "IsGoodZvtxFT0vsPV"); + h->GetXaxis()->SetBinLabel(8, "IsVertexITSTPC"); + h->GetXaxis()->SetBinLabel(9, "NoTimeFrameBorder"); + h->GetXaxis()->SetBinLabel(13, "posZ passed"); + h->GetXaxis()->SetBinLabel(14, "INEL>0 (final)"); + h->GetXaxis()->SetBinLabel(15, "INEL>1 (final)"); + + ue.add("hEta", "Track eta;#eta;Counts", HistType::kTH1D, {{20, -0.8, 0.8}}); + ue.add("hPhi", "Track phi;#varphi (rad);Counts", HistType::kTH1D, {{64, 0, 2.0 * o2::constants::math::PI}}); + ue.add("hvtxZ", "Vertex Z (data);Vertex Z (cm);Events", HistType::kTH1F, {{40, -20.0, 20.0}}); + ue.add("hvtxZmc", "MC vertex Z;Vertex Z (cm);Events", HistType::kTH1F, {{40, -20.0, 20.0}}); + + LOG(info) << "Initialized multiplicitypt task with EXCLUSIVE PID for INCLUSIVE + PARTICLE-SPECIFIC (Pi, K, p) analysis"; +} + +// ======================================================================== +// DATA PROCESSING - WITH EXCLUSIVE PID +// ======================================================================== +void multiplicitypt::processData(CollisionTableData::iterator const& collision, TrackTableData const& tracks) +{ + if (!isEventSelected(collision)) { + return; + } + ue.fill(HIST("hvtxZ"), collision.posZ()); + + for (const auto& track : tracks) { + if (!passesTrackSelection(track)) { + continue; + } + + // Inclusive charged particle (always filled) + ue.fill(HIST("Inclusive/hPtMeasured"), track.pt()); + ue.fill(HIST("hEta"), track.eta()); + ue.fill(HIST("hPhi"), track.phi()); + + // Exclusive particle identification + int bestSpecies = getBestPIDHypothesis(track); + + if (bestSpecies == kPion) { + ue.fill(HIST("Pion/hPtMeasured"), track.pt()); + if (enablePIDHistograms) { + ue.fill(HIST("Pion/hNsigmaTPC"), track.pt(), track.tpcNSigmaPi()); + } + } + else if (bestSpecies == kKaon) { + ue.fill(HIST("Kaon/hPtMeasured"), track.pt()); + if (enablePIDHistograms) { + ue.fill(HIST("Kaon/hNsigmaTPC"), track.pt(), track.tpcNSigmaKa()); + } + } + else if (bestSpecies == kProton) { + ue.fill(HIST("Proton/hPtMeasured"), track.pt()); + if (enablePIDHistograms) { + ue.fill(HIST("Proton/hNsigmaTPC"), track.pt(), track.tpcNSigmaPr()); + } + } + } +} + +// ======================================================================== +// MC PROCESSING - WITH FIXED PRIMARY FRACTION CALCULATION +// ======================================================================== +void multiplicitypt::processMC(TrackTableMC const& tracks, + aod::McParticles const& particles, + CollisionTableMCTrue const& mcCollisions, + CollisionTableMC const& collisions) +{ + LOG(info) << "=== DEBUG processMC START ==="; + LOG(info) << "MC collisions: " << mcCollisions.size(); + LOG(info) << "Reconstructed collisions: " << collisions.size(); + + // ======================================================================== + // STEP 1: Identify which MC collisions are reconstructable + // ======================================================================== + + std::set reconstructableMCCollisions; + + for (const auto& mcCollision : mcCollisions) { + auto particlesInCollision = particles.sliceBy(perMCCol, mcCollision.globalIndex()); + + if (std::abs(mcCollision.posZ()) > cfgCutVertex.value) { + continue; + } + if (cfgINELCut.value == 1 && !o2::pwglf::isINELgt0mc(particlesInCollision, pdg)) { + continue; + } + if (cfgINELCut.value == 2 && !o2::pwglf::isINELgt1mc(particlesInCollision, pdg)) { + continue; + } + + reconstructableMCCollisions.insert(mcCollision.globalIndex()); + } + + LOG(info) << "DEBUG: Physics-selected MC collisions: " << reconstructableMCCollisions.size(); + + // ======================================================================== + // STEP 2: Track reconstruction outcomes + // ======================================================================== + + std::set reconstructedMCCollisions; + std::set selectedMCCollisions; + std::set selectedCollisionIndices; + + for (const auto& collision : collisions) { + if (!collision.has_mcCollision()) { + continue; + } + + const auto& mcCollision = collision.mcCollision_as(); + int64_t mcCollId = mcCollision.globalIndex(); + + if (reconstructableMCCollisions.find(mcCollId) == reconstructableMCCollisions.end()) { + continue; + } + + reconstructedMCCollisions.insert(mcCollId); + + if (isEventSelected(collision)) { + selectedMCCollisions.insert(mcCollId); + selectedCollisionIndices.insert(collision.globalIndex()); + ue.fill(HIST("hvtxZ"), collision.posZ()); + } + } + + auto hEventsReconstructable = ue.get(HIST("hEventsReconstructable")); + auto hEventsSelectedReco = ue.get(HIST("hEventsSelectedReco")); + + hEventsReconstructable->SetBinContent(1, reconstructedMCCollisions.size()); + hEventsSelectedReco->SetBinContent(1, selectedMCCollisions.size()); + + int nReconstructableTotal = reconstructableMCCollisions.size(); + int nReconstructableWithReco = reconstructedMCCollisions.size(); + int nSelectedReco = selectedMCCollisions.size(); + + LOG(info) << "DEBUG: Reconstructed MC collisions: " << nReconstructableWithReco; + LOG(info) << "DEBUG: Selected MC collisions: " << nSelectedReco; + + if (nReconstructableTotal > 0) { + ue.fill(HIST("hEventLossBreakdown"), 1, nReconstructableTotal); + ue.fill(HIST("hEventLossBreakdown"), 2, nReconstructableWithReco); + ue.fill(HIST("hEventLossBreakdown"), 3, nSelectedReco); + ue.fill(HIST("hEventLossBreakdown"), 4, (nSelectedReco * 100.0 / nReconstructableTotal)); + } + + // ======================================================================== + // STEP 3: Process tracks with EXCLUSIVE PID - FIXED PRIMARY FRACTION + // ======================================================================== + + int totalTracksProcessed = 0; + int tracksFromSelectedEvents = 0; + int tracksPassingSelection = 0; + + std::array particleTracksIdentified = {0}; + std::array particleTracksPrimary = {0}; + std::array particleTracksSecondary = {0}; + + for (const auto& track : tracks) { + totalTracksProcessed++; + + if (!track.has_collision()) continue; + + const auto& collision = track.collision_as(); + + if (selectedCollisionIndices.find(collision.globalIndex()) == selectedCollisionIndices.end()) { + continue; + } + tracksFromSelectedEvents++; + + if (!passesTrackSelection(track)) continue; + tracksPassingSelection++; + + // ======================================================================== + // INCLUSIVE CHARGED PARTICLE ANALYSIS + // ======================================================================== + + ue.fill(HIST("Inclusive/hPtMeasured"), track.pt()); + ue.fill(HIST("Inclusive/hPtAllReco"), track.pt()); + ue.fill(HIST("hEta"), track.eta()); + ue.fill(HIST("hPhi"), track.phi()); + + // ======================================================================== + // EFFICIENCY NUMERATOR: Fill based on TRUE particle type + // ======================================================================== + + if (track.has_mcParticle()) { + const auto& particle = track.mcParticle(); + int pdgCode = std::abs(particle.pdgCode()); + + if (particle.isPhysicalPrimary()) { + ue.fill(HIST("Inclusive/hPtNumEff"), particle.pt()); + ue.fill(HIST("Inclusive/hPtPrimReco"), track.pt()); + + // Fill particle-specific efficiency numerator based on TRUE type + if (pdgCode == PDGPion) { + ue.fill(HIST("Pion/hPtNumEff"), particle.pt()); + } + if (pdgCode == PDGKaon) { + ue.fill(HIST("Kaon/hPtNumEff"), particle.pt()); + } + if (pdgCode == PDGProton) { + ue.fill(HIST("Proton/hPtNumEff"), particle.pt()); + } + } else { + ue.fill(HIST("Inclusive/hPtSecReco"), track.pt()); + } + } + + // ======================================================================== + // EXCLUSIVE PID - FIXED PRIMARY FRACTION LOGIC + // ======================================================================== + + int bestSpecies = getBestPIDHypothesis(track); + + // ======================================================================== + // PION CHANNEL + // ======================================================================== + if (bestSpecies == kPion) { + ue.fill(HIST("Pion/hPtMeasured"), track.pt()); + ue.fill(HIST("Pion/hPtAllReco"), track.pt()); + particleTracksIdentified[kPion]++; + + if (enablePIDHistograms) { + ue.fill(HIST("Pion/hNsigmaTPC"), track.pt(), track.tpcNSigmaPi()); + } + + if (track.has_mcParticle()) { + const auto& particle = track.mcParticle(); + + // KEY FIX: Primary fraction = fraction of identified pions that are primary + // This includes correctly identified pions AND misidentified kaons/protons + // that happen to be primary particles + if (particle.isPhysicalPrimary()) { + ue.fill(HIST("Pion/hPtPrimReco"), track.pt()); + particleTracksPrimary[kPion]++; + } else { + ue.fill(HIST("Pion/hPtSecReco"), track.pt()); + particleTracksSecondary[kPion]++; + } + } + } + + // ======================================================================== + // KAON CHANNEL + // ======================================================================== + else if (bestSpecies == kKaon) { + ue.fill(HIST("Kaon/hPtMeasured"), track.pt()); + ue.fill(HIST("Kaon/hPtAllReco"), track.pt()); + particleTracksIdentified[kKaon]++; + + if (enablePIDHistograms) { + ue.fill(HIST("Kaon/hNsigmaTPC"), track.pt(), track.tpcNSigmaKa()); + } + + if (track.has_mcParticle()) { + const auto& particle = track.mcParticle(); + + // KEY FIX: Primary fraction of identified kaons + // A misidentified pion that is primary still contributes to primary fraction + if (particle.isPhysicalPrimary()) { + ue.fill(HIST("Kaon/hPtPrimReco"), track.pt()); + particleTracksPrimary[kKaon]++; + } else { + ue.fill(HIST("Kaon/hPtSecReco"), track.pt()); + particleTracksSecondary[kKaon]++; + } + } + } + + // ======================================================================== + // PROTON CHANNEL + // ======================================================================== + else if (bestSpecies == kProton) { + ue.fill(HIST("Proton/hPtMeasured"), track.pt()); + ue.fill(HIST("Proton/hPtAllReco"), track.pt()); + particleTracksIdentified[kProton]++; + + if (enablePIDHistograms) { + ue.fill(HIST("Proton/hNsigmaTPC"), track.pt(), track.tpcNSigmaPr()); + } + + if (track.has_mcParticle()) { + const auto& particle = track.mcParticle(); + + // KEY FIX: Primary fraction of identified protons + if (particle.isPhysicalPrimary()) { + ue.fill(HIST("Proton/hPtPrimReco"), track.pt()); + particleTracksPrimary[kProton]++; + } else { + ue.fill(HIST("Proton/hPtSecReco"), track.pt()); + particleTracksSecondary[kProton]++; + } + } + } + } + + LOG(info) << "=== DEBUG TRACK COUNTING ==="; + LOG(info) << "Total tracks processed: " << totalTracksProcessed; + LOG(info) << "Tracks from selected events: " << tracksFromSelectedEvents; + LOG(info) << "Tracks passing selection: " << tracksPassingSelection; + LOG(info) << "Pions identified: " << particleTracksIdentified[kPion] + << ", primary: " << particleTracksPrimary[kPion] + << ", secondary: " << particleTracksSecondary[kPion]; + LOG(info) << "Kaons identified: " << particleTracksIdentified[kKaon] + << ", primary: " << particleTracksPrimary[kKaon] + << ", secondary: " << particleTracksSecondary[kKaon]; + LOG(info) << "Protons identified: " << particleTracksIdentified[kProton] + << ", primary: " << particleTracksPrimary[kProton] + << ", secondary: " << particleTracksSecondary[kProton]; + + // Calculate and log primary fractions + if (particleTracksIdentified[kPion] > 0) { + float pionPrimFrac = (float)particleTracksPrimary[kPion] / particleTracksIdentified[kPion]; + LOG(info) << "Pion primary fraction: " << pionPrimFrac * 100.0 << "%"; + } + if (particleTracksIdentified[kKaon] > 0) { + float kaonPrimFrac = (float)particleTracksPrimary[kKaon] / particleTracksIdentified[kKaon]; + LOG(info) << "Kaon primary fraction: " << kaonPrimFrac * 100.0 << "%"; + } + if (particleTracksIdentified[kProton] > 0) { + float protonPrimFrac = (float)particleTracksPrimary[kProton] / particleTracksIdentified[kProton]; + LOG(info) << "Proton primary fraction: " << protonPrimFrac * 100.0 << "%"; + } + + LOG(info) << "=== DEBUG processMC END ==="; +} + + +// ======================================================================== +// TRUE MC PROCESSING - WITH PARTICLE-SPECIFIC SIGNAL LOSS +// ======================================================================== +void multiplicitypt::processTrue(CollisionTableMCTrue const& mcCollisions, + ParticleTableMC const& particles) +{ + LOG(info) << "=== DEBUG processTrue START ==="; + LOG(info) << "Number of MC collisions: " << mcCollisions.size(); + + int nPassPhysicsSelection = 0; + int nParticlesFilledAll = 0; + int nParticlesFilledAfterPS = 0; + + std::array particleCountAll = {0}; + std::array particleCountAfterPS = {0}; + + for (const auto& mcCollision : mcCollisions) { + // Count EVERY generated event + ue.fill(HIST("hEventsAllGen"), 1.0); + + ue.fill(HIST("hvtxZmc"), mcCollision.posZ()); + auto particlesInCollision = particles.sliceBy(perMCCol, mcCollision.globalIndex()); + + // ======================================================================== + // Fill ALL generated primaries BEFORE physics selection + // ======================================================================== + for (const auto& particle : particlesInCollision) { + if (isGoodPrimary(particle)) { + ue.fill(HIST("Inclusive/hPtPrimGenAll"), particle.pt()); + nParticlesFilledAll++; + } + + if (isGoodPrimarySpecies(particle)) { + ue.fill(HIST("Pion/hPtPrimGenAll"), particle.pt()); + particleCountAll[kPion]++; + } + + if (isGoodPrimarySpecies(particle)) { + ue.fill(HIST("Kaon/hPtPrimGenAll"), particle.pt()); + particleCountAll[kKaon]++; + } + + if (isGoodPrimarySpecies(particle)) { + ue.fill(HIST("Proton/hPtPrimGenAll"), particle.pt()); + particleCountAll[kProton]++; + } + } + + // ======================================================================== + // Apply physics selection + // ======================================================================== + if (std::abs(mcCollision.posZ()) > cfgCutVertex.value) continue; + + if (cfgINELCut.value == 1 && !o2::pwglf::isINELgt0mc(particlesInCollision, pdg)) continue; + if (cfgINELCut.value == 2 && !o2::pwglf::isINELgt1mc(particlesInCollision, pdg)) continue; + + // Count physics-selected events + ue.fill(HIST("hEventsPassPhysicsSelection"), 1.0); + nPassPhysicsSelection++; + + // Fill primaries AFTER physics selection + for (const auto& particle : particlesInCollision) { + if (isGoodPrimary(particle)) { + ue.fill(HIST("Inclusive/hPtDenEff"), particle.pt()); + ue.fill(HIST("Inclusive/hPtPrimGen"), particle.pt()); + nParticlesFilledAfterPS++; + } + + if (isGoodPrimarySpecies(particle)) { + ue.fill(HIST("Pion/hPtDenEff"), particle.pt()); + ue.fill(HIST("Pion/hPtPrimGen"), particle.pt()); + particleCountAfterPS[kPion]++; + } + + if (isGoodPrimarySpecies(particle)) { + ue.fill(HIST("Kaon/hPtDenEff"), particle.pt()); + ue.fill(HIST("Kaon/hPtPrimGen"), particle.pt()); + particleCountAfterPS[kKaon]++; + } + + if (isGoodPrimarySpecies(particle)) { + ue.fill(HIST("Proton/hPtDenEff"), particle.pt()); + ue.fill(HIST("Proton/hPtPrimGen"), particle.pt()); + particleCountAfterPS[kProton]++; + } + } + } + + LOG(info) << "=== DEBUG processTrue END ==="; + LOG(info) << "All generated events: " << mcCollisions.size(); + LOG(info) << "Passing physics selection: " << nPassPhysicsSelection; + LOG(info) << "Total primaries (before PS): " << nParticlesFilledAll; + LOG(info) << "Total primaries (after PS): " << nParticlesFilledAfterPS; + + LOG(info) << "=== PARTICLE-SPECIFIC STATISTICS ==="; + LOG(info) << "Pions - All: " << particleCountAll[kPion] + << ", After PS: " << particleCountAfterPS[kPion]; + LOG(info) << "Kaons - All: " << particleCountAll[kKaon] + << ", After PS: " << particleCountAfterPS[kKaon]; + LOG(info) << "Protons - All: " << particleCountAll[kProton] + << ", After PS: " << particleCountAfterPS[kProton]; +} From aa85c935246f68365973e31159d05f28833b0806 Mon Sep 17 00:00:00 2001 From: Dushmanta Sahu Date: Tue, 20 Jan 2026 11:33:30 -0600 Subject: [PATCH 02/10] =?UTF-8?q?=E2=80=9CCOMENTARIO=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PWGLF/Tasks/Nuspex/multiplicitypt.cxx | 330 ++++++++++++++------------ 1 file changed, 178 insertions(+), 152 deletions(-) diff --git a/PWGLF/Tasks/Nuspex/multiplicitypt.cxx b/PWGLF/Tasks/Nuspex/multiplicitypt.cxx index d3d64510028..dee716b05f5 100644 --- a/PWGLF/Tasks/Nuspex/multiplicitypt.cxx +++ b/PWGLF/Tasks/Nuspex/multiplicitypt.cxx @@ -1,12 +1,25 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "PWGLF/Utils/inelGt.h" + #include "Common/Core/TrackSelection.h" #include "Common/Core/TrackSelectionDefaults.h" +#include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/Multiplicity.h" -#include "Common/DataModel/TrackSelectionTables.h" -#include "Common/DataModel/Centrality.h" #include "Common/DataModel/PIDResponse.h" -#include "Common/DataModel/PIDResponseTPC.h" #include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" #include "Framework/ASoAHelpers.h" #include "Framework/AnalysisDataModel.h" @@ -16,16 +29,15 @@ #include "Framework/StaticFor.h" #include "Framework/runDataProcessing.h" #include "ReconstructionDataFormats/Track.h" -#include "PWGLF/Utils/inelGt.h" #include "TDatabasePDG.h" #include #include #include #include -#include #include +#include #include using namespace o2; @@ -39,22 +51,15 @@ struct multiplicitypt { // Service Service pdg; - // Configurables - Matching spectraTOF approach Configurable isRun3{"isRun3", true, "is Run3 dataset"}; - - // Event selection configurables (spectraTOF style) Configurable cfgCutVertex{"cfgCutVertex", 10.0f, "Accepted z-vertex range"}; Configurable cfgINELCut{"cfgINELCut", 0, "INEL event selection: 0 no sel, 1 INEL>0, 2 INEL>1"}; Configurable askForCustomTVX{"askForCustomTVX", false, "Ask for custom TVX rather than sel8"}; - - // SET ALL THESE TO FALSE LIKE SPECTRATOF: Configurable removeITSROFrameBorder{"removeITSROFrameBorder", false, "Remove ITS Read-Out Frame border"}; Configurable removeNoSameBunchPileup{"removeNoSameBunchPileup", false, "Remove no same bunch pileup"}; Configurable requireIsGoodZvtxFT0vsPV{"requireIsGoodZvtxFT0vsPV", false, "Require good Z vertex FT0 vs PV"}; Configurable requireIsVertexITSTPC{"requireIsVertexITSTPC", false, "Require vertex ITSTPC"}; Configurable removeNoTimeFrameBorder{"removeNoTimeFrameBorder", false, "Remove no time frame border"}; - - // Track selection configurables (spectraTOF style) Configurable cfgCutEtaMax{"cfgCutEtaMax", 0.8f, "Max eta range for tracks"}; Configurable cfgCutEtaMin{"cfgCutEtaMin", -0.8f, "Min eta range for tracks"}; Configurable cfgCutY{"cfgCutY", 0.5f, "Y range for tracks"}; @@ -62,16 +67,12 @@ struct multiplicitypt { Configurable lastRequiredTrdCluster{"lastRequiredTrdCluster", -1, "Last cluster to require in TRD"}; Configurable requireTrdOnly{"requireTrdOnly", false, "Require only tracks from TRD"}; Configurable requireNoTrd{"requireNoTrd", false, "Require tracks without TRD"}; - - // Multiplicity estimator (like spectraTOF) Configurable multiplicityEstimator{"multiplicityEstimator", 6, - "Multiplicity estimator: 0=NoMult, 1=MultFV0M, 2=MultFT0M, 3=MultFDDM, 4=MultTracklets, 5=MultTPC, 6=MultNTracksPV, 7=MultNTracksPVeta1, 8=CentFT0C, 9=CentFT0M, 10=CentFV0A"}; + "Multiplicity estimator: 0=NoMult, 1=MultFV0M, 2=MultFT0M, 3=MultFDDM, 4=MultTracklets, 5=MultTPC, 6=MultNTracksPV, 7=MultNTracksPVeta1, 8=CentFT0C, 9=CentFT0M, 10=CentFV0A"}; // Analysis switches Configurable enableDCAHistograms{"enableDCAHistograms", false, "Enable DCA histograms"}; Configurable enablePIDHistograms{"enablePIDHistograms", true, "Enable PID histograms"}; - - // Track cut configurables (spectraTOF style) Configurable useCustomTrackCuts{"useCustomTrackCuts", true, "Flag to use custom track cuts"}; Configurable itsPattern{"itsPattern", 0, "0 = Run3ITSibAny, 1 = Run3ITSallAny, 2 = Run3ITSall7Layers, 3 = Run3ITSibTwo"}; Configurable requireITS{"requireITS", true, "Additional cut on the ITS requirement"}; @@ -145,7 +146,7 @@ struct multiplicitypt { // ======================================================================== // PROCESS FUNCTION DECLARATIONS - SPECTRATOF STYLE // ======================================================================== - + // Data processing void processData(CollisionTableData::iterator const& collision, TrackTableData const& tracks); @@ -166,7 +167,7 @@ struct multiplicitypt { // ======================================================================== // TRACK SELECTION FUNCTIONS - MATCHING spectraTOF // ======================================================================== - + template bool passesCutWoDCA(TrackType const& track) const { @@ -219,12 +220,12 @@ struct multiplicitypt { // ======================================================================== // PID SELECTION FUNCTIONS - TPC ONLY (OLD NON-EXCLUSIVE METHOD) // ======================================================================== - + template bool passesPIDSelection(TrackType const& track) const { float nsigmaTPC = 0.f; - + if constexpr (species == kPion) { nsigmaTPC = track.tpcNSigmaPi(); } else if constexpr (species == kKaon) { @@ -232,7 +233,7 @@ struct multiplicitypt { } else if constexpr (species == kProton) { nsigmaTPC = track.tpcNSigmaPr(); } - + // TPC-only PID (works for all pT, but better at low pT < 1 GeV/c) return (std::abs(nsigmaTPC) < cfgCutNsigma.value); } @@ -240,20 +241,20 @@ struct multiplicitypt { // ======================================================================== // EXCLUSIVE PID SELECTION - Returns best hypothesis for a track // ======================================================================== - + template int getBestPIDHypothesis(TrackType const& track) const { // Return values: -1 = no ID, 0 = pion, 1 = kaon, 2 = proton - + float nsigmaPi = std::abs(track.tpcNSigmaPi()); float nsigmaKa = std::abs(track.tpcNSigmaKa()); float nsigmaPr = std::abs(track.tpcNSigmaPr()); - + // Find the hypothesis with smallest |nσ| that passes the cut float minNSigma = 999.0f; int bestSpecies = -1; - + if (nsigmaPi < cfgCutNsigma.value && nsigmaPi < minNSigma) { minNSigma = nsigmaPi; bestSpecies = kPion; @@ -266,56 +267,76 @@ struct multiplicitypt { minNSigma = nsigmaPr; bestSpecies = kProton; } - + return bestSpecies; } // ======================================================================== // EVENT SELECTION FUNCTION - EXACT spectraTOF // ======================================================================== - + template bool isEventSelected(CollisionType const& collision) { if constexpr (fillHistograms) { ue.fill(HIST("evsel"), 1.f); - if (collision.isInelGt0()) ue.fill(HIST("evsel"), 2.f); - if (collision.isInelGt1()) ue.fill(HIST("evsel"), 3.f); + if (collision.isInelGt0()) + ue.fill(HIST("evsel"), 2.f); + if (collision.isInelGt1()) + ue.fill(HIST("evsel"), 3.f); } if (askForCustomTVX.value) { - if (!collision.selection_bit(aod::evsel::kIsTriggerTVX)) return false; + if (!collision.selection_bit(aod::evsel::kIsTriggerTVX)) + return false; } else { - if (!collision.sel8()) return false; + if (!collision.sel8()) + return false; } - if constexpr (fillHistograms) ue.fill(HIST("evsel"), 4.f); + if constexpr (fillHistograms) + ue.fill(HIST("evsel"), 4.f); - if (removeITSROFrameBorder.value && !collision.selection_bit(aod::evsel::kNoITSROFrameBorder)) return false; - if constexpr (fillHistograms) ue.fill(HIST("evsel"), 5.f); + if (removeITSROFrameBorder.value && !collision.selection_bit(aod::evsel::kNoITSROFrameBorder)) + return false; + if constexpr (fillHistograms) + ue.fill(HIST("evsel"), 5.f); - if (removeNoSameBunchPileup.value && !collision.selection_bit(aod::evsel::kNoSameBunchPileup)) return false; - if constexpr (fillHistograms) ue.fill(HIST("evsel"), 6.f); + if (removeNoSameBunchPileup.value && !collision.selection_bit(aod::evsel::kNoSameBunchPileup)) + return false; + if constexpr (fillHistograms) + ue.fill(HIST("evsel"), 6.f); - if (requireIsGoodZvtxFT0vsPV.value && !collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV)) return false; - if constexpr (fillHistograms) ue.fill(HIST("evsel"), 7.f); + if (requireIsGoodZvtxFT0vsPV.value && !collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV)) + return false; + if constexpr (fillHistograms) + ue.fill(HIST("evsel"), 7.f); - if (requireIsVertexITSTPC.value && !collision.selection_bit(aod::evsel::kIsVertexITSTPC)) return false; - if constexpr (fillHistograms) ue.fill(HIST("evsel"), 8.f); + if (requireIsVertexITSTPC.value && !collision.selection_bit(aod::evsel::kIsVertexITSTPC)) + return false; + if constexpr (fillHistograms) + ue.fill(HIST("evsel"), 8.f); - if (removeNoTimeFrameBorder.value && !collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) return false; - if constexpr (fillHistograms) ue.fill(HIST("evsel"), 9.f); + if (removeNoTimeFrameBorder.value && !collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) + return false; + if constexpr (fillHistograms) + ue.fill(HIST("evsel"), 9.f); - if (std::abs(collision.posZ()) > cfgCutVertex.value) return false; + if (std::abs(collision.posZ()) > cfgCutVertex.value) + return false; if constexpr (fillHistograms) { ue.fill(HIST("evsel"), 13.f); - if (collision.isInelGt0()) ue.fill(HIST("evsel"), 14.f); - if (collision.isInelGt1()) ue.fill(HIST("evsel"), 15.f); + if (collision.isInelGt0()) + ue.fill(HIST("evsel"), 14.f); + if (collision.isInelGt1()) + ue.fill(HIST("evsel"), 15.f); } - if (cfgINELCut.value == 1 && !collision.isInelGt0()) return false; - if (cfgINELCut.value == 2 && !collision.isInelGt1()) return false; + if (cfgINELCut.value == 1 && !collision.isInelGt0()) + return false; + if (cfgINELCut.value == 2 && !collision.isInelGt1()) + return false; return true; } @@ -329,19 +350,19 @@ struct multiplicitypt { { auto pdgParticle = pdg->GetParticle(particle.pdgCode()); if (!pdgParticle || pdgParticle->Charge() == 0.) - return false; - + return false; + if (!particle.isPhysicalPrimary()) - return false; - + return false; + if (std::abs(particle.eta()) >= cfgCutEtaMax.value) - return false; + return false; if (particle.pt() < cfgTrkLowPtCut.value) - return false; - + return false; + if (std::abs(particle.y()) > cfgCutY.value) - return false; - + return false; + return true; } @@ -351,13 +372,17 @@ struct multiplicitypt { { int pdgCode = std::abs(particle.pdgCode()); int expectedPDG = 0; - - if constexpr (species == kPion) expectedPDG = PDGPion; - else if constexpr (species == kKaon) expectedPDG = PDGKaon; - else if constexpr (species == kProton) expectedPDG = PDGProton; - - if (pdgCode != expectedPDG) return false; - + + if constexpr (species == kPion) + expectedPDG = PDGPion; + else if constexpr (species == kKaon) + expectedPDG = PDGKaon; + else if constexpr (species == kProton) + expectedPDG = PDGProton; + + if (pdgCode != expectedPDG) + return false; + return isGoodPrimary(particle); } @@ -374,11 +399,11 @@ void multiplicitypt::init(InitContext const&) // ======================================================================== // CUSTOM TRACK CUTS INITIALIZATION - MATCHING spectraTOF // ======================================================================== - + if (useCustomTrackCuts.value) { LOG(info) << "Using custom track cuts matching spectraTOF approach"; customTrackCuts = getGlobalTrackSelectionRun3ITSMatch(itsPattern.value); - + customTrackCuts.SetRequireITSRefit(requireITS.value); customTrackCuts.SetRequireTPCRefit(requireTPC.value); customTrackCuts.SetMinNClustersITS(min_ITS_nClusters.value); @@ -390,14 +415,14 @@ void multiplicitypt::init(InitContext const&) customTrackCuts.SetMinNCrossedRowsOverFindableClustersTPC(minNCrossedRowsOverFindableClustersTPC.value); customTrackCuts.SetMaxDcaXYPtDep([](float /*pt*/) { return 10000.f; }); customTrackCuts.SetMaxDcaZ(maxDcaZ.value); - + customTrackCuts.print(); } // ======================================================================== // AXIS DEFINITIONS // ======================================================================== - + ConfigurableAxis ptBinning{ "ptBinning", {0.0, 0.1, 0.15, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, @@ -405,14 +430,13 @@ void multiplicitypt::init(InitContext const&) 1.5, 1.6, 1.7, 1.8, 1.9, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.5, 4.0, 4.5, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 12.0, 14.0, 16.0, 18.0, 20.0, 25.0, 30.0, 40.0, 50.0}, - "pT bin limits" - }; + "pT bin limits"}; AxisSpec ptAxis = {ptBinning, "#it{p}_{T} (GeV/#it{c})"}; // ======================================================================== // HISTOGRAM REGISTRY - INCLUSIVE + PARTICLE-SPECIFIC // ======================================================================== - + // Event counting - EXACT spectraTOF approach ue.add("MC/GenRecoCollisions", "Generated and Reconstructed MC Collisions", HistType::kTH1D, {{10, 0.5, 10.5}}); auto hColl = ue.get(HIST("MC/GenRecoCollisions")); @@ -436,11 +460,11 @@ void multiplicitypt::init(InitContext const&) // ======================================================================== // INCLUSIVE CHARGED PARTICLE HISTOGRAMS // ======================================================================== - + // ALL generated primaries (before any physics selection) ue.add("Inclusive/hPtPrimGenAll", "All generated primaries (no cuts);#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {ptAxis}); - + // Generated primaries AFTER physics selection ue.add("Inclusive/hPtPrimGen", "Generated primaries (after physics selection);#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {ptAxis}); @@ -466,50 +490,50 @@ void multiplicitypt::init(InitContext const&) // ======================================================================== // PARTICLE-SPECIFIC HISTOGRAMS (Pions, Kaons, Protons) // ======================================================================== - + const std::array particleNames = {"Pion", "Kaon", "Proton"}; const std::array particleSymbols = {"#pi^{#pm}", "K^{#pm}", "p+#bar{p}"}; - + for (int iSpecies = 0; iSpecies < kNSpecies; ++iSpecies) { const auto& name = particleNames[iSpecies]; const auto& symbol = particleSymbols[iSpecies]; - + // Generated histograms ue.add(Form("%s/hPtPrimGenAll", name.c_str()), Form("All generated %s (no cuts);#it{p}_{T} (GeV/#it{c});Counts", symbol.c_str()), HistType::kTH1D, {ptAxis}); - + ue.add(Form("%s/hPtPrimGen", name.c_str()), Form("Generated %s (after physics selection);#it{p}_{T} (GeV/#it{c});Counts", symbol.c_str()), HistType::kTH1D, {ptAxis}); - + // Tracking efficiency ue.add(Form("%s/hPtNumEff", name.c_str()), Form("%s tracking efficiency numerator;#it{p}_{T} (GeV/#it{c});Counts", symbol.c_str()), HistType::kTH1D, {ptAxis}); - + ue.add(Form("%s/hPtDenEff", name.c_str()), Form("%s tracking efficiency denominator;#it{p}_{T} (GeV/#it{c});Counts", symbol.c_str()), HistType::kTH1D, {ptAxis}); - + // Reconstructed histograms ue.add(Form("%s/hPtAllReco", name.c_str()), Form("All reconstructed %s;#it{p}_{T} (GeV/#it{c});Counts", symbol.c_str()), HistType::kTH1D, {ptAxis}); - + ue.add(Form("%s/hPtPrimReco", name.c_str()), Form("Reconstructed primary %s;#it{p}_{T} (GeV/#it{c});Counts", symbol.c_str()), HistType::kTH1D, {ptAxis}); - + ue.add(Form("%s/hPtSecReco", name.c_str()), Form("Reconstructed secondary %s;#it{p}_{T} (GeV/#it{c});Counts", symbol.c_str()), HistType::kTH1D, {ptAxis}); - + // Measured spectra ue.add(Form("%s/hPtMeasured", name.c_str()), Form("Measured %s;#it{p}_{T} (GeV/#it{c});Counts", symbol.c_str()), HistType::kTH1D, {ptAxis}); - + // PID quality histograms - TPC ONLY if (enablePIDHistograms) { ue.add(Form("%s/hNsigmaTPC", name.c_str()), @@ -517,7 +541,7 @@ void multiplicitypt::init(InitContext const&) HistType::kTH2D, {ptAxis, {200, -10, 10}}); } } - + // ======================================================================== // MONITORING HISTOGRAMS // ======================================================================== @@ -535,12 +559,12 @@ void multiplicitypt::init(InitContext const&) h->GetXaxis()->SetBinLabel(13, "posZ passed"); h->GetXaxis()->SetBinLabel(14, "INEL>0 (final)"); h->GetXaxis()->SetBinLabel(15, "INEL>1 (final)"); - + ue.add("hEta", "Track eta;#eta;Counts", HistType::kTH1D, {{20, -0.8, 0.8}}); ue.add("hPhi", "Track phi;#varphi (rad);Counts", HistType::kTH1D, {{64, 0, 2.0 * o2::constants::math::PI}}); ue.add("hvtxZ", "Vertex Z (data);Vertex Z (cm);Events", HistType::kTH1F, {{40, -20.0, 20.0}}); ue.add("hvtxZmc", "MC vertex Z;Vertex Z (cm);Events", HistType::kTH1F, {{40, -20.0, 20.0}}); - + LOG(info) << "Initialized multiplicitypt task with EXCLUSIVE PID for INCLUSIVE + PARTICLE-SPECIFIC (Pi, K, p) analysis"; } @@ -553,12 +577,12 @@ void multiplicitypt::processData(CollisionTableData::iterator const& collision, return; } ue.fill(HIST("hvtxZ"), collision.posZ()); - + for (const auto& track : tracks) { if (!passesTrackSelection(track)) { continue; } - + // Inclusive charged particle (always filled) ue.fill(HIST("Inclusive/hPtMeasured"), track.pt()); ue.fill(HIST("hEta"), track.eta()); @@ -566,20 +590,18 @@ void multiplicitypt::processData(CollisionTableData::iterator const& collision, // Exclusive particle identification int bestSpecies = getBestPIDHypothesis(track); - + if (bestSpecies == kPion) { ue.fill(HIST("Pion/hPtMeasured"), track.pt()); if (enablePIDHistograms) { ue.fill(HIST("Pion/hNsigmaTPC"), track.pt(), track.tpcNSigmaPi()); } - } - else if (bestSpecies == kKaon) { + } else if (bestSpecies == kKaon) { ue.fill(HIST("Kaon/hPtMeasured"), track.pt()); if (enablePIDHistograms) { ue.fill(HIST("Kaon/hNsigmaTPC"), track.pt(), track.tpcNSigmaKa()); } - } - else if (bestSpecies == kProton) { + } else if (bestSpecies == kProton) { ue.fill(HIST("Proton/hPtMeasured"), track.pt()); if (enablePIDHistograms) { ue.fill(HIST("Proton/hNsigmaTPC"), track.pt(), track.tpcNSigmaPr()); @@ -592,14 +614,14 @@ void multiplicitypt::processData(CollisionTableData::iterator const& collision, // MC PROCESSING - WITH FIXED PRIMARY FRACTION CALCULATION // ======================================================================== void multiplicitypt::processMC(TrackTableMC const& tracks, - aod::McParticles const& particles, - CollisionTableMCTrue const& mcCollisions, - CollisionTableMC const& collisions) + aod::McParticles const& particles, + CollisionTableMCTrue const& mcCollisions, + CollisionTableMC const& collisions) { LOG(info) << "=== DEBUG processMC START ==="; LOG(info) << "MC collisions: " << mcCollisions.size(); LOG(info) << "Reconstructed collisions: " << collisions.size(); - + // ======================================================================== // STEP 1: Identify which MC collisions are reconstructable // ======================================================================== @@ -608,7 +630,7 @@ void multiplicitypt::processMC(TrackTableMC const& tracks, for (const auto& mcCollision : mcCollisions) { auto particlesInCollision = particles.sliceBy(perMCCol, mcCollision.globalIndex()); - + if (std::abs(mcCollision.posZ()) > cfgCutVertex.value) { continue; } @@ -618,7 +640,7 @@ void multiplicitypt::processMC(TrackTableMC const& tracks, if (cfgINELCut.value == 2 && !o2::pwglf::isINELgt1mc(particlesInCollision, pdg)) { continue; } - + reconstructableMCCollisions.insert(mcCollision.globalIndex()); } @@ -636,16 +658,16 @@ void multiplicitypt::processMC(TrackTableMC const& tracks, if (!collision.has_mcCollision()) { continue; } - + const auto& mcCollision = collision.mcCollision_as(); int64_t mcCollId = mcCollision.globalIndex(); - + if (reconstructableMCCollisions.find(mcCollId) == reconstructableMCCollisions.end()) { continue; } - + reconstructedMCCollisions.insert(mcCollId); - + if (isEventSelected(collision)) { selectedMCCollisions.insert(mcCollId); selectedCollisionIndices.insert(collision.globalIndex()); @@ -687,40 +709,42 @@ void multiplicitypt::processMC(TrackTableMC const& tracks, for (const auto& track : tracks) { totalTracksProcessed++; - - if (!track.has_collision()) continue; - + + if (!track.has_collision()) + continue; + const auto& collision = track.collision_as(); - + if (selectedCollisionIndices.find(collision.globalIndex()) == selectedCollisionIndices.end()) { continue; } tracksFromSelectedEvents++; - - if (!passesTrackSelection(track)) continue; + + if (!passesTrackSelection(track)) + continue; tracksPassingSelection++; - + // ======================================================================== // INCLUSIVE CHARGED PARTICLE ANALYSIS // ======================================================================== - + ue.fill(HIST("Inclusive/hPtMeasured"), track.pt()); ue.fill(HIST("Inclusive/hPtAllReco"), track.pt()); ue.fill(HIST("hEta"), track.eta()); ue.fill(HIST("hPhi"), track.phi()); - + // ======================================================================== // EFFICIENCY NUMERATOR: Fill based on TRUE particle type // ======================================================================== - + if (track.has_mcParticle()) { const auto& particle = track.mcParticle(); int pdgCode = std::abs(particle.pdgCode()); - + if (particle.isPhysicalPrimary()) { ue.fill(HIST("Inclusive/hPtNumEff"), particle.pt()); ue.fill(HIST("Inclusive/hPtPrimReco"), track.pt()); - + // Fill particle-specific efficiency numerator based on TRUE type if (pdgCode == PDGPion) { ue.fill(HIST("Pion/hPtNumEff"), particle.pt()); @@ -735,13 +759,13 @@ void multiplicitypt::processMC(TrackTableMC const& tracks, ue.fill(HIST("Inclusive/hPtSecReco"), track.pt()); } } - + // ======================================================================== // EXCLUSIVE PID - FIXED PRIMARY FRACTION LOGIC // ======================================================================== - + int bestSpecies = getBestPIDHypothesis(track); - + // ======================================================================== // PION CHANNEL // ======================================================================== @@ -749,14 +773,14 @@ void multiplicitypt::processMC(TrackTableMC const& tracks, ue.fill(HIST("Pion/hPtMeasured"), track.pt()); ue.fill(HIST("Pion/hPtAllReco"), track.pt()); particleTracksIdentified[kPion]++; - + if (enablePIDHistograms) { ue.fill(HIST("Pion/hNsigmaTPC"), track.pt(), track.tpcNSigmaPi()); } - + if (track.has_mcParticle()) { const auto& particle = track.mcParticle(); - + // KEY FIX: Primary fraction = fraction of identified pions that are primary // This includes correctly identified pions AND misidentified kaons/protons // that happen to be primary particles @@ -769,7 +793,7 @@ void multiplicitypt::processMC(TrackTableMC const& tracks, } } } - + // ======================================================================== // KAON CHANNEL // ======================================================================== @@ -777,14 +801,14 @@ void multiplicitypt::processMC(TrackTableMC const& tracks, ue.fill(HIST("Kaon/hPtMeasured"), track.pt()); ue.fill(HIST("Kaon/hPtAllReco"), track.pt()); particleTracksIdentified[kKaon]++; - + if (enablePIDHistograms) { ue.fill(HIST("Kaon/hNsigmaTPC"), track.pt(), track.tpcNSigmaKa()); } - + if (track.has_mcParticle()) { const auto& particle = track.mcParticle(); - + // KEY FIX: Primary fraction of identified kaons // A misidentified pion that is primary still contributes to primary fraction if (particle.isPhysicalPrimary()) { @@ -796,7 +820,7 @@ void multiplicitypt::processMC(TrackTableMC const& tracks, } } } - + // ======================================================================== // PROTON CHANNEL // ======================================================================== @@ -804,14 +828,14 @@ void multiplicitypt::processMC(TrackTableMC const& tracks, ue.fill(HIST("Proton/hPtMeasured"), track.pt()); ue.fill(HIST("Proton/hPtAllReco"), track.pt()); particleTracksIdentified[kProton]++; - + if (enablePIDHistograms) { ue.fill(HIST("Proton/hNsigmaTPC"), track.pt(), track.tpcNSigmaPr()); } - + if (track.has_mcParticle()) { const auto& particle = track.mcParticle(); - + // KEY FIX: Primary fraction of identified protons if (particle.isPhysicalPrimary()) { ue.fill(HIST("Proton/hPtPrimReco"), track.pt()); @@ -823,7 +847,7 @@ void multiplicitypt::processMC(TrackTableMC const& tracks, } } } - + LOG(info) << "=== DEBUG TRACK COUNTING ==="; LOG(info) << "Total tracks processed: " << totalTracksProcessed; LOG(info) << "Tracks from selected events: " << tracksFromSelectedEvents; @@ -837,7 +861,7 @@ void multiplicitypt::processMC(TrackTableMC const& tracks, LOG(info) << "Protons identified: " << particleTracksIdentified[kProton] << ", primary: " << particleTracksPrimary[kProton] << ", secondary: " << particleTracksSecondary[kProton]; - + // Calculate and log primary fractions if (particleTracksIdentified[kPion] > 0) { float pionPrimFrac = (float)particleTracksPrimary[kPion] / particleTracksIdentified[kPion]; @@ -851,20 +875,19 @@ void multiplicitypt::processMC(TrackTableMC const& tracks, float protonPrimFrac = (float)particleTracksPrimary[kProton] / particleTracksIdentified[kProton]; LOG(info) << "Proton primary fraction: " << protonPrimFrac * 100.0 << "%"; } - + LOG(info) << "=== DEBUG processMC END ==="; } - // ======================================================================== // TRUE MC PROCESSING - WITH PARTICLE-SPECIFIC SIGNAL LOSS // ======================================================================== void multiplicitypt::processTrue(CollisionTableMCTrue const& mcCollisions, - ParticleTableMC const& particles) + ParticleTableMC const& particles) { LOG(info) << "=== DEBUG processTrue START ==="; LOG(info) << "Number of MC collisions: " << mcCollisions.size(); - + int nPassPhysicsSelection = 0; int nParticlesFilledAll = 0; int nParticlesFilledAfterPS = 0; @@ -875,10 +898,10 @@ void multiplicitypt::processTrue(CollisionTableMCTrue const& mcCollisions, for (const auto& mcCollision : mcCollisions) { // Count EVERY generated event ue.fill(HIST("hEventsAllGen"), 1.0); - + ue.fill(HIST("hvtxZmc"), mcCollision.posZ()); auto particlesInCollision = particles.sliceBy(perMCCol, mcCollision.globalIndex()); - + // ======================================================================== // Fill ALL generated primaries BEFORE physics selection // ======================================================================== @@ -887,35 +910,38 @@ void multiplicitypt::processTrue(CollisionTableMCTrue const& mcCollisions, ue.fill(HIST("Inclusive/hPtPrimGenAll"), particle.pt()); nParticlesFilledAll++; } - + if (isGoodPrimarySpecies(particle)) { ue.fill(HIST("Pion/hPtPrimGenAll"), particle.pt()); particleCountAll[kPion]++; } - + if (isGoodPrimarySpecies(particle)) { ue.fill(HIST("Kaon/hPtPrimGenAll"), particle.pt()); particleCountAll[kKaon]++; } - + if (isGoodPrimarySpecies(particle)) { ue.fill(HIST("Proton/hPtPrimGenAll"), particle.pt()); particleCountAll[kProton]++; } } - + // ======================================================================== // Apply physics selection // ======================================================================== - if (std::abs(mcCollision.posZ()) > cfgCutVertex.value) continue; - - if (cfgINELCut.value == 1 && !o2::pwglf::isINELgt0mc(particlesInCollision, pdg)) continue; - if (cfgINELCut.value == 2 && !o2::pwglf::isINELgt1mc(particlesInCollision, pdg)) continue; - + if (std::abs(mcCollision.posZ()) > cfgCutVertex.value) + continue; + + if (cfgINELCut.value == 1 && !o2::pwglf::isINELgt0mc(particlesInCollision, pdg)) + continue; + if (cfgINELCut.value == 2 && !o2::pwglf::isINELgt1mc(particlesInCollision, pdg)) + continue; + // Count physics-selected events ue.fill(HIST("hEventsPassPhysicsSelection"), 1.0); nPassPhysicsSelection++; - + // Fill primaries AFTER physics selection for (const auto& particle : particlesInCollision) { if (isGoodPrimary(particle)) { @@ -923,19 +949,19 @@ void multiplicitypt::processTrue(CollisionTableMCTrue const& mcCollisions, ue.fill(HIST("Inclusive/hPtPrimGen"), particle.pt()); nParticlesFilledAfterPS++; } - + if (isGoodPrimarySpecies(particle)) { ue.fill(HIST("Pion/hPtDenEff"), particle.pt()); ue.fill(HIST("Pion/hPtPrimGen"), particle.pt()); particleCountAfterPS[kPion]++; } - + if (isGoodPrimarySpecies(particle)) { ue.fill(HIST("Kaon/hPtDenEff"), particle.pt()); ue.fill(HIST("Kaon/hPtPrimGen"), particle.pt()); particleCountAfterPS[kKaon]++; } - + if (isGoodPrimarySpecies(particle)) { ue.fill(HIST("Proton/hPtDenEff"), particle.pt()); ue.fill(HIST("Proton/hPtPrimGen"), particle.pt()); @@ -949,7 +975,7 @@ void multiplicitypt::processTrue(CollisionTableMCTrue const& mcCollisions, LOG(info) << "Passing physics selection: " << nPassPhysicsSelection; LOG(info) << "Total primaries (before PS): " << nParticlesFilledAll; LOG(info) << "Total primaries (after PS): " << nParticlesFilledAfterPS; - + LOG(info) << "=== PARTICLE-SPECIFIC STATISTICS ==="; LOG(info) << "Pions - All: " << particleCountAll[kPion] << ", After PS: " << particleCountAfterPS[kPion]; From 5d3db7203c3cfe6e7def6398f1415db2014180b0 Mon Sep 17 00:00:00 2001 From: Dushmanta Sahu Date: Tue, 20 Jan 2026 12:19:09 -0600 Subject: [PATCH 03/10] Fix linter errors --- PWGLF/Tasks/Nuspex/multiplicitypt.cxx | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/PWGLF/Tasks/Nuspex/multiplicitypt.cxx b/PWGLF/Tasks/Nuspex/multiplicitypt.cxx index dee716b05f5..a7d1c6152db 100644 --- a/PWGLF/Tasks/Nuspex/multiplicitypt.cxx +++ b/PWGLF/Tasks/Nuspex/multiplicitypt.cxx @@ -25,10 +25,10 @@ #include "Framework/AnalysisDataModel.h" #include "Framework/AnalysisTask.h" #include "Framework/HistogramRegistry.h" +#include "Framework/Logger.h" #include "ReconstructionDataFormats/Track.h" #include "Framework/O2DatabasePDGPlugin.h" #include "Framework/StaticFor.h" #include "Framework/runDataProcessing.h" -#include "ReconstructionDataFormats/Track.h" #include "TDatabasePDG.h" #include @@ -37,16 +37,16 @@ #include #include -#include #include using namespace o2; using namespace o2::framework; using namespace o2::framework::expressions; +using namespace o2::constants::physics; using BCsRun3 = soa::Join; -struct multiplicitypt { +struct MultiplicityPt { // Service Service pdg; @@ -139,9 +139,9 @@ struct multiplicitypt { }; // PDG codes - static constexpr int PDGPion = 211; - static constexpr int PDGKaon = 321; - static constexpr int PDGProton = 2212; + static constexpr int PDGPion = Pdg::kPiPlus; + static constexpr int PDGKaon = Pdg::kKPlus; + static constexpr int PDGProton = Pdg::kProton; // ======================================================================== // PROCESS FUNCTION DECLARATIONS - SPECTRATOF STYLE @@ -391,10 +391,10 @@ struct multiplicitypt { WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { - return WorkflowSpec{adaptAnalysisTask(cfgc)}; + return WorkflowSpec{adaptAnalysisTask(cfgc)}; } -void multiplicitypt::init(InitContext const&) +void MultiplicityPt::init(InitContext const&) { // ======================================================================== // CUSTOM TRACK CUTS INITIALIZATION - MATCHING spectraTOF @@ -571,7 +571,7 @@ void multiplicitypt::init(InitContext const&) // ======================================================================== // DATA PROCESSING - WITH EXCLUSIVE PID // ======================================================================== -void multiplicitypt::processData(CollisionTableData::iterator const& collision, TrackTableData const& tracks) +void MultiplicityPt::processData(CollisionTableData::iterator const& collision, TrackTableData const& tracks) { if (!isEventSelected(collision)) { return; @@ -613,7 +613,7 @@ void multiplicitypt::processData(CollisionTableData::iterator const& collision, // ======================================================================== // MC PROCESSING - WITH FIXED PRIMARY FRACTION CALCULATION // ======================================================================== -void multiplicitypt::processMC(TrackTableMC const& tracks, +void MultiplicityPt::processMC(TrackTableMC const& tracks, aod::McParticles const& particles, CollisionTableMCTrue const& mcCollisions, CollisionTableMC const& collisions) @@ -882,7 +882,7 @@ void multiplicitypt::processMC(TrackTableMC const& tracks, // ======================================================================== // TRUE MC PROCESSING - WITH PARTICLE-SPECIFIC SIGNAL LOSS // ======================================================================== -void multiplicitypt::processTrue(CollisionTableMCTrue const& mcCollisions, +void MultiplicityPt::processTrue(CollisionTableMCTrue const& mcCollisions, ParticleTableMC const& particles) { LOG(info) << "=== DEBUG processTrue START ==="; From 9ab8dbbad58d3f8d34f42ae5fd0c4c87ae068f79 Mon Sep 17 00:00:00 2001 From: Dushmanta Sahu Date: Tue, 20 Jan 2026 12:29:37 -0600 Subject: [PATCH 04/10] Fix linter errors: rename multiplicitypt.cxx to MultiplicityPt.cxx to match struct name --- PWGLF/Tasks/Nuspex/CMakeLists.txt | 2 +- PWGLF/Tasks/Nuspex/{multiplicitypt.cxx => MultiplicityPt.cxx} | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) rename PWGLF/Tasks/Nuspex/{multiplicitypt.cxx => MultiplicityPt.cxx} (99%) diff --git a/PWGLF/Tasks/Nuspex/CMakeLists.txt b/PWGLF/Tasks/Nuspex/CMakeLists.txt index a4ba69bfb88..8c0cbec54d0 100644 --- a/PWGLF/Tasks/Nuspex/CMakeLists.txt +++ b/PWGLF/Tasks/Nuspex/CMakeLists.txt @@ -181,7 +181,7 @@ o2physics_add_dpl_workflow(chargedparticle-raa COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(multiplicity-pt - SOURCES multiplicitypt.cxx + SOURCES MultiplicityPt.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) diff --git a/PWGLF/Tasks/Nuspex/multiplicitypt.cxx b/PWGLF/Tasks/Nuspex/MultiplicityPt.cxx similarity index 99% rename from PWGLF/Tasks/Nuspex/multiplicitypt.cxx rename to PWGLF/Tasks/Nuspex/MultiplicityPt.cxx index a7d1c6152db..6aba4b35477 100644 --- a/PWGLF/Tasks/Nuspex/multiplicitypt.cxx +++ b/PWGLF/Tasks/Nuspex/MultiplicityPt.cxx @@ -30,7 +30,6 @@ #include "Framework/StaticFor.h" #include "Framework/runDataProcessing.h" -#include "TDatabasePDG.h" #include #include #include From 5fee20c37f5bc905e6d79f0c7298b50b5c9aa442 Mon Sep 17 00:00:00 2001 From: Dushmanta Sahu Date: Tue, 20 Jan 2026 12:39:59 -0600 Subject: [PATCH 05/10] Fix linter errors --- PWGLF/Tasks/Nuspex/MultiplicityPt.cxx | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/PWGLF/Tasks/Nuspex/MultiplicityPt.cxx b/PWGLF/Tasks/Nuspex/MultiplicityPt.cxx index 6aba4b35477..b9a676b87d8 100644 --- a/PWGLF/Tasks/Nuspex/MultiplicityPt.cxx +++ b/PWGLF/Tasks/Nuspex/MultiplicityPt.cxx @@ -25,10 +25,11 @@ #include "Framework/AnalysisDataModel.h" #include "Framework/AnalysisTask.h" #include "Framework/HistogramRegistry.h" -#include "Framework/Logger.h" #include "ReconstructionDataFormats/Track.h" +#include "Framework/Logger.h" #include "Framework/O2DatabasePDGPlugin.h" #include "Framework/StaticFor.h" #include "Framework/runDataProcessing.h" +#include "ReconstructionDataFormats/Track.h" #include #include @@ -149,19 +150,19 @@ struct MultiplicityPt { // Data processing void processData(CollisionTableData::iterator const& collision, TrackTableData const& tracks); - PROCESS_SWITCH(multiplicitypt, processData, "process data", false); + PROCESS_SWITCH(MultiplicityPt, processData, "process data", false); // MC processing - EXACT spectraTOF approach void processMC(TrackTableMC const& tracks, aod::McParticles const& particles, CollisionTableMCTrue const& mcCollisions, CollisionTableMC const& collisions); - PROCESS_SWITCH(multiplicitypt, processMC, "process MC", true); + PROCESS_SWITCH(MultiplicityPt, processMC, "process MC", true); // True MC processing - EXACT spectraTOF approach void processTrue(CollisionTableMCTrue const& mcCollisions, ParticleTableMC const& particles); - PROCESS_SWITCH(multiplicitypt, processTrue, "process true MC", true); + PROCESS_SWITCH(MultiplicityPt, processTrue, "process true MC", true); // ======================================================================== // TRACK SELECTION FUNCTIONS - MATCHING spectraTOF @@ -564,7 +565,7 @@ void MultiplicityPt::init(InitContext const&) ue.add("hvtxZ", "Vertex Z (data);Vertex Z (cm);Events", HistType::kTH1F, {{40, -20.0, 20.0}}); ue.add("hvtxZmc", "MC vertex Z;Vertex Z (cm);Events", HistType::kTH1F, {{40, -20.0, 20.0}}); - LOG(info) << "Initialized multiplicitypt task with EXCLUSIVE PID for INCLUSIVE + PARTICLE-SPECIFIC (Pi, K, p) analysis"; + LOG(info) << "Initialized MultiplicityPt task with EXCLUSIVE PID for INCLUSIVE + PARTICLE-SPECIFIC (Pi, K, p) analysis"; } // ======================================================================== From 6af8231c66e547add02db5dbe6285175b26e7640 Mon Sep 17 00:00:00 2001 From: Dushmanta Sahu Date: Tue, 20 Jan 2026 14:12:57 -0600 Subject: [PATCH 06/10] Fix linter errors --- PWGLF/Tasks/Nuspex/MultiplicityPt.cxx | 3 --- 1 file changed, 3 deletions(-) diff --git a/PWGLF/Tasks/Nuspex/MultiplicityPt.cxx b/PWGLF/Tasks/Nuspex/MultiplicityPt.cxx index b9a676b87d8..59cd71909c6 100644 --- a/PWGLF/Tasks/Nuspex/MultiplicityPt.cxx +++ b/PWGLF/Tasks/Nuspex/MultiplicityPt.cxx @@ -793,7 +793,6 @@ void MultiplicityPt::processMC(TrackTableMC const& tracks, } } } - // ======================================================================== // KAON CHANNEL // ======================================================================== @@ -820,7 +819,6 @@ void MultiplicityPt::processMC(TrackTableMC const& tracks, } } } - // ======================================================================== // PROTON CHANNEL // ======================================================================== @@ -847,7 +845,6 @@ void MultiplicityPt::processMC(TrackTableMC const& tracks, } } } - LOG(info) << "=== DEBUG TRACK COUNTING ==="; LOG(info) << "Total tracks processed: " << totalTracksProcessed; LOG(info) << "Tracks from selected events: " << tracksFromSelectedEvents; From ed834fa85b592aa548549616a87cf5fcb08bac32 Mon Sep 17 00:00:00 2001 From: Dushmanta Sahu Date: Tue, 20 Jan 2026 14:20:23 -0600 Subject: [PATCH 07/10] Fix linter errors --- PWGLF/Tasks/Nuspex/MultiplicityPt.cxx | 23 ++--------------------- 1 file changed, 2 insertions(+), 21 deletions(-) diff --git a/PWGLF/Tasks/Nuspex/MultiplicityPt.cxx b/PWGLF/Tasks/Nuspex/MultiplicityPt.cxx index 59cd71909c6..ee487ddc6fb 100644 --- a/PWGLF/Tasks/Nuspex/MultiplicityPt.cxx +++ b/PWGLF/Tasks/Nuspex/MultiplicityPt.cxx @@ -760,15 +760,8 @@ void MultiplicityPt::processMC(TrackTableMC const& tracks, } } - // ======================================================================== - // EXCLUSIVE PID - FIXED PRIMARY FRACTION LOGIC - // ======================================================================== - int bestSpecies = getBestPIDHypothesis(track); - // ======================================================================== - // PION CHANNEL - // ======================================================================== if (bestSpecies == kPion) { ue.fill(HIST("Pion/hPtMeasured"), track.pt()); ue.fill(HIST("Pion/hPtAllReco"), track.pt()); @@ -780,10 +773,6 @@ void MultiplicityPt::processMC(TrackTableMC const& tracks, if (track.has_mcParticle()) { const auto& particle = track.mcParticle(); - - // KEY FIX: Primary fraction = fraction of identified pions that are primary - // This includes correctly identified pions AND misidentified kaons/protons - // that happen to be primary particles if (particle.isPhysicalPrimary()) { ue.fill(HIST("Pion/hPtPrimReco"), track.pt()); particleTracksPrimary[kPion]++; @@ -792,11 +781,7 @@ void MultiplicityPt::processMC(TrackTableMC const& tracks, particleTracksSecondary[kPion]++; } } - } - // ======================================================================== - // KAON CHANNEL - // ======================================================================== - else if (bestSpecies == kKaon) { + } else if (bestSpecies == kKaon) { ue.fill(HIST("Kaon/hPtMeasured"), track.pt()); ue.fill(HIST("Kaon/hPtAllReco"), track.pt()); particleTracksIdentified[kKaon]++; @@ -818,11 +803,7 @@ void MultiplicityPt::processMC(TrackTableMC const& tracks, particleTracksSecondary[kKaon]++; } } - } - // ======================================================================== - // PROTON CHANNEL - // ======================================================================== - else if (bestSpecies == kProton) { + } else if (bestSpecies == kProton) { ue.fill(HIST("Proton/hPtMeasured"), track.pt()); ue.fill(HIST("Proton/hPtAllReco"), track.pt()); particleTracksIdentified[kProton]++; From 29126a0f46d72c6378e53911ee2cd915cc18032e Mon Sep 17 00:00:00 2001 From: Dushmanta Sahu Date: Tue, 20 Jan 2026 14:29:55 -0600 Subject: [PATCH 08/10] Fix linter errors --- PWGLF/Tasks/Nuspex/MultiplicityPt.cxx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/PWGLF/Tasks/Nuspex/MultiplicityPt.cxx b/PWGLF/Tasks/Nuspex/MultiplicityPt.cxx index ee487ddc6fb..cbd729d398f 100644 --- a/PWGLF/Tasks/Nuspex/MultiplicityPt.cxx +++ b/PWGLF/Tasks/Nuspex/MultiplicityPt.cxx @@ -842,15 +842,15 @@ void MultiplicityPt::processMC(TrackTableMC const& tracks, // Calculate and log primary fractions if (particleTracksIdentified[kPion] > 0) { - float pionPrimFrac = (float)particleTracksPrimary[kPion] / particleTracksIdentified[kPion]; + float pionPrimFrac = static_cast particleTracksPrimary[kPion] / particleTracksIdentified[kPion]; LOG(info) << "Pion primary fraction: " << pionPrimFrac * 100.0 << "%"; } if (particleTracksIdentified[kKaon] > 0) { - float kaonPrimFrac = (float)particleTracksPrimary[kKaon] / particleTracksIdentified[kKaon]; + float kaonPrimFrac = static_cast particleTracksPrimary[kKaon] / particleTracksIdentified[kKaon]; LOG(info) << "Kaon primary fraction: " << kaonPrimFrac * 100.0 << "%"; } if (particleTracksIdentified[kProton] > 0) { - float protonPrimFrac = (float)particleTracksPrimary[kProton] / particleTracksIdentified[kProton]; + float protonPrimFrac = static_cast particleTracksPrimary[kProton] / particleTracksIdentified[kProton]; LOG(info) << "Proton primary fraction: " << protonPrimFrac * 100.0 << "%"; } From 67ab8d94498fbee7a87819f49d0f9fd99b80a84b Mon Sep 17 00:00:00 2001 From: Dushmanta Sahu Date: Wed, 21 Jan 2026 10:22:59 -0600 Subject: [PATCH 09/10] Fix linter and alibuild errors --- PWGLF/Tasks/Nuspex/MultiplicityPt.cxx | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/PWGLF/Tasks/Nuspex/MultiplicityPt.cxx b/PWGLF/Tasks/Nuspex/MultiplicityPt.cxx index cbd729d398f..e388ba4678e 100644 --- a/PWGLF/Tasks/Nuspex/MultiplicityPt.cxx +++ b/PWGLF/Tasks/Nuspex/MultiplicityPt.cxx @@ -11,6 +11,7 @@ #include "PWGLF/Utils/inelGt.h" +#include "Common/Constants/PhysicsConstants.h" #include "Common/Core/TrackSelection.h" #include "Common/Core/TrackSelectionDefaults.h" #include "Common/DataModel/Centrality.h" @@ -37,6 +38,8 @@ #include #include +#include +#include #include using namespace o2; @@ -139,15 +142,10 @@ struct MultiplicityPt { }; // PDG codes - static constexpr int PDGPion = Pdg::kPiPlus; - static constexpr int PDGKaon = Pdg::kKPlus; - static constexpr int PDGProton = Pdg::kProton; + static constexpr int PDGPion = o2::constants::physics::PionPlus; + static constexpr int PDGKaon = o2::constants::physics::KaonPlus; + static constexpr int PDGProton = o2::constants::physics::Proton; - // ======================================================================== - // PROCESS FUNCTION DECLARATIONS - SPECTRATOF STYLE - // ======================================================================== - - // Data processing void processData(CollisionTableData::iterator const& collision, TrackTableData const& tracks); PROCESS_SWITCH(MultiplicityPt, processData, "process data", false); @@ -842,15 +840,15 @@ void MultiplicityPt::processMC(TrackTableMC const& tracks, // Calculate and log primary fractions if (particleTracksIdentified[kPion] > 0) { - float pionPrimFrac = static_cast particleTracksPrimary[kPion] / particleTracksIdentified[kPion]; + float pionPrimFrac = static_cast(particleTracksPrimary[kPion]) / particleTracksIdentified[kPion]; LOG(info) << "Pion primary fraction: " << pionPrimFrac * 100.0 << "%"; } if (particleTracksIdentified[kKaon] > 0) { - float kaonPrimFrac = static_cast particleTracksPrimary[kKaon] / particleTracksIdentified[kKaon]; + float kaonPrimFrac = static_cast(particleTracksPrimary[kKaon]) / particleTracksIdentified[kKaon]; LOG(info) << "Kaon primary fraction: " << kaonPrimFrac * 100.0 << "%"; } if (particleTracksIdentified[kProton] > 0) { - float protonPrimFrac = static_cast particleTracksPrimary[kProton] / particleTracksIdentified[kProton]; + float protonPrimFrac = static_cast(particleTracksPrimary[kProton]) / particleTracksIdentified[kProton]; LOG(info) << "Proton primary fraction: " << protonPrimFrac * 100.0 << "%"; } From 4f8af3530a3ddee866e29decd44404b576088d3b Mon Sep 17 00:00:00 2001 From: Dushmanta Sahu Date: Wed, 21 Jan 2026 10:34:32 -0600 Subject: [PATCH 10/10] Fix linter and alibuild errors --- PWGLF/Tasks/Nuspex/MultiplicityPt.cxx | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/PWGLF/Tasks/Nuspex/MultiplicityPt.cxx b/PWGLF/Tasks/Nuspex/MultiplicityPt.cxx index e388ba4678e..79467bfccb5 100644 --- a/PWGLF/Tasks/Nuspex/MultiplicityPt.cxx +++ b/PWGLF/Tasks/Nuspex/MultiplicityPt.cxx @@ -11,6 +11,7 @@ #include "PWGLF/Utils/inelGt.h" +#include "Common/Constants/MathConstants.h" #include "Common/Constants/PhysicsConstants.h" #include "Common/Core/TrackSelection.h" #include "Common/Core/TrackSelectionDefaults.h" @@ -46,6 +47,7 @@ using namespace o2; using namespace o2::framework; using namespace o2::framework::expressions; using namespace o2::constants::physics; +using namespace o2::constants::math; using BCsRun3 = soa::Join; @@ -191,7 +193,10 @@ struct MultiplicityPt { if (!passesCutWoDCA(track)) { return false; } - const float maxDcaXY = maxDcaXYFactor.value * (0.0105f + 0.0350f / std::pow(track.pt(), 1.1f)); + constexpr float dcaXYConst = 0.0105f; + constexpr float dcaXYPtScale = 0.0350f; + constexpr float dcaXYPtPower = 1.1f; + const float maxDcaXY = maxDcaXYFactor.value * (dcaXYConst + dcaXYPtScale / std::pow(track.pt(), dcaXYPtPower)); if (std::abs(track.dcaXY()) > maxDcaXY) { return false; } @@ -249,8 +254,8 @@ struct MultiplicityPt { float nsigmaKa = std::abs(track.tpcNSigmaKa()); float nsigmaPr = std::abs(track.tpcNSigmaPr()); - // Find the hypothesis with smallest |nσ| that passes the cut - float minNSigma = 999.0f; + constexpr float largeNSigmaValue = 999.0f; + float minNSigma = largeNSigmaValue; int bestSpecies = -1; if (nsigmaPi < cfgCutNsigma.value && nsigmaPi < minNSigma) { @@ -540,10 +545,10 @@ void MultiplicityPt::init(InitContext const&) } } - // ======================================================================== - // MONITORING HISTOGRAMS - // ======================================================================== - ue.add("evsel", "Event selection", HistType::kTH1D, {{20, 0.5, 20.5}}); + constexpr int nEvSelBins = 20; + constexpr float evSelMin = 0.5f; + constexpr float evSelMax = 20.5f; + ue.add("evsel", "Event selection", HistType::kTH1D, {{nEvSelBins, evSelMin, evSelMax}}); auto h = ue.get(HIST("evsel")); h->GetXaxis()->SetBinLabel(1, "Events read"); h->GetXaxis()->SetBinLabel(2, "INEL>0"); @@ -559,7 +564,7 @@ void MultiplicityPt::init(InitContext const&) h->GetXaxis()->SetBinLabel(15, "INEL>1 (final)"); ue.add("hEta", "Track eta;#eta;Counts", HistType::kTH1D, {{20, -0.8, 0.8}}); - ue.add("hPhi", "Track phi;#varphi (rad);Counts", HistType::kTH1D, {{64, 0, 2.0 * o2::constants::math::PI}}); + ue.add("hPhi", "Track phi;#varphi (rad);Counts", HistType::kTH1D, {{64, 0, TwoPI}}); ue.add("hvtxZ", "Vertex Z (data);Vertex Z (cm);Events", HistType::kTH1F, {{40, -20.0, 20.0}}); ue.add("hvtxZmc", "MC vertex Z;Vertex Z (cm);Events", HistType::kTH1F, {{40, -20.0, 20.0}});