From 027e597fe789b672c25cb40323138031f1355ee5 Mon Sep 17 00:00:00 2001 From: BiaoZhang Date: Sat, 31 Jan 2026 16:46:19 +0100 Subject: [PATCH 01/14] enabled TPC pid for light nuclei based on Bethe-Bloch parametrization --- PWGHF/Core/SelectorCuts.h | 14 ++- PWGHF/TableProducer/trackIndexSkimCreator.cxx | 119 +++++++++++++++--- 2 files changed, 110 insertions(+), 23 deletions(-) diff --git a/PWGHF/Core/SelectorCuts.h b/PWGHF/Core/SelectorCuts.h index 0351681ce23..bc39b235a0a 100644 --- a/PWGHF/Core/SelectorCuts.h +++ b/PWGHF/Core/SelectorCuts.h @@ -85,12 +85,18 @@ namespace hf_presel_lightnuclei { // default values for the track cuts for lightnuclei in the track-index-skim-creator -constexpr float CutsTrackQuality[3][9] = {{-4, 3, 5., 0., 100, 100, 0.83, 160., 1.}, - {-4, 3, 5., 0., 100, 100, 0.83, 160., 1.}, - {-4, 3, 5., 0., 100, 100, 0.83, 160., 1.}}; -static const std::vector labelsCutsTrack = {"nSigmaMinIts", "minItsClusterSizes", "minItsCluster", "minItsIbCluster", "minTpcCluster", "minTpcRow", "minTpcCrossedOverFound", "maxTpcShared", "maxTpcFracShared"}; +constexpr float CutsTrackQuality[3][10] = {{-4, 3, 5., 0., 100, 100, 0.83, 160., 1., 5}, + {-4, 3, 5., 0., 100, 100, 0.83, 160., 1., 5}, + {-4, 3, 5., 0., 100, 100, 0.83, 160., 1., 5}}; +static const std::vector labelsCutsTrack = {"nSigmaMinIts", "minItsClusterSizes", "minItsCluster", "minItsIbCluster", "minTpcCluster", "minTpcRow", "minTpcCrossedOverFound", "maxTpcShared", "maxTpcFracShared", "maxTPCnSigmaBB"}; static const std::vector labelsRowsNucleiType = {"Deutron", "Triton", "Helium3"}; +constexpr float BetheBlochParams[3][6] = {{5.39302, 7.859534, 0.004048, 2.323197, 1.609307, 0.09}, + {5.39302, 7.859534, 0.004048, 2.323197, 1.609307, 0.09}, + {-126.557359, -0.858569, 1.111643, 1.210323, 2.656374, 0.09}}; + +static const std::vector labelsBetheBlochParams = {"p0", "p1", "p2", "p3", "p4", "resolution"}; + } // namespace hf_presel_lightnuclei namespace hf_cuts_bdt_multiclass diff --git a/PWGHF/TableProducer/trackIndexSkimCreator.cxx b/PWGHF/TableProducer/trackIndexSkimCreator.cxx index 396e8dea819..a2a2124d33b 100644 --- a/PWGHF/TableProducer/trackIndexSkimCreator.cxx +++ b/PWGHF/TableProducer/trackIndexSkimCreator.cxx @@ -45,6 +45,7 @@ #include "Common/DataModel/TrackSelectionTables.h" #include "Tools/ML/MlResponse.h" +#include "MathUtils/BetheBlochAleph.h" #include // for PV refit #include #include @@ -1302,7 +1303,10 @@ struct HfTrackIndexSkimCreator { Configurable> cutsDstarToD0Pi{"cutsDstarToD0Pi", {hf_cuts_presel_dstar::Cuts[0], hf_cuts_presel_dstar::NBinsPt, hf_cuts_presel_dstar::NCutVars, hf_cuts_presel_dstar::labelsPt, hf_cuts_presel_dstar::labelsCutVar}, "D*+->D0pi selections per pT bin"}; // CharmNuclei track selection - Configurable> selectionsLightNuclei{"selectionsLightNuclei", {hf_presel_lightnuclei::CutsTrackQuality[0], 3, 9, hf_presel_lightnuclei::labelsRowsNucleiType, hf_presel_lightnuclei::labelsCutsTrack}, "nuclei track selections for deuteron / triton / helium applied if proper process function enabled"}; + Configurable> selectionsLightNuclei{"selectionsLightNuclei", {hf_presel_lightnuclei::CutsTrackQuality[0], 3, 10, hf_presel_lightnuclei::labelsRowsNucleiType, hf_presel_lightnuclei::labelsCutsTrack}, "nuclei track selections for deuteron / triton / helium applied if proper process function enabled"}; + Configurable> tpcPidBBParamsLightNuclei{"tpcPidBBParamsLightNuclei", {hf_presel_lightnuclei::BetheBlochParams[0], 3, 6, hf_presel_lightnuclei::labelsRowsNucleiType, hf_presel_lightnuclei::labelsBetheBlochParams}, + "TPC PID Bethe–Bloch parameter configurations for light nuclei " + "(deuteron, triton, helium-3), used in BB-based PID when enabled"}; // proton PID selections for Lc and Xic Configurable applyProtonPidForLcToPKPi{"applyProtonPidForLcToPKPi", false, "Apply proton PID for Lc->pKpi"}; @@ -1311,6 +1315,8 @@ struct HfTrackIndexSkimCreator { Configurable applyDeuteronPidForCdToDeKPi{"applyDeuteronPidForCdToDeKPi", false, "Require deuteron PID for Cd->deKpi"}; Configurable applyTritonPidForCtToTrKPi{"applyTritonPidForCtToTrKPi", false, "Require triton PID for Ct->tKpi"}; Configurable applyHeliumPidForChToHeKPi{"applyHeliumPidForChToHeKPi", false, "Require helium3 PID for Ch->heKpi"}; + Configurable applyLightNucleiTpcPidBasedOnBB{"applyLightNucleiTpcPidBasedOnBB", false, "Apply TPC PID for light nuclei using Bethe–Bloch parameterization"}; + // lightnuclei track selection for charmnuclei Configurable applyNucleiSelcForCharmNuclei{"applyNucleiSelcForCharmNuclei", false, "Require track selection for charm nuclei"}; // ML models for triggers @@ -1654,18 +1660,36 @@ struct HfTrackIndexSkimCreator { } /// Apply track-quality (ITS/TPC) + optional ITS-PID preselection for light-nucleus daughters used in charm-nuclei 3-prong channels (Cd/Ct/Ch). - /// \tparam TrackType Track/ASoA row type providing ITS/TPC quality accessors. + /// \tparam TrackType Track providing ITS/TPC quality accessors. /// \param track Daughter track to be tested (either prong0 or prong2). - /// \param lightnuclei Species selector 0: Deuteron, 1: Triton, 2: Helium3. - /// \param nSigmaItsPid ITS nσ value for the selected light nucleus hypothesis + /// \param lightnuclei Species selector: 0=Deuteron, 1=Triton, 2=Helium3. + /// \return true if the track passes all enabled selections. template bool applyTrackSelectionForCharmNuclei(const TrackType& track, - ChannelsNucleiQA lightnuclei, - float nSigmaItsPid) + ChannelsNucleiQA lightnuclei) { // Row index in the selection table: 0 (De), 1 (Tr), 2 (He3) const int row = static_cast(lightnuclei); - const float nSigmaItsNuclei = nSigmaItsPid; + if (row < 0 || row >= NChannelsLightNucleiPid) { + return false; + } + + float nSigmaItsNuclei = -999.f; + + switch (lightnuclei) { + case ChannelsNucleiQA::Deuteron: + nSigmaItsNuclei = track.itsNSigmaDe(); + break; + case ChannelsNucleiQA::Triton: + nSigmaItsNuclei = track.itsNSigmaTr(); + break; + case ChannelsNucleiQA::Helium3: + nSigmaItsNuclei = track.itsNSigmaHe(); + break; + default: + return false; + } + // Load cuts for the selected species. const float minItsNSigmaPid = config.selectionsLightNuclei->get(row, 0u); const int minItsClusterSizes = config.selectionsLightNuclei->get(row, 1u); @@ -1677,6 +1701,9 @@ struct HfTrackIndexSkimCreator { const int maxTpcShared = config.selectionsLightNuclei->get(row, 7u); const float maxTpcFracShared = config.selectionsLightNuclei->get(row, 8u); + // Optional: BB-based TPC nσ selection (only if enabled) + const float maxTPCnSigmaBB = config.selectionsLightNuclei->get(row, 9u); + if (nSigmaItsNuclei < minItsNSigmaPid) { return false; } @@ -1704,9 +1731,71 @@ struct HfTrackIndexSkimCreator { if (track.tpcFractionSharedCls() > maxTpcFracShared) { return false; } + + if (config.applyLightNucleiTpcPidBasedOnBB) { + const float nSigmaTpcNuclei = getTPCnSigmaBB(track, lightnuclei); + if (nSigmaTpcNuclei < -999.f) { // invalid marker + return false; + } + // Correct inequality: reject if |nσ| exceeds allowed window + if (std::abs(nSigmaTpcNuclei) > maxTPCnSigmaBB) { + return false; + } + } + return true; } + /// Compute TPC nσ for light nuclei (De/Tr/He3) using a Bethe–Bloch parameter configuration (BB-based PID). + /// + /// \tparam TrackType Track/ASoA row type providing TPC accessors. + /// \param track Track to be tested. + /// \param lightnuclei Species selector: 0=Deuteron, 1=Triton, 2=Helium3. + /// \return TPC nσ for the chosen nucleus hypothesis (or -999 if not applicable). + template + float getTPCnSigmaBB(const TrackType& track, ChannelsNucleiQA lightnuclei) + { + if (!track.hasTPC()) { + return -999.f; + } + + const int row = static_cast(lightnuclei); + if (row < 0 || row >= NChannelsLightNucleiPid) { + return -999.f; + } + + // Columns: [0..4] BB params, [5] relative resolution (sigma/mean) + const double bb0 = config.tpcPidBBParamsLightNuclei->get(row, 0u); + const double bb1 = config.tpcPidBBParamsLightNuclei->get(row, 1u); + const double bb2 = config.tpcPidBBParamsLightNuclei->get(row, 2u); + const double bb3 = config.tpcPidBBParamsLightNuclei->get(row, 3u); + const double bb4 = config.tpcPidBBParamsLightNuclei->get(row, 4u); + const double relRes = config.tpcPidBBParamsLightNuclei->get(row, 5u); + + if (relRes <= 0.f) { + return -999.f; + } + + // Mass/charge hypothesis for the selected nucleus. + const double mass = + (lightnuclei == ChannelsNucleiQA::Deuteron) ? MassDeuteron : (lightnuclei == ChannelsNucleiQA::Triton) ? MassTriton + : MassHelium3; + + const int charge = (lightnuclei == ChannelsNucleiQA::Helium3) ? 2 : 1; + + const float rigidity = track.tpcInnerParam(); // p / |q| note: here we didn't apply rigidity correction + + const double x = static_cast(charge) * static_cast(rigidity) / mass; + const double expBethe = common::BetheBlochAleph(x, bb0, bb1, bb2, bb3, bb4); + const double expSigma = expBethe * static_cast(relRes); + + if (expSigma <= 0.) { + return -999.f; + } + + return static_cast((track.tpcSignal() - expBethe) / expSigma); + } + /// Method to perform selections on difference from nominal mass for phi decay /// \param binPt pt bin for the cuts /// \param pVecTrack0 is the momentum array of the first daughter track @@ -1763,25 +1852,17 @@ struct HfTrackIndexSkimCreator { iDecay3P == hf_cand_3prong::DecayType::ChToHeKPi)) { ChannelsNucleiQA nucleiType = ChannelsNucleiQA::Deuteron; - float nSigmaItsNuclei0 = track0.itsNSigmaDe(); - float nSigmaItsNuclei2 = track2.itsNSigmaDe(); - - if (iDecay3P == hf_cand_3prong::DecayType::CtToTrKPi) { + if (iDecay3P == hf_cand_3prong::DecayType::CtToTrKPi) nucleiType = ChannelsNucleiQA::Triton; - nSigmaItsNuclei0 = track0.itsNSigmaTr(); - nSigmaItsNuclei2 = track2.itsNSigmaTr(); - } else if (iDecay3P == hf_cand_3prong::DecayType::ChToHeKPi) { + else if (iDecay3P == hf_cand_3prong::DecayType::ChToHeKPi) nucleiType = ChannelsNucleiQA::Helium3; - nSigmaItsNuclei0 = track0.itsNSigmaHe(); - nSigmaItsNuclei2 = track2.itsNSigmaHe(); - } // hypo0: nucleus on track0 - if (!applyTrackSelectionForCharmNuclei(track0, nucleiType, nSigmaItsNuclei0)) { + if (!applyTrackSelectionForCharmNuclei(track0, nucleiType)) { CLRBIT(whichHypo[iDecay3P], 0); } // hypo1: nucleus on track2 - if (!applyTrackSelectionForCharmNuclei(track2, nucleiType, nSigmaItsNuclei2)) { + if (!applyTrackSelectionForCharmNuclei(track2, nucleiType)) { CLRBIT(whichHypo[iDecay3P], 1); } From 590e56377e993c204e137f66cfff70efed503c7f Mon Sep 17 00:00:00 2001 From: BiaoZhang Date: Sat, 31 Jan 2026 18:35:57 +0100 Subject: [PATCH 02/14] Add PID QA plots --- PWGHF/TableProducer/trackIndexSkimCreator.cxx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/PWGHF/TableProducer/trackIndexSkimCreator.cxx b/PWGHF/TableProducer/trackIndexSkimCreator.cxx index a2a2124d33b..bb57e8a698c 100644 --- a/PWGHF/TableProducer/trackIndexSkimCreator.cxx +++ b/PWGHF/TableProducer/trackIndexSkimCreator.cxx @@ -1294,7 +1294,7 @@ struct HfTrackIndexSkimCreator { Configurable> cutsCdToDeKPi{"cutsCdToDeKPi", {hf_cuts_presel_3prong::Cuts[0], hf_cuts_presel_3prong::NBinsPt, hf_cuts_presel_3prong::NCutVars, hf_cuts_presel_3prong::labelsPt, hf_cuts_presel_3prong::labelsCutVar}, "Cd->deKpi selections per pT bin"}; // Ct cuts Configurable> binsPtCtToTrKPi{"binsPtCtToTrKPi", std::vector{hf_cuts_presel_3prong::vecBinsPt}, "pT bin limits for Ct->tKpi pT-dependent cuts"}; - Configurable> cutsCtToTrKPi{"cutsCdToTrKPi", {hf_cuts_presel_3prong::Cuts[0], hf_cuts_presel_3prong::NBinsPt, hf_cuts_presel_3prong::NCutVars, hf_cuts_presel_3prong::labelsPt, hf_cuts_presel_3prong::labelsCutVar}, "Ct->tKpi selections per pT bin"}; + Configurable> cutsCtToTrKPi{"cutsCtToTrKPi", {hf_cuts_presel_3prong::Cuts[0], hf_cuts_presel_3prong::NBinsPt, hf_cuts_presel_3prong::NCutVars, hf_cuts_presel_3prong::labelsPt, hf_cuts_presel_3prong::labelsCutVar}, "Ct->tKpi selections per pT bin"}; // Ch cuts Configurable> binsPtChToHeKPi{"binsPtChToHeKPi", std::vector{hf_cuts_presel_3prong::vecBinsPt}, "pT bin limits for Ch->heKpi pT-dependent cuts"}; Configurable> cutsChToHeKPi{"cutsChToHeKPi", {hf_cuts_presel_3prong::Cuts[0], hf_cuts_presel_3prong::NBinsPt, hf_cuts_presel_3prong::NCutVars, hf_cuts_presel_3prong::labelsPt, hf_cuts_presel_3prong::labelsCutVar}, "Ch->heKpi selections per pT bin"}; @@ -1483,7 +1483,9 @@ struct HfTrackIndexSkimCreator { registry.add("hMassCdToDeKPi", "C Deuteron candidates;inv. mass (De K #pi) (GeV/#it{c}^{2});entries", {HistType::kTH1D, {{500, 0., 5.}}}); registry.add("hMassCtToTrKPi", "C Triton candidates;inv. mass (Tr K #pi) (GeV/#it{c}^{2});entries", {HistType::kTH1D, {{500, 0., 5.}}}); registry.add("hMassChToHeKPi", "C Helium3 candidates;inv. mass (He3 K #pi) (GeV/#it{c}^{2});entries", {HistType::kTH1D, {{500, 0., 5.}}}); - + if (config.applyNucleiSelcForCharmNuclei && config.applyLightNucleiTpcPidBasedOnBB) { + registry.add("hTPCSignalsLightNuclei", "Light Nuclei TPC signal (a.u.)", {HistType::kTH2D, {{2000, -10., 10.}, {1000, 0., 2000.}}}); + } // needed for PV refitting if (doprocess2And3ProngsWithPvRefit || doprocess2And3ProngsWithPvRefitWithPidForHfFiltersBdt) { const AxisSpec axisCollisionX{100, -20.f, 20.f, "X (cm)"}; @@ -1860,10 +1862,14 @@ struct HfTrackIndexSkimCreator { // hypo0: nucleus on track0 if (!applyTrackSelectionForCharmNuclei(track0, nucleiType)) { CLRBIT(whichHypo[iDecay3P], 0); + } else { + registry.fill(HIST("hTPCSignalsLightNuclei"), track0.tpcInnerParam() * track0.sign(), track0.tpcSignal()); } // hypo1: nucleus on track2 if (!applyTrackSelectionForCharmNuclei(track2, nucleiType)) { CLRBIT(whichHypo[iDecay3P], 1); + } else { + registry.fill(HIST("hTPCSignalsLightNuclei"), track2.tpcInnerParam() * track2.sign(), track2.tpcSignal()); } if (whichHypo[iDecay3P] == 0) { From 5200e7215c0b2f3184a5db4404c40611a40b1e5e Mon Sep 17 00:00:00 2001 From: BiaoZhang Date: Sat, 31 Jan 2026 18:46:50 +0100 Subject: [PATCH 03/14] fix typo --- PWGHF/TableProducer/trackIndexSkimCreator.cxx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/PWGHF/TableProducer/trackIndexSkimCreator.cxx b/PWGHF/TableProducer/trackIndexSkimCreator.cxx index bb57e8a698c..91039fd549a 100644 --- a/PWGHF/TableProducer/trackIndexSkimCreator.cxx +++ b/PWGHF/TableProducer/trackIndexSkimCreator.cxx @@ -1853,11 +1853,16 @@ struct HfTrackIndexSkimCreator { iDecay3P == hf_cand_3prong::DecayType::CtToTrKPi || iDecay3P == hf_cand_3prong::DecayType::ChToHeKPi)) { - ChannelsNucleiQA nucleiType = ChannelsNucleiQA::Deuteron; - if (iDecay3P == hf_cand_3prong::DecayType::CtToTrKPi) + ChannelsNucleiQA nucleiType; + if (iDecay3P == hf_cand_3prong::DecayType::CdToDeKPi) + nucleiType = ChannelsNucleiQA::Deuteron; + else if (iDecay3P == hf_cand_3prong::DecayType::CtToTrKPi) nucleiType = ChannelsNucleiQA::Triton; else if (iDecay3P == hf_cand_3prong::DecayType::ChToHeKPi) nucleiType = ChannelsNucleiQA::Helium3; + else { + LOG(fatal, "Unhandled DecayType = %d", static_cast(iDecay3P)); + } // hypo0: nucleus on track0 if (!applyTrackSelectionForCharmNuclei(track0, nucleiType)) { From 129256aea29cb477f55d45c25cdb3ca0e9c421c7 Mon Sep 17 00:00:00 2001 From: BiaoZhang Date: Sat, 31 Jan 2026 20:16:39 +0100 Subject: [PATCH 04/14] fix warning --- PWGHF/Core/SelectorCuts.h | 2 +- PWGHF/TableProducer/trackIndexSkimCreator.cxx | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/PWGHF/Core/SelectorCuts.h b/PWGHF/Core/SelectorCuts.h index bc39b235a0a..6f1e3044ad9 100644 --- a/PWGHF/Core/SelectorCuts.h +++ b/PWGHF/Core/SelectorCuts.h @@ -93,7 +93,7 @@ static const std::vector labelsRowsNucleiType = {"Deutron", "Triton constexpr float BetheBlochParams[3][6] = {{5.39302, 7.859534, 0.004048, 2.323197, 1.609307, 0.09}, {5.39302, 7.859534, 0.004048, 2.323197, 1.609307, 0.09}, - {-126.557359, -0.858569, 1.111643, 1.210323, 2.656374, 0.09}}; + {-126.55736, -0.858569, 1.11164, 1.21032, 2.656374, 0.09}}; static const std::vector labelsBetheBlochParams = {"p0", "p1", "p2", "p3", "p4", "resolution"}; diff --git a/PWGHF/TableProducer/trackIndexSkimCreator.cxx b/PWGHF/TableProducer/trackIndexSkimCreator.cxx index 91039fd549a..a95c974c002 100644 --- a/PWGHF/TableProducer/trackIndexSkimCreator.cxx +++ b/PWGHF/TableProducer/trackIndexSkimCreator.cxx @@ -1854,13 +1854,13 @@ struct HfTrackIndexSkimCreator { iDecay3P == hf_cand_3prong::DecayType::ChToHeKPi)) { ChannelsNucleiQA nucleiType; - if (iDecay3P == hf_cand_3prong::DecayType::CdToDeKPi) + if (iDecay3P == hf_cand_3prong::DecayType::CdToDeKPi) { nucleiType = ChannelsNucleiQA::Deuteron; - else if (iDecay3P == hf_cand_3prong::DecayType::CtToTrKPi) + } else if (iDecay3P == hf_cand_3prong::DecayType::CtToTrKPi) { nucleiType = ChannelsNucleiQA::Triton; - else if (iDecay3P == hf_cand_3prong::DecayType::ChToHeKPi) + } else if (iDecay3P == hf_cand_3prong::DecayType::ChToHeKPi) { nucleiType = ChannelsNucleiQA::Helium3; - else { + } else { LOG(fatal, "Unhandled DecayType = %d", static_cast(iDecay3P)); } From 01c6f00ad154b9b6eaf2e1018230fe28114866f1 Mon Sep 17 00:00:00 2001 From: BiaoZhang Date: Sat, 31 Jan 2026 21:07:10 +0100 Subject: [PATCH 05/14] fix warning --- PWGHF/TableProducer/trackIndexSkimCreator.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PWGHF/TableProducer/trackIndexSkimCreator.cxx b/PWGHF/TableProducer/trackIndexSkimCreator.cxx index a95c974c002..1af0070624e 100644 --- a/PWGHF/TableProducer/trackIndexSkimCreator.cxx +++ b/PWGHF/TableProducer/trackIndexSkimCreator.cxx @@ -1861,7 +1861,7 @@ struct HfTrackIndexSkimCreator { } else if (iDecay3P == hf_cand_3prong::DecayType::ChToHeKPi) { nucleiType = ChannelsNucleiQA::Helium3; } else { - LOG(fatal, "Unhandled DecayType = %d", static_cast(iDecay3P)); + LOGF(fatal, "Unhandled DecayType = %d", static_cast(iDecay3P)); } // hypo0: nucleus on track0 From 309a35bafe23d6a30c75811fa2c58cabe7b3a0f1 Mon Sep 17 00:00:00 2001 From: BiaoZhang Date: Sun, 1 Feb 2026 12:39:01 +0100 Subject: [PATCH 06/14] fix error --- PWGHF/TableProducer/trackIndexSkimCreator.cxx | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/PWGHF/TableProducer/trackIndexSkimCreator.cxx b/PWGHF/TableProducer/trackIndexSkimCreator.cxx index 1af0070624e..61be9a3a589 100644 --- a/PWGHF/TableProducer/trackIndexSkimCreator.cxx +++ b/PWGHF/TableProducer/trackIndexSkimCreator.cxx @@ -1853,17 +1853,9 @@ struct HfTrackIndexSkimCreator { iDecay3P == hf_cand_3prong::DecayType::CtToTrKPi || iDecay3P == hf_cand_3prong::DecayType::ChToHeKPi)) { - ChannelsNucleiQA nucleiType; - if (iDecay3P == hf_cand_3prong::DecayType::CdToDeKPi) { - nucleiType = ChannelsNucleiQA::Deuteron; - } else if (iDecay3P == hf_cand_3prong::DecayType::CtToTrKPi) { - nucleiType = ChannelsNucleiQA::Triton; - } else if (iDecay3P == hf_cand_3prong::DecayType::ChToHeKPi) { - nucleiType = ChannelsNucleiQA::Helium3; - } else { - LOGF(fatal, "Unhandled DecayType = %d", static_cast(iDecay3P)); - } - + ChannelsNucleiQA nucleiType = + (iDecay3P == hf_cand_3prong::DecayType::CdToDeKPi) ? ChannelsNucleiQA::Deuteron : (iDecay3P == hf_cand_3prong::DecayType::CtToTrKPi) ? ChannelsNucleiQA::Triton + : ChannelsNucleiQA::Helium3; // hypo0: nucleus on track0 if (!applyTrackSelectionForCharmNuclei(track0, nucleiType)) { CLRBIT(whichHypo[iDecay3P], 0); From 457ae60c96cf419a000e8d68e926214d4853849c Mon Sep 17 00:00:00 2001 From: BiaoZhang Date: Sun, 1 Feb 2026 14:29:18 +0100 Subject: [PATCH 07/14] fix comments from vit --- PWGHF/Core/SelectorCuts.h | 16 +-- PWGHF/TableProducer/trackIndexSkimCreator.cxx | 99 +++++++++++-------- 2 files changed, 70 insertions(+), 45 deletions(-) diff --git a/PWGHF/Core/SelectorCuts.h b/PWGHF/Core/SelectorCuts.h index 6f1e3044ad9..c051aa238b5 100644 --- a/PWGHF/Core/SelectorCuts.h +++ b/PWGHF/Core/SelectorCuts.h @@ -84,16 +84,20 @@ static const std::vector labelsRowsPid = {"ProtonInLcToPKPi", "Prot namespace hf_presel_lightnuclei { +static constexpr int NParticleRows = 3; // number of particles / rows +static constexpr int NVarCuts = 10; // number of cuts for each particles +static constexpr int NBetheBlochParams = 6; // number of parameters for Bethe-Bloch + // default values for the track cuts for lightnuclei in the track-index-skim-creator -constexpr float CutsTrackQuality[3][10] = {{-4, 3, 5., 0., 100, 100, 0.83, 160., 1., 5}, - {-4, 3, 5., 0., 100, 100, 0.83, 160., 1., 5}, - {-4, 3, 5., 0., 100, 100, 0.83, 160., 1., 5}}; +constexpr float CutsTrackQuality[NParticleRows][NVarCuts] = {{-4.f, 3.f, 5.f, 0.f, 100.f, 100.f, 0.83, 160.f, 1.f, 5.f}, + {-4.f, 3.f, 5.f, 0.f, 100.f, 100.f, 0.83, 160.f, 1.f, 5.f}, + {-4.f, 3.f, 5.f, 0.f, 100.f, 100.f, 0.83, 160.f, 1.f, 5.f}}; static const std::vector labelsCutsTrack = {"nSigmaMinIts", "minItsClusterSizes", "minItsCluster", "minItsIbCluster", "minTpcCluster", "minTpcRow", "minTpcCrossedOverFound", "maxTpcShared", "maxTpcFracShared", "maxTPCnSigmaBB"}; static const std::vector labelsRowsNucleiType = {"Deutron", "Triton", "Helium3"}; -constexpr float BetheBlochParams[3][6] = {{5.39302, 7.859534, 0.004048, 2.323197, 1.609307, 0.09}, - {5.39302, 7.859534, 0.004048, 2.323197, 1.609307, 0.09}, - {-126.55736, -0.858569, 1.11164, 1.21032, 2.656374, 0.09}}; +constexpr float BetheBlochParams[NParticleRows][NBetheBlochParams] = {{5.39302, 7.859534, 0.004048, 2.323197, 1.609307, 0.09}, + {5.39302, 7.859534, 0.004048, 2.323197, 1.609307, 0.09}, + {-126.55736, -0.858569, 1.11164, 1.21032, 2.656374, 0.09}}; static const std::vector labelsBetheBlochParams = {"p0", "p1", "p2", "p3", "p4", "resolution"}; diff --git a/PWGHF/TableProducer/trackIndexSkimCreator.cxx b/PWGHF/TableProducer/trackIndexSkimCreator.cxx index 61be9a3a589..c2ab79f9aeb 100644 --- a/PWGHF/TableProducer/trackIndexSkimCreator.cxx +++ b/PWGHF/TableProducer/trackIndexSkimCreator.cxx @@ -45,7 +45,6 @@ #include "Common/DataModel/TrackSelectionTables.h" #include "Tools/ML/MlResponse.h" -#include "MathUtils/BetheBlochAleph.h" #include // for PV refit #include #include @@ -66,6 +65,7 @@ #include #include #include +#include #include #include // for PV refit @@ -1303,8 +1303,8 @@ struct HfTrackIndexSkimCreator { Configurable> cutsDstarToD0Pi{"cutsDstarToD0Pi", {hf_cuts_presel_dstar::Cuts[0], hf_cuts_presel_dstar::NBinsPt, hf_cuts_presel_dstar::NCutVars, hf_cuts_presel_dstar::labelsPt, hf_cuts_presel_dstar::labelsCutVar}, "D*+->D0pi selections per pT bin"}; // CharmNuclei track selection - Configurable> selectionsLightNuclei{"selectionsLightNuclei", {hf_presel_lightnuclei::CutsTrackQuality[0], 3, 10, hf_presel_lightnuclei::labelsRowsNucleiType, hf_presel_lightnuclei::labelsCutsTrack}, "nuclei track selections for deuteron / triton / helium applied if proper process function enabled"}; - Configurable> tpcPidBBParamsLightNuclei{"tpcPidBBParamsLightNuclei", {hf_presel_lightnuclei::BetheBlochParams[0], 3, 6, hf_presel_lightnuclei::labelsRowsNucleiType, hf_presel_lightnuclei::labelsBetheBlochParams}, + Configurable> selectionsLightNuclei{"selectionsLightNuclei", {hf_presel_lightnuclei::CutsTrackQuality[0], hf_presel_lightnuclei::NParticleRows, hf_presel_lightnuclei::NVarCuts, hf_presel_lightnuclei::labelsRowsNucleiType, hf_presel_lightnuclei::labelsCutsTrack}, "nuclei track selections for deuteron / triton / helium applied if proper process function enabled"}; + Configurable> tpcPidBBParamsLightNuclei{"tpcPidBBParamsLightNuclei", {hf_presel_lightnuclei::BetheBlochParams[0], hf_presel_lightnuclei::NParticleRows, hf_presel_lightnuclei::NBetheBlochParams, hf_presel_lightnuclei::labelsRowsNucleiType, hf_presel_lightnuclei::labelsBetheBlochParams}, "TPC PID Bethe–Bloch parameter configurations for light nuclei " "(deuteron, triton, helium-3), used in BB-based PID when enabled"}; @@ -1676,71 +1676,67 @@ struct HfTrackIndexSkimCreator { return false; } - float nSigmaItsNuclei = -999.f; + float itsPidNsigma = -999.f; switch (lightnuclei) { case ChannelsNucleiQA::Deuteron: - nSigmaItsNuclei = track.itsNSigmaDe(); + itsPidNsigma = track.itsNSigmaDe(); break; case ChannelsNucleiQA::Triton: - nSigmaItsNuclei = track.itsNSigmaTr(); + itsPidNsigma = track.itsNSigmaTr(); break; case ChannelsNucleiQA::Helium3: - nSigmaItsNuclei = track.itsNSigmaHe(); + itsPidNsigma = track.itsNSigmaHe(); break; default: - return false; + LOG(fatal) << "Unhandled ChannelsNucleiQA " << static_cast(lightnuclei); } // Load cuts for the selected species. - const float minItsNSigmaPid = config.selectionsLightNuclei->get(row, 0u); - const int minItsClusterSizes = config.selectionsLightNuclei->get(row, 1u); - const int minItsCluster = config.selectionsLightNuclei->get(row, 2u); - const int minItsIbCluster = config.selectionsLightNuclei->get(row, 3u); - const int minTpcCluster = config.selectionsLightNuclei->get(row, 4u); - const int minTpcRow = config.selectionsLightNuclei->get(row, 5u); - const float minTpcCrossedOverFound = config.selectionsLightNuclei->get(row, 6u); - const int maxTpcShared = config.selectionsLightNuclei->get(row, 7u); - const float maxTpcFracShared = config.selectionsLightNuclei->get(row, 8u); + const float itsPidNsigmaMin = config.selectionsLightNuclei->get(row, 0u); + const float itsClusterSizeMin = config.selectionsLightNuclei->get(row, 1u); + const float itsClusterMin = config.selectionsLightNuclei->get(row, 2u); + const float itsIbClusterMin = config.selectionsLightNuclei->get(row, 3u); + const float tpcClusterMin = config.selectionsLightNuclei->get(row, 4u); + const float tpcCrossedRowsMin = config.selectionsLightNuclei->get(row, 5u); + const float tpcCrossedRowsOverFindMin = config.selectionsLightNuclei->get(row, 6u); + const float tpcSharedMax = config.selectionsLightNuclei->get(row, 7u); + const float tpcFracSharedMax = config.selectionsLightNuclei->get(row, 8u); // Optional: BB-based TPC nσ selection (only if enabled) - const float maxTPCnSigmaBB = config.selectionsLightNuclei->get(row, 9u); + const float tpcBbPidNsigmaMax = config.selectionsLightNuclei->get(row, 9u); - if (nSigmaItsNuclei < minItsNSigmaPid) { + if (itsPidNsigma < itsPidNsigmaMin) { return false; } - if (track.itsClusterSizes() < static_cast(minItsClusterSizes)) { + if (track.itsClusterSizes() < static_cast(itsClusterSizeMin)) { return false; } - if (track.itsNCls() < minItsCluster) { + if (track.itsNCls() < itsClusterMin) { return false; } - if (track.itsNClsInnerBarrel() < minItsIbCluster) { + if (track.itsNClsInnerBarrel() < itsIbClusterMin) { return false; } - if (track.tpcNClsFound() < minTpcCluster) { + if (track.tpcNClsFound() < tpcClusterMin) { return false; } - if (track.tpcNClsCrossedRows() < minTpcRow) { + if (track.tpcNClsCrossedRows() < tpcCrossedRowsMin) { return false; } - if (track.tpcCrossedRowsOverFindableCls() < minTpcCrossedOverFound) { + if (track.tpcCrossedRowsOverFindableCls() < tpcCrossedRowsOverFindMin) { return false; } - if (maxTpcShared >= 0 && track.tpcNClsShared() > maxTpcShared) { + if (track.tpcNClsShared() > tpcSharedMax) { return false; } - if (track.tpcFractionSharedCls() > maxTpcFracShared) { + if (track.tpcFractionSharedCls() > tpcFracSharedMax) { return false; } if (config.applyLightNucleiTpcPidBasedOnBB) { - const float nSigmaTpcNuclei = getTPCnSigmaBB(track, lightnuclei); - if (nSigmaTpcNuclei < -999.f) { // invalid marker - return false; - } - // Correct inequality: reject if |nσ| exceeds allowed window - if (std::abs(nSigmaTpcNuclei) > maxTPCnSigmaBB) { + const float tpcBbPidNsigma = getTPCnSigmaBB(track, lightnuclei); + if (std::abs(tpcBbPidNsigma) > tpcBbPidNsigmaMax) { return false; } } @@ -1779,9 +1775,20 @@ struct HfTrackIndexSkimCreator { } // Mass/charge hypothesis for the selected nucleus. - const double mass = - (lightnuclei == ChannelsNucleiQA::Deuteron) ? MassDeuteron : (lightnuclei == ChannelsNucleiQA::Triton) ? MassTriton - : MassHelium3; + double mass = 0.; + switch (lightnuclei) { + case ChannelsNucleiQA::Deuteron: + mass = MassDeuteron; + break; + case ChannelsNucleiQA::Triton: + mass = MassTriton; + break; + case ChannelsNucleiQA::Helium3: + mass = MassHelium3; + break; + default: + LOG(fatal) << "Unhandled ChannelsNucleiQA " << static_cast(lightnuclei); + } const int charge = (lightnuclei == ChannelsNucleiQA::Helium3) ? 2 : 1; @@ -1853,9 +1860,23 @@ struct HfTrackIndexSkimCreator { iDecay3P == hf_cand_3prong::DecayType::CtToTrKPi || iDecay3P == hf_cand_3prong::DecayType::ChToHeKPi)) { - ChannelsNucleiQA nucleiType = - (iDecay3P == hf_cand_3prong::DecayType::CdToDeKPi) ? ChannelsNucleiQA::Deuteron : (iDecay3P == hf_cand_3prong::DecayType::CtToTrKPi) ? ChannelsNucleiQA::Triton - : ChannelsNucleiQA::Helium3; + ChannelsNucleiQA nucleiType; + + switch (iDecay3P) { + case hf_cand_3prong::DecayType::CdToDeKPi: + nucleiType = ChannelsNucleiQA::Deuteron; + break; + case hf_cand_3prong::DecayType::CtToTrKPi: + nucleiType = ChannelsNucleiQA::Triton; + break; + case hf_cand_3prong::DecayType::ChToHeKPi: + nucleiType = ChannelsNucleiQA::Helium3; + break; + default: + LOG(fatal) << "Unhandled DecayType " << static_cast(iDecay3P); + continue; + } + // hypo0: nucleus on track0 if (!applyTrackSelectionForCharmNuclei(track0, nucleiType)) { CLRBIT(whichHypo[iDecay3P], 0); From 79aeaaadb7795dd6943b8b3a15b787f8c4c91ab9 Mon Sep 17 00:00:00 2001 From: BiaoZhang Date: Sun, 8 Feb 2026 16:18:13 +0100 Subject: [PATCH 08/14] fix comments from vit and move the track quality and pid selection to the single track skimming task --- PWGHF/Core/SelectorCuts.h | 7 +- PWGHF/TableProducer/trackIndexSkimCreator.cxx | 413 ++++++++---------- 2 files changed, 173 insertions(+), 247 deletions(-) diff --git a/PWGHF/Core/SelectorCuts.h b/PWGHF/Core/SelectorCuts.h index c051aa238b5..32107f05a39 100644 --- a/PWGHF/Core/SelectorCuts.h +++ b/PWGHF/Core/SelectorCuts.h @@ -67,18 +67,15 @@ static const std::vector labelsCutVarTrack = {"min_dcaxytoprimary", namespace hf_presel_pid { -static constexpr int NPidRows = 7; // number of PID channels / rows +static constexpr int NPidRows = 4; // number of PID channels / rows static constexpr int NPidCuts = 6; // number of cuts per PID (TPC+TOF) // default values for the PID cuts for protons in the track-index-skim-creator constexpr float CutsPid[NPidRows][NPidCuts] = {{0.f, 1000.f, 5.f, 0.f, 1000.f, 5.f}, - {0.f, 1000.f, 5.f, 0.f, 1000.f, 5.f}, - {0.f, 1000.f, 5.f, 0.f, 1000.f, 5.f}, - {0.f, 1000.f, 5.f, 0.f, 1000.f, 5.f}, {0.f, 1000.f, 5.f, 0.f, 1000.f, 5.f}, {0.f, 1000.f, 5.f, 0.f, 1000.f, 5.f}, {0.f, 1000.f, 5.f, 0.f, 1000.f, 5.f}}; static const std::vector labelsCutsPid = {"minPtTpc", "maxPtTpc", "nSigmaMaxTpc", "minPtTof", "maxPtTof", "nSigmaMaxTof"}; -static const std::vector labelsRowsPid = {"ProtonInLcToPKPi", "ProtonInXicToPKPi", "ProtonInLcToPK0S", "KaonIn3Prongs", "DeuteronInCdToDeKPi", "TritonInCtToTrKPi", "HeliumInChToHeKPi"}; +static const std::vector labelsRowsPid = {"ProtonInLcToPKPi", "ProtonInXicToPKPi", "ProtonInLcToPK0S", "KaonIn3Prongs"}; } // namespace hf_presel_pid namespace hf_presel_lightnuclei diff --git a/PWGHF/TableProducer/trackIndexSkimCreator.cxx b/PWGHF/TableProducer/trackIndexSkimCreator.cxx index c2ab79f9aeb..bd14819977c 100644 --- a/PWGHF/TableProducer/trackIndexSkimCreator.cxx +++ b/PWGHF/TableProducer/trackIndexSkimCreator.cxx @@ -333,6 +333,9 @@ struct HfTrackIndexSkimCreatorTagSelTracks { Configurable useIsGlobalTrackForSoftPion{"useIsGlobalTrackForSoftPion", false, "check isGlobalTrack status for soft pion tracks"}; Configurable useIsGlobalTrackWoDCAForSoftPion{"useIsGlobalTrackWoDCAForSoftPion", false, "check isGlobalTrackWoDCA status for soft pion tracks"}; Configurable useIsQualityTrackITSForSoftPion{"useIsQualityTrackITSForSoftPion", true, "check qualityTracksITS status for soft pion tracks"}; + // CharmNuclei track selection + Configurable> selectionsLightNuclei{"selectionsLightNuclei", {hf_presel_lightnuclei::CutsTrackQuality[0], hf_presel_lightnuclei::NParticleRows, hf_presel_lightnuclei::NVarCuts, hf_presel_lightnuclei::labelsRowsNucleiType, hf_presel_lightnuclei::labelsCutsTrack}, "nuclei track selections for deuteron / triton / helium applied if proper process function enabled"}; + Configurable> tpcPidBBParamsLightNuclei{"tpcPidBBParamsLightNuclei", {hf_presel_lightnuclei::BetheBlochParams[0], hf_presel_lightnuclei::NParticleRows, hf_presel_lightnuclei::NBetheBlochParams, hf_presel_lightnuclei::labelsRowsNucleiType, hf_presel_lightnuclei::labelsBetheBlochParams}, "TPC PID Bethe–Bloch parameter configurations for light nuclei (deuteron, triton, helium-3)"}; // proton PID, applied only if corresponding process function enabled Configurable> selectionsPid{"selectionsPid", {hf_presel_pid::CutsPid[0], hf_presel_pid::NPidRows, hf_presel_pid::NPidCuts, hf_presel_pid::labelsRowsPid, hf_presel_pid::labelsCutsPid}, "PID selections for proton / kaon / deuteron / triton /helium applied if proper process function enabled"}; // CCDB @@ -365,9 +368,6 @@ struct HfTrackIndexSkimCreatorTagSelTracks { // proton PID, if enabled std::array selectorProton{}; TrackSelectorKa selectorKaon; - TrackSelectorDe selectorDeuteron; - TrackSelectorTr selectorTriton; - TrackSelectorHe selectorHelium; Partition pvContributors = ((aod::track::flags & static_cast(aod::track::PVContributor)) == static_cast(aod::track::PVContributor)); Partition pvContributorsWithPidTpc = ((aod::track::flags & static_cast(aod::track::PVContributor)) == static_cast(aod::track::PVContributor)); @@ -431,6 +431,7 @@ struct HfTrackIndexSkimCreatorTagSelTracks { registry.add("hPtCutsCascadeBachelor", "tracks selected for cascade-bachelor vertexing;#it{p}_{T}^{track} (GeV/#it{c});entries", {HistType::kTH1D, {{360, 0., 36.}}}); registry.add("hDCAToPrimXYVsPtCutsCascadeBachelor", "tracks selected for cascade-bachelor vertexing;#it{p}_{T}^{track} (GeV/#it{c});DCAxy to prim. vtx. (cm);entries", {HistType::kTH2D, {{360, 0., 36.}, {400, -2., 2.}}}); registry.add("hEtaCutsCascadeBachelor", "tracks selected for cascade-bachelor vertexing;#it{#eta};entries", {HistType::kTH1D, {{static_cast(0.6 * (config.etaMaxTrackBachLfCasc - config.etaMinTrackBachLfCasc) * 100), -1.2 * config.etaMinTrackBachLfCasc, 1.2 * config.etaMaxTrackBachLfCasc}}}); + registry.add("hTPCSignalsLightNuclei", "Light Nuclei;p_{TPC}/z (GeV/#it{c}); d#it{E}/d#it{x}", {HistType::kTH2D, {{2000, -10., 10.}, {1000, 0., 2000.}}}); const std::string cutNames[nCuts + 1] = {"selected", "rej pT", "rej eta", "rej track quality", "rej dca"}; const std::string candNames[CandidateType::NCandidateTypes] = {"2-prong", "3-prong", "bachelor", "dstar", "lfCascBachelor"}; @@ -490,39 +491,167 @@ struct HfTrackIndexSkimCreatorTagSelTracks { selectorKaon.setRangePtTof(config.selectionsPid->get(ChannelKaonPid, 3u), config.selectionsPid->get(ChannelKaonPid, 4u)); // 3u == "minPtTof, 4u == "maxPtTof" selectorKaon.setRangeNSigmaTpc(-config.selectionsPid->get(ChannelKaonPid, 2u), config.selectionsPid->get(ChannelKaonPid, 2u)); // 2u == "nSigmaMaxTpc" selectorKaon.setRangeNSigmaTof(-config.selectionsPid->get(ChannelKaonPid, 5u), config.selectionsPid->get(ChannelKaonPid, 5u)); // 5u == "nSigmaMaxTof" + } - selectorDeuteron.setRangePtTpc(config.selectionsPid->get(ChannelsDeuteronPid, 0u), config.selectionsPid->get(ChannelsDeuteronPid, 1u)); // 0u == "minPtTpc", 1u == "maxPtTpc" - selectorDeuteron.setRangePtTof(config.selectionsPid->get(ChannelsDeuteronPid, 3u), config.selectionsPid->get(ChannelsDeuteronPid, 4u)); // 3u == "minPtTof, 4u == "maxPtTof" - selectorDeuteron.setRangeNSigmaTpc(-config.selectionsPid->get(ChannelsDeuteronPid, 2u), config.selectionsPid->get(ChannelsDeuteronPid, 2u)); // 2u == "nSigmaMaxTpc" - selectorDeuteron.setRangeNSigmaTof(-config.selectionsPid->get(ChannelsDeuteronPid, 5u), config.selectionsPid->get(ChannelsDeuteronPid, 5u)); // 5u == "nSigmaMaxTof" + /// Apply track-quality (ITS/TPC) + optional ITS-PID preselection for light-nucleus daughters used in charm-nuclei 3-prong channels (Cd/Ct/Ch). + /// \tparam TrackType Track providing ITS/TPC quality accessors. + /// \param track Daughter track to be tested (either prong0 or prong2). + /// \param lightnuclei Species selector: 0=Deuteron, 1=Triton, 2=Helium3. + /// \return true if the track passes all enabled selections. + template + bool applyPidAndTrackSelectionForCharmNuclei(const TrackType& track, + ChannelsNucleiQA lightnuclei) + { + // Row index in the selection table: 0 (De), 1 (Tr), 2 (He3) + const int row = static_cast(lightnuclei); + if (row < 0 || row >= NChannelsLightNucleiPid) { + return false; + } - selectorTriton.setRangePtTpc(config.selectionsPid->get(ChannelsTritonPid, 0u), config.selectionsPid->get(ChannelsTritonPid, 1u)); // 0u == "minPtTpc", 1u == "maxPtTpc" - selectorTriton.setRangePtTof(config.selectionsPid->get(ChannelsTritonPid, 3u), config.selectionsPid->get(ChannelsTritonPid, 4u)); // 3u == "minPtTof, 4u == "maxPtTof" - selectorTriton.setRangeNSigmaTpc(-config.selectionsPid->get(ChannelsTritonPid, 2u), config.selectionsPid->get(ChannelsTritonPid, 2u)); // 2u == "nSigmaMaxTpc" - selectorTriton.setRangeNSigmaTof(-config.selectionsPid->get(ChannelsTritonPid, 5u), config.selectionsPid->get(ChannelsTritonPid, 5u)); // 5u == "nSigmaMaxTof" + float nSigmaIts = -999.f; + + switch (lightnuclei) { + case ChannelsNucleiQA::Deuteron: + nSigmaIts = track.itsNSigmaDe(); + break; + case ChannelsNucleiQA::Triton: + nSigmaIts = track.itsNSigmaTr(); + break; + case ChannelsNucleiQA::Helium3: + nSigmaIts = track.itsNSigmaHe(); + break; + default: + LOG(fatal) << "Unhandled ChannelsNucleiQA " << static_cast(lightnuclei); + } + + // Load cuts for the selected species. + const float itsPidNsigmaMin = config.selectionsLightNuclei->get(row, 0u); + const float itsClusterSizeMin = config.selectionsLightNuclei->get(row, 1u); + const float itsClusterMin = config.selectionsLightNuclei->get(row, 2u); + const float itsIbClusterMin = config.selectionsLightNuclei->get(row, 3u); + const float tpcClusterMin = config.selectionsLightNuclei->get(row, 4u); + const float tpcCrossedRowsMin = config.selectionsLightNuclei->get(row, 5u); + const float tpcCrossedRowsOverFindMin = config.selectionsLightNuclei->get(row, 6u); + const float tpcSharedMax = config.selectionsLightNuclei->get(row, 7u); + const float tpcFracSharedMax = config.selectionsLightNuclei->get(row, 8u); - selectorHelium.setRangePtTpc(config.selectionsPid->get(ChannelsHeliumPid, 0u), config.selectionsPid->get(ChannelsHeliumPid, 1u)); // 0u == "minPtTpc", 1u == "maxPtTpc" - selectorHelium.setRangePtTof(config.selectionsPid->get(ChannelsHeliumPid, 3u), config.selectionsPid->get(ChannelsHeliumPid, 4u)); // 3u == "minPtTof, 4u == "maxPtTof" - selectorHelium.setRangeNSigmaTpc(-config.selectionsPid->get(ChannelsHeliumPid, 2u), config.selectionsPid->get(ChannelsHeliumPid, 2u)); // 2u == "nSigmaMaxTpc" - selectorHelium.setRangeNSigmaTof(-config.selectionsPid->get(ChannelsHeliumPid, 5u), config.selectionsPid->get(ChannelsHeliumPid, 5u)); // 5u == "nSigmaMaxTof" + // Optional: BB-based TPC nσ selection (only if enabled) + const float tpcBbPidNsigmaMax = config.selectionsLightNuclei->get(row, 9u); + + if (nSigmaIts < itsPidNsigmaMin) { + return false; + } + if (track.itsClusterSizes() < static_cast(itsClusterSizeMin)) { + return false; + } + if (track.itsNCls() < itsClusterMin) { + return false; + } + if (track.itsNClsInnerBarrel() < itsIbClusterMin) { + return false; + } + if (track.tpcNClsFound() < tpcClusterMin) { + return false; + } + if (track.tpcNClsCrossedRows() < tpcCrossedRowsMin) { + return false; + } + if (track.tpcCrossedRowsOverFindableCls() < tpcCrossedRowsOverFindMin) { + return false; + } + if (track.tpcNClsShared() > tpcSharedMax) { + return false; + } + if (track.tpcFractionSharedCls() > tpcFracSharedMax) { + return false; + } + + const float tpcBbPidNsigma = getTPCNSigmaLightNucleiBetheBloch(track, lightnuclei); + if (std::abs(tpcBbPidNsigma) > tpcBbPidNsigmaMax) { + return false; + } + registry.fill(HIST("hTPCSignalsLightNuclei"), track.tpcInnerParam() * track.sign(), track.tpcSignal()); + return true; + } + + /// Compute TPC nσ for light nuclei (De/Tr/He3) using a Bethe–Bloch parameter configuration (BB-based PID). + /// + /// \tparam TrackType Track/ASoA row type providing TPC accessors. + /// \param track Track to be tested. + /// \param lightnuclei Species selector: 0=Deuteron, 1=Triton, 2=Helium3. + /// \return TPC nσ for the chosen nucleus hypothesis (or -999 if not applicable). + template + float getTPCNSigmaLightNucleiBetheBloch(const TrackType& track, ChannelsNucleiQA lightnuclei) + { + if (!track.hasTPC()) { + return -999.f; + } + + const int row = static_cast(lightnuclei); + if (row < 0 || row >= NChannelsLightNucleiPid) { + return -999.f; + } + + // Columns: [0..4] BB params, [5] relative resolution (sigma/mean) + const double bb0 = config.tpcPidBBParamsLightNuclei->get(row, 0u); + const double bb1 = config.tpcPidBBParamsLightNuclei->get(row, 1u); + const double bb2 = config.tpcPidBBParamsLightNuclei->get(row, 2u); + const double bb3 = config.tpcPidBBParamsLightNuclei->get(row, 3u); + const double bb4 = config.tpcPidBBParamsLightNuclei->get(row, 4u); + const double relRes = config.tpcPidBBParamsLightNuclei->get(row, 5u); + + if (relRes <= 0.f) { + return -999.f; + } + + // Mass/charge hypothesis for the selected nucleus. + double mass = 0.; + switch (lightnuclei) { + case ChannelsNucleiQA::Deuteron: + mass = MassDeuteron; + break; + case ChannelsNucleiQA::Triton: + mass = MassTriton; + break; + case ChannelsNucleiQA::Helium3: + mass = MassHelium3; + break; + default: + LOG(fatal) << "Unhandled ChannelsNucleiQA " << static_cast(lightnuclei); + } + + const int charge = (lightnuclei == ChannelsNucleiQA::Helium3) ? 2 : 1; + + const float rigidity = track.tpcInnerParam(); // p / |q| note: here we didn't apply rigidity correction + + const double x = static_cast(charge) * static_cast(rigidity) / mass; + const double expBethe = common::BetheBlochAleph(x, bb0, bb1, bb2, bb3, bb4); + const double expSigma = expBethe * static_cast(relRes); + + if (expSigma <= 0.) { + return -999.f; + } + + return static_cast((track.tpcSignal() - expBethe) / expSigma); } /// PID track cuts (for proton only) /// \param hfTrack is a track /// \return true if the track is compatible with a proton hypothesis - template - uint8_t isSelectedPid(const T& hfTrack) + template + uint8_t isSelectedPid(const T& hfTrack, const TrackWithIts& hfTrackWithIts) { std::array statusPid{TrackSelectorPID::Accepted, TrackSelectorPID::Accepted, TrackSelectorPID::Accepted, TrackSelectorPID::Accepted, TrackSelectorPID::Accepted, TrackSelectorPID::Accepted, TrackSelectorPID::Accepted}; + auto boolToStatus = [](bool passed) { + return passed ? TrackSelectorPID::Accepted : TrackSelectorPID::Rejected; + }; + if constexpr (PidStrategy == ProtonPidStrategy::PidTofOnly) { if (hfTrack.hasTOF()) { for (auto iChannel{0u}; iChannel < ChannelsProtonPid::NChannelsProtonPid; ++iChannel) { statusPid[iChannel] = selectorProton[iChannel].statusTof(hfTrack); } statusPid[ChannelKaonPid] = selectorKaon.statusTof(hfTrack); - statusPid[ChannelsDeuteronPid] = selectorDeuteron.statusTof(hfTrack); - statusPid[ChannelsTritonPid] = selectorTriton.statusTof(hfTrack); - statusPid[ChannelsHeliumPid] = selectorHelium.statusTof(hfTrack); } } if constexpr (PidStrategy == ProtonPidStrategy::PidTpcOnly) { @@ -531,9 +660,9 @@ struct HfTrackIndexSkimCreatorTagSelTracks { statusPid[iChannel] = selectorProton[iChannel].statusTpc(hfTrack); } statusPid[ChannelKaonPid] = selectorKaon.statusTpc(hfTrack); - statusPid[ChannelsDeuteronPid] = selectorDeuteron.statusTpc(hfTrack); - statusPid[ChannelsTritonPid] = selectorTriton.statusTpc(hfTrack); - statusPid[ChannelsHeliumPid] = selectorHelium.statusTpc(hfTrack); + statusPid[ChannelsDeuteronPid] = boolToStatus(applyPidAndTrackSelectionForCharmNuclei(hfTrackWithIts, ChannelsNucleiQA::Deuteron)); + statusPid[ChannelsTritonPid] = boolToStatus(applyPidAndTrackSelectionForCharmNuclei(hfTrackWithIts, ChannelsNucleiQA::Triton)); + statusPid[ChannelsHeliumPid] = boolToStatus(applyPidAndTrackSelectionForCharmNuclei(hfTrackWithIts, ChannelsNucleiQA::Helium3)); } } if constexpr (PidStrategy == ProtonPidStrategy::PidTpcOrTof) { @@ -541,18 +670,18 @@ struct HfTrackIndexSkimCreatorTagSelTracks { statusPid[iChannel] = selectorProton[iChannel].statusTpcOrTof(hfTrack); } statusPid[ChannelKaonPid] = selectorKaon.statusTpcOrTof(hfTrack); - statusPid[ChannelsDeuteronPid] = selectorDeuteron.statusTpcOrTof(hfTrack); - statusPid[ChannelsTritonPid] = selectorTriton.statusTpcOrTof(hfTrack); - statusPid[ChannelsHeliumPid] = selectorHelium.statusTpcOrTof(hfTrack); + statusPid[ChannelsDeuteronPid] = boolToStatus(applyPidAndTrackSelectionForCharmNuclei(hfTrackWithIts, ChannelsNucleiQA::Deuteron)); + statusPid[ChannelsTritonPid] = boolToStatus(applyPidAndTrackSelectionForCharmNuclei(hfTrackWithIts, ChannelsNucleiQA::Triton)); + statusPid[ChannelsHeliumPid] = boolToStatus(applyPidAndTrackSelectionForCharmNuclei(hfTrackWithIts, ChannelsNucleiQA::Helium3)); } if constexpr (PidStrategy == ProtonPidStrategy::PidTpcAndTof) { for (auto iChannel{0u}; iChannel < ChannelsProtonPid::NChannelsProtonPid; ++iChannel) { statusPid[iChannel] = selectorProton[iChannel].statusTpcAndTof(hfTrack); } statusPid[ChannelKaonPid] = selectorKaon.statusTpcAndTof(hfTrack); - statusPid[ChannelsDeuteronPid] = selectorDeuteron.statusTpcAndTof(hfTrack); - statusPid[ChannelsTritonPid] = selectorTriton.statusTpcAndTof(hfTrack); - statusPid[ChannelsHeliumPid] = selectorHelium.statusTpcAndTof(hfTrack); + statusPid[ChannelsDeuteronPid] = boolToStatus(applyPidAndTrackSelectionForCharmNuclei(hfTrackWithIts, ChannelsNucleiQA::Deuteron)); + statusPid[ChannelsTritonPid] = boolToStatus(applyPidAndTrackSelectionForCharmNuclei(hfTrackWithIts, ChannelsNucleiQA::Triton)); + statusPid[ChannelsHeliumPid] = boolToStatus(applyPidAndTrackSelectionForCharmNuclei(hfTrackWithIts, ChannelsNucleiQA::Helium3)); } int8_t flag = BIT(NChannelsPidFor3Prong) - 1; // all bits on (including the kaon one) @@ -981,7 +1110,7 @@ struct HfTrackIndexSkimCreatorTagSelTracks { /// \param pvRefitPvCovMatrixPerTrack is a vector to be filled with PV coordinate covariances after PV refit template void runTagSelTracks(aod::Collision const& collision, - TTracks const&, + TTracks const& tracks, GroupedTrackIndices const& trackIndicesCollision, GroupedPvContributors const& pvContrCollision, aod::BCsWithTimestamps const& bcWithTimeStamps, @@ -990,9 +1119,13 @@ struct HfTrackIndexSkimCreatorTagSelTracks { std::vector>& pvRefitPvCovMatrixPerTrack) { const auto thisCollId = collision.globalIndex(); + auto tracksWithItsPid = soa::Attach(tracks); + for (const auto& trackId : trackIndicesCollision) { int statusProng = BIT(CandidateType::NCandidateTypes) - 1; // all bits on const auto track = trackId.template track_as(); + const auto trackWithItsPid = tracksWithItsPid.rawIteratorAt(trackId.trackId()); + float trackPt = track.pt(); float trackEta = track.eta(); @@ -1046,7 +1179,7 @@ struct HfTrackIndexSkimCreatorTagSelTracks { // } isSelectedTrack(track, trackPt, trackEta, pvRefitDcaXYDcaZ, statusProng); - const int8_t isIdentifiedPid = isSelectedPid(track); + const int8_t isIdentifiedPid = isSelectedPid(track, trackWithItsPid); const bool isPositive = track.sign() > 0; rowSelectedTrack(statusProng, isIdentifiedPid, isPositive); } @@ -1096,7 +1229,7 @@ struct HfTrackIndexSkimCreatorTagSelTracks { fillPvRefitTable(pvRefitDcaPerTrack, pvRefitPvCoordPerTrack, pvRefitPvCovMatrixPerTrack); } } - PROCESS_SWITCH(HfTrackIndexSkimCreatorTagSelTracks, processNoPid, "Process without PID selections", true); + PROCESS_SWITCH(HfTrackIndexSkimCreatorTagSelTracks, processNoPid, "Process without PID selections", false); void processProtonPidTpc(aod::Collisions const& collisions, TrackAssoc const& trackIndices, @@ -1127,7 +1260,7 @@ struct HfTrackIndexSkimCreatorTagSelTracks { fillPvRefitTable(pvRefitDcaPerTrack, pvRefitPvCoordPerTrack, pvRefitPvCovMatrixPerTrack); } } - PROCESS_SWITCH(HfTrackIndexSkimCreatorTagSelTracks, processProtonPidTpc, "Process with proton TPC PID selection", false); + PROCESS_SWITCH(HfTrackIndexSkimCreatorTagSelTracks, processProtonPidTpc, "Process with proton TPC PID selection", true); void processProtonPidTof(aod::Collisions const& collisions, TrackAssoc const& trackIndices, @@ -1302,12 +1435,6 @@ struct HfTrackIndexSkimCreator { Configurable> binsPtDstarToD0Pi{"binsPtDstarToD0Pi", std::vector{hf_cuts_presel_dstar::vecBinsPt}, "pT bin limits for D*+->D0pi pT-dependent cuts"}; Configurable> cutsDstarToD0Pi{"cutsDstarToD0Pi", {hf_cuts_presel_dstar::Cuts[0], hf_cuts_presel_dstar::NBinsPt, hf_cuts_presel_dstar::NCutVars, hf_cuts_presel_dstar::labelsPt, hf_cuts_presel_dstar::labelsCutVar}, "D*+->D0pi selections per pT bin"}; - // CharmNuclei track selection - Configurable> selectionsLightNuclei{"selectionsLightNuclei", {hf_presel_lightnuclei::CutsTrackQuality[0], hf_presel_lightnuclei::NParticleRows, hf_presel_lightnuclei::NVarCuts, hf_presel_lightnuclei::labelsRowsNucleiType, hf_presel_lightnuclei::labelsCutsTrack}, "nuclei track selections for deuteron / triton / helium applied if proper process function enabled"}; - Configurable> tpcPidBBParamsLightNuclei{"tpcPidBBParamsLightNuclei", {hf_presel_lightnuclei::BetheBlochParams[0], hf_presel_lightnuclei::NParticleRows, hf_presel_lightnuclei::NBetheBlochParams, hf_presel_lightnuclei::labelsRowsNucleiType, hf_presel_lightnuclei::labelsBetheBlochParams}, - "TPC PID Bethe–Bloch parameter configurations for light nuclei " - "(deuteron, triton, helium-3), used in BB-based PID when enabled"}; - // proton PID selections for Lc and Xic Configurable applyProtonPidForLcToPKPi{"applyProtonPidForLcToPKPi", false, "Apply proton PID for Lc->pKpi"}; Configurable applyProtonPidForXicToPKPi{"applyProtonPidForXicToPKPi", false, "Apply proton PID for Xic->pKpi"}; @@ -1315,10 +1442,7 @@ struct HfTrackIndexSkimCreator { Configurable applyDeuteronPidForCdToDeKPi{"applyDeuteronPidForCdToDeKPi", false, "Require deuteron PID for Cd->deKpi"}; Configurable applyTritonPidForCtToTrKPi{"applyTritonPidForCtToTrKPi", false, "Require triton PID for Ct->tKpi"}; Configurable applyHeliumPidForChToHeKPi{"applyHeliumPidForChToHeKPi", false, "Require helium3 PID for Ch->heKpi"}; - Configurable applyLightNucleiTpcPidBasedOnBB{"applyLightNucleiTpcPidBasedOnBB", false, "Apply TPC PID for light nuclei using Bethe–Bloch parameterization"}; - // lightnuclei track selection for charmnuclei - Configurable applyNucleiSelcForCharmNuclei{"applyNucleiSelcForCharmNuclei", false, "Require track selection for charm nuclei"}; // ML models for triggers Configurable applyMlForHfFilters{"applyMlForHfFilters", false, "Flag to enable ML application for HF Filters"}; Configurable mlModelPathCCDB{"mlModelPathCCDB", "EventFiltering/PWGHF/BDTSmeared", "Path on CCDB of ML models for HF Filters"}; @@ -1483,9 +1607,7 @@ struct HfTrackIndexSkimCreator { registry.add("hMassCdToDeKPi", "C Deuteron candidates;inv. mass (De K #pi) (GeV/#it{c}^{2});entries", {HistType::kTH1D, {{500, 0., 5.}}}); registry.add("hMassCtToTrKPi", "C Triton candidates;inv. mass (Tr K #pi) (GeV/#it{c}^{2});entries", {HistType::kTH1D, {{500, 0., 5.}}}); registry.add("hMassChToHeKPi", "C Helium3 candidates;inv. mass (He3 K #pi) (GeV/#it{c}^{2});entries", {HistType::kTH1D, {{500, 0., 5.}}}); - if (config.applyNucleiSelcForCharmNuclei && config.applyLightNucleiTpcPidBasedOnBB) { - registry.add("hTPCSignalsLightNuclei", "Light Nuclei TPC signal (a.u.)", {HistType::kTH2D, {{2000, -10., 10.}, {1000, 0., 2000.}}}); - } + // needed for PV refitting if (doprocess2And3ProngsWithPvRefit || doprocess2And3ProngsWithPvRefitWithPidForHfFiltersBdt) { const AxisSpec axisCollisionX{100, -20.f, 20.f, "X (cm)"}; @@ -1661,150 +1783,6 @@ struct HfTrackIndexSkimCreator { } } - /// Apply track-quality (ITS/TPC) + optional ITS-PID preselection for light-nucleus daughters used in charm-nuclei 3-prong channels (Cd/Ct/Ch). - /// \tparam TrackType Track providing ITS/TPC quality accessors. - /// \param track Daughter track to be tested (either prong0 or prong2). - /// \param lightnuclei Species selector: 0=Deuteron, 1=Triton, 2=Helium3. - /// \return true if the track passes all enabled selections. - template - bool applyTrackSelectionForCharmNuclei(const TrackType& track, - ChannelsNucleiQA lightnuclei) - { - // Row index in the selection table: 0 (De), 1 (Tr), 2 (He3) - const int row = static_cast(lightnuclei); - if (row < 0 || row >= NChannelsLightNucleiPid) { - return false; - } - - float itsPidNsigma = -999.f; - - switch (lightnuclei) { - case ChannelsNucleiQA::Deuteron: - itsPidNsigma = track.itsNSigmaDe(); - break; - case ChannelsNucleiQA::Triton: - itsPidNsigma = track.itsNSigmaTr(); - break; - case ChannelsNucleiQA::Helium3: - itsPidNsigma = track.itsNSigmaHe(); - break; - default: - LOG(fatal) << "Unhandled ChannelsNucleiQA " << static_cast(lightnuclei); - } - - // Load cuts for the selected species. - const float itsPidNsigmaMin = config.selectionsLightNuclei->get(row, 0u); - const float itsClusterSizeMin = config.selectionsLightNuclei->get(row, 1u); - const float itsClusterMin = config.selectionsLightNuclei->get(row, 2u); - const float itsIbClusterMin = config.selectionsLightNuclei->get(row, 3u); - const float tpcClusterMin = config.selectionsLightNuclei->get(row, 4u); - const float tpcCrossedRowsMin = config.selectionsLightNuclei->get(row, 5u); - const float tpcCrossedRowsOverFindMin = config.selectionsLightNuclei->get(row, 6u); - const float tpcSharedMax = config.selectionsLightNuclei->get(row, 7u); - const float tpcFracSharedMax = config.selectionsLightNuclei->get(row, 8u); - - // Optional: BB-based TPC nσ selection (only if enabled) - const float tpcBbPidNsigmaMax = config.selectionsLightNuclei->get(row, 9u); - - if (itsPidNsigma < itsPidNsigmaMin) { - return false; - } - if (track.itsClusterSizes() < static_cast(itsClusterSizeMin)) { - return false; - } - if (track.itsNCls() < itsClusterMin) { - return false; - } - if (track.itsNClsInnerBarrel() < itsIbClusterMin) { - return false; - } - if (track.tpcNClsFound() < tpcClusterMin) { - return false; - } - if (track.tpcNClsCrossedRows() < tpcCrossedRowsMin) { - return false; - } - if (track.tpcCrossedRowsOverFindableCls() < tpcCrossedRowsOverFindMin) { - return false; - } - if (track.tpcNClsShared() > tpcSharedMax) { - return false; - } - if (track.tpcFractionSharedCls() > tpcFracSharedMax) { - return false; - } - - if (config.applyLightNucleiTpcPidBasedOnBB) { - const float tpcBbPidNsigma = getTPCnSigmaBB(track, lightnuclei); - if (std::abs(tpcBbPidNsigma) > tpcBbPidNsigmaMax) { - return false; - } - } - - return true; - } - - /// Compute TPC nσ for light nuclei (De/Tr/He3) using a Bethe–Bloch parameter configuration (BB-based PID). - /// - /// \tparam TrackType Track/ASoA row type providing TPC accessors. - /// \param track Track to be tested. - /// \param lightnuclei Species selector: 0=Deuteron, 1=Triton, 2=Helium3. - /// \return TPC nσ for the chosen nucleus hypothesis (or -999 if not applicable). - template - float getTPCnSigmaBB(const TrackType& track, ChannelsNucleiQA lightnuclei) - { - if (!track.hasTPC()) { - return -999.f; - } - - const int row = static_cast(lightnuclei); - if (row < 0 || row >= NChannelsLightNucleiPid) { - return -999.f; - } - - // Columns: [0..4] BB params, [5] relative resolution (sigma/mean) - const double bb0 = config.tpcPidBBParamsLightNuclei->get(row, 0u); - const double bb1 = config.tpcPidBBParamsLightNuclei->get(row, 1u); - const double bb2 = config.tpcPidBBParamsLightNuclei->get(row, 2u); - const double bb3 = config.tpcPidBBParamsLightNuclei->get(row, 3u); - const double bb4 = config.tpcPidBBParamsLightNuclei->get(row, 4u); - const double relRes = config.tpcPidBBParamsLightNuclei->get(row, 5u); - - if (relRes <= 0.f) { - return -999.f; - } - - // Mass/charge hypothesis for the selected nucleus. - double mass = 0.; - switch (lightnuclei) { - case ChannelsNucleiQA::Deuteron: - mass = MassDeuteron; - break; - case ChannelsNucleiQA::Triton: - mass = MassTriton; - break; - case ChannelsNucleiQA::Helium3: - mass = MassHelium3; - break; - default: - LOG(fatal) << "Unhandled ChannelsNucleiQA " << static_cast(lightnuclei); - } - - const int charge = (lightnuclei == ChannelsNucleiQA::Helium3) ? 2 : 1; - - const float rigidity = track.tpcInnerParam(); // p / |q| note: here we didn't apply rigidity correction - - const double x = static_cast(charge) * static_cast(rigidity) / mass; - const double expBethe = common::BetheBlochAleph(x, bb0, bb1, bb2, bb3, bb4); - const double expSigma = expBethe * static_cast(relRes); - - if (expSigma <= 0.) { - return -999.f; - } - - return static_cast((track.tpcSignal() - expBethe) / expSigma); - } - /// Method to perform selections on difference from nominal mass for phi decay /// \param binPt pt bin for the cuts /// \param pVecTrack0 is the momentum array of the first daughter track @@ -1846,8 +1824,8 @@ struct HfTrackIndexSkimCreator { /// \param cutStatus is a 2D array with outcome of each selection (filled only in debug mode) /// \param whichHypo information of the mass hypoteses that were selected /// \param isSelected is a bitmap with selection outcome - template - void applyPreselection3Prong(T1 const& track0, T1 const& track2, T2 const& pVecTrack0, T2 const& pVecTrack1, T2 const& pVecTrack2, const auto isIdentifiedPidTrack0, const auto isIdentifiedPidTrack2, T3& cutStatus, T4& whichHypo, auto& isSelected) + template + void applyPreselection3Prong(T2 const& pVecTrack0, T2 const& pVecTrack1, T2 const& pVecTrack2, const auto isIdentifiedPidTrack0, const auto isIdentifiedPidTrack2, T3& cutStatus, T4& whichHypo, auto& isSelected) { const auto pt = RecoDecay::pt(pVecTrack0, pVecTrack1, pVecTrack2) + config.ptTolerance; // add tolerance because of no reco decay vertex @@ -1855,50 +1833,6 @@ struct HfTrackIndexSkimCreator { whichHypo[iDecay3P] = 3; // 2 bits on - if (config.applyNucleiSelcForCharmNuclei && - (iDecay3P == hf_cand_3prong::DecayType::CdToDeKPi || - iDecay3P == hf_cand_3prong::DecayType::CtToTrKPi || - iDecay3P == hf_cand_3prong::DecayType::ChToHeKPi)) { - - ChannelsNucleiQA nucleiType; - - switch (iDecay3P) { - case hf_cand_3prong::DecayType::CdToDeKPi: - nucleiType = ChannelsNucleiQA::Deuteron; - break; - case hf_cand_3prong::DecayType::CtToTrKPi: - nucleiType = ChannelsNucleiQA::Triton; - break; - case hf_cand_3prong::DecayType::ChToHeKPi: - nucleiType = ChannelsNucleiQA::Helium3; - break; - default: - LOG(fatal) << "Unhandled DecayType " << static_cast(iDecay3P); - continue; - } - - // hypo0: nucleus on track0 - if (!applyTrackSelectionForCharmNuclei(track0, nucleiType)) { - CLRBIT(whichHypo[iDecay3P], 0); - } else { - registry.fill(HIST("hTPCSignalsLightNuclei"), track0.tpcInnerParam() * track0.sign(), track0.tpcSignal()); - } - // hypo1: nucleus on track2 - if (!applyTrackSelectionForCharmNuclei(track2, nucleiType)) { - CLRBIT(whichHypo[iDecay3P], 1); - } else { - registry.fill(HIST("hTPCSignalsLightNuclei"), track2.tpcInnerParam() * track2.sign(), track2.tpcSignal()); - } - - if (whichHypo[iDecay3P] == 0) { - CLRBIT(isSelected, iDecay3P); - if (config.debug) { - cutStatus[iDecay3P][hf_cuts_presel_3prong::NCutVars + 1] = false; // nuclei track QA slot - } - continue; - } - } - // check proton PID for 3prongs if ((iDecay3P == hf_cand_3prong::DecayType::LcToPKPi && config.applyProtonPidForLcToPKPi) || (iDecay3P == hf_cand_3prong::DecayType::XicToPKPi && config.applyProtonPidForXicToPKPi) || (iDecay3P == hf_cand_3prong::DecayType::CdToDeKPi && config.applyDeuteronPidForCdToDeKPi) || (iDecay3P == hf_cand_3prong::DecayType::CtToTrKPi && config.applyTritonPidForCtToTrKPi) || (iDecay3P == hf_cand_3prong::DecayType::ChToHeKPi && config.applyHeliumPidForChToHeKPi)) { @@ -2417,14 +2351,12 @@ struct HfTrackIndexSkimCreator { //} const auto thisCollId = collision.globalIndex(); - auto tracksWithItsPid = soa::Attach(tracks); // first loop over positive tracks const auto groupedTrackIndicesPos1 = positiveFor2And3Prongs->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); int lastFilledD0 = -1; // index to be filled in table for D* mesons for (auto trackIndexPos1 = groupedTrackIndicesPos1.begin(); trackIndexPos1 != groupedTrackIndicesPos1.end(); ++trackIndexPos1) { const auto trackPos1 = trackIndexPos1.template track_as(); - const auto trackPosWithItsPid1 = tracksWithItsPid.rawIteratorAt(trackIndexPos1.trackId()); // retrieve the selection flag that corresponds to this collision const auto isSelProngPos1 = trackIndexPos1.isSelProng(); @@ -2443,7 +2375,6 @@ struct HfTrackIndexSkimCreator { const auto groupedTrackIndicesNeg1 = negativeFor2And3Prongs->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); for (auto trackIndexNeg1 = groupedTrackIndicesNeg1.begin(); trackIndexNeg1 != groupedTrackIndicesNeg1.end(); ++trackIndexNeg1) { const auto trackNeg1 = trackIndexNeg1.template track_as(); - const auto trackNegWithItsPid1 = tracksWithItsPid.rawIteratorAt(trackIndexNeg1.trackId()); // retrieve the selection flag that corresponds to this collision const auto isSelProngNeg1 = trackIndexNeg1.isSelProng(); @@ -2683,7 +2614,6 @@ struct HfTrackIndexSkimCreator { } const auto trackPos2 = trackIndexPos2.template track_as(); - const auto trackPosWithItsPid2 = tracksWithItsPid.rawIteratorAt(trackIndexPos2.trackId()); auto trackParVarPos2 = getTrackParCov(trackPos2); std::array dcaInfoPos2{trackPos2.dcaXY(), trackPos2.dcaZ()}; @@ -2707,7 +2637,7 @@ struct HfTrackIndexSkimCreator { // 3-prong preselections const auto isIdentifiedPidTrackPos1 = trackIndexPos1.isIdentifiedPid(); const auto isIdentifiedPidTrackPos2 = trackIndexPos2.isIdentifiedPid(); - applyPreselection3Prong(trackPosWithItsPid1, trackPosWithItsPid2, pVecTrackPos1, pVecTrackNeg1, pVecTrackPos2, isIdentifiedPidTrackPos1, isIdentifiedPidTrackPos2, cutStatus3Prong, whichHypo3Prong, isSelected3ProngCand); + applyPreselection3Prong(pVecTrackPos1, pVecTrackNeg1, pVecTrackPos2, isIdentifiedPidTrackPos1, isIdentifiedPidTrackPos2, cutStatus3Prong, whichHypo3Prong, isSelected3ProngCand); if (!config.debug && isSelected3ProngCand == 0) { continue; } @@ -2953,7 +2883,6 @@ struct HfTrackIndexSkimCreator { } auto trackNeg2 = trackIndexNeg2.template track_as(); - const auto trackNegWithItsPid2 = tracksWithItsPid.rawIteratorAt(trackIndexNeg2.trackId()); auto trackParVarNeg2 = getTrackParCov(trackNeg2); std::array dcaInfoNeg2{trackNeg2.dcaXY(), trackNeg2.dcaZ()}; @@ -2976,7 +2905,7 @@ struct HfTrackIndexSkimCreator { // 3-prong preselections int8_t const isIdentifiedPidTrackNeg1 = trackIndexNeg1.isIdentifiedPid(); int8_t const isIdentifiedPidTrackNeg2 = trackIndexNeg2.isIdentifiedPid(); - applyPreselection3Prong(trackNegWithItsPid1, trackNegWithItsPid2, pVecTrackNeg1, pVecTrackPos1, pVecTrackNeg2, isIdentifiedPidTrackNeg1, isIdentifiedPidTrackNeg2, cutStatus3Prong, whichHypo3Prong, isSelected3ProngCand); + applyPreselection3Prong(pVecTrackNeg1, pVecTrackPos1, pVecTrackNeg2, isIdentifiedPidTrackNeg1, isIdentifiedPidTrackNeg2, cutStatus3Prong, whichHypo3Prong, isSelected3ProngCand); if (!config.debug && isSelected3ProngCand == 0) { continue; } From e117d6513cf8a4c8d5954f1562ba875b9dce005f Mon Sep 17 00:00:00 2001 From: BiaoZhang Date: Sun, 8 Feb 2026 18:38:49 +0100 Subject: [PATCH 09/14] fix the changes related to default setting --- PWGHF/TableProducer/trackIndexSkimCreator.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PWGHF/TableProducer/trackIndexSkimCreator.cxx b/PWGHF/TableProducer/trackIndexSkimCreator.cxx index bd14819977c..de318d23ed9 100644 --- a/PWGHF/TableProducer/trackIndexSkimCreator.cxx +++ b/PWGHF/TableProducer/trackIndexSkimCreator.cxx @@ -1229,7 +1229,7 @@ struct HfTrackIndexSkimCreatorTagSelTracks { fillPvRefitTable(pvRefitDcaPerTrack, pvRefitPvCoordPerTrack, pvRefitPvCovMatrixPerTrack); } } - PROCESS_SWITCH(HfTrackIndexSkimCreatorTagSelTracks, processNoPid, "Process without PID selections", false); + PROCESS_SWITCH(HfTrackIndexSkimCreatorTagSelTracks, processNoPid, "Process without PID selections", true); void processProtonPidTpc(aod::Collisions const& collisions, TrackAssoc const& trackIndices, @@ -1260,7 +1260,7 @@ struct HfTrackIndexSkimCreatorTagSelTracks { fillPvRefitTable(pvRefitDcaPerTrack, pvRefitPvCoordPerTrack, pvRefitPvCovMatrixPerTrack); } } - PROCESS_SWITCH(HfTrackIndexSkimCreatorTagSelTracks, processProtonPidTpc, "Process with proton TPC PID selection", true); + PROCESS_SWITCH(HfTrackIndexSkimCreatorTagSelTracks, processProtonPidTpc, "Process with proton TPC PID selection", false); void processProtonPidTof(aod::Collisions const& collisions, TrackAssoc const& trackIndices, From 35a3f6992399f4205fd866ff8ad6afe0ccd7f174 Mon Sep 17 00:00:00 2001 From: BiaoZhang Date: Mon, 9 Feb 2026 17:46:36 +0100 Subject: [PATCH 10/14] Add the case for alpha into the skimming task --- PWGHF/Core/SelectorCuts.h | 6 +- PWGHF/DataModel/TrackIndexSkimmingTables.h | 1 + PWGHF/TableProducer/trackIndexSkimCreator.cxx | 63 ++++++++++++++----- 3 files changed, 52 insertions(+), 18 deletions(-) diff --git a/PWGHF/Core/SelectorCuts.h b/PWGHF/Core/SelectorCuts.h index 32107f05a39..a1bdfa0265f 100644 --- a/PWGHF/Core/SelectorCuts.h +++ b/PWGHF/Core/SelectorCuts.h @@ -81,19 +81,21 @@ static const std::vector labelsRowsPid = {"ProtonInLcToPKPi", "Prot namespace hf_presel_lightnuclei { -static constexpr int NParticleRows = 3; // number of particles / rows +static constexpr int NParticleRows = 4; // number of particles / rows static constexpr int NVarCuts = 10; // number of cuts for each particles static constexpr int NBetheBlochParams = 6; // number of parameters for Bethe-Bloch // default values for the track cuts for lightnuclei in the track-index-skim-creator constexpr float CutsTrackQuality[NParticleRows][NVarCuts] = {{-4.f, 3.f, 5.f, 0.f, 100.f, 100.f, 0.83, 160.f, 1.f, 5.f}, + {-4.f, 3.f, 5.f, 0.f, 100.f, 100.f, 0.83, 160.f, 1.f, 5.f}, {-4.f, 3.f, 5.f, 0.f, 100.f, 100.f, 0.83, 160.f, 1.f, 5.f}, {-4.f, 3.f, 5.f, 0.f, 100.f, 100.f, 0.83, 160.f, 1.f, 5.f}}; static const std::vector labelsCutsTrack = {"nSigmaMinIts", "minItsClusterSizes", "minItsCluster", "minItsIbCluster", "minTpcCluster", "minTpcRow", "minTpcCrossedOverFound", "maxTpcShared", "maxTpcFracShared", "maxTPCnSigmaBB"}; -static const std::vector labelsRowsNucleiType = {"Deutron", "Triton", "Helium3"}; +static const std::vector labelsRowsNucleiType = {"Deutron", "Triton", "Helium3", "Alpha"}; constexpr float BetheBlochParams[NParticleRows][NBetheBlochParams] = {{5.39302, 7.859534, 0.004048, 2.323197, 1.609307, 0.09}, {5.39302, 7.859534, 0.004048, 2.323197, 1.609307, 0.09}, + {-126.55736, -0.858569, 1.11164, 1.21032, 2.656374, 0.09}, {-126.55736, -0.858569, 1.11164, 1.21032, 2.656374, 0.09}}; static const std::vector labelsBetheBlochParams = {"p0", "p1", "p2", "p3", "p4", "resolution"}; diff --git a/PWGHF/DataModel/TrackIndexSkimmingTables.h b/PWGHF/DataModel/TrackIndexSkimmingTables.h index 90183815170..c21c4bf3ed8 100644 --- a/PWGHF/DataModel/TrackIndexSkimmingTables.h +++ b/PWGHF/DataModel/TrackIndexSkimmingTables.h @@ -301,6 +301,7 @@ enum DecayType { CdToDeKPi, CtToTrKPi, ChToHeKPi, + CaToAlKPi, N3ProngDecays }; } // namespace hf_cand_3prong diff --git a/PWGHF/TableProducer/trackIndexSkimCreator.cxx b/PWGHF/TableProducer/trackIndexSkimCreator.cxx index de318d23ed9..f27eb08a0ec 100644 --- a/PWGHF/TableProducer/trackIndexSkimCreator.cxx +++ b/PWGHF/TableProducer/trackIndexSkimCreator.cxx @@ -133,6 +133,7 @@ enum ChannelsLightNucleiPid { CdToDeKPi = 0, CtToTrKPi, ChToHeKPi, + CaToAlKPi, NChannelsLightNucleiPid }; @@ -141,12 +142,14 @@ constexpr int ChannelKaonPid = ChannelsProtonPid::NChannelsProtonPid; constexpr int ChannelsDeuteronPid = ChannelsProtonPid::NChannelsProtonPid + 1; constexpr int ChannelsTritonPid = ChannelsProtonPid::NChannelsProtonPid + 2; constexpr int ChannelsHeliumPid = ChannelsProtonPid::NChannelsProtonPid + 3; +constexpr int ChannelsAlphaPid = ChannelsProtonPid::NChannelsProtonPid + 4; constexpr int NChannelsPidFor3Prong = static_cast(ChannelsProtonPid::NChannelsProtonPid) + static_cast(ChannelsKaonPid::NChannelsKaonPid) + static_cast(ChannelsLightNucleiPid::NChannelsLightNucleiPid); enum class ChannelsNucleiQA : int { Deuteron = 0, Triton = 1, - Helium3 = 2 + Helium3 = 2, + Alpha = 3 }; /// Event selection struct HfTrackIndexSkimCreatorTagSelCollisions { @@ -520,6 +523,9 @@ struct HfTrackIndexSkimCreatorTagSelTracks { case ChannelsNucleiQA::Helium3: nSigmaIts = track.itsNSigmaHe(); break; + case ChannelsNucleiQA::Alpha: + nSigmaIts = track.itsNSigmaAl(); + break; default: LOG(fatal) << "Unhandled ChannelsNucleiQA " << static_cast(lightnuclei); } @@ -616,12 +622,14 @@ struct HfTrackIndexSkimCreatorTagSelTracks { case ChannelsNucleiQA::Helium3: mass = MassHelium3; break; + case ChannelsNucleiQA::Alpha: + mass = MassAlpha; + break; default: LOG(fatal) << "Unhandled ChannelsNucleiQA " << static_cast(lightnuclei); } - const int charge = (lightnuclei == ChannelsNucleiQA::Helium3) ? 2 : 1; - + const int charge = (lightnuclei == ChannelsNucleiQA::Helium3 || lightnuclei == ChannelsNucleiQA::Alpha) ? 2 : 1; const float rigidity = track.tpcInnerParam(); // p / |q| note: here we didn't apply rigidity correction const double x = static_cast(charge) * static_cast(rigidity) / mass; @@ -641,7 +649,7 @@ struct HfTrackIndexSkimCreatorTagSelTracks { template uint8_t isSelectedPid(const T& hfTrack, const TrackWithIts& hfTrackWithIts) { - std::array statusPid{TrackSelectorPID::Accepted, TrackSelectorPID::Accepted, TrackSelectorPID::Accepted, TrackSelectorPID::Accepted, TrackSelectorPID::Accepted, TrackSelectorPID::Accepted, TrackSelectorPID::Accepted}; + std::array statusPid{TrackSelectorPID::Accepted, TrackSelectorPID::Accepted, TrackSelectorPID::Accepted, TrackSelectorPID::Accepted, TrackSelectorPID::Accepted, TrackSelectorPID::Accepted, TrackSelectorPID::Accepted, TrackSelectorPID::Accepted}; auto boolToStatus = [](bool passed) { return passed ? TrackSelectorPID::Accepted : TrackSelectorPID::Rejected; }; @@ -663,6 +671,7 @@ struct HfTrackIndexSkimCreatorTagSelTracks { statusPid[ChannelsDeuteronPid] = boolToStatus(applyPidAndTrackSelectionForCharmNuclei(hfTrackWithIts, ChannelsNucleiQA::Deuteron)); statusPid[ChannelsTritonPid] = boolToStatus(applyPidAndTrackSelectionForCharmNuclei(hfTrackWithIts, ChannelsNucleiQA::Triton)); statusPid[ChannelsHeliumPid] = boolToStatus(applyPidAndTrackSelectionForCharmNuclei(hfTrackWithIts, ChannelsNucleiQA::Helium3)); + statusPid[ChannelsAlphaPid] = boolToStatus(applyPidAndTrackSelectionForCharmNuclei(hfTrackWithIts, ChannelsNucleiQA::Alpha)); } } if constexpr (PidStrategy == ProtonPidStrategy::PidTpcOrTof) { @@ -673,6 +682,7 @@ struct HfTrackIndexSkimCreatorTagSelTracks { statusPid[ChannelsDeuteronPid] = boolToStatus(applyPidAndTrackSelectionForCharmNuclei(hfTrackWithIts, ChannelsNucleiQA::Deuteron)); statusPid[ChannelsTritonPid] = boolToStatus(applyPidAndTrackSelectionForCharmNuclei(hfTrackWithIts, ChannelsNucleiQA::Triton)); statusPid[ChannelsHeliumPid] = boolToStatus(applyPidAndTrackSelectionForCharmNuclei(hfTrackWithIts, ChannelsNucleiQA::Helium3)); + statusPid[ChannelsAlphaPid] = boolToStatus(applyPidAndTrackSelectionForCharmNuclei(hfTrackWithIts, ChannelsNucleiQA::Alpha)); } if constexpr (PidStrategy == ProtonPidStrategy::PidTpcAndTof) { for (auto iChannel{0u}; iChannel < ChannelsProtonPid::NChannelsProtonPid; ++iChannel) { @@ -682,6 +692,7 @@ struct HfTrackIndexSkimCreatorTagSelTracks { statusPid[ChannelsDeuteronPid] = boolToStatus(applyPidAndTrackSelectionForCharmNuclei(hfTrackWithIts, ChannelsNucleiQA::Deuteron)); statusPid[ChannelsTritonPid] = boolToStatus(applyPidAndTrackSelectionForCharmNuclei(hfTrackWithIts, ChannelsNucleiQA::Triton)); statusPid[ChannelsHeliumPid] = boolToStatus(applyPidAndTrackSelectionForCharmNuclei(hfTrackWithIts, ChannelsNucleiQA::Helium3)); + statusPid[ChannelsAlphaPid] = boolToStatus(applyPidAndTrackSelectionForCharmNuclei(hfTrackWithIts, ChannelsNucleiQA::Alpha)); } int8_t flag = BIT(NChannelsPidFor3Prong) - 1; // all bits on (including the kaon one) @@ -1119,7 +1130,7 @@ struct HfTrackIndexSkimCreatorTagSelTracks { std::vector>& pvRefitPvCovMatrixPerTrack) { const auto thisCollId = collision.globalIndex(); - auto tracksWithItsPid = soa::Attach(tracks); + auto tracksWithItsPid = soa::Attach(tracks); for (const auto& trackId : trackIndicesCollision) { int statusProng = BIT(CandidateType::NCandidateTypes) - 1; // all bits on @@ -1431,6 +1442,9 @@ struct HfTrackIndexSkimCreator { // Ch cuts Configurable> binsPtChToHeKPi{"binsPtChToHeKPi", std::vector{hf_cuts_presel_3prong::vecBinsPt}, "pT bin limits for Ch->heKpi pT-dependent cuts"}; Configurable> cutsChToHeKPi{"cutsChToHeKPi", {hf_cuts_presel_3prong::Cuts[0], hf_cuts_presel_3prong::NBinsPt, hf_cuts_presel_3prong::NCutVars, hf_cuts_presel_3prong::labelsPt, hf_cuts_presel_3prong::labelsCutVar}, "Ch->heKpi selections per pT bin"}; + // Ca cuts + Configurable> binsPtCaToAlKPi{"binsPtCaToAlKPi", std::vector{hf_cuts_presel_3prong::vecBinsPt}, "pT bin limits for Ca->alKpi pT-dependent cuts"}; + Configurable> cutsCaToAlKPi{"cutsCaToAlKPi", {hf_cuts_presel_3prong::Cuts[0], hf_cuts_presel_3prong::NBinsPt, hf_cuts_presel_3prong::NCutVars, hf_cuts_presel_3prong::labelsPt, hf_cuts_presel_3prong::labelsCutVar}, "Ca->alKpi selections per pT bin"}; // D*+ cuts Configurable> binsPtDstarToD0Pi{"binsPtDstarToD0Pi", std::vector{hf_cuts_presel_dstar::vecBinsPt}, "pT bin limits for D*+->D0pi pT-dependent cuts"}; Configurable> cutsDstarToD0Pi{"cutsDstarToD0Pi", {hf_cuts_presel_dstar::Cuts[0], hf_cuts_presel_dstar::NBinsPt, hf_cuts_presel_dstar::NCutVars, hf_cuts_presel_dstar::labelsPt, hf_cuts_presel_dstar::labelsCutVar}, "D*+->D0pi selections per pT bin"}; @@ -1442,6 +1456,7 @@ struct HfTrackIndexSkimCreator { Configurable applyDeuteronPidForCdToDeKPi{"applyDeuteronPidForCdToDeKPi", false, "Require deuteron PID for Cd->deKpi"}; Configurable applyTritonPidForCtToTrKPi{"applyTritonPidForCtToTrKPi", false, "Require triton PID for Ct->tKpi"}; Configurable applyHeliumPidForChToHeKPi{"applyHeliumPidForChToHeKPi", false, "Require helium3 PID for Ch->heKpi"}; + Configurable applyHeliumPidForCaToAlKPi{"applyHeliumPidForCaToAlKPi", false, "Require Alpha PID for Ca->AlKpi"}; // ML models for triggers Configurable applyMlForHfFilters{"applyMlForHfFilters", false, "Flag to enable ML application for HF Filters"}; @@ -1469,12 +1484,12 @@ struct HfTrackIndexSkimCreator { // int nColls{0}; //can be added to run over limited collisions per file - for tesing purposes - static constexpr int kN2ProngDecays = hf_cand_2prong::DecayType::N2ProngDecays; // number of 2-prong hadron types - static constexpr int kN3ProngDecays = hf_cand_3prong::DecayType::N3ProngDecays; // number of 3-prong hadron types - static constexpr int kNCuts2Prong[kN2ProngDecays] = {hf_cuts_presel_2prong::NCutVars, hf_cuts_presel_2prong::NCutVars, hf_cuts_presel_2prong::NCutVars}; // how many different selections are made on 2-prongs - static constexpr int kNCuts3Prong[kN3ProngDecays] = {hf_cuts_presel_3prong::NCutVars, hf_cuts_presel_3prong::NCutVars + 1, hf_cuts_presel_ds::NCutVars, hf_cuts_presel_3prong::NCutVars + 1, hf_cuts_presel_3prong::NCutVars + 2, hf_cuts_presel_3prong::NCutVars + 2, hf_cuts_presel_3prong::NCutVars + 2}; // how many different selections are made on 3-prongs (Lc, Xic and CharmNuclei have also PID potentially, charmnuclei has also daughter track quality cut potentially) - static constexpr int kNCutsDstar = 3; // how many different selections are made on Dstars - static constexpr int kN3ProngDecaysUsedMlForHfFilters = kN3ProngDecays - NChannelsLightNucleiPid; // number of 3-prong HF decays using ML filters + static constexpr int kN2ProngDecays = hf_cand_2prong::DecayType::N2ProngDecays; // number of 2-prong hadron types + static constexpr int kN3ProngDecays = hf_cand_3prong::DecayType::N3ProngDecays; // number of 3-prong hadron types + static constexpr int kNCuts2Prong[kN2ProngDecays] = {hf_cuts_presel_2prong::NCutVars, hf_cuts_presel_2prong::NCutVars, hf_cuts_presel_2prong::NCutVars}; // how many different selections are made on 2-prongs + static constexpr int kNCuts3Prong[kN3ProngDecays] = {hf_cuts_presel_3prong::NCutVars, hf_cuts_presel_3prong::NCutVars + 1, hf_cuts_presel_ds::NCutVars, hf_cuts_presel_3prong::NCutVars + 1, hf_cuts_presel_3prong::NCutVars + 2, hf_cuts_presel_3prong::NCutVars + 2, hf_cuts_presel_3prong::NCutVars + 2, hf_cuts_presel_3prong::NCutVars + 2}; // how many different selections are made on 3-prongs (Lc, Xic and CharmNuclei have also PID potentially, charmnuclei has also daughter track quality cut potentially) + static constexpr int kNCutsDstar = 3; // how many different selections are made on Dstars + static constexpr int kN3ProngDecaysUsedMlForHfFilters = kN3ProngDecays - NChannelsLightNucleiPid; // number of 3-prong HF decays using ML filters std::array, 2>, kN2ProngDecays> arrMass2Prong{}; std::array, 2>, kN3ProngDecays> arrMass3Prong{}; // arrays of 2-prong and 3-prong cuts @@ -1551,12 +1566,15 @@ struct HfTrackIndexSkimCreator { arrMass3Prong[hf_cand_3prong::DecayType::ChToHeKPi] = std::array{std::array{MassHelium3, MassKPlus, MassPiPlus}, std::array{MassPiPlus, MassKPlus, MassHelium3}}; + arrMass3Prong[hf_cand_3prong::DecayType::CaToAlKPi] = std::array{std::array{MassAlpha, MassKPlus, MassPiPlus}, + std::array{MassPiPlus, MassKPlus, MassAlpha}}; + // cuts for 2-prong decays retrieved by json. the order must be then one in hf_cand_2prong::DecayType cut2Prong = {config.cutsD0ToPiK, config.cutsJpsiToEE, config.cutsJpsiToMuMu}; binsPt2Prong = {config.binsPtD0ToPiK, config.binsPtJpsiToEE, config.binsPtJpsiToMuMu}; // cuts for 3-prong decays retrieved by json. the order must be then one in hf_cand_3prong::DecayType - cut3Prong = {config.cutsDplusToPiKPi, config.cutsLcToPKPi, config.cutsDsToKKPi, config.cutsXicToPKPi, config.cutsCdToDeKPi, config.cutsCtToTrKPi, config.cutsChToHeKPi}; - binsPt3Prong = {config.binsPtDplusToPiKPi, config.binsPtLcToPKPi, config.binsPtDsToKKPi, config.binsPtXicToPKPi, config.binsPtCdToDeKPi, config.binsPtCtToTrKPi, config.binsPtChToHeKPi}; + cut3Prong = {config.cutsDplusToPiKPi, config.cutsLcToPKPi, config.cutsDsToKKPi, config.cutsXicToPKPi, config.cutsCdToDeKPi, config.cutsCtToTrKPi, config.cutsChToHeKPi, config.cutsCaToAlKPi}; + binsPt3Prong = {config.binsPtDplusToPiKPi, config.binsPtLcToPKPi, config.binsPtDsToKKPi, config.binsPtXicToPKPi, config.binsPtCdToDeKPi, config.binsPtCtToTrKPi, config.binsPtChToHeKPi, config.binsPtCaToAlKPi}; df2.setPropagateToPCA(config.propagateToPCA); df2.setMaxR(config.maxR); @@ -1607,6 +1625,7 @@ struct HfTrackIndexSkimCreator { registry.add("hMassCdToDeKPi", "C Deuteron candidates;inv. mass (De K #pi) (GeV/#it{c}^{2});entries", {HistType::kTH1D, {{500, 0., 5.}}}); registry.add("hMassCtToTrKPi", "C Triton candidates;inv. mass (Tr K #pi) (GeV/#it{c}^{2});entries", {HistType::kTH1D, {{500, 0., 5.}}}); registry.add("hMassChToHeKPi", "C Helium3 candidates;inv. mass (He3 K #pi) (GeV/#it{c}^{2});entries", {HistType::kTH1D, {{500, 0., 5.}}}); + registry.add("hMassCaToAlKPi", "C Alpha candidates;inv. mass (Alpha K #pi) (GeV/#it{c}^{2});entries", {HistType::kTH1D, {{500, 2., 7.}}}); // needed for PV refitting if (doprocess2And3ProngsWithPvRefit || doprocess2And3ProngsWithPvRefitWithPidForHfFiltersBdt) { @@ -1834,12 +1853,12 @@ struct HfTrackIndexSkimCreator { whichHypo[iDecay3P] = 3; // 2 bits on // check proton PID for 3prongs - if ((iDecay3P == hf_cand_3prong::DecayType::LcToPKPi && config.applyProtonPidForLcToPKPi) || (iDecay3P == hf_cand_3prong::DecayType::XicToPKPi && config.applyProtonPidForXicToPKPi) || (iDecay3P == hf_cand_3prong::DecayType::CdToDeKPi && config.applyDeuteronPidForCdToDeKPi) || (iDecay3P == hf_cand_3prong::DecayType::CtToTrKPi && config.applyTritonPidForCtToTrKPi) || (iDecay3P == hf_cand_3prong::DecayType::ChToHeKPi && config.applyHeliumPidForChToHeKPi)) { + if ((iDecay3P == hf_cand_3prong::DecayType::LcToPKPi && config.applyProtonPidForLcToPKPi) || (iDecay3P == hf_cand_3prong::DecayType::XicToPKPi && config.applyProtonPidForXicToPKPi) || (iDecay3P == hf_cand_3prong::DecayType::CdToDeKPi && config.applyDeuteronPidForCdToDeKPi) || (iDecay3P == hf_cand_3prong::DecayType::CtToTrKPi && config.applyTritonPidForCtToTrKPi) || (iDecay3P == hf_cand_3prong::DecayType::ChToHeKPi && config.applyHeliumPidForChToHeKPi) || (iDecay3P == hf_cand_3prong::DecayType::CaToAlKPi && config.applyHeliumPidForCaToAlKPi)) { - if ((iDecay3P == hf_cand_3prong::DecayType::LcToPKPi && !TESTBIT(isIdentifiedPidTrack0, ChannelsProtonPid::LcToPKPi)) || (iDecay3P == hf_cand_3prong::DecayType::XicToPKPi && !TESTBIT(isIdentifiedPidTrack0, ChannelsProtonPid::XicToPKPi)) || (iDecay3P == hf_cand_3prong::DecayType::CdToDeKPi && !TESTBIT(isIdentifiedPidTrack0, ChannelsDeuteronPid)) || (iDecay3P == hf_cand_3prong::DecayType::CtToTrKPi && !TESTBIT(isIdentifiedPidTrack0, ChannelsTritonPid)) || (iDecay3P == hf_cand_3prong::DecayType::ChToHeKPi && !TESTBIT(isIdentifiedPidTrack0, ChannelsHeliumPid))) { + if ((iDecay3P == hf_cand_3prong::DecayType::LcToPKPi && !TESTBIT(isIdentifiedPidTrack0, ChannelsProtonPid::LcToPKPi)) || (iDecay3P == hf_cand_3prong::DecayType::XicToPKPi && !TESTBIT(isIdentifiedPidTrack0, ChannelsProtonPid::XicToPKPi)) || (iDecay3P == hf_cand_3prong::DecayType::CdToDeKPi && !TESTBIT(isIdentifiedPidTrack0, ChannelsDeuteronPid)) || (iDecay3P == hf_cand_3prong::DecayType::CtToTrKPi && !TESTBIT(isIdentifiedPidTrack0, ChannelsTritonPid)) || (iDecay3P == hf_cand_3prong::DecayType::ChToHeKPi && !TESTBIT(isIdentifiedPidTrack0, ChannelsHeliumPid)) || (iDecay3P == hf_cand_3prong::DecayType::CaToAlKPi && !TESTBIT(isIdentifiedPidTrack0, ChannelsAlphaPid))) { CLRBIT(whichHypo[iDecay3P], 0); } - if ((iDecay3P == hf_cand_3prong::DecayType::LcToPKPi && !TESTBIT(isIdentifiedPidTrack2, ChannelsProtonPid::LcToPKPi)) || (iDecay3P == hf_cand_3prong::DecayType::XicToPKPi && !TESTBIT(isIdentifiedPidTrack2, ChannelsProtonPid::XicToPKPi)) || (iDecay3P == hf_cand_3prong::DecayType::CdToDeKPi && !TESTBIT(isIdentifiedPidTrack2, ChannelsDeuteronPid)) || (iDecay3P == hf_cand_3prong::DecayType::CtToTrKPi && !TESTBIT(isIdentifiedPidTrack2, ChannelsTritonPid)) || (iDecay3P == hf_cand_3prong::DecayType::ChToHeKPi && !TESTBIT(isIdentifiedPidTrack2, ChannelsHeliumPid))) { + if ((iDecay3P == hf_cand_3prong::DecayType::LcToPKPi && !TESTBIT(isIdentifiedPidTrack2, ChannelsProtonPid::LcToPKPi)) || (iDecay3P == hf_cand_3prong::DecayType::XicToPKPi && !TESTBIT(isIdentifiedPidTrack2, ChannelsProtonPid::XicToPKPi)) || (iDecay3P == hf_cand_3prong::DecayType::CdToDeKPi && !TESTBIT(isIdentifiedPidTrack2, ChannelsDeuteronPid)) || (iDecay3P == hf_cand_3prong::DecayType::CtToTrKPi && !TESTBIT(isIdentifiedPidTrack2, ChannelsTritonPid)) || (iDecay3P == hf_cand_3prong::DecayType::ChToHeKPi && !TESTBIT(isIdentifiedPidTrack2, ChannelsHeliumPid)) || (iDecay3P == hf_cand_3prong::DecayType::CaToAlKPi && !TESTBIT(isIdentifiedPidTrack2, ChannelsAlphaPid))) { CLRBIT(whichHypo[iDecay3P], 1); } if (whichHypo[iDecay3P] == 0) { @@ -2834,6 +2853,9 @@ struct HfTrackIndexSkimCreator { case hf_cand_3prong::DecayType::ChToHeKPi: registry.fill(HIST("hMassChToHeKPi"), mass3Prong); break; + case hf_cand_3prong::DecayType::CaToAlKPi: + registry.fill(HIST("hMassCaToAlKPi"), mass3Prong); + break; } } if (TESTBIT(whichHypo3Prong[iDecay3P], 1)) { @@ -2857,6 +2879,9 @@ struct HfTrackIndexSkimCreator { case hf_cand_3prong::DecayType::ChToHeKPi: registry.fill(HIST("hMassChToHeKPi"), mass3Prong); break; + case hf_cand_3prong::DecayType::CaToAlKPi: + registry.fill(HIST("hMassCaToAlKPi"), mass3Prong); + break; } } } @@ -3103,6 +3128,9 @@ struct HfTrackIndexSkimCreator { case hf_cand_3prong::DecayType::ChToHeKPi: registry.fill(HIST("hMassChToHeKPi"), mass3Prong); break; + case hf_cand_3prong::DecayType::CaToAlKPi: + registry.fill(HIST("hMassCaToAlKPi"), mass3Prong); + break; } } if (TESTBIT(whichHypo3Prong[iDecay3P], 1)) { @@ -3126,6 +3154,9 @@ struct HfTrackIndexSkimCreator { case hf_cand_3prong::DecayType::ChToHeKPi: registry.fill(HIST("hMassChToHeKPi"), mass3Prong); break; + case hf_cand_3prong::DecayType::CaToAlKPi: + registry.fill(HIST("hMassCaToAlKPi"), mass3Prong); + break; } } } From 0bb1f6f25a74b2173483b84bc7b4d9ee3f155e39 Mon Sep 17 00:00:00 2001 From: ALICE Action Bot Date: Mon, 9 Feb 2026 17:09:19 +0000 Subject: [PATCH 11/14] Please consider the following formatting changes --- PWGHF/TableProducer/trackIndexSkimCreator.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PWGHF/TableProducer/trackIndexSkimCreator.cxx b/PWGHF/TableProducer/trackIndexSkimCreator.cxx index 96f72f70ca8..94330cbc89f 100644 --- a/PWGHF/TableProducer/trackIndexSkimCreator.cxx +++ b/PWGHF/TableProducer/trackIndexSkimCreator.cxx @@ -441,7 +441,7 @@ struct HfTrackIndexSkimCreatorTagSelTracks { registry.add("hDCAToPrimXYVsPtCutsCascadeBachelor", "tracks selected for cascade-bachelor vertexing;#it{p}_{T}^{track} (GeV/#it{c});DCAxy to prim. vtx. (cm);entries", {HistType::kTH2D, {axisPtProng, axisDca}}); registry.add("hEtaCutsCascadeBachelor", "tracks selected for cascade-bachelor vertexing;#it{#eta};entries", {HistType::kTH1D, {{static_cast(scaleEtaMax * (config.etaMaxTrackBachLfCasc - config.etaMinTrackBachLfCasc) * nBinsPerEta), scaleEtaMax * config.etaMinTrackBachLfCasc, scaleEtaMax * config.etaMaxTrackBachLfCasc}}}); registry.add("hTPCSignalsLightNuclei", "Light Nuclei;p_{TPC}/z (GeV/#it{c}); d#it{E}/d#it{x}", {HistType::kTH2D, {{2000, -10., 10.}, {1000, 0., 2000.}}}); - + const std::string cutNames[nCuts + 1] = {"selected", "rej pT", "rej eta", "rej track quality", "rej dca"}; const std::string candNames[CandidateType::NCandidateTypes] = {"2-prong", "3-prong", "bachelor", "dstar", "lfCascBachelor"}; for (int iCandType = 0; iCandType < CandidateType::NCandidateTypes; iCandType++) { From 51f68110edfad567e112cc484f8bb5e824555291 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?BiaoZhang=20=28=E5=BC=A0=E5=BD=AA=29?= <52267892+zhangbiao-phy@users.noreply.github.com> Date: Mon, 9 Feb 2026 20:57:40 +0100 Subject: [PATCH 12/14] update the documentation for the para --- PWGHF/TableProducer/trackIndexSkimCreator.cxx | 1 + 1 file changed, 1 insertion(+) diff --git a/PWGHF/TableProducer/trackIndexSkimCreator.cxx b/PWGHF/TableProducer/trackIndexSkimCreator.cxx index 94330cbc89f..a126d4fdc7e 100644 --- a/PWGHF/TableProducer/trackIndexSkimCreator.cxx +++ b/PWGHF/TableProducer/trackIndexSkimCreator.cxx @@ -651,6 +651,7 @@ struct HfTrackIndexSkimCreatorTagSelTracks { /// PID track cuts (for proton only) /// \param hfTrack is a track + /// \param hfTrackWithIts is a track joined with the ITS PID table /// \return true if the track is compatible with a proton hypothesis template uint8_t isSelectedPid(const T& hfTrack, const TrackWithIts& hfTrackWithIts) From 4ed9c00f616ce7cf987eb53cc0a03b434dbc902f Mon Sep 17 00:00:00 2001 From: ALICE Action Bot Date: Mon, 9 Feb 2026 19:58:17 +0000 Subject: [PATCH 13/14] Please consider the following formatting changes --- PWGHF/TableProducer/trackIndexSkimCreator.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PWGHF/TableProducer/trackIndexSkimCreator.cxx b/PWGHF/TableProducer/trackIndexSkimCreator.cxx index a126d4fdc7e..a80255e428c 100644 --- a/PWGHF/TableProducer/trackIndexSkimCreator.cxx +++ b/PWGHF/TableProducer/trackIndexSkimCreator.cxx @@ -651,7 +651,7 @@ struct HfTrackIndexSkimCreatorTagSelTracks { /// PID track cuts (for proton only) /// \param hfTrack is a track - /// \param hfTrackWithIts is a track joined with the ITS PID table + /// \param hfTrackWithIts is a track joined with the ITS PID table /// \return true if the track is compatible with a proton hypothesis template uint8_t isSelectedPid(const T& hfTrack, const TrackWithIts& hfTrackWithIts) From 540cb246dec6de1b6683b57dd1f01375933e32f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?BiaoZhang=20=28=E5=BC=A0=E5=BD=AA=29?= <52267892+zhangbiao-phy@users.noreply.github.com> Date: Tue, 10 Feb 2026 18:29:01 +0100 Subject: [PATCH 14/14] Update trackIndexSkimCreator.cxx --- PWGHF/TableProducer/trackIndexSkimCreator.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PWGHF/TableProducer/trackIndexSkimCreator.cxx b/PWGHF/TableProducer/trackIndexSkimCreator.cxx index a80255e428c..c2dbbaa0012 100644 --- a/PWGHF/TableProducer/trackIndexSkimCreator.cxx +++ b/PWGHF/TableProducer/trackIndexSkimCreator.cxx @@ -652,7 +652,7 @@ struct HfTrackIndexSkimCreatorTagSelTracks { /// PID track cuts (for proton only) /// \param hfTrack is a track /// \param hfTrackWithIts is a track joined with the ITS PID table - /// \return true if the track is compatible with a proton hypothesis + /// \return bit mask encoding PID compatibility per channel template uint8_t isSelectedPid(const T& hfTrack, const TrackWithIts& hfTrackWithIts) {