26#include <oneapi/tbb/blocked_range.h>
66constexpr uint8_t kCompatibilityAbsCharge = 1;
69void reserveGenericTrackPublication(
TimeFrame& frame, std::size_t candidateCount, std::size_t maxReferencesPerTrack)
73 if (candidateCount >
tracks.max_size() -
tracks.size() ||
74 (maxReferencesPerTrack != 0 && candidateCount > (references.max_size() - references.size()) / maxReferencesPerTrack)) {
75 throw std::length_error{
"GenericTrack publication exceeds the output container capacity"};
78 references.reserve(references.size() + candidateCount * maxReferencesPerTrack);
81bool appendGenericTrack(TimeFrame& frame,
82 const TrackingCandidate& candidate,
83 gsl::span<
const gsl::span<const GlobalMeasurement>> layerMeasurements)
85 GenericTrack track = candidate.track;
87 std::vector<TrackClusterReference> resolvedReferences;
88 resolvedReferences.reserve(layerMeasurements.size());
89 for (std::size_t position = 0; position < layerMeasurements.size(); ++position) {
90 const int localIndex = candidate.getClusterIndex(
static_cast<int>(position));
94 if (localIndex < 0 ||
static_cast<std::size_t
>(localIndex) >= layerMeasurements[position].
size()) {
97 const auto& measurement = layerMeasurements[position][localIndex];
98 const TrackClusterReference
reference{
LayerId{
static_cast<uint16_t
>(position)}, 0, measurement.clusterId};
103 track.hitLayers.set(
static_cast<int>(position));
105 if (!
track.innerState.hasRecognizedKind() || !
track.outerState.hasRecognizedKind() ||
106 !std::isfinite(
track.timestamp.getTimeStamp()) ||
107 !std::isfinite(
track.timestamp.getTimeStampError()) ||
track.timestamp.getTimeStampError() <= 0.f || resolvedReferences.empty()) {
111 auto&
tracks = frame.getGenericTracks();
112 auto& references = frame.getTrackClusterIndices();
113 const auto oldTrackSize =
tracks.size();
114 const auto oldReferenceSize = references.size();
115 if (oldTrackSize > std::numeric_limits<uint32_t>::max() || oldReferenceSize > std::numeric_limits<uint32_t>::max() ||
116 resolvedReferences.size() > std::numeric_limits<uint32_t>::max() - oldReferenceSize) {
121 for (
const auto&
reference : resolvedReferences) {
124 track.firstClusterRef =
static_cast<uint32_t
>(oldReferenceSize);
125 track.clusterRefEnd =
static_cast<uint32_t
>(references.size());
128 references.resize(oldReferenceSize);
129 tracks.resize(oldTrackSize);
139template <
typename ROFOverlapView>
140Vertex diamondVertexForROF(
const Vertex& base,
const ROFOverlapView& rofOverlapView,
int layer,
int rofId)
143 v.setTimeStamp(rofOverlapView.getLayer(
layer).getROFTimeBounds(rofId,
true));
153 throw std::invalid_argument{
"CA traversal: iteration out of range (iteration " +
std::to_string(view.
iteration) +
")"};
155 int maxNvertices{-1};
159 int iVertex = std::min(maxNvertices, 0);
161 computeLayerTracklets(view, view.
iteration, iVertex);
163 findCellsNeighbours(view, view.
iteration);
165 }
while (++iVertex < maxNvertices);
168void TrackerTraits::computeLayerTracklets(
IterationContext& context,
const int iteration,
int iVertex)
170 auto& scratch = context.
scratch;
171 const auto scratchEdgeCount = scratch.
getTracklets().size();
172 for (
size_t edgeId = 0; edgeId < scratchEdgeCount; ++edgeId) {
173 scratch.getTracklets()[edgeId].clear();
174 scratch.getTrackletsLabel(edgeId).clear();
175 std::fill(scratch.getTrackletsLookupTable()[edgeId].begin(), scratch.getTrackletsLookupTable()[edgeId].end(), 0);
179 const auto& mMemoryPool = scratch.getMemoryPool();
180 auto* mFrame = &context.
frame;
182 const auto& mTraversalGraph = context.
topology;
185 const auto& topology = mTraversalGraph;
186 const Vertex diamondVert(trkParam.Diamond, trkParam.DiamondCov, 1, 1.f);
188 mTaskArena->execute([&] {
190 const TrackletProjectionCache& edgeCache,
int pivotROF,
auto&& emit) {
191 if (!mFrame->isROFEnabled(fromLayer, pivotROF)) {
197 gsl::span<const Vertex> primaryVertices;
198 if (trkParam.UseDiamond) {
199 diamondForROF = diamondVertexForROF(diamondVert, mFrame->getROFViews(fromLayer).overlap,
200 mFrame->getROFLocalLayer(fromLayer), pivotROF);
201 primaryVertices = gsl::span<const Vertex>(&diamondForROF, 1);
203 primaryVertices = mFrame->getPrimaryVertices(fromLayer, pivotROF);
205 if (primaryVertices.empty()) {
208 const int startVtx = iVertex >= 0 ? iVertex : 0;
209 const int endVtx = iVertex >= 0 ? o2::gpu::CAMath::Min(iVertex + 1,
int(primaryVertices.size())) :
int(primaryVertices.
size());
210 if (endVtx <= startVtx || (iVertex + 1) > primaryVertices.size()) {
214 const auto& rofOverlap = mFrame->getROFOverlap(fromLayer, toLayer, pivotROF);
215 if (!rofOverlap.getEntries()) {
219 auto layer0 = mFrame->getClustersOnLayer(pivotROF, fromLayer);
220 if (layer0.empty()) {
224 for (
int iCluster = 0; iCluster <
int(layer0.size()); ++iCluster) {
225 const GlobalMeasurement& sourceMeasurement = layer0[iCluster];
226 const int currentSortedIndex = mFrame->getSortedIndex(pivotROF, fromLayer, iCluster);
227 if (mFrame->isClusterUsed(fromLayer, sourceMeasurement.clusterId)) {
231 for (
int iV = startVtx; iV < endVtx; ++iV) {
232 const auto& pv = primaryVertices[iV];
233 if (!mFrame->isVertexCompatible(fromLayer, pivotROF, pv)) {
239 const auto& indexTableUtils = mFrame->getIndexTableUtils(toLayer);
240 TrackletSearchWindow window{};
242 kind, edgeCache, indexTableUtils,
243 mKernelParameters.nSigmaCut, window)) {
246 const auto bins = window.bins;
247 int rowBinsNum =
bins.w -
bins.y + 1;
248 if (rowBinsNum < 0) {
249 rowBinsNum += indexTableUtils.getNrowBins();
251 rowBinsNum = std::max(0, rowBinsNum);
253 for (
int targetROF = rofOverlap.getFirstEntry(); targetROF < rofOverlap.getEntriesBound(); ++targetROF) {
254 if (!mFrame->isROFEnabled(toLayer, targetROF)) {
257 auto layer1 = mFrame->getClustersOnLayer(targetROF, toLayer);
258 if (layer1.empty()) {
261 const auto ts = mFrame->getROFTimeStamp(fromLayer, pivotROF, toLayer, targetROF);
262 if (!ts.isCompatible(pv.getTimeStamp())) {
265 const auto& targetIndexTable = mFrame->getIndexTable(targetROF, toLayer);
266 const int colBinRange = (
bins.z -
bins.x) + 1;
267 for (
int iRow = 0; iRow < rowBinsNum; ++iRow) {
268 int iRowBin =
bins.y + iRow;
269 iRowBin %= indexTableUtils.getNrowBins();
270 if (iRowBin < 0 || iRowBin >= indexTableUtils.getNrowBins()) {
273 const int firstBinIdx = indexTableUtils.getBinIndex(
bins.x, iRowBin);
274 const int maxBinIdx = firstBinIdx + colBinRange;
275 const int firstRow = targetIndexTable[firstBinIdx];
276 const int lastRow = targetIndexTable[maxBinIdx];
277 for (
int iNext = firstRow; iNext <
lastRow; ++iNext) {
278 if (iNext >=
int(layer1.size())) {
281 const GlobalMeasurement& targetMeasurement = layer1[iNext];
282 if (mFrame->isClusterUsed(toLayer, targetMeasurement.clusterId)) {
288 const float referenceDelta = targetReferenceCoordinate - window.sourceReferenceCoordinate;
289 const float candidatePrediction = window.sourceProjectedCoordinate + window.slope * referenceDelta;
290 const float candidateVariance = window.varianceConstant +
291 referenceDelta * (window.varianceLinear + referenceDelta * window.varianceQuadratic);
292 const float projectedResidual = candidatePrediction - targetProjectedCoordinate;
293 const float phiResidual = std::remainder(window.phiPrediction - targetMeasurement.phi, o2::constants::math::TwoPI);
295 if (!(candidateVariance > 0.f && window.phiVariance > 0.f)) {
298 const float chi2 = o2::its::math_utils::Sq(projectedResidual) / candidateVariance +
299 o2::its::math_utils::Sq(phiResidual) / window.phiVariance;
300 if (
chi2 >= o2::its::math_utils::Sq(mKernelParameters.nSigmaCut)) {
305 const float transverseChord = std::hypot(targetMeasurement.x - sourceMeasurement.x,
306 targetMeasurement.y - sourceMeasurement.y);
307 if (!(transverseChord > 1.e-6f)) {
310 const float tanL = (targetMeasurement.z - sourceMeasurement.z) / transverseChord;
311 const float phi{o2::gpu::GPUCommonMath::ATan2(sourceMeasurement.y - targetMeasurement.y,
312 sourceMeasurement.x - targetMeasurement.x)};
313 emit(currentSortedIndex, mFrame->getSortedIndex(targetROF, toLayer, iNext), tanL, phi, ts);
321 const int maxConcurrency = std::max(1, mTaskArena->max_concurrency());
322 const int nConcurrentSinks = std::min(
static_cast<int>(edgeIds.size()), maxConcurrency);
323 tbb::parallel_for(0,
static_cast<int>(edgeIds.size()), [&](
const int edgeIndex) {
324 const auto edgeId = edgeIds[edgeIndex];
325 const auto& edge = topology.getEdge(edgeId);
326 const int fromLayer = edge.from.value();
327 const int toLayer = edge.to.value();
328 const auto kind = topology.getSurface(edge.from).kind;
329 const TrackletProjectionCache edgeCache{
330 fromLayer, toLayer, context.detectorConfiguration.getRepresentativeRadius(edge.from), context.detectorConfiguration.getRepresentativeRadius(edge.to),
331 mFrame->getMinR(toLayer), mFrame->getMaxR(toLayer),
332 mFrame->getMinZ(toLayer), mFrame->getMaxZ(toLayer),
333 context.detectorConfiguration.positionResolutions[fromLayer],
334 scratch.getEdgeMSAngle(edgeId.value()), scratch.getEdgePhiCut(edgeId.value())};
335 const int endROF = mFrame->getROFTiming(fromLayer).mNROFsTF;
337 const auto scale =
static_cast<double>(mFrame->getClusters()[fromLayer].size());
338 const auto capacity = mFrame->getCapacityEstimator().capacity(
key, scale);
339 UnorderedSlabSink<Tracklet> sink{{.capacity = capacity, .nThreads = maxConcurrency, .nConcurrentSinks = nConcurrentSinks}, mMemoryPool.get()};
340 tbb::parallel_for(0, endROF, [&](
const int pivotROF) {
341 auto& handle = sink.local();
342 forTracklets(fromLayer, toLayer,
kind, edgeCache, pivotROF,
343 [&handle](
auto&&... args) { handle.emplace(std::forward<
decltype(args)>(args)...); });
345 const auto stats = sink.stats();
346 sink.finalizeUnordered(scratch.getTracklets()[edgeId.value()]);
347 mFrame->getCapacityEstimator().update(
key, scale, stats.requested, stats.capacity, stats.emitted,
348 stats.spilled, stats.overflowed, stats.memoryLimited);
351 tbb::parallel_for(0,
static_cast<int>(edgeIds.size()), [&](
const int edgeIndex) {
352 const auto edgeId = edgeIds[edgeIndex];
355 auto& trkl{scratch.getTracklets()[edgeId.value()]};
356 std::sort(trkl.begin(), trkl.end());
357 trkl.erase(std::unique(trkl.begin(), trkl.end()), trkl.end());
358 trkl.shrink_to_fit();
359 auto& lut{scratch.getTrackletsLookupTable()[edgeId.value()]};
361 for (
const auto& tkl : trkl) {
362 lut[tkl.firstClusterIndex + 1]++;
364 std::inclusive_scan(lut.begin(), lut.end(), lut.begin());
369 if (mFrame->hasMCinformation() && trkParam.CreateArtefactLabels) {
370 tbb::parallel_for(0,
static_cast<int>(edgeIds.size()), [&](
const int edgeIndex) {
371 const auto edgeId = edgeIds[edgeIndex];
372 const auto& edge = topology.getEdge(edgeId);
373 const int fromLayer = edge.from.value();
374 const int toLayer = edge.to.value();
375 for (auto& trk : scratch.getTracklets()[edgeId.value()]) {
377 const auto currentId = mFrame->getClusters()[fromLayer][trk.firstClusterIndex].clusterId;
378 const auto nextId = mFrame->getClusters()[toLayer][trk.secondClusterIndex].clusterId;
379 for (const auto& lab1 : mFrame->getLabels(LayerId{static_cast<uint16_t>(fromLayer)}, currentId)) {
380 for (const auto& lab2 : mFrame->getLabels(LayerId{static_cast<uint16_t>(toLayer)}, nextId)) {
381 if (lab1 == lab2 && lab1.isValid()) {
386 if (label.isValid()) {
390 scratch.getTrackletsLabel(edgeId.value()).emplace_back(label);
397void TrackerTraits::computeLayerCells(IterationContext& context,
const int iteration)
399 auto& scratch = context.scratch;
400 const auto scratchCellCount = scratch.getCells().size();
401 for (
size_t cellPathId = 0; cellPathId < scratchCellCount; ++cellPathId) {
404 if (context.frame.hasMCinformation() && context.configuration.parameters.CreateArtefactLabels) {
409 const auto cellIds = context.configuration.cellIds();
410 const auto& mMemoryPool = scratch.getMemoryPool();
411 const auto& trkParam = context.configuration.parameters;
412 const auto mBz = context.bz;
413 const auto& mTraversalGraph = context.topology;
414 const auto& mKernelParameters = context.configuration.kernelParameters;
415 const auto& mLayerGlobalMeasurements = context.layerGlobalMeasurements;
416 const auto& topology = mTraversalGraph;
418 mTaskArena->execute([&] {
419 auto forTrackletCells = [&](
int firstEdgeId,
int secondEdgeId,
const std::array<int, 3>& hitLayers,
int iTracklet,
auto&& emit) {
420 const Tracklet& currentTracklet{scratch.getTracklets()[firstEdgeId][iTracklet]};
421 const int nextLayerClusterIndex{currentTracklet.secondClusterIndex};
422 const int nextLayerFirstTrackletIndex{scratch.getTrackletsLookupTable()[secondEdgeId][nextLayerClusterIndex]};
423 const int nextLayerLastTrackletIndex{scratch.getTrackletsLookupTable()[secondEdgeId][nextLayerClusterIndex + 1]};
424 for (
int iNextTracklet{nextLayerFirstTrackletIndex}; iNextTracklet < nextLayerLastTrackletIndex; ++iNextTracklet) {
425 const Tracklet& nextTracklet{scratch.getTracklets()[secondEdgeId][iNextTracklet]};
426 if (nextTracklet.firstClusterIndex != nextLayerClusterIndex) {
429 if (!currentTracklet.getTimeStamp().isCompatible(nextTracklet.getTimeStamp())) {
434 const int sortedId[3]{currentTracklet.firstClusterIndex, nextTracklet.firstClusterIndex, nextTracklet.secondClusterIndex};
436 const float edgeMSAngle = scratch.getEdgeMSAngle(secondEdgeId);
438 const float lambda01 = std::atan(currentTracklet.tanLambda);
439 const float lambda12 = std::atan(nextTracklet.tanLambda);
440 const float sinTheta = std::max(std::abs(std::cos(0.5f * (lambda01 + lambda12))),
441 o2::constants::math::Almost0);
442 const bool isDisk = topology.getSurface(LayerId{
static_cast<uint16_t
>(hitLayers[1])}).
kind == SurfaceKind::Disk;
446 const float deltaLambda = std::abs(lambda01 - lambda12);
447 if (deltaLambda > dipAngleTolerance) {
451 const auto& inner = mLayerGlobalMeasurements[hitLayers[0]][sortedId[0]];
452 const auto& middle = mLayerGlobalMeasurements[hitLayers[1]][sortedId[1]];
453 const auto& outer = mLayerGlobalMeasurements[hitLayers[2]][sortedId[2]];
454 const float length01 = std::hypot(inner.x - middle.x, inner.y - middle.y);
455 const float length12 = std::hypot(middle.x - outer.x, middle.y - outer.y);
456 const float maximumCurvature = std::min({std::abs(o2::constants::math::B2C * mBz) /
457 mKernelParameters.trackletMinPt,
460 const float maximumBending =
461 std::asin(std::clamp(0.5f * maximumCurvature * length01, 0.f, 1.f)) +
462 std::asin(std::clamp(0.5f * maximumCurvature * length12, 0.f, 1.f));
463 const float deltaPhi = std::abs(std::remainder(currentTracklet.phi - nextTracklet.phi,
464 o2::constants::math::TwoPI));
467 if (deltaPhi > maximumBending + azimuthalTolerance) {
471 const std::array<GlobalMeasurement, 3> measurements{inner, middle, outer};
472 TripletFitFactor tripletFactor{};
474 TimeEstBC ts = currentTracklet.getTimeStamp();
475 ts += nextTracklet.getTimeStamp();
478 const LayerMask hitLayerMask{hitLayers[0], hitLayers[1], hitLayers[2]};
479 Triplet
seed{hitLayerMask, sortedId[0], sortedId[1], sortedId[2], iTracklet, iNextTracklet, ts};
480 seed.tripletFactor() = tripletFactor;
481 emit(std::move(seed));
486 const int maxConcurrency = std::max(1, mTaskArena->max_concurrency());
487 for (
const auto cellId : cellIds) {
488 const auto& cellTopology = topology.getPath(cellId);
489 const auto firstEdgeId = cellTopology.first;
490 const auto secondEdgeId = cellTopology.second;
491 if (scratch.getTracklets()[firstEdgeId.value()].empty() ||
492 scratch.getTracklets()[secondEdgeId.value()].empty()) {
496 const auto& firstEdge = topology.getEdge(cellTopology.first);
497 const auto& secondEdge = topology.getEdge(cellTopology.second);
498 const std::array<int, 3> layers{firstEdge.from.value(), firstEdge.to.value(), secondEdge.to.value()};
500 auto& layerCells = scratch.getCells()[cellId.value()];
501 auto& lut = scratch.getCellsLookupTable()[cellId.value()];
502 const int currentLayerTrackletsNum{
static_cast<int>(scratch.getTracklets()[firstEdgeId.value()].size())};
503 const auto key = CapacityEstimator::makeKey(SlabSite::Cells, iteration, 0, cellId);
504 const auto scale =
static_cast<double>(currentLayerTrackletsNum);
505 const auto capacity = context.frame.getCapacityEstimator().capacity(
key, scale);
506 GroupedSlabSink<Triplet> sink{{.capacity = capacity, .nThreads = maxConcurrency}, mMemoryPool.get()};
507 tbb::parallel_for(0, currentLayerTrackletsNum, [&](
const int iTracklet) {
508 auto& handle = sink.local();
509 handle.beginProducer(iTracklet);
510 forTrackletCells(firstEdgeId.value(), secondEdgeId.value(), layers, iTracklet,
511 [&handle](Triplet seed) { handle.emplace(std::move(seed)); });
513 const auto stats = sink.stats();
514 sink.finalizeGrouped(
static_cast<size_t>(currentLayerTrackletsNum), lut, layerCells);
515 context.frame.getCapacityEstimator().update(
key, scale, stats.requested, stats.capacity, stats.emitted,
516 stats.spilled, stats.overflowed, stats.memoryLimited);
518 if (context.frame.hasMCinformation() && trkParam.CreateArtefactLabels) {
519 auto&
labels = scratch.getCellsLabel(cellId.value());
520 labels.reserve(layerCells.size());
521 for (
const auto& cell : layerCells) {
522 MCCompLabel currentLab{scratch.getTrackletsLabel(firstEdgeId.value())[cell.getFirstTrackletIndex()]};
523 MCCompLabel nextLab{scratch.getTrackletsLabel(secondEdgeId.value())[cell.getSecondTrackletIndex()]};
524 labels.emplace_back(currentLab == nextLab ? currentLab : MCCompLabel());
530 const auto scratchEdgeCount = scratch.getTracklets().size();
531 for (
size_t edgeId = 0; edgeId < scratchEdgeCount; ++edgeId) {
537void TrackerTraits::findCellsNeighbours(IterationContext& context,
const int iteration)
539 auto& scratch = context.scratch;
540 const auto& memoryPool = scratch.getMemoryPool();
541 const auto& topology = context.topology;
542 const auto& globalMeasurements = context.layerGlobalMeasurements;
543 const auto&
params = context.configuration.kernelParameters;
544 for (std::size_t slot = 0; slot < scratch.getCellsNeighbours().
size(); ++slot) {
549 const auto& scheduledCells = context.configuration.topology.scheduledPaths;
550 const auto scratchCellCount = scratch.getCells().size();
551 if (scratch.getCellsLookupTable().size() != scratchCellCount ||
552 scratch.getCellsNeighbours().size() != scratchCellCount ||
553 scratch.getCellsNeighboursTopology().size() != scratchCellCount ||
554 scratch.getCellsNeighboursLUT().size() != scratchCellCount) {
555 throw std::invalid_argument{
"CA traversal: sparse topology mismatch (iteration " +
std::to_string(iteration) +
")"};
557 mTaskArena->execute([&] {
558 std::vector<bounded_vector<TripletNeighbour>> cellsNeighboursByTarget;
559 cellsNeighboursByTarget.reserve(scratchCellCount);
560 for (
size_t cellPathId = 0; cellPathId < scratchCellCount; ++cellPathId) {
561 cellsNeighboursByTarget.emplace_back(memoryPool.get());
564 for (
const auto cellId : scheduledCells) {
565 if (
static_cast<size_t>(cellId.value()) >= scratchCellCount ||
566 static_cast<size_t>(cellId.value()) >= scratch.getCellsLookupTable().size()) {
567 throw std::invalid_argument{
"CA traversal: sparse topology mismatch (iteration " +
std::to_string(iteration) +
")"};
569 const auto& cellTopology = topology.getPath(cellId);
570 const float currentMSAngle = scratch.getEdgeMSAngle(cellTopology.second.value());
571 const float currentAngularVariance = currentMSAngle * currentMSAngle;
572 if (scratch.getCells()[cellId.value()].empty()) {
575 const auto successors = topology.getPathsStartingWithEdge(cellTopology.second);
576 if (!successors.getEntries()) {
580 struct SuccessorBinding {
582 float angularVariance;
584 std::array<SuccessorBinding, MaxLayoutSurfaces> successorBindings{};
585 size_t successorBindingCount = 0;
586 if (successors.getEntries() > successorBindings.size()) {
587 throw std::invalid_argument{
"CA traversal: sparse topology mismatch (iteration " +
std::to_string(iteration) +
")"};
589 for (uint32_t iSuccessor = 0; iSuccessor < successors.getEntries(); ++iSuccessor) {
590 const auto nextCellId = topology.pathsByFirstEdge[successors.getFirstEntry() + iSuccessor];
591 if (
static_cast<size_t>(nextCellId.value()) >= scratch.getCells().size() ||
592 static_cast<size_t>(nextCellId.value()) >= scratch.getCellsLookupTable().size()) {
593 throw std::invalid_argument{
"CA traversal: sparse topology mismatch (iteration " +
std::to_string(iteration) +
")"};
595 if (scratch.getCells()[nextCellId.value()].empty() ||
596 scratch.getCellsLookupTable()[nextCellId.value()].empty()) {
599 const auto& nextCellTopology = topology.getPath(nextCellId);
600 const float nextMSAngle = scratch.getEdgeMSAngle(nextCellTopology.second.value());
601 successorBindings[successorBindingCount++] = {nextCellId, nextMSAngle * nextMSAngle};
604 const int maxConcurrency = std::max(1, mTaskArena->max_concurrency());
605 const auto key = CapacityEstimator::makeKey(SlabSite::Neighbours, iteration, 0, cellId);
606 const auto scale =
static_cast<double>(scratch.getCells()[cellId.value()].size());
607 const auto capacity = context.frame.getCapacityEstimator().capacity(
key, scale);
608 UnorderedSlabSink<TripletNeighbour> sink{{.capacity = capacity, .nThreads = maxConcurrency}, memoryPool.get()};
609 tbb::parallel_for(0,
static_cast<int>(scratch.getCells()[cellId.value()].size()), [&](
const int iCell) {
610 auto& handle = sink.local();
611 const auto& currentTriplet{scratch.getCells()[cellId.value()][iCell]};
612 const int nextLayerTrackletIndex{currentTriplet.getSecondTrackletIndex()};
613 for (
size_t iSuccessor = 0; iSuccessor < successorBindingCount; ++iSuccessor) {
614 const auto& successor = successorBindings[iSuccessor];
615 const auto& nextCellLUT = scratch.getCellsLookupTable()[successor.cellId.value()];
616 if (nextLayerTrackletIndex < 0 || nextLayerTrackletIndex + 1 >=
static_cast<int>(nextCellLUT.size())) {
619 const int nextLayerFirstCellIndex{nextCellLUT[nextLayerTrackletIndex]};
620 const int nextLayerLastCellIndex{nextCellLUT[nextLayerTrackletIndex + 1]};
621 if (nextLayerFirstCellIndex < 0 || nextLayerLastCellIndex < nextLayerFirstCellIndex ||
622 nextLayerLastCellIndex >
static_cast<int>(scratch.getCells()[successor.cellId.value()].size())) {
623 throw std::invalid_argument{
"CA traversal: sparse topology mismatch (iteration " +
std::to_string(iteration) +
")"};
625 for (
int iNextCell{nextLayerFirstCellIndex}; iNextCell < nextLayerLastCellIndex; ++iNextCell) {
626 const auto& nextTripletRef{scratch.getCells()[successor.cellId.value()][iNextCell]};
627 if (nextTripletRef.getFirstTrackletIndex() != nextLayerTrackletIndex || !currentTriplet.getTimeStamp().isCompatible(nextTripletRef.getTimeStamp())) {
631 const auto currentMiddle = currentTriplet.getClusterReference(1);
632 const auto currentOuter = currentTriplet.getClusterReference(2);
633 const auto nextInner = nextTripletRef.getClusterReference(0);
634 const auto nextMiddle = nextTripletRef.getClusterReference(1);
635 if (currentMiddle.surfacePosition != nextInner.surfacePosition ||
636 currentMiddle.clusterIndex != nextInner.clusterIndex ||
637 currentOuter.surfacePosition != nextMiddle.surfacePosition ||
638 currentOuter.clusterIndex != nextMiddle.clusterIndex) {
642 const std::array<TripleClusterReference, 4> references{
643 currentTriplet.getClusterReference(0), currentMiddle,
644 currentOuter, nextTripletRef.getClusterReference(2)};
645 std::array<GlobalMeasurement, 4> measurements{};
646 bool measurementsValid =
true;
647 for (std::size_t hit = 0; hit < references.size(); ++hit) {
650 static_cast<std::size_t
>(
reference.surfacePosition) >= globalMeasurements.size() ||
652 static_cast<std::size_t
>(
reference.clusterIndex) >= globalMeasurements[
reference.surfacePosition].size()) {
653 measurementsValid =
false;
656 measurements[hit] = globalMeasurements[
reference.surfacePosition][
reference.clusterIndex];
658 AdjacentTripletFitResult adjacentFit{};
659 const bool fitValid = measurementsValid &&
661 currentTriplet.tripletFactor(), nextTripletRef.tripletFactor(), measurements,
662 {currentAngularVariance, successor.angularVariance}, adjacentFit);
663 if (!fitValid || adjacentFit.chi2 >
params.maxChi2ClusterAttachment) {
667 const int nextLevel = currentTriplet.getLevel() + 1;
668 handle.emplace(cellId.value(), iCell, successor.cellId.value(), iNextCell, nextLevel);
673 const auto stats = sink.stats();
674 bounded_vector<TripletNeighbour> sourceNeighbours{memoryPool.get()};
675 sink.finalizeUnordered(sourceNeighbours);
676 context.frame.getCapacityEstimator().update(
key, scale, stats.requested, stats.capacity, stats.emitted,
677 stats.spilled, stats.overflowed, stats.memoryLimited);
678 for (
const auto& neighbour : sourceNeighbours) {
679 cellsNeighboursByTarget[neighbour.nextCellTopology].push_back(neighbour);
680 if (neighbour.level > scratch.getCells()[neighbour.nextCellTopology][neighbour.nextCell].getLevel()) {
681 scratch.getCells()[neighbour.nextCellTopology][neighbour.nextCell].setLevel(neighbour.level);
686 for (
size_t cellPathId = 0; cellPathId < scratchCellCount; ++cellPathId) {
687 auto& cellsNeighbours = cellsNeighboursByTarget[cellPathId];
688 if (cellsNeighbours.empty()) {
692 std::sort(cellsNeighbours.begin(), cellsNeighbours.end(), [](
const auto&
a,
const auto&
b) {
693 return std::tie(a.nextCell, a.cellTopology, a.cell) < std::tie(b.nextCell, b.cellTopology, b.cell);
696 auto& cellsNeighbourLUT = scratch.getCellsNeighboursLUT()[cellPathId];
697 cellsNeighbourLUT.assign(scratch.getCells()[cellPathId].size(), 0);
698 for (
const auto& neigh : cellsNeighbours) {
699 ++cellsNeighbourLUT[neigh.nextCell];
701 std::inclusive_scan(cellsNeighbourLUT.begin(), cellsNeighbourLUT.end(), cellsNeighbourLUT.begin());
703 scratch.getCellsNeighbours()[cellPathId].reserve(cellsNeighbours.size());
704 scratch.getCellsNeighboursTopology()[cellPathId].reserve(cellsNeighbours.size());
705 std::ranges::transform(cellsNeighbours, std::back_inserter(scratch.getCellsNeighbours()[cellPathId]), [](
const auto& neigh) { return neigh.cell; });
706 std::ranges::transform(cellsNeighbours, std::back_inserter(scratch.getCellsNeighboursTopology()[cellPathId]), [](
const auto& neigh) { return neigh.cellTopology; });
709 for (
auto& cellLUT : scratch.getCellsLookupTable()) {
714bool TrackerTraits::buildTrackSeed(IterationContext& context,
int,
715 const Triplet& cell, TrackSeed&
output)
const
717 std::array<const GlobalMeasurement*, 3> globals{};
718 std::array<const SurfaceMeasurement*, 3> measurements{};
719 std::array<const SurfaceDescriptor*, 3> surfaces{};
720 for (
int hit = 0; hit < 3; ++hit) {
721 const auto reference = cell.getClusterReference(hit);
722 const auto surface =
LayerId{
static_cast<uint16_t
>(
reference.surfacePosition)};
723 globals[hit] = &context.layerGlobalMeasurements[
reference.surfacePosition][
reference.clusterIndex];
724 measurements[hit] = context.frame.getSurfaceMeasurement(surface, globals[hit]->clusterId);
725 surfaces[hit] = &context.topology.getSurface(surface);
728 SurfaceTrackState
state{};
730 const auto& outer = *measurements[2];
731 const auto kind = surfaces[2]->kind;
734 float curvatureSquared = 1.f;
736 state.referenceCoordinate = outer.frame.q;
737 state.alpha = (
kind == SurfaceKind::Cylinder) ? outer.frame.frameAngle : 0.f;
738 state.parameters[0] = outer.frame.u;
739 state.parameters[1] = outer.frame.v;
741 float cosAlpha, sinAlpha,
x[3],
y[3];
742 o2::math_utils::detail::sincos(
state.alpha, sinAlpha, cosAlpha);
743 for (
int i{0};
i < 3; ++
i) {
744 const auto&
pos = globals[
i]->position;
745 x[
i] =
pos.x * cosAlpha +
pos.y * sinAlpha;
746 y[
i] = -
pos.x * sinAlpha +
pos.y * cosAlpha;
748 const float dx =
x[2] -
x[1];
749 const float dy =
y[2] -
y[1];
750 const float chordLength = std::hypot(dx, dy);
751 const float inverseLength = 1.f / chordLength;
753 const float chordCos = dx * inverseLength;
754 const float chordSin = dy * inverseLength;
756 (math_utils::computeTanDipAngle(
x[0],
y[0],
x[1],
y[1], globals[0]->position.z, globals[1]->position.z) +
757 math_utils::computeTanDipAngle(
x[1],
y[1],
x[2],
y[2], globals[1]->position.z, globals[2]->position.z));
759 if (std::abs(context.bz) < 0.01f) {
763 const float curvature =
764 math_utils::computeCurvature(
765 x[2],
y[2],
x[1],
y[1],
x[0],
y[0]);
767 const float halfSin = 0.5f * curvature * chordLength;
768 const float halfCos =
769 std::sqrt((1.f - halfSin) * (1.f + halfSin));
771 cosPhi = chordCos * halfCos - chordSin * halfSin;
772 sinPhi = chordSin * halfCos + chordCos * halfSin;
773 qOverPt = curvature /
774 (context.bz * o2::constants::math::B2C);
775 curvatureSquared = curvature * curvature;
778 float phi = o2::gpu::GPUCommonMath::ASin(sinPhi);
780 phi = o2::constants::math::PI -
phi;
781 }
else if (phi < 0.f) {
782 phi += o2::constants::math::TwoPI;
785 state.parameters[2] = (
kind == SurfaceKind::Cylinder) ? sinPhi :
phi;
786 state.parameters[3] = tanLambda;
787 state.parameters[4] = qOverPt;
788 state.covariance[packedCovarianceIndex(0, 0)] = outer.covariance.uu;
789 state.covariance[packedCovarianceIndex(1, 0)] = outer.covariance.uv;
790 state.covariance[packedCovarianceIndex(1, 1)] = outer.covariance.vv;
797 state.absCharge = kCompatibilityAbsCharge;
798 state.pid = kCompatibilityPID;
800 const std::array<const SurfaceMeasurement*, 2> attachmentMeasurements{measurements[1], measurements[0]};
801 const std::array<const SurfaceDescriptor*, 2> attachmentSurfaces{surfaces[1], surfaces[0]};
802 for (
int step = 0;
step < 2; ++
step) {
803 const auto& targetSurface = *attachmentSurfaces[
step];
804 if (!Propagator::attachMeasurement(
805 state, targetSurface, *attachmentMeasurements[step], context.bz,
806 material::MaterialTraversalDirection::OppositeMomentum,
808 context.configuration.kernelParameters.maxChi2ClusterAttachment,
818template <
typename InputSeed>
819void TrackerTraits::processNeighbours(IterationContext& context,
int iteration, CellPathId startingPath,
820 int defaultCellPathId,
int startLevel,
int currentLevel,
821 const bounded_vector<InputSeed>& currentSeeds,
822 bounded_vector<RoadSeedEmission>& updatedCells,
823 const TrackingKernelParameters&
params)
825 auto* scratch = &context.scratch;
826 const auto& mMemoryPool = scratch->getMemoryPool();
827 const auto mBz = context.bz;
828 const auto& mLayerGlobalMeasurements = context.layerGlobalMeasurements;
829 const int activeSurfaceCount = context.configuration.topology.nLayers;
831 mTaskArena->execute([&] {
832 auto forTripletNeighbours = [&](
int iCell,
auto&& emit) {
833 const auto& input = currentSeeds[iCell];
834 const auto& currentCell = [&]() ->
const auto& {
835 if constexpr (std::is_same_v<InputSeed, Triplet>) {
842 int cellPathId = defaultCellPathId;
843 if constexpr (std::is_same_v<InputSeed, RoadSeedEmission>) {
844 cellId = input.cellId;
845 cellPathId = input.cellPathId;
848 if (currentCell.getLevel() != currentLevel) {
851 if constexpr (std::is_same_v<InputSeed, Triplet>) {
853 const int clusterIndex = currentCell.getCluster(
layer);
855 context.frame.isClusterUsed(
layer, mLayerGlobalMeasurements[
layer][clusterIndex].clusterId)) {
861 if (cellPathId < 0 || scratch->getCellsNeighboursLUT()[cellPathId].empty()) {
864 const int startNeighbourId{cellId ? scratch->getCellsNeighboursLUT()[cellPathId][cellId - 1] : 0};
865 const int endNeighbourId{scratch->getCellsNeighboursLUT()[cellPathId][cellId]};
866 TrackSeed baseSeed{};
867 if constexpr (std::is_same_v<InputSeed, Triplet>) {
869 if (!buildTrackSeed(context, cellPathId, currentCell, baseSeed)) {
873 baseSeed = currentCell;
875 for (
int iNeighbourCell{startNeighbourId}; iNeighbourCell < endNeighbourId; ++iNeighbourCell) {
876 const int neighbourCellPathId = scratch->getCellsNeighboursTopology()[cellPathId][iNeighbourCell];
877 const int neighbourCellId = scratch->getCellsNeighbours()[cellPathId][iNeighbourCell];
878 const auto& neighbourCell = scratch->getCells()[neighbourCellPathId][neighbourCellId];
879 if (neighbourCell.getSecondTrackletIndex() != currentCell.getFirstTrackletIndex()) {
882 if (!currentCell.getTimeStamp().isCompatible(neighbourCell.getTimeStamp())) {
885 if (currentCell.getLevel() - 1 != neighbourCell.getLevel()) {
888 const int neighbourLayer = neighbourCell.getInnerLayer();
889 if (neighbourLayer < 0 || neighbourLayer >= activeSurfaceCount) {
890 throw std::invalid_argument{
"CA traversal: sparse topology mismatch (iteration " +
std::to_string(iteration) +
")"};
892 const int neighbourCluster = neighbourCell.getFirstClusterIndex();
893 const auto& neighbourGlobal = mLayerGlobalMeasurements[neighbourLayer][neighbourCluster];
894 if (context.frame.isClusterUsed(neighbourLayer, neighbourGlobal.clusterId)) {
899 TrackSeed
seed{baseSeed};
900 seed.getTimeStamp() = currentCell.getTimeStamp();
901 seed.getTimeStamp() += neighbourCell.getTimeStamp();
903 const auto* measurement = context.frame.getSurfaceMeasurement(LayerId{
static_cast<uint16_t
>(neighbourLayer)}, neighbourGlobal.clusterId);
904 if (measurement ==
nullptr) {
909 const bool attached = Propagator::attachMeasurement(
seed.state(), context.topology.getSurface(LayerId{static_cast<uint16_t>(neighbourLayer)}), *measurement, mBz,
910 material::MaterialTraversalDirection::OppositeMomentum,
true,
917 seed.setCluster(neighbourLayer, neighbourCluster);
918 auto hitLayerMask =
seed.getHitLayerMask();
919 hitLayerMask.set(neighbourLayer);
920 seed.setHitLayerMask(hitLayerMask);
921 seed.setLevel(neighbourCell.getLevel());
922 seed.setFirstTrackletIndex(neighbourCell.getFirstTrackletIndex());
923 seed.setSecondTrackletIndex(neighbourCell.getSecondTrackletIndex());
924 emit(RoadSeedEmission{std::move(seed), neighbourCellId, neighbourCellPathId});
928 const int nCells =
static_cast<int>(currentSeeds.size());
929 const auto key = CapacityEstimator::makeKey(SlabSite::Roads, iteration,
930 CapacityEstimator::makeVariant(startLevel, currentLevel),
932 const auto scale =
static_cast<double>(nCells);
933 const auto capacity = context.frame.getCapacityEstimator().capacity(
key, scale);
934 GroupedSlabSink<RoadSeedEmission> sink{{.capacity = capacity, .nThreads = std::max(1, mTaskArena->max_concurrency())}, mMemoryPool.get()};
935 tbb::parallel_for(0, nCells, [&](
const int iCell) {
936 auto& handle = sink.local();
937 handle.beginProducer(iCell);
938 forTripletNeighbours(iCell, [&handle](RoadSeedEmission emission) { handle.emplace(std::move(emission)); });
940 const auto stats = sink.stats();
941 bounded_vector<int> lut{mMemoryPool.get()};
942 sink.finalizeGrouped(
static_cast<size_t>(nCells), lut, updatedCells);
943 context.frame.getCapacityEstimator().update(
key, scale, stats.requested, stats.capacity, stats.emitted,
944 stats.spilled, stats.overflowed, stats.memoryLimited);
948void TrackerTraits::findRoads(IterationContext& context,
const int iteration)
950 auto* scratch = &context.scratch;
951 const auto& mMemoryPool = scratch->getMemoryPool();
952 const auto& trkParam = context.configuration.parameters;
953 const auto mBz = context.bz;
954 const auto& mTraversalGraph = context.topology;
955 const auto& mKernelParameters = context.configuration.kernelParameters;
956 const auto& mLayerGlobalMeasurements = context.layerGlobalMeasurements;
957 const gsl::span<const CellPathId> roadStartCells = context.configuration.topology.roadStartPaths;
958 const int activeSurfaceCount = context.configuration.topology.nLayers;
959 bounded_vector<bounded_vector<int>> firstClusters(activeSurfaceCount, bounded_vector<int>(mMemoryPool.get()), mMemoryPool.get());
960 firstClusters.resize(activeSurfaceCount);
966 constexpr float maxAbsQOverPt = 1.e3f;
967 const auto seedingLayerMask = context.topology.seedingLayers;
968 const auto nonSeedingLayerMask = ~seedingLayerMask;
969 const int cellsPerRoad = seedingLayerMask.count() - 2;
970 const auto& componentOffsets = context.configuration.topology.roadStartComponentOffsets;
971 const auto holeLayerMask = context.frame.getDetectorConfiguration().getHoleLayers();
972 if (componentOffsets.empty() || componentOffsets.front() != 0 || componentOffsets.back() != roadStartCells.size()) {
973 throw std::invalid_argument{
"CA traversal: sparse topology mismatch (iteration " +
std::to_string(iteration) +
")"};
976 const auto componentRoadStarts = roadStartCells.subspan(componentOffsets[component],
977 componentOffsets[component + 1] - componentOffsets[component]);
978 for (
int startLevel{cellsPerRoad}; startLevel >= trkParam.CellMinimumLevel(); --startLevel) {
980 auto seedFilter = [&](
const auto&
seed) {
981 const auto hitLayerMask =
seed.getHitLayerMask();
982 const auto effectiveHoleMask = hitLayerMask.holeMask() & ~nonSeedingLayerMask;
984 return effectiveHoleMask.isAllowedHoleMask(trkParam.MaxHoles, holeLayerMask) &&
985 hitLayerMask.count() >= trkParam.MinTrackLength &&
986 std::abs(
seed.getQOverPt()) <= maxAbsQOverPt &&
seed.getChi2() <= trkParam.MaxChi2NDF * ((startLevel + 2) * 2 - 5);
989 bounded_vector<TrackSeed> trackSeeds(mMemoryPool.get());
991 for (
const auto startId : componentRoadStarts) {
994 if (scratch->getCells()[startId.value()].empty()) {
998 bounded_vector<RoadSeedEmission> currentCells(mMemoryPool.get()), updatedCells(mMemoryPool.get());
1000 processNeighbours(context, iteration, startId, startId.value(), startLevel, startLevel,
1001 scratch->getCells()[startId.value()], updatedCells, mKernelParameters);
1003 int level = startLevel;
1004 while (
level > 2 && !updatedCells.empty()) {
1005 currentCells.swap(updatedCells);
1009 currentCells, updatedCells, mKernelParameters);
1013 const auto accepted = std::count_if(updatedCells.begin(), updatedCells.end(),
1014 [&](
const auto& cell) { return seedFilter(cell.seed); });
1015 trackSeeds.reserve(trackSeeds.size() + accepted);
1016 for (
auto& cell : updatedCells) {
1017 if (seedFilter(cell.seed)) {
1018 trackSeeds.push_back(std::move(cell.seed));
1023 if (trackSeeds.empty()) {
1027 bounded_vector<TrackingCandidate>
tracks(mMemoryPool.get());
1028 mTaskArena->execute([&] {
1029 const int nSeeds =
static_cast<int>(trackSeeds.size());
1030 const auto key = CapacityEstimator::makeKey(SlabSite::Tracks, iteration,
1031 CapacityEstimator::makeVariant(startLevel,
static_cast<int>(component)), 0);
1032 const auto scale =
static_cast<double>(nSeeds);
1033 const auto capacity = context.frame.getCapacityEstimator().capacity(
key, scale);
1034 GroupedSlabSink<TrackingCandidate> sink{{.capacity = capacity, .nThreads = std::max(1, mTaskArena->max_concurrency())}, mMemoryPool.get()};
1035 tbb::parallel_for(0, nSeeds, [&](
const int iSeed) {
1036 SurfaceTrackState innerState{};
1037 SurfaceTrackState outerState{};
1040 if (!
fitTrackSeedLegs(trackSeeds[iSeed], context.frame, mLayerGlobalMeasurements,
1041 mTraversalGraph.getSurfaceCatalogView(), mBz,
1042 trkParam.ShiftRefToCluster, trkParam.MaxChi2ClusterAttachment, trkParam.MaxChi2NDF,
1043 trkParam.RepeatRefitOut, gsl::span<const float>(trkParam.MinPt),
1044 innerState, outerState,
chi2)) {
1047 TrackingCandidate temporaryTrack;
1052 auto& handle = sink.local();
1053 handle.beginProducer(iSeed);
1054 handle.emplace(std::move(temporaryTrack));
1056 const auto stats = sink.stats();
1057 bounded_vector<int> lut{mMemoryPool.get()};
1058 sink.finalizeGrouped(
static_cast<size_t>(nSeeds), lut,
tracks);
1059 context.frame.getCapacityEstimator().update(
key, scale, stats.requested, stats.capacity, stats.emitted,
1060 stats.spilled, stats.overflowed, stats.memoryLimited);
1065 std::sort(
tracks.begin(),
tracks.end(), [](
const TrackingCandidate&
a,
const TrackingCandidate&
b) {
1066 const auto ncla = a.getNumberOfClusters();
1067 const auto nclb = b.getNumberOfClusters();
1068 return (ncla == nclb) ? (a.track.chi2 < b.track.chi2) : ncla > nclb;
1070 acceptTracks(context, iteration,
tracks, firstClusters);
1075void TrackerTraits::acceptTracks(IterationContext& context,
int iteration,
1076 bounded_vector<TrackingCandidate>&
tracks,
1077 bounded_vector<bounded_vector<int>>& firstClusters)
1079 auto* scratch = &context.scratch;
1080 auto* mFrame = &context.frame;
1081 const auto& trkParam = context.configuration.parameters;
1082 const auto& mLayerGlobalMeasurements = context.layerGlobalMeasurements;
1083 const int activeSurfaceCount = context.configuration.topology.nLayers;
1084 reserveGenericTrackPublication(*mFrame,
tracks.size(),
static_cast<std::size_t
>(activeSurfaceCount));
1085 for (
auto& track :
tracks) {
1087 bool isFirstShared{
false};
1088 int firstLayer{-1}, firstCluster{-1};
1089 for (
int iLayer{0}; iLayer < activeSurfaceCount; ++iLayer) {
1093 const auto clusterId = mLayerGlobalMeasurements[iLayer][
track.getClusterIndex(iLayer)].clusterId;
1094 bool isShared = mFrame->isClusterUsed(iLayer, clusterId);
1095 nShared +=
int(isShared);
1096 if (firstLayer < 0) {
1097 firstCluster =
track.getClusterIndex(iLayer);
1098 isFirstShared = isShared && trkParam.AllowSharingFirstCluster && std::find(firstClusters[iLayer].
begin(), firstClusters[iLayer].
end(), firstCluster) != firstClusters[iLayer].end();
1099 firstLayer = iLayer;
1104 if (nShared -
int(isFirstShared && trkParam.AllowSharingFirstCluster) > trkParam.SharedMaxClusters) {
1108 bool firstCls{
true}, nominalCompatible{
true};
1109 TimeEstBC nominalTS, expandedTS;
1110 float smallestROFHalf = std::numeric_limits<float>::max();
1111 for (
int iLayer{0}; iLayer < activeSurfaceCount; ++iLayer) {
1115 smallestROFHalf = std::min(smallestROFHalf, mFrame->getROFTiming(iLayer).mROFLength * 0.5f);
1116 const auto clusterId = mLayerGlobalMeasurements[iLayer][
track.getClusterIndex(iLayer)].clusterId;
1117 mFrame->markUsedCluster(iLayer, clusterId);
1118 int currentROF = mFrame->getClusterROF(iLayer,
track.getClusterIndex(iLayer));
1119 const auto nominalROFTS = mFrame->getROFTiming(iLayer).getROFTimeBounds(currentROF);
1120 const auto expandedROFTS = mFrame->getROFTiming(iLayer).getROFTimeBounds(currentROF,
true);
1123 nominalTS = nominalROFTS;
1124 expandedTS = expandedROFTS;
1126 if (nominalCompatible) {
1127 if (nominalTS.isCompatible(nominalROFTS)) {
1128 nominalTS += nominalROFTS;
1130 nominalCompatible =
false;
1133 if (!expandedTS.isCompatible(expandedROFTS)) {
1134 LOGP(fatal,
"TS {}+/-{} are incompatible with {}+/-{}, this should not happen!", expandedROFTS.getTimeStamp(), expandedROFTS.getTimeStampError(), expandedTS.getTimeStamp(), expandedTS.getTimeStampError());
1136 expandedTS += expandedROFTS;
1139 track.track.timestamp = (nominalCompatible ? nominalTS : expandedTS).makeSymmetrical();
1141 track.track.timestamp.setTimeStampError(std::min(
track.track.timestamp.getTimeStampError(), smallestROFHalf));
1142 if (!appendGenericTrack(*mFrame, track, mLayerGlobalMeasurements)) {
1143 LOGP(fatal,
"GenericTrack publication failed for an accepted CA track");
1146 if (trkParam.AllowSharingFirstCluster) {
1147 firstClusters[firstLayer].push_back(firstCluster);
1152void TrackerTraits::setNThreads(
int n, std::shared_ptr<tbb::task_arena>& arena)
1154#if defined(OPTIMISATION_OUTPUT)
1155 mTaskArena = std::make_shared<tbb::task_arena>(1);
1157 if (arena ==
nullptr) {
1158 mTaskArena = std::make_shared<tbb::task_arena>(std::abs(
n));
1159 LOGP(info,
"Setting tracker with {} threads.",
n);
Cross-timeframe output-size prediction.
Shared CA tracking configuration for ITS and MFT.
CA geometric triplet types with hole-layer support.
Shared index-table utilities for periodic-phi surface charts.
std::vector< o2::MCCompLabel > labels
std::vector< o2::its::TrackITS > tracks
bounded_vector< float > bins
Lock-free slot allocator and single-pass sink.
Runtime-plan-owned, detector-neutral CA workspace.
GPU-portable whole-track seed for common CA tracking.
static constexpr KeyType makeKey(SlabSite site, int iteration, int variant, int slot) noexcept
void runTraversal(IterationContext &view)
Shared CA tracker traits: same ITS-style tracklet/cell/road logic; MFT uses x-y LUT and forward refit...
GLboolean GLboolean GLboolean b
GLenum const GLfloat * params
GLenum GLuint GLint GLint layer
GLboolean GLboolean GLboolean GLboolean a
uint8_t int statusCode int
double component(const Vec3 &vector, int dimension)
double angularTolerance(double radius)
Angular tolerance equivalent to a kTolerance arc length at the given radius.
constexpr int UnusedIndex
int const float const TrackSeed< NLayers > & seed
const bool const int TrackITSInternal< NLayers > & track
bool fitTrackSeedLegs(const TrackSeed &seed, const TimeFrame &frame, gsl::span< const gsl::span< const GlobalMeasurement > > layerGlobals, SurfaceCatalogView surfaceCatalog, float bz, bool shiftReferenceToMeasurement, float maxChi2ClusterAttachment, float maxChi2NDF, bool repeatRefitOut, gsl::span< const float > minPt, SurfaceTrackState &outParamIn, SurfaceTrackState &outParamOut, float &outChi2) noexcept
void deepVectorClear(std::vector< T > &vec)
detail::Identifier< LayerIdTag, uint16_t > LayerId
bool fitAdjacentTripletFactors(const TripletFitFactor &firstFactor, const TripletFitFactor &secondFactor, const std::array< GlobalMeasurement, 4 > &measurements, const std::array< float, 2 > &angularVariance, AdjacentTripletFitResult &result) noexcept
detail::Identifier< CellPathIdTag, uint16_t > CellPathId
bool makeTripletFitFactor(const std::array< GlobalMeasurement, 3 > &measurements, TripletFitFactor &factor) noexcept
bool projectTrackletSearchWindow(const GlobalMeasurement &sourceMeasurement, const o2::its::Vertex &vertex, float beamPositionVariance, SurfaceKind kind, const TrackletProjectionCache &edgeCache, const o2::itsmft::IndexTableUtilsCore &indexUtils, float nSigmaCut, TrackletSearchWindow &out)
Enum< T >::Iterator begin(Enum< T >)
a couple of static helper functions to create timestamp values for CCDB queries or override obsolete ...
std::string to_string(gsl::span< T, Size > span)
bool PerPrimaryVertexProcessing
TrackingKernelParameters kernelParameters
IterationParameters parameters
auto edgeIds() const noexcept
TraversalTopologyView topology
gsl::span< const gsl::span< const GlobalMeasurement > > layerGlobalMeasurements
TimeFrameScratch & scratch
const IterationConfiguration & configuration
auto & getGenericTracks()
auto & getTrackClusterIndices()
int getMaxVerticesPerROF() const noexcept