ATLAS Offline Software
Loading...
Searching...
No Matches
MuonTrackQuery.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration
3*/
4
5#include "MuonTrackQuery.h"
6
7#include <cmath>
8#include <iomanip>
9
10#include "AthenaKernel/Units.h"
11#include "GaudiKernel/ServiceHandle.h"
29#include "TrkSurfaces/Surface.h"
31#include "TrkTrack/Track.h"
34#include "CxxUtils/sincos.h"
36namespace Units = Athena::Units;
37namespace {
38 constexpr double OneOverSqrt2 = M_SQRT1_2;
39 static const Amg::Vector3D origin{0., 0., 0.};
40}
41namespace Rec {
42
43 MuonTrackQuery::MuonTrackQuery(const std::string& type, const std::string& name, const IInterface* parent) :
44 AthAlgTool(type, name, parent) {
45 declareInterface<IMuonTrackQuery>(this);
46 }
47
49 ATH_MSG_DEBUG("Initializing MuonTrackQuery");
50
51 // tool needed to refit slimmed tracks
52 if (!m_fitter.empty()) {
53 ATH_CHECK(m_fitter.retrieve());
54 ATH_MSG_DEBUG("Retrieved tool " << m_fitter);
55 }
56
57 // multipurpose and identifier helper tools for spectrometer
58 ATH_CHECK(m_edmHelperSvc.retrieve());
59 ATH_MSG_DEBUG("Retrieved tool " << m_edmHelperSvc);
60
61 ATH_CHECK(m_idHelperSvc.retrieve());
62 ATH_MSG_DEBUG("Retrieved tool " << m_idHelperSvc);
63
64 // tools needed when flipping tracks to outgoing
65 if (!m_mdtRotCreator.empty()) {
66 ATH_CHECK(m_mdtRotCreator.retrieve());
67 ATH_MSG_DEBUG("Retrieved tool " << m_mdtRotCreator);
68 }
69
70 // need to know which TrackingVolume we are in: indet/calo/spectrometer
71 if (!m_trackingGeometryReadKey.empty()) {
73 } else {
74 ATH_MSG_ERROR("Could not retrieve a valid tracking geometry");
75 }
76 return StatusCode::SUCCESS;
77 }
78
80 const CaloEnergy* caloEnergy = nullptr;
81
82 for (const Trk::TrackStateOnSurface* tsos : *track.trackStateOnSurfaces()) {
83 // select MaterialEffects inside calorimeter volume
84 if (!tsos->type(Trk::TrackStateOnSurface::CaloDeposit) || !tsos->materialEffectsOnTrack()) { continue; }
85
86 const Trk::MaterialEffectsOnTrack* meot = dynamic_cast<const Trk::MaterialEffectsOnTrack*>(tsos->materialEffectsOnTrack());
87
88 if (!meot) { continue; }
89
90 caloEnergy = dynamic_cast<const CaloEnergy*>(meot->energyLoss());
91 if (caloEnergy) { break; }
92 }
93
94 ATH_MSG_VERBOSE("caloEnergy " << caloEnergy);
95
96 return caloEnergy;
97 }
98 double MuonTrackQuery::caloEnergyDeposit(const Trk::Track& track, const EventContext& ctx) const {
99 const Trk::TrackingVolume* calorimeterVolume = getVolume("Calo::Container", ctx);
100
101 if (!calorimeterVolume) {
102 ATH_MSG_WARNING("Failed to retrieve Calo volume ");
103 return 0.;
104 }
105
106 const Trk::TrackingVolume* indetVolume = getVolume("InDet::Containers::InnerDetector", ctx);
107
108 if (!indetVolume) {
109 ATH_MSG_WARNING("Failed to retrieve InDeT volume ");
110 return 0.;
111 }
112
113 const Trk::TrackParameters* indetExitParameters = nullptr;
114 const Trk::TrackParameters* caloExitParameters = nullptr;
115
116 for (const Trk::TrackStateOnSurface* tsos : *track.trackStateOnSurfaces()) {
117 if (!tsos->trackParameters()) continue;
118
119 if (indetVolume->inside(tsos->trackParameters()->position())) {
120 indetExitParameters = tsos->trackParameters();
121 continue;
122 }
123
124 if (!calorimeterVolume->inside(tsos->trackParameters()->position()) && caloExitParameters) { break; }
125
126 caloExitParameters = tsos->trackParameters();
127 }
128
129 if (!indetExitParameters || !caloExitParameters) { return 0.; }
130
131 double energyDeposit = indetExitParameters->momentum().mag() - caloExitParameters->momentum().mag();
132
133 ATH_MSG_VERBOSE(" calo energy deposit ");
134
135 return energyDeposit;
136 }
137
138 FieldIntegral MuonTrackQuery::fieldIntegral(const Trk::Track& track, const EventContext& ctx) const {
139 // field integral null for straight tracks + method is not valid for slimmed tracks
140 if (isLineFit(track) || isSlimmed(track)) { return FieldIntegral(); }
141
142 const Trk::TrackingVolume* calorimeterVolume = getVolume("Calo::Container", ctx);
143
144 if (!calorimeterVolume) {
145 ATH_MSG_WARNING("Failed to retrieve Calo volume ");
146 return FieldIntegral();
147 }
148
149 const Trk::TrackingVolume* indetVolume = getVolume("InDet::Containers::InnerDetector", ctx);
150
151 if (!indetVolume) {
152 ATH_MSG_WARNING("Failed to retrieve InDeT volume ");
153 return FieldIntegral();
154 }
155
156 // sum Bdl between measurements
157 double betweenInDetMeasurements = 0.;
158 double betweenSpectrometerMeasurements = 0.;
159
160 bool haveInDet = false;
161 bool haveSpectrometer = false;
162
163 Amg::Vector3D integratedMomentumKick = origin;
164
165 const Trk::TrackParameters* parameters = nullptr;
166
167 // loop over TSOS to integrate vector Bdl
168 for (const Trk::TrackStateOnSurface* tsos : *track.trackStateOnSurfaces()) {
169 // not going to use trigger hits, outliers or pseudoMeasurements
170 bool isPreciseHit = (tsos->measurementOnTrack() && dynamic_cast<const Trk::MeasurementBase*>(tsos->measurementOnTrack()) &&
171 !dynamic_cast<const Trk::PseudoMeasurementOnTrack*>(tsos->measurementOnTrack()) &&
173
174 // skip slimmed measurements
175 if (!tsos->trackParameters() || dynamic_cast<const Trk::Perigee*>(tsos->trackParameters())) { continue; }
176
177 // skip spectrometer phi measurements
178 if (isPreciseHit && !calorimeterVolume->inside(tsos->trackParameters()->position())) {
179 Identifier id = m_edmHelperSvc->getIdentifier(*tsos->measurementOnTrack());
180 isPreciseHit = (id.is_valid() && !m_idHelperSvc->measuresPhi(id));
181 }
182
183 if (!tsos->materialEffectsOnTrack() && !isPreciseHit) { continue; }
184
185 // careful with first hit + skip any flipped parameters
186 if (!parameters) { parameters = tsos->trackParameters(); }
187
188 Amg::Vector3D startMomentum = parameters->momentum();
189 parameters = tsos->trackParameters();
190 Amg::Vector3D endDirection = parameters->momentum().unit();
191
192 if (startMomentum.dot(endDirection) < 0.) continue;
193
194 // subtract scattering angle
195 if (tsos->materialEffectsOnTrack()) {
196 const Trk::MaterialEffectsOnTrack* meot = dynamic_cast<const Trk::MaterialEffectsOnTrack*>(tsos->materialEffectsOnTrack());
197
198 if (meot && meot->scatteringAngles()) {
199 const double theta = endDirection.theta() - meot->scatteringAngles()->deltaTheta();
200 const double phi = xAOD::P4Helpers::deltaPhi(endDirection.phi() , meot->scatteringAngles()->deltaPhi());
201 const CxxUtils::sincos sc_theta {theta}, sc_phi{phi};
202 endDirection = Amg::Vector3D(sc_theta.sn * sc_phi.cs, sc_theta.sn * sc_phi.sn, sc_theta.cs);
203 }
204 }
205
206 Amg::Vector3D momentumKick = startMomentum.cross(endDirection) / (0.3 * Units::GeV);
207 integratedMomentumKick += momentumKick;
208
209 // accumulate abs(Bdl) between measurements (distinguish indet and spectrometer)
210 if (!isPreciseHit) continue;
211
212 if (indetVolume->inside(parameters->position())) {
213 if (haveInDet) {
214 betweenInDetMeasurements += integratedMomentumKick.mag();
215 integratedMomentumKick = origin;
216 } else {
217 haveInDet = true;
218 integratedMomentumKick = origin;
219 }
220 } else {
221 if (haveSpectrometer) {
222 betweenSpectrometerMeasurements += integratedMomentumKick.mag();
223 integratedMomentumKick = origin;
224 } else {
225 haveSpectrometer = true;
226 integratedMomentumKick = origin;
227 }
228 }
229 }
230
231 ATH_MSG_DEBUG(std::setiosflags(std::ios::fixed)
232 << " field integrals for track at eta, phi " << std::setw(6) << std::setprecision(2)
233 << track.perigeeParameters()->momentum().eta() << "," << std::setw(6) << std::setprecision(2)
234 << track.perigeeParameters()->momentum().phi() << " : betweenInDetMeasurements " << std::setw(8)
235 << std::setprecision(3) << betweenInDetMeasurements << " betweenSpectrometerMeasurements " << std::setw(8)
236 << std::setprecision(3) << betweenSpectrometerMeasurements);
237
238 return FieldIntegral(betweenInDetMeasurements, betweenSpectrometerMeasurements, 0., 0.);
239 }
240
241 bool MuonTrackQuery::isCaloAssociated(const Trk::Track& track, const EventContext& ctx) const {
242 const Trk::TrackingVolume* calorimeterVolume = getVolume("Calo::Container", ctx);
243
244 if (!calorimeterVolume) {
245 ATH_MSG_WARNING("Failed to retrieve Calo volume ");
246 return false;
247 }
248
249 const Trk::TrackingVolume* indetVolume = getVolume("InDet::Containers::InnerDetector", ctx);
250
251 if (!indetVolume) {
252 ATH_MSG_WARNING("Failed to retrieve InDeT volume ");
253 return false;
254 }
255
256 bool haveCaloDeposit = false;
257 int numberCaloTSOS = 0;
258
259 for (const Trk::TrackStateOnSurface* s : *track.trackStateOnSurfaces()) {
260 // CaloDeposit?
262 ATH_MSG_VERBOSE("haveCaloDeposit, " << numberCaloTSOS << " TSOS in calo volume");
263 haveCaloDeposit = true;
264 }
265
266 // select MaterialEffects inside calorimeter volume
267 if (!s->materialEffectsOnTrack()) { continue; }
268
269 Amg::Vector3D position = s->materialEffectsOnTrack()->associatedSurface().globalReferencePoint();
270
271 if (indetVolume->inside(position)) { continue; }
272 if (!calorimeterVolume->inside(position)) { break; }
273 if (++numberCaloTSOS > 2 && haveCaloDeposit) { return true; }
274 }
275
276 ATH_MSG_VERBOSE("association false, " << numberCaloTSOS << " TSOS in calo volume");
277
278 return false;
279 }
280
281 bool MuonTrackQuery::isCombined(const Trk::Track& track, const EventContext& ctx) const {
282 const Trk::TrackingVolume* calorimeterVolume = getVolume("Calo::Container", ctx);
283
284 if (!calorimeterVolume) {
285 ATH_MSG_WARNING("Failed to retrieve Calo volume ");
286 return false;
287 }
288
289 const Trk::TrackingVolume* indetVolume = getVolume("InDet::Containers::InnerDetector", ctx);
290
291 if (!indetVolume) {
292 ATH_MSG_WARNING("Failed to retrieve InDeT volume ");
293 return false;
294 }
295
296 // combined tracks have indet and spectrometer active measurements
297 bool indet = false;
298 bool spectrometer = false;
299
300 // find innermost measurement (assume some degree of measurement ordering)
301 for (const Trk::TrackStateOnSurface* tsos : *track.trackStateOnSurfaces()) {
302 if (!tsos->measurementOnTrack() || tsos->type(Trk::TrackStateOnSurface::Outlier) ||
303 dynamic_cast<const Trk::PseudoMeasurementOnTrack*>(tsos->measurementOnTrack())) {
304 continue;
305 }
306
307 if (indetVolume->inside(tsos->measurementOnTrack()->globalPosition())) {
308 indet = true;
309 break;
310 } else if (!calorimeterVolume->inside(tsos->measurementOnTrack()->globalPosition())) {
311 spectrometer = true;
312 break;
313 }
314 }
315
316 // find outermost measurement
317 Trk::TrackStates::const_reverse_iterator rs = track.trackStateOnSurfaces()->rbegin();
318 for (; rs != track.trackStateOnSurfaces()->rend(); ++rs) {
319 if (!(**rs).measurementOnTrack() || (**rs).type(Trk::TrackStateOnSurface::Outlier) ||
320 dynamic_cast<const Trk::PseudoMeasurementOnTrack*>((**rs).measurementOnTrack())) {
321 continue;
322 }
323
324 if (indetVolume->inside((**rs).measurementOnTrack()->globalPosition())) {
325 indet = true;
326 break;
327 } else if (!calorimeterVolume->inside((**rs).measurementOnTrack()->globalPosition())) {
328 spectrometer = true;
329 break;
330 }
331 }
332
333 return indet && spectrometer;
334 }
335
336 bool MuonTrackQuery::isExtrapolated(const Trk::Track& track, const EventContext& ctx) const {
337 const Trk::TrackingVolume* calorimeterVolume = getVolume("Calo::Container", ctx);
338
339 if (!calorimeterVolume) {
340 ATH_MSG_WARNING("Failed to retrieve Calo volume ");
341 return false;
342 }
343
344 const Trk::TrackingVolume* indetVolume = getVolume("InDet::Containers::InnerDetector", ctx);
345
346 if (!indetVolume) {
347 ATH_MSG_WARNING("Failed to retrieve InDeT volume ");
348 return false;
349 }
350
351 // perigee needs to be inside indet
352 const Trk::TrackParameters* parameters = track.perigeeParameters();
353 if (!parameters || !indetVolume->inside(parameters->position())) { return false; }
354
355 // extrapolated tracks have indet TSOS but not active measurements
356 // and spectrometer active measurements
357 bool indet = false;
358 bool spectrometer = false;
359
360 // find innermost measurement (assume some degree of measurement ordering)
361 for (const Trk::TrackStateOnSurface* tsos : *track.trackStateOnSurfaces()) {
362 if (!tsos->measurementOnTrack() || tsos->type(Trk::TrackStateOnSurface::Outlier) ||
363 dynamic_cast<const Trk::PseudoMeasurementOnTrack*>(tsos->measurementOnTrack())) {
364 continue;
365 }
366
367 if (indetVolume->inside(tsos->measurementOnTrack()->globalPosition())) {
368 indet = true;
369 break;
370 } else if (!calorimeterVolume->inside(tsos->measurementOnTrack()->globalPosition())) {
371 spectrometer = true;
372 break;
373 }
374 }
375
376 // find outermost measurement (assume some degree of measurement ordering)
377 Trk::TrackStates::const_reverse_iterator rs = track.trackStateOnSurfaces()->rbegin();
378 for (; rs != track.trackStateOnSurfaces()->rend(); ++rs) {
379 if (!(**rs).measurementOnTrack() || (**rs).type(Trk::TrackStateOnSurface::Outlier) ||
380 dynamic_cast<const Trk::PseudoMeasurementOnTrack*>((**rs).measurementOnTrack())) {
381 continue;
382 }
383
384 if (indetVolume->inside((**rs).measurementOnTrack()->globalPosition())) {
385 indet = true;
386 break;
387 } else if (!calorimeterVolume->inside((**rs).measurementOnTrack()->globalPosition())) {
388 spectrometer = true;
389 break;
390 }
391 }
392
393 return !indet && spectrometer;
394 }
395
396 bool MuonTrackQuery::isLineFit(const Trk::Track& track) const {
397 if (track.info().trackProperties(Trk::TrackInfo::StraightTrack)) { return true; }
398
399 // for backwards compatibility
400 const Trk::Perigee* measuredPerigee = track.perigeeParameters();
401
402 if (!measuredPerigee) return false;
403
404 if (!measuredPerigee->covariance()) {
405 ATH_MSG_WARNING(" measured Perigee has no covariance!");
406 return false;
407 }
408
409 double momentumCovariance = (*measuredPerigee->covariance())(Trk::qOverP, Trk::qOverP);
410
411 double relativeMomentumCovariance = momentumCovariance * measuredPerigee->momentum().mag2();
412
413 // unphysically small value used to signal SL
414 if (momentumCovariance < 1.E-19 || relativeMomentumCovariance < 1.E-6) return true;
415
416 // or if curvature unmeasured (error > 1000%)
417 if (relativeMomentumCovariance > 100.) return true;
418
419 return false;
420 }
421
422 bool MuonTrackQuery::isProjective(const Trk::Track& track) const {
423 // get start and end measured positions
424 Amg::Vector3D startPosition{origin};
425 for (const Trk::TrackStateOnSurface* tsos : *track.trackStateOnSurfaces()) {
426 if (tsos->measurementOnTrack() && !tsos->type(Trk::TrackStateOnSurface::Outlier) &&
427 !dynamic_cast<const Trk::PseudoMeasurementOnTrack*>(tsos->measurementOnTrack())) {
428 startPosition = tsos->measurementOnTrack()->globalPosition();
429 break;
430 }
431 }
432
433 Amg::Vector3D endPosition{origin};
434 Trk::TrackStates::const_reverse_iterator rs = track.trackStateOnSurfaces()->rbegin();
435 for (; rs != track.trackStateOnSurfaces()->rend(); ++rs) {
436 if ((**rs).measurementOnTrack() && !(**rs).type(Trk::TrackStateOnSurface::Outlier) &&
437 !dynamic_cast<const Trk::PseudoMeasurementOnTrack*>((**rs).measurementOnTrack())) {
438 endPosition = (**rs).measurementOnTrack()->globalPosition();
439 break;
440 }
441 }
442
443 // same-side check: kill if through (up-down) track
444 double side = endPosition.dot(startPosition);
445 if (side < 0.) {
446 ATH_MSG_DEBUG("track is not projective: fails same-side check ");
447 return false;
448 }
449
450 // hence change in phi between these measurements
451 const CxxUtils::sincos sc_dPhi{startPosition.deltaPhi(endPosition)};
452 // cut on change of long phi sector
453 constexpr double PiOver8 = M_PI / 8.;
454 if (std::abs(sc_dPhi.sn) > PiOver8) {
455 ATH_MSG_DEBUG("track is not projective: sinDeltaPhi " << sc_dPhi.sn);
456 return false;
457 }
458
459 // cut on change of hemisphere (except for far forward tracks)
460 if (sc_dPhi.cs < 0.) {
461 double cotTheta = startPosition.z() / startPosition.perp();
462 if (std::abs(startPosition.z()) > std::abs(endPosition.z())) { cotTheta = endPosition.z() / endPosition.perp(); }
463
464 // FIXME: isn't this same-side again?
465 if (startPosition.z() * endPosition.z() < 0. || std::abs(cotTheta) < 2.0) {
466 ATH_MSG_DEBUG("track is not projective: cosDeltaPhi " << sc_dPhi.cs);
467 return false;
468 }
469
470 ATH_MSG_WARNING("forward track changes hemisphere: cotTheta " << cotTheta << " cosDeltaPhi " << sc_dPhi.cs);
471 }
472
473 // OK - the track is at least roughly projective
474 return true;
475 }
476
478 constexpr double sectorOffset = M_PI / 16.;
479 bool isOverlap = true;
480 double cosPhi = 0.;
481 double sinPhi = 0.;
482 for (const Trk::TrackStateOnSurface* tsos : *track.trackStateOnSurfaces()) {
483 if (!tsos->measurementOnTrack() || dynamic_cast<const Trk::PseudoMeasurementOnTrack*>(tsos->measurementOnTrack())) { continue; }
484
485 if (isOverlap) {
486 isOverlap = false;
487 const Amg::Vector3D& position = tsos->measurementOnTrack()->associatedSurface().globalReferencePoint();
488
489 cosPhi = position.x() / position.perp();
490 sinPhi = position.y() / position.perp();
491 } else {
492 const Amg::Vector3D& position = tsos->measurementOnTrack()->associatedSurface().globalReferencePoint();
493
494 double sinDeltaPhi = (position.x() * sinPhi - position.y() * cosPhi) / position.perp();
496 if (std::abs(sinDeltaPhi) > sectorOffset) {
497 ATH_MSG_DEBUG("found overlap: sinDeltaPhi " << sinDeltaPhi);
498 isOverlap = true;
499 break;
500 }
501 }
502 }
503
504 return isOverlap;
505 }
506
507 bool MuonTrackQuery::isSlimmed(const Trk::Track& track) const {
508 Trk::TrackStates::const_iterator s = track.trackStateOnSurfaces()->begin();
509 for (; s != track.trackStateOnSurfaces()->end(); ++s) {
510 if ((**s).trackParameters()) continue;
511
512 ATH_MSG_VERBOSE("track is slimmed (found TSOS without trackParameters) ");
513 return true;
514 }
515
516 ATH_MSG_VERBOSE("track is not slimmed (all TSOS have trackParameters) ");
517
518 return false;
519 }
520
521 double MuonTrackQuery::momentumBalanceSignificance(const Trk::Track& track, const EventContext& ctx) const {
522 // significance only defined for combined muons
523 if (!isCombined(track, ctx)) return 0.;
524
525 // find the TSOS carrying caloEnergy (TSOS type CaloDeposit)
526 // compare parameters with those from previous TSOS
527 double energyBalance = 0.;
528
529 const Trk::EnergyLoss* energyLoss = nullptr;
530 const Trk::TrackParameters* previousParameters = nullptr;
531
532 for (const Trk::TrackStateOnSurface* tsos : *track.trackStateOnSurfaces()) {
533 if (!tsos->trackParameters()) continue;
534
535 if (tsos->materialEffectsOnTrack()) {
536 const Trk::MaterialEffectsOnTrack* meot = dynamic_cast<const Trk::MaterialEffectsOnTrack*>(tsos->materialEffectsOnTrack());
537
538 if (!meot) continue;
539 energyLoss = meot->energyLoss();
540
541 if (tsos->type(Trk::TrackStateOnSurface::CaloDeposit) && energyLoss && previousParameters) {
542 energyBalance = previousParameters->momentum().mag() - energyLoss->deltaE() - tsos->trackParameters()->momentum().mag();
543
544 if (std::abs(energyBalance) < energyLoss->sigmaDeltaE()) {
545 ATH_MSG_DEBUG(std::setiosflags(std::ios::fixed)
546 << " momentum balance " << std::setw(6) << std::setprecision(2) << energyBalance / Units::GeV
547 << " significance " << std::setw(6) << std::setprecision(1)
548 << energyBalance / energyLoss->sigmaDeltaE() << " p before/after calo" << std::setw(7)
549 << std::setprecision(2) << previousParameters->momentum().mag() / Units::GeV << " /" << std::setw(7)
550 << std::setprecision(2) << tsos->trackParameters()->momentum().mag() / Units::GeV
551 << " energy deposit sigma " << energyLoss->sigmaDeltaE() / Units::GeV << " GeV");
552
553 energyBalance /= energyLoss->sigmaDeltaE();
554 } else if (energyBalance < 0.) {
555 ATH_MSG_DEBUG(std::setiosflags(std::ios::fixed)
556 << " momentum balance " << std::setw(6) << std::setprecision(2) << energyBalance / Units::GeV
557 << " significance " << std::setw(6) << std::setprecision(1)
558 << energyBalance / energyLoss->sigmaDeltaE() << " p before/after calo" << std::setw(7)
559 << std::setprecision(2) << previousParameters->momentum().mag() / Units::GeV << " /" << std::setw(7)
560 << std::setprecision(2) << tsos->trackParameters()->momentum().mag() / Units::GeV
561 << " energy deposit sigma- " << energyLoss->sigmaMinusDeltaE() / Units::GeV << " GeV");
562
563 energyBalance /= energyLoss->sigmaMinusDeltaE();
564 } else {
565 ATH_MSG_DEBUG(std::setiosflags(std::ios::fixed)
566 << " momentum balance " << std::setw(6) << std::setprecision(2) << energyBalance / Units::GeV
567 << " significance " << std::setw(6) << std::setprecision(1)
568 << energyBalance / energyLoss->sigmaDeltaE() << " p before/after calo" << std::setw(7)
569 << std::setprecision(2) << previousParameters->momentum().mag() / Units::GeV << " /" << std::setw(7)
570 << std::setprecision(2) << tsos->trackParameters()->momentum().mag() / Units::GeV
571 << " energy deposit sigma+ " << energyLoss->sigmaPlusDeltaE() / Units::GeV << " GeV");
572
573 energyBalance /= energyLoss->sigmaPlusDeltaE();
574 }
575 break;
576 }
577 }
578
579 // update previous parameters
580 previousParameters = tsos->trackParameters();
581 }
582
583 return energyBalance;
584 }
585
587 unsigned numberPseudo = 0;
588
589 Trk::TrackStates::const_iterator s = track.trackStateOnSurfaces()->begin();
590 for (; s != track.trackStateOnSurfaces()->end(); ++s) {
591 if ((**s).measurementOnTrack() && dynamic_cast<const Trk::PseudoMeasurementOnTrack*>((**s).measurementOnTrack())) {
592 ++numberPseudo;
593 }
594 }
595
596 ATH_MSG_VERBOSE(" track has " << numberPseudo << " PseudoMeasurements");
597
598 return numberPseudo;
599 }
600
601 std::unique_ptr<const Trk::Perigee> MuonTrackQuery::outgoingPerigee(const Trk::Track& track) const {
602 // only flip Perigee
603 const Trk::Perigee* perigee = track.perigeeParameters();
604 if (!perigee) {
605 ATH_MSG_WARNING(" input track without Perigee");
606 return nullptr;
607 }
608
609 const Trk::PerigeeSurface& surface = perigee->associatedSurface();
610
611 const Trk::TrackStateOnSurface* outerTSOS = track.trackStateOnSurfaces()->back();
612 if (!perigee->covariance()) {
613 ATH_MSG_WARNING(" perigee has no covariance");
614 return std::make_unique<Trk::Perigee>(*perigee);
615 }
616
617 if (outerTSOS->trackParameters() == perigee && perigee->momentum().dot(perigee->position()) <= 0.) {
618 const AmgSymMatrix(5)& perigeeCov = (*perigee->covariance());
619
620 // need to flip
621 return std::make_unique<Trk::Perigee>(perigee->position(), -perigee->momentum(), -perigee->charge(), surface, perigeeCov);
622 }
623
624 return std::make_unique<Trk::Perigee>(*perigee);
625 }
627 const Trk::TrackingVolume* calorimeterVolume = getVolume("Calo::Container", ctx);
628 if (!calorimeterVolume) {
629 ATH_MSG_WARNING("Failed to retrieve Calo volume ");
631 }
632
633 const Trk::TrackingVolume* indetVolume = getVolume("InDet::Containers::InnerDetector", ctx);
634
635 if (!indetVolume) {
636 ATH_MSG_WARNING("Failed to retrieve InDeT volume ");
638 }
639
640 // provide refit for slimmed tracks
641 std::unique_ptr<const Trk::Track> refittedTrack;
642
643 if (isSlimmed(track)) {
644 if (!m_fitter.empty()) {
645 ATH_MSG_VERBOSE(" perform refit as track has been slimmed");
646 refittedTrack = m_fitter->fit(ctx, track, false, Trk::muon);
647 }
648 if (!refittedTrack) return ScatteringAngleSignificance(0);
649 } else {
650 refittedTrack = std::make_unique<Trk::Track>(track);
651 }
652
653 // collect sigma of scatterer up to TSOS carrying caloEnergy
654 double charge = refittedTrack->perigeeParameters()->charge();
655 bool haveMeasurement = false;
656 unsigned scatterers = 0;
657 double totalSigma = 0.;
658
659 std::vector<bool> isMeasurement;
660 std::vector<double> sigmas;
661 std::vector<double> radii;
662
663 for (const Trk::TrackStateOnSurface* tsos : *refittedTrack->trackStateOnSurfaces()) {
664 // skip leading material
665 if (!haveMeasurement) {
666 if (tsos->measurementOnTrack()) { haveMeasurement = true; }
667 continue;
668 }
669
670 // select MaterialEffects up to calorimeter volume
671 if (!tsos->materialEffectsOnTrack()) continue;
672
673 const Trk::MaterialEffectsOnTrack* meot = dynamic_cast<const Trk::MaterialEffectsOnTrack*>(tsos->materialEffectsOnTrack());
674
675 const Amg::Vector3D& position = meot->associatedSurface().globalReferencePoint();
676
677 if (!calorimeterVolume->inside(position)) break;
678
679 if (!indetVolume->inside(position) && meot->energyLoss()) break;
680
681 // get scattering angle
682 const Trk::ScatteringAngles* scattering = meot->scatteringAngles();
683 if (!scattering) continue;
684
685 ++scatterers;
686
687 if (tsos->measurementOnTrack()) {
688 isMeasurement.push_back(true);
689 } else {
690 isMeasurement.push_back(false);
691 }
692
693 radii.push_back(tsos->trackParameters()->position().perp());
694 double sigma = charge * scattering->deltaPhi() / scattering->sigmaDeltaPhi();
695 totalSigma += sigma;
696 sigmas.push_back(sigma);
697
698 ATH_MSG_VERBOSE(scatterers << " radius " << tsos->trackParameters()->position().perp() << " deltaPhi "
699 << scattering->deltaPhi() << " significance " << sigma << " totalSignificance " << totalSigma);
700 }
701
702 if (!scatterers) { return ScatteringAngleSignificance(0); }
703
704 // search for discontinuity point (sign change of integral scattering)
705 // and maximum of neighbouring scatterers
706 // FIXME: neighbouring means scatterers spanning neighbouring measurements
707 double curvatureSignificance = totalSigma;
708 double curvatureRadius = 0.;
709 double neighbourSignificance = 0.;
710 double neighbourRadius = 0.;
711 double previousSignificance = 0.;
712 double previousRadius = 0.;
713 unsigned index = scatterers - 1;
714
715 std::vector<double>::const_reverse_iterator rs = sigmas.rbegin();
716 for (; rs != sigmas.rend(); ++rs, --index) {
717 totalSigma -= 2. * (*rs);
718
719 if (std::abs(totalSigma) > std::abs(curvatureSignificance)) {
720 curvatureSignificance = totalSigma;
721 curvatureRadius = radii[index];
722 }
723
724 if (std::abs(sigmas[index] + previousSignificance) > std::abs(neighbourSignificance)) {
725 neighbourSignificance = sigmas[index] + previousSignificance;
726
727 if (std::abs(neighbourSignificance) > 0.) {
728 neighbourRadius = (sigmas[index] * radii[index] + previousSignificance * previousRadius) / neighbourSignificance;
729 } else {
730 neighbourRadius = 0.;
731 }
732 }
733
734 previousSignificance = sigmas[index];
735 previousRadius = radii[index];
736 }
737
738 // normalize
739 curvatureSignificance /= std::sqrt(static_cast<double>(scatterers));
740 neighbourSignificance *= OneOverSqrt2;
741
742 ATH_MSG_DEBUG(" scatteringAngleSignificance " << curvatureSignificance << " at radius " << curvatureRadius
743 << " neighbourSignificance " << neighbourSignificance << " at radius "
744 << neighbourRadius << " from " << scatterers << " scatterers");
745
746 return ScatteringAngleSignificance(scatterers, curvatureRadius, curvatureSignificance, neighbourRadius, neighbourSignificance);
747 }
748
749 std::unique_ptr<Trk::TrackParameters> MuonTrackQuery::spectrometerParameters(const Trk::Track& track, const EventContext& ctx) const {
750 const Trk::TrackingVolume* calorimeterVolume = getVolume("Calo::Container", ctx);
751
752 if (!calorimeterVolume) {
753 ATH_MSG_WARNING("Failed to retrieve Calo volume ");
754 return nullptr;
755 }
756
757 // find parameters at innermost spectrometer measurement
758 // clone perigee if track has been slimmed
759 const Trk::TrackParameters* parametersView{};
760 for (const Trk::TrackStateOnSurface* pThisTrackState : *track.trackStateOnSurfaces()) {
761 if (!pThisTrackState->measurementOnTrack() || pThisTrackState->type(Trk::TrackStateOnSurface::Outlier) ||
762 !pThisTrackState->trackParameters() ||
763 dynamic_cast<const Trk::PseudoMeasurementOnTrack*>(pThisTrackState->measurementOnTrack()) ||
764 calorimeterVolume->inside(pThisTrackState->trackParameters()->position())) {
765 continue;
766 }
767
768 if (parametersView && pThisTrackState->trackParameters()->position().mag() > parametersView->position().mag()) { break; }
769 parametersView = pThisTrackState->trackParameters();
770 }
771
772 if (!parametersView) { parametersView = track.perigeeParameters(); }
773
774 // if necessary, flip to outgoing parameters
775 if (parametersView->momentum().dot(parametersView->position()) > 0.) {
776 return parametersView->uniqueClone(); // creates new parameters as clone
777 } else {
778 ATH_MSG_VERBOSE(" flip spectrometer parameters ");
779 return flippedParameters(*parametersView); // creates new parameters as flipped clone
780 }
781 }
782
785 unsigned MuonTrackQuery::spectrometerPhiQuality(const Trk::Track& track, const EventContext& ctx) const {
786 const Trk::TrackingVolume* calorimeterVolume = getVolume("Calo::Container", ctx);
787
788 if (!calorimeterVolume) {
789 ATH_MSG_WARNING("Failed to retrieve Calo volume ");
790 return false;
791 }
792
793 // quality 0 - no additional phi measurement needed
794 // 1 - phi direction un- (or poorly) constrained
795 // 2 - no phi hits on track
796 const Trk::TrackStateOnSurface* leadingPhiMeasurement = nullptr;
797 const Trk::TrackStateOnSurface* trailingPhiMeasurement = nullptr;
798
799 Trk::TrackStates::const_iterator s = track.trackStateOnSurfaces()->begin();
800 for (; s != track.trackStateOnSurfaces()->end(); ++s) {
801 if (!(**s).measurementOnTrack() || (**s).type(Trk::TrackStateOnSurface::Outlier) ||
802 dynamic_cast<const Trk::PseudoMeasurementOnTrack*>((**s).measurementOnTrack()) || !(**s).trackParameters() ||
803 calorimeterVolume->inside((**s).trackParameters()->position())) {
804 continue;
805 }
806
807 Identifier id = m_edmHelperSvc->getIdentifier(*(**s).measurementOnTrack());
808 if (!id.is_valid() || !m_idHelperSvc->measuresPhi(id)) { continue; }
809
810 // require phi measurement from CSC or CompetingROT
811 if (!dynamic_cast<const Muon::CompetingMuonClustersOnTrack*>((**s).measurementOnTrack()) && !m_idHelperSvc->isCsc(id)) {
812 continue;
813 }
814
815 leadingPhiMeasurement = *s;
816 break;
817 }
818
819 if (!leadingPhiMeasurement) return 2;
820
821 Trk::TrackStates::const_reverse_iterator r = track.trackStateOnSurfaces()->rbegin();
822 for (; r != track.trackStateOnSurfaces()->rend(); ++r) {
823 if (!(**r).measurementOnTrack() || (**r).type(Trk::TrackStateOnSurface::Outlier) ||
824 dynamic_cast<const Trk::PseudoMeasurementOnTrack*>((**r).measurementOnTrack()) || !(**r).trackParameters()) {
825 continue;
826 }
827
828 if (calorimeterVolume->inside((**r).trackParameters()->position())) { break; }
829
830 Identifier id = m_edmHelperSvc->getIdentifier(*(**r).measurementOnTrack());
831
832 if (!id.is_valid() || !m_idHelperSvc->measuresPhi(id)) { continue; }
833
834 if (*r != leadingPhiMeasurement) { trailingPhiMeasurement = *r; }
835
836 break;
837 }
838
839 if (!trailingPhiMeasurement) { return 1; }
840
841 // FIXME: check separation between phi meas
842 double separation =
843 (leadingPhiMeasurement->trackParameters()->position() - trailingPhiMeasurement->trackParameters()->position()).mag();
844
845 if (separation < 2. * Units::meter) { return 1; }
846
847 return 0;
848 }
849
850 std::unique_ptr<const Trk::TrackParameters> MuonTrackQuery::triggerStationParameters(const Trk::Track& track,
851 const EventContext& ctx) const {
852 const Trk::TrackingVolume* calorimeterVolume = getVolume("Calo::Container", ctx);
853
854 if (!calorimeterVolume) {
855 ATH_MSG_WARNING("Failed to retrieve Calo volume ");
856 return nullptr;
857 }
858
859 // find parameters at innermost trigger station measurement
860 // fails if track has been slimmed
861 std::unique_ptr<const Trk::TrackParameters> parameters;
862
863 Trk::TrackStates::const_iterator s = track.trackStateOnSurfaces()->begin();
864 for (; s != track.trackStateOnSurfaces()->end(); ++s) {
865 if (!(**s).measurementOnTrack() || (**s).type(Trk::TrackStateOnSurface::Outlier) || !(**s).trackParameters() ||
866 !dynamic_cast<const Muon::CompetingMuonClustersOnTrack*>((**s).measurementOnTrack()) ||
867 calorimeterVolume->inside((**s).trackParameters()->position())) {
868 continue;
869 }
870
871 if (parameters && (**s).trackParameters()->position().mag() > parameters->position().mag()) { break; }
872
873 parameters = (**s).trackParameters()->uniqueClone();
874 }
875
876 if (!parameters) return nullptr;
877
878 // if necessary, flip to outgoing parameters
879 if (parameters->momentum().dot(parameters->position()) > 0.) {
880 parameters = parameters->uniqueClone();
881 } else {
882 ATH_MSG_VERBOSE(" flip spectrometer parameters ");
883 parameters = flippedParameters(*parameters);
884 }
885
886 return parameters;
887 }
888
889 std::unique_ptr<Trk::TrackParameters> MuonTrackQuery::flippedParameters(const Trk::TrackParameters& parameters) {
890 double phi = parameters.parameters()[Trk::phi0];
891 if (phi > 0.) {
892 phi -= M_PI;
893 } else {
894 phi += M_PI;
895 }
896
897 double theta = M_PI - parameters.parameters()[Trk::theta];
898 double qOverP = -parameters.parameters()[Trk::qOverP];
899 const Trk::Surface* surface = &(parameters.associatedSurface());
900
901 return surface->createUniqueTrackParameters(
902 parameters.parameters()[Trk::d0], parameters.parameters()[Trk::z0], phi, theta, qOverP,
903 parameters.covariance() ? std::optional<AmgSymMatrix(5)>(*parameters.covariance()) : std::nullopt);
904 }
905
906} // namespace Rec
#define M_PI
Scalar phi() const
phi method
Scalar theta() const
theta method
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_ERROR(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 rs
Wrapper to avoid constant divisions when using units.
AthAlgTool(const std::string &type, const std::string &name, const IInterface *parent)
Constructor with parameters:
class extending the basic Trk::EnergyLoss to describe the measured or parameterised muon energy loss ...
Definition CaloEnergy.h:28
DataModel_detail::const_iterator< DataVector > const_iterator
Definition DataVector.h:838
std::reverse_iterator< const_iterator > const_reverse_iterator
Definition DataVector.h:847
Class for competing MuonClusters, it extends the Trk::CompetingRIOsOnTrack base class.
lightweight return data-object for field integral track query
StatusCode initialize() override
std::unique_ptr< const Trk::TrackParameters > triggerStationParameters(const Trk::Track &track, const EventContext &ctx) const override
IMuonTrackQuery interface: trackParameters at innermost trigger chamber TSOS in MS.
SG::ReadCondHandleKey< Trk::TrackingGeometry > m_trackingGeometryReadKey
ToolHandle< Muon::IMdtDriftCircleOnTrackCreator > m_mdtRotCreator
bool isSectorOverlap(const Trk::Track &track) const override
IMuonTrackQuery interface: is there a long/short chamber overlap?
unsigned numberPseudoMeasurements(const Trk::Track &track) const override
IMuonTrackQuery interface: number of PseudoMeasurements on track (counts one for any vertex measureme...
bool isCombined(const Trk::Track &track, const EventContext &ctx) const override
IMuonTrackQuery interface: does track have measurements from indet and spectrometer ?
double caloEnergyDeposit(const Trk::Track &track, const EventContext &ctx) const override
IMuonTrackQuery interface: track energy deposit in calorimeters (as fitted or otherwise applied)
bool isCaloAssociated(const Trk::Track &track, const EventContext &ctx) const override
IMuonTrackQuery interface: does track have at least 3 TSOS's representing calorimeter ?
bool isExtrapolated(const Trk::Track &track, const EventContext &ctx) const override
IMuonTrackQuery interface: does track have perigee inside indet ?
std::unique_ptr< const Trk::Perigee > outgoingPerigee(const Trk::Track &track) const override
IMuonTrackQuery interface: perigee expressed outgoing from IP.
ServiceHandle< Muon::IMuonIdHelperSvc > m_idHelperSvc
ServiceHandle< Muon::IMuonEDMHelperSvc > m_edmHelperSvc
ScatteringAngleSignificance scatteringAngleSignificance(const Trk::Track &track, const EventContext &ctx) const override
IMuonTrackQuery interface: significance of early scattering angle pattern for combined tracks (wider ...
ToolHandle< Trk::ITrackFitter > m_fitter
bool isLineFit(const Trk::Track &track) const override
IMuonTrackQuery interface: does track have fitted curvature ?
bool isSlimmed(const Trk::Track &track) const override
IMuonTrackQuery interface: does track have TrackParameters at every TSOS ?
FieldIntegral fieldIntegral(const Trk::Track &track, const EventContext &ctx) const override
IMuonTrackQuery interface: field integral along track from momentum kick between measurements.
const Trk::TrackingVolume * getVolume(const std::string &&vol_name, const EventContext &ctx) const
const CaloEnergy * caloEnergy(const Trk::Track &track) const override
IMuonTrackQuery interface: caloEnergy from appropriate TSOS.
double momentumBalanceSignificance(const Trk::Track &track, const EventContext &ctx) const override
IMuonTrackQuery interface: significance of momentum balance for combined tracks (biassed residual)
std::unique_ptr< Trk::TrackParameters > spectrometerParameters(const Trk::Track &track, const EventContext &ctx) const override
IMuonTrackQuery interface: trackParameters at innermost measurement TSOS in MS.
MuonTrackQuery(const std::string &type, const std::string &name, const IInterface *parent)
static std::unique_ptr< Trk::TrackParameters > flippedParameters(const Trk::TrackParameters &params)
unsigned spectrometerPhiQuality(const Trk::Track &track, const EventContext &ctx) const override
IMuonTrackQuery interface: assess the number of additional phi measurements needed for MS (or SA) tra...
bool isProjective(const Trk::Track &track) const override
IMuonTrackQuery interface: is track (roughly) projective towards IP?
lightweight return data-object for (mainly indet) scattering angle analysis by track query
This class describes energy loss material effects in the ATLAS tracking EDM.
Definition EnergyLoss.h:34
double sigmaPlusDeltaE() const
returns the positive side
double sigmaMinusDeltaE() const
returns the negative side
double sigmaDeltaE() const
returns the symmatric error
double deltaE() const
returns the
const Surface & associatedSurface() const
returns the surface to which these m.eff. are associated.
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.
const Amg::Vector3D & momentum() const
Access method for the momentum.
const Amg::Vector3D & position() const
Access method for the position.
double charge() const
Returns the charge.
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...
virtual const S & associatedSurface() const override final
Access to the Surface method.
Class describing the Line to which the Perigee refers to.
Class to handle pseudo-measurements in fitters and on track objects.
virtual bool type(MeasurementBaseType::Type type) const override final
Extended method checking the type.
represents a deflection of the track caused through multiple scattering in material.
double sigmaDeltaPhi() const
returns the
double deltaPhi() 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,...
represents the track state (measurement, material, fit parameters and quality) at a surface.
const TrackParameters * trackParameters() const
return ptr to trackparameters const overload
@ Outlier
This TSoS contains an outlier, that is, it contains a MeasurementBase/RIO_OnTrack which was not used ...
@ CaloDeposit
This TSOS contains a CaloEnergy object.
Full Volume description used in Tracking, it inherits from Volume to get the geometrical structure,...
bool inside(const Amg::Vector3D &gp, double tol=0.) const
Inside() method for checks.
Definition Volume.cxx:72
int r
Definition globals.cxx:22
Eigen::Matrix< double, 3, 1 > Vector3D
Gaudi Tools.
ParametersT< TrackParametersDim, Charged, PerigeeSurface > Perigee
@ phi0
Definition ParamDefs.h:65
@ theta
Definition ParamDefs.h:66
@ qOverP
perigee
Definition ParamDefs.h:67
@ d0
Definition ParamDefs.h:63
@ z0
Definition ParamDefs.h:64
ParametersBase< TrackParametersDim, Charged > TrackParameters
Definition index.py:1
double deltaPhi(double phiA, double phiB)
delta Phi in range [-pi,pi[
Helper to simultaneously calculate sin and cos of the same angle.
Helper to simultaneously calculate sin and cos of the same angle.
Definition sincos.h:39