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();
186 o2::its::GeometryTGeo::Instance()->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::L2G, o2::math_utils::TransformType::T2G));
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]{0.f, 0.f};
884 // The MC vertex always defines the reference point of the impact
885 // parameters. Whether it *also* enters the track fit is a separate
886 // question, controlled by addPVAsCluster: constraining the fit to the same
887 // vertex the DCA is measured against compresses the DCA distribution, so
888 // for pointing-resolution studies the constraint must be switched off
889 // while the reference point stays defined.
890 const auto& eve = mMCReader.getMCEventHeader(lbl.getSourceID(), lbl.getEventID());
891 pv.setXYZ(eve.GetX(), eve.GetY(), eve.GetZ());
892 if (mParams->addPVAsCluster) {
893 auto trFitOut = iTrack.getParamIn();
894 if (!prop->propagateToDCA(pv, trFitOut, bz, base::Propagator::MAX_STEP, mParams->CorrType)) {
895 return;
896 }
897 pv.setSigmaX(20e-4f);
898 pv.setSigmaY(20e-4f);
899 pv.setSigmaZ(20e-4f);
900 float cosAlp = NAN, sinAlp = NAN;
901 o2::math_utils::sincos(trFitOut.getAlpha(), sinAlp, cosAlp);
902 cl[0].alpha = trFitOut.getAlpha();
903 cl[0].setXYZ((pv.getX() * cosAlp) + (pv.getY() * sinAlp), (-pv.getX() * sinAlp) + (pv.getY() * cosAlp), pv.getZ());
904 cl[0].setErrors(0.5f * (pv.getSigmaX2() + pv.getSigmaY2()), pv.getSigmaZ2(), 0.f);
905 cl[0].setSensorID(-1);
906 clArr[0] = &cl[0];
907 }
908
909 // collect track clusters into layer slots
910 int nCl = iTrack.getNClusters();
911 for (int i = 0; i < nCl; i++) {
912 const auto& curClu = mITScl[mITSclRef[iTrack.getClusterEntry(i)]];
913 int sens = curClu.getSensorID();
914 int llr = geom->getLayer(sens);
915 if (clArr[1 + llr]) {
916 LOGP(fatal, "Cluster at lr {} was already assigned, old sens {}, new sens {}", llr, clArr[1 + llr]->getSensorID(), sens);
917 }
918 clArr[1 + llr] = &curClu;
919 }
920
921 std::array<o2::track::TrackParCov, 8> extrapOut, extrapInw;
922 float chi2{0};
923 if (!align::doBidirRefit(iTrack, clArr, extrapOut, extrapInw, chi2, mParams->useStableRef, mParams->CorrType)) {
924 ++fitFail;
925 return;
926 }
927
928 for (int i = 0; i <= 7; i++) {
929 if (clArr[i]) {
930 const auto tInt = align::interpolateTrackParCov(extrapInw[i], extrapOut[i]);
931 if (!tInt.isValid()) {
932 continue;
933 }
934 auto phi = i == 0 ? tInt.getPhi() : tInt.getPhiPos();
935 o2::math_utils::bringTo02Pi(phi);
936 // pv is always set, so the DCA no longer depends on the PV being
937 // part of the fit (it used to be gated on clArr[0])
938 getImpactParams(tInt, pv, ip, bz);
939 (*mDBGOut) << "res"
940 << "dYInt=" << clArr[i]->getY() - tInt.getY()
941 << "dZInt=" << clArr[i]->getZ() - tInt.getZ()
942 << "dYIn=" << clArr[i]->getY() - extrapInw[i].getY()
943 << "dZIn=" << clArr[i]->getZ() - extrapInw[i].getZ()
944 << "dYOut=" << clArr[i]->getY() - extrapOut[i].getY()
945 << "dZOut=" << clArr[i]->getZ() - extrapOut[i].getZ()
946 << "chi2=" << chi2
947 << "clY=" << clArr[i]->getY()
948 << "clZ=" << clArr[i]->getZ()
949 << "clX=" << clArr[i]->getX()
950 << "alpha=" << clArr[i]->alpha
951 << "sens=" << clArr[i]->getSensorID()
952 << "phi=" << phi
953 << "dcaXY=" << ip[0]
954 << "dcaZ=" << ip[1]
955 << "pt=" << tInt.getPt()
956 << "chip=" << constants::detID::getSensorID(clArr[i]->getSensorID())
957 << "lay=" << i - 1
958 << "\n";
959 }
960 }
961 ++goodRefit;
962 };
963
964 const auto itsTracks = mRecoData.getITSTracks();
965 const auto itsMC = mRecoData.getITSTracksMCLabels();
966 for (size_t iTrk{0}; iTrk < itsTracks.size(); ++iTrk) {
967 const auto& iTrack = itsTracks[iTrk];
968 const auto& lbl = itsMC[iTrk];
969 const auto& mc = mMCReader.getTrack(lbl);
970 if (std::abs(iTrack.getEta()) > mParams->maxEta || iTrack.getChi2() > mParams->maxChi2 || iTrack.getNClusters() < mParams->minITSCls || iTrack.getPt() < mParams->minPt || !lbl.isCorrect() || !mc->isPrimary()) {
971 ++notPassedSel;
972 continue;
973 }
974 doRefits(iTrack, lbl);
975 }
976
977 LOGP(info, "\trefitted {} out of {} tracks ({} !sel, {} !fit)", goodRefit, itsTracks.size(), notPassedSel, fitFail);
978}
979
980void TrackingStudySpec::doMisalignmentStudy()
981{
982 LOGP(info, "Doing misalignment study");
983 const auto prop = o2::base::Propagator::Instance();
984 const auto geom = o2::its::GeometryTGeo::Instance();
985
986 int goodRefit{0}, notPassedSel{0}, fitFail{0}, fitFailMis{0};
988 float ip[2]{0.f, 0.f};
989 float chi2{0};
990 auto writeTree = [&](const char* treeName,
991 const std::array<const TrackingCluster*, 8>& clArr,
992 const std::array<o2::track::TrackParCov, 8>& extrapOut,
993 const std::array<o2::track::TrackParCov, 8>& extrapInw,
994 const o2::MCCompLabel& lbl) {
995 const auto mcTrk = mMCReader.getTrack(lbl);
996 if (!mcTrk) {
997 return;
998 }
999 TParticlePDG* pPDG = TDatabasePDG::Instance()->GetParticle(mcTrk->GetPdgCode());
1000 if (!pPDG) {
1001 return;
1002 }
1003 const std::array<float, 3> xyz{(float)mcTrk->GetStartVertexCoordinatesX(), (float)mcTrk->GetStartVertexCoordinatesY(), (float)mcTrk->GetStartVertexCoordinatesZ()};
1004 const std::array<float, 3> pxyz{(float)mcTrk->GetStartVertexMomentumX(), (float)mcTrk->GetStartVertexMomentumY(), (float)mcTrk->GetStartVertexMomentumZ()};
1005 const int mcCharge = TMath::Nint(pPDG->Charge() / 3);
1006
1007 auto writeRow = [&](const o2::track::TrackParCov& tInt, float dY, float dZ, int layOut) {
1008 // MC truth at same (alpha, x)
1009 o2::track::TrackPar mcTrkAtX(xyz, pxyz, mcCharge, false);
1010 if (!mcTrkAtX.rotate(tInt.getAlpha()) || !prop->PropagateToXBxByBz(mcTrkAtX, tInt.getX())) {
1011 return;
1012 }
1013 auto phi = (layOut < 0) ? tInt.getPhi() : tInt.getPhiPos();
1014 o2::math_utils::bringTo02Pi(phi);
1015 // pv is always set, so the DCA no longer depends on the PV being part of
1016 // the fit (this used to be gated on clArr[0], which left dcaXY/dcaZ
1017 // holding the previous track's values when the PV was disabled)
1018 getImpactParams(tInt, pv, ip, prop->getNominalBz());
1019 (*mDBGOut) << treeName
1020 << "trk=" << tInt
1021 << "mcTrk=" << mcTrkAtX
1022 << "chi2=" << chi2
1023 << "dY=" << dY
1024 << "dZ=" << dZ
1025 << "dcaXY=" << ip[0]
1026 << "dcaZ=" << ip[1]
1027 << "phi=" << phi
1028 << "eta=" << tInt.getEta()
1029 << "lay=" << layOut
1030 << "\n";
1031 };
1032
1033 // Track-level row, lay = -1: the impact parameters with respect to the MC
1034 // vertex. With addPVAsCluster the PV pseudo-cluster occupies slot 0 and
1035 // supplies this row. Without it slot 0 is empty, so the row is emitted here
1036 // from the innermost track state; otherwise every DCA histogram downstream
1037 // (all of which select lay == -1) would come out empty.
1038 //
1039 // The interpolation at slot i deliberately excludes the cluster of slot i
1040 // (that is what makes dY/dZ unbiased residuals), so it must be updated with
1041 // that cluster before the impact parameters are taken. Without the update a
1042 // misalignment confined to the innermost layer is invisible here by
1043 // construction: the state would be built from the outer layers alone and
1044 // would be bit-identical to the ideal one.
1045 if (!clArr[0]) {
1046 for (int i = 1; i <= 7; ++i) {
1047 if (!clArr[i]) {
1048 continue;
1049 }
1050 auto tFull = align::interpolateTrackParCov(extrapInw[i], extrapOut[i]);
1051 if (!tFull.isValid() || !tFull.update(*clArr[i])) {
1052 continue;
1053 }
1054 writeRow(tFull, 0.f, 0.f, -1);
1055 break;
1056 }
1057 }
1058
1059 for (int i = 0; i <= 7; i++) {
1060 if (!clArr[i]) {
1061 continue;
1062 }
1063 // interpolated result
1064 const auto tInt = align::interpolateTrackParCov(extrapInw[i], extrapOut[i]);
1065 if (!tInt.isValid()) {
1066 continue;
1067 }
1068 writeRow(tInt, clArr[i]->getY() - tInt.getY(), clArr[i]->getZ() - tInt.getZ(), i - 1);
1069 }
1070 };
1071
1072 const auto itsTracks = mRecoData.getITSTracks();
1073 const auto itsMC = mRecoData.getITSTracksMCLabels();
1074 for (size_t iTrk{0}; iTrk < itsTracks.size(); ++iTrk) {
1075 const auto& iTrack = itsTracks[iTrk];
1076 if (std::abs(iTrack.getEta()) > mParams->maxEta || iTrack.getChi2() > mParams->maxChi2 || iTrack.getNClusters() < mParams->minITSCls || iTrack.getPt() < mParams->minPt) {
1077 ++notPassedSel;
1078 continue;
1079 }
1080 const auto& lbl = itsMC[iTrk];
1081 if (!lbl.isCorrect() || !lbl.isValid()) {
1082 ++notPassedSel;
1083 continue;
1084 }
1085 const auto& mc = mMCReader.getTrack(lbl);
1086 if (!mc->isPrimary()) {
1087 ++notPassedSel;
1088 continue;
1089 }
1090
1091 // ideal clusters
1092 std::array<TrackingCluster, 8> cl;
1093 std::array<const TrackingCluster*, 8> clArr{nullptr};
1094 // see the note in doRefits(): the MC vertex always defines the DCA
1095 // reference point, addPVAsCluster only decides whether it constrains the fit
1096 const auto& eve = mMCReader.getMCEventHeader(lbl.getSourceID(), lbl.getEventID());
1097 pv.setXYZ(eve.GetX(), eve.GetY(), eve.GetZ());
1098 if (mParams->addPVAsCluster) {
1099 auto trFitOut = iTrack.getParamIn();
1100 if (!prop->propagateToDCA(pv, trFitOut, prop->getNominalBz(), base::Propagator::MAX_STEP, mParams->CorrType)) {
1101 continue; // skip this track, not the rest of the study
1102 }
1103 pv.setSigmaX(20e-4f);
1104 pv.setSigmaY(20e-4f);
1105 pv.setSigmaZ(20e-4f);
1106 float cosAlp = NAN, sinAlp = NAN;
1107 o2::math_utils::sincos(trFitOut.getAlpha(), sinAlp, cosAlp);
1108 cl[0].alpha = trFitOut.getAlpha();
1109 cl[0].setXYZ((pv.getX() * cosAlp) + (pv.getY() * sinAlp), (-pv.getX() * sinAlp) + (pv.getY() * cosAlp), pv.getZ());
1110 cl[0].setErrors(0.5f * (pv.getSigmaX2() + pv.getSigmaY2()), pv.getSigmaZ2(), 0.f);
1111 cl[0].setSensorID(-1);
1112 clArr[0] = &cl[0];
1113 }
1114
1115 // collect track clusters into layer slots
1116 int nCl = iTrack.getNClusters();
1117 for (int i = 0; i < nCl; i++) {
1118 const auto& curClu = mITScl[mITSclRef[iTrack.getClusterEntry(i)]];
1119 int sens = curClu.getSensorID();
1120 int llr = geom->getLayer(sens);
1121 if (clArr[1 + llr]) {
1122 LOGP(fatal, "Cluster at lr {} was already assigned, old sens {}, new sens {}", llr, clArr[1 + llr]->getSensorID(), sens);
1123 }
1124 clArr[1 + llr] = &curClu;
1125 }
1126 std::array<o2::track::TrackParCov, 8> extrapOut, extrapInw;
1127 chi2 = 0;
1128 if (!align::doBidirRefit(iTrack, clArr, extrapOut, extrapInw, chi2, mParams->useStableRef, mParams->CorrType)) {
1129 ++fitFail;
1130 continue;
1131 }
1132 writeTree("idealRes", clArr, extrapOut, extrapInw, lbl);
1133
1134 // Propagate MC truth to each cluster's (alpha, x) to get true track direction.
1135 // The shared misalignment evaluators then provide the tracking-frame dy/dz shift.
1136 const auto mcTrk = mMCReader.getTrack(lbl);
1137 if (!mcTrk) {
1138 continue;
1139 }
1140 std::array<float, 3> xyz{(float)mcTrk->GetStartVertexCoordinatesX(), (float)mcTrk->GetStartVertexCoordinatesY(), (float)mcTrk->GetStartVertexCoordinatesZ()};
1141 std::array<float, 3> pxyz{(float)mcTrk->GetStartVertexMomentumX(), (float)mcTrk->GetStartVertexMomentumY(), (float)mcTrk->GetStartVertexMomentumZ()};
1142 TParticlePDG* pPDG = TDatabasePDG::Instance()->GetParticle(mcTrk->GetPdgCode());
1143 if (!pPDG) {
1144 continue;
1145 }
1146 o2::track::TrackPar mcPar(xyz, pxyz, TMath::Nint(pPDG->Charge() / 3), false);
1147
1148 std::array<TrackingCluster, 3> misClArr; // shifted copies for up to 3 IT3 layers
1149 std::array<const TrackingCluster*, 8> clArrMis{};
1150 for (int i = 0; i <= 7; i++) {
1151 clArrMis[i] = clArr[i]; // PV and OB clusters stay the same
1152 }
1153 for (int iLay = 0; iLay < 3; ++iLay) {
1154 if (!clArr[1 + iLay]) {
1155 continue;
1156 }
1157 const auto& orig = *clArr[1 + iLay];
1158 const int sens = orig.getSensorID();
1159 if (!constants::detID::isDetITS3(sens)) {
1160 continue;
1161 }
1162 const int sensorID = constants::detID::getSensorID(sens);
1163 const int layerID = constants::detID::getDetID2Layer(sens);
1164 const auto& sensorMis = mMisalignment[sensorID];
1165
1166 // propagate MC track to cluster's tracking frame to get true slopes
1167 auto mcAtCl = mcPar;
1168 if (!mcAtCl.rotate(orig.alpha) || !prop->PropagateToXBxByBz(mcAtCl, orig.getX())) {
1169 clArrMis[1 + iLay] = nullptr; // can't compute slopes -> drop cluster
1170 continue;
1171 }
1172 const align::MisalignmentFrame misFrame{
1173 .sensorID = sensorID,
1174 .layerID = layerID,
1175 .x = orig.getX(),
1176 .alpha = orig.alpha,
1177 .z = orig.getZ()};
1178 const auto slopes = align::computeTrackSlopes(mcAtCl.getSnp(), mcAtCl.getTgl());
1179
1180 align::MisalignmentShift totalShift;
1181 if (sensorMis.hasLegendre) {
1182 const auto shift = align::evaluateLegendreShift(sensorMis, misFrame, slopes);
1183 if (!shift.accepted) {
1184 clArrMis[1 + iLay] = nullptr; // shifted outside acceptance
1185 continue;
1186 }
1187 totalShift += shift;
1188 }
1189 if (sensorMis.hasInextensional) {
1190 totalShift += align::evaluateInextensionalShift(sensorMis, misFrame, slopes);
1191 }
1192
1193 // create shifted copy: keep x=r (nominal), shift y and z
1194 misClArr[iLay] = orig;
1195 misClArr[iLay].setY(orig.getY() + totalShift.dy);
1196 misClArr[iLay].setZ(orig.getZ() + totalShift.dz);
1197 misClArr[iLay].setSigmaY2(orig.getSigmaY2() + (mParams->misAlgExtCY[sensorID] * mParams->misAlgExtCY[sensorID]));
1198 misClArr[iLay].setSigmaZ2(orig.getSigmaZ2() + (mParams->misAlgExtCZ[sensorID] * mParams->misAlgExtCZ[sensorID]));
1199 clArrMis[1 + iLay] = &misClArr[iLay];
1200 }
1201
1202 // refit with shifted clusters
1203 chi2 = 0;
1204 if (!align::doBidirRefit(iTrack, clArrMis, extrapOut, extrapInw, chi2, mParams->useStableRef, mParams->CorrType)) {
1205 ++fitFailMis;
1206 ++goodRefit; // ideal still succeeded
1207 continue;
1208 }
1209 writeTree("misRes", clArrMis, extrapOut, extrapInw, lbl);
1210
1211 ++goodRefit;
1212 }
1213
1214 LOGP(info, "\tdoMisalignmentStudy: refitted {} out of {} tracks ({} !sel, {} !fit, {} !fitMis)", goodRefit, itsTracks.size(), notPassedSel, fitFail, fitFailMis);
1215}
1216
1217// get IP for current PV
1218// copied from TrackITS::getImpactParams
1219void TrackingStudySpec::getImpactParams(const o2::track::TrackParCov& trk, const o2::dataformats::VertexBase& pv, float ip[2], float bz)
1220{
1221 float x = pv.getX(), y = pv.getY(), z = pv.getZ();
1222 float f1 = trk.getSnp(), r1 = std::sqrt((1.f - f1) * (1.f + f1));
1223 float xt = trk.getX(), yt = trk.getY();
1224 float sn = std::sin(trk.getAlpha()), cs = std::cos(trk.getAlpha());
1225 float a = (x * cs) + (y * sn);
1226 y = (-x * sn) + (y * cs);
1227 x = a;
1228 xt -= x;
1229 yt -= y;
1230 float rp4 = trk.getCurvature(bz);
1231 if ((std::abs(bz) < o2::constants::math::Almost0) || (std::abs(rp4) < o2::constants::math::Almost0)) {
1232 ip[0] = -((xt * f1) - (yt * r1));
1233 ip[1] = trk.getZ() + ((ip[0] * f1 - xt) / r1 * trk.getTgl()) - z;
1234 return;
1235 }
1236 sn = (rp4 * xt) - f1;
1237 cs = (rp4 * yt) + r1;
1238 a = (2 * (xt * f1 - yt * r1)) - (rp4 * (xt * xt + yt * yt));
1239 float rr = std::sqrt((sn * sn) + (cs * cs));
1240 ip[0] = -a / (1 + rr);
1241 float f2 = -sn / rr, r2 = std::sqrt((1.f - f2) * (1.f + f2));
1242 ip[1] = trk.getZ() + (trk.getTgl() / rp4 * std::asin((f2 * r1) - (f1 * r2))) - z;
1243}
1244
1245DataProcessorSpec getTrackingStudySpec(GTrackID::mask_t srcTracks, GTrackID::mask_t srcClusters, bool useMC, bool withPV)
1246{
1247 std::vector<OutputSpec> outputs;
1248 auto dataRequest = std::make_shared<DataRequest>();
1249
1250 dataRequest->requestTracks(srcTracks, useMC);
1251 dataRequest->requestIT3Clusters(useMC);
1252 dataRequest->requestClusters(srcClusters, useMC);
1253 if (withPV) {
1254 dataRequest->requestPrimaryVertices(useMC);
1255 }
1256 auto ggRequest = std::make_shared<o2::base::GRPGeomRequest>(false, // orbitResetTime
1257 true, // GRPECS=true
1258 true, // GRPLHCIF
1259 true, // GRPMagField
1260 true, // askMatLUT
1262 dataRequest->inputs,
1263 true);
1264
1265 return DataProcessorSpec{
1266 .name = "its3-track-study",
1267 .inputs = dataRequest->inputs,
1268 .outputs = outputs,
1269 .algorithm = AlgorithmSpec{adaptFromTask<TrackingStudySpec>(dataRequest, ggRequest, srcTracks, useMC, withPV)},
1270 .options = {}};
1271}
1272
1273} // 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.
float chi2
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:53
GPUd() value_type estimateLTFast(o2 static GPUd() float estimateLTIncrement(const o2 PropagatorImpl * Instance(bool uninitialized=false)
Definition Propagator.h:180
void printKeyValues(bool showProv=true, bool useLogger=false, bool withPadding=true, bool showHash=true) const final
Static class with identifiers, bitmasks and names for ALICE detectors.
Definition DetID.h:60
int ID
Detector identifiers: continuous, starting from 0.
Definition DetID.h:63
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
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)
double * getY(double *xyDxy, int N)
TrackParCovF TrackParCov
Definition Track.h:33
value_T f1
Definition TrackUtils.h:91
value_T f2
Definition TrackUtils.h:92
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
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"
TStopwatch sw
std::vector< Cluster > clusters