Project
Loading...
Searching...
No Matches
Clusterer.cxx
Go to the documentation of this file.
1// Copyright 2019-2026 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
11
14
18
19#include <algorithm>
20#include <numeric>
21
22namespace o2::trk
23{
24
25//__________________________________________________
26template <int DetID>
28 float yPlaneMLOT) noexcept
29{
30 const uint8_t rowSpan = *patt++;
31 const uint8_t colSpan = *patt++;
32 const int nBytes = (rowSpan * colSpan + 7) / 8;
33
34 float cogDr{0.f}, cogDc{0.f};
35 int nPix{0}, pixIdx{0};
36 for (int ib = 0; ib < nBytes; ib++) {
37 const uint8_t byte = *patt++;
38 for (int bit = 7; bit >= 0 && pixIdx < rowSpan * colSpan; bit--, pixIdx++) {
39 if (byte & (1 << bit)) {
40 cogDr += pixIdx / colSpan;
41 cogDc += pixIdx % colSpan;
42 nPix++;
43 }
44 }
45 }
46 if (nPix > 1) {
47 cogDr /= nPix;
48 cogDc /= nPix;
49 }
50
51 float x{0.f}, y{0.f}, z{0.f};
52 SegmentationChip::detectorToLocalUnchecked(cluster.row, cluster.col, x, z,
53 cluster.subDetID, cluster.layer, cluster.layer);
54
55 const float pitchRow = (cluster.subDetID == 0) ? SegmentationChip::PitchRowVD : SegmentationChip::PitchRowMLOT;
56 const float pitchCol = (cluster.subDetID == 0) ? SegmentationChip::PitchColVD : SegmentationChip::PitchColMLOT;
57 x -= cogDr * pitchRow;
58 z += cogDc * pitchCol;
59
60 if (cluster.subDetID == 0) {
61 auto cv = SegmentationChip::flatToCurved(cluster.layer, x, 0.f);
62 x = cv.X();
63 y = cv.Y();
64 } else {
65 y = yPlaneMLOT;
66 }
67
68 return {x, y, z};
69}
70
71//__________________________________________________
72template <int DetID>
73void Clusterer<DetID>::process(gsl::span<const Digit> digits,
74 gsl::span<const DigROFRecord> digitROFs,
75 std::vector<ClusterType>& clusters,
76 std::vector<unsigned char>& patterns,
77 std::vector<o2::trkft3::ROFRecord>& clusterROFs,
78 const ConstDigitTruth* digitLabels,
79 ClusterTruth* clusterLabels)
80{
81 if (!mThread) {
82 mThread = std::make_unique<ClustererThread>(this);
83 }
84
86
87 for (size_t iROF = 0; iROF < digitROFs.size(); ++iROF) {
88 const auto& inROF = digitROFs[iROF];
89 const auto outFirst = static_cast<int>(clusters.size());
90 const int first = inROF.getFirstEntry();
91 const int nEntries = inROF.getNEntries();
92
93 if (nEntries == 0) {
94 clusterROFs.emplace_back(inROF.getBCData(), inROF.getROFrame(), outFirst, 0);
95 continue;
96 }
97
98 // Sort digit indices within this ROF by (chipID, col, row) so we can process
99 // chip by chip, column by column -- the same ordering the ALPIDE scanner expects.
100 mSortIdx.resize(nEntries);
101 std::iota(mSortIdx.begin(), mSortIdx.end(), first);
102 std::sort(mSortIdx.begin(), mSortIdx.end(), [&digits](int a, int b) {
103 const auto& da = digits[a];
104 const auto& db = digits[b];
105 if (da.getChipIndex() != db.getChipIndex()) {
106 return da.getChipIndex() < db.getChipIndex();
107 }
108 if (da.getColumn() != db.getColumn()) {
109 return da.getColumn() < db.getColumn();
110 }
111 return da.getRow() < db.getRow();
112 });
113
114 // Process one chip at a time
115 int sliceStart = 0;
116 while (sliceStart < nEntries) {
117 const int chipFirst = sliceStart;
118 const uint16_t chipID = digits[mSortIdx[sliceStart]].getChipIndex();
119 while (sliceStart < nEntries && digits[mSortIdx[sliceStart]].getChipIndex() == chipID) {
120 ++sliceStart;
121 }
122 const int chipN = sliceStart - chipFirst;
123
124 mThread->processChip(digits, chipFirst, chipN, &clusters, &patterns, digitLabels, clusterLabels, geom);
125 }
126
127 clusterROFs.emplace_back(inROF.getBCData(), inROF.getROFrame(),
128 outFirst, static_cast<int>(clusters.size()) - outFirst);
129 }
130}
131
132//__________________________________________________
133template <int DetID>
135 int chipFirst, int chipN,
136 std::vector<ClusterType>* clustersOut,
137 std::vector<unsigned char>* patternsOut,
138 const ConstDigitTruth* labelsDigPtr,
139 ClusterTruth* labelsClusPtr,
142 // chipFirst and chipN are relative to mSortIdx (i.e. mSortIdx[chipFirst..chipFirst+chipN-1]
143 // are the global digit indices for this chip, already sorted by col then row).
144 // We use parent->mSortIdx to resolve the global index of each pixel.
145 const auto& sortIdx = parent->mSortIdx;
147 if (chipN == 1) {
148 finishChipSingleHitFast(digits, sortIdx[chipFirst], labelsDigPtr, labelsClusPtr, geom);
149 } else {
150 initChip(digits, sortIdx[chipFirst], geom);
151 for (int i = chipFirst + 1; i < chipFirst + chipN; ++i) {
152 updateChip(digits, sortIdx[i]);
154 finishChip(digits, labelsDigPtr, labelsClusPtr, geom);
155 }
156
157 // Flush per-thread output into the caller's containers
158 if (!clusters.empty()) {
159 clustersOut->insert(clustersOut->end(), clusters.begin(), clusters.end());
160 clusters.clear();
161 }
162 if (!patterns.empty()) {
163 patternsOut->insert(patternsOut->end(), patterns.begin(), patterns.end());
164 patterns.clear();
165 }
166 if (labelsClusPtr && labels.getNElements()) {
167 labelsClusPtr->mergeAtBack(labels);
168 labels.clear();
169 }
170}
171
172//__________________________________________________
173template <int DetID>
174void Clusterer<DetID>::ClustererThread::initChip(gsl::span<const Digit> digits, uint32_t first, GeometryTGeo* geom)
175{
176 const uint16_t chipID = digits[first].getChipIndex();
177
178 // Determine the number of rows for this chip's sensor type
179 size = constants::moduleMLOT::chip::nRows + 2; // default for ML/OT
180 if (geom) {
181 if (geom->getSubDetID(chipID) == 0) { // VD
182 const int layer = geom->getLayer(chipID);
183 size = constants::VD::petal::layer::nRows[layer] + 2;
184 }
185 }
186
187 delete[] column1;
188 delete[] column2;
189 column1 = new int[size];
190 column2 = new int[size];
191 column1[0] = column1[size - 1] = -1;
192 column2[0] = column2[size - 1] = -1;
193 prev = column1 + 1;
194 curr = column2 + 1;
195 resetColumn(curr);
196
197 pixels.clear();
198 preClusterHeads.clear();
199 preClusterIndices.clear();
200
201 const auto& pix = digits[first];
202 currCol = pix.getColumn();
203 curr[pix.getRow()] = 0;
204 preClusterHeads.push_back(0);
205 preClusterIndices.push_back(0);
206 pixels.emplace_back(-1, first);
207 noLeftCol = true;
208}
209
210//__________________________________________________
211template <int DetID>
212void Clusterer<DetID>::ClustererThread::updateChip(gsl::span<const Digit> digits, uint32_t ip)
213{
214 const auto& pix = digits[ip];
215 uint16_t row = pix.getRow();
216
217 if (currCol != pix.getColumn()) {
218 swapColumnBuffers();
219 resetColumn(curr);
220 noLeftCol = false;
221 if (pix.getColumn() > currCol + 1) {
222 // gap: no connection with previous column
223 currCol = pix.getColumn();
224 addNewPreCluster(ip, row);
225 noLeftCol = true;
226 return;
227 }
228 currCol = pix.getColumn();
229 }
230
231 bool orphan = true;
232
233 if (noLeftCol) {
234 if (curr[row - 1] >= 0) {
235 expandPreCluster(ip, row, curr[row - 1]);
236 return;
237 }
238 } else {
239#ifdef _ALLOW_DIAGONAL_TRK_CLUSTERS_
240 int neighbours[]{curr[row - 1], prev[row], prev[row + 1], prev[row - 1]};
241#else
242 int neighbours[]{curr[row - 1], prev[row]};
243#endif
244 for (auto pci : neighbours) {
245 if (pci < 0) {
246 continue;
247 }
248 if (orphan) {
249 expandPreCluster(ip, row, pci);
250 orphan = false;
251 continue;
252 }
253 // merge two pre-clusters: assign the smaller index to both
254 if (preClusterIndices[pci] < preClusterIndices[curr[row]]) {
255 preClusterIndices[curr[row]] = preClusterIndices[pci];
256 } else {
257 preClusterIndices[pci] = preClusterIndices[curr[row]];
258 }
259 }
260 }
261 if (orphan) {
262 addNewPreCluster(ip, row);
263 }
264}
265
266//__________________________________________________
267template <int DetID>
269 const ConstDigitTruth* labelsDigPtr,
270 ClusterTruth* labelsClusPtr,
271 GeometryTGeo* geom)
272{
273 const uint16_t chipID = digits[pixels[0].second].getChipIndex();
274
275 for (size_t i1 = 0; i1 < preClusterHeads.size(); ++i1) {
276 auto ci = preClusterIndices[i1];
277 if (ci < 0) {
278 continue;
279 }
280 BBox bbox(chipID);
281 int nlab = 0;
282 uint32_t totalCharge = 0;
283 pixArrBuff.clear();
284
285 // Walk the linked list for this pre-cluster head
286 auto collectPixels = [&](int head) {
287 int next = head;
288 while (next >= 0) {
289 const auto& pixEntry = pixels[next];
290 const auto& d = digits[pixEntry.second];
291 uint16_t r = d.getRow(), c = d.getColumn();
292 pixArrBuff.emplace_back(r, c);
293 bbox.adjust(r, c);
294 totalCharge += d.getCharge();
295 if (labelsClusPtr) {
296 fetchMCLabels(pixEntry.second, labelsDigPtr, nlab);
297 }
298 next = pixEntry.first;
299 }
300 };
301
302 collectPixels(preClusterHeads[i1]);
303 preClusterIndices[i1] = -1;
304
305 for (size_t i2 = i1 + 1; i2 < preClusterHeads.size(); ++i2) {
306 if (preClusterIndices[i2] != ci) {
307 continue;
308 }
309 collectPixels(preClusterHeads[i2]);
310 preClusterIndices[i2] = -1;
311 }
312
313 // Determine geometry info
314 int subDetID = -1, layer = -1;
315 if (geom) {
316 subDetID = geom->getSubDetID(chipID);
317 layer = geom->getLayer(chipID);
318 }
319
320 const bool doLabels = (labelsClusPtr != nullptr);
321 if (bbox.isAcceptableSize()) {
322 streamCluster(bbox, pixArrBuff, totalCharge, doLabels, nlab, chipID, subDetID, layer);
323 } else {
324 // Huge cluster: split into MaxRowSpan x MaxColSpan tiles (same as ITS3)
325 auto warnLeft = MaxHugeClusWarn - parent->mNHugeClus;
326 if (warnLeft > 0) {
327 LOGP(warn, "Splitting huge TRK cluster: chipID {}, rows {}:{} cols {}:{}{}",
328 chipID, bbox.rowMin, bbox.rowMax, bbox.colMin, bbox.colMax,
329 warnLeft == 1 ? " (further warnings muted)" : "");
330 parent->mNHugeClus++;
331 }
332 BBox bboxT(chipID);
333 bboxT.colMin = bbox.colMin;
334 do {
335 bboxT.rowMin = bbox.rowMin;
336 bboxT.colMax = std::min(bbox.colMax, uint16_t(bboxT.colMin + o2::itsmft::ClusterPattern::MaxColSpan - 1));
337 do {
338 bboxT.rowMax = std::min(bbox.rowMax, uint16_t(bboxT.rowMin + o2::itsmft::ClusterPattern::MaxRowSpan - 1));
339 std::vector<std::pair<uint16_t, uint16_t>> subPix;
340 uint32_t subCharge = 0;
341 for (const auto& [r, c] : pixArrBuff) {
342 if (bboxT.isInside(r, c)) {
343 subPix.emplace_back(r, c);
344 subCharge += 1;
345 }
346 }
347 if (!subPix.empty()) {
348 streamCluster(bboxT, subPix, subCharge, doLabels, nlab, chipID, subDetID, layer);
349 }
350 bboxT.rowMin = bboxT.rowMax + 1;
351 } while (bboxT.rowMin <= bbox.rowMax);
352 bboxT.colMin = bboxT.colMax + 1;
353 } while (bboxT.colMin <= bbox.colMax);
354 }
355 }
356 // flush per-thread output to the caller via processChip
357}
358
359//__________________________________________________
360template <int DetID>
362 const ConstDigitTruth* labelsDigPtr,
363 ClusterTruth* labelsClusPtr,
364 GeometryTGeo* geom)
365{
366 const auto& d = digits[hit];
367 const uint16_t chipID = d.getChipIndex();
368 const uint16_t row = d.getRow();
369 const uint16_t col = d.getColumn();
370
371 if (labelsClusPtr) {
372 int nlab = 0;
373 fetchMCLabels(hit, labelsDigPtr, nlab);
374 const auto cnt = static_cast<uint32_t>(clusters.size());
375 for (int i = nlab; i--;) {
376 labels.addElement(cnt, labelsBuff[i]);
377 }
378 }
379
380 // 1×1 pattern: rowSpan=1, colSpan=1, one byte = 0x80
381 patterns.emplace_back(1);
382 patterns.emplace_back(1);
383 patterns.emplace_back(0x80);
384
385 ClusterType cluster;
386 cluster.chipID = chipID;
387 cluster.row = row;
388 cluster.col = col;
389 cluster.size = 1;
390 if (geom) {
391 cluster.subDetID = geom->getSubDetID(chipID);
392 cluster.layer = geom->getLayer(chipID);
393 }
394 clusters.emplace_back(cluster);
395}
396
397//__________________________________________________
398template <int DetID>
400 const std::vector<std::pair<uint16_t, uint16_t>>& pixbuf,
401 uint32_t totalCharge,
402 bool doLabels, int nlab,
403 uint16_t chipID, int subDetID, int layer)
404{
405 if (doLabels) {
406 const auto cnt = static_cast<uint32_t>(clusters.size());
407 for (int i = nlab; i--;) {
408 labels.addElement(cnt, labelsBuff[i]); // accumulate in thread-local buffer
409 }
410 }
411
412 const uint16_t rowSpanW = bbox.rowSpan();
413 const uint16_t colSpanW = bbox.colSpan();
414
415 // Encode the pixel pattern bitmap (rowSpan, colSpan, bytes...)
416 std::array<unsigned char, o2::itsmft::ClusterPattern::MaxPatternBytes> patt{};
417 for (const auto& [r, c] : pixbuf) {
418 uint32_t ir = r - bbox.rowMin, ic = c - bbox.colMin;
419 int nbit = ir * colSpanW + ic;
420 patt[nbit >> 3] |= (0x1 << (7 - (nbit % 8)));
421 }
422 patterns.emplace_back(static_cast<unsigned char>(rowSpanW));
423 patterns.emplace_back(static_cast<unsigned char>(colSpanW));
424 int nBytes = (rowSpanW * colSpanW + 7) / 8;
425 patterns.insert(patterns.end(), patt.begin(), patt.begin() + nBytes);
426
427 ClusterType cluster;
428 cluster.chipID = chipID;
429 cluster.row = bbox.rowMin;
430 cluster.col = bbox.colMin;
431 cluster.size = static_cast<uint16_t>(pixbuf.size());
432 cluster.subDetID = static_cast<int16_t>(subDetID);
433 cluster.layer = static_cast<int16_t>(layer);
434 clusters.emplace_back(cluster);
435}
436
437//__________________________________________________
438template <int DetID>
439void Clusterer<DetID>::ClustererThread::fetchMCLabels(uint32_t digID, const ConstDigitTruth* labelsDig, int& nfilled)
440{
441 if (nfilled >= MaxLabels) {
442 return;
443 }
444 if (!labelsDig || digID >= labelsDig->getIndexedSize()) {
445 return;
446 }
447 const auto& lbls = labelsDig->getLabels(digID);
448 for (int i = lbls.size(); i--;) {
449 int ic = nfilled;
450 for (; ic--;) {
451 if (labelsBuff[ic] == lbls[i]) {
452 return; // already present
453 }
454 }
455 labelsBuff[nfilled++] = lbls[i];
456 if (nfilled >= MaxLabels) {
457 break;
458 }
459 }
460}
461
464
465} // namespace o2::trk
std::vector< std::string > labels
int32_t i
int collectPixels(int which, int N, double *xyDxy, double *q)
uint32_t col
Definition RawData.h:4
uint32_t c
Definition RawData.h:2
Definition of the SegmentationChipclass.
Definition of the TRK cluster finder.
gsl::span< const TruthElement > getLabels(uint32_t dataindex) const
void mergeAtBack(MCTruthContainer< TruthElement > const &other)
static constexpr uint8_t MaxRowSpan
static constexpr uint8_t MaxColSpan
std::vector< int > mSortIdx
reusable per-ROF sort buffer
Definition Clusterer.h:182
static o2::math_utils::Point3D< float > getClusterLocalCoordinates(const ClusterType &cluster, const uint8_t *patt, float yPlaneMLOT=0.f) noexcept
Definition Clusterer.cxx:27
virtual void process(gsl::span< const Digit > digits, gsl::span< const DigROFRecord > digitROFs, std::vector< ClusterType > &clusters, std::vector< unsigned char > &patterns, std::vector< o2::trkft3::ROFRecord > &clusterROFs, const ConstDigitTruth *digitLabels=nullptr, ClusterTruth *clusterLabels=nullptr)
Definition Clusterer.cxx:73
int getSubDetID(int index) const
int getLayer(int index) const
local layer index within the sub-detector (0-based per VD/MLOT)
static GeometryTGeo * Instance()
static constexpr math_utils::Vector2D< float > flatToCurved(int layer, float xFlat, float yFlat) noexcept
static void detectorToLocalUnchecked(int row, int col, float &xRow, float &zCol, int subDetID, int layer, int disk) noexcept
static constexpr float PitchColVD
static constexpr float PitchColMLOT
static constexpr float PitchRowMLOT
static constexpr float PitchRowVD
GLint GLenum GLint x
Definition glcorearb.h:403
GLsizeiptr size
Definition glcorearb.h:659
GLint first
Definition glcorearb.h:399
GLboolean GLboolean GLboolean b
Definition glcorearb.h:1233
GLint y
Definition glcorearb.h:270
GLint GLint GLsizei GLint GLenum GLenum const void * pixels
Definition glcorearb.h:275
GLenum GLuint GLint GLint layer
Definition glcorearb.h:1310
GLboolean r
Definition glcorearb.h:1233
GLboolean GLboolean GLboolean GLboolean a
Definition glcorearb.h:1233
GLdouble GLdouble GLdouble z
Definition glcorearb.h:843
bool isInside(uint16_t r, uint16_t c) const
Definition Clusterer.h:66
uint16_t chipID
Definition Cluster.h:28
o2::InteractionRecord ir(0, 0)
std::vector< Cluster > clusters
std::vector< Digit > digits
std::vector< int > row