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