@@ -49,9 +49,14 @@ struct TrackCovI {
4949 static constexpr float XRegErrFactor = 10 .f;
5050 static constexpr float XRegNone = -1 .f;
5151
52+ // Legacy (mOldMode) factor for the conversion of the track covYY to a dummy covXX: instead of
53+ // deriving the X information from the track slopes, the old code assigned sigma_x^2 = 5*Cyy and
54+ // left the XY,XZ information terms at 0, see DCAFitterN::mOldMode.
55+ static constexpr float XerrFactorOld = 5 .f;
56+
5257 GPUdDefault () TrackCovI() = default ;
5358
54- GPUd () bool set (const o2::track::TrackParCov& trc, float xRegErrFactor = XRegErrFactor)
59+ GPUd () bool set (const o2::track::TrackParCov& trc, float xRegErrFactor = XRegErrFactor, bool oldMode = true )
5560 {
5661 // Invert the 2D covariance of the measured track position (Y,Z).
5762 float cyy = trc.getSigmaY2 (), czz = trc.getSigmaZ2 (), cyz = trc.getSigmaZY ();
@@ -66,6 +71,11 @@ struct TrackCovI {
6671 syy = czz * detYZI;
6772 syz = -cyz * detYZI;
6873 szz = cyy * detYZI;
74+ if (oldMode) { // dummy X error, no slope-driven X information (xRegErrFactor is ignored)
75+ sxy = sxz = 0 .f ;
76+ sxx = 1 .f / (cyy * XerrFactorOld);
77+ return res;
78+ }
6979 const float cspI = 1 .f / trc.getCsp ();
7080 const float dydx = trc.getSnp () * cspI;
7181 const float dzdx = trc.getTgl () * cspI;
@@ -177,6 +187,10 @@ class DCAFitterN
177187 static_assert (N >= NMin && N <= NMax, " N prongs outside of allowed range" );
178188 }
179189
190+ // Setters and getters for the temporary mOldMode flag, which controls the behavior of the covariance matrix calculation.
191+ bool isOldMode () const { return mOldMode ; }
192+ void setOldMode (bool v) { mOldMode = v; }
193+
180194 // =========================================================================
181195 // /< return PCA candidate, by default best on is provided (no check for the index validity)
182196 GPUd () const Vec3D& getPCACandidate (int cand = 0 ) const { return mPCA [mOrder [cand]]; }
@@ -346,6 +360,29 @@ class DCAFitterN
346360 arrmat[ZZ ] += tcov.szz ;
347361 }
348362
363+ // /< generate 3D matrix for track rotation to global frame (mOldMode calcPCACovMatrix only)
364+ GPUd () MatStd3D getTrackRotMatrix (int i) const
365+ {
366+ MatStd3D mat;
367+ mat (2 , 2 ) = 1 ;
368+ mat (0 , 0 ) = mat (1 , 1 ) = mTrAux [i].c ;
369+ mat (0 , 1 ) = -mTrAux [i].s ;
370+ mat (1 , 0 ) = mTrAux [i].s ;
371+ return mat;
372+ }
373+
374+ // /< generate covariance matrix of track position, adding fake X error (mOldMode calcPCACovMatrix only)
375+ GPUd () MatSym3D getTrackCovMatrix (int i, int cand = 0 ) const
376+ {
377+ const auto & trc = mCandTr [mOrder [cand]][i];
378+ MatSym3D mat;
379+ mat (0 , 0 ) = trc.getSigmaY2 () * TrackCovI::XerrFactorOld;
380+ mat (1 , 1 ) = trc.getSigmaY2 ();
381+ mat (2 , 2 ) = trc.getSigmaZ2 ();
382+ mat (2 , 1 ) = trc.getSigmaZY ();
383+ return mat;
384+ }
385+
349386 GPUd () void assign (int ) {}
350387 template <class T , class ... Tr>
351388 GPUd () void assign (int i, const T& t, const Tr&... args)
@@ -444,7 +481,19 @@ class DCAFitterN
444481 float mMaxStep = 2.0 ; // Max step for propagation with Propagator
445482 int mFitterID = 0 ; // locat fitter ID (mostly for debugging)
446483 size_t mCallID = 0 ;
447- ClassDefNV (DCAFitterN, 3 );
484+
485+ // /< Temporary:
486+ // /< Reproduce exactly the behaviour preceding the x-axis error treatment fix (PR15610, commit
487+ // /< 775528b421ce6b9ec381a759c664cd5a2ab76fe6): the track information matrix gets a dummy X
488+ // /< variance TrackCovI::XerrFactorOld*Cyy with no XY/XZ terms (hence the fitted PCA and chi2 use
489+ // /< the old, artificial longitudinal error), the PCA covariance is obtained by inverting the sum
490+ // /< of the inverses of the rotated dummy track covariances, the chi2 Hessian curvature term is
491+ // /< accumulated as before and the Newton step updates only mTrPos by a Taylor expansion, leaving
492+ // /< the candidate tracks (and thus the derivatives) at the seed X. For validation/comparison only.
493+ // /< Activate it by default until the reason for D0 loss will be clarified.
494+ bool mOldMode = true ;
495+
496+ ClassDefNV (DCAFitterN, 4 );
448497};
449498
450499// /_________________________________________________________________________
@@ -696,10 +745,13 @@ GPUd() void DCAFitterN<N, Args...>::calcChi2Derivatives()
696745 const auto & dr1j = mDResidDx [k][j]; // vector of k-th residuals 1st derivative over X param of track j
697746 const auto & cidrkj = covIDrDx[i][k]; // vector covI_k * dres_k/dx_i
698747 dchi2 += o2::math_utils::Dot (dr1j, cidrkj);
699- if (i == j) {
700- const auto & res = mTrRes [mCurHyp ][k]; // vector of residuals of track k
701- const auto & covI = mTrcEInv [mCurHyp ][k]; // inverse cov matrix of track k
702- const auto & dr2ij = mD2ResidDx2 [k][i]; // vector of k-th residuals 2nd derivative over X param i
748+ // A trajectory has a second derivative only with respect to its own X parameter, hence the
749+ // curvature term contributes only to the diagonal H_ii. The mOldMode variant instead added
750+ // it wherever k == j, i.e. also to the off-diagonal elements of the column j.
751+ if (mOldMode ? (k == j) : (i == j)) {
752+ const auto & res = mTrRes [mCurHyp ][k]; // vector of residuals of track k
753+ const auto & covI = mTrcEInv [mCurHyp ][k]; // inverse cov matrix of track k
754+ const auto & dr2ij = mD2ResidDx2 [k][mOldMode ? j : i]; // vector of k-th residuals 2nd derivative over X param
703755 dchi2 += res[0 ] * (covI.sxx * dr2ij[0 ] + covI.sxy * dr2ij[1 ] + covI.sxz * dr2ij[2 ]) +
704756 res[1 ] * (covI.sxy * dr2ij[0 ] + covI.syy * dr2ij[1 ] + covI.syz * dr2ij[2 ]) +
705757 res[2 ] * (covI.sxz * dr2ij[0 ] + covI.syz * dr2ij[1 ] + covI.szz * dr2ij[2 ]);
@@ -726,13 +778,14 @@ GPUd() void DCAFitterN<N, Args...>::calcChi2DerivativesNoErr()
726778 for (int i = N; i--;) {
727779 for (int j = i + 1 ; j--;) {
728780 auto & dchi2 = mD2Chi2Dx2 [i][j];
729- dchi2 = 0 .;
781+ // A trajectory has a second derivative only with respect to its own X parameter, hence the
782+ // curvature term contributes only to H_ii. The mOldMode variant instead added the single
783+ // res_i * D2res_i/Dx_i/Dx_j term to every element with i >= j.
784+ dchi2 = mOldMode ? o2::math_utils::Dot (mTrRes [mCurHyp ][i], mD2ResidDx2 [i][j]) : 0 .;
730785 for (int k = N; k--;) {
731786 // Gauss-Newton term, present for diagonal and mixed elements.
732787 dchi2 += o2::math_utils::Dot (mDResidDx [k][i], mDResidDx [k][j]);
733- // A trajectory has a second derivative only with respect to its own
734- // X parameter, hence the curvature term contributes only to H_ii.
735- if (i == j) {
788+ if (!mOldMode && i == j) {
736789 dchi2 += o2::math_utils::Dot (mTrRes [mCurHyp ][k], mD2ResidDx2 [k][i]);
737790 }
738791 }
@@ -763,7 +816,7 @@ GPUd() bool DCAFitterN<N, Args...>::recalculatePCAWithErrors(int cand)
763816 mCurHyp = mOrder [cand];
764817 if (mUseAbsDCA ) {
765818 for (int i = N; i--;) {
766- if (!mTrcEInv [mCurHyp ][i].set (mCandTr [mCurHyp ][i])) { // prepare inverse cov.matrices at starting point
819+ if (!mTrcEInv [mCurHyp ][i].set (mCandTr [mCurHyp ][i], TrackCovI::XRegErrFactor, mOldMode )) { // prepare inverse cov.matrices at starting point
767820 if (mLoggerBadCov .needToLog ()) {
768821#ifndef GPUCA_GPUCODE
769822 printf (" fitter %d: error (%ld muted): overrode invalid track covariance from %s\n " ,
@@ -872,13 +925,34 @@ GPUd() o2::math_utils::SMatrix<double, 3, 3, o2::math_utils::MatRepSym<double, 3
872925 // the minimization (TrackCovI::XRegNone), otherwise the vertex error along the
873926 // weakly constrained direction would be defined by that dummy term.
874927 // A singular/ill-conditioned sum is caught below and replaced by a loose dummy.
928+ if (mOldMode ) { // sum the inverses of the rotated dummy-X track covariances and invert the sum
929+ MatSym3D covm;
930+ int nAdded = 0 ;
931+ for (int i = N; i--;) { // calculate sum of inverses
932+ // RS by using Similarity(mTrCFVT[mOrder[cand]][i], getTrackCovMatrix(i, cand)) we underestimate the error, use simple rotation
933+ MatSym3D covTr = o2::math_utils::Similarity (getTrackRotMatrix (i), getTrackCovMatrix (i, cand));
934+ if (covTr.Invert ()) {
935+ covm += covTr;
936+ nAdded++;
937+ }
938+ }
939+ if (nAdded && covm.Invert ()) {
940+ return covm;
941+ }
942+ // correct way has failed, use simple sum
943+ MatSym3D covmSum;
944+ for (int i = N; i--;) {
945+ covmSum += o2::math_utils::Similarity (getTrackRotMatrix (i), getTrackCovMatrix (i, cand));
946+ }
947+ return covmSum;
948+ }
875949 MatSym3D info;
876950 auto * arrmat = info.Array ();
877951 memset (arrmat, 0 , sizeof (info));
878952 const int ord = mOrder [cand];
879953 for (int i = N; i--;) {
880954 TrackCovI tcov;
881- tcov.set (mCandTr [ord][i], TrackCovI::XRegNone);
955+ tcov.set (mCandTr [ord][i], TrackCovI::XRegNone, mOldMode );
882956 addRotatedTrackInfo (arrmat, mTrAux [i], tcov);
883957 }
884958 const double maxDiag = o2::gpu::GPUCommonMath::Max (o2::gpu::GPUCommonMath::Max (info (0 , 0 ), info (1 , 1 )), info (2 , 2 ));
@@ -977,6 +1051,16 @@ GPUd() bool DCAFitterN<N, Args...>::correctTracks(const VecND& corrX)
9771051 // Propagator and material corrections): the Newton corrections are small, but the track state must
9781052 // stay synchronized with mTrPos for the next derivative update. The final propagation to the PCA
9791053 // (propagateTracksToVertex) refetches the original tracks and does use the full transport.
1054+ if (mOldMode ) { // update mTrPos only, by the Taylor expansion, leaving mCandTr at the previous X
1055+ for (int i = N; i--;) {
1056+ const auto & trDer = mTrDer [mCurHyp ][i];
1057+ auto dx2h = 0.5 * corrX[i] * corrX[i];
1058+ mTrPos [mCurHyp ][i][0 ] -= corrX[i];
1059+ mTrPos [mCurHyp ][i][1 ] -= trDer.dydx * corrX[i] - dx2h * trDer.d2ydx2 ;
1060+ mTrPos [mCurHyp ][i][2 ] -= trDer.dzdx * corrX[i] - dx2h * trDer.d2zdx2 ;
1061+ }
1062+ return true ;
1063+ }
9801064 for (int i = N; i--;) {
9811065 auto & trc = mCandTr [mCurHyp ][i];
9821066 const float x = static_cast <float >(mTrPos [mCurHyp ][i][0 ] - corrX[i]);
@@ -1080,7 +1164,7 @@ GPUd() bool DCAFitterN<N, Args...>::minimizeChi2()
10801164 return false ;
10811165 }
10821166 setTrackPos (mTrPos [mCurHyp ][i], mCandTr [mCurHyp ][i]); // prepare positions
1083- if (!mTrcEInv [mCurHyp ][i].set (mCandTr [mCurHyp ][i])) { // prepare inverse cov.matrices at starting point
1167+ if (!mTrcEInv [mCurHyp ][i].set (mCandTr [mCurHyp ][i], TrackCovI::XRegErrFactor, mOldMode )) { // prepare inverse cov.matrices at starting point
10841168 if (mLoggerBadCov .needToLog ()) {
10851169#ifndef GPUCA_GPUCODE
10861170 printf (" fitter %d: error (%ld muted): overrode invalid track covariance from %s\n " ,
0 commit comments