ATLAS Offline Software
Loading...
Searching...
No Matches
CombinedMuonTrackBuilder.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
3*/
4
6// CombinedMuonTrackBuilder
7// AlgTool gathering material effects along a combined muon track, in
8// particular the TSOS'es representing the calorimeter energy deposit and
9// Coulomb scattering.
10// The resulting track is fitted at the IP
11//
13
15
16#include <cmath>
17#include <iomanip>
18#include <memory>
20#include "AthenaKernel/Units.h"
41#include "TrkSurfaces/Surface.h"
43#include "TrkTrack/Track.h"
47#include "VxVertex/RecVertex.h"
50
51namespace {
53 const double s_sigmaPhiSector = std::tan(0.125 * M_PI / std::sqrt(12.));
54
55}
56namespace Rec {
58 CombinedMuonTrackBuilder::CombinedMuonTrackBuilder(const std::string& type, const std::string& name, const IInterface* parent) :
59 CombinedMuonTrackFitter(type, name, parent) {
60 declareInterface<ICombinedMuonTrackBuilder>(this);
61 }
62
65 ATH_MSG_DEBUG("Initializing CombinedMuonTrackBuilder.");
66 ATH_CHECK(m_caloEnergyParam.retrieve());
67 ATH_MSG_DEBUG("Retrieved tool " << m_caloEnergyParam);
68
69 m_redoRots = !m_cscRotCreator.empty() || !m_muClusterRotCreator.empty() || !m_mdtRotCreator.empty();
70 ATH_CHECK(m_cscRotCreator.retrieve(DisableTool{m_cscRotCreator.empty()}));
71 ATH_CHECK(m_muClusterRotCreator.retrieve(DisableTool{m_muClusterRotCreator.empty()}));
72 ATH_CHECK(m_mdtRotCreator.retrieve(DisableTool{m_mdtRotCreator.empty()}));
73
74 ATH_CHECK(m_materialAllocator.retrieve(DisableTool{m_materialAllocator.empty()}));
75 ATH_CHECK(m_extrapolator.retrieve());
76 ATH_MSG_DEBUG("Retrieved tool " << m_extrapolator);
77
78 ATH_CHECK(m_muonHoleRecovery.retrieve(DisableTool{m_muonHoleRecovery.empty()}));
79
80
81 ATH_CHECK(m_propagator.retrieve());
82 ATH_MSG_DEBUG("Retrieved tool " << m_propagator);
83 ATH_CHECK(m_propagatorSL.retrieve());
84 ATH_MSG_DEBUG("Retrieved tool " << m_propagatorSL);
85
86
88
89
90 // create beamAxis and vertexRegion for constrained (projective) track fits
91 Amg::Vector3D origin(0., 0., 0.);
92 m_perigeeSurface = std::make_unique<Trk::PerigeeSurface>(origin);
93
94 AmgSymMatrix(3) beamAxisCovariance;
95 beamAxisCovariance.setZero();
96 (beamAxisCovariance)(0, 0) = m_vertex2DSigmaRPhi * m_vertex2DSigmaRPhi;
97 (beamAxisCovariance)(1, 1) = m_vertex2DSigmaRPhi * m_vertex2DSigmaRPhi;
98 (beamAxisCovariance)(2, 2) = m_vertex2DSigmaZ * m_vertex2DSigmaZ;
99 m_beamAxis = std::make_unique<Trk::RecVertex>(origin, beamAxisCovariance);
100
101 AmgSymMatrix(3) vertexRegionCovariance;
102 vertexRegionCovariance.setZero();
103 (vertexRegionCovariance)(0, 0) = m_vertex3DSigmaRPhi * m_vertex3DSigmaRPhi;
104 (vertexRegionCovariance)(1, 1) = m_vertex3DSigmaRPhi * m_vertex3DSigmaRPhi;
105 (vertexRegionCovariance)(2, 2) = m_vertex3DSigmaZ * m_vertex3DSigmaZ;
106 m_vertex = std::make_unique<Trk::RecVertex>(origin, vertexRegionCovariance);
107 ATH_CHECK(m_alignUncertTool_theta.retrieve(DisableTool{!m_addIDMSerrors}));
108 ATH_CHECK(m_alignUncertTool_phi.retrieve(DisableTool{!m_addIDMSerrors}));
109
110#ifndef NDEBUG
111 ATH_MSG_DEBUG(" vertex region: ");
112 m_vertex->dump(msg(MSG::DEBUG));
113#endif
114 return StatusCode::SUCCESS;
115 }
116
119 double norm = 100. / static_cast<double>(m_countAcceptedStandaloneFit);
120 ATH_MSG_INFO("Finalizing CombinedMuonTrackBuilder:"
121 << endmsg << " " << std::setiosflags(std::ios::fixed) << std::setw(4) << std::setprecision(2)
122 << norm * static_cast<double>(m_countBeamAxis) << "% with beamAxis constraint" << endmsg << " " << std::setw(4)
123 << std::setprecision(2) << norm * static_cast<double>(m_countVertexRegion) << "% with vertexRegion constraint"
124 << endmsg << " " << m_countDegradedStandaloneFit << " degraded standalone fit-chi2 ");
125 }
127 }
128 std::unique_ptr<Trk::Track> CombinedMuonTrackBuilder::combinedFit(const EventContext& ctx, const Trk::Track& indetTrack, const Trk::Track& extrapolatedTrack,
129 const Trk::Track&) const {
130 ATH_MSG_VERBOSE("===== Start of combinedFit:: ");
131
132 if (msgLevel(MSG::DEBUG)) countAEOTs(extrapolatedTrack, " extrapolatedTrack start combinedFit ");
133
134 // require MeasuredPerigee for indetTrack
135 const Trk::Perigee* indetPerigee = indetTrack.perigeeParameters();
136 if (!indetPerigee) {
137 // missing MeasuredPerigee for indet track
138 m_messageHelper->printWarning(0);
139 return nullptr;
140 }
141
142 // take inner calorimeter scattering surface from extrapolated track
143 const Trk::Surface* surface = nullptr;
144 if (m_trackQuery->isCaloAssociated(extrapolatedTrack, ctx)) {
145 for (const Trk::TrackStateOnSurface* it : *extrapolatedTrack.trackStateOnSurfaces()) {
146 if (!it->materialEffectsOnTrack()) continue;
147
148 const Amg::Vector3D& position = it->materialEffectsOnTrack()->associatedSurface().globalReferencePoint();
149
150 if (m_indetVolume->inside(position)) continue;
151 if (!m_calorimeterVolume->inside(position)) break;
152
153 surface = &it->materialEffectsOnTrack()->associatedSurface();
154 break;
155 }
156 }
157
158 // match extrapolated indet track to inner calorimeter scattering surface
159 // provided momentum defined (solenoid on)
160 MagField::AtlasFieldCache fieldCache;
161 // Get field cache object
162 if (!loadMagneticField(ctx, fieldCache)) return nullptr;
163
164 if (surface && fieldCache.solenoidOn() && !m_updateWithCaloTG) {
165 std::unique_ptr<const Trk::TrackStateOnSurface> innerTSOS;
166 if (m_useCaloTG) {
167 ATH_MSG_VERBOSE(" Retrieving Calorimeter TSOS from " << __func__ << " at line " << __LINE__);
168 std::vector<std::unique_ptr<const Trk::TrackStateOnSurface>> caloTSOS =
169 getCaloTSOSfromMatProvider(*indetTrack.perigeeParameters(), extrapolatedTrack);
170 if (!caloTSOS.empty()) { innerTSOS.swap(caloTSOS.front()); }
171 } else {
172 innerTSOS = m_caloTSOS->innerTSOS(ctx, *indetTrack.perigeeParameters());
173 }
174
175 if (!innerTSOS) {
176 ATH_MSG_DEBUG(" indet track fails to intersect the calorimeter ");
177 return nullptr;
178 }
179
180 // will re-associate the calorimeter if they are not the same surface
181 double surfaceOffset =
182 (surface->globalReferencePoint() - innerTSOS->materialEffectsOnTrack()->associatedSurface().globalReferencePoint()).mag();
183
184 if (surfaceOffset > 1. * Gaudi::Units::mm) {
185 ATH_MSG_DEBUG(" different inner-calo-surface obtained from indet extrapolation, "
186 << "surface reference " << surface->globalReferencePoint() << " with offset " << surfaceOffset
187 << "mm. Re-evaluate the caloTSOS ");
188
189 surface = nullptr;
190 }
191 }
192
193 std::unique_ptr<Trk::Track> muonTrack;
194 if (!fieldCache.toroidOn()) {
195 ATH_MSG_VERBOSE(" SL MS track: Calling createMuonTrack from " << __func__ << " at line " << __LINE__);
196 muonTrack =
197 createMuonTrack(ctx, indetTrack, indetTrack.perigeeParameters(), nullptr, extrapolatedTrack.trackStateOnSurfaces());
198 } else {
199 // create a muon track without perigee in case of non-optimal precision -
200 // such as need to replace calorimeter material or presence of pseudomeasurements
201 if (!surface) { // extrapolate outwards to associate calorimeter material effects
202 ATH_MSG_VERBOSE("Calling createMuonTrack from " << __func__ << " at line " << __LINE__);
203 muonTrack = createMuonTrack(
204 ctx, extrapolatedTrack, indetTrack.perigeeParameters(), nullptr, extrapolatedTrack.trackStateOnSurfaces());
205 } else if (m_trackQuery->numberPseudoMeasurements(extrapolatedTrack) > 1) { // remove pseudo meas
206 ATH_MSG_VERBOSE("Calling createMuonTrack from " << __func__ << " at line " << __LINE__);
207 muonTrack = createMuonTrack(ctx, extrapolatedTrack,
208 nullptr, nullptr, extrapolatedTrack.trackStateOnSurfaces());
209 } else { // otherwise can just copy the extrapolated track
210 ATH_MSG_VERBOSE("Calling createMuonTrack from " << __func__ << " at line " << __LINE__);
211 muonTrack =
212 createMuonTrack(ctx, extrapolatedTrack, extrapolatedTrack.perigeeParameters(), nullptr,
213 extrapolatedTrack.trackStateOnSurfaces());
214 }
215 }
216
217 // no combined muon when failure to intersect calo
218 if (!muonTrack) return nullptr;
219
220 if (msgLevel(MSG::DEBUG)) countAEOTs(*muonTrack, " muonTrack track before fit ");
221
222 // combined track fit
223 std::unique_ptr<Trk::Track> combinedTrack{fit(ctx, indetTrack, *muonTrack, m_cleanCombined, Trk::muon)};
224
225 // quit if fit failure or all MS measurements removed by fit or perigee outside indet
226 bool haveMS {false}, perigeeOutside{false};
227
228 if (combinedTrack) {
229 if (msgLevel(MSG::DEBUG)) countAEOTs(*combinedTrack, " combinedTrack track after fit ");
230 ATH_MSG_VERBOSE(" combined track " << m_printer->print(*combinedTrack) << std::endl
231 << m_printer->printStations(*combinedTrack));
232
233 auto rit = combinedTrack->trackStateOnSurfaces()->rbegin();
234 auto ritEnd = combinedTrack->trackStateOnSurfaces()->rend();
235 for (; rit != ritEnd; ++rit) {
236 if (!(**rit).measurementOnTrack() || !(**rit).trackParameters()) continue;
237 if (m_calorimeterVolume->inside((**rit).trackParameters()->position())) break;
238 if (!(**rit).type(Trk::TrackStateOnSurface::Outlier)) haveMS = true;
239 }
240
241 if (!haveMS) {
242 // combinedTrack fails: MS removed by cleaner
243 ATH_MSG_DEBUG("combinedFit:: fail with MS removed by cleaner"); //Used to be: m_messageHelper->printWarning(1);
244 }
245
246 if (!combinedTrack->perigeeParameters() || !m_indetVolume->inside(combinedTrack->perigeeParameters()->position())) {
247 if (!combinedTrack->perigeeParameters()) {
248 ATH_MSG_DEBUG(" no perigee");
249 } else {
250 ATH_MSG_DEBUG(" position: r " << combinedTrack->perigeeParameters()->position().perp() << " z "
251 << combinedTrack->perigeeParameters()->position().z());
252 }
253 // combinedTrack fails as perigee outside indet
254 m_messageHelper->printWarning(2);
255 perigeeOutside = true;
256 }
257 }
258
259 if (!combinedTrack || !combinedTrack->fitQuality() || !haveMS || perigeeOutside) {
260 bool hasFitQ = combinedTrack ? (combinedTrack->fitQuality() != nullptr) : false;
261 ATH_MSG_DEBUG("combinedTrack fails with bad fit" << combinedTrack.get() << " " << hasFitQ << " " << haveMS << " "
262 << perigeeOutside);
263
264 return nullptr;
265 }
266
267 // Get parameters at calo position
268 const Trk::TrackParameters* combinedEnergyParameters{nullptr}, *muonEnergyParameters{nullptr};
269 const CaloEnergy* caloEnergy {caloEnergyParameters(combinedTrack.get(), muonTrack.get(), combinedEnergyParameters, muonEnergyParameters)};
270
271 if (!caloEnergy) {
272 // combinedTrack fails with missing caloEnergy
273 m_messageHelper->printWarning(3);
274 return nullptr;
275 }
276
277 // if significant momentum change: re-evaluate calo energy and refit
278 double pRatio = muonEnergyParameters->momentum().mag() / combinedEnergyParameters->momentum().mag();
279 if (std::abs(pRatio - 1.) > m_largeMomentumChange || m_iterateCombinedTrackFit) {
281 ATH_MSG_DEBUG(" iterate combined fit to recollect calorimeter material as significant momentum change after fit "
282 << pRatio << ", pT before " << muonEnergyParameters->momentum().perp() / Gaudi::Units::GeV << ", after "
283 << combinedEnergyParameters->momentum().perp() / Gaudi::Units::GeV << " GeV");
284 } else {
285 ATH_MSG_DEBUG(" iterate combined fit to recollect calorimeter material");
286 }
287
288 const Trk::TrackStates* combinedTSOS = combinedTrack->trackStateOnSurfaces();
289
290 std::unique_ptr<Trk::Track> indetNewTrack{createIndetTrack(indetTrack.info(), combinedTSOS)};
291
292 std::unique_ptr<Trk::Track> oldTrack(std::move(muonTrack));
293
294 ATH_MSG_VERBOSE("Calling createMuonTrack from " << __func__ << " at line " << __LINE__);
295 muonTrack = createMuonTrack(ctx, extrapolatedTrack, combinedEnergyParameters, nullptr, combinedTSOS);
296
297 if (indetNewTrack && muonTrack) {
298 std::unique_ptr<Trk::Track> refittedTrack{fit(ctx, *indetNewTrack, *muonTrack, m_cleanCombined, Trk::muon)};
299 caloEnergy = caloEnergyParameters(refittedTrack.get(), muonTrack.get(), combinedEnergyParameters, muonEnergyParameters);
300
301 if (caloEnergy) {
302 combinedTrack.swap(refittedTrack);
303 } else {
304 // why does the refit fail? This shouldn't really be necessary
305 muonTrack.swap(oldTrack);
306 caloEnergy = caloEnergyParameters(combinedTrack.get(), muonTrack.get(), combinedEnergyParameters, muonEnergyParameters);
307 }
308 }
309 }
310
311 // tracks with caloEnergy type 'tail' can arise from an incorrect categorization as isolated
312 // in case of significant energy gain, switch to parametrization and reclassify as NotIsolated
313 if (muonTrack && caloEnergy->energyLossType() == CaloEnergy::Tail && (!m_updateWithCaloTG || m_useCaloTG)) {
314 double tolerance = 0;
315
316 if (!indetPerigee->covariance()) {
317 ATH_MSG_WARNING(" indetPerigee has no covariance tolerance left as zero. ");
318 } else {
319 tolerance = m_numberSigmaFSR * Amg::error((*indetPerigee->covariance()), Trk::qOverP);
320 }
321
322 double indetMaxE = 1. / (std::abs(indetPerigee->parameters()[Trk::qOverP]) - tolerance);
323 double energyBalance = combinedEnergyParameters->momentum().mag() + caloEnergy->deltaE() - indetMaxE;
324
325 // get parametrised eloss if large energy imbalance and refit track
326 std::unique_ptr<CaloEnergy> paramEnergy;
327 if (indetMaxE > 0. && energyBalance > m_numberSigmaFSR * caloEnergy->sigmaMinusDeltaE()) {
328 // parametrized energy deposition
329 // run-2 schema, update default eloss with parametrised value
330 if (m_useCaloTG) {
331 paramEnergy.reset(m_materialUpdator->getParamCaloELoss(muonTrack.get()));
332 } else {
333 // run-1 schema, recalculate parametrised eloss
334 paramEnergy = m_caloEnergyParam->energyLoss(ctx, combinedEnergyParameters->momentum().mag(),
335 combinedEnergyParameters->position().eta(),
336 combinedEnergyParameters->position().phi());
337 }
338 paramEnergy->set_energyLossType(CaloEnergy::NotIsolated);
339 }
340
341 // FIXME: add criterion on energy-balance significance param vs tail ?
342 if (paramEnergy) {
343 ATH_MSG_DEBUG(" FSR check: energyBalance "
344 << energyBalance / Gaudi::Units::GeV << " signif " << energyBalance / caloEnergy->sigmaMinusDeltaE()
345 << " indet max E " << indetMaxE / Gaudi::Units::GeV << std::endl
346 << " param CaloEnergy: " << paramEnergy->deltaE() / Gaudi::Units::GeV << " + "
347 << paramEnergy->sigmaPlusDeltaE() / Gaudi::Units::GeV << " for P "
348 << combinedEnergyParameters->momentum().mag() / Gaudi::Units::GeV << " eta "
349 << combinedEnergyParameters->position().eta() << " phi " << combinedEnergyParameters->position().phi()
350 << endmsg << " tail-param energy diff "
351 << (caloEnergy->deltaE() - paramEnergy->deltaE()) / Gaudi::Units::GeV);
352
353 ATH_MSG_VERBOSE("Calling createMuonTrack from " << __func__ << " at line " << __LINE__);
354 muonTrack =
355 createMuonTrack(ctx, extrapolatedTrack, nullptr, std::move(paramEnergy), muonTrack->trackStateOnSurfaces());
356
357 if (muonTrack) {
358 std::unique_ptr<Trk::Track> refittedTrack{fit(ctx, indetTrack, *muonTrack, m_cleanCombined, Trk::muon)};
359 if (refittedTrack) { combinedTrack.swap(refittedTrack); }
360 }
361 }
362 }
363
364 // in case of the unexpected ...
365 if (!combinedTrack) {
366 // final combined track lost, this should not happen
367 m_messageHelper->printWarning(4);
368 return nullptr;
369 }
370
372 ATH_MSG_VERBOSE("Refining Calorimeter TSOS in Muon Combined Fit ...");
373 m_materialUpdator->updateCaloTSOS(*combinedTrack);
374 }
375
376 // adds uncertainties and removes AEOTs
377 // We will either have nullptr or a new Track.
378 // What we pass stays untouched.
379 std::unique_ptr<Trk::Track> newTrack = addIDMSerrors(combinedTrack.get());
380 // recollect eloss for combined track and refit
381 // newTrack will not be used after this block, either
382 // we updated the combined or kept the combined as it was
383 if (newTrack) {
384 if (msgLevel(MSG::DEBUG)) countAEOTs(*newTrack, " combinedTrack after addIDMSerrors ");
385 // Don't run the outliers anymore at this stage
386 dumpCaloEloss(newTrack.get(), "CB input TSOS after refine IDMS ");
387 std::unique_ptr<Trk::Track> refittedTrack{fit(ctx, *newTrack, false, Trk::muon)};
388 if (refittedTrack){
389 if (msgLevel(MSG::DEBUG)) countAEOTs(*refittedTrack, " CB fit after refit ");
390 dumpCaloEloss(refittedTrack.get(), "CB refit after refine IDMS ");
392 if (checkTrack("combinedFit", refittedTrack.get())) {
393 // Make the combined point to the refitted
394 combinedTrack.swap(refittedTrack);
395 }
396 }
397 }
398
400 if (!checkTrack("addIDMS failed", combinedTrack.get())) {
401 ATH_MSG_DEBUG("addIDMS errors failed and original track does not pass checkTrack");
402 return nullptr;
403 }
404 // hole recovery, error optimization, attach TrackSummary
405 finalTrackBuild(ctx, combinedTrack);
406
407 return combinedTrack;
408 }
409 std::unique_ptr<Trk::Track> CombinedMuonTrackBuilder::indetExtension(const EventContext& ctx,
410 const Trk::Track& indetTrack,
411 const Trk::MeasurementSet& spectrometerMeasurements,
412 std::unique_ptr<Trk::TrackParameters> innerParameters,
413 std::unique_ptr<Trk::TrackParameters> middleParameters,
414 std::unique_ptr<Trk::TrackParameters> outerParameters) const {
415 if (msgLvl(MSG::VERBOSE)) {
416 msg(MSG::VERBOSE) << endmsg << "indetExtension fit:: " << std::setiosflags(std::ios::fixed);
417
418 if (innerParameters || middleParameters || outerParameters) {
419 msg(MSG::VERBOSE) << " parameters at R,Z ";
420
421 if (innerParameters) {
422 msg(MSG::VERBOSE) << "I:" << std::setw(5) << std::setprecision(0) << innerParameters->position().perp() << ","
423 << std::setw(5) << std::setprecision(0) << innerParameters->position().z() << " ";
424 }
425
426 if (middleParameters) {
427 msg(MSG::VERBOSE) << "M:" << std::setw(5) << std::setprecision(0) << middleParameters->position().perp() << ","
428 << std::setw(5) << std::setprecision(0) << middleParameters->position().z() << " ";
429 }
430
431 if (outerParameters) {
432 msg(MSG::VERBOSE) << "O:" << std::setw(6) << std::setprecision(0) << outerParameters->position().perp() << ","
433 << std::setw(5) << std::setprecision(0) << outerParameters->position().z();
434 }
435
436 msg(MSG::VERBOSE) << " with P ";
437
438 if (innerParameters) {
439 msg(MSG::VERBOSE) << std::setw(9) << std::setprecision(3) << innerParameters->momentum().mag() / Gaudi::Units::GeV;
440 }
441
442 if (middleParameters) {
443 msg(MSG::VERBOSE) << std::setw(9) << std::setprecision(3) << middleParameters->momentum().mag() / Gaudi::Units::GeV;
444 }
445
446 if (outerParameters) {
447 msg(MSG::VERBOSE) << std::setw(9) << std::setprecision(3) << outerParameters->momentum().mag() / Gaudi::Units::GeV;
448 }
449
450 msg(MSG::VERBOSE) << " (GeV)" << endmsg;
451 } else {
452 msg(MSG::VERBOSE) << " without parameters" << endmsg;
453 }
454 }
455
456 // propagate appropriate trackParameters to front, back and middle measurements
457 // fail when solenoid off and toroid on (as extrapolation from ID is not the correct strategy)
458 const Trk::IPropagator* propagator = m_propagatorSL.get();
459
460 MagField::AtlasFieldCache fieldCache;
461 // Get field cache object
462 if (!loadMagneticField(ctx, fieldCache)) return nullptr;
463 if (fieldCache.toroidOn()) {
464 // fail when solenoid off and toroid on - as extrapolation from ID is not the correct strategy
465 // for material effects, fit starting value etc
466 if (!fieldCache.solenoidOn()) {
467 ATH_MSG_VERBOSE("indetExtension: method switched off when solenoid 'off' / toroid 'on'");
468 return nullptr;
469 }
470
473 propagator = m_propagator.get();
474 }
475
476 std::unique_ptr<Trk::TrackParameters> frontParameters, backParameters;
477
478 if (innerParameters) {
479 if (innerParameters->associatedSurface() == spectrometerMeasurements.front()->associatedSurface()) {
480 frontParameters = innerParameters->uniqueClone();
481 } else {
482 // TSoS will own this
483 frontParameters = propagator->propagate(ctx, *innerParameters, spectrometerMeasurements.front()->associatedSurface(),
485 }
486 } else if (middleParameters) {
487 if (middleParameters->associatedSurface() == spectrometerMeasurements.front()->associatedSurface()) {
488 frontParameters = middleParameters->uniqueClone();
489 } else {
490 // TSoS will own this
491 frontParameters = propagator->propagate(ctx, *middleParameters, spectrometerMeasurements.front()->associatedSurface(),
493 }
494 }
495
496 if (outerParameters) {
497 if (outerParameters->associatedSurface() == spectrometerMeasurements.back()->associatedSurface()) {
498 backParameters = outerParameters->uniqueClone();
499 } else {
500 // TSoS will own this
501 backParameters = propagator->propagate(ctx, *outerParameters, spectrometerMeasurements.back()->associatedSurface(),
503 }
504 } else if (middleParameters) {
505 if (middleParameters->associatedSurface() == spectrometerMeasurements.back()->associatedSurface()) {
506 backParameters = middleParameters->uniqueClone();
507 } else {
508 // TSoS will own this
509 backParameters = propagator->propagate(ctx, *middleParameters, spectrometerMeasurements.back()->associatedSurface(),
511 }
512 }
513
514 // find middle measurement
515 std::unique_ptr<Trk::TrackParameters> midParameters;
516 const Trk::MeasurementBase* midMeasurement = nullptr;
517
518 if (middleParameters && innerParameters && outerParameters) {
519 Amg::Vector3D direction = (outerParameters->position() - innerParameters->position()).unit();
520 double midDistance = 0.5 * direction.dot(outerParameters->position() - innerParameters->position());
521 double previousDistance = 0.;
522
523 Trk::MeasurementSet::const_iterator m = spectrometerMeasurements.begin();
524 for (++m; m != spectrometerMeasurements.end(); ++m) {
525 double distance = direction.dot((**m).globalPosition() - innerParameters->position());
526 if (distance < midDistance) {
527 previousDistance = distance;
528 } else {
529 if (midDistance - previousDistance < distance - midDistance) --m;
530 // TSoS will own this
531 midParameters = m_propagator->propagate(ctx, *middleParameters, (**m).associatedSurface(), Trk::anyDirection, false,
533
534 if (midParameters) midMeasurement = *m;
535 break;
536 }
537 }
538 }
539
540 // create muon track from spectrometer measurements
541 std::bitset<Trk::TrackStateOnSurface::NumberOfTrackStateOnSurfaceTypes> typeM;
543 std::bitset<Trk::TrackStateOnSurface::NumberOfTrackStateOnSurfaceTypes> typeP;
546
547 auto trackStateOnSurfaces = std::make_unique<Trk::TrackStates>();
548
549 trackStateOnSurfaces->reserve(spectrometerMeasurements.size());
550
551 // append the spectrometer measurements
552 for (const Trk::MeasurementBase* const in_meas : spectrometerMeasurements) {
553 //if the unique_ptr has been moved, it will be nullptr after
554 //cppcheck-suppress accessMoved
555 if (frontParameters) {
556 trackStateOnSurfaces->push_back(
557 new Trk::TrackStateOnSurface(in_meas->uniqueClone(), std::move(frontParameters), nullptr, typeP));
558 } else if (in_meas == midMeasurement) {
559 trackStateOnSurfaces->push_back(
560 new Trk::TrackStateOnSurface(in_meas->uniqueClone(), std::move(midParameters), nullptr, typeP));
561 } else if (backParameters && in_meas == spectrometerMeasurements.back()) {
562 trackStateOnSurfaces->push_back(
563 new Trk::TrackStateOnSurface(in_meas->uniqueClone(), std::move(backParameters), nullptr, typeP));
564 } else {
565 trackStateOnSurfaces->push_back(
566 new Trk::TrackStateOnSurface(in_meas->uniqueClone(), nullptr, nullptr, typeM));
567 }
568 }
569
571
572 Trk::Track muonTrack(trackInfo, std::move(trackStateOnSurfaces), nullptr);
573 if (msgLevel(MSG::DEBUG)) countAEOTs(muonTrack, " in detExtension muonTrack ");
574 // perform combined fit
575 ATH_MSG_VERBOSE("Calling combinedFit from " << __func__ << " at line " << __LINE__);
576 std::unique_ptr<Trk::Track> combinedTrack{combinedFit(ctx, indetTrack, muonTrack, muonTrack)};
577 return combinedTrack;
578 }
579 std::unique_ptr<Trk::Track> CombinedMuonTrackBuilder::standaloneFit(const EventContext& ctx, const Trk::Track& inputSpectrometerTrack,
580 const Amg::Vector3D& origin, const Trk::Vertex* inputVertex) const {
581 MagField::AtlasFieldCache fieldCache;
582 // Get field cache object
583
584 if (!loadMagneticField(ctx, fieldCache)) return nullptr;
585
586 // no SA fit with vertex constraint for Toroid off data
587 if (m_trackQuery->isLineFit(inputSpectrometerTrack) && !fieldCache.toroidOn()) { return nullptr; }
588
589 ATH_MSG_DEBUG(" standaloneFit beam position bs_x " << origin << " inputVertex "
590 << inputVertex);
591
592 if (msgLvl(MSG::VERBOSE)) {
593 msg(MSG::VERBOSE) << endmsg << "==== Start of standaloneFit:: " << std::setiosflags(std::ios::fixed);
594
595 if (m_trackQuery->isExtrapolated(inputSpectrometerTrack, ctx)) {
596 if (m_trackQuery->isLineFit(inputSpectrometerTrack)) {
597 msg(MSG::VERBOSE) << "extrapolated has lineFit";
598 } else {
599 msg(MSG::VERBOSE) << "extrapolated momentum " << std::setprecision(1)
600 << inputSpectrometerTrack.perigeeParameters()->momentum().mag() / Gaudi::Units::GeV << " (GeV)";
601 }
602
603 msg(MSG::VERBOSE) << " at eta " << std::setw(6) << std::setprecision(3)
604 << inputSpectrometerTrack.perigeeParameters()->momentum().eta() << " phi " << std::setw(6)
605 << std::setprecision(3) << inputSpectrometerTrack.perigeeParameters()->momentum().phi();
606
607 } else if (!m_trackQuery->isProjective(inputSpectrometerTrack)) {
608 msg(MSG::VERBOSE) << "spectrometer track does not project";
609 } else if (inputSpectrometerTrack.perigeeParameters()) {
610 if (m_trackQuery->isLineFit(inputSpectrometerTrack)) {
611 msg(MSG::VERBOSE) << "spectrometer has lineFit";
612 } else {
613 msg(MSG::VERBOSE) << "spectrometer momentum " << std::setprecision(1)
614 << inputSpectrometerTrack.perigeeParameters()->momentum().mag() / Gaudi::Units::GeV << " (GeV)";
615 }
616
617 msg(MSG::VERBOSE) << " at eta " << std::setw(6) << std::setprecision(3)
618 << inputSpectrometerTrack.perigeeParameters()->position().eta() << " phi " << std::setw(6)
619 << std::setprecision(3) << inputSpectrometerTrack.perigeeParameters()->position().phi();
620
621 if (inputSpectrometerTrack.perigeeParameters()->covariance()) {
622 msg(MSG::VERBOSE) << " hasCov";
623 } else {
624 msg(MSG::VERBOSE) << " noCov ";
625 }
626 } else {
627 msg(MSG::VERBOSE) << " spectrometer track without PerigeeParameters";
628 }
629
630 if (inputSpectrometerTrack.fitQuality()) {
631 msg(MSG::VERBOSE) << " fit: chi2 /DoF " << std::setprecision(2) << normalizedChi2(inputSpectrometerTrack) << " /"
632 << std::setw(2) << inputSpectrometerTrack.fitQuality()->numberDoF();
633 }
634
635 if (m_trackQuery->numberPseudoMeasurements(inputSpectrometerTrack)) {
636 msg(MSG::VERBOSE) << " pseudo " << m_trackQuery->numberPseudoMeasurements(inputSpectrometerTrack);
637 }
638
639 msg(MSG::VERBOSE) << endmsg;
640 }
641
642 // check input vertex OK
643 const Trk::RecVertex* vertex = dynamic_cast<const Trk::RecVertex*>(inputVertex);
644 if (inputVertex && !vertex) {
645 // input vertex fails dynamic_cast
646 m_messageHelper->printWarning(6);
647 return nullptr;
648 }
649
650 // fail input tracks with insufficient measurements or inconsistent structure
651 const Trk::FitQuality* fitQuality = inputSpectrometerTrack.fitQuality();
652 const Trk::TrackStates* tsos = inputSpectrometerTrack.trackStateOnSurfaces();
653
654 if (!fitQuality || !inputSpectrometerTrack.trackStateOnSurfaces() ||
655 static_cast<int>(inputSpectrometerTrack.trackStateOnSurfaces()->size()) < fitQuality->numberDoF()) {
656 // count measurements
657 int measurements = 0;
658 for (const Trk::TrackStateOnSurface* s : *tsos) {
659 measurements += (s->type(Trk::TrackStateOnSurface::Measurement) && !s->type(Trk::TrackStateOnSurface::Outlier));
660 }
661 // insufficient measurements
662 if (measurements < 4) {
663 m_messageHelper->printWarning(48);
664 ATH_MSG_VERBOSE(" SA::failed (1)");
665 return nullptr;
666 }
667
668 // inconsistent TSOS on input track
669 if (fitQuality && measurements < fitQuality->numberDoF() + 4) {
670 m_messageHelper->printWarning(49);
671 ATH_MSG_VERBOSE(" SA::failed (2)");
672 return nullptr;
673 }
674 }
675
676 // check the track is roughly projective in phi
677 const bool is_extrapolated = m_trackQuery->isExtrapolated(inputSpectrometerTrack, ctx);
678 if (!is_extrapolated && !m_trackQuery->isProjective(inputSpectrometerTrack)) {
679 ATH_MSG_VERBOSE(" SA::failed (3)");
680 return nullptr;
681 }
682
683 // possibly refit the spectrometer track with material reallocation
684 double spectrometerFitChi2 = normalizedChi2(inputSpectrometerTrack);
685 std::unique_ptr<Trk::Track> spectrometerFit = std::make_unique<Trk::Track>(inputSpectrometerTrack);
686 if (!vertex && (m_reallocateMaterial || is_extrapolated)) {
687 spectrometerFit = reallocateMaterial(ctx, inputSpectrometerTrack);
688 if (!spectrometerFit) {
689 ATH_MSG_VERBOSE(" SA::failed (4)");
690 return nullptr;
691 }
692 }
693
694 const Trk::Track& spectrometerTrack = *spectrometerFit;
695
696 // require a Perigee from the spectrometer track
697 const Trk::Perigee* measuredPerigee = spectrometerTrack.perigeeParameters();
698
699 if (!measuredPerigee || !measuredPerigee->covariance()) {
700 // missing MeasuredPerigee for spectrometer track
701 m_messageHelper->printWarning(7);
702
703 ATH_MSG_VERBOSE(" SA::failed (5)");
704 return nullptr;
705 }
706
707 // set measured momentum error and starting parameters
708 bool badlyDeterminedCurvature = false;
709
710 if (!Amg::hasPositiveDiagElems(*measuredPerigee->covariance())) {
711 ATH_MSG_WARNING("standaloneFit: measuredPerigee has non-positive-definite covariance ");
712 ATH_MSG_VERBOSE(" SA::failed (5.5)");
714 return nullptr;
715 }
716
717 double errorP = std::sqrt(measuredPerigee->momentum().mag2() * (*measuredPerigee->covariance())(Trk::qOverP, Trk::qOverP));
718
719 std::unique_ptr<Trk::RecVertex> mvertex = std::make_unique<Trk::RecVertex>(*m_vertex);
720 std::unique_ptr<Trk::RecVertex> mbeamAxis = std::make_unique<Trk::RecVertex>(*m_beamAxis);
721 std::unique_ptr<Trk::PerigeeSurface> mperigeeSurface = std::make_unique<Trk::PerigeeSurface>(*m_perigeeSurface);
722
723 std::unique_ptr<const Trk::TrackParameters> parameters;
724
725 if (vertex) {
726 // vertex association only makes sense for magnet-on tracks with measured curvature
727 if (!fieldCache.toroidOn() || m_trackQuery->isLineFit(spectrometerTrack) || errorP > m_largeMomentumError) {
728 ATH_MSG_VERBOSE("standaloneFit: vertex fit not attempted as curvature badly measured");
729 ATH_MSG_VERBOSE(" SA::failed (6)");
730 return nullptr;
731 }
732 parameters = std::make_unique<Trk::Perigee>(*spectrometerTrack.perigeeParameters());
733 } else {
734 //
735 // update -if needed vertex and beam axis positions
736 //
737 if ((origin - mvertex->position()).mag() > 0.001) {
738 // recreate beamAxis and vertexRegion for constrained (projective) track fits
739
740 mperigeeSurface = std::make_unique<Trk::PerigeeSurface>(origin);
741
742 AmgSymMatrix(3) beamAxisCovariance;
743 beamAxisCovariance.setZero();
744 (beamAxisCovariance)(0, 0) = m_vertex2DSigmaRPhi * m_vertex2DSigmaRPhi;
745 (beamAxisCovariance)(1, 1) = m_vertex2DSigmaRPhi * m_vertex2DSigmaRPhi;
746 (beamAxisCovariance)(2, 2) = m_vertex2DSigmaZ * m_vertex2DSigmaZ;
747 mbeamAxis = std::make_unique<Trk::RecVertex>(origin, beamAxisCovariance);
748
749 AmgSymMatrix(3) vertexRegionCovariance;
750 vertexRegionCovariance.setZero();
751 (vertexRegionCovariance)(0, 0) = m_vertex3DSigmaRPhi * m_vertex3DSigmaRPhi;
752 (vertexRegionCovariance)(1, 1) = m_vertex3DSigmaRPhi * m_vertex3DSigmaRPhi;
753 (vertexRegionCovariance)(2, 2) = m_vertex3DSigmaZ * m_vertex3DSigmaZ;
754 mvertex = std::make_unique<Trk::RecVertex>(origin, vertexRegionCovariance);
755 }
756
757 parameters = extrapolatedParameters(ctx, badlyDeterminedCurvature, spectrometerTrack, mvertex.get(), mperigeeSurface.get());
758 }
759
760 if (!parameters) {
761 ATH_MSG_VERBOSE(" SA::failed (7)");
762 return nullptr;
763 }
764
765 // create the spectrometer TSOS's for the extrapolated fit
766 std::vector<std::unique_ptr<const Trk::TrackStateOnSurface>> spectrometerTSOS = createSpectrometerTSOS(ctx, spectrometerTrack);
767
768 if (spectrometerTSOS.empty()) {
769 ATH_MSG_VERBOSE(" SA::failed (8)");
770 return nullptr;
771 }
772 const Trk::TrackParameters* caloParameters = nullptr;
773
774 Trk::ParticleHypothesis particleHypothesis = Trk::muon;
775
776 bool haveFitWithVertex = false;
777 bool performPrefit = false;
778
779 if (m_redoRots) {
780 for (const Trk::TrackStateOnSurface* s : *spectrometerTrack.trackStateOnSurfaces()) {
781 if (s->measurementOnTrack() && !s->trackParameters()) {
782 performPrefit = true;
783 break;
784 }
785 }
786 }
787
788 // badly defined tracks use weak vertex constraint with prefit before calo association
789 std::unique_ptr<Trk::Track> prefit;
790
791 const Trk::RecVertex* vertexInFit = vertex;
792
793 if (!vertexInFit) {
794 double errorPhi = std::sqrt((*measuredPerigee->covariance())(Trk::phi0, Trk::phi0));
795
796 bool inCSCregion = std::abs(measuredPerigee->momentum().eta()) > 2.0;
797
798 // FIXME: missing prefit case for excessive spectrometer eloss WARNING
799 // spot from line starting approx from vertex??
800 if (inCSCregion || m_trackQuery->numberPseudoMeasurements(spectrometerTrack) ||
801 (fieldCache.toroidOn() &&
802 (badlyDeterminedCurvature || errorPhi > m_largePhiError || measuredPerigee->momentum().mag() < m_lowMomentum))) {
803 performPrefit = true;
804 vertexInFit = (badlyDeterminedCurvature || inCSCregion) ? mvertex.get() : mbeamAxis.get();
805
806 if (msgLvl(MSG::DEBUG)) {
807 unsigned numberPseudo = m_trackQuery->numberPseudoMeasurements(spectrometerTrack);
808 if (errorPhi > s_sigmaPhiSector) { ++numberPseudo; }
809
810 if (badlyDeterminedCurvature) {
811 ATH_MSG_DEBUG(" prefit with vertex: " << std::setiosflags(std::ios::fixed) << " momentum " << std::setprecision(1)
812 << measuredPerigee->momentum().mag() / Gaudi::Units::GeV << " (GeV), zFirst "
813 << std::setprecision(1) << std::abs(parameters->position().z())
814 << ", phiError " << std::setprecision(2) << errorPhi << ", momentumError "
815 << std::setprecision(2) << errorP << ", numberPseudo " << numberPseudo);
816 } else {
817 ATH_MSG_DEBUG(" prefit with beamAxis: "
818 << std::setiosflags(std::ios::fixed) << " momentum " << std::setprecision(1)
819 << measuredPerigee->momentum().mag() / Gaudi::Units::GeV << " (GeV), zFirst " << std::setprecision(1)
820 << std::abs(parameters->position().z()) << ", phiError " << std::setprecision(2) << errorPhi
821 << ", momentumError " << std::setprecision(2) << errorP << ", numberPseudo " << numberPseudo);
822 }
823 }
824 }
825 }
826
827 std::unique_ptr<const Trk::Perigee> prefitResult;
828
829 // prefit to stabilize calo look-up and/or provide trackParameters
830 if (performPrefit) {
831 if (!vertexInFit) { ATH_MSG_VERBOSE(" prefit without vertex"); }
832
833 if (vertexInFit) { haveFitWithVertex = true; }
834
835 if (badlyDeterminedCurvature && parameters->momentum().mag() > m_lowMomentum) { particleHypothesis = Trk::nonInteracting; }
836
837 ATH_MSG_VERBOSE("Calling createExtrapolatedTrack from " << __func__ << " at line " << __LINE__);
838 prefit = createExtrapolatedTrack(ctx, spectrometerTrack, *parameters, particleHypothesis, false, spectrometerTSOS, vertexInFit,
839 mbeamAxis.get(), mperigeeSurface.get());
840
841 // demand prefit success
842 if (!prefit || !prefit->fitQuality() || !prefit->perigeeParameters()) {
843 ATH_MSG_DEBUG(" prefit failure ");
844 prefit.reset();
845 }
846
847 if (prefit) {
848 dumpCaloEloss(prefit.get(), " prefit ");
849 bool hasCov = prefit->perigeeParameters() ? (prefit->perigeeParameters()->covariance() != nullptr) : false;
850 ATH_MSG_VERBOSE(" got prefit " << m_printer->print(*prefit) << " hasCov " << hasCov);
851
852 if (prefit->perigeeParameters()) { prefitResult.reset(prefit->perigeeParameters()->clone()); }
853 const Trk::TrackStateOnSurface* ms_entrance = nullptr;
854 for (const Trk::TrackStateOnSurface* s : *prefit->trackStateOnSurfaces()) {
855 // look for first measured TSOS in muon volume
856 if (!s->trackParameters() || !s->trackParameters()->covariance()) { continue; }
857 if (m_calorimeterVolume->inside(s->trackParameters()->position())) { continue; }
858
859 // check that it is a measurement
861 ATH_MSG_DEBUG("Found first parameters in MS " << s->trackParameters()->position().perp() << " z "
862 << s->trackParameters()->position().z());
863 ms_entrance = s;
864 break;
865 }
866 }
867
868 if (ms_entrance && ms_entrance != prefit->trackStateOnSurfaces()->front() && ms_entrance->trackParameters()) {
869 parameters.reset(ms_entrance->trackParameters()->clone());
870 caloParameters = parameters.get();
871 } else {
872 // prefit: no parameter extrapolation to calo
873 m_messageHelper->printWarning(9);
874 }
875 }
876
877 // give up if prefit fails
878 spectrometerTSOS.clear();
879
880 if (!prefit) {
881 ATH_MSG_VERBOSE(" SA::failed (9)");
882 return nullptr;
883 }
884 const Trk::TrackStates* prefit_tsos = prefit->trackStateOnSurfaces();
885 // create spectrometerTSOS corresponding to prefit
886 // skip start perigee, then preferentially take everything following MS perigee,
887 // otherwise (if no MS perigee) rely on VolumesSvc,
888 // but be aware that by design there are inconsistencies wrt tracking geometry
890 std::find_if(prefit_tsos->begin() + 1, prefit_tsos->end(), [this](const Trk::TrackStateOnSurface* tsos) -> bool {
891 return (tsos->trackParameters() && !m_calorimeterVolume->inside(tsos->trackParameters()->position())) ||
892 tsos->type(Trk::TrackStateOnSurface::Perigee);
893 });
894
895 if (s != prefit_tsos->end() && (*s)->type(Trk::TrackStateOnSurface::Perigee)) ++s;
896
897 for (; s != prefit_tsos->end(); ++s) { spectrometerTSOS.emplace_back((*s)->clone()); }
898 }
899
900 if (m_redoRots) {
901 // recalibration: correct rots
902 for (std::unique_ptr<const Trk::TrackStateOnSurface>& t : spectrometerTSOS) {
903 if (!t->measurementOnTrack() || !t->trackParameters()) { continue; } // end of if
904
905 const Trk::RIO_OnTrack* rot = dynamic_cast<const Trk::RIO_OnTrack*>(t->measurementOnTrack());
906
907 if (!rot) continue;
908 Identifier id = rot->identify();
909
910 if (!m_idHelperSvc->isMuon(id)) continue;
911
912 std::unique_ptr<Trk::RIO_OnTrack> updatedRot;
913 if (!m_cscRotCreator.empty() && m_idHelperSvc->isCsc(id)) {
914 updatedRot.reset(m_cscRotCreator->correct(*rot->prepRawData(), *(*t).trackParameters(), ctx));
915 } else if (!m_mdtRotCreator.empty() && m_idHelperSvc->isMdt(id)) {
916 updatedRot.reset(m_mdtRotCreator->correct(*rot->prepRawData(), *(*t).trackParameters(), ctx));
917 } else if (!m_muClusterRotCreator.empty() && (m_idHelperSvc->isMM(id) || m_idHelperSvc->issTgc(id))) {
918 updatedRot.reset(m_muClusterRotCreator->correct(*rot->prepRawData(), *(*t).trackParameters(), ctx));
919 }
920
921 if (updatedRot) {
922 t = Muon::MuonTSOSHelper::createMeasTSOS(std::move(updatedRot), t->trackParameters()->uniqueClone(),
924 }
925 }
926 }
927
928 // extrapolate and fit track
929 particleHypothesis = Trk::muon;
930 bool returnAfterCleaner = !fieldCache.toroidOn();
931
932 ATH_MSG_VERBOSE("Calling createExtrapolatedTrack from " << __func__ << " at line " << __LINE__);
933 std::unique_ptr<Trk::Track> extrapolated(createExtrapolatedTrack(ctx, spectrometerTrack, *parameters, particleHypothesis,
934 m_cleanStandalone, spectrometerTSOS, vertexInFit, mbeamAxis.get(),
935 mperigeeSurface.get(), prefitResult.get()));
936
937 if (extrapolated) dumpCaloEloss(extrapolated.get(), " extrapolated ");
938
939 // fit problem: try fixup using vertex region or prefit
940 if (!extrapolated || !extrapolated->fitQuality()) {
941 if (extrapolated && !haveFitWithVertex && !vertexInFit) {
942 ATH_MSG_DEBUG(" bad fitQuality: retry with vertex ");
943 std::unique_ptr<Trk::Track> badfit(std::move(extrapolated));
944
945 auto trackStateOnSurfaces = std::make_unique<Trk::TrackStates>();
946 trackStateOnSurfaces->reserve(badfit->trackStateOnSurfaces()->size() + 1);
947
948 std::bitset<Trk::TrackStateOnSurface::NumberOfTrackStateOnSurfaceTypes> type{};
950 std::unique_ptr<Trk::PseudoMeasurementOnTrack> vertexInFit =
951 vertexOnTrack(*badfit->perigeeParameters(), mvertex.get(), mbeamAxis.get());
952
953 if (vertexInFit) type.set(Trk::TrackStateOnSurface::Measurement);
954
955 trackStateOnSurfaces->push_back(new Trk::TrackStateOnSurface(
956 std::move(vertexInFit), badfit->perigeeParameters()->uniqueClone(), nullptr, type));
957
958 for (Trk::TrackStates::const_iterator s = badfit->trackStateOnSurfaces()->begin() + 1;
959 s != badfit->trackStateOnSurfaces()->end(); ++s) {
960 trackStateOnSurfaces->push_back((**s).clone());
961 }
962
963 std::unique_ptr<Trk::Track> track =
964 std::make_unique<Trk::Track>(spectrometerTrack.info(), std::move(trackStateOnSurfaces), nullptr);
965 extrapolated = fit(ctx, *track, m_cleanStandalone, particleHypothesis);
966 }
967
968 // restart from prefit without cleaning
969 if (!extrapolated || !extrapolated->fitQuality()) {
970 if (prefit && prefit->fitQuality() && caloParameters) {
971 ATH_MSG_DEBUG(" restarting from prefit as back extrapolation fit failed");
972 spectrometerTSOS.clear();
973 // create spectrometerTSOS corresponding to prefit
974 Trk::TrackStates::const_iterator s = prefit->trackStateOnSurfaces()->begin();
975
976 while (m_calorimeterVolume->inside((**s).trackParameters()->position()) ||
978 ++s;
979 } // end of while
980
981 for (; s != prefit->trackStateOnSurfaces()->end(); ++s) { spectrometerTSOS.emplace_back((**s).clone()); }
982
983 ATH_MSG_VERBOSE("Calling createExtrapolatedTrack from " << __func__ << " at line " << __LINE__);
985 createExtrapolatedTrack(ctx, spectrometerTrack, *caloParameters, particleHypothesis, false, spectrometerTSOS,
986 vertexInFit, mbeamAxis.get(), mperigeeSurface.get(), prefitResult.get());
987 returnAfterCleaner = true;
988 }
989
990 if (!extrapolated || !extrapolated->fitQuality()) {
991 bool hasFQ = extrapolated ? (extrapolated->fitQuality() != nullptr) : false;
992 ATH_MSG_DEBUG("fail track as back extrapolation fit failed " << extrapolated.get() << " hasFQ " << hasFQ);
993
994 ATH_MSG_VERBOSE(" SA::failed (10)");
995 return nullptr;
996 }
997 }
998 }
999
1000 // keep statistics for successful fits
1002 if (vertexInFit == mbeamAxis.get()) ++m_countBeamAxis;
1003 if (vertexInFit == mvertex.get()) ++m_countVertexRegion;
1004
1005 // refit when there's been a significant momentum change (parameters at last calo scatterer)
1006 double momentum = parameters->momentum().mag();
1007
1008 bool allowRefit = !badlyDeterminedCurvature;
1009 double pRatio = 1.;
1010
1011 const Trk::TrackParameters* params_pRat = parameters.get();
1012 if (returnAfterCleaner) {
1013 allowRefit = false;
1014 } else {
1015 // pRatio is the ratio of fitted to start momentum value at calo exit
1016 // find parameters at calo exit
1017 const Trk::TrackParameters* params_pRat = nullptr;
1018 auto s = extrapolated->trackStateOnSurfaces()->begin();
1019 while (!(**s).trackParameters() || m_calorimeterVolume->inside((**s).trackParameters()->position())) {
1020 if ((**s).trackParameters() && !(**s).type(Trk::TrackStateOnSurface::Perigee)) params_pRat = (**s).trackParameters();
1021 ++s;
1022 }
1023
1024 // extrapolated fit with missing calo parameters - this should never happen!
1025 if (params_pRat) {
1026 pRatio = momentum / parameters->momentum().mag();
1027 } else {
1028 // extrapolated track missing TrackParameters at calo scatterer
1029 m_messageHelper->printWarning(10);
1030 allowRefit = false;
1031 }
1032 }
1033
1034 // in case of a significant momentum change: iterate (re-associate calo and refit)
1035 std::unique_ptr<Trk::Track> track;
1036
1037 if (allowRefit && std::abs(pRatio - 1.) > m_largeMomentumChange) {
1038 if (msgLvl(MSG::VERBOSE)) {
1039 double sinTheta = params_pRat->momentum().perp() / params_pRat->momentum().mag();
1040
1041 ATH_MSG_VERBOSE(" iterate as significant momentum change after fit "
1042 << pRatio << ", pT before " << momentum * sinTheta / Gaudi::Units::GeV << ", after "
1043 << params_pRat->momentum().perp() / Gaudi::Units::GeV << " GeV");
1044 }
1045
1046 spectrometerTSOS.clear();
1047 for (const Trk::TrackStateOnSurface* s : *extrapolated->trackStateOnSurfaces()) {
1048 if (!s->type(Trk::TrackStateOnSurface::Perigee)) spectrometerTSOS.emplace_back(s->clone());
1049 }
1050
1051 ATH_MSG_VERBOSE("Calling createExtrapolatedTrack from " << __func__ << " at line " << __LINE__);
1052
1053 track = createExtrapolatedTrack(ctx, spectrometerTrack, *parameters, particleHypothesis, m_cleanStandalone, spectrometerTSOS,
1054 vertexInFit, mbeamAxis.get(), mperigeeSurface.get(), extrapolated->perigeeParameters());
1055
1056 if (track) {
1057 double extrapChi2 = normalizedChi2(*extrapolated);
1058 double fitChi2 = normalizedChi2(*track);
1059 if (fitChi2 < m_badFitChi2 || fitChi2 < extrapChi2 + 0.5) { extrapolated.reset(); }
1060 }
1061 }
1062 if (extrapolated) { track.swap(extrapolated); }
1063
1064 if (!m_trackQuery->isCaloAssociated(*track, ctx)) { // still want to perform this check probably though
1065 // fail as calo incorrectly described
1066 m_messageHelper->printWarning(12);
1067 ATH_MSG_VERBOSE(" SA::failed (12)");
1068 return nullptr;
1069 }
1070
1071 int improvementsFailed = 0; // count the number of times the fit fails after improvements
1072
1074 ATH_MSG_VERBOSE("Refining Calorimeter TSOS in StandAlone Fit ...");
1075
1076 m_materialUpdator->updateCaloTSOS(*track);
1077
1078 std::unique_ptr<Trk::Track> refinedTrack(fit(ctx, *track, false, Trk::muon));
1079 if (checkTrack("refineFit", refinedTrack.get())) {
1080 ATH_MSG_VERBOSE(__FILE__<<":"<<__LINE__<<"refined track checks out");
1081 track.swap(refinedTrack);
1082 } else {
1083 ATH_MSG_VERBOSE("refined track fit failed");
1084 ++improvementsFailed;
1085 }
1086 }
1087
1088 // adds uncertainties
1089 // We will either have nullptr or a new Track.
1090 // What we pass stays untouched.
1091 std::unique_ptr<Trk::Track> newTrack = addIDMSerrors(track.get());
1092 // newTrack will not be used after this block, either
1093 // we updated the track or kept the track as it was
1094 if (newTrack) {
1095 if (msgLevel(MSG::DEBUG)) countAEOTs(*newTrack, " SA track after addIDMSerrors ");
1096 dumpCaloEloss(newTrack.get(), "SA input TSOS after refine IDMS ");
1097
1098 // Don't run the outliers anymore at this stage
1099 std::unique_ptr<Trk::Track> refittedTrack(fit(ctx, *newTrack, false, Trk::muon));
1100 if (msgLevel(MSG::DEBUG) && refittedTrack) { countAEOTs(*refittedTrack, " SA track after refit "); }
1101 dumpCaloEloss(refittedTrack.get(), " SA refit after refine IDMS ");
1102 if (checkTrack("standaloneFit", refittedTrack.get())) {
1103 // Here we swap
1104 track.swap(refittedTrack);
1105 } else {
1106 ++improvementsFailed;
1107 }
1108 } else {
1109 ++improvementsFailed;
1110 }
1111
1112 // hole recovery, error optimization, attach TrackSummary
1113 finalTrackBuild(ctx, track);
1114
1115 if (track) {
1116 dumpCaloEloss(track.get(), " finalTrackBuild ");
1117
1118 // report when extrapolated fit quality significantly worse than spectrometer quality
1119 double fitChi2 = normalizedChi2(*track);
1120 if (fitChi2 > m_badFitChi2 && fitChi2 > spectrometerFitChi2 + 0.5) {
1121 ATH_MSG_DEBUG("standaloneFit: fit quality degraded wrt spectrometer alone. "
1122 << " Chi2/DoF= " << fitChi2);
1123
1125 if (improvementsFailed >= 2) {
1126 ATH_MSG_DEBUG("reject track, quality degraded and improvements failed");
1127 return nullptr;
1128 }
1129 }
1130 }
1131 ATH_MSG_VERBOSE(" SA::ok ");
1132 return track;
1133 }
1134 std::unique_ptr<Trk::Track> CombinedMuonTrackBuilder::standaloneRefit(const EventContext& ctx, const Trk::Track& combinedTrack,
1135 const Amg::Vector3D& origin) const {
1136 //
1137 // update -if needed vertex and beam axis positions
1138 //
1139
1140 if (msgLevel(MSG::DEBUG)) countAEOTs(combinedTrack, " in standalone Refit input combinedTrack ");
1141
1142 MagField::AtlasFieldCache fieldCache;
1143 // Get field cache object
1144
1145 if (!loadMagneticField(ctx, fieldCache)) return nullptr;
1146
1147 if (!fieldCache.toroidOn()) {
1148 // no standalone refit for Toroid off
1149 return nullptr;
1150 }
1151
1152 ATH_MSG_DEBUG(" StandaloneRefit beam position bs_x " << origin);
1153
1154 // vertex will change track by track
1155 AmgSymMatrix(3) vertexRegionCovariance{AmgSymMatrix(3)::Zero()};
1156
1157 double error2d0 = m_vertex3DSigmaRPhi * m_vertex3DSigmaRPhi;
1158 double error2z0 = m_vertex3DSigmaZ * m_vertex3DSigmaZ;
1159 const Trk::Perigee* measuredPerigee = combinedTrack.perigeeParameters();
1160
1161 if (measuredPerigee && measuredPerigee->covariance() && m_useRefitTrackError) {
1162 error2d0 = (*measuredPerigee->covariance())(Trk::d0, Trk::d0);
1163 error2z0 = (*measuredPerigee->covariance())(Trk::z0, Trk::z0);
1164 ATH_MSG_DEBUG(" StandaloneRefit new vertex d0 error " << std::sqrt(error2d0) << " new vertex z0 error "
1165 << std::sqrt(error2z0));
1166 }
1167
1168 (vertexRegionCovariance)(0, 0) = error2d0;
1169 (vertexRegionCovariance)(1, 1) = error2d0;
1170 (vertexRegionCovariance)(2, 2) = error2z0;
1171
1172 std::unique_ptr<Trk::RecVertex> vertex = std::make_unique<Trk::RecVertex>(origin, vertexRegionCovariance);
1173
1174 ATH_MSG_DEBUG(" StandaloneRefit new vertex position x " << vertex->position().x() << " y " << vertex->position().y() << " z "
1175 << vertex->position().z());
1176
1177 bool addPhiPseudo = false;
1178 // release 21
1179 unsigned spectrometerPhiQuality = m_trackQuery->spectrometerPhiQuality(combinedTrack, ctx);
1180 if (spectrometerPhiQuality > 1) { addPhiPseudo = true; }
1181
1182 ATH_MSG_VERBOSE("standaloneRefit: using vertex region constraint with "
1183 << "spectrometerPhiQuality " << spectrometerPhiQuality);
1184
1185 // create standalone track TSOS vector
1186 auto trackStateOnSurfaces = std::make_unique<Trk::TrackStates>();
1187
1188 // size will allow for perigee + all TSOS outside indet
1189 unsigned size = combinedTrack.trackStateOnSurfaces()->size() + 3 + addPhiPseudo;
1190
1191 trackStateOnSurfaces->reserve(size);
1192
1193 // position TSOS iterator to be just after the indet
1194 bool haveCaloDeposit = false;
1195
1196 Trk::TrackStates::const_iterator s = combinedTrack.trackStateOnSurfaces()->begin();
1197 const Trk::TrackStates::const_iterator cmb_end_itr = combinedTrack.trackStateOnSurfaces()->end();
1198 do {
1199 ++s;
1200 if (s == cmb_end_itr) {
1201 // fail track as no TSOS with type CaloDeposit
1202 m_messageHelper->printWarning(13);
1203 return nullptr;
1204 }
1205 if ((*s)->type(Trk::TrackStateOnSurface::CaloDeposit)) {
1206 haveCaloDeposit = true;
1207 --s;
1208 }
1209 } while (!haveCaloDeposit);
1211 const Trk::TrackStateOnSurface* const cmb_inner_tsos = (*s);
1212 // inner calo scatterer - keep scattering angles for vertex constraint
1213 // Amg::Vector3D direction;
1214 const Trk::ScatteringAngles* innerScattering = nullptr;
1215 std::unique_ptr<Trk::TrackStateOnSurface> innerTSOS;
1216 const Trk::MaterialEffectsBase* materialEffects = cmb_inner_tsos->materialEffectsOnTrack();
1217 const Trk::TrackParameters* middleParameters = nullptr;
1218 const Trk::ScatteringAngles* outerScattering = nullptr;
1219 const Trk::TrackParameters* parameters = cmb_inner_tsos->trackParameters();
1220 std::unique_ptr<Trk::TrackParameters> param_owner;
1221 if (materialEffects && parameters && m_calorimeterVolume->inside(parameters->position())) {
1222 // keep scattering angles when vertex constrained
1223 // in r21, addVertexRegion is always true
1224
1225 innerTSOS.reset(cmb_inner_tsos->clone());
1226 const Trk::MaterialEffectsOnTrack* meot = dynamic_cast<const Trk::MaterialEffectsOnTrack*>(materialEffects);
1227
1228 if (!meot) {
1229 // innerScattering dynamic_cast failed
1230 m_messageHelper->printWarning(16);
1231 return nullptr;
1232 }
1233 innerScattering = meot->scatteringAngles();
1236 if (s != cmb_end_itr && !(*s)->type(Trk::TrackStateOnSurface::CaloDeposit)) { ++s; }
1237
1239 if (s != cmb_end_itr) {
1240 const Trk::TrackStateOnSurface* const cmb_middle_tsos = (*s);
1241 materialEffects = cmb_middle_tsos->materialEffectsOnTrack();
1242 parameters = cmb_middle_tsos->trackParameters();
1243 middleParameters = parameters;
1244 } else {
1245 // no TSOS of type CaloDeposit found
1246 m_messageHelper->printWarning(17);
1247 materialEffects = nullptr;
1248 parameters = nullptr;
1249 }
1250
1251 } else {
1252 // no inner material or parameters
1253 if (!materialEffects) m_messageHelper->printWarning(14);
1254 if (!parameters) m_messageHelper->printWarning(15);
1255 }
1256
1257 if (!innerTSOS) {
1258 // no inner scattering TSOS found
1259 m_messageHelper->printWarning(18);
1260 return nullptr;
1261 }
1262
1263 // middle calo scatterer (for energy deposit)
1264 double energyDeposit{0.};
1265
1266 std::unique_ptr<Trk::TrackStateOnSurface> middleTSOS;
1267
1268 if (materialEffects && parameters && m_calorimeterVolume->inside(parameters->position())) {
1269 const Trk::TrackStateOnSurface* const cmb_middle_tsos = (*s);
1270 middleTSOS.reset(cmb_middle_tsos->clone());
1271 const Trk::MaterialEffectsOnTrack* meot = dynamic_cast<const Trk::MaterialEffectsOnTrack*>(materialEffects);
1272
1273 if (meot && meot->energyLoss()) energyDeposit = meot->energyLoss()->deltaE();
1275
1276 ++s;
1277 if (s != cmb_end_itr) {
1278 const Trk::TrackStateOnSurface* const cmb_outer_tsos = (*s);
1279 materialEffects = cmb_outer_tsos->materialEffectsOnTrack();
1280 parameters = cmb_outer_tsos->trackParameters();
1281 } else {
1282 materialEffects = nullptr;
1283 parameters = nullptr;
1284 }
1285 } else {
1286 // no middle material or parameters
1287 if (!materialEffects) m_messageHelper->printWarning(19);
1288 if (!parameters) m_messageHelper->printWarning(20);
1289 }
1290
1291 if (!middleTSOS) {
1292 // no CaloDeposit TSOS found
1293 m_messageHelper->printWarning(21);
1294 return nullptr;
1295 }
1296
1297 // outer calo scatterer
1298 std::unique_ptr<Trk::TrackStateOnSurface> outerTSOS;
1299
1300 double pInner{0.}, pOuter{0.};
1301 if (materialEffects && parameters && m_calorimeterVolume->inside(parameters->position())) {
1302 const Trk::TrackStateOnSurface* const cmb_outer_tsos = (*s);
1303 pOuter = parameters->momentum().mag();
1304
1305 outerTSOS.reset(cmb_outer_tsos->clone());
1306
1307 const Trk::MaterialEffectsOnTrack* meot = dynamic_cast<const Trk::MaterialEffectsOnTrack*>(materialEffects);
1308
1309 if (!meot) {
1310 // outerScattering dynamic_cast failed
1311 m_messageHelper->printWarning(24);
1312 return nullptr;
1313 }
1314 outerScattering = meot->scatteringAngles();
1315
1316 // Go to the next surface
1317 ++s;
1318
1319 // get parameters at middleSurface for energy correction,
1320 // start with parameters from middle surface when vertex in fit
1321 if (outerScattering && middleTSOS) {
1322 parameters = middleTSOS->trackParameters();
1323 } else {
1324 // otherwise extrapolate outer to middleSurface without scattering correction
1325 param_owner = m_propagator->propagate(ctx, *parameters, middleTSOS->trackParameters()->associatedSurface(),
1327 parameters = param_owner.get();
1328 }
1330 if (parameters) {
1332 if (!param_owner) param_owner = parameters->uniqueClone();
1333 // corrected parameters (include unfitted calo energy deposit),
1334 // inner momentum = outer momentum plus energy deposit
1335 pInner = pOuter + energyDeposit;
1336 momentumUpdate(param_owner, pInner);
1338 parameters = param_owner.get();
1339 }
1340 } else {
1341 // no outer material or parameters
1342 if (!materialEffects) m_messageHelper->printWarning(22);
1343 if (!parameters) m_messageHelper->printWarning(23);
1344 }
1345
1346 // fail track if missing any calo surface or extrapolation failure
1347 if (!innerTSOS || !middleTSOS || !outerTSOS || !parameters) { return nullptr; }
1349 parameters = nullptr;
1350
1352 param_owner = m_propagator->propagate(ctx, *param_owner, innerTSOS->trackParameters()->associatedSurface(), Trk::oppositeMomentum,
1354
1356 if (innerScattering) { momentumUpdate(param_owner, pInner, true, -innerScattering->deltaPhi(), -innerScattering->deltaTheta()); }
1357
1358 std::unique_ptr<Trk::TrackParameters> perigee_owner;
1359 if (param_owner) {
1360 perigee_owner = m_propagator->propagate(ctx, *param_owner, *m_perigeeSurface, Trk::oppositeMomentum, false,
1363 if (perigee_owner && perigee_owner->surfaceType() != Trk::SurfaceType::Perigee) { perigee_owner.reset(); }
1364 }
1365
1366 // in case of problem above: clone combined perigee
1367 if (!perigee_owner) { perigee_owner = combinedTrack.perigeeParameters()->uniqueClone(); }
1368 // track back out to the 3 calo surfaces applying small correction for non-linearity
1369 param_owner = m_propagator->propagate(ctx, *perigee_owner, innerTSOS->trackParameters()->associatedSurface(), Trk::alongMomentum,
1371 if (!param_owner) {
1372 // failed propagation to innerTSOS
1373 m_messageHelper->printWarning(26);
1374 } else {
1375 if (innerScattering) { momentumUpdate(param_owner, pInner, true, innerScattering->deltaPhi(), innerScattering->deltaTheta()); }
1376
1377 param_owner = m_propagator->propagate(ctx, *param_owner, middleParameters->associatedSurface(), Trk::alongMomentum, false,
1379
1380 if (!param_owner) {
1381 // failed propagation to middleTSOS
1382 m_messageHelper->printWarning(27);
1383 } else {
1384 momentumUpdate(param_owner, pOuter);
1385 param_owner = m_propagator->propagate(ctx, *param_owner, outerTSOS->trackParameters()->associatedSurface(),
1387 }
1388 }
1389
1390 if (!param_owner) { return nullptr; }
1391
1392 if (outerScattering) { momentumUpdate(param_owner, pOuter, true, outerScattering->deltaPhi(), outerScattering->deltaTheta()); }
1393
1394 // small correction term
1395 const double deltaPhi = xAOD::P4Helpers::deltaPhi(outerTSOS->trackParameters()->momentum().phi(), param_owner->momentum().phi());
1396 const double deltaTheta = outerTSOS->trackParameters()->momentum().theta() - param_owner->momentum().theta();
1397
1398 momentumUpdate(perigee_owner, pInner, true, deltaPhi, deltaTheta);
1399
1400 std::unique_ptr<Trk::RecVertex> mbeamAxis = std::make_unique<Trk::RecVertex>(*m_beamAxis);
1402 std::unique_ptr<Trk::PseudoMeasurementOnTrack> vertexInFit{vertexOnTrack(*perigee_owner, vertex.get(), mbeamAxis.get())};
1403
1404 // create perigee TSOS
1405 trackStateOnSurfaces->push_back(Muon::MuonTSOSHelper::createPerigeeTSOS(std::move(perigee_owner)));
1406
1407 // including vertex region pseudoMeas if requested: in r21, this is always requested
1408 if (vertexInFit) {
1409 trackStateOnSurfaces->push_back(Muon::MuonTSOSHelper::createMeasTSOS(std::move(vertexInFit), nullptr, Trk::TrackStateOnSurface::Measurement));
1410 }
1411
1412 if (m_addElossID) {
1413 double Eloss{0.}, sigmaEloss{0.}, X0tot{0.}, sigmaDeltaPhitot2{0.}, sigmaDeltaThetatot2{0.};
1414
1415 std::vector<const Trk::TrackStateOnSurface*> scatter_tsos;
1416 scatter_tsos.reserve(combinedTrack.trackStateOnSurfaces()->size());
1417
1418 for (const Trk::TrackStateOnSurface* comb_tsos : *combinedTrack.trackStateOnSurfaces()) {
1419 if (!comb_tsos->trackParameters()) continue;
1420 if (!m_indetVolume->inside(comb_tsos->trackParameters()->position())) break;
1421 if (!comb_tsos->materialEffectsOnTrack()) { continue; }
1422 const double X0 = comb_tsos->materialEffectsOnTrack()->thicknessInX0();
1423 X0tot += X0;
1424 const Trk::MaterialEffectsOnTrack* meot =
1425 dynamic_cast<const Trk::MaterialEffectsOnTrack*>(comb_tsos->materialEffectsOnTrack());
1426
1427 if (!meot) { continue; }
1428 const Trk::EnergyLoss* energyLoss = meot->energyLoss();
1429 if (!energyLoss) { continue; }
1430 Eloss += energyLoss->deltaE();
1431 sigmaEloss += energyLoss->sigmaDeltaE();
1432
1433 ATH_MSG_DEBUG("CombinedMuonFit ID Eloss found r " << (comb_tsos->trackParameters())->position().perp() << " z "
1434 << (comb_tsos->trackParameters())->position().z() << " value "
1435 << energyLoss->deltaE() << " Eloss " << Eloss << " sigma Eloss "
1436 << energyLoss->sigmaDeltaE() << " X0 " << X0);
1437
1438 const Trk::ScatteringAngles* scat = meot->scatteringAngles();
1439 if (scat) {
1440 double sigmaDeltaPhi = scat->sigmaDeltaPhi();
1441 double sigmaDeltaTheta = scat->sigmaDeltaTheta();
1442 sigmaDeltaPhitot2 += sigmaDeltaPhi * sigmaDeltaPhi;
1443 sigmaDeltaThetatot2 += sigmaDeltaTheta * sigmaDeltaTheta;
1444 scatter_tsos.push_back(comb_tsos);
1445 }
1446 }
1447
1448 ATH_MSG_DEBUG("standaloneRefit Total ID Eloss " << Eloss << " sigma Eloss " << sigmaEloss << " X0 " << X0tot
1449 << " sigma scat phi " << std::sqrt(sigmaDeltaPhitot2) << " sigma scat theta "
1450 << std::sqrt(sigmaDeltaThetatot2));
1451 if (!scatter_tsos.empty()) {
1452 const int itsosMiddle = scatter_tsos.size() / 2;
1453 const Trk::TrackStateOnSurface* mid_scatter = scatter_tsos[itsosMiddle];
1454
1455 std::unique_ptr<Trk::EnergyLoss> energyLossNew = std::make_unique<Trk::EnergyLoss>(Eloss, sigmaEloss, sigmaEloss, sigmaEloss);
1456
1457 const Trk::Surface& surfNew = mid_scatter->trackParameters()->associatedSurface();
1458 Trk::ScatteringAngles scatNew{0., 0., std::sqrt(sigmaDeltaPhitot2), std::sqrt(sigmaDeltaThetatot2)};
1459
1460 std::bitset<Trk::MaterialEffectsBase::NumberOfMaterialEffectsTypes> meotPattern(0);
1463
1464 ATH_MSG_DEBUG(" itsosMiddle " << itsosMiddle << " tsosnr size " << scatter_tsos.size());
1465
1466 std::unique_ptr<Trk::MaterialEffectsOnTrack> meotNew = std::make_unique<Trk::MaterialEffectsOnTrack>(X0tot, scatNew, std::move(energyLossNew), surfNew, meotPattern);
1467
1468 std::unique_ptr<Trk::TrackParameters> parsNew = mid_scatter->trackParameters()->uniqueClone();
1469 std::bitset<Trk::TrackStateOnSurface::NumberOfTrackStateOnSurfaceTypes> typePatternScat(0);
1470 typePatternScat.set(Trk::TrackStateOnSurface::Scatterer);
1471
1472 std::unique_ptr<Trk::TrackStateOnSurface> newTSOS =
1473 std::make_unique<Trk::TrackStateOnSurface>(nullptr, std::move(parsNew), std::move(meotNew), typePatternScat);
1474
1475 trackStateOnSurfaces->push_back(std::move(newTSOS));
1476 ATH_MSG_DEBUG(" add new TSOS for ID ");
1477 }
1478
1479 } // end m_addElossID
1480
1481 // add the 3 surface calo model
1482 trackStateOnSurfaces->push_back(std::move(innerTSOS));
1483 trackStateOnSurfaces->push_back(std::move(middleTSOS));
1484 trackStateOnSurfaces->push_back(std::move(outerTSOS));
1485 const Trk::TrackParameters* outerTSOSParam = trackStateOnSurfaces->back()->trackParameters();
1486 // MS entrance perigee
1488 std::unique_ptr<Trk::TrackStateOnSurface> entranceTSOS = entrancePerigee(ctx, outerTSOSParam);
1489 if (entranceTSOS) trackStateOnSurfaces->push_back(std::move(entranceTSOS));
1490 }
1491
1492 // leading spectrometer material
1493 bool haveLeadingMaterial = false;
1494
1496 for (; mat_it != cmb_end_itr; ++mat_it) {
1497 if ((*mat_it)->type(Trk::TrackStateOnSurface::Measurement)) break;
1498 haveLeadingMaterial = true;
1499 }
1500
1501 // protection against overruning the end of the vector
1502 if (mat_it == cmb_end_itr) {
1503 ATH_MSG_WARNING("At end of TSOS vector");
1504 return nullptr;
1505 }
1506
1507 if (haveLeadingMaterial) appendSelectedTSOS(*trackStateOnSurfaces, s, ++mat_it);
1508
1509 // insert phi pseudo measurement if necessary
1510 if (addPhiPseudo) {
1511 std::unique_ptr<Trk::TrackStateOnSurface> tsos = createPhiPseudoMeasurement(ctx, combinedTrack);
1512 if (tsos) trackStateOnSurfaces->push_back(std::move(tsos));
1513 }
1514
1515 // then append the remaining TSOS from the input track
1516 appendSelectedTSOS(*trackStateOnSurfaces, mat_it, cmb_end_itr);
1517
1518 // create track for refit
1519 std::unique_ptr<Trk::Track> standaloneTrack =
1520 std::make_unique<Trk::Track>(combinedTrack.info(), std::move(trackStateOnSurfaces), nullptr);
1521 standaloneTrack->info().setPatternRecognitionInfo(Trk::TrackInfo::MuidStandaloneRefit);
1522 if (m_trackQuery->isCombined(*standaloneTrack, ctx)) { ATH_MSG_WARNING(" This should not happen standalone Track has ID hits "); }
1523
1524 if (msgLevel(MSG::DEBUG)) countAEOTs(*standaloneTrack, " in standalone Refit standaloneTrack track before fit ");
1525
1526 std::unique_ptr<Trk::Track> refittedTrack{fit(ctx, *standaloneTrack, false, Trk::muon)};
1527 if (!checkTrack("standaloneRefit", refittedTrack.get())) { return nullptr; }
1528
1529 // eventually this whole tool will use unique_ptrs
1530 // in the meantime, this allows the MuonErrorOptimisationTool and MuonRefitTool to use them
1531 if (refittedTrack) {
1532 if (!refittedTrack->fitQuality()) { return nullptr; }
1533
1534 if (!m_trackQuery->isCaloAssociated(*refittedTrack, ctx)) {
1535 // fail as calo incorrectly described
1536 m_messageHelper->printWarning(28);
1537 return nullptr;
1538 }
1539
1540 if (msgLevel(MSG::DEBUG)) countAEOTs(*refittedTrack, " standaloneRefit final refittedTrack ");
1541
1542 // fit with optimized spectrometer errors
1543 // this should also be inside the "if(refittedTrack) statement
1544 if (!m_muonErrorOptimizer.empty() && !refittedTrack->info().trackProperties(Trk::TrackInfo::StraightTrack) &&
1545 countAEOTs(*refittedTrack, " before optimize ") == 0) {
1546 ATH_MSG_VERBOSE(" perform spectrometer error optimization after cleaning ");
1547 std::unique_ptr<Trk::Track> optimizedTrack = m_muonErrorOptimizer->optimiseErrors(*refittedTrack, ctx);
1548
1549 if (checkTrack("standaloneRefitOpt", optimizedTrack.get())) {
1550 refittedTrack.swap(optimizedTrack);
1551 if (msgLevel(MSG::DEBUG)) countAEOTs(*refittedTrack, " standaloneRefit alignment errors Track ");
1552 }
1553 }
1554 }
1555
1556 // have to release it until the whole tool is migrated to unique_ptr
1557 return refittedTrack;
1558 }
1559
1560 std::unique_ptr<Trk::Track> CombinedMuonTrackBuilder::addIDMSerrors(const Trk::Track* track) const {
1561 //
1562 // take track and correct the two scattering planes in the Calorimeter
1563 // to take into account m_IDMS_rzSigma and m_IDMS_xySigma
1564 //
1565 // returns a new Track or nullptr does not modify the input in any way
1566 //
1567 if (!m_addIDMSerrors) { return nullptr; }
1568
1569 ATH_MSG_DEBUG(" CombinedMuonTrackBuilder addIDMSerrors to track ");
1570
1572 const Trk::TrackStateOnSurface* id_exit = nullptr;
1573 const Trk::TrackStateOnSurface* calo_entrance = nullptr;
1574 const Trk::TrackStateOnSurface* calo_exit = nullptr;
1575 const Trk::TrackStateOnSurface* ms_entrance = nullptr;
1576
1577 m_alignUncertTool_theta->get_track_state_measures(track, id_exit, calo_entrance, calo_exit, ms_entrance);
1579 if (!calo_entrance || !calo_exit || !ms_entrance) {
1580 ATH_MSG_DEBUG(" addIDMSerrors keep original track ");
1581 return nullptr;
1582 }
1583
1584 auto trackStateOnSurfaces = std::make_unique<Trk::TrackStates>();
1585 trackStateOnSurfaces->reserve(track->trackStateOnSurfaces()->size());
1586
1587 for (const Trk::TrackStateOnSurface* trk_srf : *track->trackStateOnSurfaces()) {
1588 if (calo_entrance == trk_srf || calo_entrance == trk_srf) {
1589 if (!trk_srf->materialEffectsOnTrack()) {
1590 ATH_MSG_DEBUG("No material effect on track");
1591 continue;
1592 }
1593 const Trk::MaterialEffectsOnTrack* meot =
1594 dynamic_cast<const Trk::MaterialEffectsOnTrack*>(trk_srf->materialEffectsOnTrack());
1595 if (!meot) {
1596 ATH_MSG_WARNING(" This should not happen: no MaterialEffectsOnTrack for scatterer ");
1597 continue;
1598 }
1599 const Trk::ScatteringAngles* scat = meot->scatteringAngles();
1600 if (!scat) {
1601 ATH_MSG_WARNING(" This should not happen: no Scattering Angles for scatterer ");
1602 continue;
1603 }
1604
1605 float sigmaDeltaPhi = std::hypot(scat->sigmaDeltaPhi(), m_alignUncertTool_phi->get_uncertainty(track));
1606 float sigmaDeltaTheta = std::hypot(scat->sigmaDeltaTheta(), m_alignUncertTool_theta->get_uncertainty(track));
1607 float X0 = trk_srf->materialEffectsOnTrack()->thicknessInX0();
1608 //
1609 auto energyLossNew = std::make_unique<Trk::EnergyLoss>(0., 0., 0., 0.);
1610 auto scatNew = Trk::ScatteringAngles(0., 0., sigmaDeltaPhi, sigmaDeltaTheta);
1611
1612 const Trk::Surface& surfNew = trk_srf->trackParameters()->associatedSurface();
1613
1614 std::bitset<Trk::MaterialEffectsBase::NumberOfMaterialEffectsTypes> meotPattern(0);
1617
1618 auto meotNew = std::make_unique<Trk::MaterialEffectsOnTrack>(
1619 X0,
1620 scatNew,
1621 std::move(energyLossNew),
1622 surfNew,
1623 meotPattern);
1624 auto parsNew = trk_srf->trackParameters()->uniqueClone();
1625
1626 std::bitset<Trk::TrackStateOnSurface::NumberOfTrackStateOnSurfaceTypes> typePatternScat(0);
1627 typePatternScat.set(Trk::TrackStateOnSurface::Scatterer);
1628
1629 const Trk::TrackStateOnSurface* newTSOS =
1630 new Trk::TrackStateOnSurface(nullptr, std::move(parsNew), std::move(meotNew), typePatternScat);
1631 trackStateOnSurfaces->push_back(newTSOS);
1632
1633 ATH_MSG_DEBUG(" old Calo scatterer had sigmaDeltaPhi mrad " << scat->sigmaDeltaPhi() * 1000 << " sigmaDeltaTheta mrad "
1634 << scat->sigmaDeltaTheta() * 1000 << " X0 " << X0);
1635
1636 ATH_MSG_DEBUG(" new Calo scatterer made with sigmaDeltaPhi mrad " << sigmaDeltaPhi * 1000 << " sigmaDeltaTheta mrad "
1637 << sigmaDeltaTheta * 1000);
1638
1639 } else {
1640 // skip AEOTs
1641 if (trk_srf->alignmentEffectsOnTrack()) {
1642 ATH_MSG_DEBUG(" addIDMSerrors alignmentEffectsOnTrack() found on track ");
1643 continue;
1644 }
1645 trackStateOnSurfaces->push_back(trk_srf->clone());
1646 }
1647 }
1648 ATH_MSG_DEBUG(" trackStateOnSurfaces on input track " << track->trackStateOnSurfaces()->size() << " trackStateOnSurfaces found "
1649 << trackStateOnSurfaces->size());
1650
1651 std::unique_ptr<Trk::Track> newTrack = std::make_unique<Trk::Track>(track->info(), std::move(trackStateOnSurfaces), nullptr);
1652 return newTrack;
1653 }
1654
1657 // spectrometer measurement selection
1658 std::vector<const Trk::Surface*> measurementSurfaces;
1659 measurementSurfaces.reserve(trackStateOnSurfaces.size());
1660 const Trk::Surface* previousSurface = nullptr;
1661
1663 for (; s != end; ++s) {
1664 const Trk::TrackStateOnSurface& tsos = **s;
1665 if (tsos.alignmentEffectsOnTrack()) {
1666 ATH_MSG_VERBOSE("appendSelectedTSOS:: alignmentEffectsOnTrack ");
1667 }
1668 // skip non-understood features in iPatFitter
1669 if (!tsos.measurementOnTrack() && !tsos.materialEffectsOnTrack()) {
1671 ATH_MSG_VERBOSE("appendSelectedTSOS:: skip a perigee without material and measuremet "<<tsos);
1672 continue;
1673 } else if (!tsos.type(Trk::TrackStateOnSurface::Hole) || !tsos.trackParameters()) {
1674 ATH_MSG_VERBOSE("appendSelectedTSOS:: skip unrecognized TSOS " << tsos.dumpType());
1675 continue;
1676 }
1677 }
1678
1679 if (tsos.measurementOnTrack()) {
1680 // skip any pseudo measurements
1681 if (dynamic_cast<const Trk::PseudoMeasurementOnTrack*>(tsos.measurementOnTrack())) { continue; }
1682
1683 // skip duplicate measurements on same surface
1684 const Trk::Surface* surface = &tsos.measurementOnTrack()->associatedSurface();
1685 if (previousSurface &&
1686 std::find(measurementSurfaces.begin(), measurementSurfaces.end(), surface) != measurementSurfaces.end()) {
1687 // skip duplicate measurement
1688 m_messageHelper->printWarning(34, m_idHelperSvc->toString(m_edmHelperSvc->getIdentifier(*(tsos.measurementOnTrack()))));
1689 continue;
1690 }
1691
1692 measurementSurfaces.push_back(surface);
1693 previousSurface = surface;
1694 }
1695 trackStateOnSurfaces.push_back(tsos.clone());
1696 }
1697 }
1698
1700 const Trk::TrackParameters*& combinedEnergyParameters,
1701 const Trk::TrackParameters*& muonEnergyParameters) const {
1702 // will also set the caloEnergyParameters (from both combinedTrack and muonTrack)
1703 combinedEnergyParameters = nullptr;
1704 muonEnergyParameters = nullptr;
1705
1706 // quit if missing track
1707 if (!combinedTrack || !muonTrack) return nullptr;
1708
1709 // muonTrack: get parameters at CaloDeposit
1711
1712 while (!(**s).type(Trk::TrackStateOnSurface::CaloDeposit)) {
1713 if (++s == muonTrack->trackStateOnSurfaces()->end()) {
1714 // muonTrack without caloEnergy association
1715 m_messageHelper->printWarning(35);
1716 return nullptr;
1717 }
1718 }
1719 muonEnergyParameters = (**s).trackParameters();
1720
1721 // find corresponding parameters from combinedTrack
1722 s = combinedTrack->trackStateOnSurfaces()->begin();
1723 while (!(**s).type(Trk::TrackStateOnSurface::CaloDeposit)) {
1724 if (++s == combinedTrack->trackStateOnSurfaces()->end()) {
1725 // combinedTrack without caloEnergy association
1726 m_messageHelper->printWarning(36);
1727 return nullptr;
1728 }
1729 }
1730
1731 combinedEnergyParameters = (**s).trackParameters();
1732 if (muonEnergyParameters && combinedEnergyParameters) {
1733 ATH_MSG_DEBUG("muon and combined EnergyParameters: " << muonEnergyParameters->momentum().mag() << " "
1734 << combinedEnergyParameters->momentum().mag());
1735 }
1736 // success!
1737 return m_trackQuery->caloEnergy(*combinedTrack);
1738 }
1739
1741 const EventContext& ctx, const Trk::Track& spectrometerTrack, const Trk::TrackParameters& parameters,
1742 Trk::ParticleHypothesis particleHypothesis, Trk::RunOutlierRemoval runOutlier,
1743 const std::vector<std::unique_ptr<const Trk::TrackStateOnSurface>>& spectrometerTSOS, const Trk::RecVertex* vertex,
1744 const Trk::RecVertex* mbeamAxis, const Trk::PerigeeSurface* mperigeeSurface, const Trk::Perigee* seedParameters) const {
1745 ATH_MSG_DEBUG(" createExtrapolatedTrack() - " << __LINE__ << ": pt " << parameters.momentum().perp() << " r "
1746 << parameters.position().perp() << " z " << parameters.position().z() << " cov "
1747 << parameters.covariance() << " muonfit " << (particleHypothesis == Trk::muon));
1748
1749 std::vector<std::unique_ptr<const Trk::TrackStateOnSurface>> caloTSOS, leadingTSOS;
1750
1751 std::unique_ptr<const Trk::TrackParameters> track_param_owner;
1752 const Trk::TrackParameters* trackParameters{nullptr};
1753 const Trk::Perigee* perigee{nullptr};
1754
1755 if (vertex && m_indetVolume->inside(parameters.position())) { perigee = dynamic_cast<const Trk::Perigee*>(&parameters); }
1756 if (perigee) {
1757 ATH_MSG_DEBUG("createExtrapolatedTrack(): Got a perigee ");
1758 trackParameters = perigee;
1759 } else {
1760 ATH_MSG_DEBUG("createExtrapolatedTrack(): no perigee");
1761 // extrapolate backwards to associate leading material in spectrometer
1762 // (provided material has already been allocated between measurements)
1763 const Trk::TrackParameters* leadingParameters = &parameters;
1764 if (particleHypothesis == Trk::muon) {
1765 bool haveMaterial{false}, haveLeadingMaterial{false}, firstMSHit{false};
1766
1767 for (const std::unique_ptr<const Trk::TrackStateOnSurface>& s : spectrometerTSOS) {
1768 if (s->materialEffectsOnTrack()) {
1769 haveMaterial = true;
1770 if (!firstMSHit) haveLeadingMaterial = true;
1771 }
1772
1773 if (s->measurementOnTrack() && !firstMSHit) { firstMSHit = true; }
1774
1775 if (haveMaterial && firstMSHit) { break; }
1776 }
1777
1778 // only add leading material if there is no material in fron of first muon measurement
1779
1780 if (!m_materialAllocator.empty() && haveMaterial && !haveLeadingMaterial) {
1781 // protect the momentum to avoid excessive Eloss
1782 Amg::VectorX parameterVector = parameters.parameters();
1783
1784 constexpr double Emax = 50000.;
1785
1786 if (parameterVector[Trk::qOverP] == 0.) {
1787 parameterVector[Trk::qOverP] = 1. / Emax;
1788 } else {
1789 if (std::abs(parameterVector[Trk::qOverP]) * Emax < 1) {
1790 parameterVector[Trk::qOverP] = parameters.charge() / Emax;
1791 }
1792 }
1793 std::unique_ptr<Trk::TrackParameters> correctedParameters{parameters.associatedSurface().createUniqueTrackParameters(
1794 parameterVector[Trk::loc1], parameterVector[Trk::loc2], parameterVector[Trk::phi], parameterVector[Trk::theta],
1795 parameterVector[Trk::qOverP], std::nullopt)};
1796
1798 std::unique_ptr<std::vector<const Trk::TrackStateOnSurface*>> lead_tsos_from_alloc{
1799 m_materialAllocator->leadingSpectrometerTSOS(*correctedParameters, garbage)};
1800 if (lead_tsos_from_alloc) {
1801 for (const Trk::TrackStateOnSurface* l_tsos : *lead_tsos_from_alloc) leadingTSOS.emplace_back(l_tsos);
1802 }
1803 if (!leadingTSOS.empty() && leadingTSOS.front()->trackParameters()) {
1804 leadingParameters = leadingTSOS.front()->trackParameters();
1805 }
1806 }
1807 }
1808
1809 // extrapolate backwards to associate calorimeter material effects
1810 bool caloAssociated = false;
1811
1812 if (particleHypothesis == Trk::muon) {
1813 ATH_MSG_VERBOSE(" Retrieving Calorimeter TSOS from " << __func__ << " at line " << __LINE__);
1814 if (m_useCaloTG) {
1815 caloTSOS = getCaloTSOSfromMatProvider(*leadingParameters, spectrometerTrack);
1816 // Dump CaloTSOS
1817 //
1818 if (msgLevel(MSG::DEBUG)) {
1819 for (std::unique_ptr<const Trk::TrackStateOnSurface>& m : caloTSOS) {
1820 if (!m->materialEffectsOnTrack()) continue;
1821 const Trk::MaterialEffectsOnTrack* meot =
1822 dynamic_cast<const Trk::MaterialEffectsOnTrack*>(m->materialEffectsOnTrack());
1823 double pcalo{0.}, deltaP{0.};
1824 if (!meot) continue;
1825 if (meot->thicknessInX0() <= 20) { continue; }
1826 const Trk::ScatteringAngles* scatAngles = meot->scatteringAngles();
1827
1828 ATH_MSG_DEBUG(" Calorimeter X0 " << meot->thicknessInX0() << " pointer scat " << scatAngles);
1829
1830 if (!scatAngles) { continue; }
1831 pcalo = m->trackParameters()->momentum().mag();
1832
1833 const double pullPhi = scatAngles->deltaPhi() / scatAngles->sigmaDeltaPhi();
1834 const double pullTheta = scatAngles->deltaTheta() / scatAngles->sigmaDeltaTheta();
1835
1836 ATH_MSG_DEBUG(" Calorimeter scatterer deltaPhi " << scatAngles->deltaPhi() << " pull " << pullPhi
1837 << " deltaTheta " << scatAngles->deltaTheta() << " pull "
1838 << pullTheta);
1839
1840 const Trk::EnergyLoss* energyLoss = meot->energyLoss();
1841 if (!energyLoss) continue;
1842
1843 if (m->trackParameters()) {
1844 ATH_MSG_DEBUG("Eloss found r " << (m->trackParameters())->position().perp() << " z "
1845 << (m->trackParameters())->position().z() << " deltaE "
1846 << energyLoss->deltaE());
1847 }
1848
1850 double caloEloss = std::abs(energyLoss->deltaE());
1851 if (m->trackParameters()) { deltaP = m->trackParameters()->momentum().mag() - pcalo; }
1852
1853 ATH_MSG_DEBUG(" Calorimeter Deposit " << caloEloss << " pcalo Entrance " << pcalo << " deltaP " << deltaP);
1854 }
1855 }
1856 }
1857 } else {
1858 caloTSOS = m_caloTSOS->caloTSOS(ctx, *leadingParameters);
1859 }
1860
1861 if (caloTSOS.size() > 2) {
1862 caloAssociated = true;
1863 } else {
1864 ATH_MSG_VERBOSE("Failed to associated calorimeter");
1865 }
1866 } else {
1867 // TDDO Run2 Calo TG
1868 std::unique_ptr<const Trk::TrackStateOnSurface> tsos = m_caloTSOS->innerTSOS(ctx, parameters);
1869 if (tsos) {
1870 caloTSOS.push_back(std::move(tsos));
1871 tsos = m_caloTSOS->outerTSOS(ctx, *caloTSOS.back()->trackParameters());
1872 if (tsos) {
1873 caloAssociated = true;
1874 caloTSOS.push_back(std::move(tsos));
1875 }
1876 }
1877 ATH_MSG_VERBOSE("Special non-muon case for calo: " << caloAssociated);
1878 }
1879
1880 // if association OK, create perigee surface and back-track to it
1881 if (caloAssociated) {
1882 MagField::AtlasFieldCache fieldCache;
1883 // Get field cache object
1884 if (!loadMagneticField(ctx, fieldCache)) return nullptr;
1885
1886 if (fieldCache.toroidOn()) {
1887 const Trk::TrackParameters* oldParameters = caloTSOS.front()->trackParameters();
1888
1889 if (oldParameters){
1890 if(not oldParameters->covariance()) { ATH_MSG_VERBOSE(" createExtrapolatedTrack: no cov (0)"); }
1891 // chickened out of sorting out ownership
1892 track_param_owner = m_propagator->propagate(ctx, *oldParameters, *mperigeeSurface, Trk::oppositeMomentum, false,
1894 }
1895 } else {
1896 track_param_owner = m_propagatorSL->propagate(ctx, parameters, *mperigeeSurface, Trk::oppositeMomentum, false,
1898 }
1899 trackParameters = track_param_owner.get();
1900
1901 // only accept when perigee in indet tracking volume
1902 if (trackParameters && !m_indetVolume->inside(trackParameters->position())) {
1903 ATH_MSG_DEBUG(" back extrapolation problem: probably outside indet volume ");
1904 caloAssociated = false;
1905 }
1906
1907 if (trackParameters && !trackParameters->covariance()) { ATH_MSG_VERBOSE(" createExtrapolatedTrack: no cov (1)"); }
1908
1909 if (trackParameters) {
1910 ATH_MSG_VERBOSE(" Seed parameter: r " << trackParameters->position().perp() << " z " << trackParameters->position().z()
1911 << " pt " << trackParameters->momentum().perp());
1912 }
1913
1914 } // if (caloAssociated) {
1915 // start from vertex in case of calo association problem
1916 else if (vertex) {
1917 ATH_MSG_DEBUG(" back extrapolation problem: retry with tracking out from vertex ");
1918 // delete any existing calo objects
1919 caloTSOS.clear();
1920
1921 // track out from vertex
1922 const Amg::Vector3D momentum = parameters.position().unit() * Gaudi::Units::TeV;
1923
1924 track_param_owner = std::make_unique<Trk::Perigee>(vertex->position(), momentum, 1., *mperigeeSurface);
1925 trackParameters = track_param_owner.get();
1926 particleHypothesis = Trk::nonInteracting;
1927 runOutlier = false;
1928
1929 ATH_MSG_VERBOSE(" Retrieving Calorimeter TSOS from " << __func__ << " at line " << __LINE__);
1930
1931 if (m_useCaloTG) {
1932 caloTSOS = getCaloTSOSfromMatProvider(*trackParameters, spectrometerTrack);
1933 } else {
1934 std::unique_ptr<const Trk::TrackStateOnSurface> tsos = m_caloTSOS->innerTSOS(ctx, *trackParameters);
1935 if (tsos) {
1936 caloTSOS.push_back(std::move(tsos));
1937 tsos = m_caloTSOS->outerTSOS(ctx, *trackParameters);
1938 if (tsos) {
1939 caloTSOS.push_back(std::move(tsos));
1940 } else {
1941 track_param_owner.reset();
1942 }
1943 }
1944 }
1945 trackParameters = track_param_owner.get();
1946 }
1947
1948 // failure in calo association and/or extrapolation to indet
1949 if (!trackParameters || caloTSOS.empty()) {
1950 ATH_MSG_DEBUG(" perigee back-extrapolation fails ");
1951 return nullptr;
1952 }
1953 } // if (perigee) {
1954
1955 // set seed if provided
1956 if (seedParameters) { trackParameters = seedParameters; }
1957
1958 // append TSOS objects into DataVector
1959 // reserve allows for perigee + vertex + calo + entrancePerigee + spectrometer TSOS
1960 const unsigned int size = spectrometerTSOS.size() + 3 + caloTSOS.size() + leadingTSOS.size();
1961
1962 auto trackStateOnSurfaces = std::make_unique<Trk::TrackStates>();
1963 trackStateOnSurfaces->reserve(size);
1964
1965 // start with perigee TSOS (this just carries the perigee parameters)
1966
1967 if (trackParameters && !trackParameters->covariance()) { ATH_MSG_VERBOSE(" createExtrapolatedTrack: no cov (2)"); }
1968
1969 if (trackParameters) {
1970 if (trackParameters->surfaceType() != Trk::SurfaceType::Perigee) {
1971 ATH_MSG_DEBUG("createExtrapolatedTrack() - Track parameters are not perigee " << (*trackParameters));
1972 }
1973 trackStateOnSurfaces->push_back(Muon::MuonTSOSHelper::createPerigeeTSOS(trackParameters->uniqueClone()));
1974 }
1975
1976 // optionally append a pseudoMeasurement describing the vertex
1977 if (vertex && trackParameters) {
1978 std::unique_ptr<Trk::PseudoMeasurementOnTrack> vertexInFit = vertexOnTrack(*trackParameters, vertex, mbeamAxis);
1979 if (vertexInFit) {
1980 ATH_MSG_VERBOSE("Adding vertex constraint ");
1981 trackStateOnSurfaces->push_back(Muon::MuonTSOSHelper::createMeasTSOS(std::move(vertexInFit), nullptr, Trk::TrackStateOnSurface::Measurement));
1982 }
1983 }
1984
1985 // append calo TSOS
1986 for (std::unique_ptr<const Trk::TrackStateOnSurface>& c_tsos : caloTSOS) { trackStateOnSurfaces->push_back(std::move(c_tsos)); }
1987 caloTSOS.clear();
1988 // MS entrance perigee
1990 ATH_MSG_DEBUG("adding perigee at spectrometer entrance");
1991 const Trk::TrackParameters* mstrackParameters = trackStateOnSurfaces->back()->trackParameters();
1992
1993 if (!mstrackParameters) { mstrackParameters = spectrometerTSOS.front()->trackParameters(); }
1994
1995 if (mstrackParameters) {
1996 std::unique_ptr<Trk::TrackStateOnSurface> entranceTSOS = entrancePerigee(ctx, mstrackParameters);
1997 if (entranceTSOS) { trackStateOnSurfaces->push_back(std::move(entranceTSOS)); }
1998 }
1999 }
2000
2001 // append leading MS material TSOS
2002 for (std::unique_ptr<const Trk::TrackStateOnSurface>& c_tsos : leadingTSOS) {
2003 if (c_tsos->materialEffectsOnTrack()) { trackStateOnSurfaces->push_back(std::move(c_tsos)); }
2004 }
2005 leadingTSOS.clear();
2006
2007 // append the remaining spectrometer TSOS
2008 for (const auto& s : spectrometerTSOS) {
2009 if (!s->type(Trk::TrackStateOnSurface::Perigee)) {
2011 trackStateOnSurfaces->push_back(s->clone());
2012 }
2013
2014 if (s->measurementOnTrack() && dynamic_cast<const Trk::PseudoMeasurementOnTrack*>(s->measurementOnTrack())) {
2015 ATH_MSG_VERBOSE(" MS Pseudo");
2016 }
2017 }
2018
2019 // create track
2020 std::unique_ptr<Trk::Track> track =
2021 std::make_unique<Trk::Track>(spectrometerTrack.info(), std::move(trackStateOnSurfaces), nullptr);
2022
2023 if (!track->perigeeParameters()) {
2024 ATH_MSG_DEBUG("Reject track without perigee.");
2025 return nullptr;
2026 }
2027 dumpCaloEloss(track.get(), " createExtrapolatedTrack ");
2028 if (msgLevel(MSG::DEBUG)) countAEOTs(*track, " createExtrapolatedTrack before fit ");
2029
2030 // remove material when curvature badly determined (to remove energy loss uncertainty)
2031 if (particleHypothesis == Trk::nonInteracting) {
2032 ATH_MSG_VERBOSE(" remove spectrometer material ");
2034 }
2035
2036 // fit the track
2037 ATH_MSG_VERBOSE(" fit SA track with " << track->trackStateOnSurfaces()->size() << " TSOS"
2038 << (particleHypothesis == Trk::nonInteracting ? " using nonInteracting hypothesis"
2039 : "usig interacting hypothesis"));
2040
2041 std::unique_ptr<Trk::Track> fittedTrack{fit(ctx, *track, runOutlier, particleHypothesis)};
2042 if (fittedTrack) {
2043 if (msgLevel(MSG::DEBUG)) countAEOTs(*fittedTrack, " createExtrapolatedTrack after fit");
2044
2045 // only accept when perigee in indet tracking volume
2046 if (fittedTrack->perigeeParameters() && !m_indetVolume->inside(fittedTrack->perigeeParameters()->position())) {
2047 ATH_MSG_DEBUG(" back extrapolation problem: fitted perigee outside indet volume ");
2048 return nullptr;
2049 }
2050
2051 // limit momentum for future energy loss allocation
2052 if (particleHypothesis != Trk::muon) {
2053 ATH_MSG_VERBOSE(" set momentum limit ");
2054 removeSpectrometerMaterial(fittedTrack);
2055 }
2056
2057 ATH_MSG_VERBOSE(" found track " << m_printer->print(*fittedTrack));
2058 return fittedTrack;
2059 }
2060 // return the unfitted track in case of problem
2061 return track;
2062 }
2063
2064 std::unique_ptr<Trk::Track> CombinedMuonTrackBuilder::createIndetTrack(const Trk::TrackInfo& info,
2065 const Trk::TrackStates* tsos) const {
2066 // create indet track TSOS vector
2067 auto trackStateOnSurfaces = std::make_unique<Trk::TrackStates>();
2070
2071
2072 // set end iterator to be the first TSOS after the indet
2073 unsigned size = 1;
2074
2076
2077 std::unique_ptr<Trk::TrackStateOnSurface> perigeeTSOS {(**s).clone()};
2078
2079 ++s; // keep start perigee where-ever!
2080 for (; s != end; ++s) {
2081 ++size;
2082 if (!m_indetVolume->inside((**s).trackParameters()->position())) { break; }
2083 }
2084 end = s;
2085
2086 trackStateOnSurfaces->reserve(size);
2087 trackStateOnSurfaces->push_back(std::move(perigeeTSOS));
2088
2089 // then append selected TSOS
2090 appendSelectedTSOS(*trackStateOnSurfaces, begin, end);
2091
2092 return std::make_unique<Trk::Track>(info, std::move(trackStateOnSurfaces), nullptr);
2093 }
2094
2095 std::unique_ptr<Trk::Track> CombinedMuonTrackBuilder::createMuonTrack(const EventContext& ctx, const Trk::Track& muonTrack,
2096 const Trk::TrackParameters* parameters, std::unique_ptr<CaloEnergy> caloEnergy,
2097 const Trk::TrackStates* tsos) const{
2100 size_t size = tsos->size();
2101
2102 if (msgLevel(MSG::DEBUG)) countAEOTs(muonTrack, " createMuonTrack ");
2103
2104 // set iterator to current TSOS on input track to be after the indet
2105 const Trk::TrackParameters* lastIDtp = nullptr;
2107 while ((**s).trackParameters() &&
2108 (m_indetVolume->inside((**s).trackParameters()->position()) || (**s).type(Trk::TrackStateOnSurface::Perigee))) {
2109 if (m_indetVolume->inside((**s).trackParameters()->position())) { lastIDtp = (**s).trackParameters(); }
2110 ++s;
2111 --size;
2112 }
2113
2114 // create muon track TSOS vector
2115 auto trackStateOnSurfaces = std::make_unique<Trk::TrackStates>();
2116
2117 // redo calo association from inside if requested
2118 bool redoCaloAssoc = false;
2119 if (parameters) {
2120 redoCaloAssoc = true;
2121
2122 // move current TSOS iterator to be outside the calorimeter
2123 while ((**s).trackParameters() &&
2124 (m_calorimeterVolume->inside((**s).trackParameters()->position()) || (**s).type(Trk::TrackStateOnSurface::Perigee))) {
2125 ++s;
2126 --size;
2127 }
2128
2129 // associate calo by extrapolation from last ID parameters
2130 std::vector<std::unique_ptr<const Trk::TrackStateOnSurface>> caloTSOS;
2131 if (m_useCaloTG) {
2132 if (!lastIDtp) { lastIDtp = parameters; }
2133 ATH_MSG_VERBOSE(" Retrieving Calorimeter TSOS from " << __func__ << " at line " << __LINE__);
2134 caloTSOS = getCaloTSOSfromMatProvider(*lastIDtp, muonTrack);
2135 } else {
2136 caloTSOS = m_caloTSOS->caloTSOS(ctx, *parameters);
2137 }
2138
2139 if (caloTSOS.size() < 3) {
2140 ATH_MSG_DEBUG(" muonTrack: parameters fail to fully intersect the calorimeter");
2141 return nullptr;
2142 }
2143
2144 size += caloTSOS.size();
2145 trackStateOnSurfaces->reserve(size + 1);
2146
2147 // start with the calo TSOS
2148 for (std::unique_ptr<const Trk::TrackStateOnSurface>& c_tsos : caloTSOS) { trackStateOnSurfaces->push_back(std::move(c_tsos)); }
2149
2150 } else {
2151 trackStateOnSurfaces->reserve(size + 1);
2152 }
2153
2154 // if requested, replace caloEnergy on appropriate TSOS
2156 if (caloEnergy && (**s).trackParameters() && m_calorimeterVolume->inside((**s).trackParameters()->position())) {
2157 const Trk::TrackStateOnSurface* TSOS = (**s).clone();
2158 trackStateOnSurfaces->push_back(TSOS);
2159 ++s;
2160
2161 // create MEOT owning CaloEnergy
2162 if ((**s).trackParameters() && m_calorimeterVolume->inside((**s).trackParameters()->position())) {
2163 std::bitset<Trk::MaterialEffectsBase::NumberOfMaterialEffectsTypes> typePattern;
2165
2166 std::unique_ptr<Trk::MaterialEffectsOnTrack> materialEffects =
2167 std::make_unique<Trk::MaterialEffectsOnTrack>(0., std::move(caloEnergy), (**s).trackParameters()->associatedSurface(), typePattern);
2168
2169 // create TSOS
2170
2171 std::bitset<Trk::TrackStateOnSurface::NumberOfTrackStateOnSurfaceTypes> type;
2173
2174 TSOS = new Trk::TrackStateOnSurface(nullptr, (**s).trackParameters()->uniqueClone(),std::move(materialEffects), type);
2175 trackStateOnSurfaces->push_back(TSOS);
2176 ++s;
2177 } else {
2178 // should never happen: FSR caloEnergy delete
2179 m_messageHelper->printWarning(37);
2180 }
2181 }
2182
2183 // MS entrance perigee
2184 bool hasAlreadyPerigee = false;
2186 // copy calorimeter TSOS
2187 while ((**s).trackParameters() && m_calorimeterVolume->inside((**s).trackParameters()->position())) {
2188 if (!(**s).type(Trk::TrackStateOnSurface::Perigee)) {
2189 const Trk::TrackStateOnSurface* TSOS = (**s).clone();
2190 trackStateOnSurfaces->push_back(TSOS);
2191 }
2192 ++s;
2193 }
2194
2195 // add entrance TSOS if not already present
2196 std::unique_ptr<Trk::TrackStateOnSurface> entranceTSOS;
2197
2198 if ((**s).type(Trk::TrackStateOnSurface::Perigee)) { hasAlreadyPerigee = true; }
2199
2200 if (!hasAlreadyPerigee) {
2201 if ((**s).trackParameters()) {
2202 entranceTSOS = entrancePerigee(ctx, (**s).trackParameters());
2203 } else {
2204 entranceTSOS = entrancePerigee(ctx, trackStateOnSurfaces->back()->trackParameters());
2205 }
2206 if (entranceTSOS) {
2207 double distance = (entranceTSOS->trackParameters()->position() - (**s).trackParameters()->position()).mag();
2208
2209 if (distance > 2000) {
2210 ATH_MSG_DEBUG(" Added Muon Entrance "
2211 << " r " << entranceTSOS->trackParameters()->position().perp() << " z "
2212 << entranceTSOS->trackParameters()->position().z() << " track pars r "
2213 << (**s).trackParameters()->position().perp() << " z " << (**s).trackParameters()->position().z());
2214 }
2215 trackStateOnSurfaces->push_back(std::move(entranceTSOS));
2216 hasAlreadyPerigee = true;
2217 }
2218 }
2219 }
2220
2221
2222 // then append selected TSOS from the extrapolated or spectrometer track
2223 appendSelectedTSOS(*trackStateOnSurfaces, s, end);
2225 if (!hasAlreadyPerigee && std::find_if(trackStateOnSurfaces->begin(), trackStateOnSurfaces->end(),
2226 [] (const Trk::TrackStateOnSurface* tsos){
2227 return tsos->type(Trk::TrackStateOnSurface::Perigee);
2228 }) == trackStateOnSurfaces->end() && muonTrack.perigeeParameters() ){
2229 trackStateOnSurfaces->push_back( Muon::MuonTSOSHelper::createPerigeeTSOS(muonTrack.perigeeParameters()->uniqueClone()));
2231 std::stable_sort(trackStateOnSurfaces->begin(),trackStateOnSurfaces->end(),
2233 return a->type(Trk::TrackStateOnSurface::Perigee) > b->type(Trk::TrackStateOnSurface::Perigee);
2234 });
2235 ATH_MSG_DEBUG(__FILE__<<":"<<__LINE__<<" No track perigee parameters were added. Copy the existing ones from the muon track");
2236 }
2237 std::unique_ptr<Trk::Track> newMuonTrack = std::make_unique<Trk::Track>(muonTrack.info(), std::move(trackStateOnSurfaces), nullptr);
2238 unsigned int num_ms{0}, num_precMS{0};
2239 for (const Trk::MeasurementBase* meas : *newMuonTrack->measurementsOnTrack()) {
2240 const Trk::RIO_OnTrack* rio = dynamic_cast<const Trk::RIO_OnTrack*>(meas);
2241 if (!rio || !m_idHelperSvc->isMuon(rio->identify())) continue;
2242 ++num_ms;
2243 num_precMS += !m_idHelperSvc->measuresPhi(rio->identify());
2244 }
2245 if (num_precMS < 3 || num_ms < 5) {
2246 ATH_MSG_VERBOSE(__FILE__":"<<__LINE__<<" MS track with too few meausrements constructed "<<std::endl<<
2247 m_printer->print(newMuonTrack->measurementsOnTrack()->stdcont()) );
2248 return nullptr;
2249 }
2250 // Updates the calo TSOS with the ones from TG+corrections (if needed)
2251 if (m_updateWithCaloTG && !m_useCaloTG && redoCaloAssoc) {
2252 ATH_MSG_VERBOSE("Updating Calorimeter TSOS in CreateMuonTrack ...");
2253 m_materialUpdator->updateCaloTSOS(*newMuonTrack, parameters);
2254 }
2255
2256 return newMuonTrack;
2257 }
2258
2259 std::unique_ptr<Trk::TrackStateOnSurface> CombinedMuonTrackBuilder::createPhiPseudoMeasurement(const EventContext& ctx,
2260 const Trk::Track& track) const {
2261 auto parameters = m_trackQuery->spectrometerParameters(track, ctx);
2262 Amg::MatrixX covarianceMatrix(1, 1);
2263 covarianceMatrix.setZero();
2264 covarianceMatrix(0, 0) = s_sigmaPhiSector * s_sigmaPhiSector * parameters->position().perp2();
2265
2266 std::unique_ptr<Trk::PseudoMeasurementOnTrack> pseudo = std::make_unique<Trk::PseudoMeasurementOnTrack>(
2268 std::move(covarianceMatrix),
2269 parameters->associatedSurface());
2270
2271 return Muon::MuonTSOSHelper::createMeasTSOS(std::move(pseudo), std::move(parameters), Trk::TrackStateOnSurface::Measurement);
2272 }
2273
2274 std::vector<std::unique_ptr<const Trk::TrackStateOnSurface>> CombinedMuonTrackBuilder::createSpectrometerTSOS(const EventContext& ctx,
2275 const Trk::Track& spectrometerTrack) const {
2276 const Trk::Perigee* measuredPerigee = spectrometerTrack.perigeeParameters();
2277 std::vector<std::unique_ptr<const Trk::TrackStateOnSurface>> spectrometerTSOS;
2278
2279 if (!measuredPerigee || !measuredPerigee->covariance() || !Amg::hasPositiveDiagElems(*measuredPerigee->covariance())) {
2280 // missing MeasuredPerigee for spectrometer track
2281 if (!measuredPerigee)
2282 m_messageHelper->printWarning(38);
2283 else if (!measuredPerigee->covariance())
2284 m_messageHelper->printWarning(38);
2285 else
2286 ATH_MSG_DEBUG("createSpectrometerTSOS::perigee covariance not valid");
2287 return spectrometerTSOS;
2288 }
2289
2290 double errorPhi = std::sqrt((*measuredPerigee->covariance())(Trk::phi0, Trk::phi0));
2291
2292 // create the spectrometer TSOS's for the extrapolated fit
2293 spectrometerTSOS.reserve(spectrometerTrack.trackStateOnSurfaces()->size());
2294
2295 // start with a 'phi sector constraint' pseudomeasurement when necessary
2296 unsigned numberPseudo = m_trackQuery->numberPseudoMeasurements(spectrometerTrack);
2297 if (errorPhi > s_sigmaPhiSector) { ++numberPseudo; }
2298
2299 if (numberPseudo > 1 && !m_trackQuery->isSectorOverlap(spectrometerTrack)) {
2300 ATH_MSG_VERBOSE("standaloneFit: add pseudo to constrain phi sector");
2301
2302 std::unique_ptr<Trk::TrackStateOnSurface> tsos = {createPhiPseudoMeasurement(ctx, spectrometerTrack)};
2303 if (tsos) { spectrometerTSOS.emplace_back(std::move(tsos)); }
2304 }
2305
2306 // make a measurement selection to fixup non-standard TSOS's
2307 double deltaZ = 0.;
2308 bool haveMeasurement = false;
2309
2310 std::vector<const Trk::Surface*> measurementSurfaces;
2311 measurementSurfaces.reserve(spectrometerTrack.trackStateOnSurfaces()->size());
2312
2313 unsigned numberMaterial{0}, numberParameters{0};
2314
2315 const Trk::Surface* previousSurface = nullptr;
2316 std::unique_ptr<const Trk::TrackStateOnSurface> previousTSOS;
2317
2318 for (const Trk::TrackStateOnSurface* s : *spectrometerTrack.trackStateOnSurfaces()) {
2319 // skip any leading material
2320 if (!haveMeasurement) {
2321 if (s->measurementOnTrack()) {
2322 haveMeasurement = true;
2323 } else if (s->materialEffectsOnTrack()) {
2324 continue;
2325 }
2326 }
2327
2328 // input statistics for VERBOSE
2329 const Trk::TrackParameters* trackParameters = s->trackParameters();
2330 if (msgLvl(MSG::VERBOSE)) {
2331 if (s->materialEffectsOnTrack()) ++numberMaterial;
2332 if (trackParameters) ++numberParameters;
2333 }
2334
2335 // skip unwanted TSOS and non-understood features in iPatFitter
2336 if (!s->measurementOnTrack() && !s->materialEffectsOnTrack()) {
2337 // remove holes as they will be reallocated
2338 if (s->type(Trk::TrackStateOnSurface::Hole)) continue;
2339
2340 // same for MS perigee
2341 if (s->type(Trk::TrackStateOnSurface::Perigee)) continue;
2342
2343 if (s->trackParameters()) {
2344 ATH_MSG_DEBUG("createSpectrometerTSOS:: skip unrecognized TSOS " << s->dumpType() << " r "
2345 << s->trackParameters()->position().perp() << " z "
2346 << s->trackParameters()->position().z());
2347 } else {
2348 // skip unrecognized TSOS without TrackParameters
2349 m_messageHelper->printWarning(39, s->dumpType());
2350 }
2351 continue;
2352 }
2353
2354 // several checks applied to measurements:
2355 bool trapezoid = false;
2356 bool rotatedTrap = false;
2357 if (s->measurementOnTrack()) {
2358 // skip pseudo
2359 if (dynamic_cast<const Trk::PseudoMeasurementOnTrack*>(s->measurementOnTrack())) {
2360 continue;
2361 }
2362 // careful with trapezoid ordering (put trapezoid before
2363 // rotatedTrapezoid)
2364 const Trk::Surface* surface = &s->measurementOnTrack()->associatedSurface();
2365
2366 if (previousSurface) { deltaZ = std::abs(previousSurface->center().z() - surface->center().z()); }
2367
2368 if (dynamic_cast<const Trk::PlaneSurface*>(surface)) {
2369 if (dynamic_cast<const Trk::TrapezoidBounds*>(&surface->bounds())) {
2370 trapezoid = true;
2371 } else if (dynamic_cast<const Trk::RotatedTrapezoidBounds*>(&surface->bounds())) {
2372 rotatedTrap = true;
2373 }
2374 }
2375
2376 // skip duplicate measurements on same surface
2377 if (previousSurface &&
2378 std::find(measurementSurfaces.begin(), measurementSurfaces.end(), surface) != measurementSurfaces.end()) {
2379 // skip duplicate measurement
2380 m_messageHelper->printWarning(40, m_idHelperSvc->toString(m_edmHelperSvc->getIdentifier(*(s->measurementOnTrack()))));
2381 continue;
2382 }
2383 measurementSurfaces.push_back(surface);
2384 previousSurface = surface;
2385
2386 } else if (previousTSOS) {
2387 spectrometerTSOS.emplace_back(std::move(previousTSOS));
2388 }
2389
2390 // trapezoid precedes rotatedTrapezoid
2391 std::unique_ptr<const Trk::TrackStateOnSurface> TSOS(s->clone());
2392 //cppcheck-suppress accessMoved
2393 if (previousTSOS) {
2394 if (trapezoid && deltaZ < 1. * Gaudi::Units::mm) {
2395 spectrometerTSOS.emplace_back(std::move(TSOS));
2396 TSOS = std::move(previousTSOS);
2397 } else {
2398 spectrometerTSOS.emplace_back(std::move(previousTSOS));
2399 }
2400 }
2401
2402 if (rotatedTrap) {
2403 previousTSOS.swap(TSOS);
2404 continue;
2405 }
2406
2407 spectrometerTSOS.emplace_back(std::move(TSOS));
2408 }
2409
2410 if (previousTSOS) spectrometerTSOS.emplace_back(std::move(previousTSOS));
2411
2412 ATH_MSG_VERBOSE(" input spectrometer track with " << spectrometerTrack.trackStateOnSurfaces()->size() << " TSOS, of which "
2413 << numberMaterial << " have MaterialEffects and " << numberParameters
2414 << " have TrackParameters");
2415
2416 return spectrometerTSOS;
2417 }
2418
2419 std::unique_ptr<Trk::TrackStateOnSurface> CombinedMuonTrackBuilder::entrancePerigee(const EventContext& ctx, const Trk::TrackParameters* parameters) const {
2420 // make sure the spectrometer entrance volume is available
2421 if (!parameters) return nullptr;
2422 const Trk::TrackingVolume* spectrometerEntrance = getVolume(ctx, "MuonSpectrometerEntrance");
2423 if (!spectrometerEntrance) return nullptr;
2424
2425 std::unique_ptr<Trk::TrackParameters> entranceParameters{
2426 m_extrapolator->extrapolateToVolume(ctx, *parameters, *spectrometerEntrance, Trk::anyDirection, Trk::nonInteracting)};
2427
2428 if (!entranceParameters) return nullptr;
2429
2430 Trk::PerigeeSurface surface(entranceParameters->position());
2431 std::unique_ptr<Trk::TrackParameters> trackParameters{m_extrapolator->extrapolateDirectly(ctx, *entranceParameters, surface)};
2432
2433 if (!trackParameters) return nullptr;
2434
2435 std::unique_ptr<Trk::Perigee> perigee =
2436 std::make_unique<Trk::Perigee>(trackParameters->position(), trackParameters->momentum(), trackParameters->charge(), std::move(surface));
2437 return std::unique_ptr<Trk::TrackStateOnSurface>(Muon::MuonTSOSHelper::createPerigeeTSOS(std::move(perigee)));
2438 }
2439
2440 std::unique_ptr<Trk::TrackParameters> CombinedMuonTrackBuilder::extrapolatedParameters(
2441 const EventContext& ctx, bool& badlyDeterminedCurvature, const Trk::Track& spectrometerTrack, const Trk::RecVertex* mvertex,
2442 const Trk::PerigeeSurface* mperigeeSurface) const {
2443 badlyDeterminedCurvature = false;
2444 const Trk::Perigee* measuredPerigee = spectrometerTrack.perigeeParameters();
2445
2446 if (!measuredPerigee || !measuredPerigee->covariance()) {
2447 // missing MeasuredPerigee for spectrometer track
2448 m_messageHelper->printWarning(42);
2449 return nullptr;
2450 }
2451
2452 // set starting parameters and measured momentum error
2453 auto parameters = m_trackQuery->spectrometerParameters(spectrometerTrack, ctx);
2454 if (!parameters || !parameters->covariance()) {
2455 // missing spectrometer parameters on spectrometer track
2456 m_messageHelper->printWarning(43);
2457 return nullptr;
2458 }
2459
2460 double errorP = std::sqrt(measuredPerigee->momentum().mag2() * (*measuredPerigee->covariance())(Trk::qOverP, Trk::qOverP));
2461
2462 // corrected parameters ensure the track fitting starts with a projective approximation
2463 std::unique_ptr<Trk::TrackParameters> correctedParameters{};
2464 Amg::VectorX parameterVector = parameters->parameters();
2465 double trackEnergy = 1. / std::abs(parameterVector[Trk::qOverP]);
2466
2467 // careful: need to reset parameters to have a sensible energy if starting from a lineFit
2468 if (m_trackQuery->isLineFit(spectrometerTrack)) {
2469 trackEnergy = m_lineMomentum;
2470 parameterVector[Trk::qOverP] = parameters->charge() / trackEnergy;
2471
2472 parameters = parameters->associatedSurface().createUniqueTrackParameters(
2473 parameterVector[Trk::loc1], parameterVector[Trk::loc2], parameterVector[Trk::phi], parameterVector[Trk::theta],
2474 parameterVector[Trk::qOverP], AmgSymMatrix(5)(*parameters->covariance()));
2475 }
2476
2477 // check if the track curvature is well determined (with sufficient energy to penetrate material)
2478 // (i.e. field off or small momentum error, starting parameters upstream of endcap toroid)
2479 bool curvatureOK = false;
2480
2481 const Trk::IPropagator* propagator = m_propagator.get();
2482 MagField::AtlasFieldCache fieldCache;
2483 // Get field cache object
2484 if (!loadMagneticField(ctx, fieldCache)) return nullptr;
2485 if (!fieldCache.toroidOn()) {
2486 curvatureOK = true;
2487 propagator = m_propagatorSL.get();
2488 } else if (std::abs(parameters->position().z()) < m_zECToroid &&
2489 (!m_trackQuery->isLineFit(spectrometerTrack) && errorP < m_largeMomentumError)) {
2490 curvatureOK = true;
2491 }
2492
2493 if (curvatureOK) {
2494 // TDDO Run2 Calo TG
2495 // energy loss correction
2496 std::unique_ptr<CaloEnergy> caloEnergy{
2497 m_caloEnergyParam->energyLoss(ctx, trackEnergy, parameters->position().eta(), parameters->position().phi())};
2498
2499 if (trackEnergy + caloEnergy->deltaE() < m_minEnergy) {
2500 ATH_MSG_DEBUG("standaloneFit: trapped in calorimeter");
2501 return nullptr;
2502 }
2503
2504 parameterVector[Trk::qOverP] = parameters->charge() / (trackEnergy + caloEnergy->deltaE());
2505 correctedParameters = parameters->associatedSurface().createUniqueTrackParameters(
2506 parameterVector[Trk::loc1], parameterVector[Trk::loc2], parameterVector[Trk::phi], parameterVector[Trk::theta],
2507 parameterVector[Trk::qOverP], AmgSymMatrix(5)(*parameters->covariance()));
2508
2509 // protect against spectrometer track with unrealistic energy loss
2510 // check material in spectrometer is not vastly greater than in the calo
2511 // (there are some very dense spectrometer regions)
2512 double spectrometerEnergyLoss = 0.;
2513
2515 Trk::TrackStates::const_iterator sEnd = spectrometerTrack.trackStateOnSurfaces()->end();
2516 for (; s != sEnd; ++s) {
2517 if (!(**s).materialEffectsOnTrack()) { continue; }
2518
2519 const Trk::MaterialEffectsOnTrack* meot = dynamic_cast<const Trk::MaterialEffectsOnTrack*>((**s).materialEffectsOnTrack());
2520
2521 if (meot && meot->energyLoss()) { spectrometerEnergyLoss += meot->energyLoss()->deltaE(); }
2522 }
2523
2524 if (std::abs(spectrometerEnergyLoss) > 1.5 * std::abs(caloEnergy->deltaE())) {
2525 curvatureOK = false;
2526 ATH_MSG_DEBUG("standaloneFit: excessive energy loss in spectrometer "
2527 << std::abs(spectrometerEnergyLoss / Gaudi::Units::GeV) << " GeV"
2528 << " in calo " << std::abs(caloEnergy->deltaE() / Gaudi::Units::GeV) << " GeV");
2529 }
2530 }
2531
2532 // check perigee in indet volume when the curvature is well determined
2533 // otherwise will assume projectivity for starting parameters
2534 if (curvatureOK) {
2535 std::unique_ptr<Trk::TrackParameters> perigee{propagator->propagate(
2536 ctx, *correctedParameters, *mperigeeSurface, Trk::oppositeMomentum, false, m_magFieldProperties, Trk::nonInteracting)};
2537
2538 if (!perigee) {
2539 ATH_MSG_DEBUG("standaloneFit: failed back extrapolation to perigee");
2540 return nullptr;
2541 }
2542
2543 // large impact: set phi to be projective (note iteration)
2544 if (std::abs(perigee->parameters()[Trk::d0]) < m_largeImpact || !fieldCache.toroidOn()) {
2545 ATH_MSG_DEBUG("Track d0 perigee: " << std::abs(perigee->parameters()[Trk::d0]) << " which is smaller than "
2546 << m_largeImpact);
2547 } else {
2548 Amg::Vector3D position = correctedParameters->position();
2549
2550 double deltaPhi = 0.;
2551 double deltaR = (position - perigee->position()).perp();
2552
2553 if (std::abs(deltaR * M_PI) > std::abs(perigee->parameters()[Trk::d0])) {
2554 deltaPhi = perigee->parameters()[Trk::d0] / deltaR;
2555 }
2556
2557 ATH_MSG_DEBUG("standaloneFit: large perigee impact " << perigee->parameters()[Trk::d0] << " deltaR, deltaPhi " << deltaR
2558 << ", " << deltaPhi);
2559
2560 parameterVector[Trk::phi0] += deltaPhi;
2561
2562 if (parameterVector[Trk::phi0] > M_PI) {
2563 parameterVector[Trk::phi0] -= 2. * M_PI;
2564 } else if (parameterVector[Trk::phi0] < -M_PI) {
2565 parameterVector[Trk::phi0] += 2. * M_PI;
2566 }
2567
2568 correctedParameters = parameters->associatedSurface().createUniqueTrackParameters(
2569 parameterVector[Trk::loc1], parameterVector[Trk::loc2], parameterVector[Trk::phi], parameterVector[Trk::theta],
2570 parameterVector[Trk::qOverP], AmgSymMatrix(5)(*parameters->covariance()));
2571
2572 perigee = propagator->propagate(ctx, *correctedParameters, *mperigeeSurface, Trk::oppositeMomentum, false,
2574
2575 if (perigee) {
2576 deltaPhi = 0.;
2577 deltaR = (position - perigee->position()).perp();
2578
2579 if (std::abs(deltaR * M_PI) > std::abs(perigee->parameters()[Trk::d0])) {
2580 deltaPhi = perigee->parameters()[Trk::d0] / deltaR;
2581 }
2582
2583 ATH_MSG_VERBOSE("standaloneFit: corrected perigee impact " << perigee->parameters()[Trk::d0] << " deltaR, deltaPhi "
2584 << deltaR << ", " << deltaPhi);
2585
2586 parameterVector[Trk::phi0] += deltaPhi;
2587 if (parameterVector[Trk::phi0] > M_PI) {
2588 parameterVector[Trk::phi0] -= 2. * M_PI;
2589 } else if (parameterVector[Trk::phi0] < -M_PI) {
2590 parameterVector[Trk::phi0] += 2. * M_PI;
2591 }
2592 correctedParameters = parameters->associatedSurface().createUniqueTrackParameters(
2593 parameterVector[Trk::loc1], parameterVector[Trk::loc2], parameterVector[Trk::phi], parameterVector[Trk::theta],
2594 parameterVector[Trk::qOverP], AmgSymMatrix(5)(*parameters->covariance()));
2595
2596 perigee = propagator->propagate(ctx, *correctedParameters, *mperigeeSurface, Trk::oppositeMomentum, false,
2598 }
2599
2600 if (perigee) {
2601 ATH_MSG_VERBOSE("standaloneFit: restart with impact " << perigee->parameters()[Trk::d0] << " phi0 "
2602 << perigee->parameters()[Trk::phi0]);
2603 }
2604 parameterVector[Trk::qOverP] = parameters->charge() / trackEnergy;
2605 correctedParameters = parameters->associatedSurface().createUniqueTrackParameters(
2606 parameterVector[Trk::loc1], parameterVector[Trk::loc2], parameterVector[Trk::phi], parameterVector[Trk::theta],
2607 parameterVector[Trk::qOverP], AmgSymMatrix(5)(*parameters->covariance()));
2608
2609 parameters = std::move(correctedParameters);
2610 }
2611
2612 // cut if perigee outside indet (but keep endcap halo)
2613 if (!perigee || !m_indetVolume->inside(perigee->position())) {
2614 if (perigee && perigee->position().z() * perigee->momentum().z() < 0. && perigee->momentum().eta() > 2.0) {
2615 ATH_MSG_DEBUG("standaloneFit: halo candidate, perigee at R " << perigee->position().perp() << " Z "
2616 << perigee->position().z());
2617 } else {
2618 ATH_MSG_DEBUG("standaloneFit: perigee outside indet volume");
2620 return nullptr;
2621 }
2622 }
2623 } else {
2624 // otherwise track out from origin (fix bug #54820)
2625 badlyDeterminedCurvature = true;
2626 Amg::Vector3D momentum = parameters->position().unit() * Gaudi::Units::TeV;
2627
2628 std::unique_ptr<const Trk::TrackParameters> trigParameters{m_trackQuery->triggerStationParameters(spectrometerTrack, ctx)};
2629
2630 if (trigParameters) { momentum = trigParameters->position().unit() * Gaudi::Units::TeV; }
2631
2632 if (msgLvl(MSG::VERBOSE)) {
2633 if (trigParameters) {
2634 ATH_MSG_VERBOSE("standaloneFit: imprecise curvature measurement -"
2635 << " start with line from origin to 1st trigger station ");
2636 } else {
2637 ATH_MSG_VERBOSE("standaloneFit: imprecise curvature measurement -"
2638 << " start with line from origin to 1st measurement ");
2639 }
2640 }
2641
2642 std::unique_ptr<Trk::TrackParameters> perigee =
2643 std::make_unique<Trk::Perigee>(mvertex->position(), momentum, 1., *mperigeeSurface);
2644
2645 parameters = m_propagator->propagate(ctx, *perigee, perigee->associatedSurface(), Trk::alongMomentum, false,
2647
2648 if (!parameters) {
2649 ATH_MSG_DEBUG("standaloneFit: failed back extrapolation to perigee");
2650 return nullptr;
2651 }
2652 }
2653
2654 return parameters;
2655 }
2656
2657 void CombinedMuonTrackBuilder::finalTrackBuild(const EventContext& ctx, std::unique_ptr<Trk::Track>& track) const {
2658 // as a final step:
2659 // refit the track if any holes can be recovered,
2660 // refit with optimization of the spectrometer hit errors,
2661 // add the corresponding TrackSummary to the track
2662 if (msgLevel(MSG::DEBUG)) countAEOTs(*track, " finalTrackBuilt input ");
2663 if (!m_muonHoleRecovery.empty()) {
2664 // chi2 before recovery
2665 double chi2Before = normalizedChi2(*track);
2666
2667 ATH_MSG_VERBOSE(" perform spectrometer hole recovery procedure... ");
2668
2669 std::unique_ptr<Trk::Track> recoveredTrack{m_muonHoleRecovery->recover(*track, ctx)};
2670
2671 // if (!checkTrack("finalTrackBuild1", recoveredTrack.get())) {
2672 // final track lost, this should not happen
2673 // m_messageHelper->printWarning(44);
2674 // As discussed in ATLASRECTS-7603, we want to suppress this until we can work on it
2675 // Keeping it here so we don't forget.
2676 // FIXME!
2677
2678 if (checkTrack("finalTrackBuild1", recoveredTrack.get())) {
2679 double chi2After = normalizedChi2(*recoveredTrack);
2680 if (chi2After < m_badFitChi2 || chi2After < chi2Before + 0.1) {
2681 track.swap(recoveredTrack);
2682 } else {
2683 ATH_MSG_VERBOSE(" track rejected by recovery as chi2 " << chi2After << " compared to " << chi2Before);
2684
2685 if (chi2Before > m_badFitChi2) {
2686 track.reset();
2687 return;
2688 }
2689 }
2690 }
2691 ATH_MSG_VERBOSE(" finished hole recovery procedure ");
2692 }
2693 /*
2694 * DO NOT REMOVE THIS CODE BLOCK AS IT WILL BE NEEDED IN A FUTURE MR!
2695 if (!m_muonHoleRecovery.empty()) {
2696 ATH_MSG_VERBOSE(" perform spectrometer hole recovery procedure... ");
2697 using MSTrackRecovery = Muon::IMuonHoleRecoveryTool::MSTrackRecovery;
2698 MSTrackRecovery recoverResult{};
2699 constexpr unsigned int max_itr = 3;
2700 unsigned int num_itr{0};
2701 do {
2702 const double chi2Before = normalizedChi2(*track);
2703 recoverResult = m_muonHoleRecovery->recover(ctx,*track);
2704 if (!recoverResult.track) {
2705 ATH_MSG_WARNING(__FILE__<<":"<<__LINE__<<" track got lost during hole recovery. That should not happen");
2706 break;
2707 }
2708 if (!checkTrack("holeRecovery", recoverResult.track.get())){
2709 m_messageHelper->printWarning(44);
2710 break;
2711 }
2712 if (recoverResult.new_meas) {
2713 recoverResult.track = fit(ctx, *recoverResult.track, true, Trk::muon);
2714 if (!recoverResult.track) {
2715 ATH_MSG_VERBOSE(__FILE__<<":"<<__LINE__<<" track got lost during refit of recovery.");
2716 break;
2717 }
2718 const double chi2After = normalizedChi2(*recoverResult.track);
2719 if (chi2After < chi2Before) {
2720 track.swap(recoverResult.track);
2721 ATH_MSG_VERBOSE("Recovered track has better quality... old chi2:"<<chi2Before<<". New chi2: "<<chi2After
2722 <<std::endl<<m_printer->printMeasurements(*recoverResult.track)<<std::endl
2723 <<std::endl<<m_printer->printMeasurements(*track));
2724 } else break;
2725 } else {
2726 track.swap(recoverResult.track);
2727 }
2728 } while(recoverResult.new_meas && (++num_itr) <= max_itr);
2729 ATH_MSG_VERBOSE(" finished hole recovery procedure ");
2730 }
2731 */
2732 // final fit with optimized spectrometer errors
2733 if (!m_muonErrorOptimizer.empty() && !track->info().trackProperties(Trk::TrackInfo::StraightTrack) &&
2734 countAEOTs(*track, " before optimize ") == 0) {
2735 ATH_MSG_VERBOSE(" perform spectrometer error optimization... ");
2736 std::unique_ptr<Trk::Track> optimizedTrack = m_muonErrorOptimizer->optimiseErrors(*track, ctx);
2737 if (checkTrack("finalTrackBuild2", optimizedTrack.get())) {
2738 track.swap(optimizedTrack);
2739 if (msgLevel(MSG::DEBUG)) countAEOTs(*track, " finalTrackBuilt alignment errors Track ");
2740 }
2741 }
2742
2743 // add the track summary
2744 m_trackSummary->updateTrack(ctx, *track);
2745 }
2746 void CombinedMuonTrackBuilder::momentumUpdate(std::unique_ptr<Trk::TrackParameters>& parameters, double updatedP,
2747 bool directionUpdate, double deltaPhi, double deltaTheta) const {
2748 if (!parameters) return;
2749
2750 std::unique_ptr<Trk::TrackParameters> updatedParameters;
2751
2752 // update for angle change
2753 Amg::Vector3D direction = parameters->momentum().unit();
2754
2755 if (directionUpdate) {
2756 double cosDeltaPhi = 0.;
2757 double sinDeltaPhi = std::sin(deltaPhi);
2758
2759 if (std::abs(sinDeltaPhi) < 1.) { cosDeltaPhi = std::sqrt(1. - sinDeltaPhi * sinDeltaPhi); }
2760
2761 double cosDeltaTheta = 0.;
2762 double sinDeltaTheta = std::sin(deltaTheta);
2763
2764 if (std::abs(sinDeltaTheta) < 1.) { cosDeltaTheta = std::sqrt(1. - sinDeltaTheta * sinDeltaTheta); }
2765
2766 double cosTheta = direction.z() * cosDeltaTheta - direction.perp() * sinDeltaTheta;
2767 if (std::abs(cosTheta) < 1.) {
2768 direction = Amg::Vector3D(direction.x() * cosDeltaPhi - direction.y() * sinDeltaPhi,
2769 direction.y() * cosDeltaPhi + direction.x() * sinDeltaPhi,
2770 direction.perp() * cosTheta / std::sqrt(1. - cosTheta * cosTheta));
2771
2772 } else {
2773 direction = Amg::Vector3D(0., 0., cosTheta);
2774 }
2775 direction = direction.unit();
2776 }
2777
2778 // update for momentum (magnitude) change
2779 Amg::Vector3D momentum = updatedP * direction;
2780
2781 // create updated parameters
2782 double charge = parameters->charge();
2783 Amg::Vector3D position = parameters->position();
2784 std::optional<AmgSymMatrix(5)> covariance =
2785 parameters->covariance() ? std::optional<AmgSymMatrix(5)>(*(parameters->covariance())) : std::nullopt;
2786 const Trk::Surface* surface = &(parameters->associatedSurface());
2787 updatedParameters = surface->createUniqueTrackParameters(position, momentum, charge, covariance);
2788
2789 if (updatedParameters) {
2790 parameters = std::move(updatedParameters);
2791 } else {
2792 // update failed, keeping original value
2793 m_messageHelper->printWarning(45);
2794 }
2795 }
2796 std::unique_ptr<Trk::Track> CombinedMuonTrackBuilder::reallocateMaterial(const EventContext& ctx,
2797 const Trk::Track& spectrometerTrack) const {
2798 // build MeasurementSet for the spectrometer
2799 const Trk::TrackParameters* perigeeStartValue = nullptr;
2800 double perigeeDistance = 0.;
2801
2802 Trk::MeasurementSet spectrometerMeasurements;
2803
2805 auto sEnd = spectrometerTrack.trackStateOnSurfaces()->end();
2806 for (; s != sEnd; ++s) {
2807 if ((**s).measurementOnTrack() && !(**s).type(Trk::TrackStateOnSurface::Outlier)) {
2808 // skip pseudo measurement(s)
2809 // FIXME - need phi pseudo in some cases
2810 if (dynamic_cast<const Trk::PseudoMeasurementOnTrack*>((**s).measurementOnTrack())) { continue; }
2811
2812 spectrometerMeasurements.push_back((**s).measurementOnTrack()->clone());
2813 if (!(**s).trackParameters() || (perigeeStartValue && (**s).trackParameters()->position().mag() > perigeeDistance)) {
2814 continue;
2815 }
2816
2817 perigeeDistance = (**s).trackParameters()->position().mag();
2818 perigeeStartValue = (**s).trackParameters();
2819 }
2820 }
2821
2822 // check perigeeStartValue defined
2823 if (!perigeeStartValue) {
2824 // FIXME: use spectrometerTrack.perigeeParameters()
2825 // null perigeeStartValue
2826 m_messageHelper->printWarning(46);
2827 return nullptr;
2828 }
2829
2830 // fit with various recovery strategies
2831 std::unique_ptr<Trk::Track> spectrometerFit = fit(ctx, spectrometerMeasurements, *perigeeStartValue, true, Trk::muon);
2832 if (!spectrometerFit) {
2833 spectrometerFit = fit(ctx, spectrometerMeasurements, *perigeeStartValue, false, Trk::muon);
2834
2835 if (!spectrometerFit) {
2836 spectrometerFit = fit(ctx, spectrometerMeasurements, *perigeeStartValue, false, Trk::nonInteracting);
2837
2838 if (!spectrometerFit) {
2839 // spectrometer refit fails
2840 m_messageHelper->printWarning(47);
2841 }
2842 }
2843 }
2844
2845 if (spectrometerFit) { spectrometerFit->info().addPatternReco(spectrometerTrack.info()); }
2846
2847 Trk::MeasurementSet::iterator m = spectrometerMeasurements.begin();
2848 auto mEnd = spectrometerMeasurements.end();
2849 for (; m != mEnd; ++m) { delete *m; }
2850
2851 return spectrometerFit;
2852 }
2853
2854 void CombinedMuonTrackBuilder::removeSpectrometerMaterial(std::unique_ptr<Trk::Track>& track) const {
2855 // limit momentum to avoid refit allocating excessive energy loss
2856 bool limitMomentum = false;
2857 double momentum = track->perigeeParameters()->momentum().mag();
2858 double qOverP = 0.;
2859
2860 if (momentum > m_lowMomentum) {
2861 const Trk::Perigee* measuredPerigee = track->perigeeParameters();
2862
2863 if (measuredPerigee) {
2864 Trk::TrackStates::const_reverse_iterator r = track->trackStateOnSurfaces()->rbegin();
2865
2866 while (!(**r).trackParameters()) { --r; }
2867
2868 limitMomentum = true;
2869
2870 if (!measuredPerigee->covariance()) {
2871 ATH_MSG_DEBUG("measuredPerigee has no covariance, qOverP not set");
2872 qOverP = (**r).trackParameters()->parameters()[Trk::qOverP];
2873 } else {
2874 qOverP = (**r).trackParameters()->parameters()[Trk::qOverP] +
2875 measuredPerigee->charge() * std::sqrt((*measuredPerigee->covariance())(Trk::qOverP, Trk::qOverP));
2876
2877 ATH_MSG_DEBUG(" limit momentum to " << 1. / std::abs(qOverP * Gaudi::Units::GeV) << " from original value "
2878 << momentum / Gaudi::Units::GeV);
2879 }
2880 }
2881 }
2882
2883 // remove spectrometer material from track
2884 std::bitset<Trk::TrackStateOnSurface::NumberOfTrackStateOnSurfaceTypes> defaultType;
2885 std::bitset<Trk::TrackStateOnSurface::NumberOfTrackStateOnSurfaceTypes> type = defaultType;
2886 auto trackStateOnSurfaces = std::make_unique<Trk::TrackStates>();
2887
2888 trackStateOnSurfaces->reserve(track->trackStateOnSurfaces()->size());
2889 bool is_first{true};
2890 for (const Trk::TrackStateOnSurface* tsos : *track->trackStateOnSurfaces()) {
2891 // limit perigee
2892 if (tsos->trackParameters()) {
2893 if (limitMomentum && is_first && tsos->trackParameters()->covariance() &&
2894 tsos->trackParameters()->surfaceType() == Trk::SurfaceType::Perigee) {
2895 Amg::VectorX parameterVector = tsos->trackParameters()->parameters();
2896 parameterVector[Trk::qOverP] = qOverP;
2897
2899 std::unique_ptr<Trk::TrackParameters> parameters =
2900 tsos->trackParameters()->associatedSurface().createUniqueTrackParameters(
2901 parameterVector[Trk::loc1], parameterVector[Trk::loc2], parameterVector[Trk::phi], parameterVector[Trk::theta],
2902 parameterVector[Trk::qOverP], *tsos->trackParameters()->covariance());
2903
2904 type = defaultType;
2906
2907 std::unique_ptr<Trk::MeasurementBase> measurementBase;
2908 if (tsos->measurementOnTrack()) {
2909 measurementBase = tsos->measurementOnTrack()->uniqueClone();
2911 }
2912 trackStateOnSurfaces->push_back(
2913 new Trk::TrackStateOnSurface(std::move(measurementBase), std::move(parameters), nullptr, type));
2914 } else {
2915 trackStateOnSurfaces->push_back(tsos->clone());
2916 }
2917 is_first = false;
2918 continue;
2919 }
2920 is_first = false;
2921
2922 // material in spectrometer
2923 if (tsos->materialEffectsOnTrack() &&
2924 !m_calorimeterVolume->inside(tsos->materialEffectsOnTrack()->associatedSurface().globalReferencePoint())) {
2925 if (tsos->measurementOnTrack()) {
2926 Amg::VectorX parameterVector = tsos->trackParameters()->parameters();
2927 if (limitMomentum) { parameterVector[Trk::qOverP] = qOverP; }
2928 std::unique_ptr<Trk::TrackParameters> trackParameters =
2929 tsos->trackParameters()->associatedSurface().createUniqueTrackParameters(
2930 parameterVector[Trk::loc1], parameterVector[Trk::loc2], parameterVector[Trk::phi], parameterVector[Trk::theta],
2931 parameterVector[Trk::qOverP],
2932 tsos->trackParameters()->covariance() ? std::optional<AmgSymMatrix(5)>(*tsos->trackParameters()->covariance())
2933 : std::nullopt);
2934
2935 type = defaultType;
2937
2939 std::unique_ptr<Trk::MeasurementBase> measurementBase;
2940 measurementBase = tsos->measurementOnTrack()->uniqueClone();
2941 trackStateOnSurfaces->push_back(
2942 new Trk::TrackStateOnSurface(std::move(measurementBase), std::move(trackParameters), nullptr, type));
2943 }
2944 continue;
2945 } else if (!tsos->measurementOnTrack() && tsos->trackParameters() &&
2946 !m_calorimeterVolume->inside(tsos->trackParameters()->position())) {
2947 continue;
2948 }
2949
2950 if (limitMomentum && tsos->trackParameters()) {
2951 Amg::VectorX parameterVector = tsos->trackParameters()->parameters();
2952 parameterVector[Trk::qOverP] = qOverP;
2953 std::unique_ptr<Trk::TrackParameters> trackParameters =
2954 tsos->trackParameters()->associatedSurface().createUniqueTrackParameters(
2955 parameterVector[Trk::loc1], parameterVector[Trk::loc2], parameterVector[Trk::phi], parameterVector[Trk::theta],
2956 parameterVector[Trk::qOverP],
2957 tsos->trackParameters()->covariance() ? std::optional<AmgSymMatrix(5)>(*tsos->trackParameters()->covariance())
2958 : std::nullopt);
2959
2960 type = defaultType;
2961
2962 std::unique_ptr<Trk::MeasurementBase> measurementBase;
2963 if (tsos->measurementOnTrack()) {
2965
2967
2968 measurementBase = tsos->measurementOnTrack()->uniqueClone();
2969 }
2970
2971 std::unique_ptr<Trk::MaterialEffectsBase> materialEffects;
2972 if (tsos->materialEffectsOnTrack()) {
2975
2976 materialEffects = tsos->materialEffectsOnTrack()->uniqueClone();
2977 }
2978 trackStateOnSurfaces->push_back(new Trk::TrackStateOnSurface(std::move(measurementBase), std::move(trackParameters),
2979 std::move(materialEffects), type));
2980 } else {
2981 trackStateOnSurfaces->push_back(tsos->clone());
2982 }
2983 }
2984
2985 // replace track
2986 Trk::TrackInfo trackInfo = track->info();
2987 std::unique_ptr<Trk::FitQuality> fitQuality = nullptr;
2988 if (track->fitQuality()) { fitQuality = std::make_unique<Trk::FitQuality>(*track->fitQuality()); }
2989
2990 track = std::make_unique<Trk::Track>(trackInfo, std::move(trackStateOnSurfaces), std::move(fitQuality));
2991 }
2992
2993 std::unique_ptr<Trk::PseudoMeasurementOnTrack> CombinedMuonTrackBuilder::vertexOnTrack(const Trk::TrackParameters& parameters,
2994 const Trk::RecVertex* vertex,
2995 const Trk::RecVertex* mbeamAxis) {
2996 // create the corresponding PerigeeSurface, localParameters and covarianceMatrix
2997 const Trk::PerigeeSurface surface(vertex->position());
2998 Trk::LocalParameters localParameters;
2999 Amg::MatrixX covarianceMatrix;
3000 covarianceMatrix.setZero();
3001
3002 // transform Cartesian (x,y,z) to beam axis or perigee
3003 Amg::Vector2D localPosition(0, 0);
3004 double ptInv = 1. / parameters.momentum().perp();
3005
3006 if (vertex == mbeamAxis) {
3007 Trk::DefinedParameter d0(localPosition[Trk::locX], Trk::locX);
3008 localParameters = Trk::LocalParameters(d0);
3009
3010 Amg::MatrixX jacobian(1, 3);
3011 jacobian.setZero();
3012 jacobian(0, 0) = -ptInv * parameters.momentum().y();
3013 jacobian(0, 1) = ptInv * parameters.momentum().x();
3014
3015 const Amg::MatrixX& cov = vertex->covariancePosition();
3016 covarianceMatrix = cov.similarity(jacobian);
3017 } else {
3018 localParameters = Trk::LocalParameters(localPosition);
3019
3020 Amg::MatrixX jacobian(2, 3);
3021 jacobian.setZero();
3022 jacobian(0, 0) = -ptInv * parameters.momentum().y();
3023 jacobian(0, 1) = ptInv * parameters.momentum().x();
3024 jacobian(1, 2) = 1.0;
3025
3026 const Amg::MatrixX& cov = vertex->covariancePosition();
3027 covarianceMatrix = cov.similarity(jacobian);
3028 }
3029
3030 return std::make_unique<Trk::PseudoMeasurementOnTrack>(std::move(localParameters),
3031 std::move(covarianceMatrix),
3032 surface);
3033 }
3034
3035 void CombinedMuonTrackBuilder::dumpCaloEloss(const Trk::Track* track, const std::string& txt) const {
3036 // will refit if extrapolated track was definitely bad
3037 if (!track || !msgLevel(MSG::DEBUG)) return;
3038 if (!m_trackQuery->isCaloAssociated(*track, Gaudi::Hive::currentContext())) {
3039 ATH_MSG_DEBUG(txt << " no TSOS in Calorimeter ");
3040 return;
3041 }
3042
3043 const Trk::Track& originalTrack = *track;
3044 const CaloEnergy* caloEnergy = m_trackQuery->caloEnergy(originalTrack);
3045 if (caloEnergy) {
3046 ATH_MSG_DEBUG(txt << " Calorimeter Eloss " << caloEnergy->deltaE());
3047 } else {
3048 ATH_MSG_DEBUG(txt << " No Calorimeter Eloss");
3049 }
3050
3051 const Trk::TrackStates* trackTSOS = track->trackStateOnSurfaces();
3052
3053 double Eloss = 0.;
3054 double idEloss = 0.;
3055 double caloEloss = 0.;
3056 double msEloss = 0.;
3057 double deltaP = 0.;
3058 double pcalo = 0.;
3059 double pstart = 0.;
3060 double eta = 0.;
3061 double pMuonEntry = 0.;
3062
3063 for (const auto* m : *trackTSOS) {
3064 const Trk::MeasurementBase* mot = m->measurementOnTrack();
3065
3066 if (m->trackParameters()) { pMuonEntry = m->trackParameters()->momentum().mag(); }
3067
3068 if (mot) {
3070 if (id.is_valid()) {
3071 // skip after first Muon hit
3072 if (m_idHelperSvc->isMuon(id)) { break; }
3073 }
3074 }
3075
3076 if (pstart == 0 && m->trackParameters()) {
3077 pstart = m->trackParameters()->momentum().mag();
3078 eta = m->trackParameters()->momentum().eta();
3079
3080 ATH_MSG_DEBUG("Start pars found eta " << eta << " r " << (m->trackParameters())->position().perp() << " z "
3081 << (m->trackParameters())->position().z() << " pstart " << pstart);
3082 }
3083
3084 if (m->materialEffectsOnTrack()) {
3085 const Trk::MaterialEffectsOnTrack* meot = dynamic_cast<const Trk::MaterialEffectsOnTrack*>(m->materialEffectsOnTrack());
3086
3087 if (meot) {
3088 if (meot->thicknessInX0() > 20) {
3089 const Trk::ScatteringAngles* scatAngles = meot->scatteringAngles();
3090
3091 ATH_MSG_DEBUG(" Calorimeter X0 " << meot->thicknessInX0() << " pointer scat " << scatAngles);
3092
3093 if (scatAngles) {
3094 pcalo = m->trackParameters()->momentum().mag();
3095 double pullPhi = scatAngles->deltaPhi() / scatAngles->sigmaDeltaPhi();
3096 double pullTheta = scatAngles->deltaTheta() / scatAngles->sigmaDeltaTheta();
3097
3098 ATH_MSG_DEBUG(" Calorimeter scatterer deltaPhi (mrad) "
3099 << 1000 * scatAngles->deltaPhi() << " sigma " << 1000 * scatAngles->sigmaDeltaPhi() << " pull "
3100 << pullPhi << " deltaTheta (mrad) " << 1000 * scatAngles->deltaTheta() << " sigma "
3101 << 1000 * scatAngles->sigmaDeltaTheta() << " pull " << pullTheta);
3102 }
3103 }
3104
3105 const Trk::EnergyLoss* energyLoss = meot->energyLoss();
3106 if (energyLoss) {
3107 if (m->trackParameters()) {
3108 ATH_MSG_DEBUG("Eloss found r " << (m->trackParameters())->position().perp() << " z "
3109 << (m->trackParameters())->position().z() << " value " << energyLoss->deltaE()
3110 << " Eloss " << Eloss);
3111 }
3112
3114 idEloss = Eloss;
3115 caloEloss = std::abs(energyLoss->deltaE());
3116 Eloss = 0.;
3117
3118 if (m->trackParameters()) { deltaP = m->trackParameters()->momentum().mag() - pcalo; }
3119
3120 const Trk::Surface& surface = m->surface();
3121
3122 ATH_MSG_DEBUG(" Calorimeter surface " << surface);
3123 ATH_MSG_DEBUG(txt << " Calorimeter delta p " << deltaP << " deltaE " << caloEloss
3124 << " delta pID = pcaloEntry-pstart " << pcalo - pstart);
3125
3126 } else {
3127 Eloss += std::abs(energyLoss->deltaE());
3128 }
3129 }
3130 }
3131 }
3132 }
3133
3134 msEloss = Eloss;
3135 Eloss = idEloss + caloEloss + msEloss;
3136
3137 ATH_MSG_DEBUG(txt << " eta " << eta << " pstart " << pstart / Gaudi::Units::GeV << " Eloss on TSOS idEloss " << idEloss
3138 << " caloEloss " << caloEloss << " msEloss " << msEloss << " Total " << Eloss << " pstart - pMuonEntry "
3139 << pstart - pMuonEntry);
3140
3141 return;
3142 }
3143
3144 const Trk::TrackingVolume* CombinedMuonTrackBuilder::getVolume(const EventContext& ctx, const std::string&& vol_name) const {
3147 if (!handle.isValid()) {
3148 ATH_MSG_WARNING("Could not retrieve a valid tracking geometry");
3149 return nullptr;
3150 }
3151 return handle.cptr()->trackingVolume(vol_name);
3152 }
3153 std::vector<std::unique_ptr<const Trk::TrackStateOnSurface>> CombinedMuonTrackBuilder::getCaloTSOSfromMatProvider(
3154 const Trk::TrackParameters& track_params, const Trk::Track& me_track) const {
3155 std::vector<std::unique_ptr<const Trk::TrackStateOnSurface>> to_ret;
3156 std::unique_ptr<std::vector<const Trk::TrackStateOnSurface*>> tsos_vec{m_materialUpdator->getCaloTSOS(track_params, me_track)};
3157 if (tsos_vec) {
3158 to_ret.reserve(tsos_vec->size());
3159 for (const Trk::TrackStateOnSurface* tsos : *tsos_vec) to_ret.emplace_back(tsos);
3160 }
3161 return to_ret;
3162 }
3163
3164} // namespace Rec
#define M_PI
Scalar eta() const
pseudorapidity method
Scalar deltaPhi(const MatrixBase< Derived > &vec) const
Scalar deltaR(const MatrixBase< Derived > &vec) const
#define endmsg
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_INFO(x)
#define ATH_MSG_VERBOSE(x)
#define ATH_MSG_WARNING(x)
#define ATH_MSG_DEBUG(x)
double charge(const T &p)
Definition AtlasPID.h:997
#define AmgSymMatrix(dim)
static Double_t a
Wrapper to avoid constant divisions when using units.
class extending the basic Trk::EnergyLoss to describe the measured or parameterised muon energy loss ...
Definition CaloEnergy.h:28
CaloEnergy::EnergyLossType energyLossType(void) const
Accessor methods.
Definition CaloEnergy.h:162
DataModel_detail::const_iterator< DataVector > const_iterator
Definition DataVector.h:838
value_type push_back(value_type pElem)
Add an element to the end of the collection.
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.
std::reverse_iterator< const_iterator > const_reverse_iterator
Definition DataVector.h:847
size_type size() const noexcept
Returns the number of elements in the collection.
Local cache for magnetic field (based on MagFieldServices/AtlasFieldSvcTLS.h)
bool solenoidOn() const
status of the magnets
static std::unique_ptr< Trk::TrackStateOnSurface > createMeasTSOS(std::unique_ptr< Trk::MeasurementBase > meas, std::unique_ptr< Trk::TrackParameters > pars, Trk::TrackStateOnSurface::TrackStateOnSurfaceType type)
create a TSOS with a measurement, takes ownership of the pointers
static std::unique_ptr< Trk::TrackStateOnSurface > createPerigeeTSOS(std::unique_ptr< Trk::TrackParameters > perigee)
create a perigee TSOS, takes ownership of the Perigee
void removeSpectrometerMaterial(std::unique_ptr< Trk::Track > &track) const
virtual StatusCode finalize() override
ToolHandle< Trk::IMaterialAllocator > m_materialAllocator
std::unique_ptr< Trk::Track > createMuonTrack(const EventContext &ctx, const Trk::Track &muonTrack, const Trk::TrackParameters *parameters, std::unique_ptr< CaloEnergy > caloEnergy, const Trk::TrackStates *tsos) const
Summarizes the available information about the ID track, the deposited calorimeter energies and the t...
ToolHandle< Trk::IPropagator > m_propagator
ToolHandle< Muon::IMuonHoleRecoveryTool > m_muonHoleRecovery
static std::unique_ptr< Trk::PseudoMeasurementOnTrack > vertexOnTrack(const Trk::TrackParameters &parameters, const Trk::RecVertex *vertex, const Trk::RecVertex *mbeamAxis)
ToolHandle< Muon::IMuonClusterOnTrackCreator > m_cscRotCreator
ServiceHandle< Muon::IMuonEDMHelperSvc > m_edmHelperSvc
Gaudi::Property< double > m_lineMomentum
ToolHandle< Trk::IPropagator > m_propagatorSL
void appendSelectedTSOS(Trk::TrackStates &trackStateOnSurfaces, Trk::TrackStates::const_iterator begin, Trk::TrackStates::const_iterator end) const
Gaudi::Property< double > m_largeImpact
ToolHandle< Trk::IExtrapolator > m_extrapolator
virtual StatusCode initialize() override
PublicToolHandle< Muon::IMuonAlignmentUncertTool > m_alignUncertTool_theta
ToolHandles to retrieve the uncertainties for theta and phi for the scattering uncertainties.
virtual std::unique_ptr< Trk::Track > standaloneFit(const EventContext &ctx, const Trk::Track &spectrometerTrack, const Amg::Vector3D &bs, const Trk::Vertex *vertex) const override
ICombinedMuonTrackBuilder interface: propagate to perigee adding calo energy-loss and material to MS ...
Gaudi::Property< bool > m_useRefitTrackError
Gaudi::Property< bool > m_refineELossStandAloneTrackFit
Gaudi::Property< double > m_vertex3DSigmaZ
Gaudi::Property< bool > m_perigeeAtSpectrometerEntrance
std::unique_ptr< Trk::TrackStateOnSurface > createPhiPseudoMeasurement(const EventContext &ctx, const Trk::Track &track) const
Gaudi::Property< bool > m_reallocateMaterial
std::unique_ptr< Trk::TrackStateOnSurface > entrancePerigee(const EventContext &ctx, const Trk::TrackParameters *parameters) const
std::vector< std::unique_ptr< const Trk::TrackStateOnSurface > > createSpectrometerTSOS(const EventContext &ctx, const Trk::Track &spectrometerTrack) const
virtual std::unique_ptr< Trk::Track > combinedFit(const EventContext &ctx, const Trk::Track &indetTrack, const Trk::Track &extrapolatedTrack, const Trk::Track &spectrometerTrack) const override
ICombinedMuonTrackBuilder interface: build and fit combined ID/Calo/MS track.
ToolHandle< Rec::IMuidCaloEnergy > m_caloEnergyParam
PublicToolHandle< Muon::IMuonAlignmentUncertTool > m_alignUncertTool_phi
std::unique_ptr< Trk::Track > createIndetTrack(const Trk::TrackInfo &info, const Trk::TrackStates *tsos) const
void dumpCaloEloss(const Trk::Track *track, const std::string &txt) const
const Trk::TrackingVolume * getVolume(const EventContext &ctx, const std::string &&vol_name) const
Gaudi::Property< bool > m_refineELossCombinedTrackFit
std::unique_ptr< Trk::Track > reallocateMaterial(const EventContext &ctx, const Trk::Track &spectrometerTrack) const
Gaudi::Property< bool > m_iterateCombinedTrackFit
Gaudi::Property< bool > m_cleanStandalone
const CaloEnergy * caloEnergyParameters(const Trk::Track *combinedTrack, const Trk::Track *muonTrack, const Trk::TrackParameters *&combinedEnergyParameters, const Trk::TrackParameters *&muonEnergyParameters) const
SG::ReadCondHandleKey< Trk::TrackingGeometry > m_trackingGeometryReadKey
std::unique_ptr< Trk::Track > addIDMSerrors(const Trk::Track *track) const
ToolHandle< Muon::IMdtDriftCircleOnTrackCreator > m_mdtRotCreator
std::vector< std::unique_ptr< const Trk::TrackStateOnSurface > > getCaloTSOSfromMatProvider(const Trk::TrackParameters &track_params, const Trk::Track &me_track) const
Helper method to retrieve the CaloTSO from the Material provider in a memory safe way.
std::unique_ptr< const Trk::RecVertex > m_beamAxis
Gaudi::Property< double > m_largeMomentumChange
std::unique_ptr< const Trk::RecVertex > m_vertex
Gaudi::Property< double > m_vertex2DSigmaZ
Gaudi::Property< double > m_largeMomentumError
std::unique_ptr< const Trk::PerigeeSurface > m_perigeeSurface
std::unique_ptr< Trk::Track > createExtrapolatedTrack(const EventContext &ctx, const Trk::Track &spectrometerTrack, const Trk::TrackParameters &parameters, Trk::ParticleHypothesis particleHypothesis, Trk::RunOutlierRemoval runOutlier, const std::vector< std::unique_ptr< const Trk::TrackStateOnSurface > > &trackStateOnSurfaces, const Trk::RecVertex *vertex, const Trk::RecVertex *mbeamAxis, const Trk::PerigeeSurface *mperigeeSurface, const Trk::Perigee *seedParameter=nullptr) const
Gaudi::Property< double > m_largePhiError
Trk::MagneticFieldProperties m_magFieldProperties
Gaudi::Property< double > m_lowMomentum
Gaudi::Property< double > m_vertex3DSigmaRPhi
virtual std::unique_ptr< Trk::Track > standaloneRefit(const EventContext &ctx, const Trk::Track &combinedTrack, const Amg::Vector3D &vec) const override
ICombinedMuonTrackBuilder interface: refit a track removing any indet measurements with optional addi...
virtual std::unique_ptr< Trk::Track > indetExtension(const EventContext &ctx, const Trk::Track &indetTrack, const Trk::MeasurementSet &spectrometerMeas, std::unique_ptr< Trk::TrackParameters > innerParameters, std::unique_ptr< Trk::TrackParameters > middleParameters, std::unique_ptr< Trk::TrackParameters > outerParameters) const override
ICombinedMuonTrackBuilder interface: build and fit indet track extended to include MS Measurement set...
void momentumUpdate(std::unique_ptr< Trk::TrackParameters > &parameters, double updatedP, bool directionUpdate=false, double deltaPhi=0., double deltaTheta=0.) const
void finalTrackBuild(const EventContext &ctx, std::unique_ptr< Trk::Track > &track) const
CombinedMuonTrackBuilder(const std::string &type, const std::string &name, const IInterface *parent)
Gaudi::Property< double > m_minEnergy
Gaudi::Property< double > m_numberSigmaFSR
std::unique_ptr< Trk::TrackParameters > extrapolatedParameters(const EventContext &ctx, bool &badlyDeterminedCurvature, const Trk::Track &spectrometerTrack, const Trk::RecVertex *mvertex, const Trk::PerigeeSurface *mperigeeSurface) const
Gaudi::Property< double > m_vertex2DSigmaRPhi
ToolHandle< Muon::IMuonClusterOnTrackCreator > m_muClusterRotCreator
ToolHandle< Muon::IMuonErrorOptimisationTool > m_muonErrorOptimizer
ToolHandle< Trk::ITrkMaterialProviderTool > m_materialUpdator
Gaudi::Property< double > m_badFitChi2
Gaudi::Property< double > m_zECToroid
ServiceHandle< Muon::IMuonIdHelperSvc > m_idHelperSvc
virtual StatusCode initialize() override
std::unique_ptr< MessageHelper > m_messageHelper
ToolHandle< Trk::ITrackSummaryTool > m_trackSummary
ToolHandle< Rec::IMuidCaloTrackStateOnSurface > m_caloTSOS
virtual std::unique_ptr< Trk::Track > fit(const EventContext &ctx, const Trk::Track &track, const Trk::RunOutlierRemoval runOutlier, const Trk::ParticleHypothesis particleHypothesis) const override
double normalizedChi2(const Trk::Track &track) const
unsigned int countAEOTs(const Trk::Track &track, const std::string &txt) const
std::unique_ptr< const Trk::Volume > m_calorimeterVolume
bool checkTrack(std::string_view txt, const Trk::Track *newTrack) const
virtual StatusCode finalize() override
bool loadMagneticField(const EventContext &ctx, MagField::AtlasFieldCache &field_cache) const
PublicToolHandle< Muon::MuonEDMPrinterTool > m_printer
Gaudi::Property< bool > m_updateWithCaloTG
std::unique_ptr< const Trk::Volume > m_indetVolume
ToolHandle< Rec::IMuonTrackQuery > m_trackQuery
const_pointer_type cptr()
This class describes energy loss material effects in the ATLAS tracking EDM.
Definition EnergyLoss.h:34
double sigmaMinusDeltaE() const
returns the negative side
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
Class to represent and store fit qualities from track reconstruction in terms of and number of degre...
Definition FitQuality.h:97
int numberDoF() const
returns the number of degrees of freedom of the overall track or vertex fit as integer
Definition FitQuality.h:60
std::vector< std::unique_ptr< const TrackStateOnSurface > > Garbage_t
Interface class IPropagators It inherits from IAlgTool.
Definition IPropagator.h:55
virtual std::unique_ptr< NeutralParameters > propagate(const NeutralParameters &parameters, const Surface &sf, PropDirection dir, const BoundaryCheck &bcheck, bool returnCurv=false) const =0
Main propagation method for NeutralParameters.
static void extract(std::vector< Identifier > &ids, const std::vector< const MeasurementBase * > &measurements)
base class to integrate material effects on Trk::Track in a flexible way.
@ ScatteringEffects
contains material effects due to multiple scattering
@ EnergyLossEffects
contains energy loss corrections
double thicknessInX0() const
returns the actually traversed material .
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.
This class is the pure abstract base class for all fittable tracking measurements.
virtual const Surface & associatedSurface() const =0
Interface method to get the associated Surface.
const Amg::Vector3D & momentum() const
Access method for the momentum.
virtual constexpr SurfaceType surfaceType() const override=0
Returns the Surface Type enum for the surface used to define the derived class.
virtual ParametersBase< DIM, T > * clone() const override=0
clone method for polymorphic deep copy
const Amg::Vector3D & position() const
Access method for the position.
double charge() const
Returns the charge.
virtual const Surface & associatedSurface() const override=0
Access to the Surface associated to the Parameters.
std::unique_ptr< ParametersBase< DIM, T > > uniqueClone() const
clone method for polymorphic deep copy returning unique_ptr; it is not overriden, but uses the existi...
Class describing the Line to which the Perigee refers to.
Class for a planaer rectangular or trapezoidal surface in the ATLAS detector.
Class to handle pseudo-measurements in fitters and on track objects.
Class to handle RIO On Tracks ROT) for InDet and Muons, it inherits from the common MeasurementBase.
Definition RIO_OnTrack.h:70
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
Trk::RecVertex inherits from Trk::Vertex.
Definition RecVertex.h:44
Bounds for a rotated trapezoidal, planar Surface.
represents a deflection of the track caused through multiple scattering in material.
double sigmaDeltaPhi() const
returns the
double deltaPhi() const
returns the
double sigmaDeltaTheta() const
returns the
double deltaTheta() const
returns the
Abstract Base Class for tracking surfaces.
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 & globalReferencePoint() const
Returns a global reference point on the surface, for PlaneSurface, StraightLineSurface,...
virtual const SurfaceBounds & bounds() const =0
Surface Bounds method.
const Amg::Vector3D & center() const
Returns the center position of the Surface.
Contains information about the 'fitter' of this track.
@ MuidStandaloneRefit
Standalone muon that was obtained by refitting a combined muon using the calorimeter information of t...
represents the track state (measurement, material, fit parameters and quality) at a surface.
virtual TrackStateOnSurface * clone() const
Pseudo-constructor: needed to avoid excessive RTTI.
const MeasurementBase * measurementOnTrack() const
returns MeasurementBase const overload
const TrackParameters * trackParameters() const
return ptr to trackparameters const overload
std::string dumpType() const
returns a string with the expanded type of the object (i.e.
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.
@ Parameter
This TSOS contains a Trk::ParameterBase.
@ Outlier
This TSoS contains an outlier, that is, it contains a MeasurementBase/RIO_OnTrack which was not used ...
@ Scatterer
This represents a scattering point on the track, and so will contain TrackParameters and MaterialEffe...
@ Hole
A hole on the track - this is defined in the following way.
@ CaloDeposit
This TSOS contains a CaloEnergy object.
const MaterialEffectsBase * materialEffectsOnTrack() const
return material effects const overload
const AlignmentEffectsOnTrack * alignmentEffectsOnTrack() const
return the the alignment effects const overload
const Trk::TrackStates * trackStateOnSurfaces() const
return a pointer to a const DataVector of const TrackStateOnSurfaces.
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
Full Volume description used in Tracking, it inherits from Volume to get the geometrical structure,...
Bounds for a trapezoidal, planar Surface.
This class is a simplest representation of a vertex candidate.
const Amg::Vector3D & position() const
return position of vertex
Definition Vertex.cxx:63
int r
Definition globals.cxx:22
Eigen::Matrix< double, Eigen::Dynamic, Eigen::Dynamic > MatrixX
Dynamic Matrix - dynamic allocation.
double error(const Amg::MatrixX &mat, int index)
return diagonal error of the matrix caller should ensure the matrix is symmetric and the index is in ...
bool hasPositiveDiagElems(const AmgSymMatrix(N) &mat)
Returns true if all diagonal elements of the covariance matrix are finite aka sane in the above defin...
Eigen::Matrix< double, 2, 1 > Vector2D
Eigen::Matrix< double, 3, 1 > Vector3D
Eigen::Matrix< double, Eigen::Dynamic, 1 > VectorX
Dynamic Vector - dynamic allocation.
Gaudi Tools.
@ oppositeMomentum
@ alongMomentum
@ anyDirection
std::vector< const MeasurementBase * > MeasurementSet
vector of fittable measurements
Definition FitterTypes.h:30
DataVector< const Trk::TrackStateOnSurface > TrackStates
ParametersT< TrackParametersDim, Charged, PerigeeSurface > Perigee
bool RunOutlierRemoval
switch to toggle quality processing after fit
Definition FitterTypes.h:22
@ locY
local cartesian
Definition ParamDefs.h:38
@ locX
Definition ParamDefs.h:37
@ phi0
Definition ParamDefs.h:65
@ theta
Definition ParamDefs.h:66
@ qOverP
perigee
Definition ParamDefs.h:67
@ loc2
generic first and second local coordinate
Definition ParamDefs.h:35
@ phi
Definition ParamDefs.h:75
@ loc1
Definition ParamDefs.h:34
@ d0
Definition ParamDefs.h:63
@ z0
Definition ParamDefs.h:64
std::pair< double, ParamDefs > DefinedParameter
Typedef to of a std::pair<double, ParamDefs> to identify a passed-through double as a specific type o...
ParticleHypothesis
Enumeration for Particle hypothesis respecting the interaction with material.
ParametersBase< TrackParametersDim, Charged > TrackParameters
void stable_sort(DataModel_detail::iterator< DVL > beg, DataModel_detail::iterator< DVL > end)
Specialization of stable_sort for DataVector/List.
double deltaPhi(double phiA, double phiB)
delta Phi in range [-pi,pi[
MsgStream & msg
Definition testRead.cxx:32