74 const auto topology = mTimeFrame->getTrackingTopologyView();
76 const bool useDiamondSpan = mTrkParams[iteration].UseDiamond;
78 gsl::span<const Vertex> diamondSpan(&diamondVert, 1);
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);
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)) {
93 gsl::span<const Vertex> primaryVertices = useDiamondSpan ? diamondSpan : mTimeFrame->getPrimaryVertices(
link.fromLayer, pivotROF);
94 if (primaryVertices.empty()) {
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()) {
103 const auto& rofOverlap = mTimeFrame->getROFOverlapTableView().getOverlap(
link.fromLayer,
link.toLayer, pivotROF);
104 if (!rofOverlap.getEntries()) {
108 auto layer0 = mTimeFrame->getClustersOnLayer(pivotROF,
link.fromLayer);
109 if (layer0.empty()) {
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);
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)) {
123 onProducer(currentSortedIndex);
124 const float inverseR0 = 1.f / currentCluster.
radius;
126 for (
int iV = startVtx; iV < endVtx; ++iV) {
127 const auto& pv = primaryVertices[iV];
128 if (!mTimeFrame->getROFVertexLookupTableView().isVertexCompatible(
link.fromLayer, pivotROF, pv)) {
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;
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));
141 sigmaZ * mTrkParams[iteration].NSigmaCut,
phiCut,
142 mTimeFrame->getIndexTableUtils());
146 int phiBinsNum =
bins.w -
bins.y + 1;
147 if (phiBinsNum < 0) {
148 phiBinsNum += mTrkParams[iteration].PhiBins;
151 for (
int targetROF = rofOverlap.getFirstEntry(); targetROF < rofOverlap.getEntriesBound(); ++targetROF) {
152 if (!mTimeFrame->getROFMaskView().isROFEnabled(
link.toLayer, targetROF)) {
155 auto layer1 = mTimeFrame->getClustersOnLayer(targetROF,
link.toLayer);
156 if (layer1.empty()) {
159 const auto ts = mTimeFrame->getROFOverlapTableView().getTimeStamp(
link.fromLayer, pivotROF,
link.toLayer, targetROF);
160 if (!ts.isCompatible(pv.getTimeStamp())) {
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())) {
175 const Cluster& nextCluster = layer1[iNext];
176 if (mTimeFrame->isClusterUsed(
link.toLayer, nextCluster.
clusterId)) {
181 if (deltaZ / sigmaZ < mTrkParams[iteration].NSigmaCut &&
182 math_utils::isPhiDifferenceBelow(currentCluster.
phi, nextCluster.
phi,
phiCut)) {
185 emit(currentSortedIndex, mTimeFrame->getSortedIndex(targetROF,
link.toLayer, iNext), tanL, phi, ts);
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) {});
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);
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();
223 [&handle](auto&&... args) { handle.emplace(std::forward<decltype(args)>(args)...); },
224 [&handle](int producer) { handle.beginProducer(producer); });
227 sink.finalizeGrouped(nProducers, lut, tracklets);
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();
234 [&handle](
auto&&... args) { handle.emplace(std::forward<
decltype(args)>(args)...); },
240 mTimeFrame->getCapacityEstimator().update(
key, scale,
st.requested,
st.capacity,
st.emitted,
st.spilled,
241 st.overflowed,
st.memoryLimited);
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) {
251 auto& trkl{mTimeFrame->getTracklets()[linkId]};
252 if (mTaskArena->max_concurrency() > 1) {
253 tbb::parallel_sort(trkl.begin(), trkl.end());
255 std::sort(trkl.begin(), trkl.end());
257 if (iVertex < 0 && !mTrkParams[iteration].UseDiamond) {
258 trkl.erase(std::unique(trkl.begin(), trkl.end()), trkl.end());
259 trkl.shrink_to_fit();
261 auto& lut{mTimeFrame->getTrackletsLookupTable()[linkId]};
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) {
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)) {
271 while (
end > 0 &&
end < nTracklets && sameRun(
end,
end - 1)) {
275 ++lut[tkls[
i].firstClusterIndex + 1];
278 int*
data{lut.data()};
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) {
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]) {
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()) {
309 if (label.isValid()) {
313 mTimeFrame->getTrackletsLabel(linkId).emplace_back(label);
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);
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();
356 kCl2[k] = cell.getThirdClusterIndex();
363 kPivotRof[k] = mTimeFrame->getClusterROF(1, c1);
367 kProjOk[k] = den >= constants::Tolerance &&
370 kLabel[k] = cellLabels[k];
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) {
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);
391 bounded_vector<uint8_t> cellAccepted(nCells, 0, mMemoryPool.get());
392 mTaskArena->execute([&] {
393 tbb::parallel_for(0, nCells, [&](
const int k) {
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;
407 for (
int k = 0; k < nCells; ++k) {
408 if (!cellAccepted[k]) {
411 const int pivotRof = kPivotRof[k];
412 mTimeFrame->getLines(pivotRof).emplace_back(kLine[k]);
413 mTimeFrame->getLinesQuality(pivotRof).emplace_back(
416 mTimeFrame->getLinesLabel(pivotRof).emplace_back(kLabel[k]);
420 mTimeFrame->setNLinesTotal(total);
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);
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;
573 const auto processROF = [&](
const int rofId) {
574 if (skipROF(iteration, rofId)) {
577 auto& lines = mTimeFrame->getLines(rofId);
578 const int n =
static_cast<int>(lines.size());
582 const auto lineSpan = gsl::span<const Line>(lines.data(), lines.size());
585 bounded_vector<float> zc(mMemoryPool.get());
586 bounded_vector<TimeEstBC> tc(mMemoryPool.get());
587 bounded_vector<int> lic(mMemoryPool.get());
591 for (
int i = 0;
i <
n; ++
i) {
592 const auto& line = lines[
i];
597 if (!(o2::gpu::CAMath::Abs(
z) <
maxZ)) {
601 tc.push_back(line.mTime);
604 const int m =
static_cast<int>(zc.size());
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) {
619 LI[
i] = lic[order[
i]];
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);
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());
634 markLeftmostMaxima(densityFine, winFine, mMemoryPool.get(), isMaxFine);
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]) {
648 const int np =
static_cast<int>(peaks.size());
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());
666 for (
int j = win[k].lo;
j < win[k].hi; ++
j) {
667 if (T[k].isCompatible(T[
j])) {
676 if (!seed.isValid()) {
680 bounded_vector<int> kept(mMemoryPool.get());
682 for (
const int idx :
members) {
683 if (Line::getDistance2FromPoint(lines[idx], seed.getVertex()) < pairCut2) {
687 if (kept.size() < 2) {
693 nGood =
static_cast<int>(kept.size());
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;
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]) +
707 if (!
fit.isValid() ||
static_cast<int>(
fit.getSize()) < minContributors || !(bd2 < nSigmaCut)) {
711 if (fineMaxDrift > 0.f && fineCand[p] && std::abs(
fit.getVertex()[2] - seed.getVertex()[2]) > fineMaxDrift) {
715 cand[p] = std::move(
fit);
717 candMembers[p] = std::move(kept);
723 bounded_vector<uint8_t> keep(np, mMemoryPool.get());
724 for (
int p = 0; p < np; ++p) {
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))
735 for (
int q = 0; q < np && survive; ++q) {
736 if (q == p || !ok[q]) {
739 if (!cand[p].getTimeStamp().isCompatible(cand[q].getTimeStamp())) {
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)) {
753 bounded_vector<int> accepted(mMemoryPool.get());
754 for (
int p = 0; p < np; ++p) {
756 accepted.push_back(p);
759 std::sort(accepted.begin(), accepted.end(),
760 [&](
int a,
int b) { return cand[a].getSize() > cand[b].getSize(); });
763 for (
int p = 0; p < np; ++p) {
764 if (ok[p] && !fineCand[p]) {
765 rofLoad += cand[p].getSize();
769 const float debrisThreshold = goodSig > 0.f ?
getDebrisThreshold(goodSig, rofLoad, suppressLowMultDebris) : 0.f;
770 for (
const int p : accepted) {
771 if (!rofVertices[rofId].
empty()) {
773 if (nGoodCand[p] < debrisThreshold) {
776 }
else if (
static_cast<int>(cand[p].getSize()) < suppressLowMultDebris) {
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);
788 rofVertices[rofId].push_back(
vertex);
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);
798 labels.push_back(lineLabels[idx]);
805 if (mTaskArena->max_concurrency() <= 1) {
806 for (
int rofId{0}; rofId < nRofs; ++rofId) {
810 mTaskArena->execute([&] {
811 tbb::parallel_for(0, nRofs, [&](
const int rofId) {
816 if (!tp.PassFlags[IterationStep::MarkVerticesAsUPC]) {
819 for (
int rofId{0}; rofId < nRofs; ++rofId) {
820 for (
auto&
vertex : rofVertices[rofId]) {
821 mTimeFrame->addPrimaryVertex(
vertex);
824 for (
auto&
label : rofLabels[rofId]) {
825 mTimeFrame->addPrimaryVertexLabel(
label);
830 auto& pvs = mTimeFrame->getPrimaryVertices();
831 bounded_vector<size_t>
indices(pvs.size(), mMemoryPool.get());
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;
839 return pvs[
i].getNContributors() > pvs[
j].getNContributors();
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]);
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]);
856 mTimeFrame->updateROFVertexLookupTable();
862 const auto topology = mTimeFrame->getTrackingTopologyView();
863 const bool createLabels = mTimeFrame->hasMCinformation() && mTrkParams[iteration].CreateArtefactLabels;
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]);
871 deepVectorClear(mTimeFrame->getCellsLabel(cellTopologyId));
874 if (maxConcurrency > 1) {
875 tbb::parallel_for(0,
static_cast<int>(topology.nCells), clearTopology);
877 for (
int cellTopologyId{0}; cellTopologyId < topology.nCells; ++cellTopologyId) {
878 clearTopology(cellTopologyId);
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];
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]};
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) {
908 if (!currentTracklet.getTimeStamp().isCompatible(nextTracklet.getTimeStamp())) {
911 if (!(std::abs(currentTracklet.tanLambda - nextTracklet.tanLambda) / cellDeltaTanLambdaSigma < cellTanLNSigma)) {
914 if (!math_utils::isPhiDifferenceBelow(currentTracklet.phi, nextTracklet.phi, cellDeltaPhiCut)) {
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)};
930 for (
int iC{2}; iC--;) {
931 const int hitLayer = hitLayers[iC];
932 const TrackingFrameInfo& trackingHit = mTimeFrame->getTrackingFrameInfoOnLayer(hitLayer)[clusId[iC]];
942 if (!
track.correctForMaterial(mTrkParams[iteration].LayerxX0[hitLayer], mTrkParams[iteration].LayerxX0[hitLayer] * constants::Radl * constants::Rho,
true)) {
947 if (!iC && predChi2 > mTrkParams[iteration].MaxChi2ClusterAttachment) {
959 TimeEstBC ts = currentTracklet.getTimeStamp();
960 ts += nextTracklet.getTimeStamp();
961 emit(cellTopology.hitLayerMask, clusId[0], clusId[1], clusId[2], iTracklet, iNextTracklet,
track,
chi2, ts);
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);
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);
980 auto& layerCells = mTimeFrame->getCells()[cellTopologyId];
981 auto& lut = mTimeFrame->getCellsLookupTable()[cellTopologyId];
982 const int currentLayerTrackletsNum{
static_cast<int>(mTimeFrame->getTracklets()[cellTopology.firstLink].size())};
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);
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)...); });
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);
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)...);
1007 lut.back() =
static_cast<int>(layerCells.size());
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()]};
1021 if (maxConcurrency > 1) {
1022 tbb::parallel_for(0,
static_cast<int>(activeTopologies.size()), [&](
const int i) {
1023 processTopology(activeTopologies[i]);
1026 for (
const int cellTopologyId : activeTopologies) {
1027 processTopology(cellTopologyId);
1031 auto clearTracklets = [&](
const int linkId) {
1032 deepVectorClear(mTimeFrame->getTracklets()[linkId]);
1033 deepVectorClear(mTimeFrame->getTrackletsLabel(linkId));
1035 if (maxConcurrency > 1) {
1036 tbb::parallel_for(0,
static_cast<int>(topology.nLinks), clearTracklets);
1038 for (
int linkId{0}; linkId < topology.nLinks; ++linkId) {
1039 clearTracklets(linkId);
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]);
1056 if (maxConcurrency > 1) {
1057 tbb::parallel_for(0,
static_cast<int>(topology.nCells), clearNeighbours);
1059 for (
int cellTopologyId{0}; cellTopologyId < topology.nCells; ++cellTopologyId) {
1060 clearNeighbours(cellTopologyId);
1065 return std::tie(
a.nextCellTopology,
a.nextCell,
a.cellTopology,
a.cell) <
1066 std::tie(
b.nextCellTopology,
b.nextCell,
b.cellTopology,
b.cell);
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()) {
1079 const auto successors = topology.getCellsStartingWithLink(cellTopology.secondLink);
1080 if (!successors.getEntries()) {
1083 activeTopologies.push_back(cellTopologyId);
1084 sourceCellCount += mTimeFrame->getCells()[cellTopologyId].size();
1087 if (activeTopologies.empty()) {
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()) {
1102 const auto& nextCellLUT = mTimeFrame->getCellsLookupTable()[nextCellTopologyId];
1103 if (nextLayerTrackletIndex + 1 >=
static_cast<int>(nextCellLUT.size())) {
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())) {
1114 auto nextCellSeed{mTimeFrame->getCells()[nextCellTopologyId][iNextCell]};
1115 if (!nextCellSeed.rotate(currentCellSeed.getAlpha()) ||
1116 !nextCellSeed.propagateTo(currentCellSeed.getX(), getBz())) {
1120 float chi2 = currentCellSeed.getPredictedChi2Fast(nextCellSeed);
1121 if (
chi2 > mTrkParams[iteration].MaxChi2ClusterAttachment) {
1125 const int nextLevel = currentCellSeed.getLevel() + 1;
1126 emit(cellTopologyId, iCell, nextCellTopologyId, iNextCell, nextLevel);
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)...);
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);
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)...);
1159 std::sort(waveNeighbours.begin(), waveNeighbours.end(), neighbourLess);
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())});
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);
1191 std::inclusive_scan(cellsNeighbourLUT.begin(), cellsNeighbourLUT.end(), cellsNeighbourLUT.begin());
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;
1202 if (maxConcurrency > 1) {
1203 tbb::parallel_for(0,
static_cast<int>(targetSpans.size()), finalizeTarget);
1205 for (
int i{0}; i < static_cast<int>(targetSpans.size()); ++
i) {
1212 auto clearCellLUT = [&](
const int cellTopologyId) {
1213 deepVectorClear(mTimeFrame->getCellsLookupTable()[cellTopologyId]);
1215 if (maxConcurrency > 1) {
1216 tbb::parallel_for(0,
static_cast<int>(topology.nCells), clearCellLUT);
1218 for (
int cellTopologyId{0}; cellTopologyId < topology.nCells; ++cellTopologyId) {
1219 clearCellLUT(cellTopologyId);
1229 constexpr bool IsInitial = std::is_same_v<InputSeed, CellSeed>;
1230 static_assert(IsInitial || std::is_same_v<InputSeed, RoadSeedN>);
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) {
1240 return inputSeed.seed;
1243 const int cellTopologyId = [&]() {
1244 if constexpr (IsInitial) {
1245 return defaultCellTopologyId;
1247 return inputSeed.cellTopologyId;
1250 const int cellId = [&]() {
1251 if constexpr (IsInitial) {
1254 return inputSeed.cellId;
1258 if (currentCell.getLevel() != iLevel) {
1261 if constexpr (IsInitial) {
1263 const int clusterIndex = currentCell.getCluster(
layer);
1264 if (clusterIndex != constants::UnusedIndex && mTimeFrame->isClusterUsed(
layer, clusterIndex)) {
1270 if (cellTopologyId < 0 || mTimeFrame->getCellsNeighboursLUT()[cellTopologyId].empty()) {
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()) {
1282 if (!currentCell.getTimeStamp().isCompatible(neighbourCell.getTimeStamp())) {
1285 if (currentCell.getLevel() - 1 != neighbourCell.getLevel()) {
1288 const int neighbourLayer = neighbourCell.getInnerLayer();
1289 const int neighbourCluster = neighbourCell.getFirstClusterIndex();
1290 if (mTimeFrame->isClusterUsed(neighbourLayer, neighbourCluster)) {
1296 seed.getTimeStamp() = currentCell.getTimeStamp();
1297 seed.getTimeStamp() += neighbourCell.getTimeStamp();
1298 const auto& trHit = mTimeFrame->getTrackingFrameInfoOnLayer(neighbourLayer)[neighbourCluster];
1300 if (!seed.rotate(trHit.alphaTrackingFrame)) {
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)) {
1314 auto predChi2{seed.getPredictedChi2Quiet(trHit.positionTrackingFrame, trHit.covarianceTrackingFrame)};
1315 if ((predChi2 > mTrkParams[iteration].MaxChi2ClusterAttachment) || predChi2 < 0.f) {
1318 seed.setChi2(seed.getChi2() + predChi2);
1319 if (!seed.o2::track::TrackParCov::update(trHit.positionTrackingFrame, trHit.covarianceTrackingFrame)) {
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);
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)...); });
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()};
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)...); });
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);
1433 bounded_vector<bounded_vector<int>> firstClusters(mTrkParams[iteration].NLayers, bounded_vector<int>(mMemoryPool.get()), mMemoryPool.get());
1434 firstClusters.resize(mTrkParams[iteration].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();
1442 const auto topology = mTimeFrame->getTrackingTopologyView();
1443 tbb::enumerable_thread_specific<TrackFollowerScratch> followerScratch{
1445 for (
int startLevel{mTrkParams[iteration].CellsPerRoad()}; startLevel >= mTrkParams[iteration].CellMinimumLevel(); --startLevel) {
1447 const track::TrackSeedSelector<NLayers> seedFilter{constants::MaxTrackSeedQ2Pt, mTrkParams[iteration].MaxChi2NDF, startLevel, mTrkParams[iteration].MaxHoles, mTrkParams[iteration].getMinSeedingClusters(), mTrkParams[iteration].HoleLayerMask, mTrkParams[iteration].getNonSeedingLayerMask()};
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) {
1458 bounded_vector<RoadSeedN> lastSeeds(mMemoryPool.get()), updatedSeeds(mMemoryPool.get());
1460 auto roadKey = [&](
int level) {
1461 return CapacityEstimator::makeKey(SlabSite::Roads, iteration, CapacityEstimator::makeVariant(startLevel,
level), startCellTopologyId);
1464 processNeighbours(iteration, startCellTopologyId, startLevel, roadKey(startLevel), mTimeFrame->getCells()[startCellTopologyId], updatedSeeds);
1466 int level = startLevel;
1467 while (
level > 2 && !updatedSeeds.empty()) {
1468 lastSeeds.swap(updatedSeeds);
1469 deepVectorClear(updatedSeeds);
1471 processNeighbours(iteration, constants::UnusedIndex,
level, roadKey(
level), lastSeeds, updatedSeeds);
1473 deepVectorClear(lastSeeds);
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));
1485 if (trackSeeds.empty()) {
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();
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};
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));
1515 std::mutex tracksMutex;
1516 auto flushTracks = [&](bounded_vector<TrackITSExt>& localTracks) {
1517 if (localTracks.empty()) {
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();
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();
1535 if (
static_cast<int>(localTracks.size()) == chunkSize) {
1536 flushTracks(localTracks);
1539 flushTracks(localTracks);
1540 deepVectorClear(localTracks);
1543 deepVectorClear(trackSeeds);
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]);
1553 acceptTracks(iteration,
tracks, trackIndices, firstClusters);
1555 markTracks(iteration);
1560 bounded_vector<TrackITSExt>&
tracks,
1561 const bounded_vector<int>& trackIndices,
1562 bounded_vector<bounded_vector<int>>& firstClusters)
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) {
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) {
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;
1586 if (nShared -
int(isFirstShared && mTrkParams[iteration].AllowSharingFirstCluster) > mTrkParams[iteration].SharedMaxClusters) {
1590 bool firstCls{
true}, nominalCompatible{
true};
1592 for (
int iLayer{0}; iLayer < mTrkParams[iteration].NLayers; ++iLayer) {
1593 if (
track.getClusterIndex(iLayer) == constants::UnusedIndex) {
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);
1602 nominalTS = nominalROFTS;
1603 expandedTS = expandedROFTS;
1605 if (nominalCompatible) {
1606 if (nominalTS.isCompatible(nominalROFTS)) {
1607 nominalTS += nominalROFTS;
1609 nominalCompatible =
false;
1612 if (!expandedTS.isCompatible(expandedROFTS)) {
1613 LOGP(fatal,
"TS {}+/-{} are incompatible with {}+/-{}, this should not happen!", expandedROFTS.getTimeStamp(), expandedROFTS.getTimeStampError(), expandedTS.getTimeStamp(), expandedTS.getTimeStampError());
1615 expandedTS += expandedROFTS;
1618 track.getTimeStamp() = (nominalCompatible ? nominalTS : expandedTS).makeSymmetrical();
1621 if (
track.getTimeStamp().getTimeStampError() > smallestROFHalf) {
1622 track.getTimeStamp().setTimeStampError(smallestROFHalf);
1624 const auto diff =
track.getExtendedLayerPattern<NLayers>();
1626 size_t nExtendedClusters = 0;
1627 for (
int iLayer{0}; iLayer < mTrkParams[iteration].NLayers; ++iLayer) {
1628 nExtendedClusters +=
static_cast<bool>(diff & (0x1u << iLayer));
1630 mTimeFrame->addTrackExtensionCounters(1, nExtendedClusters);
1632 track.clearExtendedLayerPattern();
1633 trks.emplace_back(
track);
1635 if (mTrkParams[iteration].AllowSharingFirstCluster) {
1636 firstClusters[firstLayer].push_back(firstCluster);