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 "DataFormatsTPC/Defs.h"
28#include "MathUtils/Tsallis.h"
29#include "TRDBase/PadPlane.h"
30#include "TMath.h"
32#include "Framework/Logger.h"
33#include <set>
34#include <algorithm>
35#include <random>
36
37using namespace o2::tpc;
40
42{
43 // perform initialization
44 LOG(info) << "Start initializing TrackInterpolation";
45 if (mInitDone) {
46 LOG(error) << "Initialization already performed.";
47 return;
48 }
49
50 const auto& elParam = ParameterElectronics::Instance();
51 mTPCTimeBinMUS = elParam.ZbinWidth;
52
53 mFastTransform = std::move(TPCFastTransformHelperO2::instance()->create(0));
54
55 mBz = o2::base::Propagator::Instance()->getNominalBz();
56 mRecoParam.setBfield(mBz);
59
60 mSourcesConfigured = src;
61 mSourcesConfiguredMap = srcMap;
62 mSingleSourcesConfigured = (mSourcesConfigured == mSourcesConfiguredMap);
63 mTrackTypes.insert({GTrackID::ITSTPC, 0});
64 mTrackTypes.insert({GTrackID::ITSTPCTRD, 1});
65 mTrackTypes.insert({GTrackID::ITSTPCTOF, 2});
66 mTrackTypes.insert({GTrackID::ITSTPCTRDTOF, 3});
67
68 mInitDone = true;
69 LOGP(info, "Done initializing TrackInterpolation. Configured track input: {}. Track input specifically for map: {}",
70 GTrackID::getSourcesNames(mSourcesConfigured), mSingleSourcesConfigured ? "identical" : GTrackID::getSourcesNames(mSourcesConfiguredMap));
71}
72
74{
75 LOGP(debug, "Check if input track {} is accepted", gid.asString());
76 bool hasOuterPoint = gidTable[GTrackID::TRD].isIndexSet() || gidTable[GTrackID::TOF].isIndexSet();
77 if (!hasOuterPoint && !mProcessITSTPConly) {
78 return false; // don't do ITS-only extrapolation through TPC
79 }
80 const auto itsTrk = mRecoCont->getITSTrack(gidTable[GTrackID::ITS]);
81 const auto tpcTrk = mRecoCont->getTPCTrack(gidTable[GTrackID::TPC]);
82
83 if (gidTable[GTrackID::TRD].isIndexSet()) {
84 // TRD specific cuts
85 const auto& trdTrk = mRecoCont->getITSTPCTRDTrack<o2::trd::TrackTRD>(gidTable[GTrackID::ITSTPCTRD]);
86 if (trdTrk.getNtracklets() < mParams->minTRDNTrklts) {
87 return false;
88 }
89 }
90 // reduced chi2 cut is the same for all track types
91 if (itsTrk.getChi2() / itsTrk.getNumberOfClusters() > mParams->maxITSChi2 || tpcTrk.getChi2() / tpcTrk.getNClusterReferences() > mParams->maxTPCChi2) {
92 return false;
93 }
94 if (!hasOuterPoint) {
95 // ITS-TPC track (does not have outer points in TRD or TOF)
96 if (itsTrk.getNumberOfClusters() < mParams->minITSNClsNoOuterPoint || tpcTrk.getNClusterReferences() < mParams->minTPCNClsNoOuterPoint) {
97 return false;
98 }
99 if (itsTrk.getPt() < mParams->minPtNoOuterPoint) {
100 return false;
101 }
102 } else {
103 if (itsTrk.getNumberOfClusters() < mParams->minITSNCls || tpcTrk.getNClusterReferences() < mParams->minTPCNCls) {
104 return false;
105 }
106 }
107
108 auto trc = mRecoCont->getTrackParam(gid);
110 auto prop = o2::base::Propagator::Instance();
111 if (!prop->propagateToDCA(pv, trc, prop->getNominalBz(), 2., o2::base::PropagatorF::MatCorrType::USEMatCorrLUT, &dca)) {
112 return false;
113 }
114 if (dca.getR2() > mParams->maxDCA * mParams->maxDCA) {
115 return false;
116 }
117 return true;
118}
119
121{
122 LOGP(debug, "Trying to find valid source for {} in {}", GTrackID::getSourceName(src), GTrackID::getSourcesNames(mask));
125 return GTrackID::ITSTPCTRD;
126 } else if (mask[GTrackID::ITSTPC]) {
127 return GTrackID::ITSTPC;
128 } else {
129 return GTrackID::NSources;
130 }
131 } else if (src == GTrackID::ITSTPCTRD || src == GTrackID::ITSTPCTOF) {
132 if (mask[GTrackID::ITSTPC]) {
133 return GTrackID::ITSTPC;
134 } else {
135 return GTrackID::NSources;
136 }
137 } else {
138 return GTrackID::NSources;
139 }
140}
141
143{
144 mRecoCont = &inp;
145 uint32_t nTrackSeeds = 0;
146 uint32_t countSeedCandidates[4] = {0};
147 auto pvvec = mRecoCont->getPrimaryVertices();
148 auto trackIndex = mRecoCont->getPrimaryVertexMatchedTracks(); // Global ID's for associated tracks
149 auto vtxRefs = mRecoCont->getPrimaryVertexMatchedTrackRefs(); // references from vertex to these track IDs
150 int nv = vtxRefs.size() - 1;
151 GTrackID::mask_t allowedSources = GTrackID::getSourcesMask("ITS-TPC,ITS-TPC-TRD,ITS-TPC-TOF,ITS-TPC-TRD-TOF");
152 constexpr std::array<int, 3> SrcFast = {int(GTrackID::ITSTPCTRD), int(GTrackID::ITSTPCTOF), int(GTrackID::ITSTPCTRDTOF)};
153
154 for (int iv = 0; iv < nv; iv++) {
155 LOGP(debug, "processing PV {} of {}", iv, nv);
156
157 const auto& vtref = vtxRefs[iv];
158 auto pv = pvvec[iv];
159 if (mParams->minTOFTRDPVContributors > 0) { // we want only PVs constrained by fast detectors
160 int nfound = 0;
161 bool usePV = false;
162 for (uint32_t is = 0; is < SrcFast.size() && !usePV; is++) {
163 int src = SrcFast[is], idMin = vtref.getFirstEntryOfSource(src), idMax = idMin + vtref.getEntriesOfSource(src);
164 for (int i = idMin; i < idMax; i++) {
165 if (trackIndex[i].isPVContributor() && (++nfound == mParams->minTOFTRDPVContributors)) {
166 usePV = true;
167 break;
168 }
169 }
170 }
171 if (!usePV) {
172 continue;
173 }
174 }
175
176 for (int is = GTrackID::NSources; is >= 0; is--) {
177 if (!allowedSources[is]) {
178 continue;
179 }
180 LOGP(debug, "Checking source {}", is);
181 int idMin = vtref.getFirstEntryOfSource(is), idMax = idMin + vtref.getEntriesOfSource(is);
182 for (int i = idMin; i < idMax; i++) {
183 auto vid = trackIndex[i];
184 auto vidOrig = vid; // in case only ITS-TPC tracks are configured vid might be overwritten. We need to remember it for the PID
185 if (mParams->ignoreNonPVContrib && !vid.isPVContributor()) {
186 continue;
187 }
188 if (vid.isAmbiguous()) {
189 continue;
190 }
191 auto gidTable = mRecoCont->getSingleDetectorRefs(vid);
192 if (!mSourcesConfigured[is]) {
193 auto src = findValidSource(mSourcesConfigured, static_cast<GTrackID::Source>(vid.getSource()));
195 LOGP(debug, "prepareInputTrackSample: Found valid source {}", GTrackID::getSourceName(src));
196 vid = gidTable[src];
197 gidTable = mRecoCont->getSingleDetectorRefs(vid);
198 } else {
199 break; // no valid source for this vertex track source exists
200 }
201 }
202 ++countSeedCandidates[mTrackTypes[vid.getSource()]];
203 LOGP(debug, "Checking vid {}", vid.asString());
204 if (!isInputTrackAccepted(vid, gidTable, pv)) {
205 continue;
206 }
207 mSeeds.push_back(mRecoCont->getITSTrack(gidTable[GTrackID::ITS]).getParamOut());
208 mSeeds.back().setPID(mRecoCont->getTrackParam(vidOrig).getPID(), true);
209 mGIDs.push_back(vid);
210 mGIDtables.push_back(gidTable);
211 mTrackTimes.push_back(pv.getTimeStamp().getTimeStamp());
212 mTrackIndices[mTrackTypes[vid.getSource()]].push_back(nTrackSeeds++);
213 }
214 }
215 }
216
217 LOGP(info, "Created {} seeds. {} out of {} ITS-TPC-TRD-TOF, {} out of {} ITS-TPC-TRD, {} out of {} ITS-TPC-TOF, {} out of {} ITS-TPC",
218 nTrackSeeds,
219 mTrackIndices[mTrackTypes[GTrackID::ITSTPCTRDTOF]].size(), countSeedCandidates[mTrackTypes[GTrackID::ITSTPCTRDTOF]],
220 mTrackIndices[mTrackTypes[GTrackID::ITSTPCTRD]].size(), countSeedCandidates[mTrackTypes[GTrackID::ITSTPCTRD]],
221 mTrackIndices[mTrackTypes[GTrackID::ITSTPCTOF]].size(), countSeedCandidates[mTrackTypes[GTrackID::ITSTPCTOF]],
222 mTrackIndices[mTrackTypes[GTrackID::ITSTPC]].size(), countSeedCandidates[mTrackTypes[GTrackID::ITSTPC]]);
223}
224
226{
227 std::random_device rd;
228 std::mt19937 g(rd());
229 std::uniform_real_distribution<> distr(0., 1.);
230 float weight = 0;
232}
233
235{
236 // main processing function
237 if (!mInitDone) {
238 LOG(error) << "Initialization not yet done. Aborting...";
239 return;
240 }
241 // set the input containers
242 mTPCTracksClusIdx = mRecoCont->getTPCTracksClusterRefs();
243 mTPCClusterIdxStruct = &mRecoCont->getTPCClusters();
244 if (mDumpTrackPoints) {
245 if (!mITSDict) {
246 LOG(error) << "No ITS dictionary available";
247 return;
248 }
249 mITSTrackClusIdx = mRecoCont->getITSTracksClusterRefs();
250 const auto clusITS = mRecoCont->getITSClusters();
251 const auto patterns = mRecoCont->getITSClustersPatterns();
252 auto pattIt = patterns.begin();
253 mITSClustersArray.clear();
254 mITSClustersArray.reserve(clusITS.size());
255 LOGP(info, "We have {} ITS clusters and the number of patterns is {}", clusITS.size(), patterns.size());
256 o2::its::ioutils::convertCompactClusters(clusITS, pattIt, mITSClustersArray, mITSDict);
257 }
258
259 // In case we have more input tracks available than are required per TF
260 // we want to sample them. But we still prefer global ITS-TPC-TRD-TOF tracks
261 // over ITS-TPC-TRD tracks and so on. So we have to shuffle the indices
262 // in blocks.
263 std::random_device rd;
264 std::mt19937 g(rd());
265 std::vector<uint32_t> trackIndices; // here we keep the GIDs for all track types in a single vector to use in loop
266 std::shuffle(mTrackIndices[mTrackTypes[GTrackID::ITSTPCTRDTOF]].begin(), mTrackIndices[mTrackTypes[GTrackID::ITSTPCTRDTOF]].end(), g);
267 std::shuffle(mTrackIndices[mTrackTypes[GTrackID::ITSTPCTRD]].begin(), mTrackIndices[mTrackTypes[GTrackID::ITSTPCTRD]].end(), g);
268 std::shuffle(mTrackIndices[mTrackTypes[GTrackID::ITSTPCTOF]].begin(), mTrackIndices[mTrackTypes[GTrackID::ITSTPCTOF]].end(), g);
269 std::shuffle(mTrackIndices[mTrackTypes[GTrackID::ITSTPC]].begin(), mTrackIndices[mTrackTypes[GTrackID::ITSTPC]].end(), g);
270 trackIndices.insert(trackIndices.end(), mTrackIndices[mTrackTypes[GTrackID::ITSTPCTRDTOF]].begin(), mTrackIndices[mTrackTypes[GTrackID::ITSTPCTRDTOF]].end());
271 trackIndices.insert(trackIndices.end(), mTrackIndices[mTrackTypes[GTrackID::ITSTPCTRD]].begin(), mTrackIndices[mTrackTypes[GTrackID::ITSTPCTRD]].end());
272 trackIndices.insert(trackIndices.end(), mTrackIndices[mTrackTypes[GTrackID::ITSTPCTOF]].begin(), mTrackIndices[mTrackTypes[GTrackID::ITSTPCTOF]].end());
273 trackIndices.insert(trackIndices.end(), mTrackIndices[mTrackTypes[GTrackID::ITSTPC]].begin(), mTrackIndices[mTrackTypes[GTrackID::ITSTPC]].end());
274
275 int nSeeds = mSeeds.size();
276 int maxOutputTracks = (mMaxTracksPerTF >= 0) ? mMaxTracksPerTF + mAddTracksForMapPerTF : nSeeds;
277 mTrackData.reserve(maxOutputTracks);
278 mClRes.reserve(maxOutputTracks * param::NPadRows);
279 bool maxTracksReached = false;
280 for (int iSeed = 0; iSeed < nSeeds; ++iSeed) {
281 if (mMaxTracksPerTF >= 0 && mTrackDataCompact.size() >= mMaxTracksPerTF + mAddTracksForMapPerTF) {
282 LOG(info) << "Maximum number of tracks per TF reached. Skipping the remaining " << nSeeds - iSeed << " tracks.";
283 break;
284 }
285 int seedIndex = trackIndices[iSeed];
286 if (mParams->enableTrackDownsampling && !isTrackSelected(mSeeds[seedIndex])) {
287 continue;
288 }
289 if (!mSingleSourcesConfigured && !mSourcesConfiguredMap[mGIDs[seedIndex].getSource()]) {
290 auto src = findValidSource(mSourcesConfiguredMap, static_cast<GTrackID::Source>(mGIDs[seedIndex].getSource()));
292 LOGP(debug, "process: Found valid source {}", GTrackID::getSourceName(src));
293 mGIDs.push_back(mGIDtables[seedIndex][src]);
294 mGIDtables.push_back(mRecoCont->getSingleDetectorRefs(mGIDs.back()));
295 mTrackTimes.push_back(mTrackTimes[seedIndex]);
296 mSeeds.push_back(mSeeds[seedIndex]);
297 }
298 }
299 if (mMaxTracksPerTF >= 0 && mTrackDataCompact.size() >= mMaxTracksPerTF) {
300 LOG(debug) << "We already have reached mMaxTracksPerTF, but we continue to create seeds until mAddTracksForMapPerTF is also reached";
301 continue;
302 }
303 if (mGIDs[seedIndex].includesDet(DetID::TRD) || mGIDs[seedIndex].includesDet(DetID::TOF)) {
304 interpolateTrack(seedIndex);
305 if (mProcessSeeds) {
306 if (mGIDs[seedIndex].includesDet(DetID::TRD) && mGIDs[seedIndex].includesDet(DetID::TOF)) {
307 mGIDs.push_back(mGIDtables[seedIndex][GTrackID::ITSTPCTRD]);
308 mGIDtables.push_back(mRecoCont->getSingleDetectorRefs(mGIDs.back()));
309 mTrackTimes.push_back(mTrackTimes[seedIndex]);
310 mSeeds.push_back(mSeeds[seedIndex]);
311 }
312 mGIDs.push_back(mGIDtables[seedIndex][GTrackID::ITSTPC]);
313 mGIDtables.push_back(mRecoCont->getSingleDetectorRefs(mGIDs.back()));
314 mTrackTimes.push_back(mTrackTimes[seedIndex]);
315 mSeeds.push_back(mSeeds[seedIndex]);
316 }
317 } else {
318 extrapolateTrack(seedIndex);
319 }
320 }
321 if (mSeeds.size() > nSeeds) {
322 LOGP(info, "Up to {} tracks out of {} additional seeds will be processed", mAddTracksForMapPerTF, mSeeds.size() - nSeeds);
323 }
324 for (int iSeed = nSeeds; iSeed < (int)mSeeds.size(); ++iSeed) {
325 if (!mProcessSeeds && mAddTracksForMapPerTF > 0 && mTrackDataCompact.size() >= mMaxTracksPerTF + mAddTracksForMapPerTF) {
326 LOG(info) << "Maximum number of additional tracks per TF reached. Skipping the remaining " << mSeeds.size() - iSeed << " tracks.";
327 break;
328 }
329 // this loop will only be entered in case mProcessSeeds is set
330 LOGP(debug, "Processing additional track {}", mGIDs[iSeed].asString());
331 if (mGIDs[iSeed].includesDet(DetID::TRD) || mGIDs[iSeed].includesDet(DetID::TOF)) {
332 interpolateTrack(iSeed);
333 } else {
334 extrapolateTrack(iSeed);
335 }
336 }
337 LOG(info) << "Could process " << mTrackData.size() << " tracks successfully. " << mRejectedResiduals << " residuals were rejected. " << mClRes.size() << " residuals were accepted.";
338 mRejectedResiduals = 0;
339}
340
342{
343 LOGP(debug, "Starting track interpolation for GID {}", mGIDs[iSeed].asString());
344 TrackData trackData;
345 std::unique_ptr<TrackDataExtended> trackDataExtended;
346 std::vector<TPCClusterResiduals> clusterResiduals;
347 auto propagator = o2::base::Propagator::Instance();
348 const auto& gidTable = mGIDtables[iSeed];
349 const auto& trkTPC = mRecoCont->getTPCTrack(gidTable[GTrackID::TPC]);
350 const auto& trkITS = mRecoCont->getITSTrack(gidTable[GTrackID::ITS]);
351 if (mDumpTrackPoints) {
352 trackDataExtended = std::make_unique<TrackDataExtended>();
353 (*trackDataExtended).gid = mGIDs[iSeed];
354 (*trackDataExtended).clIdx.setFirstEntry(mClRes.size());
355 (*trackDataExtended).trkITS = trkITS;
356 (*trackDataExtended).trkTPC = trkTPC;
357 auto nCl = trkITS.getNumberOfClusters();
358 auto clEntry = trkITS.getFirstClusterEntry();
359 for (int iCl = nCl - 1; iCl >= 0; iCl--) { // clusters are stored from outer to inner layers
360 const auto& clsITS = mITSClustersArray[mITSTrackClusIdx[clEntry + iCl]];
361 (*trackDataExtended).clsITS.push_back(clsITS);
362 }
363 }
364 trackData.gid = mGIDs[iSeed];
365 trackData.par = mSeeds[iSeed];
366 auto& trkWork = mSeeds[iSeed];
367 // reset the cache array (sufficient to set cluster available to zero)
368 for (auto& elem : mCache) {
369 elem.clAvailable = 0;
370 }
371 trackData.clIdx.setFirstEntry(mClRes.size()); // reference the first cluster residual belonging to this track
372 float clusterTimeBinOffset = mTrackTimes[iSeed] / mTPCTimeBinMUS;
373
374 // store the TPC cluster positions in the cache
375 for (int iCl = trkTPC.getNClusterReferences(); iCl--;) {
376 uint8_t sector, row;
377 uint32_t clusterIndexInRow;
378 const auto& clTPC = trkTPC.getCluster(mTPCTracksClusIdx, iCl, *mTPCClusterIdxStruct, sector, row);
379 float clTPCX;
380 std::array<float, 2> clTPCYZ;
381 mFastTransform->TransformIdeal(sector, row, clTPC.getPad(), clTPC.getTime(), clTPCX, clTPCYZ[0], clTPCYZ[1], clusterTimeBinOffset);
382 mCache[row].clSec = sector;
383 mCache[row].clAvailable = 1;
384 mCache[row].clY = clTPCYZ[0];
385 mCache[row].clZ = clTPCYZ[1];
386 mCache[row].clAngle = o2::math_utils::sector2Angle(sector);
387 }
388
389 // extrapolate seed through TPC and store track position at each pad row
390 for (int iRow = 0; iRow < param::NPadRows; ++iRow) {
391 if (!mCache[iRow].clAvailable) {
392 continue;
393 }
394 if (!trkWork.rotate(mCache[iRow].clAngle)) {
395 LOG(debug) << "Failed to rotate track during first extrapolation";
396 return;
397 }
398 if (!propagator->PropagateToXBxByBz(trkWork, param::RowX[iRow], mParams->maxSnp, mParams->maxStep, mMatCorr)) {
399 LOG(debug) << "Failed on first extrapolation";
400 return;
401 }
402 mCache[iRow].y[ExtOut] = trkWork.getY();
403 mCache[iRow].z[ExtOut] = trkWork.getZ();
404 mCache[iRow].sy2[ExtOut] = trkWork.getSigmaY2();
405 mCache[iRow].szy[ExtOut] = trkWork.getSigmaZY();
406 mCache[iRow].sz2[ExtOut] = trkWork.getSigmaZ2();
407 mCache[iRow].snp[ExtOut] = trkWork.getSnp();
408 // printf("Track alpha at row %i: %.2f, Y(%.2f), Z(%.2f)\n", iRow, trkWork.getAlpha(), trkWork.getY(), trkWork.getZ());
409 }
410
411 // start from outermost cluster with outer refit and back propagation
412 if (gidTable[GTrackID::TOF].isIndexSet()) {
413 LOG(debug) << "TOF point available";
414 const auto& clTOF = mRecoCont->getTOFClusters()[gidTable[GTrackID::TOF]];
415 if (mDumpTrackPoints) {
416 (*trackDataExtended).clsTOF = clTOF;
417 (*trackDataExtended).matchTOF = mRecoCont->getTOFMatch(mGIDs[iSeed]);
418 }
419 const int clTOFSec = clTOF.getCount();
420 const float clTOFAlpha = o2::math_utils::sector2Angle(clTOFSec);
421 if (!trkWork.rotate(clTOFAlpha)) {
422 LOG(debug) << "Failed to rotate into TOF cluster sector frame";
423 return;
424 }
425 float clTOFX = clTOF.getX();
426 std::array<float, 2> clTOFYZ{clTOF.getY(), clTOF.getZ()};
427 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
428 if (!propagator->PropagateToXBxByBz(trkWork, clTOFX, mParams->maxSnp, mParams->maxStep, mMatCorr)) {
429 LOG(debug) << "Failed final propagation to TOF radius";
430 return;
431 }
432 // TODO: check if reset of covariance matrix is needed here (or, in case TOF point is not available at outermost TRD layer)
433 if (!trkWork.update(clTOFYZ, clTOFCov)) {
434 LOG(debug) << "Failed to update extrapolated ITS track with TOF cluster";
435 // LOGF(info, "trkWork.y=%f, cl.y=%f, trkWork.z=%f, cl.z=%f", trkWork.getY(), clTOFYZ[0], trkWork.getZ(), clTOFYZ[1]);
436 return;
437 }
438 }
439 if (gidTable[GTrackID::TRD].isIndexSet()) {
440 LOG(debug) << "TRD available";
441 const auto& trkTRD = mRecoCont->getITSTPCTRDTrack<o2::trd::TrackTRD>(gidTable[GTrackID::ITSTPCTRD]);
442 if (mDumpTrackPoints) {
443 (*trackDataExtended).trkTRD = trkTRD;
444 }
445 for (int iLayer = o2::trd::constants::NLAYER - 1; iLayer >= 0; --iLayer) {
446 int trkltIdx = trkTRD.getTrackletIndex(iLayer);
447 if (trkltIdx < 0) {
448 // no TRD tracklet in this layer
449 continue;
450 }
451 const auto& trdSP = mRecoCont->getTRDCalibratedTracklets()[trkltIdx];
452 const auto& trdTrklt = mRecoCont->getTRDTracklets()[trkltIdx];
453 if (mDumpTrackPoints) {
454 (*trackDataExtended).trkltTRD.push_back(trdTrklt);
455 (*trackDataExtended).clsTRD.push_back(trdSP);
456 }
457 auto trkltDet = trdTrklt.getDetector();
458 auto trkltSec = trkltDet / (o2::trd::constants::NLAYER * o2::trd::constants::NSTACK);
459 if (trkltSec != o2::math_utils::angle2Sector(trkWork.getAlpha())) {
460 if (!trkWork.rotate(o2::math_utils::sector2Angle(trkltSec))) {
461 LOG(debug) << "Track could not be rotated in TRD tracklet coordinate system in layer " << iLayer;
462 return;
463 }
464 }
465 if (!propagator->PropagateToXBxByBz(trkWork, trdSP.getX(), mParams->maxSnp, mParams->maxStep, mMatCorr)) {
466 LOG(debug) << "Failed propagation to TRD layer " << iLayer;
467 return;
468 }
469
470 const auto* pad = mGeoTRD->getPadPlane(trkltDet);
471 float tilt = tan(TMath::DegToRad() * pad->getTiltingAngle()); // tilt is signed! and returned in degrees
472 float tiltCorrUp = tilt * (trdSP.getZ() - trkWork.getZ());
473 float zPosCorrUp = trdSP.getZ() + mRecoParam.getZCorrCoeffNRC() * trkWork.getTgl(); // maybe Z can be corrected on avarage already by the tracklet transformer?
474 float padLength = pad->getRowSize(trdTrklt.getPadRow());
475 if (!((trkWork.getSigmaZ2() < (padLength * padLength / 12.f)) && (std::fabs(trdSP.getZ() - trkWork.getZ()) < padLength))) {
476 tiltCorrUp = 0.f;
477 }
478 std::array<float, 2> trkltTRDYZ{trdSP.getY() - tiltCorrUp, zPosCorrUp};
479 std::array<float, 3> trkltTRDCov;
480 mRecoParam.recalcTrkltCov(tilt, trkWork.getSnp(), pad->getRowSize(trdTrklt.getPadRow()), trkltTRDCov);
481 if (!trkWork.update(trkltTRDYZ, trkltTRDCov)) {
482 LOG(debug) << "Failed to update track at TRD layer " << iLayer;
483 return;
484 }
485 }
486 }
487
488 if (mDumpTrackPoints) {
489 (*trackDataExtended).trkOuter = trkWork;
490 }
491
492 // go back through the TPC and store updated track positions
493 bool outerParamStored = false;
494 for (int iRow = param::NPadRows; iRow--;) {
495 if (!mCache[iRow].clAvailable) {
496 continue;
497 }
498 if (mProcessSeeds && !outerParamStored) {
499 // for debug purposes we store the track parameters
500 // of the refitted ITS-(TRD)-(TOF) track at the
501 // outermose TPC cluster if we are processing all seeds
502 // i.e. if we in any case also process the ITS-TPC only
503 // part of the same track
504 trackData.par = trkWork;
505 outerParamStored = true;
506 }
507 if (!trkWork.rotate(mCache[iRow].clAngle)) {
508 LOG(debug) << "Failed to rotate track during back propagation";
509 return;
510 }
511 if (!propagator->PropagateToXBxByBz(trkWork, param::RowX[iRow], mParams->maxSnp, mParams->maxStep, mMatCorr)) {
512 LOG(debug) << "Failed on back propagation";
513 // printf("trkX(%.2f), clX(%.2f), clY(%.2f), clZ(%.2f), alphaTOF(%.2f)\n", trkWork.getX(), param::RowX[iRow], clTOFYZ[0], clTOFYZ[1], clTOFAlpha);
514 return;
515 }
516 mCache[iRow].y[ExtIn] = trkWork.getY();
517 mCache[iRow].z[ExtIn] = trkWork.getZ();
518 mCache[iRow].sy2[ExtIn] = trkWork.getSigmaY2();
519 mCache[iRow].szy[ExtIn] = trkWork.getSigmaZY();
520 mCache[iRow].sz2[ExtIn] = trkWork.getSigmaZ2();
521 mCache[iRow].snp[ExtIn] = trkWork.getSnp();
522 }
523
524 // calculate weighted mean at each pad row (assume for now y and z are uncorrelated) and store residuals to TPC clusters
525 unsigned short deltaRow = 0;
526 for (int iRow = 0; iRow < param::NPadRows; ++iRow) {
527 if (!mCache[iRow].clAvailable) {
528 ++deltaRow;
529 continue;
530 }
531 float wTotY = 1.f / mCache[iRow].sy2[ExtOut] + 1.f / mCache[iRow].sy2[ExtIn];
532 float wTotZ = 1.f / mCache[iRow].sz2[ExtOut] + 1.f / mCache[iRow].sz2[ExtIn];
533 mCache[iRow].y[Int] = (mCache[iRow].y[ExtOut] / mCache[iRow].sy2[ExtOut] + mCache[iRow].y[ExtIn] / mCache[iRow].sy2[ExtIn]) / wTotY;
534 mCache[iRow].z[Int] = (mCache[iRow].z[ExtOut] / mCache[iRow].sz2[ExtOut] + mCache[iRow].z[ExtIn] / mCache[iRow].sz2[ExtIn]) / wTotZ;
535
536 // simple average w/o weighting for angle
537 mCache[iRow].snp[Int] = (mCache[iRow].snp[ExtOut] + mCache[iRow].snp[ExtIn]) / 2.f;
538
539 const auto dY = mCache[iRow].clY - mCache[iRow].y[Int];
540 const auto dZ = mCache[iRow].clZ - mCache[iRow].z[Int];
541 const auto y = mCache[iRow].y[Int];
542 const auto z = mCache[iRow].z[Int];
543 const auto snp = mCache[iRow].snp[Int];
544 const auto sec = mCache[iRow].clSec;
545 clusterResiduals.emplace_back(dY, dZ, y, z, snp, sec, deltaRow);
546
547 deltaRow = 1;
548 }
549 trackData.chi2TRD = gidTable[GTrackID::TRD].isIndexSet() ? mRecoCont->getITSTPCTRDTrack<o2::trd::TrackTRD>(gidTable[GTrackID::ITSTPCTRD]).getChi2() : 0;
550 trackData.chi2TPC = trkTPC.getChi2();
551 trackData.chi2ITS = trkITS.getChi2();
552 trackData.nClsTPC = trkTPC.getNClusterReferences();
553 trackData.nClsITS = trkITS.getNumberOfClusters();
554 trackData.nTrkltsTRD = gidTable[GTrackID::TRD].isIndexSet() ? mRecoCont->getITSTPCTRDTrack<o2::trd::TrackTRD>(gidTable[GTrackID::ITSTPCTRD]).getNtracklets() : 0;
555 trackData.clAvailTOF = gidTable[GTrackID::TOF].isIndexSet() ? 1 : 0;
556 trackData.dEdxTPC = trkTPC.getdEdx().dEdxTotTPC;
557
558 TrackParams params; // for refitted track parameters and flagging rejected clusters
559 if (mParams->skipOutlierFiltering || validateTrack(trackData, params, clusterResiduals)) {
560 // track is good
561 int nClValidated = 0;
562 int iRow = 0;
563 for (unsigned int iCl = 0; iCl < clusterResiduals.size(); ++iCl) {
564 iRow += clusterResiduals[iCl].dRow;
565 if (params.flagRej[iCl]) {
566 // skip masked cluster residual
567 continue;
568 }
569 ++nClValidated;
570 const float tgPhi = clusterResiduals[iCl].snp / std::sqrt((1.f - clusterResiduals[iCl].snp) * (1.f + clusterResiduals[iCl].snp));
571 const auto dy = clusterResiduals[iCl].dy;
572 const auto dz = clusterResiduals[iCl].dz;
573 const auto y = clusterResiduals[iCl].y;
574 const auto z = clusterResiduals[iCl].z;
575 const auto sec = clusterResiduals[iCl].sec;
576 if ((std::abs(dy) < param::MaxResid) && (std::abs(dz) < param::MaxResid) && (std::abs(y) < param::MaxY) && (std::abs(z) < param::MaxZ) && (std::abs(tgPhi) < param::MaxTgSlp)) {
577 mClRes.emplace_back(dy, dz, tgPhi, y, z, iRow, sec);
578 } else {
579 ++mRejectedResiduals;
580 }
581 }
582 trackData.clIdx.setEntries(nClValidated);
583 mTrackData.push_back(std::move(trackData));
584 if (mDumpTrackPoints) {
585 (*trackDataExtended).clIdx.setEntries(nClValidated);
586 mTrackDataExtended.push_back(std::move(*trackDataExtended));
587 }
588 mGIDsSuccess.push_back(mGIDs[iSeed]);
589 mTrackDataCompact.emplace_back(mClRes.size() - nClValidated, nClValidated, mGIDs[iSeed].getSource());
590 }
591 if (mParams->writeUnfiltered) {
592 TrackData trkDataTmp = trackData;
593 trkDataTmp.clIdx.setFirstEntry(mClResUnfiltered.size());
594 trkDataTmp.clIdx.setEntries(clusterResiduals.size());
595 mTrackDataUnfiltered.push_back(std::move(trkDataTmp));
596 mClResUnfiltered.insert(mClResUnfiltered.end(), clusterResiduals.begin(), clusterResiduals.end());
597 }
598}
599
601{
602 // extrapolate ITS-only track through TPC and store residuals to TPC clusters in the output vectors
603 LOGP(debug, "Starting track extrapolation for GID {}", mGIDs[iSeed].asString());
604 const auto& gidTable = mGIDtables[iSeed];
605 TrackData trackData;
606 std::unique_ptr<TrackDataExtended> trackDataExtended;
607 std::vector<TPCClusterResiduals> clusterResiduals;
608 trackData.clIdx.setFirstEntry(mClRes.size());
609 const auto& trkITS = mRecoCont->getITSTrack(gidTable[GTrackID::ITS]);
610 const auto& trkTPC = mRecoCont->getTPCTrack(gidTable[GTrackID::TPC]);
611 if (mDumpTrackPoints) {
612 trackDataExtended = std::make_unique<TrackDataExtended>();
613 (*trackDataExtended).gid = mGIDs[iSeed];
614 (*trackDataExtended).clIdx.setFirstEntry(mClRes.size());
615 (*trackDataExtended).trkITS = trkITS;
616 (*trackDataExtended).trkTPC = trkTPC;
617 auto nCl = trkITS.getNumberOfClusters();
618 auto clEntry = trkITS.getFirstClusterEntry();
619 for (int iCl = nCl - 1; iCl >= 0; iCl--) { // clusters are stored from outer to inner layers
620 const auto& clsITS = mITSClustersArray[mITSTrackClusIdx[clEntry + iCl]];
621 (*trackDataExtended).clsITS.push_back(clsITS);
622 }
623 }
624 trackData.gid = mGIDs[iSeed];
625 trackData.par = mSeeds[iSeed];
626
627 auto& trkWork = mSeeds[iSeed];
628 float clusterTimeBinOffset = mTrackTimes[iSeed] / mTPCTimeBinMUS;
629 auto propagator = o2::base::Propagator::Instance();
630 unsigned short rowPrev = 0; // used to calculate dRow of two consecutive cluster residuals
631 unsigned short nMeasurements = 0;
632 uint8_t clRowPrev = constants::MAXGLOBALPADROW; // used to identify and skip split clusters on the same pad row
633 for (int iCl = trkTPC.getNClusterReferences(); iCl--;) {
634 uint8_t sector, row;
635 uint32_t clusterIndexInRow;
636 const auto& cl = trkTPC.getCluster(mTPCTracksClusIdx, iCl, *mTPCClusterIdxStruct, sector, row);
637 if (clRowPrev == row) {
638 // if there are split clusters we only take the first one on the pad row
639 continue;
640 } else if (clRowPrev < constants::MAXGLOBALPADROW && clRowPrev > row) {
641 // we seem to be looping, abort this track
642 LOGP(debug, "TPC track with pT={} GeV and {} clusters has cluster {} on row {} while the previous cluster was on row {}",
643 mSeeds[iSeed].getPt(), trkTPC.getNClusterReferences(), iCl, row, clRowPrev);
644 return;
645 } else {
646 // this is the first cluster we see on this pad row
647 clRowPrev = row;
648 }
649 float x = 0, y = 0, z = 0;
650 mFastTransform->TransformIdeal(sector, row, cl.getPad(), cl.getTime(), x, y, z, clusterTimeBinOffset);
651 if (!trkWork.rotate(o2::math_utils::sector2Angle(sector))) {
652 return;
653 }
654 if (!propagator->PropagateToXBxByBz(trkWork, x, mParams->maxSnp, mParams->maxStep, mMatCorr)) {
655 return;
656 }
657
658 const auto dY = y - trkWork.getY();
659 const auto dZ = z - trkWork.getZ();
660 const auto ty = trkWork.getY();
661 const auto tz = trkWork.getZ();
662 const auto snp = trkWork.getSnp();
663 const auto sec = sector;
664
665 clusterResiduals.emplace_back(dY, dZ, ty, tz, snp, sec, row - rowPrev);
666
667 rowPrev = row;
668 ++nMeasurements;
669 }
670 trackData.chi2TPC = trkTPC.getChi2();
671 trackData.chi2ITS = trkITS.getChi2();
672 trackData.nClsTPC = trkTPC.getNClusterReferences();
673 trackData.nClsITS = trkITS.getNumberOfClusters();
674 trackData.clIdx.setEntries(nMeasurements);
675 trackData.dEdxTPC = trkTPC.getdEdx().dEdxTotTPC;
676 if (mDumpTrackPoints) {
677 (*trackDataExtended).trkOuter = trkWork;
678 }
679
680 TrackParams params; // for refitted track parameters and flagging rejected clusters
681 if (clusterResiduals.size() > constants::MAXGLOBALPADROW) {
682 LOGP(warn, "Extrapolated ITS-TPC track and found more reesiduals than possible ({})", clusterResiduals.size());
683 return;
684 }
685 if (mParams->skipOutlierFiltering || validateTrack(trackData, params, clusterResiduals)) {
686 // track is good
687 int nClValidated = 0;
688 int iRow = 0;
689 for (unsigned int iCl = 0; iCl < clusterResiduals.size(); ++iCl) {
690 iRow += clusterResiduals[iCl].dRow;
691 if (params.flagRej[iCl]) {
692 // skip masked cluster residual
693 continue;
694 }
695 ++nClValidated;
696 const float tgPhi = clusterResiduals[iCl].snp / std::sqrt((1.f - clusterResiduals[iCl].snp) * (1.f + clusterResiduals[iCl].snp));
697 const auto dy = clusterResiduals[iCl].dy;
698 const auto dz = clusterResiduals[iCl].dz;
699 const auto y = clusterResiduals[iCl].y;
700 const auto z = clusterResiduals[iCl].z;
701 const auto sec = clusterResiduals[iCl].sec;
702 if ((std::abs(dy) < param::MaxResid) && (std::abs(dz) < param::MaxResid) && (std::abs(y) < param::MaxY) && (std::abs(z) < param::MaxZ) && (std::abs(tgPhi) < param::MaxTgSlp)) {
703 mClRes.emplace_back(dy, dz, tgPhi, y, z, iRow, sec);
704 } else {
705 ++mRejectedResiduals;
706 }
707 }
708 trackData.clIdx.setEntries(nClValidated);
709 mTrackData.push_back(std::move(trackData));
710 mGIDsSuccess.push_back(mGIDs[iSeed]);
711 mTrackDataCompact.emplace_back(mClRes.size() - nClValidated, nClValidated, mGIDs[iSeed].getSource());
712 if (mDumpTrackPoints) {
713 (*trackDataExtended).clIdx.setEntries(nClValidated);
714 mTrackDataExtended.push_back(std::move(*trackDataExtended));
715 }
716 }
717 if (mParams->writeUnfiltered) {
718 TrackData trkDataTmp = trackData;
719 trkDataTmp.clIdx.setFirstEntry(mClResUnfiltered.size());
720 trkDataTmp.clIdx.setEntries(clusterResiduals.size());
721 mTrackDataUnfiltered.push_back(std::move(trkDataTmp));
722 mClResUnfiltered.insert(mClResUnfiltered.end(), clusterResiduals.begin(), clusterResiduals.end());
723 }
724}
725
726bool TrackInterpolation::validateTrack(const TrackData& trk, TrackParams& params, const std::vector<TPCClusterResiduals>& clsRes) const
727{
728 if (clsRes.size() < mParams->minNCl) {
729 // no enough clusters for this track to be considered
730 LOG(debug) << "Skipping track with too few clusters: " << clsRes.size();
731 return false;
732 }
733
734 bool resHelix = compareToHelix(trk, params, clsRes);
735 if (!resHelix) {
736 LOG(debug) << "Skipping track too far from helix approximation";
737 return false;
738 }
739 if (fabsf(mBz) > 0.01 && fabsf(params.qpt) > mParams->maxQ2Pt) {
740 LOG(debug) << "Skipping track with too high q/pT: " << params.qpt;
741 return false;
742 }
743 if (!outlierFiltering(trk, params, clsRes)) {
744 return false;
745 }
746 return true;
747}
748
749bool TrackInterpolation::compareToHelix(const TrackData& trk, TrackParams& params, const std::vector<TPCClusterResiduals>& clsRes) const
750{
751 std::array<float, param::NPadRows> residHelixY;
752 std::array<float, param::NPadRows> residHelixZ;
753
754 std::array<float, param::NPadRows> xLab;
755 std::array<float, param::NPadRows> yLab;
756 std::array<float, param::NPadRows> sPath;
757
758 float curvature = fabsf(trk.par.getQ2Pt() * mBz * o2::constants::physics::LightSpeedCm2S * 1e-14f);
759 int secFirst = clsRes[0].sec;
760 float phiSect = (secFirst + .5f) * o2::constants::math::SectorSpanRad;
761 float snPhi = sin(phiSect);
762 float csPhi = cos(phiSect);
763 sPath[0] = 0.f;
764
765 int iRow = 0;
766 int nCl = clsRes.size();
767 for (unsigned int iP = 0; iP < nCl; ++iP) {
768 iRow += clsRes[iP].dRow;
769 float yTrk = clsRes[iP].y;
770 // LOGF(info, "iRow(%i), yTrk(%f)", iRow, yTrk);
771 xLab[iP] = param::RowX[iRow];
772 if (clsRes[iP].sec != secFirst) {
773 float phiSectCurrent = (clsRes[iP].sec + .5f) * o2::constants::math::SectorSpanRad;
774 float cs = cos(phiSectCurrent - phiSect);
775 float sn = sin(phiSectCurrent - phiSect);
776 xLab[iP] = param::RowX[iRow] * cs - yTrk * sn;
777 yLab[iP] = yTrk * cs + param::RowX[iRow] * sn;
778 } else {
779 xLab[iP] = param::RowX[iRow];
780 yLab[iP] = yTrk;
781 }
782 // this is needed only later, but we retrieve it already now to save another loop
783 params.zTrk[iP] = clsRes[iP].z;
784 params.xTrk[iP] = param::RowX[iRow];
785 params.dy[iP] = clsRes[iP].dy;
786 params.dz[iP] = clsRes[iP].dz;
787 // done retrieving values for later
788 if (iP > 0) {
789 float dx = xLab[iP] - xLab[iP - 1];
790 float dy = yLab[iP] - yLab[iP - 1];
791 float ds2 = dx * dx + dy * dy;
792 float ds = sqrt(ds2); // circular path (linear approximation)
793 // if the curvature of the track or the (approximated) chord length is too large the more exact formula is used:
794 // chord length = 2r * asin(ds/(2r))
795 // using the first two terms of the tailer expansion for asin(x) ~ x + x^3 / 6
796 if (ds * curvature > 0.05) {
797 ds *= (1.f + ds2 * curvature * curvature / 24.f);
798 }
799 sPath[iP] = sPath[iP - 1] + ds;
800 }
801 }
802 if (fabsf(mBz) < 0.01) {
803 // for B=0 we don't need to try a circular fit...
804 return true;
805 }
806 float xcSec = 0.f;
807 float ycSec = 0.f;
808 float r = 0.f;
809 TrackResiduals::fitCircle(nCl, xLab, yLab, xcSec, ycSec, r, residHelixY);
810 // LOGF(info, "Done with circle fit. nCl(%i), xcSec(%f), ycSec(%f), r(%f).", nCl, xcSec, ycSec, r);
811 /*
812 for (int i=0; i<nCl; ++i) {
813 LOGF(info, "i(%i), xLab(%f), yLab(%f).", i, xLab[i], yLab[i]);
814 }
815 */
816 // determine curvature
817 float phiI = TMath::ATan2(yLab[0], xLab[0]);
818 float phiF = TMath::ATan2(yLab[nCl - 1], xLab[nCl - 1]);
819 if (phiI < 0) {
821 }
822 if (phiF < 0) {
824 }
825 float dPhi = phiF - phiI;
826 float curvSign = -1.f;
827 if (dPhi > 0) {
828 if (dPhi < o2::constants::math::PI) {
829 curvSign = 1.f;
830 }
831 } else if (dPhi < -o2::constants::math::PI) {
832 curvSign = 1.f;
833 }
834 params.qpt = std::copysign(1.f / (r * mBz * o2::constants::physics::LightSpeedCm2S * 1e-14f), curvSign);
835
836 // calculate circle coordinates in the lab frame
837 float xc = xcSec * csPhi - ycSec * snPhi;
838 float yc = xcSec * snPhi + ycSec * csPhi;
839
840 std::array<float, 2> pol1Z;
841 TrackResiduals::fitPoly1(nCl, sPath, params.zTrk, pol1Z);
842
843 params.tgl = pol1Z[0];
844
845 // max deviations in both directions from helix fit in y and z
846 float hMinY = 1e9f;
847 float hMaxY = -1e9f;
848 float hMinZ = 1e9f;
849 float hMaxZ = -1e9f;
850 // extract residuals in Z and fill track slopes in sector frame
851 int secCurr = secFirst;
852 iRow = 0;
853 for (unsigned int iCl = 0; iCl < nCl; ++iCl) {
854 iRow += clsRes[iCl].dRow;
855 float resZ = params.zTrk[iCl] - (pol1Z[1] + sPath[iCl] * pol1Z[0]);
856 residHelixZ[iCl] = resZ;
857 if (resZ < hMinZ) {
858 hMinZ = resZ;
859 }
860 if (resZ > hMaxZ) {
861 hMaxZ = resZ;
862 }
863 if (residHelixY[iCl] < hMinY) {
864 hMinY = residHelixY[iCl];
865 }
866 if (residHelixY[iCl] > hMaxY) {
867 hMaxY = residHelixY[iCl];
868 }
869 int sec = clsRes[iCl].sec;
870 if (sec != secCurr) {
871 secCurr = sec;
872 phiSect = (.5f + sec) * o2::constants::math::SectorSpanRad;
873 snPhi = sin(phiSect);
874 csPhi = cos(phiSect);
875 xcSec = xc * csPhi + yc * snPhi; // recalculate circle center in the sector frame
876 }
877 float cstalp = (param::RowX[iRow] - xcSec) / r;
878 if (fabsf(cstalp) > 1.f - sFloatEps) {
879 // track cannot reach this pad row
880 cstalp = std::copysign(1.f - sFloatEps, cstalp);
881 }
882 params.tglArr[iCl] = cstalp / sqrt((1 - cstalp) * (1 + cstalp)); // 1 / tan(acos(cstalp)) = cstalp / sqrt(1 - cstalp^2)
883
884 // In B+ the slope of q- should increase with x. Just look on q * B
885 if (params.qpt * mBz > 0) {
886 params.tglArr[iCl] *= -1.f;
887 }
888 }
889 // 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);
890 // LOGF(info, "New pt/Q (%f), old pt/Q (%f)", 1./params.qpt, 1./trk.qPt);
891 return fabsf(hMaxY - hMinY) < mParams->maxDevHelixY && fabsf(hMaxZ - hMinZ) < mParams->maxDevHelixZ;
892}
893
894bool TrackInterpolation::outlierFiltering(const TrackData& trk, TrackParams& params, const std::vector<TPCClusterResiduals>& clsRes) const
895{
896 if (clsRes.size() < mParams->nMALong) {
897 LOG(debug) << "Skipping track with too few clusters for long moving average: " << clsRes.size();
898 return false;
899 }
900 float rmsLong = checkResiduals(trk, params, clsRes);
901 if (static_cast<float>(params.flagRej.count()) / clsRes.size() > mParams->maxRejFrac) {
902 LOGP(debug, "Skipping track with too many clusters rejected: {} out of {}", params.flagRej.count(), clsRes.size());
903 return false;
904 }
905 if (rmsLong > mParams->maxRMSLong) {
906 LOG(debug) << "Skipping track with too large RMS: " << rmsLong;
907 return false;
908 }
909 return true;
910}
911
912float TrackInterpolation::checkResiduals(const TrackData& trk, TrackParams& params, const std::vector<TPCClusterResiduals>& clsRes) const
913{
914 float rmsLong = 0.f;
915
916 int nCl = clsRes.size();
917 int iClFirst = 0;
918 int iClLast = nCl - 1;
919 int secStart = clsRes[0].sec;
920
921 // arrays with differences / abs(differences) of points to their neighbourhood, initialized to zero
922 std::array<float, param::NPadRows> yDiffLL{};
923 std::array<float, param::NPadRows> zDiffLL{};
924 std::array<float, param::NPadRows> absDevY{};
925 std::array<float, param::NPadRows> absDevZ{};
926
927 for (unsigned int iCl = 0; iCl < nCl; ++iCl) {
928 if (iCl < iClLast && clsRes[iCl].sec == secStart) {
929 continue;
930 }
931 // sector changed or last cluster reached
932 // now run estimators for all points in the same sector
933 int nClSec = iCl - iClFirst;
934 if (iCl == iClLast) {
935 ++nClSec;
936 }
937 diffToLocLine(nClSec, iClFirst, params.xTrk, params.dy, yDiffLL);
938 diffToLocLine(nClSec, iClFirst, params.xTrk, params.dz, zDiffLL);
939 iClFirst = iCl;
940 secStart = clsRes[iCl].sec;
941 }
942 // store abs deviations
943 int nAccY = 0;
944 int nAccZ = 0;
945 for (int iCl = nCl; iCl--;) {
946 if (fabsf(yDiffLL[iCl]) > param::sEps) {
947 absDevY[nAccY++] = fabsf(yDiffLL[iCl]);
948 }
949 if (fabsf(zDiffLL[iCl]) > param::sEps) {
950 absDevZ[nAccZ++] = fabsf(zDiffLL[iCl]);
951 }
952 }
953 if (nAccY < mParams->minNumberOfAcceptedResiduals || nAccZ < mParams->minNumberOfAcceptedResiduals) {
954 // mask all clusters
955 LOGP(debug, "Accepted {} clusters for dY {} clusters for dZ, but required at least {} for both", nAccY, nAccZ, mParams->minNumberOfAcceptedResiduals);
956 params.flagRej.set();
957 return 0.f;
958 }
959 // estimate rms on 90% of the smallest deviations
960 int nKeepY = static_cast<int>(.9 * nAccY);
961 int nKeepZ = static_cast<int>(.9 * nAccZ);
962 std::nth_element(absDevY.begin(), absDevY.begin() + nKeepY, absDevY.begin() + nAccY);
963 std::nth_element(absDevZ.begin(), absDevZ.begin() + nKeepZ, absDevZ.begin() + nAccZ);
964 float rmsYkeep = 0.f;
965 float rmsZkeep = 0.f;
966 for (int i = nKeepY; i--;) {
967 rmsYkeep += absDevY[i] * absDevY[i];
968 }
969 for (int i = nKeepZ; i--;) {
970 rmsZkeep += absDevZ[i] * absDevZ[i];
971 }
972 rmsYkeep = std::sqrt(rmsYkeep / nKeepY);
973 rmsZkeep = std::sqrt(rmsZkeep / nKeepZ);
974 if (rmsYkeep < param::sEps || rmsZkeep < param::sEps) {
975 LOG(warning) << "Too small RMS: " << rmsYkeep << "(y), " << rmsZkeep << "(z).";
976 params.flagRej.set();
977 return 0.f;
978 }
979 float rmsYkeepI = 1.f / rmsYkeep;
980 float rmsZkeepI = 1.f / rmsZkeep;
981 int nAcc = 0;
982 std::array<float, param::NPadRows> yAcc;
983 std::array<float, param::NPadRows> yDiffLong;
984 for (int iCl = 0; iCl < nCl; ++iCl) {
985 yDiffLL[iCl] *= rmsYkeepI;
986 zDiffLL[iCl] *= rmsZkeepI;
987 if (yDiffLL[iCl] * yDiffLL[iCl] + zDiffLL[iCl] * zDiffLL[iCl] > mParams->maxStdDevMA) {
988 params.flagRej.set(iCl);
989 } else {
990 yAcc[nAcc++] = params.dy[iCl];
991 }
992 }
993 if (nAcc > mParams->nMALong) {
994 diffToMA(nAcc, yAcc, yDiffLong);
995 float average = 0.f;
996 float rms = 0.f;
997 for (int i = 0; i < nAcc; ++i) {
998 average += yDiffLong[i];
999 rms += yDiffLong[i] * yDiffLong[i];
1000 }
1001 average /= nAcc;
1002 rmsLong = rms / nAcc - average * average;
1003 rmsLong = (rmsLong > 0) ? std::sqrt(rmsLong) : 0.f;
1004 }
1005 return rmsLong;
1006}
1007
1008void TrackInterpolation::diffToLocLine(const int np, int idxOffset, const std::array<float, param::NPadRows>& x, const std::array<float, param::NPadRows>& y, std::array<float, param::NPadRows>& diffY) const
1009{
1010 // Calculate the difference between the points and the linear extrapolations from the neighbourhood.
1011 // Nothing more than multiple 1-d fits at once. Instead of building 4 sums (x, x^2, y, xy), 4 * nPoints sums are calculated at once
1012 // compare to TrackResiduals::fitPoly1() method
1013
1014 // adding one entry to the vectors saves an additional if statement when calculating the cumulants
1015 std::vector<float> sumX1vec(np + 1);
1016 std::vector<float> sumX2vec(np + 1);
1017 std::vector<float> sumY1vec(np + 1);
1018 std::vector<float> sumXYvec(np + 1);
1019 auto sumX1 = &(sumX1vec[1]);
1020 auto sumX2 = &(sumX2vec[1]);
1021 auto sumY1 = &(sumY1vec[1]);
1022 auto sumXY = &(sumXYvec[1]);
1023
1024 // accumulate sums for all points
1025 for (int iCl = 0; iCl < np; ++iCl) {
1026 int idx = iCl + idxOffset;
1027 sumX1[iCl] = sumX1[iCl - 1] + x[idx];
1028 sumX2[iCl] = sumX2[iCl - 1] + x[idx] * x[idx];
1029 sumY1[iCl] = sumY1[iCl - 1] + y[idx];
1030 sumXY[iCl] = sumXY[iCl - 1] + x[idx] * y[idx];
1031 }
1032
1033 for (int iCl = 0; iCl < np; ++iCl) {
1034 int iClLeft = iCl - mParams->nMAShort;
1035 int iClRight = iCl + mParams->nMAShort;
1036 if (iClLeft < 0) {
1037 iClLeft = 0;
1038 }
1039 if (iClRight >= np) {
1040 iClRight = np - 1;
1041 }
1042 int nPoints = iClRight - iClLeft;
1043 if (nPoints < mParams->nMAShort) {
1044 continue;
1045 }
1046 float nPointsInv = 1.f / nPoints;
1047 int iClLeftP = iClLeft - 1;
1048 int iClCurrP = iCl - 1;
1049 // extract sum from iClLeft to iClRight from cumulants, excluding iCl from the fit
1050 float sX1 = sumX1[iClRight] - sumX1[iClLeftP] - (sumX1[iCl] - sumX1[iClCurrP]);
1051 float sX2 = sumX2[iClRight] - sumX2[iClLeftP] - (sumX2[iCl] - sumX2[iClCurrP]);
1052 float sY1 = sumY1[iClRight] - sumY1[iClLeftP] - (sumY1[iCl] - sumY1[iClCurrP]);
1053 float sXY = sumXY[iClRight] - sumXY[iClLeftP] - (sumXY[iCl] - sumXY[iClCurrP]);
1054 float det = sX2 - nPointsInv * sX1 * sX1;
1055 if (fabsf(det) < 1e-12f) {
1056 continue;
1057 }
1058 float slope = (sXY - nPointsInv * sX1 * sY1) / det;
1059 float offset = nPointsInv * sY1 - nPointsInv * slope * sX1;
1060 diffY[iCl + idxOffset] = y[iCl + idxOffset] - slope * x[iCl + idxOffset] - offset;
1061 }
1062}
1063
1064void TrackInterpolation::diffToMA(const int np, const std::array<float, param::NPadRows>& y, std::array<float, param::NPadRows>& diffMA) const
1065{
1066 // Calculate
1067 std::vector<float> sumVec(np + 1);
1068 auto sum = &(sumVec[1]);
1069 for (int i = 0; i < np; ++i) {
1070 sum[i] = sum[i - 1] + y[i];
1071 }
1072 for (int i = 0; i < np; ++i) {
1073 diffMA[i] = 0;
1074 int iLeft = i - mParams->nMALong;
1075 int iRight = i + mParams->nMALong;
1076 if (iLeft < 0) {
1077 iLeft = 0;
1078 }
1079 if (iRight >= np) {
1080 iRight = np - 1;
1081 }
1082 int nPoints = iRight - iLeft;
1083 if (nPoints < mParams->nMALong) {
1084 // this cannot happen, since at least mParams->nMALong points are required as neighbours for this function to be called
1085 continue;
1086 }
1087 float movingAverage = (sum[iRight] - sum[iLeft - 1] - (sum[i] - sum[i - 1])) / nPoints;
1088 diffMA[i] = y[i] - movingAverage;
1089 }
1090}
1091
1093{
1094 mTrackData.clear();
1095 mTrackDataCompact.clear();
1096 mTrackDataExtended.clear();
1097 mClRes.clear();
1098 mTrackDataUnfiltered.clear();
1099 mClResUnfiltered.clear();
1100 mGIDsSuccess.clear();
1101 for (auto& vec : mTrackIndices) {
1102 vec.clear();
1103 }
1104 mGIDs.clear();
1105 mGIDtables.clear();
1106 mTrackTimes.clear();
1107 mSeeds.clear();
1108}
1109
1110//______________________________________________
1112{
1113 // 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
1114 if (v.refVDrift != mTPCVDriftRef) {
1115 mTPCVDriftRef = v.refVDrift;
1116 mTPCDriftTimeOffsetRef = v.refTimeOffset;
1117 LOGP(info, "Imposing reference VDrift={}/TDrift={} for TPC residuals extraction", mTPCVDriftRef, mTPCDriftTimeOffsetRef);
1118 o2::tpc::TPCFastTransformHelperO2::instance()->updateCalibration(*mFastTransform, 0, 1.0, mTPCVDriftRef, mTPCDriftTimeOffsetRef);
1119 }
1120}
std::string asString(TDataMember const &dm, char *pointer)
Global TRD definitions and constants.
int32_t i
Definition of the parameter class for the detector electronics.
uint16_t slope
Definition RawData.h:1
Definition of the TrackInterpolation class.
Definition of the TrackResiduals class.
calibration data from laser track calibration
std::ostringstream debug
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:143
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:58
static constexpr ID TRD
Definition DetID.h:65
static constexpr ID TOF
Definition DetID.h:66
int updateCalibration(TPCFastTransform &transform, Long_t TimeStamp, float vDriftFactor=1.f, float vDriftRef=0.f, float driftTimeOffset=0.f)
Updates the transformation with the new time stamp.
static TPCFastTransformHelperO2 * instance()
Singleton.
void prepareInputTrackSample(const o2::globaltracking::RecoContainer &inp)
Prepare input track sample (not relying on CreateTracksVariadic functionality)
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.
void diffToMA(const int np, const std::array< float, param::NPadRows > &y, std::array< float, param::NPadRows > &diffMA) const
For a given set of points, calculate their deviation from the moving average (build from the neighbou...
void diffToLocLine(const int np, int idxOffset, const std::array< float, param::NPadRows > &x, const std::array< float, param::NPadRows > &y, std::array< float, param::NPadRows > &diffY) const
For a given set of points, calculate the differences from each point to the fitted lines from all oth...
@ 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...
float checkResiduals(const TrackData &trk, TrackParams &params, const std::vector< TPCClusterResiduals > &clsRes) const
bool compareToHelix(const TrackData &trk, TrackParams &params, const std::vector< TPCClusterResiduals > &clsRes) const
void process()
Main processing function.
void setTPCVDrift(const o2::tpc::VDriftCorrFact &v)
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, TrackParams &params, const std::vector< TPCClusterResiduals > &clsRes) const
void init(o2::dataformats::GlobalTrackID::mask_t src, o2::dataformats::GlobalTrackID::mask_t srcMap)
Initialize everything, set the requested track sources.
bool validateTrack(const TrackData &trk, TrackParams &params, const std::vector< TPCClusterResiduals > &clsRes) const
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
void recalcTrkltCov(const float tilt, const float snp, const float rowSize, std::array< float, 3 > &cov) const
Recalculate tracklet covariance based on phi angle of related track.
Definition RecoParam.cxx:55
float getZCorrCoeffNRC() const
Get tracklet z correction coefficient for track-eta based corraction.
Definition RecoParam.h:49
void setBfield(float bz)
Load parameterization for given magnetic field.
Definition RecoParam.cxx:23
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
GLuint GLuint GLfloat weight
Definition glcorearb.h:5477
GLenum const GLfloat * params
Definition glcorearb.h:272
GLintptr offset
Definition glcorearb.h:660
GLboolean GLboolean g
Definition glcorearb.h:1233
GLboolean r
Definition glcorearb.h:1233
GLint GLuint mask
Definition glcorearb.h:291
GLdouble GLdouble GLdouble z
Definition glcorearb.h:843
constexpr float TwoPI
constexpr float SectorSpanRad
constexpr float PI
constexpr float LightSpeedCm2S
constexpr double MassPionCharged
void convertCompactClusters(gsl::span< const itsmft::CompClusterExt > clusters, gsl::span< const unsigned char >::iterator &pattIt, std::vector< o2::BaseCluster< float > > &output, const itsmft::TopologyDictionary *dict)
convert compact clusters to 3D spacepoints
Definition IOUtils.cxx:49
int angle2Sector(float phi)
Definition Utils.h:183
float sector2Angle(int sect)
Definition Utils.h:193
constexpr int MAXGLOBALPADROW
Definition Constants.h:34
Global TPC definitions and constants.
Definition SimTraits.h:167
Enum< T >::Iterator begin(Enum< T >)
Definition Defs.h:173
DataT sum(const Vector< DataT, N > &a)
Definition Vector.h:107
TrackParCovF TrackParCov
Definition Track.h:33
constexpr int NLAYER
the number of layers
Definition Constants.h:27
constexpr int NSTACK
the number of stacks per sector
Definition Constants.h:26
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
const o2::track::TrackParCov & getTrackParam(GTrackID gidx) const
const o2::its::TrackITS & getITSTrack(GTrackID gid) const
auto getITSTPCTRDTrack(GTrackID id) 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
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
int minTRDNTrklts
min number of TRD space points
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
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....
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, all residuals and track parameters will be aggregated and dumped additionally without outlier...
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
unsigned short nClsITS
number of attached ITS clusters
o2::dataformats::RangeReference clIdx
index of first cluster residual and total number of cluster residuals of this track
float chi2TPC
chi2 of TPC track
unsigned short nClsTPC
number of attached TPC clusters
o2::dataformats::GlobalTrackID gid
global track ID for seeding track
o2::track::TrackPar par
ITS track at inner TPC radius.
float chi2ITS
chi2 of ITS track
Structure for on-the-fly re-calculated track parameters at the validation stage.
o2::mch::DsIndex ds
std::vector< o2::ctf::BufferType > vec
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"
std::uniform_int_distribution< unsigned long long > distr
std::random_device rd
std::vector< int > row