From e22ca4a61a2aa0ef645309d5ab9274ffeeeb92fd Mon Sep 17 00:00:00 2001 From: Madalina Tarzila Date: Thu, 1 Oct 2026 15:05:09 +0300 Subject: [PATCH 1/8] =?UTF-8?q?This=20is=20the=20first=20commit=20of=20thi?= =?UTF-8?q?s=20task.=20It=20adds=20the=20two-particle=20angular=20correlat?= =?UTF-8?q?ion=20(=CE=94=CF=86,=20=CE=94=CE=B7)=20analysis=20in=20pp=20col?= =?UTF-8?q?lisions=20at=2013=20TeV,=20structured=20in=20parallel=20with=20?= =?UTF-8?q?the=20AliRoot=20AliMESppColTask=20analysis,=20to=20PWGCF/TwoPar?= =?UTF-8?q?ticleCorrelations.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds an analysis task (o2-analysis-cf-two-particle-correlations-mult-spher) that fills same-event and mixed-event Δφ-Δη correlation histograms (THnSparse for the full multi-dimensional binning, plus direct TH2D SE/ME pairs for both per-trigger and per-pair normalisation), differential in multiplicity class and transverse sphericity class, at both MC generator level and reconstructed level. Some selection variants are kept togglable via Configurables for methodology cross-checks. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01YbYBgosEJaDJTkCuqAtNHk --- .../Tasks/CMakeLists.txt | 5 + .../twoParticleCorrelationsMultSpher.cxx | 1481 +++++++++++++++++ 2 files changed, 1486 insertions(+) create mode 100644 PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationsMultSpher.cxx diff --git a/PWGCF/TwoParticleCorrelations/Tasks/CMakeLists.txt b/PWGCF/TwoParticleCorrelations/Tasks/CMakeLists.txt index b6fd881bf0e..f6b7fa27511 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/CMakeLists.txt +++ b/PWGCF/TwoParticleCorrelations/Tasks/CMakeLists.txt @@ -142,3 +142,8 @@ o2physics_add_dpl_workflow(fox-wolfram-correlation SOURCES FoxWolframCorrelation.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(two-particle-correlations-mult-spher + SOURCES twoParticleCorrelationsMultSpher.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) diff --git a/PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationsMultSpher.cxx b/PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationsMultSpher.cxx new file mode 100644 index 00000000000..ec88ca2e1c4 --- /dev/null +++ b/PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationsMultSpher.cxx @@ -0,0 +1,1481 @@ +// 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. +// +/// \brief Two-particle angular correlations in pp collisions at 13 TeV. +/// Structured in parallel with the AliRoot AliMESppColTask analysis. +/// MC truth level: McCollision + McParticles. +/// Includes: multiplicity classes, transverse sphericity, PID (MC: PDG code), +/// event mixing with 4 separate pools per sphericity class, +/// THnSparse for multi-dimensional analysis + TH2D SE/ME for +/// direct normalisation (both per-trigger and per-pair). +/// +/// Pool structure (separate for MC generated and Reco reconstructed data): +/// eventPools_MC[sphClass][zvtxBin][multBin] +/// eventPools_Reco[sphClass][zvtxBin][multBin] +/// sphClass 0 : S_T in (0.0, 0.3] — jetty +/// sphClass 1 : S_T in (0.3, 0.6] — intermediate +/// sphClass 2 : S_T in (0.6, 1.0] — isotropic +/// sphClass 3 : S_T > 0.0 — all events (no sphericity cut) +/// +/// \author Madalina Tarzila + +#include "Framework/runDataProcessing.h" +#include "Framework/AnalysisTask.h" +#include "Framework/ASoAHelpers.h" + +// Reco headers — compiled but used only by processReco (switched off for now) +#include "Common/DataModel/TrackSelectionTables.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/PIDResponseTOF.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +// ============================================================ +// PID species +// ============================================================ +enum PidSpecies { kUnidentified = 0, kPion, kKaon, kProton, kNPidSpecies }; +static constexpr const char* kPidNames[kNPidSpecies] = {"unid", "pion", "kaon", "proton"}; + +// ============================================================ +// Sphericity classes +// ============================================================ +static constexpr int nSphClasses = 4; +static constexpr double kSphMin[nSphClasses] = {0.0, 0.3, 0.6, 0.0}; +static constexpr double kSphMax[nSphClasses] = {0.3, 0.6, 1.0, 1.0}; +static constexpr const char* kSphLabels[nSphClasses] = + {"jetty", "intermediate", "isotropic", "all"}; + +// ============================================================ +struct myExampleTask { + + // ---------------------------------------------------------------- + // Configurables + // ---------------------------------------------------------------- + Configurable nBinsPt {"nBinsPt", 100, "N bins in pT histograms [0-10 GeV/c]"}; + Configurable nBinsPt2 {"nBinsPt2", 50, "N bins in pT study histograms [0-5 GeV/c]"}; + Configurable mixPoolSize {"mixPoolSize", 200, "Max events per mixing pool cell"}; + Configurable minAssocPt {"minAssocPt", 0.2, "Min pT for track selection (GeV/c)"}; + Configurable minLeadPt {"minLeadPt", 1.0, "Min pT for leading (trigger) particle (GeV/c)"}; + Configurable maxLeadPt {"maxLeadPt", 2.0, "Max pT for leading (trigger) particle (GeV/c)"}; + Configurable minAssocCorr{"minAssocCorr", 1.0, "Min pT for associated particle in correlations (GeV/c)"}; + Configurable maxAssocCorr{"maxAssocCorr", 2.0, "Max pT for associated particle in correlations (GeV/c)"}; + Configurable maxEta {"maxEta", 0.8, "Max |eta| for track selection"}; + Configurable maxZvtx {"maxZvtx", 10.0, "Max |z_vtx| (cm)"}; + // |y| cut applied ONLY to PID-identified tracks (pion/kaon/proton) -- + // independent of maxEta, which applies to all-species tracks (hSE/hME). + // Rapidity is well-defined only for massive/identified particles; at + // pT=0.5 GeV/c, proton y_max~0.48, so |y|<0.5 keeps full acceptance for + // that species. Reference: ALICE 2511.10399 (arXiv, Nov 2025), Sec. 4 -- + // "tracks reconstructed within |eta|<0.8, with the additional rapidity + // cut of |y|<0.5" (verified 2026-08-11, quoted directly from the paper). + Configurable maxRapPID {"maxRapPID", 0.5, "Max |y| for PID-identified tracks (pion/kaon/proton), independent of maxEta"}; + + // Reco-specific configurables + Configurable maxDcaXY {"maxDcaXY", 2.0, "Max DCA XY (cm) for reco tracks"}; + Configurable maxDcaZ {"maxDcaZ", 2.0, "Max DCA Z (cm) for reco tracks"}; + // Combined TPC+TOF PID criterion per ALICE 2511.10399 Sec. 4: two + // DIFFERENT thresholds, not one -- selection at 2 sigma, ambiguity + // rejection at 3 sigma (verified directly against the paper text + // 2026-08-11; an earlier draft of this instruction conflated the two + // into a single 2-sigma threshold, corrected here). + Configurable maxNSigmaPID {"maxNSigmaPID", 2.0, "Max combined N_sigma,PID for species selection (ALICE 2511.10399)"}; + Configurable maxNSigmaPIDAmbig{"maxNSigmaPIDAmbig", 3.0, "N_sigma,PID threshold for ambiguity rejection (>1 species below this -> kUnidentified)"}; + + // Pre-2026-08-11 PID thresholds, restored (not reused for anything else) + // so the legacy gating style below can be toggled back on without + // deleting the ALICE 2511.10399 criterion. Values match the original + // (myExampleTask_BeforeOnlyProtons.cxx): independent 3-sigma cuts on + // TPC and TOF, no combined quadrature, no pT-dependent TOF gating. + Configurable maxNSigmaTPC{"maxNSigmaTPC", 3.0, "[legacy] Max |N_sigma| TPC for PID, used only when useLegacyNSigmaGating=true"}; + Configurable maxNSigmaTOF{"maxNSigmaTOF", 3.0, "[legacy] Max |N_sigma| TOF for PID, used only when useLegacyNSigmaGating=true"}; + + // Three independently toggleable flags to compare pre-2026-08-11 PID + // methodology against the current ALICE 2511.10399-based one, for the + // PID-methodology-vs-statistics study (requested 2026-08-13). Each + // isolates one component instead of a single "old vs new" switch, so + // any combination of the 8 can be run to see which change drives a + // statistics difference. Gating/selection defaults still reproduce the + // 2511.10399 behavior exactly; setting all three to the legacy side + // reproduces myExampleTask_BeforeOnlyProtons.cxx's pidFromNSigma exactly. + // + // applyRapidityCutPID defaults to false as of 2026-09-03, per the + // completed PID-methodology-vs-statistics study (49-file MC, see + // ~/EventShape-Wiki/Physics/PID-Methodology-Statistics-Impact-Study.md): + // the |y|<0.5 cut only discarded already-correctly-identified tracks + // between |y|=0.5 and the |eta|<0.8 acceptance, so dropping it raises + // both yield and purity simultaneously -- no tradeoff. Gating/selection + // are kept at the current (non-legacy) values by the same decision: + // gating relaxation was found to be a real purity-for-statistics + // tradeoff, not a free win, and was declined. + Configurable useLegacyNSigmaGating {"useLegacyNSigmaGating", false, "false=pT-gated combined N_sigma,PID (2511.10399); true=independent TPC/TOF 3-sigma AND-gate, TOF always required when available (pre-2026-08-11)"}; + Configurable useLegacyPIDSelection {"useLegacyPIDSelection", false, "false=reject if >1 species below ambiguity threshold; true=pick minimum-chi2 species among those passing the gate, no rejection (pre-2026-08-11)"}; + Configurable applyRapidityCutPID {"applyRapidityCutPID", false, "false=skip the |y| pidIdx (-1 se non e' pion/kaon/proton) + static int pidSpeciesToIdx(PidSpecies pid) { + if (pid == kPion) return 0; + if (pid == kKaon) return 1; + if (pid == kProton) return 2; + return -1; + } + + using EventPool = std::deque>; + // Pool "all" — separate per MC (generated) and Reco (reconstructed) + std::array, nZvtxBins>, nSphClasses> eventPools_MC; + std::array, nZvtxBins>, nSphClasses> eventPools_Reco; + // Pool per specie PID (like-sign) — separate per MC e Reco + std::array, nZvtxBins>, nSphClasses>, nPidCorr> eventPoolsPID_MC; + std::array, nZvtxBins>, nSphClasses>, nPidCorr> eventPoolsPID_Reco; + + // ---------------------------------------------------------------- + // Histogram registry + handles + // ---------------------------------------------------------------- + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + // SE/ME TH2D "all": [sphClass][multBin] — MC e Reco separati + std::array, nMultBins>, nSphClasses> hSE_MC{}; + std::array, nMultBins>, nSphClasses> hME_MC{}; + std::array, nMultBins>, nSphClasses> hSE_Reco{}; + std::array, nMultBins>, nSphClasses> hME_Reco{}; + + // SE/ME TH2D like-sign per specie: [pidIdx][sphClass][multBin] — MC e Reco separati + std::array, nMultBins>, nSphClasses>, nPidCorr> hSEpid_MC{}; + std::array, nMultBins>, nSphClasses>, nPidCorr> hMEpid_MC{}; + std::array, nMultBins>, nSphClasses>, nPidCorr> hSEpid_Reco{}; + std::array, nMultBins>, nSphClasses>, nPidCorr> hMEpid_Reco{}; + + // Trigger counter: axes = multBin(x) x sphClass(y) — MC e Reco separati + std::shared_ptr hNtrig_MC{}; + std::shared_ptr hNtrig_Reco{}; + std::array, nPidCorr> hNtrigPID_MC{}; + std::array, nPidCorr> hNtrigPID_Reco{}; + + // Per-mult sphericity — MC e Reco separati + std::array, nMultBins> hSphMult_MC{}; + std::array, nMultBins> hSphMult_Reco{}; + + // ---------------------------------------------------------------- + // Study histograms: pT × multBin, per (sphClass, PID) + // Axes: x = pT [0,5] nBinsPt2 bins, y = multBin [0-5] 6 bins + // + // [sphClass][pid]: + // sphClass: 0=jetty, 1=interm, 2=isotr, 3=all + // pid: 0=all, 1=pion, 2=kaon, 3=proton + // + // NOTE: pid index 0 here means "all PID" (no PID selection), + // pid 1-3 correspond to kPion=1, kKaon=2, kProton=3. + // ---------------------------------------------------------------- + static constexpr int nPidStudy = 4; // all + pion + kaon + proton + static constexpr const char* kPidStudyLabels[nPidStudy] = {"all","pion","kaon","proton"}; + + // pT (all selected tracks) × multBin — MC e Reco separati + std::array, nPidStudy>, nSphClasses> hPt_sph_pid_MC{}; + std::array, nPidStudy>, nSphClasses> hPt_sph_pid_Reco{}; + // pT leading × multBin + std::array, nPidStudy>, nSphClasses> hPtLead_sph_pid_MC{}; + std::array, nPidStudy>, nSphClasses> hPtLead_sph_pid_Reco{}; + // pT associated × multBin + std::array, nPidStudy>, nSphClasses> hPtAssoc_sph_pid_MC{}; + std::array, nPidStudy>, nSphClasses> hPtAssoc_sph_pid_Reco{}; + + // S_T × multBin (discrete), per PID — MC e Reco separati + std::array, nPidStudy> hSph_vs_mult_pid_MC{}; + std::array, nPidStudy> hSph_vs_mult_pid_Reco{}; + // mult (continuous [0,80]) × sphClass (discrete 0-2), per PID + std::array, nPidStudy> hMult_vs_sph_pid_MC{}; + std::array, nPidStudy> hMult_vs_sph_pid_Reco{}; + + // ---------------------------------------------------------------- + // Varianta A: fracție per TRACK — MC e Reco separati + // ---------------------------------------------------------------- + std::array, nPidStudy> hSphTrack_vs_mult_pid_MC{}; + std::array, nPidStudy> hSphTrack_vs_mult_pid_Reco{}; + std::array, nPidStudy> hMultTrack_vs_sph_pid_MC{}; + std::array, nPidStudy> hMultTrack_vs_sph_pid_Reco{}; + + // ---------------------------------------------------------------- + // Varianta B: fracție per eveniment, PID leading — MC e Reco separati + // ---------------------------------------------------------------- + std::array, nPidStudy> hSphLead_vs_mult_pid_MC{}; + std::array, nPidStudy> hSphLead_vs_mult_pid_Reco{}; + std::array, nPidStudy> hMultLead_vs_sph_pid_MC{}; + std::array, nPidStudy> hMultLead_vs_sph_pid_Reco{}; + + // ---------------------------------------------------------------- + // Distributia de multiplicitate REALA — MC e Reco separati + // ---------------------------------------------------------------- + std::array, nPidStudy> hMultReal_vs_sph_pid_MC{}; + std::array, nPidStudy> hMultReal_vs_sph_pid_Reco{}; + std::shared_ptr hMultReal_vs_pid_MC{}; + std::shared_ptr hMultReal_vs_pid_Reco{}; + + // ---------------------------------------------------------------- + // [DEBUG, TEMPORARY] Track-cut counter for processReco diagnostics. + // Bins: 1=tracks seen, 2=passed isGlobalTrackWoDCA, 3=passed DCAxy, + // 4=passed DCAz, 5=passed eta, 6=passed pt (selected). + // Remove once the empty-Reco-histograms issue is understood/fixed. + // ---------------------------------------------------------------- + std::shared_ptr hTrackCutDebug_Reco{}; + + // ---------------------------------------------------------------- + // PID confusion matrix (true species from PDG vs. tagged species from + // pidFromNSigma applied to the SAME reconstructed track, per pT bin). + // "Rapid" contamination estimate requested 2026-08-14 -- reports + // contamination as a systematic, does NOT correct anything. Filled + // only by processMCConfusion (needs both MC truth and reconstructed + // nSigma together, unlike processMC/processReco which only ever see + // one or the other). See auto-memory / STATUS.md for the "rapid vs + // riguros" design discussion this came out of. + // ---------------------------------------------------------------- + std::shared_ptr hPidConfusion_MC{}; + + + + // ---------------------------------------------------------------- + // Helpers + // ---------------------------------------------------------------- + + static PidSpecies pidFromPDG(int pdg) + { + switch (std::abs(pdg)) { + case 211: return kPion; + case 321: return kKaon; + case 2212: return kProton; + default: return kUnidentified; + } + } + + /// PID from TPC(+TOF) nSigma for reconstructed tracks. Two independently + /// toggleable behaviors (2026-08-13, added to compare pre-2026-08-11 PID + /// methodology against the current ALICE 2511.10399-based one, without + /// deleting either -- see useLegacyNSigmaGating/useLegacyPIDSelection + /// Configurables): + /// + /// legacyGating (gate = "does this species hypothesis pass at all"): + /// false (default): pT>0.5 && TOF available -> combined + /// N_sigma,PID = sqrt(nsTPC^2+nsTOF^2) < maxSel (2 sigma, per ALICE + /// 2511.10399 Sec. 4); else TPC-only. + /// true: pre-2026-08-11 style, matches myExampleTask_BeforeOnlyProtons.cxx + /// exactly -- |nsTPC| < maxTPC AND (if TOF available) |nsTOF| < maxTOF, + /// independently (no quadrature sum for the gate itself), TOF + /// required whenever available regardless of pT. + /// + /// legacySelection (what to do with hypotheses that pass the gate): + /// false (default): reject the track outright (kUnidentified) if MORE + /// THAN ONE hypothesis also falls below the looser ambiguity + /// threshold maxAmbig (3 sigma) -- this comparison always uses the + /// combined-quadrature N_sigma,PID, regardless of legacyGating, so + /// the two flags stay independently meaningful. + /// true: pre-2026-08-11 style -- among hypotheses passing the gate, + /// pick the one with minimum chi2 = nsTPC^2+nsTOF^2 (or nsTPC^2 + /// alone if no TOF); no rejection for multiple passing hypotheses. + /// + /// The two flags combine independently (e.g. legacy gating + new + /// selection is a valid, meaningful hybrid) -- this is deliberate, so + /// the statistics impact of each component can be isolated rather than + /// only comparing "old vs new" as one indivisible block. + template + PidSpecies pidFromNSigma(const T& track, double maxSel, double maxAmbig, + double maxTPC, double maxTOF, + bool legacyGating, bool legacySelection) const + { + struct Hypo { + PidSpecies pid; + double nsTPC; + double nsTOF; + }; + Hypo hypos[3] = { + {kPion, track.tpcNSigmaPi(), track.tofNSigmaPi()}, + {kKaon, track.tpcNSigmaKa(), track.tofNSigmaKa()}, + {kProton, track.tpcNSigmaPr(), track.tofNSigmaPr()} + }; + + // TOF unavailable -> nSigma returns -999; check with any species + bool hasTOF = (std::fabs(track.tofNSigmaPi()) < 100.f); + bool useCombined = (track.pt() > 0.5) && hasTOF; // only feeds the new-style gate + + auto passesGate = [&](const Hypo& h) { + if (legacyGating) { + return (std::fabs(h.nsTPC) < maxTPC) && (!hasTOF || std::fabs(h.nsTOF) < maxTOF); + } + double nsPID = useCombined + ? std::sqrt(h.nsTPC * h.nsTPC + h.nsTOF * h.nsTOF) + : std::fabs(h.nsTPC); + return nsPID < maxSel; + }; + + if (legacySelection) { + // Best-fit (min chi2) among hypotheses passing the gate -- no + // ambiguity rejection (pre-2026-08-11 style). + PidSpecies best = kUnidentified; + double bestChi2 = 1e9; + for (const auto& h : hypos) { + if (!passesGate(h)) continue; + double chi2 = h.nsTPC * h.nsTPC + (hasTOF ? h.nsTOF * h.nsTOF : 0.0); + if (chi2 < bestChi2) { bestChi2 = chi2; best = h.pid; } + } + return best; + } + + // Ambiguity-rejection selection (current default style). + int nBelowAmbig = 0; + PidSpecies selected = kUnidentified; + for (const auto& h : hypos) { + if (!passesGate(h)) continue; + double nsPID = useCombined + ? std::sqrt(h.nsTPC * h.nsTPC + h.nsTOF * h.nsTOF) + : std::fabs(h.nsTPC); + if (nsPID < maxAmbig) ++nBelowAmbig; + selected = h.pid; + } + if (nBelowAmbig > 1) return kUnidentified; // ambiguous, reject + return selected; + } + + /// Returns PDG mass for identified species, 0 for unidentified. + static double pidMass(PidSpecies pid) + { + switch (pid) { + case kPion: return 0.13957; + case kKaon: return 0.49368; + case kProton: return 0.93827; + default: return 0.; + } + } + + // Returns specific sph class (0,1,2) or -1 if unphysical + // Note: class 3 ("all") is handled separately — it is always filled + // when sph > 0, without needing a specific bin lookup. + static int sphClass(double sph) + { + if (sph <= 0.) return -1; + if (sph <= 0.3) return 0; + if (sph <= 0.6) return 1; + if (sph <= 1.0) return 2; + return -1; + } + + static int zvtxBin(double z) + { + if (z >= -10. && z < -5.) return 0; + if (z >= -5. && z < -2.5) return 1; + if (z >= -2.5 && z < 2.5) return 2; + if (z >= 2.5 && z < 5.) return 3; + if (z >= 5. && z <= 10.) return 4; + return -1; + } + + static int multBin(int n) + { + if (n >= 1 && n <= 9) return 0; + if (n >= 10 && n <= 14) return 1; + if (n >= 15 && n <= 19) return 2; + if (n >= 20 && n <= 29) return 3; + if (n >= 30 && n <= 45) return 4; + if (n >= 46 && n <= 80) return 5; + return -1; + } + + static double computeDeltaPhi(double phi1, double phi2) + { + double dphi = phi1 - phi2; + while (dphi <= -TMath::Pi()) dphi += 2. * TMath::Pi(); + while (dphi > TMath::Pi()) dphi -= 2. * TMath::Pi(); + if (dphi <= -0.5 * TMath::Pi()) dphi += 2. * TMath::Pi(); + return dphi; + } + + static double computeSphericity(const std::vector& tracks) + { + if (tracks.size() < 3) return -1.0; + double Sxx = 0., Sxy = 0., Syy = 0.; + for (const auto& t : tracks) { + double px = t.pt * std::cos(t.phi); + double py = t.pt * std::sin(t.phi); + Sxx += px * px; + Sxy += px * py; + Syy += py * py; + } + double tr = Sxx + Syy; + double disc = tr * tr - 4.0 * (Sxx * Syy - Sxy * Sxy); + if (disc < 0.) disc = 0.; + double lambdaMin = (tr - std::sqrt(disc)) / 2.0; + if (tr < 1e-12) return -1.0; + return std::clamp(2.0 * lambdaMin / tr, 0.0, 1.0); + } + + /// Compute rapidity from pT, eta and particle mass. + /// y = 0.5 * ln((E + pz) / (E - pz)) + /// For unidentified particles (mass <= 0), returns eta. + static double computeRapidity(double pt, double eta, double mass) + { + if (mass <= 0.) return eta; // massless / unidentified -> y = eta + double pz = pt * std::sinh(eta); + double E = std::sqrt(mass * mass + pt * pt + pz * pz); + if (E <= std::fabs(pz) + 1e-12) return eta; // safety + return 0.5 * std::log((E + pz) / (E - pz)); + } + + // ---------------------------------------------------------------- + // init + // ---------------------------------------------------------------- + void init(InitContext const&) + { + const AxisSpec axisZvtx {100, -15., 15., "z_{vtx} (cm)"}; + const AxisSpec axisMult {100, 0., 180., "N_{ch}"}; + const AxisSpec axisEta { 30, -1.5, 1.5, "#eta"}; + const AxisSpec axisPhi { 60, 0., 2.*TMath::Pi(),"#phi"}; + const AxisSpec axisDphi { 30, -0.5*TMath::Pi(), 1.5*TMath::Pi(), "#Delta#phi"}; + const AxisSpec axisDeta { 32, -1.6, 1.6, "#Delta#eta"}; + // Reserved for a future rapidity-differentiated analysis -- unused + // while the PID correlations run pseudorapidity-only (see + // eventshape-pid-pseudorapidity-only auto-memory, 2026-09-15). + [[maybe_unused]] const AxisSpec axisDy { 32, -1.6, 1.6, "#Delta y"}; + const AxisSpec axisPt {nBinsPt, 0., 10., "p_{T} (GeV/c)"}; + const AxisSpec axisSph {100, 0., 1., "S_{T}"}; + const AxisSpec axisPt2 {nBinsPt2, 0., 5., "p_{T} (GeV/c)"}; + const AxisSpec axisMultBin{nMultBins, -0.5, (double)nMultBins - 0.5, "mult bin"}; + const AxisSpec axisSphCls {3, -0.5, 2.5, "sph class"}; + const AxisSpec axisMultCont{80, 0., 80., "N_{ch}"}; + + // ---------------------------------------------------------------- + // Lambda comuna: creeaza un set complet de histograme pentru un + // "canal" (MC sau Reco), cu toate numele suffixate corespunzator. + // Primeste referinte la toti membrii specifici canalului. + // ---------------------------------------------------------------- + auto bookChannel = [&](const char* suf, + std::array, nMultBins>, nSphClasses>& hSE_, + std::array, nMultBins>, nSphClasses>& hME_, + std::array, nMultBins>, nSphClasses>, nPidCorr>& hSEpid_, + std::array, nMultBins>, nSphClasses>, nPidCorr>& hMEpid_, + std::shared_ptr& hNtrig_, + std::array, nPidCorr>& hNtrigPID_, + std::array, nMultBins>& hSphMult_, + std::array, nPidStudy>, nSphClasses>& hPt_sph_pid_, + std::array, nPidStudy>, nSphClasses>& hPtLead_sph_pid_, + std::array, nPidStudy>, nSphClasses>& hPtAssoc_sph_pid_, + std::array, nPidStudy>& hSph_vs_mult_pid_, + std::array, nPidStudy>& hMult_vs_sph_pid_, + std::array, nPidStudy>& hSphTrack_vs_mult_pid_, + std::array, nPidStudy>& hMultTrack_vs_sph_pid_, + std::array, nPidStudy>& hSphLead_vs_mult_pid_, + std::array, nPidStudy>& hMultLead_vs_sph_pid_, + std::array, nPidStudy>& hMultReal_vs_sph_pid_, + std::shared_ptr& hMultReal_vs_pid_) + { + // QA + // evSel bins: 1=read 2=zvtx 3=multBin 4=zvtxBin 5=leadPt 6=ST_valid 7=sphClass 8=SE_filled + auto hQA = histos.add(Form("evSel_%s", suf), Form("Event selection (%s)", suf), HistType::kTH1D, {{8, 0.5, 8.5}}); + hQA->GetXaxis()->SetBinLabel(1, "Events read"); + hQA->GetXaxis()->SetBinLabel(2, "|z_{vtx}|<10"); + hQA->GetXaxis()->SetBinLabel(3, "multBin OK"); + hQA->GetXaxis()->SetBinLabel(4, "zvtxBin OK"); + hQA->GetXaxis()->SetBinLabel(5, "leadPt #in [min,max]"); + hQA->GetXaxis()->SetBinLabel(6, "S_{T} valid"); + hQA->GetXaxis()->SetBinLabel(7, "sphClass OK"); + hQA->GetXaxis()->SetBinLabel(8, "SE filled"); + + histos.add(Form("Zvertex_%s", suf), Form("z_{vtx} (%s);z_{vtx} (cm);events", suf), kTH1D, {axisZvtx}); + histos.add(Form("multHist_%s", suf), Form("N_{ch} (%s);N_{ch};events", suf), kTH1D, {axisMult}); + histos.add(Form("etaHistogram_%s", suf), Form("#eta (%s);#eta;tracks", suf), kTH1D, {axisEta}); + histos.add(Form("phiHistogram_%s", suf), Form("#phi (%s);#phi;tracks", suf), kTH1D, {axisPhi}); + histos.add(Form("ptHistogram_%s", suf), Form("p_{T} (%s);p_{T} (GeV/c);tracks", suf), kTH1D, {axisPt}); + histos.add(Form("ptLeadHistogram_%s", suf), Form("p_{T} lead (%s);p_{T} (GeV/c);events", suf), kTH1D, {axisPt}); + histos.add(Form("ptAssocHistogram_%s", suf),Form("p_{T} assoc (%s);p_{T} (GeV/c);pairs", suf), kTH1D, {axisPt}); + histos.add(Form("sphericity_%s", suf), Form("S_{T} (inclusive, %s);S_{T};events", suf), kTH1D, {axisSph}); + histos.add(Form("sphericity_beforeCuts_%s", suf),Form("S_{T} (all evts with >=3 tracks, %s);S_{T};events", suf), kTH1D, {axisSph}); + + // Per-mult sphericity + for (int im = 0; im < nMultBins; ++im) { + hSphMult_[im] = histos.add( + Form("sphericity_mult%d_%s", im, suf), + Form("S_T [%s] (%s);S_{T};events", kMultLabels[im], suf), + HistType::kTH1D, {axisSph}); + } + + // Trigger counter TH2: x = multBin, y = sphClass + hNtrig_ = histos.add( + Form("hNtrig_%s", suf), + Form("N_{trig} (%s);mult bin;sph class", suf), + HistType::kTH2D, + {{nMultBins, -0.5, (double)nMultBins - 0.5}, + {nSphClasses, -0.5, (double)nSphClasses - 0.5}}); + for (int im = 0; im < nMultBins; ++im) hNtrig_->GetXaxis()->SetBinLabel(im+1, kMultLabels[im]); + for (int ic = 0; ic < nSphClasses; ++ic) hNtrig_->GetYaxis()->SetBinLabel(ic+1, kSphLabels[ic]); + + // SE/ME TH2D: [sphClass][multBin] + for (int ic = 0; ic < nSphClasses; ++ic) { + for (int im = 0; im < nMultBins; ++im) { + hSE_[ic][im] = histos.add( + Form("hSE_%s_sph%d_mult%d", suf, ic, im), + Form("SE [%s][%s] (%s);#Delta#phi;#Delta#eta", kSphLabels[ic], kMultLabels[im], suf), + HistType::kTH2D, {axisDphi, axisDeta}); + hME_[ic][im] = histos.add( + Form("hME_%s_sph%d_mult%d", suf, ic, im), + Form("ME [%s][%s] (%s);#Delta#phi;#Delta#eta", kSphLabels[ic], kMultLabels[im], suf), + HistType::kTH2D, {axisDphi, axisDeta}); + } + } + + // ---- SE/ME like-sign per specie PID: [pidIdx][sphClass][multBin] --- + // Pseudorapidity-only regime (confirmed 2026-09-15, see Claude Code + // auto-memory eventshape-pid-pseudorapidity-only): PID correlations + // use Delta-eta, same as the inclusive hSE_/hME_ histograms, NOT + // Delta-y. The rapidity infrastructure (computeRapidity(), the + // per-track y field, axisDy below) is kept for a future + // higher-statistics rapidity-differentiated analysis -- it is + // deliberately unused here for now, not dead code to remove. + for (int ip = 0; ip < nPidCorr; ++ip) { + for (int ic = 0; ic < nSphClasses; ++ic) { + for (int im = 0; im < nMultBins; ++im) { + hSEpid_[ip][ic][im] = histos.add( + Form("hSE_%s_pid%d_sph%d_mult%d", suf, ip, ic, im), + Form("SE %s--%s [%s][%s] (%s);#Delta#varphi;#Delta#eta", + kPidCorrNames[ip], kPidCorrNames[ip], kSphLabels[ic], kMultLabels[im], suf), + HistType::kTH2D, {axisDphi, axisDeta}); + hMEpid_[ip][ic][im] = histos.add( + Form("hME_%s_pid%d_sph%d_mult%d", suf, ip, ic, im), + Form("ME %s--%s [%s][%s] (%s);#Delta#varphi;#Delta#eta", + kPidCorrNames[ip], kPidCorrNames[ip], kSphLabels[ic], kMultLabels[im], suf), + HistType::kTH2D, {axisDphi, axisDeta}); + } + } + // Trigger counter per specie PID + hNtrigPID_[ip] = histos.add( + Form("hNtrig_%s_pid%d", suf, ip), + Form("N_{trig} %s (%s);mult bin;sph class", kPidCorrNames[ip], suf), + HistType::kTH2D, + {{nMultBins, -0.5, (double)nMultBins - 0.5}, + {nSphClasses, -0.5, (double)nSphClasses - 0.5}}); + for (int im = 0; im < nMultBins; ++im) + hNtrigPID_[ip]->GetXaxis()->SetBinLabel(im+1, kMultLabels[im]); + for (int ic = 0; ic < nSphClasses; ++ic) + hNtrigPID_[ip]->GetYaxis()->SetBinLabel(ic+1, kSphLabels[ic]); + } + + // ---------------------------------------------------------------- + // [DISABLED — histogram budget] Categories 1-7 below are commented + // out via #if 0 to stay under O2's HistogramRegistry hard limit of + // 512 histograms (with both MC and Reco channels booked, we were + // at 578). These are auxiliary study histograms not read by any + // current macro (normalization macros only use hSE/hME/hSEpid/ + // hMEpid/hNtrig/hNtrigPID). Re-enable by removing #if 0 / #endif + // if needed for future studies. + // ---------------------------------------------------------------- +#if 0 + // ---- pT × multBin per (sphClass, PID) ----------------------- + for (int ic = 0; ic < nSphClasses; ++ic) { + for (int ip = 0; ip < nPidStudy; ++ip) { + // all selected tracks + hPt_sph_pid_[ic][ip] = histos.add( + Form("hPt_%s_sph%d_pid%d", suf, ic, ip), + Form("p_{T} all [%s][%s] (%s);p_{T} (GeV/c);mult bin", + kSphLabels[ic], kPidStudyLabels[ip], suf), + HistType::kTH2D, {axisPt2, axisMultBin}); + auto* hx = hPt_sph_pid_[ic][ip].get(); + for (int im = 0; im < nMultBins; ++im) + hx->GetYaxis()->SetBinLabel(im + 1, kMultLabels[im]); + + // leading (trigger) particle + hPtLead_sph_pid_[ic][ip] = histos.add( + Form("hPtLead_%s_sph%d_pid%d", suf, ic, ip), + Form("p_{T} lead [%s][%s] (%s);p_{T} (GeV/c);mult bin", + kSphLabels[ic], kPidStudyLabels[ip], suf), + HistType::kTH2D, {axisPt2, axisMultBin}); + auto* hl = hPtLead_sph_pid_[ic][ip].get(); + for (int im = 0; im < nMultBins; ++im) + hl->GetYaxis()->SetBinLabel(im + 1, kMultLabels[im]); + + // associated particles + hPtAssoc_sph_pid_[ic][ip] = histos.add( + Form("hPtAssoc_%s_sph%d_pid%d", suf, ic, ip), + Form("p_{T} assoc [%s][%s] (%s);p_{T} (GeV/c);mult bin", + kSphLabels[ic], kPidStudyLabels[ip], suf), + HistType::kTH2D, {axisPt2, axisMultBin}); + auto* ha = hPtAssoc_sph_pid_[ic][ip].get(); + for (int im = 0; im < nMultBins; ++im) + ha->GetYaxis()->SetBinLabel(im + 1, kMultLabels[im]); + } + } + + // ---- S_T × multBin (discrete) per PID — per eveniment ------ + for (int ip = 0; ip < nPidStudy; ++ip) { + hSph_vs_mult_pid_[ip] = histos.add( + Form("hSph_vs_mult_%s_pid%d", suf, ip), + Form("S_T vs mult bin [%s] (evt, %s);S_{T};mult bin", kPidStudyLabels[ip], suf), + HistType::kTH2D, {axisSph, axisMultBin}); + auto* hs = hSph_vs_mult_pid_[ip].get(); + for (int im = 0; im < nMultBins; ++im) + hs->GetYaxis()->SetBinLabel(im + 1, kMultLabels[im]); + } + + // ---- mult (continuous) × sphClass per PID — per eveniment --- + for (int ip = 0; ip < nPidStudy; ++ip) { + hMult_vs_sph_pid_[ip] = histos.add( + Form("hMult_vs_sph_%s_pid%d", suf, ip), + Form("mult vs sph class [%s] (evt, %s);N_{ch};sph class", kPidStudyLabels[ip], suf), + HistType::kTH2D, {axisMultCont, axisSphCls}); + auto* hm = hMult_vs_sph_pid_[ip].get(); + for (int ic = 0; ic < 3; ++ic) + hm->GetYaxis()->SetBinLabel(ic + 1, kSphLabels[ic]); + } + + // ---- Varianta A: per TRACK ----------------------------------- + for (int ip = 0; ip < nPidStudy; ++ip) { + hSphTrack_vs_mult_pid_[ip] = histos.add( + Form("hSphTrack_vs_mult_%s_pid%d", suf, ip), + Form("S_T vs mult bin [%s] (track, %s);S_{T};mult bin", kPidStudyLabels[ip], suf), + HistType::kTH2D, {axisSph, axisMultBin}); + auto* h = hSphTrack_vs_mult_pid_[ip].get(); + for (int im = 0; im < nMultBins; ++im) + h->GetYaxis()->SetBinLabel(im + 1, kMultLabels[im]); + + hMultTrack_vs_sph_pid_[ip] = histos.add( + Form("hMultTrack_vs_sph_%s_pid%d", suf, ip), + Form("mult vs sph class [%s] (track, %s);N_{ch};sph class", kPidStudyLabels[ip], suf), + HistType::kTH2D, {axisMultCont, axisSphCls}); + auto* hm = hMultTrack_vs_sph_pid_[ip].get(); + for (int ic = 0; ic < 3; ++ic) + hm->GetYaxis()->SetBinLabel(ic + 1, kSphLabels[ic]); + } + + // ---- Varianta B: per eveniment, PID leading ----------------- + for (int ip = 0; ip < nPidStudy; ++ip) { + hSphLead_vs_mult_pid_[ip] = histos.add( + Form("hSphLead_vs_mult_%s_pid%d", suf, ip), + Form("S_T vs mult bin [%s] (lead, %s);S_{T};mult bin", kPidStudyLabels[ip], suf), + HistType::kTH2D, {axisSph, axisMultBin}); + auto* h = hSphLead_vs_mult_pid_[ip].get(); + for (int im = 0; im < nMultBins; ++im) + h->GetYaxis()->SetBinLabel(im + 1, kMultLabels[im]); + + hMultLead_vs_sph_pid_[ip] = histos.add( + Form("hMultLead_vs_sph_%s_pid%d", suf, ip), + Form("mult vs sph class [%s] (lead, %s);N_{ch};sph class", kPidStudyLabels[ip], suf), + HistType::kTH2D, {axisMultCont, axisSphCls}); + auto* hm = hMultLead_vs_sph_pid_[ip].get(); + for (int ic = 0; ic < 3; ++ic) + hm->GetYaxis()->SetBinLabel(ic + 1, kSphLabels[ic]); + } + + // ---- Distributia N_ch reala --------------------------------- + // N_ch (0-80) × sphClass per PID + for (int ip = 0; ip < nPidStudy; ++ip) { + hMultReal_vs_sph_pid_[ip] = histos.add( + Form("hMultReal_vs_sph_%s_pid%d", suf, ip), + Form("N_ch real vs sph class [%s] (%s);N_{ch};sph class", kPidStudyLabels[ip], suf), + HistType::kTH2D, {axisMultCont, axisSphCls}); + auto* hm = hMultReal_vs_sph_pid_[ip].get(); + for (int ic = 0; ic < 3; ++ic) + hm->GetYaxis()->SetBinLabel(ic + 1, kSphLabels[ic]); + } + // N_ch (0-80) × PID (toate sfericitatile) + hMultReal_vs_pid_ = histos.add( + Form("hMultReal_vs_pid_%s", suf), + Form("N_ch real vs PID (%s);N_{ch};PID", suf), + HistType::kTH2D, + {axisMultCont, + {(int)nPidStudy, -0.5, (double)nPidStudy - 0.5}}); + for (int ip = 0; ip < nPidStudy; ++ip) + hMultReal_vs_pid_->GetYaxis()->SetBinLabel(ip + 1, kPidStudyLabels[ip]); +#endif // [DISABLED — histogram budget] categories 1-7 + }; + + // Book histograms for MC (generated) channel + bookChannel("MC", + hSE_MC, hME_MC, hSEpid_MC, hMEpid_MC, + hNtrig_MC, hNtrigPID_MC, hSphMult_MC, + hPt_sph_pid_MC, hPtLead_sph_pid_MC, hPtAssoc_sph_pid_MC, + hSph_vs_mult_pid_MC, hMult_vs_sph_pid_MC, + hSphTrack_vs_mult_pid_MC, hMultTrack_vs_sph_pid_MC, + hSphLead_vs_mult_pid_MC, hMultLead_vs_sph_pid_MC, + hMultReal_vs_sph_pid_MC, hMultReal_vs_pid_MC); + + // Book histograms for Reco (reconstructed) channel + bookChannel("Reco", + hSE_Reco, hME_Reco, hSEpid_Reco, hMEpid_Reco, + hNtrig_Reco, hNtrigPID_Reco, hSphMult_Reco, + hPt_sph_pid_Reco, hPtLead_sph_pid_Reco, hPtAssoc_sph_pid_Reco, + hSph_vs_mult_pid_Reco, hMult_vs_sph_pid_Reco, + hSphTrack_vs_mult_pid_Reco, hMultTrack_vs_sph_pid_Reco, + hSphLead_vs_mult_pid_Reco, hMultLead_vs_sph_pid_Reco, + hMultReal_vs_sph_pid_Reco, hMultReal_vs_pid_Reco); + + // [DEBUG, TEMPORARY] track-cut diagnostic counter for processReco + hTrackCutDebug_Reco = histos.add( + "hTrackCutDebug_Reco", + "Track cut flow (Reco, DEBUG);cut;tracks", + HistType::kTH1D, {{6, 0.5, 6.5}}); + hTrackCutDebug_Reco->GetXaxis()->SetBinLabel(1, "seen"); + hTrackCutDebug_Reco->GetXaxis()->SetBinLabel(2, "passed isGlobalTrackWoDCA"); + hTrackCutDebug_Reco->GetXaxis()->SetBinLabel(3, "passed DCAxy"); + hTrackCutDebug_Reco->GetXaxis()->SetBinLabel(4, "passed DCAz"); + hTrackCutDebug_Reco->GetXaxis()->SetBinLabel(5, "passed eta"); + hTrackCutDebug_Reco->GetXaxis()->SetBinLabel(6, "passed pt (selected)"); + + // PID confusion matrix (rapid contamination estimate, 2026-08-14) -- + // axes: pT, true species (PDG), tagged species (pidFromNSigma). + // Species axis order matches PidSpecies: 0=unid,1=pion,2=kaon,3=proton. + const AxisSpec axisPidSpecies{(int)kNPidSpecies, -0.5, (double)kNPidSpecies - 0.5, "species"}; + hPidConfusion_MC = histos.add( + "hPidConfusion_MC", + "True vs. tagged PID species (MC);p_{T} (GeV/c);true species;tagged species", + HistType::kTH3D, {axisPt2, axisPidSpecies, axisPidSpecies}); + for (int ip = 0; ip < (int)kNPidSpecies; ++ip) { + hPidConfusion_MC->GetYaxis()->SetBinLabel(ip + 1, kPidNames[ip]); + hPidConfusion_MC->GetZaxis()->SetBinLabel(ip + 1, kPidNames[ip]); + } + } + + + // ---------------------------------------------------------------- + // processMC + // ---------------------------------------------------------------- + void processMC(aod::McCollision const& mcCollision, + aod::McParticles const& mcParticles) + { + histos.fill(HIST("evSel_MC"), 1); + + const double zvtx = mcCollision.posZ(); + if (std::fabs(zvtx) > maxZvtx) return; + histos.fill(HIST("evSel_MC"), 2); + histos.fill(HIST("Zvertex_MC"), zvtx); + + // ---- one-pass: build selTracks + find leading --------------- + std::vector selTracks; + selTracks.reserve(64); + int leadIdx = -1; + double pTlead = -1., phiLead = 0., etaLead = 0., yLead = 0.; + + for (const auto& p : mcParticles) { + if (!p.isPhysicalPrimary()) continue; + auto* pdgPtr = TDatabasePDG::Instance()->GetParticle(p.pdgCode()); + if (!pdgPtr || std::fabs(pdgPtr->Charge()) < 0.1) continue; + if (std::fabs(p.eta()) > maxEta) continue; + if (p.pt() < minAssocPt) continue; + + double mass = pdgPtr->Mass(); + double rap = computeRapidity(p.pt(), p.eta(), mass); + + // |y| < maxRapPID cut applies ONLY to PID-identified species + // (pion/kaon/proton) -- the all-species channel (hSE/hME) keeps the + // track regardless, gated only by |eta| < maxEta above. Clean way to + // do this: keep pdg for all-species bookkeeping, but zero it (-> + // kUnidentified via pidFromPDG) when the rapidity cut fails, so the + // track contributes to hSE/hME but not hSEpid/hMEpid. + int pdgForPID = p.pdgCode(); + if (applyRapidityCutPID && pidFromPDG(pdgForPID) != kUnidentified && std::fabs(rap) > maxRapPID) { + pdgForPID = 0; + } + + int idx = (int)selTracks.size(); + selTracks.push_back({(float)p.phi(), (float)p.eta(), (float)p.pt(), + (float)rap, pdgForPID}); + + histos.fill(HIST("etaHistogram_MC"), p.eta()); + histos.fill(HIST("phiHistogram_MC"), p.phi()); + histos.fill(HIST("ptHistogram_MC"), p.pt()); + + if (p.pt() > pTlead) { + pTlead = p.pt(); + phiLead = p.phi(); + etaLead = p.eta(); + yLead = rap; + leadIdx = idx; + } + } + + const int nch = (int)selTracks.size(); + histos.fill(HIST("multHist_MC"), nch); + + const int mBin = multBin(nch); + if (mBin < 0) return; + histos.fill(HIST("evSel_MC"), 3); + + const int zBin = zvtxBin(zvtx); + if (zBin < 0) return; + histos.fill(HIST("evSel_MC"), 4); + + // ---- leading particle pT cut [minLeadPt, maxLeadPt] ----------- + if (leadIdx < 0) return; + if (pTlead < minLeadPt || pTlead > maxLeadPt) return; + histos.fill(HIST("evSel_MC"), 5); + histos.fill(HIST("ptLeadHistogram_MC"), pTlead); + + // ---- sphericity --------------------------------------------- + const double sph = computeSphericity(selTracks); + // Fill sphericity_beforeCuts here — no additional cuts applied yet + if (sph >= 0.) histos.fill(HIST("sphericity_beforeCuts_MC"), sph); + if (sph < 0.) return; // fewer than 3 tracks + histos.fill(HIST("evSel_MC"), 6); + histos.fill(HIST("sphericity_MC"), sph); + hSphMult_MC[mBin]->Fill(sph); + + const int sphCls = sphClass(sph); // 0,1,2 or -1 + if (sphCls < 0) return; + histos.fill(HIST("evSel_MC"), 7); + + const PidSpecies pidLead = pidFromPDG(selTracks[leadIdx].pdg); + + // ---- trigger counter ---------------------------------------- + hNtrig_MC->Fill((double)mBin, (double)sphCls); + hNtrig_MC->Fill((double)mBin, (double)(nSphClasses - 1)); // "all" + + // ================================================================ + // [DISABLED — histogram budget] Study histograms: S_T vs mult, + // mult vs sph, pT all/lead tracks, per-track and per-leading + // variants, real N_ch distributions. See matching #if 0 block in + // init()/bookChannel for rationale. Re-enable both together. + // ================================================================ +#if 0 + // Helper: maps PidSpecies (0=unid,1=pion,2=kaon,3=proton) + // to pidStudy index (0=all,1=pion,2=kaon,3=proton) + // All tracks fill index 0 ("all") always, plus their specific PID + auto fillPtStudy = [&](std::array, nPidStudy>, nSphClasses>& arr, + int iSphCls, int im, double pt, PidSpecies pid) { + // fill "all" (pid index 0) for both specific sph and "all" sph + arr[iSphCls][0]->Fill(pt, (double)im); + arr[nSphClasses - 1][0]->Fill(pt, (double)im); + // fill specific PID (pid index = (int)pid, which is 1-3 for pion/kaon/proton, 0=unid stays in "all" only) + if ((int)pid > 0 && (int)pid < nPidStudy) { + arr[iSphCls][(int)pid]->Fill(pt, (double)im); + arr[nSphClasses - 1][(int)pid]->Fill(pt, (double)im); + } + }; + + // ---- S_T vs multBin și mult vs sphClass — per eveniment ------ + int nTrackPid[nPidStudy] = {0, 0, 0, 0}; + for (const auto& t : selTracks) { + PidSpecies pid = pidFromPDG(t.pdg); + nTrackPid[0]++; + if ((int)pid > 0 && (int)pid < nPidStudy) + nTrackPid[(int)pid]++; + } + + // Per eveniment (cel putin o particula din specie) — histogramele originale + hSph_vs_mult_pid_MC[0]->Fill(sph, (double)mBin); + hMult_vs_sph_pid_MC[0]->Fill((double)nch, (double)sphCls); + for (int ip = 1; ip < nPidStudy; ++ip) { + if (nTrackPid[ip] > 0) { + hSph_vs_mult_pid_MC[ip]->Fill(sph, (double)mBin); + hMult_vs_sph_pid_MC[ip]->Fill((double)nch, (double)sphCls); + } + } + + // Varianta B: per eveniment, PID leading + hSphLead_vs_mult_pid_MC[0]->Fill(sph, (double)mBin); // all: sempre + hMultLead_vs_sph_pid_MC[0]->Fill((double)nch, (double)sphCls); + if ((int)pidLead > 0 && (int)pidLead < nPidStudy) { + hSphLead_vs_mult_pid_MC[(int)pidLead]->Fill(sph, (double)mBin); + hMultLead_vs_sph_pid_MC[(int)pidLead]->Fill((double)nch, (double)sphCls); + } + + // Distributia N_ch reala per sphClass, per PID leading + hMultReal_vs_sph_pid_MC[0]->Fill((double)nch, (double)sphCls); + if ((int)pidLead > 0 && (int)pidLead < nPidStudy) + hMultReal_vs_sph_pid_MC[(int)pidLead]->Fill((double)nch, (double)sphCls); + + // N_ch reala vs PID (per track — umplut o data per track) + // Umplut in bucla de trackuri de mai jos + // (hMultReal_vs_pid_MC se umple per eveniment cu PID dominant) + // Alegem: umplut per eveniment cu fiecare PID care exista in eveniment + hMultReal_vs_pid_MC->Fill((double)nch, 0.); // all: intotdeauna + for (int ip = 1; ip < nPidStudy; ++ip) { + if (nTrackPid[ip] > 0) + hMultReal_vs_pid_MC->Fill((double)nch, (double)ip); + } + + // Varianta A: per TRACK — umplut in bucla de trackuri + for (const auto& t : selTracks) { + PidSpecies pid = pidFromPDG(t.pdg); + // all tracks + hSphTrack_vs_mult_pid_MC[0]->Fill(sph, (double)mBin); + hMultTrack_vs_sph_pid_MC[0]->Fill((double)nch, (double)sphCls); + // specific PID + if ((int)pid > 0 && (int)pid < nPidStudy) { + hSphTrack_vs_mult_pid_MC[(int)pid]->Fill(sph, (double)mBin); + hMultTrack_vs_sph_pid_MC[(int)pid]->Fill((double)nch, (double)sphCls); + } + } + + // ---- pT leading per (sph, PID) ------------------------------ + hPtLead_sph_pid_MC[sphCls][0]->Fill(pTlead, (double)mBin); + hPtLead_sph_pid_MC[nSphClasses - 1][0]->Fill(pTlead, (double)mBin); + if ((int)pidLead > 0 && (int)pidLead < nPidStudy) { + hPtLead_sph_pid_MC[sphCls][(int)pidLead]->Fill(pTlead, (double)mBin); + hPtLead_sph_pid_MC[nSphClasses - 1][(int)pidLead]->Fill(pTlead, (double)mBin); + } + + // ---- pT tuturor trackurilor per (sph, PID) ------------------ + for (const auto& t : selTracks) { + PidSpecies pid = pidFromPDG(t.pdg); + fillPtStudy(hPt_sph_pid_MC, sphCls, mBin, (double)t.pt, pid); + } +#endif // [DISABLED — histogram budget] categories 1,2,3,5,6,7 + // ================================================================ + // SAME-EVENT correlations + // ================================================================ + bool seHasPairs = false; + for (int ia = 0; ia < nch; ++ia) { + if (ia == leadIdx) continue; + const auto& assoc = selTracks[ia]; + if (assoc.pt >= pTlead) continue; // asociat pT < leading pT + if (assoc.pt < minAssocCorr || assoc.pt > maxAssocCorr) continue; + + histos.fill(HIST("ptAssocHistogram_MC"), assoc.pt); + + const PidSpecies pidAssoc = pidFromPDG(assoc.pdg); + + // pT associated study histograms [DISABLED — histogram budget, category 4] +#if 0 + fillPtStudy(hPtAssoc_sph_pid_MC, sphCls, mBin, (double)assoc.pt, pidAssoc); +#endif + const double dphi = computeDeltaPhi(phiLead, assoc.phi); + const double deta = etaLead - assoc.eta; + + // TH2D SE: specific sph class (uses Delta-eta) + hSE_MC[sphCls][mBin]->Fill(dphi, deta); + // TH2D SE: "all" class (uses Delta-eta) + hSE_MC[nSphClasses - 1][mBin]->Fill(dphi, deta); + + // SE like-sign: fill solo se trigger e associato sono la stessa specie + // Uses Delta-eta (pseudorapidity-only regime, see + // eventshape-pid-pseudorapidity-only auto-memory) + int pidLeadIdx = pidSpeciesToIdx(pidLead); + int pidAssocIdx = pidSpeciesToIdx(pidAssoc); + if (pidLeadIdx >= 0 && pidLeadIdx == pidAssocIdx) { + hSEpid_MC[pidLeadIdx][sphCls][mBin]->Fill(dphi, deta); + hSEpid_MC[pidLeadIdx][nSphClasses - 1][mBin]->Fill(dphi, deta); + } + + seHasPairs = true; + } + if (seHasPairs) histos.fill(HIST("evSel_MC"), 8); + + // ---- hNtrig_MC per specie PID leading -------------------------- + int pidLeadIdx = pidSpeciesToIdx(pidLead); + if (pidLeadIdx >= 0) { + hNtrigPID_MC[pidLeadIdx]->Fill((double)mBin, (double)sphCls); + hNtrigPID_MC[pidLeadIdx]->Fill((double)mBin, (double)(nSphClasses - 1)); + } + + // ================================================================ + // Update pools AFTER SE + // ================================================================ + // Pool "all" — specific sph + { + auto& pool = eventPools_MC[sphCls][zBin][mBin]; + pool.push_back(selTracks); + if ((int)pool.size() > mixPoolSize) pool.pop_front(); + } + // Pool "all" — all sph + { + auto& poolAll = eventPools_MC[nSphClasses - 1][zBin][mBin]; + poolAll.push_back(selTracks); + if ((int)poolAll.size() > mixPoolSize) poolAll.pop_front(); + } + + // Pool per specie PID — contiene solo i track di quella specie + for (int ip = 0; ip < nPidCorr; ++ip) { + // Costruisci un vettore con solo i track della specie ip + std::vector pidTracks; + for (const auto& t : selTracks) { + if (pidSpeciesToIdx(pidFromPDG(t.pdg)) == ip) + pidTracks.push_back(t); + } + if (pidTracks.empty()) continue; + // Specific sph pool + { + auto& pool = eventPoolsPID_MC[ip][sphCls][zBin][mBin]; + pool.push_back(pidTracks); + if ((int)pool.size() > mixPoolSize) pool.pop_front(); + } + // "all" sph pool + { + auto& pool = eventPoolsPID_MC[ip][nSphClasses - 1][zBin][mBin]; + pool.push_back(pidTracks); + if ((int)pool.size() > mixPoolSize) pool.pop_front(); + } + } + + // ================================================================ + // MIXED-EVENT "all" + // ================================================================ + auto doMixing = [&](int poolIdx) { + const auto& pool = eventPools_MC[poolIdx][zBin][mBin]; + const int nEvts = (int)pool.size() - 1; + for (int ie = 0; ie < nEvts; ++ie) { + for (const auto& tr : pool[ie]) { + if (tr.pt >= pTlead) continue; + if (tr.pt < minAssocCorr || tr.pt > maxAssocCorr) continue; + const double dphi = computeDeltaPhi(phiLead, tr.phi); + const double deta = etaLead - tr.eta; + hME_MC[poolIdx][mBin]->Fill(dphi, deta); + } + } + }; + + doMixing(sphCls); + doMixing(nSphClasses - 1); + + // ================================================================ + // MIXED-EVENT like-sign per specie PID + // Trigger = leading della specie pidLeadIdx + // Associati = track della stessa specie dal pool PID + // Uses Delta-eta (pseudorapidity-only regime, see + // eventshape-pid-pseudorapidity-only auto-memory) + // ================================================================ + if (pidLeadIdx >= 0) { + auto doMixingPID = [&](int poolIdx) { + const auto& pool = eventPoolsPID_MC[pidLeadIdx][poolIdx][zBin][mBin]; + const int nEvts = (int)pool.size() - 1; + for (int ie = 0; ie < nEvts; ++ie) { + for (const auto& tr : pool[ie]) { + if (tr.pt >= pTlead) continue; + if (tr.pt < minAssocCorr || tr.pt > maxAssocCorr) continue; + const double dphi = computeDeltaPhi(phiLead, tr.phi); + const double detaPID = etaLead - tr.eta; + hMEpid_MC[pidLeadIdx][poolIdx][mBin]->Fill(dphi, detaPID); + } + } + }; + doMixingPID(sphCls); + doMixingPID(nSphClasses - 1); + } + + } + PROCESS_SWITCH(myExampleTask, processMC, "Process MC truth", true); + + // ================================================================ + // processReco — reconstructed data (Collision + Tracks) + // ================================================================ + using ColReco = soa::Join; + using TrackReco = soa::Join; + + void processReco(ColReco::iterator const& col, + TrackReco const& tracks) + { + histos.fill(HIST("evSel_Reco"), 1); + + // ---- event selection ---- + if (!col.sel8()) return; + const double zvtx = col.posZ(); + if (std::fabs(zvtx) > maxZvtx) return; + histos.fill(HIST("evSel_Reco"), 2); + histos.fill(HIST("Zvertex_Reco"), zvtx); + + // ---- one-pass: build selTracks + find leading --------------- + std::vector selTracks; + selTracks.reserve(64); + int leadIdx = -1; + double pTlead = -1., phiLead = 0., etaLead = 0., yLead = 0.; + + for (const auto& track : tracks) { + hTrackCutDebug_Reco->Fill(1); // seen + + // Quality selection: global track without DCA (apply DCA separately) + if (!track.isGlobalTrackWoDCA()) continue; + hTrackCutDebug_Reco->Fill(2); // passed isGlobalTrackWoDCA + + // DCA cuts + if (std::fabs(track.dcaXY()) > maxDcaXY) continue; + hTrackCutDebug_Reco->Fill(3); // passed DCAxy + if (std::fabs(track.dcaZ()) > maxDcaZ) continue; + hTrackCutDebug_Reco->Fill(4); // passed DCAz + + // Kinematic cuts (same as MC) + if (std::fabs(track.eta()) > maxEta) continue; + hTrackCutDebug_Reco->Fill(5); // passed eta + if (track.pt() < minAssocPt) continue; + hTrackCutDebug_Reco->Fill(6); // passed pt (selected) + + // PID from nSigma (combined TPC+TOF, ALICE 2511.10399, unless the + // legacy flags below switch to pre-2026-08-11 gating/selection) + PidSpecies pid = pidFromNSigma(track, maxNSigmaPID, maxNSigmaPIDAmbig, + maxNSigmaTPC, maxNSigmaTOF, + useLegacyNSigmaGating, useLegacyPIDSelection); + double mass = pidMass(pid); + double rap = computeRapidity(track.pt(), track.eta(), mass); + + // |y| < maxRapPID cut applies ONLY to PID-identified species -- see + // matching comment in processMC. Track stays in selTracks either way + // (all-species channel unaffected); only the PID assignment is reset. + if (applyRapidityCutPID && pid != kUnidentified && std::fabs(rap) > maxRapPID) { + pid = kUnidentified; + } + + // pdg = 0 for reco (no MC truth); store sign for future like/unlike-sign + int idx = (int)selTracks.size(); + selTracks.push_back({(float)track.phi(), (float)track.eta(), (float)track.pt(), + (float)rap, 0}); + // Store PID info in pdg field as signed pidIdx for later use: + // sign(track.sign()) * (211 for pion, 321 for kaon, 2212 for proton, 0 for unid) + int pdgLike = 0; + if (pid == kPion) pdgLike = 211; + if (pid == kKaon) pdgLike = 321; + if (pid == kProton) pdgLike = 2212; + selTracks.back().pdg = track.sign() * pdgLike; + + histos.fill(HIST("etaHistogram_Reco"), track.eta()); + histos.fill(HIST("phiHistogram_Reco"), track.phi()); + histos.fill(HIST("ptHistogram_Reco"), track.pt()); + + if (track.pt() > pTlead) { + pTlead = track.pt(); + phiLead = track.phi(); + etaLead = track.eta(); + yLead = rap; + leadIdx = idx; + } + } + + const int nch = (int)selTracks.size(); + histos.fill(HIST("multHist_Reco"), nch); + + const int mBin = multBin(nch); + if (mBin < 0) return; + histos.fill(HIST("evSel_Reco"), 3); + + const int zBin = zvtxBin(zvtx); + if (zBin < 0) return; + histos.fill(HIST("evSel_Reco"), 4); + + // ---- leading particle pT cut [minLeadPt, maxLeadPt] ----------- + if (leadIdx < 0) return; + if (pTlead < minLeadPt || pTlead > maxLeadPt) return; + histos.fill(HIST("evSel_Reco"), 5); + histos.fill(HIST("ptLeadHistogram_Reco"), pTlead); + + // ---- sphericity --------------------------------------------- + const double sph = computeSphericity(selTracks); + if (sph >= 0.) histos.fill(HIST("sphericity_beforeCuts_Reco"), sph); + if (sph < 0.) return; + histos.fill(HIST("evSel_Reco"), 6); + histos.fill(HIST("sphericity_Reco"), sph); + hSphMult_Reco[mBin]->Fill(sph); + + const int sphCls = sphClass(sph); + if (sphCls < 0) return; + histos.fill(HIST("evSel_Reco"), 7); + + const PidSpecies pidLead = pidFromPDG(selTracks[leadIdx].pdg); + + // ---- trigger counter ---------------------------------------- + hNtrig_Reco->Fill((double)mBin, (double)sphCls); + hNtrig_Reco->Fill((double)mBin, (double)(nSphClasses - 1)); + + // ================================================================ + // [DISABLED — histogram budget] Study histograms (identical to + // processMC). See matching #if 0 block in init()/bookChannel. + // ================================================================ +#if 0 + auto fillPtStudy = [&](std::array, nPidStudy>, nSphClasses>& arr, + int iSphCls, int im, double pt, PidSpecies pid) { + arr[iSphCls][0]->Fill(pt, (double)im); + arr[nSphClasses - 1][0]->Fill(pt, (double)im); + if ((int)pid > 0 && (int)pid < nPidStudy) { + arr[iSphCls][(int)pid]->Fill(pt, (double)im); + arr[nSphClasses - 1][(int)pid]->Fill(pt, (double)im); + } + }; + + int nTrackPid[nPidStudy] = {0, 0, 0, 0}; + for (const auto& t : selTracks) { + PidSpecies pid = pidFromPDG(t.pdg); + nTrackPid[0]++; + if ((int)pid > 0 && (int)pid < nPidStudy) + nTrackPid[(int)pid]++; + } + + hSph_vs_mult_pid_Reco[0]->Fill(sph, (double)mBin); + hMult_vs_sph_pid_Reco[0]->Fill((double)nch, (double)sphCls); + for (int ip = 1; ip < nPidStudy; ++ip) { + if (nTrackPid[ip] > 0) { + hSph_vs_mult_pid_Reco[ip]->Fill(sph, (double)mBin); + hMult_vs_sph_pid_Reco[ip]->Fill((double)nch, (double)sphCls); + } + } + + hSphLead_vs_mult_pid_Reco[0]->Fill(sph, (double)mBin); + hMultLead_vs_sph_pid_Reco[0]->Fill((double)nch, (double)sphCls); + if ((int)pidLead > 0 && (int)pidLead < nPidStudy) { + hSphLead_vs_mult_pid_Reco[(int)pidLead]->Fill(sph, (double)mBin); + hMultLead_vs_sph_pid_Reco[(int)pidLead]->Fill((double)nch, (double)sphCls); + } + + hMultReal_vs_sph_pid_Reco[0]->Fill((double)nch, (double)sphCls); + if ((int)pidLead > 0 && (int)pidLead < nPidStudy) + hMultReal_vs_sph_pid_Reco[(int)pidLead]->Fill((double)nch, (double)sphCls); + + hMultReal_vs_pid_Reco->Fill((double)nch, 0.); + for (int ip = 1; ip < nPidStudy; ++ip) { + if (nTrackPid[ip] > 0) + hMultReal_vs_pid_Reco->Fill((double)nch, (double)ip); + } + + for (const auto& t : selTracks) { + PidSpecies pid = pidFromPDG(t.pdg); + hSphTrack_vs_mult_pid_Reco[0]->Fill(sph, (double)mBin); + hMultTrack_vs_sph_pid_Reco[0]->Fill((double)nch, (double)sphCls); + if ((int)pid > 0 && (int)pid < nPidStudy) { + hSphTrack_vs_mult_pid_Reco[(int)pid]->Fill(sph, (double)mBin); + hMultTrack_vs_sph_pid_Reco[(int)pid]->Fill((double)nch, (double)sphCls); + } + } + + hPtLead_sph_pid_Reco[sphCls][0]->Fill(pTlead, (double)mBin); + hPtLead_sph_pid_Reco[nSphClasses - 1][0]->Fill(pTlead, (double)mBin); + if ((int)pidLead > 0 && (int)pidLead < nPidStudy) { + hPtLead_sph_pid_Reco[sphCls][(int)pidLead]->Fill(pTlead, (double)mBin); + hPtLead_sph_pid_Reco[nSphClasses - 1][(int)pidLead]->Fill(pTlead, (double)mBin); + } + + for (const auto& t : selTracks) { + PidSpecies pid = pidFromPDG(t.pdg); + fillPtStudy(hPt_sph_pid_Reco, sphCls, mBin, (double)t.pt, pid); + } +#endif // [DISABLED — histogram budget] categories 1,2,3,5,6,7 + + // ================================================================ + // SAME-EVENT correlations (identical logic to processMC) + // ================================================================ + bool seHasPairs = false; + for (int ia = 0; ia < nch; ++ia) { + if (ia == leadIdx) continue; + const auto& assoc = selTracks[ia]; + if (assoc.pt >= pTlead) continue; + if (assoc.pt < minAssocCorr || assoc.pt > maxAssocCorr) continue; + + histos.fill(HIST("ptAssocHistogram_Reco"), assoc.pt); + + const PidSpecies pidAssoc = pidFromPDG(assoc.pdg); +#if 0 + fillPtStudy(hPtAssoc_sph_pid_Reco, sphCls, mBin, (double)assoc.pt, pidAssoc); +#endif // [DISABLED — histogram budget, category 4] + + const double dphi = computeDeltaPhi(phiLead, assoc.phi); + const double deta = etaLead - assoc.eta; + + // All-species SE (Delta-eta) + hSE_Reco[sphCls][mBin]->Fill(dphi, deta); + hSE_Reco[nSphClasses - 1][mBin]->Fill(dphi, deta); + + // PID SE — same species, Delta-eta (pseudorapidity-only regime, see + // eventshape-pid-pseudorapidity-only auto-memory) + int pidLeadIdx = pidSpeciesToIdx(pidLead); + int pidAssocIdx = pidSpeciesToIdx(pidAssoc); + if (pidLeadIdx >= 0 && pidLeadIdx == pidAssocIdx) { + hSEpid_Reco[pidLeadIdx][sphCls][mBin]->Fill(dphi, deta); + hSEpid_Reco[pidLeadIdx][nSphClasses - 1][mBin]->Fill(dphi, deta); + } + + seHasPairs = true; + } + if (seHasPairs) histos.fill(HIST("evSel_Reco"), 8); + + // ---- hNtrig_Reco per specie PID leading -------------------------- + int pidLeadIdx = pidSpeciesToIdx(pidLead); + if (pidLeadIdx >= 0) { + hNtrigPID_Reco[pidLeadIdx]->Fill((double)mBin, (double)sphCls); + hNtrigPID_Reco[pidLeadIdx]->Fill((double)mBin, (double)(nSphClasses - 1)); + } + + // ================================================================ + // Update pools AFTER SE (identical to processMC) + // ================================================================ + { + auto& pool = eventPools_Reco[sphCls][zBin][mBin]; + pool.push_back(selTracks); + if ((int)pool.size() > mixPoolSize) pool.pop_front(); + } + { + auto& poolAll = eventPools_Reco[nSphClasses - 1][zBin][mBin]; + poolAll.push_back(selTracks); + if ((int)poolAll.size() > mixPoolSize) poolAll.pop_front(); + } + + for (int ip = 0; ip < nPidCorr; ++ip) { + std::vector pidTracks; + for (const auto& t : selTracks) { + if (pidSpeciesToIdx(pidFromPDG(t.pdg)) == ip) + pidTracks.push_back(t); + } + if (pidTracks.empty()) continue; + { + auto& pool = eventPoolsPID_Reco[ip][sphCls][zBin][mBin]; + pool.push_back(pidTracks); + if ((int)pool.size() > mixPoolSize) pool.pop_front(); + } + { + auto& pool = eventPoolsPID_Reco[ip][nSphClasses - 1][zBin][mBin]; + pool.push_back(pidTracks); + if ((int)pool.size() > mixPoolSize) pool.pop_front(); + } + } + + // ================================================================ + // MIXED-EVENT "all" (Delta-eta) + // ================================================================ + auto doMixing = [&](int poolIdx) { + const auto& pool = eventPools_Reco[poolIdx][zBin][mBin]; + const int nEvts = (int)pool.size() - 1; + for (int ie = 0; ie < nEvts; ++ie) { + for (const auto& tr : pool[ie]) { + if (tr.pt >= pTlead) continue; + if (tr.pt < minAssocCorr || tr.pt > maxAssocCorr) continue; + const double dphi = computeDeltaPhi(phiLead, tr.phi); + const double deta = etaLead - tr.eta; + hME_Reco[poolIdx][mBin]->Fill(dphi, deta); + } + } + }; + + doMixing(sphCls); + doMixing(nSphClasses - 1); + + // ================================================================ + // MIXED-EVENT PID (Delta-eta, pseudorapidity-only regime, see + // eventshape-pid-pseudorapidity-only auto-memory) + // ================================================================ + if (pidLeadIdx >= 0) { + auto doMixingPID = [&](int poolIdx) { + const auto& pool = eventPoolsPID_Reco[pidLeadIdx][poolIdx][zBin][mBin]; + const int nEvts = (int)pool.size() - 1; + for (int ie = 0; ie < nEvts; ++ie) { + for (const auto& tr : pool[ie]) { + if (tr.pt >= pTlead) continue; + if (tr.pt < minAssocCorr || tr.pt > maxAssocCorr) continue; + const double dphi = computeDeltaPhi(phiLead, tr.phi); + const double detaPID = etaLead - tr.eta; + hMEpid_Reco[pidLeadIdx][poolIdx][mBin]->Fill(dphi, detaPID); + } + } + }; + doMixingPID(sphCls); + doMixingPID(nSphClasses - 1); + } + + } + PROCESS_SWITCH(myExampleTask, processReco, "Process reconstructed data", false); + + // ---------------------------------------------------------------- + // processMCConfusion -- "rapid" PID contamination estimate, requested + // 2026-08-14. Fills hPidConfusion_MC (pT, true species, tagged + // species) on MC only -- needs BOTH the truth PDG (via McTrackLabels) + // AND the reconstructed track's nSigma values on the SAME track, + // which neither processMC (truth only) nor processReco (no truth + // link) has access to, hence a dedicated process function. Reports + // contamination as a systematic; does NOT correct anything (that's + // the separate, not-yet-started "riguros" unfolding approach -- see + // STATUS.md). Off by default, like the other MC-only diagnostics. + // ---------------------------------------------------------------- + using TrackRecoMC = soa::Join; + + void processMCConfusion(ColReco::iterator const& col, + TrackRecoMC const& tracks, + aod::McParticles const&) + { + if (!col.sel8()) return; + if (std::fabs(col.posZ()) > maxZvtx) return; + + for (const auto& track : tracks) { + if (!track.has_mcParticle()) continue; + auto mcPart = track.mcParticle(); + if (!mcPart.isPhysicalPrimary()) continue; + auto* pdgPtr = TDatabasePDG::Instance()->GetParticle(mcPart.pdgCode()); + if (!pdgPtr || std::fabs(pdgPtr->Charge()) < 0.1) continue; + + // Same track-level cuts as processReco, so the matrix reflects the + // actual analyzed track population, not an unfiltered sample. + if (std::fabs(track.dcaXY()) > maxDcaXY) continue; + if (std::fabs(track.dcaZ()) > maxDcaZ) continue; + if (std::fabs(track.eta()) > maxEta) continue; + if (track.pt() < minAssocPt) continue; + + PidSpecies truePid = pidFromPDG(mcPart.pdgCode()); + PidSpecies taggedPid = pidFromNSigma(track, maxNSigmaPID, maxNSigmaPIDAmbig, + maxNSigmaTPC, maxNSigmaTOF, + useLegacyNSigmaGating, useLegacyPIDSelection); + + // Same rapidity cut as processReco applies to its "tagged" PID -- + // without this, applyRapidityCutPID=false would silently have zero + // effect on this matrix (found 2026-08-14, comparing Phase 1 + // rapidityOnly against baseline: identical confusion-matrix numbers + // despite different processReco yields, before this fix). + if (applyRapidityCutPID && taggedPid != kUnidentified) { + double tagMass = pidMass(taggedPid); + double tagRap = computeRapidity(track.pt(), track.eta(), tagMass); + if (std::fabs(tagRap) > maxRapPID) taggedPid = kUnidentified; + } + + histos.fill(HIST("hPidConfusion_MC"), track.pt(), (double)truePid, (double)taggedPid); + } + } + PROCESS_SWITCH(myExampleTask, processMCConfusion, "Rapid PID contamination estimate: true (PDG) vs tagged (nSigma) species confusion matrix, MC only", false); + +}; // end struct + +// ---------------------------------------------------------------- +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} From 6ae5ad689e71eb0a006e154a10958c1a010299c7 Mon Sep 17 00:00:00 2001 From: Madalina Tarzila Date: Thu, 1 Oct 2026 15:54:34 +0300 Subject: [PATCH 2/8] Fix cpplint findings: C-style casts, comma spacing, missing include. Applies clang-format (project style) and converts C-style casts to static_cast, as flagged by the cpplint check in CI. No behavior change: rebuilt and re-ran the single-file smoke test, same histogram counts as before. Also adds the include cpplint pointed out for shared_ptr<>. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01YbYBgosEJaDJTkCuqAtNHk --- .../twoParticleCorrelationsMultSpher.cxx | 803 ++++++++++-------- 1 file changed, 457 insertions(+), 346 deletions(-) diff --git a/PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationsMultSpher.cxx b/PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationsMultSpher.cxx index ec88ca2e1c4..35ada096295 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationsMultSpher.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationsMultSpher.cxx @@ -27,27 +27,29 @@ /// /// \author Madalina Tarzila -#include "Framework/runDataProcessing.h" -#include "Framework/AnalysisTask.h" #include "Framework/ASoAHelpers.h" +#include "Framework/AnalysisTask.h" +#include "Framework/runDataProcessing.h" // Reco headers — compiled but used only by processReco (switched off for now) -#include "Common/DataModel/TrackSelectionTables.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/Multiplicity.h" -#include "Common/DataModel/PIDResponseTPC.h" #include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" -#include -#include -#include -#include -#include +#include #include #include #include +#include #include -#include + +#include +#include +#include +#include +#include using namespace o2; using namespace o2::framework; @@ -56,13 +58,17 @@ using namespace o2::framework::expressions; // ============================================================ // PID species // ============================================================ -enum PidSpecies { kUnidentified = 0, kPion, kKaon, kProton, kNPidSpecies }; +enum PidSpecies { kUnidentified = 0, + kPion, + kKaon, + kProton, + kNPidSpecies }; static constexpr const char* kPidNames[kNPidSpecies] = {"unid", "pion", "kaon", "proton"}; // ============================================================ // Sphericity classes // ============================================================ -static constexpr int nSphClasses = 4; +static constexpr int nSphClasses = 4; static constexpr double kSphMin[nSphClasses] = {0.0, 0.3, 0.6, 0.0}; static constexpr double kSphMax[nSphClasses] = {0.3, 0.6, 1.0, 1.0}; static constexpr const char* kSphLabels[nSphClasses] = @@ -74,16 +80,16 @@ struct myExampleTask { // ---------------------------------------------------------------- // Configurables // ---------------------------------------------------------------- - Configurable nBinsPt {"nBinsPt", 100, "N bins in pT histograms [0-10 GeV/c]"}; - Configurable nBinsPt2 {"nBinsPt2", 50, "N bins in pT study histograms [0-5 GeV/c]"}; - Configurable mixPoolSize {"mixPoolSize", 200, "Max events per mixing pool cell"}; - Configurable minAssocPt {"minAssocPt", 0.2, "Min pT for track selection (GeV/c)"}; - Configurable minLeadPt {"minLeadPt", 1.0, "Min pT for leading (trigger) particle (GeV/c)"}; - Configurable maxLeadPt {"maxLeadPt", 2.0, "Max pT for leading (trigger) particle (GeV/c)"}; - Configurable minAssocCorr{"minAssocCorr", 1.0, "Min pT for associated particle in correlations (GeV/c)"}; - Configurable maxAssocCorr{"maxAssocCorr", 2.0, "Max pT for associated particle in correlations (GeV/c)"}; - Configurable maxEta {"maxEta", 0.8, "Max |eta| for track selection"}; - Configurable maxZvtx {"maxZvtx", 10.0, "Max |z_vtx| (cm)"}; + Configurable nBinsPt{"nBinsPt", 100, "N bins in pT histograms [0-10 GeV/c]"}; + Configurable nBinsPt2{"nBinsPt2", 50, "N bins in pT study histograms [0-5 GeV/c]"}; + Configurable mixPoolSize{"mixPoolSize", 200, "Max events per mixing pool cell"}; + Configurable minAssocPt{"minAssocPt", 0.2, "Min pT for track selection (GeV/c)"}; + Configurable minLeadPt{"minLeadPt", 1.0, "Min pT for leading (trigger) particle (GeV/c)"}; + Configurable maxLeadPt{"maxLeadPt", 2.0, "Max pT for leading (trigger) particle (GeV/c)"}; + Configurable minAssocCorr{"minAssocCorr", 1.0, "Min pT for associated particle in correlations (GeV/c)"}; + Configurable maxAssocCorr{"maxAssocCorr", 2.0, "Max pT for associated particle in correlations (GeV/c)"}; + Configurable maxEta{"maxEta", 0.8, "Max |eta| for track selection"}; + Configurable maxZvtx{"maxZvtx", 10.0, "Max |z_vtx| (cm)"}; // |y| cut applied ONLY to PID-identified tracks (pion/kaon/proton) -- // independent of maxEta, which applies to all-species tracks (hSE/hME). // Rapidity is well-defined only for massive/identified particles; at @@ -91,17 +97,17 @@ struct myExampleTask { // that species. Reference: ALICE 2511.10399 (arXiv, Nov 2025), Sec. 4 -- // "tracks reconstructed within |eta|<0.8, with the additional rapidity // cut of |y|<0.5" (verified 2026-08-11, quoted directly from the paper). - Configurable maxRapPID {"maxRapPID", 0.5, "Max |y| for PID-identified tracks (pion/kaon/proton), independent of maxEta"}; + Configurable maxRapPID{"maxRapPID", 0.5, "Max |y| for PID-identified tracks (pion/kaon/proton), independent of maxEta"}; // Reco-specific configurables - Configurable maxDcaXY {"maxDcaXY", 2.0, "Max DCA XY (cm) for reco tracks"}; - Configurable maxDcaZ {"maxDcaZ", 2.0, "Max DCA Z (cm) for reco tracks"}; + Configurable maxDcaXY{"maxDcaXY", 2.0, "Max DCA XY (cm) for reco tracks"}; + Configurable maxDcaZ{"maxDcaZ", 2.0, "Max DCA Z (cm) for reco tracks"}; // Combined TPC+TOF PID criterion per ALICE 2511.10399 Sec. 4: two // DIFFERENT thresholds, not one -- selection at 2 sigma, ambiguity // rejection at 3 sigma (verified directly against the paper text // 2026-08-11; an earlier draft of this instruction conflated the two // into a single 2-sigma threshold, corrected here). - Configurable maxNSigmaPID {"maxNSigmaPID", 2.0, "Max combined N_sigma,PID for species selection (ALICE 2511.10399)"}; + Configurable maxNSigmaPID{"maxNSigmaPID", 2.0, "Max combined N_sigma,PID for species selection (ALICE 2511.10399)"}; Configurable maxNSigmaPIDAmbig{"maxNSigmaPIDAmbig", 3.0, "N_sigma,PID threshold for ambiguity rejection (>1 species below this -> kUnidentified)"}; // Pre-2026-08-11 PID thresholds, restored (not reused for anything else) @@ -130,9 +136,9 @@ struct myExampleTask { // are kept at the current (non-legacy) values by the same decision: // gating relaxation was found to be a real purity-for-statistics // tradeoff, not a free win, and was declined. - Configurable useLegacyNSigmaGating {"useLegacyNSigmaGating", false, "false=pT-gated combined N_sigma,PID (2511.10399); true=independent TPC/TOF 3-sigma AND-gate, TOF always required when available (pre-2026-08-11)"}; - Configurable useLegacyPIDSelection {"useLegacyPIDSelection", false, "false=reject if >1 species below ambiguity threshold; true=pick minimum-chi2 species among those passing the gate, no rejection (pre-2026-08-11)"}; - Configurable applyRapidityCutPID {"applyRapidityCutPID", false, "false=skip the |y| useLegacyNSigmaGating{"useLegacyNSigmaGating", false, "false=pT-gated combined N_sigma,PID (2511.10399); true=independent TPC/TOF 3-sigma AND-gate, TOF always required when available (pre-2026-08-11)"}; + Configurable useLegacyPIDSelection{"useLegacyPIDSelection", false, "false=reject if >1 species below ambiguity threshold; true=pick minimum-chi2 species among those passing the gate, no rejection (pre-2026-08-11)"}; + Configurable applyRapidityCutPID{"applyRapidityCutPID", false, "false=skip the |y| pidIdx (-1 se non e' pion/kaon/proton) - static int pidSpeciesToIdx(PidSpecies pid) { - if (pid == kPion) return 0; - if (pid == kKaon) return 1; - if (pid == kProton) return 2; + static int pidSpeciesToIdx(PidSpecies pid) + { + if (pid == kPion) + return 0; + if (pid == kKaon) + return 1; + if (pid == kProton) + return 2; return -1; } @@ -223,7 +236,7 @@ struct myExampleTask { // pid 1-3 correspond to kPion=1, kKaon=2, kProton=3. // ---------------------------------------------------------------- static constexpr int nPidStudy = 4; // all + pion + kaon + proton - static constexpr const char* kPidStudyLabels[nPidStudy] = {"all","pion","kaon","proton"}; + static constexpr const char* kPidStudyLabels[nPidStudy] = {"all", "pion", "kaon", "proton"}; // pT (all selected tracks) × multBin — MC e Reco separati std::array, nPidStudy>, nSphClasses> hPt_sph_pid_MC{}; @@ -286,8 +299,6 @@ struct myExampleTask { // ---------------------------------------------------------------- std::shared_ptr hPidConfusion_MC{}; - - // ---------------------------------------------------------------- // Helpers // ---------------------------------------------------------------- @@ -295,10 +306,14 @@ struct myExampleTask { static PidSpecies pidFromPDG(int pdg) { switch (std::abs(pdg)) { - case 211: return kPion; - case 321: return kKaon; - case 2212: return kProton; - default: return kUnidentified; + case 211: + return kPion; + case 321: + return kKaon; + case 2212: + return kProton; + default: + return kUnidentified; } } @@ -333,8 +348,8 @@ struct myExampleTask { /// only comparing "old vs new" as one indivisible block. template PidSpecies pidFromNSigma(const T& track, double maxSel, double maxAmbig, - double maxTPC, double maxTOF, - bool legacyGating, bool legacySelection) const + double maxTPC, double maxTOF, + bool legacyGating, bool legacySelection) const { struct Hypo { PidSpecies pid; @@ -342,13 +357,12 @@ struct myExampleTask { double nsTOF; }; Hypo hypos[3] = { - {kPion, track.tpcNSigmaPi(), track.tofNSigmaPi()}, - {kKaon, track.tpcNSigmaKa(), track.tofNSigmaKa()}, - {kProton, track.tpcNSigmaPr(), track.tofNSigmaPr()} - }; + {kPion, track.tpcNSigmaPi(), track.tofNSigmaPi()}, + {kKaon, track.tpcNSigmaKa(), track.tofNSigmaKa()}, + {kProton, track.tpcNSigmaPr(), track.tofNSigmaPr()}}; // TOF unavailable -> nSigma returns -999; check with any species - bool hasTOF = (std::fabs(track.tofNSigmaPi()) < 100.f); + bool hasTOF = (std::fabs(track.tofNSigmaPi()) < 100.f); bool useCombined = (track.pt() > 0.5) && hasTOF; // only feeds the new-style gate auto passesGate = [&](const Hypo& h) { @@ -356,36 +370,43 @@ struct myExampleTask { return (std::fabs(h.nsTPC) < maxTPC) && (!hasTOF || std::fabs(h.nsTOF) < maxTOF); } double nsPID = useCombined - ? std::sqrt(h.nsTPC * h.nsTPC + h.nsTOF * h.nsTOF) - : std::fabs(h.nsTPC); + ? std::sqrt(h.nsTPC * h.nsTPC + h.nsTOF * h.nsTOF) + : std::fabs(h.nsTPC); return nsPID < maxSel; }; if (legacySelection) { // Best-fit (min chi2) among hypotheses passing the gate -- no // ambiguity rejection (pre-2026-08-11 style). - PidSpecies best = kUnidentified; - double bestChi2 = 1e9; + PidSpecies best = kUnidentified; + double bestChi2 = 1e9; for (const auto& h : hypos) { - if (!passesGate(h)) continue; + if (!passesGate(h)) + continue; double chi2 = h.nsTPC * h.nsTPC + (hasTOF ? h.nsTOF * h.nsTOF : 0.0); - if (chi2 < bestChi2) { bestChi2 = chi2; best = h.pid; } + if (chi2 < bestChi2) { + bestChi2 = chi2; + best = h.pid; + } } return best; } // Ambiguity-rejection selection (current default style). - int nBelowAmbig = 0; - PidSpecies selected = kUnidentified; + int nBelowAmbig = 0; + PidSpecies selected = kUnidentified; for (const auto& h : hypos) { - if (!passesGate(h)) continue; + if (!passesGate(h)) + continue; double nsPID = useCombined - ? std::sqrt(h.nsTPC * h.nsTPC + h.nsTOF * h.nsTOF) - : std::fabs(h.nsTPC); - if (nsPID < maxAmbig) ++nBelowAmbig; + ? std::sqrt(h.nsTPC * h.nsTPC + h.nsTOF * h.nsTOF) + : std::fabs(h.nsTPC); + if (nsPID < maxAmbig) + ++nBelowAmbig; selected = h.pid; } - if (nBelowAmbig > 1) return kUnidentified; // ambiguous, reject + if (nBelowAmbig > 1) + return kUnidentified; // ambiguous, reject return selected; } @@ -393,10 +414,14 @@ struct myExampleTask { static double pidMass(PidSpecies pid) { switch (pid) { - case kPion: return 0.13957; - case kKaon: return 0.49368; - case kProton: return 0.93827; - default: return 0.; + case kPion: + return 0.13957; + case kKaon: + return 0.49368; + case kProton: + return 0.93827; + default: + return 0.; } } @@ -405,46 +430,65 @@ struct myExampleTask { // when sph > 0, without needing a specific bin lookup. static int sphClass(double sph) { - if (sph <= 0.) return -1; - if (sph <= 0.3) return 0; - if (sph <= 0.6) return 1; - if (sph <= 1.0) return 2; + if (sph <= 0.) + return -1; + if (sph <= 0.3) + return 0; + if (sph <= 0.6) + return 1; + if (sph <= 1.0) + return 2; return -1; } static int zvtxBin(double z) { - if (z >= -10. && z < -5.) return 0; - if (z >= -5. && z < -2.5) return 1; - if (z >= -2.5 && z < 2.5) return 2; - if (z >= 2.5 && z < 5.) return 3; - if (z >= 5. && z <= 10.) return 4; + if (z >= -10. && z < -5.) + return 0; + if (z >= -5. && z < -2.5) + return 1; + if (z >= -2.5 && z < 2.5) + return 2; + if (z >= 2.5 && z < 5.) + return 3; + if (z >= 5. && z <= 10.) + return 4; return -1; } static int multBin(int n) { - if (n >= 1 && n <= 9) return 0; - if (n >= 10 && n <= 14) return 1; - if (n >= 15 && n <= 19) return 2; - if (n >= 20 && n <= 29) return 3; - if (n >= 30 && n <= 45) return 4; - if (n >= 46 && n <= 80) return 5; + if (n >= 1 && n <= 9) + return 0; + if (n >= 10 && n <= 14) + return 1; + if (n >= 15 && n <= 19) + return 2; + if (n >= 20 && n <= 29) + return 3; + if (n >= 30 && n <= 45) + return 4; + if (n >= 46 && n <= 80) + return 5; return -1; } static double computeDeltaPhi(double phi1, double phi2) { double dphi = phi1 - phi2; - while (dphi <= -TMath::Pi()) dphi += 2. * TMath::Pi(); - while (dphi > TMath::Pi()) dphi -= 2. * TMath::Pi(); - if (dphi <= -0.5 * TMath::Pi()) dphi += 2. * TMath::Pi(); + while (dphi <= -TMath::Pi()) + dphi += 2. * TMath::Pi(); + while (dphi > TMath::Pi()) + dphi -= 2. * TMath::Pi(); + if (dphi <= -0.5 * TMath::Pi()) + dphi += 2. * TMath::Pi(); return dphi; } static double computeSphericity(const std::vector& tracks) { - if (tracks.size() < 3) return -1.0; + if (tracks.size() < 3) + return -1.0; double Sxx = 0., Sxy = 0., Syy = 0.; for (const auto& t : tracks) { double px = t.pt * std::cos(t.phi); @@ -453,11 +497,13 @@ struct myExampleTask { Sxy += px * py; Syy += py * py; } - double tr = Sxx + Syy; + double tr = Sxx + Syy; double disc = tr * tr - 4.0 * (Sxx * Syy - Sxy * Sxy); - if (disc < 0.) disc = 0.; + if (disc < 0.) + disc = 0.; double lambdaMin = (tr - std::sqrt(disc)) / 2.0; - if (tr < 1e-12) return -1.0; + if (tr < 1e-12) + return -1.0; return std::clamp(2.0 * lambdaMin / tr, 0.0, 1.0); } @@ -466,10 +512,12 @@ struct myExampleTask { /// For unidentified particles (mass <= 0), returns eta. static double computeRapidity(double pt, double eta, double mass) { - if (mass <= 0.) return eta; // massless / unidentified -> y = eta + if (mass <= 0.) + return eta; // massless / unidentified -> y = eta double pz = pt * std::sinh(eta); - double E = std::sqrt(mass * mass + pt * pt + pz * pz); - if (E <= std::fabs(pz) + 1e-12) return eta; // safety + double E = std::sqrt(mass * mass + pt * pt + pz * pz); + if (E <= std::fabs(pz) + 1e-12) + return eta; // safety return 0.5 * std::log((E + pz) / (E - pz)); } @@ -478,22 +526,22 @@ struct myExampleTask { // ---------------------------------------------------------------- void init(InitContext const&) { - const AxisSpec axisZvtx {100, -15., 15., "z_{vtx} (cm)"}; - const AxisSpec axisMult {100, 0., 180., "N_{ch}"}; - const AxisSpec axisEta { 30, -1.5, 1.5, "#eta"}; - const AxisSpec axisPhi { 60, 0., 2.*TMath::Pi(),"#phi"}; - const AxisSpec axisDphi { 30, -0.5*TMath::Pi(), 1.5*TMath::Pi(), "#Delta#phi"}; - const AxisSpec axisDeta { 32, -1.6, 1.6, "#Delta#eta"}; + const AxisSpec axisZvtx{100, -15., 15., "z_{vtx} (cm)"}; + const AxisSpec axisMult{100, 0., 180., "N_{ch}"}; + const AxisSpec axisEta{30, -1.5, 1.5, "#eta"}; + const AxisSpec axisPhi{60, 0., 2. * TMath::Pi(), "#phi"}; + const AxisSpec axisDphi{30, -0.5 * TMath::Pi(), 1.5 * TMath::Pi(), "#Delta#phi"}; + const AxisSpec axisDeta{32, -1.6, 1.6, "#Delta#eta"}; // Reserved for a future rapidity-differentiated analysis -- unused // while the PID correlations run pseudorapidity-only (see // eventshape-pid-pseudorapidity-only auto-memory, 2026-09-15). - [[maybe_unused]] const AxisSpec axisDy { 32, -1.6, 1.6, "#Delta y"}; - const AxisSpec axisPt {nBinsPt, 0., 10., "p_{T} (GeV/c)"}; - const AxisSpec axisSph {100, 0., 1., "S_{T}"}; - const AxisSpec axisPt2 {nBinsPt2, 0., 5., "p_{T} (GeV/c)"}; - const AxisSpec axisMultBin{nMultBins, -0.5, (double)nMultBins - 0.5, "mult bin"}; - const AxisSpec axisSphCls {3, -0.5, 2.5, "sph class"}; - const AxisSpec axisMultCont{80, 0., 80., "N_{ch}"}; + [[maybe_unused]] const AxisSpec axisDy{32, -1.6, 1.6, "#Delta y"}; + const AxisSpec axisPt{nBinsPt, 0., 10., "p_{T} (GeV/c)"}; + const AxisSpec axisSph{100, 0., 1., "S_{T}"}; + const AxisSpec axisPt2{nBinsPt2, 0., 5., "p_{T} (GeV/c)"}; + const AxisSpec axisMultBin{nMultBins, -0.5, static_cast(nMultBins) - 0.5, "mult bin"}; + const AxisSpec axisSphCls{3, -0.5, 2.5, "sph class"}; + const AxisSpec axisMultCont{80, 0., 80., "N_{ch}"}; // ---------------------------------------------------------------- // Lambda comuna: creeaza un set complet de histograme pentru un @@ -501,25 +549,24 @@ struct myExampleTask { // Primeste referinte la toti membrii specifici canalului. // ---------------------------------------------------------------- auto bookChannel = [&](const char* suf, - std::array, nMultBins>, nSphClasses>& hSE_, - std::array, nMultBins>, nSphClasses>& hME_, - std::array, nMultBins>, nSphClasses>, nPidCorr>& hSEpid_, - std::array, nMultBins>, nSphClasses>, nPidCorr>& hMEpid_, - std::shared_ptr& hNtrig_, - std::array, nPidCorr>& hNtrigPID_, - std::array, nMultBins>& hSphMult_, - std::array, nPidStudy>, nSphClasses>& hPt_sph_pid_, - std::array, nPidStudy>, nSphClasses>& hPtLead_sph_pid_, - std::array, nPidStudy>, nSphClasses>& hPtAssoc_sph_pid_, - std::array, nPidStudy>& hSph_vs_mult_pid_, - std::array, nPidStudy>& hMult_vs_sph_pid_, - std::array, nPidStudy>& hSphTrack_vs_mult_pid_, - std::array, nPidStudy>& hMultTrack_vs_sph_pid_, - std::array, nPidStudy>& hSphLead_vs_mult_pid_, - std::array, nPidStudy>& hMultLead_vs_sph_pid_, - std::array, nPidStudy>& hMultReal_vs_sph_pid_, - std::shared_ptr& hMultReal_vs_pid_) - { + std::array, nMultBins>, nSphClasses>& hSE_, + std::array, nMultBins>, nSphClasses>& hME_, + std::array, nMultBins>, nSphClasses>, nPidCorr>& hSEpid_, + std::array, nMultBins>, nSphClasses>, nPidCorr>& hMEpid_, + std::shared_ptr& hNtrig_, + std::array, nPidCorr>& hNtrigPID_, + std::array, nMultBins>& hSphMult_, + std::array, nPidStudy>, nSphClasses>& hPt_sph_pid_, + std::array, nPidStudy>, nSphClasses>& hPtLead_sph_pid_, + std::array, nPidStudy>, nSphClasses>& hPtAssoc_sph_pid_, + std::array, nPidStudy>& hSph_vs_mult_pid_, + std::array, nPidStudy>& hMult_vs_sph_pid_, + std::array, nPidStudy>& hSphTrack_vs_mult_pid_, + std::array, nPidStudy>& hMultTrack_vs_sph_pid_, + std::array, nPidStudy>& hSphLead_vs_mult_pid_, + std::array, nPidStudy>& hMultLead_vs_sph_pid_, + std::array, nPidStudy>& hMultReal_vs_sph_pid_, + std::shared_ptr& hMultReal_vs_pid_) { // QA // evSel bins: 1=read 2=zvtx 3=multBin 4=zvtxBin 5=leadPt 6=ST_valid 7=sphClass 8=SE_filled auto hQA = histos.add(Form("evSel_%s", suf), Form("Event selection (%s)", suf), HistType::kTH1D, {{8, 0.5, 8.5}}); @@ -532,15 +579,15 @@ struct myExampleTask { hQA->GetXaxis()->SetBinLabel(7, "sphClass OK"); hQA->GetXaxis()->SetBinLabel(8, "SE filled"); - histos.add(Form("Zvertex_%s", suf), Form("z_{vtx} (%s);z_{vtx} (cm);events", suf), kTH1D, {axisZvtx}); - histos.add(Form("multHist_%s", suf), Form("N_{ch} (%s);N_{ch};events", suf), kTH1D, {axisMult}); - histos.add(Form("etaHistogram_%s", suf), Form("#eta (%s);#eta;tracks", suf), kTH1D, {axisEta}); - histos.add(Form("phiHistogram_%s", suf), Form("#phi (%s);#phi;tracks", suf), kTH1D, {axisPhi}); - histos.add(Form("ptHistogram_%s", suf), Form("p_{T} (%s);p_{T} (GeV/c);tracks", suf), kTH1D, {axisPt}); - histos.add(Form("ptLeadHistogram_%s", suf), Form("p_{T} lead (%s);p_{T} (GeV/c);events", suf), kTH1D, {axisPt}); - histos.add(Form("ptAssocHistogram_%s", suf),Form("p_{T} assoc (%s);p_{T} (GeV/c);pairs", suf), kTH1D, {axisPt}); - histos.add(Form("sphericity_%s", suf), Form("S_{T} (inclusive, %s);S_{T};events", suf), kTH1D, {axisSph}); - histos.add(Form("sphericity_beforeCuts_%s", suf),Form("S_{T} (all evts with >=3 tracks, %s);S_{T};events", suf), kTH1D, {axisSph}); + histos.add(Form("Zvertex_%s", suf), Form("z_{vtx} (%s);z_{vtx} (cm);events", suf), kTH1D, {axisZvtx}); + histos.add(Form("multHist_%s", suf), Form("N_{ch} (%s);N_{ch};events", suf), kTH1D, {axisMult}); + histos.add(Form("etaHistogram_%s", suf), Form("#eta (%s);#eta;tracks", suf), kTH1D, {axisEta}); + histos.add(Form("phiHistogram_%s", suf), Form("#phi (%s);#phi;tracks", suf), kTH1D, {axisPhi}); + histos.add(Form("ptHistogram_%s", suf), Form("p_{T} (%s);p_{T} (GeV/c);tracks", suf), kTH1D, {axisPt}); + histos.add(Form("ptLeadHistogram_%s", suf), Form("p_{T} lead (%s);p_{T} (GeV/c);events", suf), kTH1D, {axisPt}); + histos.add(Form("ptAssocHistogram_%s", suf), Form("p_{T} assoc (%s);p_{T} (GeV/c);pairs", suf), kTH1D, {axisPt}); + histos.add(Form("sphericity_%s", suf), Form("S_{T} (inclusive, %s);S_{T};events", suf), kTH1D, {axisSph}); + histos.add(Form("sphericity_beforeCuts_%s", suf), Form("S_{T} (all evts with >=3 tracks, %s);S_{T};events", suf), kTH1D, {axisSph}); // Per-mult sphericity for (int im = 0; im < nMultBins; ++im) { @@ -555,10 +602,12 @@ struct myExampleTask { Form("hNtrig_%s", suf), Form("N_{trig} (%s);mult bin;sph class", suf), HistType::kTH2D, - {{nMultBins, -0.5, (double)nMultBins - 0.5}, - {nSphClasses, -0.5, (double)nSphClasses - 0.5}}); - for (int im = 0; im < nMultBins; ++im) hNtrig_->GetXaxis()->SetBinLabel(im+1, kMultLabels[im]); - for (int ic = 0; ic < nSphClasses; ++ic) hNtrig_->GetYaxis()->SetBinLabel(ic+1, kSphLabels[ic]); + {{nMultBins, -0.5, static_cast(nMultBins) - 0.5}, + {nSphClasses, -0.5, static_cast(nSphClasses) - 0.5}}); + for (int im = 0; im < nMultBins; ++im) + hNtrig_->GetXaxis()->SetBinLabel(im + 1, kMultLabels[im]); + for (int ic = 0; ic < nSphClasses; ++ic) + hNtrig_->GetYaxis()->SetBinLabel(ic + 1, kSphLabels[ic]); // SE/ME TH2D: [sphClass][multBin] for (int ic = 0; ic < nSphClasses; ++ic) { @@ -602,12 +651,12 @@ struct myExampleTask { Form("hNtrig_%s_pid%d", suf, ip), Form("N_{trig} %s (%s);mult bin;sph class", kPidCorrNames[ip], suf), HistType::kTH2D, - {{nMultBins, -0.5, (double)nMultBins - 0.5}, - {nSphClasses, -0.5, (double)nSphClasses - 0.5}}); - for (int im = 0; im < nMultBins; ++im) - hNtrigPID_[ip]->GetXaxis()->SetBinLabel(im+1, kMultLabels[im]); + {{nMultBins, -0.5, static_cast(nMultBins) - 0.5}, + {nSphClasses, -0.5, static_cast(nSphClasses) - 0.5}}); + for (int im = 0; im < nMultBins; ++im) + hNtrigPID_[ip]->GetXaxis()->SetBinLabel(im + 1, kMultLabels[im]); for (int ic = 0; ic < nSphClasses; ++ic) - hNtrigPID_[ip]->GetYaxis()->SetBinLabel(ic+1, kSphLabels[ic]); + hNtrigPID_[ip]->GetYaxis()->SetBinLabel(ic + 1, kSphLabels[ic]); } // ---------------------------------------------------------------- @@ -732,7 +781,7 @@ struct myExampleTask { Form("N_ch real vs PID (%s);N_{ch};PID", suf), HistType::kTH2D, {axisMultCont, - {(int)nPidStudy, -0.5, (double)nPidStudy - 0.5}}); + {static_cast(nPidStudy), -0.5, static_cast(nPidStudy) - 0.5}}); for (int ip = 0; ip < nPidStudy; ++ip) hMultReal_vs_pid_->GetYaxis()->SetBinLabel(ip + 1, kPidStudyLabels[ip]); #endif // [DISABLED — histogram budget] categories 1-7 @@ -740,23 +789,23 @@ struct myExampleTask { // Book histograms for MC (generated) channel bookChannel("MC", - hSE_MC, hME_MC, hSEpid_MC, hMEpid_MC, - hNtrig_MC, hNtrigPID_MC, hSphMult_MC, - hPt_sph_pid_MC, hPtLead_sph_pid_MC, hPtAssoc_sph_pid_MC, - hSph_vs_mult_pid_MC, hMult_vs_sph_pid_MC, - hSphTrack_vs_mult_pid_MC, hMultTrack_vs_sph_pid_MC, - hSphLead_vs_mult_pid_MC, hMultLead_vs_sph_pid_MC, - hMultReal_vs_sph_pid_MC, hMultReal_vs_pid_MC); + hSE_MC, hME_MC, hSEpid_MC, hMEpid_MC, + hNtrig_MC, hNtrigPID_MC, hSphMult_MC, + hPt_sph_pid_MC, hPtLead_sph_pid_MC, hPtAssoc_sph_pid_MC, + hSph_vs_mult_pid_MC, hMult_vs_sph_pid_MC, + hSphTrack_vs_mult_pid_MC, hMultTrack_vs_sph_pid_MC, + hSphLead_vs_mult_pid_MC, hMultLead_vs_sph_pid_MC, + hMultReal_vs_sph_pid_MC, hMultReal_vs_pid_MC); // Book histograms for Reco (reconstructed) channel bookChannel("Reco", - hSE_Reco, hME_Reco, hSEpid_Reco, hMEpid_Reco, - hNtrig_Reco, hNtrigPID_Reco, hSphMult_Reco, - hPt_sph_pid_Reco, hPtLead_sph_pid_Reco, hPtAssoc_sph_pid_Reco, - hSph_vs_mult_pid_Reco, hMult_vs_sph_pid_Reco, - hSphTrack_vs_mult_pid_Reco, hMultTrack_vs_sph_pid_Reco, - hSphLead_vs_mult_pid_Reco, hMultLead_vs_sph_pid_Reco, - hMultReal_vs_sph_pid_Reco, hMultReal_vs_pid_Reco); + hSE_Reco, hME_Reco, hSEpid_Reco, hMEpid_Reco, + hNtrig_Reco, hNtrigPID_Reco, hSphMult_Reco, + hPt_sph_pid_Reco, hPtLead_sph_pid_Reco, hPtAssoc_sph_pid_Reco, + hSph_vs_mult_pid_Reco, hMult_vs_sph_pid_Reco, + hSphTrack_vs_mult_pid_Reco, hMultTrack_vs_sph_pid_Reco, + hSphLead_vs_mult_pid_Reco, hMultLead_vs_sph_pid_Reco, + hMultReal_vs_sph_pid_Reco, hMultReal_vs_pid_Reco); // [DEBUG, TEMPORARY] track-cut diagnostic counter for processReco hTrackCutDebug_Reco = histos.add( @@ -773,46 +822,50 @@ struct myExampleTask { // PID confusion matrix (rapid contamination estimate, 2026-08-14) -- // axes: pT, true species (PDG), tagged species (pidFromNSigma). // Species axis order matches PidSpecies: 0=unid,1=pion,2=kaon,3=proton. - const AxisSpec axisPidSpecies{(int)kNPidSpecies, -0.5, (double)kNPidSpecies - 0.5, "species"}; + const AxisSpec axisPidSpecies{static_cast(kNPidSpecies), -0.5, static_cast(kNPidSpecies) - 0.5, "species"}; hPidConfusion_MC = histos.add( "hPidConfusion_MC", "True vs. tagged PID species (MC);p_{T} (GeV/c);true species;tagged species", HistType::kTH3D, {axisPt2, axisPidSpecies, axisPidSpecies}); - for (int ip = 0; ip < (int)kNPidSpecies; ++ip) { + for (int ip = 0; ip < static_cast(kNPidSpecies); ++ip) { hPidConfusion_MC->GetYaxis()->SetBinLabel(ip + 1, kPidNames[ip]); hPidConfusion_MC->GetZaxis()->SetBinLabel(ip + 1, kPidNames[ip]); } } - // ---------------------------------------------------------------- // processMC // ---------------------------------------------------------------- void processMC(aod::McCollision const& mcCollision, - aod::McParticles const& mcParticles) + aod::McParticles const& mcParticles) { histos.fill(HIST("evSel_MC"), 1); const double zvtx = mcCollision.posZ(); - if (std::fabs(zvtx) > maxZvtx) return; + if (std::fabs(zvtx) > maxZvtx) + return; histos.fill(HIST("evSel_MC"), 2); histos.fill(HIST("Zvertex_MC"), zvtx); // ---- one-pass: build selTracks + find leading --------------- std::vector selTracks; selTracks.reserve(64); - int leadIdx = -1; - double pTlead = -1., phiLead = 0., etaLead = 0., yLead = 0.; + int leadIdx = -1; + double pTlead = -1., phiLead = 0., etaLead = 0., yLead = 0.; for (const auto& p : mcParticles) { - if (!p.isPhysicalPrimary()) continue; + if (!p.isPhysicalPrimary()) + continue; auto* pdgPtr = TDatabasePDG::Instance()->GetParticle(p.pdgCode()); - if (!pdgPtr || std::fabs(pdgPtr->Charge()) < 0.1) continue; - if (std::fabs(p.eta()) > maxEta) continue; - if (p.pt() < minAssocPt) continue; + if (!pdgPtr || std::fabs(pdgPtr->Charge()) < 0.1) + continue; + if (std::fabs(p.eta()) > maxEta) + continue; + if (p.pt() < minAssocPt) + continue; double mass = pdgPtr->Mass(); - double rap = computeRapidity(p.pt(), p.eta(), mass); + double rap = computeRapidity(p.pt(), p.eta(), mass); // |y| < maxRapPID cut applies ONLY to PID-identified species // (pion/kaon/proton) -- the all-species channel (hSE/hME) keeps the @@ -825,58 +878,65 @@ struct myExampleTask { pdgForPID = 0; } - int idx = (int)selTracks.size(); - selTracks.push_back({(float)p.phi(), (float)p.eta(), (float)p.pt(), - (float)rap, pdgForPID}); + int idx = static_cast(selTracks.size()); + selTracks.push_back({static_cast(p.phi()), static_cast(p.eta()), static_cast(p.pt()), + static_cast(rap), pdgForPID}); histos.fill(HIST("etaHistogram_MC"), p.eta()); histos.fill(HIST("phiHistogram_MC"), p.phi()); - histos.fill(HIST("ptHistogram_MC"), p.pt()); + histos.fill(HIST("ptHistogram_MC"), p.pt()); if (p.pt() > pTlead) { - pTlead = p.pt(); + pTlead = p.pt(); phiLead = p.phi(); etaLead = p.eta(); - yLead = rap; + yLead = rap; leadIdx = idx; } } - const int nch = (int)selTracks.size(); + const int nch = static_cast(selTracks.size()); histos.fill(HIST("multHist_MC"), nch); const int mBin = multBin(nch); - if (mBin < 0) return; + if (mBin < 0) + return; histos.fill(HIST("evSel_MC"), 3); const int zBin = zvtxBin(zvtx); - if (zBin < 0) return; + if (zBin < 0) + return; histos.fill(HIST("evSel_MC"), 4); // ---- leading particle pT cut [minLeadPt, maxLeadPt] ----------- - if (leadIdx < 0) return; - if (pTlead < minLeadPt || pTlead > maxLeadPt) return; + if (leadIdx < 0) + return; + if (pTlead < minLeadPt || pTlead > maxLeadPt) + return; histos.fill(HIST("evSel_MC"), 5); histos.fill(HIST("ptLeadHistogram_MC"), pTlead); // ---- sphericity --------------------------------------------- const double sph = computeSphericity(selTracks); // Fill sphericity_beforeCuts here — no additional cuts applied yet - if (sph >= 0.) histos.fill(HIST("sphericity_beforeCuts_MC"), sph); - if (sph < 0.) return; // fewer than 3 tracks + if (sph >= 0.) + histos.fill(HIST("sphericity_beforeCuts_MC"), sph); + if (sph < 0.) + return; // fewer than 3 tracks histos.fill(HIST("evSel_MC"), 6); histos.fill(HIST("sphericity_MC"), sph); hSphMult_MC[mBin]->Fill(sph); const int sphCls = sphClass(sph); // 0,1,2 or -1 - if (sphCls < 0) return; + if (sphCls < 0) + return; histos.fill(HIST("evSel_MC"), 7); const PidSpecies pidLead = pidFromPDG(selTracks[leadIdx].pdg); // ---- trigger counter ---------------------------------------- - hNtrig_MC->Fill((double)mBin, (double)sphCls); - hNtrig_MC->Fill((double)mBin, (double)(nSphClasses - 1)); // "all" + hNtrig_MC->Fill(static_cast(mBin), static_cast(sphCls)); + hNtrig_MC->Fill(static_cast(mBin), static_cast((nSphClasses - 1))); // "all" // ================================================================ // [DISABLED — histogram budget] Study histograms: S_T vs mult, @@ -891,12 +951,12 @@ struct myExampleTask { auto fillPtStudy = [&](std::array, nPidStudy>, nSphClasses>& arr, int iSphCls, int im, double pt, PidSpecies pid) { // fill "all" (pid index 0) for both specific sph and "all" sph - arr[iSphCls][0]->Fill(pt, (double)im); - arr[nSphClasses - 1][0]->Fill(pt, (double)im); - // fill specific PID (pid index = (int)pid, which is 1-3 for pion/kaon/proton, 0=unid stays in "all" only) - if ((int)pid > 0 && (int)pid < nPidStudy) { - arr[iSphCls][(int)pid]->Fill(pt, (double)im); - arr[nSphClasses - 1][(int)pid]->Fill(pt, (double)im); + arr[iSphCls][0]->Fill(pt, static_cast(im)); + arr[nSphClasses - 1][0]->Fill(pt, static_cast(im)); + // fill specific PID (pid index = static_cast(pid), which is 1-3 for pion/kaon/proton, 0=unid stays in "all" only) + if (static_cast(pid) > 0 && static_cast(pid) < nPidStudy) { + arr[iSphCls][static_cast(pid)]->Fill(pt, static_cast(im)); + arr[nSphClasses - 1][static_cast(pid)]->Fill(pt, static_cast(im)); } }; @@ -905,68 +965,68 @@ struct myExampleTask { for (const auto& t : selTracks) { PidSpecies pid = pidFromPDG(t.pdg); nTrackPid[0]++; - if ((int)pid > 0 && (int)pid < nPidStudy) - nTrackPid[(int)pid]++; + if (static_cast(pid) > 0 && static_cast(pid) < nPidStudy) + nTrackPid[static_cast(pid)]++; } // Per eveniment (cel putin o particula din specie) — histogramele originale - hSph_vs_mult_pid_MC[0]->Fill(sph, (double)mBin); - hMult_vs_sph_pid_MC[0]->Fill((double)nch, (double)sphCls); + hSph_vs_mult_pid_MC[0]->Fill(sph, static_cast(mBin)); + hMult_vs_sph_pid_MC[0]->Fill(static_cast(nch), static_cast(sphCls)); for (int ip = 1; ip < nPidStudy; ++ip) { if (nTrackPid[ip] > 0) { - hSph_vs_mult_pid_MC[ip]->Fill(sph, (double)mBin); - hMult_vs_sph_pid_MC[ip]->Fill((double)nch, (double)sphCls); + hSph_vs_mult_pid_MC[ip]->Fill(sph, static_cast(mBin)); + hMult_vs_sph_pid_MC[ip]->Fill(static_cast(nch), static_cast(sphCls)); } } // Varianta B: per eveniment, PID leading - hSphLead_vs_mult_pid_MC[0]->Fill(sph, (double)mBin); // all: sempre - hMultLead_vs_sph_pid_MC[0]->Fill((double)nch, (double)sphCls); - if ((int)pidLead > 0 && (int)pidLead < nPidStudy) { - hSphLead_vs_mult_pid_MC[(int)pidLead]->Fill(sph, (double)mBin); - hMultLead_vs_sph_pid_MC[(int)pidLead]->Fill((double)nch, (double)sphCls); + hSphLead_vs_mult_pid_MC[0]->Fill(sph, static_cast(mBin)); // all: sempre + hMultLead_vs_sph_pid_MC[0]->Fill(static_cast(nch), static_cast(sphCls)); + if (static_cast(pidLead) > 0 && static_cast(pidLead) < nPidStudy) { + hSphLead_vs_mult_pid_MC[static_cast(pidLead)]->Fill(sph, static_cast(mBin)); + hMultLead_vs_sph_pid_MC[static_cast(pidLead)]->Fill(static_cast(nch), static_cast(sphCls)); } // Distributia N_ch reala per sphClass, per PID leading - hMultReal_vs_sph_pid_MC[0]->Fill((double)nch, (double)sphCls); - if ((int)pidLead > 0 && (int)pidLead < nPidStudy) - hMultReal_vs_sph_pid_MC[(int)pidLead]->Fill((double)nch, (double)sphCls); + hMultReal_vs_sph_pid_MC[0]->Fill(static_cast(nch), static_cast(sphCls)); + if (static_cast(pidLead) > 0 && static_cast(pidLead) < nPidStudy) + hMultReal_vs_sph_pid_MC[static_cast(pidLead)]->Fill(static_cast(nch), static_cast(sphCls)); // N_ch reala vs PID (per track — umplut o data per track) // Umplut in bucla de trackuri de mai jos // (hMultReal_vs_pid_MC se umple per eveniment cu PID dominant) // Alegem: umplut per eveniment cu fiecare PID care exista in eveniment - hMultReal_vs_pid_MC->Fill((double)nch, 0.); // all: intotdeauna + hMultReal_vs_pid_MC->Fill(static_cast(nch), 0.); // all: intotdeauna for (int ip = 1; ip < nPidStudy; ++ip) { if (nTrackPid[ip] > 0) - hMultReal_vs_pid_MC->Fill((double)nch, (double)ip); + hMultReal_vs_pid_MC->Fill(static_cast(nch), static_cast(ip)); } // Varianta A: per TRACK — umplut in bucla de trackuri for (const auto& t : selTracks) { PidSpecies pid = pidFromPDG(t.pdg); // all tracks - hSphTrack_vs_mult_pid_MC[0]->Fill(sph, (double)mBin); - hMultTrack_vs_sph_pid_MC[0]->Fill((double)nch, (double)sphCls); + hSphTrack_vs_mult_pid_MC[0]->Fill(sph, static_cast(mBin)); + hMultTrack_vs_sph_pid_MC[0]->Fill(static_cast(nch), static_cast(sphCls)); // specific PID - if ((int)pid > 0 && (int)pid < nPidStudy) { - hSphTrack_vs_mult_pid_MC[(int)pid]->Fill(sph, (double)mBin); - hMultTrack_vs_sph_pid_MC[(int)pid]->Fill((double)nch, (double)sphCls); + if (static_cast(pid) > 0 && static_cast(pid) < nPidStudy) { + hSphTrack_vs_mult_pid_MC[static_cast(pid)]->Fill(sph, static_cast(mBin)); + hMultTrack_vs_sph_pid_MC[static_cast(pid)]->Fill(static_cast(nch), static_cast(sphCls)); } } // ---- pT leading per (sph, PID) ------------------------------ - hPtLead_sph_pid_MC[sphCls][0]->Fill(pTlead, (double)mBin); - hPtLead_sph_pid_MC[nSphClasses - 1][0]->Fill(pTlead, (double)mBin); - if ((int)pidLead > 0 && (int)pidLead < nPidStudy) { - hPtLead_sph_pid_MC[sphCls][(int)pidLead]->Fill(pTlead, (double)mBin); - hPtLead_sph_pid_MC[nSphClasses - 1][(int)pidLead]->Fill(pTlead, (double)mBin); + hPtLead_sph_pid_MC[sphCls][0]->Fill(pTlead, static_cast(mBin)); + hPtLead_sph_pid_MC[nSphClasses - 1][0]->Fill(pTlead, static_cast(mBin)); + if (static_cast(pidLead) > 0 && static_cast(pidLead) < nPidStudy) { + hPtLead_sph_pid_MC[sphCls][static_cast(pidLead)]->Fill(pTlead, static_cast(mBin)); + hPtLead_sph_pid_MC[nSphClasses - 1][static_cast(pidLead)]->Fill(pTlead, static_cast(mBin)); } // ---- pT tuturor trackurilor per (sph, PID) ------------------ for (const auto& t : selTracks) { PidSpecies pid = pidFromPDG(t.pdg); - fillPtStudy(hPt_sph_pid_MC, sphCls, mBin, (double)t.pt, pid); + fillPtStudy(hPt_sph_pid_MC, sphCls, mBin, static_cast(t.pt), pid); } #endif // [DISABLED — histogram budget] categories 1,2,3,5,6,7 // ================================================================ @@ -974,10 +1034,13 @@ struct myExampleTask { // ================================================================ bool seHasPairs = false; for (int ia = 0; ia < nch; ++ia) { - if (ia == leadIdx) continue; + if (ia == leadIdx) + continue; const auto& assoc = selTracks[ia]; - if (assoc.pt >= pTlead) continue; // asociat pT < leading pT - if (assoc.pt < minAssocCorr || assoc.pt > maxAssocCorr) continue; + if (assoc.pt >= pTlead) + continue; // asociat pT < leading pT + if (assoc.pt < minAssocCorr || assoc.pt > maxAssocCorr) + continue; histos.fill(HIST("ptAssocHistogram_MC"), assoc.pt); @@ -985,7 +1048,7 @@ struct myExampleTask { // pT associated study histograms [DISABLED — histogram budget, category 4] #if 0 - fillPtStudy(hPtAssoc_sph_pid_MC, sphCls, mBin, (double)assoc.pt, pidAssoc); + fillPtStudy(hPtAssoc_sph_pid_MC, sphCls, mBin, static_cast(assoc.pt), pidAssoc); #endif const double dphi = computeDeltaPhi(phiLead, assoc.phi); const double deta = etaLead - assoc.eta; @@ -1007,13 +1070,14 @@ struct myExampleTask { seHasPairs = true; } - if (seHasPairs) histos.fill(HIST("evSel_MC"), 8); + if (seHasPairs) + histos.fill(HIST("evSel_MC"), 8); // ---- hNtrig_MC per specie PID leading -------------------------- int pidLeadIdx = pidSpeciesToIdx(pidLead); if (pidLeadIdx >= 0) { - hNtrigPID_MC[pidLeadIdx]->Fill((double)mBin, (double)sphCls); - hNtrigPID_MC[pidLeadIdx]->Fill((double)mBin, (double)(nSphClasses - 1)); + hNtrigPID_MC[pidLeadIdx]->Fill(static_cast(mBin), static_cast(sphCls)); + hNtrigPID_MC[pidLeadIdx]->Fill(static_cast(mBin), static_cast((nSphClasses - 1))); } // ================================================================ @@ -1023,13 +1087,15 @@ struct myExampleTask { { auto& pool = eventPools_MC[sphCls][zBin][mBin]; pool.push_back(selTracks); - if ((int)pool.size() > mixPoolSize) pool.pop_front(); + if (static_cast(pool.size()) > mixPoolSize) + pool.pop_front(); } // Pool "all" — all sph { auto& poolAll = eventPools_MC[nSphClasses - 1][zBin][mBin]; poolAll.push_back(selTracks); - if ((int)poolAll.size() > mixPoolSize) poolAll.pop_front(); + if (static_cast(poolAll.size()) > mixPoolSize) + poolAll.pop_front(); } // Pool per specie PID — contiene solo i track di quella specie @@ -1040,18 +1106,21 @@ struct myExampleTask { if (pidSpeciesToIdx(pidFromPDG(t.pdg)) == ip) pidTracks.push_back(t); } - if (pidTracks.empty()) continue; + if (pidTracks.empty()) + continue; // Specific sph pool { auto& pool = eventPoolsPID_MC[ip][sphCls][zBin][mBin]; pool.push_back(pidTracks); - if ((int)pool.size() > mixPoolSize) pool.pop_front(); + if (static_cast(pool.size()) > mixPoolSize) + pool.pop_front(); } // "all" sph pool { auto& pool = eventPoolsPID_MC[ip][nSphClasses - 1][zBin][mBin]; pool.push_back(pidTracks); - if ((int)pool.size() > mixPoolSize) pool.pop_front(); + if (static_cast(pool.size()) > mixPoolSize) + pool.pop_front(); } } @@ -1059,12 +1128,14 @@ struct myExampleTask { // MIXED-EVENT "all" // ================================================================ auto doMixing = [&](int poolIdx) { - const auto& pool = eventPools_MC[poolIdx][zBin][mBin]; - const int nEvts = (int)pool.size() - 1; + const auto& pool = eventPools_MC[poolIdx][zBin][mBin]; + const int nEvts = static_cast(pool.size()) - 1; for (int ie = 0; ie < nEvts; ++ie) { for (const auto& tr : pool[ie]) { - if (tr.pt >= pTlead) continue; - if (tr.pt < minAssocCorr || tr.pt > maxAssocCorr) continue; + if (tr.pt >= pTlead) + continue; + if (tr.pt < minAssocCorr || tr.pt > maxAssocCorr) + continue; const double dphi = computeDeltaPhi(phiLead, tr.phi); const double deta = etaLead - tr.eta; hME_MC[poolIdx][mBin]->Fill(dphi, deta); @@ -1084,12 +1155,14 @@ struct myExampleTask { // ================================================================ if (pidLeadIdx >= 0) { auto doMixingPID = [&](int poolIdx) { - const auto& pool = eventPoolsPID_MC[pidLeadIdx][poolIdx][zBin][mBin]; - const int nEvts = (int)pool.size() - 1; + const auto& pool = eventPoolsPID_MC[pidLeadIdx][poolIdx][zBin][mBin]; + const int nEvts = static_cast(pool.size()) - 1; for (int ie = 0; ie < nEvts; ++ie) { for (const auto& tr : pool[ie]) { - if (tr.pt >= pTlead) continue; - if (tr.pt < minAssocCorr || tr.pt > maxAssocCorr) continue; + if (tr.pt >= pTlead) + continue; + if (tr.pt < minAssocCorr || tr.pt > maxAssocCorr) + continue; const double dphi = computeDeltaPhi(phiLead, tr.phi); const double detaPID = etaLead - tr.eta; hMEpid_MC[pidLeadIdx][poolIdx][mBin]->Fill(dphi, detaPID); @@ -1099,62 +1172,68 @@ struct myExampleTask { doMixingPID(sphCls); doMixingPID(nSphClasses - 1); } - } PROCESS_SWITCH(myExampleTask, processMC, "Process MC truth", true); // ================================================================ // processReco — reconstructed data (Collision + Tracks) // ================================================================ - using ColReco = soa::Join; + using ColReco = soa::Join; using TrackReco = soa::Join; void processReco(ColReco::iterator const& col, - TrackReco const& tracks) + TrackReco const& tracks) { histos.fill(HIST("evSel_Reco"), 1); // ---- event selection ---- - if (!col.sel8()) return; + if (!col.sel8()) + return; const double zvtx = col.posZ(); - if (std::fabs(zvtx) > maxZvtx) return; + if (std::fabs(zvtx) > maxZvtx) + return; histos.fill(HIST("evSel_Reco"), 2); histos.fill(HIST("Zvertex_Reco"), zvtx); // ---- one-pass: build selTracks + find leading --------------- std::vector selTracks; selTracks.reserve(64); - int leadIdx = -1; - double pTlead = -1., phiLead = 0., etaLead = 0., yLead = 0.; + int leadIdx = -1; + double pTlead = -1., phiLead = 0., etaLead = 0., yLead = 0.; for (const auto& track : tracks) { hTrackCutDebug_Reco->Fill(1); // seen // Quality selection: global track without DCA (apply DCA separately) - if (!track.isGlobalTrackWoDCA()) continue; + if (!track.isGlobalTrackWoDCA()) + continue; hTrackCutDebug_Reco->Fill(2); // passed isGlobalTrackWoDCA // DCA cuts - if (std::fabs(track.dcaXY()) > maxDcaXY) continue; + if (std::fabs(track.dcaXY()) > maxDcaXY) + continue; hTrackCutDebug_Reco->Fill(3); // passed DCAxy - if (std::fabs(track.dcaZ()) > maxDcaZ) continue; + if (std::fabs(track.dcaZ()) > maxDcaZ) + continue; hTrackCutDebug_Reco->Fill(4); // passed DCAz // Kinematic cuts (same as MC) - if (std::fabs(track.eta()) > maxEta) continue; + if (std::fabs(track.eta()) > maxEta) + continue; hTrackCutDebug_Reco->Fill(5); // passed eta - if (track.pt() < minAssocPt) continue; + if (track.pt() < minAssocPt) + continue; hTrackCutDebug_Reco->Fill(6); // passed pt (selected) // PID from nSigma (combined TPC+TOF, ALICE 2511.10399, unless the // legacy flags below switch to pre-2026-08-11 gating/selection) PidSpecies pid = pidFromNSigma(track, maxNSigmaPID, maxNSigmaPIDAmbig, - maxNSigmaTPC, maxNSigmaTOF, - useLegacyNSigmaGating, useLegacyPIDSelection); + maxNSigmaTPC, maxNSigmaTOF, + useLegacyNSigmaGating, useLegacyPIDSelection); double mass = pidMass(pid); - double rap = computeRapidity(track.pt(), track.eta(), mass); + double rap = computeRapidity(track.pt(), track.eta(), mass); // |y| < maxRapPID cut applies ONLY to PID-identified species -- see // matching comment in processMC. Track stays in selTracks either way @@ -1164,64 +1243,74 @@ struct myExampleTask { } // pdg = 0 for reco (no MC truth); store sign for future like/unlike-sign - int idx = (int)selTracks.size(); - selTracks.push_back({(float)track.phi(), (float)track.eta(), (float)track.pt(), - (float)rap, 0}); + int idx = static_cast(selTracks.size()); + selTracks.push_back({static_cast(track.phi()), static_cast(track.eta()), static_cast(track.pt()), + static_cast(rap), 0}); // Store PID info in pdg field as signed pidIdx for later use: // sign(track.sign()) * (211 for pion, 321 for kaon, 2212 for proton, 0 for unid) int pdgLike = 0; - if (pid == kPion) pdgLike = 211; - if (pid == kKaon) pdgLike = 321; - if (pid == kProton) pdgLike = 2212; + if (pid == kPion) + pdgLike = 211; + if (pid == kKaon) + pdgLike = 321; + if (pid == kProton) + pdgLike = 2212; selTracks.back().pdg = track.sign() * pdgLike; histos.fill(HIST("etaHistogram_Reco"), track.eta()); histos.fill(HIST("phiHistogram_Reco"), track.phi()); - histos.fill(HIST("ptHistogram_Reco"), track.pt()); + histos.fill(HIST("ptHistogram_Reco"), track.pt()); if (track.pt() > pTlead) { - pTlead = track.pt(); + pTlead = track.pt(); phiLead = track.phi(); etaLead = track.eta(); - yLead = rap; + yLead = rap; leadIdx = idx; } } - const int nch = (int)selTracks.size(); + const int nch = static_cast(selTracks.size()); histos.fill(HIST("multHist_Reco"), nch); const int mBin = multBin(nch); - if (mBin < 0) return; + if (mBin < 0) + return; histos.fill(HIST("evSel_Reco"), 3); const int zBin = zvtxBin(zvtx); - if (zBin < 0) return; + if (zBin < 0) + return; histos.fill(HIST("evSel_Reco"), 4); // ---- leading particle pT cut [minLeadPt, maxLeadPt] ----------- - if (leadIdx < 0) return; - if (pTlead < minLeadPt || pTlead > maxLeadPt) return; + if (leadIdx < 0) + return; + if (pTlead < minLeadPt || pTlead > maxLeadPt) + return; histos.fill(HIST("evSel_Reco"), 5); histos.fill(HIST("ptLeadHistogram_Reco"), pTlead); // ---- sphericity --------------------------------------------- const double sph = computeSphericity(selTracks); - if (sph >= 0.) histos.fill(HIST("sphericity_beforeCuts_Reco"), sph); - if (sph < 0.) return; + if (sph >= 0.) + histos.fill(HIST("sphericity_beforeCuts_Reco"), sph); + if (sph < 0.) + return; histos.fill(HIST("evSel_Reco"), 6); histos.fill(HIST("sphericity_Reco"), sph); hSphMult_Reco[mBin]->Fill(sph); const int sphCls = sphClass(sph); - if (sphCls < 0) return; + if (sphCls < 0) + return; histos.fill(HIST("evSel_Reco"), 7); const PidSpecies pidLead = pidFromPDG(selTracks[leadIdx].pdg); // ---- trigger counter ---------------------------------------- - hNtrig_Reco->Fill((double)mBin, (double)sphCls); - hNtrig_Reco->Fill((double)mBin, (double)(nSphClasses - 1)); + hNtrig_Reco->Fill(static_cast(mBin), static_cast(sphCls)); + hNtrig_Reco->Fill(static_cast(mBin), static_cast((nSphClasses - 1))); // ================================================================ // [DISABLED — histogram budget] Study histograms (identical to @@ -1230,11 +1319,11 @@ struct myExampleTask { #if 0 auto fillPtStudy = [&](std::array, nPidStudy>, nSphClasses>& arr, int iSphCls, int im, double pt, PidSpecies pid) { - arr[iSphCls][0]->Fill(pt, (double)im); - arr[nSphClasses - 1][0]->Fill(pt, (double)im); - if ((int)pid > 0 && (int)pid < nPidStudy) { - arr[iSphCls][(int)pid]->Fill(pt, (double)im); - arr[nSphClasses - 1][(int)pid]->Fill(pt, (double)im); + arr[iSphCls][0]->Fill(pt, static_cast(im)); + arr[nSphClasses - 1][0]->Fill(pt, static_cast(im)); + if (static_cast(pid) > 0 && static_cast(pid) < nPidStudy) { + arr[iSphCls][static_cast(pid)]->Fill(pt, static_cast(im)); + arr[nSphClasses - 1][static_cast(pid)]->Fill(pt, static_cast(im)); } }; @@ -1242,56 +1331,56 @@ struct myExampleTask { for (const auto& t : selTracks) { PidSpecies pid = pidFromPDG(t.pdg); nTrackPid[0]++; - if ((int)pid > 0 && (int)pid < nPidStudy) - nTrackPid[(int)pid]++; + if (static_cast(pid) > 0 && static_cast(pid) < nPidStudy) + nTrackPid[static_cast(pid)]++; } - hSph_vs_mult_pid_Reco[0]->Fill(sph, (double)mBin); - hMult_vs_sph_pid_Reco[0]->Fill((double)nch, (double)sphCls); + hSph_vs_mult_pid_Reco[0]->Fill(sph, static_cast(mBin)); + hMult_vs_sph_pid_Reco[0]->Fill(static_cast(nch), static_cast(sphCls)); for (int ip = 1; ip < nPidStudy; ++ip) { if (nTrackPid[ip] > 0) { - hSph_vs_mult_pid_Reco[ip]->Fill(sph, (double)mBin); - hMult_vs_sph_pid_Reco[ip]->Fill((double)nch, (double)sphCls); + hSph_vs_mult_pid_Reco[ip]->Fill(sph, static_cast(mBin)); + hMult_vs_sph_pid_Reco[ip]->Fill(static_cast(nch), static_cast(sphCls)); } } - hSphLead_vs_mult_pid_Reco[0]->Fill(sph, (double)mBin); - hMultLead_vs_sph_pid_Reco[0]->Fill((double)nch, (double)sphCls); - if ((int)pidLead > 0 && (int)pidLead < nPidStudy) { - hSphLead_vs_mult_pid_Reco[(int)pidLead]->Fill(sph, (double)mBin); - hMultLead_vs_sph_pid_Reco[(int)pidLead]->Fill((double)nch, (double)sphCls); + hSphLead_vs_mult_pid_Reco[0]->Fill(sph, static_cast(mBin)); + hMultLead_vs_sph_pid_Reco[0]->Fill(static_cast(nch), static_cast(sphCls)); + if (static_cast(pidLead) > 0 && static_cast(pidLead) < nPidStudy) { + hSphLead_vs_mult_pid_Reco[static_cast(pidLead)]->Fill(sph, static_cast(mBin)); + hMultLead_vs_sph_pid_Reco[static_cast(pidLead)]->Fill(static_cast(nch), static_cast(sphCls)); } - hMultReal_vs_sph_pid_Reco[0]->Fill((double)nch, (double)sphCls); - if ((int)pidLead > 0 && (int)pidLead < nPidStudy) - hMultReal_vs_sph_pid_Reco[(int)pidLead]->Fill((double)nch, (double)sphCls); + hMultReal_vs_sph_pid_Reco[0]->Fill(static_cast(nch), static_cast(sphCls)); + if (static_cast(pidLead) > 0 && static_cast(pidLead) < nPidStudy) + hMultReal_vs_sph_pid_Reco[static_cast(pidLead)]->Fill(static_cast(nch), static_cast(sphCls)); - hMultReal_vs_pid_Reco->Fill((double)nch, 0.); + hMultReal_vs_pid_Reco->Fill(static_cast(nch), 0.); for (int ip = 1; ip < nPidStudy; ++ip) { if (nTrackPid[ip] > 0) - hMultReal_vs_pid_Reco->Fill((double)nch, (double)ip); + hMultReal_vs_pid_Reco->Fill(static_cast(nch), static_cast(ip)); } for (const auto& t : selTracks) { PidSpecies pid = pidFromPDG(t.pdg); - hSphTrack_vs_mult_pid_Reco[0]->Fill(sph, (double)mBin); - hMultTrack_vs_sph_pid_Reco[0]->Fill((double)nch, (double)sphCls); - if ((int)pid > 0 && (int)pid < nPidStudy) { - hSphTrack_vs_mult_pid_Reco[(int)pid]->Fill(sph, (double)mBin); - hMultTrack_vs_sph_pid_Reco[(int)pid]->Fill((double)nch, (double)sphCls); + hSphTrack_vs_mult_pid_Reco[0]->Fill(sph, static_cast(mBin)); + hMultTrack_vs_sph_pid_Reco[0]->Fill(static_cast(nch), static_cast(sphCls)); + if (static_cast(pid) > 0 && static_cast(pid) < nPidStudy) { + hSphTrack_vs_mult_pid_Reco[static_cast(pid)]->Fill(sph, static_cast(mBin)); + hMultTrack_vs_sph_pid_Reco[static_cast(pid)]->Fill(static_cast(nch), static_cast(sphCls)); } } - hPtLead_sph_pid_Reco[sphCls][0]->Fill(pTlead, (double)mBin); - hPtLead_sph_pid_Reco[nSphClasses - 1][0]->Fill(pTlead, (double)mBin); - if ((int)pidLead > 0 && (int)pidLead < nPidStudy) { - hPtLead_sph_pid_Reco[sphCls][(int)pidLead]->Fill(pTlead, (double)mBin); - hPtLead_sph_pid_Reco[nSphClasses - 1][(int)pidLead]->Fill(pTlead, (double)mBin); + hPtLead_sph_pid_Reco[sphCls][0]->Fill(pTlead, static_cast(mBin)); + hPtLead_sph_pid_Reco[nSphClasses - 1][0]->Fill(pTlead, static_cast(mBin)); + if (static_cast(pidLead) > 0 && static_cast(pidLead) < nPidStudy) { + hPtLead_sph_pid_Reco[sphCls][static_cast(pidLead)]->Fill(pTlead, static_cast(mBin)); + hPtLead_sph_pid_Reco[nSphClasses - 1][static_cast(pidLead)]->Fill(pTlead, static_cast(mBin)); } for (const auto& t : selTracks) { PidSpecies pid = pidFromPDG(t.pdg); - fillPtStudy(hPt_sph_pid_Reco, sphCls, mBin, (double)t.pt, pid); + fillPtStudy(hPt_sph_pid_Reco, sphCls, mBin, static_cast(t.pt), pid); } #endif // [DISABLED — histogram budget] categories 1,2,3,5,6,7 @@ -1300,16 +1389,19 @@ struct myExampleTask { // ================================================================ bool seHasPairs = false; for (int ia = 0; ia < nch; ++ia) { - if (ia == leadIdx) continue; + if (ia == leadIdx) + continue; const auto& assoc = selTracks[ia]; - if (assoc.pt >= pTlead) continue; - if (assoc.pt < minAssocCorr || assoc.pt > maxAssocCorr) continue; + if (assoc.pt >= pTlead) + continue; + if (assoc.pt < minAssocCorr || assoc.pt > maxAssocCorr) + continue; histos.fill(HIST("ptAssocHistogram_Reco"), assoc.pt); const PidSpecies pidAssoc = pidFromPDG(assoc.pdg); #if 0 - fillPtStudy(hPtAssoc_sph_pid_Reco, sphCls, mBin, (double)assoc.pt, pidAssoc); + fillPtStudy(hPtAssoc_sph_pid_Reco, sphCls, mBin, static_cast(assoc.pt), pidAssoc); #endif // [DISABLED — histogram budget, category 4] const double dphi = computeDeltaPhi(phiLead, assoc.phi); @@ -1330,13 +1422,14 @@ struct myExampleTask { seHasPairs = true; } - if (seHasPairs) histos.fill(HIST("evSel_Reco"), 8); + if (seHasPairs) + histos.fill(HIST("evSel_Reco"), 8); // ---- hNtrig_Reco per specie PID leading -------------------------- int pidLeadIdx = pidSpeciesToIdx(pidLead); if (pidLeadIdx >= 0) { - hNtrigPID_Reco[pidLeadIdx]->Fill((double)mBin, (double)sphCls); - hNtrigPID_Reco[pidLeadIdx]->Fill((double)mBin, (double)(nSphClasses - 1)); + hNtrigPID_Reco[pidLeadIdx]->Fill(static_cast(mBin), static_cast(sphCls)); + hNtrigPID_Reco[pidLeadIdx]->Fill(static_cast(mBin), static_cast((nSphClasses - 1))); } // ================================================================ @@ -1345,12 +1438,14 @@ struct myExampleTask { { auto& pool = eventPools_Reco[sphCls][zBin][mBin]; pool.push_back(selTracks); - if ((int)pool.size() > mixPoolSize) pool.pop_front(); + if (static_cast(pool.size()) > mixPoolSize) + pool.pop_front(); } { auto& poolAll = eventPools_Reco[nSphClasses - 1][zBin][mBin]; poolAll.push_back(selTracks); - if ((int)poolAll.size() > mixPoolSize) poolAll.pop_front(); + if (static_cast(poolAll.size()) > mixPoolSize) + poolAll.pop_front(); } for (int ip = 0; ip < nPidCorr; ++ip) { @@ -1359,16 +1454,19 @@ struct myExampleTask { if (pidSpeciesToIdx(pidFromPDG(t.pdg)) == ip) pidTracks.push_back(t); } - if (pidTracks.empty()) continue; + if (pidTracks.empty()) + continue; { auto& pool = eventPoolsPID_Reco[ip][sphCls][zBin][mBin]; pool.push_back(pidTracks); - if ((int)pool.size() > mixPoolSize) pool.pop_front(); + if (static_cast(pool.size()) > mixPoolSize) + pool.pop_front(); } { auto& pool = eventPoolsPID_Reco[ip][nSphClasses - 1][zBin][mBin]; pool.push_back(pidTracks); - if ((int)pool.size() > mixPoolSize) pool.pop_front(); + if (static_cast(pool.size()) > mixPoolSize) + pool.pop_front(); } } @@ -1376,12 +1474,14 @@ struct myExampleTask { // MIXED-EVENT "all" (Delta-eta) // ================================================================ auto doMixing = [&](int poolIdx) { - const auto& pool = eventPools_Reco[poolIdx][zBin][mBin]; - const int nEvts = (int)pool.size() - 1; + const auto& pool = eventPools_Reco[poolIdx][zBin][mBin]; + const int nEvts = static_cast(pool.size()) - 1; for (int ie = 0; ie < nEvts; ++ie) { for (const auto& tr : pool[ie]) { - if (tr.pt >= pTlead) continue; - if (tr.pt < minAssocCorr || tr.pt > maxAssocCorr) continue; + if (tr.pt >= pTlead) + continue; + if (tr.pt < minAssocCorr || tr.pt > maxAssocCorr) + continue; const double dphi = computeDeltaPhi(phiLead, tr.phi); const double deta = etaLead - tr.eta; hME_Reco[poolIdx][mBin]->Fill(dphi, deta); @@ -1398,12 +1498,14 @@ struct myExampleTask { // ================================================================ if (pidLeadIdx >= 0) { auto doMixingPID = [&](int poolIdx) { - const auto& pool = eventPoolsPID_Reco[pidLeadIdx][poolIdx][zBin][mBin]; - const int nEvts = (int)pool.size() - 1; + const auto& pool = eventPoolsPID_Reco[pidLeadIdx][poolIdx][zBin][mBin]; + const int nEvts = static_cast(pool.size()) - 1; for (int ie = 0; ie < nEvts; ++ie) { for (const auto& tr : pool[ie]) { - if (tr.pt >= pTlead) continue; - if (tr.pt < minAssocCorr || tr.pt > maxAssocCorr) continue; + if (tr.pt >= pTlead) + continue; + if (tr.pt < minAssocCorr || tr.pt > maxAssocCorr) + continue; const double dphi = computeDeltaPhi(phiLead, tr.phi); const double detaPID = etaLead - tr.eta; hMEpid_Reco[pidLeadIdx][poolIdx][mBin]->Fill(dphi, detaPID); @@ -1413,7 +1515,6 @@ struct myExampleTask { doMixingPID(sphCls); doMixingPID(nSphClasses - 1); } - } PROCESS_SWITCH(myExampleTask, processReco, "Process reconstructed data", false); @@ -1431,30 +1532,39 @@ struct myExampleTask { using TrackRecoMC = soa::Join; void processMCConfusion(ColReco::iterator const& col, - TrackRecoMC const& tracks, - aod::McParticles const&) + TrackRecoMC const& tracks, + aod::McParticles const&) { - if (!col.sel8()) return; - if (std::fabs(col.posZ()) > maxZvtx) return; + if (!col.sel8()) + return; + if (std::fabs(col.posZ()) > maxZvtx) + return; for (const auto& track : tracks) { - if (!track.has_mcParticle()) continue; + if (!track.has_mcParticle()) + continue; auto mcPart = track.mcParticle(); - if (!mcPart.isPhysicalPrimary()) continue; + if (!mcPart.isPhysicalPrimary()) + continue; auto* pdgPtr = TDatabasePDG::Instance()->GetParticle(mcPart.pdgCode()); - if (!pdgPtr || std::fabs(pdgPtr->Charge()) < 0.1) continue; + if (!pdgPtr || std::fabs(pdgPtr->Charge()) < 0.1) + continue; // Same track-level cuts as processReco, so the matrix reflects the // actual analyzed track population, not an unfiltered sample. - if (std::fabs(track.dcaXY()) > maxDcaXY) continue; - if (std::fabs(track.dcaZ()) > maxDcaZ) continue; - if (std::fabs(track.eta()) > maxEta) continue; - if (track.pt() < minAssocPt) continue; - - PidSpecies truePid = pidFromPDG(mcPart.pdgCode()); + if (std::fabs(track.dcaXY()) > maxDcaXY) + continue; + if (std::fabs(track.dcaZ()) > maxDcaZ) + continue; + if (std::fabs(track.eta()) > maxEta) + continue; + if (track.pt() < minAssocPt) + continue; + + PidSpecies truePid = pidFromPDG(mcPart.pdgCode()); PidSpecies taggedPid = pidFromNSigma(track, maxNSigmaPID, maxNSigmaPIDAmbig, - maxNSigmaTPC, maxNSigmaTOF, - useLegacyNSigmaGating, useLegacyPIDSelection); + maxNSigmaTPC, maxNSigmaTOF, + useLegacyNSigmaGating, useLegacyPIDSelection); // Same rapidity cut as processReco applies to its "tagged" PID -- // without this, applyRapidityCutPID=false would silently have zero @@ -1463,11 +1573,12 @@ struct myExampleTask { // despite different processReco yields, before this fix). if (applyRapidityCutPID && taggedPid != kUnidentified) { double tagMass = pidMass(taggedPid); - double tagRap = computeRapidity(track.pt(), track.eta(), tagMass); - if (std::fabs(tagRap) > maxRapPID) taggedPid = kUnidentified; + double tagRap = computeRapidity(track.pt(), track.eta(), tagMass); + if (std::fabs(tagRap) > maxRapPID) + taggedPid = kUnidentified; } - histos.fill(HIST("hPidConfusion_MC"), track.pt(), (double)truePid, (double)taggedPid); + histos.fill(HIST("hPidConfusion_MC"), track.pt(), static_cast(truePid), static_cast(taggedPid)); } } PROCESS_SWITCH(myExampleTask, processMCConfusion, "Rapid PID contamination estimate: true (PDG) vs tagged (nSigma) species confusion matrix, MC only", false); From fec0c342068782a9e4f1ecc0acafb5590f8a2700 Mon Sep 17 00:00:00 2001 From: Madalina Tarzila Date: Thu, 1 Oct 2026 16:10:56 +0300 Subject: [PATCH 3/8] Fix include style for O2 core headers per format_includes.awk. Framework/*.h headers must use angle brackets (<...>), not quotes, per the O2Physics include-style convention (Scripts/format_includes.awk) enforced by the PR formatting / clang-format CI check. Re-ran clang-format after the change; rebuilt and re-ran the single-file smoke test, same result as before. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01YbYBgosEJaDJTkCuqAtNHk --- .../Tasks/twoParticleCorrelationsMultSpher.cxx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationsMultSpher.cxx b/PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationsMultSpher.cxx index 35ada096295..2a336f7e233 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationsMultSpher.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationsMultSpher.cxx @@ -27,9 +27,9 @@ /// /// \author Madalina Tarzila -#include "Framework/ASoAHelpers.h" -#include "Framework/AnalysisTask.h" -#include "Framework/runDataProcessing.h" +#include +#include +#include // Reco headers — compiled but used only by processReco (switched off for now) #include "Common/DataModel/EventSelection.h" From 4b4b132c1cb17c281209573b7e1355ebf6540439 Mon Sep 17 00:00:00 2001 From: Madalina Tarzila Date: Wed, 7 Oct 2026 13:01:21 +0300 Subject: [PATCH 4/8] Fix missing COMPONENT_NAME closing line dropped by GitHub's web conflict resolution. The web-based merge-conflict editor used to resolve the upstream/master merge dropped the "COMPONENT_NAME Analysis)" line closing the two-particle-correlations-mult-spher workflow block, running it into the next block (hparticle-correlation) and breaking CMake configuration. Verified: CMake reconfigure, incremental rebuild, and single-file smoke test all clean after the fix. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01YbYBgosEJaDJTkCuqAtNHk --- PWGCF/TwoParticleCorrelations/Tasks/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/PWGCF/TwoParticleCorrelations/Tasks/CMakeLists.txt b/PWGCF/TwoParticleCorrelations/Tasks/CMakeLists.txt index f27144c2d06..548caef2975 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/CMakeLists.txt +++ b/PWGCF/TwoParticleCorrelations/Tasks/CMakeLists.txt @@ -146,6 +146,8 @@ o2physics_add_dpl_workflow(fox-wolfram-correlation o2physics_add_dpl_workflow(two-particle-correlations-mult-spher SOURCES twoParticleCorrelationsMultSpher.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(hparticle-correlation SOURCES hParticleCorrelation.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore From afc6a462f011ea5862ca93f293bc7bebe3ce8e12 Mon Sep 17 00:00:00 2001 From: Madalina Tarzila Date: Wed, 7 Oct 2026 16:52:59 +0300 Subject: [PATCH 5/8] Fix remaining code-check findings: unused members, dead code, const-correctness Removes the dead kSphMin constant and yLead variable, adds [[maybe_unused]] to the struct members and bookChannel lambda parameters that are only referenced inside the disabled histogram-budget blocks, and adds const to those same lambda parameters per cppcheck's constParameterReference suggestion. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01YbYBgosEJaDJTkCuqAtNHk --- .../twoParticleCorrelationsMultSpher.cxx | 53 ++++++++++--------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationsMultSpher.cxx b/PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationsMultSpher.cxx index 2a336f7e233..222be49e528 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationsMultSpher.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationsMultSpher.cxx @@ -27,8 +27,13 @@ /// /// \author Madalina Tarzila -#include +#include #include +#include +#include +#include +#include +#include #include // Reco headers — compiled but used only by processReco (switched off for now) @@ -45,6 +50,7 @@ #include #include +#include #include #include #include @@ -69,7 +75,6 @@ static constexpr const char* kPidNames[kNPidSpecies] = {"unid", "pion", "kaon", // Sphericity classes // ============================================================ static constexpr int nSphClasses = 4; -static constexpr double kSphMin[nSphClasses] = {0.0, 0.3, 0.6, 0.0}; static constexpr double kSphMax[nSphClasses] = {0.3, 0.6, 1.0, 1.0}; static constexpr const char* kSphLabels[nSphClasses] = {"jetty", "intermediate", "isotropic", "all"}; @@ -148,8 +153,8 @@ struct myExampleTask { float phi; float eta; float pt; - float y; // rapidity (from mass + kinematics); = eta for unidentified - int pdg; // PDG code: used only for SE sparse/TH2; 0 for reco + [[maybe_unused]] float y; // rapidity (from mass + kinematics); = eta for unidentified + int pdg; // PDG code: used only for SE sparse/TH2; 0 for reco }; // ---------------------------------------------------------------- @@ -236,7 +241,7 @@ struct myExampleTask { // pid 1-3 correspond to kPion=1, kKaon=2, kProton=3. // ---------------------------------------------------------------- static constexpr int nPidStudy = 4; // all + pion + kaon + proton - static constexpr const char* kPidStudyLabels[nPidStudy] = {"all", "pion", "kaon", "proton"}; + [[maybe_unused]] static constexpr const char* kPidStudyLabels[nPidStudy] = {"all", "pion", "kaon", "proton"}; // pT (all selected tracks) × multBin — MC e Reco separati std::array, nPidStudy>, nSphClasses> hPt_sph_pid_MC{}; @@ -432,12 +437,10 @@ struct myExampleTask { { if (sph <= 0.) return -1; - if (sph <= 0.3) - return 0; - if (sph <= 0.6) - return 1; - if (sph <= 1.0) - return 2; + for (int ic = 0; ic < nSphClasses - 1; ++ic) { + if (sph <= kSphMax[ic]) + return ic; + } return -1; } @@ -556,17 +559,17 @@ struct myExampleTask { std::shared_ptr& hNtrig_, std::array, nPidCorr>& hNtrigPID_, std::array, nMultBins>& hSphMult_, - std::array, nPidStudy>, nSphClasses>& hPt_sph_pid_, - std::array, nPidStudy>, nSphClasses>& hPtLead_sph_pid_, - std::array, nPidStudy>, nSphClasses>& hPtAssoc_sph_pid_, - std::array, nPidStudy>& hSph_vs_mult_pid_, - std::array, nPidStudy>& hMult_vs_sph_pid_, - std::array, nPidStudy>& hSphTrack_vs_mult_pid_, - std::array, nPidStudy>& hMultTrack_vs_sph_pid_, - std::array, nPidStudy>& hSphLead_vs_mult_pid_, - std::array, nPidStudy>& hMultLead_vs_sph_pid_, - std::array, nPidStudy>& hMultReal_vs_sph_pid_, - std::shared_ptr& hMultReal_vs_pid_) { + [[maybe_unused]] const std::array, nPidStudy>, nSphClasses>& hPt_sph_pid_, + [[maybe_unused]] const std::array, nPidStudy>, nSphClasses>& hPtLead_sph_pid_, + [[maybe_unused]] const std::array, nPidStudy>, nSphClasses>& hPtAssoc_sph_pid_, + [[maybe_unused]] const std::array, nPidStudy>& hSph_vs_mult_pid_, + [[maybe_unused]] const std::array, nPidStudy>& hMult_vs_sph_pid_, + [[maybe_unused]] const std::array, nPidStudy>& hSphTrack_vs_mult_pid_, + [[maybe_unused]] const std::array, nPidStudy>& hMultTrack_vs_sph_pid_, + [[maybe_unused]] const std::array, nPidStudy>& hSphLead_vs_mult_pid_, + [[maybe_unused]] const std::array, nPidStudy>& hMultLead_vs_sph_pid_, + [[maybe_unused]] const std::array, nPidStudy>& hMultReal_vs_sph_pid_, + [[maybe_unused]] const std::shared_ptr& hMultReal_vs_pid_) { // QA // evSel bins: 1=read 2=zvtx 3=multBin 4=zvtxBin 5=leadPt 6=ST_valid 7=sphClass 8=SE_filled auto hQA = histos.add(Form("evSel_%s", suf), Form("Event selection (%s)", suf), HistType::kTH1D, {{8, 0.5, 8.5}}); @@ -851,7 +854,7 @@ struct myExampleTask { std::vector selTracks; selTracks.reserve(64); int leadIdx = -1; - double pTlead = -1., phiLead = 0., etaLead = 0., yLead = 0.; + double pTlead = -1., phiLead = 0., etaLead = 0.; for (const auto& p : mcParticles) { if (!p.isPhysicalPrimary()) @@ -890,7 +893,6 @@ struct myExampleTask { pTlead = p.pt(); phiLead = p.phi(); etaLead = p.eta(); - yLead = rap; leadIdx = idx; } } @@ -1201,7 +1203,7 @@ struct myExampleTask { std::vector selTracks; selTracks.reserve(64); int leadIdx = -1; - double pTlead = -1., phiLead = 0., etaLead = 0., yLead = 0.; + double pTlead = -1., phiLead = 0., etaLead = 0.; for (const auto& track : tracks) { hTrackCutDebug_Reco->Fill(1); // seen @@ -1265,7 +1267,6 @@ struct myExampleTask { pTlead = track.pt(); phiLead = track.phi(); etaLead = track.eta(); - yLead = rap; leadIdx = idx; } } From e14cd542f3ce80b0aea227fb3245538075349f62 Mon Sep 17 00:00:00 2001 From: Madalina Tarzila Date: Fri, 9 Oct 2026 10:05:16 +0300 Subject: [PATCH 6/8] [PWGCF] twoParticleCorrelationsMultSpher: Address review comments - Remove dead code (#if 0 blocks, unused histograms/axes/parameters) - Use RecoDecay::constrainAngle instead of custom computeDeltaPhi - Use O2DatabasePDG service instead of TDatabasePDG - Replace hard-coded bin logic with edge arrays and o2::analysis::findBin - Generate multiplicity labels from the bin edges - Fix include grouping and remove comments between includes --- .../twoParticleCorrelationsMultSpher.cxx | 839 ++++++------------ 1 file changed, 257 insertions(+), 582 deletions(-) diff --git a/PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationsMultSpher.cxx b/PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationsMultSpher.cxx index 222be49e528..56685bd98a6 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationsMultSpher.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationsMultSpher.cxx @@ -27,23 +27,27 @@ /// /// \author Madalina Tarzila +#include "PWGHF/Utils/utilsAnalysis.h" + +#include "Common/Core/RecoDecay.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include #include +#include #include #include #include #include #include +#include #include #include -// Reco headers — compiled but used only by processReco (switched off for now) -#include "Common/DataModel/EventSelection.h" -#include "Common/DataModel/Multiplicity.h" -#include "Common/DataModel/PIDResponseTOF.h" -#include "Common/DataModel/PIDResponseTPC.h" -#include "Common/DataModel/TrackSelectionTables.h" - -#include #include #include #include @@ -55,6 +59,7 @@ #include #include #include +#include #include using namespace o2; @@ -64,20 +69,19 @@ using namespace o2::framework::expressions; // ============================================================ // PID species // ============================================================ -enum PidSpecies { kUnidentified = 0, - kPion, - kKaon, - kProton, +enum PidSpecies { kPidUnidentified = 0, + kPidPion, + kPidKaon, + kPidProton, kNPidSpecies }; -static constexpr const char* kPidNames[kNPidSpecies] = {"unid", "pion", "kaon", "proton"}; +static constexpr std::array kPidNames{"unid", "pion", "kaon", "proton"}; // ============================================================ // Sphericity classes // ============================================================ static constexpr int nSphClasses = 4; -static constexpr double kSphMax[nSphClasses] = {0.3, 0.6, 1.0, 1.0}; -static constexpr const char* kSphLabels[nSphClasses] = - {"jetty", "intermediate", "isotropic", "all"}; +static constexpr std::array kSphMax{0.3, 0.6, 1.0, 1.0}; +static constexpr std::array kSphLabels{"jetty", "intermediate", "isotropic", "all"}; // ============================================================ struct myExampleTask { @@ -113,7 +117,7 @@ struct myExampleTask { // 2026-08-11; an earlier draft of this instruction conflated the two // into a single 2-sigma threshold, corrected here). Configurable maxNSigmaPID{"maxNSigmaPID", 2.0, "Max combined N_sigma,PID for species selection (ALICE 2511.10399)"}; - Configurable maxNSigmaPIDAmbig{"maxNSigmaPIDAmbig", 3.0, "N_sigma,PID threshold for ambiguity rejection (>1 species below this -> kUnidentified)"}; + Configurable maxNSigmaPIDAmbig{"maxNSigmaPIDAmbig", 3.0, "N_sigma,PID threshold for ambiguity rejection (>1 species below this -> kPidUnidentified)"}; // Pre-2026-08-11 PID thresholds, restored (not reused for anything else) // so the legacy gating style below can be toggled back on without @@ -143,6 +147,8 @@ struct myExampleTask { // tradeoff, not a free win, and was declined. Configurable useLegacyNSigmaGating{"useLegacyNSigmaGating", false, "false=pT-gated combined N_sigma,PID (2511.10399); true=independent TPC/TOF 3-sigma AND-gate, TOF always required when available (pre-2026-08-11)"}; Configurable useLegacyPIDSelection{"useLegacyPIDSelection", false, "false=reject if >1 species below ambiguity threshold; true=pick minimum-chi2 species among those passing the gate, no rejection (pre-2026-08-11)"}; + Service pdg{}; + Configurable applyRapidityCutPID{"applyRapidityCutPID", false, "false=skip the |y| kZvtxEdges{-10., -5., -2.5, 2.5, 5., 10.}; + static constexpr std::array kMultEdges{1, 10, 15, 20, 30, 46, 81}; + + static std::string multLabel(int im) + { + return std::to_string(kMultEdges[im]) + "-" + std::to_string(kMultEdges[im + 1] - 1); + } // ---------------------------------------------------------------- // Mixing pools @@ -172,25 +183,30 @@ struct myExampleTask { // pidIdx: 0=pion, 1=kaon, 2=proton // ---------------------------------------------------------------- static constexpr int nPidCorr = 3; // pion, kaon, proton - static constexpr const char* kPidCorrNames[nPidCorr] = {"pion", "kaon", "proton"}; + static constexpr std::array kPidCorrNames{"pion", "kaon", "proton"}; // PDG codes corrispondenti a pidIdx static int pidCorrPDG(int idx) { - if (idx == 0) + if (idx == 0) { return 211; - if (idx == 1) + } + if (idx == 1) { return 321; + } return 2212; } // Mappa PidSpecies -> pidIdx (-1 se non e' pion/kaon/proton) static int pidSpeciesToIdx(PidSpecies pid) { - if (pid == kPion) + if (pid == kPidPion) { return 0; - if (pid == kKaon) + } + if (pid == kPidKaon) { return 1; - if (pid == kProton) + } + if (pid == kPidProton) { return 2; + } return -1; } @@ -219,9 +235,9 @@ struct myExampleTask { std::array, nMultBins>, nSphClasses>, nPidCorr> hSEpid_Reco{}; std::array, nMultBins>, nSphClasses>, nPidCorr> hMEpid_Reco{}; - // Trigger counter: axes = multBin(x) x sphClass(y) — MC e Reco separati - std::shared_ptr hNtrig_MC{}; - std::shared_ptr hNtrig_Reco{}; + // Trigger counter: axes = mult bin (x) x sphClass (y) — MC e Reco separati + std::shared_ptr hNtrig_MC; + std::shared_ptr hNtrig_Reco; std::array, nPidCorr> hNtrigPID_MC{}; std::array, nPidCorr> hNtrigPID_Reco{}; @@ -229,68 +245,13 @@ struct myExampleTask { std::array, nMultBins> hSphMult_MC{}; std::array, nMultBins> hSphMult_Reco{}; - // ---------------------------------------------------------------- - // Study histograms: pT × multBin, per (sphClass, PID) - // Axes: x = pT [0,5] nBinsPt2 bins, y = multBin [0-5] 6 bins - // - // [sphClass][pid]: - // sphClass: 0=jetty, 1=interm, 2=isotr, 3=all - // pid: 0=all, 1=pion, 2=kaon, 3=proton - // - // NOTE: pid index 0 here means "all PID" (no PID selection), - // pid 1-3 correspond to kPion=1, kKaon=2, kProton=3. - // ---------------------------------------------------------------- - static constexpr int nPidStudy = 4; // all + pion + kaon + proton - [[maybe_unused]] static constexpr const char* kPidStudyLabels[nPidStudy] = {"all", "pion", "kaon", "proton"}; - - // pT (all selected tracks) × multBin — MC e Reco separati - std::array, nPidStudy>, nSphClasses> hPt_sph_pid_MC{}; - std::array, nPidStudy>, nSphClasses> hPt_sph_pid_Reco{}; - // pT leading × multBin - std::array, nPidStudy>, nSphClasses> hPtLead_sph_pid_MC{}; - std::array, nPidStudy>, nSphClasses> hPtLead_sph_pid_Reco{}; - // pT associated × multBin - std::array, nPidStudy>, nSphClasses> hPtAssoc_sph_pid_MC{}; - std::array, nPidStudy>, nSphClasses> hPtAssoc_sph_pid_Reco{}; - - // S_T × multBin (discrete), per PID — MC e Reco separati - std::array, nPidStudy> hSph_vs_mult_pid_MC{}; - std::array, nPidStudy> hSph_vs_mult_pid_Reco{}; - // mult (continuous [0,80]) × sphClass (discrete 0-2), per PID - std::array, nPidStudy> hMult_vs_sph_pid_MC{}; - std::array, nPidStudy> hMult_vs_sph_pid_Reco{}; - - // ---------------------------------------------------------------- - // Varianta A: fracție per TRACK — MC e Reco separati - // ---------------------------------------------------------------- - std::array, nPidStudy> hSphTrack_vs_mult_pid_MC{}; - std::array, nPidStudy> hSphTrack_vs_mult_pid_Reco{}; - std::array, nPidStudy> hMultTrack_vs_sph_pid_MC{}; - std::array, nPidStudy> hMultTrack_vs_sph_pid_Reco{}; - - // ---------------------------------------------------------------- - // Varianta B: fracție per eveniment, PID leading — MC e Reco separati - // ---------------------------------------------------------------- - std::array, nPidStudy> hSphLead_vs_mult_pid_MC{}; - std::array, nPidStudy> hSphLead_vs_mult_pid_Reco{}; - std::array, nPidStudy> hMultLead_vs_sph_pid_MC{}; - std::array, nPidStudy> hMultLead_vs_sph_pid_Reco{}; - - // ---------------------------------------------------------------- - // Distributia de multiplicitate REALA — MC e Reco separati - // ---------------------------------------------------------------- - std::array, nPidStudy> hMultReal_vs_sph_pid_MC{}; - std::array, nPidStudy> hMultReal_vs_sph_pid_Reco{}; - std::shared_ptr hMultReal_vs_pid_MC{}; - std::shared_ptr hMultReal_vs_pid_Reco{}; - // ---------------------------------------------------------------- // [DEBUG, TEMPORARY] Track-cut counter for processReco diagnostics. // Bins: 1=tracks seen, 2=passed isGlobalTrackWoDCA, 3=passed DCAxy, // 4=passed DCAz, 5=passed eta, 6=passed pt (selected). // Remove once the empty-Reco-histograms issue is understood/fixed. // ---------------------------------------------------------------- - std::shared_ptr hTrackCutDebug_Reco{}; + std::shared_ptr hTrackCutDebug_Reco; // ---------------------------------------------------------------- // PID confusion matrix (true species from PDG vs. tagged species from @@ -302,7 +263,7 @@ struct myExampleTask { // one or the other). See auto-memory / STATUS.md for the "rapid vs // riguros" design discussion this came out of. // ---------------------------------------------------------------- - std::shared_ptr hPidConfusion_MC{}; + std::shared_ptr hPidConfusion_MC; // ---------------------------------------------------------------- // Helpers @@ -312,13 +273,13 @@ struct myExampleTask { { switch (std::abs(pdg)) { case 211: - return kPion; + return kPidPion; case 321: - return kKaon; + return kPidKaon; case 2212: - return kProton; + return kPidProton; default: - return kUnidentified; + return kPidUnidentified; } } @@ -338,7 +299,7 @@ struct myExampleTask { /// required whenever available regardless of pT. /// /// legacySelection (what to do with hypotheses that pass the gate): - /// false (default): reject the track outright (kUnidentified) if MORE + /// false (default): reject the track outright (kPidUnidentified) if MORE /// THAN ONE hypothesis also falls below the looser ambiguity /// threshold maxAmbig (3 sigma) -- this comparison always uses the /// combined-quadrature N_sigma,PID, regardless of legacyGating, so @@ -361,10 +322,9 @@ struct myExampleTask { double nsTPC; double nsTOF; }; - Hypo hypos[3] = { - {kPion, track.tpcNSigmaPi(), track.tofNSigmaPi()}, - {kKaon, track.tpcNSigmaKa(), track.tofNSigmaKa()}, - {kProton, track.tpcNSigmaPr(), track.tofNSigmaPr()}}; + const std::array hypos{{{kPidPion, track.tpcNSigmaPi(), track.tofNSigmaPi()}, + {kPidKaon, track.tpcNSigmaKa(), track.tofNSigmaKa()}, + {kPidProton, track.tpcNSigmaPr(), track.tofNSigmaPr()}}}; // TOF unavailable -> nSigma returns -999; check with any species bool hasTOF = (std::fabs(track.tofNSigmaPi()) < 100.f); @@ -383,11 +343,12 @@ struct myExampleTask { if (legacySelection) { // Best-fit (min chi2) among hypotheses passing the gate -- no // ambiguity rejection (pre-2026-08-11 style). - PidSpecies best = kUnidentified; + PidSpecies best = kPidUnidentified; double bestChi2 = 1e9; for (const auto& h : hypos) { - if (!passesGate(h)) + if (!passesGate(h)) { continue; + } double chi2 = h.nsTPC * h.nsTPC + (hasTOF ? h.nsTOF * h.nsTOF : 0.0); if (chi2 < bestChi2) { bestChi2 = chi2; @@ -399,19 +360,22 @@ struct myExampleTask { // Ambiguity-rejection selection (current default style). int nBelowAmbig = 0; - PidSpecies selected = kUnidentified; + PidSpecies selected = kPidUnidentified; for (const auto& h : hypos) { - if (!passesGate(h)) + if (!passesGate(h)) { continue; + } double nsPID = useCombined ? std::sqrt(h.nsTPC * h.nsTPC + h.nsTOF * h.nsTOF) : std::fabs(h.nsTPC); - if (nsPID < maxAmbig) + if (nsPID < maxAmbig) { ++nBelowAmbig; + } selected = h.pid; } - if (nBelowAmbig > 1) - return kUnidentified; // ambiguous, reject + if (nBelowAmbig > 1) { + return kPidUnidentified; // ambiguous, reject + } return selected; } @@ -419,11 +383,11 @@ struct myExampleTask { static double pidMass(PidSpecies pid) { switch (pid) { - case kPion: + case kPidPion: return 0.13957; - case kKaon: + case kPidKaon: return 0.49368; - case kProton: + case kPidProton: return 0.93827; default: return 0.; @@ -435,63 +399,22 @@ struct myExampleTask { // when sph > 0, without needing a specific bin lookup. static int sphClass(double sph) { - if (sph <= 0.) + if (sph <= 0.) { return -1; + } for (int ic = 0; ic < nSphClasses - 1; ++ic) { - if (sph <= kSphMax[ic]) + if (sph <= kSphMax[ic]) { return ic; + } } return -1; } - static int zvtxBin(double z) - { - if (z >= -10. && z < -5.) - return 0; - if (z >= -5. && z < -2.5) - return 1; - if (z >= -2.5 && z < 2.5) - return 2; - if (z >= 2.5 && z < 5.) - return 3; - if (z >= 5. && z <= 10.) - return 4; - return -1; - } - - static int multBin(int n) - { - if (n >= 1 && n <= 9) - return 0; - if (n >= 10 && n <= 14) - return 1; - if (n >= 15 && n <= 19) - return 2; - if (n >= 20 && n <= 29) - return 3; - if (n >= 30 && n <= 45) - return 4; - if (n >= 46 && n <= 80) - return 5; - return -1; - } - - static double computeDeltaPhi(double phi1, double phi2) - { - double dphi = phi1 - phi2; - while (dphi <= -TMath::Pi()) - dphi += 2. * TMath::Pi(); - while (dphi > TMath::Pi()) - dphi -= 2. * TMath::Pi(); - if (dphi <= -0.5 * TMath::Pi()) - dphi += 2. * TMath::Pi(); - return dphi; - } - static double computeSphericity(const std::vector& tracks) { - if (tracks.size() < 3) + if (tracks.size() < 3) { return -1.0; + } double Sxx = 0., Sxy = 0., Syy = 0.; for (const auto& t : tracks) { double px = t.pt * std::cos(t.phi); @@ -502,11 +425,13 @@ struct myExampleTask { } double tr = Sxx + Syy; double disc = tr * tr - 4.0 * (Sxx * Syy - Sxy * Sxy); - if (disc < 0.) + if (disc < 0.) { disc = 0.; + } double lambdaMin = (tr - std::sqrt(disc)) / 2.0; - if (tr < 1e-12) + if (tr < 1e-12) { return -1.0; + } return std::clamp(2.0 * lambdaMin / tr, 0.0, 1.0); } @@ -515,12 +440,14 @@ struct myExampleTask { /// For unidentified particles (mass <= 0), returns eta. static double computeRapidity(double pt, double eta, double mass) { - if (mass <= 0.) + if (mass <= 0.) { return eta; // massless / unidentified -> y = eta + } double pz = pt * std::sinh(eta); double E = std::sqrt(mass * mass + pt * pt + pz * pz); - if (E <= std::fabs(pz) + 1e-12) + if (E <= std::fabs(pz) + 1e-12) { return eta; // safety + } return 0.5 * std::log((E + pz) / (E - pz)); } @@ -542,9 +469,6 @@ struct myExampleTask { const AxisSpec axisPt{nBinsPt, 0., 10., "p_{T} (GeV/c)"}; const AxisSpec axisSph{100, 0., 1., "S_{T}"}; const AxisSpec axisPt2{nBinsPt2, 0., 5., "p_{T} (GeV/c)"}; - const AxisSpec axisMultBin{nMultBins, -0.5, static_cast(nMultBins) - 0.5, "mult bin"}; - const AxisSpec axisSphCls{3, -0.5, 2.5, "sph class"}; - const AxisSpec axisMultCont{80, 0., 80., "N_{ch}"}; // ---------------------------------------------------------------- // Lambda comuna: creeaza un set complet de histograme pentru un @@ -558,18 +482,7 @@ struct myExampleTask { std::array, nMultBins>, nSphClasses>, nPidCorr>& hMEpid_, std::shared_ptr& hNtrig_, std::array, nPidCorr>& hNtrigPID_, - std::array, nMultBins>& hSphMult_, - [[maybe_unused]] const std::array, nPidStudy>, nSphClasses>& hPt_sph_pid_, - [[maybe_unused]] const std::array, nPidStudy>, nSphClasses>& hPtLead_sph_pid_, - [[maybe_unused]] const std::array, nPidStudy>, nSphClasses>& hPtAssoc_sph_pid_, - [[maybe_unused]] const std::array, nPidStudy>& hSph_vs_mult_pid_, - [[maybe_unused]] const std::array, nPidStudy>& hMult_vs_sph_pid_, - [[maybe_unused]] const std::array, nPidStudy>& hSphTrack_vs_mult_pid_, - [[maybe_unused]] const std::array, nPidStudy>& hMultTrack_vs_sph_pid_, - [[maybe_unused]] const std::array, nPidStudy>& hSphLead_vs_mult_pid_, - [[maybe_unused]] const std::array, nPidStudy>& hMultLead_vs_sph_pid_, - [[maybe_unused]] const std::array, nPidStudy>& hMultReal_vs_sph_pid_, - [[maybe_unused]] const std::shared_ptr& hMultReal_vs_pid_) { + std::array, nMultBins>& hSphMult_) { // QA // evSel bins: 1=read 2=zvtx 3=multBin 4=zvtxBin 5=leadPt 6=ST_valid 7=sphClass 8=SE_filled auto hQA = histos.add(Form("evSel_%s", suf), Form("Event selection (%s)", suf), HistType::kTH1D, {{8, 0.5, 8.5}}); @@ -596,7 +509,7 @@ struct myExampleTask { for (int im = 0; im < nMultBins; ++im) { hSphMult_[im] = histos.add( Form("sphericity_mult%d_%s", im, suf), - Form("S_T [%s] (%s);S_{T};events", kMultLabels[im], suf), + Form("S_T [%s] (%s);S_{T};events", multLabel(im).c_str(), suf), HistType::kTH1D, {axisSph}); } @@ -607,21 +520,23 @@ struct myExampleTask { HistType::kTH2D, {{nMultBins, -0.5, static_cast(nMultBins) - 0.5}, {nSphClasses, -0.5, static_cast(nSphClasses) - 0.5}}); - for (int im = 0; im < nMultBins; ++im) - hNtrig_->GetXaxis()->SetBinLabel(im + 1, kMultLabels[im]); - for (int ic = 0; ic < nSphClasses; ++ic) + for (int im = 0; im < nMultBins; ++im) { + hNtrig_->GetXaxis()->SetBinLabel(im + 1, multLabel(im).c_str()); + } + for (int ic = 0; ic < nSphClasses; ++ic) { hNtrig_->GetYaxis()->SetBinLabel(ic + 1, kSphLabels[ic]); + } // SE/ME TH2D: [sphClass][multBin] for (int ic = 0; ic < nSphClasses; ++ic) { for (int im = 0; im < nMultBins; ++im) { hSE_[ic][im] = histos.add( Form("hSE_%s_sph%d_mult%d", suf, ic, im), - Form("SE [%s][%s] (%s);#Delta#phi;#Delta#eta", kSphLabels[ic], kMultLabels[im], suf), + Form("SE [%s][%s] (%s);#Delta#phi;#Delta#eta", kSphLabels[ic], multLabel(im).c_str(), suf), HistType::kTH2D, {axisDphi, axisDeta}); hME_[ic][im] = histos.add( Form("hME_%s_sph%d_mult%d", suf, ic, im), - Form("ME [%s][%s] (%s);#Delta#phi;#Delta#eta", kSphLabels[ic], kMultLabels[im], suf), + Form("ME [%s][%s] (%s);#Delta#phi;#Delta#eta", kSphLabels[ic], multLabel(im).c_str(), suf), HistType::kTH2D, {axisDphi, axisDeta}); } } @@ -640,12 +555,12 @@ struct myExampleTask { hSEpid_[ip][ic][im] = histos.add( Form("hSE_%s_pid%d_sph%d_mult%d", suf, ip, ic, im), Form("SE %s--%s [%s][%s] (%s);#Delta#varphi;#Delta#eta", - kPidCorrNames[ip], kPidCorrNames[ip], kSphLabels[ic], kMultLabels[im], suf), + kPidCorrNames[ip], kPidCorrNames[ip], kSphLabels[ic], multLabel(im).c_str(), suf), HistType::kTH2D, {axisDphi, axisDeta}); hMEpid_[ip][ic][im] = histos.add( Form("hME_%s_pid%d_sph%d_mult%d", suf, ip, ic, im), Form("ME %s--%s [%s][%s] (%s);#Delta#varphi;#Delta#eta", - kPidCorrNames[ip], kPidCorrNames[ip], kSphLabels[ic], kMultLabels[im], suf), + kPidCorrNames[ip], kPidCorrNames[ip], kSphLabels[ic], multLabel(im).c_str(), suf), HistType::kTH2D, {axisDphi, axisDeta}); } } @@ -656,159 +571,24 @@ struct myExampleTask { HistType::kTH2D, {{nMultBins, -0.5, static_cast(nMultBins) - 0.5}, {nSphClasses, -0.5, static_cast(nSphClasses) - 0.5}}); - for (int im = 0; im < nMultBins; ++im) - hNtrigPID_[ip]->GetXaxis()->SetBinLabel(im + 1, kMultLabels[im]); - for (int ic = 0; ic < nSphClasses; ++ic) + for (int im = 0; im < nMultBins; ++im) { + hNtrigPID_[ip]->GetXaxis()->SetBinLabel(im + 1, multLabel(im).c_str()); + } + for (int ic = 0; ic < nSphClasses; ++ic) { hNtrigPID_[ip]->GetYaxis()->SetBinLabel(ic + 1, kSphLabels[ic]); - } - - // ---------------------------------------------------------------- - // [DISABLED — histogram budget] Categories 1-7 below are commented - // out via #if 0 to stay under O2's HistogramRegistry hard limit of - // 512 histograms (with both MC and Reco channels booked, we were - // at 578). These are auxiliary study histograms not read by any - // current macro (normalization macros only use hSE/hME/hSEpid/ - // hMEpid/hNtrig/hNtrigPID). Re-enable by removing #if 0 / #endif - // if needed for future studies. - // ---------------------------------------------------------------- -#if 0 - // ---- pT × multBin per (sphClass, PID) ----------------------- - for (int ic = 0; ic < nSphClasses; ++ic) { - for (int ip = 0; ip < nPidStudy; ++ip) { - // all selected tracks - hPt_sph_pid_[ic][ip] = histos.add( - Form("hPt_%s_sph%d_pid%d", suf, ic, ip), - Form("p_{T} all [%s][%s] (%s);p_{T} (GeV/c);mult bin", - kSphLabels[ic], kPidStudyLabels[ip], suf), - HistType::kTH2D, {axisPt2, axisMultBin}); - auto* hx = hPt_sph_pid_[ic][ip].get(); - for (int im = 0; im < nMultBins; ++im) - hx->GetYaxis()->SetBinLabel(im + 1, kMultLabels[im]); - - // leading (trigger) particle - hPtLead_sph_pid_[ic][ip] = histos.add( - Form("hPtLead_%s_sph%d_pid%d", suf, ic, ip), - Form("p_{T} lead [%s][%s] (%s);p_{T} (GeV/c);mult bin", - kSphLabels[ic], kPidStudyLabels[ip], suf), - HistType::kTH2D, {axisPt2, axisMultBin}); - auto* hl = hPtLead_sph_pid_[ic][ip].get(); - for (int im = 0; im < nMultBins; ++im) - hl->GetYaxis()->SetBinLabel(im + 1, kMultLabels[im]); - - // associated particles - hPtAssoc_sph_pid_[ic][ip] = histos.add( - Form("hPtAssoc_%s_sph%d_pid%d", suf, ic, ip), - Form("p_{T} assoc [%s][%s] (%s);p_{T} (GeV/c);mult bin", - kSphLabels[ic], kPidStudyLabels[ip], suf), - HistType::kTH2D, {axisPt2, axisMultBin}); - auto* ha = hPtAssoc_sph_pid_[ic][ip].get(); - for (int im = 0; im < nMultBins; ++im) - ha->GetYaxis()->SetBinLabel(im + 1, kMultLabels[im]); } } - - // ---- S_T × multBin (discrete) per PID — per eveniment ------ - for (int ip = 0; ip < nPidStudy; ++ip) { - hSph_vs_mult_pid_[ip] = histos.add( - Form("hSph_vs_mult_%s_pid%d", suf, ip), - Form("S_T vs mult bin [%s] (evt, %s);S_{T};mult bin", kPidStudyLabels[ip], suf), - HistType::kTH2D, {axisSph, axisMultBin}); - auto* hs = hSph_vs_mult_pid_[ip].get(); - for (int im = 0; im < nMultBins; ++im) - hs->GetYaxis()->SetBinLabel(im + 1, kMultLabels[im]); - } - - // ---- mult (continuous) × sphClass per PID — per eveniment --- - for (int ip = 0; ip < nPidStudy; ++ip) { - hMult_vs_sph_pid_[ip] = histos.add( - Form("hMult_vs_sph_%s_pid%d", suf, ip), - Form("mult vs sph class [%s] (evt, %s);N_{ch};sph class", kPidStudyLabels[ip], suf), - HistType::kTH2D, {axisMultCont, axisSphCls}); - auto* hm = hMult_vs_sph_pid_[ip].get(); - for (int ic = 0; ic < 3; ++ic) - hm->GetYaxis()->SetBinLabel(ic + 1, kSphLabels[ic]); - } - - // ---- Varianta A: per TRACK ----------------------------------- - for (int ip = 0; ip < nPidStudy; ++ip) { - hSphTrack_vs_mult_pid_[ip] = histos.add( - Form("hSphTrack_vs_mult_%s_pid%d", suf, ip), - Form("S_T vs mult bin [%s] (track, %s);S_{T};mult bin", kPidStudyLabels[ip], suf), - HistType::kTH2D, {axisSph, axisMultBin}); - auto* h = hSphTrack_vs_mult_pid_[ip].get(); - for (int im = 0; im < nMultBins; ++im) - h->GetYaxis()->SetBinLabel(im + 1, kMultLabels[im]); - - hMultTrack_vs_sph_pid_[ip] = histos.add( - Form("hMultTrack_vs_sph_%s_pid%d", suf, ip), - Form("mult vs sph class [%s] (track, %s);N_{ch};sph class", kPidStudyLabels[ip], suf), - HistType::kTH2D, {axisMultCont, axisSphCls}); - auto* hm = hMultTrack_vs_sph_pid_[ip].get(); - for (int ic = 0; ic < 3; ++ic) - hm->GetYaxis()->SetBinLabel(ic + 1, kSphLabels[ic]); - } - - // ---- Varianta B: per eveniment, PID leading ----------------- - for (int ip = 0; ip < nPidStudy; ++ip) { - hSphLead_vs_mult_pid_[ip] = histos.add( - Form("hSphLead_vs_mult_%s_pid%d", suf, ip), - Form("S_T vs mult bin [%s] (lead, %s);S_{T};mult bin", kPidStudyLabels[ip], suf), - HistType::kTH2D, {axisSph, axisMultBin}); - auto* h = hSphLead_vs_mult_pid_[ip].get(); - for (int im = 0; im < nMultBins; ++im) - h->GetYaxis()->SetBinLabel(im + 1, kMultLabels[im]); - - hMultLead_vs_sph_pid_[ip] = histos.add( - Form("hMultLead_vs_sph_%s_pid%d", suf, ip), - Form("mult vs sph class [%s] (lead, %s);N_{ch};sph class", kPidStudyLabels[ip], suf), - HistType::kTH2D, {axisMultCont, axisSphCls}); - auto* hm = hMultLead_vs_sph_pid_[ip].get(); - for (int ic = 0; ic < 3; ++ic) - hm->GetYaxis()->SetBinLabel(ic + 1, kSphLabels[ic]); - } - - // ---- Distributia N_ch reala --------------------------------- - // N_ch (0-80) × sphClass per PID - for (int ip = 0; ip < nPidStudy; ++ip) { - hMultReal_vs_sph_pid_[ip] = histos.add( - Form("hMultReal_vs_sph_%s_pid%d", suf, ip), - Form("N_ch real vs sph class [%s] (%s);N_{ch};sph class", kPidStudyLabels[ip], suf), - HistType::kTH2D, {axisMultCont, axisSphCls}); - auto* hm = hMultReal_vs_sph_pid_[ip].get(); - for (int ic = 0; ic < 3; ++ic) - hm->GetYaxis()->SetBinLabel(ic + 1, kSphLabels[ic]); - } - // N_ch (0-80) × PID (toate sfericitatile) - hMultReal_vs_pid_ = histos.add( - Form("hMultReal_vs_pid_%s", suf), - Form("N_ch real vs PID (%s);N_{ch};PID", suf), - HistType::kTH2D, - {axisMultCont, - {static_cast(nPidStudy), -0.5, static_cast(nPidStudy) - 0.5}}); - for (int ip = 0; ip < nPidStudy; ++ip) - hMultReal_vs_pid_->GetYaxis()->SetBinLabel(ip + 1, kPidStudyLabels[ip]); -#endif // [DISABLED — histogram budget] categories 1-7 }; // Book histograms for MC (generated) channel bookChannel("MC", hSE_MC, hME_MC, hSEpid_MC, hMEpid_MC, - hNtrig_MC, hNtrigPID_MC, hSphMult_MC, - hPt_sph_pid_MC, hPtLead_sph_pid_MC, hPtAssoc_sph_pid_MC, - hSph_vs_mult_pid_MC, hMult_vs_sph_pid_MC, - hSphTrack_vs_mult_pid_MC, hMultTrack_vs_sph_pid_MC, - hSphLead_vs_mult_pid_MC, hMultLead_vs_sph_pid_MC, - hMultReal_vs_sph_pid_MC, hMultReal_vs_pid_MC); + hNtrig_MC, hNtrigPID_MC, hSphMult_MC); // Book histograms for Reco (reconstructed) channel bookChannel("Reco", hSE_Reco, hME_Reco, hSEpid_Reco, hMEpid_Reco, - hNtrig_Reco, hNtrigPID_Reco, hSphMult_Reco, - hPt_sph_pid_Reco, hPtLead_sph_pid_Reco, hPtAssoc_sph_pid_Reco, - hSph_vs_mult_pid_Reco, hMult_vs_sph_pid_Reco, - hSphTrack_vs_mult_pid_Reco, hMultTrack_vs_sph_pid_Reco, - hSphLead_vs_mult_pid_Reco, hMultLead_vs_sph_pid_Reco, - hMultReal_vs_sph_pid_Reco, hMultReal_vs_pid_Reco); + hNtrig_Reco, hNtrigPID_Reco, hSphMult_Reco); // [DEBUG, TEMPORARY] track-cut diagnostic counter for processReco hTrackCutDebug_Reco = histos.add( @@ -845,27 +625,32 @@ struct myExampleTask { histos.fill(HIST("evSel_MC"), 1); const double zvtx = mcCollision.posZ(); - if (std::fabs(zvtx) > maxZvtx) + if (std::fabs(zvtx) > maxZvtx) { return; + } histos.fill(HIST("evSel_MC"), 2); histos.fill(HIST("Zvertex_MC"), zvtx); // ---- one-pass: build selTracks + find leading --------------- std::vector selTracks; - selTracks.reserve(64); + selTracks.reserve(kMultEdges.back() - 1); int leadIdx = -1; double pTlead = -1., phiLead = 0., etaLead = 0.; for (const auto& p : mcParticles) { - if (!p.isPhysicalPrimary()) + if (!p.isPhysicalPrimary()) { continue; - auto* pdgPtr = TDatabasePDG::Instance()->GetParticle(p.pdgCode()); - if (!pdgPtr || std::fabs(pdgPtr->Charge()) < 0.1) + } + auto* pdgPtr = pdg->GetParticle(p.pdgCode()); + if (!pdgPtr || std::fabs(pdgPtr->Charge()) < 0.1) { continue; - if (std::fabs(p.eta()) > maxEta) + } + if (std::fabs(p.eta()) > maxEta) { continue; - if (p.pt() < minAssocPt) + } + if (p.pt() < minAssocPt) { continue; + } double mass = pdgPtr->Mass(); double rap = computeRapidity(p.pt(), p.eta(), mass); @@ -874,16 +659,15 @@ struct myExampleTask { // (pion/kaon/proton) -- the all-species channel (hSE/hME) keeps the // track regardless, gated only by |eta| < maxEta above. Clean way to // do this: keep pdg for all-species bookkeeping, but zero it (-> - // kUnidentified via pidFromPDG) when the rapidity cut fails, so the + // kPidUnidentified via pidFromPDG) when the rapidity cut fails, so the // track contributes to hSE/hME but not hSEpid/hMEpid. int pdgForPID = p.pdgCode(); - if (applyRapidityCutPID && pidFromPDG(pdgForPID) != kUnidentified && std::fabs(rap) > maxRapPID) { + if (applyRapidityCutPID && pidFromPDG(pdgForPID) != kPidUnidentified && std::fabs(rap) > maxRapPID) { pdgForPID = 0; } int idx = static_cast(selTracks.size()); - selTracks.push_back({static_cast(p.phi()), static_cast(p.eta()), static_cast(p.pt()), - static_cast(rap), pdgForPID}); + selTracks.push_back({static_cast(p.phi()), static_cast(p.eta()), static_cast(p.pt()), pdgForPID}); histos.fill(HIST("etaHistogram_MC"), p.eta()); histos.fill(HIST("phiHistogram_MC"), p.phi()); @@ -900,38 +684,45 @@ struct myExampleTask { const int nch = static_cast(selTracks.size()); histos.fill(HIST("multHist_MC"), nch); - const int mBin = multBin(nch); - if (mBin < 0) + const int mBin = o2::analysis::findBin(&kMultEdges, nch); + if (mBin < 0) { return; + } histos.fill(HIST("evSel_MC"), 3); - const int zBin = zvtxBin(zvtx); - if (zBin < 0) + const int zBin = o2::analysis::findBin(&kZvtxEdges, zvtx); + if (zBin < 0) { return; + } histos.fill(HIST("evSel_MC"), 4); // ---- leading particle pT cut [minLeadPt, maxLeadPt] ----------- - if (leadIdx < 0) + if (leadIdx < 0) { return; - if (pTlead < minLeadPt || pTlead > maxLeadPt) + } + if (pTlead < minLeadPt || pTlead > maxLeadPt) { return; + } histos.fill(HIST("evSel_MC"), 5); histos.fill(HIST("ptLeadHistogram_MC"), pTlead); // ---- sphericity --------------------------------------------- const double sph = computeSphericity(selTracks); // Fill sphericity_beforeCuts here — no additional cuts applied yet - if (sph >= 0.) + if (sph >= 0.) { histos.fill(HIST("sphericity_beforeCuts_MC"), sph); - if (sph < 0.) + } + if (sph < 0.) { return; // fewer than 3 tracks + } histos.fill(HIST("evSel_MC"), 6); histos.fill(HIST("sphericity_MC"), sph); hSphMult_MC[mBin]->Fill(sph); const int sphCls = sphClass(sph); // 0,1,2 or -1 - if (sphCls < 0) + if (sphCls < 0) { return; + } histos.fill(HIST("evSel_MC"), 7); const PidSpecies pidLead = pidFromPDG(selTracks[leadIdx].pdg); @@ -940,119 +731,27 @@ struct myExampleTask { hNtrig_MC->Fill(static_cast(mBin), static_cast(sphCls)); hNtrig_MC->Fill(static_cast(mBin), static_cast((nSphClasses - 1))); // "all" - // ================================================================ - // [DISABLED — histogram budget] Study histograms: S_T vs mult, - // mult vs sph, pT all/lead tracks, per-track and per-leading - // variants, real N_ch distributions. See matching #if 0 block in - // init()/bookChannel for rationale. Re-enable both together. - // ================================================================ -#if 0 - // Helper: maps PidSpecies (0=unid,1=pion,2=kaon,3=proton) - // to pidStudy index (0=all,1=pion,2=kaon,3=proton) - // All tracks fill index 0 ("all") always, plus their specific PID - auto fillPtStudy = [&](std::array, nPidStudy>, nSphClasses>& arr, - int iSphCls, int im, double pt, PidSpecies pid) { - // fill "all" (pid index 0) for both specific sph and "all" sph - arr[iSphCls][0]->Fill(pt, static_cast(im)); - arr[nSphClasses - 1][0]->Fill(pt, static_cast(im)); - // fill specific PID (pid index = static_cast(pid), which is 1-3 for pion/kaon/proton, 0=unid stays in "all" only) - if (static_cast(pid) > 0 && static_cast(pid) < nPidStudy) { - arr[iSphCls][static_cast(pid)]->Fill(pt, static_cast(im)); - arr[nSphClasses - 1][static_cast(pid)]->Fill(pt, static_cast(im)); - } - }; - - // ---- S_T vs multBin și mult vs sphClass — per eveniment ------ - int nTrackPid[nPidStudy] = {0, 0, 0, 0}; - for (const auto& t : selTracks) { - PidSpecies pid = pidFromPDG(t.pdg); - nTrackPid[0]++; - if (static_cast(pid) > 0 && static_cast(pid) < nPidStudy) - nTrackPid[static_cast(pid)]++; - } - - // Per eveniment (cel putin o particula din specie) — histogramele originale - hSph_vs_mult_pid_MC[0]->Fill(sph, static_cast(mBin)); - hMult_vs_sph_pid_MC[0]->Fill(static_cast(nch), static_cast(sphCls)); - for (int ip = 1; ip < nPidStudy; ++ip) { - if (nTrackPid[ip] > 0) { - hSph_vs_mult_pid_MC[ip]->Fill(sph, static_cast(mBin)); - hMult_vs_sph_pid_MC[ip]->Fill(static_cast(nch), static_cast(sphCls)); - } - } - - // Varianta B: per eveniment, PID leading - hSphLead_vs_mult_pid_MC[0]->Fill(sph, static_cast(mBin)); // all: sempre - hMultLead_vs_sph_pid_MC[0]->Fill(static_cast(nch), static_cast(sphCls)); - if (static_cast(pidLead) > 0 && static_cast(pidLead) < nPidStudy) { - hSphLead_vs_mult_pid_MC[static_cast(pidLead)]->Fill(sph, static_cast(mBin)); - hMultLead_vs_sph_pid_MC[static_cast(pidLead)]->Fill(static_cast(nch), static_cast(sphCls)); - } - - // Distributia N_ch reala per sphClass, per PID leading - hMultReal_vs_sph_pid_MC[0]->Fill(static_cast(nch), static_cast(sphCls)); - if (static_cast(pidLead) > 0 && static_cast(pidLead) < nPidStudy) - hMultReal_vs_sph_pid_MC[static_cast(pidLead)]->Fill(static_cast(nch), static_cast(sphCls)); - - // N_ch reala vs PID (per track — umplut o data per track) - // Umplut in bucla de trackuri de mai jos - // (hMultReal_vs_pid_MC se umple per eveniment cu PID dominant) - // Alegem: umplut per eveniment cu fiecare PID care exista in eveniment - hMultReal_vs_pid_MC->Fill(static_cast(nch), 0.); // all: intotdeauna - for (int ip = 1; ip < nPidStudy; ++ip) { - if (nTrackPid[ip] > 0) - hMultReal_vs_pid_MC->Fill(static_cast(nch), static_cast(ip)); - } - - // Varianta A: per TRACK — umplut in bucla de trackuri - for (const auto& t : selTracks) { - PidSpecies pid = pidFromPDG(t.pdg); - // all tracks - hSphTrack_vs_mult_pid_MC[0]->Fill(sph, static_cast(mBin)); - hMultTrack_vs_sph_pid_MC[0]->Fill(static_cast(nch), static_cast(sphCls)); - // specific PID - if (static_cast(pid) > 0 && static_cast(pid) < nPidStudy) { - hSphTrack_vs_mult_pid_MC[static_cast(pid)]->Fill(sph, static_cast(mBin)); - hMultTrack_vs_sph_pid_MC[static_cast(pid)]->Fill(static_cast(nch), static_cast(sphCls)); - } - } - - // ---- pT leading per (sph, PID) ------------------------------ - hPtLead_sph_pid_MC[sphCls][0]->Fill(pTlead, static_cast(mBin)); - hPtLead_sph_pid_MC[nSphClasses - 1][0]->Fill(pTlead, static_cast(mBin)); - if (static_cast(pidLead) > 0 && static_cast(pidLead) < nPidStudy) { - hPtLead_sph_pid_MC[sphCls][static_cast(pidLead)]->Fill(pTlead, static_cast(mBin)); - hPtLead_sph_pid_MC[nSphClasses - 1][static_cast(pidLead)]->Fill(pTlead, static_cast(mBin)); - } - - // ---- pT tuturor trackurilor per (sph, PID) ------------------ - for (const auto& t : selTracks) { - PidSpecies pid = pidFromPDG(t.pdg); - fillPtStudy(hPt_sph_pid_MC, sphCls, mBin, static_cast(t.pt), pid); - } -#endif // [DISABLED — histogram budget] categories 1,2,3,5,6,7 // ================================================================ // SAME-EVENT correlations // ================================================================ bool seHasPairs = false; for (int ia = 0; ia < nch; ++ia) { - if (ia == leadIdx) + if (ia == leadIdx) { continue; + } const auto& assoc = selTracks[ia]; - if (assoc.pt >= pTlead) + if (assoc.pt >= pTlead) { continue; // asociat pT < leading pT - if (assoc.pt < minAssocCorr || assoc.pt > maxAssocCorr) + } + if (assoc.pt < minAssocCorr || assoc.pt > maxAssocCorr) { continue; + } histos.fill(HIST("ptAssocHistogram_MC"), assoc.pt); const PidSpecies pidAssoc = pidFromPDG(assoc.pdg); - // pT associated study histograms [DISABLED — histogram budget, category 4] -#if 0 - fillPtStudy(hPtAssoc_sph_pid_MC, sphCls, mBin, static_cast(assoc.pt), pidAssoc); -#endif - const double dphi = computeDeltaPhi(phiLead, assoc.phi); + const double dphi = RecoDecay::constrainAngle(phiLead - assoc.phi, -o2::constants::math::PIHalf); const double deta = etaLead - assoc.eta; // TH2D SE: specific sph class (uses Delta-eta) @@ -1072,8 +771,9 @@ struct myExampleTask { seHasPairs = true; } - if (seHasPairs) + if (seHasPairs) { histos.fill(HIST("evSel_MC"), 8); + } // ---- hNtrig_MC per specie PID leading -------------------------- int pidLeadIdx = pidSpeciesToIdx(pidLead); @@ -1089,15 +789,17 @@ struct myExampleTask { { auto& pool = eventPools_MC[sphCls][zBin][mBin]; pool.push_back(selTracks); - if (static_cast(pool.size()) > mixPoolSize) + if (static_cast(pool.size()) > mixPoolSize) { pool.pop_front(); + } } // Pool "all" — all sph { auto& poolAll = eventPools_MC[nSphClasses - 1][zBin][mBin]; poolAll.push_back(selTracks); - if (static_cast(poolAll.size()) > mixPoolSize) + if (static_cast(poolAll.size()) > mixPoolSize) { poolAll.pop_front(); + } } // Pool per specie PID — contiene solo i track di quella specie @@ -1105,24 +807,28 @@ struct myExampleTask { // Costruisci un vettore con solo i track della specie ip std::vector pidTracks; for (const auto& t : selTracks) { - if (pidSpeciesToIdx(pidFromPDG(t.pdg)) == ip) + if (pidSpeciesToIdx(pidFromPDG(t.pdg)) == ip) { pidTracks.push_back(t); + } } - if (pidTracks.empty()) + if (pidTracks.empty()) { continue; + } // Specific sph pool { auto& pool = eventPoolsPID_MC[ip][sphCls][zBin][mBin]; pool.push_back(pidTracks); - if (static_cast(pool.size()) > mixPoolSize) + if (static_cast(pool.size()) > mixPoolSize) { pool.pop_front(); + } } // "all" sph pool { auto& pool = eventPoolsPID_MC[ip][nSphClasses - 1][zBin][mBin]; pool.push_back(pidTracks); - if (static_cast(pool.size()) > mixPoolSize) + if (static_cast(pool.size()) > mixPoolSize) { pool.pop_front(); + } } } @@ -1134,11 +840,13 @@ struct myExampleTask { const int nEvts = static_cast(pool.size()) - 1; for (int ie = 0; ie < nEvts; ++ie) { for (const auto& tr : pool[ie]) { - if (tr.pt >= pTlead) + if (tr.pt >= pTlead) { continue; - if (tr.pt < minAssocCorr || tr.pt > maxAssocCorr) + } + if (tr.pt < minAssocCorr || tr.pt > maxAssocCorr) { continue; - const double dphi = computeDeltaPhi(phiLead, tr.phi); + } + const double dphi = RecoDecay::constrainAngle(phiLead - tr.phi, -o2::constants::math::PIHalf); const double deta = etaLead - tr.eta; hME_MC[poolIdx][mBin]->Fill(dphi, deta); } @@ -1161,11 +869,13 @@ struct myExampleTask { const int nEvts = static_cast(pool.size()) - 1; for (int ie = 0; ie < nEvts; ++ie) { for (const auto& tr : pool[ie]) { - if (tr.pt >= pTlead) + if (tr.pt >= pTlead) { continue; - if (tr.pt < minAssocCorr || tr.pt > maxAssocCorr) + } + if (tr.pt < minAssocCorr || tr.pt > maxAssocCorr) { continue; - const double dphi = computeDeltaPhi(phiLead, tr.phi); + } + const double dphi = RecoDecay::constrainAngle(phiLead - tr.phi, -o2::constants::math::PIHalf); const double detaPID = etaLead - tr.eta; hMEpid_MC[pidLeadIdx][poolIdx][mBin]->Fill(dphi, detaPID); } @@ -1191,17 +901,19 @@ struct myExampleTask { histos.fill(HIST("evSel_Reco"), 1); // ---- event selection ---- - if (!col.sel8()) + if (!col.sel8()) { return; + } const double zvtx = col.posZ(); - if (std::fabs(zvtx) > maxZvtx) + if (std::fabs(zvtx) > maxZvtx) { return; + } histos.fill(HIST("evSel_Reco"), 2); histos.fill(HIST("Zvertex_Reco"), zvtx); // ---- one-pass: build selTracks + find leading --------------- std::vector selTracks; - selTracks.reserve(64); + selTracks.reserve(kMultEdges.back() - 1); int leadIdx = -1; double pTlead = -1., phiLead = 0., etaLead = 0.; @@ -1209,24 +921,29 @@ struct myExampleTask { hTrackCutDebug_Reco->Fill(1); // seen // Quality selection: global track without DCA (apply DCA separately) - if (!track.isGlobalTrackWoDCA()) + if (!track.isGlobalTrackWoDCA()) { continue; + } hTrackCutDebug_Reco->Fill(2); // passed isGlobalTrackWoDCA // DCA cuts - if (std::fabs(track.dcaXY()) > maxDcaXY) + if (std::fabs(track.dcaXY()) > maxDcaXY) { continue; + } hTrackCutDebug_Reco->Fill(3); // passed DCAxy - if (std::fabs(track.dcaZ()) > maxDcaZ) + if (std::fabs(track.dcaZ()) > maxDcaZ) { continue; + } hTrackCutDebug_Reco->Fill(4); // passed DCAz // Kinematic cuts (same as MC) - if (std::fabs(track.eta()) > maxEta) + if (std::fabs(track.eta()) > maxEta) { continue; + } hTrackCutDebug_Reco->Fill(5); // passed eta - if (track.pt() < minAssocPt) + if (track.pt() < minAssocPt) { continue; + } hTrackCutDebug_Reco->Fill(6); // passed pt (selected) // PID from nSigma (combined TPC+TOF, ALICE 2511.10399, unless the @@ -1240,23 +957,25 @@ struct myExampleTask { // |y| < maxRapPID cut applies ONLY to PID-identified species -- see // matching comment in processMC. Track stays in selTracks either way // (all-species channel unaffected); only the PID assignment is reset. - if (applyRapidityCutPID && pid != kUnidentified && std::fabs(rap) > maxRapPID) { - pid = kUnidentified; + if (applyRapidityCutPID && pid != kPidUnidentified && std::fabs(rap) > maxRapPID) { + pid = kPidUnidentified; } // pdg = 0 for reco (no MC truth); store sign for future like/unlike-sign int idx = static_cast(selTracks.size()); - selTracks.push_back({static_cast(track.phi()), static_cast(track.eta()), static_cast(track.pt()), - static_cast(rap), 0}); + selTracks.push_back({static_cast(track.phi()), static_cast(track.eta()), static_cast(track.pt()), 0}); // Store PID info in pdg field as signed pidIdx for later use: // sign(track.sign()) * (211 for pion, 321 for kaon, 2212 for proton, 0 for unid) int pdgLike = 0; - if (pid == kPion) + if (pid == kPidPion) { pdgLike = 211; - if (pid == kKaon) + } + if (pid == kPidKaon) { pdgLike = 321; - if (pid == kProton) + } + if (pid == kPidProton) { pdgLike = 2212; + } selTracks.back().pdg = track.sign() * pdgLike; histos.fill(HIST("etaHistogram_Reco"), track.eta()); @@ -1274,37 +993,44 @@ struct myExampleTask { const int nch = static_cast(selTracks.size()); histos.fill(HIST("multHist_Reco"), nch); - const int mBin = multBin(nch); - if (mBin < 0) + const int mBin = o2::analysis::findBin(&kMultEdges, nch); + if (mBin < 0) { return; + } histos.fill(HIST("evSel_Reco"), 3); - const int zBin = zvtxBin(zvtx); - if (zBin < 0) + const int zBin = o2::analysis::findBin(&kZvtxEdges, zvtx); + if (zBin < 0) { return; + } histos.fill(HIST("evSel_Reco"), 4); // ---- leading particle pT cut [minLeadPt, maxLeadPt] ----------- - if (leadIdx < 0) + if (leadIdx < 0) { return; - if (pTlead < minLeadPt || pTlead > maxLeadPt) + } + if (pTlead < minLeadPt || pTlead > maxLeadPt) { return; + } histos.fill(HIST("evSel_Reco"), 5); histos.fill(HIST("ptLeadHistogram_Reco"), pTlead); // ---- sphericity --------------------------------------------- const double sph = computeSphericity(selTracks); - if (sph >= 0.) + if (sph >= 0.) { histos.fill(HIST("sphericity_beforeCuts_Reco"), sph); - if (sph < 0.) + } + if (sph < 0.) { return; + } histos.fill(HIST("evSel_Reco"), 6); histos.fill(HIST("sphericity_Reco"), sph); hSphMult_Reco[mBin]->Fill(sph); const int sphCls = sphClass(sph); - if (sphCls < 0) + if (sphCls < 0) { return; + } histos.fill(HIST("evSel_Reco"), 7); const PidSpecies pidLead = pidFromPDG(selTracks[leadIdx].pdg); @@ -1313,99 +1039,27 @@ struct myExampleTask { hNtrig_Reco->Fill(static_cast(mBin), static_cast(sphCls)); hNtrig_Reco->Fill(static_cast(mBin), static_cast((nSphClasses - 1))); - // ================================================================ - // [DISABLED — histogram budget] Study histograms (identical to - // processMC). See matching #if 0 block in init()/bookChannel. - // ================================================================ -#if 0 - auto fillPtStudy = [&](std::array, nPidStudy>, nSphClasses>& arr, - int iSphCls, int im, double pt, PidSpecies pid) { - arr[iSphCls][0]->Fill(pt, static_cast(im)); - arr[nSphClasses - 1][0]->Fill(pt, static_cast(im)); - if (static_cast(pid) > 0 && static_cast(pid) < nPidStudy) { - arr[iSphCls][static_cast(pid)]->Fill(pt, static_cast(im)); - arr[nSphClasses - 1][static_cast(pid)]->Fill(pt, static_cast(im)); - } - }; - - int nTrackPid[nPidStudy] = {0, 0, 0, 0}; - for (const auto& t : selTracks) { - PidSpecies pid = pidFromPDG(t.pdg); - nTrackPid[0]++; - if (static_cast(pid) > 0 && static_cast(pid) < nPidStudy) - nTrackPid[static_cast(pid)]++; - } - - hSph_vs_mult_pid_Reco[0]->Fill(sph, static_cast(mBin)); - hMult_vs_sph_pid_Reco[0]->Fill(static_cast(nch), static_cast(sphCls)); - for (int ip = 1; ip < nPidStudy; ++ip) { - if (nTrackPid[ip] > 0) { - hSph_vs_mult_pid_Reco[ip]->Fill(sph, static_cast(mBin)); - hMult_vs_sph_pid_Reco[ip]->Fill(static_cast(nch), static_cast(sphCls)); - } - } - - hSphLead_vs_mult_pid_Reco[0]->Fill(sph, static_cast(mBin)); - hMultLead_vs_sph_pid_Reco[0]->Fill(static_cast(nch), static_cast(sphCls)); - if (static_cast(pidLead) > 0 && static_cast(pidLead) < nPidStudy) { - hSphLead_vs_mult_pid_Reco[static_cast(pidLead)]->Fill(sph, static_cast(mBin)); - hMultLead_vs_sph_pid_Reco[static_cast(pidLead)]->Fill(static_cast(nch), static_cast(sphCls)); - } - - hMultReal_vs_sph_pid_Reco[0]->Fill(static_cast(nch), static_cast(sphCls)); - if (static_cast(pidLead) > 0 && static_cast(pidLead) < nPidStudy) - hMultReal_vs_sph_pid_Reco[static_cast(pidLead)]->Fill(static_cast(nch), static_cast(sphCls)); - - hMultReal_vs_pid_Reco->Fill(static_cast(nch), 0.); - for (int ip = 1; ip < nPidStudy; ++ip) { - if (nTrackPid[ip] > 0) - hMultReal_vs_pid_Reco->Fill(static_cast(nch), static_cast(ip)); - } - - for (const auto& t : selTracks) { - PidSpecies pid = pidFromPDG(t.pdg); - hSphTrack_vs_mult_pid_Reco[0]->Fill(sph, static_cast(mBin)); - hMultTrack_vs_sph_pid_Reco[0]->Fill(static_cast(nch), static_cast(sphCls)); - if (static_cast(pid) > 0 && static_cast(pid) < nPidStudy) { - hSphTrack_vs_mult_pid_Reco[static_cast(pid)]->Fill(sph, static_cast(mBin)); - hMultTrack_vs_sph_pid_Reco[static_cast(pid)]->Fill(static_cast(nch), static_cast(sphCls)); - } - } - - hPtLead_sph_pid_Reco[sphCls][0]->Fill(pTlead, static_cast(mBin)); - hPtLead_sph_pid_Reco[nSphClasses - 1][0]->Fill(pTlead, static_cast(mBin)); - if (static_cast(pidLead) > 0 && static_cast(pidLead) < nPidStudy) { - hPtLead_sph_pid_Reco[sphCls][static_cast(pidLead)]->Fill(pTlead, static_cast(mBin)); - hPtLead_sph_pid_Reco[nSphClasses - 1][static_cast(pidLead)]->Fill(pTlead, static_cast(mBin)); - } - - for (const auto& t : selTracks) { - PidSpecies pid = pidFromPDG(t.pdg); - fillPtStudy(hPt_sph_pid_Reco, sphCls, mBin, static_cast(t.pt), pid); - } -#endif // [DISABLED — histogram budget] categories 1,2,3,5,6,7 - // ================================================================ // SAME-EVENT correlations (identical logic to processMC) // ================================================================ bool seHasPairs = false; for (int ia = 0; ia < nch; ++ia) { - if (ia == leadIdx) + if (ia == leadIdx) { continue; + } const auto& assoc = selTracks[ia]; - if (assoc.pt >= pTlead) + if (assoc.pt >= pTlead) { continue; - if (assoc.pt < minAssocCorr || assoc.pt > maxAssocCorr) + } + if (assoc.pt < minAssocCorr || assoc.pt > maxAssocCorr) { continue; + } histos.fill(HIST("ptAssocHistogram_Reco"), assoc.pt); const PidSpecies pidAssoc = pidFromPDG(assoc.pdg); -#if 0 - fillPtStudy(hPtAssoc_sph_pid_Reco, sphCls, mBin, static_cast(assoc.pt), pidAssoc); -#endif // [DISABLED — histogram budget, category 4] - const double dphi = computeDeltaPhi(phiLead, assoc.phi); + const double dphi = RecoDecay::constrainAngle(phiLead - assoc.phi, -o2::constants::math::PIHalf); const double deta = etaLead - assoc.eta; // All-species SE (Delta-eta) @@ -1423,8 +1077,9 @@ struct myExampleTask { seHasPairs = true; } - if (seHasPairs) + if (seHasPairs) { histos.fill(HIST("evSel_Reco"), 8); + } // ---- hNtrig_Reco per specie PID leading -------------------------- int pidLeadIdx = pidSpeciesToIdx(pidLead); @@ -1439,35 +1094,41 @@ struct myExampleTask { { auto& pool = eventPools_Reco[sphCls][zBin][mBin]; pool.push_back(selTracks); - if (static_cast(pool.size()) > mixPoolSize) + if (static_cast(pool.size()) > mixPoolSize) { pool.pop_front(); + } } { auto& poolAll = eventPools_Reco[nSphClasses - 1][zBin][mBin]; poolAll.push_back(selTracks); - if (static_cast(poolAll.size()) > mixPoolSize) + if (static_cast(poolAll.size()) > mixPoolSize) { poolAll.pop_front(); + } } for (int ip = 0; ip < nPidCorr; ++ip) { std::vector pidTracks; for (const auto& t : selTracks) { - if (pidSpeciesToIdx(pidFromPDG(t.pdg)) == ip) + if (pidSpeciesToIdx(pidFromPDG(t.pdg)) == ip) { pidTracks.push_back(t); + } } - if (pidTracks.empty()) + if (pidTracks.empty()) { continue; + } { auto& pool = eventPoolsPID_Reco[ip][sphCls][zBin][mBin]; pool.push_back(pidTracks); - if (static_cast(pool.size()) > mixPoolSize) + if (static_cast(pool.size()) > mixPoolSize) { pool.pop_front(); + } } { auto& pool = eventPoolsPID_Reco[ip][nSphClasses - 1][zBin][mBin]; pool.push_back(pidTracks); - if (static_cast(pool.size()) > mixPoolSize) + if (static_cast(pool.size()) > mixPoolSize) { pool.pop_front(); + } } } @@ -1479,11 +1140,13 @@ struct myExampleTask { const int nEvts = static_cast(pool.size()) - 1; for (int ie = 0; ie < nEvts; ++ie) { for (const auto& tr : pool[ie]) { - if (tr.pt >= pTlead) + if (tr.pt >= pTlead) { continue; - if (tr.pt < minAssocCorr || tr.pt > maxAssocCorr) + } + if (tr.pt < minAssocCorr || tr.pt > maxAssocCorr) { continue; - const double dphi = computeDeltaPhi(phiLead, tr.phi); + } + const double dphi = RecoDecay::constrainAngle(phiLead - tr.phi, -o2::constants::math::PIHalf); const double deta = etaLead - tr.eta; hME_Reco[poolIdx][mBin]->Fill(dphi, deta); } @@ -1503,11 +1166,13 @@ struct myExampleTask { const int nEvts = static_cast(pool.size()) - 1; for (int ie = 0; ie < nEvts; ++ie) { for (const auto& tr : pool[ie]) { - if (tr.pt >= pTlead) + if (tr.pt >= pTlead) { continue; - if (tr.pt < minAssocCorr || tr.pt > maxAssocCorr) + } + if (tr.pt < minAssocCorr || tr.pt > maxAssocCorr) { continue; - const double dphi = computeDeltaPhi(phiLead, tr.phi); + } + const double dphi = RecoDecay::constrainAngle(phiLead - tr.phi, -o2::constants::math::PIHalf); const double detaPID = etaLead - tr.eta; hMEpid_Reco[pidLeadIdx][poolIdx][mBin]->Fill(dphi, detaPID); } @@ -1536,31 +1201,40 @@ struct myExampleTask { TrackRecoMC const& tracks, aod::McParticles const&) { - if (!col.sel8()) + if (!col.sel8()) { return; - if (std::fabs(col.posZ()) > maxZvtx) + } + if (std::fabs(col.posZ()) > maxZvtx) { return; + } for (const auto& track : tracks) { - if (!track.has_mcParticle()) + if (!track.has_mcParticle()) { continue; + } auto mcPart = track.mcParticle(); - if (!mcPart.isPhysicalPrimary()) + if (!mcPart.isPhysicalPrimary()) { continue; - auto* pdgPtr = TDatabasePDG::Instance()->GetParticle(mcPart.pdgCode()); - if (!pdgPtr || std::fabs(pdgPtr->Charge()) < 0.1) + } + auto* pdgPtr = pdg->GetParticle(mcPart.pdgCode()); + if (!pdgPtr || std::fabs(pdgPtr->Charge()) < 0.1) { continue; + } // Same track-level cuts as processReco, so the matrix reflects the // actual analyzed track population, not an unfiltered sample. - if (std::fabs(track.dcaXY()) > maxDcaXY) + if (std::fabs(track.dcaXY()) > maxDcaXY) { continue; - if (std::fabs(track.dcaZ()) > maxDcaZ) + } + if (std::fabs(track.dcaZ()) > maxDcaZ) { continue; - if (std::fabs(track.eta()) > maxEta) + } + if (std::fabs(track.eta()) > maxEta) { continue; - if (track.pt() < minAssocPt) + } + if (track.pt() < minAssocPt) { continue; + } PidSpecies truePid = pidFromPDG(mcPart.pdgCode()); PidSpecies taggedPid = pidFromNSigma(track, maxNSigmaPID, maxNSigmaPIDAmbig, @@ -1572,11 +1246,12 @@ struct myExampleTask { // effect on this matrix (found 2026-08-14, comparing Phase 1 // rapidityOnly against baseline: identical confusion-matrix numbers // despite different processReco yields, before this fix). - if (applyRapidityCutPID && taggedPid != kUnidentified) { + if (applyRapidityCutPID && taggedPid != kPidUnidentified) { double tagMass = pidMass(taggedPid); double tagRap = computeRapidity(track.pt(), track.eta(), tagMass); - if (std::fabs(tagRap) > maxRapPID) - taggedPid = kUnidentified; + if (std::fabs(tagRap) > maxRapPID) { + taggedPid = kPidUnidentified; + } } histos.fill(HIST("hPidConfusion_MC"), track.pt(), static_cast(truePid), static_cast(taggedPid)); From 473e7e6f510a61c8b11a05040672c2645d5b3a8a Mon Sep 17 00:00:00 2001 From: Madalina Tarzila Date: Fri, 9 Oct 2026 11:29:27 +0300 Subject: [PATCH 7/8] --- .../twoParticleCorrelationsMultSpher.cxx | 452 +++++++++--------- 1 file changed, 226 insertions(+), 226 deletions(-) diff --git a/PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationsMultSpher.cxx b/PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationsMultSpher.cxx index 56685bd98a6..897093cc2a1 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationsMultSpher.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationsMultSpher.cxx @@ -9,6 +9,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. // +/// \file twoParticleCorrelationsMultSpher.cxx /// \brief Two-particle angular correlations in pp collisions at 13 TeV. /// Structured in parallel with the AliRoot AliMESppColTask analysis. /// MC truth level: McCollision + McParticles. @@ -18,8 +19,8 @@ /// direct normalisation (both per-trigger and per-pair). /// /// Pool structure (separate for MC generated and Reco reconstructed data): -/// eventPools_MC[sphClass][zvtxBin][multBin] -/// eventPools_Reco[sphClass][zvtxBin][multBin] +/// eventPoolsMC[sphClass][zvtxBin][multBin] +/// eventPoolsReco[sphClass][zvtxBin][multBin] /// sphClass 0 : S_T in (0.0, 0.3] — jetty /// sphClass 1 : S_T in (0.3, 0.6] — intermediate /// sphClass 2 : S_T in (0.6, 1.0] — isotropic @@ -37,6 +38,7 @@ #include "Common/DataModel/TrackSelectionTables.h" #include +#include #include #include #include @@ -51,12 +53,13 @@ #include #include #include -#include +#include #include #include #include #include +#include #include #include #include @@ -69,22 +72,22 @@ using namespace o2::framework::expressions; // ============================================================ // PID species // ============================================================ -enum PidSpecies { kPidUnidentified = 0, - kPidPion, - kPidKaon, - kPidProton, - kNPidSpecies }; -static constexpr std::array kPidNames{"unid", "pion", "kaon", "proton"}; +enum PidSpecies { PidUnidentified = 0, + PidPion, + PidKaon, + PidProton, + NPidSpecies }; +static constexpr std::array PidNames{"unid", "pion", "kaon", "proton"}; // ============================================================ // Sphericity classes // ============================================================ -static constexpr int nSphClasses = 4; -static constexpr std::array kSphMax{0.3, 0.6, 1.0, 1.0}; -static constexpr std::array kSphLabels{"jetty", "intermediate", "isotropic", "all"}; +static constexpr int NSphClasses = 4; +static constexpr std::array SphMax{0.3, 0.6, 1.0, 1.0}; +static constexpr std::array SphLabels{"jetty", "intermediate", "isotropic", "all"}; // ============================================================ -struct myExampleTask { +struct TwoParticleCorrelationsMultSpher { // ---------------------------------------------------------------- // Configurables @@ -117,7 +120,7 @@ struct myExampleTask { // 2026-08-11; an earlier draft of this instruction conflated the two // into a single 2-sigma threshold, corrected here). Configurable maxNSigmaPID{"maxNSigmaPID", 2.0, "Max combined N_sigma,PID for species selection (ALICE 2511.10399)"}; - Configurable maxNSigmaPIDAmbig{"maxNSigmaPIDAmbig", 3.0, "N_sigma,PID threshold for ambiguity rejection (>1 species below this -> kPidUnidentified)"}; + Configurable maxNSigmaPIDAmbig{"maxNSigmaPIDAmbig", 3.0, "N_sigma,PID threshold for ambiguity rejection (>1 species below this -> PidUnidentified)"}; // Pre-2026-08-11 PID thresholds, restored (not reused for anything else) // so the legacy gating style below can be toggled back on without @@ -156,24 +159,29 @@ struct myExampleTask { // NOTE: no PID in pool — ME does not use PID // ---------------------------------------------------------------- struct TrackSimple { - float phi; - float eta; - float pt; - int pdg; // PDG code: used only for SE sparse/TH2; 0 for reco + float phi = 0.f; + float eta = 0.f; + float pt = 0.f; + int pdg = 0; // PDG code: used only for SE sparse/TH2; 0 for reco }; // ---------------------------------------------------------------- // Event class constants // ---------------------------------------------------------------- - static constexpr int nMultBins = 6; - static constexpr int nZvtxBins = 5; + static constexpr float MaxValidNSigma = 100.f; // larger |nSigma| means the detector had no signal (-999) + static constexpr float MinPtCombinedPid = 0.5f; // GeV/c, above this TPC+TOF are combined + static constexpr std::size_t MinTracksSphericity = 3; + static constexpr double Epsilon = 1e-12; + static constexpr double MinCharge = 0.1; // charged particle: |q| in units of e + static constexpr int NMultBins = 6; + static constexpr int NZvtxBins = 5; // Bin edges: bin i spans [edge[i], edge[i+1]); values outside [front, back) give -1 in findBin. - static constexpr std::array kZvtxEdges{-10., -5., -2.5, 2.5, 5., 10.}; - static constexpr std::array kMultEdges{1, 10, 15, 20, 30, 46, 81}; + static constexpr std::array ZvtxEdges{-10., -5., -2.5, 2.5, 5., 10.}; + static constexpr std::array MultEdges{1, 10, 15, 20, 30, 46, 81}; static std::string multLabel(int im) { - return std::to_string(kMultEdges[im]) + "-" + std::to_string(kMultEdges[im + 1] - 1); + return std::to_string(MultEdges[im]) + "-" + std::to_string(MultEdges[im + 1] - 1); } // ---------------------------------------------------------------- @@ -182,29 +190,29 @@ struct myExampleTask { // [pidIdx][sphClass][zvtxBin][multBin] — per specie PID // pidIdx: 0=pion, 1=kaon, 2=proton // ---------------------------------------------------------------- - static constexpr int nPidCorr = 3; // pion, kaon, proton - static constexpr std::array kPidCorrNames{"pion", "kaon", "proton"}; + static constexpr int NPidCorr = 3; // pion, kaon, proton + static constexpr std::array PidCorrNames{"pion", "kaon", "proton"}; // PDG codes corrispondenti a pidIdx static int pidCorrPDG(int idx) { if (idx == 0) { - return 211; + return PDG_t::kPiPlus; } if (idx == 1) { - return 321; + return PDG_t::kKPlus; } - return 2212; + return PDG_t::kProton; } // Mappa PidSpecies -> pidIdx (-1 se non e' pion/kaon/proton) static int pidSpeciesToIdx(PidSpecies pid) { - if (pid == kPidPion) { + if (pid == PidPion) { return 0; } - if (pid == kPidKaon) { + if (pid == PidKaon) { return 1; } - if (pid == kPidProton) { + if (pid == PidProton) { return 2; } return -1; @@ -212,11 +220,11 @@ struct myExampleTask { using EventPool = std::deque>; // Pool "all" — separate per MC (generated) and Reco (reconstructed) - std::array, nZvtxBins>, nSphClasses> eventPools_MC; - std::array, nZvtxBins>, nSphClasses> eventPools_Reco; + std::array, NZvtxBins>, NSphClasses> eventPoolsMC; + std::array, NZvtxBins>, NSphClasses> eventPoolsReco; // Pool per specie PID (like-sign) — separate per MC e Reco - std::array, nZvtxBins>, nSphClasses>, nPidCorr> eventPoolsPID_MC; - std::array, nZvtxBins>, nSphClasses>, nPidCorr> eventPoolsPID_Reco; + std::array, NZvtxBins>, NSphClasses>, NPidCorr> eventPoolsPidMC; + std::array, NZvtxBins>, NSphClasses>, NPidCorr> eventPoolsPidReco; // ---------------------------------------------------------------- // Histogram registry + handles @@ -224,26 +232,26 @@ struct myExampleTask { HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; // SE/ME TH2D "all": [sphClass][multBin] — MC e Reco separati - std::array, nMultBins>, nSphClasses> hSE_MC{}; - std::array, nMultBins>, nSphClasses> hME_MC{}; - std::array, nMultBins>, nSphClasses> hSE_Reco{}; - std::array, nMultBins>, nSphClasses> hME_Reco{}; + std::array, NMultBins>, NSphClasses> hSeMC{}; + std::array, NMultBins>, NSphClasses> hMeMC{}; + std::array, NMultBins>, NSphClasses> hSeReco{}; + std::array, NMultBins>, NSphClasses> hMeReco{}; // SE/ME TH2D like-sign per specie: [pidIdx][sphClass][multBin] — MC e Reco separati - std::array, nMultBins>, nSphClasses>, nPidCorr> hSEpid_MC{}; - std::array, nMultBins>, nSphClasses>, nPidCorr> hMEpid_MC{}; - std::array, nMultBins>, nSphClasses>, nPidCorr> hSEpid_Reco{}; - std::array, nMultBins>, nSphClasses>, nPidCorr> hMEpid_Reco{}; + std::array, NMultBins>, NSphClasses>, NPidCorr> hSePidMC{}; + std::array, NMultBins>, NSphClasses>, NPidCorr> hMePidMC{}; + std::array, NMultBins>, NSphClasses>, NPidCorr> hSePidReco{}; + std::array, NMultBins>, NSphClasses>, NPidCorr> hMePidReco{}; // Trigger counter: axes = mult bin (x) x sphClass (y) — MC e Reco separati - std::shared_ptr hNtrig_MC; - std::shared_ptr hNtrig_Reco; - std::array, nPidCorr> hNtrigPID_MC{}; - std::array, nPidCorr> hNtrigPID_Reco{}; + std::shared_ptr hNtrigMC; + std::shared_ptr hNtrigReco; + std::array, NPidCorr> hNtrigPidMC{}; + std::array, NPidCorr> hNtrigPidReco{}; // Per-mult sphericity — MC e Reco separati - std::array, nMultBins> hSphMult_MC{}; - std::array, nMultBins> hSphMult_Reco{}; + std::array, NMultBins> hSphMultMC{}; + std::array, NMultBins> hSphMultReco{}; // ---------------------------------------------------------------- // [DEBUG, TEMPORARY] Track-cut counter for processReco diagnostics. @@ -251,7 +259,7 @@ struct myExampleTask { // 4=passed DCAz, 5=passed eta, 6=passed pt (selected). // Remove once the empty-Reco-histograms issue is understood/fixed. // ---------------------------------------------------------------- - std::shared_ptr hTrackCutDebug_Reco; + std::shared_ptr hTrackCutDebugReco; // ---------------------------------------------------------------- // PID confusion matrix (true species from PDG vs. tagged species from @@ -263,7 +271,7 @@ struct myExampleTask { // one or the other). See auto-memory / STATUS.md for the "rapid vs // riguros" design discussion this came out of. // ---------------------------------------------------------------- - std::shared_ptr hPidConfusion_MC; + std::shared_ptr hPidConfusionMC; // ---------------------------------------------------------------- // Helpers @@ -273,13 +281,13 @@ struct myExampleTask { { switch (std::abs(pdg)) { case 211: - return kPidPion; + return PidPion; case 321: - return kPidKaon; + return PidKaon; case 2212: - return kPidProton; + return PidProton; default: - return kPidUnidentified; + return PidUnidentified; } } @@ -299,7 +307,7 @@ struct myExampleTask { /// required whenever available regardless of pT. /// /// legacySelection (what to do with hypotheses that pass the gate): - /// false (default): reject the track outright (kPidUnidentified) if MORE + /// false (default): reject the track outright (PidUnidentified) if MORE /// THAN ONE hypothesis also falls below the looser ambiguity /// threshold maxAmbig (3 sigma) -- this comparison always uses the /// combined-quadrature N_sigma,PID, regardless of legacyGating, so @@ -318,17 +326,17 @@ struct myExampleTask { bool legacyGating, bool legacySelection) const { struct Hypo { - PidSpecies pid; - double nsTPC; - double nsTOF; + PidSpecies pid = PidUnidentified; + double nsTPC = 0.; + double nsTOF = 0.; }; - const std::array hypos{{{kPidPion, track.tpcNSigmaPi(), track.tofNSigmaPi()}, - {kPidKaon, track.tpcNSigmaKa(), track.tofNSigmaKa()}, - {kPidProton, track.tpcNSigmaPr(), track.tofNSigmaPr()}}}; + const std::array hypos{{{PidPion, track.tpcNSigmaPi(), track.tofNSigmaPi()}, + {PidKaon, track.tpcNSigmaKa(), track.tofNSigmaKa()}, + {PidProton, track.tpcNSigmaPr(), track.tofNSigmaPr()}}}; // TOF unavailable -> nSigma returns -999; check with any species - bool hasTOF = (std::fabs(track.tofNSigmaPi()) < 100.f); - bool useCombined = (track.pt() > 0.5) && hasTOF; // only feeds the new-style gate + bool hasTOF = (std::fabs(track.tofNSigmaPi()) < MaxValidNSigma); + bool useCombined = (track.pt() > MinPtCombinedPid) && hasTOF; // only feeds the new-style gate auto passesGate = [&](const Hypo& h) { if (legacyGating) { @@ -343,7 +351,7 @@ struct myExampleTask { if (legacySelection) { // Best-fit (min chi2) among hypotheses passing the gate -- no // ambiguity rejection (pre-2026-08-11 style). - PidSpecies best = kPidUnidentified; + PidSpecies best = PidUnidentified; double bestChi2 = 1e9; for (const auto& h : hypos) { if (!passesGate(h)) { @@ -360,7 +368,7 @@ struct myExampleTask { // Ambiguity-rejection selection (current default style). int nBelowAmbig = 0; - PidSpecies selected = kPidUnidentified; + PidSpecies selected = PidUnidentified; for (const auto& h : hypos) { if (!passesGate(h)) { continue; @@ -374,7 +382,7 @@ struct myExampleTask { selected = h.pid; } if (nBelowAmbig > 1) { - return kPidUnidentified; // ambiguous, reject + return PidUnidentified; // ambiguous, reject } return selected; } @@ -383,12 +391,12 @@ struct myExampleTask { static double pidMass(PidSpecies pid) { switch (pid) { - case kPidPion: - return 0.13957; - case kPidKaon: - return 0.49368; - case kPidProton: - return 0.93827; + case PidPion: + return o2::constants::physics::MassPiPlus; + case PidKaon: + return o2::constants::physics::MassKPlus; + case PidProton: + return o2::constants::physics::MassProton; default: return 0.; } @@ -402,8 +410,8 @@ struct myExampleTask { if (sph <= 0.) { return -1; } - for (int ic = 0; ic < nSphClasses - 1; ++ic) { - if (sph <= kSphMax[ic]) { + for (int ic = 0; ic < NSphClasses - 1; ++ic) { + if (sph <= SphMax[ic]) { return ic; } } @@ -412,31 +420,31 @@ struct myExampleTask { static double computeSphericity(const std::vector& tracks) { - if (tracks.size() < 3) { + if (tracks.size() < MinTracksSphericity) { return -1.0; } - double Sxx = 0., Sxy = 0., Syy = 0.; + double sxx = 0., sxy = 0., syy = 0.; for (const auto& t : tracks) { double px = t.pt * std::cos(t.phi); double py = t.pt * std::sin(t.phi); - Sxx += px * px; - Sxy += px * py; - Syy += py * py; + sxx += px * px; + sxy += px * py; + syy += py * py; } - double tr = Sxx + Syy; - double disc = tr * tr - 4.0 * (Sxx * Syy - Sxy * Sxy); + double tr = sxx + syy; + double disc = tr * tr - 4.0 * (sxx * syy - sxy * sxy); if (disc < 0.) { disc = 0.; } double lambdaMin = (tr - std::sqrt(disc)) / 2.0; - if (tr < 1e-12) { + if (tr < Epsilon) { return -1.0; } return std::clamp(2.0 * lambdaMin / tr, 0.0, 1.0); } /// Compute rapidity from pT, eta and particle mass. - /// y = 0.5 * ln((E + pz) / (E - pz)) + /// y = 0.5 * ln((energy + pz) / (energy - pz)) /// For unidentified particles (mass <= 0), returns eta. static double computeRapidity(double pt, double eta, double mass) { @@ -444,11 +452,11 @@ struct myExampleTask { return eta; // massless / unidentified -> y = eta } double pz = pt * std::sinh(eta); - double E = std::sqrt(mass * mass + pt * pt + pz * pz); - if (E <= std::fabs(pz) + 1e-12) { + double energy = std::sqrt(mass * mass + pt * pt + pz * pz); + if (energy <= std::fabs(pz) + Epsilon) { return eta; // safety } - return 0.5 * std::log((E + pz) / (E - pz)); + return 0.5 * std::log((energy + pz) / (energy - pz)); } // ---------------------------------------------------------------- @@ -459,8 +467,8 @@ struct myExampleTask { const AxisSpec axisZvtx{100, -15., 15., "z_{vtx} (cm)"}; const AxisSpec axisMult{100, 0., 180., "N_{ch}"}; const AxisSpec axisEta{30, -1.5, 1.5, "#eta"}; - const AxisSpec axisPhi{60, 0., 2. * TMath::Pi(), "#phi"}; - const AxisSpec axisDphi{30, -0.5 * TMath::Pi(), 1.5 * TMath::Pi(), "#Delta#phi"}; + const AxisSpec axisPhi{60, 0., o2::constants::math::TwoPI, "#phi"}; + const AxisSpec axisDphi{30, -o2::constants::math::PIHalf, o2::constants::math::PI + o2::constants::math::PIHalf, "#Delta#phi"}; const AxisSpec axisDeta{32, -1.6, 1.6, "#Delta#eta"}; // Reserved for a future rapidity-differentiated analysis -- unused // while the PID correlations run pseudorapidity-only (see @@ -476,13 +484,13 @@ struct myExampleTask { // Primeste referinte la toti membrii specifici canalului. // ---------------------------------------------------------------- auto bookChannel = [&](const char* suf, - std::array, nMultBins>, nSphClasses>& hSE_, - std::array, nMultBins>, nSphClasses>& hME_, - std::array, nMultBins>, nSphClasses>, nPidCorr>& hSEpid_, - std::array, nMultBins>, nSphClasses>, nPidCorr>& hMEpid_, - std::shared_ptr& hNtrig_, - std::array, nPidCorr>& hNtrigPID_, - std::array, nMultBins>& hSphMult_) { + std::array, NMultBins>, NSphClasses>& hSE, + std::array, NMultBins>, NSphClasses>& hME, + std::array, NMultBins>, NSphClasses>, NPidCorr>& hSEpid, + std::array, NMultBins>, NSphClasses>, NPidCorr>& hMEpid, + std::shared_ptr& hNtrig, + std::array, NPidCorr>& hNtrigPID, + std::array, NMultBins>& hSphMult) { // QA // evSel bins: 1=read 2=zvtx 3=multBin 4=zvtxBin 5=leadPt 6=ST_valid 7=sphClass 8=SE_filled auto hQA = histos.add(Form("evSel_%s", suf), Form("Event selection (%s)", suf), HistType::kTH1D, {{8, 0.5, 8.5}}); @@ -506,37 +514,37 @@ struct myExampleTask { histos.add(Form("sphericity_beforeCuts_%s", suf), Form("S_{T} (all evts with >=3 tracks, %s);S_{T};events", suf), kTH1D, {axisSph}); // Per-mult sphericity - for (int im = 0; im < nMultBins; ++im) { - hSphMult_[im] = histos.add( + for (int im = 0; im < NMultBins; ++im) { + hSphMult[im] = histos.add( Form("sphericity_mult%d_%s", im, suf), Form("S_T [%s] (%s);S_{T};events", multLabel(im).c_str(), suf), HistType::kTH1D, {axisSph}); } // Trigger counter TH2: x = multBin, y = sphClass - hNtrig_ = histos.add( - Form("hNtrig_%s", suf), + hNtrig = histos.add( + Form("hNtrig%s", suf), Form("N_{trig} (%s);mult bin;sph class", suf), HistType::kTH2D, - {{nMultBins, -0.5, static_cast(nMultBins) - 0.5}, - {nSphClasses, -0.5, static_cast(nSphClasses) - 0.5}}); - for (int im = 0; im < nMultBins; ++im) { - hNtrig_->GetXaxis()->SetBinLabel(im + 1, multLabel(im).c_str()); + {{NMultBins, -0.5, static_cast(NMultBins) - 0.5}, + {NSphClasses, -0.5, static_cast(NSphClasses) - 0.5}}); + for (int im = 0; im < NMultBins; ++im) { + hNtrig->GetXaxis()->SetBinLabel(im + 1, multLabel(im).c_str()); } - for (int ic = 0; ic < nSphClasses; ++ic) { - hNtrig_->GetYaxis()->SetBinLabel(ic + 1, kSphLabels[ic]); + for (int ic = 0; ic < NSphClasses; ++ic) { + hNtrig->GetYaxis()->SetBinLabel(ic + 1, SphLabels[ic]); } // SE/ME TH2D: [sphClass][multBin] - for (int ic = 0; ic < nSphClasses; ++ic) { - for (int im = 0; im < nMultBins; ++im) { - hSE_[ic][im] = histos.add( - Form("hSE_%s_sph%d_mult%d", suf, ic, im), - Form("SE [%s][%s] (%s);#Delta#phi;#Delta#eta", kSphLabels[ic], multLabel(im).c_str(), suf), + for (int ic = 0; ic < NSphClasses; ++ic) { + for (int im = 0; im < NMultBins; ++im) { + hSE[ic][im] = histos.add( + Form("hSE%s_sph%d_mult%d", suf, ic, im), + Form("SE [%s][%s] (%s);#Delta#phi;#Delta#eta", SphLabels[ic], multLabel(im).c_str(), suf), HistType::kTH2D, {axisDphi, axisDeta}); - hME_[ic][im] = histos.add( - Form("hME_%s_sph%d_mult%d", suf, ic, im), - Form("ME [%s][%s] (%s);#Delta#phi;#Delta#eta", kSphLabels[ic], multLabel(im).c_str(), suf), + hME[ic][im] = histos.add( + Form("hME%s_sph%d_mult%d", suf, ic, im), + Form("ME [%s][%s] (%s);#Delta#phi;#Delta#eta", SphLabels[ic], multLabel(im).c_str(), suf), HistType::kTH2D, {axisDphi, axisDeta}); } } @@ -544,75 +552,75 @@ struct myExampleTask { // ---- SE/ME like-sign per specie PID: [pidIdx][sphClass][multBin] --- // Pseudorapidity-only regime (confirmed 2026-09-15, see Claude Code // auto-memory eventshape-pid-pseudorapidity-only): PID correlations - // use Delta-eta, same as the inclusive hSE_/hME_ histograms, NOT + // use Delta-eta, same as the inclusive hSE/hME histograms, NOT // Delta-y. The rapidity infrastructure (computeRapidity(), the // per-track y field, axisDy below) is kept for a future // higher-statistics rapidity-differentiated analysis -- it is // deliberately unused here for now, not dead code to remove. - for (int ip = 0; ip < nPidCorr; ++ip) { - for (int ic = 0; ic < nSphClasses; ++ic) { - for (int im = 0; im < nMultBins; ++im) { - hSEpid_[ip][ic][im] = histos.add( - Form("hSE_%s_pid%d_sph%d_mult%d", suf, ip, ic, im), + for (int ip = 0; ip < NPidCorr; ++ip) { + for (int ic = 0; ic < NSphClasses; ++ic) { + for (int im = 0; im < NMultBins; ++im) { + hSEpid[ip][ic][im] = histos.add( + Form("hSE%s_pid%d_sph%d_mult%d", suf, ip, ic, im), Form("SE %s--%s [%s][%s] (%s);#Delta#varphi;#Delta#eta", - kPidCorrNames[ip], kPidCorrNames[ip], kSphLabels[ic], multLabel(im).c_str(), suf), + PidCorrNames[ip], PidCorrNames[ip], SphLabels[ic], multLabel(im).c_str(), suf), HistType::kTH2D, {axisDphi, axisDeta}); - hMEpid_[ip][ic][im] = histos.add( - Form("hME_%s_pid%d_sph%d_mult%d", suf, ip, ic, im), + hMEpid[ip][ic][im] = histos.add( + Form("hME%s_pid%d_sph%d_mult%d", suf, ip, ic, im), Form("ME %s--%s [%s][%s] (%s);#Delta#varphi;#Delta#eta", - kPidCorrNames[ip], kPidCorrNames[ip], kSphLabels[ic], multLabel(im).c_str(), suf), + PidCorrNames[ip], PidCorrNames[ip], SphLabels[ic], multLabel(im).c_str(), suf), HistType::kTH2D, {axisDphi, axisDeta}); } } // Trigger counter per specie PID - hNtrigPID_[ip] = histos.add( - Form("hNtrig_%s_pid%d", suf, ip), - Form("N_{trig} %s (%s);mult bin;sph class", kPidCorrNames[ip], suf), + hNtrigPID[ip] = histos.add( + Form("hNtrig%s_pid%d", suf, ip), + Form("N_{trig} %s (%s);mult bin;sph class", PidCorrNames[ip], suf), HistType::kTH2D, - {{nMultBins, -0.5, static_cast(nMultBins) - 0.5}, - {nSphClasses, -0.5, static_cast(nSphClasses) - 0.5}}); - for (int im = 0; im < nMultBins; ++im) { - hNtrigPID_[ip]->GetXaxis()->SetBinLabel(im + 1, multLabel(im).c_str()); + {{NMultBins, -0.5, static_cast(NMultBins) - 0.5}, + {NSphClasses, -0.5, static_cast(NSphClasses) - 0.5}}); + for (int im = 0; im < NMultBins; ++im) { + hNtrigPID[ip]->GetXaxis()->SetBinLabel(im + 1, multLabel(im).c_str()); } - for (int ic = 0; ic < nSphClasses; ++ic) { - hNtrigPID_[ip]->GetYaxis()->SetBinLabel(ic + 1, kSphLabels[ic]); + for (int ic = 0; ic < NSphClasses; ++ic) { + hNtrigPID[ip]->GetYaxis()->SetBinLabel(ic + 1, SphLabels[ic]); } } }; // Book histograms for MC (generated) channel bookChannel("MC", - hSE_MC, hME_MC, hSEpid_MC, hMEpid_MC, - hNtrig_MC, hNtrigPID_MC, hSphMult_MC); + hSeMC, hMeMC, hSePidMC, hMePidMC, + hNtrigMC, hNtrigPidMC, hSphMultMC); // Book histograms for Reco (reconstructed) channel bookChannel("Reco", - hSE_Reco, hME_Reco, hSEpid_Reco, hMEpid_Reco, - hNtrig_Reco, hNtrigPID_Reco, hSphMult_Reco); + hSeReco, hMeReco, hSePidReco, hMePidReco, + hNtrigReco, hNtrigPidReco, hSphMultReco); // [DEBUG, TEMPORARY] track-cut diagnostic counter for processReco - hTrackCutDebug_Reco = histos.add( + hTrackCutDebugReco = histos.add( "hTrackCutDebug_Reco", "Track cut flow (Reco, DEBUG);cut;tracks", HistType::kTH1D, {{6, 0.5, 6.5}}); - hTrackCutDebug_Reco->GetXaxis()->SetBinLabel(1, "seen"); - hTrackCutDebug_Reco->GetXaxis()->SetBinLabel(2, "passed isGlobalTrackWoDCA"); - hTrackCutDebug_Reco->GetXaxis()->SetBinLabel(3, "passed DCAxy"); - hTrackCutDebug_Reco->GetXaxis()->SetBinLabel(4, "passed DCAz"); - hTrackCutDebug_Reco->GetXaxis()->SetBinLabel(5, "passed eta"); - hTrackCutDebug_Reco->GetXaxis()->SetBinLabel(6, "passed pt (selected)"); + hTrackCutDebugReco->GetXaxis()->SetBinLabel(1, "seen"); + hTrackCutDebugReco->GetXaxis()->SetBinLabel(2, "passed isGlobalTrackWoDCA"); + hTrackCutDebugReco->GetXaxis()->SetBinLabel(3, "passed DCAxy"); + hTrackCutDebugReco->GetXaxis()->SetBinLabel(4, "passed DCAz"); + hTrackCutDebugReco->GetXaxis()->SetBinLabel(5, "passed eta"); + hTrackCutDebugReco->GetXaxis()->SetBinLabel(6, "passed pt (selected)"); // PID confusion matrix (rapid contamination estimate, 2026-08-14) -- // axes: pT, true species (PDG), tagged species (pidFromNSigma). // Species axis order matches PidSpecies: 0=unid,1=pion,2=kaon,3=proton. - const AxisSpec axisPidSpecies{static_cast(kNPidSpecies), -0.5, static_cast(kNPidSpecies) - 0.5, "species"}; - hPidConfusion_MC = histos.add( + const AxisSpec axisPidSpecies{static_cast(NPidSpecies), -0.5, static_cast(NPidSpecies) - 0.5, "species"}; + hPidConfusionMC = histos.add( "hPidConfusion_MC", "True vs. tagged PID species (MC);p_{T} (GeV/c);true species;tagged species", HistType::kTH3D, {axisPt2, axisPidSpecies, axisPidSpecies}); - for (int ip = 0; ip < static_cast(kNPidSpecies); ++ip) { - hPidConfusion_MC->GetYaxis()->SetBinLabel(ip + 1, kPidNames[ip]); - hPidConfusion_MC->GetZaxis()->SetBinLabel(ip + 1, kPidNames[ip]); + for (int ip = 0; ip < static_cast(NPidSpecies); ++ip) { + hPidConfusionMC->GetYaxis()->SetBinLabel(ip + 1, PidNames[ip]); + hPidConfusionMC->GetZaxis()->SetBinLabel(ip + 1, PidNames[ip]); } } @@ -633,7 +641,7 @@ struct myExampleTask { // ---- one-pass: build selTracks + find leading --------------- std::vector selTracks; - selTracks.reserve(kMultEdges.back() - 1); + selTracks.reserve(MultEdges.back() - 1); int leadIdx = -1; double pTlead = -1., phiLead = 0., etaLead = 0.; @@ -642,7 +650,7 @@ struct myExampleTask { continue; } auto* pdgPtr = pdg->GetParticle(p.pdgCode()); - if (!pdgPtr || std::fabs(pdgPtr->Charge()) < 0.1) { + if (!pdgPtr || std::fabs(pdgPtr->Charge()) < MinCharge) { continue; } if (std::fabs(p.eta()) > maxEta) { @@ -659,10 +667,10 @@ struct myExampleTask { // (pion/kaon/proton) -- the all-species channel (hSE/hME) keeps the // track regardless, gated only by |eta| < maxEta above. Clean way to // do this: keep pdg for all-species bookkeeping, but zero it (-> - // kPidUnidentified via pidFromPDG) when the rapidity cut fails, so the + // PidUnidentified via pidFromPDG) when the rapidity cut fails, so the // track contributes to hSE/hME but not hSEpid/hMEpid. int pdgForPID = p.pdgCode(); - if (applyRapidityCutPID && pidFromPDG(pdgForPID) != kPidUnidentified && std::fabs(rap) > maxRapPID) { + if (applyRapidityCutPID && pidFromPDG(pdgForPID) != PidUnidentified && std::fabs(rap) > maxRapPID) { pdgForPID = 0; } @@ -684,13 +692,13 @@ struct myExampleTask { const int nch = static_cast(selTracks.size()); histos.fill(HIST("multHist_MC"), nch); - const int mBin = o2::analysis::findBin(&kMultEdges, nch); + const int mBin = o2::analysis::findBin(&MultEdges, nch); if (mBin < 0) { return; } histos.fill(HIST("evSel_MC"), 3); - const int zBin = o2::analysis::findBin(&kZvtxEdges, zvtx); + const int zBin = o2::analysis::findBin(&ZvtxEdges, zvtx); if (zBin < 0) { return; } @@ -717,7 +725,7 @@ struct myExampleTask { } histos.fill(HIST("evSel_MC"), 6); histos.fill(HIST("sphericity_MC"), sph); - hSphMult_MC[mBin]->Fill(sph); + hSphMultMC[mBin]->Fill(sph); const int sphCls = sphClass(sph); // 0,1,2 or -1 if (sphCls < 0) { @@ -728,8 +736,8 @@ struct myExampleTask { const PidSpecies pidLead = pidFromPDG(selTracks[leadIdx].pdg); // ---- trigger counter ---------------------------------------- - hNtrig_MC->Fill(static_cast(mBin), static_cast(sphCls)); - hNtrig_MC->Fill(static_cast(mBin), static_cast((nSphClasses - 1))); // "all" + hNtrigMC->Fill(static_cast(mBin), static_cast(sphCls)); + hNtrigMC->Fill(static_cast(mBin), static_cast((NSphClasses - 1))); // "all" // ================================================================ // SAME-EVENT correlations @@ -755,9 +763,9 @@ struct myExampleTask { const double deta = etaLead - assoc.eta; // TH2D SE: specific sph class (uses Delta-eta) - hSE_MC[sphCls][mBin]->Fill(dphi, deta); + hSeMC[sphCls][mBin]->Fill(dphi, deta); // TH2D SE: "all" class (uses Delta-eta) - hSE_MC[nSphClasses - 1][mBin]->Fill(dphi, deta); + hSeMC[NSphClasses - 1][mBin]->Fill(dphi, deta); // SE like-sign: fill solo se trigger e associato sono la stessa specie // Uses Delta-eta (pseudorapidity-only regime, see @@ -765,8 +773,8 @@ struct myExampleTask { int pidLeadIdx = pidSpeciesToIdx(pidLead); int pidAssocIdx = pidSpeciesToIdx(pidAssoc); if (pidLeadIdx >= 0 && pidLeadIdx == pidAssocIdx) { - hSEpid_MC[pidLeadIdx][sphCls][mBin]->Fill(dphi, deta); - hSEpid_MC[pidLeadIdx][nSphClasses - 1][mBin]->Fill(dphi, deta); + hSePidMC[pidLeadIdx][sphCls][mBin]->Fill(dphi, deta); + hSePidMC[pidLeadIdx][NSphClasses - 1][mBin]->Fill(dphi, deta); } seHasPairs = true; @@ -775,11 +783,11 @@ struct myExampleTask { histos.fill(HIST("evSel_MC"), 8); } - // ---- hNtrig_MC per specie PID leading -------------------------- + // ---- hNtrigMC per specie PID leading -------------------------- int pidLeadIdx = pidSpeciesToIdx(pidLead); if (pidLeadIdx >= 0) { - hNtrigPID_MC[pidLeadIdx]->Fill(static_cast(mBin), static_cast(sphCls)); - hNtrigPID_MC[pidLeadIdx]->Fill(static_cast(mBin), static_cast((nSphClasses - 1))); + hNtrigPidMC[pidLeadIdx]->Fill(static_cast(mBin), static_cast(sphCls)); + hNtrigPidMC[pidLeadIdx]->Fill(static_cast(mBin), static_cast((NSphClasses - 1))); } // ================================================================ @@ -787,7 +795,7 @@ struct myExampleTask { // ================================================================ // Pool "all" — specific sph { - auto& pool = eventPools_MC[sphCls][zBin][mBin]; + auto& pool = eventPoolsMC[sphCls][zBin][mBin]; pool.push_back(selTracks); if (static_cast(pool.size()) > mixPoolSize) { pool.pop_front(); @@ -795,7 +803,7 @@ struct myExampleTask { } // Pool "all" — all sph { - auto& poolAll = eventPools_MC[nSphClasses - 1][zBin][mBin]; + auto& poolAll = eventPoolsMC[NSphClasses - 1][zBin][mBin]; poolAll.push_back(selTracks); if (static_cast(poolAll.size()) > mixPoolSize) { poolAll.pop_front(); @@ -803,7 +811,7 @@ struct myExampleTask { } // Pool per specie PID — contiene solo i track di quella specie - for (int ip = 0; ip < nPidCorr; ++ip) { + for (int ip = 0; ip < NPidCorr; ++ip) { // Costruisci un vettore con solo i track della specie ip std::vector pidTracks; for (const auto& t : selTracks) { @@ -816,7 +824,7 @@ struct myExampleTask { } // Specific sph pool { - auto& pool = eventPoolsPID_MC[ip][sphCls][zBin][mBin]; + auto& pool = eventPoolsPidMC[ip][sphCls][zBin][mBin]; pool.push_back(pidTracks); if (static_cast(pool.size()) > mixPoolSize) { pool.pop_front(); @@ -824,7 +832,7 @@ struct myExampleTask { } // "all" sph pool { - auto& pool = eventPoolsPID_MC[ip][nSphClasses - 1][zBin][mBin]; + auto& pool = eventPoolsPidMC[ip][NSphClasses - 1][zBin][mBin]; pool.push_back(pidTracks); if (static_cast(pool.size()) > mixPoolSize) { pool.pop_front(); @@ -836,7 +844,7 @@ struct myExampleTask { // MIXED-EVENT "all" // ================================================================ auto doMixing = [&](int poolIdx) { - const auto& pool = eventPools_MC[poolIdx][zBin][mBin]; + const auto& pool = eventPoolsMC[poolIdx][zBin][mBin]; const int nEvts = static_cast(pool.size()) - 1; for (int ie = 0; ie < nEvts; ++ie) { for (const auto& tr : pool[ie]) { @@ -848,13 +856,13 @@ struct myExampleTask { } const double dphi = RecoDecay::constrainAngle(phiLead - tr.phi, -o2::constants::math::PIHalf); const double deta = etaLead - tr.eta; - hME_MC[poolIdx][mBin]->Fill(dphi, deta); + hMeMC[poolIdx][mBin]->Fill(dphi, deta); } } }; doMixing(sphCls); - doMixing(nSphClasses - 1); + doMixing(NSphClasses - 1); // ================================================================ // MIXED-EVENT like-sign per specie PID @@ -865,7 +873,7 @@ struct myExampleTask { // ================================================================ if (pidLeadIdx >= 0) { auto doMixingPID = [&](int poolIdx) { - const auto& pool = eventPoolsPID_MC[pidLeadIdx][poolIdx][zBin][mBin]; + const auto& pool = eventPoolsPidMC[pidLeadIdx][poolIdx][zBin][mBin]; const int nEvts = static_cast(pool.size()) - 1; for (int ie = 0; ie < nEvts; ++ie) { for (const auto& tr : pool[ie]) { @@ -877,15 +885,15 @@ struct myExampleTask { } const double dphi = RecoDecay::constrainAngle(phiLead - tr.phi, -o2::constants::math::PIHalf); const double detaPID = etaLead - tr.eta; - hMEpid_MC[pidLeadIdx][poolIdx][mBin]->Fill(dphi, detaPID); + hMePidMC[pidLeadIdx][poolIdx][mBin]->Fill(dphi, detaPID); } } }; doMixingPID(sphCls); - doMixingPID(nSphClasses - 1); + doMixingPID(NSphClasses - 1); } } - PROCESS_SWITCH(myExampleTask, processMC, "Process MC truth", true); + PROCESS_SWITCH(TwoParticleCorrelationsMultSpher, processMC, "Process MC truth", true); // ================================================================ // processReco — reconstructed data (Collision + Tracks) @@ -913,38 +921,38 @@ struct myExampleTask { // ---- one-pass: build selTracks + find leading --------------- std::vector selTracks; - selTracks.reserve(kMultEdges.back() - 1); + selTracks.reserve(MultEdges.back() - 1); int leadIdx = -1; double pTlead = -1., phiLead = 0., etaLead = 0.; for (const auto& track : tracks) { - hTrackCutDebug_Reco->Fill(1); // seen + hTrackCutDebugReco->Fill(1); // seen // Quality selection: global track without DCA (apply DCA separately) if (!track.isGlobalTrackWoDCA()) { continue; } - hTrackCutDebug_Reco->Fill(2); // passed isGlobalTrackWoDCA + hTrackCutDebugReco->Fill(2); // passed isGlobalTrackWoDCA // DCA cuts if (std::fabs(track.dcaXY()) > maxDcaXY) { continue; } - hTrackCutDebug_Reco->Fill(3); // passed DCAxy + hTrackCutDebugReco->Fill(3); // passed DCAxy if (std::fabs(track.dcaZ()) > maxDcaZ) { continue; } - hTrackCutDebug_Reco->Fill(4); // passed DCAz + hTrackCutDebugReco->Fill(4); // passed DCAz // Kinematic cuts (same as MC) if (std::fabs(track.eta()) > maxEta) { continue; } - hTrackCutDebug_Reco->Fill(5); // passed eta + hTrackCutDebugReco->Fill(5); // passed eta if (track.pt() < minAssocPt) { continue; } - hTrackCutDebug_Reco->Fill(6); // passed pt (selected) + hTrackCutDebugReco->Fill(6); // passed pt (selected) // PID from nSigma (combined TPC+TOF, ALICE 2511.10399, unless the // legacy flags below switch to pre-2026-08-11 gating/selection) @@ -957,8 +965,8 @@ struct myExampleTask { // |y| < maxRapPID cut applies ONLY to PID-identified species -- see // matching comment in processMC. Track stays in selTracks either way // (all-species channel unaffected); only the PID assignment is reset. - if (applyRapidityCutPID && pid != kPidUnidentified && std::fabs(rap) > maxRapPID) { - pid = kPidUnidentified; + if (applyRapidityCutPID && pid != PidUnidentified && std::fabs(rap) > maxRapPID) { + pid = PidUnidentified; } // pdg = 0 for reco (no MC truth); store sign for future like/unlike-sign @@ -966,16 +974,8 @@ struct myExampleTask { selTracks.push_back({static_cast(track.phi()), static_cast(track.eta()), static_cast(track.pt()), 0}); // Store PID info in pdg field as signed pidIdx for later use: // sign(track.sign()) * (211 for pion, 321 for kaon, 2212 for proton, 0 for unid) - int pdgLike = 0; - if (pid == kPidPion) { - pdgLike = 211; - } - if (pid == kPidKaon) { - pdgLike = 321; - } - if (pid == kPidProton) { - pdgLike = 2212; - } + const int pidIdx = pidSpeciesToIdx(pid); + const int pdgLike = pidIdx >= 0 ? pidCorrPDG(pidIdx) : 0; selTracks.back().pdg = track.sign() * pdgLike; histos.fill(HIST("etaHistogram_Reco"), track.eta()); @@ -993,13 +993,13 @@ struct myExampleTask { const int nch = static_cast(selTracks.size()); histos.fill(HIST("multHist_Reco"), nch); - const int mBin = o2::analysis::findBin(&kMultEdges, nch); + const int mBin = o2::analysis::findBin(&MultEdges, nch); if (mBin < 0) { return; } histos.fill(HIST("evSel_Reco"), 3); - const int zBin = o2::analysis::findBin(&kZvtxEdges, zvtx); + const int zBin = o2::analysis::findBin(&ZvtxEdges, zvtx); if (zBin < 0) { return; } @@ -1025,7 +1025,7 @@ struct myExampleTask { } histos.fill(HIST("evSel_Reco"), 6); histos.fill(HIST("sphericity_Reco"), sph); - hSphMult_Reco[mBin]->Fill(sph); + hSphMultReco[mBin]->Fill(sph); const int sphCls = sphClass(sph); if (sphCls < 0) { @@ -1036,8 +1036,8 @@ struct myExampleTask { const PidSpecies pidLead = pidFromPDG(selTracks[leadIdx].pdg); // ---- trigger counter ---------------------------------------- - hNtrig_Reco->Fill(static_cast(mBin), static_cast(sphCls)); - hNtrig_Reco->Fill(static_cast(mBin), static_cast((nSphClasses - 1))); + hNtrigReco->Fill(static_cast(mBin), static_cast(sphCls)); + hNtrigReco->Fill(static_cast(mBin), static_cast((NSphClasses - 1))); // ================================================================ // SAME-EVENT correlations (identical logic to processMC) @@ -1063,16 +1063,16 @@ struct myExampleTask { const double deta = etaLead - assoc.eta; // All-species SE (Delta-eta) - hSE_Reco[sphCls][mBin]->Fill(dphi, deta); - hSE_Reco[nSphClasses - 1][mBin]->Fill(dphi, deta); + hSeReco[sphCls][mBin]->Fill(dphi, deta); + hSeReco[NSphClasses - 1][mBin]->Fill(dphi, deta); // PID SE — same species, Delta-eta (pseudorapidity-only regime, see // eventshape-pid-pseudorapidity-only auto-memory) int pidLeadIdx = pidSpeciesToIdx(pidLead); int pidAssocIdx = pidSpeciesToIdx(pidAssoc); if (pidLeadIdx >= 0 && pidLeadIdx == pidAssocIdx) { - hSEpid_Reco[pidLeadIdx][sphCls][mBin]->Fill(dphi, deta); - hSEpid_Reco[pidLeadIdx][nSphClasses - 1][mBin]->Fill(dphi, deta); + hSePidReco[pidLeadIdx][sphCls][mBin]->Fill(dphi, deta); + hSePidReco[pidLeadIdx][NSphClasses - 1][mBin]->Fill(dphi, deta); } seHasPairs = true; @@ -1081,32 +1081,32 @@ struct myExampleTask { histos.fill(HIST("evSel_Reco"), 8); } - // ---- hNtrig_Reco per specie PID leading -------------------------- + // ---- hNtrigReco per specie PID leading -------------------------- int pidLeadIdx = pidSpeciesToIdx(pidLead); if (pidLeadIdx >= 0) { - hNtrigPID_Reco[pidLeadIdx]->Fill(static_cast(mBin), static_cast(sphCls)); - hNtrigPID_Reco[pidLeadIdx]->Fill(static_cast(mBin), static_cast((nSphClasses - 1))); + hNtrigPidReco[pidLeadIdx]->Fill(static_cast(mBin), static_cast(sphCls)); + hNtrigPidReco[pidLeadIdx]->Fill(static_cast(mBin), static_cast((NSphClasses - 1))); } // ================================================================ // Update pools AFTER SE (identical to processMC) // ================================================================ { - auto& pool = eventPools_Reco[sphCls][zBin][mBin]; + auto& pool = eventPoolsReco[sphCls][zBin][mBin]; pool.push_back(selTracks); if (static_cast(pool.size()) > mixPoolSize) { pool.pop_front(); } } { - auto& poolAll = eventPools_Reco[nSphClasses - 1][zBin][mBin]; + auto& poolAll = eventPoolsReco[NSphClasses - 1][zBin][mBin]; poolAll.push_back(selTracks); if (static_cast(poolAll.size()) > mixPoolSize) { poolAll.pop_front(); } } - for (int ip = 0; ip < nPidCorr; ++ip) { + for (int ip = 0; ip < NPidCorr; ++ip) { std::vector pidTracks; for (const auto& t : selTracks) { if (pidSpeciesToIdx(pidFromPDG(t.pdg)) == ip) { @@ -1117,14 +1117,14 @@ struct myExampleTask { continue; } { - auto& pool = eventPoolsPID_Reco[ip][sphCls][zBin][mBin]; + auto& pool = eventPoolsPidReco[ip][sphCls][zBin][mBin]; pool.push_back(pidTracks); if (static_cast(pool.size()) > mixPoolSize) { pool.pop_front(); } } { - auto& pool = eventPoolsPID_Reco[ip][nSphClasses - 1][zBin][mBin]; + auto& pool = eventPoolsPidReco[ip][NSphClasses - 1][zBin][mBin]; pool.push_back(pidTracks); if (static_cast(pool.size()) > mixPoolSize) { pool.pop_front(); @@ -1136,7 +1136,7 @@ struct myExampleTask { // MIXED-EVENT "all" (Delta-eta) // ================================================================ auto doMixing = [&](int poolIdx) { - const auto& pool = eventPools_Reco[poolIdx][zBin][mBin]; + const auto& pool = eventPoolsReco[poolIdx][zBin][mBin]; const int nEvts = static_cast(pool.size()) - 1; for (int ie = 0; ie < nEvts; ++ie) { for (const auto& tr : pool[ie]) { @@ -1148,13 +1148,13 @@ struct myExampleTask { } const double dphi = RecoDecay::constrainAngle(phiLead - tr.phi, -o2::constants::math::PIHalf); const double deta = etaLead - tr.eta; - hME_Reco[poolIdx][mBin]->Fill(dphi, deta); + hMeReco[poolIdx][mBin]->Fill(dphi, deta); } } }; doMixing(sphCls); - doMixing(nSphClasses - 1); + doMixing(NSphClasses - 1); // ================================================================ // MIXED-EVENT PID (Delta-eta, pseudorapidity-only regime, see @@ -1162,7 +1162,7 @@ struct myExampleTask { // ================================================================ if (pidLeadIdx >= 0) { auto doMixingPID = [&](int poolIdx) { - const auto& pool = eventPoolsPID_Reco[pidLeadIdx][poolIdx][zBin][mBin]; + const auto& pool = eventPoolsPidReco[pidLeadIdx][poolIdx][zBin][mBin]; const int nEvts = static_cast(pool.size()) - 1; for (int ie = 0; ie < nEvts; ++ie) { for (const auto& tr : pool[ie]) { @@ -1174,19 +1174,19 @@ struct myExampleTask { } const double dphi = RecoDecay::constrainAngle(phiLead - tr.phi, -o2::constants::math::PIHalf); const double detaPID = etaLead - tr.eta; - hMEpid_Reco[pidLeadIdx][poolIdx][mBin]->Fill(dphi, detaPID); + hMePidReco[pidLeadIdx][poolIdx][mBin]->Fill(dphi, detaPID); } } }; doMixingPID(sphCls); - doMixingPID(nSphClasses - 1); + doMixingPID(NSphClasses - 1); } } - PROCESS_SWITCH(myExampleTask, processReco, "Process reconstructed data", false); + PROCESS_SWITCH(TwoParticleCorrelationsMultSpher, processReco, "Process reconstructed data", false); // ---------------------------------------------------------------- // processMCConfusion -- "rapid" PID contamination estimate, requested - // 2026-08-14. Fills hPidConfusion_MC (pT, true species, tagged + // 2026-08-14. Fills hPidConfusionMC (pT, true species, tagged // species) on MC only -- needs BOTH the truth PDG (via McTrackLabels) // AND the reconstructed track's nSigma values on the SAME track, // which neither processMC (truth only) nor processReco (no truth @@ -1217,7 +1217,7 @@ struct myExampleTask { continue; } auto* pdgPtr = pdg->GetParticle(mcPart.pdgCode()); - if (!pdgPtr || std::fabs(pdgPtr->Charge()) < 0.1) { + if (!pdgPtr || std::fabs(pdgPtr->Charge()) < MinCharge) { continue; } @@ -1246,23 +1246,23 @@ struct myExampleTask { // effect on this matrix (found 2026-08-14, comparing Phase 1 // rapidityOnly against baseline: identical confusion-matrix numbers // despite different processReco yields, before this fix). - if (applyRapidityCutPID && taggedPid != kPidUnidentified) { + if (applyRapidityCutPID && taggedPid != PidUnidentified) { double tagMass = pidMass(taggedPid); double tagRap = computeRapidity(track.pt(), track.eta(), tagMass); if (std::fabs(tagRap) > maxRapPID) { - taggedPid = kPidUnidentified; + taggedPid = PidUnidentified; } } histos.fill(HIST("hPidConfusion_MC"), track.pt(), static_cast(truePid), static_cast(taggedPid)); } } - PROCESS_SWITCH(myExampleTask, processMCConfusion, "Rapid PID contamination estimate: true (PDG) vs tagged (nSigma) species confusion matrix, MC only", false); + PROCESS_SWITCH(TwoParticleCorrelationsMultSpher, processMCConfusion, "Rapid PID contamination estimate: true (PDG) vs tagged (nSigma) species confusion matrix, MC only", false); }; // end struct // ---------------------------------------------------------------- WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { - return WorkflowSpec{adaptAnalysisTask(cfgc)}; + return WorkflowSpec{adaptAnalysisTask(cfgc)}; } From 2e34884f3c192fb8f3c06aabea1551fc92d26551 Mon Sep 17 00:00:00 2001 From: Madalina Tarzila Date: Fri, 9 Oct 2026 13:10:27 +0300 Subject: [PATCH 8/8] --- .../twoParticleCorrelationsMultSpher.cxx | 306 +++++++++--------- 1 file changed, 150 insertions(+), 156 deletions(-) diff --git a/PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationsMultSpher.cxx b/PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationsMultSpher.cxx index 897093cc2a1..8284aa29488 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationsMultSpher.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationsMultSpher.cxx @@ -61,6 +61,7 @@ #include #include #include +#include #include #include #include @@ -86,6 +87,29 @@ static constexpr int NSphClasses = 4; static constexpr std::array SphMax{0.3, 0.6, 1.0, 1.0}; static constexpr std::array SphLabels{"jetty", "intermediate", "isotropic", "all"}; +// Half a bin: puts integer-valued axes (indices, cut-flow steps) at bin centres. +static constexpr double HalfBin = 0.5; + +// ============================================================ +// Cut-flow steps (bin numbers of the evSel_* and track cut-flow histograms) +// ============================================================ +enum EventCut { EventCutRead = 1, + EventCutZvtx, + EventCutMultBin, + EventCutZvtxBin, + EventCutLeadPt, + EventCutSphericity, + EventCutSphClass, + EventCutSeFilled, + NEventCuts = EventCutSeFilled }; +enum TrackCut { TrackCutSeen = 1, + TrackCutGlobal, + TrackCutDcaXY, + TrackCutDcaZ, + TrackCutEta, + TrackCutPt, + NTrackCuts = TrackCutPt }; + // ============================================================ struct TwoParticleCorrelationsMultSpher { @@ -108,51 +132,43 @@ struct TwoParticleCorrelationsMultSpher { // pT=0.5 GeV/c, proton y_max~0.48, so |y|<0.5 keeps full acceptance for // that species. Reference: ALICE 2511.10399 (arXiv, Nov 2025), Sec. 4 -- // "tracks reconstructed within |eta|<0.8, with the additional rapidity - // cut of |y|<0.5" (verified 2026-08-11, quoted directly from the paper). + // cut of |y|<0.5". Configurable maxRapPID{"maxRapPID", 0.5, "Max |y| for PID-identified tracks (pion/kaon/proton), independent of maxEta"}; // Reco-specific configurables Configurable maxDcaXY{"maxDcaXY", 2.0, "Max DCA XY (cm) for reco tracks"}; Configurable maxDcaZ{"maxDcaZ", 2.0, "Max DCA Z (cm) for reco tracks"}; // Combined TPC+TOF PID criterion per ALICE 2511.10399 Sec. 4: two - // DIFFERENT thresholds, not one -- selection at 2 sigma, ambiguity - // rejection at 3 sigma (verified directly against the paper text - // 2026-08-11; an earlier draft of this instruction conflated the two - // into a single 2-sigma threshold, corrected here). + // different thresholds -- selection at 2 sigma, ambiguity rejection + // at 3 sigma. Configurable maxNSigmaPID{"maxNSigmaPID", 2.0, "Max combined N_sigma,PID for species selection (ALICE 2511.10399)"}; Configurable maxNSigmaPIDAmbig{"maxNSigmaPIDAmbig", 3.0, "N_sigma,PID threshold for ambiguity rejection (>1 species below this -> PidUnidentified)"}; - // Pre-2026-08-11 PID thresholds, restored (not reused for anything else) - // so the legacy gating style below can be toggled back on without - // deleting the ALICE 2511.10399 criterion. Values match the original - // (myExampleTask_BeforeOnlyProtons.cxx): independent 3-sigma cuts on - // TPC and TOF, no combined quadrature, no pT-dependent TOF gating. + // Thresholds of the legacy PID gating (independent 3-sigma cuts on TPC + // and TOF, no combined quadrature, no pT-dependent TOF gating), kept so + // that it can be switched on with useLegacyNSigmaGating without removing + // the ALICE 2511.10399 criterion. Configurable maxNSigmaTPC{"maxNSigmaTPC", 3.0, "[legacy] Max |N_sigma| TPC for PID, used only when useLegacyNSigmaGating=true"}; Configurable maxNSigmaTOF{"maxNSigmaTOF", 3.0, "[legacy] Max |N_sigma| TOF for PID, used only when useLegacyNSigmaGating=true"}; - // Three independently toggleable flags to compare pre-2026-08-11 PID - // methodology against the current ALICE 2511.10399-based one, for the - // PID-methodology-vs-statistics study (requested 2026-08-13). Each - // isolates one component instead of a single "old vs new" switch, so - // any combination of the 8 can be run to see which change drives a - // statistics difference. Gating/selection defaults still reproduce the - // 2511.10399 behavior exactly; setting all three to the legacy side - // reproduces myExampleTask_BeforeOnlyProtons.cxx's pidFromNSigma exactly. + // Three independently toggleable flags to compare the legacy PID + // methodology (independent 3-sigma TPC/TOF cuts, minimum-chi2 species + // choice, no rapidity cut) with the ALICE 2511.10399-based one. Each + // isolates one component, so any combination of the 8 can be run to + // see which change drives a statistics difference. The gating and + // selection defaults reproduce the 2511.10399 behaviour; setting all + // three to the legacy side reproduces the legacy pidFromNSigma. // - // applyRapidityCutPID defaults to false as of 2026-09-03, per the - // completed PID-methodology-vs-statistics study (49-file MC, see - // ~/EventShape-Wiki/Physics/PID-Methodology-Statistics-Impact-Study.md): - // the |y|<0.5 cut only discarded already-correctly-identified tracks - // between |y|=0.5 and the |eta|<0.8 acceptance, so dropping it raises - // both yield and purity simultaneously -- no tradeoff. Gating/selection - // are kept at the current (non-legacy) values by the same decision: - // gating relaxation was found to be a real purity-for-statistics - // tradeoff, not a free win, and was declined. - Configurable useLegacyNSigmaGating{"useLegacyNSigmaGating", false, "false=pT-gated combined N_sigma,PID (2511.10399); true=independent TPC/TOF 3-sigma AND-gate, TOF always required when available (pre-2026-08-11)"}; - Configurable useLegacyPIDSelection{"useLegacyPIDSelection", false, "false=reject if >1 species below ambiguity threshold; true=pick minimum-chi2 species among those passing the gate, no rejection (pre-2026-08-11)"}; + // applyRapidityCutPID defaults to false: the |y|<0.5 cut only discarded + // already-correctly-identified tracks between |y|=0.5 and the |eta|<0.8 + // acceptance, so dropping it raises both yield and purity at the same + // time. Gating and selection stay at the 2511.10399 values, because + // relaxing the gating trades purity for statistics. + Configurable useLegacyNSigmaGating{"useLegacyNSigmaGating", false, "false=pT-gated combined N_sigma,PID (2511.10399); true=independent TPC/TOF 3-sigma AND-gate, TOF always required when available (legacy)"}; + Configurable useLegacyPIDSelection{"useLegacyPIDSelection", false, "false=reject if >1 species below ambiguity threshold; true=pick minimum-chi2 species among those passing the gate, no rejection (legacy)"}; Service pdg{}; - Configurable applyRapidityCutPID{"applyRapidityCutPID", false, "false=skip the |y| applyRapidityCutPID{"applyRapidityCutPID", false, "false=skip the |y| PidCorrNames{"pion", "kaon", "proton"}; - // PDG codes corrispondenti a pidIdx + // PDG codes corresponding to pidIdx static int pidCorrPDG(int idx) { if (idx == 0) { @@ -203,26 +219,20 @@ struct TwoParticleCorrelationsMultSpher { } return PDG_t::kProton; } - // Mappa PidSpecies -> pidIdx (-1 se non e' pion/kaon/proton) + /// Index of a pion/kaon/proton in the NPidCorr-sized arrays, or -1 for any other species. static int pidSpeciesToIdx(PidSpecies pid) { - if (pid == PidPion) { - return 0; - } - if (pid == PidKaon) { - return 1; - } - if (pid == PidProton) { - return 2; + if (pid < PidPion || pid > PidProton) { + return -1; } - return -1; + return pid - PidPion; } using EventPool = std::deque>; // Pool "all" — separate per MC (generated) and Reco (reconstructed) std::array, NZvtxBins>, NSphClasses> eventPoolsMC; std::array, NZvtxBins>, NSphClasses> eventPoolsReco; - // Pool per specie PID (like-sign) — separate per MC e Reco + // Pool per PID species (like-sign) — separate for MC and Reco std::array, NZvtxBins>, NSphClasses>, NPidCorr> eventPoolsPidMC; std::array, NZvtxBins>, NSphClasses>, NPidCorr> eventPoolsPidReco; @@ -231,25 +241,25 @@ struct TwoParticleCorrelationsMultSpher { // ---------------------------------------------------------------- HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; - // SE/ME TH2D "all": [sphClass][multBin] — MC e Reco separati + // SE/ME TH2D "all": [sphClass][multBin] — MC and Reco separate std::array, NMultBins>, NSphClasses> hSeMC{}; std::array, NMultBins>, NSphClasses> hMeMC{}; std::array, NMultBins>, NSphClasses> hSeReco{}; std::array, NMultBins>, NSphClasses> hMeReco{}; - // SE/ME TH2D like-sign per specie: [pidIdx][sphClass][multBin] — MC e Reco separati + // SE/ME TH2D like-sign per species: [pidIdx][sphClass][multBin] — MC and Reco separate std::array, NMultBins>, NSphClasses>, NPidCorr> hSePidMC{}; std::array, NMultBins>, NSphClasses>, NPidCorr> hMePidMC{}; std::array, NMultBins>, NSphClasses>, NPidCorr> hSePidReco{}; std::array, NMultBins>, NSphClasses>, NPidCorr> hMePidReco{}; - // Trigger counter: axes = mult bin (x) x sphClass (y) — MC e Reco separati + // Trigger counter: axes = mult bin (x) x sphClass (y) — MC and Reco separate std::shared_ptr hNtrigMC; std::shared_ptr hNtrigReco; std::array, NPidCorr> hNtrigPidMC{}; std::array, NPidCorr> hNtrigPidReco{}; - // Per-mult sphericity — MC e Reco separati + // Per-mult sphericity — MC and Reco separate std::array, NMultBins> hSphMultMC{}; std::array, NMultBins> hSphMultReco{}; @@ -264,12 +274,10 @@ struct TwoParticleCorrelationsMultSpher { // ---------------------------------------------------------------- // PID confusion matrix (true species from PDG vs. tagged species from // pidFromNSigma applied to the SAME reconstructed track, per pT bin). - // "Rapid" contamination estimate requested 2026-08-14 -- reports - // contamination as a systematic, does NOT correct anything. Filled - // only by processMCConfusion (needs both MC truth and reconstructed - // nSigma together, unlike processMC/processReco which only ever see - // one or the other). See auto-memory / STATUS.md for the "rapid vs - // riguros" design discussion this came out of. + // Quick contamination estimate, reported as a systematic; it does NOT + // correct anything. Filled only by processMCConfusion (needs both MC + // truth and reconstructed nSigma together, unlike + // processMC/processReco which only ever see one or the other). // ---------------------------------------------------------------- std::shared_ptr hPidConfusionMC; @@ -280,11 +288,11 @@ struct TwoParticleCorrelationsMultSpher { static PidSpecies pidFromPDG(int pdg) { switch (std::abs(pdg)) { - case 211: + case PDG_t::kPiPlus: return PidPion; - case 321: + case PDG_t::kKPlus: return PidKaon; - case 2212: + case PDG_t::kProton: return PidProton; default: return PidUnidentified; @@ -292,17 +300,15 @@ struct TwoParticleCorrelationsMultSpher { } /// PID from TPC(+TOF) nSigma for reconstructed tracks. Two independently - /// toggleable behaviors (2026-08-13, added to compare pre-2026-08-11 PID - /// methodology against the current ALICE 2511.10399-based one, without - /// deleting either -- see useLegacyNSigmaGating/useLegacyPIDSelection - /// Configurables): + /// toggleable behaviors (see the useLegacyNSigmaGating and + /// useLegacyPIDSelection Configurables), so that the legacy and the + /// ALICE 2511.10399-based methodologies can be compared: /// /// legacyGating (gate = "does this species hypothesis pass at all"): /// false (default): pT>0.5 && TOF available -> combined /// N_sigma,PID = sqrt(nsTPC^2+nsTOF^2) < maxSel (2 sigma, per ALICE /// 2511.10399 Sec. 4); else TPC-only. - /// true: pre-2026-08-11 style, matches myExampleTask_BeforeOnlyProtons.cxx - /// exactly -- |nsTPC| < maxTPC AND (if TOF available) |nsTOF| < maxTOF, + /// true: legacy style -- |nsTPC| < maxTPC AND (if TOF available) |nsTOF| < maxTOF, /// independently (no quadrature sum for the gate itself), TOF /// required whenever available regardless of pT. /// @@ -312,7 +318,7 @@ struct TwoParticleCorrelationsMultSpher { /// threshold maxAmbig (3 sigma) -- this comparison always uses the /// combined-quadrature N_sigma,PID, regardless of legacyGating, so /// the two flags stay independently meaningful. - /// true: pre-2026-08-11 style -- among hypotheses passing the gate, + /// true: legacy style -- among hypotheses passing the gate, /// pick the one with minimum chi2 = nsTPC^2+nsTOF^2 (or nsTPC^2 /// alone if no TOF); no rejection for multiple passing hypotheses. /// @@ -330,9 +336,9 @@ struct TwoParticleCorrelationsMultSpher { double nsTPC = 0.; double nsTOF = 0.; }; - const std::array hypos{{{PidPion, track.tpcNSigmaPi(), track.tofNSigmaPi()}, - {PidKaon, track.tpcNSigmaKa(), track.tofNSigmaKa()}, - {PidProton, track.tpcNSigmaPr(), track.tofNSigmaPr()}}}; + const std::array hypos{{{PidPion, track.tpcNSigmaPi(), track.tofNSigmaPi()}, + {PidKaon, track.tpcNSigmaKa(), track.tofNSigmaKa()}, + {PidProton, track.tpcNSigmaPr(), track.tofNSigmaPr()}}}; // TOF unavailable -> nSigma returns -999; check with any species bool hasTOF = (std::fabs(track.tofNSigmaPi()) < MaxValidNSigma); @@ -350,9 +356,9 @@ struct TwoParticleCorrelationsMultSpher { if (legacySelection) { // Best-fit (min chi2) among hypotheses passing the gate -- no - // ambiguity rejection (pre-2026-08-11 style). + // ambiguity rejection (legacy style). PidSpecies best = PidUnidentified; - double bestChi2 = 1e9; + double bestChi2 = std::numeric_limits::max(); for (const auto& h : hypos) { if (!passesGate(h)) { continue; @@ -470,18 +476,17 @@ struct TwoParticleCorrelationsMultSpher { const AxisSpec axisPhi{60, 0., o2::constants::math::TwoPI, "#phi"}; const AxisSpec axisDphi{30, -o2::constants::math::PIHalf, o2::constants::math::PI + o2::constants::math::PIHalf, "#Delta#phi"}; const AxisSpec axisDeta{32, -1.6, 1.6, "#Delta#eta"}; - // Reserved for a future rapidity-differentiated analysis -- unused - // while the PID correlations run pseudorapidity-only (see - // eventshape-pid-pseudorapidity-only auto-memory, 2026-09-15). + // Reserved for a possible rapidity-differentiated analysis -- unused + // while the PID correlations run pseudorapidity-only. [[maybe_unused]] const AxisSpec axisDy{32, -1.6, 1.6, "#Delta y"}; const AxisSpec axisPt{nBinsPt, 0., 10., "p_{T} (GeV/c)"}; const AxisSpec axisSph{100, 0., 1., "S_{T}"}; const AxisSpec axisPt2{nBinsPt2, 0., 5., "p_{T} (GeV/c)"}; // ---------------------------------------------------------------- - // Lambda comuna: creeaza un set complet de histograme pentru un - // "canal" (MC sau Reco), cu toate numele suffixate corespunzator. - // Primeste referinte la toti membrii specifici canalului. + // Common lambda: books a complete set of histograms for one + // "channel" (MC or Reco), with all names suffixed accordingly. + // Takes references to all channel-specific members. // ---------------------------------------------------------------- auto bookChannel = [&](const char* suf, std::array, NMultBins>, NSphClasses>& hSE, @@ -492,16 +497,15 @@ struct TwoParticleCorrelationsMultSpher { std::array, NPidCorr>& hNtrigPID, std::array, NMultBins>& hSphMult) { // QA - // evSel bins: 1=read 2=zvtx 3=multBin 4=zvtxBin 5=leadPt 6=ST_valid 7=sphClass 8=SE_filled - auto hQA = histos.add(Form("evSel_%s", suf), Form("Event selection (%s)", suf), HistType::kTH1D, {{8, 0.5, 8.5}}); - hQA->GetXaxis()->SetBinLabel(1, "Events read"); - hQA->GetXaxis()->SetBinLabel(2, "|z_{vtx}|<10"); - hQA->GetXaxis()->SetBinLabel(3, "multBin OK"); - hQA->GetXaxis()->SetBinLabel(4, "zvtxBin OK"); - hQA->GetXaxis()->SetBinLabel(5, "leadPt #in [min,max]"); - hQA->GetXaxis()->SetBinLabel(6, "S_{T} valid"); - hQA->GetXaxis()->SetBinLabel(7, "sphClass OK"); - hQA->GetXaxis()->SetBinLabel(8, "SE filled"); + auto hQA = histos.add(Form("evSel_%s", suf), Form("Event selection (%s)", suf), HistType::kTH1D, {{NEventCuts, HalfBin, static_cast(NEventCuts) + HalfBin}}); + hQA->GetXaxis()->SetBinLabel(EventCutRead, "Events read"); + hQA->GetXaxis()->SetBinLabel(EventCutZvtx, "|z_{vtx}|<10"); + hQA->GetXaxis()->SetBinLabel(EventCutMultBin, "multBin OK"); + hQA->GetXaxis()->SetBinLabel(EventCutZvtxBin, "zvtxBin OK"); + hQA->GetXaxis()->SetBinLabel(EventCutLeadPt, "leadPt #in [min,max]"); + hQA->GetXaxis()->SetBinLabel(EventCutSphericity, "S_{T} valid"); + hQA->GetXaxis()->SetBinLabel(EventCutSphClass, "sphClass OK"); + hQA->GetXaxis()->SetBinLabel(EventCutSeFilled, "SE filled"); histos.add(Form("Zvertex_%s", suf), Form("z_{vtx} (%s);z_{vtx} (cm);events", suf), kTH1D, {axisZvtx}); histos.add(Form("multHist_%s", suf), Form("N_{ch} (%s);N_{ch};events", suf), kTH1D, {axisMult}); @@ -526,8 +530,8 @@ struct TwoParticleCorrelationsMultSpher { Form("hNtrig%s", suf), Form("N_{trig} (%s);mult bin;sph class", suf), HistType::kTH2D, - {{NMultBins, -0.5, static_cast(NMultBins) - 0.5}, - {NSphClasses, -0.5, static_cast(NSphClasses) - 0.5}}); + {{NMultBins, -HalfBin, static_cast(NMultBins) - HalfBin}, + {NSphClasses, -HalfBin, static_cast(NSphClasses) - HalfBin}}); for (int im = 0; im < NMultBins; ++im) { hNtrig->GetXaxis()->SetBinLabel(im + 1, multLabel(im).c_str()); } @@ -549,14 +553,12 @@ struct TwoParticleCorrelationsMultSpher { } } - // ---- SE/ME like-sign per specie PID: [pidIdx][sphClass][multBin] --- - // Pseudorapidity-only regime (confirmed 2026-09-15, see Claude Code - // auto-memory eventshape-pid-pseudorapidity-only): PID correlations - // use Delta-eta, same as the inclusive hSE/hME histograms, NOT - // Delta-y. The rapidity infrastructure (computeRapidity(), the - // per-track y field, axisDy below) is kept for a future - // higher-statistics rapidity-differentiated analysis -- it is - // deliberately unused here for now, not dead code to remove. + // ---- SE/ME like-sign per PID species: [pidIdx][sphClass][multBin] --- + // PID correlations use Delta-eta, same as the inclusive hSE/hME + // histograms, NOT Delta-y. The rapidity infrastructure + // (computeRapidity(), the per-track y field, axisDy) is kept for a + // possible rapidity-differentiated analysis and is deliberately + // unused for now. for (int ip = 0; ip < NPidCorr; ++ip) { for (int ic = 0; ic < NSphClasses; ++ic) { for (int im = 0; im < NMultBins; ++im) { @@ -572,13 +574,13 @@ struct TwoParticleCorrelationsMultSpher { HistType::kTH2D, {axisDphi, axisDeta}); } } - // Trigger counter per specie PID + // Trigger counter per PID species hNtrigPID[ip] = histos.add( Form("hNtrig%s_pid%d", suf, ip), Form("N_{trig} %s (%s);mult bin;sph class", PidCorrNames[ip], suf), HistType::kTH2D, - {{NMultBins, -0.5, static_cast(NMultBins) - 0.5}, - {NSphClasses, -0.5, static_cast(NSphClasses) - 0.5}}); + {{NMultBins, -HalfBin, static_cast(NMultBins) - HalfBin}, + {NSphClasses, -HalfBin, static_cast(NSphClasses) - HalfBin}}); for (int im = 0; im < NMultBins; ++im) { hNtrigPID[ip]->GetXaxis()->SetBinLabel(im + 1, multLabel(im).c_str()); } @@ -602,18 +604,18 @@ struct TwoParticleCorrelationsMultSpher { hTrackCutDebugReco = histos.add( "hTrackCutDebug_Reco", "Track cut flow (Reco, DEBUG);cut;tracks", - HistType::kTH1D, {{6, 0.5, 6.5}}); - hTrackCutDebugReco->GetXaxis()->SetBinLabel(1, "seen"); - hTrackCutDebugReco->GetXaxis()->SetBinLabel(2, "passed isGlobalTrackWoDCA"); - hTrackCutDebugReco->GetXaxis()->SetBinLabel(3, "passed DCAxy"); - hTrackCutDebugReco->GetXaxis()->SetBinLabel(4, "passed DCAz"); - hTrackCutDebugReco->GetXaxis()->SetBinLabel(5, "passed eta"); - hTrackCutDebugReco->GetXaxis()->SetBinLabel(6, "passed pt (selected)"); - - // PID confusion matrix (rapid contamination estimate, 2026-08-14) -- + HistType::kTH1D, {{NTrackCuts, HalfBin, static_cast(NTrackCuts) + HalfBin}}); + hTrackCutDebugReco->GetXaxis()->SetBinLabel(TrackCutSeen, "seen"); + hTrackCutDebugReco->GetXaxis()->SetBinLabel(TrackCutGlobal, "passed isGlobalTrackWoDCA"); + hTrackCutDebugReco->GetXaxis()->SetBinLabel(TrackCutDcaXY, "passed DCAxy"); + hTrackCutDebugReco->GetXaxis()->SetBinLabel(TrackCutDcaZ, "passed DCAz"); + hTrackCutDebugReco->GetXaxis()->SetBinLabel(TrackCutEta, "passed eta"); + hTrackCutDebugReco->GetXaxis()->SetBinLabel(TrackCutPt, "passed pt (selected)"); + + // PID confusion matrix (contamination estimate) -- // axes: pT, true species (PDG), tagged species (pidFromNSigma). // Species axis order matches PidSpecies: 0=unid,1=pion,2=kaon,3=proton. - const AxisSpec axisPidSpecies{static_cast(NPidSpecies), -0.5, static_cast(NPidSpecies) - 0.5, "species"}; + const AxisSpec axisPidSpecies{static_cast(NPidSpecies), -HalfBin, static_cast(NPidSpecies) - HalfBin, "species"}; hPidConfusionMC = histos.add( "hPidConfusion_MC", "True vs. tagged PID species (MC);p_{T} (GeV/c);true species;tagged species", @@ -630,13 +632,13 @@ struct TwoParticleCorrelationsMultSpher { void processMC(aod::McCollision const& mcCollision, aod::McParticles const& mcParticles) { - histos.fill(HIST("evSel_MC"), 1); + histos.fill(HIST("evSel_MC"), EventCutRead); const double zvtx = mcCollision.posZ(); if (std::fabs(zvtx) > maxZvtx) { return; } - histos.fill(HIST("evSel_MC"), 2); + histos.fill(HIST("evSel_MC"), EventCutZvtx); histos.fill(HIST("Zvertex_MC"), zvtx); // ---- one-pass: build selTracks + find leading --------------- @@ -696,13 +698,13 @@ struct TwoParticleCorrelationsMultSpher { if (mBin < 0) { return; } - histos.fill(HIST("evSel_MC"), 3); + histos.fill(HIST("evSel_MC"), EventCutMultBin); const int zBin = o2::analysis::findBin(&ZvtxEdges, zvtx); if (zBin < 0) { return; } - histos.fill(HIST("evSel_MC"), 4); + histos.fill(HIST("evSel_MC"), EventCutZvtxBin); // ---- leading particle pT cut [minLeadPt, maxLeadPt] ----------- if (leadIdx < 0) { @@ -711,7 +713,7 @@ struct TwoParticleCorrelationsMultSpher { if (pTlead < minLeadPt || pTlead > maxLeadPt) { return; } - histos.fill(HIST("evSel_MC"), 5); + histos.fill(HIST("evSel_MC"), EventCutLeadPt); histos.fill(HIST("ptLeadHistogram_MC"), pTlead); // ---- sphericity --------------------------------------------- @@ -723,7 +725,7 @@ struct TwoParticleCorrelationsMultSpher { if (sph < 0.) { return; // fewer than 3 tracks } - histos.fill(HIST("evSel_MC"), 6); + histos.fill(HIST("evSel_MC"), EventCutSphericity); histos.fill(HIST("sphericity_MC"), sph); hSphMultMC[mBin]->Fill(sph); @@ -731,7 +733,7 @@ struct TwoParticleCorrelationsMultSpher { if (sphCls < 0) { return; } - histos.fill(HIST("evSel_MC"), 7); + histos.fill(HIST("evSel_MC"), EventCutSphClass); const PidSpecies pidLead = pidFromPDG(selTracks[leadIdx].pdg); @@ -749,7 +751,7 @@ struct TwoParticleCorrelationsMultSpher { } const auto& assoc = selTracks[ia]; if (assoc.pt >= pTlead) { - continue; // asociat pT < leading pT + continue; // associated pT < leading pT } if (assoc.pt < minAssocCorr || assoc.pt > maxAssocCorr) { continue; @@ -767,9 +769,8 @@ struct TwoParticleCorrelationsMultSpher { // TH2D SE: "all" class (uses Delta-eta) hSeMC[NSphClasses - 1][mBin]->Fill(dphi, deta); - // SE like-sign: fill solo se trigger e associato sono la stessa specie - // Uses Delta-eta (pseudorapidity-only regime, see - // eventshape-pid-pseudorapidity-only auto-memory) + // SE like-sign: fill only if trigger and associated are the same species + // Uses Delta-eta (pseudorapidity-only regime) int pidLeadIdx = pidSpeciesToIdx(pidLead); int pidAssocIdx = pidSpeciesToIdx(pidAssoc); if (pidLeadIdx >= 0 && pidLeadIdx == pidAssocIdx) { @@ -780,10 +781,10 @@ struct TwoParticleCorrelationsMultSpher { seHasPairs = true; } if (seHasPairs) { - histos.fill(HIST("evSel_MC"), 8); + histos.fill(HIST("evSel_MC"), EventCutSeFilled); } - // ---- hNtrigMC per specie PID leading -------------------------- + // ---- hNtrigMC per PID species leading -------------------------- int pidLeadIdx = pidSpeciesToIdx(pidLead); if (pidLeadIdx >= 0) { hNtrigPidMC[pidLeadIdx]->Fill(static_cast(mBin), static_cast(sphCls)); @@ -810,9 +811,9 @@ struct TwoParticleCorrelationsMultSpher { } } - // Pool per specie PID — contiene solo i track di quella specie + // Pool per PID species — contains only the tracks of that species for (int ip = 0; ip < NPidCorr; ++ip) { - // Costruisci un vettore con solo i track della specie ip + // Build a vector with only the tracks of species ip std::vector pidTracks; for (const auto& t : selTracks) { if (pidSpeciesToIdx(pidFromPDG(t.pdg)) == ip) { @@ -865,11 +866,10 @@ struct TwoParticleCorrelationsMultSpher { doMixing(NSphClasses - 1); // ================================================================ - // MIXED-EVENT like-sign per specie PID - // Trigger = leading della specie pidLeadIdx - // Associati = track della stessa specie dal pool PID - // Uses Delta-eta (pseudorapidity-only regime, see - // eventshape-pid-pseudorapidity-only auto-memory) + // MIXED-EVENT like-sign per PID species + // Trigger = leading track of species pidLeadIdx + // Associated = tracks of the same species from the PID pool + // Uses Delta-eta (pseudorapidity-only regime) // ================================================================ if (pidLeadIdx >= 0) { auto doMixingPID = [&](int poolIdx) { @@ -906,7 +906,7 @@ struct TwoParticleCorrelationsMultSpher { void processReco(ColReco::iterator const& col, TrackReco const& tracks) { - histos.fill(HIST("evSel_Reco"), 1); + histos.fill(HIST("evSel_Reco"), EventCutRead); // ---- event selection ---- if (!col.sel8()) { @@ -916,7 +916,7 @@ struct TwoParticleCorrelationsMultSpher { if (std::fabs(zvtx) > maxZvtx) { return; } - histos.fill(HIST("evSel_Reco"), 2); + histos.fill(HIST("evSel_Reco"), EventCutZvtx); histos.fill(HIST("Zvertex_Reco"), zvtx); // ---- one-pass: build selTracks + find leading --------------- @@ -926,36 +926,36 @@ struct TwoParticleCorrelationsMultSpher { double pTlead = -1., phiLead = 0., etaLead = 0.; for (const auto& track : tracks) { - hTrackCutDebugReco->Fill(1); // seen + hTrackCutDebugReco->Fill(TrackCutSeen); // seen // Quality selection: global track without DCA (apply DCA separately) if (!track.isGlobalTrackWoDCA()) { continue; } - hTrackCutDebugReco->Fill(2); // passed isGlobalTrackWoDCA + hTrackCutDebugReco->Fill(TrackCutGlobal); // passed isGlobalTrackWoDCA // DCA cuts if (std::fabs(track.dcaXY()) > maxDcaXY) { continue; } - hTrackCutDebugReco->Fill(3); // passed DCAxy + hTrackCutDebugReco->Fill(TrackCutDcaXY); // passed DCAxy if (std::fabs(track.dcaZ()) > maxDcaZ) { continue; } - hTrackCutDebugReco->Fill(4); // passed DCAz + hTrackCutDebugReco->Fill(TrackCutDcaZ); // passed DCAz // Kinematic cuts (same as MC) if (std::fabs(track.eta()) > maxEta) { continue; } - hTrackCutDebugReco->Fill(5); // passed eta + hTrackCutDebugReco->Fill(TrackCutEta); // passed eta if (track.pt() < minAssocPt) { continue; } - hTrackCutDebugReco->Fill(6); // passed pt (selected) + hTrackCutDebugReco->Fill(TrackCutPt); // passed pt (selected) // PID from nSigma (combined TPC+TOF, ALICE 2511.10399, unless the - // legacy flags below switch to pre-2026-08-11 gating/selection) + // legacy flags below switch to legacy gating/selection) PidSpecies pid = pidFromNSigma(track, maxNSigmaPID, maxNSigmaPIDAmbig, maxNSigmaTPC, maxNSigmaTOF, useLegacyNSigmaGating, useLegacyPIDSelection); @@ -997,13 +997,13 @@ struct TwoParticleCorrelationsMultSpher { if (mBin < 0) { return; } - histos.fill(HIST("evSel_Reco"), 3); + histos.fill(HIST("evSel_Reco"), EventCutMultBin); const int zBin = o2::analysis::findBin(&ZvtxEdges, zvtx); if (zBin < 0) { return; } - histos.fill(HIST("evSel_Reco"), 4); + histos.fill(HIST("evSel_Reco"), EventCutZvtxBin); // ---- leading particle pT cut [minLeadPt, maxLeadPt] ----------- if (leadIdx < 0) { @@ -1012,7 +1012,7 @@ struct TwoParticleCorrelationsMultSpher { if (pTlead < minLeadPt || pTlead > maxLeadPt) { return; } - histos.fill(HIST("evSel_Reco"), 5); + histos.fill(HIST("evSel_Reco"), EventCutLeadPt); histos.fill(HIST("ptLeadHistogram_Reco"), pTlead); // ---- sphericity --------------------------------------------- @@ -1023,7 +1023,7 @@ struct TwoParticleCorrelationsMultSpher { if (sph < 0.) { return; } - histos.fill(HIST("evSel_Reco"), 6); + histos.fill(HIST("evSel_Reco"), EventCutSphericity); histos.fill(HIST("sphericity_Reco"), sph); hSphMultReco[mBin]->Fill(sph); @@ -1031,7 +1031,7 @@ struct TwoParticleCorrelationsMultSpher { if (sphCls < 0) { return; } - histos.fill(HIST("evSel_Reco"), 7); + histos.fill(HIST("evSel_Reco"), EventCutSphClass); const PidSpecies pidLead = pidFromPDG(selTracks[leadIdx].pdg); @@ -1066,8 +1066,7 @@ struct TwoParticleCorrelationsMultSpher { hSeReco[sphCls][mBin]->Fill(dphi, deta); hSeReco[NSphClasses - 1][mBin]->Fill(dphi, deta); - // PID SE — same species, Delta-eta (pseudorapidity-only regime, see - // eventshape-pid-pseudorapidity-only auto-memory) + // PID SE — same species, Delta-eta (pseudorapidity-only regime) int pidLeadIdx = pidSpeciesToIdx(pidLead); int pidAssocIdx = pidSpeciesToIdx(pidAssoc); if (pidLeadIdx >= 0 && pidLeadIdx == pidAssocIdx) { @@ -1078,10 +1077,10 @@ struct TwoParticleCorrelationsMultSpher { seHasPairs = true; } if (seHasPairs) { - histos.fill(HIST("evSel_Reco"), 8); + histos.fill(HIST("evSel_Reco"), EventCutSeFilled); } - // ---- hNtrigReco per specie PID leading -------------------------- + // ---- hNtrigReco per PID species leading -------------------------- int pidLeadIdx = pidSpeciesToIdx(pidLead); if (pidLeadIdx >= 0) { hNtrigPidReco[pidLeadIdx]->Fill(static_cast(mBin), static_cast(sphCls)); @@ -1157,8 +1156,7 @@ struct TwoParticleCorrelationsMultSpher { doMixing(NSphClasses - 1); // ================================================================ - // MIXED-EVENT PID (Delta-eta, pseudorapidity-only regime, see - // eventshape-pid-pseudorapidity-only auto-memory) + // MIXED-EVENT PID (Delta-eta, pseudorapidity-only regime) // ================================================================ if (pidLeadIdx >= 0) { auto doMixingPID = [&](int poolIdx) { @@ -1185,15 +1183,13 @@ struct TwoParticleCorrelationsMultSpher { PROCESS_SWITCH(TwoParticleCorrelationsMultSpher, processReco, "Process reconstructed data", false); // ---------------------------------------------------------------- - // processMCConfusion -- "rapid" PID contamination estimate, requested - // 2026-08-14. Fills hPidConfusionMC (pT, true species, tagged + // processMCConfusion -- PID contamination estimate. Fills hPidConfusionMC (pT, true species, tagged // species) on MC only -- needs BOTH the truth PDG (via McTrackLabels) // AND the reconstructed track's nSigma values on the SAME track, // which neither processMC (truth only) nor processReco (no truth // link) has access to, hence a dedicated process function. Reports - // contamination as a systematic; does NOT correct anything (that's - // the separate, not-yet-started "riguros" unfolding approach -- see - // STATUS.md). Off by default, like the other MC-only diagnostics. + // contamination as a systematic; does NOT correct anything (no + // unfolding is implemented). Off by default, like the other MC-only diagnostics. // ---------------------------------------------------------------- using TrackRecoMC = soa::Join; @@ -1243,9 +1239,7 @@ struct TwoParticleCorrelationsMultSpher { // Same rapidity cut as processReco applies to its "tagged" PID -- // without this, applyRapidityCutPID=false would silently have zero - // effect on this matrix (found 2026-08-14, comparing Phase 1 - // rapidityOnly against baseline: identical confusion-matrix numbers - // despite different processReco yields, before this fix). + // effect on this matrix even though it changes the processReco yields. if (applyRapidityCutPID && taggedPid != PidUnidentified) { double tagMass = pidMass(taggedPid); double tagRap = computeRapidity(track.pt(), track.eta(), tagMass);