4545#include " Common/DataModel/TrackSelectionTables.h"
4646#include " Tools/ML/MlResponse.h"
4747
48+ #include " MathUtils/BetheBlochAleph.h"
4849#include < CCDB/BasicCCDBManager.h> // for PV refit
4950#include < CCDB/CcdbApi.h>
5051#include < CommonConstants/PhysicsConstants.h>
@@ -1302,7 +1303,10 @@ struct HfTrackIndexSkimCreator {
13021303 Configurable<LabeledArray<double >> 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" };
13031304
13041305 // CharmNuclei track selection
1305- Configurable<LabeledArray<float >> 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" };
1306+ Configurable<LabeledArray<float >> 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" };
1307+ Configurable<LabeledArray<float >> tpcPidBBParamsLightNuclei{" tpcPidBBParamsLightNuclei" , {hf_presel_lightnuclei::BetheBlochParams[0 ], 3 , 6 , hf_presel_lightnuclei::labelsRowsNucleiType, hf_presel_lightnuclei::labelsBetheBlochParams},
1308+ " TPC PID Bethe–Bloch parameter configurations for light nuclei "
1309+ " (deuteron, triton, helium-3), used in BB-based PID when enabled" };
13061310
13071311 // proton PID selections for Lc and Xic
13081312 Configurable<bool > applyProtonPidForLcToPKPi{" applyProtonPidForLcToPKPi" , false , " Apply proton PID for Lc->pKpi" };
@@ -1311,6 +1315,8 @@ struct HfTrackIndexSkimCreator {
13111315 Configurable<bool > applyDeuteronPidForCdToDeKPi{" applyDeuteronPidForCdToDeKPi" , false , " Require deuteron PID for Cd->deKpi" };
13121316 Configurable<bool > applyTritonPidForCtToTrKPi{" applyTritonPidForCtToTrKPi" , false , " Require triton PID for Ct->tKpi" };
13131317 Configurable<bool > applyHeliumPidForChToHeKPi{" applyHeliumPidForChToHeKPi" , false , " Require helium3 PID for Ch->heKpi" };
1318+ Configurable<bool > applyLightNucleiTpcPidBasedOnBB{" applyLightNucleiTpcPidBasedOnBB" , false , " Apply TPC PID for light nuclei using Bethe–Bloch parameterization" };
1319+
13141320 // lightnuclei track selection for charmnuclei
13151321 Configurable<bool > applyNucleiSelcForCharmNuclei{" applyNucleiSelcForCharmNuclei" , false , " Require track selection for charm nuclei" };
13161322 // ML models for triggers
@@ -1654,18 +1660,36 @@ struct HfTrackIndexSkimCreator {
16541660 }
16551661
16561662 // / Apply track-quality (ITS/TPC) + optional ITS-PID preselection for light-nucleus daughters used in charm-nuclei 3-prong channels (Cd/Ct/Ch).
1657- // / \tparam TrackType Track/ASoA row type providing ITS/TPC quality accessors.
1663+ // / \tparam TrackType Track providing ITS/TPC quality accessors.
16581664 // / \param track Daughter track to be tested (either prong0 or prong2).
1659- // / \param lightnuclei Species selector 0: Deuteron, 1: Triton, 2: Helium3.
1660- // / \param nSigmaItsPid ITS nσ value for the selected light nucleus hypothesis
1665+ // / \param lightnuclei Species selector: 0= Deuteron, 1= Triton, 2= Helium3.
1666+ // / \return true if the track passes all enabled selections.
16611667 template <typename TrackType>
16621668 bool applyTrackSelectionForCharmNuclei (const TrackType& track,
1663- ChannelsNucleiQA lightnuclei,
1664- float nSigmaItsPid)
1669+ ChannelsNucleiQA lightnuclei)
16651670 {
16661671 // Row index in the selection table: 0 (De), 1 (Tr), 2 (He3)
16671672 const int row = static_cast <int >(lightnuclei);
1668- const float nSigmaItsNuclei = nSigmaItsPid;
1673+ if (row < 0 || row >= NChannelsLightNucleiPid) {
1674+ return false ;
1675+ }
1676+
1677+ float nSigmaItsNuclei = -999 .f ;
1678+
1679+ switch (lightnuclei) {
1680+ case ChannelsNucleiQA::Deuteron:
1681+ nSigmaItsNuclei = track.itsNSigmaDe ();
1682+ break ;
1683+ case ChannelsNucleiQA::Triton:
1684+ nSigmaItsNuclei = track.itsNSigmaTr ();
1685+ break ;
1686+ case ChannelsNucleiQA::Helium3:
1687+ nSigmaItsNuclei = track.itsNSigmaHe ();
1688+ break ;
1689+ default :
1690+ return false ;
1691+ }
1692+
16691693 // Load cuts for the selected species.
16701694 const float minItsNSigmaPid = config.selectionsLightNuclei ->get (row, 0u );
16711695 const int minItsClusterSizes = config.selectionsLightNuclei ->get (row, 1u );
@@ -1677,6 +1701,9 @@ struct HfTrackIndexSkimCreator {
16771701 const int maxTpcShared = config.selectionsLightNuclei ->get (row, 7u );
16781702 const float maxTpcFracShared = config.selectionsLightNuclei ->get (row, 8u );
16791703
1704+ // Optional: BB-based TPC nσ selection (only if enabled)
1705+ const float maxTPCnSigmaBB = config.selectionsLightNuclei ->get (row, 9u );
1706+
16801707 if (nSigmaItsNuclei < minItsNSigmaPid) {
16811708 return false ;
16821709 }
@@ -1704,9 +1731,71 @@ struct HfTrackIndexSkimCreator {
17041731 if (track.tpcFractionSharedCls () > maxTpcFracShared) {
17051732 return false ;
17061733 }
1734+
1735+ if (config.applyLightNucleiTpcPidBasedOnBB ) {
1736+ const float nSigmaTpcNuclei = getTPCnSigmaBB (track, lightnuclei);
1737+ if (nSigmaTpcNuclei < -999 .f ) { // invalid marker
1738+ return false ;
1739+ }
1740+ // Correct inequality: reject if |nσ| exceeds allowed window
1741+ if (std::abs (nSigmaTpcNuclei) > maxTPCnSigmaBB) {
1742+ return false ;
1743+ }
1744+ }
1745+
17071746 return true ;
17081747 }
17091748
1749+ // / Compute TPC nσ for light nuclei (De/Tr/He3) using a Bethe–Bloch parameter configuration (BB-based PID).
1750+ // /
1751+ // / \tparam TrackType Track/ASoA row type providing TPC accessors.
1752+ // / \param track Track to be tested.
1753+ // / \param lightnuclei Species selector: 0=Deuteron, 1=Triton, 2=Helium3.
1754+ // / \return TPC nσ for the chosen nucleus hypothesis (or -999 if not applicable).
1755+ template <typename TrackType>
1756+ float getTPCnSigmaBB (const TrackType& track, ChannelsNucleiQA lightnuclei)
1757+ {
1758+ if (!track.hasTPC ()) {
1759+ return -999 .f ;
1760+ }
1761+
1762+ const int row = static_cast <int >(lightnuclei);
1763+ if (row < 0 || row >= NChannelsLightNucleiPid) {
1764+ return -999 .f ;
1765+ }
1766+
1767+ // Columns: [0..4] BB params, [5] relative resolution (sigma/mean)
1768+ const double bb0 = config.tpcPidBBParamsLightNuclei ->get (row, 0u );
1769+ const double bb1 = config.tpcPidBBParamsLightNuclei ->get (row, 1u );
1770+ const double bb2 = config.tpcPidBBParamsLightNuclei ->get (row, 2u );
1771+ const double bb3 = config.tpcPidBBParamsLightNuclei ->get (row, 3u );
1772+ const double bb4 = config.tpcPidBBParamsLightNuclei ->get (row, 4u );
1773+ const double relRes = config.tpcPidBBParamsLightNuclei ->get (row, 5u );
1774+
1775+ if (relRes <= 0 .f ) {
1776+ return -999 .f ;
1777+ }
1778+
1779+ // Mass/charge hypothesis for the selected nucleus.
1780+ const double mass =
1781+ (lightnuclei == ChannelsNucleiQA::Deuteron) ? MassDeuteron : (lightnuclei == ChannelsNucleiQA::Triton) ? MassTriton
1782+ : MassHelium3;
1783+
1784+ const int charge = (lightnuclei == ChannelsNucleiQA::Helium3) ? 2 : 1 ;
1785+
1786+ const float rigidity = track.tpcInnerParam (); // p / |q| note: here we didn't apply rigidity correction
1787+
1788+ const double x = static_cast <double >(charge) * static_cast <double >(rigidity) / mass;
1789+ const double expBethe = common::BetheBlochAleph (x, bb0, bb1, bb2, bb3, bb4);
1790+ const double expSigma = expBethe * static_cast <double >(relRes);
1791+
1792+ if (expSigma <= 0 .) {
1793+ return -999 .f ;
1794+ }
1795+
1796+ return static_cast <float >((track.tpcSignal () - expBethe) / expSigma);
1797+ }
1798+
17101799 // / Method to perform selections on difference from nominal mass for phi decay
17111800 // / \param binPt pt bin for the cuts
17121801 // / \param pVecTrack0 is the momentum array of the first daughter track
@@ -1763,25 +1852,17 @@ struct HfTrackIndexSkimCreator {
17631852 iDecay3P == hf_cand_3prong::DecayType::ChToHeKPi)) {
17641853
17651854 ChannelsNucleiQA nucleiType = ChannelsNucleiQA::Deuteron;
1766- float nSigmaItsNuclei0 = track0.itsNSigmaDe ();
1767- float nSigmaItsNuclei2 = track2.itsNSigmaDe ();
1768-
1769- if (iDecay3P == hf_cand_3prong::DecayType::CtToTrKPi) {
1855+ if (iDecay3P == hf_cand_3prong::DecayType::CtToTrKPi)
17701856 nucleiType = ChannelsNucleiQA::Triton;
1771- nSigmaItsNuclei0 = track0.itsNSigmaTr ();
1772- nSigmaItsNuclei2 = track2.itsNSigmaTr ();
1773- } else if (iDecay3P == hf_cand_3prong::DecayType::ChToHeKPi) {
1857+ else if (iDecay3P == hf_cand_3prong::DecayType::ChToHeKPi)
17741858 nucleiType = ChannelsNucleiQA::Helium3;
1775- nSigmaItsNuclei0 = track0.itsNSigmaHe ();
1776- nSigmaItsNuclei2 = track2.itsNSigmaHe ();
1777- }
17781859
17791860 // hypo0: nucleus on track0
1780- if (!applyTrackSelectionForCharmNuclei (track0, nucleiType, nSigmaItsNuclei0 )) {
1861+ if (!applyTrackSelectionForCharmNuclei (track0, nucleiType)) {
17811862 CLRBIT (whichHypo[iDecay3P], 0 );
17821863 }
17831864 // hypo1: nucleus on track2
1784- if (!applyTrackSelectionForCharmNuclei (track2, nucleiType, nSigmaItsNuclei2 )) {
1865+ if (!applyTrackSelectionForCharmNuclei (track2, nucleiType)) {
17851866 CLRBIT (whichHypo[iDecay3P], 1 );
17861867 }
17871868
0 commit comments