@@ -313,6 +313,19 @@ struct HStrangeCorrelation {
313313 ConfigurableAxis axisDaughterDeltaEta{" axisDaughterDeltaEta" , {100 , -0 .2f , 0 .2f }, " trigger-daughter delta eta" };
314314 ConfigurableAxis axisDaughterDeltaPhi{" axisDaughterDeltaPhi" , {120 , -0 .3f , 0 .3f }, " trigger-daughter delta phi" };
315315 ConfigurableAxis axisDecayRadius{" axisDecayRadius" , {100 , 0 .0f , 200 .0f }, " generated K0 decay radius (cm)" };
316+ // The following mirror the isValidTrigger() cuts applied by hStrangeCorrelationFilter.cxx
317+ // when building the TriggerTracks table. They must be kept in sync by hand with whatever
318+ // values are configured for that (separate) filter task, since this diagnostic cannot read
319+ // them directly: it only sees the already-filtered TriggerTracks table, not why a given
320+ // best-collision track failed to enter it.
321+ Configurable<float > triggerTracksEtaMin{" triggerTracksEtaMin" , -0 .8f , " must match triggerEtaMin in hStrangeCorrelationFilter.cxx" };
322+ Configurable<float > triggerTracksEtaMax{" triggerTracksEtaMax" , 0 .8f , " must match triggerEtaMax in hStrangeCorrelationFilter.cxx" };
323+ Configurable<float > triggerTracksPtMin{" triggerTracksPtMin" , 3 .0f , " must match triggerPtCutMin in hStrangeCorrelationFilter.cxx" };
324+ Configurable<float > triggerTracksPtMax{" triggerTracksPtMax" , 20 .0f , " must match triggerPtCutMax in hStrangeCorrelationFilter.cxx" };
325+ Configurable<int > triggerTracksMinCrossedRows{" triggerTracksMinCrossedRows" , 70 , " must match minTPCNCrossedRows in hStrangeCorrelationFilter.cxx" };
326+ Configurable<bool > triggerTracksRequireITS{" triggerTracksRequireITS" , true , " must match triggerRequireITS in hStrangeCorrelationFilter.cxx" };
327+ Configurable<int > triggerTracksMaxSharedClusters{" triggerTracksMaxSharedClusters" , 200 , " must match triggerMaxTPCSharedClusters in hStrangeCorrelationFilter.cxx" };
328+ Configurable<bool > triggerTracksRequireLayer0{" triggerTracksRequireLayer0" , false , " must match triggerRequireL0 in hStrangeCorrelationFilter.cxx" };
316329 } pairLossK0Configurations;
317330
318331 struct ValidCollision {
@@ -490,6 +503,29 @@ struct HStrangeCorrelation {
490503 bool hasLayer0 = false ;
491504 };
492505
506+ // First-failing-condition breakdown for the stage3->4 gate ("Trigger track, best collision"
507+ // -> "Trigger in TriggerTracks"). Order mirrors the early-return order of isValidTrigger()
508+ // in hStrangeCorrelationFilter.cxx exactly, so "first reason to fail" means the same thing here.
509+ enum PairLossTriggerTracksFailureReason : int {
510+ PairLossTriggerTracksPassed = 0 ,
511+ PairLossTriggerTracksFailEta,
512+ PairLossTriggerTracksFailPt,
513+ PairLossTriggerTracksFailCrossedRows,
514+ PairLossTriggerTracksFailITS,
515+ PairLossTriggerTracksFailSharedClusters,
516+ PairLossTriggerTracksFailLayer0,
517+ PairLossTriggerTracksNReasons
518+ };
519+
520+ static constexpr std::array<std::string_view, PairLossTriggerTracksNReasons> PairLossTriggerTracksFailureNames = {
521+ " Passed all cuts (in TriggerTracks)" ,
522+ " Failed eta window" ,
523+ " Failed pT window" ,
524+ " Failed min TPC crossed rows" ,
525+ " Failed hasITS requirement" ,
526+ " Failed max TPC shared clusters" ,
527+ " Failed ITS layer-0 requirement" };
528+
493529 struct PairLossV0Info {
494530 int64_t globalIndex = -1 ;
495531 int64_t positiveTrackId = -1 ;
@@ -566,6 +602,33 @@ struct HStrangeCorrelation {
566602 .hasLayer0 = static_cast <bool >(TESTBIT (track.itsClusterMap (), 0 ))};
567603 }
568604
605+ // Replicates isValidTrigger() from hStrangeCorrelationFilter.cxx condition-by-condition
606+ // (same early-return order) so that, for a trigger already known to exist in the best
607+ // collision (stage 3), we can tell which single cut is responsible for it not making it
608+ // into the TriggerTracks table (stage 4), instead of only knowing that it failed overall.
609+ int classifyTriggerTracksFailure (PairLossTrackInfo const & info)
610+ {
611+ if (info.eta > pairLossK0Configurations.triggerTracksEtaMax || info.eta < pairLossK0Configurations.triggerTracksEtaMin ) {
612+ return PairLossTriggerTracksFailEta;
613+ }
614+ if (info.pt > pairLossK0Configurations.triggerTracksPtMax || info.pt < pairLossK0Configurations.triggerTracksPtMin ) {
615+ return PairLossTriggerTracksFailPt;
616+ }
617+ if (info.tpcCrossedRows < pairLossK0Configurations.triggerTracksMinCrossedRows ) {
618+ return PairLossTriggerTracksFailCrossedRows;
619+ }
620+ if (info.itsClusters <= 0 && pairLossK0Configurations.triggerTracksRequireITS ) {
621+ return PairLossTriggerTracksFailITS;
622+ }
623+ if (info.tpcSharedClusters > pairLossK0Configurations.triggerTracksMaxSharedClusters ) {
624+ return PairLossTriggerTracksFailSharedClusters;
625+ }
626+ if (!info.hasLayer0 && pairLossK0Configurations.triggerTracksRequireLayer0 ) {
627+ return PairLossTriggerTracksFailLayer0;
628+ }
629+ return PairLossTriggerTracksPassed;
630+ }
631+
569632 PairLossDeltaPhiStarInfo calculateMinimumDeltaPhiStar (PairLossTruthTrackInfo const & trigger, PairLossTruthTrackInfo const & daughter, double magneticField, float decayRadiusCm)
570633 {
571634 PairLossDeltaPhiStarInfo result;
@@ -2168,6 +2231,7 @@ struct HStrangeCorrelation {
21682231 const double pairLossRadiusStep = std::max (static_cast <double >(pairLossK0Configurations.radiusStep ), 0.001 );
21692232 const int pairLossRadiusBins = std::max (1 , static_cast <int >(std::ceil ((pairLossRadiusMaximum - pairLossRadiusMinimum) / pairLossRadiusStep)));
21702233 const AxisSpec axisPairLossRadiusAtMinimum{pairLossRadiusBins, pairLossRadiusMinimum, pairLossRadiusMaximum, " #it{r}_{min} (m)" };
2234+ const AxisSpec axisPairLossTriggerTracksFailureReason{PairLossTriggerTracksNReasons, -0.5 , static_cast <double >(PairLossTriggerTracksNReasons) - 0.5 , " TriggerTracks first-failing condition" };
21712235
21722236 histos.add (" PairLossK0/Event/hCounter" , " K0 pair-loss event counter" , kTH1F , {axisPairLossEventStage});
21732237 histos.add (" PairLossK0/Event/hNRecoCollisions" , " reconstructed collisions per MC collision" , kTH1F , {axisPairLossNRecoCollisions});
@@ -2176,6 +2240,7 @@ struct HStrangeCorrelation {
21762240 histos.add (" PairLossK0/Stage/hPhysics" , " stages in h-K0 physics variables" , kTHnF , {axisPairLossStage, axisPairLossTruthDeltaPhi, axisPairLossTruthDeltaEta, axisPairLossTruthK0Pt, axisPairLossTruthTriggerPt, axisPairLossFieldSign});
21772241 histos.add (" PairLossK0/Stage/hPhysicsFindable" , " stages in h-K0 physics variables for findable K0" , kTHnF , {axisPairLossStage, axisPairLossTruthDeltaPhi, axisPairLossTruthDeltaEta, axisPairLossTruthK0Pt, axisPairLossTruthTriggerPt, axisPairLossFieldSign});
21782242 histos.add (" PairLossK0/Stage/hClose" , " stages in trigger-daughter close-pair variables" , kTHnF , {axisPairLossStage, axisPairLossMinDeltaPhiStar, axisPairLossDaughterDeltaEta, axisPairLossTruthK0Pt, axisPairLossTruthTriggerPt, axisPairLossFieldSign, axisPairLossChargeProduct});
2243+ histos.add (" PairLossK0/Stage/hTriggerTracksFailureReason" , " first-failing TriggerTracks condition for best-collision triggers, in h-K0 physics variables" , kTHnF , {axisPairLossTriggerTracksFailureReason, axisPairLossTruthDeltaPhi, axisPairLossTruthDeltaEta, axisPairLossTruthK0Pt, axisPairLossTruthTriggerPt});
21792244
21802245 histos.add (" PairLossK0/State/hFinalObjectStatePhysics" , " 00/01/10/11 final trigger-K0 object state" , kTHnF , {axisPairLossFinalObjectState, axisPairLossTruthDeltaPhi, axisPairLossTruthDeltaEta, axisPairLossTruthK0Pt, axisPairLossTruthTriggerPt, axisPairLossFieldSign});
21812246 histos.add (" PairLossK0/State/hFinalObjectStateClose" , " 00/01/10/11 final trigger-K0 object state in close-pair variables" , kTHnF , {axisPairLossFinalObjectState, axisPairLossMinDeltaPhiStar, axisPairLossDaughterDeltaEta, axisPairLossTruthK0Pt, axisPairLossTruthTriggerPt, axisPairLossFieldSign, axisPairLossChargeProduct});
@@ -2229,6 +2294,11 @@ struct HStrangeCorrelation {
22292294 stageCounts->GetYaxis ()->SetTitle (" Truth-pair entries" );
22302295 stageCountsFindable->GetYaxis ()->SetTitle (" Findable truth-pair entries" );
22312296
2297+ auto triggerTracksFailureHistogram = histos.get <THn>(HIST (" PairLossK0/Stage/hTriggerTracksFailureReason" ));
2298+ for (int i = 0 ; i < PairLossTriggerTracksNReasons; ++i) {
2299+ triggerTracksFailureHistogram->GetAxis (0 )->SetBinLabel (i + 1 , PairLossTriggerTracksFailureNames[i].data ());
2300+ }
2301+
22322302 const std::array<std::string_view, 4 > objectStateLabels = {" 00 neither" , " 01 K0 only" , " 10 trigger only" , " 11 both" };
22332303 for (auto const & histogram : {histos.get <THn>(HIST (" PairLossK0/State/hFinalObjectStatePhysics" )), histos.get <THn>(HIST (" PairLossK0/State/hFinalObjectStateClose" ))}) {
22342304 for (size_t i = 0 ; i < objectStateLabels.size (); ++i) {
@@ -4103,6 +4173,16 @@ struct HStrangeCorrelation {
41034173 }
41044174 }
41054175
4176+ // Only meaningful conditional on the trigger already existing in the best collision
4177+ // (stage 3): this asks *why* that already-found, already-correctly-labelled track
4178+ // does or does not make it into the TriggerTracks table (stage 4).
4179+ if (triggerBestCollision) {
4180+ if (auto const * bestCollisionTrigger = bestTrack (tracksBestCollision, truthTrigger.globalIndex )) {
4181+ const int failureReason = classifyTriggerTracksFailure (*bestCollisionTrigger);
4182+ histos.fill (HIST (" PairLossK0/Stage/hTriggerTracksFailureReason" ), failureReason, truthDeltaPhi, truthDeltaEta, truthK0.pt , truthTrigger.pt );
4183+ }
4184+ }
4185+
41064186 const int finalObjectState = (triggerFinal ? 2 : 0 ) + (v0Final ? 1 : 0 );
41074187 const int trackLevelState = (triggerBestCollision ? 2 : 0 ) + (bothDaughtersBestCollision ? 1 : 0 );
41084188 const int daughterTrackState = (positiveDaughterBestCollision ? 2 : 0 ) + (negativeDaughterBestCollision ? 1 : 0 );
0 commit comments