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 <cstdlib>
19#include <bit>
20#include <iterator>
21#include <limits>
22#include <mutex>
23#include <numeric>
24#include <ranges>
25#include <cmath>
26#include <tuple>
27#include <type_traits>
28#include <vector>
29
30#include <oneapi/tbb/blocked_range.h>
31#include <oneapi/tbb/enumerable_thread_specific.h>
32#include <oneapi/tbb/parallel_for.h>
33#include <oneapi/tbb/parallel_scan.h>
34#include <oneapi/tbb/parallel_sort.h>
35
37#include "GPUCommonMath.h"
40#include "ITStracking/Cell.h"
53
54namespace o2::its
55{
56
62
63template <int NLayers>
65{
66 this->mTaskArena->execute([&] {
67 mTimeFrame->initialise(mTrkParams[iteration], mTrkParams[iteration].NLayers, iteration);
68 });
69}
70
71template <int NLayers>
72void TrackerTraits<NLayers>::computeLayerTracklets(const int iteration, int iVertex)
73{
74 const auto topology = mTimeFrame->getTrackingTopologyView();
75 const bool seedingPass = mTrkParams[iteration].PassFlags[IterationStep::SeedingVertexPass];
76 const bool useDiamondSpan = mTrkParams[iteration].UseDiamond;
77 const Vertex diamondVert = makeDiamondVertex(mTrkParams[iteration]);
78 gsl::span<const Vertex> diamondSpan(&diamondVert, 1);
79
80 mTaskArena->execute([&] {
81 tbb::parallel_for(0, static_cast<int>(topology.nLinks), [&](const int linkId) {
82 mTimeFrame->getTracklets()[linkId].clear();
83 mTimeFrame->getTrackletsLabel(linkId).clear();
84 auto& lut = mTimeFrame->getTrackletsLookupTable()[linkId];
85 std::fill(lut.begin(), lut.end(), 0);
86 });
87
88 auto forTracklets = [&](int linkId, int pivotROF, auto&& emit, auto&& onProducer) {
89 const auto& link = topology.getLink(linkId);
90 if (!mTimeFrame->getROFMaskView().isROFEnabled(link.fromLayer, pivotROF)) {
91 return;
92 }
93 gsl::span<const Vertex> primaryVertices = useDiamondSpan ? diamondSpan : mTimeFrame->getPrimaryVertices(link.fromLayer, pivotROF);
94 if (primaryVertices.empty()) {
95 return;
96 }
97 const int startVtx = iVertex >= 0 ? iVertex : 0;
98 const int endVtx = iVertex >= 0 ? o2::gpu::CAMath::Min(iVertex + 1, int(primaryVertices.size())) : int(primaryVertices.size());
99 if (endVtx <= startVtx || (iVertex + 1) > primaryVertices.size()) {
100 return;
101 }
102
103 const auto& rofOverlap = mTimeFrame->getROFOverlapTableView().getOverlap(link.fromLayer, link.toLayer, pivotROF);
104 if (!rofOverlap.getEntries()) {
105 return;
106 }
107
108 auto layer0 = mTimeFrame->getClustersOnLayer(pivotROF, link.fromLayer);
109 if (layer0.empty()) {
110 return;
111 }
112
113 const float meanDeltaR = mTrkParams[iteration].LayerRadii[link.toLayer] - mTrkParams[iteration].LayerRadii[link.fromLayer];
114 const float phiCut = seedingPass ? mTrkParams[iteration].VtxPhiCut : mTimeFrame->getLinkPhiCut(linkId);
115 const float msAngle = mTimeFrame->getLinkMSAngle(linkId);
116
117 for (int iCluster = 0; iCluster < int(layer0.size()); ++iCluster) {
118 const Cluster& currentCluster = layer0[iCluster];
119 const int currentSortedIndex = mTimeFrame->getSortedIndex(pivotROF, link.fromLayer, iCluster);
120 if (mTimeFrame->isClusterUsed(link.fromLayer, currentCluster.clusterId)) {
121 continue;
122 }
123 onProducer(currentSortedIndex);
124 const float inverseR0 = 1.f / currentCluster.radius;
125
126 for (int iV = startVtx; iV < endVtx; ++iV) {
127 const auto& pv = primaryVertices[iV];
128 if (!mTimeFrame->getROFVertexLookupTableView().isVertexCompatible(link.fromLayer, pivotROF, pv)) {
129 continue;
130 }
131 if (pv.isFlagSet(Vertex::Flags::UPCMode) != mTrkParams[iteration].PassFlags[IterationStep::SelectUPCVertices]) {
132 continue;
133 }
134 const float resolution = o2::gpu::CAMath::Sqrt(math_utils::Sq(mTimeFrame->getPositionResolution(link.fromLayer)) + math_utils::Sq(mTrkParams[iteration].PVres) / float(pv.getNContributors()));
135 const float tanLambda = (currentCluster.zCoordinate - pv.getZ()) * inverseR0;
136 const float zAtRmin = tanLambda * (mTimeFrame->getMinR(link.toLayer) - currentCluster.radius) + currentCluster.zCoordinate;
137 const float zAtRmax = tanLambda * (mTimeFrame->getMaxR(link.toLayer) - currentCluster.radius) + currentCluster.zCoordinate;
138 const float sqInvDeltaZ0 = 1.f / (math_utils::Sq(currentCluster.zCoordinate - pv.getZ()) + constants::Tolerance);
139 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));
140 const auto bins = o2::its::getBinsRect(currentCluster, link.toLayer, zAtRmin, zAtRmax,
141 sigmaZ * mTrkParams[iteration].NSigmaCut, phiCut,
142 mTimeFrame->getIndexTableUtils());
143 if (bins.x < 0) {
144 continue;
145 }
146 int phiBinsNum = bins.w - bins.y + 1;
147 if (phiBinsNum < 0) {
148 phiBinsNum += mTrkParams[iteration].PhiBins;
149 }
150
151 for (int targetROF = rofOverlap.getFirstEntry(); targetROF < rofOverlap.getEntriesBound(); ++targetROF) {
152 if (!mTimeFrame->getROFMaskView().isROFEnabled(link.toLayer, targetROF)) {
153 continue;
154 }
155 auto layer1 = mTimeFrame->getClustersOnLayer(targetROF, link.toLayer);
156 if (layer1.empty()) {
157 continue;
158 }
159 const auto ts = mTimeFrame->getROFOverlapTableView().getTimeStamp(link.fromLayer, pivotROF, link.toLayer, targetROF);
160 if (!ts.isCompatible(pv.getTimeStamp())) {
161 continue;
162 }
163 const auto& targetIndexTable = mTimeFrame->getIndexTable(targetROF, link.toLayer);
164 const int zBinRange = (bins.z - bins.x) + 1;
165 for (int iPhi = 0; iPhi < phiBinsNum; ++iPhi) {
166 const int iPhiBin = (bins.y + iPhi) % mTrkParams[iteration].PhiBins;
167 const int firstBinIdx = mTimeFrame->getIndexTableUtils().getBinIndex(bins.x, iPhiBin);
168 const int maxBinIdx = firstBinIdx + zBinRange;
169 const int firstRow = targetIndexTable[firstBinIdx];
170 const int lastRow = targetIndexTable[maxBinIdx];
171 for (int iNext = firstRow; iNext < lastRow; ++iNext) {
172 if (iNext >= int(layer1.size())) {
173 break;
174 }
175 const Cluster& nextCluster = layer1[iNext];
176 if (mTimeFrame->isClusterUsed(link.toLayer, nextCluster.clusterId)) {
177 continue;
178 }
179 const float deltaZ = o2::gpu::CAMath::Abs((tanLambda * (nextCluster.radius - currentCluster.radius)) + currentCluster.zCoordinate - nextCluster.zCoordinate);
180
181 if (deltaZ / sigmaZ < mTrkParams[iteration].NSigmaCut &&
182 math_utils::isPhiDifferenceBelow(currentCluster.phi, nextCluster.phi, phiCut)) {
183 const float phi{o2::math_utils::fastATan2(currentCluster.yCoordinate - nextCluster.yCoordinate, currentCluster.xCoordinate - nextCluster.xCoordinate)};
184 const float tanL = (currentCluster.zCoordinate - nextCluster.zCoordinate) / (currentCluster.radius - nextCluster.radius);
185 emit(currentSortedIndex, mTimeFrame->getSortedIndex(targetROF, link.toLayer, iNext), tanL, phi, ts);
186 }
187 }
188 }
189 }
190 }
191 }
192 };
193
194 if (mTaskArena->max_concurrency() <= 1) {
195 for (int linkId{0}; linkId < topology.nLinks; ++linkId) {
196 const int fromLayer = topology.getLink(linkId).fromLayer;
197 const int endROF = mTimeFrame->getROFOverlapTableView().getLayer(fromLayer).mNROFsTF;
198 auto& tracklets = mTimeFrame->getTracklets()[linkId];
199 for (int pivotROF{0}; pivotROF < endROF; ++pivotROF) {
200 forTracklets(linkId, pivotROF, [&tracklets](auto&&... args) { tracklets.emplace_back(std::forward<decltype(args)>(args)...); }, [](int) {});
201 }
202 }
203 } else {
204 const int maxConcurrency = std::max(1, mTaskArena->max_concurrency());
205 const int nConcurrentSinks = std::min(static_cast<int>(topology.nLinks), maxConcurrency);
206 tbb::parallel_for(0, static_cast<int>(topology.nLinks), [&](const int linkId) {
207 const int fromLayer = topology.getLink(linkId).fromLayer;
208 const int startROF = 0, endROF = mTimeFrame->getROFOverlapTableView().getLayer(fromLayer).mNROFsTF;
209 auto& tracklets = mTimeFrame->getTracklets()[linkId];
210 const auto key = CapacityEstimator::makeKey(SlabSite::Tracklets, iteration, iVertex + 1, linkId);
211 const auto scale = static_cast<double>(mTimeFrame->getClusters()[fromLayer].size());
212 const size_t capacity = mTimeFrame->getCapacityEstimator().capacity(key, scale);
213
214 SlabSinkStats st;
215 if (seedingPass) { // Seeding passes emit the tracklets already grouped by their pivot cluster, finalizeGrouped() builds the lookup table and with UseDiamond there is one vertex and no duplicates
216 auto& lut{mTimeFrame->getTrackletsLookupTable()[linkId]};
217 const size_t nProducers = mTimeFrame->getClusters()[fromLayer].size();
218 GroupedSlabSink<Tracklet> sink{{.capacity = capacity, .nThreads = maxConcurrency, .nConcurrentSinks = nConcurrentSinks}, mMemoryPool.get()};
219 tbb::parallel_for(startROF, endROF, [&](const int pivotROF) {
220 auto& handle = sink.local();
221 forTracklets(
222 linkId, pivotROF,
223 [&handle](auto&&... args) { handle.emplace(std::forward<decltype(args)>(args)...); },
224 [&handle](int producer) { handle.beginProducer(producer); });
225 });
226 st = sink.stats();
227 sink.finalizeGrouped(nProducers, lut, tracklets);
228 } else {
229 UnorderedSlabSink<Tracklet> sink{{.capacity = capacity, .nThreads = maxConcurrency, .nConcurrentSinks = nConcurrentSinks}, mMemoryPool.get()};
230 tbb::parallel_for(startROF, endROF, [&](const int pivotROF) {
231 auto& handle = sink.local();
232 forTracklets(
233 linkId, pivotROF,
234 [&handle](auto&&... args) { handle.emplace(std::forward<decltype(args)>(args)...); },
235 [](int) {});
236 });
237 st = sink.stats();
238 sink.finalizeUnordered(tracklets);
239 }
240 mTimeFrame->getCapacityEstimator().update(key, scale, st.requested, st.capacity, st.emitted, st.spilled,
241 st.overflowed, st.memoryLimited);
242 });
243 }
244
245 const bool trackletsAlreadyGrouped = seedingPass && mTaskArena->max_concurrency() > 1;
246 tbb::parallel_for(0, static_cast<int>(topology.nLinks), [&](const int linkId) {
247 if (trackletsAlreadyGrouped) {
248 return;
249 }
251 auto& trkl{mTimeFrame->getTracklets()[linkId]};
252 if (mTaskArena->max_concurrency() > 1) {
253 tbb::parallel_sort(trkl.begin(), trkl.end());
254 } else {
255 std::sort(trkl.begin(), trkl.end());
256 }
257 if (iVertex < 0 && !mTrkParams[iteration].UseDiamond) {
258 trkl.erase(std::unique(trkl.begin(), trkl.end()), trkl.end());
259 trkl.shrink_to_fit();
260 }
261 auto& lut{mTimeFrame->getTrackletsLookupTable()[linkId]};
262 if (!trkl.empty()) {
263 const size_t nTracklets{trkl.size()};
264 const Tracklet* tkls{trkl.data()};
265 tbb::parallel_for(tbb::blocked_range<size_t>(0, nTracklets), [&](const tbb::blocked_range<size_t>& r) {
266 size_t begin{r.begin()}, end{r.end()};
267 const auto sameRun = [tkls](size_t i, size_t j) { return tkls[i].firstClusterIndex == tkls[j].firstClusterIndex; };
268 while (begin > 0 && begin < nTracklets && sameRun(begin, begin - 1)) {
269 ++begin;
270 }
271 while (end > 0 && end < nTracklets && sameRun(end, end - 1)) {
272 ++end;
273 }
274 for (size_t i{begin}; i < end; ++i) {
275 ++lut[tkls[i].firstClusterIndex + 1];
276 }
277 });
278 int* data{lut.data()};
279 tbb::parallel_scan(
280 tbb::blocked_range<size_t>(0, lut.size()), 0,
281 [data](const tbb::blocked_range<size_t>& r, int running, bool isFinal) {
282 for (size_t i{r.begin()}; i < r.end(); ++i) {
283 running += data[i];
284 if (isFinal) {
285 data[i] = running;
286 }
287 }
288 return running;
289 },
290 std::plus<int>());
291 }
292 });
293
295 if (mTimeFrame->hasMCinformation() && mTrkParams[iteration].CreateArtefactLabels) {
296 tbb::parallel_for(0, static_cast<int>(topology.nLinks), [&](const int linkId) {
297 const auto& link = topology.getLink(linkId);
298 for (auto& trk : mTimeFrame->getTracklets()[linkId]) {
299 MCCompLabel label;
300 int currentId{mTimeFrame->getClusters()[link.fromLayer][trk.firstClusterIndex].clusterId};
301 int nextId{mTimeFrame->getClusters()[link.toLayer][trk.secondClusterIndex].clusterId};
302 for (const auto& lab1 : mTimeFrame->getClusterLabels(link.fromLayer, currentId)) {
303 for (const auto& lab2 : mTimeFrame->getClusterLabels(link.toLayer, nextId)) {
304 if (lab1 == lab2 && lab1.isValid()) {
305 label = lab1;
306 break;
307 }
308 }
309 if (label.isValid()) {
310 break;
311 }
312 }
313 mTimeFrame->getTrackletsLabel(linkId).emplace_back(label);
314 }
315 });
316 }
317 });
318}
319
320template <int NLayers>
322{
323 const auto& cells = mTimeFrame->getCells()[0];
324 const bool withMC = mTimeFrame->hasMCinformation() && mTrkParams[iteration].CreateArtefactLabels;
325 const auto& cellLabels = mTimeFrame->getCellsLabel(0);
326 const auto nClusterLayer0 = mTimeFrame->getUnsortedClusters()[0].size();
327 const auto nClusterLayer1 = mTimeFrame->getUnsortedClusters()[1].size();
328 const auto nClusterLayer2 = mTimeFrame->getUnsortedClusters()[2].size();
329 const int nCells = static_cast<int>(cells.size());
330 const int keepThreshold = 3 - mTrkParams[iteration].CellLineSharedClusterCut;
331 const float maxZ = mTrkParams[iteration].VtxMaxZPositionAllowed;
332 const float lineMinPt = mTrkParams[iteration].VtxLineMinPt;
333 const float beamX = mTimeFrame->getBeamX();
334 const float beamY = mTimeFrame->getBeamY();
335 const float lineCurvatureScale = mTrkParams[iteration].VtxLineCurvatureScale;
336 auto makeKey = [](float attribute, int cellIdx) -> size_t {
337 const uint32_t attributeInt = std::bit_cast<uint32_t>(attribute);
338 return (static_cast<size_t>(attributeInt) << 32) | static_cast<uint32_t>(cellIdx);
339 };
340
341 // Per-cell precompute (geometry, line, pivot ROF, label), indexed directly by cell id k.
342 bounded_vector<int> kCl0(nCells, 0, mMemoryPool.get());
343 bounded_vector<int> kCl1(nCells, 0, mMemoryPool.get());
344 bounded_vector<int> kCl2(nCells, 0, mMemoryPool.get());
345 bounded_vector<int> kPivotRof(nCells, 0, mMemoryPool.get());
346 bounded_vector<uint8_t> kGeomOk(nCells, 0, mMemoryPool.get());
347 bounded_vector<uint8_t> kProjOk(nCells, 0, mMemoryPool.get());
348 bounded_vector<Line> kLine(nCells, mMemoryPool.get());
349 bounded_vector<o2::MCCompLabel> kLabel(withMC ? nCells : 0, o2::MCCompLabel{}, mMemoryPool.get());
350 mTaskArena->execute([&] {
351 tbb::parallel_for(0, nCells, [&](const int k) {
352 const auto& cell = cells[k];
353 const int c1 = cell.getSecondClusterIndex();
354 kCl0[k] = cell.getFirstClusterIndex();
355 kCl1[k] = c1;
356 kCl2[k] = cell.getThirdClusterIndex();
357 std::array<float, 3> origin, direction;
358 if (!getCellLineAtBeam(cell, beamX, beamY, getBz(), lineCurvatureScale, origin, direction)) {
359 return;
360 }
361 kGeomOk[k] = 1;
362 kLine[k] = Line(origin, direction, cell.getTimeStamp());
363 kPivotRof[k] = mTimeFrame->getClusterROF(1, c1);
364 const float dx = origin[0] - beamX;
365 const float dy = origin[1] - beamY;
366 const float den = direction[0] * direction[0] + direction[1] * direction[1];
367 kProjOk[k] = den >= constants::Tolerance &&
368 o2::gpu::CAMath::Abs(origin[2] - (dx * direction[0] + dy * direction[1]) / den * direction[2]) < maxZ;
369 if (withMC) {
370 kLabel[k] = cellLabels[k];
371 }
372 });
373 });
374
375 bounded_vector<size_t> clusterOwnershipLayer0(nClusterLayer0, std::numeric_limits<size_t>::max(), mMemoryPool.get());
376 bounded_vector<size_t> clusterOwnershipLayer1(nClusterLayer1, std::numeric_limits<size_t>::max(), mMemoryPool.get());
377 bounded_vector<size_t> clusterOwnershipLayer2(nClusterLayer2, std::numeric_limits<size_t>::max(), mMemoryPool.get());
378 mTaskArena->execute([&] {
379 tbb::parallel_for(0, nCells, [&](const int k) {
380 if (!kGeomOk[k]) {
381 return;
382 }
383 const float pt = cells[k].getPt();
384 const size_t key = makeKey(pt > 1.e-6f ? 1.f / pt : 1.e9f, k);
385 o2::gpu::GPUCommonMath::AtomicMin(&clusterOwnershipLayer0[kCl0[k]], key);
386 o2::gpu::GPUCommonMath::AtomicMin(&clusterOwnershipLayer1[kCl1[k]], key);
387 o2::gpu::GPUCommonMath::AtomicMin(&clusterOwnershipLayer2[kCl2[k]], key);
388 });
389 });
390
391 bounded_vector<uint8_t> cellAccepted(nCells, 0, mMemoryPool.get());
392 mTaskArena->execute([&] {
393 tbb::parallel_for(0, nCells, [&](const int k) {
394 if (!kGeomOk[k]) {
395 return;
396 }
397 int nOwned = 0;
398 nOwned += static_cast<uint32_t>(clusterOwnershipLayer0[kCl0[k]]) == static_cast<uint32_t>(k);
399 nOwned += static_cast<uint32_t>(clusterOwnershipLayer1[kCl1[k]]) == static_cast<uint32_t>(k);
400 nOwned += static_cast<uint32_t>(clusterOwnershipLayer2[kCl2[k]]) == static_cast<uint32_t>(k);
401 const bool ptOk = lineMinPt <= 0.f || cells[k].getPt() >= lineMinPt;
402 cellAccepted[k] = (nOwned >= keepThreshold) && kProjOk[k] && ptOk ? 1 : 0;
403 });
404 });
405
406 int total{0};
407 for (int k = 0; k < nCells; ++k) {
408 if (!cellAccepted[k]) {
409 continue;
410 }
411 const int pivotRof = kPivotRof[k];
412 mTimeFrame->getLines(pivotRof).emplace_back(kLine[k]);
413 mTimeFrame->getLinesQuality(pivotRof).emplace_back(
414 LineQuality{cells[k].getChi2(), cells[k].getPt()});
415 if (withMC) {
416 mTimeFrame->getLinesLabel(pivotRof).emplace_back(kLabel[k]);
417 }
418 ++total;
419 }
420 mTimeFrame->setNLinesTotal(total);
421}
422
423namespace
424{
425void computeDensities(const bounded_vector<float>& Z,
426 const bounded_vector<TimeEstBC>& T,
427 const float zWindow,
428 const float fineZWindow,
430 bounded_vector<LineWindow>& win,
431 bounded_vector<int>& density,
432 bounded_vector<LineWindow>& winFine,
433 bounded_vector<int>& densityFine)
434{
435 const int m = static_cast<int>(Z.size());
436 // Z is sorted, so the window bounds advance monotonically with k
437 int lo{0}, hi{0}, loF{0}, hiF{0};
438 for (int k = 0; k < m; ++k) {
439 const float zk = Z[k];
440 while (lo < m && Z[lo] < zk - zWindow) {
441 ++lo;
442 }
443 while (hi < m && Z[hi] <= zk + zWindow) {
444 ++hi;
445 }
446 while (loF < m && Z[loF] < zk - fineZWindow) {
447 ++loF;
448 }
449 while (hiF < m && Z[hiF] <= zk + fineZWindow) {
450 ++hiF;
451 }
452 win[k] = LineWindow{lo, hi};
453 winFine[k] = LineWindow{loF, hiF};
454 }
455
456 bounded_vector<int> byTime(m, pool);
457 std::iota(byTime.begin(), byTime.end(), 0);
458 std::sort(byTime.begin(), byTime.end(), [&](const int a, const int b) {
459 return std::tuple{T[a].lower(), T[a].upper(), a} < std::tuple{T[b].lower(), T[b].upper(), b};
460 });
461 // Bucket b holds zByTime[bucketStart[b], bucketStart[b + 1]); the last entry is m.
462 bounded_vector<float> zByTime(m, pool);
463 bounded_vector<int> bucketStart(pool);
464 for (int i = 0; i < m; ++i) {
465 zByTime[i] = Z[byTime[i]];
466 const auto& t = T[byTime[i]];
467 if (i == 0 || t.lower() != T[byTime[i - 1]].lower() || t.upper() != T[byTime[i - 1]].upper()) {
468 bucketStart.push_back(i);
469 }
470 }
471 bucketStart.push_back(m);
472
473 // How many values of the sorted range [first, last) lie in [lo, hi]
474 const auto countBetween = [](auto first, auto last, auto lo, auto hi) { return static_cast<int>(std::upper_bound(first, last, hi) - std::lower_bound(first, last, lo)); };
475
476 for (int k = 0; k < m; ++k) {
477 const float zk = Z[k];
478 int count{0}, countFine{0};
479 for (size_t b = 0; b + 1 < bucketStart.size(); ++b) {
480 if (!T[k].isCompatible(T[byTime[bucketStart[b]]])) {
481 continue;
482 }
483 const auto first = zByTime.cbegin() + bucketStart[b], last = zByTime.cbegin() + bucketStart[b + 1];
484 count += countBetween(first, last, zk - zWindow, zk + zWindow);
485 countFine += countBetween(first, last, zk - fineZWindow, zk + fineZWindow);
486 }
487 density[k] = count;
488 densityFine[k] = countFine;
489 }
490}
491
492void markLeftmostMaxima(const bounded_vector<int>& density,
493 const bounded_vector<LineWindow>& win,
495 bounded_vector<uint8_t>& isMax)
496{
497 const int m = static_cast<int>(density.size());
498 // Monotonic deque over the (monotonically advancing) windowsDetectors/ITSMFT/ITS/tracking/src/TrackingInterface.cxx
499 bounded_vector<int> maxDeque(m, pool);
500 int front{0}, back{0}, next{0};
501 for (int k = 0; k < m; ++k) {
502 while (next < win[k].hi) {
503 while (back > front && density[maxDeque[back - 1]] < density[next]) {
504 --back;
505 }
506 maxDeque[back++] = next++;
507 }
508 while (front < back && maxDeque[front] < win[k].lo) {
509 ++front;
510 }
511 isMax[k] = maxDeque[front] == k; // the window always contains k itself, so the deque is non-empty
512 }
513}
514} // namespace
515
516template <int NLayers>
517bool TrackerTraits<NLayers>::skipROF(int iteration, int rof) const
518{
519 return mTrkParams[iteration].PassFlags[IterationStep::SkipROFsAboveThreshold] &&
520 (int)mTimeFrame->getROFVertexLookupTableView().getVertices(1, rof).getEntries() >
521 mTrkParams[iteration].VertPerRofThreshold;
522}
523
524template <int NLayers>
526{
527 const auto& vertices = mTimeFrame->getPrimaryVertices();
528 double sx{0.}, sy{0.}, sw{0.};
529 for (const auto& v : vertices) {
530 const double w = static_cast<double>(v.getNContributors());
531 sx += w * v.getX();
532 sy += w * v.getY();
533 sw += w;
534 }
535 if (sw <= 0.) {
536 return;
537 }
538 const float bx = static_cast<float>(sx / sw);
539 const float by = static_cast<float>(sy / sw);
540 mTimeFrame->resetBeamXY(bx, by, static_cast<float>(sw));
541}
542
543template <int NLayers>
545{
546 const int nRofs = mTimeFrame->getNrof(1);
547 const bool withMC = mTimeFrame->hasMCinformation() && mTrkParams[iteration].CreateArtefactLabels;
548 std::vector<std::vector<Vertex>> rofVertices(nRofs);
549 std::vector<std::vector<VertexLabel>> rofLabels(nRofs);
550
551 const float beamX = mTimeFrame->getBeamX();
552 const float beamY = mTimeFrame->getBeamY();
553 const auto& tp = mTrkParams[iteration];
554 const float maxZ = tp.VtxMaxZPositionAllowed;
555 const float clusterCut = tp.VtxClusterCut;
556 const float pairCut2 = tp.VtxPairCut * tp.VtxPairCut;
557 const float zWindow = 0.5f * clusterCut;
558 const float nSigmaCut = tp.VtxNSigmaCut;
559 const float duplicateZCut = tp.VtxDuplicateZCut > 0.f
560 ? tp.VtxDuplicateZCut
561 : std::max(4.f * tp.VtxPairCut, 0.5f * clusterCut);
562 const int minContributors = tp.VtxClusterContributorsCut;
563 const int suppressLowMultDebris = tp.VtxSuppressLowMultDebris;
564 const bool doFine = tp.VtxFineZWindow > 0.f && tp.VtxFineZWindow < zWindow;
565 const float fineZWindow = doFine ? tp.VtxFineZWindow : zWindow;
566 const int fineMinDensity = tp.VtxFineMinDensity;
567 const float fineMaxDrift = tp.VtxFineMaxDrift;
568 const float goodSig = tp.VtxGoodContributorsSignificance;
569 const float duplicateZScale = tp.VtxDuplicateZScale;
570 const float goodLineChi2Cut = tp.VtxGoodLineChi2Cut;
571 const float goodLinePtCut = std::abs(mBz) > 0.01f ? tp.VtxGoodLinePtCut : -1.f;
572
573 const auto processROF = [&](const int rofId) {
574 if (skipROF(iteration, rofId)) {
575 return;
576 }
577 auto& lines = mTimeFrame->getLines(rofId);
578 const int n = static_cast<int>(lines.size());
579 if (n < 2) {
580 return;
581 }
582 const auto lineSpan = gsl::span<const Line>(lines.data(), lines.size());
583
584 // project every line onto the beam line
585 bounded_vector<float> zc(mMemoryPool.get());
586 bounded_vector<TimeEstBC> tc(mMemoryPool.get());
587 bounded_vector<int> lic(mMemoryPool.get());
588 zc.reserve(n);
589 tc.reserve(n);
590 lic.reserve(n);
591 for (int i = 0; i < n; ++i) {
592 const auto& line = lines[i];
593 float z;
594 if (!Line::getZAtBeam(line, beamX, beamY, z)) {
595 continue;
596 }
597 if (!(o2::gpu::CAMath::Abs(z) < maxZ)) {
598 continue;
599 }
600 zc.push_back(z);
601 tc.push_back(line.mTime);
602 lic.push_back(i);
603 }
604 const int m = static_cast<int>(zc.size());
605 if (m < 2) {
606 return;
607 }
608
609 // sort by z
610 bounded_vector<int> order(m, mMemoryPool.get());
611 std::iota(order.begin(), order.end(), 0);
612 std::sort(order.begin(), order.end(), [&](const int a, const int b) { return zc[a] < zc[b]; });
613 bounded_vector<float> Z(m, mMemoryPool.get());
614 bounded_vector<TimeEstBC> T(m, mMemoryPool.get());
615 bounded_vector<int> LI(m, mMemoryPool.get());
616 for (int i = 0; i < m; ++i) {
617 Z[i] = zc[order[i]];
618 T[i] = tc[order[i]];
619 LI[i] = lic[order[i]];
620 }
621
622 // density of time-compatible neighbours in a z window
623 bounded_vector<int> density(m, mMemoryPool.get());
624 bounded_vector<LineWindow> win(m, mMemoryPool.get());
625 bounded_vector<int> densityFine(m, mMemoryPool.get());
626 bounded_vector<LineWindow> winFine(m, mMemoryPool.get());
627 computeDensities(Z, T, zWindow, fineZWindow, mMemoryPool.get(), win, density, winFine, densityFine);
628
629 // peaks: leftmost density maxima
630 bounded_vector<uint8_t> isMax(m, mMemoryPool.get());
631 markLeftmostMaxima(density, win, mMemoryPool.get(), isMax);
632 bounded_vector<uint8_t> isMaxFine(doFine ? m : 0, mMemoryPool.get());
633 if (doFine) {
634 markLeftmostMaxima(densityFine, winFine, mMemoryPool.get(), isMaxFine);
635 }
636 bounded_vector<int> peaks(mMemoryPool.get());
637 bounded_vector<uint8_t> isPeakFine(m, 0, mMemoryPool.get());
638 for (int k = 0; k < m; ++k) {
639 bool peak = density[k] >= 2 && isMax[k];
640 if (!peak && doFine && densityFine[k] >= fineMinDensity && isMaxFine[k]) {
641 peak = true;
642 isPeakFine[k] = 1;
643 }
644 if (peak) {
645 peaks.push_back(k);
646 }
647 }
648 const int np = static_cast<int>(peaks.size());
649 if (np == 0) {
650 return;
651 }
652
653 // one candidate per peak: seed fit over the window, prune outliers, refit, apply the cuts
654 bounded_vector<ClusterLines> cand(np, mMemoryPool.get());
655 bounded_vector<uint8_t> ok(np, mMemoryPool.get());
656 bounded_vector<int> nGoodCand(np, 0, mMemoryPool.get());
657 bounded_vector<uint8_t> fineCand(np, 0, mMemoryPool.get());
658 bounded_vector<bounded_vector<int>> candMembers(withMC ? np : 0, bounded_vector<int>(mMemoryPool.get()), mMemoryPool.get());
659 const auto& linesQuality = mTimeFrame->getLinesQuality(rofId);
660 const int nQual = static_cast<int>(linesQuality.size());
661 for (int p = 0; p < np; p++) {
662 const int k = peaks[p];
663 fineCand[p] = isPeakFine[k];
664 bounded_vector<int> members(mMemoryPool.get());
665 members.reserve(density[k]);
666 for (int j = win[k].lo; j < win[k].hi; ++j) {
667 if (T[k].isCompatible(T[j])) {
668 members.push_back(LI[j]);
669 }
670 }
671 if (members.size() < 2) {
672 ok[p] = 0;
673 continue;
674 }
675 ClusterLines seed{gsl::span<const int>{members.data(), members.size()}, lineSpan};
676 if (!seed.isValid()) {
677 ok[p] = 0;
678 continue;
679 }
680 bounded_vector<int> kept(mMemoryPool.get());
681 kept.reserve(members.size());
682 for (const int idx : members) {
683 if (Line::getDistance2FromPoint(lines[idx], seed.getVertex()) < pairCut2) {
684 kept.push_back(idx);
685 }
686 }
687 if (kept.size() < 2) {
688 ok[p] = 0;
689 continue;
690 }
691 int nGood = 0;
692 if (nQual == 0) {
693 nGood = static_cast<int>(kept.size());
694 } else {
695 for (const int idx : kept) {
696 const float chi2 = idx < nQual ? linesQuality[idx].chi2 : -1.f;
697 const float pt = idx < nQual ? linesQuality[idx].pt : -1.f;
698 const bool okChi2 = goodLineChi2Cut <= 0.f || chi2 <= goodLineChi2Cut;
699 const bool okPt = goodLinePtCut <= 0.f || pt >= goodLinePtCut;
700 nGood += okChi2 && okPt;
701 }
702 }
703 nGoodCand[p] = nGood;
704 ClusterLines fit{gsl::span<const int>{kept.data(), kept.size()}, lineSpan};
705 const float bd2 = (beamX - fit.getVertex()[0]) * (beamX - fit.getVertex()[0]) +
706 (beamY - fit.getVertex()[1]) * (beamY - fit.getVertex()[1]);
707 if (!fit.isValid() || static_cast<int>(fit.getSize()) < minContributors || !(bd2 < nSigmaCut)) {
708 ok[p] = 0;
709 continue;
710 }
711 if (fineMaxDrift > 0.f && fineCand[p] && std::abs(fit.getVertex()[2] - seed.getVertex()[2]) > fineMaxDrift) {
712 ok[p] = 0;
713 continue;
714 }
715 cand[p] = std::move(fit);
716 if (withMC) {
717 candMembers[p] = std::move(kept);
718 }
719 ok[p] = 1;
720 }
721
722 // duplicate suppression
723 bounded_vector<uint8_t> keep(np, mMemoryPool.get());
724 for (int p = 0; p < np; ++p) {
725 if (!ok[p]) {
726 keep[p] = 0;
727 continue;
728 }
729 uint8_t survive = 1;
730 const float zp = cand[p].getVertex()[2];
731 const auto sp = cand[p].getSize();
732 const float radius = (duplicateZScale > 0.f && sp > 0)
733 ? duplicateZScale / std::sqrt(static_cast<float>(sp))
734 : duplicateZCut;
735 for (int q = 0; q < np && survive; ++q) {
736 if (q == p || !ok[q]) {
737 continue;
738 }
739 if (!cand[p].getTimeStamp().isCompatible(cand[q].getTimeStamp())) {
740 continue;
741 }
742 if (o2::gpu::GPUCommonMath::Abs(zp - cand[q].getVertex()[2]) < radius) {
743 const auto sq = cand[q].getSize();
744 if (sq > sp || (sq == sp && q < p)) {
745 survive = 0;
746 }
747 }
748 }
749 keep[p] = survive;
750 }
751
752 // emit
753 bounded_vector<int> accepted(mMemoryPool.get());
754 for (int p = 0; p < np; ++p) {
755 if (keep[p]) {
756 accepted.push_back(p);
757 }
758 }
759 std::sort(accepted.begin(), accepted.end(),
760 [&](int a, int b) { return cand[a].getSize() > cand[b].getSize(); });
761 double rofLoad = 0.;
762 if (goodSig > 0.f) {
763 for (int p = 0; p < np; ++p) {
764 if (ok[p] && !fineCand[p]) {
765 rofLoad += cand[p].getSize();
766 }
767 }
768 }
769 const float debrisThreshold = goodSig > 0.f ? getDebrisThreshold(goodSig, rofLoad, suppressLowMultDebris) : 0.f;
770 for (const int p : accepted) {
771 if (!rofVertices[rofId].empty()) {
772 if (goodSig > 0.f) {
773 if (nGoodCand[p] < debrisThreshold) {
774 continue;
775 }
776 } else if (static_cast<int>(cand[p].getSize()) < suppressLowMultDebris) {
777 continue;
778 }
779 }
780 Vertex vertex{cand[p].getVertex(),
781 cand[p].getRMS2(),
782 (ushort)cand[p].getSize(),
783 cand[p].getAvgDistance2()};
784 vertex.setTimeStamp(cand[p].getTimeStamp());
785 if (mTrkParams[iteration].PassFlags[IterationStep::MarkVerticesAsUPC]) {
786 vertex.setFlags(Vertex::UPCMode);
787 }
788 rofVertices[rofId].push_back(vertex);
789 if (withMC) {
790 auto& lineLabels = mTimeFrame->getLinesLabel(rofId);
791 const int nLab = static_cast<int>(lineLabels.size());
792 bounded_vector<o2::MCCompLabel> labels(mMemoryPool.get());
793 for (const auto idx : candMembers[p]) {
794 if (idx < 0 || idx >= nLab) {
795 LOGP(error, "Seeding vertexer: line label index {} out of range (nLab={}, rof={}), skipping label", idx, nLab, rofId);
796 continue;
797 }
798 labels.push_back(lineLabels[idx]);
799 }
800 rofLabels[rofId].push_back(computeMainVertexLabel(labels));
801 }
802 }
803 };
804
805 if (mTaskArena->max_concurrency() <= 1) {
806 for (int rofId{0}; rofId < nRofs; ++rofId) {
807 processROF(rofId);
808 }
809 } else {
810 mTaskArena->execute([&] {
811 tbb::parallel_for(0, nRofs, [&](const int rofId) {
812 processROF(rofId);
813 });
814 });
815 }
816 if (!tp.PassFlags[IterationStep::MarkVerticesAsUPC]) { // UPC ROFs are near-empty by construction
817 pruneOverpopulatedRofs(rofVertices, rofLabels, tp.VtxOverpopulatedRofNSigma, suppressLowMultDebris);
818 }
819 for (int rofId{0}; rofId < nRofs; ++rofId) {
820 for (auto& vertex : rofVertices[rofId]) {
821 mTimeFrame->addPrimaryVertex(vertex);
822 }
823 if (withMC) {
824 for (auto& label : rofLabels[rofId]) {
825 mTimeFrame->addPrimaryVertexLabel(label);
826 }
827 }
828 }
829
830 auto& pvs = mTimeFrame->getPrimaryVertices();
831 bounded_vector<size_t> indices(pvs.size(), mMemoryPool.get());
832 std::iota(indices.begin(), indices.end(), 0);
833 std::sort(indices.begin(), indices.end(), [&pvs](const size_t i, const size_t j) {
834 const auto aLower = pvs[i].getTimeStamp().lower();
835 const auto bLower = pvs[j].getTimeStamp().lower();
836 if (aLower != bLower) {
837 return aLower < bLower;
838 }
839 return pvs[i].getNContributors() > pvs[j].getNContributors();
840 });
841 bounded_vector<Vertex> sortedVtx(pvs.get_allocator());
842 sortedVtx.reserve(pvs.size());
843 for (const size_t idx : indices) {
844 sortedVtx.push_back(pvs[idx]);
845 }
846 pvs.swap(sortedVtx);
847 if (withMC) {
848 auto& mc = mTimeFrame->getPrimaryVerticesLabels();
849 bounded_vector<VertexLabel> sortedMC(mc.get_allocator());
850 sortedMC.reserve(mc.size());
851 for (const size_t idx : indices) {
852 sortedMC.push_back(mc[idx]);
853 }
854 mc.swap(sortedMC);
855 }
856 mTimeFrame->updateROFVertexLookupTable();
857}
858
859template <int NLayers>
861{
862 const auto topology = mTimeFrame->getTrackingTopologyView();
863 const bool createLabels = mTimeFrame->hasMCinformation() && mTrkParams[iteration].CreateArtefactLabels;
864
865 mTaskArena->execute([&] {
866 const int maxConcurrency = std::max(1, mTaskArena->max_concurrency());
867 auto clearTopology = [&](const int cellTopologyId) {
868 deepVectorClear(mTimeFrame->getCells()[cellTopologyId]);
869 deepVectorClear(mTimeFrame->getCellsLookupTable()[cellTopologyId]);
870 if (createLabels) {
871 deepVectorClear(mTimeFrame->getCellsLabel(cellTopologyId));
872 }
873 };
874 if (maxConcurrency > 1) {
875 tbb::parallel_for(0, static_cast<int>(topology.nCells), clearTopology);
876 } else {
877 for (int cellTopologyId{0}; cellTopologyId < topology.nCells; ++cellTopologyId) {
878 clearTopology(cellTopologyId);
879 }
880 }
881
882 const float cellTanLNSigma = mTrkParams[iteration].CellDeltaTanLambdaNSigma > 0.f
883 ? mTrkParams[iteration].CellDeltaTanLambdaNSigma
884 : mTrkParams[iteration].NSigmaCut;
885 const float cellDeltaTanLambdaSigma = mTrkParams[iteration].CellDeltaTanLambdaSigma;
886 const bool isSeedingVertexPass = mTrkParams[iteration].PassFlags[IterationStep::SeedingVertexPass];
887
888 auto forTrackletCells = [&](int cellTopologyId, int iTracklet, auto&& emit) {
889 const auto& cellTopology = topology.getCell(cellTopologyId);
890 const auto& firstLink = topology.getLink(cellTopology.firstLink);
891 const auto& secondLink = topology.getLink(cellTopology.secondLink);
892 const float cellDeltaPhiCut = isSeedingVertexPass ? math_utils::cellDeltaPhiBound(mBz, mTrkParams[iteration].CellDeltaPhiMinPt,
893 mTrkParams[iteration].LayerRadii[firstLink.fromLayer],
894 mTrkParams[iteration].LayerRadii[firstLink.toLayer],
895 mTrkParams[iteration].LayerRadii[secondLink.toLayer],
896 mTimeFrame->getLinkMSAngle(cellTopology.firstLink))
897 : o2::constants::math::TwoPI;
898 const Tracklet& currentTracklet{mTimeFrame->getTracklets()[cellTopology.firstLink][iTracklet]};
899 const int nextLayerClusterIndex{currentTracklet.secondClusterIndex};
900 const int nextLayerFirstTrackletIndex{mTimeFrame->getTrackletsLookupTable()[cellTopology.secondLink][nextLayerClusterIndex]};
901 const int nextLayerLastTrackletIndex{mTimeFrame->getTrackletsLookupTable()[cellTopology.secondLink][nextLayerClusterIndex + 1]};
902 const Tracklet* nextTracklets{mTimeFrame->getTracklets()[cellTopology.secondLink].data()};
903 for (int iNextTracklet{nextLayerFirstTrackletIndex}; iNextTracklet < nextLayerLastTrackletIndex; ++iNextTracklet) {
904 const Tracklet& nextTracklet{nextTracklets[iNextTracklet]};
905 if (nextTracklet.firstClusterIndex != nextLayerClusterIndex) {
906 break;
907 }
908 if (!currentTracklet.getTimeStamp().isCompatible(nextTracklet.getTimeStamp())) {
909 continue;
910 }
911 if (!(std::abs(currentTracklet.tanLambda - nextTracklet.tanLambda) / cellDeltaTanLambdaSigma < cellTanLNSigma)) {
912 continue;
913 }
914 if (!math_utils::isPhiDifferenceBelow(currentTracklet.phi, nextTracklet.phi, cellDeltaPhiCut)) {
915 continue;
916 }
918 const int clusId[3]{
919 mTimeFrame->getClusters()[firstLink.fromLayer][currentTracklet.firstClusterIndex].clusterId,
920 mTimeFrame->getClusters()[firstLink.toLayer][nextTracklet.firstClusterIndex].clusterId,
921 mTimeFrame->getClusters()[secondLink.toLayer][nextTracklet.secondClusterIndex].clusterId};
922 const int hitLayers[3]{firstLink.fromLayer, firstLink.toLayer, secondLink.toLayer};
923 const auto& cluster1Glo = mTimeFrame->getUnsortedClusters()[firstLink.fromLayer][clusId[0]];
924 const auto& cluster2Glo = mTimeFrame->getUnsortedClusters()[firstLink.toLayer][clusId[1]];
925 const auto& cluster3Tf = mTimeFrame->getTrackingFrameInfoOnLayer(secondLink.toLayer)[clusId[2]];
926 auto track{o2::its::track::buildTrackSeed(cluster1Glo, cluster2Glo, cluster3Tf, mBz)};
927
928 float chi2{0.f};
929 bool good{false};
930 for (int iC{2}; iC--;) {
931 const int hitLayer = hitLayers[iC];
932 const TrackingFrameInfo& trackingHit = mTimeFrame->getTrackingFrameInfoOnLayer(hitLayer)[clusId[iC]];
933
934 if (!track.rotate(trackingHit.alphaTrackingFrame)) {
935 break;
936 }
937
938 if (!track.propagateTo(trackingHit.xTrackingFrame, getBz())) {
939 break;
940 }
941
942 if (!track.correctForMaterial(mTrkParams[iteration].LayerxX0[hitLayer], mTrkParams[iteration].LayerxX0[hitLayer] * constants::Radl * constants::Rho, true)) {
943 break;
944 }
945
946 const auto predChi2{track.getPredictedChi2Quiet(trackingHit.positionTrackingFrame, trackingHit.covarianceTrackingFrame)};
947 if (!iC && predChi2 > mTrkParams[iteration].MaxChi2ClusterAttachment) {
948 break;
949 }
950
951 if (!track.o2::track::TrackParCov::update(trackingHit.positionTrackingFrame, trackingHit.covarianceTrackingFrame)) {
952 break;
953 }
954
955 good = !iC;
956 chi2 += predChi2;
957 }
958 if (good) {
959 TimeEstBC ts = currentTracklet.getTimeStamp();
960 ts += nextTracklet.getTimeStamp();
961 emit(cellTopology.hitLayerMask, clusId[0], clusId[1], clusId[2], iTracklet, iNextTracklet, track, chi2, ts);
962 }
963 }
964 };
965
966 bounded_vector<int> activeTopologies(mMemoryPool.get());
967 activeTopologies.reserve(topology.nCells);
968 for (int cellTopologyId = 0; cellTopologyId < topology.nCells; ++cellTopologyId) {
969 const auto& cellTopology = topology.getCell(cellTopologyId);
970 if (!mTimeFrame->getTracklets()[cellTopology.firstLink].empty() &&
971 !mTimeFrame->getTracklets()[cellTopology.secondLink].empty()) {
972 activeTopologies.push_back(cellTopologyId);
973 }
974 }
975
976 const int nConcurrentSinks = std::min(maxConcurrency, static_cast<int>(activeTopologies.size()));
977 auto processTopology = [&](const int cellTopologyId) {
978 const auto& cellTopology = topology.getCell(cellTopologyId);
979
980 auto& layerCells = mTimeFrame->getCells()[cellTopologyId];
981 auto& lut = mTimeFrame->getCellsLookupTable()[cellTopologyId];
982 const int currentLayerTrackletsNum{static_cast<int>(mTimeFrame->getTracklets()[cellTopology.firstLink].size())};
983
984 const auto key = CapacityEstimator::makeKey(SlabSite::Cells, iteration, 0, cellTopologyId);
985 const auto scale = static_cast<double>(currentLayerTrackletsNum);
986 if (maxConcurrency > 1) {
987 const size_t capacity = mTimeFrame->getCapacityEstimator().capacity(key, scale);
988
989 GroupedSlabSink<CellSeed> sink{{.capacity = capacity, .nThreads = maxConcurrency, .nConcurrentSinks = nConcurrentSinks}, mMemoryPool.get()};
990 tbb::parallel_for(0, currentLayerTrackletsNum, [&](const int iTracklet) {
991 auto& handle = sink.local();
992 handle.beginProducer(iTracklet);
993 forTrackletCells(cellTopologyId, iTracklet, [&handle](auto&&... args) { handle.emplace(std::forward<decltype(args)>(args)...); });
994 });
995 const auto st = sink.stats();
996 sink.finalizeGrouped(size_t(currentLayerTrackletsNum), lut, layerCells);
997 mTimeFrame->getCapacityEstimator().update(key, scale, st.requested, st.capacity, st.emitted, st.spilled,
998 st.overflowed, st.memoryLimited);
999 } else {
1000 lut.resize(currentLayerTrackletsNum + 1);
1001 for (int iTracklet{0}; iTracklet < currentLayerTrackletsNum; ++iTracklet) {
1002 lut[iTracklet] = static_cast<int>(layerCells.size());
1003 forTrackletCells(cellTopologyId, iTracklet, [&](auto&&... args) {
1004 layerCells.emplace_back(std::forward<decltype(args)>(args)...);
1005 });
1006 }
1007 lut.back() = static_cast<int>(layerCells.size());
1008 }
1009
1010 if (createLabels) {
1011 auto& labels = mTimeFrame->getCellsLabel(cellTopologyId);
1012 labels.reserve(layerCells.size());
1013 for (const auto& cell : layerCells) {
1014 MCCompLabel currentLab{mTimeFrame->getTrackletsLabel(cellTopology.firstLink)[cell.getFirstTrackletIndex()]};
1015 MCCompLabel nextLab{mTimeFrame->getTrackletsLabel(cellTopology.secondLink)[cell.getSecondTrackletIndex()]};
1016 labels.emplace_back(currentLab == nextLab ? currentLab : MCCompLabel());
1017 }
1018 }
1019 };
1020
1021 if (maxConcurrency > 1) {
1022 tbb::parallel_for(0, static_cast<int>(activeTopologies.size()), [&](const int i) {
1023 processTopology(activeTopologies[i]);
1024 });
1025 } else {
1026 for (const int cellTopologyId : activeTopologies) {
1027 processTopology(cellTopologyId);
1028 }
1029 }
1030
1031 auto clearTracklets = [&](const int linkId) {
1032 deepVectorClear(mTimeFrame->getTracklets()[linkId]);
1033 deepVectorClear(mTimeFrame->getTrackletsLabel(linkId));
1034 };
1035 if (maxConcurrency > 1) {
1036 tbb::parallel_for(0, static_cast<int>(topology.nLinks), clearTracklets);
1037 } else {
1038 for (int linkId{0}; linkId < topology.nLinks; ++linkId) {
1039 clearTracklets(linkId);
1040 }
1041 }
1042 });
1043}
1044
1045template <int NLayers>
1047{
1048 const auto topology = mTimeFrame->getTrackingTopologyView();
1049 mTaskArena->execute([&] {
1050 const int maxConcurrency = std::max(1, mTaskArena->max_concurrency());
1051 auto clearNeighbours = [&](const int cellTopologyId) {
1052 deepVectorClear(mTimeFrame->getCellsNeighbours()[cellTopologyId]);
1053 deepVectorClear(mTimeFrame->getCellsNeighboursTopology()[cellTopologyId]);
1054 deepVectorClear(mTimeFrame->getCellsNeighboursLUT()[cellTopologyId]);
1055 };
1056 if (maxConcurrency > 1) {
1057 tbb::parallel_for(0, static_cast<int>(topology.nCells), clearNeighbours);
1058 } else {
1059 for (int cellTopologyId{0}; cellTopologyId < topology.nCells; ++cellTopologyId) {
1060 clearNeighbours(cellTopologyId);
1061 }
1062 }
1063
1064 auto neighbourLess = [](const CellNeighbour& a, const CellNeighbour& b) {
1065 return std::tie(a.nextCellTopology, a.nextCell, a.cellTopology, a.cell) <
1066 std::tie(b.nextCellTopology, b.nextCell, b.cellTopology, b.cell);
1067 };
1068
1069 for (int outerLayer{0}; outerLayer < NLayers; ++outerLayer) {
1070 bounded_vector<int> activeTopologies(mMemoryPool.get());
1071 activeTopologies.reserve(topology.nCells);
1072 size_t sourceCellCount{0};
1073 for (int cellTopologyId{0}; cellTopologyId < topology.nCells; ++cellTopologyId) {
1074 const auto& cellTopology = topology.getCell(cellTopologyId);
1075 if (cellTopology.hitLayerMask.last() != outerLayer ||
1076 mTimeFrame->getCells()[cellTopologyId].empty()) {
1077 continue;
1078 }
1079 const auto successors = topology.getCellsStartingWithLink(cellTopology.secondLink);
1080 if (!successors.getEntries()) {
1081 continue;
1082 }
1083 activeTopologies.push_back(cellTopologyId);
1084 sourceCellCount += mTimeFrame->getCells()[cellTopologyId].size();
1085 }
1086
1087 if (activeTopologies.empty()) {
1088 continue;
1089 }
1090
1091 auto forSourceCell = [&](const int cellTopologyId, const int iCell, auto&& emit) {
1092 const auto& cellTopology = topology.getCell(cellTopologyId);
1093 const auto successors = topology.getCellsStartingWithLink(cellTopology.secondLink);
1094 const auto& currentCellSeed{mTimeFrame->getCells()[cellTopologyId][iCell]};
1095 const int nextLayerTrackletIndex{currentCellSeed.getSecondTrackletIndex()};
1096 for (int iSuccessor{0}; iSuccessor < successors.getEntries(); ++iSuccessor) {
1097 const int nextCellTopologyId = topology.cellsByFirstLink[successors.getFirstEntry() + iSuccessor];
1098 if (mTimeFrame->getCells()[nextCellTopologyId].empty() ||
1099 mTimeFrame->getCellsLookupTable()[nextCellTopologyId].empty()) {
1100 continue;
1101 }
1102 const auto& nextCellLUT = mTimeFrame->getCellsLookupTable()[nextCellTopologyId];
1103 if (nextLayerTrackletIndex + 1 >= static_cast<int>(nextCellLUT.size())) {
1104 continue;
1105 }
1106 const int nextLayerFirstCellIndex{nextCellLUT[nextLayerTrackletIndex]};
1107 const int nextLayerLastCellIndex{nextCellLUT[nextLayerTrackletIndex + 1]};
1108 for (int iNextCell{nextLayerFirstCellIndex}; iNextCell < nextLayerLastCellIndex; ++iNextCell) {
1109 const auto& nextCellSeedRef{mTimeFrame->getCells()[nextCellTopologyId][iNextCell]};
1110 if (nextCellSeedRef.getFirstTrackletIndex() != nextLayerTrackletIndex || !currentCellSeed.getTimeStamp().isCompatible(nextCellSeedRef.getTimeStamp())) {
1111 break;
1112 }
1113
1114 auto nextCellSeed{mTimeFrame->getCells()[nextCellTopologyId][iNextCell]};
1115 if (!nextCellSeed.rotate(currentCellSeed.getAlpha()) ||
1116 !nextCellSeed.propagateTo(currentCellSeed.getX(), getBz())) {
1117 continue;
1118 }
1119
1120 float chi2 = currentCellSeed.getPredictedChi2Fast(nextCellSeed);
1121 if (chi2 > mTrkParams[iteration].MaxChi2ClusterAttachment) {
1122 continue;
1123 }
1124
1125 const int nextLevel = currentCellSeed.getLevel() + 1;
1126 emit(cellTopologyId, iCell, nextCellTopologyId, iNextCell, nextLevel);
1127 }
1128 }
1129 };
1130
1131 bounded_vector<CellNeighbour> waveNeighbours{mMemoryPool.get()};
1132 const auto key = CapacityEstimator::makeKey(SlabSite::Neighbours, iteration, 0, outerLayer);
1133 const auto scale = static_cast<double>(sourceCellCount);
1134 if (maxConcurrency > 1) {
1135 const size_t capacity = mTimeFrame->getCapacityEstimator().capacity(key, scale);
1136 UnorderedSlabSink<CellNeighbour> sink{{.capacity = capacity, .nThreads = maxConcurrency}, mMemoryPool.get()};
1137 tbb::parallel_for(0, static_cast<int>(activeTopologies.size()), [&](const int i) {
1138 const int cellTopologyId = activeTopologies[i];
1139 tbb::parallel_for(0, static_cast<int>(mTimeFrame->getCells()[cellTopologyId].size()), [&](const int iCell) {
1140 auto& handle = sink.local();
1141 forSourceCell(cellTopologyId, iCell, [&handle](auto&&... args) {
1142 handle.emplace(std::forward<decltype(args)>(args)...);
1143 });
1144 });
1145 });
1146 const auto st = sink.stats();
1147 sink.finalizeUnordered(waveNeighbours);
1148 mTimeFrame->getCapacityEstimator().update(key, scale, st.requested, st.capacity, st.emitted, st.spilled,
1149 st.overflowed, st.memoryLimited);
1150 tbb::parallel_sort(waveNeighbours.begin(), waveNeighbours.end(), neighbourLess);
1151 } else {
1152 for (const int cellTopologyId : activeTopologies) {
1153 for (int iCell{0}; iCell < static_cast<int>(mTimeFrame->getCells()[cellTopologyId].size()); ++iCell) {
1154 forSourceCell(cellTopologyId, iCell, [&](auto&&... args) {
1155 waveNeighbours.emplace_back(std::forward<decltype(args)>(args)...);
1156 });
1157 }
1158 }
1159 std::sort(waveNeighbours.begin(), waveNeighbours.end(), neighbourLess);
1160 }
1161
1162 struct TargetSpan {
1163 int topologyId;
1164 size_t begin;
1165 size_t end;
1166 };
1167 bounded_vector<TargetSpan> targetSpans{mMemoryPool.get()};
1168 targetSpans.reserve(topology.nCells);
1169 for (int targetTopologyId{0}; targetTopologyId < topology.nCells; ++targetTopologyId) {
1170 const auto first = std::lower_bound(waveNeighbours.begin(), waveNeighbours.end(), targetTopologyId,
1171 [](const CellNeighbour& neighbour, int id) { return neighbour.nextCellTopology < id; });
1172 const auto last = std::upper_bound(first, waveNeighbours.end(), targetTopologyId,
1173 [](int id, const CellNeighbour& neighbour) { return id < neighbour.nextCellTopology; });
1174 if (first != last) {
1175 targetSpans.push_back({targetTopologyId, static_cast<size_t>(first - waveNeighbours.begin()), static_cast<size_t>(last - waveNeighbours.begin())});
1176 }
1177 }
1178
1179 auto finalizeTarget = [&](const int i) {
1180 const auto [targetTopologyId, begin, end] = targetSpans[i];
1181 auto& cellsNeighbourLUT = mTimeFrame->getCellsNeighboursLUT()[targetTopologyId];
1182 cellsNeighbourLUT.assign(mTimeFrame->getCells()[targetTopologyId].size(), 0);
1183 for (size_t j{begin}; j < end; ++j) {
1184 const auto& neighbour = waveNeighbours[j];
1185 ++cellsNeighbourLUT[neighbour.nextCell];
1186 auto& targetCell = mTimeFrame->getCells()[targetTopologyId][neighbour.nextCell];
1187 if (neighbour.level > targetCell.getLevel()) {
1188 targetCell.setLevel(neighbour.level);
1189 }
1190 }
1191 std::inclusive_scan(cellsNeighbourLUT.begin(), cellsNeighbourLUT.end(), cellsNeighbourLUT.begin());
1192
1193 auto& cellsNeighbours = mTimeFrame->getCellsNeighbours()[targetTopologyId];
1194 auto& cellsNeighboursTopology = mTimeFrame->getCellsNeighboursTopology()[targetTopologyId];
1195 cellsNeighbours.resize(end - begin);
1196 cellsNeighboursTopology.resize(end - begin);
1197 for (size_t j{begin}; j < end; ++j) {
1198 cellsNeighbours[j - begin] = waveNeighbours[j].cell;
1199 cellsNeighboursTopology[j - begin] = waveNeighbours[j].cellTopology;
1200 }
1201 };
1202 if (maxConcurrency > 1) {
1203 tbb::parallel_for(0, static_cast<int>(targetSpans.size()), finalizeTarget);
1204 } else {
1205 for (int i{0}; i < static_cast<int>(targetSpans.size()); ++i) {
1206 finalizeTarget(i);
1207 }
1208 }
1209 }
1210
1211 // clean up LUTs
1212 auto clearCellLUT = [&](const int cellTopologyId) {
1213 deepVectorClear(mTimeFrame->getCellsLookupTable()[cellTopologyId]);
1214 };
1215 if (maxConcurrency > 1) {
1216 tbb::parallel_for(0, static_cast<int>(topology.nCells), clearCellLUT);
1217 } else {
1218 for (int cellTopologyId{0}; cellTopologyId < topology.nCells; ++cellTopologyId) {
1219 clearCellLUT(cellTopologyId);
1220 }
1221 }
1222 });
1223}
1224
1225template <int NLayers>
1226template <typename InputSeed>
1227void TrackerTraits<NLayers>::processNeighbours(int iteration, int defaultCellTopologyId, int iLevel, uint64_t capacityKey, const bounded_vector<InputSeed>& currentSeeds, bounded_vector<RoadSeedN>& updatedSeeds)
1228{
1229 constexpr bool IsInitial = std::is_same_v<InputSeed, CellSeed>;
1230 static_assert(IsInitial || std::is_same_v<InputSeed, RoadSeedN>);
1231 auto propagator = o2::base::Propagator::Instance();
1232
1233 mTaskArena->execute([&] {
1234 auto forCellNeighbours = [&](int iCell, auto&& emit) {
1235 const auto& inputSeed = currentSeeds[iCell];
1236 const auto& currentCell = [&]() -> const auto& {
1237 if constexpr (IsInitial) {
1238 return inputSeed;
1239 } else {
1240 return inputSeed.seed;
1241 }
1242 }();
1243 const int cellTopologyId = [&]() {
1244 if constexpr (IsInitial) {
1245 return defaultCellTopologyId;
1246 } else {
1247 return inputSeed.cellTopologyId;
1248 }
1249 }();
1250 const int cellId = [&]() {
1251 if constexpr (IsInitial) {
1252 return iCell;
1253 } else {
1254 return inputSeed.cellId;
1255 }
1256 }();
1257
1258 if (currentCell.getLevel() != iLevel) {
1259 return;
1260 }
1261 if constexpr (IsInitial) {
1262 for (int layer = 0; layer < NLayers; ++layer) {
1263 const int clusterIndex = currentCell.getCluster(layer);
1264 if (clusterIndex != constants::UnusedIndex && mTimeFrame->isClusterUsed(layer, clusterIndex)) {
1265 return;
1266 }
1267 }
1268 }
1269
1270 if (cellTopologyId < 0 || mTimeFrame->getCellsNeighboursLUT()[cellTopologyId].empty()) {
1271 return;
1272 }
1273 const int startNeighbourId{cellId ? mTimeFrame->getCellsNeighboursLUT()[cellTopologyId][cellId - 1] : 0};
1274 const int endNeighbourId{mTimeFrame->getCellsNeighboursLUT()[cellTopologyId][cellId]};
1275 for (int iNeighbourCell{startNeighbourId}; iNeighbourCell < endNeighbourId; ++iNeighbourCell) {
1276 const int neighbourCellTopologyId = mTimeFrame->getCellsNeighboursTopology()[cellTopologyId][iNeighbourCell];
1277 const int neighbourCellId = mTimeFrame->getCellsNeighbours()[cellTopologyId][iNeighbourCell];
1278 const auto& neighbourCell = mTimeFrame->getCells()[neighbourCellTopologyId][neighbourCellId];
1279 if (neighbourCell.getSecondTrackletIndex() != currentCell.getFirstTrackletIndex()) {
1280 continue;
1281 }
1282 if (!currentCell.getTimeStamp().isCompatible(neighbourCell.getTimeStamp())) {
1283 continue;
1284 }
1285 if (currentCell.getLevel() - 1 != neighbourCell.getLevel()) {
1286 continue;
1287 }
1288 const int neighbourLayer = neighbourCell.getInnerLayer();
1289 const int neighbourCluster = neighbourCell.getFirstClusterIndex();
1290 if (mTimeFrame->isClusterUsed(neighbourLayer, neighbourCluster)) {
1291 continue;
1292 }
1293
1295 TrackSeedN seed{currentCell};
1296 seed.getTimeStamp() = currentCell.getTimeStamp();
1297 seed.getTimeStamp() += neighbourCell.getTimeStamp();
1298 const auto& trHit = mTimeFrame->getTrackingFrameInfoOnLayer(neighbourLayer)[neighbourCluster];
1299
1300 if (!seed.rotate(trHit.alphaTrackingFrame)) {
1301 continue;
1302 }
1303
1304 if (!propagator->propagateToX(seed, trHit.xTrackingFrame, getBz(), o2::base::PropagatorImpl<float>::MAX_SIN_PHI, o2::base::PropagatorImpl<float>::MAX_STEP, mTrkParams[iteration].CorrType)) {
1305 continue;
1306 }
1307
1308 if (mTrkParams[iteration].CorrType == o2::base::PropagatorF::MatCorrType::USEMatCorrNONE) {
1309 if (!seed.correctForMaterial(mTrkParams[iteration].LayerxX0[neighbourLayer], mTrkParams[iteration].LayerxX0[neighbourLayer] * constants::Radl * constants::Rho, true)) {
1310 continue;
1311 }
1312 }
1313
1314 auto predChi2{seed.getPredictedChi2Quiet(trHit.positionTrackingFrame, trHit.covarianceTrackingFrame)};
1315 if ((predChi2 > mTrkParams[iteration].MaxChi2ClusterAttachment) || predChi2 < 0.f) {
1316 continue;
1317 }
1318 seed.setChi2(seed.getChi2() + predChi2);
1319 if (!seed.o2::track::TrackParCov::update(trHit.positionTrackingFrame, trHit.covarianceTrackingFrame)) {
1320 continue;
1321 }
1322
1323 seed.getClusters()[neighbourLayer] = neighbourCluster;
1324 auto mask = seed.getHitLayerMask();
1325 mask.set(neighbourLayer);
1326 seed.setHitLayerMask(mask);
1327 seed.setLevel(neighbourCell.getLevel());
1328 seed.setFirstTrackletIndex(neighbourCell.getFirstTrackletIndex());
1329 seed.setSecondTrackletIndex(neighbourCell.getSecondTrackletIndex());
1330 emit(std::move(seed), neighbourCellId, neighbourCellTopologyId);
1331 }
1332 };
1333
1334 const int nCells = static_cast<int>(currentSeeds.size());
1335 if (mTaskArena->max_concurrency() <= 1) {
1336 for (int iCell{0}; iCell < nCells; ++iCell) {
1337 forCellNeighbours(iCell, [&](auto&&... args) { updatedSeeds.emplace_back(std::forward<decltype(args)>(args)...); });
1338 }
1339 } else {
1340 const auto scale = static_cast<double>(nCells);
1341 const size_t capacity = mTimeFrame->getCapacityEstimator().capacity(capacityKey, scale);
1342 UnorderedSlabSink<RoadSeedN> sink{{.capacity = capacity, .nThreads = mTaskArena->max_concurrency()}, mMemoryPool.get()};
1343
1344 tbb::parallel_for(0, nCells, [&](const int iCell) {
1345 auto& handle = sink.local();
1346 forCellNeighbours(iCell, [&](auto&&... args) { handle.emplace(std::forward<decltype(args)>(args)...); });
1347 });
1348 const auto st = sink.stats();
1349 sink.finalizeUnordered(updatedSeeds);
1350 mTimeFrame->getCapacityEstimator().update(capacityKey, scale, st.requested, st.capacity, st.emitted, st.spilled,
1351 st.overflowed, st.memoryLimited);
1352 }
1353 });
1354}
1355
1356template <int NLayers>
1358 TrackITSExt& track,
1359 const int iteration,
1360 const TrackingFrameInfo* const* tfInfos,
1361 const Cluster* const* unsortedClusters,
1362 const o2::base::Propagator* propagator,
1363 const TrackFollowContext<NLayers>& followCtx,
1364 TrackFollowerScratch& scratch)
1365{
1366 const auto& trkParams = mTrkParams[iteration];
1368 tfInfos, trkParams.LayerxX0.data(), trkParams.NLayers, mBz,
1369 trkParams.MaxChi2ClusterAttachment, trkParams.MaxChi2NDF,
1370 propagator, trkParams.CorrType, trkParams.ShiftRefToCluster, trkParams.RepeatRefitOut};
1371 TrackITSInternal<NLayers> internalTrack;
1372 if (!track::refitTrackSeed<NLayers>(seed,
1373 internalTrack,
1374 fitCtx,
1375 unsortedClusters,
1376 trkParams.LayerRadii.data(),
1377 trkParams.MinPt.data(),
1378 trkParams.ReseedIfShorter)) {
1379 return false;
1380 }
1381 const auto passesFinalLengthCut = [&trkParams](const TrackITSExt& candidate) {
1382 LayerMask hitLayerMask{0};
1383 for (int iLayer{0}; iLayer < trkParams.NLayers; ++iLayer) {
1384 if (candidate.getClusterIndex(iLayer) != constants::UnusedIndex) {
1385 hitLayerMask.set(iLayer);
1386 }
1387 }
1388 return track::TrackSeedSelector<NLayers>::getEffectiveTrackLength(hitLayerMask, trkParams.InactiveLayerMask) >= trkParams.MinTrackLength;
1389 };
1390
1391 const bool extendTop = trkParams.PassFlags[IterationStep::TrackFollowerTop];
1392 const bool extendBot = trkParams.PassFlags[IterationStep::TrackFollowerBot];
1393 if (!extendTop && !extendBot) {
1394 track = makeTrackITSExt(internalTrack);
1395 return passesFinalLengthCut(track);
1396 }
1397
1398 if (static_cast<int>(scratch.activeHypotheses.size()) < followCtx.maxHypotheses) {
1399 scratch.activeHypotheses.resize(followCtx.maxHypotheses);
1400 }
1401 if (static_cast<int>(scratch.nextHypotheses.size()) < followCtx.maxHypotheses) {
1402 scratch.nextHypotheses.resize(followCtx.maxHypotheses);
1403 }
1404
1405 const auto backup = internalTrack;
1406 auto best = internalTrack;
1407 uint32_t bestDiff{0};
1408 auto followDirection = [&](TrackITSInternal<NLayers>& candidate, bool outward) {
1409 const TrackExtensionHypothesis<NLayers> startHypothesis{candidate, outward};
1411 if (!followTrackExtensionDirection<NLayers>(startHypothesis, fitCtx, followCtx, outward,
1412 scratch.activeHypotheses.data(),
1413 scratch.nextHypotheses.data(),
1414 bestHypothesis)) {
1415 return false;
1416 }
1417 updateTrackFromExtensionHypothesis(bestHypothesis, outward, trkParams.NLayers, candidate);
1418 return true;
1419 };
1420 TrackExtensionBestTrial<NLayers> bestTrial{backup.getPattern(), fitCtx};
1421 followTrackExtensionBranches(backup, extendTop, extendBot, trkParams.NLayers, followDirection, bestTrial, best, bestDiff);
1422
1423 track = makeTrackITSExt(best);
1424 if (bestDiff) {
1425 track.setExtendedLayerPattern<NLayers>(bestDiff);
1426 }
1427 return passesFinalLengthCut(track);
1428}
1429
1430template <int NLayers>
1431void TrackerTraits<NLayers>::findRoads(const int iteration)
1432{
1433 bounded_vector<bounded_vector<int>> firstClusters(mTrkParams[iteration].NLayers, bounded_vector<int>(mMemoryPool.get()), mMemoryPool.get());
1434 firstClusters.resize(mTrkParams[iteration].NLayers);
1435 const auto propagator = o2::base::Propagator::Instance();
1436 const TrackingFrameInfo* tfInfos[NLayers]{};
1437 const Cluster* unsortedClusters[NLayers]{};
1438 for (int iLayer = 0; iLayer < NLayers; ++iLayer) {
1439 tfInfos[iLayer] = mTimeFrame->getTrackingFrameInfoOnLayer(iLayer).data();
1440 unsortedClusters[iLayer] = mTimeFrame->getUnsortedClusters()[iLayer].data();
1441 }
1442 const auto topology = mTimeFrame->getTrackingTopologyView();
1443 tbb::enumerable_thread_specific<TrackFollowerScratch> followerScratch{
1444 [mr = mMemoryPool.get()]() { return TrackFollowerScratch{mr}; }};
1445 for (int startLevel{mTrkParams[iteration].CellsPerRoad()}; startLevel >= mTrkParams[iteration].CellMinimumLevel(); --startLevel) {
1446
1447 const track::TrackSeedSelector<NLayers> seedFilter{constants::MaxTrackSeedQ2Pt, mTrkParams[iteration].MaxChi2NDF, startLevel, mTrkParams[iteration].MaxHoles, mTrkParams[iteration].getMinSeedingClusters(), mTrkParams[iteration].HoleLayerMask, mTrkParams[iteration].getNonSeedingLayerMask()};
1448
1449 bounded_vector<TrackSeedN> trackSeeds(mMemoryPool.get());
1450 for (int startCellTopologyId{0}; startCellTopologyId < topology.nCells; ++startCellTopologyId) {
1451 const int startLayer = topology.getCell(startCellTopologyId).hitLayerMask.last();
1452 if (!(mTrkParams[iteration].StartLayerMask.has(startLayer)) ||
1453 mTimeFrame->getCells()[startCellTopologyId].empty() ||
1454 topology.getMaxCellLevel(startCellTopologyId) < startLevel) {
1455 continue;
1456 }
1457
1458 bounded_vector<RoadSeedN> lastSeeds(mMemoryPool.get()), updatedSeeds(mMemoryPool.get());
1459
1460 auto roadKey = [&](int level) {
1461 return CapacityEstimator::makeKey(SlabSite::Roads, iteration, CapacityEstimator::makeVariant(startLevel, level), startCellTopologyId);
1462 };
1463
1464 processNeighbours(iteration, startCellTopologyId, startLevel, roadKey(startLevel), mTimeFrame->getCells()[startCellTopologyId], updatedSeeds);
1465
1466 int level = startLevel;
1467 while (level > 2 && !updatedSeeds.empty()) {
1468 lastSeeds.swap(updatedSeeds);
1469 deepVectorClear(updatedSeeds);
1470 --level;
1471 processNeighbours(iteration, constants::UnusedIndex, level, roadKey(level), lastSeeds, updatedSeeds);
1472 }
1473 deepVectorClear(lastSeeds);
1474
1475 if (!updatedSeeds.empty()) {
1476 trackSeeds.reserve(trackSeeds.size() + std::count_if(updatedSeeds.begin(), updatedSeeds.end(), [&](const auto& road) { return seedFilter(road.seed); }));
1477 for (auto& road : updatedSeeds) {
1478 if (seedFilter(road.seed)) {
1479 trackSeeds.emplace_back(std::move(road.seed));
1480 }
1481 }
1482 }
1483 }
1484
1485 if (trackSeeds.empty()) {
1486 continue;
1487 }
1488
1489 const Cluster* clustersPtrs[NLayers]{};
1490 const unsigned char* usedClustersPtrs[NLayers]{};
1491 const int* clustersIndexTablesPtrs[NLayers]{};
1492 const int* rofClustersPtrs[NLayers]{};
1493 for (int iLayer{0}; iLayer < NLayers; ++iLayer) {
1494 clustersPtrs[iLayer] = mTimeFrame->getClusters()[iLayer].data();
1495 usedClustersPtrs[iLayer] = mTimeFrame->getUsedClusters(iLayer).data();
1496 clustersIndexTablesPtrs[iLayer] = mTimeFrame->getIndexTable(0, iLayer).data();
1497 rofClustersPtrs[iLayer] = mTimeFrame->getROFrameClusters(iLayer).data();
1498 }
1499 const TrackFollowContext<NLayers> followCtx{
1500 &mTimeFrame->getIndexTableUtils(),
1501 mTimeFrame->getROFMaskView(),
1502 mTimeFrame->getROFOverlapTableView(),
1503 clustersPtrs, usedClustersPtrs, clustersIndexTablesPtrs, rofClustersPtrs,
1504 mTrkParams[iteration].LayerRadii.data(), mTrkParams[iteration].PhiBins,
1505 std::max(1, mTrkParams[iteration].TrackFollowerMaxHypotheses),
1506 mTrkParams[iteration].TrackFollowerNSigmaCutPhi, mTrkParams[iteration].TrackFollowerNSigmaCutZ};
1507
1508 bounded_vector<TrackITSExt> tracks(mMemoryPool.get());
1509 mTaskArena->execute([&] {
1510 const int nSeeds = static_cast<int>(trackSeeds.size());
1511 const int maxConcurrency = std::max(1, mTaskArena->max_concurrency());
1512 const int chunkSize = std::min(nSeeds, std::clamp(nSeeds / (constants::NumberOfConcurrentSeeds * maxConcurrency), constants::MinNumberOfConcurrentSeeds, constants::MaxNumberOfConcurrentSeeds)); // acts as memory bound and minimum work
1513
1514 // flush local track vector to global vector on reaching chunkSize
1515 std::mutex tracksMutex;
1516 auto flushTracks = [&](bounded_vector<TrackITSExt>& localTracks) {
1517 if (localTracks.empty()) {
1518 return;
1519 }
1520 std::lock_guard lock{tracksMutex};
1521 tracks.insert(tracks.end(), std::make_move_iterator(localTracks.begin()), std::make_move_iterator(localTracks.end()));
1522 localTracks.clear();
1523 };
1524
1525 // each worker works on its own range
1526 tbb::parallel_for(tbb::blocked_range<int>(0, nSeeds, chunkSize), [&](const auto& range) {
1527 bounded_vector<TrackITSExt> localTracks(mMemoryPool.get());
1528 localTracks.reserve(std::min(chunkSize, static_cast<int>(range.size())));
1529 auto& scratch = followerScratch.local();
1530 for (int iSeed{range.begin()}; iSeed < range.end(); ++iSeed) {
1531 localTracks.emplace_back();
1532 if (!finaliseTrackSeed(trackSeeds[iSeed], localTracks.back(), iteration, tfInfos, unsortedClusters, propagator, followCtx, scratch)) {
1533 localTracks.pop_back();
1534 }
1535 if (static_cast<int>(localTracks.size()) == chunkSize) {
1536 flushTracks(localTracks);
1537 }
1538 }
1539 flushTracks(localTracks); // flush remaining
1540 deepVectorClear(localTracks);
1541 });
1542
1543 deepVectorClear(trackSeeds);
1544 });
1545
1546 // Sort tracks via indices to avoid moving TrackITSExt objects.
1547 bounded_vector<int> trackIndices(tracks.size(), mMemoryPool.get());
1548 std::iota(trackIndices.begin(), trackIndices.end(), 0);
1549 std::sort(trackIndices.begin(), trackIndices.end(), [&tracks](int a, int b) {
1550 return track::isBetter(tracks[a], tracks[b]);
1551 });
1552
1553 acceptTracks(iteration, tracks, trackIndices, firstClusters);
1554 }
1555 markTracks(iteration);
1556}
1557
1558template <int NLayers>
1560 bounded_vector<TrackITSExt>& tracks,
1561 const bounded_vector<int>& trackIndices,
1562 bounded_vector<bounded_vector<int>>& firstClusters)
1563{
1564 auto& trks = mTimeFrame->getTracks();
1565 trks.reserve(trks.size() + tracks.size());
1566 const float smallestROFHalf = mTimeFrame->getROFOverlapTableView().getClockLayer().mROFLength * 0.5f;
1567 for (size_t trackId{0}; trackId < trackIndices.size(); ++trackId) {
1568 auto& track = tracks[trackIndices[trackId]];
1569 int nShared = 0;
1570 bool isFirstShared{false};
1571 int firstLayer{-1}, firstCluster{-1};
1572 for (int iLayer{0}; iLayer < mTrkParams[iteration].NLayers; ++iLayer) {
1573 if (track.getClusterIndex(iLayer) == constants::UnusedIndex) {
1574 continue;
1575 }
1576 bool isShared = mTimeFrame->isClusterUsed(iLayer, track.getClusterIndex(iLayer));
1577 nShared += int(isShared);
1578 if (firstLayer < 0) {
1579 firstCluster = track.getClusterIndex(iLayer);
1580 isFirstShared = isShared && mTrkParams[iteration].AllowSharingFirstCluster && std::find(firstClusters[iLayer].begin(), firstClusters[iLayer].end(), firstCluster) != firstClusters[iLayer].end();
1581 firstLayer = iLayer;
1582 }
1583 }
1584
1586 if (nShared - int(isFirstShared && mTrkParams[iteration].AllowSharingFirstCluster) > mTrkParams[iteration].SharedMaxClusters) {
1587 continue;
1588 }
1589
1590 bool firstCls{true}, nominalCompatible{true};
1591 TimeEstBC nominalTS, expandedTS;
1592 for (int iLayer{0}; iLayer < mTrkParams[iteration].NLayers; ++iLayer) {
1593 if (track.getClusterIndex(iLayer) == constants::UnusedIndex) {
1594 continue;
1595 }
1596 mTimeFrame->markUsedCluster(iLayer, track.getClusterIndex(iLayer));
1597 int currentROF = mTimeFrame->getClusterROF(iLayer, track.getClusterIndex(iLayer));
1598 const auto nominalROFTS = mTimeFrame->getROFOverlapTableView().getLayer(iLayer).getROFTimeBounds(currentROF);
1599 const auto expandedROFTS = mTimeFrame->getROFOverlapTableView().getLayer(iLayer).getROFTimeBounds(currentROF, true);
1600 if (firstCls) {
1601 firstCls = false;
1602 nominalTS = nominalROFTS;
1603 expandedTS = expandedROFTS;
1604 } else {
1605 if (nominalCompatible) {
1606 if (nominalTS.isCompatible(nominalROFTS)) {
1607 nominalTS += nominalROFTS;
1608 } else {
1609 nominalCompatible = false;
1610 }
1611 }
1612 if (!expandedTS.isCompatible(expandedROFTS)) {
1613 LOGP(fatal, "TS {}+/-{} are incompatible with {}+/-{}, this should not happen!", expandedROFTS.getTimeStamp(), expandedROFTS.getTimeStampError(), expandedTS.getTimeStamp(), expandedTS.getTimeStampError());
1614 }
1615 expandedTS += expandedROFTS;
1616 }
1617 }
1618 track.getTimeStamp() = (nominalCompatible ? nominalTS : expandedTS).makeSymmetrical();
1619 // this is a sanity clamp
1620 // we cannot be worse than the clock so we clamp to this
1621 if (track.getTimeStamp().getTimeStampError() > smallestROFHalf) {
1622 track.getTimeStamp().setTimeStampError(smallestROFHalf);
1623 }
1624 const auto diff = track.getExtendedLayerPattern<NLayers>();
1625 if (diff) {
1626 size_t nExtendedClusters = 0;
1627 for (int iLayer{0}; iLayer < mTrkParams[iteration].NLayers; ++iLayer) {
1628 nExtendedClusters += static_cast<bool>(diff & (0x1u << iLayer));
1629 }
1630 mTimeFrame->addTrackExtensionCounters(1, nExtendedClusters);
1631 }
1632 track.clearExtendedLayerPattern();
1633 trks.emplace_back(track);
1634
1635 if (mTrkParams[iteration].AllowSharingFirstCluster) {
1636 firstClusters[firstLayer].push_back(firstCluster);
1637 }
1638 }
1639}
1640
1641template <int NLayers>
1643{
1644 if (mTrkParams[iteration].AllowSharingFirstCluster) {
1646 auto& tracks = mTimeFrame->getTracks();
1647
1648 bounded_vector<int> fclusSort(tracks.size(), mMemoryPool.get());
1649 std::iota(fclusSort.begin(), fclusSort.end(), 0);
1650 std::sort(fclusSort.begin(), fclusSort.end(), [&tracks](int a, int b) {
1651 return tracks[a].getFirstLayerClusterIndex() < tracks[b].getFirstLayerClusterIndex();
1652 });
1653
1654 auto areTracksSelected = [this, iteration](const TrackITSExt& t1, const TrackITSExt& t2) {
1655 const auto t1FirstLayer{t1.getFirstClusterLayer()}, t2FirstLayer{t2.getFirstClusterLayer()};
1656 if (t1FirstLayer != t2FirstLayer) {
1657 return false;
1658 }
1659 if (mTimeFrame->getClusterROF(t1FirstLayer, t1.getClusterIndex(t1FirstLayer)) != mTimeFrame->getClusterROF(t2FirstLayer, t2.getClusterIndex(t2FirstLayer))) {
1660 return false;
1661 }
1662 if (!math_utils::isPhiDifferenceBelow(t1.getPhi(), t2.getPhi(), mTrkParams[iteration].SharedClusterMaxDeltaPhi)) {
1663 return false;
1664 }
1665 if (std::abs(t1.getEta() - t2.getEta()) > mTrkParams[iteration].SharedClusterMaxDeltaEta) {
1666 return false;
1667 }
1668 if (mTrkParams[iteration].SharedClusterOppositeSign && t1.getSign() == t2.getSign()) {
1669 return false;
1670 }
1671 return true;
1672 };
1673
1674 for (int i{0}; i < static_cast<int>(fclusSort.size()); ++i) {
1675 auto& track = tracks[fclusSort[i]];
1676 for (int j{i + 1}; j < static_cast<int>(fclusSort.size()) && tracks[fclusSort[j]].getFirstLayerClusterIndex() == track.getFirstLayerClusterIndex(); ++j) {
1677 auto& track2 = tracks[fclusSort[j]];
1678 if (areTracksSelected(track, track2)) {
1679 track.setSharedClusters();
1680 track2.setSharedClusters();
1681 }
1682 }
1683 }
1684 }
1685}
1686
1687template <int NLayers>
1689{
1690 mBz = bz;
1691 mTimeFrame->setBz(bz);
1692}
1693
1694template <int NLayers>
1695void TrackerTraits<NLayers>::setNThreads(int n, std::shared_ptr<tbb::task_arena>& arena)
1696{
1697#if defined(OPTIMISATION_OUTPUT)
1698 mTaskArena = std::make_shared<tbb::task_arena>(1);
1699#else
1700 if (arena == nullptr) {
1701 mTaskArena = std::make_shared<tbb::task_arena>(std::abs(n));
1702 LOGP(info, "Setting tracker with {} threads.", n);
1703 } else {
1704 mTaskArena = arena;
1705 }
1706#endif
1707}
1708
1709template class TrackerTraits<7>;
1710template void TrackerTraits<7>::processNeighbours<CellSeed>(int, int, int, uint64_t, const bounded_vector<CellSeed>&, bounded_vector<RoadSeed<7>>&);
1711template void TrackerTraits<7>::processNeighbours<RoadSeed<7>>(int, int, int, uint64_t, const bounded_vector<RoadSeed<7>>&, bounded_vector<RoadSeed<7>>&);
1712// ALICE3 upgrade
1713#ifdef ENABLE_UPGRADES
1714template class TrackerTraits<11>;
1715template void TrackerTraits<11>::processNeighbours<CellSeed>(int, int, int, uint64_t, const bounded_vector<CellSeed>&, bounded_vector<RoadSeed<11>>&);
1716template void TrackerTraits<11>::processNeighbours<RoadSeed<11>>(int, int, int, uint64_t, const bounded_vector<RoadSeed<11>>&, bounded_vector<RoadSeed<11>>&);
1717template class TrackerTraits<13>;
1718template void TrackerTraits<13>::processNeighbours<CellSeed>(int, int, int, uint64_t, const bounded_vector<CellSeed>&, bounded_vector<RoadSeed<13>>&);
1719template void TrackerTraits<13>::processNeighbours<RoadSeed<13>>(int, int, int, uint64_t, const bounded_vector<RoadSeed<13>>&, bounded_vector<RoadSeed<13>>&);
1720#endif
1721
1722} // namespace o2::its
header::DataOrigin origin
uint64_t vertex
Definition RawEventData.h:9
int32_t i
int32_t lastRow
float chi2
float phiCut
Definition Tracker.cxx:213
float msAngle
Definition Tracker.cxx:212
std::vector< o2::MCCompLabel > labels
std::vector< o2::its::TrackITS > tracks
small types shared by the host and device seeding vertexers, describing lines projected onto the beam...
bounded_vector< int > members
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.
Utility functions for vertex handling.
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:180
Least-squares vertex fit over a set of lines (the normal equations AX = -B).
TrackSeed< NLayers > TrackSeedN
virtual void initialiseTimeFrame(const int iteration)
virtual void computeLayerTracklets(const int iteration, int iVertex)
GLdouble n
Definition glcorearb.h:1982
const GLfloat * m
Definition glcorearb.h:4066
GLint GLsizei count
Definition glcorearb.h:399
GLuint GLuint end
Definition glcorearb.h:469
const GLdouble * v
Definition glcorearb.h:832
GLint first
Definition glcorearb.h:399
GLboolean GLboolean GLboolean b
Definition glcorearb.h:1233
GLenum GLint * range
Definition glcorearb.h:1899
GLboolean * data
Definition glcorearb.h:298
GLuint GLsizei const GLchar * label
Definition glcorearb.h:2519
GLsizei GLenum const void * indices
Definition glcorearb.h:400
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
GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat maxZ
Definition glcorearb.h:2910
GLubyte GLubyte GLubyte GLubyte w
Definition glcorearb.h:852
GLint GLuint mask
Definition glcorearb.h:291
GLdouble GLdouble GLdouble z
Definition glcorearb.h:843
GLuint GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat t1
Definition glcorearb.h:5034
uint8_t int statusCode int
constexpr float Tolerance
Definition Constants.h:30
o2::its::LineWindow LineWindow
const bool const bool const int FollowDirection BestTrial TrackITSInternal< NLayers > uint32_t & bestDiff
const float beamX
const bool outward
const bool const bool const int FollowDirection BestTrial TrackITSInternal< NLayers > & best
const int end
void pruneOverpopulatedRofs(std::vector< VtxVec > &rofVertices, std::vector< LabVec > &rofLabels, const float nSigma, const int minContributors)
Caps ROFs whose vertex count is an outlier of the TF.
Definition VertexUtils.h:81
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)
float getDebrisThreshold(const float goodSig, const double rofLoad, const int suppressLowMultDebris)
Definition VertexUtils.h:70
const bool const bool const int FollowDirection BestTrial & bestTrial
const float const float beamY
const float const float const float bz
const float const float const float const float std::array< float, 3 > std::array< float, 3 > & direction
const track::TrackFitContext< NLayers > & fit
const bool const bool const int FollowDirection & followDirection
VertexLabel computeMainVertexLabel(const Container &elements)
Definition VertexUtils.h:37
Vertex makeDiamondVertex(const TrackingParameters &trkParam)
Definition VertexUtils.h:61
void deepVectorClear(std::vector< T > &vec)
Enum< T >::Iterator begin(Enum< T >)
Definition Defs.h:158
void empty(int)
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
TStopwatch sw
std::vector< Cell > cells
std::vector< Tracklet64 > tracklets