CbmRoot
Loading...
Searching...
No Matches
KfTrackKalmanFilter.cxx
Go to the documentation of this file.
1/* Copyright (C) 2017-2021 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt
2 SPDX-License-Identifier: GPL-3.0-only
3 Authors: Maksym Zyzak [committer], Valentina Akishina */
4
6
8#include "KfMeasurementU.h"
9#include "KfMeasurementXy.h"
10
11using namespace cbm::algo::kf;
12
13
14template<typename DataT, class Settings>
16{
17
18 DataT zeta = m.CosPhi() * fTr.X() + m.SinPhi() * fTr.Y() - m.U();
19
20 // F = CH'
21 DataT F0 = m.CosPhi() * fTr.C00() + m.SinPhi() * fTr.C10();
22 DataT F1 = m.CosPhi() * fTr.C10() + m.SinPhi() * fTr.C11();
23
24 DataT HCH = (F0 * m.CosPhi() + F1 * m.SinPhi());
25
26 DataT F2 = m.CosPhi() * fTr.C20() + m.SinPhi() * fTr.C21();
27 DataT F3 = m.CosPhi() * fTr.C30() + m.SinPhi() * fTr.C31();
28 DataT F4 = m.CosPhi() * fTr.C40() + m.SinPhi() * fTr.C41();
29
30 constexpr bool doProtect = !std::is_same<DataTscal, double>::value;
31
32 const DataTmask maskDoFilter = doProtect ? (HCH < m.Du2() * 16.f) : DataTmask(true);
33
34 // correction to HCH is needed for the case when sigma2 is so small
35 // with respect to HCH that it disappears due to the roundoff error
36 //
37 DataT w = m.Du2() + (doProtect ? (DataT(1.0000001) * HCH) : HCH);
38 DataT wi = kf::utils::iif(fMask, DataT(1.) / w, DataT(0.));
39
40 DataT zetawi = zeta / (kf::utils::iif(maskDoFilter, m.Du2(), DataT(0.)) + HCH);
41 zetawi = kf::utils::iif(fMask, zetawi, DataT(0.));
42
43 wi = kf::utils::iif(m.Du2() > DataT(0.), wi, DataT(0.));
44
45 fTr.ChiSq() += zeta * zeta * wi;
46 fTr.Ndf() += kf::utils::iif(fMask, m.Ndf(), DataT(0.));
47
48 if constexpr (Settings::kDoFitTime) {
49 DataT F5 = m.CosPhi() * fTr.C50() + m.SinPhi() * fTr.C51();
50 DataT F6 = m.CosPhi() * fTr.C60() + m.SinPhi() * fTr.C61();
51 DataT K5 = F5 * wi;
52 DataT K6 = F6 * wi;
53
54 fTr.Time() -= F5 * zetawi;
55 fTr.Vi() -= F6 * zetawi;
56
57 fTr.C50() -= K5 * F0;
58 fTr.C51() -= K5 * F1;
59 fTr.C52() -= K5 * F2;
60 fTr.C53() -= K5 * F3;
61 fTr.C54() -= K5 * F4;
62 fTr.C55() -= K5 * F5;
63
64 fTr.C60() -= K6 * F0;
65 fTr.C61() -= K6 * F1;
66 fTr.C62() -= K6 * F2;
67 fTr.C63() -= K6 * F3;
68 fTr.C64() -= K6 * F4;
69 fTr.C65() -= K6 * F5;
70 fTr.C66() -= K6 * F6;
71 }
72
73 DataT K1 = F1 * wi;
74 DataT K2 = F2 * wi;
75 DataT K3 = F3 * wi;
76 DataT K4 = F4 * wi;
77
78 fTr.X() -= F0 * zetawi;
79 fTr.Y() -= F1 * zetawi;
80 fTr.Tx() -= F2 * zetawi;
81 fTr.Ty() -= F3 * zetawi;
82 fTr.Qp() -= F4 * zetawi;
83
84 fTr.C00() -= F0 * F0 * wi;
85
86 fTr.C10() -= K1 * F0;
87 fTr.C11() -= K1 * F1;
88
89 fTr.C20() -= K2 * F0;
90 fTr.C21() -= K2 * F1;
91 fTr.C22() -= K2 * F2;
92
93 fTr.C30() -= K3 * F0;
94 fTr.C31() -= K3 * F1;
95 fTr.C32() -= K3 * F2;
96 fTr.C33() -= K3 * F3;
97
98 fTr.C40() -= K4 * F0;
99 fTr.C41() -= K4 * F1;
100 fTr.C42() -= K4 * F2;
101 fTr.C43() -= K4 * F3;
102 fTr.C44() -= K4 * F4;
103
105}
106
107template<typename DataT, class Settings>
108void TrackKalmanFilter<DataT, Settings>::FilterTime(DataT t, DataT dt2, const DataTmask& timeInfo)
109{
110 // filter track with a time measurement
111
112 if constexpr (!Settings::kDoFitTime) {
113 return;
114 }
115
116 // F = CH'
117 DataT F0 = fTr.C50();
118 DataT F1 = fTr.C51();
119 DataT F2 = fTr.C52();
120 DataT F3 = fTr.C53();
121 DataT F4 = fTr.C54();
122 DataT F5 = fTr.C55();
123 DataT F6 = fTr.C65();
124
125 DataT HCH = fTr.C55();
126
127 DataTmask mask = fMask && timeInfo;
128
129 // when dt0 is much smaller than current time error,
130 // set track time exactly to the measurement value without filtering
131 // it helps to keep the initial time errors reasonably small
132 // the calculations in the covariance matrix are not affected
133
134 const DataTmask maskDoFilter = mask && (HCH < dt2 * 16.f);
135
136 DataT wi = kf::utils::iif(mask, DataT(1.) / (dt2 + DataT(1.0000001) * HCH), DataT(0.));
137 DataT zeta = kf::utils::iif(mask, fTr.Time() - t, DataT(0.));
138 DataT zetawi = kf::utils::iif(mask, zeta / (kf::utils::iif(maskDoFilter, dt2, DataT(0.)) + HCH), DataT(0.));
139
140 fTr.ChiSqTime() += kf::utils::iif(maskDoFilter, zeta * zeta * wi, DataT(0.));
141 fTr.NdfTime() += kf::utils::iif(mask, DataT(1.), DataT(0.));
142
143 DataT K1 = F1 * wi;
144 DataT K2 = F2 * wi;
145 DataT K3 = F3 * wi;
146 DataT K4 = F4 * wi;
147 DataT K5 = F5 * wi;
148 DataT K6 = F6 * wi;
149
150 fTr.X() -= F0 * zetawi;
151 fTr.Y() -= F1 * zetawi;
152 fTr.Tx() -= F2 * zetawi;
153 fTr.Ty() -= F3 * zetawi;
154 fTr.Qp() -= F4 * zetawi;
155 fTr.Time() -= F5 * zetawi;
156 fTr.Vi() -= F6 * zetawi;
157
158 fTr.C00() -= F0 * F0 * wi;
160 fTr.C10() -= K1 * F0;
161 fTr.C11() -= K1 * F1;
163 fTr.C20() -= K2 * F0;
164 fTr.C21() -= K2 * F1;
165 fTr.C22() -= K2 * F2;
166
167 fTr.C30() -= K3 * F0;
168 fTr.C31() -= K3 * F1;
169 fTr.C32() -= K3 * F2;
170 fTr.C33() -= K3 * F3;
172 fTr.C40() -= K4 * F0;
173 fTr.C41() -= K4 * F1;
174 fTr.C42() -= K4 * F2;
175 fTr.C43() -= K4 * F3;
176 fTr.C44() -= K4 * F4;
177
178 fTr.C50() -= K5 * F0;
179 fTr.C51() -= K5 * F1;
180 fTr.C52() -= K5 * F2;
181 fTr.C53() -= K5 * F3;
182 fTr.C54() -= K5 * F4;
183 fTr.C55() -= K5 * F5;
185 fTr.C60() -= K6 * F0;
186 fTr.C61() -= K6 * F1;
187 fTr.C62() -= K6 * F2;
188 fTr.C63() -= K6 * F3;
189 fTr.C64() -= K6 * F4;
190 fTr.C65() -= K6 * F5;
191 fTr.C66() -= K6 * F6;
192}
194
195template<typename DataT, class Settings>
196void TrackKalmanFilter<DataT, Settings>::FilterXY(const kf::MeasurementXy<DataT>& mxy, bool skipUnmeasuredCoordinates)
197{
198 {
200 mx.SetCosPhi(DataT(1.));
201 mx.SetSinPhi(DataT(0.));
202 mx.SetU(mxy.X());
203 mx.SetDu2(mxy.Dx2());
204 mx.SetNdf(mxy.NdfX());
205
207 mu.SetCosPhi(-mxy.Dxy() / mxy.Dx2());
208 mu.SetSinPhi(DataT(1.));
209 mu.SetU(mu.CosPhi() * mxy.X() + mxy.Y());
210 mu.SetDu2(mxy.Dy2() - mxy.Dxy() * mxy.Dxy() / mxy.Dx2());
211 mu.SetNdf(mxy.NdfY());
212
213 auto maskOld = fMask;
214 if (skipUnmeasuredCoordinates) {
215 fMask = maskOld & (mxy.NdfX() > DataT(0.));
216 }
218 if (skipUnmeasuredCoordinates) {
219 fMask = maskOld & (mxy.NdfY() > DataT(0.));
220 }
221 Filter1d(mu);
222 fMask = maskOld;
224 return;
225 }
226
227 //----------------------------------------------------------------------------------------------
228 // the other way: filter 2 dimensions at once
229
230 const DataT TWO(2.);
231
232 DataT zeta0, zeta1, S00, S10, S11, si;
233 DataT F00, F10, F20, F30, F40, F50, F60;
234 DataT F01, F11, F21, F31, F41, F51, F61;
235 DataT K00, K10, K20, K30, K40, K50, K60;
236 DataT K01, K11, K21, K31, K41, K51, K61;
237
238 zeta0 = fTr.X() - mxy.X();
239 zeta1 = fTr.Y() - mxy.Y();
241 // F = CH'
242 F00 = fTr.C00();
243 F10 = fTr.C10();
244 F20 = fTr.C20();
245 F30 = fTr.C30();
246 F40 = fTr.C40();
247 F50 = fTr.C50();
248 F60 = fTr.C60();
249
250 F01 = fTr.C10();
251 F11 = fTr.C11();
252 F21 = fTr.C21();
253 F31 = fTr.C31();
254 F41 = fTr.C41();
255 F51 = fTr.C51();
256 F61 = fTr.C61();
257
258 S00 = F00 + mxy.Dx2();
259 S10 = F10 + mxy.Dxy();
260 S11 = F11 + mxy.Dy2();
261
262 si = 1.f / (S00 * S11 - S10 * S10);
263 DataT S00tmp = S00;
264 S00 = si * S11;
265 S10 = -si * S10;
266 S11 = si * S00tmp;
267
268 fTr.ChiSq() += zeta0 * zeta0 * S00 + 2.f * zeta0 * zeta1 * S10 + zeta1 * zeta1 * S11;
269 fTr.Ndf() += TWO;
270
271 K00 = F00 * S00 + F01 * S10;
272 K01 = F00 * S10 + F01 * S11;
273
274 K10 = F10 * S00 + F11 * S10;
275 K11 = F10 * S10 + F11 * S11;
276
277 K20 = F20 * S00 + F21 * S10;
278 K21 = F20 * S10 + F21 * S11;
279
280 K30 = F30 * S00 + F31 * S10;
281 K31 = F30 * S10 + F31 * S11;
282
283 K40 = F40 * S00 + F41 * S10;
284 K41 = F40 * S10 + F41 * S11;
285
286 K50 = F50 * S00 + F51 * S10;
287 K51 = F50 * S10 + F51 * S11;
288
289 K60 = F60 * S00 + F61 * S10;
290 K61 = F60 * S10 + F61 * S11;
291
292 fTr.X() -= K00 * zeta0 + K01 * zeta1;
293 fTr.Y() -= K10 * zeta0 + K11 * zeta1;
294 fTr.Tx() -= K20 * zeta0 + K21 * zeta1;
295 fTr.Ty() -= K30 * zeta0 + K31 * zeta1;
296 fTr.Qp() -= K40 * zeta0 + K41 * zeta1;
297 fTr.Time() -= K50 * zeta0 + K51 * zeta1;
298 fTr.Vi() -= K60 * zeta0 + K61 * zeta1;
299
300 fTr.C00() -= K00 * F00 + K01 * F01;
301
302 fTr.C10() -= K10 * F00 + K11 * F01;
303 fTr.C11() -= K10 * F10 + K11 * F11;
304
305 fTr.C20() -= K20 * F00 + K21 * F01;
306 fTr.C21() -= K20 * F10 + K21 * F11;
307 fTr.C22() -= K20 * F20 + K21 * F21;
308
309 fTr.C30() -= K30 * F00 + K31 * F01;
310 fTr.C31() -= K30 * F10 + K31 * F11;
311 fTr.C32() -= K30 * F20 + K31 * F21;
312 fTr.C33() -= K30 * F30 + K31 * F31;
313
314 fTr.C40() -= K40 * F00 + K41 * F01;
315 fTr.C41() -= K40 * F10 + K41 * F11;
316 fTr.C42() -= K40 * F20 + K41 * F21;
317 fTr.C43() -= K40 * F30 + K41 * F31;
318 fTr.C44() -= K40 * F40 + K41 * F41;
319
320 fTr.C50() -= K50 * F00 + K51 * F01;
321 fTr.C51() -= K50 * F10 + K51 * F11;
322 fTr.C52() -= K50 * F20 + K51 * F21;
323 fTr.C53() -= K50 * F30 + K51 * F31;
324 fTr.C54() -= K50 * F40 + K51 * F41;
325 fTr.C55() -= K50 * F50 + K51 * F51;
326
327 fTr.C60() -= K60 * F00 + K61 * F01;
328 fTr.C61() -= K60 * F10 + K61 * F11;
329 fTr.C62() -= K60 * F20 + K61 * F21;
330 fTr.C63() -= K60 * F30 + K61 * F31;
331 fTr.C64() -= K60 * F40 + K61 * F41;
332 fTr.C65() -= K60 * F50 + K61 * F51;
333 fTr.C66() -= K60 * F60 + K61 * F61;
334}
335
336
337template<typename DataT, class Settings>
339 std::array<DataT, 5>& Jx,
340 std::array<DataT, 5>& Jy) const
341{
342 // extrapolate track assuming it is straight (qp==0)
343 // return the extrapolated X, Y and the derivatives of the extrapolated X and Y
344
345 cnst c_light = DataT(1.e-5 * kf::defs::SpeedOfLight<double>);
346
347 cnst h = (zm - fTr.GetZ());
348
349 DataT x = fTr.X();
350 DataT y = fTr.Y();
351 DataT z = fTr.GetZ();
352 DataT tx = fTr.Tx();
353 DataT ty = fTr.Ty();
354 DataT xx = tx * tx;
355 DataT yy = ty * ty;
356 DataT xy = tx * ty;
357
358 DataT cL = c_light * sqrt(DataT(1.) + xx + yy);
359
360 DataT Sx, Sy, Sz;
361 std::tie(Sx, Sy, Sz) = Field.GetDoubleIntegrals(x, y, z, x + h * tx, y + h * ty, zm);
362
363 Jx[0] = 1.;
364 Jx[1] = 0.;
365 Jx[2] = h;
366 Jx[3] = 0.;
367 Jx[4] = cL * (Sx * xy - Sy * (xx + DataT(1.)) + Sz * ty);
368
369 Jy[0] = 0.;
370 Jy[1] = 1.;
371 Jy[2] = 0.;
372 Jy[3] = h;
373 Jy[4] = cL * (Sx * (yy + DataT(1.)) - Sy * xy - Sz * tx);
374}
375
376template<typename DataT, class Settings>
378 const std::array<DataT, 5>& Jx,
379 const std::array<DataT, 5>& Jy)
380{
381 // add a 2-D measurenent (x,y) at some z, that differs from fTr.GetZ()
382
383 auto& T = fTr;
384
385 DataT zeta0 = T.X() + Jx[2] * T.Tx() + Jx[4] * T.Qp() - m.X();
386 DataT zeta1 = T.Y() + Jy[3] * T.Ty() + Jy[4] * T.Qp() - m.Y();
387
388 // H = 1 0 Jx[2] 0 Jx[4]
389 // 0 1 0 Jy[3] Jy[4]
390
391 // ( 1 0 )
392 // ( 0 1 )
393 // H' = ( Jx[2] 0 )
394 // ( 0 Jy[3] )
395 // ( Jx[4] Jy[4] )
396
397
398 // F = CH'
399 DataT F00 = T.C00() + Jx[2] * T.C20() + Jx[4] * T.C40();
400 DataT F01 = T.C10() + Jy[3] * T.C30() + Jy[4] * T.C40();
401
402 DataT F10 = T.C10() + Jx[2] * T.C21() + Jx[4] * T.C41();
403 DataT F11 = T.C11() + Jy[3] * T.C31() + Jy[4] * T.C41();
404
405 DataT F20 = T.C20() + Jx[2] * T.C22() + Jx[4] * T.C42();
406 DataT F21 = T.C21() + Jy[3] * T.C32() + Jy[4] * T.C42();
407
408 DataT F30 = T.C30() + Jx[2] * T.C32() + Jx[4] * T.C43();
409 DataT F31 = T.C31() + Jy[3] * T.C33() + Jy[4] * T.C43();
410
411 DataT F40 = T.C40() + Jx[2] * T.C42() + Jx[4] * T.C44();
412 DataT F41 = T.C41() + Jy[3] * T.C43() + Jy[4] * T.C44();
413
414 // S = mC + H F
415
416 DataT S00 = m.Dx2() + F00 + Jx[2] * F20 + Jx[4] * F40;
417 DataT S10 = m.Dxy() + F10 + Jy[3] * F30 + Jy[4] * F40;
418 DataT S11 = m.Dy2() + F11 + Jy[3] * F31 + Jy[4] * F41;
419
420
421 DataT si = DataT(1.) / (S00 * S11 - S10 * S10);
422
423 DataT S00tmp = S00;
424 S00 = si * S11;
425 S10 = -si * S10;
426 S11 = si * S00tmp;
427
428 T.ChiSq() += zeta0 * zeta0 * S00 + DataT(2.) * zeta0 * zeta1 * S10 + zeta1 * zeta1 * S11;
429 T.Ndf() += m.NdfX() + m.NdfY();
430
431 DataT K00 = F00 * S00 + F01 * S10;
432 DataT K01 = F00 * S10 + F01 * S11;
433 DataT K10 = F10 * S00 + F11 * S10;
434 DataT K11 = F10 * S10 + F11 * S11;
435 DataT K20 = F20 * S00 + F21 * S10;
436 DataT K21 = F20 * S10 + F21 * S11;
437 DataT K30 = F30 * S00 + F31 * S10;
438 DataT K31 = F30 * S10 + F31 * S11;
439 DataT K40 = F40 * S00 + F41 * S10;
440 DataT K41 = F40 * S10 + F41 * S11;
441
442 T.X() -= K00 * zeta0 + K01 * zeta1;
443 T.Y() -= K10 * zeta0 + K11 * zeta1;
444 T.Tx() -= K20 * zeta0 + K21 * zeta1;
445 T.Ty() -= K30 * zeta0 + K31 * zeta1;
446 T.Qp() -= K40 * zeta0 + K41 * zeta1;
447
448 T.C00() -= (K00 * F00 + K01 * F01);
449 T.C10() -= (K10 * F00 + K11 * F01);
450 T.C11() -= (K10 * F10 + K11 * F11);
451 T.C20() -= (K20 * F00 + K21 * F01);
452 T.C21() -= (K20 * F10 + K21 * F11);
453 T.C22() -= (K20 * F20 + K21 * F21);
454 T.C30() -= (K30 * F00 + K31 * F01);
455 T.C31() -= (K30 * F10 + K31 * F11);
456 T.C32() -= (K30 * F20 + K31 * F21);
457 T.C33() -= (K30 * F30 + K31 * F31);
458 T.C40() -= (K40 * F00 + K41 * F01);
459 T.C41() -= (K40 * F10 + K41 * F11);
460 T.C42() -= (K40 * F20 + K41 * F21);
461 T.C43() -= (K40 * F30 + K41 * F31);
462 T.C44() -= (K40 * F40 + K41 * F41);
463}
464
465
466template<typename DataT, class Settings>
468 const std::array<DataT, 5>& Jy)
469{
470 // add a 2-D measurenent (x,y) at some z, that differs from fTr.GetZ()
471
472 auto& T = fTr;
473
474 DataT zeta = T.Y() + Jy[3] * T.Ty() + Jy[4] * T.Qp() - m.Y();
475
476 DataT F1 = T.C11() + Jy[3] * T.C31() + Jy[4] * T.C41();
477 DataT F3 = T.C31() + Jy[3] * T.C33() + Jy[4] * T.C43();
478 DataT F4 = Jy[4] * T.C44();
479
480 DataT S = DataT(1.) / (m.Dy2() + F1 + Jy[3] * F3 + Jy[4] * F4);
481
482 T.ChiSq() += zeta * zeta * S;
483
484 DataT K1 = F1 * S;
485 DataT K3 = F3 * S;
486
487 T.Y() -= K1 * zeta;
488 T.Ty() -= K3 * zeta;
489
490 T.C11() -= (K1 * F1);
491 T.C31() -= (K3 * F1);
492 T.C33() -= (K3 * F3);
493}
494
495
496template<typename DataT, class Settings>
498 const std::array<DataT, 5>& jL,
499 const kf::MeasurementXy<DataT>& mM, DataT msM,
500 const kf::MeasurementXy<DataT>& mR,
501 const std::array<DataT, 5>& jR)
502{
503 auto& T = fTr;
504 DataT qp = T.Qp();
505 DataT qpS = T.C44();
506 DataT chi2 = T.ChiSq();
507
508 DataT l3 = jL[3];
509 DataT l4 = jL[4];
510
511 DataT yL = mL.Y() - l4 * qp;
512 DataT lS = mL.Dy2() + l4 * l4 * qpS;
513
514 DataT yM = mM.Y();
515 DataT mS = mM.Dy2();
516
517
518 DataT r3 = jR[3];
519 DataT r4 = jR[4];
520
521 DataT yR = mR.Y() - r4 * qp;
522 DataT rS = mR.Dy2() + r4 * r4 * qpS;
523
524 DataT ty;
525
526 DataT a = lS + mS + l3 * l3 * msM;
527
528 DataT c33;
529
530 {
531 DataT zeta = (l3 * (yM - yR) + r3 * (yL - yM));
532
533 DataT b = r3 * lS + (r3 - l3) * mS + r3 * l3 * l3 * msM;
534
535 DataT c = l3 * l3 * rS + (l3 - r3) * (l3 - r3) * mS + r3 * r3 * lS + r3 * r3 * l3 * l3 * msM;
536
537 chi2 += zeta * zeta / c;
538
539 ty = ((yL - yM) * c - b * zeta) / l3 / c;
540 c33 = (a * c - b * b) / l3 / l3 / c;
541 }
542
543 T.Ty() = ty;
544 T.C33() = c33;
545 T.ChiSq() = chi2;
546}
547
548template<typename DataT, class Settings>
550 const std::array<DataT, 5>& jL,
551 const kf::MeasurementXy<DataT>& mM, DataT msM,
552 const kf::MeasurementXy<DataT>& mR,
553 const std::array<DataT, 5>& jR)
554{
555 auto& T = fTr;
556 DataT qp = T.Qp();
557 DataT qpS = T.C44();
558 DataT chi2 = T.ChiSq();
559
560 DataT l3 = jL[3];
561 DataT l4 = jL[4];
562
563 DataT yL = mL.Y() - l4 * qp;
564 DataT lS = mL.Dy2() + l4 * l4 * qpS;
565
566 DataT yM = mM.Y();
567 DataT mS = mM.Dy2();
568
569
570 DataT r3 = jR[3];
571 DataT r4 = jR[4];
572
573 DataT yR = mR.Y() - r4 * qp;
574 DataT rS = mR.Dy2() + r4 * r4 * qpS;
575
576
577 DataT zeta = (l3 * (yM - yR) + r3 * (yL - yM));
578 DataT c = l3 * l3 * rS + (l3 - r3) * (l3 - r3) * mS + r3 * r3 * lS + r3 * r3 * l3 * l3 * msM;
579
580 chi2 += zeta * zeta / c;
581
582 T.ChiSq() = chi2;
583}
584
585template<typename DataT, class Settings>
587{
588 // measure velocity using measured qp
589 // assuming particle mass == fMass;
590
591 const DataT kClightNsInv = kf::defs::SpeedOfLightInv<DataT>;
592
593 DataT zeta, HCH;
594 DataT F0, F1, F2, F3, F4, F5, F6;
595 DataT K1, K2, K3, K4, K5, K6;
596
597 //FilterVi(sqrt(DataT(1.) + fMass2 * fLinearisation.qp * fLinearisation.qp) * kClightNsInv);
598 //return;
599
600 DataT vi0 = sqrt(DataT(1.) + fMass2 * fLinearisation.qp * fLinearisation.qp) * kClightNsInv;
601
602 DataT h =
603 fMass2 * fLinearisation.qp / sqrt(DataT(1.) + fMass2 * fLinearisation.qp * fLinearisation.qp) * kClightNsInv;
604
605 zeta = vi0 + h * (fTr.Qp() - fLinearisation.qp) - fTr.Vi();
606
607 fTr.Vi() = vi0;
608
609 // H = (0,0,0,0, h,0, -1)
610
611 // F = CH'
612
613 F0 = h * fTr.C40() - fTr.C60();
614 F1 = h * fTr.C41() - fTr.C61();
615 F2 = h * fTr.C42() - fTr.C62();
616 F3 = h * fTr.C43() - fTr.C63();
617 F4 = h * fTr.C44() - fTr.C64();
618 F5 = h * fTr.C54() - fTr.C65();
619 F6 = h * fTr.C64() - fTr.C66();
620
621 HCH = F4 * h - F6;
622
623 DataT wi = kf::utils::iif(fMask, DataT(1.) / HCH, DataT(0.));
624 DataT zetawi = kf::utils::iif(fMask, zeta / HCH, DataT(0.));
625 fTr.ChiSqTime() += kf::utils::iif(fMask, zeta * zeta * wi, DataT(0.));
626 fTr.NdfTime() += kf::utils::iif(fMask, DataT(1.), DataT(0.));
627
628 K1 = F1 * wi;
629 K2 = F2 * wi;
630 K3 = F3 * wi;
631 K4 = F4 * wi;
632 K5 = F5 * wi;
633 K6 = F6 * wi;
634
635 fTr.X() -= F0 * zetawi;
636 fTr.Y() -= F1 * zetawi;
637 fTr.Tx() -= F2 * zetawi;
638 fTr.Ty() -= F3 * zetawi;
639 fTr.Qp() -= F4 * zetawi;
640 fTr.Time() -= F5 * zetawi;
641 fTr.Vi() -= F6 * zetawi;
642
643 fTr.C00() -= F0 * F0 * wi;
644
645 fTr.C10() -= K1 * F0;
646 fTr.C11() -= K1 * F1;
647
648 fTr.C20() -= K2 * F0;
649 fTr.C21() -= K2 * F1;
650 fTr.C22() -= K2 * F2;
651
652 fTr.C30() -= K3 * F0;
653 fTr.C31() -= K3 * F1;
654 fTr.C32() -= K3 * F2;
655 fTr.C33() -= K3 * F3;
656
657 fTr.C40() -= K4 * F0;
658 fTr.C41() -= K4 * F1;
659 fTr.C42() -= K4 * F2;
660 fTr.C43() -= K4 * F3;
661 fTr.C44() -= K4 * F4;
662
663 fTr.C50() -= K5 * F0;
664 fTr.C51() -= K5 * F1;
665 fTr.C52() -= K5 * F2;
666 fTr.C53() -= K5 * F3;
667 fTr.C54() -= K5 * F4;
668 fTr.C55() -= K5 * F5;
669
670 fTr.C60() -= K6 * F0;
671 fTr.C61() -= K6 * F1;
672 fTr.C62() -= K6 * F2;
673 fTr.C63() -= K6 * F3;
674 fTr.C64() -= K6 * F4;
675 fTr.C65() -= K6 * F5;
676 fTr.C66() -= K6 * F6;
677
678 // fTr.Vi()( fTr.Vi() < DataT(TrackParamV::kClightNsInv) ) = DataT(TrackParamV::kClightNsInv);
679}
680
681template<typename DataT, class Settings>
683{
684 // set inverse velocity to vi
685
686 DataT zeta, HCH;
687 DataT F0, F1, F2, F3, F4, F5, F6;
688 DataT K1, K2, K3, K4, K5; //, K6;
689
690 zeta = fTr.Vi() - vi;
691
692 // H = (0,0,0,0, 0, 0, 1)
693
694 // F = CH'
695
696 F0 = fTr.C60();
697 F1 = fTr.C61();
698 F2 = fTr.C62();
699 F3 = fTr.C63();
700 F4 = fTr.C64();
701 F5 = fTr.C65();
702 F6 = fTr.C66();
703
704 HCH = F6;
705
706 DataT wi = kf::utils::iif(fMask, DataT(1.) / HCH, DataT(0.));
707 DataT zetawi = kf::utils::iif(fMask, zeta / HCH, DataT(0.));
708 fTr.ChiSqTime() += kf::utils::iif(fMask, zeta * zeta * wi, DataT(0.));
709 fTr.NdfTime() += kf::utils::iif(fMask, DataT(1.), DataT(0.));
710
711 K1 = F1 * wi;
712 K2 = F2 * wi;
713 K3 = F3 * wi;
714 K4 = F4 * wi;
715 K5 = F5 * wi;
716 // K6 = F6 * wi;
717
718 fTr.X() -= F0 * zetawi;
719 fTr.Y() -= F1 * zetawi;
720 fTr.Tx() -= F2 * zetawi;
721 fTr.Ty() -= F3 * zetawi;
722 fTr.Qp() -= F4 * zetawi;
723 fTr.Time() -= F5 * zetawi;
724 // fTr.Vi() -= F6 * zetawi;
725 fTr.Vi() = vi;
726
727 fTr.C00() -= F0 * F0 * wi;
728
729 fTr.C10() -= K1 * F0;
730 fTr.C11() -= K1 * F1;
731
732 fTr.C20() -= K2 * F0;
733 fTr.C21() -= K2 * F1;
734 fTr.C22() -= K2 * F2;
735
736 fTr.C30() -= K3 * F0;
737 fTr.C31() -= K3 * F1;
738 fTr.C32() -= K3 * F2;
739 fTr.C33() -= K3 * F3;
740
741 fTr.C40() -= K4 * F0;
742 fTr.C41() -= K4 * F1;
743 fTr.C42() -= K4 * F2;
744 fTr.C43() -= K4 * F3;
745 fTr.C44() -= K4 * F4;
746
747 fTr.C50() -= K5 * F0;
748 fTr.C51() -= K5 * F1;
749 fTr.C52() -= K5 * F2;
750 fTr.C53() -= K5 * F3;
751 fTr.C54() -= K5 * F4;
752 fTr.C55() -= K5 * F5;
753
754 //fTr.C60() -= K6 * F0;
755 //fTr.C61() -= K6 * F1;
756 //fTr.C62() -= K6 * F2;
757 //fTr.C63() -= K6 * F3;
758 //fTr.C64() -= K6 * F4;
759 //fTr.C65() -= K6 * F5;
760 //fTr.C66() -= K6 * F6;
761 fTr.C60() = DataT(0.);
762 fTr.C61() = DataT(0.);
763 fTr.C62() = DataT(0.);
764 fTr.C63() = DataT(0.);
765 fTr.C64() = DataT(0.);
766 fTr.C65() = DataT(0.);
767 fTr.C66() = DataT(1.e-8); // just for a case..
768}
769
770
771template<typename DataT, class Settings>
773{
774 // extrapolates track to zOut
775 // - makes one step only
776 // implementation of the Runge-Kutta method without optimization
777 //
778
779 //
780 // Forth-order Runge-Kutta method for solution of the equation
781 // of motion of a particle with parameter qp = Q /P
782 // in the magnetic field B()
783 //
784 // ( x ) tx
785 // ( y ) ty
786 // ( tx) c_light * qp * L * ( tx*ty * Bx - (1+tx*tx) * By + ty * Bz )
787 // d ( ty) / dz = c_light * qp * L * ( (1+ty*ty) * Bx - tx*ty * By - tx * Bz ) ,
788 // ( qp) 0.
789 // ( t ) L * vi
790 // ( vi) 0.
791 //
792 // where L = sqrt ( 1 + tx*tx + ty*ty ) .
793 // c_light = 0.000299792458 [(GeV/c)/kG/cm]
794 //
795 // In the following for RK step :
796 // r[7] = {x, y, tx, ty, qp, t, vi}
797 // dr(z)/dz = f(z,r)
798 //
799 //
800 //========================================================================
801 //
802 // NIM A395 (1997) 169-184; NIM A426 (1999) 268-282.
803 //
804 // the routine is based on LHC(b) utility code
805 //
806 //========================================================================
807
808
809 cnst c_light = DataT(1.e-5 * kf::defs::SpeedOfLight<double>);
810
811 //----------------------------------------------------------------
812
813 cnst zMasked = kf::utils::iif(fMask, zOut, fTr.GetZ());
814
815 cnst h = (zMasked - fTr.GetZ());
816
817 const std::array<DataT, 5> stepDz = {0., 0., h * DataT(0.5), h * DataT(0.5), h};
818
819 constexpr int N = kNactiveParams;
820
821 DataT f[5][N] = {{DataT(0.)}}; // ( d*/dz ) [step]
822 DataT F[5][N][N] = {{{DataT(0.)}}}; // ( d *new [step] / d *old )
823
824 // Runge-Kutta steps
825 //
826
827 DataT r0[7] = {fTr.X(), fTr.Y(), fTr.Tx(), fTr.Ty(), fTr.Qp(), fTr.Time(), fTr.Vi()};
828
829 if constexpr (std::is_same_v<Linearization_t, LinearizationFull<DataT>>) {
830 r0[0] = fLinearisation.x;
831 r0[1] = fLinearisation.y;
832 r0[2] = fLinearisation.tx;
833 r0[3] = fLinearisation.ty;
834 r0[4] = fLinearisation.qp;
835 r0[5] = fLinearisation.time;
836 r0[6] = fLinearisation.vi;
837 }
838 else if constexpr (std::is_same_v<Linearization_t, LinearizationQp<DataT>>) {
839 r0[4] = fLinearisation.qp;
840 }
841 else {
842 LOG(fatal) << "TrackKalmanFilter<DataT,Settings>::ExtrapolateStep: Unknown Linearization_t type";
843 }
844
845
846 for (int step = 1; step <= 4; ++step) {
847
848 DataT rstep[N] = {DataT(0.)};
849 for (int i = 0; i < N; ++i) {
850 rstep[i] = r0[i] + stepDz[step] * f[step - 1][i];
851 }
852 DataT z = fTr.GetZ() + stepDz[step];
853
854 kf::FieldValue B = Field.Get(rstep[0], rstep[1], z);
855 DataT Bx = B.GetBx();
856 DataT By = B.GetBy();
857 DataT Bz = B.GetBz();
858 // NOTE: SZh 13.08.2024: The rstep[0] and rstep[1] make no effect on the B value, if Field is an approximation
859
860 DataT tx = rstep[2];
861 DataT ty = rstep[3];
862 DataT tx2 = tx * tx;
863 DataT ty2 = ty * ty;
864 DataT txty = tx * ty;
865 DataT L2 = DataT(1.) + tx2 + ty2;
866 DataT L2i = DataT(1.) / L2;
867 DataT L = sqrt(L2);
868 DataT cL = c_light * L;
869 DataT cLqp0 = cL * r0[4];
870
871 f[step][0] = tx;
872 F[step][0][2] = 1.;
873
874 f[step][1] = ty;
875 F[step][1][3] = 1.;
876
877 DataT f2tmp = txty * Bx - (DataT(1.) + tx2) * By + ty * Bz;
878 f[step][2] = cLqp0 * f2tmp;
879
880 F[step][2][2] = cLqp0 * (tx * f2tmp * L2i + ty * Bx - DataT(2.) * tx * By);
881 F[step][2][3] = cLqp0 * (ty * f2tmp * L2i + tx * Bx + Bz);
882 F[step][2][4] = cL * f2tmp;
883
884 DataT f3tmp = -txty * By - tx * Bz + (DataT(1.) + ty2) * Bx;
885 f[step][3] = cLqp0 * f3tmp;
886 F[step][3][2] = cLqp0 * (tx * f3tmp * L2i - ty * By - Bz);
887 F[step][3][3] = cLqp0 * (ty * f3tmp * L2i + DataT(2.) * ty * Bx - tx * By);
888 F[step][3][4] = cL * f3tmp;
889
890 f[step][4] = 0.;
891
892 if constexpr (Settings::kDoFitTime) {
893 DataT vi = rstep[6];
894 f[step][5] = vi * L;
895 F[step][5][2] = vi * tx / L;
896 F[step][5][3] = vi * ty / L;
897 F[step][5][4] = 0.;
898 F[step][5][5] = 0.;
899 F[step][5][6] = L;
900 f[step][6] = 0.;
901 }
902
903 } // end of Runge-Kutta step
904
905 cnst stepW[5] = {0., h / DataT(6.), h / DataT(3.), h / DataT(3.), h / DataT(6.)};
906
907 DataT R[N][N] = {{DataT(0.)}}; // Jacobian of the extrapolation
908 {
909 for (int i = 0; i < N; ++i) {
910 R[i][i] = 1.;
911 }
912 DataT k[5][N][N] = {{{DataT(0.)}}};
913 for (int step = 1; step <= 4; ++step) {
914 for (int i = 0; i < N; i++) {
915 for (int j = 0; j < N; j++) {
916 k[step][i][j] = F[step][i][j];
917 for (int m = 0; m < N; m++) {
918 k[step][i][j] += stepDz[step] * F[step][i][m] * k[step - 1][m][j];
919 }
920 R[i][j] += stepW[step] * k[step][i][j];
921 }
922 }
923 }
924 } // end of Jacobian calculation
925
926
927 // difference of parameters of fTr and T0
928 DataT dPar[7] = {fTr.X() - r0[0], //
929 fTr.Y() - r0[1], //
930 fTr.Tx() - r0[2], //
931 fTr.Ty() - r0[3], //
932 fTr.Qp() - r0[4], //
933 fTr.Time() - r0[5], //
934 fTr.Vi() - r0[6]};
935
936 // extrapolated linarization
937
938 for (int i = 0; i < N; i++) {
939 for (int step = 1; step <= 4; step++) {
940 r0[i] += stepW[step] * f[step][i];
941 }
942 }
943
944 // extrapolated parameters
945
946 DataT r[N] = {DataT(0.)};
947 for (int i = 0; i < N; i++) {
948 r[i] = r0[i];
949 }
950
951 for (int i = 0; i < N; i++) {
952 for (int j = 0; j < N; j++) {
953 r[i] += R[i][j] * dPar[j];
954 }
955 }
956
957
958 // update the linearization
959 if constexpr (std::is_same_v<Linearization_t, LinearizationFull<DataT>>) {
960 fLinearisation.x = r0[0];
961 fLinearisation.y = r0[1];
962 fLinearisation.tx = r0[2];
963 fLinearisation.ty = r0[3];
964 fLinearisation.qp = r0[4];
965 fLinearisation.time = r0[5];
966 fLinearisation.vi = r0[6];
967 }
968 else if constexpr (std::is_same_v<Linearization_t, LinearizationQp<DataT>>) {
969 fLinearisation.qp = r0[4];
970 }
971
972 // update parameters
973
974 fTr.X() = r[0];
975 fTr.Y() = r[1];
976 fTr.Tx() = r[2];
977 fTr.Ty() = r[3];
978 fTr.Qp() = r[4];
979 if constexpr (Settings::kDoFitTime) {
980 fTr.Time() = r[5];
981 fTr.Vi() = r[6];
982 }
983 fTr.Z() = zMasked;
984
985 // covariance matrix transport
986
987 DataT C[N][N];
988 for (int i = 0, ii = 0; i < N; i++) {
989 for (int j = 0; j <= i; j++, ii++) {
990 C[i][j] = fTr.GetCovMatrix()[ii];
991 C[j][i] = C[i][j];
992 }
993 }
994
995 DataT RC[N][N];
996 for (int i = 0; i < N; i++) {
997 for (int j = 0; j < N; j++) {
998 RC[i][j] = 0.;
999 for (int m = 0; m < N; m++) {
1000 RC[i][j] += R[i][m] * C[m][j];
1001 }
1002 }
1003 }
1004
1005 for (int i = 0, ii = 0; i < N; i++) {
1006 for (int j = 0; j <= i; j++, ii++) {
1007 DataT Cij = 0.;
1008 for (int m = 0; m < N; m++) {
1009 Cij += RC[i][m] * R[j][m];
1010 }
1011 fTr.CovMatrix()[ii] = Cij;
1012 }
1013 }
1014
1016}
1017
1018
1019template<typename DataT, class Settings>
1021{
1022 // extrapolate the track to Z = ze assuming no field
1023
1024 // x += dz * tx
1025 // y += dz * ty
1026 // t += dz * sqrt ( 1 + tx*tx + ty*ty ) * vi
1027
1028 auto& t = fTr; // use reference to shorten the text
1029
1030 cnst zMasked = kf::utils::iif(fMask, ze, fTr.GetZ());
1031
1032 cnst dz = (zMasked - fTr.GetZ());
1033
1034 DataT x = t.X();
1035 DataT y = t.Y();
1036 DataT tx = t.Tx();
1037 DataT ty = t.Ty();
1038 DataT time = t.Time();
1039 DataT vi = t.Vi();
1040
1041 if constexpr (std::is_same<Linearization_t, LinearizationFull<DataT>>::value) {
1042 tx = fLinearisation.tx;
1043 ty = fLinearisation.ty;
1044 vi = fLinearisation.vi;
1045 }
1046
1047
1048 // ( 1 0 dz 0 0 0 0 )
1049 // ( 0 1 0 dz 0 0 0 )
1050 // ( 0 0 1 0 0 0 0 )
1051 // J = ( 0 0 0 1 0 0 0 )
1052 // ( 0 0 0 0 1 0 0 )
1053 // ( 0 0 j52 j53 0 1 j56 )
1054 // ( 0 0 0 0 0 0 1 )
1055
1056 // transport parameters
1057
1058 t.X() = t.X() + t.Tx() * dz;
1059 t.Y() = t.Y() + t.Ty() * dz;
1060 t.Z() = zMasked;
1061
1062 if constexpr (std::is_same<Linearization_t, LinearizationFull<DataT>>::value) {
1063 fLinearisation.x = x + tx * dz;
1064 fLinearisation.y = y + ty * dz;
1065 }
1066
1067 // transport covariance matrix
1068
1069 // JC = J * C
1070
1071 if constexpr (Settings::kDoFitTime) {
1072
1073 DataT L = sqrt(DataT(1.) + tx * tx + ty * ty);
1074
1075 if constexpr (std::is_same<Linearization_t, LinearizationFull<DataT>>::value) {
1076 fLinearisation.time = time + L * vi * dz;
1077 }
1078
1079 DataT j52 = dz * tx * vi / L;
1080 DataT j53 = dz * ty * vi / L;
1081 DataT j56 = dz * L;
1082
1083 DataT jc50 = t.C50() + j52 * t.C20() + j53 * t.C30() + j56 * t.C60();
1084 DataT jc51 = t.C51() + j52 * t.C21() + j53 * t.C31() + j56 * t.C61();
1085 DataT jc52 = t.C52() + j52 * t.C22() + j53 * t.C32() + j56 * t.C62();
1086 DataT jc53 = t.C53() + j52 * t.C23() + j53 * t.C33() + j56 * t.C63();
1087 DataT jc54 = t.C54() + j52 * t.C24() + j53 * t.C34() + j56 * t.C64();
1088 DataT jc55 = t.C55() + j52 * t.C25() + j53 * t.C35() + j56 * t.C65();
1089 DataT jc56 = t.C56() + j52 * t.C26() + j53 * t.C36() + j56 * t.C66();
1090
1091 DataT dtx = t.Tx() - tx;
1092 DataT dty = t.Ty() - ty;
1093 DataT dvi = t.Vi() - vi;
1094
1095 t.Time() = t.Time() + L * vi * dz + j52 * dtx + j53 * dty + j56 * dvi;
1096
1097 t.C50() = jc50 + jc52 * dz;
1098 t.C60() = t.C60() + t.C62() * dz;
1099
1100 t.C51() = jc51 + jc53 * dz;
1101 t.C61() = t.C61() + t.C63() * dz;
1102
1103 t.C52() = jc52;
1104 t.C53() = jc53;
1105 t.C54() = jc54;
1106 t.C55() = jc55 + jc52 * j52 + jc53 * j53 + jc56 * j56;
1107 t.C65() = t.C65() + t.C62() * j52 + t.C63() * j53 + t.C66() * j56;
1108 }
1109
1110 DataT jc00 = t.C00() + dz * t.C20();
1111 DataT jc02 = t.C02() + dz * t.C22();
1112
1113 DataT jc10 = t.C10() + dz * t.C30();
1114 DataT jc11 = t.C11() + dz * t.C31();
1115 DataT jc12 = t.C12() + dz * t.C32();
1116 DataT jc13 = t.C13() + dz * t.C33();
1117
1118 // transpose J
1119 //
1120 // ( 1 0 0 0 0 0 0 )
1121 // ( 0 1 0 0 0 0 0 )
1122 // ( dz 0 1 0 0 j52 0 )
1123 // J' = ( 0 dz 0 1 0 j53 0 )
1124 // ( 0 0 0 0 1 0 0 )
1125 // ( 0 0 0 0 0 1 0 )
1126 // ( 0 0 0 0 0 j56 1 )
1127
1128 // C = JC * J'
1129
1130 t.C00() = jc00 + jc02 * dz;
1131 t.C10() = jc10 + jc12 * dz;
1132 t.C20() = t.C20() + t.C22() * dz;
1133 t.C30() = t.C30() + t.C32() * dz;
1134 t.C40() = t.C40() + t.C42() * dz;
1135
1136 t.C11() = jc11 + jc13 * dz;
1137 t.C21() = t.C21() + t.C23() * dz;
1138 t.C31() = t.C31() + t.C33() * dz;
1139 t.C41() = t.C41() + t.C43() * dz;
1140}
1141
1142
1143template<typename DataT, class Settings>
1145{
1146 // extrapolate the track assuming fLinearisation.qp == 0
1147 // TODO: write special simplified procedure
1148 //
1149 auto qp0 = fLinearisation.qp;
1150 fLinearisation.qp = DataT(0.);
1151 Extrapolate(z, F);
1152 fLinearisation.qp = qp0;
1153}
1154
1155
1156template<typename DataT, class Settings>
1157void TrackKalmanFilter<DataT, Settings>::MultipleScattering(DataT radThick, DataT tx, DataT ty, DataT qp)
1158{
1159 // 1974 - Highland (PDG) correction for multiple scattering
1160 // In the formula there is a replacement:
1161 // log(X/X0) == log( RadThick * sqrt(1+h) ) -> log(RadThick) + 0.5 * log(1+h);
1162 // then we use an approximation:
1163 // log(1+h) -> h - h^2/2 + h^3/3 - h^4/4
1164
1165 DataT txtx = tx * tx;
1166 DataT tyty = ty * ty;
1167 DataT txtx1 = DataT(1.) + txtx;
1168 DataT tyty1 = DataT(1.) + tyty;
1169 DataT t = sqrt(txtx1 + tyty);
1170 // DataT qpt = qp * t;
1171
1172 DataT lg = DataT(.0136) * (DataT(1.) + DataT(0.038) * log(radThick * t));
1173 lg = kf::utils::iif(lg > DataT(0.), lg, DataT(0.));
1174
1175 DataT s0 = lg * qp * t;
1176 DataT a = (DataT(1.) + fMass2 * qp * qp) * s0 * s0 * t * radThick;
1177
1178 // Approximate formula
1179
1180 // DataT h = txtx + tyty;
1181 // DataT h2 = h * h;
1182 // cnst c1 = 0.0136f, c2 = c1 * 0.038f, c3 = c2 * 0.5f, c4 = -c3 / 2.0f, c5 = c3 / 3.0f, c6 = -c3 / 4.0f;
1183 // DataT s0 = (c1 + c2 * log(radThick) + c3 * h + h2 * (c4 + c5 * h + c6 * h2)) * qp0t;
1184 // DataT a = ( (kONE+mass2*qp0*qp0t)*radThick*s0*s0 );
1185 // DataT a = ((t + fMass2 * qp0 * qp0t) * radThick * s0 * s0);
1186
1187 fTr.C22() += kf::utils::iif(fMask, txtx1 * a, DataT(0.));
1188 fTr.C32() += kf::utils::iif(fMask, tx * ty * a, DataT(0.));
1189 fTr.C33() += kf::utils::iif(fMask, tyty1 * a, DataT(0.));
1190}
1191
1192template<typename DataT, class Settings>
1194 bool fDownstream)
1195{
1196 cnst kONE = 1.;
1197
1198 DataT tx = fTr.Tx();
1199 DataT ty = fTr.Ty();
1200 DataT txtx = tx * tx;
1201 DataT tyty = ty * ty;
1202 DataT txtx1 = txtx + kONE;
1203 DataT h = txtx + tyty;
1204 DataT t = sqrt(txtx1 + tyty);
1205 DataT h2 = h * h;
1206 DataT qp0t = fLinearisation.qp * t;
1207
1208 cnst c1(0.0136), c2 = c1 * DataT(0.038), c3 = c2 * DataT(0.5), c4 = -c3 / DataT(2.0), c5 = c3 / DataT(3.0),
1209 c6 = -c3 / DataT(4.0);
1210
1211 DataT s0 = (c1 + c2 * log(radThick) + c3 * h + h2 * (c4 + c5 * h + c6 * h2)) * qp0t;
1212 //DataT a = ( (ONE+mass2*qp0*qp0t)*radThick*s0*s0 );
1213 DataT a = ((t + fMass2 * fLinearisation.qp * qp0t) * radThick * s0 * s0);
1214
1215 DataT D = (fDownstream) ? DataT(1.) : DataT(-1.);
1216 DataT T23 = (thickness * thickness) / DataT(3.0);
1217 DataT T2 = thickness / DataT(2.0);
1218
1219 fTr.C00() += kf::utils::iif(fMask, txtx1 * a * T23, DataT(0.));
1220 fTr.C10() += kf::utils::iif(fMask, tx * ty * a * T23, DataT(0.));
1221 fTr.C20() += kf::utils::iif(fMask, txtx1 * a * D * T2, DataT(0.));
1222 fTr.C30() += kf::utils::iif(fMask, tx * ty * a * D * T2, DataT(0.));
1223
1224 fTr.C11() += kf::utils::iif(fMask, (kONE + tyty) * a * T23, DataT(0.));
1225 fTr.C21() += kf::utils::iif(fMask, tx * ty * a * D * T2, DataT(0.));
1226 fTr.C31() += kf::utils::iif(fMask, (kONE + tyty) * a * D * T2, DataT(0.));
1227
1228 fTr.C22() += kf::utils::iif(fMask, txtx1 * a, DataT(0.));
1229 fTr.C32() += kf::utils::iif(fMask, tx * ty * a, DataT(0.));
1230 fTr.C33() += kf::utils::iif(fMask, (kONE + tyty) * a, DataT(0.));
1231}
1232
1233
1234template<typename DataT, class Settings>
1236{
1237 cnst qp2cut(1. / (10. * 10.)); // 10 GeV cut
1238 cnst qp02 = kf::utils::max(fLinearisation.qp * fLinearisation.qp, qp2cut);
1239 cnst p2 = DataT(1.) / qp02;
1240 cnst E2 = fMass2 + p2;
1241
1242 cnst bethe = ApproximateBetheBloch(p2 / fMass2);
1243
1244 DataT tr = sqrt(DataT(1.f) + fTr.Tx() * fTr.Tx() + fTr.Ty() * fTr.Ty());
1245
1246 DataT dE = bethe * radThick * tr * 2.33f * 9.34961f;
1247
1248 if (direction == FitDirection::kDownstream) dE = -dE;
1249
1250 cnst ECorrected = sqrt(E2) + dE;
1251 cnst E2Corrected = ECorrected * ECorrected;
1252
1253 DataT corr = sqrt(p2 / (E2Corrected - fMass2));
1254 DataTmask ok = (corr == corr) && fMask;
1255 corr = kf::utils::iif(ok, corr, DataT(1.));
1256
1257 fLinearisation.qp *= corr;
1258 fTr.Qp() *= corr;
1259 fTr.C40() *= corr;
1260 fTr.C41() *= corr;
1261 fTr.C42() *= corr;
1262 fTr.C43() *= corr;
1263 fTr.C44() *= corr * corr;
1264 fTr.C54() *= corr;
1265}
1266
1267
1268template<typename DataT, class Settings>
1270 DataTscal radLen, DataT radThick, FitDirection direction)
1271{
1272 cnst qp2cut(1. / (10. * 10.)); // 10 GeV cut
1273 cnst qp02 = kf::utils::max(fLinearisation.qp * fLinearisation.qp, qp2cut);
1274 cnst p2 = DataT(1.) / qp02;
1275 cnst E2 = fMass2 + p2;
1276
1277 DataT i;
1278 if (atomicZ < 13) {
1279 i = (12. * atomicZ + 7.) * 1.e-9;
1280 }
1281 else {
1282 i = (9.76 * atomicZ + 58.8 * std::pow(atomicZ, -0.19)) * 1.e-9;
1283 }
1284
1285 cnst bethe = ApproximateBetheBloch(p2 / fMass2, rho, 0.20, 3.00, i, atomicZ / atomicA);
1286
1287 DataT tr = sqrt(DataT(1.f) + fTr.Tx() * fTr.Tx() + fTr.Ty() * fTr.Ty());
1288
1289 DataT dE = bethe * radThick * tr * radLen * rho;
1290
1291 if (direction == FitDirection::kDownstream) dE = -dE;
1292
1293 cnst ECorrected = (sqrt(E2) + dE);
1294 cnst E2Corrected = ECorrected * ECorrected;
1295
1296 DataT corr = sqrt(p2 / (E2Corrected - fMass2));
1297 DataTmask ok = (corr == corr) && fMask;
1298 corr = kf::utils::iif(ok, corr, DataT(1.));
1299
1300 fLinearisation.qp *= corr;
1301 fTr.Qp() *= corr;
1302
1303 DataT P(sqrt(p2)); // GeV
1304
1305 DataT Z(atomicZ);
1306 DataT A(atomicA);
1307 DataT RHO(rho);
1308
1309 DataT STEP = radThick * tr * radLen;
1310 DataT EMASS(0.511 * 1e-3); // GeV
1311
1312 DataT BETA = P / ECorrected;
1313 DataT GAMMA = ECorrected / fMass;
1314
1315 // Calculate xi factor (KeV).
1316 DataT XI = (DataT(153.5) * Z * STEP * RHO) / (A * BETA * BETA);
1317
1318 // Maximum energy transfer to atomic electron (KeV).
1319 DataT ETA = BETA * GAMMA;
1320 DataT ETASQ = ETA * ETA;
1321 DataT RATIO = EMASS / fMass;
1322 DataT F1 = DataT(2.) * EMASS * ETASQ;
1323 DataT F2 = DataT(1.) + DataT(2.) * RATIO * GAMMA + RATIO * RATIO;
1324 DataT EMAX = DataT(1e6) * F1 / F2;
1325
1326 DataT DEDX2 = XI * EMAX * (DataT(1.) - (BETA * BETA / DataT(2.))) * DataT(1e-12);
1327
1328 DataT P2 = P * P;
1329 DataT SDEDX = (E2 * DEDX2) / (P2 * P2 * P2);
1330
1331 // T.fTr.C40() *= corr;
1332 // T.fTr.C41() *= corr;
1333 // T.fTr.C42() *= corr;
1334 // T.fTr.C43() *= corr;
1335 // T.fTr.C44() *= corr*corr;
1336 fTr.C44() += kf::utils::fabs(SDEDX);
1337}
1338
1339
1340template<typename DataT, class Settings>
1342{
1343 //
1344 // This is the parameterization of the Bethe-Bloch formula inspired by Geant.
1345 //
1346 // bg2 - (beta*gamma)^2
1347 // kp0 - density [g/cm^3]
1348 // kp1 - density effect first junction point
1349 // kp2 - density effect second junction point
1350 // kp3 - mean excitation energy [GeV]
1351 // kp4 - mean Z/A
1352 //
1353 // The default values for the kp* parameters are for silicon.
1354 // The returned value is in [GeV/(g/cm^2)].
1355 //
1356
1357 cnst kp0 = 2.33f;
1358 cnst kp1 = 0.20f;
1359 cnst kp2 = 3.00f;
1360 cnst kp3 = 173e-9f;
1361 cnst kp4 = 0.49848f;
1362
1363 constexpr DataTscal mK = 0.307075e-3f; // [GeV*cm^2/g]
1364 constexpr DataTscal _2me = 1.022e-3f; // [GeV/c^2]
1365 cnst rho = kp0;
1366 cnst x0 = kp1 * 2.303f;
1367 cnst x1 = kp2 * 2.303f;
1368 cnst mI = kp3;
1369 cnst mZA = kp4;
1370 cnst maxT = _2me * bg2; // neglecting the electron mass
1371
1372 //*** Density effect
1373 DataT d2(0.f);
1374 cnst x = 0.5f * log(bg2);
1375 cnst lhwI = log(28.816f * 1e-9f * sqrt(rho * mZA) / mI);
1376
1377 DataTmask init = x > x1;
1378 d2 = kf::utils::iif(init, lhwI + x - 0.5f, DataT(0.));
1379 cnst r = (x1 - x) / (x1 - x0);
1380 init = (x > x0) & (x1 > x);
1381 d2 = kf::utils::iif(init, lhwI + x - 0.5f + (0.5f - lhwI - x0) * r * r * r, d2);
1382
1383 return mK * mZA * (DataT(1.f) + bg2) / bg2
1384 * (0.5f * log(_2me * bg2 * maxT / (mI * mI)) - bg2 / (DataT(1.f) + bg2) - d2);
1385}
1386
1387template<typename DataT, class Settings>
1388DataT TrackKalmanFilter<DataT, Settings>::ApproximateBetheBloch(DataT bg2, DataT kp0, DataT kp1, DataT kp2, DataT kp3,
1389 DataT kp4)
1390{
1391 //
1392 // This is the parameterization of the Bethe-Bloch formula inspired by Geant.
1393 //
1394 // bg2 - (beta*gamma)^2
1395 // kp0 - density [g/cm^3]
1396 // kp1 - density effect first junction point
1397 // kp2 - density effect second junction point
1398 // kp3 - mean excitation energy [GeV]
1399 // kp4 - mean Z/A
1400 //
1401 // The default values for the kp* parameters are for silicon.
1402 // The returned value is in [GeV/(g/cm^2)].
1403 //
1404
1405 // cnst &kp0 = 2.33f;
1406 // cnst &kp1 = 0.20f;
1407 // cnst &kp2 = 3.00f;
1408 // cnst &kp3 = 173e-9f;
1409 // cnst &kp4 = 0.49848f;
1410
1411 constexpr DataTscal mK = 0.307075e-3f; // [GeV*cm^2/g]
1412 constexpr DataTscal _2me = 1.022e-3f; // [GeV/c^2]
1413 DataT rho = kp0;
1414 cnst x0 = kp1 * 2.303f;
1415 cnst x1 = kp2 * 2.303f;
1416 DataT mI = kp3;
1417 DataT mZA = kp4;
1418 cnst maxT = _2me * bg2; // neglecting the electron mass
1419
1420 //*** Density effect
1421 DataT d2(0.f);
1422 cnst x = 0.5f * log(bg2);
1423 cnst lhwI = log(28.816f * 1e-9f * sqrt(rho * mZA) / mI);
1424
1425 DataTmask init = x > x1;
1426 d2 = kf::utils::iif(init, lhwI + x - 0.5f, DataT(0.));
1427 cnst r = (x1 - x) / (x1 - x0);
1428 init = (x > x0) & (x1 > x);
1429 d2 = kf::utils::iif(init, lhwI + x - 0.5f + (0.5f - lhwI - x0) * r * r * r, d2);
1430
1431 return mK * mZA * (DataT(1.f) + bg2) / bg2
1432 * (0.5f * log(_2me * bg2 * maxT / (mI * mI)) - bg2 / (DataT(1.f) + bg2) - d2);
1433}
1434
1435
1436template<typename DataT, class Settings>
1438 DataT C00, DataT C10, DataT C11)
1439{
1440
1441 DataT chi2x{0.};
1442
1443 { // filter X measurement
1444 DataT zeta = x - m.X();
1445
1446 // F = CH'
1447 DataT F0 = C00;
1448 DataT F1 = C10;
1449
1450 DataT HCH = F0;
1451
1452 DataT wi = DataT(1.) / (m.Dx2() + HCH);
1453 DataT zetawi = zeta * wi;
1454 chi2x = m.NdfX() * zeta * zetawi;
1455
1456 DataT K1 = F1 * wi;
1457
1458 x -= F0 * zetawi;
1459 y -= F1 * zetawi;
1460
1461 C00 -= F0 * F0 * wi;
1462 C10 -= K1 * F0;
1463 C11 -= K1 * F1;
1464 }
1465
1466 DataT chi2u{0.};
1467
1468 { // filter U measurement, we need only chi2 here
1469 DataT cosPhi = -m.Dxy() / m.Dx2();
1470 DataT u = cosPhi * m.X() + m.Y();
1471 DataT du2 = m.Dy2() + cosPhi * m.Dxy();
1472
1473 DataT zeta = cosPhi * x + y - u;
1474
1475 // F = CH'
1476 DataT F0 = cosPhi * C00 + C10;
1477 DataT F1 = cosPhi * C10 + C11;
1478
1479 DataT HCH = (F0 * cosPhi + F1);
1480
1481 chi2u += m.NdfY() * zeta * zeta / (du2 + HCH);
1482 }
1483
1484 return std::tuple<DataT, DataT>(chi2x, chi2u);
1485}
1486
1487
1488template<typename DataT, class Settings>
1489void TrackKalmanFilter<DataT, Settings>::GuessTrack(const DataT& trackZ, const DataT hitX[], const DataT hitY[],
1490 const DataT hitZ[], const DataT hitT[], const DataT By[],
1491 const DataTmask hitW[], const DataTmask hitWtime[], int NHits)
1492{
1493 // gives nice initial approximation for x,y,tx,ty - almost same as KF fit. qp - is shifted by 4%, resid_ual - ~3.5% (KF fit resid_ual - 1%).
1494
1495 // What are the units of c_light?
1496 const DataT c_light(0.000299792458), c_light_i(DataT(1.) / c_light);
1497
1498 DataT A0 = 0., A1 = 0., A2 = 0., A3 = 0., A4 = 0., A5 = 0.;
1499 DataT a0 = 0., a1 = 0., a2 = 0.;
1500 DataT b0 = 0., b1 = 0., b2 = 0.;
1501
1502 DataT time = 0.;
1503 DataTmask isTimeSet = DataTmask(false);
1504
1505 DataT prevZ = 0.;
1506 DataT sy = 0., Sy = 0.; // field integrals
1507
1508 for (int i = 0; i < NHits; i++) {
1509
1510 DataT w = kf::utils::iif(hitW[i], DataT(1.), DataT(0.));
1511
1512 DataTmask setTime = (!isTimeSet) && hitWtime[i] && hitW[i];
1513 time = kf::utils::iif(setTime, hitT[i], time);
1514 isTimeSet = isTimeSet || setTime;
1515
1516 DataT x = hitX[i];
1517 DataT y = hitY[i];
1518 DataT z = hitZ[i] - trackZ;
1519
1520 {
1521 DataT dZ = z - prevZ;
1522 Sy += w * dZ * sy + DataT(0.5) * dZ * dZ * By[i];
1523 sy += w * dZ * By[i];
1524 prevZ = kf::utils::iif(hitW[i], z, prevZ);
1525 }
1526
1527 DataT S = Sy;
1528
1529 DataT wz = w * z;
1530 DataT wS = w * S;
1531
1532 A0 += w;
1533 A1 += wz;
1534 A2 += wz * z;
1535 A3 += wS;
1536 A4 += wS * z;
1537 A5 += wS * S;
1538
1539 a0 += w * x;
1540 a1 += wz * x;
1541 a2 += wS * x;
1542
1543 b0 += w * y;
1544 b1 += wz * y;
1545 b2 += wS * y;
1546 }
1547
1548 DataT m00 = A0;
1549 DataT m01 = A1;
1550 DataT m02 = A3;
1551
1552 DataT m11 = A2;
1553 DataT m12 = A4;
1554
1555 DataT m22 = A5;
1556
1557 DataT m21 = m12;
1558
1559 // { m00 m01 m02 } ( a0 )
1560 // { m01 m11 m12 } = x * ( a1 )
1561 // { m02 m21 m22 } ( a2 )
1562
1563 { // triangulation row 0
1564 m11 = m00 * m11 - m01 * m01;
1565 m12 = m00 * m12 - m01 * m02;
1566 a1 = m00 * a1 - m01 * a0;
1567
1568 m21 = m00 * m21 - m02 * m01;
1569 m22 = m00 * m22 - m02 * m02;
1570 a2 = m00 * a2 - m02 * a0;
1571 }
1572
1573 { // triangulation step row 1
1574 m22 = m11 * m22 - m21 * m12;
1575 a2 = m11 * a2 - m21 * a1;
1576 }
1577
1578 DataT L = 0.;
1579 { // diagonalization row 2
1580 L = kf::utils::iif((kf::utils::fabs(m22) > DataT(1.e-4)), a2 / m22, DataT(0.));
1581 a1 -= L * m12;
1582 a0 -= L * m02;
1583 }
1584
1585 { // diagonalization row 1
1586 fTr.Tx() = a1 / m11;
1587 a0 -= fTr.Tx() * m01;
1588 }
1589
1590 { // diagonalization row 0
1591 fTr.X() = a0 / m00;
1592 }
1593
1594 DataT txtx1 = DataT(1.) + fTr.Tx() * fTr.Tx();
1595 L = L / txtx1;
1596 DataT L1 = L * fTr.Tx();
1597
1598 A1 = A1 + A3 * L1;
1599 A2 = A2 + (A4 + A4 + A5 * L1) * L1;
1600 b1 += b2 * L1;
1601
1602 // { A0 A1 } = x * ( b0 )
1603 // { A1 A2 } ( b1 )
1604
1605 A2 = A0 * A2 - A1 * A1;
1606 b1 = A0 * b1 - A1 * b0;
1607
1608 fTr.Ty() = b1 / A2;
1609 fTr.Y() = (b0 - A1 * fTr.Ty()) / A0;
1610
1611 fTr.Qp() = -L * c_light_i / sqrt(txtx1 + fTr.Ty() * fTr.Ty());
1612 fTr.Time() = time;
1613 fTr.Z() = trackZ;
1615 fLinearisation.qp = fTr.Qp();
1616}
1617
Definition of the KfMeasurementXy class.
friend fvec sqrt(const fvec &a)
friend fvec log(const fvec &a)
Track fit utilities for the CA tracking based on the Kalman filter.
Some class A.
Some class B.
Some class C.
Generates beam ions for transport simulation.
Magnetic field region, corresponding to a hit triplet.
Magnetic flux density vector.
Magnetic field manager class.
Definition KfField.h:272
The class describes a 1D - measurement U in XY coordinate system.
void SetSinPhi(DataT sinPhi)
void SetCosPhi(DataT cosPhi)
The class describes a 2D - measurement (x, y) in XY coordinate system.
void FilterTime(DataT t, DataT dt2, const DataTmask &m)
filter the track with the time measurement
void EnergyLossCorrection(DataT radThick, FitDirection direction)
void ExtrapolateNoField(DataT z)
extrapolate the track to the given Z assuming no magnetic field
void FilterExtrapolatedXY(const kf::MeasurementXy< DataT > &m, const std::array< DataT, 5 > &Jx, const std::array< DataT, 5 > &Jy)
kf::TrackParam< DataT > fTr
track parameters
kf::utils::masktype< DataT > DataTmask
Linearization_t fLinearisation
linearization parameters
void Filter1d(const kf::MeasurementU< DataT > &m)
filter the track with the 1d measurement
void FilterExtrapolatedY(const kf::MeasurementXy< DataT > &m, const std::array< DataT, 5 > &Jy)
void Extrapolate(DataT z, const kf::FieldRegion< DataT > &F)
static DataT ApproximateBetheBloch(DataT bg2)
Approximate mean energy loss with Bethe-Bloch formula.
DataT fMass
particle mass (muon mass by default)
kf::utils::scaltype< DataT > DataTscal
void MultipleScattering(DataT radThick, DataT tx0, DataT ty0, DataT qp0)
apply multiple scattering correction to the track with the given Qp0
void FilterExtrapolatedYChi2(const kf::MeasurementXy< DataT > &mL, const std::array< DataT, 5 > &jL, const kf::MeasurementXy< DataT > &mM, DataT msM, const kf::MeasurementXy< DataT > &mR, const std::array< DataT, 5 > &jR)
static std::tuple< DataT, DataT > GetChi2XChi2U(kf::MeasurementXy< DataT > m, DataT x, DataT y, DataT C00, DataT C10, DataT C11)
git two chi^2 components of the track fit to measurement
void GuessTrack(const DataT &trackZ, const DataT hitX[], const DataT hitY[], const DataT hitZ[], const DataT hitT[], const DataT By[], const DataTmask hitW[], const DataTmask hitWtime[], int NHits)
fast guess of track parameterts based on its hits
void FilterXY(const kf::MeasurementXy< DataT > &m, bool skipUnmeasuredCoordinates=false)
filter the track with the XY measurement
void ExtrapolateInOneStep(DataT z, const kf::FieldRegion< DataT > &Field)
void MultipleScatteringInThickMaterial(DataT radThick, DataT thickness, bool fDownstream)
apply multiple scattering correction in thick material to the track
void MeasureVelocityWithQp()
measure the track velocity with the track Qp and the mass
void FilterVi(DataT vi)
filter the inverse speed
DataTmask fMask
mask of active elements in simd vectors
void ExtrapolateLineInField(DataT z_out, const kf::FieldRegion< DataT > &F)
extrapolate the track to the given Z using linearization at the straight line
void GetMeasurementModelAtZline(DataT zm, const kf::FieldRegion< DataT > &Field, std::array< DataT, 5 > &Jx, std::array< DataT, 5 > &Jy) const
extrapolate track as a line, return the extrapolated X, Y and the Jacobians
constexpr auto SpeedOfLightInv
Inverse speed of light [ns/cm].
Definition KfDefs.h:210
constexpr auto SpeedOfLight
Speed of light [cm/ns].
Definition KfDefs.h:207
T max(const T &a, const T &b)
Definition KfUtils.h:56
fvec iif(const fmask &m, const fvec &t, const fvec &f)
Definition KfUtils.h:29
fvec fabs(const fvec &v)
Definition KfUtils.h:30
@ N
Do not fit the time component.
Definition KfDefs.h:133