ATLAS Offline Software
Loading...
Searching...
No Matches
ActsGnnModuleMapFinderTool.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#ifdef ACTS_GNN_WITH_MODULEMAP
6
8
10
11#include "ActsPlugins/Gnn/CudaTrackBuilding.hpp"
12#include "ActsPlugins/Gnn/DWalkTrackBuilding.hpp"
13#include "ActsPlugins/Gnn/EdgeLayerConnector.hpp"
14#include "ActsPlugins/Gnn/GnnPipeline.hpp"
15#include "ActsPlugins/Gnn/ModuleMapCuda.hpp"
16#include "ActsPlugins/Gnn/OnnxEdgeClassifier.hpp"
17#include "ActsPlugins/Gnn/TensorRTEdgeClassifier.hpp"
18#include "ActsPlugins/Gnn/TorchEdgeClassifier.hpp"
19
23#include "ActsGnnHookTool.h"
24
25#include <algorithm>
26#include <numeric>
27
28
31
32 m_logger = makeActsAthenaLogger(this, "ActsGnnModuleMapFinderTool");
33
34 // Build ACTS GNN pipeline components
35
36 // 1. Graph constructor (ModuleMapCuda)
37 ActsPlugins::ModuleMapCuda::Config gcCfg;
38 gcCfg.rScale = kScaleR;
39 gcCfg.zScale = kScaleZ;
40 gcCfg.phiScale = kScalePhi;
41 gcCfg.moduleMapPath = m_moduleMapPath.value();
42 gcCfg.gpuBlocks = 512;
43 auto gc = std::make_shared<ActsPlugins::ModuleMapCuda>(
44 gcCfg, m_logger->cloneWithSuffix("ModuleMap"));
45
46 // 2. Edge classifier (ONNX / Torch / TensorRT)
47 // ONNX and Torch are not thread-safe: a mutex is emplaced to serialise run().
48 // TensorRT manages concurrency internally via execution contexts: no guard needed.
49 std::shared_ptr<ActsPlugins::EdgeClassificationBase> gnn;
50 if (m_gnnPath.value().find(".onnx") != std::string::npos) {
51#ifdef ACTS_GNN_ONNX_BACKEND
52 ActsPlugins::OnnxEdgeClassifier::Config gnnCfg;
53 gnnCfg.modelPath = m_gnnPath.value();
54 gnnCfg.cut = m_edgeCut.value();
55 gnn = std::make_shared<ActsPlugins::OnnxEdgeClassifier>(
56 gnnCfg, m_logger->cloneWithSuffix("GNN"));
57 m_runMutex.emplace();
58#else
59 ATH_MSG_FATAL("Not compiled with ONNX, cannot interpret *.onnx files");
60 return StatusCode::FAILURE;
61#endif
62 } else if (m_gnnPath.value().find(".pt") != std::string::npos) {
63#ifdef ACTS_GNN_TORCH_BACKEND
64 ActsPlugins::TorchEdgeClassifier::Config gnnCfg;
65 gnnCfg.modelPath = m_gnnPath.value();
66 gnnCfg.cut = m_edgeCut.value();
67 gnn = std::make_shared<ActsPlugins::TorchEdgeClassifier>(
68 gnnCfg, m_logger->cloneWithSuffix("GNN"));
69 m_runMutex.emplace();
70#else
71 ATH_MSG_FATAL("Not compiled with Torch, cannot interpret *.pt files");
72 return StatusCode::FAILURE;
73#endif
74 } else if (m_gnnPath.value().find(".engine") != std::string::npos) {
75#ifdef ACTS_GNN_WITH_TENSORRT
76 ActsPlugins::TensorRTEdgeClassifier::Config gnnCfg;
77 gnnCfg.modelPath = m_gnnPath.value();
78 gnnCfg.cut = m_edgeCut.value();
79 gnnCfg.numExecutionContexts = m_numTrtContexts.value();
80 gnn = std::make_shared<ActsPlugins::TensorRTEdgeClassifier>(
81 gnnCfg, m_logger->cloneWithSuffix("GNN"));
82#else
83 ATH_MSG_FATAL("Not compiled with TensorRT, cannot interpret *.engine files");
84 return StatusCode::FAILURE;
85#endif
86 } else {
87 ATH_MSG_FATAL("Unknown extension for GNN model: " << m_gnnPath.value());
88 return StatusCode::FAILURE;
89 }
90
91 // 3. Track builder
92 std::shared_ptr<ActsPlugins::TrackBuildingBase> tb;
93 if (m_trackBuilder == "junctionremoval") {
94 ATH_MSG_INFO("Configure CC&JunctionRemoval as graph segmentation algorithm");
95 ActsPlugins::CudaTrackBuilding::Config tbCfg;
96 tbCfg.doJunctionRemoval = true;
97 tb = std::make_shared<ActsPlugins::CudaTrackBuilding>(
98 tbCfg, m_logger->cloneWithSuffix("CC&JR"));
99 } else if (m_trackBuilder == "edgelayerconnector") {
100 ATH_MSG_INFO("Configure EdgeLayerConnector as graph segmentation algorithm");
101 ActsPlugins::EdgeLayerConnector::Config tbCfg;
102 tbCfg.maxHitsPerTrack = m_elcMaxHitsPerTrack;
103 tbCfg.blockSize = 512;
104 tbCfg.weightsCut = m_edgeCut;
105 tb = std::make_shared<ActsPlugins::EdgeLayerConnector>(
106 tbCfg, m_logger->cloneWithSuffix("ELC"));
107 } else if (m_trackBuilder == "dwalk") {
108 ATH_MSG_INFO("Configure D-WALK as graph segmentation algorithm");
109 ActsPlugins::DWalkTrackBuilding::Config tbCfg;
110 tbCfg.minCandidateSize = m_minCandidateMeasurements;
111 tb = std::make_shared<ActsPlugins::DWalkTrackBuilding>(
112 tbCfg, m_logger->cloneWithSuffix("D-WALK"));
113 } else {
114 ATH_MSG_ERROR("Unknown graph segmentation algorithm: " << m_trackBuilder);
115 return StatusCode::FAILURE;
116 }
117
118 // 4. Assemble pipeline
119 m_gnnPipeline = std::make_unique<ActsPlugins::GnnPipeline>(
120 gc, std::vector{std::move(gnn)}, tb, m_logger->cloneWithSuffix("Pipeline"));
121
122 return StatusCode::SUCCESS;
123}
124
126 const std::vector<const Trk::SpacePoint*>& spacepoints,
127 std::vector<std::vector<uint32_t>>& tracks,
128 std::unordered_map<int, std::unordered_map<int, float>>* edgeMap) const {
129
130 const std::size_t nSP = spacepoints.size();
131
132 ATH_MSG_DEBUG("Processing " << nSP << " spacepoints with " << NUM_FEATURES << " features");
133
134 // Sort spacepoint indices by module ID (required by module map graph construction)
135 std::vector<std::size_t> sortIdx(nSP);
136 std::iota(sortIdx.begin(), sortIdx.end(), 0);
137 std::ranges::sort(sortIdx, std::less{}, [&](std::size_t i) {
138 return spacepoints[i]->clusterList().first->detectorElement()->identify().get_compact();
139 });
140
141 // Build features, module IDs, and IDs directly in sorted order
142 std::vector<float> features(NUM_FEATURES * nSP);
143 std::vector<std::uint64_t> moduleIds(nSP);
144 std::vector<int> ids(nSP);
145
146 for (std::size_t k = 0; k < nSP; ++k) {
147 const std::size_t origIdx = sortIdx[k];
148 auto featureMap = m_spacepointFeatureTool->getFeatures(spacepoints[origIdx]);
149 // Use detector element identifier, not cluster identifier, to get the module ID
150 moduleIds[k] = spacepoints[origIdx]->clusterList().first->detectorElement()->identify().get_compact();
151 ids[k] = static_cast<int>(k);
152 for (std::size_t j = 0; j < NUM_FEATURES; ++j) {
153 features[k * NUM_FEATURES + j] = featureMap[FEATURE_NAMES[j]] / FEATURE_SCALES[j];
154 }
155 }
156
157 // Run GNN pipeline (mutex present for ONNX/Torch, absent for TRT)
158 auto candidates = [&] {
159 std::unique_lock<std::mutex> lock;
160 if (m_runMutex) lock = std::unique_lock<std::mutex>(*m_runMutex);
161
162 if (edgeMap != nullptr) {
163 ScoredGraphHook hook;
164 auto result = m_gnnPipeline->run(features, moduleIds, ids, ActsPlugins::Device::Cuda(0), hook);
165
166 // Retrieve edgeScores and edgeIndex from hook
167 const std::vector<float>& edgeScores = hook.getEdgeScores();
168 const std::vector<std::int64_t>& edgeIndex = hook.getEdgeIndex();
169 const std::size_t nEdges = edgeScores.size();
170
171 // Create a map to acces edge score (sorted indices back to original spacepoint indices)
172 for (std::size_t i = 0; i < nEdges; ++i) {
173 std::int64_t src = edgeIndex[i];
174 std::int64_t dst = edgeIndex[nEdges + i];
175 (*edgeMap)[sortIdx[src]][sortIdx[dst]] = edgeScores[i];
176 }
177 return result;
178 }
179
180 return m_gnnPipeline->run(features, moduleIds, ids, ActsPlugins::Device::Cuda(0));;
181 }();
182
183 ATH_MSG_DEBUG("GNN pipeline returned " << candidates.size() << " candidates");
184
185 // Filter by minimum measurements and convert indices back to original ordering
186 tracks.clear();
187 tracks.reserve(candidates.size());
188
189 for (const auto& candidate : candidates) {
190 if (candidate.size() < m_minCandidateMeasurements.value()) {
191 continue;
192 }
193
194 // Map sorted indices back to original spacepoint indices
195 std::vector<uint32_t> track;
196 track.reserve(candidate.size());
197 for (int sortedIdx : candidate) {
198 track.push_back(static_cast<uint32_t>(sortIdx[sortedIdx]));
199 }
200 tracks.push_back(std::move(track));
201 }
202
203 ATH_MSG_DEBUG("Returning " << tracks.size() << " track candidates after filtering (>= "
204 << m_minCandidateMeasurements.value() << " measurements)");
205
206 return StatusCode::SUCCESS;
207}
208
209MsgStream& InDet::ActsGnnModuleMapFinderTool::dump(MsgStream& out) const {
210 out << "\n";
211 out << "|---------------------------------------------------------------------|\n" ;
212 out << "| ActsGnnModuleMapFinderTool |\n" ;
213 out << "|---------------------------------------------------------------------|\n" ;
214 return out;
215}
216
217std::ostream& InDet::ActsGnnModuleMapFinderTool::dump(std::ostream& out) const {
218 return out;
219}
220
221#endif // ACTS_GNN_WITH_MODULEMAP
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_DEBUG(x,...)
#define ATH_MSG_ERROR(x,...)
#define ATH_MSG_INFO(x,...)
#define ATH_MSG_FATAL(x,...)
virtual void lock()=0
Interface to allow an object to lock itself when made const in SG.
std::unique_ptr< const Acts::Logger > makeActsAthenaLogger(IMessageSvc *svc, const std::string &name, int level, std::optional< std::string > parent_name)
Definition Logger.cxx:64
virtual StatusCode initialize() override
virtual MsgStream & dump(MsgStream &out) const override
static constexpr std::array< float, NUM_FEATURES > FEATURE_SCALES
static constexpr std::array< const char *, NUM_FEATURES > FEATURE_NAMES
std::unique_ptr< const Acts::Logger > m_logger
ToolHandle< ISpacepointFeatureTool > m_spacepointFeatureTool
static constexpr std::size_t NUM_FEATURES
virtual StatusCode getTracks(const std::vector< const Trk::SpacePoint * > &spacepoints, std::vector< std::vector< uint32_t > > &tracks, std::unordered_map< int, std::unordered_map< int, float > > *edgeMap=nullptr) const override
std::unique_ptr< ActsPlugins::GnnPipeline > m_gnnPipeline
::StatusCode StatusCode
StatusCode definition for legacy code.
float j(const xAOD::IParticle &, const xAOD::TrackMeasurementValidation &hit, const Eigen::Matrix3d &jab_inv)
setEventNumber uint32_t