ATLAS Offline Software
Loading...
Searching...
No Matches
FPGATrackSimHoughTransform_d0phi0_Tool.cxx
Go to the documentation of this file.
1// Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
2
9
19
20#include "TH2.h"
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(std::vector<T> v);
30static inline std::string instance_name(std::string const & s);
31
32
33
35// AthAlgTool
36
37FPGATrackSimHoughTransform_d0phi0_Tool::FPGATrackSimHoughTransform_d0phi0_Tool(const std::string& algname, const std::string &name, const IInterface *ifc) :
38 base_class(algname, name, ifc),
39 m_name(instance_name(name)),
40 m_monitorFile((m_name + ".root").c_str(), "RECREATE")
41{
42}
43
44
46{
47 // Move temp variables over from properties to struct
52
53 // Debug
54 ATH_MSG_INFO("Image size: " << m_imageSize_x << " x " << m_imageSize_y);
55 ATH_MSG_INFO("Convolution size: " << m_convSize_x << " x " << m_convSize_y);
56 ATH_MSG_INFO("Convolution: " << to_string(const_cast<std::vector<int>&>(m_conv.value())));
57 ATH_MSG_INFO("Hit Extend: " << to_string(const_cast<std::vector<unsigned>&>(m_hitExtend_x.value())));
58
59 // Retrieve info
62 m_nLayers = m_FPGATrackSimMapping->PlaneMap_1st(0)->getNLogiLayers();
63
64 // Error checking
65 bool ok = false;
67 ATH_MSG_FATAL("initialize() Image size must be greater than 0");
68 else if (m_conv.size() != m_convSize_x * m_convSize_y)
69 ATH_MSG_FATAL("initialize() Convolution sizes don't match");
70 else if (!m_conv.empty() && (m_convSize_x % 2 == 0 || m_convSize_y % 2 == 0))
71 ATH_MSG_FATAL("initialize() Convolution sizes must be odd");
72 else if (!m_hitExtend_x.empty() && m_hitExtend_x.size() != m_nLayers)
73 ATH_MSG_FATAL("initialize() Hit extentsion list must have size == nLayers");
74 else if (m_threshold.size() % 2 != 1)
75 ATH_MSG_FATAL("initialize() Threshold size must be odd");
76 else
77 ok = true;
78 for (unsigned const scale : m_binScale) {
79 if (m_imageSize_y % scale != 0) {
80 ATH_MSG_FATAL("initialize() The imagesize is not divisible by scale");
81 ok = false;
82 }
83 }
84 if (!ok) return StatusCode::FAILURE;
85
86 // Warnings / corrections
88 ATH_MSG_WARNING("initialize() localMaxWindowSize requires tracing hits, turning on automatically");
89 m_traceHits = true;
90 }
91
92 if (m_idealGeoRoads) {
93 if (m_useSectors) {
94 ATH_MSG_WARNING("initialize() idealGeoRoads conflicts with useSectors, switching off FPGATrackSim sector matching");
95 m_useSectors = false;
96
97 }
98 if (!m_traceHits) {
99 ATH_MSG_WARNING("initialize() idealGeoRoads requires tracing hits, turning on automatically");
100 m_traceHits = true;
101 }
102 }
103
104
105 // Fill convenience variables
108 for (unsigned i = 0; i <= m_imageSize_x; i++)
110 for (unsigned i = 0; i <= m_imageSize_y; i++)
112
113 // Initialize combine layers
114 m_nCombineLayers = *std::max_element(m_combineLayers.begin(), m_combineLayers.end()) + 1;
116 for (unsigned i = 0; i < m_combineLayers.size(); i++) {
117 m_combineLayer2D[m_combineLayers[i]].push_back(i);
118 }
119
120 // Initialize scaled binnings
121 for (unsigned const scale : m_binScale) {
122 auto iter = m_yBins_scaled.find(scale);
123
124 // if scale not found in keys, then push the scaled binning
125 if (iter == m_yBins_scaled.end()) {
126 unsigned new_size_y = m_imageSize_y / scale;
127 std::vector<size_t> yBins_scaled;
128 for (unsigned i = 0; i <= new_size_y; i++) {
129 yBins_scaled.push_back(scale * i);
130 }
131 m_yBins_scaled[scale] = std::move(yBins_scaled);
132 }
133 }
134
135 return StatusCode::SUCCESS;
136}
137
138
140{
141 m_monitorFile.Write();
142 return StatusCode::SUCCESS;
143}
144
145
147// Main Algorithm
148
149StatusCode FPGATrackSimHoughTransform_d0phi0_Tool::getRoads(const std::vector<std::shared_ptr<const FPGATrackSimHit>> & hits, std::vector<std::shared_ptr<const FPGATrackSimRoad>> & roads)
150{
151 ATH_MSG_DEBUG("Event: " << m_event << ", # hits: " << hits.size());
152 roads.clear();
153 m_roads.clear();
154
155 Image image = createImage(hits);
156 if (m_event < 5) drawImage(image, m_name + "_" + std::to_string(m_event));
157 if (!m_conv.empty()) image = convolute(image);
158
159 for (unsigned y = 0; y < m_imageSize_y; y++) {
160 for (unsigned x = 0; x < m_imageSize_x; x++) {
161 if (passThreshold(image, x, y)) {
162 m_roads.push_back(createRoad(image(y,x).second, x, y));
163 }
164 }
165 }
166
167 roads.reserve(m_roads.size());
168 for (FPGATrackSimRoad & r : m_roads) roads.emplace_back(std::make_shared<const FPGATrackSimRoad>(r));
169
170 if (roads.empty() && m_event >= 5 && m_event < 200)
171 drawImage(image, m_name + "_" + std::to_string(m_event));
172
173 m_event++;
174 return StatusCode::SUCCESS;
175}
176
177
178FPGATrackSimHoughTransform_d0phi0_Tool::Image FPGATrackSimHoughTransform_d0phi0_Tool::createLayerImage(std::vector<int> const & combine_layers, const std::vector<std::shared_ptr<const FPGATrackSimHit>> &hits, unsigned const scale) const
179{
180
182
183 for (auto const & hit : hits) {
184
185 bool belong1 = false, belong2 = false;
186 for (auto layer : combine_layers) {
187 if (belong1 || belong2) break;
188 if (hit->getLayer() == layer)
189 belong1 = true;
190 if (hit->getLayer() == layer + 1)
191 belong2 = true;
192 }
193
194 if ((!belong1 && !m_stereo) ||
195 (m_subRegion >= 0 && !m_FPGATrackSimMapping->SubRegionMap()->isInRegion(m_subRegion, *hit))) {
196 continue;
197 }
198 else if (m_stereo && belong1) { /* Intentionally blank */ }
199 else if (m_stereo && !(hit->getLayer() > 1 && (hit->getLayer() % 2 == 1 && belong2))) {
200 continue;
201 }
202
203 // This scans over y (d0) because that is more efficient in memory, in C.
204 // Unknown if firmware will want to scan over x instead.
205 unsigned new_size_y = m_imageSize_y / scale;
206
207 for (unsigned y_ = 0; y_ < new_size_y; y_++) {
208 int y_bin_min = m_yBins_scaled.at(scale)[y_];
209 int y_bin_max = m_yBins_scaled.at(scale)[y_+1];
210
211 // Find the min/max x bins
212 auto xBins = yToXBins(y_bin_min, y_bin_max, hit);
213 // Update the image
214 for (int y = y_bin_min; y < y_bin_max; y++) {
215 for (unsigned x = xBins.first; x < xBins.second; x++) {
216 image(y, x).first++;
217 if (m_traceHits) image(y, x).second.insert(hit);
218 }
219 }
220 }
221 }
222
223 return image;
224}
225
226FPGATrackSimHoughTransform_d0phi0_Tool::Image FPGATrackSimHoughTransform_d0phi0_Tool::createImage(const std::vector<std::shared_ptr<const FPGATrackSimHit>> & hits) const
227{
229
230 for (unsigned i = 0; i < m_nCombineLayers ; i++) {
231 Image layerImage = createLayerImage(m_combineLayer2D[i], hits, m_binScale[i]);
232 if (i > 1 && i % 2 == 0 && m_stereo) ++i; // not sure how to deal with this for Combine Layer setting
233 for (unsigned x = 0; x < m_imageSize_x; ++x) {
234 for (unsigned y = 0; y < m_imageSize_y; ++y) {
235 if (layerImage(y, x).first > 0) {
236 image(y, x).first++;
237 image(y, x).second.insert(layerImage(y, x).second.begin(), layerImage(y, x).second.end());
238 }
239 }
240 }
241 }
242
243 return image;
244}
245
247{
249
250 for (unsigned y0 = 0; y0 < m_imageSize_y; y0++) { // Loop over out
251 for (unsigned x0 = 0; x0 < m_imageSize_x; x0++) { //
252 for (unsigned r = 0; r < m_convSize_y; r++) { // Loop over conv
253 int y = -static_cast<int>(m_convSize_y) / 2 + r + y0; // Indices of input
254 for (unsigned c = 0; c < m_convSize_x; c++) {
255 int x = -static_cast<int>(m_convSize_x) / 2 + c + x0; //
256
257 if (y >= 0 && y < static_cast<int>(m_imageSize_y) && x >= 0 && x < static_cast<int>(m_imageSize_x)) {
258 int val = m_conv[r * m_convSize_x + c] * image(y, x).first;
259 if (val > 0) {
260 out(y0, x0).first += val;
261 out(y0, x0).second.insert(image(y, x).second.begin(), image(y, x).second.end());
262 }
263 }
264 }
265 }
266 }
267 }
268
269 return out;
270}
271
272bool FPGATrackSimHoughTransform_d0phi0_Tool::passThreshold(Image const & image, unsigned x, unsigned y) const
273{
274 // Pass window threshold
275 unsigned width = m_threshold.size() / 2;
276 if (x < width || (image.size(1) - x) < width) return false;
277 for (unsigned i = 0; i < m_threshold.size(); i++)
278 if (image(y, x - width + i).first < m_threshold[i]) return false;
279
280 // Pass local-maximum check
282 for (int j = -m_localMaxWindowSize; j <= m_localMaxWindowSize; j++)
283 for (int i = -m_localMaxWindowSize; i <= m_localMaxWindowSize; i++) {
284 if (i == 0 && j == 0) continue;
285 if (y + j < image.size(0) && x + i < image.size(1)) {
286 if (image(y+j, x+i).first > image(y, x).first) return false;
287 if (image(y+j, x+i).first == image(y, x).first) {
288 if (image(y+j, x+i).second.size() > image(y, x).second.size()) return false;
289 if (image(y+j, x+i).second.size() == image(y, x).second.size()
290 && j <= 0 && i <= 0) return false;
291 }
292 }
293 }
294
295 return true;
296}
297
299// Helpers
300
301
302// Quantizes val, given a range [min, max) split into nSteps. Returns the bin below.
303static inline int quant(double min, double max, unsigned nSteps, double val)
304{
305 return static_cast<int>((val - min) / (max - min) * nSteps);
306}
307
308// Returns the lower bound of the bin specified by step
309static inline double unquant(double min, double max, unsigned nSteps, int step)
310{
311 return min + (max - min) * step / nSteps;
312}
313
314template <typename T>
315static inline std::string to_string(std::vector<T> v)
316{
317 std::ostringstream oss;
318 oss << "[";
319 if (!v.empty()) {
320 std::copy(v.begin(), v.end()-1, std::ostream_iterator<T>(oss, ", "));
321 oss << v.back();
322 }
323 oss << "]";
324 return oss.str();
325}
326
327static inline std::string instance_name(std::string const & s)
328{
329 size_t pos = s.find_last_of('.');
330 if (pos != std::string::npos)
331 return s.substr(pos + 1);
332 return s;
333}
334
335double FPGATrackSimHoughTransform_d0phi0_Tool::yToX(double y, const std::shared_ptr<const FPGATrackSimHit> &hit) const
336{
337 double x = 0;
338
340 double r = hit->getR(); // TODO check this, and units
341 double phi_hit = hit->getGPhi(); // TODO check this, and units
342 x = -y/r + phi_hit;
343 }
344 else {
345 ATH_MSG_ERROR("yToX() not defined for the current m_par selection");
346 }
347
348 return x;
349}
350
351// Find the min/max x bins of the hit's line, in each y bin. Max is exclusive.
352// Note this assumes yToX is monotonic. Returns {0, 0} if hit lies out of bounds.
353std::pair<unsigned, unsigned> FPGATrackSimHoughTransform_d0phi0_Tool::yToXBins(size_t yBin_min, size_t yBin_max, const std::shared_ptr<const FPGATrackSimHit> &hit) const
354{
355 // Get float values
356 double x_min = yToX(m_bins_y[yBin_min], hit);
357 double x_max = yToX(m_bins_y[yBin_max], hit);
358 if (x_min > x_max) std::swap(x_min, x_max);
359 if (x_max < m_parMin[m_par_x] || x_min > m_parMax[m_par_x])
360 return { 0, 0 }; // out of bounds
361
362 // Get bins
363 int x_bin_min = quant(m_parMin[m_par_x], m_parMax[m_par_x], m_imageSize_x, x_min);
364 int x_bin_max = quant(m_parMin[m_par_x], m_parMax[m_par_x], m_imageSize_x, x_max) + 1; // exclusive
365
366 // Extend bins
367 if (!m_hitExtend_x.empty()) x_bin_min -= m_hitExtend_x[hit->getLayer()];
368 if (!m_hitExtend_x.empty()) x_bin_max += m_hitExtend_x[hit->getLayer()];
369
370 // Clamp bins
371 if (x_bin_min < 0) x_bin_min = 0;
372 if (x_bin_max > static_cast<int>(m_imageSize_x)) x_bin_max = m_imageSize_x;
373
374 return { x_bin_min, x_bin_max };
375}
376
378{
379 float pt = r.getY()*0.001; // convert to MeV
380 auto bounds = std::equal_range(fpgatracksim::QOVERPT_BINS.begin(),fpgatracksim::QOVERPT_BINS.end(),pt);
381 int sectorbin = bounds.first-fpgatracksim::QOVERPT_BINS.begin()-1;
382
383 // those bins are for tracks between the values, can't be below first value or more than the last value
384 if (sectorbin < 0) sectorbin = 0;
385 if (sectorbin > static_cast<int>(fpgatracksim::QOVERPT_BINS.size()-2)) sectorbin = fpgatracksim::QOVERPT_BINS.size()-2;
386 std::vector<module_t> modules;
387
388 for (unsigned int il = 0; il < r.getNLayers(); il++) {
389 if (r.getNHits_layer()[il] == 0) {
390 modules.push_back(-1);
391 layer_bitmask_t wc_layers = r.getWCLayers();
392 wc_layers |= (0x1 << il);
393 r.setWCLayers(wc_layers);
394
395 std::unique_ptr<FPGATrackSimHit> wcHit = std::unique_ptr<FPGATrackSimHit>(new FPGATrackSimHit());
396 wcHit->setHitType(HitType::wildcard);
397 wcHit->setLayer(il);
398 wcHit->setDetType(m_FPGATrackSimMapping->PlaneMap_1st(0)->getDetType(il));
399 r.setHits(il,std::vector<std::shared_ptr<const FPGATrackSimHit>>({std::move(wcHit)}));
400
401 }
402 else
403 modules.push_back(sectorbin);
404 }
405 const FPGATrackSimSectorBank* sectorbank = m_FPGATrackSimBankSvc->SectorBank_1st();
406 r.setSector(sectorbank->findSector(modules));
407}
408
409// Create road via hits only
410FPGATrackSimRoad FPGATrackSimHoughTransform_d0phi0_Tool::createRoad(const std::unordered_set<std::shared_ptr<const FPGATrackSimHit>> &hits, unsigned x, unsigned y) const {
411 // Get the road hits
412 std::vector<std::shared_ptr<const FPGATrackSimHit>> road_hits;
413 layer_bitmask_t hitLayers = 0;
414 for (const auto &hit : hits) {
415 road_hits.push_back(hit);
416 hitLayers |= 1 << hit->getLayer();
417 }
418
419 auto sorted_hits = ::sortByLayer(road_hits);
420 sorted_hits.resize(m_nLayers); // If no hits in last layer, return from sortByLayer will be too short
421
423 r.setRoadID(m_roads.size());
424 r.setPID(y * m_imageSize_y + x);
426 r.setHitLayers(hitLayers);
427 r.setHits(std::move(sorted_hits));
428 r.setSubRegion(m_subRegion);
429 r.setX(m_bins_x[x] + m_step_x/2);
430 r.setY(m_bins_y[y] + m_step_y/2);
431 return r;
432}
433
434// Creates a road from hits that pass through the given bin (x, y), and pushes it onto m_roads
435FPGATrackSimRoad FPGATrackSimHoughTransform_d0phi0_Tool::createRoad(std::vector<std::vector<std::shared_ptr<const FPGATrackSimHit>>> &&hits, layer_bitmask_t hitLayers, unsigned x, unsigned y) const
436{
438 r.setRoadID(m_roads.size());
439 r.setPID(y * m_imageSize_y + x);
440 if (m_useSectors) r.setSector(m_FPGATrackSimBankSvc->SectorBank_1st()->findSector(hits));
442 r.setHitLayers(hitLayers);
443 r.setHits(std::move(hits));
444 r.setSubRegion(m_subRegion);
445 r.setX(m_bins_x[x] + m_step_x/2);
446 r.setY(m_bins_y[y] + m_step_y/2);
447
448 return r;
449}
450
451
452// Creates a road from hits that pass through the given bin (x, y), and pushes it onto m_roads
453void FPGATrackSimHoughTransform_d0phi0_Tool::addRoad(const std::unordered_set<std::shared_ptr<const FPGATrackSimHit>> &hits, unsigned x, unsigned y)
454{
455 layer_bitmask_t hitLayers = 0;
456 for (auto const & hit : hits) {
457 hitLayers |= 1 << hit->getLayer();
458 }
459
460 auto sorted_hits = ::sortByLayer(hits);
461 sorted_hits.resize(m_nLayers); // If no hits in last layer, return from sortByLayer will be too short
462 m_roads.push_back(createRoad(std::move(sorted_hits), hitLayers, x, y));
463}
464
465// Use this version of addRoad when hit tracing is turned off
466void FPGATrackSimHoughTransform_d0phi0_Tool::addRoad(const std::vector<std::shared_ptr<const FPGATrackSimHit>> &hits, unsigned x, unsigned y)
467{
468 // Get the road hits
469 std::vector<std::shared_ptr<const FPGATrackSimHit>> road_hits;
470 layer_bitmask_t hitLayers = 0;
471 for (const auto &hit : hits) {
472 if (m_subRegion >= 0 && !m_FPGATrackSimMapping->SubRegionMap()->isInRegion(m_subRegion, *hit)) continue;
473
474 // get bin scaling for the hit
475 unsigned bin_scale = 0;
476 for (unsigned i = 0; i < m_nCombineLayers; i++) {
477 for (int const layer : m_combineLayer2D[i]) {
478 if (hit->getLayer() == layer) {
479 bin_scale = m_binScale[layer];
480 }
481 }
482 }
483 if (bin_scale == 0) bin_scale = 1; //avoid divide by zero,use 1:1 scale
484 unsigned y_bin_min = floor(1.0 * y / bin_scale) * bin_scale;
485 unsigned y_bin_max = ceil(1.0 * y / bin_scale) * bin_scale;
486 if (y_bin_min == y_bin_max) y_bin_max++;
487
488 // Find the min/max x bins
489 auto xBins = yToXBins(y_bin_min, y_bin_max, hit);
490
491 if (x >= xBins.first && x < xBins.second && y >= y_bin_min && y < y_bin_max) {
492 road_hits.push_back(hit);
493 hitLayers |= 1 << hit->getLayer();
494 }
495 }
496
497 auto sorted_hits = ::sortByLayer(road_hits);
498 sorted_hits.resize(m_nLayers); // If no hits in last layer, return from sortByLayer will be too short
499
500 m_roads.push_back(createRoad(std::move(sorted_hits), hitLayers, x, y));
501}
502
503
504// For debug use
505void FPGATrackSimHoughTransform_d0phi0_Tool::drawImage(Image const & image, std::string const & name)
506{
507 m_monitorFile.cd();
508
509 TH2I h(name.c_str(), "Hough Transform;phi;d0 (mm)",
512 );
513
514 for (unsigned y = 0; y < m_imageSize_y; y++)
515 for (unsigned x = 0; x < m_imageSize_x; x++)
516 h.SetBinContent(x+1, y+1, image(y, x).first); // +1 since root bins are 1-indexed
517
518 h.Write();
519}
520
#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)
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 int quant(double min, double max, unsigned nSteps, double val)
static double unquant(double min, double max, unsigned nSteps, int step)
static std::string to_string(std::vector< T > v)
static std::string instance_name(std::string const &s)
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 in d0 vs phi0, assuming q/pt = 0 (i....
static std::string instance_name(std::string const &s)
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< unsigned int > > m_combineLayers
ServiceHandle< IFPGATrackSimBankSvc > m_FPGATrackSimBankSvc
bool passThreshold(Image const &image, unsigned x, unsigned y) const
Image createLayerImage(std::vector< int > const &combine_layers, const std::vector< std::shared_ptr< const FPGATrackSimHit > > &hits, unsigned const scale) const
Gaudi::Property< std::vector< unsigned int > > m_binScale
void addRoad(const std::unordered_set< std::shared_ptr< const FPGATrackSimHit > > &hits, unsigned x, unsigned y)
Image createImage(const std::vector< std::shared_ptr< const FPGATrackSimHit > > &hits) const
FPGATrackSimHoughTransform_d0phi0_Tool(const std::string &, const std::string &, const IInterface *)
virtual StatusCode getRoads(const std::vector< std::shared_ptr< const FPGATrackSimHit > > &hits, std::vector< std::shared_ptr< const FPGATrackSimRoad > > &roads) override
FPGATrackSimRoad createRoad(const std::unordered_set< std::shared_ptr< const FPGATrackSimHit > > &hits, unsigned x, unsigned y) const
void drawImage(Image const &image, std::string const &name)
std::pair< unsigned, unsigned > yToXBins(size_t yBin_min, size_t yBin_max, const std::shared_ptr< const FPGATrackSimHit > &hit) const
vector2D< std::pair< int, std::unordered_set< std::shared_ptr< const FPGATrackSimHit > > > > Image
std::unordered_map< int, std::vector< size_t > > m_yBins_scaled
ServiceHandle< IFPGATrackSimMappingSvc > m_FPGATrackSimMapping
double yToX(double y, const std::shared_ptr< const FPGATrackSimHit > &h) const
Gaudi::Property< std::vector< unsigned int > > m_hitExtend_x
sector_t findSector(std::vector< module_t > const &modules) const
int r
Definition globals.cxx:22
constexpr std::array< double, 5 > QOVERPT_BINS
void swap(ElementLinkVector< DOBJ > &lhs, ElementLinkVector< DOBJ > &rhs)