@@ -86,13 +86,14 @@ struct Nucleibalance {
8686 O2_DEFINE_CONFIGURABLE (cfgCutMCPt, float , 0 .5f , " Minimal pT for MC particles (AO2D-MC mode)" )
8787 O2_DEFINE_CONFIGURABLE (cfgCutMCEta, float , 0 .8f , " Eta range for MC particles (AO2D-MC mode)" )
8888 O2_DEFINE_CONFIGURABLE (cfgUseFT0M, int , 1 , " Use FT0M centrality (0-100) as multiplicity axis (1=ON, 0=use Ntracks/multiplicity())" )
89- // Track-quality options (AO2D mode). Default selection corresponds to global tracks.
90- O2_DEFINE_CONFIGURABLE (cfgTPCNClsMin, int , 70 , " Minimum number of TPC clusters (tpcNClsFound) in AO2D mode" )
91- O2_DEFINE_CONFIGURABLE (cfgDcaXYMax, float , 0 .1f , " Max |DCA_{xy}| to PV (cm) in AO2D mode" )
92- O2_DEFINE_CONFIGURABLE (cfgDcaZMax, float , 0 .2f , " Max |DCA_{z}| to PV (cm) in AO2D mode" )
93- O2_DEFINE_CONFIGURABLE (chi2pertpccluster, float , 2 .5f , " Maximum Chi2/cluster for the TPC track segment in AO2D mode" )
94- O2_DEFINE_CONFIGURABLE (chi2peritscluster, float , 36 .f, " Maximum Chi2/cluster for the ITS track segment in AO2D mode" )
95- O2_DEFINE_CONFIGURABLE (itsnclusters, int , 5 , " Minimum number of ITS clusters in AO2D mode" )
89+ // Track-quality options. Default selection corresponds to global tracks.
90+ O2_DEFINE_CONFIGURABLE (cfgTPCNClsMin, int , 70 , " Minimum number of TPC clusters (tpcNClsFound)" )
91+ O2_DEFINE_CONFIGURABLE (cfgDcaXYMax, float , 0 .1f , " Max |DCA_{xy}| to PV (cm)" )
92+ O2_DEFINE_CONFIGURABLE (cfgDcaZMax, float , 0 .2f , " Max |DCA_{z}| to PV (cm)" )
93+ O2_DEFINE_CONFIGURABLE (cfgRequirePrimaryTrack, int , 1 , " Require isPrimaryTrack()= true (1=ON, 0=OFF)" )
94+ O2_DEFINE_CONFIGURABLE (chi2pertpccluster, float , 2 .5f , " Maximum Chi2/cluster for the TPC track segment" )
95+ O2_DEFINE_CONFIGURABLE (chi2peritscluster, float , 36 .f, " Maximum Chi2/cluster for the ITS track segment" )
96+ O2_DEFINE_CONFIGURABLE (itsnclusters, int , 1 , " Minimum number of ITS clusters" )
9697
9798 O2_DEFINE_CONFIGURABLE (cfgPtOrder, int , 1 , " Only consider pairs for which pT,1 < pT,2 (0 = OFF, 1 = ON)" );
9899 O2_DEFINE_CONFIGURABLE (cfgTriggerCharge, int , 0 , " Select on charge of trigger particle: 0 = all; 1 = positive; -1 = negative" );
@@ -343,6 +344,15 @@ struct Nucleibalance {
343344 return false ;
344345 }
345346 }
347+ // Match the standard Lambda(1520) task option `cfgPrimaryTrack`:
348+ // require primary tracks when the corresponding selection column is available.
349+ if (cfgRequirePrimaryTrack.value != 0 ) {
350+ if constexpr (requires { trk.isPrimaryTrack (); }) {
351+ if (!trk.isPrimaryTrack ()) {
352+ return false ;
353+ }
354+ }
355+ }
346356
347357 if constexpr (requires { trk.itsNCls (); }) {
348358 if (itsnclusters.value > 0 && trk.itsNCls () < itsnclusters.value ) {
@@ -1680,7 +1690,7 @@ struct Lambdastarproxy {
16801690 // PID strategy values
16811691 static constexpr int PidStrategyRectangular = 0 ;
16821692 static constexpr int PidStrategyCircularTPCAndTOF = 1 ;
1683- static constexpr int PidStrategyTOFOnly = 2 ;
1693+ static constexpr int PidStrategyNucleiDeuteronTPC = 2 ;
16841694 // Basic configuration for event and track selection
16851695 Configurable<float > lstarCutVertex{" lstarCutVertex" , float {CutVertexDefault}, " Accepted z-vertex range (cm)" };
16861696 Configurable<float > lstarCutPtMin{" lstarCutPtMin" , float {CutPtMinDefault}, " Minimal pT for tracks (GeV/c)" };
@@ -1700,16 +1710,26 @@ struct Lambdastarproxy {
17001710 Configurable<float > lstarCutNsigmaTOFKaon{" lstarCutNsigmaTOFKaon" , float {NsigmaTOFDefault}, " |nSigma^{TOF}_{K}| cut" };
17011711 Configurable<float > lstarCutNsigmaTPCDe{" lstarCutNsigmaTPCDe" , float {NsigmaTPCDefault}, " |nSigma^{TPC}_{d}| cut" };
17021712 Configurable<float > lstarCutNsigmaTOFDe{" lstarCutNsigmaTOFDe" , float {NsigmaTOFDefault}, " |nSigma^{TOF}_{d}| cut" };
1713+ // Optional deuteron-only TOF auxiliary selections.
1714+ // Defaults are OFF, so strategy 2 remains TPC nSigma only for deuterons.
1715+ Configurable<int > lstarEnableBetaCutDe{" lstarEnableBetaCutDe" , 0 , " Enable deuteron-only TOF beta cut using beta() > lstarBetaCutDe" };
1716+ Configurable<float > lstarBetaCutDe{" lstarBetaCutDe" , 0 .4f , " Minimum TOF beta for deuteron-only beta cut" };
1717+ Configurable<int > lstarEnableExpSignalTOFDe{" lstarEnableExpSignalTOFDe" , 0 , " Enable deuteron-only TOF expected-signal-difference cut when lstarTOFExpSignalDiffDeMax > 0" };
1718+ Configurable<float > lstarTOFExpSignalDiffDeMax{" lstarTOFExpSignalDiffDeMax" , -1 .f , " Maximum |tofExpSignalDiffDe| for deuterons; <=0 disables the numeric cut" };
1719+
17031720 // PID strategy for final K/p/d candidate selection.
1704- // 0 = rectangular cuts: |TPC| < cut and, if TOF exists, |TOF| < cut
1721+ // 0 = pT-ref dependent rectangular cuts:
1722+ // pT < pTref: require |TPC| < TPC cut only
1723+ // pT >= pTref and TOF exists: require |TPC| < TPC cut and |TOF| < TOF cut
1724+ // pT >= pTref and TOF missing: reject
17051725 // 1 = pT-ref dependent circular cut:
17061726 // pT < pTref: require |TPC| < TPC cut only
17071727 // pT >= pTref and TOF exists: require sqrt(TPC^2 + TOF^2) < circular cut
17081728 // pT >= pTref and TOF missing: reject
1709- // 2 = TOF-only above pTref :
1710- // pT < pTref : require |TPC | < TPC cut only
1711- // pT >= pTref: require TOF hit and |TOF| < TOF cut
1712- Configurable<int > lstarPidStrategy{" lstarPidStrategy" , int {PidStrategyRectangular}, " PID strategy: 0=rectangular TPC/TOF, 1=pTref circular TPC+TOF, 2=pTref TOF -only" };
1729+ // 2 = hybrid official-like PID :
1730+ // deuterons : require |TPC_d | < TPC cut only
1731+ // kaons/protons: use the Lambda(1520)-like pT-ref circular TPC+ TOF logic
1732+ Configurable<int > lstarPidStrategy{" lstarPidStrategy" , int {PidStrategyRectangular}, " PID strategy: 0=pTref rectangular TPC/TOF, 1=pTref circular TPC+TOF, 2=hybrid: K/p circular, d TPC -only" };
17131733
17141734 Configurable<float > lstarPidCircularCutKaon{" lstarPidCircularCutKaon" , 2 .0f , " Circular PID cut sqrt(nSigmaTPC_K^2+nSigmaTOF_K^2) for kaons" };
17151735 Configurable<float > lstarPidCircularCutPr{" lstarPidCircularCutPr" , 2 .0f , " Circular PID cut sqrt(nSigmaTPC_p^2+nSigmaTOF_p^2) for protons" };
@@ -1720,8 +1740,12 @@ struct Lambdastarproxy {
17201740 Configurable<float > lstarPidPtRefDe{" lstarPidPtRefDe" , 0 .8f , " pT reference for deuteron PID strategy" };
17211741
17221742 // Track quality
1743+ Configurable<int > lstarRequireINELgt0{" lstarRequireINELgt0" , 1 , " Require INEL>0 event selection using isInelGt0() when available; fallback to multNTracksPVeta1()/numContrib()" };
17231744 Configurable<bool > lstarRequireGlobalTrack{" lstarRequireGlobalTrack" , bool {RequireGlobalTrackDefault}, " Require global tracks (default)" };
1724- Configurable<int > lstarTPCNClsMin{" lstarTPCNClsMin" , int {TPCNClsMinDefault}, " Minimum number of TPC clusters (tpcNClsFound)" };
1745+ Configurable<int > lstarRequirePrimaryTrack{" lstarRequirePrimaryTrack" , 1 , " Require isPrimaryTrack() for Lambda* proxy candidates when the column is available" };
1746+ Configurable<int > lstarOnlyGlobalTrackCuts{" lstarOnlyGlobalTrackCuts" , 0 , " If enabled, apply only the global-track plus primary-track requirements and skip additional ITS/TPC/DCA/chi2 track-quality cuts" };
1747+ Configurable<int > lstarTPCNClsMin{" lstarTPCNClsMin" , int {TPCNClsMinDefault}, " Minimum number of TPC crossed rows (tpcNClsCrossedRows)" };
1748+ Configurable<float > lstarTPCCrossedRowsOverFindableMin{" lstarTPCCrossedRowsOverFindableMin" , 0 .8f , " Minimum TPC crossed rows over findable clusters" };
17251749 Configurable<float > lstarDcaXYMax{" lstarDcaXYMax" , float {DcaXYMaxDefault}, " Max |DCA_{xy}| to PV (cm)" };
17261750 Configurable<float > lstarDcaZMax{" lstarDcaZMax" , float {DcaZMaxDefault}, " Max |DCA_{z}| to PV (cm)" };
17271751 Configurable<float > lstarChi2PerTPCCluster{" lstarChi2PerTPCCluster" , float {Chi2PerTPCClusterDefault}, " Maximum Chi2/cluster for the TPC track segment" };
@@ -1790,6 +1814,22 @@ struct Lambdastarproxy {
17901814 template <typename TCollision>
17911815 bool keepCollisionAO2D (TCollision const & collision) const
17921816 {
1817+ // Prefer the official event-selection flag when available.
1818+ if (lstarRequireINELgt0.value != 0 ) {
1819+ bool isINELgt0 = true ;
1820+
1821+ if constexpr (requires { collision.isInelGt0 (); }) {
1822+ isINELgt0 = collision.isInelGt0 ();
1823+ } else if constexpr (requires { collision.multNTracksPVeta1 (); }) {
1824+ isINELgt0 = collision.multNTracksPVeta1 () > 0 ;
1825+ } else if constexpr (requires { collision.numContrib (); }) {
1826+ isINELgt0 = collision.numContrib () > 0 ;
1827+ }
1828+
1829+ if (!isINELgt0) {
1830+ return false ;
1831+ }
1832+ }
17931833 if (lstarCfgTrigger.value == TriggerNone) {
17941834 return true ;
17951835 } else if (lstarCfgTrigger.value == TriggerSel8) {
@@ -1903,14 +1943,40 @@ struct Lambdastarproxy {
19031943 }
19041944 }
19051945
1946+ // Always require primary tracks
1947+ if (lstarRequirePrimaryTrack.value != 0 ) {
1948+ if constexpr (requires { trk.isPrimaryTrack (); }) {
1949+ if (!trk.isPrimaryTrack ()) {
1950+ return false ;
1951+ }
1952+ }
1953+ }
1954+
1955+ // Optional official-baseline mode: use only the O2 global-track and primary-track definitions.
1956+ // This bypasses the extra explicit ITS/TPC/DCA/chi2 cuts below.
1957+ if (lstarOnlyGlobalTrackCuts.value != 0 ) {
1958+ return true ;
1959+ }
1960+
19061961 if constexpr (requires { trk.itsNCls (); }) {
19071962 if (lstarITSNClusters.value > 0 && trk.itsNCls () < lstarITSNClusters.value ) {
19081963 return false ;
19091964 }
19101965 }
19111966
1912- if constexpr (requires { trk.tpcNClsFound (); }) {
1913- if (lstarTPCNClsMin.value > 0 && trk.tpcNClsFound () < lstarTPCNClsMin.value ) {
1967+ if constexpr (requires { trk.tpcNClsCrossedRows (); }) {
1968+ if (lstarTPCNClsMin.value > 0 && trk.tpcNClsCrossedRows () < lstarTPCNClsMin.value ) {
1969+ return false ;
1970+ }
1971+ } else if constexpr (requires { trk.tpcNClsFindable (); trk.tpcCrossedRowsOverFindableCls (); }) {
1972+ if (lstarTPCNClsMin.value > 0 && trk.tpcNClsFindable () * trk.tpcCrossedRowsOverFindableCls () < lstarTPCNClsMin.value ) {
1973+ return false ;
1974+ }
1975+ }
1976+
1977+ if constexpr (requires { trk.tpcCrossedRowsOverFindableCls (); }) {
1978+ if (lstarTPCCrossedRowsOverFindableMin.value > 0 .f &&
1979+ trk.tpcCrossedRowsOverFindableCls () < lstarTPCCrossedRowsOverFindableMin.value ) {
19141980 return false ;
19151981 }
19161982 }
@@ -2327,14 +2393,48 @@ struct Lambdastarproxy {
23272393 return true ; // fallback: if column not present, assume available
23282394 }
23292395
2330- bool passFinalCandidatePID (float pt,
2331- float nsTPC,
2332- float nsTOF,
2333- bool hasTof,
2334- float tpcCut,
2335- float tofCut,
2336- float circularCut,
2337- float ptRef) const
2396+ template <typename TTrack>
2397+ bool passOptionalDeuteronTOFExtras (const TTrack& trk) const
2398+ {
2399+ const bool needTOF =
2400+ (lstarEnableBetaCutDe.value != 0 ) ||
2401+ (lstarEnableExpSignalTOFDe.value != 0 && lstarTOFExpSignalDiffDeMax.value > 0 .f );
2402+
2403+ if (needTOF) {
2404+ if constexpr (requires { trk.hasTOF (); }) {
2405+ if (!trk.hasTOF ()) {
2406+ return false ;
2407+ }
2408+ }
2409+ }
2410+
2411+ if (lstarEnableBetaCutDe.value != 0 ) {
2412+ if constexpr (requires { trk.beta (); }) {
2413+ if (trk.beta () <= lstarBetaCutDe.value ) {
2414+ return false ;
2415+ }
2416+ } else {
2417+ return false ;
2418+ }
2419+ }
2420+
2421+ if (lstarEnableExpSignalTOFDe.value != 0 &&
2422+ lstarTOFExpSignalDiffDeMax.value > 0 .f ) {
2423+ if constexpr (requires { trk.tofExpSignalDiffDe (); }) {
2424+ if (std::abs (trk.tofExpSignalDiffDe ()) > lstarTOFExpSignalDiffDeMax.value ) {
2425+ return false ;
2426+ }
2427+ } else {
2428+ return false ;
2429+ }
2430+ }
2431+
2432+ return true ;
2433+ }
2434+
2435+ bool passFinalCandidatePID (float pt, float nsTPC, float nsTOF, bool hasTof,
2436+ float tpcCut, float tofCut, float circularCut,
2437+ float ptRef, bool isDeuteron = false ) const
23382438 {
23392439 // Strategy 1: analysis-note style circular TPC+TOF cut
23402440 if (lstarPidStrategy.value == PidStrategyCircularTPCAndTOF) {
@@ -2349,8 +2449,14 @@ struct Lambdastarproxy {
23492449 return std::sqrt (nsTPC * nsTPC + nsTOF * nsTOF) < circularCut;
23502450 }
23512451
2352- // Strategy 2: TOF-only above pTref
2353- if (lstarPidStrategy.value == PidStrategyTOFOnly) {
2452+ if (lstarPidStrategy.value == PidStrategyNucleiDeuteronTPC) {
2453+ // For deuterons, follow the default idea of the official nuclei task:
2454+ // use TPC nσ as the main hard PID selection.
2455+ if (isDeuteron) {
2456+ return std::abs (nsTPC) < tpcCut;
2457+ }
2458+
2459+ // For kaons/protons, use the Lambda(1520)-like pT-ref circular TPC+TOF logic.
23542460 if (pt < ptRef) {
23552461 return std::abs (nsTPC) < tpcCut;
23562462 }
@@ -2359,11 +2465,18 @@ struct Lambdastarproxy {
23592465 return false ;
23602466 }
23612467
2362- return std::abs ( nsTOF) < tofCut ;
2468+ return std::sqrt (nsTPC * nsTPC + nsTOF * nsTOF ) < circularCut ;
23632469 }
23642470
2365- // Strategy 0: old rectangular logic
2366- return (std::abs (nsTPC) < tpcCut) && (!hasTof || (std::abs (nsTOF) < tofCut));
2471+ // Strategy 0: pT-ref dependent rectangular TPC+TOF cut.
2472+ // Below pTref use TPC only; above pTref require TOF and apply both TPC and TOF cuts.
2473+ if (pt < ptRef) {
2474+ return std::abs (nsTPC) < tpcCut;
2475+ }
2476+ if (!hasTof) {
2477+ return false ;
2478+ }
2479+ return (std::abs (nsTPC) < tpcCut) && (std::abs (nsTOF) < tofCut);
23672480 }
23682481
23692482 // Return: 0=#pi, 1=K, 2=p, 3=d, -1=unclassified
@@ -2650,14 +2763,12 @@ struct Lambdastarproxy {
26502763 const float nsTPCDe = trkD.tpcNSigmaDe ();
26512764 const float nsTOFDe = trkD.tofNSigmaDe ();
26522765 const bool hasTofDe = hasTOFMatch (trkD);
2653- const bool isDeuteron = passFinalCandidatePID (ptD,
2654- nsTPCDe,
2655- nsTOFDe,
2656- hasTofDe,
2657- lstarCutNsigmaTPCDe.value ,
2658- lstarCutNsigmaTOFDe.value ,
2659- lstarPidCircularCutDe.value ,
2660- lstarPidPtRefDe.value );
2766+ if (!passOptionalDeuteronTOFExtras (trkD)) {
2767+ continue ;
2768+ }
2769+ const bool isDeuteron = passFinalCandidatePID (trkD.pt (), trkD.tpcNSigmaDe (), nsTOFDe, hasTofDe,
2770+ lstarCutNsigmaTPCDe.value , lstarCutNsigmaTOFDe.value ,
2771+ lstarPidCircularCutDe.value , lstarPidPtRefDe.value , true );
26612772 if (!isDeuteron) {
26622773 continue ;
26632774 }
@@ -2724,7 +2835,7 @@ struct Lambdastarproxy {
27242835 lstarCutNsigmaTPCPr.value ,
27252836 lstarCutNsigmaTOFPr.value ,
27262837 lstarPidCircularCutPr.value ,
2727- lstarPidPtRefPr.value );
2838+ lstarPidPtRefPr.value , false );
27282839 if (!isProton) {
27292840 continue ;
27302841 }
@@ -2786,7 +2897,7 @@ struct Lambdastarproxy {
27862897 lstarCutNsigmaTPCKaon.value ,
27872898 lstarCutNsigmaTOFKaon.value ,
27882899 lstarPidCircularCutKaon.value ,
2789- lstarPidPtRefKaon.value );
2900+ lstarPidPtRefKaon.value , false );
27902901 if (!isKaon) {
27912902 continue ;
27922903 }
0 commit comments