10 #include "Acts/Definitions/Units.hpp"
11 #include "Acts/Propagator/ActorList.hpp"
12 #include "Acts/Propagator/Propagator.hpp"
13 #include "Acts/Propagator/MaterialInteractor.hpp"
14 #include "Acts/Propagator/EigenStepper.hpp"
15 #include "Acts/Propagator/AtlasStepper.hpp"
16 #include "Acts/Utilities/VectorHelpers.hpp"
17 #include "Acts/Visualization/ObjVisualization3D.hpp"
18 #include "Acts/Visualization/GeometryView3D.hpp"
19 #include "Acts/EventData/ParticleHypothesis.hpp"
20 #include "Acts/Propagator/detail/SteppingLogger.hpp"
32 using namespace Acts::UnitLiterals;
36 using SegLink_t = std::vector<ElementLink<xAOD::MuonSegmentContainer>>;
39 struct PropagatorRecorder{
55 double atlasPropabsMomentum{0.};
57 double actsPropabsMomentum{0.};
59 double actsStepSize{0.};
79 IdentifierHash MuonDetectorNavTest::layerHash(
const Identifier&
id)
const {
80 return m_r4DetMgr->getReadoutElement(
id)->layerHash(
id);
86 ATH_CHECK(m_fieldCacheCondObjInputKey.initialize());
87 ATH_CHECK(m_truthParticleKey.initialize());
88 ATH_CHECK(m_truthSegLinkKey.initialize());
92 ATH_CHECK(m_truthSegLinkKey.initialize());
97 return StatusCode::SUCCESS;
103 return StatusCode::SUCCESS;
107 const EventContext& ctx{Gaudi::Hive::currentContext()};
108 ATH_MSG_DEBUG(
"Execute in event "<<ctx.eventID().event_number());
109 m_event = ctx.eventID().event_number();
113 if (!gctx.isValid() or !truthParticles.isValid() or !magFieldHandle.isValid()) {
114 ATH_MSG_FATAL(
"Failed to retrieve either the geometry context, truth particles or magnetic field");
115 return StatusCode::FAILURE;
120 const Acts::GeometryContext geoContext = gctx->context();
122 Acts::MagneticFieldContext mfContext = Acts::MagneticFieldContext(fieldCondObj);
125 using Stepper = Acts::EigenStepper<>;
127 using Navigator = Acts::Experimental::DetectorNavigator;
128 using Propagator = Acts::Propagator<Stepper,Navigator>;
129 using ActorList = Acts::ActorList<Acts::detail::SteppingLogger, Acts::MaterialInteractor, Acts::EndOfWorldReached>;
130 using PropagatorOptions = Propagator::Options<ActorList>;
133 PropagatorOptions
options(geoContext, mfContext);
135 options.pathLimit = m_pathLimit;
136 options.stepping.maxStepSize = m_maxStepSize;
137 options.stepping.stepTolerance = m_stepTolerance;
139 options.maxTargetSkipping = m_maxTargetSkipping;
142 auto& materialInteractor =
options.actorList.get<Acts::MaterialInteractor>();
143 materialInteractor.energyLoss =
false;
144 materialInteractor.multipleScattering =
false;
145 materialInteractor.recordInteractions =
false;
149 Acts::Experimental::DetectorNavigator::Config navCfg;
151 navCfg.resolvePassive =
true;
153 auto bField = std::make_shared<ATLASMagneticFieldWrapper>();
154 auto bfieldCache = bField->makeCache(mfContext);
155 auto stepper =
Stepper(bField);
158 Acts::Experimental::DetectorNavigator navigator(navCfg, Acts::getDefaultLogger(
"DetectorNavigator",
ActsTrk::actsLevelVector(msgLevel())));
159 Acts::Propagator<Stepper, Acts::Experimental::DetectorNavigator> propagator(
160 std::move(stepper), std::move(navigator),
166 ATH_MSG_DEBUG(
"Consider truth particle "<<truthParticle->pt()<<
", "<<truthParticle->p4().P()<<
", "<<truthParticle->eta()<<
", "<<truthParticle->phi()
167 <<
", pdgId: "<<truthParticle->pdgId()<<
", status: "<<truthParticle->status()
168 <<
", unique id:"<<truthParticle->id());
171 std::vector<PropagatorRecorder> propagatedHits;
172 std::vector<std::pair<Identifier, Amg::Vector3D>> truthHits;
177 Acts::AnyCharge(truthParticle->charge()));
180 std::vector<std::pair<const xAOD::MuonSegment*, std::vector<const xAOD::MuonSimHit*>>> muonSegmentWithSimHits;
181 for (
const auto& truthSegLink : segAcc(*truthParticle)){
184 std::vector<const xAOD::MuonSimHit*> muonSimHits{unordedHits.begin(), unordedHits.end()};
186 muonSegmentWithSimHits.emplace_back(seg,muonSimHits);
188 const auto& particle = truthParticle->p4();
189 m_truthPt = particle.Pt();
190 m_truthP = particle.P();
200 double startPropP{particle.P()};
203 Amg::Vector3D magFieldPos{startPropPos.x()+
r, startPropPos.y()+
r, startPropPos.z()};
214 if(m_startFromFirstHit){
215 if (muonSegmentWithSimHits.empty()) {
219 std::ranges::sort(muonSegmentWithSimHits,
220 [](
const auto&
a,
const auto&
b) {
221 return a.first->position().
perp() <
b.first->position().perp();
225 std::vector<const xAOD::MuonSimHit*>& muonSimHits = muonSegmentWithSimHits.front().second;
227 for(
const auto& simHit: muonSimHits){
228 ATH_MSG_VERBOSE(
"Sim Hit of first segment at global position: "<<
Amg::toString(toGlobalTrf(*gctx, simHit->identify())*xAOD::toEigen(simHit->localPosition())));
233 std::ranges::sort(muonSimHits,
237 return globalPos1.norm()<globalPos2.norm();
241 for(
const auto& simHit: muonSimHits){
242 ATH_MSG_VERBOSE(
"Sim Hit of first segment at position: "<<
Amg::toString(toGlobalTrf(*gctx, simHit->identify())*xAOD::toEigen(simHit->localPosition())));
247 startPropPos = toGlobalTrf(*gctx, muonSimHits.front()->identify())*xAOD::toEigen(muonSimHits.front()->localPosition());
248 startPropDir = toGlobalTrf(*gctx, muonSimHits.front()->identify()).linear()*xAOD::toEigen(muonSimHits.front()->localDirection());
249 startPropP = muonSimHits.front()->kineticEnergy();
250 ATH_MSG_VERBOSE(
"Kinetic Energy from the simHit: "<<muonSimHits.front()->kineticEnergy() /
Gaudi::Units::GeV<<
" and mass: "<<muonSimHits.front()->mass()<<
" and energy deposit: "<<muonSimHits.front()->energyDeposit() /
Gaudi::Units::eV);
254 <<
Amg::toString(startPropDir)<<
" and momentum "<<startPropP);
257 Acts::BoundTrackParameters
start = Acts::BoundTrackParameters::createCurvilinear(
258 Acts::VectorHelpers::makeVector4(startPropPos, 0.), startPropDir,
259 truthParticle->charge() / startPropP,
268 const Acts::detail::SteppingLogger::result_type state =
result.value().get<Acts::detail::SteppingLogger::result_type>();
269 const Acts::MaterialInteractor::result_type material =
result.value().get<Acts::MaterialInteractor::result_type>();
270 ATH_MSG_DEBUG(
"Material interactions: " << material.materialInteractions.size());
273 std::vector<Acts::MaterialInteraction> interactions = material.materialInteractions;
274 for(
const Acts::MaterialInteraction& interaction : interactions){
275 ATH_MSG_DEBUG(
"Path Correction: " << interaction.pathCorrection);
280 Acts::ObjVisualization3D
helper{};
281 for (
const auto&
s : state.steps) {
282 if(
s.surface) Acts::GeometryView3D::drawSurface(
helper, *
s.surface, geoContext);
284 helper.write(
std::format(
"event_{:}_{:}_Hits.obj", ctx.eventID().event_number(), truthParticle->index()));
287 ATH_MSG_VERBOSE(
"Number of propagated steps : " << state.steps.size());
288 m_propSteps = state.steps.size();
289 m_propLength =
result.value().pathLength;
290 for(
const auto& state: state.steps){
301 ATH_MSG_VERBOSE(
"Identify propagated hit " << m_idHelperSvc->toString(
ID) <<
" with hit at local position " <<
Amg::toString(toGap*state.position)<<
" and global direction "<<
Amg::toString(state.momentum.unit()));
303 PropagatorRecorder newRecord{};
305 newRecord.actsPropPos = toGap*state.position;
306 newRecord.actsGlobalPos = state.position;
307 newRecord.actsPropDir = toGap.linear()*state.momentum.unit();
308 newRecord.actsPropabsMomentum = state.momentum.norm();
309 newRecord.actsStepSize = state.stepSize.value();
315 if(truthParticle->isMuon()){
320 std::unique_ptr<Trk::TrackParameters> atlasPars{m_extrapolator->extrapolate(ctx, atlasStart,
326 newRecord.atlasPropPos = toGap* atlasPars->position();
327 newRecord.atlasPropDir = toGap.linear()*atlasPars->momentum().unit();
328 newRecord.atlasPropabsMomentum = atlasPars->momentum().mag();
329 newRecord.atlasGlobalPos = atlasPars->position();
331 propagatedHits.emplace_back(newRecord);
334 std::size_t propHitsSize = propagatedHits.size();
337 unsigned int nMatchedTruth = 0, nMatchedProp = 0, nTruth{0};
338 for (
const auto&[
segment, simHits] : muonSegmentWithSimHits) {
344 toGlobalTrf(*gctx,simHit->identify())};
345 const Amg::Vector3D localPos = localTrf*xAOD::toEigen(simHit->localPosition());
346 const Amg::Vector3D globalPos = toGlobalTrf(*gctx, simHit->identify())*xAOD::toEigen(simHit->localPosition());
347 const Amg::Vector3D localDir = localTrf.linear()*xAOD::toEigen(simHit->localDirection());
348 m_detId.push_back(
ID);
349 m_techIdx.push_back(m_idHelperSvc->technologyIndex(
ID));
350 m_gasGapId.push_back(layerHash(
ID));
352 m_truthLoc.push_back(localPos);
353 m_truthDir.push_back(localDir);
354 m_truthGlob.push_back(globalPos);
355 m_startGlob.push_back(startPropPos);
358 auto it_begin = std::ranges::find_if(propagatedHits,
359 [
this,
ID](
const auto& propagatedHit) {
360 return m_idHelperSvc->detElId(
ID) == m_idHelperSvc->detElId(propagatedHit.id) &&
361 layerHash(
ID) == layerHash(propagatedHit.id);
363 m_isPropagated.push_back(it_begin != propagatedHits.end());
365 if (it_begin == propagatedHits.end()) {
366 m_actsPropLoc.push_back(dummyPos);
367 m_actsPropDir.push_back(dummyPos);
368 m_atlasPropLoc.push_back(dummyPos);
369 m_atlasPropDir.push_back(dummyPos);
370 m_actsPropGlob.push_back(dummyPos);
371 m_atlasPropGlob.push_back(dummyPos);
372 m_actsPropMomentum.push_back(0.);
373 m_atlasPropMomentum.push_back(0.);
374 m_actsStepSize.push_back(0.);
377 auto it_end = std::find_if(it_begin, propagatedHits.end(),
378 [
this,
ID](
const auto& propagatedHit) {
379 return m_idHelperSvc->detElId(ID) != m_idHelperSvc->detElId(propagatedHit.id) ||
380 layerHash(ID) != layerHash(propagatedHit.id);
383 auto it = std::min_element(it_begin, it_end,
384 [&localPos](
const PropagatorRecorder&
a,
385 const PropagatorRecorder&
b){
386 return (localPos -
a.actsPropPos).mag() < (localPos -
b.actsPropPos).mag();
390 ATH_MSG_DEBUG(
"Truth hit found in propagated hits: " << m_idHelperSvc->toString(
ID));
391 m_actsPropLoc.push_back(
it->actsPropPos);
392 m_actsPropDir.push_back(
it->actsPropDir);
393 m_actsPropGlob.push_back(
it->actsGlobalPos);
394 m_atlasPropLoc.push_back(
it->atlasPropPos);
395 m_atlasPropDir.push_back(
it->atlasPropDir);
396 m_atlasPropGlob.push_back(
it->atlasGlobalPos);
397 m_actsPropMomentum.push_back(
it->actsPropabsMomentum);
398 m_atlasPropMomentum.push_back(
it->atlasPropabsMomentum);
399 m_actsStepSize.push_back(
it->actsStepSize);
403 m_matchedTruthFraction = 1.f*nMatchedTruth / nTruth;
404 m_matchedPropFraction = 1.f*nMatchedProp / propHitsSize;
410 return StatusCode::SUCCESS;