ATLAS Offline Software
Loading...
Searching...
No Matches
BeamspotVertexPreProcessor.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3*/
4
12
14#include "GaudiKernel/SmartDataPtr.h"
15
16//++ new one
17
23#include "TrkTrack/Track.h"
26
27#include <cmath>
28#include <algorithm>
29#include <limits>
30
31
32namespace Trk {
33
34//________________________________________________________________________
36 const std::string & name,
37 const IInterface * parent)
38 : AthAlgTool(type,name,parent)
39 , m_trackTypeCounter(AlignTrack::NTrackTypes,0)
40{
41 declareInterface<IAlignTrackPreProcessor>(this);
42}
43
44//________________________________________________________________________
46= default;
47
48//________________________________________________________________________
50{
51 ATH_MSG_INFO("BeamspotVertexPreProcessor::initialize()");
52
53 // configure main track selector if requested
54 if (!m_trkSelector.empty()) {
55 if (m_trkSelector.retrieve().isFailure())
56 ATH_MSG_ERROR("Failed to retrieve tool "<<m_trkSelector<<". No Track Selection will be done.");
57 else
58 ATH_MSG_INFO("Retrieved " << m_trkSelector);
59 }
60
61 if (m_refitTracks) {
62 // configure main track fitter
63 if(m_trackFitter.retrieve().isFailure()) {
64 ATH_MSG_FATAL("Could not get " << m_trackFitter);
65 return StatusCode::FAILURE;
66 }
67 ATH_MSG_INFO("Retrieved " << m_trackFitter);
68
69 // configure straight-line track fitter if requested
70 if (!m_useSingleFitter) {
71 if (m_SLTrackFitter.retrieve().isFailure()) {
72 ATH_MSG_FATAL("Could not get " << m_SLTrackFitter);
73 return StatusCode::FAILURE;
74 }
75 ATH_MSG_INFO("Retrieved " << m_SLTrackFitter);
76 }
77
78 // TrackToVertexIPEstimator
79 if (m_trackToVertexIPEstimatorTool.retrieve().isFailure()) {
80 ATH_MSG_FATAL("Can not retrieve TrackToVertexIPEstimator of type " << m_trackToVertexIPEstimatorTool.typeAndName());
81 return StatusCode::FAILURE;
82 } else {
83 ATH_MSG_INFO ( "Retrieved TrackToVertexIPEstimator Tool " << m_trackToVertexIPEstimatorTool.typeAndName() );
84 }
85
86 // configure Atlas extrapolator
87 if (m_extrapolator.retrieve().isFailure()) {
88 ATH_MSG_FATAL("Failed to retrieve tool "<<m_extrapolator);
89 return StatusCode::FAILURE;
90 }
91 ATH_MSG_INFO("Retrieved " << m_extrapolator);
92
93 // configure beam-spot conditions service
94 ATH_CHECK(m_beamSpotKey.initialize());
95
96 ATH_CHECK(m_PVContainerName.initialize());
97
98 // configure beam-spot track selector if requested
100 if(m_BSTrackSelector.empty()) {
101 ATH_MSG_FATAL("Requested BeamSpot track selection but Track Selector not configured");
102 return StatusCode::FAILURE;
103 }
104 if (m_BSTrackSelector.retrieve().isFailure()) {
105 ATH_MSG_FATAL("Could not get " << m_BSTrackSelector);
106 return StatusCode::FAILURE;
107 }
108 ATH_MSG_INFO("Retrieved " << m_BSTrackSelector);
109 }
110
111 } // end of 'if (m_refitTracks)'
112
113 else if (m_doBeamspotConstraint) {
114 ATH_MSG_FATAL("Requested beam-spot constraint but RefitTracks is False.");
115 return StatusCode::FAILURE;
116 }
117
119 if ( m_alignModuleTool.retrieve().isFailure() ) {
120 ATH_MSG_FATAL("Failed to retrieve tool " << m_alignModuleTool);
121 return StatusCode::FAILURE;
122 }
123 else {
124 ATH_MSG_INFO("Retrieved tool " << m_alignModuleTool);
125
126 ATH_MSG_INFO("************************************************************************");
127 ATH_MSG_INFO("* *");
128 ATH_MSG_INFO("* You have requested the Full Vertex Constraint option. *");
129 ATH_MSG_INFO("* It is your duty to assure that all detector elements *");
130 ATH_MSG_INFO("* used for track fitting are also loaded in the alignment framework!!! *");
131 ATH_MSG_INFO("* *");
132 ATH_MSG_INFO("* Also make sure the accurate track covariance matrix *");
133 ATH_MSG_INFO("* is returned by the GlobalChi2Fitter! *");
134 ATH_MSG_INFO("* *");
135 ATH_MSG_INFO("************************************************************************");
136 }
137
138 }
139 return StatusCode::SUCCESS;
140}
141
142
143bool CompareTwoTracks::operator()(VxTrackAtVertex vtxTrk){ // MD: took away deref*
144
145 ITrackLink* trkLink = vtxTrk.trackOrParticleLink();
146 LinkToTrackParticleBase* linkToTrackParticle = dynamic_cast<Trk::LinkToTrackParticleBase*>(trkLink);
147 if(!linkToTrackParticle) return false;
148 const TrackParticleBase* tpb = *(linkToTrackParticle->cptr());
149
150 const Track* originalTrk = tpb->originalTrack();
151
152 bool equal = false;
153 // compare the addresses of these two tracks directly
154 if(m_method.find("compareAddress") != std::string::npos){
155 if (m_track == originalTrk) equal = true;
156 }
157
158 // compare the perigee parameters of these two tracks, should safer
159 if(m_method.find("comparePerigee") != std::string::npos){
160 const Trk::Perigee * measPer1 = m_track->perigeeParameters();
161 const Trk::Perigee * measPer2 = originalTrk->perigeeParameters();
162 if(! (measPer1 && measPer2 )) equal = false;
163 else{
164 float diff = std::abs(std::numeric_limits<float>::epsilon());
165 if( ( std::abs(measPer1->parameters()[Trk::d0] - measPer2->parameters()[Trk::d0]) > diff)
166 || ( std::abs(measPer1->parameters()[Trk::z0] - measPer2->parameters()[Trk::z0]) > diff)
167 || ( std::abs(measPer1->parameters()[Trk::phi] - measPer2->parameters()[Trk::phi]) > diff)
168 || ( std::abs(measPer1->parameters()[Trk::theta] - measPer2->parameters()[Trk::theta]) > diff)
169 || ( std::abs(measPer1->parameters()[Trk::qOverP] - measPer2->parameters()[Trk::qOverP]) > diff))
170 equal = false;
171 }
172 }
173 return equal;
174}
175
176
177
179
180 if(0 == vtx->vertexType()) {
181 ATH_MSG_DEBUG("this primary vertex has been rejected as type dummy");
182 return false;
183 }
184 if (vtx->numberDoF() <= 0){
185 ATH_MSG_WARNING(" VERY STRANGE!!!, this primary vertex has been rejected as non-positive DoF "<< vtx->numberDoF() <<" the type of this vertex: "<< vtx->vertexType() );
186 return false;
187 }
188 if (static_cast<int>(vtx->vxTrackAtVertex().size()) < m_minTrksInVtx){
189 ATH_MSG_DEBUG(" this primary vertex vxTrackAtVertex size: "<< vtx->vxTrackAtVertex().size() );
190 return false;
191 }
192 return true;
193}
194
195
197
198 if (vtx->numberDoF() <= 0){
199 ATH_MSG_WARNING(" VERY STRANGE!!! , the updated vertex has been rejected as non-positive DoF: "<< vtx->numberDoF() <<" the type of this vertex:"<< vtx->vertexType() );
200 return false;
201 }
202
203 if (static_cast<int>(vtx->vxTrackAtVertex().size()) < m_minTrksInVtx){
204 ATH_MSG_DEBUG(" the updated vertex has been rejected as vxTrackAtVertex size: "<< vtx->vxTrackAtVertex().size() );
205 return false;
206 }
207
208 if ((vtx->covariancePosition())(0,0)<=0 ||
209 (vtx->covariancePosition())(1,1)<=0 ||
210 (vtx->covariancePosition())(2,2)<=0){
211 ATH_MSG_WARNING(" VERY STRANGE!!! , this updated vertex has been rejected as negative diagonal error matrix ");
212 return false;
213 }
214 return true;
215}
216
217
219{
220 if(!vertices) return false;
221
222 for (const xAOD::Vertex* vtx : *vertices) {
223 if (vtx->vertexType() != 1) break;
224 if (isAssociatedToVertex(track, vtx)) return true;
225 }
226
227 return false;
228}
229
230
231//____________________________________________________________________________
233{
234 if(!vertex) return false;
235
236 std::vector<VxTrackAtVertex > vertexTracks = vertex->vxTrackAtVertex();
237 Trk::CompareTwoTracks thisCompare(track, "compareAddress");
238
239 std::vector<VxTrackAtVertex >::const_iterator iVxTrackBegin = vertexTracks.begin();
240 std::vector<VxTrackAtVertex >::const_iterator iVxTrackEnd = vertexTracks.end();
241
242 std::vector<VxTrackAtVertex>::const_iterator findResult = std::find_if(iVxTrackBegin, iVxTrackEnd, std::move(thisCompare));
243
244 return findResult != iVxTrackEnd;
245}
246
247
249
250 // do clean up firstly
251 m_allTracksVector.clear();
252
253 const EventContext& ctx = Gaudi::Hive::currentContext();
255
256 for(const xAOD::Vertex* vtx : *vtxReadHandle){
257 if(!selectVertices(vtx)) {
258 ATH_MSG_DEBUG("this vertex did not pass the primary vertex selection...");
259 continue;
260 }
261 if (vtx->vxTrackAtVertexAvailable()){
262 std::vector<VxTrackAtVertex> vtxTracks = vtx->vxTrackAtVertex();
263 m_allTracksVector.emplace_back(vtx, vtxTracks);
264 }
265 else {
266 ATH_MSG_DEBUG("this vertex did not pass the vxTrackAtVertexAvailable() call...");
267 continue;
268 }
269 }
270
271 ATH_MSG_DEBUG("m_allTracksVector size: "<<m_allTracksVector.size());
272}
273
274
276
277 const xAOD::Vertex* findVxCandidate = nullptr;
278
279 for(const auto& thisPair : m_allTracksVector){
280 auto iVxTrackBegin = thisPair.second.begin();
281 auto iVxTrackEnd = thisPair.second.end();
282 Trk::CompareTwoTracks thisCompare(track, "compareAddress");
283
284 auto findResult = std::find_if(iVxTrackBegin, iVxTrackEnd, std::move(thisCompare));
285
286 if(findResult != iVxTrackEnd){
287 ATH_MSG_DEBUG("the found VxTrackAtVertex: "<<*findResult);
288 findVxCandidate = thisPair.first;
289 break;
290 }
291 }
292
293 return findVxCandidate;
294}
295
296
298
299 const EventContext& ctx = Gaudi::Hive::currentContext();
300 const VertexOnTrack * vot = nullptr;
301 const xAOD::Vertex* tmpVtx = nullptr;
302 const xAOD::Vertex* updatedVtx = nullptr;
303
304 const xAOD::Vertex* findVtx = findVertexCandidate(track);
305
306 ATH_MSG_DEBUG("findVtx in provideVotFromVertex: "<<findVtx);
307
308 if (!( nullptr==findVtx) ) {
309 vtx = findVtx;
310
312 updatedVtx = new xAOD::Vertex(*vtx);
313 } else {
314 tmpVtx = new xAOD::Vertex(*vtx);
315 updatedVtx = m_trackToVertexIPEstimatorTool->getUnbiasedVertex(track->perigeeParameters(), vtx );
316 }
317
318
319 if(updatedVtx){
320
321 if(!selectUpdatedVertices(updatedVtx)){
322 delete tmpVtx;
323 if ( tmpVtx!= updatedVtx) delete updatedVtx;
324 return vot;
325 }
326
328 ATH_MSG_DEBUG(" updated Vertex by KalmanVertexUpdator: "<<updatedVtx);
329
331 Amg::Vector3D globPos(updatedVtx->position()); //look
332 const PerigeeSurface surface(globPos);
333 const Perigee* perigee = nullptr;
334 std::unique_ptr<const Trk::TrackParameters> tmp =
335 m_extrapolator->extrapolateTrack(ctx, *track, surface);
336 //pass ownership only if of correct type
337 if (tmp && tmp->associatedSurface().type() == Trk::SurfaceType::Perigee) {
338 perigee = static_cast<const Perigee*> (tmp.release());
339 }
340 if (!perigee) {
341 const Perigee * trackPerigee = track->perigeeParameters();
342 if ( trackPerigee && trackPerigee->associatedSurface() == surface )
343 perigee = trackPerigee->clone();
344 }
345 //if the perigee is still nonsense ...
346 if (not perigee){
347 //clean up
348 if (updatedVtx!= tmpVtx) delete updatedVtx;
349 delete tmpVtx;
350 //WARNING
351 ATH_MSG_WARNING("Perigee is nullptr in "<<__FILE__<<":"<<__LINE__);
352 //exit
353 return vot;
354 }
355
356 // create the Jacobian matrix from Cartisian to Perigee
357 AmgMatrix(2,3) Jacobian;
358 Jacobian.setZero();
359 //perigee is dereferenced here, must not be nullptr!
360 double ptInv = 1./perigee->momentum().perp();
361 Jacobian(0,0) = -ptInv*perigee->momentum().y();
362 Jacobian(0,1) = ptInv*perigee->momentum().x();
363 Jacobian(1,2) = 1.0;
364
365 ATH_MSG_DEBUG(" Jacobian matrix from Cartesian to Perigee: "<< Jacobian);
366
367 AmgSymMatrix(3) vtxCov = updatedVtx->covariancePosition();
369
370 Amg::MatrixX errorMatrix;
372 AmgSymMatrix(3) tmpCov;
373 tmpCov.setZero();
374 tmpCov(0,0) = 1.e-10 ;
375 tmpCov(1,1) = 1.e-10;
376 tmpCov(2,2) = 1.e-10;
377 errorMatrix = Amg::MatrixX( tmpCov.similarity(Jacobian) );
378 } else {
379 errorMatrix = Amg::MatrixX( vtxCov.similarity(Jacobian) );
380 }
381 delete perigee;
382 perigee = nullptr;
383
384 // in fact, in most of the normal situation, pointer tmpVtx and updatedVtx are the same. You can check the source code.
385 if (tmpVtx != updatedVtx){
386 delete updatedVtx;
387 }
388 delete tmpVtx;
389 tmpVtx=nullptr;
390 updatedVtx=nullptr;
391
393
394 // VertexOnTrack Object
395 vot = new VertexOnTrack(std::move(localParams), std::move(errorMatrix), surface);
396 ATH_MSG_DEBUG("the VertexOnTrack created from vertex: "<<*vot);
397 }
398 }
399 //belt and braces approach
400 delete tmpVtx;
401 if ( tmpVtx!= updatedVtx) delete updatedVtx;
402 return vot;
403
404}
405
406
408
409 const EventContext& ctx = Gaudi::Hive::currentContext();
410 const VertexOnTrack * vot = nullptr;
412 Amg::Vector3D bpos = beamSpotHandle->beamPos();
413 ATH_MSG_DEBUG("beam spot: "<<bpos);
414 float beamSpotX = bpos.x();
415 float beamSpotY = bpos.y();
416 float beamSpotZ = bpos.z();
417 float beamTiltX = beamSpotHandle->beamTilt(0);
418 float beamTiltY = beamSpotHandle->beamTilt(1);
419 float beamSigmaX = m_BSScalingFactor * beamSpotHandle->beamSigma(0);
420 float beamSigmaY = m_BSScalingFactor * beamSpotHandle->beamSigma(1);
421
422 ATH_MSG_DEBUG("running refit with beam-spot");
423
424 float z0 = track->perigeeParameters()->parameters()[Trk::z0];
425 float beamX = beamSpotX + std::tan(beamTiltX) * (z0-beamSpotZ);
426 float beamY = beamSpotY + std::tan(beamTiltY) * (z0-beamSpotZ);
427 Amg::Vector3D BSC(beamX, beamY, z0);
428 ATH_MSG_DEBUG("constructing beam point (x,y,z) = ( "<<beamX<<" , "<<beamY<<" , "<<z0<<" )");
429 std::optional<PerigeeSurface> surface = std::nullopt;
430 Amg::MatrixX errorMatrix;
431 LocalParameters beamSpotParameters;
432
433 // covariance matrix of the beam-spot
434 AmgSymMatrix(2) beamSpotCov;
435 beamSpotCov.setZero();
436 beamSpotCov(0,0) = beamSigmaX * beamSigmaX;
437 beamSpotCov(1,1) = beamSigmaY * beamSigmaY;
438
439 if(m_constraintMode == 0u) {
440
441 const Amg::Vector3D& globPos(BSC);
442 surface.emplace(globPos);
443
444 // create a measurement for the beamspot
445 DefinedParameter Par0(0.,Trk::d0);
446 beamSpotParameters = LocalParameters(Par0);
447
448 // calculate perigee parameters wrt. beam-spot
449 const Perigee* perigee = nullptr;
450 std::unique_ptr<const Trk::TrackParameters> tmp =
451 m_extrapolator->extrapolateTrack(ctx, *track, *surface);
452 // pass ownership only if of correct type
453 if (tmp && tmp->associatedSurface().type() == Trk::SurfaceType::Perigee) {
454 perigee = static_cast<const Perigee*>(tmp.release());
455 }
456
457 if (!perigee) {
458 const Perigee * trackPerigee = track->perigeeParameters();
459 if ( trackPerigee && trackPerigee->associatedSurface() == *surface )
460 perigee = trackPerigee->clone();
461 }
462 if (not perigee){
463 ATH_MSG_WARNING("Perigee is nullptr in "<<__FILE__<<":"<<__LINE__);
464 return vot;
465 }
466
467 Eigen::Matrix<double,1,2> jacobian;
468 jacobian.setZero();
469 //perigee is dereferenced here, must not be nullptr
470 double ptInv = 1./perigee->momentum().perp();
471 jacobian(0,0) = -ptInv * perigee->momentum().y();
472 jacobian(0,1) = ptInv * perigee->momentum().x();
473
474 errorMatrix = Amg::MatrixX( jacobian*(beamSpotCov*jacobian.transpose()));
475 if( errorMatrix.cols() != 1 )
476 ATH_MSG_FATAL("Similarity transpose done incorrectly");
477 delete perigee;
478 }
479 if (surface){
480 vot = new VertexOnTrack(std::move(beamSpotParameters),
481 std::move(errorMatrix),
482 *surface);
483 } else {
484 ATH_MSG_WARNING("surface is nullptr in "<<__FILE__<<":"<<__LINE__);
485 }
486 if (vot){
487 ATH_MSG_DEBUG(" the VertexOnTrack objects created from BeamSpot are " << *vot);
488 }
489
490 return vot;
491}
492
493
495
497 Amg::Vector3D bpos = beamSpotHandle->beamPos();
498 ATH_MSG_DEBUG("beam spot: "<<bpos);
499 float beamSpotX = bpos.x();
500 float beamSpotY = bpos.y();
501 float beamSpotZ = bpos.z();
502 float beamTiltX = beamSpotHandle->beamTilt(0);
503 float beamTiltY = beamSpotHandle->beamTilt(1);
504 float beamSigmaX = m_BSScalingFactor * beamSpotHandle->beamSigma(0);
505 float beamSigmaY = m_BSScalingFactor * beamSpotHandle->beamSigma(1);
506 float beamSigmaZ = m_BSScalingFactor * beamSpotHandle->beamSigma(2);
507
508 float z0 = b->originalPosition()->z();
509 (*v)(0) = beamSpotX + std::tan(beamTiltX) * (z0-beamSpotZ);
510 (*v)(1) = beamSpotY + std::tan(beamTiltY) * (z0-beamSpotZ);
511 (*v)(2) = beamSpotZ;
512 (*q)(0,0) = beamSigmaX*beamSigmaX;
513 (*q)(1,1) = beamSigmaY*beamSigmaY;
514 (*q)(2,2) = beamSigmaZ*beamSigmaZ;
515
516 ATH_MSG_DEBUG("VTX constraint point (x,y,z) = ( "<< (*v)[0] <<" , "<< (*v)[1] <<" , "<< (*v)[2] <<" )");
517 ATH_MSG_DEBUG("VTX constraint size (x,y,z) = ( "<< beamSigmaX <<" , "<< beamSigmaY <<" , "<< beamSigmaZ <<" )");
518}
519
520const Track*
522 ToolHandle<Trk::IGlobalTrackFitter>& fitter,
523 const Track* track,
524 const VertexOnTrack* vot,
525 const ParticleHypothesis& particleHypothesis) const
526{
527 const EventContext& ctx = Gaudi::Hive::currentContext();
528 const Track* newTrack = nullptr;
529
530 if(vot){
531
532 std::vector<const MeasurementBase *> measurementCollection;
533 measurementCollection.push_back(vot);
534 // add all other measurements
535 const auto &measurements = *(track->measurementsOnTrack());
536 for(const MeasurementBase* meas : measurements)
537 measurementCollection.push_back(meas);
538
540 // get track parameters at the vertex:
541 const PerigeeSurface& surface=vot->associatedSurface();
542 ATH_MSG_DEBUG(" Track reference surface will be: " << surface);
543 const TrackParameters* parsATvertex=m_extrapolator->extrapolateTrack(ctx, *track, surface).release();
544
545 ATH_MSG_DEBUG(" Track will be refitted at this surface ");
546 newTrack = (fitter->fit(ctx, measurementCollection,
547 *parsATvertex, m_runOutlierRemoval, particleHypothesis)).release();
548 delete parsATvertex;
549 } else {
550 newTrack = (fitter->fit(ctx,
551 measurementCollection, *(track->trackParameters()->front()),
552 m_runOutlierRemoval, particleHypothesis)).release();
553 }
554 }
555
556 return newTrack;
557}
558
560
561 const xAOD::VertexContainer* vertices = nullptr;
562 const xAOD::Vertex* vertex = nullptr;
563 bool haveVertex = false;
564
565 // retrieve the primary vertex if needed
567
568 const EventContext& ctx = Gaudi::Hive::currentContext();
570 if(!vtxReadHandle.isValid()){
571 ATH_MSG_ERROR("Cannot retrieve the \'"<<m_PVContainerName<<"\' vertex collection from StoreGate");
573 } else {
574 vertices = vtxReadHandle.cptr();
575 // if there is no vertex, we can't associate the tracks to it
576 if(vertices) {
577 ATH_MSG_DEBUG("Primary vertex collection for this event has "<<vertices->size()<<" vertices");
578 if (vertices->size()<2){
579 ATH_MSG_DEBUG("Only Dummy vertex present, no Primary vertices.");
580 } else {
581 vertex = (*vertices)[0];
582 haveVertex = true;
583 }
584 }
585 else
586 ATH_MSG_DEBUG("Could not retrieve primary vertex collection from the StoreGate");
587 }
588 }
589
590
591 if( ( m_doAssociatedToPVSelection && haveVertex && vertex && isAssociatedToPV(track,vertices) ) ||
592 ( m_doBSTrackSelection && m_BSTrackSelector->accept(*track) ) ){
593
594 if (m_maxPt > 0 )
595 {
596 const Trk::Perigee* perigee = track->perigeeParameters();
597 if (!perigee) {
598 ATH_MSG_DEBUG("NO perigee on this track");
599 return false;
600 }
601 const double qoverP = perigee->parameters()[Trk::qOverP] * 1000.;
602 double pt = 0.;
603 if (qoverP != 0 )
604 pt = std::abs(1.0/qoverP)*sin(perigee->parameters()[Trk::theta]);
605 ATH_MSG_DEBUG( " pt : "<< pt );
606 if (pt > m_maxPt)
607 return false;
608 } //maxPt selection
609
610 ATH_MSG_DEBUG("this track passes the beamspot track selection, will do beamspot constraint on it ");
611 return true;
612 }
613 else return false;
614}
615
616
618
619 AlignTrack * alignTrack = nullptr;
620 const Track* newTrack = nullptr;
621 const VertexOnTrack* vot = nullptr;
622 const xAOD::Vertex* vtx = nullptr;
624 // configuration of the material effects needed for track fitter
626
627 const EventContext& ctx = Gaudi::Hive::currentContext();
628
629 // initialization the GX2 track fitter
630 ToolHandle<Trk::IGlobalTrackFitter> fitter = m_trackFitter;
632 fitter = m_SLTrackFitter;
633
635
636 ATH_MSG_DEBUG( "doTrackRefit ** START ** ");
637
639 vot = provideVotFromVertex(track, vtx);
640 if( !vot ) ATH_MSG_INFO( "VoT not found for this track! ");
641 if( !vtx ) ATH_MSG_INFO( "VTX pointer not found for this track! ");
642 if(vot){
643 newTrack = doConstraintRefit(fitter, track, vot, particleHypothesis);
645 // this track failed the PV constraint reift
646 if (!newTrack) {
648 ATH_MSG_DEBUG("VertexConstraint track refit failed! ");
649 }
650 }
651 }
652
654 vot = provideVotFromBeamspot(track);
655 if(vot){
656 newTrack = doConstraintRefit(fitter, track, vot, particleHypothesis);
658 // this track failed the BS constraint refit
659 if (!newTrack) {
661 ATH_MSG_DEBUG("BSConstraint track refit failed! ");
662 }
663 }
664 }
665
666
667 //Refit to get full fitter covariance matrix
668 // @TODO This is a little inefficienct and should
669 // be addressed when the alignment code is made MT safe
670 if(newTrack){
671 Trk::Track* tmpTrk = fitter->alignmentFit(ctx,alignCache,*newTrack,m_runOutlierRemoval,particleHypothesis);
672 delete newTrack;
673 newTrack = tmpTrk;
674 if(!tmpTrk){
676 {
678 ATH_MSG_DEBUG("VertexConstraint track refit2 failed! ");
680 {
682 ATH_MSG_DEBUG("BSConstraint track refit2 failed! ");
683 }
684 }
685 }
686
687 if(!newTrack && m_doNormalRefit){
688 newTrack = fitter->alignmentFit(ctx,alignCache,*track,m_runOutlierRemoval,particleHypothesis);
690 // this track failed the normal refit
691 if (!newTrack) {
693 ATH_MSG_DEBUG("Normal track refit failed! ");
694 }
695 }
696
697
698
699
700
701 if(newTrack) {
702 alignTrack = new AlignTrack(*newTrack);
703 // set original track pointer
704 alignTrack->setOriginalTrack(track);
705 // set the refit type
706 alignTrack->setType(type);
707
708
709 if (msgLvl(MSG::DEBUG) || msgLvl(MSG::VERBOSE)) {
710 ATH_MSG_DEBUG("before refit: "<< *track);
711 if (msgLvl(MSG::VERBOSE)) AlignTrack::dumpLessTrackInfo(*track,msg(MSG::DEBUG));
712
713 ATH_MSG_DEBUG("after refit: "<< *newTrack);
714 if (msgLvl(MSG::VERBOSE)) AlignTrack::dumpLessTrackInfo(*newTrack,msg(MSG::DEBUG));
715 }
716
718
719 if (m_storeFitMatrices) {
720 alignTrack->setFullCovarianceMatrix(alignCache.m_fullCovarianceMatrix.get());
721 alignTrack->setDerivativeMatrix(alignCache.m_derivMatrix.get());
722 }
723 delete newTrack;
724
726 // try to log the track-vertex association in the AlignVertex object:
727 bool ifound=false;
728 for (AlignVertex* ivtx : m_AlignVertices) {
729 if( (ivtx->originalVertex())==vtx ) {
730 ifound = true;
731 }
732 }
733 if( !ifound ) {
734 AlignVertex* avtx=new AlignVertex(vtx);
735 ATH_MSG_DEBUG(" New AlignVertex has ben created.");
736
737 // Beam Spot constraint on the vertex:
738 if( m_doBeamspotConstraint && (xAOD::VxType::PriVtx == vtx->vertexType() || xAOD::VxType::PileUp == vtx->vertexType()) && vtx->vxTrackAtVertex().size()>4 ) { // a beam line verex
739 ATH_MSG_DEBUG(" The Beam Spot constraint will be added to the vertex.." );
740 AmgSymMatrix(3) qtemp;
741 AmgVector(3) vtemp;
742 provideVtxBeamspot(avtx, &qtemp, &vtemp);
743 (qtemp)(2,2) = 1000000.0; // disable Z constraint
744 avtx->setConstraint( &qtemp, &vtemp);
745 }
746
747 m_AlignVertices.push_back(avtx);
748 }
749 }
750 // increment counters
752 ++m_nTracks;
753
754 }
755 // garbage collection:
756 if(vot) delete vot;
757
758 ATH_MSG_DEBUG( "doTrackRefit ** COMPLETED ** ");
759 return alignTrack;
760}
761
762
763
764
765//____________________________________________________________________________
767{
768 ATH_MSG_DEBUG("BeamspotVertexPreProcessor::processTrackCollection()");
769
770 if( !tracks || (tracks->empty()) )
771 return nullptr;
772
773 // Clear the AlignVertex container (will destruct the objects it owns as well!)
774 m_AlignVertices.clear();
775
778
779 // the output collection of AlignTracks
780 // we define it as collection of Tracks but fill AlignTracks inside
781 DataVector<Track> * newTrks = new DataVector<Track>;
782
783 int index(0);
784 // loop over tracks
785 ATH_MSG_DEBUG( "Starting loop on input track collection: "<<index);
786 for (const auto* track : *tracks){
787 ++index;
788 ATH_MSG_DEBUG("Processing track "<<index);
789 AlignTrack * alignTrack = nullptr;
790 if (not track) continue;
791
792 // check whether the track passes the basic selection
793 if (m_doTrkSelection) {
794 ATH_MSG_DEBUG( "Testing track selection on track: "<<index);
795 if ((not m_trkSelector.empty()) and (not m_trkSelector->accept(*track))) continue;
796 } // appliying track selection
797
798 if(m_refitTracks){
799 ATH_MSG_DEBUG( "Refitting track: "<<index );
800 alignTrack = doTrackRefit(track);
801
802 // 2nd track check after refit
803 if(alignTrack && !m_trkSelector.empty()) {
804 // refitted track loses the summary information, restoring it here
805 alignTrack->setTrackSummary( std::make_unique<Trk::TrackSummary> (*track->trackSummary()) );
806 // do not check for FullVertex tracks:
807 if( !(alignTrack->getVtx()) ) {
808 if( m_doTrkSelection && !m_trkSelector->accept(*alignTrack))
809 continue;
810 }
811 }
812 else {
813 ATH_MSG_DEBUG( "Refit of track " << index << " ended with no alignTrack" );
814 }
815 } else {
816 ATH_MSG_DEBUG( "No Track refit for track " << index << " --> building new aligntrack");
817 alignTrack = new AlignTrack(*track);
818 alignTrack->setOriginalTrack(track);
819 alignTrack->setType(AlignTrack::Original);
820 }
821 // add the new align track to the collection
822 if (alignTrack) newTrks->push_back(alignTrack);
823 } // end of loop over tracks
824
825 ATH_MSG_INFO( "Processing of input track collection completed (size: " << tracks->size() << "). Size of the alignTrack collection: " << newTrks->size() );
826 // delete the collection if it's empty
827 if (newTrks->empty()) {
828 delete newTrks;
829 return nullptr;
830 }
831
832 return newTrks;
833}
834
835//____________________________________________________________________________
837
838 if( !m_doFullVertexConstraint ) return;
839
840 AlignVertex* alignVertex = alignTrack->getVtx();
841
842 ATH_MSG_DEBUG( " In accumulateVTX ");
843 if( !alignVertex ) {
844 ATH_MSG_DEBUG( "This alignTrack is not associated to any vertex -> return. ");
845 return;
846 }
847
848 // get pointers so we can reuse them if they're valid
849 const Amg::MatrixX * ptrWeights = alignTrack->weightMatrix();
850 const Amg::MatrixX * ptrWeightsFD = alignTrack->weightMatrixFirstDeriv();
851 const Amg::VectorX * ptrResiduals = alignTrack->residualVector();
852 const std::vector<AlignModuleDerivatives> * ptrDerivs = alignTrack->derivatives();
853
854 // check if pointers are valid
855 if (!ptrWeights || !ptrWeightsFD || !ptrResiduals || !ptrDerivs) {
856 ATH_MSG_ERROR("something missing from alignTrack!");
857 if (!ptrWeights) ATH_MSG_ERROR("no weights!");
858 if (!ptrWeightsFD) ATH_MSG_ERROR("no weights for first deriv!");
859 if (!ptrResiduals) ATH_MSG_ERROR("no residuals!");
860 if (!ptrDerivs) ATH_MSG_ERROR("no derivatives!");
861 return;
862 }
863
864 // get vectors
865 const Amg::VectorX& residuals = *ptrResiduals;
866 std::vector<AlignModuleDerivatives> derivatives = *ptrDerivs;
867
868 // get weight matrices
869 const Amg::MatrixX& weights = *ptrWeights;
870 const Amg::MatrixX& weightsFirstDeriv = *ptrWeightsFD;
871 ATH_MSG_VERBOSE("weights="<<weights);
872 ATH_MSG_VERBOSE("weightsFirstDeriv="<<weightsFirstDeriv);
873
874 // get all alignPars and all derivatives
875 ATH_MSG_DEBUG("accumulateVTX: The derivative vector size is " << derivatives.size() );
876
877 std::vector<const Amg::VectorX*> allDerivatives[3];
878 Amg::VectorX VTXDerivatives[3];
879 const int WSize(weights.cols());
880 Amg::MatrixX WF(3,WSize);
881 std::vector<AlignModuleVertexDerivatives> derivX;
882
883 for (const auto& deriv : derivatives) {
884 // get AlignModule
885 const AlignModule* module=deriv.first;
886
887 // get alignment parameters
888 if( module ) {
889 Amg::MatrixX F(3,WSize);
890 const std::vector<Amg::VectorX>& deriv_vec = deriv.second;
891 ATH_MSG_VERBOSE( "accumulateVTX: The deriv_vec size is " << deriv_vec.size() );
892 DataVector<AlignPar>* alignPars = m_alignModuleTool->getAlignPars(module);
893 int nModPars = alignPars->size();
894 if ((nModPars+3) != std::ssize(deriv_vec)) {
895 ATH_MSG_ERROR("accumulateVTX: Derivatives w.r.t. the vertex seem to be missing");
896 return;
897 }
898 for (int i=0;i<3;i++) {
899 allDerivatives[i].push_back(&deriv_vec[nModPars+i]);
900 for (int j=0;j<WSize;j++) {
901 F(i,j) = deriv_vec[nModPars+i][j];
902 }
903 }
904
905 // prepare the X object in the AlignVertex:
906 WF += F * weights;
907
908 } else {
909 ATH_MSG_ERROR("accumulateVTX: Derivatives do not have a valid pointer to the module.");
910 return;
911 }
912 }
913
914
915 // second loop to fill the X object:
916 for (const auto& deriv : derivatives) {
917 // get AlignModule
918 const AlignModule* module=deriv.first;
919
920 // get alignment parameters
921 if( module ) {
922 const std::vector<Amg::VectorX>& deriv_vec = deriv.second;
923 std::vector<Amg::VectorX> drdaWF;
924 ATH_MSG_DEBUG("accumulateVTX: The deriv_vec size is "
925 << deriv_vec.size());
926 DataVector<AlignPar>* alignPars = m_alignModuleTool->getAlignPars(module);
927 int nModPars = alignPars->size();
928 if ((nModPars + 3) != std::ssize(deriv_vec)) {
930 "accumulateVTX: Derivatives w.r.t. the vertex seem to be missing");
931 return;
932 }
933 drdaWF.reserve(nModPars);
934 for (int i = 0; i < nModPars; i++) {
935 drdaWF.emplace_back(2.0 * WF * deriv_vec[i]);
936 }
937 ATH_MSG_DEBUG("accumulateVTX: derivX incremented by: " << drdaWF);
938 // now add contribution from this track to the X object:
939 derivX.emplace_back(module,std::move(drdaWF));
940
941 } else {
942 ATH_MSG_ERROR("accumulateVTX: Derivatives do not have a valid pointer to the module.");
943 return;
944 }
945 }
946
947 // prepare derivatives w.r.t. the vertex position:
948 int nmodules = allDerivatives[0].size();
949 ATH_MSG_DEBUG("accumulateVTX: allDerivatives size is " << nmodules);
950 for( int ii=0; ii<3; ++ii ) {
951 VTXDerivatives[ii] = (*(allDerivatives[ii])[0]);
952 for( int jj=1; jj<nmodules; ++jj ) {
953 VTXDerivatives[ii] += (*(allDerivatives[ii])[jj]);
954 }
955 }
956
957 AmgVector(3) vtxV;
958 AmgSymMatrix(3) vtxM;
959
960 Amg::VectorX RHM= weightsFirstDeriv * residuals;
961 ATH_MSG_DEBUG("RHM: "<<RHM);
962
963 for (int ipar=0;ipar<3;ipar++) {
964
965 // calculate first derivative
966 Amg::MatrixX derivativesT = (VTXDerivatives[ipar]).transpose();
967 ATH_MSG_DEBUG("derivativesT (size "<<derivativesT.cols()<<"): "<<derivativesT);
968
969 Amg::MatrixX tempV = (2.* derivativesT * RHM);
970 vtxV[ipar] = tempV(0,0);
971
972 for (int jpar=ipar;jpar<3;jpar++) {
973
974 // calculate second derivatives
975 Amg::MatrixX RHM2 = weights * (VTXDerivatives[jpar]);
976
977 Amg::MatrixX tempM = (2.* derivativesT * RHM2);
978 vtxM(ipar,jpar) = tempM(0,0);
979
980 }
981
982 }
983
984 // increment the vtx algebra objects:
985
986 alignVertex->incrementVector(vtxV);
987 alignVertex->incrementMatrix(vtxM);
988 // ATH_MSG_DEBUG("accumulateVTX: derivX size = "<< derivX->size());
989 alignVertex->addDerivatives(&derivX);
990
991}
992
993
994//____________________________________________________________________________
996
998 ATH_MSG_DEBUG("In solveVTX. Number of vertices = " << m_AlignVertices.size() );
999 for (AlignVertex* ivtx : m_AlignVertices) {
1000 if( ivtx->Ntracks()>1 ) {
1001 ivtx->fitVertex();
1002 } else {
1003 ATH_MSG_WARNING("This vertex contains " << ivtx->Ntracks() << " tracks. No solution possible.");
1004 }
1005
1006 ATH_MSG_DEBUG( "This vertex contains " << ivtx->Ntracks() << " tracks.");
1007 if( msgLvl(MSG::DEBUG) ) ivtx->dump(msg(MSG::DEBUG));
1008 }
1009 }
1010}
1011
1012//____________________________________________________________________________
1014{
1015 if(m_logStream) {
1016
1017 *m_logStream<<"*************************************************************"<<std::endl;
1018 *m_logStream<<"****** BeamspotVertexPreProcessor summary ******"<<std::endl;
1019 *m_logStream<<"*"<<std::endl;
1020 *m_logStream<<"* number of created AlignTracks : "<<m_nTracks<<std::endl;
1021 if(m_nTracks>0) {
1022 *m_logStream<<"* --------------------------------------------"<<std::endl;
1023 for(int i=0; i<AlignTrack::NTrackTypes; ++i) {
1024 if(m_trackTypeCounter[i]>0)
1025 *m_logStream<<"* "<<(AlignTrack::AlignTrackType)i<<": "<<m_trackTypeCounter[i]<<std::endl;
1026 }
1027 }
1028 *m_logStream<<"*"<<std::endl;
1029 *m_logStream<<"* number of failed normal refits : " << m_nFailedNormalRefits << std::endl;
1030 *m_logStream<<"* number of failed refits with primary vertex : " << m_nFailedPVRefits << std::endl;
1031 *m_logStream<<"* number of failed refits with beam-spot : " << m_nFailedBSRefits << std::endl;
1032 *m_logStream<<"*"<<std::endl;
1033 }
1034}
1035
1036//____________________________________________________________________________
1038{
1039 ATH_MSG_INFO("BeamspotVertexPreProcessor::finalize()");
1040
1041 return StatusCode::SUCCESS;
1042}
1043
1044//____________________________________________________________________________
1045}
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_DEBUG(x,...)
#define ATH_MSG_ERROR(x,...)
#define ATH_MSG_WARNING(x,...)
#define ATH_MSG_VERBOSE(x,...)
#define ATH_MSG_INFO(x,...)
#define ATH_MSG_FATAL(x,...)
An STL vector of pointers that by default owns its pointed-to elements.
#define AmgSymMatrix(dim)
#define AmgVector(rows)
#define AmgMatrix(rows, cols)
#define F(x, y, z)
Definition MD5.cxx:112
void diff(const Jet &rJet1, const Jet &rJet2, std::map< std::string, double > varDiff)
Difference between jets - Non-Class function required by trigger.
Definition Jet.cxx:631
AthAlgTool(const std::string &type, const std::string &name, const IInterface *parent)
Constructor with parameters:
bool msgLvl(const MSG::Level lvl) const
MsgStream & msg() const
Derived DataVector<T>.
Definition DataVector.h:795
value_type push_back(value_type pElem)
Add an element to the end of the collection.
size_type size() const noexcept
Returns the number of elements in the collection.
bool empty() const noexcept
Returns true if the collection is empty.
bool fit(const LArSamples::AbsShape &data, const AbsShape &reference, double &k, double &deltaT, double &chi2, const ScaledErrorData *sed=0) const
virtual bool isValid() override final
Can the handle be successfully dereferenced?
const_pointer_type cptr()
Dereference the pointer.
const std::vector< AlignModuleDerivatives > * derivatives() const
The Amg::VectorX is a vector of first-derivatives of the alignTSOS on the alignTrack w....
Definition AlignTrack.h:133
void setOriginalTrack(const Track *track)
set pointer to original track
Definition AlignTrack.h:93
@ NTrackTypes
total number of track types
Definition AlignTrack.h:53
@ NormalRefitted
normally refitted, without adding any pseudo-measurement
Definition AlignTrack.h:48
@ Original
not refitted, just copy constructed from original Track
Definition AlignTrack.h:47
@ Unknown
default type
Definition AlignTrack.h:46
@ BeamspotConstrained
refitted with beamspot constraint
Definition AlignTrack.h:49
@ VertexConstrained
refitted with vertex constraint
Definition AlignTrack.h:50
const Amg::VectorX * residualVector() const
Vector of residuals of the alignTSOS on the alignTrack, to be set by AlignTrackDresser.
Definition AlignTrack.h:150
static void dumpLessTrackInfo(const Track &track, MsgStream &msg)
dump less track information
void doFindPerigee() const
For the AlignTrack, if there is pseudo-measurement in the TSOS collection, the perigee will not alway...
Definition AlignTrack.h:104
void setType(AlignTrackType type)
Definition AlignTrack.h:97
const Amg::SymMatrixX * weightMatrixFirstDeriv() const
First deriv weight matrix can be either W from Si alignment (see Eqn.
Definition AlignTrack.h:161
void setDerivativeMatrix(const Amg::MatrixX *matrix)
Definition AlignTrack.h:269
const AlignVertex * getVtx() const
set and get pointer to the associated vertex
Definition AlignTrack.h:188
const Amg::SymMatrixX * weightMatrix() const
Weight matrix is W from Si alignment (see Eqn.
Definition AlignTrack.h:156
void setFullCovarianceMatrix(const Amg::SymMatrixX *matrix)
Definition AlignTrack.h:274
bool isSLTrack() const
method to determine whether a straight line track or not
void addDerivatives(std::vector< AlignModuleVertexDerivatives > *vec)
void incrementMatrix(const AmgSymMatrix(3) vtxM)
Definition AlignVertex.h:91
void setConstraint(AmgSymMatrix(3) *, Amg::Vector3D *)
set and get the constraint on VTX position
void incrementVector(const Amg::Vector3D &vtxV)
increment algebra objects for this verterx:
Definition AlignVertex.h:90
bool doBeamspotConstraintTrackSelection(const Track *track)
PublicToolHandle< IAlignModuleTool > m_alignModuleTool
Pointer to AlignModuleTool.
SG::ReadHandleKey< xAOD::VertexContainer > m_PVContainerName
void accumulateVTX(AlignTrack *alignTrack) override
methods added for the full VTX fit:
const VertexOnTrack * provideVotFromVertex(const Track *track, const xAOD::Vertex *&vtx) const
bool selectVertices(const xAOD::Vertex *vtx) const
bool isAssociatedToVertex(const Track *track, const xAOD::Vertex *vertex)
ToolHandle< InDet::IInDetTrackSelectionTool > m_trkSelector
bool isAssociatedToPV(const Track *track, const xAOD::VertexContainer *vertices)
ToolHandle< IGlobalTrackFitter > m_trackFitter
const xAOD::Vertex * findVertexCandidate(const Track *track) const
AlignTrack * doTrackRefit(const Track *track)
virtual DataVector< Track > * processTrackCollection(const DataVector< Track > *trks) override
Main processing of track collection.
void provideVtxBeamspot(const AlignVertex *b, AmgSymMatrix(3) *q, Amg::Vector3D *v) const
ToolHandle< IExtrapolator > m_extrapolator
DataVector< AlignVertex > m_AlignVertices
collection of AlignVertices used in FullVertex constraint option
std::vector< std::pair< const xAOD::Vertex *, std::vector< VxTrackAtVertex > > > m_allTracksVector
const Track * doConstraintRefit(ToolHandle< IGlobalTrackFitter > &fitter, const Track *track, const VertexOnTrack *vot, const ParticleHypothesis &particleHypothesis) const
ToolHandle< IGlobalTrackFitter > m_SLTrackFitter
virtual StatusCode initialize() override
SG::ReadCondHandleKey< InDet::BeamSpotData > m_beamSpotKey
ToolHandle< InDet::IInDetTrackSelectionTool > m_BSTrackSelector
ToolHandle< ITrackToVertexIPEstimator > m_trackToVertexIPEstimatorTool
virtual void printSummary() override
Print processing summary to logfile.
bool selectUpdatedVertices(const xAOD::Vertex *updatedVtx) const
BeamspotVertexPreProcessor(const std::string &type, const std::string &name, const IInterface *parent)
const VertexOnTrack * provideVotFromBeamspot(const Track *track) const
bool operator()(VxTrackAtVertex vtxTrk)
std::ostream * m_logStream
logfile output stream
This class is the pure abstract base class for all fittable tracking measurements.
const Amg::Vector3D & momentum() const
Access method for the momentum.
virtual ParametersT< DIM, T, S > * clone() const override final
Virtual clone.
virtual const S & associatedSurface() const override final
Access to the Surface method.
Class describing the Line to which the Perigee refers to.
const Track * originalTrack() const
Return pointer to associated track.
void setTrackSummary(std::unique_ptr< Trk::TrackSummary > input)
Set the track summary.
const Perigee * perigeeParameters() const
return Perigee.
Class to handle Vertex On Tracks, it inherits from the common MeasurementBase.
virtual const PerigeeSurface & associatedSurface() const override final
returns the surface for the local to global transformation
The VxTrackAtVertex is a common class for all present TrkVertexFitters The VxTrackAtVertex is designe...
const ITrackLink * trackOrParticleLink(void) const
float numberDoF() const
Returns the number of degrees of freedom of the vertex fit as float.
VxType::VertexType vertexType() const
The type of the vertex.
std::vector< Trk::VxTrackAtVertex > & vxTrackAtVertex()
Non-const access to the VxTrackAtVertex vector.
const Amg::Vector3D & position() const
Returns the 3-pos.
Eigen::Matrix< double, Eigen::Dynamic, Eigen::Dynamic > MatrixX
Dynamic Matrix - dynamic allocation.
Eigen::Matrix< double, 2, 1 > Vector2D
Eigen::Matrix< double, 3, 1 > Vector3D
Eigen::Matrix< double, Eigen::Dynamic, 1 > VectorX
Dynamic Vector - dynamic allocation.
constexpr ParticleHypothesis particle[PARTICLEHYPOTHESES]
the array of masses
Ensure that the ATLAS eigen extensions are properly loaded.
ParametersT< TrackParametersDim, Charged, PerigeeSurface > Perigee
@ v
Definition ParamDefs.h:78
@ u
Enums for curvilinear frames.
Definition ParamDefs.h:77
@ theta
Definition ParamDefs.h:66
@ qOverP
perigee
Definition ParamDefs.h:67
@ phi
Definition ParamDefs.h:75
@ d0
Definition ParamDefs.h:63
@ z0
Definition ParamDefs.h:64
std::pair< double, ParamDefs > DefinedParameter
Typedef to of a std::pair<double, ParamDefs> to identify a passed-through double as a specific type o...
ParticleHypothesis
Enumeration for Particle hypothesis respecting the interaction with material.
ParametersBase< TrackParametersDim, Charged > TrackParameters
Definition index.py:1
@ PileUp
Pile-up vertex.
@ PriVtx
Primary vertex.
VertexContainer_v1 VertexContainer
Definition of the current "Vertex container version".
Vertex_v1 Vertex
Define the latest version of the vertex class.
std::unique_ptr< Amg::MatrixX > m_fullCovarianceMatrix
access to the global fitter's full covariance matrix.
std::unique_ptr< Amg::MatrixX > m_derivMatrix
access to the matrix of derivatives used during the latest global-chi2 track fit.