Project
Loading...
Searching...
No Matches
TrackerTraits.cxx
Go to the documentation of this file.
1// Copyright 2019-2020 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.
15
16#include <algorithm>
17#include <array>
18#include <iterator>
19#include <mutex>
20#include <ranges>
21#include <cmath>
22#include <type_traits>
23#include <vector>
24
25#include <oneapi/tbb/blocked_range.h>
26#include <oneapi/tbb/enumerable_thread_specific.h>
27#include <oneapi/tbb/parallel_for.h>
28#include <oneapi/tbb/parallel_scan.h>
29#include <oneapi/tbb/parallel_sort.h>
30
32#include "GPUCommonMath.h"
34#include "ITStracking/Cell.h"
44
45namespace o2::its
46{
47
52
53template <int NLayers>
55{
56 this->mTaskArena->execute([&] {
57 mTimeFrame->initialise(mTrkParams[iteration], mTrkParams[iteration].NLayers, iteration);
58 });
59}
60
61template <int NLayers>
62void TrackerTraits<NLayers>::computeLayerTracklets(const int iteration, int iVertex)
63{
64 const auto topology = mTimeFrame->getTrackingTopologyView();
65 const Vertex diamondVert(mTrkParams[iteration].Diamond, mTrkParams[iteration].DiamondCov, 1, 1.f);
66 gsl::span<const Vertex> diamondSpan(&diamondVert, 1);
67
68 mTaskArena->execute([&] {
69 tbb::parallel_for(0, static_cast<int>(topology.nLinks), [&](const int linkId) {
70 mTimeFrame->getTracklets()[linkId].clear();
71 mTimeFrame->getTrackletsLabel(linkId).clear();
72 auto& lut = mTimeFrame->getTrackletsLookupTable()[linkId];
73 std::fill(lut.begin(), lut.end(), 0);
74 });
75
76 auto forTracklets = [&](int linkId, int pivotROF, auto&& emit) {
77 const auto& link = topology.getLink(linkId);
78 if (!mTimeFrame->getROFMaskView().isROFEnabled(link.fromLayer, pivotROF)) {
79 return;
80 }
81 gsl::span<const Vertex> primaryVertices = mTrkParams[iteration].UseDiamond ? diamondSpan : mTimeFrame->getPrimaryVertices(link.fromLayer, pivotROF);
82 if (primaryVertices.empty()) {
83 return;
84 }
85 const int startVtx = iVertex >= 0 ? iVertex : 0;
86 const int endVtx = iVertex >= 0 ? o2::gpu::CAMath::Min(iVertex + 1, int(primaryVertices.size())) : int(primaryVertices.size());
87 if (endVtx <= startVtx || (iVertex + 1) > primaryVertices.size()) {
88 return;
89 }
90
91 const auto& rofOverlap = mTimeFrame->getROFOverlapTableView().getOverlap(link.fromLayer, link.toLayer, pivotROF);
92 if (!rofOverlap.getEntries()) {
93 return;
94 }
95
96 auto layer0 = mTimeFrame->getClustersOnLayer(pivotROF, link.fromLayer);
97 if (layer0.empty()) {
98 return;
99 }
100
101 const float meanDeltaR = mTrkParams[iteration].LayerRadii[link.toLayer] - mTrkParams[iteration].LayerRadii[link.fromLayer];
102 const float phiCut = mTimeFrame->getLinkPhiCut(linkId);
103 const float msAngle = mTimeFrame->getLinkMSAngle(linkId);
104
105 for (int iCluster = 0; iCluster < int(layer0.size()); ++iCluster) {
106 const Cluster& currentCluster = layer0[iCluster];
107 const int currentSortedIndex = mTimeFrame->getSortedIndex(pivotROF, link.fromLayer, iCluster);
108 if (mTimeFrame->isClusterUsed(link.fromLayer, currentCluster.clusterId)) {
109 continue;
110 }
111 const float inverseR0 = 1.f / currentCluster.radius;
112
113 for (int iV = startVtx; iV < endVtx; ++iV) {
114 const auto& pv = primaryVertices[iV];
115 if (!mTimeFrame->getROFVertexLookupTableView().isVertexCompatible(link.fromLayer, pivotROF, pv)) {
116 continue;
117 }
118 if (pv.isFlagSet(Vertex::Flags::UPCMode) != mTrkParams[iteration].PassFlags[IterationStep::SelectUPCVertices]) {
119 continue;
120 }
121 const float resolution = o2::gpu::CAMath::Sqrt(math_utils::Sq(mTimeFrame->getPositionResolution(link.fromLayer)) + math_utils::Sq(mTrkParams[iteration].PVres) / float(pv.getNContributors()));
122 const float tanLambda = (currentCluster.zCoordinate - pv.getZ()) * inverseR0;
123 const float zAtRmin = tanLambda * (mTimeFrame->getMinR(link.toLayer) - currentCluster.radius) + currentCluster.zCoordinate;
124 const float zAtRmax = tanLambda * (mTimeFrame->getMaxR(link.toLayer) - currentCluster.radius) + currentCluster.zCoordinate;
125 const float sqInvDeltaZ0 = 1.f / (math_utils::Sq(currentCluster.zCoordinate - pv.getZ()) + constants::Tolerance);
126 const float sigmaZ = o2::gpu::CAMath::Sqrt((math_utils::Sq(resolution) * math_utils::Sq(tanLambda) * ((math_utils::Sq(inverseR0) + sqInvDeltaZ0) * math_utils::Sq(meanDeltaR) + 1.f)) + math_utils::Sq(meanDeltaR * msAngle));
127 const auto bins = o2::its::getBinsRect(currentCluster, link.toLayer, zAtRmin, zAtRmax,
128 sigmaZ * mTrkParams[iteration].NSigmaCut, phiCut,
129 mTimeFrame->getIndexTableUtils());
130 if (bins.x < 0) {
131 continue;
132 }
133 int phiBinsNum = bins.w - bins.y + 1;
134 if (phiBinsNum < 0) {
135 phiBinsNum += mTrkParams[iteration].PhiBins;
136 }
137
138 for (int targetROF = rofOverlap.getFirstEntry(); targetROF < rofOverlap.getEntriesBound(); ++targetROF) {
139 if (!mTimeFrame->getROFMaskView().isROFEnabled(link.toLayer, targetROF)) {
140 continue;
141 }
142 auto layer1 = mTimeFrame->getClustersOnLayer(targetROF, link.toLayer);
143 if (layer1.empty()) {
144 continue;
145 }
146 const auto ts = mTimeFrame->getROFOverlapTableView().getTimeStamp(link.fromLayer, pivotROF, link.toLayer, targetROF);
147 if (!ts.isCompatible(pv.getTimeStamp())) {
148 continue;
149 }
150 const auto& targetIndexTable = mTimeFrame->getIndexTable(targetROF, link.toLayer);
151 const int zBinRange = (bins.z - bins.x) + 1;
152 for (int iPhi = 0; iPhi < phiBinsNum; ++iPhi) {
153 const int iPhiBin = (bins.y + iPhi) % mTrkParams[iteration].PhiBins;
154 const int firstBinIdx = mTimeFrame->getIndexTableUtils().getBinIndex(bins.x, iPhiBin);
155 const int maxBinIdx = firstBinIdx + zBinRange;
156 const int firstRow = targetIndexTable[firstBinIdx];
157 const int lastRow = targetIndexTable[maxBinIdx];
158 for (int iNext = firstRow; iNext < lastRow; ++iNext) {
159 if (iNext >= int(layer1.size())) {
160 break;
161 }
162 const Cluster& nextCluster = layer1[iNext];
163 if (mTimeFrame->isClusterUsed(link.toLayer, nextCluster.clusterId)) {
164 continue;
165 }
166 const float deltaZ = o2::gpu::CAMath::Abs((tanLambda * (nextCluster.radius - currentCluster.radius)) + currentCluster.zCoordinate - nextCluster.zCoordinate);
167
168 if (deltaZ / sigmaZ < mTrkParams[iteration].NSigmaCut &&
169 math_utils::isPhiDifferenceBelow(currentCluster.phi, nextCluster.phi, phiCut)) {
170 const float phi{o2::math_utils::fastATan2(currentCluster.yCoordinate - nextCluster.yCoordinate, currentCluster.xCoordinate - nextCluster.xCoordinate)};
171 const float tanL = (currentCluster.zCoordinate - nextCluster.zCoordinate) / (currentCluster.radius - nextCluster.radius);
172 emit(currentSortedIndex, mTimeFrame->getSortedIndex(targetROF, link.toLayer, iNext), tanL, phi, ts);
173 }
174 }
175 }
176 }
177 }
178 }
179 };
180
181 if (mTaskArena->max_concurrency() <= 1) {
182 for (int linkId{0}; linkId < topology.nLinks; ++linkId) {
183 const int fromLayer = topology.getLink(linkId).fromLayer;
184 const int endROF = mTimeFrame->getROFOverlapTableView().getLayer(fromLayer).mNROFsTF;
185 auto& tracklets = mTimeFrame->getTracklets()[linkId];
186 for (int pivotROF{0}; pivotROF < endROF; ++pivotROF) {
187 forTracklets(linkId, pivotROF, [&tracklets](auto&&... args) { tracklets.emplace_back(std::forward<decltype(args)>(args)...); });
188 }
189 }
190 } else {
191 const int maxConcurrency = std::max(1, mTaskArena->max_concurrency());
192 const int nConcurrentSinks = std::min(static_cast<int>(topology.nLinks), maxConcurrency);
193 tbb::parallel_for(0, static_cast<int>(topology.nLinks), [&](const int linkId) {
194 const int fromLayer = topology.getLink(linkId).fromLayer;
195 const int startROF = 0, endROF = mTimeFrame->getROFOverlapTableView().getLayer(fromLayer).mNROFsTF;
196 auto& tracklets = mTimeFrame->getTracklets()[linkId];
197 const auto key = CapacityEstimator::makeKey(SlabSite::Tracklets, iteration, iVertex + 1, linkId);
198 const auto scale = static_cast<double>(mTimeFrame->getClusters()[fromLayer].size());
199 const size_t capacity = mTimeFrame->getCapacityEstimator().capacity(key, scale);
200
201 UnorderedSlabSink<Tracklet> sink{{.capacity = capacity, .nThreads = maxConcurrency, .nConcurrentSinks = nConcurrentSinks}, mMemoryPool.get()};
202 tbb::parallel_for(startROF, endROF, [&](const int pivotROF) {
203 auto& handle = sink.local();
204 forTracklets(linkId, pivotROF, [&handle](auto&&... args) { handle.emplace(std::forward<decltype(args)>(args)...); });
205 });
206 const auto st = sink.stats();
207 sink.finalizeUnordered(tracklets);
208 mTimeFrame->getCapacityEstimator().update(key, scale, st.requested, st.capacity, st.emitted, st.spilled,
209 st.overflowed, st.memoryLimited);
210 });
211 }
212
213 tbb::parallel_for(0, static_cast<int>(topology.nLinks), [&](const int linkId) {
215 auto& trkl{mTimeFrame->getTracklets()[linkId]};
216 if (mTaskArena->max_concurrency() > 1) {
217 tbb::parallel_sort(trkl.begin(), trkl.end());
218 } else {
219 std::sort(trkl.begin(), trkl.end());
220 }
221 if (iVertex < 0) { // duplicates can exist simply since we evaluate for all vertices if we do perVertex duplicates cannot exist
222 trkl.erase(std::unique(trkl.begin(), trkl.end()), trkl.end());
223 trkl.shrink_to_fit();
224 }
225 auto& lut{mTimeFrame->getTrackletsLookupTable()[linkId]};
226 if (!trkl.empty()) {
227 const size_t nTracklets{trkl.size()};
228 const Tracklet* tkls{trkl.data()};
229 tbb::parallel_for(tbb::blocked_range<size_t>(0, nTracklets), [&](const tbb::blocked_range<size_t>& r) {
230 size_t begin{r.begin()}, end{r.end()};
231 const auto sameRun = [tkls](size_t i, size_t j) { return tkls[i].firstClusterIndex == tkls[j].firstClusterIndex; };
232 while (begin > 0 && begin < nTracklets && sameRun(begin, begin - 1)) {
233 ++begin;
234 }
235 while (end > 0 && end < nTracklets && sameRun(end, end - 1)) {
236 ++end;
237 }
238 for (size_t i{begin}; i < end; ++i) {
239 ++lut[tkls[i].firstClusterIndex + 1];
240 }
241 });
242 int* data{lut.data()};
243 tbb::parallel_scan(
244 tbb::blocked_range<size_t>(0, lut.size()), 0,
245 [data](const tbb::blocked_range<size_t>& r, int running, bool isFinal) {
246 for (size_t i{r.begin()}; i < r.end(); ++i) {
247 running += data[i];
248 if (isFinal) {
249 data[i] = running;
250 }
251 }
252 return running;
253 },
254 std::plus<int>());
255 }
256 });
257
259 if (mTimeFrame->hasMCinformation() && mTrkParams[iteration].CreateArtefactLabels) {
260 tbb::parallel_for(0, static_cast<int>(topology.nLinks), [&](const int linkId) {
261 const auto& link = topology.getLink(linkId);
262 for (auto& trk : mTimeFrame->getTracklets()[linkId]) {
263 MCCompLabel label;
264 int currentId{mTimeFrame->getClusters()[link.fromLayer][trk.firstClusterIndex].clusterId};
265 int nextId{mTimeFrame->getClusters()[link.toLayer][trk.secondClusterIndex].clusterId};
266 for (const auto& lab1 : mTimeFrame->getClusterLabels(link.fromLayer, currentId)) {
267 for (const auto& lab2 : mTimeFrame->getClusterLabels(link.toLayer, nextId)) {
268 if (lab1 == lab2 && lab1.isValid()) {
269 label = lab1;
270 break;
271 }
272 }
273 if (label.isValid()) {
274 break;
275 }
276 }
277 mTimeFrame->getTrackletsLabel(linkId).emplace_back(label);
278 }
279 });
280 }
281 });
282}
283
284template <int NLayers>
286{
287 const auto topology = mTimeFrame->getTrackingTopologyView();
288 const bool createLabels = mTimeFrame->hasMCinformation() && mTrkParams[iteration].CreateArtefactLabels;
289
290 mTaskArena->execute([&] {
291 const int maxConcurrency = std::max(1, mTaskArena->max_concurrency());
292 auto clearTopology = [&](const int cellTopologyId) {
293 deepVectorClear(mTimeFrame->getCells()[cellTopologyId]);
294 deepVectorClear(mTimeFrame->getCellsLookupTable()[cellTopologyId]);
295 if (createLabels) {
296 deepVectorClear(mTimeFrame->getCellsLabel(cellTopologyId));
297 }
298 };
299 if (maxConcurrency > 1) {
300 tbb::parallel_for(0, static_cast<int>(topology.nCells), clearTopology);
301 } else {
302 for (int cellTopologyId{0}; cellTopologyId < topology.nCells; ++cellTopologyId) {
303 clearTopology(cellTopologyId);
304 }
305 }
306
307 auto forTrackletCells = [&](int cellTopologyId, int iTracklet, auto&& emit) {
308 const auto& cellTopology = topology.getCell(cellTopologyId);
309 const auto& firstLink = topology.getLink(cellTopology.firstLink);
310 const auto& secondLink = topology.getLink(cellTopology.secondLink);
311 const Tracklet& currentTracklet{mTimeFrame->getTracklets()[cellTopology.firstLink][iTracklet]};
312 const int nextLayerClusterIndex{currentTracklet.secondClusterIndex};
313 const int nextLayerFirstTrackletIndex{mTimeFrame->getTrackletsLookupTable()[cellTopology.secondLink][nextLayerClusterIndex]};
314 const int nextLayerLastTrackletIndex{mTimeFrame->getTrackletsLookupTable()[cellTopology.secondLink][nextLayerClusterIndex + 1]};
315 for (int iNextTracklet{nextLayerFirstTrackletIndex}; iNextTracklet < nextLayerLastTrackletIndex; ++iNextTracklet) {
316 const Tracklet& nextTracklet{mTimeFrame->getTracklets()[cellTopology.secondLink][iNextTracklet]};
317 if (nextTracklet.firstClusterIndex != nextLayerClusterIndex) {
318 break;
319 }
320 if (!currentTracklet.getTimeStamp().isCompatible(nextTracklet.getTimeStamp())) {
321 continue;
322 }
323
324 const float deltaTanLambdaSigma = std::abs(currentTracklet.tanLambda - nextTracklet.tanLambda) / mTrkParams[iteration].CellDeltaTanLambdaSigma;
325 if (deltaTanLambdaSigma < mTrkParams[iteration].NSigmaCut) {
326
328 const int clusId[3]{
329 mTimeFrame->getClusters()[firstLink.fromLayer][currentTracklet.firstClusterIndex].clusterId,
330 mTimeFrame->getClusters()[firstLink.toLayer][nextTracklet.firstClusterIndex].clusterId,
331 mTimeFrame->getClusters()[secondLink.toLayer][nextTracklet.secondClusterIndex].clusterId};
332 const int hitLayers[3]{firstLink.fromLayer, firstLink.toLayer, secondLink.toLayer};
333 const auto& cluster1Glo = mTimeFrame->getUnsortedClusters()[firstLink.fromLayer][clusId[0]];
334 const auto& cluster2Glo = mTimeFrame->getUnsortedClusters()[firstLink.toLayer][clusId[1]];
335 const auto& cluster3Tf = mTimeFrame->getTrackingFrameInfoOnLayer(secondLink.toLayer)[clusId[2]];
336 auto track{o2::its::track::buildTrackSeed(cluster1Glo, cluster2Glo, cluster3Tf, mBz)};
337
338 float chi2{0.f};
339 bool good{false};
340 for (int iC{2}; iC--;) {
341 const int hitLayer = hitLayers[iC];
342 const TrackingFrameInfo& trackingHit = mTimeFrame->getTrackingFrameInfoOnLayer(hitLayer)[clusId[iC]];
343
344 if (!track.rotate(trackingHit.alphaTrackingFrame)) {
345 break;
346 }
347
348 if (!track.propagateTo(trackingHit.xTrackingFrame, getBz())) {
349 break;
350 }
351
352 if (!track.correctForMaterial(mTrkParams[iteration].LayerxX0[hitLayer], mTrkParams[iteration].LayerxX0[hitLayer] * constants::Radl * constants::Rho, true)) {
353 break;
354 }
355
356 const auto predChi2{track.getPredictedChi2Quiet(trackingHit.positionTrackingFrame, trackingHit.covarianceTrackingFrame)};
357 if (!iC && predChi2 > mTrkParams[iteration].MaxChi2ClusterAttachment) {
358 break;
359 }
360
361 if (!track.o2::track::TrackParCov::update(trackingHit.positionTrackingFrame, trackingHit.covarianceTrackingFrame)) {
362 break;
363 }
364
365 good = !iC;
366 chi2 += predChi2;
367 }
368 if (good) {
369 TimeEstBC ts = currentTracklet.getTimeStamp();
370 ts += nextTracklet.getTimeStamp();
371 emit(cellTopology.hitLayerMask, clusId[0], clusId[1], clusId[2], iTracklet, iNextTracklet, track, chi2, ts);
372 }
373 }
374 }
375 };
376
377 bounded_vector<int> activeTopologies(mMemoryPool.get());
378 activeTopologies.reserve(topology.nCells);
379 for (int cellTopologyId = 0; cellTopologyId < topology.nCells; ++cellTopologyId) {
380 const auto& cellTopology = topology.getCell(cellTopologyId);
381 if (!mTimeFrame->getTracklets()[cellTopology.firstLink].empty() &&
382 !mTimeFrame->getTracklets()[cellTopology.secondLink].empty()) {
383 activeTopologies.push_back(cellTopologyId);
384 }
385 }
386
387 const int nConcurrentSinks = std::min(maxConcurrency, static_cast<int>(activeTopologies.size()));
388 auto processTopology = [&](const int cellTopologyId) {
389 const auto& cellTopology = topology.getCell(cellTopologyId);
390
391 auto& layerCells = mTimeFrame->getCells()[cellTopologyId];
392 auto& lut = mTimeFrame->getCellsLookupTable()[cellTopologyId];
393 const int currentLayerTrackletsNum{static_cast<int>(mTimeFrame->getTracklets()[cellTopology.firstLink].size())};
394
395 const auto key = CapacityEstimator::makeKey(SlabSite::Cells, iteration, 0, cellTopologyId);
396 const auto scale = static_cast<double>(currentLayerTrackletsNum);
397 if (maxConcurrency > 1) {
398 const size_t capacity = mTimeFrame->getCapacityEstimator().capacity(key, scale);
399
400 GroupedSlabSink<CellSeed> sink{{.capacity = capacity, .nThreads = maxConcurrency, .nConcurrentSinks = nConcurrentSinks}, mMemoryPool.get()};
401 tbb::parallel_for(0, currentLayerTrackletsNum, [&](const int iTracklet) {
402 auto& handle = sink.local();
403 handle.beginProducer(iTracklet);
404 forTrackletCells(cellTopologyId, iTracklet, [&handle](auto&&... args) { handle.emplace(std::forward<decltype(args)>(args)...); });
405 });
406 const auto st = sink.stats();
407 sink.finalizeGrouped(size_t(currentLayerTrackletsNum), lut, layerCells);
408 mTimeFrame->getCapacityEstimator().update(key, scale, st.requested, st.capacity, st.emitted, st.spilled,
409 st.overflowed, st.memoryLimited);
410 } else {
411 lut.resize(currentLayerTrackletsNum + 1);
412 for (int iTracklet{0}; iTracklet < currentLayerTrackletsNum; ++iTracklet) {
413 lut[iTracklet] = static_cast<int>(layerCells.size());
414 forTrackletCells(cellTopologyId, iTracklet, [&](auto&&... args) {
415 layerCells.emplace_back(std::forward<decltype(args)>(args)...);
416 });
417 }
418 lut.back() = static_cast<int>(layerCells.size());
419 }
420
421 if (createLabels) {
422 auto& labels = mTimeFrame->getCellsLabel(cellTopologyId);
423 labels.reserve(layerCells.size());
424 for (const auto& cell : layerCells) {
425 MCCompLabel currentLab{mTimeFrame->getTrackletsLabel(cellTopology.firstLink)[cell.getFirstTrackletIndex()]};
426 MCCompLabel nextLab{mTimeFrame->getTrackletsLabel(cellTopology.secondLink)[cell.getSecondTrackletIndex()]};
427 labels.emplace_back(currentLab == nextLab ? currentLab : MCCompLabel());
428 }
429 }
430 };
431
432 if (maxConcurrency > 1) {
433 tbb::parallel_for(0, static_cast<int>(activeTopologies.size()), [&](const int i) {
434 processTopology(activeTopologies[i]);
435 });
436 } else {
437 for (const int cellTopologyId : activeTopologies) {
438 processTopology(cellTopologyId);
439 }
440 }
441
442 auto clearTracklets = [&](const int linkId) {
443 deepVectorClear(mTimeFrame->getTracklets()[linkId]);
444 deepVectorClear(mTimeFrame->getTrackletsLabel(linkId));
445 };
446 if (maxConcurrency > 1) {
447 tbb::parallel_for(0, static_cast<int>(topology.nLinks), clearTracklets);
448 } else {
449 for (int linkId{0}; linkId < topology.nLinks; ++linkId) {
450 clearTracklets(linkId);
451 }
452 }
453 });
454}
455
456template <int NLayers>
458{
459 const auto topology = mTimeFrame->getTrackingTopologyView();
460 mTaskArena->execute([&] {
461 const int maxConcurrency = std::max(1, mTaskArena->max_concurrency());
462 auto clearNeighbours = [&](const int cellTopologyId) {
463 deepVectorClear(mTimeFrame->getCellsNeighbours()[cellTopologyId]);
464 deepVectorClear(mTimeFrame->getCellsNeighboursTopology()[cellTopologyId]);
465 deepVectorClear(mTimeFrame->getCellsNeighboursLUT()[cellTopologyId]);
466 };
467 if (maxConcurrency > 1) {
468 tbb::parallel_for(0, static_cast<int>(topology.nCells), clearNeighbours);
469 } else {
470 for (int cellTopologyId{0}; cellTopologyId < topology.nCells; ++cellTopologyId) {
471 clearNeighbours(cellTopologyId);
472 }
473 }
474
475 auto neighbourLess = [](const CellNeighbour& a, const CellNeighbour& b) {
476 return std::tie(a.nextCellTopology, a.nextCell, a.cellTopology, a.cell) <
477 std::tie(b.nextCellTopology, b.nextCell, b.cellTopology, b.cell);
478 };
479
480 for (int outerLayer{0}; outerLayer < NLayers; ++outerLayer) {
481 bounded_vector<int> activeTopologies(mMemoryPool.get());
482 activeTopologies.reserve(topology.nCells);
483 size_t sourceCellCount{0};
484 for (int cellTopologyId{0}; cellTopologyId < topology.nCells; ++cellTopologyId) {
485 const auto& cellTopology = topology.getCell(cellTopologyId);
486 if (cellTopology.hitLayerMask.last() != outerLayer ||
487 mTimeFrame->getCells()[cellTopologyId].empty()) {
488 continue;
489 }
490 const auto successors = topology.getCellsStartingWithLink(cellTopology.secondLink);
491 if (!successors.getEntries()) {
492 continue;
493 }
494 activeTopologies.push_back(cellTopologyId);
495 sourceCellCount += mTimeFrame->getCells()[cellTopologyId].size();
496 }
497
498 if (activeTopologies.empty()) {
499 continue;
500 }
501
502 auto forSourceCell = [&](const int cellTopologyId, const int iCell, auto&& emit) {
503 const auto& cellTopology = topology.getCell(cellTopologyId);
504 const auto successors = topology.getCellsStartingWithLink(cellTopology.secondLink);
505 const auto& currentCellSeed{mTimeFrame->getCells()[cellTopologyId][iCell]};
506 const int nextLayerTrackletIndex{currentCellSeed.getSecondTrackletIndex()};
507 for (int iSuccessor{0}; iSuccessor < successors.getEntries(); ++iSuccessor) {
508 const int nextCellTopologyId = topology.cellsByFirstLink[successors.getFirstEntry() + iSuccessor];
509 if (mTimeFrame->getCells()[nextCellTopologyId].empty() ||
510 mTimeFrame->getCellsLookupTable()[nextCellTopologyId].empty()) {
511 continue;
512 }
513 const auto& nextCellLUT = mTimeFrame->getCellsLookupTable()[nextCellTopologyId];
514 if (nextLayerTrackletIndex + 1 >= static_cast<int>(nextCellLUT.size())) {
515 continue;
516 }
517 const int nextLayerFirstCellIndex{nextCellLUT[nextLayerTrackletIndex]};
518 const int nextLayerLastCellIndex{nextCellLUT[nextLayerTrackletIndex + 1]};
519 for (int iNextCell{nextLayerFirstCellIndex}; iNextCell < nextLayerLastCellIndex; ++iNextCell) {
520 const auto& nextCellSeedRef{mTimeFrame->getCells()[nextCellTopologyId][iNextCell]};
521 if (nextCellSeedRef.getFirstTrackletIndex() != nextLayerTrackletIndex || !currentCellSeed.getTimeStamp().isCompatible(nextCellSeedRef.getTimeStamp())) {
522 break;
523 }
524
525 auto nextCellSeed{mTimeFrame->getCells()[nextCellTopologyId][iNextCell]};
526 if (!nextCellSeed.rotate(currentCellSeed.getAlpha()) ||
527 !nextCellSeed.propagateTo(currentCellSeed.getX(), getBz())) {
528 continue;
529 }
530
531 float chi2 = currentCellSeed.getPredictedChi2Fast(nextCellSeed);
532 if (chi2 > mTrkParams[iteration].MaxChi2ClusterAttachment) {
533 continue;
534 }
535
536 const int nextLevel = currentCellSeed.getLevel() + 1;
537 emit(cellTopologyId, iCell, nextCellTopologyId, iNextCell, nextLevel);
538 }
539 }
540 };
541
542 bounded_vector<CellNeighbour> waveNeighbours{mMemoryPool.get()};
543 const auto key = CapacityEstimator::makeKey(SlabSite::Neighbours, iteration, 0, outerLayer);
544 const auto scale = static_cast<double>(sourceCellCount);
545 if (maxConcurrency > 1) {
546 const size_t capacity = mTimeFrame->getCapacityEstimator().capacity(key, scale);
547 UnorderedSlabSink<CellNeighbour> sink{{.capacity = capacity, .nThreads = maxConcurrency}, mMemoryPool.get()};
548 tbb::parallel_for(0, static_cast<int>(activeTopologies.size()), [&](const int i) {
549 const int cellTopologyId = activeTopologies[i];
550 tbb::parallel_for(0, static_cast<int>(mTimeFrame->getCells()[cellTopologyId].size()), [&](const int iCell) {
551 auto& handle = sink.local();
552 forSourceCell(cellTopologyId, iCell, [&handle](auto&&... args) {
553 handle.emplace(std::forward<decltype(args)>(args)...);
554 });
555 });
556 });
557 const auto st = sink.stats();
558 sink.finalizeUnordered(waveNeighbours);
559 mTimeFrame->getCapacityEstimator().update(key, scale, st.requested, st.capacity, st.emitted, st.spilled,
560 st.overflowed, st.memoryLimited);
561 tbb::parallel_sort(waveNeighbours.begin(), waveNeighbours.end(), neighbourLess);
562 } else {
563 for (const int cellTopologyId : activeTopologies) {
564 for (int iCell{0}; iCell < static_cast<int>(mTimeFrame->getCells()[cellTopologyId].size()); ++iCell) {
565 forSourceCell(cellTopologyId, iCell, [&](auto&&... args) {
566 waveNeighbours.emplace_back(std::forward<decltype(args)>(args)...);
567 });
568 }
569 }
570 std::sort(waveNeighbours.begin(), waveNeighbours.end(), neighbourLess);
571 }
572
573 struct TargetSpan {
574 int topologyId;
575 size_t begin;
576 size_t end;
577 };
578 bounded_vector<TargetSpan> targetSpans{mMemoryPool.get()};
579 targetSpans.reserve(topology.nCells);
580 for (int targetTopologyId{0}; targetTopologyId < topology.nCells; ++targetTopologyId) {
581 const auto first = std::lower_bound(waveNeighbours.begin(), waveNeighbours.end(), targetTopologyId,
582 [](const CellNeighbour& neighbour, int id) { return neighbour.nextCellTopology < id; });
583 const auto last = std::upper_bound(first, waveNeighbours.end(), targetTopologyId,
584 [](int id, const CellNeighbour& neighbour) { return id < neighbour.nextCellTopology; });
585 if (first != last) {
586 targetSpans.push_back({targetTopologyId, static_cast<size_t>(first - waveNeighbours.begin()), static_cast<size_t>(last - waveNeighbours.begin())});
587 }
588 }
589
590 auto finalizeTarget = [&](const int i) {
591 const auto [targetTopologyId, begin, end] = targetSpans[i];
592 auto& cellsNeighbourLUT = mTimeFrame->getCellsNeighboursLUT()[targetTopologyId];
593 cellsNeighbourLUT.assign(mTimeFrame->getCells()[targetTopologyId].size(), 0);
594 for (size_t j{begin}; j < end; ++j) {
595 const auto& neighbour = waveNeighbours[j];
596 ++cellsNeighbourLUT[neighbour.nextCell];
597 auto& targetCell = mTimeFrame->getCells()[targetTopologyId][neighbour.nextCell];
598 if (neighbour.level > targetCell.getLevel()) {
599 targetCell.setLevel(neighbour.level);
600 }
601 }
602 std::inclusive_scan(cellsNeighbourLUT.begin(), cellsNeighbourLUT.end(), cellsNeighbourLUT.begin());
603
604 auto& cellsNeighbours = mTimeFrame->getCellsNeighbours()[targetTopologyId];
605 auto& cellsNeighboursTopology = mTimeFrame->getCellsNeighboursTopology()[targetTopologyId];
606 cellsNeighbours.resize(end - begin);
607 cellsNeighboursTopology.resize(end - begin);
608 for (size_t j{begin}; j < end; ++j) {
609 cellsNeighbours[j - begin] = waveNeighbours[j].cell;
610 cellsNeighboursTopology[j - begin] = waveNeighbours[j].cellTopology;
611 }
612 };
613 if (maxConcurrency > 1) {
614 tbb::parallel_for(0, static_cast<int>(targetSpans.size()), finalizeTarget);
615 } else {
616 for (int i{0}; i < static_cast<int>(targetSpans.size()); ++i) {
617 finalizeTarget(i);
618 }
619 }
620 }
621
622 // clean up LUTs
623 auto clearCellLUT = [&](const int cellTopologyId) {
624 deepVectorClear(mTimeFrame->getCellsLookupTable()[cellTopologyId]);
625 };
626 if (maxConcurrency > 1) {
627 tbb::parallel_for(0, static_cast<int>(topology.nCells), clearCellLUT);
628 } else {
629 for (int cellTopologyId{0}; cellTopologyId < topology.nCells; ++cellTopologyId) {
630 clearCellLUT(cellTopologyId);
631 }
632 }
633 });
634}
635
636template <int NLayers>
637template <typename InputSeed>
638void TrackerTraits<NLayers>::processNeighbours(int iteration, int defaultCellTopologyId, int iLevel, uint64_t capacityKey, const bounded_vector<InputSeed>& currentSeeds, bounded_vector<RoadSeedN>& updatedSeeds)
639{
640 constexpr bool IsInitial = std::is_same_v<InputSeed, CellSeed>;
641 static_assert(IsInitial || std::is_same_v<InputSeed, RoadSeedN>);
642 auto propagator = o2::base::Propagator::Instance();
643
644 mTaskArena->execute([&] {
645 auto forCellNeighbours = [&](int iCell, auto&& emit) {
646 const auto& inputSeed = currentSeeds[iCell];
647 const auto& currentCell = [&]() -> const auto& {
648 if constexpr (IsInitial) {
649 return inputSeed;
650 } else {
651 return inputSeed.seed;
652 }
653 }();
654 const int cellTopologyId = [&]() {
655 if constexpr (IsInitial) {
656 return defaultCellTopologyId;
657 } else {
658 return inputSeed.cellTopologyId;
659 }
660 }();
661 const int cellId = [&]() {
662 if constexpr (IsInitial) {
663 return iCell;
664 } else {
665 return inputSeed.cellId;
666 }
667 }();
668
669 if (currentCell.getLevel() != iLevel) {
670 return;
671 }
672 if constexpr (IsInitial) {
673 for (int layer = 0; layer < NLayers; ++layer) {
674 const int clusterIndex = currentCell.getCluster(layer);
675 if (clusterIndex != constants::UnusedIndex && mTimeFrame->isClusterUsed(layer, clusterIndex)) {
676 return;
677 }
678 }
679 }
680
681 if (cellTopologyId < 0 || mTimeFrame->getCellsNeighboursLUT()[cellTopologyId].empty()) {
682 return;
683 }
684 const int startNeighbourId{cellId ? mTimeFrame->getCellsNeighboursLUT()[cellTopologyId][cellId - 1] : 0};
685 const int endNeighbourId{mTimeFrame->getCellsNeighboursLUT()[cellTopologyId][cellId]};
686 for (int iNeighbourCell{startNeighbourId}; iNeighbourCell < endNeighbourId; ++iNeighbourCell) {
687 const int neighbourCellTopologyId = mTimeFrame->getCellsNeighboursTopology()[cellTopologyId][iNeighbourCell];
688 const int neighbourCellId = mTimeFrame->getCellsNeighbours()[cellTopologyId][iNeighbourCell];
689 const auto& neighbourCell = mTimeFrame->getCells()[neighbourCellTopologyId][neighbourCellId];
690 if (neighbourCell.getSecondTrackletIndex() != currentCell.getFirstTrackletIndex()) {
691 continue;
692 }
693 if (!currentCell.getTimeStamp().isCompatible(neighbourCell.getTimeStamp())) {
694 continue;
695 }
696 if (currentCell.getLevel() - 1 != neighbourCell.getLevel()) {
697 continue;
698 }
699 const int neighbourLayer = neighbourCell.getInnerLayer();
700 const int neighbourCluster = neighbourCell.getFirstClusterIndex();
701 if (mTimeFrame->isClusterUsed(neighbourLayer, neighbourCluster)) {
702 continue;
703 }
704
706 TrackSeedN seed{currentCell};
707 seed.getTimeStamp() = currentCell.getTimeStamp();
708 seed.getTimeStamp() += neighbourCell.getTimeStamp();
709 const auto& trHit = mTimeFrame->getTrackingFrameInfoOnLayer(neighbourLayer)[neighbourCluster];
710
711 if (!seed.rotate(trHit.alphaTrackingFrame)) {
712 continue;
713 }
714
715 if (!propagator->propagateToX(seed, trHit.xTrackingFrame, getBz(), o2::base::PropagatorImpl<float>::MAX_SIN_PHI, o2::base::PropagatorImpl<float>::MAX_STEP, mTrkParams[iteration].CorrType)) {
716 continue;
717 }
718
719 if (mTrkParams[iteration].CorrType == o2::base::PropagatorF::MatCorrType::USEMatCorrNONE) {
720 if (!seed.correctForMaterial(mTrkParams[iteration].LayerxX0[neighbourLayer], mTrkParams[iteration].LayerxX0[neighbourLayer] * constants::Radl * constants::Rho, true)) {
721 continue;
722 }
723 }
724
725 auto predChi2{seed.getPredictedChi2Quiet(trHit.positionTrackingFrame, trHit.covarianceTrackingFrame)};
726 if ((predChi2 > mTrkParams[iteration].MaxChi2ClusterAttachment) || predChi2 < 0.f) {
727 continue;
728 }
729 seed.setChi2(seed.getChi2() + predChi2);
730 if (!seed.o2::track::TrackParCov::update(trHit.positionTrackingFrame, trHit.covarianceTrackingFrame)) {
731 continue;
732 }
733
734 seed.getClusters()[neighbourLayer] = neighbourCluster;
735 auto mask = seed.getHitLayerMask();
736 mask.set(neighbourLayer);
737 seed.setHitLayerMask(mask);
738 seed.setLevel(neighbourCell.getLevel());
739 seed.setFirstTrackletIndex(neighbourCell.getFirstTrackletIndex());
740 seed.setSecondTrackletIndex(neighbourCell.getSecondTrackletIndex());
741 emit(std::move(seed), neighbourCellId, neighbourCellTopologyId);
742 }
743 };
744
745 const int nCells = static_cast<int>(currentSeeds.size());
746 if (mTaskArena->max_concurrency() <= 1) {
747 for (int iCell{0}; iCell < nCells; ++iCell) {
748 forCellNeighbours(iCell, [&](auto&&... args) { updatedSeeds.emplace_back(std::forward<decltype(args)>(args)...); });
749 }
750 } else {
751 const auto scale = static_cast<double>(nCells);
752 const size_t capacity = mTimeFrame->getCapacityEstimator().capacity(capacityKey, scale);
753 UnorderedSlabSink<RoadSeedN> sink{{.capacity = capacity, .nThreads = mTaskArena->max_concurrency()}, mMemoryPool.get()};
754
755 tbb::parallel_for(0, nCells, [&](const int iCell) {
756 auto& handle = sink.local();
757 forCellNeighbours(iCell, [&](auto&&... args) { handle.emplace(std::forward<decltype(args)>(args)...); });
758 });
759 const auto st = sink.stats();
760 sink.finalizeUnordered(updatedSeeds);
761 mTimeFrame->getCapacityEstimator().update(capacityKey, scale, st.requested, st.capacity, st.emitted, st.spilled,
762 st.overflowed, st.memoryLimited);
763 }
764 });
765}
766
767template <int NLayers>
769 TrackITSExt& track,
770 const int iteration,
771 const TrackingFrameInfo* const* tfInfos,
772 const Cluster* const* unsortedClusters,
773 const o2::base::Propagator* propagator,
774 const TrackFollowContext<NLayers>& followCtx,
775 TrackFollowerScratch& scratch)
776{
777 const auto& trkParams = mTrkParams[iteration];
779 tfInfos, trkParams.LayerxX0.data(), trkParams.NLayers, mBz,
780 trkParams.MaxChi2ClusterAttachment, trkParams.MaxChi2NDF,
781 propagator, trkParams.CorrType, trkParams.ShiftRefToCluster, trkParams.RepeatRefitOut};
782 TrackITSInternal<NLayers> internalTrack;
783 if (!track::refitTrackSeed<NLayers>(seed,
784 internalTrack,
785 fitCtx,
786 unsortedClusters,
787 trkParams.LayerRadii.data(),
788 trkParams.MinPt.data(),
789 trkParams.ReseedIfShorter)) {
790 return false;
791 }
792 const auto passesFinalLengthCut = [&trkParams](const TrackITSExt& candidate) {
793 LayerMask hitLayerMask{0};
794 for (int iLayer{0}; iLayer < trkParams.NLayers; ++iLayer) {
795 if (candidate.getClusterIndex(iLayer) != constants::UnusedIndex) {
796 hitLayerMask.set(iLayer);
797 }
798 }
799 return track::TrackSeedSelector<NLayers>::getEffectiveTrackLength(hitLayerMask, trkParams.InactiveLayerMask) >= trkParams.MinTrackLength;
800 };
801
802 const bool extendTop = trkParams.PassFlags[IterationStep::TrackFollowerTop];
803 const bool extendBot = trkParams.PassFlags[IterationStep::TrackFollowerBot];
804 if (!extendTop && !extendBot) {
805 track = makeTrackITSExt(internalTrack);
806 return passesFinalLengthCut(track);
807 }
808
809 if (static_cast<int>(scratch.activeHypotheses.size()) < followCtx.maxHypotheses) {
810 scratch.activeHypotheses.resize(followCtx.maxHypotheses);
811 }
812 if (static_cast<int>(scratch.nextHypotheses.size()) < followCtx.maxHypotheses) {
813 scratch.nextHypotheses.resize(followCtx.maxHypotheses);
814 }
815
816 const auto backup = internalTrack;
817 auto best = internalTrack;
818 uint32_t bestDiff{0};
819 auto followDirection = [&](TrackITSInternal<NLayers>& candidate, bool outward) {
820 const TrackExtensionHypothesis<NLayers> startHypothesis{candidate, outward};
822 if (!followTrackExtensionDirection<NLayers>(startHypothesis, fitCtx, followCtx, outward,
823 scratch.activeHypotheses.data(),
824 scratch.nextHypotheses.data(),
826 return false;
827 }
828 updateTrackFromExtensionHypothesis(bestHypothesis, outward, trkParams.NLayers, candidate);
829 return true;
830 };
831 TrackExtensionBestTrial<NLayers> bestTrial{backup.getPattern(), fitCtx};
832 followTrackExtensionBranches(backup, extendTop, extendBot, trkParams.NLayers, followDirection, bestTrial, best, bestDiff);
833
834 track = makeTrackITSExt(best);
835 if (bestDiff) {
836 track.setExtendedLayerPattern<NLayers>(bestDiff);
837 }
838 return passesFinalLengthCut(track);
839}
840
841template <int NLayers>
842void TrackerTraits<NLayers>::findRoads(const int iteration)
843{
844 bounded_vector<bounded_vector<int>> firstClusters(mTrkParams[iteration].NLayers, bounded_vector<int>(mMemoryPool.get()), mMemoryPool.get());
845 firstClusters.resize(mTrkParams[iteration].NLayers);
846 const auto propagator = o2::base::Propagator::Instance();
847 const TrackingFrameInfo* tfInfos[NLayers]{};
848 const Cluster* unsortedClusters[NLayers]{};
849 for (int iLayer = 0; iLayer < NLayers; ++iLayer) {
850 tfInfos[iLayer] = mTimeFrame->getTrackingFrameInfoOnLayer(iLayer).data();
851 unsortedClusters[iLayer] = mTimeFrame->getUnsortedClusters()[iLayer].data();
852 }
853 const auto topology = mTimeFrame->getTrackingTopologyView();
854 tbb::enumerable_thread_specific<TrackFollowerScratch> followerScratch{
855 [mr = mMemoryPool.get()]() { return TrackFollowerScratch{mr}; }};
856 for (int startLevel{mTrkParams[iteration].CellsPerRoad()}; startLevel >= mTrkParams[iteration].CellMinimumLevel(); --startLevel) {
857
858 const track::TrackSeedSelector<NLayers> seedFilter{constants::MaxTrackSeedQ2Pt, mTrkParams[iteration].MaxChi2NDF, startLevel, mTrkParams[iteration].MaxHoles, mTrkParams[iteration].getMinSeedingClusters(), mTrkParams[iteration].HoleLayerMask, mTrkParams[iteration].getNonSeedingLayerMask()};
859
860 bounded_vector<TrackSeedN> trackSeeds(mMemoryPool.get());
861 for (int startCellTopologyId{0}; startCellTopologyId < topology.nCells; ++startCellTopologyId) {
862 const int startLayer = topology.getCell(startCellTopologyId).hitLayerMask.last();
863 if (!(mTrkParams[iteration].StartLayerMask.has(startLayer)) ||
864 mTimeFrame->getCells()[startCellTopologyId].empty() ||
865 topology.getMaxCellLevel(startCellTopologyId) < startLevel) {
866 continue;
867 }
868
869 bounded_vector<RoadSeedN> lastSeeds(mMemoryPool.get()), updatedSeeds(mMemoryPool.get());
870
871 auto roadKey = [&](int level) {
872 return CapacityEstimator::makeKey(SlabSite::Roads, iteration, CapacityEstimator::makeVariant(startLevel, level), startCellTopologyId);
873 };
874
875 processNeighbours(iteration, startCellTopologyId, startLevel, roadKey(startLevel), mTimeFrame->getCells()[startCellTopologyId], updatedSeeds);
876
877 int level = startLevel;
878 while (level > 2 && !updatedSeeds.empty()) {
879 lastSeeds.swap(updatedSeeds);
880 deepVectorClear(updatedSeeds);
881 --level;
882 processNeighbours(iteration, constants::UnusedIndex, level, roadKey(level), lastSeeds, updatedSeeds);
883 }
884 deepVectorClear(lastSeeds);
885
886 if (!updatedSeeds.empty()) {
887 trackSeeds.reserve(trackSeeds.size() + std::count_if(updatedSeeds.begin(), updatedSeeds.end(), [&](const auto& road) { return seedFilter(road.seed); }));
888 for (auto& road : updatedSeeds) {
889 if (seedFilter(road.seed)) {
890 trackSeeds.emplace_back(std::move(road.seed));
891 }
892 }
893 }
894 }
895
896 if (trackSeeds.empty()) {
897 continue;
898 }
899
900 const Cluster* clustersPtrs[NLayers]{};
901 const unsigned char* usedClustersPtrs[NLayers]{};
902 const int* clustersIndexTablesPtrs[NLayers]{};
903 const int* rofClustersPtrs[NLayers]{};
904 for (int iLayer{0}; iLayer < NLayers; ++iLayer) {
905 clustersPtrs[iLayer] = mTimeFrame->getClusters()[iLayer].data();
906 usedClustersPtrs[iLayer] = mTimeFrame->getUsedClusters(iLayer).data();
907 clustersIndexTablesPtrs[iLayer] = mTimeFrame->getIndexTable(0, iLayer).data();
908 rofClustersPtrs[iLayer] = mTimeFrame->getROFrameClusters(iLayer).data();
909 }
910 const TrackFollowContext<NLayers> followCtx{
911 &mTimeFrame->getIndexTableUtils(),
912 mTimeFrame->getROFMaskView(),
913 mTimeFrame->getROFOverlapTableView(),
914 clustersPtrs, usedClustersPtrs, clustersIndexTablesPtrs, rofClustersPtrs,
915 mTrkParams[iteration].LayerRadii.data(), mTrkParams[iteration].PhiBins,
916 std::max(1, mTrkParams[iteration].TrackFollowerMaxHypotheses),
917 mTrkParams[iteration].TrackFollowerNSigmaCutPhi, mTrkParams[iteration].TrackFollowerNSigmaCutZ};
918
919 bounded_vector<TrackITSExt> tracks(mMemoryPool.get());
920 mTaskArena->execute([&] {
921 const int nSeeds = static_cast<int>(trackSeeds.size());
922 const int maxConcurrency = std::max(1, mTaskArena->max_concurrency());
923 const int chunkSize = std::min(nSeeds, std::clamp(nSeeds / (constants::NumberOfConcurrentSeeds * maxConcurrency), constants::MinNumberOfConcurrentSeeds, constants::MaxNumberOfConcurrentSeeds)); // acts as memory bound and minimum work
924
925 // flush local track vector to global vector on reaching chunkSize
926 std::mutex tracksMutex;
927 auto flushTracks = [&](bounded_vector<TrackITSExt>& localTracks) {
928 if (localTracks.empty()) {
929 return;
930 }
931 std::lock_guard lock{tracksMutex};
932 tracks.insert(tracks.end(), std::make_move_iterator(localTracks.begin()), std::make_move_iterator(localTracks.end()));
933 localTracks.clear();
934 };
935
936 // each worker works on its own range
937 tbb::parallel_for(tbb::blocked_range<int>(0, nSeeds, chunkSize), [&](const auto& range) {
938 bounded_vector<TrackITSExt> localTracks(mMemoryPool.get());
939 localTracks.reserve(std::min(chunkSize, static_cast<int>(range.size())));
940 auto& scratch = followerScratch.local();
941 for (int iSeed{range.begin()}; iSeed < range.end(); ++iSeed) {
942 localTracks.emplace_back();
943 if (!finaliseTrackSeed(trackSeeds[iSeed], localTracks.back(), iteration, tfInfos, unsortedClusters, propagator, followCtx, scratch)) {
944 localTracks.pop_back();
945 }
946 if (static_cast<int>(localTracks.size()) == chunkSize) {
947 flushTracks(localTracks);
948 }
949 }
950 flushTracks(localTracks); // flush remaining
951 deepVectorClear(localTracks);
952 });
953
954 deepVectorClear(trackSeeds);
955 });
956
957 // Sort tracks via indices to avoid moving TrackITSExt objects.
958 bounded_vector<int> trackIndices(tracks.size(), mMemoryPool.get());
959 std::iota(trackIndices.begin(), trackIndices.end(), 0);
960 std::sort(trackIndices.begin(), trackIndices.end(), [&tracks](int a, int b) {
961 return track::isBetter(tracks[a], tracks[b]);
962 });
963
964 acceptTracks(iteration, tracks, trackIndices, firstClusters);
965 }
966 markTracks(iteration);
967}
968
969template <int NLayers>
971 bounded_vector<TrackITSExt>& tracks,
972 const bounded_vector<int>& trackIndices,
973 bounded_vector<bounded_vector<int>>& firstClusters)
974{
975 auto& trks = mTimeFrame->getTracks();
976 trks.reserve(trks.size() + tracks.size());
977 const float smallestROFHalf = mTimeFrame->getROFOverlapTableView().getClockLayer().mROFLength * 0.5f;
978 for (size_t trackId{0}; trackId < trackIndices.size(); ++trackId) {
979 auto& track = tracks[trackIndices[trackId]];
980 int nShared = 0;
981 bool isFirstShared{false};
982 int firstLayer{-1}, firstCluster{-1};
983 for (int iLayer{0}; iLayer < mTrkParams[iteration].NLayers; ++iLayer) {
984 if (track.getClusterIndex(iLayer) == constants::UnusedIndex) {
985 continue;
986 }
987 bool isShared = mTimeFrame->isClusterUsed(iLayer, track.getClusterIndex(iLayer));
988 nShared += int(isShared);
989 if (firstLayer < 0) {
990 firstCluster = track.getClusterIndex(iLayer);
991 isFirstShared = isShared && mTrkParams[iteration].AllowSharingFirstCluster && std::find(firstClusters[iLayer].begin(), firstClusters[iLayer].end(), firstCluster) != firstClusters[iLayer].end();
992 firstLayer = iLayer;
993 }
994 }
995
997 if (nShared - int(isFirstShared && mTrkParams[iteration].AllowSharingFirstCluster) > mTrkParams[iteration].SharedMaxClusters) {
998 continue;
999 }
1000
1001 bool firstCls{true}, nominalCompatible{true};
1002 TimeEstBC nominalTS, expandedTS;
1003 for (int iLayer{0}; iLayer < mTrkParams[iteration].NLayers; ++iLayer) {
1004 if (track.getClusterIndex(iLayer) == constants::UnusedIndex) {
1005 continue;
1006 }
1007 mTimeFrame->markUsedCluster(iLayer, track.getClusterIndex(iLayer));
1008 int currentROF = mTimeFrame->getClusterROF(iLayer, track.getClusterIndex(iLayer));
1009 const auto nominalROFTS = mTimeFrame->getROFOverlapTableView().getLayer(iLayer).getROFTimeBounds(currentROF);
1010 const auto expandedROFTS = mTimeFrame->getROFOverlapTableView().getLayer(iLayer).getROFTimeBounds(currentROF, true);
1011 if (firstCls) {
1012 firstCls = false;
1013 nominalTS = nominalROFTS;
1014 expandedTS = expandedROFTS;
1015 } else {
1016 if (nominalCompatible) {
1017 if (nominalTS.isCompatible(nominalROFTS)) {
1018 nominalTS += nominalROFTS;
1019 } else {
1020 nominalCompatible = false;
1021 }
1022 }
1023 if (!expandedTS.isCompatible(expandedROFTS)) {
1024 LOGP(fatal, "TS {}+/-{} are incompatible with {}+/-{}, this should not happen!", expandedROFTS.getTimeStamp(), expandedROFTS.getTimeStampError(), expandedTS.getTimeStamp(), expandedTS.getTimeStampError());
1025 }
1026 expandedTS += expandedROFTS;
1027 }
1028 }
1029 track.getTimeStamp() = (nominalCompatible ? nominalTS : expandedTS).makeSymmetrical();
1030 // this is a sanity clamp
1031 // we cannot be worse than the clock so we clamp to this
1032 if (track.getTimeStamp().getTimeStampError() > smallestROFHalf) {
1033 track.getTimeStamp().setTimeStampError(smallestROFHalf);
1034 }
1035 const auto diff = track.getExtendedLayerPattern<NLayers>();
1036 if (diff) {
1037 size_t nExtendedClusters = 0;
1038 for (int iLayer{0}; iLayer < mTrkParams[iteration].NLayers; ++iLayer) {
1039 nExtendedClusters += static_cast<bool>(diff & (0x1u << iLayer));
1040 }
1041 mTimeFrame->addTrackExtensionCounters(1, nExtendedClusters);
1042 }
1043 track.clearExtendedLayerPattern();
1044 trks.emplace_back(track);
1045
1046 if (mTrkParams[iteration].AllowSharingFirstCluster) {
1047 firstClusters[firstLayer].push_back(firstCluster);
1048 }
1049 }
1050}
1051
1052template <int NLayers>
1054{
1055 if (mTrkParams[iteration].AllowSharingFirstCluster) {
1057 auto& tracks = mTimeFrame->getTracks();
1058
1059 bounded_vector<int> fclusSort(tracks.size(), mMemoryPool.get());
1060 std::iota(fclusSort.begin(), fclusSort.end(), 0);
1061 std::sort(fclusSort.begin(), fclusSort.end(), [&tracks](int a, int b) {
1062 return tracks[a].getFirstLayerClusterIndex() < tracks[b].getFirstLayerClusterIndex();
1063 });
1064
1065 auto areTracksSelected = [this, iteration](const TrackITSExt& t1, const TrackITSExt& t2) {
1066 const auto t1FirstLayer{t1.getFirstClusterLayer()}, t2FirstLayer{t2.getFirstClusterLayer()};
1067 if (t1FirstLayer != t2FirstLayer) {
1068 return false;
1069 }
1070 if (mTimeFrame->getClusterROF(t1FirstLayer, t1.getClusterIndex(t1FirstLayer)) != mTimeFrame->getClusterROF(t2FirstLayer, t2.getClusterIndex(t2FirstLayer))) {
1071 return false;
1072 }
1073 if (!math_utils::isPhiDifferenceBelow(t1.getPhi(), t2.getPhi(), mTrkParams[iteration].SharedClusterMaxDeltaPhi)) {
1074 return false;
1075 }
1076 if (std::abs(t1.getEta() - t2.getEta()) > mTrkParams[iteration].SharedClusterMaxDeltaEta) {
1077 return false;
1078 }
1079 if (mTrkParams[iteration].SharedClusterOppositeSign && t1.getSign() == t2.getSign()) {
1080 return false;
1081 }
1082 return true;
1083 };
1084
1085 for (int i{0}; i < static_cast<int>(fclusSort.size()); ++i) {
1086 auto& track = tracks[fclusSort[i]];
1087 for (int j{i + 1}; j < static_cast<int>(fclusSort.size()) && tracks[fclusSort[j]].getFirstLayerClusterIndex() == track.getFirstLayerClusterIndex(); ++j) {
1088 auto& track2 = tracks[fclusSort[j]];
1089 if (areTracksSelected(track, track2)) {
1090 track.setSharedClusters();
1091 track2.setSharedClusters();
1092 }
1093 }
1094 }
1095 }
1096}
1097
1098template <int NLayers>
1100{
1101 mBz = bz;
1102 mTimeFrame->setBz(bz);
1103}
1104
1105template <int NLayers>
1106void TrackerTraits<NLayers>::setNThreads(int n, std::shared_ptr<tbb::task_arena>& arena)
1107{
1108#if defined(OPTIMISATION_OUTPUT)
1109 mTaskArena = std::make_shared<tbb::task_arena>(1);
1110#else
1111 if (arena == nullptr) {
1112 mTaskArena = std::make_shared<tbb::task_arena>(std::abs(n));
1113 LOGP(info, "Setting tracker with {} threads.", n);
1114 } else {
1115 mTaskArena = arena;
1116 }
1117#endif
1118}
1119
1120template class TrackerTraits<7>;
1121template void TrackerTraits<7>::processNeighbours<CellSeed>(int, int, int, uint64_t, const bounded_vector<CellSeed>&, bounded_vector<RoadSeed<7>>&);
1122template void TrackerTraits<7>::processNeighbours<RoadSeed<7>>(int, int, int, uint64_t, const bounded_vector<RoadSeed<7>>&, bounded_vector<RoadSeed<7>>&);
1123// ALICE3 upgrade
1124#ifdef ENABLE_UPGRADES
1125template class TrackerTraits<11>;
1126template void TrackerTraits<11>::processNeighbours<CellSeed>(int, int, int, uint64_t, const bounded_vector<CellSeed>&, bounded_vector<RoadSeed<11>>&);
1127template void TrackerTraits<11>::processNeighbours<RoadSeed<11>>(int, int, int, uint64_t, const bounded_vector<RoadSeed<11>>&, bounded_vector<RoadSeed<11>>&);
1128template class TrackerTraits<13>;
1129template void TrackerTraits<13>::processNeighbours<CellSeed>(int, int, int, uint64_t, const bounded_vector<CellSeed>&, bounded_vector<RoadSeed<13>>&);
1130template void TrackerTraits<13>::processNeighbours<RoadSeed<13>>(int, int, int, uint64_t, const bounded_vector<RoadSeed<13>>&, bounded_vector<RoadSeed<13>>&);
1131#endif
1132
1133} // namespace o2::its
std::vector< std::string > labels
int32_t i
int32_t lastRow
bounded_vector< float > bins
uint32_t j
Definition RawData.h:0
Lock-free slot allocator and single-pass sink.
Hypothesis search used by CPU and GPU track extension.
Shared host/device helpers for ITS tracker trait implementations.
std::vector< o2::MCCompLabel > createLabels(int N)
benchmark::State & st
StringRef key
GPUd() value_type estimateLTFast(o2 static GPUd() float estimateLTIncrement(const o2 PropagatorImpl * Instance(bool uninitialized=false)
Definition Propagator.h:178
TrackSeed< NLayers > TrackSeedN
virtual void initialiseTimeFrame(const int iteration)
virtual void computeLayerTracklets(const int iteration, int iVertex)
GLdouble n
Definition glcorearb.h:1982
GLuint GLuint end
Definition glcorearb.h:469
GLboolean GLboolean GLboolean b
Definition glcorearb.h:1233
GLenum GLint * range
Definition glcorearb.h:1899
GLboolean * data
Definition glcorearb.h:298
GLenum GLuint GLint GLint layer
Definition glcorearb.h:1310
GLint level
Definition glcorearb.h:275
GLboolean r
Definition glcorearb.h:1233
GLboolean GLboolean GLboolean GLboolean a
Definition glcorearb.h:1233
GLint GLuint mask
Definition glcorearb.h:291
GLuint GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat t1
Definition glcorearb.h:5034
constexpr float Tolerance
Definition Constants.h:30
const bool const bool const int FollowDirection BestTrial TrackITSInternal< NLayers > uint32_t & bestDiff
const bool outward
const bool const bool const int FollowDirection BestTrial TrackITSInternal< NLayers > & best
const int end
const bool const int TrackITSInternal< NLayers > & track
const bool const bool extendBot
const track::TrackFitContext< NLayers > const TrackFollowContext< NLayers > const bool TrackExtensionHypothesis< NLayers > TrackExtensionHypothesis< NLayers > TrackExtensionHypothesis< NLayers > & bestHypothesis
const bool extendTop
return getBinsRect(layerIndex, currentCluster.phi, zMean, zDelta, maxdeltaphi, utils)
const bool const bool const int FollowDirection BestTrial & bestTrial
const bool const bool const int FollowDirection & followDirection
void deepVectorClear(std::vector< T > &vec)
float yCoordinate
Definition Cluster.h:44
float zCoordinate
Definition Cluster.h:45
float xCoordinate
Definition Cluster.h:43
bounded_vector< TrackExtensionHypothesis< NLayers > > activeHypotheses
bounded_vector< TrackExtensionHypothesis< NLayers > > nextHypotheses
std::array< float, 2 > positionTrackingFrame
Definition Cluster.h:66
std::array< float, 3 > covarianceTrackingFrame
Definition Cluster.h:67
std::vector< Tracklet64 > tracklets