ATLAS Offline Software
Loading...
Searching...
No Matches
ActsMuonTrackingGeometryTest.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
9#include "GaudiKernel/EventContext.h"
10#include "GaudiKernel/ISvcLocator.h"
15
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
37namespace {
38using SegLink_t = std::vector<ElementLink<xAOD::MuonSegmentContainer>>;
39static const SG::ConstAccessor<SegLink_t> segAcc{"truthSegmentLinks"};
40static const Amg::Vector3D dummyVec{100.*Gaudi::Units::m, 100.*Gaudi::Units::m, 100.*Gaudi::Units::m};
41
42struct PropagatorRecorder{
44 Amg::Vector3D actsPropPos{dummyVec};
46 Amg::Vector3D actsGlobalPos{dummyVec};
48 Amg::Vector3D actsPropDir{Amg::Vector3D::Zero()};
50 Identifier id{};
52 double actsPropabsMomentum{0.};
54 double actsStepSize{0.};
56 double actsHitWireDist{-1.};
57};
58
59}
60
61
62namespace ActsTrk {
63
64
66 const MuonGMR4::MuonReadoutElement* reElement = m_r4DetMgr->getReadoutElement(hitId);
67 const IdentifierHash trfHash = reElement->detectorType() == ActsTrk::DetectorType::Mdt ?
68 reElement->measurementHash(hitId) : reElement->layerHash(hitId);
69 return reElement->globalToLocalTransform(gctx, trfHash);
70 }
71
73 const MuonGMR4::MuonReadoutElement* reElement = m_r4DetMgr->getReadoutElement(hitId);
74 const IdentifierHash trfHash = reElement->detectorType() == ActsTrk::DetectorType::Mdt ?
75 reElement->measurementHash(hitId) : reElement->layerHash(hitId);
76 return reElement->localToGlobalTransform(gctx, trfHash);
77 }
78
79
81 return m_r4DetMgr->getReadoutElement(id)->layerHash(id);
82 }
83
84
86 ATH_CHECK(AthHistogramAlgorithm::initialize());
87 ATH_CHECK(m_idHelperSvc.retrieve());
89 ATH_CHECK(m_detMgrKey.initialize());
90 ATH_CHECK(m_rndmGenSvc.retrieve());
91 ATH_CHECK(m_geoCtxKey.initialize());
93 ATH_CHECK(m_truthParticleKey.initialize());
94 ATH_CHECK(m_truthSegLinkKey.initialize());
95 ATH_CHECK(detStore()->retrieve(m_r4DetMgr));
96
97 ATH_CHECK(m_tree.init(this));
98 ATH_MSG_INFO("ActsMuonTrackingGeometryTest successfully initialized");
99 return StatusCode::SUCCESS;
100 }
101
103 ATH_CHECK(m_tree.write());
104 return StatusCode::SUCCESS;
105 }
106
107 StatusCode ActsMuonTrackingGeometryTest::execute(const EventContext& ctx) {
108
109
110 const ActsTrk::GeometryContext* gctx{nullptr};
111 const AtlasFieldCacheCondObj* fieldCondObj{nullptr};
112 const MuonGM::MuonDetectorManager* detMgr{nullptr};
113 const xAOD::TruthParticleContainer* truthParticles{nullptr};
114
115 ATH_CHECK(SG::get(fieldCondObj, m_fieldCacheCondObjInputKey, ctx));
116 ATH_CHECK(SG::get(detMgr, m_detMgrKey, ctx));
117 ATH_CHECK(SG::get(truthParticles, m_truthParticleKey, ctx));
118 ATH_CHECK(SG::get(gctx, m_geoCtxKey, ctx));
119
120
121 const Acts::MagneticFieldContext mfContext = Acts::MagneticFieldContext(fieldCondObj);
122
123 auto anygctx = gctx->context();
124
125 //Get the tracking geometry
126 auto trackingGeometry = m_trackingGeometryTool->trackingGeometry();
127
128 if (!trackingGeometry) {
129 ATH_MSG_ERROR("Failed to retrieve the tracking geometry");
130 return StatusCode::FAILURE;
131 }
132
133 //Configure the ACTS propagator with the navigator and the stepper
134 using Stepper = Acts::EigenStepper<>;
135 using Navigator = Acts::Navigator;
136 using Propagator = Acts::Propagator<Stepper,Navigator>;
137 using ActorList = Acts::ActorList<Acts::detail::SteppingLogger, Acts::MaterialInteractor, Acts::EndOfWorldReached>;
138 using PropagatorOptions = Propagator::Options<ActorList>;
139
140 Navigator::Config navCfg;
141 navCfg.trackingGeometry = trackingGeometry;
142 navCfg.resolveSensitive = true;
143 navCfg.resolveMaterial= false;
144 navCfg.resolvePassive = true;
145
146 Navigator navigator(navCfg, Acts::getDefaultLogger("Navigator", ActsTrk::actsLevelVector(msgLevel())));
147
148 auto bField = std::make_shared<ATLASMagneticFieldWrapper>();
149 auto bfieldCache = bField->makeCache(mfContext);
150
151 auto stepper = Stepper(bField);
152
153 PropagatorOptions options(anygctx, mfContext);
154 options.pathLimit = m_pathLimit;
155 options.stepping.maxStepSize = m_maxStepSize;
156 options.stepping.stepTolerance = m_stepTolerance;
157 options.maxSteps = m_maxSteps;
158 options.maxTargetSkipping = m_maxTargetSkipping;
159
160
161 //switch off material interactions
162 auto& materialInteractor = options.actorList.get<Acts::MaterialInteractor>();
163 materialInteractor.energyLoss = false;
164 materialInteractor.multipleScattering = false;
165 materialInteractor.recordInteractions = false;
166
167 Propagator propagator(std::move(stepper), std::move(navigator),
168 Acts::getDefaultLogger("Propagator", ActsTrk::actsLevelVector(msgLevel())));
169
170 for(const xAOD::TruthParticle* truthParticle : *truthParticles) {
171
172 ATH_MSG_DEBUG("Processing truth particle with PDG ID: " << truthParticle->pdgId() << " ,pT: "
173 << truthParticle->pt() << " , p: " << truthParticle->p4().P() << ", eta: " << truthParticle->eta() << " , phi: " << truthParticle->phi());
174
175 //select only muons or geantinos particles
176 if(std::abs(truthParticle->pdgId()) != 13 && std::abs(truthParticle->pdgId()) != 998) {
177 ATH_MSG_VERBOSE("Skipping truth particle with PDG ID: " << truthParticle->pdgId()<<" only muons or charged geantinos are being processed");
178 continue;
179 }
180
181 Acts::ParticleHypothesis actsParticleHypothesis = truthParticle->pdgId() == 998 ?
182 Acts::ParticleHypothesis::chargedGeantino() : Acts::ParticleHypothesis::muon();
183
184
185 //take the truth segments witht he sim hits
186 std::vector<std::pair<const xAOD::MuonSegment*, std::vector<const xAOD::MuonSimHit*>>> muonSegmentWithSimHits;
187 const SegLink_t& segLink = segAcc(*truthParticle);
188 if (segLink.empty()) {
189 ATH_MSG_WARNING("No segment link found for truth particle with PDG ID: " << truthParticle->pdgId());
190 continue;
191 }
192 for(const auto& truthSegLink: segLink) {
193 const xAOD::MuonSegment* seg{*truthSegLink};
194 if(!seg){
195 continue;
196 }
197 auto unordedHits = MuonR4::getMatchingSimHits(*seg);
198 std::vector<const xAOD::MuonSimHit*> muonSimHits{unordedHits.begin(), unordedHits.end()};
199 ATH_MSG_VERBOSE("Segment at : "<<Amg::toString(seg->position())<<" with Sim Hits: "<<unordedHits.size());
200 muonSegmentWithSimHits.emplace_back(seg,muonSimHits);
201
202 }
203
204 const auto& particle = truthParticle->p4();
205 m_truthPt = particle.Pt();
206 m_truthP = particle.P();
207
208 const xAOD::TruthVertex* prodVertex = truthParticle->prodVtx();
209 Amg::Vector3D prodPos = prodVertex ? Amg::Vector3D(prodVertex->x(), prodVertex->y(), prodVertex->z())
210 : Amg::Vector3D::Zero();
211 Amg::Vector3D prodDir{Amg::dirFromAngles(particle.Phi(), particle.Theta())};
212 ATH_MSG_VERBOSE("Truth particle produced at " << Amg::toString(prodPos) << " with direction " << Amg::toString(prodDir));
213
214 //position, direction and momentum at the start of the propagation
215 Amg::Vector3D startPropPos{prodPos};
216 Amg::Vector3D startPropDir{prodDir};
217 double startPropP{energyToActs(particle.P())};
218
220
221 if(muonSegmentWithSimHits.empty()) {
222 ATH_MSG_DEBUG("No segments found for truth particle with PDG ID: " << truthParticle->pdgId());
223 continue;
224 }
225 // sort the segments by their radial distance
226 std::ranges::sort(muonSegmentWithSimHits,
227 [](const auto& a, const auto& b) {
228 return a.first->position().perp() < b.first->position().perp();
229 });
230
231
232 //sort the sim hits of the first segment to get the starting propagation position as the first one
233 std::vector<const xAOD::MuonSimHit*>& muonSimHits = muonSegmentWithSimHits.front().second;
234 std::ranges::sort(muonSimHits,
235 [this, &gctx](const xAOD::MuonSimHit* hit1, const xAOD::MuonSimHit* hit2){
236 const Amg::Vector3D globalPos1 = toGlobalTrf(*gctx,hit1->identify())*xAOD::toEigen(hit1->localPosition());
237 const Amg::Vector3D globalPos2 = toGlobalTrf(*gctx,hit2->identify())*xAOD::toEigen(hit2->localPosition());
238 return globalPos1.norm()<globalPos2.norm();
239 });
240
241 ATH_MSG_VERBOSE("After sorting..");
242 for(const auto& simHit: muonSimHits){
243 ATH_MSG_VERBOSE("Sim Hit of first segment at position: "<<Amg::toString(toGlobalTrf(*gctx, simHit->identify())*xAOD::toEigen(simHit->localPosition())));
244
245 }
246
247
248 startPropPos = toGlobalTrf(*gctx, muonSimHits.front()->identify())*xAOD::toEigen(muonSimHits.front()->localPosition());
249 startPropDir = toGlobalTrf(*gctx, muonSimHits.front()->identify()).linear()*xAOD::toEigen(muonSimHits.front()->localDirection());
250 startPropP = energyToActs(muonSimHits.front()->kineticEnergy());
251
252
253 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);
254
255
256 }
257
258 ATH_MSG_VERBOSE("Starting propagation from"<<Amg::toString(startPropPos)<<" with direction"
259 <<Amg::toString(startPropDir)<<" and momentum "<<startPropP);
260
261
262
263 Acts::BoundTrackParameters start = Acts::BoundTrackParameters::createCurvilinear(
264 Acts::VectorHelpers::makeVector4(startPropPos, 0.), startPropDir,
265 truthParticle->charge() / startPropP,
266 std::nullopt,
267 actsParticleHypothesis);
268
269 ATH_MSG_DEBUG("start propagating here");
270 auto result = propagator.propagate(start, options);
271 const Acts::detail::SteppingLogger::result_type state = result.value().get<Acts::detail::SteppingLogger::result_type>();
272 const Acts::MaterialInteractor::result_type material = result.value().get<Acts::MaterialInteractor::result_type>();
273
274 m_propSteps = state.steps.size();
275 ATH_MSG_DEBUG("Number of propagated steps : " << m_propSteps);
276 m_propLength = result.value().pathLength;
277 std::vector<PropagatorRecorder> propagatedHits;
278
279 for(const auto& step : state.steps) {
280 if(!step.surface) {
281 continue;
282 }
283
284 const SurfaceCache* sCache = dynamic_cast<const SurfaceCache *>(step.surface->surfacePlacement());
285 if(!sCache) {
286 ATH_MSG_VERBOSE("Surface found but it's a portal, continuing..");
287 continue;
288 }
289 const Identifier ID = sCache->identify();
290 const Amg::Transform3D toGap{toLocalTrf(*gctx, ID)};
291 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()));
292
293
294 PropagatorRecorder newRecord{};
295 newRecord.id = ID;
296 newRecord.actsPropPos = toGap*step.position;
297 newRecord.actsGlobalPos = step.position;
298 newRecord.actsPropDir = toGap.linear()*step.momentum.unit();
299 newRecord.actsPropabsMomentum = step.momentum.norm();
300 newRecord.actsStepSize = step.stepSize.value();
301 //calculate the distance of the propagated hit to the wire in case of MDTs
302
303 if(m_idHelperSvc->isMdt(ID)){
304 //get the surface
305 const auto* tubeSurf = dynamic_cast<const Acts::StrawSurface*>(&sCache->surface());
306 if(tubeSurf){
307 Amg::Vector3D wirePos = toGap*tubeSurf->center(anygctx);
308 Amg::Vector3D wireDir = toGap.linear()*tubeSurf->lineDirection(anygctx);
309 Amg::Vector3D localPropHit = newRecord.actsPropPos;
310 Amg::Vector3D dirPropHit = newRecord.actsPropDir;
311 double distToWire = Amg::lineDistance(wirePos, wireDir, localPropHit, dirPropHit);
312 newRecord.actsHitWireDist = distToWire;
313 }
314
315 }
316
317 propagatedHits.emplace_back(newRecord);
318
319 }
320
321
322 for (const auto&[segment, simHits] : muonSegmentWithSimHits) {
323 for(const xAOD::MuonSimHit* simHit : simHits){
324
325 const Identifier ID = simHit->identify();
326
327 const Amg::Vector3D localPos = xAOD::toEigen(simHit->localPosition());
328
329 const Amg::Vector3D globalPos = toGlobalTrf(*gctx, simHit->identify())*xAOD::toEigen(simHit->localPosition());
330 const Amg::Vector3D localDir = xAOD::toEigen(simHit->localDirection());
331
332 if(m_r4DetMgr->getReadoutElement(ID)->detectorType() == ActsTrk::DetectorType::sTgc && localPos.z() != 0.0){
333 continue;
334
335 }
336
337 m_detId.push_back(ID);
338 m_techIdx.push_back(toInt(m_idHelperSvc->technologyIndex(ID)));
339 m_gasGapId.push_back(layerHash(ID));
340
341 m_truthLoc.push_back(localPos);
342 m_truthDir.push_back(localDir);
343 m_truthGlob.push_back(globalPos);
344 m_startGlob.push_back(startPropPos);
345 ATH_MSG_DEBUG("Truth hit with ID: " << m_idHelperSvc->toString(ID)
346 << " at local position: " << Amg::toString(localPos)
347 << " and global position: " << Amg::toString(globalPos)
348 << " and direction: " << Amg::toString(localDir));
349
350 //get the matching propagated hits
351 auto it_begin = std::ranges::find_if(propagatedHits,
352 [this, ID](const auto& propagatedHit) {
353 return m_idHelperSvc->detElId(ID) == m_idHelperSvc->detElId(propagatedHit.id) &&
354 layerHash(ID) == layerHash(propagatedHit.id);
355 });
356 m_isPropagated.push_back(it_begin != propagatedHits.end());
357
358 if(it_begin == propagatedHits.end()){
359 m_actsPropLoc.push_back(dummyVec);
360 Amg::Vector3D zero = Amg::Vector3D::Zero();
361 m_actsPropDir.push_back(zero);
362 m_actsPropGlob.push_back(dummyVec);
363 m_actsPropabsMomentum.push_back(0.);
364 m_actsStepSize.push_back(0.);
365 m_actsHitWireDist.push_back(-1.);
366 continue;
367 }
368
369 //find - if any - propagated hits on the same layer
370 auto it_end = std::find_if(it_begin, propagatedHits.end(),
371 [this, ID](const auto& propagatedHit) {
372 return m_idHelperSvc->detElId(ID) != m_idHelperSvc->detElId(propagatedHit.id) ||
373 layerHash(ID) != layerHash(propagatedHit.id);
374 });
375
377 auto it = std::min_element(it_begin, it_end,
378 [&localPos](const PropagatorRecorder& a,
379 const PropagatorRecorder& b){
380 return (localPos - a.actsPropPos).mag() < (localPos - b.actsPropPos).mag();
381 });
382
383 ATH_MSG_DEBUG("Found propagated hit with ID: " << m_idHelperSvc->toString(it->id)
384 << " at local position: " << Amg::toString(it->actsPropPos)
385 << " and global position: " << Amg::toString(it->actsGlobalPos)
386 << " and direction: " << Amg::toString(it->actsPropDir));
387
388 m_actsPropLoc.push_back(it->actsPropPos);
389 m_actsPropDir.push_back(it->actsPropDir);
390 m_actsPropGlob.push_back(it->actsGlobalPos);
391 m_actsPropabsMomentum.push_back(it->actsPropabsMomentum);
392 m_actsStepSize.push_back(it->actsStepSize);
393 m_actsHitWireDist.push_back(it->actsHitWireDist);
394
395
396 }
397 }
398
399 m_event = ctx.eventID().event_number();
400 m_tree.fill(ctx);
401
402
403 }
404
405
406 return StatusCode::SUCCESS;
407 }
408
409}
410
Scalar mag() const
mag method
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_ERROR(x)
#define ATH_MSG_INFO(x)
#define ATH_MSG_VERBOSE(x)
#define ATH_MSG_WARNING(x)
#define ATH_MSG_DEBUG(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
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
PublicToolHandle< ActsTrk::ITrackingGeometryTool > m_trackingGeometryTool
Acts::GeometryContext context() const
virtual DetectorType detectorType() const =0
Returns the detector element type.
: Helper class to connect the aligned transformations of each active sensor(layer) with the Acts::Sur...
Identifier identify() const override final
Returns the identifier of the Surface.
const Acts::Surface & surface() const override final
Returns the dereferenced pointer cache.
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::Transform3D & localToGlobalTransform(const ActsTrk::GeometryContext &ctx) const
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.