ATLAS Offline Software
Loading...
Searching...
No Matches
GlobalChi2Fitter.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3*/
6
8
18
19#include "TrkGeometry/Layer.h"
24
25#include "TrkVolumes/Volume.h"
27
29
33
37#include "TrkTrack/Track.h"
40
47
49
52
55
56#include "CLHEP/Units/SystemOfUnits.h"
57
60
64
66#include "LayerSort.h"
68#include "cmath"
69#include <Eigen/Dense>
70#include <Eigen/StdVector>
71
72#include <exception>
73#include <memory>
74
75using CLHEP::MeV;
76using CLHEP::mm;
77
78namespace {
79 double getDistance(const Trk::DistanceSolution& distsol) {
80 if (distsol.numberOfSolutions() == 1) {
81 return distsol.first();
82 } if (distsol.numberOfSolutions() == 2) {
83 return (
84 std::abs(distsol.first()) < std::abs(distsol.second()) ?
85 distsol.first() :
86 distsol.second()
87 );
88 } else {
89 return 0;
90 }
91 }
92
93 bool correctAngles(double& phi, double& theta) {
94 if (theta > M_PI) {
95 theta = M_PI - theta;
96 phi += M_PI;
97 }
98 if (theta < 0) {
99 theta = -theta;
100 phi += M_PI;
101 }
102
103 phi = -std::remainder(-phi, 2 * M_PI);
104
105 return theta >= 0 && theta <= M_PI && phi >= -M_PI && phi <= M_PI;
106 }
107
108 // This function used to avoid FPE divide by zero or overflow by limiting the
109 // q/p values to a more limited range
110 double limitInversePValue(double qOverP) {
111 const double magnitude = std::abs(qOverP);
112 //limits found empirically to leave the 25-event q431 digest unchanged
113 constexpr double maxP{100.*10e6*MeV};
114 constexpr double minP{1.e-3*MeV};
115 constexpr double lo {1./maxP};
116 constexpr double hi {1./minP};
117 const double limited = std::clamp(magnitude, lo, hi);
118 return std::copysign(limited, qOverP);
119 }
120
121 std::pair<const Trk::TrackParameters *, const Trk::TrackParameters *> getFirstLastIdPar(const Trk::Track & track) {
122 const Trk::TrackParameters *firstidpar = nullptr;
123 const Trk::TrackParameters *lastidpar = nullptr;
124
125 DataVector<const Trk::TrackParameters>::const_iterator parit = track.trackParameters()->begin();
126
127 while ((firstidpar == nullptr) && parit != track.trackParameters()->end()) {
128 if (
129 ((**parit).covariance() != nullptr) &&
130 (**parit).associatedSurface().type() == Trk::SurfaceType::Perigee)
131 {
132 firstidpar = *parit;
133 }
134
135 ++parit;
136 }
137
138 parit = track.trackParameters()->end();
139 do {
140 --parit;
141 if (
142 ((**parit).covariance() != nullptr) &&
143 (**parit).associatedSurface().type() == Trk::SurfaceType::Perigee)
144 {
145 lastidpar = *parit;
146 }
147 } while ((lastidpar == nullptr) && parit != track.trackParameters()->begin());
148
149 return std::make_pair(firstidpar, lastidpar);
150 }
151
152 Trk::PropDirection invertPropdir(Trk::PropDirection i) {
153 if (i == Trk::alongMomentum) {
155 } else if (i == Trk::oppositeMomentum) {
156 return Trk::alongMomentum;
157 } else {
158 return Trk::anyDirection;
159 }
160 }
161
162 bool trackParametersClose(const Trk::TrackParameters & a, const Trk::TrackParameters & b, double e) {
163 return (
164 std::abs(a.parameters()[0] - b.parameters()[0]) < e &&
165 std::abs(a.parameters()[1] - b.parameters()[1]) < e &&
166 std::abs(a.parameters()[2] - b.parameters()[2]) < e
167 );
168 }
169
170// We compile this package with optimization, even in debug builds; otherwise,
171// the heavy use of Eigen makes it too slow. However, from here we may call
172// to out-of-line Eigen code that is linked from other DSOs; in that case,
173// it would not be optimized. Avoid this by forcing all Eigen code
174// to be inlined here if possible.
176 void
177 calculateJac(Eigen::Matrix<double, 5, 5> &jac,
178 Eigen::Matrix<double, 5, 5> &out,
179 int jmin, int jmax) {
180 out = (jac * out);
181
182 if (jmin > 0) {
183 out.block(0, 0, 4, jmin).setZero();
184 }
185
186 if (jmax < 4) {
187 out.block(0, jmax + 1, 4, 5 - (jmax + 1)).setZero();
188 }
189
190 out(4, 4) = jac(4, 4);
191 }
192
193 /*
194 * Analyse a 2x2 covariance matrix and find
195 * - the smaller eigenvalue
196 * - the stereo angle
197 * This assumes, that the covariance matrix is valid, i.e. symmetric.
198 * TODO: Find a source for the stereo angle calculation
199 */
200 std::pair<double, double> principalComponentAnalysis2x2(const Amg::MatrixX & mat) {
201 const double trace = mat(0, 0) + mat(1, 1);
202 const double diagonalProduct = mat(0, 0) * mat(1, 1);
203 const double mat01Sq = mat(0, 1) * mat(0, 1);
204 const double discriminant = std::sqrt(trace * trace - 4. * (diagonalProduct - mat01Sq));
205
206 const double eigenValueSmall = 0.5 * (trace - discriminant);
207 const double stereoAngle = 0.5 * std::asin(2 * mat(0, 1) / (-discriminant));
208
209 return std::make_pair(eigenValueSmall, stereoAngle);
210 }
211} //end of anonymous namespace
212
213namespace Trk {
215 const std::string & t,
216 const std::string & n,
217 const IInterface * p
218 ):
219 base_class(t, n, p),
220 m_idVolume(nullptr, std::make_shared<Trk::CylinderVolumeBounds>(560, 2750))
221 {
222 }
223
225 ATH_CHECK(m_field_cache_key.initialize());
226
227 if (!m_ROTcreator.name().empty()) {
228 ATH_CHECK(m_ROTcreator.retrieve());
229 }
230
231 if (!m_broadROTcreator.name().empty()) {
232 ATH_CHECK(m_broadROTcreator.retrieve());
233 }
234
235 ATH_CHECK(m_updator.retrieve());
236 ATH_CHECK(m_extrapolator.retrieve());
237 ATH_CHECK(m_navigator.retrieve());
239 ATH_CHECK(m_propagator.retrieve());
240
241 if (!m_boundaryCheckTool.name().empty()) {
242 ATH_CHECK(m_boundaryCheckTool.retrieve());
243 } else if (m_holeSearch.value()) {
244 ATH_MSG_ERROR("Hole search requested but no boundary check tool provided.");
245 return StatusCode::FAILURE;
246 }
247
248 if (m_calomat) {
249 ATH_CHECK(m_calotool.retrieve());
250
251 if (!m_calotoolparam.empty()) {
252 ATH_CHECK(m_calotoolparam.retrieve());
253 }
254 } else{
255 m_calotool.disable();
256 m_calotoolparam.disable();
257 }
258
259 ATH_CHECK(m_scattool.retrieve());
260 ATH_CHECK(m_elosstool.retrieve());
261
262 if (!m_matupdator.name().empty()) {
263 ATH_CHECK(m_matupdator.retrieve());
264 }
265
266 // need an Atlas id-helper to identify sub-detectors, take the one from detStore
267 ATH_CHECK(detStore()->retrieve(m_DetID, "AtlasID"));
268
270 ATH_MSG_WARNING("FillDerivativeMatrix option selected, switching off acceleration!");
271 m_acceleration = false;
272 }
273
274 if (!m_trackingGeometryReadKey.key().empty()){
276 }
277 if (m_useCaloTG) {
279 ATH_MSG_INFO(m_caloMaterialProvider << " retrieved ");
280 }
281 else{
282 m_caloMaterialProvider.disable();
283 }
284
286
287 /*
288 * Doing a hole search only makes sense if we are also creating a track
289 * summary, because the track summary is the only way for us to export the
290 * hole search information out of the fitter. For this reason, we disable
291 * the hole search in the case that track summaries are disabled.
292 */
293 if (m_holeSearch.value() && !m_createSummary.value()) {
294 ATH_MSG_ERROR("Hole search requested but track summaries are disabled.");
295 return StatusCode::FAILURE;
296 }
297
298 ATH_MSG_INFO("fixed momentum: " << m_p);
299
300 return StatusCode::SUCCESS;
301 }
302
304
305 ATH_MSG_INFO(m_fit_status[S_FITS] << " attempted track fits");
306 if (m_fit_status[S_FITS] > 0) {
307 ATH_MSG_INFO(m_fit_status[S_SUCCESSFUL_FITS] << " successful track fits");
308 ATH_MSG_INFO(m_fit_status[S_MAT_INV_FAIL]
309 << " track fits failed because of a matrix inversion failure");
310 ATH_MSG_INFO(m_fit_status[S_NOT_ENOUGH_MEAS]
311 << " tracks were rejected by the outlier logic");
312 ATH_MSG_INFO(m_fit_status[S_PROPAGATION_FAIL]
313 << " track fits failed because of a propagation failure");
314 ATH_MSG_INFO(m_fit_status[S_INVALID_ANGLES]
315 << " track fits failed because of an invalid angle (theta/phi)");
316 ATH_MSG_INFO(m_fit_status[S_NOT_CONVERGENT]
317 << " track fits failed because the fit did not converge");
318 ATH_MSG_INFO(m_fit_status[S_HIGH_CHI2]
319 << " tracks did not pass the chi^2 cut");
320 ATH_MSG_INFO(m_fit_status[S_LOW_MOMENTUM]
321 << " tracks were killed by the energy loss update");
322 }
323
324 return StatusCode::SUCCESS;
325 }
326
327 // combined fit of two tracks
328 // --------------------------------
329 std::unique_ptr<Track>
331 const EventContext& ctx,
332 const Track& intrk1,
333 const Track& intrk2,
334 const RunOutlierRemoval,
335 const ParticleHypothesis) const
336 {
337 ATH_MSG_DEBUG("--> entering GlobalChi2Fitter::fit(Track,Track,)");
338
339 Cache cache(this);
341
342 GXFTrajectory trajectory;
343 if (!m_straightlineprop) {
344 trajectory.m_straightline = (
345 !cache.m_field_cache.solenoidOn() && !cache.m_field_cache.toroidOn()
346 );
347 }
348
349 trajectory.m_fieldprop = trajectory.m_straightline ? Trk::NoField : Trk::FullField;
350
351 const bool firstismuon = isMuonTrack(intrk1);
352 const Track *indettrack = firstismuon ? &intrk2 : &intrk1;
353 const Track *muontrack = firstismuon ? &intrk1 : &intrk2;
354 const bool muonisstraight = muontrack->info().trackProperties(TrackInfo::StraightTrack);
355 bool measphi = false;
356
357 for (const auto *i : *(muontrack->measurementsOnTrack())) {
358 const RIO_OnTrack *rot = nullptr;
359
361 const auto *const crot = static_cast<const CompetingRIOsOnTrack *>(i);
362 rot = &crot->rioOnTrack(0);
363 } else {
365 rot =static_cast<const RIO_OnTrack *>(i);
366 }
367 }
368 if ((rot != nullptr) && !m_DetID->is_mdt(rot->identify())) {
369 const Surface *surf = &rot->associatedSurface();
370 const Amg::Vector3D measdir = surf->transform().rotation().col(0);
371 const double dotprod1 = measdir.dot(Amg::Vector3D(0, 0, 1));
372 const double dotprod2 = measdir.dot(
373 Amg::Vector3D(surf->center().x(), surf->center().y(), 0) /
374 surf->center().perp());
375 if (std::abs(dotprod1) < .5 && std::abs(dotprod2) < .5) {
376 measphi = true;
377 break;
378 }
379 }
380 }
381
382 const IPropagator *prop = &*m_propagator;
383 auto [firstidpar, lastidpar] = getFirstLastIdPar(*indettrack);
384
385 if ((firstidpar == nullptr) || (lastidpar == nullptr)) {
386 return nullptr;
387 }
388
389 std::unique_ptr<const TrackParameters> parforcalo = unique_clone(firstismuon ? firstidpar : lastidpar);
390
391 if (!cache.m_field_cache.solenoidOn()) {
392 const AmgVector(5) & newpars = parforcalo->parameters();
393
394 parforcalo=parforcalo->associatedSurface().createUniqueTrackParameters(
395 newpars[0], newpars[1], newpars[2], newpars[3], 1 / 5000., std::nullopt
396 );
397 }
398
399 std::vector < MaterialEffectsOnTrack > calomeots;
400 if (!m_useCaloTG) {
401 if (!m_calotool.empty()) {
402 calomeots = m_calotool->extrapolationSurfacesAndEffects(
403 *m_navigator->highestVolume(ctx),
404 *prop,
405 *parforcalo,
406 parforcalo->associatedSurface(),
409 );
410 }
411 } else {
412 m_caloMaterialProvider->getCaloMEOT(*indettrack, *muontrack, calomeots);
413 }
414
415 if (firstismuon) {
416 std::reverse(calomeots.begin(), calomeots.end());
417 }
418
419 if (calomeots.empty()) {
420 ATH_MSG_WARNING("No calorimeter material collected, failing fit");
421 return nullptr;
422 }
423
424 std::unique_ptr<Track> track;
425
426 const bool tmp = m_calomat;
427 cache.m_calomat = false;
428 const bool tmp2 = cache.m_extmat;
429 const bool tmp4 = cache.m_idmat;
430
431 const TrackParameters *measperid = indettrack->perigeeParameters();
432 const TrackParameters *measpermuon = muontrack->perigeeParameters();
433
434 const double qoverpid = measperid != nullptr ? measperid->parameters()[Trk::qOverP] : 0;
435 const double qoverpmuon = measpermuon != nullptr ? measpermuon->parameters()[Trk::qOverP] : 0;
436
437 const AmgSymMatrix(5) * errmatid = measperid != nullptr ? measperid->covariance() : nullptr;
438 const AmgSymMatrix(5) * errmatmuon = measpermuon != nullptr ? measpermuon->covariance() : nullptr;
439
440 if (
441 !firstismuon &&
442 (errmatid != nullptr) &&
443 (errmatmuon != nullptr) &&
444 qoverpmuon != 0 &&
445 !m_calotoolparam.empty() &&
447 ) {
448 const double piderror = std::sqrt((*errmatid) (4, 4)) / (qoverpid * qoverpid);
449 const double pmuonerror = std::sqrt((*errmatmuon) (4, 4)) / (qoverpmuon * qoverpmuon);
450 const double energyerror = std::sqrt(
451 calomeots[1].energyLoss()->sigmaDeltaE() *
452 calomeots[1].energyLoss()->sigmaDeltaE() + piderror * piderror +
453 pmuonerror * pmuonerror
454 );
455
456 if (
457 (std::abs(calomeots[1].energyLoss()->deltaE()) -
458 std::abs(1 / qoverpid) + std::abs(1 / qoverpmuon)
459 ) / energyerror > 5
460 ) {
461 ATH_MSG_DEBUG("Changing from measured to parametrized energy loss");
462 calomeots = m_calotoolparam->extrapolationSurfacesAndEffects(
463 *m_navigator->highestVolume(ctx),
464 *prop,
465 *parforcalo,
466 parforcalo->associatedSurface(),
469 );
470
471 if (calomeots.empty()) {
472 ATH_MSG_WARNING("No calorimeter material collected, failing fit");
473 return nullptr;
474 }
475 }
476 }
477
478 const int nfits = cache.m_fit_status[S_FITS];
479 bool firstfitwasattempted = false;
480
481 const bool caloEntranceIsValid = ensureValidEntranceCalo(ctx, cache);
482 if (!caloEntranceIsValid) {
483 return nullptr;
484 }
485
486 if (
487 (!cache.m_field_cache.toroidOn() && !cache.m_field_cache.solenoidOn()) ||
488 (
489 cache.m_getmaterialfromtrack &&
490 !muonisstraight &&
491 measphi &&
492 muontrack->info().trackFitter() != Trk::TrackInfo::Unknown &&
493 qoverpid * qoverpmuon > 0
494 )
495 ) {
496 track.reset(mainCombinationStrategy(ctx,cache, intrk1, intrk2, trajectory, calomeots));
497
498 if (cache.m_fit_status[S_FITS] == (unsigned int) (nfits + 1)) {
499 firstfitwasattempted = true;
500 }
501 }
502
503 if (
504 (track == nullptr) &&
505 !firstfitwasattempted &&
506 (cache.m_field_cache.toroidOn() || cache.m_field_cache.solenoidOn())
507 ) {
508 // Reset the trajectory
509 GXFTrajectory trajectory2;
510 trajectory2.m_straightline = trajectory.m_straightline;
511 trajectory2.m_fieldprop = trajectory.m_fieldprop;
512 trajectory = std::move(trajectory2);
513 track.reset(backupCombinationStrategy(ctx,cache, intrk1, intrk2, trajectory, calomeots));
514 }
515
516 bool pseudoupdated = false;
517
518 if (track != nullptr) {
519 for (std::unique_ptr<GXFTrackState> & pseudostate : trajectory.trackStates()) {
520 if (pseudostate == nullptr) {
521 continue;
522 }
523
524 if (
525 pseudostate->measurementType() != TrackState::Pseudo ||
526 !pseudostate->getStateType(TrackStateOnSurface::Measurement)
527 ) {
528 continue;
529 }
530
531 if ((pseudostate == nullptr) || pseudostate->fitQuality().chiSquared() < 10) {
532 continue;
533 }
534
535 const TrackParameters *pseudopar = pseudostate->trackParameters();
536 const std::unique_ptr<const TrackParameters> updpar(m_updator->removeFromState(
537 *pseudopar,
538 pseudostate->measurement()->localParameters(),
539 pseudostate->measurement()->localCovariance()
540 ));
541
542 if (updpar == nullptr) {
543 continue;
544 }
545
546 Amg::MatrixX covMatrix(1, 1);
547 covMatrix(0, 0) = 100;
548
549 std::unique_ptr<const PseudoMeasurementOnTrack> newpseudo = std::make_unique<const PseudoMeasurementOnTrack>(
551 DefinedParameter(updpar->parameters()[Trk::locY], Trk::locY)
552 ),
553 std::move(covMatrix),
554 pseudopar->associatedSurface()
555 );
556
557 pseudostate->setMeasurement(std::move(newpseudo));
558 double errors[5];
559 errors[0] = errors[2] = errors[3] = errors[4] = -1;
560 errors[1] = 10;
561 pseudostate->setMeasurementErrors(errors);
562 pseudoupdated = true;
563 }
564
565 if (pseudoupdated) {
566 trajectory.setConverged(false);
567 cache.m_matfilled = true;
568
569 track.reset(myfit(
570 ctx,
571 cache,
572 trajectory,
573 *track->perigeeParameters(),
574 false,
575 (cache.m_field_cache.toroidOn() || cache.m_field_cache.solenoidOn()) ? muon : nonInteracting
576 ));
577
578 cache.m_matfilled = false;
579 }
580 }
581
582 cache.m_fit_status[S_FITS] = nfits + 1;
583
584 if (track != nullptr) {
585 track->info().addPatternReco(intrk1.info());
586 track->info().addPatternReco(intrk2.info());
587 cache.incrementFitStatus(S_SUCCESSFUL_FITS);
588 }
589
590 cache.m_calomat = tmp;
591 cache.m_extmat = tmp2;
592 cache.m_idmat = tmp4;
593 return track;
594 }
595
597 const EventContext& ctx,
598 Cache & cache,
599 const Track & intrk1,
600 const Track & intrk2,
601 GXFTrajectory & trajectory,
602 std::vector<MaterialEffectsOnTrack> & calomeots
603 ) const {
604 ATH_MSG_DEBUG("--> entering GlobalChi2Fitter::mainCombinationStrategy");
605
606 const double mass = Trk::ParticleMasses::mass[muon];
607
608 const bool firstismuon = isMuonTrack(intrk1);
609 const Track *indettrack = firstismuon ? &intrk2 : &intrk1;
610 const Track *muontrack = firstismuon ? &intrk1 : &intrk2;
611
612 auto [tmpfirstidpar, tmplastidpar] = getFirstLastIdPar(*indettrack);
613 std::unique_ptr<const TrackParameters> firstidpar = unique_clone(tmpfirstidpar);
614 std::unique_ptr<const TrackParameters> lastidpar = unique_clone(tmplastidpar);
615
616 if ((firstidpar == nullptr) || (lastidpar == nullptr)) {
617 return nullptr;
618 }
619
620 if (muontrack->trackStateOnSurfaces()->empty()) {
621 return nullptr;
622 }
623
625 firstismuon ?
626 muontrack->trackStateOnSurfaces()->end() - 1 :
627 muontrack->trackStateOnSurfaces()->begin();
628
629 const MeasurementBase *closestmuonmeas = nullptr;
630 std::unique_ptr<const TrackParameters> tp_closestmuon = nullptr;
631
632 while (closestmuonmeas == nullptr) {
633 closestmuonmeas = nullptr;
634 const TrackParameters *thispar = (**tsosit).trackParameters();
635
636 if ((**tsosit).measurementOnTrack() != nullptr) {
637 closestmuonmeas = (**tsosit).measurementOnTrack();
638
639 if (thispar != nullptr) {
640 const AmgVector(5) & parvec = thispar->parameters();
641 tp_closestmuon=thispar->associatedSurface().createUniqueTrackParameters(
642 parvec[0], parvec[1], parvec[2], parvec[3], parvec[4], std::nullopt
643 );
644 }
645 break;
646 }
647
648 if (firstismuon) {
649 --tsosit;
650 } else {
651 ++tsosit;
652 }
653 }
654
655 PropDirection propdir = firstismuon ? Trk::alongMomentum : oppositeMomentum;
656 std::unique_ptr<const TrackParameters> tmppar;
657
658 const bool msEntranceIsValid = ensureValidEntranceMuonSpectrometer(ctx, cache);
659 if ((tp_closestmuon != nullptr) && msEntranceIsValid) {
660 tmppar = m_extrapolator->extrapolateToVolume(
661 ctx, *tp_closestmuon, *cache.m_msEntrance, propdir, nonInteracting);
662 }
663
664 std::unique_ptr<const std::vector<const TrackStateOnSurface *>> matvec;
665
666 if (tmppar != nullptr) {
667 const Surface & associatedSurface = tmppar->associatedSurface();
668 std::unique_ptr<Surface> muonsurf = nullptr;
669
670 if (associatedSurface.type() == Trk::SurfaceType::Cylinder) {
671 if (associatedSurface.bounds().type() == Trk::SurfaceBounds::Cylinder) {
672 const CylinderBounds *cylbounds = static_cast <const CylinderBounds * >(&associatedSurface.bounds());
673 Amg::Transform3D const trans = Amg::Transform3D(associatedSurface.transform());
674 const double radius = cylbounds->r();
675 const double hlength = cylbounds->halflengthZ();
676 muonsurf = std::make_unique<CylinderSurface>(trans, radius + 1, hlength);
677 }
678 } else if (associatedSurface.type() == Trk::SurfaceType::Disc) {
679 if (associatedSurface.bounds().type() == Trk::SurfaceBounds::Disc) {
680 const double newz = (
681 associatedSurface.center().z() > 0 ?
682 associatedSurface.center().z() + 1 :
683 associatedSurface.center().z() - 1
684 );
685
686 const Amg::Vector3D newpos(
687 associatedSurface.center().x(),
688 associatedSurface.center().y(),
689 newz
690 );
691 Amg::Transform3D trans = associatedSurface.transform();
692 trans.translation() << newpos;
693
694 const DiscBounds *discbounds = static_cast<const DiscBounds *>(&associatedSurface.bounds());
695 const double rmin = discbounds->rMin();
696 const double rmax = discbounds->rMax();
697 muonsurf = std::make_unique<DiscSurface>(trans, rmin, rmax);
698 }
699 }
700
701 if (muonsurf != nullptr) {
702 matvec.reset(m_extrapolator->extrapolateM(
703 ctx,
704 *tp_closestmuon,
705 *muonsurf,
706 propdir,
707 false,
708 muon
709 ));
710 }
711 }
712
713 std::vector<const TrackStateOnSurface *> tmp_matvec;
714
715 if ((matvec != nullptr) && !matvec->empty()) {
716 tmp_matvec = *matvec;
717 delete tmp_matvec.back();
718 tmp_matvec.pop_back();
719
720 for (auto & i : tmp_matvec) {
721 propdir = firstismuon ? Trk::alongMomentum : oppositeMomentum;
722 if (i->materialEffectsOnTrack()->derivedType() != MaterialEffectsBase::MATERIAL_EFFECTS_ON_TRACK){
723 continue;
724 }
725 const MaterialEffectsOnTrack *meff = static_cast<const MaterialEffectsOnTrack *>(i->materialEffectsOnTrack());
726
727 const Surface *matsurf = &meff->associatedSurface();
728 tmppar = m_propagator->propagateParameters(
729 ctx,
730 *tp_closestmuon,
731 *matsurf,
732 propdir,
733 false,
734 trajectory.m_fieldprop,
736 );
737
738
739 if (tmppar == nullptr) {
740 propdir = !firstismuon ? Trk::alongMomentum : oppositeMomentum;
741 tmppar=m_propagator->propagateParameters(
742 ctx,
743 *tp_closestmuon,
744 *matsurf,
745 propdir,
746 false,
747 trajectory.m_fieldprop,
749 );
750
751 }
752
753 if (tmppar == nullptr) {
754 return nullptr;
755 }
756
757 AmgVector(5) newpars = tmppar->parameters();
758
759 if (newpars[Trk::qOverP] != 0) {
760 const double sign = (newpars[Trk::qOverP] > 0) ? 1 : -1;
761 const double de = std::abs(meff->energyLoss()->deltaE());
762 const double oldp = std::abs(1 / newpars[Trk::qOverP]);
763 const double newp2 = oldp * oldp + (!firstismuon ? 2 : -2) * de * std::sqrt(mass * mass + oldp * oldp) + de * de;
764
765 if (newp2 > 0) {
766 newpars[Trk::qOverP] = sign / std::sqrt(newp2);
767 }
768 }
769
770 tp_closestmuon=tmppar->associatedSurface().createUniqueTrackParameters(
771 newpars[0], newpars[1], newpars[2], newpars[3], newpars[4], std::nullopt
772 );
773 }
774
775 if (!firstismuon) {
776 std::reverse(tmp_matvec.begin(), tmp_matvec.end());
777 }
778 }
779
780 const Trk::TrackStates::const_iterator beginStates = intrk1.trackStateOnSurfaces()->begin();
781 Trk::TrackStates::const_iterator itStates = beginStates;
782 const Trk::TrackStates::const_iterator endState = firstismuon ? tsosit + 1 : intrk1.trackStateOnSurfaces()->end();
783 const Trk::TrackStates::const_iterator beginStates2 = !firstismuon ? tsosit : intrk2.trackStateOnSurfaces()->begin();
784 Trk::TrackStates::const_iterator itStates2 = beginStates2;
785 const Trk::TrackStates::const_iterator endState2 = intrk2.trackStateOnSurfaces()->end();
786
787 for (; itStates != endState; ++itStates) {
788 if (firstismuon && (*itStates)->measurementOnTrack()->type(Trk::MeasurementBaseType::PseudoMeasurementOnTrack)) {
789 continue;
790 }
791
792 const bool tmpgetmat = cache.m_getmaterialfromtrack;
793
794 if ((*itStates)->materialEffectsOnTrack() != nullptr) {
795 if (firstismuon) {
796 cache.m_extmat = false;
797 } else {
798 cache.m_idmat = false;
799 }
800
801 const auto *const pBaseMEOT = (*itStates)->materialEffectsOnTrack();
802 const bool itsAnMEOT = (pBaseMEOT->derivedType() == MaterialEffectsBase::MATERIAL_EFFECTS_ON_TRACK);
803
804 if (itsAnMEOT ){
805 const auto *const pMEOT =static_cast<const MaterialEffectsOnTrack *>((*itStates)->materialEffectsOnTrack());
806 if ((pMEOT->scatteringAngles() == nullptr) or (pMEOT->energyLoss() == nullptr)) {
807 cache.m_getmaterialfromtrack = true; // always take calorimeter layers
808 }
809 }
810 }
811
812 makeProtoState(cache, trajectory, *itStates);
813 cache.m_getmaterialfromtrack = tmpgetmat;
814 }
815
816 if (
817 !firstismuon &&
819 ) {
820 trajectory.trackStates().back()->setTrackParameters(nullptr);
821 }
822
823 std::unique_ptr<const TrackParameters> firstscatpar;
824 std::unique_ptr<const TrackParameters> lastscatpar;
825 const std::unique_ptr<const TrackParameters> origlastidpar = unique_clone(lastidpar);
826
827 double newqoverpid = 0;
828
829 if (tp_closestmuon and !firstismuon) {
830 const double de = std::abs(calomeots[1].energyLoss()->deltaE());
831 const double sigmade = std::abs(calomeots[1].energyLoss()->sigmaDeltaE());
832
833 const double pbefore = std::abs(1 / firstidpar->parameters()[Trk::qOverP]);
834 const double pafter = std::abs(1 / tp_closestmuon->parameters()[Trk::qOverP]);
835 const double elosspull = (pbefore - pafter - de) / sigmade;
836
837 if (std::abs(elosspull) > 10) {
838 if (elosspull > 10) {
839 newqoverpid = 1 / (de + pafter + 10 * sigmade);
840 } else {
841 newqoverpid = 1 / (de + pafter - 10 * sigmade);
842 }
843
844 if (tp_closestmuon->parameters()[Trk::qOverP] * newqoverpid < 0) {
845 newqoverpid *= -1;
846 }
847
848 const AmgVector(5) & newpar = firstidpar->parameters();
849 firstidpar=firstidpar->associatedSurface().createUniqueTrackParameters(
850 newpar[0], newpar[1], newpar[2], newpar[3], newqoverpid, std::nullopt
851 );
852 }
853
854 lastidpar = m_extrapolator->extrapolateToVolume(
855 ctx, *firstidpar, *cache.m_caloEntrance, alongMomentum, Trk::muon);
856 }
857
858 if (lastidpar == nullptr) {
859 lastidpar = unique_clone(origlastidpar.get());
860 }
861
862 firstscatpar= m_propagator->propagateParameters(
863 ctx,
864 *(firstismuon ? tp_closestmuon.get() : lastidpar.get()),
865 calomeots[0].associatedSurface(),
867 false,
868 trajectory.m_fieldprop,
870 );
871
872 if (firstscatpar == nullptr) {
873 return nullptr;
874 }
875
876 lastscatpar = m_propagator->propagateParameters(
877 ctx,
878 *(firstismuon ? firstidpar : tp_closestmuon),
879 calomeots[2].associatedSurface(),
881 false,
882 trajectory.m_fieldprop,
884 );
885
886 if (lastscatpar == nullptr) {
887 return nullptr;
888 }
889
890 std::optional<TransportJacobian> jac1;
891 std::optional<TransportJacobian> jac2;
892 std::unique_ptr<const TrackParameters> elosspar;
893
894 double firstscatphi = 0;
895 double secondscatphi = 0;
896 double firstscattheta = 0;
897 double secondscattheta = 0;
898 double muonscatphi = 0;
899 double muonscattheta = 0;
900
901 const TrackParameters *idscatpar = !firstismuon ? firstscatpar.get() : lastscatpar.get();
902 const TrackParameters *muonscatpar = firstismuon ? firstscatpar.get() : lastscatpar.get();
903
904 newqoverpid = idscatpar->parameters()[Trk::qOverP];
905
906 const Amg::Vector3D calosegment = lastscatpar->position() - firstscatpar->position();
907 muonscatphi = xAOD::P4Helpers::deltaPhi(calosegment.phi(), muonscatpar->parameters()[Trk::phi]);
908
909 muonscattheta = calosegment.theta() - muonscatpar->parameters()[Trk::theta];
910 std::unique_ptr<const TrackParameters> startPar = unique_clone(cache.m_idmat ? lastidpar.get() : indettrack->perigeeParameters());
911
912 for (int i = 0; i < 2; i++) {
913 std::unique_ptr<const TrackParameters> tmpelosspar;
914 AmgVector(5) params1 = muonscatpar->parameters();
915 params1[Trk::phi] += muonscatphi;
916 params1[Trk::theta] += muonscattheta;
917
918 if (!correctAngles(params1[Trk::phi], params1[Trk::theta])) {
919 return nullptr;
920 }
921
922 const std::unique_ptr<const TrackParameters> tmppar1(muonscatpar->associatedSurface().createUniqueTrackParameters(
923 params1[0], params1[1], params1[2], params1[3], params1[4], std::nullopt
924 ));
925
926 const PropDirection propdir = !firstismuon ? oppositeMomentum : alongMomentum;
927
928 tmpelosspar = m_propagator->propagateParameters(
929 ctx,
930 *tmppar1,
931 calomeots[1].
932 associatedSurface(),
933 propdir,
934 false,
935 trajectory.m_fieldprop,
936 jac1,
938 );
939
940 if (m_numderiv) {
942 ctx,
943 firstscatpar.get(),
944 calomeots[1].associatedSurface(),
945 propdir,
946 trajectory.m_fieldprop
947 );
948 }
949
950 if ((tmpelosspar == nullptr) || (jac1 == std::nullopt)) {
951 // @TODO
952 // according to coverity elosspar cannot be non NULL here
953 // because elosspar is initially NULL and only set in the last loop iteration (i==1)
954 // is this intended ?
955 // delete elosspar;
956 return nullptr;
957 }
958
959 const AmgVector(5) & newpars = tmpelosspar->parameters();
960 const std::unique_ptr<const TrackParameters> elosspar2(tmpelosspar->associatedSurface().createUniqueTrackParameters(
961 newpars[0], newpars[1], newpars[2], newpars[3], newqoverpid, std::nullopt
962 ));
963
964 if (i == 1) {
965 elosspar = std::move(tmpelosspar);
966 }
967
968 std::unique_ptr<const TrackParameters> scat2(m_propagator->propagateParameters(
969 ctx,
970 *elosspar2,
971 !firstismuon ?
972 calomeots[0].associatedSurface() :
973 calomeots[2].associatedSurface(),
974 propdir,
975 false,
976 trajectory.m_fieldprop,
977 jac2,
979 ));
980
981 if (m_numderiv) {
983 ctx,
984 elosspar2.get(),
985 !firstismuon ?
986 calomeots[0].associatedSurface() :
987 calomeots[2].associatedSurface(),
988 !firstismuon ?
991 trajectory.m_fieldprop
992 );
993 }
994
995 if ((scat2 == nullptr) || (jac2 == std::nullopt)) {
996 return nullptr;
997 }
998
999 double jac3[5][5];
1000 for (int j = 0; j < 5; j++) {
1001 for (int k = 0; k < 5; k++) {
1002 jac3[j][k] = 0;
1003 for (int l = 0; l < 5; l++) {
1004 jac3[j][k] += (*jac2) (j, l) * (*jac1) (l, k);
1005 }
1006 }
1007 }
1008
1009 jac1.reset();
1010 jac2.reset();
1011 Amg::MatrixX jac4(2, 2);
1012
1013 jac4(0, 0) = jac3[0][2];
1014 jac4(1, 1) = jac3[1][3];
1015 jac4(0, 1) = jac3[0][3];
1016 jac4(1, 0) = jac3[1][2];
1017
1018 jac4 = jac4.inverse();
1019
1020 double dloc1 = idscatpar->parameters()[Trk::loc1] - scat2->parameters()[Trk::loc1];
1021 double dloc2 = idscatpar->parameters()[Trk::loc2] - scat2->parameters()[Trk::loc2];
1022 const Trk::CylinderSurface * cylsurf = nullptr;
1023
1024 if (scat2->associatedSurface().type() == Trk::SurfaceType::Cylinder)
1025 cylsurf = static_cast<const Trk::CylinderSurface *>(&scat2->associatedSurface());
1026
1027 const Trk::DiscSurface * discsurf = nullptr;
1028
1029 if (scat2->associatedSurface().type() == Trk::SurfaceType::Cylinder)
1030 discsurf = static_cast<const Trk::DiscSurface *>(&scat2->associatedSurface());
1031
1032 if (cylsurf != nullptr) {
1033 dloc1 = -std::remainder(-dloc1, 2 * M_PI * cylsurf->bounds().r());
1034 }
1035
1036 if (discsurf != nullptr) {
1037 dloc2 = -std::remainder(-dloc2, 2 * M_PI);
1038 }
1039
1040 double dphi = jac4(0, 0) * dloc1 + jac4(0, 1) * dloc2;
1041 double dtheta = jac4(1, 0) * dloc1 + jac4(1, 1) * dloc2;
1042
1043 if (i == 1) {
1044 dphi = dtheta = 0;
1045 }
1046
1047 muonscatphi += dphi;
1048 muonscattheta += dtheta;
1049
1050 const double idscatphi = xAOD::P4Helpers::deltaPhi(idscatpar->parameters()[Trk::phi], scat2->parameters()[Trk::phi] + dphi);
1051 const double idscattheta = idscatpar->parameters()[Trk::theta] - (scat2->parameters()[Trk::theta] + dtheta);
1052
1053 if (firstismuon) {
1054 firstscatphi = muonscatphi;
1055 secondscatphi = idscatphi;
1056 firstscattheta = muonscattheta;
1057 secondscattheta = idscattheta;
1058 } else {
1059 firstscatphi = -idscatphi;
1060 secondscatphi = -muonscatphi;
1061 firstscattheta = -idscattheta;
1062 secondscattheta = -muonscattheta;
1063 }
1064
1065 if (i == 1 && cache.m_field_cache.toroidOn() && !firstismuon) {
1066 AmgVector(5) params2 = scat2->parameters();
1067 params2[Trk::phi] += idscatphi;
1068 params2[Trk::theta] += idscattheta;
1069
1070 if (!correctAngles(params2[Trk::phi], params2[Trk::theta])) {
1071 return nullptr;
1072 }
1073
1074 firstscatpar=scat2->associatedSurface().createUniqueTrackParameters(
1075 params2[0], params2[1], params2[2], params2[3], params2[4], std::nullopt
1076 );
1077 idscatpar = firstscatpar.get();
1078
1079 startPar = m_extrapolator->extrapolateToVolume(ctx,
1080 *idscatpar,
1081 *cache.m_caloEntrance,
1084
1085 if (startPar != nullptr) {
1086 const Amg::Vector3D trackdir = startPar->momentum().unit();
1087 const Amg::Vector3D curvZcrossT = -(trackdir.cross(Amg::Vector3D(0, 0, 1)));
1088 const Amg::Vector3D curvU = curvZcrossT.unit();
1089 const Amg::Vector3D curvV = trackdir.cross(curvU);
1090 Amg::RotationMatrix3D rot = Amg::RotationMatrix3D::Identity();
1091
1092 rot.col(0) = curvU;
1093 rot.col(1) = curvV;
1094 rot.col(2) = trackdir;
1095
1097 trans.linear().matrix() << rot;
1098 trans.translation() << startPar->position() - .1 * trackdir;
1099 PlaneSurface const curvlinsurf(trans);
1100
1101 const TrackParameters *curvlinpar = m_extrapolator->extrapolateDirectly(
1102 ctx,
1103 *startPar,
1104 curvlinsurf,
1107 ).release();
1108
1109 if (curvlinpar != nullptr) {
1110 startPar.reset(curvlinpar);
1111 }
1112 }
1113
1114 firstscatpar = std::move(scat2);
1115 }
1116 }
1117
1118 std::unique_ptr<GXFMaterialEffects> firstscatmeff = std::make_unique<GXFMaterialEffects>(calomeots[0]);
1119 std::unique_ptr<GXFMaterialEffects> elossmeff = std::make_unique<GXFMaterialEffects>(calomeots[1]);
1120 std::unique_ptr<GXFMaterialEffects> secondscatmeff = std::make_unique<GXFMaterialEffects>(calomeots[2]);
1121
1122 const double pull1 = std::abs(firstscatphi / firstscatmeff->sigmaDeltaPhi());
1123 const double pull2 = std::abs(secondscatphi / secondscatmeff->sigmaDeltaPhi());
1124
1125 if (firstismuon) {
1126 for (auto & i : tmp_matvec) {
1127 makeProtoState(cache, trajectory, i, -1);
1128 }
1129 }
1130
1131 firstscatmeff->setScatteringAngles(firstscatphi, firstscattheta);
1132 secondscatmeff->setScatteringAngles(secondscatphi, secondscattheta);
1133
1134 if (!firstismuon) {
1135 elossmeff->setdelta_p(1000 * (lastscatpar->parameters()[Trk::qOverP] - newqoverpid));
1136 } else {
1137 elossmeff->setdelta_p(1000 * (newqoverpid - firstscatpar->parameters()[Trk::qOverP]));
1138 }
1139
1140 elossmeff->setSigmaDeltaE(calomeots[1].energyLoss()->sigmaDeltaE());
1141
1142 trajectory.addMaterialState(std::make_unique<GXFTrackState>(std::move(firstscatmeff), std::move(firstscatpar)), -1);
1143 trajectory.addMaterialState(std::make_unique<GXFTrackState>(std::move(elossmeff), std::move(elosspar)), -1);
1144 trajectory.addMaterialState(std::make_unique<GXFTrackState>(std::move(secondscatmeff), std::move(lastscatpar)), -1);
1145
1146 if (!firstismuon) {
1147 for (auto & i : tmp_matvec) {
1148 makeProtoState(cache, trajectory, i, -1);
1149 }
1150 }
1151
1152 ATH_MSG_DEBUG("pull1: " << pull1 << " pull2: " << pull2);
1153
1154 if (startPar == nullptr) {
1155 return nullptr;
1156 }
1157
1158 if (
1159 (pull1 > 5 || pull2 > 5) &&
1160 (pull1 > 25 || pull2 > 25 || closestmuonmeas->associatedSurface().type() == Trk::SurfaceType::Line)
1161 ) {
1162 return nullptr;
1163 }
1164
1165 bool largegap = false;
1166 double previousz = 0;
1167
1168 for (itStates2 = beginStates2; itStates2 != endState2; ++itStates2) {
1169 const MaterialEffectsBase *meff = (*itStates2)->materialEffectsOnTrack();
1170 const TrackParameters *tpar = (*itStates2)->trackParameters();
1171 const MeasurementBase *meas = (*itStates2)->measurementOnTrack();
1172
1173 if (meff != nullptr) {
1174 if (!firstismuon) {
1175 const MaterialEffectsOnTrack *mefot{};
1177 mefot = static_cast<const MaterialEffectsOnTrack *>(meff);
1178 }
1179 if ( mefot and mefot->energyLoss() and
1180 std::abs(mefot->energyLoss()->deltaE()) > 250 &&
1181 mefot->energyLoss()->sigmaDeltaE() < 1.e-9
1182 ) {
1183 return nullptr;
1184 }
1185
1186 cache.m_extmat = false;
1187 } else {
1188 cache.m_idmat = false;
1189 }
1190 }
1192 if (
1193 ispseudo &&
1194 !(itStates2 == beginStates2 || itStates2 == beginStates2 + 1) &&
1195 !largegap
1196 ) {
1197 continue;
1198 }
1199
1200 makeProtoState(cache, trajectory, *itStates2);
1201
1202 if (
1203 itStates2 == endState2 - 1 &&
1205 tpar->position().perp() > 9000 &&
1206 std::abs(tpar->position().z()) < 13000
1207 ) {
1208 std::unique_ptr<const TrackParameters> pseudopar(tpar->clone());
1209 Amg::MatrixX covMatrix(1, 1);
1210 covMatrix(0, 0) = 100;
1211
1212 std::unique_ptr<const PseudoMeasurementOnTrack> newpseudo = std::make_unique<const PseudoMeasurementOnTrack>(
1213 LocalParameters(DefinedParameter(pseudopar->parameters()[Trk::locY], Trk::locY)),
1214 std::move(covMatrix),
1215 pseudopar->associatedSurface()
1216 );
1217
1218 std::unique_ptr<GXFTrackState> pseudostate = std::make_unique<GXFTrackState>(std::move(newpseudo), std::move(pseudopar));
1219 pseudostate->setMeasurementType(TrackState::Pseudo);
1220
1221 double errors[5];
1222 errors[0] = errors[2] = errors[3] = errors[4] = -1;
1223 errors[1] = 10;
1224
1225 pseudostate->setMeasurementErrors(errors);
1226 trajectory.addMeasurementState(std::move(pseudostate));
1227 ispseudo = true;
1228 ATH_MSG_DEBUG("Adding pseudomeasurement");
1229 }
1230
1231 if (
1232 std::abs(trajectory.trackStates().back()->position().z()) > 20000 &&
1233 std::abs(previousz) < 12000
1234 ) {
1235 largegap = true;
1236 }
1237
1238 if (trajectory.trackStates().back()->getStateType(TrackStateOnSurface::Measurement)) {
1239 previousz = trajectory.trackStates().back()->position().z();
1240 }
1241 }
1242
1243 Track *track = myfit(
1244 ctx,
1245 cache,
1246 trajectory,
1247 *startPar,
1248 false,
1249 (cache.m_field_cache.toroidOn() || cache.m_field_cache.solenoidOn()) ? muon : nonInteracting
1250 );
1251
1252 return track;
1253 }
1254
1255 Track *
1257 const EventContext& ctx,
1258 Cache & cache,
1259 const Track & intrk1,
1260 const Track & intrk2,
1261 GXFTrajectory & trajectory,
1262 std::vector<MaterialEffectsOnTrack> & calomeots
1263 ) const {
1264 ATH_MSG_DEBUG("--> entering GlobalChi2Fitter::backupCombinationStrategy");
1265
1266 const bool firstismuon = isMuonTrack(intrk1);
1267 const Track *indettrack = firstismuon ? &intrk2 : &intrk1;
1268
1269 const Trk::TrackStates::const_iterator beginStates = intrk1.trackStateOnSurfaces()->begin();
1270 Trk::TrackStates::const_iterator itStates = beginStates;
1271 const Trk::TrackStates::const_iterator endState = intrk1.trackStateOnSurfaces()->end();
1272 const Trk::TrackStates::const_iterator beginStates2 = intrk2.trackStateOnSurfaces()->begin();
1273 Trk::TrackStates::const_iterator itStates2 = beginStates2;
1274 const Trk::TrackStates::const_iterator endState2 = intrk2.trackStateOnSurfaces()->end();
1275
1276 const TrackParameters *firstidpar = nullptr;
1277 const auto *const pParametersVector = indettrack->trackParameters();
1278 // Dont understand why the second track parameters are taken
1279 // Is it assumed the ID track is slimmed?
1280 if (pParametersVector->size() > 1)
1281 firstidpar = (*pParametersVector)[1];
1282 else
1283 firstidpar = pParametersVector->back();
1284
1285 std::unique_ptr<const TrackParameters> lastidpar = nullptr;
1286 if ((firstidpar != nullptr) && (cache.m_caloEntrance != nullptr))
1287 lastidpar = m_extrapolator->extrapolateToVolume(
1288 ctx, *firstidpar, *cache.m_caloEntrance, alongMomentum, Trk::muon);
1289
1290 if (lastidpar == nullptr) {
1291 lastidpar.reset(pParametersVector->back()->clone());
1292 }
1293
1294 std::unique_ptr < const TrackParameters > firstscatpar;
1295 std::unique_ptr < const TrackParameters > lastscatpar;
1296 std::unique_ptr < const TrackParameters > elosspar;
1297
1298 const double charge = (lastidpar->parameters()[Trk::qOverP] < 0) ? -1 : 1;
1299
1300 Perigee startper(
1301 lastidpar->position(),
1302 lastidpar->momentum(),
1303 charge,
1304 lastidpar->position()
1305 );
1306
1307 if (!firstismuon) {
1308 firstscatpar = m_propagator->propagateParameters(
1309 ctx,
1310 *lastidpar,
1311 calomeots[0].associatedSurface(),
1313 false,
1314 trajectory.m_fieldprop,
1316
1317 if (!firstscatpar) {
1318 return nullptr;
1319 }
1320
1321 const std::unique_ptr<const TrackParameters> tmppar(
1322 m_propagator->propagateParameters(
1323 ctx,
1324 *firstscatpar,
1325 calomeots[1].associatedSurface(),
1327 false,
1328 trajectory.m_fieldprop,
1330
1331 if (!tmppar) {
1332 return nullptr;
1333 }
1334
1335 const double sign = (tmppar->parameters()[Trk::qOverP] < 0) ? -1 : 1;
1336 const double mass = Trk::ParticleMasses::mass[muon];
1337
1338 const double oldp = std::abs(1 / tmppar->parameters()[Trk::qOverP]);
1339 const double de = std::abs(calomeots[1].energyLoss()->deltaE());
1340
1341 double newp2 = oldp * oldp - 2 * de * std::sqrt(mass * mass + oldp * oldp) + de * de;
1342
1343 if (newp2 < 4.e6) {
1344 newp2 = 4.e6;
1345 }
1346
1347 const double newqoverp = sign / std::sqrt(newp2);
1348
1349 const AmgVector(5) & pars = tmppar->parameters();
1350
1351 elosspar=
1352 tmppar->associatedSurface().createUniqueTrackParameters(
1353 pars[0], pars[1], pars[2], pars[3], newqoverp, std::nullopt
1354 );
1355
1356 lastscatpar = m_propagator->propagateParameters(
1357 ctx,
1358 *elosspar,
1359 calomeots[2].associatedSurface(),
1361 false,
1362 trajectory.m_fieldprop,
1364
1365 if (!lastscatpar) {
1366 return nullptr;
1367 }
1368 } else {
1369 lastscatpar = m_propagator->propagateParameters(
1370 ctx,
1371 *firstidpar,
1372 calomeots[2].associatedSurface(),
1374 false,
1375 trajectory.m_fieldprop,
1377 );
1378
1379 if (!lastscatpar) {
1380 return nullptr;
1381 }
1382
1383 elosspar= m_propagator->propagateParameters(
1384 ctx,
1385 *lastscatpar,
1386 calomeots[1].associatedSurface(),
1388 false,
1389 trajectory.m_fieldprop,
1391 );
1392
1393 if (!elosspar) {
1394 return nullptr;
1395 }
1396
1397 const double sign = (elosspar->parameters()[Trk::qOverP] < 0) ? -1 : 1;
1398 const double newqoverp = sign /
1399 (1. / std::abs(elosspar->parameters()[Trk::qOverP]) +
1400 std::abs(calomeots[1].energyLoss()->deltaE()));
1401
1402 const AmgVector(5) & pars = elosspar->parameters();
1403
1404 std::unique_ptr<const TrackParameters>const tmppar(
1405 elosspar->associatedSurface().createUniqueTrackParameters(
1406 pars[0], pars[1], pars[2], pars[3], newqoverp, std::nullopt
1407 )
1408 );
1409
1410 firstscatpar = m_propagator->propagateParameters(
1411 ctx,
1412 *tmppar,
1413 calomeots[0].associatedSurface(),
1415 false,
1416 trajectory.m_fieldprop,
1418 );
1419
1420 if (!firstscatpar) {
1421 return nullptr;
1422 }
1423 }
1424
1425 for (; itStates != endState; ++itStates) {
1426 if (
1427 firstismuon &&
1428 (*itStates)->measurementOnTrack()->type(Trk::MeasurementBaseType::PseudoMeasurementOnTrack)
1429 ) {
1430 continue;
1431 }
1432
1433 if ((*itStates)->materialEffectsOnTrack() != nullptr) {
1434 if (!firstismuon) {
1435 cache.m_idmat = false;
1436 } else {
1437 continue;
1438 }
1439 }
1440
1441 if (firstismuon) {
1442 makeProtoState(cache, trajectory, *itStates);
1443 }
1444 }
1445
1446 std::unique_ptr<GXFMaterialEffects> firstscatmeff = std::make_unique<GXFMaterialEffects>(calomeots[0]);
1447 std::unique_ptr<GXFMaterialEffects> elossmeff = std::make_unique<GXFMaterialEffects>(calomeots[1]);
1448 std::unique_ptr<GXFMaterialEffects> secondscatmeff = std::make_unique<GXFMaterialEffects>(calomeots[2]);
1449
1450 double dp = 0;
1451 double sigmadp = 0;
1452 sigmadp = calomeots[1].energyLoss()->sigmaDeltaE();
1453 elossmeff->setSigmaDeltaE(sigmadp);
1454
1455 dp = 1000 * (lastscatpar->parameters()[Trk::qOverP] - firstscatpar->parameters()[Trk::qOverP]);
1456 elossmeff->setdelta_p(dp);
1457
1458 trajectory.addMaterialState(std::make_unique<GXFTrackState>(std::move(firstscatmeff), std::move(firstscatpar)), -1);
1459 trajectory.addMaterialState(std::make_unique<GXFTrackState>(std::move(elossmeff), std::move(elosspar)), -1);
1460 trajectory.addMaterialState(std::make_unique<GXFTrackState>(std::move(secondscatmeff), std::move(lastscatpar)), -1);
1461
1462 GXFTrackState *secondscatstate = trajectory.trackStates().back().get();
1463 const Surface *triggersurf1 = nullptr;
1464 const Surface *triggersurf2 = nullptr;
1465 Amg::Vector3D triggerpos1(0, 0, 0);
1466 Amg::Vector3D triggerpos2(0, 0, 0);
1467
1468 bool seenmdt = false;
1469 bool mdtbetweenphihits = false;
1470 int nphi = 0;
1471
1472 for (
1473 itStates2 = (!firstismuon ? beginStates2 : endState - 1);
1474 itStates2 != (!firstismuon ? endState2 : beginStates - 1);
1475 (!firstismuon ? ++itStates2 : --itStates2)
1476 ) {
1477 if (
1478 ((*itStates2)->measurementOnTrack() == nullptr) ||
1479 (*itStates2)->type(TrackStateOnSurface::Outlier)
1480 ) {
1481 continue;
1482 }
1483 const auto *const pMeasurement = (*itStates2)->measurementOnTrack();
1484 const Surface *surf = &pMeasurement->associatedSurface();
1485 const bool isCompetingRIOsOnTrack = pMeasurement->type(Trk::MeasurementBaseType::CompetingRIOsOnTrack);
1486 const RIO_OnTrack *rot = nullptr;
1487
1488 if (isCompetingRIOsOnTrack) {
1489 const auto *const crot = static_cast<const CompetingRIOsOnTrack *>(pMeasurement);
1490 rot = &crot->rioOnTrack(0);
1491 } else {
1492 if (pMeasurement->type(Trk::MeasurementBaseType::RIO_OnTrack)){
1493 rot = static_cast<const RIO_OnTrack *>(pMeasurement);
1494 }
1495 }
1496 if ((rot != nullptr) && m_DetID->is_mdt(rot->identify()) && (triggersurf1 != nullptr)) {
1497 seenmdt = true;
1498 }
1499 if (
1500 (rot != nullptr) && (
1501 m_DetID->is_tgc(rot->identify()) ||
1502 m_DetID->is_rpc(rot->identify()) ||
1503 m_DetID->is_stgc(rot->identify())
1504 )
1505 ) {
1506 const Amg::Vector3D measdir = surf->transform().rotation().col(0);
1507 const double dotprod1 = measdir.dot(Amg::Vector3D(0, 0, 1));
1508 const double dotprod2 = measdir.dot(Amg::Vector3D(surf->center().x(), surf->center().y(), 0) / surf->center().perp());
1509
1510 const bool measphi = std::abs(dotprod1) <= .5 && std::abs(dotprod2) <= .5;
1511 if (measphi) {
1512 nphi++;
1513 const Amg::Vector3D thispos =
1514 (*itStates2)->trackParameters() != nullptr ?
1515 (*itStates2)->trackParameters()->position() :
1516 rot->globalPosition();
1517 if (triggersurf1 != nullptr) {
1518 triggerpos2 = thispos;
1519 triggersurf2 = surf;
1520 if (seenmdt) {
1521 mdtbetweenphihits = true;
1522 }
1523 } else {
1524 triggerpos1 = thispos;
1525 triggersurf1 = surf;
1526 }
1527 }
1528 }
1529 }
1530
1531 double mdttrig1 = 999999;
1532 double mdttrig2 = 999999;
1533 const Surface *mdtsurf1 = nullptr;
1534 const Surface *mdtsurf2 = nullptr;
1535
1536 for (
1537 itStates2 = (!firstismuon ? beginStates2 : endState - 1);
1538 itStates2 != (!firstismuon ? endState2 : beginStates - 1);
1539 (!firstismuon ? ++itStates2 : --itStates2)
1540 ) {
1541 const Surface *surf = nullptr;
1542 if (
1543 ((*itStates2)->measurementOnTrack() != nullptr) &&
1544 !(*itStates2)->type(TrackStateOnSurface::Outlier)
1545 ) {
1546 surf = &(*itStates2)->measurementOnTrack()->associatedSurface();
1547 }
1548
1549 if (surf == nullptr) {
1550 continue;
1551 }
1552 const auto *const pThisMeasurement = (*itStates2)->measurementOnTrack();
1553 const bool isCompetingRioOnTrack = pThisMeasurement->type(Trk::MeasurementBaseType::CompetingRIOsOnTrack);
1554 const RIO_OnTrack *rot = nullptr;
1555
1556 if (isCompetingRioOnTrack) {
1557 const auto *crot = static_cast<const CompetingRIOsOnTrack *>(pThisMeasurement);
1558 rot = &crot->rioOnTrack(0);
1559 } else {
1560 if (pThisMeasurement->type(Trk::MeasurementBaseType::RIO_OnTrack)){
1561 rot = static_cast<const RIO_OnTrack *>(pThisMeasurement);
1562 }
1563 }
1564 const bool thisismdt = rot and m_DetID->is_mdt(rot->identify());
1565 if (thisismdt) {
1566 const Amg::Vector3D globpos =
1567 (*itStates2)->trackParameters() != nullptr ?
1568 (*itStates2)->trackParameters()->position() :
1569 pThisMeasurement->globalPosition();
1570 if (triggerpos1.mag() > 1 && (globpos - triggerpos1).mag() < mdttrig1) {
1571 mdttrig1 = (globpos - triggerpos1).mag();
1572 mdtsurf1 = surf;
1573 }
1574 if (triggerpos2.mag() > 1 && (globpos - triggerpos2).mag() < mdttrig2) {
1575 mdttrig2 = (globpos - triggerpos2).mag();
1576 mdtsurf2 = surf;
1577 }
1578 }
1579 }
1580
1581 GXFTrackState * firstpseudostate = nullptr;
1582 std::vector<GXFTrackState *> outlierstates;
1583 std::vector<GXFTrackState *> outlierstates2;
1584
1585 outlierstates.reserve(10);
1586 outlierstates2.reserve(10);
1587
1588 std::unique_ptr<PseudoMeasurementOnTrack> newpseudo;
1589
1590 for (itStates2 = beginStates2; itStates2 != endState2; ++itStates2) {
1591 const auto *const pMeasurement{(*itStates2)->measurementOnTrack()};
1592 const bool isPseudo = pMeasurement->type(Trk::MeasurementBaseType::PseudoMeasurementOnTrack);
1593 const bool isStraightLine =
1594 pMeasurement != nullptr ?
1595 pMeasurement->associatedSurface().type() == Trk::SurfaceType::Line :
1596 false;
1597
1598 if (
1599 isStraightLine &&
1600 !firstismuon &&
1601 (newpseudo == nullptr) && (
1602 (itStates2 == beginStates2 || itStates2 == beginStates2 + 1) &&
1603 std::abs(pMeasurement->globalPosition().z()) < 10000
1604 )
1605 ) {
1606 std::unique_ptr<const TrackParameters> par2;
1607 if (((*itStates2)->trackParameters() != nullptr) && nphi > 99) {
1608 par2.reset((*itStates2)->trackParameters()->clone());
1609 } else {
1610 par2 = m_propagator->propagateParameters(
1611 ctx,
1612 *secondscatstate->trackParameters(),
1613 pMeasurement->associatedSurface(),
1615 false,
1616 trajectory.m_fieldprop,
1618 );
1619 }
1620 if (par2 == nullptr) {
1621 continue;
1622 }
1623 Amg::MatrixX covMatrix(1, 1);
1624 covMatrix(0, 0) = 100;
1625
1626 newpseudo = std::make_unique<PseudoMeasurementOnTrack>(
1627 LocalParameters(DefinedParameter(par2->parameters()[Trk::locY], Trk::locY)),
1628 std::move(covMatrix),
1629 par2->associatedSurface()
1630 );
1631
1632 std::unique_ptr<GXFTrackState> firstpseudo = std::make_unique<GXFTrackState>(std::move(newpseudo), std::move(par2));
1633 firstpseudo->setMeasurementType(TrackState::Pseudo);
1634
1635 double errors[5];
1636 errors[0] = errors[2] = errors[3] = errors[4] = -1;
1637 errors[1] = 10;
1638
1639 firstpseudo->setMeasurementErrors(errors);
1640 firstpseudostate = firstpseudo.get();
1641 trajectory.addMeasurementState(std::move(firstpseudo));
1642 ATH_MSG_DEBUG("Adding PseudoMeasurement");
1643 continue;
1644 }
1645
1646 if (isPseudo && !firstismuon) {
1647 continue;
1648 }
1649
1650 if ((**itStates2).materialEffectsOnTrack() != nullptr) {
1651 if (firstismuon) {
1652 cache.m_idmat = false;
1653 } else {
1654 continue;
1655 }
1656 }
1657
1658 if (!firstismuon) {
1659 if (
1660 ((**itStates2).measurementOnTrack() != nullptr) &&
1661 &(**itStates2).measurementOnTrack()->associatedSurface() == triggersurf1 &&
1662 (mdtsurf1 != nullptr)
1663 ) {
1664 std::unique_ptr<Amg::Transform3D> transf = std::make_unique<Amg::Transform3D>(mdtsurf1->transform());
1665
1666 transf->translation() << triggerpos1;
1667 StraightLineSurface const slsurf(*transf);
1668 Amg::MatrixX covMatrix(1, 1);
1669 covMatrix(0, 0) = 100;
1670
1671 std::unique_ptr<const PseudoMeasurementOnTrack> newpseudo = std::make_unique<const PseudoMeasurementOnTrack>(
1672 LocalParameters(DefinedParameter(0, Trk::locY)), std::move(covMatrix), slsurf
1673 );
1674
1675 std::unique_ptr<GXFTrackState> pseudostate1 = std::make_unique<GXFTrackState>(std::move(newpseudo), nullptr);
1676 pseudostate1->setMeasurementType(TrackState::Pseudo);
1677
1678 double errors[5];
1679 errors[0] = errors[2] = errors[3] = errors[4] = -1;
1680 errors[1] = 10;
1681
1682 pseudostate1->setMeasurementErrors(errors);
1683 outlierstates2.push_back(pseudostate1.get());
1684 trajectory.addMeasurementState(std::move(pseudostate1));
1685 }
1686
1687 if (
1688 ((**itStates2).measurementOnTrack() != nullptr) &&
1689 &(**itStates2).measurementOnTrack()->associatedSurface() == triggersurf2 &&
1690 mdtbetweenphihits &&
1691 (mdtsurf2 != nullptr)
1692 ) {
1693 std::unique_ptr<Amg::Transform3D> transf = std::make_unique<Amg::Transform3D>(mdtsurf2->transform());
1694 transf->translation() << triggerpos2;
1695 StraightLineSurface const slsurf(*transf);
1696 Amg::MatrixX covMatrix(1, 1);
1697 covMatrix(0, 0) = 100;
1698
1699 std::unique_ptr<const PseudoMeasurementOnTrack> newpseudo = std::make_unique<const PseudoMeasurementOnTrack>(
1700 LocalParameters(DefinedParameter(0, Trk::locY)), std::move(covMatrix), slsurf
1701 );
1702
1703 std::unique_ptr<GXFTrackState> pseudostate2 = std::make_unique<GXFTrackState>(std::move(newpseudo), nullptr);
1704 pseudostate2->setMeasurementType(TrackState::Pseudo);
1705
1706 double errors[5];
1707 errors[0] = errors[2] = errors[3] = errors[4] = -1;
1708 errors[1] = 10;
1709
1710 pseudostate2->setMeasurementErrors(errors);
1711 // cppcheck-suppress invalidLifetime; false positive
1712 outlierstates2.push_back(pseudostate2.get());
1713 trajectory.addMeasurementState(std::move(pseudostate2));
1714 }
1715
1716 makeProtoState(cache, trajectory, *itStates2);
1717
1718 if (
1719 (
1720 trajectory.trackStates().back()->measurementType() == TrackState::TGC ||
1721 (
1722 trajectory.trackStates().back()->measurementType() == TrackState::RPC &&
1723 trajectory.trackStates().back()->measuresPhi()
1724 )
1725 ) &&
1726 trajectory.trackStates().back()->getStateType(TrackStateOnSurface::Measurement)
1727 ) {
1728 outlierstates.push_back(trajectory.trackStates().back().get());
1729 trajectory.setOutlier((int) trajectory.trackStates().size() - 1, true);
1730 }
1731 }
1732 }
1733
1734 trajectory.setNumberOfPerigeeParameters(0);
1735
1736 Track *track = nullptr;
1737
1738 trajectory.setPrefit(2);
1739 const TrackParameters *startpar2 = &startper;
1740 cache.m_matfilled = true;
1741 const bool tmpacc = cache.m_acceleration;
1742 cache.m_acceleration = false;
1743 // @TODO eventually track created but not used why ?
1744 const std::unique_ptr<Trk::Track> tmp_track(
1745 myfit(ctx, cache, trajectory, *startpar2, false, muon));
1746 cache.m_acceleration = tmpacc;
1747
1748 cache.m_matfilled = false;
1749 if (
1750 !firstismuon &&
1751 trajectory.converged() &&
1752 std::abs(trajectory.residuals().tail<1>()(0) / trajectory.errors().tail<1>()(0)) > 10
1753 ) {
1754 return nullptr;
1755 }
1756
1757 if (trajectory.converged()) {
1758 if (firstpseudostate != nullptr) {
1759 const TrackParameters *par2 = firstpseudostate->trackParameters();
1760 Amg::MatrixX covMatrix(1, 1);
1761 covMatrix(0, 0) = 100;
1762
1763 std::unique_ptr<const PseudoMeasurementOnTrack> newpseudo = std::make_unique<const PseudoMeasurementOnTrack>(
1764 LocalParameters(DefinedParameter(par2->parameters()[Trk::locY], Trk::locY)),
1765 std::move(covMatrix),
1766 par2->associatedSurface()
1767 );
1768 firstpseudostate->setMeasurement(std::move(newpseudo));
1769 firstpseudostate->setRecalibrated(false);
1770 }
1771
1772 for (int j = 0; j < (int) trajectory.trackStates().size(); j++) {
1773 for (const auto & i : outlierstates2) {
1774 if (trajectory.trackStates()[j].get() == i) {
1775 trajectory.setOutlier(j, true);
1776 }
1777 }
1778
1779 for (const auto & i : outlierstates) {
1780 if (trajectory.trackStates()[j].get() == i) {
1781 trajectory.setOutlier(j, false);
1782 }
1783 }
1784 }
1785
1786 for (
1787 itStates = (firstismuon ? beginStates2 : endState - 1);
1788 itStates != (firstismuon ? endState2 : beginStates - 1);
1789 (firstismuon ? ++itStates : --itStates)
1790 ) {
1791 if ((*itStates)->measurementOnTrack()->type(Trk::MeasurementBaseType::PseudoMeasurementOnTrack)) {
1792 continue;
1793 }
1794
1795 makeProtoState(cache, trajectory, *itStates, (firstismuon ? -1 : 0));
1796 }
1797
1798 trajectory.reset();
1799 trajectory.setPrefit(0);
1800 trajectory.setNumberOfPerigeeParameters(5);
1801 track = myfit(ctx, cache, trajectory, *firstidpar, false, muon);
1802 cache.m_matfilled = false;
1803 }
1804
1805 return track;
1806 }
1807
1808 std::unique_ptr<Track>
1810 const EventContext& ctx,
1811 const Track& inputTrack,
1812 const RunOutlierRemoval runOutlier,
1813 const ParticleHypothesis matEffects) const
1814 {
1815 ATH_MSG_DEBUG("--> entering GlobalChi2Fitter::fit(Track,)");
1816
1817 Cache cache(this);
1818 initFieldCache(ctx,cache);
1819
1820 GXFTrajectory trajectory;
1821
1822 if (!m_straightlineprop) {
1823 trajectory.m_straightline = (!cache.m_field_cache.solenoidOn() && !cache.m_field_cache.toroidOn());
1824 }
1825
1826 trajectory.m_fieldprop = trajectory.m_straightline ? Trk::NoField : Trk::FullField;
1827
1828 return std::unique_ptr<Track>(
1829 fitIm(ctx, cache, inputTrack, runOutlier, matEffects));
1830 }
1831
1832 Track *
1833 GlobalChi2Fitter::alignmentFit(const EventContext& ctx,
1834 AlignmentCache& alignCache,
1835 const Track &inputTrack,
1836 const RunOutlierRemoval runOutlier,
1837 const ParticleHypothesis matEffects) const {
1838
1839 Cache cache(this);
1840 initFieldCache(ctx, cache);
1841
1842 alignCache.m_derivMatrix.reset();
1843 alignCache.m_fullCovarianceMatrix.reset();
1844 alignCache.m_iterationsOfLastFit = 0;
1845
1846 Trk::Track* newTrack =
1847 fitIm(ctx, cache, inputTrack, runOutlier, matEffects);
1848 if(newTrack != nullptr){
1849 if(cache.m_derivmat.size() != 0)
1850 alignCache.m_derivMatrix = std::make_unique<Amg::MatrixX>(cache.m_derivmat);
1851 if(cache.m_fullcovmat.size() != 0)
1852 alignCache.m_fullCovarianceMatrix = std::make_unique<Amg::MatrixX>(cache.m_fullcovmat);
1853 alignCache.m_iterationsOfLastFit = cache.m_lastiter;
1854 }
1855 return newTrack;
1856 }
1857
1858 Track*
1860 const EventContext& ctx,
1861 Cache& cache,
1862 const Track& inputTrack,
1863 const RunOutlierRemoval runOutlier,
1864 const ParticleHypothesis matEffects) const
1865 {
1866
1867 ATH_MSG_DEBUG("--> entering GlobalChi2Fitter::fit(Track,,)");
1868
1869 GXFTrajectory trajectory;
1870
1871 if (!m_straightlineprop) {
1872 trajectory.m_straightline = (!cache.m_field_cache.solenoidOn() && !cache.m_field_cache.toroidOn());
1873 }
1874
1875 trajectory.m_fieldprop = trajectory.m_straightline ? Trk::NoField : Trk::FullField;
1876
1877 if (inputTrack.trackStateOnSurfaces()->empty()) {
1878 ATH_MSG_WARNING("Track with zero track states, cannot perform fit");
1879 return nullptr;
1880 }
1881
1882 if (inputTrack.trackParameters()->empty()) {
1883 ATH_MSG_WARNING("Track without track parameters, cannot perform fit");
1884 return nullptr;
1885 }
1886
1887 std::unique_ptr<const TrackParameters> minpar = unique_clone(inputTrack.perigeeParameters());
1888 const TrackParameters *firstidpar = nullptr;
1889 const TrackParameters *lastidpar = nullptr;
1890
1891 if (minpar == nullptr) {
1892 minpar = unique_clone(*(inputTrack.trackParameters()->begin()));
1893 }
1894
1895 const bool tmpgetmat = cache.m_getmaterialfromtrack;
1896
1897 if (
1898 matEffects == Trk::nonInteracting ||
1899 inputTrack.info().trackFitter() == TrackInfo::Unknown
1900 ) {
1901 cache.m_getmaterialfromtrack = false;
1902 }
1903
1905 const Trk::TrackStates::const_iterator endState = inputTrack.trackStateOnSurfaces()->end();
1906
1907 trajectory.trackStates().reserve(inputTrack.trackStateOnSurfaces()->size());
1908
1909 const Surface *firsthitsurf = nullptr;
1910 const Surface *lasthitsurf = nullptr;
1911 bool hasid = false;
1912 bool hasmuon = false;
1913 bool iscombined = false;
1914 bool seenphimeas = false;
1915 bool phiem = false;
1916 bool phibo = false;
1917
1918 for (; itStates != endState; ++itStates) {
1919 const auto *const pMeasurement = (**itStates).measurementOnTrack();
1920 if (
1921 (pMeasurement == nullptr) &&
1922 ((**itStates).materialEffectsOnTrack() != nullptr) &&
1923 matEffects != inputTrack.info().particleHypothesis()
1924 ) {
1925 continue;
1926 }
1927
1928 if (pMeasurement != nullptr) {
1929 const Surface *surf = &pMeasurement->associatedSurface();
1931 if (!hitid.is_valid()) {
1932 if (pMeasurement->type(Trk::MeasurementBaseType::CompetingRIOsOnTrack)) {
1933 const CompetingRIOsOnTrack *crot = static_cast<const CompetingRIOsOnTrack *>(pMeasurement);
1934 hitid = crot->rioOnTrack(0).identify();
1935 }
1936 }
1937 if (hitid.is_valid()) {
1938 if (firsthitsurf == nullptr) {
1939 firsthitsurf = surf;
1940 }
1941 lasthitsurf = surf;
1942 if (m_DetID->is_indet(hitid)) {
1943 hasid = true;
1944 if (hasmuon) {
1945 iscombined = true;
1946 }
1947 if ((**itStates).trackParameters() != nullptr) {
1948 lastidpar = (**itStates).trackParameters();
1949 if (firstidpar == nullptr) {
1950 firstidpar = lastidpar;
1951 }
1952 }
1953 } else {
1954 if (!(**itStates).type(TrackStateOnSurface::Outlier)) {
1955 const Amg::Vector3D measdir = surf->transform().rotation().col(0);
1956 const double dotprod1 = measdir.dot(Amg::Vector3D(0, 0, 1));
1957 const double dotprod2 = measdir.dot(Amg::Vector3D(surf->center().x(), surf->center().y(), 0) / surf->center().perp());
1958 if (std::abs(dotprod1) < .5 && std::abs(dotprod2) < .5) {
1959 seenphimeas = true;
1960 if (std::abs(surf->center().z()) > 13000) {
1961 phiem = true;
1962 }
1963 if (surf->center().perp() > 9000 && std::abs(surf->center().z()) < 13000) {
1964 phibo = true;
1965 }
1966 }
1967 }
1968 hasmuon = true;
1969 if (hasid) {
1970 iscombined = true;
1971 }
1972 }
1973 }
1974
1975 if (iscombined && seenphimeas && (phiem || phibo)) {
1977 continue;
1978 }
1979 }
1980 }
1981 makeProtoState(cache, trajectory, *itStates);
1982 }
1983
1984 if (
1985 cache.m_getmaterialfromtrack && trajectory.numberOfScatterers() != 0 &&
1986 (hasmuon || cache.m_acceleration)
1987 ) {
1988 cache.m_matfilled = true;
1989 }
1990
1991 if (firstidpar == lastidpar) {
1992 firstidpar = lastidpar = nullptr;
1993 }
1994
1995 if (
1996 iscombined &&
1997 !cache.m_matfilled &&
1998 (
1999 m_DetID->is_indet(firsthitsurf->associatedDetectorElementIdentifier()) !=
2000 m_DetID->is_indet(lasthitsurf->associatedDetectorElementIdentifier())
2001 ) &&
2002 (firstidpar != nullptr)
2003 ) {
2004 if (m_DetID->is_indet(firsthitsurf->associatedDetectorElementIdentifier())) {
2005 minpar = unique_clone(lastidpar);
2006 } else {
2007 minpar = unique_clone(firstidpar);
2008 }
2009 }
2010
2011 const bool tmpacc = cache.m_acceleration;
2012 const bool tmpfiteloss = m_fiteloss;
2013 const bool tmpsirecal = cache.m_sirecal;
2014 std::unique_ptr<Track> tmptrack = nullptr;
2015
2016 if (matEffects == Trk::proton || matEffects == Trk::kaon || matEffects == Trk::electron) {
2017 ATH_MSG_DEBUG("call myfit(GXFTrajectory,TP,,)");
2018 cache.m_fiteloss = true;
2019 cache.m_sirecal = false;
2020
2021 if (matEffects == Trk::electron) {
2022 cache.m_asymeloss = true;
2023 }
2024
2025 tmptrack.reset(myfit(ctx, cache, trajectory, *minpar, false, matEffects));
2026 cache.m_sirecal = tmpsirecal;
2027
2028 if (tmptrack == nullptr) {
2029 cache.m_matfilled = false;
2030 cache.m_getmaterialfromtrack = tmpgetmat;
2031 cache.m_acceleration = tmpacc;
2032 cache.m_fiteloss = tmpfiteloss;
2033 return nullptr;
2034 }
2035
2036 int nscats = 0;
2037 bool isbrem = false;
2038 double bremdp = 0;
2039 unsigned int n_brem=0;
2040
2041 for (std::unique_ptr<GXFTrackState> & state : trajectory.trackStates()) {
2042 GXFMaterialEffects *meff = state->materialEffects();
2043
2044 if (meff != nullptr) {
2045 nscats++;
2046
2047 const TrackParameters *layerpars = state->trackParameters();
2048 const MaterialProperties *matprop = meff->materialProperties();
2049
2050 const double p = 1. / std::abs(layerpars->parameters()[Trk::qOverP] - .0005 * meff->delta_p());
2051
2052 std::optional<Amg::Vector2D> locpos(state->associatedSurface().globalToLocal(layerpars->position()));
2053 const Amg::Vector3D layerNormal(state->associatedSurface().normal(*locpos));
2054 double costracksurf = 1.;
2055
2056 costracksurf = std::abs(layerNormal.dot(layerpars->momentum().unit()));
2057
2058 const double oldde = meff->deltaE();
2059
2060 std::unique_ptr<EnergyLoss> eloss;
2061 double sigmascat = 0;
2062
2063 if (matprop != nullptr) {
2064 eloss = std::make_unique<EnergyLoss>(
2065 m_elosstool->energyLoss(*matprop, p, 1. / costracksurf,
2066 Trk::alongMomentum, matEffects));
2067 sigmascat = std::sqrt(m_scattool->sigmaSquare(*matprop, p, 1. / costracksurf, matEffects));
2068
2069 if (eloss != nullptr) {
2070 meff->setDeltaE(eloss->deltaE());
2071 }
2072 } else {
2073 MaterialProperties const tmpprop(1., meff->x0(), 0., 0., 0., 0.);
2074 sigmascat = std::sqrt(m_scattool->sigmaSquare(tmpprop, p, 1. / costracksurf, matEffects));
2075 }
2076
2077 meff->setScatteringSigmas(
2078 sigmascat / std::sin(layerpars->parameters()[Trk::theta]),
2079 sigmascat
2080 );
2081
2082
2083 if (matEffects == electron) {
2084 state->resetStateType(TrackStateOnSurface::Scatterer);
2085 meff->setDeltaE(oldde);
2086 if (!meff->isKink()) {
2087 meff->setSigmaDeltaE(0);
2088 } else {
2089 isbrem = true;
2090 bremdp = meff->delta_p();
2091 }
2092 } else if (eloss != nullptr) {
2093 meff->setSigmaDeltaE(eloss->sigmaDeltaE());
2094 }
2095 if (meff->sigmaDeltaE() > 0) {
2096 ++n_brem;
2097 }
2098 }
2099 }
2100
2101 const AmgVector(5) & refpars = trajectory.referenceParameters()->parameters();
2103 refpars[0], refpars[1], refpars[2], refpars[3], refpars[4], std::nullopt
2104 );
2105
2106 trajectory.reset();
2107 cache.m_matfilled = true;
2108
2109 if (matEffects == Trk::electron) {
2110 if (!isbrem) {
2111 trajectory.brems().clear();
2112 } else {
2113 trajectory.brems().resize(1);
2114 trajectory.brems()[0] = bremdp;
2115 }
2116
2117 cache.m_asymeloss = false;
2118 trajectory.setNumberOfScatterers(nscats);
2119 // @TODO fillResiduals assumes that numberOfBrems == number of states with material effects and sigmaDeltaE() > 0
2120 // not clear whether fillResiduals has to be adjusted for electrons rather than this
2121 trajectory.setNumberOfBrems(n_brem);
2122 }
2123 }
2124
2125 std::unique_ptr<Track> track(myfit(ctx, cache, trajectory, *minpar, runOutlier, matEffects));
2126
2127 bool pseudoupdated = false;
2128
2129 if ((track != nullptr) && hasid && hasmuon) {
2130 for (std::unique_ptr<GXFTrackState> & pseudostate : trajectory.trackStates()) {
2131 if (
2132 (pseudostate == nullptr) ||
2133 pseudostate->measurementType() != TrackState::Pseudo ||
2134 pseudostate->fitQuality().chiSquared() < 10
2135 ) {
2136 continue;
2137 }
2138
2139 const TrackParameters *pseudopar = pseudostate->trackParameters();
2140 const std::unique_ptr<const TrackParameters> updpar(m_updator->removeFromState(
2141 *pseudopar,
2142 pseudostate->measurement()->localParameters(),
2143 pseudostate->measurement()->localCovariance()
2144 ));
2145
2146 if (updpar == nullptr) {
2147 continue;
2148 }
2149
2150 Amg::MatrixX covMatrix(1, 1);
2151 covMatrix(0, 0) = 100;
2152
2153 std::unique_ptr<const PseudoMeasurementOnTrack> newpseudo = std::make_unique<const PseudoMeasurementOnTrack>(
2154 LocalParameters(DefinedParameter(updpar->parameters()[Trk::locY], Trk::locY)),
2155 std::move(covMatrix),
2156 pseudopar->associatedSurface()
2157 );
2158
2159 pseudostate->setMeasurement(std::move(newpseudo));
2160 double errors[5];
2161 errors[0] = errors[2] = errors[3] = errors[4] = -1;
2162 errors[1] = 10;
2163 pseudostate->setMeasurementErrors(errors);
2164 pseudoupdated = true;
2165 }
2166
2167 if (pseudoupdated) {
2168 trajectory.setConverged(false);
2169 cache.m_matfilled = true;
2170 track.reset(myfit(ctx, cache, trajectory, *track->perigeeParameters(), false, muon));
2171 cache.m_matfilled = false;
2172 }
2173 }
2174
2175 cache.m_matfilled = false;
2176 cache.m_getmaterialfromtrack = tmpgetmat;
2177 cache.m_acceleration = tmpacc;
2178 cache.m_fiteloss = tmpfiteloss;
2179
2180 if (track != nullptr) {
2181 cache.incrementFitStatus(S_SUCCESSFUL_FITS);
2182 const TrackInfo& old_info = inputTrack.info();
2183 track->info().addPatternReco(old_info);
2184 }
2185
2186 return track.release();
2187 }
2188
2189 std::unique_ptr<Track>
2191 const EventContext& ctx,
2192 const PrepRawDataSet& prds,
2193 const TrackParameters& param,
2194 const RunOutlierRemoval runOutlier,
2195 const ParticleHypothesis matEffects) const
2196 {
2197 ATH_MSG_DEBUG("--> entering GlobalChi2Fitter::fit(PRDS,TP,)");
2198 MeasurementSet rots;
2199
2200 for (const auto *prd : prds) {
2201 const Surface & prdsurf = (*prd).detectorElement()->surface((*prd).identify());
2202 const RIO_OnTrack *rot = nullptr;
2203 const PlaneSurface *plsurf = nullptr;
2204
2205 if (prdsurf.type() == Trk::SurfaceType::Plane)
2206 plsurf = static_cast < const PlaneSurface *>(&prdsurf);
2207
2208 const StraightLineSurface *slsurf = nullptr;
2209
2210 if (prdsurf.type() == Trk::SurfaceType::Line)
2211 slsurf = static_cast < const StraightLineSurface *>(&prdsurf);
2212
2213 if ((slsurf == nullptr) && (plsurf == nullptr)) {
2214 ATH_MSG_ERROR("Surface is neither PlaneSurface nor StraightLineSurface!");
2215 }
2216
2217 if (!m_broadROTcreator.empty() && (slsurf != nullptr)) {
2218 rot = m_broadROTcreator->correct(*prd, param, ctx);
2219 } else if (slsurf != nullptr) {
2220 AtaStraightLine const atasl(
2221 slsurf->center(),
2222 param.parameters()[Trk::phi],
2223 param.parameters()[Trk::theta],
2224 param.parameters()[Trk::qOverP],
2225 *slsurf
2226 );
2227 rot = m_ROTcreator->correct(*prd, atasl, ctx);
2228 } else if (plsurf != nullptr) {
2229 if (param.covariance() != nullptr) {
2230 AtaPlane const atapl(
2231 plsurf->center(),
2232 param.parameters()[Trk::phi],
2233 param.parameters()[Trk::theta],
2234 param.parameters()[Trk::qOverP],
2235 *plsurf,
2236 AmgSymMatrix(5)(*param.covariance())
2237 );
2238 rot = m_ROTcreator->correct(*prd, atapl, ctx);
2239 } else {
2240 AtaPlane const atapl(
2241 plsurf->center(),
2242 param.parameters()[Trk::phi],
2243 param.parameters()[Trk::theta],
2244 param.parameters()[Trk::qOverP],
2245 *plsurf
2246 );
2247 rot = m_ROTcreator->correct(*prd, atapl, ctx);
2248 }
2249 }
2250
2251 if (rot != nullptr) {
2252 rots.push_back(rot);
2253 }
2254 }
2255
2256 std::unique_ptr<Track> track =
2257 fit(ctx, rots, param, runOutlier, matEffects);
2258
2259 for (const auto *rot : rots) {
2260 delete rot;
2261 }
2262
2263 return track;
2264 }
2265
2266 std::unique_ptr<Track>
2268 const EventContext& ctx,
2269 const Track& inputTrack,
2270 const MeasurementSet& addMeasColl,
2271 const RunOutlierRemoval runOutlier,
2272 const ParticleHypothesis matEffects) const
2273 {
2274 ATH_MSG_DEBUG("--> entering GlobalChi2Fitter::fit(Track,Meas'BaseSet,,)");
2275
2276 Cache cache(this);
2277 initFieldCache(ctx,cache);
2278
2279 GXFTrajectory trajectory;
2280
2281 if (!m_straightlineprop) {
2282 trajectory.m_straightline = (!cache.m_field_cache.solenoidOn() && !cache.m_field_cache.toroidOn());
2283 }
2284
2285 trajectory.m_fieldprop = trajectory.m_straightline ? Trk::NoField : Trk::FullField;
2286
2287 const TrackParameters *minpar = inputTrack.perigeeParameters();
2288
2289 if (minpar == nullptr) {
2290 minpar = *(inputTrack.trackParameters()->begin());
2291 }
2292
2293 MeasurementSet const hitColl;
2294
2295 // collect MBs from Track (speed: assume this method is used for extending track at end)
2297 const Trk::TrackStates::const_iterator endState = inputTrack.trackStateOnSurfaces()->end();
2298
2299 const bool old_reintoutl = cache.m_reintoutl;
2300 cache.m_reintoutl = false;
2301 const bool tmpasymeloss = cache.m_asymeloss;
2302
2303 if (matEffects == electron) {
2304 cache.m_asymeloss = true;
2305 }
2306
2307 for (; itStates != endState; ++itStates) {
2308 makeProtoState(cache, trajectory, *itStates);
2309 if (
2310 matEffects == electron &&
2311 !trajectory.trackStates().empty() &&
2312 (trajectory.trackStates().back()->materialEffects() != nullptr) &&
2313 trajectory.trackStates().back()->materialEffects()->sigmaDeltaE() > 50.001
2314 ) {
2315 trajectory.trackStates().back()->materialEffects()->setKink(true);
2316 }
2317 }
2318
2319 cache.m_reintoutl = old_reintoutl;
2320
2321 for (const auto & measBase : addMeasColl) {
2322 if (measBase == nullptr) {
2323 ATH_MSG_WARNING("There is an empty MeasurementBase object in the track! Skip this object..");
2324 continue;
2325 }
2326
2327 makeProtoStateFromMeasurement(cache, trajectory, measBase);
2328 }
2329
2330 // fit set of MeasurementBase using main method, start with first TrkParameter in inputTrack
2331 std::unique_ptr<Track> track(myfit(ctx, cache, trajectory, *minpar, runOutlier, matEffects));
2332 cache.m_asymeloss = tmpasymeloss;
2333
2334 if (track != nullptr) {
2335 const double oldqual =
2336 inputTrack.fitQuality()->numberDoF() != 0 ?
2337 inputTrack.fitQuality()->chiSquared() / inputTrack.fitQuality()->numberDoF() :
2338 -999;
2339
2340 const double newqual =
2341 track->fitQuality()->numberDoF() != 0 ?
2342 track->fitQuality()->chiSquared() / track->fitQuality()->numberDoF() :
2343 -999;
2344
2345 if (m_extensioncuts && runOutlier && newqual > 2 && newqual > 2 * oldqual) {
2346 ATH_MSG_DEBUG("Extension rejected");
2347
2348 cache.incrementFitStatus(S_HIGH_CHI2);
2349 track.reset(nullptr);
2350 }
2351 }
2352
2353 if (track != nullptr) {
2354 cache.incrementFitStatus(S_SUCCESSFUL_FITS);
2355 }
2356
2357 return track;
2358 }
2359
2360 // extend a track fit to include an additional set of PrepRawData objects
2361 // --------------------------------
2362 std::unique_ptr<Track>
2364 const EventContext& ctx,
2365 const Track& intrk,
2366 const PrepRawDataSet& prds,
2367 const RunOutlierRemoval runOutlier,
2368 const ParticleHypothesis matEffects) const
2369 {
2370 ATH_MSG_DEBUG("--> entering GlobalChi2Fitter::fit(Track,PRDS,)");
2371 MeasurementSet rots;
2372 const TrackParameters *hitparam = intrk.trackParameters()->back();
2373
2374 for (const auto *prd : prds) {
2375 const Surface & prdsurf = (*prd).detectorElement()->surface((*prd).identify());
2376
2377 Amg::VectorX parameterVector = hitparam->parameters();
2378 std::unique_ptr<const TrackParameters>const trackparForCorrect(
2380 parameterVector[Trk::loc1],
2381 parameterVector[Trk::loc2],
2382 parameterVector[Trk::phi],
2383 parameterVector[Trk::theta],
2384 parameterVector[Trk::qOverP],
2385 AmgSymMatrix(5)(*hitparam->covariance())
2386 )
2387 );
2388
2389 const RIO_OnTrack *rot = nullptr;
2390
2391 if (!m_broadROTcreator.empty() && prdsurf.type() == Trk::SurfaceType::Line) {
2392 rot = m_broadROTcreator->correct(*prd, *hitparam, ctx);
2393 } else {
2394 rot = m_ROTcreator->correct(*prd, *trackparForCorrect, ctx);
2395 }
2396
2397 if (rot != nullptr) {
2398 rots.push_back(rot);
2399 }
2400 }
2401
2402 std::unique_ptr<Track> track = fit(ctx,intrk, rots, runOutlier, matEffects);
2403
2404 for (const auto *rot : rots) {
2405 delete rot;
2406 }
2407
2408 return track;
2409 }
2410
2411 std::unique_ptr<Track> GlobalChi2Fitter::fit(
2412 const EventContext& ctx,
2413 const MeasurementSet & rots,
2414 const TrackParameters & param,
2415 const RunOutlierRemoval runOutlier,
2416 const ParticleHypothesis matEffects
2417 ) const {
2418 ATH_MSG_DEBUG("--> entering GlobalChi2Fitter::fit(Meas'BaseSet,,)");
2419
2420 Cache cache(this);
2421 initFieldCache(ctx,cache);
2422
2423 GXFTrajectory trajectory;
2424
2425 if (!m_straightlineprop) {
2426 trajectory.m_straightline = (!cache.m_field_cache.solenoidOn() && !cache.m_field_cache.toroidOn());
2427 }
2428
2429 trajectory.m_fieldprop = trajectory.m_straightline ? Trk::NoField : Trk::FullField;
2430
2431 for (const auto *itSet : rots) {
2432 if (itSet == nullptr) {
2433 ATH_MSG_WARNING("There is an empty MeasurementBase object in the track! Skip this object..");
2434 } else {
2435 makeProtoStateFromMeasurement(cache, trajectory, itSet);
2436 }
2437 }
2438
2439 std::unique_ptr<const TrackParameters> startpar(param.clone());
2440
2441 if (
2442 matEffects == muon &&
2443 trajectory.numberOfSiliconHits() + trajectory.numberOfTRTHits() == 0
2444 ) {
2445 cache.m_matfilled = true;
2446 trajectory.setPrefit(2);
2447
2448 myfit(ctx,cache, trajectory, *startpar, runOutlier, matEffects);
2449
2450 cache.m_matfilled = false;
2451
2452 if (!trajectory.converged()) {
2453 return nullptr;
2454 }
2455
2456 trajectory.setConverged(false);
2457 const TrackParameters *firstpar = trajectory.trackStates()[0]->trackParameters();
2458 const TrackParameters *lastpar = trajectory.trackStates().back()->trackParameters();
2459
2460 PerigeeSurface const persurf(firstpar->position() - 10 * firstpar->momentum().unit());
2461
2462 if (trajectory.trackStates().front()->measurementType() == TrackState::Pseudo) {
2463 Amg::MatrixX covMatrix(1, 1);
2464 covMatrix(0, 0) = 100;
2465
2466 std::unique_ptr<const PseudoMeasurementOnTrack> newpseudo = std::make_unique<const PseudoMeasurementOnTrack>(
2467 LocalParameters(DefinedParameter(firstpar->parameters()[Trk::locY], Trk::locY)),
2468 std::move(covMatrix),
2469 firstpar->associatedSurface()
2470 );
2471
2472 trajectory.trackStates().front()->setMeasurement(std::move(newpseudo));
2473 }
2474
2475 if (trajectory.trackStates().back()->measurementType() == TrackState::Pseudo) {
2476 Amg::MatrixX covMatrix(1, 1);
2477 covMatrix(0, 0) = 100;
2478
2479 std::unique_ptr<const PseudoMeasurementOnTrack> newpseudo = std::make_unique<const PseudoMeasurementOnTrack>(
2480 LocalParameters(DefinedParameter(lastpar->parameters()[Trk::locY], Trk::locY)),
2481 std::move(covMatrix),
2482 lastpar->associatedSurface()
2483 );
2484
2485 trajectory.trackStates().back()->setMeasurement(std::move(newpseudo));
2486 }
2487
2488 if (!trajectory.m_straightline) {
2489 trajectory.setPrefit(3);
2490 const AmgVector(5) & refpars = trajectory.referenceParameters()->parameters();
2492 refpars[0], refpars[1], refpars[2], refpars[3], refpars[4], std::nullopt
2493 );
2494
2495 trajectory.reset();
2496
2497 myfit(ctx,cache, trajectory, *startpar, runOutlier, matEffects);
2498
2499 cache.m_matfilled = true;
2500
2501 if (!trajectory.converged()) {
2502 return nullptr;
2503 }
2504 }
2505
2506 const AmgVector(5) & refpars = trajectory.referenceParameters()->parameters();
2508 refpars[0], refpars[1], refpars[2], refpars[3], refpars[4], std::nullopt
2509 );
2510
2511 trajectory.reset();
2512 trajectory.setPrefit(0);
2513
2514 if (trajectory.trackStates().front()->measurementType() == TrackState::Pseudo) {
2515 firstpar = trajectory.trackStates().front()->trackParameters();
2516
2517 Amg::MatrixX covMatrix(1, 1);
2518 covMatrix(0, 0) = 100;
2519
2520 std::unique_ptr<const PseudoMeasurementOnTrack> newpseudo = std::make_unique<const PseudoMeasurementOnTrack>(
2521 LocalParameters(DefinedParameter(firstpar->parameters()[Trk::locY], Trk::locY)),
2522 std::move(covMatrix),
2523 firstpar->associatedSurface()
2524 );
2525
2526 trajectory.trackStates().front()->setMeasurement(std::move(newpseudo));
2527 double errors[5];
2528 errors[0] = errors[2] = errors[3] = errors[4] = -1;
2529 errors[1] = 10;
2530 trajectory.trackStates().front()->setMeasurementErrors(errors);
2531 }
2532
2533 if (trajectory.trackStates().back()->measurementType() == TrackState::Pseudo) {
2534 lastpar = trajectory.trackStates().back()->trackParameters();
2535
2536 Amg::MatrixX covMatrix(1, 1);
2537 covMatrix(0, 0) = 100;
2538
2539 std::unique_ptr<const PseudoMeasurementOnTrack> newpseudo = std::make_unique<const PseudoMeasurementOnTrack>(
2540 LocalParameters(DefinedParameter(lastpar->parameters()[Trk::locY], Trk::locY)),
2541 std::move(covMatrix),
2542 lastpar->associatedSurface()
2543 );
2544
2545 trajectory.trackStates().back()->setMeasurement(std::move(newpseudo));
2546 double errors[5];
2547 errors[0] = errors[2] = errors[3] = errors[4] = -1;
2548 errors[1] = 10;
2549 trajectory.trackStates().back()->setMeasurementErrors(errors);
2550 }
2551 }
2552
2553 std::unique_ptr<Track> track;
2554
2555 if (startpar != nullptr) {
2556 track.reset(myfit(ctx,cache, trajectory, *startpar, runOutlier, matEffects));
2557 }
2558
2559 if (track != nullptr) {
2560 cache.incrementFitStatus(S_SUCCESSFUL_FITS);
2561 }
2562 cache.m_matfilled = false;
2563
2564 return track;
2565 }
2566
2568 Cache & cache,
2569 GXFTrajectory & trajectory,
2570 const TrackStateOnSurface * tsos,
2571 int index
2572 ) const {
2573 if (
2574 (
2579 ) && cache.m_getmaterialfromtrack
2580 ) {
2581 if (cache.m_acceleration && trajectory.numberOfHits() == 0) {
2582 return;
2583 }
2585 return;
2586 }
2587 const MaterialEffectsOnTrack *meff = static_cast<const MaterialEffectsOnTrack *>(tsos->materialEffectsOnTrack());
2588
2589 std::unique_ptr<GXFMaterialEffects> newmeff;
2590
2591 if (
2592 meff->scatteringAngles() or
2593 meff->energyLoss() or
2595 (tsos->trackParameters() == nullptr)
2596 ) {
2597 newmeff = std::make_unique<GXFMaterialEffects>(*meff);
2598 } else {
2599 Trk::MaterialProperties const matprop(meff->thicknessInX0(), 1., 0., 0., 0., 0.);
2600
2601 const double sigmascat = std::sqrt(m_scattool->sigmaSquare(
2602 matprop,
2603 std::abs(1. / tsos->trackParameters()->parameters()[Trk::qOverP]),
2604 1.,
2605 Trk::muon)
2606 );
2607
2608 auto newsa = Trk::ScatteringAngles(
2609 0,
2610 0,
2611 sigmascat / std::sin(tsos->trackParameters()->parameters()[Trk::theta]),
2612 sigmascat
2613 );
2614
2615 Trk::MaterialEffectsOnTrack const newmeot(
2616 meff->thicknessInX0(),
2617 newsa,
2618 nullptr,
2619 tsos->surface()
2620 );
2621
2622 newmeff = std::make_unique<GXFMaterialEffects>(newmeot);
2623 }
2624
2625 if (
2626 (meff->energyLoss() != nullptr) &&
2627 meff->energyLoss()->sigmaDeltaE() > 0 &&
2628 (
2629 (tsos->type(TrackStateOnSurface::BremPoint) && (meff->scatteringAngles() != nullptr)) ||
2630 ((meff->scatteringAngles() == nullptr) || meff->thicknessInX0() == 0)
2631 )
2632 ) {
2633 newmeff->setSigmaDeltaE(meff->energyLoss()->sigmaDeltaE());
2634
2635 if (
2636 (tsos->trackParameters() != nullptr) &&
2637 !trajectory.trackStates().empty() &&
2638 ((**trajectory.trackStates().rbegin()).trackParameters() != nullptr)
2639 ) {
2640 const double delta_p = 1000 * (
2641 tsos->trackParameters()->parameters()[Trk::qOverP] -
2642 (**trajectory.trackStates().rbegin()).trackParameters()->
2643 parameters()[Trk::qOverP]
2644 );
2645
2646 newmeff->setdelta_p(delta_p);
2647 }
2648 }
2649
2650 trajectory.addMaterialState(std::make_unique<GXFTrackState>(std::move(newmeff), unique_clone(tsos->trackParameters())), index);
2651 }
2652
2653 if (
2656 ) {
2657 const bool isoutlier = tsos->type(TrackStateOnSurface::Outlier) && !cache.m_reintoutl;
2658
2660 cache,
2661 trajectory,
2662 tsos->measurementOnTrack(),
2663 tsos->trackParameters(),
2664 isoutlier,
2665 index
2666 );
2667 }
2668 }
2669
2671 Cache & cache,
2672 GXFTrajectory & trajectory,
2673 const MeasurementBase * measbase,
2674 const TrackParameters * trackpar,
2675 bool isoutlier,
2676 int index
2677 ) const {
2678 const Segment *seg = nullptr;
2679
2682 seg = static_cast<const Segment *>(measbase);
2683 }
2684 }
2685
2686 int imax = 1;
2687
2688 if ((seg != nullptr) && m_decomposesegments) {
2689 imax = (int) seg->numberOfMeasurementBases();
2690 }
2691
2692 for (int i = 0; i < imax; i++) {
2693 const MeasurementBase *measbase2 = ((seg != nullptr) && m_decomposesegments) ? seg->measurement(i) : measbase;
2694 const TrackParameters *newtrackpar = ((seg != nullptr) && m_decomposesegments) ? nullptr : trackpar;
2695 std::unique_ptr<GXFTrackState> ptsos = std::make_unique<GXFTrackState>(
2696 std::unique_ptr<const MeasurementBase>(measbase2->clone()),
2697 std::unique_ptr<const TrackParameters>(newtrackpar != nullptr ? newtrackpar->clone() : nullptr)
2698 );
2699 const Amg::MatrixX & covmat = measbase2->localCovariance();
2700 double sinstereo = 0;
2701 double errors[5];
2702 errors[0] = errors[1] = errors[2] = errors[3] = errors[4] = -1;
2705 //const CompetingRIOsOnTrack *crot = nullptr;
2706 if (!hitid.is_valid()) {
2708 const CompetingRIOsOnTrack *crot = static_cast<const CompetingRIOsOnTrack *>(measbase2);
2709 hitid = crot->rioOnTrack(0).identify();
2710 }
2711 }
2712
2713 bool measphi = false;
2714
2715 if (hitid.is_valid() && measbase2->localParameters().contains(Trk::locX)) {
2716 bool rotated = false;
2717
2718 if (m_DetID->is_indet(hitid) && !m_DetID->is_muon(hitid)) {
2719 if (m_DetID->is_pixel(hitid)) {
2720 hittype = TrackState::Pixel;
2721 } else if (m_DetID->is_sct(hitid)) {
2722 if (covmat.cols() != 1 && covmat(1, 0) != 0) {
2723 rotated = true;
2724 }
2725 hittype = TrackState::SCT;
2726 } else if (m_DetID->is_trt(hitid)) {
2727 hittype = TrackState::TRT;
2728 }
2729 } else { // Muon hit
2730 if (m_DetID->is_rpc(hitid)) {
2731 hittype = TrackState::RPC;
2732 if (measbase->localParameters().parameterKey() != 1) {
2733 ATH_MSG_WARNING("Corrupt RPC hit, skipping it");
2734 continue;
2735 }
2736 } else if (m_DetID->is_mdt(hitid)) {
2737 hittype = TrackState::MDT;
2738 } else if (m_DetID->is_tgc(hitid)) {
2740 rotated = true;
2741 }
2742 hittype = TrackState::TGC;
2743 } else if (m_DetID->is_csc(hitid)) {
2744 hittype = TrackState::CSC;
2745 } else if (m_DetID->is_mm(hitid)) {
2746 hittype = TrackState::MM;
2747 } else if (m_DetID->is_stgc(hitid)) {
2748 hittype = TrackState::STGC;
2749 }
2750 }
2751
2752 if (rotated) {
2753 const auto [covEigenValueSmall, covStereoAngle] = principalComponentAnalysis2x2(covmat);
2754 errors[0] = std::sqrt(covEigenValueSmall);
2755 sinstereo = std::sin(covStereoAngle);
2756 } else {
2757 errors[0] = std::sqrt(covmat(0, 0));
2758 if (hittype == TrackState::Pixel) {
2759 errors[1] = std::sqrt(covmat(1, 1));
2760 }
2761 }
2762 if (
2763 hittype == TrackState::RPC ||
2764 hittype == TrackState::TGC ||
2765 hittype == TrackState::CSC ||
2766 hittype == TrackState::STGC
2767 ) {
2768 const Surface *surf = &measbase2->associatedSurface();
2769 const Amg::Vector3D measdir = surf->transform().rotation().col(0);
2770 const double dotprod1 = measdir.dot(Amg::Vector3D(0, 0, 1));
2771 const double dotprod2 = measdir.dot(Amg::Vector3D(surf->center().x(), surf->center().y(), 0) / surf->center().perp());
2772 if (std::abs(dotprod1) < .5 && std::abs(dotprod2) < .5) {
2773 measphi = true;
2774 }
2775 }
2776 } else {
2777 const Trk::LocalParameters & psmpar = measbase2->localParameters();
2778 // @TODO coverity complains about index shadowing the method argument index
2779 // this is solved by renaming index in this block by param_index
2780 int param_index = 0;
2781 if (psmpar.contains(Trk::locRPhi)) {
2782 errors[0] = std::sqrt(covmat(0, 0));
2783 param_index++;
2784 }
2785
2786 if (psmpar.contains(Trk::locZ)) {
2787 errors[1] = std::sqrt(covmat(param_index, param_index));
2788 param_index++;
2789 }
2790
2791 if (psmpar.contains(Trk::phi)) {
2792 errors[2] = std::sqrt(covmat(param_index, param_index));
2793 param_index++;
2794 }
2795
2796 if (psmpar.contains(Trk::theta)) {
2797 errors[3] = std::sqrt(covmat(param_index, param_index));
2798 param_index++;
2799 }
2800
2801 if (psmpar.contains(Trk::qOverP)) {
2802 errors[4] = std::sqrt(covmat(param_index, param_index));
2803 param_index++;
2804 }
2806 hittype = TrackState::Pseudo;
2807 ATH_MSG_DEBUG("PseudoMeasurement, pos=" << measbase2->globalPosition());
2808 } else if (measbase2->type(Trk::MeasurementBaseType::VertexOnTrack )) {
2809 hittype = TrackState::Vertex;
2810 ATH_MSG_DEBUG("VertexOnTrack, pos=" << measbase2->globalPosition()); // print out the hit type
2811 } else if (measbase2->type(Trk::MeasurementBaseType::Segment )) {
2812 hittype = TrackState::Segment;
2813 ATH_MSG_DEBUG("Segment, pos=" << measbase2->globalPosition()); // print out the hit type
2814 }
2815 }
2816 if (
2817 errors[0] > 0 ||
2818 errors[1] > 0 ||
2819 errors[2] > 0 ||
2820 errors[3] > 0 ||
2821 errors[4] > 0
2822 ) {
2823 ptsos->setMeasurementErrors(errors);
2824 ptsos->setSinStereo(sinstereo);
2825 ptsos->setMeasurementType(hittype);
2826 ptsos->setMeasuresPhi(measphi);
2827
2828 if (isoutlier && !cache.m_reintoutl) {
2829 ptsos->resetStateType(TrackStateOnSurface::Outlier);
2830 }
2831
2832 // @TODO here index really is supposed to refer to the method argument index ?
2833 const bool ok = trajectory.addMeasurementState(std::move(ptsos), index);
2834 if (!ok) {
2835 ATH_MSG_WARNING("Duplicate hit on track");
2836 }
2837 } else {
2838 ATH_MSG_WARNING("Measurement error is zero or negative, drop hit");
2839 }
2840 }
2841 }
2842
2844 Cache & cache,
2845 const Trk::TrackingVolume *tvol
2846 ) const {
2847 if (tvol == nullptr) {
2848 return false;
2849 }
2850
2851 const Trk::BinnedArray < Trk::Layer > *confinedLayers = tvol->confinedLayers();
2852
2853 // loop over confined layers
2854 if (confinedLayers != nullptr) {
2855 // loop over layers
2856 for (const auto & layer : confinedLayers->arrayObjects()) {
2857 // push_back the layer
2858 if (layer != nullptr) {
2859 // get the layerIndex
2860 const Trk::LayerIndex & layIndex = layer->layerIndex();
2861 // skip navigaion layers for the moment
2862
2863 if ((layIndex.value() == 0) || (layer->layerMaterialProperties() == nullptr)) {
2864 continue;
2865 }
2866
2867 const CylinderLayer *cyllay = nullptr;
2868 if (layer->surfaceRepresentation().type() == Trk::SurfaceType::Cylinder) {
2869 cyllay = static_cast<const CylinderLayer *>(layer);
2870 }
2871
2872 const DiscLayer *disclay = nullptr;
2873 if (layer->surfaceRepresentation().type() == Trk::SurfaceType::Disc) {
2874 disclay = static_cast<const DiscLayer *>(layer);
2875 }
2876
2877 if (disclay != nullptr) {
2878 if (disclay->center().z() < 0) {
2879 cache.m_negdiscs.push_back(disclay);
2880 } else {
2881 cache.m_posdiscs.push_back(disclay);
2882 }
2883 } else if (cyllay != nullptr) {
2884 cache.m_barrelcylinders.push_back(cyllay);
2885 } else {
2886 return false;
2887 }
2888 }
2889 }
2890 }
2891
2892 const auto & bsurf = tvol->boundarySurfaces();
2893
2894 for (size_t ib = 0 ; ib < bsurf.size(); ++ib) {
2895 const Layer *layer = bsurf[ib]->surfaceRepresentation().materialLayer();
2896
2897 if (layer == nullptr) continue;
2898
2899 const Trk::LayerIndex & layIndex = layer->layerIndex();
2900
2901 if ((layIndex.value() == 0) || (layer->layerMaterialProperties() == nullptr)) {
2902 continue;
2903 }
2904
2905 const CylinderSurface *cylsurf = nullptr;
2906
2907 if (layer->surfaceRepresentation().type() == Trk::SurfaceType::Cylinder)
2908 cylsurf = static_cast<const CylinderSurface *>(&layer->surfaceRepresentation());
2909
2910 const DiscSurface *discsurf = nullptr;
2911
2912 if (layer->surfaceRepresentation().type() == Trk::SurfaceType::Disc)
2913 discsurf = static_cast<const DiscSurface *>(&layer->surfaceRepresentation());
2914
2915 if (discsurf != nullptr) {
2916 if (
2917 discsurf->center().z() < 0 &&
2918 std::find(cache.m_negdiscs.begin(), cache.m_negdiscs.end(), layer) == cache.m_negdiscs.end()
2919 ) {
2920 cache.m_negdiscs.push_back(layer);
2921 } else if (
2922 discsurf->center().z() > 0 &&
2923 std::find(cache.m_posdiscs.begin(), cache.m_posdiscs.end(), layer) == cache.m_posdiscs.end()
2924 ) {
2925 cache.m_posdiscs.push_back(layer);
2926 }
2927 } else if (
2928 (cylsurf != nullptr) &&
2929 std::find(cache.m_barrelcylinders.begin(), cache.m_barrelcylinders.end(), layer) == cache.m_barrelcylinders.end()
2930 ) {
2931 cache.m_barrelcylinders.push_back(layer);
2932 }
2933
2934 if ((cylsurf == nullptr) && (discsurf == nullptr)) {
2935 return false;
2936 }
2937 }
2938
2939 const TrackingVolumeArray* confinedVolumes = tvol->confinedVolumes();
2940 // get the confined volumes and loop over it -> call recursively
2941 if (confinedVolumes != nullptr) {
2942 for (const auto & volume : confinedVolumes->arrayObjects()) {
2943 if (volume != nullptr) {
2944 const bool ok = processTrkVolume(cache, volume);
2945 if (!ok) {
2946 return false;
2947 }
2948 }
2949 }
2950 }
2951
2952 return true;
2953 }
2954
2955
2956
2957
2958
2959 std::optional<std::pair<Amg::Vector3D, double>>
2961 Cache & cache,
2962 const DiscSurface &surf,
2963 const TrackParameters &parforextrap,
2964 const TrackParameters &refpar2,
2965 const ParticleHypothesis matEffects
2966 ) {
2967 /*
2968 * Please refer to external sources on how to find the intersection between
2969 * a line and a disc.
2970 */
2971 double field[3];
2972 const double * pos = parforextrap.position().data();
2973 const double currentqoverp = (matEffects != Trk::electron) ? parforextrap.parameters()[Trk::qOverP] : refpar2.parameters()[Trk::qOverP];
2974 cache.m_field_cache.getFieldZR(pos, field);
2975 const double sinphi = std::sin(parforextrap.parameters()[Trk::phi0]);
2976 const double cosphi = std::cos(parforextrap.parameters()[Trk::phi0]);
2977 const double sintheta = std::sin(parforextrap.parameters()[Trk::theta]);
2978 const double costheta = std::cos(parforextrap.parameters()[Trk::theta]);
2979 //magnetic field deviation from straight line
2980 //https://cds.cern.ch/record/1281363/files/ATLAS-CONF-2010-072.pdf, equation 1
2981 //converted to MeV and kT
2982 const double r = (std::abs(currentqoverp) > 1e-10) ? -sintheta / (currentqoverp * 300. * field[2]) : 1e6;
2983 const double xc = parforextrap.position().x() - r * sinphi;
2984 const double yc = parforextrap.position().y() + r * cosphi;
2985 const double phi0 = std::atan2(parforextrap.position().y() - yc, parforextrap.position().x() - xc);
2986 const double z0 = parforextrap.position().z();
2987 const double delta_s = (surf.center().z() - z0) / costheta;
2988 const double delta_phi = delta_s * sintheta / r;
2989 const double x = xc + std::abs(r) * std::cos(phi0 + delta_phi);
2990 const double y = yc + std::abs(r) * std::sin(phi0 + delta_phi);
2991 const Amg::Vector3D intersect = Amg::Vector3D(x, y, surf.center().z());
2992 const double perp = intersect.perp();
2993 const DiscBounds *discbounds = static_cast<const DiscBounds *> (&surf.bounds());
2994
2995 if (perp > discbounds->rMax() || perp < discbounds->rMin()) {
2996 return {};
2997 }
2998
2999 const double costracksurf = std::abs(costheta);
3000
3001 return std::make_pair(intersect, costracksurf);
3002 }
3003
3004 std::optional<std::pair<Amg::Vector3D, double>>
3006 Cache & cache,
3007 const CylinderSurface &surf,
3008 const TrackParameters &parforextrap,
3009 const TrackParameters &refpar2,
3010 const ParticleHypothesis matEffects
3011 ) {
3012 /*
3013 * I hope you like trigonometry!
3014 *
3015 * For more information, please find a source elsewhere on finding
3016 * intersections with cylinders.
3017 */
3018 double field[3];
3019 const double * pos = parforextrap.position().data();
3020 const double currentqoverp = (matEffects != Trk::electron) ? parforextrap.parameters()[Trk::qOverP] : refpar2.parameters()[Trk::qOverP];
3021 cache.m_field_cache.getFieldZR(pos, field);
3022 const double sinphi = std::sin(parforextrap.parameters()[Trk::phi0]);
3023 const double cosphi = std::cos(parforextrap.parameters()[Trk::phi0]);
3024 const double sintheta = std::sin(parforextrap.parameters()[Trk::theta]);
3025 const double costheta = std::cos(parforextrap.parameters()[Trk::theta]);
3026 const double tantheta = std::tan(parforextrap.parameters()[Trk::theta]);
3027 const double r = (std::abs(currentqoverp) > 1e-10) ? -sintheta / (currentqoverp * 300. * field[2]) : 1e6;
3028 const double xc = parforextrap.position().x() - r * sinphi;
3029 const double yc = parforextrap.position().y() + r * cosphi;
3030 const double phi0 = std::atan2(parforextrap.position().y() - yc, parforextrap.position().x() - xc);
3031 const double z0 = parforextrap.position().z();
3032 const double d = xc * xc + yc * yc;
3033 const double rcyl = surf.bounds().r();
3034 double mysqrt = ((r + rcyl) * (r + rcyl) - d) * (d - (r - rcyl) * (r - rcyl));
3035
3036 if (mysqrt < 0) {
3037 return {};
3038 }
3039
3040 mysqrt = std::sqrt(mysqrt);
3041 double firstterm = xc / 2 + (xc * (rcyl * rcyl - r * r)) / (2 * d);
3042 double secondterm = (mysqrt * yc) / (2 * d);
3043 const double x1 = firstterm + secondterm;
3044 const double x2 = firstterm - secondterm;
3045 firstterm = yc / 2 + (yc * (rcyl * rcyl - r * r)) / (2 * d);
3046 secondterm = (mysqrt * xc) / (2 * d);
3047 const double y1 = firstterm - secondterm;
3048 const double y2 = firstterm + secondterm;
3049 double x = parforextrap.position().x();
3050 double y = parforextrap.position().y();
3051 const double dist1 = (x - x1) * (x - x1) + (y - y1) * (y - y1);
3052 const double dist2 = (x - x2) * (x - x2) + (y - y2) * (y - y2);
3053
3054 if (dist1 < dist2) {
3055 x = x1;
3056 y = y1;
3057 } else {
3058 x = x2;
3059 y = y2;
3060 }
3061
3062 const double phi1 = std::atan2(y - yc, x - xc);
3063 const double deltaphi = xAOD::P4Helpers::deltaPhi(phi1, phi0);
3064
3065 const double delta_z = r * deltaphi / tantheta;
3066 const double z = z0 + delta_z;
3067
3068 const Amg::Vector3D intersect = Amg::Vector3D(x, y, z);
3069
3070 if (std::abs(z - surf.center().z()) > surf.bounds().halflengthZ()) {
3071 return {};
3072 }
3073
3074 const Amg::Vector3D normal(x, y, 0);
3075 const double phidir = xAOD::P4Helpers::deltaPhi(parforextrap.parameters()[Trk::phi], -deltaphi);
3076
3077 const Amg::Vector3D trackdir(cos(phidir) * sintheta, std::sin(phidir) * sintheta, costheta);
3078
3079 const double costracksurf = std::abs(normal.unit().dot(trackdir));
3080
3081 return std::make_pair(intersect, costracksurf);
3082 }
3083
3085 Cache &cache,
3086 GXFTrajectory &trajectory,
3087 int indexoffset,
3088 std::vector<std::pair<const Layer *, const Layer *>> &layers,
3089 const TrackParameters *refpar,
3090 const TrackParameters *refpar2,
3091 ParticleHypothesis matEffects
3092 ) const {
3093 std::vector<std::unique_ptr<GXFTrackState>> & states = trajectory.trackStates();
3094 std::vector<std::unique_ptr<GXFTrackState>> oldstates = std::move(states);
3095
3096 states.clear();
3097 states.reserve(oldstates.size() + layers.size());
3098
3099 int layerindex = 0;
3100
3101 /*
3102 * First, simply copy any upstream states. We do not need to anything with
3103 * them as they are presumably already fit.
3104 */
3105 for (int i = 0; i <= indexoffset; i++) {
3106 trajectory.addBasicState(std::move(oldstates[i]));
3107 }
3108
3109 const TrackParameters *parforextrap = refpar;
3110
3111 /*
3112 * For non-upstream layers, that is to say layers after the last existing
3113 * material, the logic is not so simple.
3114 */
3115 for (int i = indexoffset + 1; i < (int) oldstates.size(); i++) {
3116 const double rmeas = oldstates[i]->position().perp();
3117 const double zmeas = oldstates[i]->position().z();
3118
3119 /*
3120 * Iterate over layers. Note that this is shared between different track
3121 * states! This makes sense, because the track states are sorted and so
3122 * are the layers. If that sorting is consistent between the two, which
3123 * it should be, this works.
3124 */
3125 while (layerindex < (int) layers.size()) {
3126 Amg::Vector3D intersect;
3127 double costracksurf = 0.0;
3128 const Layer *layer = nullptr;
3129
3130 /*
3131 * Remember how we distinguish between disc and cylinder surfaces: if
3132 * the first element of the pair is not null, then it points to a
3133 * cylinder. If the second element of the pais is not null, then it's a
3134 * disc surface. That is the logic being applied here. Separate
3135 * handling of cylinders and discs.
3136 */
3137 if (layers[layerindex].first != nullptr) {
3138 /*
3139 * First, convert the pointer to a real CylinderSurface pointer.
3140 */
3141 layer = layers[layerindex].first;
3142 const CylinderSurface *cylsurf = static_cast<const CylinderSurface *> (&layer->surfaceRepresentation());
3143
3144 /*
3145 * Check if we have a different set of parameters that make more
3146 * sense. If not, reuse the ones we already had.
3147 */
3148 if (oldstates[i]->trackParameters() != nullptr) {
3149 const double rlayer = cylsurf->bounds().r();
3150 if (std::abs(rmeas - rlayer) < std::abs(parforextrap->position().perp() - rlayer)) {
3151 parforextrap = oldstates[i]->trackParameters();
3152 }
3153 }
3154
3155 /*
3156 * Check if we have an intersection with this layer. If so, break out
3157 * of this loop, we have what we need. Otherwise, go to the next
3158 * layer and try again.
3159 */
3160 if (auto res = addMaterialFindIntersectionCyl(cache, *cylsurf, *parforextrap, *refpar2, matEffects)) {
3161 std::tie(intersect, costracksurf) = res.value();
3162 } else {
3163 layerindex++;
3164 continue;
3165 }
3166
3167 if (cylsurf->bounds().r() > rmeas) break;
3168 } else if (layers[layerindex].second != nullptr) {
3169 /*
3170 * The logic for disc surfaces is essentially identical to the logic
3171 * for cylinder surfaces. You'll find comments for that just a dozen
3172 * lines up.
3173 */
3174 layer = layers[layerindex].second;
3175 const DiscSurface *discsurf = static_cast<const DiscSurface *> (&layer->surfaceRepresentation());
3176
3177 if (oldstates[i]->trackParameters() != nullptr) {
3178 const double zlayer = discsurf->center().z();
3179 if (std::abs(zmeas - zlayer) < std::abs(parforextrap->position().z() - zlayer)) {
3180 parforextrap = oldstates[i]->trackParameters();
3181 }
3182 }
3183
3184 if (auto res = addMaterialFindIntersectionDisc(cache, *discsurf, *parforextrap, *refpar2, matEffects)) {
3185 std::tie(intersect, costracksurf) = res.value();
3186 } else {
3187 layerindex++;
3188 continue;
3189 }
3190
3191 if (std::abs(discsurf->center().z()) > std::abs(zmeas)) break;
3192 } else {
3193 throw std::logic_error("Unhandled surface.");
3194 }
3195
3196 /*
3197 * Grab the material properties from our layer. If there are none, just
3198 * go to the next layer.
3199 */
3200 const MaterialProperties *matprop = layer->layerMaterialProperties()->fullMaterial(intersect);
3201 if (matprop == nullptr) {
3202 layerindex++;
3203 continue;
3204 }
3205
3206 /*
3207 * Convert the material properties into the internal representation of
3208 * material effects.
3209 */
3210 const double X0 = matprop->thicknessInX0();
3211 const double currentqoverp = (matEffects != Trk::electron)
3212 ? parforextrap->parameters()[Trk::qOverP]
3213 : refpar2->parameters()[Trk::qOverP];
3214 const double actualx0 = X0 / costracksurf;
3215 const double de = -std::abs(
3216 (matprop->thickness() / costracksurf) *
3217 m_elosstool->dEdX(
3218 *matprop,
3219 (m_p != 0.0 ? std::abs(m_p) : std::abs(1. / currentqoverp)),
3220 matEffects));
3221 const double sintheta = std::sin(parforextrap->parameters()[Trk::theta]);
3222 const double sigmascat = std::sqrt(m_scattool->sigmaSquare(
3223 *matprop,
3224 (m_p != 0.0 ? std::abs(m_p) : std::abs(1. / currentqoverp)),
3225 1. / costracksurf,
3226 matEffects));
3227
3228 std::unique_ptr<GXFMaterialEffects> meff = std::make_unique<GXFMaterialEffects>();
3229 meff->setDeltaE(de);
3230 meff->setScatteringSigmas(sigmascat / sintheta, sigmascat);
3231 meff->setX0(actualx0);
3232 meff->setSurface(&layer->surfaceRepresentation());
3233 meff->setMaterialProperties(matprop);
3234
3235 /*
3236 * If we have an electron, or if so configured, calculate energy loss
3237 * as well.
3238 */
3239 std::unique_ptr<EnergyLoss> eloss;
3240
3241 if (cache.m_fiteloss || (matEffects == electron && cache.m_asymeloss)) {
3242 eloss = std::make_unique<EnergyLoss>(m_elosstool->energyLoss(
3243 *matprop,
3244 (m_p != 0.0 ? std::abs(m_p) : std::abs(1. / currentqoverp)),
3245 1. / costracksurf,
3247 matEffects
3248 ));
3249 if (eloss != nullptr) {
3250 meff->setSigmaDeltaE(eloss->sigmaDeltaE());
3251 }
3252 }
3253
3254 if (matEffects == electron && cache.m_asymeloss) {
3255 meff->setDeltaE(-5);
3256 if (trajectory.numberOfTRTHits() == 0) {
3257 meff->setScatteringSigmas(0, 0);
3258 }
3259
3260 meff->setSigmaDeltaE(50);
3261 if (eloss != nullptr) {
3262 meff->setSigmaDeltaEPos(eloss->sigmaPlusDeltaE());
3263 meff->setSigmaDeltaENeg(eloss->sigmaMinusDeltaE());
3264 }
3265 }
3266
3268 "X0: " << meff->x0() << " qoverp: " << currentqoverp <<
3269 " sigmascat " << meff->sigmaDeltaTheta() <<" eloss: " << meff->deltaE() <<
3270 " sigma eloss: " << meff->sigmaDeltaE()
3271 );
3272
3273 /*
3274 * Create a new track state in the internal representation and load it
3275 * with any and all information we might have.
3276 */
3277 std::unique_ptr<GXFTrackState> matstate = std::make_unique<GXFTrackState>(
3278 std::move(meff),
3279 std::unique_ptr<const TrackParameters>()
3280 );
3281 matstate->setPosition(intersect);
3282 trajectory.addMaterialState(std::move(matstate));
3283
3284 /*
3285 * We're done on this layer, so the next state will go to the next
3286 * layer.
3287 */
3288 layerindex++;
3289 }
3290
3291 trajectory.addBasicState(std::move(oldstates[i]));
3292 }
3293 }
3294
3296 Cache & cache,
3297 std::vector<std::pair<const Layer *, const Layer *>> & layers,
3298 std::vector<std::pair<const Layer *, const Layer *>> & upstreamlayers,
3299 const std::vector<std::unique_ptr<GXFTrackState>> & oldstates,
3300 GXFTrackState & firstsistate,
3301 GXFTrackState & lastsistate,
3302 const TrackParameters *refpar,
3303 bool hasmat
3304 ) {
3305 /*
3306 * Reserve some arbitrary number of layers in the output vectors.
3307 */
3308 upstreamlayers.reserve(5);
3309 layers.reserve(30);
3310
3311 /*
3312 * Gather a bunch of numbers from the parameters. Someties we need to grab
3313 * them from the first silicon state, sometimes from the last.
3314 */
3315 const double firstz = firstsistate.trackParameters()->position().z();
3316 const double firstr = firstsistate.trackParameters()->position().perp();
3317 const double firstz2 = hasmat ? lastsistate.trackParameters()->position().z() : firstsistate.trackParameters()->position().z();
3318 const double firstr2 = hasmat ? lastsistate.trackParameters()->position().perp() : firstsistate.trackParameters()->position().perp();
3319
3320 GXFTrackState *firststate = oldstates.front().get();
3321 GXFTrackState *laststate = oldstates.back().get();
3322
3323 /*
3324 * This number is particularly interesting, as it determines which side we
3325 * need to look at in regards to the disc layers.
3326 */
3327 const double lastz = laststate->position().z();
3328 const double lastr = laststate->position().perp();
3329
3330 const Layer *startlayer = firststate->associatedSurface().associatedLayer();
3331 const Layer *startlayer2 = hasmat ? lastsistate.associatedSurface().associatedLayer() : nullptr;
3332 const Layer *endlayer = laststate->associatedSurface().associatedLayer();
3333
3334 const double tantheta = std::tan(refpar->parameters()[Trk::theta]);
3335 const double slope = (tantheta != 0) ? 1 / tantheta : 0; // (lastz-firstz)/(lastr-firstr);
3336
3337 /*
3338 * First, we will grab our disc layers.
3339 */
3340 if (slope != 0) {
3341 std::vector < const Layer *>::const_iterator it;
3342 std::vector < const Layer *>::const_iterator itend;
3343
3344 /*
3345 * If we're on the positive z-side of the detector, we will iterate over
3346 * the positive discs. Otherwise, we will need to iterate over the
3347 * negative discs.
3348 */
3349 if (lastz > 0) {
3350 it = cache.m_posdiscs.begin();
3351 itend = cache.m_posdiscs.end();
3352 } else {
3353 it = cache.m_negdiscs.begin();
3354 itend = cache.m_negdiscs.end();
3355 }
3356
3357 /*
3358 * Iterate over our disc layers.
3359 */
3360 for (; it != itend; ++it) {
3361 /*
3362 * If we've overshot the last hit in our track, we don't need to look
3363 * at any further layers. We're done!
3364 */
3365 if (std::abs((*it)->surfaceRepresentation().center().z()) > std::abs(lastz)) {
3366 break;
3367 }
3368
3369 /*
3370 * Grab the bounds from the layer, which is a more useful kind of
3371 * object that allows us to do some geometric calculations.
3372 */
3373 const DiscBounds *discbounds = static_cast<const DiscBounds *> (&(*it)->surfaceRepresentation().bounds());
3374
3375 /*
3376 * Ensure that we've actually hit the layer!
3377 */
3378 if (discbounds->rMax() < firstr || discbounds->rMin() > lastr) {
3379 continue;
3380 }
3381
3382 const double rintersect = firstr + ((*it)->surfaceRepresentation().center().z() - firstz) / slope;
3383
3384 if (
3385 rintersect < discbounds->rMin() - 50 ||
3386 rintersect > discbounds->rMax() + 50
3387 ) {
3388 continue;
3389 }
3390
3391 /*
3392 * We also do not need to consider the last layer. If all goes well,
3393 * the next loop will immediately break because it will be an
3394 * overshoot.
3395 */
3396 if ((*it) == endlayer) {
3397 continue;
3398 }
3399
3400 /*
3401 * If this layer lies before the first hit, it's an upstream hit and we
3402 * add it to the upstream layer vector.
3403 *
3404 * Notice how we add this layer on the right side of the pair, that's
3405 * the convention. Discs to right, cylinders go left.
3406 */
3407 if (
3408 std::abs((*it)->surfaceRepresentation().center().z()) < std::abs(firstz) ||
3409 (*it) == startlayer
3410 ) {
3411 upstreamlayers.emplace_back((Layer *) nullptr, (*it));
3412 }
3413
3414 /*
3415 * Otherwise, it's a normal layer. Add it.
3416 */
3417 if (
3418 (*it) != startlayer &&
3419 (std::abs((*it)->surfaceRepresentation().center().z()) > std::abs(firstz2) ||
3420 (*it) == startlayer2)
3421 ) {
3422 layers.emplace_back((Layer *) nullptr, (*it));
3423 }
3424 }
3425 }
3426
3427 /*
3428 * Now, we add the barrel cylinder layers.
3429 */
3430 for (const auto *barrelcylinder : cache.m_barrelcylinders) {
3431 /*
3432 * Check for overshoots and reject them.
3433 */
3434 if (barrelcylinder->surfaceRepresentation().bounds().r() > lastr) {
3435 break;
3436 }
3437
3438 /*
3439 * Confirm intersection with the layer.
3440 */
3441 const double zintersect = firstz + (barrelcylinder->surfaceRepresentation().bounds().r() - firstr) * slope;
3442
3443 if (std::abs(zintersect - barrelcylinder->surfaceRepresentation().center().z()) >
3444 ((const CylinderSurface*)(&barrelcylinder->surfaceRepresentation()))->bounds().halflengthZ() + 50) {
3445 continue;
3446 }
3447
3448 if (barrelcylinder == endlayer) {
3449 continue;
3450 }
3451
3452 /*
3453 * Same as with the discs, add the layers to the output vectors.
3454 */
3455 if (barrelcylinder->surfaceRepresentation().bounds().r() < firstr ||
3456 barrelcylinder == startlayer) {
3457 upstreamlayers.emplace_back(barrelcylinder, (Layer*)nullptr);
3458 }
3459
3460 if (barrelcylinder != startlayer &&
3461 (barrelcylinder->surfaceRepresentation().bounds().r() > firstr2 ||
3462 barrelcylinder == startlayer2)) {
3463 layers.emplace_back(barrelcylinder, (Layer*)nullptr);
3464 }
3465 }
3466
3467 /*
3468 * Sort the layers such that they are in the right order, from close to far
3469 * in respect to the experiment center.
3470 */
3471 std::sort(layers.begin(), layers.end(), GXF::LayerSort());
3472 std::sort(upstreamlayers.begin(), upstreamlayers.end(), GXF::LayerSort());
3473 }
3474
3476 const EventContext& ctx,
3477 Cache & cache,
3478 GXFTrajectory & trajectory,
3479 const TrackParameters * refpar2,
3480 ParticleHypothesis matEffects
3481 ) const {
3482 /*
3483 * Ensure that the cache contains a valid tracking geometry that we can
3484 * use.
3485 */
3486 const bool caloEntranceIsValid = ensureValidEntranceCalo(ctx, cache);
3487 if (!caloEntranceIsValid) {
3488 return;
3489 }
3490
3491 /*
3492 * If we have not yet set the discs on either side of the detector as well
3493 * as the barrel layers, do so now.
3494 */
3495 if (
3496 cache.m_negdiscs.empty() &&
3497 cache.m_posdiscs.empty() &&
3498 cache.m_barrelcylinders.empty()
3499 ) {
3500 /*
3501 * Attempt to add the layer information to the cache using the previously
3502 * selected tracking volume.
3503 */
3504 const bool ok = processTrkVolume(cache, cache.m_caloEntrance);
3505
3506 /*
3507 * If this process somehow fails, we cannot use the fast material adding
3508 * algorithm and we must fall back to the slow version. As far as I know
3509 * this doesn't really happen.
3510 */
3511 if (!ok) {
3512 ATH_MSG_DEBUG("Falling back to slow material collection");
3513 cache.m_fastmat = false;
3514 addMaterial(ctx, cache, trajectory, refpar2, matEffects);
3515 return;
3516 }
3517
3518 /*
3519 * Sort the discs and barrel layers such that they are in the right
3520 * order. What the right order is in this case is defined a bit above
3521 * this code, in the GXF::LayerSort2 class. Should be in increasing order
3522 * of distance from the detector center.
3523 */
3524 std::stable_sort(cache.m_negdiscs.begin(), cache.m_negdiscs.end(), GXF::LayerSort2());
3525 std::stable_sort(cache.m_posdiscs.begin(), cache.m_posdiscs.end(), GXF::LayerSort2());
3526 std::stable_sort(cache.m_barrelcylinders.begin(), cache.m_barrelcylinders.end(), GXF::LayerSort2());
3527 }
3528
3529 const TrackParameters *refpar = refpar2;
3530 bool hasmat = false;
3531 int indexoffset = 0;
3532 int lastmatindex = 0;
3533 std::vector<std::unique_ptr<GXFTrackState>> & oldstates = trajectory.trackStates();
3534
3535 GXFTrackState *firstsistate = nullptr;
3536 GXFTrackState *lastsistate = nullptr;
3537
3538 /*
3539 * This loop serves several purposes in one, because it's very efficient:
3540 *
3541 * 1. It detects whether there are already any materials on this track, and
3542 * if so where they are.
3543 * 2. It determines what the first and last silicon hits are.
3544 * 3. It calculates trackparameters for any states that might not have them
3545 * for whatever reason.
3546 */
3547 for (int i = 0; i < (int) oldstates.size(); i++) {
3548 if (oldstates[i]->materialEffects() != nullptr) {
3549 hasmat = true;
3550 lastmatindex = i;
3551 }
3552
3553 if (
3554 oldstates[i]->measurementType() == TrackState::Pixel ||
3555 oldstates[i]->measurementType() == TrackState::SCT
3556 ) {
3557 if (firstsistate == nullptr) {
3558 if (oldstates[i]->trackParameters() == nullptr) {
3559 std::unique_ptr<const TrackParameters> tmppar(m_propagator->propagateParameters(
3560 ctx,
3561 *refpar,
3562 oldstates[i]->associatedSurface(),
3564 false,
3565 trajectory.m_fieldprop,
3567 ));
3568
3569 if (tmppar == nullptr) return;
3570
3571 oldstates[i]->setTrackParameters(std::move(tmppar));
3572 }
3573 firstsistate = oldstates[i].get();
3574 }
3575 lastsistate = oldstates[i].get();
3576 }
3577 }
3578
3579 /*
3580 * Only happens when there are no tracks, and that shouldn't happen in the
3581 * first place.
3582 */
3583 if (lastsistate == nullptr) {
3584 throw std::logic_error("No track state");
3585 }
3586
3587 /*
3588 * Also try to generate a set of track parameters for the last silicon hit
3589 * if it doesn't have any. I don't really know when that would happen, but
3590 * I suppose it's possible. Anything is possible, if you believe hard
3591 * enough.
3592 */
3593 if (lastsistate->trackParameters() == nullptr) {
3594 std::unique_ptr<const TrackParameters> tmppar(m_propagator->propagateParameters(
3595 ctx,
3596 *refpar,
3597 lastsistate->associatedSurface(),
3598 alongMomentum, false,
3599 trajectory.m_fieldprop,
3601 ));
3602
3603 if (tmppar == nullptr) return;
3604
3605 lastsistate->setTrackParameters(std::move(tmppar));
3606 }
3607
3608 /*
3609 * If we have found any materials on the track, we've presumably already
3610 * done a fit for that part of the track, so the reference parameters are
3611 * either the first or last silicon state's parameters.
3612 */
3613 if (hasmat) {
3614 refpar = lastsistate->trackParameters();
3615 indexoffset = lastmatindex;
3616 } else {
3617 refpar = firstsistate->trackParameters();
3618 }
3619
3620 /*
3621 * These vectors will hold the layers. The types here are a little bit
3622 * strange, but the idea is that the right member is a disc surface and the
3623 * left member is a cylindrical surface. Could be more elegantly done using
3624 * polymorphism.
3625 *
3626 * The upstream layers may already be filled due to previous fits.
3627 *
3628 * TODO: Use polymorphism to get rid of these strange types.
3629 */
3630 std::vector<std::pair<const Layer *, const Layer *>> layers;
3631 std::vector<std::pair<const Layer *, const Layer *>> & upstreamlayers = trajectory.upstreamMaterialLayers();
3632
3633 /*
3634 * Fill the aforementioned layer vectors with layers.
3635 */
3636 addMaterialGetLayers(cache, layers, upstreamlayers, oldstates, *firstsistate, *lastsistate, refpar, hasmat);
3637
3638 /*
3639 * Finally, use that layer information to actually add states to the track.
3640 */
3641 addMaterialUpdateTrajectory(cache, trajectory, indexoffset, layers, refpar, refpar2, matEffects);
3642 }
3643
3645 const EventContext& ctx,
3646 Cache & cache,
3647 GXFTrajectory & trajectory,
3648 const TrackParameters * refpar2,
3649 ParticleHypothesis matEffects
3650 ) const {
3651 if (refpar2 == nullptr) {
3652 return;
3653 }
3654 const MeasurementBase *firstmuonhit = nullptr;
3655 const MeasurementBase *lastmuonhit = nullptr;
3656 const MeasurementBase *firstidhit =
3657 nullptr;
3658 const MeasurementBase *lastidhit = nullptr;
3659 const MeasurementBase *firsthit = nullptr;
3660 const MeasurementBase *lasthit = nullptr;
3661 std::vector<std::unique_ptr<GXFTrackState>> & states = trajectory.trackStates();
3662 std::vector<std::unique_ptr<GXFTrackState>> matstates;
3663 std::unique_ptr< const std::vector < const TrackStateOnSurface *>,
3664 void (*)(const std::vector<const TrackStateOnSurface *> *) >
3666 bool matvec_used=false;
3667 std::unique_ptr<TrackParameters> startmatpar1;
3668 std::unique_ptr<TrackParameters> startmatpar2;
3669 const TrackParameters *firstidpar = nullptr;
3670 const TrackParameters *lastidpar = nullptr;
3671 const TrackParameters *firstsiliconpar = nullptr;
3672 const TrackParameters *lastsiliconpar = nullptr;
3673 const TrackParameters *firstmatpar = nullptr;
3674 const TrackParameters *firstcalopar = nullptr;
3675 const TrackParameters *lastcalopar = nullptr;
3676 const TrackParameters *firstmuonpar = nullptr;
3677 const TrackParameters *lastmuonpar = nullptr;
3678
3679 int npseudomuon1 = 0;
3680 int npseudomuon2 = 0;
3681
3682 for (auto & state : states) {
3683 TrackState::MeasurementType const meastype = state->measurementType();
3684 const TrackParameters *tp = state->trackParameters();
3685 GXFMaterialEffects *meff = state->materialEffects();
3686
3687 if (meastype == TrackState::Pseudo) {
3688 if (firstidhit == nullptr) {
3689 npseudomuon1++;
3690 } else {
3691 npseudomuon2++;
3692 }
3693 continue;
3694 }
3695
3696 if (state->getStateType(TrackStateOnSurface::Measurement) || state->getStateType(TrackStateOnSurface::Outlier)) {
3697 if (firsthit == nullptr) {
3698 firsthit = state->measurement();
3699 if (cache.m_acceleration) {
3700 if (tp == nullptr) {
3701 tp = m_extrapolator->extrapolate(
3702 ctx,
3703 *refpar2,
3704 state->associatedSurface(),
3706 false,
3707 matEffects
3708 ).release();
3709
3710 if (tp == nullptr) {
3711 return;
3712 }
3713
3714 state->setTrackParameters(std::unique_ptr<const TrackParameters>(tp));
3715 }
3716 // When acceleration is enabled, material collection starts from first hit
3717 refpar2 = tp;
3718 }
3719 }
3720 lasthit = state->measurement();
3721 if (
3722 meastype == TrackState::Pixel ||
3723 meastype == TrackState::SCT ||
3724 meastype == TrackState::TRT
3725 ) {
3726 if (firstidhit == nullptr) {
3727 firstidhit = state->measurement();
3728 }
3729
3730 if ((firstidpar == nullptr) && (tp != nullptr)) {
3731 firstidpar = tp;
3732 }
3733
3734 lastidhit = state->measurement();
3735 if (tp != nullptr) {
3736 lastidpar = tp;
3737 }
3738
3739 if ((tp != nullptr) && meastype != TrackState::TRT) {
3740 if (firstsiliconpar == nullptr) {
3741 firstsiliconpar = tp;
3742 }
3743 lastsiliconpar = tp;
3744 }
3745 }
3746
3747 if (
3748 meastype == TrackState::RPC ||
3749 meastype == TrackState::CSC ||
3750 meastype == TrackState::TGC ||
3751 meastype == TrackState::MDT ||
3752 meastype == TrackState::MM ||
3753 meastype == TrackState::STGC
3754 ) {
3755 if (firstmuonhit == nullptr) {
3756 firstmuonhit = state->measurement();
3757 if (tp != nullptr) {
3758 firstmuonpar = tp;
3759 }
3760 }
3761 lastmuonhit = state->measurement();
3762 if (tp != nullptr) {
3763 lastmuonpar = tp;
3764 }
3765 }
3766 }
3767 if (state->getStateType(TrackStateOnSurface::Scatterer) || state->getStateType(TrackStateOnSurface::BremPoint)) {
3768 if (meff->deltaE() == 0) {
3769 if (firstcalopar == nullptr) {
3770 firstcalopar = state->trackParameters();
3771 }
3772 lastcalopar = state->trackParameters();
3773 }
3774 if (firstmatpar == nullptr) {
3775 firstmatpar = state->trackParameters();
3776 }
3777 }
3778 }
3779
3780 std::unique_ptr<TrackParameters> refpar;
3781 AmgVector(5) newpars = refpar2->parameters();
3782
3783 if (trajectory.m_straightline && m_p != 0) {
3784 newpars[Trk::qOverP] = 1 / m_p;
3785 }
3786
3788 newpars[0], newpars[1], newpars[2], newpars[3], newpars[4], std::nullopt
3789 );
3790
3791 if (firstmatpar != nullptr) {
3792 startmatpar1 = unique_clone(firstsiliconpar);
3793 startmatpar2 = unique_clone(lastsiliconpar);
3794 }
3795
3796 if ((startmatpar1 == nullptr) || ((firstidhit != nullptr) && (firstmuonhit != nullptr))) {
3797 startmatpar1 = unique_clone(refpar);
3798 startmatpar2 = unique_clone(refpar);
3799
3800 const double mass = trajectory.mass();
3801 if (mass > 200 * MeV) {
3802 const AmgVector(5) & newpars = startmatpar2->parameters();
3803 const double oldp = std::abs(1 / newpars[Trk::qOverP]);
3804 const double sign = (newpars[Trk::qOverP] < 0) ? -1 : 1;
3805
3806 startmatpar2 = startmatpar2->associatedSurface().createUniqueTrackParameters(
3807 newpars[0], newpars[1], newpars[2], newpars[3],
3808 sign / std::sqrt(oldp * oldp + 2 * 100 * MeV * std::sqrt(oldp * oldp + mass * mass) + 100 * MeV * 100 * MeV),
3809 std::nullopt
3810 );
3811 }
3812 } else if (trajectory.m_straightline && m_p != 0) {
3813 AmgVector(5) newpars = startmatpar1->parameters();
3814 newpars[Trk::qOverP] = 1 / m_p;
3815
3816 startmatpar1 = startmatpar1->associatedSurface().createUniqueTrackParameters(
3817 newpars[0], newpars[1], newpars[2], newpars[3], newpars[4], std::nullopt
3818 );
3819
3820 newpars = startmatpar2->parameters();
3821 newpars[Trk::qOverP] = 1 / m_p;
3822
3823 startmatpar2 = startmatpar2->associatedSurface().createUniqueTrackParameters(
3824 newpars[0], newpars[1], newpars[2], newpars[3], newpars[4], std::nullopt
3825 );
3826 }
3827
3828 if ((firstidhit != nullptr) && trajectory.numberOfSiliconHits() > 0 && cache.m_idmat) {
3829
3831 refpar->position(),
3832 refpar->momentum().unit()
3833 );
3834
3835 const double distance = getDistance(distsol);
3836
3837 if (distance < 0 && distsol.numberOfSolutions() > 0 && !cache.m_acceleration) {
3838 ATH_MSG_DEBUG("Obtaining upstream layers from Extrapolator");
3839
3840 const Surface *destsurf = &firstidhit->associatedSurface();
3841 std::unique_ptr<const TrackParameters> tmppar;
3842
3843 if (firstmuonhit != nullptr) {
3844 const bool caloEntranceIsValid = ensureValidEntranceCalo(ctx, cache);
3845 if (caloEntranceIsValid) {
3846 tmppar = m_extrapolator->extrapolateToVolume(ctx,
3847 *startmatpar1,
3848 *cache.m_caloEntrance,
3851
3852 if (tmppar != nullptr) {
3853 destsurf = &tmppar->associatedSurface();
3854 }
3855 }
3856 }
3857
3858 if (matvec_used) cache.m_matTempStore.push_back( std::move(matvec) );
3859 matvec.reset( m_extrapolator->extrapolateM(ctx,
3860 *startmatpar1,
3861 *destsurf,
3863 false, matEffects) );
3864 matvec_used=false;
3865
3866 if (matvec && !matvec->empty()) {
3867 for (int i = (int)matvec->size() - 1; i > -1; i--) {
3868 const MaterialEffectsBase *meb = (*matvec)[i]->materialEffectsOnTrack();
3869 if (meb) {
3871 const MaterialEffectsOnTrack *meot = static_cast < const MaterialEffectsOnTrack * >(meb);
3872 std::unique_ptr<GXFMaterialEffects> meff = std::make_unique<GXFMaterialEffects>(*meot);
3873 const TrackParameters * newpars = (*matvec)[i]->trackParameters() != nullptr ? (*matvec)[i]->trackParameters()->clone() : nullptr;
3874 meff->setSigmaDeltaE(0);
3875 matstates.push_back(std::make_unique<GXFTrackState>(
3876 std::move(meff),
3877 std::unique_ptr<const TrackParameters>(newpars)
3878 ));
3879 matvec_used=true;
3880 }
3881 }
3882 }
3883 }
3884 }
3885 }
3886
3887 if ((lastidhit != nullptr) && trajectory.numberOfSiliconHits() > 0 && cache.m_idmat) {
3889 refpar->position(),
3890 refpar->momentum().unit()
3891 );
3892
3893 const double distance = getDistance(distsol);
3894
3895 if (distance > 0 && distsol.numberOfSolutions() > 0) {
3896 ATH_MSG_DEBUG("Obtaining downstream ID layers from Extrapolator");
3897 const Surface *destsurf = &lastidhit->associatedSurface();
3898 std::unique_ptr<const TrackParameters> tmppar;
3899 std::unique_ptr<Surface> calosurf;
3900 if (firstmuonhit != nullptr) {
3901 const bool caloEntranceIsValid = ensureValidEntranceCalo(ctx, cache);
3902 if (caloEntranceIsValid) {
3903 tmppar = m_extrapolator->extrapolateToVolume(ctx,
3904 *startmatpar2,
3905 *cache.m_caloEntrance,
3908 }
3909
3910 if (tmppar != nullptr) {
3911 const CylinderSurface *cylcalosurf = nullptr;
3912
3913 if (tmppar->associatedSurface().type() == Trk::SurfaceType::Cylinder)
3914 cylcalosurf = static_cast<const CylinderSurface *>(&tmppar->associatedSurface());
3915
3916 const DiscSurface *disccalosurf = nullptr;
3917
3918 if (tmppar->associatedSurface().type() == Trk::SurfaceType::Disc)
3919 disccalosurf = static_cast<const DiscSurface *>(&tmppar->associatedSurface());
3920
3921 if (cylcalosurf != nullptr) {
3922 Amg::Transform3D const trans = Amg::Transform3D(cylcalosurf->transform());
3923 const CylinderBounds & cylbounds = cylcalosurf->bounds();
3924 const double radius = cylbounds.r();
3925 const double hlength = cylbounds.halflengthZ();
3926 calosurf = std::make_unique<CylinderSurface>(trans, radius - 1, hlength);
3927 } else if (disccalosurf != nullptr) {
3928 const double newz = (
3929 disccalosurf->center().z() > 0 ?
3930 disccalosurf->center().z() - 1 :
3931 disccalosurf->center().z() + 1
3932 );
3933
3934 const Amg::Vector3D newpos(
3935 disccalosurf->center().x(),
3936 disccalosurf->center().y(),
3937 newz
3938 );
3939
3940 Amg::Transform3D trans = (disccalosurf->transform());
3941 trans.translation() << newpos;
3942
3943 const DiscBounds *discbounds = static_cast<const DiscBounds *>(&disccalosurf->bounds());
3944 const double rmin = discbounds->rMin();
3945 const double rmax = discbounds->rMax();
3946 calosurf = std::make_unique<DiscSurface>(trans, rmin, rmax);
3947 }
3948 destsurf = calosurf.release();
3949 }
3950 }
3951
3952 if (matvec_used) cache.m_matTempStore.push_back( std::move(matvec) );
3953 matvec.reset(m_extrapolator->extrapolateM(
3954 ctx, *startmatpar2, *destsurf, alongMomentum, false, matEffects));
3955 matvec_used = false;
3956
3957 if (matvec && !matvec->empty()) {
3958 for (const auto & i : *matvec) {
3959 const Trk::MaterialEffectsBase * meb = i->materialEffectsOnTrack();
3960
3961 if (meb) {
3963 const MaterialEffectsOnTrack *meot = static_cast<const MaterialEffectsOnTrack *>(meb);
3964 std::unique_ptr<GXFMaterialEffects> meff = std::make_unique<GXFMaterialEffects>(*meot);
3965 if (cache.m_fiteloss && (meot->energyLoss() != nullptr)) {
3966 meff->setSigmaDeltaE(meot->energyLoss()->sigmaDeltaE());
3967 }
3968
3969 if (matEffects == electron && cache.m_asymeloss) {
3970 meff->setDeltaE(-5);
3971
3972 if (trajectory.numberOfTRTHits() == 0) {
3973 meff->setScatteringSigmas(0, 0);
3974 }
3975
3976 meff->setSigmaDeltaE(50);
3977 }
3978
3979 const TrackParameters * newparams = i->trackParameters() != nullptr ? i->trackParameters()->clone() : nullptr;
3980
3981 matstates.push_back(std::make_unique<GXFTrackState>(
3982 std::move(meff),
3983 std::unique_ptr<const TrackParameters>(newparams)
3984 ));
3985 matvec_used=true;
3986 }
3987 }
3988 }
3989 } else {
3990 ATH_MSG_WARNING("No material layers collected from Extrapolator");
3991 }
3992 }
3993 }
3994
3995 if (cache.m_calomat && (firstmuonhit != nullptr) && (firstidhit != nullptr)) {
3996 const IPropagator *prop = &*m_propagator;
3997
3998 std::vector<MaterialEffectsOnTrack> calomeots = m_calotool->extrapolationSurfacesAndEffects(
3999 *m_navigator->highestVolume(ctx),
4000 *prop,
4001 *lastidpar,
4002 firstmuonhit->associatedSurface(),
4004 muon
4005 );
4006
4007 if (calomeots.empty()) {
4008 ATH_MSG_WARNING("No material layers collected in calorimeter");
4009 return;
4010 }
4011
4012 std::unique_ptr<const TrackParameters> prevtrackpars = unique_clone(lastidpar);
4013 if (lasthit == lastmuonhit) {
4014 for (int i = 0; i < (int) calomeots.size(); i++) {
4015 const PropDirection propdir = alongMomentum;
4016
4017 std::unique_ptr<const TrackParameters> layerpar(m_propagator->propagateParameters(
4018 ctx,
4019 *prevtrackpars,
4020 calomeots[i].associatedSurface(),
4021 propdir,
4022 false,
4023 trajectory.m_fieldprop,
4025 ));
4026
4027 if (layerpar == nullptr) {
4028 cache.incrementFitStatus(S_PROPAGATION_FAIL);
4029 return;
4030 }
4031
4032 std::unique_ptr<GXFMaterialEffects> meff = std::make_unique<GXFMaterialEffects>(calomeots[i]);
4033
4034 if (i == 2) {
4035 lastcalopar = layerpar.get();
4036 }
4037
4038 if (i == 1) {
4039 const double qoverp = layerpar->parameters()[Trk::qOverP];
4040 double qoverpbrem = 0;
4041
4042 if (
4043 npseudomuon2 < 2 &&
4044 (firstmuonpar != nullptr) &&
4045 std::abs(firstmuonpar->parameters()[Trk::qOverP]) > 1.e-9
4046 ) {
4047 qoverpbrem = firstmuonpar->parameters()[Trk::qOverP];
4048 } else {
4049 const double sign = (qoverp > 0) ? 1 : -1;
4050 qoverpbrem = sign / (1 / std::abs(qoverp) - std::abs(calomeots[i].energyLoss()->deltaE()));
4051 }
4052
4053 const AmgVector(5) & newpar = layerpar->parameters();
4054
4055 layerpar = layerpar->associatedSurface().createUniqueTrackParameters(
4056 newpar[0], newpar[1], newpar[2], newpar[3], qoverpbrem, std::nullopt
4057 );
4058 meff->setdelta_p(1000 * (qoverpbrem - qoverp));
4059 }
4060
4061 matstates.push_back(std::make_unique<GXFTrackState>(
4062 std::move(meff),
4063 std::unique_ptr<const TrackParameters>(layerpar != nullptr ? layerpar->clone() : nullptr)
4064 ));
4065 prevtrackpars = std::move(layerpar);
4066 }
4067 }
4068
4069 if (
4070 firsthit == firstmuonhit &&
4071 (!cache.m_getmaterialfromtrack || lasthit == lastidhit)
4072 ) {
4073 prevtrackpars = unique_clone(firstidpar);
4074 for (int i = 0; i < (int) calomeots.size(); i++) {
4075 const PropDirection propdir = oppositeMomentum;
4076 std::unique_ptr<const TrackParameters> layerpar(m_propagator->propagateParameters(
4077 ctx,
4078 *prevtrackpars,
4079 calomeots[i].associatedSurface(),
4080 propdir,
4081 false,
4082 trajectory.m_fieldprop,
4084 ));
4085
4086 if (layerpar == nullptr) {
4087 cache.incrementFitStatus(S_PROPAGATION_FAIL);
4088 return;
4089 }
4090
4091 std::unique_ptr<GXFMaterialEffects> meff = std::make_unique<GXFMaterialEffects>(calomeots[i]);
4092
4093 if (i == 2) {
4094 firstcalopar = unique_clone(layerpar.get()).release();
4095 }
4096
4097 prevtrackpars = unique_clone(layerpar.get());
4098
4099 if (i == 1) {
4100 const double qoverpbrem = layerpar->parameters()[Trk::qOverP];
4101 double qoverp = 0;
4102
4103 if (
4104 npseudomuon1 < 2 &&
4105 (lastmuonpar != nullptr) &&
4106 std::abs(lastmuonpar->parameters()[Trk::qOverP]) > 1.e-9
4107 ) {
4108 qoverp = lastmuonpar->parameters()[Trk::qOverP];
4109 } else {
4110 const double sign = (qoverpbrem > 0) ? 1 : -1;
4111 qoverp = sign / (1 / std::abs(qoverpbrem) + std::abs(calomeots[i].energyLoss()->deltaE()));
4112 }
4113
4114 meff->setdelta_p(1000 * (qoverpbrem - qoverp));
4115 const AmgVector(5) & newpar = layerpar->parameters();
4116
4117 prevtrackpars = layerpar->associatedSurface().createUniqueTrackParameters(
4118 newpar[0], newpar[1], newpar[2], newpar[3], qoverp, std::nullopt
4119 );
4120 }
4121
4122 matstates.insert(matstates.begin(), std::make_unique<GXFTrackState>(std::move(meff), std::move(layerpar)));
4123 }
4124 }
4125 }
4126
4127 if (lasthit == lastmuonhit && cache.m_extmat) {
4128 std::unique_ptr<const Trk::TrackParameters> muonpar1;
4129
4130 if (lastcalopar != nullptr) {
4131 const bool msEntranceIsValid = ensureValidEntranceMuonSpectrometer(ctx, cache);
4132 if (msEntranceIsValid) {
4133 if (cache.m_msEntrance->inside(lastcalopar->position())) {
4134 muonpar1 = m_extrapolator->extrapolateToVolume(ctx,
4135 *lastcalopar,
4136 *cache.m_msEntrance,
4139
4140 if (muonpar1 != nullptr) {
4141 const Amg::Vector3D trackdir = muonpar1->momentum().unit();
4142 const Amg::Vector3D curvZcrossT = -(trackdir.cross(Amg::Vector3D(0, 0, 1)));
4143 const Amg::Vector3D curvU = curvZcrossT.unit();
4144 const Amg::Vector3D curvV = trackdir.cross(curvU);
4145 Amg::RotationMatrix3D rot = Amg::RotationMatrix3D::Identity();
4146 rot.col(0) = curvU;
4147 rot.col(1) = curvV;
4148 rot.col(2) = trackdir;
4150 trans.linear().matrix() << rot;
4151 trans.translation() << muonpar1->position() - .1 * trackdir;
4152 PlaneSurface const curvlinsurf(trans);
4153
4154 std::unique_ptr<const TrackParameters> curvlinpar(m_extrapolator->extrapolateDirectly(
4155 ctx,
4156 *muonpar1,
4157 curvlinsurf,
4160 ));
4161
4162 if (curvlinpar != nullptr) {
4163 muonpar1 = std::move(curvlinpar);
4164 }
4165 }
4166 } else {
4167 muonpar1 = std::unique_ptr<TrackParameters>(lastcalopar->clone());
4168 }
4169 }
4170 } else {
4171 muonpar1 = std::unique_ptr<TrackParameters>(refpar->clone());
4172 }
4173
4174 DistanceSolution distsol;
4175
4176 if (muonpar1 != nullptr) {
4177 distsol = lastmuonhit->associatedSurface().straightLineDistanceEstimate(
4178 muonpar1->position(),
4179 muonpar1->momentum().unit()
4180 );
4181 }
4182
4183 double distance = getDistance(distsol);
4184
4185 if ((distance > 0) and(distsol.numberOfSolutions() >
4186 0) and (firstmuonhit != nullptr)) {
4187 distsol = firstmuonhit->associatedSurface().straightLineDistanceEstimate(
4188 muonpar1->position(),
4189 muonpar1->momentum().unit()
4190 );
4191
4192 distance = 0;
4193
4194 if (distsol.numberOfSolutions() == 1) {
4195 distance = distsol.first();
4196 } else if (distsol.numberOfSolutions() == 2) {
4197 distance = (
4198 std::abs(distsol.first()) < std::abs(distsol.second()) ?
4199 distsol.first() :
4200 distsol.second()
4201 );
4202 }
4203
4204 if (distance < 0 && distsol.numberOfSolutions() > 0 && (firstidhit == nullptr)) {
4205 if (firstmuonpar != nullptr) {
4206 AmgVector(5) newpars = firstmuonpar->parameters();
4207
4208 if (trajectory.m_straightline && m_p != 0) {
4209 newpars[Trk::qOverP] = 1 / m_p;
4210 }
4211
4212 muonpar1 = firstmuonpar->associatedSurface().createUniqueTrackParameters(
4213 newpars[0], newpars[1], newpars[2], newpars[3], newpars[4], std::nullopt
4214 );
4215 } else {
4216 std::unique_ptr<const TrackParameters> tmppar(m_propagator->propagateParameters(
4217 ctx,
4218 *muonpar1,
4219 firstmuonhit->associatedSurface(),
4221 false,
4222 trajectory.m_fieldprop,
4224 ));
4225
4226 if (tmppar != nullptr) {
4227 muonpar1 = std::move(tmppar);
4228 }
4229 }
4230 }
4231
4232 const TrackParameters *prevtp = muonpar1.get();
4233 ATH_MSG_DEBUG("Obtaining downstream layers from Extrapolator");
4234 if (matvec_used) cache.m_matTempStore.push_back( std::move(matvec) );
4235 matvec.reset(m_extrapolator->extrapolateM(ctx,
4236 *prevtp,
4237 states.back()->associatedSurface(),
4239 false,
4241 matvec_used = false;
4242
4243 if (matvec && matvec->size() > 1000 && m_rejectLargeNScat) {
4244 ATH_MSG_DEBUG("too many scatterers: " << matvec->size());
4245 return;
4246 }
4247
4248 if (matvec && !matvec->empty()) {
4249 for (int j = 0; j < (int) matvec->size(); j++) {
4250 const MaterialEffectsBase *meb = (*matvec)[j]->materialEffectsOnTrack();
4251
4252 if (meb) {
4253 if ((meb->derivedType() == MaterialEffectsBase::MATERIAL_EFFECTS_ON_TRACK) and (j < (int) matvec->size() - 1)) {
4254 const MaterialEffectsOnTrack *meot = static_cast<const MaterialEffectsOnTrack *>(meb);
4255 std::unique_ptr<GXFMaterialEffects> meff = std::make_unique<GXFMaterialEffects>(*meot);
4256
4257 if (
4258 !trajectory.m_straightline &&
4259 (meot->energyLoss() != nullptr) &&
4260 std::abs(meff->deltaE()) > 25 &&
4261 std::abs((*matvec)[j]->trackParameters()->position().z()) < 13000
4262 ) {
4263 meff->setSigmaDeltaE(meot->energyLoss()->sigmaDeltaE());
4264 }
4265
4266 const TrackParameters * newparams = (*matvec)[j]->trackParameters() != nullptr ? (*matvec)[j]->trackParameters()->clone() : nullptr;
4267
4268 matstates.push_back(std::make_unique<GXFTrackState>(
4269 std::move(meff),
4270 std::unique_ptr<const TrackParameters>(newparams)
4271 ));
4272 matvec_used=true;
4273 }
4274 }
4275 }
4276 }
4277 }
4278 }
4279
4280 if (firsthit == firstmuonhit && cache.m_extmat && (firstcalopar != nullptr)) {
4281 std::unique_ptr<const Trk::TrackParameters> muonpar1;
4282
4283 const bool msEntranceIsValid = ensureValidEntranceMuonSpectrometer(ctx, cache);
4284 if (msEntranceIsValid) {
4285 if (cache.m_msEntrance->inside(firstcalopar->position())) {
4286 muonpar1 = m_extrapolator->extrapolateToVolume(ctx,
4287 *firstcalopar,
4288 *cache.m_msEntrance,
4291
4292 if (muonpar1 != nullptr) {
4293 const Amg::Vector3D trackdir = muonpar1->momentum().unit();
4294 const Amg::Vector3D curvZcrossT = -(trackdir.cross(Amg::Vector3D(0, 0, 1)));
4295 const Amg::Vector3D curvU = curvZcrossT.unit();
4296 const Amg::Vector3D curvV = trackdir.cross(curvU);
4297 Amg::RotationMatrix3D rot = Amg::RotationMatrix3D::Identity();
4298 rot.col(0) = curvU;
4299 rot.col(1) = curvV;
4300 rot.col(2) = trackdir;
4302 trans.linear().matrix() << rot;
4303 trans.translation() << muonpar1->position() - .1 * trackdir;
4304 const PlaneSurface curvlinsurf(trans);
4305
4306 std::unique_ptr<const TrackParameters> curvlinpar(m_extrapolator->extrapolateDirectly(
4307 ctx,
4308 *muonpar1,
4309 curvlinsurf,
4312 ));
4313
4314 if (curvlinpar != nullptr) {
4315 muonpar1 = std::move(curvlinpar);
4316 }
4317 }
4318 } else {
4319 muonpar1 = std::unique_ptr<const TrackParameters>(firstcalopar->clone());
4320 }
4321 }
4322
4323 DistanceSolution distsol;
4324
4325 if (muonpar1 != nullptr) {
4326 distsol = firstmuonhit->associatedSurface().straightLineDistanceEstimate(
4327 muonpar1->position(),
4328 muonpar1->momentum().unit()
4329 );
4330 }
4331
4332 const double distance = getDistance(distsol);
4333
4334 if (distance < 0 && distsol.numberOfSolutions() > 0) {
4335 const TrackParameters *prevtp = muonpar1.get();
4336 ATH_MSG_DEBUG("Collecting upstream muon material from extrapolator");
4337 if (matvec_used) cache.m_matTempStore.push_back( std::move(matvec) );
4338 matvec.reset(m_extrapolator->extrapolateM(ctx,
4339 *prevtp,
4340 states[0]->associatedSurface(),
4342 false,
4344 matvec_used = false;
4345
4346 if (matvec && !matvec->empty()) {
4347 ATH_MSG_DEBUG("Retrieved " << matvec->size() << " material states");
4348
4349 for (int j = 0; j < (int) matvec->size(); j++) {
4350 const MaterialEffectsBase *meb = (*matvec)[j]->materialEffectsOnTrack();
4351
4352 if (meb != nullptr) {
4353
4354
4355 if ((meb->derivedType() == MaterialEffectsBase::MATERIAL_EFFECTS_ON_TRACK) && j < (int) matvec->size() - 1) {
4356 const MaterialEffectsOnTrack *meot = static_cast<const MaterialEffectsOnTrack *>(meb);
4357 std::unique_ptr<GXFMaterialEffects> meff = std::make_unique<GXFMaterialEffects>(*meot);
4358
4359 if (
4360 !trajectory.m_straightline &&
4361 (meot->energyLoss() != nullptr) &&
4362 std::abs(meff->deltaE()) > 25 &&
4363 std::abs((*matvec)[j]->trackParameters()->position().z()) < 13000
4364 ) {
4365 meff->setSigmaDeltaE(meot->energyLoss()->sigmaDeltaE());
4366 }
4367
4368 const TrackParameters* tmpparams =
4369 (*matvec)[j]->trackParameters() != nullptr
4370 ? (*matvec)[j]->trackParameters()->clone()
4371 : nullptr;
4372
4373 matstates.insert(matstates.begin(), std::make_unique<GXFTrackState>(
4374 std::move(meff),
4375 std::unique_ptr<const TrackParameters>(tmpparams)
4376 ));
4377 matvec_used=true;
4378 }
4379 }
4380 }
4381 }
4382 }
4383 }
4384
4385 ATH_MSG_DEBUG("Number of layers: " << matstates.size());
4386
4387 // Now insert the material states into the trajectory
4388 std::vector<std::unique_ptr<GXFTrackState>> & newstates = states;
4389 std::vector<std::unique_ptr<GXFTrackState>> oldstates = std::move(newstates);
4390
4391 newstates.clear();
4392 newstates.reserve(oldstates.size() + matstates.size());
4393
4394 int layerno = 0;
4395 int firstlayerno = -1;
4396
4397 if (cache.m_acceleration) {
4398 trajectory.addBasicState(std::move(oldstates[0]));
4399 }
4400
4401 const double cosphi = std::cos(refpar->parameters()[Trk::phi0]);
4402 const double sinphi = std::sin(refpar->parameters()[Trk::phi0]);
4403
4404 for (int i = cache.m_acceleration ? 1 : 0; i < (int) oldstates.size(); i++) {
4405 bool addlayer = true;
4406
4407 while (addlayer && layerno < (int) matstates.size()) {
4408 addlayer = false;
4409 const TrackParameters *layerpar = matstates[layerno]->trackParameters();
4410
4411 const DistanceSolution distsol = oldstates[i]->associatedSurface().straightLineDistanceEstimate(
4412 layerpar->position(),
4413 layerpar->momentum().unit()
4414 );
4415
4416 const double distance = getDistance(distsol);
4417
4418 if (distance > 0 && distsol.numberOfSolutions() > 0) {
4419 addlayer = true;
4420 }
4421
4422 if (layerpar->associatedSurface().type() == Trk::SurfaceType::Cylinder) {
4423 const double cylinderradius = layerpar->associatedSurface().bounds().r();
4424 const double trackimpact = std::abs(-refpar->position().x() * sinphi + refpar->position().y() * cosphi);
4425
4426 if (trackimpact > cylinderradius - 5 * mm) {
4427 layerno++;
4428 continue;
4429 }
4430 }
4431
4432 if (i == (int) oldstates.size() - 1) {
4433 addlayer = true;
4434 }
4435
4436 if (addlayer) {
4437 GXFMaterialEffects *meff = matstates[layerno]->materialEffects();
4438
4439 if (meff->sigmaDeltaPhi() > .4 || (meff->sigmaDeltaPhi() == 0 && meff->sigmaDeltaE() <= 0)) {
4440 if (meff->sigmaDeltaPhi() > .4) {
4441 ATH_MSG_DEBUG("Material state with excessive scattering, skipping it");
4442 }
4443
4444 if (meff->sigmaDeltaPhi() == 0) {
4445 ATH_MSG_WARNING("Material state with zero scattering, skipping it");
4446 }
4447
4448 layerno++;
4449 continue;
4450 }
4451
4452 if (firstlayerno < 0) {
4453 firstlayerno = layerno;
4454 }
4455
4456 trajectory.addMaterialState(std::move(matstates[layerno]));
4457
4458 if ((layerpar != nullptr) && matEffects != pion && matEffects != muon) {
4459 const TrackingVolume *tvol = m_navigator->volume(ctx,layerpar->position());
4460 const Layer *lay = nullptr;
4461
4462 if (tvol != nullptr) {
4463 lay = (tvol->closestMaterialLayer(layerpar->position(),layerpar->momentum().normalized())).object;
4464 }
4465
4466 const MaterialProperties *matprop = lay != nullptr ? lay->fullUpdateMaterialProperties(*layerpar) : nullptr;
4467 meff->setMaterialProperties(matprop);
4468 }
4469
4470 layerno++;
4471 }
4472 }
4473 trajectory.addBasicState(std::move(oldstates[i]));
4474 }
4475
4476 ATH_MSG_DEBUG("Total X0: " << trajectory.totalX0() << " total eloss: " << trajectory.totalEnergyLoss());
4477
4478 if (matvec_used) cache.m_matTempStore.push_back( std::move(matvec) );
4479 }
4480
4481 std::unique_ptr<const TrackParameters> GlobalChi2Fitter::makePerigee(
4482 Cache & cache,
4483 const TrackParameters & param,
4484 ParticleHypothesis matEffects
4485 ) const {
4486 const PerigeeSurface *persurf = nullptr;
4487
4489 persurf = static_cast<const PerigeeSurface *>(&param.associatedSurface());
4490
4491 if ((persurf != nullptr) && (!cache.m_acceleration || persurf->center().perp() > 5)) {
4492 const AmgVector(5) & pars = param.parameters();
4494 pars[0], pars[1], pars[2], pars[3], pars[4], std::nullopt
4495 );
4496 }
4497
4498 if (cache.m_acceleration) {
4499 return nullptr;
4500 }
4501
4502 PerigeeSurface const tmppersf;
4503 std::unique_ptr<const TrackParameters> per(m_extrapolator->extrapolate(
4504 Gaudi::Hive::currentContext(),param, tmppersf, oppositeMomentum, false, matEffects));
4505
4506 if (per == nullptr) {
4507 ATH_MSG_DEBUG("Cannot make Perigee with starting parameters");
4508 return nullptr;
4509 }
4510
4511 if(std::abs(per->position().z())>5000.) {
4512 ATH_MSG_WARNING("Pathological perigee well outside of tracking detector!! Returning nullptr");
4513 return nullptr;
4514 }
4515
4516 return per;
4517 }
4518
4520 const EventContext& ctx,
4521 Cache & cache,
4522 GXFTrajectory & trajectory,
4523 const TrackParameters & param,
4524 const RunOutlierRemoval runOutlier,
4525 const ParticleHypothesis matEffects
4526 ) const {
4527 ATH_MSG_DEBUG("--> entering GlobalChi2Fitter::myfit_helper");
4528 cache.m_fittercode = FitterStatusCode::Success;
4530
4531 if (!trajectory.m_straightline) {
4532 if (trajectory.numberOfSiliconHits() + trajectory.numberOfTRTHits() == trajectory.numberOfHits()) {
4533 trajectory.m_straightline = !cache.m_field_cache.solenoidOn();
4534 } else if ((trajectory.prefit() == 0) && trajectory.numberOfSiliconHits() + trajectory.numberOfTRTHits() == 0) {
4535 trajectory.m_straightline = !cache.m_field_cache.toroidOn();
4536 } else {
4537 trajectory.m_straightline = (!cache.m_field_cache.solenoidOn() && !cache.m_field_cache.toroidOn());
4538 }
4539 }
4540
4541 trajectory.m_fieldprop = trajectory.m_straightline ? Trk::NoField : Trk::FullField;
4542 cache.m_lastiter = 0;
4543
4544 Amg::SymMatrixX lu;
4545
4546 if (trajectory.numberOfPerigeeParameters() == -1) {
4547 cache.incrementFitStatus(S_FITS);
4548 if (trajectory.m_straightline) {
4549 trajectory.setNumberOfPerigeeParameters(4);
4550 } else {
4551 trajectory.setNumberOfPerigeeParameters(5);
4552 }
4553 }
4554
4555 if (trajectory.nDOF() < 0) {
4556 ATH_MSG_DEBUG("Not enough measurements, reject track");
4557 return nullptr;
4558 }
4559
4560 cache.m_phiweight.clear();
4561 cache.m_firstmeasurement.clear();
4562 cache.m_lastmeasurement.clear();
4563
4564 if (matEffects != nonInteracting && param.parameters()[Trk::qOverP] == 0 && m_p == 0) {
4565 ATH_MSG_WARNING("Attempt to apply material corrections with q/p=0, reject track");
4566 return nullptr;
4567 }
4568
4569 if (matEffects == Trk::electron && trajectory.m_straightline) {
4570 ATH_MSG_WARNING("Electron fit requires helix track model");
4571 return nullptr;
4572 }
4573
4574 const double mass = Trk::ParticleMasses::mass[matEffects];
4575 trajectory.setMass(mass);
4576
4577 ATH_MSG_DEBUG("start param: " << param << " pos: " << param.position() << " pt: " << param.pT());
4578
4579 std::unique_ptr<const TrackParameters> per = makePerigee(cache, param, matEffects);
4580
4581 if (!cache.m_acceleration && (per == nullptr)) {
4583 cache.incrementFitStatus(S_PROPAGATION_FAIL);
4584 ATH_MSG_DEBUG("Propagation to perigee failed 1");
4585 return nullptr;
4586 }
4587
4588 if (matEffects != Trk::nonInteracting && !cache.m_matfilled) {
4589 if (
4590 cache.m_fastmat &&
4591 cache.m_acceleration &&
4592 trajectory.numberOfSiliconHits() + trajectory.numberOfTRTHits() == trajectory.numberOfHits() &&
4593 (m_matupdator.empty() || (m_trackingGeometryReadKey.key().empty()))
4594 ) {
4595 ATH_MSG_WARNING("Tracking Geometry Service and/or Material Updator Tool not configured");
4596 ATH_MSG_WARNING("Falling back to slow material collection");
4597
4598 cache.m_fastmat = false;
4599 }
4600
4601 if (
4602 !cache.m_fastmat ||
4603 !cache.m_acceleration ||
4604 trajectory.numberOfSiliconHits() + trajectory.numberOfTRTHits() != trajectory.numberOfHits()
4605 ) {
4606 addMaterial(ctx, cache, trajectory, per != nullptr ? per.get() : &param, matEffects);
4607 } else {
4609 ctx, cache, trajectory, per != nullptr ? per.get() : &param, matEffects);
4610 }
4611 }
4612
4613 if (cache.m_acceleration && (trajectory.referenceParameters() == nullptr) && (per == nullptr)) {
4615
4616 if (trajectory.numberOfScatterers() >= 2) {
4617 GXFTrackState *scatstate = nullptr;
4618 GXFTrackState *scatstate2 = nullptr;
4619 int scatindex = 0;
4620
4621 for (const auto & state : trajectory.trackStates()) {
4622 if (state->getStateType(TrackStateOnSurface::Scatterer)) {
4623 if (
4624 scatindex == trajectory.numberOfScatterers() / 2 ||
4625 state->materialEffects()->deltaE() == 0
4626 ) {
4627 scatstate2 = state.get();
4628 break;
4629 }
4630
4631 scatindex++;
4632 scatstate = state.get();
4633 }
4634 }
4635
4636 // @TODO coverity complains about a possible null pointer dereferencing in scatstate->... or scatstate2->...
4637 // it seems to me that if (**it).materialEffects()->deltaE()==0 of the first scatterer
4638 // than scatstate will be NULL.
4639 if ((scatstate == nullptr) || (scatstate2 == nullptr)) {
4640 throw std::logic_error("Invalid scatterer");
4641 }
4642
4643 vertex = .49 * (scatstate->position() + scatstate2->position());
4644 } else {
4645 const int nstates = (int) trajectory.trackStates().size();
4646 vertex = .49 * (
4647 trajectory.trackStates()[nstates / 2 - 1]->position() +
4648 trajectory.trackStates()[nstates / 2]->position()
4649 );
4650 }
4651
4652 PerigeeSurface const persurf(vertex);
4653 std::unique_ptr<const TrackParameters> nearestpar;
4654 double mindist = 99999;
4655 std::vector < GXFTrackState * >mymatvec;
4656
4657 for (auto & it : trajectory.trackStates()) {
4658 if ((*it).trackParameters() == nullptr) {
4659 continue;
4660 }
4661
4662 const double distance = persurf
4664 (*it).trackParameters()->position(),
4665 (*it).trackParameters()->momentum().unit())
4666 .first();
4667
4668 const bool insideid = (
4669 (cache.m_caloEntrance == nullptr) ||
4670 cache.m_caloEntrance->inside((*it).trackParameters()->position())
4671 );
4672
4673 if (
4674 (((*it).measurement() != nullptr) && insideid) || (
4675 ((*it).materialEffects() != nullptr) &&
4676 distance > 0 && (
4677 (*it).materialEffects()->deltaE() == 0 ||
4678 ((*it).materialEffects()->sigmaDeltaPhi() == 0 &&
4679 !insideid) ||
4680 (*it).materialEffects()->deltaPhi() != 0
4681 )
4682 )
4683 ) {
4684 const double dist = ((*it).trackParameters()->position() - vertex).perp();
4685 if (dist < mindist) {
4686 mindist = dist;
4687 nearestpar = unique_clone((*it).trackParameters());
4688 mymatvec.clear();
4689 continue;
4690 }
4691 }
4692
4693 if (((*it).materialEffects() != nullptr) && distance > 0) {
4694 mymatvec.push_back(it.get());
4695 }
4696 }
4697
4698 if (nearestpar == nullptr) {
4699 nearestpar = unique_clone(&param);
4700 }
4701
4702 for (auto & state : mymatvec) {
4704 const Surface &matsurf = state->associatedSurface();
4705 const DistanceSolution distsol = matsurf.straightLineDistanceEstimate(
4706 nearestpar->position(), nearestpar->momentum().unit());
4707
4708 const double distance = getDistance(distsol);
4709
4710 if (distance < 0 && distsol.numberOfSolutions() > 0) {
4711 propdir = oppositeMomentum;
4712 }
4713
4714 std::unique_ptr<const TrackParameters> tmppar(m_propagator->propagateParameters(
4715 ctx,
4716 *nearestpar,
4717 matsurf,
4718 propdir,
4719 false,
4720 trajectory.m_fieldprop,
4722 ));
4723
4724 if (tmppar == nullptr) {
4725 propdir = (propdir == oppositeMomentum) ? alongMomentum : oppositeMomentum;
4726 tmppar = m_propagator->propagateParameters(
4727 ctx,
4728 *nearestpar,
4729 matsurf,
4730 propdir,
4731 false,
4732 trajectory.m_fieldprop,
4734 );
4735
4736 if (tmppar == nullptr) {
4738 cache.incrementFitStatus(S_PROPAGATION_FAIL);
4739
4740 ATH_MSG_DEBUG("Propagation to perigee failed 2");
4741
4742 return nullptr;
4743 }
4744 }
4745
4746 AmgVector(5) newpars = tmppar->parameters();
4747
4748 if (state->materialEffects()->sigmaDeltaE() > 0) {
4749 newpars[Trk::qOverP] += .001 * state->materialEffects()->delta_p();
4750 } else if (newpars[Trk::qOverP] != 0) {
4751 const double sign = (newpars[Trk::qOverP] > 0) ? 1 : -1;
4752 const double de = std::abs(state->materialEffects()->deltaE());
4753 const double oldp = std::abs(1 / newpars[Trk::qOverP]);
4754 const double newp2 = oldp * oldp - 2 * de * std::sqrt(mass * mass + oldp * oldp) + de * de;
4755 if (newp2 > 0) {
4756 newpars[Trk::qOverP] = sign / std::sqrt(newp2);
4757 }
4758 }
4759
4760 nearestpar = tmppar->associatedSurface().createUniqueTrackParameters(
4761 newpars[0], newpars[1], newpars[2], newpars[3], newpars[4], std::nullopt
4762 );
4763 }
4764
4765 std::unique_ptr<Trk::TrackParameters> tmpPars(m_propagator->propagateParameters(
4766 ctx,
4767 *nearestpar,
4768 persurf,
4770 false,
4771 trajectory.m_fieldprop,
4773 ));
4774
4775 // Parameters are at a Perigee surface so they are perigee parameters
4776 if (tmpPars != nullptr) {
4777 per.reset(static_cast < const Perigee *>(tmpPars.release()));
4778 }
4779
4780 if ((per != nullptr) && (matEffects == Trk::proton || matEffects == Trk::kaon)) {
4781 const double sign = (per->parameters()[Trk::qOverP] < 0) ? -1. : 1.;
4782 const double oldp = 1. / std::abs(per->parameters()[Trk::qOverP]);
4783 const double toteloss = std::abs(trajectory.totalEnergyLoss());
4784 const double newp = std::sqrt(oldp * oldp + 2 * toteloss * std::sqrt(oldp * oldp + mass * mass) + toteloss * toteloss);
4785 AmgVector(5) params = per->parameters();
4786 params[Trk::qOverP] = sign / newp;
4787
4788 per = per->associatedSurface().createUniqueTrackParameters(
4789 params[0], params[1], params[2], params[3], params[4], std::nullopt
4790 );
4791 }
4792
4793 if (per == nullptr) {
4795 cache.incrementFitStatus(S_PROPAGATION_FAIL);
4796 ATH_MSG_DEBUG("Propagation to perigee failed 3");
4797
4798 return nullptr;
4799 }
4800
4801 PerigeeSurface const persurf2(per->position());
4802 per = persurf2.createUniqueTrackParameters(
4803 0,
4804 0,
4805 per->parameters()[Trk::phi],
4806 per->parameters()[Trk::theta],
4807 per->parameters()[Trk::qOverP],
4808 std::nullopt
4809 );
4810 } else if (per == nullptr) {
4811 per = makePerigee(cache, param, matEffects);
4812 }
4813
4814 if ((per == nullptr) && (trajectory.referenceParameters() == nullptr)) {
4816 cache.incrementFitStatus(S_PROPAGATION_FAIL);
4817 ATH_MSG_DEBUG("Propagation to perigee failed 4");
4818
4819 return nullptr;
4820 }
4821
4822 if (trajectory.m_straightline && (per != nullptr)) {
4823 if (trajectory.numberOfPerigeeParameters() == -1) {
4824 trajectory.setNumberOfPerigeeParameters(4);
4825 }
4826
4827 const AmgVector(5) & pars = per->parameters();
4828 per = per->associatedSurface().createUniqueTrackParameters(
4829 pars[0], pars[1], pars[2], pars[3], 0, std::nullopt
4830 );
4831 } else if (trajectory.numberOfPerigeeParameters() == -1) {
4832 trajectory.setNumberOfPerigeeParameters(5);
4833 }
4834
4835 if (per != nullptr) {
4836 trajectory.setReferenceParameters(std::move(per));
4837 }
4838
4839 const int nfitpar = trajectory.numberOfFitParameters();
4840 const int nperpars = trajectory.numberOfPerigeeParameters();
4841 const int nscat = trajectory.numberOfScatterers();
4842 const int nbrem = trajectory.numberOfBrems();
4843
4844 Eigen::MatrixXd a;
4845 Eigen::MatrixXd a_inv;
4846 a.resize(nfitpar, nfitpar);
4847
4848 Amg::VectorX b(nfitpar);
4849
4850 Amg::MatrixX derivPool(5, nfitpar);
4851 derivPool.setZero();
4852
4853 for (std::unique_ptr<GXFTrackState> & state : trajectory.trackStates()) {
4854 if (state->materialEffects() != nullptr) {
4855 continue;
4856 }
4857 state->setDerivatives(derivPool);
4858 }
4859
4860 bool doderiv = true;
4861 const int tmpminiter = cache.m_miniter;
4862
4863 for (int it = 0; it < m_maxit; ++it) {
4864 cache.m_lastiter = it;
4865
4866 if (it >= m_maxit - 1) {
4867 ATH_MSG_DEBUG("Fit did not converge");
4869 cache.incrementFitStatus(S_NOT_CONVERGENT);
4870 cache.m_miniter = tmpminiter;
4871 return nullptr;
4872 }
4873
4874 if (!trajectory.converged()) {
4875 cache.m_fittercode =
4876 runIteration(ctx, cache, trajectory, it, a, b, lu, doderiv);
4877 if (cache.m_fittercode != FitterStatusCode::Success) {
4878 if (cache.m_fittercode == FitterStatusCode::ExtrapolationFailure) {
4879 cache.incrementFitStatus(S_PROPAGATION_FAIL);
4880 } else if (cache.m_fittercode == FitterStatusCode::InvalidAngles) {
4881 cache.incrementFitStatus(S_INVALID_ANGLES);
4883 cache.incrementFitStatus(S_LOW_MOMENTUM);
4884 }
4885 cache.m_miniter = tmpminiter;
4886 return nullptr;
4887 }
4888
4889 const int nhits = trajectory.numberOfHits();
4890 const int ntrthits = trajectory.numberOfTRTHits();
4891 const int nsihits = trajectory.numberOfSiliconHits();
4892 const double redchi2 = (trajectory.nDOF() > 0) ? trajectory.chi2() / trajectory.nDOF() : 0;
4893 const double prevredchi2 = (trajectory.nDOF() > 0) ? trajectory.prevchi2() / trajectory.nDOF() : 0;
4894
4895
4896 if( nsihits > 0 && it > 0 && it < m_maxitPixelROT )
4897 updatePixelROTs( trajectory, a, b, ctx);
4898
4899 if (
4900 it > 0 &&
4901 it < 4 && (
4902 (redchi2 < prevredchi2 &&
4903 (redchi2 > prevredchi2 - 1 || redchi2 < 2)) ||
4904 nsihits + ntrthits == nhits
4905 ) &&
4906 (runOutlier || m_trtrecal) &&
4907 ntrthits > 0
4908 ) {
4909 if (it != 1 || nsihits != 0 || trajectory.nDOF() <= 0 || trajectory.chi2() / trajectory.nDOF() <= 3) {
4910 ATH_MSG_DEBUG("Running TRT cleaner");
4911 runTrackCleanerTRT(cache, trajectory, a, b, lu, runOutlier, m_trtrecal, it, ctx);
4912 if (cache.m_fittercode != FitterStatusCode::Success) {
4913 ATH_MSG_DEBUG("TRT cleaner failed, returning null...");
4914 cache.m_miniter = tmpminiter;
4915 return nullptr;
4916 }
4917 }
4918 }
4919
4920 // PHF cut at iteration 3 (to save CPU time)
4921 const int ntrtprechits = trajectory.numberOfTRTPrecHits();
4922 const int ntrttubehits = trajectory.numberOfTRTTubeHits();
4923 float phf = 1.;
4924 if (ntrtprechits+ntrttubehits) {
4925 phf = float(ntrtprechits)/float(ntrtprechits+ntrttubehits);
4926 }
4927 if (phf<m_minphfcut && it>=3) {
4928 if ((ntrtprechits+ntrttubehits)>=15) {
4929 return nullptr;
4930 }
4931 }
4932 ATH_MSG_DEBUG("Iter = " << it << " | nTRTStates = " << ntrthits
4933 << " | nTRTPrecHits = " << ntrtprechits
4934 << " | nTRTTubeHits = " << ntrttubehits
4935 << " | nOutliers = "
4936 << trajectory.numberOfOutliers());
4937
4938 if (!trajectory.converged()) {
4939 cache.m_fittercode = updateFitParameters(trajectory, b, lu);
4940 if (cache.m_fittercode != FitterStatusCode::Success) {
4941 if (cache.m_fittercode == FitterStatusCode::InvalidAngles) {
4942 cache.incrementFitStatus(S_INVALID_ANGLES);
4943 }
4944 cache.m_miniter = tmpminiter;
4945 return nullptr;
4946 }
4947 }
4948 } else {
4949 break;
4950 }
4951 }
4952
4953 cache.m_miniter = tmpminiter;
4954
4955 if (trajectory.prefit() == 0) {
4956 // Solve assuming the matrix is SPD.
4957 // Cholesky Decomposition is used -- could use LDLT
4958
4959 Eigen::LLT < Eigen::MatrixXd > const lltOfW(a);
4960 if (lltOfW.info() == Eigen::Success) {
4961 // Solve for x where Wx = I
4962 // this is cheaper than invert as invert makes no assumptions about the
4963 // matrix being symmetric
4964 const int ncols = a.cols();
4965 Amg::MatrixX const weightInvAMG = Amg::MatrixX::Identity(ncols, ncols);
4966 a_inv = lltOfW.solve(weightInvAMG);
4967 } else {
4968 ATH_MSG_DEBUG("matrix inversion failed!");
4969 cache.incrementFitStatus(S_MAT_INV_FAIL);
4971 return nullptr;
4972 }
4973 }
4974
4975 GXFTrajectory *finaltrajectory = &trajectory;
4976 if (
4977 (runOutlier || cache.m_sirecal) &&
4978 trajectory.numberOfSiliconHits() == trajectory.numberOfHits()
4979 ) {
4980 calculateTrackErrors(trajectory, a_inv, true);
4981 GXFTrajectory* traj = runTrackCleanerSilicon(ctx,cache, trajectory, a, a_inv, b, runOutlier);
4982
4983 if (cache.m_fittercode != FitterStatusCode::Success) {
4984 ATH_MSG_DEBUG("Silicon cleaner failed, returning null...");
4985 if (traj != &trajectory) {
4986 delete traj;
4987 }
4988 return nullptr;
4989 }
4990 finaltrajectory = traj;
4991 }
4992
4993 if (m_domeastrackpar && (finaltrajectory->prefit() == 0)) {
4994 calculateTrackErrors(*finaltrajectory, a_inv, false);
4995 }
4996
4997 if (!cache.m_acceleration && (finaltrajectory->prefit() == 0)) {
4998 if (nperpars == 5) {
4999 for (int i = 0; i < a.cols(); i++) {
5000 a_inv(4, i) *= .001;
5001 a_inv(i, 4) *= .001;
5002 }
5003 }
5004
5005 int scatterPos = nperpars + 2 * nscat;
5006 for (int bremno = 0; bremno < nbrem; bremno++, scatterPos++) {
5007 for (int i = 0; i < a.cols(); i++) {
5008 a_inv(scatterPos, i) *= .001;
5009 a_inv(i, scatterPos) *= .001;
5010 }
5011 }
5012
5013 AmgSymMatrix(5) errmat;
5014 errmat.setZero();
5015 const int nperparams = finaltrajectory->numberOfPerigeeParameters();
5016 for (int i = 0; i < nperparams; i++) {
5017 for (int j = 0; j < nperparams; j++) {
5018 (errmat) (j, i) = a_inv(j, i);
5019 }
5020 }
5021
5022 if (trajectory.m_straightline) {
5023 (errmat) (4, 4) = 1e-20;
5024 }
5025
5026 const AmgVector(5) & perpars = finaltrajectory->referenceParameters()->parameters();
5027 std::unique_ptr<const TrackParameters> measper(
5029 perpars[0], perpars[1], perpars[2], perpars[3], perpars[4], std::move(errmat)
5030 )
5031 );
5032
5033 finaltrajectory->setReferenceParameters(std::move(measper));
5034 if (m_fillderivmatrix) {
5035 cache.m_fullcovmat = std::move(a_inv);
5036 }
5037 }
5038
5039 std::unique_ptr<Track> track = nullptr;
5040
5041 if (finaltrajectory->prefit() > 0) {
5042 if (finaltrajectory != &trajectory) {
5043 // cppcheck-suppress autovarInvalidDeallocation; false positive
5044 delete finaltrajectory;
5045 }
5046 return nullptr;
5047 }
5048
5049 if (finaltrajectory->numberOfOutliers() <= m_maxoutliers || !runOutlier) {
5050 track = makeTrack(ctx,cache, *finaltrajectory, matEffects);
5051 } else {
5052 cache.incrementFitStatus(S_NOT_ENOUGH_MEAS);
5054 }
5055
5056 const double cut = (finaltrajectory->numberOfSiliconHits() ==
5057 finaltrajectory->numberOfHits())
5058 ? 999.0
5059 : m_chi2cut.value();
5060
5061 if (
5062 runOutlier &&
5063 (track != nullptr) && (
5064 track->fitQuality()->numberDoF() != 0 &&
5065 track->fitQuality()->chiSquared() / track->fitQuality()->numberDoF() > cut
5066 )
5067 ) {
5068 track.reset(nullptr);
5069 cache.incrementFitStatus(S_HIGH_CHI2);
5070 }
5071
5072 if (track == nullptr) {
5073 ATH_MSG_DEBUG("Track rejected");
5074 }
5075
5076 if (finaltrajectory != &trajectory) {
5077 // cppcheck-suppress autovarInvalidDeallocation; false positive
5078 delete finaltrajectory;
5079 }
5080
5081 return track.release();
5082 }
5083
5085 const EventContext& ctx,
5086 const Cache & cache,
5087 GXFTrajectory & trajectory,
5088 const int it,
5089 Amg::VectorX & b,
5090 int & bremno_maxbrempull,
5091 GXFTrackState* & state_maxbrempull
5092 ) const {
5093 ATH_MSG_DEBUG("fillResidualsAndErrors");
5094
5095 std::vector<std::unique_ptr<GXFTrackState>> & states = trajectory.trackStates();
5096
5097 /*
5098 * The residual and error vectors, we want to fill in this function.
5099 */
5100 Amg::VectorX & res = trajectory.residuals();
5101 Amg::VectorX & error = trajectory.errors();
5102
5103 /*
5104 * These variables are used inside the preprocessing loop for counting and
5105 * managing some quantities.
5106 */
5107 int scatno = 0;
5108 int bremno = 0;
5109 int measno = 0;
5110
5111 /*
5112 * Total number of measurements, and brems and perigee parameters. This is
5113 * used later to fill the residual and error vector to find the offsets.
5114 */
5115 const int nmeas = (int) res.size();
5116 const int nbrem = trajectory.numberOfBrems();
5117 const int nperpars = trajectory.numberOfPerigeeParameters();
5118
5119 /*
5120 * Under certain circumstances, we create new pseudo measurements. Here are
5121 * the static conditions. Later, we have also for each state a more
5122 * confining check.
5123 */
5124 const int nidhits = trajectory.numberOfSiliconHits() + trajectory.numberOfTRTHits();
5125 const int nDOF = trajectory.nDOF();
5126 const bool doNewPseudoMeasurements = (
5127 1 < it &&
5128 it <= 100 &&
5129 nDOF != 0 &&
5130 std::abs((trajectory.prevchi2() - trajectory.chi2()) / nDOF) < 15 &&
5131 nidhits < trajectory.numberOfHits() &&
5132 (nperpars == 0 || nidhits > 0)
5133 );
5134
5135 /*
5136 * Temporary quantities.
5137 * - chi2 will be used later to set the new chi2.
5138 * - maxbrempull collects the elosspull for the kink with the largest
5139 * brems. It is initised to -0.2 to consider only definitely negative
5140 * pulls.
5141 */
5142 double chi2 = 0;
5143 double maxbrempull = -0.2;
5144
5145 /*
5146 * Loop over all hits and do some preprocessing. In this step, we do:
5147 * - Get residuals
5148 * - Get errors
5149 * - Get scattering angles
5150 * - Fill b-vector and chi2 with scattering effects (others fill later)
5151 */
5152 for (int hitno = 0; hitno < (int) states.size(); hitno++) {
5153 std::unique_ptr<GXFTrackState> & state = states[hitno];
5154 const TrackParameters *currenttrackpar = state->trackParameters();
5155 TrackState::MeasurementType const hittype = state->measurementType();
5156 const MeasurementBase *measbase = state->measurement();
5157 auto *pMattEff = state->materialEffects();
5158
5159 /*
5160 * Measurements and outliers.
5161 */
5162 if (state->getStateType(TrackStateOnSurface::Measurement)) {
5163 /*
5164 * Create new pseudo measurements when the static check (evaluated
5165 * outside the loop) and the dynamic checks both pass
5166 */
5167 if (
5168 doNewPseudoMeasurements &&
5169 hittype == TrackState::Pseudo &&
5170 !state->associatedSurface().isFree() &&
5171 !state->isRecalibrated()
5172 ) {
5173 Amg::MatrixX covMatrix(1, 1);
5174 covMatrix(0, 0) = 100;
5175
5176 std::unique_ptr<const PseudoMeasurementOnTrack> newpseudo = std::make_unique<const PseudoMeasurementOnTrack>(
5177 LocalParameters(DefinedParameter(currenttrackpar->parameters()[Trk::locY], Trk::locY)),
5178 std::move(covMatrix),
5179 currenttrackpar->associatedSurface()
5180 );
5181
5182 state->setMeasurement(std::move(newpseudo));
5183 measbase = state->measurement();
5184 }
5185
5186 /*
5187 * Separate all parameters in the residuals and errors. We will handle
5188 * them separately, asuming them uncorrelated.
5189 */
5190 double *errors = state->measurementErrors();
5191 std::array<double,5> residuals = m_residualPullCalculator->residuals(measbase, currenttrackpar, ResidualPull::Biased, hittype);
5192 for (int i = 0; i < 5; i++) {
5193 /*
5194 * Skip the parameter, if there is no accessor for it.
5195 */
5197 continue;
5198 }
5199
5200 /*
5201 * SCT and TGC are 1-dimensional, so we can skip the other parameters.
5202 */
5203 if (i > 0 && (hittype == TrackState::SCT || hittype == TrackState::TGC)) {
5204 continue;
5205 }
5206
5207 error[measno] =
5208 (trajectory.prefit() > 0 && (hittype == TrackState::MDT || (hittype == TrackState::CSC && !state->measuresPhi()))) ?
5209 2 :
5210 errors[i];
5211
5212 res[measno] = residuals[i];
5213
5214 /*
5215 * Ensure, that the phi-residual is mapped into the correct period.
5216 */
5217 if (i == 2) {
5218 res[measno] = -std::remainder(-res[measno], 2 * M_PI);
5219 }
5220
5221 measno++;
5222 }
5223 } else if (state->getStateType(TrackStateOnSurface::Outlier)) {
5224 /*
5225 * NOTE: It seems the residuals are not set in this step. Why?
5226 */
5227 double *errors = state->measurementErrors();
5228 for (int i = 0; i < 5; i++) {
5229 if (errors[i] > 0) {
5230 error[measno] = errors[i];
5231 measno++;
5232 }
5233 }
5234 }
5235
5236 /*
5237 * Scattering angles contribute to the b-vector and the chi2.
5238 */
5239 if (
5240 state->getStateType(TrackStateOnSurface::Scatterer) &&
5241 ((trajectory.prefit() == 0) || pMattEff->deltaE() == 0)
5242 ) {
5243 const double deltaPhi = pMattEff->deltaPhi();
5244 const double measDeltaPhi = pMattEff->measuredDeltaPhi();
5245 const double sigma2deltaPhi = std::pow(pMattEff->sigmaDeltaPhi(), 2);
5246 const double deltaTheta = pMattEff->deltaTheta();
5247 const double sigma2deltaTheta = std::pow(pMattEff->sigmaDeltaTheta(), 2);
5248
5249 if (trajectory.prefit() != 1) {
5250 b[nperpars + 2 * scatno] -= (deltaPhi - measDeltaPhi) / sigma2deltaPhi;
5251 b[nperpars + 2 * scatno + 1] -= deltaTheta / sigma2deltaTheta;
5252 } else {
5253 b[nperpars + scatno] -= deltaTheta / sigma2deltaTheta;
5254 }
5255
5256 chi2 += (
5257 deltaPhi * deltaPhi / sigma2deltaPhi +
5258 deltaTheta * deltaTheta / sigma2deltaTheta
5259 );
5260
5261 scatno++;
5262 }
5263
5264 /*
5265 * Energy loss will be considered in the form of a kink.
5266 */
5267 if ((pMattEff != nullptr) && pMattEff->sigmaDeltaE() > 0) {
5268 double averagenergyloss = std::abs(pMattEff->deltaE());
5269 const double qoverpbrem = limitInversePValue(1000 * states[hitno]->trackParameters()->parameters()[Trk::qOverP]);
5270 const double qoverp = limitInversePValue(qoverpbrem - pMattEff->delta_p());
5271 const double pbrem = 1. / std::abs(qoverpbrem);
5272 const double p = 1. / std::abs(qoverp);
5273 const double mass = .001 * trajectory.mass();
5274 const double energy = std::sqrt(p * p + mass * mass);
5275 const double bremEnergy = std::sqrt(pbrem * pbrem + mass * mass);
5276
5277 const double resMaterial = .001 * averagenergyloss - energy + bremEnergy;
5278 res[nmeas - nbrem + bremno] = resMaterial;
5279
5280 const double sigde = pMattEff->sigmaDeltaE();
5281 const double sigdepos = pMattEff->sigmaDeltaEPos();
5282 const double sigdeneg = pMattEff->sigmaDeltaENeg();
5283
5284 double errorMaterial = .001 * pMattEff->sigmaDeltaE();
5285 error[nmeas - nbrem + bremno] = errorMaterial;
5286
5287 /*
5288 * There is already a kink in the trajectory. No need to look for more.
5289 * - Set the maxbrempull to a small value, so no future candidate can
5290 * be found.
5291 * - Reset the pointer to the state, in case we have set one before.
5292 *
5293 * NOTE: I think, the new value of maxbrempull should be -inf since it
5294 * allows for some edge case pulls. Not sure if bug or feature.
5295 */
5296 if (pMattEff->isKink()) {
5297 maxbrempull = -999999999;
5298 state_maxbrempull = nullptr;
5299 }
5300
5301 if (
5302 cache.m_asymeloss &&
5303 it > 0 &&
5304 trajectory.prefit() == 0 &&
5305 sigde > 0 &&
5306 sigde != sigdepos &&
5307 sigde != sigdeneg
5308 ) {
5309 const double elosspull = resMaterial / errorMaterial;
5310
5311 if (trajectory.mass() > 100) {
5312 /*
5313 * If the absolute energy loss pull is too large, update the
5314 * sigmaDeltaE of the state and also update the error/
5315 */
5316 if (std::abs(elosspull) > 1) {
5317 if (elosspull < -1) {
5318 pMattEff->setSigmaDeltaE(sigdepos);
5319 } else {
5320 pMattEff->setSigmaDeltaE(sigdeneg);
5321 }
5322
5323 errorMaterial = .001 * pMattEff->sigmaDeltaE();
5324 error[nmeas - nbrem + bremno] = errorMaterial;
5325 }
5326 } else if ((trajectory.numberOfTRTHits() == 0) || it >= 3) {
5327 /*
5328 * In case the state is not yet marked as a kink, we might want to
5329 * do so later. For this, we propose a maxbrempull state if either
5330 * - we did not provide an external kink with Gaudi and we want a
5331 * definitely negative elosspull.
5332 * or
5333 * - an external kink is given with Gaudi and we are on it now.
5334 */
5335 if (
5336 !pMattEff->isKink() && (
5337 (m_fixbrem == -1 && elosspull < maxbrempull) ||
5338 (m_fixbrem >= 0 && bremno == m_fixbrem)
5339 )
5340 ) {
5341 bremno_maxbrempull = bremno;
5342 state_maxbrempull = state.get();
5343 maxbrempull = elosspull;
5344 }
5345 }
5346 }
5347
5348 if (
5349 it > 0 &&
5350 hitno >= 2 &&
5351 !m_calotoolparam.empty() &&
5352 trajectory.prefit() == 0 &&
5353 pMattEff->sigmaDeltaPhi() == 0 &&
5354 pMattEff->isMeasuredEloss() &&
5355 resMaterial / (.001 * pMattEff->sigmaDeltaEAve()) > 2.5
5356 ) {
5357 const TrackParameters* parforcalo = states[hitno - 2]->trackParameters();
5358 const IPropagator* prop = &*m_propagator;
5359
5360 std::vector<MaterialEffectsOnTrack> calomeots =
5361 m_calotoolparam->extrapolationSurfacesAndEffects(
5362 *m_navigator->highestVolume(ctx),
5363 *prop,
5364 *parforcalo,
5365 parforcalo->associatedSurface(),
5367 Trk::muon);
5368
5369 /*
5370 * Update energyLoss, sigma, residual, and error if the parametrised
5371 * energy loss results in a absolute smaller pull.
5372 */
5373 if (calomeots.size() == 3) {
5374 averagenergyloss = std::abs(calomeots[1].energyLoss()->deltaE());
5375 const double newres = .001 * averagenergyloss - energy + bremEnergy;
5376 const double newerr = .001 * calomeots[1].energyLoss()->sigmaDeltaE();
5377
5378 const double oldPull = resMaterial / errorMaterial;
5379 const double newPull = newres / newerr;
5380
5381 if (std::abs(newPull) < std::abs(oldPull)) {
5382 ATH_MSG_DEBUG("Changing from measured to parametrized energy loss");
5383
5384 pMattEff->setEloss(std::unique_ptr<EnergyLoss>(calomeots[1].energyLoss()->clone()));
5385 pMattEff->setSigmaDeltaE(calomeots[1].energyLoss()->sigmaDeltaE());
5386 res[nmeas - nbrem + bremno] = newres;
5387 error[nmeas - nbrem + bremno] = newerr;
5388 }
5389 }
5390
5391 pMattEff->setMeasuredEloss(false);
5392 }
5393
5394 bremno++;
5395 }
5396 }
5397
5398 /*
5399 * Sum up the chi2 contributions from all measurements.
5400 */
5401 for (int imeas = 0; imeas < nmeas; imeas++) {
5402 if (error[imeas] == 0) {
5403 continue;
5404 }
5405
5406 chi2 += std::pow(res[imeas] / error[imeas], 2);
5407 }
5408
5409 /*
5410 * Update trajectory with previous and current chi2
5411 */
5412 trajectory.setPrevChi2(trajectory.chi2());
5413 trajectory.setChi2(chi2);
5414 }
5415
5417 const Cache & cache,
5418 GXFTrajectory & trajectory,
5419 const int it
5420 ) const {
5421 ATH_MSG_DEBUG("tryToConverge");
5422
5423 const double oldChi2 = trajectory.prevchi2();
5424 const double newChi2 = trajectory.chi2();
5425
5426 /*
5427 * First convergence check
5428 */
5429 const double nDOF = trajectory.nDOF();
5430 const double oldRedChi2 = (nDOF > 0) ? oldChi2 / nDOF : 0;
5431 const double newRedChi2 = (nDOF > 0) ? newChi2 / nDOF : 0;
5432
5433 if (
5434 trajectory.prefit() > 0 && (
5435 (newRedChi2 < 2 && it != 0) ||
5436 (newRedChi2 < oldRedChi2 + .1 && std::abs(newRedChi2 - oldRedChi2) < 1 && it != 1)
5437 )
5438 ) {
5439 trajectory.setConverged(true);
5440 }
5441
5442 /*
5443 * Second convergence check
5444 */
5445 const int nsihits = trajectory.numberOfSiliconHits();
5446 const int ntrthits = trajectory.numberOfTRTHits();
5447 const int nhits = trajectory.numberOfHits();
5448
5449 int miniter = (nsihits != 0 && nsihits + ntrthits == nhits) ? 1 : 2;
5450 miniter = std::max(miniter, cache.m_miniter);
5451
5452 if (it >= miniter && std::abs(oldChi2 - newChi2) < 1) {
5453 trajectory.setConverged(true);
5454 }
5455 }
5456
5458 GXFTrajectory & trajectory,
5459 const int bremno_maxbrempull,
5460 GXFTrackState* state_maxbrempull,
5462 ) const {
5463 ATH_MSG_DEBUG("updateSystemWithMaxBremPull");
5464
5465 if (state_maxbrempull == nullptr) {
5466 return;
5467 }
5468
5469 state_maxbrempull->materialEffects()->setSigmaDeltaE(
5470 10 * state_maxbrempull->materialEffects()->sigmaDeltaEPos()
5471 );
5472
5473 state_maxbrempull->materialEffects()->setKink(true);
5474
5475 const int nbrem = trajectory.numberOfBrems();
5476 const Amg::VectorX & res = trajectory.residuals();
5477 const int nmeas = (int) res.size();
5478
5479 Amg::VectorX & error = trajectory.errors();
5480 const double oldError = error[nmeas - nbrem + bremno_maxbrempull];
5481 const double newError = .001 * state_maxbrempull->materialEffects()->sigmaDeltaE();
5482 error[nmeas - nbrem + bremno_maxbrempull] = newError;
5483
5484 const int nFitPars = trajectory.numberOfFitParameters();
5485 if (a.cols() != nFitPars) {
5486 ATH_MSG_ERROR("Your assumption is wrong!!!!");
5487 }
5488
5489 const double errorRatio = oldError / newError;
5490 const double errorReductionRatio = 1 - std::pow(errorRatio, 2);
5491
5492 Amg::MatrixX & weightderiv = trajectory.weightedResidualDerivatives();
5493 for (int i = 0; i < nFitPars; i++) {
5494 if (weightderiv(nmeas - nbrem + bremno_maxbrempull, i) == 0) {
5495 continue;
5496 }
5497
5498 for (int j = i; j < nFitPars; j++) {
5499 const double newaij = a(i, j) - errorReductionRatio *
5500 weightderiv(nmeas - nbrem + bremno_maxbrempull, i) *
5501 weightderiv(nmeas - nbrem + bremno_maxbrempull, j);
5502
5503 a.fillSymmetric(i, j, newaij);
5504 }
5505 weightderiv(nmeas - nbrem + bremno_maxbrempull, i) *= errorRatio;
5506 }
5507 }
5508
5510 GXFTrajectory & trajectory
5511 ) const {
5512 ATH_MSG_DEBUG("fillDerivatives");
5513
5514 std::vector<std::unique_ptr<GXFTrackState>> & states = trajectory.trackStates();
5515 int scatno = 0;
5516 int bremno = 0;
5517 int measno = 0;
5518 const int nscatupstream = trajectory.numberOfUpstreamScatterers();
5519 const int nbremupstream = trajectory.numberOfUpstreamBrems();
5520 const int nscat = trajectory.numberOfScatterers();
5521 const int nbrem = trajectory.numberOfBrems();
5522 const int nperparams = trajectory.numberOfPerigeeParameters();
5523
5524 Amg::MatrixX & weightderiv = trajectory.weightedResidualDerivatives();
5525 Amg::VectorX & error = trajectory.errors();
5526
5527 const int nmeas = (int) weightderiv.rows();
5528
5529 for (std::unique_ptr<GXFTrackState> & state : states) {
5530 if (state->getStateType(TrackStateOnSurface::Measurement)) {
5531 TrackState::MeasurementType const hittype = state->measurementType();
5532 const MeasurementBase *measbase = state->measurement();
5533 const auto [scatmin, scatmax] = std::minmax(scatno, nscatupstream);
5534 const auto [bremmin, bremmax] = std::minmax(bremno, nbremupstream);
5535
5536 Amg::MatrixX & derivatives = state->derivatives();
5537
5538 /*
5539 * Get the stereo angles for SCT and TGC.
5540 */
5541 const double sinStereo =
5542 hittype == TrackState::SCT || hittype == TrackState::TGC ?
5543 state->sinStereo() :
5544 0;
5545 const double cosStereo =
5546 sinStereo != 0 ?
5547 std::sqrt(1 - std::pow(sinStereo, 2)) :
5548 1.;
5549
5550 /*
5551 * For SCT and TGC we need modified derivatives, taking into account
5552 * the orientation.This lambda chooses the correct accessor and rotates
5553 * the derivative accordingly.
5554 */
5555 auto getThisDeriv = [sinStereo, cosStereo, &derivatives](int i, int j) -> double {
5556 if (i == 0 && sinStereo != 0) {
5557 return derivatives(0, j) * cosStereo + sinStereo * derivatives(1, j);
5558 } else {
5559 return derivatives(i, j);
5560 }
5561 };
5562
5563 for (int i = 0; i < 5; i++) {
5565 continue;
5566 }
5567
5568 /*
5569 * SCT and TGC have all information stored in the first parameter.
5570 */
5571 if ((hittype == TrackState::SCT || hittype == TrackState::TGC) && i > 0) {
5572 break;
5573 }
5574
5575 if (trajectory.numberOfPerigeeParameters() > 0) {
5576 const int cols = trajectory.m_straightline ? 4 : 5;
5577
5578 if (i == 0 && sinStereo != 0) {
5579 weightderiv.row(measno).head(cols) =
5580 (derivatives.row(0).head(cols) * cosStereo +
5581 sinStereo * derivatives.row(1).head(cols)) /
5582 error[measno];
5583 } else {
5584 weightderiv.row(measno).head(cols) = derivatives.row(i).head(cols) / error[measno];
5585 }
5586 }
5587
5588 for (int j = scatmin; j < scatmax; j++) {
5589 if (trajectory.prefit() == 1) {
5590 const int index = nperparams + j;
5591 weightderiv(measno, index) = getThisDeriv(i, index) / error[measno];
5592 } else {
5593 const int index = nperparams + 2 * j;
5594 weightderiv(measno, index) = getThisDeriv(i, index) / error[measno];
5595 weightderiv(measno, index + 1) = getThisDeriv(i, index + 1) / error[measno];
5596 }
5597 }
5598
5599 for (int j = bremmin; j < bremmax; j++) {
5600 const int index = j + nperparams + 2 * nscat;
5601 weightderiv(measno, index) = getThisDeriv(i, index) / error[measno];
5602 }
5603
5604 measno++;
5605 }
5606 } else if (state->getStateType(TrackStateOnSurface::Outlier)) {
5607 double *errors = state->measurementErrors();
5608 for (int i = 0; i < 5; i++) {
5609 if (errors[i] > 0) {
5610 measno++;
5611 }
5612 }
5613 } else if (
5614 state->getStateType(TrackStateOnSurface::Scatterer) &&
5615 ((trajectory.prefit() == 0) || state->materialEffects()->deltaE() == 0)
5616 ) {
5617 scatno++;
5618 }
5619
5620 if ((state->materialEffects() != nullptr) && state->materialEffects()->sigmaDeltaE() > 0) {
5621 //limit values to avoid FPE overflow or div by zero
5622 const double qoverpbrem = limitInversePValue(1000 * state->trackParameters()->parameters()[Trk::qOverP]);
5623 const double qoverp = limitInversePValue(qoverpbrem - state->materialEffects()->delta_p());
5624
5625 const double mass = .001 * trajectory.mass();
5626
5627 const auto thisMeasurementIdx{nmeas - nbrem + bremno};
5628 //references (courtesy of Christos Anastopoulos):
5629 //https://inspirehep.net/files/a810ad0047a22af32fbff587c6c98ce5
5630 //https://its.cern.ch/jira/browse/ATLASRECTS-6213
5631 auto multiplier = [] (double mass, double qOverP){
5632 return std::copysign(1./(qOverP * qOverP * std::sqrt(1. + mass * mass * qOverP * qOverP)), qOverP);
5633 };
5634 const auto qoverpTerm {multiplier(mass, qoverp) / error[thisMeasurementIdx]};
5635 const auto qoverpBremTerm {multiplier(mass, qoverpbrem) / error[thisMeasurementIdx]};
5636
5637 if (trajectory.numberOfPerigeeParameters() > 0) {
5638 weightderiv(thisMeasurementIdx, 4) = qoverpBremTerm - qoverpTerm;
5639 }
5640 //
5641 const auto bremNoBase = nperparams + 2 * nscat;
5642 if (bremno < nbremupstream) {
5643 weightderiv(thisMeasurementIdx, bremNoBase + bremno) = qoverpTerm;
5644 for (int bremno2 = bremno + 1; bremno2 < nbremupstream; bremno2++) {
5645 weightderiv(thisMeasurementIdx, bremNoBase + bremno2) = qoverpTerm - qoverpBremTerm;
5646 }
5647 } else {
5648 weightderiv(thisMeasurementIdx, bremNoBase + bremno) = qoverpBremTerm;
5649 for (int bremno2 = nbremupstream; bremno2 < bremno; bremno2++) {
5650 weightderiv(thisMeasurementIdx, bremNoBase + bremno2) = qoverpBremTerm - qoverpTerm;
5651 }
5652 }
5653 bremno++;
5654 }
5655 }
5656 }
5657
5659 Cache & cache,
5660 GXFTrajectory & trajectory
5661 ) {
5662 const int nFitPars = trajectory.numberOfFitParameters();
5663 const int nPerPars = trajectory.numberOfPerigeeParameters();
5664 const int nScatPars = 2 * trajectory.numberOfScatterers();
5665 const int nBrem = trajectory.numberOfBrems();
5666 const int nUpstreamStates = trajectory.numberOfUpstreamStates();
5667
5668 const Amg::VectorX & res = trajectory.residuals();
5669 const int nMeas = (int) res.size();
5670
5671 cache.m_firstmeasurement.resize(nFitPars);
5672 cache.m_lastmeasurement.resize(nFitPars);
5673
5674 for (int i = 0; i < nPerPars; i++) {
5675 cache.m_firstmeasurement[i] = 0;
5676 cache.m_lastmeasurement[i] = nMeas - nBrem;
5677 }
5678
5679 int measno = 0;
5680 int scatno = 0;
5681 int bremno = 0;
5682 for (int i = 0; i < (int) trajectory.trackStates().size(); i++) {
5683 const std::unique_ptr<GXFTrackState> & state = trajectory.trackStates()[i];
5684 const GXFMaterialEffects *meff = state->materialEffects();
5685
5686 if (meff == nullptr) {
5687 measno += state->numberOfMeasuredParameters();
5688 continue;
5689 }
5690
5691 const int firstMeasurement = i < nUpstreamStates ? 0 : measno;
5692 const int lastMeasurement = i < nUpstreamStates ? measno : nMeas - nBrem;
5693
5694 if (meff->sigmaDeltaTheta() != 0
5695 && (trajectory.prefit() == 0 || meff->deltaE() == 0)) {
5696 const int scatterPos = nPerPars + 2 * scatno;
5697
5698 cache.m_firstmeasurement[scatterPos] = firstMeasurement;
5699 cache.m_lastmeasurement[scatterPos] = lastMeasurement;
5700
5701 cache.m_firstmeasurement[scatterPos + 1] = firstMeasurement;
5702 cache.m_lastmeasurement[scatterPos + 1] = lastMeasurement;
5703
5704 scatno++;
5705 }
5706
5707 if (meff->sigmaDeltaE() > 0) {
5708 const int bremPos = nPerPars + nScatPars + bremno;
5709
5710 cache.m_firstmeasurement[bremPos] = firstMeasurement;
5711 cache.m_lastmeasurement[bremPos] = lastMeasurement;
5712
5713 bremno++;
5714 }
5715 }
5716 }
5717
5719 const Cache & cache,
5720 GXFTrajectory & trajectory,
5721 Amg::VectorX & b
5722 ) {
5723 const int nFitPars = trajectory.numberOfFitParameters();
5724 const int nPerPars = trajectory.numberOfPerigeeParameters();
5725 const int nScatPars = 2 * trajectory.numberOfScatterers();
5726 const int nBrem = trajectory.numberOfBrems();
5727 const Amg::MatrixX & weightDeriv = trajectory.weightedResidualDerivatives();
5728
5729 const Amg::VectorX & res = trajectory.residuals();
5730 const Amg::VectorX & error = trajectory.errors();
5731
5732 const int nMeas = (int) res.size();
5733
5734 for (int k = 0; k < nFitPars; k++) {
5735 const int minMeasK = cache.m_firstmeasurement[k];
5736 const int maxMeasK = cache.m_lastmeasurement[k];
5737
5738 /*
5739 * NOTE: It is necessary to do r * invError * weight instead of doing
5740 * r / error * w. Otherwise, the implementation tests fail do to
5741 * numerical reasons.
5742 */
5743 for (int measno = minMeasK; measno < maxMeasK; measno++) {
5744 b[k] += res[measno] * (1. / error[measno]) * weightDeriv(measno, k);
5745 }
5746
5747 /*
5748 * For qOverP and brems, we also have a contribution to brems elements.
5749 *
5750 * NOTE: It is necessary to do r * invError * weight instead of doing
5751 * r / error * w. Otherwise, the implementation tests fail do to
5752 * numerical reasons.
5753 */
5754 if (k == 4 || k >= nPerPars + nScatPars) {
5755 for (int measno = nMeas - nBrem; measno < nMeas; measno++) {
5756 b[k] += res[measno] * (1. / error[measno]) * weightDeriv(measno, k);
5757 }
5758 }
5759 }
5760 }
5761
5763 const Cache & cache,
5764 GXFTrajectory & trajectory,
5766 ) {
5767 const int nFitPars = trajectory.numberOfFitParameters();
5768 const Amg::MatrixX & weightDeriv = trajectory.weightedResidualDerivatives();
5769
5770 for (int k = 0; k < nFitPars; k++) {
5771 for (int l = k; l < nFitPars; l++) {
5772 const int minMeas = std::max(cache.m_firstmeasurement[k], cache.m_firstmeasurement[l]);
5773 const int maxMeas = std::min(cache.m_lastmeasurement[k], cache.m_lastmeasurement[l]);
5774
5775 double a_kl = 0;
5776 for (int measno = minMeas; measno < maxMeas; measno++) {
5777 a_kl += weightDeriv(measno, k) * weightDeriv(measno, l);
5778 }
5779
5780 a.fillSymmetric(l, k, a_kl);
5781 }
5782 }
5783 }
5784
5786 GXFTrajectory & trajectory,
5788 ) {
5789 const int nFitPars = trajectory.numberOfFitParameters();
5790 const int nPerPars = trajectory.numberOfPerigeeParameters();
5791 const int nScatPars = 2 * trajectory.numberOfScatterers();
5792 const int nBrem = trajectory.numberOfBrems();
5793 const Amg::MatrixX & weightDeriv = trajectory.weightedResidualDerivatives();
5794
5795 const Amg::VectorX & res = trajectory.residuals();
5796 const auto & scatSigmas = trajectory.scatteringSigmas();
5797
5798 const int nMeas = (int) res.size();
5799
5800 int scatno = 0;
5801
5802 /*
5803 * Direct contribution on the diagonal from the scatterer itself.
5804 */
5805 for (int k = nPerPars; k < nPerPars + nScatPars; k += 2) {
5806 a(k, k) += 1. / std::pow(scatSigmas[scatno].first, 2);
5807 a(k + 1, k + 1) += 1. / std::pow(scatSigmas[scatno].second, 2);
5808
5809 scatno++;
5810 }
5811
5812 /*
5813 * Indirect contribution on the qOverP and brems derivatives.
5814 */
5815 for (int measno = nMeas - nBrem; measno < nMeas; measno++) {
5816 for (int k = 4; k < nFitPars; k++) {
5817 if (k == 5) {
5818 k = nPerPars + nScatPars;
5819 }
5820
5821 for (int l = k; l < nFitPars; l++) {
5822 if (l == 5) {
5823 l = nPerPars + nScatPars;
5824 }
5825
5826 const double a_kl = a(l, k) + weightDeriv(measno, k) * weightDeriv(measno, l);
5827 a.fillSymmetric(l, k, a_kl);
5828 }
5829 }
5830 }
5831 }
5832
5834 Cache & cache,
5835 GXFTrajectory & trajectory,
5837 const bool doDeriv,
5838 const int it,
5839 const double oldRedChi2,
5840 const double newRedChi2
5841 ) {
5842 const int nPerPars = trajectory.numberOfPerigeeParameters();
5843
5844 /*
5845 * The return value collects, if any weights changed while looping over all
5846 * material states.
5847 */
5848 bool weightChanged = false;
5849
5850 /*
5851 * The weights for the diagonal material components in the [a]-matrix
5852 * depend on how far we are in the iteration process (iteration number or
5853 * chi2 convergence).
5854 */
5855 double newPhiWeight = 1.1;
5856 double newThetaWeight = 1.001;
5857 if (trajectory.prefit() == 0) {
5858 /*
5859 * We do not consider theta at all in the prefit 0 case. Therefore, we do
5860 * not need to adjust the theta weights.
5861 */
5862 if (it == 0) {
5863 newPhiWeight = 1.00000001;
5864 } else if (it == 1) {
5865 newPhiWeight = 1.0000001;
5866 } else if (it <= 3) {
5867 newPhiWeight = 1.0001;
5868 } else if (it <= 6) {
5869 newPhiWeight = 1.01;
5870 }
5871 } else {
5872 if (newRedChi2 > oldRedChi2 - 1 && newRedChi2 < oldRedChi2) {
5873 newPhiWeight = 1.0001;
5874 newThetaWeight = 1.0001;
5875 } else if (newRedChi2 > oldRedChi2 - 25 && newRedChi2 < oldRedChi2) {
5876 newPhiWeight = 1.001;
5877 newThetaWeight = 1.0001;
5878 }
5879 }
5880
5881 /*
5882 * Counter for the scattering states. We cannot directly loop over them.
5883 */
5884 std::size_t scatno = 0;
5885
5886 /*
5887 * Loop over all track states. Skip states without material effects.
5888 */
5889 for (const auto & state : trajectory.trackStates()) {
5890 const GXFMaterialEffects *meff = state->materialEffects();
5891
5892 if (meff == nullptr) {
5893 continue;
5894 }
5895
5896 const bool isValidPlaneSurface =
5898 static_cast<const PlaneSurface *>(&state->associatedSurface()) != nullptr;
5899
5900 /*
5901 * Modify the diagonal material elements in the [a]-matrix.
5902 */
5903 if (meff->deltaE() == 0 || (trajectory.prefit() == 0 && isValidPlaneSurface)) {
5904 weightChanged = true;
5905
5906 const int scatNoIndex = 2 * scatno + nPerPars;
5907
5908 if (trajectory.prefit() == 0 && meff->sigmaDeltaPhi() != 0) {
5909 if (scatno >= cache.m_phiweight.size()) {
5910 std::stringstream message;
5911 message << "scatno is out of range " << scatno << " !< " << cache.m_phiweight.size();
5912 throw std::range_error(message.str());
5913 }
5914
5915 /*
5916 * In case, no derivative is necessary, the weight will be
5917 * effectively replaced by the relative weight change
5918 */
5919 if (!doDeriv) {
5920 a(scatNoIndex, scatNoIndex) /= cache.m_phiweight[scatno];
5921 }
5922
5923 cache.m_phiweight[scatno] = newPhiWeight;
5924 a(scatNoIndex, scatNoIndex) *= newPhiWeight;
5925 } else if (trajectory.prefit() >= 2) {
5926 a(scatNoIndex, scatNoIndex) *= newPhiWeight;
5927 a(scatNoIndex + 1, scatNoIndex + 1) *= newThetaWeight;
5928 }
5929 }
5930
5931 /*
5932 * The state is a valid scatterer even, if not considered in the
5933 * modification of the weights before. Therefore increment the count.
5934 *
5935 * NOTE: It is not clear, why this check is not at the beginning of the
5936 * loop. This way, a mismatch in the state counting could happen.
5937 */
5938 if (
5939 meff->sigmaDeltaPhi() != 0 &&
5940 (trajectory.prefit() == 0 || meff->deltaE() == 0)
5941 ) {
5942 scatno++;
5943 }
5944 }
5945
5946 /*
5947 * Add a weight to the qOverP component of the [a]-matrix if a set of
5948 * pre-conditions are met and the reduced chi2 either
5949 * - converges very fast (e.g. at the beginning of the fit)
5950 * OR
5951 * - gets larger (e.g. moving away from minimum or overshooting by a lot)
5952 */
5953 if (
5954 trajectory.prefit() == 2 &&
5955 doDeriv &&
5956 trajectory.numberOfBrems() > 0 &&
5957 (newRedChi2 < oldRedChi2 - 25 || newRedChi2 > oldRedChi2)
5958 ) {
5959 a(4, 4) *= 1.001;
5960 }
5961
5962 return weightChanged;
5963 }
5964
5966 Cache & cache,
5967 GXFTrajectory & trajectory,
5969 ) {
5970 const int nPerPars = trajectory.numberOfPerigeeParameters();
5971 std::size_t scatno = 0;
5972
5973 for (auto & state : trajectory.trackStates()) {
5974 const GXFMaterialEffects *meff = state->materialEffects();
5975
5976 if (meff == nullptr || meff->sigmaDeltaPhi() == 0) {
5977 continue;
5978 }
5979
5980 if (scatno >= cache.m_phiweight.size()) {
5981 std::stringstream message;
5982 message << "scatno is out of range " << scatno << " !< " << cache.m_phiweight.size();
5983 throw std::range_error(message.str());
5984 }
5985
5986 const bool isValidPlaneSurface =
5988 static_cast<const PlaneSurface *>(&state->associatedSurface()) != nullptr;
5989
5990 if (meff->deltaE() == 0 || isValidPlaneSurface) {
5991 const int scatNoIndex = 2 * scatno + nPerPars;
5992 a(scatNoIndex, scatNoIndex) /= cache.m_phiweight[scatno];
5993 cache.m_phiweight[scatno] = 1;
5994 }
5995
5996 /*
5997 * NOTE: We already check for this in the beginning of the loop. Is
5998 * there any way, this can change?
5999 */
6000 if (meff->sigmaDeltaPhi() != 0) {
6001 scatno++;
6002 }
6003 }
6004 }
6005
6007 const EventContext& ctx,
6008 Cache & cache,
6009 GXFTrajectory & trajectory,
6010 const int it,
6012 Amg::VectorX & b,
6013 Amg::SymMatrixX & lu,
6014 bool & doDeriv
6015 ) const {
6016 const int nDOFold = trajectory.nDOF();
6017 const double oldChi2 = trajectory.chi2();
6018 const double oldRedChi2 = nDOFold > 0 ? oldChi2 / nDOFold : 0;
6019
6020 if (cache.m_phiweight.empty()) {
6021 cache.m_phiweight.assign(trajectory.trackStates().size(), 1);
6022 }
6023
6024 FitterStatusCode fsc = calculateTrackParameters(ctx, trajectory, doDeriv);
6025
6026 if (fsc != FitterStatusCode::Success) {
6027 return fsc;
6028 }
6029
6030 /*
6031 * Reset the b-vector. We want to add to the components later.
6032 */
6033 b.setZero();
6034
6035 /*
6036 * Here we store the information on where to find the maxbrempull, in case
6037 * we find any large ones during the residual calculation. We might need it
6038 * later to update our errors.
6039 */
6040 int bremno_maxbrempull = 0;
6041 GXFTrackState* state_maxbrempull = nullptr;
6042
6043 fillResidualsAndErrors(ctx, cache, trajectory, it, b, bremno_maxbrempull, state_maxbrempull);
6044
6045 /*
6046 * Check if we hit any convergence conditions.
6047 */
6048 tryToConverge(cache, trajectory, it);
6049
6050 /*
6051 * In case we converged but have a state with maxbrempull (a kink) we want
6052 * to do more iterations. Therefore, reset the convergence flag and inflate
6053 * the chi2. Then update the error estimates using the state with the
6054 * maxbrempull.
6055 */
6056 if ((state_maxbrempull != nullptr) && trajectory.converged()) {
6057 trajectory.setConverged(false);
6058 trajectory.setChi2(1e15);
6059 doDeriv = true;
6060
6061 updateSystemWithMaxBremPull(trajectory, bremno_maxbrempull, state_maxbrempull, a);
6062 lu = a;
6063 }
6064
6065 const int nDOFnew = trajectory.nDOF();
6066 const double newChi2 = trajectory.chi2();
6067 const double newRedChi2 = nDOFnew > 0 ? newChi2 / nDOFnew : 0;
6068
6069 ATH_MSG_DEBUG("old chi2: " << oldChi2 << "/" << nDOFold << "=" << oldRedChi2 <<
6070 ", new chi2: " << newChi2 << "/" << nDOFnew << "=" << newRedChi2);
6071
6072 if (trajectory.prefit() > 0 && trajectory.converged()) {
6074 }
6075
6076 if (doDeriv) {
6077 calculateDerivatives(trajectory);
6078 fillDerivatives(trajectory);
6079 }
6080
6081 if (cache.m_firstmeasurement.empty()) {
6082 fillFirstLastMeasurement(cache, trajectory);
6083 }
6084
6085 if (a.cols() != trajectory.numberOfFitParameters()) {
6086 ATH_MSG_ERROR("Your assumption is wrong!!!!");
6087 }
6088
6089 fillBfromMeasurements(cache, trajectory, b);
6090
6091 /*
6092 * The [a]-matrix does not depend on the residuals. We only need to change
6093 * it, if the derivatives have changed.
6094 */
6095 if (doDeriv) {
6096 fillAfromMeasurements(cache, trajectory, a);
6097 fillAfromScatterers(trajectory, a);
6098 }
6099
6100 const bool weightChanged = tryToWeightAfromMaterial(cache, trajectory, a, doDeriv, it, oldRedChi2, newRedChi2);
6101
6102 /*
6103 * Update the [lu]-matrix if we modified the [a]-matrix.
6104 */
6105 if (doDeriv || weightChanged) {
6106 lu = a;
6107 }
6108
6109 /*
6110 * Special handling for prefit == 0:
6111 * - If we already converged, but there are hits apart from Si and TRT or
6112 * the numbers don't match, the applied phi weights need to be reset.
6113 * - If we got in an early iteration to a low reduced chi2 or converged
6114 * with the reduced chi2, we don't need to redo derivatives.
6115 */
6116 if (trajectory.prefit() == 0) {
6117 if (trajectory.converged()) {
6118 const int nSiHits = trajectory.numberOfSiliconHits();
6119 const int nTrtHits = trajectory.numberOfTRTHits();
6120 const int nHits = trajectory.numberOfHits();
6121
6122 if (nSiHits + nTrtHits != nHits) {
6123 compensatePhiWeights(cache, trajectory, a);
6124 lu = a;
6125 }
6126 } else if (
6127 !m_redoderivs &&
6128 it < 5 &&
6129 (newRedChi2 < 2 || (newRedChi2 < oldRedChi2 && newRedChi2 > oldRedChi2 - .5))
6130 ) {
6131 doDeriv = false;
6132 }
6133 }
6134
6136 }
6137
6139 GXFTrajectory & trajectory,
6140 const Amg::VectorX & b,
6141 const Amg::SymMatrixX & lu_m
6142 ) const {
6143 ATH_MSG_DEBUG("UpdateFitParameters");
6144
6145 /*
6146 * Compute the parameter update from [llt] * deltaParameters = b.
6147 * In case we cannot do a Cholesky decomposition, we do not update and
6148 * use an early return.
6149 * TODO: Investigate, if it is really Success, if we do not update.
6150 */
6151 Eigen::LLT<Eigen::MatrixXd> const llt(lu_m);
6152
6153 if (llt.info() != Eigen::Success) {
6155 }
6156
6157 const Amg::VectorX deltaParameters = llt.solve(b);
6158
6159 /*
6160 * Collect the number of each parameter type for the offsets in the
6161 * deltaParameters vector.
6162 */
6163 const int nscat = trajectory.numberOfScatterers();
6164 const int nbrem = trajectory.numberOfBrems();
6165 const int nperparams = trajectory.numberOfPerigeeParameters();
6166
6167 /*
6168 * Update the perigee parameters.
6169 * The parameters are not modified in place. In case the angles are pushed
6170 * too far and cannot be corrected anymore, the parameters should not be
6171 * updated and the fit should fail.
6172 *
6173 * NOTE: It is not clear if the fit should fail for fitter reasons or
6174 * because the angle correction is not stable enough.
6175 */
6176 const TrackParameters *refpar = trajectory.referenceParameters();
6177 double d0 = refpar->parameters()[Trk::d0];
6178 double z0 = refpar->parameters()[Trk::z0];
6179 double phi = refpar->parameters()[Trk::phi0];
6180 double theta = refpar->parameters()[Trk::theta];
6181 double qoverp = refpar->parameters()[Trk::qOverP];
6182
6183 if (nperparams > 0) {
6184 d0 += deltaParameters[0];
6185 z0 += deltaParameters[1];
6186 phi += deltaParameters[2];
6187 theta += deltaParameters[3];
6188 qoverp = (trajectory.m_straightline) ? 0 : .001 * deltaParameters[4] + qoverp;
6189 }
6190
6191 if (!correctAngles(phi, theta)) {
6192 ATH_MSG_DEBUG("angles out of range: " << theta << " " << phi);
6193 ATH_MSG_DEBUG("Fit failed");
6195 }
6196
6197 /*
6198 * Update the scattering angles.
6199 */
6200 std::vector < std::pair < double, double >>&scatangles = trajectory.scatteringAngles();
6201 for (int i = 0; i < nscat; i++) {
6202 scatangles[i].first += deltaParameters[2 * i + nperparams];
6203 scatangles[i].second += deltaParameters[2 * i + nperparams + 1];
6204 }
6205
6206 /*
6207 * Update the brems.
6208 */
6209 std::vector < double >&delta_ps = trajectory.brems();
6210 for (int i = 0; i < nbrem; i++) {
6211 delta_ps[i] += deltaParameters[nperparams + 2 * nscat + i];
6212 }
6213
6214 /*
6215 * Create new peregee parameters from the updated ones.
6216 */
6217 std::unique_ptr<const TrackParameters> newper(
6219 d0, z0, phi, theta, qoverp, std::nullopt
6220 )
6221 );
6222
6223 /*
6224 * Apply all changes.
6225 */
6226 trajectory.setReferenceParameters(std::move(newper));
6227 trajectory.setScatteringAngles(scatangles);
6228 trajectory.setBrems(delta_ps);
6229
6231 }
6232
6234 GXFTrajectory & trajectory,
6236 Amg::VectorX & b,
6237 const EventContext& evtctx
6238 ) const{
6239 if ( trajectory.numberOfSiliconHits() == 0) {
6240 return;
6241 }
6242
6243 if ( m_clusterSplitProbContainer.empty() ){
6244 return;
6245 }
6246
6248 if (!splitProbContainer.isValid()) {
6249 ATH_MSG_FATAL("Failed to get cluster splitting probability container " << m_clusterSplitProbContainer);
6250 }
6251
6252 std::vector<std::unique_ptr<GXFTrackState>> & states = trajectory.trackStates();
6253 Amg::VectorX & res = trajectory.residuals();
6254 Amg::VectorX & err = trajectory.errors();
6255 Amg::MatrixX & weightderiv = trajectory.weightedResidualDerivatives();
6256 const int nfitpars = trajectory.numberOfFitParameters();
6257
6258 int measno = 0;
6259 for (size_t stateno = 0; stateno < states.size(); stateno++) {
6260
6261 // Increment the measurement counter everytime we have crossed a measurement/outlier surface
6262 if ( stateno > 0 && ( states[stateno-1]->getStateType(TrackStateOnSurface::Measurement) ||
6263 states[stateno-1]->getStateType(TrackStateOnSurface::Outlier) ) ) {
6264 measno += states[stateno-1]->numberOfMeasuredParameters();
6265 }
6266
6267 std::unique_ptr<GXFTrackState> & state = states[stateno];
6268 if (!state->getStateType(TrackStateOnSurface::Measurement)) {
6269 continue;
6270 }
6271
6272 TrackState::MeasurementType const hittype = state->measurementType();
6273 if (hittype != TrackState::Pixel) {
6274 continue;
6275 }
6276
6277 const PrepRawData *prd{};
6278 if (const auto *const pMeas = state->measurement(); pMeas->type(Trk::MeasurementBaseType::RIO_OnTrack)){
6279 const auto *const rot = static_cast<const RIO_OnTrack *>(pMeas);
6280 prd = rot->prepRawData();
6281 }
6282
6283 if(!prd)
6284 continue;
6285
6287 continue;
6288 }
6289 const InDet::PixelCluster* pixelCluster = static_cast<const InDet::PixelCluster*> ( prd );
6290 const auto &splitProb = splitProbContainer->splitProbability(pixelCluster);
6291 if (!splitProb.isSplit()) {
6292 ATH_MSG_DEBUG( "Pixel cluster is not split so no need to update" );
6293 continue;
6294 }
6295
6296 std::unique_ptr < const RIO_OnTrack > newrot;
6297 double *olderror = state->measurementErrors();
6298 const TrackParameters *trackpars = state->trackParameters();
6299
6300 double newerror[5] = {-1,-1,-1,-1,-1};
6301 double newres[2] = {-1,-1};
6302
6303 newrot.reset(m_ROTcreator->correct(*prd, *trackpars, evtctx));
6304
6305 if(!newrot)
6306 continue;
6307
6308 const Amg::MatrixX & covmat = newrot->localCovariance();
6309
6310 newerror[0] = std::sqrt(covmat(0, 0));
6311 newres[0] = newrot->localParameters()[Trk::locX] - trackpars->parameters()[Trk::locX];
6312 newerror[1] = std::sqrt(covmat(1, 1));
6313 newres[1] = newrot->localParameters()[Trk::locY] - trackpars->parameters()[Trk::locY];
6314
6315 if (a.cols() != nfitpars) {
6316 ATH_MSG_ERROR("Your assumption is wrong!!!!");
6317 }
6318
6319 //loop over both measurements -- treated as uncorrelated
6320 for( int k =0; k<2; k++ ){
6321 const double oldres = res[measno+k];
6322 res[measno+k] = newres[k];
6323 err[measno+k] = newerror[k];
6324
6325 for (int i = 0; i < nfitpars; i++) {
6326 if (weightderiv(measno+k, i) == 0) {
6327 continue;
6328 }
6329
6330 b[i] -= weightderiv(measno+k, i) * (oldres / olderror[k] - (newres[k] * olderror[k]) / (newerror[k] * newerror[k]));
6331
6332 for (int j = i; j < nfitpars; j++) {
6333 a.fillSymmetric(
6334 i, j,
6335 a(i, j) + (
6336 weightderiv(measno+k, i) *
6337 weightderiv(measno+k, j) *
6338 ((olderror[k] * olderror[k]) / (newerror[k] * newerror[k]) - 1)
6339 )
6340 );
6341 }
6342 weightderiv(measno+k, i) *= olderror[k] / newerror[k];
6343 }
6344 }
6345
6346 state->setMeasurement(std::move(newrot));
6347 state->setMeasurementErrors(newerror);
6348
6349 }// end for
6350 }
6351
6352
6354 Cache & cache,
6355 GXFTrajectory & trajectory,
6357 Amg::VectorX & b,
6358 Amg::SymMatrixX & lu_m,
6359 bool runOutlier,
6360 bool trtrecal,
6361 int it,
6362 const EventContext& ctx
6363 ) const {
6364 double scalefactor = m_scalefactor;
6365
6366 if (it == 1 && trajectory.numberOfSiliconHits() + trajectory.numberOfTRTHits() == trajectory.numberOfHits()) {
6367 scalefactor *= 2;
6368 }
6369
6370 std::vector<std::unique_ptr<GXFTrackState>> & states = trajectory.trackStates();
6371 Amg::VectorX & res = trajectory.residuals();
6372 Amg::VectorX & err = trajectory.errors();
6373 Amg::MatrixX & weightderiv = trajectory.weightedResidualDerivatives();
6374 const int nfitpars = trajectory.numberOfFitParameters();
6375
6376 if (a.cols() != nfitpars) {
6377 ATH_MSG_ERROR("Your assumption is wrong!!!!");
6378 }
6379
6380 const int nperpars = trajectory.numberOfPerigeeParameters();
6381 const int nscats = trajectory.numberOfScatterers();
6382 int hitno = 0;
6383 int measno = 0;
6384 bool outlierremoved = false;
6385 bool hitrecalibrated = false;
6386
6387 for (int stateno = 0; stateno < (int) states.size(); stateno++) {
6388 std::unique_ptr<GXFTrackState> & state = states[stateno];
6389
6390 if (state->getStateType(TrackStateOnSurface::Measurement)) { // Hit is not (yet) an outlier
6391 TrackState::MeasurementType const hittype = state->measurementType();
6392
6393 if (hittype == TrackState::TRT) {
6394 if (
6395 runOutlier &&
6396 std::abs(state->trackParameters()->parameters()[Trk::driftRadius]) > 1.05 * state->associatedSurface().bounds().r()
6397 ) {
6398 ATH_MSG_DEBUG("Removing TRT hit #" << hitno);
6399
6400 trajectory.setOutlier(stateno);
6401 outlierremoved = true;
6402
6403 double *errors = state->measurementErrors();
6404 const double olderror = errors[0];
6405
6406 trajectory.updateTRTHitCount(stateno, olderror);
6407
6408 for (int i = 0; i < nfitpars; i++) {
6409 if (weightderiv(measno, i) == 0) {
6410 continue;
6411 }
6412
6413 b[i] -= res[measno] * weightderiv(measno, i) / olderror;
6414
6415 for (int j = i; j < nfitpars; j++) {
6416 a.fillSymmetric(
6417 i, j,
6418 a(i, j) - weightderiv(measno, i) * weightderiv(measno, j)
6419 );
6420 }
6421 weightderiv(measno, i) = 0;
6422 }
6423
6424 res[measno] = 0;
6425 } else if (trtrecal) {
6426 double *errors = state->measurementErrors();
6427 const double olderror = errors[0];
6428 const Trk::RIO_OnTrack * oldrot{};
6429 const auto *const thisMeasurement{state->measurement()};
6430 if ( not thisMeasurement->type(Trk::MeasurementBaseType::RIO_OnTrack)){
6431 continue;
6432 }
6433 oldrot = static_cast<const Trk::RIO_OnTrack *>(thisMeasurement);
6434 const double oldradius = oldrot->localParameters()[Trk::driftRadius];
6435 if (oldrot->prepRawData() != nullptr) {
6436 const double dcradius = oldrot->prepRawData()->localPosition()[Trk::driftRadius];
6437 const double dcerror = std::sqrt(oldrot->prepRawData()->localCovariance()(Trk::driftRadius, Trk::driftRadius));
6438 const double trackradius = state->trackParameters()->parameters()[Trk::driftRadius];
6439
6440 std::unique_ptr<const Trk::RIO_OnTrack> newrot = nullptr;
6441 const double distance = std::abs(std::abs(trackradius) - dcradius);
6442
6443 if (distance < scalefactor * dcerror && (olderror > 1. || trackradius * oldradius < 0)) {
6444 newrot.reset(m_ROTcreator->correct(*oldrot->prepRawData(), *state->trackParameters(), ctx));
6445 } else if (distance > scalefactor * dcerror && olderror < 1.) {
6446 newrot.reset(m_broadROTcreator->correct(*oldrot->prepRawData(), *state->trackParameters(), ctx));
6447 }
6448
6449 if (newrot != nullptr) {
6450 ATH_MSG_DEBUG("Recalibrating TRT hit #" << hitno);
6451 hitrecalibrated = true;
6452 const double newradius = newrot->localParameters()[Trk::driftRadius];
6453 const double newerror = std::sqrt(newrot->localCovariance()(Trk::driftRadius, Trk::driftRadius));
6454
6455 if ((measno < 0) or (measno >= (int) res.size())) {
6456 throw std::runtime_error(
6457 "'res' array index out of range in TrkGlobalChi2Fitter/src/GlobalChi2Fitter.cxx:" + std::to_string(__LINE__)
6458 );
6459 }
6460
6461 const double oldres = res[measno];
6462 const double newres = newradius - state->trackParameters()->parameters()[Trk::driftRadius];
6463 errors[0] = newerror;
6464 state->setMeasurement(std::move(newrot));
6465
6466 trajectory.updateTRTHitCount(stateno, olderror);
6467
6468 for (int i = 0; i < nfitpars; i++) {
6469 if (weightderiv(measno, i) == 0) {
6470 continue;
6471 }
6472
6473 b[i] -= weightderiv(measno, i) * (oldres / olderror - (newres * olderror) / (newerror * newerror));
6474
6475 for (int j = i; j < nfitpars; j++) {
6476 double weight = 1;
6477
6478 if (
6479 !cache.m_phiweight.empty() &&
6480 i == j &&
6481 i >= nperpars &&
6482 i < nperpars + 2 * nscats &&
6483 (i - nperpars) % 2 == 0
6484 ) {
6485 weight = cache.m_phiweight[(i - nperpars) / 2];
6486 }
6487
6488 a.fillSymmetric(
6489 i, j,
6490 a(i, j) + weightderiv(measno, i) * weightderiv(measno, j) * ((olderror * olderror) / (newerror * newerror) - 1) * weight
6491 );
6492 }
6493 weightderiv(measno, i) *= olderror / newerror;
6494 }
6495
6496 res[measno] = newres;
6497 err[measno] = newerror;
6498 }
6499 }
6500 }
6501 }
6502 }
6503
6504 if (state->getStateType(TrackStateOnSurface::Measurement) || state->getStateType(TrackStateOnSurface::Outlier)) {
6505 hitno++;
6506 measno += state->numberOfMeasuredParameters();
6507 }
6508 }
6509
6510 if (trajectory.nDOF() < 0) {
6512 cache.incrementFitStatus(S_NOT_ENOUGH_MEAS);
6513 }
6514
6515 if (outlierremoved || hitrecalibrated) {
6516 lu_m = a;
6517 trajectory.setConverged(false);
6518
6519 cache.m_miniter = it + 2;
6520 }
6521 }
6522
6524 const EventContext& ctx,
6525 Cache & cache,
6526 GXFTrajectory &trajectory,
6528 Amg::SymMatrixX &fullcov,
6529 Amg::VectorX & b,
6530 bool runoutlier
6531 ) const {
6532 bool trackok = false;
6533 GXFTrajectory *oldtrajectory = &trajectory;
6534 std::unique_ptr < GXFTrajectory > cleanup_oldtrajectory;
6535 GXFTrajectory *newtrajectory = nullptr;
6536 std::unique_ptr < GXFTrajectory > cleanup_newtrajectory;
6537
6538 // the oldtrajectory will be returned, so in case newtrajectory==oldtrajectory
6539 // the cleanup_newtrajectory == NULL and cleanup_oldtrajectory = oldtrajectory, otherwise
6540 // cleanup_newtrajectory will destroy the object oldtrajectory is pointing to.
6541
6542 while (!trackok && oldtrajectory->nDOF() > 0) {
6543 trackok = true;
6544 std::vector<std::unique_ptr<GXFTrackState>> & states = oldtrajectory->trackStates();
6545 Amg::VectorX & res = oldtrajectory->residuals();
6546 Amg::VectorX & err = oldtrajectory->errors();
6547 Amg::MatrixX & weightderiv = oldtrajectory->weightedResidualDerivatives();
6548 const int nfitpars = oldtrajectory->numberOfFitParameters();
6549 const int nhits = oldtrajectory->numberOfHits();
6550 const int nsihits = oldtrajectory->numberOfSiliconHits();
6551
6552 if (nhits != nsihits) {
6553 return &trajectory;
6554 }
6555
6556 double maxsipull = -1;
6557 int hitno = 0;
6558 int hitno_maxsipull = -1;
6559 int measno_maxsipull = -1;
6560 int stateno_maxsipull = 0;
6561 GXFTrackState *state_maxsipull = nullptr;
6562 int measno = 0;
6563 int n3sigma = 0;
6564 const double cut = m_outlcut;
6565 double cut2 = m_outlcut - 1.;
6566 const int noutl = oldtrajectory->numberOfOutliers();
6567
6568 if (noutl > 0) {
6569 cut2 = cut - 1.25;
6570 }
6571
6572 for (int stateno = 0; stateno < (int) states.size(); stateno++) {
6573 std::unique_ptr<GXFTrackState> & state = states[stateno];
6574
6575 if (state->getStateType(TrackStateOnSurface::Measurement)) {
6576 TrackState::MeasurementType const hittype = state->measurementType();
6577
6578 if ((hittype == TrackState::Pixel || hittype == TrackState::SCT) && state->hasTrackCovariance()) {
6579 double *errors = state->measurementErrors();
6580 AmgSymMatrix(5) & trackcov = state->trackCovariance();
6581 const Amg::MatrixX & hitcov = state->measurement()->localCovariance();
6582 const double sinstereo = state->sinStereo();
6583 const double cosstereo = (sinstereo == 0) ? 1 : std::sqrt(1 - sinstereo * sinstereo);
6584 double weight1 = -1;
6585
6586 if (hitcov(0, 0) > trackcov(0, 0)) {
6587 if (sinstereo == 0) {
6588 weight1 = errors[0] * errors[0] - trackcov(0, 0);
6589 } else {
6590 weight1 = errors[0] * errors[0] - (
6591 trackcov(0, 0) * cosstereo * cosstereo + 2 *
6592 trackcov(1, 0) * cosstereo * sinstereo + trackcov(1, 1) * sinstereo * sinstereo
6593 );
6594 }
6595 }
6596
6597 const double weight2 = (
6598 hittype == TrackState::Pixel && hitcov(1, 1) > trackcov(1, 1) ?
6599 errors[1] * errors[1] - trackcov(1, 1) :
6600 -1
6601 );
6602
6603 double sipull1 = weight1 > 0 ? std::abs(res[measno] / std::sqrt(weight1)) : -1;
6604 const double sipull2 = (
6605 hittype == TrackState::Pixel && weight2 > 0 ?
6606 std::abs(res[measno + 1] / std::sqrt(weight2)) :
6607 -1
6608 );
6609 sipull1 = std::max(sipull1, sipull2);
6610
6611 if (sipull1 > maxsipull) {
6612 maxsipull = sipull1;
6613 measno_maxsipull = measno;
6614 stateno_maxsipull = stateno;
6615 hitno_maxsipull = hitno;
6616 }
6617
6618 if (hittype == TrackState::Pixel && sipull1 > cut2) {
6619 n3sigma++;
6620 }
6621 }
6622 }
6623
6624 if (state->getStateType(TrackStateOnSurface::Measurement) || state->getStateType(TrackStateOnSurface::Outlier)) {
6625 hitno++;
6626 measno += state->numberOfMeasuredParameters();
6627 }
6628 }
6629
6630 const double maxpull = maxsipull;
6631
6632 ATH_MSG_DEBUG(" maxsipull: " << maxsipull << " hitno_maxsipull: " <<
6633 hitno_maxsipull << " n3sigma: " << n3sigma << " cut: " << cut << " cut2: " << cut2);
6634
6635 Amg::SymMatrixX * newap = &a;
6636 Amg::VectorX * newbp = &b;
6637 Amg::SymMatrixX newa(nfitpars, nfitpars);
6638 Amg::VectorX newb(nfitpars);
6639
6640 if (
6641 maxpull > 2 &&
6642 oldtrajectory->chi2() / oldtrajectory->nDOF() > .25 * m_chi2cut
6643 ) {
6644 state_maxsipull = oldtrajectory->trackStates()[stateno_maxsipull].get();
6645 const PrepRawData *prd{};
6646 if (const auto *const pMeas = state_maxsipull->measurement(); pMeas->type(Trk::MeasurementBaseType::RIO_OnTrack)){
6647 const auto *const rot = static_cast<const RIO_OnTrack *>(pMeas);
6648 prd = rot->prepRawData();
6649 }
6650 std::unique_ptr < const RIO_OnTrack > broadrot;
6651 double *olderror = state_maxsipull->measurementErrors();
6652 TrackState::MeasurementType const hittype_maxsipull = state_maxsipull->measurementType();
6653 const TrackParameters *trackpar_maxsipull = state_maxsipull->trackParameters();
6654
6655 Amg::VectorX parameterVector = trackpar_maxsipull->parameters();
6656 const std::unique_ptr<const TrackParameters> trackparForCorrect(
6657 trackpar_maxsipull->associatedSurface().createUniqueTrackParameters(
6658 parameterVector[Trk::loc1],
6659 parameterVector[Trk::loc2],
6660 parameterVector[Trk::phi],
6661 parameterVector[Trk::theta],
6662 parameterVector[Trk::qOverP],
6663 state_maxsipull->hasTrackCovariance()
6664 ? std::optional<AmgSymMatrix(5)>(
6665 state_maxsipull->trackCovariance())
6666 : std::nullopt));
6667
6668 double newerror[5];
6669 newerror[0] = newerror[1] = newerror[2] = newerror[3] = newerror[4] = -1;
6670 double newpull = -1;
6671 double newpull1 = -1;
6672 double newpull2 = -1;
6673 double newres1 = -1;
6674 double newres2 = -1;
6675 double newsinstereo = 0;
6676
6677 if (
6678 (prd != nullptr) &&
6679 !state_maxsipull->isRecalibrated() &&
6680 maxpull > 2.5 &&
6681 oldtrajectory->chi2() / trajectory.nDOF() > .3 * m_chi2cut &&
6682 cache.m_sirecal
6683 ) {
6684 broadrot.reset(m_broadROTcreator->correct(*prd, *trackparForCorrect, ctx));
6685 }
6686
6687 if (broadrot) {
6688 const Amg::MatrixX & covmat = broadrot->localCovariance();
6689
6690 if (state_maxsipull->sinStereo() != 0) {
6691 const auto [covEigenValueSmall, covStereoAngle] = principalComponentAnalysis2x2(covmat);
6692 newerror[0] = std::sqrt(covEigenValueSmall);
6693 newsinstereo = std::sin(covStereoAngle);
6694 } else {
6695 newerror[0] = std::sqrt(covmat(0, 0));
6696 }
6697
6698 const double cosstereo = (newsinstereo == 0) ? 1. : std::sqrt(1 - newsinstereo * newsinstereo);
6699
6700 if (cosstereo != 1.) {
6701 newres1 = (
6702 cosstereo * (broadrot->localParameters()[Trk::locX] - trackpar_maxsipull->parameters()[Trk::locX]) +
6703 newsinstereo * (broadrot->localParameters()[Trk::locY] - trackpar_maxsipull->parameters()[Trk::locY])
6704 );
6705 } else {
6706 newres1 = broadrot->localParameters()[Trk::locX] - trackpar_maxsipull->parameters()[Trk::locX];
6707 }
6708
6709 if (newerror[0] == 0.0) {
6710 ATH_MSG_WARNING("Measurement error is zero or negative, treating as outlier");
6711 newpull1 = 9999.;
6712 } else {
6713 newpull1 = std::abs(newres1 / newerror[0]);
6714 }
6715
6716 if (hittype_maxsipull == TrackState::Pixel) {
6717 newerror[1] = std::sqrt(covmat(1, 1));
6718 newres2 = broadrot->localParameters()[Trk::locY] - trackpar_maxsipull->parameters()[Trk::locY];
6719 newpull2 = std::abs(newres2 / newerror[1]);
6720 }
6721
6722 newpull = std::max(newpull1, newpull2);
6723 }
6724
6725 if (
6726 broadrot &&
6727 newpull < m_outlcut &&
6728 (newerror[0] > 1.5 * olderror[0] || newerror[1] > 1.5 * std::abs(olderror[1]))
6729 ) {
6730 if ((measno_maxsipull < 0) or(measno_maxsipull >= (int) res.size())) {
6731 throw std::runtime_error(
6732 "'res' array index out of range in TrkGlobalChi2Fitter/src/GlobalChi2Fitter.cxx:" + std::to_string(__LINE__)
6733 );
6734 }
6735
6736 trackok = false;
6737 newtrajectory = oldtrajectory;
6738
6739 if (a.cols() != nfitpars) {
6740 ATH_MSG_ERROR("Your assumption is wrong!!!!");
6741 }
6742
6743 const double oldres1 = res[measno_maxsipull];
6744 res[measno_maxsipull] = newres1;
6745 err[measno_maxsipull] = newerror[0];
6746
6747 for (int i = 0; i < nfitpars; i++) {
6748 if (weightderiv(measno_maxsipull, i) == 0) {
6749 continue;
6750 }
6751
6752 b[i] -= weightderiv(measno_maxsipull, i) * (oldres1 / olderror[0] - (newres1 * olderror[0]) / (newerror[0] * newerror[0]));
6753
6754 for (int j = i; j < nfitpars; j++) {
6755 a.fillSymmetric(
6756 i, j,
6757 a(i, j) + (
6758 weightderiv(measno_maxsipull, i) *
6759 weightderiv(measno_maxsipull, j) *
6760 ((olderror[0] * olderror[0]) / (newerror[0] * newerror[0]) - 1)
6761 )
6762 );
6763 }
6764 weightderiv(measno_maxsipull, i) *= olderror[0] / newerror[0];
6765 }
6766
6767 if (hittype_maxsipull == TrackState::Pixel) {
6768 const double oldres2 = res[measno_maxsipull + 1];
6769 res[measno_maxsipull + 1] = newres2;
6770 err[measno_maxsipull + 1] = newerror[1];
6771
6772 for (int i = 0; i < nfitpars; i++) {
6773 if (weightderiv(measno_maxsipull + 1, i) == 0) {
6774 continue;
6775 }
6776
6777 b[i] -= weightderiv(measno_maxsipull + 1, i) * (oldres2 / olderror[1] - (newres2 * olderror[1]) / (newerror[1] * newerror[1]));
6778
6779 for (int j = i; j < nfitpars; j++) {
6780 a.fillSymmetric(
6781 i, j,
6782 a(i, j) + (
6783 weightderiv(measno_maxsipull + 1, i) *
6784 weightderiv(measno_maxsipull + 1, j) *
6785 ((olderror[1] * olderror[1]) / (newerror[1] * newerror[1]) - 1)
6786 )
6787 );
6788 }
6789
6790 weightderiv(measno_maxsipull + 1, i) *= olderror[1] / newerror[1];
6791 }
6792 }
6793
6795 "Recovering outlier, hitno=" << hitno_maxsipull << " measno=" <<
6796 measno_maxsipull << " pull=" << maxsipull << " olderror_0=" <<
6797 olderror[0] << " newerror_0=" << newerror[0] << " olderror_1=" <<
6798 olderror[1] << " newerror_1=" << newerror[1]
6799 );
6800
6801 state_maxsipull->setMeasurement(std::move(broadrot));
6802 state_maxsipull->setSinStereo(newsinstereo);
6803 state_maxsipull->setMeasurementErrors(newerror);
6804 } else if (
6805 (
6806 (
6807 ((n3sigma < 2 && maxsipull > cut2 && maxsipull < cut) || n3sigma > 1) &&
6808 (oldtrajectory->chi2() / oldtrajectory->nDOF() > .3 * m_chi2cut || noutl > 1)
6809 ) ||
6810 maxsipull > cut
6811 ) &&
6812 (oldtrajectory->nDOF() > 1 || hittype_maxsipull == TrackState::SCT) &&
6813 runoutlier
6814 ) {
6815 trackok = false;
6817 "Removing outlier, hitno=" << hitno_maxsipull << ", measno=" <<
6818 measno_maxsipull << " pull=" << maxsipull
6819 );
6820
6821 newa = a;
6822 newb = b;
6823 newap = &newa;
6824 newbp = &newb;
6825 cleanup_newtrajectory = std::make_unique<GXFTrajectory>(*oldtrajectory);
6826 newtrajectory = cleanup_newtrajectory.get();
6827
6828 if (newa.cols() != nfitpars) {
6829 ATH_MSG_ERROR("Your assumption is wrong!!!!");
6830 }
6831
6832 Amg::VectorX & newres = newtrajectory->residuals();
6833 Amg::MatrixX & newweightderiv = newtrajectory->weightedResidualDerivatives();
6834 if ((measno_maxsipull < 0) or(measno_maxsipull >= (int) res.size())) {
6835 throw std::runtime_error(
6836 "'res' array index out of range in TrkGlobalChi2Fitter/src/GlobalChi2Fitter.cxx:" + std::to_string(__LINE__)
6837 );
6838 }
6839
6840 const double oldres1 = res[measno_maxsipull];
6841 newres[measno_maxsipull] = 0;
6842
6843 for (int i = 0; i < nfitpars; i++) {
6844 if (weightderiv(measno_maxsipull, i) == 0) {
6845 continue;
6846 }
6847
6848 newb[i] -= weightderiv(measno_maxsipull, i) * oldres1 / olderror[0];
6849
6850 for (int j = i; j < nfitpars; j++) {
6851 newa.fillSymmetric(
6852 i, j,
6853 newa(i, j) - (
6854 weightderiv(measno_maxsipull, i) *
6855 weightderiv(measno_maxsipull, j)
6856 )
6857 );
6858 }
6859 newweightderiv(measno_maxsipull, i) = 0;
6860 }
6861
6862 if (hittype_maxsipull == TrackState::Pixel) {
6863 const double oldres2 = res[measno_maxsipull + 1];
6864 newres[measno_maxsipull + 1] = 0;
6865
6866 for (int i = 0; i < nfitpars; i++) {
6867 if (weightderiv(measno_maxsipull + 1, i) == 0) {
6868 continue;
6869 }
6870
6871 newb[i] -= weightderiv(measno_maxsipull + 1, i) * oldres2 / olderror[1];
6872
6873 for (int j = i; j < nfitpars; j++) {
6874 if (weightderiv(measno_maxsipull + 1, j) == 0) {
6875 continue;
6876 }
6877
6878 newa.fillSymmetric(
6879 i, j,
6880 newa(i, j) - (
6881 weightderiv(measno_maxsipull + 1, i) *
6882 weightderiv(measno_maxsipull + 1, j)
6883 )
6884 );
6885 }
6886 newweightderiv(measno_maxsipull + 1, i) = 0;
6887 }
6888 }
6889
6890 newtrajectory->setOutlier(stateno_maxsipull);
6891 }
6892 }
6893
6894 if (!trackok) {
6895 Amg::SymMatrixX lu_m = *newap;
6896 newtrajectory->setConverged(false);
6897 bool doderiv = m_redoderivs;
6898 cache.m_fittercode = updateFitParameters(*newtrajectory, *newbp, lu_m);
6899 if (cache.m_fittercode != FitterStatusCode::Success) {
6900 cache.incrementFitStatus(S_NOT_ENOUGH_MEAS);
6901 return nullptr;
6902 }
6903
6904 for (int it = 0; it < m_maxit; ++it) {
6905 if (it == m_maxit - 1) {
6906 ATH_MSG_DEBUG("Fit did not converge");
6908 cache.incrementFitStatus(S_NOT_CONVERGENT);
6909 return nullptr;
6910 }
6911
6912 if (!newtrajectory->converged()) {
6913 cache.m_fittercode = runIteration(
6914 ctx, cache, *newtrajectory, it, *newap, *newbp, lu_m, doderiv);
6915
6916 if (cache.m_fittercode != FitterStatusCode::Success) {
6917 cache.incrementFitStatus(S_NOT_ENOUGH_MEAS);
6918 return nullptr;
6919 }
6920
6921 if (!newtrajectory->converged()) {
6922 cache.m_fittercode = updateFitParameters(*newtrajectory, *newbp, lu_m);
6923 if (cache.m_fittercode != FitterStatusCode::Success) {
6924 cache.incrementFitStatus(S_NOT_ENOUGH_MEAS);
6925
6926 return nullptr;
6927 }
6928 }
6929 } else {
6930 const double oldchi2 = oldtrajectory->chi2() / oldtrajectory->nDOF();
6931 const double newchi2 = (newtrajectory->nDOF() > 0) ? newtrajectory->chi2() / newtrajectory->nDOF() : 0;
6932 double mindiff = 0;
6933
6934 if (newtrajectory->nDOF() != oldtrajectory->nDOF() && maxsipull > cut2) {
6935 mindiff = (oldchi2 > .33 * m_chi2cut || noutl > 0) ? .8 : 1.;
6936
6937 if (noutl == 0 && maxsipull < cut - .5 && oldchi2 < .5 * m_chi2cut) {
6938 mindiff = 2.;
6939 }
6940 }
6941
6942 if (newchi2 > oldchi2 || (newchi2 > oldchi2 - mindiff && newchi2 > .33 * oldchi2)) {
6943 ATH_MSG_DEBUG("Outlier not confirmed, keeping old trajectory");
6944
6945 if (oldchi2 > m_chi2cut) {
6947 cache.incrementFitStatus(S_NOT_ENOUGH_MEAS);
6948 return nullptr;
6949 }
6950
6951 (void)cleanup_oldtrajectory.release();
6952 return oldtrajectory;
6953 }
6954 if (oldtrajectory != newtrajectory) {
6955 cleanup_oldtrajectory = std::move(cleanup_newtrajectory);
6956 oldtrajectory = newtrajectory;
6957 a = std::move(newa);
6958 b = std::move(newb);
6959 }
6960
6961 // Solve assuming the matrix is SPD.
6962 // Cholesky Decomposition is used
6963 Eigen::LLT < Eigen::MatrixXd > const lltOfW(a);
6964 if (lltOfW.info() == Eigen::Success) {
6965 // Solve for x where Wx = I
6966 // this is cheaper than invert as invert makes no assumptions about the
6967 // matrix being symmetric
6968 const int ncols = a.cols();
6969 Amg::MatrixX const weightInvAMG = Amg::MatrixX::Identity(ncols, ncols);
6970 fullcov = lltOfW.solve(weightInvAMG);
6971 } else {
6972 ATH_MSG_DEBUG("matrix inversion failed!");
6973 cache.incrementFitStatus(S_MAT_INV_FAIL);
6975 return nullptr;
6976 }
6977 break;
6978 }
6979 }
6980 }
6981
6982 if (!trackok) {
6983 calculateTrackErrors(*oldtrajectory, fullcov, true);
6984 }
6985 }
6986
6987 if (
6988 oldtrajectory->nDOF() > 0 &&
6989 oldtrajectory->chi2() / oldtrajectory->nDOF() > m_chi2cut &&
6990 runoutlier
6991 ) {
6993 cache.incrementFitStatus(S_NOT_ENOUGH_MEAS);
6994 return nullptr;
6995 }
6996 /*
6997 * When an outlier is accepted, oldtrajectory may point to a heap copy owned by
6998 * cleanup_oldtrajectory. Releasing it before returning transfers that ownership to
6999 * the caller. The caller stores it in finaltrajectory and deletes it whenever it
7000 * differs from the original stack-owned trajectory.
7001 */
7002 //coverity[RESOURCE_LEAK]
7003 (void)cleanup_oldtrajectory.release();
7004 return oldtrajectory;
7005 }
7006
7008 Cache &cache,
7009 GXFTrajectory &oldtrajectory
7010 ) {
7011 Amg::MatrixX & derivs = oldtrajectory.weightedResidualDerivatives();
7012 Amg::VectorX & errors = oldtrajectory.errors();
7013 int nrealmeas = 0;
7014
7015 for (auto & hit : oldtrajectory.trackStates()) {
7016 if (const auto *pMeas{hit->measurement()};
7017 hit->getStateType(TrackStateOnSurface::Measurement) and (
7020 )
7021 ) {
7022 nrealmeas += hit->numberOfMeasuredParameters();
7023 }
7024 }
7025 cache.m_derivmat.resize(nrealmeas, oldtrajectory.numberOfFitParameters());
7026 cache.m_derivmat.setZero();
7027 int measindex = 0;
7028 int measindex2 = 0;
7029 const int nperpars = oldtrajectory.numberOfPerigeeParameters();
7030 const int nscat = oldtrajectory.numberOfScatterers();
7031 for (auto & hit : oldtrajectory.trackStates()) {
7032 if (const auto *pMeas{hit->measurement()};
7033 hit->getStateType(TrackStateOnSurface::Measurement) and (
7036 )
7037 ) {
7038 for (int i = measindex; i < measindex + hit->numberOfMeasuredParameters(); i++) {
7039 for (int j = 0; j < oldtrajectory.numberOfFitParameters(); j++) {
7040 cache.m_derivmat(i, j) = derivs(measindex2, j) * errors[measindex2];
7041 if ((j == 4 && !oldtrajectory.m_straightline) || j >= nperpars + 2 * nscat) {
7042 cache.m_derivmat(i, j) *= 1000;
7043 }
7044 }
7045
7046 measindex2++;
7047 }
7048
7049 measindex += hit->numberOfMeasuredParameters();
7050 } else if (hit->materialEffects() == nullptr) {
7051 measindex2 += hit->numberOfMeasuredParameters();
7052 }
7053 }
7054 }
7055
7056 std::unique_ptr<const TrackParameters> GlobalChi2Fitter::makeTrackFindPerigeeParameters(
7057 const EventContext & ctx,
7058 Cache &cache,
7059 GXFTrajectory &oldtrajectory,
7060 const ParticleHypothesis matEffects
7061 ) const {
7062 GXFTrackState *firstmeasstate = nullptr;
7063 GXFTrackState *lastmeasstate = nullptr;
7064 std::tie(firstmeasstate, lastmeasstate) = oldtrajectory.findFirstLastMeasurement();
7065 std::unique_ptr<const TrackParameters> per(nullptr);
7066
7067 if (cache.m_acceleration && !m_matupdator.empty()) {
7068 std::unique_ptr<const TrackParameters> prevpar(
7069 firstmeasstate->trackParameters() != nullptr ?
7070 firstmeasstate->trackParameters()->clone() :
7071 nullptr
7072 );
7073 std::vector<std::pair<const Layer *, const Layer *>> & upstreamlayers = oldtrajectory.upstreamMaterialLayers();
7074 bool first = true;
7075
7076 for (const auto & [layer1, layer2] : upstreamlayers | std::views::reverse) {
7077 if (prevpar == nullptr) {
7078 break;
7079 }
7080
7082 const Layer *layer = layer1 != nullptr ? layer1 : layer2;
7083
7084 const DistanceSolution distsol = layer->surfaceRepresentation().straightLineDistanceEstimate(
7085 prevpar->position(), prevpar->momentum().unit()
7086 );
7087 const double distance = getDistance(distsol);
7088
7089 if (distsol.numberOfSolutions() == 2) {
7090 if (std::abs(distance) < 0.01) {
7091 continue;
7092 }
7093
7094 if (distsol.first() * distsol.second() < 0 && !first) {
7095 continue;
7096 }
7097 }
7098
7099 if (first && distance > 0) {
7100 propdir = alongMomentum;
7101 }
7102
7103 std::unique_ptr<const TrackParameters> layerpar(
7104 m_propagator->propagate(
7105 ctx,
7106 *prevpar,
7107 layer->surfaceRepresentation(),
7108 propdir,
7109 true,
7110 oldtrajectory.m_fieldprop,
7112 )
7113 );
7114
7115 if (layerpar == nullptr) {
7116 continue;
7117 }
7118
7119 if (layer->surfaceRepresentation().bounds().inside(layerpar->localPosition())) {
7120 layerpar = m_matupdator->update(layerpar.get(), *layer, oppositeMomentum, matEffects);
7121 }
7122
7123 prevpar = std::move(layerpar);
7124 first = false;
7125 }
7126
7127 const Layer *startlayer = firstmeasstate->trackParameters()->associatedSurface().associatedLayer();
7128
7129 if ((startlayer != nullptr) && (startlayer->layerMaterialProperties() != nullptr)) {
7130 double startfactor = startlayer->layerMaterialProperties()->alongPostFactor();
7131 const Surface & discsurf = startlayer->surfaceRepresentation();
7132
7133 if (discsurf.type() == Trk::SurfaceType::Disc && discsurf.center().z() * discsurf.normal().z() < 0) {
7134 startfactor = startlayer->layerMaterialProperties()->oppositePostFactor();
7135 }
7136 if (startfactor > 0.5) {
7137 std::unique_ptr<const TrackParameters> updatedpar = m_matupdator->update(
7138 firstmeasstate->trackParameters(), *startlayer, oppositeMomentum, matEffects
7139 );
7140
7141 if (updatedpar != nullptr) {
7142 firstmeasstate->setTrackParameters(std::move(updatedpar));
7143 }
7144 }
7145 }
7146
7147 // @TODO Coverity complains about a possible NULL pointer dereferencing in lastmeasstate->...
7148 // Now an exception is thrown if there is no firstmeastate. Thus if the code here is
7149 // reached then there should be a firstmeasstate and a lastmeasstate
7150
7151 const Layer *endlayer = lastmeasstate->trackParameters()->associatedSurface().associatedLayer();
7152
7153 if ((endlayer != nullptr) && (endlayer->layerMaterialProperties() != nullptr)) {
7154 double endfactor = endlayer->layerMaterialProperties()->alongPreFactor();
7155 const Surface & discsurf = endlayer->surfaceRepresentation();
7156
7157 if (discsurf.type() == Trk::SurfaceType::Disc && discsurf.center().z() * discsurf.normal().z() < 0) {
7158 endfactor = endlayer->layerMaterialProperties()->oppositePreFactor();
7159 }
7160
7161 if (endfactor > 0.5) {
7162 std::unique_ptr<const TrackParameters> updatedpar = m_matupdator->update(
7163 lastmeasstate->trackParameters(), *endlayer, alongMomentum, matEffects
7164 );
7165
7166 if (updatedpar != nullptr) {
7167 lastmeasstate->setTrackParameters(std::move(updatedpar));
7168 }
7169 }
7170 }
7171
7172 if (prevpar != nullptr) {
7173 per = m_propagator->propagate(
7174 ctx,
7175 *prevpar,
7176 PerigeeSurface(Amg::Vector3D(0, 0, 0)),
7178 false,
7179 oldtrajectory.m_fieldprop,
7181 );
7182 }
7183
7184 if (per == nullptr) {
7185 ATH_MSG_DEBUG("Failed to extrapolate to perigee, returning 0");
7186 cache.incrementFitStatus(S_PROPAGATION_FAIL);
7188 return nullptr;
7189 }
7190 } else if (cache.m_acceleration && (firstmeasstate->trackParameters() != nullptr)) {
7191 per = m_extrapolator->extrapolate(ctx,
7192 *firstmeasstate->trackParameters(),
7193 PerigeeSurface(Amg::Vector3D(0, 0, 0)),
7195 false,
7196 matEffects);
7197 } else {
7198 per.reset(oldtrajectory.referenceParameters()->clone());
7199 }
7200
7201 return per;
7202 }
7203
7204 std::unique_ptr<GXFTrackState>
7206 const EventContext & ctx,
7207 Cache &cache,
7208 GXFTrajectory &oldtrajectory,
7209 const ParticleHypothesis matEffects
7210 ) const {
7211 std::unique_ptr<const TrackParameters> per = makeTrackFindPerigeeParameters(ctx, cache, oldtrajectory, matEffects);
7212
7213 if (per == nullptr) {
7214 return nullptr;
7215 }
7216
7217 ATH_MSG_DEBUG("Final perigee: " << *per << " pos: " << per->position() << " pT: " << per->pT());
7218
7219 return std::make_unique<GXFTrackState>(std::move(per), TrackStateOnSurface::Perigee);
7220 }
7221
7223 const std::vector<std::unique_ptr<TrackParameters>> & hc,
7224 std::set<Identifier> & id_set,
7225 std::set<Identifier> & sct_set,
7226 TrackHoleCount & rv,
7227 bool count_holes,
7228 bool count_dead
7229 ) const {
7230 /*
7231 * Our input is a list of track states, which we are iterating over. We
7232 * need to examine each one and update the values in our track hole count
7233 * accordingly.
7234 */
7235 for (const std::unique_ptr<TrackParameters> & tp : hc) {
7236 /*
7237 * It is possible, expected even, for some of these pointers to be null.
7238 * In those cases, it would be dangerous to continue, so we need to make
7239 * sure we skip them.
7240 */
7241 if (tp == nullptr) {
7242 continue;
7243 }
7244
7245 /*
7246 * Extract the detector element of the track parameter surface for
7247 * examination. If for whatever reason there is none (i.e. the surface
7248 * is not a detector at all), we can skip it and continue.
7249 */
7250 const TrkDetElementBase * de = tp->associatedSurface().associatedDetectorElement();
7251
7252 if (de == nullptr) {
7253 continue;
7254 }
7255
7256 Identifier const id = de->identify();
7257
7258 /*
7259 * If, for whatever reason, we have already visited this detector, we do
7260 * not want to visit it again. Otherwise we might end up with modules
7261 * counted twice, and that would be very bad.
7262 */
7263 if (id_set.find(id) != id_set.end()) {
7264 continue;
7265 }
7266
7267 /*
7268 * This is the meat of the pudding, we use the boundary checking tool
7269 * to see whether this set of parameters is a hole candidate, a dead
7270 * module, or not a hole at all.
7271 */
7272 BoundaryCheckResult const bc = m_boundaryCheckTool->boundaryCheck(*tp);
7273
7274 if (bc == BoundaryCheckResult::DeadElement && count_dead) {
7275 /*
7276 * If the module is dead, our job is very simple. We just check
7277 * whether it is a Pixel or an SCT and increment the appropriate
7278 * counter. We also insert the module into our set of visited elements.
7279 */
7280 if (m_DetID->is_pixel(id)) {
7281 ++rv.m_pixel_dead;
7282 } else if (m_DetID->is_sct(id)) {
7283 ++rv.m_sct_dead;
7284 }
7285 id_set.insert(id);
7286 } else if (bc == BoundaryCheckResult::Candidate && count_holes) {
7287 /*
7288 * If the module is a candidate, it's much the same, but we also need
7289 * to handle double SCT holes.
7290 */
7291 if (m_DetID->is_pixel(id)) {
7292 ++rv.m_pixel_hole;
7293 } else if (m_DetID->is_sct(id)) {
7294 ++rv.m_sct_hole;
7295
7296 /*
7297 * To check for SCT double holes, we need to first fetch the other
7298 * side of the current SCT. Thankfully, the detector description
7299 * makes this very easy.
7300 */
7301 const InDetDD::SiDetectorElement* e = static_cast<const InDetDD::SiDetectorElement *>(de);
7302 const Identifier os = e->otherSide()->identify();
7303
7304 /*
7305 * We keep a special set containing only SCT hole IDs. We simply
7306 * check whether the ID of the other side of the SCT is in this set
7307 * to confirm that we have a double hole. Note that the first side
7308 * in a double hole will be counted as a SCT hole only, and the
7309 * second side will count as another hole as well as a double hole,
7310 * which is exactly the behaviour we would expect to see.
7311 */
7312 if (sct_set.find(os) != sct_set.end()) {
7313 ++rv.m_sct_double_hole;
7314 }
7315
7316 /*
7317 * We need to add our SCT to the SCT identifier set if it is a
7318 * candidate hit, otherwise known as a hole in this context.
7319 */
7320 sct_set.insert(id);
7321 }
7322
7323 /*
7324 * SCTs are also added to the set of all identifiers to avoid double
7325 * counting them.
7326 */
7327 id_set.insert(id);
7328 }
7329 }
7330 }
7331
7332 std::vector<std::reference_wrapper<GXFTrackState>> GlobalChi2Fitter::holeSearchStates(
7333 GXFTrajectory & trajectory
7334 ) const {
7335 /*
7336 * Firstly, we will need to find the last measurement state on our track.
7337 * This will allow us to break the main loop later once we are done with
7338 * our work.
7339 */
7340 GXFTrackState * lastmeas = nullptr;
7341
7342 for (const std::unique_ptr<GXFTrackState> & s : trajectory.trackStates()) {
7343 if (s->getStateType(TrackStateOnSurface::Measurement)) {
7344 lastmeas = s.get();
7345 }
7346 }
7347
7348 /*
7349 * We create a vector of reference wrappers and reserve at least enough
7350 * space to contain the entire trajectory. This is perhaps a little
7351 * wasteful since we will never need this much space, but it may be more
7352 * efficient than taking the resizing pentalty on the chin.
7353 */
7354 std::vector<std::reference_wrapper<GXFTrackState>> rv;
7355 rv.reserve(trajectory.trackStates().size());
7356
7357 /*
7358 * The main body of our method now. We iterate over all track states in
7359 * the track, at least until we find the last measurement state as found
7360 * above.
7361 */
7362 for (const std::unique_ptr<GXFTrackState> & s : trajectory.trackStates()) {
7363 /*
7364 * We are only interested in collecting measurements, perigees, and any
7365 * outlier states.
7366 */
7367 if (
7368 s->getStateType(TrackStateOnSurface::Measurement) ||
7369 s->getStateType(TrackStateOnSurface::Perigee) ||
7370 s->getStateType(TrackStateOnSurface::Outlier)
7371 ) {
7372 /*
7373 * We store a reference to the current track state in our return value
7374 * vector.
7375 */
7376 rv.emplace_back(*s);
7377
7378 /*
7379 * We want to make sure we do not collect any TRT results or other
7380 * non-SCT and non-Pixel detector types. For that, we need to access
7381 * the details of the detector element and determine the detector type.
7382 */
7383 const TrkDetElementBase * de = s->trackParameters()->associatedSurface().associatedDetectorElement();
7384
7385 if (de != nullptr) {
7386 Identifier const id = de->identify();
7387
7388 if (!m_DetID->is_pixel(id) && !m_DetID->is_sct(id)) {
7389 break;
7390 }
7391 }
7392
7393 /*
7394 * We also have no interest in going past the final measurement, so we
7395 * break out of the loop if we find it.
7396 */
7397 //cppcheck-suppress iterators3
7398 if (s.get() == lastmeas) {
7399 break;
7400 }
7401 }
7402 }
7403
7404 return rv;
7405 }
7406
7407 std::optional<GlobalChi2Fitter::TrackHoleCount> GlobalChi2Fitter::holeSearchProcess(
7408 const EventContext & ctx,
7409 const std::vector<std::reference_wrapper<GXFTrackState>> & states
7410 ) const {
7411 /*
7412 * Firstly, we need to guard against tracks having too few measurement
7413 * states to perform a good hole search. This is a mechanism that we
7414 * inherit from the reference hole search. If we have too few states, we
7415 * return a non-extant result to indicate an error state.
7416 *
7417 * TODO: The minimum value of 3 is also borrowed from the reference
7418 * implementation. It's hardcoded for now, but could be a parameter in the
7419 * future.
7420 */
7421 constexpr uint min_meas = 3;
7422 if (std::count_if(states.begin(), states.end(), [](const GXFTrackState & s){ return s.getStateType(TrackStateOnSurface::Measurement); }) < min_meas) {
7423 return {};
7424 }
7425
7426 bool seen_meas = false;
7427 TrackHoleCount rv;
7428 std::set<Identifier> id_set;
7429 std::set<Identifier> sct_set;
7430
7431 /*
7432 * Using an old-school integer-based for loop because we need to iterate
7433 * over successive pairs of states to do an extrapolation between.
7434 */
7435 for (std::size_t i = 0; i < states.size() - 1; i++) {
7436 /*
7437 * Gather references to the state at the beginning of the extrapolation,
7438 * named beg, and the end, named end.
7439 */
7440 GXFTrackState & beg = states[i];
7441 GXFTrackState const& end = states[i + 1];
7442
7443 /*
7444 * Update the boolean keeping track of whether we have seen a measurement
7445 * or outlier yet. Once we see one, this will remain true forever, but
7446 * it helps us make sure we don't collect holes before the first
7447 * measurement.
7448 */
7449 seen_meas |= beg.getStateType(TrackStateOnSurface::Measurement) || beg.getStateType(TrackStateOnSurface::Outlier);
7450
7451 /*
7452 * Calculate the distance between the position of the starting parameters
7453 * and the end parameters. If this distance is sufficiently small, there
7454 * can be no elements between them (for example, between two SCTs), and
7455 * we don't need to do an extrapolation. This can easily save us a few
7456 * microseconds.
7457 */
7458 const double dist = (beg.trackParameters()->position() - end.trackParameters()->position()).norm();
7459
7460 const bool zStartValid = std::abs(beg.trackParameters()->position().z())<10000.;
7461 if(!zStartValid){
7462 ATH_MSG_DEBUG("Pathological track parameter well outside of detector");
7463 ATH_MSG_DEBUG("Propagator might have issue with this, skipping");
7464 ATH_MSG_VERBOSE("dumping track parameters " << *(beg.trackParameters()));
7465 }
7466
7467 /*
7468 * Only proceed to count holes if we have seen a measurement before (this
7469 * may include the starting track state, if it is a measurement) and the
7470 * distance between start and end is at least 2.5 millimeters
7471 * and the z position is valid
7472 */
7473 if (seen_meas && dist >= 2.5 && zStartValid) {
7474 /*
7475 * First, we retrieve the hole data stored in the beginning state. Note
7476 * that this may very well be non-extant, but it is possible for the
7477 * fitter to have deposited some hole information into the track state
7478 * earlier on in the fitting process.
7479 */
7480 std::optional<std::vector<std::unique_ptr<TrackParameters>>> & hc = beg.getHoles();
7481 std::vector<std::unique_ptr<TrackParameters>> theseStates;
7482
7483 /*
7484 * Gather the track states between the start and end of the
7485 * extrapolation. If the track state contained hole search information,
7486 * we simply move that out and use it. If there was no information, we
7487 * do a fresh extrapolation. This can be a CPU hog!
7488 */
7489 if (hc.has_value()) {
7490 theseStates = std::move(*hc);
7491 } else {
7492 theseStates = holesearchExtrapolation(ctx, *beg.trackParameters(), end, alongMomentum);
7493 }
7494
7495 /*
7496 * Finally, we process the collected hole candidate states, checking
7497 * them for liveness and other properties. This helper function will
7498 * increment the values in rv accordingly.
7499 */
7500 holeSearchHelper(theseStates, id_set, sct_set, rv, true, true);
7501 }
7502 }
7503
7504 /*
7505 * Once we are done processing our measurements, we also need to do a
7506 * final blind extrapolation to collect and dead modules (but not holes)
7507 * behind the last measurement. For this, we do a blind extrapolation
7508 * from the final state.
7509 */
7510 GXFTrackState const& last = states.back();
7511
7512 /*
7513 * To do the blind extrapolation, we need to have a set of track parameters
7514 * for our last measurement state. We also check whether the position of
7515 * the last measurement is still inside the inner detector. If it is not,
7516 * we don't need to blindly extrapolate because we're only interested in
7517 * collecting inner detector dead modules. This check saves us a few tens
7518 * of microseconds.
7519 */
7520 if (
7521 last.trackParameters() != nullptr &&
7522 m_idVolume.inside(last.trackParameters()->position())
7523 ) {
7524 /*
7525 * Simply conduct the blind extrapolation, and then use the helper tool
7526 * to ensure that the hole counts are updated.
7527 */
7528 std::vector<std::unique_ptr<Trk::TrackParameters>> const bl = m_extrapolator->extrapolateBlindly(
7529 ctx,
7530 *last.trackParameters(),
7532 false,
7533 Trk::pion,
7534 &m_idVolume
7535 );
7536
7537 /*
7538 * Note that we have flipped one of the boolean parameters of the helper
7539 * method here to make sure it only collects dead modules, not hole
7540 * candidates.
7541 */
7542 holeSearchHelper(bl, id_set, sct_set, rv, false, true);
7543 }
7544
7545 return rv;
7546 }
7547
7548 std::unique_ptr<Track> GlobalChi2Fitter::makeTrack(
7549 const EventContext & ctx,
7550 Cache & cache,
7551 GXFTrajectory & oldtrajectory,
7552 ParticleHypothesis matEffects
7553 ) const {
7554 // Convert internal trajectory into track
7555 auto trajectory = std::make_unique<Trk::TrackStates>();
7556
7557 if (m_fillderivmatrix) {
7558 makeTrackFillDerivativeMatrix(cache, oldtrajectory);
7559 }
7560
7561 GXFTrajectory tmptrajectory(oldtrajectory);
7562
7563 std::unique_ptr<GXFTrackState> perigee_ts = makeTrackFindPerigee(ctx, cache, oldtrajectory, matEffects);
7564
7565 if (perigee_ts == nullptr) {
7566 return nullptr;
7567 }
7568
7569 tmptrajectory.addBasicState(std::move(perigee_ts), cache.m_acceleration ? 0 : tmptrajectory.numberOfUpstreamStates());
7570 //reserve the ouput size
7571 trajectory->reserve(tmptrajectory.trackStates().size());
7572 for (auto & hit : tmptrajectory.trackStates()) {
7573 if (
7574 hit->measurementType() == TrackState::Pseudo &&
7575 hit->getStateType(TrackStateOnSurface::Outlier)
7576 ) {
7577 hit->resetTrackCovariance();
7578 continue;
7579 }
7580
7581 if (!Trk::consistentSurfaces (hit->trackParameters(),
7582 hit->measurement(),
7583 hit->materialEffects()))
7584 {
7585 return nullptr;
7586 }
7587
7588 //should check hit->isSane() here with better equality check(other than ptr comparison)
7589 auto trackState = hit->trackStateOnSurface();
7590 hit->resetTrackCovariance();
7591 trajectory->emplace_back(trackState.release());
7592 }
7593
7594 auto qual = std::make_unique<FitQuality>(tmptrajectory.chi2(), tmptrajectory.nDOF());
7595
7596
7597 TrackInfo info;
7598
7599 if (matEffects != electron) {
7600 info = TrackInfo(TrackInfo::GlobalChi2Fitter, matEffects);
7601 } else {
7603 info.setTrackProperties(TrackInfo::BremFit);
7604
7605 if (matEffects == electron && tmptrajectory.hasKink()) {
7606 info.setTrackProperties(TrackInfo::BremFitSuccessful);
7607 }
7608 }
7609
7610 if (tmptrajectory.m_straightline) {
7611 info.setTrackProperties(TrackInfo::StraightTrack);
7612 }
7613
7614 std::unique_ptr<Track> rv = std::make_unique<Track>(info, std::move(trajectory), std::move(qual));
7615
7616 /*
7617 * Here, we create a track summary and attach it to our newly created
7618 * track. Note that this code only runs if the m_createSummary Gaudi
7619 * property is set. In cases where having a track summary on the track is
7620 * not desired, such as for compatibility with other tools, this can be
7621 * turned off.
7622 */
7623 if (m_createSummary.value()) {
7624 std::unique_ptr<TrackSummary> ts = std::make_unique<TrackSummary>();
7625
7626 /*
7627 * This segment determines the hole search behaviour of the track fitter.
7628 * It is only invoked if the DoHoleSearch parameter is set, but it can
7629 * take a significant amount of CPU time, since the hole search is rather
7630 * expensive. Beware of that!
7631 */
7632 if (m_holeSearch.value()) {
7633 std::optional<TrackHoleCount> hole_count;
7634
7635 /*
7636 * First, we collect a list of states that will act as our hole search
7637 * extrapolation states. This will serve as our source of truth in
7638 * regards to which track states we need to extrapolate between.
7639 */
7640 std::vector<std::reference_wrapper<GXFTrackState>> const states = holeSearchStates(tmptrajectory);
7641
7642 /*
7643 * Then, collect the actual hole search infomation using our state list
7644 * from before. This is the expensive operation, as it will invoke a
7645 * series of extrapolations if not all states have existing hole
7646 * information! It will also check all the hole candidates to see if
7647 * they are actually holes or not.
7648 */
7649 hole_count = holeSearchProcess(ctx, states);
7650
7651 /*
7652 * Note that the hole search is not guaranteed to return a useful set
7653 * of values. It can, for example, reach an error state if the number
7654 * of measurements on a track is below a certain threshold. In that
7655 * case, a non-extant result will be returned, which we must guard
7656 * against. In that case, the hole counts will remain unset.
7657 */
7658 if (hole_count.has_value()) {
7659 /*
7660 * If the hole search did return good results, we can proceed to
7661 * simply copy the numerical values in the track summary.
7662 */
7663 ts->update(Trk::numberOfPixelHoles, hole_count->m_pixel_hole);
7664 ts->update(Trk::numberOfSCTHoles, hole_count->m_sct_hole);
7665 ts->update(Trk::numberOfSCTDoubleHoles, hole_count->m_sct_double_hole);
7666 ts->update(Trk::numberOfPixelDeadSensors, hole_count->m_pixel_dead);
7667 ts->update(Trk::numberOfSCTDeadSensors, hole_count->m_sct_dead);
7668 }
7669 }
7670
7671 rv->setTrackSummary(std::move(ts));
7672 }
7673
7674 return rv;
7675 }
7676
7678
7679 std::vector<std::unique_ptr<TrackParameters>> GlobalChi2Fitter::holesearchExtrapolation(
7680 const EventContext & ctx,
7681 const TrackParameters & src,
7682 const GXFTrackState & dst,
7683 PropDirection propdir
7684 ) const {
7685 /*
7686 * First, we conduct a bog standard stepwise extrapolation. This will
7687 * yield some unwanted results, but we will filter those later.
7688 */
7689 std::vector<std::unique_ptr<TrackParameters>> rv = m_extrapolator->extrapolateStepwise(
7690 ctx, src, dst.associatedSurface(), propdir, false
7691 );
7692
7693 /*
7694 * It is possible for the first returned track parameter to be on the same
7695 * surface as we started on. That's probably due to some rounding errors.
7696 * We check for this possibility, and set the pointer to null if it
7697 * occurs. Note that this leaves some null pointers in the returned vector
7698 * but this is more performant compared to removing them properly.
7699 */
7700 if (
7701 !rv.empty() && (
7702 &rv.front()->associatedSurface() == &dst.associatedSurface() ||
7703 &rv.front()->associatedSurface() == &src.associatedSurface() ||
7704 trackParametersClose(*rv.front(), src, 0.001) ||
7705 trackParametersClose(*rv.front(), *dst.trackParameters(), 0.001)
7706 )
7707 ) {
7708 rv.front().reset(nullptr);
7709 }
7710
7711 /*
7712 * Same logic, but for the last returned element. In that case, we get a
7713 * set of parameters on the destination surface, which we also do not
7714 * want.
7715 */
7716 if (
7717 rv.size() > 1 && (
7718 &rv.back()->associatedSurface() == &dst.associatedSurface() ||
7719 &rv.back()->associatedSurface() == &src.associatedSurface() ||
7720 trackParametersClose(*rv.back(), src, 0.001) ||
7721 trackParametersClose(*rv.back(), *dst.trackParameters(), 0.001)
7722 )
7723 ) {
7724 rv.back().reset(nullptr);
7725 }
7726
7727 return rv;
7728 }
7729
7731 const EventContext & ctx,
7732 const TrackParameters & prev,
7733 const GXFTrackState & ts,
7734 PropDirection propdir,
7735 const MagneticFieldProperties& bf,
7736 bool calcderiv,
7737 bool holesearch
7738 ) const {
7739 std::unique_ptr<const TrackParameters> rv;
7740 std::optional<TransportJacobian> jac{};
7741
7742 if (calcderiv && !m_numderiv) {
7743 rv = m_propagator->propagateParameters(
7744 ctx, prev, ts.associatedSurface(), propdir, false, bf, jac, Trk::nonInteracting, false
7745 );
7746 } else {
7747 rv = m_propagator->propagateParameters(
7748 ctx, prev, ts.associatedSurface(), propdir, false, bf, Trk::nonInteracting, false
7749 );
7750
7751 if (rv != nullptr && calcderiv) {
7752 jac = numericalDerivatives(ctx, &prev, ts.associatedSurface(), propdir, bf);
7753 }
7754 }
7755
7756 std::optional<std::vector<std::unique_ptr<TrackParameters>>> extrapolation;
7757
7758 if (holesearch) {
7759 extrapolation = holesearchExtrapolation(ctx, prev, ts, propdir);
7760 }
7761
7762 return PropagationResult {
7763 std::move(rv),
7764 std::move(jac),
7765 std::move(extrapolation)
7766 };
7767 }
7768
7770 const EventContext & ctx,
7771 const TrackParameters & prev,
7772 const GXFTrackState & ts,
7773 PropDirection propdir,
7774 const MagneticFieldProperties& bf,
7775 bool calcderiv,
7776 bool holesearch
7777 ) const {
7779
7781 ctx, prev, ts, propdir, bf, calcderiv, holesearch
7782 );
7783
7784 if (rv.m_parameters == nullptr) {
7785 propdir = invertPropdir(propdir);
7786
7788 ctx, prev, ts, propdir, bf, calcderiv, holesearch
7789 );
7790 }
7791
7792 return rv;
7793 }
7794
7796 const EventContext& ctx,
7797 GXFTrajectory & trajectory,
7798 bool calcderiv
7799 ) const {
7800 // Loop over states, calculate track parameters and (optionally) jacobian at each state
7801 ATH_MSG_DEBUG("CalculateTrackParameters");
7802
7803 std::vector<std::unique_ptr<GXFTrackState>> & states = trajectory.trackStates();
7804 const int nstatesupstream = trajectory.numberOfUpstreamStates();
7805 const TrackParameters *prevtrackpar = trajectory.referenceParameters();
7806 std::unique_ptr<const TrackParameters> tmptrackpar;
7807
7808 for (int hitno = nstatesupstream - 1; hitno >= 0; hitno--) {
7809 const Surface &surf1 = states[hitno]->associatedSurface();
7811
7812 const DistanceSolution distsol = surf1.straightLineDistanceEstimate(
7813 prevtrackpar->position(), prevtrackpar->momentum().unit()
7814 );
7815
7816 const double distance = getDistance(distsol);
7817
7818 if (
7819 distance > 0 &&
7820 distsol.numberOfSolutions() > 0 &&
7821 prevtrackpar != trajectory.referenceParameters()
7822 ) {
7823 propdir = Trk::alongMomentum;
7824 }
7825
7827 ctx,
7828 *prevtrackpar,
7829 *states[hitno],
7830 propdir,
7831 trajectory.m_fieldprop,
7832 calcderiv,
7833 false
7834 );
7835
7836 if (
7837 propdir == Trk::alongMomentum &&
7838 (rv.m_parameters != nullptr) &&
7839 (prevtrackpar->position() - rv.m_parameters->position()).mag() > 5 * mm
7840 ) {
7841 ATH_MSG_DEBUG("Propagation in wrong direction");
7842
7843 }
7844
7845 if (rv.m_parameters == nullptr) {
7846 ATH_MSG_DEBUG("propagation failed, prev par: " << *prevtrackpar <<
7847 " pos: " << prevtrackpar->position() << " destination surface: " << surf1);
7849 }
7850
7851 states[hitno]->setTrackParameters(std::move(rv.m_parameters));
7852 const TrackParameters *currenttrackpar = states[hitno]->trackParameters();
7853 const Surface &surf = states[hitno]->associatedSurface();
7854
7855 if (rv.m_jacobian != std::nullopt) {
7856 if (
7857 states[hitno]->materialEffects() != nullptr &&
7858 states[hitno]->materialEffects()->deltaE() != 0 &&
7859 states[hitno]->materialEffects()->sigmaDeltaE() <= 0 &&
7860 !trajectory.m_straightline
7861 ) {
7862 const double p = 1. / std::abs(currenttrackpar->parameters()[Trk::qOverP]);
7863 const double de = std::abs(states[hitno]->materialEffects()->deltaE());
7864 const double mass = trajectory.mass();
7865 const double newp = std::sqrt(p * p + 2 * de * std::sqrt(mass * mass + p * p) + de * de);
7866 (*rv.m_jacobian) (4, 4) = ((p + p * de / std::sqrt(p * p + mass * mass)) / newp) * p * p / (newp * newp);
7867 }
7868
7869 states[hitno]->setJacobian(*rv.m_jacobian);
7870 } else if (calcderiv) {
7871 ATH_MSG_WARNING("Jacobian is null");
7873 }
7874
7875 GXFMaterialEffects *meff = states[hitno]->materialEffects();
7876
7877 if (meff != nullptr && hitno != 0) {
7878 std::variant<std::unique_ptr<const TrackParameters>, FitterStatusCode> r = updateEnergyLoss(
7879 surf, *meff, *states[hitno]->trackParameters(), trajectory.mass(), -1
7880 );
7881
7882 if (std::holds_alternative<FitterStatusCode>(r)) {
7883 return std::get<FitterStatusCode>(r);
7884 }
7885
7886 tmptrackpar = std::move(std::get<std::unique_ptr<const TrackParameters>>(r));
7887 prevtrackpar = tmptrackpar.get();
7888 } else {
7889 prevtrackpar = currenttrackpar;
7890 }
7891 }
7892
7893 prevtrackpar = trajectory.referenceParameters();
7894
7895 for (int hitno = nstatesupstream; hitno < (int) states.size(); hitno++) {
7896 const Surface &surf = states[hitno]->associatedSurface();
7898 const DistanceSolution distsol = surf.straightLineDistanceEstimate(prevtrackpar->position(), prevtrackpar->momentum().unit());
7899
7900 const double distance = getDistance(distsol);
7901
7902 if (distance < 0 && distsol.numberOfSolutions() > 0 && prevtrackpar != trajectory.referenceParameters()) {
7903 propdir = Trk::oppositeMomentum;
7904 }
7905
7907 ctx,
7908 *prevtrackpar,
7909 *states[hitno],
7910 propdir,
7911 trajectory.m_fieldprop,
7912 calcderiv,
7913 false
7914 );
7915
7916 if (
7917 (rv.m_parameters != nullptr) &&
7918 propdir == Trk::oppositeMomentum &&
7919 (prevtrackpar->position() - rv.m_parameters->position()).mag() > 5 * mm
7920 ) {
7921 ATH_MSG_DEBUG("Propagation in wrong direction");
7922 }
7923
7924 if (rv.m_parameters == nullptr) {
7925 ATH_MSG_DEBUG("propagation failed, prev par: " << *prevtrackpar <<
7926 " pos: " << prevtrackpar->
7927 position() << " destination surface: " << surf);
7929 }
7930
7931 if (rv.m_jacobian != std::nullopt) {
7932 if (
7933 states[hitno]->materialEffects() != nullptr &&
7934 states[hitno]->materialEffects()->deltaE() != 0 &&
7935 states[hitno]->materialEffects()->sigmaDeltaE() <= 0 &&
7936 !trajectory.m_straightline
7937 ) {
7938 const double p = 1 / std::abs(rv.m_parameters->parameters()[Trk::qOverP]);
7939 const double de = std::abs(states[hitno]->materialEffects()->deltaE());
7940 const double mass = trajectory.mass();
7941 double newp = p * p - 2 * de * std::sqrt(mass * mass + p * p) + de * de;
7942
7943 if (newp > 0) {
7944 newp = std::sqrt(newp);
7945 }
7946
7947 (*rv.m_jacobian) (4, 4) = ((p - p * de / std::sqrt(p * p + mass * mass)) / newp) * p * p / (newp * newp);
7948 }
7949
7950 states[hitno]->setJacobian(*rv.m_jacobian);
7951 } else if (calcderiv) {
7952 ATH_MSG_WARNING("Jacobian is null");
7954 }
7955
7956 GXFMaterialEffects *meff = states[hitno]->materialEffects();
7957
7958 if (meff != nullptr) {
7959 std::variant<std::unique_ptr<const TrackParameters>, FitterStatusCode> r = updateEnergyLoss(
7960 surf, *meff, *rv.m_parameters, trajectory.mass(), +1
7961 );
7962
7963 if (std::holds_alternative<FitterStatusCode>(r)) {
7964 return std::get<FitterStatusCode>(r);
7965 }
7966
7967 rv.m_parameters = std::move(std::get<std::unique_ptr<const TrackParameters>>(r));
7968 }
7969
7970 states[hitno]->setTrackParameters(std::move(rv.m_parameters));
7971 prevtrackpar = states[hitno]->trackParameters();
7972 }
7973
7975 }
7976
7977 std::variant<std::unique_ptr<const TrackParameters>, FitterStatusCode> GlobalChi2Fitter::updateEnergyLoss(
7978 const Surface & surf,
7979 const GXFMaterialEffects & meff,
7980 const TrackParameters & param,
7981 double mass,
7982 int sign
7983 ) const {
7984 const AmgVector(5) & old = param.parameters();
7985
7986 double newphi = old[Trk::phi0] + sign * meff.deltaPhi();
7987 double newtheta = old[Trk::theta] + sign * meff.deltaTheta();
7988
7989 if (!correctAngles(newphi, newtheta)) {
7990 ATH_MSG_DEBUG("Angles out of range, phi: " << newphi << " theta: " << newtheta);
7992 }
7993
7994 double newqoverp = 0;
7995
7996 if (meff.sigmaDeltaE() <= 0) {
7997 if (std::abs(old[Trk::qOverP]) < 1.e-12) {
7998 newqoverp = 0.;
7999 } else {
8000 const double oldp = std::abs(1 / old[Trk::qOverP]);
8001 const double newp2 = oldp * oldp - sign * 2 * std::abs(meff.deltaE()) * std::sqrt(mass * mass + oldp * oldp) + meff.deltaE() * meff.deltaE();
8002
8003 if (newp2 < 0) {
8004 ATH_MSG_DEBUG("Track killed by energy loss update");
8006 }
8007
8008 newqoverp = std::copysign(1 / std::sqrt(newp2), old[Trk::qOverP]);
8009 }
8010 } else {
8011 newqoverp = old[Trk::qOverP] + sign * .001 * meff.delta_p();
8012 }
8013
8014 return surf.createUniqueTrackParameters(
8015 old[0], old[1], newphi, newtheta, newqoverp, std::nullopt
8016 );
8017 }
8018
8020 const int nstatesupstream = trajectory.numberOfUpstreamStates();
8021 const int nscatupstream = trajectory.numberOfUpstreamScatterers();
8022 const int nbremupstream = trajectory.numberOfUpstreamBrems();
8023 const int nscats = trajectory.numberOfScatterers();
8024 const int nperpars = trajectory.numberOfPerigeeParameters();
8025 const int nfitpars = trajectory.numberOfFitParameters();
8026
8027 using Matrix55 = Eigen::Matrix<double, 5, 5>;
8028
8029 Matrix55 initialjac;
8030 initialjac.setZero();
8031 initialjac(4, 4) = 1;
8032
8033 Matrix55 jacvertex(initialjac);
8034
8035 std::vector<Matrix55, Eigen::aligned_allocator<Matrix55>> jacscat(trajectory.numberOfScatterers(), initialjac);
8036 std::vector<Matrix55, Eigen::aligned_allocator<Matrix55>> jacbrem(trajectory.numberOfBrems(), initialjac);
8037
8038 std::vector<std::unique_ptr<GXFTrackState>> & states = trajectory.trackStates();
8039 GXFTrackState *prevstate = nullptr;
8040 GXFTrackState *state = nullptr;
8041
8042 int hit_begin = 0;
8043 int hit_end = 0;
8044 int scatno = 0;
8045 int bremno = 0;
8046
8047 for (const bool forward : {false, true}) {
8048 if (forward) {
8049 hit_begin = nstatesupstream;
8050 hit_end = (int) states.size();
8051 scatno = nscatupstream;
8052 bremno = nbremupstream;
8053 } else {
8054 hit_begin = nstatesupstream - 1;
8055 hit_end = 0;
8056 scatno = trajectory.numberOfUpstreamScatterers() - 1;
8057 bremno = trajectory.numberOfUpstreamBrems() - 1;
8058 }
8059
8060 for (
8061 int hitno = hit_begin;
8062 forward ? (hitno < hit_end) : (hitno >= hit_end);
8063 hitno += (forward ? 1 : -1)
8064 ) {
8065
8066 state = states[hitno].get();
8067
8068 const bool fillderivmat = (!state->getStateType(TrackStateOnSurface::Scatterer) && !state->getStateType(TrackStateOnSurface::BremPoint));
8069
8070 if (fillderivmat && state->derivatives().cols() != nfitpars) {
8071 state->derivatives().resize(5, nfitpars);
8072 state->derivatives().setZero();
8073 }
8074
8075 int jminscat = 0;
8076 int jmaxscat = 4;
8077 int jminbrem = 0;
8078 const int jmaxbrem = 4;
8079
8080 if (hitno == (forward ? hit_end - 1 : 0)) {
8081 if (!fillderivmat) {
8082 break;
8083 }
8084 jminscat = 2;
8085 jmaxscat = 3;
8086 jminbrem = 4;
8087 }
8088
8089 Eigen::Matrix<double, 5, 5> & jac = state->jacobian();
8090
8091 if (hitno == nstatesupstream + (forward ? 0 : -1)) {
8092 jacvertex.block<4, 5>(0, 0) = jac.block<4, 5>(0, 0);
8093 jacvertex(4, 4) = jac(4, 4);
8094 } else {
8095 int jmin = 0;
8096 int jmax = 0;
8097 int jcnt = 0;
8098 int lp_bgn = 0;
8099 int lp_end = 0;
8100
8101 jmin = jminscat;
8102 jmax = jmaxscat;
8103 jcnt = jmax - jmin + 1;
8104
8105 lp_bgn = forward ? nscatupstream : nscatupstream - 1;
8106 lp_end = scatno;
8107
8108 for (int i = lp_bgn; forward ? (i < lp_end) : (i > lp_end); i += (forward ? 1 : -1)) {
8109 if (
8110 i == scatno + (forward ? -1 : 1) &&
8111 prevstate != nullptr &&
8113 (!trajectory.prefit() || prevstate->materialEffects()->deltaE() == 0)
8114 ) {
8115 jacscat[i].block(0, jmin, 4, jcnt) = jac.block(0, jmin, 4, jcnt);
8116 jacscat[i](4, 4) = jac(4, 4);
8117 } else {
8118 calculateJac(jac, jacscat[i], jmin, jmax);
8119 }
8120
8121 if (fillderivmat) {
8122 Eigen::MatrixXd & derivmat = state->derivatives();
8123 const int scatterPos = nperpars + 2 * i;
8124
8125 derivmat.block<4, 2>(0, scatterPos) = (forward ? 1 : -1) * jacscat[i].block<4, 2>(0, 2);
8126 }
8127 }
8128
8129 jmin = jminbrem;
8130 jmax = jmaxbrem;
8131 jcnt = jmax - jmin + 1;
8132
8133 lp_bgn = forward ? nbremupstream : nbremupstream - 1;
8134 lp_end = bremno;
8135
8136 for (int i = lp_bgn; forward ? (i < lp_end) : (i > lp_end); i += (forward ? 1 : -1)) {
8137 if (
8138 i == bremno + (forward ? -1 : 1) &&
8139 prevstate &&
8140 prevstate->materialEffects() &&
8141 prevstate->materialEffects()->sigmaDeltaE() > 0
8142 ) {
8143 jacbrem[i].block(0, jmin, 4, jcnt) = jac.block(0, jmin, 4, jcnt);
8144 jacbrem[i](4, 4) = jac(4, 4);
8145 } else {
8146 calculateJac(jac, jacbrem[i], jmin, jmax);
8147 }
8148
8149 if (fillderivmat) {
8150 Eigen::MatrixXd & derivmat = state->derivatives();
8151 const int scatterPos = nperpars + 2 * nscats + i;
8152
8153 derivmat.block<5, 1>(0, scatterPos) = (forward ? .001 : -.001) * jacbrem[i].block<5, 1>(0, 4);
8154 }
8155 }
8156
8157 calculateJac(jac, jacvertex, 0, 4);
8158 }
8159
8160 if (fillderivmat) {
8161 Eigen::MatrixXd & derivmat = state->derivatives();
8162 derivmat.block(0, 0, 4, nperpars) = jacvertex.block(0, 0, 4, nperpars);
8163
8164 if (nperpars == 5) {
8165 derivmat.col(4).segment(0, 4) *= .001;
8166 derivmat(4, 4) = .001 * jacvertex(4, 4);
8167 }
8168 }
8169
8170 if (
8172 (!trajectory.prefit() || states[hitno]->materialEffects()->deltaE() == 0)
8173 ) {
8174 scatno += (forward ? 1 : -1);
8175 }
8176
8177 if (
8178 states[hitno]->materialEffects() &&
8179 states[hitno]->materialEffects()->sigmaDeltaE() > 0
8180 ) {
8181 bremno += (forward ? 1 : -1);
8182 }
8183
8184 prevstate = states[hitno].get();
8185 }
8186 }
8187 }
8188
8189 void
8190
8192 Amg::SymMatrixX & fullcovmat,
8193 bool onlylocal) const {
8194 //
8195 // Calculate track errors at each state, except scatterers and brems
8196 //
8197 ATH_MSG_DEBUG("CalculateTrackErrors");
8198
8199 std::vector<std::unique_ptr<GXFTrackState>> & states = trajectory.trackStates();
8200 const int nstatesupstream = trajectory.numberOfUpstreamStates();
8201 std::vector < int >indices(states.size());
8202 GXFTrackState *prevstate = nullptr;
8203 int i = nstatesupstream;
8204 for (int j = 0; j < (int) states.size(); j++) {
8205 if (j < nstatesupstream) {
8206 i--;
8207 indices[j] = i;
8208 } else {
8209 indices[j] = j;
8210 }
8211 }
8212 for (int stateno = 0; stateno < (int) states.size(); stateno++) {
8213 if (stateno == 0 || stateno == nstatesupstream) {
8214 prevstate = nullptr;
8215 }
8216 const int index = indices[stateno];
8217 std::unique_ptr<GXFTrackState> & state = states[index];
8218 if (state->materialEffects() != nullptr) {
8219 prevstate = state.get();
8220 continue;
8221 }
8222
8223 if (!state->hasTrackCovariance()) {
8224 state->zeroTrackCovariance();
8225 }
8226 AmgMatrix(5, 5) & trackerrmat = state->trackCovariance();
8227
8228 if ((prevstate != nullptr) &&
8231 && !onlylocal) {
8232 Eigen::Matrix<double, 5, 5> & jac = state->jacobian();
8233 const AmgMatrix(5, 5)& prevcov = states[indices[stateno - 1]]->trackCovariance();
8234
8235 trackerrmat = jac * prevcov * jac.transpose();
8236 } else {
8237 Amg::MatrixX & derivatives = state->derivatives();
8238
8239 trackerrmat = derivatives * fullcovmat * derivatives.transpose();
8240 }
8241
8242 if (!onlylocal) {
8243 const MeasurementBase *measurement = state->measurement();
8244 const Amg::MatrixX & meascov = measurement->localCovariance();
8245 int j = 0;
8246 int indices[5] = {
8247 -1, -1, -1, -1, -1
8248 };
8249 bool errorok = true;
8250 for (int i = 0; i < 5; i++) {
8252 if (state->getStateType(TrackStateOnSurface::Measurement)
8253 && trackerrmat(i, i) > meascov(j, j)) {
8254 errorok = false;
8255 const double scale = std::sqrt(meascov(j, j) / trackerrmat(i, i));
8256 trackerrmat(i, i) = meascov(j, j);
8257 for (int k = 0; k < 5; k++) {
8258 if (k != i) {
8259 trackerrmat(k, i) *= scale;
8260 }
8261 }
8262 indices[i] = j;
8263 }
8264 j++;
8265 }
8266 }
8267 for (int i = 0; i < 5; i++) {
8268 if (indices[i] == -1) {
8269 continue;
8270 }
8271 for (int j = 0; j < 5; j++) {
8272 if (indices[j] == -1) {
8273 continue;
8274 }
8275 trackerrmat(i, j) = meascov(indices[i], indices[j]);
8276 }
8277 }
8278 if (trajectory.m_straightline) {
8279 trackerrmat(4, 4) = 1e-20;
8280 }
8281
8282 const TrackParameters *tmptrackpar =
8283 state->trackParameters();
8284
8285 std::optional<AmgMatrix(5, 5)> trkerrmat;
8286
8287 if (state->hasTrackCovariance()) {
8288 trkerrmat = (state->trackCovariance());
8289 } else {
8290 trkerrmat = std::nullopt;
8291 }
8292
8293 const AmgVector(5) & tpars = tmptrackpar->parameters();
8294 std::unique_ptr<const TrackParameters> trackpar(
8295 tmptrackpar->associatedSurface().createUniqueTrackParameters(tpars[0],
8296 tpars[1],
8297 tpars[2],
8298 tpars[3],
8299 tpars[4],
8300 std::move(trkerrmat))
8301 );
8302 state->setTrackParameters(std::move(trackpar));
8303 FitQualityOnSurface fitQual{};
8304 if (state->getStateType(TrackStateOnSurface::Measurement)) {
8305 if (errorok && trajectory.nDOF() > 0) {
8306 fitQual = m_updator->fullStateFitQuality(
8307 *state->trackParameters(),
8308 measurement->localParameters(),
8309 measurement->localCovariance()
8310 );
8311 } else {
8312 fitQual = FitQualityOnSurface(0, state->numberOfMeasuredParameters());
8313 }
8314 }
8315 state->setFitQuality(fitQual);
8316 }
8317 prevstate = state.get();
8318 }
8319 }
8320
8321 std::optional<TransportJacobian>
8323 const EventContext& ctx,
8324 const TrackParameters* prevpar,
8325 const Surface & surf,
8326 PropDirection propdir,
8327 const MagneticFieldProperties& fieldprop) const
8328 {
8329 double J[25] = {
8330 1, 0, 0, 0, 0,
8331 0, 1, 0, 0, 0,
8332 0, 0, 1, 0, 0,
8333 0, 0, 0, 1, 0,
8334 0, 0, 0, 0, 1
8335 };
8336 std::optional<TransportJacobian> jac = std::make_optional<TransportJacobian>(J);
8337 const TrackParameters *tmpprevpar = prevpar;
8338 double eps[5] = {
8339 0.01, 0.01, 0.00001, 0.00001, 0.000000001
8340 };
8341
8342 const AmgVector(5) & vec = tmpprevpar->parameters();
8343
8344 const bool cylsurf = surf.type() == Trk::SurfaceType::Cylinder;
8345 const bool discsurf = surf.type() == Trk::SurfaceType::Disc;
8346 const Surface & previousSurface = tmpprevpar->associatedSurface();
8347 const bool thiscylsurf = previousSurface.type() == Trk::SurfaceType::Cylinder;
8348 const bool thisdiscsurf = previousSurface.type() == Trk::SurfaceType::Disc;
8349
8350 for (int i = 0; i < 5; i++) {
8351 AmgVector(5) vecpluseps = vec, vecminuseps = vec;
8352
8353 if (thisdiscsurf && i == 1) {
8354 eps[i] /= vec[0];
8355 }
8356
8357 vecpluseps[Trk::ParamDefsAccessor::pardef[i]] += eps[i];
8358 vecminuseps[Trk::ParamDefsAccessor::pardef[i]] -= eps[i];
8359 if (i == 0 && thiscylsurf) {
8360 vecminuseps[i] = -std::remainder(-vecminuseps[i], 2 * M_PI * previousSurface.bounds().r());
8361 } else if (i == 1 && thisdiscsurf) {
8362 vecpluseps[i] = -std::remainder(-vecpluseps[i], 2 * M_PI);
8363 }
8364 correctAngles(vecminuseps[Trk::phi], vecminuseps[Trk::theta]);
8365 correctAngles(vecpluseps[Trk::phi], vecpluseps[Trk::theta]);
8366
8367 std::unique_ptr<const TrackParameters> parpluseps(
8369 vecpluseps[0],
8370 vecpluseps[1],
8371 vecpluseps[2],
8372 vecpluseps[3],
8373 vecpluseps[4],
8374 std::nullopt
8375 )
8376 );
8377 const std::unique_ptr<const TrackParameters> parminuseps(
8379 vecminuseps[0],
8380 vecminuseps[1],
8381 vecminuseps[2],
8382 vecminuseps[3],
8383 vecminuseps[4],
8384 std::nullopt
8385 )
8386 );
8387
8388 std::unique_ptr<const TrackParameters> newparpluseps(
8389 m_propagator->propagateParameters(
8390 ctx,
8391 *parpluseps,
8392 surf,
8393 propdir,
8394 false,
8395 fieldprop,
8397 )
8398 );
8399 std::unique_ptr<const TrackParameters> newparminuseps(
8400 m_propagator->propagateParameters(
8401 ctx,
8402 *parminuseps,
8403 surf,
8404 propdir,
8405 false,
8406 fieldprop,
8408 )
8409 );
8410
8411 const PropDirection propdir2 =
8412 (propdir ==
8414 if (newparpluseps == nullptr) {
8415 newparpluseps =
8416 m_propagator->propagateParameters(
8417 ctx,
8418 *parpluseps,
8419 surf,
8420 propdir2,
8421 false,
8422 fieldprop,
8424 );
8425 }
8426 if (newparminuseps == nullptr) {
8427 newparminuseps =
8428 m_propagator->propagateParameters(
8429 ctx,
8430 *parminuseps,
8431 surf,
8432 propdir2,
8433 false,
8434 fieldprop,
8436 );
8437 }
8438 if ((newparpluseps == nullptr) || (newparminuseps == nullptr)) {
8439 return nullptr;
8440 }
8441
8442 for (int j = 0; j < 5; j++) {
8443 double diff = newparpluseps->parameters()[Trk::ParamDefsAccessor::pardef[j]] -
8444 newparminuseps->parameters()[Trk::ParamDefsAccessor::pardef[j]];
8445
8446 if (j == 0 && cylsurf) {
8447 diff = -std::remainder(-diff, 2 * M_PI * surf.bounds().r());
8448 } else if (j == 1 && discsurf) {
8449 diff = -std::remainder(-diff, 2 * M_PI);
8450 }
8451
8452 (*jac) (j, i) = diff / (2 * eps[i]);
8453 }
8454
8455 }
8456 return jac;
8457 }
8458
8459 bool
8460 GlobalChi2Fitter::isMuonTrack(const Track & intrk1) const {
8461 const auto *pDataVector = intrk1.measurementsOnTrack();
8462 auto nmeas1 = pDataVector->size();
8463 const auto *pLastValue = (*pDataVector)[nmeas1 - 1];
8464 //
8465 const bool lastMeasIsRIO = pLastValue->type(Trk::MeasurementBaseType::RIO_OnTrack);
8466 const bool lastMeasIsCompetingRIO = pLastValue->type(Trk::MeasurementBaseType::CompetingRIOsOnTrack);
8467 //we only need the RIO on track pointer to be valid to identify
8468 const RIO_OnTrack *testrot{};
8469 //
8470 if (lastMeasIsRIO){
8471 testrot = static_cast<const RIO_OnTrack *>(pLastValue);
8472 } else {
8473 if (lastMeasIsCompetingRIO){
8474 const auto *testcrot = static_cast<const CompetingRIOsOnTrack*>(pLastValue);
8475 testrot = &testcrot->rioOnTrack(0);
8476 }
8477 }
8478 //still undefined, so try penultimate measurement as well
8479 if (testrot == nullptr) {
8480 const auto *pPenultimate = (*pDataVector)[nmeas1 - 2];
8481 const bool penultimateIsRIO = pPenultimate->type(Trk::MeasurementBaseType::RIO_OnTrack);
8482 const bool penultimateIsCompetingRIO = pPenultimate->type(Trk::MeasurementBaseType::CompetingRIOsOnTrack);
8483 if(penultimateIsRIO){
8484 testrot = static_cast<const RIO_OnTrack *>(pPenultimate);
8485 } else {
8486 if (penultimateIsCompetingRIO){
8487 const auto *testcrot = static_cast<const CompetingRIOsOnTrack*>(pPenultimate);
8488 testrot = &testcrot->rioOnTrack(0);
8489 }
8490 }
8491 }
8492 //check: we've successfully got a valid RIO on track; it's not the inner detector;
8493 //it's really the muon detector (question: doesn't that make the previous check redundant?)
8494 return (
8495 (testrot != nullptr) &&
8496 !m_DetID->is_indet(testrot->identify()) &&
8497 m_DetID->is_muon(testrot->identify())
8498 );
8499 }
8500
8501 void
8503 const
8504 {
8507 ctx
8508 );
8509
8510 const AtlasFieldCacheCondObj * cond_obj(*rh);
8511
8512 if (cond_obj == nullptr) {
8513 ATH_MSG_ERROR("Failed to create AtlasFieldCacheCondObj!");
8514 return;
8515 }
8516
8517 cond_obj->getInitializedCache(cache.m_field_cache);
8518 }
8519
8521 std::stringstream msg;
8522 msg << "Failed to get conditions data " << m_trackingGeometryReadKey.key() << ".";
8523 throw std::runtime_error(msg.str());
8524 }
8525
8526 bool GlobalChi2Fitter::ensureValidEntranceCalo(const EventContext& ctx, Cache& cache) const {
8527 if (cache.m_caloEntrance == nullptr) {
8528 const TrackingGeometry *geometry = trackingGeometry(cache, ctx);
8529
8530 if (geometry != nullptr) {
8531 cache.m_caloEntrance = geometry->trackingVolume("InDet::Containers::InnerDetector");
8532 } else {
8533 ATH_MSG_ERROR("Tracking Geometry not available");
8534 }
8535
8536 /*
8537 * Check, if we managed to find an entrance.
8538 */
8539 if (cache.m_caloEntrance == nullptr) {
8540 ATH_MSG_ERROR("calo entrance not available");
8541 }
8542 }
8543
8544 return cache.m_caloEntrance != nullptr;
8545 }
8546
8548 if (cache.m_msEntrance == nullptr) {
8549 const TrackingGeometry *geometry = trackingGeometry(cache, ctx);
8550
8551 if (geometry != nullptr) {
8552 cache.m_msEntrance = geometry->trackingVolume("MuonSpectrometerEntrance");
8553 } else {
8554 ATH_MSG_ERROR("Tracking Geometry not available");
8555 }
8556
8557 /*
8558 * Check, if we managed to find an entrance.
8559 */
8560 if (cache.m_msEntrance == nullptr) {
8561 ATH_MSG_ERROR("MS entrance not available");
8562 }
8563 }
8564
8565 return cache.m_msEntrance != nullptr;
8566 }
8567}
#define M_PI
Scalar perp() const
perp method - perpendicular length
Scalar deltaPhi(const MatrixBase< Derived > &vec) const
Scalar phi() const
phi method
Scalar theta() const
theta method
Scalar mag() const
mag method
#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,...)
This class provides an interface to generate or decode an identifier for the upper levels of the dete...
double charge(const T &p)
Definition AtlasPID.h:999
std::vector< size_t > vec
#define AmgSymMatrix(dim)
#define AmgVector(rows)
#define AmgMatrix(rows, cols)
static const double MeV
std::pair< std::vector< unsigned int >, bool > res
bool hit(const Container &ids, int pdgId)
unsigned int uint
static Double_t a
static const uint32_t nHits
size_t size() const
Number of registered mappings.
int sign(int a)
int imax(int i, int j)
void diff(const TrigCaloCluster &a, const TrigCaloCluster &b, std::map< std::string, double > &varChange)
void getInitializedCache(MagField::AtlasFieldCache &cache) const
get B field cache for evaluation as a function of 2-d or 3-d position.
DataModel_detail::const_iterator< DataVector > const_iterator
Standard const_iterator.
Definition DataVector.h:861
const_iterator end() const noexcept
Return a const_iterator pointing past the end of the collection.
const_iterator begin() const noexcept
Return a const_iterator pointing at the beginning 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 is_valid() const
Check if id is in a valid state.
Class to hold geometrical description of a silicon detector element.
virtual bool isValid() override final
Can the handle be successfully dereferenced?
virtual std::span< T *const > arrayObjects()=0
Return all objects of the Array non-const we can still modify the T.
Base class for all CompetingRIOsOnTack implementations, extends the common MeasurementBase.
virtual const RIO_OnTrack & rioOnTrack(unsigned int) const =0
returns the RIO_OnTrack (also known as ROT) objects depending on the integer.
Bounds for a cylindrical Surface.
virtual double r() const override final
This method returns the radius.
double halflengthZ() const
This method returns the halflengthZ.
Class to describe a cylindrical detector layer for tracking, it inhertis from both,...
Class for a CylinderSurface in the ATLAS detector.
virtual const CylinderBounds & bounds() const override final
This method returns the CylinderBounds by reference (NoBounds is not possible for cylinder).
Bounds for a cylindrical Volume, the decomposeToSurfaces method creates a vector of up to 6 surfaces:
Class to describe the bounds for a planar DiscSurface.
Definition DiscBounds.h:44
double rMax() const
This method returns outer radius.
double rMin() const
This method returns inner radius.
Class to describe a disc-like detector layer for tracking, it inhertis from both, Layer base class an...
Definition DiscLayer.h:45
Class for a DiscSurface in the ATLAS detector.
Definition DiscSurface.h:54
const SurfaceBounds & bounds() const override final
This method returns the bounds by reference.
Access to distance solutions.
double second() const
Distance to second intersection solution along direction (for a cylinder surface).
int numberOfSolutions() const
Number of intersection solutions.
double first() const
Distance to first intersection solution along direction.
double sigmaDeltaE() const
returns the symmatric error
double deltaE() const
returns the
int numberDoF() const
returns the number of degrees of freedom of the overall track or vertex fit as integer
Definition FitQuality.h:60
double chiSquared() const
returns the of the overall track fit
Definition FitQuality.h:56
Status codes for track fitters.
@ ExtrapolationFailureDueToSmallMomentum
extrapolation failed due to small momentum
@ OutlierLogicFailure
outlier logic failed
@ ExtrapolationFailure
extrapolation failed
@ Success
fit successfull
class that is similar to MaterialEffectsOnTrack, but has 'set' methods for more flexibility during tr...
const Surface & associatedSurface() const
void setScatteringSigmas(double, double)
void setMaterialProperties(const MaterialProperties *)
Set the material properties of this material effects instance.
const MaterialProperties * materialProperties() const
bool getStateType(TrackStateOnSurface::TrackStateOnSurfaceType type) const
Retrieve the value of a specific type bit.
Amg::MatrixX & derivatives()
Eigen::Matrix< double, 5, 5 > & jacobian()
void setSinStereo(double)
const MeasurementBase * measurement(void)
Amg::Vector3D position()
TrackState::MeasurementType measurementType()
bool hasTrackCovariance(void) const
void setMeasurement(std::unique_ptr< const MeasurementBase >)
void setMeasurementErrors(const double *)
const TrackParameters * trackParameters(void) const
double * measurementErrors()
GXFMaterialEffects * materialEffects()
double sinStereo() const
void setTrackParameters(std::unique_ptr< const TrackParameters >)
const Surface & associatedSurface() const
Internal representation of the track, used in the track fit.
void setPrevChi2(double)
int numberOfOutliers() const
std::vector< double > & brems()
double chi2() const
double totalX0() const
void setNumberOfScatterers(int)
double mass() const
int numberOfSiliconHits() const
std::vector< std::pair< const Layer *, const Layer * > > & upstreamMaterialLayers()
int numberOfFitParameters() const
int numberOfTRTPrecHits() const
double prevchi2() const
Amg::MatrixX & weightedResidualDerivatives()
const std::vector< std::unique_ptr< GXFTrackState > > & trackStates() const
int numberOfTRTTubeHits() const
int numberOfTRTHits() const
int numberOfUpstreamBrems() const
double totalEnergyLoss() const
int numberOfUpstreamScatterers() const
int numberOfUpstreamStates() const
MagneticFieldProperties m_fieldprop
void setNumberOfPerigeeParameters(int)
const TrackParameters * referenceParameters()
std::vector< std::pair< double, double > > & scatteringSigmas()
std::pair< GXFTrackState *, GXFTrackState * > findFirstLastMeasurement(void)
void addMaterialState(std::unique_ptr< GXFTrackState >, int index=-1)
int numberOfScatterers() const
bool addMeasurementState(std::unique_ptr< GXFTrackState >, int index=-1)
std::vector< std::pair< double, double > > & scatteringAngles()
void addBasicState(std::unique_ptr< GXFTrackState >, int index=-1)
Amg::VectorX & residuals()
void setBrems(std::vector< double > &)
int numberOfPerigeeParameters() const
int numberOfBrems() const
Amg::VectorX & errors()
void setOutlier(int, bool isoutlier=true)
void updateTRTHitCount(int index, float oldError)
void setScatteringAngles(std::vector< std::pair< double, double > > &)
void setReferenceParameters(std::unique_ptr< const TrackParameters >)
Gaudi::Property< int > m_maxoutliers
ToolHandle< IBoundaryCheckTool > m_boundaryCheckTool
std::unique_ptr< Track > makeTrack(const EventContext &ctx, Cache &, GXFTrajectory &, const ParticleHypothesis) const
void tryToConverge(const Cache &cache, GXFTrajectory &trajectory, const int it) const
void makeProtoState(Cache &, GXFTrajectory &, const TrackStateOnSurface *, int index=-1) const
Gaudi::Property< double > m_p
FitterStatusCode calculateTrackParameters(const EventContext &ctx, GXFTrajectory &, bool) const
Track * myfit(const EventContext &ctx, Cache &, GXFTrajectory &, const TrackParameters &, const RunOutlierRemoval runOutlier=false, const ParticleHypothesis matEffects=nonInteracting) const
void fillResidualsAndErrors(const EventContext &ctx, const Cache &cache, GXFTrajectory &trajectory, const int it, Amg::VectorX &b, int &bremno_maxbrempull, GXFTrackState *&state_maxbrempull) const
Gaudi::Property< double > m_scalefactor
Gaudi::Property< bool > m_rejectLargeNScat
std::variant< std::unique_ptr< const TrackParameters >, FitterStatusCode > updateEnergyLoss(const Surface &, const GXFMaterialEffects &, const TrackParameters &, double, int) const
ToolHandle< Trk::ITrkMaterialProviderTool > m_caloMaterialProvider
Gaudi::Property< bool > m_trtrecal
bool processTrkVolume(Cache &, const Trk::TrackingVolume *tvol) const
ToolHandle< IPropagator > m_propagator
Gaudi::Property< bool > m_straightlineprop
Gaudi::Property< bool > m_fiteloss
std::optional< GlobalChi2Fitter::TrackHoleCount > holeSearchProcess(const EventContext &ctx, const std::vector< std::reference_wrapper< GXFTrackState > > &states) const
Conduct a hole search between a list of states, possibly reusing existing information.
static std::optional< std::pair< Amg::Vector3D, double > > addMaterialFindIntersectionDisc(Cache &cache, const DiscSurface &surface, const TrackParameters &param1, const TrackParameters &param2, const ParticleHypothesis mat)
Find the intersection of a set of track parameters onto a disc surface.
ToolHandle< INavigator > m_navigator
void updateSystemWithMaxBremPull(GXFTrajectory &trajectory, const int bremno_maxbrempull, GXFTrackState *state_maxbrempull, Amg::SymMatrixX &a) const
SG::ReadHandleKey< Trk::ClusterSplitProbabilityContainer > m_clusterSplitProbContainer
virtual ~GlobalChi2Fitter()
ToolHandle< IRIO_OnTrackCreator > m_broadROTcreator
std::unique_ptr< GXFTrackState > makeTrackFindPerigee(const EventContext &, Cache &, GXFTrajectory &, const ParticleHypothesis) const
FitterStatusCode updateFitParameters(GXFTrajectory &, const Amg::VectorX &, const Amg::SymMatrixX &) const
Method to update peregee parameters, scattering angles, and brems.
void addMaterial(const EventContext &ctx, Cache &, GXFTrajectory &, const TrackParameters *, ParticleHypothesis) const
static void fillFirstLastMeasurement(Cache &cache, GXFTrajectory &trajectory)
PropagationResult calculateTrackParametersPropagateHelper(const EventContext &, const TrackParameters &, const GXFTrackState &, PropDirection, const MagneticFieldProperties &, bool, bool) const
Helper method that encapsulates calls to the propagator tool in the calculateTrackParameters() method...
ToolHandle< IUpdator > m_updator
static void fillBfromMeasurements(const Cache &cache, GXFTrajectory &trajectory, Amg::VectorX &b)
std::unique_ptr< const TrackParameters > makeTrackFindPerigeeParameters(const EventContext &, Cache &, GXFTrajectory &, const ParticleHypothesis) const
SG::ReadCondHandleKey< AtlasFieldCacheCondObj > m_field_cache_key
Gaudi::Property< bool > m_domeastrackpar
Gaudi::Property< int > m_fixbrem
Track * backupCombinationStrategy(const EventContext &ctx, Cache &, const Track &, const Track &, GXFTrajectory &, std::vector< MaterialEffectsOnTrack > &) const
GlobalChi2Fitter(const std::string &, const std::string &, const IInterface *)
Track * mainCombinationStrategy(const EventContext &ctx, Cache &, const Track &, const Track &, GXFTrajectory &, std::vector< MaterialEffectsOnTrack > &) const
static void compensatePhiWeights(Cache &cache, GXFTrajectory &trajectory, Amg::SymMatrixX &a)
const TrackingGeometry * trackingGeometry(Cache &cache, const EventContext &ctx) const
Gaudi::Property< int > m_maxit
PropagationResult calculateTrackParametersPropagate(const EventContext &, const TrackParameters &, const GXFTrackState &, PropDirection, const MagneticFieldProperties &, bool, bool) const
Propagate onto a track state, collecting new track parameters, and optionally the Jacobian and possib...
ToolHandle< IExtrapolator > m_extrapolator
void makeProtoStateFromMeasurement(Cache &, GXFTrajectory &, const MeasurementBase *, const TrackParameters *trackpar=nullptr, bool isoutlier=false, int index=-1) const
std::optional< TransportJacobian > numericalDerivatives(const EventContext &ctx, const TrackParameters *, const Surface &, PropDirection, const MagneticFieldProperties &) const
void addIDMaterialFast(const EventContext &ctx, Cache &cache, GXFTrajectory &track, const TrackParameters *parameters, ParticleHypothesis part) const
A faster strategy for adding scatter material to tracks, works only for inner detector tracks.
static void fillAfromScatterers(GXFTrajectory &trajectory, Amg::SymMatrixX &a)
void updatePixelROTs(GXFTrajectory &, Amg::SymMatrixX &, Amg::VectorX &, const EventContext &evtctx) const
Update the Pixel ROT using the current trajectory/local track parameters.
Gaudi::Property< double > m_outlcut
Gaudi::Property< bool > m_calomat
Gaudi::Property< bool > m_fillderivmatrix
ToolHandle< IMaterialEffectsOnTrackProvider > m_calotoolparam
ToolHandle< IRIO_OnTrackCreator > m_ROTcreator
GXFTrajectory * runTrackCleanerSilicon(const EventContext &ctx, Cache &, GXFTrajectory &, Amg::SymMatrixX &, Amg::SymMatrixX &, Amg::VectorX &, bool) const
std::unique_ptr< const TrackParameters > makePerigee(Cache &, const TrackParameters &, const ParticleHypothesis) const
virtual std::unique_ptr< Track > fit(const EventContext &ctx, const PrepRawDataSet &, const TrackParameters &, const RunOutlierRemoval runOutlier=false, const ParticleHypothesis matEffects=nonInteracting) const override final
Gaudi::Property< double > m_chi2cut
void throwFailedToGetTrackingGeomtry() const
virtual Track * alignmentFit(const EventContext &ctx, AlignmentCache &, const Track &, const RunOutlierRemoval runOutlier=false, const ParticleHypothesis matEffects=Trk::nonInteracting) const override
Gaudi::Property< bool > m_extensioncuts
bool ensureValidEntranceCalo(const EventContext &ctx, Cache &cache) const
static void addMaterialGetLayers(Cache &cache, std::vector< std::pair< const Layer *, const Layer * > > &layers, std::vector< std::pair< const Layer *, const Layer * > > &uplayers, const std::vector< std::unique_ptr< GXFTrackState > > &states, GXFTrackState &first, GXFTrackState &last, const TrackParameters *refpar, bool hasmat)
Collect all possible layers that a given track could have passed through.
static std::optional< std::pair< Amg::Vector3D, double > > addMaterialFindIntersectionCyl(Cache &cache, const CylinderSurface &surface, const TrackParameters &param1, const TrackParameters &param2, const ParticleHypothesis mat)
Find the intersection of a set of track parameters onto a cylindrical surface.
void runTrackCleanerTRT(Cache &, GXFTrajectory &, Amg::SymMatrixX &, Amg::VectorX &, Amg::SymMatrixX &, bool, bool, int, const EventContext &ctx) const
static void calculateDerivatives(GXFTrajectory &)
Gaudi::Property< bool > m_decomposesegments
void calculateTrackErrors(GXFTrajectory &, Amg::SymMatrixX &, bool) const
bool ensureValidEntranceMuonSpectrometer(const EventContext &ctx, Cache &cache) const
bool isMuonTrack(const Track &) const
std::vector< std::unique_ptr< TrackParameters > > holesearchExtrapolation(const EventContext &ctx, const TrackParameters &src, const GXFTrackState &dst, PropDirection propdir) const
Helper method which performs an extrapolation with additional logic for hole search.
virtual StatusCode finalize() override
Gaudi::Property< bool > m_useCaloTG
std::vector< std::reference_wrapper< GXFTrackState > > holeSearchStates(GXFTrajectory &trajectory) const
Extracts a collection of track states which are important for hole search.
Track * fitIm(const EventContext &ctx, Cache &cache, const Track &inputTrack, const RunOutlierRemoval runOutlier, const ParticleHypothesis matEffects) const
Gaudi::Property< bool > m_redoderivs
void initFieldCache(const EventContext &ctx, Cache &cache) const
Initialize a field cache inside a fit cache object.
Gaudi::Property< int > m_maxitPixelROT
ToolHandle< IMaterialEffectsOnTrackProvider > m_calotool
ToolHandle< IMaterialEffectsUpdator > m_matupdator
void holeSearchHelper(const std::vector< std::unique_ptr< TrackParameters > > &hc, std::set< Identifier > &id_set, std::set< Identifier > &sct_set, TrackHoleCount &rv, bool count_holes, bool count_dead) const
Helper method for the hole search that does the actual counting of holes and dead modules.
ToolHandle< IResidualPullCalculator > m_residualPullCalculator
void addMaterialUpdateTrajectory(Cache &cache, GXFTrajectory &track, int offset, std::vector< std::pair< const Layer *, const Layer * > > &layers, const TrackParameters *ref1, const TrackParameters *ref2, ParticleHypothesis mat) const
Given layer information, probe those layers for scatterers and add them to a track.
ToolHandle< IEnergyLossUpdator > m_elosstool
void fillDerivatives(GXFTrajectory &traj) const
Gaudi::Property< bool > m_numderiv
SG::ReadCondHandleKey< TrackingGeometry > m_trackingGeometryReadKey
Gaudi::Property< bool > m_acceleration
static bool tryToWeightAfromMaterial(Cache &cache, GXFTrajectory &trajectory, Amg::SymMatrixX &a, const bool doDeriv, const int it, const double oldRedChi2, const double newRedChi2)
const AtlasDetectorID * m_DetID
static void fillAfromMeasurements(const Cache &cache, GXFTrajectory &trajectory, Amg::SymMatrixX &a)
virtual StatusCode initialize() override
static void makeTrackFillDerivativeMatrix(Cache &, GXFTrajectory &)
Gaudi::Property< bool > m_holeSearch
FitterStatusCode runIteration(const EventContext &ctx, Cache &cache, GXFTrajectory &trajectory, const int it, Amg::SymMatrixX &a, Amg::VectorX &b, Amg::SymMatrixX &lu, bool &doDeriv) const
Gaudi::Property< bool > m_createSummary
ToolHandle< IMultipleScatteringUpdator > m_scattool
Interface class IPropagators It inherits from IAlgTool.
Definition IPropagator.h:54
LayerIndex for the identification of layers in a simplified detector geometry of Cylinders and Discs.
Definition LayerIndex.h:37
int value() const
layerIndex expressed in an integer
Definition LayerIndex.h:71
double oppositePostFactor() const
Return method for post update material description of the Layer along normalvector.
double alongPreFactor() const
Return method for pre update material description of the Layer along normalvector.
double alongPostFactor() const
Return method for post update material description of the Layer along normalvector.
double oppositePreFactor() const
Return method for pre update material description of the Layer along normalvector.
Base Class for a Detector Layer in the Tracking realm.
Definition Layer.h:72
virtual const Surface & surfaceRepresentation() const =0
Transforms the layer into a Surface representation for extrapolation.
const MaterialProperties * fullUpdateMaterialProperties(const TrackParameters &par) const
getting the MaterialProperties back - for full update
Definition Layer.cxx:169
const LayerMaterialProperties * layerMaterialProperties() const
getting the LayerMaterialProperties including full/pre/post update
int parameterKey() const
Identifier key for matrix expansion/reduction.
bool contains(ParamDefs par) const
The simple check for the clients whether the parameter is contained.
magnetic field properties to steer the behavior of the extrapolation
base class to integrate material effects on Trk::Track in a flexible way.
const Surface & associatedSurface() const
returns the surface to which these m.eff. are associated.
double thicknessInX0() const
returns the actually traversed material .
virtual MaterialEffectsDerivedType derivedType() const =0
Returns the concrete derived type.
represents the full description of deflection and e-loss of a track in material.
const EnergyLoss * energyLoss() const
returns the energy loss object.
const ScatteringAngles * scatteringAngles() const
returns the MCS-angles object.
Material with information about thickness of material.
float thicknessInX0() const
Return the radiationlength fraction.
float thickness() const
Return the thickness in mm.
This class is the pure abstract base class for all fittable tracking measurements.
virtual MeasurementBase * clone() const =0
Pseudo-Constructor.
const LocalParameters & localParameters() const
Interface method to get the LocalParameters.
virtual const Surface & associatedSurface() const =0
Interface method to get the associated Surface.
virtual bool type(MeasurementBaseType::Type type) const =0
Interface method checking the type.
virtual const Amg::Vector3D & globalPosition() const =0
Interface method to get the global Position.
const Amg::MatrixX & localCovariance() const
Interface method to get the localError.
const Amg::Vector3D & momentum() const
Access method for the momentum.
virtual ParametersBase< DIM, T > * clone() const override=0
clone method for polymorphic deep copy
const Amg::Vector3D & position() const
Access method for the position.
double pT() const
Access method for transverse momentum.
virtual const Surface & associatedSurface() const =0
Access to the Surface associated to the Parameters.
Class describing the Line to which the Perigee refers to.
virtual Surface::ChargedTrackParametersUniquePtr createUniqueTrackParameters(double l1, double l2, double phi, double theta, double qop, const std::optional< AmgSymMatrix(5)> &cov=std::nullopt) const override final
Use the Surface as a ParametersBase constructor, from local parameters - charged.
virtual DistanceSolution straightLineDistanceEstimate(const Amg::Vector3D &pos, const Amg::Vector3D &dir) const override final
fast straight line distance evaluation to Surface
Class for a planaer rectangular or trapezoidal surface in the ATLAS detector.
const Amg::Vector2D & localPosition() const
return the local position reference
virtual bool type(PrepRawDataType type) const
Interface method checking the type.
const Amg::MatrixX & localCovariance() const
return const ref to the error matrix
Class to handle RIO On Tracks ROT) for InDet and Muons, it inherits from the common MeasurementBase.
Definition RIO_OnTrack.h:70
virtual const Surface & associatedSurface() const override=0
returns the surface for the local to global transformation
virtual const Trk::PrepRawData * prepRawData() const =0
returns the PrepRawData (also known as RIO) object to which this RIO_OnTrack is associated.
Identifier identify() const
return the identifier -extends MeasurementBase
virtual const Amg::Vector3D & globalPosition() const override=0
Interface method to get the global Position.
@ Biased
RP with track state including the hit.
represents a deflection of the track caused through multiple scattering in material.
Base class for all TrackSegment implementations, extends the common MeasurementBase.
const MeasurementBase * measurement(unsigned int) const
returns the Trk::MeasurementBase objects depending on the integer
unsigned int numberOfMeasurementBases() const
Return the number of contained Trk::MeasurementBase (s).
Class for a StraightLineSurface in the ATLAS detector to describe dirft tube and straw like detectors...
virtual double r() const =0
Interface method for the maximal extension or the radius.
virtual BoundsType type() const =0
Return the bounds type - for persistency optimization.
Abstract Base Class for tracking surfaces.
Definition Surface.h:79
virtual DistanceSolution straightLineDistanceEstimate(const Amg::Vector3D &pos, const Amg::Vector3D &dir) const =0
fast straight line distance evaluation to Surface
virtual ChargedTrackParametersUniquePtr createUniqueTrackParameters(double l1, double l2, double phi, double theat, double qop, const std::optional< AmgSymMatrix(5)> &cov=std::nullopt) const =0
Use the Surface as a ParametersBase constructor, from local parameters - charged.
virtual const Amg::Vector3D & normal() const
Returns the normal vector of the Surface (i.e.
const Amg::Transform3D & transform() const
Returns HepGeom::Transform3D by reference.
Identifier associatedDetectorElementIdentifier() const
return Identifier of the associated Detector Element
virtual constexpr SurfaceType type() const =0
Returns the Surface type to avoid dynamic casts.
virtual const SurfaceBounds & bounds() const =0
Surface Bounds method.
const Trk::Layer * associatedLayer() const
return the associated Layer
const Amg::Vector3D & center() const
Returns the center position of the Surface.
Contains information about the 'fitter' of this track.
bool trackProperties(const TrackProperties &property) const
Access methods for track properties.
@ GlobalChi2Fitter
Track's from Thijs' global chi^2 fitter.
ParticleHypothesis particleHypothesis() const
Returns the particle hypothesis used for Track fitting.
const TrackFitter & trackFitter() const
Access methods for track fitter.
@ BremFit
A brem fit was performed on this track.
@ BremFitSuccessful
A brem fit was performed on this track and this fit was successful.
represents the track state (measurement, material, fit parameters and quality) at a surface.
const MeasurementBase * measurementOnTrack() const
returns MeasurementBase const overload
const TrackParameters * trackParameters() const
return ptr to trackparameters const overload
bool type(const TrackStateOnSurfaceType type) const
Use this method to find out if the TSoS is of a certain type: i.e.
@ Measurement
This is a measurement, and will at least contain a Trk::MeasurementBase.
@ Perigee
This represents a perigee, and so will contain a Perigee object only.
@ BremPoint
This represents a brem point on the track, and so will contain TrackParameters and MaterialEffectsBas...
@ Outlier
This TSoS contains an outlier, that is, it contains a MeasurementBase/RIO_OnTrack which was not used ...
@ InertMaterial
This represents inert material, and so will contain MaterialEffectsBase.
@ Scatterer
This represents a scattering point on the track, and so will contain TrackParameters and MaterialEffe...
@ CaloDeposit
This TSOS contains a CaloEnergy object.
const MaterialEffectsBase * materialEffectsOnTrack() const
return material effects const overload
const Trk::Surface & surface() const
return associated surface
const Trk::TrackStates * trackStateOnSurfaces() const
return a pointer to a const DataVector of const TrackStateOnSurfaces.
const DataVector< const MeasurementBase > * measurementsOnTrack() const
return a pointer to a vector of MeasurementBase (NOT including any that come from outliers).
const DataVector< const TrackParameters > * trackParameters() const
Return a pointer to a vector of TrackParameters.
const TrackInfo & info() const
Returns a const ref to info of a const tracks.
const Perigee * perigeeParameters() const
return Perigee.
const FitQuality * fitQuality() const
return a pointer to the fit quality const-overload
The TrackingGeometry class is the owner of the constructed TrackingVolumes.
Full Volume description used in Tracking, it inherits from Volume to get the geometrical structure,...
const LayerArray * confinedLayers() const
Return the subLayer array.
LayerIntersection< Amg::Vector3D > closestMaterialLayer(const Amg::Vector3D &gp, const Amg::Vector3D &dir, PropDirection pDir=alongMomentum, const BoundaryCheck &bchk=true) const
Return the closest layer with material description.
const TrackingVolumeArray * confinedVolumes() const
Return the subLayer array.
std::vector< std::shared_ptr< BoundarySurface< TrackingVolume > > > & boundarySurfaces()
Method to return the BoundarySurfaces.
This is the base class for all tracking detector elements with read-out relevant information.
virtual Identifier identify() const =0
Identifier.
TH1F * trans(TH1F *h, bool t=false)
double chi2(TH1 *h0, TH1 *h1)
std::string head(std::string s, const std::string &pattern)
head of a string
int ts
Definition globals.cxx:24
int r
Definition globals.cxx:22
#define ATH_FLATTEN
Eigen::Matrix< double, 3, 3 > RotationMatrix3D
Eigen::Matrix< double, Eigen::Dynamic, Eigen::Dynamic > MatrixX
Dynamic Matrix - dynamic allocation.
Eigen::Matrix< double, Eigen::Dynamic, Eigen::Dynamic > SymMatrixX
Eigen::Affine3d Transform3D
Eigen::Matrix< double, 3, 1 > Vector3D
Eigen::Matrix< double, Eigen::Dynamic, 1 > VectorX
Dynamic Vector - dynamic allocation.
double getDistance(const xAOD::Vertex *vtx1, const xAOD::Vertex *vtx2)
constexpr double mass[PARTICLEHYPOTHESES]
the array of masses
MeasurementType
enum describing the flavour of MeasurementBase
Ensure that the ATLAS eigen extensions are properly loaded.
PropDirection
PropDirection, enum for direction of the propagation.
@ oppositeMomentum
@ alongMomentum
@ anyDirection
std::vector< const MeasurementBase * > MeasurementSet
vector of fittable measurements
Definition FitterTypes.h:30
ParametersT< TrackParametersDim, Charged, PerigeeSurface > Perigee
@ pixelCluster
ParametersT< TrackParametersDim, Charged, StraightLineSurface > AtaStraightLine
@ DeadElement
outside the element
bool consistentSurfaces(U)
std::unique_ptr< T > unique_clone(const T *v)
Definition unique_clone.h:8
bool RunOutlierRemoval
switch to toggle quality processing after fit
Definition FitterTypes.h:22
@ NoField
Field is set to 0., 0., 0.,.
@ FullField
Field is set to be realistic, but within a given Volume.
@ driftRadius
trt, straws
Definition ParamDefs.h:53
@ locY
local cartesian
Definition ParamDefs.h:38
@ x
Definition ParamDefs.h:55
@ locX
Definition ParamDefs.h:37
@ z
global position (cartesian)
Definition ParamDefs.h:57
@ locRPhi
Definition ParamDefs.h:40
@ u
Enums for curvilinear frames.
Definition ParamDefs.h:77
@ phi0
Definition ParamDefs.h:65
@ theta
Definition ParamDefs.h:66
@ qOverP
perigee
Definition ParamDefs.h:67
@ y
Definition ParamDefs.h:56
@ loc2
generic first and second local coordinate
Definition ParamDefs.h:35
@ phi
Definition ParamDefs.h:75
@ loc1
Definition ParamDefs.h:34
@ locZ
local cylindrical
Definition ParamDefs.h:42
@ 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.
@ nonInteractingMuon
std::pair< long int, long int > indices
BinnedArray< TrackingVolume > TrackingVolumeArray
simply for the eye
const Amg::Vector3D & position() const
Method to retrieve the position of the Intersection.
ParametersBase< TrackParametersDim, Charged > TrackParameters
ParametersT< TrackParametersDim, Charged, PlaneSurface > AtaPlane
const IIntersectionCache * cache() const
Retrieve the associated cache block, if it exists.
std::vector< const PrepRawData * > PrepRawDataSet
vector of clusters and drift circles
Definition FitterTypes.h:26
AmgSymMatrix(5) &GXFTrackState
@ numberOfSCTHoles
number of Holes in both sides of a SCT module
@ numberOfPixelHoles
number of pixels which have a ganged ambiguity.
@ numberOfPixelDeadSensors
number of pixel hits with broad errors (width/sqrt(12))
Definition index.py:1
STL namespace.
void sort(typename DataModel_detail::iterator< DVL > beg, typename DataModel_detail::iterator< DVL > end)
Specialization of sort for DataVector/List.
void stable_sort(DataModel_detail::iterator< DVL > beg, DataModel_detail::iterator< DVL > end)
Specialization of stable_sort for DataVector/List.
void reverse(typename DataModel_detail::iterator< DVL > beg, typename DataModel_detail::iterator< DVL > end)
Specialization of reverse for DataVector/List.
double deltaPhi(double phiA, double phiB)
delta Phi in range [-pi,pi[
static void objVectorDeleter(const std::vector< const T * > *ptr)
static constexpr std::array< ParamDefs, 6 > pardef
Constructor.
Definition ParamDefs.h:94
MsgStream & msg
Definition testRead.cxx:32