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::trkft3::TRKCluster>& clusters,
162 std::vector<unsigned char>& patterns,
163 std::vector<o2::trkft3::ROFRecord>& clusterROFs,
164 const ConstDigitTruth* digitLabels,
165 ClusterTruth* clusterLabels)
166{
167 if (!mThread) {
168 mThread = std::make_unique<ClustererThread>(this);
169 }
170
171 auto* geom = o2::trk::GeometryTGeo::Instance();
172
173 for (size_t iROF = 0; iROF < digitROFs.size(); ++iROF) {
174 const auto& inROF = digitROFs[iROF];
175 const auto outFirst = static_cast<int>(clusters.size());
176 const int first = inROF.getFirstEntry();
177 const int nEntries = inROF.getNEntries();
178
179 if (nEntries == 0) {
180 clusterROFs.emplace_back(inROF.getBCData(), inROF.getROFrame(), outFirst, 0);
181 continue;
182 }
183
184 // Sort digit indices within this ROF by (chipID, col, row) so we can process
185 // chip by chip, column by column -- the same ordering the ALPIDE scanner expects.
186 mSortIdx.resize(nEntries);
187 std::iota(mSortIdx.begin(), mSortIdx.end(), first);
188 std::sort(mSortIdx.begin(), mSortIdx.end(), [&digits](int a, int b) {
189 const auto& da = digits[a];
190 const auto& db = digits[b];
191 if (da.getChipIndex() != db.getChipIndex()) {
192 return da.getChipIndex() < db.getChipIndex();
193 }
194 if (da.getColumn() != db.getColumn()) {
195 return da.getColumn() < db.getColumn();
196 }
197 return da.getRow() < db.getRow();
198 });
199
200 // Type aliases for ACTS clustering
201 using Cell = Cell2D;
202 using CellCollection = std::vector<Cell>;
203 using Cluster = Cluster2D;
204 using ClusterCollection = std::vector<Cluster>;
205 static constexpr int GridDim = 2;
206
207 CellCollection cells; // Input collection of cells (pixels) to be clustered
208 Acts::Ccl::ClusteringData data; // Internal data structure used by ACTS clustering algorithm
209 ClusterCollection clsCollection; // Output collection of clusters found by the algorithm
210
211 // Process one chip at a time
212 int sliceStart = 0;
213 while (sliceStart < nEntries) {
214 const int chipFirst = sliceStart;
215 const uint16_t chipID = digits[mSortIdx[sliceStart]].getChipIndex();
216 while (sliceStart < nEntries && digits[mSortIdx[sliceStart]].getChipIndex() == chipID) {
217 ++sliceStart;
218 }
219 const int chipN = sliceStart - chipFirst;
220
221 // Fill cells from digits for this chip
222 cells.clear();
223 data.clear();
224 clsCollection.clear();
225 cells.reserve(chipN);
226 for (int i = chipFirst; i < chipFirst + chipN; ++i) {
227 const auto& digit = digits[mSortIdx[i]];
228 cells.emplace_back(digit.getRow(), digit.getColumn(), mSortIdx[i]);
229 }
230
231 LOG(debug) << "Clustering with ACTS on chip " << chipID << " " << cells.size() << " digits";
232 Acts::Ccl::createClusters<CellCollection, ClusterCollection, GridDim>(data,
233 cells,
234 clsCollection,
235 Acts::Ccl::DefaultConnect<Cell, GridDim>(false));
236
237 LOG(debug) << " found " << clsCollection.size() << " clusters";
238
239 // Convert ACTS clusters to O2 clusters
240 for (const auto& actsCluster : clsCollection) {
241 if (actsCluster.cells.empty()) {
242 continue;
243 }
244
245 // Calculate bounding box
246 uint16_t rowMin = static_cast<uint16_t>(actsCluster.cells[0].row);
247 uint16_t rowMax = rowMin;
248 uint16_t colMin = static_cast<uint16_t>(actsCluster.cells[0].col);
249 uint16_t colMax = colMin;
250
251 for (const auto& cell : actsCluster.cells) {
252 rowMin = std::min(rowMin, static_cast<uint16_t>(cell.row));
253 rowMax = std::max(rowMax, static_cast<uint16_t>(cell.row));
254 colMin = std::min(colMin, static_cast<uint16_t>(cell.col));
255 colMax = std::max(colMax, static_cast<uint16_t>(cell.col));
256 }
257
258 const uint16_t rowSpan = rowMax - rowMin + 1;
259 const uint16_t colSpan = colMax - colMin + 1;
260
261 // Check if cluster needs splitting (too large for pattern encoding)
262 const bool isHuge = rowSpan > o2::itsmft::ClusterPattern::MaxRowSpan ||
264
265 if (isHuge) {
266 // Split huge cluster into MaxRowSpan x MaxColSpan tiles
267 LOG(warning) << "Splitting huge TRK cluster: chipID " << chipID
268 << ", rows " << rowMin << ":" << rowMax
269 << " cols " << colMin << ":" << colMax;
270
271 for (uint16_t tileColMin = colMin; tileColMin <= colMax;
272 tileColMin = static_cast<uint16_t>(tileColMin + o2::itsmft::ClusterPattern::MaxColSpan)) {
273 uint16_t tileColMax = std::min(colMax, static_cast<uint16_t>(tileColMin + o2::itsmft::ClusterPattern::MaxColSpan - 1));
274
275 for (uint16_t tileRowMin = rowMin; tileRowMin <= rowMax;
276 tileRowMin = static_cast<uint16_t>(tileRowMin + o2::itsmft::ClusterPattern::MaxRowSpan)) {
277 uint16_t tileRowMax = std::min(rowMax, static_cast<uint16_t>(tileRowMin + o2::itsmft::ClusterPattern::MaxRowSpan - 1));
278
279 // Collect cells in this tile
280 std::vector<std::pair<uint16_t, uint16_t>> tileCells;
281 for (const auto& cell : actsCluster.cells) {
282 uint16_t r = static_cast<uint16_t>(cell.row);
283 uint16_t c = static_cast<uint16_t>(cell.col);
284 if (r >= tileRowMin && r <= tileRowMax && c >= tileColMin && c <= tileColMax) {
285 tileCells.emplace_back(r, c);
286 }
287 }
288
289 if (tileCells.empty()) {
290 continue;
291 }
292
293 uint16_t tileRowSpan = tileRowMax - tileRowMin + 1;
294 uint16_t tileColSpan = tileColMax - tileColMin + 1;
295
296 // Encode pattern for this tile
297 std::array<unsigned char, o2::itsmft::ClusterPattern::MaxPatternBytes> patt{};
298 for (const auto& [r, c] : tileCells) {
299 uint32_t ir = r - tileRowMin;
300 uint32_t ic = c - tileColMin;
301 int nbit = ir * tileColSpan + ic;
302 patt[nbit >> 3] |= (0x1 << (7 - (nbit % 8)));
303 }
304 patterns.emplace_back(static_cast<unsigned char>(tileRowSpan));
305 patterns.emplace_back(static_cast<unsigned char>(tileColSpan));
306 const int nBytes = (tileRowSpan * tileColSpan + 7) / 8;
307 patterns.insert(patterns.end(), patt.begin(), patt.begin() + nBytes);
308
309 // Handle MC labels for this tile
310 if (clusterLabels && digitLabels) {
311 const auto clsIdx = static_cast<uint32_t>(clusters.size());
312 for (const auto& cell : actsCluster.cells) {
313 uint16_t r = static_cast<uint16_t>(cell.row);
314 uint16_t c = static_cast<uint16_t>(cell.col);
315 if (r >= tileRowMin && r <= tileRowMax && c >= tileColMin && c <= tileColMax) {
316 if (cell.digitIdx < digitLabels->getIndexedSize()) {
317 const auto& lbls = digitLabels->getLabels(cell.digitIdx);
318 for (const auto& lbl : lbls) {
319 clusterLabels->addElement(clsIdx, lbl);
320 }
321 }
322 }
323 }
324 }
325
326 // Create O2 cluster for this tile
328 cluster.chipID = chipID;
329 cluster.row = tileRowMin;
330 cluster.col = tileColMin;
331 cluster.size = static_cast<uint16_t>(tileCells.size());
332 if (geom) {
333 cluster.subDetID = static_cast<int16_t>(geom->getSubDetID(chipID));
334 cluster.layer = static_cast<int16_t>(geom->getLayer(chipID));
335 }
336 clusters.emplace_back(cluster);
337 }
338 }
339 } else {
340 // Normal cluster - encode directly
341 std::array<unsigned char, o2::itsmft::ClusterPattern::MaxPatternBytes> patt{};
342 for (const auto& cell : actsCluster.cells) {
343 uint32_t ir = static_cast<uint32_t>(cell.row - rowMin);
344 uint32_t ic = static_cast<uint32_t>(cell.col - colMin);
345 int nbit = ir * colSpan + ic;
346 patt[nbit >> 3] |= (0x1 << (7 - (nbit % 8)));
347 }
348 patterns.emplace_back(static_cast<unsigned char>(rowSpan));
349 patterns.emplace_back(static_cast<unsigned char>(colSpan));
350 const int nBytes = (rowSpan * colSpan + 7) / 8;
351 patterns.insert(patterns.end(), patt.begin(), patt.begin() + nBytes);
352
353 // Handle MC labels
354 if (clusterLabels && digitLabels) {
355 const auto clsIdx = static_cast<uint32_t>(clusters.size());
356 for (const auto& cell : actsCluster.cells) {
357 if (cell.digitIdx < digitLabels->getIndexedSize()) {
358 const auto& lbls = digitLabels->getLabels(cell.digitIdx);
359 for (const auto& lbl : lbls) {
360 clusterLabels->addElement(clsIdx, lbl);
361 }
362 }
363 }
364 }
365
366 // Create O2 cluster
368 cluster.chipID = chipID;
369 cluster.row = rowMin;
370 cluster.col = colMin;
371 cluster.size = static_cast<uint16_t>(actsCluster.cells.size());
372 if (geom) {
373 cluster.subDetID = static_cast<int16_t>(geom->getSubDetID(chipID));
374 cluster.layer = static_cast<int16_t>(geom->getLayer(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}
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)
HMPID cluster implementation.
Definition Cluster.h:27
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::trkft3::TRKCluster > &clusters, std::vector< unsigned char > &patterns, std::vector< o2::trkft3::ROFRecord > &clusterROFs, const ConstDigitTruth *digitLabels=nullptr, ClusterTruth *clusterLabels=nullptr) override
std::unique_ptr< ClustererThread > mThread
Definition Clusterer.h:181
std::vector< int > mSortIdx
reusable per-ROF sort buffer
Definition Clusterer.h:182
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
uint16_t chipID
Definition Cluster.h:28
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"
o2::InteractionRecord ir(0, 0)
std::vector< Cluster > clusters
std::vector< Cell > cells
std::vector< Digit > digits