ATLAS Offline Software
Loading...
Searching...
No Matches
FPGATrackSimNNTrackTool.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3 */
13
19
21FPGATrackSimNNTrackTool::FPGATrackSimNNTrackTool(const std::string &algname, const std::string &name, const IInterface *ifc) : FPGATrackSimTrackingToolBase(algname, name, ifc), OnnxRuntimeBase() {}
22
23
24// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
27 ATH_CHECK(m_tHistSvc.retrieve());
28 if (m_useSpacePoints) ATH_CHECK(m_spRoadFilterTool.retrieve(EnableTool{m_spRoadFilterTool}));
29
30 if (m_FPGATrackSimMapping->getFakeNNMapString() != "") {
31 m_fakeNN_1st.initialize(m_FPGATrackSimMapping->getFakeNNMapString());
32 }
33 else {
34 ATH_MSG_ERROR("Path to 1st stage NN-based fake track removal ONNX file is empty! If you want to run this pipeline, you need to provide an input file.");
35 return StatusCode::FAILURE;
36 }
37
38 if (m_FPGATrackSimMapping->getParamNNMapString() != "") {
39 m_paramNN_1st.initialize(m_FPGATrackSimMapping->getParamNNMapString());
40 }
41 else {
42 ATH_MSG_INFO("Path 1st stage to NN-based track parameter estimation ONNX file is empty! Estimation is not run...");
43 m_useParamNN_1st = false;
44 }
45
46 if (m_FPGATrackSimMapping->getFakeNNMap2ndString() != "") {
47 m_fakeNN_2nd.initialize(m_FPGATrackSimMapping->getFakeNNMap2ndString());
48 }
49 else {
50 ATH_MSG_ERROR("Path to 2nd stage NN-based fake track 1st stage removal ONNX file is empty! If you want to run this pipeline, you need to provide an input file.");
51 return StatusCode::FAILURE;
52 }
53
54 if (m_FPGATrackSimMapping->getParamNNMap2ndString() != "") {
55 m_paramNN_2nd.initialize(m_FPGATrackSimMapping->getParamNNMap2ndString());
56 }
57 else {
58 ATH_MSG_INFO("Path to 2nd stage NN-based track parameter estimation 2nd ONNX file is empty! Estimation is not run...");
59 m_useParamNN_2nd = false;
60 }
61
62 return StatusCode::SUCCESS;
63}
64
65
66StatusCode FPGATrackSimNNTrackTool::setTrackParameters(std::vector<FPGATrackSimTrack> &tracks, bool isFirst, const FPGATrackSimTrackPars& min, const FPGATrackSimTrackPars& max) {
67
68 ATH_MSG_DEBUG("Running NN-based track parameter estimation!");
69 std::vector<float> paramNNoutputs;
70 if (!m_useParamNN_1st && isFirst) return StatusCode::SUCCESS;
71 if (!m_useParamNN_2nd && !isFirst) return StatusCode::SUCCESS;
72
73 for (auto &track : tracks) {
74 if (!track.passedOR()) continue;
75 std::vector<float> inputTensorValues;
76 const auto& hits = track.getFPGATrackSimHitPtrs();
77 bool gotSecondSP = false;
78 float tmp_xf;
79 float tmp_yf;
80 float tmp_zf;
81 float tmp_rf;
82 float tmp_phif;
83
84 for (const auto& hit_ptr : hits) {
85 if (!hit_ptr) {
86 ATH_MSG_ERROR("Null hit pointer in track");
87 return StatusCode::FAILURE;
88 }
89 if (!hit_ptr->isReal()) continue;
90 const auto& hit = *hit_ptr;
91
92 // Need to rotate hits
93 float xf = hit.getX();
94 float yf = hit.getY();
95 float zf = hit.getZ();
96 float rf = std::sqrt(xf*xf+yf*yf);
97 float phif = hit.getGPhi();
98
99 // Get average of values for strip hit pairs
100 // TODO: this needs to be fixed in the future, for this to work for other cases
101 if (hit.isStrip()) {
102 if (hit.getHitType() != HitType::spacepoint) { // this is a strip but not a SP!
103 if (m_useCartesian) {
104 float xf_scaled = (xf) / (getXScale());
105 float yf_scaled = (yf) / (getYScale());
106 float zf_scaled = (zf) / (getZScale());
107
108 // Get average of two hits for strip hits
109 inputTensorValues.push_back(xf_scaled);
110 inputTensorValues.push_back(yf_scaled);
111 inputTensorValues.push_back(zf_scaled);
112
113 }
114 else {
115 float rf_scaled = (rf) / (getRScale());
116 float phif_scaled = (phif) / (getPhiScale());
117 float zf_scaled = (zf) / (getZScale());
118 // Get average of two hits for strip hits
119 inputTensorValues.push_back(rf_scaled);
120 inputTensorValues.push_back(phif_scaled);
121 inputTensorValues.push_back(zf_scaled);
122 }
123 }
124 else if (!gotSecondSP) {
125 tmp_xf = xf;
126 tmp_yf = yf;
127 tmp_zf = zf;
128 tmp_rf = rf;
129 tmp_phif = phif;
130 gotSecondSP = true;
131 }
132 else {
133 gotSecondSP = false;
134
135 if (m_useCartesian) {
136 float xf_scaled = (xf + tmp_xf) / (2.*getXScale());
137 float yf_scaled = (yf + tmp_yf) / (2.*getYScale());
138 float zf_scaled = (zf + tmp_zf) / (2.*getZScale());
139
140 // Get average of two hits for strip hits
141 inputTensorValues.push_back(xf_scaled);
142 inputTensorValues.push_back(yf_scaled);
143 inputTensorValues.push_back(zf_scaled);
144
145 }
146 else {
147 float rf_scaled = (rf+tmp_rf) / (2.*getRScale());
148 float phif_scaled = (phif+tmp_phif) / (2.*getPhiScale());
149 float zf_scaled = (zf + tmp_zf) / (2.*getZScale());
150 // Get average of two hits for strip hits
151 inputTensorValues.push_back(rf_scaled);
152 inputTensorValues.push_back(phif_scaled);
153 inputTensorValues.push_back(zf_scaled);
154 }
155 }
156 }
157 else {
158 if (m_useCartesian) {
159 float xf_scaled = (xf) / (getXScale());
160 float yf_scaled = (yf) / (getYScale());
161 float zf_scaled = (zf) / (getZScale());
162 inputTensorValues.push_back(xf_scaled);
163 inputTensorValues.push_back(yf_scaled);
164 inputTensorValues.push_back(zf_scaled);
165 }
166 else {
167 float rf_scaled = (rf) / (getRScale());
168 float phif_scaled = (phif) / (getPhiScale());
169 float zf_scaled = (zf) / (getZScale());
170 // Get average of two hits for strip hits
171 inputTensorValues.push_back(rf_scaled);
172 inputTensorValues.push_back(phif_scaled);
173 inputTensorValues.push_back(zf_scaled);
174 }
175 }
176 }
177
178 if (isFirst){
179 if (inputTensorValues.size() < 15) {
180 inputTensorValues.resize(15, 0.0f); // Resize to 15 and fill with 0.0f
181 }
182
183 if (m_doGNNTracking) {
184 inputTensorValues.resize(m_nInputsGNN * 3);
185 }
186 }
187 else {
188 if (inputTensorValues.size() < 39) {
189 inputTensorValues.resize(39, 0.0f); // Resize to 39 and fill with 0.0f
190 }
191 else if (inputTensorValues.size() > 39) {
192 inputTensorValues.resize(39); // Resize to 39 and keep the first e9 elements
193 }
194 }
195
196 if (m_doGNNTracking) {
197 inputTensorValues.resize(m_nInputsGNN * 3);
198 }
199
200
201 if (isFirst) paramNNoutputs = m_paramNN_1st.runONNXInference(inputTensorValues);
202 else paramNNoutputs = m_paramNN_2nd.runONNXInference(inputTensorValues);
203
204 ATH_MSG_DEBUG("Estimated Track Parameters");
205 for (unsigned int i = 0; i < paramNNoutputs.size(); i++) {
206 ATH_MSG_DEBUG(paramNNoutputs[i]);
207 }
208
209
210 double qopt = paramNNoutputs[0]*getQoverPtScale();
211 double eta = paramNNoutputs[1]*getEtaScale();
212 double phi = paramNNoutputs[2]*getPhiScale();
213 double d0 = paramNNoutputs[3]*getD0Scale();
214 double z0 = paramNNoutputs[4]*getZ0Scale();
215
216
227
228 track.setQOverPt(qopt);
229 track.setEta(eta);
230 track.setPhi(phi);
231 track.setD0(d0);
232 track.setZ0(z0);
233 }
234 return StatusCode::SUCCESS;
235}
236
237
238StatusCode FPGATrackSimNNTrackTool::getTracks_1st(std::vector<FPGATrackSimRoad> &roads, std::vector<FPGATrackSimTrack> &tracks) {
239
240 if(m_doGNNTracking) {
241 ATH_CHECK(getTracks_GNN(roads, tracks));
242 return StatusCode::SUCCESS;
243 }
244
246 int n_track = 0;
247
248 std::vector<std::vector<float> >inputTensorValuesAll;
249
250 // Loop over roads
251 for (auto const &iroad : roads) {
252
253 double y = iroad.getY();
254
255 // Just used to get number of layers considered
256 const FPGATrackSimPlaneMap *planeMap = m_FPGATrackSimMapping->PlaneMap_1st(iroad.getSubRegion());
257
258 // Get info on layers with missing hits
259 int nMissing = 0;
260 layer_bitmask_t missing_mask = iroad.getNWCLayers();
261 for (unsigned ilayer = 0; ilayer < planeMap->getNLogiLayers(); ilayer++) {
262 if ((missing_mask >> ilayer) & 0x1) {
263 nMissing++;
264 if (planeMap->isPixel(ilayer)) nMissing++;
265 }
266 }
267
268
269 // Create a template track with common parameters filled already for
270 // initializing below
273 temp.setNLayers(planeMap->getNLogiLayers());
274 temp.setBankID(-1);
275 temp.setPatternID(iroad.getPID());
276 temp.setFirstSectorID(iroad.getSector());
277 temp.setHitMap(missing_mask);
278 temp.setNMissing(nMissing);
279 temp.setQOverPt(y);
280
281 temp.setSubRegion(iroad.getSubRegion());
282 temp.setHoughX(iroad.getX());
283 temp.setHoughY(iroad.getY());
284 temp.setHoughXBin(iroad.getXBin());
285 temp.setHoughYBin(iroad.getYBin());
286
287 temp.setBinIdx(iroad.getBinIdx());
288
290 // Get a list of indices for all possible combinations given a certain
291 // number of layers
292 std::vector<std::vector<int>> combs;
293
294 combs = getComboIndices(iroad.getNHits_layer());
295
296 // Loop over possible combinations for this road
297 for (size_t icomb = 0; icomb < combs.size(); icomb++) {
298 std::vector<float> inputTensorValues;
299 std::vector<std::shared_ptr<const FPGATrackSimHit>> hit_list;
300
301 // list of indices for this particular combination
302 std::vector<int> const &hit_indices = combs[icomb];
303
304 // Loop over all layers
305 for (unsigned layer = 0; layer < planeMap->getNLogiLayers(); layer++) {
306
307 // Check to see if this is a valid hit
308 if (hit_indices[layer] >= 0) {
309
310 std::shared_ptr<const FPGATrackSimHit> hit = iroad.getHitPtrs(layer)[hit_indices[layer]];
311 // Add this hit to the road
312 if (hit->isReal()){
313 hit_list.push_back(std::move(hit));
314 }
315 }
316 }
317
318 // Sort the list by radial distance
319 std::sort(hit_list.begin(), hit_list.end(),
320 [](std::shared_ptr<const FPGATrackSimHit> &hit1, std::shared_ptr<const FPGATrackSimHit> &hit2) {
321 double rho1 = std::hypot(hit1->getX(), hit1->getY());
322 double rho2 = std::hypot(hit2->getX(), hit2->getY());
323 return rho1 < rho2;
324 });
325
326
327 int index = 1;
328 bool flipZ = false;
329 double rotateAngle = 0;
330 bool gotSecondSP = false;
331 float tmp_xf;
332 float tmp_yf;
333 float tmp_zf;
334 float tmp_rf;
335 float tmp_phif;
336 // Loop over all hits
337 for (const auto &hit : hit_list) {
338 // Need to rotate hits
339 float x0 = hit->getX();
340 float y0 = hit->getY();
341 float z0 = hit->getZ();
342 float r0 = std::sqrt(x0*x0+y0*y0);
343 float phi0 = hit->getGPhi();
344 float xf = x0;
345 float yf = y0;
346 float zf = z0;
347 float rf = r0;
348 float phif = phi0;
349 if (m_useCartesian) {
350 if (index == 1) {
351 if (z0 < 0)
352 flipZ = true;
353 rotateAngle = std::atan(x0 / y0);
354 if (y0 < 0)
355 rotateAngle += M_PI;
356 }
357 xf = x0 * std::cos(rotateAngle) - y0 * std::sin(rotateAngle);
358 yf = x0 * std::sin(rotateAngle) + y0 * std::cos(rotateAngle);
359 zf = z0;
360 if (flipZ) zf = z0 * -1;
361 }
362
363 // Get average of values for strip hit pairs
364 // TODO: this needs to be fixed in the future, for this to work for other cases
365 if (hit->isStrip()) {
366
367 if (hit->getHitType() != HitType::spacepoint) { // this is a strip but not a SP!
368 if (m_useCartesian) {
369 float xf_scaled = (xf) / (getXScale());
370 float yf_scaled = (yf) / (getYScale());
371 float zf_scaled = (zf) / (getZScale());
372
373 // Get average of two hits for strip hits
374 inputTensorValues.push_back(xf_scaled);
375 inputTensorValues.push_back(yf_scaled);
376 inputTensorValues.push_back(zf_scaled);
377
378 }
379 else {
380 float rf_scaled = (rf) / (getRScale());
381 float phif_scaled = (phif) / (getPhiScale());
382 float zf_scaled = (zf) / (getZScale());
383 // Get average of two hits for strip hits
384 inputTensorValues.push_back(rf_scaled);
385 inputTensorValues.push_back(phif_scaled);
386 inputTensorValues.push_back(zf_scaled);
387 }
388 }
389 else if (!gotSecondSP) {
390 tmp_xf = xf;
391 tmp_yf = yf;
392 tmp_zf = zf;
393 tmp_phif = phif;
394 tmp_rf = rf;
395 gotSecondSP = true;
396 }
397 else {
398 gotSecondSP = false;
399 if (m_useCartesian) {
400 float xf_scaled = (xf + tmp_xf) / (2.*getXScale());
401 float yf_scaled = (yf + tmp_yf) / (2.*getYScale());
402 float zf_scaled = (zf + tmp_zf) / (2.*getZScale());
403
404 // Get average of two hits for strip hits
405 inputTensorValues.push_back(xf_scaled);
406 inputTensorValues.push_back(yf_scaled);
407 inputTensorValues.push_back(zf_scaled);
408 index++;
409 }
410 else {
411 float rf_scaled = (rf + tmp_rf) / (2.*getRScale());
412 float phif_scaled = (phif + tmp_phif) / (2.*getPhiScale());
413 float zf_scaled = (zf + tmp_zf) / (2.*getZScale());
414 inputTensorValues.push_back(rf_scaled);
415 inputTensorValues.push_back(phif_scaled);
416 inputTensorValues.push_back(zf_scaled);
417 }
418 }
419 }
420 else {
421 if (m_useCartesian) {
422 float xf_scaled = (xf) / (getXScale());
423 float yf_scaled = (yf) / (getYScale());
424 float zf_scaled = (zf) / (getZScale());
425 inputTensorValues.push_back(xf_scaled);
426 inputTensorValues.push_back(yf_scaled);
427 inputTensorValues.push_back(zf_scaled);
428 index++;
429 }
430 else {
431 float rf_scaled = (rf) / (getRScale());
432 float phif_scaled = (phif) / (getPhiScale());
433 float zf_scaled = (zf) / (getZScale());
434 inputTensorValues.push_back(rf_scaled);
435 inputTensorValues.push_back(phif_scaled);
436 inputTensorValues.push_back(zf_scaled);
437 }
438 }
439 }
440
441 if (inputTensorValues.size() != planeMap->getNLogiLayers()*3) {
442 inputTensorValues.resize(planeMap->getNLogiLayers()*3);
443 }
444 inputTensorValues.resize(15); // Retain only the first 15 values for consistency
445
446 inputTensorValuesAll.push_back(inputTensorValues);
447 FPGATrackSimTrack track_cand;
448 n_track++;
449 track_cand.setTrackID(n_track);
450 track_cand.setNLayers(planeMap->getNLogiLayers());
451 track_cand.setNMissing(nMissing);
452 for (unsigned ihit = 0; ihit < hit_list.size(); ihit++) {
453 track_cand.setFPGATrackSimHit(ihit, hit_list[ihit]);
454 }
455 tracks.push_back(std::move(track_cand));
456
457 ATH_MSG_DEBUG("NN InputTensorValues:");
458 ATH_MSG_DEBUG(inputTensorValues);
459 } // loop over combinations
460 } // loop over roads
461
463 auto NNoutputs = m_fakeNN_1st.runONNXInference(inputTensorValuesAll);
464
465 for (unsigned itrack = 0; itrack < NNoutputs.size(); itrack++) {
466
467 float nn_val = NNoutputs[itrack][0];
468 ATH_MSG_DEBUG("NN output:" << nn_val);
469 double chi2 = (1 - nn_val) * (tracks[itrack].getNCoords() - tracks[itrack].getNMissing() - 5);
470 //std::cout << "1st stage" << chi2 << std::endl;
471 tracks[itrack].setOrigChi2(chi2);
472 tracks[itrack].setChi2(chi2);
473 }
474
475 // Add truth info
476 for (FPGATrackSimTrack &t : tracks) {
477 compute_truth(t); // match the track to a geant particle using the
478 // channel-level geant info in the hit data.
479 }
480 return StatusCode::SUCCESS;
481}
482
483
484StatusCode FPGATrackSimNNTrackTool::getTracks_2nd(std::vector<FPGATrackSimRoad> &roads, std::vector<FPGATrackSimTrack> &tracks) {
485
487 int n_track = 0;
488 std::vector<std::vector<float> >inputTensorValuesAll;
489
490 // Loop over roads
491 for (auto const &iroad : roads) {
492
493 double y = iroad.getY();
494
495 const FPGATrackSimPlaneMap *planeMap = m_FPGATrackSimMapping->PlaneMap_2nd(iroad.getSubRegion());
496 // Get info on layers with missing hits
497 int nMissing = 0;
498 layer_bitmask_t missing_mask = iroad.getNWCLayers();
499 layer_bitmask_t hit_mask = 0x0;
500 for (unsigned ilayer = 0; ilayer < 13; ilayer++) {
501 if ((missing_mask >> ilayer) & 0x1) {
502 nMissing++;
503 if (planeMap->isPixel(ilayer)) nMissing++;
504 }
505 else {
506 hit_mask |= (0x1 << ilayer);
507 }
508 }
509
510 // Create a template track with common parameters filled already for
511 // initializing below
514 temp.setNLayers(13);
515 temp.setBankID(-1);
516 temp.setPatternID(iroad.getPID());
517 temp.setFirstSectorID(iroad.getSector());
518 temp.setHitMap(hit_mask);
519 temp.setNMissing(nMissing);
520 temp.setQOverPt(y);
521
522 temp.setSubRegion(iroad.getSubRegion());
523 temp.setHoughX(iroad.getX());
524 temp.setHoughY(iroad.getY());
525 temp.setHoughXBin(iroad.getXBin());
526 temp.setHoughYBin(iroad.getYBin());
528 // Get a list of indices for all possible combinations given a certain
529 // number of layers
530 std::vector<std::vector<int>> combs =
531 getComboIndices(iroad.getNHits_layer());
532
533 // Loop over possible combinations for this road
534 for (size_t icomb = 0; icomb < combs.size(); icomb++) {
535 std::vector<float> inputTensorValues;
536
537 // list of indices for this particular combination
538 std::vector<int> const &hit_indices = combs[icomb];
539 std::vector<std::shared_ptr<const FPGATrackSimHit>> hit_list;
540
541 // Loop over all layers
542 for (unsigned layer = 0; layer < 13; layer++) {
543
544 // Check to see if this is a valid hit
545 if (hit_indices[layer] >= 0) {
546
547 std::shared_ptr<const FPGATrackSimHit> hit = iroad.getHitPtrs(layer)[hit_indices[layer]];
548 // Add this hit to the road
549 if (hit->isReal()){
550 hit_list.push_back(std::move(hit));
551 }
552 }
553 }
554
555 // Sort the list by radial distance
556 std::sort(hit_list.begin(), hit_list.end(),
557 [](std::shared_ptr<const FPGATrackSimHit> &hit1, std::shared_ptr<const FPGATrackSimHit> &hit2) {
558 double rho1 = std::hypot(hit1->getX(), hit1->getY());
559 double rho2 = std::hypot(hit2->getX(), hit2->getY());
560 return rho1 < rho2;
561 });
562
563
564 int index = 1;
565 bool flipZ = false;
566 double rotateAngle = 0;
567 bool gotSecondSP = false;
568 float tmp_xf;
569 float tmp_yf;
570 float tmp_zf;
571 float tmp_rf;
572 float tmp_phif;
573 // Loop over all hits
574 for (const auto &hit : hit_list) {
575
576 // Need to rotate hits
577 float x0 = hit->getX();
578 float y0 = hit->getY();
579 float z0 = hit->getZ();
580 float r0 = std::sqrt(x0*x0+y0*y0);
581 float phi0 = hit->getGPhi();
582 float xf = x0;
583 float yf = y0;
584 float zf = z0;
585 float rf = r0;
586 float phif = phi0;
587
588 if (m_useCartesian) {
589 if (index == 1) {
590 if (z0 < 0)
591 flipZ = true;
592 rotateAngle = std::atan(x0 / y0);
593 if (y0 < 0)
594 rotateAngle += M_PI;
595 }
596 xf = x0 * std::cos(rotateAngle) - y0 * std::sin(rotateAngle);
597 yf = x0 * std::sin(rotateAngle) + y0 * std::cos(rotateAngle);
598 zf = z0;
599
600 if (flipZ) zf = z0 * -1;
601 }
602
603 // Get average of values for strip hit pairs
604 // TODO: this needs to be fixed in the future, for this to work for other cases
605 if (hit->isStrip()) {
606
607 if (hit->getHitType() != HitType::spacepoint) { // this is a strip but not a SP!
608 if (m_useCartesian) {
609 float xf_scaled = (xf) / (getXScale());
610 float yf_scaled = (yf) / (getYScale());
611 float zf_scaled = (zf) / (getZScale());
612
613 // Get average of two hits for strip hits
614 inputTensorValues.push_back(xf_scaled);
615 inputTensorValues.push_back(yf_scaled);
616 inputTensorValues.push_back(zf_scaled);
617 }
618 else {
619 float rf_scaled = (rf) / (getRScale());
620 float phif_scaled = (phif) / (getPhiScale());
621 float zf_scaled = (zf) / (getZScale());
622 // Get average of two hits for strip hits
623 inputTensorValues.push_back(rf_scaled);
624 inputTensorValues.push_back(phif_scaled);
625 inputTensorValues.push_back(zf_scaled);
626 }
627 }
628 else if (!gotSecondSP) {
629 tmp_xf = xf;
630 tmp_yf = yf;
631 tmp_zf = zf;
632 tmp_rf = rf;
633 tmp_phif = phif;
634 gotSecondSP = true;
635 }
636 else {
637 gotSecondSP = false;
638 if (m_useCartesian) {
639 float xf_scaled = (xf + tmp_xf) / (2.*getXScale());
640 float yf_scaled = (yf + tmp_yf) / (2.*getYScale());
641 float zf_scaled = (zf + tmp_zf) / (2.*getZScale());
642
643 // Get average of two hits for strip hits
644 inputTensorValues.push_back(xf_scaled);
645 inputTensorValues.push_back(yf_scaled);
646 inputTensorValues.push_back(zf_scaled);
647 index++;
648 }
649 else {
650 float rf_scaled = (rf + tmp_rf) / (2.*getRScale());
651 float phif_scaled = (phif + tmp_phif) / (2.*getPhiScale());
652 float zf_scaled = (zf + tmp_zf) / (2.*getZScale());
653 inputTensorValues.push_back(rf_scaled);
654 inputTensorValues.push_back(phif_scaled);
655 inputTensorValues.push_back(zf_scaled);
656 }
657 }
658 }
659 else {
660 if (m_useCartesian) {
661 float xf_scaled = (xf) / (getXScale());
662 float yf_scaled = (yf) / (getYScale());
663 float zf_scaled = (zf) / (getZScale());
664 inputTensorValues.push_back(xf_scaled);
665 inputTensorValues.push_back(yf_scaled);
666 inputTensorValues.push_back(zf_scaled);
667 index++;
668 }
669 else {
670 float rf_scaled = (rf) / (getRScale());
671 float phif_scaled = (phif) / (getPhiScale());
672 float zf_scaled = (zf) / (getZScale());
673 inputTensorValues.push_back(rf_scaled);
674 inputTensorValues.push_back(phif_scaled);
675 inputTensorValues.push_back(zf_scaled);
676 }
677 }
678 }
679
680
681 if (inputTensorValues.size() < 39) {
682 inputTensorValues.resize(39, 0.0f); // Resize to 39 and fill with 0.0f
683 }
684 else if (inputTensorValues.size() > 39) {
685 inputTensorValues.resize(39); // Resize to 39 and keep the first 39 elements
686 }
687 inputTensorValuesAll.push_back(inputTensorValues);
688
689 ATH_MSG_DEBUG("NN InputTensorValues:");
690 ATH_MSG_DEBUG(inputTensorValues);
691
692 n_track++;
693 FPGATrackSimTrack track_cand;
694 track_cand.setTrackID(n_track);
695 track_cand.setNLayers(13);
696 track_cand.setNMissing(nMissing);
697 for (unsigned ihit = 0; ihit < hit_list.size(); ihit++) {
698 track_cand.setFPGATrackSimHit(ihit, hit_list[ihit]);
699 }
700 tracks.push_back(std::move(track_cand));
701
702 } // loop over combinations
703 } // loop over roads
704
706 auto NNoutputs = m_fakeNN_2nd.runONNXInference(inputTensorValuesAll);
707 for (unsigned itrack = 0; itrack < NNoutputs.size(); itrack++) {
708
709 float nn_val = NNoutputs[itrack][0];
710 ATH_MSG_DEBUG("NN output:" << nn_val);
711
712 double chi2 = (1 - nn_val) * (tracks[itrack].getNCoords() - tracks[itrack].getNMissing() - 5);
713
714 tracks[itrack].setOrigChi2(chi2);
715 tracks[itrack].setChi2(chi2);
716 }
717
718 // Add truth info
719 for (FPGATrackSimTrack &t : tracks) {
720 compute_truth(t); // match the track to a geant particle using the
721 // channel-level geant info in the hit data.
722 }
723
724 return StatusCode::SUCCESS;
725}
726
727StatusCode FPGATrackSimNNTrackTool::getTracks_GNN(std::vector<FPGATrackSimRoad> &roads, std::vector<FPGATrackSimTrack> &tracks) {
728
730 int n_track = 0;
731
732 std::vector<std::vector<float> >inputTensorValuesAll;
733
734 // Loop over roads
735 for (auto const &iroad : roads) {
736 // Just used to get number of layers considered
737 const FPGATrackSimPlaneMap *planeMap = m_FPGATrackSimMapping->PlaneMap_1st(iroad.getSubRegion());
738
739 double y = iroad.getY();
740
741 // Get info on layers with missing hits
742 int nMissing = 0;
743 //if missing_mask is set to zero, then the condition ""(missing_mask >> ilayer)
744 // & 1U" cannot be true.
745 layer_bitmask_t missing_mask = iroad.getNWCLayers();
746 layer_bitmask_t hit_mask = 0x0;
747 for (unsigned ilayer = 0; ilayer < 13; ilayer++) {
748 if ((missing_mask >> ilayer) & 0x1) {
749 nMissing++;
750 if (planeMap->isPixel(ilayer)) nMissing++;
751 }
752 else {
753 hit_mask |= (0x1 << ilayer);
754 }
755 }
756
757 // Create a template track with common parameters filled already for
758 // initializing below
761 temp.setNLayers(planeMap->getNLogiLayers());
762 temp.setBankID(-1);
763 temp.setPatternID(iroad.getPID());
764 temp.setFirstSectorID(iroad.getSector());
765 temp.setHitMap(hit_mask);
766 temp.setNMissing(nMissing);
767 temp.setQOverPt(y);
768
769 temp.setSubRegion(iroad.getSubRegion());
770 temp.setHoughX(iroad.getX());
771 temp.setHoughY(iroad.getY());
772 temp.setHoughXBin(iroad.getXBin());
773 temp.setHoughYBin(iroad.getYBin());
774
776 // Get a list of indices for all possible combinations given a certain
777 // number of layers
778 std::vector<std::vector<int>> combs;
779 std::vector<std::shared_ptr<const FPGATrackSimHit>> all_hits;
780
781 std::vector<std::shared_ptr<const FPGATrackSimHit>> all_pixel_hits;
782 std::vector<std::shared_ptr<const FPGATrackSimHit>> all_strip_hits;
783 size_t pixelCount = 0;
784
785 for (unsigned layer = 0; layer < iroad.getNLayers(); ++layer) {
786 all_hits.insert(all_hits.end(), iroad.getHitPtrs(layer).begin(), iroad.getHitPtrs(layer).end());
787 }
788
789 for (const auto& hit : all_hits) {
790 if(hit->isPixel()) {
791 pixelCount++;
792 }
793 }
794
795 if (pixelCount < 1) continue; // Cannot use a form a track candidate from a road that does not have a pixel hit
796
797 std::vector<float> inputTensorValues;
798 std::vector<std::shared_ptr<const FPGATrackSimHit>> hit_list;
799
800 for (const auto &hit : all_hits) {
801 if (hit->isReal()) {
802 hit_list.push_back(hit);
803 }
804 }
805
806 if (hit_list.size() < m_minNumberOfRealHitsInATrack) continue;
807
808 // Sort the list by radial distance
809 std::sort(hit_list.begin(), hit_list.end(),
810 [](std::shared_ptr<const FPGATrackSimHit> &hit1, std::shared_ptr<const FPGATrackSimHit> &hit2) {
811 double rho1 = std::hypot(hit1->getX(), hit1->getY());
812 double rho2 = std::hypot(hit2->getX(), hit2->getY());
813 return rho1 < rho2;
814 });
815
816
817 int index = 1;
818 bool flipZ = false;
819 double rotateAngle = 0;
820 bool gotSecondSP = false;
821 float tmp_xf;
822 float tmp_yf;
823 float tmp_zf;
824 float tmp_phif;
825 float tmp_rf;
826
827 // Loop over all hits
828 for (const auto &hit : hit_list) {
829 // Need to rotate hits
830 float x0 = hit->getX();
831 float y0 = hit->getY();
832 float z0 = hit->getZ();
833 float r0 = std::sqrt(x0*x0+y0*y0);
834 float phi0 = hit->getGPhi();
835 float xf = x0;
836 float yf = y0;
837 float zf = z0;
838 float rf = r0;
839 float phif = phi0;
840
841 if (m_useCartesian) {
842 if (index == 1) {
843 if (z0 < 0)
844 flipZ = true;
845 rotateAngle = std::atan(x0 / y0);
846 if (y0 < 0)
847 rotateAngle += M_PI;
848 }
849 xf = x0 * std::cos(rotateAngle) - y0 * std::sin(rotateAngle);
850 yf = x0 * std::sin(rotateAngle) + y0 * std::cos(rotateAngle);
851 zf = z0;
852
853 if (flipZ) zf = z0 * -1;
854 }
855
856 // Get average of values for strip hit pairs
857 // TODO: this needs to be fixed in the future, for this to work for other cases
858 if (hit->isStrip()) {
859
860 if (hit->getHitType() != HitType::spacepoint) { // this is a strip but not a SP!
861 if (m_useCartesian) {
862 float xf_scaled = (xf) / (getXScale());
863 float yf_scaled = (yf) / (getYScale());
864 float zf_scaled = (zf) / (getZScale());
865
866 // Get average of two hits for strip hits
867 inputTensorValues.push_back(xf_scaled);
868 inputTensorValues.push_back(yf_scaled);
869 inputTensorValues.push_back(zf_scaled);
870
871 }
872 else {
873 float rf_scaled = (rf) / (getRScale());
874 float phif_scaled = (phif) / (getPhiScale());
875 float zf_scaled = (zf) / (getZScale());
876 // Get average of two hits for strip hits
877 inputTensorValues.push_back(rf_scaled);
878 inputTensorValues.push_back(phif_scaled);
879 inputTensorValues.push_back(zf_scaled);
880 }
881 }
882 else if (!gotSecondSP) {
883 tmp_xf = xf;
884 tmp_yf = yf;
885 tmp_zf = zf;
886 tmp_phif = phif;
887 tmp_rf = rf;
888 gotSecondSP = true;
889 }
890 else {
891 gotSecondSP = false;
892 if (m_useCartesian) {
893 float xf_scaled = (xf + tmp_xf) / (2.*getXScale());
894 float yf_scaled = (yf + tmp_yf) / (2.*getYScale());
895 float zf_scaled = (zf + tmp_zf) / (2.*getZScale());
896
897 // Get average of two hits for strip hits
898 inputTensorValues.push_back(xf_scaled);
899 inputTensorValues.push_back(yf_scaled);
900 inputTensorValues.push_back(zf_scaled);
901 index++;
902 }
903 else {
904 float rf_scaled = (rf + tmp_rf) / (2.*getRScale());
905 float phif_scaled = (phif + tmp_phif) / (2.*getPhiScale());
906 float zf_scaled = (zf + tmp_zf) / (2.*getZScale());
907 inputTensorValues.push_back(rf_scaled);
908 inputTensorValues.push_back(phif_scaled);
909 inputTensorValues.push_back(zf_scaled);
910 }
911 }
912 }
913 else {
914 if (m_useCartesian) {
915 float xf_scaled = (xf) / (getXScale());
916 float yf_scaled = (yf) / (getYScale());
917 float zf_scaled = (zf) / (getZScale());
918 inputTensorValues.push_back(xf_scaled);
919 inputTensorValues.push_back(yf_scaled);
920 inputTensorValues.push_back(zf_scaled);
921 index++;
922 }
923 else {
924 float rf_scaled = (rf) / (getRScale());
925 float phif_scaled = (phif) / (getPhiScale());
926 float zf_scaled = (zf) / (getZScale());
927 inputTensorValues.push_back(rf_scaled);
928 inputTensorValues.push_back(phif_scaled);
929 inputTensorValues.push_back(zf_scaled);
930 }
931 }
932 }
933
934 // NN Estimator can either be 5 or 9 spacepoints as inputs
935 // Let this be decided by m_nInputsGNN
936
937 // NN Estimator needs 9 spacepoints -> 27 inputs
938 // If there are more than 9 spacepoints entered, then it accepts the first 9
939 // If there are less than 9 spacepoints, then it enters no values for it (although I actually probably need to just reject these)
940 inputTensorValues.resize(m_nInputsGNN * 3);
941
942 inputTensorValuesAll.push_back(inputTensorValues);
943 FPGATrackSimTrack track_cand;
944 track_cand.setTrackID(n_track);
945 track_cand.setNLayers(hit_list.size());
946 for (unsigned ihit = 0; ihit < hit_list.size(); ihit++) {
947 track_cand.setFPGATrackSimHit(ihit, hit_list[ihit]);
948 }
949 tracks.push_back(std::move(track_cand));
950
951
952 ATH_MSG_DEBUG("NN InputTensorValues:");
953 ATH_MSG_DEBUG(inputTensorValues);
954 } // loop over roads
955
957 auto NNoutputs = m_fakeNN_1st.runONNXInference(inputTensorValuesAll);
958
959 for (unsigned itrack = 0; itrack < NNoutputs.size(); itrack++) {
960
961 float nn_val = NNoutputs[itrack][0];
962 ATH_MSG_DEBUG("NN output:" << nn_val);
963 double chi2 = (1 - nn_val) * (tracks[itrack].getNCoords() - tracks[itrack].getNMissing() - 5);
964 tracks[itrack].setOrigChi2(chi2);
965 tracks[itrack].setChi2(chi2);
966 }
967
968 // Add truth info
969 for (FPGATrackSimTrack &t : tracks) {
970 compute_truth(t); // match the track to a geant particle using the
971 // channel-level geant info in the hit data.
972 }
973
974 return StatusCode::SUCCESS;
975}
976
977
978
979
980// Borrowed same code from TrackFitter - probably a nicer way to inherit instead
982 std::vector<FPGATrackSimMultiTruth> mtv;
983
984 unsigned nl = (m_do2ndStage ? 13 : 5);
985 const auto& hits = t.getFPGATrackSimHitPtrs();
986 for (unsigned layer = 0; layer < nl; layer++) {
987 if (!(t.getHitMap() & (1 << layer))) continue;
988
989 // Sanity check that we have enough hits.
990 if (layer < hits.size() && hits[layer])
991 mtv.push_back(hits[layer]->getTruth());
992 // adjust weight for hits without (and also with) a truth match, so that
993 // each is counted with the same weight.
994 mtv.back().assign_equal_normalization();
995 }
996
997 FPGATrackSimMultiTruth mt(std::accumulate(mtv.begin(), mtv.end(), FPGATrackSimMultiTruth(),
999 // frac is then the fraction of the total number of hits on the track
1000 // attributed to the barcode.
1001
1004 const bool ok = mt.best(tbarcode, tfrac);
1005 if (ok) {
1006 t.setEventIndex(tbarcode.first);
1007 t.setBarcode(tbarcode.second);
1008 t.setBarcodeFrac(tfrac);
1009 }
1010}
#define M_PI
Scalar eta() const
pseudorapidity method
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_DEBUG(x)
std::vector< std::vector< int > > getComboIndices(std::vector< size_t > const &sizes)
Given a vector of sizes (of arrays), generates a vector of all combinations of indices to index one e...
Map for NN tracking.
Utilize NN score to build track candidates.
uint32_t layer_bitmask_t
#define y
#define min(a, b)
Definition cfImp.cxx:40
#define max(a, b)
Definition cfImp.cxx:41
bool best(FPGATrackSimMultiTruth::Barcode &code, FPGATrackSimMultiTruth::Weight &weight) const
std::pair< unsigned long, unsigned long > Barcode
Gaudi::Property< int > m_nInputsGNN
StatusCode getTracks_GNN(std::vector< FPGATrackSimRoad > &roads, std::vector< FPGATrackSimTrack > &tracks)
StatusCode getTracks_1st(std::vector< FPGATrackSimRoad > &roads, std::vector< FPGATrackSimTrack > &tracks)
OnnxRuntimeBase(TString fileName)
FPGATrackSimNNTrackTool(const std::string &, const std::string &, const IInterface *)
Gaudi::Property< bool > m_doGNNTracking
virtual StatusCode initialize() override
Gaudi::Property< unsigned int > m_minNumberOfRealHitsInATrack
ServiceHandle< IFPGATrackSimMappingSvc > m_FPGATrackSimMapping
StatusCode getTracks_2nd(std::vector< FPGATrackSimRoad > &roads, std::vector< FPGATrackSimTrack > &tracks)
ServiceHandle< ITHistSvc > m_tHistSvc
Gaudi::Property< bool > m_useCartesian
void compute_truth(FPGATrackSimTrack &newtrk) const
StatusCode setTrackParameters(std::vector< FPGATrackSimTrack > &tracks, bool isFirst, const FPGATrackSimTrackPars &min, const FPGATrackSimTrackPars &max)
uint32_t getNLogiLayers() const
bool isPixel(int pl) const
void setTrackStage(TrackStage v)
void setFPGATrackSimHit(unsigned i, std::shared_ptr< const FPGATrackSimHit > hit)
void setNLayers(int)
set the number of layers in the track.
ToolHandle< IFPGATrackSimRoadFilterTool > m_spRoadFilterTool
StatusCode setRoadSectors(std::vector< FPGATrackSimRoad > &roads)
FPGATrackSimTrackingToolBase(const std::string &type, const std::string &name, const IInterface *parent)
double chi2(TH1 *h0, TH1 *h1)
Definition index.py:1
void sort(typename DataModel_detail::iterator< DVL > beg, typename DataModel_detail::iterator< DVL > end)
Specialization of sort for DataVector/List.