Skip to content

Commit f51657c

Browse files
committed
TPC TimeSeries: clamp fix + ITS cluster sizes + TRD tracklets + TRD matching
Phase 0.2 — binning overflow fix: - Replace bounds-check-and-return with std::clamp on all 4 bin indices - Edge bins act as saturated overflow; no tracks silently dropped Phase 0.3 D1 — ITS cluster sizes (per-track, unbinned): - itsClusterSizes: packed 4-bit per layer (bit 28 kSharedClusters masked) - itsHasSharedClusters, itsPattern: 7-bit layer hit pattern Phase 0.3 D2 — TRD tracklet objects (per-track, unbinned): - Native Tracklet64[6] and CalibratedTracklet[6] per layer - trdPattern (6-bit validity mask), nTRDTracklets - requestTRDTracklets added to DataRequest Phase 0.3 D3 — TRD matching fraction (per-TF): - nITSTPCBasedPVContributors, nITSTPCWithTRDPVContributors, fracTRD - NaN for zero denominator. ClassDefNV 7 -> 8.
1 parent f254791 commit f51657c

2 files changed

Lines changed: 123 additions & 4 deletions

File tree

Detectors/Calibration/include/DetectorsCalibration/IntegratedClusterCalibrator.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,10 @@ struct TimeSeriesITSTPC {
365365
std::vector<float> vertexY_ITSTPC_RMS; ///< vertex y RMS with ITS-TPC cut (nContributorsITS + nContributorsITSTPC)<0.95
366366
std::vector<float> vertexZ_ITSTPC_RMS; ///< vertex z RMS with ITS-TPC cut (nContributorsITS + nContributorsITSTPC)<0.95
367367

368+
std::vector<float> nITSTPCBasedPVContributors; ///< number of ITS-TPC-based PV contributors (denominator for TRD matching fraction)
369+
std::vector<float> nITSTPCWithTRDPVContributors; ///< number of ITS-TPC-TRD PV contributors (numerator for TRD matching fraction)
370+
std::vector<float> fracTRD; ///< fraction of ITS-TPC PV contributors with TRD match (NaN if denominator=0)
371+
368372
int quantileValues = 23; ///<! number of values in quantiles + truncated mean (hardcoded for the moment)
369373
std::vector<float> nVertexContributors_Quantiles; ///< number of primary vertices for quantiles 0.1, 0.2, ... 0.9 and truncated mean values 0.05->0.95, 0.1->0.9, 0.2->0.8
370374

@@ -498,12 +502,15 @@ struct TimeSeriesITSTPC {
498502
vertexX_ITSTPC_RMS.resize(nTotalVtx);
499503
vertexY_ITSTPC_RMS.resize(nTotalVtx);
500504
vertexZ_ITSTPC_RMS.resize(nTotalVtx);
505+
nITSTPCBasedPVContributors.resize(nTotalVtx);
506+
nITSTPCWithTRDPVContributors.resize(nTotalVtx);
507+
fracTRD.resize(nTotalVtx);
501508

502509
const int nTotalQ = quantileValues * nTotal / mTSTPC.getNBins();
503510
nVertexContributors_Quantiles.resize(nTotalQ);
504511
}
505512

506-
ClassDefNV(TimeSeriesITSTPC, 7);
513+
ClassDefNV(TimeSeriesITSTPC, 8);
507514
};
508515

509516
} // end namespace tpc

Detectors/TPC/workflow/src/TPCTimeSeriesSpec.cxx

