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