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