From a8c9038ca1483437faf121ddfb0f794855d62d78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CRuiqi?= <23210190047@m.fudan.edu.cn> Date: Thu, 22 Oct 2026 23:03:21 +0200 Subject: [PATCH 1/2] [PWGHF] Split kaon DCA secondary origins into HF and LF --- PWGCF/DataModel/FemtoDerived.h | 6 +- .../FemtoDream/Core/femtoDreamParticleHisto.h | 28 ++++++++ PWGCF/FemtoDream/Core/femtoDreamUtils.h | 63 +++++++++++++++++ .../producerCharmHadronsTrackFemtoDream.cxx | 67 ++++++++++--------- 4 files changed, 132 insertions(+), 32 deletions(-) diff --git a/PWGCF/DataModel/FemtoDerived.h b/PWGCF/DataModel/FemtoDerived.h index 0b779485244..cac62250bbe 100644 --- a/PWGCF/DataModel/FemtoDerived.h +++ b/PWGCF/DataModel/FemtoDerived.h @@ -743,6 +743,8 @@ enum ParticleOriginMCTruth { kSecondaryDaughterXistar0, //! Daughter from a Xi*^0 decay kSecondaryDaughterXistarMinus, //! Daughter from a Xi*^- decay kElse, //! none of the above; (NOTE: used to catch bugs. will be removed once MC usage is properly validated) + kSecondaryFromHF, //! Kaon from a charm- or beauty-hadron decay chain + kSecondaryFromLF, //! Kaon from a light-flavour decay without a heavy-flavour ancestor kNOriginMCTruthTypes }; @@ -762,7 +764,9 @@ static constexpr std::string_view ParticleOriginMCTruthName[kNOriginMCTruthTypes "_SecondaryDaughterOmegaMinus", "_SecondaryDaughterXistar0", "_SecondaryDaughterXistarMinus", - "_Else"}; + "_Else", + "_SecondaryFromHF", + "_SecondaryFromLF"}; /// Distinguished between reconstructed and truth enum MCType { diff --git a/PWGCF/FemtoDream/Core/femtoDreamParticleHisto.h b/PWGCF/FemtoDream/Core/femtoDreamParticleHisto.h index 096b9bb7b28..410e79774a5 100644 --- a/PWGCF/FemtoDream/Core/femtoDreamParticleHisto.h +++ b/PWGCF/FemtoDream/Core/femtoDreamParticleHisto.h @@ -233,6 +233,12 @@ class FemtoDreamParticleHisto mHistogramRegistry->add((folderName + folderSuffix + "/Debug/hPDGmother_SecondaryDaughterLambda").c_str(), "; PDG mother; Entries", o2::framework::HistType::kTH1I, {{12001, -6000.5, 6000.5}}); mHistogramRegistry->add((folderName + folderSuffix + "/Debug/hPDGmother_SecondaryDaughterSigmaplus").c_str(), "; PDG mother; Entries", o2::framework::HistType::kTH1I, {{12001, -6000.5, 6000.5}}); mHistogramRegistry->add((folderName + folderSuffix + "/Debug/hPDGmother_SecondaryDaughterOmegaMinus").c_str(), "; PDG mother; Entries", o2::framework::HistType::kTH1I, {{12001, -6000.5, 6000.5}}); + if (std::abs(mPDG) == kKPlus) { + mHistogramRegistry->add((folderName + folderSuffix + "/Debug/hPDGmother_SecondaryFromHF").c_str(), "; PDG mother; Entries", o2::framework::HistType::kTH1I, {{12001, -6000.5, 6000.5}}); + mHistogramRegistry->add((folderName + folderSuffix + "/Debug/hPDGmother_SecondaryFromLF").c_str(), "; PDG mother; Entries", o2::framework::HistType::kTH1I, {{12001, -6000.5, 6000.5}}); + mHistogramRegistry->add((folderName + folderSuffix + "/hDCAxy_SecondaryFromHF").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", o2::framework::HistType::kTHnSparseF, {tempFitVarpTAxis, tempFitVarAxis, dcazAxis, multAxis}); + mHistogramRegistry->add((folderName + folderSuffix + "/hDCAxy_SecondaryFromLF").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", o2::framework::HistType::kTHnSparseF, {tempFitVarpTAxis, tempFitVarAxis, dcazAxis, multAxis}); + } mHistogramRegistry->add((folderName + folderSuffix + "/hDCAxy_Primary").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", o2::framework::HistType::kTHnSparseF, {tempFitVarpTAxis, tempFitVarAxis, dcazAxis, multAxis}); mHistogramRegistry->add((folderName + folderSuffix + "/hDCAxy_Secondary").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", o2::framework::HistType::kTHnSparseF, {tempFitVarpTAxis, tempFitVarAxis, dcazAxis, multAxis}); @@ -245,6 +251,10 @@ class FemtoDreamParticleHisto mHistogramRegistry->add((folderName + folderSuffix + "/hDCAxy_Else").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", o2::framework::HistType::kTHnSparseF, {tempFitVarpTAxis, tempFitVarAxis, dcazAxis, multAxis}); } else { mHistogramRegistry->add((folderName + folderSuffix + "/hDCAxy_Primary").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", o2::framework::HistType::kTH2F, {tempFitVarpTAxis, tempFitVarAxis}); + if (std::abs(mPDG) == kKPlus) { + mHistogramRegistry->add((folderName + folderSuffix + "/hDCAxy_SecondaryFromHF").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", o2::framework::HistType::kTH2F, {tempFitVarpTAxis, tempFitVarAxis}); + mHistogramRegistry->add((folderName + folderSuffix + "/hDCAxy_SecondaryFromLF").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", o2::framework::HistType::kTH2F, {tempFitVarpTAxis, tempFitVarAxis}); + } mHistogramRegistry->add((folderName + folderSuffix + "/hDCAxy_Secondary").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", o2::framework::HistType::kTH2F, {tempFitVarpTAxis, tempFitVarAxis}); mHistogramRegistry->add((folderName + folderSuffix + "/hDCAxy_Material").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", o2::framework::HistType::kTH2F, {tempFitVarpTAxis, tempFitVarAxis}); mHistogramRegistry->add((folderName + folderSuffix + "/hDCAxy_WrongCollision").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", o2::framework::HistType::kTH2F, {tempFitVarpTAxis, tempFitVarAxis}); @@ -597,6 +607,16 @@ class FemtoDreamParticleHisto mHistogramRegistry->fill(HIST(o2::aod::femtodreamparticle::ParticleTypeName[mParticleType]) + HIST(mFolderSuffix[mFolderSuffixType]) + HIST("_MC/hDCAxy_Secondary"), part.pt(), part.tempFitVar(), part.dcaZ(), mult); break; + case (o2::aod::femtodreamMCparticle::kSecondaryFromHF): + mHistogramRegistry->fill(HIST(o2::aod::femtodreamparticle::ParticleTypeName[mParticleType]) + HIST(mFolderSuffix[mFolderSuffixType]) + HIST("_MC/Debug/hPDGmother_SecondaryFromHF"), part.fdExtMCParticle().motherPDG()); + mHistogramRegistry->fill(HIST(o2::aod::femtodreamparticle::ParticleTypeName[mParticleType]) + HIST(mFolderSuffix[mFolderSuffixType]) + HIST("_MC/hDCAxy_SecondaryFromHF"), + part.pt(), part.tempFitVar(), part.dcaZ(), mult); + break; + case (o2::aod::femtodreamMCparticle::kSecondaryFromLF): + mHistogramRegistry->fill(HIST(o2::aod::femtodreamparticle::ParticleTypeName[mParticleType]) + HIST(mFolderSuffix[mFolderSuffixType]) + HIST("_MC/Debug/hPDGmother_SecondaryFromLF"), part.fdExtMCParticle().motherPDG()); + mHistogramRegistry->fill(HIST(o2::aod::femtodreamparticle::ParticleTypeName[mParticleType]) + HIST(mFolderSuffix[mFolderSuffixType]) + HIST("_MC/hDCAxy_SecondaryFromLF"), + part.pt(), part.tempFitVar(), part.dcaZ(), mult); + break; case (o2::aod::femtodreamMCparticle::kMaterial): mHistogramRegistry->fill(HIST(o2::aod::femtodreamparticle::ParticleTypeName[mParticleType]) + HIST(mFolderSuffix[mFolderSuffixType]) + HIST("_MC/Debug/hPDGmother_Material"), part.fdExtMCParticle().motherPDG()); mHistogramRegistry->fill(HIST(o2::aod::femtodreamparticle::ParticleTypeName[mParticleType]) + HIST(mFolderSuffix[mFolderSuffixType]) + HIST("_MC/hDCAxy_Material"), @@ -645,6 +665,14 @@ class FemtoDreamParticleHisto mHistogramRegistry->fill(HIST(o2::aod::femtodreamparticle::ParticleTypeName[mParticleType]) + HIST(mFolderSuffix[mFolderSuffixType]) + HIST("_MC/hDCAxy_Secondary"), part.pt(), part.tempFitVar()); break; + case (o2::aod::femtodreamMCparticle::kSecondaryFromHF): + mHistogramRegistry->fill(HIST(o2::aod::femtodreamparticle::ParticleTypeName[mParticleType]) + HIST(mFolderSuffix[mFolderSuffixType]) + HIST("_MC/hDCAxy_SecondaryFromHF"), + part.pt(), part.tempFitVar()); + break; + case (o2::aod::femtodreamMCparticle::kSecondaryFromLF): + mHistogramRegistry->fill(HIST(o2::aod::femtodreamparticle::ParticleTypeName[mParticleType]) + HIST(mFolderSuffix[mFolderSuffixType]) + HIST("_MC/hDCAxy_SecondaryFromLF"), + part.pt(), part.tempFitVar()); + break; case (o2::aod::femtodreamMCparticle::kMaterial): mHistogramRegistry->fill(HIST(o2::aod::femtodreamparticle::ParticleTypeName[mParticleType]) + HIST(mFolderSuffix[mFolderSuffixType]) + HIST("_MC/hDCAxy_Material"), part.pt(), part.tempFitVar()); diff --git a/PWGCF/FemtoDream/Core/femtoDreamUtils.h b/PWGCF/FemtoDream/Core/femtoDreamUtils.h index fde124f5db4..ca74da93ceb 100644 --- a/PWGCF/FemtoDream/Core/femtoDreamUtils.h +++ b/PWGCF/FemtoDream/Core/femtoDreamUtils.h @@ -18,9 +18,12 @@ #include "PWGCF/DataModel/FemtoDerived.h" +#include "Common/Core/RecoDecay.h" + #include #include +#include #include #include @@ -102,6 +105,66 @@ inline float getMass(int pdgCode) return mass; } +/// Identify conventional charm/beauty mesons and baryons from their PDG quark digits. +inline bool isHeavyFlavorHadron(int pdgCode) +{ + constexpr int DigitBase = 10; + constexpr int HadronLimit = 10000000; + const auto code = std::abs(pdgCode); + if (code >= HadronLimit || code % DigitBase == 0) { + return false; + } + const auto quark1 = (code / RecoDecay::PdgDivisorBaryon) % DigitBase; + const auto quark2 = (code / RecoDecay::PdgDivisorMeson) % DigitBase; + const auto quark3 = (code / DigitBase) % DigitBase; + // Quarks, diquarks and generator-specific entries are not hadron decays. + if (quark1 > kTop || quark2 < kDown || quark2 > kTop || quark3 < kDown || quark3 > kTop) { + return false; + } + return quark1 == kCharm || quark1 == kBottom || quark2 == kCharm || quark2 == kBottom || quark3 == kCharm || quark3 == kBottom; +} + +template +inline bool hasHeavyFlavorHadronAncestor(T const& particles, typename T::iterator const& particle) +{ + auto ancestor = particle; + std::vector motherIds; + while (ancestor.has_mothers()) { + motherIds.clear(); + RecoDecay::getOrderedMotherIds(ancestor, motherIds); + if (motherIds.empty()) { + break; + } + ancestor = particles.rawIteratorAt(motherIds.front() - particles.offset()); + if (isHeavyFlavorHadron(ancestor.pdgCode())) { + return true; + } + } + return false; +} + +/// Classify true kaons from the correct collision for the DCA templates. +template +inline int getKaonOrigin(T const& particles, typename T::iterator const& particle) +{ + using namespace o2::aod::femtodreamMCparticle; + constexpr int GenFromTransport = -1; + if (particle.getProcess() == TMCProcess::kPHInhelastic && particle.getGenStatusCode() == GenFromTransport) { + return kMaterial; + } + // HF decay products can be physical primaries. Check their ancestry first. + if (hasHeavyFlavorHadronAncestor(particles, particle)) { + return kSecondaryFromHF; + } + if (particle.isPhysicalPrimary()) { + return kPrimary; + } + if (particle.getProcess() == TMCProcess::kPDecay && particle.getGenStatusCode() == GenFromTransport && particle.has_mothers()) { + return kSecondaryFromLF; + } + return kElse; +} + inline int checkDaughterType(o2::aod::femtodreamparticle::ParticleType partType, int motherPDG, int daughterPDG = 0) { int partOrigin = 0; diff --git a/PWGHF/HFC/TableProducer/producerCharmHadronsTrackFemtoDream.cxx b/PWGHF/HFC/TableProducer/producerCharmHadronsTrackFemtoDream.cxx index dc7eeb02b03..74d01a83f01 100644 --- a/PWGHF/HFC/TableProducer/producerCharmHadronsTrackFemtoDream.cxx +++ b/PWGHF/HFC/TableProducer/producerCharmHadronsTrackFemtoDream.cxx @@ -466,7 +466,7 @@ struct HfProducerCharmHadronsTrackFemtoDream { } template - void fillMcParticle(CollisionType const& col, ParticleType const& particle, o2::aod::femtodreamparticle::ParticleType fdparttype) + void fillMcParticle(CollisionType const& col, ParticleType const& particle, o2::aod::femtodreamparticle::ParticleType fdparttype, aod::McParticles const& mcParticles) { if (particle.has_mcParticle()) { // get corresponding MC particle and its info @@ -484,6 +484,11 @@ struct HfProducerCharmHadronsTrackFemtoDream { // check if the collision associated with the particle is the same as the analyzed collision by checking their Ids if ((col.has_mcCollision() && (particleMc.mcCollisionId() != col.mcCollisionId())) || !col.has_mcCollision()) { particleOrigin = aod::femtodreamMCparticle::ParticleOriginMCTruth::kWrongCollision; + } else if (std::abs(pdgCode) == kKPlus) { + particleOrigin = getKaonOrigin(mcParticles, particleMc); + if (!motherparticlesMc.empty()) { + pdgCodeMother = motherparticlesMc.front().pdgCode(); + } // check if particle is primary } else if (particleMc.isPhysicalPrimary()) { particleOrigin = aod::femtodreamMCparticle::ParticleOriginMCTruth::kPrimary; @@ -537,7 +542,7 @@ struct HfProducerCharmHadronsTrackFemtoDream { } template - bool fillTracksForCharmHadron(CollisionType const& col, TrackType const& tracks) + bool fillTracksForCharmHadron(CollisionType const& col, TrackType const& tracks, aod::McParticles const* mcParticles = nullptr) { std::vector const childIDs = {0, 0}; // these IDs are necessary to keep track of the children @@ -590,14 +595,14 @@ struct HfProducerCharmHadronsTrackFemtoDream { } if constexpr (IsMc) { - fillMcParticle(col, track, o2::aod::femtodreamparticle::ParticleType::kTrack); + fillMcParticle(col, track, o2::aod::femtodreamparticle::ParticleType::kTrack, *mcParticles); } } return fIsTrackFilled; } template - void fillCharmHadronTable(CollisionType const& col, TrackType const& tracks, CandType const& candidates) + void fillCharmHadronTable(CollisionType const& col, TrackType const& tracks, CandType const& candidates, aod::McParticles const* mcParticles = nullptr) { const auto vtxZ = col.posZ(); const auto sizeCand = candidates.size(); @@ -884,7 +889,7 @@ struct HfProducerCharmHadronsTrackFemtoDream { fillTable(2, candidate.isSelDstarToD0Pi(), outputMlDstar.at(0), outputMlDstar.at(1), outputMlDstar.at(2)); } } - isTrackFilled = fillTracksForCharmHadron(col, tracks); + isTrackFilled = fillTracksForCharmHadron(col, tracks, mcParticles); aod::femtodreamcollision::BitMaskType bitTrack = 0; if (isTrackFilled) { @@ -908,7 +913,7 @@ struct HfProducerCharmHadronsTrackFemtoDream { } template - void fillXicHadronTable(CollisionType const& col, TrackType const& tracks, CandType const& candidates, DaughterTrackType const&) + void fillXicHadronTable(CollisionType const& col, TrackType const& tracks, CandType const& candidates, DaughterTrackType const&, aod::McParticles const* mcParticles = nullptr) { const auto vtxZ = col.posZ(); const auto sizeCand = candidates.size(); @@ -1030,7 +1035,7 @@ struct HfProducerCharmHadronsTrackFemtoDream { } } - isTrackFilled = fillTracksForCharmHadron(col, tracks); + isTrackFilled = fillTracksForCharmHadron(col, tracks, mcParticles); aod::femtodreamcollision::BitMaskType bitTrack = 0; if (isTrackFilled) { @@ -1153,25 +1158,25 @@ struct HfProducerCharmHadronsTrackFemtoDream { void processMcD0ToPiK(FemtoFullCollisionMc const& col, aod::BCsWithTimestamps const&, FemtoHFMcTracks const& tracks, - aod::McParticles const&, + aod::McParticles const& mcParticles, CandidateD0Mc const& candidates) { // get magnetic field for run getMagneticFieldTesla(col.bc_as()); - fillCharmHadronTable(col, tracks, candidates); + fillCharmHadronTable(col, tracks, candidates, &mcParticles); } PROCESS_SWITCH(HfProducerCharmHadronsTrackFemtoDream, processMcD0ToPiK, "Provide Mc for D0ToPiK", false); void processMcD0ToPiKWithML(FemtoFullCollisionMc const& col, aod::BCsWithTimestamps const&, FemtoHFMcTracks const& tracks, - aod::McParticles const&, + aod::McParticles const& mcParticles, soa::Join const& candidates) { // get magnetic field for run getMagneticFieldTesla(col.bc_as()); - fillCharmHadronTable(col, tracks, candidates); + fillCharmHadronTable(col, tracks, candidates, &mcParticles); } PROCESS_SWITCH(HfProducerCharmHadronsTrackFemtoDream, processMcD0ToPiKWithML, "Provide Mc for D0ToPiK with ml", false); @@ -1208,25 +1213,25 @@ struct HfProducerCharmHadronsTrackFemtoDream { void processMcDstarToD0Pi(FemtoFullCollisionMc const& col, aod::BCsWithTimestamps const&, FemtoHFMcTracks const& tracks, - aod::McParticles const&, + aod::McParticles const& mcParticles, CandidateDstarMc const& candidates) { // get magnetic field for run getMagneticFieldTesla(col.bc_as()); - fillCharmHadronTable(col, tracks, candidates); + fillCharmHadronTable(col, tracks, candidates, &mcParticles); } PROCESS_SWITCH(HfProducerCharmHadronsTrackFemtoDream, processMcDstarToD0Pi, "Provide Mc for DstarToD0Pi", false); void processMcDstarToD0PiWithML(FemtoFullCollisionMc const& col, aod::BCsWithTimestamps const&, FemtoHFMcTracks const& tracks, - aod::McParticles const&, + aod::McParticles const& mcParticles, soa::Join const& candidates) { // get magnetic field for run getMagneticFieldTesla(col.bc_as()); - fillCharmHadronTable(col, tracks, candidates); + fillCharmHadronTable(col, tracks, candidates, &mcParticles); } PROCESS_SWITCH(HfProducerCharmHadronsTrackFemtoDream, processMcDstarToD0PiWithML, "Provide Mc for DstarToD0Pi with ml", false); @@ -1265,25 +1270,25 @@ struct HfProducerCharmHadronsTrackFemtoDream { void processMcDplusToPiKPi(FemtoFullCollisionMc const& col, aod::BCsWithTimestamps const&, FemtoHFMcTracks const& tracks, - aod::McParticles const&, + aod::McParticles const& mcParticles, CandidateDplusMc const& candidates) { // get magnetic field for run getMagneticFieldTesla(col.bc_as()); - fillCharmHadronTable(col, tracks, candidates); + fillCharmHadronTable(col, tracks, candidates, &mcParticles); } PROCESS_SWITCH(HfProducerCharmHadronsTrackFemtoDream, processMcDplusToPiKPi, "Provide Mc for DplusToPiKPi", false); void processMcDplusToPiKPiWithML(FemtoFullCollisionMc const& col, aod::BCsWithTimestamps const&, FemtoHFMcTracks const& tracks, - aod::McParticles const&, + aod::McParticles const& mcParticles, soa::Join const& candidates) { // get magnetic field for run getMagneticFieldTesla(col.bc_as()); - fillCharmHadronTable(col, tracks, candidates); + fillCharmHadronTable(col, tracks, candidates, &mcParticles); } PROCESS_SWITCH(HfProducerCharmHadronsTrackFemtoDream, processMcDplusToPiKPiWithML, "Provide Mc for DplusToPiKPi with ml", false); @@ -1322,25 +1327,25 @@ struct HfProducerCharmHadronsTrackFemtoDream { void processMcLcToPKPi(FemtoFullCollisionMc const& col, aod::BCsWithTimestamps const&, FemtoHFMcTracks const& tracks, - aod::McParticles const&, + aod::McParticles const& mcParticles, CandidateLcMc const& candidates) { // get magnetic field for run getMagneticFieldTesla(col.bc_as()); - fillCharmHadronTable(col, tracks, candidates); + fillCharmHadronTable(col, tracks, candidates, &mcParticles); } PROCESS_SWITCH(HfProducerCharmHadronsTrackFemtoDream, processMcLcToPKPi, "Provide Mc for lctopkpi", false); void processMcLcToPKPiWithML(FemtoFullCollisionMc const& col, aod::BCsWithTimestamps const&, FemtoHFMcTracks const& tracks, - aod::McParticles const&, + aod::McParticles const& mcParticles, soa::Join const& candidates) { // get magnetic field for run getMagneticFieldTesla(col.bc_as()); - fillCharmHadronTable(col, tracks, candidates); + fillCharmHadronTable(col, tracks, candidates, &mcParticles); } PROCESS_SWITCH(HfProducerCharmHadronsTrackFemtoDream, processMcLcToPKPiWithML, "Provide Mc for lctopkpi with ml", false); @@ -1400,11 +1405,11 @@ struct HfProducerCharmHadronsTrackFemtoDream { aod::BCsWithTimestamps const&, FemtoHFMcTracks const& tracks, aod::FullTracks const& daughterTracks, - aod::McParticles const&, + aod::McParticles const& mcParticles, soa::Filtered const& candidates) { getMagneticFieldTesla(col.bc_as()); - fillXicHadronTable(col, tracks, candidates, daughterTracks); + fillXicHadronTable(col, tracks, candidates, daughterTracks, &mcParticles); } PROCESS_SWITCH(HfProducerCharmHadronsTrackFemtoDream, processMcXicToXiPiPi, "MC for XicToXiPiPi (DCAFitter)", false); @@ -1412,11 +1417,11 @@ struct HfProducerCharmHadronsTrackFemtoDream { aod::BCsWithTimestamps const&, FemtoHFMcTracks const& tracks, aod::FullTracks const& daughterTracks, - aod::McParticles const&, + aod::McParticles const& mcParticles, soa::Filtered const& candidates) { getMagneticFieldTesla(col.bc_as()); - fillXicHadronTable(col, tracks, candidates, daughterTracks); + fillXicHadronTable(col, tracks, candidates, daughterTracks, &mcParticles); } PROCESS_SWITCH(HfProducerCharmHadronsTrackFemtoDream, processMcXicToXiPiPiKf, "MC for XicToXiPiPi (KFParticle)", false); @@ -1424,11 +1429,11 @@ struct HfProducerCharmHadronsTrackFemtoDream { aod::BCsWithTimestamps const&, FemtoHFMcTracks const& tracks, aod::FullTracks const& daughterTracks, - aod::McParticles const&, + aod::McParticles const& mcParticles, soa::Filtered> const& candidates) { getMagneticFieldTesla(col.bc_as()); - fillXicHadronTable(col, tracks, candidates, daughterTracks); + fillXicHadronTable(col, tracks, candidates, daughterTracks, &mcParticles); } PROCESS_SWITCH(HfProducerCharmHadronsTrackFemtoDream, processMcXicToXiPiPiWithML, "MC for XicToXiPiPi with ML (DCAFitter)", false); @@ -1436,11 +1441,11 @@ struct HfProducerCharmHadronsTrackFemtoDream { aod::BCsWithTimestamps const&, FemtoHFMcTracks const& tracks, aod::FullTracks const& daughterTracks, - aod::McParticles const&, + aod::McParticles const& mcParticles, soa::Filtered> const& candidates) { getMagneticFieldTesla(col.bc_as()); - fillXicHadronTable(col, tracks, candidates, daughterTracks); + fillXicHadronTable(col, tracks, candidates, daughterTracks, &mcParticles); } PROCESS_SWITCH(HfProducerCharmHadronsTrackFemtoDream, processMcXicToXiPiPiWithMLKf, "MC for XicToXiPiPi with ML (KFParticle)", false); From 240d1d3833a672020bfba660c1e0aa06ca81444b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CRuiqi?= <23210190047@m.fudan.edu.cn> Date: Tue, 27 Oct 2026 16:23:21 +0100 Subject: [PATCH 2/2] [PWGHF] Use RecoDecay for kaon origin classification Follow RecoDecay::getParticleOrigin with its default arguments and classify nonzero origins as HF, as suggested by Fabrizio. Remove the custom PDG and ancestry helpers without changing the remaining origin routing. Co-authored-by: Fabrizio Grosa --- PWGCF/FemtoDream/Core/femtoDreamUtils.h | 40 +------------------------ 1 file changed, 1 insertion(+), 39 deletions(-) diff --git a/PWGCF/FemtoDream/Core/femtoDreamUtils.h b/PWGCF/FemtoDream/Core/femtoDreamUtils.h index ca74da93ceb..16a7314c380 100644 --- a/PWGCF/FemtoDream/Core/femtoDreamUtils.h +++ b/PWGCF/FemtoDream/Core/femtoDreamUtils.h @@ -105,44 +105,6 @@ inline float getMass(int pdgCode) return mass; } -/// Identify conventional charm/beauty mesons and baryons from their PDG quark digits. -inline bool isHeavyFlavorHadron(int pdgCode) -{ - constexpr int DigitBase = 10; - constexpr int HadronLimit = 10000000; - const auto code = std::abs(pdgCode); - if (code >= HadronLimit || code % DigitBase == 0) { - return false; - } - const auto quark1 = (code / RecoDecay::PdgDivisorBaryon) % DigitBase; - const auto quark2 = (code / RecoDecay::PdgDivisorMeson) % DigitBase; - const auto quark3 = (code / DigitBase) % DigitBase; - // Quarks, diquarks and generator-specific entries are not hadron decays. - if (quark1 > kTop || quark2 < kDown || quark2 > kTop || quark3 < kDown || quark3 > kTop) { - return false; - } - return quark1 == kCharm || quark1 == kBottom || quark2 == kCharm || quark2 == kBottom || quark3 == kCharm || quark3 == kBottom; -} - -template -inline bool hasHeavyFlavorHadronAncestor(T const& particles, typename T::iterator const& particle) -{ - auto ancestor = particle; - std::vector motherIds; - while (ancestor.has_mothers()) { - motherIds.clear(); - RecoDecay::getOrderedMotherIds(ancestor, motherIds); - if (motherIds.empty()) { - break; - } - ancestor = particles.rawIteratorAt(motherIds.front() - particles.offset()); - if (isHeavyFlavorHadron(ancestor.pdgCode())) { - return true; - } - } - return false; -} - /// Classify true kaons from the correct collision for the DCA templates. template inline int getKaonOrigin(T const& particles, typename T::iterator const& particle) @@ -153,7 +115,7 @@ inline int getKaonOrigin(T const& particles, typename T::iterator const& particl return kMaterial; } // HF decay products can be physical primaries. Check their ancestry first. - if (hasHeavyFlavorHadronAncestor(particles, particle)) { + if (RecoDecay::getParticleOrigin(particles, particle) != RecoDecay::OriginType::None) { return kSecondaryFromHF; } if (particle.isPhysicalPrimary()) {