Project
Loading...
Searching...
No Matches
ClustererACTS.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#include <Acts/Clusterization/Clusterization.hpp>
19
20#include <algorithm>
21#include <array>
22#include <numeric>
23
24using namespace o2::trk;
25
26// Data formats for ACTS interface
27struct Cell2D {
28 Cell2D(int rowv, int colv, uint32_t digIdx = 0) : row(rowv), col(colv), digitIdx(digIdx) {}
29 int row, col;
30 uint32_t digitIdx;
31 Acts::Ccl::Label label{Acts::Ccl::NO_LABEL};
32};
33
34int getCellRow(const Cell2D& cell)
35{
36 return cell.row;
37}
38
39int getCellColumn(const Cell2D& cell)
40{
41 return cell.col;
42}
43
44bool operator==(const Cell2D& left, const Cell2D& right)
45{
46 return left.row == right.row && left.col == right.col;
47}
48
49bool cellComp(const Cell2D& left, const Cell2D& right)
50{
51 return (left.row == right.row) ? left.col < right.col : left.row < right.row;
52}
53
54struct Cluster2D {
55 std::vector<Cell2D> cells;
56 std::size_t hash{0};
57};
58
59void clusterAddCell(Cluster2D& cl, const Cell2D& cell)
60{
61 cl.cells.push_back(cell);
62}
63
64void hash(Cluster2D& cl)
65{
66 std::ranges::sort(cl.cells, cellComp);
67 cl.hash = 0;
68 // for (const Cell2D& c : cl.cells) {
69 // boost::hash_combine(cl.hash, c.col);
70 // }
71}
72
74{
75 return left.hash < right.hash;
76}
77
78template <typename RNG>
79void genclusterw(int x, int y, int x0, int y0, int x1, int y1,
80 std::vector<Cell2D>& cells, RNG& rng, double startp = 0.5,
81 double decayp = 0.9)
82{
83 std::vector<Cell2D> add;
84
85 auto maybe_add = [&](int x_, int y_) {
86 Cell2D c(x_, y_);
87 // if (std::uniform_real_distribution<double>()(rng) < startp &&
88 // !rangeContainsValue(cells, c)) {
89 // cells.push_back(c);
90 // add.push_back(c);
91 // }
92 };
93
94 // NORTH
95 if (y < y1) {
96 maybe_add(x, y + 1);
97 }
98 // NORTHEAST
99 if (x < x1 && y < y1) {
100 maybe_add(x + 1, y + 1);
101 }
102 // EAST
103 if (x < x1) {
104 maybe_add(x + 1, y);
105 }
106 // SOUTHEAST
107 if (x < x1 && y > y0) {
108 maybe_add(x + 1, y - 1);
109 }
110 // SOUTH
111 if (y > y0) {
112 maybe_add(x, y - 1);
113 }
114 // SOUTHWEST
115 if (x > x0 && y > y0) {
116 maybe_add(x - 1, y - 1);
117 }
118 // WEST
119 if (x > x0) {
120 maybe_add(x - 1, y);
121 }
122 // NORTHWEST
123 if (x > x0 && y < y1) {
124 maybe_add(x - 1, y + 1);
125 }
126
127 for (Cell2D& c : add) {
128 genclusterw(c.row, c.col, x0, y0, x1, y1, cells, rng, startp * decayp,
129 decayp);
130 }
131}
132
133template <typename RNG>
134Cluster2D gencluster(int x0, int y0, int x1, int y1, RNG& rng,
135 double startp = 0.5, double decayp = 0.9)
136{
137 int x0_ = x0 + 1;
138 int x1_ = x1 - 1;
139 int y0_ = y0 + 1;
140 int y1_ = y1 - 1;
141
142 int x = std::uniform_int_distribution<std::int32_t>(x0_, x1_)(rng);
143 int y = std::uniform_int_distribution<std::int32_t>(y0_, y1_)(rng);
144
145 std::vector<Cell2D> cells = {Cell2D(x, y)};
146 genclusterw(x, y, x0_, y0_, x1_, y1_, cells, rng, startp, decayp);
147
148 Cluster2D cl;
149 cl.cells = std::move(cells);
150
151 return cl;
152}
153
154//__________________________________________________
155void ClustererACTS::process(gsl::span<const Digit> digits,
156 gsl::span<const DigROFRecord> digitROFs,
157 std::vector<o2::trk::Cluster>& clusters,
158 std::vector<unsigned char>& patterns,
159 std::vector<o2::trk::ROFRecord>& clusterROFs,
160 const ConstDigitTruth* digitLabels,
161 ClusterTruth* clusterLabels,
162 gsl::span<const DigMC2ROFRecord> digMC2ROFs,
163 std::vector<o2::trk::MC2ROFRecord>* clusterMC2ROFs)
164{
165 if (!mThread) {
166 mThread = std::make_unique<ClustererThread>(this);
167 }
168
169 auto* geom = o2::trk::GeometryTGeo::Instance();
170
171 for (size_t iROF = 0; iROF < digitROFs.size(); ++iROF) {
172 const auto& inROF = digitROFs[iROF];
173 const auto outFirst = static_cast<int>(clusters.size());
174 const int first = inROF.getFirstEntry();
175 const int nEntries = inROF.getNEntries();
176
177 if (nEntries == 0) {
178 clusterROFs.emplace_back(inROF.getBCData(), inROF.getROFrame(), outFirst, 0);
179 continue;
180 }
181
182 // Sort digit indices within this ROF by (chipID, col, row) so we can process
183 // chip by chip, column by column -- the same ordering the ALPIDE scanner expects.
184 mSortIdx.resize(nEntries);
185 std::iota(mSortIdx.begin(), mSortIdx.end(), first);
186 std::sort(mSortIdx.begin(), mSortIdx.end(), [&digits](int a, int b) {
187 const auto& da = digits[a];
188 const auto& db = digits[b];
189 if (da.getChipIndex() != db.getChipIndex()) {
190 return da.getChipIndex() < db.getChipIndex();
191 }
192 if (da.getColumn() != db.getColumn()) {
193 return da.getColumn() < db.getColumn();
194 }
195 return da.getRow() < db.getRow();
196 });
197
198 // Type aliases for ACTS clustering
199 using Cell = Cell2D;
200 using CellCollection = std::vector<Cell>;
201 using Cluster = Cluster2D;
202 using ClusterCollection = std::vector<Cluster>;
203 static constexpr int GridDim = 2;
204
205 CellCollection cells; // Input collection of cells (pixels) to be clustered
206 Acts::Ccl::ClusteringData data; // Internal data structure used by ACTS clustering algorithm
207 ClusterCollection clsCollection; // Output collection of clusters found by the algorithm
208
209 // Process one chip at a time
210 int sliceStart = 0;
211 while (sliceStart < nEntries) {
212 const int chipFirst = sliceStart;
213 const uint16_t chipID = digits[mSortIdx[sliceStart]].getChipIndex();
214 while (sliceStart < nEntries && digits[mSortIdx[sliceStart]].getChipIndex() == chipID) {
215 ++sliceStart;
216 }
217 const int chipN = sliceStart - chipFirst;
218
219 // Fill cells from digits for this chip
220 cells.clear();
221 data.clear();
222 clsCollection.clear();
223 cells.reserve(chipN);
224 for (int i = chipFirst; i < chipFirst + chipN; ++i) {
225 const auto& digit = digits[mSortIdx[i]];
226 cells.emplace_back(digit.getRow(), digit.getColumn(), mSortIdx[i]);
227 }
228
229 LOG(debug) << "Clustering with ACTS on chip " << chipID << " " << cells.size() << " digits";
230 Acts::Ccl::createClusters<CellCollection, ClusterCollection, GridDim>(data,
231 cells,
232 clsCollection,
233 Acts::Ccl::DefaultConnect<Cell, GridDim>(false));
234
235 LOG(debug) << " found " << clsCollection.size() << " clusters";
236
237 // Convert ACTS clusters to O2 clusters
238 for (const auto& actsCluster : clsCollection) {
239 if (actsCluster.cells.empty()) {
240 continue;
241 }
242
243 // Calculate bounding box
244 uint16_t rowMin = static_cast<uint16_t>(actsCluster.cells[0].row);
245 uint16_t rowMax = rowMin;
246 uint16_t colMin = static_cast<uint16_t>(actsCluster.cells[0].col);
247 uint16_t colMax = colMin;
248
249 for (const auto& cell : actsCluster.cells) {
250 rowMin = std::min(rowMin, static_cast<uint16_t>(cell.row));
251 rowMax = std::max(rowMax, static_cast<uint16_t>(cell.row));
252 colMin = std::min(colMin, static_cast<uint16_t>(cell.col));
253 colMax = std::max(colMax, static_cast<uint16_t>(cell.col));
254 }
255
256 const uint16_t rowSpan = rowMax - rowMin + 1;
257 const uint16_t colSpan = colMax - colMin + 1;
258
259 // Check if cluster needs splitting (too large for pattern encoding)
260 const bool isHuge = rowSpan > o2::itsmft::ClusterPattern::MaxRowSpan ||
262
263 if (isHuge) {
264 // Split huge cluster into MaxRowSpan x MaxColSpan tiles
265 LOG(warning) << "Splitting huge TRK cluster: chipID " << chipID
266 << ", rows " << rowMin << ":" << rowMax
267 << " cols " << colMin << ":" << colMax;
268
269 for (uint16_t tileColMin = colMin; tileColMin <= colMax;
270 tileColMin = static_cast<uint16_t>(tileColMin + o2::itsmft::ClusterPattern::MaxColSpan)) {
271 uint16_t tileColMax = std::min(colMax, static_cast<uint16_t>(tileColMin + o2::itsmft::ClusterPattern::MaxColSpan - 1));
272
273 for (uint16_t tileRowMin = rowMin; tileRowMin <= rowMax;
274 tileRowMin = static_cast<uint16_t>(tileRowMin + o2::itsmft::ClusterPattern::MaxRowSpan)) {
275 uint16_t tileRowMax = std::min(rowMax, static_cast<uint16_t>(tileRowMin + o2::itsmft::ClusterPattern::MaxRowSpan - 1));
276
277 // Collect cells in this tile
278 std::vector<std::pair<uint16_t, uint16_t>> tileCells;
279 for (const auto& cell : actsCluster.cells) {
280 uint16_t r = static_cast<uint16_t>(cell.row);
281 uint16_t c = static_cast<uint16_t>(cell.col);
282 if (r >= tileRowMin && r <= tileRowMax && c >= tileColMin && c <= tileColMax) {
283 tileCells.emplace_back(r, c);
284 }
285 }
286
287 if (tileCells.empty()) {
288 continue;
289 }
290
291 uint16_t tileRowSpan = tileRowMax - tileRowMin + 1;
292 uint16_t tileColSpan = tileColMax - tileColMin + 1;
293
294 // Encode pattern for this tile
295 std::array<unsigned char, o2::itsmft::ClusterPattern::MaxPatternBytes> patt{};
296 for (const auto& [r, c] : tileCells) {
297 uint32_t ir = r - tileRowMin;
298 uint32_t ic = c - tileColMin;
299 int nbit = ir * tileColSpan + ic;
300 patt[nbit >> 3] |= (0x1 << (7 - (nbit % 8)));
301 }
302 patterns.emplace_back(static_cast<unsigned char>(tileRowSpan));
303 patterns.emplace_back(static_cast<unsigned char>(tileColSpan));
304 const int nBytes = (tileRowSpan * tileColSpan + 7) / 8;
305 patterns.insert(patterns.end(), patt.begin(), patt.begin() + nBytes);
306
307 // Handle MC labels for this tile
308 if (clusterLabels && digitLabels) {
309 const auto clsIdx = static_cast<uint32_t>(clusters.size());
310 for (const auto& cell : actsCluster.cells) {
311 uint16_t r = static_cast<uint16_t>(cell.row);
312 uint16_t c = static_cast<uint16_t>(cell.col);
313 if (r >= tileRowMin && r <= tileRowMax && c >= tileColMin && c <= tileColMax) {
314 if (cell.digitIdx < digitLabels->getIndexedSize()) {
315 const auto& lbls = digitLabels->getLabels(cell.digitIdx);
316 for (const auto& lbl : lbls) {
317 clusterLabels->addElement(clsIdx, lbl);
318 }
319 }
320 }
321 }
322 }
323
324 // Create O2 cluster for this tile
325 o2::trk::Cluster cluster;
326 cluster.chipID = chipID;
327 cluster.row = tileRowMin;
328 cluster.col = tileColMin;
329 cluster.size = static_cast<uint16_t>(tileCells.size());
330 if (geom) {
331 cluster.subDetID = static_cast<int16_t>(geom->getSubDetID(chipID));
332 cluster.layer = static_cast<int16_t>(geom->getLayer(chipID));
333 cluster.disk = static_cast<int16_t>(geom->getDisk(chipID));
334 }
335 clusters.emplace_back(cluster);
336 }
337 }
338 } else {
339 // Normal cluster - encode directly
340 std::array<unsigned char, o2::itsmft::ClusterPattern::MaxPatternBytes> patt{};
341 for (const auto& cell : actsCluster.cells) {
342 uint32_t ir = static_cast<uint32_t>(cell.row - rowMin);
343 uint32_t ic = static_cast<uint32_t>(cell.col - colMin);
344 int nbit = ir * colSpan + ic;
345 patt[nbit >> 3] |= (0x1 << (7 - (nbit % 8)));
346 }
347 patterns.emplace_back(static_cast<unsigned char>(rowSpan));
348 patterns.emplace_back(static_cast<unsigned char>(colSpan));
349 const int nBytes = (rowSpan * colSpan + 7) / 8;
350 patterns.insert(patterns.end(), patt.begin(), patt.begin() + nBytes);
351
352 // Handle MC labels
353 if (clusterLabels && digitLabels) {
354 const auto clsIdx = static_cast<uint32_t>(clusters.size());
355 for (const auto& cell : actsCluster.cells) {
356 if (cell.digitIdx < digitLabels->getIndexedSize()) {
357 const auto& lbls = digitLabels->getLabels(cell.digitIdx);
358 for (const auto& lbl : lbls) {
359 clusterLabels->addElement(clsIdx, lbl);
360 }
361 }
362 }
363 }
364
365 // Create O2 cluster
366 o2::trk::Cluster cluster;
367 cluster.chipID = chipID;
368 cluster.row = rowMin;
369 cluster.col = colMin;
370 cluster.size = static_cast<uint16_t>(actsCluster.cells.size());
371 if (geom) {
372 cluster.subDetID = static_cast<int16_t>(geom->getSubDetID(chipID));
373 cluster.layer = static_cast<int16_t>(geom->getLayer(chipID));
374 cluster.disk = static_cast<int16_t>(geom->getDisk(chipID));
375 }
376 clusters.emplace_back(cluster);
377 }
378 }
379
380 LOG(debug) << " clusterization of chip " << chipID << " completed!";
381 }
382 clusterROFs.emplace_back(inROF.getBCData(), inROF.getROFrame(),
383 outFirst, static_cast<int>(clusters.size()) - outFirst);
384 }
385
386 if (clusterMC2ROFs && !digMC2ROFs.empty()) {
387 clusterMC2ROFs->reserve(clusterMC2ROFs->size() + digMC2ROFs.size());
388 for (const auto& in : digMC2ROFs) {
389 clusterMC2ROFs->emplace_back(in.eventRecordID, in.rofRecordID, in.minROF, in.maxROF);
390 }
391 }
392}
bool operator==(const Cell2D &left, const Cell2D &right)
void clusterAddCell(Cluster2D &cl, const Cell2D &cell)
bool clHashComp(const Cluster2D &left, const Cluster2D &right)
int getCellColumn(const Cell2D &cell)
void genclusterw(int x, int y, int x0, int y0, int x1, int y1, std::vector< Cell2D > &cells, RNG &rng, double startp=0.5, double decayp=0.9)
bool cellComp(const Cell2D &left, const Cell2D &right)
int getCellRow(const Cell2D &cell)
Cluster2D gencluster(int x0, int y0, int x1, int y1, RNG &rng, double startp=0.5, double decayp=0.9)
Definition of the TRK cluster finder.
uint32_t hash
std::ostringstream debug
int32_t i
uint32_t c
Definition RawData.h:2
gsl::span< const TruthElement > getLabels(uint32_t dataindex) const
void addElement(uint32_t dataindex, TruthElement const &element, bool noElement=false)
static constexpr uint8_t MaxRowSpan
static constexpr uint8_t MaxColSpan
void process(gsl::span< const Digit > digits, gsl::span< const DigROFRecord > digitROFs, std::vector< o2::trk::Cluster > &clusters, std::vector< unsigned char > &patterns, std::vector< o2::trk::ROFRecord > &clusterROFs, const ConstDigitTruth *digitLabels=nullptr, ClusterTruth *clusterLabels=nullptr, gsl::span< const DigMC2ROFRecord > digMC2ROFs={}, std::vector< o2::trk::MC2ROFRecord > *clusterMC2ROFs=nullptr) override
std::unique_ptr< ClustererThread > mThread
Definition Clusterer.h:176
std::vector< int > mSortIdx
reusable per-ROF sort buffer
Definition Clusterer.h:177
static GeometryTGeo * Instance()
GLint GLenum GLint x
Definition glcorearb.h:403
GLuint GLfloat GLfloat GLfloat GLfloat y1
Definition glcorearb.h:5034
GLuint GLfloat GLfloat GLfloat x1
Definition glcorearb.h:5034
GLdouble GLdouble right
Definition glcorearb.h:4077
GLboolean GLboolean GLboolean b
Definition glcorearb.h:1233
GLboolean * data
Definition glcorearb.h:298
GLuint GLsizei const GLchar * label
Definition glcorearb.h:2519
GLuint GLfloat x0
Definition glcorearb.h:5034
GLboolean r
Definition glcorearb.h:1233
GLboolean GLboolean GLboolean GLboolean a
Definition glcorearb.h:1233
GLuint GLfloat GLfloat y0
Definition glcorearb.h:5034
uint32_t digitIdx
Index of the original digit (for MC label retrieval)
Cell2D(int rowv, int colv, uint32_t digIdx=0)
std::size_t hash
std::vector< Cell2D > cells
int16_t layer
Definition Cluster.h:28
uint16_t size
Definition Cluster.h:26
uint16_t row
Definition Cluster.h:24
uint16_t col
Definition Cluster.h:25
int16_t subDetID
Definition Cluster.h:27
int16_t disk
Definition Cluster.h:29
uint16_t chipID
Definition Cluster.h:23
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"
o2::InteractionRecord ir(0, 0)
std::vector< Cluster > clusters
std::vector< Cell > cells
std::vector< Digit > digits