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