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
146 std::set<const FPGATrackSimHit*, HitCompare > hitsInTrack1;
147 for ( auto& hit : track1.getFPGATrackSimHits()) {
148 if (hit.isReal()) hitsInTrack1.insert(&hit);
149 }
150
151 std::set<const FPGATrackSimHit*, HitCompare> hitsInTrack2;
152 for ( auto& hit: track2.getFPGATrackSimHits()){
153 if (hit.isReal()) hitsInTrack2.insert(&hit);
154 }
155
156 std::vector<const FPGATrackSimHit*> sharedHits;
157 std::set_intersection( hitsInTrack1.begin(), hitsInTrack1.end(),
158 hitsInTrack2.begin(), hitsInTrack2.end(),
159 std::back_inserter(sharedHits),
160 HitCompare() );
161
162 // Number of real hits in track 1, number of real hits in track 2, number of shared hits, number of non-shared hits (?)
163 int nHitsInTrack1 = hitsInTrack1.size();
164 int nHitsInTrack2 = hitsInTrack2.size();
165 int nSharedHits = sharedHits.size();
166 // Original version seems to be only track 1; I want to compare each pair only once so
167 // let's make this the most conservative option (i.e. smallest number) ?
168 int nonOverlappingHits = std::min(nHitsInTrack1 - nSharedHits, nHitsInTrack2 - nSharedHits);
169
170 // Now check if these pass our criteria for overlapping. If not, just return.
172 // Here decision is based on number of overlapping hits.
173 // Consider these overlapping if they share >= m_NumOfHitPerGrouping
174 if(nSharedHits < m_NumOfHitPerGrouping) return StatusCode::SUCCESS;
175
176 } else if(getAlgorithm() == ORAlgo::InvertGrouping) {
177 // This is the opposite: duplicates of number of unique hits is <= m_NumOfHitPerGrouping.
178 if(nonOverlappingHits > m_NumOfHitPerGrouping) return StatusCode::SUCCESS;
179
180 } else {
181 // Unknown.
182 return StatusCode::FAILURE;
183 }
184
185 // Made it here: these tracks are overlapping.
186 // But we already sorted them such that track 2 is the one
187 // we want to keep, so we can just set track 1 to be removed.
188 track1.setPassedOR(0);
189
190 return StatusCode::SUCCESS;
191}
192
194{
195 std::vector<const FPGATrackSimHit*> hitsInTrack1;
196 for ( auto& hit : track1.getFPGATrackSimHits()) {
197 if (hit.isReal()) hitsInTrack1.push_back(&hit);
198 }
199
200 std::vector<const FPGATrackSimHit*> hitsInTrack2;
201 for ( auto& hit: track2.getFPGATrackSimHits()){
202 if (hit.isReal()) hitsInTrack2.push_back(&hit);
203 }
204
205 // If one track has more hits than the other, it's better.
206 // Return true if track 2 is better than track 1.
207 // Otherwise, decide based on chi2.
208 bool goodOrder = true;
209 if (hitsInTrack1.size() == hitsInTrack2.size()) {
210 // Surprising number of cases where the chi2 is actually identical.
211 // In these cases, let's default to the track ID number as the next most important property.
212 // So put it higher since we are considering later tracks to be better.
213 if (track1.getChi2ndof() == track2.getChi2ndof() && track1.getTrackID() < track2.getTrackID()) goodOrder = false;
214 // Now assuming they're different, we want them in decreasing chi2 order
215 else if (track1.getChi2ndof() < track2.getChi2ndof()) goodOrder = false;
216 } else if (hitsInTrack1.size() > hitsInTrack2.size()) {
217 goodOrder = false;
218 }
219
220 return goodOrder;
221
222}
223
224StatusCode FPGATrackSimOverlapRemovalTool::runOverlapRemoval_fast(std::vector<FPGATrackSimTrack>& tracks)
225{
226 ATH_MSG_DEBUG("Beginning fast overlap removal");
227
228 // Sort tracks in order of increasing quality.
229 // This way, once a track has been eliminated in a comparison, we don't
230 // need to check it against any other tracks - they will always be
231 // better than it.
232 std::sort(std::begin(tracks),
233 std::end(tracks),
235
236 // Now compare every pair of tracks.
237 // Set passedOR to 0 for the worst one (first one)
238 // if they are found to overlap.
239 for (unsigned int i=0; i < tracks.size(); i++) {
240
241 // Skip track i if bad chi2.
242 if (tracks.at(i).getChi2ndof() > m_minChi2.value()) {
243 tracks.at(i).setPassedOR(0);
244 continue;
245 }
246
247 // Now check against all other tracks.
248 for (unsigned int j=i+1; j< tracks.size(); j++) {
249
250 // Uniquely comparing tracks i and j here.
251
252 // If have set track i to 0 in a previous comparison,
253 // no need to look at it any more - we're done with it.
254 if (!tracks.at(i).passedOR()) break;
255
256 // Ignore j if its chi2 is bad.
257 if (tracks.at(j).getChi2ndof() > m_minChi2.value()) tracks.at(j).setPassedOR(0);
258
259 // If we just set track j to zero for bad chi2,
260 // no need to do the comparison.
261 if (!tracks.at(j).passedOR()) continue;
262
263 // If we're still here, two at least semi-decent tracks.
264 // Compare them and remove one if necessary.
265 ATH_CHECK(removeOverlapping(tracks.at(i),tracks.at(j)));
266
267 }
268 }
269
270 return StatusCode::SUCCESS;
271}
272
273// V2 comparison: sorts worst-to-best (fewer hits = worse, higher chi2 = worse)
275{
276 // Count real hits in each track
277 int nHits1 = countRealHits_v2(track1);
278 int nHits2 = countRealHits_v2(track2);
279
280 // Sort worst to best: fewer hits is worse, higher chi2 is worse
281 // Return true if track1 should come before track2 (i.e., track1 is worse)
282
283 // First compare number of hits
284 if (nHits1 != nHits2) {
285 return nHits1 < nHits2; // Fewer hits = worse
286 }
287
288 // Same number of hits: compare chi2 (higher is worse)
289 float chi2_1 = track1.getChi2ndof();
290 float chi2_2 = track2.getChi2ndof();
291
292 // Then compare chi2
293 if (std::abs(chi2_1 - chi2_2) > std::numeric_limits<float>::epsilon()) {
294 return chi2_1 > chi2_2; // Higher chi2 = worse
295 }
296
297 // Tie-breaker:
298 // in case of same number of hits and same chi2, use track ID (higher ID = worse)
299 return track1.getTrackID() > track2.getTrackID();
300}
301
303{
304 int nHits = 0;
305 for (const auto& hit : track.getFPGATrackSimHits()) {
306 if (hit.isReal()) nHits++;
307 }
308 return nHits;
309}
310
312{
313 // Use the existing functions from FPGATrackSimHoughFunctions
314 if (m_compareAllHits) {
315 return findNCommonHitsGlobal(track1, track2);
316 } else {
317 return findNCommonHits(track1, track2);
318 }
319}
320
321StatusCode FPGATrackSimOverlapRemovalTool::runOverlapRemoval_v2(std::vector<FPGATrackSimTrack>& tracks)
322{
323 ATH_MSG_DEBUG("Beginning v2 overlap removal on " << tracks.size() << " tracks");
324
325 // First pass: mark tracks with bad chi2
326 for (auto& track : tracks) {
327 if (track.getChi2ndof() > m_minChi2.value()) {
328 track.setPassedOR(0);
329 }
330 }
331
332 // Sort tracks from worst to best quality using v2 comparison
333 // After sorting: index 0 = worst track (fewest hits, highest chi2)
334 // last index = best track (most hits, lowest chi2)
335 std::sort(tracks.begin(), tracks.end(), compareTrackQuality_v2);
336
337 // Process from worst to best
338 // For each track at index i, compare with better tracks (j > i)
339 // If overlap found, mark track i as rejected and move to next
340 for (size_t i = 0; i < tracks.size(); i++) {
341
342 // Skip if already rejected (bad chi2 or previous overlap)
343 if (!tracks[i].passedOR()) continue;
344
345 // Compare with all better tracks (j > i)
346 for (size_t j = i + 1; j < tracks.size(); j++) {
347
348 // Skip if track j already rejected
349 if (!tracks[j].passedOR()) continue;
350
351 // Count overlapping hits
352 int nOverlapping = countOverlappingHits_v2(tracks[i], tracks[j]);
353
354 // Check if tracks overlap based on algorithm
355 bool isOverlap = false;
356 if (m_algo == ORAlgo::Normal) {
357 isOverlap = (nOverlapping >= m_NumOfHitPerGrouping);
358 } else if (m_algo == ORAlgo::InvertGrouping) {
359 // For InvertGrouping: overlap if non-overlapping hits <= threshold
360 int nHits_i = countRealHits_v2(tracks[i]);
361 int nHits_j = countRealHits_v2(tracks[j]);
362 int nonOverlapping = std::min(nHits_i - nOverlapping, nHits_j - nOverlapping);
363 isOverlap = (nonOverlapping <= m_NumOfHitPerGrouping);
364 }
365
366 if (isOverlap) {
367 // Track i overlaps with better track j -> reject track i
368 tracks[i].setPassedOR(0);
369 ATH_MSG_DEBUG("Track " << i << " rejected due to overlap with better track " << j);
370
371 // No need to compare track i with any more tracks
372 break;
373 }
374 }
375 }
376
377 // Count survivors for monitoring
378 int nSurvivors = 0;
379 for (const auto& track : tracks) {
380 if (track.passedOR()) nSurvivors++;
381 }
382 ATH_MSG_DEBUG("V2 OR complete: " << nSurvivors << " tracks surviving out of " << tracks.size());
383
384 return StatusCode::SUCCESS;
385}
386
387
#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)
float getChi2ndof() const
const std::vector< FPGATrackSimHit > & getFPGATrackSimHits() 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.