Analysis Software
Documentation for sPHENIX simulation software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
HoughTransformSeeder.hpp
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file HoughTransformSeeder.hpp
1 // This file is part of the Acts project.
2 //
3 // Copyright (C) 2022 CERN for the benefit of the Acts project
4 //
5 // This Source Code Form is subject to the terms of the Mozilla Public
6 // License, v. 2.0. If a copy of the MPL was not distributed with this
7 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 
9 // @file HoughTransformSeeder.hpp
10 // @author Riley Xu then modified to ACTS by Jahred Adelman
11 // @brief Implements track-seeding using a Hough transform.
12 //
13 // Using the Lorentz force equation, one can relate the phi of a track and the
14 // coordinate of a single hit:
15 //
16 // A * q / pT = sin(phi_track - phi_hit) / r
17 //
18 // where
19 // A : 3 * 10^-4 GeV / (c*mm*e) for the ATLAS B field (to be configured)
20 // q : charge of the particle
21 // pT : transverse momentum
22 // r : cylindrical radius of the hit from the beamline
23 // phi : in radians
24 //
25 // Here, q/pT and phi_track (ie at phi at perigee) are unknown. This equation
26 // forms a line in q/pT vs phi_track spac since the sin function above can be
27 // approximated with sin(x) ~ x. Each hit will have its own line based on its
28 // position (phi and r). However, note that hits belonging to the same track
29 // will have lines that intersect at the track's q/pT and phi. In this manner,
30 // we can conduct pattern -matching by looking for intersections of these pT-phi
31 // lines.
32 //
33 // In other words, given some assumed q/pT for the track one can take the phi
34 // and r for a hit and convert that to what the phi(perigee) for a track must
35 // have been. We loop over the q/pT bins, and at the true value all the hits
36 // should line up in the same bin
37 //
38 // To easily find intersections, we first pixelate (equivalently, we make a 2d
39 // histogram from) the graph of all the hit's lines in q/pT vs phi_track space.
40 // We then apply a convolution (i.e. a scanning window) to pick out points with
41 // multiple lines going through them. These points become our seed.
42 //
43 // In principle the Hough transform can be used for an entire region (i.e. .2
44 // phi x .2 eta) or larger. However this can lead to an excessive number of
45 // hits/lines in the transform houghHist, leading to spurious intersections.
46 // Instead, we can use multiple transforms that each cover a slice in z0, and
47 // simply combine all the seeds found. These are the subregions
48 //
49 // References:
50 // Martensson Thesis:
51 // http://uu.diva-portal.org/smash/get/diva2:1341509/FULLTEXT01.pdf
52 //
53 
54 // We adopt the following nomenclature within this class:
55 // houghHist: The 'graph' in q/pT vs phi_track space, filled with a line
56 // calculated as above for each hit. point: A specific q/pT and phi_track
57 // bin in the above houghHist; i.e. what is normally called a pixel
58 // but I don't want to confuse this with the detector type. A
59 // point's value is the number of lines that go through it.
60 //
61 // For the first iteration, x refers to phi_track, and y refers to q/pT,
62 // although this should remain flexible. These are set via the variables m_par_x
63 // and m_par_y.
64 //
65 // NOTE: y=0 represents the lowest q/pT bin, x=0 represents the lowest
66 // phi(perigee) bin
67 // houghHist[y=0][x=0] : lowest q/pT and lowest phi_track bin
68 // houghHist[y=size-1][x=0] : highest q/pT and lowest phi_track bin
69 //
70 
71 #pragma once
72 
86 
87 #include <cstddef>
88 #include <memory>
89 #include <string>
90 #include <unordered_set>
91 #include <utility>
92 #include <vector>
93 
94 namespace ActsExamples {
95 struct AlgorithmContext;
96 } // namespace ActsExamples
97 
101 
102 using FieldCorrector = Acts::Delegate<ResultDouble(
103  unsigned, double, double)>; // (unsigned region, double y, double r)
104 using LayerIDFinder = Acts::Delegate<ResultUnsigned(
105  double)>; // (double r) this function will map the r of a measurement to a
106  // layer.
107 using SliceTester = Acts::Delegate<ResultBool(
108  double, unsigned, int)>; // (double z,unsigned layer, int slice) returns
109  // true if measurement in slice
110 
111 namespace Acts {
112 class TrackingGeometry;
113 }
114 
115 namespace ActsExamples {
122 // The unsigned is a counter that will point to a spacepoint or to a measurement
123 // object
124 
132 
133 enum HoughHitType { SP = 0, MEASUREMENT = 1 };
134 
138  unsigned layer;
139  double phi;
140  double radius;
141  double z;
142  std::vector<Index> indices;
144  HoughMeasurementStruct(unsigned l, double p, double r, double thez,
145  std::vector<Index>& i, HoughHitType t)
146  : layer(l), phi(p), radius(r), z(thez), indices(i), type(t) {}
147 };
148 
149 thread_local std::vector<std::shared_ptr<HoughMeasurementStruct>>
151 
153 class HoughTransformSeeder final : public IAlgorithm {
154  public:
155  struct Config {
163  std::vector<std::string> inputSpacePoints;
171  std::shared_ptr<const Acts::TrackingGeometry> trackingGeometry;
179  std::vector<Acts::GeometryIdentifier> geometrySelection;
180 
183 
184  // Subregions are ways to divide up hits for the Hough Transform. Just as
185  // one simple example, one may consider that hits with z < 50 mm belong to
186  // one subregion, and hits with z > -50 mm belong to a second subregion.
187  // Note that hits even in this toy example belong to more than one
188  // subregion. But since not all hits are considered this provides a way to
189  // reduce potential combinatorics
190 
191  std::vector<int> subRegions = {
192  -1}; // -1 for entire region (no slicing), but this can be more than
193  // one region if data are sliced
194 
195  unsigned nLayers = 10; // total number of layers
196 
197  float xMin = 0; // minphi
198  float xMax = 2 * 3.14159; // maxphi
199  float yMin = -1.0; // min q/pt, -1/1 GeV
200  float yMax = 1.0; // max q/pt, +1/1 GeV
201 
209 
210  unsigned houghHistSize_x = 7000; // i.e. number of bins in phi_track
211  unsigned houghHistSize_y = 216; // i.e. number of bins in q/pT
212 
216  std::vector<unsigned> hitExtend_x = {
217  1, 1, 0, 0, 0, 0,
218  0, 0, 0, 0}; // Hit lines will fill extra bins in x by this amount on
219  // each side, size == nLayers
220 
222  std::vector<int> threshold = {
223  9}; // Minimum number of measurements per bin to accept as a
224  // prototrack/seed. Right now this is a single number, can be
225  // expanded in the future if we want to be more clever
226 
227  int localMaxWindowSize = 0; // Only create candidates from a local maximum
228 
229  double kA = 0.0003; // Assume B = 2T constant. Can apply corrections to
230  // this with fieldCorrection function
231  // This 3e-4 comes from the 2T field when converted to
232  // units of GeV / (c*mm*e)
233 
234  // it's up to the user to connect these to the functions they want to use
238  };
239 
245 
250  ProcessCode execute(const AlgorithmContext& ctx) const final;
251 
253  const Config& config() const { return m_cfg; }
254 
255  double getMinX() const { return m_cfg.xMin; }
256  double getMaxX() const { return m_cfg.xMax; }
257  double getMinY() const { return m_cfg.yMin; }
258  double getMaxY() const { return m_cfg.yMax; }
259  unsigned getThreshold() const {
260  return m_cfg.threshold[0]; // for now this is just one number in the
261  // vector, can be more in the future
262  }
263  std::vector<int> getSubRegions() const { return m_cfg.subRegions; }
264 
265  double yToX(double y, double r,
266  double phi) const; // calculate the hough equation
267 
268  private:
270  std::unique_ptr<const Acts::Logger> m_logger;
271  const Acts::Logger& logger() const { return *m_logger; }
272 
274  "OutputProtoTracks"};
275  std::vector<std::unique_ptr<ReadDataHandle<SimSpacePointContainer>>>
277 
279  "InputMeasurements"};
280 
282  this, "InputSourceLinks"};
283 
286 
287  double m_step_x; // step size of the bin boundaries in x
288  double m_step_y; // step size of the bin boundaries in y
291  std::vector<double> m_bins_x; // size == m_houghHistSize_x + 1.
292  std::vector<double> m_bins_y; // size == m_houghHistSize_y + 1
293 
295  // Core functions, the second/ one calls the first one per layer
296  HoughHist createLayerHoughHist(unsigned layer, int subregion) const;
297  HoughHist createHoughHist(int subregion) const;
298 
300  // Helpers
301  std::pair<unsigned, unsigned> yToXBins(size_t yBin_min, size_t yBin_max,
302  double r, double phi,
303  unsigned layer)
304  const; // given y bins, return x bins passed that need to be filled in
305  // the HoughHist, including extensions
306 
307  unsigned getExtension(unsigned y, unsigned layer) const; // return extensions
308  bool passThreshold(HoughHist const& houghHist, unsigned x,
309  unsigned y) const; // did we pass extensions?
310  void drawHoughHist(HoughHist const& houghHist,
311  std::string const& name); // for making pretty plots
312  std::vector<std::vector<int>> getComboIndices(std::vector<size_t>& sizes)
313  const; // useful to find all candidates from given bins that pass
314  // (looping over hit combinatorics)
315 
316  // functions to clean up the code and convert SPs and measurements to the
317  // HoughMeasurement format
318  void addMeasurements(const AlgorithmContext& ctx) const;
319  void addSpacePoints(const AlgorithmContext& ctx) const;
320 };
321 
322 } // namespace ActsExamples