Project
Loading...
Searching...
No Matches
ROFLookupTables.h
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
12#ifndef TRACKINGITSU_INCLUDE_ROFOVERLAPTABLE_H_
13#define TRACKINGITSU_INCLUDE_ROFOVERLAPTABLE_H_
14
15#include <cstddef>
16#include <cstdint>
17#include <limits>
18#include <string>
19#include <vector>
20#include <ranges>
21
22#ifndef GPUCA_GPUCODE
23#include <format>
24#include "Framework/Logger.h"
25#endif
26
31#include "GPUCommonMath.h"
32#include "GPUCommonDef.h"
33
34namespace o2::its
35{
36
37// Layer timing definition
40 BCType mNROFsTF{0}; // number of ROFs per timeframe
41 BCType mROFLength{0}; // ROF length in BC
42 BCType mROFDelay{0}; // delay of ROFs wrt start of first orbit in TF in BC
43 BCType mROFBias{0}; // bias wrt to the LHC clock in BC
44 BCType mROFAddTimeErr{0}; // additionally imposed uncertainty on ROF time in BC
45
46 // return start of ROF in BC
47 // this does not account for the opt. error!
48 GPUhdi() BCType getROFStartInBC(BCType rofId) const noexcept
49 {
50 assert(rofId < mNROFsTF && rofId >= 0);
51 return (mROFLength * rofId) + mROFDelay + mROFBias;
52 }
53
54 // return end of ROF in BCs
55 // this does not account for the opt. error!
56 GPUhdi() BCType getROFEndInBC(BCType rofId) const noexcept
57 {
58 assert(rofId < mNROFsTF);
59 return getROFStartInBC(rofId) + mROFLength;
60 }
61
62 // return (clamped) time-interval of rof
63 GPUhdi() TimeEstBC getROFTimeBounds(BCType rofId, bool withError = false) const noexcept
64 {
65 if (withError) {
66 int64_t start = getROFStartInBC(rofId);
67 int64_t end = getROFEndInBC(rofId);
68 start = o2::gpu::CAMath::Max(start - mROFAddTimeErr, int64_t(0));
70 return {static_cast<BCType>(start), static_cast<TimeStampErrorType>(end - start)};
71 }
72 return {getROFStartInBC(rofId), static_cast<TimeStampErrorType>(mROFLength)};
73 }
74
75 // return which ROF this BC belongs to
76 GPUhi() BCType getROF(BCType bc) const noexcept
77 {
79 if (bc <= offset) {
80 return 0;
81 }
82 return (bc - offset) / mROFLength;
83 }
84
85 // return which ROF this timestamp belongs by its lower edge
86 GPUhi() BCType getROF(TimeStamp ts) const noexcept
87 {
89 const BCType bc = (ts.getTimeStamp() < ts.getTimeStampError()) ? BCType(0) : static_cast<BCType>(o2::gpu::CAMath::Floor(ts.getTimeStamp() - ts.getTimeStampError()));
90 if (bc <= offset) {
91 return 0;
92 }
93 return (bc - offset) / mROFLength;
94 }
95
96#ifndef GPUCA_GPUCODE
97 GPUh() std::string asString() const
98 {
99 return std::format("NROFsPerTF {:4} ROFLength {:4} ({:4} per Orbit) ROFDelay {:4} ROFBias {:4} ROFAddTimeErr {:4}", mNROFsTF, mROFLength, (o2::constants::lhc::LHCMaxBunches / mROFLength), mROFDelay, mROFBias, mROFAddTimeErr);
100 }
101
102 GPUh() void print() const
103 {
104 LOG(info) << asString();
105 }
106#endif
107};
108
109// Base class for lookup to define layers
110template <int32_t NLayers>
112{
113 protected:
115
116 public:
118 LayerTimingBase() = default;
119
120 GPUh() void defineLayer(int32_t layer, T nROFsTF, T rofLength, T rofDelay, T rofBias, T rofTE)
121 {
122 assert(layer >= 0 && layer < NLayers);
124 }
125
126 GPUh() void defineLayer(int32_t layer, const LayerTiming& timing)
127 {
128 assert(layer >= 0 && layer < NLayers);
130 }
131
132 GPUhdi() const LayerTiming& getLayer(int32_t layer) const
133 {
134 assert(layer >= 0 && layer < NLayers);
135 return mLayers[layer];
136 }
137
138 GPUhdi() constexpr int32_t getEntries() noexcept { return NLayers; }
139
140#ifndef GPUCA_GPUCODE
141 GPUh() void print() const
142 {
143 LOGP(info, "Imposed time structure:");
144 for (int32_t iL{0}; iL < NLayers; ++iL) {
145 LOGP(info, "\tLayer:{} {}", iL, mLayers[iL].asString());
146 }
147 }
148#endif
149};
150
151// GPU friendly view of the table below
152template <int32_t NLayers, typename TableEntry, typename TableIndex>
154 const TableEntry* mFlatTable{nullptr};
155 const TableIndex* mIndices{nullptr};
156 const LayerTiming* mLayers{nullptr};
157
159 {
160 assert(layer >= 0 && layer < NLayers);
161 return mLayers[layer];
162 }
163
164 GPUh() int32_t getClock() const noexcept
165 {
166 // we take the fastest layer as clock
167 int32_t fastest = 0;
168 uint32_t maxNROFs{0};
169 for (int32_t iL{0}; iL < NLayers; ++iL) {
170 const auto& layer = getLayer(iL);
171 // by definition the fastest layer has the most ROFs
172 // this also solves the problem of a delay large than ROFLength
173 // if mNROFsTF is correct
174 if (layer.mNROFsTF > maxNROFs) {
175 fastest = iL;
176 maxNROFs = layer.mNROFsTF;
177 }
178 }
179 return fastest;
180 }
181
183 {
184 return mLayers[getClock()];
185 }
186
187 GPUhdi() const TableEntry& getOverlap(int32_t from, int32_t to, size_t rofIdx) const noexcept
188 {
189 assert(from < NLayers && to < NLayers);
190 const size_t linearIdx = (from * NLayers) + to;
191 const auto& idx = mIndices[linearIdx];
192 assert(rofIdx < idx.getEntries());
193 return mFlatTable[idx.getFirstEntry() + rofIdx];
194 }
195
196 GPUhdi() bool doROFsOverlap(int32_t layer0, size_t rof0, int32_t layer1, size_t rof1) const noexcept
197 {
198 if (layer0 == layer1) { // layer is compatible with itself
199 return rof0 == rof1;
200 }
201
202 assert(layer0 < NLayers && layer1 < NLayers);
203 const size_t linearIdx = (layer0 * NLayers) + layer1;
204 const auto& idx = mIndices[linearIdx];
205
206 if (rof0 >= idx.getEntries()) {
207 return false;
208 }
209
210 const auto& overlap = mFlatTable[idx.getFirstEntry() + rof0];
211
212 if (overlap.getEntries() == 0) {
213 return false;
214 }
215
216 const size_t firstCompatible = overlap.getFirstEntry();
217 const size_t lastCompatible = firstCompatible + overlap.getEntries() - 1;
219 }
220
221 GPUhdi() TimeEstBC getTimeStamp(int32_t layer0, size_t rof0, int32_t layer1, size_t rof1) const noexcept
222 {
223 assert(layer0 < NLayers && layer1 < NLayers);
224 assert(doROFsOverlap(layer0, rof0, layer1, rof1));
225 // retrieves the combined timestamp
226 // e.g., taking one cluster from rof0 and one from rof1
227 // and constructing a tracklet (doublet) what is its time
228 // this assumes that the rofs overlap, e.g. doROFsOverlap -> true
229 // get timestamp including margins from rof0 and rof1
230 const auto t0 = mLayers[layer0].getROFTimeBounds(rof0, true);
231 const auto t1 = mLayers[layer1].getROFTimeBounds(rof1, true);
232 return t0 + t1;
233 }
234
235#ifndef GPUCA_GPUCODE
237 GPUh() void printAll() const
238 {
239 for (int32_t i = 0; i < NLayers; ++i) {
240 for (int32_t j = 0; j < NLayers; ++j) {
241 if (i != j) {
242 printMapping(i, j);
243 }
244 }
245 }
246 printSummary();
247 }
248
249 GPUh() void printMapping(int32_t from, int32_t to) const
250 {
251 if (from == to) {
252 LOGP(error, "No self-lookup supported");
253 return;
254 }
255
256 constexpr int w_index = 10;
257 constexpr int w_first = 12;
258 constexpr int w_last = 12;
259 constexpr int w_count = 10;
260
261 LOGF(info, "Overlap mapping: Layer %d -> Layer %d", from, to);
262 LOGP(info, "From: {}", mLayers[from].asString());
263 LOGP(info, "To : {}", mLayers[to].asString());
264 LOGF(info, "%*s | %*s | %*s | %*s", w_index, "ROF.index", w_first, "First.ROF", w_last, "Last.ROF", w_count, "Count");
265 LOGF(info, "%.*s-+-%.*s-+-%.*s-+-%.*s", w_index, "----------", w_first, "------------", w_last, "------------", w_count, "----------");
266
267 const size_t linearIdx = (from * NLayers) + to;
268 const auto& idx = mIndices[linearIdx];
269 for (int32_t i = 0; i < idx.getEntries(); ++i) {
270 const auto& overlap = getOverlap(from, to, i);
271 LOGF(info, "%*d | %*d | %*d | %*d", w_index, i, w_first, overlap.getFirstEntry(), w_last, overlap.getEntriesBound() - 1, w_count, overlap.getEntries());
272 }
273 }
274
275 GPUh() void printSummary() const
276 {
277 uint32_t totalEntries{0};
278 size_t flatTableSize{0};
279
280 for (int32_t i = 0; i < NLayers; ++i) {
281 for (int32_t j = 0; j < NLayers; ++j) {
282 if (i != j) {
283 const size_t linearIdx = (i * NLayers) + j;
284 const auto& idx = mIndices[linearIdx];
285 totalEntries += idx.getEntries();
286 flatTableSize += idx.getEntries();
287 }
288 }
289 }
290
291 for (int32_t i = 0; i < NLayers; ++i) {
292 mLayers[i].print();
293 }
294
295 const uint32_t totalBytes = (flatTableSize * sizeof(TableEntry)) + (static_cast<unsigned long>(NLayers * NLayers) * sizeof(TableIndex));
296 LOGF(info, "------------------------------------------------------------");
297 LOGF(info, "Total overlap table size: %u entries", totalEntries);
298 LOGF(info, "Flat table size: %zu entries", flatTableSize);
299 LOGF(info, "Total view size: %u bytes", totalBytes);
300 LOGF(info, "------------------------------------------------------------");
301 }
302#endif
303};
304
305// Precalculated lookup table to find overlapping ROFs in another layer given a ROF index in the current layer
306template <int32_t NLayers>
307class ROFOverlapTable : public LayerTimingBase<NLayers>
308{
309 public:
313
315 ROFOverlapTable() = default;
316
317 GPUh() void init()
318 {
319 std::vector<TableEntry> table[NLayers][NLayers];
320 for (int32_t i{0}; i < NLayers; ++i) {
321 for (int32_t j{0}; j < NLayers; ++j) {
322 if (i != j) { // we do not need self-lookup
323 buildMapping(i, j, table[i][j]);
324 }
325 }
326 }
327 flatten(table);
328 }
329
330 GPUh() View getView() const
331 {
332 View view;
333 view.mFlatTable = mFlatTable.data();
334 view.mIndices = mIndices;
335 view.mLayers = this->mLayers;
336 return view;
337 }
338
339 GPUh() View getDeviceView(const TableEntry* deviceFlatTablePtr, const TableIndex* deviceIndicesPtr, const LayerTiming* deviceLayerTimingPtr) const
340 {
341 View view;
342 view.mFlatTable = deviceFlatTablePtr;
344 view.mLayers = deviceLayerTimingPtr;
345 return view;
346 }
347
348 GPUh() size_t getFlatTableSize() const noexcept { return mFlatTable.size(); }
349 static GPUh() constexpr size_t getIndicesSize() { return static_cast<size_t>(NLayers * NLayers); }
350
351 private:
352 GPUh() void buildMapping(int32_t from, int32_t to, std::vector<TableEntry>& table)
353 {
354 const auto& layerFrom = this->mLayers[from];
355 const auto& layerTo = this->mLayers[to];
356 table.resize(layerFrom.mNROFsTF);
357
358 for (int32_t iROF{0}; iROF < layerFrom.mNROFsTF; ++iROF) {
359 int64_t fromStart = o2::gpu::CAMath::Max((int64_t)layerFrom.getROFStartInBC(iROF) - (int64_t)layerFrom.mROFAddTimeErr, int64_t(0));
360 int64_t fromEnd = (int64_t)layerFrom.getROFEndInBC(iROF) + layerFrom.mROFAddTimeErr;
361
362 int32_t firstROFTo = o2::gpu::CAMath::Max(0, (int32_t)((fromStart - (int64_t)layerTo.mROFAddTimeErr - (int64_t)layerTo.mROFDelay - (int64_t)layerTo.mROFBias) / (int64_t)layerTo.mROFLength));
363 auto lastROFTo = (int32_t)((fromEnd + (int64_t)layerTo.mROFAddTimeErr - (int64_t)layerTo.mROFDelay - (int64_t)layerTo.mROFBias - 1) / (int64_t)layerTo.mROFLength);
364 firstROFTo = o2::gpu::CAMath::Max(0, firstROFTo);
365 lastROFTo = o2::gpu::CAMath::Min((int32_t)layerTo.mNROFsTF - 1, lastROFTo);
366
367 while (firstROFTo <= lastROFTo) {
368 int64_t toStart = o2::gpu::CAMath::Max((int64_t)layerTo.getROFStartInBC(firstROFTo) - (int64_t)layerTo.mROFAddTimeErr, int64_t(0));
369 int64_t toEnd = (int64_t)layerTo.getROFEndInBC(firstROFTo) + layerTo.mROFAddTimeErr;
370 if (toEnd > fromStart && toStart < fromEnd) {
371 break;
372 }
373 ++firstROFTo;
374 }
375 while (lastROFTo >= firstROFTo) {
376 int64_t toStart = o2::gpu::CAMath::Max((int64_t)layerTo.getROFStartInBC(lastROFTo) - (int64_t)layerTo.mROFAddTimeErr, int64_t(0));
377 int64_t toEnd = (int64_t)layerTo.getROFEndInBC(lastROFTo) + layerTo.mROFAddTimeErr;
378 if (toEnd > fromStart && toStart < fromEnd) {
379 break;
380 }
381 --lastROFTo;
382 }
383 int32_t count = (firstROFTo <= lastROFTo) ? (lastROFTo - firstROFTo + 1) : 0;
384 table[iROF] = {static_cast<T>(firstROFTo), static_cast<T>(count)};
385 }
386 }
387
388 GPUh() void flatten(const std::vector<TableEntry> table[NLayers][NLayers])
389 {
390 size_t total{0};
391 for (int32_t i{0}; i < NLayers; ++i) {
392 for (int32_t j{0}; j < NLayers; ++j) {
393 if (i != j) { // we do not need self-lookup
394 total += table[i][j].size();
395 }
396 }
397 }
398
399 mFlatTable.reserve(total);
400
401 for (int32_t i{0}; i < NLayers; ++i) {
402 for (int32_t j{0}; j < NLayers; ++j) {
403 size_t idx = (i * NLayers) + j;
404 if (i != j) {
405 mIndices[idx].setFirstEntry(static_cast<T>(mFlatTable.size()));
406 mIndices[idx].setEntries(static_cast<T>(table[i][j].size()));
407 mFlatTable.insert(mFlatTable.end(), table[i][j].begin(), table[i][j].end());
408 } else {
409 mIndices[idx] = {0, 0};
410 }
411 }
412 }
413 }
414
415 TableIndex mIndices[NLayers * NLayers];
416 std::vector<TableEntry> mFlatTable;
417};
418
419// GPU friendly view of the table below
420template <int32_t NLayers, typename TableEntry, typename TableIndex>
422 const TableEntry* mFlatTable{nullptr};
423 const TableIndex* mIndices{nullptr};
424 const LayerTiming* mLayers{nullptr};
425
426 GPUhdi() const LayerTiming& getLayer(int32_t layer) const noexcept
427 {
428 assert(layer >= 0 && layer < NLayers);
429 return mLayers[layer];
430 }
431
432 GPUhdi() const TableEntry& getVertices(int32_t layer, size_t rofIdx) const noexcept
433 {
434 assert(layer < NLayers);
435 const auto& idx = mIndices[layer];
436 assert(rofIdx < idx.getEntries());
437 return mFlatTable[idx.getFirstEntry() + rofIdx];
438 }
439
440 GPUh() int32_t getMaxVerticesPerROF() const noexcept
441 {
442 int32_t maxCount = 0;
443 for (int32_t layer = 0; layer < NLayers; ++layer) {
444 const auto& idx = mIndices[layer];
445 for (int32_t i = 0; i < idx.getEntries(); ++i) {
446 const auto& entry = mFlatTable[idx.getFirstEntry() + i];
447 maxCount = o2::gpu::CAMath::Max(maxCount, static_cast<int32_t>(entry.getEntries()));
448 }
449 }
450 return maxCount;
451 }
452
453 // Check if a specific vertex is compatible with a given ROF
454 GPUhdi() bool isVertexCompatible(int32_t layer, size_t rofIdx, const Vertex& vertex) const noexcept
455 {
456 assert(layer < NLayers);
457 const auto& layerDef = mLayers[layer];
458 int64_t rofLower = o2::gpu::CAMath::Max((int64_t)layerDef.getROFStartInBC(rofIdx) - (int64_t)layerDef.mROFAddTimeErr, int64_t(0));
459 int64_t rofUpper = (int64_t)layerDef.getROFEndInBC(rofIdx) + layerDef.mROFAddTimeErr;
460 auto vLower = (int64_t)vertex.getTimeStamp().lower();
461 auto vUpper = (int64_t)vertex.getTimeStamp().upper();
463 }
464
465#ifndef GPUCA_GPUCODE
466 GPUh() void printAll() const
467 {
468 for (int32_t i = 0; i < NLayers; ++i) {
469 printLayer(i);
470 }
471 printSummary();
472 }
473
474 GPUh() void printLayer(int32_t layer) const
475 {
476 constexpr int w_rof = 10;
477 constexpr int w_first = 12;
478 constexpr int w_last = 12;
479 constexpr int w_count = 10;
480
481 LOGF(info, "Vertex lookup: Layer %d", layer);
482 LOGF(info, "%*s | %*s | %*s | %*s", w_rof, "ROF.index", w_first, "First.Vtx", w_last, "Last.Vtx", w_count, "Count");
483 LOGF(info, "%.*s-+-%.*s-+-%.*s-+-%.*s", w_rof, "----------", w_first, "------------", w_last, "------------", w_count, "----------");
484
485 const auto& idx = mIndices[layer];
486 for (int32_t i = 0; i < idx.getEntries(); ++i) {
487 const auto& entry = mFlatTable[idx.getFirstEntry() + i];
488 int first = entry.getFirstEntry();
489 int count = entry.getEntries();
490 int last = first + count - 1;
491 LOGF(info, "%*d | %*d | %*d | %*d", w_rof, i, w_first, first, w_last, last, w_count, count);
492 }
493 }
494
495 GPUh() void printSummary() const
496 {
497 uint32_t totalROFs{0};
498 uint32_t totalVertexRefs{0};
499
500 for (int32_t i = 0; i < NLayers; ++i) {
501 const auto& idx = mIndices[i];
502 totalROFs += idx.getEntries();
503
504 for (int32_t j = 0; j < idx.getEntries(); ++j) {
505 const auto& entry = mFlatTable[idx.getFirstEntry() + j];
506 totalVertexRefs += entry.getEntries();
507 }
508 }
509
510 const uint32_t totalBytes = (totalROFs * sizeof(TableEntry)) + (NLayers * sizeof(TableIndex));
511 LOGF(info, "------------------------------------------------------------");
512 LOGF(info, "Total ROFs in table: %u", totalROFs);
513 LOGF(info, "Total vertex references: %u", totalVertexRefs);
514 LOGF(info, "Total view size: %u bytes", totalBytes);
515 LOGF(info, "------------------------------------------------------------");
516 }
517#endif
518};
519
520// Precalculated lookup table to find vertices compatible with ROFs
521// Given a layer and ROF index, returns the range of vertices that overlap in time.
522// The vertex time is defined as symmetrical [t0-e,t0+e]
523// It needs to be guaranteed that the input vertices are sorted by their lower-bound!
524// additionally compatibliyty has to be queried per vertex!
525template <int32_t NLayers>
527{
528 public:
534
536
537 GPUh() size_t getFlatTableSize() const noexcept { return mFlatTable.size(); }
538 static GPUh() constexpr size_t getIndicesSize() { return NLayers; }
539
540 // Build the lookup table given a sorted array of vertices
541 // vertices must be sorted by timestamp, then by error (secondary)
542 GPUh() void init(const Vertex* vertices, size_t nVertices)
543 {
544 if (nVertices > std::numeric_limits<T>::max()) {
545 LOGF(fatal, "too many vertices %zu, max supported is %u", nVertices, std::numeric_limits<T>::max());
546 }
547
548 std::vector<TableEntry> table[NLayers];
549 for (int32_t layer{0}; layer < NLayers; ++layer) {
550 buildMapping(layer, vertices, nVertices, table[layer]);
551 }
552 flatten(table);
553 }
554
555 // Pre-allocated needed memory, then use update(...)
556 GPUh() void init()
557 {
558 size_t total{0};
559 for (int32_t layer{0}; layer < NLayers; ++layer) {
560 total += this->mLayers[layer].mNROFsTF;
561 }
562 mFlatTable.resize(total, {0, 0});
563 size_t offset = 0;
564 for (int32_t layer{0}; layer < NLayers; ++layer) {
565 size_t nROFs = this->mLayers[layer].mNROFsTF;
566 mIndices[layer].setFirstEntry(static_cast<T>(offset));
567 mIndices[layer].setEntries(static_cast<T>(nROFs));
568 offset += nROFs;
569 }
570 }
571
572 // Recalculate lookup table with new vertices
573 GPUh() void update(const Vertex* vertices, size_t nVertices)
574 {
575 size_t offset = 0;
576 for (int32_t layer{0}; layer < NLayers; ++layer) {
577 const auto& idx = mIndices[layer];
578 size_t nROFs = idx.getEntries();
579 for (size_t iROF = 0; iROF < nROFs; ++iROF) {
580 updateROFMapping(layer, iROF, vertices, nVertices, offset + iROF);
581 }
582 offset += nROFs;
583 }
584 }
585
586 GPUh() View getView() const
587 {
588 View view;
589 view.mFlatTable = mFlatTable.data();
590 view.mIndices = mIndices;
591 view.mLayers = this->mLayers;
592 return view;
593 }
594
595 GPUh() View getDeviceView(const TableEntry* deviceFlatTablePtr, const TableIndex* deviceIndicesPtr, const LayerTiming* deviceLayerTimingPtr) const
596 {
597 View view;
598 view.mFlatTable = deviceFlatTablePtr;
600 view.mLayers = deviceLayerTimingPtr;
601 return view;
602 }
603
604 private:
605 // Build the mapping for one layer
606 GPUh() void buildMapping(int32_t layer, const Vertex* vertices, size_t nVertices, std::vector<TableEntry>& table)
607 {
608 const auto& layerDef = this->mLayers[layer];
609 table.resize(layerDef.mNROFsTF);
610 size_t vertexSearchStart = 0;
611 for (int32_t iROF{0}; iROF < layerDef.mNROFsTF; ++iROF) {
612 int64_t rofLower = o2::gpu::CAMath::Max((int64_t)layerDef.getROFStartInBC(iROF) - (int64_t)layerDef.mROFAddTimeErr, int64_t(0));
613 int64_t rofUpper = (int64_t)layerDef.getROFEndInBC(iROF) + layerDef.mROFAddTimeErr;
614 size_t lastVertex = binarySearchFirst(vertices, nVertices, vertexSearchStart, rofUpper);
615 size_t firstVertex = vertexSearchStart;
616 while (firstVertex < lastVertex) {
617 auto vUpper = (int64_t)vertices[firstVertex].getTimeStamp().upper();
618 if (vUpper > rofLower) {
619 break;
620 }
621 ++firstVertex;
622 }
623 size_t count = (lastVertex > firstVertex) ? (lastVertex - firstVertex) : 0;
624 table[iROF] = {static_cast<T>(firstVertex), static_cast<T>(count)};
625 vertexSearchStart = firstVertex;
626 }
627 }
628
629 // Update a single ROF's vertex mapping
630 GPUh() void updateROFMapping(int32_t layer, size_t iROF, const Vertex* vertices, size_t nVertices, size_t flatTableIdx)
631 {
632 const auto& layerDef = this->mLayers[layer];
633 int64_t rofLower = o2::gpu::CAMath::Max((int64_t)layerDef.getROFStartInBC(iROF) - (int64_t)layerDef.mROFAddTimeErr, int64_t(0));
634 int64_t rofUpper = (int64_t)layerDef.getROFEndInBC(iROF) + layerDef.mROFAddTimeErr;
635 size_t lastVertex = binarySearchFirst(vertices, nVertices, 0, rofUpper);
636 size_t firstVertex = 0;
637 while (firstVertex < lastVertex) {
638 int64_t vUpper = (int64_t)vertices[firstVertex].getTimeStamp().getTimeStamp() +
639 (int64_t)vertices[firstVertex].getTimeStamp().getTimeStampError();
640 if (vUpper > rofLower) {
641 break;
642 }
643 ++firstVertex;
644 }
645 size_t count = (lastVertex > firstVertex) ? (lastVertex - firstVertex) : 0;
646 mFlatTable[flatTableIdx].setFirstEntry(static_cast<T>(firstVertex));
647 mFlatTable[flatTableIdx].setEntries(static_cast<T>(count));
648 }
649
650 // Binary search for first vertex where maxBC >= targetBC
651 GPUh() size_t binarySearchFirst(const Vertex* vertices, size_t nVertices, size_t searchStart, BCType targetBC) const
652 {
653 size_t left = searchStart;
654 size_t right = nVertices;
655 while (left < right) {
656 size_t mid = left + ((right - left) / 2);
657 int64_t lower = (int64_t)vertices[mid].getTimeStamp().getTimeStamp() -
658 (int64_t)vertices[mid].getTimeStamp().getTimeStampError();
659 if (lower < targetBC) {
660 left = mid + 1;
661 } else {
662 right = mid;
663 }
664 }
665 return left;
666 }
667
668 // Compress the temporary table into a single flat table
669 GPUh() void flatten(const std::vector<TableEntry> table[NLayers])
670 {
671 // Count total entries
672 size_t total{0};
673 for (int32_t i{0}; i < NLayers; ++i) {
674 total += table[i].size();
675 }
676
677 mFlatTable.reserve(total);
678
679 // Build flat table and indices
680 for (int32_t i{0}; i < NLayers; ++i) {
681 mIndices[i].setFirstEntry(static_cast<T>(mFlatTable.size()));
682 mIndices[i].setEntries(static_cast<T>(table[i].size()));
683 mFlatTable.insert(mFlatTable.end(), table[i].begin(), table[i].end());
684 }
685 }
686
687 TableIndex mIndices[NLayers];
688 std::vector<TableEntry> mFlatTable;
689};
690
691// GPU-friendly view of the ROF mask table
692template <int32_t NLayers, typename TableEntry, typename TableIndex>
694 const TableEntry* mFlatMask{nullptr};
695 const TableIndex* mLayerROFOffsets{nullptr}; // size NLayers+1
696
697 GPUhdi() bool isROFEnabled(int32_t layer, int32_t rofId) const noexcept
698 {
699 assert(layer >= 0 && layer < NLayers);
700 return mFlatMask[mLayerROFOffsets[layer] + rofId] != 0u;
701 }
702
703#ifndef GPUCA_GPUCODE
704 GPUh() void printAll() const
705 {
706 for (int32_t i = 0; i < NLayers; ++i) {
707 printLayer(i);
708 }
709 }
710
711 GPUh() void printLayer(int32_t layer) const
712 {
713 constexpr int w_rof = 10;
714 constexpr int w_active = 10;
715 int32_t nROFs = mLayerROFOffsets[layer + 1] - mLayerROFOffsets[layer];
716 LOGF(info, "Mask table: Layer %d", layer);
717 LOGF(info, "%*s | %*s", w_rof, "ROF", w_active, "Enabled");
718 LOGF(info, "%.*s-+-%.*s", w_rof, "----------", w_active, "----------");
719 for (int32_t i = 0; i < nROFs; ++i) {
720 LOGF(info, "%*d | %*d", w_rof, i, w_active, (int)isROFEnabled(layer, i));
721 }
722 }
723
724 GPUh() std::string asString(int32_t layer) const
725 {
726 int32_t nROFs = mLayerROFOffsets[layer + 1] - mLayerROFOffsets[layer];
727 int32_t enabledROFs = 0;
728 for (int32_t j = 0; j < nROFs; ++j) {
729 if (isROFEnabled(layer, j)) {
730 ++enabledROFs;
731 }
732 }
733 return std::format("ROFMask on Layer {} ROFs enabled: {}/{}", layer, enabledROFs, nROFs);
734 }
735
736 GPUh() void print(int32_t layer) const
737 {
738 LOG(info) << asString(layer);
739 }
740#endif
741};
742
743// Per-ROF per-layer boolean mask (uint8_t for GPU compatibility).
744template <int32_t NLayers>
745class ROFMaskTable : public LayerTimingBase<NLayers>
746{
747 public:
750 using TableIndex = uint32_t;
751 using TableEntry = uint8_t;
753
754 ROFMaskTable() = default;
755 GPUh() explicit ROFMaskTable(const LayerTimingBase<NLayers>& timingBase) : LayerTimingBase<NLayers>(timingBase) { init(); }
756
757 GPUh() void init()
758 {
759 int32_t totalROFs = 0;
760 for (int32_t layer{0}; layer < NLayers; ++layer) {
761 mLayerROFOffsets[layer] = totalROFs;
762 totalROFs += this->getLayer(layer).mNROFsTF;
763 }
764 mLayerROFOffsets[NLayers] = totalROFs; // sentinel
765 mFlatMask.resize(totalROFs, 0u);
766 }
767
768 GPUh() size_t getFlatMaskSize() const noexcept { return mFlatMask.size(); }
769
770 GPUh() void setROFEnabled(int32_t layer, int32_t rofId, uint8_t state = 1) noexcept
771 {
772 assert(layer >= 0 && layer < NLayers);
773 assert(rofId >= 0 && rofId < mLayerROFOffsets[layer + 1] - mLayerROFOffsets[layer]);
774 mFlatMask[mLayerROFOffsets[layer] + rofId] = state;
775 }
776
777 GPUh() void setROFsEnabled(int32_t layer, int32_t firstRof, int32_t nRofs, uint8_t state = 1) noexcept
778 {
779 assert(layer >= 0 && layer < NLayers);
780 assert(firstRof >= 0);
781 assert(firstRof + nRofs <= mLayerROFOffsets[layer + 1] - mLayerROFOffsets[layer]);
782 std::memset(mFlatMask.data() + mLayerROFOffsets[layer] + firstRof, state, nRofs);
783 }
784
785 // Enable all ROFs in all layers that are time-compatible with the given BC range
786 GPUh() void selectROF(const BCRange& t)
787 {
788 const int32_t bcStart = t.getFirstEntry();
789 const int32_t bcEnd = t.getEntriesBound();
790 for (int32_t layer{0}; layer < NLayers; ++layer) {
791 const auto& lay = this->getLayer(layer);
792 const int32_t offset = mLayerROFOffsets[layer];
793 for (int32_t rofId{0}; rofId < lay.mNROFsTF; ++rofId) {
794 if (static_cast<int32_t>(lay.getROFStartInBC(rofId)) < bcEnd &&
795 static_cast<int32_t>(lay.getROFEndInBC(rofId)) > bcStart) {
796 mFlatMask[offset + rofId] = 1u;
797 }
798 }
799 }
800 }
801
802 // Reset mask to 0, then enable all ROFs compatible with any of the given BC ranges
803 GPUh() void selectROFs(const std::vector<BCRange>& ts)
804 {
805 resetMask();
806 for (const auto& t : ts) {
807 selectROF(t);
808 }
809 }
810
811 GPUh() void resetMask(uint8_t s = 0u)
812 {
813 std::memset(mFlatMask.data(), s, mFlatMask.size());
814 }
815
816 GPUh() void invertMask()
817 {
818 std::ranges::transform(mFlatMask, mFlatMask.begin(), [](uint8_t x) { return 1 - x; });
819 }
820
821 GPUh() void swap(ROFMaskTable& other) noexcept
822 {
823 std::swap(mFlatMask, other.mFlatMask);
824 std::swap(mLayerROFOffsets, other.mLayerROFOffsets);
825 }
826
827 GPUh() View getView() const
828 {
829 View view;
830 view.mFlatMask = mFlatMask.data();
831 view.mLayerROFOffsets = mLayerROFOffsets;
832 return view;
833 }
834
835 GPUh() View getDeviceView(const TableEntry* deviceFlatMaskPtr, const TableIndex* deviceOffsetPtr) const
836 {
837 View view;
838 view.mFlatMask = deviceFlatMaskPtr;
839 view.mLayerROFOffsets = deviceOffsetPtr;
840 return view;
841 }
842
843 private:
844 TableIndex mLayerROFOffsets[NLayers + 1] = {0};
845 std::vector<TableEntry> mFlatMask;
846};
847
848} // namespace o2::its
849
850#endif
std::string asString(TDataMember const &dm, char *pointer)
uint64_t vertex
Definition RawEventData.h:9
uint64_t bc
Definition RawEventData.h:5
void print() const
int32_t i
#define GPUhi()
#define GPUh()
Header to collect LHC related constants.
Class to refer to the 1st entry and N elements of some group in the continuous container.
uint32_t j
Definition RawData.h:0
GPUhdi() const LayerTiming &getLayer(int32_t layer) const
const LayerTiming & timing
GPUh() void defineLayer(int32_t layer
LayerTiming::BCType T
LayerTiming mLayers[NLayers]
GPUhdi() const expr int32_t getEntries() noexcept
GPUh() void print() const
GPUh() explicit ROFMaskTable(const LayerTimingBase< NLayers > &timingBase)
dataformats::RangeReference< T, T > BCRange
GPUh() size_t getFlatMaskSize() const noexcept
ROFMaskTableView< NLayers, TableEntry, TableIndex > View
GPUh() void setROFEnabled(int32_t layer
LayerTimingBase< NLayers >::T T
std::vector< TableEntry > mFlatMask
dataformats::RangeReference< T, T > TableEntry
LayerTimingBase< NLayers >::T T
const TableIndex * deviceIndicesPtr
GPUh() View getView() const
static GPUh() const expr size_t getIndicesSize()
GPUh() size_t getFlatTableSize() const noexcept
dataformats::RangeReference< T, T > TableIndex
const TableIndex const LayerTiming *deviceLayerTimingPtr const
const TableIndex * deviceIndicesPtr
const TableIndex const LayerTiming *deviceLayerTimingPtr const
GPUh() View getView() const
dataformats::RangeReference< T, T > TableEntry
GPUh() void update(const Vertex *vertices
dataformats::RangeReference< T, T > TableIndex
static GPUh() const expr size_t getIndicesSize()
GPUh() size_t getFlatTableSize() const noexcept
GPUh() void init(const Vertex *vertices
LayerTimingBase< NLayers >::T T
GLint GLenum GLint x
Definition glcorearb.h:403
GLint GLsizei count
Definition glcorearb.h:399
GLuint entry
Definition glcorearb.h:5735
GLsizeiptr size
Definition glcorearb.h:659
GLuint GLuint end
Definition glcorearb.h:469
GLdouble GLdouble right
Definition glcorearb.h:4077
GLsizei maxCount
Definition glcorearb.h:792
GLintptr offset
Definition glcorearb.h:660
typedef void(APIENTRYP PFNGLCULLFACEPROC)(GLenum mode)
GLenum GLuint GLint GLint layer
Definition glcorearb.h:1310
GLuint GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat t0
Definition glcorearb.h:5034
GLuint start
Definition glcorearb.h:469
GLuint GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat t1
Definition glcorearb.h:5034
constexpr int LHCMaxBunches
uint32_t TimeStampType
Definition TimeEstBC.h:26
o2::dataformats::Vertex< o2::its::TimeEstBC > Vertex
Definition Vertex.h:26
uint16_t TimeStampErrorType
Definition TimeEstBC.h:27
uint64_t getTimeStamp(o2::framework::ProcessingContext &pc)
GPUhdi() BCType getROFStartInBC(BCType rofId) const noexcept
GPUhdi() TimeEstBC getROFTimeBounds(BCType rofId
GPUhdi() BCType getROFEndInBC(BCType rofId) const noexcept
GPUhdi() bool isROFEnabled(int32_t layer
GPUh() void printLayer(int32_t layer) const
const TableIndex * mLayerROFOffsets
GPUh() void printAll() const
const TableEntry * mFlatMask
int32_t rofId const noexcept
LOGP(info, "From: {}", mLayers[from].asString())
LOGF(info, "%*s | %*s | %*s | %*s", w_index, "ROF.index", w_first, "First.ROF", w_last, "Last.ROF", w_count, "Count")
GPUhdi() const LayerTiming &getLayer(int32_t layer) const noexcept
GPUhdi() bool doROFsOverlap(int32_t layer0
assert(rofIdx< idx.getEntries())
int32_t size_t rofIdx const noexcept
GPUh() void printSummary() const
LOGF(info, "Overlap mapping: Layer %d -> Layer %d", from, to)
GPUh() const LayerTiming &getClockLayer() const noexcept
GPUh() void printAll() const
Print functions.
GPUh() int32_t getClock() const noexcept
GPUhdi() const TableEntry &getOverlap(int32_t from
GPUhdi() TimeEstBC getTimeStamp(int32_t layer0
LOGP(info, "To : {}", mLayers[to].asString())
LOGF(info, "%.*s-+-%.*s-+-%.*s-+-%.*s", w_index, "----------", w_first, "------------", w_last, "------------", w_count, "----------")
assert(doROFsOverlap(layer0, rof0, layer1, rof1))
assert(rofIdx< idx.getEntries())
GPUh() void printLayer(int32_t layer) const
GPUhdi() const LayerTiming &getLayer(int32_t layer) const noexcept
GPUh() int32_t getMaxVerticesPerROF() const noexcept
GPUh() void printSummary() const
GPUhdi() const TableEntry &getVertices(int32_t layer
GPUhdi() bool isVertexCompatible(int32_t layer
VectorOfTObjectPtrs other
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"