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// /
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
6062using namespace o2 ;
6163using 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