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