Project
Loading...
Searching...
No Matches
GPUTRDTracker.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// #define ENABLE_GPUTRDDEBUG
16#define ENABLE_WARNING 0
17#define ENABLE_INFO 0
18
19#include "GPUTRDTracker.h"
20#include "GPUTRDTrackletWord.h"
21#include "GPUTRDGeometry.h"
22#include "GPUTRDTrackerDebug.h"
23#include "GPUCommonMath.h"
24#include "GPUCommonAlgorithm.h"
25#include "GPUConstantMem.h"
26#include "GPUTRDRecoParam.h"
27
28using namespace o2::gpu;
29
31
32#ifndef GPUCA_GPUCODE
33#include "GPUMemoryResource.h"
34#include "GPUReconstruction.h"
35#include <chrono>
36#include <vector>
37
38#include "GPUChainTracking.h"
39
40template <class TRDTRK, class PROP>
42{
43 mNMaxTracks = std::max(std::max(io.nOutputTracksTPCO2, io.nTracksTPCITSO2), std::max(io.nMergedTracks, io.nOutputTracksTPCO2)); // TODO: This is a bit stupid, we should just take the correct number, not the max of all
44 mNMaxSpacePoints = io.nTRDTracklets;
45 mNMaxCollisions = io.nTRDTriggerRecords;
46}
47
48template <class TRDTRK, class PROP>
50{
51 AllocateAndInitializeLate();
52 mMemoryPermanent = mRec->RegisterMemoryAllocation(this, &GPUTRDTracker_t<TRDTRK, PROP>::SetPointersBase, GPUMemoryResource::MEMORY_PERMANENT, "TRDInitialize");
53 mMemoryTracklets = mRec->RegisterMemoryAllocation(this, &GPUTRDTracker_t<TRDTRK, PROP>::SetPointersTracklets, GPUMemoryResource::MEMORY_INPUT, "TRDTracklets");
54 mMemoryTracks = mRec->RegisterMemoryAllocation(this, &GPUTRDTracker_t<TRDTRK, PROP>::SetPointersTracks, GPUMemoryResource::MEMORY_INOUT, "TRDTracks");
57template <class TRDTRK, class PROP>
60 //--------------------------------------------------------------------
61 // Allocate memory for fixed size objects (needs to be done only once)
62 //--------------------------------------------------------------------
63 mMaxBackendThreads = mRec->GetMaxBackendThreads();
64 computePointerWithAlignment(base, mR, kNChambers);
65 computePointerWithAlignment(base, mHypothesis, mNCandidates * mMaxBackendThreads);
66 computePointerWithAlignment(base, mCandidates, mNCandidates * 2 * mMaxBackendThreads);
67 return base;
68}
69
70template <class TRDTRK, class PROP>
72{
73 //--------------------------------------------------------------------
74 // Allocate memory for tracklets and space points
75 // (size might change for different events)
76 //--------------------------------------------------------------------
77 if (mGenerateSpacePoints) {
78 computePointerWithAlignment(base, mSpacePoints, mNMaxSpacePoints);
79 }
80 computePointerWithAlignment(base, mTrackletIndexArray, (kNChambers + 1) * mNMaxCollisions);
81 return base;
82}
83
84template <class TRDTRK, class PROP>
86{
87 //--------------------------------------------------------------------
88 // Allocate memory for tracks (this is done once per event)
89 //--------------------------------------------------------------------
90 computePointerWithAlignment(base, mTracks, mNMaxTracks);
91 computePointerWithAlignment(base, mTrackAttribs, mNMaxTracks);
92 return base;
93}
94
95template <class TRDTRK, class PROP>
96GPUTRDTracker_t<TRDTRK, PROP>::GPUTRDTracker_t() : mR(nullptr), mIsInitialized(false), mGenerateSpacePoints(false), mProcessPerTimeFrame(false), mNAngleHistogramBins(25), mAngleHistogramRange(50), mMemoryPermanent(-1), mMemoryTracklets(-1), mMemoryTracks(-1), mNMaxCollisions(0), mNMaxTracks(0), mNMaxSpacePoints(0), mTracks(nullptr), mTrackAttribs(nullptr), mNCandidates(1), mNTracks(0), mNEvents(0), mMaxBackendThreads(100), mTrackletIndexArray(nullptr), mFT0TriggeredBC(nullptr), mNFT0BC(0), mHypothesis(nullptr), mCandidates(nullptr), mSpacePoints(nullptr), mGeo(nullptr), mRecoParam(nullptr), mDebugOutput(false), mMaxEta(0.84f), mRoadZ(18.f), mTPCVdrift(2.58f), mTPCTDriftOffset(0.f), mDebug(new GPUTRDTrackerDebug<TRDTRK>())
97{
98 //--------------------------------------------------------------------
99 // Default constructor
100 //--------------------------------------------------------------------
101}
103template <class TRDTRK, class PROP>
105{
106 //--------------------------------------------------------------------
107 // Destructor
108 //--------------------------------------------------------------------
109 delete mDebug;
110}
111
112template <class TRDTRK, class PROP>
114{
115 //--------------------------------------------------------------------
116 // Initialise tracker
117 //--------------------------------------------------------------------
118 mRecoParam = GetConstantMem()->calibObjects.trdRecoParam;
119 UpdateGeometry();
120 mDebug->ExpandVectors();
121 mIsInitialized = true;
122}
123
124template <class TRDTRK, class PROP>
126{
127 //--------------------------------------------------------------------
128 // Update Geometry of TRDTracker
129 //--------------------------------------------------------------------
130 mGeo = (const GPUTRDGeometry*)GetConstantMem()->calibObjects.trdGeometry;
131 if (!mGeo) {
132 GPUFatal("TRD geometry must be provided externally");
133 }
134 // obtain average radius of TRD chambers
135 float x0[kNLayers] = {300.2f, 312.8f, 325.4f, 338.0f, 350.6f, 363.2f}; // used as default value in case no transformation matrix can be obtained
136 auto* matrix = mGeo->GetClusterMatrix(0);
137 float loc[3] = {mGeo->AnodePos(), 0.f, 0.f};
138 float glb[3] = {0.f, 0.f, 0.f};
139 for (int32_t iDet = 0; iDet < kNChambers; ++iDet) {
140 matrix = mGeo->GetClusterMatrix(iDet);
141 if (!matrix) {
142 mR[iDet] = x0[mGeo->GetLayer(iDet)];
143 continue;
144 }
145 matrix->LocalToMaster(loc, glb);
146 mR[iDet] = glb[0];
147 }
148}
149
150template <class TRDTRK, class PROP>
152{
153 //--------------------------------------------------------------------
154 // Reset tracker
155 //--------------------------------------------------------------------
156 mNTracks = 0;
157}
158
159template <class TRDTRK, class PROP>
161{
162 //--------------------------------------------------------------------
163 // Prepare tracklet index array and if requested calculate space points
164 // in part duplicated from DoTracking() method to allow for calling
165 // this function on the host prior to GPU processing
166 //--------------------------------------------------------------------
167 for (uint32_t iColl = 0; iColl < GetConstantMem()->ioPtrs.nTRDTriggerRecords; ++iColl) {
168 if (GetConstantMem()->ioPtrs.trdTrigRecMask && GetConstantMem()->ioPtrs.trdTrigRecMask[iColl] == 0) {
169 // this trigger is masked as there is no ITS information available for it
170 continue;
171 }
172 int32_t nTrklts = 0;
173 int32_t idxOffset = 0;
174 if (mProcessPerTimeFrame) {
175 idxOffset = GetConstantMem()->ioPtrs.trdTrackletIdxFirst[iColl];
176 nTrklts = (iColl < GetConstantMem()->ioPtrs.nTRDTriggerRecords - 1) ? GetConstantMem()->ioPtrs.trdTrackletIdxFirst[iColl + 1] - GetConstantMem()->ioPtrs.trdTrackletIdxFirst[iColl] : GetConstantMem()->ioPtrs.nTRDTracklets - GetConstantMem()->ioPtrs.trdTrackletIdxFirst[iColl];
177 } else {
178 nTrklts = GetConstantMem()->ioPtrs.nTRDTracklets;
179 }
180 const GPUTRDTrackletWord* tracklets = &((GetConstantMem()->ioPtrs.trdTracklets)[idxOffset]);
181 int32_t* trkltIndexArray = &mTrackletIndexArray[iColl * (kNChambers + 1) + 1];
182 trkltIndexArray[-1] = 0;
183 int32_t currDet = 0;
184 int32_t nextDet = 0;
185 int32_t trkltCounter = 0;
186 for (int32_t iTrklt = 0; iTrklt < nTrklts; ++iTrklt) {
187 if (tracklets[iTrklt].GetDetector() > currDet) {
188 nextDet = tracklets[iTrklt].GetDetector();
189 for (int32_t iDet = currDet; iDet < nextDet; ++iDet) {
190 trkltIndexArray[iDet] = trkltCounter;
191 }
192 currDet = nextDet;
193 }
194 ++trkltCounter;
195 }
196 for (int32_t iDet = currDet; iDet <= kNChambers; ++iDet) {
197 trkltIndexArray[iDet] = trkltCounter;
198 }
199 if (mGenerateSpacePoints) {
200 if (!CalculateSpacePoints(iColl)) {
201 GPUError("Space points for at least one chamber could not be calculated (for interaction %i)", iColl);
202 break;
203 }
204 }
205 }
206 if (mGenerateSpacePoints) {
207 chainTracking->mIOPtrs.trdSpacePoints = mSpacePoints;
208 }
209 mNEvents++;
210}
211
212template <class TRDTRK, class PROP>
214{
215 //--------------------------------------------------------------------
216 // set the number of candidates to be used
217 //--------------------------------------------------------------------
218 if (!mIsInitialized) {
219 mNCandidates = n;
220 } else {
221 GPUError("Cannot change mNCandidates after initialization");
222 }
223}
224
225template <class TRDTRK, class PROP>
227{
228 //--------------------------------------------------------------------
229 // print current settings to screen
230 //--------------------------------------------------------------------
231 GPUInfo("##############################################################");
232 GPUInfo("Current settings for GPU TRD tracker:");
233 GPUInfo(" maxChi2(%.2f), chi2Penalty(%.2f), nCandidates(%i), maxMissingLayers(%i)", Param().rec.trd.maxChi2, Param().rec.trd.penaltyChi2, mNCandidates, Param().rec.trd.stopTrkAfterNMissLy);
234 GPUInfo(" ptCut = %.2f GeV, abs(eta) < %.2f", Param().rec.trd.minTrackPt, mMaxEta);
235 GPUInfo("##############################################################");
236}
237
238template <class TRDTRK, class PROP>
240{
241 mDebug->CreateStreamer();
242}
243
244#endif
245
246template <>
247GPUdi() const GPUTRDPropagatorGPU::propagatorParam* GPUTRDTracker_t<GPUTRDTrackGPU, GPUTRDPropagatorGPU>::getPropagatorParam()
248{
249 return &Param().polynomialField;
250}
251
252template <class TRDTRK, class PROP>
253GPUdi() const typename PROP::propagatorParam* GPUTRDTracker_t<TRDTRK, PROP>::getPropagatorParam()
254{
255 return GetConstantMem()->calibObjects.o2Propagator;
256}
257
258template <class TRDTRK, class PROP>
259GPUd() bool GPUTRDTracker_t<TRDTRK, PROP>::CheckTrackTRDCandidate(const TRDTRK& trk) const
260{
261 if (!trk.CheckNumericalQuality()) {
262 return false;
263 }
264 if (CAMath::Abs(trk.getEta()) > mMaxEta) {
265 return false;
266 }
267 if (trk.getPt() < Param().rec.trd.minTrackPt) {
268 return false;
269 }
270 return true;
271}
272
273template <class TRDTRK, class PROP>
274GPUd() int32_t GPUTRDTracker_t<TRDTRK, PROP>::LoadTrack(const TRDTRK& trk, uint32_t tpcTrackId, bool checkTrack, HelperTrackAttributes* attribs)
275{
276 if (mNTracks >= mNMaxTracks) {
277#ifndef GPUCA_GPUCODE
278 GPUError("Error: Track dropped (no memory available) -> must not happen");
279#endif
280 return (1);
281 }
282 if (checkTrack && !CheckTrackTRDCandidate(trk)) {
283 return 2;
284 }
285 mTracks[mNTracks] = trk;
286 mTracks[mNTracks].setRefGlobalTrackIdRaw(tpcTrackId);
287 if (attribs) {
288 mTrackAttribs[mNTracks] = *attribs;
289 }
290 mNTracks++;
291 return (0);
292}
293
294template <class TRDTRK, class PROP>
295GPUd() void GPUTRDTracker_t<TRDTRK, PROP>::DumpTracks()
296{
297 //--------------------------------------------------------------------
298 // helper function (only for debugging purposes)
299 //--------------------------------------------------------------------
300 GPUInfo("There are in total %i tracklets loaded", GetConstantMem()->ioPtrs.nTRDTracklets);
301 GPUInfo("There are %i tracks loaded. mNMaxTracks(%i)", mNTracks, mNMaxTracks);
302 for (int32_t i = 0; i < mNTracks; ++i) {
303 auto* trk = &(mTracks[i]);
304 GPUInfo("track %i: x=%f, alpha=%f, nTracklets=%i, pt=%f, time=%f", i, trk->getX(), trk->getAlpha(), trk->getNtracklets(), trk->getPt(), mTrackAttribs[i].mTime);
305 }
306}
307
308template <class TRDTRK, class PROP>
309GPUd() int32_t GPUTRDTracker_t<TRDTRK, PROP>::GetCollisionIDs(int32_t iTrk, int32_t* collisionIds) const
310{
311 //--------------------------------------------------------------------
312 // Check which TRD trigger times possibly match given input track.
313 // If ITS-TPC matches or CE-crossing TPC tracks the time is precisely
314 // known and max 1 trigger time can be assigned.
315 // For TPC-only tracks the collision IDs are stored in collisionIds array
316 // and the number of valid entries in the array is returned
317 //--------------------------------------------------------------------
318 int32_t nColls = 0;
319 for (uint32_t iColl = 0; iColl < GetConstantMem()->ioPtrs.nTRDTriggerRecords; ++iColl) {
320 if (GetConstantMem()->ioPtrs.trdTrigRecMask && GetConstantMem()->ioPtrs.trdTrigRecMask[iColl] == 0) {
321 continue;
322 }
323 if (GetConstantMem()->ioPtrs.trdTriggerTimes[iColl] > mTrackAttribs[iTrk].GetTimeMin() && GetConstantMem()->ioPtrs.trdTriggerTimes[iColl] < mTrackAttribs[iTrk].GetTimeMax()) {
324 if (nColls == 20) {
325 GPUError("Found too many collision candidates for track with tMin(%f) and tMax(%f)", mTrackAttribs[iTrk].GetTimeMin(), mTrackAttribs[iTrk].GetTimeMax());
326 return nColls;
327 }
328 collisionIds[nColls++] = iColl;
329 }
330 }
331 return nColls;
332}
333
334template <class TRDTRK, class PROP>
335GPUd() void GPUTRDTracker_t<TRDTRK, PROP>::DoTrackingThread(int32_t iTrk, int32_t threadId)
336{
337 //--------------------------------------------------------------------
338 // perform the tracking for one track (must be threadsafe)
339 //--------------------------------------------------------------------
340 int32_t collisionIds[20] = {0}; // due to the dead time there will never exist more possible TRD triggers for a single track
341 int32_t nCollisionIds = 1; // initialize with 1 for AliRoot compatibility
342 if (mProcessPerTimeFrame) {
343 nCollisionIds = GetCollisionIDs(iTrk, collisionIds);
344 if (nCollisionIds == 0) {
345 if (ENABLE_INFO) {
346 GPUInfo("Did not find TRD data for track %i with t=%f. tMin(%f), tMax(%f)", iTrk, mTrackAttribs[iTrk].mTime, mTrackAttribs[iTrk].GetTimeMin(), mTrackAttribs[iTrk].GetTimeMax());
347 }
348 // no TRD data available for the bunch crossing this track originates from
349 return;
350 }
351 }
352 PROP prop(getPropagatorParam());
353 mTracks[iTrk].setChi2(Param().rec.trd.penaltyChi2); // TODO check if this should not be higher
354
355 auto trkStart = mTracks[iTrk];
356 for (int32_t iColl = 0; iColl < nCollisionIds; ++iColl) {
357 // do track following for each collision candidate and keep best track
358 auto trkCopy = trkStart;
359 prop.setTrack(&trkCopy);
360 prop.setFitInProjections(true);
361 if (!FollowProlongation(&prop, &trkCopy, iTrk, threadId, collisionIds[iColl])) {
362 // track following failed
363 continue;
364 }
365 if (trkCopy.getReducedChi2() < mTracks[iTrk].getReducedChi2()) {
366 mTracks[iTrk] = trkCopy; // copy back the resulting track
367 }
368 }
369}
370
371template <class TRDTRK, class PROP>
372GPUd() bool GPUTRDTracker_t<TRDTRK, PROP>::CalculateSpacePoints(int32_t iCollision)
373{
374 //--------------------------------------------------------------------
375 // Calculates TRD space points in sector tracking coordinates
376 // from online tracklets
377 //--------------------------------------------------------------------
378
379 bool result = true;
380 int32_t idxOffset = iCollision * (kNChambers + 1); // offset for accessing mTrackletIndexArray for collision iCollision
381
382 const GPUTRDTrackletWord* tracklets = GetConstantMem()->ioPtrs.trdTracklets;
383
384 for (int32_t iDet = 0; iDet < kNChambers; ++iDet) {
385 int32_t iFirstTrackletInDet = mTrackletIndexArray[idxOffset + iDet];
386 int32_t iFirstTrackletInNextDet = mTrackletIndexArray[idxOffset + iDet + 1];
387 int32_t nTrackletsInDet = iFirstTrackletInNextDet - iFirstTrackletInDet;
388 if (nTrackletsInDet == 0) {
389 continue;
390 }
391 if (!mGeo->ChamberInGeometry(iDet)) {
392 GPUError("Found TRD tracklets in chamber %i which is not included in the geometry", iDet);
393 return false;
394 }
395 auto* matrix = mGeo->GetClusterMatrix(iDet);
396 if (!matrix) {
397 GPUError("No cluster matrix available for chamber %i. Skipping it...", iDet);
398 result = false;
399 continue;
400 }
401 const GPUTRDpadPlane* pp = mGeo->GetPadPlane(iDet);
402
403 int32_t trkltIdxOffset = (mProcessPerTimeFrame) ? GetConstantMem()->ioPtrs.trdTrackletIdxFirst[iCollision] : 0; // global index of first tracklet in iCollision
404 int32_t trkltIdxStart = trkltIdxOffset + iFirstTrackletInDet;
405 for (int32_t trkltIdx = trkltIdxStart; trkltIdx < trkltIdxStart + nTrackletsInDet; ++trkltIdx) {
406 int32_t trkltZbin = tracklets[trkltIdx].GetZbin();
407 float xTrkltDet[3] = {0.f}; // trklt position in chamber coordinates
408 float xTrkltSec[3] = {0.f}; // trklt position in sector coordinates
409 xTrkltDet[0] = mGeo->AnodePos() + sRadialOffset;
410 xTrkltDet[1] = tracklets[trkltIdx].GetY();
411 xTrkltDet[2] = pp->GetRowPos(trkltZbin) - pp->GetRowSize(trkltZbin) / 2.f - pp->GetRowPos(pp->GetNrows() / 2);
412 // GPUInfo("Space point local %i: x=%f, y=%f, z=%f", trkltIdx, xTrkltDet[0], xTrkltDet[1], xTrkltDet[2]);
413 matrix->LocalToMaster(xTrkltDet, xTrkltSec);
414 mSpacePoints[trkltIdx].setX(xTrkltSec[0]);
415 mSpacePoints[trkltIdx].setY(xTrkltSec[1]);
416 mSpacePoints[trkltIdx].setZ(xTrkltSec[2]);
417 mSpacePoints[trkltIdx].setDy(tracklets[trkltIdx].GetdY());
418
419 // GPUInfo("Space point global %i: x=%f, y=%f, z=%f", trkltIdx, mSpacePoints[trkltIdx].getX(), mSpacePoints[trkltIdx].getY(), mSpacePoints[trkltIdx].getZ());
420 }
421 }
422 return result;
423}
424
425template <class TRDTRK, class PROP>
426GPUd() bool GPUTRDTracker_t<TRDTRK, PROP>::FollowProlongation(PROP* prop, TRDTRK* t, int32_t iTrk, int32_t threadId, int32_t collisionId)
427{
428 //--------------------------------------------------------------------
429 // Propagate TPC track layerwise through TRD and pick up closest
430 // tracklet(s) on the way
431 // -> returns false if prolongation could not be executed fully
432 // or track does not fullfill threshold conditions
433 //--------------------------------------------------------------------
434
435 if (ENABLE_INFO) {
436 GPUInfo("Start track following for track %i at x=%f with pt=%f", t->getRefGlobalTrackIdRaw(), t->getX(), t->getPt());
437 }
438 mDebug->Reset();
439 t->setChi2(0.f);
440
441 // Find compatible BC ids
442 int32_t nIdxBCMin = -1;
443 int32_t nIdxBCMax = -1;
444
445 for (int32_t iBC = 0; iBC < mNFT0BC; iBC++) {
446 int32_t deltaBC = CAMath::Round(mFT0TriggeredBC[iBC] - GetConstantMem()->ioPtrs.trdTriggerTimes[collisionId] / o2::constants::lhc::LHCBunchSpacingMUS);
447 if (nIdxBCMin == -1 && deltaBC > mRecoParam->getPileUpRangeBefore()) {
448 nIdxBCMin = iBC;
449 }
450 if (deltaBC >= mRecoParam->getPileUpRangeAfter()) {
451 nIdxBCMax = iBC;
452 break;
453 }
454 if (iBC == mNFT0BC - 1) {
455 nIdxBCMax = iBC + 1;
456 if (nIdxBCMin == -1) {
457 // we did not find the correct BC, so we don't do any pile-up correction
458 nIdxBCMin = nIdxBCMax;
459 }
460 }
461 }
462
463 float zShiftTrk = 0.f;
464 if (mProcessPerTimeFrame) {
465 zShiftTrk = (mTrackAttribs[iTrk].mTime - GetConstantMem()->ioPtrs.trdTriggerTimes[collisionId]) * mTPCVdrift * mTrackAttribs[iTrk].mSide;
466 // float addZerr = (mTrackAttribs[iTrk].mTimeAddMax + mTrackAttribs[iTrk].mTimeSubMax) * .5f * mTPCVdrift;
467 // increase Z error based on time window
468 // -> this is here since it was done before, but the efficiency seems to be better if the covariance is not updated (more tracklets are attached)
469 // t->updateCovZ2(addZerr * addZerr); // TODO check again once detailed performance study tools are available, maybe this can be tuned
470 }
471 const GPUTRDpadPlane* pad = nullptr;
472 const GPUTRDTrackletWord* tracklets = GetConstantMem()->ioPtrs.trdTracklets;
473 const GPUTRDSpacePoint* spacePoints = GetConstantMem()->ioPtrs.trdSpacePoints;
474
475#ifdef ENABLE_GPUTRDDEBUG
476 TRDTRK trackNoUp(*t);
477#endif
478
479 int32_t candidateIdxOffset = threadId * 2 * mNCandidates;
480 int32_t hypothesisIdxOffset = threadId * mNCandidates;
481 int32_t trkltIdxOffset = collisionId * (kNChambers + 1); // offset for accessing mTrackletIndexArray for given collision
482 int32_t glbTrkltIdxOffset = (mProcessPerTimeFrame) ? GetConstantMem()->ioPtrs.trdTrackletIdxFirst[collisionId] : 0; // offset of first tracklet in given collision in global tracklet array
483
484 auto trkWork = t;
485 if (mNCandidates > 1) {
486 // copy input track to first candidate
487 mCandidates[candidateIdxOffset] = *t;
488 }
489
490 int32_t nCandidates = 1; // we always start with one candidate
491 int32_t nCurrHypothesis = 0; // the number of track hypothesis in given iLayer
492
493 // search window
494 float roadY = 0.f;
495 float roadZ = 0.f;
496 const int32_t nMaxChambersToSearch = 4;
497
498 mDebug->SetGeneralInfo(mNEvents, mNTracks, iTrk, t->getPt());
499
500 for (int32_t iLayer = 0; iLayer < kNLayers; ++iLayer) {
501 nCurrHypothesis = 0;
502 bool isOK = false; // if at least one candidate could be propagated or the track was stopped this becomes true
503 int32_t currIdx = candidateIdxOffset + iLayer % 2;
504 int32_t nextIdx = candidateIdxOffset + (iLayer + 1) % 2;
505 pad = mGeo->GetPadPlane(iLayer, 0);
506 float tilt = CAMath::Tan(CAMath::Pi() / 180.f * pad->GetTiltingAngle()); // tilt is signed!
507 const float zMaxTRD = pad->GetRow0();
508
509 // --------------------------------------------------------------------------------
510 //
511 // for each candidate, propagate to layer radius and look for close tracklets
512 //
513 // --------------------------------------------------------------------------------
514 for (int32_t iCandidate = 0; iCandidate < nCandidates; iCandidate++) {
515
516 int32_t det[nMaxChambersToSearch] = {-1, -1, -1, -1}; // TRD chambers to be searched for tracklets
517
518 if (mNCandidates > 1) {
519 trkWork = &mCandidates[2 * iCandidate + currIdx];
520 prop->setTrack(trkWork);
521 }
522
523 if (trkWork->getIsStopped()) {
524 Hypothesis hypo(trkWork->getNlayersFindable(), iCandidate, -1, trkWork->getChi2());
525 InsertHypothesis(hypo, nCurrHypothesis, hypothesisIdxOffset);
526 isOK = true;
527 continue;
528 }
529
530 // propagate track to average radius of TRD layer iLayer (sector 0, stack 2 is chosen as a reference)
531 if (!prop->propagateToX(mR[2 * kNLayers + iLayer], .8f, 2.f)) {
532 if (ENABLE_INFO) {
533 GPUInfo("Track propagation failed for track %i candidate %i in layer %i (pt=%f, x=%f, mR[layer]=%f)", iTrk, iCandidate, iLayer, trkWork->getPt(), trkWork->getX(), mR[2 * kNLayers + iLayer]);
534 }
535 continue;
536 }
537
538 // rotate track in new sector in case of sector crossing
539 if (!AdjustSector(prop, trkWork)) {
540 if (ENABLE_INFO) {
541 GPUInfo("Adjusting sector failed for track %i candidate %i in layer %i", iTrk, iCandidate, iLayer);
542 }
543 continue;
544 }
545
546 // check if track is findable
547 if (IsGeoFindable(trkWork, iLayer, prop->getAlpha(), zShiftTrk)) {
548 trkWork->setIsFindable(iLayer);
549 }
550
551 // define search window
552 roadY = 7.f * CAMath::Sqrt(trkWork->getSigmaY2() + 0.1f * 0.1f) + Param().rec.trd.extraRoadY; // add constant to the road to account for uncertainty due to radial deviations (few mm)
553 // roadZ = 7.f * CAMath::Sqrt(trkWork->getSigmaZ2() + 9.f * 9.f / 12.f); // take longest pad length
554 roadZ = mRoadZ + Param().rec.trd.extraRoadZ; // simply twice the longest pad length -> efficiency 99.996%
555 //
556 if (CAMath::Abs(trkWork->getZ() + zShiftTrk) - roadZ >= zMaxTRD) {
557 if (ENABLE_INFO) {
558 GPUInfo("Track out of TRD acceptance with z=%f in layer %i (eta=%f)", trkWork->getZ() + zShiftTrk, iLayer, trkWork->getEta());
559 }
560 continue;
561 }
562
563 // determine chamber(s) to be searched for tracklets
564 FindChambersInRoad(trkWork, roadY, roadZ, iLayer, det, zMaxTRD, prop->getAlpha(), zShiftTrk);
565
566 // track debug information to be stored in case no matching tracklet can be found
567 mDebug->SetTrackParameter(*trkWork, iLayer);
568
569 // look for tracklets in chamber(s)
570 for (int32_t iDet = 0; iDet < nMaxChambersToSearch; iDet++) {
571 int32_t currDet = det[iDet];
572 if (currDet == -1) {
573 continue;
574 }
575 pad = mGeo->GetPadPlane(currDet);
576 int32_t currSec = mGeo->GetSector(currDet);
577 if (currSec != GetSector(prop->getAlpha())) {
578 if (!prop->rotate(GetAlphaOfSector(currSec))) {
579 if (ENABLE_WARNING) {
580 GPUWarning("Track could not be rotated in tracklet coordinate system");
581 }
582 break;
583 }
584 }
585 if (currSec != GetSector(prop->getAlpha())) {
586 GPUError("Track is in sector %i and sector %i is searched for tracklets", GetSector(prop->getAlpha()), currSec);
587 continue;
588 }
589 // propagate track to radius of chamber
590 if (!prop->propagateToX(mR[currDet], .8f, .2f)) {
591 if (ENABLE_WARNING) {
592 GPUWarning("Track parameter for track %i, x=%f at chamber %i x=%f in layer %i cannot be retrieved", iTrk, trkWork->getX(), currDet, mR[currDet], iLayer);
593 }
594 }
595
596 // first propagate track to x of tracklet
597 for (int32_t trkltIdx = glbTrkltIdxOffset + mTrackletIndexArray[trkltIdxOffset + currDet]; trkltIdx < glbTrkltIdxOffset + mTrackletIndexArray[trkltIdxOffset + currDet + 1]; ++trkltIdx) {
598 if (CAMath::Abs(trkWork->getY() - spacePoints[trkltIdx].getY()) > roadY || CAMath::Abs(trkWork->getZ() + zShiftTrk - spacePoints[trkltIdx].getZ()) > roadZ) {
599 // skip tracklets which are too far away
600 // although the radii of space points and tracks may differ by ~ few mm the roads are large enough to allow no efficiency loss by this cut
601 continue;
602 }
603 float projY, projZ;
604 prop->getPropagatedYZ(spacePoints[trkltIdx].getX(), projY, projZ);
605 // correction for tilted pads (only applied if deltaZ < lPad && track z err << lPad)
606 float tiltCorr = tilt * (spacePoints[trkltIdx].getZ() - projZ);
607 float dyTiltCorr = tilt * trkWork->getTgl() * mGeo->GetCdrHght();
608 float lPad = pad->GetRowSize(tracklets[trkltIdx].GetZbin());
609 if (!((CAMath::Abs(spacePoints[trkltIdx].getZ() - projZ) < lPad) && (trkWork->getSigmaZ2() < (lPad * lPad / 12.f)))) {
610 tiltCorr = 0.f; // will be zero also for TPC tracks which are shifted in z
611 dyTiltCorr = 0.f;
612 }
613
614 // Correction for pile-up: if the track comes from a pile-up event, it should not be extrapolated to the anode plane at t=0,
615 // contrary to the assumption when tracklet is reconstructed, so we cancel this extrapolation.
616 // The correction is extracted from the most probable trigger. There is also an additional error depending on all the compatible triggers
617 float yCorrPileUp = 0.f;
618 float yAddErrPileUp2 = 0.f;
619 // additional zshiftTrk for TPC only tracks due to pile-up
620 float zShiftTrkPileUp = 0.f;
621 if (nIdxBCMax - nIdxBCMin >= 2) {
622 float maxProb = 0.f;
623 // The uncertainty is the RMS wrt the default correction of all possible corrections weighted by their probability
624 float sumCorr = 0.f;
625 float sumCorr2 = 0.f;
626 float sumProb = 0.f;
627 // conversion from slope in pad per time bin to slope in cm per BC = tracklets[trkltIdx].getSlopeFloat() * padWidth / BCperTimeBin
628 float slopeFactor = tracklets[trkltIdx].GetSlopeFloat() * mGeo->GetPadPlaneWidthIPad(tracklets[trkltIdx].GetDetector()) / 4.f;
629 for (int32_t iBC = nIdxBCMin; iBC < nIdxBCMax; iBC++) {
630 int32_t deltaBC = CAMath::Round(mFT0TriggeredBC[iBC] - GetConstantMem()->ioPtrs.trdTriggerTimes[collisionId] / o2::constants::lhc::LHCBunchSpacingMUS);
631 float probBC = mRecoParam->getPileUpProbTracklet(deltaBC, true, (tracklets[trkltIdx].GetQ0() != 0), (tracklets[trkltIdx].GetQ1() != 0));
632 sumCorr += probBC * slopeFactor * deltaBC;
633 sumCorr2 += probBC * slopeFactor * deltaBC * slopeFactor * deltaBC;
634 sumProb += probBC;
635 if (probBC > maxProb) {
636 maxProb = probBC;
637 yCorrPileUp = -slopeFactor * deltaBC;
638 zShiftTrkPileUp = -deltaBC * o2::constants::lhc::LHCBunchSpacingMUS * mTPCVdrift * mTrackAttribs[iTrk].mSide;
639 }
640 }
641 if (sumProb > 1e-6f) {
642 yAddErrPileUp2 = sumCorr2 / sumProb - 2 * yCorrPileUp * sumCorr / sumProb + yCorrPileUp * yCorrPileUp;
643 }
644 }
645
646 // number of tracklets within the chamber is the current TRD occupancy estimator
647 int nTrackletsChamber = mTrackletIndexArray[trkltIdxOffset + currDet + 1] - mTrackletIndexArray[trkltIdxOffset + currDet];
648 float angularPull = GetAngularPull(spacePoints[trkltIdx].getDy() + dyTiltCorr, trkWork->getSnp(), nTrackletsChamber);
649
650 // correction for mean z position of tracklet (is not the center of the pad if track eta != 0)
651 float zPosCorr = spacePoints[trkltIdx].getZ() + mRecoParam->getZCorrCoeffNRC() * trkWork->getTgl();
652 float yPosCorr = spacePoints[trkltIdx].getY() - tiltCorr + yCorrPileUp;
653 zPosCorr -= zShiftTrk + zShiftTrkPileUp; // shift tracklet instead of track in order to avoid having to do a re-fit for each collision
654
655 // Correction of y position based on angular pull
656 if (Param().rec.trd.useAngularPull == 3 || Param().rec.trd.useAngularPull == 4) {
657 float corrPull = -angularPull * mRecoParam->getCorrYDy(trkWork->getSnp());
658 yPosCorr += corrPull;
659 }
660
661 float deltaY = yPosCorr - projY;
662 float deltaZ = zPosCorr - projZ;
663
664 float trkltPosTmpYZ[2] = {yPosCorr, zPosCorr};
665 float trkltCovTmp[3] = {0.f};
666 if ((CAMath::Abs(deltaY) < roadY) && (CAMath::Abs(deltaZ) < roadZ)) { // TODO: check if this is still necessary after the cut before propagation of track
667 // tracklet is in window: get predicted chi2 for update and store tracklet index if best guess
668 RecalcTrkltCov(tilt, trkWork->getSnp(), pad->GetRowSize(tracklets[trkltIdx].GetZbin()), (Param().rec.trd.useAngularPull == 2 || Param().rec.trd.useAngularPull == 4 ? angularPull : 0.f), nTrackletsChamber, trkltCovTmp);
669 trkltCovTmp[0] += yAddErrPileUp2;
670 float chi2 = prop->getPredictedChi2(trkltPosTmpYZ, trkltCovTmp);
671
672 if (Param().rec.trd.addDeflectionInChi2 >= 1 && (trkWork->getSnp() < 1.f - 1e-6f) && (trkWork->getSnp() > -1.f + 1e-6f)) {
673 // we add the slope in the chi2 calculation
674 float trkltCovTmpWithDy[6] = {trkltCovTmp[0], trkltCovTmp[1], trkltCovTmp[2], 0.f, 0.f, 0.f};
675 RecalcTrkltCovDy(tilt, trkWork->getSnp(), (Param().rec.trd.useAngularPull == 2 || Param().rec.trd.useAngularPull == 4 ? angularPull : 0.f), nTrackletsChamber, trkltCovTmpWithDy);
676 trkltCovTmpWithDy[0] += trkWork->getSigmaY2();
677 trkltCovTmpWithDy[1] += trkWork->getSigmaZY();
678 trkltCovTmpWithDy[2] += trkWork->getSigmaZ2();
679
680 if (Param().rec.trd.useAngularPull == 3 || Param().rec.trd.useAngularPull == 4) {
681 // In this case the correlation between tracklet y and tracklet dy is already corrected for so we can cancel it
682 trkltCovTmpWithDy[3] = 0.;
683 trkltCovTmpWithDy[4] = 0.;
684 }
685
686 // We add the correlation between track y and track dy
687 trkltCovTmpWithDy[3] += trkWork->getSigmaSnpY() * mGeo->GetCdrHght();
688 trkltCovTmpWithDy[4] += trkWork->getSigmaSnpZ() * mGeo->GetCdrHght();
689
690 // keep these parameters before matrix inversion
691 float sigmaZ2 = trkltCovTmpWithDy[2];
692 float sigmaDy2 = trkltCovTmpWithDy[5];
693
694 // For now, dy uncertainty parametrization also includes track uncertainty, so no need to add additional uncertainty
695 if (InvertCov(trkltCovTmpWithDy)) {
696 float deltaDy = spacePoints[trkltIdx].getDy() + dyTiltCorr - mRecoParam->convertAngleToDy(trkWork->getSnp());
697 if (Param().rec.trd.addDeflectionInChi2 == 2 || Param().rec.trd.addDeflectionInChi2 == 3) {
698 // In this case we take into account the full likelihood, so we replace (deltaDy/sigmaDy)^2 by -2*ln(likelihood), which is the same in the default Gaussian case
699 double likelihood = mRecoParam->getDyLikelihood(trkWork->getSnp(), spacePoints[trkltIdx].getDy() + dyTiltCorr, nTrackletsChamber);
700 if (likelihood < 1e-6f) {
701 continue; // likelihood of 1e-6 is equivalent to 5 sigma deviation, so we can safely cut it to avoid numerical instability in log calculation
702 }
703 deltaDy = CAMath::Sqrt(-2.f * CAMath::Log(likelihood) * sigmaDy2) * (deltaDy > 0.f ? 1.f : -1.f);
704 }
705 if (Param().rec.trd.addDeflectionInChi2 == 3) {
706 // We do the same for deltaZ
707 double likelihood = mRecoParam->getZLikelihood(deltaZ, pad->GetRowSize(tracklets[trkltIdx].GetZbin()), CAMath::Sqrt(trkWork->getSigmaZ2()));
708 if (likelihood < 1e-6f) {
709 continue;
710 }
711 deltaZ = CAMath::Sqrt(-2.f * CAMath::Log(likelihood) * sigmaZ2) * (deltaZ > 0.f ? 1.f : -1.f);
712 }
713 chi2 = deltaY * trkltCovTmpWithDy[0] * deltaY + 2 * deltaY * trkltCovTmpWithDy[1] * deltaZ + 2 * deltaY * trkltCovTmpWithDy[3] * deltaDy + deltaZ * trkltCovTmpWithDy[2] * deltaZ + 2 * deltaZ * trkltCovTmpWithDy[4] * deltaDy + deltaDy * trkltCovTmpWithDy[5] * deltaDy;
714 }
715 }
716 // TODO cut on angular pull should be made stricter when proper v-drift calibration for the TRD tracklets is implemented
717 if ((chi2 > Param().rec.trd.maxChi2) || (Param().rec.trd.applyDeflectionCut && CAMath::Abs(angularPull) > 4)) {
718 continue;
719 }
720 Hypothesis hypo(trkWork->getNlayersFindable(), iCandidate, trkltIdx, trkWork->getChi2() + chi2);
721 InsertHypothesis(hypo, nCurrHypothesis, hypothesisIdxOffset);
722 } // end tracklet in window
723 } // tracklet loop
724 } // chamber loop
725
726 // add no update to hypothesis list
727 Hypothesis hypoNoUpdate(trkWork->getNlayersFindable(), iCandidate, -1, trkWork->getChi2() + Param().rec.trd.penaltyChi2);
728 InsertHypothesis(hypoNoUpdate, nCurrHypothesis, hypothesisIdxOffset);
729 isOK = true;
730 } // end candidate loop
731
732 mDebug->SetChi2Update(mHypothesis[0 + hypothesisIdxOffset].mChi2 - t->getChi2(), iLayer); // only meaningful for ONE candidate!!!
733 mDebug->SetRoad(roadY, roadZ, iLayer); // only meaningful for ONE candidate
734 bool wasTrackStored = false;
735 // --------------------------------------------------------------------------------
736 //
737 // loop over the best N_candidates hypothesis
738 //
739 // --------------------------------------------------------------------------------
740 // GPUInfo("nCurrHypothesis=%i, nCandidates=%i", nCurrHypothesis, nCandidates);
741 // for (int32_t idx=0; idx<10; ++idx) { GPUInfo("mHypothesis[%i]: candidateId=%i, nLayers=%i, trackletId=%i, chi2=%f", idx, mHypothesis[idx].mCandidateId, mHypothesis[idx].mLayers, mHypothesis[idx].mTrackletId, mHypothesis[idx].mChi2); }
742 for (int32_t iUpdate = 0; iUpdate < nCurrHypothesis && iUpdate < mNCandidates; iUpdate++) {
743 if (mHypothesis[iUpdate + hypothesisIdxOffset].mCandidateId == -1) {
744 // no more candidates
745 if (iUpdate == 0) {
746 return false; // no valid candidates for this track (probably propagation failed)
747 }
748 break; // go to next layer
749 }
750 nCandidates = iUpdate + 1;
751 if (mNCandidates > 1) {
752 mCandidates[2 * iUpdate + nextIdx] = mCandidates[2 * mHypothesis[iUpdate + hypothesisIdxOffset].mCandidateId + currIdx];
753 trkWork = &mCandidates[2 * iUpdate + nextIdx];
754 }
755 if (mHypothesis[iUpdate + hypothesisIdxOffset].mTrackletId == -1) {
756 // no matching tracklet found
757 if (trkWork->getIsFindable(iLayer)) {
758 if (trkWork->getNmissingConsecLayers(iLayer) > Param().rec.trd.stopTrkAfterNMissLy) {
759 trkWork->setIsStopped();
760 }
761 trkWork->setChi2(trkWork->getChi2() + Param().rec.trd.penaltyChi2);
762 }
763 if (iUpdate == 0 && mNCandidates > 1) { // TODO: is thie really necessary????? CHECK!
764 *t = mCandidates[2 * iUpdate + nextIdx];
765 }
766 continue;
767 }
768 // matching tracklet found
769 if (mNCandidates > 1) {
770 prop->setTrack(trkWork);
771 }
772 int32_t trkltSec = mGeo->GetSector(tracklets[mHypothesis[iUpdate + hypothesisIdxOffset].mTrackletId].GetDetector());
773 if (trkltSec != GetSector(prop->getAlpha())) {
774 // if after a matching tracklet was found another sector was searched for tracklets the track needs to be rotated back
775 prop->rotate(GetAlphaOfSector(trkltSec));
776 }
777 if (!prop->propagateToX(spacePoints[mHypothesis[iUpdate + hypothesisIdxOffset].mTrackletId].getX(), .8f, 2.f)) {
778 if (ENABLE_WARNING) {
779 GPUWarning("Final track propagation for track %i update %i in layer %i failed", iTrk, iUpdate, iLayer);
780 }
781 trkWork->setChi2(trkWork->getChi2() + Param().rec.trd.penaltyChi2);
782 if (trkWork->getIsFindable(iLayer)) {
783 if (trkWork->getNmissingConsecLayers(iLayer) >= Param().rec.trd.stopTrkAfterNMissLy) {
784 trkWork->setIsStopped();
785 }
786 }
787 if (iUpdate == 0 && mNCandidates > 1) {
788 *t = mCandidates[2 * iUpdate + nextIdx];
789 }
790 continue;
791 }
792
793 pad = mGeo->GetPadPlane(tracklets[mHypothesis[iUpdate + hypothesisIdxOffset].mTrackletId].GetDetector());
794 float tiltCorrUp = tilt * (spacePoints[mHypothesis[iUpdate + hypothesisIdxOffset].mTrackletId].getZ() - trkWork->getZ());
795 float dyTiltCorr = tilt * trkWork->getTgl() * mGeo->GetCdrHght();
796
797 float yPosCorrUp = spacePoints[mHypothesis[iUpdate + hypothesisIdxOffset].mTrackletId].getY() - tiltCorrUp;
798
799 float zPosCorrUp = spacePoints[mHypothesis[iUpdate + hypothesisIdxOffset].mTrackletId].getZ() + mRecoParam->getZCorrCoeffNRC() * trkWork->getTgl();
800 zPosCorrUp -= zShiftTrk;
801 float padLength = pad->GetRowSize(tracklets[mHypothesis[iUpdate + hypothesisIdxOffset].mTrackletId].GetZbin());
802 if (!((trkWork->getSigmaZ2() < (padLength * padLength / 12.f)) && (CAMath::Abs(spacePoints[mHypothesis[iUpdate + hypothesisIdxOffset].mTrackletId].getZ() - trkWork->getZ()) < padLength))) {
803 tiltCorrUp = 0.f;
804 dyTiltCorr = 0.f;
805 }
806
807 // Correction for pile-up: if the track comes from a pile-up event, it should not be extrapolated to the anode plane at t=0,
808 // contrary to the assumption when tracklet is reconstructed, so we cancel this extrapolation.
809 // The correction is extracted from the most probable trigger. There is also an additional error depending on all the compatible triggers
810 float yCorrPileUp = 0.f;
811 float yAddErrPileUp2 = 0.f;
812 float zShiftTrkPileUp = 0.f;
813 if (nIdxBCMax - nIdxBCMin >= 2) {
814 float maxProb = 0.f;
815 // The uncertainty is the RMS wrt the default correction of all possible corrections weighted by their probability
816 float sumCorr = 0.f;
817 float sumCorr2 = 0.f;
818 float sumProb = 0.f;
819 // conversion from slope in pad per time bin to slope in cm per BC = tracklets[trkltIdx].getSlopeFloat() * padWidth / BCperTimeBin
820 float slopeFactor = tracklets[mHypothesis[iUpdate + hypothesisIdxOffset].mTrackletId].GetSlopeFloat() * mGeo->GetPadPlaneWidthIPad(tracklets[mHypothesis[iUpdate + hypothesisIdxOffset].mTrackletId].GetDetector()) / 4.f;
821 for (int32_t iBC = nIdxBCMin; iBC < nIdxBCMax; iBC++) {
822 int32_t deltaBC = CAMath::Round(mFT0TriggeredBC[iBC] - GetConstantMem()->ioPtrs.trdTriggerTimes[collisionId] / o2::constants::lhc::LHCBunchSpacingMUS);
823 float probBC = mRecoParam->getPileUpProbTracklet(deltaBC, true, (tracklets[mHypothesis[iUpdate + hypothesisIdxOffset].mTrackletId].GetQ0() != 0), (tracklets[mHypothesis[iUpdate + hypothesisIdxOffset].mTrackletId].GetQ1() != 0));
824 sumCorr += probBC * slopeFactor * deltaBC;
825 sumCorr2 += probBC * slopeFactor * deltaBC * slopeFactor * deltaBC;
826 sumProb += probBC;
827 if (probBC > maxProb) {
828 maxProb = probBC;
829 yCorrPileUp = -slopeFactor * deltaBC;
830 zShiftTrkPileUp = -deltaBC * o2::constants::lhc::LHCBunchSpacingMUS * mTPCVdrift * mTrackAttribs[iTrk].mSide;
831 }
832 }
833 if (sumProb > 1e-6f) {
834 yAddErrPileUp2 = sumCorr2 / sumProb - 2 * yCorrPileUp * sumCorr / sumProb + yCorrPileUp * yCorrPileUp;
835 }
836 }
837
838 zPosCorrUp -= zShiftTrkPileUp;
839 yPosCorrUp += yCorrPileUp;
840
841 const auto currDet = tracklets[mHypothesis[iUpdate + hypothesisIdxOffset].mTrackletId].GetDetector();
842 int nTrackletsChamber = mTrackletIndexArray[trkltIdxOffset + currDet + 1] - mTrackletIndexArray[trkltIdxOffset + currDet];
843 float angularPull = GetAngularPull(spacePoints[mHypothesis[iUpdate + hypothesisIdxOffset].mTrackletId].getDy() + dyTiltCorr, trkWork->getSnp(), nTrackletsChamber);
844
845 // Correction of y position based on angular pull
846 if (Param().rec.trd.useAngularPull == 3 || Param().rec.trd.useAngularPull == 4) {
847 float corrPull = -angularPull * mRecoParam->getCorrYDy(trkWork->getSnp());
848 yPosCorrUp += corrPull;
849 }
850
851 float trkltPosUp[2] = {yPosCorrUp, zPosCorrUp};
852 float trkltCovUp[3] = {0.f};
853 RecalcTrkltCov(tilt, trkWork->getSnp(), pad->GetRowSize(tracklets[mHypothesis[iUpdate + hypothesisIdxOffset].mTrackletId].GetZbin()), ((Param().rec.trd.useAngularPull != 0) ? angularPull : 0.f), nTrackletsChamber, trkltCovUp);
854 trkltCovUp[0] += yAddErrPileUp2;
855
856#ifdef ENABLE_GPUTRDDEBUG
857 prop->setTrack(&trackNoUp);
858 prop->rotate(GetAlphaOfSector(trkltSec));
859 // prop->propagateToX(spacePoints[mHypothesis[iUpdate + hypothesisIdxOffset].mTrackletId].getX(), .8f, 2.f);
860 prop->propagateToX(mR[tracklets[mHypothesis[iUpdate + hypothesisIdxOffset].mTrackletId].GetDetector()], .8f, 2.f);
861 prop->setTrack(trkWork);
862#endif
863
864 if (!wasTrackStored) {
865#ifdef ENABLE_GPUTRDDEBUG
866 mDebug->SetTrackParameterNoUp(trackNoUp, iLayer);
867#endif
868 mDebug->SetTrackParameter(*trkWork, iLayer);
869 mDebug->SetRawTrackletPosition(spacePoints[mHypothesis[iUpdate + hypothesisIdxOffset].mTrackletId].getX(), spacePoints[mHypothesis[iUpdate + hypothesisIdxOffset].mTrackletId].getY(), spacePoints[mHypothesis[iUpdate + hypothesisIdxOffset].mTrackletId].getZ(), iLayer);
870 mDebug->SetCorrectedTrackletPosition(trkltPosUp, iLayer);
871 mDebug->SetTrackletCovariance(trkltCovUp, iLayer);
872 mDebug->SetTrackletProperties(spacePoints[mHypothesis[iUpdate + hypothesisIdxOffset].mTrackletId].getDy(), tracklets[mHypothesis[iUpdate + hypothesisIdxOffset].mTrackletId].GetDetector(), iLayer);
873 wasTrackStored = true;
874 }
875
876 if (!prop->update(trkltPosUp, trkltCovUp)) {
877 if (ENABLE_WARNING) {
878 GPUWarning("Failed to update track %i with space point in layer %i", iTrk, iLayer);
879 }
880 trkWork->setChi2(trkWork->getChi2() + Param().rec.trd.penaltyChi2);
881 if (trkWork->getIsFindable(iLayer)) {
882 if (trkWork->getNmissingConsecLayers(iLayer) >= Param().rec.trd.stopTrkAfterNMissLy) {
883 trkWork->setIsStopped();
884 }
885 }
886 if (iUpdate == 0 && mNCandidates > 1) {
887 *t = mCandidates[2 * iUpdate + nextIdx];
888 }
889 continue;
890 }
891 if (!trkWork->CheckNumericalQuality()) {
892 if (ENABLE_INFO) {
893 GPUInfo("Track %i has invalid covariance matrix. Aborting track following\n", iTrk);
894 }
895 return false;
896 }
897 trkWork->addTracklet(iLayer, mHypothesis[iUpdate + hypothesisIdxOffset].mTrackletId);
898 trkWork->setChi2(mHypothesis[iUpdate + hypothesisIdxOffset].mChi2);
899 trkWork->setIsFindable(iLayer);
900 trkWork->setCollisionId(collisionId);
901 // check if track crosses padrows
902 float projZEntry, projYEntry;
903 // Get Z for Entry of Track
904 prop->getPropagatedYZ(trkWork->getX() - mGeo->GetCdrHght(), projYEntry, projZEntry);
905 // Get Padrow number for Exit&Entry and compare. If is not equal mark
906 // as padrow crossing. While simple, this comes with the cost of not
907 // properly handling Tracklets that hit a different Pad but still get
908 // attached to the Track. It is estimated that the probability for
909 // this is low.
910 const auto padrowEntry = pad->GetPadRowNumber(projZEntry);
911 const auto padrowExit = pad->GetPadRowNumber(trkWork->getZ());
912 if (padrowEntry != padrowExit) {
913 trkWork->setIsCrossingNeighbor(iLayer);
914 trkWork->setHasPadrowCrossing();
915 }
916
917 // Mark tracklets as Padrow crossing if they have a neighboring tracklet.
918 for (int32_t trkltIdx = glbTrkltIdxOffset + mTrackletIndexArray[trkltIdxOffset + currDet]; trkltIdx < glbTrkltIdxOffset + mTrackletIndexArray[trkltIdxOffset + currDet + 1]; ++trkltIdx) {
919 // skip orig tracklet
920 if (mHypothesis[iUpdate + hypothesisIdxOffset].mTrackletId == trkltIdx) {
921 continue;
922 }
923 if (CAMath::Abs(tracklets[mHypothesis[iUpdate + hypothesisIdxOffset].mTrackletId].GetZbin() - tracklets[trkltIdx].GetZbin()) == 1 &&
924 CAMath::Abs(tracklets[mHypothesis[iUpdate + hypothesisIdxOffset].mTrackletId].GetY() - tracklets[trkltIdx].GetY()) < 1) {
925 trkWork->setIsCrossingNeighbor(iLayer);
926 trkWork->setHasNeighbor();
927 break;
928 }
929 }
930 if (iUpdate == 0 && mNCandidates > 1) {
931 *t = mCandidates[2 * iUpdate + nextIdx];
932 }
933
934 } // end update loop
935
936 if (!isOK) {
937 if (ENABLE_INFO) {
938 GPUInfo("Track %i cannot be followed. Stopped in layer %i", iTrk, iLayer);
939 }
940 return false;
941 }
942 } // end layer loop
943
944 // --------------------------------------------------------------------------------
945 // add some debug information (compare labels of attached tracklets to track label)
946 // and store full track information
947 // --------------------------------------------------------------------------------
948 if (mDebugOutput) {
949 mDebug->SetTrack(*t);
950 mDebug->Output();
951 }
952 if (ENABLE_INFO) {
953 GPUInfo("Ended track following for track %i at x=%f with pt=%f. Attached %i tracklets", t->getRefGlobalTrackIdRaw(), t->getX(), t->getPt(), t->getNtracklets());
954 }
955 if (nCurrHypothesis > 1) {
956 if (CAMath::Abs(mHypothesis[hypothesisIdxOffset + 1].GetReducedChi2() - mHypothesis[hypothesisIdxOffset].GetReducedChi2()) < Param().rec.trd.chi2SeparationCut) {
957 t->setIsAmbiguous();
958 }
959 }
960 return true;
961}
962
963template <class TRDTRK, class PROP>
964GPUd() void GPUTRDTracker_t<TRDTRK, PROP>::InsertHypothesis(Hypothesis hypo, int32_t& nCurrHypothesis, int32_t idxOffset)
965{
966 // Insert hypothesis into the array. If the array is full and the reduced chi2 is worse
967 // than the worst hypothesis in the array it is dropped.
968 // The hypothesis array is always sorted.
969
970 if (nCurrHypothesis == 0) {
971 // this is the first hypothesis in the array
972 mHypothesis[idxOffset] = hypo;
973 ++nCurrHypothesis;
974 } else if (nCurrHypothesis > 0 && nCurrHypothesis < mNCandidates) {
975 // insert the hypothesis into the right position and shift all worse hypothesis to the right
976 for (int32_t i = idxOffset; i < nCurrHypothesis + idxOffset; ++i) {
977 if (hypo.GetReducedChi2() < mHypothesis[i].GetReducedChi2()) {
978 for (int32_t k = nCurrHypothesis + idxOffset; k > i; --k) {
979 mHypothesis[k] = mHypothesis[k - 1];
980 }
981 mHypothesis[i] = hypo;
982 ++nCurrHypothesis;
983 return;
984 }
985 }
986 mHypothesis[nCurrHypothesis + idxOffset] = hypo;
987 ++nCurrHypothesis;
988 return;
989 } else {
990 // array is already full, check if new hypothesis should be inserted
991 int32_t i = nCurrHypothesis + idxOffset - 1;
992 for (; i >= idxOffset; --i) {
993 if (mHypothesis[i].GetReducedChi2() < hypo.GetReducedChi2()) {
994 break;
995 }
996 }
997 if (i < (nCurrHypothesis + idxOffset - 1)) {
998 // new hypothesis should be inserted into the array
999 for (int32_t k = nCurrHypothesis + idxOffset - 1; k > i + 1; --k) {
1000 mHypothesis[k] = mHypothesis[k - 1];
1001 }
1002 mHypothesis[i + 1] = hypo;
1003 }
1004 }
1005}
1006
1007template <class TRDTRK, class PROP>
1008GPUd() int32_t GPUTRDTracker_t<TRDTRK, PROP>::GetDetectorNumber(const float zPos, const float alpha, const int32_t layer) const
1009{
1010 //--------------------------------------------------------------------
1011 // if track position is within chamber return the chamber number
1012 // otherwise return -1
1013 //--------------------------------------------------------------------
1014 int32_t stack = mGeo->GetStack(zPos, layer);
1015 if (stack < 0) {
1016 return -1;
1017 }
1018 int32_t sector = GetSector(alpha);
1019
1020 return mGeo->GetDetector(layer, stack, sector);
1021}
1022
1023template <class TRDTRK, class PROP>
1024GPUd() bool GPUTRDTracker_t<TRDTRK, PROP>::AdjustSector(PROP* prop, TRDTRK* t) const
1025{
1026 //--------------------------------------------------------------------
1027 // rotate track in new sector if necessary and
1028 // propagate to previous x afterwards
1029 // cancel if track crosses two sector boundaries
1030 //--------------------------------------------------------------------
1031 float alpha = mGeo->GetAlpha();
1032 float xTmp = t->getX();
1033 float y = t->getY();
1034 float yMax = t->getX() * CAMath::Tan(0.5f * alpha);
1035 float alphaCurr = t->getAlpha();
1036
1037 if (CAMath::Abs(y) > 2.f * yMax) {
1038 if (ENABLE_INFO) {
1039 GPUInfo("AdjustSector: Track %i with pT = %f crossing two sector boundaries at x = %f", t->getRefGlobalTrackIdRaw(), t->getPt(), t->getX());
1040 }
1041 return false;
1042 }
1043
1044 int32_t nTries = 0;
1045 while (CAMath::Abs(y) > yMax) {
1046 if (nTries >= 2) {
1047 return false;
1048 }
1049 int32_t sign = (y > 0) ? 1 : -1;
1050 float alphaNew = alphaCurr + alpha * sign;
1051 if (alphaNew > CAMath::Pi()) {
1052 alphaNew -= 2 * CAMath::Pi();
1053 } else if (alphaNew < -CAMath::Pi()) {
1054 alphaNew += 2 * CAMath::Pi();
1055 }
1056 if (!prop->rotate(alphaNew)) {
1057 return false;
1058 }
1059 if (!prop->propagateToX(xTmp, .8f, 2.f)) {
1060 return false;
1061 }
1062 y = t->getY();
1063 ++nTries;
1064 }
1065 return true;
1066}
1067
1068template <class TRDTRK, class PROP>
1069GPUd() int32_t GPUTRDTracker_t<TRDTRK, PROP>::GetSector(float alpha) const
1070{
1071 //--------------------------------------------------------------------
1072 // TRD sector number for reference system alpha
1073 //--------------------------------------------------------------------
1074 if (alpha < 0) {
1075 alpha += 2.f * CAMath::Pi();
1076 } else if (alpha >= 2.f * CAMath::Pi()) {
1077 alpha -= 2.f * CAMath::Pi();
1078 }
1079 return (int32_t)(alpha * (float)kNSectors / (2.f * CAMath::Pi()));
1080}
1081
1082template <class TRDTRK, class PROP>
1083GPUd() float GPUTRDTracker_t<TRDTRK, PROP>::GetAlphaOfSector(const int32_t sec) const
1084{
1085 //--------------------------------------------------------------------
1086 // rotation angle for TRD sector sec
1087 //--------------------------------------------------------------------
1088 float alpha = 2.0f * CAMath::Pi() / (float)kNSectors * ((float)sec + 0.5f);
1089 if (alpha > CAMath::Pi()) {
1090 alpha -= 2 * CAMath::Pi();
1091 }
1092 return alpha;
1093}
1094
1095template <class TRDTRK, class PROP>
1096GPUd() void GPUTRDTracker_t<TRDTRK, PROP>::RecalcTrkltCov(const float tilt, const float snp, const float rowSize, const float pull, const int occupancy, float (&cov)[3])
1097{
1098 //--------------------------------------------------------------------
1099 // recalculate tracklet covariance taking track phi angle into account
1100 // store the new values in cov
1101 //--------------------------------------------------------------------
1102 float t2 = tilt * tilt; // tan^2 (tilt)
1103 float c2 = 1.f / (1.f + t2); // cos^2 (tilt)
1104 float sy2 = mRecoParam->getRPhiRes(snp, CAMath::Abs(pull), occupancy);
1105 float sz2 = rowSize * rowSize / 12.f;
1106 cov[0] = c2 * (sy2 + t2 * sz2);
1107 cov[1] = c2 * tilt * (sz2 - sy2);
1108 cov[2] = c2 * (t2 * sy2 + sz2);
1109}
1110
1111template <class TRDTRK, class PROP>
1112GPUd() void GPUTRDTracker_t<TRDTRK, PROP>::RecalcTrkltCovDy(const float tilt, const float snp, const float pull, const int occupancy, float (&cov)[6])
1113{
1114 float t2 = tilt * tilt; // tan^2 (tilt)
1115 float c2 = 1.f / (1.f + t2); // cos^2 (tilt)
1116 // float sy2 = mRecoParam->getRPhiRes(snp, CAMath::Abs(pull), occupancy);
1117 float sdy2 = mRecoParam->getDyRes(snp, occupancy);
1118 cov[3] = mRecoParam->getCorrYDy(snp) * CAMath::Sqrt(sdy2 * c2);
1119 cov[4] = -tilt * mRecoParam->getCorrYDy(snp) * CAMath::Sqrt(sdy2 * c2);
1120 cov[5] = sdy2;
1121}
1122
1123template <class TRDTRK, class PROP>
1124GPUd() bool GPUTRDTracker_t<TRDTRK, PROP>::InvertCov(float (&cov)[6])
1125{
1126 // invert a 3*3 symmetric matrix. Adapted from https://root.cern.ch/doc/master/TMatrixTSymCramerInv_8cxx_source.html
1127
1128 float c00 = cov[2] * cov[5] - cov[4] * cov[4];
1129 float c01 = cov[4] * cov[3] - cov[1] * cov[5];
1130 float c02 = cov[1] * cov[4] - cov[2] * cov[3];
1131 float c11 = cov[5] * cov[0] - cov[3] * cov[3];
1132 float c12 = cov[3] * cov[1] - cov[4] * cov[0];
1133 float c22 = cov[0] * cov[2] - cov[1] * cov[1];
1134
1135 float t0 = CAMath::Abs(cov[0]);
1136 float t1 = CAMath::Abs(cov[1]);
1137 float t2 = CAMath::Abs(cov[3]);
1138
1139 float det;
1140 float tmp;
1141
1142 if (t0 >= t1) {
1143 if (t2 >= t0) {
1144 tmp = cov[3];
1145 det = c12 * c01 - c11 * c02;
1146 } else {
1147 tmp = cov[0];
1148 det = c11 * c22 - c12 * c12;
1149 }
1150 } else if (t2 >= t1) {
1151 tmp = cov[3];
1152 det = c12 * c01 - c11 * c02;
1153 } else {
1154 tmp = cov[1];
1155 det = c02 * c12 - c01 * c22;
1156 }
1157
1158 if (det == 0 || tmp == 0) {
1159 return false;
1160 }
1161
1162 float s = tmp / det;
1163
1164 cov[0] = s * c00;
1165 cov[1] = s * c01;
1166 cov[3] = s * c02;
1167 cov[2] = s * c11;
1168 cov[4] = s * c12;
1169 cov[5] = s * c22;
1170
1171 return true;
1172}
1173
1174template <class TRDTRK, class PROP>
1175GPUd() float GPUTRDTracker_t<TRDTRK, PROP>::GetAngularPull(float dYtracklet, float snp, int occupancy) const
1176{
1177 float dYtrack = mRecoParam->convertAngleToDy(snp);
1178 float dYresolution = mRecoParam->getDyRes(snp, occupancy);
1179 if (dYresolution < 1e-6f) {
1180 return 999.f;
1181 }
1182 return (dYtracklet - dYtrack) / CAMath::Sqrt(dYresolution);
1183}
1184
1185template <class TRDTRK, class PROP>
1186GPUd() int GPUTRDTracker_t<TRDTRK, PROP>::GetNtrackletsChamber(int collisionId, int detector) const
1187{
1188 // get the number of tracklets for a given chamber and trigger
1189 int32_t trkltIdxOffset = collisionId * (kNChambers + 1);
1190 int nTrackletsChamber = mTrackletIndexArray[trkltIdxOffset + detector + 1] - mTrackletIndexArray[trkltIdxOffset + detector];
1191 return nTrackletsChamber;
1192}
1193
1194template <class TRDTRK, class PROP>
1195GPUd() void GPUTRDTracker_t<TRDTRK, PROP>::FindChambersInRoad(const TRDTRK* t, const float roadY, const float roadZ, const int32_t iLayer, int32_t* det, const float zMax, const float alpha, const float zShiftTrk) const
1196{
1197 //--------------------------------------------------------------------
1198 // determine initial chamber where the track ends up
1199 // add more chambers of the same sector or (and) neighbouring
1200 // stack if track is close the edge(s) of the chamber
1201 //--------------------------------------------------------------------
1202
1203 const float yMax = CAMath::Abs(mGeo->GetCol0(iLayer));
1204 float zTrk = t->getZ() + zShiftTrk;
1205
1206 int32_t currStack = mGeo->GetStack(zTrk, iLayer);
1207 int32_t currSec = GetSector(alpha);
1208 int32_t currDet;
1209
1210 int32_t nDets = 0;
1211
1212 if (currStack > -1) {
1213 // chamber unambiguous
1214 currDet = mGeo->GetDetector(iLayer, currStack, currSec);
1215 det[nDets++] = currDet;
1216 const GPUTRDpadPlane* pp = mGeo->GetPadPlane(iLayer, currStack);
1217 int32_t lastPadRow = mGeo->GetRowMax(iLayer, currStack, 0);
1218 float zCenter = pp->GetRowPos(lastPadRow / 2);
1219 if ((zTrk + roadZ) > pp->GetRow0() || (zTrk - roadZ) < pp->GetRowEnd()) {
1220 int32_t addStack = zTrk > zCenter ? currStack - 1 : currStack + 1;
1221 if (addStack < kNStacks && addStack > -1) {
1222 det[nDets++] = mGeo->GetDetector(iLayer, addStack, currSec);
1223 }
1224 }
1225 } else {
1226 if (CAMath::Abs(zTrk) > zMax) {
1227 // shift track in z so it is in the TRD acceptance
1228 if (zTrk > 0) {
1229 currDet = mGeo->GetDetector(iLayer, 0, currSec);
1230 } else {
1231 currDet = mGeo->GetDetector(iLayer, kNStacks - 1, currSec);
1232 }
1233 det[nDets++] = currDet;
1234 currStack = mGeo->GetStack(currDet);
1235 } else {
1236 // track in between two stacks, add both surrounding chambers
1237 // gap between two stacks is 4 cm wide
1238 currDet = GetDetectorNumber(zTrk + 4.0f, alpha, iLayer);
1239 if (currDet != -1) {
1240 det[nDets++] = currDet;
1241 }
1242 currDet = GetDetectorNumber(zTrk - 4.0f, alpha, iLayer);
1243 if (currDet != -1) {
1244 det[nDets++] = currDet;
1245 }
1246 }
1247 }
1248 // add chamber(s) from neighbouring sector in case the track is close to the boundary
1249 if ((CAMath::Abs(t->getY()) + roadY) > yMax) {
1250 const int32_t nStacksToSearch = nDets;
1251 int32_t newSec;
1252 if (t->getY() > 0) {
1253 newSec = (currSec + 1) % kNSectors;
1254 } else {
1255 newSec = (currSec > 0) ? currSec - 1 : kNSectors - 1;
1256 }
1257 for (int32_t idx = 0; idx < nStacksToSearch; ++idx) {
1258 currStack = mGeo->GetStack(det[idx]);
1259 det[nDets++] = mGeo->GetDetector(iLayer, currStack, newSec);
1260 }
1261 }
1262 // skip PHOS hole and non-existing chamber 17_4_4
1263 for (int32_t iDet = 0; iDet < nDets; iDet++) {
1264 if (!mGeo->ChamberInGeometry(det[iDet])) {
1265 det[iDet] = -1;
1266 }
1267 }
1268}
1269
1270template <class TRDTRK, class PROP>
1271GPUd() bool GPUTRDTracker_t<TRDTRK, PROP>::IsGeoFindable(const TRDTRK* t, const int32_t layer, const float alpha, const float zShiftTrk) const
1272{
1273 //--------------------------------------------------------------------
1274 // returns true if track position inside active area of the TRD
1275 // and not too close to the boundaries
1276 //--------------------------------------------------------------------
1277
1278 float zTrk = t->getZ() + zShiftTrk;
1279
1280 int32_t det = GetDetectorNumber(zTrk, alpha, layer);
1281
1282 // reject tracks between stacks
1283 if (det < 0) {
1284 return false;
1285 }
1286
1287 // reject tracks in PHOS hole and for non existent chamber 17_4_4
1288 if (!mGeo->ChamberInGeometry(det)) {
1289 return false;
1290 }
1291
1292 // reject tracks in bad chambers
1293 if (mChamberStatus[det]) {
1294 return false;
1295 }
1296
1297 const GPUTRDpadPlane* pp = mGeo->GetPadPlane(det);
1298 float yMax = pp->GetColEnd();
1299 float zMax = pp->GetRow0();
1300 float zMin = pp->GetRowEnd();
1301
1302 float epsY = 5.f;
1303 float epsZ = 5.f;
1304
1305 // reject tracks closer than epsY cm to pad plane boundary
1306 if (yMax - CAMath::Abs(t->getY()) < epsY) {
1307 return false;
1308 }
1309 // reject tracks closer than epsZ cm to stack boundary
1310 if (!((zTrk > zMin + epsZ) && (zTrk < zMax - epsZ))) {
1311 return false;
1312 }
1313
1314 // reject tracks if the corresponding pad and neighboring pads (to take into account track uncertainty and charge sharing with neighbours) are masked
1315 /*int32_t padrow = pp->GetPadRowNumber(zTrk);
1316 int32_t padcol = pp->GetPadColNumber(t->getY());
1317 int32_t idxPad = det * kNPadColumns * kNPadRows + padcol * kNPadRows + padrow;
1318 if (padrow != -1 && padcol != -1 && mPadStatus[idxPad] && (padrow == 0 || mPadStatus[idxPad - kNPadRows]) && (padrow == kNPadRows - 1 || mPadStatus[idxPad + kNPadRows])) {
1319 return false;
1320 }*/
1321
1322 return true;
1323}
1324
1325#ifndef GPUCA_GPUCODE
1326namespace o2::gpu
1327{
1330} // namespace o2::gpu
1331#endif
int32_t i
float float float & zMax
float & yMax
For performance analysis + error parametrization of the TRD tracker.
#define ENABLE_INFO
#define ENABLE_WARNING
Online TRD tracker based on extrapolated TPC tracks.
TRD Tracklet word for GPU tracker - 32bit tracklet info + half chamber ID + index.
float chi2
float zMin
uint32_t stack
Definition RawData.h:1
GPUTrackingInOutPointers & mIOPtrs
void * SetPointersTracks(void *base)
void * SetPointersBase(void *base)
void SetNCandidates(int32_t n)
void SetMaxData(const GPUTrackingInOutPointers &io)
void PrepareTracking(GPUChainTracking *chainTracking)
void * SetPointersTracklets(void *base)
GLdouble n
Definition glcorearb.h:1982
GLfloat GLfloat GLfloat alpha
Definition glcorearb.h:279
GLuint64EXT * result
Definition glcorearb.h:5662
GLint y
Definition glcorearb.h:270
typedef void(APIENTRYP PFNGLCULLFACEPROC)(GLenum mode)
GLuint GLfloat x0
Definition glcorearb.h:5034
GLenum GLuint GLint GLint layer
Definition glcorearb.h:1310
GLuint GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat t0
Definition glcorearb.h:5034
GLuint GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat t1
Definition glcorearb.h:5034
GPUdi() o2
Definition TrackTRD.h:39
GPUd() const expr uint32_t MultivariatePolynomialHelper< Dim
double * getX(double *xyDxy, int N)
double * getY(double *xyDxy, int N)
bool isOK(const SanityError &error)
GPUReconstruction * rec
GPUChainTracking * chainTracking
const GPUTRDSpacePoint * trdSpacePoints
std::vector< Tracklet64 > tracklets