Project
Loading...
Searching...
No Matches
Tracker.cxx
Go to the documentation of this file.
1// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
15
17
18#include <algorithm>
19#include <array>
20#include <chrono>
21#include <cmath>
22#include <cstdlib>
23#include <gsl/span>
24#include <limits>
25#include <numeric>
26#include <queue>
27#include <ranges>
28#include <stdexcept>
29#include <string>
30#include <utility>
31
33#include "Framework/Logger.h"
34#include "GPUCommonMath.h"
39
41{
42
43namespace
44{
45constexpr std::size_t kindIndex(SurfaceKind kind) noexcept
46{
47 return kind == SurfaceKind::Cylinder ? 0u : 1u;
48}
49
50TrackingKernelParameters bindTrackingKernelParameters(const IterationParameters& params) noexcept
51{
52 TrackingKernelParameters out;
53 out.trackletMinPt = params.TrackletMinPt;
54 out.nSigmaCut = params.NSigmaCut;
55 out.maxChi2ClusterAttachment = params.MaxChi2ClusterAttachment;
56 out.maxChi2NDF = params.MaxChi2NDF;
57 out.pvResolution = params.PVres;
58 return out;
59}
60
61} // namespace
62
63namespace
64{
65void validateSparsePlan(const IterationConfiguration& configuration, int iteration, const TraversalTopologyView& layout)
66{
67 const auto fail = [iteration]() { throw std::invalid_argument{"CA traversal: sparse topology mismatch (iteration " + std::to_string(iteration) + ")"}; };
68 const auto& topology = layout;
69 if (layout.catalog.surfaces == nullptr || layout.catalog.nSurfaces == 0 ||
70 (topology.nEdges != 0 && (topology.edges == nullptr || topology.pathsByFirstEdgeOffsets == nullptr)) ||
71 (topology.nPaths != 0 && (topology.paths == nullptr || topology.pathsByFirstEdge == nullptr))) {
72 fail();
73 }
74
75 const auto edges = configuration.edgeIds();
76 const auto cells = configuration.cellIds();
77 if (edges.empty() || edges.size() > topology.nEdges || cells.size() > topology.nPaths) {
78 fail();
79 }
80 for (const auto id : edges) {
81 if (!id.isValid() || id.value() >= topology.nEdges || !configuration.getEdgeSlot(id)) {
82 fail();
83 }
84 const auto& edge = topology.getEdge(id);
85 if (!configuration.hasLayer(edge.from) || !configuration.hasLayer(edge.to)) {
86 fail();
87 }
88 }
89 for (const auto id : cells) {
90 if (!id.isValid() || id.value() >= topology.nPaths || !configuration.getCellSlot(id)) {
91 fail();
92 }
93 const auto& path = topology.getPath(id);
94 const auto& firstEdge = topology.getEdge(path.first);
95 const auto& secondEdge = topology.getEdge(path.second);
96 if (!configuration.getEdgeSlot(path.first) || !configuration.getEdgeSlot(path.second) ||
97 !configuration.hasLayer(firstEdge.from) || !configuration.hasLayer(firstEdge.to) ||
98 !configuration.hasLayer(secondEdge.to) ||
99 !configuration.topology.activeLayers.has(firstEdge.from.value()) ||
100 !configuration.topology.activeLayers.has(firstEdge.to.value()) ||
101 !configuration.topology.activeLayers.has(secondEdge.to.value())) {
102 fail();
103 }
104 }
105 for (const auto id : configuration.topology.scheduledPaths) {
106 if (!configuration.getCellSlot(id)) {
107 fail();
108 }
109 }
110 for (const auto id : configuration.topology.roadStartPaths) {
111 if (!configuration.getCellSlot(id)) {
112 fail();
113 }
114 }
115}
116
117void prepareDetectorConfiguration(DetectorConfiguration& configuration, const DetectorParameters& parameters)
118{
119 const auto catalog = configuration.getSurfaceCatalog();
120 const auto surfaceCount = configuration.size();
121 if (surfaceCount == 0 || surfaceCount > MaxLayoutSurfaces ||
122 parameters.AddTimeError.size() < surfaceCount ||
123 parameters.SystError2Col.size() < surfaceCount ||
124 parameters.SystError2Row.size() < surfaceCount ||
125 parameters.LayerResolution.size() < surfaceCount) {
126 throw std::invalid_argument{"CA traversal: invalid surface parameters"};
127 }
128 configuration.addTimeError.assign(parameters.AddTimeError.begin(), parameters.AddTimeError.begin() + surfaceCount);
129 configuration.layerResolution.assign(parameters.LayerResolution.begin(), parameters.LayerResolution.begin() + surfaceCount);
130 configuration.systError2Row.assign(parameters.SystError2Row.begin(), parameters.SystError2Row.begin() + surfaceCount);
131 configuration.systError2Col.assign(parameters.SystError2Col.begin(), parameters.SystError2Col.begin() + surfaceCount);
132 configuration.positionResolutions.resize(surfaceCount);
133 std::array<SurfaceChartRange, MaxLayoutSurfaces> chartRanges{};
134 for (std::size_t position = 0; position < surfaceCount; ++position) {
135 const auto surface = LayerId{static_cast<uint16_t>(position)};
136 const auto& descriptor = catalog.getSurface(surface);
137 chartRanges[position] = descriptor.chartRange;
138 configuration.positionResolutions[position] = o2::gpu::CAMath::Sqrt(
139 0.5f * (parameters.SystError2Col[position] + parameters.SystError2Row[position]) +
140 parameters.LayerResolution[position] * parameters.LayerResolution[position]);
141 }
142 if (!configuration.indexTableConfigs.reset(catalog)) {
143 throw std::invalid_argument{"CA traversal: invalid index table configuration"};
144 }
145 const gsl::span<const SurfaceChartRange> chartRangeView{chartRanges.data(), surfaceCount};
146 for (const auto kind : {SurfaceKind::Cylinder, SurfaceKind::Disk}) {
147 if (configuration.indexTableConfigs.hasKind(kind) &&
148 !configureIndexTableUtils(configuration.indexTableConfigs.forKind(kind), parameters,
149 static_cast<int>(surfaceCount), kind, chartRangeView)) {
150 throw std::invalid_argument{"CA traversal: invalid index table configuration"};
151 }
152 }
153}
154
155void prepareIterationConfiguration(const DetectorConfiguration& detector,
156 IterationConfiguration& configuration, int iteration)
157{
158 const auto topology = configuration.getTopologyView(detector.getSurfaceCatalog());
159 const auto& parameters = configuration.parameters;
160 const auto layerCount = configuration.topology.nLayers;
161 if (layerCount == 0 || layerCount > MaxLayoutSurfaces ||
162 parameters.NLayers != static_cast<int>(layerCount)) {
163 throw std::invalid_argument{"CA traversal: legacy material mismatch (iteration " + std::to_string(iteration) + ")"};
164 }
165 if (!topology.catalog.surfaces || topology.catalog.nSurfaces < layerCount ||
166 detector.positionResolutions.size() < layerCount ||
167 detector.indexTableConfigs.size() < layerCount) {
168 throw std::invalid_argument{"CA traversal: invalid surface parameters (iteration " + std::to_string(iteration) + ")"};
169 }
170
171 for (uint16_t position = 0; position < layerCount; ++position) {
172 const auto surface = LayerId{position};
173 const auto& descriptor = topology.getSurface(surface);
174 if (descriptor.kind != SurfaceKind::Cylinder && descriptor.kind != SurfaceKind::Disk) {
175 throw std::invalid_argument{"CA traversal: unsupported surface kind (iteration " + std::to_string(iteration) + ")"};
176 }
177 const auto& material = descriptor.material;
178 if (!o2::gpu::GPUCommonMath::Finite(material.xOverX0) || material.xOverX0 < 0.f ||
179 !o2::gpu::GPUCommonMath::Finite(material.arealDensityGPerCm2) || material.arealDensityGPerCm2 < 0.f) {
180 throw std::invalid_argument{"CA traversal: invalid surface parameters (iteration " + std::to_string(iteration) + ")"};
181 }
182 }
183
184 configuration.kernelParameters = bindTrackingKernelParameters(parameters);
185 if (!configuration.kernelParameters.isValid()) {
186 throw std::invalid_argument{"CA traversal: invalid surface parameters (iteration " + std::to_string(iteration) + ")"};
187 }
188 validateSparsePlan(configuration, iteration, topology);
189}
190
191float diskLayerMultipleScatteringAngle(float layerxX0, float layerRadius, float referenceCoordinate, float trackletMinPt)
192{
193 const float invP = 1.f / trackletMinPt;
194 const float tanlRef = (std::abs(layerRadius) > 1e-6f)
195 ? referenceCoordinate / layerRadius
196 : 0.f;
197 const float absTanl = std::abs(tanlRef);
198 const float cscLambda = (absTanl > 1e-6f)
199 ? std::sqrt(1.f + tanlRef * tanlRef) / absTanl
200 : 1e6f;
201 return 0.0136f * invP * std::sqrt(layerxX0 * cscLambda);
202}
203
204float clampEdgeCurvature(float oneOverR, float outerRadius) noexcept
205{
206 return (outerRadius > 0.f && 0.5f * oneOverR >= 1.f / outerRadius)
207 ? (2.f / outerRadius) - o2::constants::math::Almost0
208 : oneOverR;
209}
210
211struct EdgeScatteringBendingPrep {
212 float msAngle;
213 float phiCut;
214};
215
216EdgeScatteringBendingPrep prepareEdgeScatteringAndBending(
217 gsl::span<const float> perLayerMSAngle, int fromLayer, int toLayer,
218 float r1, float r2, float clampedOneOverR, float res1, float res2) noexcept
219{
220 float ms2 = 0.f;
221 for (int layer = fromLayer; layer < toLayer; ++layer) {
222 ms2 += o2::its::math_utils::Sq(perLayerMSAngle[layer]);
223 }
224 const float msAngle = o2::gpu::CAMath::Sqrt(ms2);
225 const float cosTheta1half = o2::gpu::CAMath::Sqrt(1.f - o2::its::math_utils::Sq(0.5f * r1 * clampedOneOverR));
226 const float cosTheta2half = o2::gpu::CAMath::Sqrt(1.f - o2::its::math_utils::Sq(0.5f * r2 * clampedOneOverR));
227 const float x = (r2 * cosTheta1half) - (r1 * cosTheta2half);
228 const float delta = o2::gpu::CAMath::Sqrt(1.f / (1.f - 0.25f * o2::its::math_utils::Sq(x * clampedOneOverR)) *
229 (o2::its::math_utils::Sq((0.25f * r1 * r2 * o2::its::math_utils::Sq(clampedOneOverR) / cosTheta2half) + cosTheta1half) * o2::its::math_utils::Sq(res1) +
230 o2::its::math_utils::Sq((0.25f * r1 * r2 * o2::its::math_utils::Sq(clampedOneOverR) / cosTheta1half) + cosTheta2half) * o2::its::math_utils::Sq(res2)));
231 const float phiCut = o2::gpu::CAMath::Min(o2::gpu::CAMath::ASin(0.5f * x * clampedOneOverR) + 2.f * msAngle + delta, o2::constants::math::PI * 0.5f);
232 return {msAngle, phiCut};
233}
234
235void prepareTraversalEdgeTolerances(
236 IterationContext& context,
237 int iteration)
238{
239 auto& scratch = context.scratch;
240 const auto& graph = context.topology;
241 const auto& trkParam = context.configuration.parameters;
242 const auto& topology = graph;
243
244 const int layerCount = context.configuration.topology.nLayers;
245 std::array<float, MaxLayoutSurfaces> msAngles{};
246 for (int iLayer{0}; iLayer < layerCount; ++iLayer) {
247 const auto surface = LayerId{static_cast<uint16_t>(iLayer)};
248 const auto& descriptor = topology.getSurface(surface);
249 if (descriptor.kind == SurfaceKind::Cylinder) {
250 msAngles[iLayer] = o2::its::math_utils::MSangle(0.14f, trkParam.TrackletMinPt, descriptor.material.xOverX0);
251 } else {
252 msAngles[iLayer] = diskLayerMultipleScatteringAngle(
253 descriptor.material.xOverX0, context.detectorConfiguration.getRepresentativeRadius(surface),
254 descriptor.referenceCoordinate, trkParam.TrackletMinPt);
255 }
256 }
257
258 auto& edgeMSAngles = scratch.getEdgeMSAngles();
259 auto& edgePhiCuts = scratch.getEdgePhiCuts();
260 const float oneOverR{0.001f * 0.3f * std::abs(context.bz) / trkParam.TrackletMinPt};
261 for (const auto edgeId : context.configuration.edgeIds()) {
262 const auto edgeSlot = context.configuration.getEdgeSlot(edgeId);
263 if (!edgeSlot) {
264 throw std::invalid_argument{"CA traversal: traversal binding mismatch (iteration " + std::to_string(iteration) + ")"};
265 }
266 const auto& edge = topology.getEdge(edgeId);
267 if (!context.configuration.hasLayer(edge.from) || !context.configuration.hasLayer(edge.to)) {
268 throw std::invalid_argument{"CA traversal: traversal binding mismatch (iteration " + std::to_string(iteration) + ")"};
269 }
270 const int fromLayer = edge.from.value();
271 const int toLayer = edge.to.value();
272 const float r1 = std::min(context.detectorConfiguration.getRepresentativeRadius(edge.from), context.detectorConfiguration.getRepresentativeRadius(edge.to));
273 const float r2 = std::max(context.detectorConfiguration.getRepresentativeRadius(edge.from), context.detectorConfiguration.getRepresentativeRadius(edge.to));
274 const float edgeOneOverR = clampEdgeCurvature(oneOverR, r2);
275 const float res1 = o2::gpu::CAMath::Hypot(trkParam.PVres, context.detectorConfiguration.positionResolutions[fromLayer]);
276 const float res2 = o2::gpu::CAMath::Hypot(trkParam.PVres, context.detectorConfiguration.positionResolutions[toLayer]);
277 const auto prep = prepareEdgeScatteringAndBending(
278 gsl::span<const float>(msAngles.data(), static_cast<std::size_t>(layerCount)), fromLayer, toLayer, r1, r2, edgeOneOverR, res1, res2);
279 edgeMSAngles[*edgeSlot] = prep.msAngle;
280 edgePhiCuts[*edgeSlot] = prep.phiCut;
281 }
282}
283
284} // namespace
285
286void Tracker::initializeIteration(IterationContext& context) const
287{
288 const int iteration = context.iteration;
289 if (iteration < 0 || static_cast<size_t>(iteration) >= mIterations.size()) {
290 throw std::invalid_argument{"CA traversal: iteration out of range (iteration " + std::to_string(iteration) + ")"};
291 }
292 const auto& configuration = context.configuration;
293 const auto& parameters = configuration.parameters;
294 auto& frame = context.frame;
295 auto& scratch = context.scratch;
296 const auto layerCount = configuration.topology.nLayers;
297
298 if (parameters.PassFlags[IterationStep::FirstPass]) {
299 frame.prepareIndexTables(context.detectorConfiguration.indexTableConfigs);
300 } else {
301 for (std::size_t position = 0; position < layerCount; ++position) {
302 if (!indexTableConfigurationsMatch(context.detectorConfiguration.indexTableConfigs[position],
303 frame.getIndexTableUtils(static_cast<int>(position)),
304 static_cast<int>(layerCount))) {
305 throw std::invalid_argument{"CA traversal: index table configuration mismatch (iteration " + std::to_string(iteration) + ")"};
306 }
307 }
308 }
309 if (parameters.PassFlags[IterationStep::RebuildClusterLUT]) {
310 frame.prepareClusters(static_cast<int>(layerCount));
311 }
312
313 const auto edgeIds = context.configuration.edgeIds();
314 const auto cellIds = context.configuration.cellIds();
315 std::array<std::size_t, MaxLayoutEdges> trackletLookupSizes;
316 for (const auto edgeId : edgeIds) {
317 const auto from = context.topology.getEdge(edgeId).from;
318 if (!configuration.hasLayer(from) || from.value() >= context.layerGlobalMeasurements.size()) {
319 throw std::invalid_argument{"CA traversal: traversal binding mismatch (iteration " + std::to_string(iteration) + ")"};
320 }
321 trackletLookupSizes[edgeId.value()] = context.layerGlobalMeasurements[from.value()].size();
322 }
323 scratch.beginIteration(edgeIds.size(), cellIds.size(), {trackletLookupSizes.data(), edgeIds.size()});
324
325 // Sorted clusters are a locator cache. Validate every enabled ROF that can
326 // participate in a configured edge, including LUT-reuse paths.
327 // Keep spans local until validation and kind setup complete.
328 std::array<bool, MaxLayoutSurfaces> candidateReachableLayers{};
329 for (const auto edgeId : edgeIds) {
330 const auto& edge = context.topology.getEdge(edgeId);
331 if (!configuration.hasLayer(edge.from) || !configuration.hasLayer(edge.to)) {
332 throw std::invalid_argument{"CA traversal: sparse topology mismatch (iteration " + std::to_string(iteration) + ")"};
333 }
334 candidateReachableLayers[edge.from.value()] = true;
335 candidateReachableLayers[edge.to.value()] = true;
336 }
337 for (std::size_t layer = 0; layer < layerCount; ++layer) {
338 if (!candidateReachableLayers[layer]) {
339 continue;
340 }
341 const auto measurements = context.layerGlobalMeasurements[layer];
342 const auto rofBoundaries = frame.getROFrameClusters(static_cast<int>(layer));
343 const auto rofMask = frame.getROFViews(static_cast<int>(layer)).mask;
344 // Orchestration-only users may omit the mask; without it no ROF is reachable.
345 if (rofMask.mFlatMask == nullptr || rofMask.mLayerROFOffsets == nullptr) {
346 continue;
347 }
348 for (int rof = 0; rof < frame.getNrof(static_cast<int>(layer)); ++rof) {
349 const auto sorted = frame.getClustersOnLayer(rof, static_cast<int>(layer));
350 if (sorted.empty()) {
351 continue;
352 }
353 if (!frame.isROFEnabled(static_cast<int>(layer), rof)) {
354 continue;
355 }
356 const int first = rofBoundaries[rof];
357 const int last = rofBoundaries[rof + 1];
358 if (first < 0 || last < first || last > static_cast<int>(measurements.size()) ||
359 sorted.size() != static_cast<size_t>(last - first)) {
360 throw std::invalid_argument{"CA traversal: normalized measurement mismatch (iteration " + std::to_string(iteration) + ")"};
361 }
362 std::vector<uint32_t> seen;
363 seen.reserve(sorted.size());
364 for (const auto& measurement : sorted) {
365 if (!measurement.hasValidClusterId() ||
366 frame.getSurfaceMeasurement(LayerId{static_cast<uint16_t>(layer)}, measurement.clusterId) == nullptr) {
367 throw std::invalid_argument{"CA traversal: normalized measurement mismatch (iteration " + std::to_string(iteration) + ")"};
368 }
369 seen.push_back(measurement.clusterId);
370 }
371 std::sort(seen.begin(), seen.end());
372 if (std::adjacent_find(seen.begin(), seen.end()) != seen.end()) {
373 throw std::invalid_argument{"CA traversal: normalized measurement mismatch (iteration " + std::to_string(iteration) + ")"};
374 }
375 }
376 }
377
378 prepareTraversalEdgeTolerances(context, iteration);
379}
380
381gsl::span<const gsl::span<const GlobalMeasurement>> Tracker::prepareTimeFrame(
382 TimeFrame& frame, std::array<gsl::span<const GlobalMeasurement>, MaxLayoutSurfaces>& measurements) const
383{
384 const auto layerCount = mIterations.front().topology.nLayers;
385 for (uint16_t position = 0; position < layerCount; ++position) {
386 const auto surface = LayerId{position};
387 const auto globals = frame.getGlobalMeasurements(surface);
388 if (globals.size() > static_cast<std::size_t>(std::numeric_limits<int>::max())) {
389 throw std::invalid_argument{"CA traversal: normalized measurement mismatch"};
390 }
391 for (const auto& global : globals) {
392 if (!global.hasValidClusterId() || global.clusterId > static_cast<uint32_t>(std::numeric_limits<int>::max()) ||
393 frame.getSurfaceMeasurement(surface, global.clusterId) == nullptr) {
394 throw std::invalid_argument{"CA traversal: normalized measurement mismatch"};
395 }
396 }
397 const auto rofBoundaries = frame.getROFrameClusters(static_cast<int>(position));
398 if (rofBoundaries.empty() || rofBoundaries.front() != 0 ||
399 rofBoundaries.back() != static_cast<int>(globals.size())) {
400 throw std::invalid_argument{"CA traversal: normalized measurement mismatch"};
401 }
402 for (std::size_t rof = 0; rof + 1 < rofBoundaries.size(); ++rof) {
403 const int first = rofBoundaries[rof];
404 const int last = rofBoundaries[rof + 1];
405 if (first < 0 || last < first || last > static_cast<int>(globals.size())) {
406 throw std::invalid_argument{"CA traversal: normalized measurement mismatch"};
407 }
408 }
409 measurements[position] = globals;
410 }
411 return {measurements.data(), layerCount};
412}
413
414bool Tracker::initialize(TimeFrame& frame, const TrackerInitialization& configuration)
415{
416 if (frame.isConfigured()) {
417 LOGP(error, "CA tracker initialization failed: TimeFrame is already configured");
418 return false;
419 }
420 if (configuration.plan.iterations.empty()) {
421 LOGP(error, "CA tracker initialization failed: no tracking iterations configured");
422 return false;
423 }
424 if (configuration.catalog.surfaces == nullptr || configuration.catalog.nSurfaces == 0) {
425 LOGP(error, "CA tracker initialization failed: missing surface catalog");
426 return false;
427 }
428 if (!configuration.memoryPool) {
429 LOGP(error, "CA tracker initialization failed: missing memory pool");
430 return false;
431 }
432
433 DetectorConfiguration detector{gsl::span<const SurfaceDescriptor>{configuration.catalog.surfaces,
434 configuration.catalog.nSurfaces},
435 configuration.componentOffsets, configuration.holeLayers};
436 if (!detector.valid()) {
437 LOGP(error, "CA tracker initialization failed: invalid detector layout (error={})", static_cast<int>(detector.getError()));
438 return false;
439 }
440 try {
441 prepareDetectorConfiguration(detector, configuration.plan.detector);
442 } catch (const std::invalid_argument& err) {
443 LOGP(error, "CA tracker initialization failed: {}", err.what());
444 return false;
445 }
446
447 std::vector<IterationConfiguration> iterations;
448 std::size_t maxEdges = 0;
449 std::size_t maxCells = 0;
450 iterations.reserve(configuration.plan.iterations.size());
451
452 for (std::size_t iteration = 0; iteration < configuration.plan.iterations.size(); ++iteration) {
453 const auto& input = configuration.plan.iterations[iteration];
454 if (input.NLayers != 0 && input.NLayers != detector.size()) {
455 LOGP(error, "CA tracker initialization failed at iteration {}: configured layer count {} differs from detector size {}",
456 iteration, input.NLayers, detector.size());
457 return false;
458 }
459 const auto topology = deriveTraversalTopology(detector, input);
460 if (!topology.ok()) {
461 LOGP(error, "CA tracker initialization failed at iteration {}: invalid traversal topology (error={})",
462 iteration, static_cast<int>(topology.error));
463 return false;
464 }
465 IterationConfiguration iterationConfiguration;
466 iterationConfiguration.parameters = input;
467 iterationConfiguration.parameters.NLayers = static_cast<int>(detector.size());
468 iterationConfiguration.topology = *topology.topology;
469 try {
470 prepareIterationConfiguration(detector, iterationConfiguration, static_cast<int>(iteration));
471 } catch (const std::invalid_argument& err) {
472 LOGP(error, "CA tracker initialization failed at iteration {}: {}", iteration, err.what());
473 return false;
474 }
475 maxEdges = std::max(maxEdges, iterationConfiguration.topology.edges.size());
476 maxCells = std::max(maxCells, iterationConfiguration.topology.paths.size());
477 iterations.push_back(std::move(iterationConfiguration));
478 }
479
480 if (!frame.configure(std::move(detector), maxEdges, maxCells, configuration.memoryPool)) {
481 LOGP(error, "CA tracker initialization failed: TimeFrame rejected the detector configuration or workspace capacity");
482 return false;
483 }
484 mExecutionPolicy = configuration.plan.execution;
485 mIterations = std::move(iterations);
486 mFrame = &frame;
487 return true;
488}
489
490bool Tracker::isConfiguredFor(const TimeFrame& frame) const noexcept
491{
492 return mFrame == &frame && !mIterations.empty() && frame.isConfigured();
493}
494
495void Tracker::computeTracksMClabels(TimeFrame& frame) const
496{
497 bounded_vector<MCCompLabel> trackLabels(frame.getMemoryPool().get());
498 if (!frame.hasMCinformation()) {
499 frame.getTrackLabels().swap(trackLabels);
500 return;
501 }
502
503 const auto& tracks = frame.getGenericTracks();
504 const auto& references = frame.getTrackClusterIndices();
505 trackLabels.reserve(tracks.size());
506
507 struct Candidate {
508 MCCompLabel representative;
509 std::size_t count{0};
510 std::size_t lastSeenCluster{0};
511 };
512
513 for (const auto& track : tracks) {
514 if (!isValidTrackRange(track, static_cast<uint32_t>(references.size()))) {
515 throw std::logic_error{"Tracker::computeTracksMClabels(): invalid track cluster-reference range"};
516 }
517
518 std::vector<Candidate> candidates;
519 std::size_t attachedClusters = 0;
520 for (uint32_t index = track.firstClusterRef; index < track.clusterRefEnd; ++index) {
521 const auto& reference = references[index];
522 if (!reference.isValid() || frame.getSurfaceMeasurement(reference.layer, reference.clusterId) == nullptr) {
523 throw std::logic_error{"Tracker::computeTracksMClabels(): unresolved track cluster reference"};
524 }
525
526 ++attachedClusters;
527 for (const auto& label : frame.getLabels(reference.layer, reference.clusterId)) {
528 const auto candidate = std::find_if(candidates.begin(), candidates.end(), [&label](const auto& current) {
529 return label == current.representative;
530 });
531 if (candidate == candidates.end()) {
532 candidates.push_back({label, 1, attachedClusters});
533 } else if (candidate->lastSeenCluster != attachedClusters) {
534 ++candidate->count;
535 candidate->lastSeenCluster = attachedClusters;
536 }
537 }
538 }
539
540 MCCompLabel winner;
541 if (candidates.empty()) {
542 winner.setFakeFlag();
543 } else {
544 const auto best = std::max_element(candidates.begin(), candidates.end(), [](const auto& left, const auto& right) {
545 return left.count < right.count;
546 });
547 winner = best->representative;
548 // A single attached cluster without the winning identity makes the
549 // reconstructed track fake.
550 if (best->count != attachedClusters) {
551 winner.setFakeFlag();
552 }
553 }
554 trackLabels.push_back(winner);
555 }
556
557 frame.getTrackLabels().swap(trackLabels);
558}
559
560void Tracker::configureBeamPosition(TimeFrame& frame) const
561{
562 const auto& params = mIterations.front().parameters;
563 if (!params.UseDiamond) {
564 return;
565 }
566 const auto& detector = frame.getDetectorConfiguration();
567 const float systErrY2 = detector.systError2Row.empty() ? 0.f : detector.systError2Row[0];
568 const float layerRes = detector.layerResolution.empty() ? 0.f : detector.layerResolution[0];
569 frame.setBeamPosition(params.Diamond[0], params.Diamond[1], params.DiamondCov[3], layerRes, systErrY2);
570}
571
573{
574 mRunStatistics = {};
575 if (!isConfiguredFor(frame)) {
576 throw std::invalid_argument{"CA traversal: missing layout"};
577 }
578 const auto start = std::chrono::steady_clock::now();
579 std::vector<std::size_t> acceptedTrackCounts;
580 auto& estimator = frame.getCapacityEstimator();
581 bool estimatorTransactionStarted{false};
582 const auto rollbackEstimator = [&] {
583 if (estimatorTransactionStarted) {
584 estimator.rollbackTransaction();
585 estimatorTransactionStarted = false;
586 }
587 };
588 try {
589 estimator.beginTransaction();
590 estimatorTransactionStarted = true;
591 configureBeamPosition(frame);
592 auto& scratch = frame.getScratch();
593 acceptedTrackCounts.reserve(mIterations.size());
594 std::array<gsl::span<const GlobalMeasurement>, MaxLayoutSurfaces> measurementSpans;
595 const auto layerGlobalMeasurements = prepareTimeFrame(frame, measurementSpans);
596 const auto& memoryPool = frame.getMemoryPool();
597 // Apply a tighter event-local limit when configured; this also lets
598 // workflows and tests inject a resource failure after loading.
599 if (mExecutionPolicy.MaxMemory != std::numeric_limits<size_t>::max() &&
600 memoryPool->getMaxMemory() > mExecutionPolicy.MaxMemory) {
601 memoryPool->setMaxMemory(mExecutionPolicy.MaxMemory);
602 }
603 for (int iteration = 0; iteration < static_cast<int>(mIterations.size()); ++iteration) {
604 const auto& configuration = mIterations[iteration];
605 const auto& trkParam = configuration.parameters;
606 if (trkParam.PassFlags[IterationStep::UseUPCMask]) {
607 frame.useUPCMask();
608 }
609
610 const auto acceptedTrackBegin = frame.getGenericTracks().size();
611 IterationContext context{iteration, frame, scratch,
612 configuration.getTopologyView(frame.getDetectorConfiguration().getSurfaceCatalog()),
613 configuration, layerGlobalMeasurements,
614 frame.getBz()};
615 initializeIteration(context);
616 traits.runTraversal(context);
617 acceptedTrackCounts.push_back(frame.getGenericTracks().size() - acceptedTrackBegin);
618 }
619 computeTracksMClabels(frame);
620 if (std::getenv("O2_ITSMFT_PRINT_SLAB_STATS") != nullptr) {
621 estimator.print();
622 }
623 estimator.commitTransaction();
624 estimatorTransactionStarted = false;
625 } catch (const BoundedMemoryResource::MemoryLimitExceeded& err) {
626 // Recoverable per-TF resource failure: the bounded pool budget was
627 // exceeded for this TimeFrame.
628 LOGP(error, "CA tracker exceeded memory limit: {}", err.what());
629 rollbackEstimator();
630 frame.resetTimeFrame();
631 if (mExecutionPolicy.DropTFUponFailure) {
632 return false;
633 }
634 throw;
635 } catch (const std::bad_alloc& err) {
636 // Some CA scratch containers use the plain heap instead of the bounded
637 // pool, so memory pressure can surface as bad_alloc. Handle it likewise.
638 LOGP(error, "CA tracker allocation failed: {}", err.what());
639 rollbackEstimator();
640 frame.resetTimeFrame();
641 if (mExecutionPolicy.DropTFUponFailure) {
642 return false;
643 }
644 throw;
645 } catch (const std::exception& err) {
646 // Unclassified exceptions are treated as structural and always propagate;
647 // recoverability is not inferred from std::exception alone.
648 LOGP(error, "CA tracker failed with an unclassified exception; treating as structural: {}", err.what());
649 rollbackEstimator();
650 frame.resetTimeFrame();
651 throw;
652 }
653
654 mRunStatistics.elapsedMs = std::chrono::duration<float, std::milli>(std::chrono::steady_clock::now() - start).count();
655 mRunStatistics.acceptedTrackCounts = std::move(acceptedTrackCounts);
656 return true;
657}
658
659} // namespace o2::itsmft::tracking
Tracker orchestrator.
float phiCut
Definition Tracker.cxx:213
float msAngle
Definition Tracker.cxx:212
std::vector< o2::its::TrackITS > tracks
useful math constants
std::vector< SidecarEdge > edges
void runTraversal(IterationContext &view)
bool initialize(TimeFrame &frame, const TrackerInitialization &configuration)
Definition Tracker.cxx:414
bool run(TimeFrame &frame, TrackerTraits &traits)
Definition Tracker.cxx:572
bool isConfiguredFor(const TimeFrame &frame) const noexcept
Definition Tracker.cxx:490
GLint GLenum GLint x
Definition glcorearb.h:403
GLint GLsizei count
Definition glcorearb.h:399
GLuint index
Definition glcorearb.h:781
GLdouble GLdouble right
Definition glcorearb.h:4077
GLint first
Definition glcorearb.h:399
GLint reference
Definition glcorearb.h:5487
GLsizei const GLfloat * value
Definition glcorearb.h:819
GLenum const GLfloat * params
Definition glcorearb.h:272
GLint left
Definition glcorearb.h:1979
GLuint GLsizei const GLchar * label
Definition glcorearb.h:2519
GLsizei const GLchar *const * path
Definition glcorearb.h:3591
GLenum GLuint GLint GLint layer
Definition glcorearb.h:1310
GLuint start
Definition glcorearb.h:469
const bool const bool const int FollowDirection BestTrial TrackITSInternal< NLayers > & best
const bool const int TrackITSInternal< NLayers > & track
bool indexTableConfigurationsMatch(const o2::itsmft::IndexTableUtilsCore &a, const o2::itsmft::IndexTableUtilsCore &b, int activeSurfaceCount) noexcept
detail::Identifier< LayerIdTag, uint16_t > LayerId
Definition IdTypes.h:60
bool configureIndexTableUtils(o2::itsmft::IndexTableUtilsCore &destination, const DetectorParameters &params, int activeSurfaceCount, SurfaceKind kind, gsl::span< const SurfaceChartRange > chartRanges) noexcept
constexpr uint32_t MaxLayoutSurfaces
Definition IdTypes.h:70
std::pmr::vector< T > bounded_vector
TraversalTopologyBuildResult deriveTraversalTopology(const DetectorConfiguration &layout, const o2::itsmft::IterationParameters &parameters)
std::unique_ptr< const o2::dataformats::MCTruthContainer< MCLabel > > getLabels(framework::ProcessingContext &pc, std::string_view dataBind, EventType eventType=EventType::Standard)
std::string to_string(gsl::span< T, Size > span)
Definition common.h:52
bool isValid(std::string alias)
TrackingExecutionPolicy execution
DetectorParameters detector
std::vector< IterationParameters > iterations
bool hasMCinformation() const noexcept
TimeFrameScratch & getScratch()
bool isConfigured() const noexcept
Definition TimeFrame.h:156
CapacityEstimator & getCapacityEstimator() noexcept
Definition TimeFrame.h:151
const SurfaceMeasurement * getSurfaceMeasurement(LayerId layer, uint32_t clusterId) const noexcept
Definition TimeFrame.cxx:79
auto & getMemoryPool() const noexcept
Definition TimeFrame.h:171
bool configure(DetectorConfiguration &&layout, std::size_t maxEdges, std::size_t maxCells, std::shared_ptr< BoundedMemoryResource > memoryPool)
std::shared_ptr< BoundedMemoryResource > memoryPool
Definition Tracker.h:54
std::vector< uint16_t > componentOffsets
Definition Tracker.h:51
std::vector< std::size_t > acceptedTrackCounts
Definition Tracker.h:45
std::vector< Cell > cells