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