ATLAS Offline Software
Loading...
Searching...
No Matches
FPGATrackSimGNNRoadMakerTool.cxx
Go to the documentation of this file.
1
2// Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
3
6
8// AthAlgTool
9
10FPGATrackSimGNNRoadMakerTool::FPGATrackSimGNNRoadMakerTool(const std::string& algname, const std::string &name, const IInterface *ifc)
11 : AthAlgTool(algname, name, ifc) {}
12
14{
16 m_nLayers = m_FPGATrackSimMapping->PlaneMap_1st(0)->getNLogiLayers();
17 ATH_CHECK(m_layerNumberTool.retrieve());
18 m_pix_h2l = m_layerNumberTool->pixelLayers();
19 m_layerGeometry = m_layerNumberTool->layerGeometry();
20
21 return StatusCode::SUCCESS;
22}
23
25// Functions
26
27StatusCode FPGATrackSimGNNRoadMakerTool::makeRoads(const std::vector<std::shared_ptr<const FPGATrackSimHit>> & hits, const std::vector<std::shared_ptr<FPGATrackSimGNNHit>> & gnn_hits, const std::vector<std::shared_ptr<FPGATrackSimGNNEdge>> & edges, std::vector<std::shared_ptr<const FPGATrackSimRoad>> & roads)
28{
29 m_num_nodes = gnn_hits.size();
30 doScoreCut(edges);
31
32 if(m_roadMakerTool == "ConnectedComponents") {
34 addRoads(hits, gnn_hits, roads);
35 } else if (m_roadMakerTool == "JunctionAwareCC"){
37 addRoads(hits, gnn_hits, roads);
38 }
39
41
42 return StatusCode::SUCCESS;
43}
44
45void FPGATrackSimGNNRoadMakerTool::doScoreCut(const std::vector<std::shared_ptr<FPGATrackSimGNNEdge>> & edges)
46{
47 for (const auto& edge : edges) {
48 if(edge->getEdgeScore() > m_edgeScoreCut) {
49 m_pass_edge_index_1.push_back(edge->getEdgeIndex1());
50 m_pass_edge_index_2.push_back(edge->getEdgeIndex2());
51 }
52 }
53}
54
56{
58 boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS> g(m_unique_nodes.size());
59
60 for (size_t i = 0; i < m_pass_edge_index_1.size(); i++) {
63 add_edge(u, v, g); // Add the edge between the mapped node indices
64 }
65
66 std::vector<int> components(num_vertices(g), -1);
67 m_component.resize(num_vertices(g), std::vector<int>(1,-1));
68 m_num_components = boost::connected_components(g, &components[0]);
69
70 for (size_t i = 0; i != components.size(); ++i){
71 m_component[i][0] = components[i];
72 }
73
74 m_labels.resize(m_num_nodes, std::vector<int>(1,-1));
75
76 for (size_t i = 0; i < m_unique_indices.size(); i++) {
78 }
79
80}
81
83{
85 boost::adjacency_list<boost::vecS, boost::vecS, boost::bidirectionalS> g(m_unique_nodes.size());
86
87 for (size_t i = 0; i < m_pass_edge_index_1.size(); i++) {
90 add_edge(u, v, g); // Add the edge between the mapped node indices
91 }
92 // Predecessor map, allowing to keep track of the previous hits in a component
93 std::unordered_map<Vertex, std::vector<Vertex>> pred_map;
94 // Visited vertices map
95 std::vector<bool> visited(num_vertices(g), false);
96
97 m_control_var.resize(num_vertices(g), -1);
98 m_component.resize(num_vertices(g), std::vector<int>(1, -1));
99 int comp_id = 0;
100
101 for (Vertex v = 0; v < num_vertices(g); ++v){
102 if(!visited[v] && boost::in_degree(v,g) == 0){
103
104 // Create a color map to store state of the nodes during the BFS
105 std::vector<boost::default_color_type> color_storage(num_vertices(g));
106 auto color_map = boost::make_iterator_property_map(color_storage.begin(), get(boost::vertex_index,g));
107
108 JunctionAwareVisitor JA_vis(comp_id, m_control_var, m_component, pred_map, color_map);
109 breadth_first_search(g, v, visitor(JA_vis).color_map(color_map));
110 // Mark visited vertices and reset control variables
111 for (unsigned long u = 0; u != num_vertices(g); ++u){
112 if (m_control_var[u] != -1){
113 visited[u] = true;
114 m_control_var[u] = -1;
115 }
116 }
117 // Clear predecessor map
118 pred_map.clear();
119 ++comp_id;
120 }
121 }
122 m_num_components = comp_id;
123
124 m_labels.resize(m_num_nodes, std::vector<int>(1,-1));
125
126 for (size_t i = 0; i < m_unique_indices.size(); i++) {
128 }
129}
130
131void FPGATrackSimGNNRoadMakerTool::addRoads(const std::vector<std::shared_ptr<const FPGATrackSimHit>> & hits,
132 const std::vector<std::shared_ptr<FPGATrackSimGNNHit>> & gnn_hits,
133 std::vector<std::shared_ptr<const FPGATrackSimRoad>> & roads)
134{
135 roads.clear();
136 m_roads.clear();
137
139 for (size_t i = 0; i < m_labels.size(); i++) {
140 for (auto& current_label : m_labels[i]){
141 if(current_label != -1) {
142 m_road_hit_list[current_label].push_back(gnn_hits[i]->getHitID());
143 }
144 }
145 }
146 for (const auto& hit_list : m_road_hit_list) {
147 if(m_doGNNPixelSeeding) { addRoadForPixelSeed(hits, hit_list); }
148 else { addRoad(hits, hit_list); }
149 }
150
151 roads.reserve(m_roads.size());
152 for (const FPGATrackSimRoad & r : m_roads) roads.emplace_back(std::make_shared<const FPGATrackSimRoad>(r));
153}
154
155void FPGATrackSimGNNRoadMakerTool::addRoad(const std::vector<std::shared_ptr<const FPGATrackSimHit>> & hits, const std::vector<int>& road_hitIDs)
156{
157 // Take the HitID values and find the correct hit from FPGATrackSimHit, then insert it into a vector of mapped FPGATrackSimHit which are needed for the FPGATrackSimRoad
158 std::vector<std::shared_ptr<const FPGATrackSimHit>> mapped_road_hits;
159 const FPGATrackSimPlaneMap *pmap = m_FPGATrackSimMapping->PlaneMap_1st(0);
160 layer_bitmask_t hitLayers = 0;
161
162 for (const auto& hitID : road_hitIDs) {
163 if(hitID+1 < static_cast<int>(hits.size()) && hits[hitID]->isStrip() && hits[hitID+1]->isStrip() &&
164 to_string(hits[hitID]->getHitType()) == "spacepoint" && to_string(hits[hitID+1]->getHitType()) == "spacepoint" &&
165 hits[hitID]->getX() == hits[hitID+1]->getX()) {
166 auto &hit1 = hits[hitID];
167 std::shared_ptr<FPGATrackSimHit> hitCopy1 = std::make_shared<FPGATrackSimHit>(*hit1);
168 pmap->map(*hitCopy1);
169 hitLayers |= 1 << hitCopy1->getLayer();
170 mapped_road_hits.push_back(std::move(hitCopy1));
171 //
172 auto &hit2 = hits[hitID+1];
173 std::shared_ptr<FPGATrackSimHit> hitCopy2 = std::make_shared<FPGATrackSimHit>(*hit2);
174 pmap->map(*hitCopy2);
175 hitLayers |= 1 << hitCopy2->getLayer();
176 mapped_road_hits.push_back(std::move(hitCopy2));
177 }
178 else {
179 auto &hit = hits[hitID];
180 std::shared_ptr<FPGATrackSimHit> hitCopy = std::make_shared<FPGATrackSimHit>(*hit);
181 pmap->map(*hitCopy);
182 hitLayers |= 1 << hitCopy->getLayer();
183 mapped_road_hits.push_back(std::move(hitCopy));
184 }
185 }
186 m_roads.emplace_back();
187 FPGATrackSimRoad & r = m_roads.back();
188 auto sorted_hits = ::sortByLayer(mapped_road_hits);
189 sorted_hits.resize(m_nLayers);
190 r.setRoadID(m_roads.size() - 1);
191 r.setHitLayers(hitLayers);
192 r.setHits(std::vector<std::vector<std::shared_ptr<const FPGATrackSimHit>>>(std::move(sorted_hits)));
193 r.setSubRegion(0);
194}
195
196void FPGATrackSimGNNRoadMakerTool::addRoadForPixelSeed(const std::vector<std::shared_ptr<const FPGATrackSimHit>> & hits, const std::vector<int>& road_hitIDs)
197{
198 std::vector<std::shared_ptr<const FPGATrackSimHit>> all_hits;
199 std::vector<std::shared_ptr<const FPGATrackSimHit>> track_hit_list;
200
201 for (const auto& hitID : road_hitIDs) {
202 auto &hit = hits[hitID]; //Hit object inside the road candidate
203 all_hits.push_back(hit);
204 }
205
206 // Sort hits in CC list by rho
207 std::sort(all_hits.begin(), all_hits.end(),
208 [](const std::shared_ptr<const FPGATrackSimHit>& hit1,
209 const std::shared_ptr<const FPGATrackSimHit>& hit2) {
210 double rho1 = std::hypot(hit1->getX(), hit1->getY());
211 double rho2 = std::hypot(hit2->getX(), hit2->getY());
212 return rho1 < rho2;
213 });
214
215 // From all the hits in the CC list, only accept one per global layer ID, defined by the layer configuration from GBTS
216 std::unordered_set<int> seenLayers;
217 for (const auto &hit : all_hits) {
218 short layer = m_pix_h2l->at(static_cast<int>(hit->getIdentifierHash()));
219 TrigInDetSiLayer layerGeometry = m_layerGeometry->at(layer);
220 int combinedId = layerGeometry.m_subdet;
221
222 if (seenLayers.count(combinedId) == 0) { // unique layer
223 track_hit_list.push_back(hit);
224 seenLayers.insert(combinedId);
225 }
226 }
227
228 std::size_t nSp = track_hit_list.size();
229 std::vector<std::vector<std::shared_ptr<const FPGATrackSimHit>>> seed_hit_list;
230
231 // Use a method to choose which spacepoints to keep for the road, which becomes a seed
232 // Need at least 3 spacepoints to form a seed, so only consider 3 spacepoint lists of hits-per-layer
233 // For >=4 spacepoints, consider a evenly-spaced method instead of first N spacepoints
234 // So the road candidate can have anywhere from 3-5 spacepoints
235 auto spacePointIndicesFun = [](std::size_t nSp, std::size_t nSeeds) -> std::vector<std::size_t> {
236 std::vector<std::size_t> idx;
237
238 for (std::size_t i = 0; i < nSeeds; ++i) {
239 std::size_t pos = (i * (nSp - 1)) / (nSeeds - 1); // evenly spaced
240 if (idx.empty() || idx.back() != pos) { // avoid duplicates
241 idx.push_back(pos);
242 }
243 }
244 return idx;
245 };
246
247 // Only keep road candidates with at >= 3 spacepoints in unique layers
248 if (nSp >= 3) {
249 std::size_t nSeeds = std::min<std::size_t>(5, nSp);
250 auto indices = spacePointIndicesFun(nSp, nSeeds);
251
252 for (auto idx : indices) {
253 seed_hit_list.push_back({track_hit_list[idx]});
254 }
255
256 m_roads.emplace_back();
257 FPGATrackSimRoad & r = m_roads.back();
258 r.setHits(std::move(seed_hit_list));
259 r.setRoadID(m_roads.size() - 1);
260 }
261}
262
264{
265 m_pass_edge_index_1.clear();
266 m_pass_edge_index_2.clear();
267 m_unique_nodes.clear();
268 m_node_index_map.clear();
269 m_unique_indices.clear();
270 m_component.clear();
271 m_labels.clear();
272 m_road_hit_list.clear();
273}
274
276{
277 // Remove isolated nodes from list of nodes using list of edges and indices
280
281 int index = 0;
282 for (int node : m_unique_nodes) {
283 m_node_index_map[node] = index++; // Mapping original node index to new graph index
284 }
285
286 for (const auto& entry : m_node_index_map) {
287 m_unique_indices.push_back(entry.first); // Push the original node index into unique_indices
288 }
289}
290
291JunctionAwareVisitor::JunctionAwareVisitor(int& in_current, std::vector<int>& in_control_vars, std::vector<std::vector<int>>& in_comps,
292 std::unordered_map<Vertex, std::vector<Vertex>>& in_pred_map, ColorMap in_cmap) :
293 m_current_comp(in_current), m_control_vars(in_control_vars), m_components(in_comps),
294 m_pred_map(in_pred_map), m_initial_comp(in_current), m_color_map(in_cmap) {}
295
296template <typename VertexT, typename GraphT>
297void JunctionAwareVisitor::discover_vertex(VertexT v, const GraphT& g)
298{
299 if (m_control_vars[v] == -1){
300 m_control_vars[v] = boost:: out_degree(v,g) < 2 ? 1 : -2; // -2 labels junctions before they are transversed
301 m_components[v].push_back(m_current_comp);
302 }
303}
304
305template <typename EdgeT, typename GraphT>
306void JunctionAwareVisitor::examine_edge(EdgeT e, const GraphT& g)
307{
308 auto src_node = source(e,g);
309 auto tar_node = target(e,g);
310 m_pred_map[tar_node].push_back(src_node);
311 process_edges(src_node, tar_node, g);
312
313 // If target node was already visited, propagate the component also to the nodes it is connected to
314 if ((get(m_color_map, tar_node) == boost::black_color) && (boost::out_degree(tar_node, g) > 0)){
315 m_control_vars[tar_node] = boost:: out_degree(tar_node,g) < 2 ? 1 : -2;
316 auto out_edges = boost::out_edges(tar_node, g);
317 for (auto it = out_edges.first; it != out_edges.second; ++ it){
318 Vertex next_dst = target(*it, g);
319 process_edges(tar_node, next_dst, g);
320 }
321 }
322}
323
324template <typename VertexT, typename GraphT>
325void JunctionAwareVisitor::process_edges(VertexT src_node, VertexT tar_node, const GraphT& g){
326 std::vector<int> src_comp; // Store the components of the source that need to be tracked
327
328 for(int comp : m_components[src_node]){
329 if(comp >= m_initial_comp &&
330 std::find(m_components[tar_node].begin(), m_components[tar_node].end(), comp) == m_components[tar_node].end()){
331 src_comp.push_back(comp); // Components of source node not in target
332 }
333 }
334
335 // No junction case
336 if (m_control_vars[src_node] == 1){
337 m_components[tar_node].insert(m_components[tar_node].end(), src_comp.begin(), src_comp.end());
338 m_control_vars[tar_node] = boost::out_degree(tar_node, g) < 2 ? 1 : -2;
339 }
340
341 // First time visiting a junction
342 else if (m_control_vars[src_node] == -2){
343 m_components[tar_node].insert(m_components[tar_node].end(), src_comp.begin(), src_comp.end());
344 m_control_vars[tar_node] = boost::out_degree(tar_node, g) < 2 ? 1 : -2;
345 m_control_vars[src_node] = 2; // 2 labels a junction after the first visit to it
346 m_n_iter = src_comp.size(); // Number of indices to add to each component going out of this junction
347 }
348
349 // Going through a junction already visited
350 else if (m_control_vars[src_node] == 2){
351 for(int i = 0; i != m_n_iter; ++i){
353 m_components[tar_node].push_back(m_current_comp);
354 m_control_vars[tar_node] = boost::out_degree(tar_node, g) < 2 ? 1 : -2;
355 // Backtrace the node's predecessors to propagate the new label
356 std::unordered_set<Vertex> visited;
357 std::function<void(Vertex)> backtrace = [&](Vertex node){
358 if(visited.count(node)) return;
359 visited.insert(node);
360 const auto& node_comps = m_components[node];
361 if((std::find(node_comps.begin(), node_comps.end(), m_current_comp) == node_comps.end()) &&
362 (src_comp.empty() || std::find(node_comps.begin(), node_comps.end(), src_comp[i]) != node_comps.end())){ // Backpropagate with junction awareness
363 m_components[node].push_back(m_current_comp);
364 }
365 for (const auto& pred : m_pred_map[node]){
366 backtrace(pred);
367 }
368 };
369 backtrace(tar_node);
370 }
371 }
372}
#define ATH_CHECK
Evaluate an expression and check for errors.
Implements algorithm to construct a road from a list of hits using edge scores.
boost::graph_traits< boost::adjacency_list< boost::vecS, boost::vecS, boost::bidirectionalS > >::vertex_descriptor Vertex
boost::iterator_property_map< std::vector< boost::default_color_type >::iterator, boost::property_map< boost::adjacency_list< boost::vecS, boost::vecS, boost::bidirectionalS >, boost::vertex_index_t >::type > ColorMap
static std::string to_string(const std::vector< T > &v)
std::vector< std::vector< std::shared_ptr< const FPGATrackSimHit > > > sortByLayer(Container const &hits)
Maps physical layers to logical layers.
uint32_t layer_bitmask_t
AthAlgTool(const std::string &type, const std::string &name, const IInterface *parent)
Constructor with parameters:
void doScoreCut(const std::vector< std::shared_ptr< FPGATrackSimGNNEdge > > &edges)
void addRoads(const std::vector< std::shared_ptr< const FPGATrackSimHit > > &hits, const std::vector< std::shared_ptr< FPGATrackSimGNNHit > > &gnn_hits, std::vector< std::shared_ptr< const FPGATrackSimRoad > > &roads)
virtual StatusCode makeRoads(const std::vector< std::shared_ptr< const FPGATrackSimHit > > &hits, const std::vector< std::shared_ptr< FPGATrackSimGNNHit > > &gnn_hits, const std::vector< std::shared_ptr< FPGATrackSimGNNEdge > > &edges, std::vector< std::shared_ptr< const FPGATrackSimRoad > > &roads)
const std::vector< short > * m_pix_h2l
std::vector< FPGATrackSimRoad > m_roads
const std::vector< TrigInDetSiLayer > * m_layerGeometry
void addRoadForPixelSeed(const std::vector< std::shared_ptr< const FPGATrackSimHit > > &hits, const std::vector< int > &road_hitIDs)
ServiceHandle< IFPGATrackSimMappingSvc > m_FPGATrackSimMapping
void addRoad(const std::vector< std::shared_ptr< const FPGATrackSimHit > > &hits, const std::vector< int > &road_hitIDs)
FPGATrackSimGNNRoadMakerTool(const std::string &, const std::string &, const IInterface *)
std::vector< std::vector< int > > m_component
Gaudi::Property< std::string > m_roadMakerTool
std::vector< std::vector< int > > m_labels
ToolHandle< ITrigL2LayerNumberTool > m_layerNumberTool
std::vector< std::vector< int > > m_road_hit_list
virtual StatusCode initialize() override
void map(FPGATrackSimHit &hit) const
std::unordered_map< Vertex, std::vector< Vertex > > & m_pred_map
void discover_vertex(VertexT v, const GraphT &g)
void examine_edge(EdgeT e, const GraphT &g)
std::vector< std::vector< int > > & m_components
void process_edges(VertexT src, VertexT tar, const GraphT &g)
JunctionAwareVisitor(int &current, std::vector< int > &in_control_vars, std::vector< std::vector< int > > &in_comps, std::unordered_map< Vertex, std::vector< Vertex > > &in_pred_map, ColorMap in_cmap)
Definition node.h:24
int r
Definition globals.cxx:22
T * get(TKey *tobj)
get a TObject* from a TKey* (why can't a TObject be a TKey?)
Definition hcg.cxx:130
Definition index.py:1
void sort(typename DataModel_detail::iterator< DVL > beg, typename DataModel_detail::iterator< DVL > end)
Specialization of sort for DataVector/List.