ATLAS Offline Software
Loading...
Searching...
No Matches
FPGATrackSimHoughTransformTool.cxx
Go to the documentation of this file.
1// Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
2
9
21
22#include <sstream>
23#include <cmath>
24#include <algorithm>
25
26static inline int quant(double min, double max, unsigned nSteps, double val);
27static inline double unquant(double min, double max, unsigned nSteps, int step);
28template <typename T>
29static inline std::string to_string(const std::vector<T> &v);
30
31
33{
34
35 // Move temp variables over from properties to struct
42
43
44 // Debug
45 ATH_MSG_INFO("Image size: " << m_imageSize_x << " x " << m_imageSize_y);
46 ATH_MSG_INFO("Convolution size: " << m_convSize_x << " x " << m_convSize_y);
47 ATH_MSG_INFO("Convolution: " << to_string(const_cast<std::vector<int>&>(m_conv.value())));
48 ATH_MSG_INFO("Hit Extend: " << to_string(const_cast<std::vector<unsigned>&>(m_hitExtend_x.value())));
49
50 // Retrieve info
53 m_nLayers = m_FPGATrackSimMapping->PlaneMap_1st(0)->getNLogiLayers();
54
55 // Error checking
56 // TODO check bounds are set correctly
57 bool ok = false;
59 ATH_MSG_FATAL("initialize() Image size must be greater than 0");
60 else if (m_conv.size() != m_convSize_x * m_convSize_y)
61 ATH_MSG_FATAL("initialize() Convolution sizes don't match");
62 else if (!m_conv.empty() && (m_convSize_x % 2 == 0 || m_convSize_y % 2 == 0))
63 ATH_MSG_FATAL("initialize() Convolution sizes must be odd");
64 else if (m_hitExtend_x.size() % m_nLayers)
65 ATH_MSG_FATAL("initialize() Hit extentsion list must have size % nLayers");
66 else if (!m_combineLayers.empty() && m_combineLayers.size() != m_nLayers)
67 ATH_MSG_FATAL("initialize() Combine layers list must have size = nLayers");
68 else if (m_threshold.size() % 2 != 1)
69 ATH_MSG_FATAL("initialize() Threshold size must be odd");
70 else if (!m_binScale.empty() && m_binScale.size() != m_nLayers)
71 ATH_MSG_FATAL("initialize() Bin scale list must have size = nLayers");
72 else if (std::any_of(m_binScale.begin(), m_binScale.end(), [&](unsigned i){ return m_imageSize_y % i != 0; }))
73 ATH_MSG_FATAL("initialize() The imagesize is not divisible by scale");
74 else
75 ok = true;
76 if (!ok) return StatusCode::FAILURE;
77
78 // Warnings / corrections
80 {
81 ATH_MSG_WARNING("initialize() localMaxWindowSize requires tracing hits, turning on automatically");
82 m_traceHits = true;
83 }
85 {
86 if (m_useSectors)
87 {
88 ATH_MSG_WARNING("initialize() idealGeoRoads conflicts with useSectors, switching off FPGATrackSim sector matching");
89 m_useSectors = false;
90 }
91 if (!m_traceHits)
92 {
93 ATH_MSG_WARNING("initialize() idealGeoRoads requires tracing hits, turning on automatically");
94 m_traceHits = true;
95 }
96 }
97 if (m_binScale.empty()) m_binScale.value().resize(m_nLayers, 1);
98
99 // Fill convenience variables
102 for (unsigned i = 0; i <= m_imageSize_x; i++)
104 for (unsigned i = 0; i <= m_imageSize_y; i++)
106
107 // Initialize combine layers
108 if (!m_combineLayers.empty())
109 {
110 m_nCombineLayers = *std::max_element(m_combineLayers.begin(), m_combineLayers.end()) + 1;
112 for (unsigned i = 0; i < m_combineLayers.size(); i++)
113 m_combineLayer2D[m_combineLayers[i]].push_back(i);
114 }
115 else
116 {
118 for (unsigned i = 0; i < m_nLayers; i++)
119 m_combineLayer2D.push_back({ i });
120 }
121
122 if (m_houghType == "LowResource"){
123 makeLUT(m_LUT, m_h_rfix, Form("%d-%d", m_imageSize_y.value(), m_imageSize_x.value()));
124 } else if (m_houghType == "Flexible") {
125 double phi0_max_bit_th = (m_parMax.phi * m_phi_coord_max * m_bitwise_phi0_conv) / (m_phi_range);
126 double qApt_max_bit_th = (m_parMax.qOverPt * fpgatracksim::A * m_phi_coord_max * m_r_max_mm * m_bitwise_qApt_conv) / (m_r_max * m_phi_range);
127
128 m_one_r_const_twoexp = std::pow(2, static_cast<int>(2 * std::log2(m_r_max + 1)));
131 m_DBinQApt_bit_int = (qApt_max_bit_th - m_qApt_min_bit) / m_imageSize_y;
132 m_DBinPhi0_bit_int = (phi0_max_bit_th - m_phi0_min_bit) / m_imageSize_x;
133 double phi0_max_bit = m_phi0_min_bit + (m_imageSize_x * m_DBinPhi0_bit_int);
134 double qApt_max_bit = m_qApt_min_bit + (m_imageSize_y * m_DBinQApt_bit_int);
142
143 double DBinQApt_clean = ((qApt_max_bit_th - m_qApt_min_bit) / m_imageSize_y) / m_bitwise_qApt_conv;
144 double DBinPhi0_clean = ((phi0_max_bit_th - m_phi0_min_bit) / m_imageSize_x) / m_bitwise_phi0_conv;
145 m_HT_sel = static_cast<int>(DBinPhi0_clean / DBinQApt_clean);
146 }
147 return StatusCode::SUCCESS;
148}
149
150
151
153// Main Algorithm
154
155StatusCode FPGATrackSimHoughTransformTool::getRoads(const std::vector<std::shared_ptr<const FPGATrackSimHit>> & hits, std::vector<FPGATrackSimRoad> & roads)
156{
157 roads.clear();
158 m_roads.clear();
159
160 m_image = createImage(hits);
161
162 //objects for road merge, all roads go through them, except m_traceHits is disabled
163 std::vector<std::pair<unsigned, unsigned>> roadListXY;
164 std::unordered_set<std::shared_ptr<const FPGATrackSimHit>> merged_image;
166
167 if (!m_conv.empty()) m_image = convolute(m_image);
168
169 for (unsigned y = 0; y < m_imageSize_y; y++){
170 for (unsigned x = 0; x < m_imageSize_x; x++){
171 if (passThreshold(m_image, x, y)){
172 if (m_traceHits){
173 //first road is pushed to the roadList
174 roadListXY.push_back({x, y});
175 }else{
176 addRoad(hits, x, y);
177 }
178 }
179 }
180 }
181
182 for (const auto & roadXY : roadListXY){
183 ATH_MSG_DEBUG("roadList x : " << roadXY.first << ", y : " << roadXY.second);
184 }
185
186 if(!m_traceHits){
187 if(m_roadMerge)
188 ATH_MSG_DEBUG("Trace hits is disabled. Road merge doesn't work with this state at the moment.");
189 }
190
191 if(!m_roadMerge){//in case road marge is disabled, add road as usual
192 for (const auto & roadXY : roadListXY){
193 unsigned x = roadXY.first;
194 unsigned y = roadXY.second;
195 addRoad(m_image(y, x).second, x, y);
196 }
197 }else{
198 if(!roadListXY.empty()){
199 std::vector<std::pair<unsigned, unsigned>> mergedRoadListXY;//merged road liste
200 size_t roadCounter = 0; // the number of roads after road merge
202 //Road marge works as follows
203 //1. Take a road from the input list, add it to the merged road list, and remove it from the input list
204 //2. If any road in the input list is neighbouring to the road in the merged road list, then add it to the merged road list and remove it from the input list
205 //3. Once it reaches the end of the input list, select the second road in the merged road list, and repeat the check with the input list
206 //4. Until it reaches the end of the merged road list, repeat 2.-3.
207 //5. Once it reaches the end of the merged road list, all the hits associated with the road in the merged road list are re-associated with the first road in the merged road list
208 //6. As a representative, only the first road is added as a road
209 //7. Select the next road in the input list, then repeat 2.-6.
210 //8. Repeat until all the roda in the input list are considered
212 while(!roadListXY.empty()){
213 mergedRoadListXY.push_back(roadListXY[0]);
214 roadListXY.erase(roadListXY.begin());
215 for(size_t i = 0; i < mergedRoadListXY.size(); i++){
216 for(int j = 0; j < std::ssize(roadListXY); j++){
217 if(std::abs(static_cast<int>(roadListXY[j].first) - static_cast<int>(mergedRoadListXY[i].first)) < 2 &&
218 std::abs(static_cast<int>(roadListXY[j].second) - static_cast<int>(mergedRoadListXY[i].second)) < 2){
219 mergedRoadListXY.push_back(roadListXY[j]);
220 roadListXY.erase(roadListXY.begin() + j);
221 //note: on first iteration, j=0 so j-- will produce negative number
222 j--;
223 }
224 }
225 }
226 if (!mergedRoadListXY.empty()){
227 ATH_MSG_DEBUG( mergedRoadListXY.size() -1 <<" roads are merged to road(" << mergedRoadListXY[0].first << "," << mergedRoadListXY[0].second << ")");
228 }
229 for (const auto & roadXY : mergedRoadListXY){
230 unsigned x = roadXY.first;
231 unsigned y = roadXY.second;
232 merged_image.insert(m_image(y, x).second.begin(), m_image(y, x).second.end());
233 }
234 addRoad(merged_image, mergedRoadListXY[0].first, mergedRoadListXY[0].second);
235 mergedRoadListXY.clear();
236 merged_image.clear();
237 roadCounter++;
238 }
239 ATH_MSG_DEBUG("There is/are " << roadCounter << " roads after road merge");
240 }
241 }
242
243 roads = std::move(m_roads);
244
245 return StatusCode::SUCCESS;
246}
247
248FPGATrackSimHoughTransformTool::Image FPGATrackSimHoughTransformTool::createLayerImage(std::vector<unsigned> const & layers, const std::vector<std::shared_ptr<const FPGATrackSimHit>> & hits, unsigned const scale) const
249{
251
252 for (const auto& hit : hits) {
253 if (std::find(layers.begin(), layers.end(), hit->getLayer()) == layers.end()) continue;
254
255 if (m_houghType == "LowResource"){
256 //FIXME at the moment, only the barrel is considered. And as the r is mostly the same for two strip layers, those two layers share the LUTs of acceptable input phi values. Here, LUT_layer is used to switch the list of LUTs
257 int LUT_layer = -1;
258 switch (hit->getLayer()){
259 case 0 :
260 LUT_layer = 0;
261 break;
262 case 1 :
263 case 2 :
264 LUT_layer = 1;
265 break;
266 case 3 :
267 case 4 :
268 LUT_layer = 2;
269 break;
270 case 5 :
271 case 6 :
272 LUT_layer = 3;
273 break;
274 case 7 :
275 case 8 :
276 LUT_layer = 4;
277 break;
278 default :
279 ATH_MSG_FATAL("Debug: something wrong! layer: " << hit->getLayer());
280 break;
281 }
282 if (LUT_layer <0){
283 ATH_MSG_ERROR("FPGATrackSimHoughTransformTool: array index is negative");
284 return image;
285 }
286 int phi_L_bin = m_h_rfix.at(LUT_layer)->FindBin(hit->getGPhi());
287 int MSB = (phi_L_bin >> (m_bitlength -6));
288 if(MSB >= 64) MSB = 63;//for barrel, MSB should be up to 63, but this line force it to not crash
289 for(const auto& LUT_i: m_LUT.at(LUT_layer).at(MSB)){//check LUTs only correspoinding MSB
290 if(LUT_i.input_begin <= phi_L_bin && phi_L_bin <= LUT_i.input_end){//If in the range, then fire it
291 for(const auto& pos: LUT_i.output){
292 image(pos.y -1, pos.x -1).first++;//NOTE : pos is 1 start
293 if (m_traceHits) image(pos.y -1, pos.x -1).second.insert(hit);
294 }
295 }
296 }
297 } else if (m_houghType == "Flexible") {
298 double r = hit->getR();
299 int r_bit = static_cast<int>(r * m_r_max / m_r_max_mm);
300 int input_bins_vector_size = 0;
301
302 //Bologna Flexible HT uses qA/Pt=.... or Phi0=.... depending from the axis with more bins (qA/Pt axis #bin > Phi0 then Phi0=..... and viceversa)
303 if (r_bit > m_HT_sel) { //formula for qA/Pt HT
304 input_bins_vector_size = m_imageSize_x;
305 } else { //formula for Phi0 HT
306 input_bins_vector_size = m_imageSize_y;
307 }
308
309 std::vector<std::vector<int>> hitLineQAPtPhi0(input_bins_vector_size);
310 hitLineQAPtPhi0 = lineGenLay(hit);
311
312 for (const std::vector<int>& it_base : hitLineQAPtPhi0) {
313 if (it_base[0] != -1 && it_base[1] != -1) {
314 image(it_base[0] , it_base[1]).first++;
315 if (m_traceHits) {
316 image(it_base[0] , it_base[1]).second.insert(hit);
317 }
318 }
319 }
320 }else{
321 // This scans over y (pT) because that is more efficient in memory, in C.
322 // Unknown if firmware will want to scan over x instead.
323 unsigned new_size_y = m_imageSize_y / scale;
324 for (unsigned y_ = 0; y_ < new_size_y; y_++) {
325 unsigned y_bin_min = scale * y_;
326 unsigned y_bin_max = scale * (y_ + 1);
327
328 // Find the min/max x bins
329 auto xBins = yToXBins(y_bin_min, y_bin_max, hit);
330
331 // Update the image
332 for (unsigned y = y_bin_min; y < y_bin_max; y++){
333 for (unsigned x = xBins.first; x < xBins.second; x++) {
334 image(y, x).first++;
335 if (m_traceHits) image(y, x).second.insert(hit);
336 }
337 }
338 }
339 }
340 }
341
342 return image;
343}
344
345FPGATrackSimHoughTransformTool::Image FPGATrackSimHoughTransformTool::createImage(const std::vector<std::shared_ptr<const FPGATrackSimHit>> & hits) const
346{
348
349 for (unsigned i = 0; i < m_nCombineLayers; i++)
350 {
351 Image layerImage = createLayerImage(m_combineLayer2D[i], hits, m_binScale[i]);
352 for (unsigned x = 0; x < m_imageSize_x; ++x)
353 for (unsigned y = 0; y < m_imageSize_y; ++y)
354 if (layerImage(y, x).first > 0)
355 {
356 image(y, x).first++;
357 image(y, x).second.insert(layerImage(y, x).second.begin(), layerImage(y, x).second.end());
358 }
359 }
360 return image;
361}
362
364{
366
367 for (unsigned y0 = 0; y0 < m_imageSize_y; y0++) // Loop over out
368 for (unsigned x0 = 0; x0 < m_imageSize_x; x0++)
369 for (unsigned r = 0; r < m_convSize_y; r++) // Loop over conv
370 for (unsigned c = 0; c < m_convSize_x; c++) {
371 int y = -static_cast<int>(m_convSize_y) / 2 + r + y0; // Indices of input
372 int x = -static_cast<int>(m_convSize_x) / 2 + c + x0; //
373
374 if (y >= 0 && y < static_cast<int>(m_imageSize_y) && x >= 0 && x < static_cast<int>(m_imageSize_x)) {
375 int val = m_conv[r * m_convSize_x + c] * image(y, x).first;
376 if (val > 0) {
377 out(y0, x0).first += val;
378 out(y0, x0).second.insert(image(y, x).second.begin(), image(y, x).second.end());
379 }
380 }
381 }
382 return out;
383}
384
385bool FPGATrackSimHoughTransformTool::passThreshold(Image const & image, unsigned x, unsigned y) const
386{
387 // Pass window threshold
388 unsigned width = m_threshold.size() / 2;
389 if (x < width || (image.size(1) - x) < width) return false;
390 for (unsigned i = 0; i < m_threshold.size(); i++) {
391 if (image(y, x - width + i).first < m_threshold[i]) return false;
392 }
393
394 // Pass local-maximum check
396 for (int j = -m_localMaxWindowSize; j <= m_localMaxWindowSize; j++) {
397 for (int i = -m_localMaxWindowSize; i <= m_localMaxWindowSize; i++) {
398 if (i == 0 && j == 0) continue;
399 if (y + j < image.size(0) && x + i < image.size(1)) {
400 if (image(y+j, x+i).first > image(y, x).first) return false;
401 if (image(y+j, x+i).first == image(y, x).first) {
402 if (image(y+j, x+i).second.size() > image(y, x).second.size()) return false;
403 if (image(y+j, x+i).second.size() == image(y, x).second.size() && j <= 0 && i <= 0) return false; // favor bottom-left (low phi, low neg q/pt)
404 }
405 }
406 }
407 }
408 }
409 return true;
410}
411
413// Helpers
414
415
416// Quantizes val, given a range [min, max) split into nSteps. Returns the bin below.
417static inline int quant(double min, double max, unsigned nSteps, double val)
418{
419 return static_cast<int>((val - min) / (max - min) * nSteps);
420}
421
422// Returns the lower bound of the bin specified by step
423static inline double unquant(double min, double max, unsigned nSteps, int step)
424{
425 return min + (max - min) * step / nSteps;
426}
427
428template <typename T>
429static inline std::string to_string(const std::vector<T> &v)
430{
431 std::ostringstream oss;
432 oss << "[";
433 if (!v.empty())
434 {
435 std::copy(v.begin(), v.end()-1, std::ostream_iterator<T>(oss, ", "));
436 oss << v.back();
437 }
438 oss << "]";
439 return oss.str();
440}
441
442double FPGATrackSimHoughTransformTool::yToX(double y, const std::shared_ptr<const FPGATrackSimHit> &hit) const
443{
444 double x = 0;
445
447 {
448 double r = hit->getR(); // mm
449 double phi_hit = hit->getGPhi(); // radians
450 double d0 = std::isnan(m_parMin.d0) ? 0 : m_parMin.d0; // mm, assume min = max
451 x = asin(r * fpgatracksim::A * y - d0 / r) + phi_hit;
452
453 if (m_fieldCorrection) x += fieldCorrection(m_EvtSel->getRegionID(), y, r);
454 }
455 else
456 {
457 ATH_MSG_ERROR("yToX() not defined for the current m_par selection");
458 }
459
460 return x;
461}
462
463
464
465std::vector<std::vector<int>> FPGATrackSimHoughTransformTool::lineGenLay(const std::shared_ptr<const FPGATrackSimHit> &hit) const
466{
467 /*
468 Chosen strategy:
469 The Bologna Flexible HT firmware does mathematical operations to implement the HT and utilizes only integer with variable bits range at the necessary minimum to do so.
470 In the firmware some variables and flags are used to select 1)the scale factors the lead the float values to integer values and 2)the bits range of the internal variables.
471 Because to lead the firmware to behave exactly as a "regular" software would have required to many work and resources on the FPGA, it has been chosen to:
472 - make the firmware behave as a HT and adapt the software to the firmware to fully reproduce firmware performance;
473 - change the HT software to properly behave as the firmware in some operations (as the fact that the bits range is not infinite and so on);
474 Strategies to fill the accumulator:
475 - 1 input bin to 1 output bin;
476 - Assuming [Nx, Ny] binning accumulator is used, here the accumulator is separated into M "pipes," which each of them are[Nx/M, Ny] or [Nx, Ny/M]accumulator. In each pipe the line is drawn as described below:
477 - only part of the "line" coming from a hit is drawn using the HT formula, the rest is done "shifting and replacing" the drawn line to the rest of the pipe
478 - the lines drawn in the pipes start each one from a different point, following the monotonouse behavior of the line;
479 - this reduce the amount of multiplication in the firmware but is not "exactly" as it would be in software, that is why it ahs been decided to keep it;
480 - the accumulator in separated in "sectors" (not overlapped) alongside 1 axis. The first sector is filled with the line drawn with the HT formula, the others with the "shift and replace";
481 */
482
483 std::vector<int64_t> bins_x_new;
484 std::vector<int64_t> bins_y_new;
485
486 //Input values of the bins for the HT formula. The value is the center of the bin (in dev code the latter can be changed)
487 int64_t x_offset = m_phi0_min_bit + m_DBinPhi0_bit_int / 2;
488 int64_t y_offset = m_qApt_min_bit + m_DBinQApt_bit_int / 2;
489 for (unsigned i = 0; i < m_imageSize_x; i++) {
490 bins_x_new.push_back(x_offset + m_DBinPhi0_bit_int * i);
491 }
492 for (unsigned i = 0; i < m_imageSize_y; i++) {
493 bins_y_new.push_back(y_offset + m_DBinQApt_bit_int * i);
494 }
495
496
497 std::vector<int64_t> qApt_ht_array;
498 std::vector<int64_t> phi0_ht_array;
499 std::vector<int> zeros = {-1, -1};
500
501 std::vector<std::vector<int>> hitLineQAPtPhi0;
502
503 const unsigned bins_along_phi0_sector = m_phi0Bins_bit / m_phi0_sectors;
504 const unsigned bins_along_qApt_sector = m_qAptBins_bit / m_qApt_sectors;
505
506 for (unsigned i = 0; i < bins_along_phi0_sector; i++) {
507 qApt_ht_array.push_back(0);
508 }
509 for (unsigned i = 0; i < bins_along_qApt_sector; i++) {
510 phi0_ht_array.push_back(0);
511 }
512
513 int64_t phi_conv_offseted = 0;
514 int64_t Delta_qApt_after_first_sector = 0;
515 int64_t qApt_ht = -1;
516 int64_t phi0_ht = -1;
517 int64_t qApt_ht_pre = -1;
518 int64_t phi0_ht_pre = -1;
519 int64_t qApt_ht_sector = 0;
520 int64_t phi0_ht_sector = 0;
521 int64_t Delta_phi0_after_first_sector = 0;
522
523 std::vector<double> d_qApt_ht_array;
524 std::vector<double> d_phi0_ht_array;
525
526 for (unsigned i = 0; i < bins_along_phi0_sector; i++) {
527 d_qApt_ht_array.push_back(0.0);
528 }
529 for (unsigned i = 0; i < bins_along_qApt_sector; i++) {
530 d_phi0_ht_array.push_back(0.0);
531 }
532
533 double d_phi_conv_offseted = 0.0;
534 double d_qApt_ht_sector = 0.0;
535 double d_phi0_ht_sector = 0.0;
536 double d_one_over_r_by_const = 0.0;
537 double d_phi_conv_over_DBinQApt = 0.0;
538 double d_Delta_phi0_after_first_sector = 0.0;
539
541 double r = hit->getR();
542 double phi_hit = hit->getGPhi(); // radians
543 int r_bit = static_cast<int>(r * m_r_max / m_r_max_mm);
544 int64_t phi_bit = phi_hit * m_phi_coord_max / m_phi_range;
545 if (r_bit > m_HT_sel) { //Case for qA/Pt HT formula
546 for (unsigned i = 0; i < m_imageSize_x; i++) {
547 hitLineQAPtPhi0.push_back(zeros);
548 }
549 //formula first sector
550
551 d_one_over_r_by_const = m_one_r_const_twoexp / r_bit;
552 d_phi_conv_over_DBinQApt = phi_bit * m_tot_bitwise_conv / m_DBinQApt_bit_int;
553
554 for (int pp = 0; pp < m_pipes_phi0; pp++) {//loop over pipes pipes alongside phi0 axis
555 unsigned start_phi0_bins_first_sector = static_cast <unsigned>(pp * m_phi0Bins_bit / m_pipes_phi0);
556 unsigned end_phi0_bins_first_sector = m_phi0_bins_first_sector + static_cast <unsigned>(pp * m_phi0Bins_bit / m_pipes_phi0);
557
558 for (unsigned j = start_phi0_bins_first_sector; j < end_phi0_bins_first_sector; j++) {
559 double d_c1 = m_bitwise_qApt_conv * bins_x_new[j] / m_DBinQApt_bit_int;
560 int64_t c2 = (static_cast<int64_t>(d_c1) - static_cast<int64_t>(d_phi_conv_over_DBinQApt)) * static_cast<int64_t>(d_one_over_r_by_const) / m_one_r_const_twoexp_post_conv;
561 int64_t c3 = (static_cast<int64_t>(d_c1) - static_cast<int64_t>(d_phi_conv_over_DBinQApt)) * static_cast<int64_t>(d_one_over_r_by_const);
562
563 double d_c2 = (d_c1 - d_phi_conv_over_DBinQApt) * d_one_over_r_by_const / m_one_r_const_twoexp_post_conv;
564 double d_c3 = (d_c1 - d_phi_conv_over_DBinQApt) * d_one_over_r_by_const;
566
567 if (static_cast<int64_t>(d_c1) <= static_cast<int64_t>(d_phi_conv_over_DBinQApt)) {
568 qApt_ht_pre = c2 - m_qApt_min_post_conv;
569 }else{
570 qApt_ht_pre = 1 + c2 - m_qApt_min_post_conv;
571 }
572 qApt_ht_sector = c3;
573 d_qApt_ht_sector = d_c3;
574 qApt_ht_array[j - start_phi0_bins_first_sector] = qApt_ht_sector;
575 d_qApt_ht_array[j - start_phi0_bins_first_sector] = d_qApt_ht_sector;
576
577 if (static_cast<int64_t>(d_c1) <= static_cast<int64_t>(d_phi_conv_over_DBinQApt) && (d_c2 - m_qApt_min_post_conv - d_c4) >= 0) {
578 qApt_ht = qApt_ht_pre;
579 }
580 if (static_cast<int64_t>(d_c1) > static_cast<int64_t>(d_phi_conv_over_DBinQApt) && 1 + (d_c2 - m_qApt_min_post_conv - d_c4) >= 0) {
581 qApt_ht = qApt_ht_pre;
582 }
583
584 if (qApt_ht >= 0 && qApt_ht <= m_imageSize_y -1) {
585 hitLineQAPtPhi0[j] = {static_cast<int>(qApt_ht), static_cast<int>(j)};
586 }
587 qApt_ht = -1;
588 }
589 if (m_phi0_sectors > 1) { //formula after first sector
590 for (int ps = 0; ps < m_phi0_sectors -1; ps++) {
591 //note: this is integer division, e.g. 3/2 = 1
593
594 Delta_phi0_after_first_sector = d_c6 * static_cast<int64_t>(d_one_over_r_by_const);
595 //want a double here
596 d_Delta_phi0_after_first_sector = static_cast<double>(d_c6) * d_one_over_r_by_const;
597 for (unsigned psi = start_phi0_bins_first_sector; psi < end_phi0_bins_first_sector; psi++) {
598 int64_t c7 = (qApt_ht_array[psi - start_phi0_bins_first_sector] + Delta_phi0_after_first_sector) / m_one_r_const_twoexp_post_conv;
599
601
602 if (-qApt_ht_array[psi - start_phi0_bins_first_sector] < Delta_phi0_after_first_sector) {
603 qApt_ht = 1 + c7 - m_qApt_min_post_conv;
604 }else{
605 qApt_ht = 0 + c7 - m_qApt_min_post_conv;
606 }
607
608 double d_c7 = (d_qApt_ht_array[psi - start_phi0_bins_first_sector] + d_Delta_phi0_after_first_sector) / m_one_r_const_twoexp_post_conv;
609
610 if (qApt_ht >= 0 && qApt_ht <= m_imageSize_y -1 && (1 + d_c7 - d_c9) >= 0 && (0 + d_c7 - d_c9) >= 0 ) {
611
612 hitLineQAPtPhi0[((ps + 1) * m_phi0_bins_first_sector) + psi] = {static_cast<int>(qApt_ht), static_cast<int>(((ps + 1) * m_phi0_bins_first_sector) + psi)};
613 }
614 qApt_ht = -1;
615 }
616 }
617 }
618 }
619 } else { //Case for Phi0 HT formula
620 for (unsigned i = 0; i < m_imageSize_y; i++) {
621 hitLineQAPtPhi0.push_back(zeros);
622 }
623 phi_conv_offseted = (m_bitwise_phi0_conv * phi_bit - static_cast<int64_t>(m_phi0_min_bit)) * m_bitwise_qApt_conv / m_DBinPhi0_bit_int;
624 d_phi_conv_offseted = (m_bitwise_phi0_conv * phi_bit - m_phi0_min_bit) * m_bitwise_qApt_conv / m_DBinPhi0_bit_int;
625
626 for (int pq = 0; pq < m_pipes_qApt; pq++) { //loop over pipes alongside qA/Pt axis
627
628 unsigned start_qApt_bins_first_sector = static_cast <unsigned>(pq * m_qAptBins_bit / m_pipes_qApt);
629 unsigned end_qApt_bins_first_sector = m_qApt_bins_first_sector + static_cast <unsigned>(pq * m_qAptBins_bit / m_pipes_qApt);
630
631 for (unsigned n = start_qApt_bins_first_sector; n < end_qApt_bins_first_sector; n++) {
632 //formula for first sector
633 //note: this is integer division e.g. 3/2 = 1
634 int64_t d2 =m_bitwise_phi0_conv * bins_y_new[n] / m_DBinPhi0_bit_int;
635
636 phi0_ht_sector = phi_conv_offseted + r_bit * d2;
637 phi0_ht_array[n - start_qApt_bins_first_sector] = phi0_ht_sector;
638 //want a double result here
639 d_phi0_ht_sector = d_phi_conv_offseted + r_bit * static_cast<double>(d2);
640 d_phi0_ht_array[n - start_qApt_bins_first_sector] = d_phi0_ht_sector;
641
642 if (phi0_ht_sector >= 0) {
643 phi0_ht_pre = phi0_ht_sector / m_bitwise_qApt_conv;
644 } else {
645 phi0_ht_pre = -1 + phi0_ht_sector / m_bitwise_qApt_conv;
646 }
647
648 if (phi0_ht_sector >= 0 && phi0_ht_pre >= 0 && (phi0_ht_sector >= m_bitwise_qApt_conv - m_imageSize_x)) {
649 phi0_ht = phi0_ht_pre;
650 }
656 if (phi0_ht <= m_imageSize_x - 1 && phi0_ht >= 0){
657 hitLineQAPtPhi0[n] = {static_cast<int>(n), static_cast<int>(phi0_ht)};
658 }
659 phi0_ht = -1;
660 }
661 if (m_qApt_sectors > 1) { //formula after first sector
662 for (int ts = 0; ts < m_qApt_sectors - 1; ts++) {
664 Delta_qApt_after_first_sector = r_bit * d3;
665 for (unsigned tsi = start_qApt_bins_first_sector; tsi < end_qApt_bins_first_sector; tsi++) {
666 phi0_ht = (phi0_ht_array[tsi - start_qApt_bins_first_sector] + Delta_qApt_after_first_sector) / m_bitwise_qApt_conv;
667 double d_phi0_ht = (d_phi0_ht_array[tsi - start_qApt_bins_first_sector] + Delta_qApt_after_first_sector) / m_bitwise_qApt_conv;
668
669 if (phi0_ht <= m_imageSize_x - 1 && phi0_ht >= 0 && d_phi0_ht >= 0 && phi0_ht >= m_bitwise_qApt_conv - m_imageSize_x) {
670 hitLineQAPtPhi0[((ts + 1) * m_qApt_bins_first_sector) + tsi] = {static_cast<int>((ts + 1) * m_qApt_bins_first_sector + tsi), static_cast<int>(phi0_ht)};
671 }
672 phi0_ht = -1;
673 }
674 }
675 }
676 }
677 }
678 } else {
679 ATH_MSG_ERROR("lineGenLay() not defined for the current m_par selection");
680 }
681
682 //return { HitLine, BinInPos };
683 return hitLineQAPtPhi0;
684}
685
686// Find the min/max x bins of the hit's line, in each y bin. Max is exclusive.
687// Note this assumes yToX is monotonic. Returns {0, 0} if hit lies out of bounds.
688std::pair<unsigned, unsigned> FPGATrackSimHoughTransformTool::yToXBins(size_t yBin_min, size_t yBin_max, const std::shared_ptr<const FPGATrackSimHit> & hit) const
689{
690 // Get float values
691 double x_min = yToX(m_bins_y[yBin_min], hit);
692 double x_max = yToX(m_bins_y[yBin_max], hit);
693 if (x_min > x_max) std::swap(x_min, x_max);
694 if (x_max < m_parMin[m_par_x] || x_min > m_parMax[m_par_x])
695 return { 0, 0 }; // out of bounds
696
697 // Get bins
698 int x_bin_min = quant(m_parMin[m_par_x], m_parMax[m_par_x], m_imageSize_x, x_min);
699 int x_bin_max = quant(m_parMin[m_par_x], m_parMax[m_par_x], m_imageSize_x, x_max) + 1; // exclusive
700
701 // Extend bins
702 unsigned extend = getExtension(yBin_min, hit->getLayer());
703 x_bin_min -= extend;
704 x_bin_max += extend;
705
706 // Clamp bins
707 if (x_bin_min < 0) x_bin_min = 0;
708 if (x_bin_max > static_cast<int>(m_imageSize_x)) x_bin_max = m_imageSize_x;
709
710 return { x_bin_min, x_bin_max };
711}
712
713// We allow variable extension based on the size of m_hitExtend_x. See comments below.
714unsigned FPGATrackSimHoughTransformTool::getExtension(unsigned y, unsigned layer) const
715{
716 if (m_hitExtend_x.size() == m_nLayers) return m_hitExtend_x[layer];
717 if (m_hitExtend_x.size() == m_nLayers * 2)
718 {
719 // different extension for low pt vs high pt, split in half but irrespective of sign
720 // first nLayers entries of m_hitExtend_x is for low pt half, rest are for high pt half
722 return m_hitExtend_x[m_nLayers + layer];
723 }
724 return 0;
725}
726
727// Creates a road from hits that pass through the given bin (x, y), and pushes it onto m_roads
728void FPGATrackSimHoughTransformTool::addRoad(const std::vector<std::vector<std::shared_ptr<const FPGATrackSimHit>>> & hits, layer_bitmask_t hitLayers, unsigned x, unsigned y)
729{
730 m_roads.emplace_back();
731 FPGATrackSimRoad & r = m_roads.back();
732
733 r.setRoadID(m_roads.size() - 1);
734 r.setPID(y * m_imageSize_y + x);
735 r.setHits( std::vector<std::vector<std::shared_ptr<const FPGATrackSimHit>>>(hits)); //copy hits
736
737 // We use the y coordinate in matchIdealGeoSectors
738 // and so it needs to be available before setting the sector.
739
740 r.setSubRegion(m_subRegion);
741 r.setX(m_bins_x[x] + m_step_x/2);
742 r.setY(m_bins_y[y] + m_step_y/2);
743 r.setXBin(x);
744 r.setYBin(y);
745 r.setHitLayers(hitLayers);
746 r.setSubRegion(m_subRegion);
747}
748
749
750// Creates a road from hits that pass through the given bin (x, y), and pushes it onto m_roads
751void FPGATrackSimHoughTransformTool::addRoad(const std::unordered_set<std::shared_ptr<const FPGATrackSimHit>> & hits, unsigned x, unsigned y)
752{
753 layer_bitmask_t hitLayers = 0;
754 for (auto const & hit : hits)
755 hitLayers |= 1 << hit->getLayer();
756
757 auto sorted_hits = ::sortByLayer(hits);
758 sorted_hits.resize(m_nLayers); // If no hits in last layer, return from sortByLayer will be too short
759
760 addRoad(sorted_hits, hitLayers, x, y);
761}
762
763// Use this version of addRoad when hit tracing is turned off
764void FPGATrackSimHoughTransformTool::addRoad(const std::vector<std::shared_ptr<const FPGATrackSimHit>> & hits, unsigned x, unsigned y)
765{
766 // Get the road hits
767 std::vector<std::shared_ptr<const FPGATrackSimHit>> road_hits;
768 layer_bitmask_t hitLayers = 0;
769 for (const auto & hit : hits)
770 {
771 // Find the min/max y bins (after scaling)
772 unsigned int y_bin_min = (y / m_binScale[hit->getLayer()]) * m_binScale[hit->getLayer()];
773 unsigned int y_bin_max = y_bin_min + m_binScale[hit->getLayer()];
774
775 // Find the min/max x bins
776 auto xBins = yToXBins(y_bin_min, y_bin_max, hit);
777 if (x >= xBins.first && x < xBins.second)
778 {
779 road_hits.push_back(hit);
780 hitLayers |= 1 << hit->getLayer();
781 }
782 }
783
784 auto sorted_hits = ::sortByLayer(road_hits);
785 sorted_hits.resize(m_nLayers); // If no hits in last layer, return from sortByLayer will be too short
786
787 addRoad(sorted_hits, hitLayers, x, y);
788}
789
790// A pre-calculated list of LUTs is installed. Each LUT represents the range of input phi value to fire one bin (x,y) on a given layer l.
791void FPGATrackSimHoughTransformTool::makeLUT(std::vector<std::vector<std::vector<LUT>>> &v_LUT, std::vector<TH1D*> &v_h, const std::string& tag){
792 const std::string filepath = m_requirements.value() + "requirement-" + tag + ".root";
793 TFile* fin = TFile::Open(filepath.c_str());
794 ATH_MSG_INFO("open: " << tag);
795 m_bitlength = 14;//FIXME length needed to represent input phi. Should be define from requirement file
796 TTree* requirement = fin->Get<TTree>("requirement");
797 v_LUT.resize(5);
798 for(int i=0; i<5; i++){//FIXME as this target only for barrel so far, hardcoded
799 v_LUT.at(i).resize(64);
800 TH1D* h = (TH1D*)fin->Get(Form("in%d",i));//This is 1D hist to convert float phi to bitwise, depending on layer
801 v_h.push_back(h);
802 }
803 int nrequirement = requirement->GetEntries();
804 int in_min;
805 int in_max;
806 int xi;
807 int yi;
808 int ri;
809 requirement->SetBranchAddress("input_begin", &in_min);
810 requirement->SetBranchAddress("input_end", &in_max);
811 requirement->SetBranchAddress("output_x", &xi);
812 requirement->SetBranchAddress("output_y", &yi);
813 requirement->SetBranchAddress("output_l", &ri);
814 for(int i=0; i<nrequirement; i++){
815 requirement->GetEntry(i);
816 //process for min range
817 int MSB = (in_min >> (m_bitlength -6));
818 bool toBeFilled = true;
819 if(!v_LUT.at(ri).at(MSB).empty()){
820 for(auto& LUT_i : v_LUT.at(ri).at(MSB)){// For the sake of process speed and reproducibility of FPGA performance, LUTs are pushed to different vectors depending on their input MSB value.
821 if(LUT_i.input_begin == in_min && LUT_i.input_end == in_max){
822 LUT_i.output.push_back({xi, yi, ri});
823 toBeFilled = false;
824 break;
825 }
826 }
827 }
828 if(toBeFilled){
829 LUT LUT_i;
830 LUT_i.input_begin = in_min;
831 LUT_i.input_end = in_max;
832 LUT_i.layer = ri;
833 LUT_i.output.push_back({xi, yi, ri});
834 v_LUT.at(ri).at(MSB).push_back(std::move(LUT_i));
835 }
836 //process for max range
837 int MSB2 = (in_max >> (m_bitlength -6));
838 if(MSB != MSB2){
839 MSB = MSB2;
840 toBeFilled = true;
841 if(!v_LUT.at(ri).at(MSB).empty()){
842 for(auto& LUT_i : v_LUT.at(ri).at(MSB)){
843 if(LUT_i.input_begin == in_min && LUT_i.input_end == in_max){
844 LUT_i.output.push_back({xi, yi, ri});
845 toBeFilled = false;
846 break;
847 }
848 }
849 }
850 if(toBeFilled){
851 LUT LUT_i;
852 LUT_i.input_begin = in_min;
853 LUT_i.input_end = in_max;
854 LUT_i.layer = ri;
855 LUT_i.output.push_back({xi, yi, ri});
856 v_LUT.at(ri).at(MSB).push_back(std::move(LUT_i));
857 }
858 }
859 }
860 int LUTsize = v_LUT.size();
861 for(int layer = 0; layer < LUTsize; layer++){
862 for(int msb = 0; msb < 64; msb++){
863 ATH_MSG_DEBUG("LUT size L(" << layer << ") msb(" << msb << "): " << v_LUT.at(layer).at(msb).size());
864 }
865 }
866}
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_ERROR(x)
#define ATH_MSG_FATAL(x)
#define ATH_MSG_INFO(x)
#define ATH_MSG_WARNING(x)
#define ATH_MSG_DEBUG(x)
double fieldCorrection(unsigned region, double qoverpt, double r)
static std::string to_string(const std::vector< T > &v)
: FPGATrackSim-specific class to represent an hit in the detector.
std::vector< std::vector< std::shared_ptr< const FPGATrackSimHit > > > sortByLayer(Container const &hits)
static std::string to_string(const std::vector< T > &v)
static int quant(double min, double max, unsigned nSteps, double val)
static double unquant(double min, double max, unsigned nSteps, int step)
Implements road finding using a Hough transform.
Maps physical layers to logical layers.
Maps ITK module indices to FPGATrackSim regions.
This file declares a class that stores the module IDs of the sectors.
uint32_t layer_bitmask_t
const double width
#define y
#define x
#define min(a, b)
Definition cfImp.cxx:40
#define max(a, b)
Definition cfImp.cxx:41
Header file for AthHistogramAlgorithm.
Gaudi::Property< std::vector< int > > m_conv
FPGATrackSimTrackPars::pars_index m_par_x
Image createLayerImage(std::vector< unsigned > const &combine_layers, const std::vector< std::shared_ptr< const FPGATrackSimHit > > &hits, unsigned const scale) const
ServiceHandle< IFPGATrackSimEventSelectionSvc > m_EvtSel
ServiceHandle< IFPGATrackSimBankSvc > m_FPGATrackSimBankSvc
Gaudi::Property< std::vector< int > > m_threshold
Gaudi::Property< std::vector< unsigned > > m_binScale
ServiceHandle< IFPGATrackSimMappingSvc > m_FPGATrackSimMapping
std::pair< unsigned, unsigned > yToXBins(size_t yBin_min, size_t yBin_max, const std::shared_ptr< const FPGATrackSimHit > &hit) const
std::vector< std::vector< std::vector< LUT > > > m_LUT
vector2D< std::pair< int, std::unordered_set< std::shared_ptr< const FPGATrackSimHit > > > > Image
Gaudi::Property< std::vector< unsigned > > m_combineLayers
unsigned getExtension(unsigned y, unsigned layer) const
Gaudi::Property< std::string > m_houghType
std::vector< std::vector< int > > lineGenLay(const std::shared_ptr< const FPGATrackSimHit > &hit) const
double yToX(double y, const std::shared_ptr< const FPGATrackSimHit > &hit) const
void addRoad(const std::vector< std::vector< std::shared_ptr< const FPGATrackSimHit > > > &hits, layer_bitmask_t hitLayers, unsigned x, unsigned y)
Gaudi::Property< std::vector< unsigned > > m_hitExtend_x
std::vector< FPGATrackSimRoad > m_roads
FPGATrackSimTrackPars::pars_index m_par_y
Gaudi::Property< std::string > m_requirements
void makeLUT(std::vector< std::vector< std::vector< LUT > > > &v_LUT, std::vector< TH1D * > &v_h, const std::string &tag="")
virtual StatusCode getRoads(const std::vector< std::shared_ptr< const FPGATrackSimHit > > &hits, std::vector< FPGATrackSimRoad > &roads) override
bool passThreshold(Image const &image, unsigned x, unsigned y) const
std::vector< std::vector< unsigned > > m_combineLayer2D
Image createImage(const std::vector< std::shared_ptr< const FPGATrackSimHit > > &hits) const
int ts
Definition globals.cxx:24
int r
Definition globals.cxx:22
static constexpr double A
void swap(ElementLinkVector< DOBJ > &lhs, ElementLinkVector< DOBJ > &rhs)