Lines changed: 115 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@
4141
#include <chrono>
4242
#include "DataFormatsTPC/PIDResponse.h"
4343
#include "DataFormatsITS/TrackITS.h"
44+
#include "DataFormatsTRD/TrackTRD.h"
45+
#include "DataFormatsTRD/Tracklet64.h"
46+
#include "DataFormatsTRD/CalibratedTracklet.h"
4447
#include "TROOT.h"
4548
#include "ReconstructionDataFormats/MatchInfoTOF.h"
4649
#include "DataFormatsTOF/Cluster.h"
@@ -63,6 +66,13 @@ namespace tpc
6366
class TPCTimeSeries : public Task
6467
{
6568
public:
69+
/// D2: per-track TRD tracklet lookup data
70+
struct TRDTrackletData {
71+
uint8_t trdPattern = 0;
72+
uint8_t nTRDTracklets = 0;
73+
int trackletIndices[6] = {-1, -1, -1, -1, -1, -1};
74+
};
75+
6676
/// \constructor
6777
TPCTimeSeries(std::shared_ptr<o2::base::GRPGeomRequest> req, const bool disableWriter, const o2::base::Propagator::MatCorrType matType, const bool enableUnbinnedWriter, const bool tpcOnly, std::shared_ptr<o2::globaltracking::DataRequest> dr) : mCCDBRequest(req), mDisableWriter(disableWriter), mMatType(matType), mUnbinnedWriter(enableUnbinnedWriter), mTPCOnly(tpcOnly), mDataRequest(dr) {};
6878

@@ -303,6 +313,38 @@ class TPCTimeSeries : public Task
303313
// find nearest vertex of tracks which have no vertex assigned
304314
findNearesVertex(tracksTPC, vertices);
305315

316+
// D2: build TPC track index → TRD tracklet data map (for unbinned output)
317+
// For each TPC track that has a TRD match, store the TrackTRD tracklet indices
318+
std::unordered_map<unsigned int, TRDTrackletData> tpcToTRDMap;
319+
auto trdTracklets = mTPCOnly ? gsl::span<const o2::trd::Tracklet64>() : recoData.getTRDTracklets();
320+
auto trdCalibTracklets = mTPCOnly ? gsl::span<const o2::trd::CalibratedTracklet>() : recoData.getTRDCalibratedTracklets();
321+
if (mUnbinnedWriter && !mTPCOnly) {
322+
// scan ITS-TPC-TRD tracks
323+
auto itstpctrdTracks = recoData.getITSTPCTRDTracks<o2::trd::TrackTRD>();
324+
for (unsigned int ig = 0; ig < itstpctrdTracks.size(); ++ig) {
325+
auto gid = GTrackID(ig, GTrackID::ITSTPCTRD);
326+
auto refTPC = recoData.getTPCContributorGID(gid);
327+
if (!refTPC.isIndexSet()) {
328+
continue;
329+
}
330+
auto refTRD = recoData.getSingleDetectorRefs(gid)[GTrackID::TRD];
331+
if (!refTRD.isIndexSet()) {
332+
continue;
333+
}
334+
const auto& trdTrack = recoData.getTrack<o2::trd::TrackTRD>(refTRD);
335+
TRDTrackletData trdData;
336+
for (int iLay = 0; iLay < 6; ++iLay) {
337+
auto trkltId = trdTrack.getTrackletIndex(iLay);
338+
if (trkltId >= 0) {
339+
trdData.trdPattern |= (1 << iLay);
340+
trdData.nTRDTracklets++;
341+
trdData.trackletIndices[iLay] = trkltId;
342+
}
343+
}
344+
tpcToTRDMap[refTPC] = trdData;
345+
}
346+
}
347+
306348
// getting cluster references for cluster bitmask
307349
if (mUnbinnedWriter) {
308350
mTPCTrackClIdx = pc.inputs().get<gsl::span<o2::tpc::TPCClRefElem>>("trackTPCClRefs");
@@ -472,7 +514,7 @@ class TPCTimeSeries : public Task
472514
auto myThread = [&](int iThread) {
473515
for (size_t i = iThread; i < loopEnd; i += mNThreads) {
474516
if (acceptTrack(tracksTPC[i])) {
475-
fillDCA(tracksTPC, tracksITSTPC, vertices, i, iThread, indicesITSTPC, tracksITS, idxTPCTrackToTOFCluster, tofClusters);
517+
fillDCA(tracksTPC, tracksITSTPC, vertices, i, iThread, indicesITSTPC, tracksITS, idxTPCTrackToTOFCluster, tofClusters, tpcToTRDMap, trdTracklets, trdCalibTracklets);
476518
}
477519
}
478520
};
@@ -489,7 +531,7 @@ class TPCTimeSeries : public Task
489531
auto myThread = [&](int iThread) {
490532
for (size_t i = iThread; i < loopEnd; i += mNThreads) {
491533
if (acceptTrack(tracksTPC[i])) {
492-
fillDCA(tracksTPC, tracksITSTPC, vertices, i, iThread, indicesITSTPC, tracksITS, idxTPCTrackToTOFCluster, tofClusters);
534+
fillDCA(tracksTPC, tracksITSTPC, vertices, i, iThread, indicesITSTPC, tracksITS, idxTPCTrackToTOFCluster, tofClusters, tpcToTRDMap, trdTracklets, trdCalibTracklets);
493535
}
494536
}
495537
};
@@ -1133,7 +1175,7 @@ class TPCTimeSeries : public Task
11331175
return isGoodTrack;
11341176
}
11351177

1136-
void fillDCA(const gsl::span<const TrackTPC> tracksTPC, const gsl::span<const o2::dataformats::TrackTPCITS> tracksITSTPC, const gsl::span<const o2::dataformats::PrimaryVertex> vertices, const int iTrk, const int iThread, const std::unordered_map<unsigned int, std::array<int, 2>>& indicesITSTPC, const gsl::span<const o2::its::TrackITS> tracksITS, const std::vector<std::tuple<int, float, float, o2::track::TrackLTIntegral, double, float, unsigned int, unsigned short>>& idxTPCTrackToTOFCluster, const gsl::span<const o2::tof::Cluster> tofClusters)
1178+
void fillDCA(const gsl::span<const TrackTPC> tracksTPC, const gsl::span<const o2::dataformats::TrackTPCITS> tracksITSTPC, const gsl::span<const o2::dataformats::PrimaryVertex> vertices, const int iTrk, const int iThread, const std::unordered_map<unsigned int, std::array<int, 2>>& indicesITSTPC, const gsl::span<const o2::its::TrackITS> tracksITS, const std::vector<std::tuple<int, float, float, o2::track::TrackLTIntegral, double, float, unsigned int, unsigned short>>& idxTPCTrackToTOFCluster, const gsl::span<const o2::tof::Cluster> tofClusters, const std::unordered_map<unsigned int, TRDTrackletData>& tpcToTRDMap, const gsl::span<const o2::trd::Tracklet64> trdTracklets, const gsl::span<const o2::trd::CalibratedTracklet> trdCalibTracklets)
11371179
{
11381180
const auto& trackFull = tracksTPC[iTrk];
11391181
const bool isGoodTrack = checkTrack(trackFull);
@@ -1179,6 +1221,7 @@ class TPCTimeSeries : public Task
11791221
return;
11801222
}
11811223

1224+
// Saturate bin indices — edge bins act as overflow (Phase 0.2 fix)
11821225
const int tglBin = std::clamp(static_cast<int>(mTglBins * std::abs(trackTmp.getTgl()) / mMaxTgl) + mPhiBins,
11831226
mPhiBins, mPhiBins + mTglBins - 1);
11841227
const int phiBin = std::clamp(static_cast<int>(mPhiBins * trackTmp.getPhi() / o2::constants::math::TwoPI),
@@ -1354,6 +1397,42 @@ class TPCTimeSeries : public Task
13541397
const float chi2match_ITSTPC = hasITSTPC ? tracksITSTPC[idxITSTPC.front()].getChi2Match() : -1;
13551398
const int nClITS = idxITSCheck ? tracksITS[idxITSTrack].getNClusters() : -1;
13561399
const int chi2ITS = idxITSCheck ? tracksITS[idxITSTrack].getChi2() : -1;
1400+
// D1: ITS cluster sizes (4-bit per layer, mask bit 28 = kSharedClusters)
1401+
const uint32_t itsClusterSizes = idxITSCheck ? (static_cast<uint32_t>(tracksITS[idxITSTrack].getClusterSizes()) & 0x0FFFFFFFu) : 0u;
1402+
const bool itsHasSharedClusters = idxITSCheck ? tracksITS[idxITSTrack].hasSharedClusters() : false;
1403+
const uint32_t itsPattern = idxITSCheck ? (tracksITS[idxITSTrack].getPattern() & 0x7Fu) : 0u;
1404+
1405+
// D2: TRD tracklet data — flat arrays per layer
1406+
uint8_t trdPattern = 0;
1407+
uint8_t nTRDTracklets = 0;
1408+
std::vector<uint64_t> trdTrackletWords(6, 0); // raw Tracklet64 words
1409+
std::vector<int16_t> trdQ0(6, -1); // charge slice 0
1410+
std::vector<int16_t> trdQ1(6, -1); // charge slice 1
1411+
std::vector<int16_t> trdQ2(6, -1); // charge slice 2
1412+
std::vector<float> trdCalibX(6, 0.f); // calibrated x
1413+
std::vector<float> trdCalibY(6, 0.f); // calibrated y
1414+
std::vector<float> trdCalibZ(6, 0.f); // calibrated z
1415+
auto itTRD = tpcToTRDMap.find(iTrk);
1416+
if (itTRD != tpcToTRDMap.end()) {
1417+
const auto& trdData = itTRD->second;
1418+
trdPattern = trdData.trdPattern;
1419+
nTRDTracklets = trdData.nTRDTracklets;
1420+
for (int iLay = 0; iLay < 6; ++iLay) {
1421+
if (trdData.trackletIndices[iLay] >= 0) {
1422+
const auto& trklt = trdTracklets[trdData.trackletIndices[iLay]];
1423+
trdTrackletWords[iLay] = trklt.getTrackletWord();
1424+
trdQ0[iLay] = trklt.getQ0();
1425+
trdQ1[iLay] = trklt.getQ1();
1426+
trdQ2[iLay] = trklt.getQ2();
1427+
if (trdData.trackletIndices[iLay] < static_cast<int>(trdCalibTracklets.size())) {
1428+
const auto& ctrklt = trdCalibTracklets[trdData.trackletIndices[iLay]];
1429+
trdCalibX[iLay] = ctrklt.getX();
1430+
trdCalibY[iLay] = ctrklt.getY();
1431+
trdCalibZ[iLay] = ctrklt.getZ();
1432+
}
1433+
}
1434+
}
1435+
}
13571436
int typeSide = 2; // A- and C-Side cluster
13581437
if (trackFull.hasASideClustersOnly()) {
13591438
typeSide = 0;
@@ -1488,6 +1567,19 @@ class TPCTimeSeries : public Task
14881567
<< "mX_ITS=" << mx_ITS
14891568
<< "nClITS=" << nClITS
14901569
<< "chi2ITS=" << chi2ITS
1570+
<< "itsClusterSizes=" << itsClusterSizes
1571+
<< "itsHasSharedClusters=" << itsHasSharedClusters
1572+
<< "itsPattern=" << itsPattern
1573+
// D2: TRD tracklet data
1574+
<< "trdPattern=" << trdPattern
1575+
<< "nTRDTracklets=" << nTRDTracklets
1576+
<< "trdTrackletWords=" << trdTrackletWords
1577+
<< "trdQ0=" << trdQ0
1578+
<< "trdQ1=" << trdQ1
1579+
<< "trdQ2=" << trdQ2
1580+
<< "trdCalibX=" << trdCalibX
1581+
<< "trdCalibY=" << trdCalibY
1582+
<< "trdCalibZ=" << trdCalibZ
14911583
<< "chi2match_ITSTPC=" << chi2match_ITSTPC
14921584
<< "PID=" << trkOrig.getPID().getID()
14931585
// TPC cov at vertex (without vertex constrained)
@@ -1680,6 +1772,7 @@ class TPCTimeSeries : public Task
16801772

16811773
std::unordered_map<int, int> nContributors_ITS; // ITS: vertex ID -> n contributors
16821774
std::unordered_map<int, int> nContributors_ITSTPC; // ITS-TPC (and ITS-TPC-TRD, ITS-TPC-TOF, ITS-TPC-TRD-TOF): vertex ID -> n contributors
1775+
std::unordered_map<int, int> nContributors_TRD; // ITS-TPC-TRD (and ITS-TPC-TRD-TOF): vertex ID -> n TRD-matched PV contributors
16831776

16841777
// loop over collisions
16851778
if (!vertices.empty()) {
@@ -1700,6 +1793,10 @@ class TPCTimeSeries : public Task
17001793
if (refITSTPC.isIndexSet()) {
17011794
indicesITSTPC_vtx[refITSTPC] = vID;
17021795
++nContributors_ITSTPC[vID];
1796+
// count TRD-matched PV contributors
1797+
if (source == TrkSrc::ITSTPCTRD || source == TrkSrc::ITSTPCTRDTOF) {
1798+
++nContributors_TRD[vID];
1799+
}
17031800
} else {
17041801
++nContributors_ITS[vID];
17051802
}
@@ -1761,6 +1858,17 @@ class TPCTimeSeries : public Task
17611858
mBufferDCA.vertexY_ITSTPC_RMS.front() = avgVtxITSTPC[1].getStdDev();
17621859
mBufferDCA.vertexZ_ITSTPC_RMS.front() = avgVtxITSTPC[2].getStdDev();
17631860

1861+
// TRD matching fraction (summed over all vertices in this TF)
1862+
int sumITSTPCBased = 0;
1863+
int sumWithTRD = 0;
1864+
for (int ivtx = 0; ivtx < vertices.size(); ++ivtx) {
1865+
sumITSTPCBased += nContributors_ITSTPC[ivtx];
1866+
sumWithTRD += nContributors_TRD[ivtx];
1867+
}
1868+
mBufferDCA.nITSTPCBasedPVContributors.front() = sumITSTPCBased;
1869+
mBufferDCA.nITSTPCWithTRDPVContributors.front() = sumWithTRD;
1870+
mBufferDCA.fracTRD.front() = (sumITSTPCBased > 0) ? static_cast<float>(sumWithTRD) / sumITSTPCBased : std::nanf("");
1871+
17641872
// quantiles and truncated mean
17651873
RobustAverage avg(vertices.size(), false);
17661874
for (const auto& vtx : vertices) {
@@ -1850,6 +1958,10 @@ o2::framework::DataProcessorSpec getTPCTimeSeriesSpec(const bool disableWriter,
18501958
if (src[GTrackID::TPC]) {
18511959
dataRequest->requestClusters(GTrackID::getSourcesMask("TPC"), useMC);
18521960
}
1961+
// D2: request TRD tracklets for tracks with TRD contribution
1962+
if (srcTracks[GTrackID::ITSTPCTRD] || srcTracks[GTrackID::ITSTPCTRDTOF]) {
1963+
dataRequest->requestTRDTracklets(useMC);
1964+
}
18531965

18541966
bool tpcOnly = srcTracks == GTrackID::getSourcesMask("TPC");
18551967
if (srcTracks.any() && !tpcOnly) {

0 commit comments

Comments
 (0)