Skip to content

Commit 027e597

Browse files
committed
enabled TPC pid for light nuclei based on Bethe-Bloch parametrization
1 parent 695559c commit 027e597

File tree

2 files changed

+110
-23
lines changed

2 files changed

+110
-23
lines changed

PWGHF/Core/SelectorCuts.h

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,12 +85,18 @@ namespace hf_presel_lightnuclei
8585
{
8686

8787
// default values for the track cuts for lightnuclei in the track-index-skim-creator
88-
constexpr float CutsTrackQuality[3][9] = {{-4, 3, 5., 0., 100, 100, 0.83, 160., 1.},
89-
{-4, 3, 5., 0., 100, 100, 0.83, 160., 1.},
90-
{-4, 3, 5., 0., 100, 100, 0.83, 160., 1.}};
91-
static const std::vector<std::string> labelsCutsTrack = {"nSigmaMinIts", "minItsClusterSizes", "minItsCluster", "minItsIbCluster", "minTpcCluster", "minTpcRow", "minTpcCrossedOverFound", "maxTpcShared", "maxTpcFracShared"};
88+
constexpr float CutsTrackQuality[3][10] = {{-4, 3, 5., 0., 100, 100, 0.83, 160., 1., 5},
89+
{-4, 3, 5., 0., 100, 100, 0.83, 160., 1., 5},
90+
{-4, 3, 5., 0., 100, 100, 0.83, 160., 1., 5}};
91+
static const std::vector<std::string> labelsCutsTrack = {"nSigmaMinIts", "minItsClusterSizes", "minItsCluster", "minItsIbCluster", "minTpcCluster", "minTpcRow", "minTpcCrossedOverFound", "maxTpcShared", "maxTpcFracShared", "maxTPCnSigmaBB"};
9292
static const std::vector<std::string> labelsRowsNucleiType = {"Deutron", "Triton", "Helium3"};
9393

94+
constexpr float BetheBlochParams[3][6] = {{5.39302, 7.859534, 0.004048, 2.323197, 1.609307, 0.09},
95+
{5.39302, 7.859534, 0.004048, 2.323197, 1.609307, 0.09},
96+
{-126.557359, -0.858569, 1.111643, 1.210323, 2.656374, 0.09}};
97+
98+
static const std::vector<std::string> labelsBetheBlochParams = {"p0", "p1", "p2", "p3", "p4", "resolution"};
99+
94100
} // namespace hf_presel_lightnuclei
95101

96102
namespace hf_cuts_bdt_multiclass

PWGHF/TableProducer/trackIndexSkimCreator.cxx

Lines changed: 100 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
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

Comments
 (0)