2828//
2929#include " Common/Core/RecoDecay.h"
3030#include " Common/Core/trackUtilities.h"
31+ #include " Common/DataModel/PIDResponseTOF.h"
32+ #include " Common/DataModel/PIDResponseTPC.h"
33+ #include " ReconstructionDataFormats/PID.h"
3134
3235#include < CCDB/BasicCCDBManager.h>
3336#include < CCDB/CcdbApi.h>
@@ -97,6 +100,7 @@ enum HfTriggers {
97100 kBtoJPsiPhi ,
98101 kBtoJPsiPrKa ,
99102 kBtoJPsiPi ,
103+ kSigmaCP ,
100104 kNtriggersHF
101105};
102106
@@ -245,7 +249,7 @@ static const int nTotBeautyParts = static_cast<int>(kNBeautyParticles) + static_
245249static const std::array<std::string, nTotBeautyParts> beautyParticleNames{" Bplus" , " B0toDStar" , " Bc" , " B0" , " Bs" , " Lb" , " Xib" , " BplusToJPsi" , " B0ToJPsi" , " BsToJPsi" , " LbToJPsi" , " BcToJPsi" };
246250static const std::array<int , kNCharmParticles > pdgCodesCharm{421 , 411 , 431 , 4122 , 4232 };
247251static const std::array<std::string, 2 > eventTitles = {" all" , " rejected" };
248- static const std::vector<std::string> hfTriggerNames{filtering::HfHighPt2P::columnLabel(), filtering::HfHighPt3P::columnLabel(), filtering::HfBeauty3P::columnLabel(), filtering::HfBeauty4P::columnLabel(), filtering::HfFemto2P::columnLabel(), filtering::HfFemto3P::columnLabel(), filtering::HfDoubleCharm2P::columnLabel(), filtering::HfDoubleCharm3P::columnLabel(), filtering::HfDoubleCharmMix::columnLabel(), filtering::HfV0Charm2P::columnLabel(), filtering::HfV0Charm3P::columnLabel(), filtering::HfCharmBarToXiBach::columnLabel(), filtering::HfSigmaCPPK::columnLabel(), filtering::HfSigmaC0K0::columnLabel(), filtering::HfPhotonCharm2P::columnLabel(), filtering::HfPhotonCharm3P::columnLabel(), filtering::HfSingleCharm2P::columnLabel(), filtering::HfSingleCharm3P::columnLabel(), filtering::HfSingleNonPromptCharm2P::columnLabel(), filtering::HfSingleNonPromptCharm3P::columnLabel(), filtering::HfCharmBarToXi2Bach::columnLabel(), filtering::HfPrCharm2P::columnLabel(), filtering::HfBtoJPsiKa::columnLabel(), filtering::HfBtoJPsiKstar::columnLabel(), filtering::HfBtoJPsiPhi::columnLabel(), filtering::HfBtoJPsiPrKa::columnLabel(), filtering::HfBtoJPsiPi::columnLabel()};
252+ static const std::vector<std::string> hfTriggerNames{filtering::HfHighPt2P::columnLabel(), filtering::HfHighPt3P::columnLabel(), filtering::HfBeauty3P::columnLabel(), filtering::HfBeauty4P::columnLabel(), filtering::HfFemto2P::columnLabel(), filtering::HfFemto3P::columnLabel(), filtering::HfDoubleCharm2P::columnLabel(), filtering::HfDoubleCharm3P::columnLabel(), filtering::HfDoubleCharmMix::columnLabel(), filtering::HfV0Charm2P::columnLabel(), filtering::HfV0Charm3P::columnLabel(), filtering::HfCharmBarToXiBach::columnLabel(), filtering::HfSigmaCPPK::columnLabel(), filtering::HfSigmaC0K0::columnLabel(), filtering::HfPhotonCharm2P::columnLabel(), filtering::HfPhotonCharm3P::columnLabel(), filtering::HfSingleCharm2P::columnLabel(), filtering::HfSingleCharm3P::columnLabel(), filtering::HfSingleNonPromptCharm2P::columnLabel(), filtering::HfSingleNonPromptCharm3P::columnLabel(), filtering::HfCharmBarToXi2Bach::columnLabel(), filtering::HfPrCharm2P::columnLabel(), filtering::HfBtoJPsiKa::columnLabel(), filtering::HfBtoJPsiKstar::columnLabel(), filtering::HfBtoJPsiPhi::columnLabel(), filtering::HfBtoJPsiPrKa::columnLabel(), filtering::HfBtoJPsiPi::columnLabel(), filtering::HfSigmaCP::columnLabel()};
249253
250254static const std::array<std::string, kNV0 > v0Labels{" #gamma" , " K_{S}^{0}" , " #Lambda" , " #bar{#Lambda}" };
251255static const std::array<std::string, kNV0 > v0Names{" Photon" , " K0S" , " Lambda" , " AntiLambda" };
@@ -293,7 +297,7 @@ static const o2::framework::AxisSpec alphaAxis{100, -1.f, 1.f};
293297static const o2::framework::AxisSpec qtAxis{100 , 0 .f , 0 .25f };
294298static const o2::framework::AxisSpec bdtAxis{100 , 0 .f , 1 .f };
295299static const o2::framework::AxisSpec phiAxis{36 , 0 ., o2::constants::math::TwoPI};
296- static const std::array<o2::framework::AxisSpec, kNCharmParticles + 23> massAxisC = {o2::framework::AxisSpec{250, 1.65f, 2.15f}, o2::framework::AxisSpec{250, 1.65f, 2.15f}, o2::framework::AxisSpec{250, 1.75f, 2.25f}, o2::framework::AxisSpec{250, 2.05f, 2.55f}, o2::framework::AxisSpec{250, 2.25f, 2.75f}, o2::framework::AxisSpec{200, 0.139f, 0.159f}, o2::framework::AxisSpec{250, 0.f, 0.25f}, o2::framework::AxisSpec{250, 0.f, 0.25f}, o2::framework::AxisSpec{200, 0.48f, 0.88f}, o2::framework::AxisSpec{200, 0.48f, 0.88f}, o2::framework::AxisSpec{200, 1.1f, 1.4f}, o2::framework::AxisSpec{200, 1.1f, 1.4f}, o2::framework::AxisSpec{200, 1.1f, 1.4f}, o2::framework::AxisSpec{200, 1.1f, 1.4f}, o2::framework::AxisSpec{170, 0.13f, 0.3f}, o2::framework::AxisSpec{170, 0.13f, 0.3f}, o2::framework::AxisSpec{200, 0.4f, 0.8f}, o2::framework::AxisSpec{200, 0.4f, 0.8f}, o2::framework::AxisSpec{200, 0.4f, 0.8f}, o2::framework::AxisSpec{200, 0.4f, 0.8f}, o2::framework::AxisSpec{350, 2.3f, 3.0f}, o2::framework::AxisSpec{350, 2.3f, 3.0f}, o2::framework::AxisSpec{350, 2.3f, 3.0f}, o2::framework::AxisSpec{240, 2.4f, 3.6f}, o2::framework::AxisSpec{300, 0.7f, 1.3f}, o2::framework::AxisSpec{300, 0.7f, 1.3f}, o2::framework::AxisSpec{300, 0.7f, 1.3f}, o2::framework::AxisSpec{300, 0.7f, 1.3f}};
300+ static const std::array<o2::framework::AxisSpec, kNCharmParticles + 24> massAxisC = {o2::framework::AxisSpec{250, 1.65f, 2.15f}, o2::framework::AxisSpec{250, 1.65f, 2.15f}, o2::framework::AxisSpec{250, 1.75f, 2.25f}, o2::framework::AxisSpec{250, 2.05f, 2.55f}, o2::framework::AxisSpec{250, 2.25f, 2.75f}, o2::framework::AxisSpec{200, 0.139f, 0.159f}, o2::framework::AxisSpec{250, 0.f, 0.25f}, o2::framework::AxisSpec{250, 0.f, 0.25f}, o2::framework::AxisSpec{200, 0.48f, 0.88f}, o2::framework::AxisSpec{200, 0.48f, 0.88f}, o2::framework::AxisSpec{200, 1.1f, 1.4f}, o2::framework::AxisSpec{200, 1.1f, 1.4f}, o2::framework::AxisSpec{200, 1.1f, 1.4f}, o2::framework::AxisSpec{200, 1.1f, 1.4f}, o2::framework::AxisSpec{170, 0.13f, 0.3f}, o2::framework::AxisSpec{170, 0.13f, 0.3f}, o2::framework::AxisSpec{200, 0.4f, 0.8f}, o2::framework::AxisSpec{200, 0.4f, 0.8f}, o2::framework::AxisSpec{200, 0.4f, 0.8f}, o2::framework::AxisSpec{200, 0.4f, 0.8f}, o2::framework::AxisSpec{350, 2.3f, 3.0f}, o2::framework::AxisSpec{350, 2.3f, 3.0f}, o2::framework::AxisSpec{350, 2.3f, 3.0f}, o2::framework::AxisSpec{240, 2.4f, 3.6f}, o2::framework::AxisSpec{300, 0.7f, 1.3f}, o2::framework::AxisSpec{300, 0.7f, 1.3f}, o2::framework::AxisSpec{300, 0.7f, 1.3f}, o2::framework::AxisSpec{300, 0.7f, 1.3f}, o2::framework::AxisSpec{300, 0.14f, 0.26f}};
297301static const std::array<o2::framework::AxisSpec, nTotBeautyParts> massAxisB = {o2::framework::AxisSpec{500 , 4 .2f , 6 .2f }, o2::framework::AxisSpec{500 , 4 .2f , 6 .2f }, o2::framework::AxisSpec{500 , 5 .4f , 7 .4f }, o2::framework::AxisSpec{500 , 4 .2f , 6 .2f }, o2::framework::AxisSpec{500 , 4 .4f , 6 .4f }, o2::framework::AxisSpec{400 , 5 .0f , 6 .6f }, o2::framework::AxisSpec{500 , 4 .2f , 6 .2f }, o2::framework::AxisSpec{500 , 4 .2f , 6 .2f }, o2::framework::AxisSpec{500 , 4 .2f , 6 .2f }, o2::framework::AxisSpec{500 , 4 .2f , 6 .2f }, o2::framework::AxisSpec{400 , 5 .0f , 6 .6f }, o2::framework::AxisSpec{240 , 5 .8f , 7 .0f }};
298302
299303// default values for configurables
@@ -625,6 +629,8 @@ class HfFilterHelper
625629 int16_t isSelectedTrackForSoftPionOrBeauty (const T& track, const T1& trackPar, const T2& dca);
626630 template <typename T1, typename T2, typename H2>
627631 bool isSelectedTrack4Femto (const T1& track, const T2& trackPar, const int & activateQA, H2 hTPCPID, H2 hTOFPID, const int & trackSpecies);
632+ template <typename Atrack, typename SpeciesContainer, typename T1, typename T2>
633+ bool isSelectedTrack4Corr (Atrack const & track, SpeciesContainer const mPIDspecies , T1 const maxTPC, T2 const maxTOF, float minPt = 0.39 , float maxPt = 4.6 , float ptThreshold = 1.0 , bool tofForced = false );
628634 template <typename T>
629635 int8_t isDzeroPreselected (const T& trackPos, const T& trackNeg);
630636 template <typename T>
@@ -644,6 +650,8 @@ class HfFilterHelper
644650 template <int charge, typename T, typename H2>
645651 int8_t isSelectedSigmaCInDeltaMassRange (const T& pTrackSameChargeFirst, const T& pTrackSameChargeSecond, const T& pTrackOppositeCharge, const T& pTrackSoftPi, const float ptSigmaC, const int8_t isSelectedLc, H2 hMassVsPt, const int & activateQA);
646652 template <typename T, typename H2>
653+ bool selectionSigmaCForScPCorr (const T& pTrackSameChargeFirst, const T& pTrackSameChargeSecond, const T& pTrackOppositeCharge, const T& pTrackSoftPi, const float ptSigmaC, const int8_t isSelectedLc, H2 hMassVsPt, const int & activateQA, float mDeltaMassMinSigmaC = 0.155 , float mDeltaMassMaxSigmaC = 0.2 , float mPtMinSigmaC = 4.99 , float mPtMaxSigmaC = 12.0 );
654+ template <typename T, typename H2>
647655 int8_t isSelectedXicInMassRange (const T& pTrackSameChargeFirst, const T& pTrackSameChargeSecond, const T& pTrackOppositeCharge, const float & ptXic, const int8_t isSelected, const int & activateQA, H2 hMassVsPt);
648656 template <typename V0, typename H2>
649657 int8_t isSelectedV0 (const V0& v0, const int & activateQA, H2 hV0Selected, std::array<H2, 4 >& hArmPod);
@@ -867,7 +875,7 @@ inline int16_t HfFilterHelper::isSelectedTrackForSoftPionOrBeauty(const T& track
867875 return kRejected ;
868876 }
869877
870- if constexpr (whichTrigger == kSigmaCPPK || whichTrigger == kSigmaC0K0 ) {
878+ if constexpr (whichTrigger == kSigmaCPPK || whichTrigger == kSigmaC0K0 || whichTrigger == kSigmaCP ) {
871879
872880 // SigmaC0,++ soft pion pt cut
873881 if (pT < mPtMinSoftPionForSigmaC || pT > mPtMaxSoftPionForSigmaC ) {
@@ -922,6 +930,113 @@ inline int16_t HfFilterHelper::isSelectedTrackForSoftPionOrBeauty(const T& track
922930 return retValue;
923931}
924932
933+ // / Basic selection of proton or deuteron candidates
934+ // / \param track is a track
935+ // / \param mPIDspecies is a vector of different particle species
936+ // / \param activateQA flag to activate the filling of QA histos
937+ // / \param maxTPC is a vector of max TPCnSigma for different particle species
938+ // / \param maxTOF is a vector of max TOFnSigma for different particle species
939+ // / \param hProtonTPCPID histo with NsigmaTPC vs. p
940+ // / \param hProtonTOFPID histo with NsigmaTOF vs. p
941+ // / \return true if track passes all cuts
942+ template <typename Atrack, typename SpeciesContainer, typename T1, typename T2>
943+ inline bool HfFilterHelper::isSelectedTrack4Corr (Atrack const & track, SpeciesContainer const mPIDspecies ,
944+ T1 const maxTPC, T2 const maxTOF, float minPt, float maxPt, float ptThreshold, bool tofForced)
945+ {
946+ // Ensure size consistency
947+ if (mPIDspecies .value .size () != maxTPC.value .size () || mPIDspecies .value .size () != maxTOF.value .size ()) {
948+ LOGF (error, " Size of particle species and corresponding nSigma selection arrays should be the same" );
949+ return false ; // Early exit on error
950+ }
951+
952+ if (!track.isGlobalTrackWoDCA ()) {
953+ return false ;
954+ }
955+
956+ if (track.pt () < minPt || track.pt () > maxPt){
957+ return false ;
958+ }
959+
960+ for (size_t speciesIndex = 0 ; speciesIndex < mPIDspecies .value .size (); ++speciesIndex) {
961+ float nSigmaTPC;
962+ auto const & pid = mPIDspecies ->at (speciesIndex);
963+
964+ nSigmaTPC = o2::aod::pidutils::tpcNSigma (pid, track);
965+
966+ if (track.pt () > ptThreshold && tofForced && !track.hasTOF ())
967+ return false ;
968+
969+ int parSpecies = -1 ;
970+ float tpcNCls = track.tpcNClsFound ();
971+ float tpcPin = track.tpcInnerParam ();
972+ float eta = track.eta ();
973+
974+ if (pid == o2::track::PID::Proton) {
975+ parSpecies = kPr ;
976+ } else if (pid == o2::track::PID::Kaon) {
977+ parSpecies = kKa ;
978+ } else if (pid == o2::track::PID::Pion) {
979+ parSpecies = kPi ;
980+ } else {
981+ LOGF (fatal, " particle species is not defined in isSelectedTrack4Corr" );
982+ }
983+
984+ // 2. Apply nSigmaTPC using selected calibration
985+ if (mTpcPidCalibrationOption == 1 ) {
986+ // Option 1: post-calibration → no sign dependence
987+ nSigmaTPC = getTPCPostCalib (tpcPin, tpcNCls, eta, nSigmaTPC, parSpecies);
988+
989+ }
990+ if (mTpcPidCalibrationOption == 2 ) {
991+ float dEdx = track.tpcSignal ();
992+ // Option 2: spline calibration → charge-dependent
993+ if (track.sign () > 0 ) {
994+ // Positive track
995+ nSigmaTPC = getTPCSplineCalib (tpcPin, dEdx, parSpecies);
996+ } else {
997+ // Negative track
998+ if (pid == o2::track::PID::Proton) {
999+ parSpecies = kAntiPr ;
1000+ } else if (pid == o2::track::PID::Kaon) {
1001+ parSpecies = kAntiKa ;
1002+ } else if (pid == o2::track::PID::Pion) {
1003+ parSpecies = kAntiPi ;
1004+ } else {
1005+ LOGF (fatal, " particle species is not defined in isSelectedTrack4Corr" );
1006+ }
1007+ nSigmaTPC = getTPCSplineCalib (tpcPin, dEdx, parSpecies);
1008+ }
1009+ }
1010+
1011+ if (speciesIndex == 0 ) { // First species logic
1012+
1013+ if (std::abs (nSigmaTPC) > maxTPC->at (speciesIndex)) {
1014+ return false ; // TPC check failed
1015+ }
1016+ if (track.hasTOF ()) {
1017+ auto nSigmaTOF = o2::aod::pidutils::tofNSigma (pid, track);
1018+ if (std::abs (nSigmaTOF) > maxTOF->at (speciesIndex)) {
1019+ return false ; // TOF check failed
1020+ }
1021+ }
1022+ } else { // Other species logic
1023+ if (std::abs (nSigmaTPC) < maxTPC->at (speciesIndex)) { // Check TPC nSigma first
1024+ if (track.hasTOF ()) {
1025+ auto nSigmaTOF = o2::aod::pidutils::tofNSigma (pid, track);
1026+ if (std::abs (nSigmaTOF) < maxTOF->at (speciesIndex)) {
1027+ return false ; // Reject if both TPC and TOF are within thresholds
1028+ }
1029+ } else {
1030+ return false ; // Reject if only TPC is within threshold and TOF is unavailable
1031+ }
1032+ }
1033+ }
1034+ }
1035+ return true ; // Passed all checks
1036+ }
1037+
1038+
1039+
9251040// / Basic selection of proton or deuteron candidates
9261041// / \param track is a track
9271042// / \param trackPar is a track parameter
@@ -1312,6 +1427,52 @@ inline int8_t HfFilterHelper::isSelectedLcInMassRange(const T& pTrackSameChargeF
13121427 return retValue;
13131428}
13141429
1430+ // / Delta mass selection on SigmaC candidates for correlation
1431+ template <typename T, typename H2>
1432+ inline bool HfFilterHelper::selectionSigmaCForScPCorr (const T& pTrackSameChargeFirst, const T& pTrackSameChargeSecond, const T& pTrackOppositeCharge, const T& pTrackSoftPi, const float ptSigmaC, const int8_t isSelectedLc, H2 hMassVsPt, const int & activateQA, float mDeltaMassMinSigmaC , float mDeltaMassMaxSigmaC , float mPtMinSigmaC , float mPtMaxSigmaC )
1433+ {
1434+ if (ptSigmaC < mPtMinSigmaC || ptSigmaC > mPtMaxSigmaC ){
1435+ return false ;
1436+ }
1437+ bool isSigmaCSelected{false };
1438+ if (TESTBIT (isSelectedLc, 0 )) {
1439+ // / Lc->pKpi case
1440+ auto invMassLcToPKPi = RecoDecay::m (std::array{pTrackSameChargeFirst, pTrackOppositeCharge, pTrackSameChargeSecond}, std::array{massProton, massKa, massPi});
1441+ std::array<float , 4 > massDausSigmaCToLcPKPi{massProton, massKa, massPi, massPi};
1442+ float invMassSigmaCToLcPKPi = RecoDecay::m (std::array{pTrackSameChargeFirst, pTrackOppositeCharge, pTrackSameChargeSecond, pTrackSoftPi}, massDausSigmaCToLcPKPi);
1443+ float deltaMassPKPi = invMassSigmaCToLcPKPi - invMassLcToPKPi;
1444+ isSigmaCSelected = (mDeltaMassMinSigmaC < deltaMassPKPi && deltaMassPKPi < mDeltaMassMaxSigmaC );
1445+ if (isSigmaCSelected){
1446+ if (activateQA){
1447+ hMassVsPt->Fill (ptSigmaC, deltaMassPKPi);
1448+ }
1449+ return true ;
1450+ }
1451+
1452+ }
1453+ if (TESTBIT (isSelectedLc, 1 )) {
1454+ // / Lc->piKp case
1455+ auto invMassLcToPiKP = RecoDecay::m (std::array{pTrackSameChargeFirst, pTrackOppositeCharge, pTrackSameChargeSecond}, std::array{massPi, massKa, massProton});
1456+ std::array<float , 4 > massDausSigmaCToLcPiKP{massPi, massKa, massProton, massPi};
1457+ float invMassSigmaCToLcPiKP = RecoDecay::m (std::array{pTrackSameChargeFirst, pTrackOppositeCharge, pTrackSameChargeSecond, pTrackSoftPi}, massDausSigmaCToLcPiKP);
1458+ float deltaMassPiKP = invMassSigmaCToLcPiKP - invMassLcToPiKP;
1459+
1460+ isSigmaCSelected = (mDeltaMassMinSigmaC < deltaMassPiKP && deltaMassPiKP < mDeltaMassMaxSigmaC );
1461+ if (isSigmaCSelected){
1462+ if (activateQA){
1463+ hMassVsPt->Fill (ptSigmaC, deltaMassPiKP);
1464+ }
1465+ return true ;
1466+ }
1467+
1468+ }
1469+
1470+ return isSigmaCSelected;
1471+ // / TODO: add QA plot
1472+ }
1473+
1474+
1475+
13151476// / Delta mass selection on SigmaC candidates
13161477template <int charge, typename T, typename H2>
13171478inline int8_t HfFilterHelper::isSelectedSigmaCInDeltaMassRange (const T& pTrackSameChargeFirst, const T& pTrackSameChargeSecond, const T& pTrackOppositeCharge, const T& pTrackSoftPi, const float ptSigmaC, const int8_t isSelectedLc, H2 hMassVsPt, const int & activateQA)
0 commit comments