Skip to content

Commit 3e64a79

Browse files
committed
Add GPUdoubleCalc and use it for the double-precision track math
The track propagation computes its Jacobian and covariance intermediates in double even when the track is float, because the terms cancel: jj = dx * (dy2dx - f2 * r2inv) is a difference of nearly equal quantities. MSL has no double, and plain float is not an option there. GPUdoubleCalc is compensated two-float arithmetic: the value is mHi + mLo, so the rounding error of each operation is carried explicitly. Over 500k samples of the isolated jj expression, float intermediates reach 3.6e-2 relative error with 23 samples past 1e-3, while the two-float form lands at 5.9e-8, half a float ulp, with none past 1e-3. End to end over 524288 tracks with strongly correlated covariances it keeps the covariance within 1.8e-6 of sqrt(C_ii C_jj) of the CPU double result, where plain float is at 1.2e-5 and the CPU double is itself 1.1e-6 away from a cancellation-free reference. Off Metal it is a plain double, so nothing else moves: substituting the alias back reproduces the previous source exactly, and the files compile unchanged with their real build flags. Cost on an M1 Max, over the whole propagateTo kernel with fast math off: 2.85x the plain float path. Metal has no fp64 at all, so the comparison is against not compiling. The type is defined for every backend so the arithmetic can be tested on the host, where GPUCA_FORCE_DOUBLECALC and GPUCA_FORCE_FLOATCALC select the representation explicitly.
1 parent 1f3f269 commit 3e64a79

5 files changed

Lines changed: 249 additions & 144 deletions

File tree

‎DataFormats/Reconstruction/include/ReconstructionDataFormats/HelixHelper.h‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#define _ALICEO2_HELIX_HELPER_
1818

1919
#include "CommonConstants/MathConstants.h"
20+
#include "GPUCommonDouble.h"
2021
#include "MathUtils/Utils.h"
2122
#include "MathUtils/Primitive2D.h"
2223

@@ -247,8 +248,8 @@ struct CrossInfo {
247248
auto tgp = trcL.getSnp() * cspi;
248249
float kx = traxL.c - traxL.s * tgp;
249250
float ky = traxL.s + traxL.c * tgp;
250-
double dk = dx * kx + dy * ky;
251-
double det = dk * dk - cspi2 * (dx * dx + dy * dy - traxH.rC * traxH.rC);
251+
o2::gpu::GPUdoubleCalc dk = dx * kx + dy * ky;
252+
o2::gpu::GPUdoubleCalc det = dk * dk - cspi2 * (dx * dx + dy * dy - traxH.rC * traxH.rC);
252253
if (det > 0) { // 2 crossings
253254
det = o2::gpu::GPUCommonMath::Sqrt(det);
254255
float t0 = (-dk + det) * cspi2;

‎DataFormats/Reconstruction/src/TrackParametrization.cxx‎

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
/// @brief
1616

1717
#include "ReconstructionDataFormats/TrackParametrization.h"
18+
#include "GPUCommonDouble.h"
1819
#include "ReconstructionDataFormats/Vertex.h"
1920
#include "ReconstructionDataFormats/DCA.h"
2021
#include <MathUtils/Cartesian.h>
@@ -477,7 +478,7 @@ GPUd() bool TrackParametrization<value_T>::getYZAt(value_t xk, value_t b, value_
477478
if (gpu::CAMath::Abs(r2) < constants::math::Almost0) {
478479
return false;
479480
}
480-
double dy2dx = (f1 + f2) / (r1 + r2);
481+
GPUdoubleCalc dy2dx = (f1 + f2) / (r1 + r2);
481482
y += dx * dy2dx;
482483
if (gpu::CAMath::Abs(x2r) < 0.05f) {
483484
z += dx * (r2 + f2 * dy2dx) * getTgl();
@@ -692,7 +693,7 @@ GPUd() bool TrackParametrization<value_T>::getXatLabR(value_t r, value_t& x, val
692693
// DirOutward (==1) - go along the track (increasing mX)
693694
// DirInward (==-1) - go backward (decreasing mX)
694695
//
695-
const double fy = mP[0], sn = mP[2];
696+
const GPUdoubleCalc fy = mP[0], sn = mP[2];
696697
const value_t kEps = 1.e-6;
697698
//
698699
if (gpu::CAMath::Abs(getSnp()) > constants::math::Almost1) {
@@ -711,7 +712,7 @@ GPUd() bool TrackParametrization<value_T>::getXatLabR(value_t r, value_t& x, val
711712
if (r0 <= constants::math::Almost0) {
712713
return false; // the track is concentric to circle
713714
}
714-
double tR2r0 = 1., g = 0., tmp = 0.;
715+
GPUdoubleCalc tR2r0 = 1., g = 0., tmp = 0.;
715716
if (gpu::CAMath::Abs(circle.rC - r0) > kEps) {
716717
tR2r0 = circle.rC / r0;
717718
g = 0.5f * (r * r / (r0 * circle.rC) - tR2r0 - 1.f / tR2r0);
@@ -786,7 +787,7 @@ GPUd() bool TrackParametrization<value_T>::getXatLabR(value_t r, value_t& x, val
786787
}
787788
// this is a straight track
788789
if (gpu::CAMath::Abs(sn) >= constants::math::Almost1) { // || to Y axis
789-
double det = (r - mX) * (r + mX);
790+
GPUdoubleCalc det = (r - mX) * (r + mX);
790791
if (det < 0.f) {
791792
return false; // does not reach raduis r
792793
}
@@ -815,7 +816,7 @@ GPUd() bool TrackParametrization<value_T>::getXatLabR(value_t r, value_t& x, val
815816
}
816817
}
817818
} else if (gpu::CAMath::Abs(sn) <= constants::math::Almost0) { // || to X axis
818-
double det = (r - fy) * (r + fy);
819+
GPUdoubleCalc det = (r - fy) * (r + fy);
819820
if (det < 0.) {
820821
return false; // does not reach raduis r
821822
}

0 commit comments

Comments
 (0)