@@ -311,6 +311,19 @@ struct HStrangeCorrelation {
311311 ConfigurableAxis axisDaughterDeltaEta{" axisDaughterDeltaEta" , {100 , -0 .2f , 0 .2f }, " trigger-daughter delta eta" };
312312 ConfigurableAxis axisDaughterDeltaPhi{" axisDaughterDeltaPhi" , {120 , -0 .3f , 0 .3f }, " trigger-daughter delta phi" };
313313 ConfigurableAxis axisDecayRadius{" axisDecayRadius" , {100 , 0 .0f , 200 .0f }, " generated K0 decay radius (cm)" };
314+ // The following mirror the isValidTrigger() cuts applied by hStrangeCorrelationFilter.cxx
315+ // when building the TriggerTracks table. They must be kept in sync by hand with whatever
316+ // values are configured for that (separate) filter task, since this diagnostic cannot read
317+ // them directly: it only sees the already-filtered TriggerTracks table, not why a given
318+ // best-collision track failed to enter it.
319+ Configurable<float > triggerTracksEtaMin{" triggerTracksEtaMin" , -0 .8f , " must match triggerEtaMin in hStrangeCorrelationFilter.cxx" };
320+ Configurable<float > triggerTracksEtaMax{" triggerTracksEtaMax" , 0 .8f , " must match triggerEtaMax in hStrangeCorrelationFilter.cxx" };
321+ Configurable<float > triggerTracksPtMin{" triggerTracksPtMin" , 3 .0f , " must match triggerPtCutMin in hStrangeCorrelationFilter.cxx" };
322+ Configurable<float > triggerTracksPtMax{" triggerTracksPtMax" , 20 .0f , " must match triggerPtCutMax in hStrangeCorrelationFilter.cxx" };
323+ Configurable<int > triggerTracksMinCrossedRows{" triggerTracksMinCrossedRows" , 70 , " must match minTPCNCrossedRows in hStrangeCorrelationFilter.cxx" };
324+ Configurable<bool > triggerTracksRequireITS{" triggerTracksRequireITS" , true , " must match triggerRequireITS in hStrangeCorrelationFilter.cxx" };
325+ Configurable<int > triggerTracksMaxSharedClusters{" triggerTracksMaxSharedClusters" , 200 , " must match triggerMaxTPCSharedClusters in hStrangeCorrelationFilter.cxx" };
326+ Configurable<bool > triggerTracksRequireLayer0{" triggerTracksRequireLayer0" , false , " must match triggerRequireL0 in hStrangeCorrelationFilter.cxx" };
314327 } pairLossK0Configurations;
315328
316329 struct ValidCollision {
@@ -488,6 +501,29 @@ struct HStrangeCorrelation {
488501 bool hasLayer0 = false ;
489502 };
490503
504+ // First-failing-condition breakdown for the stage3->4 gate ("Trigger track, best collision"
505+ // -> "Trigger in TriggerTracks"). Order mirrors the early-return order of isValidTrigger()
506+ // in hStrangeCorrelationFilter.cxx exactly, so "first reason to fail" means the same thing here.
507+ enum PairLossTriggerTracksFailureReason : int {
508+ PairLossTriggerTracksPassed = 0 ,
509+ PairLossTriggerTracksFailEta,
510+ PairLossTriggerTracksFailPt,
511+ PairLossTriggerTracksFailCrossedRows,
512+ PairLossTriggerTracksFailITS,
513+ PairLossTriggerTracksFailSharedClusters,
514+ PairLossTriggerTracksFailLayer0,
515+ PairLossTriggerTracksNReasons
516+ };
517+
518+ static constexpr std::array<std::string_view, PairLossTriggerTracksNReasons> PairLossTriggerTracksFailureNames = {
519+ " Passed all cuts (in TriggerTracks)" ,
520+ " Failed eta window" ,
521+ " Failed pT window" ,
522+ " Failed min TPC crossed rows" ,
523+ " Failed hasITS requirement" ,
524+ " Failed max TPC shared clusters" ,
525+ " Failed ITS layer-0 requirement" };
526+
491527 struct PairLossV0Info {
492528 int64_t globalIndex = -1 ;
493529 int64_t positiveTrackId = -1 ;
@@ -564,6 +600,33 @@ struct HStrangeCorrelation {
564600 .hasLayer0 = static_cast <bool >(TESTBIT (track.itsClusterMap (), 0 ))};
565601 }
566602
603+ // Replicates isValidTrigger() from hStrangeCorrelationFilter.cxx condition-by-condition
604+ // (same early-return order) so that, for a trigger already known to exist in the best
605+ // collision (stage 3), we can tell which single cut is responsible for it not making it
606+ // into the TriggerTracks table (stage 4), instead of only knowing that it failed overall.
607+ int classifyTriggerTracksFailure (PairLossTrackInfo const & info)
608+ {
609+ if (info.eta > pairLossK0Configurations.triggerTracksEtaMax || info.eta < pairLossK0Configurations.triggerTracksEtaMin ) {
610+ return PairLossTriggerTracksFailEta;
611+ }
612+ if (info.pt > pairLossK0Configurations.triggerTracksPtMax || info.pt < pairLossK0Configurations.triggerTracksPtMin ) {
613+ return PairLossTriggerTracksFailPt;
614+ }
615+ if (info.tpcCrossedRows < pairLossK0Configurations.triggerTracksMinCrossedRows ) {
616+ return PairLossTriggerTracksFailCrossedRows;
617+ }
618+ if (info.itsClusters <= 0 && pairLossK0Configurations.triggerTracksRequireITS ) {
619+ return PairLossTriggerTracksFailITS;
620+ }
621+ if (info.tpcSharedClusters > pairLossK0Configurations.triggerTracksMaxSharedClusters ) {
622+ return PairLossTriggerTracksFailSharedClusters;
623+ }
624+ if (!info.hasLayer0 && pairLossK0Configurations.triggerTracksRequireLayer0 ) {
625+ return PairLossTriggerTracksFailLayer0;
626+ }
627+ return PairLossTriggerTracksPassed;
628+ }
629+
567630 PairLossDeltaPhiStarInfo calculateMinimumDeltaPhiStar (PairLossTruthTrackInfo const & trigger, PairLossTruthTrackInfo const & daughter, double magneticField, float decayRadiusCm)
568631 {
569632 PairLossDeltaPhiStarInfo result;
@@ -2166,13 +2229,15 @@ struct HStrangeCorrelation {
21662229 const double pairLossRadiusStep = std::max (static_cast <double >(pairLossK0Configurations.radiusStep ), 0.001 );
21672230 const int pairLossRadiusBins = std::max (1 , static_cast <int >(std::ceil ((pairLossRadiusMaximum - pairLossRadiusMinimum) / pairLossRadiusStep)));
21682231 const AxisSpec axisPairLossRadiusAtMinimum{pairLossRadiusBins, pairLossRadiusMinimum, pairLossRadiusMaximum, " #it{r}_{min} (m)" };
2232+ const AxisSpec axisPairLossTriggerTracksFailureReason{PairLossTriggerTracksNReasons, -0.5 , static_cast <double >(PairLossTriggerTracksNReasons) - 0.5 , " TriggerTracks first-failing condition" };
21692233
21702234 histos.add (" PairLossK0/Event/hCounter" , " K0 pair-loss event counter" , kTH1F , {axisPairLossEventStage});
21712235 histos.add (" PairLossK0/Event/hNRecoCollisions" , " reconstructed collisions per MC collision" , kTH1F , {axisPairLossNRecoCollisions});
21722236 histos.add (" PairLossK0/Stage/hCounts" , " pair-loss diagnostic stage counts" , kTH1F , {axisPairLossStage});
21732237 histos.add (" PairLossK0/Stage/hCountsFindable" , " pair-loss diagnostic stage counts for findable K0" , kTH1F , {axisPairLossStage});
21742238 histos.add (" PairLossK0/Stage/hPhysics" , " stages in h-K0 physics variables" , kTHnF , {axisPairLossStage, axisPairLossTruthDeltaPhi, axisPairLossTruthDeltaEta, axisPairLossTruthK0Pt, axisPairLossTruthTriggerPt, axisPairLossFieldSign});
21752239 histos.add (" PairLossK0/Stage/hClose" , " stages in trigger-daughter close-pair variables" , kTHnF , {axisPairLossStage, axisPairLossMinDeltaPhiStar, axisPairLossDaughterDeltaEta, axisPairLossTruthK0Pt, axisPairLossTruthTriggerPt, axisPairLossFieldSign, axisPairLossChargeProduct});
2240+ histos.add (" PairLossK0/Stage/hTriggerTracksFailureReason" , " first-failing TriggerTracks condition for best-collision triggers, in h-K0 physics variables" , kTHnF , {axisPairLossTriggerTracksFailureReason, axisPairLossTruthDeltaPhi, axisPairLossTruthDeltaEta, axisPairLossTruthK0Pt, axisPairLossTruthTriggerPt});
21762241
21772242 histos.add (" PairLossK0/State/hFinalObjectStatePhysics" , " 00/01/10/11 final trigger-K0 object state" , kTHnF , {axisPairLossFinalObjectState, axisPairLossTruthDeltaPhi, axisPairLossTruthDeltaEta, axisPairLossTruthK0Pt, axisPairLossTruthTriggerPt, axisPairLossFieldSign});
21782243 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});
@@ -2226,6 +2291,11 @@ struct HStrangeCorrelation {
22262291 stageCounts->GetYaxis ()->SetTitle (" Truth-pair entries" );
22272292 stageCountsFindable->GetYaxis ()->SetTitle (" Findable truth-pair entries" );
22282293
2294+ auto triggerTracksFailureHistogram = histos.get <THn>(HIST (" PairLossK0/Stage/hTriggerTracksFailureReason" ));
2295+ for (int i = 0 ; i < PairLossTriggerTracksNReasons; ++i) {
2296+ triggerTracksFailureHistogram->GetAxis (0 )->SetBinLabel (i + 1 , PairLossTriggerTracksFailureNames[i].data ());
2297+ }
2298+
22292299 const std::array<std::string_view, 4 > objectStateLabels = {" 00 neither" , " 01 K0 only" , " 10 trigger only" , " 11 both" };
22302300 for (auto const & histogram : {histos.get <THn>(HIST (" PairLossK0/State/hFinalObjectStatePhysics" )), histos.get <THn>(HIST (" PairLossK0/State/hFinalObjectStateClose" ))}) {
22312301 for (size_t i = 0 ; i < objectStateLabels.size (); ++i) {
@@ -4089,6 +4159,16 @@ struct HStrangeCorrelation {
40894159 }
40904160 }
40914161
4162+ // Only meaningful conditional on the trigger already existing in the best collision
4163+ // (stage 3): this asks *why* that already-found, already-correctly-labelled track
4164+ // does or does not make it into the TriggerTracks table (stage 4).
4165+ if (triggerBestCollision) {
4166+ if (auto const * bestCollisionTrigger = bestTrack (tracksBestCollision, truthTrigger.globalIndex )) {
4167+ const int failureReason = classifyTriggerTracksFailure (*bestCollisionTrigger);
4168+ histos.fill (HIST (" PairLossK0/Stage/hTriggerTracksFailureReason" ), failureReason, truthDeltaPhi, truthDeltaEta, truthK0.pt , truthTrigger.pt );
4169+ }
4170+ }
4171+
40924172 const int finalObjectState = (triggerFinal ? 2 : 0 ) + (v0Final ? 1 : 0 );
40934173 const int trackLevelState = (triggerBestCollision ? 2 : 0 ) + (bothDaughtersBestCollision ? 1 : 0 );
40944174 const int daughterTrackState = (positiveDaughterBestCollision ? 2 : 0 ) + (negativeDaughterBestCollision ? 1 : 0 );
0 commit comments