Project
Loading...
Searching...
No Matches
TrackInterpolation.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
17
20#include "ITStracking/IOUtils.h"
23#include "TOFBase/Geo.h"
25#include "DataFormatsTPC/Defs.h"
30#include "MathUtils/Tsallis.h"
31#include "TRDBase/PadPlane.h"
32#include "TMath.h"
34#include "Framework/Logger.h"
36#include "GPUO2InterfaceUtils.h"
38#include "GPUO2InterfaceRefit.h"
39#include "GPUParam.h"
40#include "GPUParam.inc"
41#include <set>
42#include <algorithm>
43#include <random>
44
45using namespace o2::tpc;
48
49bool UnbinnedResid::gInitDone = false;
50
52{
53 if (!isITS()) {
55 }
56 // ITS alpha repends on the chip ID
59}
60
62{
63 if (isTPC()) {
64 return param::RowX[row];
65 }
67 if (isITS()) {
68 return o2::its::GeometryTGeo::Instance()->getSensorRefX(channel); // ITS X repends on the chip ID
69 }
70 if (isTRD()) {
71 auto geo = o2::trd::Geometry::instance();
72 ROOT::Math::Impl::Transform3D<double>::Point local{geo->cdrHght() - 0.5 - 0.279, 0., 0.}; // see TrackletTransformer::transformTracklet
73 return (geo->getMatrixT2L(channel) ^ local).X();
74 }
75 if (isTOF()) {
76 int det[5];
78 float pos[3] = {0.f, 0.f, 0.f};
80 float posl[3] = {pos[0], pos[1], pos[2]};
82 return pos[2]; // coordinates in sector frame: note that the rotation above puts z in pos[1], the radial coordinate in pos[2], and the tangent coordinate in pos[0] (this is to match the TOF residual system, where we don't use the radial component), so we swap their positions.
83 }
84 LOGP(fatal, "Did not recognize detector type: row:{}, sec:{}, channel:{}", row, sec, channel);
85 return 0.;
86}
87
89{
90 if (!gInitDone) {
91 LOGP(warn, "geometry initialization was not done, doing this for the current timestamp");
92 init();
93 if (!gInitDone) {
94 LOGP(fatal, "geometry initialization failed");
95 }
96 }
97}
98
99void UnbinnedResid::init(long timestamp)
100{
101 if (gInitDone) {
102 LOGP(warn, "Initialization was already done");
103 return;
104 }
105 if (!gGeoManager) {
106 o2::ccdb::BasicCCDBManager::instance().getSpecific<TGeoManager>("GLO/Config/GeometryAligned", timestamp);
107 }
108 auto geoTRD = o2::trd::Geometry::instance();
109 geoTRD->createPadPlaneArray();
110 geoTRD->createClusterMatrixArray();
111 gInitDone = true;
112}
113
118
120{
121 if (mDBGOut) {
122 mDBGOut->Close();
123 mDBGOut.reset();
124 }
125}
126
128{
129 // perform initialization
130 LOG(info) << "Start initializing TrackInterpolation";
131 if (mInitDone) {
132 LOG(error) << "Initialization already performed.";
133 return;
134 }
135
136 const auto& elParam = ParameterElectronics::Instance();
137 mTPCTimeBinMUS = elParam.ZbinWidth;
138
139 mFastTransform = std::move(TPCFastTransformHelperO2::instance()->create(0));
140
141 mBz = o2::base::Propagator::Instance()->getNominalBz();
142 mRecoParam.init(mBz);
143 mGeoTRD = o2::trd::Geometry::instance();
145
146 mSourcesConfigured = src;
147 mSourcesConfiguredMap = srcMap;
148 mSingleSourcesConfigured = (mSourcesConfigured == mSourcesConfiguredMap);
149 mTrackTypes.insert({GTrackID::ITSTPC, 0});
150 mTrackTypes.insert({GTrackID::ITSTPCTRD, 1});
151 mTrackTypes.insert({GTrackID::ITSTPCTOF, 2});
152 mTrackTypes.insert({GTrackID::ITSTPCTRDTOF, 3});
153
155 geom->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::L2G));
156 mTPCParam = o2::gpu::GPUO2InterfaceUtils::getFullParamShared(0.f, mNHBPerTF);
157
158 if (mParams->writeValidationData) {
159 std::string dbgnm = mNLanes == 1 ? "track_interpolation_dbg.root" : fmt::format("track_interpolation_dbg_{}.root", mLaneID);
160 mDBGOut = std::make_unique<o2::utils::TreeStreamRedirector>(dbgnm.c_str(), "recreate");
161 }
162
163 mInitDone = true;
164 LOGP(info, "Done initializing TrackInterpolation. Configured track input: {}. Track input specifically for map: {}",
165 GTrackID::getSourcesNames(mSourcesConfigured), mSingleSourcesConfigured ? "identical" : GTrackID::getSourcesNames(mSourcesConfiguredMap));
166}
167
169{
170 LOGP(debug, "Check if input track {} is accepted", gid.asString());
171 bool hasOuterPoint = gidTable[GTrackID::TRD].isIndexSet() || gidTable[GTrackID::TOF].isIndexSet();
172 if (!hasOuterPoint && !mProcessITSTPConly) {
173 return false; // don't do ITS-only extrapolation through TPC
174 }
175 const auto itsTrk = mRecoCont->getITSTrack(gidTable[GTrackID::ITS]);
176 const auto tpcTrk = mRecoCont->getTPCTrack(gidTable[GTrackID::TPC]);
177
178 if (gidTable[GTrackID::TRD].isIndexSet()) {
179 // TRD specific cuts
180 const auto& trdTrk = mRecoCont->getITSTPCTRDTrack<o2::trd::TrackTRD>(gidTable[GTrackID::ITSTPCTRD]);
181 if (trdTrk.getNtracklets() < mParams->minTRDNTrklts) {
182 return false;
183 }
184 }
185 // reduced chi2 cut is the same for all track types
186 if (itsTrk.getChi2() / itsTrk.getNumberOfClusters() > mParams->maxITSChi2 || tpcTrk.getChi2() / tpcTrk.getNClusterReferences() > mParams->maxTPCChi2) {
187 return false;
188 }
189 if (!hasOuterPoint) {
190 // ITS-TPC track (does not have outer points in TRD or TOF)
191 if (itsTrk.getNumberOfClusters() < mParams->minITSNClsNoOuterPoint || tpcTrk.getNClusterReferences() < mParams->minTPCNClsNoOuterPoint) {
192 return false;
193 }
194 if (itsTrk.getPt() < mParams->minPtNoOuterPoint) {
195 return false;
196 }
197 } else {
198 if (itsTrk.getNumberOfClusters() < mParams->minITSNCls || tpcTrk.getNClusterReferences() < mParams->minTPCNCls) {
199 return false;
200 }
201 }
202
203 auto trc = mRecoCont->getTrackParam(gid);
205 auto prop = o2::base::Propagator::Instance();
206 if (!prop->propagateToDCA(pv, trc, prop->getNominalBz(), 2., o2::base::PropagatorF::MatCorrType::USEMatCorrLUT, &dca)) {
207 return false;
208 }
209 if (dca.getR2() > mParams->maxDCA * mParams->maxDCA) {
210 return false;
211 }
212 return true;
213}
214
216{
217 LOGP(debug, "Trying to find valid source for {} in {}", GTrackID::getSourceName(src), GTrackID::getSourcesNames(mask));
220 return GTrackID::ITSTPCTRD;
221 } else if (mask[GTrackID::ITSTPC]) {
222 return GTrackID::ITSTPC;
223 } else {
224 return GTrackID::NSources;
225 }
226 } else if (src == GTrackID::ITSTPCTRD || src == GTrackID::ITSTPCTOF) {
227 if (mask[GTrackID::ITSTPC]) {
228 return GTrackID::ITSTPC;
229 } else {
230 return GTrackID::NSources;
231 }
232 } else {
233 return GTrackID::NSources;
234 }
235}
236
238{
239 mRecoCont = &inp;
240 uint32_t nTrackSeeds = 0;
241 uint32_t countSeedCandidates[4] = {0};
242 auto pvvec = mRecoCont->getPrimaryVertices();
243 auto trackIndex = mRecoCont->getPrimaryVertexMatchedTracks(); // Global ID's for associated tracks
244 auto vtxRefs = mRecoCont->getPrimaryVertexMatchedTrackRefs(); // references from vertex to these track IDs
245 int nv = vtxRefs.size() - 1;
246 GTrackID::mask_t allowedSources = GTrackID::getSourcesMask("ITS-TPC,ITS-TPC-TRD,ITS-TPC-TOF,ITS-TPC-TRD-TOF") & mSourcesConfigured;
247 constexpr std::array<int, 3> SrcFast = {int(GTrackID::ITSTPCTRD), int(GTrackID::ITSTPCTOF), int(GTrackID::ITSTPCTRDTOF)};
248 if (mParams->refitITS) {
249 mITSRefitSeedID.resize(mRecoCont->getITSTracks().size(), -1);
250 }
251
252 for (int iv = 0; iv < nv; iv++) {
253 LOGP(debug, "processing PV {} of {}", iv, nv);
254
255 const auto& vtref = vtxRefs[iv];
256 auto pv = pvvec[iv];
257 if (mParams->minTOFTRDPVContributors > 0) { // we want only PVs constrained by fast detectors
258 int nfound = 0;
259 bool usePV = false;
260 for (uint32_t is = 0; is < SrcFast.size() && !usePV; is++) {
261 int src = SrcFast[is], idMin = vtref.getFirstEntryOfSource(src), idMax = idMin + vtref.getEntriesOfSource(src);
262 for (int i = idMin; i < idMax; i++) {
263 if (trackIndex[i].isPVContributor() && (++nfound == mParams->minTOFTRDPVContributors)) {
264 usePV = true;
265 break;
266 }
267 }
268 }
269 if (!usePV) {
270 continue;
271 }
272 }
273
274 for (int is = GTrackID::NSources; is--;) {
275 if (!allowedSources[is]) {
276 continue;
277 }
278 LOGP(debug, "Checking source {}", is);
279 int idMin = vtref.getFirstEntryOfSource(is), idMax = idMin + vtref.getEntriesOfSource(is);
280 for (int i = idMin; i < idMax; i++) {
281 auto vid = trackIndex[i];
282 auto vidOrig = vid; // in case only ITS-TPC tracks are configured vid might be overwritten. We need to remember it for the PID
283 if (mParams->ignoreNonPVContrib && !vid.isPVContributor()) {
284 continue;
285 }
286 if (vid.isAmbiguous()) {
287 continue;
288 }
289 auto gidTable = mRecoCont->getSingleDetectorRefs(vid);
290 if (!mSourcesConfigured[is]) {
291 auto src = findValidSource(mSourcesConfigured, static_cast<GTrackID::Source>(vid.getSource()));
293 LOGP(debug, "prepareInputTrackSample: Found valid source {}", GTrackID::getSourceName(src));
294 vid = gidTable[src];
295 gidTable = mRecoCont->getSingleDetectorRefs(vid);
296 } else {
297 break; // no valid source for this vertex track source exists
298 }
299 }
300 ++countSeedCandidates[mTrackTypes[vid.getSource()]];
301 LOGP(debug, "Checking vid {}", vid.asString());
302 if (!isInputTrackAccepted(vid, gidTable, pv)) {
303 continue;
304 }
305 mSeeds.push_back(mRecoCont->getITSTrack(gidTable[GTrackID::ITS]).getParamOut());
306 mSeeds.back().setPID(mRecoCont->getTrackParam(vidOrig).getPID(), true);
307 mGIDs.push_back(vid);
308 mGIDtables.push_back(gidTable);
309 mTrackTimes.push_back(pv.getTimeStamp().getTimeStamp());
310 mTrackIndices[mTrackTypes[vid.getSource()]].push_back(nTrackSeeds++);
311 mTrackPVID.push_back(iv);
312 }
313 }
314 }
315
316 LOGP(info, "Created {} seeds. {} out of {} ITS-TPC-TRD-TOF, {} out of {} ITS-TPC-TRD, {} out of {} ITS-TPC-TOF, {} out of {} ITS-TPC",
317 nTrackSeeds,
318 mTrackIndices[mTrackTypes[GTrackID::ITSTPCTRDTOF]].size(), countSeedCandidates[mTrackTypes[GTrackID::ITSTPCTRDTOF]],
319 mTrackIndices[mTrackTypes[GTrackID::ITSTPCTRD]].size(), countSeedCandidates[mTrackTypes[GTrackID::ITSTPCTRD]],
320 mTrackIndices[mTrackTypes[GTrackID::ITSTPCTOF]].size(), countSeedCandidates[mTrackTypes[GTrackID::ITSTPCTOF]],
321 mTrackIndices[mTrackTypes[GTrackID::ITSTPC]].size(), countSeedCandidates[mTrackTypes[GTrackID::ITSTPC]]);
322}
323
325{
326 std::random_device rd;
327 std::mt19937 g(rd());
328 std::uniform_real_distribution<> distr(0., 1.);
329 float weight = 0;
330 return o2::math_utils::Tsallis::downsampleTsallisCharged(trk.getPt(), mParams->tsalisThreshold, mSqrtS, weight, distr(g), o2::constants::physics::MassPionCharged);
331}
332
334{
335 // main processing function
336 if (!mInitDone) {
337 LOG(error) << "Initialization not yet done. Aborting...";
338 return;
339 }
340 // set the input containers
341 mTPCTrackClusIdx = mRecoCont->getTPCTracksClusterRefs();
342 mTPCShClassMap = mRecoCont->clusterShMapTPC;
343 mTPCClusterIdxStruct = &mRecoCont->getTPCClusters();
344 int nbOccTOT = o2::gpu::GPUO2InterfaceRefit::fillOccupancyMapGetSize(mNHBPerTF, mTPCParam.get());
345 o2::gpu::GPUO2InterfaceUtils::paramUseExternalOccupancyMap(mTPCParam.get(), mNHBPerTF, mRecoCont->occupancyMapTPC.data(), nbOccTOT);
346 mNTPCOccBinLength = mTPCParam->rec.tpc.occupancyMapTimeBins;
347 mNTPCOccBinLengthInv = 1.f / mNTPCOccBinLength;
348 {
349 if (!mITSDict) {
350 LOG(error) << "No ITS dictionary available";
351 return;
352 }
353 mITSTrackClusIdx = mRecoCont->getITSTracksClusterRefs();
354 int nLr = mRecoCont->getITSPerLayer() ? o2::globaltracking::MaxITSLayers : 1;
355 mITSClustersArray.init(nLr);
356 for (int lr = 0; lr < nLr; lr++) { // with a single (monolithic) input all clusters are in the layer slot 0
357 mITSClustersArray.beginLayer(lr);
358 const auto clusITS = mRecoCont->getITSClusters(lr);
359 const auto patterns = mRecoCont->getITSClustersPatterns(lr);
360 auto pattIt = patterns.begin();
361 mITSClustersArray.getClusters().reserve(mITSClustersArray.size() + clusITS.size());
362 LOGP(info, "We have {} ITS clusters and the number of patterns is {} on the layer slot {}", clusITS.size(), patterns.size(), lr);
363 o2::its::ioutils::convertCompactClusters(clusITS, pattIt, mITSClustersArray.getClusters(), mITSDict);
364 }
365 mITSClustersArray.finalize();
366 }
367
368 // In case we have more input tracks available than are required per TF
369 // we want to sample them. But we still prefer global ITS-TPC-TRD-TOF tracks
370 // over ITS-TPC-TRD tracks and so on. So we have to shuffle the indices
371 // in blocks.
372 std::random_device rd;
373 std::mt19937 g(rd());
374 std::vector<uint32_t> trackIndices; // here we keep the GIDs for all track types in a single vector to use in loop
375 std::shuffle(mTrackIndices[mTrackTypes[GTrackID::ITSTPCTRDTOF]].begin(), mTrackIndices[mTrackTypes[GTrackID::ITSTPCTRDTOF]].end(), g);
376 std::shuffle(mTrackIndices[mTrackTypes[GTrackID::ITSTPCTRD]].begin(), mTrackIndices[mTrackTypes[GTrackID::ITSTPCTRD]].end(), g);
377 std::shuffle(mTrackIndices[mTrackTypes[GTrackID::ITSTPCTOF]].begin(), mTrackIndices[mTrackTypes[GTrackID::ITSTPCTOF]].end(), g);
378 std::shuffle(mTrackIndices[mTrackTypes[GTrackID::ITSTPC]].begin(), mTrackIndices[mTrackTypes[GTrackID::ITSTPC]].end(), g);
379 trackIndices.insert(trackIndices.end(), mTrackIndices[mTrackTypes[GTrackID::ITSTPCTRDTOF]].begin(), mTrackIndices[mTrackTypes[GTrackID::ITSTPCTRDTOF]].end());
380 trackIndices.insert(trackIndices.end(), mTrackIndices[mTrackTypes[GTrackID::ITSTPCTRD]].begin(), mTrackIndices[mTrackTypes[GTrackID::ITSTPCTRD]].end());
381 trackIndices.insert(trackIndices.end(), mTrackIndices[mTrackTypes[GTrackID::ITSTPCTOF]].begin(), mTrackIndices[mTrackTypes[GTrackID::ITSTPCTOF]].end());
382 trackIndices.insert(trackIndices.end(), mTrackIndices[mTrackTypes[GTrackID::ITSTPC]].begin(), mTrackIndices[mTrackTypes[GTrackID::ITSTPC]].end());
383 int nSeeds = mSeeds.size(), lastChecked = 0;
384 mParentID.clear();
385 mParentID.resize(nSeeds, -1);
386
387 int maxOutputTracks = (mMaxTracksPerTF >= 0) ? mMaxTracksPerTF + mAddTracksForMapPerTF : nSeeds;
388 mTrackData.reserve(maxOutputTracks);
389 mClRes.reserve(maxOutputTracks * param::NPadRows);
390 mDetInfoRes.reserve(maxOutputTracks * param::NPadRows);
391 bool maxTracksReached = false;
392 for (int iSeed = 0; iSeed < nSeeds; ++iSeed) {
393 if (mMaxTracksPerTF >= 0 && mTrackDataCompact.size() >= mMaxTracksPerTF + mAddTracksForMapPerTF) {
394 LOG(info) << "Maximum number of tracks per TF reached. Skipping the remaining " << nSeeds - iSeed << " tracks.";
395 break;
396 }
397 int seedIndex = trackIndices[iSeed];
398 if (mParams->enableTrackDownsampling && !isTrackSelected(mSeeds[seedIndex])) {
399 continue;
400 }
401 auto addPart = [this, seedIndex](GTrackID::Source src) {
402 this->mGIDs.push_back(this->mGIDtables[seedIndex][src]);
403 this->mGIDtables.push_back(this->mRecoCont->getSingleDetectorRefs(this->mGIDs.back()));
404 this->mTrackTimes.push_back(this->mTrackTimes[seedIndex]);
405 this->mSeeds.push_back(this->mSeeds[seedIndex]);
406 this->mParentID.push_back(seedIndex); // store parent seed id
407 this->mTrackPVID.push_back(this->mTrackPVID[seedIndex]);
408 };
409
410 GTrackID::mask_t partsAdded;
411 if (!mSingleSourcesConfigured && !mSourcesConfiguredMap[mGIDs[seedIndex].getSource()]) {
412 auto src = findValidSource(mSourcesConfiguredMap, static_cast<GTrackID::Source>(mGIDs[seedIndex].getSource()));
414 LOGP(debug, "process {}: Found valid source {} for {} | nseeds:{} mSeeds:{} used: {}", iSeed, GTrackID::getSourceName(src), GTrackID::getSourceName(mGIDs[seedIndex].getSource()), nSeeds, mSeeds.size(), mTrackDataCompact.size());
415 addPart(src);
416 }
417 }
418 if (mMaxTracksPerTF >= 0 && mTrackDataCompact.size() >= mMaxTracksPerTF) {
419 if (!maxTracksReached) {
420 LOGP(info, "We already have reached mMaxTracksPerTF={}, but we continue to create seeds until mAddTracksForMapPerTF={} is also reached, iSeed: {} of {} inital seeds", mMaxTracksPerTF, mAddTracksForMapPerTF, iSeed, nSeeds);
421 }
422 maxTracksReached = true;
423 continue;
424 }
425 if (mGIDs[seedIndex].includesDet(DetID::TRD) || mGIDs[seedIndex].includesDet(DetID::TOF)) {
426 interpolateTrack(seedIndex);
427 LOGP(debug, "interpolateTrack {} {}, accepted: {}", iSeed, GTrackID::getSourceName(mGIDs[seedIndex].getSource()), mTrackDataCompact.size());
428 if (mProcessSeeds) {
429 if (mGIDs[seedIndex].includesDet(DetID::TRD) && mGIDs[seedIndex].includesDet(DetID::TOF) && !partsAdded[GTrackID::ITSTPCTRD]) {
430 addPart(GTrackID::ITSTPCTRD);
431 }
432 if (!partsAdded[GTrackID::ITSTPC]) {
433 addPart(GTrackID::ITSTPC);
434 }
435 }
436 } else {
437 extrapolateTrack(seedIndex);
438 LOGP(debug, "extrapolateTrack {} {}, accepted: {}", iSeed, GTrackID::getSourceName(mGIDs[seedIndex].getSource()), mTrackDataCompact.size());
439 }
440 lastChecked = iSeed;
441 }
442 std::vector<int> remSeeds;
443 if (mSeeds.size() > ++lastChecked) {
444 remSeeds.resize(mSeeds.size() - lastChecked);
445 std::iota(remSeeds.begin(), remSeeds.end(), lastChecked);
446 std::shuffle(remSeeds.begin(), remSeeds.end(), g);
447 LOGP(info, "Up to {} tracks out of {} additional seeds will be processed in random order, of which {} are stripped versions, accepted seeds: {}",
448 mAddTracksForMapPerTF > 0 ? mAddTracksForMapPerTF : remSeeds.size(),
449 remSeeds.size(), mSeeds.size() - nSeeds, mTrackDataCompact.size());
450 }
451 int extraChecked = 0;
452 for (int iSeed : remSeeds) {
453 if (mAddTracksForMapPerTF > 0 && mTrackDataCompact.size() >= mMaxTracksPerTF + mAddTracksForMapPerTF) {
454 LOGP(info, "Maximum number {} of additional tracks per TF reached. Skipping the remaining {} tracks", mAddTracksForMapPerTF, remSeeds.size() - extraChecked);
455 break;
456 }
457 extraChecked++;
458 if (mGIDs[iSeed].includesDet(DetID::TRD) || mGIDs[iSeed].includesDet(DetID::TOF)) {
459 interpolateTrack(iSeed);
460 LOGP(debug, "extra check {} of {}, seed {} interpolateTrack {}, used: {}", extraChecked, remSeeds.size(), iSeed, GTrackID::getSourceName(mGIDs[iSeed].getSource()), mTrackDataCompact.size());
461 } else {
462 LOGP(debug, "extra check {} of {}, seed {} extrapolateTrack {}, used: {}", extraChecked, remSeeds.size(), iSeed, GTrackID::getSourceName(mGIDs[iSeed].getSource()), mTrackDataCompact.size());
463 extrapolateTrack(iSeed);
464 }
465 }
466 LOGP(info, "Could process {} tracks successfully ({} rejected in refits, {} in propagation, {} as loopers), {} residuals were rejected, {} accepted",
467 mTrackData.size(), mNRejRefit, mNRejProp, mNRejLoop, mRejectedResiduals, mClRes.size());
468 if (mParams->keepClustersOnPropFail) {
469 LOGP(info, "keepClustersOnPropFail: {} tracks kept after a propagation failure, {} position-only TPC clusters stored", mNPosOnlyTracks, mNPosOnlyClusters);
470 }
471 mNPosOnlyTracks = 0;
472 mNPosOnlyClusters = 0;
473 mRejectedResiduals = 0;
474 mNRejRefit = 0;
475 mNRejProp = 0;
476 mNRejLoop = 0;
477}
478
479namespace
480{
482struct PositionOnlyCluster {
483 float y, z;
484 unsigned char sec, row, flags;
485};
486} // namespace
487
489{
490 LOGP(debug, "Starting track interpolation for GID {}", mGIDs[iSeed].asString());
491 TrackData trackData;
492 o2::trd::Tracklet64 trkl64;
494 std::unique_ptr<TrackDataExtended> trackDataExtended;
495 std::vector<TPCClusterResiduals> clusterResiduals;
496 auto propagator = o2::base::Propagator::Instance();
497 const auto& gidTable = mGIDtables[iSeed];
498 const auto& trkTPC = mRecoCont->getTPCTrack(gidTable[GTrackID::TPC]);
499 const auto& trkITS = mRecoCont->getITSTrack(gidTable[GTrackID::ITS]);
500 if (mDumpTrackPoints) {
501 trackDataExtended = std::make_unique<TrackDataExtended>();
502 (*trackDataExtended).gid = mGIDs[iSeed];
503 (*trackDataExtended).clIdx.setFirstEntry(mClRes.size());
504 (*trackDataExtended).trkITS = trkITS;
505 (*trackDataExtended).trkTPC = trkTPC;
506 auto nCl = trkITS.getNumberOfClusters();
507 auto clEntry = trkITS.getFirstClusterEntry();
508 for (int iCl = nCl - 1; iCl >= 0; iCl--) { // clusters are stored from outer to inner layers
509 const auto& clsITS = mITSClustersArray[mITSTrackClusIdx[clEntry + iCl]];
510 (*trackDataExtended).clsITS.push_back(clsITS);
511 }
512 }
513 if (mParams->refitITS && !refITSTrack(gidTable[GTrackID::ITS], iSeed)) {
514 mNRejRefit++;
515 return;
516 }
517 trackData.gid = mGIDs[iSeed];
518 trackData.par = mSeeds[iSeed];
519 auto trkWork = mSeeds[iSeed];
520 o2::track::TrackPar trkInner{trkWork};
521 // reset the cache array (sufficient to set cluster available to zero)
522 for (auto& elem : mCache) {
523 elem.clAvailable = 0;
524 }
525 trackData.clIdx.setFirstEntry(mClRes.size()); // reference the first cluster residual belonging to this track
526 float clusterTimeBinOffset = mTrackTimes[iSeed] / mTPCTimeBinMUS;
527
528 // store the TPC cluster positions in the cache, as well as dedx info
529 std::array<std::pair<uint16_t, uint16_t>, constants::MAXGLOBALPADROW> mCacheDEDX{};
530 std::array<short, constants::MAXGLOBALPADROW> multBins{};
531 // keepClustersOnPropFail: a row gets a residual only if both the outward (ITS) and the inward (TRD/TOF) propagation reached
532 // it; the other TPC clusters are stored position-only. allLost: the outward pass or the outer anchor failed.
533 bool allLost = false;
534 std::array<bool, constants::MAXGLOBALPADROW> refOut{}, refIn{};
535 std::vector<PositionOnlyCluster> posOnly;
536 for (int iCl = trkTPC.getNClusterReferences(); iCl--;) {
537 uint8_t sector, row;
538 uint32_t clusterIndexInRow;
539 trkTPC.getClusterReference(mTPCTrackClusIdx, iCl, sector, row, clusterIndexInRow);
540 unsigned int absoluteIndex = mTPCClusterIdxStruct->clusterOffset[sector][row] + clusterIndexInRow;
541 const auto& clTPC = mTPCClusterIdxStruct->clustersLinear[absoluteIndex];
542 float clTPCX;
543 std::array<float, 2> clTPCYZ;
544 mFastTransform->TransformIdeal(sector, row, clTPC.getPad(), clTPC.getTime(), clTPCX, clTPCYZ[0], clTPCYZ[1], clusterTimeBinOffset);
545 mCache[row].clSec = sector;
546 mCache[row].clAvailable = 1;
547 mCache[row].clY = clTPCYZ[0];
548 mCache[row].clZ = clTPCYZ[1];
549 mCache[row].clAngle = o2::math_utils::sector2Angle(sector);
550 mCache[row].clFlags = clTPC.getFlags();
551 if (mTPCShClassMap[absoluteIndex] & o2::gpu::GPUTPCGMMergedTrackHit::flagShared) {
553 }
554 mCacheDEDX[row].first = std::min<uint16_t>(clTPC.getQtot(), UINT16_MAX);
555 mCacheDEDX[row].second = clTPC.getQmax();
556 int imb = int(clTPC.getTime() * mNTPCOccBinLengthInv);
557 if (imb < mTPCParam->occupancyMapSize) {
558 multBins[row] = 1 + std::max(0, imb);
559 }
560 }
561
562 // extrapolate seed through TPC and store track position at each pad row
563 for (int iRow = 0; iRow < param::NPadRows; ++iRow) {
564 if (!mCache[iRow].clAvailable) {
565 continue;
566 }
567 if (!trkWork.rotate(mCache[iRow].clAngle) || !propagator->PropagateToXBxByBz(trkWork, param::RowX[iRow], mParams->maxSnp, mParams->maxStep, mMatCorr)) {
568 LOG(debug) << "Failed on first extrapolation";
569 if (!mParams->keepClustersOnPropFail) {
570 mNRejProp++;
571 return;
572 }
573 allLost = true; // no outer anchor can be reached: every TPC cluster is stored position-only
574 break;
575 }
576 refOut[iRow] = true;
577 mCache[iRow].y[ExtOut] = trkWork.getY();
578 mCache[iRow].z[ExtOut] = trkWork.getZ();
579 mCache[iRow].sy2[ExtOut] = trkWork.getSigmaY2();
580 mCache[iRow].szy[ExtOut] = trkWork.getSigmaZY();
581 mCache[iRow].sz2[ExtOut] = trkWork.getSigmaZ2();
582 mCache[iRow].snp[ExtOut] = trkWork.getSnp();
583 // printf("Track alpha at row %i: %.2f, Y(%.2f), Z(%.2f)\n", iRow, trkWork.getAlpha(), trkWork.getY(), trkWork.getZ());
584 }
585
586 // start from outermost cluster with outer refit and back propagation
587 if (!allLost && gidTable[GTrackID::TOF].isIndexSet()) {
588 LOG(debug) << "TOF point available";
589 const auto& clTOF = mRecoCont->getTOFClusters()[gidTable[GTrackID::TOF]];
590 if (mDumpTrackPoints) {
591 (*trackDataExtended).clsTOF = clTOF;
592 (*trackDataExtended).matchTOF = mRecoCont->getTOFMatch(mGIDs[iSeed]);
593 }
594 const int clTOFSec = clTOF.getCount();
595 const float clTOFAlpha = o2::math_utils::sector2Angle(clTOFSec);
596 float clTOFxyz[3] = {clTOF.getX(), clTOF.getY(), clTOF.getZ()};
597 if (!clTOF.isInNominalSector()) {
598 o2::tof::Geo::alignedToNominalSector(clTOFxyz, clTOFSec); // go from the aligned to nominal sector frame
599 }
600 std::array<float, 2> clTOFYZ{clTOFxyz[1], clTOFxyz[2]};
601 std::array<float, 3> clTOFCov{mParams->sigYZ2TOF, 0.f, mParams->sigYZ2TOF}; // assume no correlation between y and z and equal cluster error sigma^2 = (3cm)^2 / 12
602 // TODO: check if reset of covariance matrix is needed here (or, in case TOF point is not available at outermost TRD layer)
603 if (!trkWork.rotate(clTOFAlpha) || !propagator->PropagateToXBxByBz(trkWork, clTOFxyz[0], mParams->maxSnp, mParams->maxStep, mMatCorr) || !trkWork.update(clTOFYZ, clTOFCov)) {
604 LOG(debug) << "Failed to rotate/propagate/update the extrapolated ITS track at the TOF cluster";
605 if (!mParams->keepClustersOnPropFail) {
606 mNRejProp++;
607 return;
608 }
609 allLost = true;
610 }
611 }
612 if (!allLost && gidTable[GTrackID::TRD].isIndexSet()) {
613 LOG(debug) << "TRD available";
614 const auto& trkTRD = mRecoCont->getITSTPCTRDTrack<o2::trd::TrackTRD>(gidTable[GTrackID::ITSTPCTRD]);
615 if (mDumpTrackPoints) {
616 (*trackDataExtended).trkTRD = trkTRD;
617 }
618 for (int iLayer = o2::trd::constants::NLAYER - 1; iLayer >= 0; --iLayer) {
619 std::array<float, 2> trkltTRDYZ{};
620 std::array<float, 3> trkltTRDCov{};
621 int res = processTRDLayer(trkTRD, iLayer, trkWork, &trkltTRDYZ, &trkltTRDCov);
622 if (res == -1) { // no TRD tracklet in this layer
623 continue;
624 }
625 if (res < -1 || !trkWork.update(trkltTRDYZ, trkltTRDCov)) { // failed to reach this layer or to update
626 LOG(debug) << "Failed to reach or update the track at TRD layer " << iLayer;
627 if (!mParams->keepClustersOnPropFail) {
628 if (res >= -1) {
629 mNRejProp++; // unchanged: only the update failure was counted
630 }
631 return;
632 }
633 allLost = true;
634 break;
635 }
636 }
637 }
638
639 if (mDumpTrackPoints) {
640 (*trackDataExtended).trkOuter = trkWork;
641 }
642 auto trkOuter = trkWork; // outer param
643
644 // go back through the TPC and store updated track positions
645 bool outerParamStored = false;
646 for (int iRow = param::NPadRows; !allLost && iRow--;) {
647 if (!mCache[iRow].clAvailable) {
648 continue;
649 }
650 if (mProcessSeeds && !outerParamStored) {
651 // for debug purposes we store the track parameters
652 // of the refitted ITS-(TRD)-(TOF) track at the
653 // outermose TPC cluster if we are processing all seeds
654 // i.e. if we in any case also process the ITS-TPC only
655 // part of the same track
656 trackData.par = trkWork;
657 outerParamStored = true;
658 }
659 if (!trkWork.rotate(mCache[iRow].clAngle) || !propagator->PropagateToXBxByBz(trkWork, param::RowX[iRow], mParams->maxSnp, mParams->maxStep, mMatCorr)) {
660 LOG(debug) << "Failed on back propagation";
661 if (!mParams->keepClustersOnPropFail) {
662 mNRejProp++;
663 return;
664 }
665 break; // this row and all inner ones have no inward reference: stored position-only
666 }
667 refIn[iRow] = true;
668 mCache[iRow].y[ExtIn] = trkWork.getY();
669 mCache[iRow].z[ExtIn] = trkWork.getZ();
670 mCache[iRow].sy2[ExtIn] = trkWork.getSigmaY2();
671 mCache[iRow].szy[ExtIn] = trkWork.getSigmaZY();
672 mCache[iRow].sz2[ExtIn] = trkWork.getSigmaZ2();
673 mCache[iRow].snp[ExtIn] = trkWork.getSnp();
674 }
675
676 // calculate weighted mean at each pad row (assume for now y and z are uncorrelated) and store residuals to TPC clusters
677 unsigned short deltaRow = 0;
678 for (int iRow = 0; iRow < param::NPadRows; ++iRow) {
679 if (!mCache[iRow].clAvailable) {
680 ++deltaRow;
681 continue;
682 }
683 if (!refOut[iRow] || !refIn[iRow]) { // keepClustersOnPropFail only: no reference at this row
684 posOnly.push_back({mCache[iRow].clY, mCache[iRow].clZ, mCache[iRow].clSec, (unsigned char)iRow, mCache[iRow].clFlags});
685 ++deltaRow;
686 continue;
687 }
688 float wTotY = 1.f / mCache[iRow].sy2[ExtOut] + 1.f / mCache[iRow].sy2[ExtIn];
689 float wTotZ = 1.f / mCache[iRow].sz2[ExtOut] + 1.f / mCache[iRow].sz2[ExtIn];
690 mCache[iRow].y[Int] = (mCache[iRow].y[ExtOut] / mCache[iRow].sy2[ExtOut] + mCache[iRow].y[ExtIn] / mCache[iRow].sy2[ExtIn]) / wTotY;
691 mCache[iRow].z[Int] = (mCache[iRow].z[ExtOut] / mCache[iRow].sz2[ExtOut] + mCache[iRow].z[ExtIn] / mCache[iRow].sz2[ExtIn]) / wTotZ;
692
693 // simple average w/o weighting for angle
694 mCache[iRow].snp[Int] = (mCache[iRow].snp[ExtOut] + mCache[iRow].snp[ExtIn]) / 2.f;
695
696 const auto dY = mCache[iRow].clY - mCache[iRow].y[Int];
697 const auto dZ = mCache[iRow].clZ - mCache[iRow].z[Int];
698 const auto y = mCache[iRow].y[Int];
699 const auto z = mCache[iRow].z[Int];
700 const auto snp = mCache[iRow].snp[Int];
701 const auto sec = mCache[iRow].clSec;
702 clusterResiduals.emplace_back(dY, dZ, y, z, snp, sec, deltaRow, mCache[iRow].clFlags);
703
704 deltaRow = 1;
705 }
706 trackData.chi2TRD = gidTable[GTrackID::TRD].isIndexSet() ? mRecoCont->getITSTPCTRDTrack<o2::trd::TrackTRD>(gidTable[GTrackID::ITSTPCTRD]).getChi2() : 0;
707 trackData.chi2TPC = trkTPC.getChi2();
708 trackData.chi2ITS = trkITS.getChi2();
709 trackData.nClsTPC = trkTPC.getNClusterReferences();
710 trackData.nClsITS = trkITS.getNumberOfClusters();
711 trackData.nTrkltsTRD = gidTable[GTrackID::TRD].isIndexSet() ? mRecoCont->getITSTPCTRDTrack<o2::trd::TrackTRD>(gidTable[GTrackID::ITSTPCTRD]).getNtracklets() : 0;
712
713 double t0forTOF = 0.; // to be set if TOF is matched
714 float t0forTOFwithinBC = 0.f;
715 float t0forTOFres = 9999.f;
716
717 if (gidTable[GTrackID::TOF].isIndexSet()) {
718 const auto& tofMatch = mRecoCont->getTOFMatch(mGIDs[iSeed]);
719 ULong64_t bclongtof = (tofMatch.getSignal() - 10000) * o2::tof::Geo::BC_TIME_INPS_INV;
720 t0forTOF = tofMatch.getFT0Best(); // setting t0 for TOF
721 t0forTOFwithinBC = t0forTOF - bclongtof * o2::tof::Geo::BC_TIME_INPS;
722 t0forTOFres = tofMatch.getFT0BestRes();
723 trackData.deltaTOF = tofMatch.getSignal() - t0forTOF - tofMatch.getLTIntegralOut().getTOF(trkTPC.getPID().getID());
724 trackData.clAvailTOF = uint16_t(t0forTOFres);
725 } else {
726 trackData.clAvailTOF = 0;
727 }
728 trackData.dEdxTPC = trkTPC.getdEdx().dEdxTotTPC;
729
730 mTrackValidation.clear(); // for refitted track parameters and flagging rejected clusters
731
732 bool stored = false;
733 // keepClustersOnPropFail: a track without any interpolated residual has nothing to validate
734 trackData.filterFlag = mParams->skipOutlierFiltering ? -1 : ((mParams->keepClustersOnPropFail && clusterResiduals.empty()) ? int8_t(0x1) : validateTrack(trackData, mTrackValidation, clusterResiduals, true));
735 if (trackData.filterFlag <= 0 || mParams->writeUnfiltered) {
736 int nClValidated = 0;
737 int iRow = 0;
738 // keepClustersOnPropFail: store the position-only clusters in row order between the residuals
739 size_t iPosOnly = 0;
740 auto flushPosOnly = [&](int rowLimit) {
741 for (; iPosOnly < posOnly.size() && posOnly[iPosOnly].row < rowLimit; ++iPosOnly) {
742 const auto& pc = posOnly[iPosOnly];
743 if (std::abs(pc.y) < param::MaxY && std::abs(pc.z) < param::MaxZ) {
744 mClRes.emplace_back(0.f, 0.f, 0.f, pc.y, pc.z, pc.row, pc.sec, pc.flags, false);
745 mClRes.back().tgSlp = UnbinnedResid::TgSlpPositionOnly;
746 mDetInfoRes.emplace_back().setTPC(mCacheDEDX[pc.row].first, mCacheDEDX[pc.row].second); // qtot, qmax
747 ++nClValidated;
748 ++mNPosOnlyClusters;
749 } else {
750 ++mRejectedResiduals;
751 }
752 }
753 };
754 for (unsigned int iCl = 0; iCl < clusterResiduals.size(); ++iCl) {
755 iRow += clusterResiduals[iCl].dRow;
756 flushPosOnly(iRow);
757 const auto rej = trackData.filterFlag < 0 ? false : mTrackValidation.points[iCl].flagRej;
758 if (rej && !mParams->keepRejectedResiduals) { // skip masked cluster residual
759 continue;
760 }
761 const float tgPhi = clusterResiduals[iCl].snp / std::sqrt((1.f - clusterResiduals[iCl].snp) * (1.f + clusterResiduals[iCl].snp));
762 const auto dy = clusterResiduals[iCl].dy;
763 const auto dz = clusterResiduals[iCl].dz;
764 const auto y = clusterResiduals[iCl].y;
765 const auto z = clusterResiduals[iCl].z;
766 const auto sec = clusterResiduals[iCl].sec;
767 const short flags = clusterResiduals[iCl].flags;
768 // scdcalib.clampTgSlp: keep a cluster whose |tan(phi)| exceeds the packing range, with tgSlp saturated, instead of dropping it
769 const bool tgPhiOK = (std::abs(tgPhi) < param::MaxTgSlp) || mParams->clampTgSlp;
770 const float tgPhiStore = std::clamp(tgPhi, -param::MaxTgSlp, param::MaxTgSlp);
771 if ((std::abs(dy) < param::MaxResid) && (std::abs(dz) < param::MaxResid) && (std::abs(y) < param::MaxY) && (std::abs(z) < param::MaxZ) && tgPhiOK) {
772 mClRes.emplace_back(dy, dz, tgPhiStore, y, z, iRow, sec, flags, rej);
773 mDetInfoRes.emplace_back().setTPC(mCacheDEDX[iRow].first, mCacheDEDX[iRow].second); // qtot, qmax
774 ++nClValidated;
775 } else {
776 ++mRejectedResiduals;
777 }
778 }
779 flushPosOnly(constants::MAXGLOBALPADROW);
780 if (!posOnly.empty()) {
781 ++mNPosOnlyTracks;
782 }
783 trackData.clIdx.setEntries(nClValidated);
784
785 // store multiplicity info
786 for (int ist = 0; ist < NSTACKS; ist++) {
787 int mltBinMin = 0x7ffff, mltBinMax = -1, prevBin = -1;
788 for (int ir = STACKROWS[ist]; ir < STACKROWS[ist + 1]; ir++) {
789 if (multBins[ir] != prevBin && multBins[ir] > 0) { // there is a cluster different from previous one
790 prevBin = multBins[ir];
791 if (multBins[ir] > mltBinMax) {
792 mltBinMax = multBins[ir];
793 }
794 if (multBins[ir] < mltBinMin) {
795 mltBinMin = multBins[ir];
796 }
797 }
798 }
799 if (--mltBinMin >= 0) { // we were offsetting bin IDs by 1!
800 float avMlt = 0;
801 for (int ib = mltBinMin; ib < mltBinMax; ib++) {
802 avMlt += mTPCParam->occupancyMap[ib];
803 }
804 avMlt /= (mltBinMax - mltBinMin);
805 trackData.setMultStack(avMlt, ist);
806 }
807 }
808
809 bool stopPropagation = !mExtDetResid;
810 GTrackID gidTRDUsed{};
811 if (!stopPropagation) {
812 // do we have TRD residuals to add?
813 trkWork = trkOuter;
814 if (!allLost && gidTable[GTrackID::TRD].isIndexSet()) { // allLost: trkOuter is not a valid outer param
815 gidTRDUsed = gidTable[GTrackID::ITSTPCTRD];
816 const auto& trkTRD = mRecoCont->getITSTPCTRDTrack<o2::trd::TrackTRD>(gidTable[GTrackID::ITSTPCTRD]);
817 for (int iLayer = 0; iLayer < o2::trd::constants::NLAYER; iLayer++) {
818 std::array<float, 2> trkltTRDYZ{};
819 int res = processTRDLayer(trkTRD, iLayer, trkWork, &trkltTRDYZ, nullptr, &trackData, &trkl64, &trklCalib);
820 if (res == -1) { // no traklet on this layer
821 continue;
822 }
823 if (res < -1) { // failed to reach this layer
824 stopPropagation = true;
825 break;
826 }
827
828 float tgPhi = trkWork.getSnp() / std::sqrt((1.f - trkWork.getSnp()) * (1.f + trkWork.getSnp()));
829 auto dy = trkltTRDYZ[0] - trkWork.getY();
830 auto dz = trkltTRDYZ[1] - trkWork.getZ();
831 if ((std::abs(dy) < param::MaxResid) && (std::abs(dz) < param::MaxResid) && (std::abs(trkWork.getY()) < param::MaxY) && (std::abs(trkWork.getZ()) < param::MaxZ) && (std::abs(tgPhi) < param::MaxTgSlp)) {
832 mClRes.emplace_back(dy, dz, tgPhi, trkWork.getY(), trkWork.getZ(), 160 + iLayer, o2::math_utils::angle2Sector(trkWork.getAlpha()), (short)res);
833 mDetInfoRes.emplace_back().setTRD(trkl64.getQ0(), trkl64.getQ1(), trkl64.getQ2(), trklCalib.getDy()); // q0,q1,q2,slope
834 trackData.nExtDetResid++;
835 }
836 }
837 }
838
839 // do we have TOF residual to add?
840 while (!allLost && gidTable[GTrackID::TOF].isIndexSet() && !stopPropagation) {
841 const auto& clTOF = mRecoCont->getTOFClusters()[gidTable[GTrackID::TOF]];
842 float clTOFxyz[3] = {clTOF.getX(), clTOF.getY(), clTOF.getZ()};
843 if (!clTOF.isInNominalSector()) {
844 o2::tof::Geo::alignedToNominalSector(clTOFxyz, clTOF.getCount()); // go from the aligned to nominal sector frame
845 }
846 const float clTOFAlpha = o2::math_utils::sector2Angle(clTOF.getCount());
847 if (trkWork.getAlpha() != clTOFAlpha && !trkWork.rotate(clTOFAlpha)) {
848 LOG(debug) << "Failed to rotate into TOF cluster sector frame";
849 stopPropagation = true;
850 break;
851 }
852 if (!propagator->PropagateToXBxByBz(trkWork, clTOFxyz[0], mParams->maxSnp, mParams->maxStep, mMatCorr)) {
853 LOG(debug) << "Failed final propagation to TOF radius";
854 break;
855 }
856
857 float tgPhi = trkWork.getSnp() / std::sqrt((1.f - trkWork.getSnp()) * (1.f + trkWork.getSnp()));
858 auto dy = clTOFxyz[1] - trkWork.getY();
859 auto dz = clTOFxyz[2] - trkWork.getZ();
860 // get seeding track time
861
862 if ((std::abs(dy) < param::MaxResid) && (std::abs(dz) < param::MaxResid) && (std::abs(trkWork.getY()) < param::MaxY) && (std::abs(trkWork.getZ()) < param::MaxZ) && (std::abs(tgPhi) < param::MaxTgSlp)) {
863 mClRes.emplace_back(dy, dz, tgPhi, trkWork.getY(), trkWork.getZ(), 170, clTOF.getCount(), clTOF.getPadInSector());
864 // get seeding track time
865 if (!gidTable[GTrackID::ITSTPC].isIndexSet()) {
866 LOGP(fatal, "ITS-TPC seed index is not set for TOF track");
867 }
868 float tdif = static_cast<float>(clTOF.getTime() - t0forTOF); // time in \mus wrt interaction time0
869 mDetInfoRes.emplace_back().setTOF(tdif * 1e-6);
870 trackData.nExtDetResid++;
871 }
872 break;
873 }
874
875 // add ITS residuals
876 while (!stopPropagation) {
877 auto& trkWorkITS = trkInner; // this is ITS outer param
878 auto nCl = trkITS.getNumberOfClusters();
879 auto clEntry = trkITS.getFirstClusterEntry();
881 for (int iCl = 0; iCl < nCl; iCl++) { // clusters are stored from outer to inner layers
882 const auto& cls = mITSClustersArray[mITSTrackClusIdx[clEntry + iCl]];
883 int chip = cls.getSensorID();
884 float chipX, chipAlpha;
885 geom->getSensorXAlphaRefPlane(cls.getSensorID(), chipX, chipAlpha);
886 if (!trkWorkITS.rotate(chipAlpha) || !propagator->PropagateToXBxByBz(trkWorkITS, chipX, mParams->maxSnp, mParams->maxStep, mMatCorr)) {
887 LOGP(debug, "Failed final propagation to ITS X={} alpha={}", chipX, chipAlpha);
888 stopPropagation = true;
889 break;
890 }
891 float tgPhi = trkWorkITS.getSnp() / std::sqrt((1.f - trkWorkITS.getSnp()) * (1.f + trkWorkITS.getSnp()));
892 auto dy = cls.getY() - trkWorkITS.getY();
893 auto dz = cls.getZ() - trkWorkITS.getZ();
894 if ((std::abs(dy) < param::MaxResid) && (std::abs(dz) < param::MaxResid) && (std::abs(trkWorkITS.getY()) < param::MaxY) && (std::abs(trkWorkITS.getZ()) < param::MaxZ) && (std::abs(tgPhi) < param::MaxTgSlp)) {
895 mClRes.emplace_back(dy, dz, tgPhi, trkWorkITS.getY(), trkWorkITS.getZ(), 180 + geom->getLayer(cls.getSensorID()), -1, cls.getSensorID());
896 mDetInfoRes.emplace_back(); // empty placeholder
897 trackData.nExtDetResid++;
898 }
899 }
900 if (!stopPropagation) { // add residual to PV
901 const auto& pv = mRecoCont->getPrimaryVertices()[mTrackPVID[iSeed]];
902 o2::math_utils::Point3D<float> vtx{pv.getX(), pv.getY(), pv.getZ()};
903 if (!propagator->propagateToDCA(vtx, trkWorkITS, mBz, mParams->maxStep, mMatCorr)) {
904 LOGP(debug, "Failed propagation to DCA to PV ({} {} {}), {}", pv.getX(), pv.getY(), pv.getZ(), trkWorkITS.asString());
905 stopPropagation = true;
906 break;
907 }
908 // rotate PV to the track frame
909 float sn, cs, alpha = trkWorkITS.getAlpha();
910 math_utils::detail::bringToPMPi(alpha);
911 math_utils::detail::sincos<float>(alpha, sn, cs);
912 float xv = vtx.X() * cs + vtx.Y() * sn, yv = -vtx.X() * sn + vtx.Y() * cs, zv = vtx.Z();
913 auto dy = yv - trkWorkITS.getY();
914 auto dz = zv - trkWorkITS.getZ();
915 if ((std::abs(dy) < param::MaxResid) && (std::abs(dz) < param::MaxResid) && (std::abs(trkWorkITS.getY()) < param::MaxY) && (std::abs(trkWorkITS.getZ()) < param::MaxZ) && std::abs(xv) < param::MaxVtxX) {
916 short compXV = static_cast<short>(xv * 0x7fff / param::MaxVtxX);
917 mClRes.emplace_back(dy, dz, alpha / TMath::Pi(), trkWorkITS.getY(), trkWorkITS.getZ(), 190, -1, compXV);
918 if (!gidTable[GTrackID::ITSTPC].isIndexSet()) {
919 LOGP(fatal, "ITS-TPC seed index is not set for TOF track");
920 }
921 float tdif = pv.getTimeStamp().getTimeStamp() - mRecoCont->getTPCITSTrack(gidTable[GTrackID::ITSTPC]).getTimeMUS().getTimeStamp();
922 mDetInfoRes.emplace_back().setPV(tdif); // time in \mus wrt seeding ITS-TPC track
923 trackData.nExtDetResid++;
924 }
925 }
926 break;
927 }
928 }
929
930 mGIDsSuccess.push_back(mGIDs[iSeed]);
931 mTRDGIDsSuccess.push_back(gidTRDUsed);
932 mTrackDataCompact.emplace_back(trackData.clIdx.getFirstEntry(), trackData.multStack, nClValidated, mGIDs[iSeed].getSource(), trackData.nExtDetResid, trackData.filterFlag);
933 mTrackData.push_back(std::move(trackData));
934 stored = true;
935 if (mDumpTrackPoints) {
936 (*trackDataExtended).clIdx.setEntries(nClValidated);
937 (*trackDataExtended).nExtDetResid = trackData.nExtDetResid;
938 (*trackDataExtended).filterFlag = trackData.filterFlag;
939 mTrackDataExtended.push_back(std::move(*trackDataExtended));
940 }
941 }
942 if (mParams->writeValidationData && trackData.filterFlag >= 0 && mDBGOut) {
943 (*mDBGOut) << "valdata" << "params=" << mTrackValidation << "trackData=" << (stored ? mTrackData.back() : trackData) << "\n";
944 }
945}
946
948 std::array<float, 2>* trkltTRDYZ, std::array<float, 3>* trkltTRDCov, TrackData* trkData,
950{
951 // return chamber ID (0:539) in case of successful processing, -1 if there is no TRD tracklet at given layer, -2 if processing failed
952 int trkltIdx = trkTRD.getTrackletIndex(iLayer);
953 if (trkltIdx < 0) {
954 return -1; // no TRD tracklet in this layer
955 }
956 const auto& trdSP = mRecoCont->getTRDCalibratedTracklets()[trkltIdx];
957 const auto& trdTrklt = mRecoCont->getTRDTracklets()[trkltIdx];
958 auto trkltDet = trdTrklt.getDetector();
959 auto trkltSec = trkltDet / (o2::trd::constants::NLAYER * o2::trd::constants::NSTACK);
960 if (trkltSec != o2::math_utils::angle2Sector(trkWork.getAlpha())) {
961 if (!trkWork.rotate(o2::math_utils::sector2Angle(trkltSec))) {
962 LOG(debug) << "Track could not be rotated in TRD tracklet coordinate system in layer " << iLayer;
963 return -2;
964 }
965 }
966 if (!o2::base::Propagator::Instance()->PropagateToXBxByBz(trkWork, trdSP.getX(), mParams->maxSnp, mParams->maxStep, mMatCorr)) {
967 LOG(debug) << "Failed propagation to TRD layer " << iLayer;
968 return -2;
969 }
970 if (trkltTRDYZ) {
971 const auto* pad = mGeoTRD->getPadPlane(trkltDet);
972 float tilt = tan(TMath::DegToRad() * pad->getTiltingAngle()); // tilt is signed! and returned in degrees
973 float tiltCorrUp = tilt * (trdSP.getZ() - trkWork.getZ());
974 float zPosCorrUp = trdSP.getZ() + mRecoParam.getZCorrCoeffNRC() * trkWork.getTgl(); // maybe Z can be corrected on avarage already by the tracklet transformer?
975 float padLength = pad->getRowSize(trdTrklt.getPadRow());
976 if (!((trkWork.getSigmaZ2() < (padLength * padLength / 12.f)) && (std::abs(trdSP.getZ() - trkWork.getZ()) < padLength))) {
977 tiltCorrUp = 0.f;
978 }
979 (*trkltTRDYZ)[0] = trdSP.getY() - tiltCorrUp;
980 (*trkltTRDYZ)[1] = zPosCorrUp;
981 if (trkltTRDCov) {
982 mRecoParam.recalcTrkltCov(tilt, trkWork.getSnp(), pad->getRowSize(trdTrklt.getPadRow()), *trkltTRDCov);
983 }
984 }
985 if (trkData) {
986 auto slope = trdSP.getDy();
987 if (std::abs(slope) < param::MaxTRDSlope) {
988 trkData->TRDTrkltSlope[iLayer] = slope * 0x7fff / param::MaxTRDSlope;
989 }
990 }
991 if (trk64) {
992 *trk64 = trdTrklt;
993 }
994 if (trkCalib) {
995 *trkCalib = trdSP;
996 }
997 return trkltDet;
998}
999
1001{
1002 // extrapolate ITS-only track through TPC and store residuals to TPC clusters in the output vectors
1003 LOGP(debug, "Starting track extrapolation for GID {}", mGIDs[iSeed].asString());
1004 const auto& gidTable = mGIDtables[iSeed];
1005 TrackData trackData;
1006 o2::trd::Tracklet64 trkl64;
1008 std::unique_ptr<TrackDataExtended> trackDataExtended;
1009 std::vector<TPCClusterResiduals> clusterResiduals;
1010 trackData.clIdx.setFirstEntry(mClRes.size());
1011 const auto& trkITS = mRecoCont->getITSTrack(gidTable[GTrackID::ITS]);
1012 const auto& trkTPC = mRecoCont->getTPCTrack(gidTable[GTrackID::TPC]);
1013 if (mDumpTrackPoints) {
1014 trackDataExtended = std::make_unique<TrackDataExtended>();
1015 (*trackDataExtended).gid = mGIDs[iSeed];
1016 (*trackDataExtended).clIdx.setFirstEntry(mClRes.size());
1017 (*trackDataExtended).trkITS = trkITS;
1018 (*trackDataExtended).trkTPC = trkTPC;
1019 auto nCl = trkITS.getNumberOfClusters();
1020 auto clEntry = trkITS.getFirstClusterEntry();
1021 for (int iCl = nCl - 1; iCl >= 0; iCl--) { // clusters are stored from outer to inner layers
1022 const auto& clsITS = mITSClustersArray[mITSTrackClusIdx[clEntry + iCl]];
1023 (*trackDataExtended).clsITS.push_back(clsITS);
1024 }
1025 }
1026 if (mParams->refitITS && !refITSTrack(gidTable[GTrackID::ITS], iSeed)) {
1027 mNRejRefit++;
1028 return;
1029 }
1030 trackData.gid = mGIDs[iSeed];
1031 trackData.par = mSeeds[iSeed];
1032
1033 auto trkWork = mSeeds[iSeed];
1034 float clusterTimeBinOffset = mTrackTimes[iSeed] / mTPCTimeBinMUS;
1035 auto propagator = o2::base::Propagator::Instance();
1036 unsigned short rowPrev = 0; // used to calculate dRow of two consecutive cluster residuals
1037 unsigned short nMeasurements = 0;
1038 uint8_t clRowPrev = constants::MAXGLOBALPADROW; // used to identify and skip split clusters on the same pad row
1039 std::array<std::pair<uint16_t, uint16_t>, constants::MAXGLOBALPADROW> mCacheDEDX{};
1040 std::array<short, constants::MAXGLOBALPADROW> multBins{};
1041 bool refLost = false; // keepClustersOnPropFail: the ITS extrapolation failed at an earlier cluster
1042 std::vector<PositionOnlyCluster> posOnly; // keepClustersOnPropFail: clusters after the failure, rows ascending
1043 for (int iCl = trkTPC.getNClusterReferences(); iCl--;) {
1044 uint8_t sector, row;
1045 uint32_t clusterIndexInRow;
1046 trkTPC.getClusterReference(mTPCTrackClusIdx, iCl, sector, row, clusterIndexInRow);
1047 unsigned int absoluteIndex = mTPCClusterIdxStruct->clusterOffset[sector][row] + clusterIndexInRow;
1048 const auto& cl = mTPCClusterIdxStruct->clustersLinear[absoluteIndex];
1049 if (clRowPrev == row) {
1050 // if there are split clusters we only take the first one on the pad row
1051 continue;
1052 } else if (clRowPrev < constants::MAXGLOBALPADROW && clRowPrev > row) {
1053 // we seem to be looping, abort this track
1054 LOGP(debug, "TPC track with pT={} GeV and {} clusters has cluster {} on row {} while the previous cluster was on row {}",
1055 mSeeds[iSeed].getPt(), trkTPC.getNClusterReferences(), iCl, row, clRowPrev);
1056 mNRejLoop++;
1057 return;
1058 } else {
1059 // this is the first cluster we see on this pad row
1060 clRowPrev = row;
1061 }
1062 float x = 0, y = 0, z = 0;
1063 mFastTransform->TransformIdeal(sector, row, cl.getPad(), cl.getTime(), x, y, z, clusterTimeBinOffset);
1064 unsigned char flags = cl.getFlags();
1065 if (mTPCShClassMap[absoluteIndex] & o2::gpu::GPUTPCGMMergedTrackHit::flagShared) {
1067 }
1068 if (!refLost && !(trkWork.rotate(o2::math_utils::sector2Angle(sector)) && propagator->PropagateToXBxByBz(trkWork, x, mParams->maxSnp, mParams->maxStep, mMatCorr))) {
1069 if (!mParams->keepClustersOnPropFail) {
1070 mNRejProp++;
1071 return;
1072 }
1073 refLost = true; // scdcalib.keepClustersOnPropFail: this and all further clusters are stored position-only
1074 }
1075 mCacheDEDX[row].first = cl.getQtot();
1076 mCacheDEDX[row].second = cl.getQmax();
1077 if (refLost) {
1078 posOnly.push_back({y, z, sector, row, flags});
1079 } else {
1080 const auto dY = y - trkWork.getY();
1081 const auto dZ = z - trkWork.getZ();
1082 const auto ty = trkWork.getY();
1083 const auto tz = trkWork.getZ();
1084 const auto snp = trkWork.getSnp();
1085 const auto sec = sector;
1086 clusterResiduals.emplace_back(dY, dZ, ty, tz, snp, sec, row - rowPrev, flags);
1087 rowPrev = row;
1088 }
1089 int imb = int(cl.getTime() * mNTPCOccBinLengthInv);
1090 if (imb < mTPCParam->occupancyMapSize) {
1091 multBins[row] = 1 + std::max(0, imb);
1092 }
1093 ++nMeasurements;
1094 }
1095
1096 mTrackValidation.clear(); // for refitted track parameters and flagging rejected clusters
1097 if (clusterResiduals.size() > constants::MAXGLOBALPADROW) {
1098 LOGP(warn, "Extrapolated ITS-TPC track and found more residuals than possible ({})", clusterResiduals.size());
1099 mNRejLoop++;
1100 return;
1101 }
1102
1103 trackData.chi2TPC = trkTPC.getChi2();
1104 trackData.chi2ITS = trkITS.getChi2();
1105 trackData.nClsTPC = trkTPC.getNClusterReferences();
1106 trackData.nClsITS = trkITS.getNumberOfClusters();
1107 trackData.clIdx.setEntries(nMeasurements);
1108 trackData.dEdxTPC = trkTPC.getdEdx().dEdxTotTPC;
1109 if (mDumpTrackPoints) {
1110 (*trackDataExtended).trkOuter = trkWork;
1111 }
1112
1113 bool stored = false;
1114 // keepClustersOnPropFail: a track that lost its reference before the first cluster has no residual to validate
1115 trackData.filterFlag = mParams->skipOutlierFiltering ? -1 : ((mParams->keepClustersOnPropFail && clusterResiduals.empty()) ? int8_t(0x1) : validateTrack(trackData, mTrackValidation, clusterResiduals, false));
1116 if (trackData.filterFlag <= 0 || mParams->writeUnfiltered) {
1117 int nClValidated = 0, iRow = 0;
1118 unsigned int iCl = 0;
1119 // keepClustersOnPropFail: store the position-only clusters in row order between the residuals
1120 size_t iPosOnly = 0;
1121 auto flushPosOnly = [&](int rowLimit) {
1122 for (; iPosOnly < posOnly.size() && posOnly[iPosOnly].row < rowLimit; ++iPosOnly) {
1123 const auto& pc = posOnly[iPosOnly];
1124 if (std::abs(pc.y) < param::MaxY && std::abs(pc.z) < param::MaxZ) {
1125 mClRes.emplace_back(0.f, 0.f, 0.f, pc.y, pc.z, pc.row, pc.sec, pc.flags, false);
1126 mClRes.back().tgSlp = UnbinnedResid::TgSlpPositionOnly;
1127 mDetInfoRes.emplace_back().setTPC(mCacheDEDX[pc.row].first, mCacheDEDX[pc.row].second); // qtot, qmax
1128 ++nClValidated;
1129 ++mNPosOnlyClusters;
1130 } else {
1131 ++mRejectedResiduals;
1132 }
1133 }
1134 };
1135 for (iCl = 0; iCl < clusterResiduals.size(); ++iCl) {
1136 iRow += clusterResiduals[iCl].dRow;
1137 flushPosOnly(iRow);
1138 if (iRow >= param::NPadRows) { // RS why do we need this?
1139 continue;
1140 }
1141 const auto rej = trackData.filterFlag < 0 ? false : mTrackValidation.points[iCl].flagRej;
1142 if (rej && !mParams->keepRejectedResiduals) { // skip masked cluster residual
1143 continue;
1144 }
1145 const float tgPhi = clusterResiduals[iCl].snp / std::sqrt((1.f - clusterResiduals[iCl].snp) * (1.f + clusterResiduals[iCl].snp));
1146 const auto dy = clusterResiduals[iCl].dy;
1147 const auto dz = clusterResiduals[iCl].dz;
1148 const auto y = clusterResiduals[iCl].y;
1149 const auto z = clusterResiduals[iCl].z;
1150 const short flags = clusterResiduals[iCl].flags;
1151 // scdcalib.clampTgSlp: keep a cluster whose |tan(phi)| exceeds the packing range, with tgSlp saturated, instead of dropping it
1152 const bool tgPhiOK = (std::abs(tgPhi) < param::MaxTgSlp) || mParams->clampTgSlp;
1153 const float tgPhiStore = std::clamp(tgPhi, -param::MaxTgSlp, param::MaxTgSlp);
1154 if ((std::abs(dy) < param::MaxResid) && (std::abs(dz) < param::MaxResid) && (std::abs(y) < param::MaxY) && (std::abs(z) < param::MaxZ) && tgPhiOK) {
1155 mClRes.emplace_back(dy, dz, tgPhiStore, y, z, iRow, clusterResiduals[iCl].sec, flags, rej);
1156 mDetInfoRes.emplace_back().setTPC(mCacheDEDX[iRow].first, mCacheDEDX[iRow].second); // qtot, qmax
1157 ++nClValidated;
1158 } else {
1159 ++mRejectedResiduals;
1160 }
1161 }
1162 flushPosOnly(constants::MAXGLOBALPADROW);
1163 if (!posOnly.empty()) {
1164 ++mNPosOnlyTracks;
1165 }
1166 trackData.clIdx.setEntries(nClValidated);
1167
1168 // store multiplicity info
1169 for (int ist = 0; ist < NSTACKS; ist++) {
1170 int mltBinMin = 0x7ffff, mltBinMax = -1, prevBin = -1;
1171 for (int ir = STACKROWS[ist]; ir < STACKROWS[ist + 1]; ir++) {
1172 if (multBins[ir] != prevBin && multBins[ir] > 0) { // there is a cluster
1173 prevBin = multBins[ir];
1174 if (multBins[ir] > mltBinMax) {
1175 mltBinMax = multBins[ir];
1176 }
1177 if (multBins[ir] < mltBinMin) {
1178 mltBinMin = multBins[ir];
1179 }
1180 }
1181 }
1182 if (--mltBinMin >= 0) { // we were offsetting bin IDs by 1!
1183 float avMlt = 0;
1184 for (int ib = mltBinMin; ib < mltBinMax; ib++) {
1185 avMlt += mTPCParam->occupancyMap[ib];
1186 }
1187 avMlt /= (mltBinMax - mltBinMin);
1188 trackData.setMultStack(avMlt, ist);
1189 }
1190 }
1191
1192 bool stopPropagation = !mExtDetResid;
1193 GTrackID gidTRDUsed{};
1194 if (!stopPropagation) {
1195 // do we have TRD residuals to add?
1196 int iSeedFull = mParentID[iSeed] == -1 ? iSeed : mParentID[iSeed];
1197 auto gidFull = mGIDs[iSeedFull];
1198 const auto& gidTableFull = mGIDtables[iSeedFull];
1199 if (!refLost && gidTableFull[GTrackID::TRD].isIndexSet()) { // refLost: trkWork did not reach the TPC outer end
1200 gidTRDUsed = gidTableFull[GTrackID::ITSTPCTRD];
1201 const auto& trkTRD = mRecoCont->getITSTPCTRDTrack<o2::trd::TrackTRD>(gidTableFull[GTrackID::ITSTPCTRD]);
1202 trackData.nTrkltsTRD = trkTRD.getNtracklets();
1203 trackData.chi2TRD = trkTRD.getChi2();
1204 for (int iLayer = 0; iLayer < o2::trd::constants::NLAYER; iLayer++) {
1205 std::array<float, 2> trkltTRDYZ{};
1206 int res = processTRDLayer(trkTRD, iLayer, trkWork, &trkltTRDYZ, nullptr, &trackData, &trkl64, &trklCalib);
1207 if (res == -1) { // no traklet on this layer
1208 continue;
1209 }
1210 if (res < -1) { // failed to reach this layer
1211 stopPropagation = true;
1212 break;
1213 }
1214
1215 float tgPhi = trkWork.getSnp() / std::sqrt((1.f - trkWork.getSnp()) * (1.f + trkWork.getSnp()));
1216 auto dy = trkltTRDYZ[0] - trkWork.getY();
1217 auto dz = trkltTRDYZ[1] - trkWork.getZ();
1218 const auto sec = clusterResiduals[iCl].sec;
1219 if ((std::abs(dy) < param::MaxResid) && (std::abs(dz) < param::MaxResid) && (std::abs(trkWork.getY()) < param::MaxY) && (std::abs(trkWork.getZ()) < param::MaxZ) && (std::abs(tgPhi) < param::MaxTgSlp)) {
1220 mClRes.emplace_back(dy, dz, tgPhi, trkWork.getY(), trkWork.getZ(), 160 + iLayer, o2::math_utils::angle2Sector(trkWork.getAlpha()), (short)res);
1221 mDetInfoRes.emplace_back().setTRD(trkl64.getQ0(), trkl64.getQ1(), trkl64.getQ2(), trklCalib.getDy()); // q0,q1,q2,slope
1222 trackData.nExtDetResid++;
1223 }
1224 }
1225 }
1226
1227 // do we have TOF residual to add?
1228 trackData.clAvailTOF = 0;
1229 while (!refLost && gidTableFull[GTrackID::TOF].isIndexSet() && !stopPropagation) {
1230 const auto& tofMatch = mRecoCont->getTOFMatch(gidFull);
1231 ULong64_t bclongtof = (tofMatch.getSignal() - 10000) * o2::tof::Geo::BC_TIME_INPS_INV;
1232 double t0forTOF = tofMatch.getFT0Best(); // setting t0 for TOF
1233 float t0forTOFwithinBC = t0forTOF - bclongtof * o2::tof::Geo::BC_TIME_INPS;
1234 float t0forTOFres = tofMatch.getFT0BestRes();
1235 trackData.deltaTOF = tofMatch.getSignal() - t0forTOF - tofMatch.getLTIntegralOut().getTOF(trkTPC.getPID().getID());
1236 trackData.clAvailTOF = uint16_t(t0forTOFres);
1237 const auto& clTOF = mRecoCont->getTOFClusters()[gidTableFull[GTrackID::TOF]];
1238 const float clTOFAlpha = o2::math_utils::sector2Angle(clTOF.getCount());
1239 float clTOFxyz[3] = {clTOF.getX(), clTOF.getY(), clTOF.getZ()};
1240 if (!clTOF.isInNominalSector()) {
1241 o2::tof::Geo::alignedToNominalSector(clTOFxyz, clTOF.getCount()); // go from the aligned to nominal sector frame
1242 }
1243 if (trkWork.getAlpha() != clTOFAlpha && !trkWork.rotate(clTOFAlpha)) {
1244 LOG(debug) << "Failed to rotate into TOF cluster sector frame";
1245 stopPropagation = true;
1246 break;
1247 }
1248 if (!propagator->PropagateToXBxByBz(trkWork, clTOFxyz[0], mParams->maxSnp, mParams->maxStep, mMatCorr)) {
1249 LOG(debug) << "Failed final propagation to TOF radius";
1250 break;
1251 }
1252
1253 float tgPhi = trkWork.getSnp() / std::sqrt((1.f - trkWork.getSnp()) * (1.f + trkWork.getSnp()));
1254 auto dy = clTOFxyz[1] - trkWork.getY();
1255 auto dz = clTOFxyz[2] - trkWork.getZ();
1256 if ((std::abs(dy) < param::MaxResid) && (std::abs(dz) < param::MaxResid) && (std::abs(trkWork.getY()) < param::MaxY) && (std::abs(trkWork.getZ()) < param::MaxZ) && (std::abs(tgPhi) < param::MaxTgSlp)) {
1257 mClRes.emplace_back(dy, dz, tgPhi, trkWork.getY(), trkWork.getZ(), 170, clTOF.getCount(), clTOF.getPadInSector());
1258 // get seeding track time
1259 if (!gidTableFull[GTrackID::ITSTPC].isIndexSet()) {
1260 LOGP(fatal, "ITS-TPC seed index is not set for TOF track");
1261 }
1262
1263 float tdif = static_cast<float>(clTOF.getTime() - t0forTOF); // time in \mus wrt interaction time0
1264 mDetInfoRes.emplace_back().setTOF(tdif * 1e-6); // time in \mus wrt seeding ITS-TPC track
1265 trackData.nExtDetResid++;
1266 }
1267 break;
1268 }
1269
1270 // add ITS residuals
1271 while (!stopPropagation) {
1272 o2::track::TrackPar trkWorkITS{trackData.par}; // this is ITS outer param
1273 auto nCl = trkITS.getNumberOfClusters();
1274 auto clEntry = trkITS.getFirstClusterEntry();
1275 auto geom = o2::its::GeometryTGeo::Instance();
1276 for (int iCl = 0; iCl < nCl; iCl++) { // clusters are stored from outer to inner layers
1277 const auto& cls = mITSClustersArray[mITSTrackClusIdx[clEntry + iCl]];
1278 int chip = cls.getSensorID();
1279 float chipX, chipAlpha;
1280 geom->getSensorXAlphaRefPlane(cls.getSensorID(), chipX, chipAlpha);
1281 if (!trkWorkITS.rotate(chipAlpha) || !propagator->propagateToX(trkWorkITS, chipX, mBz, mParams->maxSnp, mParams->maxStep, mMatCorr)) {
1282 LOGP(debug, "Failed final propagation to ITS X={} alpha={}", chipX, chipAlpha);
1283 stopPropagation = true;
1284 break;
1285 }
1286 float tgPhi = trkWorkITS.getSnp() / std::sqrt((1.f - trkWorkITS.getSnp()) * (1.f + trkWorkITS.getSnp()));
1287 auto dy = cls.getY() - trkWorkITS.getY();
1288 auto dz = cls.getZ() - trkWorkITS.getZ();
1289 if ((std::abs(dy) < param::MaxResid) && (std::abs(dz) < param::MaxResid) && (std::abs(trkWorkITS.getY()) < param::MaxY) && (std::abs(trkWorkITS.getZ()) < param::MaxZ) && (std::abs(tgPhi) < param::MaxTgSlp)) {
1290 mClRes.emplace_back(dy, dz, tgPhi, trkWorkITS.getY(), trkWorkITS.getZ(), 180 + geom->getLayer(cls.getSensorID()), -1, cls.getSensorID());
1291 mDetInfoRes.emplace_back(); // empty placeholder
1292 trackData.nExtDetResid++;
1293 }
1294 }
1295 if (!stopPropagation) { // add residual to PV
1296 const auto& pv = mRecoCont->getPrimaryVertices()[mTrackPVID[iSeed]];
1297 o2::math_utils::Point3D<float> vtx{pv.getX(), pv.getY(), pv.getZ()};
1298 if (!propagator->propagateToDCA(vtx, trkWorkITS, mBz, mParams->maxStep, mMatCorr)) {
1299 LOGP(debug, "Failed propagation to DCA to PV ({} {} {}), {}", pv.getX(), pv.getY(), pv.getZ(), trkWorkITS.asString());
1300 stopPropagation = true;
1301 break;
1302 }
1303 // rotate PV to the track frame
1304 float sn, cs, alpha = trkWorkITS.getAlpha();
1305 math_utils::detail::bringToPMPi(alpha);
1306 math_utils::detail::sincos<float>(alpha, sn, cs);
1307 float xv = vtx.X() * cs + vtx.Y() * sn, yv = -vtx.X() * sn + vtx.Y() * cs, zv = vtx.Z();
1308 auto dy = yv - trkWorkITS.getY();
1309 auto dz = zv - trkWorkITS.getZ();
1310 if ((std::abs(dy) < param::MaxResid) && (std::abs(dz) < param::MaxResid) && (std::abs(trkWorkITS.getY()) < param::MaxY) && (std::abs(trkWorkITS.getZ()) < param::MaxZ) && std::abs(xv) < param::MaxVtxX) {
1311 short compXV = static_cast<short>(xv * 0x7fff / param::MaxVtxX);
1312 mClRes.emplace_back(dy, dz, alpha / TMath::Pi(), trkWorkITS.getY(), trkWorkITS.getZ(), 190, -1, compXV);
1313 if (!gidTableFull[GTrackID::ITSTPC].isIndexSet()) {
1314 LOGP(fatal, "ITS-TPC seed index is not set for TOF track");
1315 }
1316 float tdif = pv.getTimeStamp().getTimeStamp() - mRecoCont->getTPCITSTrack(gidTableFull[GTrackID::ITSTPC]).getTimeMUS().getTimeStamp();
1317 mDetInfoRes.emplace_back().setPV(tdif); // time in \mus wrt seeding ITS-TPC track
1318 trackData.nExtDetResid++;
1319 }
1320 }
1321 break;
1322 }
1323 }
1324 mTrackData.push_back(std::move(trackData));
1325 stored = true;
1326 mGIDsSuccess.push_back(mGIDs[iSeed]);
1327 mTRDGIDsSuccess.push_back(gidTRDUsed);
1328 mTrackDataCompact.emplace_back(trackData.clIdx.getFirstEntry(), trackData.multStack, nClValidated, mGIDs[iSeed].getSource(), trackData.nExtDetResid, trackData.filterFlag);
1329 if (mDumpTrackPoints) {
1330 (*trackDataExtended).clIdx.setEntries(nClValidated);
1331 (*trackDataExtended).nExtDetResid = trackData.nExtDetResid;
1332 (*trackDataExtended).filterFlag = trackData.filterFlag;
1333 mTrackDataExtended.push_back(std::move(*trackDataExtended));
1334 }
1335 }
1336 if (mParams->writeValidationData && trackData.filterFlag >= 0 && mDBGOut) {
1337 (*mDBGOut) << "valdata" << "params=" << mTrackValidation << "trackData=" << (stored ? mTrackData.back() : trackData) << "\n";
1338 }
1339}
1340
1341int8_t TrackInterpolation::validateTrack(const TrackData& trk, TrackValidationData& params, const std::vector<TPCClusterResiduals>& clsRes, bool interpol)
1342{
1343 int8_t status = 0;
1344 while (true) {
1345 if (clsRes.size() < mParams->minNCl) {
1346 // no enough clusters for this track to be considered
1347 LOG(debug) << "Skipping track with too few clusters: " << clsRes.size();
1348 status |= 0x1;
1349 if (!mParams->keepRejectedResiduals) {
1350 break; // we don't keep de-validated tracks, no need to check further
1351 }
1352 }
1353
1354 bool resHelix = compareToHelix(trk, params, clsRes);
1355 if (!resHelix && interpol) {
1356 LOG(debug) << "Skipping track too far from helix approximation";
1357 status |= 0x1 << 1;
1358 if (!mParams->keepRejectedResiduals) {
1359 break; // we don't keep de-validated tracks, no need to check further
1360 }
1361 }
1362 if (interpol && (std::abs(mBz) > 0.01 && std::abs(params.qpt) > mParams->maxQ2Pt)) {
1363 LOG(debug) << "Skipping track with too high q/pT: " << params.qpt;
1364 status |= 0x1 << 2;
1365 if (!mParams->keepRejectedResiduals) {
1366 break; // we don't keep de-validated tracks, no need to check further
1367 }
1368 }
1369 if (!outlierFiltering(trk, params, clsRes)) {
1370 status |= 0x1 << 3;
1371 if (!mParams->keepRejectedResiduals) {
1372 break; // we don't keep de-validated tracks, no need to check further
1373 }
1374 }
1375 break;
1376 }
1377 return status & 0x7f;
1378}
1379
1380bool TrackInterpolation::compareToHelix(const TrackData& trk, TrackValidationData& params, const std::vector<TPCClusterResiduals>& clsRes)
1381{
1382 float curvature = std::abs(trk.par.getQ2Pt() * mBz * o2::constants::physics::LightSpeedCm2S * 1e-14f);
1383 int secFirst = clsRes[0].sec;
1384 float phiSect = (secFirst + .5f) * o2::constants::math::SectorSpanRad;
1385 float snPhi = sin(phiSect);
1386 float csPhi = cos(phiSect);
1387
1388 int iRow = 0;
1389 int nCl = clsRes.size();
1390 for (unsigned int iP = 0; iP < nCl; ++iP) {
1391 auto& point = params.points.emplace_back();
1392
1393 iRow += clsRes[iP].dRow;
1394 point.yTrk = clsRes[iP].y;
1395 point.sec = clsRes[iP].sec;
1396 if (clsRes[iP].sec != secFirst) {
1397 float phiSectCurrent = (clsRes[iP].sec + .5f) * o2::constants::math::SectorSpanRad;
1398 float cs = cos(phiSectCurrent - phiSect);
1399 float sn = sin(phiSectCurrent - phiSect);
1400 point.xLab = param::RowX[iRow] * cs - point.yTrk * sn;
1401 point.yLab = point.yTrk * cs + param::RowX[iRow] * sn;
1402 } else {
1403 point.xLab = param::RowX[iRow];
1404 point.yLab = point.yTrk;
1405 }
1406 // this is needed only later, but we retrieve it already now to save another loop
1407 point.zTrk = clsRes[iP].z;
1408 point.xTrk = param::RowX[iRow];
1409 point.dy = clsRes[iP].dy;
1410 point.dz = clsRes[iP].dz;
1411 // done retrieving values for later
1412 if (iP > 0) {
1413 float dx = point.xLab - params.points[iP - 1].xLab;
1414 float dy = point.yLab - params.points[iP - 1].yLab;
1415 float ds2 = dx * dx + dy * dy;
1416 float ds = sqrt(ds2); // circular path (linear approximation)
1417 // if the curvature of the track or the (approximated) chord length is too large the more exact formula is used:
1418 // chord length = 2r * asin(ds/(2r))
1419 // using the first two terms of the tailer expansion for asin(x) ~ x + x^3 / 6
1420 if (ds * curvature > 0.05) {
1421 ds *= (1.f + ds2 * curvature * curvature / 24.f);
1422 }
1423 point.sPath = params.points[iP - 1].sPath + ds;
1424 } else {
1425 point.sPath = 0;
1426 }
1427 }
1428 if (std::abs(mBz) < 0.01) {
1429 // for B=0 we don't need to try a circular fit...
1430 return true;
1431 }
1433 // determine curvature
1434 float phiI = TMath::ATan2(params.points.front().yLab, params.points.front().xLab);
1435 float phiF = TMath::ATan2(params.points.back().yLab, params.points.back().xLab);
1436 if (phiI < 0) {
1437 phiI += o2::constants::math::TwoPI;
1438 }
1439 if (phiF < 0) {
1440 phiF += o2::constants::math::TwoPI;
1441 }
1442 float dPhi = phiF - phiI;
1443 float curvSign = -1.f;
1444 if (dPhi > 0) {
1445 if (dPhi < o2::constants::math::PI) {
1446 curvSign = 1.f;
1447 }
1448 } else if (dPhi < -o2::constants::math::PI) {
1449 curvSign = 1.f;
1450 }
1451 params.qpt = std::copysign(1.f / (params.r * mBz * o2::constants::physics::LightSpeedCm2S * 1e-14f), curvSign);
1452
1454
1455 // max deviations in both directions from helix fit in y and z
1456 float hMinY = 1e9f;
1457 float hMaxY = -1e9f;
1458 float hMinZ = 1e9f;
1459 float hMaxZ = -1e9f;
1460 // extract residuals in Z and fill track slopes in sector frame
1461 int secCurr = -1;
1462 iRow = 0;
1463 float xcSec = 0;
1464 for (unsigned int iCl = 0; iCl < nCl; ++iCl) {
1465 iRow += clsRes[iCl].dRow;
1466 auto& pnt = params.points[iCl];
1467 pnt.residHelixZ = pnt.zTrk - (params.zOffs + pnt.sPath * params.tgl);
1468 if (pnt.residHelixZ < hMinZ) {
1469 hMinZ = pnt.residHelixZ;
1470 }
1471 if (pnt.residHelixZ > hMaxZ) {
1472 hMaxZ = pnt.residHelixZ;
1473 }
1474 if (pnt.residHelixY < hMinY) {
1475 hMinY = pnt.residHelixY;
1476 }
1477 if (pnt.residHelixY > hMaxY) {
1478 hMaxY = pnt.residHelixY;
1479 }
1480 int sec = clsRes[iCl].sec;
1481 if (sec != secCurr) {
1482 secCurr = sec;
1483 phiSect = (.5f + sec) * o2::constants::math::SectorSpanRad;
1484 snPhi = sin(phiSect);
1485 csPhi = cos(phiSect);
1486 xcSec = params.xcLab * csPhi + params.ycLab * snPhi; // recalculate circle center in the new sector frame
1487 }
1488 float cstalp = (param::RowX[iRow] - xcSec) / params.r;
1489 if (std::abs(cstalp) > 1.f - sFloatEps) {
1490 // track cannot reach this pad row
1491 cstalp = std::copysign(1.f - sFloatEps, cstalp);
1492 }
1493 pnt.tglArr = cstalp / sqrt((1 - cstalp) * (1 + cstalp)); // 1 / tan(acos(cstalp)) = cstalp / sqrt(1 - cstalp^2)
1494
1495 // In B+ the slope of q- should increase with x. Just look on q * B
1496 if (params.qpt * mBz > 0) {
1497 pnt.tglArr = -pnt.tglArr;
1498 }
1499 }
1500 // LOGF(info, "CompareToHelix: hMaxY(%f), hMinY(%f), hMaxZ(%f), hMinZ(%f). Max deviation allowed: y(%.2f), z(%.2f)", hMaxY, hMinY, hMaxZ, hMinZ, mParams->maxDevHelixY, mParams->maxDevHelixZ);
1501 // LOGF(info, "New pt/Q (%f), old pt/Q (%f)", 1./params.qpt, 1./trk.qPt);
1502 return std::abs(hMaxY - hMinY) < mParams->maxDevHelixY && std::abs(hMaxZ - hMinZ) < mParams->maxDevHelixZ;
1503}
1504
1505bool TrackInterpolation::outlierFiltering(const TrackData& trk, TrackValidationData& params, const std::vector<TPCClusterResiduals>& clsRes)
1506{
1507 if (clsRes.size() < mParams->nMALong) {
1508 LOG(debug) << "Skipping track with too few clusters for long moving average: " << clsRes.size();
1509 return false;
1510 }
1511 float rmsLong = checkResiduals(trk, params, clsRes);
1512 if (static_cast<float>(params.nRej) / clsRes.size() > mParams->maxRejFrac) {
1513 LOGP(debug, "Skipping track with too many clusters rejected: {} out of {}", params.nRej, clsRes.size());
1514 return false;
1515 }
1516 if (rmsLong > mParams->maxRMSLong) {
1517 LOG(debug) << "Skipping track with too large RMS: " << rmsLong;
1518 return false;
1519 }
1520 return true;
1521}
1522
1523float TrackInterpolation::checkResiduals(const TrackData& trk, TrackValidationData& params, const std::vector<TPCClusterResiduals>& clsRes)
1524{
1525 float rmsLong = 0.f;
1526
1527 int nCl = clsRes.size();
1528 int iClFirst = 0;
1529 int iClLast = nCl - 1;
1530 int secStart = clsRes[0].sec;
1531
1532 auto rejectAll = [&params]() {
1533 for (auto& pnt : params.points) {
1534 pnt.flagRej = true;
1535 }
1536 params.nRej = params.points.size();
1537 };
1538
1539 // arrays with differences / abs(differences) of points to their neighbourhood, initialized to zero
1540 std::array<float, param::NPadRows> absDevY{};
1541 std::array<float, param::NPadRows> absDevZ{};
1542
1543 for (unsigned int iCl = 0; iCl < nCl; ++iCl) {
1544 if (iCl < iClLast && clsRes[iCl].sec == secStart) {
1545 continue;
1546 }
1547 // sector changed or last cluster reached
1548 // now run estimators for all points in the same sector
1549 int nClSec = iCl - iClFirst;
1550 if (iCl == iClLast) {
1551 ++nClSec;
1552 }
1553 diffToLocLine(params, iClFirst, nClSec);
1554 iClFirst = iCl;
1555 secStart = clsRes[iCl].sec;
1556 }
1557 // store abs deviations
1558 int nAccY = 0;
1559 int nAccZ = 0;
1560 for (int iCl = nCl; iCl--;) {
1561 const auto pnt = params.points[iCl];
1562 if (std::abs(pnt.diffYSmooth) > param::sEps) {
1563 absDevY[nAccY++] = std::abs(pnt.diffYSmooth);
1564 }
1565 if (std::abs(pnt.diffZSmooth) > param::sEps) {
1566 absDevZ[nAccZ++] = std::abs(pnt.diffZSmooth);
1567 }
1568 }
1569 if (nAccY < mParams->minNumberOfAcceptedResiduals || nAccZ < mParams->minNumberOfAcceptedResiduals) {
1570 // mask all clusters
1571 LOGP(debug, "Accepted {} clusters for dY {} clusters for dZ, but required at least {} for both", nAccY, nAccZ, mParams->minNumberOfAcceptedResiduals);
1572 rejectAll();
1573 return 0.f;
1574 }
1575 // estimate rms on 90% of the smallest deviations
1576 int nKeepY = static_cast<int>(.9 * nAccY);
1577 int nKeepZ = static_cast<int>(.9 * nAccZ);
1578 std::nth_element(absDevY.begin(), absDevY.begin() + nKeepY, absDevY.begin() + nAccY);
1579 std::nth_element(absDevZ.begin(), absDevZ.begin() + nKeepZ, absDevZ.begin() + nAccZ);
1580 float rmsYkeep = 0.f;
1581 float rmsZkeep = 0.f;
1582 for (int i = nKeepY; i--;) {
1583 rmsYkeep += absDevY[i] * absDevY[i];
1584 }
1585 for (int i = nKeepZ; i--;) {
1586 rmsZkeep += absDevZ[i] * absDevZ[i];
1587 }
1588 rmsYkeep = std::sqrt(rmsYkeep / nKeepY);
1589 rmsZkeep = std::sqrt(rmsZkeep / nKeepZ);
1590 if (rmsYkeep < param::sEps || rmsZkeep < param::sEps) {
1591 LOG(warning) << "Too small RMS: " << rmsYkeep << "(y), " << rmsZkeep << "(z).";
1592 rejectAll();
1593 return 0.f;
1594 }
1595 float rmsYkeepI = 1.f / rmsYkeep;
1596 float rmsZkeepI = 1.f / rmsZkeep;
1597 int nAcc = 0;
1598 std::array<float, param::NPadRows> yAcc;
1599 std::array<float, param::NPadRows> yDiffLong;
1600 for (int iCl = 0; iCl < nCl; ++iCl) {
1601 auto& pnt = params.points[iCl];
1602 auto yDiffScl = pnt.diffYSmooth * rmsYkeepI;
1603 auto zDiffScl = pnt.diffZSmooth * rmsZkeepI;
1604 if (yDiffScl * yDiffScl + zDiffScl * zDiffScl > mParams->maxStdDevMA) {
1605 pnt.flagRej = true;
1606 params.nRej++;
1607 } else {
1608 yAcc[nAcc++] = pnt.dy;
1609 }
1610 }
1611 if (nAcc > mParams->nMALong) {
1612 diffToMA(nAcc, yAcc, yDiffLong);
1613 float average = 0.f, rms = 0.f;
1614 for (int i = 0; i < nAcc; ++i) {
1615 average += yDiffLong[i];
1616 rms += yDiffLong[i] * yDiffLong[i];
1617 }
1618 average /= nAcc;
1619 rmsLong = rms / nAcc - average * average;
1620 rmsLong = (rmsLong > 0) ? std::sqrt(rmsLong) : 0.f;
1621 }
1622 return rmsLong;
1623}
1624
1626{
1627 std::array<float, param::NPadRows + 1> sumX1{}, sumX2{}, sumY1{}, sumXY{}, sumZ1{}, sumXZ{};
1628 for (int i = 0; i < np; ++i) {
1629 const auto& pnt = params.points[start + i];
1630 const float x = pnt.xTrk, y = pnt.dy, z = pnt.dz;
1631 sumX1[i + 1] = sumX1[i] + x;
1632 sumX2[i + 1] = sumX2[i] + x * x;
1633 sumY1[i + 1] = sumY1[i] + y;
1634 sumXY[i + 1] = sumXY[i] + x * y;
1635 sumZ1[i + 1] = sumZ1[i] + z;
1636 sumXZ[i + 1] = sumXZ[i] + x * z;
1637 }
1638
1639 for (int i = 0; i < np; ++i) {
1640 auto& pnt = params.points[start + i];
1641
1642 const int iLeft = std::max(0, i - mParams->nMAShort);
1643 const int iRight = std::min(np - 1, i + mParams->nMAShort);
1644
1645 const int nPoints = iRight - iLeft; // excluding current point
1646
1647 if (nPoints < mParams->nMAShort) {
1648 continue;
1649 }
1650
1651 const float nPointsInv = 1.f / nPoints;
1652
1653 float sX1 = sumX1[iRight + 1] - sumX1[iLeft] - pnt.xTrk;
1654 float sX2 = sumX2[iRight + 1] - sumX2[iLeft] - pnt.xTrk * pnt.xTrk;
1655 float sY1 = sumY1[iRight + 1] - sumY1[iLeft] - pnt.dy;
1656 float sXY = sumXY[iRight + 1] - sumXY[iLeft] - pnt.xTrk * pnt.dy;
1657 float sZ1 = sumZ1[iRight + 1] - sumZ1[iLeft] - pnt.dz;
1658 float sXZ = sumXZ[iRight + 1] - sumXZ[iLeft] - pnt.xTrk * pnt.dz;
1659
1660 const float det = sX2 - nPointsInv * sX1 * sX1;
1661
1662 if (std::abs(det) < 1e-12f) {
1663 continue;
1664 }
1665
1666 const float slopeY = (sXY - nPointsInv * sX1 * sY1) / det;
1667 const float offsetY = nPointsInv * (sY1 - slopeY * sX1);
1668 const float slopeZ = (sXZ - nPointsInv * sX1 * sZ1) / det;
1669 const float offsetZ = nPointsInv * (sZ1 - slopeZ * sX1);
1670 pnt.diffYSmooth = pnt.dy - (slopeY * pnt.xTrk + offsetY);
1671 pnt.diffZSmooth = pnt.dz - (slopeZ * pnt.xTrk + offsetZ);
1672 }
1673}
1674
1675void TrackInterpolation::diffToMA(const int np, const std::array<float, param::NPadRows>& y, std::array<float, param::NPadRows>& diffMA)
1676{
1677 // Calculate
1678 std::array<float, param::NPadRows + 1> sum{};
1679 for (int i = 0; i < np; ++i) {
1680 sum[i + 1] = sum[i] + y[i];
1681 }
1682 for (int i = 0; i < np; ++i) {
1683 diffMA[i] = 0.f;
1684 int iLeft = std::max(0, i - mParams->nMALong);
1685 int iRight = std::min(np - 1, i + mParams->nMALong);
1686 int nPoints = iRight - iLeft;
1687 if (nPoints < mParams->nMALong) { // this cannot happen, since at least mParams->nMALong points are required as neighbours for this function to be called
1688 continue;
1689 }
1690 float movingAverage = (sum[iRight + 1] - sum[iLeft] - y[i]) / nPoints;
1691 diffMA[i] = y[i] - movingAverage;
1692 }
1693}
1694
1696{
1697 mTrackData.clear();
1698 mTrackDataCompact.clear();
1699 mTrackDataExtended.clear();
1700 mClRes.clear();
1701 mDetInfoRes.clear();
1702 mGIDsSuccess.clear();
1703 mTRDGIDsSuccess.clear();
1704 for (auto& vec : mTrackIndices) {
1705 vec.clear();
1706 }
1707 mGIDs.clear();
1708 mGIDtables.clear();
1709 mTrackTimes.clear();
1710 mSeeds.clear();
1711 mITSRefitSeedID.clear();
1712 mTrackPVID.clear();
1713}
1714
1715//______________________________________________
1717{
1718 // Attention! For the refit we are using reference VDrift and TDriftOffest rather than high-rate calibrated, since we want to have fixed reference over the run
1719 if (v.refVDrift != mTPCVDriftRef) {
1720 mTPCVDriftRef = v.refVDrift;
1721 mTPCDriftTimeOffsetRef = v.refTimeOffset;
1722 LOGP(info, "Imposing reference VDrift={}/TDrift={} for TPC residuals extraction", mTPCVDriftRef, mTPCDriftTimeOffsetRef);
1723 o2::tpc::TPCFastTransformHelperO2::instance()->updateCalibration(*mFastTransform, 0, 1.0, mTPCVDriftRef, mTPCDriftTimeOffsetRef);
1724 }
1725}
1726
1727//______________________________________________
1729{
1730 // refit ITS track outwards taking PID (unless already refitted) from the seed and reassign to the seed
1731 auto& seed = mSeeds[seedID];
1732 int refitID = mITSRefitSeedID[gid.getIndex()];
1733 if (refitID >= 0) { // track was already refitted
1734 if (mSeeds[refitID].getPID() == seed.getPID()) {
1735 seed = mSeeds[refitID];
1736 }
1737 return true;
1738 }
1739 const auto& trkITS = mRecoCont->getITSTrack(gid);
1740 // fetch clusters
1741 auto nCl = trkITS.getNumberOfClusters();
1742 auto clEntry = trkITS.getFirstClusterEntry();
1743 o2::track::TrackParCov track(trkITS); // start from the inner param
1744 track.resetCovariance();
1745 track.setCov(track.getQ2Pt() * track.getQ2Pt() * track.getCov()[o2::track::CovLabels::kSigQ2Pt2], o2::track::CovLabels::kSigQ2Pt2);
1746 track.setPID(seed.getPID());
1747 o2::track::TrackPar refLin(track); // and use it also as linearization reference
1748 auto geom = o2::its::GeometryTGeo::Instance();
1749 auto prop = o2::base::Propagator::Instance();
1750 for (int iCl = nCl - 1; iCl >= 0; iCl--) { // clusters are stored from outer to inner layers
1751 const auto& cls = mITSClustersArray[mITSTrackClusIdx[clEntry + iCl]];
1752 int chip = cls.getSensorID();
1753 float chipX, chipAlpha;
1754 geom->getSensorXAlphaRefPlane(cls.getSensorID(), chipX, chipAlpha);
1755 if (!track.rotate(chipAlpha, refLin, mBz)) {
1756 LOGP(debug, "failed to rotate ITS tracks to alpha={} for the refit: {}", chipAlpha, track.asString());
1757 return false;
1758 }
1759 if (!prop->propagateToX(track, refLin, cls.getX(), mBz, o2::base::PropagatorImpl<float>::MAX_SIN_PHI, o2::base::PropagatorImpl<float>::MAX_STEP, o2::base::PropagatorF::MatCorrType::USEMatCorrLUT)) {
1760 LOGP(debug, "failed to propagate ITS tracks to X={}: {}", cls.getX(), track.asString());
1761 return false;
1762 }
1763 std::array<float, 2> posTF{cls.getY(), cls.getZ()};
1764 std::array<float, 3> covTF{cls.getSigmaY2(), cls.getSigmaYZ(), cls.getSigmaZ2()};
1765 if (!track.update(posTF, covTF)) {
1766 LOGP(debug, "failed to update ITS tracks by cluster ({},{})/({},{},{})", track.asString(), cls.getY(), cls.getZ(), cls.getSigmaY2(), cls.getSigmaYZ(), cls.getSigmaZ2());
1767 return false;
1768 }
1769 if (mParams->shiftRefToCluster) {
1770 refLin.setY(posTF[0]);
1771 refLin.setZ(posTF[1]);
1772 }
1773 }
1774 seed = track;
1775 // memorize that this ITS track was already refitted
1776 mITSRefitSeedID[gid.getIndex()] = seedID;
1777 return true;
1778}
std::string asString(TDataMember const &dm, char *pointer)
Global TRD definitions and constants.
std::ostringstream debug
std::vector< double > sum
int32_t i
Definition of the GeometryTGeo class.
Definition of the parameter class for the detector electronics.
uint16_t pos
Definition RawData.h:3
uint16_t slope
Definition RawData.h:1
uint32_t res
Definition RawData.h:0
Definition of the TrackInterpolation class.
Definition of the TrackResiduals class.
calibration data from laser track calibration
Referenc on track indices contributing to the vertex, with possibility chose tracks from specific sou...
GPUd() value_type estimateLTFast(o2 static GPUd() float estimateLTIncrement(const o2 PropagatorImpl * Instance(bool uninitialized=false)
Definition Propagator.h:180
static BasicCCDBManager & instance()
T * getSpecific(std::string const &path, long timestamp=-1, MD metaData=MD(), std::map< std::string, std::string > *headers=nullptr)
retrieve an object of type T from CCDB as stored under path, timestamp and metaData
static mask_t getSourcesMask(const std::string_view srcList)
static std::string getSourcesNames(mask_t srcm)
Static class with identifiers, bitmasks and names for ALICE detectors.
Definition DetID.h:60
static size_t fillOccupancyMapGetSize(uint32_t nHbfPerTf, const GPUParam *param=nullptr)
static void paramUseExternalOccupancyMap(GPUParam *param, uint32_t nHbfPerTf, const uint32_t *occupancymap, int32_t occupancyMapSize)
static std::shared_ptr< GPUParam > getFullParamShared(float solenoidBz, uint32_t nHbfPerTf=0, std::unique_ptr< GPUO2InterfaceConfiguration > *pConfiguration=nullptr, std::unique_ptr< GPUSettingsO2 > *pO2Settings=nullptr, bool *autoMaxTimeBin=nullptr)
void init(float bz, const GPUSettingsRec *rec=nullptr)
Load parameterization for given magnetic field.
float getSensorRefAlpha(int isn) const
static GeometryTGeo * Instance()
float getSensorRefX(int isn) const
void beginLayer(int lr)
to be called once all the layers were filled
void init(int nLr)
< prepare for filling nLr layer slots, discarding the previous content
static constexpr Double_t BC_TIME_INPS
Definition Geo.h:105
static constexpr Double_t BC_TIME_INPS_INV
Definition Geo.h:106
static void rotateToSector(Float_t *xyz, Int_t isector)
Definition Geo.cxx:1029
static void getPos(Int_t *det, Float_t *pos)
Definition Geo.cxx:491
static constexpr Int_t NPADSXSECTOR
Definition Geo.h:120
static void getVolumeIndices(Int_t index, Int_t *detId)
Definition Geo.cxx:543
static void alignedToNominalSector(Float_t *xyz, Int_t isector)
Definition Geo.cxx:1049
static TPCFastTransformHelperO2 * instance()
Singleton.
int updateCalibration(TPCFastTransform &fastTransform, int64_t TimeStamp, float vDriftFactor=1.f, float vDriftRef=0.f, float driftTimeOffset=0.f)
Updates the transformation with the new time stamp.
float checkResiduals(const TrackData &trk, TrackValidationData &params, const std::vector< TPCClusterResiduals > &clsRes)
void prepareInputTrackSample(const o2::globaltracking::RecoContainer &inp)
Prepare input track sample (not relying on CreateTracksVariadic functionality)
int processTRDLayer(const o2::trd::TrackTRD &trkTRD, int iLayer, o2::track::TrackParCov &trkWork, std::array< float, 2 > *trkltTRDYZ=nullptr, std::array< float, 3 > *trkltTRDCov=nullptr, TrackData *trkData=nullptr, o2::trd::Tracklet64 *trk64=nullptr, o2::trd::CalibratedTracklet *trkCalib=nullptr)
bool isInputTrackAccepted(const o2::dataformats::GlobalTrackID &gid, const o2::globaltracking::RecoContainer::GlobalIDSet &gidTable, const o2::dataformats::PrimaryVertex &pv) const
Check if input track passes configured cuts.
@ ExtIn
extrapolation inwards of TRD/TOF track
@ Int
interpolation (mean positions of both extrapolations)
@ ExtOut
extrapolation outwards of ITS track
o2::dataformats::GlobalTrackID::Source findValidSource(const o2::dataformats::GlobalTrackID::mask_t mask, const o2::dataformats::GlobalTrackID::Source src) const
For given vertex track source which is not in mSourcesConfigured find the seeding source which is ena...
bool refITSTrack(o2::dataformats::GlobalTrackID, int iSeed)
void diffToMA(const int np, const std::array< float, param::NPadRows > &y, std::array< float, param::NPadRows > &diffMA)
For a given set of points, calculate their deviation from the moving average (build from the neighbou...
bool compareToHelix(const TrackData &trk, TrackValidationData &params, const std::vector< TPCClusterResiduals > &clsRes)
void diffToLocLine(TrackValidationData &params, int start, int np)
For a given set of points, calculate the differences from each point to the fitted lines from all oth...
void process()
Main processing function.
void setTPCVDrift(const o2::tpc::VDriftCorrFact &v)
int8_t validateTrack(const TrackData &trk, TrackValidationData &params, const std::vector< TPCClusterResiduals > &clsRes, bool interpol)
bool isTrackSelected(const o2::track::TrackParCov &trk) const
Given the defined downsampling factor tsalisThreshold check if track is selected.
void reset()
Reset cache and output vectors.
bool outlierFiltering(const TrackData &trk, TrackValidationData &params, const std::vector< TPCClusterResiduals > &clsRes)
void init(o2::dataformats::GlobalTrackID::mask_t src, o2::dataformats::GlobalTrackID::mask_t srcMap)
Initialize everything, set the requested track sources.
static bool fitPoly1(int nCl, std::array< float, param::NPadRows > &x, std::array< float, param::NPadRows > &y, std::array< float, 2 > &res)
static void fitCircle(int nCl, std::array< float, param::NPadRows > &x, std::array< float, param::NPadRows > &y, float &xc, float &yc, float &r, std::array< float, param::NPadRows > &residHelixY)
static Geometry * instance()
Definition Geometry.h:33
GLfloat GLfloat GLfloat alpha
Definition glcorearb.h:279
GLint GLenum GLint x
Definition glcorearb.h:403
GLenum src
Definition glcorearb.h:1767
GLsizeiptr size
Definition glcorearb.h:659
GLuint GLuint end
Definition glcorearb.h:469
const GLdouble * v
Definition glcorearb.h:832
GLint first
Definition glcorearb.h:399
GLuint GLuint GLfloat weight
Definition glcorearb.h:5477
GLint y
Definition glcorearb.h:270
GLenum const GLfloat * params
Definition glcorearb.h:272
GLboolean GLboolean g
Definition glcorearb.h:1233
GLbitfield flags
Definition glcorearb.h:1570
GLuint start
Definition glcorearb.h:469
GLint GLuint mask
Definition glcorearb.h:291
GLdouble GLdouble GLdouble z
Definition glcorearb.h:843
constexpr int MaxITSLayers
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 angle2Sector(float phi)
Definition Utils.h:183
float sector2Angle(int sect)
Definition Utils.h:193
Global TPC definitions and constants.
Definition SimTraits.h:172
Enum< T >::Iterator begin(Enum< T >)
Definition Defs.h:158
TrackParCovF TrackParCov
Definition Track.h:33
GlobalIDSet getSingleDetectorRefs(GTrackID gidx) const
const o2::tpc::TrackTPC & getTPCTrack(GTrackID id) const
gsl::span< const o2::trd::CalibratedTracklet > getTRDCalibratedTracklets() const
const o2::tpc::ClusterNativeAccess & getTPCClusters() const
auto getITSClustersPatterns(int layer=0) const
const o2::track::TrackParCov & getTrackParam(GTrackID gidx) const
gsl::span< const unsigned char > clusterShMapTPC
externally set TPC clusters sharing map
const o2::dataformats::TrackTPCITS & getTPCITSTrack(GTrackID gid) const
const o2::its::TrackITS & getITSTrack(GTrackID gid) const
auto getITSTPCTRDTrack(GTrackID id) const
gsl::span< const unsigned int > occupancyMapTPC
externally set TPC clusters occupancy map
auto getITSClusters(int layer=0) const
std::array< GTrackID, GTrackID::NSources > GlobalIDSet
gsl::span< const o2::trd::Tracklet64 > getTRDTracklets() const
const o2::dataformats::MatchInfoTOF & getTOFMatch(GTrackID id) const
static bool downsampleTsallisCharged(float pt, float factorPt, float sqrts, float &weight, float rnd, float mass=0.13957)
Definition Tsallis.cxx:31
unsigned int clusterOffset[constants::MAXSECTOR][constants::MAXGLOBALPADROW]
const ClusterNative * clustersLinear
float maxRejFrac
if the fraction of rejected clusters of a track is higher, the full track is invalidated
float maxDevHelixZ
max deviation in Z for clusters wrt helix fit
int nMALong
number of points to be used for moving average (long range)
float minPtNoOuterPoint
minimum pt for ITS-TPC tracks to be considered for extrapolation
bool clampTgSlp
store TPC cluster residuals with |tan(phi)| >= param::MaxTgSlp saturated (tgSlp = +-0x7fff,...
int minTRDNTrklts
min number of TRD space points
bool refitITS
refit ITS tracks with PID attached to the seed
bool keepClustersOnPropFail
if the reference track propagation fails (maxSnp, rotation), keep the track: TPC clusters without a r...
int nMAShort
number of points to be used for estimation of distance from local line (short range)
float maxDevHelixY
max deviation in Y for clusters wrt helix fit
float maxStdDevMA
max cluster std. deviation (Y^2 + Z^2) wrt moving average to accept
bool skipOutlierFiltering
if set, the outlier filtering will not be applied at all
bool ignoreNonPVContrib
flag if tracks which did not contribute to the PV should be ignored or not
bool keepRejectedResiduals
if set, keep rejected residuals setting rejected flag
int minITSNClsNoOuterPoint
min number of ITS clusters if no hit in TRD or TOF exists
int minTPCNCls
min number of TPC clusters
float tsalisThreshold
in case the sampling functions returns a value smaller than this the track is discarded (1....
bool shiftRefToCluster
when reftting the ITS track, shift the lin.reference to cluster after every update (better material m...
bool writeValidationData
write track validation data for debugging
float maxStep
maximum step for propagation
int minTOFTRDPVContributors
min contributors from TRD or TOF (fast detectors) to consider tracks of this PV
bool enableTrackDownsampling
flag if track sampling shall be enabled or not
float maxQ2Pt
max fitted q/pt for a track to be used for calibration
float sigYZ2TOF
for now assume cluster error for TOF equal for all clusters in both Y and Z
int minNumberOfAcceptedResiduals
min number of accepted residuals for
float maxRMSLong
maximum variance of the cluster residuals wrt moving avarage for a track to be considered
int minITSNCls
min number of ITS clusters
float maxSnp
max snp when propagating tracks
bool writeUnfiltered
if set, tracks failing validation will be kept with filterFlag>0 giving the reason of rejection
int minNCl
min number of clusters in a track to be used for calibration
int minTPCNClsNoOuterPoint
min number of TPC clusters if no hit in TRD or TOF exists
Structure filled for each track with track quality information and a vector with TPCClusterResiduals.
float chi2TRD
chi2 of TRD track
float dEdxTPC
TPC dEdx information.
unsigned short nTrkltsTRD
number of attached TRD tracklets
unsigned short clAvailTOF
whether or not track seed has a matched TOF cluster, if so, gives the resolution of the T0 in ps
std::array< uint8_t, 4 > multStack
short TRDTrkltSlope[6]
TRD tracklet slope 0x7fff / param::MaxTRDSlope.
unsigned short nClsITS
number of attached ITS clusters
o2::dataformats::RangeReference clIdx
index of first cluster residual and total number of TPC cluster residuals of this track
float chi2TPC
chi2 of TPC track
o2::track::TrackParCov par
ITS track at inner TPC radius.
unsigned short nClsTPC
number of attached TPC clusters
void setMultStack(float v, int stack)
o2::dataformats::GlobalTrackID gid
global track ID for seeding track
float deltaTOF
TOFsignal - T0 - texp(PID), if T0 is available.
int8_t filterFlag
-1: validation was not done, 0: validated, >0 : reason not passing validation, see validateTrack meth...
uint8_t nExtDetResid
number of external detectors (to TPC) residuals stored, on top of clIdx.getEntries
float chi2ITS
chi2 of ITS track
unsigned char sec
TPC sector (0..35)
unsigned char row
TPC pad row.
static void init(long timestamp=-1)
static constexpr short TgSlpPositionOnly
short channel
extra channel info (ITS chip ID, TRD chamber, TOF main pad within the sector)
o2::mch::DsIndex ds
std::vector< o2::ctf::BufferType > vec
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"
o2::InteractionRecord ir(0, 0)
std::uniform_int_distribution< unsigned long long > distr
std::random_device rd
std::vector< int > row