ATLAS Offline Software
Loading...
Searching...
No Matches
FPGATrackSimOverlapRemovalTool.cxx
Go to the documentation of this file.
1// Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration
2
6
7#include "GaudiKernel/MsgStream.h"
8
9#include <sstream>
10#include <iostream>
11#include <fstream>
12
14FPGATrackSimOverlapRemovalTool::FPGATrackSimOverlapRemovalTool(const std::string& algname, const std::string& name, const IInterface *ifc) :
15 AthAlgTool(algname, name, ifc)
16{
17}
18
20{
21 ATH_MSG_INFO( "FPGATrackSimOverlapRemovalTool::initialize()" );
22
23 if (!m_monTool.empty()) ATH_CHECK(m_monTool.retrieve());
24
25 // Check road OR
27 ATH_MSG_WARNING("LocalMaxOR only being run per hough slice (i.e. this tool does nothing) since roadSliceOR is turned off");
28
29
30 // Setup OR algorithm
31 if(m_algorithm == "Normal") m_algo=ORAlgo::Normal;
32 else if(m_algorithm == "Invert") m_algo=ORAlgo::InvertGrouping;
33 else
34 {
35 ATH_MSG_ERROR("initialize(): OR algorithm doesn't exist. ");
36 return StatusCode::FAILURE;
37 }
38 ATH_MSG_DEBUG("Overlap removal algorithm is "<<m_algorithm.value());
39
40 return StatusCode::SUCCESS;
41}
42
43bool isLocalMax(vector2D<FPGATrackSimRoad*> const & acc, unsigned x, unsigned y, int localMaxWindowSize)
44{
45 if (!localMaxWindowSize) return true;
46 if (!acc(y, x)) return false;
47 for (int j = -localMaxWindowSize; j <= localMaxWindowSize; j++)
48 for (int i = -localMaxWindowSize; i <= localMaxWindowSize; i++)
49 {
50 if (i == 0 && j == 0) continue;
51 if (y + j < acc.size(0) && x + i < acc.size(1))
52 {
53 if (!acc(y+j, x+i)) continue;
54 if (acc(y+j, x+i)->getNHitLayers() > acc(y, x)->getNHitLayers()) return false;
55 if (acc(y+j, x+i)->getNHitLayers() == acc(y, x)->getNHitLayers())
56 {
57 if (acc(y+j, x+i)->getNHits() > acc(y, x)->getNHits()) return false;
58 if (acc(y+j, x+i)->getNHits() == acc(y, x)->getNHits() && j <= 0 && i <= 0) return false;
59 }
60 }
61 }
62
63 return true;
64}
65
66StatusCode FPGATrackSimOverlapRemovalTool::runOverlapRemoval(std::vector<FPGATrackSimRoad>& roads)
67{
68 if (roads.empty()) return StatusCode::SUCCESS;
69
70 if (!m_roadSliceOR) return StatusCode::SUCCESS;
71 size_t in = roads.size();
72
73 // Hough image
75
76 // Slice-wise duplicate removal: accept only one road (with most hits) per bin
77 for (auto &r: roads)
78 {
79 FPGATrackSimRoad* & old = acc(r.getYBin(), r.getXBin());
80 if (!old) old = new FPGATrackSimRoad (r);
81 else if (r.getNHitLayers() > old->getNHitLayers()) *old = r;
82 else if (r.getNHitLayers() == old->getNHitLayers() && r.getNHits() > old->getNHits()) *old = r;
83 }
84
85 // Reformat to vector
86 roads.clear();
87 for (unsigned y = 0; y < m_imageSize_y; y++)
88 for (unsigned x = 0; x < m_imageSize_x; x++)
89 if (FPGATrackSimRoad *tempPtr = acc(y, x); tempPtr && isLocalMax(acc, x, y, m_localMaxWindowSize)/*All-slices local max*/) {
90 roads.emplace_back(*tempPtr);
91 acc(y, x) = nullptr;
92 }
93 else {
94 delete acc(y,x);
95 acc(y,x) = nullptr;
96 }
97
98 ATH_MSG_DEBUG("Input: " << in << " Output: " << roads.size());
99 return StatusCode::SUCCESS;
100}
101
102StatusCode FPGATrackSimOverlapRemovalTool::runOverlapRemoval(std::vector<FPGATrackSimTrack>& tracks)
103{
104
105 // Do fast OR instead of requested
106 if (m_doFastOR) return runOverlapRemoval_fast(tracks);
107
108 // Otherwise, proceed
109 ATH_MSG_DEBUG("Beginning runOverlapRemoval()");
110
111 ATH_MSG_DEBUG("Tracks in event: " << tracks.size());
112
113
114 if (m_useV2OR)
115 return runOverlapRemoval_v2(tracks);
116 return ::runOverlapRemoval(tracks, m_minChi2.value(), m_NumOfHitPerGrouping, getAlgorithm(), m_monTool, m_compareAllHits);
117}
118
119
121
122 // Hit comparison
123 struct HitCompare {
124 bool operator()(const FPGATrackSimHit* a, const FPGATrackSimHit* b) const {
125 auto hash_a = a->getIdentifierHash();
126 auto hash_b = b->getIdentifierHash();
127 if ( hash_a == hash_b ) {
128 auto phi_a = a->getPhiIndex();
129 auto phi_b = b->getPhiIndex();
130 if ( phi_a == phi_b ) {
131 auto eta_a = a->getEtaIndex();
132 auto eta_b = b->getEtaIndex();
133 if ( eta_a == eta_b) {
134 auto layer_a = a->getPhysLayer();
135 auto layer_b = b->getPhysLayer();
136 return layer_a < layer_b;
137 }
138 return eta_a < eta_b;
139 }
140 return phi_a < phi_b;
141 }
142 return hash_a < hash_b;
143 }
144 };
145 std::set<const FPGATrackSimHit*, HitCompare > hitsInTrack1;
146 for ( const auto& hit_ptr : track1.getFPGATrackSimHitPtrs()) {
147 if (!hit_ptr) throw std::runtime_error("Null hit pointer in compareTrackQuality: tracks should not have unassigned layers");
148 if (hit_ptr->isReal()) hitsInTrack1.insert(hit_ptr.get());
149 }
150
151 std::set<const FPGATrackSimHit*, HitCompare> hitsInTrack2;
152 for ( const auto& hit_ptr: track2.getFPGATrackSimHitPtrs()){
153 if (!hit_ptr) throw std::runtime_error("Null hit pointer in compareTrackQuality: tracks should not have unassigned layers");
154 if (hit_ptr->isReal()) hitsInTrack2.insert(hit_ptr.get());
155 }
156
157 std::vector<const FPGATrackSimHit*> sharedHits;
158 std::set_intersection( hitsInTrack1.begin(), hitsInTrack1.end(),
159 hitsInTrack2.begin(), hitsInTrack2.end(),
160 std::back_inserter(sharedHits),
161 HitCompare() );
162
163 // Number of real hits in track 1, number of real hits in track 2, number of shared hits, number of non-shared hits (?)
164 int nHitsInTrack1 = hitsInTrack1.size();
165 int nHitsInTrack2 = hitsInTrack2.size();
166 int nSharedHits = sharedHits.size();
167 // Original version seems to be only track 1; I want to compare each pair only once so
168 // let's make this the most conservative option (i.e. smallest number) ?
169 int nonOverlappingHits = std::min(nHitsInTrack1 - nSharedHits, nHitsInTrack2 - nSharedHits);
170
171 // Now check if these pass our criteria for overlapping. If not, just return.
173 // Here decision is based on number of overlapping hits.
174 // Consider these overlapping if they share >= m_NumOfHitPerGrouping
175 if(nSharedHits < m_NumOfHitPerGrouping) return StatusCode::SUCCESS;
176
177 } else if(getAlgorithm() == ORAlgo::InvertGrouping) {
178 // This is the opposite: duplicates of number of unique hits is <= m_NumOfHitPerGrouping.
179 if(nonOverlappingHits > m_NumOfHitPerGrouping) return StatusCode::SUCCESS;
180
181 } else {
182 // Unknown.
183 return StatusCode::FAILURE;
184 }
185
186 // Made it here: these tracks are overlapping.
187 // But we already sorted them such that track 2 is the one
188 // we want to keep, so we can just set track 1 to be removed.
189 track1.setPassedOR(0);
190
191 return StatusCode::SUCCESS;
192}
193
195{
196 std::vector<const FPGATrackSimHit*> hitsInTrack1;
197 for ( const auto& hit_ptr : track1.getFPGATrackSimHitPtrs()) {
198 if (!hit_ptr) throw std::runtime_error("Null hit pointer in countOverlappingHits_v2: tracks should not have unassigned layers");
199 if (hit_ptr->isReal()) hitsInTrack1.push_back(hit_ptr.get());
200 }
201
202 std::vector<const FPGATrackSimHit*> hitsInTrack2;
203 for ( const auto& hit_ptr: track2.getFPGATrackSimHitPtrs()){
204 if (!hit_ptr) throw std::runtime_error("Null hit pointer in countOverlappingHits_v2: tracks should not have unassigned layers");
205 if (hit_ptr->isReal()) hitsInTrack2.push_back(hit_ptr.get());
206 }
207
208 // If one track has more hits than the other, it's better.
209 // Return true if track 2 is better than track 1.
210 // Otherwise, decide based on chi2.
211 bool goodOrder = true;
212 if (hitsInTrack1.size() == hitsInTrack2.size()) {
213 // Surprising number of cases where the chi2 is actually identical.
214 // In these cases, let's default to the track ID number as the next most important property.
215 // So put it higher since we are considering later tracks to be better.
216 if (track1.getChi2ndof() == track2.getChi2ndof() && track1.getTrackID() < track2.getTrackID()) goodOrder = false;
217 // Now assuming they're different, we want them in decreasing chi2 order
218 else if (track1.getChi2ndof() < track2.getChi2ndof()) goodOrder = false;
219 } else if (hitsInTrack1.size() > hitsInTrack2.size()) {
220 goodOrder = false;
221 }
222
223 return goodOrder;
224
225}
226
227StatusCode FPGATrackSimOverlapRemovalTool::runOverlapRemoval_fast(std::vector<FPGATrackSimTrack>& tracks)
228{
229 ATH_MSG_DEBUG("Beginning fast overlap removal");
230
231 // Sort tracks in order of increasing quality.
232 // This way, once a track has been eliminated in a comparison, we don't
233 // need to check it against any other tracks - they will always be
234 // better than it.
235 std::sort(std::begin(tracks),
236 std::end(tracks),
238
239 // Now compare every pair of tracks.
240 // Set passedOR to 0 for the worst one (first one)
241 // if they are found to overlap.
242 for (unsigned int i=0; i < tracks.size(); i++) {
243
244 // Skip track i if bad chi2.
245 if (tracks.at(i).getChi2ndof() > m_minChi2.value()) {
246 tracks.at(i).setPassedOR(0);
247 continue;
248 }
249
250 // Now check against all other tracks.
251 for (unsigned int j=i+1; j< tracks.size(); j++) {
252
253 // Uniquely comparing tracks i and j here.
254
255 // If have set track i to 0 in a previous comparison,
256 // no need to look at it any more - we're done with it.
257 if (!tracks.at(i).passedOR()) break;
258
259 // Ignore j if its chi2 is bad.
260 if (tracks.at(j).getChi2ndof() > m_minChi2.value()) tracks.at(j).setPassedOR(0);
261
262 // If we just set track j to zero for bad chi2,
263 // no need to do the comparison.
264 if (!tracks.at(j).passedOR()) continue;
265
266 // If we're still here, two at least semi-decent tracks.
267 // Compare them and remove one if necessary.
268 ATH_CHECK(removeOverlapping(tracks.at(i),tracks.at(j)));
269
270 }
271 }
272
273 return StatusCode::SUCCESS;
274}
275
276// V2 comparison: sorts worst-to-best (fewer hits = worse, higher chi2 = worse)
278{
279 // Count real hits in each track
280 int nHits1 = countRealHits_v2(track1);
281 int nHits2 = countRealHits_v2(track2);
282
283 // Sort worst to best: fewer hits is worse, higher chi2 is worse
284 // Return true if track1 should come before track2 (i.e., track1 is worse)
285
286 // First compare number of hits
287 if (nHits1 != nHits2) {
288 return nHits1 < nHits2; // Fewer hits = worse
289 }
290
291 // Same number of hits: compare chi2 (higher is worse)
292 float chi2_1 = track1.getChi2ndof();
293 float chi2_2 = track2.getChi2ndof();
294
295 // Then compare chi2
296 if (std::abs(chi2_1 - chi2_2) > std::numeric_limits<float>::epsilon()) {
297 return chi2_1 > chi2_2; // Higher chi2 = worse
298 }
299
300 // Tie-breaker:
301 // in case of same number of hits and same chi2, use track ID (higher ID = worse)
302 return track1.getTrackID() > track2.getTrackID();
303}
304
306{
307 int nHits = 0;
308 for (const auto& hit_ptr : track.getFPGATrackSimHitPtrs()) {
309 if (!hit_ptr) throw std::runtime_error("Null hit pointer in countRealHits_v2: tracks should not have unassigned layers");
310 if (hit_ptr->isReal()) nHits++;
311 }
312 return nHits;
313}
314
316{
317 // Use the existing functions from FPGATrackSimHoughFunctions
318 if (m_compareAllHits) {
319 return findNCommonHitsGlobal(track1, track2);
320 } else {
321 return findNCommonHits(track1, track2);
322 }
323}
324
325StatusCode FPGATrackSimOverlapRemovalTool::runOverlapRemoval_v2(std::vector<FPGATrackSimTrack>& tracks)
326{
327 ATH_MSG_DEBUG("Beginning v2 overlap removal on " << tracks.size() << " tracks");
328
329 // First pass: mark tracks with bad chi2
330 for (auto& track : tracks) {
331 if (track.getChi2ndof() > m_minChi2.value()) {
332 track.setPassedOR(0);
333 }
334 }
335
336 // Sort tracks from worst to best quality using v2 comparison
337 // After sorting: index 0 = worst track (fewest hits, highest chi2)
338 // last index = best track (most hits, lowest chi2)
339 std::sort(tracks.begin(), tracks.end(), compareTrackQuality_v2);
340
341 // Process from worst to best
342 // For each track at index i, compare with better tracks (j > i)
343 // If overlap found, mark track i as rejected and move to next
344 for (size_t i = 0; i < tracks.size(); i++) {
345
346 // Skip if already rejected (bad chi2 or previous overlap)
347 if (!tracks[i].passedOR()) continue;
348
349 // Compare with all better tracks (j > i)
350 for (size_t j = i + 1; j < tracks.size(); j++) {
351
352 // Skip if track j already rejected
353 if (!tracks[j].passedOR()) continue;
354
355 // Count overlapping hits
356 int nOverlapping = countOverlappingHits_v2(tracks[i], tracks[j]);
357
358 // Check if tracks overlap based on algorithm
359 bool isOverlap = false;
360 if (m_algo == ORAlgo::Normal) {
361 isOverlap = (nOverlapping >= m_NumOfHitPerGrouping);
362 } else if (m_algo == ORAlgo::InvertGrouping) {
363 // For InvertGrouping: overlap if non-overlapping hits <= threshold
364 int nHits_i = countRealHits_v2(tracks[i]);
365 int nHits_j = countRealHits_v2(tracks[j]);
366 int nonOverlapping = std::min(nHits_i - nOverlapping, nHits_j - nOverlapping);
367 isOverlap = (nonOverlapping <= m_NumOfHitPerGrouping);
368 }
369
370 if (isOverlap) {
371 // Track i overlaps with better track j -> reject track i
372 tracks[i].setPassedOR(0);
373 ATH_MSG_DEBUG("Track " << i << " rejected due to overlap with better track " << j);
374
375 // No need to compare track i with any more tracks
376 break;
377 }
378 }
379 }
380
381 // Count survivors for monitoring
382 int nSurvivors = 0;
383 for (const auto& track : tracks) {
384 if (track.passedOR()) nSurvivors++;
385 }
386 ATH_MSG_DEBUG("V2 OR complete: " << nSurvivors << " tracks surviving out of " << tracks.size());
387
388 return StatusCode::SUCCESS;
389}
390
391
#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)
int findNCommonHitsGlobal(const FPGATrackSimTrack &Track1, const FPGATrackSimTrack &Track2)
int findNCommonHits(const FPGATrackSimTrack &Track1, const FPGATrackSimTrack &Track2)
bool isLocalMax(vector2D< FPGATrackSimRoad * > const &acc, unsigned x, unsigned y, int localMaxWindowSize)
Overlap removal tool for FPGATrackSimTrack.
Maps physical layers to logical layers.
Defines several vector wrappers for homogenous multi-dimensional vectors, declared as 1D arrays for l...
static Double_t a
static const uint32_t nHits
#define y
#define x
AthAlgTool(const std::string &type, const std::string &name, const IInterface *parent)
Constructor with parameters:
ToolHandle< GenericMonitoringTool > m_monTool
int countOverlappingHits_v2(const FPGATrackSimTrack &track1, const FPGATrackSimTrack &track2) const
static bool compareTrackQuality_v2(const FPGATrackSimTrack &track1, const FPGATrackSimTrack &track2)
StatusCode runOverlapRemoval_v2(std::vector< FPGATrackSimTrack > &tracks)
StatusCode runOverlapRemoval_fast(std::vector< FPGATrackSimTrack > &tracks)
static int countRealHits_v2(const FPGATrackSimTrack &track)
StatusCode runOverlapRemoval(std::vector< FPGATrackSimRoad > &roads)
static bool compareTrackQuality(const FPGATrackSimTrack &track1, const FPGATrackSimTrack &track2)
StatusCode removeOverlapping(FPGATrackSimTrack &track1, FPGATrackSimTrack &track2)
Gaudi::Property< std::string > m_algorithm
void setPassedOR(unsigned int)
const std::vector< std::shared_ptr< const FPGATrackSimHit > > & getFPGATrackSimHitPtrs() const
float getChi2ndof() const
int r
Definition globals.cxx:22
void sort(typename DataModel_detail::iterator< DVL > beg, typename DataModel_detail::iterator< DVL > end)
Specialization of sort for DataVector/List.