Project
Loading...
Searching...
No Matches
testComputeLayerTrackletsOrchestration.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.
11
12#define BOOST_TEST_MODULE ITSMFT ComputeLayerTracklets orchestration
13#define BOOST_TEST_MAIN
14#define BOOST_TEST_DYN_LINK
15
16#include <array>
17#include <cmath>
18#include <functional>
19#include <limits>
20#include <memory>
21#include <utility>
22#include <stdexcept>
23#include <vector>
24
25#include <boost/test/unit_test.hpp>
26
27#include <oneapi/tbb/task_arena.h>
28
45#include "MFTTracking/Constants.h"
47
49
51using namespace o2::itsmft;
52using namespace o2::itsmft::tracking;
53
54namespace
55{
56
57constexpr float Bz = 0.5f;
58constexpr std::array<unsigned char, 3> OnePixelPattern{1, 1, 0x80};
59
60const TopologyDictionary& dict()
61{
62 static const TopologyDictionary d;
63 return d;
64}
65
66std::vector<LayerId> identitySurfaces(uint16_t nLayers)
67{
68 std::vector<LayerId> mapping;
69 mapping.reserve(nLayers);
70 for (uint16_t i = 0; i < nLayers; ++i) {
71 mapping.push_back(LayerId{i});
72 }
73 return mapping;
74}
75
76std::vector<SurfaceDescriptor> makeCatalog(uint16_t nLayers, o2::detectors::DetID::ID detector, SurfaceKind kind)
77{
78 std::vector<SurfaceDescriptor> surfaces;
79 surfaces.reserve(nLayers);
80 for (uint16_t i = 0; i < nLayers; ++i) {
81 surfaces.push_back(SurfaceDescriptor{i, static_cast<uint8_t>(detector), kind});
82 surfaces.back().chartRange = kind == SurfaceKind::Disk ? SurfaceChartRange{0.1f, 20.f} : SurfaceChartRange{-20.f, 20.f};
83 surfaces.back().referenceCoordinate = kind == SurfaceKind::Disk
85 : 3.f + static_cast<float>(i);
86 // Use the material from the detector surface catalog.
87 const float xOverX0 = detector == o2::detectors::DetID::MFT ? kMFTSurfaces[i % MFTNLayers].material.xOverX0 : kITSSurfaces[i % ITSNLayers].material.xOverX0;
88 surfaces.back().material.xOverX0 = xOverX0;
89 surfaces.back().material.arealDensityGPerCm2 = xOverX0 * o2::its::constants::Radl * o2::its::constants::Rho;
90 }
91 return surfaces;
92}
93
94class PrescribedDecoder
95{
96 public:
97 PrescribedDecoder(o2::detectors::DetID::ID detector, SurfaceKind kind, std::vector<DecodedCluster> clusters)
98 : mDetector{detector}, mKind{kind}, mClusters{std::move(clusters)}
99 {
100 }
101
103 const CompClusterExt& cluster,
104 gsl::span<const unsigned char>::iterator& patterns,
105 const TopologyDictionary* dictionary,
106 uint32_t externalIndex) const
107 {
108 const auto clusterData = o2::itsmft::ioutils::extractClusterData(cluster, patterns, dictionary);
110 if (externalIndex >= mClusters.size()) {
111 return result;
112 }
113 auto decoded = mClusters[externalIndex];
114 decoded.nPixels = clusterData.nPixels;
115 result = decoded;
116 return result;
117 }
118
119 private:
120 o2::detectors::DetID::ID mDetector;
121 SurfaceKind mKind;
122 std::vector<DecodedCluster> mClusters;
123};
124
125struct TrackletSnapshot {
126 int edgeId{-1};
127 std::vector<Tracklet> tracklets;
128 std::vector<int> lookup;
129 o2::its::TimeEstBC expectedTimestamp;
130 bool nonparticipatingEdgesEmpty{false};
131 // Gate 4 Slice 0a additions: full per-(legacy-edgeId) tracklet/LUT
132 // content and (fromLayer,toLayer) identity, for multi-edge
133 // candidate-set/order/LUT parity checks that go beyond the single
134 // `edgeId` above. Indices across these three vectors correspond
135 // 1:1, in ascending legacy edgeId order.
136 std::vector<int> allEdgeFromLayer;
137 std::vector<int> allEdgeToLayer;
138 std::vector<std::vector<Tracklet>> allTracklets;
139 std::vector<std::vector<int>> allLookups;
140};
141
142template <int NLayers>
143TrackletSnapshot runFixture(o2::detectors::DetID::ID detector,
145 SurfaceKind tag,
146 std::vector<DecodedCluster> decoded,
147 int nThreads,
148 std::function<void(ReferenceTrackingParameters&)> customizeParams = {},
149 LayerMask holeLayers = {})
150{
151 auto pool = std::make_shared<BoundedMemoryResource>();
152 TimeFrame frame;
153 Tracker tracker;
154 TrackerTraits traits;
155 std::shared_ptr<tbb::task_arena> arena;
156 std::vector<ReferenceTrackingParameters> params(1);
157 resetReferenceTrackingParameters(params[0], detector);
158 params[0].UseDiamond = true;
159 params[0].CreateArtefactLabels = false;
160 params[0].PassFlags.reset();
161 params[0].PassFlags.set(IterationStep::FirstPass, IterationStep::RebuildClusterLUT);
162 if (customizeParams) {
163 customizeParams(params[0]);
164 }
165
166 traits.setNThreads(nThreads, arena);
167 frame.setBz(Bz);
168
169 const auto orderedSurfaces = identitySurfaces(static_cast<uint16_t>(NLayers));
170 const auto catalog = makeCatalog(static_cast<uint16_t>(NLayers), detector, kind);
171 const SurfaceCatalogView catalogView{catalog.data(), static_cast<uint32_t>(catalog.size())};
172 TrackerInitialization configuration;
173 configuration.catalog = catalogView;
174 configuration.memoryPool = pool;
175 configuration.holeLayers = holeLayers;
177 BOOST_REQUIRE(tracker.initialize(frame, configuration));
178 auto& tf = frame.getScratch();
179 const auto& layout = frame.getDetectorConfiguration();
180
181 std::vector<CompClusterExt> compactClusters;
182 std::vector<unsigned char> patterns;
183 compactClusters.reserve(decoded.size());
184 patterns.reserve(decoded.size() * OnePixelPattern.size());
185 for (const auto& cluster : decoded) {
186 compactClusters.emplace_back(0, 0, CompCluster::InvalidPatternID, cluster.layer);
187 patterns.insert(patterns.end(), OnePixelPattern.begin(), OnePixelPattern.end());
188 }
189 const std::vector<ROFRecord> rofs{ROFRecord{{100, 5}, 0, 0, static_cast<int>(compactClusters.size())}};
190 PrescribedDecoder decoder{detector, kind, std::move(decoded)};
191 BOOST_REQUIRE_NO_THROW(test::loadTimeFrameSource(frame, decoder, o2::InteractionRecord{50, 5}, o2::its::LayerTiming{.mROFLength = 40},
192 compactClusters, patterns, rofs, &dict(), nullptr, detector,
193 gsl::span<const LayerId>{orderedSurfaces}, layout.getSurfaceCatalog()));
194
195 o2::its::LayerTiming layerTiming{};
196 layerTiming.mNROFsTF = 1;
197 layerTiming.mROFLength = 40;
199 for (int layer = 0; layer < NLayers; ++layer) {
200 rofTable.defineLayer(layer, layerTiming);
201 }
202 rofTable.init();
203 // Real production workflow timing construction
204 // always builds and sets this alongside the ROFOverlapTable above, from
205 // the same per-layer LayerTiming, regardless of UseDiamond -- the diamond
206 // vertex derived per-ROF for tracklet finding (TrackerTraits.cxx) is
207 // checked through the genuine isVertexCompatible() on this table, not a
208 // useDiamond-skipped shortcut, so this fixture needs it populated too.
210 for (int layer = 0; layer < NLayers; ++layer) {
211 vtxTable.defineLayer(layer, layerTiming);
212 }
213 vtxTable.init();
215 mask.resetMask();
216 for (int layer = 0; layer < NLayers; ++layer) {
217 mask.setROFsEnabled(layer, 0, 1, 1);
218 }
219 frame.setROFViews(RuntimeROFViews{rofTable.getView(), vtxTable.getView(), mask.getView(), {}});
220
221 std::array<gsl::span<const GlobalMeasurement>, MaxLayoutSurfaces> measurementSpans;
222 auto view = TrackerTestAccess::prepare(tracker, frame, 0, measurementSpans);
223 BOOST_CHECK(view.layerGlobalMeasurements.data() == measurementSpans.data());
224 const auto layoutView = view.topology;
225
226 // Prepared edge arrays must be complete and finite.
227 {
228 const auto preparedTopology = layoutView;
229 const auto& msAngles = tf.getEdgeMSAngles();
230 const auto& phiCuts = tf.getEdgePhiCuts();
231 BOOST_REQUIRE_EQUAL(msAngles.size(), static_cast<size_t>(preparedTopology.nEdges));
232 BOOST_REQUIRE_EQUAL(phiCuts.size(), static_cast<size_t>(preparedTopology.nEdges));
233 for (int id = 0; id < preparedTopology.nEdges; ++id) {
234 BOOST_CHECK(std::isfinite(msAngles[id]));
235 BOOST_CHECK(std::isfinite(phiCuts[id]));
236 }
237 }
238
239 const auto topology = layoutView;
240 int edgeId = -1;
241 for (int id = 0; id < topology.nEdges; ++id) {
242 const auto& edge = topology.getEdge(EdgeId{static_cast<uint16_t>(id)});
243 if (edge.from.value() == 0 && edge.to.value() == 1) {
244 edgeId = id;
245 break;
246 }
247 }
248 BOOST_REQUIRE_GE(edgeId, 0);
249
251
252 TrackletSnapshot result;
253 result.edgeId = edgeId;
254 result.expectedTimestamp = frame.getROFOverlapView().getTimeStamp(0, 0, 1, 0);
255 const auto& tracklets = tf.getTracklets()[edgeId];
256 result.tracklets.assign(tracklets.begin(), tracklets.end());
257 const auto& lookup = tf.getTrackletsLookupTable()[edgeId];
258 result.lookup.assign(lookup.begin(), lookup.end());
259 result.nonparticipatingEdgesEmpty = true;
260 for (int id = 0; id < topology.nEdges; ++id) {
261 if (id != edgeId && !tf.getTracklets()[id].empty()) {
262 result.nonparticipatingEdgesEmpty = false;
263 break;
264 }
265 }
266
267 // Gate 4 Slice 0a: full per-edge snapshot, ascending legacy
268 // edgeId order, for multi-edge candidate-set/order/LUT parity
269 // checks (see e.g. ItsIdentityLayoutTrackletsSpanMultipleAdjacentEdgesInOrder).
270 for (int id = 0; id < topology.nEdges; ++id) {
271 const auto& edge = topology.getEdge(EdgeId{static_cast<uint16_t>(id)});
272 result.allEdgeFromLayer.push_back(edge.from.value());
273 result.allEdgeToLayer.push_back(edge.to.value());
274 const auto& idTracklets = tf.getTracklets()[id];
275 result.allTracklets.emplace_back(idTracklets.begin(), idTracklets.end());
276 const auto& idLookup = tf.getTrackletsLookupTable()[id];
277 result.allLookups.emplace_back(idLookup.begin(), idLookup.end());
278 }
279 return result;
280}
281
282void checkSame(const TrackletSnapshot& serial, const TrackletSnapshot& parallel)
283{
284 BOOST_CHECK_EQUAL(serial.edgeId, parallel.edgeId);
285 BOOST_REQUIRE_EQUAL(serial.tracklets.size(), parallel.tracklets.size());
286 BOOST_CHECK_EQUAL_COLLECTIONS(serial.lookup.begin(), serial.lookup.end(), parallel.lookup.begin(), parallel.lookup.end());
287 for (size_t i = 0; i < serial.tracklets.size(); ++i) {
288 BOOST_CHECK(serial.tracklets[i] == parallel.tracklets[i]);
289 BOOST_CHECK_EQUAL(serial.tracklets[i].tanLambda, parallel.tracklets[i].tanLambda);
290 BOOST_CHECK_EQUAL(serial.tracklets[i].phi, parallel.tracklets[i].phi);
291 BOOST_CHECK_EQUAL(serial.tracklets[i].getTimeStamp().getTimeStamp(), parallel.tracklets[i].getTimeStamp().getTimeStamp());
292 BOOST_CHECK_EQUAL(serial.tracklets[i].getTimeStamp().getTimeStampError(), parallel.tracklets[i].getTimeStamp().getTimeStampError());
293 }
294}
295
296void checkExactTracklet(const TrackletSnapshot& snapshot, float expectedTanLambda, float expectedPhi)
297{
298 BOOST_REQUIRE_EQUAL(snapshot.tracklets.size(), 1u);
299 const auto& tracklet = snapshot.tracklets.front();
300 BOOST_CHECK_EQUAL(tracklet.firstClusterIndex, 0);
301 BOOST_CHECK_EQUAL(tracklet.secondClusterIndex, 0);
302 BOOST_CHECK_EQUAL(tracklet.tanLambda, expectedTanLambda);
303 BOOST_CHECK_EQUAL(tracklet.phi, expectedPhi);
304 BOOST_CHECK_EQUAL(tracklet.getTimeStamp().getTimeStamp(), snapshot.expectedTimestamp.getTimeStamp());
305 BOOST_CHECK_EQUAL(tracklet.getTimeStamp().getTimeStampError(), snapshot.expectedTimestamp.getTimeStampError());
306 const std::vector<int> expectedLookup{0, 1};
307 BOOST_CHECK_EQUAL_COLLECTIONS(snapshot.lookup.begin(), snapshot.lookup.end(), expectedLookup.begin(), expectedLookup.end());
308 BOOST_CHECK(snapshot.nonparticipatingEdgesEmpty);
309}
310
311DecodedCluster cylinderCluster(float radius, float z, int layer)
312{
313 DecodedCluster cluster{};
314 cluster.global = {radius, 0.f, z};
315 cluster.cylinderFrame = {radius, 0.f, z, 0.f};
316 cluster.rowColumnCovariance = {1.e-4f, 0.f, 1.e-4f};
317 cluster.layer = layer;
318 return cluster;
319}
320
321DecodedCluster diskCluster(float x, float y, float z, int layer)
322{
323 DecodedCluster cluster{};
324 cluster.global = {x, y, z};
325 cluster.rowColumnCovariance = {1.e-2f, 0.f, 1.e-2f};
326 cluster.layer = layer;
327 return cluster;
328}
329
330} // namespace
331
332BOOST_AUTO_TEST_CASE(CylinderOnePassAndTwoPassProduceIdenticalTracklets)
333{
334 const std::vector<DecodedCluster> clusters{
335 cylinderCluster(3.f, 0.3f, 0),
336 cylinderCluster(4.f, 0.4f, 1)};
337 const auto serial = runFixture<ITSNLayers>(o2::detectors::DetID::ITS, SurfaceKind::Cylinder,
338 SurfaceKind::Cylinder, clusters, 1);
339 const auto parallel = runFixture<ITSNLayers>(o2::detectors::DetID::ITS, SurfaceKind::Cylinder,
340 SurfaceKind::Cylinder, clusters, 4);
341 checkExactTracklet(serial, (0.3f - 0.4f) / (3.f - 4.f), o2::gpu::CAMath::ATan2(0.f, -1.f));
342 checkExactTracklet(parallel, (0.3f - 0.4f) / (3.f - 4.f), o2::gpu::CAMath::ATan2(0.f, -1.f));
343 checkSame(serial, parallel);
344}
345
346BOOST_AUTO_TEST_CASE(CylinderDisplacedChordPreservesBothLongitudinalSigns)
347{
348 // A line parallel to x, displaced by y=1: its transverse length is exactly
349 // one, while the difference of beam-axis radii is smaller than one.
350 for (const float sign : {-1.f, 1.f}) {
351 std::vector<DecodedCluster> clusters;
352 for (int layer = 0; layer < 2; ++layer) {
353 const float x = 3.f + layer;
354 const float z = sign * 0.25f * (layer + 1);
355 auto cluster = cylinderCluster(x, z, layer);
356 cluster.global.y = 1.f;
357 cluster.cylinderFrame.u = 1.f;
358 clusters.push_back(cluster);
359 }
360 const auto widenSearch = [](ReferenceTrackingParameters& p) {
361 p.NSigmaCut = 100.f;
362 p.PVres = 10.f; // Widen the independent azimuthal search gate too.
363 };
364 const auto serial = runFixture<ITSNLayers>(o2::detectors::DetID::ITS, SurfaceKind::Cylinder,
365 SurfaceKind::Cylinder, clusters, 1, widenSearch);
366 const auto parallel = runFixture<ITSNLayers>(o2::detectors::DetID::ITS, SurfaceKind::Cylinder,
367 SurfaceKind::Cylinder, clusters, 4, widenSearch);
368 const float expectedPhi = o2::gpu::CAMath::ATan2(0.f, -1.f);
369 checkExactTracklet(serial, sign * 0.25f, expectedPhi);
370 checkExactTracklet(parallel, sign * 0.25f, expectedPhi);
371 checkSame(serial, parallel);
372 }
373}
374
375BOOST_AUTO_TEST_CASE(DiskEqualRadiusDistinctHitsHaveFiniteSignedSlope)
376{
377 const float fromZ = kMFTSurfaces[0].referenceCoordinate;
378 const float toZ = kMFTSurfaces[1].referenceCoordinate;
379 // Same radius, different positions, with a transverse chord of exactly one.
380 const std::vector<DecodedCluster> clusters{
381 diskCluster(1.f, 0.5f, fromZ, 0),
382 diskCluster(1.f, -0.5f, toZ, 1)};
383 const auto widenSearch = [](ReferenceTrackingParameters& p) {
384 p.NSigmaCut = 100.f;
385 p.PVres = 10.f;
386 };
387 const auto serial = runFixture<MFTNLayers>(o2::detectors::DetID::MFT, SurfaceKind::Disk,
388 SurfaceKind::Disk, clusters, 1, widenSearch);
389 const auto parallel = runFixture<MFTNLayers>(o2::detectors::DetID::MFT, SurfaceKind::Disk,
390 SurfaceKind::Disk, clusters, 4, widenSearch);
391 const float expectedPhi = o2::gpu::CAMath::ATan2(1.f, 0.f);
392 checkExactTracklet(serial, toZ - fromZ, expectedPhi);
393 checkExactTracklet(parallel, toZ - fromZ, expectedPhi);
394 checkSame(serial, parallel);
395}
396
397BOOST_AUTO_TEST_CASE(DiskZeroTransverseChordRejectsTracklet)
398{
399 const float fromZ = kMFTSurfaces[0].referenceCoordinate;
400 const float toZ = kMFTSurfaces[1].referenceCoordinate;
401 const std::vector<DecodedCluster> clusters{
402 diskCluster(1.f, 0.5f, fromZ, 0),
403 diskCluster(1.f, 0.5f, toZ, 1)};
404 const auto widenSearch = [](ReferenceTrackingParameters& params) { params.NSigmaCut = 1.e6f; };
405 const auto serial = runFixture<MFTNLayers>(o2::detectors::DetID::MFT, SurfaceKind::Disk,
406 SurfaceKind::Disk, clusters, 1, widenSearch);
407 const auto parallel = runFixture<MFTNLayers>(o2::detectors::DetID::MFT, SurfaceKind::Disk,
408 SurfaceKind::Disk, clusters, 4, widenSearch);
409 BOOST_CHECK(serial.tracklets.empty());
410 BOOST_CHECK(parallel.tracklets.empty());
411 checkSame(serial, parallel);
412}
413
414BOOST_AUTO_TEST_CASE(PerTimeFrameValidationFailureLeavesEdgeArraysZeroFilledNotPartial)
415{
416 // Edge arrays are cleared before validating normalized measurements.
417 // Duplicate cluster IDs below must fail before any edge values are computed,
418 // leaving correctly sized, zero-filled arrays rather than partial results.
419 auto pool = std::make_shared<BoundedMemoryResource>();
420 TimeFrame frame;
421 Tracker tracker;
422 TrackerTraits traits;
423 std::shared_ptr<tbb::task_arena> arena;
424 std::vector<ReferenceTrackingParameters> params(1);
425 resetReferenceTrackingParameters(params[0], o2::detectors::DetID::ITS);
426 params[0].PassFlags.reset();
427 params[0].PassFlags.set(IterationStep::FirstPass, IterationStep::RebuildClusterLUT);
428
429 traits.setNThreads(1, arena);
430 frame.setBz(Bz);
431
432 const auto orderedSurfaces = identitySurfaces(static_cast<uint16_t>(ITSNLayers));
433 const auto catalog = makeCatalog(static_cast<uint16_t>(ITSNLayers), o2::detectors::DetID::ITS, SurfaceKind::Cylinder);
434 const SurfaceCatalogView catalogView{catalog.data(), static_cast<uint32_t>(catalog.size())};
435 TrackerInitialization configuration;
436 configuration.catalog = catalogView;
437 configuration.memoryPool = pool;
439 BOOST_REQUIRE(tracker.initialize(frame, configuration));
440 auto& tf = frame.getScratch();
441 const auto& layout = frame.getDetectorConfiguration();
442 const auto topologyBuild = deriveTraversalTopology(layout, params[0]);
443 BOOST_REQUIRE(topologyBuild.ok());
444 const auto layoutView = topologyBuild.topology->getView(layout.getSurfaceCatalog());
445
446 // Same minimal cluster/ROF/mask setup as runFixture(): TimeFrame::initialise()
447 // (called unconditionally, before any of this test's induced failure) needs
448 // it to size mIndexTables/mClusters correctly, regardless of what this test
449 // is actually probing.
450 const std::vector<DecodedCluster> decoded{cylinderCluster(3.f, 0.3f, 0), cylinderCluster(3.1f, 0.31f, 0),
451 cylinderCluster(4.f, 0.4f, 1)};
452 std::vector<CompClusterExt> compactClusters;
453 std::vector<unsigned char> patterns;
454 compactClusters.reserve(decoded.size());
455 patterns.reserve(decoded.size() * OnePixelPattern.size());
456 for (const auto& cluster : decoded) {
457 compactClusters.emplace_back(0, 0, CompCluster::InvalidPatternID, cluster.layer);
458 patterns.insert(patterns.end(), OnePixelPattern.begin(), OnePixelPattern.end());
459 }
460 const std::vector<ROFRecord> rofs{ROFRecord{{100, 5}, 0, 0, static_cast<int>(compactClusters.size())}};
461 PrescribedDecoder decoder{o2::detectors::DetID::ITS, SurfaceKind::Cylinder, decoded};
462 BOOST_REQUIRE_NO_THROW(test::loadTimeFrameSource(frame, decoder, o2::InteractionRecord{50, 5}, o2::its::LayerTiming{.mROFLength = 40},
463 compactClusters, patterns, rofs, &dict(), nullptr, o2::detectors::DetID::ITS,
464 gsl::span<const LayerId>{orderedSurfaces}, layout.getSurfaceCatalog()));
465 auto layer0 = frame.getGlobalMeasurements(LayerId{0});
466 BOOST_REQUIRE_EQUAL(layer0.size(), 2u);
467 layer0[1].clusterId = layer0[0].clusterId;
468
469 o2::its::LayerTiming layerTiming{};
470 layerTiming.mNROFsTF = 1;
471 layerTiming.mROFLength = 40;
473 for (int layer = 0; layer < ITSNLayers; ++layer) {
474 rofTable.defineLayer(layer, layerTiming);
475 }
476 rofTable.init();
478 for (int layer = 0; layer < ITSNLayers; ++layer) {
479 vtxTable.defineLayer(layer, layerTiming);
480 }
481 vtxTable.init();
483 mask.resetMask();
484 for (int layer = 0; layer < ITSNLayers; ++layer) {
485 mask.setROFsEnabled(layer, 0, 1, 1);
486 }
487 frame.setROFViews(RuntimeROFViews{rofTable.getView(), vtxTable.getView(), mask.getView(), {}});
488
489 std::array<gsl::span<const GlobalMeasurement>, MaxLayoutSurfaces> measurementSpans;
490 BOOST_CHECK_THROW(TrackerTestAccess::prepare(tracker, frame, 0, measurementSpans), std::invalid_argument);
491
492 const auto topology = layoutView;
493 const auto& msAngles = tf.getEdgeMSAngles();
494 const auto& phiCuts = tf.getEdgePhiCuts();
495 BOOST_REQUIRE_EQUAL(msAngles.size(), static_cast<size_t>(topology.nEdges));
496 BOOST_REQUIRE_EQUAL(phiCuts.size(), static_cast<size_t>(topology.nEdges));
497 for (int id = 0; id < topology.nEdges; ++id) {
498 BOOST_CHECK_EQUAL(msAngles[id], 0.f);
499 BOOST_CHECK_EQUAL(phiCuts[id], 0.f);
500 }
501}
502
503// ---------------------------------------------------------------------------
504// Gate 4 Slice 0a (sparse-topology tracklet migration) additions below.
505// ---------------------------------------------------------------------------
506
507BOOST_AUTO_TEST_CASE(ItsIdentityLayoutTrackletsSpanMultipleAdjacentEdgesInOrder)
508{
509 // Collinear track across 4 barrel layers (z = 0.1 * r for every cluster).
510 // Under ITS's default MaxHoles=0 only strictly-adjacent edges exist
511 // at all, so this directly proves edge-level tracklet/LUT/order
512 // parity across three distinct edges simultaneously -- each
513 // resolved through the migrated computeLayerTrackletsForKind() via a
514 // fresh mSurfaceToLegacyLayer lookup -- not just the single edge the
515 // tests above check, while every non-participating edge (touching
516 // layers 4/5/6) stays empty.
517 const std::vector<DecodedCluster> clusters{
518 cylinderCluster(3.f, 0.3f, 0),
519 cylinderCluster(4.f, 0.4f, 1),
520 cylinderCluster(5.f, 0.5f, 2),
521 cylinderCluster(6.f, 0.6f, 3)};
522 const auto snapshot = runFixture<ITSNLayers>(o2::detectors::DetID::ITS, SurfaceKind::Cylinder,
523 SurfaceKind::Cylinder, clusters, 1);
524 // Each edge's expected tanLambda is computed from its own specific
525 // (radius, z) pair rather than one shared constant: although every pair
526 // shares the same nominal slope (z = 0.1 * r), float subtraction/division
527 // of different operand pairs does not generally round to the identical
528 // bit pattern even when the mathematical result is the same value.
529 constexpr std::array<float, 4> radii{3.f, 4.f, 5.f, 6.f};
530 constexpr std::array<float, 4> zs{0.3f, 0.4f, 0.5f, 0.6f};
531 const float expectedPhi = o2::gpu::CAMath::ATan2(0.f, -1.f);
532 const std::vector<int> expectedLookup{0, 1};
533
534 BOOST_REQUIRE_EQUAL(snapshot.allEdgeFromLayer.size(), snapshot.allTracklets.size());
535 BOOST_REQUIRE_EQUAL(snapshot.allEdgeFromLayer.size(), snapshot.allLookups.size());
536 bool sawEdge01 = false, sawEdge12 = false, sawEdge23 = false;
537 for (size_t id = 0; id < snapshot.allEdgeFromLayer.size(); ++id) {
538 const int from = snapshot.allEdgeFromLayer[id];
539 const int to = snapshot.allEdgeToLayer[id];
540 const bool participates = (from == 0 && to == 1) || (from == 1 && to == 2) || (from == 2 && to == 3);
541 if (participates) {
542 BOOST_REQUIRE_EQUAL(snapshot.allTracklets[id].size(), 1u);
543 const auto& tracklet = snapshot.allTracklets[id].front();
544 BOOST_CHECK_EQUAL(tracklet.firstClusterIndex, 0);
545 BOOST_CHECK_EQUAL(tracklet.secondClusterIndex, 0);
546 const float expectedTanLambda = (zs[from] - zs[to]) / (radii[from] - radii[to]);
547 BOOST_CHECK_EQUAL(tracklet.tanLambda, expectedTanLambda);
548 BOOST_CHECK_EQUAL(tracklet.phi, expectedPhi);
549 BOOST_CHECK_EQUAL_COLLECTIONS(snapshot.allLookups[id].begin(), snapshot.allLookups[id].end(), expectedLookup.begin(), expectedLookup.end());
550 sawEdge01 |= (from == 0 && to == 1);
551 sawEdge12 |= (from == 1 && to == 2);
552 sawEdge23 |= (from == 2 && to == 3);
553 } else {
554 BOOST_CHECK(snapshot.allTracklets[id].empty());
555 }
556 }
557 BOOST_CHECK(sawEdge01);
558 BOOST_CHECK(sawEdge12);
559 BOOST_CHECK(sawEdge23);
560}
561
562BOOST_AUTO_TEST_CASE(ItsHoleEdgeTrackletResolvesCorrectLegacyLayerEndpoints)
563{
564 // MaxHoles=1 with layer 1 an allowed hole introduces a (0,2)-skip-1
565 // edge whose sparse Edge endpoints are LayerId{0}/
566 // LayerId{2} -- a direct, non-adjacent exercise of mSurfaceToLegacyLayer
567 // resolving a edge's endpoints correctly, and of hole/skipped-surface
568 // behaviour staying identical to the pre-migration code (which read the
569 // same fromLayer/toLayer straight off the legacy view). No cluster is
570 // placed on layer 1 at all, so only the hole edge can produce a
571 // tracklet.
572 const std::vector<DecodedCluster> clusters{
573 cylinderCluster(3.f, 0.3f, 0),
574 cylinderCluster(5.f, 0.5f, 2)};
575 const auto snapshot = runFixture<ITSNLayers>(
576 o2::detectors::DetID::ITS, SurfaceKind::Cylinder, SurfaceKind::Cylinder, clusters, 1,
578 p.MaxHoles = 1;
579 },
580 LayerMask{static_cast<uint16_t>(1u << 1)});
581
582 const float expectedTanLambda = (0.3f - 0.5f) / (3.f - 5.f);
583 const float expectedPhi = o2::gpu::CAMath::ATan2(0.f, -1.f);
584 bool sawHoleEdge = false;
585 BOOST_REQUIRE_EQUAL(snapshot.allEdgeFromLayer.size(), snapshot.allTracklets.size());
586 for (size_t id = 0; id < snapshot.allEdgeFromLayer.size(); ++id) {
587 const int from = snapshot.allEdgeFromLayer[id];
588 const int to = snapshot.allEdgeToLayer[id];
589 if (from == 0 && to == 2) {
590 sawHoleEdge = true;
591 BOOST_REQUIRE_EQUAL(snapshot.allTracklets[id].size(), 1u);
592 const auto& tracklet = snapshot.allTracklets[id].front();
593 BOOST_CHECK_EQUAL(tracklet.tanLambda, expectedTanLambda);
594 BOOST_CHECK_EQUAL(tracklet.phi, expectedPhi);
595 const std::vector<int> expectedLookup{0, 1};
596 BOOST_CHECK_EQUAL_COLLECTIONS(snapshot.allLookups[id].begin(), snapshot.allLookups[id].end(), expectedLookup.begin(), expectedLookup.end());
597 } else {
598 BOOST_CHECK(snapshot.allTracklets[id].empty());
599 }
600 }
601 BOOST_CHECK(sawHoleEdge);
602}
603
604BOOST_AUTO_TEST_CASE(DenseLayerIdentityIsDerivedFromDescriptorPosition)
605{
606 const auto surfaces = makeCatalog(static_cast<uint16_t>(ITSNLayers), o2::detectors::DetID::ITS, SurfaceKind::Cylinder);
607 const auto layout = DetectorConfiguration{surfaces};
608 BOOST_REQUIRE(layout.valid());
609 BOOST_REQUIRE_EQUAL(layout.size(), static_cast<std::size_t>(ITSNLayers));
610 for (uint16_t position = 0; position < ITSNLayers; ++position) {
611 BOOST_CHECK(&layout[LayerId{position}] == &layout.getLayers()[position]);
612 }
613}
614
615BOOST_AUTO_TEST_CASE(CombinedCylinderAndDiskLayoutBindsAsOneDisconnectedPlan)
616{
617 const auto nCylinders = static_cast<uint16_t>(ITSNLayers);
618 const auto nDisks = static_cast<uint16_t>(MFTNLayers);
619 auto surfaces = makeCatalog(nCylinders, o2::detectors::DetID::ITS, SurfaceKind::Cylinder);
620 auto disks = makeCatalog(nDisks, o2::detectors::DetID::MFT, SurfaceKind::Disk);
621 surfaces.insert(surfaces.end(), disks.begin(), disks.end());
622 const std::vector<uint16_t> componentOffsets = {0, nCylinders};
623 const auto layout = DetectorConfiguration{surfaces, componentOffsets};
625 parameters.NLayers = static_cast<int>(layout.size());
626 const auto result = deriveTraversalTopology(layout, parameters);
627 BOOST_REQUIRE(result.ok());
628 BOOST_CHECK_EQUAL(result.topology->edges.size(), static_cast<std::size_t>(nCylinders + nDisks - 2));
629}
Definition of the ITSMFT compact cluster.
Definition of the ClusterTopology class.
uint8_t lookup(const char input) noexcept
Shared CA tracking configuration for ITS and MFT.
Passive common TimeFrame owner.
int32_t i
Definition of the ITSMFT ROFrame (trigger) record.
useful math constants
Runtime-plan-owned, detector-neutral CA workspace.
int ID
Detector identifiers: continuous, starting from 0.
Definition DetID.h:63
static constexpr unsigned short InvalidPatternID
Definition CompCluster.h:46
void setNThreads(int n, std::shared_ptr< tbb::task_arena > &arena)
bool initialize(TimeFrame &frame, const TrackerInitialization &configuration)
Definition Tracker.cxx:414
Shared CA tracker traits: same ITS-style tracklet/cell/road logic; MFT uses x-y LUT and forward refit...
GLint GLenum GLint x
Definition glcorearb.h:403
GLuint64EXT * result
Definition glcorearb.h:5662
GLint y
Definition glcorearb.h:270
GLenum const GLfloat * params
Definition glcorearb.h:272
GLenum GLuint GLint GLint layer
Definition glcorearb.h:1310
GLint GLuint mask
Definition glcorearb.h:291
GLuint id
Definition glcorearb.h:650
GLdouble GLdouble GLdouble z
Definition glcorearb.h:843
WorkflowSpec parallel(DataProcessorSpec original, size_t maxIndex, std::function< void(DataProcessorSpec &, size_t id)> amendCallback)
constexpr float Radl
Definition Constants.h:34
constexpr float Rho
Definition Constants.h:35
ClusterData< T > extractClusterData(const CompClusterExt &c, gsl::span< const unsigned char >::iterator &patterns, const TopologyDictionary *dict)
Definition IOUtils.h:81
TrackingPlan makeTrackingPlan(const TrackingParameters &parameters)
constexpr std::array< SurfaceDescriptor, MFTNLayers > kMFTSurfaces
constexpr int MFTNLayers
MFT CA half-disk layer count.
constexpr std::array< SurfaceDescriptor, ITSNLayers > kITSSurfaces
constexpr int ITSNLayers
ITS CA layer count.
constexpr uint32_t MaxLayoutSurfaces
Definition IdTypes.h:70
TraversalTopologyBuildResult deriveTraversalTopology(const DetectorConfiguration &layout, const o2::itsmft::IterationParameters &parameters)
BOOST_AUTO_TEST_CASE(Cluster_messageable)
constexpr std::array< Float_t, LayersNumber > LayerZCoordinate()
Definition Constants.h:44
std::unique_ptr< GPUReconstructionTimeframe > tf
const DetectorConfiguration & getDetectorConfiguration() const noexcept
Definition TimeFrame.h:157
TimeFrameScratch & getScratch()
void setROFViews(RuntimeROFViews views) noexcept
gsl::span< const GlobalMeasurement > getGlobalMeasurements(LayerId surface) const
Definition TimeFrame.cxx:46
const RuntimeROFOverlapView & getROFOverlapView() const noexcept
Definition TimeFrame.h:137
std::shared_ptr< BoundedMemoryResource > memoryPool
Definition Tracker.h:54
static IterationContext prepare(Tracker &tracker, TimeFrame &frame, int iteration, std::array< gsl::span< const GlobalMeasurement >, MaxLayoutSurfaces > &measurementSpans)
static void computeTracklets(TrackerTraits &traits, IterationContext &view, int vertex)
BOOST_CHECK(tree)
coder decode(ctfImage, triggersD, clustersD)
std::vector< Cluster > clusters
BOOST_CHECK_EQUAL(triggersD.size(), triggers.size())
std::vector< Tracklet64 > tracklets