ATLAS Offline Software
Loading...
Searching...
No Matches
MooTrackFitter.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2023 CERN for the benefit of the ATLAS collaboration
3*/
4
5#include "MooTrackFitter.h"
6
32#include "TrkGeometry/Layer.h"
38#include "TrkTrack/Track.h"
39#include "TrkTrack/TrackInfo.h"
42
43namespace {
44 const double FiftyOverSqrt12 = 50. / std::sqrt(12);
45
46 struct Unowned{
47 void operator()(const Muon::MuonSegment*) const {};
48 };
49}
50namespace Muon {
51 using namespace MuonStationIndex;
52
53 MooTrackFitter::MooTrackFitter(const std::string& t, const std::string& n, const IInterface* p) : AthAlgTool(t, n, p) {
54 declareInterface<MooTrackFitter>(this);
55 }
56
58 ATH_CHECK(m_propagator.retrieve());
59 ATH_CHECK(m_trackSummaryTool.retrieve());
60 ATH_CHECK(m_cleaner.retrieve());
61 ATH_CHECK(m_overlapResolver.retrieve());
62 ATH_CHECK(m_trackFitter.retrieve());
64 ATH_CHECK(m_idHelperSvc.retrieve());
65 ATH_CHECK(m_edmHelperSvc.retrieve());
66 ATH_CHECK(m_hitHandler.retrieve());
67 ATH_CHECK(m_printer.retrieve());
68
69 // Configuration of the material effects
72
74 ATH_CHECK(m_phiHitSelector.retrieve());
75
76 return StatusCode::SUCCESS;
77 }
78
80 double nfits = m_nfits.load() > 0 ? m_nfits.load() : 1.;
81 double nfailedExtractInital = m_nfailedExtractInital / nfits;
82 double nfailedMinMaxPhi = m_nfailedMinMaxPhi / nfits;
83 double nfailedParsInital = m_nfailedParsInital / nfits;
84 double nfailedExtractCleaning = m_nfailedExtractCleaning / nfits;
85 double nfailedFakeInitial = m_nfailedFakeInitial / nfits;
86 double nfailedTubeFit = m_nfailedTubeFit / nfits;
87 double noPerigee = m_noPerigee / nfits;
88 double nlowMomentum = m_nlowMomentum / nfits;
89 double nfailedExtractPrecise = m_nfailedExtractPrecise / nfits;
90 double nfailedFakePrecise = m_nfailedFakePrecise / nfits;
91 double nfailedFitPrecise = m_nfailedFitPrecise / nfits;
92 double nsuccess = m_nsuccess / nfits;
93 ATH_MSG_INFO(" Summarizing statistics: nfits "
94 << m_nfits << std::endl
95 << "| extract | phi range | startPars | clean phi | add fake | fit | no perigee | low mom | extract | "
96 "add fake | final fit | passed |"
97 << std::endl
98 << std::setprecision(2) << std::setw(12) << nfailedExtractInital << std::setw(12) << nfailedMinMaxPhi << std::setw(12)
99 << nfailedParsInital << std::setw(12) << nfailedExtractCleaning << std::setw(12) << nfailedFakeInitial << std::setw(12)
100 << nfailedTubeFit << std::setw(13) << noPerigee << std::setw(12) << nlowMomentum << std::setw(12)
101 << nfailedExtractPrecise << std::setw(12) << nfailedFakePrecise << std::setw(12) << nfailedFitPrecise << std::setw(12)
102 << nsuccess);
103
104 return StatusCode::SUCCESS;
105 }
106
108 if (entry.hits().size() < entry.etaHits().size()) {
109 ATH_MSG_WARNING(" Corrupt Entry: more eta hits than hits! ");
110 return true;
111 }
112 if (entry.etaHits().size() < 3) { return (entry.ncscHitsEta < 2); }
113 return false;
114 }
115
116 std::unique_ptr<Trk::Track> MooTrackFitter::refit(const EventContext& ctx, const MuPatTrack& trkCan) const {
117
118 // internal representation of the track in fitter
119 FitterData fitterData;
120
121 // copy hit list
122 fitterData.hitList = trkCan.hitList();
123
124 // extract hits from hit list and add them to fitterData
125 if (!extractData(fitterData, true)) return nullptr;
126
127 // create start parameters
128 const Trk::Perigee* pp = trkCan.track().perigeeParameters();
129 if (!pp) return nullptr;
130
131 // fit track
132 std::unique_ptr<Trk::Track> track = fit(ctx, *pp, fitterData.measurements, Trk::muon, false);
133
134 if (track) {
135 // clean and evaluate track
136 std::set<Identifier> excludedChambers;
137 std::unique_ptr<Trk::Track> cleanTrack = cleanAndEvaluateTrack(ctx, *track, excludedChambers);
138 if (cleanTrack && !(*cleanTrack->perigeeParameters() == *track->perigeeParameters())) track.swap(cleanTrack);
139 } else {
140 ATH_MSG_DEBUG(" Fit failed ");
141 }
142 return track;
143 }
144
145 std::unique_ptr<Trk::Track> MooTrackFitter::refit(const EventContext& ctx, const Trk::Track& track) const {
146 if (msgLvl(MSG::DEBUG)) {
147 const Trk::TrackStates* states = track.trackStateOnSurfaces();
148 int nStates = 0;
149 if (states) nStates = states->size();
150 msg(MSG::DEBUG) << MSG::DEBUG << "refit: fitting track with hits: " << nStates;
151 if (msgLvl(MSG::VERBOSE)) { msg(MSG::VERBOSE) << std::endl << m_printer->printMeasurements(track); }
152 msg(MSG::DEBUG) << endmsg;
153 }
154 // fit track
155 std::unique_ptr<Trk::Track> newTrack = m_trackFitter->fit(ctx,track, false, m_ParticleHypothesis);
156
157 if (newTrack) {
158 // clean and evaluate track
159 std::set<Identifier> excludedChambers;
160 std::unique_ptr<Trk::Track> cleanTrack = cleanAndEvaluateTrack(ctx, *newTrack, excludedChambers);
161
162 if (cleanTrack) {
163 // check whether cleaner returned same track, if not delete old track
164 if (!(*cleanTrack->perigeeParameters() == *newTrack->perigeeParameters())) newTrack.swap(cleanTrack);
165 } else {
166 ATH_MSG_DEBUG(" Refit failed, rejected by cleaner ");
167 newTrack.reset();
168 }
169 } else {
170 ATH_MSG_DEBUG(" Refit failed ");
171 }
172
173 return newTrack;
174 }
175
176 std::unique_ptr<Trk::Track> MooTrackFitter::fit(const EventContext& ctx, const MuPatCandidateBase& entry1, const MuPatCandidateBase& entry2,
177 const PrepVec& externalPhiHits) const {
178 ++m_nfits;
179 // internal representation of the track in fitter
180 FitterData fitterData;
181
182 // extract hits and geometrical information
183 if (!extractData(entry1, entry2, fitterData)) {
184 ATH_MSG_DEBUG(" Failed to extract data for initial fit");
185 return nullptr;
186 }
188
189 // get the minimum and maximum phi compatible with all eta hits, don't use for cosmics
190 if (!m_cosmics && !getMinMaxPhi(fitterData)) {
191 ATH_MSG_DEBUG(" Phi range check failed, candidate stations not pointing. Will not fit track");
192 return nullptr;
193 }
194
196
197 // create start parameters
198 createStartParameters(ctx, fitterData);
199 std::unique_ptr<Trk::Perigee>& startPars = fitterData.startPars;
200 if (!startPars) {
201 ATH_MSG_DEBUG(" Creation of start parameters failed ");
202 return nullptr;
203 }
204 ATH_MSG_VERBOSE("Extracted start parameters: "<<Amg::toString(startPars->momentum().unit())<< " "<<Amg::toString(startPars->position()));
206
207 // clean phi hits and reevaluate hits. Do not run for cosmics
208 bool hasCleaned = m_cleanPhiHits && cleanPhiHits(ctx, startPars->momentum().mag(), fitterData, externalPhiHits);
209 if (hasCleaned) {
210 ATH_MSG_DEBUG(" Cleaned phi hits, re-extracting hits");
211 bool usePrecise = m_usePreciseHits || (fitterData.firstHasMomentum || fitterData.secondHasMomentum);
212 ATH_MSG_VERBOSE("Call extract data with usePrecise "<<usePrecise);
213 if (!extractData(fitterData, usePrecise)) {
214 ATH_MSG_DEBUG(" Failed to extract data after phi hit cleaning");
215 return nullptr;
216 }
217 }
219
220 // check whether there are enough phi constraints, if not add fake phi hits
221 if (!addFakePhiHits(ctx, fitterData, *startPars)) {
222 ATH_MSG_DEBUG(" Failed to add fake phi hits for precise fit");
223 return nullptr;
224 }
226
227 // fit track with broad errors, no material
228 bool doPreFit = m_usePrefit;
230 if (fitterData.firstHasMomentum || fitterData.secondHasMomentum) doPreFit = false;
231 if (!doPreFit) particleType = Trk::muon;
232
233 std::unique_ptr<Trk::Track> track = fit(ctx, *startPars, fitterData.measurements, particleType, doPreFit);
234
235 if (!track) {
236 ATH_MSG_DEBUG(" Fit failed ");
237 return nullptr;
238 }
240
241 // create start parameters
242 const Trk::Perigee* pp = track->perigeeParameters();
243 if (!pp) {
244 ATH_MSG_DEBUG(" Track without perigee parameters, exit ");
245 return nullptr;
246 }
247 ++m_noPerigee;
248
249 if (!m_slFit && !validMomentum(*pp)) {
250 ATH_MSG_VERBOSE(" Low momentum, rejected ");
251 return nullptr;
252 }
254
255 if (!fitterData.firstHasMomentum && !fitterData.secondHasMomentum && doPreFit) {
256 ATH_MSG_DEBUG(" Performing second fit ");
257
258 // refit with precise errors
259 FitterData fitterDataRefit{};
260 fitterDataRefit.startPars.reset(pp->clone());
261 fitterDataRefit.firstIsTrack = fitterData.firstIsTrack;
262 fitterDataRefit.secondIsTrack = fitterData.secondIsTrack;
263 fitterDataRefit.firstHasMomentum = fitterData.firstHasMomentum;
264 fitterDataRefit.secondHasMomentum = fitterData.secondHasMomentum;
265 fitterDataRefit.avePhi = fitterData.avePhi;
266 fitterDataRefit.phiMin = fitterData.phiMin;
267 fitterDataRefit.phiMax = fitterData.phiMax;
268 fitterDataRefit.firstEntry = fitterData.firstEntry;
269 fitterDataRefit.secondEntry = fitterData.secondEntry;
270 fitterDataRefit.hitList = fitterData.hitList;
271
272 // extract hits from hit list and add them to fitterData
273 if (!extractData(fitterDataRefit, true)) {
274 ATH_MSG_DEBUG(" Failed to extract data for precise fit");
275 return nullptr;
276 }
278
279 // check whether there are enough phi constraints, if not add fake phi hits
280 if (!addFakePhiHits(ctx, fitterDataRefit, *startPars)) {
281 ATH_MSG_DEBUG(" Failed to add fake phi hits for precise fit");
282 return nullptr;
283 }
285
286 // fit track
287 std::unique_ptr<Trk::Track> newTrack = fit(ctx, *pp, fitterDataRefit.measurements, Trk::muon, false);
288 if (newTrack)
289 track.swap(newTrack);
290 else if (!m_allowFirstFit) {
291 ATH_MSG_DEBUG(" Precise fit failed ");
292 return nullptr;
293 } else {
294 ATH_MSG_DEBUG(" Precise fit failed, keep fit with broad errors");
295 }
297
298 fitterData.garbage.insert(fitterData.garbage.end(), std::make_move_iterator(fitterDataRefit.garbage.begin()),
299 std::make_move_iterator(fitterDataRefit.garbage.end()));
300 fitterDataRefit.garbage.clear();
301 }
302
303 if (track) {
304 // clean and evaluate track
305 std::set<Identifier> excludedChambers;
306 if (fitterData.firstIsTrack && !fitterData.secondIsTrack) {
307 excludedChambers = fitterData.secondEntry->chamberIds();
308 ATH_MSG_VERBOSE(" Using exclusion list of second entry for cleaning");
309 } else if (!fitterData.firstIsTrack && fitterData.secondIsTrack) {
310 excludedChambers = fitterData.firstEntry->chamberIds();
311 ATH_MSG_VERBOSE(" Using exclusion list of first entry for cleaning");
312 }
313 if (!excludedChambers.empty()) { ATH_MSG_DEBUG(" Using exclusion list for cleaning"); }
314 std::unique_ptr<Trk::Track> cleanTrack = cleanAndEvaluateTrack(ctx, *track, excludedChambers);
315 if (cleanTrack) {
316 if (!(*cleanTrack->perigeeParameters() == *track->perigeeParameters())) track.swap(cleanTrack);
317 } else
318 track.reset();
319 }
320
321 if (track) ++m_nsuccess;
322
323 if (msgLvl(MSG::DEBUG) && track) msg(MSG::DEBUG) << MSG::DEBUG << " Track found " << endmsg;
324 return track;
325 }
326
328 MooTrackFitter::FitterData& fitterData) const {
329 // sanity checks on the entries
330 if (corruptEntry(entry1)) {
331 ATH_MSG_DEBUG(" corrupt first entry, cannot perform fit: eta hits " << entry1.etaHits().size());
332 return false;
333 }
334 if (corruptEntry(entry2)) {
335 ATH_MSG_DEBUG(" corrupt second entry, cannot perform fit: eta hits " << entry2.etaHits().size());
336 return false;
337 }
338 // are we in the endcap region?
339 bool isEndcap = entry1.hasEndcap() || entry2.hasEndcap();
340
341
342 // measurement sorting function
343 SortMeasurementsByPosition sortMeasurements(isEndcap);
344
345 bool entry1IsFirst = sortMeasurements(entry1.hits().front(), entry2.hits().front());
346 if (m_cosmics) {
347 DistanceToPars distToPars(&entry1.entryPars());
348 double distToSecond = distToPars(entry2.entryPars().position());
349 if (distToSecond < 0) entry1IsFirst = false;
350 ATH_MSG_DEBUG(" first entry dir " << Amg::toString(entry1.entryPars().momentum())
351 << " pos " << Amg::toString(entry1.entryPars().position()) << " second "
352 << Amg::toString(entry2.entryPars().position()) << " dist " << distToSecond);
353 }
354 const MuPatCandidateBase& firstEntry = entry1IsFirst ? entry1 : entry2;
355 const MuPatCandidateBase& secondEntry = entry1IsFirst ? entry2 : entry1;
356
357 fitterData.firstEntry = &firstEntry;
358 fitterData.secondEntry = &secondEntry;
359
360 // check whether we are dealing with a track or a segment
361 if (dynamic_cast<const MuPatTrack*>(fitterData.firstEntry)) {
362 fitterData.firstIsTrack = true;
363 fitterData.firstHasMomentum = fitterData.firstEntry->hasMomentum();
364 }
365 if (dynamic_cast<const MuPatTrack*>(fitterData.secondEntry)) {
366 fitterData.secondIsTrack = true;
367 fitterData.secondHasMomentum = fitterData.secondEntry->hasMomentum();
368 }
369 // merge hitLists and add them to the fitterData
370 fitterData.hitList = m_hitHandler->merge(entry1.hitList(), entry2.hitList());
371
372 bool usePrecise = m_usePreciseHits || (fitterData.firstHasMomentum || fitterData.secondHasMomentum);
373 if (msgLvl(MSG::DEBUG)) {
374 msg(MSG::DEBUG) << MSG::DEBUG << " entering fitter: etaHits, first entry: ";
375 if (fitterData.firstIsTrack)
376 msg(MSG::DEBUG) << " track ";
377 else
378 msg(MSG::DEBUG) << " segment ";
379 msg(MSG::DEBUG) << fitterData.firstEntry->etaHits().size() << std::endl
380 << m_hitHandler->print(fitterData.firstEntry->hitList()) << std::endl
381 << " second entry: ";
382 if (fitterData.secondIsTrack)
383 msg(MSG::DEBUG) << " track ";
384 else
385 msg(MSG::DEBUG) << " segment ";
386 msg(MSG::DEBUG) << fitterData.secondEntry->etaHits().size() << std::endl
387 << m_hitHandler->print(fitterData.secondEntry->hitList()) << endmsg;
388 }
389
390 if (msgLvl(MSG::DEBUG)) {
391 msg(MSG::DEBUG) << MSG::DEBUG << " merged hit lists, new list size: " << fitterData.hitList.size();
392 if (usePrecise) msg(MSG::DEBUG) << " using precise errors" << endmsg;
393 if (msgLvl(MSG::VERBOSE)) msg(MSG::DEBUG) << std::endl << m_hitHandler->print(fitterData.hitList);
394 msg(MSG::DEBUG) << endmsg;
395 }
396
397 return extractData(fitterData, usePrecise);
398 }
399
400 bool MooTrackFitter::extractData(MooTrackFitter::FitterData& fitterData, bool usePreciseHits) const {
401 ATH_MSG_DEBUG(" extracting hits from hit list, using " << (usePreciseHits ? "precise measurements" : "broad measurements"));
402
403 MuPatHitList& hitList = fitterData.hitList;
404 // make sure the vector is sufficiently large
405 unsigned int nhits = hitList.size();
406
407 fitterData.measurements.clear();
408 fitterData.firstLastMeasurements.clear();
409 fitterData.etaHits.clear();
410 fitterData.phiHits.clear();
411 fitterData.measurements.reserve(nhits);
412 fitterData.firstLastMeasurements.reserve(nhits);
413
414 if (usePreciseHits && fitterData.startPars) removeSegmentOutliers(fitterData);
415
416 StIndex firstStation = StIndex::StUnknown;
417 ChIndex currentChIndex = ChIndex::ChUnknown;
418 bool currentMeasPhi = false;
419 MuPatHitPtr previousHit{nullptr};
420
421 // loop over hit list
422 for (const MuPatHitPtr& hit : hitList) {
423 const Identifier& id = hit->info().id;
424 if (hit->info().status != MuPatHit::OnTrack || !id.is_valid()) {
425 ATH_MSG_VERBOSE("Discard outlier "<<m_idHelperSvc->toString(id));
426 continue;
427 }
428 // in theory, there are only MuPatHit objects for MS hits
429 if (!m_idHelperSvc->isMuon(id)) {
430 ATH_MSG_WARNING("given Identifier " << id.get_compact() << " (" << m_idHelperSvc->toString(id)
431 << ") is not a muon Identifier, continuing");
432 continue;
433 }
434
435 ChIndex chIndex = m_idHelperSvc->chamberIndex(id);
437 fitterData.stations.insert(stIndex);
438
439 if (firstStation == StIndex::StUnknown) {
440 firstStation = stIndex;
441 }
442
443 const bool measuresPhi = hit->info().measuresPhi;
444
445 if (!m_idHelperSvc->isTgc(id) && !measuresPhi) {
446 const bool isSmall = m_idHelperSvc->isSmallChamber(id);
447 SmallLargeChambers& stCount = fitterData.smallLargeChambersPerStation[stIndex];
448 stCount.first += isSmall;
449 stCount.second += !isSmall;
450 }
451
452 bool isEndcap = m_idHelperSvc->isEndcap(id);
453 fitterData.hasEndcap |= isEndcap;
454 fitterData.hasBarrel |= !isEndcap;
455
456 const Trk::MeasurementBase* meas = usePreciseHits ? &hit->preciseMeasurement() : &hit->broadMeasurement();
457
458 // special treatment of hits in first stations on the track to stabalise the track fit
459 if (!usePreciseHits && m_preciseFirstStation) { meas = &hit->preciseMeasurement(); }
460
461 if (measuresPhi)
462 fitterData.phiHits.push_back(meas);
463 else
464 fitterData.etaHits.push_back(meas);
465
466 if (msgLvl(MSG::DEBUG)) {
467 double rDrift = meas->localParameters()[Trk::locR];
468 double rError = Amg::error(meas->localCovariance(), Trk::locR);
469 if (usePreciseHits && m_idHelperSvc->isMdt(id) && std::abs(rDrift) < 0.01 && rError > 4.) {
470 ATH_MSG_WARNING(" MDT hit error broad but expected precise error ");
471 }
472 }
473
474 fitterData.measurements.push_back(meas);
475
476 if (!measuresPhi && m_idHelperSvc->isTrigger(id)) continue;
477
478 if (!previousHit) {
479 currentChIndex = chIndex;
480 currentMeasPhi = measuresPhi;
481 fitterData.firstLastMeasurements.push_back(&hit->broadMeasurement());
482 } else if (currentChIndex == chIndex && currentMeasPhi == measuresPhi) {
483 previousHit = hit;
484 } else {
485 currentChIndex = chIndex;
486 currentMeasPhi = measuresPhi;
487 previousHit = hit;
488 fitterData.firstLastMeasurements.push_back(&hit->broadMeasurement());
489 }
490 }
491
492 // add last hit if not already inserted
493 if (previousHit && &previousHit->broadMeasurement() != fitterData.firstLastMeasurements.back())
494 fitterData.firstLastMeasurements.push_back(&previousHit->broadMeasurement());
495
496 // require at least 6 measurements on a track
497 if (fitterData.measurements.size() < 7) {
498 ATH_MSG_VERBOSE(" Too few measurements, cannot perform fit " << fitterData.measurements.size());
499 return false;
500 }
501
502 // require at least 6 measurements on a track
503 if (fitterData.etaHits.size() < 7) {
504 ATH_MSG_VERBOSE(" Too few eta measurements, cannot perform fit " << fitterData.etaHits.size());
505 return false;
506 }
507
508 ATH_MSG_VERBOSE(" Extracted measurements: total " << fitterData.measurements.size() << " eta "
509 << fitterData.etaHits.size() << " phi " << fitterData.phiHits.size()<< std::endl << m_printer->print(fitterData.measurements));
510 return true;
511 }
512
513 bool MooTrackFitter::addFakePhiHits(const EventContext& ctx, MooTrackFitter::FitterData& fitterData, const Trk::TrackParameters& startpar) const {
514
515 // check whether we have enough phi constraints
516 unsigned nphiConstraints = hasPhiConstrain(fitterData);
517
518 // do we have enough constraints to fit the track
519 if (nphiConstraints >= 2) {
520 if (fitterData.firstEntry->stations().size() == 1 && fitterData.firstEntry->containsStation(StIndex::EI) &&
521 (fitterData.firstEntry->phiHits().empty() || (fitterData.firstEntry->containsChamber(ChIndex::CSS) ||
522 fitterData.firstEntry->containsChamber(ChIndex::CSL)))) {
523 ATH_MSG_VERBOSE( " Special treatment of the forward region: adding fake at ip ");
524 }
525 return true;
526 }
527
528 // in case of a single station overlap fit, calculate a global position that is more or less located at
529 // the overlap. It is used to decide on which side of the tube the fake should be produced
530 std::unique_ptr<Amg::Vector3D> overlapPos;
531 std::unique_ptr<const Amg::Vector3D> phiPos;
532 if (fitterData.numberOfSLOverlaps() > 0 || (fitterData.numberOfSmallChambers() > 0 && fitterData.numberOfLargeChambers() > 0)) {
533 // in case of SL overlaps, pass average position of the two segments
534 // overlapPos = new Amg::Vector3D( 0.5*(fitterData.firstEntry->etaHits().front()->globalPosition() +
535 // fitterData.secondEntry->etaHits().front()->globalPosition()) );
536 overlapPos = std::make_unique<Amg::Vector3D>(1., 1., 1.);
537 double phi = fitterData.avePhi;
538 double theta = fitterData.secondEntry->etaHits().front()->globalPosition().theta();
539 if (m_cosmics) {
540 if (fitterData.firstEntry->hasSLOverlap()) {
541 theta = fitterData.firstEntry->entryPars().momentum().theta();
542 phi = fitterData.firstEntry->entryPars().momentum().phi();
543 } else {
544 theta = fitterData.secondEntry->entryPars().momentum().theta();
545 phi = fitterData.secondEntry->entryPars().momentum().phi();
546 }
547 }
548 Amg::setThetaPhi(*overlapPos, theta, phi);
549
550 if (fitterData.startPars) {
551 phiPos = std::make_unique<Amg::Vector3D>(fitterData.startPars->momentum());
552 } else {
553 phiPos = std::make_unique<Amg::Vector3D>(*overlapPos);
554 }
555
556 if (msgLvl(MSG::VERBOSE)) {
557 if (fitterData.numberOfSLOverlaps() > 0) {
558 if (fitterData.stations.size() == 1)
559 msg(MSG::VERBOSE) << " one station fit with SL overlap, using overlapPos " << *overlapPos << endmsg;
560 else
561 msg(MSG::VERBOSE) << " multi station fit with SL overlap, using overlapPos " << *overlapPos << endmsg;
562 } else if (fitterData.numberOfSmallChambers() > 0 && fitterData.numberOfLargeChambers() > 0) {
563 msg(MSG::VERBOSE) << " multi station fit, SL overlap not in same station, using overlapPos " << *overlapPos << endmsg;
564 } else
565 ATH_MSG_WARNING(" Unknown overlap type ");
566 }
567 }
568 // add fake phi hit on first and last measurement
569 if (fitterData.phiHits.empty()) {
570 const MuPatSegment* segInfo1 = dynamic_cast<const MuPatSegment*>(fitterData.firstEntry);
571 const MuPatSegment* segInfo2 = dynamic_cast<const MuPatSegment*>(fitterData.secondEntry);
572 if (segInfo1 && segInfo2 && fitterData.stations.size() == 1 && fitterData.numberOfSLOverlaps() == 1) {
573 // only perform SL overlap fit for MDT segments
574 Identifier chId = m_edmHelperSvc->chamberId(*segInfo1->segment);
575 if (!m_idHelperSvc->isMdt(chId)) return false;
576
577 ATH_MSG_VERBOSE(" Special treatment for tracks with one station, a SL overlap and no phi hits ");
578
580 m_overlapResolver->matchResult(ctx, *segInfo1->segment, *segInfo2->segment);
581
582 if (!result.goodMatch()) {
583 ATH_MSG_VERBOSE(" Match failed ");
584 return false;
585 }
586 // create a track parameter for the segment
587 double locx1 = result.segmentResult1.positionInTube1;
588 double locy1 =
589 segInfo1->segment->localParameters().contains(Trk::locY) ? segInfo1->segment->localParameters()[Trk::locY] : 0.;
590 Trk::AtaPlane segPars1(locx1, locy1, result.phiResult.segmentDirection1.phi(), result.phiResult.segmentDirection1.theta(),
591 0., segInfo1->segment->associatedSurface());
592 // ownership retained, original code deleted exPars1
593 std::unique_ptr<Trk::TrackParameters> exPars1 = m_propagator->propagate(ctx,
594 segPars1, fitterData.measurements.front()->associatedSurface(), Trk::anyDirection,
595 false, m_magFieldProperties);
596 if (exPars1) {
597 Amg::Vector3D position = exPars1->position();
598 std::unique_ptr<Trk::MeasurementBase> fake =
599 createFakePhiForMeasurement(*fitterData.measurements.front(), &position, nullptr, 10.);
600 if (fake) {
601 fitterData.phiHits.push_back(fake.get());
602 fitterData.measurements.insert(fitterData.measurements.begin(), fake.get());
603 fitterData.firstLastMeasurements.insert(fitterData.firstLastMeasurements.begin(), fake.get());
604 fitterData.garbage.push_back(std::move(fake));
605 }
606 } else {
607 ATH_MSG_WARNING(" failed to create fake for first segment ");
608 return false;
609 }
610 double locx2 = result.segmentResult2.positionInTube1;
611 double locy2 =
612 segInfo2->segment->localParameters().contains(Trk::locY) ? segInfo2->segment->localParameters()[Trk::locY] : 0.;
613 Trk::AtaPlane segPars2(locx2, locy2, result.phiResult.segmentDirection2.phi(), result.phiResult.segmentDirection2.theta(),
614 0., segInfo2->segment->associatedSurface());
615 // ownership retained
616 auto exPars2 = m_propagator->propagate(ctx,
617 segPars2, fitterData.measurements.back()->associatedSurface(), Trk::anyDirection,
618 false, m_magFieldProperties);
619 if (exPars2) {
620 Amg::Vector3D position = exPars2->position();
621 std::unique_ptr<Trk::MeasurementBase> fake =
622 createFakePhiForMeasurement(*fitterData.measurements.back(), &position, nullptr, 10.);
623 if (fake) {
624 fitterData.phiHits.push_back(fake.get());
625 fitterData.measurements.push_back(fake.get());
626 fitterData.firstLastMeasurements.push_back(fake.get());
627 fitterData.garbage.push_back(std::move(fake));
628 }
629 } else {
630 ATH_MSG_WARNING(" failed to create fake for second segment ");
631 return false;
632 }
633 } else if (nphiConstraints == 0 || (fitterData.stations.size() == 1) ||
634 (nphiConstraints == 1 && fitterData.numberOfSLOverlaps() == 0 && fitterData.numberOfSmallChambers() > 0 &&
635 fitterData.numberOfLargeChambers() > 0)) {
636 {
637 std::unique_ptr<Trk::MeasurementBase> fake = createFakePhiForMeasurement(*fitterData.measurements.front(),
638 overlapPos.get(), phiPos.get(), 100.);
639 if (fake) {
640 fitterData.phiHits.push_back(fake.get());
641 fitterData.measurements.insert(fitterData.measurements.begin(), fake.get());
642 fitterData.firstLastMeasurements.insert(fitterData.firstLastMeasurements.begin(), fake.get());
643 fitterData.garbage.push_back(std::move(fake));
644 }
645
646 }
647 {
648 std::unique_ptr<Trk::MeasurementBase> fake = createFakePhiForMeasurement(*fitterData.measurements.back(),
649 overlapPos.get(), phiPos.get(), 100.);
650 if (fake) {
651 fitterData.phiHits.push_back(fake.get());
652 fitterData.measurements.push_back(fake.get());
653 fitterData.firstLastMeasurements.push_back(fake.get());
654 fitterData.garbage.push_back(std::move(fake));
655 }
656 }
657
658 } else if (fitterData.numberOfSLOverlaps() == 1) {
659 // there is one overlap, add the fake in the station without the overlap
660 ATH_MSG_VERBOSE(" Special treatment for tracks with one SL overlap and no phi hits ");
661
662 StIndex overlapStation = StIndex::StUnknown;
663 SLStationMap::iterator it = fitterData.smallLargeChambersPerStation.begin();
664 SLStationMap::iterator it_end = fitterData.smallLargeChambersPerStation.end();
665 for (; it != it_end; ++it) {
666 if (it->second.first && it->second.second) {
667 overlapStation = it->first;
668 break;
669 }
670 }
671 if (overlapStation == StIndex::StUnknown) {
672 ATH_MSG_WARNING(" unexpected condition, unknown station type ");
673 return false;
674 }
675
676 Identifier firstId = m_edmHelperSvc->getIdentifier(*fitterData.measurements.front());
677 if (!firstId.is_valid()) {
678 ATH_MSG_WARNING(" unexpected condition, first measurement has no identifier ");
679 return false;
680 }
681 MuonStationIndex::StIndex firstSt = m_idHelperSvc->stationIndex(firstId);
682 if (overlapStation == firstSt) {
683 ATH_MSG_VERBOSE(" Adding fake in same station as overlap ");
684
685 // create pseudo at end of track
686 std::unique_ptr<Trk::MeasurementBase> fake = createFakePhiForMeasurement(*fitterData.measurements.back(),
687 overlapPos.get(), phiPos.get(), 100.);
688 if (fake) {
689 fitterData.phiHits.push_back(fake.get());
690 fitterData.measurements.push_back(fake.get());
691 fitterData.firstLastMeasurements.push_back(fake.get());
692 fitterData.garbage.push_back(std::move(fake));
693 }
694 } else {
695 ATH_MSG_VERBOSE(" Adding fake in other station as overlap ");
696 // create pseudo at begin of track
697 std::unique_ptr<Trk::MeasurementBase> fake = createFakePhiForMeasurement(*fitterData.measurements.front(),
698 overlapPos.get(), phiPos.get(), 100.);
699 if (fake) {
700 fitterData.phiHits.push_back(fake.get());
701 fitterData.measurements.insert(fitterData.measurements.begin(), fake.get());
702 fitterData.firstLastMeasurements.insert(fitterData.firstLastMeasurements.begin(), fake.get());
703 fitterData.garbage.push_back(std::move(fake));
704 }
705 }
706
707 } else {
708 ATH_MSG_WARNING(" unexpected condition, cannot create fakes ");
709 return false;
710 }
711 } else {
712 // calculate distance between first phi/eta hit and last phi/eta hit
713 double distFirstEtaPhi =
714 (fitterData.measurements.front()->globalPosition() - fitterData.phiHits.front()->globalPosition()).mag();
715 double distLastEtaPhi = (fitterData.measurements.back()->globalPosition() - fitterData.phiHits.back()->globalPosition()).mag();
716
717 // if there is one phi, use its phi + IP constraint to calculate position of fake
718 if (!overlapPos && m_seedWithAvePhi) {
719 phiPos = std::make_unique<Amg::Vector3D>(fitterData.phiHits.back()->globalPosition());
720 ATH_MSG_VERBOSE(" using pointing constraint to calculate fake phi hit ");
721 }
722
723 // create a fake on the first and/or last MDT measurement if not near phi hits
724 const Trk::Surface *firstmdtsurf = nullptr, *lastmdtsurf = nullptr;
725 const Trk::TrackParameters *lastmdtpar = nullptr;
726 int indexfirst = 0, indexlast = (int)fitterData.measurements.size();
727 MuPatHitCit hitit = fitterData.hitList.begin();
728 for (; hitit != fitterData.hitList.end(); hitit++) {
729 if ((**hitit).info().measuresPhi) break;
730 if ((**hitit).info().type == MuPatHit::MDT || (**hitit).info().type == MuPatHit::sTGC) {
731 firstmdtsurf = &(**hitit).measurement().associatedSurface();
732 break;
733 }
734 indexfirst++;
735 }
736 hitit = fitterData.hitList.end();
737 hitit--;
738 for (; hitit != fitterData.hitList.begin(); hitit--) {
739 if ((**hitit).info().measuresPhi) break;
740 if ((**hitit).info().type == MuPatHit::MDT) {
741 lastmdtsurf = &(**hitit).measurement().associatedSurface();
742 lastmdtpar = &(**hitit).parameters();
743 break;
744 }
745 indexlast--;
746 }
749 indexlast = std::max(indexlast, 0);
750 bool phifromextrapolation = false;
751 if (!fitterData.secondEntry->phiHits().empty() || !fitterData.firstEntry->phiHits().empty()) phifromextrapolation = true;
752
753 const Trk::Surface *firstphisurf = nullptr, *lastphisurf = nullptr;
754 Amg::Vector3D firstphinormal{Amg::Vector3D::Zero()}, lastphinormal{Amg::Vector3D::Zero()};
755 if (!fitterData.phiHits.empty()) {
756 firstphisurf = &fitterData.phiHits.front()->associatedSurface();
757 lastphisurf = &fitterData.phiHits.back()->associatedSurface();
758 firstphinormal = firstphisurf->normal();
759 lastphinormal = lastphisurf->normal();
760 if (firstphinormal.dot(startpar.momentum()) < 0) firstphinormal = -firstphinormal;
761 if (lastphinormal.dot(startpar.momentum()) < 0) lastphinormal = -lastphinormal;
762 }
763 if (lastmdtsurf && (!firstphisurf || (lastmdtsurf->center() - lastphisurf->center()).dot(lastphinormal) > 1000)) {
764 ATH_MSG_VERBOSE(" Adding fake at last hit: dist first phi/first eta " << distFirstEtaPhi << " dist last phi/last eta "
765 << distLastEtaPhi);
766 std::unique_ptr<Trk::MeasurementBase> fake{};
767 if (fitterData.secondEntry->hasSLOverlap() || phifromextrapolation) {
768 // this scope manages lifetime of mdtpar
769 std::unique_ptr<Trk::TrackParameters> mdtpar{};
770 if (fitterData.secondEntry->hasSLOverlap()){
771 mdtpar = lastmdtpar->uniqueClone();
772 } else {
773 mdtpar = m_propagator->propagateParameters(ctx, startpar, *lastmdtsurf,
775 }
776 if (mdtpar) {
777 Amg::MatrixX cov(1, 1);
778 cov(0, 0) = 100.;
779 fake = std::make_unique<Trk::PseudoMeasurementOnTrack>(
781 std::move(cov),
782 mdtpar->associatedSurface());
783
784 }
785 } else {
786 fake = createFakePhiForMeasurement(*fitterData.measurements.back(), overlapPos.get(), phiPos.get(), 10.);
787 }
788 if (fake) {
789 fitterData.phiHits.push_back(fake.get());
790 fitterData.measurements.insert(fitterData.measurements.begin() + indexlast, fake.get());
791 fitterData.firstLastMeasurements.push_back(fake.get());
792 fitterData.garbage.push_back(std::move(fake));
793 }
794 }
795
796 if (firstmdtsurf && (!firstphisurf || (firstmdtsurf->center() - firstphisurf->center()).dot(firstphinormal) < -1000)) {
797 ATH_MSG_VERBOSE(" Adding fake at first hit: dist first phi/first eta " << distFirstEtaPhi << " dist last phi/last eta "
798 << distLastEtaPhi);
799 std::unique_ptr<Trk::MeasurementBase> fake{};
800 if (phifromextrapolation) {
801 // lifetime of mdtpar managed here
802 auto mdtpar = m_propagator->propagateParameters(ctx, startpar,
803 *firstmdtsurf, Trk::oppositeMomentum, false, m_magFieldProperties);
804 if (mdtpar) {
805 Amg::MatrixX cov(1, 1);
806 cov(0, 0) = 100.;
807 fake = std::make_unique<Trk::PseudoMeasurementOnTrack>(
809 std::move(cov),
810 mdtpar->associatedSurface());
811 }
812 } else{
813 fake = createFakePhiForMeasurement(*fitterData.measurements.front(), overlapPos.get(), phiPos.get(), 100.);
814 }
815 if (fake) {
816 fitterData.phiHits.insert(fitterData.phiHits.begin(), fake.get());
817 fitterData.measurements.insert(fitterData.measurements.begin() + indexfirst, fake.get());
818 fitterData.firstLastMeasurements.insert(fitterData.firstLastMeasurements.begin(), fake.get());
819 fitterData.garbage.push_back(std::move(fake));
820 }
821 }
822 }
823 return true;
824 }
825
826 std::unique_ptr<Trk::MeasurementBase> MooTrackFitter::createFakePhiForMeasurement(const Trk::MeasurementBase& meas,
827 const Amg::Vector3D* overlapPos, const Amg::Vector3D* phiPos,
828 double errPos) const {
829 // check whether measuring phi
830 Identifier id = m_edmHelperSvc->getIdentifier(meas);
831 if (id == Identifier()) {
832 ATH_MSG_WARNING(" Cannot create fake phi hit from a measurement without Identifier ");
833 return nullptr;
834 }
835 // check whether phi measurement, exit if true
836 if (m_idHelperSvc->measuresPhi(id)) {
837 ATH_MSG_WARNING(" Measurement is a phi measurement! " << m_idHelperSvc->toString(id));
838 return nullptr;
839 }
840
841
842 // the MDT and CSC hits are ROTs
843 const Trk::RIO_OnTrack* rot = dynamic_cast<const Trk::RIO_OnTrack*>(&meas);
844 if (!rot) {
845 const CompetingMuonClustersOnTrack* crot = dynamic_cast<const CompetingMuonClustersOnTrack*>(&meas);
846 // if also to a crot we cannot create a fake phi hit
847 if (!crot || crot->containedROTs().empty()) return nullptr;
848 rot = &crot->rioOnTrack(0);
849 }
850
851
852
853 // get channel length and detector element
854 const Trk::TrkDetElementBase* detEl = rot->detectorElement();
855 if (!detEl) {
856 return nullptr;
857 }
858
859 auto [halfLengthL, halfLengthR] = getElementHalfLengths(id, detEl);
860
861 // error
862 std::optional<Amg::Vector2D> lpos = std::nullopt;
863 if (phiPos) {
864 Amg::Vector3D dir(1., 0., 0.);
865 Amg::setThetaPhi(dir, rot->globalPosition().theta(), phiPos->phi());
866 Amg::Vector3D ip{Amg::Vector3D::Zero()};
867 Trk::Perigee perigee(ip, dir, 0., ip);
868
869 // special treatment of MDTs, intersect with detector element surface instead of wire surface to avoid problem with
870 // shift of phi angle after projection into wire frame
871 const Trk::Surface& measSurf = m_idHelperSvc->isMdt(id) ? detEl->surface() : meas.associatedSurface();
872
873 Trk::Intersection intersect = measSurf.straightLineIntersection(ip, dir);
874 if (!intersect.valid) {
875 ATH_MSG_WARNING(" Intersect with surface failed for measurement "
876 << m_printer->print(meas) << " position " << Amg::toString(ip) << " direction: phi " << dir.phi()
877 << " theta " << dir.theta() << " seed: phi " << phiPos->phi() << " theta "
878 << rot->globalPosition().theta());
879 } else {
880 // now get central phi position on surface of segment
881 lpos = meas.associatedSurface().globalToLocal(intersect.position, 3000.);
882
883 }
884 } else {
885 // now get central phi position on surface of segment
886 lpos = meas.associatedSurface().globalToLocal(detEl->center(), 3000.);
887 if (!lpos){
888 lpos = meas.associatedSurface().globalToLocal(meas.associatedSurface().center(), 3000.);
889 }
890
891 }
892
893 if (!lpos) {
894 ATH_MSG_WARNING(" No local position, cannot make fake phi hit "<<m_idHelperSvc->toString(id)<<" "<<typeid(meas).name());
895 return nullptr;
896 }
897
898 double ly = (*lpos)[Trk::locY];
899 double halfLength = ly < 0 ? halfLengthL : halfLengthR;
900 bool shiftedPos = false;
901
902 if (std::abs(ly) > halfLength) {
903 double lyold = ly;
904 ly = ly < 0 ? -halfLength : halfLength;
905 ATH_MSG_DEBUG(" extrapolated position outside detector, shifting it back: " << lyold << " size " << halfLength << " new pos "
906 << ly);
907 if (phiPos && std::abs(lyold) - halfLength > 1000.) {
908 ATH_MSG_DEBUG(" rejecting track ");
909 return nullptr;
910 }
911 shiftedPos = true;
912 }
913
914 ATH_MSG_VERBOSE("Creating fake measurement using measurement " << m_printer->print(meas));
915
916 const Trk::Surface& surf = meas.associatedSurface();
917
918 // no overlap, put fake phi hit at center of the chamber
919 if (overlapPos) {
920 // overlap, fake hit at 100. mm from the edge, error 100./sqrt(12.)
921
922 // transform estimate of overlap position into surface frame
923 std::optional<Amg::Vector2D> loverlapPos = surf.globalToLocal(*overlapPos, 3000.);
924 if (!loverlapPos) {
925 ATH_MSG_DEBUG(" globalToLocal failed for overlap position: " << surf.transform() * (*overlapPos));
926 // calculate position fake
927 double lyfake = halfLength - 50.;
928 if (ly < 0.) lyfake *= -1.;
929 ly = lyfake;
930 errPos = FiftyOverSqrt12;
931
932 if (msgLvl(MSG::VERBOSE)){
933 Amg::Vector2D LocVec2D_plus(0., lyfake);
934 const Amg::Vector3D fakePos_plus = meas.associatedSurface().localToGlobal(LocVec2D_plus);
935 Amg::Vector2D LocVec2D_min(0., -lyfake);
936 const Amg::Vector3D fakePos_min = meas.associatedSurface().localToGlobal(LocVec2D_min);
937
938 double phi_min = fakePos_min.phi();
939 double phi_plus = fakePos_plus.phi();
940 double phi_overlap = phiPos->phi();
941 ATH_MSG_VERBOSE(" fake lpos " << lyfake << " ch half length " << halfLength << " phi+ " << phi_plus << " phi- " << phi_min
942 << " phi overlap " << phi_overlap << " err " << errPos);
943 }
944
945 } else {
946 ly = (*loverlapPos)[Trk::locY];
947 halfLength = ly < 0 ? halfLengthL : halfLengthR; // safer to redo since ly changed
948 if (std::abs(ly) > halfLength) { ly = ly < 0 ? -halfLength : halfLength; }
949 ATH_MSG_VERBOSE(" fake from overlap: lpos " << ly << " ch half length " << halfLength << " overlapPos " << *overlapPos);
950 }
951 }
952
954 // Error matrix
955 Amg::MatrixX cov(1, 1);
956 cov(0, 0) = errPos * errPos;
957 std::unique_ptr<Trk::PseudoMeasurementOnTrack> fake = std::make_unique<Trk::PseudoMeasurementOnTrack>(std::move(locPars),
958 std::move(cov), surf);
959
960 if (msgLvl(MSG::DEBUG)) {
961 Amg::Vector2D LocVec2D(0., fake->localParameters().get(Trk::locY));
962 const Amg::Vector3D fakePos = meas.associatedSurface().localToGlobal(LocVec2D);
963
964 msg(MSG::DEBUG) << MSG::DEBUG << " createFakePhiForMeasurement for: " << m_idHelperSvc->toStringChamber(id) << " locY " << ly
965 << " errpr " << errPos << " phi " << fakePos.phi() << endmsg;
966
967 if (!shiftedPos && !overlapPos && phiPos && std::abs(phiPos->phi() - fakePos.phi()) > 0.01) {
968 Amg::Transform3D gToLocal = meas.associatedSurface().transform().inverse();
969 Amg::Vector3D locMeas = gToLocal * fake->globalPosition();
970 ATH_MSG_WARNING(" Problem calculating fake from IP seed: phi fake " << fakePos.phi() << " IP phi " << phiPos->phi()
971 << " local meas pos " << locMeas);
972 }
973 }
974 return fake;
975 }
976
978 // check distance between first and last hit to determine whether we need additional phi constrainst
979
980 if ((fitterData.firstEntry->containsChamber(ChIndex::CSS) ||
981 fitterData.firstEntry->containsChamber(ChIndex::CSL)) &&
982 !fitterData.secondEntry->phiHits().empty())
983 return 2;
984
985 unsigned int nphiConstraints = fitterData.phiHits.size();
986 // count SL overlap as one phi constraint
987 // if( fitterData.numberOfSLOverlaps() > 0 ) {
988 // nphiConstraints += fitterData.numberOfSLOverlaps();
989 // ATH_MSG_VERBOSE(" track has small/large overlaps " << fitterData.numberOfSLOverlaps() );
990 // }else if( (fitterData.numberOfSmallChambers() > 0 && fitterData.numberOfLargeChambers() > 0 ) ){
991 // nphiConstraints += 1;
992 // ATH_MSG_VERBOSE(" track has small and large chambers " );
993 // }
994
995 double distanceMin = 400.;
996 double distance = 0.;
997 // we need at least two phis
998 if (fitterData.phiHits.size() > 1) {
999 // assume the hits to be sorted
1000 const Amg::Vector3D& gposFirstPhi = fitterData.phiHits.front()->globalPosition();
1001 const Amg::Vector3D& gposLastPhi = fitterData.phiHits.back()->globalPosition();
1002 double distFirstEtaPhi =
1003 (fitterData.measurements.front()->globalPosition() - fitterData.phiHits.front()->globalPosition()).mag();
1004 double distLastEtaPhi = (fitterData.measurements.back()->globalPosition() - fitterData.phiHits.back()->globalPosition()).mag();
1005
1006 // calculate difference between hits
1007 Amg::Vector3D globalDistance = gposLastPhi - gposFirstPhi;
1008
1009 // calculate 'projective' distance
1010 distance = fitterData.hasEndcap ? std::abs(globalDistance.z()) : globalDistance.perp();
1011
1012 // if the distance between the first and last phi hit is smaller than 1000. count as 1 phi hit
1013 if (distance < distanceMin || distFirstEtaPhi > 1000 || distLastEtaPhi > 1000) {
1014 nphiConstraints -= fitterData.phiHits.size(); // subtract phi hits as they should only count as one phi hit
1015 nphiConstraints += 1; // add one phi hit
1016 ATH_MSG_VERBOSE(" distance between phi hits too small, updating phi constraints ");
1017 } else {
1018 ATH_MSG_VERBOSE(" distance between phi hits sufficient, no fake hits needed ");
1019 }
1020 }
1021 if (msgLvl(MSG::DEBUG)) {
1022 msg(MSG::DEBUG) << MSG::DEBUG << " Check phi: " << std::endl
1023 << " | nphi hits | distance | SL station overlaps | small ch | large ch | nphiConstraints " << std::endl
1024 << std::setw(12) << fitterData.phiHits.size() << std::setw(11) << (int)distance << std::setw(22)
1025 << fitterData.numberOfSLOverlaps() << std::setw(11) << fitterData.numberOfSmallChambers() << std::setw(11)
1026 << fitterData.numberOfLargeChambers() << std::setw(18) << nphiConstraints << endmsg;
1027 }
1028
1029 return nphiConstraints;
1030 }
1031
1032 unsigned int MooTrackFitter::hasPhiConstrain(Trk::Track* track) const {
1033 std::map<MuonStationIndex::StIndex, StationPhiData> stationDataMap;
1034
1035 // Get a MuonTrackSummary.
1036 const Trk::TrackSummary* summary = track->trackSummary();
1037 Trk::MuonTrackSummary muonSummary;
1038 if (summary) {
1039 if (summary->muonTrackSummary()) {
1040 muonSummary = *summary->muonTrackSummary();
1041 } else {
1042 Trk::TrackSummary tmpSum(*summary);
1043 m_trackSummaryTool->addDetailedTrackSummary(*track, tmpSum);
1044 if (tmpSum.muonTrackSummary()) muonSummary = *(tmpSum.muonTrackSummary());
1045 }
1046 } else {
1047 Trk::TrackSummary tmpSummary;
1048 m_trackSummaryTool->addDetailedTrackSummary(*track, tmpSummary);
1049 if (tmpSummary.muonTrackSummary()) muonSummary = *(tmpSummary.muonTrackSummary());
1050 }
1051
1052 std::vector<Trk::MuonTrackSummary::ChamberHitSummary>::const_iterator chit = muonSummary.chamberHitSummary().begin();
1053 std::vector<Trk::MuonTrackSummary::ChamberHitSummary>::const_iterator chit_end = muonSummary.chamberHitSummary().end();
1054 for (; chit != chit_end; ++chit) {
1055 // get station data
1056 const Identifier& chId = chit->chamberId();
1057 MuonStationIndex::StIndex stIndex = m_idHelperSvc->stationIndex(chId);
1058 StationPhiData& stData = stationDataMap[stIndex];
1059 if (chit->isMdt()) {
1060 if (m_idHelperSvc->isSmallChamber(chId))
1061 ++stData.nSmallChambers;
1062 else
1063 ++stData.nLargeChambers;
1064 } else {
1065 if (chit->phiProjection().nhits) ++stData.nphiHits;
1066 }
1067 }
1068
1069 unsigned int phiConstraints = 0;
1070 unsigned int stationsWithSmall = 0;
1071 unsigned int stationsWithLarge = 0;
1072
1073 std::map<MuonStationIndex::StIndex, StationPhiData>::iterator sit = stationDataMap.begin();
1074 std::map<MuonStationIndex::StIndex, StationPhiData>::iterator sit_end = stationDataMap.end();
1075 for (; sit != sit_end; ++sit) {
1076 StationPhiData& stData = sit->second;
1077
1078 // count all stations with phi hits as phi constraint
1079 phiConstraints += stData.nphiHits;
1080
1081 if (stData.nSmallChambers > 0 && stData.nLargeChambers > 0)
1082 ++phiConstraints;
1083 else if (stData.nSmallChambers > 0)
1084 ++stationsWithSmall;
1085 else if (stData.nLargeChambers > 0)
1086 ++stationsWithLarge;
1087 }
1088
1089 if (stationsWithSmall > 0 && stationsWithLarge > 0) { ++phiConstraints; }
1090
1091 return phiConstraints;
1092 }
1093 std::pair<double, double> MooTrackFitter::getElementHalfLengths(const Identifier& id, const Trk::TrkDetElementBase* ele ) const{
1094 if (m_idHelperSvc->isMdt(id)) {
1095 const MuonGM::MdtReadoutElement* mdtDetEl = dynamic_cast<const MuonGM::MdtReadoutElement*>(ele);
1096 if (!mdtDetEl){ return std::make_pair(0., 0.); }
1097 const double halfLength = 0.5 * mdtDetEl->getActiveTubeLength(m_idHelperSvc->mdtIdHelper().tubeLayer(id),
1098 m_idHelperSvc->mdtIdHelper().tube(id));
1099 return std::make_pair(halfLength, halfLength);
1100
1101 } else if (m_idHelperSvc->isCsc(id)) {
1102 const MuonGM::CscReadoutElement* cscDetEl = dynamic_cast<const MuonGM::CscReadoutElement*>(ele);
1103 if (!cscDetEl) { return std::make_pair(0., 0.);}
1104 const double halfLenghth = 0.5 * cscDetEl->stripLength(id);
1105 return std::make_pair(halfLenghth, halfLenghth);
1106 } else if (m_idHelperSvc->isRpc(id)) {
1107 const MuonGM::RpcReadoutElement* rpcDetEl = dynamic_cast<const MuonGM::RpcReadoutElement*>(ele);
1108 if (!rpcDetEl) { return std::make_pair(0, 0);}
1109 const double halfLength = 0.5 * rpcDetEl->StripLength(false); // eta-strip
1110 return std::make_pair(halfLength, halfLength);
1111 } else if (m_idHelperSvc->isTgc(id)) {
1112 const MuonGM::TgcReadoutElement* tgcDetEl = dynamic_cast<const MuonGM::TgcReadoutElement*>(ele);
1113 if (!tgcDetEl) {return std::make_pair(0.,0.);}
1114 const double halfLength = 0.5 * tgcDetEl->gangCentralWidth(m_idHelperSvc->tgcIdHelper().gasGap(id), m_idHelperSvc->tgcIdHelper().channel(id));
1115 return std::make_pair(halfLength, halfLength);
1116 } else if (m_idHelperSvc->issTgc(id)) {
1117 const MuonGM::sTgcReadoutElement* stgcDetEl = dynamic_cast<const MuonGM::sTgcReadoutElement*>(ele);
1118 if (!stgcDetEl || !stgcDetEl->getDesign(id)) {return std::make_pair(0.,0.);}
1119 const double halfLength = 0.5 * stgcDetEl->getDesign(id)->channelLength(m_idHelperSvc->stgcIdHelper().channel(id));
1120 return std::make_pair(halfLength, halfLength);
1121
1122 } else if (m_idHelperSvc->isMM(id)) {
1123 const MuonGM::MMReadoutElement* mmDetEl = dynamic_cast<const MuonGM::MMReadoutElement*>(ele);
1124 if (mmDetEl) {
1125 return std::make_pair(mmDetEl->stripActiveLengthLeft(id) ,
1126 mmDetEl->stripActiveLengthRight(id)); // components along the local Y axis
1127 }
1128 }
1129 return std::make_pair(0.,0.);
1130 }
1132 double phiStart = fitterData.etaHits.front()->globalPosition().phi();
1133 double phiOffset = 0.;
1134 double phiRange = 0.75 * M_PI;
1135 double phiRange2 = 0.25 * M_PI;
1136 if (phiStart > phiRange || phiStart < -phiRange)
1137 phiOffset = 2 * M_PI;
1138 else if (phiStart > -phiRange2 && phiStart < phiRange2)
1139 phiOffset = M_PI;
1140
1141 double phiMin = -999.;
1142 double phiMax = 999.;
1143
1144 for (const Trk::MeasurementBase* meas :fitterData.etaHits) {
1145
1146 const Trk::RIO_OnTrack* rot = dynamic_cast<const Trk::RIO_OnTrack*>(meas);
1147 if (!rot) {
1148 const CompetingMuonClustersOnTrack* crot = dynamic_cast<const CompetingMuonClustersOnTrack*>(meas);
1149 // if also to a crot we cannot create a fake phi hit
1150 if (!crot || crot->containedROTs().empty()) continue;
1151 rot = crot->containedROTs().front();
1152 }
1153
1154 if (!rot) {
1155 ATH_MSG_WARNING(" Measurement not a ROT ");
1156 continue;
1157 }
1158
1159 Identifier id = rot->identify();
1160 const Trk::Surface& surf = meas->associatedSurface();
1161 double x = rot->localParameters()[Trk::locX];
1162
1163 // distinguishing left-right just to treat the asymmetry of the active region in MM chambers
1164 // getElementHalfLengths
1165 auto [halfLengthL, halfLengthR] = getElementHalfLengths(id, rot->detectorElement());
1166
1167 Amg::Vector2D lpLeft(x, -halfLengthL);
1168 const Amg::Vector3D gposLeft = surf.localToGlobal(lpLeft);
1169 double phiLeft = gposLeft.phi();
1170
1171 Amg::Vector2D lpRight(x, halfLengthR);
1172 const Amg::Vector3D gposRight = surf.localToGlobal(lpRight);
1173 double phiRight = gposRight.phi();
1174
1175 if (phiOffset > 1.5 * M_PI) {
1176 if (phiLeft < 0) phiLeft = phiOffset + phiLeft;
1177 if (phiRight < 0) phiRight = phiOffset + phiRight;
1178 } else if (phiOffset > 0.) {
1179 phiLeft = phiLeft + phiOffset;
1180 phiRight = phiRight + phiOffset;
1181 }
1182
1183 bool leftSmaller = phiLeft < phiRight;
1184
1185 double phiMinMeas = leftSmaller ? phiLeft : phiRight;
1186 double phiMaxMeas = leftSmaller ? phiRight : phiLeft;
1187 double orgPhiMin = phiMinMeas;
1188 double orgPhiMax = phiMaxMeas;
1189
1190 if (phiOffset > 1.5 * M_PI) {
1191 if (orgPhiMin > M_PI) orgPhiMin = orgPhiMin - phiOffset;
1192 if (orgPhiMax > M_PI) orgPhiMax = orgPhiMax - phiOffset;
1193 } else if (phiOffset > 0.) {
1194 orgPhiMin = orgPhiMin - phiOffset;
1195 orgPhiMax = orgPhiMax - phiOffset;
1196 }
1197
1198 if (phiMinMeas > phiMin) { phiMin = phiMinMeas; }
1199 if (phiMaxMeas < phiMax) { phiMax = phiMaxMeas; }
1200 }
1201 if (phiMin < -998 || phiMax > 998) {
1202 ATH_MSG_WARNING(" Could not determine minimum and maximum phi ");
1203 return false;
1204 }
1205
1206 double diffPhi = phiMax - phiMin;
1207 double avePhi = phiMin + 0.5 * diffPhi;
1208
1209 ATH_MSG_VERBOSE("Phi ranges: min " << phiMin << " max " << phiMax << " average phi " << avePhi);
1210
1211 if (diffPhi < 0) {
1212 if (std::abs(diffPhi) > m_openingAngleCut) {
1213 ATH_MSG_VERBOSE(" Inconsistent min/max, rejecting track ");
1214 return false;
1215 }
1216 }
1217
1218 if (!fitterData.phiHits.empty()) {
1219 for (const Trk::MeasurementBase* meas : fitterData.phiHits) {
1220 const double minPhi = std::min(phiMin, phiMax);
1221 const double maxPhi = std::max(phiMin, phiMax);
1222 double phiMeas = meas->globalPosition().phi();
1223 if (phiOffset > 1.5 * M_PI) {
1224 if (phiMeas < 0.) phiMeas = phiOffset + phiMeas;
1225 } else if (phiOffset > 0.) {
1226 phiMeas = phiMeas + phiOffset;
1227 }
1228 double diffMin = phiMeas - minPhi;
1229 double diffMax = phiMeas - maxPhi;
1230 if (diffMin < 0. || diffMax > 0.) {
1231 if (diffMin < -m_openingAngleCut || diffMax > m_openingAngleCut) {
1232 ATH_MSG_VERBOSE(" Phi hits inconsistent with min/max, rejecting track: phi meas " << phiMeas);
1233 return false;
1234 }
1235 }
1236 }
1237 }
1238
1239 if (phiOffset > 1.5 * M_PI) {
1240 if (phiMax > M_PI) phiMax = phiMax - phiOffset;
1241 if (phiMin > M_PI) phiMin = phiMin - phiOffset;
1242 if (avePhi > M_PI) avePhi = avePhi - phiOffset;
1243 } else if (phiOffset > 0.) {
1244 phiMax = phiMax - phiOffset;
1245 phiMin = phiMin - phiOffset;
1246 avePhi = avePhi - phiOffset;
1247 }
1248
1249 fitterData.avePhi = avePhi;
1250 fitterData.phiMin = phiMin;
1251 fitterData.phiMax = phiMax;
1252
1253 return true;
1254 }
1255
1256 std::shared_ptr<const MuonSegment> MooTrackFitter::segmentFromEntry(const EventContext& ctx, const MuPatCandidateBase& entry) const {
1257 // if track entry use first segment
1258 const MuPatTrack* trkEntry = dynamic_cast<const MuPatTrack*>(&entry);
1259 if (trkEntry) {
1260 std::shared_ptr<MuonSegment> seg{m_trackToSegmentTool->convert(ctx, trkEntry->track())};
1261 return seg;
1262 }
1263
1264 // if segment entry use segment directly
1265 const MuPatSegment* segEntry = dynamic_cast<const MuPatSegment*>(&entry);
1266 if (segEntry) {
1267 return std::shared_ptr<const MuonSegment> {segEntry->segment, Unowned()};
1268 }
1269
1270 return nullptr;
1271 }
1272
1274 return entry.entryPars().charge() / entry.entryPars().momentum().mag();
1275 }
1276
1277 double MooTrackFitter::qOverPFromEntries(const EventContext& ctx, const MuPatCandidateBase& firstEntry, const MuPatCandidateBase& secondEntry) const {
1278 if (m_slFit) return 0;
1279
1280 double qOverP = 0;
1281 // if available take momentum from entries
1282 if (firstEntry.hasMomentum())
1283 return qOverPFromEntry(firstEntry);
1284 else if (secondEntry.hasMomentum())
1285 return qOverPFromEntry(secondEntry);
1286
1287 // no momentum yet, estimate from segments
1288 std::shared_ptr<const MuonSegment> segFirst = segmentFromEntry(ctx, firstEntry);
1289 if (!segFirst) {
1290 ATH_MSG_WARNING(" failed to get segment for first entry, this should not happen ");
1291 return qOverP;
1292 }
1293
1294 std::shared_ptr<const MuonSegment> segSecond = segmentFromEntry(ctx, secondEntry);
1295 if (!segSecond) {
1296 ATH_MSG_WARNING(" failed to get segment for second entry, this should not happen ");
1297 return qOverP;
1298 }
1299
1300 std::vector<const MuonSegment*> segments{segFirst.get(), segSecond.get()};
1301
1302 double momentum{1.};
1303 m_momentumEstimator->fitMomentumVectorSegments(ctx, segments, momentum);
1304 // momentum = restrictedMomentum(momentum);
1305
1306 if (momentum == 0.) return 0.;
1307
1308 qOverP = 1. / momentum;
1309 return qOverP;
1310 }
1311
1312 double MooTrackFitter::phiSeeding(const EventContext& ctx, MooTrackFitter::FitterData& fitterData) const {
1313 if (m_cosmics) {
1314 // check whether the first entry is a track, if not consider the second entry for phi seeding
1315 if (!dynamic_cast<const MuPatTrack*>(fitterData.firstEntry)) {
1316 // if second entry is a track, use its phi value
1317 if (dynamic_cast<const MuPatTrack*>(fitterData.secondEntry)) {
1318 ATH_MSG_DEBUG("Using phi of second entry " << fitterData.secondEntry->entryPars().momentum().phi());
1319 return fitterData.secondEntry->entryPars().momentum().phi();
1320 }
1321 }
1322
1323 // add fake phi hit on first and last measurement
1324 const MuPatSegment* segInfo1 = dynamic_cast<const MuPatSegment*>(fitterData.firstEntry);
1325 const MuPatSegment* segInfo2 = dynamic_cast<const MuPatSegment*>(fitterData.secondEntry);
1326 if (segInfo1 && segInfo2 && fitterData.stations.size() == 1 && fitterData.numberOfSLOverlaps() == 1) {
1327 // only perform SL overlap fit for MDT segments
1328 Identifier chId = m_edmHelperSvc->chamberId(*segInfo1->segment);
1329 if (m_idHelperSvc->isMdt(chId)) {
1331 m_overlapResolver->matchResult(ctx, *segInfo1->segment, *segInfo2->segment);
1332
1333 if (!result.goodMatch()) {
1334 ATH_MSG_VERBOSE(" Match failed ");
1335 return false;
1336 }
1337 double overlapPhi = result.phiResult.segmentDirection1.phi();
1338 ATH_MSG_DEBUG(" using overlap phi " << overlapPhi);
1339 return overlapPhi;
1340 }
1341 }
1342
1343 // calculate the difference between the segment positions to estimate the direction of the muon
1344 Amg::Vector3D difPos = fitterData.firstEntry->entryPars().position() - fitterData.secondEntry->entryPars().position();
1345 // check whether the new direction is pointing down
1346 if (difPos.y() > 0) difPos *= -1.;
1347
1348 // calculate difference in angle between phi from segment and the phi of the two new segment positions
1349 const double dphi = fitterData.firstEntry->entryPars().momentum().deltaPhi(difPos);
1350 if (std::abs(dphi) > 0.2) {
1351 ATH_MSG_DEBUG("Large diff between phi of segment direction and of position "
1352 << fitterData.firstEntry->entryPars().momentum().phi() << " from pos " << difPos.phi() << " dphi " << dphi);
1353 return difPos.phi();
1354 }
1355
1356 ATH_MSG_DEBUG("Using phi of first entry " << fitterData.firstEntry->entryPars().momentum().phi() << " phi from position "
1357 << dphi);
1358
1359 return fitterData.firstEntry->entryPars().momentum().phi();
1360 }
1361
1362 double phi(0.);
1363 // update parameters if seeding purely with positions
1365 // use eta of vector connecting first/last hit
1366 MooTrackFitter::MeasVec& etaHits = fitterData.etaHits;
1367 Amg::Vector3D difPos = etaHits.back()->globalPosition() - etaHits.front()->globalPosition();
1368 if (difPos.mag() > 3000) {
1369 ATH_MSG_DEBUG("Seeding phi using average phi of eta hits ");
1370 phi = difPos.phi();
1371 return phi;
1372 }
1373 }
1374
1375 // should have at least one phi hit
1376 MooTrackFitter::MeasVec& phiHits = fitterData.phiHits;
1377
1378 if (phiHits.empty()) {
1379 ATH_MSG_DEBUG("No phi hits, using average phi ");
1380 // use average phi value to seed fit
1381 return fitterData.avePhi;
1382 }
1383
1384 // in this case can't do much...
1385 if (phiHits.size() == 1) { return phiHits.front()->globalPosition().phi(); }
1386
1387 // in endcap, if first segment in EI use its direction
1388 const MuPatSegment* segInfo1 = dynamic_cast<const MuPatSegment*>(fitterData.firstEntry);
1389 if (segInfo1 && segInfo1->containsStation(StIndex::EI) && !segInfo1->phiHits().empty()) {
1390 return segInfo1->segment->globalDirection().phi();
1391 }
1392
1393 // in endcap, if first segment in EM use its direction
1394 if (segInfo1 && segInfo1->containsStation(StIndex::EM) && !segInfo1->phiHits().empty()) {
1395 return segInfo1->segment->globalDirection().phi();
1396 }
1397
1398 if (m_seedWithAvePhi) {
1399 // use average phi value to seed fit
1400 MeasCit hit = phiHits.begin();
1401 MeasCit hit_end = phiHits.end();
1402 Amg::Vector3D avePos(0., 0., 0.);
1403 for (; hit != hit_end; ++hit) avePos += (*hit)->globalPosition();
1404 avePos /= phiHits.size();
1405 phi = avePos.phi();
1406 } else {
1407 // use phi of vector connecting first/last hit
1408 Amg::Vector3D difPos = phiHits.back()->globalPosition() - phiHits.front()->globalPosition();
1409 phi = difPos.phi();
1410 }
1411
1412 return phi;
1413 }
1414
1416 // should have at least one eta hit
1417 if (etaHits.empty()) {
1418 ATH_MSG_WARNING(" cannot calculate eta seed from empty vector ");
1419 return 0;
1420 }
1421
1422 double theta(0.);
1424 theta = entry.entryPars().momentum().theta();
1425 } else {
1426 // in this case can't do much...
1427 if (etaHits.size() == 1) {
1428 theta = etaHits.front()->globalPosition().theta();
1429 } else {
1430 // use eta of vector connecting first/last hit
1431 Amg::Vector3D difPos = etaHits.back()->globalPosition() - etaHits.front()->globalPosition();
1432 theta = difPos.theta();
1433 }
1434 }
1435
1436 return theta;
1437 }
1438
1439 void MooTrackFitter::createStartParameters(const EventContext& ctx, MooTrackFitter::FitterData& fitterData) const {
1440 // get momentum + charge from entry if available, else use MuonSegmentMomentum to estimate the momentum
1441 double qOverP = qOverPFromEntries(ctx, *fitterData.firstEntry, *fitterData.secondEntry);
1442 std::unique_ptr<Trk::Perigee> startPars{nullptr};
1443 const MuPatCandidateBase *entry1 = fitterData.firstEntry, *entry2 = fitterData.secondEntry;
1444
1445 const MuPatTrack* trkEntry1 = dynamic_cast<const MuPatTrack*>(entry1);
1446 const MuPatTrack* trkEntry2 = dynamic_cast<const MuPatTrack*>(entry2);
1447 const MuPatSegment* seg1 = dynamic_cast<const MuPatSegment*>(entry1);
1448 const MuPatSegment* seg2 = dynamic_cast<const MuPatSegment*>(entry2);
1449
1450 Amg::Vector3D dir1{Amg::Vector3D::Zero()}, dir2{Amg::Vector3D::Zero()};
1451 Amg::Vector3D point1{Amg::Vector3D::Zero()}, point2{Amg::Vector3D::Zero()};
1452 if (seg1) {
1453 dir1 = seg1->segment->globalDirection();
1454 point1 = seg1->segment->globalPosition();
1455 } else {
1456 if (trkEntry1->track().perigeeParameters()) {
1457 dir1 = trkEntry1->track().perigeeParameters()->momentum().unit();
1458 point1 = trkEntry1->track().perigeeParameters()->position();
1459 } else {
1460 return;
1461 }
1462 }
1463 if (seg2) {
1464 dir2 = seg2->segment->globalDirection();
1465 point2 = seg2->segment->globalPosition();
1466 } else {
1467 if (trkEntry2->track().perigeeParameters()) {
1468 dir2 = trkEntry2->track().perigeeParameters()->momentum().unit();
1469 point2 = trkEntry2->track().perigeeParameters()->position();
1470 } else {
1471 return;
1472 }
1473 }
1474 if (dir1.dot(point2 - point1) < 0) {
1475 std::swap(point1, point2);
1476 std::swap(dir1, dir2);
1477 entry1 = fitterData.secondEntry;
1478 entry2 = fitterData.firstEntry;
1479 trkEntry1 = dynamic_cast<const MuPatTrack*>(entry1);
1480 trkEntry2 = dynamic_cast<const MuPatTrack*>(entry2);
1481 seg1 = dynamic_cast<const MuPatSegment*>(entry1);
1482 seg2 = dynamic_cast<const MuPatSegment*>(entry2);
1483 }
1484
1485 const MuPatCandidateBase* bestentry = entry1;
1486 double dist1 = -1, dist2 = -1;
1487 MuPatHitPtr firstphi1{nullptr}, lastphi1{nullptr}, firstphi2{nullptr}, lastphi2{nullptr};
1488
1489 for (const MuPatHitPtr& hit : entry1->hitList()) {
1490 if (hit->info().type != MuPatHit::Pseudo && hit->info().measuresPhi) {
1491 if (!firstphi1) firstphi1 = hit;
1492 lastphi1 = hit;
1493 }
1494 }
1495 for (const MuPatHitPtr& hit : entry2->hitList()) {
1496 if (hit->info().type != MuPatHit::Pseudo && hit->info().measuresPhi) {
1497 if (!firstphi2) firstphi2 = hit;
1498 lastphi2 = hit;
1499 }
1500 }
1501 if (firstphi1) dist1 = std::abs((firstphi1->measurement().globalPosition() - lastphi1->measurement().globalPosition()).dot(dir1));
1502 if (firstphi2) dist2 = std::abs((firstphi2->measurement().globalPosition() - lastphi2->measurement().globalPosition()).dot(dir2));
1503 if (dist2 > dist1) { bestentry = entry2; }
1504 const MuPatTrack* besttrkEntry = dynamic_cast<const MuPatTrack*>(bestentry);
1505 const MuPatSegment* bestseg = dynamic_cast<const MuPatSegment*>(bestentry);
1506 if (besttrkEntry) {
1507 const Trk::TrackParameters* mdtpar = nullptr;
1509 parit != besttrkEntry->track().trackParameters()->end(); ++parit) {
1510 mdtpar = *parit;
1511 if (mdtpar) break;
1512 }
1513 if (!mdtpar) {
1514 ATH_MSG_WARNING("Failed to find valid Trackparameters on track ");
1515 return;
1516 }
1517 Amg::VectorX newpar = mdtpar->parameters(); // besttrkEntry->track().perigeeParameters()->parameters();
1518 newpar[Trk::qOverP] = qOverP;
1519 Trk::PerigeeSurface persurf(mdtpar->position());
1520 startPars = std::make_unique<Trk::Perigee>(0, 0, newpar[Trk::phi], newpar[Trk::theta], qOverP,
1521 persurf); // besttrkEntry->track().perigeeParameters()->cloneToNew(newpar);
1522 }
1523
1524 else {
1525 Trk::PerigeeSurface persurf(bestseg->segment->globalPosition());
1526 double phi = bestseg->segment->globalDirection().phi();
1527 if ((fitterData.firstEntry->containsChamber(ChIndex::CSS) ||
1528 fitterData.firstEntry->containsChamber(ChIndex::CSL)) &&
1529 fitterData.secondEntry->hasSLOverlap())
1530 phi = (fitterData.hitList.back()->parameters().position() - bestseg->segment->globalPosition()).phi();
1531
1532 double theta = bestseg->segment->globalDirection().theta();
1533 // create start parameter
1534 startPars = std::make_unique<Trk::Perigee>(0, 0, phi, theta, qOverP, persurf);
1535
1536 }
1537 if (!startPars) {
1538 ATH_MSG_DEBUG(" failed to create perigee ");
1539 return;
1540 }
1541 fitterData.startPars = std::move(startPars);
1542 }
1543
1544 std::unique_ptr<Trk::Perigee> MooTrackFitter::createPerigee(const EventContext& ctx, const Trk::TrackParameters& firstPars, const Trk::MeasurementBase& firstMeas) const {
1545 // const Amg::Vector3D& firstPos = firstMeas.globalPosition();
1546
1547 Amg::Vector3D perpos{Amg::Vector3D::Zero()};
1548 // propagate segment parameters to first measurement
1549 const Trk::TrackParameters* exPars = &firstPars;
1550 std::unique_ptr<Trk::TrackParameters> garbage;
1551 double shift = 1.;
1553 // not sure whats going on with ownership here, so let this code take care of it
1554 garbage =
1555 m_propagator->propagate(ctx, firstPars, firstMeas.associatedSurface(), Trk::anyDirection, false, m_magFieldProperties);
1556 if (!exPars) {
1557 ATH_MSG_DEBUG(" Propagation failed in createPerigee!! ");
1558 return nullptr;
1559 }
1560 exPars = garbage.get();
1561 shift = 100.;
1562 }
1563
1564 // small shift towards the ip
1565 double sign = exPars->position().dot(exPars->momentum()) > 0 ? 1. : -1.;
1566 perpos = exPars->position() - shift * sign * exPars->momentum().unit();
1567
1568 // create perigee
1569 double phi = exPars->momentum().phi();
1570 double theta = exPars->momentum().theta();
1571 double qoverp = exPars->charge() / exPars->momentum().mag();
1572 if (m_slFit)
1573 qoverp = 0;
1574 else {
1575 if (!validMomentum(*exPars)) {
1576 return nullptr;
1577 }
1578 }
1579
1580 Trk::PerigeeSurface persurf(perpos);
1581 std::unique_ptr<Trk::Perigee> perigee = std::make_unique<Trk::Perigee>(0, 0, phi, theta, qoverp, persurf);
1582
1583 ATH_MSG_DEBUG( std::setprecision(5) << " creating perigee: phi " << phi << " theta " << theta << " q*mom "
1584 << perigee->charge() * perigee->momentum().mag() << " r " << perigee->position().perp() << " z "
1585 << perigee->position().z() << " input q*mom " << firstPars.charge() * firstPars.momentum().mag());
1586 return perigee;
1587 }
1588
1589 double MooTrackFitter::restrictedMomentum(double momentum) {
1590 // restrict range of momenta to 2 GeV / 10 GeV
1591 if (momentum > 0) {
1592 if (momentum > 20000.)
1593 momentum = 20000.;
1594 else if (momentum < 2000)
1595 momentum = 2000;
1596 } else {
1597 if (momentum < -20000.)
1598 momentum = -20000.;
1599 else if (momentum > -2000.)
1600 momentum = -2000.;
1601 }
1602
1603 return momentum;
1604 }
1605
1606 std::unique_ptr<Trk::Track> MooTrackFitter::fit(const EventContext& ctx, const Trk::Perigee& startPars, MooTrackFitter::MeasVec& hits,
1607 Trk::ParticleHypothesis partHypo, bool prefit) const {
1608 if (hits.empty()) return nullptr;
1609 std::unique_ptr<Trk::Perigee> perigee{};
1610 ATH_MSG_VERBOSE(std::setprecision(5) << " track start parameter: phi " << startPars.momentum().phi() << " theta "
1611 << startPars.momentum().theta() << " q*mom " << startPars.charge() * startPars.momentum().mag()
1612 << " r " << startPars.position().perp() << " z " << startPars.position().z() << std::endl
1613 << " start par is a perigee "
1614 << " partHypo " << partHypo << std::endl
1615 << m_printer->print(hits));
1616
1617 const Trk::TrackParameters* pars = &startPars;
1619 DistanceAlongParameters distAlongPars;
1620 double dist = distAlongPars(startPars, *hits.front());
1621
1622 if (dist < 0.) {
1623 ATH_MSG_DEBUG(" start parameters after first hit, shifting them.... ");
1624 perigee = createPerigee(ctx, startPars, *hits.front());
1625 if (perigee) {
1626 pars = perigee.get();
1627 } else {
1628 ATH_MSG_DEBUG(" failed to move start pars, failing fit ");
1629 return nullptr;
1630 }
1631 }
1632 }
1633
1634 // fit track
1635
1636 ATH_MSG_VERBOSE("fit: " << (prefit ? "prefit" : "fit") << "track with hits: " << hits.size() << std::endl
1637 << m_printer->print(hits));
1638 //The 'pars' is from perigee.get(), and the perigee object still exists in the 'garbage' vector
1639 //cppcheck-suppress invalidLifetime
1640 std::unique_ptr<Trk::Track> track{m_trackFitter->fit(ctx, hits, *pars, m_runOutlier, partHypo)};
1641
1642 // 'sign' track
1643 if (track) {
1644 ATH_MSG_VERBOSE("Got back this track:"<<*track);
1645 track->info().setPatternRecognitionInfo(m_patRecInfo);
1646 }
1647
1648 return track;
1649 }
1650
1651 std::unique_ptr<Trk::Track> MooTrackFitter::fitWithRefit(const EventContext& ctx, const Trk::Perigee& startPars, MooTrackFitter::MeasVec& hits) const {
1652
1653 std::unique_ptr<Trk::Track> track = fit(ctx, startPars, hits, Trk::muon, false);
1654
1655 // exceptions that are not refitted
1656 if (m_slFit) return track;
1657
1658 // refit track to ensure correct handling of material effects
1659 if (track) {
1660 const Trk::Perigee* pp = track->perigeeParameters();
1661 if (pp) {
1662 ATH_MSG_VERBOSE(" found track: " << m_printer->print(*track));
1663
1664 // refit if absolute difference of the initial momentum and the final momentum is larger than 5 GeV
1665 double difMom = startPars.momentum().mag() - pp->momentum().mag();
1666 if (std::abs(difMom) > 5000.) {
1667 ATH_MSG_DEBUG(" absolute difference in momentum too large, refitting track. Dif momentum= " << difMom);
1668 ATH_MSG_DEBUG("fitWithRefit: refitting track with hits: " << hits.size() << std::endl << m_printer->print(hits));
1669 std::unique_ptr<Trk::Track> refittedTrack(m_trackFitter->fit(ctx, hits, *pp, false, m_ParticleHypothesis));
1670 if (refittedTrack) {
1671 track.swap(refittedTrack);
1672
1673 if (msgLvl(MSG::DEBUG)) {
1674 const Trk::Perigee* pp = track->perigeeParameters();
1675 if (pp) {
1676 ATH_MSG_DEBUG(" refitted track fit perigee: r " << pp->position().perp() << " z " << pp->position().z());
1677 }
1678 }
1679 }
1680 }
1681 }
1682 }
1683
1684 return track;
1685 }
1686
1687 bool MooTrackFitter::cleanPhiHits(const EventContext& ctx, double momentum, MooTrackFitter::FitterData& fitterData,
1688 const std::vector<const Trk::PrepRawData*>& patternPhiHits) const {
1689 ATH_MSG_VERBOSE(" cleaning phi hits ");
1690
1691 MeasVec& phiHits = fitterData.phiHits;
1692
1693 // copy phi ROTs into vector, split up competing ROTs
1694 std::vector<const Trk::RIO_OnTrack*> rots;
1695 std::vector<std::unique_ptr<const Trk::RIO_OnTrack>> rotsNSW; // hack as the phi hit cleaning does not work for NSW hits
1696 std::set<Identifier> ids;
1697 std::set<MuonStationIndex::StIndex> stations;
1698 rots.reserve(phiHits.size() + 5);
1699
1700 for (const Trk::MeasurementBase* hit : phiHits) {
1701 const Trk::RIO_OnTrack* rot = dynamic_cast<const Trk::RIO_OnTrack*>(hit);
1702 if (rot) {
1703 if (m_idHelperSvc->issTgc(rot->identify())) {
1704 rotsNSW.push_back(rot->uniqueClone());
1705 continue;
1706 }
1707 rots.push_back(rot);
1708 ids.insert(rot->identify());
1709 continue;
1710 }
1711 const CompetingMuonClustersOnTrack* crot = dynamic_cast<const CompetingMuonClustersOnTrack*>(hit);
1712 if (crot) {
1713 for (const MuonClusterOnTrack* mit : crot->containedROTs()) {
1714 rots.push_back(mit);
1715 ids.insert(mit->identify());
1716 MuonStationIndex::StIndex stIndex = m_idHelperSvc->stationIndex(mit->identify());
1717 stations.insert(stIndex);
1718 }
1719 } else {
1720 ATH_MSG_WARNING(" phi hits should be ROTs or competing ROTs! Dropping hit ");
1721 }
1722 }
1723
1724 if (rots.empty()) return false;
1725
1726 if (rots.size() > m_phiHitsMax) {
1727 ATH_MSG_DEBUG(" too many phi hits, not running cleaning " << rots.size());
1728 return true;
1729 }
1730
1731 if (msgLvl(MSG::VERBOSE)) {
1732 msg(MSG::VERBOSE) << " contained phi hits ";
1733 for (const Trk::RIO_OnTrack* rit : rots) { msg(MSG::DEBUG) << std::endl << " " << m_printer->print(*rit); }
1734 msg(MSG::DEBUG) << endmsg;
1735 }
1736
1737 // if available, extract additional phi hits from the road that were not on the entries
1738 std::vector<const Trk::PrepRawData*> roadPhiHits;
1739 std::copy_if(patternPhiHits.begin(), patternPhiHits.end(), std::back_inserter(roadPhiHits), [&ids, this](const Trk::PrepRawData* prd){
1740 return !(ids.count(prd->identify()) ||
1742 m_idHelperSvc->isCsc(prd->identify()) || m_idHelperSvc->issTgc(prd->identify()));
1743 });
1744
1745
1746 if (roadPhiHits.size() + rots.size() > m_phiHitsMax) {
1747 ATH_MSG_VERBOSE(" too many pattern phi hits, not adding any " << roadPhiHits.size());
1748 roadPhiHits.clear(); // cleaning road hits as we do not want to add them but we do want to clean
1749 }
1750 if (msgLvl(MSG::VERBOSE)) {
1751 if (!roadPhiHits.empty()) {
1752 msg(MSG::VERBOSE) << " additional pattern phi hits " << std::endl;
1753 std::vector<const Trk::PrepRawData*>::const_iterator pit = roadPhiHits.begin();
1754 std::vector<const Trk::PrepRawData*>::const_iterator pit_end = roadPhiHits.end();
1755 for (; pit != pit_end; ++pit) {
1756 msg(MSG::DEBUG) << " " << m_printer->print(**pit);
1757 if (pit + 1 != pit_end)
1758 msg(MSG::DEBUG) << std::endl;
1759 else
1760 msg(MSG::DEBUG) << endmsg;
1761 }
1762 }
1763 }
1764
1765 std::vector<std::unique_ptr<const Trk::MeasurementBase>> newMeasurements{m_phiHitSelector->select_rio(momentum, rots, roadPhiHits)};
1766
1767 newMeasurements.insert(newMeasurements.end(),
1768 std::make_move_iterator(rotsNSW.begin()),
1769 std::make_move_iterator(rotsNSW.end()));
1770
1771 ATH_MSG_VERBOSE(" selected phi hits " << newMeasurements.size());
1772
1773 if (newMeasurements.empty()) {
1774 ATH_MSG_DEBUG(" empty list of phi hits return from phi hit selector: input size " << phiHits.size());
1775 return false;
1776 }
1777
1778 // require the new hits to be within a certain distance of the hits already on the track candidate
1779 DistanceAlongParameters distAlongPars;
1780 constexpr double maxDistCut = 800.;
1781 std::vector<const Trk::MeasurementBase*> measurementsToBeAdded{};
1782 measurementsToBeAdded.reserve(newMeasurements.size());
1783 for (std::unique_ptr<const Trk::MeasurementBase>& meas : newMeasurements) {
1784 const Identifier id = m_edmHelperSvc->getIdentifier(*meas);
1785 if (!id.is_valid()) {
1786 ATH_MSG_WARNING(" Phi measurement without valid Identifier! ");
1787 continue;
1788 }
1789 ATH_MSG_VERBOSE(" Test phi hit - " << m_printer->print(*meas));
1790 // only add hits if the hitList is not empty so we can calculate the distance of the new hit to the first and last hit on the
1791 // track
1792 if (fitterData.hitList.empty()) {
1793 ATH_MSG_VERBOSE(" discarding phi hit due to empty hitList ");
1794 break;
1795 }
1796
1797 // calculate the distance of the phi hit to the first hit. If the phi hit lies before the first hit,
1798 // remove the hit if the distance is too large
1799 double dist = distAlongPars(fitterData.hitList.front()->parameters(), *meas);
1800 if (dist < -maxDistCut) {
1801 ATH_MSG_VERBOSE(" discarded phi hit, distance to first hit too large " << dist);
1802 continue;
1803 }
1804
1805 // calculate the distance of the phi hit to the last hit. If the phi hit lies after the last hit,
1806 // remove the hit if the distance is too large
1807 double distBack = distAlongPars(fitterData.hitList.back()->parameters(), *meas);
1808 if (distBack > maxDistCut) {
1809 ATH_MSG_VERBOSE(" discarded phi hit, distance to last hit too large " << distBack);
1810 continue;
1811 }
1812
1813 ATH_MSG_VERBOSE(" new phi hit, distance from start pars " << dist << " distance to last pars " << distBack);
1814
1815 measurementsToBeAdded.push_back(meas.get());
1816 fitterData.garbage.push_back(std::move(meas));
1817 }
1818
1819 // now remove all previous phi hits and replace them with the new ones
1820
1821 for (const Trk::MeasurementBase* hit : phiHits) {
1822 ATH_MSG_VERBOSE(" Remove phi measurement " << m_printer->print(*hit));
1823 m_hitHandler->remove(*hit, fitterData.hitList);
1824
1825 }
1826
1827 // add the new phi hits to the hit list
1828 if (!measurementsToBeAdded.empty()) {
1829 ATH_MSG_VERBOSE(" adding measurements "<<std::endl<<m_printer->print(measurementsToBeAdded));
1830 MuPatHitList newHitList;
1831 m_hitHandler->create(ctx, fitterData.firstEntry->entryPars(), measurementsToBeAdded, newHitList);
1832 fitterData.hitList = m_hitHandler->merge(newHitList, fitterData.hitList);
1833 }
1834
1835 ATH_MSG_VERBOSE(" done cleaning ");
1836
1837 return true;
1838 }
1839
1840 std::unique_ptr<Trk::Track> MooTrackFitter::cleanAndEvaluateTrack(const EventContext& ctx, Trk::Track& track,
1841 const std::set<Identifier>& excludedChambers) const {
1842 // preselection to get ride of really bad tracks
1843 if (!m_edmHelperSvc->goodTrack(track, m_preCleanChi2Cut)) {
1844 ATH_MSG_DEBUG(" Track rejected due to large chi2" << std::endl << m_printer->print(track));
1845 return nullptr;
1846 }
1847
1848 // perform cleaning of track
1849 std::unique_ptr<Trk::Track> cleanTrack;
1850 if (excludedChambers.empty())
1851 cleanTrack = m_cleaner->clean(track, ctx);
1852 else {
1853 ATH_MSG_DEBUG(" Cleaning with exclusion list " << excludedChambers.size());
1854 cleanTrack = m_cleaner->clean(track, excludedChambers, ctx);
1855 }
1856 if (!cleanTrack) {
1857 ATH_MSG_DEBUG(" Cleaner returned a zero pointer, reject track ");
1858 return nullptr;
1859 }
1860
1861 // selection to get ride of bad tracks after cleaning
1862 if (!m_edmHelperSvc->goodTrack(*cleanTrack, m_chi2Cut)) {
1863 ATH_MSG_DEBUG(" Track rejected after cleaning " << std::endl << m_printer->print(*cleanTrack));
1864 return nullptr;
1865 }
1866
1867 ATH_MSG_DEBUG(" Track passed selection after cleaning! ");
1868 return cleanTrack;
1869 }
1870
1872 const double p = pars.momentum().mag();
1873 if (p < m_pThreshold) {
1874 ATH_MSG_DEBUG(" momentum below threshold: momentum " << pars.momentum().mag() << " p " << pars.momentum().perp());
1875 return false;
1876 }
1877 return true;
1878 }
1879
1880 void MooTrackFitter::cleanEntry(const MuPatCandidateBase& entry, std::set<Identifier>& removedIdentifiers) const {
1881 // if segment entry use segment directly
1882 const MuPatSegment* segEntry = dynamic_cast<const MuPatSegment*>(&entry);
1883 if (segEntry && segEntry->segment) {
1884 if (entry.hasSmallChamber() && entry.hasLargeChamber()) {
1885 ATH_MSG_DEBUG(" Segment with SL overlap, cannot perform cleaning ");
1886 } else {
1887 cleanSegment(*segEntry->segment, removedIdentifiers);
1888 }
1889 }
1890 }
1891
1892 void MooTrackFitter::cleanSegment(const MuonSegment& seg, std::set<Identifier>& removedIdentifiers) const {
1894 /* ******** Mdt hits ******** */
1895
1896 const MuonGM::MdtReadoutElement* detEl = nullptr;
1897
1898 Amg::Transform3D gToStation{Amg::Transform3D::Identity()};
1899
1900
1901 float tubeRadius = 14.6;
1902
1903 ATH_MSG_DEBUG("loop through hits for segment");
1904 for ( const Trk::MeasurementBase* meas : seg.containedMeasurements()) {
1905 const MdtDriftCircleOnTrack* mdt = dynamic_cast<const MdtDriftCircleOnTrack*>(meas);
1906
1907 if (!mdt) { continue; }
1908 Identifier id = mdt->identify();
1909 if (!detEl) {
1910 detEl = mdt->prepRawData()->detectorElement();
1911 gToStation = detEl->GlobalToAmdbLRSTransform();
1912 }
1913 if (!detEl) {
1914 ATH_MSG_WARNING(" error aborting not detEl found ");
1915 break;
1916 }
1917
1918 ATH_MSG_DEBUG("detector element of station type " << detEl->getStationType());
1919 tubeRadius = detEl->innerTubeRadius();
1920
1921 // calculate local AMDB position
1922 Amg::Vector3D LocVec2D = gToStation * mdt->prepRawData()->globalPosition();
1923 TrkDriftCircleMath::LocVec2D lpos(LocVec2D.y(), LocVec2D.z());
1924
1925 double r = mdt->localParameters()[Trk::locR];
1926 double dr = Amg::error(mdt->localCovariance(), Trk::locR);
1927
1928 // create identifier
1929 TrkDriftCircleMath::MdtId mdtid(m_idHelperSvc->mdtIdHelper().isBarrel(id), m_idHelperSvc->mdtIdHelper().multilayer(id) - 1,
1930 m_idHelperSvc->mdtIdHelper().tubeLayer(id) - 1, m_idHelperSvc->mdtIdHelper().tube(id) - 1);
1931
1932 // create new DriftCircle
1934 TrkDriftCircleMath::DCOnTrack dcOnTrack(dc, 1., 1.);
1935 ATH_MSG_VERBOSE(" new MDT hit " << m_idHelperSvc->toString(id));
1936
1937 dcs.push_back(dcOnTrack);
1938
1939 }
1940
1941 double angleYZ = seg.localDirection().angleYZ();
1942 const Amg::Vector3D lpos = gToStation * seg.globalPosition();
1943
1944 ATH_MSG_DEBUG(" cleaning segment " << m_printer->print(seg) << std::endl
1945 << " local parameters " << lpos.y() << " " << lpos.z() << " phi " << angleYZ << " with "
1946 << dcs.size() << " hits ");
1947
1948 TrkDriftCircleMath::LocVec2D segPos(lpos.y(), lpos.z());
1949 TrkDriftCircleMath::Line segPars(segPos, angleYZ);
1950
1953 fitter.fit(segment, segPars, dcs);
1954 segment.hitsOnTrack(dcs.size());
1955 ATH_MSG_DEBUG(" segment after fit " << segment.chi2() << " ndof " << segment.ndof() << " local parameters " << segment.line().x0()
1956 << " " << segment.line().y0() << " phi " << segment.line().phi());
1957
1958 bool hasDroppedHit = false;
1959 unsigned int dropDepth = 0;
1961 bool success = finder.dropHits(segment, hasDroppedHit, dropDepth);
1962 if (!success) {
1963 ATH_MSG_DEBUG(" drop hits failed ");
1964 return;
1965 }
1966
1967 if (dcs.size() == segment.ndof() + 2) {
1968 ATH_MSG_DEBUG(" segment unchanges ");
1969 } else if (dcs.size() != segment.ndof() + 3) {
1970 ATH_MSG_DEBUG(" more than one hit removed, keeping old segment ");
1971 } else {
1972 ATH_MSG_DEBUG(" removed single hit, not using it in fit ");
1973
1975 const TrkDriftCircleMath::DCOnTrackVec& matchedDCs = matchDC.match(segment.dcs());
1976
1977 for (const TrkDriftCircleMath::DCOnTrack& dcit: matchedDCs) {
1978 if (dcit.state() == TrkDriftCircleMath::DCOnTrack::OnTrack) continue;
1979 removedIdentifiers.insert(dcit.rot()->identify());
1980 ATH_MSG_VERBOSE("Removing hit "<<m_idHelperSvc->toString(dcit.rot()->identify()));
1981 }
1982 }
1983 }
1984
1986 if (fitterData.startPars->momentum().mag() < 4000.) return;
1987
1988 std::set<Identifier> removedIdentifiers;
1989 if (fitterData.firstEntry) cleanEntry(*fitterData.firstEntry, removedIdentifiers);
1990 if (fitterData.secondEntry) cleanEntry(*fitterData.secondEntry, removedIdentifiers);
1991
1992 ATH_MSG_DEBUG(" removing hits " << removedIdentifiers.size());
1993
1994 for (const Identifier& id : removedIdentifiers) {
1995 ATH_MSG_VERBOSE("Remove hit "<<m_idHelperSvc->toString(id));
1996 m_hitHandler->remove(id, fitterData.hitList);
1997 }
1998 }
1999
2000 std::pair<std::unique_ptr<Trk::Track>, std::unique_ptr<Trk::Track>> MooTrackFitter::splitTrack(const EventContext& ctx, const Trk::Track& track) const {
2001
2002 // access TSOS of track
2003 const Trk::TrackStates* oldTSOT = track.trackStateOnSurfaces();
2004 if (!oldTSOT) return std::make_pair<std::unique_ptr<Trk::Track>, std::unique_ptr<Trk::Track>>(nullptr, nullptr);
2005
2006 // check whether the perigee is expressed at the point of closes approach or at muon entry
2007 const Trk::Perigee* perigee = track.perigeeParameters();
2008 if (perigee) {
2009 bool atIP = std::abs(perigee->position().dot(perigee->momentum().unit())) < 10;
2010 if (atIP) {
2011 ATH_MSG_DEBUG(" track extressed at perigee, cannot split it ");
2012 return std::make_pair<std::unique_ptr<Trk::Track>, std::unique_ptr<Trk::Track>>(nullptr, nullptr);
2013 }
2014 }
2015
2016 // loop over content input track and count perigees
2017 unsigned int nperigees(0);
2018 Trk::TrackStates::const_iterator tit = oldTSOT->begin();
2019 Trk::TrackStates::const_iterator tit_end = oldTSOT->end();
2020 for (; tit != tit_end; ++tit) {
2021 // look whether state is a perigee
2022 if ((*tit)->type(Trk::TrackStateOnSurface::Perigee) && dynamic_cast<const Trk::Perigee*>((*tit)->trackParameters()))
2023 ++nperigees;
2024 }
2025 if (nperigees != 2) {
2026 ATH_MSG_DEBUG(" Number of perigees is not one, cannot split it " << nperigees);
2027 return std::make_pair<std::unique_ptr<Trk::Track>, std::unique_ptr<Trk::Track>>(nullptr, nullptr);
2028 }
2029
2030 struct TrackContent {
2031 TrackContent() : firstParameters(nullptr), tsos(), track() {}
2032 const Trk::TrackParameters* firstParameters;
2033 std::vector<const Trk::TrackStateOnSurface*> tsos;
2034 std::unique_ptr<Trk::Track> track;
2035 std::set<MuonStationIndex::StIndex> stations;
2036 };
2037
2038 // store content of split tracks
2039 TrackContent firstTrack;
2040 firstTrack.tsos.reserve(oldTSOT->size());
2041
2042 TrackContent secondTrack;
2043 secondTrack.tsos.reserve(oldTSOT->size());
2044
2045 // keep track of the current set that is being filled
2046 TrackContent* currentTrack = &firstTrack;
2047
2048 // count perigees on track
2049 nperigees = 0;
2050
2051 // loop over content input track and extract the content of the two split tracks
2052 tit = oldTSOT->begin();
2053 tit_end = oldTSOT->end();
2054 for (; tit != tit_end; ++tit) {
2055 const Trk::TrackParameters* pars = (*tit)->trackParameters();
2056
2057 // look whether state is a perigee
2058 if ((*tit)->type(Trk::TrackStateOnSurface::Perigee) && dynamic_cast<const Trk::Perigee*>(pars)) {
2059 ++nperigees;
2060
2061 if (msgLvl(MSG::DEBUG) && nperigees == 1) msg(MSG::DEBUG) << MSG::DEBUG << " found first perigee on track " << endmsg;
2062
2063 // if this is the second perigee, switch to second part of the track
2064 if (nperigees == 2) {
2065 ATH_MSG_DEBUG(" found second perigee, switch to second track");
2066 currentTrack = &secondTrack;
2067
2068 } else if (nperigees > 2) {
2069 ATH_MSG_WARNING(" Unexpected number of perigees: " << nperigees);
2070 }
2071 }
2072
2073 // we should drop all states inbetween the two perigees
2074 if (nperigees == 1) { ATH_MSG_VERBOSE(" state between the two perigees "); }
2075
2076 // check whether the current TrackContent has firstParameters, if not set them
2077 if (!currentTrack->firstParameters) {
2078 ATH_MSG_VERBOSE(" found first parameters " << m_printer->print(*pars));
2079 currentTrack->firstParameters = pars;
2080 }
2081
2082 // check whether there are track parameters on the current state
2083 if (!pars) {
2084 ATH_MSG_VERBOSE(" adding state without parameters ");
2085 currentTrack->tsos.push_back(*tit);
2086 continue;
2087 }
2088
2089 // check whether state is a measurement, if not continue
2090 const Trk::MeasurementBase* meas = (*tit)->measurementOnTrack();
2091 if (!meas) {
2092 ATH_MSG_VERBOSE(" adding state without measurement " << m_printer->print(*pars));
2093 currentTrack->tsos.push_back(*tit);
2094 continue;
2095 }
2096
2097 // get identifier, if it has no identifier or is not a muon hit continue
2098 Identifier id = m_edmHelperSvc->getIdentifier(*meas);
2099 if (!id.is_valid() || !m_idHelperSvc->isMuon(id)) {
2100 ATH_MSG_VERBOSE(" adding state with measurement without valid identifier " << m_printer->print(*pars));
2101 currentTrack->tsos.push_back(*tit);
2102 continue;
2103 }
2104
2105 if (nperigees == 1) ATH_MSG_WARNING(" found muon measurement inbetween the two perigees, this should not happen ");
2106
2107 // add station index for MDT and CSC hits
2108 if (!m_idHelperSvc->isTrigger(id)) currentTrack->stations.insert(m_idHelperSvc->stationIndex(id));
2109
2110 ATH_MSG_VERBOSE(" adding " << m_printer->print(*meas));
2111
2112 currentTrack->tsos.push_back(*tit);
2113 }
2114
2115 if (firstTrack.firstParameters)
2116 ATH_MSG_DEBUG(" first track content: states " << firstTrack.tsos.size() << " stations " << firstTrack.stations.size() << endmsg
2117 << " first pars " << m_printer->print(*firstTrack.firstParameters));
2118
2119 else if (secondTrack.firstParameters)
2120 ATH_MSG_DEBUG(" second track content: states " << secondTrack.tsos.size() << " stations " << secondTrack.stations.size()
2121 << endmsg << " first pars " << m_printer->print(*secondTrack.firstParameters));
2122
2123 // check whether both tracks have start parameters and sufficient stations
2124 if ((firstTrack.firstParameters && firstTrack.stations.size() > 1) &&
2125 (secondTrack.firstParameters && secondTrack.stations.size() > 1)) {
2126 ATH_MSG_DEBUG(" track candidate can be split, trying to fit split tracks ");
2127 // fit the two tracks
2128 firstTrack.track = fitSplitTrack(ctx, *firstTrack.firstParameters, firstTrack.tsos);
2129 if (firstTrack.track) {
2130 ATH_MSG_DEBUG(" fitted first track, trying second ");
2131
2132 secondTrack.track = fitSplitTrack(ctx, *secondTrack.firstParameters, secondTrack.tsos);
2133
2134 if (secondTrack.track) {
2135 ATH_MSG_DEBUG(" fitted second track ");
2136
2137 } else {
2138 ATH_MSG_DEBUG(" failed to fit second track ");
2139 firstTrack.track.reset();
2140 }
2141 } else {
2142 ATH_MSG_DEBUG(" failed to fit first track ");
2143 }
2144 }
2145
2146 return std::make_pair<std::unique_ptr<Trk::Track>, std::unique_ptr<Trk::Track>>(std::move(firstTrack.track),
2147 std::move(secondTrack.track));
2148 }
2149
2150 std::unique_ptr<Trk::Track> MooTrackFitter::fitSplitTrack(const EventContext& ctx, const Trk::TrackParameters& startPars,
2151 const std::vector<const Trk::TrackStateOnSurface*>& tsos) const {
2152 // first create track out of the constituent
2153 double phi = startPars.momentum().phi();
2154 double theta = startPars.momentum().theta();
2155 double qoverp = startPars.charge() / startPars.momentum().mag();
2156 if (m_slFit) qoverp = 0;
2157 Trk::PerigeeSurface persurf(startPars.position());
2158 std::unique_ptr<Trk::Perigee> perigee = std::make_unique<Trk::Perigee>(0, 0, phi, theta, qoverp, persurf);
2159
2160 auto trackStateOnSurfaces = std::make_unique<Trk::TrackStates>();
2161 trackStateOnSurfaces->reserve(tsos.size() + 1);
2162 trackStateOnSurfaces->push_back(MuonTSOSHelper::createPerigeeTSOS(std::move(perigee)));
2163 for (const Trk::TrackStateOnSurface* tsos : tsos) trackStateOnSurfaces->push_back(tsos->clone());
2164
2166 std::unique_ptr<Trk::Track> track = std::make_unique<Trk::Track>(trackInfo, std::move(trackStateOnSurfaces), nullptr);
2167
2168 unsigned int nphi = hasPhiConstrain(track.get());
2169
2170 if (nphi > 1) {
2171 ATH_MSG_DEBUG("Track has sufficient phi constraints, fitting ");
2172
2173 } else {
2174 // loop over the track and find the first and last measurement and the phi hit if any
2175 const Trk::TrackStateOnSurface* firstMeas = nullptr;
2176 const Trk::TrackStateOnSurface* lastMeas = nullptr;
2177 const Trk::TrackStateOnSurface* phiMeas = nullptr;
2178
2179 std::vector<const Trk::TrackStateOnSurface*>::const_iterator tit = tsos.begin();
2180 std::vector<const Trk::TrackStateOnSurface*>::const_iterator tit_end = tsos.end();
2181 for (; tit != tit_end; ++tit) {
2182 // require track parameters;
2183 if (!(*tit)->trackParameters()) continue;
2184
2185 const Trk::MeasurementBase* meas = (*tit)->measurementOnTrack();
2186 if (!meas) continue;
2187
2188 Identifier id = m_edmHelperSvc->getIdentifier(*meas);
2189 if (!id.is_valid() || !m_idHelperSvc->isMuon(id)) continue;
2190
2191 if (m_idHelperSvc->isMdt(id)) {
2192 if (!firstMeas) firstMeas = *tit;
2193 lastMeas = *tit;
2194 } else if (m_idHelperSvc->measuresPhi(id)) {
2195 phiMeas = *tit;
2196 }
2197 }
2198
2199 if (!firstMeas) {
2200 ATH_MSG_WARNING(" failed to find first MDT measurement with track parameters");
2201 return nullptr;
2202 }
2203
2204 if (!lastMeas) {
2205 ATH_MSG_WARNING(" failed to find second MDT measurement with track parameters");
2206 return nullptr;
2207 }
2208
2209 if (firstMeas == lastMeas) {
2210 ATH_MSG_WARNING(" first MDT measurement with track parameters equals to second");
2211 return nullptr;
2212 }
2213
2214 const Trk::TrackStateOnSurface* positionFirstFake = nullptr;
2215 const Trk::TrackStateOnSurface* positionSecondFake = nullptr;
2216
2217 if (phiMeas) {
2218 double distFirst = (firstMeas->trackParameters()->position() - phiMeas->trackParameters()->position()).mag();
2219 double distSecond = (lastMeas->trackParameters()->position() - phiMeas->trackParameters()->position()).mag();
2220 ATH_MSG_DEBUG("Track has one phi constraints, adding second: dist to first " << distFirst << " dist to second "
2221 << distSecond);
2222 // add fake at position furthest away from phi measurement
2223 if (distFirst < distSecond) {
2224 positionFirstFake = lastMeas;
2225 } else {
2226 positionFirstFake = firstMeas;
2227 }
2228
2229 } else {
2230 ATH_MSG_DEBUG("Track has no phi constraints, adding one at beginning and one at the end of the track ");
2231
2232 positionFirstFake = firstMeas;
2233 positionSecondFake = lastMeas;
2234 }
2235
2236 // clean up previous track and create new one with fake hits
2237 auto uniquePerigee = std::make_unique<Trk::Perigee>(0, 0, phi, theta, qoverp, persurf);
2238 auto trackStateOnSurfaces = std::make_unique<Trk::TrackStates>();
2239 trackStateOnSurfaces->reserve(tsos.size() + 3);
2240 trackStateOnSurfaces->push_back(MuonTSOSHelper::createPerigeeTSOS(std::move(uniquePerigee)));
2241
2242 tit = tsos.begin();
2243 tit_end = tsos.end();
2244 for (; tit != tit_end; ++tit) {
2245 // remove existing pseudo measurements
2246 const Trk::MeasurementBase* meas = (*tit)->measurementOnTrack();
2247 if (meas && dynamic_cast<const Trk::PseudoMeasurementOnTrack*>(meas)) {
2248 ATH_MSG_DEBUG("removing existing pseudo measurement ");
2249 continue;
2250 }
2251
2252 trackStateOnSurfaces->push_back((*tit)->clone());
2253 if (*tit == positionFirstFake) {
2254 double fakeError = 100.;
2255 if (positionFirstFake->trackParameters()->covariance()) {
2256 fakeError = Amg::error(*positionFirstFake->trackParameters()->covariance(), Trk::loc2);
2257 ATH_MSG_DEBUG(" Using error of parameters " << fakeError);
2258 }
2259
2260 Amg::Vector3D position = positionFirstFake->trackParameters()->position();
2261 std::unique_ptr<Trk::MeasurementBase> fake =
2262 createFakePhiForMeasurement(*(positionFirstFake->measurementOnTrack()), &position, nullptr, fakeError);
2263 if (fake) {
2264 // need to clone as fake is already added to garbage collection
2265 trackStateOnSurfaces->push_back(MuonTSOSHelper::createMeasTSOSWithUpdate(
2266 **tit, std::move(fake), positionFirstFake->trackParameters()->uniqueClone(), Trk::TrackStateOnSurface::Measurement));
2267 } else {
2268 ATH_MSG_WARNING(" failed to create fake at first measurement ");
2269 }
2270 }
2271 if (*tit == positionSecondFake && positionSecondFake) {
2272 double fakeError = 100.;
2273 if (positionSecondFake->trackParameters()->covariance()) {
2274 fakeError = Amg::error(*positionSecondFake->trackParameters()->covariance(), Trk::loc2);
2275 ATH_MSG_DEBUG(" Using error of parameters " << fakeError);
2276 }
2277
2278 Amg::Vector3D position = positionSecondFake->trackParameters()->position();
2279 std::unique_ptr<Trk::MeasurementBase> fake =
2280 createFakePhiForMeasurement(*(positionSecondFake->measurementOnTrack()), &position, nullptr, fakeError);
2281 if (fake) {
2282 // need to clone as fake is already added to garbage collection
2283 trackStateOnSurfaces->push_back(MuonTSOSHelper::createMeasTSOSWithUpdate(
2284 **tit, std::move(fake), positionSecondFake->trackParameters()->uniqueClone(), Trk::TrackStateOnSurface::Measurement));
2285 } else {
2286 ATH_MSG_WARNING(" failed to create fake at second measurement ");
2287 }
2288 }
2289 }
2290
2292 track = std::make_unique<Trk::Track>(trackInfo, std::move(trackStateOnSurfaces), nullptr);
2293 }
2294
2295 // refit track
2296 std::unique_ptr<Trk::Track> refittedTrack = refit(ctx, *track);
2297 if (refittedTrack) refittedTrack->info().setPatternRecognitionInfo(m_patRecInfo);
2298
2299 return refittedTrack;
2300 }
2301
2302} // namespace Muon
#define M_PI
Scalar phi() const
phi method
Scalar theta() const
theta method
#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)
int sign(int a)
#define x
AthAlgTool(const std::string &type, const std::string &name, const IInterface *parent)
Constructor with parameters:
bool msgLvl(const MSG::Level lvl) const
MsgStream & msg() const
DataModel_detail::const_iterator< DataVector > const_iterator
Standard const_iterator.
Definition DataVector.h:838
const_iterator end() const noexcept
Return a const_iterator pointing past the end of the collection.
const_iterator begin() const noexcept
Return a const_iterator pointing at the beginning of the collection.
size_type size() const noexcept
Returns the number of elements in the collection.
bool is_valid() const
Check if id is in a valid state.
bool fit(const LArSamples::AbsShape &data, const AbsShape &reference, double &k, double &deltaT, double &chi2, const ScaledErrorData *sed=0) const
double stripLength(int chamberLayer, int measuresPhi, int stripNumber, double &epsilon) const
An MMReadoutElement corresponds to a single STGC module; therefore typicaly a barrel muon station con...
double stripActiveLengthRight(const Identifier &id) const
double stripActiveLengthLeft(const Identifier &id) const
double getActiveTubeLength(const int tubeLayer, const int tube) const
double innerTubeRadius() const
Returns the inner tube radius excluding the aluminium walls.
An RpcReadoutElement corresponds to a single RPC module; therefore typicaly a barrel muon station con...
double StripLength(bool measphi) const
returns the strip length for the phi or eta plane
A TgcReadoutElement corresponds to a single TGC chamber; therefore typically a TGC station contains s...
double gangCentralWidth(int gasGap, int gang) const
Returns the length of the central wire in the gang.
An sTgcReadoutElement corresponds to a single STGC module; therefore typicaly a barrel muon station c...
const MuonChannelDesign * getDesign(const Identifier &id) const
returns the MuonChannelDesign class for the given identifier
Class for competing MuonClusters, it extends the Trk::CompetingRIOsOnTrack base class.
const std::vector< const MuonClusterOnTrack * > & containedROTs() const
returns the vector of SCT_ClusterOnTrack objects .
const MuonClusterOnTrack & rioOnTrack(unsigned int) const
returns the RIO_OnTrack (also known as ROT) objects depending on the integer
This class represents the corrected MDT measurements, where the corrections include the effects of wi...
virtual const MdtPrepData * prepRawData() const override final
Returns the PrepRawData used to create this corrected measurement.
virtual const MuonGM::MdtReadoutElement * detectorElement() const override
Returns the detector element corresponding to this PRD.
virtual const Amg::Vector3D & globalPosition() const
Returns the global position of the CENTER of the drift tube (i.e.
std::atomic_uint m_nfailedTubeFit
double phiSeeding(const EventContext &ctx, FitterData &fitterData) const
calculate phi used to for seeding the fit
static double qOverPFromEntry(const MuPatCandidateBase &entry)
get q/p from entry
std::pair< int, int > SmallLargeChambers
Gaudi::Property< double > m_chi2Cut
bool validMomentum(const Trk::TrackParameters &pars) const
check whether mometum of start parameter is ok
ServiceHandle< Muon::IMuonIdHelperSvc > m_idHelperSvc
Gaudi::Property< unsigned int > m_phiHitsMax
std::shared_ptr< const MuonSegment > segmentFromEntry(const EventContext &ctx, const MuPatCandidateBase &entry) const
get segment from entry
bool cleanPhiHits(const EventContext &ctx, double momentum, FitterData &phiHits, const PrepVec &patternPhiHits) const
clean phi hits, returns true if anything happened during the cleaning
std::atomic_uint m_nfits
Gaudi::Property< Trk::RunOutlierRemoval > m_runOutlier
std::atomic_uint m_nsuccess
std::atomic_uint m_nfailedFakePrecise
ToolHandle< Trk::ITrackFitter > m_trackFitter
fitter
Gaudi::Property< bool > m_cleanPhiHits
ToolHandle< IMuonSegmentMomentumEstimator > m_momentumEstimator
tool to estimate track momentum
bool corruptEntry(const MuPatCandidateBase &entry) const
sanity check for entries
Gaudi::Property< bool > m_seedWithSegmentTheta
ToolHandle< IMuonHitSelector > m_phiHitSelector
tool to clean phi hits
Trk::MagneticFieldProperties m_magFieldProperties
magnetic field properties
double qOverPFromEntries(const EventContext &ctx, const MuPatCandidateBase &firstEntry, const MuPatCandidateBase &secondEntry) const
get q/p using angle + position of the two entries
Gaudi::Property< bool > m_seedPhiWithEtaHits
std::pair< std::unique_ptr< Trk::Track >, std::unique_ptr< Trk::Track > > splitTrack(const EventContext &ctx, const Trk::Track &track) const
split given track if it crosses the calorimeter volume, code assumes that the track was already extra...
ToolHandle< IMuonSegmentInOverlapResolvingTool > m_overlapResolver
Gaudi::Property< bool > m_allowFirstFit
void createStartParameters(const EventContext &ctx, FitterData &inputData) const
create a perigee parameter give the input data
StatusCode finalize()
finialize method, method taken from bass-class AlgTool
std::atomic_uint m_nfailedFakeInitial
std::unique_ptr< Trk::Track > fitWithRefit(const EventContext &ctx, const Trk::Perigee &startPars, MeasVec &hits) const
fit track, refit if needed
std::unique_ptr< Trk::MeasurementBase > createFakePhiForMeasurement(const Trk::MeasurementBase &measurement, const Amg::Vector3D *overlapPos, const Amg::Vector3D *phiPos, double error) const
create fake phi hit on the surface of the give measurement
Trk::ParticleHypothesis m_ParticleHypothesis
nomen est omen
Trk::TrackInfo::TrackPatternRecoInfo m_patRecInfo
Gaudi::Property< int > m_matEffects
std::unique_ptr< Trk::Track > fitSplitTrack(const EventContext &ctx, const Trk::TrackParameters &startPars, const std::vector< const Trk::TrackStateOnSurface * > &tsos) const
construct a track from a list of TSOS and a start parameters
Gaudi::Property< bool > m_usePreciseHits
Gaudi::Property< bool > m_seedWithAvePhi
double thetaSeeding(const MuPatCandidateBase &entry, MeasVec &etaHits) const
calculate theta used for seeding the fit
std::unique_ptr< Trk::Track > fit(const EventContext &ctx, const MuPatCandidateBase &firstEntry, const MuPatCandidateBase &secondEntry, const PrepVec &externalPhiHits) const
fit the hits of two MuPatCandidateBase
std::unique_ptr< Trk::Perigee > createPerigee(const EventContext &ctx, const Trk::TrackParameters &firstPars, const Trk::MeasurementBase &firstMeas) const
create perigee parameter to initialize fit
bool getMinMaxPhi(FitterData &fitterData) const
calculate the minimum and maximum phi value a track could have to pass all eta channels
std::atomic_uint m_nfailedExtractCleaning
Gaudi::Property< bool > m_slFit
Gaudi::Property< bool > m_cosmics
std::atomic_uint m_nlowMomentum
std::vector< const Trk::MeasurementBase * > MeasVec
Gaudi::Property< double > m_pThreshold
ToolHandle< IMuonTrackToSegmentTool > m_trackToSegmentTool
helper tool to convert tracks into segments
std::atomic_uint m_nfailedParsInital
ServiceHandle< IMuonEDMHelperSvc > m_edmHelperSvc
multi purpose helper tool
std::pair< double, double > getElementHalfLengths(const Identifier &id, const Trk::TrkDetElementBase *ele) const
void cleanSegment(const MuonSegment &seg, std::set< Identifier > &removedIdentifiers) const
std::atomic_uint m_nfailedExtractPrecise
Gaudi::Property< bool > m_seedAtStartOfTrack
Gaudi::Property< bool > m_slProp
static double restrictedMomentum(double momentum)
impose upper and lower bound on momentum
MeasVec::const_iterator MeasCit
Gaudi::Property< bool > m_usePrefit
std::atomic_uint m_nfailedExtractInital
PublicToolHandle< MuPatHitTool > m_hitHandler
hit handler
Gaudi::Property< double > m_openingAngleCut
ToolHandle< IMuonTrackCleaner > m_cleaner
bool addFakePhiHits(const EventContext &ctx, FitterData &fitterData, const Trk::TrackParameters &referenceParameter) const
check fitterData, add fake phi hits if needed.
unsigned int hasPhiConstrain(FitterData &inputData) const
check whether data has sufficient phi constraints
Gaudi::Property< bool > m_preciseFirstStation
void cleanEntry(const MuPatCandidateBase &entry, std::set< Identifier > &removedIdentifiers) const
Gaudi::Property< double > m_preCleanChi2Cut
bool extractData(const MuPatCandidateBase &entry1, const MuPatCandidateBase &entry2, FitterData &fitterData) const
extract all information needed for the fit from the track
StatusCode initialize()
initialize method, method taken from bass-class AlgTool
std::atomic_uint m_nfailedMinMaxPhi
std::unique_ptr< Trk::Track > refit(const EventContext &ctx, const MuPatTrack &trkCan) const
refit a MuPatTrack
std::atomic_uint m_nfailedFitPrecise
std::atomic_uint m_noPerigee
std::vector< const Trk::PrepRawData * > PrepVec
void removeSegmentOutliers(FitterData &fitterData) const
MooTrackFitter(const std::string &, const std::string &, const IInterface *)
default AlgTool constructor
std::unique_ptr< Trk::Track > cleanAndEvaluateTrack(const EventContext &ctx, Trk::Track &track, const std::set< Identifier > &excludedChambers) const
clean and evaluate the track,
PublicToolHandle< MuonEDMPrinterTool > m_printer
tool to print out EDM objects
ToolHandle< Trk::IPropagator > m_propagator
propagator
ToolHandle< Trk::ITrackSummaryHelperTool > m_trackSummaryTool
track candidate entry object.
virtual const Trk::TrackParameters & entryPars() const =0
return track parameters representing the entry
const MeasVec & etaHits() const
return all eta hits on the entry
const MuPatHitList & hitList() const
returns a reference to the hit list
bool containsStation(MuonStationIndex::StIndex chIndex) const
returns whether the StationIndex is already contained in candidate
const MeasVec & hits() const
return all hits on the entry.
bool containsChamber(MuonStationIndex::ChIndex chIndex) const
returns whether the ChamberIndex is already contained in candidate
const std::set< Identifier > & chamberIds() const
returns set with contained chamber ids
const std::set< MuonStationIndex::StIndex > & stations() const
returns set with contained stationIndices
bool hasEndcap() const
returns whether the entry contains endcap hits
bool hasMomentum() const
returns whether entry has a momentum measurement
bool hasSLOverlap() const
returns whether there is at least one small/large overlap in the same station layer
const MeasVec & phiHits() const
return all phi hits on the entry
segment candidate object.
const MuonSegment * segment
track candidate object.
Definition MuPatTrack.h:37
Trk::Track & track() const
access to track
Definition MuPatTrack.h:175
Base class for Muon cluster RIO_OnTracks.
This is the common class for 3D segments used in the muon spectrometer.
virtual const Amg::Vector3D & globalPosition() const override final
global position
virtual const Trk::PlaneSurface & associatedSurface() const override final
returns the surface for the local to global transformation
static std::unique_ptr< Trk::TrackStateOnSurface > createPerigeeTSOS(std::unique_ptr< Trk::TrackParameters > perigee)
create a perigee TSOS, takes ownership of the Perigee
static std::unique_ptr< Trk::TrackStateOnSurface > createMeasTSOSWithUpdate(const Trk::TrackStateOnSurface &tsos, 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
class representing a drift circle meaurement on segment
Definition DCOnTrack.h:16
This class represents a drift time measurement.
Definition DriftCircle.h:22
@ InTime
drift time too small to be compatible with drift spectrum
Definition DriftCircle.h:27
double x0() const
Definition Line.h:63
double phi() const
Definition Line.h:62
double y0() const
Definition Line.h:64
Implementation of 2 dimensional vector class.
Definition LocVec2D.h:16
const DCOnTrackVec & match(const DCVec &dcs)
double angleYZ() const
access method for angle of local YZ projection
bool contains(ParamDefs par) const
The simple check for the clients whether the parameter is contained.
This class is the pure abstract base class for all fittable tracking measurements.
const LocalParameters & localParameters() const
Interface method to get the LocalParameters.
virtual const Surface & associatedSurface() const =0
Interface method to get the associated Surface.
virtual const Amg::Vector3D & globalPosition() const =0
Interface method to get the global Position.
const Amg::MatrixX & localCovariance() const
Interface method to get the localError.
Detailed track summary for the muon system Give access to hit counts per chamber.
const std::vector< ChamberHitSummary > & chamberHitSummary() const
access to the vector of chamber hit summaries on the track
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 ParametersT< DIM, T, S > * clone() const override final
Virtual clone.
Class describing the Line to which the Perigee refers to.
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 TrkDetElementBase * detectorElement() const =0
returns the detector element, assoicated with the PRD of this class
Identifier identify() const
return the identifier -extends MeasurementBase
std::unique_ptr< RIO_OnTrack > uniqueClone() const
NVI clone returning unique_ptr.
Definition RIO_OnTrack.h:97
virtual const Amg::Vector3D & globalPosition() const override=0
Interface method to get the global Position.
const std::vector< const Trk::MeasurementBase * > & containedMeasurements() const
returns the vector of Trk::MeasurementBase objects
Abstract Base Class for tracking surfaces.
virtual bool globalToLocal(const Amg::Vector3D &glob, const Amg::Vector3D &mom, Amg::Vector2D &loc) const =0
Specified by each surface type: GlobalToLocal method without dynamic memory allocation - boolean chec...
virtual const Amg::Vector3D & normal() const
Returns the normal vector of the Surface (i.e.
const Amg::Transform3D & transform() const
Returns HepGeom::Transform3D by reference.
const Amg::Vector3D & center() const
Returns the center position of the Surface.
virtual void localToGlobal(const Amg::Vector2D &locp, const Amg::Vector3D &mom, Amg::Vector3D &glob) const =0
Specified by each surface type: LocalToGlobal method without dynamic memory allocation.
Intersection straightLineIntersection(const T &pars, bool forceDir=false, const Trk::BoundaryCheck &bchk=false) const
fst straight line intersection schema - templated for charged and neutral parameters
Contains information about the 'fitter' of this track.
represents the track state (measurement, material, fit parameters and quality) at a surface.
const MeasurementBase * measurementOnTrack() const
returns MeasurementBase const overload
const TrackParameters * trackParameters() const
return ptr to trackparameters const overload
@ 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.
A summary of the information contained by a track.
const MuonTrackSummary * muonTrackSummary() const
returns a pointer to the MuonTrackSummary if available
const DataVector< const TrackParameters > * trackParameters() const
Return a pointer to a vector of TrackParameters.
const Perigee * perigeeParameters() const
return Perigee.
This is the base class for all tracking detector elements with read-out relevant information.
virtual const Amg::Vector3D & center() const =0
Return the center of the element.
virtual const Surface & surface() const =0
Return surface associated with this detector element.
int r
Definition globals.cxx:22
std::string toString(const Translation3D &translation, int precision=4)
GeoPrimitvesToStringConverter.
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 ...
Eigen::Affine3d Transform3D
Eigen::Matrix< double, 2, 1 > Vector2D
Eigen::Matrix< double, 3, 1 > Vector3D
void setThetaPhi(Amg::Vector3D &v, double theta, double phi)
sets the theta and phi angle of a vector without changing the magnitude
Eigen::Matrix< double, Eigen::Dynamic, 1 > VectorX
Dynamic Vector - dynamic allocation.
StIndex
enum to classify the different station layers in the muon spectrometer
ChIndex chIndex(const std::string &index)
convert ChIndex name string to enum
StIndex toStationIndex(ChIndex index)
convert ChIndex into StIndex
bool isSmall(const ChIndex index)
Returns true if the chamber index is in a small sector.
ChIndex
enum to classify the different chamber layers in the muon spectrometer
NRpcCablingAlg reads raw condition data and writes derived condition data to the condition store.
std::vector< MuPatHitPtr > MuPatHitList
Definition MuPatHit.h:26
std::shared_ptr< MuPatHit > MuPatHitPtr
Definition MuPatHit.h:25
MuPatHitList::const_iterator MuPatHitCit
Definition MuPatHit.h:27
std::vector< DCOnTrack > DCOnTrackVec
Definition DCOnTrack.h:59
constexpr ParticleHypothesis particle[PARTICLEHYPOTHESES]
the array of masses
@ oppositeMomentum
@ alongMomentum
@ anyDirection
DataVector< const Trk::TrackStateOnSurface > TrackStates
ParametersT< TrackParametersDim, Charged, PerigeeSurface > Perigee
@ NoField
Field is set to 0., 0., 0.,.
@ FullField
Field is set to be realistic, but within a given Volume.
@ locY
local cartesian
Definition ParamDefs.h:38
@ locX
Definition ParamDefs.h:37
@ locR
Definition ParamDefs.h:44
@ 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
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
ParametersT< TrackParametersDim, Charged, PlaneSurface > AtaPlane
void swap(ElementLinkVector< DOBJ > &lhs, ElementLinkVector< DOBJ > &rhs)
double channelLength(int channel) const
STRIPS ONLY: calculate channel length for a given strip number.
std::set< MuonStationIndex::StIndex > stations
const MuPatCandidateBase * firstEntry
std::unique_ptr< Trk::Perigee > startPars
const MuPatCandidateBase * secondEntry
std::vector< std::unique_ptr< const Trk::MeasurementBase > > garbage