Project
Loading...
Searching...
No Matches
TripletFitting.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
13
14#include <algorithm>
15#include <array>
16#include <cmath>
17#include <cstddef>
18#include <limits>
19
21{
22namespace
23{
24
25constexpr std::size_t NMeasurementCoordinates = 9;
26constexpr std::size_t NCoordinates = NMeasurementCoordinates;
27constexpr std::size_t NAdjacentKinks = 4;
28
29using KinkVector = std::array<float, NAdjacentKinks>;
30using KinkCovariance = std::array<std::array<float, NAdjacentKinks>, NAdjacentKinks>;
31
32struct DualNumber {
33 float value{0.};
34 std::array<float, NCoordinates> derivative{};
35
36 static DualNumber variable(float val, std::size_t index) noexcept
37 {
38 DualNumber result{val};
39 result.derivative[index] = 1.;
40 return result;
41 }
42};
43
44DualNumber operator+(const DualNumber& lhs, const DualNumber& rhs) noexcept
45{
46 DualNumber result{lhs.value + rhs.value};
47 for (std::size_t i = 0; i < NCoordinates; ++i) {
48 result.derivative[i] = lhs.derivative[i] + rhs.derivative[i];
49 }
50 return result;
51}
52
53DualNumber operator-(const DualNumber& lhs, const DualNumber& rhs) noexcept
54{
55 DualNumber result{lhs.value - rhs.value};
56 for (std::size_t i = 0; i < NCoordinates; ++i) {
57 result.derivative[i] = lhs.derivative[i] - rhs.derivative[i];
58 }
59 return result;
60}
61
62DualNumber operator-(const DualNumber& value) noexcept
63{
64 DualNumber result{-value.value};
65 for (std::size_t i = 0; i < NCoordinates; ++i) {
66 result.derivative[i] = -value.derivative[i];
67 }
68 return result;
69}
70
71DualNumber operator*(const DualNumber& lhs, const DualNumber& rhs) noexcept
72{
73 DualNumber result{lhs.value * rhs.value};
74 for (std::size_t i = 0; i < NCoordinates; ++i) {
75 result.derivative[i] = lhs.derivative[i] * rhs.value + lhs.value * rhs.derivative[i];
76 }
77 return result;
78}
79
80DualNumber operator/(const DualNumber& lhs, const DualNumber& rhs) noexcept
81{
82 const float inverse = 1. / rhs.value;
83 DualNumber result{lhs.value * inverse};
84 for (std::size_t i = 0; i < NCoordinates; ++i) {
85 result.derivative[i] = (lhs.derivative[i] - result.value * rhs.derivative[i]) * inverse;
86 }
87 return result;
88}
89
90DualNumber squareRoot(const DualNumber& argument) noexcept
91{
92 const float root = std::sqrt(argument.value);
93 DualNumber result{root};
94 const float scale = 0.5 / root;
95 for (std::size_t i = 0; i < NCoordinates; ++i) {
96 result.derivative[i] = scale * argument.derivative[i];
97 }
98 return result;
99}
100
101DualNumber arcSine(const DualNumber& argument) noexcept
102{
103 DualNumber result{std::asin(argument.value)};
104 const float scale = 1. / std::sqrt(1. - argument.value * argument.value);
105 for (std::size_t i = 0; i < NCoordinates; ++i) {
106 result.derivative[i] = scale * argument.derivative[i];
107 }
108 return result;
109}
110
111DualNumber arcTangent2(const DualNumber& y, const DualNumber& x) noexcept
112{
113 DualNumber result{std::atan2(y.value, x.value)};
114 const float denominator = x.value * x.value + y.value * y.value;
115 for (std::size_t i = 0; i < NCoordinates; ++i) {
116 result.derivative[i] = (x.value * y.derivative[i] - y.value * x.derivative[i]) / denominator;
117 }
118 return result;
119}
120
121struct SegmentGeometry {
122 DualNumber bendingAngle;
124 DualNumber cotangentTheta;
125 DualNumber sineTheta;
126 DualNumber cosineTheta;
127 DualNumber index;
128};
129
130bool makeSegmentGeometry(const DualNumber& transverseCurvature, const DualNumber& chordLength,
131 const DualNumber& deltaZ, SegmentGeometry& result) noexcept
132{
133 const DualNumber halfSine = DualNumber{0.5} * transverseCurvature * chordLength;
134 if (std::abs(halfSine.value) >= 1.) {
135 return false;
136 }
137
138 const DualNumber halfSine2 = halfSine * halfSine;
139 const DualNumber halfSine4 = halfSine2 * halfSine2;
140 DualNumber asinOverArgument;
141 DualNumber angleCotangent;
142 const DualNumber halfAngle = arcSine(halfSine);
143 if (std::abs(halfSine.value) < 1.e-4) {
144 asinOverArgument = DualNumber{1.} + halfSine2 * DualNumber{1. / 6.} + halfSine4 * DualNumber{3. / 40.};
145 angleCotangent = DualNumber{1.} - halfSine2 * DualNumber{1. / 3.} - halfSine4 * DualNumber{2. / 15.};
146 } else {
147 asinOverArgument = halfAngle / halfSine;
148 angleCotangent = halfAngle * squareRoot(DualNumber{1.} - halfSine2) / halfSine;
149 }
150
151 const DualNumber bendingAngle = DualNumber{2.} * halfAngle;
152 const DualNumber transverseArcLength = chordLength * asinOverArgument;
153 const DualNumber cotangentTheta = deltaZ / transverseArcLength;
154 const DualNumber sineTheta = DualNumber{1.} / squareRoot(DualNumber{1.} + cotangentTheta * cotangentTheta);
155 const DualNumber cosineTheta = cotangentTheta * sineTheta;
156 const DualNumber index = DualNumber{1.} /
157 (angleCotangent * sineTheta * sineTheta + cosineTheta * cosineTheta);
158 if (transverseArcLength.value <= 0. || sineTheta.value <= 0. || index.value <= 0.) {
159 return false;
160 }
162 return true;
163}
164
165struct TripletGeometry {
166 DualNumber phiTilde;
167 DualNumber thetaTilde;
168 DualNumber rhoPhi;
169 DualNumber rhoTheta;
170};
171
172bool makeTripletGeometry(const std::array<GlobalMeasurement, 3>& measurements,
173 TripletGeometry& result) noexcept
174{
175 std::array<std::array<DualNumber, 3>, 3> point{};
176 for (std::size_t hit = 0; hit < measurements.size(); ++hit) {
177 const std::array<float, 3> position{
178 measurements[hit].x, measurements[hit].y, measurements[hit].z};
179 for (std::size_t coordinate = 0; coordinate < 3; ++coordinate) {
180 const std::size_t index = 3 * hit + coordinate;
181 point[hit][coordinate] = DualNumber::variable(position[coordinate], index);
182 }
183 }
184
185 const DualNumber dx01 = point[1][0] - point[0][0];
186 const DualNumber dy01 = point[1][1] - point[0][1];
187 const DualNumber dz01 = point[1][2] - point[0][2];
188 const DualNumber dx12 = point[2][0] - point[1][0];
189 const DualNumber dy12 = point[2][1] - point[1][1];
190 const DualNumber dz12 = point[2][2] - point[1][2];
191 const DualNumber dx02 = point[2][0] - point[0][0];
192 const DualNumber dy02 = point[2][1] - point[0][1];
193 const DualNumber length01 = squareRoot(dx01 * dx01 + dy01 * dy01);
194 const DualNumber length12 = squareRoot(dx12 * dx12 + dy12 * dy12);
195 const DualNumber length02 = squareRoot(dx02 * dx02 + dy02 * dy02);
196 if (length01.value <= 0. ||
197 length12.value <= 0. || length02.value <= 0.) {
198 return false;
199 }
200
201 const DualNumber cross = dx01 * dy12 - dy01 * dx12;
202 const DualNumber transverseCurvature = DualNumber{2.} * cross / (length01 * length12 * length02);
203 SegmentGeometry firstSegment;
204 SegmentGeometry secondSegment;
205 if (!makeSegmentGeometry(transverseCurvature, length01, dz01, firstSegment) ||
206 !makeSegmentGeometry(transverseCurvature, length12, dz12, secondSegment)) {
207 return false;
208 }
209
210 const DualNumber theta01 = arcTangent2(firstSegment.transverseArcLength, dz01);
211 const DualNumber theta12 = arcTangent2(secondSegment.transverseArcLength, dz12);
212 const DualNumber phiTilde = DualNumber{0.5} *
213 (firstSegment.bendingAngle * firstSegment.index +
214 secondSegment.bendingAngle * secondSegment.index);
215 const DualNumber thetaTilde = theta12 - theta01 +
216 (DualNumber{1.} - secondSegment.index) * secondSegment.cotangentTheta -
217 (DualNumber{1.} - firstSegment.index) * firstSegment.cotangentTheta;
218 const DualNumber rhoPhi = DualNumber{-0.5} *
219 (firstSegment.transverseArcLength * firstSegment.index / firstSegment.sineTheta +
220 secondSegment.transverseArcLength * secondSegment.index / secondSegment.sineTheta);
221
222 DualNumber rhoTheta;
223 const float maximumHalfSine = 0.5 * std::abs(transverseCurvature.value) *
224 std::max(length01.value, length12.value);
225 if (maximumHalfSine < 1.e-4) {
226 rhoTheta = transverseCurvature *
227 (length12 * length12 * secondSegment.cosineTheta -
228 length01 * length01 * firstSegment.cosineTheta) /
229 DualNumber{12.};
230 } else {
231 rhoTheta = ((DualNumber{1.} - firstSegment.index) * firstSegment.cotangentTheta / firstSegment.sineTheta -
232 (DualNumber{1.} - secondSegment.index) * secondSegment.cotangentTheta / secondSegment.sineTheta) /
233 transverseCurvature;
234 }
235
236 if (rhoPhi.value == 0.) {
237 return false;
238 }
240 return true;
241}
242
243float covarianceContraction(const std::array<float, 3>& left,
244 const GlobalCovariance3F& covariance,
245 const std::array<float, 3>& right) noexcept
246{
247 return left[0] * (covariance.xx * right[0] + covariance.xy * right[1] + covariance.xz * right[2]) +
248 left[1] * (covariance.xy * right[0] + covariance.yy * right[1] + covariance.yz * right[2]) +
249 left[2] * (covariance.xz * right[0] + covariance.yz * right[1] + covariance.zz * right[2]);
250}
251
252bool choleskyDecompose(const KinkCovariance& covariance,
253 KinkCovariance& lower) noexcept
254{
255 for (std::size_t row = 0; row < NAdjacentKinks; ++row) {
256 for (std::size_t column = 0; column <= row; ++column) {
257 float value = covariance[row][column];
258 for (std::size_t k = 0; k < column; ++k) {
259 value -= lower[row][k] * lower[column][k];
260 }
261 if (row == column) {
262 if (value <= 0.) {
263 return false;
264 }
265 lower[row][column] = std::sqrt(value);
266 } else {
267 lower[row][column] = value / lower[column][column];
268 }
269 }
270 }
271 return true;
272}
273
274bool choleskySolve(const KinkCovariance& lower, const KinkVector& right,
275 KinkVector& solution) noexcept
276{
277 KinkVector intermediate{};
278 for (std::size_t row = 0; row < NAdjacentKinks; ++row) {
279 float value = right[row];
280 for (std::size_t column = 0; column < row; ++column) {
281 value -= lower[row][column] * intermediate[column];
282 }
283 intermediate[row] = value / lower[row][row];
284 }
285 for (int row = static_cast<int>(NAdjacentKinks) - 1; row >= 0; --row) {
286 float value = intermediate[row];
287 for (std::size_t column = static_cast<std::size_t>(row) + 1;
288 column < NAdjacentKinks; ++column) {
289 value -= lower[column][row] * solution[column];
290 }
291 solution[row] = value / lower[row][row];
292 }
293 return true;
294}
295
296float dotProduct(const KinkVector& left, const KinkVector& right) noexcept
297{
298 float result = 0.;
299 for (std::size_t i = 0; i < NAdjacentKinks; ++i) {
300 result += left[i] * right[i];
301 }
302 return result;
303}
304
305bool referenceSinTheta(const GlobalMeasurement& first,
306 const GlobalMeasurement& third,
307 float& sineTheta) noexcept
308{
309 const float dx = third.x - first.x;
310 const float dy = third.y - first.y;
311 const float dz = third.z - first.z;
312 const float transverse = std::hypot(dx, dy);
313 const float length = std::hypot(transverse, dz);
314 sineTheta = transverse / length;
315 return sineTheta > 0. && sineTheta <= 1.;
316}
317
318} // namespace
319
321 const std::array<GlobalMeasurement, 3>& measurements,
322 TripletFitFactor& result) noexcept
323{
324 TripletGeometry geometry;
325 if (!makeTripletGeometry(measurements, geometry)) {
326 return false;
327 }
328 const float kappaReference = -geometry.phiTilde.value / geometry.rhoPhi.value;
329 TripletFitFactor scratch{
330 {geometry.thetaTilde.value, geometry.phiTilde.value},
331 {geometry.rhoTheta.value, geometry.rhoPhi.value},
332 {}};
333
334 for (std::size_t hit = 0; hit < measurements.size(); ++hit) {
335 for (std::size_t coordinate = 0; coordinate < 3; ++coordinate) {
336 const std::size_t index = 3 * hit + coordinate;
337 const float gradientTheta = geometry.thetaTilde.derivative[index] +
338 kappaReference * geometry.rhoTheta.derivative[index];
339 const float gradientPhi = geometry.phiTilde.derivative[index] +
340 kappaReference * geometry.rhoPhi.derivative[index];
341 scratch.h[hit].theta[coordinate] = gradientTheta;
342 scratch.h[hit].phi[coordinate] = gradientPhi;
343 }
344 }
345 if (!scratch.isValid()) {
346 return false;
347 }
348 result = scratch;
349 return true;
350}
351
353 const TripletFitFactor& firstFactor,
354 const TripletFitFactor& secondFactor,
355 const std::array<GlobalMeasurement, 4>& measurements,
356 const std::array<float, 2>& angularVariance,
358{
359 std::array<float, 2> sineTheta{};
360 if (!referenceSinTheta(measurements[0], measurements[2], sineTheta[0]) ||
361 !referenceSinTheta(measurements[1], measurements[3], sineTheta[1])) {
362 return false;
363 }
364
365 const KinkVector psi{
366 firstFactor.psi.theta, firstFactor.psi.phi,
367 secondFactor.psi.theta, secondFactor.psi.phi};
368 const KinkVector rho{
369 firstFactor.rho.theta, firstFactor.rho.phi,
370 secondFactor.rho.theta, secondFactor.rho.phi};
371 KinkCovariance covariance{};
372 covariance[0][0] = angularVariance[0];
373 covariance[1][1] = angularVariance[0] / (sineTheta[0] * sineTheta[0]);
374 covariance[2][2] = angularVariance[1];
375 covariance[3][3] = angularVariance[1] / (sineTheta[1] * sineTheta[1]);
376
377 // Build H for four unique hits. The factors use slots (0,1,2) and (1,2,3),
378 // so shared hits contribute to the cross-triplet covariance.
379 std::array<std::array<std::array<float, 3>, NAdjacentKinks>, 4> gradients{};
380 for (std::size_t coordinate = 0; coordinate < 3; ++coordinate) {
381 for (std::size_t hit = 0; hit < 3; ++hit) {
382 gradients[hit][0][coordinate] = firstFactor.h[hit].theta[coordinate];
383 gradients[hit][1][coordinate] = firstFactor.h[hit].phi[coordinate];
384 gradients[hit + 1][2][coordinate] = secondFactor.h[hit].theta[coordinate];
385 gradients[hit + 1][3][coordinate] = secondFactor.h[hit].phi[coordinate];
386 }
387 }
388 for (std::size_t hit = 0; hit < measurements.size(); ++hit) {
389 for (std::size_t row = 0; row < NAdjacentKinks; ++row) {
390 for (std::size_t column = 0; column <= row; ++column) {
391 const float contribution = covarianceContraction(
392 gradients[hit][row], measurements[hit].covariance, gradients[hit][column]);
393 covariance[row][column] += contribution;
394 if (row != column) {
395 covariance[column][row] += contribution;
396 }
397 }
398 }
399 }
400
401 KinkCovariance lower{};
402 KinkVector precisionPsi{};
403 KinkVector precisionRho{};
404 if (!choleskyDecompose(covariance, lower) ||
405 !choleskySolve(lower, psi, precisionPsi) ||
406 !choleskySolve(lower, rho, precisionRho)) {
407 return false;
408 }
409 const float rhoPrecisionPsi = dotProduct(rho, precisionPsi);
410 const float rhoPrecisionRho = dotProduct(rho, precisionRho);
411 const float psiPrecisionPsi = dotProduct(psi, precisionPsi);
412 if (rhoPrecisionRho <= 0.) {
413 return false;
414 }
415
416 const float curvature = -rhoPrecisionPsi / rhoPrecisionRho;
417 const float curvatureVariance = 1. / rhoPrecisionRho;
418 const float removedCurvatureTerm = rhoPrecisionPsi * rhoPrecisionPsi / rhoPrecisionRho;
419 float chi2 = psiPrecisionPsi - removedCurvatureTerm;
420 const float chi2Tolerance = 128. * std::numeric_limits<float>::epsilon() *
421 std::max(std::abs(psiPrecisionPsi), std::abs(removedCurvatureTerm));
422 if (chi2 < 0. && chi2 >= -chi2Tolerance) {
423 chi2 = 0.;
424 }
425 if (curvatureVariance <= 0. || chi2 < 0.) {
426 return false;
427 }
428
429 result = {curvature, curvatureVariance, chi2};
430 return true;
431}
432
433} // namespace o2::itsmft::tracking
Hit operator+(const Hit &lhs, const Hit &rhs)
Definition Hit.cxx:46
int32_t i
float chi2
double lower[3]
DualNumber rhoTheta
DualNumber cotangentTheta
DualNumber phiTilde
DualNumber cosineTheta
DualNumber bendingAngle
DualNumber thetaTilde
std::array< float, NCoordinates > derivative
DualNumber rhoPhi
DualNumber transverseArcLength
DualNumber sineTheta
DualNumber index
GLint GLenum GLint x
Definition glcorearb.h:403
GLuint64EXT * result
Definition glcorearb.h:5662
GLuint index
Definition glcorearb.h:781
GLdouble GLdouble right
Definition glcorearb.h:4077
GLint first
Definition glcorearb.h:399
GLint y
Definition glcorearb.h:270
GLsizei const GLfloat * value
Definition glcorearb.h:819
GLint left
Definition glcorearb.h:1979
GLuint GLsizei GLsizei * length
Definition glcorearb.h:790
GLuint GLfloat * val
Definition glcorearb.h:1582
Vec3 operator-(const Vec3 &firstVector, const Vec3 &secondVector)
Vec3 cross(const Vec3 &firstVector, const Vec3 &secondVector)
bool fitAdjacentTripletFactors(const TripletFitFactor &firstFactor, const TripletFitFactor &secondFactor, const std::array< GlobalMeasurement, 4 > &measurements, const std::array< float, 2 > &angularVariance, AdjacentTripletFitResult &result) noexcept
bool makeTripletFitFactor(const std::array< GlobalMeasurement, 3 > &measurements, TripletFitFactor &factor) noexcept
D const SVectorGPU< T, D > & rhs
Definition SMatrixGPU.h:193
MultPolicyGPU< T, R1, R2 >::RepType operator*(const SMatrixGPU< T, D1, D, R1 > &lhs, const SMatrixGPU< T, D, D2, R2 > &rhs)
Definition SMatrixGPU.h:750
std::vector< int > row