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);
326 initFieldCache(ctx,cache);
327
328 GXFTrajectory trajectory;
329 if (!m_straightlineprop) {
330 trajectory.m_straightline = (
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 (
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 &&
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,
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());
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,
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(AlignmentCache& alignCache,
1820 const Track &inputTrack,
1821 const RunOutlierRemoval runOutlier,
1822 const ParticleHypothesis matEffects) const {
1823
1824
1825 const EventContext& ctx = Gaudi::Hive::currentContext();
1826 Cache cache(this);
1827 initFieldCache(ctx, cache);
1828
1829 alignCache.m_derivMatrix.reset();
1830 alignCache.m_fullCovarianceMatrix.reset();
1831 alignCache.m_iterationsOfLastFit = 0;
1832
1833 Trk::Track* newTrack =
1834 fitIm(ctx, cache, inputTrack, runOutlier, matEffects);
1835 if(newTrack != nullptr){
1836 if(cache.m_derivmat.size() != 0)
1837 alignCache.m_derivMatrix = std::make_unique<Amg::MatrixX>(cache.m_derivmat);
1838 if(cache.m_fullcovmat.size() != 0)
1839 alignCache.m_fullCovarianceMatrix = std::make_unique<Amg::MatrixX>(cache.m_fullcovmat);
1840 alignCache.m_iterationsOfLastFit = cache.m_lastiter;
1841 }
1842 return newTrack;
1843 }
1844
1845 Track*
1847 const EventContext& ctx,
1848 Cache& cache,
1849 const Track& inputTrack,
1850 const RunOutlierRemoval runOutlier,
1851 const ParticleHypothesis matEffects) const
1852 {
1853
1854 ATH_MSG_DEBUG("--> entering GlobalChi2Fitter::fit(Track,,)");
1855
1856 GXFTrajectory trajectory;
1857
1858 if (!m_straightlineprop) {
1859 trajectory.m_straightline = (!cache.m_field_cache.solenoidOn() && !cache.m_field_cache.toroidOn());
1860 }
1861
1862 trajectory.m_fieldprop = trajectory.m_straightline ? Trk::NoField : Trk::FullField;
1863
1864 if (inputTrack.trackStateOnSurfaces()->empty()) {
1865 ATH_MSG_WARNING("Track with zero track states, cannot perform fit");
1866 return nullptr;
1867 }
1868
1869 if (inputTrack.trackParameters()->empty()) {
1870 ATH_MSG_WARNING("Track without track parameters, cannot perform fit");
1871 return nullptr;
1872 }
1873
1874 std::unique_ptr<const TrackParameters> minpar = unique_clone(inputTrack.perigeeParameters());
1875 const TrackParameters *firstidpar = nullptr;
1876 const TrackParameters *lastidpar = nullptr;
1877
1878 if (minpar == nullptr) {
1879 minpar = unique_clone(*(inputTrack.trackParameters()->begin()));
1880 }
1881
1882 const bool tmpgetmat = cache.m_getmaterialfromtrack;
1883
1884 if (
1885 matEffects == Trk::nonInteracting ||
1886 inputTrack.info().trackFitter() == TrackInfo::Unknown
1887 ) {
1888 cache.m_getmaterialfromtrack = false;
1889 }
1890
1892 const Trk::TrackStates::const_iterator endState = inputTrack.trackStateOnSurfaces()->end();
1893
1894 trajectory.trackStates().reserve(inputTrack.trackStateOnSurfaces()->size());
1895
1896 const Surface *firsthitsurf = nullptr;
1897 const Surface *lasthitsurf = nullptr;
1898 bool hasid = false;
1899 bool hasmuon = false;
1900 bool iscombined = false;
1901 bool seenphimeas = false;
1902 bool phiem = false;
1903 bool phibo = false;
1904
1905 for (; itStates != endState; ++itStates) {
1906 const auto *const pMeasurement = (**itStates).measurementOnTrack();
1907 if (
1908 (pMeasurement == nullptr) &&
1909 ((**itStates).materialEffectsOnTrack() != nullptr) &&
1910 matEffects != inputTrack.info().particleHypothesis()
1911 ) {
1912 continue;
1913 }
1914
1915 if (pMeasurement != nullptr) {
1916 const Surface *surf = &pMeasurement->associatedSurface();
1918 if (!hitid.is_valid()) {
1919 if (pMeasurement->type(Trk::MeasurementBaseType::CompetingRIOsOnTrack)) {
1920 const CompetingRIOsOnTrack *crot = static_cast<const CompetingRIOsOnTrack *>(pMeasurement);
1921 hitid = crot->rioOnTrack(0).identify();
1922 }
1923 }
1924 if (hitid.is_valid()) {
1925 if (firsthitsurf == nullptr) {
1926 firsthitsurf = surf;
1927 }
1928 lasthitsurf = surf;
1929 if (m_DetID->is_indet(hitid)) {
1930 hasid = true;
1931 if (hasmuon) {
1932 iscombined = true;
1933 }
1934 if ((**itStates).trackParameters() != nullptr) {
1935 lastidpar = (**itStates).trackParameters();
1936 if (firstidpar == nullptr) {
1937 firstidpar = lastidpar;
1938 }
1939 }
1940 } else {
1941 if (!(**itStates).type(TrackStateOnSurface::Outlier)) {
1942 const Amg::Vector3D measdir = surf->transform().rotation().col(0);
1943 const double dotprod1 = measdir.dot(Amg::Vector3D(0, 0, 1));
1944 const double dotprod2 = measdir.dot(Amg::Vector3D(surf->center().x(), surf->center().y(), 0) / surf->center().perp());
1945 if (std::abs(dotprod1) < .5 && std::abs(dotprod2) < .5) {
1946 seenphimeas = true;
1947 if (std::abs(surf->center().z()) > 13000) {
1948 phiem = true;
1949 }
1950 if (surf->center().perp() > 9000 && std::abs(surf->center().z()) < 13000) {
1951 phibo = true;
1952 }
1953 }
1954 }
1955 hasmuon = true;
1956 if (hasid) {
1957 iscombined = true;
1958 }
1959 }
1960 }
1961
1962 if (iscombined && seenphimeas && (phiem || phibo)) {
1964 continue;
1965 }
1966 }
1967 }
1968 makeProtoState(cache, trajectory, *itStates);
1969 }
1970
1971 if (
1972 cache.m_getmaterialfromtrack && trajectory.numberOfScatterers() != 0 &&
1973 (hasmuon || cache.m_acceleration)
1974 ) {
1975 cache.m_matfilled = true;
1976 }
1977
1978 if (firstidpar == lastidpar) {
1979 firstidpar = lastidpar = nullptr;
1980 }
1981
1982 if (
1983 iscombined &&
1984 !cache.m_matfilled &&
1985 (
1986 m_DetID->is_indet(firsthitsurf->associatedDetectorElementIdentifier()) !=
1987 m_DetID->is_indet(lasthitsurf->associatedDetectorElementIdentifier())
1988 ) &&
1989 (firstidpar != nullptr)
1990 ) {
1991 if (m_DetID->is_indet(firsthitsurf->associatedDetectorElementIdentifier())) {
1992 minpar = unique_clone(lastidpar);
1993 } else {
1994 minpar = unique_clone(firstidpar);
1995 }
1996 }
1997
1998 const bool tmpacc = cache.m_acceleration;
1999 const bool tmpfiteloss = m_fiteloss;
2000 const bool tmpsirecal = cache.m_sirecal;
2001 std::unique_ptr<Track> tmptrack = nullptr;
2002
2003 if (matEffects == Trk::proton || matEffects == Trk::kaon || matEffects == Trk::electron) {
2004 ATH_MSG_DEBUG("call myfit(GXFTrajectory,TP,,)");
2005 cache.m_fiteloss = true;
2006 cache.m_sirecal = false;
2007
2008 if (matEffects == Trk::electron) {
2009 cache.m_asymeloss = true;
2010 }
2011
2012 tmptrack.reset(myfit(ctx, cache, trajectory, *minpar, false, matEffects));
2013 cache.m_sirecal = tmpsirecal;
2014
2015 if (tmptrack == nullptr) {
2016 cache.m_matfilled = false;
2017 cache.m_getmaterialfromtrack = tmpgetmat;
2018 cache.m_acceleration = tmpacc;
2019 cache.m_fiteloss = tmpfiteloss;
2020 return nullptr;
2021 }
2022
2023 int nscats = 0;
2024 bool isbrem = false;
2025 double bremdp = 0;
2026 unsigned int n_brem=0;
2027
2028 for (std::unique_ptr<GXFTrackState> & state : trajectory.trackStates()) {
2029 GXFMaterialEffects *meff = state->materialEffects();
2030
2031 if (meff != nullptr) {
2032 nscats++;
2033
2034 const TrackParameters *layerpars = state->trackParameters();
2035 const MaterialProperties *matprop = meff->materialProperties();
2036
2037 const double p = 1. / std::abs(layerpars->parameters()[Trk::qOverP] - .0005 * meff->delta_p());
2038
2039 std::optional<Amg::Vector2D> locpos(state->associatedSurface().globalToLocal(layerpars->position()));
2040 const Amg::Vector3D layerNormal(state->associatedSurface().normal(*locpos));
2041 double costracksurf = 1.;
2042
2043 costracksurf = std::abs(layerNormal.dot(layerpars->momentum().unit()));
2044
2045 const double oldde = meff->deltaE();
2046
2047 std::unique_ptr<EnergyLoss> eloss;
2048 double sigmascat = 0;
2049
2050 if (matprop != nullptr) {
2051 eloss = std::make_unique<EnergyLoss>(
2052 m_elosstool->energyLoss(*matprop, p, 1. / costracksurf,
2053 Trk::alongMomentum, matEffects));
2054 sigmascat = std::sqrt(m_scattool->sigmaSquare(*matprop, p, 1. / costracksurf, matEffects));
2055
2056 if (eloss != nullptr) {
2057 meff->setDeltaE(eloss->deltaE());
2058 }
2059 } else {
2060 MaterialProperties const tmpprop(1., meff->x0(), 0., 0., 0., 0.);
2061 sigmascat = std::sqrt(m_scattool->sigmaSquare(tmpprop, p, 1. / costracksurf, matEffects));
2062 }
2063
2064 meff->setScatteringSigmas(
2065 sigmascat / std::sin(layerpars->parameters()[Trk::theta]),
2066 sigmascat
2067 );
2068
2069
2070 if (matEffects == electron) {
2071 state->resetStateType(TrackStateOnSurface::Scatterer);
2072 meff->setDeltaE(oldde);
2073 if (!meff->isKink()) {
2074 meff->setSigmaDeltaE(0);
2075 } else {
2076 isbrem = true;
2077 bremdp = meff->delta_p();
2078 }
2079 } else if (eloss != nullptr) {
2080 meff->setSigmaDeltaE(eloss->sigmaDeltaE());
2081 }
2082 if (meff->sigmaDeltaE() > 0) {
2083 ++n_brem;
2084 }
2085 }
2086 }
2087
2088 const AmgVector(5) & refpars = trajectory.referenceParameters()->parameters();
2090 refpars[0], refpars[1], refpars[2], refpars[3], refpars[4], std::nullopt
2091 );
2092
2093 trajectory.reset();
2094 cache.m_matfilled = true;
2095
2096 if (matEffects == Trk::electron) {
2097 if (!isbrem) {
2098 trajectory.brems().clear();
2099 } else {
2100 trajectory.brems().resize(1);
2101 trajectory.brems()[0] = bremdp;
2102 }
2103
2104 cache.m_asymeloss = false;
2105 trajectory.setNumberOfScatterers(nscats);
2106 // @TODO fillResiduals assumes that numberOfBrems == number of states with material effects and sigmaDeltaE() > 0
2107 // not clear whether fillResiduals has to be adjusted for electrons rather than this
2108 trajectory.setNumberOfBrems(n_brem);
2109 }
2110 }
2111
2112 std::unique_ptr<Track> track(myfit(ctx, cache, trajectory, *minpar, runOutlier, matEffects));
2113
2114 bool pseudoupdated = false;
2115
2116 if ((track != nullptr) && hasid && hasmuon) {
2117 for (std::unique_ptr<GXFTrackState> & pseudostate : trajectory.trackStates()) {
2118 if (
2119 (pseudostate == nullptr) ||
2120 pseudostate->measurementType() != TrackState::Pseudo ||
2121 pseudostate->fitQuality().chiSquared() < 10
2122 ) {
2123 continue;
2124 }
2125
2126 const TrackParameters *pseudopar = pseudostate->trackParameters();
2127 const std::unique_ptr<const TrackParameters> updpar(m_updator->removeFromState(
2128 *pseudopar,
2129 pseudostate->measurement()->localParameters(),
2130 pseudostate->measurement()->localCovariance()
2131 ));
2132
2133 if (updpar == nullptr) {
2134 continue;
2135 }
2136
2137 Amg::MatrixX covMatrix(1, 1);
2138 covMatrix(0, 0) = 100;
2139
2140 std::unique_ptr<const PseudoMeasurementOnTrack> newpseudo = std::make_unique<const PseudoMeasurementOnTrack>(
2141 LocalParameters(DefinedParameter(updpar->parameters()[Trk::locY], Trk::locY)),
2142 std::move(covMatrix),
2143 pseudopar->associatedSurface()
2144 );
2145
2146 pseudostate->setMeasurement(std::move(newpseudo));
2147 double errors[5];
2148 errors[0] = errors[2] = errors[3] = errors[4] = -1;
2149 errors[1] = 10;
2150 pseudostate->setMeasurementErrors(errors);
2151 pseudoupdated = true;
2152 }
2153
2154 if (pseudoupdated) {
2155 trajectory.setConverged(false);
2156 cache.m_matfilled = true;
2157 track.reset(myfit(ctx, cache, trajectory, *track->perigeeParameters(), false, muon));
2158 cache.m_matfilled = false;
2159 }
2160 }
2161
2162 cache.m_matfilled = false;
2163 cache.m_getmaterialfromtrack = tmpgetmat;
2164 cache.m_acceleration = tmpacc;
2165 cache.m_fiteloss = tmpfiteloss;
2166
2167 if (track != nullptr) {
2169 const TrackInfo& old_info = inputTrack.info();
2170 track->info().addPatternReco(old_info);
2171 }
2172
2173 return track.release();
2174 }
2175
2176 std::unique_ptr<Track>
2178 const EventContext& ctx,
2179 const PrepRawDataSet& prds,
2180 const TrackParameters& param,
2181 const RunOutlierRemoval runOutlier,
2182 const ParticleHypothesis matEffects) const
2183 {
2184 ATH_MSG_DEBUG("--> entering GlobalChi2Fitter::fit(PRDS,TP,)");
2185 MeasurementSet rots;
2186
2187 for (const auto *prd : prds) {
2188 const Surface & prdsurf = (*prd).detectorElement()->surface((*prd).identify());
2189 const RIO_OnTrack *rot = nullptr;
2190 const PlaneSurface *plsurf = nullptr;
2191
2192 if (prdsurf.type() == Trk::SurfaceType::Plane)
2193 plsurf = static_cast < const PlaneSurface *>(&prdsurf);
2194
2195 const StraightLineSurface *slsurf = nullptr;
2196
2197 if (prdsurf.type() == Trk::SurfaceType::Line)
2198 slsurf = static_cast < const StraightLineSurface *>(&prdsurf);
2199
2200 if ((slsurf == nullptr) && (plsurf == nullptr)) {
2201 ATH_MSG_ERROR("Surface is neither PlaneSurface nor StraightLineSurface!");
2202 }
2203
2204 if (!m_broadROTcreator.empty() && (slsurf != nullptr)) {
2205 rot = m_broadROTcreator->correct(*prd, param, ctx);
2206 } else if (slsurf != nullptr) {
2207 AtaStraightLine const atasl(
2208 slsurf->center(),
2209 param.parameters()[Trk::phi],
2210 param.parameters()[Trk::theta],
2211 param.parameters()[Trk::qOverP],
2212 *slsurf
2213 );
2214 rot = m_ROTcreator->correct(*prd, atasl, ctx);
2215 } else if (plsurf != nullptr) {
2216 if (param.covariance() != nullptr) {
2217 AtaPlane const atapl(
2218 plsurf->center(),
2219 param.parameters()[Trk::phi],
2220 param.parameters()[Trk::theta],
2221 param.parameters()[Trk::qOverP],
2222 *plsurf,
2223 AmgSymMatrix(5)(*param.covariance())
2224 );
2225 rot = m_ROTcreator->correct(*prd, atapl, ctx);
2226 } else {
2227 AtaPlane const atapl(
2228 plsurf->center(),
2229 param.parameters()[Trk::phi],
2230 param.parameters()[Trk::theta],
2231 param.parameters()[Trk::qOverP],
2232 *plsurf
2233 );
2234 rot = m_ROTcreator->correct(*prd, atapl, ctx);
2235 }
2236 }
2237
2238 if (rot != nullptr) {
2239 rots.push_back(rot);
2240 }
2241 }
2242
2243 std::unique_ptr<Track> track =
2244 fit(ctx, rots, param, runOutlier, matEffects);
2245
2246 for (const auto *rot : rots) {
2247 delete rot;
2248 }
2249
2250 return track;
2251 }
2252
2253 std::unique_ptr<Track>
2255 const EventContext& ctx,
2256 const Track& inputTrack,
2257 const MeasurementSet& addMeasColl,
2258 const RunOutlierRemoval runOutlier,
2259 const ParticleHypothesis matEffects) const
2260 {
2261 ATH_MSG_DEBUG("--> entering GlobalChi2Fitter::fit(Track,Meas'BaseSet,,)");
2262
2263 Cache cache(this);
2264 initFieldCache(ctx,cache);
2265
2266 GXFTrajectory trajectory;
2267
2268 if (!m_straightlineprop) {
2269 trajectory.m_straightline = (!cache.m_field_cache.solenoidOn() && !cache.m_field_cache.toroidOn());
2270 }
2271
2272 trajectory.m_fieldprop = trajectory.m_straightline ? Trk::NoField : Trk::FullField;
2273
2274 const TrackParameters *minpar = inputTrack.perigeeParameters();
2275
2276 if (minpar == nullptr) {
2277 minpar = *(inputTrack.trackParameters()->begin());
2278 }
2279
2280 MeasurementSet const hitColl;
2281
2282 // collect MBs from Track (speed: assume this method is used for extending track at end)
2284 const Trk::TrackStates::const_iterator endState = inputTrack.trackStateOnSurfaces()->end();
2285
2286 const bool old_reintoutl = cache.m_reintoutl;
2287 cache.m_reintoutl = false;
2288 const bool tmpasymeloss = cache.m_asymeloss;
2289
2290 if (matEffects == electron) {
2291 cache.m_asymeloss = true;
2292 }
2293
2294 for (; itStates != endState; ++itStates) {
2295 makeProtoState(cache, trajectory, *itStates);
2296 if (
2297 matEffects == electron &&
2298 !trajectory.trackStates().empty() &&
2299 (trajectory.trackStates().back()->materialEffects() != nullptr) &&
2300 trajectory.trackStates().back()->materialEffects()->sigmaDeltaE() > 50.001
2301 ) {
2302 trajectory.trackStates().back()->materialEffects()->setKink(true);
2303 }
2304 }
2305
2306 cache.m_reintoutl = old_reintoutl;
2307
2308 for (const auto & measBase : addMeasColl) {
2309 if (measBase == nullptr) {
2310 ATH_MSG_WARNING("There is an empty MeasurementBase object in the track! Skip this object..");
2311 continue;
2312 }
2313
2314 makeProtoStateFromMeasurement(cache, trajectory, measBase);
2315 }
2316
2317 // fit set of MeasurementBase using main method, start with first TrkParameter in inputTrack
2318 std::unique_ptr<Track> track(myfit(ctx, cache, trajectory, *minpar, runOutlier, matEffects));
2319 cache.m_asymeloss = tmpasymeloss;
2320
2321 if (track != nullptr) {
2322 const double oldqual =
2323 inputTrack.fitQuality()->numberDoF() != 0 ?
2324 inputTrack.fitQuality()->chiSquared() / inputTrack.fitQuality()->numberDoF() :
2325 -999;
2326
2327 const double newqual =
2328 track->fitQuality()->numberDoF() != 0 ?
2329 track->fitQuality()->chiSquared() / track->fitQuality()->numberDoF() :
2330 -999;
2331
2332 if (m_extensioncuts && runOutlier && newqual > 2 && newqual > 2 * oldqual) {
2333 ATH_MSG_DEBUG("Extension rejected");
2334
2336 track.reset(nullptr);
2337 }
2338 }
2339
2340 if (track != nullptr) {
2342 }
2343
2344 return track;
2345 }
2346
2347 // extend a track fit to include an additional set of PrepRawData objects
2348 // --------------------------------
2349 std::unique_ptr<Track>
2351 const EventContext& ctx,
2352 const Track& intrk,
2353 const PrepRawDataSet& prds,
2354 const RunOutlierRemoval runOutlier,
2355 const ParticleHypothesis matEffects) const
2356 {
2357 ATH_MSG_DEBUG("--> entering GlobalChi2Fitter::fit(Track,PRDS,)");
2358 MeasurementSet rots;
2359 const TrackParameters *hitparam = intrk.trackParameters()->back();
2360
2361 for (const auto *prd : prds) {
2362 const Surface & prdsurf = (*prd).detectorElement()->surface((*prd).identify());
2363
2364 Amg::VectorX parameterVector = hitparam->parameters();
2365 std::unique_ptr<const TrackParameters>const trackparForCorrect(
2367 parameterVector[Trk::loc1],
2368 parameterVector[Trk::loc2],
2369 parameterVector[Trk::phi],
2370 parameterVector[Trk::theta],
2371 parameterVector[Trk::qOverP],
2372 AmgSymMatrix(5)(*hitparam->covariance())
2373 )
2374 );
2375
2376 const RIO_OnTrack *rot = nullptr;
2377
2378 if (!m_broadROTcreator.empty() && prdsurf.type() == Trk::SurfaceType::Line) {
2379 rot = m_broadROTcreator->correct(*prd, *hitparam, ctx);
2380 } else {
2381 rot = m_ROTcreator->correct(*prd, *trackparForCorrect, ctx);
2382 }
2383
2384 if (rot != nullptr) {
2385 rots.push_back(rot);
2386 }
2387 }
2388
2389 std::unique_ptr<Track> track = fit(ctx,intrk, rots, runOutlier, matEffects);
2390
2391 for (const auto *rot : rots) {
2392 delete rot;
2393 }
2394
2395 return track;
2396 }
2397
2398 std::unique_ptr<Track> GlobalChi2Fitter::fit(
2399 const EventContext& ctx,
2400 const MeasurementSet & rots,
2401 const TrackParameters & param,
2402 const RunOutlierRemoval runOutlier,
2403 const ParticleHypothesis matEffects
2404 ) const {
2405 ATH_MSG_DEBUG("--> entering GlobalChi2Fitter::fit(Meas'BaseSet,,)");
2406
2407 Cache cache(this);
2408 initFieldCache(ctx,cache);
2409
2410 GXFTrajectory trajectory;
2411
2412 if (!m_straightlineprop) {
2413 trajectory.m_straightline = (!cache.m_field_cache.solenoidOn() && !cache.m_field_cache.toroidOn());
2414 }
2415
2416 trajectory.m_fieldprop = trajectory.m_straightline ? Trk::NoField : Trk::FullField;
2417
2418 for (const auto *itSet : rots) {
2419 if (itSet == nullptr) {
2420 ATH_MSG_WARNING("There is an empty MeasurementBase object in the track! Skip this object..");
2421 } else {
2422 makeProtoStateFromMeasurement(cache, trajectory, itSet);
2423 }
2424 }
2425
2426 std::unique_ptr<const TrackParameters> startpar(param.clone());
2427
2428 if (
2429 matEffects == muon &&
2430 trajectory.numberOfSiliconHits() + trajectory.numberOfTRTHits() == 0
2431 ) {
2432 cache.m_matfilled = true;
2433 trajectory.setPrefit(2);
2434
2435 myfit(ctx,cache, trajectory, *startpar, runOutlier, matEffects);
2436
2437 cache.m_matfilled = false;
2438
2439 if (!trajectory.converged()) {
2440 return nullptr;
2441 }
2442
2443 trajectory.setConverged(false);
2444 const TrackParameters *firstpar = trajectory.trackStates()[0]->trackParameters();
2445 const TrackParameters *lastpar = trajectory.trackStates().back()->trackParameters();
2446
2447 PerigeeSurface const persurf(firstpar->position() - 10 * firstpar->momentum().unit());
2448
2449 if (trajectory.trackStates().front()->measurementType() == TrackState::Pseudo) {
2450 Amg::MatrixX covMatrix(1, 1);
2451 covMatrix(0, 0) = 100;
2452
2453 std::unique_ptr<const PseudoMeasurementOnTrack> newpseudo = std::make_unique<const PseudoMeasurementOnTrack>(
2454 LocalParameters(DefinedParameter(firstpar->parameters()[Trk::locY], Trk::locY)),
2455 std::move(covMatrix),
2456 firstpar->associatedSurface()
2457 );
2458
2459 trajectory.trackStates().front()->setMeasurement(std::move(newpseudo));
2460 }
2461
2462 if (trajectory.trackStates().back()->measurementType() == TrackState::Pseudo) {
2463 Amg::MatrixX covMatrix(1, 1);
2464 covMatrix(0, 0) = 100;
2465
2466 std::unique_ptr<const PseudoMeasurementOnTrack> newpseudo = std::make_unique<const PseudoMeasurementOnTrack>(
2467 LocalParameters(DefinedParameter(lastpar->parameters()[Trk::locY], Trk::locY)),
2468 std::move(covMatrix),
2469 lastpar->associatedSurface()
2470 );
2471
2472 trajectory.trackStates().back()->setMeasurement(std::move(newpseudo));
2473 }
2474
2475 if (!trajectory.m_straightline) {
2476 trajectory.setPrefit(3);
2477 const AmgVector(5) & refpars = trajectory.referenceParameters()->parameters();
2479 refpars[0], refpars[1], refpars[2], refpars[3], refpars[4], std::nullopt
2480 );
2481
2482 trajectory.reset();
2483
2484 myfit(ctx,cache, trajectory, *startpar, runOutlier, matEffects);
2485
2486 cache.m_matfilled = true;
2487
2488 if (!trajectory.converged()) {
2489 return nullptr;
2490 }
2491 }
2492
2493 const AmgVector(5) & refpars = trajectory.referenceParameters()->parameters();
2495 refpars[0], refpars[1], refpars[2], refpars[3], refpars[4], std::nullopt
2496 );
2497
2498 trajectory.reset();
2499 trajectory.setPrefit(0);
2500
2501 if (trajectory.trackStates().front()->measurementType() == TrackState::Pseudo) {
2502 firstpar = trajectory.trackStates().front()->trackParameters();
2503
2504 Amg::MatrixX covMatrix(1, 1);
2505 covMatrix(0, 0) = 100;
2506
2507 std::unique_ptr<const PseudoMeasurementOnTrack> newpseudo = std::make_unique<const PseudoMeasurementOnTrack>(
2508 LocalParameters(DefinedParameter(firstpar->parameters()[Trk::locY], Trk::locY)),
2509 std::move(covMatrix),
2510 firstpar->associatedSurface()
2511 );
2512
2513 trajectory.trackStates().front()->setMeasurement(std::move(newpseudo));
2514 double errors[5];
2515 errors[0] = errors[2] = errors[3] = errors[4] = -1;
2516 errors[1] = 10;
2517 trajectory.trackStates().front()->setMeasurementErrors(errors);
2518 }
2519
2520 if (trajectory.trackStates().back()->measurementType() == TrackState::Pseudo) {
2521 lastpar = trajectory.trackStates().back()->trackParameters();
2522
2523 Amg::MatrixX covMatrix(1, 1);
2524 covMatrix(0, 0) = 100;
2525
2526 std::unique_ptr<const PseudoMeasurementOnTrack> newpseudo = std::make_unique<const PseudoMeasurementOnTrack>(
2527 LocalParameters(DefinedParameter(lastpar->parameters()[Trk::locY], Trk::locY)),
2528 std::move(covMatrix),
2529 lastpar->associatedSurface()
2530 );
2531
2532 trajectory.trackStates().back()->setMeasurement(std::move(newpseudo));
2533 double errors[5];
2534 errors[0] = errors[2] = errors[3] = errors[4] = -1;
2535 errors[1] = 10;
2536 trajectory.trackStates().back()->setMeasurementErrors(errors);
2537 }
2538 }
2539
2540 std::unique_ptr<Track> track;
2541
2542 if (startpar != nullptr) {
2543 track.reset(myfit(ctx,cache, trajectory, *startpar, runOutlier, matEffects));
2544 }
2545
2546 if (track != nullptr) {
2548 }
2549 cache.m_matfilled = false;
2550
2551 return track;
2552 }
2553
2555 Cache & cache,
2556 GXFTrajectory & trajectory,
2557 const TrackStateOnSurface * tsos,
2558 int index
2559 ) const {
2560 if (
2561 (
2566 ) && cache.m_getmaterialfromtrack
2567 ) {
2568 if (cache.m_acceleration && trajectory.numberOfHits() == 0) {
2569 return;
2570 }
2572 return;
2573 }
2574 const MaterialEffectsOnTrack *meff = static_cast<const MaterialEffectsOnTrack *>(tsos->materialEffectsOnTrack());
2575
2576 std::unique_ptr<GXFMaterialEffects> newmeff;
2577
2578 if (
2579 meff->scatteringAngles() or
2580 meff->energyLoss() or
2582 (tsos->trackParameters() == nullptr)
2583 ) {
2584 newmeff = std::make_unique<GXFMaterialEffects>(*meff);
2585 } else {
2586 Trk::MaterialProperties const matprop(meff->thicknessInX0(), 1., 0., 0., 0., 0.);
2587
2588 const double sigmascat = std::sqrt(m_scattool->sigmaSquare(
2589 matprop,
2590 std::abs(1. / tsos->trackParameters()->parameters()[Trk::qOverP]),
2591 1.,
2592 Trk::muon)
2593 );
2594
2595 auto newsa = Trk::ScatteringAngles(
2596 0,
2597 0,
2598 sigmascat / std::sin(tsos->trackParameters()->parameters()[Trk::theta]),
2599 sigmascat
2600 );
2601
2602 Trk::MaterialEffectsOnTrack const newmeot(
2603 meff->thicknessInX0(),
2604 newsa,
2605 nullptr,
2606 tsos->surface()
2607 );
2608
2609 newmeff = std::make_unique<GXFMaterialEffects>(newmeot);
2610 }
2611
2612 if (
2613 (meff->energyLoss() != nullptr) &&
2614 meff->energyLoss()->sigmaDeltaE() > 0 &&
2615 (
2616 (tsos->type(TrackStateOnSurface::BremPoint) && (meff->scatteringAngles() != nullptr)) ||
2617 ((meff->scatteringAngles() == nullptr) || meff->thicknessInX0() == 0)
2618 )
2619 ) {
2620 newmeff->setSigmaDeltaE(meff->energyLoss()->sigmaDeltaE());
2621
2622 if (
2623 (tsos->trackParameters() != nullptr) &&
2624 !trajectory.trackStates().empty() &&
2625 ((**trajectory.trackStates().rbegin()).trackParameters() != nullptr)
2626 ) {
2627 const double delta_p = 1000 * (
2628 tsos->trackParameters()->parameters()[Trk::qOverP] -
2629 (**trajectory.trackStates().rbegin()).trackParameters()->
2630 parameters()[Trk::qOverP]
2631 );
2632
2633 newmeff->setdelta_p(delta_p);
2634 }
2635 }
2636
2637 trajectory.addMaterialState(std::make_unique<GXFTrackState>(std::move(newmeff), unique_clone(tsos->trackParameters())), index);
2638 }
2639
2640 if (
2643 ) {
2644 const bool isoutlier = tsos->type(TrackStateOnSurface::Outlier) && !cache.m_reintoutl;
2645
2647 cache,
2648 trajectory,
2649 tsos->measurementOnTrack(),
2650 tsos->trackParameters(),
2651 isoutlier,
2652 index
2653 );
2654 }
2655 }
2656
2658 Cache & cache,
2659 GXFTrajectory & trajectory,
2660 const MeasurementBase * measbase,
2661 const TrackParameters * trackpar,
2662 bool isoutlier,
2663 int index
2664 ) const {
2665 const Segment *seg = nullptr;
2666
2669 seg = static_cast<const Segment *>(measbase);
2670 }
2671 }
2672
2673 int imax = 1;
2674
2675 if ((seg != nullptr) && m_decomposesegments) {
2676 imax = (int) seg->numberOfMeasurementBases();
2677 }
2678
2679 for (int i = 0; i < imax; i++) {
2680 const MeasurementBase *measbase2 = ((seg != nullptr) && m_decomposesegments) ? seg->measurement(i) : measbase;
2681 const TrackParameters *newtrackpar = ((seg != nullptr) && m_decomposesegments) ? nullptr : trackpar;
2682 std::unique_ptr<GXFTrackState> ptsos = std::make_unique<GXFTrackState>(
2683 std::unique_ptr<const MeasurementBase>(measbase2->clone()),
2684 std::unique_ptr<const TrackParameters>(newtrackpar != nullptr ? newtrackpar->clone() : nullptr)
2685 );
2686 const Amg::MatrixX & covmat = measbase2->localCovariance();
2687 double sinstereo = 0;
2688 double errors[5];
2689 errors[0] = errors[1] = errors[2] = errors[3] = errors[4] = -1;
2692 //const CompetingRIOsOnTrack *crot = nullptr;
2693 if (!hitid.is_valid()) {
2695 const CompetingRIOsOnTrack *crot = static_cast<const CompetingRIOsOnTrack *>(measbase2);
2696 hitid = crot->rioOnTrack(0).identify();
2697 }
2698 }
2699
2700 bool measphi = false;
2701
2702 if (hitid.is_valid() && measbase2->localParameters().contains(Trk::locX)) {
2703 bool rotated = false;
2704
2705 if (m_DetID->is_indet(hitid) && !m_DetID->is_muon(hitid)) {
2706 if (m_DetID->is_pixel(hitid)) {
2707 hittype = TrackState::Pixel;
2708 } else if (m_DetID->is_sct(hitid)) {
2709 if (covmat.cols() != 1 && covmat(1, 0) != 0) {
2710 rotated = true;
2711 }
2712 hittype = TrackState::SCT;
2713 } else if (m_DetID->is_trt(hitid)) {
2714 hittype = TrackState::TRT;
2715 }
2716 } else { // Muon hit
2717 if (m_DetID->is_rpc(hitid)) {
2718 hittype = TrackState::RPC;
2719 if (measbase->localParameters().parameterKey() != 1) {
2720 ATH_MSG_WARNING("Corrupt RPC hit, skipping it");
2721 continue;
2722 }
2723 } else if (m_DetID->is_mdt(hitid)) {
2724 hittype = TrackState::MDT;
2725 } else if (m_DetID->is_tgc(hitid)) {
2727 rotated = true;
2728 }
2729 hittype = TrackState::TGC;
2730 } else if (m_DetID->is_csc(hitid)) {
2731 hittype = TrackState::CSC;
2732 } else if (m_DetID->is_mm(hitid)) {
2733 hittype = TrackState::MM;
2734 } else if (m_DetID->is_stgc(hitid)) {
2735 hittype = TrackState::STGC;
2736 }
2737 }
2738
2739 if (rotated) {
2740 const auto [covEigenValueSmall, covStereoAngle] = principalComponentAnalysis2x2(covmat);
2741 errors[0] = std::sqrt(covEigenValueSmall);
2742 sinstereo = std::sin(covStereoAngle);
2743 } else {
2744 errors[0] = std::sqrt(covmat(0, 0));
2745 if (hittype == TrackState::Pixel) {
2746 errors[1] = std::sqrt(covmat(1, 1));
2747 }
2748 }
2749 if (
2750 hittype == TrackState::RPC ||
2751 hittype == TrackState::TGC ||
2752 hittype == TrackState::CSC ||
2753 hittype == TrackState::STGC
2754 ) {
2755 const Surface *surf = &measbase2->associatedSurface();
2756 const Amg::Vector3D measdir = surf->transform().rotation().col(0);
2757 const double dotprod1 = measdir.dot(Amg::Vector3D(0, 0, 1));
2758 const double dotprod2 = measdir.dot(Amg::Vector3D(surf->center().x(), surf->center().y(), 0) / surf->center().perp());
2759 if (std::abs(dotprod1) < .5 && std::abs(dotprod2) < .5) {
2760 measphi = true;
2761 }
2762 }
2763 } else {
2764 const Trk::LocalParameters & psmpar = measbase2->localParameters();
2765 // @TODO coverity complains about index shadowing the method argument index
2766 // this is solved by renaming index in this block by param_index
2767 int param_index = 0;
2768 if (psmpar.contains(Trk::locRPhi)) {
2769 errors[0] = std::sqrt(covmat(0, 0));
2770 param_index++;
2771 }
2772
2773 if (psmpar.contains(Trk::locZ)) {
2774 errors[1] = std::sqrt(covmat(param_index, param_index));
2775 param_index++;
2776 }
2777
2778 if (psmpar.contains(Trk::phi)) {
2779 errors[2] = std::sqrt(covmat(param_index, param_index));
2780 param_index++;
2781 }
2782
2783 if (psmpar.contains(Trk::theta)) {
2784 errors[3] = std::sqrt(covmat(param_index, param_index));
2785 param_index++;
2786 }
2787
2788 if (psmpar.contains(Trk::qOverP)) {
2789 errors[4] = std::sqrt(covmat(param_index, param_index));
2790 param_index++;
2791 }
2793 hittype = TrackState::Pseudo;
2794 ATH_MSG_DEBUG("PseudoMeasurement, pos=" << measbase2->globalPosition());
2795 } else if (measbase2->type(Trk::MeasurementBaseType::VertexOnTrack )) {
2796 hittype = TrackState::Vertex;
2797 ATH_MSG_DEBUG("VertexOnTrack, pos=" << measbase2->globalPosition()); // print out the hit type
2798 } else if (measbase2->type(Trk::MeasurementBaseType::Segment )) {
2799 hittype = TrackState::Segment;
2800 ATH_MSG_DEBUG("Segment, pos=" << measbase2->globalPosition()); // print out the hit type
2801 }
2802 }
2803 if (
2804 errors[0] > 0 ||
2805 errors[1] > 0 ||
2806 errors[2] > 0 ||
2807 errors[3] > 0 ||
2808 errors[4] > 0
2809 ) {
2810 ptsos->setMeasurementErrors(errors);
2811 ptsos->setSinStereo(sinstereo);
2812 ptsos->setMeasurementType(hittype);
2813 ptsos->setMeasuresPhi(measphi);
2814
2815 if (isoutlier && !cache.m_reintoutl) {
2816 ptsos->resetStateType(TrackStateOnSurface::Outlier);
2817 }
2818
2819 // @TODO here index really is supposed to refer to the method argument index ?
2820 const bool ok = trajectory.addMeasurementState(std::move(ptsos), index);
2821 if (!ok) {
2822 ATH_MSG_WARNING("Duplicate hit on track");
2823 }
2824 } else {
2825 ATH_MSG_WARNING("Measurement error is zero or negative, drop hit");
2826 }
2827 }
2828 }
2829
2831 Cache & cache,
2832 const Trk::TrackingVolume *tvol
2833 ) const {
2834 if (tvol == nullptr) {
2835 return false;
2836 }
2837
2838 const Trk::BinnedArray < Trk::Layer > *confinedLayers = tvol->confinedLayers();
2839
2840 // loop over confined layers
2841 if (confinedLayers != nullptr) {
2842 // loop over layers
2843 for (const auto & layer : confinedLayers->arrayObjects()) {
2844 // push_back the layer
2845 if (layer != nullptr) {
2846 // get the layerIndex
2847 const Trk::LayerIndex & layIndex = layer->layerIndex();
2848 // skip navigaion layers for the moment
2849
2850 if ((layIndex.value() == 0) || (layer->layerMaterialProperties() == nullptr)) {
2851 continue;
2852 }
2853
2854 const CylinderLayer *cyllay = nullptr;
2855 if (layer->surfaceRepresentation().type() == Trk::SurfaceType::Cylinder) {
2856 cyllay = static_cast<const CylinderLayer *>(layer);
2857 }
2858
2859 const DiscLayer *disclay = nullptr;
2860 if (layer->surfaceRepresentation().type() == Trk::SurfaceType::Disc) {
2861 disclay = static_cast<const DiscLayer *>(layer);
2862 }
2863
2864 if (disclay != nullptr) {
2865 if (disclay->center().z() < 0) {
2866 cache.m_negdiscs.push_back(disclay);
2867 } else {
2868 cache.m_posdiscs.push_back(disclay);
2869 }
2870 } else if (cyllay != nullptr) {
2871 cache.m_barrelcylinders.push_back(cyllay);
2872 } else {
2873 return false;
2874 }
2875 }
2876 }
2877 }
2878
2879 const auto & bsurf = tvol->boundarySurfaces();
2880
2881 for (size_t ib = 0 ; ib < bsurf.size(); ++ib) {
2882 const Layer *layer = bsurf[ib]->surfaceRepresentation().materialLayer();
2883
2884 if (layer == nullptr) continue;
2885
2886 const Trk::LayerIndex & layIndex = layer->layerIndex();
2887
2888 if ((layIndex.value() == 0) || (layer->layerMaterialProperties() == nullptr)) {
2889 continue;
2890 }
2891
2892 const CylinderSurface *cylsurf = nullptr;
2893
2894 if (layer->surfaceRepresentation().type() == Trk::SurfaceType::Cylinder)
2895 cylsurf = static_cast<const CylinderSurface *>(&layer->surfaceRepresentation());
2896
2897 const DiscSurface *discsurf = nullptr;
2898
2899 if (layer->surfaceRepresentation().type() == Trk::SurfaceType::Disc)
2900 discsurf = static_cast<const DiscSurface *>(&layer->surfaceRepresentation());
2901
2902 if (discsurf != nullptr) {
2903 if (
2904 discsurf->center().z() < 0 &&
2905 std::find(cache.m_negdiscs.begin(), cache.m_negdiscs.end(), layer) == cache.m_negdiscs.end()
2906 ) {
2907 cache.m_negdiscs.push_back(layer);
2908 } else if (
2909 discsurf->center().z() > 0 &&
2910 std::find(cache.m_posdiscs.begin(), cache.m_posdiscs.end(), layer) == cache.m_posdiscs.end()
2911 ) {
2912 cache.m_posdiscs.push_back(layer);
2913 }
2914 } else if (
2915 (cylsurf != nullptr) &&
2916 std::find(cache.m_barrelcylinders.begin(), cache.m_barrelcylinders.end(), layer) == cache.m_barrelcylinders.end()
2917 ) {
2918 cache.m_barrelcylinders.push_back(layer);
2919 }
2920
2921 if ((cylsurf == nullptr) && (discsurf == nullptr)) {
2922 return false;
2923 }
2924 }
2925
2926 const TrackingVolumeArray* confinedVolumes = tvol->confinedVolumes();
2927 // get the confined volumes and loop over it -> call recursively
2928 if (confinedVolumes != nullptr) {
2929 for (const auto & volume : confinedVolumes->arrayObjects()) {
2930 if (volume != nullptr) {
2931 const bool ok = processTrkVolume(cache, volume);
2932 if (!ok) {
2933 return false;
2934 }
2935 }
2936 }
2937 }
2938
2939 return true;
2940 }
2941
2942
2943
2944
2945
2946 std::optional<std::pair<Amg::Vector3D, double>>
2948 Cache & cache,
2949 const DiscSurface &surf,
2950 const TrackParameters &parforextrap,
2951 const TrackParameters &refpar2,
2952 const ParticleHypothesis matEffects
2953 ) {
2954 /*
2955 * Please refer to external sources on how to find the intersection between
2956 * a line and a disc.
2957 */
2958 double field[3];
2959 const double * pos = parforextrap.position().data();
2960 const double currentqoverp = (matEffects != Trk::electron) ? parforextrap.parameters()[Trk::qOverP] : refpar2.parameters()[Trk::qOverP];
2961 cache.m_field_cache.getFieldZR(pos, field);
2962 const double sinphi = std::sin(parforextrap.parameters()[Trk::phi0]);
2963 const double cosphi = std::cos(parforextrap.parameters()[Trk::phi0]);
2964 const double sintheta = std::sin(parforextrap.parameters()[Trk::theta]);
2965 const double costheta = std::cos(parforextrap.parameters()[Trk::theta]);
2966 //magnetic field deviation from straight line
2967 //https://cds.cern.ch/record/1281363/files/ATLAS-CONF-2010-072.pdf, equation 1
2968 //converted to MeV and kT
2969 const double r = (std::abs(currentqoverp) > 1e-10) ? -sintheta / (currentqoverp * 300. * field[2]) : 1e6;
2970 const double xc = parforextrap.position().x() - r * sinphi;
2971 const double yc = parforextrap.position().y() + r * cosphi;
2972 const double phi0 = std::atan2(parforextrap.position().y() - yc, parforextrap.position().x() - xc);
2973 const double z0 = parforextrap.position().z();
2974 const double delta_s = (surf.center().z() - z0) / costheta;
2975 const double delta_phi = delta_s * sintheta / r;
2976 const double x = xc + std::abs(r) * std::cos(phi0 + delta_phi);
2977 const double y = yc + std::abs(r) * std::sin(phi0 + delta_phi);
2978 const Amg::Vector3D intersect = Amg::Vector3D(x, y, surf.center().z());
2979 const double perp = intersect.perp();
2980 const DiscBounds *discbounds = static_cast<const DiscBounds *> (&surf.bounds());
2981
2982 if (perp > discbounds->rMax() || perp < discbounds->rMin()) {
2983 return {};
2984 }
2985
2986 const double costracksurf = std::abs(costheta);
2987
2988 return std::make_pair(intersect, costracksurf);
2989 }
2990
2991 std::optional<std::pair<Amg::Vector3D, double>>
2993 Cache & cache,
2994 const CylinderSurface &surf,
2995 const TrackParameters &parforextrap,
2996 const TrackParameters &refpar2,
2997 const ParticleHypothesis matEffects
2998 ) {
2999 /*
3000 * I hope you like trigonometry!
3001 *
3002 * For more information, please find a source elsewhere on finding
3003 * intersections with cylinders.
3004 */
3005 double field[3];
3006 const double * pos = parforextrap.position().data();
3007 const double currentqoverp = (matEffects != Trk::electron) ? parforextrap.parameters()[Trk::qOverP] : refpar2.parameters()[Trk::qOverP];
3008 cache.m_field_cache.getFieldZR(pos, field);
3009 const double sinphi = std::sin(parforextrap.parameters()[Trk::phi0]);
3010 const double cosphi = std::cos(parforextrap.parameters()[Trk::phi0]);
3011 const double sintheta = std::sin(parforextrap.parameters()[Trk::theta]);
3012 const double costheta = std::cos(parforextrap.parameters()[Trk::theta]);
3013 const double tantheta = std::tan(parforextrap.parameters()[Trk::theta]);
3014 const double r = (std::abs(currentqoverp) > 1e-10) ? -sintheta / (currentqoverp * 300. * field[2]) : 1e6;
3015 const double xc = parforextrap.position().x() - r * sinphi;
3016 const double yc = parforextrap.position().y() + r * cosphi;
3017 const double phi0 = std::atan2(parforextrap.position().y() - yc, parforextrap.position().x() - xc);
3018 const double z0 = parforextrap.position().z();
3019 const double d = xc * xc + yc * yc;
3020 const double rcyl = surf.bounds().r();
3021 double mysqrt = ((r + rcyl) * (r + rcyl) - d) * (d - (r - rcyl) * (r - rcyl));
3022
3023 if (mysqrt < 0) {
3024 return {};
3025 }
3026
3027 mysqrt = std::sqrt(mysqrt);
3028 double firstterm = xc / 2 + (xc * (rcyl * rcyl - r * r)) / (2 * d);
3029 double secondterm = (mysqrt * yc) / (2 * d);
3030 const double x1 = firstterm + secondterm;
3031 const double x2 = firstterm - secondterm;
3032 firstterm = yc / 2 + (yc * (rcyl * rcyl - r * r)) / (2 * d);
3033 secondterm = (mysqrt * xc) / (2 * d);
3034 const double y1 = firstterm - secondterm;
3035 const double y2 = firstterm + secondterm;
3036 double x = parforextrap.position().x();
3037 double y = parforextrap.position().y();
3038 const double dist1 = (x - x1) * (x - x1) + (y - y1) * (y - y1);
3039 const double dist2 = (x - x2) * (x - x2) + (y - y2) * (y - y2);
3040
3041 if (dist1 < dist2) {
3042 x = x1;
3043 y = y1;
3044 } else {
3045 x = x2;
3046 y = y2;
3047 }
3048
3049 const double phi1 = std::atan2(y - yc, x - xc);
3050 const double deltaphi = xAOD::P4Helpers::deltaPhi(phi1, phi0);
3051
3052 const double delta_z = r * deltaphi / tantheta;
3053 const double z = z0 + delta_z;
3054
3055 const Amg::Vector3D intersect = Amg::Vector3D(x, y, z);
3056
3057 if (std::abs(z - surf.center().z()) > surf.bounds().halflengthZ()) {
3058 return {};
3059 }
3060
3061 const Amg::Vector3D normal(x, y, 0);
3062 const double phidir = xAOD::P4Helpers::deltaPhi(parforextrap.parameters()[Trk::phi], -deltaphi);
3063
3064 const Amg::Vector3D trackdir(cos(phidir) * sintheta, std::sin(phidir) * sintheta, costheta);
3065
3066 const double costracksurf = std::abs(normal.unit().dot(trackdir));
3067
3068 return std::make_pair(intersect, costracksurf);
3069 }
3070
3072 Cache &cache,
3073 GXFTrajectory &trajectory,
3074 int indexoffset,
3075 std::vector<std::pair<const Layer *, const Layer *>> &layers,
3076 const TrackParameters *refpar,
3077 const TrackParameters *refpar2,
3078 ParticleHypothesis matEffects
3079 ) const {
3080 std::vector<std::unique_ptr<GXFTrackState>> & states = trajectory.trackStates();
3081 std::vector<std::unique_ptr<GXFTrackState>> oldstates = std::move(states);
3082
3083 states.clear();
3084 states.reserve(oldstates.size() + layers.size());
3085
3086 int layerindex = 0;
3087
3088 /*
3089 * First, simply copy any upstream states. We do not need to anything with
3090 * them as they are presumably already fit.
3091 */
3092 for (int i = 0; i <= indexoffset; i++) {
3093 trajectory.addBasicState(std::move(oldstates[i]));
3094 }
3095
3096 const TrackParameters *parforextrap = refpar;
3097
3098 /*
3099 * For non-upstream layers, that is to say layers after the last existing
3100 * material, the logic is not so simple.
3101 */
3102 for (int i = indexoffset + 1; i < (int) oldstates.size(); i++) {
3103 const double rmeas = oldstates[i]->position().perp();
3104 const double zmeas = oldstates[i]->position().z();
3105
3106 /*
3107 * Iterate over layers. Note that this is shared between different track
3108 * states! This makes sense, because the track states are sorted and so
3109 * are the layers. If that sorting is consistent between the two, which
3110 * it should be, this works.
3111 */
3112 while (layerindex < (int) layers.size()) {
3113 Amg::Vector3D intersect;
3114 double costracksurf = 0.0;
3115 const Layer *layer = nullptr;
3116
3117 /*
3118 * Remember how we distinguish between disc and cylinder surfaces: if
3119 * the first element of the pair is not null, then it points to a
3120 * cylinder. If the second element of the pais is not null, then it's a
3121 * disc surface. That is the logic being applied here. Separate
3122 * handling of cylinders and discs.
3123 */
3124 if (layers[layerindex].first != nullptr) {
3125 /*
3126 * First, convert the pointer to a real CylinderSurface pointer.
3127 */
3128 layer = layers[layerindex].first;
3129 const CylinderSurface *cylsurf = static_cast<const CylinderSurface *> (&layer->surfaceRepresentation());
3130
3131 /*
3132 * Check if we have a different set of parameters that make more
3133 * sense. If not, reuse the ones we already had.
3134 */
3135 if (oldstates[i]->trackParameters() != nullptr) {
3136 const double rlayer = cylsurf->bounds().r();
3137 if (std::abs(rmeas - rlayer) < std::abs(parforextrap->position().perp() - rlayer)) {
3138 parforextrap = oldstates[i]->trackParameters();
3139 }
3140 }
3141
3142 /*
3143 * Check if we have an intersection with this layer. If so, break out
3144 * of this loop, we have what we need. Otherwise, go to the next
3145 * layer and try again.
3146 */
3147 if (auto res = addMaterialFindIntersectionCyl(cache, *cylsurf, *parforextrap, *refpar2, matEffects)) {
3148 std::tie(intersect, costracksurf) = res.value();
3149 } else {
3150 layerindex++;
3151 continue;
3152 }
3153
3154 if (cylsurf->bounds().r() > rmeas) break;
3155 } else if (layers[layerindex].second != nullptr) {
3156 /*
3157 * The logic for disc surfaces is essentially identical to the logic
3158 * for cylinder surfaces. You'll find comments for that just a dozen
3159 * lines up.
3160 */
3161 layer = layers[layerindex].second;
3162 const DiscSurface *discsurf = static_cast<const DiscSurface *> (&layer->surfaceRepresentation());
3163
3164 if (oldstates[i]->trackParameters() != nullptr) {
3165 const double zlayer = discsurf->center().z();
3166 if (std::abs(zmeas - zlayer) < std::abs(parforextrap->position().z() - zlayer)) {
3167 parforextrap = oldstates[i]->trackParameters();
3168 }
3169 }
3170
3171 if (auto res = addMaterialFindIntersectionDisc(cache, *discsurf, *parforextrap, *refpar2, matEffects)) {
3172 std::tie(intersect, costracksurf) = res.value();
3173 } else {
3174 layerindex++;
3175 continue;
3176 }
3177
3178 if (std::abs(discsurf->center().z()) > std::abs(zmeas)) break;
3179 } else {
3180 throw std::logic_error("Unhandled surface.");
3181 }
3182
3183 /*
3184 * Grab the material properties from our layer. If there are none, just
3185 * go to the next layer.
3186 */
3187 const MaterialProperties *matprop = layer->layerMaterialProperties()->fullMaterial(intersect);
3188 if (matprop == nullptr) {
3189 layerindex++;
3190 continue;
3191 }
3192
3193 /*
3194 * Convert the material properties into the internal representation of
3195 * material effects.
3196 */
3197 const double X0 = matprop->thicknessInX0();
3198 const double currentqoverp = (matEffects != Trk::electron)
3199 ? parforextrap->parameters()[Trk::qOverP]
3200 : refpar2->parameters()[Trk::qOverP];
3201 const double actualx0 = X0 / costracksurf;
3202 const double de = -std::abs(
3203 (matprop->thickness() / costracksurf) *
3204 m_elosstool->dEdX(
3205 *matprop,
3206 (m_p != 0.0 ? std::abs(m_p) : std::abs(1. / currentqoverp)),
3207 matEffects));
3208 const double sintheta = std::sin(parforextrap->parameters()[Trk::theta]);
3209 const double sigmascat = std::sqrt(m_scattool->sigmaSquare(
3210 *matprop,
3211 (m_p != 0.0 ? std::abs(m_p) : std::abs(1. / currentqoverp)),
3212 1. / costracksurf,
3213 matEffects));
3214
3215 std::unique_ptr<GXFMaterialEffects> meff = std::make_unique<GXFMaterialEffects>();
3216 meff->setDeltaE(de);
3217 meff->setScatteringSigmas(sigmascat / sintheta, sigmascat);
3218 meff->setX0(actualx0);
3219 meff->setSurface(&layer->surfaceRepresentation());
3220 meff->setMaterialProperties(matprop);
3221
3222 /*
3223 * If we have an electron, or if so configured, calculate energy loss
3224 * as well.
3225 */
3226 std::unique_ptr<EnergyLoss> eloss;
3227
3228 if (cache.m_fiteloss || (matEffects == electron && cache.m_asymeloss)) {
3229 eloss = std::make_unique<EnergyLoss>(m_elosstool->energyLoss(
3230 *matprop,
3231 (m_p != 0.0 ? std::abs(m_p) : std::abs(1. / currentqoverp)),
3232 1. / costracksurf,
3234 matEffects
3235 ));
3236 if (eloss != nullptr) {
3237 meff->setSigmaDeltaE(eloss->sigmaDeltaE());
3238 }
3239 }
3240
3241 if (matEffects == electron && cache.m_asymeloss) {
3242 meff->setDeltaE(-5);
3243 if (trajectory.numberOfTRTHits() == 0) {
3244 meff->setScatteringSigmas(0, 0);
3245 }
3246
3247 meff->setSigmaDeltaE(50);
3248 if (eloss != nullptr) {
3249 meff->setSigmaDeltaEPos(eloss->sigmaPlusDeltaE());
3250 meff->setSigmaDeltaENeg(eloss->sigmaMinusDeltaE());
3251 }
3252 }
3253
3255 "X0: " << meff->x0() << " qoverp: " << currentqoverp <<
3256 " sigmascat " << meff->sigmaDeltaTheta() <<" eloss: " << meff->deltaE() <<
3257 " sigma eloss: " << meff->sigmaDeltaE()
3258 );
3259
3260 /*
3261 * Create a new track state in the internal representation and load it
3262 * with any and all information we might have.
3263 */
3264 std::unique_ptr<GXFTrackState> matstate = std::make_unique<GXFTrackState>(
3265 std::move(meff),
3266 std::unique_ptr<const TrackParameters>()
3267 );
3268 matstate->setPosition(intersect);
3269 trajectory.addMaterialState(std::move(matstate));
3270
3271 /*
3272 * We're done on this layer, so the next state will go to the next
3273 * layer.
3274 */
3275 layerindex++;
3276 }
3277
3278 trajectory.addBasicState(std::move(oldstates[i]));
3279 }
3280 }
3281
3283 Cache & cache,
3284 std::vector<std::pair<const Layer *, const Layer *>> & layers,
3285 std::vector<std::pair<const Layer *, const Layer *>> & upstreamlayers,
3286 const std::vector<std::unique_ptr<GXFTrackState>> & oldstates,
3287 GXFTrackState & firstsistate,
3288 GXFTrackState & lastsistate,
3289 const TrackParameters *refpar,
3290 bool hasmat
3291 ) {
3292 /*
3293 * Reserve some arbitrary number of layers in the output vectors.
3294 */
3295 upstreamlayers.reserve(5);
3296 layers.reserve(30);
3297
3298 /*
3299 * Gather a bunch of numbers from the parameters. Someties we need to grab
3300 * them from the first silicon state, sometimes from the last.
3301 */
3302 const double firstz = firstsistate.trackParameters()->position().z();
3303 const double firstr = firstsistate.trackParameters()->position().perp();
3304 const double firstz2 = hasmat ? lastsistate.trackParameters()->position().z() : firstsistate.trackParameters()->position().z();
3305 const double firstr2 = hasmat ? lastsistate.trackParameters()->position().perp() : firstsistate.trackParameters()->position().perp();
3306
3307 GXFTrackState *firststate = oldstates.front().get();
3308 GXFTrackState *laststate = oldstates.back().get();
3309
3310 /*
3311 * This number is particularly interesting, as it determines which side we
3312 * need to look at in regards to the disc layers.
3313 */
3314 const double lastz = laststate->position().z();
3315 const double lastr = laststate->position().perp();
3316
3317 const Layer *startlayer = firststate->associatedSurface().associatedLayer();
3318 const Layer *startlayer2 = hasmat ? lastsistate.associatedSurface().associatedLayer() : nullptr;
3319 const Layer *endlayer = laststate->associatedSurface().associatedLayer();
3320
3321 const double tantheta = std::tan(refpar->parameters()[Trk::theta]);
3322 const double slope = (tantheta != 0) ? 1 / tantheta : 0; // (lastz-firstz)/(lastr-firstr);
3323
3324 /*
3325 * First, we will grab our disc layers.
3326 */
3327 if (slope != 0) {
3328 std::vector < const Layer *>::const_iterator it;
3329 std::vector < const Layer *>::const_iterator itend;
3330
3331 /*
3332 * If we're on the positive z-side of the detector, we will iterate over
3333 * the positive discs. Otherwise, we will need to iterate over the
3334 * negative discs.
3335 */
3336 if (lastz > 0) {
3337 it = cache.m_posdiscs.begin();
3338 itend = cache.m_posdiscs.end();
3339 } else {
3340 it = cache.m_negdiscs.begin();
3341 itend = cache.m_negdiscs.end();
3342 }
3343
3344 /*
3345 * Iterate over our disc layers.
3346 */
3347 for (; it != itend; ++it) {
3348 /*
3349 * If we've overshot the last hit in our track, we don't need to look
3350 * at any further layers. We're done!
3351 */
3352 if (std::abs((*it)->surfaceRepresentation().center().z()) > std::abs(lastz)) {
3353 break;
3354 }
3355
3356 /*
3357 * Grab the bounds from the layer, which is a more useful kind of
3358 * object that allows us to do some geometric calculations.
3359 */
3360 const DiscBounds *discbounds = static_cast<const DiscBounds *> (&(*it)->surfaceRepresentation().bounds());
3361
3362 /*
3363 * Ensure that we've actually hit the layer!
3364 */
3365 if (discbounds->rMax() < firstr || discbounds->rMin() > lastr) {
3366 continue;
3367 }
3368
3369 const double rintersect = firstr + ((*it)->surfaceRepresentation().center().z() - firstz) / slope;
3370
3371 if (
3372 rintersect < discbounds->rMin() - 50 ||
3373 rintersect > discbounds->rMax() + 50
3374 ) {
3375 continue;
3376 }
3377
3378 /*
3379 * We also do not need to consider the last layer. If all goes well,
3380 * the next loop will immediately break because it will be an
3381 * overshoot.
3382 */
3383 if ((*it) == endlayer) {
3384 continue;
3385 }
3386
3387 /*
3388 * If this layer lies before the first hit, it's an upstream hit and we
3389 * add it to the upstream layer vector.
3390 *
3391 * Notice how we add this layer on the right side of the pair, that's
3392 * the convention. Discs to right, cylinders go left.
3393 */
3394 if (
3395 std::abs((*it)->surfaceRepresentation().center().z()) < std::abs(firstz) ||
3396 (*it) == startlayer
3397 ) {
3398 upstreamlayers.emplace_back((Layer *) nullptr, (*it));
3399 }
3400
3401 /*
3402 * Otherwise, it's a normal layer. Add it.
3403 */
3404 if (
3405 (*it) != startlayer &&
3406 (std::abs((*it)->surfaceRepresentation().center().z()) > std::abs(firstz2) ||
3407 (*it) == startlayer2)
3408 ) {
3409 layers.emplace_back((Layer *) nullptr, (*it));
3410 }
3411 }
3412 }
3413
3414 /*
3415 * Now, we add the barrel cylinder layers.
3416 */
3417 for (const auto *barrelcylinder : cache.m_barrelcylinders) {
3418 /*
3419 * Check for overshoots and reject them.
3420 */
3421 if (barrelcylinder->surfaceRepresentation().bounds().r() > lastr) {
3422 break;
3423 }
3424
3425 /*
3426 * Confirm intersection with the layer.
3427 */
3428 const double zintersect = firstz + (barrelcylinder->surfaceRepresentation().bounds().r() - firstr) * slope;
3429
3430 if (std::abs(zintersect - barrelcylinder->surfaceRepresentation().center().z()) >
3431 ((const CylinderSurface*)(&barrelcylinder->surfaceRepresentation()))->bounds().halflengthZ() + 50) {
3432 continue;
3433 }
3434
3435 if (barrelcylinder == endlayer) {
3436 continue;
3437 }
3438
3439 /*
3440 * Same as with the discs, add the layers to the output vectors.
3441 */
3442 if (barrelcylinder->surfaceRepresentation().bounds().r() < firstr ||
3443 barrelcylinder == startlayer) {
3444 upstreamlayers.emplace_back(barrelcylinder, (Layer*)nullptr);
3445 }
3446
3447 if (barrelcylinder != startlayer &&
3448 (barrelcylinder->surfaceRepresentation().bounds().r() > firstr2 ||
3449 barrelcylinder == startlayer2)) {
3450 layers.emplace_back(barrelcylinder, (Layer*)nullptr);
3451 }
3452 }
3453
3454 /*
3455 * Sort the layers such that they are in the right order, from close to far
3456 * in respect to the experiment center.
3457 */
3458 std::sort(layers.begin(), layers.end(), GXF::LayerSort());
3459 std::sort(upstreamlayers.begin(), upstreamlayers.end(), GXF::LayerSort());
3460 }
3461
3463 const EventContext& ctx,
3464 Cache & cache,
3465 GXFTrajectory & trajectory,
3466 const TrackParameters * refpar2,
3467 ParticleHypothesis matEffects
3468 ) const {
3469 /*
3470 * Ensure that the cache contains a valid tracking geometry that we can
3471 * use.
3472 */
3473 const bool caloEntranceIsValid = ensureValidEntranceCalo(ctx, cache);
3474 if (!caloEntranceIsValid) {
3475 return;
3476 }
3477
3478 /*
3479 * If we have not yet set the discs on either side of the detector as well
3480 * as the barrel layers, do so now.
3481 */
3482 if (
3483 cache.m_negdiscs.empty() &&
3484 cache.m_posdiscs.empty() &&
3485 cache.m_barrelcylinders.empty()
3486 ) {
3487 /*
3488 * Attempt to add the layer information to the cache using the previously
3489 * selected tracking volume.
3490 */
3491 const bool ok = processTrkVolume(cache, cache.m_caloEntrance);
3492
3493 /*
3494 * If this process somehow fails, we cannot use the fast material adding
3495 * algorithm and we must fall back to the slow version. As far as I know
3496 * this doesn't really happen.
3497 */
3498 if (!ok) {
3499 ATH_MSG_DEBUG("Falling back to slow material collection");
3500 cache.m_fastmat = false;
3501 addMaterial(ctx, cache, trajectory, refpar2, matEffects);
3502 return;
3503 }
3504
3505 /*
3506 * Sort the discs and barrel layers such that they are in the right
3507 * order. What the right order is in this case is defined a bit above
3508 * this code, in the GXF::LayerSort2 class. Should be in increasing order
3509 * of distance from the detector center.
3510 */
3511 std::stable_sort(cache.m_negdiscs.begin(), cache.m_negdiscs.end(), GXF::LayerSort2());
3512 std::stable_sort(cache.m_posdiscs.begin(), cache.m_posdiscs.end(), GXF::LayerSort2());
3514 }
3515
3516 const TrackParameters *refpar = refpar2;
3517 bool hasmat = false;
3518 int indexoffset = 0;
3519 int lastmatindex = 0;
3520 std::vector<std::unique_ptr<GXFTrackState>> & oldstates = trajectory.trackStates();
3521
3522 GXFTrackState *firstsistate = nullptr;
3523 GXFTrackState *lastsistate = nullptr;
3524
3525 /*
3526 * This loop serves several purposes in one, because it's very efficient:
3527 *
3528 * 1. It detects whether there are already any materials on this track, and
3529 * if so where they are.
3530 * 2. It determines what the first and last silicon hits are.
3531 * 3. It calculates trackparameters for any states that might not have them
3532 * for whatever reason.
3533 */
3534 for (int i = 0; i < (int) oldstates.size(); i++) {
3535 if (oldstates[i]->materialEffects() != nullptr) {
3536 hasmat = true;
3537 lastmatindex = i;
3538 }
3539
3540 if (
3541 oldstates[i]->measurementType() == TrackState::Pixel ||
3542 oldstates[i]->measurementType() == TrackState::SCT
3543 ) {
3544 if (firstsistate == nullptr) {
3545 if (oldstates[i]->trackParameters() == nullptr) {
3546 std::unique_ptr<const TrackParameters> tmppar(m_propagator->propagateParameters(
3547 ctx,
3548 *refpar,
3549 oldstates[i]->associatedSurface(),
3551 false,
3552 trajectory.m_fieldprop,
3554 ));
3555
3556 if (tmppar == nullptr) return;
3557
3558 oldstates[i]->setTrackParameters(std::move(tmppar));
3559 }
3560 firstsistate = oldstates[i].get();
3561 }
3562 lastsistate = oldstates[i].get();
3563 }
3564 }
3565
3566 /*
3567 * Only happens when there are no tracks, and that shouldn't happen in the
3568 * first place.
3569 */
3570 if (lastsistate == nullptr) {
3571 throw std::logic_error("No track state");
3572 }
3573
3574 /*
3575 * Also try to generate a set of track parameters for the last silicon hit
3576 * if it doesn't have any. I don't really know when that would happen, but
3577 * I suppose it's possible. Anything is possible, if you believe hard
3578 * enough.
3579 */
3580 if (lastsistate->trackParameters() == nullptr) {
3581 std::unique_ptr<const TrackParameters> tmppar(m_propagator->propagateParameters(
3582 ctx,
3583 *refpar,
3584 lastsistate->associatedSurface(),
3585 alongMomentum, false,
3586 trajectory.m_fieldprop,
3588 ));
3589
3590 if (tmppar == nullptr) return;
3591
3592 lastsistate->setTrackParameters(std::move(tmppar));
3593 }
3594
3595 /*
3596 * If we have found any materials on the track, we've presumably already
3597 * done a fit for that part of the track, so the reference parameters are
3598 * either the first or last silicon state's parameters.
3599 */
3600 if (hasmat) {
3601 refpar = lastsistate->trackParameters();
3602 indexoffset = lastmatindex;
3603 } else {
3604 refpar = firstsistate->trackParameters();
3605 }
3606
3607 /*
3608 * These vectors will hold the layers. The types here are a little bit
3609 * strange, but the idea is that the right member is a disc surface and the
3610 * left member is a cylindrical surface. Could be more elegantly done using
3611 * polymorphism.
3612 *
3613 * The upstream layers may already be filled due to previous fits.
3614 *
3615 * TODO: Use polymorphism to get rid of these strange types.
3616 */
3617 std::vector<std::pair<const Layer *, const Layer *>> layers;
3618 std::vector<std::pair<const Layer *, const Layer *>> & upstreamlayers = trajectory.upstreamMaterialLayers();
3619
3620 /*
3621 * Fill the aforementioned layer vectors with layers.
3622 */
3623 addMaterialGetLayers(cache, layers, upstreamlayers, oldstates, *firstsistate, *lastsistate, refpar, hasmat);
3624
3625 /*
3626 * Finally, use that layer information to actually add states to the track.
3627 */
3628 addMaterialUpdateTrajectory(cache, trajectory, indexoffset, layers, refpar, refpar2, matEffects);
3629 }
3630
3632 const EventContext& ctx,
3633 Cache & cache,
3634 GXFTrajectory & trajectory,
3635 const TrackParameters * refpar2,
3636 ParticleHypothesis matEffects
3637 ) const {
3638 if (refpar2 == nullptr) {
3639 return;
3640 }
3641 const MeasurementBase *firstmuonhit = nullptr;
3642 const MeasurementBase *lastmuonhit = nullptr;
3643 const MeasurementBase *firstidhit =
3644 nullptr;
3645 const MeasurementBase *lastidhit = nullptr;
3646 const MeasurementBase *firsthit = nullptr;
3647 const MeasurementBase *lasthit = nullptr;
3648 std::vector<std::unique_ptr<GXFTrackState>> & states = trajectory.trackStates();
3649 std::vector<std::unique_ptr<GXFTrackState>> matstates;
3650 std::unique_ptr< const std::vector < const TrackStateOnSurface *>,
3651 void (*)(const std::vector<const TrackStateOnSurface *> *) >
3653 bool matvec_used=false;
3654 std::unique_ptr<TrackParameters> startmatpar1;
3655 std::unique_ptr<TrackParameters> startmatpar2;
3656 const TrackParameters *firstidpar = nullptr;
3657 const TrackParameters *lastidpar = nullptr;
3658 const TrackParameters *firstsiliconpar = nullptr;
3659 const TrackParameters *lastsiliconpar = nullptr;
3660 const TrackParameters *firstmatpar = nullptr;
3661 const TrackParameters *firstcalopar = nullptr;
3662 const TrackParameters *lastcalopar = nullptr;
3663 const TrackParameters *firstmuonpar = nullptr;
3664 const TrackParameters *lastmuonpar = nullptr;
3665
3666 int npseudomuon1 = 0;
3667 int npseudomuon2 = 0;
3668
3669 for (auto & state : states) {
3670 TrackState::MeasurementType const meastype = state->measurementType();
3671 const TrackParameters *tp = state->trackParameters();
3672 GXFMaterialEffects *meff = state->materialEffects();
3673
3674 if (meastype == TrackState::Pseudo) {
3675 if (firstidhit == nullptr) {
3676 npseudomuon1++;
3677 } else {
3678 npseudomuon2++;
3679 }
3680 continue;
3681 }
3682
3683 if (state->getStateType(TrackStateOnSurface::Measurement) || state->getStateType(TrackStateOnSurface::Outlier)) {
3684 if (firsthit == nullptr) {
3685 firsthit = state->measurement();
3686 if (cache.m_acceleration) {
3687 if (tp == nullptr) {
3688 tp = m_extrapolator->extrapolate(
3689 ctx,
3690 *refpar2,
3691 state->associatedSurface(),
3693 false,
3694 matEffects
3695 ).release();
3696
3697 if (tp == nullptr) {
3698 return;
3699 }
3700
3701 state->setTrackParameters(std::unique_ptr<const TrackParameters>(tp));
3702 }
3703 // When acceleration is enabled, material collection starts from first hit
3704 refpar2 = tp;
3705 }
3706 }
3707 lasthit = state->measurement();
3708 if (
3709 meastype == TrackState::Pixel ||
3710 meastype == TrackState::SCT ||
3711 meastype == TrackState::TRT
3712 ) {
3713 if (firstidhit == nullptr) {
3714 firstidhit = state->measurement();
3715 }
3716
3717 if ((firstidpar == nullptr) && (tp != nullptr)) {
3718 firstidpar = tp;
3719 }
3720
3721 lastidhit = state->measurement();
3722 if (tp != nullptr) {
3723 lastidpar = tp;
3724 }
3725
3726 if ((tp != nullptr) && meastype != TrackState::TRT) {
3727 if (firstsiliconpar == nullptr) {
3728 firstsiliconpar = tp;
3729 }
3730 lastsiliconpar = tp;
3731 }
3732 }
3733
3734 if (
3735 meastype == TrackState::RPC ||
3736 meastype == TrackState::CSC ||
3737 meastype == TrackState::TGC ||
3738 meastype == TrackState::MDT ||
3739 meastype == TrackState::MM ||
3740 meastype == TrackState::STGC
3741 ) {
3742 if (firstmuonhit == nullptr) {
3743 firstmuonhit = state->measurement();
3744 if (tp != nullptr) {
3745 firstmuonpar = tp;
3746 }
3747 }
3748 lastmuonhit = state->measurement();
3749 if (tp != nullptr) {
3750 lastmuonpar = tp;
3751 }
3752 }
3753 }
3754 if (state->getStateType(TrackStateOnSurface::Scatterer) || state->getStateType(TrackStateOnSurface::BremPoint)) {
3755 if (meff->deltaE() == 0) {
3756 if (firstcalopar == nullptr) {
3757 firstcalopar = state->trackParameters();
3758 }
3759 lastcalopar = state->trackParameters();
3760 }
3761 if (firstmatpar == nullptr) {
3762 firstmatpar = state->trackParameters();
3763 }
3764 }
3765 }
3766
3767 std::unique_ptr<TrackParameters> refpar;
3768 AmgVector(5) newpars = refpar2->parameters();
3769
3770 if (trajectory.m_straightline && m_p != 0) {
3771 newpars[Trk::qOverP] = 1 / m_p;
3772 }
3773
3775 newpars[0], newpars[1], newpars[2], newpars[3], newpars[4], std::nullopt
3776 );
3777
3778 if (firstmatpar != nullptr) {
3779 startmatpar1 = unique_clone(firstsiliconpar);
3780 startmatpar2 = unique_clone(lastsiliconpar);
3781 }
3782
3783 if ((startmatpar1 == nullptr) || ((firstidhit != nullptr) && (firstmuonhit != nullptr))) {
3784 startmatpar1 = unique_clone(refpar);
3785 startmatpar2 = unique_clone(refpar);
3786
3787 const double mass = trajectory.mass();
3788 if (mass > 200 * MeV) {
3789 const AmgVector(5) & newpars = startmatpar2->parameters();
3790 const double oldp = std::abs(1 / newpars[Trk::qOverP]);
3791 const double sign = (newpars[Trk::qOverP] < 0) ? -1 : 1;
3792
3793 startmatpar2 = startmatpar2->associatedSurface().createUniqueTrackParameters(
3794 newpars[0], newpars[1], newpars[2], newpars[3],
3795 sign / std::sqrt(oldp * oldp + 2 * 100 * MeV * std::sqrt(oldp * oldp + mass * mass) + 100 * MeV * 100 * MeV),
3796 std::nullopt
3797 );
3798 }
3799 } else if (trajectory.m_straightline && m_p != 0) {
3800 AmgVector(5) newpars = startmatpar1->parameters();
3801 newpars[Trk::qOverP] = 1 / m_p;
3802
3803 startmatpar1 = startmatpar1->associatedSurface().createUniqueTrackParameters(
3804 newpars[0], newpars[1], newpars[2], newpars[3], newpars[4], std::nullopt
3805 );
3806
3807 newpars = startmatpar2->parameters();
3808 newpars[Trk::qOverP] = 1 / m_p;
3809
3810 startmatpar2 = startmatpar2->associatedSurface().createUniqueTrackParameters(
3811 newpars[0], newpars[1], newpars[2], newpars[3], newpars[4], std::nullopt
3812 );
3813 }
3814
3815 if ((firstidhit != nullptr) && trajectory.numberOfSiliconHits() > 0 && cache.m_idmat) {
3816
3818 refpar->position(),
3819 refpar->momentum().unit()
3820 );
3821
3822 const double distance = getDistance(distsol);
3823
3824 if (distance < 0 && distsol.numberOfSolutions() > 0 && !cache.m_acceleration) {
3825 ATH_MSG_DEBUG("Obtaining upstream layers from Extrapolator");
3826
3827 const Surface *destsurf = &firstidhit->associatedSurface();
3828 std::unique_ptr<const TrackParameters> tmppar;
3829
3830 if (firstmuonhit != nullptr) {
3831 const bool caloEntranceIsValid = ensureValidEntranceCalo(ctx, cache);
3832 if (caloEntranceIsValid) {
3833 tmppar = m_extrapolator->extrapolateToVolume(ctx,
3834 *startmatpar1,
3835 *cache.m_caloEntrance,
3838
3839 if (tmppar != nullptr) {
3840 destsurf = &tmppar->associatedSurface();
3841 }
3842 }
3843 }
3844
3845 if (matvec_used) cache.m_matTempStore.push_back( std::move(matvec) );
3846 matvec.reset( m_extrapolator->extrapolateM(ctx,
3847 *startmatpar1,
3848 *destsurf,
3850 false, matEffects) );
3851 matvec_used=false;
3852
3853 if (matvec && !matvec->empty()) {
3854 for (int i = (int)matvec->size() - 1; i > -1; i--) {
3855 const MaterialEffectsBase *meb = (*matvec)[i]->materialEffectsOnTrack();
3856 if (meb) {
3858 const MaterialEffectsOnTrack *meot = static_cast < const MaterialEffectsOnTrack * >(meb);
3859 std::unique_ptr<GXFMaterialEffects> meff = std::make_unique<GXFMaterialEffects>(*meot);
3860 const TrackParameters * newpars = (*matvec)[i]->trackParameters() != nullptr ? (*matvec)[i]->trackParameters()->clone() : nullptr;
3861 meff->setSigmaDeltaE(0);
3862 matstates.push_back(std::make_unique<GXFTrackState>(
3863 std::move(meff),
3864 std::unique_ptr<const TrackParameters>(newpars)
3865 ));
3866 matvec_used=true;
3867 }
3868 }
3869 }
3870 }
3871 }
3872 }
3873
3874 if ((lastidhit != nullptr) && trajectory.numberOfSiliconHits() > 0 && cache.m_idmat) {
3876 refpar->position(),
3877 refpar->momentum().unit()
3878 );
3879
3880 const double distance = getDistance(distsol);
3881
3882 if (distance > 0 && distsol.numberOfSolutions() > 0) {
3883 ATH_MSG_DEBUG("Obtaining downstream ID layers from Extrapolator");
3884 const Surface *destsurf = &lastidhit->associatedSurface();
3885 std::unique_ptr<const TrackParameters> tmppar;
3886 std::unique_ptr<Surface> calosurf;
3887 if (firstmuonhit != nullptr) {
3888 const bool caloEntranceIsValid = ensureValidEntranceCalo(ctx, cache);
3889 if (caloEntranceIsValid) {
3890 tmppar = m_extrapolator->extrapolateToVolume(ctx,
3891 *startmatpar2,
3892 *cache.m_caloEntrance,
3895 }
3896
3897 if (tmppar != nullptr) {
3898 const CylinderSurface *cylcalosurf = nullptr;
3899
3900 if (tmppar->associatedSurface().type() == Trk::SurfaceType::Cylinder)
3901 cylcalosurf = static_cast<const CylinderSurface *>(&tmppar->associatedSurface());
3902
3903 const DiscSurface *disccalosurf = nullptr;
3904
3905 if (tmppar->associatedSurface().type() == Trk::SurfaceType::Disc)
3906 disccalosurf = static_cast<const DiscSurface *>(&tmppar->associatedSurface());
3907
3908 if (cylcalosurf != nullptr) {
3909 Amg::Transform3D const trans = Amg::Transform3D(cylcalosurf->transform());
3910 const CylinderBounds & cylbounds = cylcalosurf->bounds();
3911 const double radius = cylbounds.r();
3912 const double hlength = cylbounds.halflengthZ();
3913 calosurf = std::make_unique<CylinderSurface>(trans, radius - 1, hlength);
3914 } else if (disccalosurf != nullptr) {
3915 const double newz = (
3916 disccalosurf->center().z() > 0 ?
3917 disccalosurf->center().z() - 1 :
3918 disccalosurf->center().z() + 1
3919 );
3920
3921 const Amg::Vector3D newpos(
3922 disccalosurf->center().x(),
3923 disccalosurf->center().y(),
3924 newz
3925 );
3926
3927 Amg::Transform3D trans = (disccalosurf->transform());
3928 trans.translation() << newpos;
3929
3930 const DiscBounds *discbounds = static_cast<const DiscBounds *>(&disccalosurf->bounds());
3931 const double rmin = discbounds->rMin();
3932 const double rmax = discbounds->rMax();
3933 calosurf = std::make_unique<DiscSurface>(trans, rmin, rmax);
3934 }
3935 destsurf = calosurf.release();
3936 }
3937 }
3938
3939 if (matvec_used) cache.m_matTempStore.push_back( std::move(matvec) );
3940 matvec.reset(m_extrapolator->extrapolateM(
3941 ctx, *startmatpar2, *destsurf, alongMomentum, false, matEffects));
3942 matvec_used = false;
3943
3944 if (matvec && !matvec->empty()) {
3945 for (const auto & i : *matvec) {
3946 const Trk::MaterialEffectsBase * meb = i->materialEffectsOnTrack();
3947
3948 if (meb) {
3950 const MaterialEffectsOnTrack *meot = static_cast<const MaterialEffectsOnTrack *>(meb);
3951 std::unique_ptr<GXFMaterialEffects> meff = std::make_unique<GXFMaterialEffects>(*meot);
3952 if (cache.m_fiteloss && (meot->energyLoss() != nullptr)) {
3953 meff->setSigmaDeltaE(meot->energyLoss()->sigmaDeltaE());
3954 }
3955
3956 if (matEffects == electron && cache.m_asymeloss) {
3957 meff->setDeltaE(-5);
3958
3959 if (trajectory.numberOfTRTHits() == 0) {
3960 meff->setScatteringSigmas(0, 0);
3961 }
3962
3963 meff->setSigmaDeltaE(50);
3964 }
3965
3966 const TrackParameters * newparams = i->trackParameters() != nullptr ? i->trackParameters()->clone() : nullptr;
3967
3968 matstates.push_back(std::make_unique<GXFTrackState>(
3969 std::move(meff),
3970 std::unique_ptr<const TrackParameters>(newparams)
3971 ));
3972 matvec_used=true;
3973 }
3974 }
3975 }
3976 } else {
3977 ATH_MSG_WARNING("No material layers collected from Extrapolator");
3978 }
3979 }
3980 }
3981
3982 if (cache.m_calomat && (firstmuonhit != nullptr) && (firstidhit != nullptr)) {
3983 const IPropagator *prop = &*m_propagator;
3984
3985 std::vector<MaterialEffectsOnTrack> calomeots = m_calotool->extrapolationSurfacesAndEffects(
3986 *m_navigator->highestVolume(ctx),
3987 *prop,
3988 *lastidpar,
3989 firstmuonhit->associatedSurface(),
3991 muon
3992 );
3993
3994 if (calomeots.empty()) {
3995 ATH_MSG_WARNING("No material layers collected in calorimeter");
3996 return;
3997 }
3998
3999 std::unique_ptr<const TrackParameters> prevtrackpars = unique_clone(lastidpar);
4000 if (lasthit == lastmuonhit) {
4001 for (int i = 0; i < (int) calomeots.size(); i++) {
4002 const PropDirection propdir = alongMomentum;
4003
4004 std::unique_ptr<const TrackParameters> layerpar(m_propagator->propagateParameters(
4005 ctx,
4006 *prevtrackpars,
4007 calomeots[i].associatedSurface(),
4008 propdir,
4009 false,
4010 trajectory.m_fieldprop,
4012 ));
4013
4014 if (layerpar == nullptr) {
4016 return;
4017 }
4018
4019 std::unique_ptr<GXFMaterialEffects> meff = std::make_unique<GXFMaterialEffects>(calomeots[i]);
4020
4021 if (i == 2) {
4022 lastcalopar = layerpar.get();
4023 }
4024
4025 if (i == 1) {
4026 const double qoverp = layerpar->parameters()[Trk::qOverP];
4027 double qoverpbrem = 0;
4028
4029 if (
4030 npseudomuon2 < 2 &&
4031 (firstmuonpar != nullptr) &&
4032 std::abs(firstmuonpar->parameters()[Trk::qOverP]) > 1.e-9
4033 ) {
4034 qoverpbrem = firstmuonpar->parameters()[Trk::qOverP];
4035 } else {
4036 const double sign = (qoverp > 0) ? 1 : -1;
4037 qoverpbrem = sign / (1 / std::abs(qoverp) - std::abs(calomeots[i].energyLoss()->deltaE()));
4038 }
4039
4040 const AmgVector(5) & newpar = layerpar->parameters();
4041
4042 layerpar = layerpar->associatedSurface().createUniqueTrackParameters(
4043 newpar[0], newpar[1], newpar[2], newpar[3], qoverpbrem, std::nullopt
4044 );
4045 meff->setdelta_p(1000 * (qoverpbrem - qoverp));
4046 }
4047
4048 matstates.push_back(std::make_unique<GXFTrackState>(
4049 std::move(meff),
4050 std::unique_ptr<const TrackParameters>(layerpar != nullptr ? layerpar->clone() : nullptr)
4051 ));
4052 prevtrackpars = std::move(layerpar);
4053 }
4054 }
4055
4056 if (
4057 firsthit == firstmuonhit &&
4058 (!cache.m_getmaterialfromtrack || lasthit == lastidhit)
4059 ) {
4060 prevtrackpars = unique_clone(firstidpar);
4061 for (int i = 0; i < (int) calomeots.size(); i++) {
4062 const PropDirection propdir = oppositeMomentum;
4063 std::unique_ptr<const TrackParameters> layerpar(m_propagator->propagateParameters(
4064 ctx,
4065 *prevtrackpars,
4066 calomeots[i].associatedSurface(),
4067 propdir,
4068 false,
4069 trajectory.m_fieldprop,
4071 ));
4072
4073 if (layerpar == nullptr) {
4075 return;
4076 }
4077
4078 std::unique_ptr<GXFMaterialEffects> meff = std::make_unique<GXFMaterialEffects>(calomeots[i]);
4079
4080 if (i == 2) {
4081 firstcalopar = unique_clone(layerpar.get()).release();
4082 }
4083
4084 prevtrackpars = unique_clone(layerpar.get());
4085
4086 if (i == 1) {
4087 const double qoverpbrem = layerpar->parameters()[Trk::qOverP];
4088 double qoverp = 0;
4089
4090 if (
4091 npseudomuon1 < 2 &&
4092 (lastmuonpar != nullptr) &&
4093 std::abs(lastmuonpar->parameters()[Trk::qOverP]) > 1.e-9
4094 ) {
4095 qoverp = lastmuonpar->parameters()[Trk::qOverP];
4096 } else {
4097 const double sign = (qoverpbrem > 0) ? 1 : -1;
4098 qoverp = sign / (1 / std::abs(qoverpbrem) + std::abs(calomeots[i].energyLoss()->deltaE()));
4099 }
4100
4101 meff->setdelta_p(1000 * (qoverpbrem - qoverp));
4102 const AmgVector(5) & newpar = layerpar->parameters();
4103
4104 prevtrackpars = layerpar->associatedSurface().createUniqueTrackParameters(
4105 newpar[0], newpar[1], newpar[2], newpar[3], qoverp, std::nullopt
4106 );
4107 }
4108
4109 matstates.insert(matstates.begin(), std::make_unique<GXFTrackState>(std::move(meff), std::move(layerpar)));
4110 }
4111 }
4112 }
4113
4114 if (lasthit == lastmuonhit && cache.m_extmat) {
4115 std::unique_ptr<const Trk::TrackParameters> muonpar1;
4116
4117 if (lastcalopar != nullptr) {
4118 const bool msEntranceIsValid = ensureValidEntranceMuonSpectrometer(ctx, cache);
4119 if (msEntranceIsValid) {
4120 if (cache.m_msEntrance->inside(lastcalopar->position())) {
4121 muonpar1 = m_extrapolator->extrapolateToVolume(ctx,
4122 *lastcalopar,
4123 *cache.m_msEntrance,
4126
4127 if (muonpar1 != nullptr) {
4128 const Amg::Vector3D trackdir = muonpar1->momentum().unit();
4129 const Amg::Vector3D curvZcrossT = -(trackdir.cross(Amg::Vector3D(0, 0, 1)));
4130 const Amg::Vector3D curvU = curvZcrossT.unit();
4131 const Amg::Vector3D curvV = trackdir.cross(curvU);
4132 Amg::RotationMatrix3D rot = Amg::RotationMatrix3D::Identity();
4133 rot.col(0) = curvU;
4134 rot.col(1) = curvV;
4135 rot.col(2) = trackdir;
4136 Amg::Transform3D trans;
4137 trans.linear().matrix() << rot;
4138 trans.translation() << muonpar1->position() - .1 * trackdir;
4139 PlaneSurface const curvlinsurf(trans);
4140
4141 std::unique_ptr<const TrackParameters> curvlinpar(m_extrapolator->extrapolateDirectly(
4142 ctx,
4143 *muonpar1,
4144 curvlinsurf,
4147 ));
4148
4149 if (curvlinpar != nullptr) {
4150 muonpar1 = std::move(curvlinpar);
4151 }
4152 }
4153 } else {
4154 muonpar1 = std::unique_ptr<TrackParameters>(lastcalopar->clone());
4155 }
4156 }
4157 } else {
4158 muonpar1 = std::unique_ptr<TrackParameters>(refpar->clone());
4159 }
4160
4161 DistanceSolution distsol;
4162
4163 if (muonpar1 != nullptr) {
4164 distsol = lastmuonhit->associatedSurface().straightLineDistanceEstimate(
4165 muonpar1->position(),
4166 muonpar1->momentum().unit()
4167 );
4168 }
4169
4170 double distance = getDistance(distsol);
4171
4172 if ((distance > 0) and(distsol.numberOfSolutions() >
4173 0) and (firstmuonhit != nullptr)) {
4174 distsol = firstmuonhit->associatedSurface().straightLineDistanceEstimate(
4175 muonpar1->position(),
4176 muonpar1->momentum().unit()
4177 );
4178
4179 distance = 0;
4180
4181 if (distsol.numberOfSolutions() == 1) {
4182 distance = distsol.first();
4183 } else if (distsol.numberOfSolutions() == 2) {
4184 distance = (
4185 std::abs(distsol.first()) < std::abs(distsol.second()) ?
4186 distsol.first() :
4187 distsol.second()
4188 );
4189 }
4190
4191 if (distance < 0 && distsol.numberOfSolutions() > 0 && (firstidhit == nullptr)) {
4192 if (firstmuonpar != nullptr) {
4193 AmgVector(5) newpars = firstmuonpar->parameters();
4194
4195 if (trajectory.m_straightline && m_p != 0) {
4196 newpars[Trk::qOverP] = 1 / m_p;
4197 }
4198
4199 muonpar1 = firstmuonpar->associatedSurface().createUniqueTrackParameters(
4200 newpars[0], newpars[1], newpars[2], newpars[3], newpars[4], std::nullopt
4201 );
4202 } else {
4203 std::unique_ptr<const TrackParameters> tmppar(m_propagator->propagateParameters(
4204 ctx,
4205 *muonpar1,
4206 firstmuonhit->associatedSurface(),
4208 false,
4209 trajectory.m_fieldprop,
4211 ));
4212
4213 if (tmppar != nullptr) {
4214 muonpar1 = std::move(tmppar);
4215 }
4216 }
4217 }
4218
4219 const TrackParameters *prevtp = muonpar1.get();
4220 ATH_MSG_DEBUG("Obtaining downstream layers from Extrapolator");
4221 if (matvec_used) cache.m_matTempStore.push_back( std::move(matvec) );
4222 matvec.reset(m_extrapolator->extrapolateM(ctx,
4223 *prevtp,
4224 states.back()->associatedSurface(),
4226 false,
4228 matvec_used = false;
4229
4230 if (matvec && matvec->size() > 1000 && m_rejectLargeNScat) {
4231 ATH_MSG_DEBUG("too many scatterers: " << matvec->size());
4232 return;
4233 }
4234
4235 if (matvec && !matvec->empty()) {
4236 for (int j = 0; j < (int) matvec->size(); j++) {
4237 const MaterialEffectsBase *meb = (*matvec)[j]->materialEffectsOnTrack();
4238
4239 if (meb) {
4240 if ((meb->derivedType() == MaterialEffectsBase::MATERIAL_EFFECTS_ON_TRACK) and (j < (int) matvec->size() - 1)) {
4241 const MaterialEffectsOnTrack *meot = static_cast<const MaterialEffectsOnTrack *>(meb);
4242 std::unique_ptr<GXFMaterialEffects> meff = std::make_unique<GXFMaterialEffects>(*meot);
4243
4244 if (
4245 !trajectory.m_straightline &&
4246 (meot->energyLoss() != nullptr) &&
4247 std::abs(meff->deltaE()) > 25 &&
4248 std::abs((*matvec)[j]->trackParameters()->position().z()) < 13000
4249 ) {
4250 meff->setSigmaDeltaE(meot->energyLoss()->sigmaDeltaE());
4251 }
4252
4253 const TrackParameters * newparams = (*matvec)[j]->trackParameters() != nullptr ? (*matvec)[j]->trackParameters()->clone() : nullptr;
4254
4255 matstates.push_back(std::make_unique<GXFTrackState>(
4256 std::move(meff),
4257 std::unique_ptr<const TrackParameters>(newparams)
4258 ));
4259 matvec_used=true;
4260 }
4261 }
4262 }
4263 }
4264 }
4265 }
4266
4267 if (firsthit == firstmuonhit && cache.m_extmat && (firstcalopar != nullptr)) {
4268 std::unique_ptr<const Trk::TrackParameters> muonpar1;
4269
4270 const bool msEntranceIsValid = ensureValidEntranceMuonSpectrometer(ctx, cache);
4271 if (msEntranceIsValid) {
4272 if (cache.m_msEntrance->inside(firstcalopar->position())) {
4273 muonpar1 = m_extrapolator->extrapolateToVolume(ctx,
4274 *firstcalopar,
4275 *cache.m_msEntrance,
4278
4279 if (muonpar1 != nullptr) {
4280 const Amg::Vector3D trackdir = muonpar1->momentum().unit();
4281 const Amg::Vector3D curvZcrossT = -(trackdir.cross(Amg::Vector3D(0, 0, 1)));
4282 const Amg::Vector3D curvU = curvZcrossT.unit();
4283 const Amg::Vector3D curvV = trackdir.cross(curvU);
4284 Amg::RotationMatrix3D rot = Amg::RotationMatrix3D::Identity();
4285 rot.col(0) = curvU;
4286 rot.col(1) = curvV;
4287 rot.col(2) = trackdir;
4288 Amg::Transform3D trans;
4289 trans.linear().matrix() << rot;
4290 trans.translation() << muonpar1->position() - .1 * trackdir;
4291 const PlaneSurface curvlinsurf(trans);
4292
4293 std::unique_ptr<const TrackParameters> curvlinpar(m_extrapolator->extrapolateDirectly(
4294 ctx,
4295 *muonpar1,
4296 curvlinsurf,
4299 ));
4300
4301 if (curvlinpar != nullptr) {
4302 muonpar1 = std::move(curvlinpar);
4303 }
4304 }
4305 } else {
4306 muonpar1 = std::unique_ptr<const TrackParameters>(firstcalopar->clone());
4307 }
4308 }
4309
4310 DistanceSolution distsol;
4311
4312 if (muonpar1 != nullptr) {
4313 distsol = firstmuonhit->associatedSurface().straightLineDistanceEstimate(
4314 muonpar1->position(),
4315 muonpar1->momentum().unit()
4316 );
4317 }
4318
4319 const double distance = getDistance(distsol);
4320
4321 if (distance < 0 && distsol.numberOfSolutions() > 0) {
4322 const TrackParameters *prevtp = muonpar1.get();
4323 ATH_MSG_DEBUG("Collecting upstream muon material from extrapolator");
4324 if (matvec_used) cache.m_matTempStore.push_back( std::move(matvec) );
4325 matvec.reset(m_extrapolator->extrapolateM(ctx,
4326 *prevtp,
4327 states[0]->associatedSurface(),
4329 false,
4331 matvec_used = false;
4332
4333 if (matvec && !matvec->empty()) {
4334 ATH_MSG_DEBUG("Retrieved " << matvec->size() << " material states");
4335
4336 for (int j = 0; j < (int) matvec->size(); j++) {
4337 const MaterialEffectsBase *meb = (*matvec)[j]->materialEffectsOnTrack();
4338
4339 if (meb != nullptr) {
4340
4341
4342 if ((meb->derivedType() == MaterialEffectsBase::MATERIAL_EFFECTS_ON_TRACK) && j < (int) matvec->size() - 1) {
4343 const MaterialEffectsOnTrack *meot = static_cast<const MaterialEffectsOnTrack *>(meb);
4344 std::unique_ptr<GXFMaterialEffects> meff = std::make_unique<GXFMaterialEffects>(*meot);
4345
4346 if (
4347 !trajectory.m_straightline &&
4348 (meot->energyLoss() != nullptr) &&
4349 std::abs(meff->deltaE()) > 25 &&
4350 std::abs((*matvec)[j]->trackParameters()->position().z()) < 13000
4351 ) {
4352 meff->setSigmaDeltaE(meot->energyLoss()->sigmaDeltaE());
4353 }
4354
4355 const TrackParameters* tmpparams =
4356 (*matvec)[j]->trackParameters() != nullptr
4357 ? (*matvec)[j]->trackParameters()->clone()
4358 : nullptr;
4359
4360 matstates.insert(matstates.begin(), std::make_unique<GXFTrackState>(
4361 std::move(meff),
4362 std::unique_ptr<const TrackParameters>(tmpparams)
4363 ));
4364 matvec_used=true;
4365 }
4366 }
4367 }
4368 }
4369 }
4370 }
4371
4372 ATH_MSG_DEBUG("Number of layers: " << matstates.size());
4373
4374 // Now insert the material states into the trajectory
4375 std::vector<std::unique_ptr<GXFTrackState>> & newstates = states;
4376 std::vector<std::unique_ptr<GXFTrackState>> oldstates = std::move(newstates);
4377
4378 newstates.clear();
4379 newstates.reserve(oldstates.size() + matstates.size());
4380
4381 int layerno = 0;
4382 int firstlayerno = -1;
4383
4384 if (cache.m_acceleration) {
4385 trajectory.addBasicState(std::move(oldstates[0]));
4386 }
4387
4388 const double cosphi = std::cos(refpar->parameters()[Trk::phi0]);
4389 const double sinphi = std::sin(refpar->parameters()[Trk::phi0]);
4390
4391 for (int i = cache.m_acceleration ? 1 : 0; i < (int) oldstates.size(); i++) {
4392 bool addlayer = true;
4393
4394 while (addlayer && layerno < (int) matstates.size()) {
4395 addlayer = false;
4396 const TrackParameters *layerpar = matstates[layerno]->trackParameters();
4397
4398 const DistanceSolution distsol = oldstates[i]->associatedSurface().straightLineDistanceEstimate(
4399 layerpar->position(),
4400 layerpar->momentum().unit()
4401 );
4402
4403 const double distance = getDistance(distsol);
4404
4405 if (distance > 0 && distsol.numberOfSolutions() > 0) {
4406 addlayer = true;
4407 }
4408
4409 if (layerpar->associatedSurface().type() == Trk::SurfaceType::Cylinder) {
4410 const double cylinderradius = layerpar->associatedSurface().bounds().r();
4411 const double trackimpact = std::abs(-refpar->position().x() * sinphi + refpar->position().y() * cosphi);
4412
4413 if (trackimpact > cylinderradius - 5 * mm) {
4414 layerno++;
4415 continue;
4416 }
4417 }
4418
4419 if (i == (int) oldstates.size() - 1) {
4420 addlayer = true;
4421 }
4422
4423 if (addlayer) {
4424 GXFMaterialEffects *meff = matstates[layerno]->materialEffects();
4425
4426 if (meff->sigmaDeltaPhi() > .4 || (meff->sigmaDeltaPhi() == 0 && meff->sigmaDeltaE() <= 0)) {
4427 if (meff->sigmaDeltaPhi() > .4) {
4428 ATH_MSG_DEBUG("Material state with excessive scattering, skipping it");
4429 }
4430
4431 if (meff->sigmaDeltaPhi() == 0) {
4432 ATH_MSG_WARNING("Material state with zero scattering, skipping it");
4433 }
4434
4435 layerno++;
4436 continue;
4437 }
4438
4439 if (firstlayerno < 0) {
4440 firstlayerno = layerno;
4441 }
4442
4443 trajectory.addMaterialState(std::move(matstates[layerno]));
4444
4445 if ((layerpar != nullptr) && matEffects != pion && matEffects != muon) {
4446 const TrackingVolume *tvol = m_navigator->volume(ctx,layerpar->position());
4447 const Layer *lay = nullptr;
4448
4449 if (tvol != nullptr) {
4450 lay = (tvol->closestMaterialLayer(layerpar->position(),layerpar->momentum().normalized())).object;
4451 }
4452
4453 const MaterialProperties *matprop = lay != nullptr ? lay->fullUpdateMaterialProperties(*layerpar) : nullptr;
4454 meff->setMaterialProperties(matprop);
4455 }
4456
4457 layerno++;
4458 }
4459 }
4460 trajectory.addBasicState(std::move(oldstates[i]));
4461 }
4462
4463 ATH_MSG_DEBUG("Total X0: " << trajectory.totalX0() << " total eloss: " << trajectory.totalEnergyLoss());
4464
4465 if (matvec_used) cache.m_matTempStore.push_back( std::move(matvec) );
4466 }
4467
4468 std::unique_ptr<const TrackParameters> GlobalChi2Fitter::makePerigee(
4469 Cache & cache,
4470 const TrackParameters & param,
4471 ParticleHypothesis matEffects
4472 ) const {
4473 const PerigeeSurface *persurf = nullptr;
4474
4476 persurf = static_cast<const PerigeeSurface *>(&param.associatedSurface());
4477
4478 if ((persurf != nullptr) && (!cache.m_acceleration || persurf->center().perp() > 5)) {
4479 const AmgVector(5) & pars = param.parameters();
4481 pars[0], pars[1], pars[2], pars[3], pars[4], std::nullopt
4482 );
4483 }
4484
4485 if (cache.m_acceleration) {
4486 return nullptr;
4487 }
4488
4489 PerigeeSurface const tmppersf;
4490 std::unique_ptr<const TrackParameters> per(m_extrapolator->extrapolate(
4491 Gaudi::Hive::currentContext(),param, tmppersf, oppositeMomentum, false, matEffects));
4492
4493 if (per == nullptr) {
4494 ATH_MSG_DEBUG("Cannot make Perigee with starting parameters");
4495 return nullptr;
4496 }
4497
4498 if(std::abs(per->position().z())>5000.) {
4499 ATH_MSG_WARNING("Pathological perigee well outside of tracking detector!! Returning nullptr");
4500 return nullptr;
4501 }
4502
4503 return per;
4504 }
4505
4507 const EventContext& ctx,
4508 Cache & cache,
4509 GXFTrajectory & trajectory,
4510 const TrackParameters & param,
4511 const RunOutlierRemoval runOutlier,
4512 const ParticleHypothesis matEffects
4513 ) const {
4514 ATH_MSG_DEBUG("--> entering GlobalChi2Fitter::myfit_helper");
4517
4518 if (!trajectory.m_straightline) {
4519 if (trajectory.numberOfSiliconHits() + trajectory.numberOfTRTHits() == trajectory.numberOfHits()) {
4520 trajectory.m_straightline = !cache.m_field_cache.solenoidOn();
4521 } else if ((trajectory.prefit() == 0) && trajectory.numberOfSiliconHits() + trajectory.numberOfTRTHits() == 0) {
4522 trajectory.m_straightline = !cache.m_field_cache.toroidOn();
4523 } else {
4524 trajectory.m_straightline = (!cache.m_field_cache.solenoidOn() && !cache.m_field_cache.toroidOn());
4525 }
4526 }
4527
4528 trajectory.m_fieldprop = trajectory.m_straightline ? Trk::NoField : Trk::FullField;
4529 cache.m_lastiter = 0;
4530
4531 Amg::SymMatrixX lu;
4532
4533 if (trajectory.numberOfPerigeeParameters() == -1) {
4535 if (trajectory.m_straightline) {
4536 trajectory.setNumberOfPerigeeParameters(4);
4537 } else {
4538 trajectory.setNumberOfPerigeeParameters(5);
4539 }
4540 }
4541
4542 if (trajectory.nDOF() < 0) {
4543 ATH_MSG_DEBUG("Not enough measurements, reject track");
4544 return nullptr;
4545 }
4546
4547 cache.m_phiweight.clear();
4548 cache.m_firstmeasurement.clear();
4549 cache.m_lastmeasurement.clear();
4550
4551 if (matEffects != nonInteracting && param.parameters()[Trk::qOverP] == 0 && m_p == 0) {
4552 ATH_MSG_WARNING("Attempt to apply material corrections with q/p=0, reject track");
4553 return nullptr;
4554 }
4555
4556 if (matEffects == Trk::electron && trajectory.m_straightline) {
4557 ATH_MSG_WARNING("Electron fit requires helix track model");
4558 return nullptr;
4559 }
4560
4561 const double mass = Trk::ParticleMasses::mass[matEffects];
4562 trajectory.setMass(mass);
4563
4564 ATH_MSG_DEBUG("start param: " << param << " pos: " << param.position() << " pt: " << param.pT());
4565
4566 std::unique_ptr<const TrackParameters> per = makePerigee(cache, param, matEffects);
4567
4568 if (!cache.m_acceleration && (per == nullptr)) {
4571 ATH_MSG_DEBUG("Propagation to perigee failed 1");
4572 return nullptr;
4573 }
4574
4575 if (matEffects != Trk::nonInteracting && !cache.m_matfilled) {
4576 if (
4577 cache.m_fastmat &&
4578 cache.m_acceleration &&
4579 trajectory.numberOfSiliconHits() + trajectory.numberOfTRTHits() == trajectory.numberOfHits() &&
4580 (m_matupdator.empty() || (m_trackingGeometryReadKey.key().empty()))
4581 ) {
4582 ATH_MSG_WARNING("Tracking Geometry Service and/or Material Updator Tool not configured");
4583 ATH_MSG_WARNING("Falling back to slow material collection");
4584
4585 cache.m_fastmat = false;
4586 }
4587
4588 if (
4589 !cache.m_fastmat ||
4590 !cache.m_acceleration ||
4591 trajectory.numberOfSiliconHits() + trajectory.numberOfTRTHits() != trajectory.numberOfHits()
4592 ) {
4593 addMaterial(ctx, cache, trajectory, per != nullptr ? per.get() : &param, matEffects);
4594 } else {
4596 ctx, cache, trajectory, per != nullptr ? per.get() : &param, matEffects);
4597 }
4598 }
4599
4600 if (cache.m_acceleration && (trajectory.referenceParameters() == nullptr) && (per == nullptr)) {
4602
4603 if (trajectory.numberOfScatterers() >= 2) {
4604 GXFTrackState *scatstate = nullptr;
4605 GXFTrackState *scatstate2 = nullptr;
4606 int scatindex = 0;
4607
4608 for (const auto & state : trajectory.trackStates()) {
4609 if (state->getStateType(TrackStateOnSurface::Scatterer)) {
4610 if (
4611 scatindex == trajectory.numberOfScatterers() / 2 ||
4612 state->materialEffects()->deltaE() == 0
4613 ) {
4614 scatstate2 = state.get();
4615 break;
4616 }
4617
4618 scatindex++;
4619 scatstate = state.get();
4620 }
4621 }
4622
4623 // @TODO coverity complains about a possible null pointer dereferencing in scatstate->... or scatstate2->...
4624 // it seems to me that if (**it).materialEffects()->deltaE()==0 of the first scatterer
4625 // than scatstate will be NULL.
4626 if ((scatstate == nullptr) || (scatstate2 == nullptr)) {
4627 throw std::logic_error("Invalid scatterer");
4628 }
4629
4630 vertex = .49 * (scatstate->position() + scatstate2->position());
4631 } else {
4632 const int nstates = (int) trajectory.trackStates().size();
4633 vertex = .49 * (
4634 trajectory.trackStates()[nstates / 2 - 1]->position() +
4635 trajectory.trackStates()[nstates / 2]->position()
4636 );
4637 }
4638
4639 PerigeeSurface const persurf(vertex);
4640 std::unique_ptr<const TrackParameters> nearestpar;
4641 double mindist = 99999;
4642 std::vector < GXFTrackState * >mymatvec;
4643
4644 for (auto & it : trajectory.trackStates()) {
4645 if ((*it).trackParameters() == nullptr) {
4646 continue;
4647 }
4648
4649 const double distance = persurf
4651 (*it).trackParameters()->position(),
4652 (*it).trackParameters()->momentum().unit())
4653 .first();
4654
4655 const bool insideid = (
4656 (cache.m_caloEntrance == nullptr) ||
4657 cache.m_caloEntrance->inside((*it).trackParameters()->position())
4658 );
4659
4660 if (
4661 (((*it).measurement() != nullptr) && insideid) || (
4662 ((*it).materialEffects() != nullptr) &&
4663 distance > 0 && (
4664 (*it).materialEffects()->deltaE() == 0 ||
4665 ((*it).materialEffects()->sigmaDeltaPhi() == 0 &&
4666 !insideid) ||
4667 (*it).materialEffects()->deltaPhi() != 0
4668 )
4669 )
4670 ) {
4671 const double dist = ((*it).trackParameters()->position() - vertex).perp();
4672 if (dist < mindist) {
4673 mindist = dist;
4674 nearestpar = unique_clone((*it).trackParameters());
4675 mymatvec.clear();
4676 continue;
4677 }
4678 }
4679
4680 if (((*it).materialEffects() != nullptr) && distance > 0) {
4681 mymatvec.push_back(it.get());
4682 }
4683 }
4684
4685 if (nearestpar == nullptr) {
4686 nearestpar = unique_clone(&param);
4687 }
4688
4689 for (auto & state : mymatvec) {
4691 const Surface &matsurf = state->associatedSurface();
4692 const DistanceSolution distsol = matsurf.straightLineDistanceEstimate(
4693 nearestpar->position(), nearestpar->momentum().unit());
4694
4695 const double distance = getDistance(distsol);
4696
4697 if (distance < 0 && distsol.numberOfSolutions() > 0) {
4698 propdir = oppositeMomentum;
4699 }
4700
4701 std::unique_ptr<const TrackParameters> tmppar(m_propagator->propagateParameters(
4702 ctx,
4703 *nearestpar,
4704 matsurf,
4705 propdir,
4706 false,
4707 trajectory.m_fieldprop,
4709 ));
4710
4711 if (tmppar == nullptr) {
4712 propdir = (propdir == oppositeMomentum) ? alongMomentum : oppositeMomentum;
4713 tmppar = m_propagator->propagateParameters(
4714 ctx,
4715 *nearestpar,
4716 matsurf,
4717 propdir,
4718 false,
4719 trajectory.m_fieldprop,
4721 );
4722
4723 if (tmppar == nullptr) {
4726
4727 ATH_MSG_DEBUG("Propagation to perigee failed 2");
4728
4729 return nullptr;
4730 }
4731 }
4732
4733 AmgVector(5) newpars = tmppar->parameters();
4734
4735 if (state->materialEffects()->sigmaDeltaE() > 0) {
4736 newpars[Trk::qOverP] += .001 * state->materialEffects()->delta_p();
4737 } else if (newpars[Trk::qOverP] != 0) {
4738 const double sign = (newpars[Trk::qOverP] > 0) ? 1 : -1;
4739 const double de = std::abs(state->materialEffects()->deltaE());
4740 const double oldp = std::abs(1 / newpars[Trk::qOverP]);
4741 const double newp2 = oldp * oldp - 2 * de * std::sqrt(mass * mass + oldp * oldp) + de * de;
4742 if (newp2 > 0) {
4743 newpars[Trk::qOverP] = sign / std::sqrt(newp2);
4744 }
4745 }
4746
4747 nearestpar = tmppar->associatedSurface().createUniqueTrackParameters(
4748 newpars[0], newpars[1], newpars[2], newpars[3], newpars[4], std::nullopt
4749 );
4750 }
4751
4752 std::unique_ptr<Trk::TrackParameters> tmpPars(m_propagator->propagateParameters(
4753 ctx,
4754 *nearestpar,
4755 persurf,
4757 false,
4758 trajectory.m_fieldprop,
4760 ));
4761
4762 // Parameters are at a Perigee surface so they are perigee parameters
4763 if (tmpPars != nullptr) {
4764 per.reset(static_cast < const Perigee *>(tmpPars.release()));
4765 }
4766
4767 if ((per != nullptr) && (matEffects == Trk::proton || matEffects == Trk::kaon)) {
4768 const double sign = (per->parameters()[Trk::qOverP] < 0) ? -1. : 1.;
4769 const double oldp = 1. / std::abs(per->parameters()[Trk::qOverP]);
4770 const double toteloss = std::abs(trajectory.totalEnergyLoss());
4771 const double newp = std::sqrt(oldp * oldp + 2 * toteloss * std::sqrt(oldp * oldp + mass * mass) + toteloss * toteloss);
4772 AmgVector(5) params = per->parameters();
4773 params[Trk::qOverP] = sign / newp;
4774
4775 per = per->associatedSurface().createUniqueTrackParameters(
4776 params[0], params[1], params[2], params[3], params[4], std::nullopt
4777 );
4778 }
4779
4780 if (per == nullptr) {
4783 ATH_MSG_DEBUG("Propagation to perigee failed 3");
4784
4785 return nullptr;
4786 }
4787
4788 PerigeeSurface const persurf2(per->position());
4789 per = persurf2.createUniqueTrackParameters(
4790 0,
4791 0,
4792 per->parameters()[Trk::phi],
4793 per->parameters()[Trk::theta],
4794 per->parameters()[Trk::qOverP],
4795 std::nullopt
4796 );
4797 } else if (per == nullptr) {
4798 per = makePerigee(cache, param, matEffects);
4799 }
4800
4801 if ((per == nullptr) && (trajectory.referenceParameters() == nullptr)) {
4804 ATH_MSG_DEBUG("Propagation to perigee failed 4");
4805
4806 return nullptr;
4807 }
4808
4809 if (trajectory.m_straightline && (per != nullptr)) {
4810 if (trajectory.numberOfPerigeeParameters() == -1) {
4811 trajectory.setNumberOfPerigeeParameters(4);
4812 }
4813
4814 const AmgVector(5) & pars = per->parameters();
4815 per = per->associatedSurface().createUniqueTrackParameters(
4816 pars[0], pars[1], pars[2], pars[3], 0, std::nullopt
4817 );
4818 } else if (trajectory.numberOfPerigeeParameters() == -1) {
4819 trajectory.setNumberOfPerigeeParameters(5);
4820 }
4821
4822 if (per != nullptr) {
4823 trajectory.setReferenceParameters(std::move(per));
4824 }
4825
4826 const int nfitpar = trajectory.numberOfFitParameters();
4827 const int nperpars = trajectory.numberOfPerigeeParameters();
4828 const int nscat = trajectory.numberOfScatterers();
4829 const int nbrem = trajectory.numberOfBrems();
4830
4831 Eigen::MatrixXd a;
4832 Eigen::MatrixXd a_inv;
4833 a.resize(nfitpar, nfitpar);
4834
4835 Amg::VectorX b(nfitpar);
4836
4837 Amg::MatrixX derivPool(5, nfitpar);
4838 derivPool.setZero();
4839
4840 for (std::unique_ptr<GXFTrackState> & state : trajectory.trackStates()) {
4841 if (state->materialEffects() != nullptr) {
4842 continue;
4843 }
4844 state->setDerivatives(derivPool);
4845 }
4846
4847 bool doderiv = true;
4848 const int tmpminiter = cache.m_miniter;
4849
4850 for (int it = 0; it < m_maxit; ++it) {
4851 cache.m_lastiter = it;
4852
4853 if (it >= m_maxit - 1) {
4854 ATH_MSG_DEBUG("Fit did not converge");
4857 cache.m_miniter = tmpminiter;
4858 return nullptr;
4859 }
4860
4861 if (!trajectory.converged()) {
4862 cache.m_fittercode =
4863 runIteration(ctx, cache, trajectory, it, a, b, lu, doderiv);
4867 } else if (cache.m_fittercode == FitterStatusCode::InvalidAngles) {
4871 }
4872 cache.m_miniter = tmpminiter;
4873 return nullptr;
4874 }
4875
4876 const int nhits = trajectory.numberOfHits();
4877 const int ntrthits = trajectory.numberOfTRTHits();
4878 const int nsihits = trajectory.numberOfSiliconHits();
4879 const double redchi2 = (trajectory.nDOF() > 0) ? trajectory.chi2() / trajectory.nDOF() : 0;
4880 const double prevredchi2 = (trajectory.nDOF() > 0) ? trajectory.prevchi2() / trajectory.nDOF() : 0;
4881
4882
4883 if( nsihits > 0 && it > 0 && it < m_maxitPixelROT )
4884 updatePixelROTs( trajectory, a, b, ctx);
4885
4886 if (
4887 it > 0 &&
4888 it < 4 && (
4889 (redchi2 < prevredchi2 &&
4890 (redchi2 > prevredchi2 - 1 || redchi2 < 2)) ||
4891 nsihits + ntrthits == nhits
4892 ) &&
4893 (runOutlier || m_trtrecal) &&
4894 ntrthits > 0
4895 ) {
4896 if (it != 1 || nsihits != 0 || trajectory.nDOF() <= 0 || trajectory.chi2() / trajectory.nDOF() <= 3) {
4897 ATH_MSG_DEBUG("Running TRT cleaner");
4898 runTrackCleanerTRT(cache, trajectory, a, b, lu, runOutlier, m_trtrecal, it, ctx);
4900 ATH_MSG_DEBUG("TRT cleaner failed, returning null...");
4901 cache.m_miniter = tmpminiter;
4902 return nullptr;
4903 }
4904 }
4905 }
4906
4907 // PHF cut at iteration 3 (to save CPU time)
4908 const int ntrtprechits = trajectory.numberOfTRTPrecHits();
4909 const int ntrttubehits = trajectory.numberOfTRTTubeHits();
4910 float phf = 1.;
4911 if (ntrtprechits+ntrttubehits) {
4912 phf = float(ntrtprechits)/float(ntrtprechits+ntrttubehits);
4913 }
4914 if (phf<m_minphfcut && it>=3) {
4915 if ((ntrtprechits+ntrttubehits)>=15) {
4916 return nullptr;
4917 }
4918 }
4919 ATH_MSG_DEBUG("Iter = " << it << " | nTRTStates = " << ntrthits
4920 << " | nTRTPrecHits = " << ntrtprechits
4921 << " | nTRTTubeHits = " << ntrttubehits
4922 << " | nOutliers = "
4923 << trajectory.numberOfOutliers());
4924
4925 if (!trajectory.converged()) {
4926 cache.m_fittercode = updateFitParameters(trajectory, b, lu);
4930 }
4931 cache.m_miniter = tmpminiter;
4932 return nullptr;
4933 }
4934 }
4935 } else {
4936 break;
4937 }
4938 }
4939
4940 cache.m_miniter = tmpminiter;
4941
4942 if (trajectory.prefit() == 0) {
4943 // Solve assuming the matrix is SPD.
4944 // Cholesky Decomposition is used -- could use LDLT
4945
4946 Eigen::LLT < Eigen::MatrixXd > const lltOfW(a);
4947 if (lltOfW.info() == Eigen::Success) {
4948 // Solve for x where Wx = I
4949 // this is cheaper than invert as invert makes no assumptions about the
4950 // matrix being symmetric
4951 const int ncols = a.cols();
4952 Amg::MatrixX const weightInvAMG = Amg::MatrixX::Identity(ncols, ncols);
4953 a_inv = lltOfW.solve(weightInvAMG);
4954 } else {
4955 ATH_MSG_DEBUG("matrix inversion failed!");
4958 return nullptr;
4959 }
4960 }
4961
4962 GXFTrajectory *finaltrajectory = &trajectory;
4963 if (
4964 (runOutlier || cache.m_sirecal) &&
4965 trajectory.numberOfSiliconHits() == trajectory.numberOfHits()
4966 ) {
4967 calculateTrackErrors(trajectory, a_inv, true);
4968 GXFTrajectory* traj = runTrackCleanerSilicon(ctx,cache, trajectory, a, a_inv, b, runOutlier);
4969
4971 ATH_MSG_DEBUG("Silicon cleaner failed, returning null...");
4972 if (traj != &trajectory) {
4973 delete traj;
4974 }
4975 return nullptr;
4976 }
4977 finaltrajectory = traj;
4978 }
4979
4980 if (m_domeastrackpar && (finaltrajectory->prefit() == 0)) {
4981 calculateTrackErrors(*finaltrajectory, a_inv, false);
4982 }
4983
4984 if (!cache.m_acceleration && (finaltrajectory->prefit() == 0)) {
4985 if (nperpars == 5) {
4986 for (int i = 0; i < a.cols(); i++) {
4987 a_inv(4, i) *= .001;
4988 a_inv(i, 4) *= .001;
4989 }
4990 }
4991
4992 int scatterPos = nperpars + 2 * nscat;
4993 for (int bremno = 0; bremno < nbrem; bremno++, scatterPos++) {
4994 for (int i = 0; i < a.cols(); i++) {
4995 a_inv(scatterPos, i) *= .001;
4996 a_inv(i, scatterPos) *= .001;
4997 }
4998 }
4999
5000 AmgSymMatrix(5) errmat;
5001 errmat.setZero();
5002 const int nperparams = finaltrajectory->numberOfPerigeeParameters();
5003 for (int i = 0; i < nperparams; i++) {
5004 for (int j = 0; j < nperparams; j++) {
5005 (errmat) (j, i) = a_inv(j, i);
5006 }
5007 }
5008
5009 if (trajectory.m_straightline) {
5010 (errmat) (4, 4) = 1e-20;
5011 }
5012
5013 const AmgVector(5) & perpars = finaltrajectory->referenceParameters()->parameters();
5014 std::unique_ptr<const TrackParameters> measper(
5016 perpars[0], perpars[1], perpars[2], perpars[3], perpars[4], std::move(errmat)
5017 )
5018 );
5019
5020 finaltrajectory->setReferenceParameters(std::move(measper));
5021 if (m_fillderivmatrix) {
5022 cache.m_fullcovmat = a_inv;
5023 }
5024 }
5025
5026 std::unique_ptr<Track> track = nullptr;
5027
5028 if (finaltrajectory->prefit() > 0) {
5029 if (finaltrajectory != &trajectory) {
5030 // cppcheck-suppress autovarInvalidDeallocation; false positive
5031 delete finaltrajectory;
5032 }
5033 return nullptr;
5034 }
5035
5036 if (finaltrajectory->numberOfOutliers() <= m_maxoutliers || !runOutlier) {
5037 track = makeTrack(ctx,cache, *finaltrajectory, matEffects);
5038 } else {
5041 }
5042
5043 const double cut = (finaltrajectory->numberOfSiliconHits() ==
5044 finaltrajectory->numberOfHits())
5045 ? 999.0
5046 : m_chi2cut.value();
5047
5048 if (
5049 runOutlier &&
5050 (track != nullptr) && (
5051 track->fitQuality()->numberDoF() != 0 &&
5052 track->fitQuality()->chiSquared() / track->fitQuality()->numberDoF() > cut
5053 )
5054 ) {
5055 track.reset(nullptr);
5057 }
5058
5059 if (track == nullptr) {
5060 ATH_MSG_DEBUG("Track rejected");
5061 }
5062
5063 if (finaltrajectory != &trajectory) {
5064 delete finaltrajectory;
5065 }
5066
5067 return track.release();
5068 }
5069
5071 const EventContext& ctx,
5072 const Cache & cache,
5073 GXFTrajectory & trajectory,
5074 const int it,
5075 Amg::VectorX & b,
5076 int & bremno_maxbrempull,
5077 GXFTrackState* & state_maxbrempull
5078 ) const {
5079 ATH_MSG_DEBUG("fillResidualsAndErrors");
5080
5081 std::vector<std::unique_ptr<GXFTrackState>> & states = trajectory.trackStates();
5082
5083 /*
5084 * The residual and error vectors, we want to fill in this function.
5085 */
5086 Amg::VectorX & res = trajectory.residuals();
5087 Amg::VectorX & error = trajectory.errors();
5088
5089 /*
5090 * These variables are used inside the preprocessing loop for counting and
5091 * managing some quantities.
5092 */
5093 int scatno = 0;
5094 int bremno = 0;
5095 int measno = 0;
5096
5097 /*
5098 * Total number of measurements, and brems and perigee parameters. This is
5099 * used later to fill the residual and error vector to find the offsets.
5100 */
5101 const int nmeas = (int) res.size();
5102 const int nbrem = trajectory.numberOfBrems();
5103 const int nperpars = trajectory.numberOfPerigeeParameters();
5104
5105 /*
5106 * Under certain circumstances, we create new pseudo measurements. Here are
5107 * the static conditions. Later, we have also for each state a more
5108 * confining check.
5109 */
5110 const int nidhits = trajectory.numberOfSiliconHits() + trajectory.numberOfTRTHits();
5111 const int nDOF = trajectory.nDOF();
5112 const bool doNewPseudoMeasurements = (
5113 1 < it &&
5114 it <= 100 &&
5115 nDOF != 0 &&
5116 std::abs((trajectory.prevchi2() - trajectory.chi2()) / nDOF) < 15 &&
5117 nidhits < trajectory.numberOfHits() &&
5118 (nperpars == 0 || nidhits > 0)
5119 );
5120
5121 /*
5122 * Temporary quantities.
5123 * - chi2 will be used later to set the new chi2.
5124 * - maxbrempull collects the elosspull for the kink with the largest
5125 * brems. It is initised to -0.2 to consider only definitely negative
5126 * pulls.
5127 */
5128 double chi2 = 0;
5129 double maxbrempull = -0.2;
5130
5131 /*
5132 * Loop over all hits and do some preprocessing. In this step, we do:
5133 * - Get residuals
5134 * - Get errors
5135 * - Get scattering angles
5136 * - Fill b-vector and chi2 with scattering effects (others fill later)
5137 */
5138 for (int hitno = 0; hitno < (int) states.size(); hitno++) {
5139 std::unique_ptr<GXFTrackState> & state = states[hitno];
5140 const TrackParameters *currenttrackpar = state->trackParameters();
5141 TrackState::MeasurementType const hittype = state->measurementType();
5142 const MeasurementBase *measbase = state->measurement();
5143
5144 /*
5145 * Measurements and outliers.
5146 */
5147 if (state->getStateType(TrackStateOnSurface::Measurement)) {
5148 /*
5149 * Create new pseudo measurements when the static check (evaluated
5150 * outside the loop) and the dynamic checks both pass
5151 */
5152 if (
5153 doNewPseudoMeasurements &&
5154 hittype == TrackState::Pseudo &&
5155 !state->associatedSurface().isFree() &&
5156 !state->isRecalibrated()
5157 ) {
5158 Amg::MatrixX covMatrix(1, 1);
5159 covMatrix(0, 0) = 100;
5160
5161 std::unique_ptr<const PseudoMeasurementOnTrack> newpseudo = std::make_unique<const PseudoMeasurementOnTrack>(
5162 LocalParameters(DefinedParameter(currenttrackpar->parameters()[Trk::locY], Trk::locY)),
5163 std::move(covMatrix),
5164 currenttrackpar->associatedSurface()
5165 );
5166
5167 state->setMeasurement(std::move(newpseudo));
5168 measbase = state->measurement();
5169 }
5170
5171 /*
5172 * Separate all parameters in the residuals and errors. We will handle
5173 * them separately, asuming them uncorrelated.
5174 */
5175 double *errors = state->measurementErrors();
5176 std::array<double,5> residuals = m_residualPullCalculator->residuals(measbase, currenttrackpar, ResidualPull::Biased, hittype);
5177 for (int i = 0; i < 5; i++) {
5178 /*
5179 * Skip the parameter, if there is no accessor for it.
5180 */
5182 continue;
5183 }
5184
5185 /*
5186 * SCT and TGC are 1-dimensional, so we can skip the other parameters.
5187 */
5188 if (i > 0 && (hittype == TrackState::SCT || hittype == TrackState::TGC)) {
5189 continue;
5190 }
5191
5192 error[measno] =
5193 (trajectory.prefit() > 0 && (hittype == TrackState::MDT || (hittype == TrackState::CSC && !state->measuresPhi()))) ?
5194 2 :
5195 errors[i];
5196
5197 res[measno] = residuals[i];
5198
5199 /*
5200 * Ensure, that the phi-residual is mapped into the correct period.
5201 */
5202 if (i == 2) {
5203 res[measno] = -std::remainder(-res[measno], 2 * M_PI);
5204 }
5205
5206 measno++;
5207 }
5208 } else if (state->getStateType(TrackStateOnSurface::Outlier)) {
5209 /*
5210 * NOTE: It seems the residuals are not set in this step. Why?
5211 */
5212 double *errors = state->measurementErrors();
5213 for (int i = 0; i < 5; i++) {
5214 if (errors[i] > 0) {
5215 error[measno] = errors[i];
5216 measno++;
5217 }
5218 }
5219 }
5220
5221 /*
5222 * Scattering angles contribute to the b-vector and the chi2.
5223 */
5224 if (
5225 state->getStateType(TrackStateOnSurface::Scatterer) &&
5226 ((trajectory.prefit() == 0) || state->materialEffects()->deltaE() == 0)
5227 ) {
5228 const double deltaPhi = state->materialEffects()->deltaPhi();
5229 const double measDeltaPhi = state->materialEffects()->measuredDeltaPhi();
5230 const double sigma2deltaPhi = std::pow(state->materialEffects()->sigmaDeltaPhi(), 2);
5231 const double deltaTheta = state->materialEffects()->deltaTheta();
5232 const double sigma2deltaTheta = std::pow(state->materialEffects()->sigmaDeltaTheta(), 2);
5233
5234 if (trajectory.prefit() != 1) {
5235 b[nperpars + 2 * scatno] -= (deltaPhi - measDeltaPhi) / sigma2deltaPhi;
5236 b[nperpars + 2 * scatno + 1] -= deltaTheta / sigma2deltaTheta;
5237 } else {
5238 b[nperpars + scatno] -= deltaTheta / sigma2deltaTheta;
5239 }
5240
5241 chi2 += (
5242 deltaPhi * deltaPhi / sigma2deltaPhi +
5243 deltaTheta * deltaTheta / sigma2deltaTheta
5244 );
5245
5246 scatno++;
5247 }
5248
5249 /*
5250 * Energy loss will be considered in the form of a kink.
5251 */
5252 if ((state->materialEffects() != nullptr) && state->materialEffects()->sigmaDeltaE() > 0) {
5253 double averagenergyloss = std::abs(state->materialEffects()->deltaE());
5254 const double qoverpbrem = limitInversePValue(1000 * states[hitno]->trackParameters()->parameters()[Trk::qOverP]);
5255 const double qoverp = limitInversePValue(qoverpbrem - state->materialEffects()->delta_p());
5256 const double pbrem = 1. / std::abs(qoverpbrem);
5257 const double p = 1. / std::abs(qoverp);
5258 const double mass = .001 * trajectory.mass();
5259 const double energy = std::sqrt(p * p + mass * mass);
5260 const double bremEnergy = std::sqrt(pbrem * pbrem + mass * mass);
5261
5262 const double resMaterial = .001 * averagenergyloss - energy + bremEnergy;
5263 res[nmeas - nbrem + bremno] = resMaterial;
5264
5265 const double sigde = state->materialEffects()->sigmaDeltaE();
5266 const double sigdepos = state->materialEffects()->sigmaDeltaEPos();
5267 const double sigdeneg = state->materialEffects()->sigmaDeltaENeg();
5268
5269 double errorMaterial = .001 * state->materialEffects()->sigmaDeltaE();
5270 error[nmeas - nbrem + bremno] = errorMaterial;
5271
5272 /*
5273 * There is already a kink in the trajectory. No need to look for more.
5274 * - Set the maxbrempull to a small value, so no future candidate can
5275 * be found.
5276 * - Reset the pointer to the state, in case we have set one before.
5277 *
5278 * NOTE: I think, the new value of maxbrempull should be -inf since it
5279 * allows for some edge case pulls. Not sure if bug or feature.
5280 */
5281 if (state->materialEffects()->isKink()) {
5282 maxbrempull = -999999999;
5283 state_maxbrempull = nullptr;
5284 }
5285
5286 if (
5287 cache.m_asymeloss &&
5288 it > 0 &&
5289 trajectory.prefit() == 0 &&
5290 sigde > 0 &&
5291 sigde != sigdepos &&
5292 sigde != sigdeneg
5293 ) {
5294 const double elosspull = resMaterial / errorMaterial;
5295
5296 if (trajectory.mass() > 100) {
5297 /*
5298 * If the absolute energy loss pull is too large, update the
5299 * sigmaDeltaE of the state and also update the error/
5300 */
5301 if (std::abs(elosspull) > 1) {
5302 if (elosspull < -1) {
5303 state->materialEffects()->setSigmaDeltaE(sigdepos);
5304 } else {
5305 state->materialEffects()->setSigmaDeltaE(sigdeneg);
5306 }
5307
5308 errorMaterial = .001 * state->materialEffects()->sigmaDeltaE();
5309 error[nmeas - nbrem + bremno] = errorMaterial;
5310 }
5311 } else if ((trajectory.numberOfTRTHits() == 0) || it >= 3) {
5312 /*
5313 * In case the state is not yet marked as a kink, we might want to
5314 * do so later. For this, we propose a maxbrempull state if either
5315 * - we did not provide an external kink with Gaudi and we want a
5316 * definitely negative elosspull.
5317 * or
5318 * - an external kink is given with Gaudi and we are on it now.
5319 */
5320 if (
5321 !state->materialEffects()->isKink() && (
5322 (m_fixbrem == -1 && elosspull < maxbrempull) ||
5323 (m_fixbrem >= 0 && bremno == m_fixbrem)
5324 )
5325 ) {
5326 bremno_maxbrempull = bremno;
5327 state_maxbrempull = state.get();
5328 maxbrempull = elosspull;
5329 }
5330 }
5331 }
5332
5333 if (
5334 it > 0 &&
5335 hitno >= 2 &&
5336 !m_calotoolparam.empty() &&
5337 trajectory.prefit() == 0 &&
5338 state->materialEffects()->sigmaDeltaPhi() == 0 &&
5339 state->materialEffects()->isMeasuredEloss() &&
5340 resMaterial / (.001 * state->materialEffects()->sigmaDeltaEAve()) > 2.5
5341 ) {
5342 const TrackParameters* parforcalo = states[hitno - 2]->trackParameters();
5343 const IPropagator* prop = &*m_propagator;
5344
5345 std::vector<MaterialEffectsOnTrack> calomeots =
5346 m_calotoolparam->extrapolationSurfacesAndEffects(
5347 *m_navigator->highestVolume(ctx),
5348 *prop,
5349 *parforcalo,
5350 parforcalo->associatedSurface(),
5352 Trk::muon);
5353
5354 /*
5355 * Update energyLoss, sigma, residual, and error if the parametrised
5356 * energy loss results in a absolute smaller pull.
5357 */
5358 if (calomeots.size() == 3) {
5359 averagenergyloss = std::abs(calomeots[1].energyLoss()->deltaE());
5360 const double newres = .001 * averagenergyloss - energy + bremEnergy;
5361 const double newerr = .001 * calomeots[1].energyLoss()->sigmaDeltaE();
5362
5363 const double oldPull = resMaterial / errorMaterial;
5364 const double newPull = newres / newerr;
5365
5366 if (std::abs(newPull) < std::abs(oldPull)) {
5367 ATH_MSG_DEBUG("Changing from measured to parametrized energy loss");
5368
5369 state->materialEffects()->setEloss(std::unique_ptr<EnergyLoss>(calomeots[1].energyLoss()->clone()));
5370 state->materialEffects()->setSigmaDeltaE(calomeots[1].energyLoss()->sigmaDeltaE());
5371 res[nmeas - nbrem + bremno] = newres;
5372 error[nmeas - nbrem + bremno] = newerr;
5373 }
5374 }
5375
5376 state->materialEffects()->setMeasuredEloss(false);
5377 }
5378
5379 bremno++;
5380 }
5381 }
5382
5383 /*
5384 * Sum up the chi2 contributions from all measurements.
5385 */
5386 for (int imeas = 0; imeas < nmeas; imeas++) {
5387 if (error[imeas] == 0) {
5388 continue;
5389 }
5390
5391 chi2 += std::pow(res[imeas] / error[imeas], 2);
5392 }
5393
5394 /*
5395 * Update trajectory with previous and current chi2
5396 */
5397 trajectory.setPrevChi2(trajectory.chi2());
5398 trajectory.setChi2(chi2);
5399 }
5400
5402 const Cache & cache,
5403 GXFTrajectory & trajectory,
5404 const int it
5405 ) const {
5406 ATH_MSG_DEBUG("tryToConverge");
5407
5408 const double oldChi2 = trajectory.prevchi2();
5409 const double newChi2 = trajectory.chi2();
5410
5411 /*
5412 * First convergence check
5413 */
5414 const double nDOF = trajectory.nDOF();
5415 const double oldRedChi2 = (nDOF > 0) ? oldChi2 / nDOF : 0;
5416 const double newRedChi2 = (nDOF > 0) ? newChi2 / nDOF : 0;
5417
5418 if (
5419 trajectory.prefit() > 0 && (
5420 (newRedChi2 < 2 && it != 0) ||
5421 (newRedChi2 < oldRedChi2 + .1 && std::abs(newRedChi2 - oldRedChi2) < 1 && it != 1)
5422 )
5423 ) {
5424 trajectory.setConverged(true);
5425 }
5426
5427 /*
5428 * Second convergence check
5429 */
5430 const int nsihits = trajectory.numberOfSiliconHits();
5431 const int ntrthits = trajectory.numberOfTRTHits();
5432 const int nhits = trajectory.numberOfHits();
5433
5434 int miniter = (nsihits != 0 && nsihits + ntrthits == nhits) ? 1 : 2;
5435 miniter = std::max(miniter, cache.m_miniter);
5436
5437 if (it >= miniter && std::abs(oldChi2 - newChi2) < 1) {
5438 trajectory.setConverged(true);
5439 }
5440 }
5441
5443 GXFTrajectory & trajectory,
5444 const int bremno_maxbrempull,
5445 GXFTrackState* state_maxbrempull,
5447 ) const {
5448 ATH_MSG_DEBUG("updateSystemWithMaxBremPull");
5449
5450 if (state_maxbrempull == nullptr) {
5451 return;
5452 }
5453
5454 state_maxbrempull->materialEffects()->setSigmaDeltaE(
5455 10 * state_maxbrempull->materialEffects()->sigmaDeltaEPos()
5456 );
5457
5458 state_maxbrempull->materialEffects()->setKink(true);
5459
5460 const int nbrem = trajectory.numberOfBrems();
5461 const Amg::VectorX & res = trajectory.residuals();
5462 const int nmeas = (int) res.size();
5463
5464 Amg::VectorX & error = trajectory.errors();
5465 const double oldError = error[nmeas - nbrem + bremno_maxbrempull];
5466 const double newError = .001 * state_maxbrempull->materialEffects()->sigmaDeltaE();
5467 error[nmeas - nbrem + bremno_maxbrempull] = newError;
5468
5469 const int nFitPars = trajectory.numberOfFitParameters();
5470 if (a.cols() != nFitPars) {
5471 ATH_MSG_ERROR("Your assumption is wrong!!!!");
5472 }
5473
5474 const double errorRatio = oldError / newError;
5475 const double errorReductionRatio = 1 - std::pow(errorRatio, 2);
5476
5477 Amg::MatrixX & weightderiv = trajectory.weightedResidualDerivatives();
5478 for (int i = 0; i < nFitPars; i++) {
5479 if (weightderiv(nmeas - nbrem + bremno_maxbrempull, i) == 0) {
5480 continue;
5481 }
5482
5483 for (int j = i; j < nFitPars; j++) {
5484 const double newaij = a(i, j) - errorReductionRatio *
5485 weightderiv(nmeas - nbrem + bremno_maxbrempull, i) *
5486 weightderiv(nmeas - nbrem + bremno_maxbrempull, j);
5487
5488 a.fillSymmetric(i, j, newaij);
5489 }
5490 weightderiv(nmeas - nbrem + bremno_maxbrempull, i) *= errorRatio;
5491 }
5492 }
5493
5495 GXFTrajectory & trajectory
5496 ) const {
5497 ATH_MSG_DEBUG("fillDerivatives");
5498
5499 std::vector<std::unique_ptr<GXFTrackState>> & states = trajectory.trackStates();
5500 int scatno = 0;
5501 int bremno = 0;
5502 int measno = 0;
5503 const int nscatupstream = trajectory.numberOfUpstreamScatterers();
5504 const int nbremupstream = trajectory.numberOfUpstreamBrems();
5505 const int nscat = trajectory.numberOfScatterers();
5506 const int nbrem = trajectory.numberOfBrems();
5507 const int nperparams = trajectory.numberOfPerigeeParameters();
5508
5509 Amg::MatrixX & weightderiv = trajectory.weightedResidualDerivatives();
5510 Amg::VectorX & error = trajectory.errors();
5511
5512 const int nmeas = (int) weightderiv.rows();
5513
5514 for (std::unique_ptr<GXFTrackState> & state : states) {
5515 if (state->getStateType(TrackStateOnSurface::Measurement)) {
5516 TrackState::MeasurementType const hittype = state->measurementType();
5517 const MeasurementBase *measbase = state->measurement();
5518 const auto [scatmin, scatmax] = std::minmax(scatno, nscatupstream);
5519 const auto [bremmin, bremmax] = std::minmax(bremno, nbremupstream);
5520
5521 Amg::MatrixX & derivatives = state->derivatives();
5522
5523 /*
5524 * Get the stereo angles for SCT and TGC.
5525 */
5526 const double sinStereo =
5527 hittype == TrackState::SCT || hittype == TrackState::TGC ?
5528 state->sinStereo() :
5529 0;
5530 const double cosStereo =
5531 sinStereo != 0 ?
5532 std::sqrt(1 - std::pow(sinStereo, 2)) :
5533 1.;
5534
5535 /*
5536 * For SCT and TGC we need modified derivatives, taking into account
5537 * the orientation.This lambda chooses the correct accessor and rotates
5538 * the derivative accordingly.
5539 */
5540 auto getThisDeriv = [sinStereo, cosStereo, &derivatives](int i, int j) -> double {
5541 if (i == 0 && sinStereo != 0) {
5542 return derivatives(0, j) * cosStereo + sinStereo * derivatives(1, j);
5543 } else {
5544 return derivatives(i, j);
5545 }
5546 };
5547
5548 for (int i = 0; i < 5; i++) {
5550 continue;
5551 }
5552
5553 /*
5554 * SCT and TGC have all information stored in the first parameter.
5555 */
5556 if ((hittype == TrackState::SCT || hittype == TrackState::TGC) && i > 0) {
5557 break;
5558 }
5559
5560 if (trajectory.numberOfPerigeeParameters() > 0) {
5561 const int cols = trajectory.m_straightline ? 4 : 5;
5562
5563 if (i == 0 && sinStereo != 0) {
5564 weightderiv.row(measno).head(cols) =
5565 (derivatives.row(0).head(cols) * cosStereo +
5566 sinStereo * derivatives.row(1).head(cols)) /
5567 error[measno];
5568 } else {
5569 weightderiv.row(measno).head(cols) = derivatives.row(i).head(cols) / error[measno];
5570 }
5571 }
5572
5573 for (int j = scatmin; j < scatmax; j++) {
5574 if (trajectory.prefit() == 1) {
5575 const int index = nperparams + j;
5576 weightderiv(measno, index) = getThisDeriv(i, index) / error[measno];
5577 } else {
5578 const int index = nperparams + 2 * j;
5579 weightderiv(measno, index) = getThisDeriv(i, index) / error[measno];
5580 weightderiv(measno, index + 1) = getThisDeriv(i, index + 1) / error[measno];
5581 }
5582 }
5583
5584 for (int j = bremmin; j < bremmax; j++) {
5585 const int index = j + nperparams + 2 * nscat;
5586 weightderiv(measno, index) = getThisDeriv(i, index) / error[measno];
5587 }
5588
5589 measno++;
5590 }
5591 } else if (state->getStateType(TrackStateOnSurface::Outlier)) {
5592 double *errors = state->measurementErrors();
5593 for (int i = 0; i < 5; i++) {
5594 if (errors[i] > 0) {
5595 measno++;
5596 }
5597 }
5598 } else if (
5599 state->getStateType(TrackStateOnSurface::Scatterer) &&
5600 ((trajectory.prefit() == 0) || state->materialEffects()->deltaE() == 0)
5601 ) {
5602 scatno++;
5603 }
5604
5605 if ((state->materialEffects() != nullptr) && state->materialEffects()->sigmaDeltaE() > 0) {
5606 //limit values to avoid FPE overflow or div by zero
5607 const double qoverpbrem = limitInversePValue(1000 * state->trackParameters()->parameters()[Trk::qOverP]);
5608 const double qoverp = limitInversePValue(qoverpbrem - state->materialEffects()->delta_p());
5609
5610 const double mass = .001 * trajectory.mass();
5611
5612 const auto thisMeasurementIdx{nmeas - nbrem + bremno};
5613 //references (courtesy of Christos Anastopoulos):
5614 //https://inspirehep.net/files/a810ad0047a22af32fbff587c6c98ce5
5615 //https://its.cern.ch/jira/browse/ATLASRECTS-6213
5616 auto multiplier = [] (double mass, double qOverP){
5617 return std::copysign(1./(qOverP * qOverP * std::sqrt(1. + mass * mass * qOverP * qOverP)), qOverP);
5618 };
5619 const auto qoverpTerm {multiplier(mass, qoverp) / error[thisMeasurementIdx]};
5620 const auto qoverpBremTerm {multiplier(mass, qoverpbrem) / error[thisMeasurementIdx]};
5621
5622 if (trajectory.numberOfPerigeeParameters() > 0) {
5623 weightderiv(thisMeasurementIdx, 4) = qoverpBremTerm - qoverpTerm;
5624 }
5625 //
5626 const auto bremNoBase = nperparams + 2 * nscat;
5627 if (bremno < nbremupstream) {
5628 weightderiv(thisMeasurementIdx, bremNoBase + bremno) = qoverpTerm;
5629 for (int bremno2 = bremno + 1; bremno2 < nbremupstream; bremno2++) {
5630 weightderiv(thisMeasurementIdx, bremNoBase + bremno2) = qoverpTerm - qoverpBremTerm;
5631 }
5632 } else {
5633 weightderiv(thisMeasurementIdx, bremNoBase + bremno) = qoverpBremTerm;
5634 for (int bremno2 = nbremupstream; bremno2 < bremno; bremno2++) {
5635 weightderiv(thisMeasurementIdx, bremNoBase + bremno2) = qoverpBremTerm - qoverpTerm;
5636 }
5637 }
5638 bremno++;
5639 }
5640 }
5641 }
5642
5644 Cache & cache,
5645 GXFTrajectory & trajectory
5646 ) {
5647 const int nFitPars = trajectory.numberOfFitParameters();
5648 const int nPerPars = trajectory.numberOfPerigeeParameters();
5649 const int nScatPars = 2 * trajectory.numberOfScatterers();
5650 const int nBrem = trajectory.numberOfBrems();
5651 const int nUpstreamStates = trajectory.numberOfUpstreamStates();
5652
5653 const Amg::VectorX & res = trajectory.residuals();
5654 const int nMeas = (int) res.size();
5655
5656 cache.m_firstmeasurement.resize(nFitPars);
5657 cache.m_lastmeasurement.resize(nFitPars);
5658
5659 for (int i = 0; i < nPerPars; i++) {
5660 cache.m_firstmeasurement[i] = 0;
5661 cache.m_lastmeasurement[i] = nMeas - nBrem;
5662 }
5663
5664 int measno = 0;
5665 int scatno = 0;
5666 int bremno = 0;
5667 for (int i = 0; i < (int) trajectory.trackStates().size(); i++) {
5668 const std::unique_ptr<GXFTrackState> & state = trajectory.trackStates()[i];
5669 const GXFMaterialEffects *meff = state->materialEffects();
5670
5671 if (meff == nullptr) {
5672 measno += state->numberOfMeasuredParameters();
5673 continue;
5674 }
5675
5676 const int firstMeasurement = i < nUpstreamStates ? 0 : measno;
5677 const int lastMeasurement = i < nUpstreamStates ? measno : nMeas - nBrem;
5678
5679 if (meff->sigmaDeltaTheta() != 0
5680 && (trajectory.prefit() == 0 || meff->deltaE() == 0)) {
5681 const int scatterPos = nPerPars + 2 * scatno;
5682
5683 cache.m_firstmeasurement[scatterPos] = firstMeasurement;
5684 cache.m_lastmeasurement[scatterPos] = lastMeasurement;
5685
5686 cache.m_firstmeasurement[scatterPos + 1] = firstMeasurement;
5687 cache.m_lastmeasurement[scatterPos + 1] = lastMeasurement;
5688
5689 scatno++;
5690 }
5691
5692 if (meff->sigmaDeltaE() > 0) {
5693 const int bremPos = nPerPars + nScatPars + bremno;
5694
5695 cache.m_firstmeasurement[bremPos] = firstMeasurement;
5696 cache.m_lastmeasurement[bremPos] = lastMeasurement;
5697
5698 bremno++;
5699 }
5700 }
5701 }
5702
5704 const Cache & cache,
5705 GXFTrajectory & trajectory,
5706 Amg::VectorX & b
5707 ) {
5708 const int nFitPars = trajectory.numberOfFitParameters();
5709 const int nPerPars = trajectory.numberOfPerigeeParameters();
5710 const int nScatPars = 2 * trajectory.numberOfScatterers();
5711 const int nBrem = trajectory.numberOfBrems();
5712 const Amg::MatrixX & weightDeriv = trajectory.weightedResidualDerivatives();
5713
5714 const Amg::VectorX & res = trajectory.residuals();
5715 const Amg::VectorX & error = trajectory.errors();
5716
5717 const int nMeas = (int) res.size();
5718
5719 for (int k = 0; k < nFitPars; k++) {
5720 const int minMeasK = cache.m_firstmeasurement[k];
5721 const int maxMeasK = cache.m_lastmeasurement[k];
5722
5723 /*
5724 * NOTE: It is necessary to do r * invError * weight instead of doing
5725 * r / error * w. Otherwise, the implementation tests fail do to
5726 * numerical reasons.
5727 */
5728 for (int measno = minMeasK; measno < maxMeasK; measno++) {
5729 b[k] += res[measno] * (1. / error[measno]) * weightDeriv(measno, k);
5730 }
5731
5732 /*
5733 * For qOverP and brems, we also have a contribution to brems elements.
5734 *
5735 * NOTE: It is necessary to do r * invError * weight instead of doing
5736 * r / error * w. Otherwise, the implementation tests fail do to
5737 * numerical reasons.
5738 */
5739 if (k == 4 || k >= nPerPars + nScatPars) {
5740 for (int measno = nMeas - nBrem; measno < nMeas; measno++) {
5741 b[k] += res[measno] * (1. / error[measno]) * weightDeriv(measno, k);
5742 }
5743 }
5744 }
5745 }
5746
5748 const Cache & cache,
5749 GXFTrajectory & trajectory,
5751 ) {
5752 const int nFitPars = trajectory.numberOfFitParameters();
5753 const Amg::MatrixX & weightDeriv = trajectory.weightedResidualDerivatives();
5754
5755 for (int k = 0; k < nFitPars; k++) {
5756 for (int l = k; l < nFitPars; l++) {
5757 const int minMeas = std::max(cache.m_firstmeasurement[k], cache.m_firstmeasurement[l]);
5758 const int maxMeas = std::min(cache.m_lastmeasurement[k], cache.m_lastmeasurement[l]);
5759
5760 double a_kl = 0;
5761 for (int measno = minMeas; measno < maxMeas; measno++) {
5762 a_kl += weightDeriv(measno, k) * weightDeriv(measno, l);
5763 }
5764
5765 a.fillSymmetric(l, k, a_kl);
5766 }
5767 }
5768 }
5769
5771 GXFTrajectory & trajectory,
5773 ) {
5774 const int nFitPars = trajectory.numberOfFitParameters();
5775 const int nPerPars = trajectory.numberOfPerigeeParameters();
5776 const int nScatPars = 2 * trajectory.numberOfScatterers();
5777 const int nBrem = trajectory.numberOfBrems();
5778 const Amg::MatrixX & weightDeriv = trajectory.weightedResidualDerivatives();
5779
5780 const Amg::VectorX & res = trajectory.residuals();
5781 const auto & scatSigmas = trajectory.scatteringSigmas();
5782
5783 const int nMeas = (int) res.size();
5784
5785 int scatno = 0;
5786
5787 /*
5788 * Direct contribution on the diagonal from the scatterer itself.
5789 */
5790 for (int k = nPerPars; k < nPerPars + nScatPars; k += 2) {
5791 a(k, k) += 1. / std::pow(scatSigmas[scatno].first, 2);
5792 a(k + 1, k + 1) += 1. / std::pow(scatSigmas[scatno].second, 2);
5793
5794 scatno++;
5795 }
5796
5797 /*
5798 * Indirect contribution on the qOverP and brems derivatives.
5799 */
5800 for (int measno = nMeas - nBrem; measno < nMeas; measno++) {
5801 for (int k = 4; k < nFitPars; k++) {
5802 if (k == 5) {
5803 k = nPerPars + nScatPars;
5804 }
5805
5806 for (int l = k; l < nFitPars; l++) {
5807 if (l == 5) {
5808 l = nPerPars + nScatPars;
5809 }
5810
5811 const double a_kl = a(l, k) + weightDeriv(measno, k) * weightDeriv(measno, l);
5812 a.fillSymmetric(l, k, a_kl);
5813 }
5814 }
5815 }
5816 }
5817
5819 Cache & cache,
5820 GXFTrajectory & trajectory,
5822 const bool doDeriv,
5823 const int it,
5824 const double oldRedChi2,
5825 const double newRedChi2
5826 ) {
5827 const int nPerPars = trajectory.numberOfPerigeeParameters();
5828
5829 /*
5830 * The return value collects, if any weights changed while looping over all
5831 * material states.
5832 */
5833 bool weightChanged = false;
5834
5835 /*
5836 * The weights for the diagonal material components in the [a]-matrix
5837 * depend on how far we are in the iteration process (iteration number or
5838 * chi2 convergence).
5839 */
5840 double newPhiWeight = 1.1;
5841 double newThetaWeight = 1.001;
5842 if (trajectory.prefit() == 0) {
5843 /*
5844 * We do not consider theta at all in the prefit 0 case. Therefore, we do
5845 * not need to adjust the theta weights.
5846 */
5847 if (it == 0) {
5848 newPhiWeight = 1.00000001;
5849 } else if (it == 1) {
5850 newPhiWeight = 1.0000001;
5851 } else if (it <= 3) {
5852 newPhiWeight = 1.0001;
5853 } else if (it <= 6) {
5854 newPhiWeight = 1.01;
5855 }
5856 } else {
5857 if (newRedChi2 > oldRedChi2 - 1 && newRedChi2 < oldRedChi2) {
5858 newPhiWeight = 1.0001;
5859 newThetaWeight = 1.0001;
5860 } else if (newRedChi2 > oldRedChi2 - 25 && newRedChi2 < oldRedChi2) {
5861 newPhiWeight = 1.001;
5862 newThetaWeight = 1.0001;
5863 }
5864 }
5865
5866 /*
5867 * Counter for the scattering states. We cannot directly loop over them.
5868 */
5869 std::size_t scatno = 0;
5870
5871 /*
5872 * Loop over all track states. Skip states without material effects.
5873 */
5874 for (const auto & state : trajectory.trackStates()) {
5875 const GXFMaterialEffects *meff = state->materialEffects();
5876
5877 if (meff == nullptr) {
5878 continue;
5879 }
5880
5881 const bool isValidPlaneSurface =
5883 static_cast<const PlaneSurface *>(&state->associatedSurface()) != nullptr;
5884
5885 /*
5886 * Modify the diagonal material elements in the [a]-matrix.
5887 */
5888 if (meff->deltaE() == 0 || (trajectory.prefit() == 0 && isValidPlaneSurface)) {
5889 weightChanged = true;
5890
5891 const int scatNoIndex = 2 * scatno + nPerPars;
5892
5893 if (trajectory.prefit() == 0 && meff->sigmaDeltaPhi() != 0) {
5894 if (scatno >= cache.m_phiweight.size()) {
5895 std::stringstream message;
5896 message << "scatno is out of range " << scatno << " !< " << cache.m_phiweight.size();
5897 throw std::range_error(message.str());
5898 }
5899
5900 /*
5901 * In case, no derivative is necessary, the weight will be
5902 * effectively replaced by the relative weight change
5903 */
5904 if (!doDeriv) {
5905 a(scatNoIndex, scatNoIndex) /= cache.m_phiweight[scatno];
5906 }
5907
5908 cache.m_phiweight[scatno] = newPhiWeight;
5909 a(scatNoIndex, scatNoIndex) *= newPhiWeight;
5910 } else if (trajectory.prefit() >= 2) {
5911 a(scatNoIndex, scatNoIndex) *= newPhiWeight;
5912 a(scatNoIndex + 1, scatNoIndex + 1) *= newThetaWeight;
5913 }
5914 }
5915
5916 /*
5917 * The state is a valid scatterer even, if not considered in the
5918 * modification of the weights before. Therefore increment the count.
5919 *
5920 * NOTE: It is not clear, why this check is not at the beginning of the
5921 * loop. This way, a mismatch in the state counting could happen.
5922 */
5923 if (
5924 meff->sigmaDeltaPhi() != 0 &&
5925 (trajectory.prefit() == 0 || meff->deltaE() == 0)
5926 ) {
5927 scatno++;
5928 }
5929 }
5930
5931 /*
5932 * Add a weight to the qOverP component of the [a]-matrix if a set of
5933 * pre-conditions are met and the reduced chi2 either
5934 * - converges very fast (e.g. at the beginning of the fit)
5935 * OR
5936 * - gets larger (e.g. moving away from minimum or overshooting by a lot)
5937 */
5938 if (
5939 trajectory.prefit() == 2 &&
5940 doDeriv &&
5941 trajectory.numberOfBrems() > 0 &&
5942 (newRedChi2 < oldRedChi2 - 25 || newRedChi2 > oldRedChi2)
5943 ) {
5944 a(4, 4) *= 1.001;
5945 }
5946
5947 return weightChanged;
5948 }
5949
5951 Cache & cache,
5952 GXFTrajectory & trajectory,
5954 ) {
5955 const int nPerPars = trajectory.numberOfPerigeeParameters();
5956 std::size_t scatno = 0;
5957
5958 for (auto & state : trajectory.trackStates()) {
5959 const GXFMaterialEffects *meff = state->materialEffects();
5960
5961 if (meff == nullptr || meff->sigmaDeltaPhi() == 0) {
5962 continue;
5963 }
5964
5965 if (scatno >= cache.m_phiweight.size()) {
5966 std::stringstream message;
5967 message << "scatno is out of range " << scatno << " !< " << cache.m_phiweight.size();
5968 throw std::range_error(message.str());
5969 }
5970
5971 const bool isValidPlaneSurface =
5973 static_cast<const PlaneSurface *>(&state->associatedSurface()) != nullptr;
5974
5975 if (meff->deltaE() == 0 || isValidPlaneSurface) {
5976 const int scatNoIndex = 2 * scatno + nPerPars;
5977 a(scatNoIndex, scatNoIndex) /= cache.m_phiweight[scatno];
5978 cache.m_phiweight[scatno] = 1;
5979 }
5980
5981 /*
5982 * NOTE: We already check for this in the beginning of the loop. Is
5983 * there any way, this can change?
5984 */
5985 if (meff->sigmaDeltaPhi() != 0) {
5986 scatno++;
5987 }
5988 }
5989 }
5990
5992 const EventContext& ctx,
5993 Cache & cache,
5994 GXFTrajectory & trajectory,
5995 const int it,
5997 Amg::VectorX & b,
5998 Amg::SymMatrixX & lu,
5999 bool & doDeriv
6000 ) const {
6001 const int nDOFold = trajectory.nDOF();
6002 const double oldChi2 = trajectory.chi2();
6003 const double oldRedChi2 = nDOFold > 0 ? oldChi2 / nDOFold : 0;
6004
6005 if (cache.m_phiweight.empty()) {
6006 cache.m_phiweight.assign(trajectory.trackStates().size(), 1);
6007 }
6008
6009 FitterStatusCode fsc = calculateTrackParameters(ctx, trajectory, doDeriv);
6010
6011 if (fsc != FitterStatusCode::Success) {
6012 return fsc;
6013 }
6014
6015 /*
6016 * Reset the b-vector. We want to add to the components later.
6017 */
6018 b.setZero();
6019
6020 /*
6021 * Here we store the information on where to find the maxbrempull, in case
6022 * we find any large ones during the residual calculation. We might need it
6023 * later to update our errors.
6024 */
6025 int bremno_maxbrempull = 0;
6026 GXFTrackState* state_maxbrempull = nullptr;
6027
6028 fillResidualsAndErrors(ctx, cache, trajectory, it, b, bremno_maxbrempull, state_maxbrempull);
6029
6030 /*
6031 * Check if we hit any convergence conditions.
6032 */
6033 tryToConverge(cache, trajectory, it);
6034
6035 /*
6036 * In case we converged but have a state with maxbrempull (a kink) we want
6037 * to do more iterations. Therefore, reset the convergence flag and inflate
6038 * the chi2. Then update the error estimates using the state with the
6039 * maxbrempull.
6040 */
6041 if ((state_maxbrempull != nullptr) && trajectory.converged()) {
6042 trajectory.setConverged(false);
6043 trajectory.setChi2(1e15);
6044 doDeriv = true;
6045
6046 updateSystemWithMaxBremPull(trajectory, bremno_maxbrempull, state_maxbrempull, a);
6047 lu = a;
6048 }
6049
6050 const int nDOFnew = trajectory.nDOF();
6051 const double newChi2 = trajectory.chi2();
6052 const double newRedChi2 = nDOFnew > 0 ? newChi2 / nDOFnew : 0;
6053
6054 ATH_MSG_DEBUG("old chi2: " << oldChi2 << "/" << nDOFold << "=" << oldRedChi2 <<
6055 ", new chi2: " << newChi2 << "/" << nDOFnew << "=" << newRedChi2);
6056
6057 if (trajectory.prefit() > 0 && trajectory.converged()) {
6059 }
6060
6061 if (doDeriv) {
6062 calculateDerivatives(trajectory);
6063 fillDerivatives(trajectory);
6064 }
6065
6066 if (cache.m_firstmeasurement.empty()) {
6067 fillFirstLastMeasurement(cache, trajectory);
6068 }
6069
6070 if (a.cols() != trajectory.numberOfFitParameters()) {
6071 ATH_MSG_ERROR("Your assumption is wrong!!!!");
6072 }
6073
6074 fillBfromMeasurements(cache, trajectory, b);
6075
6076 /*
6077 * The [a]-matrix does not depend on the residuals. We only need to change
6078 * it, if the derivatives have changed.
6079 */
6080 if (doDeriv) {
6081 fillAfromMeasurements(cache, trajectory, a);
6082 fillAfromScatterers(trajectory, a);
6083 }
6084
6085 const bool weightChanged = tryToWeightAfromMaterial(cache, trajectory, a, doDeriv, it, oldRedChi2, newRedChi2);
6086
6087 /*
6088 * Update the [lu]-matrix if we modified the [a]-matrix.
6089 */
6090 if (doDeriv || weightChanged) {
6091 lu = a;
6092 }
6093
6094 /*
6095 * Special handling for prefit == 0:
6096 * - If we already converged, but there are hits apart from Si and TRT or
6097 * the numbers don't match, the applied phi weights need to be reset.
6098 * - If we got in an early iteration to a low reduced chi2 or converged
6099 * with the reduced chi2, we don't need to redo derivatives.
6100 */
6101 if (trajectory.prefit() == 0) {
6102 if (trajectory.converged()) {
6103 const int nSiHits = trajectory.numberOfSiliconHits();
6104 const int nTrtHits = trajectory.numberOfTRTHits();
6105 const int nHits = trajectory.numberOfHits();
6106
6107 if (nSiHits + nTrtHits != nHits) {
6108 compensatePhiWeights(cache, trajectory, a);
6109 lu = a;
6110 }
6111 } else if (
6112 !m_redoderivs &&
6113 it < 5 &&
6114 (newRedChi2 < 2 || (newRedChi2 < oldRedChi2 && newRedChi2 > oldRedChi2 - .5))
6115 ) {
6116 doDeriv = false;
6117 }
6118 }
6119
6121 }
6122
6124 GXFTrajectory & trajectory,
6125 const Amg::VectorX & b,
6126 const Amg::SymMatrixX & lu_m
6127 ) const {
6128 ATH_MSG_DEBUG("UpdateFitParameters");
6129
6130 /*
6131 * Compute the parameter update from [llt] * deltaParameters = b.
6132 * In case we cannot do a Cholesky decomposition, we do not update and
6133 * use an early return.
6134 * TODO: Investigate, if it is really Success, if we do not update.
6135 */
6136 Eigen::LLT<Eigen::MatrixXd> const llt(lu_m);
6137
6138 if (llt.info() != Eigen::Success) {
6140 }
6141
6142 const Amg::VectorX deltaParameters = llt.solve(b);
6143
6144 /*
6145 * Collect the number of each parameter type for the offsets in the
6146 * deltaParameters vector.
6147 */
6148 const int nscat = trajectory.numberOfScatterers();
6149 const int nbrem = trajectory.numberOfBrems();
6150 const int nperparams = trajectory.numberOfPerigeeParameters();
6151
6152 /*
6153 * Update the perigee parameters.
6154 * The parameters are not modified in place. In case the angles are pushed
6155 * too far and cannot be corrected anymore, the parameters should not be
6156 * updated and the fit should fail.
6157 *
6158 * NOTE: It is not clear if the fit should fail for fitter reasons or
6159 * because the angle correction is not stable enough.
6160 */
6161 const TrackParameters *refpar = trajectory.referenceParameters();
6162 double d0 = refpar->parameters()[Trk::d0];
6163 double z0 = refpar->parameters()[Trk::z0];
6164 double phi = refpar->parameters()[Trk::phi0];
6165 double theta = refpar->parameters()[Trk::theta];
6166 double qoverp = refpar->parameters()[Trk::qOverP];
6167
6168 if (nperparams > 0) {
6169 d0 += deltaParameters[0];
6170 z0 += deltaParameters[1];
6171 phi += deltaParameters[2];
6172 theta += deltaParameters[3];
6173 qoverp = (trajectory.m_straightline) ? 0 : .001 * deltaParameters[4] + qoverp;
6174 }
6175
6176 if (!correctAngles(phi, theta)) {
6177 ATH_MSG_DEBUG("angles out of range: " << theta << " " << phi);
6178 ATH_MSG_DEBUG("Fit failed");
6180 }
6181
6182 /*
6183 * Update the scattering angles.
6184 */
6185 std::vector < std::pair < double, double >>&scatangles = trajectory.scatteringAngles();
6186 for (int i = 0; i < nscat; i++) {
6187 scatangles[i].first += deltaParameters[2 * i + nperparams];
6188 scatangles[i].second += deltaParameters[2 * i + nperparams + 1];
6189 }
6190
6191 /*
6192 * Update the brems.
6193 */
6194 std::vector < double >&delta_ps = trajectory.brems();
6195 for (int i = 0; i < nbrem; i++) {
6196 delta_ps[i] += deltaParameters[nperparams + 2 * nscat + i];
6197 }
6198
6199 /*
6200 * Create new peregee parameters from the updated ones.
6201 */
6202 std::unique_ptr<const TrackParameters> newper(
6204 d0, z0, phi, theta, qoverp, std::nullopt
6205 )
6206 );
6207
6208 /*
6209 * Apply all changes.
6210 */
6211 trajectory.setReferenceParameters(std::move(newper));
6212 trajectory.setScatteringAngles(scatangles);
6213 trajectory.setBrems(delta_ps);
6214
6216 }
6217
6219 GXFTrajectory & trajectory,
6221 Amg::VectorX & b,
6222 const EventContext& evtctx
6223 ) const{
6224 if ( trajectory.numberOfSiliconHits() == 0) {
6225 return;
6226 }
6227
6228 if ( m_clusterSplitProbContainer.empty() ){
6229 return;
6230 }
6231
6233 if (!splitProbContainer.isValid()) {
6234 ATH_MSG_FATAL("Failed to get cluster splitting probability container " << m_clusterSplitProbContainer);
6235 }
6236
6237 std::vector<std::unique_ptr<GXFTrackState>> & states = trajectory.trackStates();
6238 Amg::VectorX & res = trajectory.residuals();
6239 Amg::VectorX & err = trajectory.errors();
6240 Amg::MatrixX & weightderiv = trajectory.weightedResidualDerivatives();
6241 const int nfitpars = trajectory.numberOfFitParameters();
6242
6243 int measno = 0;
6244 for (size_t stateno = 0; stateno < states.size(); stateno++) {
6245
6246 // Increment the measurement counter everytime we have crossed a measurement/outlier surface
6247 if ( stateno > 0 && ( states[stateno-1]->getStateType(TrackStateOnSurface::Measurement) ||
6248 states[stateno-1]->getStateType(TrackStateOnSurface::Outlier) ) ) {
6249 measno += states[stateno-1]->numberOfMeasuredParameters();
6250 }
6251
6252 std::unique_ptr<GXFTrackState> & state = states[stateno];
6253 if (!state->getStateType(TrackStateOnSurface::Measurement)) {
6254 continue;
6255 }
6256
6257 TrackState::MeasurementType const hittype = state->measurementType();
6258 if (hittype != TrackState::Pixel) {
6259 continue;
6260 }
6261
6262 const PrepRawData *prd{};
6263 if (const auto *const pMeas = state->measurement(); pMeas->type(Trk::MeasurementBaseType::RIO_OnTrack)){
6264 const auto *const rot = static_cast<const RIO_OnTrack *>(pMeas);
6265 prd = rot->prepRawData();
6266 }
6267
6268 if(!prd)
6269 continue;
6270
6272 continue;
6273 }
6274 const InDet::PixelCluster* pixelCluster = static_cast<const InDet::PixelCluster*> ( prd );
6275 const auto &splitProb = splitProbContainer->splitProbability(pixelCluster);
6276 if (!splitProb.isSplit()) {
6277 ATH_MSG_DEBUG( "Pixel cluster is not split so no need to update" );
6278 continue;
6279 }
6280
6281 std::unique_ptr < const RIO_OnTrack > newrot;
6282 double *olderror = state->measurementErrors();
6283 const TrackParameters *trackpars = state->trackParameters();
6284
6285 double newerror[5] = {-1,-1,-1,-1,-1};
6286 double newres[2] = {-1,-1};
6287
6288 newrot.reset(m_ROTcreator->correct(*prd, *trackpars, evtctx));
6289
6290 if(!newrot)
6291 continue;
6292
6293 const Amg::MatrixX & covmat = newrot->localCovariance();
6294
6295 newerror[0] = std::sqrt(covmat(0, 0));
6296 newres[0] = newrot->localParameters()[Trk::locX] - trackpars->parameters()[Trk::locX];
6297 newerror[1] = std::sqrt(covmat(1, 1));
6298 newres[1] = newrot->localParameters()[Trk::locY] - trackpars->parameters()[Trk::locY];
6299
6300 if (a.cols() != nfitpars) {
6301 ATH_MSG_ERROR("Your assumption is wrong!!!!");
6302 }
6303
6304 //loop over both measurements -- treated as uncorrelated
6305 for( int k =0; k<2; k++ ){
6306 const double oldres = res[measno+k];
6307 res[measno+k] = newres[k];
6308 err[measno+k] = newerror[k];
6309
6310 for (int i = 0; i < nfitpars; i++) {
6311 if (weightderiv(measno+k, i) == 0) {
6312 continue;
6313 }
6314
6315 b[i] -= weightderiv(measno+k, i) * (oldres / olderror[k] - (newres[k] * olderror[k]) / (newerror[k] * newerror[k]));
6316
6317 for (int j = i; j < nfitpars; j++) {
6318 a.fillSymmetric(
6319 i, j,
6320 a(i, j) + (
6321 weightderiv(measno+k, i) *
6322 weightderiv(measno+k, j) *
6323 ((olderror[k] * olderror[k]) / (newerror[k] * newerror[k]) - 1)
6324 )
6325 );
6326 }
6327 weightderiv(measno+k, i) *= olderror[k] / newerror[k];
6328 }
6329 }
6330
6331 state->setMeasurement(std::move(newrot));
6332 state->setMeasurementErrors(newerror);
6333
6334 }// end for
6335 }
6336
6337
6339 Cache & cache,
6340 GXFTrajectory & trajectory,
6342 Amg::VectorX & b,
6343 Amg::SymMatrixX & lu_m,
6344 bool runOutlier,
6345 bool trtrecal,
6346 int it,
6347 const EventContext& ctx
6348 ) const {
6349 double scalefactor = m_scalefactor;
6350
6351 if (it == 1 && trajectory.numberOfSiliconHits() + trajectory.numberOfTRTHits() == trajectory.numberOfHits()) {
6352 scalefactor *= 2;
6353 }
6354
6355 std::vector<std::unique_ptr<GXFTrackState>> & states = trajectory.trackStates();
6356 Amg::VectorX & res = trajectory.residuals();
6357 Amg::VectorX & err = trajectory.errors();
6358 Amg::MatrixX & weightderiv = trajectory.weightedResidualDerivatives();
6359 const int nfitpars = trajectory.numberOfFitParameters();
6360
6361 if (a.cols() != nfitpars) {
6362 ATH_MSG_ERROR("Your assumption is wrong!!!!");
6363 }
6364
6365 const int nperpars = trajectory.numberOfPerigeeParameters();
6366 const int nscats = trajectory.numberOfScatterers();
6367 int hitno = 0;
6368 int measno = 0;
6369 bool outlierremoved = false;
6370 bool hitrecalibrated = false;
6371
6372 for (int stateno = 0; stateno < (int) states.size(); stateno++) {
6373 std::unique_ptr<GXFTrackState> & state = states[stateno];
6374
6375 if (state->getStateType(TrackStateOnSurface::Measurement)) { // Hit is not (yet) an outlier
6376 TrackState::MeasurementType const hittype = state->measurementType();
6377
6378 if (hittype == TrackState::TRT) {
6379 if (
6380 runOutlier &&
6381 std::abs(state->trackParameters()->parameters()[Trk::driftRadius]) > 1.05 * state->associatedSurface().bounds().r()
6382 ) {
6383 ATH_MSG_DEBUG("Removing TRT hit #" << hitno);
6384
6385 trajectory.setOutlier(stateno);
6386 outlierremoved = true;
6387
6388 double *errors = state->measurementErrors();
6389 const double olderror = errors[0];
6390
6391 trajectory.updateTRTHitCount(stateno, olderror);
6392
6393 for (int i = 0; i < nfitpars; i++) {
6394 if (weightderiv(measno, i) == 0) {
6395 continue;
6396 }
6397
6398 b[i] -= res[measno] * weightderiv(measno, i) / olderror;
6399
6400 for (int j = i; j < nfitpars; j++) {
6401 a.fillSymmetric(
6402 i, j,
6403 a(i, j) - weightderiv(measno, i) * weightderiv(measno, j)
6404 );
6405 }
6406 weightderiv(measno, i) = 0;
6407 }
6408
6409 res[measno] = 0;
6410 } else if (trtrecal) {
6411 double *errors = state->measurementErrors();
6412 const double olderror = errors[0];
6413 const Trk::RIO_OnTrack * oldrot{};
6414 const auto *const thisMeasurement{state->measurement()};
6415 if ( not thisMeasurement->type(Trk::MeasurementBaseType::RIO_OnTrack)){
6416 continue;
6417 }
6418 oldrot = static_cast<const Trk::RIO_OnTrack *>(thisMeasurement);
6419 const double oldradius = oldrot->localParameters()[Trk::driftRadius];
6420 if (oldrot->prepRawData() != nullptr) {
6421 const double dcradius = oldrot->prepRawData()->localPosition()[Trk::driftRadius];
6422 const double dcerror = std::sqrt(oldrot->prepRawData()->localCovariance()(Trk::driftRadius, Trk::driftRadius));
6423 const double trackradius = state->trackParameters()->parameters()[Trk::driftRadius];
6424
6425 std::unique_ptr<const Trk::RIO_OnTrack> newrot = nullptr;
6426 const double distance = std::abs(std::abs(trackradius) - dcradius);
6427
6428 if (distance < scalefactor * dcerror && (olderror > 1. || trackradius * oldradius < 0)) {
6429 newrot.reset(m_ROTcreator->correct(*oldrot->prepRawData(), *state->trackParameters(), ctx));
6430 } else if (distance > scalefactor * dcerror && olderror < 1.) {
6431 newrot.reset(m_broadROTcreator->correct(*oldrot->prepRawData(), *state->trackParameters(), ctx));
6432 }
6433
6434 if (newrot != nullptr) {
6435 ATH_MSG_DEBUG("Recalibrating TRT hit #" << hitno);
6436 hitrecalibrated = true;
6437 const double newradius = newrot->localParameters()[Trk::driftRadius];
6438 const double newerror = std::sqrt(newrot->localCovariance()(Trk::driftRadius, Trk::driftRadius));
6439
6440 if ((measno < 0) or (measno >= (int) res.size())) {
6441 throw std::runtime_error(
6442 "'res' array index out of range in TrkGlobalChi2Fitter/src/GlobalChi2Fitter.cxx:" + std::to_string(__LINE__)
6443 );
6444 }
6445
6446 const double oldres = res[measno];
6447 const double newres = newradius - state->trackParameters()->parameters()[Trk::driftRadius];
6448 errors[0] = newerror;
6449 state->setMeasurement(std::move(newrot));
6450
6451 trajectory.updateTRTHitCount(stateno, olderror);
6452
6453 for (int i = 0; i < nfitpars; i++) {
6454 if (weightderiv(measno, i) == 0) {
6455 continue;
6456 }
6457
6458 b[i] -= weightderiv(measno, i) * (oldres / olderror - (newres * olderror) / (newerror * newerror));
6459
6460 for (int j = i; j < nfitpars; j++) {
6461 double weight = 1;
6462
6463 if (
6464 !cache.m_phiweight.empty() &&
6465 i == j &&
6466 i >= nperpars &&
6467 i < nperpars + 2 * nscats &&
6468 (i - nperpars) % 2 == 0
6469 ) {
6470 weight = cache.m_phiweight[(i - nperpars) / 2];
6471 }
6472
6473 a.fillSymmetric(
6474 i, j,
6475 a(i, j) + weightderiv(measno, i) * weightderiv(measno, j) * ((olderror * olderror) / (newerror * newerror) - 1) * weight
6476 );
6477 }
6478 weightderiv(measno, i) *= olderror / newerror;
6479 }
6480
6481 res[measno] = newres;
6482 err[measno] = newerror;
6483 }
6484 }
6485 }
6486 }
6487 }
6488
6489 if (state->getStateType(TrackStateOnSurface::Measurement) || state->getStateType(TrackStateOnSurface::Outlier)) {
6490 hitno++;
6491 measno += state->numberOfMeasuredParameters();
6492 }
6493 }
6494
6495 if (trajectory.nDOF() < 0) {
6498 }
6499
6500 if (outlierremoved || hitrecalibrated) {
6501 lu_m = a;
6502 trajectory.setConverged(false);
6503
6504 cache.m_miniter = it + 2;
6505 }
6506 }
6507
6509 const EventContext& ctx,
6510 Cache & cache,
6511 GXFTrajectory &trajectory,
6513 Amg::SymMatrixX &fullcov,
6514 Amg::VectorX & b,
6515 bool runoutlier
6516 ) const {
6517 bool trackok = false;
6518 GXFTrajectory *oldtrajectory = &trajectory;
6519 std::unique_ptr < GXFTrajectory > cleanup_oldtrajectory;
6520 GXFTrajectory *newtrajectory = nullptr;
6521 std::unique_ptr < GXFTrajectory > cleanup_newtrajectory;
6522
6523 // the oldtrajectory will be returned, so in case newtrajectory==oldtrajectory
6524 // the cleanup_newtrajectory == NULL and cleanup_oldtrajectory = oldtrajectory, otherwise
6525 // cleanup_newtrajectory will destroy the object oldtrajectory is pointing to.
6526
6527 while (!trackok && oldtrajectory->nDOF() > 0) {
6528 trackok = true;
6529 std::vector<std::unique_ptr<GXFTrackState>> & states = oldtrajectory->trackStates();
6530 Amg::VectorX & res = oldtrajectory->residuals();
6531 Amg::VectorX & err = oldtrajectory->errors();
6532 Amg::MatrixX & weightderiv = oldtrajectory->weightedResidualDerivatives();
6533 const int nfitpars = oldtrajectory->numberOfFitParameters();
6534 const int nhits = oldtrajectory->numberOfHits();
6535 const int nsihits = oldtrajectory->numberOfSiliconHits();
6536
6537 if (nhits != nsihits) {
6538 return &trajectory;
6539 }
6540
6541 double maxsipull = -1;
6542 int hitno = 0;
6543 int hitno_maxsipull = -1;
6544 int measno_maxsipull = -1;
6545 int stateno_maxsipull = 0;
6546 GXFTrackState *state_maxsipull = nullptr;
6547 int measno = 0;
6548 int n3sigma = 0;
6549 const double cut = m_outlcut;
6550 double cut2 = m_outlcut - 1.;
6551 const int noutl = oldtrajectory->numberOfOutliers();
6552
6553 if (noutl > 0) {
6554 cut2 = cut - 1.25;
6555 }
6556
6557 for (int stateno = 0; stateno < (int) states.size(); stateno++) {
6558 std::unique_ptr<GXFTrackState> & state = states[stateno];
6559
6560 if (state->getStateType(TrackStateOnSurface::Measurement)) {
6561 TrackState::MeasurementType const hittype = state->measurementType();
6562
6563 if ((hittype == TrackState::Pixel || hittype == TrackState::SCT) && state->hasTrackCovariance()) {
6564 double *errors = state->measurementErrors();
6565 AmgSymMatrix(5) & trackcov = state->trackCovariance();
6566 const Amg::MatrixX & hitcov = state->measurement()->localCovariance();
6567 const double sinstereo = state->sinStereo();
6568 const double cosstereo = (sinstereo == 0) ? 1 : std::sqrt(1 - sinstereo * sinstereo);
6569 double weight1 = -1;
6570
6571 if (hitcov(0, 0) > trackcov(0, 0)) {
6572 if (sinstereo == 0) {
6573 weight1 = errors[0] * errors[0] - trackcov(0, 0);
6574 } else {
6575 weight1 = errors[0] * errors[0] - (
6576 trackcov(0, 0) * cosstereo * cosstereo + 2 *
6577 trackcov(1, 0) * cosstereo * sinstereo + trackcov(1, 1) * sinstereo * sinstereo
6578 );
6579 }
6580 }
6581
6582 const double weight2 = (
6583 hittype == TrackState::Pixel && hitcov(1, 1) > trackcov(1, 1) ?
6584 errors[1] * errors[1] - trackcov(1, 1) :
6585 -1
6586 );
6587
6588 double sipull1 = weight1 > 0 ? std::abs(res[measno] / std::sqrt(weight1)) : -1;
6589 const double sipull2 = (
6590 hittype == TrackState::Pixel && weight2 > 0 ?
6591 std::abs(res[measno + 1] / std::sqrt(weight2)) :
6592 -1
6593 );
6594 sipull1 = std::max(sipull1, sipull2);
6595
6596 if (sipull1 > maxsipull) {
6597 maxsipull = sipull1;
6598 measno_maxsipull = measno;
6599 state_maxsipull = state.get();
6600 stateno_maxsipull = stateno;
6601 hitno_maxsipull = hitno;
6602 }
6603
6604 if (hittype == TrackState::Pixel && sipull1 > cut2) {
6605 n3sigma++;
6606 }
6607 }
6608 }
6609
6610 if (state->getStateType(TrackStateOnSurface::Measurement) || state->getStateType(TrackStateOnSurface::Outlier)) {
6611 hitno++;
6612 measno += state->numberOfMeasuredParameters();
6613 }
6614 }
6615
6616 const double maxpull = maxsipull;
6617
6618 ATH_MSG_DEBUG(" maxsipull: " << maxsipull << " hitno_maxsipull: " <<
6619 hitno_maxsipull << " n3sigma: " << n3sigma << " cut: " << cut << " cut2: " << cut2);
6620
6621 Amg::SymMatrixX * newap = &a;
6622 Amg::VectorX * newbp = &b;
6623 Amg::SymMatrixX newa(nfitpars, nfitpars);
6624 Amg::VectorX newb(nfitpars);
6625
6626 if (
6627 maxpull > 2 &&
6628 oldtrajectory->chi2() / oldtrajectory->nDOF() > .25 * m_chi2cut
6629 ) {
6630 state_maxsipull = oldtrajectory->trackStates()[stateno_maxsipull].get();
6631 const PrepRawData *prd{};
6632 if (const auto *const pMeas = state_maxsipull->measurement(); pMeas->type(Trk::MeasurementBaseType::RIO_OnTrack)){
6633 const auto *const rot = static_cast<const RIO_OnTrack *>(pMeas);
6634 prd = rot->prepRawData();
6635 }
6636 std::unique_ptr < const RIO_OnTrack > broadrot;
6637 double *olderror = state_maxsipull->measurementErrors();
6638 TrackState::MeasurementType const hittype_maxsipull = state_maxsipull->measurementType();
6639 const TrackParameters *trackpar_maxsipull = state_maxsipull->trackParameters();
6640
6641 Amg::VectorX parameterVector = trackpar_maxsipull->parameters();
6642 const std::unique_ptr<const TrackParameters> trackparForCorrect(
6643 trackpar_maxsipull->associatedSurface().createUniqueTrackParameters(
6644 parameterVector[Trk::loc1],
6645 parameterVector[Trk::loc2],
6646 parameterVector[Trk::phi],
6647 parameterVector[Trk::theta],
6648 parameterVector[Trk::qOverP],
6649 state_maxsipull->hasTrackCovariance()
6650 ? std::optional<AmgSymMatrix(5)>(
6651 state_maxsipull->trackCovariance())
6652 : std::nullopt));
6653
6654 double newerror[5];
6655 newerror[0] = newerror[1] = newerror[2] = newerror[3] = newerror[4] = -1;
6656 double newpull = -1;
6657 double newpull1 = -1;
6658 double newpull2 = -1;
6659 double newres1 = -1;
6660 double newres2 = -1;
6661 double newsinstereo = 0;
6662
6663 if (
6664 (prd != nullptr) &&
6665 !state_maxsipull->isRecalibrated() &&
6666 maxpull > 2.5 &&
6667 oldtrajectory->chi2() / trajectory.nDOF() > .3 * m_chi2cut &&
6668 cache.m_sirecal
6669 ) {
6670 broadrot.reset(m_broadROTcreator->correct(*prd, *trackparForCorrect, ctx));
6671 }
6672
6673 if (broadrot) {
6674 const Amg::MatrixX & covmat = broadrot->localCovariance();
6675
6676 if (state_maxsipull->sinStereo() != 0) {
6677 const auto [covEigenValueSmall, covStereoAngle] = principalComponentAnalysis2x2(covmat);
6678 newerror[0] = std::sqrt(covEigenValueSmall);
6679 newsinstereo = std::sin(covStereoAngle);
6680 } else {
6681 newerror[0] = std::sqrt(covmat(0, 0));
6682 }
6683
6684 const double cosstereo = (newsinstereo == 0) ? 1. : std::sqrt(1 - newsinstereo * newsinstereo);
6685
6686 if (cosstereo != 1.) {
6687 newres1 = (
6688 cosstereo * (broadrot->localParameters()[Trk::locX] - trackpar_maxsipull->parameters()[Trk::locX]) +
6689 newsinstereo * (broadrot->localParameters()[Trk::locY] - trackpar_maxsipull->parameters()[Trk::locY])
6690 );
6691 } else {
6692 newres1 = broadrot->localParameters()[Trk::locX] - trackpar_maxsipull->parameters()[Trk::locX];
6693 }
6694
6695 if (newerror[0] == 0.0) {
6696 ATH_MSG_WARNING("Measurement error is zero or negative, treating as outlier");
6697 newpull1 = 9999.;
6698 } else {
6699 newpull1 = std::abs(newres1 / newerror[0]);
6700 }
6701
6702 if (hittype_maxsipull == TrackState::Pixel) {
6703 newerror[1] = std::sqrt(covmat(1, 1));
6704 newres2 = broadrot->localParameters()[Trk::locY] - trackpar_maxsipull->parameters()[Trk::locY];
6705 newpull2 = std::abs(newres2 / newerror[1]);
6706 }
6707
6708 newpull = std::max(newpull1, newpull2);
6709 }
6710
6711 if (
6712 broadrot &&
6713 newpull < m_outlcut &&
6714 (newerror[0] > 1.5 * olderror[0] || newerror[1] > 1.5 * std::abs(olderror[1]))
6715 ) {
6716 if ((measno_maxsipull < 0) or(measno_maxsipull >= (int) res.size())) {
6717 throw std::runtime_error(
6718 "'res' array index out of range in TrkGlobalChi2Fitter/src/GlobalChi2Fitter.cxx:" + std::to_string(__LINE__)
6719 );
6720 }
6721
6722 trackok = false;
6723 newtrajectory = oldtrajectory;
6724
6725 if (a.cols() != nfitpars) {
6726 ATH_MSG_ERROR("Your assumption is wrong!!!!");
6727 }
6728
6729 const double oldres1 = res[measno_maxsipull];
6730 res[measno_maxsipull] = newres1;
6731 err[measno_maxsipull] = newerror[0];
6732
6733 for (int i = 0; i < nfitpars; i++) {
6734 if (weightderiv(measno_maxsipull, i) == 0) {
6735 continue;
6736 }
6737
6738 b[i] -= weightderiv(measno_maxsipull, i) * (oldres1 / olderror[0] - (newres1 * olderror[0]) / (newerror[0] * newerror[0]));
6739
6740 for (int j = i; j < nfitpars; j++) {
6741 a.fillSymmetric(
6742 i, j,
6743 a(i, j) + (
6744 weightderiv(measno_maxsipull, i) *
6745 weightderiv(measno_maxsipull, j) *
6746 ((olderror[0] * olderror[0]) / (newerror[0] * newerror[0]) - 1)
6747 )
6748 );
6749 }
6750 weightderiv(measno_maxsipull, i) *= olderror[0] / newerror[0];
6751 }
6752
6753 if (hittype_maxsipull == TrackState::Pixel) {
6754 const double oldres2 = res[measno_maxsipull + 1];
6755 res[measno_maxsipull + 1] = newres2;
6756 err[measno_maxsipull + 1] = newerror[1];
6757
6758 for (int i = 0; i < nfitpars; i++) {
6759 if (weightderiv(measno_maxsipull + 1, i) == 0) {
6760 continue;
6761 }
6762
6763 b[i] -= weightderiv(measno_maxsipull + 1, i) * (oldres2 / olderror[1] - (newres2 * olderror[1]) / (newerror[1] * newerror[1]));
6764
6765 for (int j = i; j < nfitpars; j++) {
6766 a.fillSymmetric(
6767 i, j,
6768 a(i, j) + (
6769 weightderiv(measno_maxsipull + 1, i) *
6770 weightderiv(measno_maxsipull + 1, j) *
6771 ((olderror[1] * olderror[1]) / (newerror[1] * newerror[1]) - 1)
6772 )
6773 );
6774 }
6775
6776 weightderiv(measno_maxsipull + 1, i) *= olderror[1] / newerror[1];
6777 }
6778 }
6779
6781 "Recovering outlier, hitno=" << hitno_maxsipull << " measno=" <<
6782 measno_maxsipull << " pull=" << maxsipull << " olderror_0=" <<
6783 olderror[0] << " newerror_0=" << newerror[0] << " olderror_1=" <<
6784 olderror[1] << " newerror_1=" << newerror[1]
6785 );
6786
6787 state_maxsipull->setMeasurement(std::move(broadrot));
6788 state_maxsipull->setSinStereo(newsinstereo);
6789 state_maxsipull->setMeasurementErrors(newerror);
6790 } else if (
6791 (
6792 (
6793 ((n3sigma < 2 && maxsipull > cut2 && maxsipull < cut) || n3sigma > 1) &&
6794 (oldtrajectory->chi2() / oldtrajectory->nDOF() > .3 * m_chi2cut || noutl > 1)
6795 ) ||
6796 maxsipull > cut
6797 ) &&
6798 (oldtrajectory->nDOF() > 1 || hittype_maxsipull == TrackState::SCT) &&
6799 runoutlier
6800 ) {
6801 trackok = false;
6803 "Removing outlier, hitno=" << hitno_maxsipull << ", measno=" <<
6804 measno_maxsipull << " pull=" << maxsipull
6805 );
6806
6807 newa = a;
6808 newb = b;
6809 newap = &newa;
6810 newbp = &newb;
6811 cleanup_newtrajectory = std::make_unique<GXFTrajectory>(*oldtrajectory);
6812 newtrajectory = cleanup_newtrajectory.get();
6813
6814 if (newa.cols() != nfitpars) {
6815 ATH_MSG_ERROR("Your assumption is wrong!!!!");
6816 }
6817
6818 Amg::VectorX & newres = newtrajectory->residuals();
6819 Amg::MatrixX & newweightderiv = newtrajectory->weightedResidualDerivatives();
6820 if ((measno_maxsipull < 0) or(measno_maxsipull >= (int) res.size())) {
6821 throw std::runtime_error(
6822 "'res' array index out of range in TrkGlobalChi2Fitter/src/GlobalChi2Fitter.cxx:" + std::to_string(__LINE__)
6823 );
6824 }
6825
6826 const double oldres1 = res[measno_maxsipull];
6827 newres[measno_maxsipull] = 0;
6828
6829 for (int i = 0; i < nfitpars; i++) {
6830 if (weightderiv(measno_maxsipull, i) == 0) {
6831 continue;
6832 }
6833
6834 newb[i] -= weightderiv(measno_maxsipull, i) * oldres1 / olderror[0];
6835
6836 for (int j = i; j < nfitpars; j++) {
6837 newa.fillSymmetric(
6838 i, j,
6839 newa(i, j) - (
6840 weightderiv(measno_maxsipull, i) *
6841 weightderiv(measno_maxsipull, j)
6842 )
6843 );
6844 }
6845 newweightderiv(measno_maxsipull, i) = 0;
6846 }
6847
6848 if (hittype_maxsipull == TrackState::Pixel) {
6849 const double oldres2 = res[measno_maxsipull + 1];
6850 newres[measno_maxsipull + 1] = 0;
6851
6852 for (int i = 0; i < nfitpars; i++) {
6853 if (weightderiv(measno_maxsipull + 1, i) == 0) {
6854 continue;
6855 }
6856
6857 newb[i] -= weightderiv(measno_maxsipull + 1, i) * oldres2 / olderror[1];
6858
6859 for (int j = i; j < nfitpars; j++) {
6860 if (weightderiv(measno_maxsipull + 1, j) == 0) {
6861 continue;
6862 }
6863
6864 newa.fillSymmetric(
6865 i, j,
6866 newa(i, j) - (
6867 weightderiv(measno_maxsipull + 1, i) *
6868 weightderiv(measno_maxsipull + 1, j)
6869 )
6870 );
6871 }
6872 newweightderiv(measno_maxsipull + 1, i) = 0;
6873 }
6874 }
6875
6876 newtrajectory->setOutlier(stateno_maxsipull);
6877 }
6878 }
6879
6880 if (!trackok) {
6881 Amg::SymMatrixX lu_m = *newap;
6882 newtrajectory->setConverged(false);
6883 bool doderiv = m_redoderivs;
6884 cache.m_fittercode = updateFitParameters(*newtrajectory, *newbp, lu_m);
6887 return nullptr;
6888 }
6889
6890 for (int it = 0; it < m_maxit; ++it) {
6891 if (it == m_maxit - 1) {
6892 ATH_MSG_DEBUG("Fit did not converge");
6895 return nullptr;
6896 }
6897
6898 if (!newtrajectory->converged()) {
6899 cache.m_fittercode = runIteration(
6900 ctx, cache, *newtrajectory, it, *newap, *newbp, lu_m, doderiv);
6901
6904 return nullptr;
6905 }
6906
6907 if (!newtrajectory->converged()) {
6908 cache.m_fittercode = updateFitParameters(*newtrajectory, *newbp, lu_m);
6911
6912 return nullptr;
6913 }
6914 }
6915 } else {
6916 const double oldchi2 = oldtrajectory->chi2() / oldtrajectory->nDOF();
6917 const double newchi2 = (newtrajectory->nDOF() > 0) ? newtrajectory->chi2() / newtrajectory->nDOF() : 0;
6918 double mindiff = 0;
6919
6920 if (newtrajectory->nDOF() != oldtrajectory->nDOF() && maxsipull > cut2) {
6921 mindiff = (oldchi2 > .33 * m_chi2cut || noutl > 0) ? .8 : 1.;
6922
6923 if (noutl == 0 && maxsipull < cut - .5 && oldchi2 < .5 * m_chi2cut) {
6924 mindiff = 2.;
6925 }
6926 }
6927
6928 if (newchi2 > oldchi2 || (newchi2 > oldchi2 - mindiff && newchi2 > .33 * oldchi2)) {
6929 ATH_MSG_DEBUG("Outlier not confirmed, keeping old trajectory");
6930
6931 if (oldchi2 > m_chi2cut) {
6934 return nullptr;
6935 }
6936
6937 (void)cleanup_oldtrajectory.release();
6938 return oldtrajectory;
6939 }
6940 if (oldtrajectory != newtrajectory) {
6941 cleanup_oldtrajectory = std::move(cleanup_newtrajectory);
6942 oldtrajectory = newtrajectory;
6943 a = newa;
6944 b = newb;
6945 }
6946
6947 // Solve assuming the matrix is SPD.
6948 // Cholesky Decomposition is used
6949 Eigen::LLT < Eigen::MatrixXd > const lltOfW(a);
6950 if (lltOfW.info() == Eigen::Success) {
6951 // Solve for x where Wx = I
6952 // this is cheaper than invert as invert makes no assumptions about the
6953 // matrix being symmetric
6954 const int ncols = a.cols();
6955 Amg::MatrixX const weightInvAMG = Amg::MatrixX::Identity(ncols, ncols);
6956 fullcov = lltOfW.solve(weightInvAMG);
6957 } else {
6958 ATH_MSG_DEBUG("matrix inversion failed!");
6961 return nullptr;
6962 }
6963 break;
6964 }
6965 }
6966 }
6967
6968 if (!trackok) {
6969 calculateTrackErrors(*oldtrajectory, fullcov, true);
6970 }
6971 }
6972
6973 if (
6974 oldtrajectory->nDOF() > 0 &&
6975 oldtrajectory->chi2() / oldtrajectory->nDOF() > m_chi2cut &&
6976 runoutlier
6977 ) {
6980 return nullptr;
6981 }
6982
6983 (void)cleanup_oldtrajectory.release();
6984 return oldtrajectory;
6985 }
6986
6988 Cache &cache,
6989 GXFTrajectory &oldtrajectory
6990 ) {
6991 Amg::MatrixX & derivs = oldtrajectory.weightedResidualDerivatives();
6992 Amg::VectorX & errors = oldtrajectory.errors();
6993 int nrealmeas = 0;
6994
6995 for (auto & hit : oldtrajectory.trackStates()) {
6996 if (const auto *pMeas{hit->measurement()};
6997 hit->getStateType(TrackStateOnSurface::Measurement) and (
7000 )
7001 ) {
7002 nrealmeas += hit->numberOfMeasuredParameters();
7003 }
7004 }
7005 cache.m_derivmat.resize(nrealmeas, oldtrajectory.numberOfFitParameters());
7006 cache.m_derivmat.setZero();
7007 int measindex = 0;
7008 int measindex2 = 0;
7009 const int nperpars = oldtrajectory.numberOfPerigeeParameters();
7010 const int nscat = oldtrajectory.numberOfScatterers();
7011 for (auto & hit : oldtrajectory.trackStates()) {
7012 if (const auto *pMeas{hit->measurement()};
7013 hit->getStateType(TrackStateOnSurface::Measurement) and (
7016 )
7017 ) {
7018 for (int i = measindex; i < measindex + hit->numberOfMeasuredParameters(); i++) {
7019 for (int j = 0; j < oldtrajectory.numberOfFitParameters(); j++) {
7020 cache.m_derivmat(i, j) = derivs(measindex2, j) * errors[measindex2];
7021 if ((j == 4 && !oldtrajectory.m_straightline) || j >= nperpars + 2 * nscat) {
7022 cache.m_derivmat(i, j) *= 1000;
7023 }
7024 }
7025
7026 measindex2++;
7027 }
7028
7029 measindex += hit->numberOfMeasuredParameters();
7030 } else if (hit->materialEffects() == nullptr) {
7031 measindex2 += hit->numberOfMeasuredParameters();
7032 }
7033 }
7034 }
7035
7036 std::unique_ptr<const TrackParameters> GlobalChi2Fitter::makeTrackFindPerigeeParameters(
7037 const EventContext & ctx,
7038 Cache &cache,
7039 GXFTrajectory &oldtrajectory,
7040 const ParticleHypothesis matEffects
7041 ) const {
7042 GXFTrackState *firstmeasstate = nullptr;
7043 GXFTrackState *lastmeasstate = nullptr;
7044 std::tie(firstmeasstate, lastmeasstate) = oldtrajectory.findFirstLastMeasurement();
7045 std::unique_ptr<const TrackParameters> per(nullptr);
7046
7047 if (cache.m_acceleration && !m_matupdator.empty()) {
7048 std::unique_ptr<const TrackParameters> prevpar(
7049 firstmeasstate->trackParameters() != nullptr ?
7050 firstmeasstate->trackParameters()->clone() :
7051 nullptr
7052 );
7053 std::vector<std::pair<const Layer *, const Layer *>> & upstreamlayers = oldtrajectory.upstreamMaterialLayers();
7054 bool first = true;
7055
7056 for (const auto & [layer1, layer2] : upstreamlayers | std::views::reverse) {
7057 if (prevpar == nullptr) {
7058 break;
7059 }
7060
7062 const Layer *layer = layer1 != nullptr ? layer1 : layer2;
7063
7064 const DistanceSolution distsol = layer->surfaceRepresentation().straightLineDistanceEstimate(
7065 prevpar->position(), prevpar->momentum().unit()
7066 );
7067 const double distance = getDistance(distsol);
7068
7069 if (distsol.numberOfSolutions() == 2) {
7070 if (std::abs(distance) < 0.01) {
7071 continue;
7072 }
7073
7074 if (distsol.first() * distsol.second() < 0 && !first) {
7075 continue;
7076 }
7077 }
7078
7079 if (first && distance > 0) {
7080 propdir = alongMomentum;
7081 }
7082
7083 std::unique_ptr<const TrackParameters> layerpar(
7084 m_propagator->propagate(
7085 ctx,
7086 *prevpar,
7087 layer->surfaceRepresentation(),
7088 propdir,
7089 true,
7090 oldtrajectory.m_fieldprop,
7092 )
7093 );
7094
7095 if (layerpar == nullptr) {
7096 continue;
7097 }
7098
7099 if (layer->surfaceRepresentation().bounds().inside(layerpar->localPosition())) {
7100 layerpar = m_matupdator->update(layerpar.get(), *layer, oppositeMomentum, matEffects);
7101 }
7102
7103 prevpar = std::move(layerpar);
7104 first = false;
7105 }
7106
7107 const Layer *startlayer = firstmeasstate->trackParameters()->associatedSurface().associatedLayer();
7108
7109 if ((startlayer != nullptr) && (startlayer->layerMaterialProperties() != nullptr)) {
7110 double startfactor = startlayer->layerMaterialProperties()->alongPostFactor();
7111 const Surface & discsurf = startlayer->surfaceRepresentation();
7112
7113 if (discsurf.type() == Trk::SurfaceType::Disc && discsurf.center().z() * discsurf.normal().z() < 0) {
7114 startfactor = startlayer->layerMaterialProperties()->oppositePostFactor();
7115 }
7116 if (startfactor > 0.5) {
7117 std::unique_ptr<const TrackParameters> updatedpar = m_matupdator->update(
7118 firstmeasstate->trackParameters(), *startlayer, oppositeMomentum, matEffects
7119 );
7120
7121 if (updatedpar != nullptr) {
7122 firstmeasstate->setTrackParameters(std::move(updatedpar));
7123 }
7124 }
7125 }
7126
7127 // @TODO Coverity complains about a possible NULL pointer dereferencing in lastmeasstate->...
7128 // Now an exception is thrown if there is no firstmeastate. Thus if the code here is
7129 // reached then there should be a firstmeasstate and a lastmeasstate
7130
7131 const Layer *endlayer = lastmeasstate->trackParameters()->associatedSurface().associatedLayer();
7132
7133 if ((endlayer != nullptr) && (endlayer->layerMaterialProperties() != nullptr)) {
7134 double endfactor = endlayer->layerMaterialProperties()->alongPreFactor();
7135 const Surface & discsurf = endlayer->surfaceRepresentation();
7136
7137 if (discsurf.type() == Trk::SurfaceType::Disc && discsurf.center().z() * discsurf.normal().z() < 0) {
7138 endfactor = endlayer->layerMaterialProperties()->oppositePreFactor();
7139 }
7140
7141 if (endfactor > 0.5) {
7142 std::unique_ptr<const TrackParameters> updatedpar = m_matupdator->update(
7143 lastmeasstate->trackParameters(), *endlayer, alongMomentum, matEffects
7144 );
7145
7146 if (updatedpar != nullptr) {
7147 lastmeasstate->setTrackParameters(std::move(updatedpar));
7148 }
7149 }
7150 }
7151
7152 if (prevpar != nullptr) {
7153 per = m_propagator->propagate(
7154 ctx,
7155 *prevpar,
7156 PerigeeSurface(Amg::Vector3D(0, 0, 0)),
7158 false,
7159 oldtrajectory.m_fieldprop,
7161 );
7162 }
7163
7164 if (per == nullptr) {
7165 ATH_MSG_DEBUG("Failed to extrapolate to perigee, returning 0");
7168 return nullptr;
7169 }
7170 } else if (cache.m_acceleration && (firstmeasstate->trackParameters() != nullptr)) {
7171 per = m_extrapolator->extrapolate(ctx,
7172 *firstmeasstate->trackParameters(),
7173 PerigeeSurface(Amg::Vector3D(0, 0, 0)),
7175 false,
7176 matEffects);
7177 } else {
7178 per.reset(oldtrajectory.referenceParameters()->clone());
7179 }
7180
7181 return per;
7182 }
7183
7184 std::unique_ptr<GXFTrackState>
7186 const EventContext & ctx,
7187 Cache &cache,
7188 GXFTrajectory &oldtrajectory,
7189 const ParticleHypothesis matEffects
7190 ) const {
7191 std::unique_ptr<const TrackParameters> per = makeTrackFindPerigeeParameters(ctx, cache, oldtrajectory, matEffects);
7192
7193 if (per == nullptr) {
7194 return nullptr;
7195 }
7196
7197 ATH_MSG_DEBUG("Final perigee: " << *per << " pos: " << per->position() << " pT: " << per->pT());
7198
7199 return std::make_unique<GXFTrackState>(std::move(per), TrackStateOnSurface::Perigee);
7200 }
7201
7203 const std::vector<std::unique_ptr<TrackParameters>> & hc,
7204 std::set<Identifier> & id_set,
7205 std::set<Identifier> & sct_set,
7206 TrackHoleCount & rv,
7207 bool count_holes,
7208 bool count_dead
7209 ) const {
7210 /*
7211 * Our input is a list of track states, which we are iterating over. We
7212 * need to examine each one and update the values in our track hole count
7213 * accordingly.
7214 */
7215 for (const std::unique_ptr<TrackParameters> & tp : hc) {
7216 /*
7217 * It is possible, expected even, for some of these pointers to be null.
7218 * In those cases, it would be dangerous to continue, so we need to make
7219 * sure we skip them.
7220 */
7221 if (tp == nullptr) {
7222 continue;
7223 }
7224
7225 /*
7226 * Extract the detector element of the track parameter surface for
7227 * examination. If for whatever reason there is none (i.e. the surface
7228 * is not a detector at all), we can skip it and continue.
7229 */
7230 const TrkDetElementBase * de = tp->associatedSurface().associatedDetectorElement();
7231
7232 if (de == nullptr) {
7233 continue;
7234 }
7235
7236 Identifier const id = de->identify();
7237
7238 /*
7239 * If, for whatever reason, we have already visited this detector, we do
7240 * not want to visit it again. Otherwise we might end up with modules
7241 * counted twice, and that would be very bad.
7242 */
7243 if (id_set.find(id) != id_set.end()) {
7244 continue;
7245 }
7246
7247 /*
7248 * This is the meat of the pudding, we use the boundary checking tool
7249 * to see whether this set of parameters is a hole candidate, a dead
7250 * module, or not a hole at all.
7251 */
7252 BoundaryCheckResult const bc = m_boundaryCheckTool->boundaryCheck(*tp);
7253
7254 if (bc == BoundaryCheckResult::DeadElement && count_dead) {
7255 /*
7256 * If the module is dead, our job is very simple. We just check
7257 * whether it is a Pixel or an SCT and increment the appropriate
7258 * counter. We also insert the module into our set of visited elements.
7259 */
7260 if (m_DetID->is_pixel(id)) {
7261 ++rv.m_pixel_dead;
7262 } else if (m_DetID->is_sct(id)) {
7263 ++rv.m_sct_dead;
7264 }
7265 id_set.insert(id);
7266 } else if (bc == BoundaryCheckResult::Candidate && count_holes) {
7267 /*
7268 * If the module is a candidate, it's much the same, but we also need
7269 * to handle double SCT holes.
7270 */
7271 if (m_DetID->is_pixel(id)) {
7272 ++rv.m_pixel_hole;
7273 } else if (m_DetID->is_sct(id)) {
7274 ++rv.m_sct_hole;
7275
7276 /*
7277 * To check for SCT double holes, we need to first fetch the other
7278 * side of the current SCT. Thankfully, the detector description
7279 * makes this very easy.
7280 */
7281 const InDetDD::SiDetectorElement* e = dynamic_cast<const InDetDD::SiDetectorElement *>(de);
7282 const Identifier os = e->otherSide()->identify();
7283
7284 /*
7285 * We keep a special set containing only SCT hole IDs. We simply
7286 * check whether the ID of the other side of the SCT is in this set
7287 * to confirm that we have a double hole. Note that the first side
7288 * in a double hole will be counted as a SCT hole only, and the
7289 * second side will count as another hole as well as a double hole,
7290 * which is exactly the behaviour we would expect to see.
7291 */
7292 if (sct_set.find(os) != sct_set.end()) {
7293 ++rv.m_sct_double_hole;
7294 }
7295
7296 /*
7297 * We need to add our SCT to the SCT identifier set if it is a
7298 * candidate hit, otherwise known as a hole in this context.
7299 */
7300 sct_set.insert(id);
7301 }
7302
7303 /*
7304 * SCTs are also added to the set of all identifiers to avoid double
7305 * counting them.
7306 */
7307 id_set.insert(id);
7308 }
7309 }
7310 }
7311
7312 std::vector<std::reference_wrapper<GXFTrackState>> GlobalChi2Fitter::holeSearchStates(
7313 GXFTrajectory & trajectory
7314 ) const {
7315 /*
7316 * Firstly, we will need to find the last measurement state on our track.
7317 * This will allow us to break the main loop later once we are done with
7318 * our work.
7319 */
7320 GXFTrackState * lastmeas = nullptr;
7321
7322 for (const std::unique_ptr<GXFTrackState> & s : trajectory.trackStates()) {
7323 if (s->getStateType(TrackStateOnSurface::Measurement)) {
7324 lastmeas = s.get();
7325 }
7326 }
7327
7328 /*
7329 * We create a vector of reference wrappers and reserve at least enough
7330 * space to contain the entire trajectory. This is perhaps a little
7331 * wasteful since we will never need this much space, but it may be more
7332 * efficient than taking the resizing pentalty on the chin.
7333 */
7334 std::vector<std::reference_wrapper<GXFTrackState>> rv;
7335 rv.reserve(trajectory.trackStates().size());
7336
7337 /*
7338 * The main body of our method now. We iterate over all track states in
7339 * the track, at least until we find the last measurement state as found
7340 * above.
7341 */
7342 for (const std::unique_ptr<GXFTrackState> & s : trajectory.trackStates()) {
7343 /*
7344 * We are only interested in collecting measurements, perigees, and any
7345 * outlier states.
7346 */
7347 if (
7348 s->getStateType(TrackStateOnSurface::Measurement) ||
7349 s->getStateType(TrackStateOnSurface::Perigee) ||
7350 s->getStateType(TrackStateOnSurface::Outlier)
7351 ) {
7352 /*
7353 * We store a reference to the current track state in our return value
7354 * vector.
7355 */
7356 rv.emplace_back(*s);
7357
7358 /*
7359 * We want to make sure we do not collect any TRT results or other
7360 * non-SCT and non-Pixel detector types. For that, we need to access
7361 * the details of the detector element and determine the detector type.
7362 */
7363 const TrkDetElementBase * de = s->trackParameters()->associatedSurface().associatedDetectorElement();
7364
7365 if (de != nullptr) {
7366 Identifier const id = de->identify();
7367
7368 if (!m_DetID->is_pixel(id) && !m_DetID->is_sct(id)) {
7369 break;
7370 }
7371 }
7372
7373 /*
7374 * We also have no interest in going past the final measurement, so we
7375 * break out of the loop if we find it.
7376 */
7377 //cppcheck-suppress iterators3
7378 if (s.get() == lastmeas) {
7379 break;
7380 }
7381 }
7382 }
7383
7384 return rv;
7385 }
7386
7387 std::optional<GlobalChi2Fitter::TrackHoleCount> GlobalChi2Fitter::holeSearchProcess(
7388 const EventContext & ctx,
7389 const std::vector<std::reference_wrapper<GXFTrackState>> & states
7390 ) const {
7391 /*
7392 * Firstly, we need to guard against tracks having too few measurement
7393 * states to perform a good hole search. This is a mechanism that we
7394 * inherit from the reference hole search. If we have too few states, we
7395 * return a non-extant result to indicate an error state.
7396 *
7397 * TODO: The minimum value of 3 is also borrowed from the reference
7398 * implementation. It's hardcoded for now, but could be a parameter in the
7399 * future.
7400 */
7401 constexpr uint min_meas = 3;
7402 if (std::count_if(states.begin(), states.end(), [](const GXFTrackState & s){ return s.getStateType(TrackStateOnSurface::Measurement); }) < min_meas) {
7403 return {};
7404 }
7405
7406 bool seen_meas = false;
7407 TrackHoleCount rv;
7408 std::set<Identifier> id_set;
7409 std::set<Identifier> sct_set;
7410
7411 /*
7412 * Using an old-school integer-based for loop because we need to iterate
7413 * over successive pairs of states to do an extrapolation between.
7414 */
7415 for (std::size_t i = 0; i < states.size() - 1; i++) {
7416 /*
7417 * Gather references to the state at the beginning of the extrapolation,
7418 * named beg, and the end, named end.
7419 */
7420 GXFTrackState & beg = states[i];
7421 GXFTrackState const& end = states[i + 1];
7422
7423 /*
7424 * Update the boolean keeping track of whether we have seen a measurement
7425 * or outlier yet. Once we see one, this will remain true forever, but
7426 * it helps us make sure we don't collect holes before the first
7427 * measurement.
7428 */
7429 seen_meas |= beg.getStateType(TrackStateOnSurface::Measurement) || beg.getStateType(TrackStateOnSurface::Outlier);
7430
7431 /*
7432 * Calculate the distance between the position of the starting parameters
7433 * and the end parameters. If this distance is sufficiently small, there
7434 * can be no elements between them (for example, between two SCTs), and
7435 * we don't need to do an extrapolation. This can easily save us a few
7436 * microseconds.
7437 */
7438 const double dist = (beg.trackParameters()->position() - end.trackParameters()->position()).norm();
7439
7440 const bool zStartValid = std::abs(beg.trackParameters()->position().z())<10000.;
7441 if(!zStartValid){
7442 ATH_MSG_DEBUG("Pathological track parameter well outside of detector");
7443 ATH_MSG_DEBUG("Propagator might have issue with this, skipping");
7444 ATH_MSG_VERBOSE("dumping track parameters " << *(beg.trackParameters()));
7445 }
7446
7447 /*
7448 * Only proceed to count holes if we have seen a measurement before (this
7449 * may include the starting track state, if it is a measurement) and the
7450 * distance between start and end is at least 2.5 millimeters
7451 * and the z position is valid
7452 */
7453 if (seen_meas && dist >= 2.5 && zStartValid) {
7454 /*
7455 * First, we retrieve the hole data stored in the beginning state. Note
7456 * that this may very well be non-extant, but it is possible for the
7457 * fitter to have deposited some hole information into the track state
7458 * earlier on in the fitting process.
7459 */
7460 std::optional<std::vector<std::unique_ptr<TrackParameters>>> & hc = beg.getHoles();
7461 std::vector<std::unique_ptr<TrackParameters>> states;
7462
7463 /*
7464 * Gather the track states between the start and end of the
7465 * extrapolation. If the track state contained hole search information,
7466 * we simply move that out and use it. If there was no information, we
7467 * do a fresh extrapolation. This can be a CPU hog!
7468 */
7469 if (hc.has_value()) {
7470 states = std::move(*hc);
7471 } else {
7472 states = holesearchExtrapolation(ctx, *beg.trackParameters(), end, alongMomentum);
7473 }
7474
7475 /*
7476 * Finally, we process the collected hole candidate states, checking
7477 * them for liveness and other properties. This helper function will
7478 * increment the values in rv accordingly.
7479 */
7480 holeSearchHelper(states, id_set, sct_set, rv, true, true);
7481 }
7482 }
7483
7484 /*
7485 * Once we are done processing our measurements, we also need to do a
7486 * final blind extrapolation to collect and dead modules (but not holes)
7487 * behind the last measurement. For this, we do a blind extrapolation
7488 * from the final state.
7489 */
7490 GXFTrackState const& last = states.back();
7491
7492 /*
7493 * To do the blind extrapolation, we need to have a set of track parameters
7494 * for our last measurement state. We also check whether the position of
7495 * the last measurement is still inside the inner detector. If it is not,
7496 * we don't need to blindly extrapolate because we're only interested in
7497 * collecting inner detector dead modules. This check saves us a few tens
7498 * of microseconds.
7499 */
7500 if (
7501 last.trackParameters() != nullptr &&
7502 m_idVolume.inside(last.trackParameters()->position())
7503 ) {
7504 /*
7505 * Simply conduct the blind extrapolation, and then use the helper tool
7506 * to ensure that the hole counts are updated.
7507 */
7508 std::vector<std::unique_ptr<Trk::TrackParameters>> const bl = m_extrapolator->extrapolateBlindly(
7509 ctx,
7510 *last.trackParameters(),
7512 false,
7513 Trk::pion,
7514 &m_idVolume
7515 );
7516
7517 /*
7518 * Note that we have flipped one of the boolean parameters of the helper
7519 * method here to make sure it only collects dead modules, not hole
7520 * candidates.
7521 */
7522 holeSearchHelper(bl, id_set, sct_set, rv, false, true);
7523 }
7524
7525 return rv;
7526 }
7527
7528 std::unique_ptr<Track> GlobalChi2Fitter::makeTrack(
7529 const EventContext & ctx,
7530 Cache & cache,
7531 GXFTrajectory & oldtrajectory,
7532 ParticleHypothesis matEffects
7533 ) const {
7534 // Convert internal trajectory into track
7535 auto trajectory = std::make_unique<Trk::TrackStates>();
7536
7537 if (m_fillderivmatrix) {
7538 makeTrackFillDerivativeMatrix(cache, oldtrajectory);
7539 }
7540
7541 GXFTrajectory tmptrajectory(oldtrajectory);
7542
7543 std::unique_ptr<GXFTrackState> perigee_ts = makeTrackFindPerigee(ctx, cache, oldtrajectory, matEffects);
7544
7545 if (perigee_ts == nullptr) {
7546 return nullptr;
7547 }
7548
7549 tmptrajectory.addBasicState(std::move(perigee_ts), cache.m_acceleration ? 0 : tmptrajectory.numberOfUpstreamStates());
7550 //reserve the ouput size
7551 trajectory->reserve(tmptrajectory.trackStates().size());
7552 for (auto & hit : tmptrajectory.trackStates()) {
7553 if (
7554 hit->measurementType() == TrackState::Pseudo &&
7555 hit->getStateType(TrackStateOnSurface::Outlier)
7556 ) {
7557 hit->resetTrackCovariance();
7558 continue;
7559 }
7560
7561 if (!Trk::consistentSurfaces (hit->trackParameters(),
7562 hit->measurement(),
7563 hit->materialEffects()))
7564 {
7565 return nullptr;
7566 }
7567
7568 //should check hit->isSane() here with better equality check(other than ptr comparison)
7569 auto trackState = hit->trackStateOnSurface();
7570 hit->resetTrackCovariance();
7571 trajectory->emplace_back(trackState.release());
7572 }
7573
7574 auto qual = std::make_unique<FitQuality>(tmptrajectory.chi2(), tmptrajectory.nDOF());
7575
7576
7577 TrackInfo info;
7578
7579 if (matEffects != electron) {
7580 info = TrackInfo(TrackInfo::GlobalChi2Fitter, matEffects);
7581 } else {
7583 info.setTrackProperties(TrackInfo::BremFit);
7584
7585 if (matEffects == electron && tmptrajectory.hasKink()) {
7586 info.setTrackProperties(TrackInfo::BremFitSuccessful);
7587 }
7588 }
7589
7590 if (tmptrajectory.m_straightline) {
7591 info.setTrackProperties(TrackInfo::StraightTrack);
7592 }
7593
7594 std::unique_ptr<Track> rv = std::make_unique<Track>(info, std::move(trajectory), std::move(qual));
7595
7596 /*
7597 * Here, we create a track summary and attach it to our newly created
7598 * track. Note that this code only runs if the m_createSummary Gaudi
7599 * property is set. In cases where having a track summary on the track is
7600 * not desired, such as for compatibility with other tools, this can be
7601 * turned off.
7602 */
7603 if (m_createSummary.value()) {
7604 std::unique_ptr<TrackSummary> ts = std::make_unique<TrackSummary>();
7605
7606 /*
7607 * This segment determines the hole search behaviour of the track fitter.
7608 * It is only invoked if the DoHoleSearch parameter is set, but it can
7609 * take a significant amount of CPU time, since the hole search is rather
7610 * expensive. Beware of that!
7611 */
7612 if (m_holeSearch.value()) {
7613 std::optional<TrackHoleCount> hole_count;
7614
7615 /*
7616 * First, we collect a list of states that will act as our hole search
7617 * extrapolation states. This will serve as our source of truth in
7618 * regards to which track states we need to extrapolate between.
7619 */
7620 std::vector<std::reference_wrapper<GXFTrackState>> const states = holeSearchStates(tmptrajectory);
7621
7622 /*
7623 * Then, collect the actual hole search infomation using our state list
7624 * from before. This is the expensive operation, as it will invoke a
7625 * series of extrapolations if not all states have existing hole
7626 * information! It will also check all the hole candidates to see if
7627 * they are actually holes or not.
7628 */
7629 hole_count = holeSearchProcess(ctx, states);
7630
7631 /*
7632 * Note that the hole search is not guaranteed to return a useful set
7633 * of values. It can, for example, reach an error state if the number
7634 * of measurements on a track is below a certain threshold. In that
7635 * case, a non-extant result will be returned, which we must guard
7636 * against. In that case, the hole counts will remain unset.
7637 */
7638 if (hole_count.has_value()) {
7639 /*
7640 * If the hole search did return good results, we can proceed to
7641 * simply copy the numerical values in the track summary.
7642 */
7643 ts->update(Trk::numberOfPixelHoles, hole_count->m_pixel_hole);
7644 ts->update(Trk::numberOfSCTHoles, hole_count->m_sct_hole);
7645 ts->update(Trk::numberOfSCTDoubleHoles, hole_count->m_sct_double_hole);
7646 ts->update(Trk::numberOfPixelDeadSensors, hole_count->m_pixel_dead);
7647 ts->update(Trk::numberOfSCTDeadSensors, hole_count->m_sct_dead);
7648 }
7649 }
7650
7651 rv->setTrackSummary(std::move(ts));
7652 }
7653
7654 return rv;
7655 }
7656
7658
7659 std::vector<std::unique_ptr<TrackParameters>> GlobalChi2Fitter::holesearchExtrapolation(
7660 const EventContext & ctx,
7661 const TrackParameters & src,
7662 const GXFTrackState & dst,
7663 PropDirection propdir
7664 ) const {
7665 /*
7666 * First, we conduct a bog standard stepwise extrapolation. This will
7667 * yield some unwanted results, but we will filter those later.
7668 */
7669 std::vector<std::unique_ptr<TrackParameters>> rv = m_extrapolator->extrapolateStepwise(
7670 ctx, src, dst.associatedSurface(), propdir, false
7671 );
7672
7673 /*
7674 * It is possible for the first returned track parameter to be on the same
7675 * surface as we started on. That's probably due to some rounding errors.
7676 * We check for this possibility, and set the pointer to null if it
7677 * occurs. Note that this leaves some null pointers in the returned vector
7678 * but this is more performant compared to removing them properly.
7679 */
7680 if (
7681 !rv.empty() && (
7682 &rv.front()->associatedSurface() == &dst.associatedSurface() ||
7683 &rv.front()->associatedSurface() == &src.associatedSurface() ||
7684 trackParametersClose(*rv.front(), src, 0.001) ||
7685 trackParametersClose(*rv.front(), *dst.trackParameters(), 0.001)
7686 )
7687 ) {
7688 rv.front().reset(nullptr);
7689 }
7690
7691 /*
7692 * Same logic, but for the last returned element. In that case, we get a
7693 * set of parameters on the destination surface, which we also do not
7694 * want.
7695 */
7696 if (
7697 rv.size() > 1 && (
7698 &rv.back()->associatedSurface() == &dst.associatedSurface() ||
7699 &rv.back()->associatedSurface() == &src.associatedSurface() ||
7700 trackParametersClose(*rv.back(), src, 0.001) ||
7701 trackParametersClose(*rv.back(), *dst.trackParameters(), 0.001)
7702 )
7703 ) {
7704 rv.back().reset(nullptr);
7705 }
7706
7707 return rv;
7708 }
7709
7711 const EventContext & ctx,
7712 const TrackParameters & prev,
7713 const GXFTrackState & ts,
7714 PropDirection propdir,
7715 const MagneticFieldProperties& bf,
7716 bool calcderiv,
7717 bool holesearch
7718 ) const {
7719 std::unique_ptr<const TrackParameters> rv;
7720 std::optional<TransportJacobian> jac{};
7721
7722 if (calcderiv && !m_numderiv) {
7723 rv = m_propagator->propagateParameters(
7724 ctx, prev, ts.associatedSurface(), propdir, false, bf, jac, Trk::nonInteracting, false
7725 );
7726 } else {
7727 rv = m_propagator->propagateParameters(
7728 ctx, prev, ts.associatedSurface(), propdir, false, bf, Trk::nonInteracting, false
7729 );
7730
7731 if (rv != nullptr && calcderiv) {
7732 jac = numericalDerivatives(ctx, &prev, ts.associatedSurface(), propdir, bf);
7733 }
7734 }
7735
7736 std::optional<std::vector<std::unique_ptr<TrackParameters>>> extrapolation;
7737
7738 if (holesearch) {
7739 extrapolation = holesearchExtrapolation(ctx, prev, ts, propdir);
7740 }
7741
7742 return PropagationResult {
7743 std::move(rv),
7744 std::move(jac),
7745 std::move(extrapolation)
7746 };
7747 }
7748
7750 const EventContext & ctx,
7751 const TrackParameters & prev,
7752 const GXFTrackState & ts,
7753 PropDirection propdir,
7754 const MagneticFieldProperties& bf,
7755 bool calcderiv,
7756 bool holesearch
7757 ) const {
7759
7761 ctx, prev, ts, propdir, bf, calcderiv, holesearch
7762 );
7763
7764 if (rv.m_parameters == nullptr) {
7765 propdir = invertPropdir(propdir);
7766
7768 ctx, prev, ts, propdir, bf, calcderiv, holesearch
7769 );
7770 }
7771
7772 return rv;
7773 }
7774
7776 const EventContext& ctx,
7777 GXFTrajectory & trajectory,
7778 bool calcderiv
7779 ) const {
7780 // Loop over states, calculate track parameters and (optionally) jacobian at each state
7781 ATH_MSG_DEBUG("CalculateTrackParameters");
7782
7783 std::vector<std::unique_ptr<GXFTrackState>> & states = trajectory.trackStates();
7784 const int nstatesupstream = trajectory.numberOfUpstreamStates();
7785 const TrackParameters *prevtrackpar = trajectory.referenceParameters();
7786 std::unique_ptr<const TrackParameters> tmptrackpar;
7787
7788 for (int hitno = nstatesupstream - 1; hitno >= 0; hitno--) {
7789 const Surface &surf1 = states[hitno]->associatedSurface();
7791
7792 const DistanceSolution distsol = surf1.straightLineDistanceEstimate(
7793 prevtrackpar->position(), prevtrackpar->momentum().unit()
7794 );
7795
7796 const double distance = getDistance(distsol);
7797
7798 if (
7799 distance > 0 &&
7800 distsol.numberOfSolutions() > 0 &&
7801 prevtrackpar != trajectory.referenceParameters()
7802 ) {
7803 propdir = Trk::alongMomentum;
7804 }
7805
7807 ctx,
7808 *prevtrackpar,
7809 *states[hitno],
7810 propdir,
7811 trajectory.m_fieldprop,
7812 calcderiv,
7813 false
7814 );
7815
7816 if (
7817 propdir == Trk::alongMomentum &&
7818 (rv.m_parameters != nullptr) &&
7819 (prevtrackpar->position() - rv.m_parameters->position()).mag() > 5 * mm
7820 ) {
7821 ATH_MSG_DEBUG("Propagation in wrong direction");
7822
7823 }
7824
7825 if (rv.m_parameters == nullptr) {
7826 ATH_MSG_DEBUG("propagation failed, prev par: " << *prevtrackpar <<
7827 " pos: " << prevtrackpar->position() << " destination surface: " << surf1);
7829 }
7830
7831 states[hitno]->setTrackParameters(std::move(rv.m_parameters));
7832 const TrackParameters *currenttrackpar = states[hitno]->trackParameters();
7833 const Surface &surf = states[hitno]->associatedSurface();
7834
7835 if (rv.m_jacobian != std::nullopt) {
7836 if (
7837 states[hitno]->materialEffects() != nullptr &&
7838 states[hitno]->materialEffects()->deltaE() != 0 &&
7839 states[hitno]->materialEffects()->sigmaDeltaE() <= 0 &&
7840 !trajectory.m_straightline
7841 ) {
7842 const double p = 1. / std::abs(currenttrackpar->parameters()[Trk::qOverP]);
7843 const double de = std::abs(states[hitno]->materialEffects()->deltaE());
7844 const double mass = trajectory.mass();
7845 const double newp = std::sqrt(p * p + 2 * de * std::sqrt(mass * mass + p * p) + de * de);
7846 (*rv.m_jacobian) (4, 4) = ((p + p * de / std::sqrt(p * p + mass * mass)) / newp) * p * p / (newp * newp);
7847 }
7848
7849 states[hitno]->setJacobian(*rv.m_jacobian);
7850 } else if (calcderiv) {
7851 ATH_MSG_WARNING("Jacobian is null");
7853 }
7854
7855 GXFMaterialEffects *meff = states[hitno]->materialEffects();
7856
7857 if (meff != nullptr && hitno != 0) {
7858 std::variant<std::unique_ptr<const TrackParameters>, FitterStatusCode> r = updateEnergyLoss(
7859 surf, *meff, *states[hitno]->trackParameters(), trajectory.mass(), -1
7860 );
7861
7862 if (std::holds_alternative<FitterStatusCode>(r)) {
7863 return std::get<FitterStatusCode>(r);
7864 }
7865
7866 tmptrackpar = std::move(std::get<std::unique_ptr<const TrackParameters>>(r));
7867 prevtrackpar = tmptrackpar.get();
7868 } else {
7869 prevtrackpar = currenttrackpar;
7870 }
7871 }
7872
7873 prevtrackpar = trajectory.referenceParameters();
7874
7875 for (int hitno = nstatesupstream; hitno < (int) states.size(); hitno++) {
7876 const Surface &surf = states[hitno]->associatedSurface();
7878 const DistanceSolution distsol = surf.straightLineDistanceEstimate(prevtrackpar->position(), prevtrackpar->momentum().unit());
7879
7880 const double distance = getDistance(distsol);
7881
7882 if (distance < 0 && distsol.numberOfSolutions() > 0 && prevtrackpar != trajectory.referenceParameters()) {
7883 propdir = Trk::oppositeMomentum;
7884 }
7885
7887 ctx,
7888 *prevtrackpar,
7889 *states[hitno],
7890 propdir,
7891 trajectory.m_fieldprop,
7892 calcderiv,
7893 false
7894 );
7895
7896 if (
7897 (rv.m_parameters != nullptr) &&
7898 propdir == Trk::oppositeMomentum &&
7899 (prevtrackpar->position() - rv.m_parameters->position()).mag() > 5 * mm
7900 ) {
7901 ATH_MSG_DEBUG("Propagation in wrong direction");
7902 }
7903
7904 if (rv.m_parameters == nullptr) {
7905 ATH_MSG_DEBUG("propagation failed, prev par: " << *prevtrackpar <<
7906 " pos: " << prevtrackpar->
7907 position() << " destination surface: " << surf);
7909 }
7910
7911 if (rv.m_jacobian != std::nullopt) {
7912 if (
7913 states[hitno]->materialEffects() != nullptr &&
7914 states[hitno]->materialEffects()->deltaE() != 0 &&
7915 states[hitno]->materialEffects()->sigmaDeltaE() <= 0 &&
7916 !trajectory.m_straightline
7917 ) {
7918 const double p = 1 / std::abs(rv.m_parameters->parameters()[Trk::qOverP]);
7919 const double de = std::abs(states[hitno]->materialEffects()->deltaE());
7920 const double mass = trajectory.mass();
7921 double newp = p * p - 2 * de * std::sqrt(mass * mass + p * p) + de * de;
7922
7923 if (newp > 0) {
7924 newp = std::sqrt(newp);
7925 }
7926
7927 (*rv.m_jacobian) (4, 4) = ((p - p * de / std::sqrt(p * p + mass * mass)) / newp) * p * p / (newp * newp);
7928 }
7929
7930 states[hitno]->setJacobian(*rv.m_jacobian);
7931 } else if (calcderiv) {
7932 ATH_MSG_WARNING("Jacobian is null");
7934 }
7935
7936 GXFMaterialEffects *meff = states[hitno]->materialEffects();
7937
7938 if (meff != nullptr) {
7939 std::variant<std::unique_ptr<const TrackParameters>, FitterStatusCode> r = updateEnergyLoss(
7940 surf, *meff, *rv.m_parameters, trajectory.mass(), +1
7941 );
7942
7943 if (std::holds_alternative<FitterStatusCode>(r)) {
7944 return std::get<FitterStatusCode>(r);
7945 }
7946
7947 rv.m_parameters = std::move(std::get<std::unique_ptr<const TrackParameters>>(r));
7948 }
7949
7950 states[hitno]->setTrackParameters(std::move(rv.m_parameters));
7951 prevtrackpar = states[hitno]->trackParameters();
7952 }
7953
7955 }
7956
7957 std::variant<std::unique_ptr<const TrackParameters>, FitterStatusCode> GlobalChi2Fitter::updateEnergyLoss(
7958 const Surface & surf,
7959 const GXFMaterialEffects & meff,
7960 const TrackParameters & param,
7961 double mass,
7962 int sign
7963 ) const {
7964 const AmgVector(5) & old = param.parameters();
7965
7966 double newphi = old[Trk::phi0] + sign * meff.deltaPhi();
7967 double newtheta = old[Trk::theta] + sign * meff.deltaTheta();
7968
7969 if (!correctAngles(newphi, newtheta)) {
7970 ATH_MSG_DEBUG("Angles out of range, phi: " << newphi << " theta: " << newtheta);
7972 }
7973
7974 double newqoverp = 0;
7975
7976 if (meff.sigmaDeltaE() <= 0) {
7977 if (std::abs(old[Trk::qOverP]) < 1.e-12) {
7978 newqoverp = 0.;
7979 } else {
7980 const double oldp = std::abs(1 / old[Trk::qOverP]);
7981 const double newp2 = oldp * oldp - sign * 2 * std::abs(meff.deltaE()) * std::sqrt(mass * mass + oldp * oldp) + meff.deltaE() * meff.deltaE();
7982
7983 if (newp2 < 0) {
7984 ATH_MSG_DEBUG("Track killed by energy loss update");
7986 }
7987
7988 newqoverp = std::copysign(1 / std::sqrt(newp2), old[Trk::qOverP]);
7989 }
7990 } else {
7991 newqoverp = old[Trk::qOverP] + sign * .001 * meff.delta_p();
7992 }
7993
7994 return surf.createUniqueTrackParameters(
7995 old[0], old[1], newphi, newtheta, newqoverp, std::nullopt
7996 );
7997 }
7998
8000 const int nstatesupstream = trajectory.numberOfUpstreamStates();
8001 const int nscatupstream = trajectory.numberOfUpstreamScatterers();
8002 const int nbremupstream = trajectory.numberOfUpstreamBrems();
8003 const int nscats = trajectory.numberOfScatterers();
8004 const int nperpars = trajectory.numberOfPerigeeParameters();
8005 const int nfitpars = trajectory.numberOfFitParameters();
8006
8007 using Matrix55 = Eigen::Matrix<double, 5, 5>;
8008
8009 Matrix55 initialjac;
8010 initialjac.setZero();
8011 initialjac(4, 4) = 1;
8012
8013 Matrix55 jacvertex(initialjac);
8014
8015 std::vector<Matrix55, Eigen::aligned_allocator<Matrix55>> jacscat(trajectory.numberOfScatterers(), initialjac);
8016 std::vector<Matrix55, Eigen::aligned_allocator<Matrix55>> jacbrem(trajectory.numberOfBrems(), initialjac);
8017
8018 std::vector<std::unique_ptr<GXFTrackState>> & states = trajectory.trackStates();
8019 GXFTrackState *prevstate = nullptr;
8020 GXFTrackState *state = nullptr;
8021
8022 int hit_begin = 0;
8023 int hit_end = 0;
8024 int scatno = 0;
8025 int bremno = 0;
8026
8027 for (const bool forward : {false, true}) {
8028 if (forward) {
8029 hit_begin = nstatesupstream;
8030 hit_end = (int) states.size();
8031 scatno = nscatupstream;
8032 bremno = nbremupstream;
8033 } else {
8034 hit_begin = nstatesupstream - 1;
8035 hit_end = 0;
8036 scatno = trajectory.numberOfUpstreamScatterers() - 1;
8037 bremno = trajectory.numberOfUpstreamBrems() - 1;
8038 }
8039
8040 for (
8041 int hitno = hit_begin;
8042 forward ? (hitno < hit_end) : (hitno >= hit_end);
8043 hitno += (forward ? 1 : -1)
8044 ) {
8045
8046 state = states[hitno].get();
8047
8048 const bool fillderivmat = (!state->getStateType(TrackStateOnSurface::Scatterer) && !state->getStateType(TrackStateOnSurface::BremPoint));
8049
8050 if (fillderivmat && state->derivatives().cols() != nfitpars) {
8051 state->derivatives().resize(5, nfitpars);
8052 state->derivatives().setZero();
8053 }
8054
8055 int jminscat = 0;
8056 int jmaxscat = 4;
8057 int jminbrem = 0;
8058 const int jmaxbrem = 4;
8059
8060 if (hitno == (forward ? hit_end - 1 : 0)) {
8061 if (!fillderivmat) {
8062 break;
8063 }
8064 jminscat = 2;
8065 jmaxscat = 3;
8066 jminbrem = 4;
8067 }
8068
8069 Eigen::Matrix<double, 5, 5> & jac = state->jacobian();
8070
8071 if (hitno == nstatesupstream + (forward ? 0 : -1)) {
8072 jacvertex.block<4, 5>(0, 0) = jac.block<4, 5>(0, 0);
8073 jacvertex(4, 4) = jac(4, 4);
8074 } else {
8075 int jmin = 0;
8076 int jmax = 0;
8077 int jcnt = 0;
8078 int lp_bgn = 0;
8079 int lp_end = 0;
8080
8081 jmin = jminscat;
8082 jmax = jmaxscat;
8083 jcnt = jmax - jmin + 1;
8084
8085 lp_bgn = forward ? nscatupstream : nscatupstream - 1;
8086 lp_end = scatno;
8087
8088 for (int i = lp_bgn; forward ? (i < lp_end) : (i > lp_end); i += (forward ? 1 : -1)) {
8089 if (
8090 i == scatno + (forward ? -1 : 1) &&
8091 prevstate != nullptr &&
8093 (!trajectory.prefit() || prevstate->materialEffects()->deltaE() == 0)
8094 ) {
8095 jacscat[i].block(0, jmin, 4, jcnt) = jac.block(0, jmin, 4, jcnt);
8096 jacscat[i](4, 4) = jac(4, 4);
8097 } else {
8098 calculateJac(jac, jacscat[i], jmin, jmax);
8099 }
8100
8101 if (fillderivmat) {
8102 Eigen::MatrixXd & derivmat = state->derivatives();
8103 const int scatterPos = nperpars + 2 * i;
8104
8105 derivmat.block<4, 2>(0, scatterPos) = (forward ? 1 : -1) * jacscat[i].block<4, 2>(0, 2);
8106 }
8107 }
8108
8109 jmin = jminbrem;
8110 jmax = jmaxbrem;
8111 jcnt = jmax - jmin + 1;
8112
8113 lp_bgn = forward ? nbremupstream : nbremupstream - 1;
8114 lp_end = bremno;
8115
8116 for (int i = lp_bgn; forward ? (i < lp_end) : (i > lp_end); i += (forward ? 1 : -1)) {
8117 if (
8118 i == bremno + (forward ? -1 : 1) &&
8119 prevstate &&
8120 prevstate->materialEffects() &&
8121 prevstate->materialEffects()->sigmaDeltaE() > 0
8122 ) {
8123 jacbrem[i].block(0, jmin, 4, jcnt) = jac.block(0, jmin, 4, jcnt);
8124 jacbrem[i](4, 4) = jac(4, 4);
8125 } else {
8126 calculateJac(jac, jacbrem[i], jmin, jmax);
8127 }
8128
8129 if (fillderivmat) {
8130 Eigen::MatrixXd & derivmat = state->derivatives();
8131 const int scatterPos = nperpars + 2 * nscats + i;
8132
8133 derivmat.block<5, 1>(0, scatterPos) = (forward ? .001 : -.001) * jacbrem[i].block<5, 1>(0, 4);
8134 }
8135 }
8136
8137 calculateJac(jac, jacvertex, 0, 4);
8138 }
8139
8140 if (fillderivmat) {
8141 Eigen::MatrixXd & derivmat = state->derivatives();
8142 derivmat.block(0, 0, 4, nperpars) = jacvertex.block(0, 0, 4, nperpars);
8143
8144 if (nperpars == 5) {
8145 derivmat.col(4).segment(0, 4) *= .001;
8146 derivmat(4, 4) = .001 * jacvertex(4, 4);
8147 }
8148 }
8149
8150 if (
8152 (!trajectory.prefit() || states[hitno]->materialEffects()->deltaE() == 0)
8153 ) {
8154 scatno += (forward ? 1 : -1);
8155 }
8156
8157 if (
8158 states[hitno]->materialEffects() &&
8159 states[hitno]->materialEffects()->sigmaDeltaE() > 0
8160 ) {
8161 bremno += (forward ? 1 : -1);
8162 }
8163
8164 prevstate = states[hitno].get();
8165 }
8166 }
8167 }
8168
8169 void
8170
8172 Amg::SymMatrixX & fullcovmat,
8173 bool onlylocal) const {
8174 //
8175 // Calculate track errors at each state, except scatterers and brems
8176 //
8177 ATH_MSG_DEBUG("CalculateTrackErrors");
8178
8179 std::vector<std::unique_ptr<GXFTrackState>> & states = trajectory.trackStates();
8180 const int nstatesupstream = trajectory.numberOfUpstreamStates();
8181 std::vector < int >indices(states.size());
8182 GXFTrackState *prevstate = nullptr;
8183 int i = nstatesupstream;
8184 for (int j = 0; j < (int) states.size(); j++) {
8185 if (j < nstatesupstream) {
8186 i--;
8187 indices[j] = i;
8188 } else {
8189 indices[j] = j;
8190 }
8191 }
8192 for (int stateno = 0; stateno < (int) states.size(); stateno++) {
8193 if (stateno == 0 || stateno == nstatesupstream) {
8194 prevstate = nullptr;
8195 }
8196 const int index = indices[stateno];
8197 std::unique_ptr<GXFTrackState> & state = states[index];
8198 if (state->materialEffects() != nullptr) {
8199 prevstate = state.get();
8200 continue;
8201 }
8202
8203 if (!state->hasTrackCovariance()) {
8204 state->zeroTrackCovariance();
8205 }
8206 AmgMatrix(5, 5) & trackerrmat = state->trackCovariance();
8207
8208 if ((prevstate != nullptr) &&
8211 && !onlylocal) {
8212 Eigen::Matrix<double, 5, 5> & jac = state->jacobian();
8213 const AmgMatrix(5, 5)& prevcov = states[indices[stateno - 1]]->trackCovariance();
8214
8215 trackerrmat = jac * prevcov * jac.transpose();
8216 } else {
8217 Amg::MatrixX & derivatives = state->derivatives();
8218
8219 trackerrmat = derivatives * fullcovmat * derivatives.transpose();
8220 }
8221
8222 if (!onlylocal) {
8223 const MeasurementBase *measurement = state->measurement();
8224 const Amg::MatrixX & meascov = measurement->localCovariance();
8225 int j = 0;
8226 int indices[5] = {
8227 -1, -1, -1, -1, -1
8228 };
8229 bool errorok = true;
8230 for (int i = 0; i < 5; i++) {
8232 if (state->getStateType(TrackStateOnSurface::Measurement)
8233 && trackerrmat(i, i) > meascov(j, j)) {
8234 errorok = false;
8235 const double scale = std::sqrt(meascov(j, j) / trackerrmat(i, i));
8236 trackerrmat(i, i) = meascov(j, j);
8237 for (int k = 0; k < 5; k++) {
8238 if (k != i) {
8239 trackerrmat(k, i) *= scale;
8240 }
8241 }
8242 indices[i] = j;
8243 }
8244 j++;
8245 }
8246 }
8247 for (int i = 0; i < 5; i++) {
8248 if (indices[i] == -1) {
8249 continue;
8250 }
8251 for (int j = 0; j < 5; j++) {
8252 if (indices[j] == -1) {
8253 continue;
8254 }
8255 trackerrmat(i, j) = meascov(indices[i], indices[j]);
8256 }
8257 }
8258 if (trajectory.m_straightline) {
8259 trackerrmat(4, 4) = 1e-20;
8260 }
8261
8262 const TrackParameters *tmptrackpar =
8263 state->trackParameters();
8264
8265 std::optional<AmgMatrix(5, 5)> trkerrmat;
8266
8267 if (state->hasTrackCovariance()) {
8268 trkerrmat = (state->trackCovariance());
8269 } else {
8270 trkerrmat = std::nullopt;
8271 }
8272
8273 const AmgVector(5) & tpars = tmptrackpar->parameters();
8274 std::unique_ptr<const TrackParameters> trackpar(
8275 tmptrackpar->associatedSurface().createUniqueTrackParameters(tpars[0],
8276 tpars[1],
8277 tpars[2],
8278 tpars[3],
8279 tpars[4],
8280 std::move(trkerrmat))
8281 );
8282 state->setTrackParameters(std::move(trackpar));
8283 FitQualityOnSurface fitQual{};
8284 if (state->getStateType(TrackStateOnSurface::Measurement)) {
8285 if (errorok && trajectory.nDOF() > 0) {
8286 fitQual = m_updator->fullStateFitQuality(
8287 *state->trackParameters(),
8288 measurement->localParameters(),
8289 measurement->localCovariance()
8290 );
8291 } else {
8292 fitQual = FitQualityOnSurface(0, state->numberOfMeasuredParameters());
8293 }
8294 }
8295 state->setFitQuality(fitQual);
8296 }
8297 prevstate = state.get();
8298 }
8299 }
8300
8301 std::optional<TransportJacobian>
8303 const EventContext& ctx,
8304 const TrackParameters* prevpar,
8305 const Surface & surf,
8306 PropDirection propdir,
8307 const MagneticFieldProperties& fieldprop) const
8308 {
8309 double J[25] = {
8310 1, 0, 0, 0, 0,
8311 0, 1, 0, 0, 0,
8312 0, 0, 1, 0, 0,
8313 0, 0, 0, 1, 0,
8314 0, 0, 0, 0, 1
8315 };
8316 std::optional<TransportJacobian> jac = std::make_optional<TransportJacobian>(J);
8317 const TrackParameters *tmpprevpar = prevpar;
8318 double eps[5] = {
8319 0.01, 0.01, 0.00001, 0.00001, 0.000000001
8320 };
8321
8322 const AmgVector(5) & vec = tmpprevpar->parameters();
8323
8324 const bool cylsurf = surf.type() == Trk::SurfaceType::Cylinder;
8325 const bool discsurf = surf.type() == Trk::SurfaceType::Disc;
8326 const Surface & previousSurface = tmpprevpar->associatedSurface();
8327 const bool thiscylsurf = previousSurface.type() == Trk::SurfaceType::Cylinder;
8328 const bool thisdiscsurf = previousSurface.type() == Trk::SurfaceType::Disc;
8329
8330 for (int i = 0; i < 5; i++) {
8331 AmgVector(5) vecpluseps = vec, vecminuseps = vec;
8332
8333 if (thisdiscsurf && i == 1) {
8334 eps[i] /= vec[0];
8335 }
8336
8337 vecpluseps[Trk::ParamDefsAccessor::pardef[i]] += eps[i];
8338 vecminuseps[Trk::ParamDefsAccessor::pardef[i]] -= eps[i];
8339 if (i == 0 && thiscylsurf) {
8340 vecminuseps[i] = -std::remainder(-vecminuseps[i], 2 * M_PI * previousSurface.bounds().r());
8341 } else if (i == 1 && thisdiscsurf) {
8342 vecpluseps[i] = -std::remainder(-vecpluseps[i], 2 * M_PI);
8343 }
8344 correctAngles(vecminuseps[Trk::phi], vecminuseps[Trk::theta]);
8345 correctAngles(vecpluseps[Trk::phi], vecpluseps[Trk::theta]);
8346
8347 std::unique_ptr<const TrackParameters> parpluseps(
8349 vecpluseps[0],
8350 vecpluseps[1],
8351 vecpluseps[2],
8352 vecpluseps[3],
8353 vecpluseps[4],
8354 std::nullopt
8355 )
8356 );
8357 const std::unique_ptr<const TrackParameters> parminuseps(
8359 vecminuseps[0],
8360 vecminuseps[1],
8361 vecminuseps[2],
8362 vecminuseps[3],
8363 vecminuseps[4],
8364 std::nullopt
8365 )
8366 );
8367
8368 std::unique_ptr<const TrackParameters> newparpluseps(
8369 m_propagator->propagateParameters(
8370 ctx,
8371 *parpluseps,
8372 surf,
8373 propdir,
8374 false,
8375 fieldprop,
8377 )
8378 );
8379 std::unique_ptr<const TrackParameters> newparminuseps(
8380 m_propagator->propagateParameters(
8381 ctx,
8382 *parminuseps,
8383 surf,
8384 propdir,
8385 false,
8386 fieldprop,
8388 )
8389 );
8390
8391 const PropDirection propdir2 =
8392 (propdir ==
8394 if (newparpluseps == nullptr) {
8395 newparpluseps =
8396 m_propagator->propagateParameters(
8397 ctx,
8398 *parpluseps,
8399 surf,
8400 propdir2,
8401 false,
8402 fieldprop,
8404 );
8405 }
8406 if (newparminuseps == nullptr) {
8407 newparminuseps =
8408 m_propagator->propagateParameters(
8409 ctx,
8410 *parminuseps,
8411 surf,
8412 propdir2,
8413 false,
8414 fieldprop,
8416 );
8417 }
8418 if ((newparpluseps == nullptr) || (newparminuseps == nullptr)) {
8419 return nullptr;
8420 }
8421
8422 for (int j = 0; j < 5; j++) {
8423 double diff = newparpluseps->parameters()[Trk::ParamDefsAccessor::pardef[j]] -
8424 newparminuseps->parameters()[Trk::ParamDefsAccessor::pardef[j]];
8425
8426 if (j == 0 && cylsurf) {
8427 diff = -std::remainder(-diff, 2 * M_PI * surf.bounds().r());
8428 } else if (j == 1 && discsurf) {
8429 diff = -std::remainder(-diff, 2 * M_PI);
8430 }
8431
8432 (*jac) (j, i) = diff / (2 * eps[i]);
8433 }
8434
8435 }
8436 return jac;
8437 }
8438
8439 int
8441 return 0;
8442 } void
8445 ("Configure the minimum number of Iterations via jobOptions");
8446 }
8447
8448 bool
8450 if (theta > M_PI) {
8451 theta = M_PI - theta;
8452 phi += M_PI;
8453 }
8454 if (theta < 0) {
8455 theta = -theta;
8456 phi += M_PI;
8457 }
8458
8459 phi = -std::remainder(-phi, 2 * M_PI);
8460
8461 return theta >= 0 && theta <= M_PI && phi >= -M_PI && phi <= M_PI;
8462 }
8463
8464 bool
8465 GlobalChi2Fitter::isMuonTrack(const Track & intrk1) const {
8466 const auto *pDataVector = intrk1.measurementsOnTrack();
8467 auto nmeas1 = pDataVector->size();
8468 const auto *pLastValue = (*pDataVector)[nmeas1 - 1];
8469 //
8470 const bool lastMeasIsRIO = pLastValue->type(Trk::MeasurementBaseType::RIO_OnTrack);
8471 const bool lastMeasIsCompetingRIO = pLastValue->type(Trk::MeasurementBaseType::CompetingRIOsOnTrack);
8472 //we only need the RIO on track pointer to be valid to identify
8473 const RIO_OnTrack *testrot{};
8474 //
8475 if (lastMeasIsRIO){
8476 testrot = static_cast<const RIO_OnTrack *>(pLastValue);
8477 } else {
8478 if (lastMeasIsCompetingRIO){
8479 const auto *testcrot = static_cast<const CompetingRIOsOnTrack*>(pLastValue);
8480 testrot = &testcrot->rioOnTrack(0);
8481 }
8482 }
8483 //still undefined, so try penultimate measurement as well
8484 if (testrot == nullptr) {
8485 const auto *pPenultimate = (*pDataVector)[nmeas1 - 2];
8486 const bool penultimateIsRIO = pPenultimate->type(Trk::MeasurementBaseType::RIO_OnTrack);
8487 const bool penultimateIsCompetingRIO = pPenultimate->type(Trk::MeasurementBaseType::CompetingRIOsOnTrack);
8488 if(penultimateIsRIO){
8489 testrot = static_cast<const RIO_OnTrack *>(pPenultimate);
8490 } else {
8491 if (penultimateIsCompetingRIO){
8492 const auto *testcrot = static_cast<const CompetingRIOsOnTrack*>(pPenultimate);
8493 testrot = &testcrot->rioOnTrack(0);
8494 }
8495 }
8496 }
8497 //check: we've successfully got a valid RIO on track; it's not the inner detector;
8498 //it's really the muon detector (question: doesn't that make the previous check redundant?)
8499 return (
8500 (testrot != nullptr) &&
8501 !m_DetID->is_indet(testrot->identify()) &&
8502 m_DetID->is_muon(testrot->identify())
8503 );
8504 }
8505
8506 void
8507 GlobalChi2Fitter::initFieldCache(const EventContext& ctx, Cache& cache)
8508 const
8509 {
8512 ctx
8513 );
8514
8515 const AtlasFieldCacheCondObj * cond_obj(*rh);
8516
8517 if (cond_obj == nullptr) {
8518 ATH_MSG_ERROR("Failed to create AtlasFieldCacheCondObj!");
8519 return;
8520 }
8521
8522 cond_obj->getInitializedCache(cache.m_field_cache);
8523 }
8524
8526 std::stringstream msg;
8527 msg << "Failed to get conditions data " << m_trackingGeometryReadKey.key() << ".";
8528 throw std::runtime_error(msg.str());
8529 }
8530
8531 bool GlobalChi2Fitter::ensureValidEntranceCalo(const EventContext& ctx, Cache& cache) const {
8532 if (cache.m_caloEntrance == nullptr) {
8533 const TrackingGeometry *geometry = trackingGeometry(cache, ctx);
8534
8535 if (geometry != nullptr) {
8536 cache.m_caloEntrance = geometry->trackingVolume("InDet::Containers::InnerDetector");
8537 } else {
8538 ATH_MSG_ERROR("Tracking Geometry not available");
8539 }
8540
8541 /*
8542 * Check, if we managed to find an entrance.
8543 */
8544 if (cache.m_caloEntrance == nullptr) {
8545 ATH_MSG_ERROR("calo entrance not available");
8546 }
8547 }
8548
8549 return cache.m_caloEntrance != nullptr;
8550 }
8551
8552 bool GlobalChi2Fitter::ensureValidEntranceMuonSpectrometer(const EventContext& ctx, Cache& cache) const {
8553 if (cache.m_msEntrance == nullptr) {
8554 const TrackingGeometry *geometry = trackingGeometry(cache, ctx);
8555
8556 if (geometry != nullptr) {
8557 cache.m_msEntrance = geometry->trackingVolume("MuonSpectrometerEntrance");
8558 } else {
8559 ATH_MSG_ERROR("Tracking Geometry not available");
8560 }
8561
8562 /*
8563 * Check, if we managed to find an entrance.
8564 */
8565 if (cache.m_msEntrance == nullptr) {
8566 ATH_MSG_ERROR("MS entrance not available");
8567 }
8568 }
8569
8570 return cache.m_msEntrance != nullptr;
8571 }
8572}
#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)
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
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.
bool solenoidOn() const
status of the magnets
void getFieldZR(const double *ATH_RESTRICT xyz, double *ATH_RESTRICT bxyz, double *ATH_RESTRICT deriv=nullptr)
get B field valaue on the z-r plane at given position works only inside the solenoid.
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 Track * alignmentFit(AlignmentCache &, const Track &, const RunOutlierRemoval runOutlier=false, const ParticleHypothesis matEffects=Trk::nonInteracting) const override
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
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:55
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.
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.
bool inside(const Amg::Vector3D &gp, double tol=0.) const
Inside() method for checks.
Definition Volume.cxx:72
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
ParametersBase< TrackParametersDim, Charged > TrackParameters
ParametersT< TrackParametersDim, Charged, PlaneSurface > AtaPlane
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[
std::vector< std::unique_ptr< const std::vector< const TrackStateOnSurface * >, void(*)(const std::vector< const TrackStateOnSurface * > *) > > m_matTempStore
void incrementFitStatus(enum FitterStatusType status)
std::vector< const Trk::Layer * > m_posdiscs
std::vector< int > m_lastmeasurement
std::vector< const Trk::Layer * > m_negdiscs
std::vector< const Trk::Layer * > m_barrelcylinders
MagField::AtlasFieldCache m_field_cache
static void objVectorDeleter(const std::vector< const T * > *ptr)
std::vector< int > m_firstmeasurement
std::array< unsigned int, S_MAX_VALUE > m_fit_status
std::vector< double > m_phiweight
const TrackingVolume * m_msEntrance
const TrackingVolume * m_caloEntrance
static constexpr std::array< ParamDefs, 6 > pardef
Constructor.
Definition ParamDefs.h:94
MsgStream & msg
Definition testRead.cxx:32