Project
Loading...
Searching...
No Matches
TrackFitter.cxx
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
16
18
19#include <stdexcept>
20
21#include <TGeoGlobalMagField.h>
22#include <TMatrixD.h>
23
24#include "Field/MagneticField.h"
26
27namespace o2
28{
29namespace mch
30{
31
32using namespace std;
33
34constexpr double TrackFitter::SDefaultChamberZ[10];
35constexpr double TrackFitter::SChamberThicknessInX0[10];
36
37//_________________________________________________________________________________________________
38void TrackFitter::initField(float l3Current, float dipoleCurrent)
39{
41
42 if (TGeoGlobalMagField::Instance()->GetField()) {
43 return;
44 }
45
46 auto field =
48 "A-A", "$(O2_ROOT)/share/Common/maps/mfchebKGI_sym.root");
49 TGeoGlobalMagField::Instance()->SetField(field);
50 TGeoGlobalMagField::Instance()->Lock();
51
53}
54
55//_________________________________________________________________________________________________
56void TrackFitter::fit(Track& track, bool smooth, bool finalize,
57 std::list<TrackParam>::reverse_iterator* itStartingParam,
58 bool skipLocalChi2Calculation)
59{
66
67 // initialize the starting track parameters and cluster
68 auto itParam(track.rbegin());
69 if (itStartingParam != nullptr) {
70 // use the ones pointed to by itStartingParam
71 if (*itStartingParam == track.rend()) {
72 throw runtime_error("invalid starting parameters");
73 }
74 itParam = *itStartingParam;
75 } else {
76 // or start from the last cluster and compute the track parameters from its position
77 // and the one of the first previous cluster found on a different chamber
78 auto itPreviousParam(itParam);
79 do {
80 ++itPreviousParam;
81 if (itPreviousParam == track.rend()) {
82 throw runtime_error("A track is made of at least 2 clusters in 2 different chambers");
83 }
84 } while (itPreviousParam->getClusterPtr()->getChamberId() == itParam->getClusterPtr()->getChamberId());
85 initTrack(*itPreviousParam->getClusterPtr(), *itParam->getClusterPtr(), *itParam);
86 }
87
88 // recusively add the upstream clusters and update the track parameters
89 TrackParam* startingParam = &*itParam;
90 while (++itParam != track.rend()) {
91 addCluster(*startingParam, *itParam->getClusterPtr(), *itParam);
92 startingParam = &*itParam;
93 }
94
95 // smooth the track if requested and the smoother enabled
96 if (smooth && mSmooth) {
97 smoothTrack(track, finalize, skipLocalChi2Calculation);
98 }
99}
100
101//_________________________________________________________________________________________________
102void TrackFitter::initTrack(const Cluster& cl1, const Cluster& cl2, TrackParam& param)
103{
108
109 // compute the track parameters at the last cluster
110 double dZ = cl1.getZ() - cl2.getZ();
111 param.setNonBendingCoor(cl2.getX());
112 param.setBendingCoor(cl2.getY());
113 param.setZ(cl2.getZ());
114 param.setNonBendingSlope((cl1.getX() - cl2.getX()) / dZ);
115 param.setBendingSlope((cl1.getY() - cl2.getY()) / dZ);
116 double bendingImpact = cl2.getY() - cl2.getZ() * param.getBendingSlope();
117 double inverseBendingMomentum = 1. / TrackExtrap::getBendingMomentumFromImpactParam(bendingImpact);
118 param.setInverseBendingMomentum(inverseBendingMomentum);
119
120 // compute the track parameter covariances at the last cluster (as if the other clusters did not exist)
121 TMatrixD lastParamCov(5, 5);
122 lastParamCov.Zero();
123 double cl1Ey2(0.);
124 if (mUseChamberResolution) {
125 // Non bending plane
126 lastParamCov(0, 0) = mChamberResolutionX2;
127 lastParamCov(1, 1) = (1000. * mChamberResolutionX2 + lastParamCov(0, 0)) / dZ / dZ;
128 // Bending plane
129 lastParamCov(2, 2) = mChamberResolutionY2;
130 cl1Ey2 = mChamberResolutionY2;
131 } else {
132 // Non bending plane
133 lastParamCov(0, 0) = cl2.getEx2();
134 lastParamCov(1, 1) = (1000. * cl1.getEx2() + lastParamCov(0, 0)) / dZ / dZ;
135 // Bending plane
136 lastParamCov(2, 2) = cl2.getEy2();
137 cl1Ey2 = cl1.getEy2();
138 }
139 // Non bending plane
140 lastParamCov(0, 1) = -lastParamCov(0, 0) / dZ;
141 lastParamCov(1, 0) = lastParamCov(0, 1);
142 // Bending plane
143 lastParamCov(2, 3) = -lastParamCov(2, 2) / dZ;
144 lastParamCov(3, 2) = lastParamCov(2, 3);
145 lastParamCov(3, 3) = (1000. * cl1Ey2 + lastParamCov(2, 2)) / dZ / dZ;
146 // Inverse bending momentum (vertex resolution + bending slope resolution + 10% error on dipole parameters+field)
148 lastParamCov(4, 4) =
149 ((mBendingVertexDispersion2 +
150 (cl1.getZ() * cl1.getZ() * lastParamCov(2, 2) + cl2.getZ() * cl2.getZ() * 1000. * cl1Ey2) / dZ / dZ) /
151 bendingImpact / bendingImpact +
152 0.1 * 0.1) *
153 inverseBendingMomentum * inverseBendingMomentum;
154 lastParamCov(2, 4) = cl1.getZ() * lastParamCov(2, 2) * inverseBendingMomentum / bendingImpact / dZ;
155 lastParamCov(4, 2) = lastParamCov(2, 4);
156 lastParamCov(3, 4) = -(cl1.getZ() * lastParamCov(2, 2) + cl2.getZ() * 1000. * cl1Ey2) * inverseBendingMomentum /
157 bendingImpact / dZ / dZ;
158 lastParamCov(4, 3) = lastParamCov(3, 4);
159 } else {
160 lastParamCov(4, 4) = inverseBendingMomentum * inverseBendingMomentum;
161 }
162 param.setCovariances(lastParamCov);
163
164 // set other parameters
165 param.setClusterPtr(&cl2);
166 param.setTrackChi2(0.);
167}
168
169//_________________________________________________________________________________________________
170void TrackFitter::addCluster(const TrackParam& startingParam, const Cluster& cl, TrackParam& param)
171{
176
177 if (cl.getZ() <= startingParam.getZ()) {
178 throw runtime_error("The new cluster must be upstream");
179 }
180
181 // copy the current parameters into the new ones
182 param.setParameters(startingParam.getParameters());
183 param.setZ(startingParam.getZ());
184 param.setCovariances(startingParam.getCovariances());
185 param.setTrackChi2(startingParam.getTrackChi2());
186
187 // add MCS effect in the current chamber
188 int currentChamber(startingParam.getClusterPtr()->getChamberId());
189 TrackExtrap::addMCSEffect(param, SChamberThicknessInX0[currentChamber], -1.);
190
191 // reset propagator for smoother
192 if (mSmooth) {
193 param.resetPropagator();
194 }
195
196 // add MCS in missing chambers if any
197 int expectedChamber(currentChamber - 1);
198 currentChamber = cl.getChamberId();
199 while (currentChamber < expectedChamber) {
200 if (!TrackExtrap::extrapToZCov(param, SDefaultChamberZ[expectedChamber], mSmooth)) {
201 throw runtime_error("Track extrapolation failed");
202 }
203 TrackExtrap::addMCSEffect(param, SChamberThicknessInX0[expectedChamber], -1.);
204 expectedChamber--;
205 }
206
207 // extrapolate to the z position of the new cluster
208 if (!TrackExtrap::extrapToZCov(param, cl.getZ(), mSmooth)) {
209 throw runtime_error("Track extrapolation failed");
210 }
211
212 // save extrapolated parameters and covariances for smoother
213 if (mSmooth) {
214 param.setExtrapParameters(param.getParameters());
215 param.setExtrapCovariances(param.getCovariances());
216 }
217
218 // recompute the parameters
219 param.setClusterPtr(&cl);
221}
222
223//_________________________________________________________________________________________________
224void TrackFitter::smoothTrack(Track& track, bool finalize, bool skipLocalChi2Calculation)
225{
230
231 auto itCurrentParam(track.begin());
232 auto itPreviousParam(itCurrentParam);
233 ++itCurrentParam;
234
235 // smoothed parameters and covariances at first cluster = filtered parameters and covariances
236 itPreviousParam->setSmoothParameters(itPreviousParam->getParameters());
237 itPreviousParam->setSmoothCovariances(itPreviousParam->getCovariances());
238
239 // local chi2 at first cluster = last additional chi2 provided by Kalman
240 itPreviousParam->setLocalChi2(itPreviousParam->getTrackChi2() - itCurrentParam->getTrackChi2());
241
242 // recursively smooth the next parameters and covariances
243 // compute the local chi2 if requested and needed (i.e. if there are more than 2 clusters)
244 auto skipChi2 = skipLocalChi2Calculation || track.getNClusters() < 3;
245 do {
246 runSmoother(*itPreviousParam, *itCurrentParam, skipChi2);
247 ++itPreviousParam;
248 } while (++itCurrentParam != track.end());
249
250 // update the regular parameters and covariances if requested
251 if (finalize) {
252 for (auto& param : track) {
253 param.setParameters(param.getSmoothParameters());
254 param.setCovariances(param.getSmoothCovariances());
255 }
256 }
257}
258
259//_________________________________________________________________________________________________
261{
265
266 // get actual track parameters (p)
267 TMatrixD param(trackParam.getParameters());
268
269 // get new cluster parameters (m)
270 const Cluster* cluster = trackParam.getClusterPtr();
271 TMatrixD clusterParam(5, 1);
272 clusterParam.Zero();
273 clusterParam(0, 0) = cluster->getX();
274 clusterParam(2, 0) = cluster->getY();
275
276 // compute the actual parameter weight (W)
277 TMatrixD paramWeight(trackParam.getCovariances());
278 if (paramWeight.Determinant() != 0) {
279 paramWeight.Invert();
280 } else {
281 throw runtime_error("Determinant = 0");
282 }
283
284 // compute the new cluster weight (U)
285 TMatrixD clusterWeight(5, 5);
286 clusterWeight.Zero();
287 if (mUseChamberResolution) {
288 clusterWeight(0, 0) = 1. / mChamberResolutionX2;
289 clusterWeight(2, 2) = 1. / mChamberResolutionY2;
290 } else {
291 clusterWeight(0, 0) = 1. / cluster->getEx2();
292 clusterWeight(2, 2) = 1. / cluster->getEy2();
293 }
294
295 // compute the new parameters covariance matrix ((W+U)^-1)
296 TMatrixD newParamCov(paramWeight, TMatrixD::kPlus, clusterWeight);
297 if (newParamCov.Determinant() != 0) {
298 newParamCov.Invert();
299 } else {
300 throw runtime_error("Determinant = 0");
301 }
302 trackParam.setCovariances(newParamCov);
303
304 // compute the new parameters (p' = ((W+U)^-1)U(m-p) + p)
305 TMatrixD tmp(clusterParam, TMatrixD::kMinus, param); // m-p
306 TMatrixD tmp2(clusterWeight, TMatrixD::kMult, tmp); // U(m-p)
307 TMatrixD newParam(newParamCov, TMatrixD::kMult, tmp2); // ((W+U)^-1)U(m-p)
308 newParam += param; // ((W+U)^-1)U(m-p) + p
309 trackParam.setParameters(newParam);
310
311 // compute the additional chi2 (= ((p'-p)^-1)W(p'-p) + ((p'-m)^-1)U(p'-m))
312 tmp = newParam; // p'
313 tmp -= param; // (p'-p)
314 TMatrixD tmp3(paramWeight, TMatrixD::kMult, tmp); // W(p'-p)
315 TMatrixD addChi2Track(tmp, TMatrixD::kTransposeMult, tmp3); // ((p'-p)^-1)W(p'-p)
316 tmp = newParam; // p'
317 tmp -= clusterParam; // (p'-m)
318 TMatrixD tmp4(clusterWeight, TMatrixD::kMult, tmp); // U(p'-m)
319 addChi2Track += TMatrixD(tmp, TMatrixD::kTransposeMult, tmp4); // ((p'-p)^-1)W(p'-p) + ((p'-m)^-1)U(p'-m)
320 trackParam.setTrackChi2(trackParam.getTrackChi2() + addChi2Track(0, 0));
321}
322
323//_________________________________________________________________________________________________
324void TrackFitter::runSmoother(const TrackParam& previousParam, TrackParam& param, bool skipLocalChi2Calculation)
325{
328
329 // get variables
330 const TMatrixD& extrapParameters = previousParam.getExtrapParameters(); // X(k+1 k)
331 const TMatrixD& filteredParameters = param.getParameters(); // X(k k)
332 const TMatrixD& previousSmoothParameters = previousParam.getSmoothParameters(); // X(k+1 n)
333 const TMatrixD& propagator = previousParam.getPropagator(); // F(k)
334 const TMatrixD& extrapCovariances = previousParam.getExtrapCovariances(); // C(k+1 k)
335 const TMatrixD& filteredCovariances = param.getCovariances(); // C(k k)
336 const TMatrixD& previousSmoothCovariances = previousParam.getSmoothCovariances(); // C(k+1 n)
337
338 // compute smoother gain: A(k) = C(kk) * F(k)^t * (C(k+1 k))^-1
339 TMatrixD extrapWeight(extrapCovariances);
340 if (extrapWeight.Determinant() != 0) {
341 extrapWeight.Invert(); // (C(k+1 k))^-1
342 } else {
343 throw runtime_error("Determinant = 0");
344 }
345 TMatrixD smootherGain(filteredCovariances, TMatrixD::kMultTranspose, propagator); // C(kk) * F(k)^t
346 smootherGain *= extrapWeight; // C(kk) * F(k)^t * (C(k+1 k))^-1
347
348 // compute smoothed parameters: X(k n) = X(k k) + A(k) * (X(k+1 n) - X(k+1 k))
349 TMatrixD tmpParam(previousSmoothParameters, TMatrixD::kMinus, extrapParameters); // X(k+1 n) - X(k+1 k)
350 TMatrixD smoothParameters(smootherGain, TMatrixD::kMult, tmpParam); // A(k) * (X(k+1 n) - X(k+1 k))
351 smoothParameters += filteredParameters; // X(k k) + A(k) * (X(k+1 n) - X(k+1 k))
352 param.setSmoothParameters(smoothParameters);
353
354 // compute smoothed covariances: C(k n) = C(k k) + A(k) * (C(k+1 n) - C(k+1 k)) * (A(k))^t
355 TMatrixD tmpCov(previousSmoothCovariances, TMatrixD::kMinus, extrapCovariances); // C(k+1 n) - C(k+1 k)
356 TMatrixD tmpCov2(tmpCov, TMatrixD::kMultTranspose, smootherGain); // (C(k+1 n) - C(k+1 k)) * (A(k))^t
357 TMatrixD smoothCovariances(smootherGain, TMatrixD::kMult, tmpCov2); // A(k) * (C(k+1 n) - C(k+1 k)) * (A(k))^t
358 smoothCovariances += filteredCovariances; // C(k k) + A(k) * (C(k+1 n) - C(k+1 k)) * (A(k))^t
359 param.setSmoothCovariances(smoothCovariances);
360
361 // skip the local chi2 calculation if requested to do so
362 if (skipLocalChi2Calculation) {
363 param.setLocalChi2(0.);
364 return;
365 }
366
367 // compute smoothed residual: r(k n) = cluster - X(k n)
368 const Cluster* cluster = param.getClusterPtr();
369 TMatrixD smoothResidual(2, 1);
370 smoothResidual.Zero();
371 smoothResidual(0, 0) = cluster->getX() - smoothParameters(0, 0);
372 smoothResidual(1, 0) = cluster->getY() - smoothParameters(2, 0);
373
374 // compute weight of smoothed residual: W(k n) = (clusterCov - C(k n))^-1
375 TMatrixD smoothResidualWeight(2, 2);
376 if (mUseChamberResolution) {
377 smoothResidualWeight(0, 0) = mChamberResolutionX2 - smoothCovariances(0, 0);
378 smoothResidualWeight(1, 1) = mChamberResolutionY2 - smoothCovariances(2, 2);
379 } else {
380 smoothResidualWeight(0, 0) = cluster->getEx2() - smoothCovariances(0, 0);
381 smoothResidualWeight(1, 1) = cluster->getEy2() - smoothCovariances(2, 2);
382 }
383 smoothResidualWeight(0, 1) = -smoothCovariances(0, 2);
384 smoothResidualWeight(1, 0) = -smoothCovariances(2, 0);
385 if (smoothResidualWeight.Determinant() != 0) {
386 smoothResidualWeight.Invert();
387 } else {
388 throw runtime_error("Determinant = 0");
389 }
390
391 // compute local chi2 = (r(k n))^t * W(k n) * r(k n)
392 TMatrixD tmpChi2(smoothResidual, TMatrixD::kTransposeMult, smoothResidualWeight); // (r(k n))^t * W(k n)
393 TMatrixD localChi2(tmpChi2, TMatrixD::kMult, smoothResidual); // (r(k n))^t * W(k n) * r(k n)
394 param.setLocalChi2(localChi2(0, 0));
395}
396
397} // namespace mch
398} // namespace o2
Definition of a class to fit a track to a set of clusters.
Definition of the MagF class.
Definition of tools for track extrapolation.
static MagneticField * createFieldMap(float l3Current=-30000., float diCurrent=-6000., Int_t convention=0, Bool_t uniform=kFALSE, float beamenergy=7000, const Char_t *btype="pp", const std::string path=std::string(gSystem->Getenv("VMCWORKDIR"))+std::string("/Common/maps/mfchebKGI_sym.root"))
HMPID cluster implementation.
Definition Cluster.h:27
static void addMCSEffect(TrackParam &trackParam, double dZ, double x0)
static bool isFieldON()
Return true if the field is switched ON.
Definition TrackExtrap.h:47
static void setField()
static bool extrapToZCov(TrackParam &trackParam, double zEnd, bool updatePropagator=false)
static double getBendingMomentumFromImpactParam(double impactParam)
void initField(float l3Current, float dipoleCurrent)
void fit(Track &track, bool smooth=true, bool finalize=true, std::list< TrackParam >::reverse_iterator *itStartingParam=nullptr, bool skipLocalChi2Calculation=false)
void runKalmanFilter(TrackParam &trackParam)
track parameters for internal use
Definition TrackParam.h:34
const TMatrixD & getExtrapCovariances() const
const TMatrixD & getSmoothCovariances() const
void setTrackChi2(Double_t chi2)
set the chi2 of the track when the associated cluster was attached
Definition TrackParam.h:132
Double_t getTrackChi2() const
return the chi2 of the track when the associated cluster was attached
Definition TrackParam.h:130
const TMatrixD & getParameters() const
return track parameters
Definition TrackParam.h:81
void setCovariances(const TMatrixD &covariances)
const TMatrixD & getPropagator() const
const TMatrixD & getCovariances() const
const TMatrixD & getExtrapParameters() const
void setParameters(const TMatrixD &parameters)
set track parameters
Definition TrackParam.h:83
const TMatrixD & getSmoothParameters() const
const Cluster * getClusterPtr() const
get pointer to associated cluster
Definition TrackParam.h:120
track for internal use
Definition Track.h:33
auto rend()
Return a reverse iterator passing the track parameters at first cluster.
Definition Track.h:64
auto rbegin()
Return a reverse iterator to the track parameters at clusters (point to the last one)
Definition Track.h:61
GLenum GLfloat param
Definition glcorearb.h:271
RuntimeErrorRef runtime_error(const char *)
a couple of static helper functions to create timestamp values for CCDB queries or override obsolete ...
Defining DataPointCompositeObject explicitly as copiable.
cluster minimal structure
Definition Cluster.h:31
double getEx2() const
Return the cluster resolution square along x.
Definition Cluster.h:52
double getY() const
Return the cluster position along y as double.
Definition Cluster.h:44
double getX() const
Return the cluster position along x as double.
Definition Cluster.h:42
double getEy2() const
Return the cluster resolution square along y.
Definition Cluster.h:54
double getZ() const
Return the cluster position along z as double.
Definition Cluster.h:46