ATLAS Offline Software
Loading...
Searching...
No Matches
TrigInDetPattRecoTools/src/SeedingToolBase.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3*/
4
7
9
11
12#include "GNN_TrackingFilter.h"
13
15
16#include "SeedingToolBase.h"
17
18#include "GNN_TrackingFilter.h"
19
20#include <cmath>
21
22#include <algorithm> //for std::sort
23
25 ATH_CHECK(AthAlgTool::initialize());
26
27 ATH_CHECK(m_layerNumberTool.retrieve());
28
29
30 ATH_CHECK(detStore()->retrieve(m_atlasId, "AtlasID"));
31
32 ATH_CHECK(detStore()->retrieve(m_pixelId, "PixelID"));
33
34 ATH_CHECK(detStore()->retrieve(m_sctId, "SCT_ID"));
35
36 std::string conn_fileName = PathResolver::find_file(m_connectionFile, "DATAPATH");
37 if (conn_fileName.empty()) {
38 ATH_MSG_FATAL("Cannot find layer connections file " << conn_fileName);
39 return StatusCode::FAILURE;
40 }
41 else {
42
43 std::ifstream ifs(conn_fileName.c_str());
44
45 m_connector = std::make_unique<GNN_FASTRACK_CONNECTOR>(ifs, m_LRTmode);
46 if (m_etaBinOverride != 0.0f) {
47 m_connector->m_etaBin = m_etaBinOverride;
48 }
49
50 ATH_MSG_INFO("Layer connections are initialized from file " << conn_fileName);
51 }
52
53 const std::vector<TrigInDetSiLayer>* pVL = m_layerNumberTool->layerGeometry();
54
55 std::copy(pVL->begin(),pVL->end(), std::back_inserter(m_layerGeometry));
56
57 m_geo = std::make_unique<TrigFTF_GNN_Geometry>(m_layerGeometry, m_connector);
58
59
60 if (m_useML) {
61 std::string lut_fileName = PathResolver::find_file(m_lutFile, "DATAPATH");
62 if (lut_fileName.empty()) {
63 ATH_MSG_FATAL("Cannot find ML predictor LUT file " << lut_fileName);
64 return StatusCode::FAILURE;
65 }
66 else {
67 m_mlLUT.reserve(100);
68 std::ifstream ifs(lut_fileName.c_str());
69 while (!ifs.eof()) {
70 float cl_width, min1, max1, min2, max2;
71 ifs >> cl_width >> min1 >> max1 >> min2 >> max2;
72 if (ifs.eof()) break;
73 std::array<float, 5> lut_line = {cl_width, min1, max1, min2, max2};
74 m_mlLUT.emplace_back(lut_line);
75 }
76 ifs.close();
77 ATH_MSG_INFO("ML predictor is initialized from file " << lut_fileName<<" LUT has "<<m_mlLUT.size()<<" entries");
78 }
79 }
80
82
83 ATH_MSG_INFO("SeedingToolBase initialized ");
84
85 ATH_MSG_DEBUG("Property useML "<< m_useML);
86 ATH_MSG_DEBUG("Property DoPhiFiltering "<<m_filter_phi);
87 ATH_MSG_DEBUG("Property pTmin "<<m_minPt);
88 ATH_MSG_DEBUG("Property LRTmode "<<m_LRTmode);
89
90 return StatusCode::SUCCESS;
91}
92
94 StatusCode sc = AthAlgTool::finalize();
95 return sc;
96}
97
98std::pair<int, int> SeedingToolBase::buildTheGraph(const IRoiDescriptor& roi, const std::unique_ptr<TrigFTF_GNN_DataStorage>& storage, std::vector<TrigFTF_GNN_Edge>& edgeStorage) const {
99
100
101 struct GBTS_SlidingWindow {
102
103 GBTS_SlidingWindow() : m_first_it(0), m_deltaPhi(0.0), m_has_nodes(false), m_bin(nullptr) {};
104
105 unsigned int m_first_it;// sliding window position
106 float m_deltaPhi; // window half-width;
107 bool m_has_nodes; // active or not
108
109 const TrigFTF_GNN_EtaBin* m_bin;//associated eta bin
110 };
111
112 constexpr float M_2PI = 2.0*M_PI;
113
114 const float cut_dphi_max = m_LRTmode ? 0.07f : 0.012f;
115 const float cut_dcurv_max = m_LRTmode ? 0.015f : 0.001f;
116 const float cut_tau_ratio_max = m_LRTmode ? 0.015f : static_cast<float>(m_tau_ratio_cut);
117 const float min_z0 = m_LRTmode ? -600.0 : roi.zedMinus();
118 const float max_z0 = m_LRTmode ? 600.0 : roi.zedPlus();
119 const float min_deltaPhi = m_LRTmode ? 0.01f : 0.001f;
120 const float tau_ratio_precut = 0.009f;
121
122 const float maxOuterRadius = m_LRTmode ? 1050.0 : 550.0;
123
124 const float cut_zMinU = min_z0 + maxOuterRadius*roi.dzdrMinus();
125 const float cut_zMaxU = max_z0 + maxOuterRadius*roi.dzdrPlus();
126
127 constexpr float ptCoeff = 0.29997*1.9972/2.0;// ~0.3*B/2 - assuming nominal field of 2*T
128
129 float tripletPtMin = 0.8f*m_minPt;//correction due to limited pT resolution
130 const float pt_scale = 900.0f/m_minPt;//to re-scale original tunings done for the 900 MeV pT cut
131
132 float maxCurv = ptCoeff/tripletPtMin;
133
134 float maxKappa_high_eta = m_LRTmode ? 1.0f*maxCurv : std::sqrt(0.8f)*maxCurv;
135 float maxKappa_low_eta = m_LRTmode ? 1.0f*maxCurv : std::sqrt(0.6f)*maxCurv;
136
137 if(!m_useOldTunings && !m_LRTmode) {//new settings for curvature cuts
138 maxKappa_high_eta = 4.75e-4f*pt_scale;
139 maxKappa_low_eta = 3.75e-4f*pt_scale;
140 }
141
142 const float dphi_coeff = m_LRTmode ? 1.0f*maxCurv : 0.68f*maxCurv;
143
144 const float minDeltaRadius = 2.0;
145
146 float deltaPhi0 = 0.5f*m_phiSliceWidth;//the default sliding window along phi
147
148 unsigned int nConnections = 0;
149
150 edgeStorage.reserve(m_nMaxEdges);
151
152 int nEdges = 0;
153
154 for(const auto& bg : m_geo->bin_groups()) {//loop over bin groups
155
156 TrigFTF_GNN_EtaBin& B1 = storage->getEtaBin(bg.first);
157
158 if(B1.empty()) continue;
159
160 float rb1 = B1.getMinBinRadius();
161
162 const unsigned int lk1 = B1.m_layerKey;
163
164 //prepare a sliding window for each bin2 in the group
165
166 std::vector<GBTS_SlidingWindow> vSLW;
167
168 vSLW.resize(bg.second.size());//initialization using default ctor
169
170 int win_idx = 0;
171
172 for(const auto& b2_idx : bg.second) { //loop over n2 eta-bins in L2 layers
173
174 const TrigFTF_GNN_EtaBin& B2 = storage->getEtaBin(b2_idx);
175
176 if(B2.empty()) {
177 win_idx++;
178 continue;
179 }
180
181 float rb2 = B2.getMaxBinRadius();
182
183 float deltaPhi = deltaPhi0;//the default
184
185 if(m_useEtaBinning) { //override the default window width
186 float abs_dr = std::fabs(rb2-rb1);
187 if (m_useOldTunings) {
188 deltaPhi = min_deltaPhi + dphi_coeff*abs_dr;
189 }
190 else {
191 if(abs_dr < 60.0) {
192 deltaPhi = 0.002f + 4.33e-4f*pt_scale*abs_dr;
193 } else {
194 deltaPhi = 0.015f + 2.2e-4f*pt_scale*abs_dr;
195 }
196 }
197 }
198
199 vSLW[win_idx].m_bin = &B2;
200 vSLW[win_idx].m_has_nodes = true;
201 vSLW[win_idx].m_deltaPhi = deltaPhi;
202 win_idx++;
203 }
204
205 for(unsigned int n1Idx = 0;n1Idx<B1.m_vn.size();n1Idx++) {//in GBTSv3 the outer loop goes over n1 nodes in the Layer 1 bin
206
207 B1.m_vFirstEdge[n1Idx] = nEdges;//initialization using the top watermark of the edge storage
208
209 unsigned short num_created_edges = 0;//the counter for the incoming graph edges created for n1
210
211 bool is_connected = false;
212
213 const std::array<float, 5>& n1pars = B1.m_params[n1Idx];
214
215 float phi1 = n1pars[2];
216 float r1 = n1pars[3];
217 float z1 = n1pars[4];
218
219 for(unsigned int winIdx = 0; winIdx < vSLW.size(); winIdx++) {//the intermediate loop over sliding windows
220
221 GBTS_SlidingWindow& slw = vSLW[winIdx];
222
223 if (!slw.m_has_nodes) continue;
224
225 const TrigFTF_GNN_EtaBin& B2 = *slw.m_bin;
226
227 float deltaPhi = slw.m_deltaPhi;
228
229 //sliding window phi1 +/- deltaPhi
230
231 float minPhi = phi1 - deltaPhi;
232 float maxPhi = phi1 + deltaPhi;
233
234 for(unsigned int n2PhiIdx = slw.m_first_it; n2PhiIdx<B2.m_vPhiNodes.size();n2PhiIdx++) {//the inner loop over n2 nodes using sliding window
235
236 float phi2 = B2.m_vPhiNodes[n2PhiIdx].first;
237
238 if(phi2 < minPhi) {
239 slw.m_first_it = n2PhiIdx; //update the window position
240 continue;
241 }
242 if(phi2 > maxPhi) break; //break and go to the next window
243
244 unsigned int n2Idx = B2.m_vPhiNodes[n2PhiIdx].second;
245
246 if ((lk1 == 80000) && (B2.m_vIsConnected[n2Idx] == 0) ) continue;//skip isolated nodes as their incoming edges lead to nowhere
247
248 unsigned int n2_first_edge = B2.m_vFirstEdge[n2Idx];
249 unsigned short n2_num_edges = B2.m_vNumEdges[n2Idx];
250 unsigned int n2_last_edge = n2_first_edge + n2_num_edges;
251
252 const std::array<float, 5>& n2pars = B2.m_params[n2Idx];
253
254 float r2 = n2pars[3];
255
256 float dr = r2 - r1;
257
258 if(dr < minDeltaRadius) {
259 continue;
260 }
261
262 float z2 = n2pars[4];
263
264 float dz = z2 - z1;
265 float tau = dz/dr;
266 float ftau = std::fabs(tau);
267 if (ftau > 36.0) {
268 continue;
269 }
270
271 if(ftau < n1pars[0]) continue;
272 if(ftau > n1pars[1]) continue;
273
274 if(ftau < n2pars[0]) continue;
275 if(ftau > n2pars[1]) continue;
276
277 if (m_doubletFilterRZ) {
278
279 float z0 = z1 - r1*tau;
280
281 if(z0 < min_z0 || z0 > max_z0) continue;
282
283 float zouter = z0 + maxOuterRadius*tau;
284
285 if(zouter < cut_zMinU || zouter > cut_zMaxU) continue;
286 }
287
288 float curv = (phi2-phi1)/dr;
289 float abs_curv = std::abs(curv);
290
291 if(ftau < 4.0) {//eta = 2.1
292 if(abs_curv > maxKappa_low_eta) {
293 continue;
294 }
295 }
296 else {
297 if(abs_curv > maxKappa_high_eta) {
298 continue;
299 }
300 }
301
302 float exp_eta = std::sqrt(1.f+tau*tau) - tau;
303
304 if (m_matchBeforeCreate && (lk1 == 80000 || lk1 == 81000) ) {//match edge candidate against edges incoming to n2
305
306 bool isGood = n2_num_edges <= 2;//we must have enough incoming edges to decide
307
308 if(!isGood) {
309
310 float uat_1 = 1.0f/exp_eta;
311
312 for(unsigned int n2_in_idx = n2_first_edge; n2_in_idx < n2_last_edge; n2_in_idx++) {
313
314 float tau2 = edgeStorage.at(n2_in_idx).m_p[0];
315 float tau_ratio = tau2*uat_1 - 1.0f;
316
317 if(std::fabs(tau_ratio) > tau_ratio_precut){//bad match
318 continue;
319 }
320 isGood = true;//good match found
321 break;
322 }
323 }
324
325 if(!isGood) {//no match found, skip creating [n1 <- n2] edge
326 continue;
327 }
328 }
329
330 float dPhi2 = curv*r2;
331 float dPhi1 = curv*r1;
332
333 if(nEdges < m_nMaxEdges) {
334
335 edgeStorage.emplace_back(B1.m_vn[n1Idx], B2.m_vn[n2Idx], exp_eta, curv, phi1 + dPhi1);
336
337 num_created_edges++;
338
339 int outEdgeIdx = nEdges;
340
341 float uat_2 = 1.f/exp_eta;
342 float Phi2 = phi2 + dPhi2;
343 float curv2 = curv;
344
345 for(unsigned int inEdgeIdx = n2_first_edge; inEdgeIdx < n2_last_edge; inEdgeIdx++) {//looking for neighbours of the new edge
346
347 TrigFTF_GNN_Edge* pS = &(edgeStorage.at(inEdgeIdx));
348
349 if(pS->m_nNei >= N_SEG_CONNS) continue;
350
351 float tau_ratio = pS->m_p[0]*uat_2 - 1.0f;
352
353 if(std::abs(tau_ratio) > cut_tau_ratio_max){//bad match
354 continue;
355 }
356
357 float dPhi = Phi2 - pS->m_p[2];
358
359 if(dPhi<-M_PI) dPhi += M_2PI;
360 else if(dPhi>M_PI) dPhi -= M_2PI;
361
362 if(std::abs(dPhi) > cut_dphi_max) {
363 continue;
364 }
365
366 float dcurv = curv2 - pS->m_p[1];
367
368 if(dcurv < -cut_dcurv_max || dcurv > cut_dcurv_max) {
369 continue;
370 }
371
372 pS->m_vNei[pS->m_nNei++] = outEdgeIdx;
373
374 is_connected = true;//there is at least one good match
375
376 nConnections++;
377
378 }
379 nEdges++;
380 }
381 } //loop over n2 (outer) nodes inside a sliding window on n2 bin
382 } //loop over sliding windows associated with n2 bins
383
384 //updating the n1 node attributes
385
386 B1.m_vNumEdges[n1Idx] = num_created_edges;
387 if (is_connected) {
388 B1.m_vIsConnected[n1Idx] = 1;
389 }
390
391 } //loop over n1 (inner) nodes
392 } //loop over bin groups: a single n1 bin and multiple n2 bins
393
394 if(nEdges >= m_nMaxEdges) {
395 ATH_MSG_WARNING("Maximum number of graph edges exceeded - possible efficiency loss "<< nEdges);
396 }
397
398 return std::make_pair(nEdges, nConnections);
399}
400
401int SeedingToolBase::runCCA(int nEdges, std::vector<TrigFTF_GNN_Edge>& edgeStorage) const {
402
403 constexpr int maxIter = 15;
404
405 int maxLevel = 0;
406
407 int iter = 0;
408
409 std::vector<TrigFTF_GNN_Edge*> v_old;
410
411 for(int edgeIndex=0;edgeIndex<nEdges;edgeIndex++) {
412
413 TrigFTF_GNN_Edge* pS = &(edgeStorage[edgeIndex]);
414 if(pS->m_nNei == 0) continue;
415
416 v_old.push_back(pS);//TO-DO: increment level for segments as they already have at least one neighbour
417 }
418
419 std::vector<TrigFTF_GNN_Edge*> v_new;
420 v_new.reserve(v_old.size());
421
422 for(;iter<maxIter;iter++) {
423
424 //generate proposals
425 v_new.clear();
426
427 for(auto pS : v_old) {
428
429 int next_level = pS->m_level;
430
431 for(int nIdx=0;nIdx<pS->m_nNei;nIdx++) {
432
433 unsigned int nextEdgeIdx = pS->m_vNei[nIdx];
434
435 TrigFTF_GNN_Edge* pN = &(edgeStorage[nextEdgeIdx]);
436
437 if(pS->m_level == pN->m_level) {
438 next_level = pS->m_level + 1;
439 v_new.push_back(pS);
440 break;
441 }
442 }
443
444 pS->m_next = next_level;//proposal
445 }
446
447 //update
448
449 int nChanges = 0;
450
451 for(auto pS : v_new) {
452 if(pS->m_next != pS->m_level) {
453 nChanges++;
454 pS->m_level = pS->m_next;
455 if(maxLevel < pS->m_level) maxLevel = pS->m_level;
456 }
457 }
458
459 if(nChanges == 0) break;
460
461
462 v_old.swap(v_new);
463 v_new.clear();
464 }
465
466 return maxLevel;
467}
468
469void SeedingToolBase::extractSeedsFromTheGraph(int maxLevel, int nEdges, int nHits, std::vector<GNN_Edge>& edgeStorage, std::vector<std::tuple<float, int, std::vector<unsigned int> > >& vSeedCandidates) const {
470
471 const float edge_mask_min_eta = 1.5;
472 const float hit_share_threshold = 0.49;
473
474 vSeedCandidates.clear();
475
476 int minLevel = 3;//a triplet + 2 confirmation
477
478 if(m_LRTmode) {
479 minLevel = 2;//a triplet + 1 confirmation
480 }
481
482 if(maxLevel < minLevel) return;
483
484 std::vector<GNN_Edge*> vSeeds;
485
486 vSeeds.reserve(nEdges/2);
487
488 for(int edgeIndex = 0; edgeIndex < nEdges; edgeIndex++) {
489
490 GNN_Edge* pS = &(edgeStorage.at(edgeIndex));
491
492 if(pS->m_level < minLevel) continue;
493
494 vSeeds.push_back(pS);
495 }
496
497 if(vSeeds.empty()) return;
498
499 std::sort(vSeeds.begin(), vSeeds.end(), GNN_Edge::CompareLevel());
500
501 //backtracking
502
503 vSeedCandidates.reserve(vSeeds.size());
504
505 auto tFilter = std::make_unique<TrigFTF_GNN_TrackingFilter>(m_layerGeometry, edgeStorage);
506
507 for(auto pS : vSeeds) {
508
509 if(pS->m_level == -1) continue;
510
512
513 tFilter->followTrack(pS, rs);
514
515 if(!rs.m_initialized) {
516 continue;
517 }
518
519 if(static_cast<int>(rs.m_vs.size()) < minLevel) continue;
520
521 float seed_eta = std::abs(-std::log(pS->m_p[0]));
522
523 std::vector<const GNN_Node*> vN;
524
525 for(std::vector<GNN_Edge*>::reverse_iterator sIt=rs.m_vs.rbegin();sIt!=rs.m_vs.rend();++sIt) {
526
527 if (seed_eta > edge_mask_min_eta) {
528 (*sIt)->m_level = -1;//mark as collected
529 }
530
531 if(sIt == rs.m_vs.rbegin()) {
532 vN.push_back((*sIt)->m_n1);
533 }
534
535 vN.push_back((*sIt)->m_n2);
536
537 }
538
539 if(vN.size()<3) continue;
540
541 std::vector<unsigned int> vSpIdx;
542
543 vSpIdx.resize(vN.size());
544
545 for(unsigned int k = 0; k < vN.size(); k++) {
546 vSpIdx[k] = vN[k]->sp_idx();
547 }
548
549 vSeedCandidates.emplace_back(-rs.m_J/vN.size(), 0, vSpIdx);
550
551 }
552
553 //clone removal code goes below ...
554
555 std::sort(vSeedCandidates.begin(), vSeedCandidates.end());
556
557 std::vector<int> H2T(nHits + 1, 0);//hit to track associations
558
559 int trackId = 0;
560
561 for(const auto& seed : vSeedCandidates) {
562
563 trackId++;
564
565 for(const auto& h : std::get<2>(seed) ) {//loop over spacepoints indices
566
567 unsigned int hit_id = h + 1;
568
569 int tid = H2T[hit_id];
570
571 if(tid == 0 || tid > trackId) {//unused hit or used by a lesser track
572
573 H2T[hit_id] = trackId;//overwrite
574
575 }
576 }
577 }
578
579 for(unsigned int trackIdx = 0; trackIdx < vSeedCandidates.size(); trackIdx++) {
580
581 int nTotal = std::get<2>(vSeedCandidates[trackIdx]).size();
582 int nOther = 0;
583
584 int trackId = trackIdx + 1;
585
586 for(const auto& h : std::get<2>(vSeedCandidates[trackIdx]) ) {
587
588 unsigned int hit_id = h + 1;
589
590 int tid = H2T[hit_id];
591
592 if(tid != trackId) {//taken by a better candidate
593 nOther++;
594 }
595 }
596
597 if (nOther > hit_share_threshold*nTotal) {
598 std::get<1>(vSeedCandidates[trackIdx]) = -1;//reject
599 }
600
601 }
602}
#define M_PI
Scalar deltaPhi(const MatrixBase< Derived > &vec) const
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_FATAL(x)
#define ATH_MSG_INFO(x)
#define ATH_MSG_WARNING(x)
#define ATH_MSG_DEBUG(x)
This class provides an interface to generate or decode an identifier for the upper levels of the dete...
#define M_2PI
#define N_SEG_CONNS
static Double_t rs
static Double_t sc
static const uint32_t nHits
This is an Identifier helper class for the Pixel subdetector.
This is an Identifier helper class for the SCT subdetector.
const ServiceHandle< StoreGateSvc > & detStore() const
Header file for AthHistogramAlgorithm.
Describes the API of the Region of Ineterest geometry.
virtual double zedPlus() const =0
the zed and eta values at the most forward and most rear ends of the RoI
virtual double dzdrMinus() const =0
return the gradients
virtual double zedMinus() const =0
virtual double dzdrPlus() const =0
static std::string find_file(const std::string &logical_file_name, const std::string &search_path)
int runCCA(int, std::vector< GNN_Edge > &) const
ToolHandle< ITrigL2LayerNumberTool > m_layerNumberTool
std::vector< std::array< float, 5 > > m_mlLUT
std::vector< TrigInDetSiLayer > m_layerGeometry
std::pair< int, int > buildTheGraph(const IRoiDescriptor &, const std::unique_ptr< GNN_DataStorage > &, std::vector< GNN_Edge > &) const
std::unique_ptr< GNN_FasTrackConnector > m_connector
void extractSeedsFromTheGraph(int, int, int, std::vector< GNN_Edge > &, std::vector< std::tuple< float, int, std::vector< unsigned int > > > &) const
std::unique_ptr< const TrigFTF_GNN_Geometry > m_geo
unsigned int m_vNei[N_SEG_CONNS]
unsigned char m_nNei
std::vector< const TrigFTF_GNN_Node * > m_vn
float getMinBinRadius() const
std::vector< unsigned short > m_vIsConnected
float getMaxBinRadius() const
std::vector< unsigned int > m_vFirstEdge
std::vector< std::pair< float, unsigned int > > m_vPhiNodes
std::vector< std::array< float, 5 > > m_params
unsigned int m_layerKey
std::vector< unsigned short > m_vNumEdges
void sort(typename DataModel_detail::iterator< DVL > beg, typename DataModel_detail::iterator< DVL > end)
Specialization of sort for DataVector/List.