ATLAS Offline Software
Loading...
Searching...
No Matches
ActsMuonTrackingGeometryTest.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3*/
4
6
9#include "GaudiKernel/EventContext.h"
10#include "GaudiKernel/ISvcLocator.h"
14
22
23
24
25//ACTS
26#include "Acts/Definitions/Units.hpp"
27#include "Acts/EventData/ParticleHypothesis.hpp"
28#include "Acts/Propagator/ActorList.hpp"
29#include "Acts/Propagator/Propagator.hpp"
30#include "Acts/Propagator/MaterialInteractor.hpp"
31#include "Acts/Propagator/EigenStepper.hpp"
32#include "Acts/Propagator/Navigator.hpp"
33#include "Acts/Propagator/detail/SteppingLogger.hpp"
34#include "Acts/Surfaces/StrawSurface.hpp"
35#include "Acts/Utilities/AngleHelpers.hpp"
36
37#include <chrono>
38
39namespace {
40using SegLink_t = std::vector<ElementLink<xAOD::MuonSegmentContainer>>;
41static const SG::ConstAccessor<SegLink_t> segAcc{"truthSegmentLinks"};
42static const Amg::Vector3D dummyVec{100.*Gaudi::Units::m, 100.*Gaudi::Units::m, 100.*Gaudi::Units::m};
43
44struct PropagatorRecorder{
46 Amg::Vector3D actsPropPos{dummyVec};
48 Amg::Vector3D actsGlobalPos{dummyVec};
50 Amg::Vector3D actsPropDir{Amg::Vector3D::Zero()};
52 Identifier id{};
54 double actsPropabsMomentum{0.};
56 double actsStepSize{0.};
58 double actsHitWireDist{-1.};
59};
60
61}
62
63
64namespace ActsTrk {
65
66
68 const MuonGMR4::MuonReadoutElement* reElement = m_r4DetMgr->getReadoutElement(hitId);
69 const IdentifierHash trfHash = reElement->detectorType() == ActsTrk::DetectorType::Mdt ?
70 reElement->measurementHash(hitId) : reElement->layerHash(hitId);
71 return reElement->globalToLocalTransform(gctx, trfHash);
72 }
73
75 const MuonGMR4::MuonReadoutElement* reElement = m_r4DetMgr->getReadoutElement(hitId);
76 const IdentifierHash trfHash = reElement->detectorType() == ActsTrk::DetectorType::Mdt ?
77 reElement->measurementHash(hitId) : reElement->layerHash(hitId);
78 return reElement->localToGlobalTransform(gctx, trfHash);
79 }
80
81
83 return m_r4DetMgr->getReadoutElement(id)->layerHash(id);
84 }
85
86
88 ATH_CHECK(AthHistogramAlgorithm::initialize());
89 ATH_CHECK(m_idHelperSvc.retrieve());
91 ATH_CHECK(m_detMgrKey.initialize());
92 ATH_CHECK(m_rndmGenSvc.retrieve());
93 ATH_CHECK(m_geoCtxKey.initialize());
95 ATH_CHECK(m_truthParticleKey.initialize());
96 ATH_CHECK(m_truthSegLinkKey.initialize());
97 ATH_CHECK(detStore()->retrieve(m_r4DetMgr));
98
99 ATH_CHECK(m_tree.init(this));
100 ATH_MSG_INFO("ActsMuonTrackingGeometryTest successfully initialized");
101 return StatusCode::SUCCESS;
102 }
103
105 ATH_CHECK(m_tree.write());
106 return StatusCode::SUCCESS;
107 }
108
109 StatusCode ActsMuonTrackingGeometryTest::execute(const EventContext& ctx) {
110
111
112 const ActsTrk::GeometryContext* gctx{nullptr};
113 const AtlasFieldCacheCondObj* fieldCondObj{nullptr};
114 const MuonGM::MuonDetectorManager* detMgr{nullptr};
115 const xAOD::TruthParticleContainer* truthParticles{nullptr};
116
117 ATH_CHECK(SG::get(fieldCondObj, m_fieldCacheCondObjInputKey, ctx));
118 ATH_CHECK(SG::get(detMgr, m_detMgrKey, ctx));
119 ATH_CHECK(SG::get(truthParticles, m_truthParticleKey, ctx));
120 ATH_CHECK(SG::get(gctx, m_geoCtxKey, ctx));
121
122
123 const Acts::MagneticFieldContext mfContext = Acts::MagneticFieldContext(fieldCondObj);
124
125 auto anygctx = gctx->context();
126
127 //Get the tracking geometry
128 auto trackingGeometry = m_trackingGeometrySvc->trackingGeometry();
129
130 if (!trackingGeometry) {
131 ATH_MSG_ERROR("Failed to retrieve the tracking geometry");
132 return StatusCode::FAILURE;
133 }
134
135 //Configure the ACTS propagator with the navigator and the stepper
136 using Stepper = Acts::EigenStepper<>;
137 using Navigator = Acts::Navigator;
138 using Propagator = Acts::Propagator<Stepper,Navigator>;
139 using ActorList = Acts::ActorList<Acts::detail::SteppingLogger, Acts::MaterialInteractor, Acts::EndOfWorldReached>;
140 using PropagatorOptions = Propagator::Options<ActorList>;
141
142 Navigator::Config navCfg;
143 navCfg.trackingGeometry = trackingGeometry;
144 navCfg.resolveSensitive = true;
145 navCfg.resolveMaterial= false;
146 navCfg.resolvePassive = true;
147
148 Navigator navigator(navCfg, Acts::getDefaultLogger("Navigator", ActsTrk::actsLevelVector(msgLevel())));
149
150 auto bField = std::make_shared<ATLASMagneticFieldWrapper>();
151 auto bfieldCache = bField->makeCache(mfContext);
152
153 auto stepper = Stepper(bField);
154
155 PropagatorOptions options(anygctx, mfContext);
156 options.pathLimit = m_pathLimit;
157 options.stepping.maxStepSize = m_maxStepSize;
158 options.stepping.stepTolerance = m_stepTolerance;
159 options.maxSteps = m_maxSteps;
160 options.maxTargetSkipping = m_maxTargetSkipping;
161
162
163 //switch off material interactions
164 auto& materialInteractor = options.actorList.get<Acts::MaterialInteractor>();
165 materialInteractor.energyLoss = false;
166 materialInteractor.multipleScattering = false;
167 materialInteractor.recordInteractions = false;
168
169 Propagator propagator(std::move(stepper), std::move(navigator),
170 Acts::getDefaultLogger("Propagator", ActsTrk::actsLevelVector(msgLevel())));
171
172 for(const xAOD::TruthParticle* truthParticle : *truthParticles) {
173
174 ATH_MSG_DEBUG("Processing truth particle with PDG ID: " << truthParticle->pdgId() << " ,pT: "
175 << truthParticle->pt() << " , p: " << truthParticle->p4().P() << ", eta: " << truthParticle->eta() << " , phi: " << truthParticle->phi());
176
177 //select only muons or geantinos particles
178 if(std::abs(truthParticle->pdgId()) != 13 && std::abs(truthParticle->pdgId()) != 998) {
179 ATH_MSG_VERBOSE("Skipping truth particle with PDG ID: " << truthParticle->pdgId()<<" only muons or charged geantinos are being processed");
180 continue;
181 }
182
183 Acts::ParticleHypothesis actsParticleHypothesis = truthParticle->pdgId() == 998 ?
184 Acts::ParticleHypothesis::chargedGeantino() : Acts::ParticleHypothesis::muon();
185
186
187 //take the truth segments witht he sim hits
188 std::vector<std::pair<const xAOD::MuonSegment*, std::vector<const xAOD::MuonSimHit*>>> muonSegmentWithSimHits;
189 const SegLink_t& segLink = segAcc(*truthParticle);
190 if (segLink.empty()) {
191 ATH_MSG_WARNING("No segment link found for truth particle with PDG ID: " << truthParticle->pdgId());
192 continue;
193 }
194 for(const auto& truthSegLink: segLink) {
195 const xAOD::MuonSegment* seg{*truthSegLink};
196 if(!seg){
197 continue;
198 }
199 auto unordedHits = MuonR4::getMatchingSimHits(*seg);
200 std::vector<const xAOD::MuonSimHit*> muonSimHits{unordedHits.begin(), unordedHits.end()};
201 ATH_MSG_VERBOSE("Segment at : "<<Amg::toString(seg->position())<<" with Sim Hits: "<<unordedHits.size());
202 muonSegmentWithSimHits.emplace_back(seg,muonSimHits);
203
204 }
205
206 const auto& particle = truthParticle->p4();
207 m_truthPt = particle.Pt();
208 m_truthP = particle.P();
209 m_eta = particle.Eta();
210 m_phi = particle.Phi();
211
212 const xAOD::TruthVertex* prodVertex = truthParticle->prodVtx();
213 Amg::Vector3D prodPos = prodVertex ? Amg::Vector3D(prodVertex->x(), prodVertex->y(), prodVertex->z())
214 : Amg::Vector3D::Zero();
215 Amg::Vector3D prodDir{Amg::dirFromAngles(particle.Phi(), particle.Theta())};
216 ATH_MSG_VERBOSE("Truth particle produced at " << Amg::toString(prodPos) << " with direction " << Amg::toString(prodDir));
217
218 //position, direction and momentum at the start of the propagation
219 Amg::Vector3D startPropPos{prodPos};
220 Amg::Vector3D startPropDir{prodDir};
221 double startPropP{energyToActs(particle.P())};
222
224
225 if(muonSegmentWithSimHits.empty()) {
226 ATH_MSG_DEBUG("No segments found for truth particle with PDG ID: " << truthParticle->pdgId());
227 continue;
228 }
229 // sort the segments by their radial distance
230 std::ranges::sort(muonSegmentWithSimHits,
231 [](const auto& a, const auto& b) {
232 return a.first->position().perp() < b.first->position().perp();
233 });
234
235
236 //sort the sim hits of the first segment to get the starting propagation position as the first one
237 std::vector<const xAOD::MuonSimHit*>& muonSimHits = muonSegmentWithSimHits.front().second;
238 std::ranges::sort(muonSimHits,
239 [this, &gctx](const xAOD::MuonSimHit* hit1, const xAOD::MuonSimHit* hit2){
240 const Amg::Vector3D globalPos1 = toGlobalTrf(*gctx,hit1->identify())*xAOD::toEigen(hit1->localPosition());
241 const Amg::Vector3D globalPos2 = toGlobalTrf(*gctx,hit2->identify())*xAOD::toEigen(hit2->localPosition());
242 return globalPos1.norm()<globalPos2.norm();
243 });
244
245 ATH_MSG_VERBOSE("After sorting..");
246 for(const auto& simHit: muonSimHits){
247 ATH_MSG_VERBOSE("Sim Hit of first segment at position: "<<Amg::toString(toGlobalTrf(*gctx, simHit->identify())*xAOD::toEigen(simHit->localPosition())));
248
249 }
250
251
252 startPropPos = toGlobalTrf(*gctx, muonSimHits.front()->identify())*xAOD::toEigen(muonSimHits.front()->localPosition());
253 startPropDir = toGlobalTrf(*gctx, muonSimHits.front()->identify()).linear()*xAOD::toEigen(muonSimHits.front()->localDirection());
254 startPropP = energyToActs(muonSimHits.front()->kineticEnergy());
255
256
257 ATH_MSG_VERBOSE("Kinetic Energy from the simHit: "<<startPropP / Gaudi::Units::GeV<<" and mass: "<<muonSimHits.front()->mass()<<" and energy deposit: "<<muonSimHits.front()->energyDeposit() / Gaudi::Units::eV);
258
259
260 }
261
262 ATH_MSG_VERBOSE("Starting propagation from"<<Amg::toString(startPropPos)<<" with direction"
263 <<Amg::toString(startPropDir)<<" and momentum "<<startPropP);
264
265
266
267 Acts::BoundTrackParameters start = Acts::BoundTrackParameters::createCurvilinear(
268 Acts::VectorHelpers::makeVector4(startPropPos, 0.), startPropDir,
269 truthParticle->charge() / startPropP,
270 std::nullopt,
271 actsParticleHypothesis);
272
273 ATH_MSG_DEBUG("start propagating here");
274 //measure the propagation time and save it to the ntuple for performance validation
275 //start the clock
276 const auto propagationStart = std::chrono::steady_clock::now();
277 auto result = propagator.propagate(start, options);
278 const auto propagationEnd = std::chrono::steady_clock::now(); //stop the clock
279
280 const Acts::detail::SteppingLogger::result_type state = result.value().get<Acts::detail::SteppingLogger::result_type>();
281 const Acts::MaterialInteractor::result_type material = result.value().get<Acts::MaterialInteractor::result_type>();
282
283 m_propSteps = state.steps.size();
284 m_propTime = (std::chrono::duration<double>(propagationEnd - propagationStart).count()) * 1000;
285
286 ATH_MSG_DEBUG("Number of propagated steps : " << m_propSteps<< "in [clock time]: "<< m_propTime<<" ms");
287 m_propLength = result.value().pathLength;
288 std::vector<PropagatorRecorder> propagatedHits;
289
290 for(const auto& step : state.steps) {
291 if(!step.surface) {
292 continue;
293 }
294
295 const auto* sCache = dynamic_cast<const ISurfacePlacement*>(step.surface->surfacePlacement());
296 if(!sCache) {
297 ATH_MSG_VERBOSE("Surface found but it's a portal, continuing..");
298 continue;
299 }
300 const Identifier ID = sCache->identify();
301 const Amg::Transform3D toGap{toLocalTrf(*gctx, ID)};
302 ATH_MSG_VERBOSE("Identify propagated hit " << m_idHelperSvc->toString(ID) << " with hit at local position " << Amg::toString(toGap*step.position)<<" and global direction "<<Amg::toString(step.momentum.unit()));
303
304
305 PropagatorRecorder newRecord{};
306 newRecord.id = ID;
307 newRecord.actsPropPos = toGap*step.position;
308 newRecord.actsGlobalPos = step.position;
309 newRecord.actsPropDir = toGap.linear()*step.momentum.unit();
310 newRecord.actsPropabsMomentum = step.momentum.norm();
311 newRecord.actsStepSize = step.stepSize.value();
312 //calculate the distance of the propagated hit to the wire in case of MDTs
313
314 if(m_idHelperSvc->isMdt(ID)){
315 //get the surface
316 const auto* tubeSurf = dynamic_cast<const Acts::StrawSurface*>(&sCache->surface());
317 if(tubeSurf){
318 Amg::Vector3D wirePos = toGap*tubeSurf->center(anygctx);
319 Amg::Vector3D wireDir = toGap.linear()*tubeSurf->lineDirection(anygctx);
320 Amg::Vector3D localPropHit = newRecord.actsPropPos;
321 Amg::Vector3D dirPropHit = newRecord.actsPropDir;
322 double distToWire = Amg::lineDistance(wirePos, wireDir, localPropHit, dirPropHit);
323 newRecord.actsHitWireDist = distToWire;
324 }
325
326 }
327
328 propagatedHits.emplace_back(newRecord);
329
330 }
331
332
333 for (const auto&[segment, simHits] : muonSegmentWithSimHits) {
334 for(const xAOD::MuonSimHit* simHit : simHits){
335
336 const Identifier ID = simHit->identify();
337
338 const Amg::Vector3D localPos = xAOD::toEigen(simHit->localPosition());
339
340 const Amg::Vector3D globalPos = toGlobalTrf(*gctx, simHit->identify())*xAOD::toEigen(simHit->localPosition());
341 const Amg::Vector3D localDir = xAOD::toEigen(simHit->localDirection());
342
343 if(m_r4DetMgr->getReadoutElement(ID)->detectorType() == ActsTrk::DetectorType::sTgc && localPos.z() != 0.0){
344 continue;
345
346 }
347
348 m_detId.push_back(ID);
349 m_techIdx.push_back(toInt(m_idHelperSvc->technologyIndex(ID)));
350 m_gasGapId.push_back(layerHash(ID));
351
352 m_truthLoc.push_back(localPos);
353 m_truthDir.push_back(localDir);
354 m_truthGlob.push_back(globalPos);
355 m_startGlob.push_back(startPropPos);
356 ATH_MSG_DEBUG("Truth hit with ID: " << m_idHelperSvc->toString(ID)
357 << " at local position: " << Amg::toString(localPos)
358 << " and global position: " << Amg::toString(globalPos)
359 << " and direction: " << Amg::toString(localDir));
360
361 //get the matching propagated hits
362 auto it_begin = std::ranges::find_if(propagatedHits,
363 [this, ID](const auto& propagatedHit) {
364 return m_idHelperSvc->detElId(ID) == m_idHelperSvc->detElId(propagatedHit.id) &&
365 layerHash(ID) == layerHash(propagatedHit.id);
366 });
367 m_isPropagated.push_back(it_begin != propagatedHits.end());
368
369 if(it_begin == propagatedHits.end()){
370 m_actsPropLoc.push_back(dummyVec);
371 Amg::Vector3D zero = Amg::Vector3D::Zero();
372 m_actsPropDir.push_back(zero);
373 m_actsPropGlob.push_back(dummyVec);
374 m_actsPropabsMomentum.push_back(0.);
375 m_actsStepSize.push_back(0.);
376 m_actsHitWireDist.push_back(-1.);
377 continue;
378 }
379
380 //find - if any - propagated hits on the same layer
381 auto it_end = std::find_if(it_begin, propagatedHits.end(),
382 [this, ID](const auto& propagatedHit) {
383 return m_idHelperSvc->detElId(ID) != m_idHelperSvc->detElId(propagatedHit.id) ||
384 layerHash(ID) != layerHash(propagatedHit.id);
385 });
386
388 auto it = std::min_element(it_begin, it_end,
389 [&localPos](const PropagatorRecorder& a,
390 const PropagatorRecorder& b){
391 return (localPos - a.actsPropPos).mag() < (localPos - b.actsPropPos).mag();
392 });
393
394 ATH_MSG_DEBUG("Found propagated hit with ID: " << m_idHelperSvc->toString(it->id)
395 << " at local position: " << Amg::toString(it->actsPropPos)
396 << " and global position: " << Amg::toString(it->actsGlobalPos)
397 << " and direction: " << Amg::toString(it->actsPropDir));
398
399 m_actsPropLoc.push_back(it->actsPropPos);
400 m_actsPropDir.push_back(it->actsPropDir);
401 m_actsPropGlob.push_back(it->actsGlobalPos);
402 m_actsPropabsMomentum.push_back(it->actsPropabsMomentum);
403 m_actsStepSize.push_back(it->actsStepSize);
404 m_actsHitWireDist.push_back(it->actsHitWireDist);
405
406
407 }
408 }
409
410 m_event = ctx.eventID().event_number();
411 m_tree.fill(ctx);
412
413
414 }
415
416
417 return StatusCode::SUCCESS;
418 }
419
420}
421
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,...)
#define ATH_MSG_INFO(x,...)
ATLAS-specific HepMC functions.
static Double_t a
ElementLink< xAOD::MuonSegmentContainer > SegLink_t
MuonVal::ScalarBranch< unsigned int > & m_propSteps
MuonVal::VectorBranch< unsigned short > & m_isPropagated
MuonVal::VectorBranch< unsigned short > & m_gasGapId
MuonVal::VectorBranch< float > & m_actsHitWireDist
SG::ReadHandleKey< xAOD::TruthParticleContainer > m_truthParticleKey
const MuonGMR4::MuonDetectorManager * m_r4DetMgr
ServiceHandle< Muon::IMuonIdHelperSvc > m_idHelperSvc
ServiceHandle< ActsTrk::ITrackingGeometrySvc > m_trackingGeometrySvc
Amg::Transform3D toGlobalTrf(const ActsTrk::GeometryContext &gctx, const Identifier &hitId) const
StatusCode execute(const EventContext &ctx) override
Execute method.
SG::ReadDecorHandleKey< xAOD::TruthParticleContainer > m_truthSegLinkKey
MuonVal::VectorBranch< float > & m_actsPropabsMomentum
SG::ReadCondHandleKey< AtlasFieldCacheCondObj > m_fieldCacheCondObjInputKey
IdentifierHash layerHash(const Identifier &id) const
MuonVal::ScalarBranch< unsigned int > & m_event
Amg::Transform3D toLocalTrf(const ActsTrk::GeometryContext &gctx, const Identifier &hitId) const
MuonVal::VectorBranch< unsigned short > & m_techIdx
SG::ReadCondHandleKey< MuonGM::MuonDetectorManager > m_detMgrKey
MuonVal::VectorBranch< float > & m_actsStepSize
Acts::GeometryContext context() const
virtual DetectorType detectorType() const =0
Returns the detector element type.
Extension of the interface of the Acts::SurfacePlacementBase for ATLAS.
const ServiceHandle< StoreGateSvc > & detStore() const
This is a "hash" representation of an Identifier.
MuonReadoutElement is an abstract class representing the geometry of a muon detector.
const Amg::Isometry3D & localToGlobalTransform(const ActsTrk::GeometryContext &ctx) const override final
Returns the transformation from the local coordinate system of the readout element into the global AT...
Amg::Transform3D globalToLocalTransform(const ActsTrk::GeometryContext &ctx) const
Returns the transformation from the global ATLAS coordinate system into the local coordinate system o...
virtual IdentifierHash measurementHash(const Identifier &measId) const =0
The measurement hash is a continous numbering schema of all readout channels described by the specifi...
virtual IdentifierHash layerHash(const Identifier &measId) const =0
The layer hash removes the bits from the IdentifierHash corresponding to the measurement's channel nu...
The MuonDetectorManager stores the transient representation of the Muon Spectrometer geometry and pro...
Helper class to provide constant type-safe access to aux data.
Amg::Vector3D position() const
Returns the position as Amg::Vector.
Identifier identify() const
Returns the global ATLAS identifier of the SimHit.
ConstVectorMap< 3 > localPosition() const
Returns the local postion of the traversing particle.
float z() const
Vertex longitudinal distance along the beam line form the origin.
float y() const
Vertex y displacement.
float x() const
Vertex x displacement.
void zero(TH2 *h)
zero the contents of a 2d histogram
The AlignStoreProviderAlg loads the rigid alignment corrections and pipes them through the readout ge...
constexpr double energyToActs(const double athenaE)
Converts an energy scalar from Athena to Acts units.
@ sTgc
Micromegas (NSW).
@ Mdt
MuonSpectrometer.
Acts::Logging::Level actsLevelVector(MSG::Level lvl)
std::string toString(const Translation3D &translation, int precision=4)
GeoPrimitvesToStringConverter.
double lineDistance(const AmgVector(N)&posA, const AmgVector(N)&dirA, const AmgVector(N)&posB, const AmgVector(N)&dirB)
: Calculates the shortest distance between two lines
Amg::Vector3D dirFromAngles(const double phi, const double theta)
Constructs a direction vector from the azimuthal & polar angles.
Eigen::Affine3d Transform3D
Eigen::Matrix< double, 3, 1 > Vector3D
std::unordered_set< const xAOD::MuonSimHit * > getMatchingSimHits(const xAOD::MuonSegment &segment)
: Returns all sim hits matched to a xAOD::MuonSegment
const T * get(const ReadCondHandleKey< T > &key, const EventContext &ctx)
Convenience function to retrieve an object given a ReadCondHandleKey.
TruthVertex_v1 TruthVertex
Typedef to implementation.
Definition TruthVertex.h:15
MuonSimHit_v1 MuonSimHit
Defined the version of the MuonSimHit.
Definition MuonSimHit.h:12
TruthParticle_v1 TruthParticle
Typedef to implementation.
MuonSegment_v1 MuonSegment
Reference the current persistent version:
TruthParticleContainer_v1 TruthParticleContainer
Declare the latest version of the truth particle container.