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<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 = std::move(m_roads);
168
169 if (roads.empty() && m_event >= 5 && m_event < 200)
170 drawImage(image, m_name + "_" + std::to_string(m_event));
171
172 m_event++;
173 return StatusCode::SUCCESS;
174}
175
176
177FPGATrackSimHoughTransform_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
178{
179
181
182 for (auto const & hit : hits) {
183
184 bool belong1 = false, belong2 = false;
185 for (auto layer : combine_layers) {
186 if (belong1 || belong2) break;
187 if (hit->getLayer() == layer)
188 belong1 = true;
189 if (hit->getLayer() == layer + 1)
190 belong2 = true;
191 }
192
193 if ((!belong1 && !m_stereo) ||
194 (m_subRegion >= 0 && !m_FPGATrackSimMapping->SubRegionMap()->isInRegion(m_subRegion, *hit))) {
195 continue;
196 }
197 else if (m_stereo && belong1) { /* Intentionally blank */ }
198 else if (m_stereo && !(hit->getLayer() > 1 && (hit->getLayer() % 2 == 1 && belong2))) {
199 continue;
200 }
201
202 // This scans over y (d0) because that is more efficient in memory, in C.
203 // Unknown if firmware will want to scan over x instead.
204 unsigned new_size_y = m_imageSize_y / scale;
205
206 for (unsigned y_ = 0; y_ < new_size_y; y_++) {
207 int y_bin_min = m_yBins_scaled.at(scale)[y_];
208 int y_bin_max = m_yBins_scaled.at(scale)[y_+1];
209
210 // Find the min/max x bins
211 auto xBins = yToXBins(y_bin_min, y_bin_max, hit);
212 // Update the image
213 for (int y = y_bin_min; y < y_bin_max; y++) {
214 for (unsigned x = xBins.first; x < xBins.second; x++) {
215 image(y, x).first++;
216 if (m_traceHits) image(y, x).second.insert(hit);
217 }
218 }
219 }
220 }
221
222 return image;
223}
224
225FPGATrackSimHoughTransform_d0phi0_Tool::Image FPGATrackSimHoughTransform_d0phi0_Tool::createImage(const std::vector<std::shared_ptr<const FPGATrackSimHit>> & hits) const
226{
228
229 for (unsigned i = 0; i < m_nCombineLayers ; i++) {
230 Image layerImage = createLayerImage(m_combineLayer2D[i], hits, m_binScale[i]);
231 if (i > 1 && i % 2 == 0 && m_stereo) ++i; // not sure how to deal with this for Combine Layer setting
232 for (unsigned x = 0; x < m_imageSize_x; ++x) {
233 for (unsigned y = 0; y < m_imageSize_y; ++y) {
234 if (layerImage(y, x).first > 0) {
235 image(y, x).first++;
236 image(y, x).second.insert(layerImage(y, x).second.begin(), layerImage(y, x).second.end());
237 }
238 }
239 }
240 }
241
242 return image;
243}
244
246{
248
249 for (unsigned y0 = 0; y0 < m_imageSize_y; y0++) { // Loop over out
250 for (unsigned x0 = 0; x0 < m_imageSize_x; x0++) { //
251 for (unsigned r = 0; r < m_convSize_y; r++) { // Loop over conv
252 int y = -static_cast<int>(m_convSize_y) / 2 + r + y0; // Indices of input
253 for (unsigned c = 0; c < m_convSize_x; c++) {
254 int x = -static_cast<int>(m_convSize_x) / 2 + c + x0; //
255
256 if (y >= 0 && y < static_cast<int>(m_imageSize_y) && x >= 0 && x < static_cast<int>(m_imageSize_x)) {
257 int val = m_conv[r * m_convSize_x + c] * image(y, x).first;
258 if (val > 0) {
259 out(y0, x0).first += val;
260 out(y0, x0).second.insert(image(y, x).second.begin(), image(y, x).second.end());
261 }
262 }
263 }
264 }
265 }
266 }
267
268 return out;
269}
270
271bool FPGATrackSimHoughTransform_d0phi0_Tool::passThreshold(Image const & image, unsigned x, unsigned y) const
272{
273 // Pass window threshold
274 unsigned width = m_threshold.size() / 2;
275 if (x < width || (image.size(1) - x) < width) return false;
276 for (unsigned i = 0; i < m_threshold.size(); i++)
277 if (image(y, x - width + i).first < m_threshold[i]) return false;
278
279 // Pass local-maximum check
281 for (int j = -m_localMaxWindowSize; j <= m_localMaxWindowSize; j++)
282 for (int i = -m_localMaxWindowSize; i <= m_localMaxWindowSize; i++) {
283 if (i == 0 && j == 0) continue;
284 if (y + j < image.size(0) && x + i < image.size(1)) {
285 if (image(y+j, x+i).first > image(y, x).first) return false;
286 if (image(y+j, x+i).first == image(y, x).first) {
287 if (image(y+j, x+i).second.size() > image(y, x).second.size()) return false;
288 if (image(y+j, x+i).second.size() == image(y, x).second.size()
289 && j <= 0 && i <= 0) return false;
290 }
291 }
292 }
293
294 return true;
295}
296
298// Helpers
299
300
301// Quantizes val, given a range [min, max) split into nSteps. Returns the bin below.
302static inline int quant(double min, double max, unsigned nSteps, double val)
303{
304 return static_cast<int>((val - min) / (max - min) * nSteps);
305}
306
307// Returns the lower bound of the bin specified by step
308static inline double unquant(double min, double max, unsigned nSteps, int step)
309{
310 return min + (max - min) * step / nSteps;
311}
312
313template <typename T>
314static inline std::string to_string(std::vector<T> v)
315{
316 std::ostringstream oss;
317 oss << "[";
318 if (!v.empty()) {
319 std::copy(v.begin(), v.end()-1, std::ostream_iterator<T>(oss, ", "));
320 oss << v.back();
321 }
322 oss << "]";
323 return oss.str();
324}
325
326static inline std::string instance_name(std::string const & s)
327{
328 size_t pos = s.find_last_of('.');
329 if (pos != std::string::npos)
330 return s.substr(pos + 1);
331 return s;
332}
333
334double FPGATrackSimHoughTransform_d0phi0_Tool::yToX(double y, const std::shared_ptr<const FPGATrackSimHit> &hit) const
335{
336 double x = 0;
337
339 double r = hit->getR(); // TODO check this, and units
340 double phi_hit = hit->getGPhi(); // TODO check this, and units
341 x = -y/r + phi_hit;
342 }
343 else {
344 ATH_MSG_ERROR("yToX() not defined for the current m_par selection");
345 }
346
347 return x;
348}
349
350// Find the min/max x bins of the hit's line, in each y bin. Max is exclusive.
351// Note this assumes yToX is monotonic. Returns {0, 0} if hit lies out of bounds.
352std::pair<unsigned, unsigned> FPGATrackSimHoughTransform_d0phi0_Tool::yToXBins(size_t yBin_min, size_t yBin_max, const std::shared_ptr<const FPGATrackSimHit> &hit) const
353{
354 // Get float values
355 double x_min = yToX(m_bins_y[yBin_min], hit);
356 double x_max = yToX(m_bins_y[yBin_max], hit);
357 if (x_min > x_max) std::swap(x_min, x_max);
358 if (x_max < m_parMin[m_par_x] || x_min > m_parMax[m_par_x])
359 return { 0, 0 }; // out of bounds
360
361 // Get bins
362 int x_bin_min = quant(m_parMin[m_par_x], m_parMax[m_par_x], m_imageSize_x, x_min);
363 int x_bin_max = quant(m_parMin[m_par_x], m_parMax[m_par_x], m_imageSize_x, x_max) + 1; // exclusive
364
365 // Extend bins
366 if (!m_hitExtend_x.empty()) x_bin_min -= m_hitExtend_x[hit->getLayer()];
367 if (!m_hitExtend_x.empty()) x_bin_max += m_hitExtend_x[hit->getLayer()];
368
369 // Clamp bins
370 if (x_bin_min < 0) x_bin_min = 0;
371 if (x_bin_max > static_cast<int>(m_imageSize_x)) x_bin_max = m_imageSize_x;
372
373 return { x_bin_min, x_bin_max };
374}
375
377{
378 float pt = r.getY()*0.001; // convert to MeV
379 auto bounds = std::equal_range(fpgatracksim::QOVERPT_BINS.begin(),fpgatracksim::QOVERPT_BINS.end(),pt);
380 int sectorbin = bounds.first-fpgatracksim::QOVERPT_BINS.begin()-1;
381
382 // those bins are for tracks between the values, can't be below first value or more than the last value
383 if (sectorbin < 0) sectorbin = 0;
384 if (sectorbin > static_cast<int>(fpgatracksim::QOVERPT_BINS.size()-2)) sectorbin = fpgatracksim::QOVERPT_BINS.size()-2;
385 std::vector<module_t> modules;
386
387 for (unsigned int il = 0; il < r.getNLayers(); il++) {
388 if (r.getNHits_layer()[il] == 0) {
389 modules.push_back(-1);
390 layer_bitmask_t wc_layers = r.getWCLayers();
391 wc_layers |= (0x1 << il);
392 r.setWCLayers(wc_layers);
393
394 std::unique_ptr<FPGATrackSimHit> wcHit = std::unique_ptr<FPGATrackSimHit>(new FPGATrackSimHit());
395 wcHit->setHitType(HitType::wildcard);
396 wcHit->setLayer(il);
397 wcHit->setDetType(m_FPGATrackSimMapping->PlaneMap_1st(0)->getDetType(il));
398 r.setHits(il,std::vector<std::shared_ptr<const FPGATrackSimHit>>({std::move(wcHit)}));
399
400 }
401 else
402 modules.push_back(sectorbin);
403 }
404 const FPGATrackSimSectorBank* sectorbank = m_FPGATrackSimBankSvc->SectorBank_1st();
405 r.setSector(sectorbank->findSector(modules));
406}
407
408// Create road via hits only
409FPGATrackSimRoad FPGATrackSimHoughTransform_d0phi0_Tool::createRoad(const std::unordered_set<std::shared_ptr<const FPGATrackSimHit>> &hits, unsigned x, unsigned y) const {
410 // Get the road hits
411 std::vector<std::shared_ptr<const FPGATrackSimHit>> road_hits;
412 layer_bitmask_t hitLayers = 0;
413 for (const auto &hit : hits) {
414 road_hits.push_back(hit);
415 hitLayers |= 1 << hit->getLayer();
416 }
417
418 auto sorted_hits = ::sortByLayer(road_hits);
419 sorted_hits.resize(m_nLayers); // If no hits in last layer, return from sortByLayer will be too short
420
422 r.setRoadID(m_roads.size());
423 r.setPID(y * m_imageSize_y + x);
425 r.setHitLayers(hitLayers);
426 r.setHits(std::move(sorted_hits));
427 r.setSubRegion(m_subRegion);
428 r.setX(m_bins_x[x] + m_step_x/2);
429 r.setY(m_bins_y[y] + m_step_y/2);
430 return r;
431}
432
433// Creates a road from hits that pass through the given bin (x, y), and pushes it onto m_roads
434FPGATrackSimRoad FPGATrackSimHoughTransform_d0phi0_Tool::createRoad(std::vector<std::vector<std::shared_ptr<const FPGATrackSimHit>>> &&hits, layer_bitmask_t hitLayers, unsigned x, unsigned y) const
435{
437 r.setRoadID(m_roads.size());
438 r.setPID(y * m_imageSize_y + x);
439 if (m_useSectors) r.setSector(m_FPGATrackSimBankSvc->SectorBank_1st()->findSector(hits));
441 r.setHitLayers(hitLayers);
442 r.setHits(std::move(hits));
443 r.setSubRegion(m_subRegion);
444 r.setX(m_bins_x[x] + m_step_x/2);
445 r.setY(m_bins_y[y] + m_step_y/2);
446
447 return r;
448}
449
450
451// Creates a road from hits that pass through the given bin (x, y), and pushes it onto m_roads
452void FPGATrackSimHoughTransform_d0phi0_Tool::addRoad(const std::unordered_set<std::shared_ptr<const FPGATrackSimHit>> &hits, unsigned x, unsigned y)
453{
454 layer_bitmask_t hitLayers = 0;
455 for (auto const & hit : hits) {
456 hitLayers |= 1 << hit->getLayer();
457 }
458
459 auto sorted_hits = ::sortByLayer(hits);
460 sorted_hits.resize(m_nLayers); // If no hits in last layer, return from sortByLayer will be too short
461 m_roads.push_back(createRoad(std::move(sorted_hits), hitLayers, x, y));
462}
463
464// Use this version of addRoad when hit tracing is turned off
465void FPGATrackSimHoughTransform_d0phi0_Tool::addRoad(const std::vector<std::shared_ptr<const FPGATrackSimHit>> &hits, unsigned x, unsigned y)
466{
467 // Get the road hits
468 std::vector<std::shared_ptr<const FPGATrackSimHit>> road_hits;
469 layer_bitmask_t hitLayers = 0;
470 for (const auto &hit : hits) {
471 if (m_subRegion >= 0 && !m_FPGATrackSimMapping->SubRegionMap()->isInRegion(m_subRegion, *hit)) continue;
472
473 // get bin scaling for the hit
474 unsigned bin_scale = 0;
475 for (unsigned i = 0; i < m_nCombineLayers; i++) {
476 for (int const layer : m_combineLayer2D[i]) {
477 if (hit->getLayer() == layer) {
478 bin_scale = m_binScale[layer];
479 }
480 }
481 }
482 if (bin_scale == 0) bin_scale = 1; //avoid divide by zero,use 1:1 scale
483 unsigned y_bin_min = floor(1.0 * y / bin_scale) * bin_scale;
484 unsigned y_bin_max = ceil(1.0 * y / bin_scale) * bin_scale;
485 if (y_bin_min == y_bin_max) y_bin_max++;
486
487 // Find the min/max x bins
488 auto xBins = yToXBins(y_bin_min, y_bin_max, hit);
489
490 if (x >= xBins.first && x < xBins.second && y >= y_bin_min && y < y_bin_max) {
491 road_hits.push_back(hit);
492 hitLayers |= 1 << hit->getLayer();
493 }
494 }
495
496 auto sorted_hits = ::sortByLayer(road_hits);
497 sorted_hits.resize(m_nLayers); // If no hits in last layer, return from sortByLayer will be too short
498
499 m_roads.push_back(createRoad(std::move(sorted_hits), hitLayers, x, y));
500}
501
502
503// For debug use
504void FPGATrackSimHoughTransform_d0phi0_Tool::drawImage(Image const & image, std::string const & name)
505{
506 m_monitorFile.cd();
507
508 TH2I h(name.c_str(), "Hough Transform;phi;d0 (mm)",
511 );
512
513 for (unsigned y = 0; y < m_imageSize_y; y++)
514 for (unsigned x = 0; x < m_imageSize_x; x++)
515 h.SetBinContent(x+1, y+1, image(y, x).first); // +1 since root bins are 1-indexed
516
517 h.Write();
518}
519
#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 *)
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)
virtual StatusCode getRoads(const std::vector< std::shared_ptr< const FPGATrackSimHit > > &hits, std::vector< FPGATrackSimRoad > &roads) override
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)