Project
Loading...
Searching...
No Matches
DCAFitterN.h
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
17
18#ifndef _ALICEO2_DCA_FITTERN_
19#define _ALICEO2_DCA_FITTERN_
20
23#include "MathUtils/Cartesian.h"
25
26namespace o2
27{
28namespace vertexing
29{
30
33struct TrackCovI {
34 // Independent elements of the symmetric 3D information matrix
35 // H^T Cyz^{-1} H. A track constrains Y and Z at a given X through
36 // H = {{-dY/dX, 1, 0}, {-dZ/dX, 0, 1}}.
37 float sxx, sxy, sxz, syy, syz, szz;
38
39 // H^T Cyz^{-1} H is singular by construction (rank 2): the chi2 is invariant
40 // under sliding the reference point along the trajectory. A weak dummy X error
41 // sigma_x^2 = XRegErrFactor * Cyy is added to the sxx element to regularize it.
42 // This is needed ONLY to keep the Newton Hessian of the chi2 minimization
43 // invertible for (nearly) collinear prongs.
44 // It must NOT be used when the single track information matrices are summed to
45 // obtain the PCA covariance (see calcPCACovMatrix): there the regularization
46 // would define the longitudinal vertex error by this dummy term instead of by
47 // the track slopes, i.e. reintroduce the very artifact it replaces. Pass
48 // XRegNone in that case.
49 static constexpr float XRegErrFactor = 10.f;
50 static constexpr float XRegNone = -1.f;
51
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
57 GPUdDefault() TrackCovI() = default;
58
59 GPUd() bool set(const o2::track::TrackParCov& trc, float xRegErrFactor = XRegErrFactor, bool oldMode = true)
60 {
61 // Invert the 2D covariance of the measured track position (Y,Z).
62 float cyy = trc.getSigmaY2(), czz = trc.getSigmaZ2(), cyz = trc.getSigmaZY();
63 float detYZ = cyy * czz - cyz * cyz;
64 bool res = true;
65 if (detYZ <= 0.) {
66 cyz = o2::gpu::GPUCommonMath::Sqrt(cyy * czz) * (cyz > 0 ? 0.98f : -0.98f);
67 detYZ = cyy * czz - cyz * cyz;
68 res = false;
69 }
70 auto detYZI = 1. / detYZ;
71 syy = czz * detYZI;
72 syz = -cyz * detYZI;
73 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 }
79 const float cspI = 1.f / trc.getCsp();
80 const float dydx = trc.getSnp() * cspI;
81 const float dzdx = trc.getTgl() * cspI;
82 sxy = -(syy * dydx + syz * dzdx);
83 sxz = -(syz * dydx + szz * dzdx);
84 sxx = dydx * dydx * syy + 2.f * dydx * dzdx * syz + dzdx * dzdx * szz;
85 if (xRegErrFactor > 0.f) { // regularize the sxx term only, this preserves the YZ block exactly
86 sxx += 1.f / (cyy * xRegErrFactor);
87 }
88 return res;
89 }
90};
91
94struct TrackDeriv {
96 GPUdDefault() TrackDeriv() = default;
97 GPUd() TrackDeriv(const o2::track::TrackPar& trc, float bz) { set(trc, bz); }
98 GPUd() void set(const o2::track::TrackPar& trc, float bz)
99 {
100 float snp = trc.getSnp(), csp = o2::gpu::GPUCommonMath::Sqrt((1. - snp) * (1. + snp)), cspI = 1. / csp, crv2c = trc.getCurvature(bz) * cspI;
101 dydx = snp * cspI; // = snp/csp
102 dzdx = trc.getTgl() * cspI; // = tgl/csp
103 d2ydx2 = crv2c * cspI * cspI; // = crv/csp^3
104 d2zdx2 = crv2c * dzdx * dydx; // = crv*tgl*snp/csp^3
105 }
106};
107
111 size_t evCount{0};
112 size_t nextLog{1};
113 GPUdi() bool needToLog()
114 {
115 if (++evCount > nextLog) {
116 nextLog *= 2;
117 return true;
118 }
119 return false;
120 }
122 {
123 evCount = 0;
124 nextLog = 1;
125 }
126};
127
128template <int N, typename... Args>
130{
131 static constexpr double NMin = 2;
132 static constexpr double NMax = 4;
133 static constexpr double NInv = 1. / N;
134 static constexpr int MAXHYP = 2;
135 using Track = o2::track::TrackParCov;
138
145 using TrackCoefVtx = MatStd3D;
146 using ArrTrack = std::array<Track, N>; // container for prongs (tracks) at single vertex cand.
147 using ArrTrackCovI = std::array<TrackCovI, N>; // container for inv.cov.matrices at single vertex cand.
148 using ArrTrCoef = std::array<TrackCoefVtx, N>; // container of TrackCoefVtx coefficients at single vertex cand.
149 using ArrTrDer = std::array<TrackDeriv, N>; // container of Track 1st and 2nd derivative over their X param
150 using ArrTrPos = std::array<Vec3D, N>; // container of Track positions
151
152 public:
153 enum BadCovPolicy : uint8_t { // if encountering non-positive defined cov. matrix, the choice is:
154 Discard = 0, // stop evaluation
155 Override = 1, // override correlation coef. to have cov.matrix pos.def and continue
156 OverrideAndFlag = 2 // override correlation coef. to have cov.matrix pos.def, set mPropFailed flag of corresponding candidate to true and continue (up to the user to check the flag)
157 };
158
159 enum FitStatus : uint8_t { // fit status of crossing hypothesis
160 None, // no status set (should not be possible!)
161
162 /* Good Conditions */
163 Converged, // fit converged
164 MaxIter, // max iterations reached before fit convergence
165
166 /* Error Conditions */
167 NoCrossing, // no reasaonable crossing was found
168 RejRadius, // radius of crossing was not acceptable
169 RejTrackX, // one candidate track x was below the mimimum required radius
170 RejTrackRoughZ, // rejected by rough cut on tracks Z difference
171 RejChi2Max, // rejected by maximum chi2 cut
172 FailProp, // propagation of at least prong to PCA failed
173 FailInvCov, // inversion of cov.-matrix failed
174 FailInvWeight, // inversion of Ti weight matrix failed
175 FailInv2ndDeriv, // inversion of 2nd derivatives failed
176 FailCorrTracks, // correction of tracks to updated x failed
177 FailCloserAlt, // alternative PCA is closer
178 //
180 };
181
182 static constexpr int getNProngs() { return N; }
183
184 DCAFitterN() = default;
185 DCAFitterN(float bz, bool useAbsDCA, bool prop2DCA) : mBz(bz), mUseAbsDCA(useAbsDCA), mPropagateToPCA(prop2DCA)
186 {
187 static_assert(N >= NMin && N <= NMax, "N prongs outside of allowed range");
188 }
189
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
194 //=========================================================================
196 GPUd() const Vec3D& getPCACandidate(int cand = 0) const { return mPCA[mOrder[cand]]; }
197 GPUd() const auto getPCACandidatePos(int cand = 0) const
198 {
199 const auto& vd = mPCA[mOrder[cand]];
200 return std::array<float, 3>{static_cast<float>(vd[0]), static_cast<float>(vd[1]), static_cast<float>(vd[2])};
201 }
202
204 int getCandidatePosition(int cand = 0) const { return mOrder[cand]; }
205
207 float getChi2AtPCACandidate(int cand = 0) const { return mChi2[mOrder[cand]]; }
208
211 GPUd() bool propagateTracksToVertex(int cand = 0);
212
214 GPUd() bool isPropagateTracksToVertexDone(int cand = 0) const { return mTrPropDone[mOrder[cand]]; }
215
217 bool isPropagationFailure(int cand = 0) const { return mPropFailed[mOrder[cand]]; }
218
221 Track& getTrack(int i, int cand = 0)
222 {
223 if (!mTrPropDone[mOrder[cand]]) {
224#ifndef GPUCA_GPUCODE_DEVICE
225 throw std::runtime_error("propagateTracksToVertex was not called yet");
226#endif
227 }
228 return mCandTr[mOrder[cand]][i];
229 }
230
231 const Track& getTrack(int i, int cand = 0) const
232 {
233 if (!mTrPropDone[mOrder[cand]]) {
234#ifndef GPUCA_GPUCODE_DEVICE
235 throw std::runtime_error("propagateTracksToVertex was not called yet");
236#endif
237 }
238 return mCandTr[mOrder[cand]][i];
239 }
240
242 GPUd() o2::track::TrackParCov createParentTrackParCov(int cand = 0, bool sectorAlpha = true) const;
243
245 GPUd() o2::track::TrackPar createParentTrackPar(int cand = 0, bool sectorAlpha = true) const;
246
248 GPUd() o2::track::TrackPar getTrackParamAtPCA(int i, int cand = 0);
249
251 GPUd() bool recalculatePCAWithErrors(int cand = 0);
252
253 GPUd() double calcCollinearInflation(int cand) const;
254 GPUd() MatSym3D calcPCACovMatrix(int cand = 0) const;
255
256 std::array<float, 6> calcPCACovMatrixFlat(int cand = 0) const
257 {
258 auto m = calcPCACovMatrix(cand);
259 return {static_cast<float>(m(0, 0)), static_cast<float>(m(1, 0)), static_cast<float>(m(1, 1)), static_cast<float>(m(2, 0)), static_cast<float>(m(2, 1)), static_cast<float>(m(2, 2))};
260 }
261
262 const Track* getOrigTrackPtr(int i) const { return mOrigTrPtr[i]; }
263
264 GPUdi() FitStatus getFitStatus(int cand = 0) const noexcept { return mFitStatus[mOrder[cand]]; }
265
267 GPUdi() int getNIterations(int cand = 0) const { return mNIters[mOrder[cand]]; }
268 GPUdi() void setPropagateToPCA(bool v = true) { mPropagateToPCA = v; }
269 GPUdi() void setMaxIter(int n = 20) { mMaxIter = n > 2 ? n : 2; }
270 GPUdi() void setMaxR(float r = 200.) { mMaxR2 = r * r; }
271 GPUdi() void setMaxDZIni(float d = 4.) { mMaxDZIni = d; }
272 GPUdi() void setMaxDXYIni(float d = 4.) { mMaxDXYIni = d > 0 ? d : 1e9; }
273 GPUdi() void setMaxChi2(float chi2 = 999.) { mMaxChi2 = chi2; }
274 GPUdi() void setBz(float bz) { mBz = o2::gpu::GPUCommonMath::Abs(bz) > o2::constants::math::Almost0 ? bz : 0.f; }
275 GPUdi() void setMinParamChange(float x = 1e-3) { mMinParamChange = x > 1e-4 ? x : 1.e-4; }
276 GPUdi() void setMinRelChi2Change(float r = 0.9) { mMinRelChi2Change = r > 0.1 ? r : 999.; }
277 GPUdi() void setUseAbsDCA(bool v) { mUseAbsDCA = v; }
278 GPUdi() void setWeightedFinalPCA(bool v) { mWeightedFinalPCA = v; }
279 GPUdi() void setMaxDistance2ToMerge(float v) { mMaxDist2ToMergeSeeds = v; }
280 GPUdi() void setMatCorrType(o2::base::Propagator::MatCorrType m = o2::base::Propagator::MatCorrType::USEMatCorrLUT) { mMatCorr = m; }
281 GPUdi() void setUsePropagator(bool v) { mUsePropagator = v; }
282 GPUdi() void setRefitWithMatCorr(bool v) { mRefitWithMatCorr = v; }
283 GPUdi() void setMaxSnp(float s) { mMaxSnp = s; }
284 GPUdi() void setMaxStep(float s) { mMaxStep = s; }
285 GPUdi() void setMinXSeed(float x) { mMinXSeed = x; }
286 GPUdi() void setCollinear(bool isCollinear) { mIsCollinear = isCollinear; }
287
288 GPUdi() int getNCandidates() const { return mCurHyp; }
289 GPUdi() int getMaxIter() const { return mMaxIter; }
290 GPUdi() float getMaxR() const { return o2::gpu::GPUCommonMath::Sqrt(mMaxR2); }
291 GPUdi() float getMaxDZIni() const { return mMaxDZIni; }
292 GPUdi() float getMaxDXYIni() const { return mMaxDXYIni; }
293 GPUdi() float getMaxChi2() const { return mMaxChi2; }
294 GPUdi() float getMinParamChange() const { return mMinParamChange; }
295 GPUdi() float getBz() const { return mBz; }
296 GPUdi() float getMaxDistance2ToMerge() const { return mMaxDist2ToMergeSeeds; }
297 GPUdi() bool getUseAbsDCA() const { return mUseAbsDCA; }
298 GPUdi() bool getWeightedFinalPCA() const { return mWeightedFinalPCA; }
299 GPUdi() bool getPropagateToPCA() const { return mPropagateToPCA; }
300 GPUdi() o2::base::Propagator::MatCorrType getMatCorrType() const { return mMatCorr; }
301 GPUdi() bool getUsePropagator() const { return mUsePropagator; }
302 GPUdi() bool getRefitWithMatCorr() const { return mRefitWithMatCorr; }
303 GPUdi() float getMaxSnp() const { return mMaxSnp; }
304 GPUdi() float getMasStep() const { return mMaxStep; }
305 GPUdi() float getMinXSeed() const { return mMinXSeed; }
306
307 template <class... Tr>
308 GPUd() int process(const Tr&... args);
309 GPUd() void print() const;
310
311 GPUdi() int getFitterID() const { return mFitterID; }
312 GPUdi() void setFitterID(int i) { mFitterID = i; }
313 GPUdi() size_t getCallID() const { return mCallID; }
314
315 protected:
316 GPUd() bool calcPCACoefs();
317 GPUd() bool calcInverseWeight();
318 GPUd() void calcResidDerivatives();
319 GPUd() void calcResidDerivativesNoErr();
320 GPUd() void calcRMatrices();
321 GPUd() void calcChi2Derivatives();
322 GPUd() void calcChi2DerivativesNoErr();
323 GPUd() void calcPCA();
324 GPUd() void calcPCANoErr();
325 GPUd() void calcTrackResiduals();
326 GPUd() void calcTrackDerivatives();
327 GPUd() double calcChi2() const;
328 GPUd() double calcChi2NoErr() const;
329 GPUd() bool correctTracks(const VecND& corrX);
330 GPUd() bool minimizeChi2();
331 GPUd() bool minimizeChi2NoErr();
332 GPUd() bool roughDZCut() const;
333 GPUd() bool closerToAlternative() const;
334 GPUd() bool propagateToX(o2::track::TrackParCov& t, float x);
335 GPUd() bool propagateParamToX(o2::track::TrackPar& t, float x);
336
337 GPUd() static double getAbsMax(const VecND& v);
339 GPUdi() const Vec3D& getTrackPos(int i, int cand = 0) const { return mTrPos[mOrder[cand]][i]; }
340
342 GPUd() float getTrackX(int i, int cand = 0) const { return getTrackPos(i, cand)[0]; }
343
347 GPUd() static void addRotatedTrackInfo(double* arrmat, const TrackAuxPar& taux, const TrackCovI& tcov)
348 {
349 enum { XX,
350 XY,
351 YY,
352 XZ,
353 YZ,
354 ZZ };
355 arrmat[XX] += taux.cc * tcov.sxx - 2. * taux.cs * tcov.sxy + taux.ss * tcov.syy;
356 arrmat[XY] += taux.cs * (tcov.sxx - tcov.syy) + (taux.cc - taux.ss) * tcov.sxy;
357 arrmat[XZ] += taux.c * tcov.sxz - taux.s * tcov.syz;
358 arrmat[YY] += taux.ss * tcov.sxx + 2. * taux.cs * tcov.sxy + taux.cc * tcov.syy;
359 arrmat[YZ] += taux.s * tcov.sxz + taux.c * tcov.syz;
360 arrmat[ZZ] += tcov.szz;
361 }
362
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
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
386 GPUd() void assign(int) {}
387 template <class T, class... Tr>
388 GPUd() void assign(int i, const T& t, const Tr&... args)
389 {
390#ifndef GPUCA_GPUCODE_DEVICE
391 static_assert(std::is_convertible<T, Track>(), "Wrong track type");
392#endif
394 assign(i + 1, args...);
395 }
396
398 {
399 mCurHyp = 0;
400 mAllowAltPreference = true;
401 mOrder.fill(0);
402 mPropFailed.fill(false);
403 mTrPropDone.fill(false);
404 mNIters.fill(0);
405 mChi2.fill(-1);
406 mFitStatus.fill(FitStatus::None);
407 }
408
409 GPUdi() static void setTrackPos(Vec3D& pnt, const Track& tr)
410 {
411 pnt[0] = tr.getX();
412 pnt[1] = tr.getY();
413 pnt[2] = tr.getZ();
414 }
415
416 GPUdi() void clearLogThrottlers()
417 {
418 mLoggerBadCov.clear();
419 mLoggerBadInv.clear();
420 mLoggerBadProp.clear();
421 }
422
423 void setBadCovPolicy(BadCovPolicy v) { mBadCovPolicy = v; }
424 BadCovPolicy getBadCovPolicy() const { return mBadCovPolicy; }
425
426 private:
427 // vectors of 1st derivatives of track local residuals over X parameters
428 std::array<std::array<Vec3D, N>, N> mDResidDx;
429 // vectors of 1nd derivatives of track local residuals over X parameters
430 // (cross-derivatives DR/(dx_j*dx_k) = 0 for j!=k, therefore the hessian is diagonal)
431 std::array<std::array<Vec3D, N>, N> mD2ResidDx2;
432 VecND mDChi2Dx; // 1st derivatives of chi2 over tracks X params
433 MatSymND mD2Chi2Dx2; // 2nd derivatives of chi2 over tracks X params (symmetric matrix)
434 MatSymND mCosDif; // matrix with cos(alp_j-alp_i) for j<i
435 MatSymND mSinDif; // matrix with sin(alp_j-alp_i) for j<i
436 std::array<const Track*, N> mOrigTrPtr;
437 std::array<TrackAuxPar, N> mTrAux; // Aux track info for each track at each cand. vertex
438 CrossInfo mCrossings; // info on track crossing
439
440 std::array<ArrTrackCovI, MAXHYP> mTrcEInv; // errors for each track at each cand. vertex
441 std::array<ArrTrack, MAXHYP> mCandTr; // tracks at each cond. vertex (Note: Errors are at seed XY point)
442 std::array<ArrTrCoef, MAXHYP> mTrCFVT; // TrackCoefVtx for each track at each cand. vertex
443 std::array<ArrTrDer, MAXHYP> mTrDer; // Track derivativse
444 std::array<ArrTrPos, MAXHYP> mTrPos; // Track positions
445 std::array<ArrTrPos, MAXHYP> mTrRes; // Track residuals
446 std::array<Vec3D, MAXHYP> mPCA; // PCA for each vertex candidate
447 std::array<float, MAXHYP> mChi2 = {0}; // Chi2 at PCA candidate
448 std::array<int, MAXHYP> mNIters; // number of iterations for each seed
449 std::array<bool, MAXHYP> mTrPropDone{}; // Flag that the tracks are fully propagated to PCA
450 std::array<bool, MAXHYP> mPropFailed{}; // Flag that some propagation failed for this PCA candidate
451 mutable LogLogThrottler mLoggerBadCov{};
452 mutable LogLogThrottler mLoggerBadInv{};
453 mutable LogLogThrottler mLoggerBadProp{};
454 mutable LogLogThrottler mLoggerBadPCACov{};
455 MatSym3D mWeightInv; // inverse weight of single track, [sum{M^T E M}]^-1 in EQ.T
456 std::array<int, MAXHYP> mOrder{0};
457 int mCurHyp = 0;
458 int mCrossIDCur = 0;
459 int mCrossIDAlt = -1;
460 BadCovPolicy mBadCovPolicy{BadCovPolicy::Discard}; // what to do in case of non-pos-def. cov. matrix, see BadCovPolicy enum
461 std::array<FitStatus, MAXHYP> mFitStatus{}; // fit status of each hypothesis fit
462 bool mAllowAltPreference = true; // if the fit converges to alternative PCA seed, abandon the current one
463 bool mUseAbsDCA = false; // use abs. distance minimization rather than chi2
464 bool mWeightedFinalPCA = false; // recalculate PCA as a cov-matrix weighted mean, even if absDCA method was used
465 bool mPropagateToPCA = true; // create tracks version propagated to PCA
466 bool mUsePropagator = false; // use propagator with 3D B-field, set automatically if material correction is requested
467 bool mRefitWithMatCorr = false; // when doing propagateTracksToVertex, propagate tracks to V0 with material corrections and rerun minimization again
468 bool mIsCollinear = false; // use collinear fits when there 2 crossing points
469 o2::base::Propagator::MatCorrType mMatCorr = o2::base::Propagator::MatCorrType::USEMatCorrNONE; // material corrections type
470 int mMaxIter = 20; // max number of iterations
471 float mBz = 0; // bz field, to be set by user
472 float mMaxR2 = 200. * 200.; // reject PCA's above this radius
473 float mMinXSeed = -50.; // reject seed if it corresponds to X-param < mMinXSeed for one of candidates (e.g. X becomes strongly negative)
474 float mMaxDZIni = 4.; // reject (if>0) PCA candidate if tracks DZ exceeds threshold
475 float mMaxDXYIni = 4.; // reject (if>0) PCA candidate if tracks dXY exceeds threshold
476 float mMinParamChange = 1e-3; // stop iterations if largest change of any X is smaller than this
477 float mMinRelChi2Change = 0.9; // stop iterations is chi2/chi2old > this
478 float mMaxChi2 = 100; // abs cut on chi2 or abs distance
479 float mMaxDist2ToMergeSeeds = 1.; // merge 2 seeds to their average if their distance^2 is below the threshold
480 float mMaxSnp = 0.95; // Max snp for propagation with Propagator
481 float mMaxStep = 2.0; // Max step for propagation with Propagator
482 int mFitterID = 0; // locat fitter ID (mostly for debugging)
483 size_t mCallID = 0;
484
494 bool mOldMode = true;
495
496 ClassDefNV(DCAFitterN, 4);
497};
498
500template <int N, typename... Args>
501template <class... Tr>
502GPUd() int DCAFitterN<N, Args...>::process(const Tr&... args)
503{
504 // This is a main entry point: fit PCA of N tracks
505 mCallID++;
506 static_assert(sizeof...(args) == N, "incorrect number of input tracks");
507 assign(0, args...);
508 clear();
509 for (int i = 0; i < N; i++) {
510 mTrAux[i].set(*mOrigTrPtr[i], mBz);
511 }
512 if (!mCrossings.set(mTrAux[0], *mOrigTrPtr[0], mTrAux[1], *mOrigTrPtr[1], mMaxDXYIni, mIsCollinear)) { // even for N>2 it should be enough to test just 1 loop
513 mFitStatus[mCurHyp] = FitStatus::NoCrossing;
514 return 0;
515 }
516 if (mUseAbsDCA) {
517 calcRMatrices(); // needed for fast residuals derivatives calculation in case of abs. distance minimization
518 }
519 if (mCrossings.nDCA == MAXHYP) { // if there are 2 candidates and they are too close, chose their mean as a starting point
520 auto dst2 = (mCrossings.xDCA[0] - mCrossings.xDCA[1]) * (mCrossings.xDCA[0] - mCrossings.xDCA[1]) +
521 (mCrossings.yDCA[0] - mCrossings.yDCA[1]) * (mCrossings.yDCA[0] - mCrossings.yDCA[1]);
522 if (dst2 < mMaxDist2ToMergeSeeds) {
523 mCrossings.nDCA = 1;
524 mCrossings.xDCA[0] = 0.5 * (mCrossings.xDCA[0] + mCrossings.xDCA[1]);
525 mCrossings.yDCA[0] = 0.5 * (mCrossings.yDCA[0] + mCrossings.yDCA[1]);
526 }
527 }
528 // check all crossings
529 for (int ic = 0; ic < mCrossings.nDCA; ic++) {
530 // check if radius is acceptable
531 if (mCrossings.xDCA[ic] * mCrossings.xDCA[ic] + mCrossings.yDCA[ic] * mCrossings.yDCA[ic] > mMaxR2) {
532 mFitStatus[mCurHyp] = FitStatus::RejRadius;
533 continue;
534 }
535 mCrossIDCur = ic;
536 mCrossIDAlt = (mCrossings.nDCA == 2 && mAllowAltPreference) ? 1 - ic : -1; // works for max 2 crossings
537 mPCA[mCurHyp][0] = mCrossings.xDCA[ic];
538 mPCA[mCurHyp][1] = mCrossings.yDCA[ic];
539
540 if (mUseAbsDCA ? minimizeChi2NoErr() : minimizeChi2()) {
541 mOrder[mCurHyp] = mCurHyp;
542 if (mPropagateToPCA && !propagateTracksToVertex(mCurHyp)) {
543 continue; // discard candidate if failed to propagate to it
544 }
545 mCurHyp++;
546 }
547 }
548
549 for (int i = mCurHyp; i--;) { // order in quality
550 for (int j = i; j--;) {
551 if (mChi2[mOrder[i]] < mChi2[mOrder[j]]) {
552 o2::gpu::GPUCommonMath::Swap(mOrder[i], mOrder[j]);
553 }
554 }
555 }
556 if (mUseAbsDCA && mWeightedFinalPCA) {
557 for (int i = mCurHyp; i--;) {
558 recalculatePCAWithErrors(i);
559 }
560 }
561 return mCurHyp;
562}
563
564//__________________________________________________________________________
565template <int N, typename... Args>
566GPUd() bool DCAFitterN<N, Args...>::calcPCACoefs()
567{
568 //< calculate Ti matrices for global vertex decomposition to V = sum_{0<i<N} Ti pi, see EQ.T in the ref
569 if (!calcInverseWeight()) {
570 mFitStatus[mCurHyp] = FitStatus::FailInvWeight;
571 return false;
572 }
573 for (int i = N; i--;) { // build Mi*Ei matrix
574 const auto& taux = mTrAux[i];
575 const auto& tcov = mTrcEInv[mCurHyp][i];
576 MatStd3D miei;
577 miei[0][0] = taux.c * tcov.sxx - taux.s * tcov.sxy;
578 miei[0][1] = taux.c * tcov.sxy - taux.s * tcov.syy;
579 miei[0][2] = taux.c * tcov.sxz - taux.s * tcov.syz;
580 miei[1][0] = taux.s * tcov.sxx + taux.c * tcov.sxy;
581 miei[1][1] = taux.s * tcov.sxy + taux.c * tcov.syy;
582 miei[1][2] = taux.s * tcov.sxz + taux.c * tcov.syz;
583 miei[2][0] = tcov.sxz;
584 miei[2][1] = tcov.syz;
585 miei[2][2] = tcov.szz;
586 mTrCFVT[mCurHyp][i] = mWeightInv * miei;
587 }
588 return true;
589}
590
591//__________________________________________________________________________
592template <int N, typename... Args>
593GPUd() bool DCAFitterN<N, Args...>::calcInverseWeight()
594{
595 //< calculate [sum_{0<j<N} M_j*E_j*M_j^T]^-1 used for Ti matrices, see EQ.T
596 auto* arrmat = mWeightInv.Array();
597 memset(arrmat, 0, sizeof(mWeightInv));
598 for (int i = N; i--;) { // the mTrcEInv used here are regularized, see TrackCovI::XRegErrFactor
599 addRotatedTrackInfo(arrmat, mTrAux[i], mTrcEInv[mCurHyp][i]);
600 }
601 // invert 3x3 symmetrix matrix
602 return mWeightInv.Invert();
603}
604
605//__________________________________________________________________________
606template <int N, typename... Args>
607GPUd() void DCAFitterN<N, Args...>::calcResidDerivatives()
608{
609 //< calculate matrix of derivatives for weighted chi2: residual i vs parameter X of track j
610 MatStd3D matMT;
611 for (int i = N; i--;) { // residual being differentiated
612 const auto& taux = mTrAux[i];
613 for (int j = N; j--;) { // track over which we differentiate
614 const auto& matT = mTrCFVT[mCurHyp][j]; // coefficient matrix for track J
615 const auto& trDx = mTrDer[mCurHyp][j]; // track point derivs over track X param
616 auto& dr1 = mDResidDx[i][j];
617 auto& dr2 = mD2ResidDx2[i][j];
618 // calculate M_i^tr * T_j
619 matMT[0][0] = taux.c * matT[0][0] + taux.s * matT[1][0];
620 matMT[0][1] = taux.c * matT[0][1] + taux.s * matT[1][1];
621 matMT[0][2] = taux.c * matT[0][2] + taux.s * matT[1][2];
622 matMT[1][0] = -taux.s * matT[0][0] + taux.c * matT[1][0];
623 matMT[1][1] = -taux.s * matT[0][1] + taux.c * matT[1][1];
624 matMT[1][2] = -taux.s * matT[0][2] + taux.c * matT[1][2];
625 matMT[2][0] = matT[2][0];
626 matMT[2][1] = matT[2][1];
627 matMT[2][2] = matT[2][2];
628
629 // calculate DResid_i/Dx_j = (delta_ij - M_i^tr * T_j) * DTrack_k/Dx_k
630 dr1[0] = -(matMT[0][0] + matMT[0][1] * trDx.dydx + matMT[0][2] * trDx.dzdx);
631 dr1[1] = -(matMT[1][0] + matMT[1][1] * trDx.dydx + matMT[1][2] * trDx.dzdx);
632 dr1[2] = -(matMT[2][0] + matMT[2][1] * trDx.dydx + matMT[2][2] * trDx.dzdx);
633
634 // calculate D2Resid_I/(Dx_J Dx_K) = (delta_ijk - M_i^tr * T_j * delta_jk) * D2Track_k/dx_k^2
635 dr2[0] = -(matMT[0][1] * trDx.d2ydx2 + matMT[0][2] * trDx.d2zdx2);
636 dr2[1] = -(matMT[1][1] * trDx.d2ydx2 + matMT[1][2] * trDx.d2zdx2);
637 dr2[2] = -(matMT[2][1] * trDx.d2ydx2 + matMT[2][2] * trDx.d2zdx2);
638
639 if (i == j) {
640 dr1[0] += 1.;
641 dr1[1] += trDx.dydx;
642 dr1[2] += trDx.dzdx;
643
644 dr2[1] += trDx.d2ydx2;
645 dr2[2] += trDx.d2zdx2;
646 }
647 } // track over which we differentiate
648 } // residual being differentiated
649}
650
651//__________________________________________________________________________
652template <int N, typename... Args>
653GPUd() void DCAFitterN<N, Args...>::calcResidDerivativesNoErr()
654{
655 //< calculate matrix of derivatives for absolute distance chi2: residual i vs parameter X of track j
656 constexpr double NInv1 = 1. - NInv; // profit from Rii = I/Ninv
657 for (int i = N; i--;) { // residual being differentiated
658 const auto& trDxi = mTrDer[mCurHyp][i]; // track point derivs over track X param
659 auto& dr1ii = mDResidDx[i][i];
660 auto& dr2ii = mD2ResidDx2[i][i];
661 dr1ii[0] = NInv1;
662 dr1ii[1] = NInv1 * trDxi.dydx;
663 dr1ii[2] = NInv1 * trDxi.dzdx;
664
665 dr2ii[0] = 0;
666 dr2ii[1] = NInv1 * trDxi.d2ydx2;
667 dr2ii[2] = NInv1 * trDxi.d2zdx2;
668
669 for (int j = i; j--;) { // track over which we differentiate
670 auto& dr1ij = mDResidDx[i][j];
671 auto& dr1ji = mDResidDx[j][i];
672 const auto& trDxj = mTrDer[mCurHyp][j]; // track point derivs over track X param
673 auto cij = mCosDif[i][j], sij = mSinDif[i][j]; // M_i^T*M_j / N matrices non-trivial elements = {ci*cj+si*sj , si*cj-ci*sj }, see 5 in ref.
674
675 // calculate DResid_i/Dx_j = (delta_ij - R_ij) * DTrack_j/Dx_j for j<i
676 dr1ij[0] = -(cij + sij * trDxj.dydx);
677 dr1ij[1] = -(-sij + cij * trDxj.dydx);
678 dr1ij[2] = -trDxj.dzdx * NInv;
679
680 // calculate DResid_j/Dx_i = (delta_ij - R_ji) * DTrack_i/Dx_i for j<i
681 dr1ji[0] = -(cij - sij * trDxi.dydx);
682 dr1ji[1] = -(sij + cij * trDxi.dydx);
683 dr1ji[2] = -trDxi.dzdx * NInv;
684
685 auto& dr2ij = mD2ResidDx2[i][j];
686 auto& dr2ji = mD2ResidDx2[j][i];
687 // calculate D2Resid_I/(Dx_J Dx_K) = (delta_ij - Rij) * D2Track_j/dx_j^2 * delta_jk for j<i
688 dr2ij[0] = -sij * trDxj.d2ydx2;
689 dr2ij[1] = -cij * trDxj.d2ydx2;
690 dr2ij[2] = -trDxj.d2zdx2 * NInv;
691
692 // calculate D2Resid_j/(Dx_i Dx_k) = (delta_ij - Rji) * D2Track_i/dx_i^2 * delta_ik for j<i
693 dr2ji[0] = sij * trDxi.d2ydx2;
694 dr2ji[1] = -cij * trDxi.d2ydx2;
695 dr2ji[2] = -trDxi.d2zdx2 * NInv;
696
697 } // track over which we differentiate
698 } // residual being differentiated
699}
700
701//__________________________________________________________________________
702template <int N, typename... Args>
703GPUd() void DCAFitterN<N, Args...>::calcRMatrices()
704{
705 //< calculate Rij = 1/N M_i^T * M_j matrices (rotation from j-th track to i-th track frame)
706 for (int i = N; i--;) {
707 const auto& mi = mTrAux[i];
708 for (int j = i; j--;) {
709 const auto& mj = mTrAux[j];
710 mCosDif[i][j] = (mi.c * mj.c + mi.s * mj.s) * NInv; // cos(alp_i-alp_j) / N
711 mSinDif[i][j] = (mi.s * mj.c - mi.c * mj.s) * NInv; // sin(alp_i-alp_j) / N
712 }
713 }
714}
715
716//__________________________________________________________________________
717template <int N, typename... Args>
718GPUd() void DCAFitterN<N, Args...>::calcChi2Derivatives()
719{
720 //< calculate 1st and 2nd derivatives of wighted DCA (chi2) over track parameters X, see EQ.Chi2 in the ref
721 std::array<std::array<Vec3D, N>, N> covIDrDx; // tempory vectors of covI_j * dres_j/dx_i
722
723 // chi2 1st derivative
724 for (int i = N; i--;) {
725 auto& dchi1 = mDChi2Dx[i]; // DChi2/Dx_i = sum_j { res_j * covI_j * Dres_j/Dx_i }
726 dchi1 = 0;
727 for (int j = N; j--;) {
728 const auto& res = mTrRes[mCurHyp][j]; // vector of residuals of track j
729 const auto& covI = mTrcEInv[mCurHyp][j]; // inverse cov matrix of track j
730 const auto& dr1 = mDResidDx[j][i]; // vector of j-th residuals 1st derivative over X param of track i
731 auto& cidr = covIDrDx[i][j]; // vector covI_j * dres_j/dx_i, save for 2nd derivative calculation
732 cidr[0] = covI.sxx * dr1[0] + covI.sxy * dr1[1] + covI.sxz * dr1[2];
733 cidr[1] = covI.sxy * dr1[0] + covI.syy * dr1[1] + covI.syz * dr1[2];
734 cidr[2] = covI.sxz * dr1[0] + covI.syz * dr1[1] + covI.szz * dr1[2];
735 // calculate res_i * covI_j * dres_j/dx_i
736 dchi1 += o2::math_utils::Dot(res, cidr);
737 }
738 }
739 // chi2 2nd derivative
740 for (int i = N; i--;) {
741 for (int j = i + 1; j--;) { // symmetric matrix
742 auto& dchi2 = mD2Chi2Dx2[i][j]; // D2Chi2/Dx_i/Dx_j = sum_k { Dres_k/Dx_j * covI_k * Dres_k/Dx_i + res_k * covI_k * D2res_k/Dx_i/Dx_j }
743 dchi2 = 0;
744 for (int k = N; k--;) {
745 const auto& dr1j = mDResidDx[k][j]; // vector of k-th residuals 1st derivative over X param of track j
746 const auto& cidrkj = covIDrDx[i][k]; // vector covI_k * dres_k/dx_i
747 dchi2 += o2::math_utils::Dot(dr1j, cidrkj);
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
755 dchi2 += res[0] * (covI.sxx * dr2ij[0] + covI.sxy * dr2ij[1] + covI.sxz * dr2ij[2]) +
756 res[1] * (covI.sxy * dr2ij[0] + covI.syy * dr2ij[1] + covI.syz * dr2ij[2]) +
757 res[2] * (covI.sxz * dr2ij[0] + covI.syz * dr2ij[1] + covI.szz * dr2ij[2]);
758 }
759 }
760 }
761 }
762}
763
764//__________________________________________________________________________
765template <int N, typename... Args>
766GPUd() void DCAFitterN<N, Args...>::calcChi2DerivativesNoErr()
767{
768 //< calculate 1st and 2nd derivatives of abs DCA (chi2) over track parameters X, see (6) in the ref
769 for (int i = N; i--;) {
770 auto& dchi1 = mDChi2Dx[i]; // DChi2/Dx_i = sum_j { res_j * Dres_j/Dx_i }
771 dchi1 = 0; // chi2 1st derivative
772 for (int k = N; k--;) {
773 const auto& res = mTrRes[mCurHyp][k]; // vector of residuals of track k
774 const auto& dr1 = mDResidDx[k][i]; // vector of k-th residuals 1st derivative over X param of track i
775 dchi1 += o2::math_utils::Dot(res, dr1);
776 }
777 }
778 for (int i = N; i--;) {
779 for (int j = i + 1; j--;) {
780 auto& dchi2 = mD2Chi2Dx2[i][j];
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.;
785 for (int k = N; k--;) {
786 // Gauss-Newton term, present for diagonal and mixed elements.
787 dchi2 += o2::math_utils::Dot(mDResidDx[k][i], mDResidDx[k][j]);
788 if (!mOldMode && i == j) {
789 dchi2 += o2::math_utils::Dot(mTrRes[mCurHyp][k], mD2ResidDx2[k][i]);
790 }
791 }
792 }
793 }
794}
795
796//___________________________________________________________________
797template <int N, typename... Args>
798GPUd() void DCAFitterN<N, Args...>::calcPCA()
799{
800 // calculate point of closest approach for N prongs
801 mPCA[mCurHyp] = mTrCFVT[mCurHyp][N - 1] * mTrPos[mCurHyp][N - 1];
802 for (int i = N - 1; i--;) {
803 mPCA[mCurHyp] += mTrCFVT[mCurHyp][i] * mTrPos[mCurHyp][i];
804 }
805}
806
807//___________________________________________________________________
808template <int N, typename... Args>
809GPUd() bool DCAFitterN<N, Args...>::recalculatePCAWithErrors(int cand)
810{
811 // recalculate PCA as a cov-matrix weighted mean, even if absDCA method was used
812 if (isPropagateTracksToVertexDone(cand) && !propagateTracksToVertex(cand)) {
813 return false;
814 }
815 int saveCurHyp = mCurHyp;
816 mCurHyp = mOrder[cand];
817 if (mUseAbsDCA) {
818 for (int i = N; i--;) {
819 if (!mTrcEInv[mCurHyp][i].set(mCandTr[mCurHyp][i], TrackCovI::XRegErrFactor, mOldMode)) { // prepare inverse cov.matrices at starting point
820 if (mLoggerBadCov.needToLog()) {
821#ifndef GPUCA_GPUCODE
822 printf("fitter %d: error (%ld muted): overrode invalid track covariance from %s\n",
823 mFitterID, mLoggerBadCov.evCount, mCandTr[mCurHyp][i].asString().c_str());
824#else
825 printf("fitter %d: error (%ld muted): overrode invalid track covariance cyy:%e czz:%e cyz:%e\n",
826 mFitterID, mLoggerBadCov.evCount, mCandTr[mCurHyp][i].getSigmaY2(), mCandTr[mCurHyp][i].getSigmaZ2(), mCandTr[mCurHyp][i].getSigmaZY());
827#endif
828 }
829 mFitStatus[mCurHyp] = FitStatus::FailInvCov;
830 if (mBadCovPolicy == Discard) {
831 return false;
832 } else if (mBadCovPolicy == OverrideAndFlag) {
833 mPropFailed[mCurHyp] = true;
834 } // otherwise, just use overridden errors w/o flagging
835 }
836 }
837 if (!calcPCACoefs()) {
838 mCurHyp = saveCurHyp;
839 return false;
840 }
841 }
842 auto oldPCA = mPCA[mOrder[cand]];
843 calcPCA();
844 mCurHyp = saveCurHyp;
845 return true;
846}
847
848//___________________________________________________________________
849template <int N, typename... Args>
850GPUd() void DCAFitterN<N, Args...>::calcPCANoErr()
851{
852 // calculate point of closest approach for N prongs w/o errors
853 auto& pca = mPCA[mCurHyp];
854 o2::math_utils::rotateZd(mTrPos[mCurHyp][N - 1][0], mTrPos[mCurHyp][N - 1][1], pca[0], pca[1], mTrAux[N - 1].s, mTrAux[N - 1].c);
855 // RRRR mTrAux[N-1].loc2glo(mTrPos[mCurHyp][N-1][0], mTrPos[mCurHyp][N-1][1], pca[0], pca[1] );
856 pca[2] = mTrPos[mCurHyp][N - 1][2];
857 for (int i = N - 1; i--;) {
858 double x, y;
859 o2::math_utils::rotateZd(mTrPos[mCurHyp][i][0], mTrPos[mCurHyp][i][1], x, y, mTrAux[i].s, mTrAux[i].c);
860 // RRRR mTrAux[i].loc2glo(mTrPos[mCurHyp][i][0], mTrPos[mCurHyp][i][1], x, y );
861 pca[0] += x;
862 pca[1] += y;
863 pca[2] += mTrPos[mCurHyp][i][2];
864 }
865 pca[0] *= NInv;
866 pca[1] *= NInv;
867 pca[2] *= NInv;
868}
869
870//___________________________________________________________________
871template <int N, typename... Args>
872GPUd() double DCAFitterN<N, Args...>::calcCollinearInflation(int cand) const
873{
874 // Note: only std::array and o2::gpu::GPUCommonMath are used here, no host-only <algorithm>/<cmath>,
875 // so that the method stays compilable for the device even though it is currently not called.
876 std::array<std::array<double, 3>, N> u{};
877 int nu = 0;
878
879 for (int i = 0; i < N; ++i) {
880 std::array<float, 3> p{};
881 if (!getTrack(i, cand).getPxPyPzGlo(p)) {
882 continue;
883 }
884 const float p2 = p[0] * p[0] + p[1] * p[1] + p[2] * p[2]; // float: GPUCommonMath::Sqrt is float-only and p is float anyway
885 if (p2 <= 0.f) {
886 continue;
887 }
888 const double pI = 1. / o2::gpu::GPUCommonMath::Sqrt(p2);
889 u[nu++] = {p[0] * pI, p[1] * pI, p[2] * pI};
890 }
891
892 if (nu < 2) {
893 return 1.;
894 }
895
896 double sin2Mean = 0.;
897 int npairs = 0;
898 for (int i = 0; i < nu; ++i) {
899 for (int j = i + 1; j < nu; ++j) {
900 double cij = u[i][0] * u[j][0] + u[i][1] * u[j][1] + u[i][2] * u[j][2];
901 cij = o2::gpu::GPUCommonMath::Clamp(cij, -1., 1.);
902 sin2Mean += o2::gpu::GPUCommonMath::Max(0., 1. - cij * cij);
903 ++npairs;
904 }
905 }
906 sin2Mean /= npairs;
907
908 constexpr double Sin2Ref = 1.e-5;
909 constexpr double MaxInflation = 1.e4;
910 if (sin2Mean <= 0.) {
911 return MaxInflation;
912 }
913 return sin2Mean < Sin2Ref ? o2::gpu::GPUCommonMath::Min(MaxInflation, Sin2Ref / sin2Mean) : 1.;
914}
915
916//___________________________________________________________________
917template <int N, typename... Args>
918GPUd() o2::math_utils::SMatrix<double, 3, 3, o2::math_utils::MatRepSym<double, 3>> DCAFitterN<N, Args...>::calcPCACovMatrix(int cand) const
919{
920 // Each track measures Y and Z at the vertex X. With the local slopes
921 // sy = dY/dX and sz = dZ/dX, its vertex measurement matrix is
922 // H = {{-sy, 1, 0}, {-sz, 0, 1}}. The longitudinal information must come
923 // from the track geometry, not from a dummy X variance: hence the per-track
924 // information matrices are built here WITHOUT the sxx regularization used by
925 // the minimization (TrackCovI::XRegNone), otherwise the vertex error along the
926 // weakly constrained direction would be defined by that dummy term.
927 // 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 }
949 MatSym3D info;
950 auto* arrmat = info.Array();
951 memset(arrmat, 0, sizeof(info));
952 const int ord = mOrder[cand];
953 for (int i = N; i--;) {
954 TrackCovI tcov;
955 tcov.set(mCandTr[ord][i], TrackCovI::XRegNone, mOldMode);
956 addRotatedTrackInfo(arrmat, mTrAux[i], tcov);
957 }
958 const double maxDiag = o2::gpu::GPUCommonMath::Max(o2::gpu::GPUCommonMath::Max(info(0, 0), info(1, 1)), info(2, 2));
959 const double det2 = info(0, 0) * info(1, 1) - info(1, 0) * info(1, 0);
960 const double det3 = info(0, 0) * (info(1, 1) * info(2, 2) - info(2, 1) * info(2, 1)) -
961 info(1, 0) * (info(1, 0) * info(2, 2) - info(2, 1) * info(2, 0)) +
962 info(2, 0) * (info(1, 0) * info(2, 1) - info(1, 1) * info(2, 0));
963 constexpr double MinRelDet = 1.e-12;
964 const bool isWellConditionedInfo = maxDiag > 0. && info(0, 0) > 0. && det2 > 0. && det3 > MinRelDet * maxDiag * maxDiag * maxDiag;
965 if (isWellConditionedInfo) {
966 auto cov = info;
967 if (cov.Invert() && cov(0, 0) > 0. && cov(1, 1) > 0. && cov(2, 2) > 0.) {
968 // TODO: for the collinear mode the covariance along the (badly defined) common direction
969 // may need an extra inflation, calcCollinearInflation() provides a candidate scaling.
970 // Kept disabled until validated on data.
971 // if (mIsCollinear) {
972 // cov *= calcCollinearInflation(cand);
973 // }
974 return cov;
975 }
976 }
977 if (mLoggerBadPCACov.needToLog()) {
978 printf("fitter %d: error (%ld muted): override ill-conditioned PCACovMatrix by dummy matrix\n", mFitterID, mLoggerBadPCACov.evCount);
979 }
980 // Fall back on a deliberately loose vertex covariance. Returning a tight
981 // identity covariance for a singular or ill-conditioned information matrix
982 // would shrink the uncertainty in the weakly constrained direction.
983 memset(arrmat, 0, sizeof(info));
984 info(0, 0) = 4.;
985 info(1, 1) = 4.;
986 info(2, 2) = 4.;
987 return info;
988}
989
990//___________________________________________________________________
991template <int N, typename... Args>
992GPUd() void DCAFitterN<N, Args...>::calcTrackResiduals()
993{
994 // calculate residuals
995 Vec3D vtxLoc;
996 for (int i = N; i--;) {
997 mTrRes[mCurHyp][i] = mTrPos[mCurHyp][i];
998 vtxLoc = mPCA[mCurHyp];
999 o2::math_utils::rotateZInvd(vtxLoc[0], vtxLoc[1], vtxLoc[0], vtxLoc[1], mTrAux[i].s, mTrAux[i].c); // glo->loc
1000 mTrRes[mCurHyp][i] -= vtxLoc;
1001 }
1002}
1003
1004//___________________________________________________________________
1005template <int N, typename... Args>
1006GPUdi() void DCAFitterN<N, Args...>::calcTrackDerivatives()
1007{
1008 // calculate track derivatives over X param
1009 for (int i = N; i--;) {
1010 mTrDer[mCurHyp][i].set(mCandTr[mCurHyp][i], mBz);
1011 }
1012}
1013
1014//___________________________________________________________________
1015template <int N, typename... Args>
1016GPUdi() double DCAFitterN<N, Args...>::calcChi2() const
1017{
1018 // calculate current chi2
1019 double chi2 = 0;
1020 for (int i = N; i--;) {
1021 const auto& res = mTrRes[mCurHyp][i];
1022 const auto& covI = mTrcEInv[mCurHyp][i];
1023 chi2 += res[0] * res[0] * covI.sxx + res[1] * res[1] * covI.syy + res[2] * res[2] * covI.szz +
1024 2. * (res[0] * res[1] * covI.sxy + res[0] * res[2] * covI.sxz + res[1] * res[2] * covI.syz);
1025 }
1026 return chi2;
1027}
1028
1029//___________________________________________________________________
1030template <int N, typename... Args>
1031GPUdi() double DCAFitterN<N, Args...>::calcChi2NoErr() const
1032{
1033 // calculate current chi2 of abs. distance minimization
1034 double chi2 = 0;
1035 for (int i = N; i--;) {
1036 const auto& res = mTrRes[mCurHyp][i];
1037 chi2 += res[0] * res[0] + res[1] * res[1] + res[2] * res[2];
1038 }
1039 return chi2;
1040}
1041
1042//___________________________________________________________________
1043template <int N, typename... Args>
1044GPUd() bool DCAFitterN<N, Args...>::correctTracks(const VecND& corrX)
1045{
1046 // Propagate the actual candidate tracks to the updated X. Updating only mTrPos by a Taylor
1047 // expansion (as was done before) leaves mCandTr at the previous X, hence calcTrackDerivatives()
1048 // (which reads mCandTr) stays insensitive to the update and the slopes/curvatures remain frozen
1049 // at the seed for all Newton iterations.
1050 // The analytic constant-Bz transport is used on purpose (rather than propagate{Param}ToX with the
1051 // Propagator and material corrections): the Newton corrections are small, but the track state must
1052 // stay synchronized with mTrPos for the next derivative update. The final propagation to the PCA
1053 // (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 }
1064 for (int i = N; i--;) {
1065 auto& trc = mCandTr[mCurHyp][i];
1066 const float x = static_cast<float>(mTrPos[mCurHyp][i][0] - corrX[i]);
1067 const bool propagated = mUseAbsDCA ? trc.propagateParamTo(x, mBz) : trc.propagateTo(x, mBz);
1068 if (!propagated) { // flag and log as done by propagate{Param}ToX
1069 mPropFailed[mCurHyp] = true;
1070 if (mLoggerBadProp.needToLog()) {
1071#ifndef GPUCA_GPUCODE
1072 printf("fitter %d: error (%ld muted): Newton step propagation to %.4f failed for %s\n", mFitterID, mLoggerBadProp.evCount, x, trc.asString().c_str());
1073#else
1074 printf("fitter %d: error (%ld muted): Newton step propagation to %.4f failed\n", mFitterID, mLoggerBadProp.evCount, x);
1075#endif
1076 }
1077 return false;
1078 }
1079 setTrackPos(mTrPos[mCurHyp][i], trc);
1080 }
1081 return true;
1082}
1083
1084//___________________________________________________________________
1085template <int N, typename... Args>
1086GPUd() bool DCAFitterN<N, Args...>::propagateTracksToVertex(int icand)
1087{
1088 // propagate tracks to current vertex
1089 int ord = mOrder[icand];
1090 if (mTrPropDone[ord]) {
1091 return true;
1092 }
1093
1094 // need to refit taking as a seed already found vertex
1095 if (mRefitWithMatCorr) {
1096 int curHypSav = mCurHyp, curCrosIDAlt = mCrossIDAlt; // save
1097 mCurHyp = ord;
1098 mCrossIDAlt = -1; // disable alternative check
1099 auto restore = [this, curHypSav, curCrosIDAlt]() { this->mCurHyp = curHypSav; this->mCrossIDAlt = curCrosIDAlt; };
1100 if (!(mUseAbsDCA ? minimizeChi2NoErr() : minimizeChi2())) { // do final propagation
1101 restore();
1102 return false;
1103 }
1104 restore();
1105 }
1106
1107 for (int i = N; i--;) {
1108 if (mUseAbsDCA || mUsePropagator || mMatCorr != o2::base::Propagator::MatCorrType::USEMatCorrNONE) {
1109 mCandTr[ord][i] = *mOrigTrPtr[i]; // fetch the track again, as mCandTr might have been propagated w/o errors or material corrections might be wrong
1110 }
1111 auto x = mTrAux[i].c * mPCA[ord][0] + mTrAux[i].s * mPCA[ord][1]; // X of PCA in the track frame
1112 if (!propagateToX(mCandTr[ord][i], x)) {
1113 return false;
1114 }
1115 }
1116
1117 mTrPropDone[ord] = true;
1118 return true;
1119}
1120
1121//___________________________________________________________________
1122template <int N, typename... Args>
1123GPUdi() o2::track::TrackPar DCAFitterN<N, Args...>::getTrackParamAtPCA(int i, int icand)
1124{
1125 // propagate tracks param only to current vertex (if not already done)
1126 int ord = mOrder[icand];
1127 o2::track::TrackPar trc(mCandTr[ord][i]);
1128 if (!mTrPropDone[ord]) {
1129 auto x = mTrAux[i].c * mPCA[ord][0] + mTrAux[i].s * mPCA[ord][1]; // X of PCA in the track frame
1130 if (!propagateParamToX(trc, x)) {
1131 trc.invalidate();
1132 }
1133 }
1134 return trc;
1135}
1136
1137//___________________________________________________________________
1138template <int N, typename... Args>
1139GPUdi() double DCAFitterN<N, Args...>::getAbsMax(const VecND& v)
1140{
1141 double mx = -1;
1142 for (int i = N; i--;) {
1143 auto vai = o2::gpu::GPUCommonMath::Abs(v[i]);
1144 if (mx < vai) {
1145 mx = vai;
1146 }
1147 }
1148 return mx;
1149}
1150
1151//___________________________________________________________________
1152template <int N, typename... Args>
1153GPUd() bool DCAFitterN<N, Args...>::minimizeChi2()
1154{
1155 // find best chi2 (weighted DCA) of N tracks in the vicinity of the seed PCA
1156 for (int i = N; i--;) {
1157 mCandTr[mCurHyp][i] = *mOrigTrPtr[i];
1158 auto x = mTrAux[i].c * mPCA[mCurHyp][0] + mTrAux[i].s * mPCA[mCurHyp][1]; // X of PCA in the track frame
1159 if (x < mMinXSeed) {
1160 mFitStatus[mCurHyp] = FitStatus::RejTrackX;
1161 return false;
1162 }
1163 if (!propagateToX(mCandTr[mCurHyp][i], x)) {
1164 return false;
1165 }
1166 setTrackPos(mTrPos[mCurHyp][i], mCandTr[mCurHyp][i]); // prepare positions
1167 if (!mTrcEInv[mCurHyp][i].set(mCandTr[mCurHyp][i], TrackCovI::XRegErrFactor, mOldMode)) { // prepare inverse cov.matrices at starting point
1168 if (mLoggerBadCov.needToLog()) {
1169#ifndef GPUCA_GPUCODE
1170 printf("fitter %d: error (%ld muted): overrode invalid track covariance from %s\n",
1171 mFitterID, mLoggerBadCov.evCount, mCandTr[mCurHyp][i].asString().c_str());
1172#else
1173 printf("fitter %d: error (%ld muted): overrode invalid track covariance cyy:%e czz:%e cyz:%e\n",
1174 mFitterID, mLoggerBadCov.evCount, mCandTr[mCurHyp][i].getSigmaY2(), mCandTr[mCurHyp][i].getSigmaZ2(), mCandTr[mCurHyp][i].getSigmaZY());
1175#endif
1176 }
1177 mFitStatus[mCurHyp] = FitStatus::FailInvCov;
1178 if (mBadCovPolicy == Discard) {
1179 return false;
1180 } else if (mBadCovPolicy == OverrideAndFlag) {
1181 mPropFailed[mCurHyp] = true;
1182 } // otherwise, just use overridden errors w/o flagging
1183 }
1184 }
1185
1186 if (mMaxDZIni > 0 && !roughDZCut()) { // apply rough cut on tracks Z difference
1187 mFitStatus[mCurHyp] = FitStatus::RejTrackRoughZ;
1188 return false;
1189 }
1190
1191 if (!calcPCACoefs()) { // prepare tracks contribution matrices to the global PCA
1192 return false;
1193 }
1194 calcPCA(); // current PCA
1195 calcTrackResiduals(); // current track residuals
1196 float chi2Upd, chi2 = calcChi2();
1197 do {
1198 calcTrackDerivatives(); // current track derivatives (1st and 2nd)
1199 calcResidDerivatives(); // current residals derivatives (1st and 2nd)
1200 calcChi2Derivatives(); // current chi2 derivatives (1st and 2nd)
1201
1202 // do Newton-Rapson iteration with corrections = - dchi2/d{x0..xN} * [ d^2chi2/d{x0..xN}^2 ]^-1
1203 if (!mD2Chi2Dx2.Invert()) {
1204 if (mLoggerBadInv.needToLog()) {
1205 printf("fitter %d: error (%ld muted): Inversion failed\n", mFitterID, mLoggerBadCov.evCount);
1206 }
1207 mFitStatus[mCurHyp] = FitStatus::FailInv2ndDeriv;
1208 return false;
1209 }
1210 VecND dx = mD2Chi2Dx2 * mDChi2Dx;
1211 if (!correctTracks(dx)) {
1212 mFitStatus[mCurHyp] = FitStatus::FailCorrTracks;
1213 return false;
1214 }
1215 calcPCA(); // updated PCA
1216 if (mCrossIDAlt >= 0 && closerToAlternative()) {
1217 mFitStatus[mCurHyp] = FitStatus::FailCloserAlt;
1218 mAllowAltPreference = false;
1219 return false;
1220 }
1221 calcTrackResiduals(); // updated residuals
1222 chi2Upd = calcChi2(); // updated chi2
1223 if (getAbsMax(dx) < mMinParamChange || chi2Upd > chi2 * mMinRelChi2Change) {
1224 chi2 = chi2Upd;
1225 mFitStatus[mCurHyp] = FitStatus::Converged;
1226 break; // converged
1227 }
1228 chi2 = chi2Upd;
1229 } while (++mNIters[mCurHyp] < mMaxIter);
1230 if (mNIters[mCurHyp] == mMaxIter) {
1231 mFitStatus[mCurHyp] = FitStatus::MaxIter;
1232 }
1233 //
1234 mChi2[mCurHyp] = chi2 * NInv;
1235 if (mChi2[mCurHyp] >= mMaxChi2) {
1236 mFitStatus[mCurHyp] = FitStatus::RejChi2Max;
1237 return false;
1238 }
1239 return true;
1240}
1241
1242//___________________________________________________________________
1243template <int N, typename... Args>
1244GPUd() bool DCAFitterN<N, Args...>::minimizeChi2NoErr()
1245{
1246 // find best chi2 (absolute DCA) of N tracks in the vicinity of the PCA seed
1247
1248 for (int i = N; i--;) {
1249 mCandTr[mCurHyp][i] = *mOrigTrPtr[i];
1250 auto x = mTrAux[i].c * mPCA[mCurHyp][0] + mTrAux[i].s * mPCA[mCurHyp][1]; // X of PCA in the track frame
1251 if (x < mMinXSeed) {
1252 mFitStatus[mCurHyp] = FitStatus::RejTrackX;
1253 return false;
1254 }
1255 if (!propagateParamToX(mCandTr[mCurHyp][i], x)) {
1256 return false;
1257 }
1258 setTrackPos(mTrPos[mCurHyp][i], mCandTr[mCurHyp][i]); // prepare positions
1259 }
1260 if (mMaxDZIni > 0 && !roughDZCut()) { // apply rough cut on tracks Z difference
1261 mFitStatus[mCurHyp] = FitStatus::RejTrackRoughZ;
1262 return false;
1263 }
1264
1265 calcPCANoErr(); // current PCA
1266 calcTrackResiduals(); // current track residuals
1267 float chi2Upd, chi2 = calcChi2NoErr();
1268 do {
1269 calcTrackDerivatives(); // current track derivatives (1st and 2nd)
1270 calcResidDerivativesNoErr(); // current residals derivatives (1st and 2nd)
1271 calcChi2DerivativesNoErr(); // current chi2 derivatives (1st and 2nd)
1272
1273 // do Newton-Rapson iteration with corrections = - dchi2/d{x0..xN} * [ d^2chi2/d{x0..xN}^2 ]^-1
1274 if (!mD2Chi2Dx2.Invert()) {
1275 if (mLoggerBadInv.needToLog()) {
1276 printf("fitter %d: error (%ld muted): Inversion failed\n", mFitterID, mLoggerBadCov.evCount);
1277 }
1278 mFitStatus[mCurHyp] = FitStatus::FailInv2ndDeriv;
1279 return false;
1280 }
1281 VecND dx = mD2Chi2Dx2 * mDChi2Dx;
1282 if (!correctTracks(dx)) {
1283 mFitStatus[mCurHyp] = FitStatus::FailCorrTracks;
1284 return false;
1285 }
1286 calcPCANoErr(); // updated PCA
1287 if (mCrossIDAlt >= 0 && closerToAlternative()) {
1288 mFitStatus[mCurHyp] = FitStatus::FailCloserAlt;
1289 mAllowAltPreference = false;
1290 return false;
1291 }
1292 calcTrackResiduals(); // updated residuals
1293 chi2Upd = calcChi2NoErr(); // updated chi2
1294 if (getAbsMax(dx) < mMinParamChange || chi2Upd > chi2 * mMinRelChi2Change) {
1295 chi2 = chi2Upd;
1296 mFitStatus[mCurHyp] = FitStatus::Converged;
1297 break; // converged
1298 }
1299 chi2 = chi2Upd;
1300 } while (++mNIters[mCurHyp] < mMaxIter);
1301 if (mNIters[mCurHyp] == mMaxIter) {
1302 mFitStatus[mCurHyp] = FitStatus::MaxIter;
1303 }
1304 //
1305 mChi2[mCurHyp] = chi2 * NInv;
1306 if (mChi2[mCurHyp] >= mMaxChi2) {
1307 mFitStatus[mCurHyp] = FitStatus::RejChi2Max;
1308 return false;
1309 }
1310 return true;
1311}
1312
1313//___________________________________________________________________
1314template <int N, typename... Args>
1315GPUd() bool DCAFitterN<N, Args...>::roughDZCut() const
1316{
1317 // apply rough cut on DZ between the tracks in the seed point
1318 bool accept = true;
1319 for (int i = N; accept && i--;) {
1320 for (int j = i; j--;) {
1321 if (o2::gpu::GPUCommonMath::Abs(mCandTr[mCurHyp][i].getZ() - mCandTr[mCurHyp][j].getZ()) > mMaxDZIni) {
1322 accept = false;
1323 break;
1324 }
1325 }
1326 }
1327 return accept;
1328}
1329
1330//___________________________________________________________________
1331template <int N, typename... Args>
1332GPUd() bool DCAFitterN<N, Args...>::closerToAlternative() const
1333{
1334 // check if the point current PCA point is closer to the seeding XY point being tested or to alternative see (if any)
1335 auto dxCur = mPCA[mCurHyp][0] - mCrossings.xDCA[mCrossIDCur], dyCur = mPCA[mCurHyp][1] - mCrossings.yDCA[mCrossIDCur];
1336 auto dxAlt = mPCA[mCurHyp][0] - mCrossings.xDCA[mCrossIDAlt], dyAlt = mPCA[mCurHyp][1] - mCrossings.yDCA[mCrossIDAlt];
1337 return dxCur * dxCur + dyCur * dyCur > dxAlt * dxAlt + dyAlt * dyAlt;
1338}
1339
1340//___________________________________________________________________
1341template <int N, typename... Args>
1342GPUd() void DCAFitterN<N, Args...>::print() const
1343{
1344#ifndef GPUCA_GPUCODE_DEVICE
1345 LOG(info) << N << "-prong vertex fitter in " << (mUseAbsDCA ? "abs." : "weighted") << " distance minimization mode, collinear tracks mode: " << (mIsCollinear ? "ON" : "OFF");
1346 LOG(info) << "Bz: " << mBz << " MaxIter: " << mMaxIter << " MaxChi2: " << mMaxChi2 << " MatCorrType: " << int(mMatCorr);
1347 LOG(info) << "Stopping condition: Max.param change < " << mMinParamChange << " Rel.Chi2 change > " << mMinRelChi2Change;
1348 LOG(info) << "Discard candidates for : Rvtx > " << getMaxR() << " DZ between tracks > " << mMaxDZIni;
1349 LOG(info) << "PropagateToPCA:" << mPropagateToPCA << " WeightedFinalPCA:" << mWeightedFinalPCA << " UsePropagator:" << mUsePropagator << " RefitWithMatCorr:" << mRefitWithMatCorr;
1350 std::string rep{};
1351 for (int i = 0; i < mCrossings.nDCA; i++) {
1352 rep += fmt::format("seed{}:{}/{} ", i, mTrPropDone[i], mPropFailed[i]);
1353 }
1354 LOG(info) << "Last call: NCand:" << mCurHyp << " from " << mCrossings.nDCA << " seeds, prop.done/failed: " << rep;
1355#else
1356 if (mUseAbsDCA) {
1357 printf("%d-prong vertex fitter in abs. distance minimization mode\n", N);
1358 } else {
1359 printf("%d-prong vertex fitter in weighted distance minimization mode\n", N);
1360 }
1361 printf("Bz: %1.f MaxIter: %3.d MaxChi2: %2.3f\n", mBz, mMaxIter, mMaxChi2);
1362 printf("Stopping condition: Max.param change < %2.3f Rel.Chi2 change > %2.3f\n", mMinParamChange, mMinRelChi2Change);
1363 printf("Discard candidates for : Rvtx > %2.3f DZ between tracks > %2.3f\n", getMaxR(), mMaxDZIni);
1364#endif
1365}
1366
1367//___________________________________________________________________
1368template <int N, typename... Args>
1369GPUd() o2::track::TrackParCov DCAFitterN<N, Args...>::createParentTrackParCov(int cand, bool sectorAlpha) const
1370{
1371 std::array<float, o2::track::kLabCovMatSize> covV = {0.};
1372 std::array<float, 3> pvecV = {0.};
1373 int q = 0;
1374 for (int it = 0; it < N; it++) {
1375 const auto& trc = getTrack(it, cand);
1376 std::array<float, 3> pvecT = {0.};
1377 std::array<float, o2::track::kLabCovMatSize> covT = {0.};
1378 trc.getPxPyPzGlo(pvecT);
1379 // The momentum block of getCovXYZPxPyPzGlo is already J*C*J^T for the native O2 momentum
1380 // parameters (snp,tgl,q/pt), with the track-frame alpha rotation folded into J, so there is
1381 // no need to re-derive it here (and both methods share the same |q/pt|/|snp| validity guard,
1382 // zeroing the covariance if it fails). The daughter momentum covariances are summed in the
1383 // lab px,py,pz frame; the TrackParCov constructor below rotates the sum to the parent frame.
1384 trc.getCovXYZPxPyPzGlo(covT);
1385 constexpr int MomInd[6] = {9, 13, 14, 18, 19, 20}; // cov matrix elements for momentum component
1386 for (int i = 0; i < 6; i++) {
1387 covV[MomInd[i]] += covT[MomInd[i]];
1388 }
1389 for (int i = 0; i < 3; i++) {
1390 pvecV[i] += pvecT[i];
1391 }
1392 q += trc.getCharge();
1393 }
1394 auto covVtxV = calcPCACovMatrix(cand);
1395 covV[0] = covVtxV(0, 0);
1396 covV[1] = covVtxV(1, 0);
1397 covV[2] = covVtxV(1, 1);
1398 covV[3] = covVtxV(2, 0);
1399 covV[4] = covVtxV(2, 1);
1400 covV[5] = covVtxV(2, 2);
1401 return o2::track::TrackParCov(getPCACandidatePos(cand), pvecV, covV, q, sectorAlpha);
1402}
1403
1404//___________________________________________________________________
1405template <int N, typename... Args>
1406GPUd() o2::track::TrackPar DCAFitterN<N, Args...>::createParentTrackPar(int cand, bool sectorAlpha) const
1407{
1408 const auto& trP = getTrack(0, cand);
1409 const auto& trN = getTrack(1, cand);
1410 const auto& wvtx = getPCACandidate(cand);
1411 std::array<float, 3> pvecV = {0.};
1412 int q = 0;
1413 for (int it = 0; it < N; it++) {
1414 const auto& trc = getTrack(it, cand);
1415 std::array<float, 3> pvecT = {0.};
1416 trc.getPxPyPzGlo(pvecT);
1417 for (int i = 0; i < 3; i++) {
1418 pvecV[i] += pvecT[i];
1419 }
1420 q += trc.getCharge();
1421 }
1422 const std::array<float, 3> vertex = {(float)wvtx[0], (float)wvtx[1], (float)wvtx[2]};
1423 return o2::track::TrackPar(vertex, pvecV, q, sectorAlpha);
1424}
1425
1426//___________________________________________________________________
1427template <int N, typename... Args>
1428GPUdi() bool DCAFitterN<N, Args...>::propagateParamToX(o2::track::TrackPar& t, float x)
1429{
1430 bool res = true;
1431 if (mUsePropagator || mMatCorr != o2::base::Propagator::MatCorrType::USEMatCorrNONE) {
1432#ifndef GPUCA_GPUCODE
1433 res = o2::base::Propagator::Instance()->PropagateToXBxByBz(t, x, mMaxSnp, mMaxStep, mMatCorr);
1434#endif
1435 } else {
1436 res = t.propagateParamTo(x, mBz);
1437 }
1438 if (!res) {
1439 mFitStatus[mCurHyp] = FitStatus::FailProp;
1440 mPropFailed[mCurHyp] = true;
1441 if (mLoggerBadProp.needToLog()) {
1442#ifndef GPUCA_GPUCODE
1443 printf("fitter %d: error (%ld muted): propagation to %.4f failed for %s\n", mFitterID, mLoggerBadProp.evCount, x, t.asString().c_str());
1444#else
1445 printf("fitter %d: error (%ld muted): propagation to %.4f failed\n", mFitterID, mLoggerBadProp.evCount, x);
1446#endif
1447 }
1448 }
1449 return res;
1450}
1451
1452//___________________________________________________________________
1453template <int N, typename... Args>
1454GPUdi() bool DCAFitterN<N, Args...>::propagateToX(o2::track::TrackParCov& t, float x)
1455{
1456 bool res = true;
1457 if (mUsePropagator || mMatCorr != o2::base::Propagator::MatCorrType::USEMatCorrNONE) {
1458#ifndef GPUCA_GPUCODE
1459 res = o2::base::Propagator::Instance()->PropagateToXBxByBz(t, x, mMaxSnp, mMaxStep, mMatCorr);
1460#endif
1461 } else {
1462 res = t.propagateTo(x, mBz);
1463 }
1464 if (!res) {
1465 mFitStatus[mCurHyp] = FitStatus::FailProp;
1466 mPropFailed[mCurHyp] = true;
1467 if (mLoggerBadProp.needToLog()) {
1468#ifndef GPUCA_GPUCODE
1469 printf("fitter %d: error (%ld muted): propagation to %.4f failed for %s\n", mFitterID, mLoggerBadProp.evCount, x, t.asString().c_str());
1470#else
1471 printf("fitter %d: error (%ld muted): propagation to %.4f failed\n", mFitterID, mLoggerBadProp.evCount, x);
1472#endif
1473 }
1474 }
1475 return res;
1476}
1477
1480
1481namespace device
1482{
1483template <typename Fitter>
1484void print(const int nBlocks, const int nThreads, Fitter& ft);
1485
1486template <typename Fitter, class... Tr>
1487int process(const int nBlocks, const int nThreads, Fitter&, Tr&... args);
1488
1489template <class Fitter, class... Tr>
1490void processBulk(const int nBlocks, const int nThreads, const int nBatches, std::vector<Fitter>& fitters, std::vector<int>& results, std::vector<Tr>&... args);
1491} // namespace device
1492
1493} // namespace vertexing
1494} // namespace o2
1495#endif // _ALICEO2_DCA_FITTERN_
std::function< void(void *, const void *)> assign
Base track model for the Barrel, params only, w/o covariance.
uint64_t vertex
Definition RawEventData.h:9
void print() const
int32_t i
Helper classes for helical tracks manipulations.
constexpr int p2()
uint32_t j
Definition RawData.h:0
uint32_t res
Definition RawData.h:0
uint32_t c
Definition RawData.h:2
o2::track::TrackParCov TrackParCov
Definition Recon.h:39
GPUd() value_type estimateLTFast(o2 static GPUd() float estimateLTIncrement(const o2 PropagatorImpl * Instance(bool uninitialized=false)
Definition Propagator.h:178
const Track & getTrack(int i, int cand=0) const
create parent track param with errors for decay vertex
Definition DCAFitterN.h:231
GPUd() const auto getPCACandidatePos(int cand=0) const
return position of quality-ordered candidate in the internal structures
Definition DCAFitterN.h:197
const Track * getOrigTrackPtr(int i) const
Definition DCAFitterN.h:262
GPUdi() FitStatus getFitStatus(int cand=0) const noexcept
return number of iterations during minimization (no check for its validity)
Definition DCAFitterN.h:264
int class Tr const T & t
Definition DCAFitterN.h:388
void setBadCovPolicy(BadCovPolicy v)
Definition DCAFitterN.h:423
GPUd() bool calcPCACoefs()
GPUdi() size_t getCallID() const
Definition DCAFitterN.h:313
GPUdi() void setFitterID(int i)
Definition DCAFitterN.h:312
int cand
track X-param at V0 candidate (no check for the candidate validity)
Definition DCAFitterN.h:339
GPUdi() int getNIterations(int cand=0) const
Definition DCAFitterN.h:267
GPUd() const Vec3D &getPCACandidate(int cand=0) const
< return PCA candidate, by default best on is provided (no check for the index validity)
Definition DCAFitterN.h:196
GPUdi() void clearLogThrottlers()
Definition DCAFitterN.h:416
int getCandidatePosition(int cand=0) const
return Chi2 at PCA candidate (no check for its validity)
Definition DCAFitterN.h:204
static constexpr int getNProngs()
Definition DCAFitterN.h:182
bool isPropagationFailure(int cand=0) const
Definition DCAFitterN.h:217
DCAFitterN(float bz, bool useAbsDCA, bool prop2DCA)
Definition DCAFitterN.h:185
GPUdi() void setPropagateToPCA(bool v
Track & getTrack(int i, int cand=0)
Definition DCAFitterN.h:221
std::array< float, 6 > calcPCACovMatrixFlat(int cand=0) const
Definition DCAFitterN.h:256
GPUdi() static void setTrackPos(Vec3D &pnt
float getChi2AtPCACandidate(int cand=0) const
Definition DCAFitterN.h:207
GPUd() bool propagateTracksToVertex(int cand=0)
check if propagation of tracks to candidate vertex was done
BadCovPolicy getBadCovPolicy() const
Definition DCAFitterN.h:424
int class Tr const T const Tr & args
Definition DCAFitterN.h:389
GLdouble n
Definition glcorearb.h:1982
GLint GLenum GLint x
Definition glcorearb.h:403
const GLfloat * m
Definition glcorearb.h:4066
const GLdouble * v
Definition glcorearb.h:832
GLenum array
Definition glcorearb.h:4274
GLint y
Definition glcorearb.h:270
typedef void(APIENTRYP PFNGLCULLFACEPROC)(GLenum mode)
GLboolean r
Definition glcorearb.h:1233
constexpr float Almost0
const TrackingFrameInfo *const const Cluster *const const float const float bz
std::tuple< double, double > rotateZd(double xL, double yL, double snAlp, double csAlp)
Definition Utils.h:167
std::tuple< double, double > rotateZInvd(double xG, double yG, double snAlp, double csAlp)
Definition Utils.h:147
SMatrix< T, D1, D1, MatRepSym< T, D1 > > Similarity(const SMatrix< T, D1, D2, R > &lhs, const SMatrix< T, D2, D2, MatRepSym< T, D2 > > &rhs)
Definition Cartesian.h:263
T Dot(const SVector< T, D > &lhs, const SVector< T, D > &rhs)
Definition Cartesian.h:257
TrackParCovF TrackParCov
Definition Track.h:33
TrackParF TrackPar
Definition Track.h:29
int process(const int nBlocks, const int nThreads, Fitter &, Tr &... args)
void processBulk(const int nBlocks, const int nThreads, const int nBatches, std::vector< Fitter > &fitters, std::vector< int > &results, std::vector< Tr > &... args)
GPUd() int DCAFitterN< N
ROOT::Math::SVector< double, 3 > Vec3D
GPUdi() void DCAFitterN< N
a couple of static helper functions to create timestamp values for CCDB queries or override obsolete ...
int process(po::variables_map &vm)
static constexpr float XRegNone
Definition DCAFitterN.h:50
static constexpr float XRegErrFactor
Definition DCAFitterN.h:49
GPUd() bool set(const o2
Definition DCAFitterN.h:59
GPUdDefault() TrackCovI()=default
static constexpr float XerrFactorOld
Definition DCAFitterN.h:55
GPUd() TrackDeriv(const o2
Definition DCAFitterN.h:97
GPUd() void set(const o2
Definition DCAFitterN.h:98
GPUdDefault() TrackDeriv()=default
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"
vec clear()