From 72ca60c885f10437cba46c8dca0f8eec8924b1d1 Mon Sep 17 00:00:00 2001 From: creetz16 Date: Mon, 23 Feb 2026 16:10:55 +0100 Subject: [PATCH 1/3] Update MC matching to include MC bkg in MC output table --- .../Nuspex/decay3bodybuilder.cxx | 162 +++++++++++------- 1 file changed, 99 insertions(+), 63 deletions(-) diff --git a/PWGLF/TableProducer/Nuspex/decay3bodybuilder.cxx b/PWGLF/TableProducer/Nuspex/decay3bodybuilder.cxx index cae41a2bd67..2df3254cea5 100644 --- a/PWGLF/TableProducer/Nuspex/decay3bodybuilder.cxx +++ b/PWGLF/TableProducer/Nuspex/decay3bodybuilder.cxx @@ -123,6 +123,9 @@ struct decay3bodyBuilder { Configurable doVertexQA{"doVertexQA", false, "Flag to fill QA histograms for PV of (selected) events."}; Configurable disableITSROFCut{"disableITSROFCut", false, "Disable ITS ROF border cut"}; + // MC processing options + Configurable doStoreMcBkg{"doStoreMcBkg", false, "Flag to store candidates which were not matched to true H3L/Anti-H3L decaying via three-body decay in MC (i.e. MC background) in the output table"}; + // data processing options Configurable doSkimmedProcessing{"doSkimmedProcessing", false, "Apply Zoroo counting in case of skimmed data input"}; Configurable triggerList{"triggerList", "fTriggerEventF1Proton, fTrackedOmega, fTrackedXi, fOmegaLargeRadius, fDoubleOmega, fOmegaHighMult, fSingleXiYN, fQuadrupleXi, fDoubleXi, fhadronOmega, fOmegaXi, fTripleXi, fOmega, fGammaVeryLowPtEMCAL, fGammaVeryLowPtDCAL, fGammaHighPtEMCAL, fGammaLowPtEMCAL, fGammaVeryHighPtDCAL, fGammaVeryHighPtEMCAL, fGammaLowPtDCAL, fJetNeutralLowPt, fJetNeutralHighPt, fGammaHighPtDCAL, fJetFullLowPt, fJetFullHighPt, fEMCALReadout, fPCMandEE, fPHOSnbar, fPCMHighPtPhoton, fPHOSPhoton, fLD, fPPPHI, fPD, fLLL, fPLL, fPPL, fPPP, fLeadingPtTrack, fHighFt0cFv0Flat, fHighFt0cFv0Mult, fHighFt0Flat, fHighFt0Mult, fHighMultFv0, fHighTrackMult, fHfSingleNonPromptCharm3P, fHfSingleNonPromptCharm2P, fHfSingleCharm3P, fHfPhotonCharm3P, fHfHighPt2P, fHfSigmaC0K0, fHfDoubleCharm2P, fHfBeauty3P, fHfFemto3P, fHfFemto2P, fHfHighPt3P, fHfSigmaCPPK, fHfDoubleCharm3P, fHfDoubleCharmMix, fHfPhotonCharm2P, fHfV0Charm2P, fHfBeauty4P, fHfV0Charm3P, fHfSingleCharm2P, fHfCharmBarToXiBach, fSingleMuHigh, fSingleMuLow, fLMeeHMR, fDiMuon, fDiElectron, fLMeeIMR, fSingleE, fTrackHighPt, fTrackLowPt, fJetChHighPt, fJetChLowPt, fUDdiffLarge, fUDdiffSmall, fITSextremeIonisation, fITSmildIonisation, fH3L3Body, fHe, fH2", "List of triggers used to select events"}; @@ -794,47 +797,56 @@ struct decay3bodyBuilder { // check if daughters have MC particle if (!trackProton.has_mcParticle() || !trackPion.has_mcParticle() || !trackDeuteron.has_mcParticle()) { - continue; - } + if (!doStoreMcBkg) { + continue; // if not storing MC background, skip candidates where at least one daughter is not matched to MC particle + } else { + this3BodyMCInfo.label = -5; // at least one non-matched daughter + // fill analysis table (only McVtx3BodyDatas is filled here) + fillAnalysisTables(); + } + } else { // all daughters are matched to MC particles, get their MC info + // get MC daughter particles + auto mcTrackProton = trackProton.template mcParticle_as(); + auto mcTrackPion = trackPion.template mcParticle_as(); + auto mcTrackDeuteron = trackDeuteron.template mcParticle_as(); + + // set daughter MC info (also for non-matched mothers) + this3BodyMCInfo.daughterPrPdgCode = mcTrackProton.pdgCode(); + this3BodyMCInfo.daughterPiPdgCode = mcTrackPion.pdgCode(); + this3BodyMCInfo.daughterDePdgCode = mcTrackDeuteron.pdgCode(); + this3BodyMCInfo.isDeuteronPrimary = mcTrackDeuteron.isPhysicalPrimary(); + this3BodyMCInfo.genMomProton = mcTrackProton.p(); + this3BodyMCInfo.genMomPion = mcTrackPion.p(); + this3BodyMCInfo.genMomDeuteron = mcTrackDeuteron.p(); + this3BodyMCInfo.genPtProton = mcTrackProton.pt(); + this3BodyMCInfo.genPtPion = mcTrackPion.pt(); + this3BodyMCInfo.genPtDeuteron = mcTrackDeuteron.pt(); + + // daughters are matched to MC, now we check if reco mother is true H3L/Anti-H3l and decayed via three-body decay + this3BodyMCInfo.label = checkH3LTruth(mcTrackProton, mcTrackPion, mcTrackDeuteron); // returns global index of mother if true H3L/Anti-H3L mother decaying via three-body decay, otherwise negative value + + // if not storing MC background, skip candidates where mother is not true H3L/Anti-H3L decaying via three-body decay + if (!doStoreMcBkg && this3BodyMCInfo.label <= 0) { + continue; + } - // get MC daughter particles - auto mcTrackProton = trackProton.template mcParticle_as(); - auto mcTrackPion = trackPion.template mcParticle_as(); - auto mcTrackDeuteron = trackDeuteron.template mcParticle_as(); - - // set daughter MC info (also for non-matched candidates) - this3BodyMCInfo.daughterPrPdgCode = mcTrackProton.pdgCode(); - this3BodyMCInfo.daughterPiPdgCode = mcTrackPion.pdgCode(); - this3BodyMCInfo.daughterDePdgCode = mcTrackDeuteron.pdgCode(); - this3BodyMCInfo.isDeuteronPrimary = mcTrackDeuteron.isPhysicalPrimary(); - this3BodyMCInfo.genMomProton = mcTrackProton.p(); - this3BodyMCInfo.genMomPion = mcTrackPion.p(); - this3BodyMCInfo.genMomDeuteron = mcTrackDeuteron.p(); - this3BodyMCInfo.genPtProton = mcTrackProton.pt(); - this3BodyMCInfo.genPtPion = mcTrackPion.pt(); - this3BodyMCInfo.genPtDeuteron = mcTrackDeuteron.pt(); - - // check if reco mother is true H3L/Anti-H3l - bool isMuonReco; - int motherID = checkH3LTruth(mcTrackProton, mcTrackPion, mcTrackDeuteron, isMuonReco); - - // get generated mother MC info - if (motherID > 0) { - auto mcTrackH3L = mcParticles.rawIteratorAt(motherID); - this3BodyMCInfo.motherPdgCode = mcTrackH3L.pdgCode(); - this3BodyMCInfo.label = motherID; - this3BodyMCInfo.genMomentum = {mcTrackH3L.px(), mcTrackH3L.py(), mcTrackH3L.pz()}; - this3BodyMCInfo.genDecVtx = {mcTrackProton.vx(), mcTrackProton.vy(), mcTrackProton.vz()}; - this3BodyMCInfo.genCt = RecoDecay::sqrtSumOfSquares(mcTrackProton.vx() - mcTrackH3L.vx(), mcTrackProton.vy() - mcTrackH3L.vy(), mcTrackProton.vz() - mcTrackH3L.vz()) * o2::constants::physics::MassHyperTriton / mcTrackH3L.p(); - this3BodyMCInfo.genPhi = mcTrackH3L.phi(); - this3BodyMCInfo.genEta = mcTrackH3L.eta(); - this3BodyMCInfo.genRapidity = mcTrackH3L.y(); - this3BodyMCInfo.isTrueH3L = this3BodyMCInfo.motherPdgCode > 0 ? true : false; - this3BodyMCInfo.isTrueAntiH3L = this3BodyMCInfo.motherPdgCode < 0 ? true : false; - } + // get generated mother MC info for matched candidates + if (this3BodyMCInfo.label > -1) { + auto mcTrackH3L = mcParticles.rawIteratorAt(this3BodyMCInfo.label); + this3BodyMCInfo.motherPdgCode = mcTrackH3L.pdgCode(); + this3BodyMCInfo.genMomentum = {mcTrackH3L.px(), mcTrackH3L.py(), mcTrackH3L.pz()}; + this3BodyMCInfo.genDecVtx = {mcTrackProton.vx(), mcTrackProton.vy(), mcTrackProton.vz()}; + this3BodyMCInfo.genCt = RecoDecay::sqrtSumOfSquares(mcTrackProton.vx() - mcTrackH3L.vx(), mcTrackProton.vy() - mcTrackH3L.vy(), mcTrackProton.vz() - mcTrackH3L.vz()) * o2::constants::physics::MassHyperTriton / mcTrackH3L.p(); + this3BodyMCInfo.genPhi = mcTrackH3L.phi(); + this3BodyMCInfo.genEta = mcTrackH3L.eta(); + this3BodyMCInfo.genRapidity = mcTrackH3L.y(); + this3BodyMCInfo.isTrueH3L = this3BodyMCInfo.motherPdgCode > 0 ? true : false; + this3BodyMCInfo.isTrueAntiH3L = this3BodyMCInfo.motherPdgCode < 0 ? true : false; + } - // fill analysis tables (only McVtx3BodyDatas is filled here) - fillAnalysisTables(); + // fill analysis tables (only McVtx3BodyDatas is filled here) + fillAnalysisTables(); + } // end of check if daughters have MC particle // mark mcParticle as reconstructed if (this3BodyMCInfo.label > -1) { @@ -1179,30 +1191,23 @@ struct decay3bodyBuilder { // ______________________________________________________________ // function to check if a reconstructed mother is a true H3L/Anti-H3L (returns -1 if not) template - int checkH3LTruth(MCTrack3B const& mcParticlePr, MCTrack3B const& mcParticlePi, MCTrack3B const& mcParticleDe, bool& isMuonReco) + int checkH3LTruth(MCTrack3B const& mcParticlePr, MCTrack3B const& mcParticlePi, MCTrack3B const& mcParticleDe) { - if (std::abs(mcParticlePr.pdgCode()) != PDG_t::kProton || std::abs(mcParticleDe.pdgCode()) != o2::constants::physics::Pdg::kDeuteron) { - return -1; - } - // check proton and deuteron mother - int prDeMomID = -1; - for (const auto& motherPr : mcParticlePr.template mothers_as()) { - for (const auto& motherDe : mcParticleDe.template mothers_as()) { - if (motherPr.globalIndex() == motherDe.globalIndex() && std::abs(motherPr.pdgCode()) == o2::constants::physics::Pdg::kHyperTriton) { - prDeMomID = motherPr.globalIndex(); - break; - } - } - } - if (prDeMomID == -1) { - return -1; - } - if (std::abs(mcParticlePi.pdgCode()) != PDG_t::kPiPlus && std::abs(mcParticlePi.pdgCode()) != PDG_t::kMuonMinus) { - return -1; + // return legend + // -4: proton, pion, or deuteron have wrong identity + // -3: proton and pion have a common mother which is a Lambda (i.e., not a direct daughter of hypertriton) + // -2: proton, pion, and deuteron don't have a common mother + // -1: proton, pion, and deuteron have common mother but it's NOT a hypertriton + // global mother ID: proton, pion, and deuteron have common mother and it's a hypertriton + + // first, check identity of MC daughters + if (std::abs(mcParticlePr.pdgCode()) != PDG_t::kProton || std::abs(mcParticleDe.pdgCode()) != o2::constants::physics::Pdg::kDeuteron || (std::abs(mcParticlePi.pdgCode()) != PDG_t::kPiPlus && std::abs(mcParticlePi.pdgCode()) != PDG_t::kMuonMinus)) { + return -4; } // check if the pion track is a muon coming from a pi -> mu + vu decay, if yes, take the mother pi auto mcParticlePiTmp = mcParticlePi; if (std::abs(mcParticlePiTmp.pdgCode()) == PDG_t::kMuonMinus) { + bool isMuonReco = false; for (const auto& motherPi : mcParticlePiTmp.template mothers_as()) { if (std::abs(motherPi.pdgCode()) == PDG_t::kPiPlus) { mcParticlePiTmp = motherPi; @@ -1210,14 +1215,45 @@ struct decay3bodyBuilder { break; } } + // If the track is a muon but none of its mothers is a pi+, treat as wrong identity + if (!isMuonReco) { + return -4; + } } - // now loop over the pion mother - for (const auto& motherPi : mcParticlePiTmp.template mothers_as()) { - if (motherPi.globalIndex() == prDeMomID) { - return motherPi.globalIndex(); + + // now first check if the proton and pion have the same mother and it is a Lambda + for (const auto& motherPr : mcParticlePr.template mothers_as()) { + for (const auto& motherPi : mcParticlePiTmp.template mothers_as()) { + if (motherPr.globalIndex() == motherPi.globalIndex() && std::abs(motherPr.pdgCode()) == PDG_t::kLambda) { + return -3; + } } } - return -1; + + // now check if all three daughters have the same mother + int momID = -1; + int momPdgCode = 0; + for (const auto& motherPr : mcParticlePr.template mothers_as()) { + for (const auto& motherDe : mcParticleDe.template mothers_as()) { + for (const auto& motherPi : mcParticlePiTmp.template mothers_as()) { + if (motherPr.globalIndex() == motherDe.globalIndex() && motherPr.globalIndex() == motherPi.globalIndex()) { + momID = motherPr.globalIndex(); + momPdgCode = motherPr.pdgCode(); + break; + } + } + } + } + if (momID == -1) { + return -2; + } + + // check if the common mother is a hypertriton + if (std::abs(momPdgCode) == o2::constants::physics::Pdg::kHyperTriton) { + return momID; + } else { + return -1; // common mother found but not a hypertriton + } } // ______________________________________________________________ From fdc37bbb5e714906b9c7ecf4b547fb7c166badb4 Mon Sep 17 00:00:00 2001 From: creetz16 Date: Tue, 24 Feb 2026 14:28:22 +0100 Subject: [PATCH 2/3] Formatting --- PWGLF/TableProducer/Nuspex/decay3bodybuilder.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PWGLF/TableProducer/Nuspex/decay3bodybuilder.cxx b/PWGLF/TableProducer/Nuspex/decay3bodybuilder.cxx index 2df3254cea5..583e9ae5c43 100644 --- a/PWGLF/TableProducer/Nuspex/decay3bodybuilder.cxx +++ b/PWGLF/TableProducer/Nuspex/decay3bodybuilder.cxx @@ -1229,7 +1229,7 @@ struct decay3bodyBuilder { } } } - + // now check if all three daughters have the same mother int momID = -1; int momPdgCode = 0; From b7ea28927ce9d26d5e37fdef78ac92963d08f593 Mon Sep 17 00:00:00 2001 From: Francesco Mazzaschi Date: Tue, 24 Feb 2026 15:37:10 +0100 Subject: [PATCH 3/3] fix lambda pdg --- PWGLF/TableProducer/Nuspex/decay3bodybuilder.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PWGLF/TableProducer/Nuspex/decay3bodybuilder.cxx b/PWGLF/TableProducer/Nuspex/decay3bodybuilder.cxx index 2df3254cea5..599aed4bddf 100644 --- a/PWGLF/TableProducer/Nuspex/decay3bodybuilder.cxx +++ b/PWGLF/TableProducer/Nuspex/decay3bodybuilder.cxx @@ -1224,7 +1224,7 @@ struct decay3bodyBuilder { // now first check if the proton and pion have the same mother and it is a Lambda for (const auto& motherPr : mcParticlePr.template mothers_as()) { for (const auto& motherPi : mcParticlePiTmp.template mothers_as()) { - if (motherPr.globalIndex() == motherPi.globalIndex() && std::abs(motherPr.pdgCode()) == PDG_t::kLambda) { + if (motherPr.globalIndex() == motherPi.globalIndex() && std::abs(motherPr.pdgCode()) == PDG_t::kLambda0) { return -3; } }