Project
Loading...
Searching...
No Matches
TrackingStudy.cxx
Go to the documentation of this file.
1// Copyright 2019-2026 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
11
12#include <vector>
13#include <cmath>
14
15#include <TStopwatch.h>
16#include <TF1.h>
17#include <Eigen/Dense>
18
32#include "Framework/Task.h"
34#include "ITS3Base/SpecsV2.h"
40#include "ITS3Align/TrackFit.h"
49#include "Framework/Logger.h"
50
51namespace o2::its3::study
52{
53
54using namespace o2::framework;
60using T2VMap = std::unordered_map<GTrackID, size_t>;
61
62class TrackingStudySpec final : public Task
63{
64 public:
69 TrackingStudySpec(std::shared_ptr<DataRequest> dr, std::shared_ptr<o2::base::GRPGeomRequest> gr, GTrackID::mask_t src, bool useMC, bool withPV)
70 : mDataRequest(dr), mGGCCDBRequest(gr), mTracksSrc(src), mUseMC(useMC), mWithPV(withPV) {}
71 ~TrackingStudySpec() final = default;
72 void init(InitContext& ic) final;
73 void run(ProcessingContext& pc) final;
74 void endOfStream(EndOfStreamContext& ec) final;
75 void finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) final;
76
77 private:
78 void process();
79 void updateTimeDependentParams(ProcessingContext& pc);
80 void prepareITSClusters();
81 bool selectTrack(GTrackID trkID, bool checkMCTruth = true) const;
82 T2VMap buildT2V(bool includeCont = false, bool requireMCMatch = true) const;
83 bool refitITSPVTrack(o2::track::TrackParCov& trFit, GTrackID gidx, const o2::dataformats::VertexBase& pv);
84 void getImpactParams(const o2::track::TrackParCov& trk, const o2::dataformats::VertexBase& pv, float ip[2], float bz);
85
86 void doDCAStudy();
87 void doDCARefitStudy();
88 void doPullStudy();
89 void doMCStudy();
90 void doResidStudy();
91 void doMisalignmentStudy();
92
93 struct TrackCounter {
94 TrackCounter() = default;
95
96 void operator+=(int src)
97 {
98 if (src >= 0 && src < static_cast<int>(mSuccess.size())) {
99 ++mSuccess[src];
100 }
101 }
102
103 void operator-=(int src)
104 {
105 if (src >= 0 && src < static_cast<int>(mirrors.size())) {
106 ++mirrors[src];
107 }
108 }
109
110 void operator&=(int src)
111 {
112 if (src >= 0 && src < static_cast<int>(mRejected.size())) {
113 ++mRejected[src];
114 }
115 }
116
117 void print() const
118 {
119 LOGP(info, "\t\t\tSuccess / Error / Rejected");
120 for (int cis = 0; cis < GTrackID::NSources; ++cis) {
121 const auto cdm = GTrackID::getSourceDetectorsMask(cis);
122 if (cdm[DetID::ITS]) {
123 LOGP(info, "\t{:{}}\t{} / {} / {}", GTrackID::getSourceName(cis), 15, mSuccess[cis], mirrors[cis], mRejected[cis]);
124 }
125 }
126 }
127
128 void reset()
129 {
130 mSuccess.fill(0);
131 mirrors.fill(0);
132 mRejected.fill(0);
133 }
134
135 std::array<size_t, GTrackID::NSources> mSuccess{};
136 std::array<size_t, GTrackID::NSources> mirrors{};
137 std::array<size_t, GTrackID::NSources> mRejected{};
138 };
139 TrackCounter mTrackCounter;
140
141 using TrackingCluster = align::TrackingCluster<float>;
142 std::vector<TrackingCluster> mITScl;
143 std::span<const int> mITSclRef;
144
145 const ITS3TrackingStudyParam* mParams{nullptr};
146 std::unique_ptr<o2::utils::TreeStreamRedirector> mDBGOut;
147 std::shared_ptr<DataRequest> mDataRequest;
148 std::shared_ptr<o2::base::GRPGeomRequest> mGGCCDBRequest;
149 bool mUseMC{false};
150 bool mWithPV{false};
151 GTrackID::mask_t mTracksSrc;
152 o2::vertexing::PVertexer mVertexer;
153 o2::steer::MCKinematicsReader mMCReader; // reader of MC information
154 const o2::its3::TopologyDictionary* mITSDict = nullptr; // cluster patterns dictionary
156 align::MisalignmentModel mMisalignment;
157};
158
160{
162 int lane = ic.services().get<const o2::framework::DeviceSpec>().inputTimesliceId;
163 int maxLanes = ic.services().get<const o2::framework::DeviceSpec>().maxInputTimeslices;
164 std::string dbgnm = maxLanes == 1 ? "its3TrackStudy.root" : fmt::format("its3TrackStudy_{}.root", lane);
165 mDBGOut = std::make_unique<o2::utils::TreeStreamRedirector>(dbgnm.c_str(), "recreate");
166
168 LOGP(fatal, "initialization of MCKinematicsReader failed");
169 }
170}
171
173{
174 mRecoData.collectData(pc, *mDataRequest);
175 updateTimeDependentParams(pc);
176 process();
177}
178
179void TrackingStudySpec::updateTimeDependentParams(ProcessingContext& pc)
180{
182 if (static bool initOnceDone{false}; !initOnceDone) { // this params need to be queried only once
183 initOnceDone = true;
185 mVertexer.init();
188 mParams->printKeyValues(true, true);
189 if (mParams->doMisalignment) {
190 mMisalignment = {};
191 if (!mParams->misAlgJson.empty()) {
192 mMisalignment = align::loadMisalignmentModel(mParams->misAlgJson);
193 }
194 }
195 }
196}
197
199{
200 mDBGOut.reset();
201}
202
204{
206 return;
207 }
208 if (matcher == ConcreteDataMatcher("IT3", "CLUSDICT", 0)) {
209 LOG(info) << "cluster dictionary updated";
210 mITSDict = (const o2::its3::TopologyDictionary*)obj;
211 return;
212 }
213}
214
215void TrackingStudySpec::process()
216{
217 prepareITSClusters();
218 if (mParams->doDCA) {
219 doDCAStudy();
220 }
221 if (mParams->doDCARefit) {
222 doDCARefitStudy();
223 }
224 if (mUseMC && mParams->doPull) {
225 doPullStudy();
226 }
227 if (mUseMC && mParams->doMC) {
228 doMCStudy();
229 }
230 if (mParams->doResid) {
231 doResidStudy();
232 }
233 if (mUseMC && mParams->doMisalignment) {
234 doMisalignmentStudy();
235 }
236}
237
238void TrackingStudySpec::prepareITSClusters()
239{
240 const auto& clusITS = mRecoData.getITSClusters();
241 LOGP(info, "Preparing {} measurments", clusITS.size());
242 const auto& patterns = mRecoData.getITSClustersPatterns();
243 mITScl.reserve(clusITS.size());
244 auto pattIt = patterns.begin();
245 auto geom = its::GeometryTGeo::Instance();
246 mITSclRef = mRecoData.getITSTracksClusterRefs();
247 mITScl.clear();
248 mITScl.reserve(clusITS.size());
249 for (const auto& cls : clusITS) {
250 const auto sens = cls.getSensorID();
251 float sigmaY2{0}, sigmaZ2{0};
252 math_utils::Point3D<float> locXYZ = o2::its3::ioutils::extractClusterData(cls, pattIt, mITSDict, sigmaY2, sigmaZ2);
253 // Transformation to the local --> global
254 const auto gloXYZ = geom->getMatrixL2G(sens) * locXYZ;
255 // Inverse transformation to the local --> tracking
256 o2::math_utils::Point3D<float> trkXYZ = geom->getMatrixT2L(sens) ^ locXYZ;
257 // Tracking alpha angle
258 // We want that each cluster rotates its tracking frame to the clusters phi
259 // that way the track linearization around the measurement is less biases to the arc
260 // this means automatically that the measurement on the arc is at 0 for the curved layers
261 float alpha = geom->getSensorRefAlpha(sens);
262 if (constants::detID::isDetITS3(sens)) {
263 trkXYZ.SetY(0.f);
264 // alpha&x always have to be defined wrt to the global Z axis!
265 trkXYZ.SetX(std::hypot(gloXYZ.x(), gloXYZ.y()));
266 alpha = std::atan2(gloXYZ.y(), gloXYZ.x());
267 }
268 auto& cl3d = mITScl.emplace_back(sens, trkXYZ);
269 cl3d.setErrors(sigmaY2, sigmaZ2, 0.f);
270 cl3d.alpha = alpha;
271 math_utils::detail::bringToPMPi(cl3d.alpha); // alpha is defined on -Pi,Pi
272 }
273}
274
275bool TrackingStudySpec::selectTrack(GTrackID trkID, bool checkMCTruth) const
276{
277 if (!trkID.includesDet(GTrackID::ITS)) {
278 return false;
279 }
280 if (!mRecoData.isTrackSourceLoaded(trkID.getSource())) {
281 return false;
282 }
283 auto contributorsGID = mRecoData.getSingleDetectorRefs(trkID);
284 if (!contributorsGID[GTrackID::ITS].isIndexSet()) { // we need of course ITS
285 return false;
286 }
287 // ITS specific
288 const auto& itsTrk = mRecoData.getITSTrack(contributorsGID[GTrackID::ITS]);
289 if (itsTrk.getChi2() > mParams->maxChi2 || itsTrk.getNClusters() < mParams->minITSCls) {
290 return false;
291 }
292 // TPC specific
293 if (contributorsGID[GTrackID::TPC].isIndexSet()) {
294 const auto& tpcTrk = mRecoData.getTPCTrack(contributorsGID[GTrackID::TPC]);
295 if (tpcTrk.getNClusters() < mParams->minTPCCls) {
296 return false;
297 }
298 }
299 // general
300 const auto& gTrk = mRecoData.getTrackParam(trkID);
301 if (gTrk.getPt() < mParams->minPt || gTrk.getPt() > mParams->maxPt) {
302 return false;
303 }
304 if (std::abs(gTrk.getEta()) > mParams->maxEta) {
305 return false;
306 }
307 if (mUseMC && checkMCTruth) {
308 const auto& itsLbl = mRecoData.getTrackMCLabel(contributorsGID[GTrackID::ITS]);
309 if (!itsLbl.isValid()) {
310 return false;
311 }
312 if (contributorsGID[GTrackID::TPC].isIndexSet()) {
313 const auto& tpcLbl = mRecoData.getTrackMCLabel(contributorsGID[GTrackID::TPC]);
314 if (itsLbl != tpcLbl) {
315 return false;
316 }
317 }
318 if (contributorsGID[GTrackID::TRD].isIndexSet()) {
319 // TODO
320 }
321 if (contributorsGID[GTrackID::TOF].isIndexSet()) {
322 const auto& tofLbls = mRecoData.getTOFClustersMCLabels()->getLabels(contributorsGID[GTrackID::TOF]);
323 for (const auto& lbl : tofLbls) {
324 if (lbl.isValid()) {
325 return true;
326 }
327 }
328 }
329 }
330 return true;
331}
332
333T2VMap TrackingStudySpec::buildT2V(bool includeCont, bool requireMCMatch) const
334{
335 // build track->vertex assoc., maybe including contributor tracks
336 auto pvvec = mRecoData.getPrimaryVertices();
337 auto trackIndex = mRecoData.getPrimaryVertexMatchedTracks(); // Global ID's for associated tracks
338 auto vtxRefs = mRecoData.getPrimaryVertexMatchedTrackRefs(); // references from vertex to these track IDs
339 auto nv = vtxRefs.size() - 1; // last entry is for unassigned tracks, ignore them
340 T2VMap t2v;
341 for (size_t iv = 0; iv < nv; ++iv) {
342 const auto& pv = pvvec[iv];
343 if (pv.getNContributors() - 1 < mParams->minPVCont) {
344 continue;
345 }
346 if (requireMCMatch) {
347 auto pvl = mRecoData.getPrimaryVertexMCLabel(iv);
348 }
349 const auto& vtxRef = vtxRefs[iv];
350 int it = vtxRef.getFirstEntry(), itLim = it + vtxRef.getEntries();
351 for (; it < itLim; it++) {
352 const auto& tvid = trackIndex[it];
353 if (tvid.isAmbiguous()) {
354 continue;
355 }
356 if (!mRecoData.isTrackSourceLoaded(tvid.getSource())) {
357 continue;
358 }
359 if (mUseMC && requireMCMatch) {
360 const auto& pvlbl = mRecoData.getPrimaryVertexMCLabel(iv);
361 if (pvlbl.getEventID() != mRecoData.getTrackMCLabel(tvid).getEventID()) {
362 continue;
363 }
364 }
365 t2v[tvid] = iv;
366 if (includeCont) {
367 auto contributorsGID = mRecoData.getSingleDetectorRefs(tvid);
368 for (int cis = 0; cis < GTrackID::NSources; cis++) {
369 const auto cdm = GTrackID::getSourceDetectorsMask(cis);
370 if (!mRecoData.isTrackSourceLoaded(cis) || !cdm[DetID::ITS] || !contributorsGID[cis].isIndexSet()) {
371 continue;
372 }
373 if (mUseMC && requireMCMatch) {
374 const auto& pvlbl = mRecoData.getPrimaryVertexMCLabel(iv);
375 if (pvlbl.getEventID() != mRecoData.getTrackMCLabel(contributorsGID[cis]).getEventID()) {
376 continue;
377 }
378 }
379 t2v[contributorsGID[cis]] = iv;
380 }
381 }
382 }
383 }
384 return std::move(t2v);
385}
386
387bool TrackingStudySpec::refitITSPVTrack(o2::track::TrackParCov& trFit, GTrackID gidx, const o2::dataformats::VertexBase& pv)
388{
389 if (gidx.getSource() != GTrackID::ITS) {
390 return false;
391 }
392
393 const auto geom = o2::its::GeometryTGeo::Instance();
394 const auto prop = o2::base::Propagator::Instance();
395 std::array<const TrackingCluster*, 8> clArr{nullptr};
396 const auto trkIn = mRecoData.getTrackParam(gidx);
397 const auto& itsTrOrig = mRecoData.getITSTrack(gidx);
398
399 // convert PV to a fake cluster in the track DCA frame
400 auto trkPV = trkIn;
401 if (!prop->propagateToDCA(pv, trkPV, prop->getNominalBz(), 2.0, mParams->CorrType)) {
402 mTrackCounter -= gidx.getSource();
403 return false;
404 }
405 // create base cluster from the PV, with the alpha corresponding to the track at DCA
406 float cosAlp = NAN, sinAlp = NAN;
407 o2::math_utils::sincos(trkPV.getAlpha(), sinAlp, cosAlp);
408 // vertex position rotated to track frame
409 TrackingCluster pvCls;
410 pvCls.alpha = trkPV.getAlpha();
411 pvCls.setXYZ((pv.getX() * cosAlp) + (pv.getY() * sinAlp), (-pv.getX() * sinAlp) + (pv.getY() * cosAlp), pv.getZ());
412 pvCls.setErrors(0.5f * (pv.getSigmaX2() + pv.getSigmaY2()), pv.getSigmaZ2(), 0.f);
413 pvCls.setSensorID(-1);
414 clArr[0] = &pvCls;
415
416 const int ncl = itsTrOrig.getNumberOfClusters();
417 for (int icl = 0; icl < ncl; ++icl) {
418 const auto& curClu = mITScl[mITSclRef[itsTrOrig.getClusterEntry(icl)]];
419 const int llr = geom->getLayer(curClu.getSensorID());
420 if (clArr[1 + llr]) {
421 LOGP(fatal, "Cluster at lr {} was already assigned, old sens {}, new sens {}", llr, clArr[1 + llr]->getSensorID(), curClu.getSensorID());
422 }
423 clArr[1 + llr] = &curClu;
424 }
425
426 trFit = mRecoData.getTrackParamOut(gidx);
427 trFit.resetCovariance(1'000);
428 float chi2{0};
429 for (int icl = clArr.size() - 1; icl >= 0; --icl) { // go backwards
430 if (!clArr[icl]) {
431 continue;
432 }
433 if (!trFit.rotate(clArr[icl]->alpha) || !prop->propagateToX(trFit, clArr[icl]->getX(), prop->getNominalBz(), 0.85, 2.0, mParams->CorrType)) {
434 mTrackCounter -= gidx.getSource();
435 return false;
436 }
437 chi2 += trFit.getPredictedChi2(*clArr[icl]);
438 if (!trFit.update(*clArr[icl])) {
439 mTrackCounter -= gidx.getSource();
440 return false;
441 }
442 }
443 return true;
444};
445
446void TrackingStudySpec::doDCAStudy()
447{
449 LOGP(info, "Doing DCA study");
450 mTrackCounter.reset();
451 auto prop = o2::base::Propagator::Instance();
452 TStopwatch sw;
453 sw.Start();
454 int nDCAFits{0}, nDCAFitsFail{0};
455 auto pvvec = mRecoData.getPrimaryVertices();
456 auto trackIndex = mRecoData.getPrimaryVertexMatchedTracks(); // Global ID's for associated tracks
457 auto vtxRefs = mRecoData.getPrimaryVertexMatchedTrackRefs(); // references from vertex to these track IDs
458 auto nv = vtxRefs.size() - 1; // last entry is for unassigned tracks, ignore them
459 auto& stream = (*mDBGOut) << "dca";
460 for (int iv = 0; iv < nv; iv++) {
461 const auto& pv = pvvec[iv];
462 const auto& vtref = vtxRefs[iv];
463 for (int is = 0; is < GTrackID::NSources; is++) {
464 const auto dm = GTrackID::getSourceDetectorsMask(is);
465 if (!mRecoData.isTrackSourceLoaded(is) || !dm[DetID::ITS]) {
466 mTrackCounter &= is;
467 continue;
468 }
469 int idMin = vtref.getFirstEntryOfSource(is), idMax = idMin + vtref.getEntriesOfSource(is);
470 for (int i = idMin; i < idMax; i++) {
471 const auto vid = trackIndex[i];
472 if (!vid.isPVContributor()) {
473 mTrackCounter &= vid.getSource();
474 continue;
475 }
476
477 // we fit each different sub-track type, that include ITS, e.g.
478 // ITS,ITS-TPC,ITS-TPC-TRD,ITS-TPC-TOF,ITS-TPC-TRD-TOF
479 auto contributorsGID = mRecoData.getSingleDetectorRefs(vid);
480 for (int cis = 0; cis < GTrackID::NSources && cis <= is; cis++) {
481 const auto cdm = GTrackID::getSourceDetectorsMask(cis);
482 if (!mRecoData.isTrackSourceLoaded(cis) || !cdm[DetID::ITS] || !contributorsGID[cis].isIndexSet()) {
483 mTrackCounter &= cis;
484 continue;
485 }
486 if (!selectTrack(contributorsGID[cis])) {
487 mTrackCounter &= vid.getSource();
488 continue;
489 }
490
491 o2::dataformats::DCA dcaInfo;
492 const auto& trk = mRecoData.getTrackParam(contributorsGID[cis]);
493 auto trkRefit = trk;
494 // for ITS standalone tracks instead of having the trk at the pv we refit with the pv
495 if (mWithPV && mParams->refitITS && cis == GTrackID::ITS && !refitITSPVTrack(trkRefit, contributorsGID[cis], pv)) {
496 mTrackCounter -= cis;
497 continue;
498 } else if (!(mWithPV && mParams->refitITS && cis == GTrackID::ITS)) {
499 trkRefit.invalidate();
500 };
501
502 auto trkDCA = trk;
503 if (!prop->propagateToDCABxByBz(pv, trkDCA, 2.f, mParams->CorrType, &dcaInfo)) {
504 mTrackCounter -= cis;
505 ++nDCAFitsFail;
506 continue;
507 }
508
509 stream << "src=" << cis
510 << "pv=" << pv
511 << "trk=" << trk
512 << "trkRefit=" << trkRefit
513 << "trkAtPV=" << trkDCA
514 << "dca=" << dcaInfo;
515
516 if (mUseMC) {
517 const auto& lbl = mRecoData.getTrackMCLabel(contributorsGID[cis]);
518 lbl.print();
519 o2::dataformats::DCA dcaInfoMC;
520 const auto& eve = mMCReader.getMCEventHeader(lbl.getSourceID(), lbl.getEventID());
522 mcEve.setPos({(float)eve.GetX(), (float)eve.GetY(), (float)eve.GetZ()});
523 auto trkC = trk;
524 if (!prop->propagateToDCABxByBz(mcEve, trkC, 2.f, mParams->CorrType, &dcaInfoMC)) {
525 mTrackCounter -= cis;
526 ++nDCAFitsFail;
527 continue;
528 }
529 const auto& mcTrk = mMCReader.getTrack(lbl);
530 if (mcTrk == nullptr) {
531 LOGP(fatal, "mcTrk is null did selection fail?");
532 }
533 stream << "mcTrk=" << *mcTrk
534 << "dca2MC=" << dcaInfoMC
535 << "lbl=" << lbl;
536 }
537 stream << "\n";
538
539 ++nDCAFits;
540 mTrackCounter += cis;
541 }
542 }
543 }
544 }
545 sw.Stop();
546 LOGP(info, "doDCAStudy: accepted {} fits, failed {} (in {:.2f} seconds)", nDCAFits, nDCAFitsFail, sw.RealTime());
547 mTrackCounter.print();
548}
549
550void TrackingStudySpec::doDCARefitStudy()
551{
553 LOGP(info, "Doing DCARefit study");
554 mTrackCounter.reset();
555 auto prop = o2::base::Propagator::Instance();
556 TStopwatch sw;
557 sw.Start();
558
559 // build track->vertex assoc.
560 auto pvvec = mRecoData.getPrimaryVertices();
561 auto vtxRefs = mRecoData.getPrimaryVertexMatchedTrackRefs(); // references from vertex to these track IDs
562 auto nv = vtxRefs.size() - 1; // last entry is for unassigned tracks, ignore them
563 auto t2v = buildT2V();
564 std::vector<std::vector<GTrackID>> v2t;
565 v2t.resize(nv);
566 auto creator = [&](const auto& trk, GTrackID trkID, float _t0, float terr) -> bool {
567 if constexpr (!isBarrelTrack<decltype(trk)>()) {
568 mTrackCounter &= trkID.getSource();
569 return false;
570 }
571 if (!trkID.includesDet(GTrackID::ITS)) {
572 mTrackCounter &= trkID.getSource();
573 return false;
574 }
575 // general
576 if constexpr (isBarrelTrack<decltype(trk)>()) {
577 if (trk.getPt() < mParams->minPt || trk.getPt() > mParams->maxPt) {
578 mTrackCounter &= trkID.getSource();
579 return false;
580 }
581 if (std::abs(trk.getEta()) > mParams->maxEta) {
582 mTrackCounter &= trkID.getSource();
583 return false;
584 }
585 if (!t2v.contains(trkID)) {
586 mTrackCounter &= trkID.getSource();
587 return false;
588 }
589 if (!selectTrack(trkID, mUseMC)) {
590 mTrackCounter &= trkID.getSource();
591 return false;
592 }
593 }
594 v2t[t2v[trkID]].push_back(trkID);
595 return true;
596 };
597 mRecoData.createTracksVariadic(creator);
598
599 int nDCAFits{0}, nDCAFitsFail{0};
600 auto& stream = (*mDBGOut) << "dcaRefit";
601 for (size_t iv = 0; iv < nv; ++iv) {
602 const auto& pv = pvvec[iv];
603 const auto& trkIDs = v2t[iv];
604 if (trkIDs.size() - 1 < mParams->minPVCont) {
605 continue;
606 }
607 std::vector<o2::track::TrackParCov> trks;
608 trks.reserve(trkIDs.size());
609 for (const auto& trkID : trkIDs) {
610 trks.push_back(mRecoData.getTrackParam(trkID));
611 }
612
613 if (!mVertexer.prepareVertexRefit(trks, pv)) {
614 continue;
615 }
616 std::vector<bool> trkMask(trkIDs.size(), true);
617 for (size_t it{0}; it < trkMask.size(); ++it) {
618 trkMask[it] = false; // mask current track from pv refit
619 if (it != 0) {
620 trkMask[it - 1] = true; // unmask previoustrack from pv refit
621 }
622 auto pvRefit = mVertexer.refitVertex(trkMask, pv);
623 if (pvRefit.getChi2() < 0) {
624 trkMask[it] = true;
625 continue;
626 }
627
628 // check DCA both for refitted and original PV
629 o2::dataformats::DCA dcaInfo;
630 auto trkC = trks[it];
631 if (!prop->propagateToDCABxByBz(pv, trkC, 2.f, mParams->CorrType, &dcaInfo)) {
632 mTrackCounter -= trkIDs[it].getSource();
633 ++nDCAFitsFail;
634 continue;
635 }
636 o2::dataformats::DCA dcaInfoRefit;
637 auto trkCRefit = trks[it];
638 if (!prop->propagateToDCABxByBz(pv, trkCRefit, 2.f, mParams->CorrType, &dcaInfoRefit)) {
639 mTrackCounter -= trkIDs[it].getSource();
640 ++nDCAFitsFail;
641 continue;
642 }
643
644 stream << "src=" << trkIDs[it].getSource()
645 << "pv=" << pv
646 << "trkAtPV=" << trkC
647 << "dca=" << dcaInfo
648 << "pvRefit=" << pvRefit
649 << "trkAtPVRefit=" << trkC
650 << "dcaRefit=" << dcaInfoRefit;
651 if (mUseMC) {
652 const auto& mcTrk = mMCReader.getTrack(mRecoData.getTrackMCLabel(trkIDs[it]));
653 if (mcTrk == nullptr) {
654 LOGP(fatal, "mcTrk is null did selection fail?");
655 }
656 stream << "mcTrk=" << *mcTrk;
657 }
658 stream << "\n";
659 ++nDCAFits;
660 mTrackCounter += trkIDs[it].getSource();
661 }
662 }
663 sw.Stop();
664 LOGP(info, "doDCARefitStudy: accepted {} fits, failed {} (in {:.2f} seconds)", nDCAFits, nDCAFitsFail, sw.RealTime());
665 mTrackCounter.print();
666}
667
668void TrackingStudySpec::doPullStudy()
669{
670 // check track pulls compared to mc generation
671 LOGP(info, "Doing Pull study");
672 mTrackCounter.reset();
673 TStopwatch sw;
674 sw.Start();
675 int nPulls{0}, nPullsFail{0};
676 auto prop = o2::base::Propagator::Instance();
677
678 auto checkInTrack = [&](GTrackID trkID) {
679 if (!selectTrack(trkID)) {
680 mTrackCounter &= trkID.getSource();
681 return;
682 }
683 const auto& lbl = mRecoData.getTrackMCLabel(trkID);
684 const auto mcTrk = mMCReader.getTrack(lbl);
685 if (!mcTrk) {
686 return;
687 }
688 if (!mcTrk->isPrimary()) {
689 mTrackCounter &= trkID.getSource();
690 return;
691 }
692 auto trk = mRecoData.getTrackParam(trkID);
693
694 // for ITS standalone tracks we add the MC event vertex as an additional measurement point
695 if (mParams->refitITS && trkID.getSource() == GTrackID::ITS) {
696 const auto& eve = mMCReader.getMCEventHeader(lbl.getSourceID(), lbl.getEventID());
698 mcEve.setXYZ((float)eve.GetX(), (float)eve.GetY(), (float)eve.GetZ());
699 mcEve.setSigmaX(20e-4f);
700 mcEve.setSigmaY(20e-4f);
701 mcEve.setSigmaZ(20e-4f);
702 if (!refitITSPVTrack(trk, trkID, mcEve)) {
703 mTrackCounter -= trkID.getSource();
704 ++nPullsFail;
705 return;
706 }
707 }
708
709 std::array<float, 3> xyz{(float)mcTrk->GetStartVertexCoordinatesX(), (float)mcTrk->GetStartVertexCoordinatesY(), (float)mcTrk->GetStartVertexCoordinatesZ()},
710 pxyz{(float)mcTrk->GetStartVertexMomentumX(), (float)mcTrk->GetStartVertexMomentumY(), (float)mcTrk->GetStartVertexMomentumZ()};
711 TParticlePDG* pPDG = TDatabasePDG::Instance()->GetParticle(mcTrk->GetPdgCode());
712 if (!pPDG) {
713 mTrackCounter -= trkID.getSource();
714 ++nPullsFail;
715 return;
716 }
717 o2::track::TrackPar mcTrkO2(xyz, pxyz, TMath::Nint(pPDG->Charge() / 3), false);
718 // propagate it to the alpha/X of the reconstructed track
719 if (!mcTrkO2.rotate(trk.getAlpha()) || !prop->PropagateToXBxByBz(mcTrkO2, trk.getX())) {
720 mTrackCounter -= trkID.getSource();
721 ++nPullsFail;
722 return;
723 }
724 const auto contTrk = mRecoData.getSingleDetectorRefs(trkID);
725 const auto& itsTrk = mRecoData.getITSTrack(contTrk[GTrackID::ITS]);
726
727 (*mDBGOut)
728 << "pull"
729 << "src=" << trkID.getSource()
730 << "itsTrk=" << itsTrk
731 << "mcTrk=" << mcTrkO2
732 << "mcPart=" << mcTrk
733 << "trk=" << trk
734 << "\n";
735 ++nPulls;
736 mTrackCounter += trkID.getSource();
737 };
738
739 for (size_t iTrk{0}; iTrk < mRecoData.getITSTracks().size(); ++iTrk) {
740 checkInTrack(GTrackID(iTrk, GTrackID::ITS));
741 }
742 for (size_t iTrk{0}; iTrk < mRecoData.getTPCITSTracks().size(); ++iTrk) {
743 checkInTrack(GTrackID(iTrk, GTrackID::ITSTPC));
744 }
745 for (size_t iTrk{0}; iTrk < mRecoData.getITSTPCTRDTracksMCLabels().size(); ++iTrk) {
746 checkInTrack(GTrackID(iTrk, GTrackID::ITSTPCTRD));
747 }
748 for (size_t iTrk{0}; iTrk < mRecoData.getITSTPCTOFMatches().size(); ++iTrk) {
749 checkInTrack(GTrackID(iTrk, GTrackID::ITSTPCTOF));
750 }
751 for (size_t iTrk{0}; iTrk < mRecoData.getITSTPCTRDTOFMatches().size(); ++iTrk) {
752 checkInTrack(GTrackID(iTrk, GTrackID::ITSTPCTRDTOF));
753 }
754 sw.Stop();
755 LOGP(info, "doPullStudy: accepted {} pulls; rejected {} (in {:.2f} seconds)", nPulls, nPullsFail, sw.RealTime());
756 mTrackCounter.print();
757}
758
759void TrackingStudySpec::doMCStudy()
760{
761 LOGP(info, "Doing MC study");
762 mTrackCounter.reset();
763 TStopwatch sw;
764 sw.Start();
765 int nTracks{0};
766
767 const int iSrc{0};
768 const int nev = mMCReader.getNEvents(iSrc);
769 std::unordered_map<o2::MCCompLabel, ParticleInfoExt> info;
770
771 LOGP(info, "** Filling particle table ... ");
772 for (int iEve{0}; iEve < nev; ++iEve) {
773 const auto& mcTrks = mMCReader.getTracks(iSrc, iEve);
774 for (int iTrk{0}; iTrk < mcTrks.size(); ++iTrk) {
775 const auto& mcTrk = mcTrks[iTrk];
776 const auto pdg = mcTrk.GetPdgCode();
777 if (o2::O2DatabasePDG::Instance()->GetParticle(pdg) == nullptr) {
778 continue;
779 }
780 const auto apdg = std::abs(pdg);
781 if (apdg != 11 && apdg != 211 && apdg != 321 && apdg != 2212) {
782 continue;
783 }
784 o2::MCCompLabel lbl(iTrk, iEve, iSrc);
785 auto& part = info[lbl];
786 part.mcTrack = mcTrk;
787 }
788 }
789 LOGP(info, "** Creating particle/clusters correspondence ... ");
790 const auto& clusters = mRecoData.getITSClusters();
791 const auto& clustersMCLCont = mRecoData.getITSClustersMCLabels();
792 for (auto iCluster{0}; iCluster < clusters.size(); ++iCluster) {
793 auto labs = clustersMCLCont->getLabels(iCluster);
794 for (auto& lab : labs) {
795 if (!lab.isValid() || lab.getSourceID() != 0 || !lab.isCorrect()) {
796 continue;
797 }
798 int trackID = 0, evID = 0, srcID = 0;
799 bool fake = false;
800 lab.get(trackID, evID, srcID, fake);
801 auto& cluster = clusters[iCluster];
802 auto layer = o2::its::GeometryTGeo::Instance()->getLayer(cluster.getSensorID());
803 auto& part = info[{trackID, evID, srcID}];
804 part.clusters |= (1 << layer);
805 if (fake) {
806 part.fakeClusters |= (1 << layer);
807 }
808 }
809 }
810 LOGP(info, "** Analysing tracks ... ");
811 auto accountLbl = [&](const globaltracking::RecoContainer::GlobalIDSet& contributorsGID, DetID::ID det) {
812 if (contributorsGID[det].isIndexSet()) {
813 const auto& lbl = mRecoData.getTrackMCLabel(contributorsGID[det]);
814 if (lbl.isValid()) {
815 o2::MCCompLabel iLbl(lbl.getTrackID(), lbl.getEventID(), lbl.getSourceID());
816 if (info.contains(iLbl)) {
817 auto& part = info[iLbl];
818 SETBIT(part.recoTracks, det);
819 if (lbl.isFake()) {
820 SETBIT(part.fakeTracks, det);
821 }
822 }
823 }
824 }
825 };
826 auto creator = [&](const auto& trk, GTrackID trkID, float _t0, float terr) -> bool {
827 if constexpr (!isBarrelTrack<decltype(trk)>()) {
828 return false;
829 }
830 if (!trkID.includesDet(GTrackID::ITS)) {
831 return false;
832 }
833 // general
834 auto contributorsGID = mRecoData.getSingleDetectorRefs(trkID);
835 if (!contributorsGID[GTrackID::ITS].isIndexSet()) { // we need of course ITS
836 return false;
837 }
838 const auto& gLbl = mRecoData.getTrackMCLabel(trkID);
839 if (!gLbl.isValid()) {
840 return false;
841 }
842 o2::MCCompLabel iLbl(gLbl.getTrackID(), gLbl.getEventID(), gLbl.getSourceID());
843 if (!info.contains(iLbl)) {
844 return false;
845 }
846 auto& part = info[iLbl];
847 part.recoTrack = mRecoData.getTrackParam(trkID);
848
849 accountLbl(contributorsGID, DetID::ITS);
850 accountLbl(contributorsGID, DetID::TPC);
851 accountLbl(contributorsGID, DetID::TRD);
852 accountLbl(contributorsGID, DetID::TOF);
853
854 ++nTracks;
855 return true;
856 };
857 mRecoData.createTracksVariadic(creator);
858
859 LOGP(info, "Streaming output to tree");
860 for (const auto& [_, part] : info) {
861 (*mDBGOut) << "mc"
862 << "part=" << part
863 << "\n";
864 }
865
866 sw.Stop();
867 LOGP(info, "doMCStudy: accounted {} MCParticles and {} tracks (in {:.2f} seconds)", info.size(), nTracks, sw.RealTime());
868}
869
870void TrackingStudySpec::doResidStudy()
871{
872 LOGP(info, "Doing residual study");
873 const auto geom = o2::its::GeometryTGeo::Instance();
874 const auto prop = o2::base::Propagator::Instance();
875 const float bz = prop->getNominalBz();
876
877 int goodRefit{0}, notPassedSel{0}, fitFail{0};
878
879 auto doRefits = [&](const o2::its::TrackITS& iTrack, const o2::MCCompLabel& lbl) {
880 std::array<TrackingCluster, 8> cl;
881 std::array<const TrackingCluster*, 8> clArr{nullptr};
883 float ip[2];
884 if (mParams->addPVAsCluster) {
885 const auto& eve = mMCReader.getMCEventHeader(lbl.getSourceID(), lbl.getEventID());
886 auto trFitOut = iTrack.getParamIn();
887 pv.setXYZ(eve.GetX(), eve.GetY(), eve.GetZ());
888 if (!prop->propagateToDCA(pv, trFitOut, bz, base::Propagator::MAX_STEP, mParams->CorrType)) {
889 return;
890 }
891 pv.setSigmaX(20e-4f);
892 pv.setSigmaY(20e-4f);
893 pv.setSigmaZ(20e-4f);
894 float cosAlp = NAN, sinAlp = NAN;
895 o2::math_utils::sincos(trFitOut.getAlpha(), sinAlp, cosAlp);
896 cl[0].alpha = trFitOut.getAlpha();
897 cl[0].setXYZ((pv.getX() * cosAlp) + (pv.getY() * sinAlp), (-pv.getX() * sinAlp) + (pv.getY() * cosAlp), pv.getZ());
898 cl[0].setErrors(0.5f * (pv.getSigmaX2() + pv.getSigmaY2()), pv.getSigmaZ2(), 0.f);
899 cl[0].setSensorID(-1);
900 clArr[0] = &cl[0];
901 }
902
903 // collect track clusters into layer slots
904 int nCl = iTrack.getNClusters();
905 for (int i = 0; i < nCl; i++) {
906 const auto& curClu = mITScl[mITSclRef[iTrack.getClusterEntry(i)]];
907 int sens = curClu.getSensorID();
908 int llr = geom->getLayer(sens);
909 if (clArr[1 + llr]) {
910 LOGP(fatal, "Cluster at lr {} was already assigned, old sens {}, new sens {}", llr, clArr[1 + llr]->getSensorID(), sens);
911 }
912 clArr[1 + llr] = &curClu;
913 }
914
915 std::array<o2::track::TrackParCov, 8> extrapOut, extrapInw;
916 float chi2{0};
917 if (!align::doBidirRefit(iTrack, clArr, extrapOut, extrapInw, chi2, mParams->useStableRef, mParams->CorrType)) {
918 ++fitFail;
919 return;
920 }
921
922 for (int i = 0; i <= 7; i++) {
923 if (clArr[i]) {
924 const auto tInt = align::interpolateTrackParCov(extrapInw[i], extrapOut[i]);
925 if (!tInt.isValid()) {
926 continue;
927 }
928 auto phi = i == 0 ? tInt.getPhi() : tInt.getPhiPos();
929 o2::math_utils::bringTo02Pi(phi);
930 if (clArr[0]) {
931 getImpactParams(tInt, pv, ip, bz);
932 }
933 (*mDBGOut) << "res"
934 << "dYInt=" << clArr[i]->getY() - tInt.getY()
935 << "dZInt=" << clArr[i]->getZ() - tInt.getZ()
936 << "dYIn=" << clArr[i]->getY() - extrapInw[i].getY()
937 << "dZIn=" << clArr[i]->getZ() - extrapInw[i].getZ()
938 << "dYOut=" << clArr[i]->getY() - extrapOut[i].getY()
939 << "dZOut=" << clArr[i]->getZ() - extrapOut[i].getZ()
940 << "chi2=" << chi2
941 << "clY=" << clArr[i]->getY()
942 << "clZ=" << clArr[i]->getZ()
943 << "clX=" << clArr[i]->getX()
944 << "alpha=" << clArr[i]->alpha
945 << "sens=" << clArr[i]->getSensorID()
946 << "phi=" << phi
947 << "dcaXY=" << ip[0]
948 << "dcaZ=" << ip[1]
949 << "pt=" << tInt.getPt()
950 << "chip=" << constants::detID::getSensorID(clArr[i]->getSensorID())
951 << "lay=" << i - 1
952 << "\n";
953 }
954 }
955 ++goodRefit;
956 };
957
958 const auto itsTracks = mRecoData.getITSTracks();
959 const auto itsMC = mRecoData.getITSTracksMCLabels();
960 for (size_t iTrk{0}; iTrk < itsTracks.size(); ++iTrk) {
961 const auto& iTrack = itsTracks[iTrk];
962 const auto& lbl = itsMC[iTrk];
963 const auto& mc = mMCReader.getTrack(lbl);
964 if (std::abs(iTrack.getEta()) > mParams->maxEta || iTrack.getChi2() > mParams->maxChi2 || iTrack.getNClusters() < mParams->minITSCls || iTrack.getPt() < mParams->minPt || !lbl.isCorrect() || !mc->isPrimary()) {
965 ++notPassedSel;
966 continue;
967 }
968 doRefits(iTrack, lbl);
969 }
970
971 LOGP(info, "\trefitted {} out of {} tracks ({} !sel, {} !fit)", goodRefit, itsTracks.size(), notPassedSel, fitFail);
972}
973
974void TrackingStudySpec::doMisalignmentStudy()
975{
976 LOGP(info, "Doing misalignment study");
977 const auto prop = o2::base::Propagator::Instance();
978 const auto geom = o2::its::GeometryTGeo::Instance();
979
980 int goodRefit{0}, notPassedSel{0}, fitFail{0}, fitFailMis{0};
982 float ip[2];
983 float chi2{0};
984 auto writeTree = [&](const char* treeName,
985 const std::array<const TrackingCluster*, 8>& clArr,
986 const std::array<o2::track::TrackParCov, 8>& extrapOut,
987 const std::array<o2::track::TrackParCov, 8>& extrapInw,
988 const o2::MCCompLabel& lbl) {
989 for (int i = 0; i <= 7; i++) {
990 if (!clArr[i]) {
991 continue;
992 }
993 // interpolated result
994 auto tInt = align::interpolateTrackParCov(extrapInw[i], extrapOut[i]);
995 if (!tInt.isValid()) {
996 continue;
997 }
998 float dY = clArr[i]->getY() - tInt.getY();
999 float dZ = clArr[i]->getZ() - tInt.getZ();
1000 // MC truth at same (alpha, x)
1001 o2::track::TrackPar mcTrkAtX;
1002 const auto mcTrk = mMCReader.getTrack(lbl);
1003 if (mcTrk) {
1004 std::array<float, 3> xyz{(float)mcTrk->GetStartVertexCoordinatesX(), (float)mcTrk->GetStartVertexCoordinatesY(), (float)mcTrk->GetStartVertexCoordinatesZ()};
1005 std::array<float, 3> pxyz{(float)mcTrk->GetStartVertexMomentumX(), (float)mcTrk->GetStartVertexMomentumY(), (float)mcTrk->GetStartVertexMomentumZ()};
1006 TParticlePDG* pPDG = TDatabasePDG::Instance()->GetParticle(mcTrk->GetPdgCode());
1007 if (pPDG) {
1008 mcTrkAtX = o2::track::TrackPar(xyz, pxyz, TMath::Nint(pPDG->Charge() / 3), false);
1009 if (mcTrkAtX.rotate(tInt.getAlpha()) && prop->PropagateToXBxByBz(mcTrkAtX, tInt.getX())) {
1010 auto phi = i == 0 ? tInt.getPhi() : tInt.getPhiPos();
1011 o2::math_utils::bringTo02Pi(phi);
1012 if (clArr[0]) {
1013 getImpactParams(tInt, pv, ip, prop->getNominalBz());
1014 }
1015 (*mDBGOut) << treeName
1016 << "trk=" << tInt
1017 << "mcTrk=" << mcTrkAtX
1018 << "chi2=" << chi2
1019 << "dY=" << dY
1020 << "dZ=" << dZ
1021 << "dcaXY=" << ip[0]
1022 << "dcaZ=" << ip[1]
1023 << "phi=" << phi
1024 << "eta=" << tInt.getEta()
1025 << "lay=" << i - 1
1026 << "\n";
1027 }
1028 }
1029 }
1030 }
1031 };
1032
1033 const auto itsTracks = mRecoData.getITSTracks();
1034 const auto itsMC = mRecoData.getITSTracksMCLabels();
1035 for (size_t iTrk{0}; iTrk < itsTracks.size(); ++iTrk) {
1036 const auto& iTrack = itsTracks[iTrk];
1037 if (std::abs(iTrack.getEta()) > mParams->maxEta || iTrack.getChi2() > mParams->maxChi2 || iTrack.getNClusters() < mParams->minITSCls || iTrack.getPt() < mParams->minPt) {
1038 ++notPassedSel;
1039 continue;
1040 }
1041 const auto& lbl = itsMC[iTrk];
1042 if (!lbl.isCorrect() || !lbl.isValid()) {
1043 ++notPassedSel;
1044 continue;
1045 }
1046 const auto& mc = mMCReader.getTrack(lbl);
1047 if (!mc->isPrimary()) {
1048 ++notPassedSel;
1049 continue;
1050 }
1051
1052 // ideal clusters
1053 std::array<TrackingCluster, 8> cl;
1054 std::array<const TrackingCluster*, 8> clArr{nullptr};
1055 if (mParams->addPVAsCluster) {
1056 const auto& eve = mMCReader.getMCEventHeader(lbl.getSourceID(), lbl.getEventID());
1057 auto trFitOut = iTrack.getParamIn();
1058 pv.setXYZ(eve.GetX(), eve.GetY(), eve.GetZ());
1059 if (!prop->propagateToDCA(pv, trFitOut, prop->getNominalBz(), base::Propagator::MAX_STEP, mParams->CorrType)) {
1060 return;
1061 }
1062 pv.setSigmaX(20e-4f);
1063 pv.setSigmaY(20e-4f);
1064 pv.setSigmaZ(20e-4f);
1065 float cosAlp = NAN, sinAlp = NAN;
1066 o2::math_utils::sincos(trFitOut.getAlpha(), sinAlp, cosAlp);
1067 cl[0].alpha = trFitOut.getAlpha();
1068 cl[0].setXYZ((pv.getX() * cosAlp) + (pv.getY() * sinAlp), (-pv.getX() * sinAlp) + (pv.getY() * cosAlp), pv.getZ());
1069 cl[0].setErrors(0.5f * (pv.getSigmaX2() + pv.getSigmaY2()), pv.getSigmaZ2(), 0.f);
1070 cl[0].setSensorID(-1);
1071 clArr[0] = &cl[0];
1072 }
1073
1074 // collect track clusters into layer slots
1075 int nCl = iTrack.getNClusters();
1076 for (int i = 0; i < nCl; i++) {
1077 const auto& curClu = mITScl[mITSclRef[iTrack.getClusterEntry(i)]];
1078 int sens = curClu.getSensorID();
1079 int llr = geom->getLayer(sens);
1080 if (clArr[1 + llr]) {
1081 LOGP(fatal, "Cluster at lr {} was already assigned, old sens {}, new sens {}", llr, clArr[1 + llr]->getSensorID(), sens);
1082 }
1083 clArr[1 + llr] = &curClu;
1084 }
1085 std::array<o2::track::TrackParCov, 8> extrapOut, extrapInw;
1086 chi2 = 0;
1087 if (!align::doBidirRefit(iTrack, clArr, extrapOut, extrapInw, chi2, mParams->useStableRef, mParams->CorrType)) {
1088 ++fitFail;
1089 continue;
1090 }
1091 writeTree("idealRes", clArr, extrapOut, extrapInw, lbl);
1092
1093 // Propagate MC truth to each cluster's (alpha, x) to get true track direction.
1094 // The shared misalignment evaluators then provide the tracking-frame dy/dz shift.
1095 const auto mcTrk = mMCReader.getTrack(lbl);
1096 if (!mcTrk) {
1097 continue;
1098 }
1099 std::array<float, 3> xyz{(float)mcTrk->GetStartVertexCoordinatesX(), (float)mcTrk->GetStartVertexCoordinatesY(), (float)mcTrk->GetStartVertexCoordinatesZ()};
1100 std::array<float, 3> pxyz{(float)mcTrk->GetStartVertexMomentumX(), (float)mcTrk->GetStartVertexMomentumY(), (float)mcTrk->GetStartVertexMomentumZ()};
1101 TParticlePDG* pPDG = TDatabasePDG::Instance()->GetParticle(mcTrk->GetPdgCode());
1102 if (!pPDG) {
1103 continue;
1104 }
1105 o2::track::TrackPar mcPar(xyz, pxyz, TMath::Nint(pPDG->Charge() / 3), false);
1106
1107 std::array<TrackingCluster, 3> misClArr; // shifted copies for up to 3 IT3 layers
1108 std::array<const TrackingCluster*, 8> clArrMis{};
1109 for (int i = 0; i <= 7; i++) {
1110 clArrMis[i] = clArr[i]; // PV and OB clusters stay the same
1111 }
1112 for (int iLay = 0; iLay < 3; ++iLay) {
1113 if (!clArr[1 + iLay]) {
1114 continue;
1115 }
1116 const auto& orig = *clArr[1 + iLay];
1117 const int sens = orig.getSensorID();
1118 if (!constants::detID::isDetITS3(sens)) {
1119 continue;
1120 }
1121 const int sensorID = constants::detID::getSensorID(sens);
1122 const int layerID = constants::detID::getDetID2Layer(sens);
1123 const auto& sensorMis = mMisalignment[sensorID];
1124
1125 // propagate MC track to cluster's tracking frame to get true slopes
1126 auto mcAtCl = mcPar;
1127 if (!mcAtCl.rotate(orig.alpha) || !prop->PropagateToXBxByBz(mcAtCl, orig.getX())) {
1128 clArrMis[1 + iLay] = nullptr; // can't compute slopes -> drop cluster
1129 continue;
1130 }
1131 const align::MisalignmentFrame misFrame{
1132 .sensorID = sensorID,
1133 .layerID = layerID,
1134 .x = orig.getX(),
1135 .alpha = orig.alpha,
1136 .z = orig.getZ()};
1137 const auto slopes = align::computeTrackSlopes(mcAtCl.getSnp(), mcAtCl.getTgl());
1138
1139 align::MisalignmentShift totalShift;
1140 if (sensorMis.hasLegendre) {
1141 const auto shift = align::evaluateLegendreShift(sensorMis, misFrame, slopes);
1142 if (!shift.accepted) {
1143 clArrMis[1 + iLay] = nullptr; // shifted outside acceptance
1144 continue;
1145 }
1146 totalShift += shift;
1147 }
1148 if (sensorMis.hasInextensional) {
1149 totalShift += align::evaluateInextensionalShift(sensorMis, misFrame, slopes);
1150 }
1151
1152 // create shifted copy: keep x=r (nominal), shift y and z
1153 misClArr[iLay] = orig;
1154 misClArr[iLay].setY(orig.getY() + totalShift.dy);
1155 misClArr[iLay].setZ(orig.getZ() + totalShift.dz);
1156 misClArr[iLay].setSigmaY2(orig.getSigmaY2() + (mParams->misAlgExtCY[sensorID] * mParams->misAlgExtCY[sensorID]));
1157 misClArr[iLay].setSigmaZ2(orig.getSigmaZ2() + (mParams->misAlgExtCZ[sensorID] * mParams->misAlgExtCZ[sensorID]));
1158 clArrMis[1 + iLay] = &misClArr[iLay];
1159 }
1160
1161 // refit with shifted clusters
1162 chi2 = 0;
1163 if (!align::doBidirRefit(iTrack, clArrMis, extrapOut, extrapInw, chi2, mParams->useStableRef, mParams->CorrType)) {
1164 ++fitFailMis;
1165 ++goodRefit; // ideal still succeeded
1166 continue;
1167 }
1168 writeTree("misRes", clArrMis, extrapOut, extrapInw, lbl);
1169
1170 ++goodRefit;
1171 }
1172
1173 LOGP(info, "\tdoMisalignmentStudy: refitted {} out of {} tracks ({} !sel, {} !fit, {} !fitMis)", goodRefit, itsTracks.size(), notPassedSel, fitFail, fitFailMis);
1174}
1175
1176// get IP for current PV
1177// copied from TrackITS::getImpactParams
1178void TrackingStudySpec::getImpactParams(const o2::track::TrackParCov& trk, const o2::dataformats::VertexBase& pv, float ip[2], float bz)
1179{
1180 float x = pv.getX(), y = pv.getY(), z = pv.getZ();
1181 float f1 = trk.getSnp(), r1 = std::sqrt((1.f - f1) * (1.f + f1));
1182 float xt = trk.getX(), yt = trk.getY();
1183 float sn = std::sin(trk.getAlpha()), cs = std::cos(trk.getAlpha());
1184 float a = (x * cs) + (y * sn);
1185 y = (-x * sn) + (y * cs);
1186 x = a;
1187 xt -= x;
1188 yt -= y;
1189 float rp4 = trk.getCurvature(bz);
1190 if ((std::abs(bz) < o2::constants::math::Almost0) || (std::abs(rp4) < o2::constants::math::Almost0)) {
1191 ip[0] = -((xt * f1) - (yt * r1));
1192 ip[1] = trk.getZ() + ((ip[0] * f1 - xt) / r1 * trk.getTgl()) - z;
1193 return;
1194 }
1195 sn = (rp4 * xt) - f1;
1196 cs = (rp4 * yt) + r1;
1197 a = (2 * (xt * f1 - yt * r1)) - (rp4 * (xt * xt + yt * yt));
1198 float rr = std::sqrt((sn * sn) + (cs * cs));
1199 ip[0] = -a / (1 + rr);
1200 float f2 = -sn / rr, r2 = std::sqrt((1.f - f2) * (1.f + f2));
1201 ip[1] = trk.getZ() + (trk.getTgl() / rp4 * std::asin((f2 * r1) - (f1 * r2))) - z;
1202}
1203
1204DataProcessorSpec getTrackingStudySpec(GTrackID::mask_t srcTracks, GTrackID::mask_t srcClusters, bool useMC, bool withPV)
1205{
1206 std::vector<OutputSpec> outputs;
1207 auto dataRequest = std::make_shared<DataRequest>();
1208
1209 dataRequest->requestTracks(srcTracks, useMC);
1210 dataRequest->requestIT3Clusters(useMC);
1211 dataRequest->requestClusters(srcClusters, useMC);
1212 if (withPV) {
1213 dataRequest->requestPrimaryVertices(useMC);
1214 }
1215 auto ggRequest = std::make_shared<o2::base::GRPGeomRequest>(false, // orbitResetTime
1216 true, // GRPECS=true
1217 true, // GRPLHCIF
1218 true, // GRPMagField
1219 true, // askMatLUT
1221 dataRequest->inputs,
1222 true);
1223
1224 return DataProcessorSpec{
1225 .name = "its3-track-study",
1226 .inputs = dataRequest->inputs,
1227 .outputs = outputs,
1228 .algorithm = AlgorithmSpec{adaptFromTask<TrackingStudySpec>(dataRequest, ggRequest, srcTracks, useMC, withPV)},
1229 .options = {}};
1230}
1231
1232} // namespace o2::its3::study
Wrapper container for different reconstructed object types.
Definition of the ITSMFT digit.
Definition of the GeometryManager class.
Definition of the ITSMFT Hit class.
Definition of the BuildTopologyDictionary class for ITS3.
void print() const
int32_t i
Helper for geometry and GRP related CCDB requests.
Global index for barrel track: provides provenance (detectors combination), index in respective array...
Definition of the GeometryTGeo class.
Utility functions for MC particles.
Primary vertex finder.
Wrapper container for different reconstructed object types.
o2::track::TrackParCov TrackParCov
Definition Recon.h:39
Referenc on track indices contributing to the vertex, with possibility chose tracks from specific sou...
void print() const
int getEventID() const
Double_t GetStartVertexCoordinatesX() const
Definition MCTrack.h:82
static TDatabasePDG * Instance()
void checkUpdates(o2::framework::ProcessingContext &pc)
static GRPGeomHelper & instance()
void setRequest(std::shared_ptr< GRPGeomRequest > req)
static std::string getCollisionContextFileName(const std::string_view prefix="")
Definition NameConf.cxx:52
static constexpr float MAX_STEP
Definition Propagator.h:73
GPUd() value_type estimateLTFast(o2 static GPUd() float estimateLTIncrement(const o2 PropagatorImpl * Instance(bool uninitialized=false)
Definition Propagator.h:178
void printKeyValues(bool showProv=true, bool useLogger=false, bool withPadding=true, bool showHash=true) const final
Static class with identifiers, bitmasks and names for ALICE detectors.
Definition DetID.h:58
static constexpr ID ITS
Definition DetID.h:63
static constexpr ID TRD
Definition DetID.h:65
static constexpr ID TPC
Definition DetID.h:64
static constexpr ID TOF
Definition DetID.h:66
ServiceRegistryRef services()
Definition InitContext.h:34
void finaliseCCDB(ConcreteDataMatcher &matcher, void *obj) final
TrackingStudySpec & operator=(TrackingStudySpec &&)=delete
void run(ProcessingContext &pc) final
void endOfStream(EndOfStreamContext &ec) final
This is invoked whenever we have an EndOfStream event.
TrackingStudySpec(const TrackingStudySpec &)=delete
void init(InitContext &ic) final
TrackingStudySpec(TrackingStudySpec &&)=delete
TrackingStudySpec(std::shared_ptr< DataRequest > dr, std::shared_ptr< o2::base::GRPGeomRequest > gr, GTrackID::mask_t src, bool useMC, bool withPV)
TrackingStudySpec & operator=(const TrackingStudySpec &)=delete
static GeometryTGeo * Instance()
int getLayer(int index) const final
Get chip layer, from 0.
void fillMatrixCache(int mask) override
int getClusterEntry(int i) const
Definition TrackITS.h:72
bool initFromDigitContext(std::string_view filename)
MCTrack const * getTrack(o2::MCCompLabel const &) const
size_t getNEvents(int source) const
Get number of events.
o2::dataformats::MCEventHeader const & getMCEventHeader(int source, int event) const
retrieves the MCEventHeader for a given eventID and sourceID
std::vector< MCTrack > const & getTracks(int source, int event) const
variant returning all tracks for source and event at once
bool prepareVertexRefit(const TR &tracks, const o2d::VertexBase &vtxSeed)
Definition PVertexer.h:362
PVertex refitVertex(const std::vector< bool > useTrack, const o2d::VertexBase &vtxSeed)
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
GLint y
Definition glcorearb.h:270
GLenum GLuint GLint GLint layer
Definition glcorearb.h:1310
GLboolean GLboolean GLboolean GLboolean a
Definition glcorearb.h:1233
GLuint GLuint stream
Definition glcorearb.h:1806
GLdouble GLdouble GLdouble z
Definition glcorearb.h:843
constexpr float Almost0
Defining ITS Vertex explicitly as messageable.
Definition Cartesian.h:288
MisalignmentShift evaluateInextensionalShift(const SensorMisalignment &sensor, const MisalignmentFrame &frame, const TrackSlopes &slopes)
bool doBidirRefit(const o2::its::TrackITS &iTrack, std::array< const TrackingCluster< T > *, 8 > &clArr, std::array< o2::track::TrackParametrizationWithError< T >, 8 > &extrapOut, std::array< o2::track::TrackParametrizationWithError< T >, 8 > &extrapInw, T &chi2, bool useStableRef, typename o2::base::PropagatorImpl< T >::MatCorrType corrType)
Definition TrackFit.h:108
MisalignmentModel loadMisalignmentModel(const std::string &jsonPath)
o2::track::TrackParametrizationWithError< T > interpolateTrackParCov(const o2::track::TrackParametrizationWithError< T > &tA, const o2::track::TrackParametrizationWithError< T > &tB)
Definition TrackFit.h:59
MisalignmentShift evaluateLegendreShift(const SensorMisalignment &sensor, const MisalignmentFrame &frame, const TrackSlopes &slopes)
TrackSlopes computeTrackSlopes(double snp, double tgl)
bool isDetITS3(T detID)
Definition SpecsV2.h:210
o2::math_utils::Point3D< T > extractClusterData(const itsmft::CompClusterExt &c, iterator &iter, const o2::its3::TopologyDictionary *dict, T &sig2y, T &sig2z)
Definition IOUtils.h:37
o2::framework::DataProcessorSpec getTrackingStudySpec(o2::dataformats::GlobalTrackID::mask_t srcTracks, o2::dataformats::GlobalTrackID::mask_t srcClus, bool useMC, bool withPV)
std::unordered_map< GTrackID, size_t > T2VMap
o2::dataformats::GlobalTrackID GTrackID
o2::track::TrackParCov int int int float int nCl
const TrackingFrameInfo *const const Cluster *const const float const float bz
double * getX(double *xyDxy, int N)
TrackParCovF TrackParCov
Definition Track.h:33
value_T f1
Definition TrackUtils.h:91
value_T f2
Definition TrackUtils.h:92
TrackParF TrackPar
Definition Track.h:29
a couple of static helper functions to create timestamp values for CCDB queries or override obsolete ...
GlobalIDSet getSingleDetectorRefs(GTrackID gidx) const
const o2::tpc::TrackTPC & getTPCTrack(GTrackID id) const
void createTracksVariadic(T creator, GTrackID::mask_t srcSel=GTrackID::getSourcesMask("all")) const
o2::MCCompLabel getTrackMCLabel(GTrackID id) const
const o2::track::TrackParCov & getTrackParam(GTrackID gidx) const
const o2::MCEventLabel & getPrimaryVertexMCLabel(int i) const
void collectData(o2::framework::ProcessingContext &pc, const DataRequest &request)
const o2::track::TrackParCov & getTrackParamOut(GTrackID gidx) const
const o2::its::TrackITS & getITSTrack(GTrackID gid) const
std::array< GTrackID, GTrackID::NSources > GlobalIDSet
o2::base::PropagatorImpl< float >::MatCorrType CorrType
static constexpr int L2G
Definition Cartesian.h:54
static constexpr int T2L
Definition Cartesian.h:55
static constexpr int T2G
Definition Cartesian.h:56
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"
TStopwatch sw
std::vector< Cluster > clusters