Project
Loading...
Searching...
No Matches
testCellFinding.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 CellFindingNative
13#define BOOST_TEST_MAIN
14#define BOOST_TEST_DYN_LINK
15
16#include <cmath>
17#include <cstring>
18#include <limits>
19
20#include <boost/test/unit_test.hpp>
21
25#include "ITStracking/Cluster.h"
26
47using namespace o2::itsmft::tracking;
48
49namespace
50{
51
52template <typename T>
53bool bitEqual(const T& lhs, const T& rhs)
54{
55 return std::memcmp(&lhs, &rhs, sizeof(T)) == 0;
56}
57
58bool attachMeasurement(SurfaceTrackState& state, const SurfaceMeasurement& measurement,
59 NominalSurfaceMaterial material, float bz, float& chi2,
60 const TrackingKernelParameters& parameters)
61{
64 target.material = material;
65 return Propagator::attachMeasurement(state, target, measurement, bz,
66 material::MaterialTraversalDirection::OppositeMomentum, true,
67 parameters.maxChi2ClusterAttachment, chi2);
68}
69
70SurfaceMeasurement barrelMeasurementFromHit(const o2::its::TrackingFrameInfo& hit)
71{
72 SurfaceMeasurement measurement{};
73 measurement.frame.q = hit.xTrackingFrame;
74 measurement.frame.frameAngle = hit.alphaTrackingFrame;
75 measurement.frame.u = hit.positionTrackingFrame[0];
76 measurement.frame.v = hit.positionTrackingFrame[1];
77 measurement.covariance.uu = hit.covarianceTrackingFrame[0];
78 measurement.covariance.uv = hit.covarianceTrackingFrame[1];
79 measurement.covariance.vv = hit.covarianceTrackingFrame[2];
80 return measurement;
81}
82
83SurfaceMeasurement diskMeasurementFromHit(const o2::its::TrackingFrameInfo& hit)
84{
85 SurfaceMeasurement measurement{};
86 measurement.frame = {hit.zCoordinate, hit.xCoordinate, hit.yCoordinate, 0.f};
87 measurement.covariance.uu = hit.covarianceTrackingFrame[0];
88 measurement.covariance.uv = 0.f;
89 measurement.covariance.vv = hit.covarianceTrackingFrame[2];
90 return measurement;
91}
92
93// --- attachHit fixtures -----------------------------------------------
94
95SurfaceTrackState barrelAttachState()
96{
98 state.parameters[0] = 1.25f;
99 state.parameters[1] = -0.75f;
100 state.parameters[2] = 0.2f;
101 state.parameters[3] = -0.35f;
102 state.parameters[4] = 0.8f;
104 state.alpha = 0.3f;
105 state.kind = SurfaceKind::Cylinder;
106 state.absCharge = 1;
107 state.pid = o2::track::PID::Kaon;
108 for (uint8_t row = 0; row < 5; ++row) {
109 for (uint8_t column = 0; column <= row; ++column) {
110 state.covariance[packedCovarianceIndex(row, column)] = row == column ? 0.01f * (row + 1) : 0.0002f * (row + column + 1);
111 }
112 }
113 return state;
114}
115
116o2::its::TrackingFrameInfo barrelAttachHit()
117{
118 return o2::its::TrackingFrameInfo{0.f, 0.f, 0.f, 2.5f, 0.3f, {0.8f, -0.45f}, {0.04f, 0.012f, 0.09f}};
119}
120
121constexpr float BarrelAttachBz = 5.f;
122
123NominalSurfaceMaterial barrelAttachMaterial() { return NominalSurfaceMaterial{0.01f, 0.001f}; }
124
125SurfaceTrackState diskAttachState()
126{
128 state.parameters[0] = 1.25f;
129 state.parameters[1] = -0.75f;
130 state.parameters[2] = 0.35f;
131 state.parameters[3] = -2.5f;
132 state.parameters[4] = 0.8f;
134 state.kind = SurfaceKind::Disk;
135 state.absCharge = 2;
136 state.pid = o2::track::PID::Pion;
137 for (uint8_t row = 0; row < 5; ++row) {
138 for (uint8_t column = 0; column <= row; ++column) {
139 state.covariance[packedCovarianceIndex(row, column)] = row == column ? 0.01f * (row + 1) : 0.0002f * (row + column + 1);
140 }
141 }
142 return state;
143}
144
145o2::its::TrackingFrameInfo diskAttachHit()
146{
147 return o2::its::TrackingFrameInfo{0.8f, -0.45f, -50.f, 0.f, 0.f, {0.f, 0.f}, {0.04f, 0.f, 0.09f}};
148}
149
150constexpr float DiskAttachBz = 5.f;
151
152NominalSurfaceMaterial diskAttachMaterial() { return NominalSurfaceMaterial{0.02f, 0.002f}; }
153
154// Test-local field-mapping helper: builds the SurfaceMeasurement
155// attachDiskHit reads from a single legacy hit (Disk field mapping:
156// global coordinates -> measurement.global, reference z -> measurement.
157// frame.q [read as the propagate target, in place of the retired
158// hit.zCoordinate], measured covariance -> measurement.covariance).
159SurfaceMeasurement diskAttachMeasurementFrom(const o2::its::TrackingFrameInfo& hit)
160{
161 auto measurement = diskMeasurementFromHit(hit);
162 measurement.frame.q = hit.zCoordinate;
163 return measurement;
164}
165
166SurfaceMeasurement diskAttachMeasurement() { return diskAttachMeasurementFrom(diskAttachHit()); }
167
168} // namespace
169
170// ===========================================================================
171// Measurement attachment
172// ===========================================================================
173
174BOOST_AUTO_TEST_CASE(AttachHitBarrelSuccessAndExactChi2Threshold)
175{
176 const auto state0 = barrelAttachState();
177 const auto hit = barrelMeasurementFromHit(barrelAttachHit());
178 const auto material = barrelAttachMaterial();
179
180 auto probe = state0;
181 float probeChi2 = 0.f;
182
183 TrackingKernelParameters permissive;
184 permissive.maxChi2ClusterAttachment = 1.e6f;
185 BOOST_REQUIRE(attachMeasurement(probe, hit, material, BarrelAttachBz, probeChi2, permissive));
186 BOOST_REQUIRE_GT(probeChi2, 0.f);
187
188 auto accepted = state0;
189 float acceptedChi2 = 0.f;
191 accept.maxChi2ClusterAttachment = probeChi2;
192 BOOST_CHECK(attachMeasurement(accepted, hit, material, BarrelAttachBz, acceptedChi2, accept));
193 BOOST_CHECK(bitEqual(accepted, probe));
194 BOOST_CHECK_EQUAL(acceptedChi2, probeChi2);
195
196 auto rejected = state0;
197 float rejectedChi2 = -1.f;
198 const auto before = rejected;
199 const float chi2Before = rejectedChi2;
201 reject.maxChi2ClusterAttachment = std::nextafter(probeChi2, -std::numeric_limits<float>::infinity());
202 BOOST_CHECK(!attachMeasurement(rejected, hit, material, BarrelAttachBz, rejectedChi2, reject));
203
204 BOOST_CHECK(bitEqual(rejected, before));
205 BOOST_CHECK_EQUAL(rejectedChi2, chi2Before);
206}
207
208BOOST_AUTO_TEST_CASE(AttachHitBarrelEachFailureStagePreservesStateTransactionally)
209{
210 const auto state0 = barrelAttachState();
211 TrackingKernelParameters permissive;
212 permissive.maxChi2ClusterAttachment = 1.e6f;
213
214 auto checkFailure = [&](const SurfaceMeasurement& measurement, const NominalSurfaceMaterial& material) {
215 auto state = state0;
216 float chi2 = -1.f;
217 const auto before = state;
218 const float chi2Before = chi2;
219
220 BOOST_CHECK(!attachMeasurement(state, measurement, material, BarrelAttachBz, chi2, permissive));
221
222 BOOST_CHECK(bitEqual(state, before));
223 BOOST_CHECK_EQUAL(chi2, chi2Before);
224 };
225
226 // Rotation failure.
227 {
228 auto farHit = barrelAttachHit();
229 farHit.alphaTrackingFrame = state0.alpha + 3.f;
230 checkFailure(barrelMeasurementFromHit(farHit), barrelAttachMaterial());
231 }
232
233 // Propagation failure.
234 {
235 auto farHit = barrelAttachHit();
236 farHit.xTrackingFrame = -50000.f;
237 checkFailure(barrelMeasurementFromHit(farHit), barrelAttachMaterial());
238 }
239}
240
241BOOST_AUTO_TEST_CASE(AttachHitBarrelNegativeChi2IsRejectedMatchingLegacyInclusiveCut)
242{
243 // No-op rotate (hit shares state's alpha) and no-op propagate (hit's
244 // target x equals state's own referenceCoordinate) plus a no-op material
245 // budget ({0,0} is the documented unconditional no-op) keep the state
246 // byte-identical to barrelAttachState() through predictedChi2, so its
247 // known covariance can be reasoned about directly: a pathologically large
248 // measurement cross-covariance (uv) makes the combined 2x2 determinant
249 // negative, which residualInverse's own gate does not reject outright
250 // (only exact-zero/non-finite determinants are), producing a negative
251 // predicted chi2 -- the same `< 0.f` established rejection
252 // attachCylinderHit already applies today. Propagator::updateBarrel shares
253 // the identical residualInverse gate and therefore cannot independently
254 // fail once predictedChi2 has already succeeded with the same inputs; the
255 // two checks share one deterministic failure precedence (predictedChi2,
256 // evaluated before update, is always the one that observes a bad
257 // residualInverse first).
258 auto state = barrelAttachState();
259 auto hit = barrelAttachHit();
260 hit.alphaTrackingFrame = state.alpha;
261 hit.xTrackingFrame = state.referenceCoordinate;
262 hit.covarianceTrackingFrame[1] = 50.f; // huge uv cross term
263 const auto measurement = barrelMeasurementFromHit(hit);
264 const NominalSurfaceMaterial noopMaterial{0.f, 0.f};
265 const auto before = state;
266 float chi2 = -1.f;
267 const float chi2Before = chi2;
268
269 TrackingKernelParameters permissive;
270 permissive.maxChi2ClusterAttachment = 1.e6f;
271 BOOST_CHECK(!attachMeasurement(state, measurement, noopMaterial, BarrelAttachBz, chi2, permissive));
272
273 BOOST_CHECK(bitEqual(state, before));
274 BOOST_CHECK_EQUAL(chi2, chi2Before);
275}
276
277BOOST_AUTO_TEST_CASE(AttachHitBarrelIsByteDeterministic)
278{
279 auto first = barrelAttachState();
280 auto second = barrelAttachState();
281 float chi2First = 0.f;
282 float chi2Second = 0.f;
283
284 TrackingKernelParameters permissive;
285 permissive.maxChi2ClusterAttachment = 1.e6f;
286 BOOST_REQUIRE(attachMeasurement(first, barrelMeasurementFromHit(barrelAttachHit()), barrelAttachMaterial(), BarrelAttachBz, chi2First, permissive));
287 BOOST_REQUIRE(attachMeasurement(second, barrelMeasurementFromHit(barrelAttachHit()), barrelAttachMaterial(), BarrelAttachBz, chi2Second, permissive));
288 BOOST_CHECK(bitEqual(first, second));
289 BOOST_CHECK_EQUAL(chi2First, chi2Second);
290}
291
292// ===========================================================================
293// attachDiskHit
294// ===========================================================================
295
296BOOST_AUTO_TEST_CASE(AttachHitDiskSuccessAndExactChi2Threshold)
297{
298 const auto state0 = diskAttachState();
299 const auto hit = diskAttachMeasurement();
300 const auto material = diskAttachMaterial();
301
302 auto probe = state0;
303 float probeChi2 = 0.f;
304
305 TrackingKernelParameters permissive;
306 permissive.maxChi2ClusterAttachment = 1.e6f;
307 BOOST_REQUIRE(attachMeasurement(probe, hit, material, DiskAttachBz, probeChi2, permissive));
308 BOOST_REQUIRE_GT(probeChi2, 0.f);
309
310 auto accepted = state0;
311 float acceptedChi2 = 0.f;
313 accept.maxChi2ClusterAttachment = probeChi2;
314 BOOST_CHECK(attachMeasurement(accepted, hit, material, DiskAttachBz, acceptedChi2, accept));
315 BOOST_CHECK(bitEqual(accepted, probe));
316 BOOST_CHECK_EQUAL(acceptedChi2, probeChi2);
317
318 auto rejected = state0;
319 float rejectedChi2 = -1.f;
320 const auto before = rejected;
321 const float chi2Before = rejectedChi2;
323 reject.maxChi2ClusterAttachment = std::nextafter(probeChi2, -std::numeric_limits<float>::infinity());
324 BOOST_CHECK(!attachMeasurement(rejected, hit, material, DiskAttachBz, rejectedChi2, reject));
325
326 BOOST_CHECK(bitEqual(rejected, before));
327 BOOST_CHECK_EQUAL(rejectedChi2, chi2Before);
328}
329
330BOOST_AUTO_TEST_CASE(AttachHitDiskEachFailureStagePreservesStateTransactionally)
331{
332 const auto state0 = diskAttachState();
333 TrackingKernelParameters permissive;
334 permissive.maxChi2ClusterAttachment = 1.e6f;
335
336 // Propagation failure: tanl == 0 at zero field rejects with
337 // UnreachableTarget.
338 {
339 auto zeroTanl = state0;
340 zeroTanl.parameters[3] = 0.f;
341 auto hit = diskAttachHit();
342 hit.zCoordinate = -60.f; // dz != 0
343 const auto measurement = diskAttachMeasurementFrom(hit);
344 auto state = zeroTanl;
345 float chi2 = -1.f;
346 const auto before = state;
347 const float chi2Before = chi2;
348
349 BOOST_CHECK(!attachMeasurement(state, measurement, diskAttachMaterial(), 0.f, chi2, permissive));
350
351 BOOST_CHECK(bitEqual(state, before));
352 BOOST_CHECK_EQUAL(chi2, chi2Before);
353 }
354}
355
356BOOST_AUTO_TEST_CASE(AttachHitDiskActivatesEnergyLossUnlikeLegacyMcsOnlyPath)
357{
358 auto noLossMaterial = diskAttachMaterial();
359 noLossMaterial.arealDensityGPerCm2 = 0.f;
360 auto withLossMaterial = diskAttachMaterial();
361
362 auto stateNoLoss = diskAttachState();
363 auto stateWithLoss = diskAttachState();
364 const auto hit = diskAttachMeasurement();
365 float chi2NoLoss = 0.f;
366 float chi2WithLoss = 0.f;
367
368 TrackingKernelParameters permissive;
369 permissive.maxChi2ClusterAttachment = 1.e6f;
370 BOOST_REQUIRE(attachMeasurement(stateNoLoss, hit, noLossMaterial, DiskAttachBz, chi2NoLoss, permissive));
371 BOOST_REQUIRE(attachMeasurement(stateWithLoss, hit, withLossMaterial, DiskAttachBz, chi2WithLoss, permissive));
372 BOOST_CHECK_NE(stateNoLoss.parameters[4], stateWithLoss.parameters[4]);
373}
374
375BOOST_AUTO_TEST_CASE(AttachHitDiskIsByteDeterministic)
376{
377 auto first = diskAttachState();
378 auto second = diskAttachState();
379 float chi2First = 0.f;
380 float chi2Second = 0.f;
381
382 TrackingKernelParameters permissive;
383 permissive.maxChi2ClusterAttachment = 1.e6f;
384 BOOST_REQUIRE(attachMeasurement(first, diskAttachMeasurement(), diskAttachMaterial(), DiskAttachBz, chi2First, permissive));
385 BOOST_REQUIRE(attachMeasurement(second, diskAttachMeasurement(), diskAttachMaterial(), DiskAttachBz, chi2Second, permissive));
386 BOOST_CHECK(bitEqual(first, second));
387 BOOST_CHECK_EQUAL(chi2First, chi2Second);
388}
389
390// ===========================================================================
391// Compatibility-projection coverage (private TrackingFrameInfo ->
392// SurfaceMeasurement boundary, exercised indirectly through attachHit).
393// ===========================================================================
394
395BOOST_AUTO_TEST_CASE(BarrelProjectionUsesFullCovarianceIncludingCrossTerm)
396{
397 const auto state0 = barrelAttachState();
398 auto lowCrossTerm = barrelAttachHit();
399 lowCrossTerm.covarianceTrackingFrame[1] = 0.f;
400 auto highCrossTerm = barrelAttachHit();
401 highCrossTerm.covarianceTrackingFrame[1] = 0.03f;
402
403 auto stateLow = state0;
404 auto stateHigh = state0;
405 float chi2Low = 0.f;
406 float chi2High = 0.f;
407
408 TrackingKernelParameters permissive;
409 permissive.maxChi2ClusterAttachment = 1.e6f;
410 BOOST_REQUIRE(attachMeasurement(stateLow, barrelMeasurementFromHit(lowCrossTerm), barrelAttachMaterial(), BarrelAttachBz, chi2Low, permissive));
411 BOOST_REQUIRE(attachMeasurement(stateHigh, barrelMeasurementFromHit(highCrossTerm), barrelAttachMaterial(), BarrelAttachBz, chi2High, permissive));
412 BOOST_CHECK_NE(chi2Low, chi2High);
413}
414
415BOOST_AUTO_TEST_CASE(ForwardProjectionIsDiagonalOnlyAndIgnoresUnreadTrackingFrameFields)
416{
417 const auto state0 = diskAttachState();
418 const auto baseline = diskAttachHit();
419
420 auto varyingCrossTerm = baseline;
421 varyingCrossTerm.covarianceTrackingFrame[1] = 999.f; // forward never reads this slot
422
423 auto varyingUnreadFields = baseline;
424 varyingUnreadFields.xTrackingFrame = 12345.f;
425 varyingUnreadFields.alphaTrackingFrame = 6.7f;
426 varyingUnreadFields.positionTrackingFrame = {-999.f, 999.f};
427
428 TrackingKernelParameters permissive;
429 permissive.maxChi2ClusterAttachment = 1.e6f;
430
431 auto stateBaseline = state0;
432 float chi2Baseline = 0.f;
433 BOOST_REQUIRE(attachMeasurement(stateBaseline, diskAttachMeasurementFrom(baseline), diskAttachMaterial(), DiskAttachBz, chi2Baseline, permissive));
434
435 auto stateCrossTerm = state0;
436 float chi2CrossTerm = 0.f;
437 BOOST_REQUIRE(attachMeasurement(stateCrossTerm, diskAttachMeasurementFrom(varyingCrossTerm), diskAttachMaterial(), DiskAttachBz, chi2CrossTerm, permissive));
438 BOOST_CHECK(bitEqual(stateBaseline, stateCrossTerm));
439 BOOST_CHECK_EQUAL(chi2Baseline, chi2CrossTerm);
440
441 auto stateUnreadFields = state0;
442 float chi2UnreadFields = 0.f;
443 BOOST_REQUIRE(attachMeasurement(stateUnreadFields, diskAttachMeasurementFrom(varyingUnreadFields), diskAttachMaterial(), DiskAttachBz, chi2UnreadFields, permissive));
444 BOOST_CHECK(bitEqual(stateBaseline, stateUnreadFields));
445 BOOST_CHECK_EQUAL(chi2Baseline, chi2UnreadFields);
446}
SurfaceTrackState state
float chi2
GLint first
Definition glcorearb.h:399
GLenum target
Definition glcorearb.h:1641
std::array< float, 2 > positionTrackingFrame
Definition Cluster.h:66
std::array< float, 3 > covarianceTrackingFrame
Definition Cluster.h:67
BOOST_AUTO_TEST_CASE(AttachHitBarrelSuccessAndExactChi2Threshold)
BOOST_CHECK(tree)
BOOST_CHECK_EQUAL(triggersD.size(), triggers.size())
std::vector< int > row