Project
Loading...
Searching...
No Matches
MatchTPCITS.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
12#include "GPUO2ExternalUser.h" // Needed for propper settings in GPUParam.h
13#include "GPUParam.h"
14#include "GPUParam.inc"
15#ifdef WITH_OPENMP
16#include <omp.h>
17#endif
18
19#include <TTree.h>
20#include <cassert>
21#include <algorithm>
22
23#include <fairlogger/Logger.h>
24#include "Field/MagneticField.h"
25#include "Field/MagFieldFast.h"
28
29#include "DataFormatsTPC/Defs.h"
32#include "MathUtils/Cartesian.h"
33#include "MathUtils/Utils.h"
39
40#include <Math/SMatrix.h>
41#include <Math/SVector.h>
42#include <TFile.h>
43#include <TGeoGlobalMagField.h>
54#include "ITStracking/IOUtils.h"
55
56#ifdef ENABLE_UPGRADES
58#endif
59
60using namespace o2::globaltracking;
61
67constexpr float MatchTPCITS::Tan70, MatchTPCITS::Cos70I2, MatchTPCITS::MaxSnp, MatchTPCITS::MaxTgp;
68
70
71const o2::gpu::GPUTPCGeometry MatchTPCITS::TPCGeometry{};
72
73//______________________________________________
74MatchTPCITS::MatchTPCITS() = default;
75
76//______________________________________________
78
79//______________________________________________
83 pmr::vector<int>& ABTrackletClusterIDs,
85 pmr::vector<o2::MCCompLabel>& ABTrackletLabels,
87{
89 if (!mInitDone) {
90 LOG(fatal) << "init() was not done yet";
91 }
92 clear();
93 mRecoCont = &inp;
94 mStartIR = inp.startIR;
95 updateTimeDependentParams();
96
97 mTimer[SWTot].Start(false);
98
99 while (1) {
100 if (!prepareITSData() || !prepareTPCData() || !prepareFITData()) {
101 break;
102 }
103 if (mVDriftCalibOn) { // in the beginning of the output vector we send the full and reference VDrift used for this TF
104 calib.emplace_back(mTPCVDrift, mTPCDrift.refVDrift, mTPCDrift.refTP);
105 calib.emplace_back(mTPCDriftTimeOffset, mTPCDrift.refTimeOffset, -999.);
106 }
107
108 mTimer[SWDoMatching].Start(false);
109 for (int sec = o2::constants::math::NSectors; sec--;) {
110 doMatching(sec);
111 }
112 mTimer[SWDoMatching].Stop();
113 if constexpr (false) { // enabling this creates very verbose output
114 mTimer[SWTot].Stop();
117 mTimer[SWTot].Start(false);
118 }
119
120 selectBestMatches();
121
122 bool fullMatchRefitDone = false;
123 if (mUseFT0 && mParams->runAfterBurner) {
124 fullMatchRefitDone = runAfterBurner(matchedTracks, matchLabels, ABTrackletLabels, ABTrackletClusterIDs, ABTrackletRefs, calib);
125 }
126 if (!fullMatchRefitDone) {
127 refitWinners(matchedTracks, matchLabels, calib); // it afterburner is active, full matches refit will be done by it
128 }
129 if (mParams->verbosity > 0) {
130 reportSizes(matchedTracks, ABTrackletRefs, ABTrackletClusterIDs, matchLabels, ABTrackletLabels, calib);
131 }
132#ifdef _ALLOW_DEBUG_TREES_
133 if (mDBGOut && isDebugFlag(WinnerMatchesTree)) {
135 }
136#endif
137 break;
138 }
139 mTimer[SWTot].Stop();
140
141 if (mParams->verbosity > 0) {
142 reportTiming();
143 }
144 mTFCount++;
145}
146
147//______________________________________________
149{
150 for (int i = 0; i < NStopWatches; i++) {
151 LOGF(info, "Timing for %15s: Cpu: %.3e Real: %.3e s in %d slots of TF#%d", TimerName[i], mTimer[i].CpuTime(), mTimer[i].RealTime(), mTimer[i].Counter() - 1, mTFCount);
152 }
153}
154
155//______________________________________________
157{
158#ifdef _ALLOW_DEBUG_TREES_
159 mDBGOut.reset();
160#endif
161}
162
163//______________________________________________
165{
167 mMatchRecordsTPC.clear();
168 mMatchRecordsITS.clear();
169 mWinnerChi2Refit.clear();
170 mITSWork.clear();
171 mTPCWork.clear();
172 mInteractions.clear();
173 mITSROFTimes.clear();
174 mITSTrackROFContMapping.clear();
175 mITSClustersArray.clear();
176 mITSClusterSizes.clear();
177 mTPCABSeeds.clear();
178 mTPCABIndexCache.clear();
179 mABWinnersIDs.clear();
180 mABClusterLinkIndex.clear();
181 mNMatchesControl = 0;
182
183 for (int sec = o2::constants::math::NSectors; sec--;) {
184 mITSSectIndexCache[sec].clear();
185 mITSTimeStart[sec].clear();
186 mTPCSectIndexCache[sec].clear();
187 mTPCTimeStart[sec].clear();
188 }
189
190 if (mMCTruthON) {
191 mTPCLblWork.clear();
192 mITSLblWork.clear();
193 }
194 for (int i = 0; i < mNThreads; i++) {
195 mABLinksPool.threadPool[i].clear();
196 }
197}
198
199//______________________________________________
201{
202 mTPCDrift = v;
203 mTPCVDrift = v.getVDrift();
204 mTPCDriftTimeOffset = v.getTimeOffset();
205}
206
207//______________________________________________
209{
210 mTPCCorrMapsHelper = maph;
211}
212
213//______________________________________________
215{
217 if (mInitDone) {
218 LOG(error) << "Initialization was already done";
219 return;
220 }
221 for (int i = NStopWatches; i--;) {
222 mTimer[i].Stop();
223 mTimer[i].Reset();
224 }
225 mParams = &Params::Instance();
226 YMaxAtXMatchingRef = mParams->XMatchingRef * 0.17632698;
227 mParams->printKeyValues();
228 mFT0Params = &o2::ft0::InteractionTag::Instance();
229 setUseMatCorrFlag(mParams->matCorr);
230 auto* prop = o2::base::Propagator::Instance();
231 if (!prop->getMatLUT() && mParams->matCorr == o2::base::Propagator::MatCorrType::USEMatCorrLUT) {
232 LOG(warning) << "Requested material LUT is not loaded, switching to TGeo usage";
233 setUseMatCorrFlag(o2::base::Propagator::MatCorrType::USEMatCorrTGeo);
234 }
235
236 // make sure T2GRot matrices are loaded into ITS geometry helper
238
239 mSectEdgeMargin = mParams->crudeAbsDiffCut[o2::track::kY] / std::sqrt(Cos70I2);
240
241#ifdef _ALLOW_DEBUG_TREES_
242 // debug streamer
243 if (mDBGFlags) {
244 mDBGOut = std::make_unique<o2::utils::TreeStreamRedirector>(mDebugTreeFileName.data(), "recreate");
245 }
246#endif
247
248 if (mParams->runAfterBurner) { // only used in AfterBurner
249 mRGHelper.init(mParams->lowestLayerAB); // prepare helper for TPC track / ITS clusters matching
250 }
251
252 clear();
253
254 mInitDone = true;
255
256 if (fair::Logger::Logging(fair::Severity::info)) {
257 print();
258 }
259}
260
261//______________________________________________
262void MatchTPCITS::updateTimeDependentParams()
263{
266 auto& detParam = o2::tpc::ParameterDetector::Instance();
267 mTPCTBinMUS = elParam.ZbinWidth;
268 mTPCTBinNS = mTPCTBinMUS * 1e3;
269 mTPCZMax = detParam.TPClength;
270 mTPCTBinMUSInv = 1. / mTPCTBinMUS;
271 assert(mITSROFrameLengthMUS > 0.0f);
272 mTPCBin2Z = mTPCTBinMUS * mTPCVDrift;
273 mZ2TPCBin = 1. / mTPCBin2Z;
274 mTPCVDriftInv = 1. / mTPCVDrift;
275 mNTPCBinsFullDrift = mTPCZMax * mZ2TPCBin;
276 mTPCTimeEdgeTSafeMargin = z2TPCBin(mParams->safeMarginTPCTimeEdge);
277 mTPCExtConstrainedNSigmaInv = 1.f / mParams->tpcExtConstrainedNSigma;
278 mBz = o2::base::Propagator::Instance()->getNominalBz();
279 mFieldON = std::abs(mBz) > 0.01;
280
281 mMinTPCTrackPtInv = (mFieldON && mParams->minTPCTrackR > 0) ? 1. / std::abs(mParams->minTPCTrackR * mBz * o2::constants::math::B2C) : 999.;
282 mMinITSTrackPtInv = (mFieldON && mParams->minITSTrackR > 0) ? 1. / std::abs(mParams->minITSTrackR * mBz * o2::constants::math::B2C) : 999.;
283
284 o2::math_utils::Point3D<float> p0(90., 1., 1), p1(90., 100., 100.);
285 auto matbd = o2::base::Propagator::Instance()->getMatBudget(mParams->matCorr, p0, p1);
286 mTPCmeanX0Inv = matbd.meanX2X0 / matbd.length;
287
288 const auto& trackTune = TrackTuneParams::Instance();
289 float scale = mTPCCorrMapsHelper->getInstLumiCTP();
290 if (scale < 0.f) {
291 scale = 0.f;
292 }
293 mCovDiagInner = trackTune.getCovInnerTotal(scale);
294 mCovDiagOuter = trackTune.getCovOuterTotal(scale);
295}
296
297//______________________________________________
298void MatchTPCITS::selectBestMatches()
299{
301 mTimer[SWSelectBest].Start(false);
302 int nValidated = 0, iter = 0;
303 mNMatches = 0;
304 mNCalibPrelim = 0;
305 do {
306 nValidated = 0;
307 int ntpc = mTPCWork.size(), nremaining = 0;
308 for (int it = 0; it < ntpc; it++) {
309 auto& tTPC = mTPCWork[it];
310 if (isDisabledTPC(tTPC) || isValidatedTPC(tTPC)) {
311 continue;
312 }
313 nremaining++;
314 if (validateTPCMatch(it)) {
315 nValidated++;
316 if (mVDriftCalibOn && (!mFieldON || std::abs(tTPC.getQ2Pt()) < mParams->maxVDriftTrackQ2Pt)) {
317 mNCalibPrelim++;
318 }
319 continue;
320 }
321 }
322 if (mParams->verbosity > 0) {
323 LOGP(info, "iter {}: Validated {} of {} remaining matches", iter, nValidated, nremaining);
324 }
325 iter++;
326 mNMatches += nValidated;
327 } while (nValidated);
328
329 mTimer[SWSelectBest].Stop();
330 LOGP(info, "Validated {} matches out of {} for {} TPC and {} ITS tracks in {} iterations", mNMatches, mNMatchesControl, mTPCWork.size(), mITSWork.size(), iter);
331}
332
333//______________________________________________
334bool MatchTPCITS::validateTPCMatch(int iTPC)
335{
336 auto& tTPC = mTPCWork[iTPC];
337 auto& rcTPC = mMatchRecordsTPC[tTPC.matchID]; // best TPC->ITS match
338 // check if it is consistent with corresponding ITS->TPC match
339 auto& tITS = mITSWork[rcTPC.partnerID]; // partner ITS track
340 auto& rcITS = mMatchRecordsITS[tITS.matchID]; // best ITS->TPC match record
341 if (rcITS.nextRecID == Validated) {
342 return false;
343 }
344 if (rcITS.partnerID == iTPC) { // is best matching TPC track for this ITS track actually iTPC?
345 int cloneID = tITS.getCloneShift(); // check if there is a clone of tITS
346 while (cloneID) {
347 cloneID += rcTPC.partnerID;
348 auto& tITSClone = mITSWork[cloneID];
349 if (isDisabledITS(tITSClone)) { // ignore clone
350 break;
351 }
352 int nextITSCloneMatchID = tITSClone.matchID;
353 if (rcITS.isBetter(mMatchRecordsITS[nextITSCloneMatchID])) { // best ITS->TPC match record for the clone is worse than the rcITS
354 LOGP(debug, "Suppressing clone cloneID={} of winner clone {} of source ITS {}", cloneID, rcTPC.partnerID, tITS.sourceID);
355 while (nextITSCloneMatchID > MinusOne) {
356 auto& rcITSClone = mMatchRecordsITS[nextITSCloneMatchID];
357 removeITSfromTPC(cloneID, rcITSClone.partnerID);
358 nextITSCloneMatchID = rcITSClone.nextRecID;
359 }
360 tITSClone.matchID = MinusTen; // disable
361 break;
362 }
363 return false; // ignore match at this iteration
364 }
365 // unlink winner TPC track from all ITS candidates except winning one
366 int nextTPC = rcTPC.nextRecID;
367 while (nextTPC > MinusOne) {
368 auto& rcTPCrem = mMatchRecordsTPC[nextTPC];
369 removeTPCfromITS(iTPC, rcTPCrem.partnerID); // remove references on mtID from ITS match=rcTPCrem.partnerID
370 nextTPC = rcTPCrem.nextRecID;
371 }
372 rcTPC.nextRecID = Validated;
373 int itsWinID = rcTPC.partnerID;
374
375 // unlink winner ITS match from all TPC matches using it
376 int nextITS = rcITS.nextRecID;
377 while (nextITS > MinusOne) {
378 auto& rcITSrem = mMatchRecordsITS[nextITS];
379 removeITSfromTPC(itsWinID, rcITSrem.partnerID); // remove references on itsWinID from TPC match=rcITSrem.partnerID
380 nextITS = rcITSrem.nextRecID;
381 }
382 rcITS.nextRecID = Validated;
383 tTPC.gid.setBit(0); // Flag full match
384 return true;
385 }
386 return false;
387}
388
389//______________________________________________
390int MatchTPCITS::getNMatchRecordsTPC(const TrackLocTPC& tTPC) const
391{
393 int count = 0, recID = tTPC.matchID;
394 while (recID > MinusOne) {
395 recID = mMatchRecordsTPC[recID].nextRecID;
396 count++;
397 }
398 return count;
399}
400
401//______________________________________________
402int MatchTPCITS::getNMatchRecordsITS(const TrackLocITS& tTPC) const
403{
405 int count = 0, recID = tTPC.matchID;
406 while (recID > MinusOne) {
407 auto& itsRecord = mMatchRecordsITS[recID];
408 recID = itsRecord.nextRecID;
409 count++;
410 }
411 return count;
412}
413
414//______________________________________________
415int MatchTPCITS::addTPCSeed(const o2::track::TrackParCov& _tr, float t0, float terr, GTrackID srcGID, int tpcID)
416{
417 // account single TPC seed, can be from standalone TPC track or constrained track from match to TRD and/or TOF
418 const float SQRT12DInv = 2. / sqrt(12.);
419 if (_tr.getX() > o2::constants::geom::XTPCInnerRef + 0.1 || std::abs(_tr.getQ2Pt()) > mMinTPCTrackPtInv) {
420 return -99;
421 }
422 const auto& tpcOrig = mTPCTracksArray[tpcID];
423 // discard tracks w/o certain number of total or innermost pads (last cluster is innermost one)
424 if (tpcOrig.getNClusterReferences() < mParams->minTPCClusters) {
425 return -89;
426 }
427 uint8_t clSect = 0, clRow = 0;
428 uint32_t clIdx = 0;
429 tpcOrig.getClusterReference(mTPCTrackClusIdx, tpcOrig.getNClusterReferences() - 1, clSect, clRow, clIdx);
430 if (clRow > mParams->askMinTPCRow[clSect]) {
431 return -9;
432 }
433 const auto& clus = mTPCClusterIdxStruct->clusters[clSect][clRow][clIdx];
434 uint8_t padFromEdge = uint8_t(clus.getPad());
435 if (padFromEdge > TPCGeometry.NPads(clRow) / 2) {
436 padFromEdge = TPCGeometry.NPads(clRow) - 1 - padFromEdge;
437 }
438
439 // create working copy of track param
440 bool extConstrained = srcGID.getSource() != GTrackID::TPC;
441 if (extConstrained) {
442 terr *= mParams->tpcExtConstrainedNSigma;
443 } else {
444 terr += tpcTimeBin2MUS(tpcOrig.hasBothSidesClusters() ? mParams->safeMarginTPCITSTimeBin : mTPCTimeEdgeTSafeMargin);
445 }
446 auto& trc = mTPCWork.emplace_back(
447 TrackLocTPC{_tr, {t0 - terr, t0 + terr}, extConstrained ? t0 : tpcTimeBin2MUS(tpcOrig.getTime0()),
448 // for A/C constrained tracks the terr is half-interval, for externally constrained tracks it is sigma*Nsigma
449 terr * (extConstrained ? mTPCExtConstrainedNSigmaInv : SQRT12DInv),
450 tpcID,
451 srcGID,
452 MinusOne,
453 clRow,
454 padFromEdge,
455 (extConstrained || tpcOrig.hasBothSidesClusters()) ? TrackLocTPC::Constrained : (tpcOrig.hasASideClustersOnly() ? TrackLocTPC::ASide : TrackLocTPC::CSide)});
456 // propagate to matching Xref
457 const auto& trackTune = TrackTuneParams::Instance();
458 // only TPC standalone need to be corrected on the input, provided they were not corrected at the source level,
459 // other inputs are corrected in respective upstream matching processes
460 if (srcGID.getSource() == GTrackID::TPC && !trackTune.sourceLevelTPC) {
461 if (trackTune.useTPCInnerCorr) {
462 trc.updateParams(trackTune.tpcParInner);
463 }
464 if (trackTune.tpcCovInnerType != TrackTuneParams::AddCovType::Disable) {
465 trc.updateCov(mCovDiagInner, trackTune.tpcCovInnerType == TrackTuneParams::AddCovType::WithCorrelations);
466 }
467 }
468 if (!propagateToRefX(trc)) {
469 mTPCWork.pop_back(); // discard track whose propagation to XMatchingRef failed
470 return -1;
471 }
472 if (mMCTruthON) {
473 mTPCLblWork.emplace_back(mTPCTrkLabels[tpcID]);
474 }
475 // cache work track index
476 mTPCSectIndexCache[o2::math_utils::angle2Sector(trc.getAlpha())].push_back(mTPCWork.size() - 1);
477 return 0;
478}
479
480//______________________________________________
481bool MatchTPCITS::prepareTPCData()
482{
484 mTimer[SWPrepTPC].Start(false);
485 const auto& inp = *mRecoCont;
486
487 mTPCTracksArray = inp.getTPCTracks();
488 mTPCTrackClusIdx = inp.getTPCTracksClusterRefs();
489 mTPCClusterIdxStruct = &inp.inputsTPCclusters->clusterIndex;
490 mTPCRefitterShMap = inp.clusterShMapTPC;
491 mTPCRefitterOccMap = inp.occupancyMapTPC;
492
493 if (mMCTruthON) {
494 mTPCTrkLabels = inp.getTPCTracksMCLabels();
495 }
496
497 int ntr = mTPCTracksArray.size(), ntrW = 0.7 * ntr;
498 mMatchRecordsTPC.reserve(mParams->maxMatchCandidates * ntrW); // number of records might be actually more than N tracks!
499 mTPCWork.reserve(ntrW);
500 if (mMCTruthON) {
501 mTPCLblWork.reserve(ntrW);
502 }
503 for (int sec = o2::constants::math::NSectors; sec--;) {
504 mTPCSectIndexCache[sec].reserve(100 + 1.2 * ntrW / o2::constants::math::NSectors);
505 }
506
507 mTPCRefitter = std::make_unique<o2::gpu::GPUO2InterfaceRefit>(mTPCClusterIdxStruct, mTPCCorrMapsHelper, mBz, mTPCTrackClusIdx.data(), 0, mTPCRefitterShMap.data(), mTPCRefitterOccMap.data(), mTPCRefitterOccMap.size(), nullptr, o2::base::Propagator::Instance());
508 mNTPCOccBinLength = mTPCRefitter->getParam()->rec.tpc.occupancyMapTimeBins;
509 mTBinClOcc.clear();
510 if (mNTPCOccBinLength > 1 && mTPCRefitterOccMap.size()) {
511 mNTPCOccBinLengthInv = 1. / mNTPCOccBinLength;
512 int nTPCBins = mNHBPerTF * o2::constants::lhc::LHCMaxBunches / 8, ninteg = 0;
513 int nTPCOccBins = nTPCBins * mNTPCOccBinLengthInv, sumBins = std::max(1, int(o2::constants::lhc::LHCMaxBunches / 8 * mNTPCOccBinLengthInv));
514 mTBinClOcc.resize(nTPCOccBins);
515 std::vector<float> mltHistTB(nTPCOccBins);
516 float sm = 0., tb = 0.5 * mNTPCOccBinLength;
517 for (int i = 0; i < nTPCOccBins; i++) {
518 mltHistTB[i] = mTPCRefitter->getParam()->GetUnscaledMult(tb);
519 tb += mNTPCOccBinLength;
520 }
521 for (int i = nTPCOccBins; i--;) {
522 sm += mltHistTB[i];
523 if (i + sumBins < nTPCOccBins) {
524 sm -= mltHistTB[i + sumBins];
525 }
526 mTBinClOcc[i] = sm;
527 }
528 } else {
529 mTBinClOcc.resize(1);
530 }
531
532 auto creator = [this](auto& trk, GTrackID gid, float time0, float terr) {
533 if constexpr (isITSTrack<decltype(trk)>()) {
534 // do nothing, ITS tracks will be processed in a direct loop over ROFs
535 }
536 if constexpr (!std::is_base_of_v<o2::track::TrackParCov, std::decay_t<decltype(trk)>>) {
537 return true;
538 } else if (std::abs(trk.getQ2Pt()) > mMinTPCTrackPtInv) {
539 return true;
540 }
541 int resAdd = -100;
542 int tpcIndex = -1;
543 if constexpr (isTPCTrack<decltype(trk)>()) {
544 // unconstrained TPC track, with t0 = TrackTPC.getTime0+0.5*(DeltaFwd-DeltaBwd) and terr = 0.5*(DeltaFwd+DeltaBwd) in TimeBins
545 if (!this->mSkipTPCOnly && trk.getNClusters() > 0) {
546 resAdd = this->addTPCSeed(trk, this->tpcTimeBin2MUS(time0), this->tpcTimeBin2MUS(terr), gid, (tpcIndex = gid.getIndex()));
547 }
548 }
549 if constexpr (isTPCTOFTrack<decltype(trk)>()) {
550 // TPC track constrained by TOF time, time and its error in \mus
551 resAdd = this->addTPCSeed(trk, time0, terr, gid, (tpcIndex = this->mRecoCont->getTPCContributorGID(gid)));
552 }
553 if constexpr (isTRDTrack<decltype(trk)>()) {
554 // TPC track constrained by TRD trigger time, time and its error in \mus
555 resAdd = this->addTPCSeed(trk, time0, terr, gid, (tpcIndex = this->mRecoCont->getTPCContributorGID(gid)));
556 }
557#ifdef _ALLOW_DEBUG_TREES_
558 if (resAdd > -10 && mDBGOut && isDebugFlag(TPCOrigTree)) {
559 dumpTPCOrig(resAdd == 0, tpcIndex);
560 }
561#endif
562 // note: TPCTRDTPF tracks are actually TRD track with extra TOF cluster
563 return true;
564 };
565 mRecoCont->createTracksVariadic(creator);
566
567 float maxTime = 0;
568 int nITSROFs = mITSROFTimes.size();
569 // sort tracks in each sector according to their timeMax
570 for (int sec = o2::constants::math::NSectors; sec--;) {
571 auto& indexCache = mTPCSectIndexCache[sec];
572 if (mParams->verbosity > 0) {
573 LOG(info) << "Sorting sector" << sec << " | " << indexCache.size() << " TPC tracks";
574 }
575 if (!indexCache.size()) {
576 continue;
577 }
578 std::sort(indexCache.begin(), indexCache.end(), [this](int a, int b) {
579 auto& trcA = mTPCWork[a];
580 auto& trcB = mTPCWork[b];
581 return (trcA.tBracket.getMax() - trcB.tBracket.getMax()) < 0.;
582 });
583
584 // build array of 1st entries with tmax corresponding to each ITS ROF (or trigger),
585 // TPC tracks below this entry cannot match to ITS tracks of this and higher ROFs
586
587 float tmax = mTPCWork[indexCache.back()].tBracket.getMax();
588 if (maxTime < tmax) {
589 maxTime = tmax;
590 }
591 int nbins = 1 + (mITSTriggered ? time2ITSROFrameTrig(tmax, 0) : time2ITSROFrameCont(tmax));
592 auto& timeStart = mTPCTimeStart[sec];
593 timeStart.resize(nbins, -1);
594 int itsROF = 0;
595
596 timeStart[0] = 0;
597 for (int itr = 0; itr < (int)indexCache.size(); itr++) {
598 auto& trc = mTPCWork[indexCache[itr]];
599 while (itsROF < nITSROFs && !(trc.tBracket < mITSROFTimes[itsROF])) { // 1st ITS frame after max allowed time for this TPC track
600 itsROF++;
601 }
602 int itsROFMatch = itsROF;
603 if (itsROFMatch && timeStart[--itsROFMatch] == -1) { // register ITSrof preceding the one which exceeds the TPC track tmax
604 timeStart[itsROFMatch] = itr;
605 }
606 }
607 for (int i = 1; i < nbins; i++) {
608 if (timeStart[i] == -1) { // fill gaps with preceding indices
609 timeStart[i] = timeStart[i - 1];
610 }
611 }
612 } // loop over tracks of single sector
613
614 // FIXME We probably don't need this
615 /*
616 // create mapping from TPC time to ITS ROFs
617 if (mITSROFTimes.back() < maxTime) {
618 maxTime = mITSROFTimes.back().getMax();
619 }
620 int nb = int(maxTime) + 1;
621 mITSROFofTPCBin.resize(nb, -1);
622 int itsROF = 0;
623 for (int ib = 0; ib < nb; ib++) {
624 while (itsROF < nITSROFs && ib < mITSROFTimes[itsROF].getMin()) {
625 itsROF++;
626 }
627 mITSROFofTPCBin[ib] = itsROF;
628 }
629*/
630 mInteractionMUSLUT.clear();
631 mInteractionMUSLUT.resize(maxTime + 3 * o2::constants::lhc::LHCOrbitMUS, -1);
632 mTimer[SWPrepTPC].Stop();
633 return mTPCWork.size() > 0;
634}
635
636//_____________________________________________________
637bool MatchTPCITS::prepareITSData()
638{
639 static size_t errCount = 0;
640 constexpr size_t MaxErrors2Report = 10;
641 // Do preparatory work for matching
642 mTimer[SWPrepITS].Start(false);
643 const auto& inp = *mRecoCont;
644
645 // ITS clusters
646 mITSClusterROFRec = inp.getITSClustersROFRecords();
647 const auto clusITS = inp.getITSClusters();
648 if (mITSClusterROFRec.empty() || clusITS.empty()) {
649 LOG(info) << "No ITS clusters";
650 return false;
651 }
652 const auto patterns = inp.getITSClustersPatterns();
653 auto pattIt = patterns.begin();
654 mITSClustersArray.reserve(clusITS.size());
655#ifdef ENABLE_UPGRADES
656 bool withITS3 = o2::GlobalParams::Instance().withITS3;
657 if (withITS3) {
658 o2::its3::ioutils::convertCompactClusters(clusITS, pattIt, mITSClustersArray, mIT3Dict);
659 } else {
660 o2::its::ioutils::convertCompactClusters(clusITS, pattIt, mITSClustersArray, mITSDict);
661 }
662#else
663 o2::its::ioutils::convertCompactClusters(clusITS, pattIt, mITSClustersArray, mITSDict);
664#endif
665
666 // ITS clusters sizes
667 mITSClusterSizes.reserve(clusITS.size());
668 auto pattIt2 = patterns.begin();
669 for (auto& clus : clusITS) {
670 auto pattID = clus.getPatternID();
671 unsigned int npix;
672#ifdef ENABLE_UPGRADES
673 auto ib = o2::its3::constants::detID::isDetITS3(clus.getChipID());
674 if ((pattID == o2::itsmft::CompCluster::InvalidPatternID) || ((withITS3) ? mIT3Dict->isGroup(pattID, ib) : mITSDict->isGroup(pattID))) { // braces guarantee evaluation order
675#else
676 if (pattID == o2::itsmft::CompCluster::InvalidPatternID || mITSDict->isGroup(pattID)) {
677#endif
679 patt.acquirePattern(pattIt2);
680 npix = patt.getNPixels();
681 } else {
682#ifdef ENABLE_UPGRADES
683 if (withITS3) {
684 npix = mIT3Dict->getNpixels(pattID, ib);
685 } else {
686 npix = mITSDict->getNpixels(pattID);
687 }
688#else
689 npix = mITSDict->getNpixels(pattID);
690#endif
691 }
692 mITSClusterSizes.push_back(std::clamp(npix, 0u, 255u));
693 }
694
695 if (mMCTruthON) {
696 mITSClsLabels = inp.mcITSClusters.get();
697 }
698
699 // ITS tracks
700 mITSTracksArray = inp.getITSTracks();
701 mITSTrackClusIdx = inp.getITSTracksClusterRefs();
702 mITSTrackROFRec = inp.getITSTracksROFRecords();
703 if (mMCTruthON) {
704 mITSTrkLabels = inp.getITSTracksMCLabels();
705 }
706 int nROFs = mITSTrackROFRec.size();
707 mITSWork.reserve(mITSTracksArray.size());
708
709 // total N ITS clusters in TF
710 const auto& lastClROF = mITSClusterROFRec[nROFs - 1];
711 int nITSClus = lastClROF.getFirstEntry() + lastClROF.getNEntries();
712 mABClusterLinkIndex.resize(nITSClus, MinusOne);
713 for (int sec = o2::constants::math::NSectors; sec--;) {
714 mITSTimeStart[sec].resize(nROFs, -1); // start of ITS work tracks in every sector
715 }
717 long maxBCs = nHBF * long(o2::constants::lhc::LHCMaxBunches);
719 trackLTInt.setTimeNotNeeded();
720
721 for (int irof = 0; irof < nROFs; irof++) {
722 const auto& rofRec = mITSTrackROFRec[irof];
723 long nBC = rofRec.getBCData().differenceInBC(mStartIR);
724 if (nBC > maxBCs || nBC < 0) {
725 if (++errCount < MaxErrors2Report) {
726 LOGP(alarm, "ITS ROF#{} start {} is not compatible with TF 1st orbit {} or TF length of {} HBFs",
727 irof, rofRec.getBCData().asString(), mStartIR.asString(), nHBF);
728 }
729 break;
730 }
731 float tMin = nBC * o2::constants::lhc::LHCBunchSpacingMUS + mITSTimeBiasMUS;
732 float tMax = (nBC + mITSROFrameLengthInBC) * o2::constants::lhc::LHCBunchSpacingMUS + mITSTimeBiasMUS;
733 if (!mITSTriggered) {
734 size_t irofCont = nBC / mITSROFrameLengthInBC;
735 if (mITSTrackROFContMapping.size() <= irofCont) { // there might be gaps in the non-empty rofs, this will map continuous ROFs index to non empty ones
736 mITSTrackROFContMapping.resize((1 + irofCont / 128) * 128, 0);
737 }
738 mITSTrackROFContMapping[irofCont] = irof;
739 }
740
741 mITSROFTimes.emplace_back(tMin, tMax); // ITS ROF min/max time
742
743 for (int sec = o2::constants::math::NSectors; sec--;) { // start of sector's tracks for this ROF
744 mITSTimeStart[sec][irof] = mITSSectIndexCache[sec].size(); // The sorting does not affect this
745 }
746
747 int trlim = rofRec.getFirstEntry() + rofRec.getNEntries();
748 for (int it = rofRec.getFirstEntry(); it < trlim; it++) {
749 const auto& trcOrig = mITSTracksArray[it];
750 if (mParams->runAfterBurner) {
751 flagUsedITSClusters(trcOrig);
752 }
753 if (trcOrig.getParamOut().getX() < 1.) {
754 continue; // backward refit failed
755 }
756 if (std::abs(trcOrig.getQ2Pt()) > mMinITSTrackPtInv) {
757 continue;
758 }
759 int nWorkTracks = mITSWork.size();
760 // working copy of outer track param
761 auto& trc = mITSWork.emplace_back(TrackLocITS{trcOrig.getParamOut(), {tMin, tMax}, it, irof, MinusOne});
762 if (!trc.rotate(o2::math_utils::angle2Alpha(trc.getPhiPos()))) {
763 mITSWork.pop_back(); // discard failed track
764 continue;
765 }
766 // make sure the track is at the ref. radius
767 trackLTInt.clearFast();
768 if (!propagateToRefX(trc, &trackLTInt)) {
769 mITSWork.pop_back(); // discard failed track
770 continue; // add to cache only those ITS tracks which reached ref.X and have reasonable snp
771 }
772 trc.xrho = trackLTInt.getXRho(); // we collect seen x*rho and distance to the reference X for further PID correcrions
773 trc.dL = trackLTInt.getL();
774
775 if (mMCTruthON) {
776 mITSLblWork.emplace_back(mITSTrkLabels[it]);
777 }
778 trc.setUserField(0);
779 // cache work track index
780 int sector = o2::math_utils::angle2Sector(trc.getAlpha());
781 mITSSectIndexCache[sector].push_back(nWorkTracks);
782
783 // If the ITS track is very close to the sector edge, it may match also to a TPC track in the neighb. sector.
784 // For a track with Yr and Phir at Xr the distance^2 between the poisition of this track in the neighb. sector
785 // when propagated to Xr (in this neighbouring sector) and the edge will be (neglecting the curvature)
786 // [(Xr*tg(10)-Yr)/(tgPhir+tg70)]^2 / cos(70)^2 // for the next sector
787 // [(Xr*tg(10)+Yr)/(tgPhir-tg70)]^2 / cos(70)^2 // for the prev sector
788 // Distances to the sector edges in neighbourings sectors (at Xref in their proper frames)
789 float trcY = trc.getY(), tgp = trc.getSnp();
790 tgp /= std::sqrt((1.f - tgp) * (1.f + tgp)); // tan of track direction XY
791
792 float dyUpDn[2] = {std::abs((YMaxAtXMatchingRef - trcY) / (tgp + Tan70)), std::abs((YMaxAtXMatchingRef + trcY) / (tgp - Tan70))}; // sector up, down edge distances
793 // we do the cloning for closest edge only
794 int sel = dyUpDn[0] < dyUpDn[1] ? 0 : 1;
795 if (dyUpDn[sel] < mSectEdgeMargin) { // need to check this track for matching in sector up or down
796 int sectNeib = sel == 0 ? (sector < (o2::constants::math::NSectors - 1) ? sector + 1 : 0) : (sector > 1 ? sector - 1 : o2::constants::math::NSectors - 1);
797 addLastTrackCloneForNeighbourSector(sectNeib, &trackLTInt);
798 }
799 }
800 }
801
802 if (!mITSTriggered) { // fill the gaps;
803 int nr = mITSTrackROFContMapping.size();
804 for (int i = 1; i < nr; i++) {
805 if (mITSTrackROFContMapping[i] < mITSTrackROFContMapping[i - 1]) {
806 mITSTrackROFContMapping[i] = mITSTrackROFContMapping[i - 1];
807 }
808 }
809 }
810
811 // sort tracks in each sector according to their min time, then tgl
812 // RSTODO: sorting in tgl will be dangerous once the tracks with different time uncertaincies will be added
813 for (int sec = o2::constants::math::NSectors; sec--;) {
814 auto& indexCache = mITSSectIndexCache[sec];
815 if (mParams->verbosity > 0) {
816 LOG(info) << "Sorting sector" << sec << " | " << indexCache.size() << " ITS tracks";
817 }
818 if (!indexCache.size()) {
819 continue;
820 }
821 std::sort(indexCache.begin(), indexCache.end(), [this](int a, int b) {
822 auto& trackA = mITSWork[a];
823 auto& trackB = mITSWork[b];
824 if (trackA.tBracket.getMin() < trackB.tBracket.getMin()) {
825 return true;
826 } else if (trackA.tBracket.getMin() > trackB.tBracket.getMin()) {
827 return false;
828 }
829 return trackA.getTgl() < trackB.getTgl();
830 });
831 } // loop over tracks of single sector
832 mMatchRecordsITS.reserve(mITSWork.size() * mParams->maxMatchCandidates);
833 mTimer[SWPrepITS].Stop();
834
835 return nITSClus > 0;
836}
837
838//_____________________________________________________
839bool MatchTPCITS::prepareFITData()
840{
841 // If available, read FIT Info
842 if (mUseFT0) {
843 mFITInfo = mRecoCont->getFT0RecPoints();
844 prepareInteractionTimes();
845 }
846 return true;
847}
848
849//_____________________________________________________
850void MatchTPCITS::doMatching(int sec)
851{
853 auto& cacheITS = mITSSectIndexCache[sec]; // array of cached ITS track indices for this sector
854 auto& cacheTPC = mTPCSectIndexCache[sec]; // array of cached ITS track indices for this sector
855 auto& timeStartTPC = mTPCTimeStart[sec]; // array of 1st TPC track with timeMax in ITS ROFrame
856 auto& timeStartITS = mITSTimeStart[sec];
857 int nTracksTPC = cacheTPC.size(), nTracksITS = cacheITS.size();
858 if (!nTracksTPC || !nTracksITS) {
859 if (mParams->verbosity > 0) {
860 LOG(info) << "Matchng sector " << sec << " : N tracks TPC:" << nTracksTPC << " ITS:" << nTracksITS << " in sector " << sec;
861 }
862 return;
863 }
864
866 float maxTDriftSafe = tpcTimeBin2MUS(mNTPCBinsFullDrift + mParams->safeMarginTPCITSTimeBin + mTPCTimeEdgeTSafeMargin);
867 float vdErrT = tpcTimeBin2MUS(mZ2TPCBin * mParams->maxVDriftUncertainty);
868
869 // get min ROFrame of ITS tracks currently in cache
870 auto minROFITS = mITSWork[cacheITS.front()].roFrame;
871
872 if (minROFITS >= int(timeStartTPC.size())) {
873 LOG(info) << "ITS min ROFrame " << minROFITS << " exceeds all cached TPC track ROF eqiuvalent " << cacheTPC.size() - 1;
874 return;
875 }
876
877 int nCheckTPCControl = 0, nCheckITSControl = 0, nMatchesControl = 0; // temporary
878 int idxMinTPC = timeStartTPC[minROFITS]; // index of 1st cached TPC track within cached ITS ROFrames
879 auto t2nbs = tpcTimeBin2MUS(mZ2TPCBin * mParams->tpcTimeICMatchingNSigma);
880 bool checkInteractionCandidates = mUseFT0 && mParams->validateMatchByFIT != MatchTPCITSParams::Disable;
881
882 int itsROBin = 0;
883 for (int itpc = idxMinTPC; itpc < nTracksTPC; itpc++) {
884 auto& trefTPC = mTPCWork[cacheTPC[itpc]];
885 // estimate ITS 1st ROframe bin this track may match to: TPC track are sorted according to their
886 // timeMax, hence the timeMax - MaxmNTPCBinsFullDrift are non-decreasing
887 auto tmn = trefTPC.tBracket.getMax() - maxTDriftSafe;
888 itsROBin = mITSTriggered ? time2ITSROFrameTrig(tmn, itsROBin) : time2ITSROFrameCont(tmn);
889
890 if (itsROBin >= int(timeStartITS.size())) { // time of TPC track exceeds the max time of ITS in the cache
891 break;
892 }
893 int iits0 = timeStartITS[itsROBin];
894 nCheckTPCControl++;
895 for (auto iits = iits0; iits < nTracksITS; iits++) {
896 auto& trefITS = mITSWork[cacheITS[iits]];
897 // compare if the ITS and TPC tracks may overlap in time
898 LOG(debug) << "TPC bracket: " << trefTPC.tBracket.asString() << " ITS bracket: " << trefITS.tBracket.asString() << " TPCtgl: " << trefTPC.getTgl() << " ITStgl: " << trefITS.getTgl();
899 if (trefTPC.tBracket < trefITS.tBracket) { // since TPC tracks are sorted in timeMax and ITS tracks are sorted in timeMin all following ITS tracks also will not match
900 break;
901 }
902 if (trefTPC.tBracket > trefITS.tBracket) { // its bracket precedes TPC bracket
903 continue;
904 }
905
906 // is corrected TPC track time compatible with ITS ROF expressed
907 auto deltaT = (trefITS.getZ() - trefTPC.getZ()) * mTPCVDriftInv; // drift time difference corresponding to Z differences
908 auto timeCorr = trefTPC.getCorrectedTime(deltaT); // TPC time required to match to Z of ITS track
909 auto timeCorrErr = std::sqrt(trefITS.getSigmaZ2() + trefTPC.getSigmaZ2()) * t2nbs + mParams->safeMarginTimeCorrErr; // nsigma*error
910 if (mVDriftCalibOn) {
911 timeCorrErr += vdErrT * (250. - abs(trefITS.getZ())); // account for the extra error from TPC VDrift uncertainty
912 }
913 o2::math_utils::Bracketf_t trange(timeCorr - timeCorrErr, timeCorr + timeCorrErr);
914 LOG(debug) << "TPC range: " << trange.asString() << " ITS bracket: " << trefITS.tBracket.asString() << " DZ: " << (trefITS.getZ() - trefTPC.getZ()) << " DT: " << timeCorr;
915 // check if the assigned time is strictly within the ITS bracket
916 auto cmpITSBracket = trefITS.tBracket.isOutside(timeCorr);
917 if (cmpITSBracket) { // no, check if brackets are overlapping at all
918 if (trefITS.tBracket.isOutside(trange)) {
919 continue;
920 }
922 if (cmpITSBracket == o2::math_utils::Bracketf_t::Below) {
923 timeCorr = trefITS.tBracket.getMin();
924 trange.setMin(timeCorr);
925 } else {
926 timeCorr = trefITS.tBracket.getMax();
927 trange.setMax(timeCorr);
928 }
930 continue;
931 }
932 }
933
934 nCheckITSControl++;
935 float chi2 = -1;
936 int rejFlag = compareTPCITSTracks(trefITS, trefTPC, chi2);
937
938#ifdef _ALLOW_DEBUG_TREES_
939 if (mDBGOut && ((rejFlag == Accept && isDebugFlag(MatchTreeAccOnly)) || isDebugFlag(MatchTreeAll))) {
940 fillTPCITSmatchTree(cacheITS[iits], cacheTPC[itpc], rejFlag, chi2, timeCorr);
941 }
942#endif
943 /*
944 // RS: this might be dangerous for ITS tracks with different time coverages.
945 if (rejFlag == RejectOnTgl) {
946 // ITS tracks in each ROFrame are ordered in Tgl, hence if this check failed on Tgl check
947 // (i.e. tgl_its>tgl_tpc+tolerance), then all other ITS tracks in this ROFrame will also have tgl too large.
948 // Jump on the 1st ITS track of the next ROFrame
949 int rof = trefITS.roFrame;
950 bool stop = false;
951 do {
952 if (++rof >= int(timeStartITS.size())) {
953 stop = true;
954 break; // no more ITS ROFrames in cache
955 }
956 iits = timeStartITS[rof] - 1; // next track to be checked -1
957 } while (iits <= timeStartITS[trefITS.roFrame]); // skip empty bins
958 if (stop) {
959 break;
960 }
961 continue;
962 }
963 */
964 if (rejFlag != Accept) {
965 continue;
966 }
967 int matchedIC = MinusOne;
968 if (!isCosmics()) {
969 // validate by bunch filling scheme
970 if (mUseBCFilling) {
971 auto irBracket = tBracket2IRBracket(trange);
972 if (irBracket.isInvalid()) {
973 continue;
974 }
975 }
976 if (checkInteractionCandidates && mInteractions.size()) {
977 // check if corrected TPC track time is compatible with any of interaction times
978 int tmus = trange.getMin();
979 if (tmus < 0) {
980 tmus = 0;
981 }
982 auto entStart = tmus < int(mInteractionMUSLUT.size()) ? mInteractionMUSLUT[tmus] : (mInteractionMUSLUT.size() ? mInteractionMUSLUT.back() : 0);
983 for (int ent = entStart; ent < (int)mInteractions.size(); ent++) {
984 auto cmp = mInteractions[ent].tBracket.isOutside(trange);
985 if (cmp == o2::math_utils::Bracketf_t::Above) { // trange is above this interaction candidate, the following ones may match
986 continue;
987 }
989 matchedIC = ent;
990 }
991 break; // we loop till 1st matching IC or the one above the trange (since IC are ordered, all others will be above too)
992 }
993 }
994 if (mParams->validateMatchByFIT == MatchTPCITSParams::Require && matchedIC == MinusOne) {
995 continue;
996 }
997 }
998 registerMatchRecordTPC(cacheITS[iits], cacheTPC[itpc], chi2, matchedIC); // register matching candidate
999 nMatchesControl++;
1000 }
1001 }
1002 if (mParams->verbosity > 0) {
1003 LOG(info) << "Match sector " << sec << " N tracks TPC:" << nTracksTPC << " ITS:" << nTracksITS
1004 << " N TPC tracks checked: " << nCheckTPCControl << " (starting from " << idxMinTPC
1005 << "), checks: " << nCheckITSControl << ", matches:" << nMatchesControl;
1006 }
1007 mNMatchesControl += nMatchesControl;
1008}
1009
1010//______________________________________________
1011void MatchTPCITS::suppressMatchRecordITS(int itsID, int tpcID)
1012{
1014 auto& tITS = mITSWork[itsID];
1015 int topID = MinusOne, recordID = tITS.matchID; // 1st entry in mMatchRecordsITS
1016 while (recordID > MinusOne) { // navigate over records for given ITS track
1017 if (mMatchRecordsITS[recordID].partnerID == tpcID) {
1018 // unlink this record, connecting its child to its parrent
1019 if (topID < 0) {
1020 tITS.matchID = mMatchRecordsITS[recordID].nextRecID;
1021 } else {
1022 mMatchRecordsITS[topID].nextRecID = mMatchRecordsITS[recordID].nextRecID;
1023 }
1024 return;
1025 }
1026 topID = recordID;
1027 recordID = mMatchRecordsITS[recordID].nextRecID; // check next record
1028 }
1029}
1030
1031//______________________________________________
1032bool MatchTPCITS::registerMatchRecordTPC(int iITS, int iTPC, float chi2, int candIC)
1033{
1036 auto& tTPC = mTPCWork[iTPC]; // get MatchRecord structure of this TPC track, create if none
1037 if (tTPC.matchID < 0) { // no matches yet, just add new record
1038 registerMatchRecordITS(iITS, iTPC, chi2, candIC); // register TPC track in the ITS records
1039 tTPC.matchID = mMatchRecordsTPC.size(); // new record will be added in the end
1040 mMatchRecordsTPC.emplace_back(iITS, chi2, MinusOne, candIC); // create new record with empty reference on next match
1041 return true;
1042 }
1043
1044 int count = 0, nextID = tTPC.matchID, topID = MinusOne;
1045 do {
1046 auto& nextMatchRec = mMatchRecordsTPC[nextID];
1047 count++;
1048 if (!nextMatchRec.isBetter(chi2, candIC)) { // need to insert new record before nextMatchRec?
1049 if (count < mParams->maxMatchCandidates) {
1050 break; // will insert in front of nextID
1051 } else { // max number of candidates reached, will overwrite the last one
1052 suppressMatchRecordITS(nextMatchRec.partnerID, iTPC); // flag as disabled the overriden ITS match
1053 registerMatchRecordITS(iITS, iTPC, chi2, candIC); // register TPC track entry in the ITS records
1054 // reuse the record of suppressed ITS match to store better one
1055 nextMatchRec.chi2 = chi2;
1056 nextMatchRec.matchedIC = candIC;
1057 nextMatchRec.partnerID = iITS;
1058 return true;
1059 }
1060 }
1061 topID = nextID; // check next match record
1062 nextID = nextMatchRec.nextRecID;
1063 } while (nextID > MinusOne);
1064
1065 // if count == mParams->maxMatchCandidates, the max number of candidates was already reached, and the
1066 // new candidated was either discarded (if its chi2 is worst one) or has overwritten worst
1067 // existing candidate. Otherwise, we need to add new entry
1068 if (count < mParams->maxMatchCandidates) {
1069 if (topID < 0) { // the new match is top candidate
1070 topID = tTPC.matchID = mMatchRecordsTPC.size(); // register new record as top one
1071 } else { // there are better candidates
1072 topID = mMatchRecordsTPC[topID].nextRecID = mMatchRecordsTPC.size(); // register to his parent
1073 }
1074 // nextID==-1 will mean that the while loop run over all candidates->the new one is the worst (goes to the end)
1075 registerMatchRecordITS(iITS, iTPC, chi2, candIC); // register TPC track in the ITS records
1076 mMatchRecordsTPC.emplace_back(iITS, chi2, nextID, candIC); // create new record with empty reference on next match
1077 // make sure that after addition the number of candidates don't exceed allowed number
1078 count++;
1079 while (nextID > MinusOne) {
1080 if (count > mParams->maxMatchCandidates) {
1081 suppressMatchRecordITS(mMatchRecordsTPC[nextID].partnerID, iTPC);
1082 // exclude nextID record, w/o changing topID (which becomes the last record)
1083 nextID = mMatchRecordsTPC[topID].nextRecID = mMatchRecordsTPC[nextID].nextRecID;
1084 continue;
1085 }
1086 count++;
1087 topID = nextID;
1088 nextID = mMatchRecordsTPC[nextID].nextRecID;
1089 }
1090 return true;
1091 } else {
1092 return false; // unless nextID was assigned OverrideExisting, new candidate was discarded
1093 }
1094}
1095
1096//______________________________________________
1097void MatchTPCITS::registerMatchRecordITS(const int iITS, int iTPC, float chi2, int candIC)
1098{
1100 auto& tITS = mITSWork[iITS];
1101 int idnew = mMatchRecordsITS.size();
1102 auto& newRecord = mMatchRecordsITS.emplace_back(iTPC, chi2, MinusOne, candIC); // associate iTPC with this record
1103 if (tITS.matchID < 0) {
1104 tITS.matchID = idnew;
1105 return;
1106 }
1107 // there are other matches for this ITS track, insert the new record preserving quality order
1108 // navigate till last record or the one with worse chi2
1109 int topID = MinusOne, nextRecord = tITS.matchID;
1110 do {
1111 auto& nextMatchRec = mMatchRecordsITS[nextRecord];
1112 if (!nextMatchRec.isBetter(chi2, candIC)) { // need to insert new record before nextMatchRec?
1113 newRecord.nextRecID = nextRecord; // new one will refer to old one it overtook
1114 if (topID < 0) {
1115 tITS.matchID = idnew; // the new one is the best match, track will refer to it
1116 } else {
1117 mMatchRecordsITS[topID].nextRecID = idnew; // new record will follow existing better one
1118 }
1119 return;
1120 }
1121 topID = nextRecord;
1122 nextRecord = mMatchRecordsITS[nextRecord].nextRecID;
1123 } while (nextRecord > MinusOne);
1124
1125 // if we reached here, the new record should be added in the end
1126 mMatchRecordsITS[topID].nextRecID = idnew; // register new link
1127}
1128
1129//______________________________________________
1130int MatchTPCITS::compareTPCITSTracks(const TrackLocITS& tITS, const TrackLocTPC& tTPC, float& chi2) const
1131{
1133 chi2 = -1.f;
1134 int rejFlag = Accept;
1135 float diff; // make rough check differences and their nsigmas
1136
1137 // start with check on Tgl, since rjection on it will allow to profit from sorting
1138 diff = tITS.getParam(o2::track::kTgl) - tTPC.getParam(o2::track::kTgl);
1139 if ((rejFlag = roughCheckDif(diff, mParams->crudeAbsDiffCut[o2::track::kTgl], RejectOnTgl))) {
1140 return rejFlag;
1141 }
1142 auto err2Tgl = tITS.getDiagError2(o2::track::kTgl) + tTPC.getDiagError2(o2::track::kTgl);
1143 if (mVDriftCalibOn) {
1144 auto addErr = tITS.getParam(o2::track::kTgl) * mParams->maxVDriftUncertainty;
1145 err2Tgl += addErr * addErr; // account for VDrift uncertainty
1146 }
1147 diff *= diff / err2Tgl;
1148 if ((rejFlag = roughCheckDif(diff, mParams->crudeNSigma2Cut[o2::track::kTgl], RejectOnTgl + NSigmaShift))) {
1149 return rejFlag;
1150 }
1151 // do we need to account for different PID hypotheses used for ITS and TPC tracks propagation to ref. X?
1152 bool testOtherPID = false;
1153 float itsParam[5] = {tITS.getY(), tITS.getZ(), tITS.getSnp(), tITS.getTgl(), tITS.getQ2Pt()};
1154 if (tTPC.getPID() > tITS.getPID() && tITS.dL > 0.f && tTPC.getP2() / tTPC.getPID().getMass2() < mParams->minBetaGammaForPIDDiff) {
1155 o2::track::TrackPar tPID(mITSTracksArray[tITS.sourceID].getParamOut()); // clone original ITS track at highest update point
1156 tPID.setPID(tTPC.getPID(), true);
1157 if (!tPID.correctForELoss(tITS.xrho)) {
1158 return RejectoOnPIDCorr;
1159 }
1160 float dCurv = (tPID.getQ2Pt() - tITS.getQ2Pt()) * mBz * o2::constants::math::B2C, dLEff = tITS.dL * mParams->ITSStepEffFraction, dCurvL = dCurv * dLEff;
1161 itsParam[o2::track::kQ2Pt] = tPID.getQ2Pt();
1162 itsParam[o2::track::kSnp] += dCurvL;
1163 if (std::abs(itsParam[o2::track::kSnp]) >= 1.) {
1164 itsParam[o2::track::kSnp] = std::copysign(0.99, itsParam[o2::track::kSnp]);
1165 }
1166 itsParam[o2::track::kY] += dCurvL * dLEff * 0.5;
1167 testOtherPID = true;
1168 }
1169
1170 diff = itsParam[o2::track::kY] - tTPC.getParam(o2::track::kY);
1171 if ((rejFlag = roughCheckDif(diff, mParams->crudeAbsDiffCut[o2::track::kY], RejectOnY))) {
1172 return rejFlag;
1173 }
1174 diff *= diff / (tITS.getDiagError2(o2::track::kY) + tTPC.getDiagError2(o2::track::kY));
1175 if ((rejFlag = roughCheckDif(diff, mParams->crudeNSigma2Cut[o2::track::kY], RejectOnY + NSigmaShift))) {
1176 return rejFlag;
1177 }
1178
1179 if (tTPC.constraint == TrackLocTPC::Constrained) { // in continuous only constrained tracks can be compared in Z
1180 diff = itsParam[o2::track::kZ] - tTPC.getParam(o2::track::kZ);
1181 if ((rejFlag = roughCheckDif(diff, mParams->crudeAbsDiffCut[o2::track::kZ], RejectOnZ))) {
1182 return rejFlag;
1183 }
1184 diff *= diff / (tITS.getDiagError2(o2::track::kZ) + tTPC.getDiagError2(o2::track::kZ));
1185 if ((rejFlag = roughCheckDif(diff, mParams->crudeNSigma2Cut[o2::track::kZ], RejectOnZ + NSigmaShift))) {
1186 return rejFlag;
1187 }
1188 }
1189
1190 diff = itsParam[o2::track::kSnp] - tTPC.getParam(o2::track::kSnp);
1191 if ((rejFlag = roughCheckDif(diff, mParams->crudeAbsDiffCut[o2::track::kSnp], RejectOnSnp))) {
1192 return rejFlag;
1193 }
1194 diff *= diff / (tITS.getDiagError2(o2::track::kSnp) + tTPC.getDiagError2(o2::track::kSnp));
1195 if ((rejFlag = roughCheckDif(diff, mParams->crudeNSigma2Cut[o2::track::kSnp], RejectOnSnp + NSigmaShift))) {
1196 return rejFlag;
1197 }
1198
1199 diff = itsParam[o2::track::kQ2Pt] - tTPC.getParam(o2::track::kQ2Pt);
1200 if ((rejFlag = roughCheckDif(diff, mParams->crudeAbsDiffCut[o2::track::kQ2Pt], RejectOnQ2Pt))) {
1201 return rejFlag;
1202 }
1203 diff *= diff / (tITS.getDiagError2(o2::track::kQ2Pt) + tTPC.getDiagError2(o2::track::kQ2Pt));
1204 if ((rejFlag = roughCheckDif(diff, mParams->crudeNSigma2Cut[o2::track::kQ2Pt], RejectOnQ2Pt + NSigmaShift))) {
1205 return rejFlag;
1206 }
1207 // calculate mutual chi2 excluding Z in continuous mode
1208 if (testOtherPID) { // temporarily substitute pion params by alternative ones
1209 auto tITSAlt = tITS;
1210 tITSAlt.setPID(tTPC.getPID());
1211 tITSAlt.setParam(itsParam[o2::track::kY], o2::track::kY);
1212 tITSAlt.setParam(itsParam[o2::track::kSnp], o2::track::kSnp);
1213 tITSAlt.setParam(itsParam[o2::track::kQ2Pt], o2::track::kQ2Pt);
1214 chi2 = getPredictedChi2NoZ(tITSAlt, tTPC);
1215 } else {
1216 chi2 = getPredictedChi2NoZ(tITS, tTPC);
1217 }
1218 if (chi2 > mParams->cutMatchingChi2 || chi2 < 0.) { // sometimes due to the numerical stability the chi2 is negative, reject it.
1219 return RejectOnChi2;
1220 }
1221
1222 return Accept;
1223}
1224
1225//______________________________________________
1227{
1229 int ntpc = mTPCWork.size();
1230 printf("\n\nPrinting all TPC -> ITS matches for %d TPC tracks\n", ntpc);
1231 for (int i = 0; i < ntpc; i++) {
1232 const auto& tTPC = mTPCWork[i];
1233 int nm = getNMatchRecordsTPC(tTPC);
1234 printf("*** trackTPC#%6d %6d : Ncand = %d Best = %d\n", i, tTPC.sourceID, nm, tTPC.matchID);
1235 int count = 0, recID = tTPC.matchID;
1236 while (recID > MinusOne) {
1237 const auto& rcTPC = mMatchRecordsTPC[recID];
1238 const auto& tITS = mITSWork[rcTPC.partnerID];
1239 printf(" * cand %2d : ITS track %6d(src:%6d) Chi2: %.2f\n", count, rcTPC.partnerID, tITS.sourceID, rcTPC.chi2);
1240 count++;
1241 recID = rcTPC.nextRecID;
1242 }
1243 }
1244}
1245
1246//______________________________________________
1248{
1250 int nits = mITSWork.size();
1251 printf("\n\nPrinting all ITS -> TPC matches for %d ITS tracks\n", nits);
1252
1253 for (int i = 0; i < nits; i++) {
1254 const auto& tITS = mITSWork[i];
1255 printf("*** trackITS#%6d %6d : Ncand = %d Best = %d\n", i, tITS.sourceID, getNMatchRecordsITS(tITS), tITS.matchID);
1256 int count = 0, recID = tITS.matchID;
1257 while (recID > MinusOne) {
1258 const auto& rcITS = mMatchRecordsITS[recID];
1259 const auto& tTPC = mTPCWork[rcITS.partnerID];
1260 printf(" * cand %2d : TPC track %6d(src:%6d) Chi2: %.2f\n", count, rcITS.partnerID, tTPC.sourceID, rcITS.chi2);
1261 count++;
1262 recID = rcITS.nextRecID;
1263 }
1264 }
1265}
1266
1267//______________________________________________
1268float MatchTPCITS::getPredictedChi2NoZ(const o2::track::TrackParCov& trITS, const o2::track::TrackParCov& trTPC) const
1269{
1272
1273 // if (std::abs(trITS.getAlpha() - trTPC.getAlpha()) > FLT_EPSILON) {
1274 // LOG(error) << "The reference Alpha of the tracks differ: "
1275 // << trITS.getAlpha() << " : " << trTPC.getAlpha();
1276 // return 2. * o2::track::HugeF;
1277 // }
1278 // if (std::abs(trITS.getX() - trTPC.getX()) > FLT_EPSILON) {
1279 // LOG(error) << "The reference X of the tracks differ: "
1280 // << trITS.getX() << " : " << trTPC.getX();
1281 // return 2. * o2::track::HugeF;
1282 // }
1283 MatrixDSym4 covMat;
1284 covMat(0, 0) = static_cast<double>(trITS.getSigmaY2()) + static_cast<double>(trTPC.getSigmaY2());
1285 covMat(1, 0) = static_cast<double>(trITS.getSigmaSnpY()) + static_cast<double>(trTPC.getSigmaSnpY());
1286 covMat(1, 1) = static_cast<double>(trITS.getSigmaSnp2()) + static_cast<double>(trTPC.getSigmaSnp2());
1287 covMat(2, 0) = static_cast<double>(trITS.getSigmaTglY()) + static_cast<double>(trTPC.getSigmaTglY());
1288 covMat(2, 1) = static_cast<double>(trITS.getSigmaTglSnp()) + static_cast<double>(trTPC.getSigmaTglSnp());
1289 covMat(2, 2) = static_cast<double>(trITS.getSigmaTgl2()) + static_cast<double>(trTPC.getSigmaTgl2());
1290 if (mVDriftCalibOn) {
1291 auto addErr = trITS.getParam(o2::track::kTgl) * mParams->maxVDriftUncertainty;
1292 covMat(2, 2) += addErr * addErr;
1293 }
1294 covMat(3, 0) = static_cast<double>(trITS.getSigma1PtY()) + static_cast<double>(trTPC.getSigma1PtY());
1295 covMat(3, 1) = static_cast<double>(trITS.getSigma1PtSnp()) + static_cast<double>(trTPC.getSigma1PtSnp());
1296 covMat(3, 2) = static_cast<double>(trITS.getSigma1PtTgl()) + static_cast<double>(trTPC.getSigma1PtTgl());
1297 covMat(3, 3) = static_cast<double>(trITS.getSigma1Pt2()) + static_cast<double>(trTPC.getSigma1Pt2());
1298 if (!covMat.Invert()) {
1299 LOG(error) << "Cov.matrix inversion failed: " << covMat;
1300 return 2. * o2::track::HugeF;
1301 }
1302 double chi2diag = 0., chi2ndiag = 0.,
1303 diff[o2::track::kNParams - 1] = {trITS.getParam(o2::track::kY) - trTPC.getParam(o2::track::kY),
1304 trITS.getParam(o2::track::kSnp) - trTPC.getParam(o2::track::kSnp),
1305 trITS.getParam(o2::track::kTgl) - trTPC.getParam(o2::track::kTgl),
1306 trITS.getParam(o2::track::kQ2Pt) - trTPC.getParam(o2::track::kQ2Pt)};
1307 for (int i = o2::track::kNParams - 1; i--;) {
1308 chi2diag += diff[i] * diff[i] * covMat(i, i);
1309 for (int j = i; j--;) {
1310 chi2ndiag += diff[i] * diff[j] * covMat(i, j);
1311 }
1312 }
1313 return chi2diag + 2. * chi2ndiag;
1314}
1315
1316//______________________________________________
1317void MatchTPCITS::addLastTrackCloneForNeighbourSector(int sector, o2::track::TrackLTIntegral* trackLTInt)
1318{
1319 // add clone of the src ITS track cache, propagate it to ref.X in requested sector
1320 // and register its index in the sector cache. Used for ITS tracks which are so close
1321 // to their setctor edge that their matching should be checked also in the neighbouring sector
1322 mITSWork.push_back(mITSWork.back()); // clone the last track defined in given sector
1323 auto& trc = mITSWork.back();
1324 if (trc.rotate(o2::math_utils::sector2Angle(sector)) &&
1325 o2::base::Propagator::Instance()->PropagateToXBxByBz(trc, mParams->XMatchingRef, MaxSnp, 2., mUseMatCorrFlag, trackLTInt)) {
1326 // TODO: use faster prop here, no 3d field, materials
1327 mITSSectIndexCache[sector].push_back(mITSWork.size() - 1); // register track CLONE
1328 // flag clone
1329 mITSWork.back().setCloneBefore();
1330 if (trackLTInt) {
1331 mITSWork.back().xrho = trackLTInt->getXRho(); // we collect seen x*rho and distance to the reference X for further PID correcrions
1332 mITSWork.back().dL = trackLTInt->getL();
1333 }
1334 mITSWork[mITSWork.size() - 2].setCloneAfter();
1335 if (mMCTruthON) {
1336 mITSLblWork.emplace_back(mITSTrkLabels[trc.sourceID]);
1337 }
1338 } else {
1339 mITSWork.pop_back(); // rotation / propagation failed
1340 }
1341}
1342
1343//______________________________________________
1344bool MatchTPCITS::propagateToRefX(o2::track::TrackParCov& trc, o2::track::TrackLTIntegral* lti)
1345{
1346 // propagate track to matching reference X, making sure its assigned alpha
1347 // is consistent with TPC sector
1348 constexpr float TgHalfSector = 0.17632698f;
1349 bool refReached = false;
1350 refReached = mParams->XMatchingRef < 10.; // RS: tmp, to cover XMatchingRef~0
1351 int trialsLeft = 2;
1352 while (o2::base::Propagator::Instance()->PropagateToXBxByBz(trc, mParams->XMatchingRef, MaxSnp, 2., mUseMatCorrFlag, lti)) {
1353 if (refReached) {
1354 break;
1355 }
1356 // make sure the track is indeed within the sector defined by alpha
1357 if (fabs(trc.getY()) < mParams->XMatchingRef * TgHalfSector) {
1358 refReached = true;
1359 break; // ok, within
1360 }
1361 if (!trialsLeft--) {
1362 break;
1363 }
1364 auto alphaNew = o2::math_utils::angle2Alpha(trc.getPhiPos());
1365 if (!trc.rotate(alphaNew) != 0) {
1366 break; // failed (RS: check effect on matching tracks to neighbouring sector)
1367 }
1368 }
1369 return refReached && std::abs(trc.getSnp()) < MaxSnp;
1370}
1371
1372//______________________________________________
1374{
1376 printf("\n******************** TPC-ITS matching component ********************\n");
1377 if (!mInitDone) {
1378 printf("init is not done yet\n");
1379 return;
1380 }
1381
1382 printf("MC truth: %s\n", mMCTruthON ? "on" : "off");
1383 printf("Matching reference X: %.3f\n", mParams->XMatchingRef);
1384 printf("Account Z dimension: %s\n", mCompareTracksDZ ? "on" : "off");
1385 printf("Cut on matching chi2: %.3f\n", mParams->cutMatchingChi2);
1386 printf("Max number ITS candidates per TPC track: %d\n", mParams->maxMatchCandidates);
1387 printf("Crude cut on track params: ");
1388 for (int i = 0; i < o2::track::kNParams; i++) {
1389 printf(" %.3e", mParams->crudeAbsDiffCut[i]);
1390 }
1391 printf("\n");
1392
1393 printf("NSigma^2 cut on track params: ");
1394 for (int i = 0; i < o2::track::kNParams; i++) {
1395 printf(" %6.2f", mParams->crudeNSigma2Cut[i]);
1396 }
1397 printf("\n");
1398
1399 printf("TPC Z->time(bins) bracketing safety margin: %6.2f\n", mParams->safeMarginTPCTimeEdge);
1400
1401#ifdef _ALLOW_DEBUG_TREES_
1402
1403 printf("Output debug tree (%s) file: %s\n", mDBGFlags ? "on" : "off", mDebugTreeFileName.data());
1404 if (getDebugFlags()) {
1405 printf("Debug stream flags:\n");
1407 printf("* matching canditate pairs: %s\n", isDebugFlag(MatchTreeAccOnly) ? "accepted" : "all");
1408 }
1410 printf("* winner matches\n");
1411 }
1412 }
1413#endif
1414
1415 printf("**********************************************************************\n");
1416}
1417
1418//______________________________________________
1420{
1422 mTimer[SWRefit].Start(false);
1423 matchedTracks.reserve(mNMatches + mABWinnersIDs.size());
1424 matchedTracks.resize(mNMatches);
1425 if (mMCTruthON) {
1426 matchLabels.reserve(mNMatches + mABWinnersIDs.size());
1427 matchLabels.resize(mNMatches);
1428 }
1429 if (mVDriftCalibOn) {
1430 calib.reserve(mNCalibPrelim * 1.2 + 1);
1431 }
1432 std::vector<int> tpcToFit;
1433 tpcToFit.reserve(mNMatches);
1434 for (int iTPC = 0; iTPC < (int)mTPCWork.size(); iTPC++) {
1435 const auto& tTPC = mTPCWork[iTPC];
1436 if (!isDisabledTPC(tTPC) && tTPC.gid.testBit(0)) {
1437 tpcToFit.push_back(iTPC);
1438 }
1439 }
1440 LOG(debug) << "Refitting winner matches";
1441 mWinnerChi2Refit.resize(mITSWork.size(), -1.f);
1442 int nToFit = (int)tpcToFit.size();
1443 unsigned int nFailedRefit{0};
1444
1445#ifdef WITH_OPENMP
1446#pragma omp parallel for schedule(dynamic) num_threads(mNThreads) \
1447 reduction(+ : nFailedRefit)
1448#endif
1449 for (int ifit = 0; ifit < nToFit; ifit++) {
1450 int iTPC = tpcToFit[ifit], iITS;
1451 const auto& tTPC = mTPCWork[iTPC];
1452 if (refitTrackTPCITS(ifit, iTPC, iITS, matchedTracks, matchLabels, calib)) {
1453 mWinnerChi2Refit[iITS] = matchedTracks.back().getChi2Refit();
1454 } else {
1455 ++nFailedRefit;
1456 }
1457 }
1458 LOGP(info, "Failed {} TPC-ITS refits out of {}", nFailedRefit, nToFit);
1459
1460 // suppress tracks failed on refit and fill calib/debug data (if needed)
1461 int last = nToFit;
1462 mNMatches = 0;
1463 for (int ifit = 0; ifit < nToFit; ifit++) {
1464 int itpc = tpcToFit[ifit];
1465 if (!matchedTracks[ifit].isValid()) { // move the last good track from the back to the slot to delete
1466 while (--last > ifit && !matchedTracks[last].isValid()) {
1467 } // find the highest valid track
1468 if (last > ifit) {
1469 matchedTracks[ifit] = matchedTracks[last];
1470 matchedTracks[last].invalidate();
1471 itpc = tpcToFit[last];
1472 if (mMCTruthON) {
1473 matchLabels[ifit] = matchLabels[last];
1474 }
1475 } else {
1476 break;
1477 }
1478 }
1479 if (mDBGOut || mVDriftCalibOn) {
1480 fillCalibDebug(ifit, itpc, matchedTracks[ifit], calib);
1481 }
1482 mNMatches++;
1483 }
1484 // adjust sizes
1485 matchedTracks.resize(mNMatches);
1486 if (mMCTruthON) {
1487 matchLabels.resize(mNMatches);
1488 }
1489 mTimer[SWRefit].Stop();
1490}
1491
1492//______________________________________________
1494{
1495 const auto& tTPC = mTPCWork[iTPC];
1496 int iITS = mMatchRecordsTPC[tTPC.matchID].partnerID;
1497 const auto& tITS = mITSWork[iITS];
1498 float minDiffFT0 = -999., timeC = 0.f;
1499 std::vector<float> dtimes;
1500 bool fillVDCalib = mVDriftCalibOn && (!mFieldON || std::abs(match.getQ2Pt()) < mParams->maxVDriftTrackQ2Pt);
1501 if (fillVDCalib || mDBGOut) {
1502 timeC = match.getTimeMUS().getTimeStamp(); // find closest FIT record
1503 float minDiffA = mParams->maxVDritTimeOffset;
1504 if (mInteractions.size()) {
1505 int timeC0 = timeC - minDiffA;
1506 if (timeC0 < 0) {
1507 timeC0 = 0;
1508 }
1509 auto entStart = timeC0 < int(mInteractionMUSLUT.size()) ? mInteractionMUSLUT[timeC0] : (mInteractionMUSLUT.size() ? mInteractionMUSLUT.back() : 0);
1510 for (int ent = entStart; ent < (int)mInteractions.size(); ent++) {
1511 float diff = mInteractions[ent].tBracket.mean() - timeC;
1512 if (diff > minDiffA) {
1513 break; // all following will be the same
1514 }
1515 if (diff < -minDiffA) {
1516 continue;
1517 }
1518 dtimes.push_back(diff);
1519 minDiffFT0 = diff;
1520 minDiffA = std::abs(minDiffFT0);
1521 }
1522 }
1523 }
1524 if (fillVDCalib) {
1525 calib.emplace_back(tITS.getTgl(), tTPC.getTgl(), minDiffFT0);
1526 }
1527#ifdef _ALLOW_DEBUG_TREES_
1528 if (mDBGOut) {
1529 o2::track::TrackPar itsRefPIDCorr(tITS); // version ad hoc corrected for TPC PID being different from the pion, as it is done in the matching
1530 o2::track::TrackParCov itsRefAltPID(tITS); // version with full propagation to account for TPC PID being different from the pion
1531 itsRefPIDCorr.setX(0);
1532 if (tTPC.getPID() > tITS.getPID() && tITS.dL > 0.f && tTPC.getP2() / tTPC.getPID().getMass2() < mParams->minBetaGammaForPIDDiff) {
1533 itsRefAltPID = mITSTracksArray[tITS.sourceID].getParamOut(); // clone original ITS track at highest update point
1534 itsRefPIDCorr.setPID(tTPC.getPID(), true);
1535 itsRefPIDCorr = itsRefAltPID;
1536 // fast correction
1537 if (!itsRefPIDCorr.correctForELoss(tITS.xrho)) {
1538 itsRefPIDCorr.setX(-10);
1539 } else {
1540 float q2ptPID = itsRefPIDCorr.getQ2Pt();
1541 float dCurv = (q2ptPID - tITS.getQ2Pt()) * mBz * o2::constants::math::B2C, dLEff = tITS.dL * mParams->ITSStepEffFraction, dCurvL = dCurv * dLEff;
1542 itsRefPIDCorr = tITS;
1543 itsRefPIDCorr.setPID(tTPC.getPID(), true);
1544 itsRefPIDCorr.setQ2Pt(q2ptPID);
1545 auto snp = tITS.getSnp() + dCurvL;
1546 if (std::abs(snp) >= 1.) {
1547 snp = std::copysign(0.99, snp);
1548 }
1549 itsRefPIDCorr.setSnp(snp);
1550 itsRefPIDCorr.setY(tITS.getY() + dCurvL * dLEff * 0.5);
1551 }
1552 // full propagation
1553 if (!itsRefAltPID.rotate(tTPC.getAlpha()) || !o2::base::Propagator::Instance()->PropagateToXBxByBz(itsRefAltPID, mParams->XMatchingRef, MaxSnp, 2., mUseMatCorrFlag)) {
1554 itsRefAltPID.setX(-10);
1555 }
1556 }
1557 int tb = mTPCTracksArray[tTPC.sourceID].getTime0() * mNTPCOccBinLengthInv;
1558 float mltTPC = tb < 0 ? mTBinClOcc[0] : (tb >= mTBinClOcc.size() ? mTBinClOcc.back() : mTBinClOcc[tb]);
1559 (*mDBGOut) << "refit"
1560 << "tpcOrig=" << mTPCTracksArray[tTPC.sourceID] << "itsOrig=" << mITSTracksArray[tITS.sourceID] << "itsRef=" << tITS << "tpcRef=" << tTPC << "matchRefit=" << match
1561 << "timeCorr=" << timeC << "dTimeFT0=" << minDiffFT0 << "dTimes=" << dtimes
1562 << "itsRefAltPID=" << itsRefAltPID << "itsRefPIDCorr=" << itsRefPIDCorr;
1563 if (mMCTruthON) {
1564 (*mDBGOut) << "refit"
1565 << "itsLbl=" << mITSLblWork[iITS] << "tpcLbl=" << mTPCLblWork[iTPC];
1566 }
1567 (*mDBGOut) << "refit"
1568 << "multTPC=" << mltTPC
1569 << "multITSTr=" << mITSTrackROFRec[tITS.roFrame].getNEntries()
1570 << "multITSCl=" << mITSClusterROFRec[tITS.roFrame].getNEntries()
1571 << "tf=" << mTFCount << "\n";
1572 }
1573#endif
1574}
1575
1576//______________________________________________
1578{
1580
1581 const float maxStep = 2.f; // max propagation step (TODO: tune)
1582 const auto& tTPC = mTPCWork[iTPC];
1583 const auto& tpcMatchRec = mMatchRecordsTPC[tTPC.matchID];
1584 iITS = tpcMatchRec.partnerID;
1585 const auto& tITS = mITSWork[iITS];
1586 const auto& itsTrOrig = mITSTracksArray[tITS.sourceID];
1587 auto& trfit = matchedTracks[slot];
1588 ((o2::track::TrackParCov&)trfit) = (const o2::track::TrackParCov&)tTPC;
1589 trfit.getParamOut() = (const o2::track::TrackParCov&)tITS; // create a copy of TPC track at xRef
1590 trfit.getParamOut().setUserField(0); // reset eventual clones flag
1591 trfit.setPID(tTPC.getPID(), true);
1592 trfit.getParamOut().setPID(tTPC.getPID(), true);
1593 // in continuos mode the Z of TPC track is meaningless, unless it is CE crossing
1594 // track (currently absent, TODO)
1595 if (!mCompareTracksDZ) {
1596 trfit.setZ(tITS.getZ()); // fix the seed Z
1597 }
1598 float deltaT = (trfit.getZ() - tTPC.getZ()) * mTPCVDriftInv; // time correction in \mus
1599 float timeErr = tTPC.constraint == TrackLocTPC::Constrained ? tTPC.timeErr : std::sqrt(tITS.getSigmaZ2() + tTPC.getSigmaZ2()) * mTPCVDriftInv; // estimate the error on time
1600 if (timeErr > mITSTimeResMUS && tTPC.constraint != TrackLocTPC::Constrained) {
1601 timeErr = mITSTimeResMUS; // chose smallest error
1602 deltaT = tTPC.constraint == TrackLocTPC::ASide ? tITS.tBracket.mean() - tTPC.time0 : tTPC.time0 - tITS.tBracket.mean();
1603 }
1604 timeErr += mParams->globalTimeExtraErrorMUS;
1605 float timeC = tTPC.getCorrectedTime(deltaT) + mParams->globalTimeBiasMUS;
1610 } else { // == MatchTPCITSParams::TimeOutliersPolicy::Reject
1611 trfit.invalidate();
1612 return false;
1613 }
1614 }
1615 if (timeC < 0) { // RS TODO similar check is needed for other edge of TF
1616 if (timeC + std::min(timeErr, mParams->tfEdgeTimeToleranceMUS * mTPCTBinMUSInv) < 0) {
1617 trfit.invalidate(); // destroy failed track
1618 return false;
1619 }
1620 timeC = 0.;
1621 }
1622
1623 // refit TPC track inward into the ITS
1624 int nclRefit = 0, ncl = itsTrOrig.getNumberOfClusters();
1625 float chi2 = 0.f;
1626 auto geom = o2::its::GeometryTGeo::Instance();
1627 auto propagator = o2::base::Propagator::Instance();
1628 int clEntry = itsTrOrig.getFirstClusterEntry();
1629
1630 float addErr2 = 0;
1631 // extra error on tgl due to the assumed vdrift uncertainty
1632 if (mVDriftCalibOn) {
1633 addErr2 = tITS.getParam(o2::track::kTgl) * mParams->maxVDriftUncertainty;
1634 addErr2 *= addErr2;
1635 trfit.updateCov(addErr2, o2::track::kSigTgl2);
1636 }
1637
1638 for (int icl = 0; icl < ncl; icl++) {
1639 const auto& clus = mITSClustersArray[mITSTrackClusIdx[clEntry++]];
1640 float alpha = geom->getSensorRefAlpha(clus.getSensorID()), x = clus.getX();
1641 if (!trfit.rotate(alpha) ||
1642 // note: here we also calculate the L,T integral (in the inward direction, but this is irrelevant)
1643 // note: we should eventually use TPC pid in the refit (TODO)
1644 // note: since we are at small R, we can use field BZ component at origin rather than 3D field
1645 !propagator->propagateToX(trfit, x, propagator->getNominalBz(),
1646 MaxSnp, maxStep, mUseMatCorrFlag, &trfit.getLTIntegralOut())) {
1647 break;
1648 }
1649 chi2 += trfit.getPredictedChi2(clus);
1650 if (!trfit.update(clus)) {
1651 break;
1652 }
1653 nclRefit++;
1654 }
1655 if (nclRefit != ncl) {
1656 LOGP(debug, "Refit in ITS failed after ncl={}, match between TPC track #{} and ITS track #{}", nclRefit, tTPC.sourceID, tITS.sourceID);
1657 LOGP(debug, "{:s}", trfit.asString());
1658 trfit.invalidate(); // destroy failed track
1659 return false;
1660 }
1661
1662 // We need to update the LTOF integral by the distance to the "primary vertex"
1663 // We want to leave the track at the the position of its last update, so we do a fast propagation on the TrackPar copy of trfit,
1664 // and since for the LTOF calculation the material effects are irrelevant, we skip material corrections
1665 const o2::dataformats::VertexBase vtxDummy; // at the moment using dummy vertex: TODO use MeanVertex constraint instead
1666 o2::track::TrackPar trpar(trfit);
1667 if (!propagator->propagateToDCA(vtxDummy.getXYZ(), trpar, propagator->getNominalBz(),
1668 maxStep, MatCorrType::USEMatCorrNONE, nullptr, &trfit.getLTIntegralOut())) {
1669 LOG(error) << "LTOF integral might be incorrect";
1670 }
1671 auto& tofL = trfit.getLTIntegralOut(); // this is TL integral calculated from the RefX to the DCA to the beamline, invert material integrals for outward propagation
1672 tofL.setX2X0(-tofL.getX2X0());
1673 tofL.setXRho(-tofL.getXRho());
1674
1675 // outward refit
1676 auto& tracOut = trfit.getParamOut(); // this is a clone of ITS outward track already at the matching reference X
1677 if (tTPC.getPID() > tITS.getPID() && tTPC.getP2() / tTPC.getPID().getMass2() < mParams->minBetaGammaForPIDDiff) {
1678 // in case the TPC track hypothesis is not pion, we redo the outward propagation to ref.x with TPC PID
1679 tracOut = mITSTracksArray[tITS.sourceID].getParamOut();
1680 tracOut.setPID(tTPC.getPID(), true);
1681 if (!tracOut.rotate(tTPC.getAlpha()) || !o2::base::Propagator::Instance()->PropagateToXBxByBz(tracOut, mParams->XMatchingRef, MaxSnp, 2., mUseMatCorrFlag)) {
1682 LOGP(debug, "Failed to rotate ITSouter with imposed PID to TPC alpha {} or propagate to X={}: {:s}", tTPC.getAlpha(), mParams->XMatchingRef, tracOut.asString());
1683 trfit.invalidate(); // destroy failed track
1684 return false;
1685 }
1686 }
1687 {
1688 float xtogo = 0;
1689 if (!tracOut.getXatLabR(o2::constants::geom::XTPCInnerRef, xtogo, mBz, o2::track::DirOutward) ||
1690 !propagator->PropagateToXBxByBz(tracOut, xtogo, MaxSnp, 10., mUseMatCorrFlag, &tofL)) {
1691 LOG(debug) << "Propagation to inner TPC boundary X=" << xtogo << " failed, Xtr=" << tracOut.getX() << " snp=" << tracOut.getSnp();
1692 trfit.invalidate(); // destroy failed track
1693 return false;
1694 }
1695 if (mVDriftCalibOn) {
1696 tracOut.updateCov(addErr2, o2::track::kSigTgl2);
1697 }
1698 float chi2Out = 0;
1699 auto posStart = tracOut.getXYZGlo();
1700 auto tImposed = timeC * mTPCTBinMUSInv;
1701 if (std::abs(tImposed - mTPCTracksArray[tTPC.sourceID].getTime0()) > 550) {
1702 LOGP(alarm, "Impossible imposed timebin {} for TPC track time0:{}, dBwd:{} dFwd:{} TB | ZShift:{}, TShift:{}", tImposed, mTPCTracksArray[tTPC.sourceID].getTime0(),
1703 mTPCTracksArray[tTPC.sourceID].getDeltaTBwd(), mTPCTracksArray[tTPC.sourceID].getDeltaTFwd(), trfit.getZ() - tTPC.getZ(), deltaT);
1704 LOGP(info, "Trc: {}", mTPCTracksArray[tTPC.sourceID].asString());
1705 trfit.invalidate(); // destroy failed track
1706 return false;
1707 }
1708 int retVal = mTPCRefitter->RefitTrackAsTrackParCov(tracOut, mTPCTracksArray[tTPC.sourceID].getClusterRef(), tImposed, &chi2Out, true, false); // outward refit
1709 if (retVal < 0) {
1710 LOG(debug) << "Refit failed";
1711 trfit.invalidate(); // destroy failed track
1712 return false;
1713 }
1714 auto posEnd = tracOut.getXYZGlo();
1715 auto lInt = propagator->estimateLTIncrement(tracOut, posStart, posEnd);
1716 tofL.addStep(lInt, tracOut.getQ2P2());
1717 tofL.addX2X0(lInt * mTPCmeanX0Inv);
1718 propagator->PropagateToXBxByBz(tracOut, o2::constants::geom::XTPCOuterRef, MaxSnp, 10., mUseMatCorrFlag, &tofL);
1719
1720 const auto& trackTune = TrackTuneParams::Instance();
1721 if (trackTune.useTPCOuterCorr) {
1722 tracOut.updateParams(trackTune.tpcParOuter);
1723 }
1724 if (trackTune.tpcCovOuterType != TrackTuneParams::AddCovType::Disable) {
1725 tracOut.updateCov(mCovDiagOuter, trackTune.tpcCovOuterType == TrackTuneParams::AddCovType::WithCorrelations);
1726 }
1727 }
1728 trfit.setChi2Match(tpcMatchRec.chi2);
1729 trfit.setChi2Refit(chi2);
1730 trfit.setTimeMUS(timeC, timeErr);
1731 trfit.setRefTPC({unsigned(tTPC.sourceID), o2::dataformats::GlobalTrackID::TPC});
1732 trfit.setRefITS({unsigned(tITS.sourceID), o2::dataformats::GlobalTrackID::ITS});
1733
1734 if (mMCTruthON) { // store MC info: we assign TPC track label and declare the match fake if the ITS and TPC labels are different (their fake flag is ignored)
1735 matchLabels[slot] = mTPCLblWork[iTPC];
1736 matchLabels[slot].setFakeFlag(mITSLblWork[iITS] != mTPCLblWork[iTPC]);
1737 }
1738
1739 return true;
1740}
1741
1742//______________________________________________
1743bool MatchTPCITS::refitABTrack(int iITSAB, const TPCABSeed& seed, pmr::vector<o2::dataformats::TrackTPCITS>& matchedTracks, pmr::vector<int>& ABTrackletClusterIDs, pmr::vector<o2::itsmft::TrkClusRef>& ABTrackletRefs)
1744{
1746
1747 const float maxStep = 2.f; // max propagation step (TODO: tune)
1748 const auto& tTPC = mTPCWork[seed.tpcWID];
1749 const auto& winLink = seed.getLink(seed.winLinkID);
1750 auto& newtr = matchedTracks.emplace_back(winLink, winLink); // create a copy of winner param at innermost ITS cluster
1751 auto& tracOut = newtr.getParamOut();
1752 auto& tofL = newtr.getLTIntegralOut();
1753 auto geom = o2::its::GeometryTGeo::Instance();
1754 auto propagator = o2::base::Propagator::Instance();
1755 tracOut.resetCovariance();
1756 o2::track::TrackPar refLin(tracOut);
1757 propagator->estimateLTFast(tofL, winLink); // guess about initial value for the track integral from the origin
1758 // refit track outward in the ITS
1759 const auto& itsClRefs = ABTrackletRefs[iITSAB];
1760 int nclRefit = 0, ncl = itsClRefs.getNClusters();
1761 float chi2 = 0.f;
1762 // NOTE: the ITS cluster absolute indices are stored from inner to outer layers
1763 for (int icl = itsClRefs.getFirstEntry(); icl < itsClRefs.getEntriesBound(); icl++) {
1764 const auto& clus = mITSClustersArray[ABTrackletClusterIDs[icl]];
1765 float alpha = geom->getSensorRefAlpha(clus.getSensorID()), x = clus.getX();
1766 if (!tracOut.rotate(alpha, refLin, propagator->getNominalBz()) ||
1767 // note: here we also calculate the L,T integral
1768 // note: we should eventually use TPC pid in the refit (TODO)
1769 // note: since we are at small R, we can use field BZ component at origin rather than 3D field
1770 !propagator->propagateToX(tracOut, refLin, x, propagator->getNominalBz(), MaxSnp, maxStep, mUseMatCorrFlag, &tofL)) {
1771 break;
1772 }
1773 chi2 += tracOut.getPredictedChi2(clus);
1774 if (!tracOut.update(clus)) {
1775 break;
1776 }
1777 nclRefit++;
1778 }
1779 if (nclRefit != ncl) {
1780 LOGP(debug, "AfterBurner refit in ITS failed after ncl={}, match between TPC track #{} and ITS tracklet #{}", nclRefit, tTPC.sourceID, iITSAB);
1781 LOGP(debug, "{:s}", tracOut.asString());
1782 matchedTracks.pop_back(); // destroy failed track
1783 return false;
1784 }
1785 // perform TPC refit with interaction time constraint
1786 float timeC = mInteractions[seed.ICCanID].tBracket.mean();
1787 float timeErr = mInteractions[seed.ICCanID].tBracket.delta(); // RS FIXME shall we use gaussian error?
1788 {
1789 float xtogo = 0;
1790 if (!tracOut.getXatLabR(o2::constants::geom::XTPCInnerRef, xtogo, mBz, o2::track::DirOutward) ||
1791 !propagator->PropagateToXBxByBz(tracOut, refLin, xtogo, MaxSnp, 10., mUseMatCorrFlag, &tofL)) {
1792 LOG(debug) << "Propagation to inner TPC boundary X=" << xtogo << " failed, Xtr=" << tracOut.getX() << " snp=" << tracOut.getSnp();
1793 matchedTracks.pop_back(); // destroy failed track
1794 return false;
1795 }
1796 float chi2Out = 0;
1797 auto posStart = tracOut.getXYZGlo();
1798 int retVal = mTPCRefitter->RefitTrackAsTrackParCov(tracOut, mTPCTracksArray[tTPC.sourceID].getClusterRef(), timeC * mTPCTBinMUSInv, &chi2Out, true, false); // outward refit
1799 if (retVal < 0) {
1800 LOG(debug) << "Refit failed";
1801 matchedTracks.pop_back(); // destroy failed track
1802 return false;
1803 }
1804 auto posEnd = tracOut.getXYZGlo();
1805 auto lInt = propagator->estimateLTIncrement(tracOut, posStart, posEnd);
1806 tofL.addStep(lInt, tracOut.getQ2P2());
1807 tofL.addX2X0(lInt * mTPCmeanX0Inv);
1808 propagator->PropagateToXBxByBz(tracOut, o2::constants::geom::XTPCOuterRef, MaxSnp, 10., mUseMatCorrFlag, &tofL);
1809 const auto& trackTune = TrackTuneParams::Instance();
1810 if (trackTune.useTPCOuterCorr) {
1811 tracOut.updateParams(trackTune.tpcParOuter);
1812 }
1813 if (trackTune.tpcCovOuterType != TrackTuneParams::AddCovType::Disable) {
1814 tracOut.updateCov(mCovDiagOuter, trackTune.tpcCovOuterType == TrackTuneParams::AddCovType::WithCorrelations);
1815 }
1816 }
1817
1818 newtr.setChi2Match(winLink.chi2Norm());
1819 newtr.setChi2Refit(chi2);
1820 newtr.setTimeMUS(timeC, timeErr);
1821 newtr.setRefTPC({unsigned(tTPC.sourceID), o2::dataformats::GlobalTrackID::TPC});
1822 newtr.setRefITS({unsigned(iITSAB), o2::dataformats::GlobalTrackID::ITSAB});
1823
1824 return true;
1825}
1826
1827//______________________________________________
1828bool MatchTPCITS::refitTPCInward(o2::track::TrackParCov& trcIn, float& chi2, float xTgt, int trcID, float timeTB) const
1829{
1830 // inward refit
1831 const auto& tpcTrOrig = mTPCTracksArray[trcID];
1832
1833 trcIn = tpcTrOrig.getOuterParam();
1834 chi2 = 0;
1835
1836 auto propagator = o2::base::Propagator::Instance();
1837 int retVal = mTPCRefitter->RefitTrackAsTrackParCov(trcIn, tpcTrOrig.getClusterRef(), timeTB, &chi2, false, true); // inward refit with matrix reset
1838 if (retVal < 0) {
1839 LOG(warning) << "Refit failed";
1840 LOG(warning) << trcIn.asString();
1841 return false;
1842 }
1843 //
1844 // propagate to the inner edge of the TPC
1845 // Note: it is allowed to not reach the requested radius
1846 if (!propagator->PropagateToXBxByBz(trcIn, xTgt, MaxSnp, 2., mUseMatCorrFlag)) {
1847 LOG(debug) << "Propagation to target X=" << xTgt << " failed, Xtr=" << trcIn.getX() << " snp=" << trcIn.getSnp() << " pT=" << trcIn.getPt();
1848 LOG(debug) << trcIn.asString();
1849 return false;
1850 }
1851 return true;
1852}
1853
1854//>>============================= AfterBurner for TPC-track / ITS cluster matching ===================>>
1855//______________________________________________
1856int MatchTPCITS::prepareABSeeds()
1857{
1859 const auto& outerLr = mRGHelper.layers.back();
1860 // to avoid difference between 3D field propagation and Bz-bazed getXatLabR we propagate RMax+margin
1861 const float ROuter = outerLr.rRange.getMax() + 0.5f;
1862 auto propagator = o2::base::Propagator::Instance();
1863
1864 for (int iTPC = 0; iTPC < (int)mTPCWork.size(); iTPC++) {
1865 auto& tTPC = mTPCWork[iTPC];
1866 if (isDisabledTPC(tTPC)) {
1867 // Popagate to the vicinity of the out layer. Note: the Z of the track might be uncertain,
1868 // in this case the material corrections will be correct only in the limit of their uniformity in Z,
1869 // which should be good assumption....
1870 float xTgt;
1871 if (!tTPC.getXatLabR(ROuter, xTgt, propagator->getNominalBz(), o2::track::DirInward) ||
1872 !propagator->PropagateToXBxByBz(tTPC, xTgt, MaxSnp, 2., mUseMatCorrFlag)) {
1873 continue;
1874 }
1875 mTPCABIndexCache.push_back(iTPC);
1876 }
1877 }
1878 // sort tracks according to their timeMin
1879 std::sort(mTPCABIndexCache.begin(), mTPCABIndexCache.end(), [this](int a, int b) {
1880 auto& trcA = mTPCWork[a];
1881 auto& trcB = mTPCWork[b];
1882 return (trcA.tBracket.getMin() - trcB.tBracket.getMin()) < 0.;
1883 });
1884
1885 float maxTDriftSafe = tpcTimeBin2MUS(mNTPCBinsFullDrift + mParams->safeMarginTPCITSTimeBin + mTPCTimeEdgeTSafeMargin);
1886 int nIntCand = mInteractions.size(), nTPCCand = mTPCABIndexCache.size();
1887 int tpcStart = 0;
1888 for (int ic = 0; ic < nIntCand; ic++) {
1889 int icFirstSeed = mTPCABSeeds.size();
1890 auto& intCand = mInteractions[ic];
1891 auto tic = intCand.tBracket.mean();
1892 for (int it = tpcStart; it < nTPCCand; it++) {
1893 auto& trc = mTPCWork[mTPCABIndexCache[it]];
1894 auto cmp = trc.tBracket.isOutside(intCand.tBracket);
1895 if (cmp < 0) {
1896 break; // all other TPC tracks will be also in future wrt the interaction
1897 }
1898 if (cmp > 0) {
1899 if (trc.tBracket.getMin() + maxTDriftSafe < intCand.tBracket.getMin()) {
1900 tpcStart++; // all following int.candidates would be in future wrt this track
1901 }
1902 continue;
1903 }
1904 // we beed to create seed from this TPC track and interaction candidate
1905 float dt = trc.getSignedDT(tic - trc.time0);
1906 float dz = dt * mTPCVDrift, z = trc.getZ() + dz;
1907 if (outerLr.zRange.isOutside(z, std::sqrt(trc.getSigmaZ2()) + 2.)) { // RS FIXME introduce margin as parameter?
1908 continue;
1909 }
1910 // make sure seed crosses the outer ITS layer (with some margin)
1911 auto& seed = mTPCABSeeds.emplace_back(mTPCABIndexCache[it], ic, trc);
1912 seed.track.setZ(z); // RS FIXME : in case of distortions and large dz the track must be refitted
1913 }
1914 intCand.seedsRef.set(icFirstSeed, mTPCABSeeds.size() - icFirstSeed);
1915 }
1916 return mTPCABIndexCache.size();
1917}
1918
1919//______________________________________________
1920int MatchTPCITS::prepareInteractionTimes()
1921{
1922 // guess interaction times from various sources and relate with ITS rofs
1923 const float ft0Uncertainty = 0.5e-3;
1924 int nITSROFs = mITSROFTimes.size();
1925 if (mFITInfo.size()) {
1926 int rof = 0;
1927 for (const auto& ft : mFITInfo) {
1928 if (!mFT0Params->isSelected(ft)) {
1929 continue;
1930 }
1931 auto fitTime = ft.getInteractionRecord().differenceInBCMUS(mStartIR);
1932 if (fitTime < 0) { // should not happen
1933 continue;
1934 }
1935 if (size_t(fitTime) >= mInteractionMUSLUT.size()) {
1936 mInteractionMUSLUT.resize(size_t(fitTime) + 1, -1);
1937 }
1938 if (mInteractionMUSLUT[fitTime] < 0) {
1939 mInteractionMUSLUT[fitTime] = mInteractions.size();
1940 }
1941 for (; rof < nITSROFs; rof++) {
1942 if (mITSROFTimes[rof] < fitTime) {
1943 continue;
1944 }
1945 break;
1946 }
1947 if (rof >= nITSROFs) {
1948 break;
1949 }
1950 mInteractions.emplace_back(ft.getInteractionRecord(), fitTime, ft0Uncertainty, rof, o2::detectors::DetID::FT0);
1951 }
1952 }
1953 int ent = 0;
1954 for (auto& val : mInteractionMUSLUT) {
1955 if (val < 0) { // was not assigned == no interactions in this mus, assign previous one
1956 val = ent;
1957 } else {
1958 ent = val > 0 ? val - 1 : val;
1959 }
1960 }
1961 return mInteractions.size();
1962}
1963
1964//______________________________________________
1967{
1968 mTimer[SWABSeeds].Start(false);
1969 mNABRefsClus = 0;
1970 prepareABSeeds();
1971 int nIntCand = mInteractions.size(), nABSeeds = mTPCABSeeds.size();
1972 LOGP(info, "AfterBurner will check {} seeds from {} TPC tracks and {} interaction candidates with {} threads", nABSeeds, mTPCABIndexCache.size(), nIntCand, mNThreads); // TMP
1973 mTimer[SWABSeeds].Stop();
1974 if (!nIntCand || !mTPCABSeeds.size()) {
1975 return false;
1976 }
1977 mTimer[SWABMatch].Start(false);
1978
1979 std::vector<ITSChipClustersRefs> itsChipClRefsBuff(mNThreads);
1980#ifdef ENABLE_UPGRADES
1981 // with upgrades the datatype changed, hence we need to initialize
1982 // each element individually
1983 std::generate(itsChipClRefsBuff.begin(), itsChipClRefsBuff.end(), []() {
1984 return ITSChipClustersRefs(o2::its::GeometryTGeo::Instance()->getNumberOfChips());
1985 });
1986#endif
1987
1988#ifdef WITH_OPENMP
1989#pragma omp parallel for schedule(dynamic) num_threads(mNThreads)
1990#endif
1991 for (int ic = 0; ic < nIntCand; ic++) {
1992 const auto& intCand = mInteractions[ic];
1993 LOGP(debug, "cand T {} Entries: {} : {} : {} | ITS ROF: {}", intCand.tBracket.mean(), intCand.seedsRef.getEntries(), intCand.seedsRef.getFirstEntry(), intCand.seedsRef.getEntriesBound(), intCand.rofITS);
1994 if (!intCand.seedsRef.getEntries()) {
1995 continue;
1996 }
1997#ifdef WITH_OPENMP
1998 uint8_t tid = (uint8_t)omp_get_thread_num();
1999#else
2000 uint8_t tid = 0;
2001#endif
2002 fillClustersForAfterBurner(intCand.rofITS, 1, itsChipClRefsBuff[tid]); // RS FIXME account for possibility of filling 2 ROFs
2003 for (int is = intCand.seedsRef.getFirstEntry(); is < intCand.seedsRef.getEntriesBound(); is++) { // loop over all seeds of this interaction candidate
2004 processABSeed(is, itsChipClRefsBuff[tid], tid);
2005 }
2006 }
2007 mTimer[SWABMatch].Stop();
2008 mTimer[SWABWinners].Start(false);
2009 int nwin = 0;
2010 // select winners
2011 struct SID {
2012 int seedID = -1;
2013 float chi2 = 1e9;
2014 };
2015 std::vector<SID> candAB;
2016 candAB.reserve(nABSeeds);
2017 mABWinnersIDs.reserve(mTPCABIndexCache.size());
2018
2019 for (int i = 0; i < nABSeeds; i++) {
2020 auto& ABSeed = mTPCABSeeds[i];
2021 if (ABSeed.isDisabled()) {
2022 continue;
2023 }
2024 candAB.emplace_back(SID{i, ABSeed.getLink(ABSeed.getBestLinkID()).chi2Norm()});
2025 }
2026 std::sort(candAB.begin(), candAB.end(), [](SID a, SID b) { return a.chi2 < b.chi2; });
2027 for (int i = 0; i < (int)candAB.size(); i++) {
2028 auto& ABSeed = mTPCABSeeds[candAB[i].seedID];
2029 if (ABSeed.isDisabled()) {
2030 // RSTMP LOG(info) << "Iter: " << iter << " seed is disabled: " << i << "[" << candAB[i].seedID << "/" << candAB[i].chi2 << "]" << " last lr: " << int(ABSeed.lowestLayer);
2031 continue;
2032 }
2033 auto& tTPC = mTPCWork[ABSeed.tpcWID];
2034 if (tTPC.matchID > MinusOne) { // this tracks was already validated with other IC
2035 ABSeed.disable();
2036 // RSTMP LOG(info) << "Iter: " << iter << " disabling seed " << i << "[" << candAB[i].seedID << "/" << candAB[i].chi2 << "]" << " TPC track " << ABSeed.tpcWID << " already validated" << " last lr: " << int(ABSeed.lowestLayer);
2037 continue;
2038 }
2039 auto bestID = ABSeed.getBestLinkID();
2040 if (ABSeed.checkLinkHasUsedClusters(bestID, mABClusterLinkIndex)) {
2041 ABSeed.setNeedAlternative(); // flag for later processing
2042 // RSTMP LOG(info) << "Iter: " << iter << " seed has used clusters " << i << "[" << candAB[i].seedID << "/" << candAB[i].chi2 << "]" << " last lr: " << int(ABSeed.lowestLayer) << " Ncont: " << int(link.nContLayers);;
2043 continue;
2044 }
2045 ABSeed.validate(bestID);
2046 ABSeed.flagLinkUsedClusters(bestID, mABClusterLinkIndex);
2047 mABWinnersIDs.push_back(tTPC.matchID = candAB[i].seedID);
2048 mNABRefsClus += ABSeed.getNLayers();
2049 nwin++;
2050 // RSTMP LOG(info) << "Iter: " << iter << " validated seed " << i << "[" << candAB[i].seedID << "/" << candAB[i].chi2 << "] for TPC track " << ABSeed.tpcWID << " last lr: " << int(ABSeed.lowestLayer) << " Ncont: " << int(link.nContLayers);
2051 }
2052 mTimer[SWABWinners].Stop();
2053 mTimer[SWABRefit].Start(false);
2054 refitABWinners(matchedTracks, matchLabels, ABTrackletLabels, ABTrackletClusterIDs, ABTrackletRefs, calib);
2055 mTimer[SWABRefit].Stop();
2056 return true;
2057}
2058
2059//______________________________________________
2062{
2063 // refit normal matches
2064 refitWinners(matchedTracks, matchLabels, calib);
2065
2066 ABTrackletClusterIDs.reserve(mNABRefsClus);
2067 ABTrackletRefs.reserve(mABWinnersIDs.size());
2068 if (mMCTruthON) {
2069 ABTrackletLabels.reserve(mABWinnersIDs.size());
2070 }
2071 if (matchedTracks.capacity() < mABWinnersIDs.size() + matchedTracks.size()) {
2072 LOGP(warn, "need to expand matched tracks container from {} to {}", matchedTracks.capacity(), mABWinnersIDs.size() + matchedTracks.size());
2073 matchedTracks.reserve(mABWinnersIDs.size() + matchedTracks.size());
2074 if (mMCTruthON) {
2075 matchLabels.reserve(mABWinnersIDs.size() + matchedTracks.size());
2076 }
2077 }
2078
2079 std::map<o2::MCCompLabel, int> labelOccurence;
2080 auto accountClusterLabel = [&labelOccurence, itsClLabs = mITSClsLabels](int clID) {
2081 auto labels = itsClLabs->getLabels(clID);
2082 for (auto lab : labels) { // check all labels of the cluster
2083 if (lab.isSet()) {
2084 labelOccurence[lab]++;
2085 }
2086 }
2087 };
2088
2089 for (auto wid : mABWinnersIDs) {
2090 const auto& ABSeed = mTPCABSeeds[wid];
2091 int start = ABTrackletClusterIDs.size();
2092 int lID = ABSeed.winLinkID, ncl = 0;
2093 auto& clref = ABTrackletRefs.emplace_back(start, ncl);
2094 while (lID > MinusOne) {
2095 const auto& winL = ABSeed.getLink(lID);
2096 if (winL.clID > MinusOne) {
2097 ABTrackletClusterIDs.push_back(winL.clID);
2098 ncl++;
2099 clref.pattern |= 0x1 << winL.layerID;
2100 clref.setClusterSize(winL.layerID, mITSClusterSizes[winL.clID]);
2101 if (mMCTruthON) {
2102 accountClusterLabel(winL.clID);
2103 }
2104 }
2105 lID = winL.parentID;
2106 }
2107 clref.setEntries(ncl);
2108 if (!refitABTrack(ABTrackletRefs.size() - 1, ABSeed, matchedTracks, ABTrackletClusterIDs, ABTrackletRefs)) { // on failure, destroy added tracklet reference
2109 ABTrackletRefs.pop_back();
2110 ABTrackletClusterIDs.resize(start); // RSS
2111 if (mMCTruthON) {
2112 labelOccurence.clear();
2113 }
2114 continue;
2115 }
2116 if (mMCTruthON) {
2117 o2::MCCompLabel lab;
2118 int maxL = 0; // find most encountered label
2119 for (auto [label, count] : labelOccurence) {
2120 if (count > maxL) {
2121 maxL = count;
2122 lab = label;
2123 }
2124 }
2125 if (maxL < ncl) {
2126 lab.setFakeFlag();
2127 }
2128 labelOccurence.clear();
2129 ABTrackletLabels.push_back(lab); // ITSAB tracklet label
2130 auto& lblGlo = matchLabels.emplace_back(mTPCLblWork[ABSeed.tpcWID]);
2131 lblGlo.setFakeFlag(lab != lblGlo);
2132 LOG(debug) << "ABWinner ncl=" << ncl << " mcLBAB " << lab << " mcLBGlo " << lblGlo << " chi2=" << ABSeed.getLink(ABSeed.winLinkID).chi2Norm() << " pT = " << ABSeed.track.getPt();
2133 }
2134 // build MC label
2135 }
2136 LOG(info) << "AfterBurner validated " << ABTrackletRefs.size() << " tracks";
2137}
2138
2139//______________________________________________
2140void MatchTPCITS::processABSeed(int sid, const ITSChipClustersRefs& itsChipClRefs, uint8_t tID)
2141{
2142 // prepare matching hypothesis tree for given seed
2143 auto& ABSeed = mTPCABSeeds[sid];
2144 ABSeed.threadID = tID;
2145 ABSeed.linksEntry = mABLinksPool.threadPool[tID].size();
2146 followABSeed(ABSeed.track, itsChipClRefs, MinusTen, NITSLayers - 1, ABSeed); // check matches on outermost layer
2147 for (int ilr = NITSLayers - 1; ilr > mParams->lowestLayerAB; ilr--) {
2148 int nextLinkID = ABSeed.firstInLr[ilr];
2149 if (nextLinkID < 0) {
2150 break;
2151 }
2152 while (nextLinkID > MinusOne) {
2153 const auto& seedLink = ABSeed.getLink(nextLinkID);
2154 if (seedLink.isDisabled()) {
2155 nextLinkID = seedLink.nextOnLr;
2156 continue;
2157 }
2158 int next2nextLinkID = seedLink.nextOnLr; // fetch now since the seedLink may change due to the relocation
2159 followABSeed(seedLink, itsChipClRefs, nextLinkID, ilr - 1, ABSeed); // check matches on the next layer
2160 nextLinkID = next2nextLinkID;
2161 }
2162 }
2163 // is this seed has chance to be validated?
2164 auto candID = ABSeed.getBestLinkID();
2165 if (ABSeed.isDisabled() ||
2166 ABSeed.lowestLayer > mParams->requireToReachLayerAB ||
2167 candID < 0 ||
2168 ABSeed.getLink(candID).nContLayers < mParams->minContributingLayersAB) { // free unused links
2169 ABSeed.disable();
2170 mABLinksPool.threadPool[tID].resize(size_t(ABSeed.linksEntry));
2171 }
2172
2173 /* // RS FIXME remove on final clean-up
2174 auto bestLinkID = ABSeed.getBestLinkID();
2175 if (bestLinkID>MinusOne) {
2176 const auto& bestL = ABSeed.getLink(bestLinkID);
2177 LOG(info) << "seed " << sid << " last lr: " << int(ABSeed.lowestLayer) << " Ncont: " << int(bestL.nContLayers) << " chi2 " << bestL.chi2;
2178 }
2179 else {
2180 LOG(info) << "seed " << sid << " : NONE";
2181 }
2182 */
2183}
2184
2185//______________________________________________
2186int MatchTPCITS::followABSeed(const o2::track::TrackParCov& seed, const ITSChipClustersRefs& itsChipClRefs, int seedID, int lrID, TPCABSeed& ABSeed)
2187{
2188
2189 auto propagator = o2::base::Propagator::Instance();
2190 float xTgt;
2191 const auto& lr = mRGHelper.layers[lrID];
2192 auto seedC = seed;
2193 if (!seedC.getXatLabR(lr.rRange.getMax(), xTgt, propagator->getNominalBz(), o2::track::DirInward) ||
2194 !propagator->propagateToX(seedC, xTgt, propagator->getNominalBz(), MaxSnp, 2., mUseMatCorrFlag)) { // Bz-propagation only in ITS
2195 return -1;
2196 }
2197
2198 float zDRStep = -seedC.getTgl() * lr.rRange.delta(); // approximate Z span when going from layer rMin to rMax
2199 float errZ = std::sqrt(seedC.getSigmaZ2() + mParams->err2ABExtraZ);
2200 if (lr.zRange.isOutside(seedC.getZ(), mParams->nABSigmaZ * errZ + std::abs(zDRStep))) {
2201 return 0;
2202 }
2203 std::vector<int> chipSelClusters; // preliminary cluster candidates //RS TODO do we keep this local / consider array instead of vector
2204 o2::math_utils::CircleXYf_t trcCircle; // circle parameters for B ON data
2205 o2::math_utils::IntervalXYf_t trcLinPar; // line parameters for B OFF data
2206 float sna, csa;
2207 // approximate errors
2208 float errY = std::sqrt(seedC.getSigmaY2() + mParams->err2ABExtraY), errYFrac = errY * mRGHelper.ladderWidthInv();
2209 if (mFieldON) {
2210 seedC.getCircleParams(propagator->getNominalBz(), trcCircle, sna, csa);
2211 } else {
2212 seedC.getLineParams(trcLinPar, sna, csa);
2213 }
2214 float xCurr, yCurr;
2215 o2::math_utils::rotateZ(seedC.getX(), seedC.getY(), xCurr, yCurr, sna, csa); // lab X,Y
2216 float phi = std::atan2(yCurr, xCurr); // RS: TODO : can we use fast atan2 here?
2217 // find approximate ladder and chip_in_ladder corresponding to this track extrapolation
2218 int nLad2Check = 0, ladIDguess = lr.getLadderID(phi), chipIDguess = lr.getChipID(seedC.getZ() + 0.5 * zDRStep);
2219 std::array<int, MaxLadderCand> lad2Check;
2220 nLad2Check = mFieldON ? findLaddersToCheckBOn(lrID, ladIDguess, trcCircle, errYFrac, lad2Check) : findLaddersToCheckBOff(lrID, ladIDguess, trcLinPar, errYFrac, lad2Check);
2221
2222 for (int ilad = nLad2Check; ilad--;) {
2223 int ladID = lad2Check[ilad];
2224 const auto& lad = lr.ladders[ladID];
2225
2226 // we assume that close chips on the same ladder will have close xyEdges, so it is enough to calculate track-chip crossing
2227 // coordinates xCross,yCross,zCross for this central chipIDguess, although we are going to check also neighbours
2228 float t = 1e9, xCross, yCross;
2229 const auto& chipC = lad.chips[chipIDguess];
2230 if (mFieldON) {
2231 chipC.xyEdges.circleCrossParam(trcCircle, t);
2232 } else {
2233 chipC.xyEdges.lineCrossParam(trcLinPar, t);
2234 }
2235 chipC.xyEdges.eval(t, xCross, yCross);
2236 float dx = xCross - xCurr, dy = yCross - yCurr, dst2 = dx * dx + dy * dy, dst = sqrtf(dst2);
2237 // Z-step sign depends on radius decreasing or increasing during the propagation
2238 float zCross = seedC.getZ() + seedC.getTgl() * (dst2 < 2 * (dx * xCurr + dy * yCurr) ? dst : -dst);
2239
2240 for (int ich = -1; ich < 2; ich++) {
2241 int chipID = chipIDguess + ich;
2242
2243 if (chipID < 0 || chipID >= static_cast<int>(lad.chips.size())) {
2244 continue;
2245 }
2246 if (lad.chips[chipID].zRange.isOutside(zCross, mParams->nABSigmaZ * errZ)) {
2247 continue;
2248 }
2249 const auto& clRange = itsChipClRefs.chipRefs[lad.chips[chipID].id];
2250 if (!clRange.getEntries()) {
2251 LOG(debug) << "No clusters in chip range";
2252 continue;
2253 }
2254 // track Y error in chip frame
2255 float errYcalp = errY * (csa * chipC.csAlp + sna * chipC.snAlp); // sigY_rotate(from alpha0 to alpha1) = sigY * cos(alpha1 - alpha0);
2256 float tolerZ = errZ * mParams->nABSigmaZ, tolerY = errYcalp * mParams->nABSigmaY;
2257 float yTrack = -xCross * chipC.snAlp + yCross * chipC.csAlp; // track-chip crossing Y in chip frame
2258 if (!preselectChipClusters(chipSelClusters, clRange, itsChipClRefs, yTrack, zCross, tolerY, tolerZ)) { // select candidate clusters for this chip
2259 LOG(debug) << "No compatible clusters found";
2260 continue;
2261 }
2262 o2::track::TrackParCov trcLC = seedC;
2263
2264 if (!trcLC.rotate(chipC.alp) || !trcLC.propagateTo(chipC.xRef, propagator->getNominalBz())) {
2265 LOG(debug) << " failed to rotate to alpha=" << chipC.alp << " or prop to X=" << chipC.xRef;
2266 // trcLC.print();
2267 break; // the chips of the ladder are practically on the same X and alpha
2268 }
2269
2270 for (auto clID : chipSelClusters) {
2271 const auto& cls = mITSClustersArray[clID];
2272 auto chi2 = trcLC.getPredictedChi2(cls);
2273 if (chi2 > mParams->cutABTrack2ClChi2) {
2274 continue;
2275 }
2276 int lnkID = registerABTrackLink(ABSeed, trcLC, clID, seedID, lrID, ladID, chi2); // add new link with track copy
2277 if (lnkID > MinusOne) {
2278 auto& link = ABSeed.getLink(lnkID);
2279 link.update(cls);
2280 if (seedID >= MinusOne) {
2281 ABSeed.getLink(seedID).nDaughters++; // RS FIXME : do we need this?
2282 }
2283 if (lrID < ABSeed.lowestLayer) {
2284 ABSeed.lowestLayer = lrID; // update lowest layer reached
2285 }
2286 }
2287 }
2288 }
2289 }
2290 return 0;
2291}
2292
2293//______________________________________________
2294void MatchTPCITS::accountForOverlapsAB(int lrSeed)
2295{
2296 // TODO
2297 LOG(warning) << "TODO";
2298}
2299
2300//______________________________________________
2301int MatchTPCITS::findLaddersToCheckBOn(int ilr, int lad0, const o2::math_utils::CircleXYf_t& circle, float errYFrac,
2302 std::array<int, MatchTPCITS::MaxLadderCand>& lad2Check) const
2303{
2304 // check if ladder lad0 and at most +-MaxUpDnLadders around it are compatible with circular track of
2305 // r^2 = r2 and centered at xC,yC
2306 const auto& lr = mRGHelper.layers[ilr];
2307 int nacc = 0, jmp = 0;
2308 if (lr.ladders[lad0].xyEdges.seenByCircle(circle, errYFrac)) {
2309 lad2Check[nacc++] = lad0;
2310 }
2311 bool doUp = true, doDn = true;
2312 while ((doUp || doDn) && jmp++ < MaxUpDnLadders) {
2313 if (doUp) {
2314 int ldID = (lad0 + jmp) % lr.nLadders;
2315 if (lr.ladders[ldID].xyEdges.seenByCircle(circle, errYFrac)) {
2316 lad2Check[nacc++] = ldID;
2317 } else {
2318 doUp = false;
2319 }
2320 }
2321 if (doDn) {
2322 int ldID = lad0 - jmp;
2323 if (ldID < 0) {
2324 ldID += lr.nLadders;
2325 }
2326 if (lr.ladders[ldID].xyEdges.seenByCircle(circle, errYFrac)) {
2327 lad2Check[nacc++] = ldID;
2328 } else {
2329 doDn = false;
2330 }
2331 }
2332 }
2333 return nacc;
2334}
2335
2336//______________________________________________
2337int MatchTPCITS::findLaddersToCheckBOff(int ilr, int lad0, const o2::math_utils::IntervalXYf_t& trcLinPar, float errYFrac,
2338 std::array<int, MatchTPCITS::MaxLadderCand>& lad2Check) const
2339{
2340 // check if ladder lad0 and at most +-MaxUpDnLadders around it are compatible with linear track
2341
2342 const auto& lr = mRGHelper.layers[ilr];
2343 int nacc = 0, jmp = 0;
2344 if (lr.ladders[lad0].xyEdges.seenByLine(trcLinPar, errYFrac)) {
2345 lad2Check[nacc++] = lad0;
2346 }
2347 bool doUp = true, doDn = true;
2348 while ((doUp || doDn) && jmp++ < MaxUpDnLadders) {
2349 if (doUp) {
2350 int ldID = (lad0 + jmp) % lr.nLadders;
2351 if (lr.ladders[ldID].xyEdges.seenByLine(trcLinPar, errYFrac)) {
2352 lad2Check[nacc++] = ldID;
2353 } else {
2354 doUp = false;
2355 }
2356 }
2357 if (doDn) {
2358 int ldID = lad0 - jmp;
2359 if (ldID < 0) {
2360 ldID += lr.nLadders;
2361 }
2362 if (lr.ladders[ldID].xyEdges.seenByLine(trcLinPar, errYFrac)) {
2363 lad2Check[nacc++] = ldID;
2364 } else {
2365 doDn = false;
2366 }
2367 }
2368 }
2369 return nacc;
2370}
2371
2372//______________________________________________
2373int MatchTPCITS::registerABTrackLink(TPCABSeed& ABSeed, const o2::track::TrackParCov& trc, int clID, int parentID, int lr, int laddID, float chi2Cl)
2374{
2375 // registers new ABLink on the layer, assigning provided kinematics. The link will be registered in a
2376 // way preserving the quality ordering of the links on the layer
2377 int lnkID = ABSeed.getNLinks(), nextID = ABSeed.firstInLr[lr], nc = 1 + (parentID > MinusOne ? ABSeed.getLink(parentID).nContLayers : 0);
2378 float chi2 = chi2Cl + (parentID > MinusOne ? ABSeed.getLink(parentID).chi2 : 0.);
2379 // LOG(info) << "Reg on lr " << lr << " nc = " << nc << " chi2cl=" << chi2Cl << " -> " << chi2; // RSTMP
2380
2381 if (ABSeed.firstInLr[lr] == MinusOne) { // no links on this layer yet
2382 ABSeed.firstInLr[lr] = lnkID;
2383 ABSeed.addLink(trc, clID, parentID, MinusOne, lr, nc, laddID, chi2);
2384 return lnkID;
2385 }
2386 // add new link sorting links of this layer in quality
2387
2388 int count = 0, topID = MinusOne;
2389 do {
2390 auto& nextLink = ABSeed.getLink(nextID);
2391 count++;
2392 bool newIsBetter = parentID <= MinusOne ? isBetter(chi2, nextLink.chi2) : isBetter(ABSeed.getLink(parentID).chi2NormPredict(chi2Cl), nextLink.chi2Norm());
2393 if (newIsBetter) { // need to insert new link before nextLink
2394 if (count < mParams->maxABLinksOnLayer) { // will insert in front of nextID
2395 ABSeed.addLink(trc, clID, parentID, nextID, lr, nc, laddID, chi2);
2396 if (topID == MinusOne) { // are we comparing new link with best link on the layer?
2397 ABSeed.firstInLr[lr] = lnkID; // flag as best on the layer
2398 } else {
2399 ABSeed.getLink(topID).nextOnLr = lnkID; // point from previous one
2400 }
2401 return lnkID;
2402 } else { // max number of candidates reached, will overwrite the last one
2403 nextLink = ABTrackLink(trc, clID, parentID, MinusOne, lr, nc, laddID, chi2);
2404 return nextID;
2405 }
2406 }
2407 topID = nextID;
2408 nextID = nextLink.nextOnLr;
2409 } while (nextID > MinusOne);
2410 // new link is worse than all others, add it only if there is a room to expand
2411 if (count < mParams->maxABLinksOnLayer) {
2412 ABSeed.addLink(trc, clID, parentID, MinusOne, lr, nc, laddID, chi2);
2413 if (topID > MinusOne) {
2414 ABSeed.getLink(topID).nextOnLr = lnkID; // point from previous one
2415 }
2416 return lnkID;
2417 }
2418 return MinusOne; // link to be ignored
2419}
2420
2421//______________________________________________
2422float MatchTPCITS::correctTPCTrack(o2::track::TrackParCov& trc, const TrackLocTPC& tTPC, const InteractionCandidate& cand) const
2423{
2424 // Correct the track copy trc of the working TPC track tTPC in continuous RO mode for the assumed interaction time
2425 // return extra uncertainty in Z due to the interaction time incertainty
2426 // TODO: at the moment, apply simple shift, but with Z-dependent calibration we may
2427 // need to do corrections on TPC cluster level and refit
2429 return 0.f;
2430 }
2431 auto tpcTrOrig = mTPCTracksArray[tTPC.sourceID];
2432 float timeIC = cand.tBracket.mean();
2433 float driftErr = cand.tBracket.delta() * mTPCBin2Z;
2434
2435 // we use this for refit, at the moment it is not done ...
2436 /*
2437 {
2438 float r = std::sqrt(trc.getX()*trc.getX() + trc.getY()*trc.getY());
2439 float chi2 = 0;
2440 bool res = refitTPCInward(trc, chi2, r, tTPC.sourceID(), timeIC );
2441 if (!res) {
2442 return -1;
2443 }
2444 float xTgt;
2445 auto propagator = o2::base::Propagator::Instance();
2446 if (!trc.getXatLabR(r, xTgt, propagator->getNominalBz(), o2::track::DirInward) ||
2447 !propagator->PropagateToXBxByBz(trc, xTgt, MaxSnp, 2., mUseMatCorrFlag)) {
2448 return -1;
2449 }
2450 }
2451 */
2452 // if interaction time precedes the initial assumption on t0 (i.e. timeIC < timeTrc),
2453 // the track actually was drifting longer, i.e. tracks should be shifted closer to the CE
2454 float dDrift = (timeIC - tTPC.time0) * mTPCBin2Z;
2455
2456 // float zz = tTPC.getZ() + (tpcTrOrig.hasASideClustersOnly() ? dDrift : -dDrift); // tmp
2457 // LOG(info) << "CorrTrack Z=" << trc.getZ() << " (zold= " << zz << ") at TIC= " << timeIC << " Ttr= " << tTPC.time0; // tmp
2458
2459 // we use this w/o refit
2460 //
2461 {
2462 trc.setZ(tTPC.getZ() + (tTPC.constraint == TrackLocTPC::ASide ? dDrift : -dDrift));
2463 }
2464 //
2465 trc.setCov(trc.getSigmaZ2() + driftErr * driftErr, o2::track::kSigZ2);
2466
2467 return driftErr;
2468}
2469
2470//______________________________________________
2471void MatchTPCITS::fillClustersForAfterBurner(int rofStart, int nROFs, ITSChipClustersRefs& itsChipClRefs)
2472{
2473 // Prepare unused clusters of given ROFs range for matching in the afterburner
2474 // Note: normally only 1 ROF needs to be filled (nROFs==1 ) unless we want
2475 // to account for interaction on the boundary of 2 rofs, which then may contribute to both ROFs.
2476 int first = mITSClusterROFRec[rofStart].getFirstEntry(), last = first;
2477 for (int ir = nROFs; ir--;) {
2478 last += mITSClusterROFRec[rofStart + ir].getNEntries();
2479 }
2480 itsChipClRefs.clear();
2481 auto& idxSort = itsChipClRefs.clusterID;
2482 for (int icl = first; icl < last; icl++) {
2483 if (mABClusterLinkIndex[icl] != MinusTen) { // clusters with MinusOne are used in main matching
2484 idxSort.push_back(icl);
2485 }
2486 }
2487 // sort in chip, Z
2488 const auto& clusArr = mITSClustersArray;
2489 std::sort(idxSort.begin(), idxSort.end(), [&clusArr](int i, int j) {
2490 const auto &clI = clusArr[i], &clJ = clusArr[j];
2491 if (clI.getSensorID() < clJ.getSensorID()) {
2492 return true;
2493 }
2494 if (clI.getSensorID() == clJ.getSensorID()) {
2495 return clI.getZ() < clJ.getZ();
2496 }
2497 return false;
2498 });
2499
2500 int ncl = idxSort.size();
2501 int lastSens = -1, nClInSens = 0;
2502 ClusRange* chipClRefs = nullptr;
2503 for (int icl = 0; icl < ncl; icl++) {
2504 const auto& clus = mITSClustersArray[idxSort[icl]];
2505 int sens = clus.getSensorID();
2506 if (sens != lastSens) {
2507 if (chipClRefs) { // finalize chip reference
2508 chipClRefs->setEntries(nClInSens);
2509 nClInSens = 0;
2510 }
2511 chipClRefs = &itsChipClRefs.chipRefs[(lastSens = sens)];
2512 chipClRefs->setFirstEntry(icl);
2513 }
2514 nClInSens++;
2515 }
2516 if (chipClRefs) {
2517 chipClRefs->setEntries(nClInSens); // finalize last chip reference
2518 }
2519}
2520
2521//______________________________________________
2523{
2524 mITSTimeBiasInBC = n;
2525 mITSTimeBiasMUS = mITSTimeBiasInBC * o2::constants::lhc::LHCBunchSpacingNS * 1e-3;
2526}
2527
2528//______________________________________________
2530{
2531 mITSROFrameLengthMUS = fums;
2532 mITSTimeResMUS = mITSROFrameLengthMUS / std::sqrt(12.f);
2533 mITSROFrameLengthMUSInv = 1. / mITSROFrameLengthMUS;
2534 mITSROFrameLengthInBC = std::max(1, int(mITSROFrameLengthMUS / (o2::constants::lhc::LHCBunchSpacingNS * 1e-3)));
2535}
2536
2537//______________________________________________
2539{
2540 mITSROFrameLengthInBC = nbc;
2541 mITSROFrameLengthMUS = nbc * o2::constants::lhc::LHCBunchSpacingNS * 1e-3;
2542 mITSTimeResMUS = mITSROFrameLengthMUS / std::sqrt(12.f);
2543 mITSROFrameLengthMUSInv = 1. / mITSROFrameLengthMUS;
2544}
2545
2546//___________________________________________________________________
2548{
2549 mBunchFilling = bf;
2550 // find closest (from above) filled bunch
2551 int minBC = bf.getFirstFilledBC(), maxBC = bf.getLastFilledBC();
2552 if (minBC < 0 && mUseBCFilling) {
2553 if (mUseBCFilling) {
2554 mUseBCFilling = false;
2555 LOG(warning) << "Disabling match validation by BunchFilling as no interacting bunches found";
2556 }
2557 return;
2558 }
2559 int bcAbove = minBC;
2560 for (int i = o2::constants::lhc::LHCMaxBunches; i--;) {
2561 if (bf.testBC(i)) {
2562 bcAbove = i;
2563 }
2564 mClosestBunchAbove[i] = bcAbove;
2565 }
2566 int bcBelow = maxBC;
2567 for (int i = 0; i < o2::constants::lhc::LHCMaxBunches; i++) {
2568 if (bf.testBC(i)) {
2569 bcBelow = i;
2570 }
2571 mClosestBunchBelow[i] = bcBelow;
2572 }
2573}
2574
2575//___________________________________________________________________
2576MatchTPCITS::BracketIR MatchTPCITS::tBracket2IRBracket(const BracketF tbrange)
2577{
2578 // convert time bracket to IR bracket
2579 o2::InteractionRecord irMin(mStartIR), irMax(mStartIR);
2580 if (tbrange.getMin() > 0) {
2581 irMin += o2::InteractionRecord(tbrange.getMin() * 1000.f); // time in ns is needed
2582 }
2583 irMax += o2::InteractionRecord(tbrange.getMax() * 1000.f);
2584 irMax++; // to account for rounding
2585 int bc = mClosestBunchAbove[irMin.bc];
2586 if (bc < irMin.bc) {
2587 irMin.orbit++;
2588 }
2589 irMin.bc = bc;
2590 bc = mClosestBunchBelow[irMax.bc];
2591 if (bc > irMax.bc) {
2592 if (irMax.orbit == 0) {
2593 bc = 0;
2594 } else {
2595 irMax.orbit--;
2596 }
2597 }
2598 irMax.bc = bc;
2599 return {irMin, irMax};
2600}
2601
2602//______________________________________________
2603void MatchTPCITS::removeTPCfromITS(int tpcID, int itsID)
2604{
2606 auto& tITS = mITSWork[itsID];
2607 if (isValidatedITS(tITS)) {
2608 return;
2609 }
2610 int topID = MinusOne, next = tITS.matchID; // ITS MatchRecord
2611 while (next > MinusOne) {
2612 auto& rcITS = mMatchRecordsITS[next];
2613 if (rcITS.partnerID == tpcID) {
2614 if (topID < 0) {
2615 tITS.matchID = rcITS.nextRecID;
2616 } else {
2617 mMatchRecordsITS[topID].nextRecID = rcITS.nextRecID;
2618 }
2619 return;
2620 }
2621 topID = next;
2622 next = rcITS.nextRecID;
2623 }
2624}
2625
2626//______________________________________________
2627void MatchTPCITS::removeITSfromTPC(int itsID, int tpcID)
2628{
2630 auto& tTPC = mTPCWork[tpcID];
2631 if (isValidatedTPC(tTPC)) {
2632 return;
2633 }
2634 int topID = MinusOne, next = tTPC.matchID;
2635 while (next > MinusOne) {
2636 auto& rcTPC = mMatchRecordsTPC[next];
2637 if (rcTPC.partnerID == itsID) {
2638 if (topID < 0) {
2639 tTPC.matchID = rcTPC.nextRecID;
2640 } else {
2641 mMatchRecordsTPC[topID].nextRecID = rcTPC.nextRecID;
2642 }
2643 return;
2644 }
2645 topID = next;
2646 next = rcTPC.nextRecID;
2647 }
2648}
2649
2650//______________________________________________
2651void MatchTPCITS::flagUsedITSClusters(const o2::its::TrackITS& track)
2652{
2653 // flag clusters used by this track
2654 int clEntry = track.getFirstClusterEntry();
2655 for (int icl = track.getNumberOfClusters(); icl--;) {
2656 mABClusterLinkIndex[mITSTrackClusIdx[clEntry++]] = MinusTen;
2657 }
2658}
2659
2660//__________________________________________________________
2661int MatchTPCITS::preselectChipClusters(std::vector<int>& clVecOut, const ClusRange& clRange, const ITSChipClustersRefs& itsChipClRefs,
2662 float trackY, float trackZ, float tolerY, float tolerZ) const
2663{
2664 clVecOut.clear();
2665 int icID = clRange.getFirstEntry();
2666 for (int icl = clRange.getEntries(); icl--;) { // note: clusters within a chip are sorted in Z
2667 int clID = itsChipClRefs.clusterID[icID++]; // so, we go in clusterID increasing direction
2668 const auto& cls = mITSClustersArray[clID];
2669 float dz = cls.getZ() - trackZ;
2670 LOG(debug) << "cl" << icl << '/' << clID << " "
2671 << " dZ: " << dz << " [" << tolerZ << "| dY: " << trackY - cls.getY() << " [" << tolerY << "]";
2672 if (dz > tolerZ) {
2673 float clsZ = cls.getZ();
2674 LOG(debug) << "Skip the rest since " << trackZ << " < " << clsZ << "\n";
2675 break;
2676 } else if (dz < -tolerZ) {
2677 LOG(debug) << "Skip cluster dz=" << dz << " Ztr=" << trackZ << " zCl=" << cls.getZ();
2678 continue;
2679 }
2680 if (fabs(trackY - cls.getY()) > tolerY) {
2681 LOG(debug) << "Skip cluster dy= " << trackY - cls.getY() << " Ytr=" << trackY << " yCl=" << cls.getY();
2682 continue;
2683 }
2684 clVecOut.push_back(clID);
2685 }
2686 return clVecOut.size();
2687}
2688
2689//__________________________________________________________
2692 pmr::vector<int>& ABTrackletClusterIDs,
2693 pmr::vector<o2::MCCompLabel>& matchLabels,
2694 pmr::vector<o2::MCCompLabel>& ABTrackletLabels,
2696{
2697 size_t sizTotShm = 0, capTotShm = 0, sizTot = 0, capTot = 0, siz = 0, cap = 0, cnt = 0, cntCap = 0;
2698 {
2699 siz = matchedTracks.size() * sizeof(o2::dataformats::TrackTPCITS);
2700 cap = matchedTracks.capacity() * sizeof(o2::dataformats::TrackTPCITS);
2701 sizTotShm += siz;
2702 capTotShm += cap;
2703 LOGP(info, "Size SHM, matchedTracks : size {:9} cap {:9}", siz, cap);
2704 //
2705 siz = ABTrackletRefs.size() * sizeof(o2::itsmft::TrkClusRef);
2706 cap = ABTrackletRefs.capacity() * sizeof(o2::itsmft::TrkClusRef);
2707 sizTotShm += siz;
2708 capTotShm += cap;
2709 LOGP(info, "Size SHM, ABTrackletRefs : size {:9} cap {:9}", siz, cap);
2710 //
2711 siz = ABTrackletClusterIDs.size() * sizeof(int);
2712 cap = ABTrackletClusterIDs.capacity() * sizeof(int);
2713 sizTotShm += siz;
2714 capTotShm += cap;
2715 LOGP(info, "Size SHM, ABTrackletClusterIDs : size {:9} cap {:9}", siz, cap);
2716 //
2717 siz = matchLabels.size() * sizeof(o2::MCCompLabel);
2718 cap = matchLabels.capacity() * sizeof(o2::MCCompLabel);
2719 sizTotShm += siz;
2720 capTotShm += cap;
2721 LOGP(info, "Size SHM, matchLabels : size {:9} cap {:9}", siz, cap);
2722 //
2723 siz = ABTrackletLabels.size() * sizeof(o2::MCCompLabel);
2724 cap = ABTrackletLabels.capacity() * sizeof(o2::MCCompLabel);
2725 sizTotShm += siz;
2726 capTotShm += cap;
2727 LOGP(info, "Size SHM, ABTrackletLabels : size {:9} cap {:9}", siz, cap);
2728 //
2729 siz = calib.size() * sizeof(o2::dataformats::Triplet<float, float, float>);
2730 cap = calib.capacity() * sizeof(o2::dataformats::Triplet<float, float, float>);
2731 sizTotShm += siz;
2732 capTotShm += cap;
2733 LOGP(info, "Size SHM, calib : size {:9} cap {:9}", siz, cap);
2734 }
2735 {
2736 siz = mITSClustersArray.size() * sizeof(ITSCluster);
2737 cap = mITSClustersArray.capacity() * sizeof(ITSCluster);
2738 sizTot += siz;
2739 capTot += cap;
2740 LOGP(info, "Size RSS, mITSClustersArray : size {:9} cap {:9}", siz, cap);
2741 //
2742 siz = mMatchRecordsTPC.size() * sizeof(MatchRecord);
2743 cap = mMatchRecordsTPC.capacity() * sizeof(MatchRecord);
2744 sizTot += siz;
2745 capTot += cap;
2746 LOGP(info, "Size RSS, mMatchRecordsTPC : size {:9} cap {:9}", siz, cap);
2747 //
2748 siz = mMatchRecordsITS.size() * sizeof(MatchRecord);
2749 cap = mMatchRecordsITS.capacity() * sizeof(MatchRecord);
2750 sizTot += siz;
2751 capTot += cap;
2752 LOGP(info, "Size RSS, mMatchRecordsITS : size {:9} cap {:9}", siz, cap);
2753 //
2754 siz = mITSROFTimes.size() * sizeof(BracketF);
2755 cap = mITSROFTimes.capacity() * sizeof(BracketF);
2756 sizTot += siz;
2757 capTot += cap;
2758 LOGP(info, "Size RSS, mITSROFTimes : size {:9} cap {:9}", siz, cap);
2759 //
2760 siz = mTPCWork.size() * sizeof(TrackLocTPC);
2761 cap = mTPCWork.capacity() * sizeof(TrackLocTPC);
2762 sizTot += siz;
2763 capTot += cap;
2764 LOGP(info, "Size RSS, mTPCWork : size {:9} cap {:9}", siz, cap);
2765 //
2766 siz = mITSWork.size() * sizeof(TrackLocITS);
2767 cap = mITSWork.capacity() * sizeof(TrackLocITS);
2768 sizTot += siz;
2769 capTot += cap;
2770 LOGP(info, "Size RSS, mITSWork : size {:9} cap {:9}", siz, cap);
2771 //
2772 siz = mWinnerChi2Refit.size() * sizeof(float);
2773 cap = mWinnerChi2Refit.capacity() * sizeof(float);
2774 sizTot += siz;
2775 capTot += cap;
2776 LOGP(info, "Size RSS, mWinnerChi2Refit : size {:9} cap {:9}", siz, cap);
2777 //
2778 siz = mTPCABSeeds.size() * sizeof(float);
2779 cap = mTPCABSeeds.capacity() * sizeof(float);
2780 cnt = 0;
2781 cntCap = 0;
2782 for (const auto& a : mTPCABSeeds) {
2783 siz += a.sizeInternal();
2784 cap += a.capInternal();
2785 cnt += a.getNLinks();
2786 cntCap += a.getNLinks();
2787 }
2788 sizTot += siz;
2789 capTot += cap;
2790 LOGP(info, "Size RSS, mTPCABSeeds : size {:9} cap {:9} | internals size:{}/capacity:{} for {} elements", siz, cap, cnt, cntCap, mTPCABSeeds.size());
2791 //
2792 siz = mTPCABIndexCache.size() * sizeof(int);
2793 cap = mTPCABIndexCache.capacity() * sizeof(int);
2794 sizTot += siz;
2795 capTot += cap;
2796 LOGP(info, "Size RSS, mTPCABIndexCache : size {:9} cap {:9}", siz, cap);
2797 //
2798 siz = mABWinnersIDs.size() * sizeof(int);
2799 cap = mABWinnersIDs.capacity() * sizeof(int);
2800 sizTot += siz;
2801 capTot += cap;
2802 LOGP(info, "Size RSS, mABWinnersIDs : size {:9} cap {:9}", siz, cap);
2803 //
2804 siz = mABClusterLinkIndex.size() * sizeof(int);
2805 cap = mABClusterLinkIndex.capacity() * sizeof(int);
2806 sizTot += siz;
2807 capTot += cap;
2808 LOGP(info, "Size RSS, mABClusterLinkIndex : size {:9} cap {:9}", siz, cap);
2809 //
2810 for (int is = 0; is < o2::constants::math::NSectors; is++) {
2811 siz += mTPCSectIndexCache[is].size() * sizeof(int);
2812 cap += mTPCSectIndexCache[is].capacity() * sizeof(int);
2813 }
2814 sizTot += siz;
2815 capTot += cap;
2816 LOGP(info, "Size RSS, mTPCSectIndexCache : size {:9} cap {:9}", siz, cap);
2817 //
2818 for (int is = 0; is < o2::constants::math::NSectors; is++) {
2819 siz += mITSSectIndexCache[is].size() * sizeof(int);
2820 cap += mITSSectIndexCache[is].capacity() * sizeof(int);
2821 }
2822 sizTot += siz;
2823 capTot += cap;
2824 LOGP(info, "Size RSS, mITSSectIndexCache : size {:9} cap {:9}", siz, cap);
2825 //
2826 for (int is = 0; is < o2::constants::math::NSectors; is++) {
2827 siz += mTPCTimeStart[is].size() * sizeof(int);
2828 cap += mTPCTimeStart[is].capacity() * sizeof(int);
2829 }
2830 sizTot += siz;
2831 capTot += cap;
2832 LOGP(info, "Size RSS, mTPCTimeStart : size {:9} cap {:9}", siz, cap);
2833 //
2834 for (int is = 0; is < o2::constants::math::NSectors; is++) {
2835 siz += mITSTimeStart[is].size() * sizeof(int);
2836 cap += mITSTimeStart[is].capacity() * sizeof(int);
2837 }
2838 sizTot += siz;
2839 capTot += cap;
2840 LOGP(info, "Size RSS, mITSTimeStart : size {:9} cap {:9}", siz, cap);
2841 //
2842 siz = mITSTrackROFContMapping.size() * sizeof(int);
2843 cap = mITSTrackROFContMapping.capacity() * sizeof(int);
2844 sizTot += siz;
2845 capTot += cap;
2846 LOGP(info, "Size RSS, ITSTrackROFContMapping: size {:9} cap {:9}", siz, cap);
2847 }
2848 LOGP(info, "TotalSizes/Capacities: SHM: {}/{} Heap: {}/{}", sizTotShm, capTotShm, sizTot, capTot);
2849}
2850
2851//__________________________________________________________
2853{
2854#ifdef WITH_OPENMP
2855 mNThreads = n > 0 ? n : 1;
2856#else
2857 LOG(warning) << "Multithreading is not supported, imposing single thread";
2858 mNThreads = 1;
2859#endif
2860 mABLinksPool.threadPool.resize(mNThreads);
2861 TPCABSeed::gLinksPool = &mABLinksPool;
2862}
2863
2864//<<============================= AfterBurner for TPC-track / ITS cluster matching ===================<<
2865
2866#ifdef _ALLOW_DEBUG_TREES_
2867//______________________________________________
2868void MatchTPCITS::setDebugFlag(UInt_t flag, bool on)
2869{
2871 if (on) {
2872 mDBGFlags |= flag;
2873 } else {
2874 mDBGFlags &= ~flag;
2875 }
2876}
2877
2878//_________________________________________________________
2879void MatchTPCITS::dumpTPCOrig(bool acc, int tpcIndex)
2880{
2882 mTimer[SWDBG].Start(false);
2883 const auto& tpcOrig = mTPCTracksArray[tpcIndex];
2884 uint8_t clSect = 0, clRow = 0, prevRow = 0xff, padFromEdge = -1;
2885 uint32_t clIdx = 0;
2886 int nshared = 0;
2887 std::array<bool, 152> shMap{};
2888 bool prevRawShared = false;
2889 for (int i = 0; i < tpcOrig.getNClusterReferences(); i++) {
2890 tpcOrig.getClusterReference(mTPCTrackClusIdx, i, clSect, clRow, clIdx);
2891 unsigned int absoluteIndex = mTPCClusterIdxStruct->clusterOffset[clSect][clRow] + clIdx;
2892 if (mTPCRefitterShMap[absoluteIndex] & o2::gpu::GPUTPCGMMergedTrackHit::flagShared) {
2893 if (!(prevRow == clRow && prevRawShared)) {
2894 nshared++;
2895 }
2896 prevRow = clRow;
2897 prevRawShared = true;
2898 }
2899 }
2900 const auto& clus = mTPCClusterIdxStruct->clusters[clSect][clRow][clIdx];
2901 padFromEdge = uint8_t(clus.getPad());
2902 if (padFromEdge > TPCGeometry.NPads(clRow) / 2) {
2903 padFromEdge = TPCGeometry.NPads(clRow) - 1 - padFromEdge;
2904 }
2905 int tb = tpcOrig.getTime0() * mNTPCOccBinLengthInv;
2906 float mltTPC = tb < 0 ? mTBinClOcc[0] : (tb >= mTBinClOcc.size() ? mTBinClOcc.back() : mTBinClOcc[tb]);
2907 (*mDBGOut) << "tpcOrig"
2908 << "tf=" << mTFCount
2909 << "index=" << tpcIndex
2910 << "acc=" << acc
2911 << "chi2TPC=" << tpcOrig.getChi2()
2912 << "nClus=" << tpcOrig.getNClusters()
2913 << "nShared=" << nshared
2914 << "time0=" << tpcOrig.getTime0()
2915 << "trc=" << ((o2::track::TrackParCov&)tpcOrig)
2916 << "minRow=" << clRow
2917 << "padFromEdge=" << padFromEdge
2918 << "multTPC=" << mltTPC;
2919 if (mMCTruthON) {
2920 (*mDBGOut) << "tpcOrig"
2921 << "tpcLbl=" << mTPCTrkLabels[tpcIndex];
2922 }
2923 (*mDBGOut) << "tpcOrig"
2924 << "\n";
2925 mTimer[SWDBG].Stop();
2926}
2927
2928//_________________________________________________________
2929void MatchTPCITS::fillTPCITSmatchTree(int itsID, int tpcID, int rejFlag, float chi2, float tCorr)
2930{
2932
2933 mTimer[SWDBG].Start(false);
2934
2935 auto& trackITS = mITSWork[itsID];
2936 auto& trackTPC = mTPCWork[tpcID];
2937 if (chi2 < 0.) { // need to recalculate
2938 chi2 = getPredictedChi2NoZ(trackITS, trackTPC);
2939 }
2940 (*mDBGOut) << "match"
2941 << "tf=" << mTFCount << "chi2Match=" << chi2 << "its=" << trackITS << "tpc=" << trackTPC << "tcorr=" << tCorr;
2942 if (mMCTruthON) {
2943 (*mDBGOut) << "match"
2944 << "itsLbl=" << mITSLblWork[itsID] << "tpcLbl=" << mTPCLblWork[tpcID];
2945 }
2946 int tb = mTPCTracksArray[trackTPC.sourceID].getTime0() * mNTPCOccBinLengthInv;
2947 float mltTPC = tb < 0 ? mTBinClOcc[0] : (tb >= mTBinClOcc.size() ? mTBinClOcc.back() : mTBinClOcc[tb]);
2948 (*mDBGOut) << "match"
2949 << "rejFlag=" << rejFlag
2950 << "multTPC=" << mltTPC
2951 << "multITSTr=" << mITSTrackROFRec[trackITS.roFrame].getNEntries()
2952 << "multITSCl=" << mITSClusterROFRec[trackITS.roFrame].getNEntries()
2953 << "\n";
2954
2955 mTimer[SWDBG].Stop();
2956}
2957
2958//______________________________________________
2960{
2962
2963 mTimer[SWDBG].Start(false);
2964
2965 LOG(info) << "Dumping debug tree for winner matches";
2966 for (int iits = 0; iits < int(mITSWork.size()); iits++) {
2967 auto& tITS = mITSWork[iits];
2968 if (isDisabledITS(tITS)) {
2969 continue;
2970 }
2971 auto& itsMatchRec = mMatchRecordsITS[tITS.matchID];
2972 int itpc = itsMatchRec.partnerID;
2973 auto& tTPC = mTPCWork[itpc];
2974
2975 (*mDBGOut) << "matchWin"
2976 << "tf=" << mTFCount << "chi2Match=" << itsMatchRec.chi2 << "chi2Refit=" << mWinnerChi2Refit[iits] << "its=" << tITS << "tpc=" << tTPC;
2977
2978 if (mMCTruthON) {
2979 (*mDBGOut) << "matchWin"
2980 << "itsLbl=" << mITSLblWork[iits] << "tpcLbl=" << mTPCLblWork[itpc];
2981 }
2982 int tb = mTPCTracksArray[tTPC.sourceID].getTime0() * mNTPCOccBinLengthInv;
2983 float mltTPC = tb < 0 ? mTBinClOcc[0] : (tb >= mTBinClOcc.size() ? mTBinClOcc.back() : mTBinClOcc[tb]);
2984 (*mDBGOut) << "matchWin"
2985 << "multTPC=" << mltTPC
2986 << "multITSTr=" << mITSTrackROFRec[tITS.roFrame].getNEntries()
2987 << "multITSCl=" << mITSClusterROFRec[tITS.roFrame].getNEntries()
2988 << "\n";
2989 }
2990 mTimer[SWDBG].Stop();
2991}
2992
2993#endif
std::vector< std::string > labels
General auxilliary methods.
Wrapper container for different reconstructed object types.
Definition of the GeometryManager class.
std::ostringstream debug
int64_t timeC
uint64_t bc
Definition RawEventData.h:5
int32_t i
int32_t retVal
Helper for geometry and GRP related CCDB requests.
Some ALICE geometry constants of common interest.
constexpr int p1()
constexpr to accelerate the coordinates changing
Definition of the GeometryTGeo class.
Definition of a container to keep Monte Carlo truth external to simulation objects.
Definition of the fast magnetic field parametrization MagFieldFast.
Definition of the MagF class.
Class to perform TPC ITS matching.
useful math constants
Definition of the Names Generator class.
Definition of the parameter class for the detector.
Definition of the parameter class for the detector electronics.
Header to collect physics constants.
uint32_t j
Definition RawData.h:0
Wrapper container for different reconstructed object types.
class to create TPC fast transformation
Configurable params for tracks ad hoc tuning.
Helper class to obtain TPC clusters / digits / labels from DPL.
int getLastFilledBC(int dir=-1) const
bool testBC(int bcID, int dir=-1) const
int getFirstFilledBC(int dir=-1) const
void setFakeFlag(bool v=true)
GPUd() value_type estimateLTFast(o2 static GPUd() float estimateLTIncrement(const o2 PropagatorImpl * Instance(bool uninitialized=false)
Definition Propagator.h:178
void printKeyValues(bool showProv=true, bool useLogger=false, bool withPadding=true, bool showHash=true) const final
static constexpr ID FT0
Definition DetID.h:75
o2::math_utils::Bracket< float > BracketF
void setUseMatCorrFlag(MatCorrType f)
void fillCalibDebug(int ifit, int iTPC, const o2::dataformats::TrackTPCITS &match, pmr::vector< o2::dataformats::Triplet< float, float, float > > &calib)
o2::BaseCluster< float > ITSCluster
void reportTiming()
clear results of previous event reco
void setITSROFrameLengthMUS(float fums)
set ITS ROFrame duration in BC (continuous mode only)
@ MatchTreeAccOnly
fill the matching candidates tree only once the cut is passed
@ TPCOrigTree
original TPC tracks with some aux info
@ MatchTreeAll
produce matching candidates tree for all candidates
@ WinnerMatchesTree
separate debug tree for winner matches
void clear()
set Bunch filling and init helpers for validation by BCs
void refitWinners(pmr::vector< o2::dataformats::TrackTPCITS > &matchedTracks, pmr::vector< o2::MCCompLabel > &matchLabels, pmr::vector< o2::dataformats::Triplet< float, float, float > > &calib)
void refitABWinners(pmr::vector< o2::dataformats::TrackTPCITS > &matchedTracks, pmr::vector< o2::MCCompLabel > &matchLabels, pmr::vector< o2::MCCompLabel > &ABTrackletLabels, pmr::vector< int > &ABTrackletClusterIDs, pmr::vector< o2::itsmft::TrkClusRef > &ABTrackletRefs, pmr::vector< o2::dataformats::Triplet< float, float, float > > &calib)
bool refitABTrack(int iITSAB, const TPCABSeed &seed, pmr::vector< o2::dataformats::TrackTPCITS > &matchedTracks, pmr::vector< int > &ABTrackletClusterIDs, pmr::vector< o2::itsmft::TrkClusRef > &ABTrackletRefs)
void setTPCVDrift(const o2::tpc::VDriftCorrFact &v)
o2::dataformats::RangeReference< int, int > ClusRange
bool isDebugFlag(UInt_t flags) const
get debug trees flags
bool runAfterBurner(pmr::vector< o2::dataformats::TrackTPCITS > &matchedTracks, pmr::vector< o2::MCCompLabel > &matchLabels, pmr::vector< o2::MCCompLabel > &ABTrackletLabels, pmr::vector< int > &ABTrackletClusterIDs, pmr::vector< o2::itsmft::TrkClusRef > &ABTrackletRefs, pmr::vector< o2::dataformats::Triplet< float, float, float > > &calib)
void setTPCCorrMaps(o2::gpu::CorrectionMapsHelper *maph)
print settings
void reportSizes(pmr::vector< o2::dataformats::TrackTPCITS > &matchedTracks, pmr::vector< o2::itsmft::TrkClusRef > &ABTrackletRefs, pmr::vector< int > &ABTrackletClusterIDs, pmr::vector< o2::MCCompLabel > &matchLabels, pmr::vector< o2::MCCompLabel > &ABTrackletLabels, pmr::vector< o2::dataformats::Triplet< float, float, float > > &calib)
void fillTPCITSmatchTree(int itsID, int tpcID, int rejFlag, float chi2=-1., float tCorr=0.)
bool refitTrackTPCITS(int slot, int iTPC, int &iITS, pmr::vector< o2::dataformats::TrackTPCITS > &matchedTracks, pmr::vector< o2::MCCompLabel > &matchLabels, pmr::vector< o2::dataformats::Triplet< float, float, float > > &calib)
static constexpr int MaxUpDnLadders
void run(const o2::globaltracking::RecoContainer &inp, pmr::vector< o2::dataformats::TrackTPCITS > &matchedTracks, pmr::vector< o2::itsmft::TrkClusRef > &ABTrackletRefs, pmr::vector< int > &ABTrackletClusterIDs, pmr::vector< o2::MCCompLabel > &matchLabels, pmr::vector< o2::MCCompLabel > &ABTrackletLabels, pmr::vector< o2::dataformats::Triplet< float, float, float > > &calib)
void setBunchFilling(const o2::BunchFilling &bf)
void dumpTPCOrig(bool acc, int tpcIndex)
void setDebugFlag(UInt_t flag, bool on=true)
set the name of output debug file
static constexpr int NITSLayers
perform matching for provided input
UInt_t getDebugFlags() const
set or unset debug stream flag
void setNThreads(int n)
perform all initializations
static GeometryTGeo * Instance()
void fillMatrixCache(int mask) override
int getFirstClusterEntry() const
Definition TrackITS.h:66
void acquirePattern(iterator &pattIt)
int getNPixels() const
Returns the number of fired pixels.
static constexpr unsigned short InvalidPatternID
Definition CompCluster.h:46
int getNpixels(int n) const
Returns the number of fired pixels of the n_th element.
bool isGroup(int n) const
Returns true if the element corresponds to a group of rare topologies.
Relation isOutside(const Bracket< T > &t) const
Definition Bracket.h:230
bool match(const std::vector< std::string > &queries, const char *pattern)
Definition dcs-ccdb.cxx:229
GLdouble n
Definition glcorearb.h:1982
GLfloat GLfloat GLfloat alpha
Definition glcorearb.h:279
GLint GLenum GLint x
Definition glcorearb.h:403
GLint GLsizei count
Definition glcorearb.h:399
const GLdouble * v
Definition glcorearb.h:832
GLint first
Definition glcorearb.h:399
GLboolean GLboolean GLboolean b
Definition glcorearb.h:1233
GLenum GLenum dst
Definition glcorearb.h:1767
GLuint GLsizei const GLchar * label
Definition glcorearb.h:2519
GLuint GLfloat * val
Definition glcorearb.h:1582
GLuint GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat t0
Definition glcorearb.h:5034
GLuint start
Definition glcorearb.h:469
GLboolean GLboolean GLboolean GLboolean a
Definition glcorearb.h:1233
GLdouble GLdouble GLdouble z
Definition glcorearb.h:843
uint8_t itsSharedClusterMap uint8_t
constexpr float XTPCOuterRef
reference radius to propagate outer TPC track
constexpr float XTPCInnerRef
reference radius at which TPC provides the tracks
constexpr double LHCBunchSpacingMUS
constexpr int LHCMaxBunches
constexpr double LHCOrbitMUS
constexpr double LHCBunchSpacingNS
constexpr int NSectors
constexpr float B2C
constexpr int MinusTen
constexpr int MinusOne
constexpr int Validated
flags to tell the status of TPC-ITS tracks comparison
bool isDetITS3(T detID)
Definition SpecsV2.h:210
void convertCompactClusters(gsl::span< const itsmft::CompClusterExt > clusters, gsl::span< const unsigned char >::iterator &pattIt, std::vector< o2::BaseCluster< float > > &output, const its3::TopologyDictionary *dict)
convert compact clusters to 3D spacepoints
Definition IOUtils.cxx:30
void convertCompactClusters(gsl::span< const itsmft::CompClusterExt > clusters, gsl::span< const unsigned char >::iterator &pattIt, std::vector< o2::BaseCluster< float > > &output, const itsmft::TopologyDictionary *dict)
convert compact clusters to 3D spacepoints
Definition IOUtils.cxx:35
if(!okForPhiMin(phi0, phi1))
float angle2Alpha(float phi)
Definition Utils.h:203
int angle2Sector(float phi)
Definition Utils.h:183
std::tuple< float, float > rotateZ(float xL, float yL, float snAlp, float csAlp)
Definition Utils.h:162
float sector2Angle(int sect)
Definition Utils.h:193
std::vector< T, fair::mq::pmr::polymorphic_allocator< T > > vector
constexpr float HugeF
TrackParCovF TrackParCov
Definition Track.h:33
constexpr int kNParams
a couple of static helper functions to create timestamp values for CCDB queries or override obsolete ...
bool isValid(std::string alias)
std::string asString() const
bool isSelected(const RecPoints &rp) const
std::array< ClusRange, o2::its::RecoGeomHelper::getNChips()> chipRefs
o2::math_utils::Bracketf_t tBracket
std::vector< std::deque< ABTrackLink > > threadPool
bool runAfterBurner
run afterburner for TPCtrack-ITScluster matching
float nABSigmaZ
nSigma cut on afterburner track-cluster Z distance
float err2ABExtraZ
extra "systematic" error on Z
float tpcTimeICMatchingNSigma
nsigma for matching TPC corrected time and InteractionCandidate from FT0
float XMatchingRef
reference radius to propagate tracks for matching
float crudeNSigma2Cut[o2::track::kNParams]
float tfEdgeTimeToleranceMUS
corrected TPC time allowed to go out from the TF time edges by this amount
float maxVDriftUncertainty
max assumed VDrift relative uncertainty, used only in VDrift calibration mode
float globalTimeBiasMUS
global time shift to apply to assigned time, brute force way to eliminate bias wrt FIT
int requireToReachLayerAB
AB tracks should reach at least this layer from above.
int maxMatchCandidates
max allowed matching candidates per TPC track
float minTPCTrackR
cut on minimal TPC tracks radius to consider for matching, 666*pt_gev*B_kgaus/5
float safeMarginTPCITSTimeBin
safety margin (in TPC time bins) for ITS-TPC tracks time (in TPC time bins!) comparison
int minTPCClusters
minimum number of clusters to consider
ValidateMatchByFIT validateMatchByFIT
when comparing ITS-TPC matches, prefer those which have time of Interaction Candidate
int lowestLayerAB
lowest layer to reach in AfterBurner
float tpcExtConstrainedNSigma
nsigma to apply to externally (TRD,TOF) time-constrained TPC tracks time error
float maxVDritTimeOffset
max possible TDrift offset to calibrate
o2::base::Propagator::MatCorrType matCorr
int minContributingLayersAB
AB tracks must have at least this amount on contributing layers.
float crudeAbsDiffCut[o2::track::kNParams]
float cutMatchingChi2
cut on matching chi2
float safeMarginTimeCorrErr
safety marging (in \mus) for TPC track time corrected by ITS constraint
float cutABTrack2ClChi2
cut on AfterBurner track-cluster chi2
float maxVDriftTrackQ2Pt
use only tracks below this q/pt (with field only)
float nABSigmaY
nSigma cut on afterburner track-cluster Y distance
float minITSTrackR
cut on minimal ITS tracks radius to consider for matching, 666*pt_gev*B_kgaus/5
float err2ABExtraY
extra "systematic" error on Y
float globalTimeExtraErrorMUS
extra error to add to global time estimate
float safeMarginTPCTimeEdge
safety margin in cm when estimating TPC track tMin and tMax from assigned time0 and its track Z posit...
void createTracksVariadic(T creator, GTrackID::mask_t srcSel=GTrackID::getSourcesMask("all")) const
GTrackID getTPCContributorGID(GTrackID source) const
std::array< int, o2::its::RecoGeomHelper::getNLayers()> firstInLr
entry of 1st (best) hypothesis on each layer
int winLinkID
ID of the validated link.
void addLink(const o2::track::TrackParCov &trc, int clID, int parentID, int nextID, int lr, int nc, int laddID, float chi2)
int ICCanID
interaction candidate ID (they are sorted in increasing time)
ABTrackLink & getLink(int i)
static LinksPoolMT * gLinksPool
pool of links per thread
int8_t lowestLayer
lowest layer reached
int roFrame
ITS readout frame assigned to this track.
o2::math_utils::Bracketf_t tBracket
bracketing time in \mus
float dL
distance integrated during propagation to reference X (as pion)
int matchID
entry (non if MinusOne) of its matchCand struct in the mMatchesITS
float xrho
x*rho seen during propagation to reference X (as pion)
float time0
nominal time in \mus since start of TF (time0 for bare TPC tracks, constrained time for TRD/TOF const...
int sourceID
TPC track origin in.
int matchID
entry (non if MinusOne) of its matchTPC struct in the mMatchesTPC
float getCorrectedTime(float dt) const
float timeErr
time sigma (makes sense for constrained tracks only)
o2::dataformats::GlobalTrackID gid
std::array< RecoLayer, o2::itsmft::ChipMappingITS::NLayers > layers
void init(int minLayer=0, int maxLayer=getNLayers())
static constexpr float ladderWidthInv()
static constexpr int T2L
Definition Cartesian.h:55
static constexpr int T2GRot
Definition Cartesian.h:57
const ClusterNative * clusters[constants::MAXSECTOR][constants::MAXGLOBALPADROW]
unsigned int clusterOffset[constants::MAXSECTOR][constants::MAXGLOBALPADROW]
float refTimeOffset
additive time offset reference (\mus)
float refVDrift
reference vdrift for which factor was extracted
float refTP
reference temperature / pressure for which refVDrift was extracted
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"
o2::InteractionRecord ir(0, 0)
char const *restrict const cmp
Definition x9.h:96