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<std::shared_ptr<const 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.reserve(m_roads.size());
244 for (FPGATrackSimRoad & r : m_roads) roads.emplace_back(std::make_shared<const FPGATrackSimRoad>(r));
245
246 return StatusCode::SUCCESS;
247}
248
249FPGATrackSimHoughTransformTool::Image FPGATrackSimHoughTransformTool::createLayerImage(std::vector<unsigned> const & layers, const std::vector<std::shared_ptr<const FPGATrackSimHit>> & hits, unsigned const scale) const
250{
252
253 for (const auto& hit : hits) {
254 if (std::find(layers.begin(), layers.end(), hit->getLayer()) == layers.end()) continue;
255
256 if (m_houghType == "LowResource"){
257 //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
258 int LUT_layer = -1;
259 switch (hit->getLayer()){
260 case 0 :
261 LUT_layer = 0;
262 break;
263 case 1 :
264 case 2 :
265 LUT_layer = 1;
266 break;
267 case 3 :
268 case 4 :
269 LUT_layer = 2;
270 break;
271 case 5 :
272 case 6 :
273 LUT_layer = 3;
274 break;
275 case 7 :
276 case 8 :
277 LUT_layer = 4;
278 break;
279 default :
280 ATH_MSG_FATAL("Debug: something wrong! layer: " << hit->getLayer());
281 break;
282 }
283 if (LUT_layer <0){
284 ATH_MSG_ERROR("FPGATrackSimHoughTransformTool: array index is negative");
285 return image;
286 }
287 int phi_L_bin = m_h_rfix.at(LUT_layer)->FindBin(hit->getGPhi());
288 int MSB = (phi_L_bin >> (m_bitlength -6));
289 if(MSB >= 64) MSB = 63;//for barrel, MSB should be up to 63, but this line force it to not crash
290 for(const auto& LUT_i: m_LUT.at(LUT_layer).at(MSB)){//check LUTs only correspoinding MSB
291 if(LUT_i.input_begin <= phi_L_bin && phi_L_bin <= LUT_i.input_end){//If in the range, then fire it
292 for(const auto& pos: LUT_i.output){
293 image(pos.y -1, pos.x -1).first++;//NOTE : pos is 1 start
294 if (m_traceHits) image(pos.y -1, pos.x -1).second.insert(hit);
295 }
296 }
297 }
298 } else if (m_houghType == "Flexible") {
299 double r = hit->getR();
300 int r_bit = static_cast<int>(r * m_r_max / m_r_max_mm);
301 int input_bins_vector_size = 0;
302
303 //Bologna Flexible HT uses qA/Pt=.... or Phi0=.... depending from the axis with more bins (qA/Pt axis #bin > Phi0 then Phi0=..... and viceversa)
304 if (r_bit > m_HT_sel) { //formula for qA/Pt HT
305 input_bins_vector_size = m_imageSize_x;
306 } else { //formula for Phi0 HT
307 input_bins_vector_size = m_imageSize_y;
308 }
309
310 std::vector<std::vector<int>> hitLineQAPtPhi0(input_bins_vector_size);
311 hitLineQAPtPhi0 = lineGenLay(hit);
312
313 for (const std::vector<int>& it_base : hitLineQAPtPhi0) {
314 if (it_base[0] != -1 && it_base[1] != -1) {
315 image(it_base[0] , it_base[1]).first++;
316 if (m_traceHits) {
317 image(it_base[0] , it_base[1]).second.insert(hit);
318 }
319 }
320 }
321 }else{
322 // This scans over y (pT) because that is more efficient in memory, in C.
323 // Unknown if firmware will want to scan over x instead.
324 unsigned new_size_y = m_imageSize_y / scale;
325 for (unsigned y_ = 0; y_ < new_size_y; y_++) {
326 unsigned y_bin_min = scale * y_;
327 unsigned y_bin_max = scale * (y_ + 1);
328
329 // Find the min/max x bins
330 auto xBins = yToXBins(y_bin_min, y_bin_max, hit);
331
332 // Update the image
333 for (unsigned y = y_bin_min; y < y_bin_max; y++){
334 for (unsigned x = xBins.first; x < xBins.second; x++) {
335 image(y, x).first++;
336 if (m_traceHits) image(y, x).second.insert(hit);
337 }
338 }
339 }
340 }
341 }
342
343 return image;
344}
345
346FPGATrackSimHoughTransformTool::Image FPGATrackSimHoughTransformTool::createImage(const std::vector<std::shared_ptr<const FPGATrackSimHit>> & hits) const
347{
349
350 for (unsigned i = 0; i < m_nCombineLayers; i++)
351 {
352 Image layerImage = createLayerImage(m_combineLayer2D[i], hits, m_binScale[i]);
353 for (unsigned x = 0; x < m_imageSize_x; ++x)
354 for (unsigned y = 0; y < m_imageSize_y; ++y)
355 if (layerImage(y, x).first > 0)
356 {
357 image(y, x).first++;
358 image(y, x).second.insert(layerImage(y, x).second.begin(), layerImage(y, x).second.end());
359 }
360 }
361 return image;
362}
363
365{
367
368 for (unsigned y0 = 0; y0 < m_imageSize_y; y0++) // Loop over out
369 for (unsigned x0 = 0; x0 < m_imageSize_x; x0++)
370 for (unsigned r = 0; r < m_convSize_y; r++) // Loop over conv
371 for (unsigned c = 0; c < m_convSize_x; c++) {
372 int y = -static_cast<int>(m_convSize_y) / 2 + r + y0; // Indices of input
373 int x = -static_cast<int>(m_convSize_x) / 2 + c + x0; //
374
375 if (y >= 0 && y < static_cast<int>(m_imageSize_y) && x >= 0 && x < static_cast<int>(m_imageSize_x)) {
376 int val = m_conv[r * m_convSize_x + c] * image(y, x).first;
377 if (val > 0) {
378 out(y0, x0).first += val;
379 out(y0, x0).second.insert(image(y, x).second.begin(), image(y, x).second.end());
380 }
381 }
382 }
383 return out;
384}
385
386bool FPGATrackSimHoughTransformTool::passThreshold(Image const & image, unsigned x, unsigned y) const
387{
388 // Pass window threshold
389 unsigned width = m_threshold.size() / 2;
390 if (x < width || (image.size(1) - x) < width) return false;
391 for (unsigned i = 0; i < m_threshold.size(); i++) {
392 if (image(y, x - width + i).first < m_threshold[i]) return false;
393 }
394
395 // Pass local-maximum check
397 for (int j = -m_localMaxWindowSize; j <= m_localMaxWindowSize; j++) {
398 for (int i = -m_localMaxWindowSize; i <= m_localMaxWindowSize; i++) {
399 if (i == 0 && j == 0) continue;
400 if (y + j < image.size(0) && x + i < image.size(1)) {
401 if (image(y+j, x+i).first > image(y, x).first) return false;
402 if (image(y+j, x+i).first == image(y, x).first) {
403 if (image(y+j, x+i).second.size() > image(y, x).second.size()) return false;
404 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)
405 }
406 }
407 }
408 }
409 }
410 return true;
411}
412
414// Helpers
415
416
417// Quantizes val, given a range [min, max) split into nSteps. Returns the bin below.
418static inline int quant(double min, double max, unsigned nSteps, double val)
419{
420 return static_cast<int>((val - min) / (max - min) * nSteps);
421}
422
423// Returns the lower bound of the bin specified by step
424static inline double unquant(double min, double max, unsigned nSteps, int step)
425{
426 return min + (max - min) * step / nSteps;
427}
428
429template <typename T>
430static inline std::string to_string(const std::vector<T> &v)
431{
432 std::ostringstream oss;
433 oss << "[";
434 if (!v.empty())
435 {
436 std::copy(v.begin(), v.end()-1, std::ostream_iterator<T>(oss, ", "));
437 oss << v.back();
438 }
439 oss << "]";
440 return oss.str();
441}
442
443double FPGATrackSimHoughTransformTool::yToX(double y, const std::shared_ptr<const FPGATrackSimHit> &hit) const
444{
445 double x = 0;
446
448 {
449 double r = hit->getR(); // mm
450 double phi_hit = hit->getGPhi(); // radians
451 double d0 = std::isnan(m_parMin.d0) ? 0 : m_parMin.d0; // mm, assume min = max
452 x = asin(r * fpgatracksim::A * y - d0 / r) + phi_hit;
453
454 if (m_fieldCorrection) x += fieldCorrection(m_EvtSel->getRegionID(), y, r);
455 }
456 else
457 {
458 ATH_MSG_ERROR("yToX() not defined for the current m_par selection");
459 }
460
461 return x;
462}
463
464
465
466std::vector<std::vector<int>> FPGATrackSimHoughTransformTool::lineGenLay(const std::shared_ptr<const FPGATrackSimHit> &hit) const
467{
468 /*
469 Chosen strategy:
470 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.
471 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.
472 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:
473 - make the firmware behave as a HT and adapt the software to the firmware to fully reproduce firmware performance;
474 - 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);
475 Strategies to fill the accumulator:
476 - 1 input bin to 1 output bin;
477 - 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:
478 - 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
479 - the lines drawn in the pipes start each one from a different point, following the monotonouse behavior of the line;
480 - 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;
481 - 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";
482 */
483
484 std::vector<int64_t> bins_x_new;
485 std::vector<int64_t> bins_y_new;
486
487 //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)
488 int64_t x_offset = m_phi0_min_bit + m_DBinPhi0_bit_int / 2;
489 int64_t y_offset = m_qApt_min_bit + m_DBinQApt_bit_int / 2;
490 for (unsigned i = 0; i < m_imageSize_x; i++) {
491 bins_x_new.push_back(x_offset + m_DBinPhi0_bit_int * i);
492 }
493 for (unsigned i = 0; i < m_imageSize_y; i++) {
494 bins_y_new.push_back(y_offset + m_DBinQApt_bit_int * i);
495 }
496
497
498 std::vector<int64_t> qApt_ht_array;
499 std::vector<int64_t> phi0_ht_array;
500 std::vector<int> zeros = {-1, -1};
501
502 std::vector<std::vector<int>> hitLineQAPtPhi0;
503
504 const unsigned bins_along_phi0_sector = m_phi0Bins_bit / m_phi0_sectors;
505 const unsigned bins_along_qApt_sector = m_qAptBins_bit / m_qApt_sectors;
506
507 for (unsigned i = 0; i < bins_along_phi0_sector; i++) {
508 qApt_ht_array.push_back(0);
509 }
510 for (unsigned i = 0; i < bins_along_qApt_sector; i++) {
511 phi0_ht_array.push_back(0);
512 }
513
514 int64_t phi_conv_offseted = 0;
515 int64_t Delta_qApt_after_first_sector = 0;
516 int64_t qApt_ht = -1;
517 int64_t phi0_ht = -1;
518 int64_t qApt_ht_pre = -1;
519 int64_t phi0_ht_pre = -1;
520 int64_t qApt_ht_sector = 0;
521 int64_t phi0_ht_sector = 0;
522 int64_t Delta_phi0_after_first_sector = 0;
523
524 std::vector<double> d_qApt_ht_array;
525 std::vector<double> d_phi0_ht_array;
526
527 for (unsigned i = 0; i < bins_along_phi0_sector; i++) {
528 d_qApt_ht_array.push_back(0.0);
529 }
530 for (unsigned i = 0; i < bins_along_qApt_sector; i++) {
531 d_phi0_ht_array.push_back(0.0);
532 }
533
534 double d_phi_conv_offseted = 0.0;
535 double d_qApt_ht_sector = 0.0;
536 double d_phi0_ht_sector = 0.0;
537 double d_one_over_r_by_const = 0.0;
538 double d_phi_conv_over_DBinQApt = 0.0;
539 double d_Delta_phi0_after_first_sector = 0.0;
540
542 double r = hit->getR();
543 double phi_hit = hit->getGPhi(); // radians
544 int r_bit = static_cast<int>(r * m_r_max / m_r_max_mm);
545 int64_t phi_bit = phi_hit * m_phi_coord_max / m_phi_range;
546 if (r_bit > m_HT_sel) { //Case for qA/Pt HT formula
547 for (unsigned i = 0; i < m_imageSize_x; i++) {
548 hitLineQAPtPhi0.push_back(zeros);
549 }
550 //formula first sector
551
552 d_one_over_r_by_const = m_one_r_const_twoexp / r_bit;
553 d_phi_conv_over_DBinQApt = phi_bit * m_tot_bitwise_conv / m_DBinQApt_bit_int;
554
555 for (int pp = 0; pp < m_pipes_phi0; pp++) {//loop over pipes pipes alongside phi0 axis
556 unsigned start_phi0_bins_first_sector = static_cast <unsigned>(pp * m_phi0Bins_bit / m_pipes_phi0);
557 unsigned end_phi0_bins_first_sector = m_phi0_bins_first_sector + static_cast <unsigned>(pp * m_phi0Bins_bit / m_pipes_phi0);
558
559 for (unsigned j = start_phi0_bins_first_sector; j < end_phi0_bins_first_sector; j++) {
560 double d_c1 = m_bitwise_qApt_conv * bins_x_new[j] / m_DBinQApt_bit_int;
561 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;
562 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);
563
564 double d_c2 = (d_c1 - d_phi_conv_over_DBinQApt) * d_one_over_r_by_const / m_one_r_const_twoexp_post_conv;
565 double d_c3 = (d_c1 - d_phi_conv_over_DBinQApt) * d_one_over_r_by_const;
567
568 if (static_cast<int64_t>(d_c1) <= static_cast<int64_t>(d_phi_conv_over_DBinQApt)) {
569 qApt_ht_pre = c2 - m_qApt_min_post_conv;
570 }else{
571 qApt_ht_pre = 1 + c2 - m_qApt_min_post_conv;
572 }
573 qApt_ht_sector = c3;
574 d_qApt_ht_sector = d_c3;
575 qApt_ht_array[j - start_phi0_bins_first_sector] = qApt_ht_sector;
576 d_qApt_ht_array[j - start_phi0_bins_first_sector] = d_qApt_ht_sector;
577
578 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) {
579 qApt_ht = qApt_ht_pre;
580 }
581 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) {
582 qApt_ht = qApt_ht_pre;
583 }
584
585 if (qApt_ht >= 0 && qApt_ht <= m_imageSize_y -1) {
586 hitLineQAPtPhi0[j] = {static_cast<int>(qApt_ht), static_cast<int>(j)};
587 }
588 qApt_ht = -1;
589 }
590 if (m_phi0_sectors > 1) { //formula after first sector
591 for (int ps = 0; ps < m_phi0_sectors -1; ps++) {
592 //note: this is integer division, e.g. 3/2 = 1
594
595 Delta_phi0_after_first_sector = d_c6 * static_cast<int64_t>(d_one_over_r_by_const);
596 //want a double here
597 d_Delta_phi0_after_first_sector = static_cast<double>(d_c6) * d_one_over_r_by_const;
598 for (unsigned psi = start_phi0_bins_first_sector; psi < end_phi0_bins_first_sector; psi++) {
599 int64_t c7 = (qApt_ht_array[psi - start_phi0_bins_first_sector] + Delta_phi0_after_first_sector) / m_one_r_const_twoexp_post_conv;
600
602
603 if (-qApt_ht_array[psi - start_phi0_bins_first_sector] < Delta_phi0_after_first_sector) {
604 qApt_ht = 1 + c7 - m_qApt_min_post_conv;
605 }else{
606 qApt_ht = 0 + c7 - m_qApt_min_post_conv;
607 }
608
609 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;
610
611 if (qApt_ht >= 0 && qApt_ht <= m_imageSize_y -1 && (1 + d_c7 - d_c9) >= 0 && (0 + d_c7 - d_c9) >= 0 ) {
612
613 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)};
614 }
615 qApt_ht = -1;
616 }
617 }
618 }
619 }
620 } else { //Case for Phi0 HT formula
621 for (unsigned i = 0; i < m_imageSize_y; i++) {
622 hitLineQAPtPhi0.push_back(zeros);
623 }
624 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;
625 d_phi_conv_offseted = (m_bitwise_phi0_conv * phi_bit - m_phi0_min_bit) * m_bitwise_qApt_conv / m_DBinPhi0_bit_int;
626
627 for (int pq = 0; pq < m_pipes_qApt; pq++) { //loop over pipes alongside qA/Pt axis
628
629 unsigned start_qApt_bins_first_sector = static_cast <unsigned>(pq * m_qAptBins_bit / m_pipes_qApt);
630 unsigned end_qApt_bins_first_sector = m_qApt_bins_first_sector + static_cast <unsigned>(pq * m_qAptBins_bit / m_pipes_qApt);
631
632 for (unsigned n = start_qApt_bins_first_sector; n < end_qApt_bins_first_sector; n++) {
633 //formula for first sector
634 //note: this is integer division e.g. 3/2 = 1
635 int64_t d2 =m_bitwise_phi0_conv * bins_y_new[n] / m_DBinPhi0_bit_int;
636
637 phi0_ht_sector = phi_conv_offseted + r_bit * d2;
638 phi0_ht_array[n - start_qApt_bins_first_sector] = phi0_ht_sector;
639 //want a double result here
640 d_phi0_ht_sector = d_phi_conv_offseted + r_bit * static_cast<double>(d2);
641 d_phi0_ht_array[n - start_qApt_bins_first_sector] = d_phi0_ht_sector;
642
643 if (phi0_ht_sector >= 0) {
644 phi0_ht_pre = phi0_ht_sector / m_bitwise_qApt_conv;
645 } else {
646 phi0_ht_pre = -1 + phi0_ht_sector / m_bitwise_qApt_conv;
647 }
648
649 if (phi0_ht_sector >= 0 && phi0_ht_pre >= 0 && (phi0_ht_sector >= m_bitwise_qApt_conv - m_imageSize_x)) {
650 phi0_ht = phi0_ht_pre;
651 }
657 if (phi0_ht <= m_imageSize_x - 1 && phi0_ht >= 0){
658 hitLineQAPtPhi0[n] = {static_cast<int>(n), static_cast<int>(phi0_ht)};
659 }
660 phi0_ht = -1;
661 }
662 if (m_qApt_sectors > 1) { //formula after first sector
663 for (int ts = 0; ts < m_qApt_sectors - 1; ts++) {
665 Delta_qApt_after_first_sector = r_bit * d3;
666 for (unsigned tsi = start_qApt_bins_first_sector; tsi < end_qApt_bins_first_sector; tsi++) {
667 phi0_ht = (phi0_ht_array[tsi - start_qApt_bins_first_sector] + Delta_qApt_after_first_sector) / m_bitwise_qApt_conv;
668 double d_phi0_ht = (d_phi0_ht_array[tsi - start_qApt_bins_first_sector] + Delta_qApt_after_first_sector) / m_bitwise_qApt_conv;
669
670 if (phi0_ht <= m_imageSize_x - 1 && phi0_ht >= 0 && d_phi0_ht >= 0 && phi0_ht >= m_bitwise_qApt_conv - m_imageSize_x) {
671 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)};
672 }
673 phi0_ht = -1;
674 }
675 }
676 }
677 }
678 }
679 } else {
680 ATH_MSG_ERROR("lineGenLay() not defined for the current m_par selection");
681 }
682
683 //return { HitLine, BinInPos };
684 return hitLineQAPtPhi0;
685}
686
687// Find the min/max x bins of the hit's line, in each y bin. Max is exclusive.
688// Note this assumes yToX is monotonic. Returns {0, 0} if hit lies out of bounds.
689std::pair<unsigned, unsigned> FPGATrackSimHoughTransformTool::yToXBins(size_t yBin_min, size_t yBin_max, const std::shared_ptr<const FPGATrackSimHit> & hit) const
690{
691 // Get float values
692 double x_min = yToX(m_bins_y[yBin_min], hit);
693 double x_max = yToX(m_bins_y[yBin_max], hit);
694 if (x_min > x_max) std::swap(x_min, x_max);
695 if (x_max < m_parMin[m_par_x] || x_min > m_parMax[m_par_x])
696 return { 0, 0 }; // out of bounds
697
698 // Get bins
699 int x_bin_min = quant(m_parMin[m_par_x], m_parMax[m_par_x], m_imageSize_x, x_min);
700 int x_bin_max = quant(m_parMin[m_par_x], m_parMax[m_par_x], m_imageSize_x, x_max) + 1; // exclusive
701
702 // Extend bins
703 unsigned extend = getExtension(yBin_min, hit->getLayer());
704 x_bin_min -= extend;
705 x_bin_max += extend;
706
707 // Clamp bins
708 if (x_bin_min < 0) x_bin_min = 0;
709 if (x_bin_max > static_cast<int>(m_imageSize_x)) x_bin_max = m_imageSize_x;
710
711 return { x_bin_min, x_bin_max };
712}
713
714// We allow variable extension based on the size of m_hitExtend_x. See comments below.
715unsigned FPGATrackSimHoughTransformTool::getExtension(unsigned y, unsigned layer) const
716{
717 if (m_hitExtend_x.size() == m_nLayers) return m_hitExtend_x[layer];
718 if (m_hitExtend_x.size() == m_nLayers * 2)
719 {
720 // different extension for low pt vs high pt, split in half but irrespective of sign
721 // first nLayers entries of m_hitExtend_x is for low pt half, rest are for high pt half
723 return m_hitExtend_x[m_nLayers + layer];
724 }
725 return 0;
726}
727
728// Creates a road from hits that pass through the given bin (x, y), and pushes it onto m_roads
729void FPGATrackSimHoughTransformTool::addRoad(const std::vector<std::vector<std::shared_ptr<const FPGATrackSimHit>>> & hits, layer_bitmask_t hitLayers, unsigned x, unsigned y)
730{
731 m_roads.emplace_back();
732 FPGATrackSimRoad & r = m_roads.back();
733
734 r.setRoadID(m_roads.size() - 1);
735 r.setPID(y * m_imageSize_y + x);
736 r.setHits( std::vector<std::vector<std::shared_ptr<const FPGATrackSimHit>>>(hits)); //copy hits
737
738 // We use the y coordinate in matchIdealGeoSectors
739 // and so it needs to be available before setting the sector.
740
741 r.setSubRegion(m_subRegion);
742 r.setX(m_bins_x[x] + m_step_x/2);
743 r.setY(m_bins_y[y] + m_step_y/2);
744 r.setXBin(x);
745 r.setYBin(y);
746 r.setHitLayers(hitLayers);
747 r.setSubRegion(m_subRegion);
748}
749
750
751// Creates a road from hits that pass through the given bin (x, y), and pushes it onto m_roads
752void FPGATrackSimHoughTransformTool::addRoad(const std::unordered_set<std::shared_ptr<const FPGATrackSimHit>> & hits, unsigned x, unsigned y)
753{
754 layer_bitmask_t hitLayers = 0;
755 for (auto const & hit : hits)
756 hitLayers |= 1 << hit->getLayer();
757
758 auto sorted_hits = ::sortByLayer(hits);
759 sorted_hits.resize(m_nLayers); // If no hits in last layer, return from sortByLayer will be too short
760
761 addRoad(sorted_hits, hitLayers, x, y);
762}
763
764// Use this version of addRoad when hit tracing is turned off
765void FPGATrackSimHoughTransformTool::addRoad(const std::vector<std::shared_ptr<const FPGATrackSimHit>> & hits, unsigned x, unsigned y)
766{
767 // Get the road hits
768 std::vector<std::shared_ptr<const FPGATrackSimHit>> road_hits;
769 layer_bitmask_t hitLayers = 0;
770 for (const auto & hit : hits)
771 {
772 // Find the min/max y bins (after scaling)
773 unsigned int y_bin_min = (y / m_binScale[hit->getLayer()]) * m_binScale[hit->getLayer()];
774 unsigned int y_bin_max = y_bin_min + m_binScale[hit->getLayer()];
775
776 // Find the min/max x bins
777 auto xBins = yToXBins(y_bin_min, y_bin_max, hit);
778 if (x >= xBins.first && x < xBins.second)
779 {
780 road_hits.push_back(hit);
781 hitLayers |= 1 << hit->getLayer();
782 }
783 }
784
785 auto sorted_hits = ::sortByLayer(road_hits);
786 sorted_hits.resize(m_nLayers); // If no hits in last layer, return from sortByLayer will be too short
787
788 addRoad(sorted_hits, hitLayers, x, y);
789}
790
791// 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.
792void FPGATrackSimHoughTransformTool::makeLUT(std::vector<std::vector<std::vector<LUT>>> &v_LUT, std::vector<TH1D*> &v_h, const std::string& tag){
793 const std::string filepath = m_requirements.value() + "requirement-" + tag + ".root";
794 TFile* fin = TFile::Open(filepath.c_str());
795 ATH_MSG_INFO("open: " << tag);
796 m_bitlength = 14;//FIXME length needed to represent input phi. Should be define from requirement file
797 TTree* requirement = fin->Get<TTree>("requirement");
798 v_LUT.resize(5);
799 for(int i=0; i<5; i++){//FIXME as this target only for barrel so far, hardcoded
800 v_LUT.at(i).resize(64);
801 TH1D* h = (TH1D*)fin->Get(Form("in%d",i));//This is 1D hist to convert float phi to bitwise, depending on layer
802 v_h.push_back(h);
803 }
804 int nrequirement = requirement->GetEntries();
805 int in_min;
806 int in_max;
807 int xi;
808 int yi;
809 int ri;
810 requirement->SetBranchAddress("input_begin", &in_min);
811 requirement->SetBranchAddress("input_end", &in_max);
812 requirement->SetBranchAddress("output_x", &xi);
813 requirement->SetBranchAddress("output_y", &yi);
814 requirement->SetBranchAddress("output_l", &ri);
815 for(int i=0; i<nrequirement; i++){
816 requirement->GetEntry(i);
817 //process for min range
818 int MSB = (in_min >> (m_bitlength -6));
819 bool toBeFilled = true;
820 if(!v_LUT.at(ri).at(MSB).empty()){
821 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.
822 if(LUT_i.input_begin == in_min && LUT_i.input_end == in_max){
823 LUT_i.output.push_back({xi, yi, ri});
824 toBeFilled = false;
825 break;
826 }
827 }
828 }
829 if(toBeFilled){
830 LUT LUT_i;
831 LUT_i.input_begin = in_min;
832 LUT_i.input_end = in_max;
833 LUT_i.layer = ri;
834 LUT_i.output.push_back({xi, yi, ri});
835 v_LUT.at(ri).at(MSB).push_back(std::move(LUT_i));
836 }
837 //process for max range
838 int MSB2 = (in_max >> (m_bitlength -6));
839 if(MSB != MSB2){
840 MSB = MSB2;
841 toBeFilled = true;
842 if(!v_LUT.at(ri).at(MSB).empty()){
843 for(auto& LUT_i : v_LUT.at(ri).at(MSB)){
844 if(LUT_i.input_begin == in_min && LUT_i.input_end == in_max){
845 LUT_i.output.push_back({xi, yi, ri});
846 toBeFilled = false;
847 break;
848 }
849 }
850 }
851 if(toBeFilled){
852 LUT LUT_i;
853 LUT_i.input_begin = in_min;
854 LUT_i.input_end = in_max;
855 LUT_i.layer = ri;
856 LUT_i.output.push_back({xi, yi, ri});
857 v_LUT.at(ri).at(MSB).push_back(std::move(LUT_i));
858 }
859 }
860 }
861 int LUTsize = v_LUT.size();
862 for(int layer = 0; layer < LUTsize; layer++){
863 for(int msb = 0; msb < 64; msb++){
864 ATH_MSG_DEBUG("LUT size L(" << layer << ") msb(" << msb << "): " << v_LUT.at(layer).at(msb).size());
865 }
866 }
867}
#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)
virtual StatusCode getRoads(const std::vector< std::shared_ptr< const FPGATrackSimHit > > &hits, std::vector< std::shared_ptr< const FPGATrackSimRoad > > &roads) override
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="")
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)