Skip to content

Commit 4f7fdfb

Browse files
author
Bong-Hwi Lim
committed
[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.
1 parent a2758f4 commit 4f7fdfb

3 files changed

Lines changed: 181 additions & 22 deletions

File tree

‎PWGLF/Core/Omega2012AnalysisCore.h‎

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,17 @@ using o2::analysis::resonance::ResoAnalysisSelectionCore;
5858
using o2::analysis::resonance::TrackCuts;
5959
using o2::analysis::resonance::TrackStage;
6060

61+
// Omega(2012) analysis and ML export share these event-level selection defaults.
62+
// Keep the generic EventCuts defaults unchanged for other resonance tasks.
63+
inline EventCuts makeOmega2012EventCuts()
64+
{
65+
EventCuts cuts;
66+
cuts.cRecoINELgt0.value = true;
67+
cuts.cMCINELgt0.value = true;
68+
cuts.cMCVtxIn10.value = true;
69+
return cuts;
70+
}
71+
6172
enum class DecayMode : uint8_t {
6273
XiK0s = 0, // Omega(2012)- -> Xi- K0S
6374
Xi1530K = 1 // Omega(2012)- -> Xi(1530)0 K- -> Xi- pi+ K-
@@ -932,19 +943,20 @@ class Omega2012AnalysisCore
932943
}
933944
}
934945

