ATLAS Offline Software
Loading...
Searching...
No Matches
MsTrackFindingAlg.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3*/
4
5#include "MsTrackFindingAlg.h"
6
7#include "Acts/Surfaces/PerigeeSurface.hpp"
8#include "Acts/Surfaces/PlaneSurface.hpp"
9#include "Acts/Surfaces/detail/PlanarHelper.hpp"
10
11
17
18#include "GaudiKernel/PhysicalConstants.h"
21
23#include "GaudiKernel/PhysicalConstants.h"
26
27#include <system_error>
28
29using namespace Acts::UnitLiterals;
30using namespace Acts::PlanarHelper;
31
32namespace {
33 bool isNswSegment(const xAOD::MuonSegment& seg) {
36 }
37}
38
39namespace MuonR4{
41 ATH_CHECK(m_segmentKey.initialize());
42 ATH_CHECK(m_idHelperSvc.retrieve());
43 ATH_CHECK(detStore()->retrieve(m_detMgr));
44 ATH_CHECK(m_segSelector.retrieve());
45 ATH_CHECK(m_msTrkSeedKey.initialize());
46
47 ATH_CHECK(m_visualizationTool.retrieve(EnableTool{!m_visualizationTool.empty()}));
50 ATH_CHECK(m_trackFitTool.retrieve());
51 ATH_CHECK(m_calibTool.retrieve());
52 ATH_CHECK(m_writeKey.initialize());
53
54 if (m_trackingGeometryTool->trackingGeometry()->geometryVersion() !=
55 Acts::TrackingGeometry::GeometryVersion::Gen3){
56 ATH_MSG_ERROR("The MS track fit requires the Gen 3 geometry format");
57 return StatusCode::FAILURE;
58 }
59
60 MsTrackSeeder::Config seederCfg{};
62 seederCfg.selector = m_segSelector.get();
63 seederCfg.detMgr = m_detMgr;
64
65 m_seeder = std::make_unique<MsTrackSeeder>(name(), std::move(seederCfg));
66 return StatusCode::SUCCESS;
67 }
68
70
71 StatusCode MsTrackFindingAlg::execute(const EventContext& ctx) const {
72 ATH_MSG_VERBOSE("Run track finding in event "<<ctx.eventID().event_number());
73
74 const xAOD::MuonSegmentContainer* allEventSegs{nullptr};
75 ATH_CHECK(SG::get(allEventSegs, m_segmentKey, ctx));
76
77 auto seedContainer = findTrackSeeds(ctx, *allEventSegs);
78
79 const Acts::GeometryContext tgContext = m_trackingGeometryTool->getGeometryContext(ctx).context();
80 const Acts::MagneticFieldContext mfContext = m_extrapolationTool->getMagneticFieldContext(ctx);
81 const Acts::CalibrationContext calContext{ActsTrk::getCalibrationContext(ctx)};
82
83
84 Acts::VectorTrackContainer trackBackend{};
85 Acts::VectorMultiTrajectory trackStateBackend{};
86 ActsTrk::MutableTrackContainer cacheTrkContainer{std::move(trackBackend),
87 std::move(trackStateBackend)};
89 cacheTrkContainer.addColumn<std::size_t>("parentSeed");
90 unsigned seedIdx{0};
91 for (const MsTrackSeed& seed : *seedContainer) {
92 if (!fitSeedCandidate(tgContext, mfContext, calContext, seed,
93 cacheTrkContainer)) {
94 ++seedIdx;
95 continue;
96 }
97 auto lastTrack = cacheTrkContainer.getTrack(cacheTrkContainer.size() -1);
98 lastTrack.component<std::size_t, Acts::hashString("parentSeed")>() = seedIdx;
99 ++seedIdx;
100 }
101 SG::WriteHandle writeHandleSeed{m_msTrkSeedKey, ctx};
102 ATH_CHECK(writeHandleSeed.record(std::move(seedContainer)));
103
104 // Constant declination
105 Acts::ConstVectorTrackContainer ctrackBackend{std::move(cacheTrkContainer.container())};
106 Acts::ConstVectorMultiTrajectory ctrackStateBackend{std::move(cacheTrkContainer.trackStateContainer())};
107 auto ctc = std::make_unique<ActsTrk::TrackContainer>(std::move(ctrackBackend),
108 std::move(ctrackStateBackend));
109
110 SG::WriteHandle writeHandle{m_writeKey, ctx};
111 ATH_CHECK(writeHandle.record(std::move(ctc)));
112 return StatusCode::SUCCESS;
113 }
114
115 std::unique_ptr<MsTrackSeedContainer>
116 MsTrackFindingAlg::findTrackSeeds(const EventContext& ctx,
117 const xAOD::MuonSegmentContainer& segments) const {
118
119 auto seedContainer = m_seeder->findTrackSeeds(ctx, m_trackingGeometryTool->getGeometryContext(ctx), segments);
120
121 if (!m_visualizationTool.empty()) {
122 m_visualizationTool->displaySeeds(ctx, *m_seeder, segments, *seedContainer);
123 }
124 return seedContainer;
125 }
128 MsTrackFindingAlg::prepareFit(const Acts::GeometryContext& tgContext,
129 const Acts::MagneticFieldContext& mfContext,
130 const Acts::CalibrationContext& calContext,
131 const MsTrackSeed& seed) const {
132 const EventContext& ctx{*calContext.get<const EventContext*>()};
133 MeasVec_t measurements{};
134 measurements.reserve(100);
136 const xAOD::MuonSegment* refSeg{nullptr};
137 for (const xAOD::MuonSegment* segment : seed.segments()) {
139 m_calibTool->stampSignsOnMeasurements(*segment);
140 MeasVec_t segMeasurements = collectMeasurements(*segment, /*skipOutlier:*/ true);
141 if (msgLvl(MSG::VERBOSE)) {
142 std::stringstream sstr{};
143 for (const xAOD::UncalibratedMeasurement* m : segMeasurements) {
144 const Acts::Surface& surf{xAOD::muonSurface(m)};
145 sstr<<" *** "<<m_idHelperSvc->toString(xAOD::identify(m))
146 <<", "<<m->numDimensions()<<", "
147 <<", "<<surf.geometryId()<<" @ "<<Amg::toString(surf.localToGlobalTransform(tgContext))<<std::endl;
148 }
149 ATH_MSG_VERBOSE("Fetch measurements from segment: "<<Amg::toString(segment->position())
150 <<", direction: "<<Amg::toString(segment->direction()) << " eta " << segment->direction().eta() << " phi " << segment->direction().phi() <<"\n"<<sstr.str());
151 }
152 measurements.insert(measurements.end(),
153 std::make_move_iterator(segMeasurements.begin()),
154 std::make_move_iterator(segMeasurements.end()));
155
156 // Ususally we would like to take the first segment with a sufficient amount of phi hits to set the initial position and direction of the track fit. However in some cases the segment from the NSW has a missreconstructed phi direction which causes the track fit to loose all BW and OW hits in the first iteration. Therefore if the first segment is a NSW segment we first try use a non-NSW segments with enough phi hits. If we don't find any segment with enough phi hits we will use the NSW segment as reference as long as it passes the seeding quality criteria.
157 if (!refSeg && !isNswSegment(*segment) && m_segSelector->passSeedingQuality(ctx, *detailedSegment(*segment))) {
158 refSeg = segment;
159 ATH_MSG_VERBOSE("Set reference segment");
160 }
161 }
162 //if we did not find a reference segment let's try the NSW one before we give up on the track
163 if(!refSeg){
164 for (const xAOD::MuonSegment* segment : seed.segments()) {
165 if (isNswSegment(*segment) && m_segSelector->passSeedingQuality(ctx, *detailedSegment(*segment))) {
166 refSeg = segment;
167 ATH_MSG_VERBOSE("Set reference segment from NSW");
168 break;
169 }
170 }
171 }
172
173 if (!refSeg || measurements.empty()) {
174 ATH_MSG_WARNING(__func__<<"() "<<__LINE__
175 <<" - No reference segment passing seeding quality "<<
176 (refSeg != nullptr)<<" was found. #"<<measurements.size()<<" measurements. ");
177 return std::make_pair(Acts::Result<Acts::BoundTrackParameters>::failure(std::make_error_code(std::errc::invalid_argument)),
178 std::vector<const xAOD::UncalibratedMeasurement_v1*>{});
179 }
180 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - "<<measurements.size()<<" measurements");
181 Amg::Vector3D seedPos{refSeg->position()};
182 Amg::Vector3D seedDir{refSeg->direction()};
183 ATH_MSG_DEBUG(__func__<<"() "<<__LINE__<<" - Initial seed pos: "<<Amg::toString(seedPos)
184 <<", dir: "<<Amg::toString(seedDir) << " eta " << seedDir.eta() << " phi " << seedDir.phi() /Gaudi::Units::degree );
188 if (refSeg != seed.segments().front()) {
189 const MuonGMR4::SpectrometerSector* innerPlane = m_seeder->envelope(*seed.segments().front());
190 const Acts::PlaneSurface& surf = innerPlane->surface();
191 const Amg::Transform3D toInnerPlane = surf.localToGlobalTransform(tgContext).inverse();
192 const Amg::Vector3D locSeedPos = toInnerPlane * seedPos;
193 const Amg::Vector3D locSeedDir = toInnerPlane.linear() * seedDir;
194
195 auto seedOnInner = Acts::PlanarHelper::intersectPlane(locSeedPos, locSeedDir,
196 Amg::Vector3D::UnitZ(), 0.);
197
198 using enum SegmentFit::ParamDefs;
199 SegmentFit::Parameters innerPars = SegmentFit::localSegmentPars(*seed.segments().front());
200 innerPars[Acts::toUnderlying(x0)] = seedOnInner.position().x();
201 const Amg::Vector3D innerSegDir =
202 Acts::makeDirectionFromPhiTheta(innerPars[Acts::toUnderlying(phi)],
203 innerPars[Acts::toUnderlying(theta)]);
204 const Amg::Vector3D combSegDir =
205 Acts::makeDirectionFromAxisTangents(houghTanAlpha(locSeedDir),
206 houghTanBeta(innerSegDir));
207 seedPos = surf.localToGlobalTransform(tgContext) * Amg::Vector3D{innerPars[Acts::toUnderlying(x0)],
208 innerPars[Acts::toUnderlying(y0)], 0};
209 seedDir = surf.localToGlobalTransform(tgContext).linear() * combSegDir;
210 }
217 const xAOD::UncalibratedMeasurement* firstVolumeMeas{nullptr};
218 const Acts::TrackingVolume* volume{nullptr};
219 for (const xAOD::UncalibratedMeasurement* meas : measurements) {
220 const Acts::GeometryIdentifier volId = volumeId(xAOD::muonSurface(meas));
221 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Check measurement "
222 <<m_idHelperSvc->toString(xAOD::identify(meas))<<", "<<xAOD::muonSurface(meas).geometryId());
223 volume = m_trackingGeometryTool->trackingGeometry()->findVolume(volId);
224 if (volume) {
225 firstVolumeMeas = meas;
226 break;
227 }
228 }
229
230 if (!volume) {
231 ATH_MSG_WARNING(__func__<<"() "<<__LINE__
232 <<" - Failed to find tracking volume for any seed measurement");
233 return std::make_pair(Acts::Result<Acts::BoundTrackParameters>::failure(std::make_error_code(std::errc::invalid_argument)),
234 std::vector<const xAOD::UncalibratedMeasurement_v1*>{});
235 }
236 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Using seed measurement "
237 << m_idHelperSvc->toString(xAOD::identify(firstVolumeMeas))
238 << " with volume id " << volumeId(xAOD::muonSurface(firstVolumeMeas)));
239
240 auto boundSurf = MuonGMR4::bottomBoundary(*volume);
241 if (!boundSurf) {
242 ATH_MSG_WARNING(__func__<<"() "<<__LINE__<<" - Failed to find boundary surface for tracking volume");
243 return std::make_pair(Acts::Result<Acts::BoundTrackParameters>::failure(std::make_error_code(std::errc::invalid_argument)),
244 std::vector<const xAOD::UncalibratedMeasurement_v1*>{});
245 }
246 auto targetSurf = boundSurf->getSharedPtr();
247 using namespace Acts::PlanarHelper;
248
249 auto pIsect = intersectPlane(seedPos, seedDir,
250 targetSurf->localToGlobalTransform(tgContext).linear().col(2),
251 targetSurf->center(tgContext));
252
253 auto fourPos = ActsTrk::convertPosToActs(pIsect.position(), pIsect.position().mag() / Gaudi::Units::c_light);
254 const double qOverP = 1./ m_seeder->estimateQtimesP(*tgContext.get<const ActsTrk::GeometryContext*>(),
255 *mfContext.get<const AtlasFieldCacheCondObj*>(), seed);
256 auto initialPars = Acts::BoundTrackParameters::create(tgContext, targetSurf, fourPos,
257 seedDir, ActsTrk::energyToActs(qOverP),
258 Acts::BoundMatrix::Identity(),
259 Acts::ParticleHypothesis::muon());
260 return std::make_pair(std::move(initialPars), std::move(measurements));
261
262 }
263 bool MsTrackFindingAlg::fitSeedCandidate(const Acts::GeometryContext& tgContext,
264 const Acts::MagneticFieldContext& mfContext,
265 const Acts::CalibrationContext& calContext,
266 const MsTrackSeed& seed,
267 ActsTrk::MutableTrackContainer& outContainer) const {
268
269 ATH_MSG_DEBUG(__func__<<"() "<<__LINE__<<" - Attempt to fit a new track seed \n"<<seed);
270 const EventContext& ctx{*calContext.get<const EventContext*>()};
271 const auto [initialPars, measurements] = prepareFit(tgContext, mfContext, calContext, seed);
272
273 if (!initialPars.ok()) {
274 ATH_MSG_WARNING(__func__<<"() "<<__LINE__<<" - Failed to construct valid parameters for seed \n"<<seed);
275 if (m_visualizationTool.isEnabled()) {
276 m_visualizationTool->displayTrackSeedObj(ctx, seed, initialPars, "FailedStartPars");
277 }
278 return false;
279 }
280 auto fitTraject = m_trackFitTool->fit(measurements, *initialPars,
281 tgContext, mfContext, calContext,
282 &(*initialPars).referenceSurface());
283 if (!fitTraject || fitTraject->size() == 0) {
284 ATH_MSG_DEBUG(__func__<<"() "<<__LINE__<<" - Fit failed ");
285 if (m_visualizationTool.isEnabled()) {
286 m_visualizationTool->displayTrackSeedObj(ctx, seed, initialPars, "FailedFit");
287 }
288 return false;
289 }
290
291 auto track = fitTraject->getTrack(0);
292 const Amg::Vector3D trkP4 = ActsTrk::convertMomFromActs(track.fourMomentum()).first;
293 double pt = trkP4.perp() / 1000; //in GeV
294 if(pt < 2 ) {
295 double chi2PerDoF = track.chi2() / (std::max(track.nDoF(), 1u));
296 ATH_MSG_DEBUG(" ===cat dog: found low pt track candidate with pt "<<pt<<" GeV chi2/ndof "<< chi2PerDoF << " chi2 "<< track.chi2() << " nDOF "<< track.nDoF() <<"eta: "<<trkP4.eta());
297
298 track.container().trackStateContainer().visitBackwards(track.tipIndex(), [&](const auto& state) {
299 if(state.hasUncalibratedSourceLink()){
300 const auto* uncalib = dynamic_cast<const xAOD::MuonMeasurement*>(ActsTrk::detail::xAODUncalibMeasCalibrator::unpack(state.getUncalibratedSourceLink()));
301 if(uncalib){
302 ATH_MSG_DEBUG(" has meas: "<<m_idHelperSvc->toString(xAOD::identify(uncalib)) << " state " << state.typeFlags());
303 }
304 }
305 }
306 );
307
308 }
313 {
314 fitTraject->addColumn<std::vector<const xAOD::MuonSegment*>>("muonSegLinks");
315 fitTraject->getTrack(0).component<std::vector<const xAOD::MuonSegment*>>("muonSegLinks") = seed.segments();
316 }
317 outContainer.ensureDynamicColumns(*fitTraject);
318 auto destProxy = outContainer.getTrack(outContainer.addTrack());
319 destProxy.copyFrom(fitTraject->getTrack(0));
320 ATH_MSG_DEBUG(__func__<<"() "<<__LINE__<<" - Good track fit...");
321 if (m_visualizationTool.isEnabled()) {
322 m_visualizationTool->displayTrackSeedObj(ctx, seed,
323 destProxy.createParametersAtReference(), "GoodFit");
324 }
325 return true;
326 }
327
328}
Scalar phi() const
phi method
Scalar theta() const
theta method
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_ERROR(x)
#define ATH_MSG_VERBOSE(x)
#define ATH_MSG_WARNING(x)
#define ATH_MSG_DEBUG(x)
ATLAS-specific HepMC functions.
const ServiceHandle< StoreGateSvc > & detStore() const
bool msgLvl(const MSG::Level lvl) const
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.
Acts::Result< Acts::BoundTrackParameters > OptBoundPars_t
Gaudi::Property< double > m_seedHalfLength
Maximum search window to search segments for.
std::unique_ptr< MsTrackSeedContainer > findTrackSeeds(const EventContext &ctx, const xAOD::MuonSegmentContainer &segments) const
Iterates over the search tree and combines close-by segments to a track seed.
std::vector< const xAOD::UncalibratedMeasurement * > MeasVec_t
SG::WriteHandleKey< ActsTrk::TrackContainer > m_writeKey
Key to the output track container.
std::unique_ptr< MsTrackSeeder > m_seeder
Pointer to the actual seeder implementation.
SG::WriteHandleKey< MsTrackSeedContainer > m_msTrkSeedKey
Temporary container write handle to push the seeds to store gate for later efficiency analysis.
virtual StatusCode execute(const EventContext &ctx) const override final
Standard algorithm execution hook.
ToolHandle< ISpacePointCalibrator > m_calibTool
Calibration tool to fill the track states.
ToolHandle< MuonValR4::ITrackVisualizationTool > m_visualizationTool
Visualization tool to debug the track finding.
const MuonGMR4::MuonDetectorManager * m_detMgr
Pointer to the MuonDetectorManager.
ToolHandle< ActsTrk::IFitterTool > m_trackFitTool
Track fitting tool.
PublicToolHandle< ActsTrk::ITrackingGeometryTool > m_trackingGeometryTool
Tracking geometry tool.
virtual StatusCode initialize() override final
Standard algorithm hook to setup the extrapolator, retrieve the tools and declare algorithm's data de...
ToolHandle< ISegmentSelectionTool > m_segSelector
Segment selection tool to pick the good quality segments.
std::pair< OptBoundPars_t, MeasVec_t > prepareFit(const Acts::GeometryContext &tgContext, const Acts::MagneticFieldContext &mfContext, const Acts::CalibrationContext &calContext, const MsTrackSeed &seed) const
Prepares the input by the fit by collecting the measurements on the segment &.
ToolHandle< ActsTrk::IExtrapolationTool > m_extrapolationTool
Track extrapolation tool.
SG::ReadHandleKey< xAOD::MuonSegmentContainer > m_segmentKey
Declare the data dependency on the standard Mdt+Rpc+Tgc segment container & on the NSW segment contai...
ServiceHandle< Muon::IMuonIdHelperSvc > m_idHelperSvc
IdHelperSvc to decode the Identifiers.
bool fitSeedCandidate(const Acts::GeometryContext &gCtx, const Acts::MagneticFieldContext &mCtx, const Acts::CalibrationContext &cCtx, const MsTrackSeed &seed, ActsTrk::MutableTrackContainer &outContainer) const
Attempts to fit the track seed candidate to a full track and returns whether the fit succeeded.
StatusCode record(std::unique_ptr< T > data)
Record a const object to the store.
Amg::Vector3D direction() const
Returns the direction as Amg::Vector.
::Muon::MuonStationIndex::TechnologyIndex technology() const
Returns the main technology of the segment.
Amg::Vector3D position() const
Returns the position as Amg::Vector.
constexpr double energyToActs(const double athenaE)
Converts an energy scalar from Athena to Acts units.
std::pair< Amg::Vector3D, double > convertMomFromActs(const Acts::Vector4 &actsMom)
Converts an Acts four-momentum vector into an pair of an Athena three-momentum and the paritcle's ene...
Acts::TrackContainer< MutableTrackBackend, MutableTrackStateBackend, Acts::detail::ValueHolder > MutableTrackContainer
Acts::CalibrationContext getCalibrationContext(const EventContext &ctx)
The Acts::Calibration context is piped through the Acts fitters to (re)calibrate the Acts::SourceLink...
Acts::Vector4 convertPosToActs(const Amg::Vector3D &athenaPos, const double athenaTime=0.)
Converts a position vector & time from Athena units into Acts units.
std::string toString(const Translation3D &translation, int precision=4)
GeoPrimitvesToStringConverter.
Eigen::Affine3d Transform3D
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.
SeedingAux::FitParIndex ParamDefs
Use the same parameter indices as used by the CompSpacePointAuxiliaries.
Parameters localSegmentPars(const xAOD::MuonSegment &seg)
Returns the localSegPars decoration from a xAODMuon::Segment.
Acts::Experimental::CompositeSpacePointLineFitter::ParamVec_t Parameters
This header ties the generic definitions in this package.
double houghTanBeta(const Amg::Vector3D &v)
Returns the hough tanBeta [y] / [z].
std::vector< const xAOD::UncalibratedMeasurement * > collectMeasurements(const xAOD::MuonSegment &segment, bool skipOutlier=true)
Helper function to extract the measurements from the segment.
Acts::GeometryIdentifier volumeId(const Acts::Surface &surface)
Returns the identifier of the volume in which the surface is embedded.
double houghTanAlpha(const Amg::Vector3D &v)
: Returns the hough tanAlpha [x] / [z]
const Segment * detailedSegment(const xAOD::MuonSegment &seg)
Helper function to navigate from the xAOD::MuonSegment to the MuonR4::Segment.
const T * get(const ReadCondHandleKey< T > &key, const EventContext &ctx)
Convenience function to retrieve an object given a ReadCondHandleKey.
MuonSegmentContainer_v1 MuonSegmentContainer
Definition of the current "MuonSegment container version".
const Identifier & identify(const UncalibratedMeasurement *meas)
Returns the associated identifier from the muon measurement.
MuonSegment_v1 MuonSegment
Reference the current persistent version:
const Acts::Surface & muonSurface(const UncalibratedMeasurement *meas)
Returns the associated Acts surface to the measurement.
UncalibratedMeasurement_v1 UncalibratedMeasurement
Define the version of the uncalibrated measurement class.
Configuration object.
const MuonGMR4::MuonDetectorManager * detMgr
Detector manager to fetch the sector enevelope transforms.
double seedHalfLength
Maximum separation of point on the cylinder to be picked up onto a seed.
const ISegmentSelectionTool * selector
Pointer to the segement selection tool which compares two segments for their compatibilitiy.