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 <stdexcept>
20#include <cstring>
21#include <cassert>
22#include <utility>
23#include <vector>
24
25#ifndef GPUCA_GPUCODE
26#include <format>
27#include "Framework/Logger.h"
28#endif
29
34#include "GPUCommonMath.h"
35#include "GPUCommonDef.h"
37
39{
40
42
43// Base class for lookup to define layers
45{
46 protected:
47 std::vector<LayerTiming> mLayers;
48
49 public:
51 explicit LayerTimingBase(int32_t nLayers = 0)
52 {
53 if (nLayers < 0) {
54 throw std::invalid_argument{"negative ROF layer count"};
55 }
56 mLayers.resize(nLayers);
57 }
58
59 GPUh() void defineLayer(int32_t layer, T nROFsTF, T rofLength, T rofDelay, T rofBias, T rofTE)
60 {
61 assert(layer >= 0 && layer < getEntries());
63 }
64
65 GPUh() void defineLayer(int32_t layer, const LayerTiming& timing)
66 {
67 assert(layer >= 0 && layer < getEntries());
69 }
70
71 GPUh() const LayerTiming& getLayer(int32_t layer) const
72 {
73 assert(layer >= 0 && layer < getEntries());
74 return mLayers[layer];
75 }
76
77 GPUh() int32_t getEntries() const noexcept { return static_cast<int32_t>(mLayers.size()); }
78
79#ifndef GPUCA_GPUCODE
80 GPUh() void print() const
81 {
82 LOGP(info, "Imposed time structure:");
83 for (int32_t iL{0}; iL < getEntries(); ++iL) {
84 LOGP(info, "\tLayer:{} {}", iL, mLayers[iL].asString());
85 }
86 }
87#endif
88};
89
90// Precalculated lookup table to find overlapping ROFs in another layer given a ROF index in the current layer
92{
93 public:
97
99 explicit ROFOverlapTable(int32_t nLayers = 0) : LayerTimingBase(nLayers), mIndices(static_cast<size_t>(nLayers) * nLayers) {}
100
101 GPUh() void init()
102 {
103 std::vector<std::vector<TableEntry>> table(static_cast<size_t>(getEntries()) * getEntries());
104 for (int32_t i{0}; i < getEntries(); ++i) {
105 for (int32_t j{0}; j < getEntries(); ++j) {
106 if (i != j) { // we do not need self-lookup
107 buildMapping(i, j, table[static_cast<size_t>(i) * getEntries() + j]);
108 }
109 }
110 }
111 flatten(table);
112 }
113
114 GPUh() View getView() const
115 {
116 View view;
117 view.mFlatTable = mFlatTable.data();
118 view.mIndices = mIndices.data();
119 view.mLayers = mLayers.data();
120 view.mLayerCount = getEntries();
121 return view;
122 }
123
124 GPUh() View getDeviceView(const TableEntry* deviceFlatTablePtr, const TableIndex* deviceIndicesPtr, const LayerTiming* deviceLayerTimingPtr) const
125 {
126 View view;
127 view.mFlatTable = deviceFlatTablePtr;
129 view.mLayers = deviceLayerTimingPtr;
130 view.mLayerCount = getEntries();
131 return view;
132 }
133
134 GPUh() size_t getFlatTableSize() const noexcept { return mFlatTable.size(); }
135 GPUh() size_t getIndicesSize() const noexcept { return mIndices.size(); }
136
137 private:
138 GPUh() void buildMapping(int32_t from, int32_t to, std::vector<TableEntry>& table)
139 {
140 const auto& layerFrom = this->mLayers[from];
141 const auto& layerTo = this->mLayers[to];
142 table.resize(layerFrom.mNROFsTF);
143
144 for (int32_t iROF{0}; iROF < layerFrom.mNROFsTF; ++iROF) {
145 int64_t fromStart = o2::gpu::CAMath::Max((int64_t)layerFrom.getROFStartInBC(iROF) - (int64_t)layerFrom.mROFAddTimeErr, int64_t(0));
146 int64_t fromEnd = (int64_t)layerFrom.getROFEndInBC(iROF) + layerFrom.mROFAddTimeErr;
147
148 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));
149 auto lastROFTo = (int32_t)((fromEnd + (int64_t)layerTo.mROFAddTimeErr - (int64_t)layerTo.mROFDelay - (int64_t)layerTo.mROFBias - 1) / (int64_t)layerTo.mROFLength);
150 firstROFTo = o2::gpu::CAMath::Max(0, firstROFTo);
151 lastROFTo = o2::gpu::CAMath::Min((int32_t)layerTo.mNROFsTF - 1, lastROFTo);
152
153 while (firstROFTo <= lastROFTo) {
154 int64_t toStart = o2::gpu::CAMath::Max((int64_t)layerTo.getROFStartInBC(firstROFTo) - (int64_t)layerTo.mROFAddTimeErr, int64_t(0));
155 int64_t toEnd = (int64_t)layerTo.getROFEndInBC(firstROFTo) + layerTo.mROFAddTimeErr;
156 if (toEnd > fromStart && toStart < fromEnd) {
157 break;
158 }
159 ++firstROFTo;
160 }
161 while (lastROFTo >= firstROFTo) {
162 int64_t toStart = o2::gpu::CAMath::Max((int64_t)layerTo.getROFStartInBC(lastROFTo) - (int64_t)layerTo.mROFAddTimeErr, int64_t(0));
163 int64_t toEnd = (int64_t)layerTo.getROFEndInBC(lastROFTo) + layerTo.mROFAddTimeErr;
164 if (toEnd > fromStart && toStart < fromEnd) {
165 break;
166 }
167 --lastROFTo;
168 }
169 int32_t count = (firstROFTo <= lastROFTo) ? (lastROFTo - firstROFTo + 1) : 0;
170 table[iROF] = {static_cast<T>(firstROFTo), static_cast<T>(count)};
171 }
172 }
173
174 GPUh() void flatten(const std::vector<std::vector<TableEntry>>& table)
175 {
176 size_t total{0};
177 for (int32_t i{0}; i < getEntries(); ++i) {
178 for (int32_t j{0}; j < getEntries(); ++j) {
179 if (i != j) { // we do not need self-lookup
180 total += table[static_cast<size_t>(i) * getEntries() + j].size();
181 }
182 }
183 }
184
185 mFlatTable.clear();
186 mFlatTable.reserve(total);
187
188 for (int32_t i{0}; i < getEntries(); ++i) {
189 for (int32_t j{0}; j < getEntries(); ++j) {
190 size_t idx = static_cast<size_t>(i) * getEntries() + j;
191 if (i != j) {
192 mIndices[idx].setFirstEntry(static_cast<T>(mFlatTable.size()));
193 mIndices[idx].setEntries(static_cast<T>(table[static_cast<size_t>(i) * getEntries() + j].size()));
194 mFlatTable.insert(mFlatTable.end(), table[static_cast<size_t>(i) * getEntries() + j].begin(), table[static_cast<size_t>(i) * getEntries() + j].end());
195 } else {
196 mIndices[idx] = {0, 0};
197 }
198 }
199 }
200 }
201
202 std::vector<TableIndex> mIndices;
203 std::vector<TableEntry> mFlatTable;
204};
205
206// Precalculated lookup table to find vertices compatible with ROFs
207// Given a layer and ROF index, returns the range of vertices that overlap in time.
208// The vertex time is defined as symmetrical [t0-e,t0+e]
209// It needs to be guaranteed that the input vertices are sorted by their lower-bound!
210// additionally compatibliyty has to be queried per vertex!
212{
213 public:
219
220 explicit ROFVertexLookupTable(int32_t nLayers = 0) : LayerTimingBase(nLayers), mIndices(nLayers) {}
221
222 GPUh() size_t getFlatTableSize() const noexcept { return mFlatTable.size(); }
223 GPUh() size_t getIndicesSize() const noexcept { return mIndices.size(); }
224
225 // Build the lookup table given a sorted array of vertices
226 // vertices must be sorted by timestamp, then by error (secondary)
227 GPUh() void init(const o2::its::Vertex* vertices, size_t nVertices)
228 {
229 if (nVertices > std::numeric_limits<T>::max()) {
230 LOGF(fatal, "too many vertices %zu, max supported is %u", nVertices, std::numeric_limits<T>::max());
231 }
232
233 std::vector<std::vector<TableEntry>> table(getEntries());
234 for (int32_t layer{0}; layer < getEntries(); ++layer) {
235 buildMapping(layer, vertices, nVertices, table[layer]);
236 }
237 flatten(table);
238 }
239
240 // Pre-allocated needed memory, then use update(...)
241 GPUh() void init()
242 {
243 size_t total{0};
244 for (int32_t layer{0}; layer < getEntries(); ++layer) {
245 total += this->mLayers[layer].mNROFsTF;
246 }
247 mFlatTable.resize(total, {0, 0});
248 size_t offset = 0;
249 for (int32_t layer{0}; layer < getEntries(); ++layer) {
250 size_t nROFs = this->mLayers[layer].mNROFsTF;
251 mIndices[layer].setFirstEntry(static_cast<T>(offset));
252 mIndices[layer].setEntries(static_cast<T>(nROFs));
253 offset += nROFs;
254 }
255 }
256
257 // Recalculate lookup table with new vertices
258 GPUh() void update(const o2::its::Vertex* vertices, size_t nVertices)
259 {
260 size_t offset = 0;
261 for (int32_t layer{0}; layer < getEntries(); ++layer) {
262 const auto& idx = mIndices[layer];
263 size_t nROFs = idx.getEntries();
264 for (size_t iROF = 0; iROF < nROFs; ++iROF) {
265 updateROFMapping(layer, iROF, vertices, nVertices, offset + iROF);
266 }
267 offset += nROFs;
268 }
269 }
270
271 GPUh() View getView() const
272 {
273 View view;
274 view.mFlatTable = mFlatTable.data();
275 view.mIndices = mIndices.data();
276 view.mLayers = mLayers.data();
277 view.mLayerCount = getEntries();
278 return view;
279 }
280
281 GPUh() View getDeviceView(const TableEntry* deviceFlatTablePtr, const TableIndex* deviceIndicesPtr, const LayerTiming* deviceLayerTimingPtr) const
282 {
283 View view;
284 view.mFlatTable = deviceFlatTablePtr;
286 view.mLayers = deviceLayerTimingPtr;
287 view.mLayerCount = getEntries();
288 return view;
289 }
290
291 private:
292 // Build the mapping for one layer
293 GPUh() void buildMapping(int32_t layer, const o2::its::Vertex* vertices, size_t nVertices, std::vector<TableEntry>& table)
294 {
295 const auto& layerDef = this->mLayers[layer];
296 table.resize(layerDef.mNROFsTF);
297 size_t vertexSearchStart = 0;
298 for (int32_t iROF{0}; iROF < layerDef.mNROFsTF; ++iROF) {
299 int64_t rofLower = o2::gpu::CAMath::Max((int64_t)layerDef.getROFStartInBC(iROF) - (int64_t)layerDef.mROFAddTimeErr, int64_t(0));
300 int64_t rofUpper = (int64_t)layerDef.getROFEndInBC(iROF) + layerDef.mROFAddTimeErr;
301 size_t lastVertex = binarySearchFirst(vertices, nVertices, vertexSearchStart, rofUpper);
302 size_t firstVertex = vertexSearchStart;
303 while (firstVertex < lastVertex) {
304 auto vUpper = (int64_t)vertices[firstVertex].getTimeStamp().upper();
305 if (vUpper > rofLower) {
306 break;
307 }
308 ++firstVertex;
309 }
310 size_t count = (lastVertex > firstVertex) ? (lastVertex - firstVertex) : 0;
311 table[iROF] = {static_cast<T>(firstVertex), static_cast<T>(count)};
312 vertexSearchStart = firstVertex;
313 }
314 }
315
316 // Update a single ROF's vertex mapping
317 GPUh() void updateROFMapping(int32_t layer, size_t iROF, const o2::its::Vertex* vertices, size_t nVertices, size_t flatTableIdx)
318 {
319 const auto& layerDef = this->mLayers[layer];
320 int64_t rofLower = o2::gpu::CAMath::Max((int64_t)layerDef.getROFStartInBC(iROF) - (int64_t)layerDef.mROFAddTimeErr, int64_t(0));
321 int64_t rofUpper = (int64_t)layerDef.getROFEndInBC(iROF) + layerDef.mROFAddTimeErr;
322 size_t lastVertex = binarySearchFirst(vertices, nVertices, 0, rofUpper);
323 size_t firstVertex = 0;
324 while (firstVertex < lastVertex) {
325 int64_t vUpper = (int64_t)vertices[firstVertex].getTimeStamp().getTimeStamp() +
326 (int64_t)vertices[firstVertex].getTimeStamp().getTimeStampError();
327 if (vUpper > rofLower) {
328 break;
329 }
330 ++firstVertex;
331 }
332 size_t count = (lastVertex > firstVertex) ? (lastVertex - firstVertex) : 0;
333 mFlatTable[flatTableIdx].setFirstEntry(static_cast<T>(firstVertex));
334 mFlatTable[flatTableIdx].setEntries(static_cast<T>(count));
335 }
336
337 // Binary search for first vertex where lowerBC >= targetBC
338 GPUh() size_t binarySearchFirst(const o2::its::Vertex* vertices, size_t nVertices, size_t searchStart, BCType targetBC) const
339 {
340 size_t left = searchStart;
341 size_t right = nVertices;
342 while (left < right) {
343 size_t mid = left + ((right - left) / 2);
344 int64_t lower = (int64_t)vertices[mid].getTimeStamp().lower();
345 if (lower < targetBC) {
346 left = mid + 1;
347 } else {
348 right = mid;
349 }
350 }
351 return left;
352 }
353
354 // Compress the temporary table into a single flat table
355 GPUh() void flatten(const std::vector<std::vector<TableEntry>>& table)
356 {
357 // Count total entries
358 size_t total{0};
359 for (int32_t i{0}; i < getEntries(); ++i) {
360 total += table[i].size();
361 }
362
363 mFlatTable.clear();
364 mFlatTable.reserve(total);
365
366 // Build flat table and indices
367 for (int32_t i{0}; i < getEntries(); ++i) {
368 mIndices[i].setFirstEntry(static_cast<T>(mFlatTable.size()));
369 mIndices[i].setEntries(static_cast<T>(table[i].size()));
370 mFlatTable.insert(mFlatTable.end(), table[i].begin(), table[i].end());
371 }
372 }
373
374 std::vector<TableIndex> mIndices;
375 std::vector<TableEntry> mFlatTable;
376};
377
378// Per-ROF per-layer boolean mask (uint8_t for GPU compatibility).
380{
381 public:
384 using TableIndex = uint32_t;
385 using TableEntry = uint8_t;
387
388 explicit ROFMaskTable(int32_t nLayers = 0) : LayerTimingBase(nLayers), mLayerROFOffsets(static_cast<size_t>(nLayers) + 1, 0) {}
389 GPUh() explicit ROFMaskTable(const LayerTimingBase& timingBase) : LayerTimingBase(timingBase), mLayerROFOffsets(static_cast<size_t>(getEntries()) + 1, 0) { init(); }
390
391 GPUh() void init()
392 {
393 int32_t totalROFs = 0;
394 for (int32_t layer{0}; layer < getEntries(); ++layer) {
395 mLayerROFOffsets[layer] = totalROFs;
396 totalROFs += this->getLayer(layer).mNROFsTF;
397 }
398 mLayerROFOffsets[getEntries()] = totalROFs; // sentinel
399 mFlatMask.resize(totalROFs, 0u);
400 }
401
402 GPUh() size_t getFlatMaskSize() const noexcept { return mFlatMask.size(); }
403
404 GPUh() void setROFEnabled(int32_t layer, int32_t rofId, uint8_t state = 1) noexcept
405 {
406 assert(layer >= 0 && layer < getEntries());
407 assert(rofId >= 0 && rofId < mLayerROFOffsets[layer + 1] - mLayerROFOffsets[layer]);
408 mFlatMask[mLayerROFOffsets[layer] + rofId] = state;
409 }
410
411 GPUh() void setROFsEnabled(int32_t layer, int32_t firstRof, int32_t nRofs, uint8_t state = 1) noexcept
412 {
413 assert(layer >= 0 && layer < getEntries());
414 assert(firstRof >= 0);
415 assert(firstRof + nRofs <= mLayerROFOffsets[layer + 1] - mLayerROFOffsets[layer]);
416 std::memset(mFlatMask.data() + mLayerROFOffsets[layer] + firstRof, state, nRofs);
417 }
418
419 // Enable all ROFs in all layers that are time-compatible with the given BC range
420 GPUh() void selectROF(const BCRange& t)
421 {
422 const int32_t bcStart = t.getFirstEntry();
423 const int32_t bcEnd = t.getEntriesBound();
424 for (int32_t layer{0}; layer < getEntries(); ++layer) {
425 const auto& lay = this->getLayer(layer);
426 const int32_t offset = mLayerROFOffsets[layer];
427 for (int32_t rofId{0}; rofId < lay.mNROFsTF; ++rofId) {
428 if (static_cast<int32_t>(lay.getROFStartInBC(rofId)) < bcEnd &&
429 static_cast<int32_t>(lay.getROFEndInBC(rofId)) > bcStart) {
430 mFlatMask[offset + rofId] = 1u;
431 }
432 }
433 }
434 }
435
436 // Reset mask to 0, then enable all ROFs compatible with any of the given BC ranges
437 GPUh() void selectROFs(const std::vector<BCRange>& ts)
438 {
439 resetMask();
440 for (const auto& t : ts) {
441 selectROF(t);
442 }
443 }
444
445 GPUh() void resetMask(uint8_t s = 0u)
446 {
447 std::memset(mFlatMask.data(), s, mFlatMask.size());
448 }
449
450 GPUh() void invertMask()
451 {
452 for (auto& state : mFlatMask) {
453 state = 1 - state;
454 }
455 }
456
457 GPUh() void swap(ROFMaskTable& other) noexcept
458 {
459 std::swap(mLayers, other.mLayers);
460 std::swap(mFlatMask, other.mFlatMask);
461 std::swap(mLayerROFOffsets, other.mLayerROFOffsets);
462 }
463
464 GPUh() View getView() const
465 {
466 View view;
467 view.mFlatMask = mFlatMask.data();
468 view.mLayerROFOffsets = mLayerROFOffsets.data();
469 view.mLayerCount = getEntries();
470 return view;
471 }
472
473 GPUh() View getDeviceView(const TableEntry* deviceFlatMaskPtr, const TableIndex* deviceOffsetPtr) const
474 {
475 View view;
476 view.mFlatMask = deviceFlatMaskPtr;
477 view.mLayerROFOffsets = deviceOffsetPtr;
478 view.mLayerCount = getEntries();
479 return view;
480 }
481
482 private:
483 std::vector<TableIndex> mLayerROFOffsets;
484 std::vector<TableEntry> mFlatMask;
485};
486
487} // namespace o2::itsmft::tracking
488
489namespace o2::its
490{
492
493// Keep the fixed-layer API for legacy ITS callers; storage and algorithms are
494// shared with the runtime tables used by the common tracker.
495template <int32_t NLayers>
497{
498 public:
499 LayerTimingBase() : o2::itsmft::tracking::LayerTimingBase(NLayers) {}
500 GPUhdi() constexpr int32_t getEntries() const noexcept { return NLayers; }
501};
502
503template <int32_t NLayers, typename TableEntry, typename TableIndex>
505template <int32_t NLayers, typename TableEntry, typename TableIndex>
507template <int32_t NLayers, typename TableEntry, typename TableIndex>
509
510template <int32_t NLayers>
512{
513 public:
514 ROFOverlapTable() : o2::itsmft::tracking::ROFOverlapTable(NLayers) {}
515 GPUhdi() constexpr int32_t getEntries() const noexcept { return NLayers; }
516 static GPUh() constexpr size_t getIndicesSize() { return static_cast<size_t>(NLayers) * NLayers; }
517};
518
519template <int32_t NLayers>
521{
522 public:
523 ROFVertexLookupTable() : o2::itsmft::tracking::ROFVertexLookupTable(NLayers) {}
524 GPUhdi() constexpr int32_t getEntries() const noexcept { return NLayers; }
525 static GPUh() constexpr size_t getIndicesSize() { return NLayers; }
526};
527
528template <int32_t NLayers>
530{
531 public:
532 ROFMaskTable() : o2::itsmft::tracking::ROFMaskTable(NLayers) {}
533 GPUh() explicit ROFMaskTable(const o2::itsmft::tracking::LayerTimingBase& timing)
534 : o2::itsmft::tracking::ROFMaskTable(timing)
535 {
536 if (timing.getEntries() != NLayers) {
537 throw std::invalid_argument{"ROF mask layer count differs from legacy table extent"};
538 }
539 }
540 GPUh() void swap(ROFMaskTable& other) noexcept { o2::itsmft::tracking::ROFMaskTable::swap(other); }
541 GPUhdi() constexpr int32_t getEntries() const noexcept { return NLayers; }
542};
543} // namespace o2::its
544
545#endif
std::string asString(TDataMember const &dm, char *pointer)
void print() const
int32_t i
Header to collect LHC related constants.
double lower[3]
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 expr int32_t getEntries() const noexcept
GPUhdi() const expr int32_t getEntries() const noexcept
GPUh() explicit ROFMaskTable(const o2
GPUh() void swap(ROFMaskTable &other) noexcept
static GPUh() const expr size_t getIndicesSize()
GPUhdi() const expr int32_t getEntries() const noexcept
GPUhdi() const expr int32_t getEntries() const noexcept
static GPUh() const expr size_t getIndicesSize()
GPUh() int32_t getEntries() const noexcept
GPUh() void defineLayer(int32_t layer
std::vector< LayerTiming > mLayers
GPUh() const LayerTiming &getLayer(int32_t layer) const
GPUh() void setROFEnabled(int32_t layer
GPUh() explicit ROFMaskTable(const LayerTimingBase &timingBase)
ROFMaskView< TableEntry, TableIndex > View
std::vector< TableEntry > mFlatMask
dataformats::RangeReference< T, T > BCRange
GPUh() size_t getFlatMaskSize() const noexcept
const TableIndex const LayerTiming *deviceLayerTimingPtr const
GPUh() size_t getIndicesSize() const noexcept
GPUh() size_t getFlatTableSize() const noexcept
dataformats::RangeReference< T, T > TableEntry
GPUh() size_t getIndicesSize() const noexcept
const TableIndex const LayerTiming *deviceLayerTimingPtr const
GPUh() size_t getFlatTableSize() const noexcept
dataformats::RangeReference< T, T > TableEntry
GLint GLsizei count
Definition glcorearb.h:399
GLsizeiptr size
Definition glcorearb.h:659
GLdouble GLdouble right
Definition glcorearb.h:4077
GLint left
Definition glcorearb.h:1979
GLintptr offset
Definition glcorearb.h:660
typedef void(APIENTRYP PFNGLCULLFACEPROC)(GLenum mode)
GLenum GLuint GLint GLint layer
Definition glcorearb.h:1310
uint32_t trackClusterIndicesSize noexcept
ROFTimingLayer LayerTiming
o2::its::Vertex Vertex
Definition TimeFrame.h:47
uint64_t getTimeStamp(o2::framework::ProcessingContext &pc)
a couple of static helper functions to create timestamp values for CCDB queries or override obsolete ...
o2::its::TimeStampType BCType
Definition ROFViews.h:42
VectorOfTObjectPtrs other