From 4f7fdfb4bbdfb0bdb915f5355ef13afdb81185e3 Mon Sep 17 00:00:00 2001 From: Bong-Hwi Lim Date: Fri, 23 Oct 2026 18:26:30 +0900 Subject: [PATCH 1/3] [PWGLF] Align Omega2012 MC efficiency and exporter event selection Add selected-event generated denominators and reconstructed response histograms for the XiK0s and Xi1530K channels. Match reconstructed mothers to the parent table and apply consistent truth-rapidity, kinematic and optional injected-generator selections. Share Omega2012 event-selection defaults between analysis and ML export. Use an independent generated-mother rapidity limit so the audit includes selected events without candidates while preserving the loose candidate rapidity acceptance. --- PWGLF/Core/Omega2012AnalysisCore.h | 22 ++- PWGLF/Tasks/Resonances/omega2012Analysis.cxx | 165 ++++++++++++++++-- .../Resonances/omega2012TrainingTable.cxx | 16 +- 3 files changed, 181 insertions(+), 22 deletions(-) diff --git a/PWGLF/Core/Omega2012AnalysisCore.h b/PWGLF/Core/Omega2012AnalysisCore.h index d0d3600d7b4..57444555806 100644 --- a/PWGLF/Core/Omega2012AnalysisCore.h +++ b/PWGLF/Core/Omega2012AnalysisCore.h @@ -58,6 +58,17 @@ using o2::analysis::resonance::ResoAnalysisSelectionCore; using o2::analysis::resonance::TrackCuts; using o2::analysis::resonance::TrackStage; +// Omega(2012) analysis and ML export share these event-level selection defaults. +// Keep the generic EventCuts defaults unchanged for other resonance tasks. +inline EventCuts makeOmega2012EventCuts() +{ + EventCuts cuts; + cuts.cRecoINELgt0.value = true; + cuts.cMCINELgt0.value = true; + cuts.cMCVtxIn10.value = true; + return cuts; +} + enum class DecayMode : uint8_t { XiK0s = 0, // Omega(2012)- -> Xi- K0S Xi1530K = 1 // Omega(2012)- -> Xi(1530)0 K- -> Xi- pi+ K- @@ -932,11 +943,12 @@ class Omega2012AnalysisCore } } - // Generated Omega(2012) parents of a selected reconstructed MC collision (ResoMCParents_001). The optional callback - // receives (parent, immediate channel) for the parents inside the rapidity window. Split reconstructed collisions - // repeat parent sets: this is not an unconditional generated denominator. + // Generated Omega(2012) parents of a selected reconstructed MC collision (ResoMCParents_001). The caller supplies + // the denominator rapidity limit so it can remain independent of a wider candidate-export window. The optional + // callback receives (parent, immediate channel) for the parents inside that rapidity window. Split reconstructed + // collisions repeat parent sets: this is not an unconditional generated denominator. template - void forEachGeneratedOmega2012(o2::framework::HistogramRegistry& histos, const ParentsType& resoParents, Callback callback = nullptr) + void forEachGeneratedOmega2012(o2::framework::HistogramRegistry& histos, const ParentsType& resoParents, float rapidityMax, Callback callback = nullptr) { for (const auto& part : resoParents) { if (std::abs(part.pdgCode()) != o2::constants::physics::Pdg::kOmega2012Minus) { @@ -944,7 +956,7 @@ class Omega2012AnalysisCore } const GeneratedChannel channel = classifyGeneratedOmega2012(part.pdgCode(), part.daughterPDG1(), part.daughterPDG2()); histos.fill(HIST("CutFlow/generated"), 0, static_cast(channel)); - if (!(std::abs(part.y()) < mCandidateCuts.cfgRapidityCut.value)) { + if (!(std::abs(part.y()) < rapidityMax)) { continue; } histos.fill(HIST("CutFlow/generated"), 1, static_cast(channel)); diff --git a/PWGLF/Tasks/Resonances/omega2012Analysis.cxx b/PWGLF/Tasks/Resonances/omega2012Analysis.cxx index 74146abee60..31bc465954d 100644 --- a/PWGLF/Tasks/Resonances/omega2012Analysis.cxx +++ b/PWGLF/Tasks/Resonances/omega2012Analysis.cxx @@ -14,10 +14,10 @@ /// \author Bong-Hwi Lim /// /// Two decay modes, analysed separately (never merged in any histogram or process function): -/// - mode A (XiK0s): Omega(2012)- -> Xi- K0S. processData, processMixedEvent, processMC, processMCGenerated. +/// - mode A (XiK0s): Omega(2012)- -> Xi- K0S. processData, processMixedEvent, processMC, processMCGen, processMCGenerated. /// Histograms: Event/, QAbefore/, QAafter/, omega2012/, MC/ (unchanged). /// - mode B (Xi1530K): Omega(2012)- -> Xi(1530)0 K- -> Xi- pi+ K- with a charged kaon track. -/// processXi1530KMicro, processXi1530KTracks, processXi1530KMCMicro, processXi1530KMixedMicro. +/// processXi1530KMicro, processXi1530KTracks, processXi1530KMCMicro, processXi1530KMCGen, processXi1530KMixedMicro. /// Histograms: xi1530K/ (signal charge pattern) and xi1530K_wrongSign/ (charge-pattern controls). /// Selection, candidate enumeration and truth classification: PWGLF/Core/Omega2012AnalysisCore.h. /// @@ -49,6 +49,7 @@ #include #include #include +#include #include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) #include @@ -56,6 +57,7 @@ #include #include +#include using namespace o2; using namespace o2::framework; @@ -85,7 +87,7 @@ struct Omega2012Analysis { Configurable cInvMassBins{"cInvMassBins", 600, "Invariant mass bins"}; // Selection shared with the Omega(2012) training-table task (plain JSON keys; the mode-B track cuts carry "trk.") - o2::analysis::resonance::EventCuts eventCuts; + o2::analysis::resonance::EventCuts eventCuts = makeOmega2012EventCuts(); XiCuts xiCuts; K0sCuts k0sCuts; Xi1530KCuts xi1530KCuts; @@ -93,6 +95,35 @@ struct Omega2012Analysis { PionPidCuts pionPID; KaonPidCuts kaonPID; CandidateCuts candidateCuts; + Configurable cUseTruthRapidity{"cUseTruthRapidity", true, "Require the matched Omega(2012) truth rapidity inside cfgRapidityCut"}; + Configurable cMCInjection{"cMCInjection", false, "Restrict MC numerator and denominator to Omega parents with generator status -11"}; + + struct OmegaParentTruth { + float pt = 0.f; + float y = 0.f; + int pdgCode = 0; + bool injected = false; + }; + using OmegaParentTruthMap = std::unordered_map; + + template + static bool isInjectedOmegaParent(const Parent& parent) + { + constexpr int InjectedGeneratorStatus = -11; + return o2::mcgenstatus::getGenStatusCode(parent.statusCode()) == InjectedGeneratorStatus; + } + + template + OmegaParentTruthMap omegaParentTruth(const Parents& parents) + { + OmegaParentTruthMap parentTruth; + for (const auto& parent : parents) { + if (std::abs(parent.pdgCode()) == kOmega2012Minus) { + parentTruth.emplace(parent.originalMcParticleId(), OmegaParentTruth{parent.pt(), parent.y(), parent.pdgCode(), isInjectedOmegaParent(parent)}); + } + } + return parentTruth; + } // Event Mixing Configurable nEvtMixing{"nEvtMixing", 10, "Number of events to mix"}; @@ -217,6 +248,20 @@ struct Omega2012Analysis { histos.add("MC/hMCGenOmega2012Y", "MC Generated Omega(2012) rapidity", kTH1F, {rapidityAxis}); histos.add("MC/hMCRecOmega2012Pt", "MC Reconstructed Omega(2012) pT", kTH1F, {ptAxis}); histos.add("MC/hMCRecOmega2012PtEta", "MC Reconstructed Omega(2012) pT vs eta", kTH2F, {ptAxis, etaAxis}); + if (doprocessMCGen) { + histos.add("MC/hMCGenSelectedXiK0sPt", "MC generated Omega(2012) #rightarrow #Xi K^{0}_{S} in selected events and rapidity window;p_{T} (GeV/c)", kTH1F, {ptAxis}); + const AxisSpec truthPtAxis{binsPt, "p_{T}^{truth} (GeV/c)"}; + const AxisSpec signAxis{2, -1.5, 1.5, "Omega PDG sign (+1 particle, -1 antiparticle)"}; + histos.add("MC/hMCGenSelectedXiK0sResponse", "Selected-event generated #Omega #rightarrow #Xi K^{0}_{S}", kTHnSparseD, + {truthPtAxis, centAxis, {500, 0., 500., "MC multiplicity"}, signAxis}); + } + if (doprocessMC) { + const AxisSpec recoPtAxis{binsPt, "p_{T}^{reco} (GeV/c)"}; + const AxisSpec truthPtAxis{binsPt, "p_{T}^{truth} (GeV/c)"}; + const AxisSpec signAxis{2, -1.5, 1.5, "Omega PDG sign (+1 particle, -1 antiparticle)"}; + histos.add("MC/hMCRecXiK0sResponse", "Truth-matched selected #Omega #rightarrow #Xi K^{0}_{S}", kTHnSparseD, + {recoPtAxis, truthPtAxis, centAxis, {500, 0., 500., "MC multiplicity"}, signAxis}); + } // MC truth invariant mass (from MC particles) histos.add("MC/hMCTruthInvMassXiK0s", "MC Truth Inv Mass Xi + K^{0}_{S}", kTH1F, {invMassAxis}); @@ -230,7 +275,7 @@ struct Omega2012Analysis { ProcessModes modes; modes.xiK0s = doprocessData || doprocessMixedEvent || doprocessMC; - modes.xi1530K = doprocessXi1530KMicro || doprocessXi1530KTracks || doprocessXi1530KMCMicro || doprocessXi1530KMixedMicro; + modes.xi1530K = doprocessXi1530KMicro || doprocessXi1530KTracks || doprocessXi1530KMCMicro || doprocessXi1530KMCGen || doprocessXi1530KMixedMicro; modes.microTracks = doprocessXi1530KMicro || doprocessXi1530KMCMicro || doprocessXi1530KMixedMicro; modes.mcReco = doprocessMC || doprocessXi1530KMCMicro; modes.mixing = doprocessMixedEvent || doprocessXi1530KMixedMicro; @@ -289,6 +334,17 @@ struct Omega2012Analysis { histos.add("xi1530K/MC/hMCRecXiPt", "MC reconstructed Xi pT", kTH1F, {ptAxis}); histos.add("xi1530K/MC/hMCRecPionPt", "MC reconstructed pion pT", kTH1F, {ptAxis}); histos.add("xi1530K/MC/hMCRecKaonPt", "MC reconstructed kaon pT", kTH1F, {ptAxis}); + const AxisSpec recoPtAxis{binsPt, "p_{T}^{reco} (GeV/c)"}; + const AxisSpec truthPtAxis{binsPt, "p_{T}^{truth} (GeV/c)"}; + const AxisSpec signAxis{2, -1.5, 1.5, "Omega PDG sign (+1 particle, -1 antiparticle)"}; + histos.add("xi1530K/MC/hMCRecXi1530KResponse", "Truth-matched selected #Omega #rightarrow #Xi(1530) K", kTHnSparseD, + {recoPtAxis, truthPtAxis, centAxis, {500, 0., 500., "MC multiplicity"}, signAxis}); + } + if (doprocessXi1530KMCGen) { + const AxisSpec truthPtAxis{binsPt, "p_{T}^{truth} (GeV/c)"}; + const AxisSpec signAxis{2, -1.5, 1.5, "Omega PDG sign (+1 particle, -1 antiparticle)"}; + histos.add("xi1530K/MC/hMCGenSelectedXi1530KResponse", "Selected-event generated #Omega #rightarrow #Xi(1530) K", kTHnSparseD, + {truthPtAxis, centAxis, {500, 0., 500., "MC multiplicity"}, signAxis}); } } @@ -449,28 +505,71 @@ struct Omega2012Analysis { // MC reconstructed processing: match reconstructed Xi + K0s pairs to a common Omega(2012) mother void processMC(ResoMCCollisions::iterator const& collision, ResoMCCascades const& resocasc, - ResoMCV0s const& resov0s) + ResoMCV0s const& resov0s, + aod::ResoMCParents_001 const& resoParents) { if (!core.passesEventCuts(collision) || !core.passesMCEventCuts(collision)) { return; } - // No kinematic cut in the truth-matched spectra (as before the refactoring) + const auto omegaParents = omegaParentTruth(resoParents); auto onCandidate = [&](auto const& xi, auto const& v0, XiK0sCandidateValues const& c) { if (classifyXiK0sTruth(xi, v0) != XiK0sTruth::Matched) { return; } - if (!c.inRapidity) { + const int motherId = xi.motherId(); + const auto parentIt = omegaParents.find(motherId); + if (parentIt == omegaParents.end() || (cMCInjection && !parentIt->second.injected)) { + return; + } + if (cUseTruthRapidity && (!(std::abs(xi.motherRap()) < candidateCuts.cfgRapidityCut.value) || !std::isfinite(xi.motherRap()))) { + return; + } + if ((core.kinCutsEnabled() && !c.passesKinCut) || !c.inRapidity) { return; } histos.fill(HIST("MC/hMCRecOmega2012Pt"), c.omega.Pt()); histos.fill(HIST("MC/hMCRecOmega2012PtEta"), c.omega.Pt(), c.omega.Eta()); histos.fill(HIST("MC/hMCRecXiPt"), xi.pt()); histos.fill(HIST("MC/hMCRecK0sPt"), v0.pt()); + const double sign = xi.motherPDG() > 0 ? 1. : -1.; + histos.fill(HIST("MC/hMCRecXiK0sResponse"), c.omega.Pt(), xi.motherPt(), collision.cent(), collision.mcMultiplicity(), sign); }; core.forEachXiK0sCandidate(histos, collision, collision, resocasc, resov0s, false, nullptr, nullptr, onCandidate); } PROCESS_SWITCH(Omega2012Analysis, processMC, "Process MC with truth matching", false); + // Acceptance x efficiency denominator, counted per selected reconstructed collision, + // as in the Xi(1530) and Xi(1820) tasks. Split collisions repeat the parent set; + // this is not a unique-MC-event denominator or a signal-loss correction. + void processMCGen(ResoMCCollisions::iterator const& collision, + aod::ResoMCParents_001 const& resoParents) + { + if (!core.passesEventCuts(collision) || !core.passesMCEventCuts(collision)) { + return; + } + for (const auto& part : resoParents) { + if (std::abs(part.pdgCode()) != kOmega2012Minus) { + continue; + } + if (!(std::abs(part.y()) < candidateCuts.cfgRapidityCut.value) || !std::isfinite(part.y())) { + continue; + } + if (cMCInjection && !isInjectedOmegaParent(part)) { + continue; + } + // Require the same immediate Xi + K0S channel as processMCGenerated. + if ((std::abs(part.daughterPDG1()) == kXiMinus && part.daughterPDG2() == kK0Short) || + (std::abs(part.daughterPDG2()) == kXiMinus && part.daughterPDG1() == kK0Short)) { + histos.fill(HIST("MC/hMCGenSelectedXiK0sPt"), part.pt()); + const double sign = part.pdgCode() > 0 ? 1. : -1.; + histos.fill(HIST("MC/hMCGenSelectedXiK0sResponse"), part.pt(), collision.cent(), collision.mcMultiplicity(), sign); + } + } + } + PROCESS_SWITCH(Omega2012Analysis, processMCGen, "Process MC generated XiK0s parents in selected events", false); + + // Inclusive MC-particle QA. The rapidity-limited source histogram is a legacy all-event QA denominator; + // selected-event efficiency denominators are filled by processMCGen and processXi1530KMCGen. void processMCGenerated(aod::McParticles const& mcParticles) { // Process MC generated particles (no reconstruction requirement) @@ -524,7 +623,11 @@ struct Omega2012Analysis { if ((std::abs(daughter1PDG) == kXiMinus && daughter2PDG == kK0Short) || (std::abs(daughter2PDG) == kXiMinus && daughter1PDG == kK0Short)) { histos.fill(HIST("MC/hMCTruthInvMassXiK0s"), motherM); - histos.fill(HIST("MC/hMCTruthMassPtXiK0s"), motherM, motherPt); + // Legacy all-event rapidity-limited QA histogram; selected-event efficiency + // denominators are written by processMCGen and processXi1530KMCGen. + if (std::abs(y) < candidateCuts.cfgRapidityCut.value) { + histos.fill(HIST("MC/hMCTruthMassPtXiK0s"), motherM, motherPt); + } const bool isDaughter1Xi = std::abs(daughter1PDG) == kXiMinus; const auto& pXiTruth = isDaughter1Xi ? p1 : p2; @@ -538,7 +641,8 @@ struct Omega2012Analysis { // Mode B: Xi(1530)0 K- -> Xi- pi+ K- (and charge conjugate) from one collision, or Xi and tracks from two mixed collisions template - void fillXi1530K(const CollisionT& collision, float cent, const CascadesT& cascades, const TracksT& tracks, const TrackIdsT& trackIds) + void fillXi1530K(const CollisionT& collision, float cent, const CascadesT& cascades, const TracksT& tracks, const TrackIdsT& trackIds, + const OmegaParentTruthMap* omegaParents = nullptr) { // Input QA: same event only (each object once per collision) auto onXi = [&](auto const& xi, bool selected) { @@ -594,7 +698,7 @@ struct Omega2012Analysis { }; const bool fillWrongSign = core.fillWrongSign(); - auto onCandidate = [&]([[maybe_unused]] auto const& xi, [[maybe_unused]] auto const& pion, [[maybe_unused]] auto const& kaon, Xi1530KCandidateValues const& c) { + auto onCandidate = [&](auto const& xi, auto const& pion, auto const& kaon, Xi1530KCandidateValues const& c) { if (c.chargePattern != o2::analysis::omega2012ml::kSignalPattern) { // Charge-pattern controls: same event only, never in the signal histograms if constexpr (!IsMix) { @@ -623,6 +727,16 @@ struct Omega2012Analysis { if (classifyXi1530KTruth(xi, pion, kaon) != Xi1530KTruth::Matched) { return; } + if (!omegaParents) { + return; + } + const auto parentIt = omegaParents->find(kaon.motherId()); + if (parentIt == omegaParents->end() || (cMCInjection && !parentIt->second.injected)) { + return; + } + if (cUseTruthRapidity && (!std::isfinite(parentIt->second.y) || !(std::abs(parentIt->second.y) < candidateCuts.cfgRapidityCut.value))) { + return; + } histos.fill(HIST("xi1530K/MC/hMCRecOmega2012Pt"), c.omega.Pt()); histos.fill(HIST("xi1530K/MC/hMCRecOmega2012PtEta"), c.omega.Pt(), c.omega.Eta()); histos.fill(HIST("xi1530K/MC/hMCRecMass"), c.omega.M()); @@ -630,6 +744,8 @@ struct Omega2012Analysis { histos.fill(HIST("xi1530K/MC/hMCRecXiPt"), xi.pt()); histos.fill(HIST("xi1530K/MC/hMCRecPionPt"), pion.pt()); histos.fill(HIST("xi1530K/MC/hMCRecKaonPt"), kaon.pt()); + const double sign = parentIt->second.pdgCode > 0 ? 1. : -1.; + histos.fill(HIST("xi1530K/MC/hMCRecXi1530KResponse"), c.omega.Pt(), parentIt->second.pt, cent, collision.mcMultiplicity(), sign); } }; @@ -661,15 +777,40 @@ struct Omega2012Analysis { void processXi1530KMCMicro(ResoMCCollisions::iterator const& collision, ResoMCCascades const& resocasc, - ResoMCMicroTracks const& resomicrotracks) + ResoMCMicroTracks const& resomicrotracks, + aod::ResoMCParents_001 const& resoParents) { if (!core.passesEventCuts(collision) || !core.passesMCEventCuts(collision)) { return; } - fillXi1530K(collision, collision.cent(), resocasc, resomicrotracks, nullptr); + const auto omegaParents = omegaParentTruth(resoParents); + fillXi1530K(collision, collision.cent(), resocasc, resomicrotracks, nullptr, &omegaParents); } PROCESS_SWITCH(Omega2012Analysis, processXi1530KMCMicro, "Process Xi(1530)0 K mode with truth matching (ResoMicroTracks_001)", false); + // Selected-event generated denominator for Omega -> Xi(1530) K, counted once + // per selected reconstructed collision. Split collisions repeat the MC parents. + void processXi1530KMCGen(ResoMCCollisions::iterator const& collision, + aod::ResoMCParents_001 const& resoParents) + { + if (!core.passesEventCuts(collision) || !core.passesMCEventCuts(collision)) { + return; + } + for (const auto& parent : resoParents) { + if (std::abs(parent.pdgCode()) != kOmega2012Minus || + !std::isfinite(parent.y()) || !(std::abs(parent.y()) < candidateCuts.cfgRapidityCut.value) || + (cMCInjection && !isInjectedOmegaParent(parent))) { + continue; + } + if (classifyGeneratedOmega2012(parent.pdgCode(), parent.daughterPDG1(), parent.daughterPDG2()) != GeneratedChannel::Xi1530K) { + continue; + } + const double sign = parent.pdgCode() > 0 ? 1. : -1.; + histos.fill(HIST("xi1530K/MC/hMCGenSelectedXi1530KResponse"), parent.pt(), collision.cent(), collision.mcMultiplicity(), sign); + } + } + PROCESS_SWITCH(Omega2012Analysis, processXi1530KMCGen, "Process generated Xi(1530) K parents in selected events", false); + // Mixed events: Xi from collision 1, pion and kaon from collision 2 void processXi1530KMixedMicro(ResoCollisions const& collisions, aod::ResoCascades const& resocasc, diff --git a/PWGLF/Tasks/Resonances/omega2012TrainingTable.cxx b/PWGLF/Tasks/Resonances/omega2012TrainingTable.cxx index 3d2a397f6d6..240714b89a1 100644 --- a/PWGLF/Tasks/Resonances/omega2012TrainingTable.cxx +++ b/PWGLF/Tasks/Resonances/omega2012TrainingTable.cxx @@ -17,9 +17,10 @@ /// processXi1530KMicro, processXi1530KMCMicro. Generated parents: processMCTrue. MC truth of reconstructed MC: processMcTruth. /// Each candidate is written only to the tables of its own mode (PWGLF/DataModel/LFOmega2012MlTables.h); /// the selection is the one of omega2012Analysis.cxx (PWGLF/Core/Omega2012AnalysisCore.h, same JSON keys). -/// Only collisions with written candidates get an event row. In reconstructed MC the daughter labels of the written -/// objects and the pruned McParticles rows (Omega(2012) decay trees, ancestry of the labels) carry the truth, so the -/// output needs no source table. +/// Only collisions with written candidates get an event row. Generated Omega(2012) mothers are written separately to +/// OMMLGENAUDIT for every selected reconstructed MC collision, including collisions with no exported candidates. +/// In reconstructed MC, daughter labels of written objects and pruned McParticles rows carry candidate truth, so the +/// candidate output needs no source table. #include "PWGLF/Core/Omega2012AnalysisCore.h" #include "PWGLF/Core/Omega2012MlFeatures.h" @@ -45,6 +46,7 @@ #include #include #include +#include #include #include #include @@ -110,7 +112,7 @@ struct Omega2012TrainingTable { HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; // Selection shared with the Omega(2012) histogram task (same JSON keys) - o2::analysis::resonance::EventCuts eventCuts; + o2::analysis::resonance::EventCuts eventCuts = makeOmega2012EventCuts(); XiCuts xiCuts; K0sCuts k0sCuts; Xi1530KCuts xi1530KCuts; @@ -123,6 +125,7 @@ struct Omega2012TrainingTable { Configurable omega2012MlLooseAudit{"omega2012MlLooseAudit", true, "Record the loose cut flow and mass/pT/activity spectrum per mode"}; Configurable omega2012MlParityRows{"omega2012MlParityRows", 0, "Log feature hashes of the first N exported candidates of each mode"}; Configurable omega2012MlExportWrongSign{"omega2012MlExportWrongSign", false, "Also export the wrong-sign charge-pattern controls of the Xi1530K mode"}; + Configurable omega2012MlGenRapidityCut{"omega2012MlGenRapidityCut", 0.5f, "Generated-mother denominator rapidity limit; independent of candidate cfgRapidityCut"}; // Event row written before the first candidate of the collision struct PendingEvent { @@ -169,6 +172,9 @@ struct Omega2012TrainingTable { if (omega2012MlParityRows < 0) { LOG(fatal) << "omega2012MlParityRows must not be negative"; } + if (doprocessMCTrue && (!std::isfinite(omega2012MlGenRapidityCut.value) || omega2012MlGenRapidityCut.value <= 0.f)) { + LOG(fatal) << "omega2012MlGenRapidityCut must be finite and positive when processMCTrue is enabled"; + } if (omega2012MlExportStage.value != "loose" && omega2012MlExportStage.value != "selected") { LOG(fatal) << "omega2012MlExportStage must be loose or selected"; } @@ -538,7 +544,7 @@ struct Omega2012TrainingTable { if (!core.passesEventCuts(collision) || !core.passesMCEventCuts(collision)) { return; } - core.forEachGeneratedOmega2012(histos, resoParents, [&](auto const& part, GeneratedChannel channel) { + core.forEachGeneratedOmega2012(histos, resoParents, omega2012MlGenRapidityCut.value, [&](auto const& part, GeneratedChannel channel) { mlGenAudit(collision.globalIndex(), static_cast(part.originalMcParticleId()), part.pdgCode(), part.daughterPDG1(), part.daughterPDG2(), static_cast(channel), part.pt(), part.y()); }); From 132b8b8bf5e90e996a7a0c98650bc1191d90076d Mon Sep 17 00:00:00 2001 From: Bong-Hwi Lim Date: Fri, 23 Oct 2026 18:58:12 +0900 Subject: [PATCH 2/3] [PWGLF] Align K1 MC parent truth and efficiency response --- PWGLF/Core/K1AnalysisMicroCore.h | 57 +++++++++++++++++++++- PWGLF/DataModel/LFK1MlTables.h | 4 +- PWGLF/Tasks/Resonances/k1AnalysisMicro.cxx | 40 +++++++++++---- PWGLF/Tasks/Resonances/k1TrainingTable.cxx | 22 +++++---- 4 files changed, 101 insertions(+), 22 deletions(-) diff --git a/PWGLF/Core/K1AnalysisMicroCore.h b/PWGLF/Core/K1AnalysisMicroCore.h index 4e2cd0e720c..3b024afbf68 100644 --- a/PWGLF/Core/K1AnalysisMicroCore.h +++ b/PWGLF/Core/K1AnalysisMicroCore.h @@ -43,6 +43,7 @@ #include #include #include +#include #include #include @@ -64,6 +65,16 @@ enum class K1TruthChannel { KStarPi = 2 }; +struct K1ParentTruth { + double pt = std::numeric_limits::quiet_NaN(); + double y = std::numeric_limits::quiet_NaN(); + int pdgCode = 0; + K1TruthChannel channel = K1TruthChannel::None; +}; +using K1ParentTruthMap = std::unordered_map; + +inline K1TruthChannel classifyGeneratedK1(int charge, int daughter1, int daughter2); + // The value is the species index of the PID configuration in ResoAnalysisSelectionCore. enum class Species : int { Pion = 0, @@ -217,6 +228,49 @@ K1TruthChannel classifyK1Truth(const Track& pion1, const Track& pion2, const Kao return K1TruthChannel::None; } +template +int k1MotherId(K1TruthChannel channel, const Kaon& kaon, const Pion& pion1, const Pion& pion2) +{ + if (channel == K1TruthChannel::RhoK) { + return kaon.motherId(); + } + if (channel == K1TruthChannel::KStarPi) { + return std::abs(pion1.motherPDG()) == o2::constants::physics::Pdg::kK1_1270Plus ? pion1.motherId() : pion2.motherId(); + } + return -1; +} + +template +K1ParentTruthMap makeK1ParentTruthMap(const ParentsType& parents) +{ + K1ParentTruthMap result; + for (const auto& part : parents) { + if (std::abs(part.pdgCode()) != o2::constants::physics::Pdg::kK1_1270Plus || part.originalMcParticleId() < 0) { + continue; + } + const auto id = static_cast(part.originalMcParticleId()); + const int charge = part.pdgCode() > 0 ? 1 : -1; + result.emplace(id, K1ParentTruth{part.pt(), part.y(), part.pdgCode(), classifyGeneratedK1(charge, part.daughterPDG1(), part.daughterPDG2())}); + } + return result; +} + +inline const K1ParentTruth* findK1ParentTruth(const K1ParentTruthMap& parents, int64_t motherId, + K1TruthChannel channel, int charge) +{ + const auto it = parents.find(motherId); + if (it == parents.end()) { + return nullptr; + } + const auto& parent = it->second; + const int expectedPdg = charge * o2::constants::physics::Pdg::kK1_1270Plus; + if (channel == K1TruthChannel::None || parent.channel != channel || parent.pdgCode != expectedPdg || + !std::isfinite(parent.pt) || parent.pt < 0. || !std::isfinite(parent.y)) { + return nullptr; + } + return &parent; +} + // Immediate decay channel of a generated K1 from the PDG codes of its two daughters. inline K1TruthChannel classifyGeneratedK1(int charge, int daughter1, int daughter2) { @@ -635,7 +689,8 @@ class K1AnalysisMicroCore const int charge = part.pdgCode() > 0 ? 1 : -1; const K1TruthChannel channel = classifyGeneratedK1(charge, part.daughterPDG1(), part.daughterPDG2()); histos.fill(HIST("CutFlow/generated"), 0, static_cast(channel)); - if (part.y() < mCandidateCuts.cK1MinRap || part.y() > mCandidateCuts.cK1MaxRap) { + if (!std::isfinite(part.pt()) || part.pt() < 0. || !std::isfinite(part.y()) || + part.y() < mCandidateCuts.cK1MinRap || part.y() > mCandidateCuts.cK1MaxRap) { continue; } histos.fill(HIST("CutFlow/generated"), 1, static_cast(channel)); diff --git a/PWGLF/DataModel/LFK1MlTables.h b/PWGLF/DataModel/LFK1MlTables.h index 6add0d58252..d65b9b0a6d1 100644 --- a/PWGLF/DataModel/LFK1MlTables.h +++ b/PWGLF/DataModel/LFK1MlTables.h @@ -69,8 +69,8 @@ DECLARE_SOA_COLUMN(K1TruthStatus, k1TruthStatus, uint8_t); //! 0 data, DECLARE_SOA_COLUMN(K1TruthChannel, k1TruthChannel, uint8_t); //! 0 none, 1 rho K, 2 K* pi DECLARE_SOA_COLUMN(K1MotherPdg, k1MotherPdg, int32_t); DECLARE_SOA_COLUMN(K1MotherId, k1MotherId, int64_t); -DECLARE_SOA_COLUMN(K1GeneratedPt, k1GeneratedPt, float); //! NaN in K1MlTruth: not filled for reconstructed candidates -DECLARE_SOA_COLUMN(K1GeneratedY, k1GeneratedY, float); //! NaN in K1MlTruth: not filled for reconstructed candidates +DECLARE_SOA_COLUMN(K1GeneratedPt, k1GeneratedPt, float); //! Parent-table pT for a validated reconstructed MC match; NaN otherwise +DECLARE_SOA_COLUMN(K1GeneratedY, k1GeneratedY, float); //! Parent-table y for a validated reconstructed MC match; NaN otherwise DECLARE_SOA_COLUMN(K1OriginalMcParticleId, k1OriginalMcParticleId, int64_t); DECLARE_SOA_COLUMN(K1DaughterPdg1, k1DaughterPdg1, int32_t); DECLARE_SOA_COLUMN(K1DaughterPdg2, k1DaughterPdg2, int32_t); diff --git a/PWGLF/Tasks/Resonances/k1AnalysisMicro.cxx b/PWGLF/Tasks/Resonances/k1AnalysisMicro.cxx index a84ed359767..efd75953055 100644 --- a/PWGLF/Tasks/Resonances/k1AnalysisMicro.cxx +++ b/PWGLF/Tasks/Resonances/k1AnalysisMicro.cxx @@ -52,6 +52,7 @@ struct HistogramOptions : ConfigurableGroup { Configurable cNbinsDiv{"cNbinsDiv", 1, "Integer to divide the number of bins"}; Configurable additionalQAplots{"additionalQAplots", true, "Additional QA plots"}; Configurable cfgTruthDebug{"cfgTruthDebug", 0, "Maximum logged matched candidates per truth channel"}; + Configurable cUseTruthRapidity{"cUseTruthRapidity", true, "Require generated K1 rapidity in the configured window for MC efficiency numerators"}; }; enum BinAnti : unsigned int { @@ -231,12 +232,15 @@ struct K1AnalysisMicro { AxisSpec channelAxis = {3, -0.5, 2.5, "0: non-K1, 1: rho K, 2: K* pi"}; histos.add("MCReco/collisions", "Selected reconstructed MC collisions", HistType::kTH1D, {{1, 0, 1}}); histos.add("MCReco/microTracks", "Input micro tracks in selected MC collisions", HistType::kTH1D, {{1, 0, 1}}); - histos.add("MCReco/channel", "All selected pi-pi-K combinations by truth channel", HistType::kTH1D, {channelAxis}); + // These channel and pT histograms are pre-truth-fiducial QA; MCReco/response is the validated numerator. + histos.add("MCReco/channel", "Pre-truth-fiducial QA: selected pi-pi-K combinations by truth channel", HistType::kTH1D, {channelAxis}); histos.add("MCReco/mass", "Reconstructed mass by truth channel", HistType::kTH2D, {channelAxis, invMassAxisReso}); - histos.add("MCReco/pt", "Reconstructed pT by truth channel", HistType::kTH2D, {channelAxis, ptAxis}); + histos.add("MCReco/pt", "Pre-truth-fiducial QA: reconstructed pT by truth channel", HistType::kTH2D, {channelAxis, ptAxis}); histos.add("MCReco/piPiMass", "pi-pi mass by truth channel", HistType::kTH2D, {channelAxis, invMassAxisRho}); histos.add("MCReco/pi1KMass", "First pion-kaon mass by truth channel", HistType::kTH2D, {channelAxis, invMassAxisK892}); histos.add("MCReco/pi2KMass", "Second pion-kaon mass by truth channel", HistType::kTH2D, {channelAxis, invMassAxisK892}); + histos.add("MCReco/response", "Selected MC candidates;reconstructed pT;generated pT;channel;charge;centrality", HistType::kTHnSparseD, + {ptAxis, ptAxis, channelAxis, AxisSpec{2, -1.5, 1.5, "K1 charge"}, centAxis}); histos.add("k1invmass_MC", "Invariant mass of K1(1270)", HistType::kTH1F, {invMassAxisReso}); histos.add("k1invmass_MC_noK1", "Invariant mass of K1(1270)", HistType::kTH1F, {invMassAxisReso}); @@ -272,6 +276,8 @@ struct K1AnalysisMicro { AxisSpec channelAxis = {3, -0.5, 2.5, "0: other/unresolved, 1: rho K, 2: K* pi"}; histos.add("MCGen/chargeChannel", "K1 parents in selected reconstructed events, inside the K1 rapidity window", HistType::kTH2D, {{2, -1.5, 1.5, "K1 charge"}, channelAxis}); histos.add("MCGen/ptChannel", "Generated K1 pT by immediate decay channel", HistType::kTH2D, {channelAxis, ptAxis}); + histos.add("MCGen/response", "Generated K1 parents;generated pT;channel;charge;centrality", HistType::kTHnSparseD, + {ptAxis, channelAxis, AxisSpec{2, -1.5, 1.5, "K1 charge"}, centAxis}); } } @@ -380,7 +386,8 @@ struct K1AnalysisMicro { // (or one mixed pair of collisions). The selection itself is the shared K1 core. // dTracks1: bachelor kaons, dTracks2: pions. template - void fillHistograms(const CollisionType& collision, const TracksType& dTracks1, const TracksType& dTracks2) + void fillHistograms(const CollisionType& collision, const TracksType& dTracks1, const TracksType& dTracks2, + const K1ParentTruthMap* parentTruth = nullptr) { const bool fillQA = !IsMix && histogramOptions.additionalQAplots; const auto multiplicity = collision.cent(); @@ -474,9 +481,17 @@ struct K1AnalysisMicro { pion1.motherPDG(), pion2.motherPDG(), kaon.motherPDG(), pion1.siblingIds()[0], pion1.siblingIds()[1], pion2.siblingIds()[0], pion2.siblingIds()[1], kaon.siblingIds()[0], kaon.siblingIds()[1]); } - typeK1 = kaon.sign() > 0 ? BinType::kK1P_Rec : BinType::kK1N_Rec; - histos.fill(HIST("hInvmass_K1"), typeNormal, typeK1, multiplicity, c.k1.Pt(), c.k1.M()); - histos.fill(HIST("k1invmass_MC"), c.k1.M()); + const int motherId = k1MotherId(channel, kaon, pion1, pion2); + const int charge = kaon.pdgCode() > 0 ? 1 : -1; + const auto* parent = parentTruth ? findK1ParentTruth(*parentTruth, motherId, channel, charge) : nullptr; + const bool truthRapidity = c.isUnlikeSign && parent && (!histogramOptions.cUseTruthRapidity || (parent->y >= candidateCuts.cK1MinRap && parent->y <= candidateCuts.cK1MaxRap)); + if (truthRapidity) { + typeK1 = kaon.sign() > 0 ? BinType::kK1P_Rec : BinType::kK1N_Rec; + histos.fill(HIST("hInvmass_K1"), typeNormal, typeK1, multiplicity, c.k1.Pt(), c.k1.M()); + histos.fill(HIST("k1invmass_MC"), c.k1.M()); + const int parentCharge = parent->pdgCode > 0 ? 1 : -1; + histos.fill(HIST("MCReco/response"), c.k1.Pt(), parent->pt, channelBin, parentCharge, multiplicity); + } histos.fill(HIST("QAMC/K1OA"), c.angle); histos.fill(HIST("QAMC/K1PairAsym"), c.pairAsym); histos.fill(HIST("QAMC/hInvmassK892_Rho"), c.mass13, c.secondary.M()); @@ -515,17 +530,18 @@ struct K1AnalysisMicro { PROCESS_SWITCH(K1AnalysisMicro, processResoMicroTracks, "Process ResoMicroTracks", true); void processMC(ResoMCCols::iterator const& collision, - ResoMCTracks const& resotracks) + ResoMCTracks const& resotracks, ResoMCParents const& resoParents) { if (!core.passesEventCuts(collision) || !core.passesMCEventCuts(collision)) { return; } histos.fill(HIST("MCReco/collisions"), 0.5); - fillHistograms(collision, resotracks, resotracks); + const auto parentTruth = makeK1ParentTruthMap(resoParents); + fillHistograms(collision, resotracks, resotracks, &parentTruth); } PROCESS_SWITCH(K1AnalysisMicro, processMC, "Process Event for MC", false); - void processMCMicro(ResoMCCols::iterator const& collision, ResoMCMicroTracks const& tracks) + void processMCMicro(ResoMCCols::iterator const& collision, ResoMCMicroTracks const& tracks, ResoMCParents const& resoParents) { // The modular producer already selected these reconstructed collisions. // Apply precisely the same reconstruction loop as the data baseline. @@ -534,7 +550,8 @@ struct K1AnalysisMicro { } histos.fill(HIST("MCReco/collisions"), 0.5); histos.fill(HIST("MCReco/microTracks"), 0.5, tracks.size()); - fillHistograms(collision, tracks, tracks); + const auto parentTruth = makeK1ParentTruthMap(resoParents); + fillHistograms(collision, tracks, tracks, &parentTruth); } PROCESS_SWITCH(K1AnalysisMicro, processMCMicro, "Process reconstructed MC with micro v001 tables", false); @@ -548,6 +565,9 @@ struct K1AnalysisMicro { const int charge = part.pdgCode() > 0 ? 1 : -1; histos.fill(HIST("MCGen/chargeChannel"), charge, static_cast(channel)); histos.fill(HIST("MCGen/ptChannel"), static_cast(channel), part.pt()); + if (channel != K1TruthChannel::None) { + histos.fill(HIST("MCGen/response"), part.pt(), static_cast(channel), charge, collision.cent()); + } }); } PROCESS_SWITCH(K1AnalysisMicro, processMCTrue, "Process generated K1 in selected events with v001 parents", false); diff --git a/PWGLF/Tasks/Resonances/k1TrainingTable.cxx b/PWGLF/Tasks/Resonances/k1TrainingTable.cxx index 8b070a97be1..008984a0112 100644 --- a/PWGLF/Tasks/Resonances/k1TrainingTable.cxx +++ b/PWGLF/Tasks/Resonances/k1TrainingTable.cxx @@ -182,7 +182,7 @@ struct K1TrainingTable { template void writeK1MlCandidate(Collision const& collision, Kaon const& kaon, Pion const& samePion, Pion const& oppPion, - K1TruthChannel channel, uint16_t passBits) + K1TruthChannel channel, uint16_t passBits, const K1ParentTruthMap* parentTruth = nullptr) { using o2::analysis::k1ml::BuildStatus; const auto canonical = o2::analysis::k1ml::canonicalizeUS(o2::analysis::k1ml::makeTrackSnapshot(kaon), o2::analysis::k1ml::makeTrackSnapshot(samePion), o2::analysis::k1ml::makeTrackSnapshot(oppPion)); @@ -215,14 +215,17 @@ struct K1TrainingTable { if constexpr (IsMC) { const bool matched = channel != K1TruthChannel::None; int motherId = -1; + const K1ParentTruth* parent = nullptr; if (matched) { - motherId = channel == K1TruthChannel::RhoK ? kaon.motherId() : std::abs(samePion.motherPDG()) == Pdg::kK1_1270Plus ? samePion.motherId() - : oppPion.motherId(); + motherId = k1MotherId(channel, kaon, samePion, oppPion); + const int charge = kaon.pdgCode() > 0 ? 1 : -1; + parent = parentTruth ? findK1ParentTruth(*parentTruth, motherId, channel, charge) : nullptr; } // Immediate-mother information alone does not prove a physical UID. k1MlTruth(row, matched ? uint8_t{1} : uint8_t{2}, static_cast(channel), - matched ? kaon.sign() * Pdg::kK1_1270Plus : 0, static_cast(motherId), - std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN()); + parent ? parent->pdgCode : (matched ? kaon.sign() * Pdg::kK1_1270Plus : 0), static_cast(motherId), + parent ? static_cast(parent->pt) : std::numeric_limits::quiet_NaN(), + parent ? static_cast(parent->y) : std::numeric_limits::quiet_NaN()); } else { k1MlTruth(row, uint8_t{0}, uint8_t{0}, 0, int64_t{-1}, std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN()); @@ -244,7 +247,7 @@ struct K1TrainingTable { } PROCESS_SWITCH(K1TrainingTable, processResoMicroTracks, "Write K1 candidates from data micro v001 tables", true); - void processMCMicro(ResoMCCols::iterator const& collision, ResoMCMicroTracks const& tracks) + void processMCMicro(ResoMCCols::iterator const& collision, ResoMCMicroTracks const& tracks, ResoMCParents const& resoParents) { // The modular producer already selected these reconstructed collisions. if (!core.passesEventCuts(collision) || !core.passesMCEventCuts(collision)) { @@ -253,10 +256,11 @@ struct K1TrainingTable { histos.fill(HIST("MCReco/collisions"), 0.5); histos.fill(HIST("MCReco/microTracks"), 0.5, tracks.size()); writeK1MlEvent(collision); + const auto parentTruth = makeK1ParentTruthMap(resoParents); core.forEachCandidate(histos, collision, tracks, tracks, false, nullptr, nullptr, - [this](auto const& coll, auto const& kaon, auto const& samePion, auto const& oppPion, - K1TruthChannel channel, uint16_t passBits) { - writeK1MlCandidate(coll, kaon, samePion, oppPion, channel, passBits); + [this, &parentTruth](auto const& coll, auto const& kaon, auto const& samePion, auto const& oppPion, + K1TruthChannel channel, uint16_t passBits) { + writeK1MlCandidate(coll, kaon, samePion, oppPion, channel, passBits, &parentTruth); }); } PROCESS_SWITCH(K1TrainingTable, processMCMicro, "Write K1 candidates with truth from reconstructed MC micro v001 tables", false); From 116f23d4438962b70c0d943d377db0329fd441b0 Mon Sep 17 00:00:00 2001 From: Bong-Hwi Lim Date: Fri, 23 Oct 2026 23:10:09 +0900 Subject: [PATCH 3/3] [PWGLF] Fix clang-tidy findings in Omega2012 and K1 tables Mark MC-only callback parameters as maybe_unused and remove redundant integer casts. Bind fixed-size daughter index arrays by reference. Keep the persistent K1 feature column format and document its narrow exception to modernize-avoid-c-arrays. --- PWGLF/DataModel/LFK1MlTables.h | 1 + PWGLF/Tasks/Resonances/omega2012Analysis.cxx | 2 +- PWGLF/Tasks/Resonances/omega2012TrainingTable.cxx | 12 ++++++------ 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/PWGLF/DataModel/LFK1MlTables.h b/PWGLF/DataModel/LFK1MlTables.h index d65b9b0a6d1..7b2c20c8174 100644 --- a/PWGLF/DataModel/LFK1MlTables.h +++ b/PWGLF/DataModel/LFK1MlTables.h @@ -63,6 +63,7 @@ DECLARE_SOA_COLUMN(K1Charge, k1Charge, int8_t); // 16 = candidate cuts. // Candidates written at the "selected" export stage carry 31. DECLARE_SOA_COLUMN(K1BaselinePassBits, k1BaselinePassBits, uint16_t); +// NOLINTNEXTLINE(modernize-avoid-c-arrays) -- persistent fixed-size AOD column DECLARE_SOA_COLUMN(K1MasterFeatures, k1MasterFeatures, float[125]); //! 125 master features of the K1 ML feature contract (PWGLF/Core/K1MlFeatures.h) DECLARE_SOA_COLUMN(K1FeatureStatus, k1FeatureStatus, uint8_t); //! o2::analysis::k1ml::BuildStatus; only Ok (0) rows are written DECLARE_SOA_COLUMN(K1TruthStatus, k1TruthStatus, uint8_t); //! 0 data, 1 matched, 2 unmatched diff --git a/PWGLF/Tasks/Resonances/omega2012Analysis.cxx b/PWGLF/Tasks/Resonances/omega2012Analysis.cxx index 31bc465954d..ec08e498c86 100644 --- a/PWGLF/Tasks/Resonances/omega2012Analysis.cxx +++ b/PWGLF/Tasks/Resonances/omega2012Analysis.cxx @@ -698,7 +698,7 @@ struct Omega2012Analysis { }; const bool fillWrongSign = core.fillWrongSign(); - auto onCandidate = [&](auto const& xi, auto const& pion, auto const& kaon, Xi1530KCandidateValues const& c) { + auto onCandidate = [&]([[maybe_unused]] auto const& xi, [[maybe_unused]] auto const& pion, [[maybe_unused]] auto const& kaon, Xi1530KCandidateValues const& c) { if (c.chargePattern != o2::analysis::omega2012ml::kSignalPattern) { // Charge-pattern controls: same event only, never in the signal histograms if constexpr (!IsMix) { diff --git a/PWGLF/Tasks/Resonances/omega2012TrainingTable.cxx b/PWGLF/Tasks/Resonances/omega2012TrainingTable.cxx index 240714b89a1..d6c7eb317a3 100644 --- a/PWGLF/Tasks/Resonances/omega2012TrainingTable.cxx +++ b/PWGLF/Tasks/Resonances/omega2012TrainingTable.cxx @@ -274,7 +274,7 @@ struct Omega2012TrainingTable { return -1; } const auto row = trackId - static_cast(mcTrackLabelsDf->offset()); - if (row < 0 || row >= static_cast(mcTrackLabelsDf->size())) { + if (row < 0 || row >= mcTrackLabelsDf->size()) { return -1; } return mcTrackLabelsDf->rawIteratorAt(row).mcParticleId(); @@ -289,7 +289,7 @@ struct Omega2012TrainingTable { int64_t row = label; for (int step = 0; step <= MaxAncestorSteps; ++step) { const auto local = row - static_cast(mcParticlesDf->offset()); - if (row < 0 || local < 0 || local >= static_cast(mcParticlesDf->size())) { + if (row < 0 || local < 0 || local >= mcParticlesDf->size()) { return; } const auto particle = mcParticlesDf->rawIteratorAt(local); @@ -311,7 +311,7 @@ struct Omega2012TrainingTable { if (auto it = mlCascadeRows.find(id); it != mlCascadeRows.end()) { return it->second; } - const auto indices = xi.cascadeIndices(); + const auto& indices = xi.cascadeIndices(); const std::array daughterIds{indices[0], indices[1], indices[2]}; mlCascades(mlEventRow, id, xi.px(), xi.py(), xi.pz(), static_cast(xi.sign()), xi.mXi(), xi.mLambda(), xi.v0CosPA(), xi.cascCosPA(), xi.daughDCA(), xi.cascDaughDCA(), @@ -344,7 +344,7 @@ struct Omega2012TrainingTable { if (auto it = mlV0Rows.find(id); it != mlV0Rows.end()) { return it->second; } - const auto indices = v0.indices(); + const auto& indices = v0.indices(); const std::array daughterIds{indices[0], indices[1]}; mlV0s(mlEventRow, id, v0.px(), v0.py(), v0.pz(), v0.mK0Short(), v0.mLambda(), v0.mAntiLambda(), v0.v0CosPA(), v0.daughDCA(), v0.dcapostopv(), v0.dcanegtopv(), v0.dcav0topv(), v0.transRadius(), @@ -557,14 +557,14 @@ struct Omega2012TrainingTable { { mlAncestryRows.clear(); const auto offset = static_cast(mcParticles.offset()); - const auto nRows = static_cast(mcParticles.size()); + const auto nRows = mcParticles.size(); std::unordered_set treeRows; std::vector> stack; for (const auto& parent : mcParticles) { if (std::abs(parent.pdgCode()) != o2::constants::physics::Pdg::kOmega2012Minus) { continue; } - stack.assign(1, {static_cast(parent.globalIndex()), 0}); + stack.assign(1, {parent.globalIndex(), 0}); while (!stack.empty()) { const auto [row, depth] = stack.back(); stack.pop_back();