Skip to content

Commit edb2171

Browse files
committed
Switch for pre PR-15610 compatibility mode
If setOldMode(true) is set, the behavior of the DCAFitter will reproduce the pre-PR-15610 version.
1 parent 28d6213 commit edb2171

2 files changed

Lines changed: 100 additions & 11 deletions

File tree

Common/DCAFitter/include/DCAFitter/DCAFitterN.h

Lines changed: 94 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,11 @@ 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

5459
GPUd() bool set(const o2::track::TrackParCov& trc, float xRegErrFactor = XRegErrFactor)
@@ -66,6 +71,11 @@ struct TrackCovI {
6671
syy = czz * detYZI;
6772
syz = -cyz * detYZI;
6873
szz = cyy * detYZI;
74+
if (mOldMode) { // 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,18 @@ 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+
bool mOldMode = false;
494+
495+
ClassDefNV(DCAFitterN, 4);
448496
};
449497

450498
///_________________________________________________________________________
@@ -696,10 +744,13 @@ GPUd() void DCAFitterN<N, Args...>::calcChi2Derivatives()
696744
const auto& dr1j = mDResidDx[k][j]; // vector of k-th residuals 1st derivative over X param of track j
697745
const auto& cidrkj = covIDrDx[i][k]; // vector covI_k * dres_k/dx_i
698746
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
747+
// A trajectory has a second derivative only with respect to its own X parameter, hence the
748+
// curvature term contributes only to the diagonal H_ii. The mOldMode variant instead added
749+
// it wherever k == j, i.e. also to the off-diagonal elements of the column j.
750+
if (mOldMode ? (k == j) : (i == j)) {
751+
const auto& res = mTrRes[mCurHyp][k]; // vector of residuals of track k
752+
const auto& covI = mTrcEInv[mCurHyp][k]; // inverse cov matrix of track k
753+
const auto& dr2ij = mD2ResidDx2[k][mOldMode ? j : i]; // vector of k-th residuals 2nd derivative over X param
703754
dchi2 += res[0] * (covI.sxx * dr2ij[0] + covI.sxy * dr2ij[1] + covI.sxz * dr2ij[2]) +
704755
res[1] * (covI.sxy * dr2ij[0] + covI.syy * dr2ij[1] + covI.syz * dr2ij[2]) +
705756
res[2] * (covI.sxz * dr2ij[0] + covI.syz * dr2ij[1] + covI.szz * dr2ij[2]);
@@ -726,13 +777,14 @@ GPUd() void DCAFitterN<N, Args...>::calcChi2DerivativesNoErr()
726777
for (int i = N; i--;) {
727778
for (int j = i + 1; j--;) {
728779
auto& dchi2 = mD2Chi2Dx2[i][j];
729-
dchi2 = 0.;
780+
// A trajectory has a second derivative only with respect to its own X parameter, hence the
781+
// curvature term contributes only to H_ii. The mOldMode variant instead added the single
782+
// res_i * D2res_i/Dx_i/Dx_j term to every element with i >= j.
783+
dchi2 = mOldMode ? o2::math_utils::Dot(mTrRes[mCurHyp][i], mD2ResidDx2[i][j]) : 0.;
730784
for (int k = N; k--;) {
731785
// Gauss-Newton term, present for diagonal and mixed elements.
732786
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) {
787+
if (!mOldMode && i == j) {
736788
dchi2 += o2::math_utils::Dot(mTrRes[mCurHyp][k], mD2ResidDx2[k][i]);
737789
}
738790
}
@@ -763,7 +815,7 @@ GPUd() bool DCAFitterN<N, Args...>::recalculatePCAWithErrors(int cand)
763815
mCurHyp = mOrder[cand];
764816
if (mUseAbsDCA) {
765817
for (int i = N; i--;) {
766-
if (!mTrcEInv[mCurHyp][i].set(mCandTr[mCurHyp][i])) { // prepare inverse cov.matrices at starting point
818+
if (!mTrcEInv[mCurHyp][i].set(mCandTr[mCurHyp][i], TrackCovI::XRegErrFactor, mOldMode)) { // prepare inverse cov.matrices at starting point
767819
if (mLoggerBadCov.needToLog()) {
768820
#ifndef GPUCA_GPUCODE
769821
printf("fitter %d: error (%ld muted): overrode invalid track covariance from %s\n",
@@ -872,6 +924,27 @@ GPUd() o2::math_utils::SMatrix<double, 3, 3, o2::math_utils::MatRepSym<double, 3
872924
// the minimization (TrackCovI::XRegNone), otherwise the vertex error along the
873925
// weakly constrained direction would be defined by that dummy term.
874926
// A singular/ill-conditioned sum is caught below and replaced by a loose dummy.
927+
if (mOldMode) { // sum the inverses of the rotated dummy-X track covariances and invert the sum
928+
MatSym3D covm;
929+
int nAdded = 0;
930+
for (int i = N; i--;) { // calculate sum of inverses
931+
// RS by using Similarity(mTrCFVT[mOrder[cand]][i], getTrackCovMatrix(i, cand)) we underestimate the error, use simple rotation
932+
MatSym3D covTr = o2::math_utils::Similarity(getTrackRotMatrix(i), getTrackCovMatrix(i, cand));
933+
if (covTr.Invert()) {
934+
covm += covTr;
935+
nAdded++;
936+
}
937+
}
938+
if (nAdded && covm.Invert()) {
939+
return covm;
940+
}
941+
// correct way has failed, use simple sum
942+
MatSym3D covmSum;
943+
for (int i = N; i--;) {
944+
covmSum += o2::math_utils::Similarity(getTrackRotMatrix(i), getTrackCovMatrix(i, cand));
945+
}
946+
return covmSum;
947+
}
875948
MatSym3D info;
876949
auto* arrmat = info.Array();
877950
memset(arrmat, 0, sizeof(info));
@@ -977,6 +1050,16 @@ GPUd() bool DCAFitterN<N, Args...>::correctTracks(const VecND& corrX)
9771050
// Propagator and material corrections): the Newton corrections are small, but the track state must
9781051
// stay synchronized with mTrPos for the next derivative update. The final propagation to the PCA
9791052
// (propagateTracksToVertex) refetches the original tracks and does use the full transport.
1053+
if (mOldMode) { // update mTrPos only, by the Taylor expansion, leaving mCandTr at the previous X
1054+
for (int i = N; i--;) {
1055+
const auto& trDer = mTrDer[mCurHyp][i];
1056+
auto dx2h = 0.5 * corrX[i] * corrX[i];
1057+
mTrPos[mCurHyp][i][0] -= corrX[i];
1058+
mTrPos[mCurHyp][i][1] -= trDer.dydx * corrX[i] - dx2h * trDer.d2ydx2;
1059+
mTrPos[mCurHyp][i][2] -= trDer.dzdx * corrX[i] - dx2h * trDer.d2zdx2;
1060+
}
1061+
return true;
1062+
}
9801063
for (int i = N; i--;) {
9811064
auto& trc = mCandTr[mCurHyp][i];
9821065
const float x = static_cast<float>(mTrPos[mCurHyp][i][0] - corrX[i]);
@@ -1080,7 +1163,7 @@ GPUd() bool DCAFitterN<N, Args...>::minimizeChi2()
10801163
return false;
10811164
}
10821165
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
1166+
if (!mTrcEInv[mCurHyp][i].set(mCandTr[mCurHyp][i], TrackCovI::XRegErrFactor, mOldMode)) { // prepare inverse cov.matrices at starting point
10841167
if (mLoggerBadCov.needToLog()) {
10851168
#ifndef GPUCA_GPUCODE
10861169
printf("fitter %d: error (%ld muted): overrode invalid track covariance from %s\n",

Common/DCAFitter/test/testDCAFitterN.cxx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@ inline void printStat(const FitStatusArray& a)
171171

172172
BOOST_AUTO_TEST_CASE(DCAFitterNProngs)
173173
{
174+
constexpr bool oldMode = false; // if true, use the old mode of DCAFitterN, which is less correct but faster
174175
constexpr int NTest = 10000;
175176
o2::utils::TreeStreamRedirector outStream("dcafitterNTest.root");
176177

@@ -196,6 +197,7 @@ BOOST_AUTO_TEST_CASE(DCAFitterNProngs)
196197
std::memset(fitstat.data(), 0, sizeof(fitstat));
197198

198199
o2::vertexing::DCAFitterN<2> ft; // 2 prong fitter
200+
ft.setOldMode(oldMode); // use the old mode of DCAFitterN
199201
ft.setBz(bz);
200202
ft.setPropagateToPCA(true); // After finding the vertex, propagate tracks to the DCA. This is default anyway
201203
ft.setMaxR(200); // do not consider V0 seeds with 2D circles crossing above this R. This is default anyway
@@ -280,6 +282,7 @@ BOOST_AUTO_TEST_CASE(DCAFitterNProngs)
280282
std::memset(fitstat.data(), 0, sizeof(fitstat));
281283

282284
o2::vertexing::DCAFitterN<2> ft; // 2 prong fitter
285+
ft.setOldMode(oldMode); // use the old mode of DCAFitterN
283286
ft.setBz(bz);
284287
ft.setPropagateToPCA(true); // After finding the vertex, propagate tracks to the DCA. This is default anyway
285288
ft.setMaxR(200); // do not consider V0 seeds with 2D circles crossing above this R. This is default anyway
@@ -366,6 +369,7 @@ BOOST_AUTO_TEST_CASE(DCAFitterNProngs)
366369
std::memset(fitstat.data(), 0, sizeof(fitstat));
367370

368371
o2::vertexing::DCAFitterN<2> ft; // 2 prong fitter
372+
ft.setOldMode(oldMode); // use the old mode of DCAFitterN
369373
ft.setBz(bz);
370374
ft.setPropagateToPCA(true); // After finding the vertex, propagate tracks to the DCA. This is default anyway
371375
ft.setMaxR(200); // do not consider V0 seeds with 2D circles crossing above this R. This is default anyway
@@ -451,6 +455,7 @@ BOOST_AUTO_TEST_CASE(DCAFitterNProngs)
451455
std::memset(fitstat.data(), 0, sizeof(fitstat));
452456

453457
o2::vertexing::DCAFitterN<2> ft; // 2 prong fitter
458+
ft.setOldMode(oldMode); // use the old mode of DCAFitterN
454459
ft.setBz(bz);
455460
ft.setPropagateToPCA(true); // After finding the vertex, propagate tracks to the DCA. This is default anyway
456461
ft.setMaxR(200); // do not consider V0 seeds with 2D circles crossing above this R. This is default anyway
@@ -535,6 +540,7 @@ BOOST_AUTO_TEST_CASE(DCAFitterNProngs)
535540
std::memset(fitstat.data(), 0, sizeof(fitstat));
536541

537542
o2::vertexing::DCAFitterN<3> ft; // 3 prong fitter
543+
ft.setOldMode(oldMode); // use the old mode of DCAFitterN
538544
ft.setBz(bz);
539545
ft.setPropagateToPCA(true); // After finding the vertex, propagate tracks to the DCA. This is default anyway
540546
ft.setMaxR(200); // do not consider V0 seeds with 2D circles crossing above this R. This is default anyway

0 commit comments

Comments
 (0)