From 45ea3f97f5461b5dbf1156143dceaa651a7dec56 Mon Sep 17 00:00:00 2001 From: Christian Sonnabend Date: Wed, 7 Oct 2026 15:50:23 +0200 Subject: [PATCH 1/2] Add GPU QA histograms for missing inner clusters and track clones --- GPU/GPUTracking/qa/GPUQA.cxx | 60 ++++++++++++++++++++++++++++++++++++ GPU/GPUTracking/qa/GPUQA.h | 3 ++ 2 files changed, 63 insertions(+) diff --git a/GPU/GPUTracking/qa/GPUQA.cxx b/GPU/GPUTracking/qa/GPUQA.cxx index 060766b54f3f4..44c4711b04df3 100644 --- a/GPU/GPUTracking/qa/GPUQA.cxx +++ b/GPU/GPUTracking/qa/GPUQA.cxx @@ -525,6 +525,10 @@ int32_t GPUQA::InitQACreateHistograms() createHist(mClusters[i], name, name, AXIS_BINS[4], binsPt.get()); } + const int32_t maxPads = GPUTPCGeometry::NPads(GPUTPCGeometry::NROWS - 1); + 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); + 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); + 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); createHist(mPadRow[1], "padrow1", "padrow1", 100.f, -0.2f, 0.2f, GPUTPCGeometry::NROWS - PADROW_CHECK_MINCLS, 0, GPUTPCGeometry::NROWS - 1 - PADROW_CHECK_MINCLS); 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* tracksEx } } if (mTracking->mIOPtrs.nMergedTracks && clNative) { + // Keep these per MC label: the reverse label map retains only one clone. + struct LowestRowInfo { + int32_t row = GPUTPCGeometry::NROWS; + int32_t nTracks = 0; + }; + std::vector> lowestRowInfo(GetNMCCollissions()); + for (uint32_t iCol = 0; iCol < GetNMCCollissions(); iCol++) { + lowestRowInfo[iCol].resize(GetNMCTracks(iCol)); + } + for (uint32_t i = 0; i < nReconstructedTracks; i++) { + const auto& label = mTrackMCLabels[i]; + // Count all reconstructed segments, including fake-flagged label matches, + // without applying the selection cuts to the other segments. + if (mTracking->mIOPtrs.mergedTracks[i].OK() && label.isValid() && !label.isNoise()) { + GetMCTrackObj(lowestRowInfo, label).nTracks++; + } + } std::fill(lowestPadRow.begin(), lowestPadRow.end(), 255); for (uint32_t iSector = 0; iSector < GPUTPCGeometry::NSECTORS; iSector++) { for (uint32_t iRow = 0; iRow < GPUTPCGeometry::NROWS; iRow++) { for (uint32_t iCl = 0; iCl < clNative->nClusters[iSector][iRow]; iCl++) { int32_t i = clNative->clusterOffset[iSector][iRow] + iCl; for (int32_t j = 0; j < GetMCLabelNID(i); j++) { + const mcLabelI_t label = GetMCLabel(i, j); + if (!label.isValid() || label.isNoise()) { + continue; + } + auto& info = GetMCTrackObj(lowestRowInfo, label); + info.row = std::min(info.row, (int32_t)iRow); uint32_t trackId = GetMCTrackObj(mTrackMCLabelsReverse, GetMCLabel(i, j)); if (trackId < lowestPadRow.size() && lowestPadRow[trackId] > iRow) { lowestPadRow[trackId] = iRow; @@ -1119,6 +1146,28 @@ void GPUQA::RunQA(bool matchOnly, const std::vector* tracksEx } for (uint32_t i = 0; i < mTracking->mIOPtrs.nMergedTracks; i++) { const auto& trk = mTracking->mIOPtrs.mergedTracks[i]; + const auto& label = mTrackMCLabels[i]; + if (trk.OK() && trk.NClustersFitted() > 50 && CAMath::Abs(trk.GetParam().GetQPt()) < 1.f && label.isValid() && !label.isNoise() && + (mMCTrackMin == -1 || label.getTrackID() >= mMCTrackMin) && (mMCTrackMax == -1 || label.getTrackID() < mMCTrackMax)) { + const auto& info = GetMCTrackObj(lowestRowInfo, label); + if (info.row < 10) { + const GPUTPCGMMergedTrackHit* lowestCl = nullptr; + for (uint32_t j = 0; j < trk.NClusters(); j++) { + const auto& cl = mTracking->mIOPtrs.mergedTrackHits[trk.FirstClusterRef() + j]; + if (!(cl.state & GPUTPCGMMergedTrackHit::flagReject) && (!lowestCl || cl.row < lowestCl->row)) { + lowestCl = &cl; + } + } + if (lowestCl) { + const float pad = clNative->clustersLinear[lowestCl->num].getPad(); + const int32_t difference = (int32_t)lowestCl->row - info.row; + mLowestRowDifferenceVsPad->Fill(pad, difference); + if (CAMath::Abs(difference) > 5 && pad > 5.f && pad < GPUTPCGeometry::NPads(lowestCl->row) - 5.f) { + mLowestRowTrackMultiplicity->Fill(info.nTracks); + } + } + } + } if (trk.OK() && lowestPadRow[i] != 255 && trk.NClustersFitted() >= PADROW_CHECK_MINCLS && CAMath::Abs(trk.GetParam().GetQPt()) < 1.0) { 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]; const int32_t lowestRow = lowestCl.row; @@ -2912,6 +2961,17 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) } } + if (mQATasks & taskClusterAttach) { + for (TH1* h : {static_cast(mLowestRowDifferenceVsPad), static_cast(mLowestRowTrackMultiplicity)}) { + if (tout && !mConfig.inputHistogramsOnly) { + h->Write(); + } + if (qcout) { + qcout->Add(h); + } + } + } + // Process cluster count statistics if ((mQATasks & taskClusterCounts) && !mHaveExternalHists && !mConfig.clusterRejectionHistograms && !mConfig.inputHistogramsOnly) { DoClusterCounts(attachClusterCounts); diff --git a/GPU/GPUTracking/qa/GPUQA.h b/GPU/GPUTracking/qa/GPUQA.h index b3c518e9798e8..f257c9a418659 100644 --- a/GPU/GPUTracking/qa/GPUQA.h +++ b/GPU/GPUTracking/qa/GPUQA.h @@ -318,6 +318,9 @@ class GPUQA TPad* mPClRej[3]; TPad* mPClRejP; + TH2F* mLowestRowDifferenceVsPad; + TH1F* mLowestRowTrackMultiplicity; + TH2F* mPadRow[4]; TCanvas* mCPadRow[4]; TPad* mPPadRow[4]; From 29d1fb69933aae1d3810412886db121acaa01332 Mon Sep 17 00:00:00 2001 From: Christian Sonnabend Date: Fri, 9 Oct 2026 22:46:59 +0200 Subject: [PATCH 2/2] Changing to mRecTracks[iCol][i] --- GPU/GPUTracking/qa/GPUQA.cxx | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/GPU/GPUTracking/qa/GPUQA.cxx b/GPU/GPUTracking/qa/GPUQA.cxx index 44c4711b04df3..f3285986c8222 100644 --- a/GPU/GPUTracking/qa/GPUQA.cxx +++ b/GPU/GPUTracking/qa/GPUQA.cxx @@ -1110,20 +1110,11 @@ void GPUQA::RunQA(bool matchOnly, const std::vector* tracksEx // Keep these per MC label: the reverse label map retains only one clone. struct LowestRowInfo { int32_t row = GPUTPCGeometry::NROWS; - int32_t nTracks = 0; }; std::vector> lowestRowInfo(GetNMCCollissions()); for (uint32_t iCol = 0; iCol < GetNMCCollissions(); iCol++) { lowestRowInfo[iCol].resize(GetNMCTracks(iCol)); } - for (uint32_t i = 0; i < nReconstructedTracks; i++) { - const auto& label = mTrackMCLabels[i]; - // Count all reconstructed segments, including fake-flagged label matches, - // without applying the selection cuts to the other segments. - if (mTracking->mIOPtrs.mergedTracks[i].OK() && label.isValid() && !label.isNoise()) { - GetMCTrackObj(lowestRowInfo, label).nTracks++; - } - } std::fill(lowestPadRow.begin(), lowestPadRow.end(), 255); for (uint32_t iSector = 0; iSector < GPUTPCGeometry::NSECTORS; iSector++) { for (uint32_t iRow = 0; iRow < GPUTPCGeometry::NROWS; iRow++) { @@ -1163,7 +1154,7 @@ void GPUQA::RunQA(bool matchOnly, const std::vector* tracksEx const int32_t difference = (int32_t)lowestCl->row - info.row; mLowestRowDifferenceVsPad->Fill(pad, difference); if (CAMath::Abs(difference) > 5 && pad > 5.f && pad < GPUTPCGeometry::NPads(lowestCl->row) - 5.f) { - mLowestRowTrackMultiplicity->Fill(info.nTracks); + mLowestRowTrackMultiplicity->Fill(GetMCTrackObj(mRecTracks, label)); } } }