CbmRoot
Loading...
Searching...
No Matches
CbmBbaAlignmentTask.cxx
Go to the documentation of this file.
1/* Copyright (C) 2023-2023 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt
2 SPDX-License-Identifier: GPL-3.0-only
3 Authors: S.Gorbunov[committer], N.Bluhme */
4
11
12// Cbm Headers ----------------------
13#include "CbmBbaAlignmentTask.h"
14
15
16// ROOT headers
17
18#include "FairRootManager.h"
19#include "FairRunAna.h"
20#include "TClonesArray.h"
21#include "TFile.h"
22#include "TGeoPhysicalNode.h"
23#include "TH1F.h"
24#include "TRandom.h"
25
26//
28#include "CbmGlobalTrack.h"
29#include "CbmL1.h"
30#include "CbmMuchTrack.h"
32#include "CbmMvdHit.h"
34#include "CbmStsHit.h"
35#include "CbmStsSetup.h"
36#include "CbmStsTrack.h"
38#include "CbmTofTrack.h"
40#include "CbmTrdTrack.h"
42#include "TCanvas.h"
43#include "TGeoPhysicalNode.h"
44#include "bba/BBA.h"
45
46#include <cmath>
47#include <iostream>
48#include <thread>
49
50namespace
51{
52 using namespace cbm::algo;
53}
54
55std::pair<std::string, TGeoHMatrix> AlignNode(std::string path, double shiftX, double shiftY, double shiftZ,
56 double rotX, double rotY, double rotZ)
57{
58
59 TGeoHMatrix result;
60 result.SetDx(shiftX);
61 result.SetDy(shiftY);
62 result.SetDz(shiftZ);
63 result.RotateX(rotX);
64 result.RotateY(rotY);
65 result.RotateZ(rotZ);
66
67 //std::cout << "Alignment matrix for node " << path << " is: " << std::endl;
68 //result.Print();
69 //std::cout << std::endl;
70
71 return std::pair<std::string, TGeoHMatrix>(path, result);
72}
73
75{
76 fNmvdHits = 0;
77 fNstsHits = 0;
78 fNmuchHits = 0;
79 fNtrd1dHits = 0;
80 fNtrd2dHits = 0;
81 fNtofHits = 0;
82 for (const auto& n : fUnalignedTrack.fNodes) {
83 switch (n.fHitSystemId) {
84 case ECbmModuleId::kMvd: fNmvdHits++; break;
85 case ECbmModuleId::kSts: fNstsHits++; break;
86 case ECbmModuleId::kMuch: fNmuchHits++; break;
87 case ECbmModuleId::kTrd: fNtrd1dHits++; break;
88 case ECbmModuleId::kTrd2d: fNtrd2dHits++; break;
89 case ECbmModuleId::kTof: fNtofHits++; break;
90 default: break;
91 }
92 }
93}
94
95// void addVirtualMisalignment()
96// {
97
98
99// }
100
101CbmBbaAlignmentTask::CbmBbaAlignmentTask(const char* name, Int_t iVerbose, TString histoFileName)
102 : FairTask(name, iVerbose)
103 , fHistoFileName(histoFileName)
104{
105 TFile* curFile = gFile;
106 TDirectory* curDirectory = gDirectory;
107
108 if (!(fHistoFileName == "")) {
109 fHistoFile = new TFile(fHistoFileName.Data(), "RECREATE");
110 }
111 else {
112 fHistoFile = gFile;
113 }
114
115 fHistoFile->cd();
116
117 fHistoDir = fHistoFile->mkdir("Bba");
118 fHistoDir->cd();
119
120 gFile = curFile;
121 gDirectory = curDirectory;
122}
123
124
126
127
129{
130
131 {
132 const char* env = std::getenv("OMP_NUM_THREADS");
133 if (env) {
134 fNthreads = TString(env).Atoi();
135 LOG(info) << "BBA: found environment variable OMP_NUM_THREADS = \"" << env
136 << "\", read as integer: " << fNthreads;
137 }
138 }
139
140 // ensure that at least one thread is set
141
142 if (fNthreads < 1) {
143 fNthreads = 1;
144 }
145
146 // fNthreads = 1; // enforce n threads to one
147
148 fFitter.Init();
150
151 //Get ROOT Manager
152 FairRootManager* ioman = FairRootManager::Instance();
153
154 if (!ioman) {
155 LOG(error) << "CbmBbaAlignmentTask::Init :: RootManager not instantiated!";
156 return kERROR;
157 }
158
159 // Get global tracks & matches
160
162
163 fInputGlobalTracks = static_cast<TClonesArray*>(ioman->GetObject("GlobalTrack"));
164 if (!fInputGlobalTracks) {
165 LOG(error) << "CbmBbaAlignmentTask::Init: Global track array not found!";
166 return kERROR;
167 }
168
169 fInputGlobalTrackMatches = static_cast<TClonesArray*>(ioman->GetObject("GlobalTrackMatch"));
170
172 LOG(error) << "CbmBbaAlignmentTask::Init: Global track matches array not found!";
173 //return kERROR;
174 }
175 }
176
177 fInputStsTracks = static_cast<TClonesArray*>(ioman->GetObject("StsTrack"));
178 if (!fInputStsTracks) {
179 LOG(error) << "CbmBbaAlignmentTask::Init: Sts track array not found!";
180 return kERROR;
181 }
182
183 fInputStsTrackMatches = static_cast<TClonesArray*>(ioman->GetObject("StsTrackMatch"));
184
186 LOG(error) << "CbmBbaAlignmentTask::Init: Sts track matches array not found!";
187 //return kERROR;
188 }
189
190 // MC tracks
191 fInputMcTracks = static_cast<TClonesArray*>(ioman->GetObject("MCTrack"));
192 if (!fInputMcTracks) {
193 Warning("CbmBbaAlignmentTask::Init", "mc track array not found!");
194 //return kERROR;
195 }
196
197 fTracks.clear();
198
199
201 const ca::Parameters<ca::fvec>& l1Param = l1->GetParameters();
203 // TO get rid of weird station counting:
204 // fNtrackingStations = l1Param.GetNstationsActive() - l1Param. GetNstationsActive(ca::EDetectorID::kMvd);
205
206 TDirectory* curDirectory = gDirectory;
207
208 fHistoDir->cd();
209
210 for (int i = -1; i < fNtrackingStations; i++) {
211 const char* n1 = Form("Station%d", i);
212 const char* n2 = Form(", station %d", i);
213 if (i == -1) {
214 n1 = "All";
215 n2 = "";
216 }
217 fHistoDir->mkdir(n1);
218 fHistoDir->cd(n1);
219
220 int nBins = 301;
221
222 double sizeX = 0.1;
223 double sizeY = 0.1;
224 double sizePX = 10.;
225 double sizePY = 10.;
226 if (fTrackingMode == kSts) {
227 sizeX = 0.1;
228 sizeY = 0.1;
229 }
230 else if (fTrackingMode == kMcbm) {
231
232 if (i == -1) {
233 sizeX = 5.;
234 sizeY = 5.;
235 sizePX = 10.;
236 sizePY = 10.;
237 }
238
239 if (i == 0 || i == 1) {
240 sizeX = 1.0;
241 sizeY = 1.0;
242 }
243 if (i == 2) {
244 sizeX = 5.;
245 sizeY = 5.;
246 }
247 if (i == 3) {
248 sizeX = 5.;
249 sizeY = 20.;
250 sizePX = 10.;
251 }
252 if (i == 4) {
253 sizeX = 20.;
254 sizeY = 5.;
255 sizePY = 10.;
256 }
257 if (i >= 5) {
258 sizeX = 5.;
259 sizeY = 5.;
260 }
261 }
262
264 new TH1F(Form("ResBeforeAli%s_X", n1), Form("X-Residuals Before Alignment%s", n2), nBins, -sizeX, sizeX));
266 new TH1F(Form("ResBeforeAli%s_Y", n1), Form("Y-Residuals Before Alignment%s", n2), nBins, -sizeY, sizeY));
268 new TH1F(Form("ResAfterAli%s_X", n1), Form("X-Residuals After Alignment%s", n2), nBins, -sizeX, sizeX));
270 new TH1F(Form("ResAfterAli%s_Y", n1), Form("Y-Residuals After Alignment%s", n2), nBins, -sizeY, sizeY));
271
272 hPullsBeforeAlignmentX.push_back(
273 new TH1F(Form("PullBeforeAli%s_X", n1), Form("X-Pulls Before Alignment%s", n2), nBins, -sizePX, sizePX));
274 hPullsBeforeAlignmentY.push_back(
275 new TH1F(Form("PullBeforeAli%s_Y", n1), Form("Y-Pulls Before Alignment%s", n2), nBins, -sizePY, sizePY));
276 hPullsAfterAlignmentX.push_back(
277 new TH1F(Form("PullAfterAli%s_X", n1), Form("X-Pulls After Alignment%s", n2), nBins, -sizePX, sizePX));
278 hPullsAfterAlignmentY.push_back(
279 new TH1F(Form("PullAfterAli%s_Y", n1), Form("Y-Pulls After Alignment%s", n2), nBins, -sizePY, sizePY));
280 }
281
282 for (int i = 0; i < fNtrackingStations + 1; i++) {
283 // set line colors
284 hResidualsAfterAlignmentX[i]->SetLineColor(kRed);
285 hResidualsAfterAlignmentY[i]->SetLineColor(kRed);
286 hPullsAfterAlignmentX[i]->SetLineColor(kRed);
287 hPullsAfterAlignmentY[i]->SetLineColor(kRed);
288 // set histograms to dynamic binning
289 hResidualsBeforeAlignmentX[i]->SetCanExtend(TH1::kXaxis);
290 hResidualsBeforeAlignmentY[i]->SetCanExtend(TH1::kXaxis);
291 hResidualsAfterAlignmentX[i]->SetCanExtend(TH1::kXaxis);
292 hResidualsAfterAlignmentY[i]->SetCanExtend(TH1::kXaxis);
293 hPullsBeforeAlignmentX[i]->SetCanExtend(TH1::kXaxis);
294 hPullsBeforeAlignmentY[i]->SetCanExtend(TH1::kXaxis);
295 hPullsAfterAlignmentX[i]->SetCanExtend(TH1::kXaxis);
296 hPullsAfterAlignmentY[i]->SetCanExtend(TH1::kXaxis);
297 }
298
299 gDirectory = curDirectory;
300
301 return kSUCCESS;
302}
303
304void CbmBbaAlignmentTask::Exec(Option_t* /*opt*/)
305{
306
307 LOG(info) << "BBA: exec event N " << fNEvents;
308
309 fNEvents++;
310
311 if (static_cast<int>(fTracks.size()) >= fMaxNtracks) {
312 return;
313 }
314
316 const ca::Parameters<ca::fvec>& l1Par = l1->GetParameters();
317
318 fFitter.SetDoSmooth(true);
319
320 // select tracks for alignment and store them
321 unsigned num_rejected_fit = 0;
323 int numPosMomentum = 0;
324 int numNegMomentum = 0;
325
327
328 for (int iTr = 0; iTr < fInputStsTracks->GetEntriesFast(); iTr++) {
329 if (static_cast<int>(fTracks.size()) >= fMaxNtracks) {
330 break;
331 }
332 const CbmStsTrack* stsTrack = dynamic_cast<CbmStsTrack*>(fInputStsTracks->At(iTr));
333 if (!stsTrack) continue;
335 if (!fFitter.CreateMvdStsTrack(t.fUnalignedTrack, iTr)) continue;
336 t.MakeConsistent();
337
338 for (auto& n : t.fUnalignedTrack.fNodes) {
339 n.fIsTimeSet = false;
340 // attempt to switch off MVD
341 // if (n.fHitSystemId == ECbmModuleId::kMvd) {
342 // n.fIsXySet = false;
343 // }
344 }
345
347 LOG(debug) << "failed to fit the track! ";
348 num_rejected_fit++;
349 continue;
350 }
351
352 const auto& par = t.fUnalignedTrack.fNodes.front().fParamUp;
353
354 if (t.fNstsHits < l1Par.GetNstationsActive(ca::EDetectorID::kSts)) continue;
355 if (!std::isfinite(par.GetQp())) continue;
356 if (fabs(par.GetQp()) > 1.) continue; // select tracks with min 1 GeV momentum
357 if (par.GetQp() > 0.) {
358 // if (numPosMomentum > numNegMomentum) {
359 // continue;
360 // }
361 numPosMomentum++;
362 }
363 else {
364 numNegMomentum++;
365 }
367 fTracks.push_back(t);
368 }
369 LOG(info) << "BBA: selected " << fTracks.size() << " tracks for alignment, " << numPosMomentum << " positive and "
370 << numNegMomentum << " negative momentum tracks";
371 }
372 else if (fTrackingMode == kMcbm && fInputGlobalTracks) {
373
376
377 for (int iTr = 0; iTr < fInputGlobalTracks->GetEntriesFast(); iTr++) {
378 if (static_cast<int>(fTracks.size()) >= fMaxNtracks) {
379 break;
380 }
381 const CbmGlobalTrack* globalTrack = dynamic_cast<const CbmGlobalTrack*>(fInputGlobalTracks->At(iTr));
382 if (!globalTrack) {
383 LOG(fatal) << "BBA: null pointer to the global track!";
384 break;
385 }
387 if (!fFitter.CreateGlobalTrack(t.fUnalignedTrack, *globalTrack)) {
388 LOG(fatal) << "BBA: can not create the global track for the fit! ";
389 break;
390 }
391 t.MakeConsistent();
392
393 for (auto& n : t.fUnalignedTrack.fNodes) {
394 n.fIsTimeSet = false;
395 if (n.fHitSystemId == ECbmModuleId::kTrd) {
396 if (n.fMxy.Dx2() < n.fMxy.Dy2()) {
397 n.fMxy.SetNdfY(0);
398 }
399 else {
400 n.fMxy.SetNdfX(0);
401 }
402 }
403 }
404
405 if (t.fNstsHits < 2) continue;
406 if (t.fNtofHits < 2) continue;
407 if (t.fNtrd1dHits + t.fNtrd2dHits < 2) continue;
409 LOG(debug) << "failed to fit the track! ";
410 num_rejected_fit++;
411 continue;
412 }
414 fTracks.push_back(t);
415 }
416 } // end of mcbm tracking mode
417
418 if (num_rejected_fit != 0) {
419 LOG(warn) << "failed to fit " << num_rejected_fit << " tracks";
420 }
421
422 // fix the material radiation thickness to avoid the chi2 rattling at the material map bin boundaries
423 // (to be improved)
424 unsigned num_fix_rad_max = 0;
425 unsigned num_fix_rad_min = 0;
426 unsigned num_rejected_fit2 = 0;
427 for (TrackContainer& t : fTracks) {
428 if (!t.fIsActive) continue;
429 for (unsigned int in = 0; in < t.fUnalignedTrack.fNodes.size(); in++) {
430 CbmKfTrackFitter::TrajectoryNode& node = t.fUnalignedTrack.fNodes[in];
431 if (!node.fIsFitted) {
432 t.fIsActive = false;
433 num_rejected_fit2++;
434 break;
435 }
436 node.fIsRadThickFixed = true;
437 if (node.fRadThick > 0.01) {
438 LOG(debug) << "CbmBbaAlignmentTask: radiation thickness is too large: " << node.fRadThick;
439 num_fix_rad_max++;
440 node.fRadThick = 0.01;
441 }
442 if (node.fRadThick < 0.001) {
443 LOG(debug) << "CbmBbaAlignmentTask: radiation thickness is too small: " << node.fRadThick;
444 num_fix_rad_min++;
445 node.fRadThick = 0.001;
446 }
447 }
448 }
449 if (num_fix_rad_max != 0) {
450 LOG(warn) << "CbmBbaAlignmentTask: radiation thickness is too large " << num_fix_rad_max << " times";
451 }
452 if (num_fix_rad_min != 0) {
453 LOG(warn) << "CbmBbaAlignmentTask: radiation thickness is too small " << num_fix_rad_min << " times";
454 }
455 if (num_rejected_fit2 != 0) {
456 LOG(warn) << "Rejected fit 2 " << num_rejected_fit2 << " tracks";
457 }
458
459 // ensure that all the hits are not too much deviated from the trajectory
460 // (to be improved)
461 unsigned num_rejected_traj = 0;
462 for (TrackContainer& t : fTracks) {
463 if (!t.fIsActive) continue;
464 const double cutX = 8.;
465 const double cutY = 8.;
466 for (unsigned int in = 0; in < t.fUnalignedTrack.fNodes.size(); in++) {
467 CbmKfTrackFitter::Trajectory tr = t.fUnalignedTrack;
469 if (!node.fIsXySet) {
470 continue;
471 }
472 node.fIsXySet = false; // exclude the node from the fit
473 if (!fFitter.FitTrajectory(tr)) {
474 t.fIsActive = false;
475 num_rejected_traj++;
476 break;
477 }
478 double x_residual = node.fMxy.X() - node.fParamDn.X(); // this should be the difference vector
479 double y_residual = node.fMxy.Y() - node.fParamDn.Y();
480 double x_pull = x_residual / sqrt(node.fMxy.Dx2() + node.fParamDn.GetCovariance(0, 0));
481 double y_pull = y_residual / sqrt(node.fMxy.Dy2() + node.fParamDn.GetCovariance(1, 1));
482 if (!node.fIsFitted || (node.fMxy.NdfX() > 0. && fabs(x_pull) > cutX)
483 || (node.fMxy.NdfY() > 0. && fabs(y_pull) > cutY)) {
484 t.fIsActive = false;
485 num_rejected_traj++;
486 break;
487 }
488 }
489 }
490 if (num_rejected_traj != 0) {
491 LOG(warn) << "Rejected " << num_rejected_traj << " tracks for hits deviating from the trajectory";
492 }
493
494 TClonesArray* inputTracks = nullptr;
495 if (fTrackingMode == kSts) {
496 inputTracks = fInputStsTracks;
497 }
498 else if (fTrackingMode == kMcbm) {
499 inputTracks = fInputGlobalTracks;
500 }
501 static int statTracks = 0;
502 statTracks += inputTracks->GetEntriesFast();
503
504 unsigned active_tracks = 0;
505 for (auto& t : fTracks) {
506 if (t.fIsActive) active_tracks++;
507 }
508
509 LOG(info) << "Bba: " << inputTracks->GetEntriesFast() << " tracks in event, " << statTracks << " tracks in total, "
510 << fTracks.size() << " tracks selected for alignment, " << active_tracks << " tracks active";
511}
512
513
514// This function destroys alignment if stations are defined as alignment bodies, do not use
515void CbmBbaAlignmentTask::ConstrainStation(std::vector<double>& par, int iSta, int ixyz)
516{
517 // set overall shifts of the station to zero
518
519 double mean = 0.;
520 int n = 0;
521 for (unsigned int i = 0; i < fAlignmentBodies.size(); i++) {
522 if (fAlignmentBodies[i].fTrackingStation == iSta) {
523 mean += par[3 * i + ixyz];
524 n++;
525 }
526 }
527 if (n <= 0) return;
528 mean /= n;
529 for (unsigned int i = 0; i < fAlignmentBodies.size(); i++) {
530 if (fAlignmentBodies[i].fTrackingStation == iSta) {
531 par[3 * i + ixyz] -= mean;
532 }
533 }
534}
535
536void CbmBbaAlignmentTask::ApplyConstraints(std::vector<double>& par)
537{
538 // apply alignment parameters to the hits
539 ConstrainStation(par, 0, 2);
543}
544
545// This function destroys alignment if stations are defined as alignment bodies, do not use
546void CbmBbaAlignmentTask::ApplyAlignment(const std::vector<double>& par)
547{
548 // apply alignment parameters to the hits
549
550 std::vector<double> parConstrained = par;
551
552 //ApplyConstraints(parConstrained);
553
554 for (TrackContainer& t : fTracks) {
555
556 for (unsigned int in = 0; in < t.fUnalignedTrack.fNodes.size(); in++) {
557 CbmKfTrackFitter::TrajectoryNode& node = t.fUnalignedTrack.fNodes[in];
558 if (!node.fIsXySet) {
559 continue;
560 }
561 CbmKfTrackFitter::TrajectoryNode& nodeAligned = t.fAlignedTrack.fNodes[in];
562 int iSensor = node.fReference1;
563 assert(iSensor >= 0 && iSensor < (int) fSensors.size());
564 assert(nodeAligned.fReference1 == node.fReference1);
565
566 Sensor& s = fSensors[iSensor];
567 if (s.fAlignmentBody < 0) continue;
568
569 double dx = parConstrained[3 * s.fAlignmentBody + 0];
570 double dy = parConstrained[3 * s.fAlignmentBody + 1];
571 double dz = parConstrained[3 * s.fAlignmentBody + 2];
572
573 // shift the hit
574 nodeAligned.fMxy.SetX(node.fMxy.X() + dx);
575 nodeAligned.fMxy.SetY(node.fMxy.Y() + dy);
576 nodeAligned.fZ = node.fZ + dz;
577
578 // shift the fitted track to the Z position of the hit
579 {
580 auto& p = nodeAligned.fParamDn;
581 p.SetX(p.X() + p.Tx() * dz);
582 p.SetY(p.Y() + p.Ty() * dz);
583 p.SetZ(nodeAligned.fZ);
584 }
585 {
586 auto& p = nodeAligned.fParamUp;
587 p.SetX(p.X() + p.Tx() * dz);
588 p.SetY(p.Y() + p.Ty() * dz);
589 p.SetZ(nodeAligned.fZ);
590 }
591 }
592 }
593
594 static int statNCalls = 0;
595 statNCalls++;
596 if (statNCalls % 100 == 0) {
597 std::vector<double>& r = parConstrained;
598 LOG(info) << "Current parameters: ";
599 for (int is = 0; is < fNalignmentBodies; is++) {
600 auto& b = fAlignmentBodies[is];
601 LOG(info) << "Body " << is << " sta " << b.fTrackingStation << ": x " << r[3 * is + 0] << " y " << r[3 * is + 1]
602 << " z " << r[3 * is + 2];
603 }
604 }
605}
606
607
608double CbmBbaAlignmentTask::CostFunction(const std::vector<double>& par)
609{
610 // apply new parameters to the hits
611
612 for (auto& t : fTracks) {
613 t.fAlignedTrack = t.fUnalignedTrack;
614 }
615
616 ApplyAlignment(par);
617
618 std::vector<double> chi2Thread(fNthreads, 0.);
619 std::vector<long> ndfThread(fNthreads, 0);
620
621 auto fitThread = [&](int iThread) {
623 fit.SetDoSmooth(false);
625
626 for (unsigned int itr = iThread; itr < fTracks.size(); itr += fNthreads) {
627 auto& t = fTracks[itr];
628 if (!t.fIsActive) continue;
629
630 // fit.SetDebugInfo(" track " + std::to_string(itr));
631
632 for (unsigned int in = 0; in < t.fAlignedTrack.fNodes.size(); in++) {
633 CbmKfTrackFitter::TrajectoryNode& node = t.fAlignedTrack.fNodes[in];
634 if (!node.fIsFitted) {
635 LOG(fatal) << "BBA: Cost function: aligned node is not fitted! ";
636 }
637 }
638 bool ok = fit.FitTrajectory(t.fAlignedTrack);
639 double chi2 = t.fAlignedTrack.fNodes.back().fParamDn.GetChiSq();
640 double ndf = t.fAlignedTrack.fNodes.back().fParamDn.GetNdf();
641 if (!ok || !std::isfinite(chi2) || chi2 <= 0. || ndf <= 0.) {
642 // TODO: deactivate the track and continue
643 LOG(fatal) << "BBA: fit failed! ";
644 chi2 = 0.;
645 ndf = 0.;
646 }
647 chi2Thread[iThread] += chi2;
648 ndfThread[iThread] += (int) ndf;
649 }
650 };
651
652 std::vector<std::thread> threads(fNthreads);
653
654 // run n threads
655 for (int i = 0; i < fNthreads; i++) {
656 threads[i] = std::thread(fitThread, i);
657 }
658
659 // wait for the threads to finish
660 for (auto& th : threads) {
661 th.join();
662 }
663
664 fChi2Total = 0.;
665 fNdfTotal = 0;
666 for (int i = 0; i < fNthreads; i++) {
667 fChi2Total += chi2Thread[i];
668 fNdfTotal += ndfThread[i];
669 }
670
671 double cost = fChi2Total / (fNdfTotal + 1);
672 if (fFixedNdf > 0 && fNdfTotal != fFixedNdf) {
673 cost = 1.e30;
674 }
675 //LOG(info) << "BBA: calculate cost function: ndf " << fNdfTotal << ", cost " << cost
676 // << ", diff to ideal cost: " << cost - fCostIdeal << ", diff to initial cost: " << cost - fCostInitial;
677
678 return cost;
679}
680
682{
683 // perform the alignment
684
685 LOG(info) << "BBA: start the alignment procedure with " << fTracks.size() << " tracks ...";
686
687 // collect the sensors from the hits
688 // TODO: read it from the detector setup
689
690 if (fTrackingMode == kSts) {
692 }
693
694 std::set<Sensor> sensorSet;
695 for (auto& t : fTracks) {
696 for (auto& n : t.fUnalignedTrack.fNodes) {
697 if (!n.fIsXySet) {
698 continue;
699 }
700 Sensor s;
701 s.fSystemId = n.fHitSystemId;
702 s.fSensorId = n.fHitAddress;
703 // TODO: get the station index from n.fHitSystemId, n.fHitAddress
704 s.fTrackingStation = n.fMaterialLayer;
705
708 }
709
712 // hardcode path to physical node in geometry
713 if (s.fTrackingStation == 2) {
714 s.fNodePath = "/cave_1/trd_v22h_mcbm_0/layer01_20101/module9_101001001";
715 }
716 else if (s.fTrackingStation == 3) {
717 s.fNodePath = "/cave_1/trd_v22h_mcbm_0/layer02_10202/module8_101002001";
718 }
719 else if (s.fTrackingStation == 4) {
720 s.fNodePath = "/cave_1/trd_v22h_mcbm_0/layer03_11303/module8_101303001";
721 }
722 }
723 else if (s.fSystemId == ECbmModuleId::kTof) {
724 s.fSensorId = CbmTofAddress::GetRpcFullId(n.fHitAddress);
725 }
726
727 sensorSet.insert(s);
728 }
729 }
730
731 fSensors.clear();
732 for (auto& s : sensorSet) { // convert set to a vector
733 fSensors.push_back(s);
734 }
735 std::sort(fSensors.begin(), fSensors.end(), [](const Sensor& a, const Sensor& b) { return a < b; });
736
737 for (auto& t : fTracks) {
738 for (auto& n : t.fUnalignedTrack.fNodes) {
739 if (!n.fIsXySet) {
740 continue;
741 }
742 Sensor s;
743 s.fSystemId = n.fHitSystemId;
744 s.fSensorId = n.fHitAddress;
745 s.fTrackingStation = n.fMaterialLayer;
746
749 }
750
753 }
754 else if (s.fSystemId == ECbmModuleId::kTof) {
755 s.fSensorId = CbmTofAddress::GetRpcFullId(n.fHitAddress);
756 }
757 auto iter = std::lower_bound( // find the sensor in the vector
758 fSensors.begin(), fSensors.end(), s, [](const Sensor& a, const Sensor& b) { return a < b; });
759 assert(iter != fSensors.end());
760 assert(*iter == s);
761 int iSensor = std::distance(fSensors.begin(), iter);
762 n.fReference1 = iSensor;
763 }
764 t.fAlignedTrack = t.fUnalignedTrack;
765 }
766
767 if (1) { // one alignment body per tracking station
768
771 for (int i = 0; i < fNalignmentBodies; i++) {
772 fAlignmentBodies[i].fTrackingStation = i;
773 }
774 for (auto& s : fSensors) {
775 s.fAlignmentBody = s.fTrackingStation;
776 }
777 }
778 else { // one alignment body per sensor
779
782 for (int unsigned i = 0; i < fSensors.size(); i++) {
783 fSensors[i].fAlignmentBody = i;
784 fAlignmentBodies[i].fTrackingStation = fSensors[i].fTrackingStation;
785 }
786 }
787
788 LOG(info) << "BBA: " << fSensors.size() << " sensors, " << fNalignmentBodies << " alignment bodies";
789
790 LOG(info) << "\n Sensors: ";
791 {
792 for (unsigned int is = 0; is < fSensors.size(); is++) {
793 auto& s = fSensors[is];
794 LOG(info) << "Sensor " << is << " sys " << s.fSystemId << " sta " << s.fTrackingStation << " body "
795 << s.fAlignmentBody;
796 }
797 }
798
799 LOG(info) << "\n Alignment bodies: ";
800 {
801 for (int is = 0; is < fNalignmentBodies; is++) {
802 auto& b = fAlignmentBodies[is];
803 LOG(info) << "Body: " << is << " station: " << b.fTrackingStation;
804 }
805 }
806
807 int nParameters = 3 * fNalignmentBodies; // x, y, z
808
809 std::vector<bba::Parameter> par(nParameters);
810
811 for (int ip = 0; ip < nParameters; ip++) {
812 bba::Parameter& p = par[ip];
813 p.SetActive(0);
814 p.SetValue(0.);
815 p.SetBoundaryMin(-2.);
816 p.SetBoundaryMax(2.);
817 p.SetStepMin(1.e-4);
818 p.SetStepMax(0.1);
819 p.SetStep(1.e-2);
820 }
821
822 // set active parameters
823 // fix first and last station
824 for (unsigned int ib = 0; ib < fAlignmentBodies.size(); ib++) {
825 auto& b = fAlignmentBodies[ib];
826 if ((fTrackingMode == kSts && b.fTrackingStation == 0)
828 par[3 * ib + 0].SetActive(0);
829 par[3 * ib + 1].SetActive(0);
830 par[3 * ib + 2].SetActive(0);
831 }
832 else {
833 par[3 * ib + 0].SetActive(1);
834 par[3 * ib + 1].SetActive(1);
835 par[3 * ib + 2].SetActive(1);
836 }
837 // fix x in the middle station
839 par[3 * ib + 0].SetActive(0);
840 }
841 }
842
843
844 // set parameter ranges for different subsystems
845 for (const auto& s : fSensors) {
846 int ib = s.fAlignmentBody;
847 if (ib < 0 || ib >= fNalignmentBodies) continue;
848 //auto& b = fAlignmentBodies[ib];
849 for (int j = 0; j < 3; j++) {
850 bba::Parameter& p = par[ib * 3 + j];
851 p.SetStepMin(1.e-4);
852 if (s.fSystemId == ECbmModuleId::kTrd || s.fSystemId == ECbmModuleId::kTrd2d) {
853 p.SetStepMin(10.e-4);
854 }
855 else if (s.fSystemId == ECbmModuleId::kTof) {
856 p.SetStepMin(10.e-4);
857 }
858 }
859 }
860
861 gRandom->SetSeed(1);
862 LOG(info) << "Simulated misalignment range: " << fSimulatedMisalignmentRange;
864
865 for (int is = 0; is < fNalignmentBodies; is++) {
866 bba::Parameter& px = par[3 * is + 0];
867 bba::Parameter& py = par[3 * is + 1];
868 bba::Parameter& pz = par[3 * is + 2];
869
870 // +- d cm misalignment
871 if (px.IsActive()) {
872 px.SetValue(gRandom->Uniform(2. * d) - d);
873 }
874 if (py.IsActive()) {
875 py.SetValue(gRandom->Uniform(2. * d) - d);
876 }
877 if (pz.IsActive()) {
878 pz.SetValue(gRandom->Uniform(2. * d) - d);
879 }
880 }
881
882 if (0) { // test
883 LOG(info) << "STS sensor paths: ";
884 for (auto& s : fSensors) {
885 if (s.fSystemId != ECbmModuleId::kSts) {
886 continue;
887 }
888 const CbmStsElement* el = CbmStsSetup::Instance()->GetElement(s.fSensorId, kStsSensor);
889 if (!el) {
890 LOG(error) << "Element not found: " << s.fSensorId;
891 continue;
892 }
893 el->Print();
894 const auto* n = el->GetPnode();
895 if (n) {
896 LOG(info) << "Node: ";
897 n->Print();
898 }
899 }
900 }
901
902 bba::BBA alignment;
903
904 alignment.setParameters(par);
905
906 auto lambdaCostFunction = [this](const std::vector<double>& p) { return CostFunction(p); };
907
908 alignment.setChi2(lambdaCostFunction);
909 alignment.printInfo();
910
911 std::vector<double> parIdeal;
912 std::vector<double> parMisaligned;
913 {
914 const std::vector<double>& r = alignment.getResult();
915 for (unsigned int i = 0; i < r.size(); i++) {
916 parMisaligned.push_back(r[i]);
917 parIdeal.push_back(0.);
918 }
919 }
920
921 {
922 std::cout << "initial cost function..." << std::endl;
923
924 fCostInitial = CostFunction(parMisaligned);
925
926 unsigned tracks_rejected = 0;
927 for (auto& t : fTracks) {
928 double ndf = t.fAlignedTrack.fNodes.back().fParamDn.GetNdf();
929 double chi2 = t.fAlignedTrack.fNodes.back().fParamDn.GetChiSq();
930 if (ndf < 0. || chi2 < 0. || !std::isfinite(ndf) || !std::isfinite(chi2)) {
931 t.fIsActive = false;
932 tracks_rejected++;
933 }
934 }
935 std::cout << "reject " << tracks_rejected << " bad tracks and recalculate the initial cost function..."
936 << std::endl;
937 fCostInitial = CostFunction(parMisaligned);
938 fFixedNdf = -1; //fNdfTotal;
939
940 // plot the residuals before alignment
941
942 for (const auto& t : fTracks) {
943 if (!t.fIsActive) continue;
944 // calculate the residuals for all tracks at each TrajectoryNode before alignment
945 for (unsigned int in = 0; in < t.fAlignedTrack.fNodes.size(); in++) {
946 CbmKfTrackFitter::Trajectory tr = t.fAlignedTrack;
948
949 if (!node.fIsXySet) {
950 continue;
951 }
952 node.fIsXySet = false;
953
955
956 if (!node.fIsFitted) {
957 continue;
958 }
959
960 Sensor& s = fSensors[node.fReference1];
961
962 double x_residual = node.fMxy.X() - node.fParamDn.X(); // this should be the difference vector
963 double y_residual = node.fMxy.Y() - node.fParamDn.Y();
964 double x_pull = x_residual / sqrt(node.fMxy.Dx2() + node.fParamDn.GetCovariance(0, 0));
965 double y_pull = y_residual / sqrt(node.fMxy.Dy2() + node.fParamDn.GetCovariance(1, 1));
966
967 if (node.fMxy.NdfX() > 0) {
968 hResidualsBeforeAlignmentX[0]->Fill(x_residual);
969 hPullsBeforeAlignmentX[0]->Fill(x_pull);
970 }
971 if (node.fMxy.NdfY() > 0) {
972 hResidualsBeforeAlignmentY[0]->Fill(y_residual);
973 hPullsBeforeAlignmentY[0]->Fill(y_pull);
974 }
975
976 hResidualsBeforeAlignmentX[s.fTrackingStation + 1]->Fill(x_residual);
977 hResidualsBeforeAlignmentY[s.fTrackingStation + 1]->Fill(y_residual);
978
979 hPullsBeforeAlignmentX[s.fTrackingStation + 1]->Fill(x_pull);
980 hPullsBeforeAlignmentY[s.fTrackingStation + 1]->Fill(y_pull);
981 }
982 }
983 }
984
985 std::cout << "calculate ideal cost function..." << std::endl;
986 fCostIdeal = CostFunction(parIdeal);
987
988
989 LOG(info) << " Cost function for the true parameters: " << fCostIdeal;
990 LOG(info) << " Cost function for the initial parameters: " << fCostInitial;
991
992 // run the alignment
993 alignment.align();
994
995 double costResult = CostFunction(alignment.getResult());
996
997 CostFunction(alignment.getResult());
998 CostFunction(alignment.getResult());
999
1000 // Create alignment matrices:
1001 std::vector<double> result = alignment.getResult();
1002 std::map<std::string, TGeoHMatrix> alignmentMatrices;
1003
1004 for (auto& s : fSensors) {
1005 int i = s.fAlignmentBody;
1006 assert(i < fNalignmentBodies);
1007 if (i < 0) continue;
1008
1009 // For STS sensors
1010 if (s.fSystemId == ECbmModuleId::kSts) {
1011 // get sensor for sensorID
1012 const CbmStsElement* el = CbmStsSetup::Instance()->GetElement(s.fSensorId, kStsSensor);
1013 if (!el) {
1014 LOG(error) << "Element not found: " << s.fSensorId;
1015 continue;
1016 }
1017 const auto* n = el->GetPnode();
1018 LOG(debug) << "Node: ";
1019 LOG(debug) << n->GetName();
1020 alignmentMatrices.insert(
1021 AlignNode(n->GetName(), result[i * 3 + 0], result[i * 3 + 1], result[i * 3 + 2], 0., 0., 0.));
1022 }
1023 // for Trd stations
1024 else if (s.fSystemId == ECbmModuleId::kTrd) {
1025 LOG(debug) << "Node: ";
1026 LOG(debug) << s.fNodePath;
1027 alignmentMatrices.insert(
1028 AlignNode(s.fNodePath, result[i * 3 + 0], result[i * 3 + 1], result[i * 3 + 2], 0., 0., 0.));
1029 }
1030 // for Trd2D station
1031 else if (s.fSystemId == ECbmModuleId::kTrd2d) {
1032 LOG(debug) << "Node: ";
1033 LOG(debug) << s.fNodePath;
1034 alignmentMatrices.insert(
1035 AlignNode(s.fNodePath, result[i * 3 + 0], result[i * 3 + 1], result[i * 3 + 2], 0., 0., 0.));
1036 }
1037 // TODO: for TOF stations
1038 // else {
1039 // LOG(info) << "fSystemId: " << s.fSystemId << "\tfTrackongStation: " << s.fTrackingStation
1040 // << "\t SensorId: " << s.fSensorId;
1041 // alignmentMatrices.insert(AlignNode(Form("fTrackingStation %i", s.fTrackingStation), result[i * 3 + 0],
1042 // result[i * 3 + 1], result[i * 3 + 2], 0., 0., 0.));
1043 // }
1044 } // sensors
1045
1046 // save matrices to disk
1047 TFile* misalignmentMatrixRootfile =
1048 new TFile("AlignmentMatrices_mcbm_beam_2022_05_23_nickel_finetuning.root", "RECREATE");
1049 if (misalignmentMatrixRootfile->IsOpen()) {
1050 gDirectory->WriteObject(&alignmentMatrices, "AlignmentMatrices");
1051 misalignmentMatrixRootfile->Write();
1052 misalignmentMatrixRootfile->Close();
1053 }
1054
1055 LOG(info) << "\n Misaligned parameters: ";
1056 for (int is = 0; is < fNalignmentBodies; is++) {
1057 const std::vector<double>& r = parMisaligned;
1058 LOG(info) << "Body " << is << ": x " << r[3 * is + 0] << " y " << r[3 * is + 1] << " z " << r[3 * is + 2];
1059 }
1060
1061 LOG(info) << "\n Alignment results: ";
1062 {
1063 const std::vector<double>& r = alignment.getResult();
1064 for (int is = 0; is < fNalignmentBodies; is++) {
1065 auto& b = fAlignmentBodies[is];
1066 LOG(info) << "Body " << is << " sta " << b.fTrackingStation << ": x " << r[3 * is + 0] << " y " << r[3 * is + 1]
1067 << " z " << r[3 * is + 2];
1068 }
1069 }
1070
1071 LOG(info) << "\n Alignment results, constrained: ";
1072 {
1073 std::vector<double> r = alignment.getResult();
1074 //ApplyConstraints(r);
1075 for (int is = 0; is < fNalignmentBodies; is++) {
1076 auto& b = fAlignmentBodies[is];
1077 LOG(info) << "Body " << is << " sta " << b.fTrackingStation << ": x " << r[3 * is + 0] << " y " << r[3 * is + 1]
1078 << " z " << r[3 * is + 2];
1079 }
1080 }
1081
1082 unsigned active_tracks = 0;
1083 for (auto& t : fTracks) {
1084 if (t.fIsActive) active_tracks++;
1085 }
1086
1087 LOG(info) << "\n";
1088
1089 LOG(info) << "Bba: " << fTracks.size() << " tracks used for alignment\n";
1090 LOG(info) << " " << active_tracks << " tracks active\n";
1091
1092 LOG(info) << " Cost function for the true parameters: " << fCostIdeal;
1093 LOG(info) << " Cost function for the initial parameters: " << fCostInitial;
1094 LOG(info) << " Cost function for the aligned parameters: " << costResult;
1095
1096 for (auto& t : fTracks) {
1097 if (!t.fIsActive) continue;
1098 // calculate the residuals for all tracks at each TrajectoryNode after alignment
1099 // TODO: put into function
1100 for (unsigned int in = 0; in < t.fAlignedTrack.fNodes.size(); in++) {
1101 CbmKfTrackFitter::Trajectory tr = t.fAlignedTrack;
1103 if (!node.fIsXySet) {
1104 continue;
1105 }
1106 node.fIsXySet = false;
1108
1109 Sensor& s = fSensors[node.fReference1];
1110
1111 double x_residual = node.fMxy.X() - node.fParamDn.X();
1112 double y_residual = node.fMxy.Y() - node.fParamDn.Y();
1113 double x_pull = x_residual / sqrt(node.fMxy.Dx2() + node.fParamDn.GetCovariance(0, 0));
1114 double y_pull = y_residual / sqrt(node.fMxy.Dy2() + node.fParamDn.GetCovariance(1, 1));
1115
1116 if (node.fMxy.NdfX() > 0) {
1117 hResidualsAfterAlignmentX[0]->Fill(x_residual);
1118 hPullsAfterAlignmentX[0]->Fill(x_pull);
1119 }
1120 if (node.fMxy.NdfY() > 0) {
1121 hResidualsAfterAlignmentY[0]->Fill(y_residual);
1122 hPullsAfterAlignmentY[0]->Fill(y_pull);
1123 }
1124
1125 hResidualsAfterAlignmentX[s.fTrackingStation + 1]->Fill(x_residual);
1126 hResidualsAfterAlignmentY[s.fTrackingStation + 1]->Fill(y_residual);
1127
1128 hPullsAfterAlignmentX[s.fTrackingStation + 1]->Fill(x_pull);
1129 hPullsAfterAlignmentY[s.fTrackingStation + 1]->Fill(y_pull);
1130 }
1131 }
1132
1133 // store the histograms
1134
1135 TDirectory* curr = gDirectory;
1136 TFile* currentFile = gFile;
1137 // Open output file and write histograms
1138
1139 fHistoFile->cd();
1141 if (!(fHistoFileName == "")) {
1142 fHistoFile->Close();
1143 fHistoFile->Delete();
1144 }
1145 gFile = currentFile;
1146 gDirectory = curr;
1147}
1148
1149
1151{
1152 if (!obj->IsFolder())
1153 obj->Write();
1154 else {
1155 TDirectory* cur = gDirectory;
1156 TFile* currentFile = gFile;
1157
1158 TDirectory* sub = cur->GetDirectory(obj->GetName());
1159 sub->cd();
1160 TList* listSub = (static_cast<TDirectory*>(obj))->GetList();
1161 TIter it(listSub);
1162 while (TObject* obj1 = it()) {
1163 WriteHistosCurFile(obj1);
1164 }
1165 cur->cd();
1166 gFile = currentFile;
1167 gDirectory = cur;
1168 }
1169}
1170
ClassImp(CbmBbaAlignmentTask)
std::pair< std::string, TGeoHMatrix > AlignNode(std::string path, double shiftX, double shiftY, double shiftZ, double rotX, double rotY, double rotZ)
Task class for alignment.
Time-slice/event reader for CA tracker in CBM (header)
@ kMvd
Micro-Vertex Detector.
@ kTrd
Transition Radiation Detector.
@ kTof
Time-of-flight Detector.
@ kSts
Silicon Tracking System.
@ kTrd2d
TRD-FASP Detector (FIXME)
@ kMuch
Muon detection system.
@ kStsSensor
Data class for a reconstructed hit in the STS.
Data class for STS tracks.
friend fvec sqrt(const fvec &a)
int Int_t
an example of alignment using BBA package
TClonesArray * fInputGlobalTrackMatches
void WriteHistosCurFile(TObject *obj)
std::vector< TrackContainer > fTracks
CbmBbaAlignmentTask(const char *name="CbmBbaAlignmentTask", Int_t iVerbose=0, TString histoFileName="CbmBbaAlignmentHisto.root")
std::vector< TH1F * > hPullsBeforeAlignmentX
TClonesArray * fInputStsTrackMatches
std::vector< TH1F * > hPullsAfterAlignmentX
TClonesArray * fInputMcTracks
TClonesArray * fInputStsTracks
std::vector< TH1F * > hResidualsAfterAlignmentX
std::vector< AlignmentBody > fAlignmentBodies
std::vector< TH1F * > hPullsBeforeAlignmentY
void ApplyAlignment(const std::vector< double > &par)
void ApplyConstraints(std::vector< double > &par)
TClonesArray * fInputGlobalTracks
std::vector< TH1F * > hResidualsAfterAlignmentY
std::vector< Sensor > fSensors
std::vector< TH1F * > hResidualsBeforeAlignmentX
void Exec(Option_t *opt)
std::vector< TH1F * > hResidualsBeforeAlignmentY
std::vector< TH1F * > hPullsAfterAlignmentY
double CostFunction(const std::vector< double > &par)
void ConstrainStation(std::vector< double > &par, int iSta, int ixyz)
void SetSkipUnmeasuredCoordinates(bool skip=true)
skip unmeasured coordinates
bool CreateMvdStsTrack(Trajectory &kfTrack, int stsTrackIndex)
set the input data arrays
bool CreateGlobalTrack(Trajectory &kfTrack, int globalTrackIndex)
void FixMomentumForMs(bool fix=true)
fix the inverse momentum for the Multiple Scattering calculation
void SetDefaultMomentumForMs(double p)
set the default inverse momentum for the Multiple Scattering calculation
void SetDoSmooth(bool doSmooth)
do the KF-smoothing to define track pars at all the nodes
bool FitTrajectory(CbmKfTrackFitter::Trajectory &t)
fit the track
void SetNoMultipleScattering()
set the default inverse momentum for the Multiple Scattering calculation
static CbmL1 * Instance()
Pointer to CbmL1 instance.
Definition CbmL1.h:171
ca::Framework * fpAlgo
Pointer to the L1 track finder algorithm.
Definition CbmL1.h:425
Class representing an element of the STS setup.
TGeoPhysicalNode * GetPnode() const
virtual void Print(Option_t *opt="") const
CbmStsElement * GetElement(Int_t address, Int_t level)
static CbmStsSetup * Instance()
static CbmStsTrackingInterface * Instance()
Gets pointer to the instance of the CbmStsTrackingInterface class.
int GetTrackingStationIndex(const CbmHit *hit) const override
Gets a tracking station of a CbmHit.
static int32_t GetRpcFullId(uint32_t address)
int GetNtrackingStations() const
Gets actual number of stations, provided by the current geometry setup.
const Parameters< fvec > & GetParameters() const
Gets a pointer to the Framework parameters object.
Definition CaFramework.h:87
A container for all external parameters of the CA tracking algorithm.
int GetNstationsActive() const
Gets total number of active stations.
T X() const
Gets x position [cm].
T Y() const
Gets y position [cm].
void SetX(T v)
Sets x position [cm].
T GetCovariance(int i, int j) const
Get covariance matrix element.
CbmKfTrackFitter::Trajectory fUnalignedTrack
CbmKfTrackFitter::Trajectory fAlignedTrack
cbm::algo::kf::TrackParamD fParamUp
fitted track parameters upstream the node
double fZ
Z coordinate of the node.
bool fIsFitted
true if the track parameters at the node are fitted
cbm::algo::kf::MeasurementXy< double > fMxy
== Hit information ( if present )
int fReference1
some reference can be set by the user
cbm::algo::kf::TrackParamD fParamDn
fitted track parameters downstream the node
bool fIsRadThickFixed
true if the radiation thickness is fixed to the fRadThick value
A trajectory to be fitted.
std::vector< TrajectoryNode > fNodes
nodes on the trajectory