ATLAS Offline Software
Loading...
Searching...
No Matches
FPGATrackSimNNPathfinderExtensionTool.cxx
Go to the documentation of this file.
1// Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
2
3
14
15#include "AthenaKernel/Chrono.h"
16
17#include <cmath>
18#include <algorithm>
19#include <deque>
20#include <unordered_map>
21#include "CLHEP/Units/SystemOfUnits.h"
22
23using CLHEP::pi;
24
25namespace {
26 // Helper to wrap a raw pointer as a non-owning shared_ptr<const FPGATrackSimHit>
27 inline std::shared_ptr<const FPGATrackSimHit> makeNonOwningHitPtr(const FPGATrackSimHit* hit) {
28 return std::shared_ptr<const FPGATrackSimHit>(hit, [](const FPGATrackSimHit*){});
29 }
30
31 // Helper to create spatial hash key from coordinates for strip matching
32 inline long makeCoordinatesKey(float x, float y, float z, float gridSize = 1.0f) {
33 int ix = static_cast<int>(std::floor(x / gridSize));
34 int iy = static_cast<int>(std::floor(y / gridSize));
35 int iz = static_cast<int>(std::floor(z / gridSize));
36 return (static_cast<long>(ix + 10000) << 40) |
37 (static_cast<long>(iy + 10000) << 20) |
38 static_cast<long>(iz + 10000);
39 }
40
41 // Comparator for Cartesian sorting
42 template<typename HitVec>
43 bool cartesianComparator(const HitVec& a, const HitVec& b, double predr, double predz) {
44 const auto& hitA = *(a[0]);
45 const auto& hitB = *(b[0]);
46 double hitz_a = hitA.getZ();
47 double hitr_a = hitA.getR();
48 double hitz_b = hitB.getZ();
49 double hitr_b = hitB.getR();
50 float distance_a = (hitr_a - predr)*(hitr_a - predr) + (hitz_a - predz)*(hitz_a - predz);
51 float distance_b = (hitr_b - predr)*(hitr_b - predr) + (hitz_b - predz)*(hitz_b - predz);
52 return distance_a < distance_b;
53 }
54
55 // Comparator for polar sorting
56 template<typename HitVec>
57 bool polarComparator(const HitVec& a, const HitVec& b, double predr, double predphi, double predz, double zScale2, double phiScale2, double rScale2) {
58 const auto& hitA = *(a[0]);
59 const auto& hitB = *(b[0]);
60
61 double hitr_a = hitA.getR();
62 double hitphi_a = hitA.getGPhi();
63 double hitz_a = hitA.getZ();
64 double dz_a = abs(hitz_a - predz);
65 double dr_a = abs(hitr_a - predr);
66 double dphi_a = abs(hitphi_a - predphi);
67 while (dphi_a > pi) dphi_a -= pi;
68 // scaled distance because z and phi and r are not in same units
69 float distance_a = dz_a*dz_a/zScale2 + dphi_a*dphi_a/phiScale2 + dr_a*dr_a/rScale2;
70
71 double hitr_b = hitB.getR();
72 double hitphi_b = hitB.getGPhi();
73 double hitz_b = hitB.getZ();
74 double dz_b = abs(hitz_b - predz);
75 double dr_b = abs(hitr_b - predr);
76 double dphi_b = abs(hitphi_b - predphi);
77 while (dphi_b > pi) dphi_b -= pi;
78 // scaled distance because z and phi and r are not in same units
79 float distance_b = dz_b*dz_b/zScale2 + dphi_b*dphi_b/phiScale2 + dr_b*dr_b/rScale2;
80
81 return distance_a < distance_b;
82 }
83
84 // Spatial index for fast hit lookup
85 struct HitSpatialIndex {
86 std::unordered_map<long, std::vector<const FPGATrackSimHit*>> fineIDToHits;
87 std::unordered_map<long, std::vector<const FPGATrackSimHit*>> coordToHits;
88
89 void build(const std::vector<std::shared_ptr<const FPGATrackSimHit>>& hits) {
90 fineIDToHits.clear();
91 coordToHits.clear();
92 fineIDToHits.reserve(50000);
93 coordToHits.reserve(hits.size());
94
95 for (const auto& hitPtr : hits) {
96 if (!hitPtr->isReal()) continue;
97
98 // For fineID index we can just skip second half of strip SPs since they get added via the first half
99 if (!(hitPtr->getHitType() == HitType::spacepoint && (hitPtr->getPhysLayer(true) % 2 == 1))) {
100 long fineID = getFineID(*hitPtr);
101 fineIDToHits[fineID].push_back(hitPtr.get());
102 }
103
104 // For coordinate index we include all hits since we need to find strip SP pairs
105 long coordKey = makeCoordinatesKey(hitPtr->getX(), hitPtr->getY(), hitPtr->getZ());
106 coordToHits[coordKey].push_back(hitPtr.get());
107 }
108 }
109
110 const std::vector<const FPGATrackSimHit*>* getHits(long fineID) const {
111 auto it = fineIDToHits.find(fineID);
112 return (it != fineIDToHits.end()) ? &(it->second) : nullptr;
113 }
114
115 const std::vector<const FPGATrackSimHit*>* getHitsByCoord(float x, float y, float z) const {
116 long coordKey = makeCoordinatesKey(x, y, z);
117 auto it = coordToHits.find(coordKey);
118 return (it != coordToHits.end()) ? &(it->second) : nullptr;
119 }
120 };
121}
122
124
125 // Retrieve the mapping service.
127
128 // hard code this for now but we may chance in the future
131
132 if (m_windowR.size() != 1 && m_windowR.size() != m_windowFineID.size()) {
133 ATH_MSG_ERROR("Window r size = " << m_windowR << " is not equal to 1 (for all layers) and not equal to " << m_windowFineID.size());
134 return StatusCode::FAILURE;
135 }
136
137 if (m_windowPhi.size() != 1 && m_windowPhi.size() != m_windowFineID.size()) {
138 ATH_MSG_ERROR("Window phi size = " << m_windowPhi << " is not equal to 1 (for all layers) and not equal to " << m_windowFineID.size());
139 return StatusCode::FAILURE;
140 }
141
142 if (m_windowZ.size() != 1 && m_windowZ.size() != m_windowFineID.size()) {
143 ATH_MSG_ERROR("Window z size = " << m_windowZ << " is not equal to 1 (for all layers) and not equal to " << m_windowFineID.size());
144 return StatusCode::FAILURE;
145 }
146
147 if (m_FPGATrackSimMapping->getExtensionNNVolMapString() != "" && m_FPGATrackSimMapping->getExtensionNNHitMapString() != "") {
148 ATH_MSG_INFO("Initializing extension hit NN with string = " << m_FPGATrackSimMapping->getExtensionNNHitMapString());
149 m_extensionHitNN.initialize(m_FPGATrackSimMapping->getExtensionNNHitMapString());
150 ATH_MSG_INFO("Initializing volume NN with string = " << m_FPGATrackSimMapping->getExtensionNNVolMapString());
151 m_extensionVolNN.initialize(m_FPGATrackSimMapping->getExtensionNNVolMapString());
152 }
153 else {
154 ATH_MSG_ERROR("Path to NN-based track extension ONNX file is empty! If you want to run this pipeline, you need to provide an input file.");
155 return StatusCode::FAILURE;
156 }
157
158 ATH_CHECK(m_tHistSvc.retrieve());
159 ATH_CHECK(m_chronoSvc.retrieve());
160
161 return StatusCode::SUCCESS;
162}
163
164
165StatusCode FPGATrackSimNNPathfinderExtensionTool::extendTracks(const std::vector<std::shared_ptr<const FPGATrackSimHit>> & hits,
166 const FPGATrackSimTrackCollection & tracks,
167 std::vector<FPGATrackSimRoad> & roads) {
168
169 // Reset the internal second stage roads storage.
170 roads.clear();
171 m_roads.clear();
172 const FPGATrackSimRegionMap* rmap_2nd = m_FPGATrackSimMapping->SubRegionMap_2nd();
173
174 // Create one "tower" per slice for this event.
175 // Note that there now might be only one "slice", at least for the time being.
176 if (m_slicedHitHeader) {
177 for (int ireg = 0; ireg < rmap_2nd->getNRegions(); ireg++) {
179 m_slicedHitHeader->addTower(tower);
180 }
181 }
182
183 // Build spatial index (maps) once per event
184 HitSpatialIndex hitIndex;
185 {
186 Athena::Chrono chronoBuildIndex("NNPathfinder:BuildSpatialIndex", m_chronoSvc.get());
187 hitIndex.build(hits);
188 }
189
190 if(m_debugEvent) ATH_MSG_DEBUG("Got: "<<tracks.size()<<" tracks to extrapolate");
191 // Now, loop over the tracks.
192 for (const FPGATrackSimTrack& track : tracks) {
193 Athena::Chrono chronoTrackLoop("NNPathfinder:TrackLoop", m_chronoSvc.get());
194 if(m_debugEvent) ATH_MSG_DEBUG("\033[1;31m-------------------------- extraploating Track ------------------ \033[0m");
195 if (track.passedOR() == 0) {
196 continue;
197 }
198 const std::vector<FPGATrackSimHit> hitsOnTrack = track.getFPGATrackSimHits();
199 miniRoad road;
200 float pt = track.getPt();
201
202 for (const auto &thit : hitsOnTrack) {
203 road.addHit(std::make_shared<const FPGATrackSimHit>(thit)); // add all hits, we check if WC later
204 }
205
206 if (m_debugEvent) {
207 ATH_MSG_DEBUG("-----------------Hits in event");
208 for (const auto& hit : hits) {
209 ATH_MSG_DEBUG("Hit " << " X: " << hit->getX() << " Y: " << hit->getY() << " Z: " << hit->getZ() << " R: " << hit->getR() << "phi = " << hit->getGPhi() << " hitType: " << hit->getHitType() << " getDetType: " << hit->getDetType());
210 }
211 }
212 // Using deque instead of vector for O(1) pop_front since we need operate only the first road each iteration
213 std::deque<miniRoad> roadsToExtrapolate;
214 roadsToExtrapolate.push_back(std::move(road));
215
216 std::vector<miniRoad> completedRoads;
217
218 int count = 0;
219 // FIXED: Add maximum iteration limit to prevent infinite loops
220 const int MAX_ROADS = 10000;
221 while(!roadsToExtrapolate.empty() && count < MAX_ROADS ) {
222 miniRoad currentRoad = std::move(roadsToExtrapolate.front());
223
224 // Erase this road from the deque
225 roadsToExtrapolate.pop_front();
226 count ++;
227 if(m_debugEvent) {
228 ATH_MSG_DEBUG("\033[1;31m-------------------------- extraploating road "<< count << "------------------ \033[0m");
229 printRoad(currentRoad);
230 }
231 // Check exit condition
232 if (currentRoad.getNHits() >= (m_nLayers_1stStage+m_nLayers_2ndStage))
233 {
234 completedRoads.push_back(std::move(currentRoad));
235 continue; // this one is done
236 }
237 // Other try to find the next hit in this road
238 std::vector<float> inputTensorValues;
239 std::vector<float> predhit;
240 long fineID;
241 {
242 Athena::Chrono chronoNN("NNPathfinder:NNInference", m_chronoSvc.get());
243 if (!fillInputTensorForNN(currentRoad, inputTensorValues)) {
244 ATH_MSG_WARNING("Failed to create input tensor for this road");
245 continue;
246 }
247 if (!getPredictedHit(inputTensorValues, predhit, fineID)) {
248 ATH_MSG_WARNING("Failed to predict hit for this road");
249 continue;
250 }
251 }
252 // Check if exist conditions are there
253 if (m_doOutsideIn) {
254 // Make sure we are not predicting inside the inner most layer (x and y < 25, or r < 25)
255 // If we are, road is done
256 if ((m_useCartesian && (abs(predhit[0]) < 25 && abs(predhit[1]) < 25)) ||
257 (!m_useCartesian && abs(predhit[0]) < 25))
258 {
259 completedRoads.push_back(std::move(currentRoad));
260 continue;
261 }
262 }
263 else {
264 // Make sure we are not predicting outside the outer most layer
265 // if we are, road is done
266 double radius = std::sqrt(predhit[0] * predhit[0] + predhit[1] * predhit[1]);
267 if ((m_useCartesian && (abs(predhit[0]) > 1024 || abs(predhit[1]) > 1024 || radius > 1024 || abs(predhit[2]) > 3000)) ||
268 (!m_useCartesian && (abs(predhit[0]) > 1024 || abs(predhit[2]) > 3000))) {
269 completedRoads.push_back(std::move(currentRoad));
270 continue;
271 }
272 }
273 if(m_debugEvent) {
274 ATH_MSG_DEBUG("Predicted hit at: " << predhit[0] << " " << predhit[1] << " " << predhit[2]);
275 }
276
277 // Now search for the hits
278 bool foundhitForRoad = false;
279 if(fineID == 215){
280 ATH_MSG_DEBUG("Stopping condition reached");
281 completedRoads.push_back(std::move(currentRoad));
282 continue;
283 }
284 // Get the last layer and hit in the road
285 unsigned lastLayerInRoad = 0;
286 std::shared_ptr<const FPGATrackSimHit> lastHit;
287 if(!getLastLayer(currentRoad, lastLayerInRoad, lastHit) or !lastHit) {
288 ATH_MSG_WARNING("Failed to find last layer this road");
289 continue;
290 }
291 unsigned layer = lastLayerInRoad+1; // the layer we're looking to find
292 bool lastHitWasReal = lastHit->isReal();
293 float lastHitR = lastHit->getR();
294 if(layer >= (m_nLayers_1stStage + m_nLayers_2ndStage)) {
295 completedRoads.push_back(std::move(currentRoad));
296 continue;
297 }
298 unsigned int hitsInWindow = 0;
299
300 // Cache predicted values and window parameters once per road, not per hit
301 const double predr = (m_useCartesian ? sqrt(predhit[0] * predhit[0] + predhit[1] * predhit[1]) : predhit[0]);
302 const double predphi = predhit[1];
303 const double predz = predhit[2];
304
305 // Calculate window parameters once
306 double windowR = m_windowR[0];
307 double windowPhi = m_windowPhi[0];
308 double windowZ = m_windowZ[0];
309 int fineID_index = 0;
310
311 if (m_windowZ.size() > 1 || m_windowR.size() > 1 || m_windowPhi.size() > 1) {
312 auto fineID_it = std::find(m_windowFineID.begin(), m_windowFineID.end(), fineID);
313 if (fineID_it == m_windowFineID.end()){
314 ATH_MSG_DEBUG("No windows for predicted fineID " << fineID << ", using maximum in provided list instead!");
315 fineID_index = -1;
316 }
317 else {
318 fineID_index = fineID_it - m_windowFineID.begin();
319 }
320 }
321 if (m_windowR.size() > 1) {
322 windowR = (fineID_index == -1) ?
323 *std::max_element(m_windowR.begin(), m_windowR.end()) :
324 m_windowR[fineID_index];
325 }
326 if (m_windowZ.size() > 1) {
327 windowZ = (fineID_index == -1) ?
328 *std::max_element(m_windowZ.begin(), m_windowZ.end()) :
329 m_windowZ[fineID_index];
330 }
331 if (m_windowPhi.size() > 1) {
332 windowPhi = (fineID_index == -1) ?
333 *std::max_element(m_windowPhi.begin(), m_windowPhi.end()) :
334 m_windowPhi[fineID_index];
335 }
336
337 // Apply scaling factors
338 if (m_missedHitRScaling > 0 && !lastHitWasReal) windowR *= m_missedHitRScaling;
339 if (m_missedHitZScaling > 0 && !lastHitWasReal) windowZ *= m_missedHitZScaling;
340 if (m_missedHitPhiScaling > 0 && !lastHitWasReal) windowPhi *= m_missedHitPhiScaling;
341 if (pt < m_lowPtValueForWindowRScaling.value()) windowR *= m_lowPtWindowRScaling.value();
342 if (pt < m_lowPtValueForWindowZScaling.value()) windowZ *= m_lowPtWindowZScaling.value();
343 if (pt < m_lowPtValueForWindowPhiScaling.value()) windowPhi *= m_lowPtWindowPhiScaling.value();
344
345 // List of all the hits, with their distances to the predicted point
346 std::vector<std::vector<std::shared_ptr<const FPGATrackSimHit>>> listofHitsFound;
347
348 {
349 Athena::Chrono chronoHitSearch("NNPathfinder:HitSearchLoop", m_chronoSvc.get());
350
351 // Use spatial index (HitSpatialIndex) to get only candidate hits with matching fineID
352 const auto* candidateHits = hitIndex.getHits(fineID);
353 if (candidateHits) {
354 listofHitsFound.reserve(candidateHits->size());
355
356 for (const FPGATrackSimHit* hit : *candidateHits) {
357 // Apply direction filter
358 const float hitr = hit->getR();
359 if (m_doOutsideIn && hitr > lastHitR) continue;
360 if (!m_doOutsideIn && hitr < lastHitR) continue;
361
362 if(m_debugEvent) {
363 ATH_MSG_DEBUG("In the hit loop hit at x: " << hit->getX() << " y " << hit->getY() << " z " << hit->getZ() << " phi " << hit->getGPhi());
364 }
365
366 // Check if hit is within window
367 const double hitz = hit->getZ();
368 const double hitphi = hit->getGPhi();
369
370 const double dr = abs(hitr - predr);
371 const double dz = abs(hitz - predz);
372 double dphi = abs(hitphi - predphi);
373 while (dphi > pi) dphi -= pi;
374
375 const bool inWindow = (m_useCartesian && dr < windowR && dz < windowZ) ||
376 (!m_useCartesian && dphi < windowPhi && dz < windowZ && dr < windowR);
377
378 if (!inWindow) continue;
379
380 // Only create shared_ptr when hit passes all filters
381 std::vector<std::shared_ptr<const FPGATrackSimHit>> theseHits{ makeNonOwningHitPtr(hit) };
382 hitsInWindow = hitsInWindow + 1;
383
384 // Handle strip space points
385 if(hit->isStrip()) {
386 if (hit->getHitType() == HitType::spacepoint) {
387 // Use spatial index for strip matching
388 const float EPSILON = 0.00001f;
389 const float searchX = hit->getX();
390 const float searchY = hit->getY();
391 const float searchZ = hit->getZ();
392 const auto searchHash = hit->getIdentifierHash();
393 bool found = false;
394
395 const auto* coordCandidates = hitIndex.getHitsByCoord(searchX, searchY, searchZ);
396 if (coordCandidates) {
397 for (const FPGATrackSimHit* candidateHit : *coordCandidates) {
398 if (candidateHit->getIdentifierHash() != searchHash &&
399 abs(candidateHit->getX() - searchX) < EPSILON &&
400 abs(candidateHit->getY() - searchY) < EPSILON &&
401 abs(candidateHit->getZ() - searchZ) < EPSILON) {
402 theseHits.push_back(makeNonOwningHitPtr(candidateHit));
403 found = true;
404 break;
405 }
406 }
407 }
408 if (!found) {
409 ATH_MSG_WARNING("For a SP in layer " << layer << " Couldn't find a matching strip SP");
410 }
411 }
412 else {
413 std::shared_ptr<FPGATrackSimHit> guessedSecondHitPtr = std::make_shared<FPGATrackSimHit>();
414 guessedSecondHitPtr->setX(0);
415 guessedSecondHitPtr->setY(0);
416 guessedSecondHitPtr->setZ(0);
417 guessedSecondHitPtr->setPhysLayer(lastHit->getPhysLayer(true)+1);
418 guessedSecondHitPtr->setHitType(HitType::undefined);
419 if(isFineIDInStrip(fineID)) guessedSecondHitPtr->setDetType(SiliconTech::strip);
420 else guessedSecondHitPtr->setDetType(SiliconTech::pixel);
421
422 theseHits.push_back(guessedSecondHitPtr);
423 }
424 }
425 // Store the hits for now
426 listofHitsFound.push_back(std::move(theseHits));
427 }
428 }
429 } // end HitSearchLoop timing
430
431 {
432 Athena::Chrono chronoSort("NNPathfinder:HitSorting", m_chronoSvc.get());
433
434 // Use partial_sort instead of sort since we only need the top N hits
435 const size_t nToSort = (m_maxBranches.value() >= 0) ?
436 std::min(static_cast<size_t>(m_maxBranches.value()), listofHitsFound.size()) :
437 listofHitsFound.size();
438
439 // Sort the hit by the distance
440 if (m_useCartesian) {
441 auto comparatorFunc = [&](const auto& a, const auto& b) {
442 return cartesianComparator(a, b, predr, predz);
443 };
444 if (nToSort < listofHitsFound.size()) {
445 std::partial_sort(listofHitsFound.begin(),
446 listofHitsFound.begin() + nToSort,
447 listofHitsFound.end(),
448 comparatorFunc);
449 } else {
450 std::sort(listofHitsFound.begin(), listofHitsFound.end(), comparatorFunc);
451 }
452 }
453 else {
454 const double zScale = getZScale();
455 const double phiScale = getPhiScale();
456 const double rScale = getRScale();
457 const double zScale2 = zScale * zScale;
458 const double phiScale2 = phiScale * phiScale;
459 const double rScale2 = rScale * rScale;
460
461 auto comparatorFunc = [&](const auto& a, const auto& b) {
462 return polarComparator(a, b, predr, predphi, predz, zScale2, phiScale2, rScale2);
463 };
464 if (nToSort < listofHitsFound.size()) {
465 std::partial_sort(listofHitsFound.begin(),
466 listofHitsFound.begin() + nToSort,
467 listofHitsFound.end(),
468 comparatorFunc);
469 } else {
470 std::sort(listofHitsFound.begin(), listofHitsFound.end(), comparatorFunc);
471 }
472 }
473 } // end HitSorting timing
474
475 // Select the top N hits
476 std::vector<std::vector<std::shared_ptr<const FPGATrackSimHit>>> cleanHitsToGrow;
477
478 // If max branches are limited, pick only the top N from the list of hits found at each level
479 if (m_maxBranches.value() >= 0) {
480 int nHitsToChoose = std::min(int(m_maxBranches.value()), int(listofHitsFound.size()));
481 cleanHitsToGrow.reserve(nHitsToChoose);
482 std::copy(listofHitsFound.begin(), listofHitsFound.begin() + nHitsToChoose, std::back_inserter(cleanHitsToGrow));
483 }
484 else {
485 cleanHitsToGrow = std::move(listofHitsFound);
486 }
487
488 {
489 Athena::Chrono chronoRoadBuilding("NNPathfinder:RoadBuilding", m_chronoSvc.get());
490 for (auto& hitsFound: cleanHitsToGrow) {
491
492 // We got a hit, lets make a road
493 miniRoad newRoad;
494 if(!addHitToRoad(newRoad, currentRoad, std::move(hitsFound))) {
495 ATH_MSG_WARNING("Failed to make a new road");
496 continue;
497 }
498 roadsToExtrapolate.push_back(newRoad);
499 foundhitForRoad = true;
500 if(m_debugEvent) {
501 ATH_MSG_DEBUG("------ road grown with hit from layer "<<layer<<" to");
502 printRoad(newRoad);
503 }
504 }
505 // If the hit wasn't found, push a fake hit
506 if (!foundhitForRoad) {
507 // did not find a hit to extrapolate to, check if we need to delete this road. if not, add a guessed hit if still useful
508 if (currentRoad.getNWCLayers() >= m_maxMiss) {
509 // we don't want this road, so we continue
510 continue;
511 }
512 else {
513 std::vector<std::shared_ptr<const FPGATrackSimHit>> theseHits;
514 // first make the fake hit that we will add
515 if (!getFakeHit(currentRoad, predhit, fineID, theseHits)) {
516 ATH_MSG_WARNING("Failed adding a guessed hit in extrapolation");
517 continue;
518 }
519
520 if((isFineIDInPixel(fineID) && theseHits.size() != 1) || (isFineIDInStrip(fineID) && theseHits.size() != 2)) {
521 continue;
522 }
523 // add the hit to the road
524 miniRoad newroad;
525 if (!addHitToRoad(newroad, currentRoad, std::move(theseHits))) {
526 ATH_MSG_WARNING("Failed making a new road with fake hit");
527 continue;
528 }
529 roadsToExtrapolate.push_back(std::move(newroad));
530 }
531 }
532 } // end RoadBuilding timing
533 }
534 // This track has been extrapolated, copy the completed tracks to the full list with full road objects
535 {
536 Athena::Chrono chronoRoadConversion("NNPathfinder:RoadConversion", m_chronoSvc.get());
537 for (const auto &miniroad : completedRoads) {
538 FPGATrackSimRoad road;
539 road.setWCLayers(miniroad.getWCLayers());
540 road.setHitLayers(miniroad.getHitLayers());
541 road.setRoadID(m_roads.size() - 1);
542 // Set the "Hough x" and "Hough y" using the track parameters.
543 road.setX(track.getPhi());
544 road.setY(track.getQOverPt());
545 road.setXBin(track.getHoughXBin());
546 road.setYBin(track.getHoughYBin());
547 road.setSubRegion(track.getSubRegion());
548
549 // just force the right number of layers now, in case we find fewer than expected (needed downstream)
550 std::vector<std::vector<std::shared_ptr<const FPGATrackSimHit>>> roadhits = miniroad.getVecHits();
551 unsigned nexpected = m_nLayers_1stStage+m_nLayers_2ndStage;
552 if (roadhits.size() > nexpected) { // cut off the last ones
553 roadhits.resize(nexpected);
554 }
555 else if (roadhits.size() < nexpected) { // fill with missing hits
556 for (unsigned layer = roadhits.size(); layer < nexpected; layer++) {
557 std::shared_ptr<FPGATrackSimHit> emptyHitPtr = std::make_shared<FPGATrackSimHit>();
558 emptyHitPtr->setX(0);
559 emptyHitPtr->setY(0);
560 emptyHitPtr->setZ(0);
561 emptyHitPtr->setLayer(layer);
562 emptyHitPtr->setHitType(HitType::wildcard);
563
564 roadhits.emplace_back(1,emptyHitPtr);
565 layer_bitmask_t wclayers = road.getWCLayers();
566 wclayers |= (1 << layer);
567 road.setWCLayers(wclayers);
568 }
569 }
570 road.setHits(std::move(roadhits));
571
572 m_roads.push_back(std::move(road));
573 }
574 } // end RoadConversion timing
575 }
576 // Copy the roads we found into the output argument and return success.
577 roads.reserve(m_roads.size());
578 for (FPGATrackSimRoad & r : m_roads)
579 {
580 if (r.getNWCLayers() >= m_maxMiss) continue; // extra check on this
581 roads.emplace_back(r);
582 }
583 ATH_MSG_DEBUG("Found " << roads.size() << " new roads in second stage.");
584
585 return StatusCode::SUCCESS;
586}
587
588StatusCode FPGATrackSimNNPathfinderExtensionTool::fillInputTensorForNN(miniRoad& thisroad, std::vector<float>& inputTensorValues)
589{
590 std::vector<std::shared_ptr<const FPGATrackSimHit>> hitsR;
591
592 std::vector<std::shared_ptr<const FPGATrackSimHit>> hits = thisroad.getHits();
593
594
595 for (unsigned ihit = 0; ihit < hits.size(); ihit++) {
596 if (ihit < m_nLayers_1stStage && !(hits[ihit]->isReal())) continue; // skip guessed hits for the 1st stage
597 hitsR.push_back(hits[ihit]);
598 }
599 // Sort in increasing 3D distance.
600 std::sort(hitsR.begin(), hitsR.end(), [](auto& a, auto& b){
601 double dist_a = std::hypot(a->getX(), a->getY(), a->getZ());
602 double dist_b = std::hypot(b->getX(), b->getY(), b->getZ());
603 return dist_a < dist_b;
604 });
605
606 if(m_debugEvent) ATH_MSG_DEBUG("hitsR");
607 for (const auto& thit : hitsR)
608 {
609 if (m_debugEvent) ATH_MSG_DEBUG(thit->getX() << " " << thit->getY() << " " << thit->getZ() << " and phi = " << thit->getGPhi());
610 }
611
612 // Remove all the duplicate space points
613 std::vector<std::shared_ptr<const FPGATrackSimHit>> cleanHits;
614 bool skipHit = false;
615 for (auto thit : hitsR)
616 {
617 if(skipHit)
618 {
619 skipHit = false;
620 continue;
621 }
622 if (thit->isPixel())
623 {
624 cleanHits.push_back(std::move(thit));
625 }
626 else if (thit->isStrip() && (thit->getHitType() == HitType::spacepoint))
627 {
628 // This is a proper strips SP, push the first hit back and skip the next one since its a duplicate
629 cleanHits.push_back(std::move(thit));
630 skipHit = true;
631 }
632 else if (thit->isStrip() && (thit->getHitType() == HitType::guessed))
633 {
634 // this is a guessed strip SP, push the first hit back and skip the next one since its a duplicate
635 cleanHits.push_back(std::move(thit));
636 skipHit = true;
637 }
638 else if (thit->isStrip() && (thit->getHitType() == HitType::undefined))
639 {
640 // this is a fake hit, inserted for a unpaired SP, continue
641 continue;
642 }
643 else if (thit->isStrip() && thit->isReal())
644 {
645 // What is left here is a unpaired hit, push it back
646 cleanHits.push_back(std::move(thit));
647 }
648 else
649 {
650 ATH_MSG_WARNING("No clue how to deal with this hit in the NN predicition ");
651 continue;
652 }
653 }
654
655 // Reverse the hits as we changed the ordering before
656 if(!m_doOutsideIn)
657 {
658 std::reverse(cleanHits.begin(), cleanHits.end());
659 }
660
661 // Select the top N hits
662 std::vector<std::shared_ptr<const FPGATrackSimHit>> hitsToEncode;
663 std::copy(cleanHits.begin(), cleanHits.begin() + m_predictionWindowLength, std::back_inserter(hitsToEncode));
664
665 if(m_debugEvent) ATH_MSG_DEBUG("Clean hits");
666 for (const auto& thit : cleanHits)
667 {
668 if (m_debugEvent) ATH_MSG_DEBUG(thit->getX() << " " << thit->getY() << " " << thit->getZ() << " " << thit->isStrip() << " and gphi = " << thit->getGPhi());
669 }
670
671 // Reverse this vector so we can encode it the format as expected from the NN
672 std::reverse(hitsToEncode.begin(), hitsToEncode.end());
673
674 if (m_debugEvent) ATH_MSG_DEBUG("Input for NN prediction");
675 for (const auto& thit : hitsToEncode)
676 {
677 if (m_useCartesian) {
678 inputTensorValues.push_back(thit->getX() / getXScale());
679 inputTensorValues.push_back(thit->getY() / getYScale());
680 inputTensorValues.push_back(thit->getZ() / getZScale());
681 }
682 else {
683 inputTensorValues.push_back(thit->getR() / getRScale());
684 inputTensorValues.push_back(thit->getGPhi() / getPhiScale());
685 inputTensorValues.push_back(thit->getZ() / getZScale());
686 }
687
688 if (m_debugEvent) ATH_MSG_DEBUG(thit->getX() << " " << thit->getY() << " " << thit->getZ() << " and gphi = " << thit->getGPhi());
689 }
690
691 return StatusCode::SUCCESS;
692
693}
694
695
696StatusCode FPGATrackSimNNPathfinderExtensionTool::getPredictedHit(std::vector<float>& inputTensorValues, std::vector<float>& outputTensorValues, long& fineID)
697{
698 std::vector<float> NNVoloutput = m_extensionVolNN.runONNXInference(inputTensorValues);
699 fineID = std::distance(NNVoloutput.begin(),std::max_element(NNVoloutput.begin(), NNVoloutput.end()));
700 // Insert the output for the second stage NN
701 inputTensorValues.insert(inputTensorValues.end(), NNVoloutput.begin(), NNVoloutput.end()); //now we append the first NN to the list of coordinates
702
703 // use the above to predict the next hit position
704 outputTensorValues = m_extensionHitNN.runONNXInference(inputTensorValues);
705
706 // now scale back
707 if (m_useCartesian) {
708 outputTensorValues[0] *= getXScale();
709 outputTensorValues[1] *= getYScale();
710 outputTensorValues[2] *= getZScale();
711 }
712 else {
713 outputTensorValues[0] *= getRScale();
714 outputTensorValues[1] *= getPhiScale();
715 outputTensorValues[2] *= getZScale();
716 }
717
718 return StatusCode::SUCCESS;
719}
720
721StatusCode FPGATrackSimNNPathfinderExtensionTool::addHitToRoad(miniRoad& newroad, miniRoad& currentRoad, const std::vector<std::shared_ptr<const FPGATrackSimHit>>& hits)
722{
723 newroad.setHits(currentRoad.getHits());
724 newroad.addHits(hits);
725 return StatusCode::SUCCESS;
726}
727
728StatusCode FPGATrackSimNNPathfinderExtensionTool::getFakeHit(miniRoad& currentRoad, std::vector<float>& predhit, const long& fineID, std::vector<std::shared_ptr<const FPGATrackSimHit>>& hits) {
729
730 unsigned guessedLayer(0);
731
732 std::shared_ptr<FPGATrackSimHit> guessedHitPtr = std::make_shared<FPGATrackSimHit>();
733 if (m_useCartesian) {
734 guessedHitPtr->setX(predhit[0]);
735 guessedHitPtr->setY(predhit[1]);
736 guessedHitPtr->setZ(predhit[2]);
737 }
738 else {
739 double r = predhit[0];
740 double phi = predhit[1];
741 guessedHitPtr->setX(r*cos(phi));
742 guessedHitPtr->setY(r*sin(phi));
743 guessedHitPtr->setZ(predhit[2]);
744 }
745
746
747 if (m_doOutsideIn) { // outside in
748 // TODO
749 }
750 else { // nope, inside out
751 guessedLayer = currentRoad.getNHits();
752 }
753
754 guessedHitPtr->setLayer(guessedLayer);
755 guessedHitPtr->setHitType(HitType::guessed);
756
757 if(isFineIDInStrip(fineID)) {
758 guessedHitPtr->setDetType(SiliconTech::strip);
759 guessedHitPtr->setPhysLayer(0); // it is just to set the side, it's not actually 0
760 }
761 else guessedHitPtr->setDetType(SiliconTech::pixel);
762
763
764 // Make sure that the hit is inside the boundaries of the layers
765 if(guessedLayer < (m_nLayers_1stStage + m_nLayers_2ndStage ))
766 {
767 hits.push_back(guessedHitPtr);
768 }
769 // if in strips, add the second hit into the list
770 if(isFineIDInStrip(fineID))
771 {
772 std::shared_ptr<FPGATrackSimHit> guessedSecondHitPtr = std::make_shared<FPGATrackSimHit>();
773 guessedSecondHitPtr->setX(0);
774 guessedSecondHitPtr->setY(0);
775 guessedSecondHitPtr->setZ(0);
776 guessedSecondHitPtr->setLayer( guessedLayer + 1 );
777 guessedSecondHitPtr->setHitType(HitType::guessed);
778
779 guessedSecondHitPtr->setDetType(SiliconTech::strip);
780 guessedHitPtr->setPhysLayer(1); // it is just to set the side, it's not actually 1
781 // Make sure that the hit is inside the boundaries of the layers
782 if((guessedLayer + 1) < (m_nLayers_1stStage + m_nLayers_2ndStage ))
783 {
784 hits.push_back(guessedSecondHitPtr);
785 }
786 }
787
788
789
790 return StatusCode::SUCCESS;
791
792}
793
795{
796 if (!m_debugEvent) return;
797
798 // print this road
799 if (m_debugEvent)
800 {
801 std::vector<std::shared_ptr<const FPGATrackSimHit>> hitsR;
802 for (const auto& hit : currentRoad.getHits()) {
803 hitsR.push_back(hit);
804 }
805
806 // If outside in, sort in increasing R, otherwise, decreasing R
807 if (m_doOutsideIn)
808 {
809 std::sort(hitsR.begin(), hitsR.end(), [](const auto& a, const auto& b) {
810 if (a->getR() == b->getR()) return a->getLayer() < b->getLayer();
811 return a->getR() < b->getR();
812 });
813 }
814 else
815 {
816 std::sort(hitsR.begin(), hitsR.end(), [](const auto& a, const auto& b) {
817 return a->getR() > b->getR();
818 });
819 }
820
821 for (unsigned long i = 0; i < hitsR.size(); i++)
822 {
823 ATH_MSG_DEBUG("Hit i " << i << " X: " << hitsR[i]->getX() << " Y: " << hitsR[i]->getY() << " Z: " << hitsR[i]->getZ() << " R: " << hitsR[i]->getR() << " hitType: " << hitsR[i]->getHitType() << " getDetType: " << hitsR[i]->getDetType() << "phi = " << hitsR[i]->getGPhi());
824 }
825 }
826
827}
828
829StatusCode FPGATrackSimNNPathfinderExtensionTool::getLastLayer(miniRoad& currentRoad, unsigned& lastHitLayer, std::shared_ptr<const FPGATrackSimHit>& lastHit)
830{
831 lastHitLayer = currentRoad.getNHits()-1;
832 lastHit = currentRoad.getHit(lastHitLayer);
833 return StatusCode::SUCCESS;
834
835}
Scalar phi() const
phi method
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_ERROR(x)
#define ATH_MSG_INFO(x)
#define ATH_MSG_WARNING(x)
#define ATH_MSG_DEBUG(x)
Exception-safe IChronoSvc caller.
constexpr float EPSILON
bool isFineIDInStrip(long ID)
long getFineID(const FPGATrackSimHit &hit)
bool isFineIDInPixel(long ID)
Default track extension algorithm to produce "second stage" roads.
This file declares a class that stores the module IDs of the sectors.
std::vector< FPGATrackSimTrack > FPGATrackSimTrackCollection
uint32_t layer_bitmask_t
static Double_t a
#define pi
#define y
#define x
#define z
Exception-safe IChronoSvc caller.
Definition Chrono.h:50
float getY() const
unsigned getIdentifierHash() const
float getX() const
float getGPhi() const
float getZ() const
float getR() const
SiliconTech getDetType() const
bool isStrip() const
HitType getHitType() const
ServiceHandle< IFPGATrackSimMappingSvc > m_FPGATrackSimMapping
StatusCode getPredictedHit(std::vector< float > &inputTensorValues, std::vector< float > &outputTensorValues, long &fineID)
StatusCode getFakeHit(miniRoad &currentRoad, std::vector< float > &predhit, const long &fineID, std::vector< std::shared_ptr< const FPGATrackSimHit > > &hits)
Gaudi::Property< std::vector< float > > m_windowPhi
virtual StatusCode extendTracks(const std::vector< std::shared_ptr< const FPGATrackSimHit > > &hits, const FPGATrackSimTrackCollection &tracks, std::vector< FPGATrackSimRoad > &roads) override
StatusCode getLastLayer(miniRoad &currentRoad, unsigned &lastHitLayer, std::shared_ptr< const FPGATrackSimHit > &lastHit)
StatusCode addHitToRoad(miniRoad &newroad, miniRoad &currentRoad, const std::vector< std::shared_ptr< const FPGATrackSimHit > > &hits)
FPGATrackSimLogicalEventInputHeader * m_slicedHitHeader
StatusCode fillInputTensorForNN(miniRoad &thisRoad, std::vector< float > &inputTensorValues)
void setHitLayers(layer_bitmask_t hit_layers)
void setY(float v)
void setHits(std::vector< std::vector< std::shared_ptr< const FPGATrackSimHit > > > &&hits)
void setXBin(unsigned v)
void setYBin(unsigned v)
void setRoadID(int roadID)
void setSubRegion(int v)
layer_bitmask_t getWCLayers() const
void setX(float v)
void setWCLayers(layer_bitmask_t wc_layers)
int r
Definition globals.cxx:22
int count(std::string s, const std::string &regx)
count how many occurances of a regx are in a string
Definition hcg.cxx:146
void sort(typename DataModel_detail::iterator< DVL > beg, typename DataModel_detail::iterator< DVL > end)
Specialization of sort for DataVector/List.
void partial_sort(DataModel_detail::iterator< DVL > beg, DataModel_detail::iterator< DVL > mid, DataModel_detail::iterator< DVL > end)
Specialization of partial_sort for DataVector/List.
void reverse(typename DataModel_detail::iterator< DVL > beg, typename DataModel_detail::iterator< DVL > end)
Specialization of reverse for DataVector/List.
void addHits(const std::vector< std::shared_ptr< const FPGATrackSimHit > > &hits)
void addHit(const std::shared_ptr< const FPGATrackSimHit > &hit)
std::vector< std::shared_ptr< const FPGATrackSimHit > > & getHits()
std::shared_ptr< const FPGATrackSimHit > getHit(size_t layer) const
void setHits(std::vector< std::shared_ptr< const FPGATrackSimHit > > &hits)