935-
// Generated Omega(2012) parents of a selected reconstructed MC collision (ResoMCParents_001). The optional callback
936-
// receives (parent, immediate channel) for the parents inside the rapidity window. Split reconstructed collisions
937-
// repeat parent sets: this is not an unconditional generated denominator.
946+
// Generated Omega(2012) parents of a selected reconstructed MC collision (ResoMCParents_001). The caller supplies
947+
// the denominator rapidity limit so it can remain independent of a wider candidate-export window. The optional
948+
// callback receives (parent, immediate channel) for the parents inside that rapidity window. Split reconstructed
949+
// collisions repeat parent sets: this is not an unconditional generated denominator.
938950
template <typename ParentsType, typename Callback = std::nullptr_t>
939-
void forEachGeneratedOmega2012(o2::framework::HistogramRegistry& histos, const ParentsType& resoParents, Callback callback = nullptr)
951+
void forEachGeneratedOmega2012(o2::framework::HistogramRegistry& histos, const ParentsType& resoParents, float rapidityMax, Callback callback = nullptr)
940952
{
941953
for (const auto& part : resoParents) {
942954
if (std::abs(part.pdgCode()) != o2::constants::physics::Pdg::kOmega2012Minus) {
943955
continue;
944956
}
945957
const GeneratedChannel channel = classifyGeneratedOmega2012(part.pdgCode(), part.daughterPDG1(), part.daughterPDG2());
946958
histos.fill(HIST("CutFlow/generated"), 0, static_cast<int>(channel));
947-
if (!(std::abs(part.y()) < mCandidateCuts.cfgRapidityCut.value)) {
959+
if (!(std::abs(part.y()) < rapidityMax)) {
948960
continue;
949961
}
950962
histos.fill(HIST("CutFlow/generated"), 1, static_cast<int>(channel));

‎PWGLF/Tasks/Resonances/omega2012Analysis.cxx‎

Lines changed: 153 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@
1414
/// \author Bong-Hwi Lim <bong-hwi.lim@cern.ch>
1515
///
1616
/// Two decay modes, analysed separately (never merged in any histogram or process function):
17-
/// - mode A (XiK0s): Omega(2012)- -> Xi- K0S. processData, processMixedEvent, processMC, processMCGenerated.
17+
/// - mode A (XiK0s): Omega(2012)- -> Xi- K0S. processData, processMixedEvent, processMC, processMCGen, processMCGenerated.
1818
/// Histograms: Event/, QAbefore/, QAafter/, omega2012/, MC/ (unchanged).
1919
/// - mode B (Xi1530K): Omega(2012)- -> Xi(1530)0 K- -> Xi- pi+ K- with a charged kaon track.
20-
/// processXi1530KMicro, processXi1530KTracks, processXi1530KMCMicro, processXi1530KMixedMicro.
20+
/// processXi1530KMicro, processXi1530KTracks, processXi1530KMCMicro, processXi1530KMCGen, processXi1530KMixedMicro.
2121
/// Histograms: xi1530K/ (signal charge pattern) and xi1530K_wrongSign/ (charge-pattern controls).
2222
/// Selection, candidate enumeration and truth classification: PWGLF/Core/Omega2012AnalysisCore.h.
2323
///
@@ -49,13 +49,15 @@
4949
#include <Framework/OutputObjHeader.h>
5050
#include <Framework/SliceCache.h>
5151
#include <Framework/runDataProcessing.h>
52+
#include <SimulationDataFormat/MCGenProperties.h>
5253

5354
#include <Math/Vector4D.h> // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h)
5455
#include <Math/Vector4Dfwd.h>
5556
#include <TPDGCode.h>
5657

5758
#include <cmath>
5859
#include <tuple>
60+
#include <unordered_map>
5961

6062
using namespace o2;
6163
using namespace o2::framework;
@@ -85,14 +87,43 @@ struct Omega2012Analysis {
8587
Configurable<int> cInvMassBins{"cInvMassBins", 600, "Invariant mass bins"};
8688

8789
// Selection shared with the Omega(2012) training-table task (plain JSON keys; the mode-B track cuts carry "trk.")
88-
o2::analysis::resonance::EventCuts eventCuts;
90+
o2::analysis::resonance::EventCuts eventCuts = makeOmega2012EventCuts();
8991
XiCuts xiCuts;
9092
K0sCuts k0sCuts;
9193
Xi1530KCuts xi1530KCuts;
9294
o2::analysis::resonance::TrackCuts trackCuts = makeXi1530KTrackCuts();
9395
PionPidCuts pionPID;
9496
KaonPidCuts kaonPID;
9597
CandidateCuts candidateCuts;
98+
Configurable<bool> cUseTruthRapidity{"cUseTruthRapidity", true, "Require the matched Omega(2012) truth rapidity inside cfgRapidityCut"};
99+
Configurable<bool> cMCInjection{"cMCInjection", false, "Restrict MC numerator and denominator to Omega parents with generator status -11"};
100+
101+
struct OmegaParentTruth {
102+
float pt = 0.f;
103+
float y = 0.f;
104+
int pdgCode = 0;
105+
bool injected = false;
106+
};
107+
using OmegaParentTruthMap = std::unordered_map<int, OmegaParentTruth>;
108+
109+
template <typename Parent>
110+
static bool isInjectedOmegaParent(const Parent& parent)
111+
{
112+
constexpr int InjectedGeneratorStatus = -11;
113+
return o2::mcgenstatus::getGenStatusCode(parent.statusCode()) == InjectedGeneratorStatus;
114+
}
115+
116+
template <typename Parents>
117+
OmegaParentTruthMap omegaParentTruth(const Parents& parents)
118+
{
119+
OmegaParentTruthMap parentTruth;
120+
for (const auto& parent : parents) {
121+
if (std::abs(parent.pdgCode()) == kOmega2012Minus) {
122+
parentTruth.emplace(parent.originalMcParticleId(), OmegaParentTruth{parent.pt(), parent.y(), parent.pdgCode(), isInjectedOmegaParent(parent)});
123+
}
124+
}
125+
return parentTruth;
126+
}
96127

97128
// Event Mixing
98129
Configurable<int> nEvtMixing{"nEvtMixing", 10, "Number of events to mix"};
@@ -217,6 +248,20 @@ struct Omega2012Analysis {
217248
histos.add("MC/hMCGenOmega2012Y", "MC Generated Omega(2012) rapidity", kTH1F, {rapidityAxis});
218249
histos.add("MC/hMCRecOmega2012Pt", "MC Reconstructed Omega(2012) pT", kTH1F, {ptAxis});
219250
histos.add("MC/hMCRecOmega2012PtEta", "MC Reconstructed Omega(2012) pT vs eta", kTH2F, {ptAxis, etaAxis});
251+
if (doprocessMCGen) {
252+
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});
253+
const AxisSpec truthPtAxis{binsPt, "p_{T}^{truth} (GeV/c)"};
254+
const AxisSpec signAxis{2, -1.5, 1.5, "Omega PDG sign (+1 particle, -1 antiparticle)"};
255+
histos.add("MC/hMCGenSelectedXiK0sResponse", "Selected-event generated #Omega #rightarrow #Xi K^{0}_{S}", kTHnSparseD,
256+
{truthPtAxis, centAxis, {500, 0., 500., "MC multiplicity"}, signAxis});
257+
}
258+
if (doprocessMC) {
259+
const AxisSpec recoPtAxis{binsPt, "p_{T}^{reco} (GeV/c)"};
260+
const AxisSpec truthPtAxis{binsPt, "p_{T}^{truth} (GeV/c)"};
261+
const AxisSpec signAxis{2, -1.5, 1.5, "Omega PDG sign (+1 particle, -1 antiparticle)"};
262+
histos.add("MC/hMCRecXiK0sResponse", "Truth-matched selected #Omega #rightarrow #Xi K^{0}_{S}", kTHnSparseD,
263+
{recoPtAxis, truthPtAxis, centAxis, {500, 0., 500., "MC multiplicity"}, signAxis});
264+
}
220265

221266
// MC truth invariant mass (from MC particles)
222267
histos.add("MC/hMCTruthInvMassXiK0s", "MC Truth Inv Mass Xi + K^{0}_{S}", kTH1F, {invMassAxis});
@@ -230,7 +275,7 @@ struct Omega2012Analysis {
230275

231276
ProcessModes modes;
232277
modes.xiK0s = doprocessData || doprocessMixedEvent || doprocessMC;
233-
modes.xi1530K = doprocessXi1530KMicro || doprocessXi1530KTracks || doprocessXi1530KMCMicro || doprocessXi1530KMixedMicro;
278+
modes.xi1530K = doprocessXi1530KMicro || doprocessXi1530KTracks || doprocessXi1530KMCMicro || doprocessXi1530KMCGen || doprocessXi1530KMixedMicro;
234279
modes.microTracks = doprocessXi1530KMicro || doprocessXi1530KMCMicro || doprocessXi1530KMixedMicro;
235280
modes.mcReco = doprocessMC || doprocessXi1530KMCMicro;
236281
modes.mixing = doprocessMixedEvent || doprocessXi1530KMixedMicro;
@@ -289,6 +334,17 @@ struct Omega2012Analysis {
289334
histos.add("xi1530K/MC/hMCRecXiPt", "MC reconstructed Xi pT", kTH1F, {ptAxis});
290335
histos.add("xi1530K/MC/hMCRecPionPt", "MC reconstructed pion pT", kTH1F, {ptAxis});
291336
histos.add("xi1530K/MC/hMCRecKaonPt", "MC reconstructed kaon pT", kTH1F, {ptAxis});
337+
const AxisSpec recoPtAxis{binsPt, "p_{T}^{reco} (GeV/c)"};
338+
const AxisSpec truthPtAxis{binsPt, "p_{T}^{truth} (GeV/c)"};
339+
const AxisSpec signAxis{2, -1.5, 1.5, "Omega PDG sign (+1 particle, -1 antiparticle)"};
340+
histos.add("xi1530K/MC/hMCRecXi1530KResponse", "Truth-matched selected #Omega #rightarrow #Xi(1530) K", kTHnSparseD,
341+
{recoPtAxis, truthPtAxis, centAxis, {500, 0., 500., "MC multiplicity"}, signAxis});
342+
}
343+
if (doprocessXi1530KMCGen) {
344+
const AxisSpec truthPtAxis{binsPt, "p_{T}^{truth} (GeV/c)"};
345+
const AxisSpec signAxis{2, -1.5, 1.5, "Omega PDG sign (+1 particle, -1 antiparticle)"};
346+
histos.add("xi1530K/MC/hMCGenSelectedXi1530KResponse", "Selected-event generated #Omega #rightarrow #Xi(1530) K", kTHnSparseD,
347+
{truthPtAxis, centAxis, {500, 0., 500., "MC multiplicity"}, signAxis});
292348
}
293349
}
294350

@@ -449,28 +505,71 @@ struct Omega2012Analysis {
449505
// MC reconstructed processing: match reconstructed Xi + K0s pairs to a common Omega(2012) mother
450506
void processMC(ResoMCCollisions::iterator const& collision,
451507
ResoMCCascades const& resocasc,
452-
ResoMCV0s const& resov0s)
508+
ResoMCV0s const& resov0s,
509+
aod::ResoMCParents_001 const& resoParents)
453510
{
454511
if (!core.passesEventCuts(collision) || !core.passesMCEventCuts(collision)) {
455512
return;
456513
}
457-
// No kinematic cut in the truth-matched spectra (as before the refactoring)
514+
const auto omegaParents = omegaParentTruth(resoParents);
458515
auto onCandidate = [&](auto const& xi, auto const& v0, XiK0sCandidateValues const& c) {
459516
if (classifyXiK0sTruth(xi, v0) != XiK0sTruth::Matched) {
460517
return;
461518
}
462-
if (!c.inRapidity) {
519+
const int motherId = xi.motherId();
520+
const auto parentIt = omegaParents.find(motherId);
521+
if (parentIt == omegaParents.end() || (cMCInjection && !parentIt->second.injected)) {
522+
return;
523+
}
524+
if (cUseTruthRapidity && (!(std::abs(xi.motherRap()) < candidateCuts.cfgRapidityCut.value) || !std::isfinite(xi.motherRap()))) {
525+
return;
526+
}
527+
if ((core.kinCutsEnabled() && !c.passesKinCut) || !c.inRapidity) {
463528
return;
464529
}
465530
histos.fill(HIST("MC/hMCRecOmega2012Pt"), c.omega.Pt());
466531
histos.fill(HIST("MC/hMCRecOmega2012PtEta"), c.omega.Pt(), c.omega.Eta());
467532
histos.fill(HIST("MC/hMCRecXiPt"), xi.pt());
468533
histos.fill(HIST("MC/hMCRecK0sPt"), v0.pt());
534+
const double sign = xi.motherPDG() > 0 ? 1. : -1.;
535+
histos.fill(HIST("MC/hMCRecXiK0sResponse"), c.omega.Pt(), xi.motherPt(), collision.cent(), collision.mcMultiplicity(), sign);
469536
};
470537
core.forEachXiK0sCandidate<true, false>(histos, collision, collision, resocasc, resov0s, false, nullptr, nullptr, onCandidate);
471538
}
472539
PROCESS_SWITCH(Omega2012Analysis, processMC, "Process MC with truth matching", false);
473540

541+
// Acceptance x efficiency denominator, counted per selected reconstructed collision,
542+
// as in the Xi(1530) and Xi(1820) tasks. Split collisions repeat the parent set;
543+
// this is not a unique-MC-event denominator or a signal-loss correction.
544+
void processMCGen(ResoMCCollisions::iterator const& collision,
545+
aod::ResoMCParents_001 const& resoParents)
546+
{
547+
if (!core.passesEventCuts(collision) || !core.passesMCEventCuts(collision)) {
548+
return;
549+
}
550+
for (const auto& part : resoParents) {
551+
if (std::abs(part.pdgCode()) != kOmega2012Minus) {
552+
continue;
553+
}
554+
if (!(std::abs(part.y()) < candidateCuts.cfgRapidityCut.value) || !std::isfinite(part.y())) {
555+
continue;
556+
}
557+
if (cMCInjection && !isInjectedOmegaParent(part)) {
558+
continue;
559+
}
560+
// Require the same immediate Xi + K0S channel as processMCGenerated.
561+
if ((std::abs(part.daughterPDG1()) == kXiMinus && part.daughterPDG2() == kK0Short) ||
562+
(std::abs(part.daughterPDG2()) == kXiMinus && part.daughterPDG1() == kK0Short)) {
563+
histos.fill(HIST("MC/hMCGenSelectedXiK0sPt"), part.pt());
564+
const double sign = part.pdgCode() > 0 ? 1. : -1.;
565+
histos.fill(HIST("MC/hMCGenSelectedXiK0sResponse"), part.pt(), collision.cent(), collision.mcMultiplicity(), sign);
566+
}
567+
}
568+
}
569+
PROCESS_SWITCH(Omega2012Analysis, processMCGen, "Process MC generated XiK0s parents in selected events", false);
570+
571+
// Inclusive MC-particle QA. The rapidity-limited source histogram is a legacy all-event QA denominator;
572+
// selected-event efficiency denominators are filled by processMCGen and processXi1530KMCGen.
474573
void processMCGenerated(aod::McParticles const& mcParticles)
475574
{
476575
// Process MC generated particles (no reconstruction requirement)
@@ -524,7 +623,11 @@ struct Omega2012Analysis {
524623
if ((std::abs(daughter1PDG) == kXiMinus && daughter2PDG == kK0Short) ||
525624
(std::abs(daughter2PDG) == kXiMinus && daughter1PDG == kK0Short)) {
526625
histos.fill(HIST("MC/hMCTruthInvMassXiK0s"), motherM);
527-
histos.fill(HIST("MC/hMCTruthMassPtXiK0s"), motherM, motherPt);
626+
// Legacy all-event rapidity-limited QA histogram; selected-event efficiency
627+
// denominators are written by processMCGen and processXi1530KMCGen.
628+
if (std::abs(y) < candidateCuts.cfgRapidityCut.value) {
629+
histos.fill(HIST("MC/hMCTruthMassPtXiK0s"), motherM, motherPt);
630+
}
528631

529632
const bool isDaughter1Xi = std::abs(daughter1PDG) == kXiMinus;
530633
const auto& pXiTruth = isDaughter1Xi ? p1 : p2;
@@ -538,7 +641,8 @@ struct Omega2012Analysis {
538641

539642
// Mode B: Xi(1530)0 K- -> Xi- pi+ K- (and charge conjugate) from one collision, or Xi and tracks from two mixed collisions
540643
template <bool IsMC, bool IsMix, bool IsResoMicrotrack, typename CollisionT, typename CascadesT, typename TracksT, typename TrackIdsT>
541-
void fillXi1530K(const CollisionT& collision, float cent, const CascadesT& cascades, const TracksT& tracks, const TrackIdsT& trackIds)
644+
void fillXi1530K(const CollisionT& collision, float cent, const CascadesT& cascades, const TracksT& tracks, const TrackIdsT& trackIds,
645+
const OmegaParentTruthMap* omegaParents = nullptr)
542646
{
543647
// Input QA: same event only (each object once per collision)
544648
auto onXi = [&](auto const& xi, bool selected) {
@@ -594,7 +698,7 @@ struct Omega2012Analysis {
594698
};
595699

596700
const bool fillWrongSign = core.fillWrongSign();
597-
auto onCandidate = [&]([[maybe_unused]] auto const& xi, [[maybe_unused]] auto const& pion, [[maybe_unused]] auto const& kaon, Xi1530KCandidateValues const& c) {
701+
auto onCandidate = [&](auto const& xi, auto const& pion, auto const& kaon, Xi1530KCandidateValues const& c) {
598702
if (c.chargePattern != o2::analysis::omega2012ml::kSignalPattern) {
599703
// Charge-pattern controls: same event only, never in the signal histograms
600704
if constexpr (!IsMix) {
@@ -623,13 +727,25 @@ struct Omega2012Analysis {
623727
if (classifyXi1530KTruth(xi, pion, kaon) != Xi1530KTruth::Matched) {
624728
return;
625729
}
730+
if (!omegaParents) {
731+
return;
732+
}
733+
const auto parentIt = omegaParents->find(kaon.motherId());
734+
if (parentIt == omegaParents->end() || (cMCInjection && !parentIt->second.injected)) {
735+
return;
736+
}
737+
if (cUseTruthRapidity && (!std::isfinite(parentIt->second.y) || !(std::abs(parentIt->second.y) < candidateCuts.cfgRapidityCut.value))) {
738+
return;
739+
}
626740
histos.fill(HIST("xi1530K/MC/hMCRecOmega2012Pt"), c.omega.Pt());
627741
histos.fill(HIST("xi1530K/MC/hMCRecOmega2012PtEta"), c.omega.Pt(), c.omega.Eta());
628742
histos.fill(HIST("xi1530K/MC/hMCRecMass"), c.omega.M());
629743
histos.fill(HIST("xi1530K/MC/hMCRecMassXiPi"), c.massXiPi);
630744
histos.fill(HIST("xi1530K/MC/hMCRecXiPt"), xi.pt());
631745
histos.fill(HIST("xi1530K/MC/hMCRecPionPt"), pion.pt());
632746
histos.fill(HIST("xi1530K/MC/hMCRecKaonPt"), kaon.pt());
747+
const double sign = parentIt->second.pdgCode > 0 ? 1. : -1.;
748+
histos.fill(HIST("xi1530K/MC/hMCRecXi1530KResponse"), c.omega.Pt(), parentIt->second.pt, cent, collision.mcMultiplicity(), sign);
633749
}
634750
};
635751

@@ -661,15 +777,40 @@ struct Omega2012Analysis {
661777

662778
void processXi1530KMCMicro(ResoMCCollisions::iterator const& collision,
663779
ResoMCCascades const& resocasc,
664-
ResoMCMicroTracks const& resomicrotracks)
780+
ResoMCMicroTracks const& resomicrotracks,
781+
aod::ResoMCParents_001 const& resoParents)
665782
{
666783
if (!core.passesEventCuts(collision) || !core.passesMCEventCuts(collision)) {
667784
return;
668785
}
669-
fillXi1530K<true, false, true>(collision, collision.cent(), resocasc, resomicrotracks, nullptr);
786+
const auto omegaParents = omegaParentTruth(resoParents);
787+
fillXi1530K<true, false, true>(collision, collision.cent(), resocasc, resomicrotracks, nullptr, &omegaParents);
670788
}
671789
PROCESS_SWITCH(Omega2012Analysis, processXi1530KMCMicro, "Process Xi(1530)0 K mode with truth matching (ResoMicroTracks_001)", false);
672790

791+
// Selected-event generated denominator for Omega -> Xi(1530) K, counted once
792+
// per selected reconstructed collision. Split collisions repeat the MC parents.
793+
void processXi1530KMCGen(ResoMCCollisions::iterator const& collision,
794+
aod::ResoMCParents_001 const& resoParents)
795+
{
796+
if (!core.passesEventCuts(collision) || !core.passesMCEventCuts(collision)) {
797+
return;
798+
}
799+
for (const auto& parent : resoParents) {
800+
if (std::abs(parent.pdgCode()) != kOmega2012Minus ||
801+
!std::isfinite(parent.y()) || !(std::abs(parent.y()) < candidateCuts.cfgRapidityCut.value) ||
802+
(cMCInjection && !isInjectedOmegaParent(parent))) {
803+
continue;
804+
}
805+
if (classifyGeneratedOmega2012(parent.pdgCode(), parent.daughterPDG1(), parent.daughterPDG2()) != GeneratedChannel::Xi1530K) {
806+
continue;
807+
}
808+
const double sign = parent.pdgCode() > 0 ? 1. : -1.;
809+
histos.fill(HIST("xi1530K/MC/hMCGenSelectedXi1530KResponse"), parent.pt(), collision.cent(), collision.mcMultiplicity(), sign);
810+
}
811+
}
812+
PROCESS_SWITCH(Omega2012Analysis, processXi1530KMCGen, "Process generated Xi(1530) K parents in selected events", false);
813+
673814
// Mixed events: Xi from collision 1, pion and kaon from collision 2
674815
void processXi1530KMixedMicro(ResoCollisions const& collisions,
675816
aod::ResoCascades const& resocasc,

0 commit comments

Comments
 (0)