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 "GPUO2Interface.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, -999.);
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(); // 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 mTPCRefitter->setTrackReferenceX(900); // disable propagation after refit by setting reference to value > 500
509 mNTPCOccBinLength = mTPCRefitter->getParam()->rec.tpc.occupancyMapTimeBins;
510 mTBinClOcc.clear();
511 if (mNTPCOccBinLength > 1 && mTPCRefitterOccMap.size()) {
512 mNTPCOccBinLengthInv = 1. / mNTPCOccBinLength;
513 int nTPCBins = mNHBPerTF * o2::constants::lhc::LHCMaxBunches / 8, ninteg = 0;
514 int nTPCOccBins = nTPCBins * mNTPCOccBinLengthInv, sumBins = std::max(1, int(o2::constants::lhc::LHCMaxBunches / 8 * mNTPCOccBinLengthInv));
515 mTBinClOcc.resize(nTPCOccBins);
516 std::vector<float> mltHistTB(nTPCOccBins);
517 float sm = 0., tb = 0.5 * mNTPCOccBinLength;
518 for (int i = 0; i < nTPCOccBins; i++) {
519 mltHistTB[i] = mTPCRefitter->getParam()->GetUnscaledMult(tb);
520 tb += mNTPCOccBinLength;
521 }
522 for (int i = nTPCOccBins; i--;) {
523 sm += mltHistTB[i];
524 if (i + sumBins < nTPCOccBins) {
525 sm -= mltHistTB[i + sumBins];
526 }
527 mTBinClOcc[i] = sm;
528 }
529 } else {
530 mTBinClOcc.resize(1);
531 }
532
533 auto creator = [this](auto& trk, GTrackID gid, float time0, float terr) {
534 if constexpr (isITSTrack<decltype(trk)>()) {
535 // do nothing, ITS tracks will be processed in a direct loop over ROFs
536 }
537 if constexpr (!std::is_base_of_v<o2::track::TrackParCov, std::decay_t<decltype(trk)>>) {
538 return true;
539 } else if (std::abs(trk.getQ2Pt()) > mMinTPCTrackPtInv) {
540 return true;
541 }
542 int resAdd = -100;
543 int tpcIndex = -1;
544 if constexpr (isTPCTrack<decltype(trk)>()) {
545 // unconstrained TPC track, with t0 = TrackTPC.getTime0+0.5*(DeltaFwd-DeltaBwd) and terr = 0.5*(DeltaFwd+DeltaBwd) in TimeBins
546 if (!this->mSkipTPCOnly && trk.getNClusters() > 0) {
547 resAdd = this->addTPCSeed(trk, this->tpcTimeBin2MUS(time0), this->tpcTimeBin2MUS(terr), gid, (tpcIndex = gid.getIndex()));
548 }
549 }
550 if constexpr (isTPCTOFTrack<decltype(trk)>()) {
551 // TPC track constrained by TOF time, time and its error in \mus
552 resAdd = this->addTPCSeed(trk, time0, terr, gid, (tpcIndex = this->mRecoCont->getTPCContributorGID(gid)));
553 }
554 if constexpr (isTRDTrack<decltype(trk)>()) {
555 // TPC track constrained by TRD trigger time, time and its error in \mus
556 resAdd = this->addTPCSeed(trk, time0, terr, gid, (tpcIndex = this->mRecoCont->getTPCContributorGID(gid)));
557 }
558#ifdef _ALLOW_DEBUG_TREES_
559 if (resAdd > -10 && mDBGOut && isDebugFlag(TPCOrigTree)) {
560 dumpTPCOrig(resAdd == 0, tpcIndex);
561 }
562#endif
563 // note: TPCTRDTPF tracks are actually TRD track with extra TOF cluster
564 return true;
565 };
566 mRecoCont->createTracksVariadic(creator);
567
568 float maxTime = 0;
569 int nITSROFs = mITSROFTimes.size();
570 // sort tracks in each sector according to their timeMax
571 for (int sec = o2::constants::math::NSectors; sec--;) {
572 auto& indexCache = mTPCSectIndexCache[sec];
573 if (mParams->verbosity > 0) {
574 LOG(info) << "Sorting sector" << sec << " | " << indexCache.size() << " TPC tracks";
575 }
576 if (!indexCache.size()) {
577 continue;
578 }
579 std::sort(indexCache.begin(), indexCache.end(), [this](int a, int b) {
580 auto& trcA = mTPCWork[a];
581 auto& trcB = mTPCWork[b];
582 return (trcA.tBracket.getMax() - trcB.tBracket.getMax()) < 0.;
583 });
584
585 // build array of 1st entries with tmax corresponding to each ITS ROF (or trigger),
586 // TPC tracks below this entry cannot match to ITS tracks of this and higher ROFs
587
588 float tmax = mTPCWork[indexCache.back()].tBracket.getMax();
589 if (maxTime < tmax) {
590 maxTime = tmax;
591 }
592 int nbins = 1 + (mITSTriggered ? time2ITSROFrameTrig(tmax, 0) : time2ITSROFrameCont(tmax));
593 auto& timeStart = mTPCTimeStart[sec];
594 timeStart.resize(nbins, -1);
595 int itsROF = 0;
596
597 timeStart[0] = 0;
598 for (int itr = 0; itr < (int)indexCache.size(); itr++) {
599 auto& trc = mTPCWork[indexCache[itr]];
600 while (itsROF < nITSROFs && !(trc.tBracket < mITSROFTimes[itsROF])) { // 1st ITS frame after max allowed time for this TPC track
601 itsROF++;
602 }
603 int itsROFMatch = itsROF;
604 if (itsROFMatch && timeStart[--itsROFMatch] == -1) { // register ITSrof preceding the one which exceeds the TPC track tmax
605 timeStart[itsROFMatch] = itr;
606 }
607 }
608 for (int i = 1; i < nbins; i++) {
609 if (timeStart[i] == -1) { // fill gaps with preceding indices
610 timeStart[i] = timeStart[i - 1];
611 }
612 }
613 } // loop over tracks of single sector
614
615 // FIXME We probably don't need this
616 /*
617 // create mapping from TPC time to ITS ROFs
618 if (mITSROFTimes.back() < maxTime) {
619 maxTime = mITSROFTimes.back().getMax();
620 }
621 int nb = int(maxTime) + 1;
622 mITSROFofTPCBin.resize(nb, -1);
623 int itsROF = 0;
624 for (int ib = 0; ib < nb; ib++) {
625 while (itsROF < nITSROFs && ib < mITSROFTimes[itsROF].getMin()) {
626 itsROF++;
627 }
628 mITSROFofTPCBin[ib] = itsROF;
629 }
630*/
631 mInteractionMUSLUT.clear();
632 mInteractionMUSLUT.resize(maxTime + 3 * o2::constants::lhc::LHCOrbitMUS, -1);
633 mTimer[SWPrepTPC].Stop();
634 return mTPCWork.size() > 0;
635}
636
637//_____________________________________________________
638bool MatchTPCITS::prepareITSData()
639{
640 static size_t errCount = 0;
641 constexpr size_t MaxErrors2Report = 10;
642 // Do preparatory work for matching
643 mTimer[SWPrepITS].Start(false);
644 const auto& inp = *mRecoCont;
645
646 // ITS clusters
647 mITSClusterROFRec = inp.getITSClustersROFRecords();
648 const auto clusITS = inp.getITSClusters();
649 if (mITSClusterROFRec.empty() || clusITS.empty()) {
650 LOG(info) << "No ITS clusters";
651 return false;
652 }
653 const auto patterns = inp.getITSClustersPatterns();
654 auto pattIt = patterns.begin();
655 mITSClustersArray.reserve(clusITS.size());
656#ifdef ENABLE_UPGRADES
657 bool withITS3 = o2::GlobalParams::Instance().withITS3;
658 if (withITS3) {
659 o2::its3::ioutils::convertCompactClusters(clusITS, pattIt, mITSClustersArray, mIT3Dict);
660 } else {
661 o2::its::ioutils::convertCompactClusters(clusITS, pattIt, mITSClustersArray, mITSDict);
662 }
663#else
664 o2::its::ioutils::convertCompactClusters(clusITS, pattIt, mITSClustersArray, mITSDict);
665#endif
666
667 // ITS clusters sizes
668 mITSClusterSizes.reserve(clusITS.size());
669 auto pattIt2 = patterns.begin();
670 for (auto& clus : clusITS) {
671 auto pattID = clus.getPatternID();
672 unsigned int npix;
673#ifdef ENABLE_UPGRADES
674 if ((pattID == o2::itsmft::CompCluster::InvalidPatternID) || ((withITS3) ? mIT3Dict->isGroup(pattID) : 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);
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(+ \
1448 : nFailedRefit)
1449#endif
1450 for (int ifit = 0; ifit < nToFit; ifit++) {
1451 int iTPC = tpcToFit[ifit], iITS;
1452 const auto& tTPC = mTPCWork[iTPC];
1453 if (refitTrackTPCITS(ifit, iTPC, iITS, matchedTracks, matchLabels, calib)) {
1454 mWinnerChi2Refit[iITS] = matchedTracks.back().getChi2Refit();
1455 } else {
1456 ++nFailedRefit;
1457 }
1458 }
1459 LOGP(info, "Failed {} TPC-ITS refits out of {}", nFailedRefit, nToFit);
1460
1461 // suppress tracks failed on refit and fill calib/debug data (if needed)
1462 int last = nToFit;
1463 mNMatches = 0;
1464 for (int ifit = 0; ifit < nToFit; ifit++) {
1465 int itpc = tpcToFit[ifit];
1466 if (!matchedTracks[ifit].isValid()) { // move the last good track from the back to the slot to delete
1467 while (--last > ifit && !matchedTracks[last].isValid()) {
1468 } // find the highest valid track
1469 if (last > ifit) {
1470 matchedTracks[ifit] = matchedTracks[last];
1471 matchedTracks[last].invalidate();
1472 itpc = tpcToFit[last];
1473 if (mMCTruthON) {
1474 matchLabels[ifit] = matchLabels[last];
1475 }
1476 } else {
1477 break;
1478 }
1479 }
1480 if (mDBGOut || mVDriftCalibOn) {
1481 fillCalibDebug(ifit, itpc, matchedTracks[ifit], calib);
1482 }
1483 mNMatches++;
1484 }
1485 // adjust sizes
1486 matchedTracks.resize(mNMatches);
1487 if (mMCTruthON) {
1488 matchLabels.resize(mNMatches);
1489 }
1490 mTimer[SWRefit].Stop();
1491}
1492
1493//______________________________________________
1495{
1496 const auto& tTPC = mTPCWork[iTPC];
1497 int iITS = mMatchRecordsTPC[tTPC.matchID].partnerID;
1498 const auto& tITS = mITSWork[iITS];
1499 float minDiffFT0 = -999., timeC = 0.f;
1500 std::vector<float> dtimes;
1501 bool fillVDCalib = mVDriftCalibOn && (!mFieldON || std::abs(match.getQ2Pt()) < mParams->maxVDriftTrackQ2Pt);
1502 if (fillVDCalib || mDBGOut) {
1503 timeC = match.getTimeMUS().getTimeStamp(); // find closest FIT record
1504 float minDiffA = mParams->maxVDritTimeOffset;
1505 if (mInteractions.size()) {
1506 int timeC0 = timeC - minDiffA;
1507 if (timeC0 < 0) {
1508 timeC0 = 0;
1509 }
1510 auto entStart = timeC0 < int(mInteractionMUSLUT.size()) ? mInteractionMUSLUT[timeC0] : (mInteractionMUSLUT.size() ? mInteractionMUSLUT.back() : 0);
1511 for (int ent = entStart; ent < (int)mInteractions.size(); ent++) {
1512 float diff = mInteractions[ent].tBracket.mean() - timeC;
1513 if (diff > minDiffA) {
1514 break; // all following will be the same
1515 }
1516 if (diff < -minDiffA) {
1517 continue;
1518 }
1519 dtimes.push_back(diff);
1520 minDiffFT0 = diff;
1521 minDiffA = std::abs(minDiffFT0);
1522 }
1523 }
1524 }
1525 if (fillVDCalib) {
1526 calib.emplace_back(tITS.getTgl(), tTPC.getTgl(), minDiffFT0);
1527 }
1528#ifdef _ALLOW_DEBUG_TREES_
1529 if (mDBGOut) {
1530 o2::track::TrackPar itsRefPIDCorr(tITS); // version ad hoc corrected for TPC PID being different from the pion, as it is done in the matching
1531 o2::track::TrackParCov itsRefAltPID(tITS); // version with full propagation to account for TPC PID being different from the pion
1532 itsRefPIDCorr.setX(0);
1533 if (tTPC.getPID() > tITS.getPID() && tITS.dL > 0.f && tTPC.getP2() / tTPC.getPID().getMass2() < mParams->minBetaGammaForPIDDiff) {
1534 itsRefAltPID = mITSTracksArray[tITS.sourceID].getParamOut(); // clone original ITS track at highest update point
1535 itsRefPIDCorr.setPID(tTPC.getPID(), true);
1536 itsRefPIDCorr = itsRefAltPID;
1537 // fast correction
1538 if (!itsRefPIDCorr.correctForELoss(tITS.xrho)) {
1539 itsRefPIDCorr.setX(-10);
1540 } else {
1541 float q2ptPID = itsRefPIDCorr.getQ2Pt();
1542 float dCurv = (q2ptPID - tITS.getQ2Pt()) * mBz * o2::constants::math::B2C, dLEff = tITS.dL * mParams->ITSStepEffFraction, dCurvL = dCurv * dLEff;
1543 itsRefPIDCorr = tITS;
1544 itsRefPIDCorr.setPID(tTPC.getPID(), true);
1545 itsRefPIDCorr.setQ2Pt(q2ptPID);
1546 auto snp = tITS.getSnp() + dCurvL;
1547 if (std::abs(snp) >= 1.) {
1548 snp = std::copysign(0.99, snp);
1549 }
1550 itsRefPIDCorr.setSnp(snp);
1551 itsRefPIDCorr.setY(tITS.getY() + dCurvL * dLEff * 0.5);
1552 }
1553 // full propagation
1554 if (!itsRefAltPID.rotate(tTPC.getAlpha()) || !o2::base::Propagator::Instance()->PropagateToXBxByBz(itsRefAltPID, mParams->XMatchingRef, MaxSnp, 2., mUseMatCorrFlag)) {
1555 itsRefAltPID.setX(-10);
1556 }
1557 }
1558 int tb = mTPCTracksArray[tTPC.sourceID].getTime0() * mNTPCOccBinLengthInv;
1559 float mltTPC = tb < 0 ? mTBinClOcc[0] : (tb >= mTBinClOcc.size() ? mTBinClOcc.back() : mTBinClOcc[tb]);
1560 (*mDBGOut) << "refit"
1561 << "tpcOrig=" << mTPCTracksArray[tTPC.sourceID] << "itsOrig=" << mITSTracksArray[tITS.sourceID] << "itsRef=" << tITS << "tpcRef=" << tTPC << "matchRefit=" << match
1562 << "timeCorr=" << timeC << "dTimeFT0=" << minDiffFT0 << "dTimes=" << dtimes
1563 << "itsRefAltPID=" << itsRefAltPID << "itsRefPIDCorr=" << itsRefPIDCorr;
1564 if (mMCTruthON) {
1565 (*mDBGOut) << "refit"
1566 << "itsLbl=" << mITSLblWork[iITS] << "tpcLbl=" << mTPCLblWork[iTPC];
1567 }
1568 (*mDBGOut) << "refit"
1569 << "multTPC=" << mltTPC
1570 << "multITSTr=" << mITSTrackROFRec[tITS.roFrame].getNEntries()
1571 << "multITSCl=" << mITSClusterROFRec[tITS.roFrame].getNEntries()
1572 << "tf=" << mTFCount << "\n";
1573 }
1574#endif
1575}
1576
1577//______________________________________________
1579{
1581
1582 const float maxStep = 2.f; // max propagation step (TODO: tune)
1583 const auto& tTPC = mTPCWork[iTPC];
1584 const auto& tpcMatchRec = mMatchRecordsTPC[tTPC.matchID];
1585 iITS = tpcMatchRec.partnerID;
1586 const auto& tITS = mITSWork[iITS];
1587 const auto& itsTrOrig = mITSTracksArray[tITS.sourceID];
1588 auto& trfit = matchedTracks[slot];
1589 ((o2::track::TrackParCov&)trfit) = (const o2::track::TrackParCov&)tTPC;
1590 trfit.getParamOut() = (const o2::track::TrackParCov&)tITS; // create a copy of TPC track at xRef
1591 trfit.getParamOut().setUserField(0); // reset eventual clones flag
1592 trfit.setPID(tTPC.getPID(), true);
1593 trfit.getParamOut().setPID(tTPC.getPID(), true);
1594 // in continuos mode the Z of TPC track is meaningless, unless it is CE crossing
1595 // track (currently absent, TODO)
1596 if (!mCompareTracksDZ) {
1597 trfit.setZ(tITS.getZ()); // fix the seed Z
1598 }
1599 float deltaT = (trfit.getZ() - tTPC.getZ()) * mTPCVDriftInv; // time correction in \mus
1600 float timeErr = tTPC.constraint == TrackLocTPC::Constrained ? tTPC.timeErr : std::sqrt(tITS.getSigmaZ2() + tTPC.getSigmaZ2()) * mTPCVDriftInv; // estimate the error on time
1601 if (timeErr > mITSTimeResMUS && tTPC.constraint != TrackLocTPC::Constrained) {
1602 timeErr = mITSTimeResMUS; // chose smallest error
1603 deltaT = tTPC.constraint == TrackLocTPC::ASide ? tITS.tBracket.mean() - tTPC.time0 : tTPC.time0 - tITS.tBracket.mean();
1604 }
1605 timeErr += mParams->globalTimeExtraErrorMUS;
1606 float timeC = tTPC.getCorrectedTime(deltaT) + mParams->globalTimeBiasMUS;
1611 } else { // == MatchTPCITSParams::TimeOutliersPolicy::Reject
1612 trfit.invalidate();
1613 return false;
1614 }
1615 }
1616 if (timeC < 0) { // RS TODO similar check is needed for other edge of TF
1617 if (timeC + std::min(timeErr, mParams->tfEdgeTimeToleranceMUS * mTPCTBinMUSInv) < 0) {
1618 trfit.invalidate(); // destroy failed track
1619 return false;
1620 }
1621 timeC = 0.;
1622 }
1623
1624 // refit TPC track inward into the ITS
1625 int nclRefit = 0, ncl = itsTrOrig.getNumberOfClusters();
1626 float chi2 = 0.f;
1627 auto geom = o2::its::GeometryTGeo::Instance();
1628 auto propagator = o2::base::Propagator::Instance();
1629 int clEntry = itsTrOrig.getFirstClusterEntry();
1630
1631 float addErr2 = 0;
1632 // extra error on tgl due to the assumed vdrift uncertainty
1633 if (mVDriftCalibOn) {
1634 addErr2 = tITS.getParam(o2::track::kTgl) * mParams->maxVDriftUncertainty;
1635 addErr2 *= addErr2;
1636 trfit.updateCov(addErr2, o2::track::kSigTgl2);
1637 }
1638
1639 for (int icl = 0; icl < ncl; icl++) {
1640 const auto& clus = mITSClustersArray[mITSTrackClusIdx[clEntry++]];
1641 float alpha = geom->getSensorRefAlpha(clus.getSensorID()), x = clus.getX();
1642 if (!trfit.rotate(alpha) ||
1643 // note: here we also calculate the L,T integral (in the inward direction, but this is irrelevant)
1644 // note: we should eventually use TPC pid in the refit (TODO)
1645 // note: since we are at small R, we can use field BZ component at origin rather than 3D field
1646 !propagator->propagateToX(trfit, x, propagator->getNominalBz(),
1647 MaxSnp, maxStep, mUseMatCorrFlag, &trfit.getLTIntegralOut())) {
1648 break;
1649 }
1650 chi2 += trfit.getPredictedChi2(clus);
1651 if (!trfit.update(clus)) {
1652 break;
1653 }
1654 nclRefit++;
1655 }
1656 if (nclRefit != ncl) {
1657 LOGP(debug, "Refit in ITS failed after ncl={}, match between TPC track #{} and ITS track #{}", nclRefit, tTPC.sourceID, tITS.sourceID);
1658 LOGP(debug, "{:s}", trfit.asString());
1659 trfit.invalidate(); // destroy failed track
1660 return false;
1661 }
1662
1663 // We need to update the LTOF integral by the distance to the "primary vertex"
1664 // 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,
1665 // and since for the LTOF calculation the material effects are irrelevant, we skip material corrections
1666 const o2::dataformats::VertexBase vtxDummy; // at the moment using dummy vertex: TODO use MeanVertex constraint instead
1667 o2::track::TrackPar trpar(trfit);
1668 if (!propagator->propagateToDCA(vtxDummy.getXYZ(), trpar, propagator->getNominalBz(),
1669 maxStep, MatCorrType::USEMatCorrNONE, nullptr, &trfit.getLTIntegralOut())) {
1670 LOG(error) << "LTOF integral might be incorrect";
1671 }
1672 auto& tofL = trfit.getLTIntegralOut(); // this is TL integral calculated from the RefX to the DCA to the beamline, invert material integrals for outward propagation
1673 tofL.setX2X0(-tofL.getX2X0());
1674 tofL.setXRho(-tofL.getXRho());
1675
1676 // outward refit
1677 auto& tracOut = trfit.getParamOut(); // this is a clone of ITS outward track already at the matching reference X
1678 if (tTPC.getPID() > tITS.getPID() && tTPC.getP2() / tTPC.getPID().getMass2() < mParams->minBetaGammaForPIDDiff) {
1679 // in case the TPC track hypothesis is not pion, we redo the outward propagation to ref.x with TPC PID
1680 tracOut = mITSTracksArray[tITS.sourceID].getParamOut();
1681 tracOut.setPID(tTPC.getPID(), true);
1682 if (!tracOut.rotate(tTPC.getAlpha()) || !o2::base::Propagator::Instance()->PropagateToXBxByBz(tracOut, mParams->XMatchingRef, MaxSnp, 2., mUseMatCorrFlag)) {
1683 LOGP(debug, "Failed to rotate ITSouter with imposed PID to TPC alpha {} or propagate to X={}: {:s}", tTPC.getAlpha(), mParams->XMatchingRef, tracOut.asString());
1684 trfit.invalidate(); // destroy failed track
1685 return false;
1686 }
1687 }
1688 {
1689 float xtogo = 0;
1690 if (!tracOut.getXatLabR(o2::constants::geom::XTPCInnerRef, xtogo, mBz, o2::track::DirOutward) ||
1691 !propagator->PropagateToXBxByBz(tracOut, xtogo, MaxSnp, 10., mUseMatCorrFlag, &tofL)) {
1692 LOG(debug) << "Propagation to inner TPC boundary X=" << xtogo << " failed, Xtr=" << tracOut.getX() << " snp=" << tracOut.getSnp();
1693 trfit.invalidate(); // destroy failed track
1694 return false;
1695 }
1696 if (mVDriftCalibOn) {
1697 tracOut.updateCov(addErr2, o2::track::kSigTgl2);
1698 }
1699 float chi2Out = 0;
1700 auto posStart = tracOut.getXYZGlo();
1701 auto tImposed = timeC * mTPCTBinMUSInv;
1702 if (std::abs(tImposed - mTPCTracksArray[tTPC.sourceID].getTime0()) > 550) {
1703 LOGP(alarm, "Impossible imposed timebin {} for TPC track time0:{}, dBwd:{} dFwd:{} TB | ZShift:{}, TShift:{}", tImposed, mTPCTracksArray[tTPC.sourceID].getTime0(),
1704 mTPCTracksArray[tTPC.sourceID].getDeltaTBwd(), mTPCTracksArray[tTPC.sourceID].getDeltaTFwd(), trfit.getZ() - tTPC.getZ(), deltaT);
1705 LOGP(info, "Trc: {}", mTPCTracksArray[tTPC.sourceID].asString());
1706 trfit.invalidate(); // destroy failed track
1707 return false;
1708 }
1709 int retVal = mTPCRefitter->RefitTrackAsTrackParCov(tracOut, mTPCTracksArray[tTPC.sourceID].getClusterRef(), tImposed, &chi2Out, true, false); // outward refit
1710 if (retVal < 0) {
1711 LOG(debug) << "Refit failed";
1712 trfit.invalidate(); // destroy failed track
1713 return false;
1714 }
1715 auto posEnd = tracOut.getXYZGlo();
1716 auto lInt = propagator->estimateLTIncrement(tracOut, posStart, posEnd);
1717 tofL.addStep(lInt, tracOut.getP2Inv());
1718 tofL.addX2X0(lInt * mTPCmeanX0Inv);
1719 propagator->PropagateToXBxByBz(tracOut, o2::constants::geom::XTPCOuterRef, MaxSnp, 10., mUseMatCorrFlag, &tofL);
1720
1721 const auto& trackTune = TrackTuneParams::Instance();
1722 if (trackTune.useTPCOuterCorr) {
1723 tracOut.updateParams(trackTune.tpcParOuter);
1724 }
1725 if (trackTune.tpcCovOuterType != TrackTuneParams::AddCovType::Disable) {
1726 tracOut.updateCov(mCovDiagOuter, trackTune.tpcCovOuterType == TrackTuneParams::AddCovType::WithCorrelations);
1727 }
1728 }
1729 trfit.setChi2Match(tpcMatchRec.chi2);
1730 trfit.setChi2Refit(chi2);
1731 trfit.setTimeMUS(timeC, timeErr);
1732 trfit.setRefTPC({unsigned(tTPC.sourceID), o2::dataformats::GlobalTrackID::TPC});
1733 trfit.setRefITS({unsigned(tITS.sourceID), o2::dataformats::GlobalTrackID::ITS});
1734
1735 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)
1736 matchLabels[slot] = mTPCLblWork[iTPC];
1737 matchLabels[slot].setFakeFlag(mITSLblWork[iITS] != mTPCLblWork[iTPC]);
1738 }
1739
1740 return true;
1741}
1742
1743//______________________________________________
1744bool MatchTPCITS::refitABTrack(int iITSAB, const TPCABSeed& seed, pmr::vector<o2::dataformats::TrackTPCITS>& matchedTracks, pmr::vector<int>& ABTrackletClusterIDs, pmr::vector<o2::itsmft::TrkClusRef>& ABTrackletRefs)
1745{
1747
1748 const float maxStep = 2.f; // max propagation step (TODO: tune)
1749 const auto& tTPC = mTPCWork[seed.tpcWID];
1750 const auto& winLink = seed.getLink(seed.winLinkID);
1751 auto& newtr = matchedTracks.emplace_back(winLink, winLink); // create a copy of winner param at innermost ITS cluster
1752 auto& tracOut = newtr.getParamOut();
1753 auto& tofL = newtr.getLTIntegralOut();
1754 auto geom = o2::its::GeometryTGeo::Instance();
1755 auto propagator = o2::base::Propagator::Instance();
1756 tracOut.resetCovariance();
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
1762 float chi2 = 0.f;
1763 // NOTE: the ITS cluster absolute indices are stored from inner to outer layers
1764 for (int icl = itsClRefs.getFirstEntry(); icl < itsClRefs.getEntriesBound(); icl++) {
1765 const auto& clus = mITSClustersArray[ABTrackletClusterIDs[icl]];
1766 float alpha = geom->getSensorRefAlpha(clus.getSensorID()), x = clus.getX();
1767 if (!tracOut.rotate(alpha) ||
1768 // note: here we also calculate the L,T integral
1769 // note: we should eventually use TPC pid in the refit (TODO)
1770 // note: since we are at small R, we can use field BZ component at origin rather than 3D field
1771 !propagator->propagateToX(tracOut, x, propagator->getNominalBz(), MaxSnp, maxStep, mUseMatCorrFlag, &tofL)) {
1772 break;
1773 }
1774 chi2 += tracOut.getPredictedChi2(clus);
1775 if (!tracOut.update(clus)) {
1776 break;
1777 }
1778 nclRefit++;
1779 }
1780 if (nclRefit != ncl) {
1781 LOGP(debug, "AfterBurner refit in ITS failed after ncl={}, match between TPC track #{} and ITS tracklet #{}", nclRefit, tTPC.sourceID, iITSAB);
1782 LOGP(debug, "{:s}", tracOut.asString());
1783 matchedTracks.pop_back(); // destroy failed track
1784 return false;
1785 }
1786 // perform TPC refit with interaction time constraint
1787 float timeC = mInteractions[seed.ICCanID].tBracket.mean();
1788 float timeErr = mInteractions[seed.ICCanID].tBracket.delta(); // RS FIXME shall we use gaussian error?
1789 {
1790 float xtogo = 0;
1791 if (!tracOut.getXatLabR(o2::constants::geom::XTPCInnerRef, xtogo, mBz, o2::track::DirOutward) ||
1792 !propagator->PropagateToXBxByBz(tracOut, xtogo, MaxSnp, 10., mUseMatCorrFlag, &tofL)) {
1793 LOG(debug) << "Propagation to inner TPC boundary X=" << xtogo << " failed, Xtr=" << tracOut.getX() << " snp=" << tracOut.getSnp();
1794 matchedTracks.pop_back(); // destroy failed track
1795 return false;
1796 }
1797 float chi2Out = 0;
1798 auto posStart = tracOut.getXYZGlo();
1799 int retVal = mTPCRefitter->RefitTrackAsTrackParCov(tracOut, mTPCTracksArray[tTPC.sourceID].getClusterRef(), timeC * mTPCTBinMUSInv, &chi2Out, true, false); // outward refit
1800 if (retVal < 0) {
1801 LOG(debug) << "Refit failed";
1802 matchedTracks.pop_back(); // destroy failed track
1803 return false;
1804 }
1805 auto posEnd = tracOut.getXYZGlo();
1806 auto lInt = propagator->estimateLTIncrement(tracOut, posStart, posEnd);
1807 tofL.addStep(lInt, tracOut.getP2Inv());
1808 tofL.addX2X0(lInt * mTPCmeanX0Inv);
1809 propagator->PropagateToXBxByBz(tracOut, o2::constants::geom::XTPCOuterRef, MaxSnp, 10., mUseMatCorrFlag, &tofL);
1810 const auto& trackTune = TrackTuneParams::Instance();
1811 if (trackTune.useTPCOuterCorr) {
1812 tracOut.updateParams(trackTune.tpcParOuter);
1813 }
1814 if (trackTune.tpcCovOuterType != TrackTuneParams::AddCovType::Disable) {
1815 tracOut.updateCov(mCovDiagOuter, trackTune.tpcCovOuterType == TrackTuneParams::AddCovType::WithCorrelations);
1816 }
1817 }
1818
1819 newtr.setChi2Match(winLink.chi2Norm());
1820 newtr.setChi2Refit(chi2);
1821 newtr.setTimeMUS(timeC, timeErr);
1822 newtr.setRefTPC({unsigned(tTPC.sourceID), o2::dataformats::GlobalTrackID::TPC});
1823 newtr.setRefITS({unsigned(iITSAB), o2::dataformats::GlobalTrackID::ITSAB});
1824
1825 return true;
1826}
1827
1828//______________________________________________
1829bool MatchTPCITS::refitTPCInward(o2::track::TrackParCov& trcIn, float& chi2, float xTgt, int trcID, float timeTB) const
1830{
1831 // inward refit
1832 const auto& tpcTrOrig = mTPCTracksArray[trcID];
1833
1834 trcIn = tpcTrOrig.getOuterParam();
1835 chi2 = 0;
1836
1837 auto propagator = o2::base::Propagator::Instance();
1838 int retVal = mTPCRefitter->RefitTrackAsTrackParCov(trcIn, tpcTrOrig.getClusterRef(), timeTB, &chi2, false, true); // inward refit with matrix reset
1839 if (retVal < 0) {
1840 LOG(warning) << "Refit failed";
1841 LOG(warning) << trcIn.asString();
1842 return false;
1843 }
1844 //
1845 // propagate to the inner edge of the TPC
1846 // Note: it is allowed to not reach the requested radius
1847 if (!propagator->PropagateToXBxByBz(trcIn, xTgt, MaxSnp, 2., mUseMatCorrFlag)) {
1848 LOG(debug) << "Propagation to target X=" << xTgt << " failed, Xtr=" << trcIn.getX() << " snp=" << trcIn.getSnp() << " pT=" << trcIn.getPt();
1849 LOG(debug) << trcIn.asString();
1850 return false;
1851 }
1852 return true;
1853}
1854
1855//>>============================= AfterBurner for TPC-track / ITS cluster matching ===================>>
1856//______________________________________________
1857int MatchTPCITS::prepareABSeeds()
1858{
1860 const auto& outerLr = mRGHelper.layers.back();
1861 // to avoid difference between 3D field propagation and Bz-bazed getXatLabR we propagate RMax+margin
1862 const float ROuter = outerLr.rRange.getMax() + 0.5f;
1863 auto propagator = o2::base::Propagator::Instance();
1864
1865 for (int iTPC = 0; iTPC < (int)mTPCWork.size(); iTPC++) {
1866 auto& tTPC = mTPCWork[iTPC];
1867 if (isDisabledTPC(tTPC)) {
1868 // Popagate to the vicinity of the out layer. Note: the Z of the track might be uncertain,
1869 // in this case the material corrections will be correct only in the limit of their uniformity in Z,
1870 // which should be good assumption....
1871 float xTgt;
1872 if (!tTPC.getXatLabR(ROuter, xTgt, propagator->getNominalBz(), o2::track::DirInward) ||
1873 !propagator->PropagateToXBxByBz(tTPC, xTgt, MaxSnp, 2., mUseMatCorrFlag)) {
1874 continue;
1875 }
1876 mTPCABIndexCache.push_back(iTPC);
1877 }
1878 }
1879 // sort tracks according to their timeMin
1880 std::sort(mTPCABIndexCache.begin(), mTPCABIndexCache.end(), [this](int a, int b) {
1881 auto& trcA = mTPCWork[a];
1882 auto& trcB = mTPCWork[b];
1883 return (trcA.tBracket.getMin() - trcB.tBracket.getMin()) < 0.;
1884 });
1885
1886 float maxTDriftSafe = tpcTimeBin2MUS(mNTPCBinsFullDrift + mParams->safeMarginTPCITSTimeBin + mTPCTimeEdgeTSafeMargin);
1887 int nIntCand = mInteractions.size(), nTPCCand = mTPCABIndexCache.size();
1888 int tpcStart = 0;
1889 for (int ic = 0; ic < nIntCand; ic++) {
1890 int icFirstSeed = mTPCABSeeds.size();
1891 auto& intCand = mInteractions[ic];
1892 auto tic = intCand.tBracket.mean();
1893 for (int it = tpcStart; it < nTPCCand; it++) {
1894 auto& trc = mTPCWork[mTPCABIndexCache[it]];
1895 auto cmp = trc.tBracket.isOutside(intCand.tBracket);
1896 if (cmp < 0) {
1897 break; // all other TPC tracks will be also in future wrt the interaction
1898 }
1899 if (cmp > 0) {
1900 if (trc.tBracket.getMin() + maxTDriftSafe < intCand.tBracket.getMin()) {
1901 tpcStart++; // all following int.candidates would be in future wrt this track
1902 }
1903 continue;
1904 }
1905 // we beed to create seed from this TPC track and interaction candidate
1906 float dt = trc.getSignedDT(tic - trc.time0);
1907 float dz = dt * mTPCVDrift, z = trc.getZ() + dz;
1908 if (outerLr.zRange.isOutside(z, std::sqrt(trc.getSigmaZ2()) + 2.)) { // RS FIXME introduce margin as parameter?
1909 continue;
1910 }
1911 // make sure seed crosses the outer ITS layer (with some margin)
1912 auto& seed = mTPCABSeeds.emplace_back(mTPCABIndexCache[it], ic, trc);
1913 seed.track.setZ(z); // RS FIXME : in case of distortions and large dz the track must be refitted
1914 }
1915 intCand.seedsRef.set(icFirstSeed, mTPCABSeeds.size() - icFirstSeed);
1916 }
1917 return mTPCABIndexCache.size();
1918}
1919
1920//______________________________________________
1921int MatchTPCITS::prepareInteractionTimes()
1922{
1923 // guess interaction times from various sources and relate with ITS rofs
1924 const float ft0Uncertainty = 0.5e-3;
1925 int nITSROFs = mITSROFTimes.size();
1926 if (mFITInfo.size()) {
1927 int rof = 0;
1928 for (const auto& ft : mFITInfo) {
1929 if (!mFT0Params->isSelected(ft)) {
1930 continue;
1931 }
1932 auto fitTime = ft.getInteractionRecord().differenceInBCMUS(mStartIR);
1933 if (fitTime < 0) { // should not happen
1934 continue;
1935 }
1936 if (size_t(fitTime) >= mInteractionMUSLUT.size()) {
1937 mInteractionMUSLUT.resize(size_t(fitTime) + 1, -1);
1938 }
1939 if (mInteractionMUSLUT[fitTime] < 0) {
1940 mInteractionMUSLUT[fitTime] = mInteractions.size();
1941 }
1942 for (; rof < nITSROFs; rof++) {
1943 if (mITSROFTimes[rof] < fitTime) {
1944 continue;
1945 }
1946 break;
1947 }
1948 if (rof >= nITSROFs) {
1949 break;
1950 }
1951 mInteractions.emplace_back(ft.getInteractionRecord(), fitTime, ft0Uncertainty, rof, o2::detectors::DetID::FT0);
1952 }
1953 }
1954 int ent = 0;
1955 for (auto& val : mInteractionMUSLUT) {
1956 if (val < 0) { // was not assigned == no interactions in this mus, assign previous one
1957 val = ent;
1958 } else {
1959 ent = val > 0 ? val - 1 : val;
1960 }
1961 }
1962 return mInteractions.size();
1963}
1964
1965//______________________________________________
1968{
1969 mTimer[SWABSeeds].Start(false);
1970 mNABRefsClus = 0;
1971 prepareABSeeds();
1972 int nIntCand = mInteractions.size(), nABSeeds = mTPCABSeeds.size();
1973 LOGP(info, "AfterBurner will check {} seeds from {} TPC tracks and {} interaction candidates with {} threads", nABSeeds, mTPCABIndexCache.size(), nIntCand, mNThreads); // TMP
1974 mTimer[SWABSeeds].Stop();
1975 if (!nIntCand || !mTPCABSeeds.size()) {
1976 return false;
1977 }
1978 mTimer[SWABMatch].Start(false);
1979
1980 std::vector<ITSChipClustersRefs> itsChipClRefsBuff(mNThreads);
1981#ifdef ENABLE_UPGRADES
1982 // with upgrades the datatype changed, hence we need to initialize
1983 // each element individually
1984 std::generate(itsChipClRefsBuff.begin(), itsChipClRefsBuff.end(), []() {
1985 return ITSChipClustersRefs(o2::its::GeometryTGeo::Instance()->getNumberOfChips());
1986 });
1987#endif
1988
1989#ifdef WITH_OPENMP
1990#pragma omp parallel for schedule(dynamic) num_threads(mNThreads)
1991#endif
1992 for (int ic = 0; ic < nIntCand; ic++) {
1993 const auto& intCand = mInteractions[ic];
1994 LOGP(debug, "cand T {} Entries: {} : {} : {} | ITS ROF: {}", intCand.tBracket.mean(), intCand.seedsRef.getEntries(), intCand.seedsRef.getFirstEntry(), intCand.seedsRef.getEntriesBound(), intCand.rofITS);
1995 if (!intCand.seedsRef.getEntries()) {
1996 continue;
1997 }
1998#ifdef WITH_OPENMP
1999 uint8_t tid = (uint8_t)omp_get_thread_num();
2000#else
2001 uint8_t tid = 0;
2002#endif
2003 fillClustersForAfterBurner(intCand.rofITS, 1, itsChipClRefsBuff[tid]); // RS FIXME account for possibility of filling 2 ROFs
2004 for (int is = intCand.seedsRef.getFirstEntry(); is < intCand.seedsRef.getEntriesBound(); is++) { // loop over all seeds of this interaction candidate
2005 processABSeed(is, itsChipClRefsBuff[tid], tid);
2006 }
2007 }
2008 mTimer[SWABMatch].Stop();
2009 mTimer[SWABWinners].Start(false);
2010 int nwin = 0;
2011 // select winners
2012 struct SID {
2013 int seedID = -1;
2014 float chi2 = 1e9;
2015 };
2016 std::vector<SID> candAB;
2017 candAB.reserve(nABSeeds);
2018 mABWinnersIDs.reserve(mTPCABIndexCache.size());
2019
2020 for (int i = 0; i < nABSeeds; i++) {
2021 auto& ABSeed = mTPCABSeeds[i];
2022 if (ABSeed.isDisabled()) {
2023 continue;
2024 }
2025 candAB.emplace_back(SID{i, ABSeed.getLink(ABSeed.getBestLinkID()).chi2Norm()});
2026 }
2027 std::sort(candAB.begin(), candAB.end(), [](SID a, SID b) { return a.chi2 < b.chi2; });
2028 for (int i = 0; i < (int)candAB.size(); i++) {
2029 auto& ABSeed = mTPCABSeeds[candAB[i].seedID];
2030 if (ABSeed.isDisabled()) {
2031 // RSTMP LOG(info) << "Iter: " << iter << " seed is disabled: " << i << "[" << candAB[i].seedID << "/" << candAB[i].chi2 << "]" << " last lr: " << int(ABSeed.lowestLayer);
2032 continue;
2033 }
2034 auto& tTPC = mTPCWork[ABSeed.tpcWID];
2035 if (tTPC.matchID > MinusOne) { // this tracks was already validated with other IC
2036 ABSeed.disable();
2037 // 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);
2038 continue;
2039 }
2040 auto bestID = ABSeed.getBestLinkID();
2041 if (ABSeed.checkLinkHasUsedClusters(bestID, mABClusterLinkIndex)) {
2042 ABSeed.setNeedAlternative(); // flag for later processing
2043 // 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);;
2044 continue;
2045 }
2046 ABSeed.validate(bestID);
2047 ABSeed.flagLinkUsedClusters(bestID, mABClusterLinkIndex);
2048 mABWinnersIDs.push_back(tTPC.matchID = candAB[i].seedID);
2049 mNABRefsClus += ABSeed.getNLayers();
2050 nwin++;
2051 // 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);
2052 }
2053 mTimer[SWABWinners].Stop();
2054 mTimer[SWABRefit].Start(false);
2055 refitABWinners(matchedTracks, matchLabels, ABTrackletLabels, ABTrackletClusterIDs, ABTrackletRefs, calib);
2056 mTimer[SWABRefit].Stop();
2057 return true;
2058}
2059
2060//______________________________________________
2063{
2064 // refit normal matches
2065 refitWinners(matchedTracks, matchLabels, calib);
2066
2067 ABTrackletClusterIDs.reserve(mNABRefsClus);
2068 ABTrackletRefs.reserve(mABWinnersIDs.size());
2069 if (mMCTruthON) {
2070 ABTrackletLabels.reserve(mABWinnersIDs.size());
2071 }
2072 if (matchedTracks.capacity() < mABWinnersIDs.size() + matchedTracks.size()) {
2073 LOGP(warn, "need to expand matched tracks container from {} to {}", matchedTracks.capacity(), mABWinnersIDs.size() + matchedTracks.size());
2074 matchedTracks.reserve(mABWinnersIDs.size() + matchedTracks.size());
2075 if (mMCTruthON) {
2076 matchLabels.reserve(mABWinnersIDs.size() + matchedTracks.size());
2077 }
2078 }
2079
2080 std::map<o2::MCCompLabel, int> labelOccurence;
2081 auto accountClusterLabel = [&labelOccurence, itsClLabs = mITSClsLabels](int clID) {
2082 auto labels = itsClLabs->getLabels(clID);
2083 for (auto lab : labels) { // check all labels of the cluster
2084 if (lab.isSet()) {
2085 labelOccurence[lab]++;
2086 }
2087 }
2088 };
2089
2090 for (auto wid : mABWinnersIDs) {
2091 const auto& ABSeed = mTPCABSeeds[wid];
2092 int start = ABTrackletClusterIDs.size();
2093 int lID = ABSeed.winLinkID, ncl = 0;
2094 auto& clref = ABTrackletRefs.emplace_back(start, ncl);
2095 while (lID > MinusOne) {
2096 const auto& winL = ABSeed.getLink(lID);
2097 if (winL.clID > MinusOne) {
2098 ABTrackletClusterIDs.push_back(winL.clID);
2099 ncl++;
2100 clref.pattern |= 0x1 << winL.layerID;
2101 clref.setClusterSize(winL.layerID, mITSClusterSizes[winL.clID]);
2102 if (mMCTruthON) {
2103 accountClusterLabel(winL.clID);
2104 }
2105 }
2106 lID = winL.parentID;
2107 }
2108 clref.setEntries(ncl);
2109 if (!refitABTrack(ABTrackletRefs.size() - 1, ABSeed, matchedTracks, ABTrackletClusterIDs, ABTrackletRefs)) { // on failure, destroy added tracklet reference
2110 ABTrackletRefs.pop_back();
2111 ABTrackletClusterIDs.resize(start); // RSS
2112 if (mMCTruthON) {
2113 labelOccurence.clear();
2114 }
2115 continue;
2116 }
2117 if (mMCTruthON) {
2118 o2::MCCompLabel lab;
2119 int maxL = 0; // find most encountered label
2120 for (auto [label, count] : labelOccurence) {
2121 if (count > maxL) {
2122 maxL = count;
2123 lab = label;
2124 }
2125 }
2126 if (maxL < ncl) {
2127 lab.setFakeFlag();
2128 }
2129 labelOccurence.clear();
2130 ABTrackletLabels.push_back(lab); // ITSAB tracklet label
2131 auto& lblGlo = matchLabels.emplace_back(mTPCLblWork[ABSeed.tpcWID]);
2132 lblGlo.setFakeFlag(lab != lblGlo);
2133 LOG(debug) << "ABWinner ncl=" << ncl << " mcLBAB " << lab << " mcLBGlo " << lblGlo << " chi2=" << ABSeed.getLink(ABSeed.winLinkID).chi2Norm() << " pT = " << ABSeed.track.getPt();
2134 }
2135 // build MC label
2136 }
2137 LOG(info) << "AfterBurner validated " << ABTrackletRefs.size() << " tracks";
2138}
2139
2140//______________________________________________
2141void MatchTPCITS::processABSeed(int sid, const ITSChipClustersRefs& itsChipClRefs, uint8_t tID)
2142{
2143 // prepare matching hypothesis tree for given seed
2144 auto& ABSeed = mTPCABSeeds[sid];
2145 ABSeed.threadID = tID;
2146 ABSeed.linksEntry = mABLinksPool.threadPool[tID].size();
2147 followABSeed(ABSeed.track, itsChipClRefs, MinusTen, NITSLayers - 1, ABSeed); // check matches on outermost layer
2148 for (int ilr = NITSLayers - 1; ilr > mParams->lowestLayerAB; ilr--) {
2149 int nextLinkID = ABSeed.firstInLr[ilr];
2150 if (nextLinkID < 0) {
2151 break;
2152 }
2153 while (nextLinkID > MinusOne) {
2154 const auto& seedLink = ABSeed.getLink(nextLinkID);
2155 if (seedLink.isDisabled()) {
2156 nextLinkID = seedLink.nextOnLr;
2157 continue;
2158 }
2159 int next2nextLinkID = seedLink.nextOnLr; // fetch now since the seedLink may change due to the relocation
2160 followABSeed(seedLink, itsChipClRefs, nextLinkID, ilr - 1, ABSeed); // check matches on the next layer
2161 nextLinkID = next2nextLinkID;
2162 }
2163 }
2164 // is this seed has chance to be validated?
2165 auto candID = ABSeed.getBestLinkID();
2166 if (ABSeed.isDisabled() ||
2167 ABSeed.lowestLayer > mParams->requireToReachLayerAB ||
2168 candID < 0 ||
2169 ABSeed.getLink(candID).nContLayers < mParams->minContributingLayersAB) { // free unused links
2170 ABSeed.disable();
2171 mABLinksPool.threadPool[tID].resize(size_t(ABSeed.linksEntry));
2172 }
2173
2174 /* // RS FIXME remove on final clean-up
2175 auto bestLinkID = ABSeed.getBestLinkID();
2176 if (bestLinkID>MinusOne) {
2177 const auto& bestL = ABSeed.getLink(bestLinkID);
2178 LOG(info) << "seed " << sid << " last lr: " << int(ABSeed.lowestLayer) << " Ncont: " << int(bestL.nContLayers) << " chi2 " << bestL.chi2;
2179 }
2180 else {
2181 LOG(info) << "seed " << sid << " : NONE";
2182 }
2183 */
2184}
2185
2186//______________________________________________
2187int MatchTPCITS::followABSeed(const o2::track::TrackParCov& seed, const ITSChipClustersRefs& itsChipClRefs, int seedID, int lrID, TPCABSeed& ABSeed)
2188{
2189
2190 auto propagator = o2::base::Propagator::Instance();
2191 float xTgt;
2192 const auto& lr = mRGHelper.layers[lrID];
2193 auto seedC = seed;
2194 if (!seedC.getXatLabR(lr.rRange.getMax(), xTgt, propagator->getNominalBz(), o2::track::DirInward) ||
2195 !propagator->propagateToX(seedC, xTgt, propagator->getNominalBz(), MaxSnp, 2., mUseMatCorrFlag)) { // Bz-propagation only in ITS
2196 return -1;
2197 }
2198
2199 float zDRStep = -seedC.getTgl() * lr.rRange.delta(); // approximate Z span when going from layer rMin to rMax
2200 float errZ = std::sqrt(seedC.getSigmaZ2() + mParams->err2ABExtraZ);
2201 if (lr.zRange.isOutside(seedC.getZ(), mParams->nABSigmaZ * errZ + std::abs(zDRStep))) {
2202 return 0;
2203 }
2204 std::vector<int> chipSelClusters; // preliminary cluster candidates //RS TODO do we keep this local / consider array instead of vector
2205 o2::math_utils::CircleXYf_t trcCircle; // circle parameters for B ON data
2206 o2::math_utils::IntervalXYf_t trcLinPar; // line parameters for B OFF data
2207 float sna, csa;
2208 // approximate errors
2209 float errY = std::sqrt(seedC.getSigmaY2() + mParams->err2ABExtraY), errYFrac = errY * mRGHelper.ladderWidthInv();
2210 if (mFieldON) {
2211 seedC.getCircleParams(propagator->getNominalBz(), trcCircle, sna, csa);
2212 } else {
2213 seedC.getLineParams(trcLinPar, sna, csa);
2214 }
2215 float xCurr, yCurr;
2216 o2::math_utils::rotateZ(seedC.getX(), seedC.getY(), xCurr, yCurr, sna, csa); // lab X,Y
2217 float phi = std::atan2(yCurr, xCurr); // RS: TODO : can we use fast atan2 here?
2218 // find approximate ladder and chip_in_ladder corresponding to this track extrapolation
2219 int nLad2Check = 0, ladIDguess = lr.getLadderID(phi), chipIDguess = lr.getChipID(seedC.getZ() + 0.5 * zDRStep);
2220 std::array<int, MaxLadderCand> lad2Check;
2221 nLad2Check = mFieldON ? findLaddersToCheckBOn(lrID, ladIDguess, trcCircle, errYFrac, lad2Check) : findLaddersToCheckBOff(lrID, ladIDguess, trcLinPar, errYFrac, lad2Check);
2222
2223 for (int ilad = nLad2Check; ilad--;) {
2224 int ladID = lad2Check[ilad];
2225 const auto& lad = lr.ladders[ladID];
2226
2227 // we assume that close chips on the same ladder will have close xyEdges, so it is enough to calculate track-chip crossing
2228 // coordinates xCross,yCross,zCross for this central chipIDguess, although we are going to check also neighbours
2229 float t = 1e9, xCross, yCross;
2230 const auto& chipC = lad.chips[chipIDguess];
2231 if (mFieldON) {
2232 chipC.xyEdges.circleCrossParam(trcCircle, t);
2233 } else {
2234 chipC.xyEdges.lineCrossParam(trcLinPar, t);
2235 }
2236 chipC.xyEdges.eval(t, xCross, yCross);
2237 float dx = xCross - xCurr, dy = yCross - yCurr, dst2 = dx * dx + dy * dy, dst = sqrtf(dst2);
2238 // Z-step sign depends on radius decreasing or increasing during the propagation
2239 float zCross = seedC.getZ() + seedC.getTgl() * (dst2 < 2 * (dx * xCurr + dy * yCurr) ? dst : -dst);
2240
2241 for (int ich = -1; ich < 2; ich++) {
2242 int chipID = chipIDguess + ich;
2243
2244 if (chipID < 0 || chipID >= static_cast<int>(lad.chips.size())) {
2245 continue;
2246 }
2247 if (lad.chips[chipID].zRange.isOutside(zCross, mParams->nABSigmaZ * errZ)) {
2248 continue;
2249 }
2250 const auto& clRange = itsChipClRefs.chipRefs[lad.chips[chipID].id];
2251 if (!clRange.getEntries()) {
2252 LOG(debug) << "No clusters in chip range";
2253 continue;
2254 }
2255 // track Y error in chip frame
2256 float errYcalp = errY * (csa * chipC.csAlp + sna * chipC.snAlp); // sigY_rotate(from alpha0 to alpha1) = sigY * cos(alpha1 - alpha0);
2257 float tolerZ = errZ * mParams->nABSigmaZ, tolerY = errYcalp * mParams->nABSigmaY;
2258 float yTrack = -xCross * chipC.snAlp + yCross * chipC.csAlp; // track-chip crossing Y in chip frame
2259 if (!preselectChipClusters(chipSelClusters, clRange, itsChipClRefs, yTrack, zCross, tolerY, tolerZ)) { // select candidate clusters for this chip
2260 LOG(debug) << "No compatible clusters found";
2261 continue;
2262 }
2263 o2::track::TrackParCov trcLC = seedC;
2264
2265 if (!trcLC.rotate(chipC.alp) || !trcLC.propagateTo(chipC.xRef, propagator->getNominalBz())) {
2266 LOG(debug) << " failed to rotate to alpha=" << chipC.alp << " or prop to X=" << chipC.xRef;
2267 // trcLC.print();
2268 break; // the chips of the ladder are practically on the same X and alpha
2269 }
2270
2271 for (auto clID : chipSelClusters) {
2272 const auto& cls = mITSClustersArray[clID];
2273 auto chi2 = trcLC.getPredictedChi2(cls);
2274 if (chi2 > mParams->cutABTrack2ClChi2) {
2275 continue;
2276 }
2277 int lnkID = registerABTrackLink(ABSeed, trcLC, clID, seedID, lrID, ladID, chi2); // add new link with track copy
2278 if (lnkID > MinusOne) {
2279 auto& link = ABSeed.getLink(lnkID);
2280 link.update(cls);
2281 if (seedID >= MinusOne) {
2282 ABSeed.getLink(seedID).nDaughters++; // RS FIXME : do we need this?
2283 }
2284 if (lrID < ABSeed.lowestLayer) {
2285 ABSeed.lowestLayer = lrID; // update lowest layer reached
2286 }
2287 }
2288 }
2289 }
2290 }
2291 return 0;
2292}
2293
2294//______________________________________________
2295void MatchTPCITS::accountForOverlapsAB(int lrSeed)
2296{
2297 // TODO
2298 LOG(warning) << "TODO";
2299}
2300
2301//______________________________________________
2302int MatchTPCITS::findLaddersToCheckBOn(int ilr, int lad0, const o2::math_utils::CircleXYf_t& circle, float errYFrac,
2303 std::array<int, MatchTPCITS::MaxLadderCand>& lad2Check) const
2304{
2305 // check if ladder lad0 and at most +-MaxUpDnLadders around it are compatible with circular track of
2306 // r^2 = r2 and centered at xC,yC
2307 const auto& lr = mRGHelper.layers[ilr];
2308 int nacc = 0, jmp = 0;
2309 if (lr.ladders[lad0].xyEdges.seenByCircle(circle, errYFrac)) {
2310 lad2Check[nacc++] = lad0;
2311 }
2312 bool doUp = true, doDn = true;
2313 while ((doUp || doDn) && jmp++ < MaxUpDnLadders) {
2314 if (doUp) {
2315 int ldID = (lad0 + jmp) % lr.nLadders;
2316 if (lr.ladders[ldID].xyEdges.seenByCircle(circle, errYFrac)) {
2317 lad2Check[nacc++] = ldID;
2318 } else {
2319 doUp = false;
2320 }
2321 }
2322 if (doDn) {
2323 int ldID = lad0 - jmp;
2324 if (ldID < 0) {
2325 ldID += lr.nLadders;
2326 }
2327 if (lr.ladders[ldID].xyEdges.seenByCircle(circle, errYFrac)) {
2328 lad2Check[nacc++] = ldID;
2329 } else {
2330 doDn = false;
2331 }
2332 }
2333 }
2334 return nacc;
2335}
2336
2337//______________________________________________
2338int MatchTPCITS::findLaddersToCheckBOff(int ilr, int lad0, const o2::math_utils::IntervalXYf_t& trcLinPar, float errYFrac,
2339 std::array<int, MatchTPCITS::MaxLadderCand>& lad2Check) const
2340{
2341 // check if ladder lad0 and at most +-MaxUpDnLadders around it are compatible with linear track
2342
2343 const auto& lr = mRGHelper.layers[ilr];
2344 int nacc = 0, jmp = 0;
2345 if (lr.ladders[lad0].xyEdges.seenByLine(trcLinPar, errYFrac)) {
2346 lad2Check[nacc++] = lad0;
2347 }
2348 bool doUp = true, doDn = true;
2349 while ((doUp || doDn) && jmp++ < MaxUpDnLadders) {
2350 if (doUp) {
2351 int ldID = (lad0 + jmp) % lr.nLadders;
2352 if (lr.ladders[ldID].xyEdges.seenByLine(trcLinPar, errYFrac)) {
2353 lad2Check[nacc++] = ldID;
2354 } else {
2355 doUp = false;
2356 }
2357 }
2358 if (doDn) {
2359 int ldID = lad0 - jmp;
2360 if (ldID < 0) {
2361 ldID += lr.nLadders;
2362 }
2363 if (lr.ladders[ldID].xyEdges.seenByLine(trcLinPar, errYFrac)) {
2364 lad2Check[nacc++] = ldID;
2365 } else {
2366 doDn = false;
2367 }
2368 }
2369 }
2370 return nacc;
2371}
2372
2373//______________________________________________
2374int MatchTPCITS::registerABTrackLink(TPCABSeed& ABSeed, const o2::track::TrackParCov& trc, int clID, int parentID, int lr, int laddID, float chi2Cl)
2375{
2376 // registers new ABLink on the layer, assigning provided kinematics. The link will be registered in a
2377 // way preserving the quality ordering of the links on the layer
2378 int lnkID = ABSeed.getNLinks(), nextID = ABSeed.firstInLr[lr], nc = 1 + (parentID > MinusOne ? ABSeed.getLink(parentID).nContLayers : 0);
2379 float chi2 = chi2Cl + (parentID > MinusOne ? ABSeed.getLink(parentID).chi2 : 0.);
2380 // LOG(info) << "Reg on lr " << lr << " nc = " << nc << " chi2cl=" << chi2Cl << " -> " << chi2; // RSTMP
2381
2382 if (ABSeed.firstInLr[lr] == MinusOne) { // no links on this layer yet
2383 ABSeed.firstInLr[lr] = lnkID;
2384 ABSeed.addLink(trc, clID, parentID, MinusOne, lr, nc, laddID, chi2);
2385 return lnkID;
2386 }
2387 // add new link sorting links of this layer in quality
2388
2389 int count = 0, topID = MinusOne;
2390 do {
2391 auto& nextLink = ABSeed.getLink(nextID);
2392 count++;
2393 bool newIsBetter = parentID <= MinusOne ? isBetter(chi2, nextLink.chi2) : isBetter(ABSeed.getLink(parentID).chi2NormPredict(chi2Cl), nextLink.chi2Norm());
2394 if (newIsBetter) { // need to insert new link before nextLink
2395 if (count < mParams->maxABLinksOnLayer) { // will insert in front of nextID
2396 ABSeed.addLink(trc, clID, parentID, nextID, lr, nc, laddID, chi2);
2397 if (topID == MinusOne) { // are we comparing new link with best link on the layer?
2398 ABSeed.firstInLr[lr] = lnkID; // flag as best on the layer
2399 } else {
2400 ABSeed.getLink(topID).nextOnLr = lnkID; // point from previous one
2401 }
2402 return lnkID;
2403 } else { // max number of candidates reached, will overwrite the last one
2404 nextLink = ABTrackLink(trc, clID, parentID, MinusOne, lr, nc, laddID, chi2);
2405 return nextID;
2406 }
2407 }
2408 topID = nextID;
2409 nextID = nextLink.nextOnLr;
2410 } while (nextID > MinusOne);
2411 // new link is worse than all others, add it only if there is a room to expand
2412 if (count < mParams->maxABLinksOnLayer) {
2413 ABSeed.addLink(trc, clID, parentID, MinusOne, lr, nc, laddID, chi2);
2414 if (topID > MinusOne) {
2415 ABSeed.getLink(topID).nextOnLr = lnkID; // point from previous one
2416 }
2417 return lnkID;
2418 }
2419 return MinusOne; // link to be ignored
2420}
2421
2422//______________________________________________
2423float MatchTPCITS::correctTPCTrack(o2::track::TrackParCov& trc, const TrackLocTPC& tTPC, const InteractionCandidate& cand) const
2424{
2425 // Correct the track copy trc of the working TPC track tTPC in continuous RO mode for the assumed interaction time
2426 // return extra uncertainty in Z due to the interaction time incertainty
2427 // TODO: at the moment, apply simple shift, but with Z-dependent calibration we may
2428 // need to do corrections on TPC cluster level and refit
2430 return 0.f;
2431 }
2432 auto tpcTrOrig = mTPCTracksArray[tTPC.sourceID];
2433 float timeIC = cand.tBracket.mean();
2434 float driftErr = cand.tBracket.delta() * mTPCBin2Z;
2435
2436 // we use this for refit, at the moment it is not done ...
2437 /*
2438 {
2439 float r = std::sqrt(trc.getX()*trc.getX() + trc.getY()*trc.getY());
2440 float chi2 = 0;
2441 bool res = refitTPCInward(trc, chi2, r, tTPC.sourceID(), timeIC );
2442 if (!res) {
2443 return -1;
2444 }
2445 float xTgt;
2446 auto propagator = o2::base::Propagator::Instance();
2447 if (!trc.getXatLabR(r, xTgt, propagator->getNominalBz(), o2::track::DirInward) ||
2448 !propagator->PropagateToXBxByBz(trc, xTgt, MaxSnp, 2., mUseMatCorrFlag)) {
2449 return -1;
2450 }
2451 }
2452 */
2453 // if interaction time precedes the initial assumption on t0 (i.e. timeIC < timeTrc),
2454 // the track actually was drifting longer, i.e. tracks should be shifted closer to the CE
2455 float dDrift = (timeIC - tTPC.time0) * mTPCBin2Z;
2456
2457 // float zz = tTPC.getZ() + (tpcTrOrig.hasASideClustersOnly() ? dDrift : -dDrift); // tmp
2458 // LOG(info) << "CorrTrack Z=" << trc.getZ() << " (zold= " << zz << ") at TIC= " << timeIC << " Ttr= " << tTPC.time0; // tmp
2459
2460 // we use this w/o refit
2461 //
2462 {
2463 trc.setZ(tTPC.getZ() + (tTPC.constraint == TrackLocTPC::ASide ? dDrift : -dDrift));
2464 }
2465 //
2466 trc.setCov(trc.getSigmaZ2() + driftErr * driftErr, o2::track::kSigZ2);
2467
2468 return driftErr;
2469}
2470
2471//______________________________________________
2472void MatchTPCITS::fillClustersForAfterBurner(int rofStart, int nROFs, ITSChipClustersRefs& itsChipClRefs)
2473{
2474 // Prepare unused clusters of given ROFs range for matching in the afterburner
2475 // Note: normally only 1 ROF needs to be filled (nROFs==1 ) unless we want
2476 // to account for interaction on the boundary of 2 rofs, which then may contribute to both ROFs.
2477 int first = mITSClusterROFRec[rofStart].getFirstEntry(), last = first;
2478 for (int ir = nROFs; ir--;) {
2479 last += mITSClusterROFRec[rofStart + ir].getNEntries();
2480 }
2481 itsChipClRefs.clear();
2482 auto& idxSort = itsChipClRefs.clusterID;
2483 for (int icl = first; icl < last; icl++) {
2484 if (mABClusterLinkIndex[icl] != MinusTen) { // clusters with MinusOne are used in main matching
2485 idxSort.push_back(icl);
2486 }
2487 }
2488 // sort in chip, Z
2489 const auto& clusArr = mITSClustersArray;
2490 std::sort(idxSort.begin(), idxSort.end(), [&clusArr](int i, int j) {
2491 const auto &clI = clusArr[i], &clJ = clusArr[j];
2492 if (clI.getSensorID() < clJ.getSensorID()) {
2493 return true;
2494 }
2495 if (clI.getSensorID() == clJ.getSensorID()) {
2496 return clI.getZ() < clJ.getZ();
2497 }
2498 return false;
2499 });
2500
2501 int ncl = idxSort.size();
2502 int lastSens = -1, nClInSens = 0;
2503 ClusRange* chipClRefs = nullptr;
2504 for (int icl = 0; icl < ncl; icl++) {
2505 const auto& clus = mITSClustersArray[idxSort[icl]];
2506 int sens = clus.getSensorID();
2507 if (sens != lastSens) {
2508 if (chipClRefs) { // finalize chip reference
2509 chipClRefs->setEntries(nClInSens);
2510 nClInSens = 0;
2511 }
2512 chipClRefs = &itsChipClRefs.chipRefs[(lastSens = sens)];
2513 chipClRefs->setFirstEntry(icl);
2514 }
2515 nClInSens++;
2516 }
2517 if (chipClRefs) {
2518 chipClRefs->setEntries(nClInSens); // finalize last chip reference
2519 }
2520}
2521
2522//______________________________________________
2524{
2525 mITSTimeBiasInBC = n;
2526 mITSTimeBiasMUS = mITSTimeBiasInBC * o2::constants::lhc::LHCBunchSpacingNS * 1e-3;
2527}
2528
2529//______________________________________________
2531{
2532 mITSROFrameLengthMUS = fums;
2533 mITSTimeResMUS = mITSROFrameLengthMUS / std::sqrt(12.f);
2534 mITSROFrameLengthMUSInv = 1. / mITSROFrameLengthMUS;
2535 mITSROFrameLengthInBC = std::max(1, int(mITSROFrameLengthMUS / (o2::constants::lhc::LHCBunchSpacingNS * 1e-3)));
2536}
2537
2538//______________________________________________
2540{
2541 mITSROFrameLengthInBC = nbc;
2542 mITSROFrameLengthMUS = nbc * o2::constants::lhc::LHCBunchSpacingNS * 1e-3;
2543 mITSTimeResMUS = mITSROFrameLengthMUS / std::sqrt(12.f);
2544 mITSROFrameLengthMUSInv = 1. / mITSROFrameLengthMUS;
2545}
2546
2547//___________________________________________________________________
2549{
2550 mBunchFilling = bf;
2551 // find closest (from above) filled bunch
2552 int minBC = bf.getFirstFilledBC(), maxBC = bf.getLastFilledBC();
2553 if (minBC < 0 && mUseBCFilling) {
2554 if (mUseBCFilling) {
2555 mUseBCFilling = false;
2556 LOG(warning) << "Disabling match validation by BunchFilling as no interacting bunches found";
2557 }
2558 return;
2559 }
2560 int bcAbove = minBC;
2561 for (int i = o2::constants::lhc::LHCMaxBunches; i--;) {
2562 if (bf.testBC(i)) {
2563 bcAbove = i;
2564 }
2565 mClosestBunchAbove[i] = bcAbove;
2566 }
2567 int bcBelow = maxBC;
2568 for (int i = 0; i < o2::constants::lhc::LHCMaxBunches; i++) {
2569 if (bf.testBC(i)) {
2570 bcBelow = i;
2571 }
2572 mClosestBunchBelow[i] = bcBelow;
2573 }
2574}
2575
2576//___________________________________________________________________
2577MatchTPCITS::BracketIR MatchTPCITS::tBracket2IRBracket(const BracketF tbrange)
2578{
2579 // convert time bracket to IR bracket
2580 o2::InteractionRecord irMin(mStartIR), irMax(mStartIR);
2581 if (tbrange.getMin() > 0) {
2582 irMin += o2::InteractionRecord(tbrange.getMin() * 1000.f); // time in ns is needed
2583 }
2584 irMax += o2::InteractionRecord(tbrange.getMax() * 1000.f);
2585 irMax++; // to account for rounding
2586 int bc = mClosestBunchAbove[irMin.bc];
2587 if (bc < irMin.bc) {
2588 irMin.orbit++;
2589 }
2590 irMin.bc = bc;
2591 bc = mClosestBunchBelow[irMax.bc];
2592 if (bc > irMax.bc) {
2593 if (irMax.orbit == 0) {
2594 bc = 0;
2595 } else {
2596 irMax.orbit--;
2597 }
2598 }
2599 irMax.bc = bc;
2600 return {irMin, irMax};
2601}
2602
2603//______________________________________________
2604void MatchTPCITS::removeTPCfromITS(int tpcID, int itsID)
2605{
2607 auto& tITS = mITSWork[itsID];
2608 if (isValidatedITS(tITS)) {
2609 return;
2610 }
2611 int topID = MinusOne, next = tITS.matchID; // ITS MatchRecord
2612 while (next > MinusOne) {
2613 auto& rcITS = mMatchRecordsITS[next];
2614 if (rcITS.partnerID == tpcID) {
2615 if (topID < 0) {
2616 tITS.matchID = rcITS.nextRecID;
2617 } else {
2618 mMatchRecordsITS[topID].nextRecID = rcITS.nextRecID;
2619 }
2620 return;
2621 }
2622 topID = next;
2623 next = rcITS.nextRecID;
2624 }
2625}
2626
2627//______________________________________________
2628void MatchTPCITS::removeITSfromTPC(int itsID, int tpcID)
2629{
2631 auto& tTPC = mTPCWork[tpcID];
2632 if (isValidatedTPC(tTPC)) {
2633 return;
2634 }
2635 int topID = MinusOne, next = tTPC.matchID;
2636 while (next > MinusOne) {
2637 auto& rcTPC = mMatchRecordsTPC[next];
2638 if (rcTPC.partnerID == itsID) {
2639 if (topID < 0) {
2640 tTPC.matchID = rcTPC.nextRecID;
2641 } else {
2642 mMatchRecordsTPC[topID].nextRecID = rcTPC.nextRecID;
2643 }
2644 return;
2645 }
2646 topID = next;
2647 next = rcTPC.nextRecID;
2648 }
2649}
2650
2651//______________________________________________
2652void MatchTPCITS::flagUsedITSClusters(const o2::its::TrackITS& track)
2653{
2654 // flag clusters used by this track
2655 int clEntry = track.getFirstClusterEntry();
2656 for (int icl = track.getNumberOfClusters(); icl--;) {
2657 mABClusterLinkIndex[mITSTrackClusIdx[clEntry++]] = MinusTen;
2658 }
2659}
2660
2661//__________________________________________________________
2662int MatchTPCITS::preselectChipClusters(std::vector<int>& clVecOut, const ClusRange& clRange, const ITSChipClustersRefs& itsChipClRefs,
2663 float trackY, float trackZ, float tolerY, float tolerZ) const
2664{
2665 clVecOut.clear();
2666 int icID = clRange.getFirstEntry();
2667 for (int icl = clRange.getEntries(); icl--;) { // note: clusters within a chip are sorted in Z
2668 int clID = itsChipClRefs.clusterID[icID++]; // so, we go in clusterID increasing direction
2669 const auto& cls = mITSClustersArray[clID];
2670 float dz = cls.getZ() - trackZ;
2671 LOG(debug) << "cl" << icl << '/' << clID << " "
2672 << " dZ: " << dz << " [" << tolerZ << "| dY: " << trackY - cls.getY() << " [" << tolerY << "]";
2673 if (dz > tolerZ) {
2674 float clsZ = cls.getZ();
2675 LOG(debug) << "Skip the rest since " << trackZ << " < " << clsZ << "\n";
2676 break;
2677 } else if (dz < -tolerZ) {
2678 LOG(debug) << "Skip cluster dz=" << dz << " Ztr=" << trackZ << " zCl=" << cls.getZ();
2679 continue;
2680 }
2681 if (fabs(trackY - cls.getY()) > tolerY) {
2682 LOG(debug) << "Skip cluster dy= " << trackY - cls.getY() << " Ytr=" << trackY << " yCl=" << cls.getY();
2683 continue;
2684 }
2685 clVecOut.push_back(clID);
2686 }
2687 return clVecOut.size();
2688}
2689
2690//__________________________________________________________
2693 pmr::vector<int>& ABTrackletClusterIDs,
2694 pmr::vector<o2::MCCompLabel>& matchLabels,
2695 pmr::vector<o2::MCCompLabel>& ABTrackletLabels,
2697{
2698 size_t sizTotShm = 0, capTotShm = 0, sizTot = 0, capTot = 0, siz = 0, cap = 0, cnt = 0, cntCap = 0;
2699 {
2700 siz = matchedTracks.size() * sizeof(o2::dataformats::TrackTPCITS);
2701 cap = matchedTracks.capacity() * sizeof(o2::dataformats::TrackTPCITS);
2702 sizTotShm += siz;
2703 capTotShm += cap;
2704 LOGP(info, "Size SHM, matchedTracks : size {:9} cap {:9}", siz, cap);
2705 //
2706 siz = ABTrackletRefs.size() * sizeof(o2::itsmft::TrkClusRef);
2707 cap = ABTrackletRefs.capacity() * sizeof(o2::itsmft::TrkClusRef);
2708 sizTotShm += siz;
2709 capTotShm += cap;
2710 LOGP(info, "Size SHM, ABTrackletRefs : size {:9} cap {:9}", siz, cap);
2711 //
2712 siz = ABTrackletClusterIDs.size() * sizeof(int);
2713 cap = ABTrackletClusterIDs.capacity() * sizeof(int);
2714 sizTotShm += siz;
2715 capTotShm += cap;
2716 LOGP(info, "Size SHM, ABTrackletClusterIDs : size {:9} cap {:9}", siz, cap);
2717 //
2718 siz = matchLabels.size() * sizeof(o2::MCCompLabel);
2719 cap = matchLabels.capacity() * sizeof(o2::MCCompLabel);
2720 sizTotShm += siz;
2721 capTotShm += cap;
2722 LOGP(info, "Size SHM, matchLabels : size {:9} cap {:9}", siz, cap);
2723 //
2724 siz = ABTrackletLabels.size() * sizeof(o2::MCCompLabel);
2725 cap = ABTrackletLabels.capacity() * sizeof(o2::MCCompLabel);
2726 sizTotShm += siz;
2727 capTotShm += cap;
2728 LOGP(info, "Size SHM, ABTrackletLabels : size {:9} cap {:9}", siz, cap);
2729 //
2730 siz = calib.size() * sizeof(o2::dataformats::Triplet<float, float, float>);
2731 cap = calib.capacity() * sizeof(o2::dataformats::Triplet<float, float, float>);
2732 sizTotShm += siz;
2733 capTotShm += cap;
2734 LOGP(info, "Size SHM, calib : size {:9} cap {:9}", siz, cap);
2735 }
2736 {
2737 siz = mITSClustersArray.size() * sizeof(ITSCluster);
2738 cap = mITSClustersArray.capacity() * sizeof(ITSCluster);
2739 sizTot += siz;
2740 capTot += cap;
2741 LOGP(info, "Size RSS, mITSClustersArray : size {:9} cap {:9}", siz, cap);
2742 //
2743 siz = mMatchRecordsTPC.size() * sizeof(MatchRecord);
2744 cap = mMatchRecordsTPC.capacity() * sizeof(MatchRecord);
2745 sizTot += siz;
2746 capTot += cap;
2747 LOGP(info, "Size RSS, mMatchRecordsTPC : size {:9} cap {:9}", siz, cap);
2748 //
2749 siz = mMatchRecordsITS.size() * sizeof(MatchRecord);
2750 cap = mMatchRecordsITS.capacity() * sizeof(MatchRecord);
2751 sizTot += siz;
2752 capTot += cap;
2753 LOGP(info, "Size RSS, mMatchRecordsITS : size {:9} cap {:9}", siz, cap);
2754 //
2755 siz = mITSROFTimes.size() * sizeof(BracketF);
2756 cap = mITSROFTimes.capacity() * sizeof(BracketF);
2757 sizTot += siz;
2758 capTot += cap;
2759 LOGP(info, "Size RSS, mITSROFTimes : size {:9} cap {:9}", siz, cap);
2760 //
2761 siz = mTPCWork.size() * sizeof(TrackLocTPC);
2762 cap = mTPCWork.capacity() * sizeof(TrackLocTPC);
2763 sizTot += siz;
2764 capTot += cap;
2765 LOGP(info, "Size RSS, mTPCWork : size {:9} cap {:9}", siz, cap);
2766 //
2767 siz = mITSWork.size() * sizeof(TrackLocITS);
2768 cap = mITSWork.capacity() * sizeof(TrackLocITS);
2769 sizTot += siz;
2770 capTot += cap;
2771 LOGP(info, "Size RSS, mITSWork : size {:9} cap {:9}", siz, cap);
2772 //
2773 siz = mWinnerChi2Refit.size() * sizeof(float);
2774 cap = mWinnerChi2Refit.capacity() * sizeof(float);
2775 sizTot += siz;
2776 capTot += cap;
2777 LOGP(info, "Size RSS, mWinnerChi2Refit : size {:9} cap {:9}", siz, cap);
2778 //
2779 siz = mTPCABSeeds.size() * sizeof(float);
2780 cap = mTPCABSeeds.capacity() * sizeof(float);
2781 cnt = 0;
2782 cntCap = 0;
2783 for (const auto& a : mTPCABSeeds) {
2784 siz += a.sizeInternal();
2785 cap += a.capInternal();
2786 cnt += a.getNLinks();
2787 cntCap += a.getNLinks();
2788 }
2789 sizTot += siz;
2790 capTot += cap;
2791 LOGP(info, "Size RSS, mTPCABSeeds : size {:9} cap {:9} | internals size:{}/capacity:{} for {} elements", siz, cap, cnt, cntCap, mTPCABSeeds.size());
2792 //
2793 siz = mTPCABIndexCache.size() * sizeof(int);
2794 cap = mTPCABIndexCache.capacity() * sizeof(int);
2795 sizTot += siz;
2796 capTot += cap;
2797 LOGP(info, "Size RSS, mTPCABIndexCache : size {:9} cap {:9}", siz, cap);
2798 //
2799 siz = mABWinnersIDs.size() * sizeof(int);
2800 cap = mABWinnersIDs.capacity() * sizeof(int);
2801 sizTot += siz;
2802 capTot += cap;
2803 LOGP(info, "Size RSS, mABWinnersIDs : size {:9} cap {:9}", siz, cap);
2804 //
2805 siz = mABClusterLinkIndex.size() * sizeof(int);
2806 cap = mABClusterLinkIndex.capacity() * sizeof(int);
2807 sizTot += siz;
2808 capTot += cap;
2809 LOGP(info, "Size RSS, mABClusterLinkIndex : size {:9} cap {:9}", siz, cap);
2810 //
2811 for (int is = 0; is < o2::constants::math::NSectors; is++) {
2812 siz += mTPCSectIndexCache[is].size() * sizeof(int);
2813 cap += mTPCSectIndexCache[is].capacity() * sizeof(int);
2814 }
2815 sizTot += siz;
2816 capTot += cap;
2817 LOGP(info, "Size RSS, mTPCSectIndexCache : size {:9} cap {:9}", siz, cap);
2818 //
2819 for (int is = 0; is < o2::constants::math::NSectors; is++) {
2820 siz += mITSSectIndexCache[is].size() * sizeof(int);
2821 cap += mITSSectIndexCache[is].capacity() * sizeof(int);
2822 }
2823 sizTot += siz;
2824 capTot += cap;
2825 LOGP(info, "Size RSS, mITSSectIndexCache : size {:9} cap {:9}", siz, cap);
2826 //
2827 for (int is = 0; is < o2::constants::math::NSectors; is++) {
2828 siz += mTPCTimeStart[is].size() * sizeof(int);
2829 cap += mTPCTimeStart[is].capacity() * sizeof(int);
2830 }
2831 sizTot += siz;
2832 capTot += cap;
2833 LOGP(info, "Size RSS, mTPCTimeStart : size {:9} cap {:9}", siz, cap);
2834 //
2835 for (int is = 0; is < o2::constants::math::NSectors; is++) {
2836 siz += mITSTimeStart[is].size() * sizeof(int);
2837 cap += mITSTimeStart[is].capacity() * sizeof(int);
2838 }
2839 sizTot += siz;
2840 capTot += cap;
2841 LOGP(info, "Size RSS, mITSTimeStart : size {:9} cap {:9}", siz, cap);
2842 //
2843 siz = mITSTrackROFContMapping.size() * sizeof(int);
2844 cap = mITSTrackROFContMapping.capacity() * sizeof(int);
2845 sizTot += siz;
2846 capTot += cap;
2847 LOGP(info, "Size RSS, ITSTrackROFContMapping: size {:9} cap {:9}", siz, cap);
2848 }
2849 LOGP(info, "TotalSizes/Capacities: SHM: {}/{} Heap: {}/{}", sizTotShm, capTotShm, sizTot, capTot);
2850}
2851
2852//__________________________________________________________
2854{
2855#ifdef WITH_OPENMP
2856 mNThreads = n > 0 ? n : 1;
2857#else
2858 LOG(warning) << "Multithreading is not supported, imposing single thread";
2859 mNThreads = 1;
2860#endif
2861 mABLinksPool.threadPool.resize(mNThreads);
2862 TPCABSeed::gLinksPool = &mABLinksPool;
2863}
2864
2865//<<============================= AfterBurner for TPC-track / ITS cluster matching ===================<<
2866
2867#ifdef _ALLOW_DEBUG_TREES_
2868//______________________________________________
2869void MatchTPCITS::setDebugFlag(UInt_t flag, bool on)
2870{
2872 if (on) {
2873 mDBGFlags |= flag;
2874 } else {
2875 mDBGFlags &= ~flag;
2876 }
2877}
2878
2879//_________________________________________________________
2880void MatchTPCITS::dumpTPCOrig(bool acc, int tpcIndex)
2881{
2883 mTimer[SWDBG].Start(false);
2884 const auto& tpcOrig = mTPCTracksArray[tpcIndex];
2885 uint8_t clSect = 0, clRow = 0, prevRow = 0xff, padFromEdge = -1;
2886 uint32_t clIdx = 0;
2887 int nshared = 0;
2888 std::array<bool, 152> shMap{};
2889 bool prevRawShared = false;
2890 for (int i = 0; i < tpcOrig.getNClusterReferences(); i++) {
2891 tpcOrig.getClusterReference(mTPCTrackClusIdx, i, clSect, clRow, clIdx);
2892 unsigned int absoluteIndex = mTPCClusterIdxStruct->clusterOffset[clSect][clRow] + clIdx;
2893 if (mTPCRefitterShMap[absoluteIndex] & o2::gpu::GPUTPCGMMergedTrackHit::flagShared) {
2894 if (!(prevRow == clRow && prevRawShared)) {
2895 nshared++;
2896 }
2897 prevRow = clRow;
2898 prevRawShared = true;
2899 }
2900 }
2901 const auto& clus = mTPCClusterIdxStruct->clusters[clSect][clRow][clIdx];
2902 padFromEdge = uint8_t(clus.getPad());
2903 if (padFromEdge > TPCGeometry.NPads(clRow) / 2) {
2904 padFromEdge = TPCGeometry.NPads(clRow) - 1 - padFromEdge;
2905 }
2906 int tb = tpcOrig.getTime0() * mNTPCOccBinLengthInv;
2907 float mltTPC = tb < 0 ? mTBinClOcc[0] : (tb >= mTBinClOcc.size() ? mTBinClOcc.back() : mTBinClOcc[tb]);
2908 (*mDBGOut) << "tpcOrig"
2909 << "tf=" << mTFCount
2910 << "index=" << tpcIndex
2911 << "acc=" << acc
2912 << "chi2TPC=" << tpcOrig.getChi2()
2913 << "nClus=" << tpcOrig.getNClusters()
2914 << "nShared=" << nshared
2915 << "time0=" << tpcOrig.getTime0()
2916 << "trc=" << ((o2::track::TrackParCov&)tpcOrig)
2917 << "minRow=" << clRow
2918 << "padFromEdge=" << padFromEdge
2919 << "multTPC=" << mltTPC;
2920 if (mMCTruthON) {
2921 (*mDBGOut) << "tpcOrig"
2922 << "tpcLbl=" << mTPCTrkLabels[tpcIndex];
2923 }
2924 (*mDBGOut) << "tpcOrig"
2925 << "\n";
2926 mTimer[SWDBG].Stop();
2927}
2928
2929//_________________________________________________________
2930void MatchTPCITS::fillTPCITSmatchTree(int itsID, int tpcID, int rejFlag, float chi2, float tCorr)
2931{
2933
2934 mTimer[SWDBG].Start(false);
2935
2936 auto& trackITS = mITSWork[itsID];
2937 auto& trackTPC = mTPCWork[tpcID];
2938 if (chi2 < 0.) { // need to recalculate
2939 chi2 = getPredictedChi2NoZ(trackITS, trackTPC);
2940 }
2941 (*mDBGOut) << "match"
2942 << "tf=" << mTFCount << "chi2Match=" << chi2 << "its=" << trackITS << "tpc=" << trackTPC << "tcorr=" << tCorr;
2943 if (mMCTruthON) {
2944 (*mDBGOut) << "match"
2945 << "itsLbl=" << mITSLblWork[itsID] << "tpcLbl=" << mTPCLblWork[tpcID];
2946 }
2947 int tb = mTPCTracksArray[trackTPC.sourceID].getTime0() * mNTPCOccBinLengthInv;
2948 float mltTPC = tb < 0 ? mTBinClOcc[0] : (tb >= mTBinClOcc.size() ? mTBinClOcc.back() : mTBinClOcc[tb]);
2949 (*mDBGOut) << "match"
2950 << "rejFlag=" << rejFlag
2951 << "multTPC=" << mltTPC
2952 << "multITSTr=" << mITSTrackROFRec[trackITS.roFrame].getNEntries()
2953 << "multITSCl=" << mITSClusterROFRec[trackITS.roFrame].getNEntries()
2954 << "\n";
2955
2956 mTimer[SWDBG].Stop();
2957}
2958
2959//______________________________________________
2961{
2963
2964 mTimer[SWDBG].Start(false);
2965
2966 LOG(info) << "Dumping debug tree for winner matches";
2967 for (int iits = 0; iits < int(mITSWork.size()); iits++) {
2968 auto& tITS = mITSWork[iits];
2969 if (isDisabledITS(tITS)) {
2970 continue;
2971 }
2972 auto& itsMatchRec = mMatchRecordsITS[tITS.matchID];
2973 int itpc = itsMatchRec.partnerID;
2974 auto& tTPC = mTPCWork[itpc];
2975
2976 (*mDBGOut) << "matchWin"
2977 << "tf=" << mTFCount << "chi2Match=" << itsMatchRec.chi2 << "chi2Refit=" << mWinnerChi2Refit[iits] << "its=" << tITS << "tpc=" << tTPC;
2978
2979 if (mMCTruthON) {
2980 (*mDBGOut) << "matchWin"
2981 << "itsLbl=" << mITSLblWork[iits] << "tpcLbl=" << mTPCLblWork[itpc];
2982 }
2983 int tb = mTPCTracksArray[tTPC.sourceID].getTime0() * mNTPCOccBinLengthInv;
2984 float mltTPC = tb < 0 ? mTBinClOcc[0] : (tb >= mTBinClOcc.size() ? mTBinClOcc.back() : mTBinClOcc[tb]);
2985 (*mDBGOut) << "matchWin"
2986 << "multTPC=" << mltTPC
2987 << "multITSTr=" << mITSTrackROFRec[tITS.roFrame].getNEntries()
2988 << "multITSCl=" << mITSClusterROFRec[tITS.roFrame].getNEntries()
2989 << "\n";
2990 }
2991 mTimer[SWDBG].Stop();
2992}
2993
2994#endif
General auxilliary methods.
Wrapper container for different reconstructed object types.
Definition of the GeometryManager class.
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.
std::ostringstream debug
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:143
void printKeyValues(bool showProv=true, bool useLogger=false) 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
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:31
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:49
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, o2::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 ...
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
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
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"
o2::InteractionRecord ir(0, 0)
char const *restrict const cmp
Definition x9.h:96