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