Project
Loading...
Searching...
No Matches
GPUTPCGMPropagator.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
14
15#include "GPUTPCGMPropagator.h"
17#include "GPUParam.h"
19#include "GPUO2DataTypes.h"
20#include "GPUParam.inc"
21#include "GPUTPCGMMergerTypes.h"
22#include "GPUDebugStreamer.h"
23#include "GPUTPCGMMerger.h"
24
25#if defined(GPUCA_GM_USE_FULL_FIELD)
26#include "AliTracker.h"
27#include "AliMagF.h"
28#endif
29
30using namespace o2::gpu;
31
32GPUd() void GPUTPCGMPropagator::GetBxByBzBase(float cosAlpha, float sinAlpha, float X, float Y, float Z, float B[3]) const
33{
34 // get global coordinates
35
36 float gx = getGlobalX(cosAlpha, sinAlpha, X, Y);
37 float gy = getGlobalY(cosAlpha, sinAlpha, X, Y);
38
39#if defined(GPUCA_GM_USE_FULL_FIELD)
40 const float kCLight = gpu_common_constants::kCLight;
41 double r[3] = {gx, gy, Z};
42 double bb[3];
43 AliTracker::GetBxByBz(r, bb);
44 bb[0] *= kCLight;
45 bb[1] *= kCLight;
46 bb[2] *= kCLight;
47/*
48 cout<<"AliTracker::GetBz()= "<<AliTracker::GetBz()<<endl;
49 cout<<"AliTracker::UniformField() "<<AliTracker::UniformField()<<endl;
50 AliMagF* fld = (AliMagF*)TGeoGlobalMagField::Instance()->GetField();
51 cout<<"Fast field = "<<(void*) fld->GetFastField()<<endl;
52 AliMagF::BMap_t type = fld->GetMapType() ;
53 cout<<"Field type: "<<type<<endl;
54 // fMapType==k2BMap_t
55*/
56#else
57 float bb[3];
58 switch (mFieldRegion) {
59 case ITS:
60 mField->GetFieldIts(gx, gy, Z, bb);
61 break;
62 case TRD:
63 mField->GetFieldTrd(gx, gy, Z, bb);
64 break;
65 case TPC:
66 default:
67 mField->GetField(gx, gy, Z, bb);
68 }
69
70#endif
71
72 // rotate field to local coordinates
73
74 B[0] = bb[0] * cosAlpha + bb[1] * sinAlpha;
75 B[1] = -bb[0] * sinAlpha + bb[1] * cosAlpha;
76 B[2] = bb[2];
77 /*if( mToyMCEvents ){ // special treatment for toy monte carlo
78 B[0] = 0;
79 B[1] = 0;
80 B[2] = mField->GetNominalBz();
81 }*/
82}
83
84GPUd() float GPUTPCGMPropagator::GetBzBase(float cosAlpha, float sinAlpha, float X, float Y, float Z) const
85{
86 if (mToyMCEvents) { // special treatment for toy monte carlo
87 float B[3];
88 GetBxByBzBase(cosAlpha, sinAlpha, X, Y, Z, B);
89 return B[2];
90 }
91
92 // get global coordinates
93
94 float gx = getGlobalX(cosAlpha, sinAlpha, X, Y);
95 float gy = getGlobalY(cosAlpha, sinAlpha, X, Y);
96
97#if defined(GPUCA_GM_USE_FULL_FIELD)
98 const float kCLight = gpu_common_constants::kCLight;
99 double r[3] = {gx, gy, Z};
100 double bb[3];
101 AliTracker::GetBxByBz(r, bb);
102 return bb[2] * kCLight;
103#else
104 switch (mFieldRegion) {
105 case ITS:
106 return mField->GetFieldItsBz(gx, gy, Z);
107 case TRD:
108 return mField->GetFieldTrdBz(gx, gy, Z);
109 case TPC:
110 default:
111 return mField->GetFieldBz(gx, gy, Z);
112 }
113#endif
114}
115
116GPUd() int32_t GPUTPCGMPropagator::RotateToAlpha(float newAlpha)
117{
118 //
119 // Rotate the track coordinate system in XY to the angle newAlpha
120 // return value is error code (0==no error)
121 //
122
123 float newCosAlpha, newSinAlpha;
124 CAMath::SinCos(newAlpha, newSinAlpha, newCosAlpha);
125
126 float cc = newCosAlpha * mCosAlpha + newSinAlpha * mSinAlpha; // CAMath::Cos(newAlpha - mAlpha);
127 float ss = newSinAlpha * mCosAlpha - newCosAlpha * mSinAlpha; // CAMath::Sin(newAlpha - mAlpha);
128
130
131 float x0 = mT0.X();
132 float y0 = mT0.Y();
133 float px0 = mT0.Px();
134 float py0 = mT0.Py();
135 // float pt0 = mT0.GetPt();
136
137 if (CAMath::Abs(mT->GetSinPhi()) >= mMaxSinPhi || CAMath::Abs(px0) < (1 - mMaxSinPhi)) {
138 return -1;
139 }
140
141 // rotate t0 track
142 float px1 = px0 * cc + py0 * ss;
143 float py1 = -px0 * ss + py0 * cc;
144
145 {
146 t0.X() = x0 * cc + y0 * ss;
147 t0.Y() = -x0 * ss + y0 * cc;
148 t0.Px() = px1;
149 t0.Py() = py1;
150 t0.UpdateValues();
151 }
152
153 if (CAMath::Abs(py1) > mMaxSinPhi * mT0.GetPt() || CAMath::Abs(px1) < (1 - mMaxSinPhi)) {
154 return -1;
155 }
156
157 // calculate X of rotated track:
158 float trackX = x0 * cc + ss * mT->Y();
159
160 // transport t0 to trackX
161 float dLp = 0;
162 float Bz;
163 if (mPropagateBzOnly) {
164 Bz = GetBzBase(newCosAlpha, newSinAlpha, t0.X(), t0.Y(), t0.Z());
165 if (t0.PropagateToXBzLight(trackX, Bz, dLp)) {
166 return -1;
167 }
168 } else {
169 float B[3];
170 GetBxByBz(newAlpha, t0.X(), t0.Y(), t0.Z(), B);
171 Bz = B[2];
172 if (t0.PropagateToXBxByBz(trackX, B[0], B[1], B[2], dLp)) {
173 return -1;
174 }
175 }
176
177 if (CAMath::Abs(t0.SinPhi()) >= mMaxSinPhi) {
178 return -1;
179 }
180
181 // now t0 is rotated and propagated, all checks are passed
182
183 // Rotate track using mT0 for linearisation. After rotation X is not fixed, but has a covariance
184
185 // Y Z Sin DzDs q/p
186 // Jacobian J0 = { { j0, 0, 0, 0, 0 }, // Y
187 // { 0, 1, 0, 0, 0 }, // Z
188 // { 0, 0, j1, 0, 0 }, // SinPhi
189 // { 0, 0, 0, 1, 0 }, // DzDs
190 // { 0, 0, 0, 0, 1 }, // q/p
191 // { j2, 0, 0, 0, 0 } }// X (rotated )
192
193 float j0 = cc;
194 float j1 = px1 / px0;
195 float j2 = ss;
196 // float dy = mT->Y() - y0;
197 // float ds = mT->SinPhi() - mT0.SinPhi();
198
199 mT->X() = trackX; // == x0*cc + ss*mT->Y() == t0.X() + j0*dy;
200 mT->Y() = -x0 * ss + cc * mT->Y(); //== t0.Y() + j0*dy;
201 // mT->SinPhi() = py1/pt0 + j1*ds; // == t0.SinPhi() + j1*ds; // use py1, since t0.SinPhi can have different sign
202 mT->SinPhi() = -CAMath::Sqrt(1.f - mT->SinPhi() * mT->SinPhi()) * ss + mT->SinPhi() * cc;
203
204 // Rotate cov. matrix Cr = J0 x C x J0T. Cr has one more row+column for X:
205 float* c = mT->Cov();
206
207 float c15 = c[0] * j0 * j2;
208 float c16 = c[1] * j2;
209 float c17 = c[3] * j1 * j2;
210 float c18 = c[6] * j2;
211 float c19 = c[10] * j2;
212 float c20 = c[0] * j2 * j2;
213
214 c[0] *= j0 * j0;
215 c[3] *= j0;
216 c[10] *= j0;
217
218 c[3] *= j1;
219 c[5] *= j1 * j1;
220 c[12] *= j1;
221
222 if (!mFitInProjections && mT->NDF() > 0) {
223 c[1] *= j0;
224 c[6] *= j0;
225 c[4] *= j1;
226 c[8] *= j1;
227 }
228
229 if (t0.SetDirectionAlongX()) { // change direction if Px < 0
230 mT->SinPhi() = -mT->SinPhi();
231 mT->DzDs() = -mT->DzDs();
232 mT->QPt() = -mT->QPt();
233 c[3] = -c[3]; // covariances with SinPhi
234 c[4] = -c[4];
235 c17 = -c17;
236 c[6] = -c[6]; // covariances with DzDs
237 c[7] = -c[7];
238 c18 = -c18;
239 c[10] = -c[10]; // covariances with QPt
240 c[11] = -c[11];
241 c19 = -c19;
242 }
243
244 // Now fix the X coordinate: so to say, transport track T to fixed X = mT->X().
245 // only covariance changes. Use rotated and transported t0 for linearisation
246 float j3 = -t0.Py() / t0.Px();
247 float j4 = -t0.Pz() / t0.Px();
248 float j5 = t0.QPt() * Bz;
249
250 // Y Z Sin DzDs q/p X
251 // Jacobian J1 = { { 1, 0, 0, 0, 0, j3 }, // Y
252 // { 0, 1, 0, 0, 0, j4 }, // Z
253 // { 0, 0, 1, 0, 0, j5 }, // SinPhi
254 // { 0, 0, 0, 1, 0, 0 }, // DzDs
255 // { 0, 0, 0, 0, 1, 0 } }; // q/p
256
257 float h15 = c15 + c20 * j3;
258 float h16 = c16 + c20 * j4;
259 float h17 = c17 + c20 * j5;
260
261 c[0] += j3 * (c15 + h15);
262
263 c[2] += j4 * (c16 + h16);
264
265 c[3] += c17 * j3 + h15 * j5;
266 c[5] += j5 * (c17 + h17);
267
268 c[7] += c18 * j4;
269 // c[ 9] = c[ 9];
270
271 c[10] += c19 * j3;
272 c[12] += c19 * j5;
273 // c[14] = c[14];
274
275 if (!mFitInProjections && mT->NDF() > 0) {
276 c[1] += c16 * j3 + h15 * j4;
277 c[4] += c17 * j4 + h16 * j5;
278 c[6] += c18 * j3;
279 c[8] += c18 * j5;
280 c[11] += c19 * j4;
281 // c[13] = c[13];
282 }
283
284 mAlpha = newAlpha;
285 mCosAlpha = newCosAlpha;
286 mSinAlpha = newSinAlpha;
287 mT0 = t0;
288
289 return 0;
290}
291
292GPUd() int32_t GPUTPCGMPropagator::PropagateToXAlpha(float posX, float posAlpha, bool inFlyDirection)
293{
294 if (mPropagateBzOnly) {
295 return PropagateToXAlphaBz(posX, posAlpha, inFlyDirection);
296 }
297 if (CAMath::Abs(posAlpha - mAlpha) > 1.e-4f) {
298 if (RotateToAlpha(posAlpha) != 0) {
299 return -2;
300 }
301 }
302 if (CAMath::Abs(posX - mT->X()) < 1.e-7f) {
303 mT->SetX(posX);
304 return 0;
305 }
306
307 float B[3];
308 GetBxByBz(mT0.X(), mT0.Y(), mT0.Z(), B);
309
310 // propagate mT0 to t0e
311
313 float dLp = 0;
314 if (t0e.PropagateToXBxByBz(posX, B[0], B[1], B[2], dLp) && t0e.PropagateToXBzLight(posX, B[2], dLp)) {
315 return 1;
316 }
317
318 if (CAMath::Abs(t0e.SinPhi()) >= mMaxSinPhi) {
319 return -3;
320 }
321
322 return FollowLinearization(t0e, B[2], dLp, inFlyDirection);
323}
324
325GPUd() int32_t GPUTPCGMPropagator::PropagateToXAlphaBz(float posX, float posAlpha, bool inFlyDirection)
326{
327 if (CAMath::Abs(posAlpha - mAlpha) > 1.e-4f) {
328 if (RotateToAlpha(posAlpha) != 0) {
329 return -2;
330 }
331 }
332 if (CAMath::Abs(posX - mT->X()) < 1.e-7f) {
333 mT->SetX(posX);
334 return 0;
335 }
336
337 float Bz = GetBz(mT0.X(), mT0.Y(), mT0.Z());
338
339 // propagate mT0 to t0e
340
342 float dLp = 0;
343 if (t0e.PropagateToXBzLight(posX, Bz, dLp)) {
344 return 1;
345 }
346
347 if (CAMath::Abs(t0e.SinPhi()) >= mMaxSinPhi) {
348 return -3;
349 }
350
351 return FollowLinearization(t0e, Bz, dLp, inFlyDirection);
352}
353
354GPUd() int32_t GPUTPCGMPropagator::FollowLinearization(const GPUTPCGMPhysicalTrackModel& GPUrestrict() t0e, float Bz, float dLp, bool inFlyDirection)
355{
356 // t0e is alrerady extrapolated t0
357
358 // propagate track and cov matrix with derivatives for (0,0,Bz) field
359
360 float dS = dLp * t0e.GetPt();
361 float dL = CAMath::Abs(dLp * t0e.GetP());
362
363 if (inFlyDirection) {
364 dL = -dL;
365 }
366
367 float ey = mT0.SinPhi();
368 float ex = mT0.CosPhi();
369 float exi = mT0.SecPhi();
370 float ey1 = t0e.GetSinPhi();
371 float ex1 = t0e.GetCosPhi();
372 float ex1i = t0e.GetSecPhi();
373
374 float k = -mT0.QPt() * Bz;
375 float dx = t0e.GetX() - mT0.X();
376 float kdx = k * dx;
377 float cc = ex + ex1;
378 float cci = 1.f / cc;
379
380 float dxcci = dx * cci;
381 float hh = dxcci * ex1i * (1.f + ex * ex1 + ey * ey1);
382
383 float j02 = exi * hh;
384 float j04 = -Bz * dxcci * hh;
385 float j13 = dS;
386 float j24 = -dx * Bz;
387
388 float* p = mT->Par();
389
390 float d0 = p[0] - mT0.Y();
391 float d1 = p[1] - mT0.Z();
392 float d2 = p[2] - mT0.SinPhi();
393 float d3 = p[3] - mT0.DzDs();
394 float d4 = p[4] - mT0.QPt();
395
396 float newSinPhi = ey1 + d2 + j24 * d4;
397 if (mT->NDF() >= 15 && CAMath::Abs(newSinPhi) > GPUCA_MAX_SIN_PHI) {
398 return -4;
399 }
400
401 if (mMatLUT) {
402 UpdateMaterial(t0e);
403 }
404
405 mT0 = t0e;
406 mT->X() = t0e.GetX();
407 p[0] = t0e.GetY() + d0 + j02 * d2 + j04 * d4;
408 p[1] = t0e.GetZ() + d1 + j13 * d3;
409 p[2] = newSinPhi;
410 p[3] = t0e.GetDzDs() + d3;
411 p[4] = t0e.GetQPt() + d4;
412
413 float* c = mT->Cov();
414
415 float c20 = c[3];
416 float c21 = c[4];
417 float c22 = c[5];
418
419 float c30 = c[6];
420 float c31 = c[7];
421 float c32 = c[8];
422 float c33 = c[9];
423
424 float c40 = c[10];
425 float c41 = c[11];
426 float c42 = c[12];
427 float c43 = c[13];
428 float c44 = c[14];
429
430 if (mFitInProjections || mT->NDF() <= 0) {
431 float c20ph04c42 = c20 + j04 * c42;
432 float j02c22 = j02 * c22;
433 float j04c44 = j04 * c44;
434
435 float n6 = c30 + j02 * c32 + j04 * c43;
436 float n7 = c31 + j13 * c33;
437 float n10 = c40 + j02 * c42 + j04c44;
438 float n11 = c41 + j13 * c43;
439 float n12 = c42 + j24 * c44;
440
441 c[0] += j02 * j02c22 + j04 * j04c44 + 2.f * (j02 * c20ph04c42 + j04 * c40);
442 c[1] += j02 * c21 + j04 * c41 + j13 * n6;
443 c[2] += j13 * (c31 + n7);
444 c[3] = c20ph04c42 + j02c22 + j24 * n10;
445 c[4] = c21 + j13 * c32 + j24 * n11;
446 c[5] = c22 + j24 * (c42 + n12);
447 c[6] = n6;
448 c[7] = n7;
449 c[8] = c32 + c43 * j24;
450 c[10] = n10;
451 c[11] = n11;
452 c[12] = n12;
453 } else {
454 float c00 = c[0];
455 float c10 = c[1];
456 float c11 = c[2];
457
458 float ss = ey + ey1;
459 float tg = ss * cci;
460 float xx = 1.f - 0.25f * kdx * kdx * (1.f + tg * tg);
461 if (xx < 1.e-8f) {
462 return -1;
463 }
464 xx = CAMath::Sqrt(xx);
465 float yy = CAMath::Sqrt(ss * ss + cc * cc);
466
467 float j12 = dx * mT0.DzDs() * tg * (2.f + tg * (ey * exi + ey1 * ex1i)) / (xx * yy);
468 float j14 = 0;
469 if (CAMath::Abs(mT0.QPt()) > 1.e-6f) {
470 j14 = (2.f * xx * ex1i * dx / yy - dS) * mT0.DzDs() / mT0.QPt();
471 } else {
472 j14 = -mT0.DzDs() * Bz * dx * dx * exi * exi * exi * (0.5f * ey + (1.f / 3.f) * kdx * (1 + 2.f * ey * ey) * exi * exi);
473 }
474
475 p[1] += j12 * d2 + j14 * d4;
476
477 float h00 = c00 + c20 * j02 + c40 * j04;
478 // float h01 = c10 + c21*j02 + c41*j04;
479 float h02 = c20 + c22 * j02 + c42 * j04;
480 // float h03 = c30 + c32*j02 + c43*j04;
481 float h04 = c40 + c42 * j02 + c44 * j04;
482
483 float h10 = c10 + c20 * j12 + c30 * j13 + c40 * j14;
484 float h11 = c11 + c21 * j12 + c31 * j13 + c41 * j14;
485 float h12 = c21 + c22 * j12 + c32 * j13 + c42 * j14;
486 float h13 = c31 + c32 * j12 + c33 * j13 + c43 * j14;
487 float h14 = c41 + c42 * j12 + c43 * j13 + c44 * j14;
488
489 float h20 = c20 + c40 * j24;
490 float h21 = c21 + c41 * j24;
491 float h22 = c22 + c42 * j24;
492 float h23 = c32 + c43 * j24;
493 float h24 = c42 + c44 * j24;
494
495 c[0] = h00 + h02 * j02 + h04 * j04;
496
497 c[1] = h10 + h12 * j02 + h14 * j04;
498 c[2] = h11 + h12 * j12 + h13 * j13 + h14 * j14;
499
500 c[3] = h20 + h22 * j02 + h24 * j04;
501 c[4] = h21 + h22 * j12 + h23 * j13 + h24 * j14;
502 c[5] = h22 + h24 * j24;
503
504 c[6] = c30 + c32 * j02 + c43 * j04;
505 c[7] = c31 + c32 * j12 + c33 * j13 + c43 * j14;
506 c[8] = c32 + c43 * j24;
507 // c[ 9] = c33;
508
509 c[10] = c40 + c42 * j02 + c44 * j04;
510 c[11] = c41 + c42 * j12 + c43 * j13 + c44 * j14;
511 c[12] = c42 + c44 * j24;
512 // c[13] = c43;
513 // c[14] = c44;
514 }
515
516 float& mC22 = c[5];
517 float& mC33 = c[9];
518 float& mC40 = c[10];
519 float& mC41 = c[11];
520 float& mC42 = c[12];
521 float& mC43 = c[13];
522 float& mC44 = c[14];
523
524 float dLmask = 0.f;
525 bool maskMS = (CAMath::Abs(dL) < mMaterial.DLMax);
526 if (maskMS) {
527 dLmask = dL;
528 }
529 float dLabs = CAMath::Abs(dLmask);
530
531 // Energy Loss
532
533 if (1 || !mToyMCEvents) {
534 // std::cout<<"APPLY ENERGY LOSS!!!"<<std::endl;
535 float corr = 1.f - mMaterial.EP2 * dLmask;
536 float corrInv = 1.f / corr;
537 mT0.Px() *= corrInv;
538 mT0.Py() *= corrInv;
539 mT0.Pz() *= corrInv;
540 mT0.Pt() *= corrInv;
541 mT0.P() *= corrInv;
542 mT0.QPt() *= corr;
543
544 p[4] *= corr;
545
546 mC40 *= corr;
547 mC41 *= corr;
548 mC42 *= corr;
549 mC43 *= corr;
550 mC44 = mC44 * corr * corr + dLabs * mMaterial.sigmadE2;
551 } else {
552 // std::cout<<"DONT APPLY ENERGY LOSS!!!"<<std::endl;
553 }
554
555 // Multiple Scattering
556
557 if (!mToyMCEvents) {
558 mC22 += dLabs * mMaterial.k22 * mT0.CosPhi() * mT0.CosPhi();
559 mC33 += dLabs * mMaterial.k33;
560 mC43 += dLabs * mMaterial.k43;
561 mC44 += dLabs * mMaterial.k44;
562 }
563 return 0;
564}
565
566GPUd() int32_t GPUTPCGMPropagator::GetPropagatedYZ(float x, float& GPUrestrict() projY, float& GPUrestrict() projZ)
567{
568 float bz = GetBz(mT->X(), mT->Y(), mT->Z());
569 float k = mT0.QPt() * bz;
570 float dx = x - mT->X();
571 float ex = mT0.CosPhi();
572 float ey = mT0.SinPhi();
573 float ey1 = ey - k * dx;
574 if (CAMath::Abs(ey1) > GPUCA_MAX_SIN_PHI) {
575 return 1;
576 }
577 float ss = ey + ey1;
578 float ex1 = CAMath::Sqrt(1.f - ey1 * ey1);
579 if (ex < 0) {
580 ex1 = -ex1;
581 }
582 float cc = ex + ex1;
583 float dxcci = dx / cc;
584 float dy = dxcci * ss;
585 float norm2 = 1.f + ey * ey1 + ex * ex1;
586 float dl = dxcci * CAMath::Sqrt(norm2 + norm2);
587 float dS;
588 {
589 float dSin = 0.5f * k * dl;
590 float a = dSin * dSin;
591 const float k2 = 1.f / 6.f;
592 const float k4 = 3.f / 40.f;
593 dS = dl + dl * a * (k2 + a * (k4));
594 }
595 float dz = dS * mT0.DzDs();
596 projY = mT->Y() + dy;
597 projZ = mT->Z() + dz;
598 return 0;
599}
600
601GPUd() void GPUTPCGMPropagator::GetErr2(float& GPUrestrict() err2Y, float& GPUrestrict() err2Z, const GPUParam& GPUrestrict() param, float posZ, int32_t iRow, int16_t clusterState, int8_t sector, float time, float avgCharge, float charge) const
602{
603 GetErr2(err2Y, err2Z, param, mT0.GetSinPhi(), mT0.DzDs(), posZ, mT->GetX(), mT->GetY(), iRow, clusterState, sector, time, avgCharge, charge, mSeedingErrors);
604}
605
606GPUd() void GPUTPCGMPropagator::GetErr2(float& GPUrestrict() err2Y, float& GPUrestrict() err2Z, const GPUParam& GPUrestrict() param, float snp, float tgl, float posZ, float trackX, float trackY, int32_t iRow, int16_t clusterState, int8_t sector, float time, float avgCharge, float charge, bool seedingErrors)
607{
608#ifndef GPUCA_TPC_GEOMETRY_O2
609 if (seedingErrors) {
610 param.GetClusterErrorsSeeding2(sector, iRow, posZ, snp, tgl, time, err2Y, err2Z);
611 } else
612#endif
613 {
614 param.GetClusterErrors2(sector, iRow, posZ, snp, tgl, time, avgCharge, charge, err2Y, err2Z);
615 }
616 param.UpdateClusterError2ByState(clusterState, err2Y, err2Z);
617 float statErr2 = param.GetSystematicClusterErrorIFC2(trackX, trackY, posZ, sector >= (GPUCA_NSECTORS / 2));
618 if (sector >= GPUCA_NSECTORS / 2 + 1 && sector <= GPUCA_NSECTORS / 2 + 2) {
619 statErr2 += param.GetSystematicClusterErrorC122(trackX, trackY, sector);
620 }
621 err2Y += statErr2;
622 err2Z += statErr2;
623}
624
625GPUd() float GPUTPCGMPropagator::PredictChi2(float posY, float posZ, int32_t iRow, const GPUParam& GPUrestrict() param, int16_t clusterState, int8_t sector, float time, float avgCharge, float charge) const
626{
627 float err2Y, err2Z;
628 GetErr2(err2Y, err2Z, param, posZ, iRow, clusterState, sector, time, avgCharge, charge);
629 return PredictChi2(posY, posZ, err2Y, err2Z);
630}
631
632GPUd() float GPUTPCGMPropagator::PredictChi2(float posY, float posZ, float err2Y, float err2Z) const
633{
634 const float* mC = mT->Cov();
635 const float* mP = mT->Par();
636 const float z0 = posY - mP[0];
637 const float z1 = posZ - mP[1];
638
639 if (!mFitInProjections || mT->NDF() <= 0) {
640 const float w0 = 1.f / (err2Y + mC[0]);
641 const float w2 = 1.f / (err2Z + mC[2]);
642 return w0 * z0 * z0 + w2 * z1 * z1;
643 } else {
644 float w0 = mC[2] + err2Z, w1 = mC[1], w2 = mC[0] + err2Y;
645 { // Invert symmetric matrix
646 float det = w0 * w2 - w1 * w1;
647 if (CAMath::Abs(det) < 1.e-10f) {
648 det = 1.e-10f;
649 }
650 det = 1.f / det;
651 w0 = w0 * det;
652 w1 = -w1 * det;
653 w2 = w2 * det;
654 }
655 return CAMath::Abs((w0 * z0 + w1 * z1) * z0) + CAMath::Abs((w1 * z0 + w2 * z1) * z1);
656 }
657}
658
659GPUd() int32_t GPUTPCGMPropagator::Update(float posY, float posZ, int32_t iRow, const GPUParam& GPUrestrict() param, int16_t clusterState, int8_t rejectChi2, gputpcgmmergertypes::InterpolationErrorHit* inter, bool refit, int8_t sector, float time, float avgInvCharge, float invCharge GPUCA_DEBUG_STREAMER_CHECK(, DebugStreamerVals* debugVals))
660{
661 float err2Y, err2Z;
662 GetErr2(err2Y, err2Z, param, posZ, iRow, clusterState, sector, time, avgInvCharge, invCharge);
663 GPUCA_DEBUG_STREAMER_CHECK(if (debugVals) { debugVals->err2Y = err2Y; debugVals->err2Z = err2Z; });
664
665 if (rejectChi2 >= rejectInterFill) {
666 if (rejectChi2 == rejectInterReject && inter->errorY < (GPUCA_MERGER_INTERPOLATION_ERROR_TYPE)0) {
667 rejectChi2 = rejectDirect;
668 } else {
669 int32_t retVal = InterpolateReject(param, posY, posZ, clusterState, rejectChi2, inter, err2Y, err2Z);
670 GPUCA_DEBUG_STREAMER_CHECK(if (debugVals) { debugVals->retVal = retVal; });
671 if (retVal) {
672 return retVal;
673 }
674 }
675 }
676
677 if (mT->NDF() == -5) { // first measurement: no need to filter, as the result is known in advance. just set it.
678 mT->ResetCovariance();
679 float* mC = mT->Cov();
680 float* mP = mT->Par();
681 if (refit) {
682 mC[14] = CAMath::Max(0.5f, CAMath::Abs(mP[4]));
683 mC[5] = CAMath::Max(0.2f, CAMath::Abs(mP[2]) / 2);
684 mC[9] = CAMath::Max(0.5f, CAMath::Abs(mP[3]) / 2);
685 }
686 mP[0] = posY;
687 mP[1] = posZ;
688 mC[0] = err2Y;
689 mC[2] = err2Z;
690 mT->NDF() = -3;
691 return 0;
692 }
693
694 return Update(posY, posZ, clusterState, rejectChi2 == rejectDirect, err2Y, err2Z, &param);
695}
696
697GPUd() int32_t GPUTPCGMPropagator::InterpolateReject(const GPUParam& GPUrestrict() param, float posY, float posZ, int16_t clusterState, int8_t rejectChi2, gputpcgmmergertypes::InterpolationErrorHit* inter, float err2Y, float err2Z)
698{
699 float* GPUrestrict() mC = mT->Cov();
700 float* GPUrestrict() mP = mT->Par();
701 if (rejectChi2 == rejectInterFill) {
702 inter->posY = mP[0];
703 inter->posZ = mP[1];
704 inter->errorY = mC[0];
705 inter->errorZ = mC[2];
706 } else if (rejectChi2 == rejectInterReject) {
707 float chiY, chiZ;
708 if (mFitInProjections || mT->NDF() <= 0) {
709 const float Iz0 = inter->posY - mP[0];
710 const float Iz1 = inter->posZ - mP[1];
711 const float Iw0 = 1.f / (mC[0] + (float)inter->errorY);
712 const float Iw2 = 1.f / (mC[2] + (float)inter->errorZ);
713 const float Ik00 = mC[0] * Iw0;
714 const float Ik11 = mC[2] * Iw2;
715 const float ImP0 = mP[0] + Ik00 * Iz0;
716 const float ImP1 = mP[1] + Ik11 * Iz1;
717 const float ImC0 = mC[0] - Ik00 * mC[0];
718 const float ImC2 = mC[2] - Ik11 * mC[2];
719 //printf("\t%21sInterpo ----- abde artaf%16s Y %8.3f, Z %8.3f (Errors %f <-- (%f, %f) %f <-- (%f, %f))\n", "", "", ImP0, ImP1, sqrtf(ImC0), sqrtf(mC[0]), sqrtf(inter->errorY), sqrtf(ImC2), sqrtf(mC[2]), sqrtf(inter->errorZ));
720 const float Jz0 = posY - ImP0;
721 const float Jz1 = posZ - ImP1;
722 const float Jw0 = 1.f / (ImC0 + err2Y);
723 const float Jw2 = 1.f / (ImC2 + err2Z);
724 chiY = Jw0 * Jz0 * Jz0;
725 chiZ = Jw2 * Jz1 * Jz1;
726 } else {
727 const float Iz0 = inter->posY - mP[0];
728 const float Iz1 = inter->posZ - mP[1];
729 float Iw0 = mC[2] + (float)inter->errorZ;
730 float Iw2 = mC[0] + (float)inter->errorY;
731 float Idet = CAMath::Max(1e-10f, Iw0 * Iw2 - mC[1] * mC[1]);
732 Idet = 1.f / Idet;
733 Iw0 *= Idet;
734 const float Iw1 = mC[1] * Idet;
735 Iw2 *= Idet;
736 const float Ik00 = mC[0] * Iw0 + mC[1] * Iw1;
737 const float Ik01 = mC[0] * Iw1 + mC[1] * Iw2;
738 const float Ik10 = mC[1] * Iw0 + mC[2] * Iw1;
739 const float Ik11 = mC[1] * Iw1 + mC[2] * Iw2;
740 const float ImP0 = mP[0] + Ik00 * Iz0 + Ik01 * Iz1;
741 const float ImP1 = mP[1] + Ik10 * Iz0 + Ik11 * Iz1;
742 const float ImC0 = mC[0] - Ik00 * mC[0] + Ik01 * mC[1];
743 const float ImC1 = mC[1] - Ik10 * mC[0] + Ik11 * mC[1];
744 const float ImC2 = mC[2] - Ik10 * mC[1] + Ik11 * mC[2];
745 const float Jz0 = posY - ImP0;
746 const float Jz1 = posZ - ImP1;
747 float Jw0 = ImC2 + err2Z;
748 float Jw2 = ImC0 + err2Y;
749 float Jdet = CAMath::Max(1e-10f, Jw0 * Jw2 - ImC1 * ImC1);
750 Jdet = 1.f / Jdet;
751 Jw0 *= Jdet;
752 const float Jw1 = ImC1 * Jdet;
753 Jw2 *= Jdet;
754 chiY = CAMath::Abs((Jw0 * Jz0 + Jw1 * Jz1) * Jz0);
755 chiZ = CAMath::Abs((Jw1 * Jz0 + Jw2 * Jz1) * Jz1);
756 }
757 if (RejectCluster(chiY * param.rec.tpc.clusterRejectChi2TolleranceY, chiZ * param.rec.tpc.clusterRejectChi2TolleranceZ, clusterState)) { // TODO: Relative Pt resolution decreases slightly, why?
758 return updateErrorClusterRejected;
759 }
760 }
761 return 0;
762}
763
764GPUd() int32_t GPUTPCGMPropagator::Update(float posY, float posZ, int16_t clusterState, bool rejectChi2, float err2Y, float err2Z, const GPUParam* GPUrestrict() param)
765{
766 float* GPUrestrict() mC = mT->Cov();
767 float* GPUrestrict() mP = mT->Par();
768
769 const float d00 = mC[0], d01 = mC[1], d02 = mC[3], d03 = mC[6], d04 = mC[10];
770 const float d10 = mC[1], d11 = mC[2], d12 = mC[4], d13 = mC[7], d14 = mC[11];
771
772 const float z0 = posY - mP[0];
773 const float z1 = posZ - mP[1];
774 float w0, w1, w2, chiY, chiZ;
775 if (mFitInProjections || mT->NDF() <= 0) {
776 w0 = 1.f / (err2Y + d00);
777 w1 = 0;
778 w2 = 1.f / (err2Z + d11);
779 chiY = w0 * z0 * z0;
780 chiZ = w2 * z1 * z1;
781 } else {
782 w0 = d11 + err2Z, w1 = d10, w2 = d00 + err2Y;
783 { // Invert symmetric matrix
784 float det = w0 * w2 - w1 * w1;
785 if (CAMath::Abs(det) < 1.e-10f) {
786 return updateErrorFitFailed;
787 }
788 det = 1.f / det;
789 w0 = w0 * det;
790 w1 = -w1 * det;
791 w2 = w2 * det;
792 }
793 chiY = CAMath::Abs((w0 * z0 + w1 * z1) * z0);
794 chiZ = CAMath::Abs((w1 * z0 + w2 * z1) * z1);
795 }
796 float dChi2 = chiY + chiZ;
797 // GPUInfo("hits %d chi2 %f, new %f %f (dy %f dz %f)", N, mChi2, chiY, chiZ, z0, z1);
798 if (rejectChi2 == 1 && RejectCluster(chiY * param->rec.tpc.clusterRejectChi2TolleranceY, chiZ * param->rec.tpc.clusterRejectChi2TolleranceZ, clusterState)) {
799 return updateErrorClusterRejected;
800 }
801 mT->Chi2() += dChi2;
802 mT->NDF() += 2;
803
804 if (mFitInProjections || mT->NDF() <= 0) {
805 const float k00 = d00 * w0;
806 const float k20 = d02 * w0;
807 const float k40 = d04 * w0;
808 const float k11 = d11 * w2;
809 const float k31 = d13 * w2;
810 mP[0] += k00 * z0;
811 mP[1] += k11 * z1;
812 mP[2] += k20 * z0;
813 mP[3] += k31 * z1;
814 mP[4] += k40 * z0;
815
816 mC[0] -= k00 * d00;
817 mC[2] -= k11 * d11;
818 mC[3] -= k20 * d00;
819 mC[5] -= k20 * d02;
820 mC[7] -= k31 * d11;
821 mC[9] -= k31 * d13;
822 mC[10] -= k00 * d04;
823 mC[12] -= k40 * d02;
824 mC[14] -= k40 * d04;
825 } else {
826 const float k00 = d00 * w0 + d01 * w1;
827 const float k01 = d00 * w1 + d10 * w2;
828 const float k10 = d01 * w0 + d11 * w1;
829 const float k11 = d01 * w1 + d11 * w2;
830 const float k20 = d02 * w0 + d12 * w1;
831 const float k21 = d02 * w1 + d12 * w2;
832 const float k30 = d03 * w0 + d13 * w1;
833 const float k31 = d03 * w1 + d13 * w2;
834 const float k40 = d04 * w0 + d14 * w1;
835 const float k41 = d04 * w1 + d14 * w2;
836
837 mP[0] += k00 * z0 + k01 * z1;
838 mP[1] += k10 * z0 + k11 * z1;
839 mP[2] += k20 * z0 + k21 * z1;
840 mP[3] += k30 * z0 + k31 * z1;
841 mP[4] += k40 * z0 + k41 * z1;
842
843 mC[0] -= k00 * d00 + k01 * d10;
844
845 mC[2] -= k10 * d01 + k11 * d11;
846
847 mC[3] -= k20 * d00 + k21 * d10;
848 mC[5] -= k20 * d02 + k21 * d12;
849
850 mC[7] -= k30 * d01 + k31 * d11;
851 mC[9] -= k30 * d03 + k31 * d13;
852
853 mC[10] -= k40 * d00 + k41 * d10;
854 mC[12] -= k40 * d02 + k41 * d12;
855 mC[14] -= k40 * d04 + k41 * d14;
856
857 mC[1] -= k10 * d00 + k11 * d10;
858 mC[4] -= k20 * d01 + k21 * d11;
859 mC[6] -= k30 * d00 + k31 * d10;
860 mC[8] -= k30 * d02 + k31 * d12;
861 mC[11] -= k40 * d01 + k41 * d11;
862 mC[13] -= k40 * d03 + k41 * d13;
863 }
864 return 0;
865}
866
867//*
868//* Multiple scattering and energy losses
869//*
870
871GPUd() float GPUTPCGMPropagator::ApproximateBetheBloch(float beta2)
872{
873 //------------------------------------------------------------------
874 // This is an approximation of the Bethe-Bloch formula with
875 // the density effect taken into account at beta*gamma > 3.5
876 // (the approximation is reasonable only for solid materials)
877 //------------------------------------------------------------------
878
879 const float log0 = CAMath::Log(5940.f);
880 const float log1 = CAMath::Log(3.5f * 5940.f);
881
882 bool bad = (beta2 >= .999f) || (beta2 < 1.e-8f);
883
884 if (bad) {
885 beta2 = 0.5f;
886 }
887
888 float a = beta2 / (1.f - beta2);
889 float b = 0.5f * CAMath::Log(a);
890 float d = 0.153e-3f / beta2;
891 float c = b - beta2;
892
893 float ret = d * (log0 + b + c);
894 float case1 = d * (log1 + c);
895
896 if (a > 3.5f * 3.5f) {
897 ret = case1;
898 }
899 if (bad) {
900 ret = 0.f;
901 }
902
903 return ret;
904}
905
906GPUd() void GPUTPCGMPropagator::CalculateMaterialCorrection()
907{
908 //*!
909
910 const float mass = 0.13957f;
911
912 float qpt = mT0.GetQPt();
913 if (CAMath::Abs(qpt) > 20) {
914 qpt = 20;
915 }
916
917 float w2 = (1.f + mT0.GetDzDs() * mT0.GetDzDs()); //==(P/pt)2
918 float pti2 = qpt * qpt;
919 if (pti2 < 1.e-4f) {
920 pti2 = 1.e-4f;
921 }
922
923 float mass2 = mass * mass;
924 float beta2 = w2 / (w2 + mass2 * pti2);
925
926 float p2 = w2 / pti2; // impuls 2
927 float betheRho = ApproximateBetheBloch(p2 / mass2) * mMaterial.rho;
928 float E = CAMath::Sqrt(p2 + mass2);
929 float theta2 = (14.1f * 14.1f / 1.e6f) / (beta2 * p2) * mMaterial.radLenInv;
930
931 mMaterial.EP2 = E / p2;
932
933 // Approximate energy loss fluctuation (M.Ivanov)
934
935 const float knst = 0.0007f; // To be tuned.
936 mMaterial.sigmadE2 = knst * mMaterial.EP2 * qpt;
937 mMaterial.sigmadE2 = mMaterial.sigmadE2 * mMaterial.sigmadE2;
938
939 mMaterial.k22 = theta2 * w2;
940 mMaterial.k33 = mMaterial.k22 * w2;
941 mMaterial.k43 = 0.f;
942 mMaterial.k44 = theta2 * mT0.GetDzDs() * mT0.GetDzDs() * pti2;
943
944 float br = (betheRho > 1.e-8f) ? betheRho : 1.e-8f;
945 mMaterial.DLMax = 0.3f * E / br;
946 mMaterial.EP2 *= betheRho;
947 mMaterial.sigmadE2 = mMaterial.sigmadE2 * betheRho; // + mMaterial.fK44;
948}
949
950GPUd() void GPUTPCGMPropagator::Rotate180()
951{
952 mT0.X() = -mT0.X();
953 mT0.Y() = -mT0.Y();
954 mT0.Q() = -mT0.Q();
955 mT0.Pz() = -mT0.Pz();
956 mT0.UpdateValues();
957
958 mT->X() = -mT->X();
959 mT->Y() = -mT->Y();
960 mT->QPt() = -mT->QPt();
961 mT->DzDs() = -mT->DzDs();
962
963 mAlpha = mAlpha + CAMath::Pi();
964 while (mAlpha >= CAMath::Pi()) {
965 mAlpha -= CAMath::TwoPi();
966 }
967 while (mAlpha < -CAMath::Pi()) {
968 mAlpha += CAMath::TwoPi();
969 }
970 mCosAlpha = -mCosAlpha;
971 mSinAlpha = -mSinAlpha;
972
973 float* c = mT->Cov();
974 c[6] = -c[6];
975 c[7] = -c[7];
976 c[8] = -c[8];
977 c[10] = -c[10];
978 c[11] = -c[11];
979 c[12] = -c[12];
980}
981
982GPUd() void GPUTPCGMPropagator::ChangeDirection()
983{
984 mT0.Py() = -mT0.Py();
985 mT0.Pz() = -mT0.Pz();
986 mT0.Q() = -mT0.Q();
987 mT->SinPhi() = -mT->SinPhi();
988 mT->DzDs() = -mT->DzDs();
989 mT->QPt() = -mT->QPt();
990 mT0.UpdateValues();
991
992 float* c = mT->Cov();
993 c[3] = -c[3];
994 c[4] = -c[4];
995 c[6] = -c[6];
996 c[7] = -c[7];
997 c[10] = -c[10];
998 c[11] = -c[11];
999}
1000
1001GPUd() void GPUTPCGMPropagator::Mirror(bool inFlyDirection)
1002{
1003 // mirror the track and the track approximation to the point which has the same X, but located on the other side of trajectory
1004 float B[3];
1005 GetBxByBz(mT0.X(), mT0.Y(), mT0.Z(), B);
1006 float Bz = B[2];
1007 if (CAMath::Abs(Bz) < 1.e-8f) {
1008 Bz = 1.e-8f;
1009 }
1010
1011 float dy = -2.f * mT0.Q() * mT0.Px() / Bz;
1012 float dS; // path in XY
1013 {
1014 float chord = dy; // chord to the extrapolated point == |dy|*sign(x direction)
1015 float sa = -mT0.CosPhi(); // sin( half of the rotation angle ) == (chord/2) / radius
1016
1017 // dS = (Pt/b)*2*arcsin( sa )
1018 // = (Pt/b)*2*sa*(1 + 1/6 sa^2 + 3/40 sa^4 + 5/112 sa^6 +... )
1019 // = chord*(1 + 1/6 sa^2 + 3/40 sa^4 + 5/112 sa^6 +... )
1020
1021 float sa2 = sa * sa;
1022 const float k2 = 1.f / 6.f;
1023 const float k4 = 3.f / 40.f;
1024 // const float k6 = 5.f/112.f;
1025 dS = chord + chord * sa2 * (k2 + k4 * sa2);
1026 // dS = CAMath::Sqrt(pt2)/b*2.f*CAMath::ASin( sa );
1027 }
1028
1029 if (mT0.SinPhi() < 0.f) {
1030 dS = -dS;
1031 }
1032
1033 mT0.Y() = mT0.Y() + dy;
1034 mT0.Z() = mT0.Z() + mT0.DzDs() * dS;
1035 mT0.Px() = mT0.Px(); // should be positive
1036 mT->Y() = mT->Y() + dy;
1037 mT->Z() = mT->Z() + mT0.DzDs() * dS;
1038 ChangeDirection();
1039
1040 // Energy Loss
1041 if (1 || !mToyMCEvents) {
1042 // std::cout<<"MIRROR: APPLY ENERGY LOSS!!!"<<std::endl;
1043
1044 float dL = CAMath::Abs(dS * mT0.GetDlDs());
1045
1046 if (inFlyDirection) {
1047 dL = -dL;
1048 }
1049
1050 float* c = mT->Cov();
1051 float& mC40 = c[10];
1052 float& mC41 = c[11];
1053 float& mC42 = c[12];
1054 float& mC43 = c[13];
1055 float& mC44 = c[14];
1056
1057 float dLmask = 0.f;
1058 bool maskMS = (CAMath::Abs(dL) < mMaterial.DLMax);
1059 if (maskMS) {
1060 dLmask = dL;
1061 }
1062 float dLabs = CAMath::Abs(dLmask);
1063 float corr = 1.f - mMaterial.EP2 * dLmask;
1064
1065 float corrInv = 1.f / corr;
1066 mT0.Px() *= corrInv;
1067 mT0.Py() *= corrInv;
1068 mT0.Pz() *= corrInv;
1069 mT0.Pt() *= corrInv;
1070 mT0.P() *= corrInv;
1071 mT0.QPt() *= corr;
1072
1073 mT->QPt() *= corr;
1074
1075 mC40 *= corr;
1076 mC41 *= corr;
1077 mC42 *= corr;
1078 mC43 *= corr;
1079 mC44 = mC44 * corr * corr + dLabs * mMaterial.sigmadE2;
1080 } else {
1081 // std::cout<<"MIRROR: DONT APPLY ENERGY LOSS!!!"<<std::endl;
1082 }
1083}
1084
1085GPUd() o2::base::MatBudget GPUTPCGMPropagator::getMatBudget(const float* p1, const float* p2)
1086{
1087 return mMatLUT->getMatBudget(p1[0], p1[1], p1[2], p2[0], p2[1], p2[2]);
1088}
1089
1091{
1092 float xyz1[3] = {getGlobalX(mT0.GetX(), mT0.GetY()), getGlobalY(mT0.GetX(), mT0.GetY()), mT0.GetZ()};
1093 float xyz2[3] = {getGlobalX(t0e.GetX(), t0e.GetY()), getGlobalY(t0e.GetX(), t0e.GetY()), t0e.GetZ()};
1094 o2::base::MatBudget mat = getMatBudget(xyz1, xyz2);
1095 if (mat.meanX2X0 > 1.e-8) {
1096 SetMaterial(mat.length / mat.meanX2X0, mat.meanRho);
1097 } else {
1098 SetMaterialTPC();
1099 }
1100}
int16_t charge
Definition RawEventData.h:5
int16_t time
Definition RawEventData.h:4
#define GPUdic(...)
#define GPUrestrict()
#define GPUCA_DEBUG_STREAMER_CHECK(...)
#define GPUCA_MAX_SIN_PHI
int32_t retVal
const int16_t bb
GPUd() void GPUTPCGMPropagator
#define GPUCA_NSECTORS
constexpr int p2()
constexpr int p1()
constexpr to accelerate the coordinates changing
uint32_t c
Definition RawData.h:2
Definition B.h:16
GLint GLenum GLint x
Definition glcorearb.h:403
GLboolean GLboolean GLboolean b
Definition glcorearb.h:1233
typedef void(APIENTRYP PFNGLCULLFACEPROC)(GLenum mode)
GLuint GLfloat x0
Definition glcorearb.h:5034
GLboolean r
Definition glcorearb.h:1233
GLuint GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat t0
Definition glcorearb.h:5034
GLenum GLfloat param
Definition glcorearb.h:271
GLboolean GLboolean GLboolean GLboolean a
Definition glcorearb.h:1233
GLuint GLfloat GLfloat y0
Definition glcorearb.h:5034
float float float float z1
Definition MathUtils.h:44
value_T d2
Definition TrackUtils.h:135
kp1 *kp2 *value_T beta2
Definition TrackUtils.h:131
constexpr uint32_t TPC
Definition Triggers.h:39
a couple of static helper functions to create timestamp values for CCDB queries or override obsolete ...
float length
length in material
Definition MatCell.h:55
float meanRho
mean density, g/cm^3
Definition MatCell.h:30
float meanX2X0
fraction of radiaton lenght
Definition MatCell.h:31
std::vector< o2::mch::ChannelCode > cc