ATLAS Offline Software
Loading...
Searching...
No Matches
MsTrackSeederTool.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3*/
4#include "MsTrackSeederTool.h"
5
7
8#include "Acts/Utilities/Helpers.hpp"
9#include "Acts/Definitions/Tolerance.hpp"
10#include "Acts/Definitions/Units.hpp"
11#include "Acts/Geometry/TrackingGeometry.hpp"
12
13#include "Acts/Surfaces/LineBounds.hpp"
14#include "Acts/Surfaces/RectangleBounds.hpp"
15#include "Acts/Surfaces/DiamondBounds.hpp"
16#include "Acts/Surfaces/TrapezoidBounds.hpp"
17#include "Acts/Surfaces/PlaneSurface.hpp"
18
22#include "GaudiKernel/PhysicalConstants.h"
23
24namespace {
25 using namespace Acts::UnitLiterals;
27 inline double momentumDev(double PtimesQ1, double PtimesQ2) {
28 const double denom {std::max(std::abs(PtimesQ1) + std::abs(PtimesQ2), Acts::s_epsilon)};
29 return std::abs(PtimesQ1 - PtimesQ2) / denom;
30 };
32 bool isNswSegment(const xAOD::MuonSegment& seg) {
33 using namespace Muon::MuonStationIndex;
34 return seg.technology() == TechnologyIndex::STGC ||
36 toStationIndex(seg.chamberIndex()) == StIndex::EE;
37 }
39 double inDeg(double angle) {
40 return angle / Gaudi::Units::deg;
41 }
42}
43
44namespace MuonR4{
47
49 ATH_CHECK(m_ctxProvider.initialize());
50 ATH_CHECK(m_segSelector.retrieve());
52 ATH_CHECK(m_segmentKey.initialize(!m_segmentKey.empty()));
53 ATH_CHECK(detStore()->retrieve(m_detMgr));
54 if (m_nFieldSteps == 0) {
55 ATH_MSG_ERROR("The number of field steps must not be zero "<<m_nFieldSteps);
56 return StatusCode::FAILURE;
57 }
59 const double stepSize {1. / m_nFieldSteps};
60 for (std::size_t i = 0; i < m_nFieldSteps; ++i) {
61 m_fieldExtpSteps.push_back((static_cast<double>(i) + 0.5) * stepSize);
62 }
63 return StatusCode::SUCCESS;
64 }
65
66 Acts::Result<Acts::BoundTrackParameters>
68 const MsTrackSeed& seed) const {
69 const Acts::GeometryContext tgContext = m_ctxProvider.getGeometryContext(ctx);
70 const Acts::MagneticFieldContext mfContext = m_ctxProvider.getMagneticFieldContext(ctx);
72 mfContext.get<const AtlasFieldCacheCondObj*>()->getInitializedCache(magField);
73
74 const xAOD::MuonSegment* refSeg{nullptr};
75 Acts::BoundMatrix cov{Acts::BoundMatrix::Zero()};
76 for (const xAOD::MuonSegment* segment : seed.segments()) {
83 if (!refSeg && !isNswSegment(*segment) &&
84 m_segSelector->passSeedingQuality(ctx, *segment)) {
85 refSeg = segment;
86 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Set reference segment to "<<printSegment(*segment));
87 }
88 Acts::BoundTrackParameters boundPars = SegmentFit::boundSegmentPars(tgContext, *m_detMgr, *segment);
89 if (!boundPars.covariance()) {
90 continue;
91 }
92 for (int i =0 ; i < cov.cols(); ++i) {
93 cov(i,i) += (*boundPars.covariance())(i,i);
94 }
95
96 }
97 //if we did not find a reference segment let's try the NSW one before we give up on the track
98 if(!refSeg){
99 for (const xAOD::MuonSegment* segment : seed.segments()) {
100 if (isNswSegment(*segment) &&
101 m_segSelector->passSeedingQuality(ctx, *segment)) {
102 refSeg = segment;
103 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - NSW is the best what we have apparently....");
104 break;
105 }
106 }
107 }
108
109 if (!refSeg) {
110 ATH_MSG_WARNING(__func__<<"() "<<__LINE__
111 <<" - No reference segment passing the seeding quality was found.");
112 return Acts::Result<Acts::BoundTrackParameters>::failure(std::make_error_code(std::errc::invalid_argument));
113 }
114 Amg::Vector3D seedPos{atFirstSurface(tgContext, *refSeg)};
115 Amg::Vector3D seedDir{refSeg->direction()};
116 ATH_MSG_DEBUG(__func__<<"() "<<__LINE__<<" - Initial seed Pos: "<<Amg::toString(seedPos)
117 <<" theta/phi: "<<inDeg(seedPos.theta())<<" / "<<inDeg(seedPos.phi())
118 <<", dir: "<<Amg::toString(seedDir)
119 <<" theta/phi: "<<inDeg(seedDir.theta())<<" / "<<inDeg(seedDir.phi()));
120
121 const xAOD::MuonSegment* frontSegment = seed.segments().front();
122
123 const xAOD::UncalibratedMeasurement* firstMeas {firstMeasurement(*frontSegment)};
124 const Acts::Surface& firstSurf = xAOD::muonSurface(firstMeas);
125 const Acts::GeometryIdentifier volId = volumeId(firstSurf);
126
127 // Find the first measurement
128 const Acts::TrackingVolume* volume{MuonGMR4::highestAlignable(m_trackingGeometrySvc->trackingGeometry()->findVolume(volId))};
129
130 if (!volume) {
131 ATH_MSG_WARNING(__func__<<"() "<<__LINE__
132 <<" - Failed to find tracking volume for seed measurement "<<volId);
133 return Acts::Result<Acts::BoundTrackParameters>::failure(std::make_error_code(std::errc::invalid_argument));
134 }
135 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__
136 <<" - Bounding volume "<<volume->volumeName()
137 <<", trf: "<<Amg::toString(volume->localToGlobalTransform(tgContext))
138 <<", bounds: "<<volume->volumeBounds());
142 if (frontSegment != refSeg) {
143 const Amg::Vector3D frontSegPos = atFirstSurface(tgContext, *frontSegment);
144 const Acts::Transform3 toFirstTrf = firstSurf.localToGlobalTransform(tgContext).inverse();
145 const Amg::Vector3D locFrontSegPos = toFirstTrf * frontSegPos;
146 if (!volume->inside(tgContext, frontSegPos)) {
147 ATH_MSG_WARNING(__func__<<"() "<<__LINE__<<" - Segment "<<printSegment(*frontSegment)
148 <<" not inside mother volume: "<<volume->volumeName()<<", "
149 <<Amg::toString(volume->globalToLocalTransform(tgContext)*frontSegPos)
150 <<", bounds: "<<volume->volumeBounds()<<", "
151 <<SegmentFit::localSegmentPars(*frontSegment));
152 }
154 {
155 const Acts::Transform3& toLoc{volume->globalToLocalTransform(tgContext)};
156 const Amg::Vector3D locSeedDir = toLoc.linear() * seedDir;
157 const Amg::Vector3D locFrontDir = toLoc.linear() * frontSegment->direction();
158 seedDir = volume->localToGlobalTransform(tgContext).linear() *
159 Acts::makeDirectionFromAxisTangents(houghTanAlpha(locSeedDir),
160 houghTanBeta(locFrontDir));
161 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Updated seed direction theta/phi: "
162 <<inDeg(seedDir.theta())<<" / "<<inDeg(seedDir.phi()));
163 }
167 const Acts::MultiIntersection firstIsect = firstSurf.intersect(tgContext, seedPos, seedDir,
168 Acts::BoundaryTolerance::Infinite());
169 const Amg::Vector3D locSeedAtFirst = toFirstTrf * firstIsect.at(0).position();
170 if (firstSurf.type() == Acts::Surface::SurfaceType::Straw) {
171 const auto& bounds = static_cast<const Acts::LineBounds&>(firstSurf.bounds());
172 using enum Acts::LineBounds::BoundValues;
175 const Amg::Vector3D locStartPos{locFrontSegPos.x(), locFrontSegPos.y(),
176 std::clamp(locSeedAtFirst.z(), -bounds.get(eHalfLengthZ), bounds.get(eHalfLengthZ))};
177 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - The first surface is a straw "
178 <<bounds<<", track seed @first: "<<Amg::toString(locSeedAtFirst)<<" vs. segment @first: "
179 <<Amg::toString(locFrontSegPos)<<", updated local start pos: "<<Amg::toString(locStartPos));
180 seedPos = firstSurf.localToGlobalTransform(tgContext) * locStartPos;
181 } else if (firstSurf.type() == Acts::Surface::SurfaceType::Plane) {
182 if (isNswSegment(*frontSegment)) {
183 seedPos = frontSegPos;
184 } else {
187 Acts::Vector2 locStartPos2D {locFrontSegPos.x(), locSeedAtFirst.y()};
188 const auto& bounds = firstSurf.bounds();
189 switch (bounds.type()) {
190 case Acts::SurfaceBounds::BoundsType::eRectangle:
191 if (!bounds.inside(locStartPos2D)) {
192 locStartPos2D = bounds.closestPoint(locStartPos2D, Acts::SquareMatrix2::Identity());
193 }
194 break;
195 case Acts::SurfaceBounds::BoundsType::eTrapezoid:
196 case Acts::SurfaceBounds::BoundsType::eDiamond:
198 std::swap(locStartPos2D.x(), locStartPos2D.y());
199 if (!bounds.inside(locStartPos2D)) {
200 locStartPos2D = bounds.closestPoint(locStartPos2D, Acts::SquareMatrix2::Identity());
201 }
202 std::swap(locStartPos2D.x(), locStartPos2D.y());
203 break;
204 default:
205 ATH_MSG_WARNING(__func__<<"() "<<__LINE__<<" - Unexpected surface bounds type "
206 <<firstSurf.bounds().type());
207 return Acts::Result<Acts::BoundTrackParameters>::failure(std::make_error_code(std::errc::invalid_argument));
208 }
209 const Amg::Vector3D locStartPos {locStartPos2D.x(),locStartPos2D.y(), locFrontSegPos.z()};
210
211 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - The first surface is a plane with bounds "
212 <<firstSurf.bounds() <<", track seed @first: "<<Amg::toString(locSeedAtFirst)<<" vs. segment @first: "
213 <<Amg::toString(locFrontSegPos)<<", updated local start pos: "<<Amg::toString(locStartPos));
214 seedPos = firstSurf.localToGlobalTransform(tgContext) * locStartPos;
215 }
216 } else {
217 ATH_MSG_WARNING(__func__<<"() "<<__LINE__<<" - Unexpected surface type "<<firstSurf.type());
218 return Acts::Result<Acts::BoundTrackParameters>::failure(std::make_error_code(std::errc::invalid_argument));
219 }
220 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Updated seed position: "<<Amg::toString(seedPos)
221 <<" theta/phi: "<<inDeg(seedPos.theta())<<" / "<<inDeg(seedPos.phi()));
222 }
223
224
225 auto boundSurf = MuonGMR4::bottomBoundary(*volume);
226 if (!boundSurf) {
227 ATH_MSG_WARNING(__func__<<"() "<<__LINE__<<" - Failed to find boundary surface for tracking volume");
228 return Acts::Result<Acts::BoundTrackParameters>::failure(std::make_error_code(std::errc::invalid_argument));
229 }
230 std::shared_ptr<const Acts::Surface> targetSurf{};
235 auto propagateToBoundary = [&](const Acts::Surface& volBoundary) -> Acts::Result<Amg::Vector3D> {
236
237 const Acts::Transform3& trf{volBoundary.localToGlobalTransform(tgContext)};
238 using namespace Acts::PlanarHelper;
239 auto pIsect = intersectPlane(seedPos, seedDir, trf.linear().col(Amg::z), trf.translation());
241 if (pIsect.pathLength() > Acts::s_epsilon || !pIsect.isValid()) {
242 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Intersection @"<<Amg::toString(pIsect.position())
243 <<" is forward "<<pIsect.pathLength()<<" or invalid "<<(!pIsect.isValid())
244 <<" within volume "<<volume->inside(tgContext, pIsect.position()));
245 return Acts::Result<Amg::Vector3D>::failure(std::make_error_code(std::errc::invalid_argument));
246 }
247 Acts::Result<Amg::Vector2D> locPos = volBoundary.globalToLocal(tgContext, pIsect.position(),
248 Amg::Vector3D::Zero());
249 if (!locPos.ok()){
250 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Intersection is not on surface "<<
251 Amg::toString(trf.inverse()*pIsect.position()));
252 return Acts::Result<Amg::Vector3D>::failure(std::make_error_code(std::errc::invalid_argument));
253 }
254 if (!volBoundary.insideBounds(*locPos)) {
255 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Intersection is outside the boundaries: "<<
256 Amg::toString(*locPos)<<", bounds: "<<volBoundary.bounds());
257 return Acts::Result<Amg::Vector3D>::failure(std::make_error_code(std::errc::invalid_argument));
258 }
259 targetSurf = volBoundary.getSharedPtr();
260 return Acts::Result<Amg::Vector3D>::success(pIsect.position());
261 };
263 auto pIsect = propagateToBoundary(*boundSurf);
267 if (!pIsect.ok() && volume->isAlignable()) {
268 const Acts::VolumePlacementBase* placement = volume->volumePlacement();
269 for (std::size_t portal = 0; !pIsect.ok() && portal< placement->nPortalPlacements(); ++portal) {
270 pIsect = propagateToBoundary(placement->portalPlacement(portal)->surface());
271 }
272 }
273 if (!pIsect.ok()) {
274 ATH_MSG_WARNING(__func__<<"() "<<__LINE__<<" - Cannot create valid start parameters from seed "<<seed<<".");
275 return Acts::Result<Acts::BoundTrackParameters>::failure(std::make_error_code(std::errc::invalid_argument));
276 }
277 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Extrapolated seed position: "<<Amg::toString(*pIsect)
278 <<" eta/phi: "<<pIsect->eta()<<" / "<<(inDeg(pIsect->phi())));
279
280 const Acts::Vector4 fourPos = ActsTrk::convertPosToActs(*pIsect, (*pIsect).mag() / Gaudi::Units::c_light);
282 const double momRes {seed.location() == Location::Barrel ? m_barrelMomentumRes : m_endcapMomentumRes};
283 const double qOverP = 1./ ActsTrk::energyToActs(estimateQtimesP(tgContext, seed, magField));
284 cov (Acts::eBoundQOverP, Acts::eBoundQOverP) = Acts::square(momRes * qOverP);
285
286 return Acts::BoundTrackParameters::create(tgContext, targetSurf,
287 fourPos, seedDir, qOverP, cov,
288 Acts::ParticleHypothesis::muon());
289 }
290 Amg::Vector3D MsTrackSeederTool::segPosOntoPhiPlane(const Acts::GeometryContext& tgContext,
291 const Amg::Vector3D& planeNormal,
292 const xAOD::MuonSegment& segment) const{
293 const std::size_t nMeas = nMeasurements(segment);
294 Amg::Vector3D wireDir{Amg::Vector3D::Zero()};
295
296 for (std::size_t meas = 0; meas < nMeas; ++ meas) {
297 if (isOutlierMeasurement(segment, meas)) {
298 continue;
299 }
300 const xAOD::UncalibratedMeasurement* measPtr = getMeasurement(segment, meas);
301 if (xAOD::isPrecisionHit(measPtr)) {
302 wireDir = m_trackingGeometrySvc->trackingGeometry()->findVolume(volumeId(xAOD::muonSurface(measPtr)))->
303 localToGlobalTransform(tgContext).linear().col(Amg::x);
304 break;
305 }
306 }
308 if (nMeas == 0ul) {
309 wireDir = envelope(segment)->surface().localToGlobalTransform(tgContext).linear().col(Amg::x);
310 }
311
312 return Acts::PlanarHelper::intersectPlane(segment.position(), wireDir,
313 planeNormal, Amg::Vector3D::Zero()).position();
314 }
315 Amg::Vector2D MsTrackSeederTool::expressOnCylinder(const Acts::GeometryContext& tgContext,
316 const xAOD::MuonSegment& segment,
317 const Location loc,
318 const ExpandedSector sector) const {
320 const Amg::Vector3D pos{segPosOntoPhiPlane(tgContext, sector.normalDir(), segment)};
321 const Amg::Vector3D dir{segment.direction()};
322
323 const Amg::Vector2D projPos{pos.perp(), pos.z()};
324 const Amg::Vector2D projDir{dir.perp(), dir.z()};
325
326 ATH_MSG_VERBOSE( "segment position:" << Amg::toString(segment.position())
327 << ", direction: " << Amg::toString(segment.direction()) );
328 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Express segment in @"<<Amg::toString(pos)
329 <<", direction: "<<Amg::toString(dir)<< " sector projector: " << sector
330 << " location: " << Acts::toUnderlying(loc));
331 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Projected position onto sector: "<<Amg::toString(projPos)
332 <<", projected direction: "<<Amg::toString(projDir));
333
334 double lambda{0.};
335 if (Location::Barrel == loc) {
336 lambda = Amg::intersect<2>(projPos, projDir, Amg::Vector2D::UnitX(),
337 m_barrelRadius).value_or(10. * Gaudi::Units::km);
338 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Intersect with barrel at radius: "<<m_barrelRadius<<" --> "<<Amg::toString(projPos + lambda * projDir));
339 } else {
340 lambda = Amg::intersect<2>(projPos, projDir, Amg::Vector2D::UnitY(),
341 Acts::copySign(1.*m_endcapDiscZ, projPos[1])).value_or(10. * Gaudi::Units::km);
342 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Intersect with endcap at z: "<<Acts::copySign(1.*m_endcapDiscZ, projPos[1])
343 <<" --> "<<Amg::toString(projPos + lambda * projDir));
344 }
345 return projPos + lambda * projDir;
346 }
348 const Location loc) const {
349 using enum Location;
350 if (loc == Barrel && std::abs(projPos[1]) > std::min(m_endcapDiscZ, m_barrelLength)) {
351 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Position "<<Amg::toString(projPos)<<
352 " exceeds cylinder boundaries ("<<(1.*m_barrelRadius)<<", "
353 <<std::min(m_endcapDiscZ, m_barrelLength)<<")");
354 return false;
355 } else if (loc == Endcap && (0 > projPos[0] || projPos[0] > m_endcapDiscRadius)) {
356 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Position "<<Amg::toString(projPos)<<
357 " exceeds endcap boundaries ("<<(1.*m_endcapDiscRadius)<<", "<<Acts::copySign(1.*m_endcapDiscZ, projPos[1])<<")");
358 return false;
359 }
360 return true;
361 }
363 const PosMomPair_t& p1,
364 const PosMomPair_t& p2,
365 const PosMomPair_t& p3,
366 MagField::AtlasFieldCache& fieldCache) const {
367 // When 3 points are available, we can use each pair of segments to estimate
368 // the momentum and charge, and then combine the estimates. To make the
369 // combination, we define a struct to hold each PtimesQ estimate
370 struct Estimate {
371 double PtimesQ{0.};
372 // Weighting factor based on the magnitude of the integrated force.
373 double weight{0.};
374 // Scores as a combination of charge agreement and momentum deviation.
375 double score{0.};
376 };
377
378 const Amg::Vector3D force12 {forceIntegration(p1, p2, planeNorm, fieldCache)};
379 const Amg::Vector3D force23 {forceIntegration(p2, p3, planeNorm, fieldCache)};
380 const Amg::Vector3D force13 {force12 + force23};
381
382 std::vector<Estimate> estimates{};
383 const double weightNorm {force12.mag() + force23.mag()};
384 // Pairwise momentum estimates: 12 and 23
385 estimates.emplace_back(getPtimesQ(force12, p2.second - p1.second), force12.mag()/weightNorm, 0.);
386 estimates.emplace_back(getPtimesQ(force23, p3.second - p2.second), force23.mag()/weightNorm, 0.);
387 // Two estimates for pair 13: using segment directions and using position differences
388 const double weight13 {force13.mag()/weightNorm};
389 estimates.emplace_back(getPtimesQ(force13, p3.second - p1.second), weight13, 0.);
390 const Amg::Vector3D t12 {(p2.first - p1.first).unit()};
391 const Amg::Vector3D t23 {(p3.first - p2.first).unit()};
392 estimates.emplace_back(getPtimesQ(force13, t23 - t12), weight13, 0.);
393
394 for (std::size_t i {0}; i < estimates.size(); ++i) {
395 Estimate& est1 {estimates[i]};
396 for (std::size_t j {i+1}; j < estimates.size(); ++j) {
397 Estimate& est2 {estimates[j]};
398 double agreementScore {std::copysign(1., est1.PtimesQ*est2.PtimesQ) -
399 momentumDev(est1.PtimesQ, est2.PtimesQ)};
402 est1.score += est2.weight * agreementScore;
403 est2.score += est1.weight * agreementScore;
404 }
405 }
408 const double minScore {std::ranges::min_element(estimates,
409 {}, &Estimate::score)->score};
410 for (Estimate& est : estimates) {
411 est.score -= minScore;
412 }
413 if (msgLvl(MSG::VERBOSE)) {
414 std::vector<std::string> names {"Pair01", "Pair12", "Pair02Seg", "Pair02Pos"};
415 for (const auto [i, est] : Acts::enumerate(estimates)) {
416 ATH_MSG_VERBOSE(__func__<<"() Estimate "<<names[i]<<": PtimesQ: "<<est.PtimesQ*1e-3
417 <<", weight: "<<est.weight<<", score: "<<est.score);
418 }
419 }
422 const Estimate& bestEstimate {*std::ranges::max_element(estimates,
423 std::ranges::less{}, [](const Estimate& est){
424 return est.score * est.weight;})};
425 const double charge {std::copysign(1., bestEstimate.PtimesQ)};
426
427 double totalSum {0.}, totalWeight {0.};
428 for (const Estimate& est : estimates) {
431 if (&est == &estimates.back()) {
432 continue;
433 }
434 if (est.PtimesQ * charge > 0.) {
435 totalSum += est.PtimesQ * est.weight;
436 totalWeight += est.weight;
437 }
438 }
439 assert(totalWeight > Acts::s_epsilon);
440 return totalSum / totalWeight;
441 }
443 const PosMomPair_t& p1,
444 const PosMomPair_t& p2,
445 MagField::AtlasFieldCache& fieldCache) const {
446
447 return getPtimesQ(forceIntegration(p1, p2, planeNorm, fieldCache),
448 p2.second - p1.second);
449 }
451 const PosMomPair_t& point2,
452 const Amg::Vector3D& planeNorm,
453 MagField::AtlasFieldCache& fieldCache) const {
454 const auto& [pos1, dir1] = point1;
455 const auto& [pos2, dir2] = point2;
456 ATH_MSG_DEBUG(__func__<<"() "<<__LINE__<<" - Integrate field from "<<Amg::toString(pos1)
457 <<" to "<<Amg::toString(pos2)<<", direction changing from "
458 <<inDeg(dir1.theta())<<" / "<<inDeg(dir1.phi())<<" to "
459 <<inDeg(dir2.theta())<<" / "<<inDeg(dir2.phi()));
460
461 Amg::Vector3D locField{Amg::Vector3D::Zero()};
462 Amg::Vector3D accumForce{Amg::Vector3D::Zero()};
463 for (double fieldStep : m_fieldExtpSteps) {
464 const Amg::Vector3D extPos {(1. - fieldStep) * pos1 + fieldStep * pos2};
465 const Amg::Vector3D extDir {((1. - fieldStep) * dir1 + fieldStep * dir2).unit()};
466
467 fieldCache.getField(extPos.data(), locField.data());
468 const Amg::Vector3D locForce {locField.dot(planeNorm) * extDir.cross(planeNorm)};
469 accumForce += locForce;
470
471 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - step: "<<fieldStep
472 <<", pos: "<<Amg::toString(extPos)<<", dir: "<<inDeg(extDir.theta())
473 <<" / "<<inDeg(extDir.phi())<<" --> local |B|: "<<locField.mag()*1e3
474 <<" [T]"<<", |Bnorm|: "<<locField.dot(planeNorm)*Gaudi::Units::GeV
475 <<" [T], local |v x Bnorm|: "<<locForce.mag()*Gaudi::Units::GeV<<" [T].");
476 }
477 const double dS {(pos2 - pos1).mag() / static_cast<double>(m_fieldExtpSteps.size())};
478 ATH_MSG_DEBUG(__func__<<"() "<<__LINE__<<" - Integrated force: "<<Amg::toString(accumForce)<<", dS: "<<dS);
479 return accumForce * dS;
480 }
481 double MsTrackSeederTool::getPtimesQ(const Amg::Vector3D& forceIntegral,
482 const Amg::Vector3D& deltaDir) const {
483 const double PtimesQ {0.3 * Gaudi::Units::GeV * forceIntegral.mag2() / deltaDir.dot(forceIntegral)};
484
485 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - estimateQtimesP() force integral: "<<forceIntegral.mag()<<" [T*m], deltaDir: "
486 <<deltaDir.mag()<<", cos: "<<deltaDir.dot(forceIntegral)/ (deltaDir.mag() * forceIntegral.mag())
487 <<", PtimesQ: "<<PtimesQ/Gaudi::Units::GeV <<" [GeV].");
488 return PtimesQ;
489 }
490 double MsTrackSeederTool::estimateQtimesP(const Acts::GeometryContext& tgContext,
491 const MsTrackSeed& seed,
492 MagField::AtlasFieldCache& magField) const {
493 using namespace Muon::MuonStationIndex;
494
496 double deltaPhiAcc {0.};
497 std::optional<double> centralPhi {};
498 unsigned nSegsWithPhi{0};
499 for (const xAOD::MuonSegment* segment : seed.segments()) {
500 if (segment->nPhiLayers() > 0) {
501 const double segPhi {segment->position().phi()};
502 if (!centralPhi) centralPhi = segPhi;
503 deltaPhiAcc += P4Helpers::deltaPhi(*centralPhi, segPhi);
504 ++nSegsWithPhi;
505 }
506 }
507 const double circPhi {nSegsWithPhi > 0
508 ? P4Helpers::deltaPhi(*centralPhi + deltaPhiAcc / nSegsWithPhi, 0.)
509 : seed.sector().phi()};
510 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Average segment phi: "
511 <<inDeg(circPhi)<<" [deg], nSegsWithPhi: "<<nSegsWithPhi);
512
513 std::array<const xAOD::MuonSegment*, 3> segmentsToUse{};
514 // Try first to find segments in the inner, middle and outer layers. If both a barrel
515 // and endcap segments are present in the same layer, the barrel segment is preferred.
516 for (const xAOD::MuonSegment* segment : seed.segments()) {
517 ChIndex chIndex {segment->chamberIndex()};
518 switch(toLayerIndex(chIndex)) {
519 using enum LayerIndex;
520 case Inner:
521 if (!segmentsToUse[0] || isBarrel(chIndex)) {
522 segmentsToUse[0] = segment;
523 }
524 break;
525 case Middle:
526 if (!segmentsToUse[1] || isBarrel(chIndex)) {
527 segmentsToUse[1] = segment;
528 }
529 break;
530 case Outer:
531 if (!segmentsToUse[2] || isBarrel(chIndex)) {
532 segmentsToUse[2] = segment;
533 }
534 break;
535 default:
536 break;
537 }
538 }
539 unsigned nSegments = std::ranges::count_if(segmentsToUse,
540 [](const xAOD::MuonSegment* seg) { return seg != nullptr; });
541
544 if (nSegments < 3) {
545 auto missingSeg = std::ranges::find(segmentsToUse, nullptr);
546 for (const xAOD::MuonSegment* segment : seed.segments()) {
547 LayerIndex layIndex {toLayerIndex(segment->chamberIndex())};
548 if (layIndex != LayerIndex::Extended && layIndex != LayerIndex::BarrelExtended) {
549 continue;
550 }
551 assert(missingSeg != segmentsToUse.end());
552 *missingSeg = segment;
553 ++nSegments;
554 if (nSegments == 3) {
555 break;
556 }
557 missingSeg = std::ranges::find(segmentsToUse, nullptr);
558 }
559 std::ranges::sort(segmentsToUse, [](const xAOD::MuonSegment* seg1, const xAOD::MuonSegment* seg2) {
560 if (!seg1 || !seg2) {
561 return seg1 != nullptr;
562 }
563 return seg1->position().perp() < seg2->position().perp();
564 });
565 }
566 const Amg::Vector3D planeNorm {Acts::makeDirectionFromPhiTheta(circPhi + 90._degree, 90._degree)};
567 auto point = [&](const xAOD::MuonSegment* seg) {
568 const Amg::Vector3D projSegPos {segPosOntoPhiPlane(tgContext, planeNorm, *seg)};
569
571 if (nMeasurements(*seg) == 0ul) {
572 return std::make_pair(projSegPos,
573 Acts::makeDirectionFromPhiTheta(circPhi, seg->direction().theta()));
574 }
575
576 const Acts::Surface& firstSurf {xAOD::muonSurface(firstMeasurement(*seg))};
577 const Acts::TrackingVolume* volume{
578 MuonGMR4::highestAlignable(m_trackingGeometrySvc->trackingGeometry()->findVolume(volumeId(firstSurf)))};
579
580 if (!volume) {
581 ATH_MSG_WARNING("estimateQtimesP() "<<__LINE__
582 <<" - Failed to find tracking volume for seed measurement "<<volumeId(firstSurf));
583 return std::make_pair(projSegPos,
584 Acts::makeDirectionFromPhiTheta(circPhi, seg->direction().theta()));
585 }
586 const Acts::Transform3& toLoc {volume->globalToLocalTransform(tgContext)};
587 const Amg::Vector3D locSegDir {toLoc.linear() * seg->direction()};
588 const Amg::Vector3D locNormal {toLoc.linear() * planeNorm};
591 const double tanBeta {houghTanBeta(locSegDir)};
592 const double tanAlpha {- (locNormal.y() * tanBeta + locNormal.z()) / locNormal.x()};
593 const Amg::Vector3D newDir = volume->localToGlobalTransform(tgContext).linear() *
594 Acts::makeDirectionFromAxisTangents(tanAlpha, tanBeta);
595
596 return std::make_pair(projSegPos, newDir);
597 };
598 switch (nSegments) {
599 case 3:
600 return estimateQtimesP(planeNorm, point(segmentsToUse[0]),
601 point(segmentsToUse[1]), point(segmentsToUse[2]), magField);
602 case 2:
603 return estimateQtimesP(planeNorm, point(segmentsToUse[0]), point(segmentsToUse[1]), magField);
604 default:
605 break;
606 }
607 return 6.*Gaudi::Units::TeV;
608 }
609 void MsTrackSeederTool::appendSegment(const Acts::GeometryContext& tgContext,
610 const xAOD::MuonSegment* segment,
611 const Location loc,
612 TreeRawVec_t& outContainer) const {
613
614 const unsigned segSector = segment->sector();
615 for (const auto proj : {SectorProjector::leftOverlap,
616 SectorProjector::center,
617 SectorProjector::rightOverlap}) {
619 const ExpandedSector projSector{segSector, proj};
620 if (segment->nPhiLayers() > 0 && projSector != ExpandedSector{segment->position().phi()}) {
621 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Segment @"<<Amg::toString(segment->position())
622 <<" is not in sector "<<projSector);
623 continue;
624 }
625 const Amg::Vector2D refPoint{expressOnCylinder(tgContext, *segment, loc, projSector)};
626 if (!withinBounds(refPoint, loc)) {
627 continue;
628 }
629 using enum SeedCoords;
630 std::array<double, 3> coords{Acts::filledArray<double, 3>(0.)};
632 coords[Acts::toUnderlying(eSector)] = projSector.sector();
635 coords[Acts::toUnderlying(eDetSection)] = Acts::copySign(Acts::toUnderlying(loc), refPoint[1]);
637 coords[Acts::toUnderlying(ePosOnCylinder)] = refPoint[Location::Barrel == loc];
638 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Add segment "<<printSegment(*segment)
639 <<" with "<<coords<<" to the search tree");
640 outContainer.emplace_back(std::move(coords), segment);
641 }
642 }
643 SearchTree_t MsTrackSeederTool::constructTree(const Acts::GeometryContext& tgContext,
644 const xAOD::MuonSegmentContainer& segments) const{
645 TreeRawVec_t rawData{};
646 rawData.reserve(3*segments.size());
647 for (const xAOD::MuonSegment* segment : segments){
648 appendSegment(tgContext, segment, Location::Barrel, rawData);
649 appendSegment(tgContext, segment, Location::Endcap, rawData);
650 }
651 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Create a new tree with "<<rawData.size()<<" entries. ");
652 return SearchTree_t{std::move(rawData)};
653 }
654 StatusCode MsTrackSeederTool::findTrackSeeds(const EventContext& ctx,
655 std::vector<MsTrackSeed>& outputSeeds) const {
656
657 const xAOD::MuonSegmentContainer* segments{nullptr};
658 ATH_CHECK(SG::get(segments, m_segmentKey , ctx));
659 const Acts::GeometryContext tgContext = m_ctxProvider.getGeometryContext(ctx);
660 SearchTree_t orderedSegs{constructTree(tgContext, *segments)};
661 MsTrackSeedContainer trackSeeds{};
662 using enum SeedCoords;
663 for (const auto& [coords, seedingSeg] : orderedSegs) {
666 if (!m_segSelector->passSeedingQuality(ctx, *seedingSeg)) {
667 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Segment "<<printSegment(*seedingSeg)
668 <<" does not pass the seeding quality.");
669 continue;
670 }
672 SearchTree_t::range_t selectRange{};
675 selectRange[Acts::toUnderlying(eDetSection)].shrink(coords[Acts::toUnderlying(eDetSection)] - 0.1,
676 coords[Acts::toUnderlying(eDetSection)] + 0.1);
678 selectRange[Acts::toUnderlying(ePosOnCylinder)].shrink(coords[Acts::toUnderlying(ePosOnCylinder)] - m_seedHalfLength,
679 coords[Acts::toUnderlying(ePosOnCylinder)] + m_seedHalfLength);
681 selectRange[Acts::toUnderlying(eSector)].shrink(coords[Acts::toUnderlying(eSector)] -0.25,
682 coords[Acts::toUnderlying(eSector)] +0.25);
683
684 MsTrackSeed newSeed{static_cast<Location>(std::abs(coords[Acts::toUnderlying(eDetSection)])),
685 ExpandedSector{static_cast<std::int8_t>(coords[Acts::toUnderlying(eSector)])}};
687 newSeed.addSegment(seedingSeg);
689 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Search for compatible segments to "<<printSegment(*seedingSeg)<<".");
690 orderedSegs.rangeSearchMapDiscard(selectRange, [&](
691 const SearchTree_t::coordinate_t& /*coords*/,
692 const xAOD::MuonSegment* extendWithMe) {
694 if (!m_segSelector->compatibleForTrack(ctx, *seedingSeg, *extendWithMe)) {
695 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Segment "<<printSegment(*extendWithMe)<<" is not compatible.");
696 return;
697 }
698 newSeed.addSegment(extendWithMe);
699 });
701 if (newSeed.stations().size() < 2) {
702 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Reject seed with segments only "<<newSeed.stations().size()
703 <<" stations");
704 continue;
705 }
707 const double r = newSeed.location() == Location::Barrel ? 1.*m_barrelRadius
708 : coords[Acts::toUnderlying(ePosOnCylinder)];
709 const double z = newSeed.location() == Location::Barrel ? coords[Acts::toUnderlying(ePosOnCylinder)]
710 : coords[Acts::toUnderlying(eDetSection)]* m_endcapDiscZ;
711
712 Amg::Vector3D pos = r * newSeed.sector().radialDir()
713 + z * Amg::Vector3D::UnitZ();
715 newSeed.setPosition(std::move(pos));
716 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Add new seed "<<newSeed);
717 trackSeeds.emplace_back(std::move(newSeed));
718 }
719 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Found in total "<<trackSeeds.size()<<" before overlap removal");
720 // outputSeeds
721 trackSeeds = resolveOverlaps(std::move(trackSeeds));
722 outputSeeds.insert(outputSeeds.end(), std::make_move_iterator(trackSeeds.begin()),
723 std::make_move_iterator(trackSeeds.end()));
724 return StatusCode::SUCCESS;
725 }
726
727 bool MsTrackSeederTool::splitSeed(const MsTrackSeed& seedCand) const {
728 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Check whether \n"<<seedCand
729 <<"\n has two segments on the same station");
730 const auto seedSegments = seedCand.segments();
732 if (seedSegments.size() == seedCand.stations().size()) {
733 return false;
734 }
735 for (std::size_t seg = 1 ; seg < seedSegments.size(); ++seg) {
736 for (std::size_t seg1 = 0 ; seg1 < seg; ++seg1) {
737 if (seedSegments[seg]->chamberIndex() == seedSegments[seg1]->chamberIndex()) {
738 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Found two segments in the same station on the seed \n"
739 <<" --- "<<printSegment(*seedSegments[seg])<<"\n"
740 <<" --- "<<printSegment(*seedSegments[seg1]));
741 return true;
742 }
743 }
744 }
745 return false;
746 }
747
748
750 MsTrackSeedContainer seedsToSplit{};
751 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Select the seeds amongst "
752 <<unsplitted.size()<<" seed candidates which can be split");
754 std::ranges::copy_if(unsplitted, std::back_inserter(seedsToSplit),
755 [this](const MsTrackSeed& seedCand){
756 return splitSeed(seedCand);
757 });
758 // No splittable candidte found
759 if (seedsToSplit.empty()) {
760 return unsplitted;
761 }
762 // Remove first the seed candidates to be split
763 auto [begin , end] = std::ranges::remove_if(unsplitted, [this](const MsTrackSeed& seedCand){
764 return splitSeed(seedCand);
765 });
766 unsplitted.erase(begin, end);
767 using namespace Muon::MuonStationIndex;
769 std::array<SegVec_t, Acts::toUnderlying(ChIndex::ChIndexMax)> segsPerStation{};
770 std::vector<SegVec_t> splitSeedSegs{};
771 for (const MsTrackSeed& splitMe : seedsToSplit) {
772 for (const xAOD::MuonSegment* seg : splitMe.segments()) {
773 segsPerStation[Acts::toUnderlying(seg->chamberIndex())].push_back(seg);
774 }
775 for (SegVec_t& vec : segsPerStation) {
776 if (vec.empty()) {
777 continue;
778 }
781 if (splitSeedSegs.empty()) {
782 for (const xAOD::MuonSegment* seg : vec) {
783 splitSeedSegs.emplace_back(std::vector{seg});
784 }
785 }
788 else if (vec.size() == 1) {
789 for (SegVec_t& appendMe : splitSeedSegs) {
790 appendMe.push_back(vec.front());
791 }
792 } else {
794 std::ranges::sort(vec, [](const xAOD::MuonSegment* a,
795 const xAOD::MuonSegment* b){
796 if (a->nPrecisionHits() != b->nPrecisionHits()) {
797 return a->nPrecisionHits() > b->nPrecisionHits();
798 }
799 return a->nPhiLayers() > b->nPhiLayers();
800 });
802 std::vector<SegVec_t> expandMe{std::move(splitSeedSegs)};
803 splitSeedSegs.reserve(expandMe.size() * vec.size());
804 for (const xAOD::MuonSegment* seg : vec) {
805 for (const SegVec_t& expand : expandMe) {
806 splitSeedSegs.emplace_back(expand).push_back(seg);
807 }
808 }
809 }
810 // Do not to forget to clear at the end
811 vec.clear();
812 }
814 ATH_MSG_DEBUG(__func__<<"() "<<__LINE__<<" - Split the "
815 <<splitMe<<"\n into "<<splitSeedSegs.size()<<" seeds.");
816 for (SegVec_t splitted : splitSeedSegs){
817 unsplitted.push_back(MsTrackSeed{splitMe.location(), splitMe.sector()});
818 for (const xAOD::MuonSegment* seg: splitted) {
819 unsplitted.back().addSegment(seg);
820 }
821 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Created new "<<unsplitted.back());
822 }
823 splitSeedSegs.clear();
824 }
825 return unsplitted;
826 }
827
829 const MsTrackSeed& seedB) const {
831 using namespace Muon::MuonStationIndex;
832 std::array<std::uint8_t, Acts::toUnderlying(StIndex::StIndexMax)> sharedCounter{};
833
834 for (std::size_t a{0}; a < seedA.segments().size(); ++a){
835 const xAOD::MuonSegment* segA = seedA.segments()[a];
836 std::uint8_t& shared = sharedCounter[Acts::toUnderlying(toStationIndex(segA->chamberIndex()))];
837
838 for(std::size_t b{0}; shared == 0 && b< seedB.segments().size(); ++b){
839 const xAOD::MuonSegment* segB = seedB.segments()[b];
840 if (countShared(*segA, *segB) )
841 shared += (countShared(*segA, *segB) >= m_nHitsShareSeg);
842 }
843 }
844 std::uint8_t shared{0};
845 for (std::uint8_t n : sharedCounter) {
846 shared+=n;
847 }
848 return shared;
849 }
850
851 inline std::uint8_t
853 const xAOD::MuonSegment& b) const {
855 if (a.index() == b.index()) {
856 return a.nPrecisionHits();
857 }
858 if (a.chamberIndex() != b.chamberIndex() || a.sector() != b.sector() ||
859 a.etaIndex() != b.etaIndex()) {
860 return 0u;
861 }
862 if (a.nPrecisionHits() > b.nPrecisionHits()) {
863 return countShared(b, a);
864 }
865 unsigned shared{0};
866 for (std::size_t hitA =0; hitA < nMeasurements(a); ++hitA) {
867 if (isOutlierMeasurement(a, hitA)) {
868 continue;
869 }
871 const xAOD::UncalibratedMeasurement* measA = getMeasurement(a, hitA);
872 if (!xAOD::isPrecisionHit(measA)) {
873 continue;
874 }
875 for (std::size_t hitB = 0 ; hitB < nMeasurements(b); ++hitB) {
876 if (getMeasurement(b, hitB) == measA) {
877 ++shared;
878 break;
879 }
880 }
881 }
882 return shared;
883 }
884
888 std::ranges::sort(unresolved, [](const MsTrackSeed& a, const MsTrackSeed&b) {
889 return a.segments().size() > b.segments().size();
890 });
891 auto [begin, end] = std::ranges::unique(unresolved,
892 [](const MsTrackSeed& a, const MsTrackSeed& b) {
893 const auto segsA = a.segments();
894 const auto segsB = b.segments();
895 if (segsA.size() == segsB.size()) {
896 for (std::size_t s = 0 ; s < segsA.size(); ++s) {
897 if (segsA[s] != segsB[s]) {
898 return false;
899 }
900 }
901 return true;
902 } else if (segsA.size() < segsB.size()) {
903 return std::ranges::all_of(segsA, [&segsB](const xAOD::MuonSegment* seg) {
904 return Acts::rangeContainsValue(segsB, seg);
905 });
906 }
907 return std::ranges::all_of(segsB, [&segsA]( const xAOD::MuonSegment* seg) {
908 return Acts::rangeContainsValue(segsA, seg);
909 });
910 });
911 unresolved.erase(begin, end);
912 MsTrackSeedContainer outputSeeds{splitSeeds(std::move(unresolved))};
913 std::shared_ptr<std::uint8_t> overlapMarker{};
914 for (std::size_t seed = 0; seed < outputSeeds.size(); ++seed) {
916 for (std::size_t seed1 = seed +1 ;seed1 < outputSeeds.size(); ++seed1) {
917 unsigned shared = countShared(outputSeeds.at(seed),
918 outputSeeds.at(seed1));
919 if (shared >= m_nSegShareSeed) {
920 if(!overlapMarker) {
921 overlapMarker = std::make_shared<std::uint8_t>(0);
922 outputSeeds.at(seed).prepareOverlap(overlapMarker);
923 }
924 outputSeeds.at(seed1).prepareOverlap(overlapMarker);
925 }
926 }
927 overlapMarker.reset();
928 }
929 return outputSeeds;
930 }
933 return m_detMgr->getSectorEnvelope(segment.chamberIndex(),
934 segment.sector(),
935 segment.etaIndex());
936 }
937}
Scalar mag() const
mag method
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_DEBUG(x,...)
#define ATH_MSG_ERROR(x,...)
#define ATH_MSG_WARNING(x,...)
#define ATH_MSG_VERBOSE(x,...)
double charge(const T &p)
Definition AtlasPID.h:1025
std::vector< size_t > vec
detray::unit< scalar_t > unit
static Double_t a
size_t size() const
Number of registered mappings.
double angle(const GeoTrf::Vector2D &a, const GeoTrf::Vector2D &b)
constexpr float inDeg(const float rad)
#define z
size_type size() const noexcept
Returns the number of elements in the collection.
Local cache for magnetic field (based on MagFieldServices/AtlasFieldSvcTLS.h).
void getField(const double *ATH_RESTRICT xyz, double *ATH_RESTRICT bxyz, double *ATH_RESTRICT deriv=nullptr)
get B field value at given position xyz[3] is in mm, bxyz[3] is in kT if deriv[9] is given,...
A spectrometer sector forms the envelope of all chambers that are placed in the same MS sector & laye...
const Acts::PlaneSurface & surface() const
Returns the associated surface.
Amg::Vector3D normalDir() const
Returns the vector that is normal to the plane spanned by the expanded sector.
std::int8_t sector() const
Returns the expanded sector number.
Amg::Vector3D radialDir() const
Returns the vector pointing radially along the sector plane.
void addSegment(const xAOD::MuonSegment *seg)
Append a segment to the seed.
Location location() const
Returns the location of the seed.
ExpandedSector sector() const
Returns the seed's sector.
Definition MsTrackSeed.h:66
std::span< const Muon::MuonStationIndex::StIndex > stations() const
The station indices of the associated sements.
std::span< const xAOD::MuonSegment *const > segments() const
Returns the vector of associated segments.
void setPosition(Amg::Vector3D &&pos)
set the seed's position
void appendSegment(const Acts::GeometryContext &tgContext, const xAOD::MuonSegment *segment, const Location loc, TreeRawVec_t &outContainer) const
Append the segment to the raw data container.
SG::ReadHandleKey< xAOD::MuonSegmentContainer > m_segmentKey
Declare the data dependency on the standard Mdt+Rpc+Tgc segment container & on the NSW segment contai...
virtual Acts::Result< Acts::BoundTrackParameters > estimateStartParameters(const EventContext &ctx, const MsTrackSeed &seed) const override final
Estimate the start track parameters for a given track seed.
UnsignedIntegerProperty m_nFieldSteps
number of steps between two segments to integrate the magnetic field
virtual bool withinBounds(const Amg::Vector2D &projPos, const Location loc) const override final
MsTrackSeed::Location Location
Enum toggling whether the segment is in the endcap or barrel.
std::uint8_t countShared(const xAOD::MuonSegment &a, const xAOD::MuonSegment &b) const
Counts the number of shared precision hits betwen two segments Two segment share the same hit if the ...
std::pair< Amg::Vector3D, Amg::Vector3D > PosMomPair_t
ToolHandle< ISegmentSelectionTool > m_segSelector
Pointer to the segement selection tool which compares two segments for their compatibilitiy.
bool splitSeed(const MsTrackSeed &seed) const
Checks whether the seed has collected two segments on the same stations.
SearchTree_t::vector_t TreeRawVec_t
Abbrivation of the KDTree raw data vector.
DoubleProperty m_barrelRadius
The radius of he barrel cylinder.
DoubleProperty m_endcapMomentumRes
Momentum resolution in the endcap.
ServiceHandle< ActsTrk::ITrackingGeometrySvc > m_trackingGeometrySvc
Tracking geometry service.
SearchTree_t constructTree(const Acts::GeometryContext &tgContext, const xAOD::MuonSegmentContainer &segments) const
Construct a complete search tree from a MuonSegment container.
Acts::KDTree< 3, const xAOD::MuonSegment *, double, std::array, 6 > SearchTree_t
Definition of the search tree class.
DoubleProperty m_endcapDiscRadius
Radius of the endcap discs.
UnsignedIntegerProperty m_nHitsShareSeg
Consider two track seeds sharing a segment if their number of precision hits exceeds the threshold.
ActsTrk::ContextUtility m_ctxProvider
Utility to fetch the geometry, magnetic field and calibration context in the event.
UnsignedIntegerProperty m_nSegShareSeed
Consider two segments to be equivalent if they share segments on in x stations.
const MuonGMR4::SpectrometerSector * envelope(const xAOD::MuonSegment &segment) const
Returns the spectrometer envelope associated to the segment (Coord system where the parameter are exp...
std::vector< double > m_fieldExtpSteps
The list of field steps in the force field integration.
std::vector< const xAOD::MuonSegment * > SegVec_t
Abrivate the segment vector.
double getPtimesQ(const Amg::Vector3D &forceIntegral, const Amg::Vector3D &deltaDir) const
Compute the charge times momentum from the integral of lorentz force and the total change in directio...
virtual double estimateQtimesP(const Acts::GeometryContext &tgContext, const MsTrackSeed &seed, MagField::AtlasFieldCache &fieldCache) const override final
Estimate the charge times momentum of a muon track candidate from the contained segments.
virtual StatusCode initialize() override final
virtual StatusCode findTrackSeeds(const EventContext &ctx, std::vector< MsTrackSeed > &outSeeds) const override final
Retrieves the segment container from StoreGate and constructs TrackSeeds from them.
Amg::Vector3D segPosOntoPhiPlane(const Acts::GeometryContext &tgContext, const Amg::Vector3D &planeNormal, const xAOD::MuonSegment &segment) const
Projects the segment position onto the plane with global phi = x The local coordinate system is arran...
SeedCoords
Abrivation of the seed coordinates.
@ eSector
Sector of the associated spectrometer sector.
@ ePosOnCylinder
Extrapolation position along the cylinder surface.
@ eDetSection
Encode the seed location (-1,1 -> endcaps, 0 -> barrel.
virtual Amg::Vector2D expressOnCylinder(const Acts::GeometryContext &tgContext, const xAOD::MuonSegment &segment, const Location loc, const ExpandedSector sector) const override final
Expresses the passed segment on the virtual cylinder constructed by the track seeder.
const MuonGMR4::MuonDetectorManager * m_detMgr
MsTrackSeedContainer splitSeeds(MsTrackSeedContainer &&unsplitted) const
MsTrackSeedContainer resolveOverlaps(MsTrackSeedContainer &&unresolved) const
Removes exact duplciates or partial subsets of the MsTrackSeeds.
DoubleProperty m_endcapDiscZ
Position of the endcap discs.
DoubleProperty m_seedHalfLength
Maximum separation of point on the cylinder to be picked up onto a seed.
DoubleProperty m_barrelMomentumRes
Momentum resolution in the barrel.
DoubleProperty m_barrelLength
The maximum length of the barrel cylinder, if not capped by the placement of the endcap discs.
Amg::Vector3D forceIntegration(const PosMomPair_t &point1, const PosMomPair_t &point2, const Amg::Vector3D &planeNorm, MagField::AtlasFieldCache &fieldCache) const
Compute the integral of magnetic force (v x B ) dS along a trajectory, given the initial and final po...
Amg::Vector3D direction() const
Returns the direction as Amg::Vector.
::Muon::MuonStationIndex::TechnologyIndex technology() const
Returns the main technology of the segment.
::Muon::MuonStationIndex::ChIndex chamberIndex() const
Returns the chamber index.
Amg::Vector3D position() const
Returns the position as Amg::Vector.
int etaIndex() const
Returns the eta index, which corresponds to stationEta in the offline identifiers (and the ).
std::uint8_t nPhiLayers() const
Returns the number of trigger phi hits.
int r
Definition globals.cxx:22
constexpr double energyToActs(const double athenaE)
Converts an energy scalar from Athena to Acts units.
Acts::Vector4 convertPosToActs(const Amg::Vector3D &athenaPos, const double athenaTime=0.)
Converts a position vector & time from Athena units into Acts units.
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, 2, 1 > Vector2D
Eigen::Matrix< double, 3, 1 > Vector3D
const Acts::Surface * bottomBoundary(const Acts::TrackingVolume &volume)
Returns the boundary surface parallel to the x-y plane at negative local z.
const Acts::TrackingVolume * highestAlignable(const Acts::TrackingVolume *volume)
Returns the highest parent volume that is alignable.
Acts::BoundTrackParameters boundSegmentPars(const ActsTrk::GeometryContext &gctx, const MuonGMR4::MuonDetectorManager &detMgr, const xAOD::MuonSegment &segment, const Acts::ParticleHypothesis hypot=Acts::ParticleHypothesis::muon())
Returns the segment parameters as boundTrackParameters.
Parameters localSegmentPars(const xAOD::MuonSegment &seg)
Returns the localSegPars decoration from a xAODMuon::Segment.
This header ties the generic definitions in this package.
std::vector< MsTrackSeed > MsTrackSeedContainer
Definition MsTrackSeed.h:96
const xAOD::UncalibratedMeasurement * getMeasurement(const xAOD::MuonSegment &segment, const std::size_t n)
Returns the n-th uncalibrated measurement.
std::string printSegment(const xAOD::MuonSegment &seg)
Print the details of a segment.
double houghTanBeta(const Amg::Vector3D &v)
Returns the hough tanBeta [y] / [z].
std::size_t nMeasurements(const xAOD::MuonSegment &segment)
Returns the number of associated Uncalibrated measurements.
Acts::GeometryIdentifier volumeId(const Acts::Surface &surface)
Returns the identifier of the volume in which the surface is embedded.
bool isOutlierMeasurement(const xAOD::MuonSegment &segment, const std::size_t n)
Returns whether the n-the uncalibrated measurement is an outlier.
const xAOD::UncalibratedMeasurement * firstMeasurement(const xAOD::MuonSegment &segment, const bool skipOutlier=true)
Retrieves the first measurement associated with the segment.
Amg::Vector3D atFirstSurface(const Acts::GeometryContext &gctx, const xAOD::MuonSegment &segment, const bool skipOutlier=true)
Expresses the segment position on the surface of the first measurement.
MsTrackSeederTool::SegVec_t SegVec_t
double houghTanAlpha(const Amg::Vector3D &v)
: Returns the hough tanAlpha [x] / [z]
MsTrackSeederTool::SearchTree_t SearchTree_t
constexpr float inDeg(const float rad)
ChIndex chIndex(const std::string &index)
convert ChIndex name string to enum
StIndex toStationIndex(ChIndex index)
convert ChIndex into StIndex
bool isBarrel(const ChIndex index)
Returns true if the chamber index points to a barrel chamber.
LayerIndex
enum to classify the different layers in the muon spectrometer
LayerIndex toLayerIndex(ChIndex index)
convert ChIndex into LayerIndex
ChIndex
enum to classify the different chamber layers in the muon spectrometer
double deltaPhi(double phiA, double phiB)
delta Phi in range [-pi,pi[
Definition P4Helpers.h:34
const T * get(const ReadCondHandleKey< T > &key, const EventContext &ctx)
Convenience function to retrieve an object given a ReadCondHandleKey.
void swap(ElementLinkVector< DOBJ > &lhs, ElementLinkVector< DOBJ > &rhs)
UncalibratedMeasurement_v1 UncalibratedMeasurement
Define the version of the uncalibrated measurement class.
MuonSegmentContainer_v1 MuonSegmentContainer
Definition of the current "MuonSegment container version".
bool isPrecisionHit(const UncalibratedMeasurement *meas)
Returns whether the measurement is a precision hit.
MuonSegment_v1 MuonSegment
Reference the current persistent version:
const Acts::Surface & muonSurface(const UncalibratedMeasurement *meas)
Returns the associated Acts surface to the measurement.
Tell the compiler to optimize assuming that FP may trap.