@@ -525,6 +525,10 @@ int32_t GPUQA::InitQACreateHistograms()
525525 createHist (mClusters [i], name, name, AXIS_BINS [4 ], binsPt.get ());
526526 }
527527
528+ const int32_t maxPads = GPUTPCGeometry::NPads (GPUTPCGeometry::NROWS - 1 );
529+ createHist (mLowestRowDifferenceVsPad , " lowest_row_difference_vs_pad" , " Lowest cluster row difference;Pad of lowest reconstructed cluster;Lowest reconstructed row - lowest MC row" , maxPads, -0.5 , maxPads - 0.5 , 2 * GPUTPCGeometry::NROWS - 1 , -GPUTPCGeometry::NROWS + 0.5 , GPUTPCGeometry::NROWS - 0.5 );
530+ createHist (mLowestRowTrackMultiplicity , " lowest_row_track_multiplicity" , " Track multiplicity for missing inner clusters;Reconstructed tracks with the same MC label;Selected reconstructed tracks" , 100 , -0.5 , 99.5 );
531+
528532 createHist (mPadRow [0 ], " padrow0" , " padrow0" , GPUTPCGeometry::NROWS - PADROW_CHECK_MINCLS , 0 , GPUTPCGeometry::NROWS - 1 - PADROW_CHECK_MINCLS , GPUTPCGeometry::NROWS - PADROW_CHECK_MINCLS , 0 , GPUTPCGeometry::NROWS - 1 - PADROW_CHECK_MINCLS );
529533 createHist (mPadRow [1 ], " padrow1" , " padrow1" , 100 .f , -0 .2f , 0 .2f , GPUTPCGeometry::NROWS - PADROW_CHECK_MINCLS , 0 , GPUTPCGeometry::NROWS - 1 - PADROW_CHECK_MINCLS );
530534 createHist (mPadRow [2 ], " padrow2" , " padrow2" , 100 .f , -0 .2f , 0 .2f , GPUTPCGeometry::NROWS - PADROW_CHECK_MINCLS , 0 , GPUTPCGeometry::NROWS - 1 - PADROW_CHECK_MINCLS );
@@ -1103,12 +1107,35 @@ void GPUQA::RunQA(bool matchOnly, const std::vector<o2::tpc::TrackTPC>* tracksEx
11031107 }
11041108 }
11051109 if (mTracking ->mIOPtrs .nMergedTracks && clNative) {
1110+ // Keep these per MC label: the reverse label map retains only one clone.
1111+ struct LowestRowInfo {
1112+ int32_t row = GPUTPCGeometry::NROWS ;
1113+ int32_t nTracks = 0 ;
1114+ };
1115+ std::vector<std::vector<LowestRowInfo>> lowestRowInfo (GetNMCCollissions ());
1116+ for (uint32_t iCol = 0 ; iCol < GetNMCCollissions (); iCol++) {
1117+ lowestRowInfo[iCol].resize (GetNMCTracks (iCol));
1118+ }
1119+ for (uint32_t i = 0 ; i < nReconstructedTracks; i++) {
1120+ const auto & label = mTrackMCLabels [i];
1121+ // Count all reconstructed segments, including fake-flagged label matches,
1122+ // without applying the selection cuts to the other segments.
1123+ if (mTracking ->mIOPtrs .mergedTracks [i].OK () && label.isValid () && !label.isNoise ()) {
1124+ GetMCTrackObj (lowestRowInfo, label).nTracks ++;
1125+ }
1126+ }
11061127 std::fill (lowestPadRow.begin (), lowestPadRow.end (), 255 );
11071128 for (uint32_t iSector = 0 ; iSector < GPUTPCGeometry::NSECTORS ; iSector++) {
11081129 for (uint32_t iRow = 0 ; iRow < GPUTPCGeometry::NROWS ; iRow++) {
11091130 for (uint32_t iCl = 0 ; iCl < clNative->nClusters [iSector][iRow]; iCl++) {
11101131 int32_t i = clNative->clusterOffset [iSector][iRow] + iCl;
11111132 for (int32_t j = 0 ; j < GetMCLabelNID (i); j++) {
1133+ const mcLabelI_t label = GetMCLabel (i, j);
1134+ if (!label.isValid () || label.isNoise ()) {
1135+ continue ;
1136+ }
1137+ auto & info = GetMCTrackObj (lowestRowInfo, label);
1138+ info.row = std::min (info.row , (int32_t )iRow);
11121139 uint32_t trackId = GetMCTrackObj (mTrackMCLabelsReverse , GetMCLabel (i, j));
11131140 if (trackId < lowestPadRow.size () && lowestPadRow[trackId] > iRow) {
11141141 lowestPadRow[trackId] = iRow;
@@ -1119,6 +1146,28 @@ void GPUQA::RunQA(bool matchOnly, const std::vector<o2::tpc::TrackTPC>* tracksEx
11191146 }
11201147 for (uint32_t i = 0 ; i < mTracking ->mIOPtrs .nMergedTracks ; i++) {
11211148 const auto & trk = mTracking ->mIOPtrs .mergedTracks [i];
1149+ const auto & label = mTrackMCLabels [i];
1150+ if (trk.OK () && trk.NClustersFitted () > 50 && CAMath::Abs (trk.GetParam ().GetQPt ()) < 1 .f && label.isValid () && !label.isNoise () &&
1151+ (mMCTrackMin == -1 || label.getTrackID () >= mMCTrackMin ) && (mMCTrackMax == -1 || label.getTrackID () < mMCTrackMax )) {
1152+ const auto & info = GetMCTrackObj (lowestRowInfo, label);
1153+ if (info.row < 10 ) {
1154+ const GPUTPCGMMergedTrackHit* lowestCl = nullptr ;
1155+ for (uint32_t j = 0 ; j < trk.NClusters (); j++) {
1156+ const auto & cl = mTracking ->mIOPtrs .mergedTrackHits [trk.FirstClusterRef () + j];
1157+ if (!(cl.state & GPUTPCGMMergedTrackHit::flagReject) && (!lowestCl || cl.row < lowestCl->row )) {
1158+ lowestCl = &cl;
1159+ }
1160+ }
1161+ if (lowestCl) {
1162+ const float pad = clNative->clustersLinear [lowestCl->num ].getPad ();
1163+ const int32_t difference = (int32_t )lowestCl->row - info.row ;
1164+ mLowestRowDifferenceVsPad ->Fill (pad, difference);
1165+ if (CAMath::Abs (difference) > 5 && pad > 5 .f && pad < GPUTPCGeometry::NPads (lowestCl->row ) - 5 .f ) {
1166+ mLowestRowTrackMultiplicity ->Fill (info.nTracks );
1167+ }
1168+ }
1169+ }
1170+ }
11221171 if (trk.OK () && lowestPadRow[i] != 255 && trk.NClustersFitted () >= PADROW_CHECK_MINCLS && CAMath::Abs (trk.GetParam ().GetQPt ()) < 1.0 ) {
11231172 const auto & lowestCl = mTracking ->mIOPtrs .mergedTrackHits [trk.FirstClusterRef ()].row < mTracking ->mIOPtrs .mergedTrackHits [trk.FirstClusterRef () + trk.NClusters () - 1 ].row ? mTracking ->mIOPtrs .mergedTrackHits [trk.FirstClusterRef ()] : mTracking ->mIOPtrs .mergedTrackHits [trk.FirstClusterRef () + trk.NClusters () - 1 ];
11241173 const int32_t lowestRow = lowestCl.row ;
@@ -2912,6 +2961,17 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout)
29122961 }
29132962 }
29142963
2964+ if (mQATasks & taskClusterAttach) {
2965+ for (TH1 * h : {static_cast <TH1 *>(mLowestRowDifferenceVsPad ), static_cast <TH1 *>(mLowestRowTrackMultiplicity )}) {
2966+ if (tout && !mConfig .inputHistogramsOnly ) {
2967+ h->Write ();
2968+ }
2969+ if (qcout) {
2970+ qcout->Add (h);
2971+ }
2972+ }
2973+ }
2974+
29152975 // Process cluster count statistics
29162976 if ((mQATasks & taskClusterCounts) && !mHaveExternalHists && !mConfig .clusterRejectionHistograms && !mConfig .inputHistogramsOnly ) {
29172977 DoClusterCounts (attachClusterCounts);
0 commit comments