ATLAS Offline Software
Loading...
Searching...
No Matches
DCMathSegmentMaker.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
3*/
4
6
7#include <cassert>
8#include <functional>
9#include <iostream>
10#include <ranges>
11
43#include "TrkRoad/TrackRoad.h"
46#include "TrkTrack/Track.h"
47
48namespace {
49
50 double cot(double x) {
52 constexpr double dX = std::numeric_limits<float>::epsilon();
53 if (std::abs(x) < dX || std::abs(x - M_PI) < dX) return std::numeric_limits<float>::max();
55 return std::tan(M_PI_2 - x);
56 }
57 template <typename T> constexpr T absmax(const T& a, const T& b) {
58 return std::abs(a) > std::abs(b) ? a : b;}
59
60} // namespace
61namespace Muon {
62 using namespace MuonStationIndex;
63
65 ATH_CHECK(m_mdtKey.initialize(m_removeDeltas && !m_mdtKey.empty()));
66 // retrieve MuonDetectorManager
67 ATH_CHECK(m_DetectorManagerKey.initialize());
68 ATH_CHECK(m_mdtCreator.retrieve());
69 ATH_CHECK(m_mdtCreatorT0.retrieve(DisableTool{m_mdtCreatorT0.empty()}));
70 ATH_CHECK(m_clusterCreator.retrieve());
72 ATH_CHECK(m_idHelperSvc.retrieve());
73 ATH_CHECK(m_printer.retrieve());
74 ATH_CHECK(m_edmHelperSvc.retrieve());
75 ATH_CHECK(m_segmentFinder.retrieve());
77 ATH_CHECK(m_segmentFitter.retrieve(DisableTool{!m_refitParameters}));
78 ATH_CHECK(m_dcslFitProvider.retrieve(DisableTool{m_dcslFitProvider.empty()}));
79
80 // initialise for data handles
81 ATH_CHECK(m_chamberGeoKey.initialize());
82 return StatusCode::SUCCESS;
83 }
84
85 void DCMathSegmentMaker::find(const Amg::Vector3D& roadpos, const Amg::Vector3D& roaddir,
86 const std::vector<const MdtDriftCircleOnTrack*>& mdts,
87 const std::vector<const MuonClusterOnTrack*>& clusters, bool hasPhiMeasurements,
88 Trk::SegmentCollection* segColl, double momentum, double sinAngleCut, double beta) const {
89 const EventContext& ctx = Gaudi::Hive::currentContext();
91 ATH_MSG_DEBUG("Timeout reached. Aborting sequence.");
92 return;
93 }
94
95 ATH_MSG_DEBUG("In find, passed " << mdts.size() << " MDTs & "<<clusters.size()<<" clusters");
96
97 if (mdts.size() < 3) return;
98
100
101 if (!firstRot) { return; }
102
103 const MuonGM::MdtReadoutElement* detEl = firstRot->detectorElement();
104
105 if (!detEl) {
106 ATH_MSG_WARNING(" no MdtReadoutElement found, returning 0 ");
107 return;
108 }
109
110 // identifier
111 Identifier chid = firstRot->identify();
112
113 // endcap or barrel
114 bool isEndcap = m_idHelperSvc->mdtIdHelper().isEndcap(chid);
115
116 // global to local transformation for chamber
117 Amg::Transform3D gToStation = detEl->GlobalToAmdbLRSTransform();
118
119 // define axis of chamber in global coordinates
120 Amg::Transform3D amdbToGlobal = detEl->AmdbLRSToGlobalTransform();
121
122 // transform nominal pointing chamber position into surface frame
123 Amg::Vector3D globalDirCh{detEl->center()};
124 Amg::Vector3D dirCh(gToStation.linear() * globalDirCh);
125 double chamber_angleYZ = std::atan2(dirCh.z(), dirCh.y());
126
127 Amg::Vector3D roaddir2 = roaddir;
128 double dotprod = globalDirCh.perp() * std::sin(roaddir2.theta()) + globalDirCh.z() * std::cos(roaddir2.theta());
129 if (dotprod < 0) roaddir2 = -roaddir2;
130
131 // transform the global direction into the surface frame
132 Amg::Vector3D d(gToStation.linear() * roaddir2);
133 // calculate the local road angles in the surface frame
134 double road_angleXZ = std::atan2(d.z(), d.x());
135 double road_angleYZ = std::atan2(d.z(), d.y());
136
137 if (!hasPhiMeasurements) road_angleXZ = M_PI; // if no phi, take phi perpendicular to plane
138 ATH_MSG_VERBOSE("global road pos "<<Amg::toString(roadpos)<<", global road dir " << Amg::toString(roaddir2) << " XZ " << road_angleXZ << " YZ " << road_angleYZ << " isEndcap "
139 << isEndcap << " central phi " << detEl->center().phi() << " r " << detEl->center().perp()
140 << " z " << detEl->center().z());
141
142 // rescale errors for low momentum
143 double errorScale = errorScaleFactor(chid, momentum, hasPhiMeasurements);
144
145 /* ***** create cluster hits ******** */
146 ATH_MSG_DEBUG(" adding clusters " << clusters.size());
147 ClusterVecPair spVecs;
148 if (m_doSpacePoints)
149 spVecs = create2DClusters(clusters);
150 else
151 spVecs = create1DClusters(clusters);
152 TrkDriftCircleMath::CLVec cls = createClusterVec(chid, spVecs.first, gToStation);
153
154 /* ***** create MDT hits ************ */
155 if (msgLvl(MSG::VERBOSE)) {
156 std::stringstream sstr{};
157 for (const MdtDriftCircleOnTrack* mdt : mdts)
158 sstr<<m_printer->print(*mdt)<<std::endl;
159 ATH_MSG_VERBOSE(" adding mdts " << mdts.size()<<std::endl<<sstr.str());
160 }
161
162 // set to get Identifiers of chambers with hits
163 std::set<Identifier> chamberSet;
164 double phimin{-9999}, phimax{9999};
165 TrkDriftCircleMath::DCStatistics dcStatistics; // statistics on chamber occupancy
166 TrkDriftCircleMath::DCVec dcs = createDCVec(mdts, errorScale, chamberSet, phimin, phimax, dcStatistics, gToStation, amdbToGlobal);
167
168 // create geometry
169 std::shared_ptr<const TrkDriftCircleMath::ChamberGeometry> multiGeo;
170 if (m_doGeometry) {
171 ATH_MSG_VERBOSE(" using chamber geometry with #chambers " << chamberSet.size());
172 // vector to store chamber geometries
173 std::vector<TrkDriftCircleMath::MdtChamberGeometry> geos{};
174
175 // loop over chambers
176 geos.reserve(chamberSet.size());
177 for (const Identifier& id : chamberSet) {
178 ATH_MSG_VERBOSE("Chamber: "<<m_idHelperSvc->toStringChamber(id));
179 geos.push_back(createChamberGeometry(id, gToStation));
180 }
181 // create new geometry
182 multiGeo = std::make_unique<TrkDriftCircleMath::MdtMultiChamberGeometry>(geos);
183
184
185 }
186
187 double angle = m_sinAngleCut;
188 if (sinAngleCut > 0) angle = sinAngleCut;
189 TrkDriftCircleMath::Road road(TrkDriftCircleMath::LocVec2D{0.,0.}, road_angleYZ, chamber_angleYZ, angle);
190
191 // call segment finder
192 TrkDriftCircleMath::SegVec segs = m_segmentFinder->findSegments(dcs, cls, road, dcStatistics, multiGeo.get());
193
194 if (msgLvl(MSG::VERBOSE)) {
195 std::stringstream sstr{};
196 unsigned int seg_n{0};
197 for (const TrkDriftCircleMath::Segment& seg: segs) {
198 constexpr double toDeg = 1./Gaudi::Units::degree;
199 sstr<<"Segment number "<<seg_n<<" is at ("<<seg.line().x0()<<","<<seg.line().y0()<<") pointing to "<<seg.line().phi()*toDeg<<" chi2: "<<
200 (seg.chi2()/seg.ndof())<<"("<<seg.ndof()<<")"<<std::endl;
201 sstr<<"Mdt measurements: "<<seg.dcs().size()<<std::endl;
202 for (const TrkDriftCircleMath::DCOnTrack & mdt_meas : seg.dcs()){
203 sstr<<" **** "<<m_printer->print(*mdt_meas.rot());
204 sstr<<" ("<<mdt_meas.state()<<")"<<std::endl;
205 }
206 sstr<<"Cluster measurements "<<seg.clusters().size()<<std::endl;
207 for (const TrkDriftCircleMath::Cluster& clus: seg.clusters()) {
208 sstr<<" ---- "<<m_printer->print(*clus.rot())<<std::endl;
209 }
210 sstr<<std::endl;
211 ++seg_n;
212 }
213 ATH_MSG_VERBOSE("Found " << segs.size() << " segments "<<std::endl<<sstr.str());
214 }
215
216 // return
217 if (segs.empty()) { return; }
218
219 // loop over segments
220 segmentCreationInfo sInfo(spVecs, multiGeo.get(), gToStation, amdbToGlobal, phimin, phimax);
221 for (TrkDriftCircleMath::Segment& seg : segs) {
222 std::unique_ptr<MuonSegment> segment = createSegment(ctx, seg, chid, roadpos, roaddir2, mdts, hasPhiMeasurements, sInfo, beta);
223 if (segment) segColl->push_back(segment.release());
224 }
225 ATH_MSG_DEBUG(" Done ");
226 }
227
228 std::unique_ptr<MuonSegment> DCMathSegmentMaker::createSegment(const EventContext& ctx, TrkDriftCircleMath::Segment& segment, const Identifier& chid,
229 const Amg::Vector3D& roadpos, const Amg::Vector3D& roaddir2,
230 const std::vector<const MdtDriftCircleOnTrack*>& mdts, bool hasPhiMeasurements,
231 segmentCreationInfo& sInfo, double beta) const {
232 bool isEndcap = m_idHelperSvc->isEndcap(chid);
233 // find all curved segments
234 MuonStationIndex::ChIndex chIndex = m_idHelperSvc->chamberIndex(chid);
235
236 static constexpr std::array<ChIndex ,4> statWithField{ChIndex::BIL, ChIndex::BML, ChIndex::BMS, ChIndex::BOL};
237 const bool isCurvedSegment = segment.hasCurvatureParameters() &&
238 std::ranges::find(statWithField, chIndex) != statWithField.end();
239
240 // remove segments with too few hits
241 if (segment.hitsOnTrack() < 3) return nullptr;
242
243 // convert segment parameters + x position from road
244 const TrkDriftCircleMath::Line& line = segment.line();
245
246 ATH_MSG_DEBUG("New segment: chi2 " << segment.chi2() << " ndof " << segment.ndof() << " line " << line.position().x() << ","
247 << line.position().y() << " phi " << line.phi() << " associated clusters "
248 << segment.clusters().size());
249
250 // local position along x from road
251 Amg::Vector3D lroadpos = sInfo.globalTrans * roadpos;
252 Amg::Vector3D lroaddir = sInfo.globalTrans.linear() * roaddir2;
253
254 // local x position of first tube used if no phi measurement is present
255 double lxroad = 0.;
256
257 if (hasPhiMeasurements) {
258 // calculate local position of segment along tube using the road
259 // calculate intersect pattern measurement plane
260 double sphi = 0.;
261 double cphi = lroaddir.x();
262 // swap local y and z in the endcaps
263 if (isEndcap) {
264 sphi = lroaddir.y();
265 lxroad = lroadpos.x() + (-lroadpos.y() + line.position().x()) * cphi / absmax(sphi, std::numeric_limits<double>::min());
266 } else {
267 sphi = lroaddir.z();
268 lxroad = lroadpos.x() + (-lroadpos.z() + line.position().y()) * cphi / absmax(sphi, std::numeric_limits<double>::min());
269 }
270
271 double shortestTubeLen = 1e9;
272 // loop over hits and get the shortest tube on the segment
273 for (const TrkDriftCircleMath::DCOnTrack& driftCircle : segment.dcs()) {
274 if (driftCircle.state() != TrkDriftCircleMath::DCOnTrack::OnTrack) continue;
275
276 const MdtDriftCircleOnTrack* riodc{driftCircle.rot()};
277 int lay = m_idHelperSvc->mdtIdHelper().tubeLayer(riodc->identify());
278 int tube = m_idHelperSvc->mdtIdHelper().tube(riodc->identify());
279 double tubelen = 0.5 * riodc->prepRawData()->detectorElement()->getActiveTubeLength(lay, tube);
280 if (tubelen < shortestTubeLen) shortestTubeLen = tubelen;
281 }
282 // if the predicted position lies outside the chamber move it back inside
283 if (std::abs(lxroad) > shortestTubeLen) {
284 ATH_MSG_DEBUG("coordinates far outside chamber! using global position of first hit ");
285 if (lxroad < 0.) shortestTubeLen *= -1.;
286 lxroad = shortestTubeLen;
287 }
288 } else {
289 lxroad = (sInfo.globalTrans * mdts[0]->prepRawData()->detectorElement()->surface(mdts[0]->identify()).center()).x();
290 }
291
292 // calculate local direction vector
293 Amg::Vector3D lpos(lxroad, line.position().x(), line.position().y());
294
295 // global position segment
296 Amg::Vector3D gpos = sInfo.amdbTrans * lpos;
297
298 // create new surface
299 Amg::Transform3D surfaceTransform(sInfo.amdbTrans.rotation());
300 surfaceTransform.pretranslate(gpos);
301 double surfDim = 500.;
302 std::unique_ptr<Trk::PlaneSurface> surf = std::make_unique<Trk::PlaneSurface>(surfaceTransform, surfDim, surfDim);
303
304 // measurements
305 Amg::Vector2D segLocPos{Amg::Vector2D::Zero()};
306 double linephi = line.phi();
307
308 // now update the global direction using the local precision angle of the segment and the global phi angle of the
309 // road.
310 Amg::Vector3D gdir = updateDirection(linephi, *surf, roaddir2, isCurvedSegment);
311
312 // extract RIO_OnTracks
313 std::vector<std::pair<double, std::unique_ptr<const Trk::MeasurementBase>> > rioDistVec; // vector to store the distance of a ROT to the segment
314
315 // associate MDT hits to segment
316 std::set<Identifier> deltaVec;
317 std::set<Identifier> outoftimeVec;
318
319 associateMDTsToSegment(gdir, segment, sInfo.geom, sInfo.globalTrans, sInfo.amdbTrans, deltaVec, outoftimeVec, rioDistVec, beta);
320 std::vector<std::pair<double, std::unique_ptr<const Trk::MeasurementBase>>> garbage_collector;
321
323
324 if (m_redo2DFit && !isCurvedSegment) {
325 // refit segment after recalibration
326 TrkDriftCircleMath::DCSLFitter defaultFitter;
328 bool goodFit = defaultFitter.fit(result, line, segment.dcs(), hitSelector.selectHitsOnTrack(segment.dcs()));
329 if (goodFit) {
330 if (std::abs(xAOD::P4Helpers::deltaPhi(segment.line().phi(), result.line().phi())) > 0.01 ||
331 std::abs(segment.line().x0() - result.line().x0()) > 0.01 ||
332 std::abs(segment.line().y0() - result.line().y0()) > 0.01) {
333 // update local position and global
334 linephi = result.line().phi();
335 lpos[1] = result.line().position().x();
336 lpos[2] = result.line().position().y();
337 gpos = sInfo.amdbTrans * lpos;
338
339 // recreate surface
340 surfaceTransform = Amg::Transform3D(sInfo.amdbTrans.rotation());
341 surfaceTransform.pretranslate(gpos);
342 surf = std::make_unique<Trk::PlaneSurface>(surfaceTransform, surfDim, surfDim);
343
344 // finally update global direction
345 gdir = updateDirection(linephi, *surf, roaddir2, isCurvedSegment);
346 }
347 }
348 }
349
350 // create local segment direction
351 Trk::LocalDirection segLocDir;
352 surf->globalToLocalDirection(gdir, segLocDir);
353 if (segLocDir.angleYZ() == 0 && segLocDir.angleXZ() == 0) {
354 ATH_MSG_DEBUG("invalid local direction");
355 return nullptr;
356 }
357
358 // sanity checks
359 const double diff_phi = xAOD::P4Helpers::deltaPhi(roaddir2.phi(), gdir.phi());
360 const double diff_prec = xAOD::P4Helpers::deltaPhi(linephi, segLocDir.angleYZ());
362 if (std::min(std::abs(diff_phi), std::abs( std::abs(diff_phi) - M_PI)) > 1.e-3 ||
363 std::min(std::abs(diff_prec), std::abs(std::abs(diff_prec) - M_PI)) > 1.e-3) {
364 ATH_MSG_WARNING(" ALARM updated angles wrong: diff phi " << diff_phi << " prec " << diff_prec << " phi rdir " << roaddir2.phi()
365 << " gdir " << gdir.phi() << " lphi " << linephi << " seg "
366 << segLocDir.angleYZ());
367 }
368
369 // associate Clusters to segment, uses spVecs to get clusters
370 std::pair<std::pair<int, int>, bool> netaPhiHits =
371 associateClustersToSegment(segment, chid, sInfo.globalTrans, sInfo.clusters, sInfo.phimin, sInfo.phimax, rioDistVec);
372
373 if (rioDistVec.empty()){
374 ATH_MSG_VERBOSE("No measurements were collected.");
375 return nullptr;
376
377 }
379 auto meas_for_fit = [&rioDistVec] () {
380 std::vector<const Trk::MeasurementBase*> out{};
381 out.reserve(rioDistVec.size());
382 std::sort(rioDistVec.begin(), rioDistVec.end(), SortByDistanceToSegment());
383 for (const std::pair<double, std::unique_ptr<const Trk::MeasurementBase>>& ele : rioDistVec) out.push_back(ele.second.get());
384 return out;
385 };
386
387
388 double dlocx{1000.}, dangleXZ{1000.}, qoverp{-99999.}, dqoverp{-99999.};
389 bool hasMeasuredCoordinate = false;
390 if (m_refitParameters && netaPhiHits.second) {
391 ATH_MSG_DEBUG(" distance between first and last phi hit sufficient to perform 4D fit: phi " << gdir.phi() << " theta "
392 << gdir.theta());
393
394 std::unique_ptr<Trk::Track> track{m_segmentFitter->fit(gpos, gdir, *surf, meas_for_fit())};
395
396 if (track) {
397 if (isCurvedSegment && track->perigeeParameters() && track->perigeeParameters()->covariance()) {
398 qoverp = track->perigeeParameters()->parameters()[Trk::qOverP];
399 dqoverp = Amg::error(*track->perigeeParameters()->covariance(), Trk::qOverP);
400 }
401 hasMeasuredCoordinate = true;
402 // hack to update the second coordinate errors
403 Amg::MatrixX updatedCov(5, 5);
404 updatedCov.setZero();
405 m_segmentFitter->updateSegmentParameters(*track, *surf, segLocPos, segLocDir, updatedCov);
406 if (Amg::error(updatedCov, Trk::locX) > 0 && Amg::error(updatedCov, Trk::phi) > 0.) {
407 dlocx = Amg::error(updatedCov, Trk::locX);
408 dangleXZ = Amg::error(updatedCov, Trk::phi); // hack (2): phi not always angleXZ
409 } else {
410 ATH_MSG_WARNING(" Corrupt error matrix returned from fitter " << Amg::toString(updatedCov));
411 }
412
414 surf->localToGlobal(segLocPos, gdir, gpos);
415 surf->localToGlobalDirection(segLocDir, gdir);
416
417 if (track->measurementsOnTrack() && rioDistVec.size() != track->measurementsOnTrack()->size()) {
418 if (track->measurementsOnTrack()->empty()) {
419 ATH_MSG_DEBUG("No measurements survived");
420 return nullptr;
421 }
422 ATH_MSG_DEBUG(" ROT vector size changed after fit, updating ");
423 garbage_collector = std::move(rioDistVec);
424 rioDistVec.reserve(track->measurementsOnTrack()->size());
425 const Trk::TrackParameters* firstPars = nullptr;
426 for (const Trk::TrackStateOnSurface* tsit : *track->trackStateOnSurfaces()) {
427 const Trk::TrackParameters* pars = tsit->trackParameters();
428 if (!pars) continue;
429 if (!firstPars) firstPars = pars;
430
431 // check whether state is a measurement, skip outliers if they are not MDT
432 const Trk::MeasurementBase* meas = tsit->measurementOnTrack();
433 if (!meas) continue;
434 if (tsit->type(Trk::TrackStateOnSurface::Outlier) && !dynamic_cast<const MdtDriftCircleOnTrack*>(meas)) continue;
435 double dist = (pars->position() - firstPars->position()).dot(firstPars->momentum().unit());
436 rioDistVec.emplace_back(dist, meas->uniqueClone());
437 }
438 }
439 } else {
440 ATH_MSG_DEBUG(" refit of segment failed!! ");
441 netaPhiHits.second = false;
442 }
443 }
444
445 // optional update of phi position and direction, only performed if the segment was not refitted and there are phi
446 // hits
447 if (m_updatePhiUsingPhiHits && !netaPhiHits.second) {
448 if (updateSegmentPhi(gpos, gdir, segLocPos, segLocDir, *surf, meas_for_fit(), sInfo.phimin, sInfo.phimax)) {
449 surf->localToGlobal(segLocPos, gpos, gpos);
450 surf->localToGlobalDirection(segLocDir, gdir);
451 hasMeasuredCoordinate = true;
452 dlocx = 100.;
453 dangleXZ = 0.1;
454 }
455 }
456
457 if (msgLvl(MSG::DEBUG)) {
458 std::vector<const Trk::MeasurementBase*> debug_meas = meas_for_fit();
459 ATH_MSG_DEBUG(" number of hits " << debug_meas.size() << " of which trigger " << netaPhiHits.first.first << " eta and "
460 << netaPhiHits.first.second << " phi ");
461 for (const Trk::MeasurementBase* mit : debug_meas) {
462 const Trk::RIO_OnTrack* rot = dynamic_cast<const Trk::RIO_OnTrack*>(mit);
463 if (rot) {
464 ATH_MSG_DEBUG(m_idHelperSvc->toString(rot->identify()));
465 const MdtDriftCircleOnTrack* mdt = dynamic_cast<const MdtDriftCircleOnTrack*>(rot);
466 if (mdt)
467 ATH_MSG_DEBUG(std::setprecision(4)
468 << " radius " << std::setw(6) << mdt->driftRadius() << " time " << std::setw(6) << mdt->driftTime());
469 continue;
470 }
471 const CompetingMuonClustersOnTrack* crot = dynamic_cast<const CompetingMuonClustersOnTrack*>(mit);
472 if (crot) {
473 ATH_MSG_DEBUG(m_idHelperSvc->toString(crot->rioOnTrack(0).identify())
474 << " comp rot with hits " << crot->containedROTs().size());
475 continue;
476 }
477 ATH_MSG_WARNING("failed to dynamic_cast to ROT ");
478 }
479 }
480 // recalculate holes
481 std::vector<Identifier> holeVec = calculateHoles(ctx, chid, gpos, gdir, hasMeasuredCoordinate, deltaVec, outoftimeVec, rioDistVec);
482
483 // currently not taking into account masked channels
484 if (!outoftimeVec.empty()) holeVec.insert(holeVec.end(), std::make_move_iterator(outoftimeVec.begin()),
485 std::make_move_iterator(outoftimeVec.end()));
486 MuonSegmentQuality* quality = new MuonSegmentQuality(segment.chi2(), segment.ndof(), holeVec);
487
488 const TrkDriftCircleMath::DCSLFitter* dcslFitter = m_dcslFitProvider->getFitter();
490 if (dcslFitter && !segment.hasT0Shift() && m_outputFittedT0) {
491 if (!dcslFitter->fit(result, segment.line(), segment.dcs(), hitSelector.selectHitsOnTrack(segment.dcs()))) {
492 ATH_MSG_DEBUG(" T0 refit failed ");
493 } else {
494 ATH_MSG_DEBUG(" Fitted T0 " << result.t0Shift()<<" is valid "<<result.hasT0Shift());
495 }
496 }
497 bool hasFittedT0 = false;
498 double fittedT0{0}, errorFittedT0{1.};
499 if (m_outputFittedT0 && (segment.hasT0Shift() || (dcslFitter && result.hasT0Shift()))) {
500 hasFittedT0 = true;
501 if (segment.hasT0Shift()) {
502 fittedT0 = segment.t0Shift();
503 errorFittedT0 = segment.t0Error();
504 } else if (dcslFitter && result.hasT0Shift()) {
505 fittedT0 = result.t0Shift();
506 errorFittedT0 = result.t0Error();
507 } else {
508 ATH_MSG_WARNING(" Failed to access fitted t0 ");
509 hasFittedT0 = false;
510 }
511 }
512 // create new segment
513 std::unique_ptr<MuonSegment> msegment;
514 if (isCurvedSegment) { // curved segments
515 if (qoverp == -99999.) {
516 double charge = gpos.z() * std::tan(gdir.theta());
517 charge = charge / std::abs(charge);
518 // if the curved segment was not refit, then use a momentum estimate
519 constexpr double BILALPHA(28.4366), BMLALPHA(62.8267), BMSALPHA(53.1259), BOLALPHA(29.7554);
520 if (chIndex == ChIndex::BIL) {
521 qoverp = (charge * segment.deltaAlpha()) / BILALPHA;
522 dqoverp = M_SQRT2 * segment.dtheta() / BILALPHA;
523 } else if (chIndex == ChIndex::BML) {
524 qoverp = (charge * segment.deltaAlpha()) / BMLALPHA;
525 dqoverp = M_SQRT2 * segment.dtheta() / BMLALPHA;
526 } else if (chIndex == ChIndex::BMS) {
527 qoverp = (charge * segment.deltaAlpha()) / BMSALPHA;
528 dqoverp = M_SQRT2 * segment.dtheta() / BMSALPHA;
529 } else if (chIndex == ChIndex::BOL) {
530 qoverp = (charge * segment.deltaAlpha()) / BOLALPHA;
531 dqoverp = M_SQRT2 * segment.dtheta() / BOLALPHA;
532 }
533 }
534 Amg::MatrixX covMatrix(5, 5);
535 covMatrix.setIdentity();
536 covMatrix(0, 0) = dlocx * dlocx;
537 covMatrix(1, 1) = segment.dy0() * segment.dy0();
538 covMatrix(2, 2) = dangleXZ * dangleXZ;
539 covMatrix(3, 3) = segment.dtheta() * segment.dtheta();
540 covMatrix(4, 4) = dqoverp * dqoverp;
541
542 std::vector<Trk::DefinedParameter> defPars;
543 defPars.emplace_back(segLocPos[Trk::loc1], Trk::loc1);
544 defPars.emplace_back(segLocPos[Trk::loc2], Trk::loc2);
545 defPars.emplace_back(gdir.phi(), Trk::phi);
546 defPars.emplace_back(gdir.theta(), Trk::theta);
547 defPars.emplace_back(qoverp, Trk::qOverP);
548 Trk::LocalParameters segLocPar(defPars);
549 msegment = std::make_unique<MuonSegment>(
550 std::move(segLocPar),
551 std::move(covMatrix),
552 surf.release(),
553 createROTVec(rioDistVec),
554 quality,
556 } else { // straight segments
557 // errors (for now no correlations)
558 Amg::MatrixX covMatrix(4, 4);
559 covMatrix.setIdentity();
560 covMatrix(0, 0) = dlocx * dlocx;
561 covMatrix(1, 1) = segment.dy0() * segment.dy0();
562 covMatrix(2, 2) = dangleXZ * dangleXZ;
563 covMatrix(3, 3) = segment.dtheta() * segment.dtheta();
564 msegment =
565 std::make_unique<MuonSegment>(segLocPos,
566 segLocDir,
567 std::move(covMatrix),
568 surf.release(),
569 createROTVec(rioDistVec),
570 quality,
572 }
573
574 if (hasFittedT0) msegment->setT0Error(fittedT0, errorFittedT0);
575
576 // check whether segment satisfies minimum quality criteria
577 int segmentQuality = m_segmentSelectionTool->quality(*msegment);
578
579 if (msgLvl(MSG::DEBUG)) {
580 ATH_MSG_DEBUG(m_printer->print(*msegment) << " quality " << segmentQuality);
581 if (segmentQuality < 0) ATH_MSG_DEBUG(" BAD segment ");
582 if (hasFittedT0) ATH_MSG_DEBUG(" T0 " << fittedT0);
583 if (isCurvedSegment) ATH_MSG_DEBUG(" Curved " << fittedT0);
584 }
585 if (segmentQuality < 0) { return nullptr; }
586 return msegment;
587 }
588
589 void DCMathSegmentMaker::find(const std::vector<const Trk::RIO_OnTrack*>& rios, Trk::SegmentCollection* segColl) const {
590 std::vector<const MdtDriftCircleOnTrack*> mdts;
591 std::vector<const MuonClusterOnTrack*> clusters;
592
593 for (const Trk::RIO_OnTrack* it : rios) {
594 Identifier id = it->identify();
595 if (m_idHelperSvc->isMdt(id)) {
596 const MdtDriftCircleOnTrack* mdt = dynamic_cast<const MdtDriftCircleOnTrack*>(it);
597 if (!mdt) { ATH_MSG_WARNING("failed dynamic_cast, not a MDT but hit has MDT id!!!"); }
598 mdts.push_back(mdt);
599 } else if (m_idHelperSvc->isTrigger(id)) {
600 const MuonClusterOnTrack* clus = dynamic_cast<const MuonClusterOnTrack*>(it);
601 if (!clus) { ATH_MSG_WARNING("failed dynamic_cast, not a cluster but hit has RPC/TGC id!!!"); }
602 clusters.push_back(clus);
603 }
604 }
605 find(mdts, clusters, segColl);
606 }
607
608 void DCMathSegmentMaker::find(const std::vector<const MdtDriftCircleOnTrack*>& mdts,
609 const std::vector<const MuonClusterOnTrack*>& clusters, Trk::SegmentCollection* segColl) const {
610 if (mdts.empty()) return;
611
612 const MdtDriftCircleOnTrack* mdt = mdts.front();
613 if (!mdt) return;
614
615 bool hasPhiMeasurements = false;
616 Amg::Vector3D gpos = mdt->globalPosition();
617 Amg::Vector3D gdir = gpos.unit();
618 find(gpos, gdir, mdts, clusters, hasPhiMeasurements, segColl);
619 }
620
621 void DCMathSegmentMaker::find(const std::vector<const Trk::RIO_OnTrack*>& rios1,
622 const std::vector<const Trk::RIO_OnTrack*>& rios2) const {
623 std::vector<const Trk::RIO_OnTrack*> rios = rios1;
624 rios.insert(rios.end(), rios2.begin(), rios2.end());
625 find(rios);
626 }
627
628 void DCMathSegmentMaker::find(const Trk::TrackRoad& road, const std::vector<std::vector<const MdtDriftCircleOnTrack*> >& mdts,
629 const std::vector<std::vector<const MuonClusterOnTrack*> >& clusters, Trk::SegmentCollection* segColl,
630 bool hasPhiMeasurements, double momentum) const {
631 // copy all mdt hits into one vector
632 std::vector<const MdtDriftCircleOnTrack*> all_mdts;
633 for (const std::vector<const MdtDriftCircleOnTrack*>& circle_vec : mdts) { std::copy(circle_vec.begin(), circle_vec.end(), std::back_inserter(all_mdts)); }
634
635 // copy all clusters into one vector
636 std::vector<const MuonClusterOnTrack*> all_clus;
637 for ( const std::vector<const MuonClusterOnTrack*>& clus_vec : clusters) { std::copy(clus_vec.begin(), clus_vec.end(), std::back_inserter(all_clus)); }
638
639 const Amg::Vector3D& gpos = road.globalPosition();
640 const Amg::Vector3D& gdir = road.globalDirection();
641 find(gpos, gdir, all_mdts, all_clus, hasPhiMeasurements, segColl, momentum, road.deltaEta());
642 }
643
644 double DCMathSegmentMaker::errorScaleFactor(const Identifier& id, double curvature, bool hasPhiMeasurements) const {
645 double scale = 1.;
646 if (!m_curvedErrorScaling) return scale;
647
648 if (!errorScalingRegion(id)) return scale;
649
650 double scaleMax = 5.;
651 if (m_curvedErrorScaling && curvature > 2) {
652 scale = std::min(scaleMax, 1. + curvature / 10000);
653 ATH_MSG_DEBUG(" rescaled errors " << scale << " curvature " << curvature);
654 }
655 scale *= 2;
656
657 // rescale errors is no phi measurement was found
658 if (!hasPhiMeasurements) {
659 double phiScale = 1.;
660 // rescale errors
661 int stRegion = m_idHelperSvc->mdtIdHelper().stationRegion(id);
662 if (stRegion == 0)
663 phiScale = 2.; // inner
664 else if (stRegion == 1)
665 phiScale = 2.5; // extended
666 else if (stRegion == 2)
667 phiScale = 2.5; // middle
668 else
669 phiScale = 3.; // outer
670 scale = std::sqrt(scale*scale + phiScale*phiScale);
671 ATH_MSG_DEBUG(" rescaled error for missing phi road " << scale);
672 }
673
674 return scale;
675 }
676
678
679 {
680 // simple division of MuonSpectrometer in regions using barrel/endcap seperation plus
681 // inner/middle/outer seperation
682
683 bool isEndcap = m_idHelperSvc->isEndcap(id);
684
685 if (isEndcap) {
686 std::string stName = m_idHelperSvc->mdtIdHelper().stationNameString(m_idHelperSvc->mdtIdHelper().stationName(id));
687 if (stName[1] == 'I') return true;
688
689 } else {
690 return true;
691 }
692 return false;
693 }
694
695 DCMathSegmentMaker::ClusterVecPair DCMathSegmentMaker::create1DClusters(const std::vector<const MuonClusterOnTrack*>& clusters) const {
696 // if empty return
697 if (clusters.empty()) return {};
698 // some useful typedefs...
699
700 // create a vector to hold the clusters
701 ClusterVec clVec;
702 ClusterVec phiVec;
703 clVec.reserve(clusters.size());
704
705 for (const MuonClusterOnTrack* clust : clusters) {
706 const Identifier id = clust->identify();
707 const Identifier gasGapId = m_idHelperSvc->gasGapId(id);
708
709 if (m_idHelperSvc->measuresPhi(id)) {
710 phiVec.push_back(createSpacePoint(gasGapId, nullptr, clust));
711 if (phiVec.back().corrupt()) phiVec.pop_back();
712 } else {
713 clVec.push_back(createSpacePoint(gasGapId, clust, nullptr));
714 if (clVec.back().corrupt()) clVec.pop_back();
715 }
716 }
717
718 return ClusterVecPair(clVec, phiVec);
719 }
720
721 DCMathSegmentMaker::ClusterVecPair DCMathSegmentMaker::create2DClusters(const std::vector<const MuonClusterOnTrack*>& clusters) const {
722 // if empty return
723 if (clusters.empty()) return {};
724
725 ChIdHitMap gasGapHitMap;
726 for (const MuonClusterOnTrack* clus: clusters) {
727 const Identifier id = clus->identify();
728 ATH_MSG_VERBOSE(" new trigger hit " << m_idHelperSvc->toString(id));
729
730 const Identifier chId = m_idHelperSvc->chamberId(id);
731 const Identifier gasGapId = m_idHelperSvc->gasGapId(id);
732 // eta hits first than phi hits
733 if (!m_idHelperSvc->measuresPhi(id))
734 gasGapHitMap[chId][gasGapId].first.push_back(clus);
735 else
736 gasGapHitMap[chId][gasGapId].second.push_back(clus);
737 }
738
739 return createSpacePoints(gasGapHitMap);
740 }
741
743 // vector to store output
744 ClusterVec spacePoints{}, phiVec{};
745 spacePoints.reserve(20);
746 phiVec.reserve(20);
747 // loop over chambers
748 for ( const auto& [id, gasGapHits] : chIdHitMap) {
749 // create clusters per chamber and copy them in to result vector
750 ClusterVecPair cls = createSpacePoints(gasGapHits);
751 std::copy(std::make_move_iterator(cls.first.begin()),
752 std::make_move_iterator(cls.first.end()), std::back_inserter(spacePoints));
753 std::copy(std::make_move_iterator(cls.second.begin()),
754 std::make_move_iterator(cls.second.end()), std::back_inserter(phiVec));
755 }
756
757 return std::make_pair(std::move(spacePoints), std::move(phiVec));
758 }
759
761 ClusterVec spacePoints{}, phiVec{};
762 bool isEndcap = m_idHelperSvc->isEndcap((*(gasGapHitMap.begin())).first);
763
764 ATH_MSG_VERBOSE(" creating Space points for " << gasGapHitMap.size() << " gas gaps ");
765
766 for (const auto& [gasGapId, etaPhiHits] : gasGapHitMap) {
767 // flag whether phi hits are matched with a eta hit
768 std::vector<bool> flagPhihit(etaPhiHits.second.size(), 0);
769
770 // store Identifier of previous hit to remove duplicates
771 Identifier prevEtaId;
772
773 ATH_MSG_VERBOSE(" New gasgap " << m_idHelperSvc->toString(gasGapId) << " neta " << etaPhiHits.first.size() << " nphi "
774 << etaPhiHits.second.size());
775
776 for (const MuonClusterOnTrack* etaHit : etaPhiHits.first) {
777 // check whether we are not dealing with a duplicate hit
778 if (etaHit->identify() == prevEtaId) continue;
779 prevEtaId = etaHit->identify();
780
781 ATH_MSG_VERBOSE(" Eta hit " << m_idHelperSvc->toString(etaHit->identify()));
782
783 if (isEndcap) {
784 // check whether match with phi hits was found
785 bool foundSP = false;
786 Identifier prevPhiId;
787 int phi_idx{-1};
788 for (const MuonClusterOnTrack* phiHit : etaPhiHits.second) {
789 // check for duplicate phi hits
790 ++phi_idx;
791 if (phiHit->identify() == prevPhiId) continue;
792 prevPhiId = phiHit->identify();
793
794 ATH_MSG_VERBOSE(" Phi hit " << m_idHelperSvc->toString(phiHit->identify()));
795
796 Cluster2D sp = createTgcSpacePoint(gasGapId, etaHit, phiHit);
797 if (sp.corrupt()) continue;
798 spacePoints.push_back(std::move(sp));
799 // mark as used
800 foundSP = true;
801 flagPhihit[phi_idx] = true;
802 }
803
804 // add single eta hit if not matching phi hit was found
805 if (!foundSP) {
806 Cluster2D sp = createSpacePoint(gasGapId, etaHit, nullptr);
807 if (sp.corrupt()) continue;
808 spacePoints.push_back(std::move(sp));
809 }
810 } else {
811 Cluster2D sp = createRpcSpacePoint(gasGapId, etaHit, etaPhiHits.second);
812 if (sp.corrupt()) continue;
813 // flag all phi hits, not very elegant, but works
814 flagPhihit = std::vector<bool>(etaPhiHits.second.size(), 1);
815 spacePoints.push_back(std::move(sp));
816 }
817 }
818 if (isEndcap) {
819 // loop over flag vector and add unmatched phi hits to phiVec;
820 Identifier prevPhiId;
821 for (unsigned int i = 0; i < flagPhihit.size(); ++i) {
822 if (flagPhihit[i]) continue;
823
824 // check for duplicate phi hits
825 if (etaPhiHits.second[i]->identify() == prevPhiId) continue;
826 prevPhiId = etaPhiHits.second[i]->identify();
827
828 Cluster2D sp = createTgcSpacePoint(gasGapId, nullptr, etaPhiHits.second[i]);
829 if (sp.corrupt()) continue;
830 phiVec.push_back(std::move(sp));
831 }
832 } else if (etaPhiHits.first.empty() && !etaPhiHits.second.empty()) {
833 // if there were no eta hits create one phi spacePoint of all phi hits in gasgap
834 Cluster2D sp = createRpcSpacePoint(gasGapId, nullptr, etaPhiHits.second);
835 if (sp.corrupt()) continue;
836 phiVec.push_back(std::move(sp));
837 }
838 }
839
840 ATH_MSG_VERBOSE(" Creating space points, number of gas-gaps " << gasGapHitMap.size() << " space points " << spacePoints.size());
841
842 return std::make_pair(std::move(spacePoints), std::move(phiVec));
843 }
844
846 const MuonClusterOnTrack* phiHit) const {
847 bool isEndcap = m_idHelperSvc->isEndcap(gasGapId);
848 double error{1.}, lpx{0.}, lpy{0.};
849 // case one hit missing. Take position and error of the available hit
850 if (!etaHit) {
851 if (!phiHit) {
852 ATH_MSG_WARNING("Both eta and phi hits missing");
853 error = 0;
854 } else {
855 lpx = phiHit->localParameters()[Trk::locX];
857 }
858 } else if (!phiHit) {
859 lpx = etaHit->localParameters()[Trk::locX];
861 } else if (etaHit && phiHit) {
862 if (isEndcap) {
863 return createTgcSpacePoint(gasGapId, etaHit, phiHit);
864 } else {
865 std::vector<const MuonClusterOnTrack*> phiVec{phiHit};
866 return createRpcSpacePoint(gasGapId, etaHit, phiVec);
867 }
868 }
869 Identifier detElId = m_idHelperSvc->detElId(gasGapId);
870 if (std::abs(error) < 0.001) {
871 ATH_MSG_WARNING(" Unphysical error assigned for gasgap " << m_idHelperSvc->toString(gasGapId));
872 error = 0.;
873 }
874 return Cluster2D(detElId, gasGapId, Amg::Vector2D(lpx, lpy), error, etaHit, phiHit);
875 }
876
878 const MuonClusterOnTrack* etaHit,
879 const MuonClusterOnTrack* phiHit) const {
880 double error{1.}, lpx{0.}, lpy{0.};
881 Identifier detElId = m_idHelperSvc->detElId(gasGapId);
882 // case one hit missing. Take position and error of the available hit
883 if (!etaHit) {
884 lpx = phiHit->localParameters()[Trk::locX];
886 } else if (!phiHit) {
887 lpx = etaHit->localParameters()[Trk::locX];
889 } else if (etaHit && phiHit) {
890 // get orientation angle of strip to rotate back from local frame to strip
891 // copy code from ROT creator
892 const MuonGM::TgcReadoutElement* detEl = dynamic_cast<const MuonGM::TgcReadoutElement*>(etaHit->detectorElement());
893 const Amg::Vector3D lSpacePoint = Amg::getRotateZ3D(-90 * Gaudi::Units::deg) * detEl->localSpacePoint(phiHit->identify(),
894 etaHit->globalPosition(),
895 phiHit->globalPosition());
896
897
898 lpx = lSpacePoint.x();
899 lpy = lSpacePoint.y();
901 if (error <= std::numeric_limits<double>::epsilon()) {
902 ATH_MSG_WARNING(" Unphysical error assigned for " << m_idHelperSvc->toString(etaHit->identify()));
903 if (etaHit->prepRawData())
904 ATH_MSG_WARNING(" PRD error " << Amg::error(etaHit->prepRawData()->localCovariance(), Trk::locX));
905 }
906 ATH_MSG_DEBUG(" TGC space point: error " << error << " stripWith " << error * M_SQRT2 << std::endl
907 << " " << m_idHelperSvc->toString(etaHit->identify()) << std::endl
908 << " " << m_idHelperSvc->toString(phiHit->identify()));
909 }
910 if (std::abs(error) < 0.001) {
911 ATH_MSG_WARNING(" Unphysical error assigned for gasgap " << m_idHelperSvc->toString(gasGapId));
912 error = 1.;
913 }
914 ATH_MSG_VERBOSE("New space point for "<<m_idHelperSvc->toStringGasGap(gasGapId)<<" at ("<<lpx<<","<<lpy<<")");
915 return Cluster2D(detElId, gasGapId, Amg::Vector2D(lpx, lpy), error, etaHit, phiHit);
916 }
917
919 const std::vector<const MuonClusterOnTrack*>& phiHits) const {
920 // create vector to store phi hits after removal of duplicate hits
921 std::vector<const MuonClusterOnTrack*> cleanPhihits;
922 cleanPhihits.reserve(phiHits.size());
923
924 double error{1.}, lpx{0.}, lpy{0.};
925 // case one hit missing. Take position and error of the available hit
926 if (!etaHit) {
927 lpx = phiHits.front()->localParameters()[Trk::locX];
928 error = Amg::error(phiHits.front()->localCovariance(), Trk::locX);
929 // loop over phi hits, remove duplicate phi hits
930 Identifier prevId;
931 for (const MuonClusterOnTrack* clus : phiHits) {
932 // remove duplicate phi hits
933 if (clus->identify() == prevId) continue;
934 prevId = clus->identify();
935 cleanPhihits.push_back(clus);
936 }
937 } else if (phiHits.empty()) {
938 lpx = etaHit->localParameters()[Trk::locX];
940 } else if (etaHit && !phiHits.empty()) {
941 lpx = etaHit->localParameters()[Trk::locX];
943
944 ATH_MSG_DEBUG(" RPC space point: error " << error << " stripWith " << error * M_SQRT2 << std::endl
945 << " " << m_idHelperSvc->toString(etaHit->identify()));
946
947 double minPos{1e9}, maxPos{-1e9};
948 Identifier prevId;
949
950 // loop over phi hits, calculate average position + cluster width, remove duplicate phi hits
951 for (const MuonClusterOnTrack* phiHit : phiHits) {
952 // remove duplicate phi hits
953 if (phiHit->identify() == prevId) continue;
954 prevId = phiHit->identify();
955
956 // calculate phi hit position in local eta hit reference frame
957 Amg::Vector2D phiLocPos{Amg::Vector2D::Zero()};
958 if (etaHit->associatedSurface().globalToLocal(phiHit->globalPosition(), phiHit->globalPosition(), phiLocPos)) {
959 lpy = phiLocPos[Trk::locY];
960 minPos = std::min(minPos, lpy);
961 maxPos = std::max(maxPos, lpy);
962 ATH_MSG_DEBUG(" " << m_idHelperSvc->toString(phiHit->identify()));
963 cleanPhihits.push_back(phiHit);
964 }
965 }
966 if (cleanPhihits.size() > 1)
967 ATH_MSG_DEBUG(" multiple phi hits: nhits " << cleanPhihits.size() << " cl width " << maxPos - minPos);
968 } else {
969 ATH_MSG_DEBUG(" ARRRGGG got two empty pointers!!! ");
970 }
971 Identifier detElId = m_idHelperSvc->detElId(gasGapId);
972 if (std::abs(error) < 0.001) {
973 ATH_MSG_WARNING(" Unphysical error assigned for gasgap " << m_idHelperSvc->toString(gasGapId));
974 error = 1.;
975 }
976 return Cluster2D(detElId, gasGapId, Amg::Vector2D(lpx, lpy), error, etaHit, !cleanPhihits.empty() ? cleanPhihits : phiHits);
977 }
978
980 const Amg::Transform3D& gToStation) const {
982
983 const int chPhi = m_idHelperSvc->mdtIdHelper().stationPhi(chid);
984
985 // loop over clusters
986 int index{-1};
987 cls.reserve(spVec.size());
988 for (const Cluster2D& clust : spVec) {
989 ++index;
990 const MuonClusterOnTrack* meas = clust.etaHit ? clust.etaHit : clust.phiHit;
991 // construct cluster id
992 const Identifier id = meas->identify();
993 const int measuresPhi = m_idHelperSvc->measuresPhi(id);
994 const int eta = m_idHelperSvc->stationEta(id);
995 const int phi = m_idHelperSvc->stationPhi(id);
996 const int isTgc = m_idHelperSvc->isTgc(id);
997 const int name = isTgc ? m_idHelperSvc->tgcIdHelper().stationName(id) : m_idHelperSvc->rpcIdHelper().stationName(id);
998 if (!isTgc) {
999 if (chPhi != phi) {
1000 ATH_MSG_VERBOSE(" Discarding cluster, wrong station phi " << m_idHelperSvc->toString(id));
1001 continue;
1002 }
1003 }
1004 TrkDriftCircleMath::ClusterId clid{name, eta, phi, isTgc, measuresPhi};
1005
1006 // calculate local cluster position
1007 Amg::Vector3D locPos = gToStation * clust.globalPos;
1008 TrkDriftCircleMath::LocVec2D lp(locPos.y(), locPos.z());
1009
1010 if (std::abs(lp.y()) > m_maxAssociateClusterDistance) {
1011 ATH_MSG_VERBOSE(" Discarding cluster with large distance from chamber " << m_idHelperSvc->toString(id));
1012 continue;
1013 }
1014 ATH_MSG_VERBOSE(" " << m_idHelperSvc->toString(id) << " clid: " << clid.id() << " central phi "
1015 << meas->detectorElement()->center().phi() << " index " << index);
1016 cls.emplace_back(lp, clust.error, clid, meas, index);
1017 }
1018 return cls;
1019 }
1020
1021 TrkDriftCircleMath::DCVec DCMathSegmentMaker::createDCVec(const std::vector<const MdtDriftCircleOnTrack*>& mdts, double errorScale,
1022 std::set<Identifier>& chamberSet, double& phimin, double& phimax,
1023 TrkDriftCircleMath::DCStatistics& dcStatistics, const Amg::Transform3D& gToStation,
1024 const Amg::Transform3D& amdbToGlobal) const {
1026 dcs.reserve(mdts.size());
1027 /* ******** Mdt hits ******** */
1028 bool firstMdt = true;
1029
1030 for (const MdtDriftCircleOnTrack* rot : mdts) {
1031
1032 Identifier id = rot->identify();
1033 Identifier elId = m_idHelperSvc->mdtIdHelper().elementID(id);
1034
1035 // calculate local AMDB position
1036 Amg::Vector3D locPos = gToStation * rot->prepRawData()->globalPosition();
1037 TrkDriftCircleMath::LocVec2D lpos(locPos.y(), locPos.z());
1038
1039 double r = rot->localParameters()[Trk::locR];
1040 double dr = Amg::error(rot->localCovariance(), Trk::locR) * errorScale;
1041
1042 // create identifier
1043 TrkDriftCircleMath::MdtId mdtid(m_idHelperSvc->mdtIdHelper().isBarrel(id), m_idHelperSvc->mdtIdHelper().multilayer(id) - 1,
1044 m_idHelperSvc->mdtIdHelper().tubeLayer(id) - 1, m_idHelperSvc->mdtIdHelper().tube(id) - 1);
1045
1046 //
1047 double preciseError = dr;
1048 if (m_usePreciseError) { preciseError = m_preciseErrorScale * (0.23 * std::exp(-std::abs(r) / 6.06) + 0.0362); }
1049 // create new DriftCircle
1050 TrkDriftCircleMath::DriftCircle dc(lpos, r, dr, preciseError, TrkDriftCircleMath::DriftCircle::InTime, mdtid, rot);
1051
1052 TubeEnds tubeEnds = localTubeEnds(*rot, gToStation, amdbToGlobal);
1053 if (firstMdt) {
1054 phimin = tubeEnds.phimin;
1055 phimax = tubeEnds.phimax;
1056 firstMdt = false;
1057 } else {
1058 updatePhiRanges(tubeEnds.phimin, tubeEnds.phimax, phimin, phimax);
1059 }
1060
1061 ATH_MSG_VERBOSE(" new MDT hit " << m_idHelperSvc->toString(id) << " x " << lpos.x() << " y " << lpos.y() << " time "
1062 << rot->driftTime() << " r " << r << " dr " << dr << " phi range " << tubeEnds.phimin << " "
1063 << tubeEnds.phimax<<" precise error "<<preciseError);
1064 dcs.push_back(std::move(dc));
1065
1066 chamberSet.insert(elId);
1067
1068 ++dcStatistics[rot->prepRawData()->detectorElement()];
1069 }
1070
1071 return dcs;
1072 }
1073
1075 const Amg::Transform3D& gToStation) const {
1076 /* calculate chamber geometry
1077 it takes as input:
1078 distance between the first and second tube in the chamber within a layer along the tube layer (tube distance)
1079 distance between the first tube in the first layer and the first tube in the second layer along the tube layer
1080 (tube stagering) distance between the first and second layer perpendicular to the tube layers (layer distance)
1081 position of the first hit in ml 0 and ml 1 (2D in plane)
1082 total number of multilayers
1083 total number of layers
1084 total number of tubes per layer for each multilayer
1085 an identifier uniquely identifying the chamber
1086 */
1087
1088 Amg::Vector3D firstTubeMl0{Amg::Vector3D::Zero()};
1089 Amg::Vector3D firstTubeMl1{Amg::Vector3D::Zero()};
1090
1091 // get id
1092 int eta = m_idHelperSvc->mdtIdHelper().stationEta(chid);
1093 int phi = m_idHelperSvc->mdtIdHelper().stationPhi(chid);
1094 int name = m_idHelperSvc->mdtIdHelper().stationName(chid);
1095
1097 const MuonGM::MuonDetectorManager* MuonDetMgr{*DetectorManagerHandle};
1098 if (!MuonDetMgr) {
1099 ATH_MSG_ERROR("Null pointer to the read MuonDetectorManager conditions object");
1100 return {};
1101 }
1102
1103 // get detEL for first ml (always there)
1104 const MuonGM::MdtReadoutElement* detEl1 =
1105 MuonDetMgr->getMdtReadoutElement(m_idHelperSvc->mdtIdHelper().channelID(name, eta, phi, 1, 1, 1));
1106 const MuonGM::MdtReadoutElement* detEl2 = nullptr;
1107 int ntube2 = 0;
1108 // number of multilayers in chamber
1109 int nml = detEl1->nMDTinStation();
1110
1111 // treament of chambers with two ml
1112 if (nml == 2) {
1113 Identifier firstIdml1 = m_idHelperSvc->mdtIdHelper().channelID(name, eta, phi, 2, 1, 1);
1114 detEl2 = MuonDetMgr->getMdtReadoutElement(firstIdml1);
1115 firstTubeMl1 = gToStation * (detEl2->surface(firstIdml1).center());
1116 ntube2 = detEl2->getNtubesperlayer();
1117 }
1118
1119 // number of layers and tubes
1120 int nlay = detEl1->getNLayers();
1121 int ntube1 = detEl1->getNtubesperlayer();
1122
1123 // position first tube in ml 0
1124 Identifier firstIdml0 = m_idHelperSvc->mdtIdHelper().channelID(name, eta, phi, 1, 1, 1);
1125 firstTubeMl0 = gToStation * (detEl1->surface(firstIdml0).center());
1126
1127 // position second tube in ml 0
1128 Identifier secondIdml0 = m_idHelperSvc->mdtIdHelper().channelID(name, eta, phi, 1, 1, 2);
1129 Amg::Vector3D secondTubeMl0 = gToStation * (detEl1->surface(secondIdml0).center());
1130
1131 TrkDriftCircleMath::LocVec2D firstTube0(firstTubeMl0.y(), firstTubeMl0.z());
1132 TrkDriftCircleMath::LocVec2D firstTube1(firstTubeMl1.y(), firstTubeMl1.z());
1133
1134 // position first tube ml 0 and 1
1135 Identifier firstIdml0lay1 = m_idHelperSvc->mdtIdHelper().channelID(name, eta, phi, 1, 2, 1);
1136 Amg::Vector3D firstTubeMl0lay1 = gToStation * (detEl1->surface(firstIdml0lay1).center());
1137
1138 double tubeDist = (secondTubeMl0 - firstTubeMl0).y(); // distance between tube in a given layer
1139 double tubeStage = (firstTubeMl0lay1 - firstTubeMl0).y(); // tube stagering distance
1140 double layDist = (firstTubeMl0lay1 - firstTubeMl0).z(); // distance between layers
1141
1142 TrkDriftCircleMath::MdtChamberGeometry mdtgeo(chid, m_idHelperSvc.get(), nml, nlay, ntube1, ntube2, firstTube0, firstTube1, tubeDist, tubeStage,
1143 layDist, detEl1->surface().center().theta());
1144
1145 if (msgLvl(MSG::VERBOSE)) mdtgeo.print(msgStream());
1146
1147 return mdtgeo;
1148 }
1149
1151 const Amg::Vector3D& gdir, TrkDriftCircleMath::Segment& segment,
1152 const TrkDriftCircleMath::ChamberGeometry* multiGeo, const Amg::Transform3D& gToStation, const Amg::Transform3D& amdbToGlobal,
1153 std::set<Identifier>& deltaVec, std::set<Identifier>& outoftimeVec,
1154 std::vector<std::pair<double, std::unique_ptr<const Trk::MeasurementBase>> >& rioDistVec, double beta) const {
1155 // clear result vectors
1156
1157 // convert segment parameters + x position from road
1158 const TrkDriftCircleMath::Line& line = segment.line();
1161 if (segment.hasCurvatureParameters()) {
1162 // ml2 segment direction
1163 double ml2phi = line.phi() - segment.deltaAlpha();
1164 TrkDriftCircleMath::LocVec2D ml2dir(std::cos(ml2phi), std::sin(ml2phi));
1165 // ml2 segment position
1166 const TrkDriftCircleMath::LocVec2D ml1LocPos = multiGeo->tubePosition(0, multiGeo->nlay(), 0);
1167 const TrkDriftCircleMath::LocVec2D ml2LocPos = multiGeo->tubePosition(1, 1, 0);
1168 double chamberMidPtY = (ml1LocPos.y() + ml2LocPos.y()) / 2.0;
1169 TrkDriftCircleMath::LocVec2D ml2pos(segment.deltab(), chamberMidPtY);
1170 // construct the new ml2 segment line & transform
1171 const TrkDriftCircleMath::Line ml2line(ml2pos, ml2dir);
1172 TrkDriftCircleMath::TransformToLine tmptoLine(ml2line);
1173 // set the ml2 line
1174 toLineml2 = tmptoLine;
1175 }
1176
1177 for (TrkDriftCircleMath::DCOnTrack& dcit : segment.dcs()) {
1178 if (dcit.state() == TrkDriftCircleMath::DCOnTrack::Delta) { deltaVec.insert(dcit.rot()->identify()); }
1179
1180 if (dcit.state() == TrkDriftCircleMath::DCOnTrack::OutOfTime) { outoftimeVec.insert(dcit.rot()->identify()); }
1181
1182 if (dcit.state() != TrkDriftCircleMath::DCOnTrack::OnTrack) continue;
1183
1184 const MdtDriftCircleOnTrack* riodc{dcit.rot()};
1185
1186 // choose which line to use (ml1 or ml2)
1187 TrkDriftCircleMath::TransformToLine toLine = toLineml1;
1188 if (m_idHelperSvc->mdtIdHelper().multilayer(riodc->identify()) == 2) toLine = toLineml2;
1189 // calculate position of hit in line frame
1190 TrkDriftCircleMath::LocVec2D pointOnHit = toLine.toLine(dcit.position());
1191
1192 // calculate position of hit on line in line frame
1193 TrkDriftCircleMath::LocVec2D pointOnLine(pointOnHit.x(), 0.);
1194
1195 // transform back to local AMDB coordinates
1196 TrkDriftCircleMath::LocVec2D pointOnLineAMDB = toLine.toLocal(pointOnLine);
1197
1198 // get position along wire from ROT
1199 Amg::Vector3D posAlong = gToStation * riodc->globalPosition();
1200
1201 // set yz components
1202 posAlong[1] = pointOnLineAMDB.x();
1203 posAlong[2] = pointOnLineAMDB.y();
1204
1205 // back to global
1206 Amg::Vector3D mdtGP = amdbToGlobal * posAlong;
1207
1208 const Trk::StraightLineSurface* surf = dynamic_cast<const Trk::StraightLineSurface*>(&riodc->associatedSurface());
1209 if (!surf) {
1210 ATH_MSG_WARNING(" dynamic cast to StraightLineSurface failed for mdt!!! ");
1211 continue;
1212 }
1213
1214 // calculate Amg::Vector2D using surf to obtain sign
1215 Amg::Vector2D locPos{Amg::Vector2D::Zero()};
1216 if (!surf->globalToLocal(mdtGP, gdir, locPos)) ATH_MSG_WARNING(" globalToLocal failed ");
1217
1218 // calculate side
1220
1221 std::unique_ptr<MdtDriftCircleOnTrack> nonconstDC;
1222 bool hasT0 = segment.hasT0Shift();
1223 if (!hasT0 || m_mdtCreatorT0.empty()) {
1224 // ATH_MSG_VERBOSE(" recalibrate MDT hit");
1225 nonconstDC.reset(m_mdtCreator->createRIO_OnTrack(*riodc->prepRawData(), mdtGP, &gdir, 0., nullptr, beta, 0.));
1226 if (hasT0) ATH_MSG_WARNING("Attempted to change t0 without a properly configured MDT creator tool. ");
1227 } else {
1228 ATH_MSG_VERBOSE(" recalibrate MDT hit with shift " << segment.t0Shift()<<" "<<m_printer->print(*riodc));
1229 nonconstDC.reset(m_mdtCreatorT0->createRIO_OnTrack(*riodc->prepRawData(), mdtGP, &gdir, segment.t0Shift()));
1230 }
1231
1232 if (!nonconstDC) {
1234 continue;
1235 }
1236 ATH_MSG_VERBOSE("Post calibrated hit "<<m_printer->print(*nonconstDC));
1237
1238 // update the drift radius after recalibration, keep error
1239 TrkDriftCircleMath::DriftCircle new_dc(dcit.position(), std::abs(nonconstDC->driftRadius()), dcit.dr(), dcit.drPrecise(),
1240 dcit.driftState(), dcit.id(),
1241 nonconstDC.get());
1242 TrkDriftCircleMath::DCOnTrack new_dc_on_track(new_dc, dcit.residual(), dcit.errorTrack());
1243 dcit = std::move(new_dc_on_track);
1244
1245 if (hasT0) {
1246 if (msgLvl(MSG::VERBOSE)) {
1247 double shift = riodc->driftTime() - nonconstDC->driftTime();
1248 ATH_MSG_VERBOSE(" t0 shift " << segment.t0Shift() << " from hit " << shift << " recal " << nonconstDC->driftRadius()
1249 << " t " << nonconstDC->driftTime() << " from fit " << dcit.r() << " old "
1250 << riodc->driftRadius() << " t " << riodc->driftTime());
1251 if (std::abs(std::abs(nonconstDC->driftRadius()) - std::abs(dcit.r())) > 0.1 && nonconstDC->driftRadius() < 19. &&
1252 nonconstDC->driftRadius() > 1.) {
1253 ATH_MSG_WARNING("Detected invalid recalibration after T0 shift");
1254 }
1255 }
1256 }
1257 m_mdtCreator->updateSign(*nonconstDC, side);
1258 double dist = pointOnHit.x();
1259 rioDistVec.emplace_back(dist, std::move(nonconstDC));
1260 }
1261 }
1262
1263 template <class T> struct IdDataVec {
1264 using Entry = T;
1265 using EntryVec = std::vector<Entry>;
1266
1267 IdDataVec() = default;
1268 explicit IdDataVec(const Identifier& i) : id(i) {}
1269
1272 };
1273
1274 template <class T> struct SortIdDataVec {
1275 bool operator()(const IdDataVec<T>& d1, const IdDataVec<T>& d2) { return d1.id < d2.id; }
1276 };
1277
1279 bool operator()(const std::pair<double, DCMathSegmentMaker::Cluster2D>& d1,
1280 const std::pair<double, DCMathSegmentMaker::Cluster2D>& d2) {
1281 return std::abs(d1.first) < std::abs(d2.first);
1282 }
1283 };
1284
1285 std::pair<std::pair<int, int>, bool> DCMathSegmentMaker::associateClustersToSegment(
1286 const TrkDriftCircleMath::Segment& segment, const Identifier& chid, const Amg::Transform3D& gToStation, ClusterVecPair& spVecs,
1287 double phimin, double phimax, std::vector<std::pair<double, std::unique_ptr<const Trk::MeasurementBase>> >& rioDistVec) const {
1289 typedef IdDataVec<GasGapData> ChamberData;
1290 typedef std::vector<ChamberData> ChamberDataVec;
1291 ChamberDataVec chamberDataVec;
1292 bool isEndcap = m_idHelperSvc->isEndcap(chid);
1293
1294 // keep track of the number of eta/phi hits on the segment
1295 bool refit = false;
1296 std::pair<std::pair<int, int>, bool> netaPhiHits(std::make_pair(0, 0), false);
1297 if (segment.clusters().empty()) return netaPhiHits;
1298
1299 std::vector<const Trk::MeasurementBase*> phiHits;
1300
1301 // only refit if there are sufficient phi hits and no multiple phi hits per surface
1302 refit = true;
1303
1304 // keep track of detector elements which space points added to the track
1305 std::set<Identifier> detElOnSegments;
1306 std::set<MuonStationIndex::PhiIndex> phiIndices;
1307
1308 ATH_MSG_DEBUG(" Associating clusters: " << segment.clusters().size() << " number of space points " << spVecs.first.size());
1309
1310 // associate space points and sort them per detector element and gas gap
1311 for (const TrkDriftCircleMath::Cluster& clust : segment.clusters()) {
1312
1313 const Cluster2D& spacePoint = spVecs.first[clust.index()];
1314 ATH_MSG_VERBOSE(" accessing cluster: " << clust.index()<<", "
1315 <<m_idHelperSvc->toString(spacePoint.identify()));
1316 // skip corrupt space points
1317 if (spacePoint.corrupt()) {
1318 ATH_MSG_DEBUG(" Found corrupt space point: index " << clust.index());
1319 continue;
1320 }
1321 // reject TGC clusters that are not 2D
1322 if (m_reject1DTgcSpacePoints && !spacePoint.is2D() && m_idHelperSvc->isTgc(spacePoint.identify())) {
1323 ATH_MSG_DEBUG(" Rejecting 1D tgc space point " << m_idHelperSvc->toString(spacePoint.identify()));
1324 continue;
1325 }
1326 if (m_assumePointingPhi && spacePoint.is2D() && !checkPhiConsistency(spacePoint.globalPos.phi(), phimin, phimax)) {
1327 ATH_MSG_DEBUG(" Inconsistent phi angle, dropping space point: phi " << spacePoint.globalPos.phi() << " range " << phimin
1328 << " " << phimax);
1329 continue;
1330 }
1331
1332 std::pair<double, double> resPull = residualAndPullWithSegment(segment, spacePoint, gToStation);
1333
1334 // if empty or new chamber, add chamber
1335 if (chamberDataVec.empty() || chamberDataVec.back().id != spacePoint.detElId) {
1336 detElOnSegments.insert(spacePoint.detElId);
1337 chamberDataVec.emplace_back(spacePoint.detElId);
1338 MuonStationIndex::PhiIndex phiIndex = m_idHelperSvc->phiIndex(spacePoint.detElId);
1339 phiIndices.insert(phiIndex);
1340 }
1341
1342 // reference to current chamber data
1343 ChamberData& chamber = chamberDataVec.back();
1344
1345 // if same detector element
1346 if (spacePoint.detElId == chamber.id) {
1347 // if chamber empty or new gas gap, add gasp gap
1348 if (chamber.data.empty() || chamber.data.back().id != spacePoint.gasGapId) {
1349 chamber.data.emplace_back(spacePoint.gasGapId);
1350 }
1351 }
1352
1353 // reference to current gas gap data
1354 GasGapData& gasGap = chamber.data.back();
1355 gasGap.data.emplace_back(resPull.second, spacePoint);
1356 }
1357
1358 // calculate the distance between the first and last station, use r in barrel and z in endcaps
1359 double posFirstPhiStation{FLT_MAX}, posLastPhiStation{0.};
1360
1361 // loop over chambers and create competing ROTs per chamber
1362 for (ChamberData& chamb : chamberDataVec) {
1363 // select best clusters per gas gap in chamber
1364 std::list<const Trk::PrepRawData*> etaClusterVec{}, phiClusterVec{};
1365 std::unordered_set<Identifier> etaIds;
1366 // loop over gas gaps
1367 for (GasGapData& gasGap : chamb.data) {
1368 // sort space points by their pull with the segment
1369 std::ranges::sort(gasGap.data, SortClByPull());
1370
1371 // select all space points with a pull that is within 1 of the best pull
1372 double bestPull = std::abs(gasGap.data.front().first);
1373
1374 // count number of associated clusters in gas gap
1375 unsigned int nassociatedSp = 0;
1376 GasGapData::EntryVec::const_iterator cl_it = gasGap.data.begin();
1377 while (cl_it != gasGap.data.end() && std::abs(cl_it->first) - bestPull < 1.) {
1378 const Cluster2D& sp = cl_it->second;
1380 double dist = distanceToSegment(segment, sp.globalPos, gToStation);
1381 ATH_MSG_VERBOSE(" selected space point: " << m_idHelperSvc->toString(sp.identify()) << " pull "
1382 << std::abs(cl_it->first) << " distance to segment " << dist << " phi "
1383 << sp.globalPos.phi());
1384
1385 // here keep open the option not to create CompetingMuonClustersOnTrack
1386 if (sp.etaHit) {
1387 if (etaIds.insert(sp.etaHit->identify()).second) {
1388 // BI rpc measurements have 2D coordinates
1390 sp.etaHit->prepRawData()->localCovariance().cols() == 1) {
1391 etaClusterVec.push_back(sp.etaHit->prepRawData());
1392 } else {
1393 rioDistVec.emplace_back(dist, sp.etaHit->uniqueClone());
1394 ++netaPhiHits.first.first;
1395 }
1396 }
1397 }
1398 if (!sp.phiHits.empty()) {
1400 // can have multiple phi hits per cluster, loop over phi hits and add them
1401 std::ranges::transform(sp.phiHits, std::back_inserter(phiClusterVec),
1402 [](const Muon::MuonClusterOnTrack* clus){
1403 return clus->prepRawData();
1404 });
1405 } else {
1406 // can have multiple phi hits per cluster, loop over phi hits and add them
1407 for (const MuonClusterOnTrack* phi_hit : sp.phiHits) {
1408 rioDistVec.emplace_back(dist, phi_hit->uniqueClone());
1409 ++netaPhiHits.first.second;
1410 phiHits.push_back(phi_hit);
1411
1412 // calculate position
1413 double phiPos = isEndcap ? std::abs(phi_hit->globalPosition().z()) : phi_hit->globalPosition().perp();
1414 posFirstPhiStation = std::min(phiPos, posFirstPhiStation);
1415 posLastPhiStation = std::max(phiPos, posLastPhiStation);
1416 }
1417 if (sp.phiHits.size() > 1) refit = false;
1418 }
1419 }
1420 ++nassociatedSp;
1421 ++cl_it;
1422 }
1423 // multiple clusters in same gas gap, don't refit
1424 if (!m_createCompetingROTsPhi && nassociatedSp > 1) refit = false;
1425 }
1426
1428 // create competing ROT for eta hits
1429 if (!etaClusterVec.empty()) {
1430 std::unique_ptr<const CompetingMuonClustersOnTrack> etaCompCluster = m_compClusterCreator->createBroadCluster(etaClusterVec, 0.);
1431 if (!etaCompCluster) {
1432 ATH_MSG_DEBUG(" failed to create competing ETA ROT " << etaClusterVec.size());
1433 } else {
1434 double dist = distanceToSegment(segment, etaCompCluster->globalPosition(), gToStation);
1435 ++netaPhiHits.first.first;
1436 if (msgLvl(MSG::VERBOSE)) {
1437 ATH_MSG_VERBOSE(" selected cluster: " << m_idHelperSvc->toString(etaClusterVec.front()->identify()));
1438 for (unsigned int i = 0; i < etaCompCluster->containedROTs().size(); ++i) {
1440 " content: " << m_idHelperSvc->toString(etaCompCluster->containedROTs()[i]->identify()));
1441 }
1442 }
1443 rioDistVec.emplace_back(dist, std::move(etaCompCluster));
1444 }
1445 }
1446 }
1447
1449 // create competing ROT for phi hits
1450 if (!phiClusterVec.empty()) {
1451 std::unique_ptr<const CompetingMuonClustersOnTrack> phiCompCluster = m_compClusterCreator->createBroadCluster(phiClusterVec, 0.);
1452 if (!phiCompCluster) {
1453 ATH_MSG_DEBUG(" failed to create competing PHI ROT " << phiClusterVec.size());
1454 } else {
1455 double dist = distanceToSegment(segment, phiCompCluster->globalPosition(), gToStation);
1456 phiHits.push_back(phiCompCluster.get());
1457
1458 ++netaPhiHits.first.second;
1459
1460 if (msgLvl(MSG::VERBOSE)) {
1461 ATH_MSG_VERBOSE(" selected cluster: " << m_idHelperSvc->toString(phiClusterVec.front()->identify()));
1462 for (unsigned int i = 0; i < phiCompCluster->containedROTs().size(); ++i) {
1464 " content: " << m_idHelperSvc->toString(phiCompCluster->containedROTs()[i]->identify()));
1465 }
1466 }
1467
1468
1469 // calculate position
1470 double phiPos = isEndcap ? std::abs(phiCompCluster->globalPosition().z()) :
1471 phiCompCluster->globalPosition().perp();
1472 posFirstPhiStation = std::min(phiPos,posFirstPhiStation);
1473 posLastPhiStation = std::max(phiPos,posLastPhiStation);
1474 rioDistVec.emplace_back(dist, std::move(phiCompCluster));
1475
1476 }
1477 }
1478 }
1479 }
1480
1481 // add phi hits that were not associated with an eta hit (only in barrel)
1482 if ((!spVecs.second.empty() || m_recoverBadRpcCabling) && m_addUnassociatedPhiHits && !isEndcap) {
1483 TrkDriftCircleMath::ResidualWithSegment resWithSegment(segment);
1484
1485 std::map<Identifier, std::list<const Trk::PrepRawData*> > phiClusterMap;
1486
1487 std::set<const MuonClusterOnTrack*> selectedClusters;
1488 std::vector<const Cluster2D*> phiClusters;
1489 phiClusters.reserve(spVecs.second.size());
1490
1491 // create lists of PrepRawData per detector element
1492 for (const Cluster2D& phi_clus :spVecs.second) {
1493 if (!phi_clus.phiHit || phi_clus.corrupt()) {
1494 ATH_MSG_WARNING(" phi clusters without phi hit!!");
1495 continue;
1496 }
1497 phiClusters.push_back(&phi_clus);
1498 selectedClusters.insert(phi_clus.phiHit);
1499 }
1500
1501 unsigned int recoveredUnassociatedPhiHits(0);
1503 // now loop over 2D space points and add the phi hits to the list if the detEl is not yet added to the
1504 // segment
1505 for (const Cluster2D& rpc_clust : spVecs.first) {
1506 // skip clusters without phi hit
1507 if (!rpc_clust.phiHit || rpc_clust.corrupt()) continue;
1508
1509 // skip clusters in detector elements that are already associated (ok as this is only done for RPC)
1510 if (detElOnSegments.count(rpc_clust.detElId)) continue;
1511
1512 MuonStationIndex::PhiIndex phiIndex = m_idHelperSvc->phiIndex(rpc_clust.detElId);
1513 // skip clusters in detector layer
1514 if (phiIndices.count(phiIndex)) continue;
1515
1516 bool wasFound = false;
1517 for (const MuonClusterOnTrack* phi_hit : rpc_clust.phiHits) {
1518 // now to avoid duplicate also skip if the given ROT is already in the list
1519 if (!selectedClusters.insert(phi_hit).second) {
1520 // flag as found
1521 wasFound = true;
1522
1523 // just because I'm paranoid, remove the hits from this cluster that were already inserted up to
1524 // here
1525 for (const MuonClusterOnTrack* erase_me : rpc_clust.phiHits) {
1526 if (erase_me == phi_hit) break;
1527 selectedClusters.erase(erase_me);
1528 }
1529 break;
1530 }
1531 }
1532 if (wasFound) continue;
1533
1534 // if we get here we should add the hit
1535 phiClusters.push_back(&rpc_clust);
1536 ++recoveredUnassociatedPhiHits;
1537 }
1538 }
1539
1540 unsigned int addedPhiHits(0);
1541 for (const Cluster2D* phi_clus : phiClusters) {
1542 const Identifier& detElId = phi_clus->detElId;
1543
1544 // check that detector element is not already added to segment
1545 if (detElOnSegments.count(detElId)) continue;
1546
1547 MuonStationIndex::PhiIndex phiIndex = m_idHelperSvc->phiIndex(detElId);
1548 // skip clusters in detector layer
1549 if (phiIndices.count(phiIndex)) continue;
1550
1551 // calculate local cluster position
1552 Amg::Vector3D locPos = gToStation * phi_clus->globalPos;
1553
1554 // calculate intersect of segment with cluster
1555 TrkDriftCircleMath::Cluster cl(TrkDriftCircleMath::LocVec2D(locPos.y(), locPos.z()), 1.);
1556 double residual = resWithSegment.residual(cl);
1557 double segError = std::sqrt(resWithSegment.trackError2(cl));
1558 const MuonGM::RpcReadoutElement* detEl = dynamic_cast<const MuonGM::RpcReadoutElement*>(phi_clus->phiHit->detectorElement());
1559 if (!detEl) {
1560 ATH_MSG_WARNING("found RPC prd without RpcReadoutElement");
1561 continue;
1562 }
1563
1564 // perform bound check
1565 double stripLength = detEl->StripLength(1);
1566 bool inBounds = std::abs(residual) < 0.5 * stripLength + 2. + segError;
1567 if (msgLvl(MSG::DEBUG)) {
1568 ATH_MSG_DEBUG(" Unassociated " << m_idHelperSvc->toString(phi_clus->phiHit->identify()) << " pos x " << cl.position().x()
1569 << " pos y " << cl.position().y() << " : residual " << residual << " strip half length "
1570 << 0.5 * stripLength << " segment error " << segError);
1571 if (inBounds)
1572 ATH_MSG_DEBUG(" inBounds");
1573 else
1574 ATH_MSG_DEBUG(" outBounds");
1575 }
1576 if (inBounds) {
1577 // can have multiple phi hits per cluster, loop over phi hits and add them
1578 std::list<const Trk::PrepRawData*>& cham_hits{phiClusterMap[detElId]};
1579 std::transform(phi_clus->phiHits.begin(), phi_clus->phiHits.end(), std::back_inserter(cham_hits),
1580 [](const MuonClusterOnTrack* clus){
1581 return clus->prepRawData();
1582 });
1583 }
1584 }
1585
1586 // loop over detector elements and created competing ROTs
1587 for (const auto& [phi_id, prds] : phiClusterMap) {
1588 if (prds.empty()) {
1589 ATH_MSG_WARNING(" chamber without phi hits " << m_idHelperSvc->toString(phi_id));
1590 continue;
1591 }
1592
1593 std::unique_ptr<const CompetingMuonClustersOnTrack> phiCompCluster = m_compClusterCreator->createBroadCluster(prds, 0.);
1594 if (!phiCompCluster) {
1595 ATH_MSG_DEBUG(" failed to create competing PHI ROT " << prds.size());
1596 } else {
1597 double dist = distanceToSegment(segment, phiCompCluster->globalPosition(), gToStation);
1598
1599 if (std::abs(dist) > m_maxAssociateClusterDistance) {
1600
1601 ATH_MSG_VERBOSE(" rejected unassociated cluster: " << m_idHelperSvc->toString(prds.front()->identify())
1602 << " distance to segment " << dist);
1603 continue;
1604 }
1605 phiHits.push_back(phiCompCluster.get());
1606 ++netaPhiHits.first.second;
1607 ++addedPhiHits;
1608 if (msgLvl(MSG::VERBOSE)) {
1609 ATH_MSG_VERBOSE(" selected unassociated cluster: " << m_idHelperSvc->toString(prds.front()->identify())
1610 << " distance to segment " << dist);
1611 for (unsigned int i = 0; i < phiCompCluster->containedROTs().size(); ++i) {
1613 " content: " << m_idHelperSvc->toString(phiCompCluster->containedROTs()[i]->identify()));
1614 }
1615 }
1616 rioDistVec.emplace_back(dist, std::move(phiCompCluster));
1617 }
1618 }
1619 ATH_MSG_VERBOSE("Added " << addedPhiHits << " unass phi hits out of " << spVecs.second.size()
1620 << " phi hits without eta hit and " << recoveredUnassociatedPhiHits << " with unassociated eta hit ");
1621 }
1622
1623 // calculate distance between first and last phi trigger hit, refit if there is a good constraint on phi
1624 double phiDistanceMax = posLastPhiStation - posFirstPhiStation;
1625 if (isEndcap && phiDistanceMax < 1000.)
1626 refit = false;
1627 else if (phiDistanceMax < 400.)
1628 refit = false;
1629
1630 netaPhiHits.second = refit;
1631 return netaPhiHits;
1632 }
1633
1635 const Amg::Transform3D& gToStation) {
1636 const TrkDriftCircleMath::Line& line = segment.line();
1638 double cos_sinLine = cot(line.phi());
1639
1640 // calculate local AMDB position
1641 Amg::Vector3D locPos = gToStation * hitPos;
1642
1643 TrkDriftCircleMath::LocVec2D lpos(locPos.y(), locPos.z());
1644
1645 // calculate distance of segment to measurement surface
1646 double delta_y = lpos.y() - line.position().y();
1647
1648 // calculate position of hit in line frame
1649 TrkDriftCircleMath::LocVec2D lineSurfaceIntersect(delta_y * cos_sinLine + line.position().x(), lpos.y());
1650
1651 // calculate position of hit in line frame
1652 TrkDriftCircleMath::LocVec2D pointOnHit = toLine.toLine(lineSurfaceIntersect);
1653
1654 return pointOnHit.x();
1655 }
1656
1658 std::vector<std::pair<double, std::unique_ptr<const Trk::MeasurementBase>> >& rioDistVec) {
1659 // sort hits according to they distance to the segment position
1660 std::sort(rioDistVec.begin(), rioDistVec.end(), SortByDistanceToSegment());
1661
1663 rioVec.reserve(rioDistVec.size());
1664 for (std::pair<double, std::unique_ptr<const Trk::MeasurementBase>>& rdit : rioDistVec) { rioVec.push_back(std::move(rdit.second)); }
1665 rioDistVec.clear();
1666 return rioVec;
1667 }
1668
1670 const Cluster2D& spacePoint,
1671 const Amg::Transform3D& gToStation) {
1672 const TrkDriftCircleMath::Line& line = segment.line();
1673 double cos_sinLine = cot(line.phi());
1674
1675 // calculate sp postion in AMDB reference frame
1676 Amg::Vector3D locPos = gToStation * spacePoint.globalPos;
1677 TrkDriftCircleMath::LocVec2D lpos(locPos.y(), locPos.z());
1678
1679 // calculate distance of segment to measurement surface
1680 double delta_y = lpos.y() - line.position().y();
1681
1682 // calculate position of hit in line frame
1683 TrkDriftCircleMath::LocVec2D lineSurfaceIntersect(delta_y * cos_sinLine + line.position().x(), lpos.y());
1684
1685 // calculate position of hit in line frame
1686 double residual = lpos.x() - lineSurfaceIntersect.x();
1687 double pull = residual / spacePoint.error;
1688 return std::make_pair(residual, pull);
1689 }
1690
1691 std::vector<Identifier> DCMathSegmentMaker::calculateHoles(const EventContext& ctx,
1692 Identifier chid, const Amg::Vector3D& gpos, const Amg::Vector3D& gdir, bool hasMeasuredCoordinate, std::set<Identifier>& deltaVec,
1693 std::set<Identifier>& outoftimeVec, const std::vector<std::pair<double, std::unique_ptr<const Trk::MeasurementBase>> >& rioDistVec) const {
1694 // calculate crossed tubes
1696 if (!InterSectSvc.isValid()) {
1697 ATH_MSG_ERROR("Null pointer to the read MuonDetectorManager conditions object");
1698 return {};
1699 }
1701 const MuonGM::MuonDetectorManager* MuonDetMgr = detMgr.cptr();
1702
1703 const MuonStationIntersect intersect = InterSectSvc->tubesCrossedByTrack(MuonDetMgr, chid, gpos, gdir);
1704
1705 // set to identify the hit on the segment
1706 std::set<Identifier> hitsOnSegment, chambersOnSegment;
1707 int firstLayer{-1}, lastLayer{-1};
1708 for (const std::pair<double, std::unique_ptr<const Trk::MeasurementBase>>& rdit : rioDistVec) {
1709 const MdtDriftCircleOnTrack* mdt = dynamic_cast<const MdtDriftCircleOnTrack*>(rdit.second.get());
1710 if (!mdt) continue;
1711 const Identifier id = mdt->identify();
1712 int layer = (m_idHelperSvc->mdtIdHelper().tubeLayer(id) - 1) + 4 * (m_idHelperSvc->mdtIdHelper().multilayer(id) - 1);
1713 if (firstLayer == -1)
1714 firstLayer = layer;
1715 else
1716 lastLayer = layer;
1717
1718 hitsOnSegment.insert(id);
1719 chambersOnSegment.insert(m_idHelperSvc->chamberId(id));
1720 }
1721
1722 // cross check for cosmic case
1723 if (firstLayer > lastLayer) { std::swap(firstLayer, lastLayer); }
1724 ATH_MSG_VERBOSE(" Tube layer ranges: " << firstLayer << " -- " << lastLayer << " crossed tubes "
1725 << intersect.tubeIntersects().size());
1726 // clear hole vector
1727 std::vector<Identifier> holeVec;
1728 for (const MuonTubeIntersect& tint : intersect.tubeIntersects()) {
1729 if (!chambersOnSegment.count(m_idHelperSvc->chamberId(tint.tubeId))) {
1730 ATH_MSG_VERBOSE(" chamber not on segment, not counting tube " << tint.rIntersect << " l " << tint.xIntersect << " "
1731 << m_idHelperSvc->toString(tint.tubeId));
1732 continue;
1733 }
1734
1735 const Identifier& id = tint.tubeId;
1736 int layer = (m_idHelperSvc->mdtIdHelper().tubeLayer(id) - 1) + 4 * (m_idHelperSvc->mdtIdHelper().multilayer(id) - 1);
1737
1738 bool notBetweenHits = layer < firstLayer || layer > lastLayer;
1739 double distanceCut = hasMeasuredCoordinate ? -20 : -200.;
1740 double innerRadius = MuonDetMgr->getMdtReadoutElement(id)->innerTubeRadius();
1741 if (notBetweenHits && (std::abs(tint.rIntersect) > innerRadius || (!m_allMdtHoles && tint.xIntersect > distanceCut))) {
1742 ATH_MSG_VERBOSE(" not counting tube: distance to wire " << tint.rIntersect << " dist to tube end " << tint.xIntersect
1743 << " " << m_idHelperSvc->toString(tint.tubeId));
1744 continue;
1745 }
1746 // check whether there is a hit in this tube
1747 if (hitsOnSegment.count(tint.tubeId)) {
1748 ATH_MSG_VERBOSE(" tube on segment: distance to wire " << tint.rIntersect << " dist to tube end " << tint.xIntersect
1749 << " " << m_idHelperSvc->toString(tint.tubeId));
1750 continue;
1751 }
1752 // check whether there is a delta electron in this tube
1753 if (m_removeDeltas) {
1754 if (deltaVec.count(tint.tubeId)) {
1755 ATH_MSG_VERBOSE(" removing delta, distance to wire " << tint.rIntersect << " dist to tube end " << tint.xIntersect
1756 << " " << m_idHelperSvc->toString(tint.tubeId));
1757 continue;
1758 }
1759
1760 const MdtPrepData* prd = findMdt(ctx, id);
1761 if (prd && std::abs(prd->localPosition()[Trk::locR]) < std::abs(tint.rIntersect)) {
1762 ATH_MSG_VERBOSE(" found and removed delta, distance to wire " << tint.rIntersect << " dist to tube end "
1763 << tint.xIntersect << " "
1764 << m_idHelperSvc->toString(tint.tubeId));
1765 continue;
1766 }
1767 }
1768 ATH_MSG_VERBOSE((outoftimeVec.count(tint.tubeId) ? "Out-of-time" : "hole") << " distance to wire "
1769 << tint.rIntersect << " dist to tube end " << tint.xIntersect << " "
1770 << m_idHelperSvc->toString(tint.tubeId)<<(notBetweenHits ? "outside hits" : "between hits"));
1771
1772 holeVec.push_back(tint.tubeId);
1773 }
1774 return holeVec;
1775 }
1776
1777 const MdtPrepData* DCMathSegmentMaker::findMdt(const EventContext& ctx, const Identifier& id) const {
1778 IdentifierHash colHash;
1779 if (m_idHelperSvc->mdtIdHelper().get_module_hash(m_idHelperSvc->chamberId(id), colHash)){
1780 ATH_MSG_VERBOSE("Invalid Mdt identifier "<<m_idHelperSvc->toString(id));
1781 return nullptr;
1782 }
1784 if (!MdtCont.isValid()) {
1785 ATH_MSG_WARNING("Cannot retrieve MdtPrepDataContainer ");
1786 return nullptr;
1787 }
1788 const MdtPrepDataCollection* collptr = MdtCont->indexFindPtr(colHash);
1789 if (!collptr) return nullptr;
1790 for (const MdtPrepData* prd : *collptr) {
1791 if (prd->identify() == id) return prd;
1792 }
1793 return nullptr;
1794 }
1795
1797 const std::vector<const MdtDriftCircleOnTrack*>& mdts) const {
1798 int hitsInChamberWithMostHits = 0;
1799 std::map<Identifier, int> hitsPerChamber;
1800 int currentSector = -1;
1801 const MdtDriftCircleOnTrack* rotInChamberWithMostHits = nullptr;
1802
1803 // loop over all MDTs and count number of MDTs per chamber
1804 for (const MdtDriftCircleOnTrack* rot : mdts) {
1805 if (!rot) {
1806 ATH_MSG_WARNING(" rot not a MdtDriftCircleOnTrack ");
1807 continue;
1808 }
1809 Identifier chId = m_idHelperSvc->chamberId(rot->identify());
1810 int sector = m_idHelperSvc->sector(chId);
1811 if (currentSector == -1) {
1812 currentSector = sector;
1813 } else if (sector != currentSector) {
1814 return nullptr;
1815 }
1816 int& hitsInCh = hitsPerChamber[chId];
1817 ++hitsInCh;
1818 if (hitsInCh > hitsInChamberWithMostHits) {
1819 hitsInChamberWithMostHits = hitsInCh;
1820 rotInChamberWithMostHits = rot;
1821 }
1822 }
1823 return rotInChamberWithMostHits;
1824 }
1825
1826 bool DCMathSegmentMaker::checkBoundsInXZ(double xline, double zline, double dXdZ,
1827 const std::vector<DCMathSegmentMaker::HitInXZ>& hits) const {
1828 bool ok = true;
1829
1830 // look over hits and check whether all are in bounds
1831 for (const HitInXZ& hit : hits) {
1832 bool outBounds = false;
1833 double locExX = xline + dXdZ * (hit.z - zline);
1834 if (hit.isMdt && (locExX < hit.xmin - 1. || locExX > hit.xmax + 1.)) {
1835 ok = false;
1836 outBounds = true;
1837 if (!msgLvl(MSG::DEBUG)) break;
1838 }
1839
1840 if (outBounds && msgLvl(MSG::DEBUG)) {
1841 ATH_MSG_DEBUG(" " << std::setw(65) << m_idHelperSvc->toString(hit.id) << " pos (" << std::setw(6) << (int)hit.x << ","
1842 << std::setw(6) << (int)hit.z << ") ex pos " << std::setw(6) << (int)locExX << " min " << std::setw(6)
1843 << (int)hit.xmin << " max " << std::setw(6) << (int)hit.xmax << " phimin " << std::setw(6)
1844 << hit.phimin << " phimax " << std::setw(6) << hit.phimax << " outBounds, cross-check");
1845 }
1846 }
1847 return ok;
1848 }
1849
1851 Trk::LocalDirection& segLocDir, Trk::PlaneSurface& surf,
1852 const std::vector<const Trk::MeasurementBase*>& rots, double seg_phimin,
1853 double seg_phimax) const {
1854 bool hasUpdated = false;
1855
1856 const Amg::Transform3D& segmentToGlobal = surf.transform();
1857 Amg::Transform3D gToSegment = surf.transform().inverse();
1858 Amg::Vector3D ldir = gToSegment * gdir;
1859
1860 // ensure that we can extrapolate
1861 if (ldir.z() < 0.0001) return false;
1862
1863 double dXdZ = ldir.x() / ldir.z();
1864 Amg::Vector3D lsegPos = gToSegment * gpos;
1865 double xline = lsegPos.x();
1866 double zline = lsegPos.z();
1867 ATH_MSG_VERBOSE(" Associated hits " << rots.size() << " angleXZ " << 90. * segLocDir.angleXZ() / (M_PI_2) << " dXdZ " << dXdZ
1868 << " seg Pos (" << xline << " " << zline << ") " << segLocPos);
1869
1870 std::vector<HitInXZ> hits;
1871 hits.reserve(rots.size());
1872
1873 unsigned int nphiHits(0);
1874 const HitInXZ* firstPhiHit{nullptr}, *lastPhiHit{nullptr};
1875
1876 for (const Trk::MeasurementBase* meas : rots) {
1877 Identifier id = m_edmHelperSvc->getIdentifier(*meas);
1878 if (!id.is_valid()) continue;
1879 Amg::Vector3D lpos{Amg::Vector3D::Zero()};
1880 double lxmin{0}, lxmax{0}, phimin{0}, phimax{0};
1881 bool isMdt = m_idHelperSvc->isMdt(id);
1882 bool measuresPhi = m_idHelperSvc->measuresPhi(id);
1883 if (isMdt) {
1884 lpos.setZero();
1885 const MdtDriftCircleOnTrack* mdt = static_cast<const MdtDriftCircleOnTrack*>(meas);
1886 TubeEnds tubeEnds = localTubeEnds(*mdt, gToSegment, segmentToGlobal);
1887
1888 lxmin = tubeEnds.lxmin;
1889 lxmax = tubeEnds.lxmax;
1890 phimin = tubeEnds.phimin;
1891 phimax = tubeEnds.phimax;
1892 } else {
1893 lpos = gToSegment * meas->globalPosition();
1894 lxmin = lpos.x() - 5 * Amg::error(meas->localCovariance(), Trk::locX);
1895 lxmax = lpos.x() + 5 * Amg::error(meas->localCovariance(), Trk::locX);
1896
1897 const CompetingMuonClustersOnTrack* crot = dynamic_cast<const CompetingMuonClustersOnTrack*>(meas);
1898 if (!measuresPhi) {
1899 if (crot) {
1900 const MuonGM::RpcReadoutElement* detEl =
1901 dynamic_cast<const MuonGM::RpcReadoutElement*>(crot->containedROTs().front()->prepRawData()->detectorElement());
1902 if (detEl) {
1903 // perform bound check
1904 double stripLength = detEl->StripLength(0);
1905 lxmin = lpos.x() - 0.5 * stripLength;
1906 lxmax = lpos.x() + 0.5 * stripLength;
1907 }
1908 }
1909 Amg::Vector3D locPosition = lpos;
1910 locPosition[0] = lxmin;
1911 Amg::Vector3D globalPos = segmentToGlobal * locPosition;
1912 double phi1 = globalPos.phi();
1913
1914 locPosition[0] = lxmax;
1915 globalPos = segmentToGlobal * locPosition;
1916 double phi2 = globalPos.phi();
1917 phimin = std::min(phi1, phi2);
1918 phimax = std::max(phi1, phi2);
1919
1920 } else {
1921 if (m_idHelperSvc->isTgc(id)) {
1922 // need some special tricks for TGC phi hits as their reference plane can be rotated
1923 // with respect to the MDT frame
1924
1925 // get orientation angle of strip to rotate back from local frame to strip
1926 // copy code from ROT creator
1927 int stripNo = m_idHelperSvc->tgcIdHelper().channel(id);
1928 int gasGap = m_idHelperSvc->tgcIdHelper().gasGap(id);
1929 if (!crot) {
1930 ATH_MSG_WARNING("dynamic cast failed for CompetingMuonClustersOnTrack");
1931 continue;
1932 }
1933 auto detEl = dynamic_cast<const MuonGM::TgcReadoutElement*>(crot->containedROTs().front()->prepRawData()->detectorElement());
1934
1935 // transform the two points inth
1936 const Amg::Vector3D segFrame_StripDir = gToSegment.linear()* detEl->stripDir(gasGap, stripNo);
1937 const Amg::Vector3D segFrame_stripPos = gToSegment * detEl->channelPos(id);
1938
1939 lpos = segFrame_stripPos +
1940 Amg::intersect<3>(lsegPos, ldir, segFrame_stripPos, segFrame_StripDir).value_or(0) * segFrame_StripDir;
1941
1942 ATH_MSG_VERBOSE(" In seg frame: phi pos " << Amg::toString(lsegPos)
1943 << " shifted pos " << Amg::toString(segFrame_StripDir)
1944 << " intersect with segment " << Amg::toString(lpos));
1945 }
1946 Amg::Vector3D globalPos = segmentToGlobal * lpos;
1947 phimin = globalPos.phi();
1948 phimax = phimin;
1949
1950 // check whether phi is consistent with segment phi range
1951 bool phiOk = checkPhiConsistency(phimin, seg_phimin, seg_phimax);
1952 if (!phiOk) {
1953 ATH_MSG_DEBUG(" Inconsistent phi " << phimin << " range " << seg_phimin << " " << seg_phimax);
1954 }
1955 }
1956 }
1957
1958 hits.emplace_back(id, isMdt, measuresPhi, lpos.x(), lpos.z(), lxmin, lxmax, phimin, phimax);
1959 if (measuresPhi) {
1960 ++nphiHits;
1961 if (!firstPhiHit)
1962 firstPhiHit = &hits.back();
1963 else {
1964 double distPhiHits = std::abs(firstPhiHit->z - hits.back().z);
1965 if (distPhiHits > 500.) {
1966 lastPhiHit = &hits.back();
1967 } else {
1968 // not count this phi hit
1969 --nphiHits;
1970 ATH_MSG_DEBUG(" close phi hits, distance " << distPhiHits);
1971 }
1972 }
1973 }
1974 if (msgLvl(MSG::VERBOSE)) {
1975 double locExX = xline + dXdZ * (lpos.z() - zline);
1976 ATH_MSG_VERBOSE(" " << std::setw(65) << m_idHelperSvc->toString(id) << " pos (" << std::setw(6) << (int)lpos.x() << ","
1977 << std::setw(6) << (int)lpos.z() << ") ex pos " << std::setw(6) << (int)locExX << " min "
1978 << std::setw(6) << (int)lxmin << " max " << std::setw(6) << (int)lxmax << " phimin " << std::setw(6)
1979 << phimin << " phimax " << std::setw(6) << phimax);
1980 if (lpos.x() < lxmin || lpos.x() > lxmax) ATH_MSG_VERBOSE(" outBounds");
1981 }
1982 }
1983
1984 if (nphiHits == 1) {
1985 if (!firstPhiHit) {
1986 ATH_MSG_WARNING(" Pointer to first phi hit not set, this should not happen! ");
1987 } else {
1988 if (xline != firstPhiHit->x) {
1989 hasUpdated = true;
1990
1991 // use phi position of the phi hit
1992 xline = firstPhiHit->x;
1993 zline = firstPhiHit->z;
1994
1995 if (m_assumePointingPhi) {
1996 Amg::Vector3D ipLocPos = gToSegment.translation();
1997 ATH_MSG_VERBOSE(" IP position in local frame " << ipLocPos);
1998
1999 double dz = ipLocPos.z() - zline;
2000 if (std::abs(dz) > 0.001) {
2001 ATH_MSG_VERBOSE(" hit (" << xline << "," << zline << ") IP (" << ipLocPos.x() << "," << ipLocPos.z()
2002 << ") dXdZ " << (ipLocPos.x() - xline) / dz << " old " << dXdZ);
2003 dXdZ = (ipLocPos.x() - xline) / dz;
2004 }
2005 }
2006 }
2007 }
2008 } else if (nphiHits == 2) {
2009 if (!firstPhiHit || !lastPhiHit) {
2010 ATH_MSG_WARNING(" Pointer to one of the two phi hit not set, this should not happen! ");
2011 } else {
2012 double dz = lastPhiHit->z - firstPhiHit->z;
2013 // use phi position of the first hit
2014 xline = firstPhiHit->x;
2015 zline = firstPhiHit->z;
2016 if (std::abs(dz) > 300.) {
2017 double dx = lastPhiHit->x - firstPhiHit->x;
2018 hasUpdated = true;
2019
2020 // if the two hits are far enough apart, also use the direction of the line connecting the two hits.
2021 dXdZ = dx / dz;
2022 }
2023 }
2024 } else {
2025 // in all other cases just rotate until the MDTs are ok
2026 }
2027
2028 if (hasUpdated) {
2029 // move segment to position of phi hit
2030 double segX = xline - dXdZ * zline;
2031
2032 // finally check whether now everything is in bounds
2033 bool ok = checkBoundsInXZ(segX, 0., dXdZ, hits);
2034 if (!ok) {
2035 // give WARNING and give up for now
2036 ATH_MSG_DEBUG("still several out of bounds hits after rotation: posx(" << segX << ") dXdZ " << dXdZ
2037 << " keeping old result ");
2038 }
2039
2040 // update segment parameters
2041 double alphaYZ = segLocDir.angleYZ();
2042 double alphaXZ = std::atan2(1, dXdZ);
2043
2044 segLocPos[Trk::locX] = segX;
2045 segLocDir = Trk::LocalDirection(alphaXZ, alphaYZ);
2046 }
2047 return hasUpdated;
2048 }
2049
2051 const Amg::Transform3D& segmentToG) const {
2052 TubeEnds tubeEnds;
2053 const Identifier& id = mdt.identify();
2054 Amg::Vector3D lpos = gToSegment * mdt.prepRawData()->globalPosition();
2055
2056 // use readout and hv side as the surface frame is not that of the chamber
2057 Amg::Vector3D lropos = gToSegment * mdt.prepRawData()->detectorElement()->ROPos(id);
2058 Amg::Vector3D lhvpos = lpos + (lpos - lropos);
2059
2060 // rescale to correctly take into account active tube length
2061 double tubeLen = (lropos - lhvpos).mag();
2062 double activeTubeLen =
2063 mdt.detectorElement()->getActiveTubeLength(m_idHelperSvc->mdtIdHelper().tubeLayer(id), m_idHelperSvc->mdtIdHelper().tube(id));
2064 double scaleFactor = activeTubeLen / tubeLen;
2065 lropos[0] = scaleFactor * lropos.x();
2066 lhvpos[0] = scaleFactor * lhvpos.x();
2067
2068 tubeEnds.lxmin = std::min(lropos.x(), lhvpos.x());
2069 tubeEnds.lxmax = std::max(lropos.x(), lhvpos.x());
2070
2071 Amg::Vector3D ropos = segmentToG * lropos;
2072 Amg::Vector3D hvpos = segmentToG * lhvpos;
2073 const double phiRO = ropos.phi();
2074 const double phiHV = hvpos.phi();
2075 tubeEnds.phimin = std::min(phiRO, phiHV);
2076 tubeEnds.phimax = std::max(phiRO, phiHV);
2077 return tubeEnds;
2078 }
2079
2080 void DCMathSegmentMaker::updatePhiRanges(double phiminNew, double phimaxNew, double& phiminRef, double& phimaxRef) {
2081 // check whether we are at the boundary where phi changes sign
2082 if (phiminRef * phimaxRef < 0.) {
2083 if (phiminRef < -1.1) {
2084 if (phiminRef > phiminNew) phiminRef = phiminNew;
2085 if (phimaxRef < phimaxNew) phimaxRef = phimaxNew;
2086 } else {
2087 if (phiminRef < phiminNew) phiminRef = phiminNew;
2088 if (phimaxRef > phimaxNew) phimaxRef = phimaxNew;
2089 }
2090 } else {
2091 // if not life is 'easy'
2092 if (phiminRef < 0.) {
2093 if (phiminRef < phiminNew) phiminRef = phiminNew;
2094 if (phimaxRef > phimaxNew) phimaxRef = phimaxNew;
2095 } else {
2096 if (phiminRef > phiminNew) phiminRef = phiminNew;
2097 if (phimaxRef < phimaxNew) phimaxRef = phimaxNew;
2098 }
2099 }
2100 }
2101
2102 bool DCMathSegmentMaker::checkPhiConsistency(double phi, double phimin, double phimax) const {
2103 // only if assuming pointing phi
2104 if (!m_assumePointingPhi) return true;
2105
2106 bool phiOk = true;
2107 double offset = 0.05;
2108 if (phimin * phimax < 0.) {
2109 if (phi < 0.) {
2110 if (phi > -1.1) {
2111 if (phi < phimin - offset) phiOk = false;
2112 } else {
2113 if (phi > phimin + offset) phiOk = false;
2114 }
2115 } else {
2116 if (phi > 1.1) {
2117 if (phi < phimax - offset) phiOk = false;
2118 } else {
2119 if (phi > phimax + offset) phiOk = false;
2120 }
2121 }
2122 } else {
2123 if (phi < phimin - offset || phi > phimax + offset) phiOk = false;
2124 }
2125 return phiOk;
2126 }
2127
2129 bool isCurvedSegment) const {
2130 // Local direction along precision measurement (0, dy, dz)
2131 Trk::LocalDirection segLocDirs(M_PI_2, linephi);
2132 Amg::Vector3D gdirs;
2133 surf.localToGlobalDirection(segLocDirs, gdirs);
2134 // Local direction in plane (1,0,0)
2135 Trk::LocalDirection segLocDiro(0., M_PI_2);
2136 Amg::Vector3D gdiro;
2137 surf.localToGlobalDirection(segLocDiro, gdiro);
2138
2139 // recalculate the value of the local XZ angle for the give YZ angle of the segment such that the global phi
2140 // direction remains unchanged
2141 double dx = std::sin(gdirs.theta()) * std::cos(gdirs.phi());
2142 double dy = std::sin(gdirs.theta()) * std::sin(gdirs.phi());
2143 double dz = std::cos(gdirs.theta());
2144
2145 // vector gdiro
2146
2147 double dxo = std::sin(gdiro.theta()) * std::cos(gdiro.phi());
2148 double dyo = std::sin(gdiro.theta()) * std::sin(gdiro.phi());
2149 double dzo = std::cos(gdiro.theta());
2150
2151 // solve system real direction = A * gdir + B * gdiro
2152 // phi global constraint: (1)*sin(phi road) - (2)*cos(phi road) = 0
2153 // ( A * dx + B * dxo ) sin (phi ) - (A * dy + B *dyo ) cos (phi) = 0
2154 // A ( dx sin - dy cos ) + B (dx0 sin -dy0 cos) = A * a0 + B * b0 = 0
2155 // psi = atan (-b0 , a0)
2156
2157 double a0 = dx * std::sin(roaddir.phi()) - dy * std::cos(roaddir.phi());
2158 double b0 = dxo * std::sin(roaddir.phi()) - dyo * std::cos(roaddir.phi());
2159 if (b0 < 1e-8 && b0 > 0) b0 = 1e-8;
2160 if (b0 > -1e-8 && b0 < 0) b0 = -1e-8;
2161 double dxn = dx - a0 * dxo / b0;
2162 double dyn = dy - a0 * dyo / b0;
2163 double dzn = dz - a0 * dzo / b0;
2164 double norm = std::sqrt(dxn * dxn + dyn * dyn + dzn * dzn);
2165
2166 // flip the sign if the direction NOT parallel to road
2167 if (m_assumePointingPhi) {
2168 if (dxn * roaddir.x() + dyn * roaddir.y() + dzn * roaddir.z() < 0.) { norm = -norm; }
2169 } else {
2170 if (dxn * roaddir.x() + dyn * roaddir.y() < 0.) { norm = -norm; }
2171 }
2172
2173 if (isCurvedSegment) norm = norm / 2.;
2174
2175 //
2176 // Follow segment fit direction
2177 //
2178 dxn = dxn / norm;
2179 dyn = dyn / norm;
2180 dzn = dzn / norm;
2181
2182 return Amg::Vector3D(dxn, dyn, dzn);
2183 }
2184} // namespace Muon
#define M_PI
Scalar eta() const
pseudorapidity method
Scalar phi() const
phi method
Scalar mag() const
mag method
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_ERROR(x)
#define ATH_MSG_VERBOSE(x)
#define ATH_MSG_WARNING(x)
#define ATH_MSG_DEBUG(x)
double charge(const T &p)
Definition AtlasPID.h:997
static Double_t sp
static Double_t a
size_t size() const
Number of registered mappings.
double angle(const GeoTrf::Vector2D &a, const GeoTrf::Vector2D &b)
Timeout singleton.
#define y
#define x
#define z
static Timeout & instance()
Get reference to Timeout singleton.
Definition Timeout.h:64
Derived DataVector<T>.
Definition DataVector.h:795
void reserve(size_type n)
Attempt to preallocate enough memory for a specified number of elements.
value_type push_back(value_type pElem)
Add an element to the end of the collection.
This is a "hash" representation of an Identifier.
unsigned int nMDTinStation() const
How many MDT chambers are in the station.
double getActiveTubeLength(const int tubeLayer, const int tube) const
Amg::Vector3D ROPos(const int tubelayer, const int tube) const
int getNLayers() const
Returns the number of tube layers inside the multilayer.
int getNtubesperlayer() const
Returns the number of tubes in each tube layer.
virtual const Trk::Surface & surface() const override final
Return surface associated with this detector element.
double innerTubeRadius() const
Returns the inner tube radius excluding the aluminium walls.
virtual const Amg::Vector3D & center(const Identifier &) const override final
Return the center of the surface associated with this identifier In the case of silicon it returns th...
virtual const Amg::Vector3D & center() const override
Return the center of the element.
The MuonDetectorManager stores the transient representation of the Muon Spectrometer geometry and pro...
const MdtReadoutElement * getMdtReadoutElement(const Identifier &id) const
access via extended identifier (requires unpacking)
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...
Amg::Vector3D localSpacePoint(const Identifier &stripId, const Amg::Vector3D &etaHitPos, const Amg::Vector3D &phiHitPos) const
Class for competing MuonClusters, it extends the Trk::CompetingRIOsOnTrack base class.
const std::vector< std::unique_ptr< 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
Gaudi::Property< bool > m_doSpacePoints
void find(const std::vector< const Trk::RIO_OnTrack * > &rios, Trk::SegmentCollection *segColl=nullptr) const
find segments starting from a list of RIO_OnTrack objects, implementation of IMuonSegmentMaker interf...
std::unique_ptr< MuonSegment > createSegment(const EventContext &ctx, TrkDriftCircleMath::Segment &segment, const Identifier &chid, const Amg::Vector3D &roadpos, const Amg::Vector3D &roaddir2, const std::vector< const MdtDriftCircleOnTrack * > &mdts, bool hasPhiMeasurements, segmentCreationInfo &sInfo, double beta=1.) const
bool errorScalingRegion(const Identifier &id) const
apply error scaling for low mometum tracks
ClusterVecPair create1DClusters(const std::vector< const MuonClusterOnTrack * > &clusters) const
std::map< Identifier, EtaPhiHitsPair > IdHitMap
Gaudi::Property< bool > m_addUnassociatedPhiHits
Gaudi::Property< bool > m_updatePhiUsingPhiHits
Gaudi::Property< bool > m_recoverBadRpcCabling
ToolHandle< IDCSLFitProvider > m_dcslFitProvider
Gaudi::Property< bool > m_doGeometry
Amg::Vector3D updateDirection(double linephi, const Trk::PlaneSurface &surf, const Amg::Vector3D &roaddir, bool isCurvedSegment) const
update the global direction, keeping the phi of the input road direction but using the local angle YZ
Gaudi::Property< double > m_sinAngleCut
Gaudi::Property< bool > m_removeDeltas
ToolHandle< IMuonClusterOnTrackCreator > m_clusterCreator
Gaudi::Property< bool > m_createCompetingROTsEta
ToolHandle< IMuonSegmentSelectionTool > m_segmentSelectionTool
std::map< Identifier, IdHitMap > ChIdHitMap
TrkDriftCircleMath::MdtChamberGeometry createChamberGeometry(const Identifier &chid, const Amg::Transform3D &gToStation) const
Gaudi::Property< bool > m_doTimeOutChecks
static double distanceToSegment(const TrkDriftCircleMath::Segment &segment, const Amg::Vector3D &hitPos, const Amg::Transform3D &gToStation)
Gaudi::Property< bool > m_outputFittedT0
const MdtDriftCircleOnTrack * findFirstRotInChamberWithMostHits(const std::vector< const MdtDriftCircleOnTrack * > &mdts) const
Gaudi::Property< double > m_preciseErrorScale
Gaudi::Property< bool > m_reject1DTgcSpacePoints
ToolHandle< IMuonSegmentFittingTool > m_segmentFitter
PublicToolHandle< MuonEDMPrinterTool > m_printer
virtual StatusCode initialize()
TrkDriftCircleMath::CLVec createClusterVec(const Identifier &chid, ClusterVec &spVec, const Amg::Transform3D &gToStation) const
Cluster2D createTgcSpacePoint(const Identifier &gasGapId, const MuonClusterOnTrack *etaHit, const MuonClusterOnTrack *phiHit) const
Gaudi::Property< bool > m_curvedErrorScaling
Gaudi::Property< bool > m_redo2DFit
Gaudi::Property< bool > m_allMdtHoles
Gaudi::Property< bool > m_refitParameters
std::vector< Cluster2D > ClusterVec
bool updateSegmentPhi(const Amg::Vector3D &gpos, const Amg::Vector3D &gdir, Amg::Vector2D &segLocPos, Trk::LocalDirection &segLocDir, Trk::PlaneSurface &surf, const std::vector< const Trk::MeasurementBase * > &rots, double phimin, double phimax) const
TubeEnds localTubeEnds(const MdtDriftCircleOnTrack &mdt, const Amg::Transform3D &gToSegment, const Amg::Transform3D &segmentToG) const
calculate positions of tube ends
static void updatePhiRanges(double phiminNew, double phimaxNew, double &phiminRef, double &phimaxRef)
update phi ranges
SG::ReadHandleKey< Muon::MdtPrepDataContainer > m_mdtKey
ClusterVecPair create2DClusters(const std::vector< const MuonClusterOnTrack * > &clusters) const
SG::ReadCondHandleKey< Muon::MuonIntersectGeoData > m_chamberGeoKey
SG::ReadCondHandleKey< MuonGM::MuonDetectorManager > m_DetectorManagerKey
pointers to IdHelpers
bool checkBoundsInXZ(double xline, double zline, double dXdZ, const std::vector< HitInXZ > &hits) const
check whether all hits are in bounds in the XZ plane
ServiceHandle< Muon::IMuonIdHelperSvc > m_idHelperSvc
ToolHandle< IMuonCompetingClustersOnTrackCreator > m_compClusterCreator
void associateMDTsToSegment(const Amg::Vector3D &gdir, TrkDriftCircleMath::Segment &segment, const TrkDriftCircleMath::ChamberGeometry *multiGeo, const Amg::Transform3D &gToStation, const Amg::Transform3D &amdbToGlobal, std::set< Identifier > &deltaVec, std::set< Identifier > &outoftimeVec, std::vector< std::pair< double, std::unique_ptr< const Trk::MeasurementBase > > > &rioDistVec, double beta=1.) const
double errorScaleFactor(const Identifier &id, double curvature, bool hasPhiMeasurements) const
calculate error scaling factor
Cluster2D createRpcSpacePoint(const Identifier &gasGapId, const MuonClusterOnTrack *etaHit, const std::vector< const MuonClusterOnTrack * > &phiHits) const
Gaudi::Property< bool > m_createCompetingROTsPhi
ToolHandle< IMdtDriftCircleOnTrackCreator > m_mdtCreator
Cluster2D createSpacePoint(const Identifier &gasGapId, const MuonClusterOnTrack *etaHit, const MuonClusterOnTrack *phiHit) const
Gaudi::Property< double > m_maxAssociateClusterDistance
std::pair< ClusterVec, ClusterVec > ClusterVecPair
ClusterVecPair createSpacePoints(const ChIdHitMap &chIdHitMap) const
const MdtPrepData * findMdt(const EventContext &ctx, const Identifier &id) const
Gaudi::Property< bool > m_usePreciseError
std::pair< std::pair< int, int >, bool > associateClustersToSegment(const TrkDriftCircleMath::Segment &segment, const Identifier &chid, const Amg::Transform3D &gToStation, ClusterVecPair &spVecs, double phimin, double phimax, std::vector< std::pair< double, std::unique_ptr< const Trk::MeasurementBase > > > &rioDistVec) const
ToolHandle< IMdtSegmentFinder > m_segmentFinder
static DataVector< const Trk::MeasurementBase > createROTVec(std::vector< std::pair< double, std::unique_ptr< const Trk::MeasurementBase > > > &rioDistVec)
ServiceHandle< IMuonEDMHelperSvc > m_edmHelperSvc
ToolHandle< IMdtDriftCircleOnTrackCreator > m_mdtCreatorT0
Gaudi::Property< bool > m_assumePointingPhi
static std::pair< double, double > residualAndPullWithSegment(const TrkDriftCircleMath::Segment &segment, const Cluster2D &spacePoint, const Amg::Transform3D &gToStation)
bool checkPhiConsistency(double phi, double phimin, double phimax) const
check whether phi is consistent with segment phi
std::vector< Identifier > calculateHoles(const EventContext &ctx, Identifier chid, const Amg::Vector3D &gpos, const Amg::Vector3D &gdir, bool hasMeasuredCoordinate, std::set< Identifier > &deltaVec, std::set< Identifier > &outoftimeVec, const std::vector< std::pair< double, std::unique_ptr< const Trk::MeasurementBase > > > &rioDistVec) const
TrkDriftCircleMath::DCVec createDCVec(const std::vector< const MdtDriftCircleOnTrack * > &mdts, double errorScale, std::set< Identifier > &chamberSet, double &phimin, double &phimax, TrkDriftCircleMath::DCStatistics &dcStatistics, const Amg::Transform3D &gToStation, const Amg::Transform3D &amdbToGlobal) const
This class represents the corrected MDT measurements, where the corrections include the effects of wi...
virtual const Amg::Vector3D & globalPosition() const override final
Returns the global Position.
double driftRadius() const
Returns the value of the drift radius.
double driftTime() const
Returns the value of the drift time used to obtain the drift radius.
virtual const Trk::StraightLineSurface & associatedSurface() const override final
Returns the surface on which this measurement was taken.
virtual const MuonGM::MdtReadoutElement * detectorElement() const override final
Returns the detector element, assoicated with the PRD of this class.
virtual const MdtPrepData * prepRawData() const override final
Returns the PrepRawData used to create this corrected measurement.
Class to represent measurements from the Monitored Drift Tubes.
Definition MdtPrepData.h:33
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.
Base class for Muon cluster RIO_OnTracks.
virtual const MuonGM::MuonClusterReadoutElement * detectorElement() const override=0
Returns the detector element, associated with the PRD of this class.
virtual const MuonCluster * prepRawData() const override=0
Returns the Trk::PrepRawData - is a MuonCluster in this scope.
virtual const Amg::Vector3D & globalPosition() const override
Returns global position.
This is the common muon segment quality object.
const_pointer_type cptr()
virtual bool isValid() override final
Can the handle be successfully dereferenced?
virtual LocVec2D tubePosition(unsigned int ml, unsigned int lay, unsigned int tube) const =0
virtual unsigned int nlay() const =0
class representing a drift circle meaurement on segment
Definition DCOnTrack.h:16
@ OutOfTime
delta electron
Definition DCOnTrack.h:22
virtual bool fit(Segment &result, const Line &line, const DCOnTrackVec &dcs, double t0Seed=-99999.) const
const HitSelection selectHitsOnTrack(const DCOnTrackVec &dcs) const
This class offers no functionality, but to define a standard device for the maker to transfer to the ...
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
double y() const
Returns the y coordinate of the vector.
Definition LocVec2D.h:29
double x() const
Returns the x coordinate of the vector.
Definition LocVec2D.h:27
void print(MsgStream &msg) const override
double residual(const LocVec2D &pos) const
class to calculate residual of a hit with a segment and calculate the local track errors
double trackError2(const DriftCircle &dc) const
calculate the track error at the position of a drift circle
TrkDriftCircleMath::Road - encodes the road given to the segment finder in station coordinates.
Definition Road.h:15
LocVec2D toLocal(const LocVec2D &pos) const
LocVec2D toLine(const LocVec2D &pos) const
represents the three-dimensional global direction with respect to a planar surface frame.
double angleXZ() const
access method for angle of local XZ projection
double angleYZ() const
access method for angle of local YZ projection
This class is the pure abstract base class for all fittable tracking measurements.
const LocalParameters & localParameters() const
Interface method to get the LocalParameters.
const Amg::MatrixX & localCovariance() const
Interface method to get the localError.
std::unique_ptr< MeasurementBase > uniqueClone() const
NVI Clone giving up unique pointer.
const Amg::Vector3D & momentum() const
Access method for the momentum.
const Amg::Vector3D & position() const
Access method for the position.
Class for a planaer rectangular or trapezoidal surface in the ATLAS detector.
void localToGlobalDirection(const Trk::LocalDirection &locdir, Amg::Vector3D &globdir) const
This method transforms a local direction wrt the plane to a global direction.
const Amg::Vector2D & localPosition() const
return the local position reference
const Amg::MatrixX & localCovariance() const
return const ref to the error matrix
Class to handle RIO On Tracks ROT) for InDet and Muons, it inherits from the common MeasurementBase.
Definition RIO_OnTrack.h:70
virtual const Surface & associatedSurface() const override=0
returns the surface for the local to global transformation
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
Class for a StraightLineSurface in the ATLAS detector to describe dirft tube and straw like detectors...
virtual bool globalToLocal(const Amg::Vector3D &glob, const Amg::Vector3D &mom, Amg::Vector2D &loc) const override final
Specified for StraightLineSurface: GlobalToLocal method without dynamic memory allocation This method...
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...
const Amg::Transform3D & transform() const
Returns HepGeom::Transform3D by reference.
const Amg::Vector3D & center() const
Returns the center position of the Surface.
Encapsulates the information required by the find() method of the muon segment makers.
Definition TrackRoad.h:21
const Amg::Vector3D & globalPosition() const
Get the global position of the road.
Definition TrackRoad.h:137
double deltaEta() const
Get the width of the road in the eta direction.
Definition TrackRoad.h:149
const Amg::Vector3D & globalDirection() const
Get the global direction of the road.
Definition TrackRoad.h:143
represents the track state (measurement, material, fit parameters and quality) at a surface.
@ Outlier
This TSoS contains an outlier, that is, it contains a MeasurementBase/RIO_OnTrack which was not used ...
int r
Definition globals.cxx:22
double a0
Definition globals.cxx:27
std::optional< double > intersect(const AmgVector(N)&posA, const AmgVector(N)&dirA, const AmgVector(N)&posB, const AmgVector(N)&dirB)
Calculates the point B' along the line B that's closest to a second line A.
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 ...
Amg::Transform3D getRotateZ3D(double angle)
get a rotation transformation around Z-axis
Eigen::Affine3d Transform3D
Eigen::Matrix< double, 2, 1 > Vector2D
Eigen::Matrix< double, 3, 1 > Vector3D
ChIndex chIndex(const std::string &index)
convert ChIndex name string to enum
PhiIndex
enum to classify the different phi layers in the muon spectrometer
const std::string & stName(StIndex index)
convert StIndex into a string
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.
MuonPrepDataCollection< MdtPrepData > MdtPrepDataCollection
@ OWN_ELEMENTS
this data object owns its elements
std::vector< DriftCircle > DCVec
std::vector< DCOnTrack > DCOnTrackVec
Definition DCOnTrack.h:59
DriftCircleSide
Enumerates the 'side' of the wire on which the tracks passed (i.e.
@ RIGHT
the drift radius is positive (see Trk::AtaStraightLine)
@ LEFT
the drift radius is negative (see Trk::AtaStraightLine)
DataVector< Trk::Segment > SegmentCollection
@ driftRadius
trt, straws
Definition ParamDefs.h:53
@ 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
@ loc1
Definition ParamDefs.h:34
ParametersBase< TrackParametersDim, Charged > TrackParameters
Definition index.py:1
void sort(typename DataModel_detail::iterator< DVL > beg, typename DataModel_detail::iterator< DVL > end)
Specialization of sort for DataVector/List.
void swap(ElementLinkVector< DOBJ > &lhs, ElementLinkVector< DOBJ > &rhs)
double deltaPhi(double phiA, double phiB)
delta Phi in range [-pi,pi[
std::vector< const MuonClusterOnTrack * > phiHits
const TrkDriftCircleMath::ChamberGeometry * geom
IdDataVec(const Identifier &i)
std::vector< Entry > EntryVec
IdDataVec()=default
Function object to sort pairs containing a double and a pointer to a MuonClusterOnTrack.
bool operator()(const std::pair< double, DCMathSegmentMaker::Cluster2D > &d1, const std::pair< double, DCMathSegmentMaker::Cluster2D > &d2)
bool operator()(const IdDataVec< T > &d1, const IdDataVec< T > &d2)