Skip to content

Commit a1c7f8d

Browse files
committed
Switch for the pre-PR-15610 compatibility mode (set as default)
If isOldMode() is true (default), the behavior of the DCAFitter will reproduce the pre-PR-15610 version. One can swith any time (before calling DCAFitter::process(..)) between the modes via DCAFitter::setOldMode(bool v). In the o2-secondary-vertexing-workflow and related study workflows it can be steered by the svertexer.oldDCAFitterMode=true (default) configurable param.
1 parent 28d6213 commit a1c7f8d

6 files changed

Lines changed: 109 additions & 13 deletions

File tree

Common/DCAFitter/include/DCAFitter/DCAFitterN.h

Lines changed: 97 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -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",

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

Detectors/GlobalTrackingWorkflow/study/src/SVStudy.cxx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@ void SVStudySpec::updateTimeDependentParams(ProcessingContext& pc)
175175
const auto& svparam = o2::vertexing::SVertexerParams::Instance();
176176
// Note: reading of the ITS AlpideParam needed for ITS timing is done by the RecoContainer
177177
mFitterV0.setBz(mBz);
178+
mFitterV0.setOldMode(svparam.oldDCAFitterMode);
178179
mFitterV0.setUseAbsDCA(svparam.useAbsDCA);
179180
mFitterV0.setPropagateToPCA(false);
180181
mFitterV0.setMaxR(svparam.maxRIni);

Detectors/GlobalTrackingWorkflow/study/src/TrackMCStudy.cxx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,7 @@ void TrackMCStudy::updateTimeDependentParams(ProcessingContext& pc)
211211
if (mCheckSV) {
212212
const auto& svparam = o2::vertexing::SVertexerParams::Instance();
213213
mFitterV0.setBz(o2::base::Propagator::Instance()->getNominalBz());
214+
mFitterV0.setOldMode(svparam.oldDCAFitterMode);
214215
mFitterV0.setUseAbsDCA(svparam.useAbsDCA);
215216
mFitterV0.setPropagateToPCA(false);
216217
mFitterV0.setMaxR(svparam.maxRIni);

Detectors/Vertexing/include/DetectorsVertexing/SVertexerParams.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ namespace vertexing
3030
struct SVertexerParams : public o2::conf::ConfigurableParamHelper<SVertexerParams> {
3131

3232
// parameters
33+
bool oldDCAFitterMode = true; ///< pre(old) or post(new) PR15610+15784 behaviour of DCAFitter
3334
bool createFullV0s = false; ///< fill V0s prongs/kinematics
3435
bool createFullCascades = false; ///< fill cascades prongs/kinematics
3536
bool createFull3Bodies = false; ///< fill 3-body decays prongs/kinematics

Detectors/Vertexing/src/SVertexer.cxx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,7 @@ void SVertexer::setupThreads()
352352
mBz = o2::base::Propagator::Instance()->getNominalBz();
353353
int fitCounter = 0;
354354
for (auto& fitter : mFitterV0) {
355+
fitter.setOldMode(mSVParams->oldDCAFitterMode);
355356
fitter.setFitterID(fitCounter++);
356357
fitter.setBz(mBz);
357358
fitter.setUseAbsDCA(mSVParams->useAbsDCA);
@@ -372,6 +373,7 @@ void SVertexer::setupThreads()
372373
mFitterCasc.resize(mNThreads);
373374
fitCounter = 1000;
374375
for (auto& fitter : mFitterCasc) {
376+
fitter.setOldMode(mSVParams->oldDCAFitterMode);
375377
fitter.setFitterID(fitCounter++);
376378
fitter.setBz(mBz);
377379
fitter.setUseAbsDCA(mSVParams->useAbsDCA);
@@ -393,6 +395,7 @@ void SVertexer::setupThreads()
393395
mFitter3body.resize(mNThreads);
394396
fitCounter = 2000;
395397
for (auto& fitter : mFitter3body) {
398+
fitter.setOldMode(mSVParams->oldDCAFitterMode);
396399
fitter.setFitterID(fitCounter++);
397400
fitter.setBz(mBz);
398401
fitter.setUseAbsDCA(mSVParams->useAbsDCA);

0 commit comments

Comments
 (0)