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);
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);
76 auto forTracklets = [&](
int linkId,
int pivotROF,
auto&& emit) {
77 const auto&
link = topology.getLink(linkId);
78 if (!mTimeFrame->getROFMaskView().isROFEnabled(
link.fromLayer, pivotROF)) {
81 gsl::span<const Vertex> primaryVertices = mTrkParams[iteration].UseDiamond ? diamondSpan : mTimeFrame->getPrimaryVertices(
link.fromLayer, pivotROF);
82 if (primaryVertices.empty()) {
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()) {
91 const auto& rofOverlap = mTimeFrame->getROFOverlapTableView().getOverlap(
link.fromLayer,
link.toLayer, pivotROF);
92 if (!rofOverlap.getEntries()) {
96 auto layer0 = mTimeFrame->getClustersOnLayer(pivotROF,
link.fromLayer);
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);
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)) {
111 const float inverseR0 = 1.f / currentCluster.
radius;
113 for (
int iV = startVtx; iV < endVtx; ++iV) {
114 const auto& pv = primaryVertices[iV];
115 if (!mTimeFrame->getROFVertexLookupTableView().isVertexCompatible(
link.fromLayer, pivotROF, pv)) {
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;
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));
128 sigmaZ * mTrkParams[iteration].NSigmaCut, phiCut,
129 mTimeFrame->getIndexTableUtils());
133 int phiBinsNum =
bins.w -
bins.y + 1;
134 if (phiBinsNum < 0) {
135 phiBinsNum += mTrkParams[iteration].PhiBins;
138 for (
int targetROF = rofOverlap.getFirstEntry(); targetROF < rofOverlap.getEntriesBound(); ++targetROF) {
139 if (!mTimeFrame->getROFMaskView().isROFEnabled(
link.toLayer, targetROF)) {
142 auto layer1 = mTimeFrame->getClustersOnLayer(targetROF,
link.toLayer);
143 if (layer1.empty()) {
146 const auto ts = mTimeFrame->getROFOverlapTableView().getTimeStamp(
link.fromLayer, pivotROF,
link.toLayer, targetROF);
147 if (!ts.isCompatible(pv.getTimeStamp())) {
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())) {
162 const Cluster& nextCluster = layer1[iNext];
163 if (mTimeFrame->isClusterUsed(
link.toLayer, nextCluster.
clusterId)) {
168 if (deltaZ / sigmaZ < mTrkParams[iteration].NSigmaCut &&
169 math_utils::isPhiDifferenceBelow(currentCluster.
phi, nextCluster.
phi, phiCut)) {
172 emit(currentSortedIndex, mTimeFrame->getSortedIndex(targetROF,
link.toLayer, iNext), tanL, phi, ts);
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)...); });
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);
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)...); });
206 const auto st = sink.stats();
208 mTimeFrame->getCapacityEstimator().update(
key, scale,
st.requested,
st.capacity,
st.emitted,
st.spilled,
209 st.overflowed,
st.memoryLimited);
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());
219 std::sort(trkl.begin(), trkl.end());
222 trkl.erase(std::unique(trkl.begin(), trkl.end()), trkl.end());
223 trkl.shrink_to_fit();
225 auto& lut{mTimeFrame->getTrackletsLookupTable()[linkId]};
227 const size_t nTracklets{trkl.size()};
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)) {
235 while (
end > 0 &&
end < nTracklets && sameRun(
end,
end - 1)) {
238 for (
size_t i{begin};
i <
end; ++
i) {
239 ++lut[tkls[
i].firstClusterIndex + 1];
242 int*
data{lut.data()};
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) {
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]) {
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()) {
273 if (label.isValid()) {
277 mTimeFrame->getTrackletsLabel(linkId).emplace_back(label);
287 const auto topology = mTimeFrame->getTrackingTopologyView();
288 const bool createLabels = mTimeFrame->hasMCinformation() && mTrkParams[iteration].CreateArtefactLabels;
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]);
296 deepVectorClear(mTimeFrame->getCellsLabel(cellTopologyId));
299 if (maxConcurrency > 1) {
300 tbb::parallel_for(0,
static_cast<int>(topology.nCells), clearTopology);
302 for (
int cellTopologyId{0}; cellTopologyId < topology.nCells; ++cellTopologyId) {
303 clearTopology(cellTopologyId);
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]};
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) {
320 if (!currentTracklet.getTimeStamp().isCompatible(nextTracklet.getTimeStamp())) {
324 const float deltaTanLambdaSigma = std::abs(currentTracklet.tanLambda - nextTracklet.tanLambda) / mTrkParams[iteration].CellDeltaTanLambdaSigma;
325 if (deltaTanLambdaSigma < mTrkParams[iteration].NSigmaCut) {
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)};
340 for (
int iC{2}; iC--;) {
341 const int hitLayer = hitLayers[iC];
342 const TrackingFrameInfo& trackingHit = mTimeFrame->getTrackingFrameInfoOnLayer(hitLayer)[clusId[iC]];
352 if (!
track.correctForMaterial(mTrkParams[iteration].LayerxX0[hitLayer], mTrkParams[iteration].LayerxX0[hitLayer] * constants::Radl * constants::Rho,
true)) {
357 if (!iC && predChi2 > mTrkParams[iteration].MaxChi2ClusterAttachment) {
369 TimeEstBC ts = currentTracklet.getTimeStamp();
370 ts += nextTracklet.getTimeStamp();
371 emit(cellTopology.hitLayerMask, clusId[0], clusId[1], clusId[2], iTracklet, iNextTracklet,
track, chi2, ts);
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);
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);
391 auto& layerCells = mTimeFrame->getCells()[cellTopologyId];
392 auto& lut = mTimeFrame->getCellsLookupTable()[cellTopologyId];
393 const int currentLayerTrackletsNum{
static_cast<int>(mTimeFrame->getTracklets()[cellTopology.firstLink].size())};
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);
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)...); });
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);
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)...);
418 lut.back() =
static_cast<int>(layerCells.size());
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()]};
432 if (maxConcurrency > 1) {
433 tbb::parallel_for(0,
static_cast<int>(activeTopologies.size()), [&](
const int i) {
434 processTopology(activeTopologies[i]);
437 for (
const int cellTopologyId : activeTopologies) {
438 processTopology(cellTopologyId);
442 auto clearTracklets = [&](
const int linkId) {
443 deepVectorClear(mTimeFrame->getTracklets()[linkId]);
444 deepVectorClear(mTimeFrame->getTrackletsLabel(linkId));
446 if (maxConcurrency > 1) {
447 tbb::parallel_for(0,
static_cast<int>(topology.nLinks), clearTracklets);
449 for (
int linkId{0}; linkId < topology.nLinks; ++linkId) {
450 clearTracklets(linkId);
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]);
467 if (maxConcurrency > 1) {
468 tbb::parallel_for(0,
static_cast<int>(topology.nCells), clearNeighbours);
470 for (
int cellTopologyId{0}; cellTopologyId < topology.nCells; ++cellTopologyId) {
471 clearNeighbours(cellTopologyId);
476 return std::tie(
a.nextCellTopology,
a.nextCell,
a.cellTopology,
a.cell) <
477 std::tie(
b.nextCellTopology,
b.nextCell,
b.cellTopology,
b.cell);
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()) {
490 const auto successors = topology.getCellsStartingWithLink(cellTopology.secondLink);
491 if (!successors.getEntries()) {
494 activeTopologies.push_back(cellTopologyId);
495 sourceCellCount += mTimeFrame->getCells()[cellTopologyId].size();
498 if (activeTopologies.empty()) {
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()) {
513 const auto& nextCellLUT = mTimeFrame->getCellsLookupTable()[nextCellTopologyId];
514 if (nextLayerTrackletIndex + 1 >=
static_cast<int>(nextCellLUT.size())) {
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())) {
525 auto nextCellSeed{mTimeFrame->getCells()[nextCellTopologyId][iNextCell]};
526 if (!nextCellSeed.rotate(currentCellSeed.getAlpha()) ||
527 !nextCellSeed.propagateTo(currentCellSeed.getX(), getBz())) {
531 float chi2 = currentCellSeed.getPredictedChi2Fast(nextCellSeed);
532 if (chi2 > mTrkParams[iteration].MaxChi2ClusterAttachment) {
536 const int nextLevel = currentCellSeed.getLevel() + 1;
537 emit(cellTopologyId, iCell, nextCellTopologyId, iNextCell, nextLevel);
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)...);
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);
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)...);
570 std::sort(waveNeighbours.begin(), waveNeighbours.end(), neighbourLess);
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; });
586 targetSpans.push_back({targetTopologyId,
static_cast<size_t>(
first - waveNeighbours.begin()),
static_cast<size_t>(last - waveNeighbours.begin())});
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);
602 std::inclusive_scan(cellsNeighbourLUT.begin(), cellsNeighbourLUT.end(), cellsNeighbourLUT.begin());
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;
613 if (maxConcurrency > 1) {
614 tbb::parallel_for(0,
static_cast<int>(targetSpans.size()), finalizeTarget);
616 for (
int i{0}; i < static_cast<int>(targetSpans.size()); ++
i) {
623 auto clearCellLUT = [&](
const int cellTopologyId) {
624 deepVectorClear(mTimeFrame->getCellsLookupTable()[cellTopologyId]);
626 if (maxConcurrency > 1) {
627 tbb::parallel_for(0,
static_cast<int>(topology.nCells), clearCellLUT);
629 for (
int cellTopologyId{0}; cellTopologyId < topology.nCells; ++cellTopologyId) {
630 clearCellLUT(cellTopologyId);
640 constexpr bool IsInitial = std::is_same_v<InputSeed, CellSeed>;
641 static_assert(IsInitial || std::is_same_v<InputSeed, RoadSeedN>);
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) {
651 return inputSeed.seed;
654 const int cellTopologyId = [&]() {
655 if constexpr (IsInitial) {
656 return defaultCellTopologyId;
658 return inputSeed.cellTopologyId;
661 const int cellId = [&]() {
662 if constexpr (IsInitial) {
665 return inputSeed.cellId;
669 if (currentCell.getLevel() != iLevel) {
672 if constexpr (IsInitial) {
674 const int clusterIndex = currentCell.getCluster(
layer);
675 if (clusterIndex != constants::UnusedIndex && mTimeFrame->isClusterUsed(
layer, clusterIndex)) {
681 if (cellTopologyId < 0 || mTimeFrame->getCellsNeighboursLUT()[cellTopologyId].empty()) {
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()) {
693 if (!currentCell.getTimeStamp().isCompatible(neighbourCell.getTimeStamp())) {
696 if (currentCell.getLevel() - 1 != neighbourCell.getLevel()) {
699 const int neighbourLayer = neighbourCell.getInnerLayer();
700 const int neighbourCluster = neighbourCell.getFirstClusterIndex();
701 if (mTimeFrame->isClusterUsed(neighbourLayer, neighbourCluster)) {
707 seed.getTimeStamp() = currentCell.getTimeStamp();
708 seed.getTimeStamp() += neighbourCell.getTimeStamp();
709 const auto& trHit = mTimeFrame->getTrackingFrameInfoOnLayer(neighbourLayer)[neighbourCluster];
711 if (!seed.rotate(trHit.alphaTrackingFrame)) {
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)) {
725 auto predChi2{seed.getPredictedChi2Quiet(trHit.positionTrackingFrame, trHit.covarianceTrackingFrame)};
726 if ((predChi2 > mTrkParams[iteration].MaxChi2ClusterAttachment) || predChi2 < 0.f) {
729 seed.setChi2(seed.getChi2() + predChi2);
730 if (!seed.o2::track::TrackParCov::update(trHit.positionTrackingFrame, trHit.covarianceTrackingFrame)) {
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);
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)...); });
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()};
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)...); });
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);
844 bounded_vector<bounded_vector<int>> firstClusters(mTrkParams[iteration].NLayers, bounded_vector<int>(mMemoryPool.get()), mMemoryPool.get());
845 firstClusters.resize(mTrkParams[iteration].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();
853 const auto topology = mTimeFrame->getTrackingTopologyView();
854 tbb::enumerable_thread_specific<TrackFollowerScratch> followerScratch{
856 for (
int startLevel{mTrkParams[iteration].CellsPerRoad()}; startLevel >= mTrkParams[iteration].CellMinimumLevel(); --startLevel) {
858 const track::TrackSeedSelector<NLayers> seedFilter{constants::MaxTrackSeedQ2Pt, mTrkParams[iteration].MaxChi2NDF, startLevel, mTrkParams[iteration].MaxHoles, mTrkParams[iteration].getMinSeedingClusters(), mTrkParams[iteration].HoleLayerMask, mTrkParams[iteration].getNonSeedingLayerMask()};
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) {
869 bounded_vector<RoadSeedN> lastSeeds(mMemoryPool.get()), updatedSeeds(mMemoryPool.get());
871 auto roadKey = [&](
int level) {
872 return CapacityEstimator::makeKey(SlabSite::Roads, iteration, CapacityEstimator::makeVariant(startLevel,
level), startCellTopologyId);
875 processNeighbours(iteration, startCellTopologyId, startLevel, roadKey(startLevel), mTimeFrame->getCells()[startCellTopologyId], updatedSeeds);
877 int level = startLevel;
878 while (
level > 2 && !updatedSeeds.empty()) {
879 lastSeeds.swap(updatedSeeds);
880 deepVectorClear(updatedSeeds);
882 processNeighbours(iteration, constants::UnusedIndex,
level, roadKey(
level), lastSeeds, updatedSeeds);
884 deepVectorClear(lastSeeds);
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));
896 if (trackSeeds.empty()) {
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();
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};
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));
926 std::mutex tracksMutex;
927 auto flushTracks = [&](bounded_vector<TrackITSExt>& localTracks) {
928 if (localTracks.empty()) {
931 std::lock_guard lock{tracksMutex};
932 tracks.insert(tracks.end(), std::make_move_iterator(localTracks.begin()), std::make_move_iterator(localTracks.end()));
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();
946 if (
static_cast<int>(localTracks.size()) == chunkSize) {
947 flushTracks(localTracks);
950 flushTracks(localTracks);
951 deepVectorClear(localTracks);
954 deepVectorClear(trackSeeds);
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]);
964 acceptTracks(iteration, tracks, trackIndices, firstClusters);
966 markTracks(iteration);
971 bounded_vector<TrackITSExt>& tracks,
972 const bounded_vector<int>& trackIndices,
973 bounded_vector<bounded_vector<int>>& firstClusters)
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]];
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) {
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();
997 if (nShared -
int(isFirstShared && mTrkParams[iteration].AllowSharingFirstCluster) > mTrkParams[iteration].SharedMaxClusters) {
1001 bool firstCls{
true}, nominalCompatible{
true};
1003 for (
int iLayer{0}; iLayer < mTrkParams[iteration].NLayers; ++iLayer) {
1004 if (
track.getClusterIndex(iLayer) == constants::UnusedIndex) {
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);
1013 nominalTS = nominalROFTS;
1014 expandedTS = expandedROFTS;
1016 if (nominalCompatible) {
1017 if (nominalTS.isCompatible(nominalROFTS)) {
1018 nominalTS += nominalROFTS;
1020 nominalCompatible =
false;
1023 if (!expandedTS.isCompatible(expandedROFTS)) {
1024 LOGP(fatal,
"TS {}+/-{} are incompatible with {}+/-{}, this should not happen!", expandedROFTS.getTimeStamp(), expandedROFTS.getTimeStampError(), expandedTS.getTimeStamp(), expandedTS.getTimeStampError());
1026 expandedTS += expandedROFTS;
1029 track.getTimeStamp() = (nominalCompatible ? nominalTS : expandedTS).makeSymmetrical();
1032 if (
track.getTimeStamp().getTimeStampError() > smallestROFHalf) {
1033 track.getTimeStamp().setTimeStampError(smallestROFHalf);
1035 const auto diff =
track.getExtendedLayerPattern<NLayers>();
1037 size_t nExtendedClusters = 0;
1038 for (
int iLayer{0}; iLayer < mTrkParams[iteration].NLayers; ++iLayer) {
1039 nExtendedClusters +=
static_cast<bool>(diff & (0x1u << iLayer));
1041 mTimeFrame->addTrackExtensionCounters(1, nExtendedClusters);
1043 track.clearExtendedLayerPattern();
1044 trks.emplace_back(
track);
1046 if (mTrkParams[iteration].AllowSharingFirstCluster) {
1047 firstClusters[firstLayer].push_back(firstCluster);
1055 if (mTrkParams[iteration].AllowSharingFirstCluster) {
1057 auto& tracks = mTimeFrame->getTracks();
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();
1066 const auto t1FirstLayer{
t1.getFirstClusterLayer()}, t2FirstLayer{t2.getFirstClusterLayer()};
1067 if (t1FirstLayer != t2FirstLayer) {
1070 if (mTimeFrame->getClusterROF(t1FirstLayer,
t1.getClusterIndex(t1FirstLayer)) != mTimeFrame->getClusterROF(t2FirstLayer, t2.getClusterIndex(t2FirstLayer))) {
1073 if (!math_utils::isPhiDifferenceBelow(
t1.getPhi(), t2.getPhi(), mTrkParams[iteration].SharedClusterMaxDeltaPhi)) {
1076 if (std::abs(
t1.getEta() - t2.getEta()) > mTrkParams[iteration].SharedClusterMaxDeltaEta) {
1079 if (mTrkParams[iteration].SharedClusterOppositeSign &&
t1.getSign() == t2.getSign()) {
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();