Belle II Software development
SingleHoughSpaceFastInterceptFinder.cc
1/**************************************************************************
2 * basf2 (Belle II Analysis Software Framework) *
3 * Author: The Belle II Collaboration *
4 * *
5 * See git log for contributors and copyright holders. *
6 * This file is licensed under LGPL-3.0, see LICENSE.md. *
7 **************************************************************************/
8#include <tracking/vxdHoughTracking/findlets/SingleHoughSpaceFastInterceptFinder.h>
9#include <tracking/vxdHoughTracking/entities/VXDHoughState.h>
10#include <tracking/spacePointCreation/SpacePoint.h>
11#include <tracking/spacePointCreation/SpacePointTrackCand.h>
12#include <tracking/trackingUtilities/utilities/StringManipulation.h>
13#include <tracking/trackingUtilities/utilities/Algorithms.h>
14#include <vxd/dataobjects/VxdID.h>
15#include <framework/core/ModuleParamList.h>
16
17using namespace Belle2;
18using namespace TrackingUtilities;
19using namespace vxdHoughTracking;
20
24
25void SingleHoughSpaceFastInterceptFinder::exposeParameters(ModuleParamList* moduleParamList, const std::string& prefix)
26{
27 Super::exposeParameters(moduleParamList, prefix);
28
29 moduleParamList->addParameter(TrackingUtilities::prefixed(prefix, "maximumRecursionLevel"), m_maxRecursionLevel,
30 "Maximum recursion level for the fast Hough trafo algorithm.", m_maxRecursionLevel);
31
32 moduleParamList->addParameter(TrackingUtilities::prefixed(prefix, "nAngleSectors"), m_nAngleSectors,
33 "Number of angle sectors (= x-axis) dividing the Hough space.", m_nAngleSectors);
34
35 moduleParamList->addParameter(TrackingUtilities::prefixed(prefix, "nVerticalSectors"), m_nVerticalSectors,
36 "Number of vertical sectors (= y-axis) dividing the Hough space.", m_nVerticalSectors);
37
38 moduleParamList->addParameter(TrackingUtilities::prefixed(prefix, "verticalHoughSpaceSize"), m_verticalHoughSpaceSize,
39 "Vertical size of the Hough space.", m_verticalHoughSpaceSize);
40
41 moduleParamList->addParameter(TrackingUtilities::prefixed(prefix, "HoughSpaceMinimumX"), m_minimumX,
42 "Minimum x value of the Hough space.", m_minimumX);
43
44 moduleParamList->addParameter(TrackingUtilities::prefixed(prefix, "HoughSpaceMaximumX"), m_maximumX,
45 "Maximum x value of the Hough space.", m_maximumX);
46
47 moduleParamList->addParameter(TrackingUtilities::prefixed(prefix, "minimumHSClusterSize"), m_MinimumHSClusterSize,
48 "Maximum x value of the Hough space.", m_MinimumHSClusterSize);
49
50 moduleParamList->addParameter(TrackingUtilities::prefixed(prefix, "maximumHSClusterSize"), m_MaximumHSClusterSize,
51 "Maximum x value of the Hough space.", m_MaximumHSClusterSize);
52
53 moduleParamList->addParameter(TrackingUtilities::prefixed(prefix, "maximumHSClusterSizeX"), m_MaximumHSClusterSizeX,
54 "Maximum x value of the Hough space.", m_MaximumHSClusterSizeX);
55
56 moduleParamList->addParameter(TrackingUtilities::prefixed(prefix, "maximumHSClusterSizeY"), m_MaximumHSClusterSizeY,
57 "Maximum x value of the Hough space.", m_MaximumHSClusterSizeY);
58
59}
60
62{
64
65 const ushort maxRecursionLevelFromSectors = ceil(log2(std::max(m_nAngleSectors, m_nVerticalSectors))) - 1;
66 m_maxRecursionLevel = std::max(maxRecursionLevelFromSectors, m_maxRecursionLevel);
67 B2ASSERT("The maximum number of recursions (maximumRecursionLevel) must not be larger than " << c_maxAllowedRecusionLevel <<
68 ", but it is " << m_maxRecursionLevel <<
69 ", please choose a smaller value for maximumRecursionLevel, and / or for nAngleSectors and / or nVerticalSectors.",
71 B2ASSERT("The maximum number of angleSectors must not be larger than " << c_maxHSSectorNumber <<
72 ", but it is " << m_nAngleSectors << ", please choose a smaller value for nAngleSectors.",
74 B2ASSERT("The maximum number of verticalSectors must not be larger than " << c_maxHSSectorNumber <<
75 ", but it is " << m_nVerticalSectors << ", please choose a smaller value for nAngleSectors.",
77
80
81 for (ushort i = 0; i < m_nAngleSectors; i++) {
82 float x = m_minimumX + m_unitX * (float)i;
83 float xc = x + 0.5 * m_unitX;
84
85 m_HSSinValuesLUT[i] = sin(x);
86 m_HSCosValuesLUT[i] = cos(x);
87 m_HSCenterSinValuesLUT[i] = sin(xc);
88 m_HSCenterCosValuesLUT[i] = cos(xc);
89 }
92
93 B2DEBUG(29, "HS size x: " << (m_maximumX - m_minimumX) << " HS size y: " << m_verticalHoughSpaceSize <<
94 " unitX: " << m_unitX << " unitY: " << m_unitY);
95}
96
97
98void SingleHoughSpaceFastInterceptFinder::apply(std::vector<VXDHoughState>& hits,
99 std::vector<std::vector<VXDHoughState*>>& rawTrackCandidates)
100{
101 m_trackCandidates.clear();
102 m_activeSectorsMap.clear();
104
105 const std::vector<VXDHoughState*> currentEventHitList = TrackingUtilities::as_pointers<VXDHoughState>(hits);
106
107 fastInterceptFinder2d(currentEventHitList, 0, m_nAngleSectors, 0, m_nVerticalSectors, 0);
108
110
111 for (auto& trackCand : m_trackCandidates) {
112 // sort for layer, and 2D radius in case of same layer before storing as SpacePointTrackCand
113 // outer hit goes first, as later on tracks are build from outside to inside
114 std::sort(trackCand.begin(), trackCand.end(),
115 [](const VXDHoughState * a, const VXDHoughState * b) {
116 return
117 (a->getDataCache().layer > b->getDataCache().layer) or
118 (a->getDataCache().layer == b->getDataCache().layer
119 and a->getHit()->getPosition().Perp2() > b->getHit()->getPosition().Perp2());
120 });
121
122 rawTrackCandidates.emplace_back(trackCand);
123 }
124
125 B2DEBUG(29, "m_trackCandidates.size: " << m_trackCandidates.size());
126
127}
128
129
130void SingleHoughSpaceFastInterceptFinder::fastInterceptFinder2d(const std::vector<VXDHoughState*>& hits,
131 const ushort xmin, const ushort xmax, const ushort ymin, const ushort ymax, const ushort currentRecursion)
132{
133 std::vector<VXDHoughState*> containedHits;
134 containedHits.reserve(hits.size());
135 std::bitset<8> layerHits; /* For layer filter */
136
137 if (currentRecursion == m_maxRecursionLevel + 1) return;
138
139 // these int-divisions can cause {min, center} or {center, max} to be the same, which is a desired behaviour
140 const ushort centerx = xmin + (ushort)((xmax - xmin) >> 1); // >> 1 equals a division by 2
141 const ushort centery = ymin + (ushort)((ymax - ymin) >> 1); // >> 1 equals a division by 2
142 const ushort xIndexCache[3] = {xmin, centerx, xmax};
143 const ushort yIndexCache[3] = {ymin, centery, ymax};
144
145 for (int i = 0; i < 2 ; ++i) {
146 const ushort left = xIndexCache[i];
147 const ushort right = xIndexCache[i + 1];
148 const ushort localIndexX = left;
149
150 if (left == right) continue;
151
152 const float& sinLeft = m_HSSinValuesLUT[left];
153 const float& cosLeft = m_HSCosValuesLUT[left];
154 const float& sinRight = m_HSSinValuesLUT[right];
155 const float& cosRight = m_HSCosValuesLUT[right];
156
157 // the sin and cos of the current center can't be stored in a LUT, as the number of possible centers
158 // is quite large and the logic would become rather complex, so this is just an approximation which is good enough
159 const float sinCenter = m_HSCenterSinValuesLUT[(left + right) >> 1]; // >> 1 equals a division by 2
160 const float cosCenter = m_HSCenterCosValuesLUT[(left + right) >> 1]; // >> 1 equals a division by 2
161
162 for (int j = 0; j < 2; ++j) {
163 const ushort lowerIndex = yIndexCache[j];
164 const ushort upperIndex = yIndexCache[j + 1];
165
166 if (lowerIndex == upperIndex) continue;
167
168 const ushort localIndexY = lowerIndex;
169 // Sector counting for y starts at positive values, i.e. the topmost sector has index 0,
170 // and the bottommost sector has the highest index
171 const float localUpperCoordinate = m_verticalHoughSpaceSize - m_unitY * lowerIndex;
172 const float localLowerCoordinate = m_verticalHoughSpaceSize - m_unitY * upperIndex;
173
174 // reset layerHits and containedHits
175 layerHits = 0;
176 containedHits.clear();
177 for (VXDHoughState* hit : hits) {
178
179 const VXDHoughState::DataCache& hitData = hit->getDataCache();
180 const float& m = hitData.xConformal;
181 const float& a = hitData.yConformal;
182
183 const float derivativeyLeft = m * -sinLeft + a * cosLeft;
184 const float derivativeyRight = m * -sinRight + a * cosRight;
185 const float derivativeyCenter = m * -sinCenter + a * cosCenter;
186
187 // Only interested in the rising arm of the sinosoidal curves.
188 // Thus if derivative on both sides of the cell is negative, ignore and continue.
189 if (derivativeyLeft < 0 and derivativeyRight < 0 and derivativeyCenter < 0) continue;
190
191 const float yLeft = m * cosLeft + a * sinLeft;
192 const float yRight = m * cosRight + a * sinRight;
193 const float yCenter = m * cosCenter + a * sinCenter;
194
195 /* Check if HS-parameter curve is inside (or outside) actual sub-HS */
196 if ((yLeft <= localUpperCoordinate and yRight >= localLowerCoordinate) or
197 (yCenter <= localUpperCoordinate and yLeft >= localLowerCoordinate and yRight >= localLowerCoordinate) or
198 (yCenter >= localLowerCoordinate and yLeft <= localUpperCoordinate and yRight <= localUpperCoordinate)) {
199 layerHits[hitData.layer] = true;
200 containedHits.emplace_back(hit);
201 }
202 }
203
204 if (layerFilter(layerHits) > 0) {
205 // recursive call of fastInterceptFinder2d, until currentRecursion == m_maxRecursionLevel
206 if (currentRecursion < m_maxRecursionLevel) {
207 fastInterceptFinder2d(containedHits, left, right, lowerIndex, upperIndex, currentRecursion + 1);
208 } else {
209 // As usual in creating a 2D-array as 1D, calculate the global index as
210 // xIndex + yIndex * ySize
211 // A bit more complicated here though, since the y-axis is inverted on the fly.
212 // Instead of starting the coordinate system in the top-left corner, it has to start in the bottom-left corner
213 // for creating HS clusters from bottom to top and from left to right, so this becomes
214 // xIndex + ySize * (ySize - yIndex) (eq. 0)
215 const uint globalIndex = localIndexX + c_maxHSSectorNumber * (c_maxHSSectorNumber - localIndexY);
216 m_activeSectorsMap.insert({globalIndex, containedHits});
217 m_activeSectorsIndices.push_back(globalIndex);
218 }
219 }
220 }
221 }
222}
223
224
226{
227 m_clusterCount = 1;
228
229 // Sort vector to create HS clusters from bottom left to top right
230 std::sort(m_activeSectorsIndices.begin(), m_activeSectorsIndices.end());
231
232 for (const uint& currentGlobalSectorIndex : m_activeSectorsIndices) {
233
234 const auto currentCellHits = m_activeSectorsMap.find(currentGlobalSectorIndex);
235 if (currentCellHits == m_activeSectorsMap.end()) {
236 continue;
237 }
238
239 // Get local (x, y) indices out of the globalSectorIndex by reverting (eq. 0)
240 // Bitwise and with c_xIndexBitMask (which is (c_maxHSSectorNumber - 1))
241 // (currentGlobalSectorIndex & c_xIndexBitMask)
242 // equals % c_maxHSSectorNumber, i.e.
243 // currentGlobalSectorIndex % c_maxHSSectorNumber.
244 // Bitshifting right by c_maxAllowedRecusionLevel, i.e.
245 // >> c_maxAllowedRecusionLevel
246 // equals a division by 2^c_maxAllowedRecusionLevel but is much faster.
247 m_clusterInitialPosition = std::make_pair((currentGlobalSectorIndex & c_xIndexBitMask),
248 c_maxHSSectorNumber - (currentGlobalSectorIndex >> c_maxAllowedRecusionLevel));
249 m_clusterSize = 1;
250
252 for (VXDHoughState* hit : currentCellHits->second) {
253 m_currentTrackCandidate.emplace_back(hit);
254 }
255 // this sector now has been used and the hits have been processed, so it can be removed from the map
256 m_activeSectorsMap.erase(currentGlobalSectorIndex);
257
258 // Check for HS sectors connected to each other which could form a cluster
259 DepthFirstSearch(currentGlobalSectorIndex);
260 // if cluster valid (i.e. not too small and not too big): finalize!
264 }
266 }
267}
268
269void SingleHoughSpaceFastInterceptFinder::DepthFirstSearch(const uint lastGlobalSectorIndex)
270{
272
273 // Get local (x, y) indices out of the globalSectorIndex by reverting (eq. 0)
274 // Bitwise and with c_xIndexBitMask (which is (c_maxHSSectorNumber - 1))
275 // (currentGlobalSectorIndex & c_xIndexBitMask)
276 // equals % c_maxHSSectorNumber, i.e.
277 // currentGlobalSectorIndex % c_maxHSSectorNumber.
278 // Bitshifting right by c_maxAllowedRecusionLevel, i.e.
279 // >> c_maxAllowedRecusionLevel
280 // equals a division by 2^c_maxAllowedRecusionLevel but is much faster.
281 const ushort lastLocalIndexX = (lastGlobalSectorIndex & c_xIndexBitMask);
282 const ushort lastLocalIndexY = c_maxHSSectorNumber - (lastGlobalSectorIndex >> c_maxAllowedRecusionLevel);
283
284 // For the iterative / recursive search, just check the direct neighbours in x and y direction
285 for (ushort currentLocalIndexY = lastLocalIndexY; currentLocalIndexY >= lastLocalIndexY - 1; currentLocalIndexY--) {
286 if (std::abs(static_cast<short>(m_clusterInitialPosition.second) - static_cast<short>(currentLocalIndexY)) >=
288 or m_clusterSize >= m_MaximumHSClusterSize or currentLocalIndexY > m_nVerticalSectors) {
289 return;
290 }
291
292 for (ushort currentLocalIndexX = lastLocalIndexX; currentLocalIndexX <= lastLocalIndexX + 1; currentLocalIndexX++) {
293 if (std::abs(static_cast<short>(m_clusterInitialPosition.first) - static_cast<short>(currentLocalIndexX)) >= m_MaximumHSClusterSizeX
294 or m_clusterSize >= m_MaximumHSClusterSize or currentLocalIndexX > m_nAngleSectors) {
295 return;
296 }
297
298 // Calculate the global index for this sector by applying (eq. 0)
299 const uint currentGlobalSectorIndex = currentLocalIndexX + c_maxHSSectorNumber * (c_maxHSSectorNumber - currentLocalIndexY);
300 // If currentGlobalSectorIndex == lastGlobalSectorIndex, the sector in check is the "parent" we came from
301 // and thus has already been checked, so continue
302 if (currentGlobalSectorIndex == lastGlobalSectorIndex) {
303 continue;
304 }
305
306 // first check bounds to avoid out-of-bound array access
307 // as they are uints, they are always >= 0, and in case of an overflow they would be too large
308 if (currentLocalIndexX < m_nAngleSectors and currentLocalIndexY < m_nVerticalSectors) {
309
310 const auto activeSectorHits = m_activeSectorsMap.find(currentGlobalSectorIndex);
311 // Only continue searching if the current cluster is smaller than the maximum cluster size
312 if (activeSectorHits != m_activeSectorsMap.end()) {
314
315 // No need to check whether currentGlobalSectorIndex exists as a key in m_activeSectorsMap as they were
316 // created at the same time so it's certain the key exists.
317 for (VXDHoughState* hit : activeSectorHits->second) {
318 if (not TrackingUtilities::is_in(hit, m_currentTrackCandidate)) {
319 m_currentTrackCandidate.emplace_back(hit);
320 }
321 }
322 // this sector now has been used and the hits have been processed, so it can be removed from the map
323 m_activeSectorsMap.erase(currentGlobalSectorIndex);
324
325 // search in the next Hough Space cells...
326 DepthFirstSearch(currentGlobalSectorIndex);
327 }
328 }
329 }
330 }
331}
The Module parameter list class.
virtual void exposeParameters(ModuleParamList *moduleParamList, const std::string &prefix)
std::unordered_map< uint, std::vector< VXDHoughState * > > m_activeSectorsMap
Map containing only active HS sectors, i.e.
ushort m_maxRecursionLevel
maximum number of recursive calls of FastInterceptFinder2d
static unsigned short layerFilter(const std::bitset< 8 > &layer)
layer filter, checks if at least hits from 3 layers are in a set of hits
std::array< float, c_maxLUTSize > m_HSSinValuesLUT
Look-Up-Tables for values as cache to speed up calculation sine values of the Hough Space sector boar...
std::vector< uint > m_activeSectorsIndices
Cache the global indices of the active sectors for sorted access.
std::vector< std::vector< VXDHoughState * > > m_trackCandidates
vector containing track candidates, consisting of the found intersection values in the Hough Space
ushort m_nVerticalSectors
number of sectors of the Hough Space on the vertical axis
float m_minimumX
minimum x value of the Hough Space, defaults to the value for u-side
std::pair< ushort, ushort > m_clusterInitialPosition
start cell of the recursive cluster finding in the Hough Space
std::array< float, c_maxHSSectorNumber > m_HSCenterSinValuesLUT
sine values of the Hough Space sector center coordinates
TrackingUtilities::Findlet< VXDHoughState, std::vector< VXDHoughState * > > Super
Parent class.
void fastInterceptFinder2d(const std::vector< VXDHoughState * > &hits, const ushort xmin, const ushort xmax, const ushort ymin, const ushort ymax, const ushort currentRecursion)
find intercepts in the 2D Hough Space by recursively calling itself until no hits are assigned to a g...
ushort m_MaximumHSClusterSizeX
maximum cluster size in x of sectors belonging to intercepts in the Hough Space
ushort m_MaximumHSClusterSize
maximum cluster size of sectors belonging to intercepts in the Hough Space
static const ushort c_maxAllowedRecusionLevel
Define some magic numbers Maximum allowed recursion level.
ushort m_nAngleSectors
number of sectors of the Hough Space on the horizontal axis
static constexpr ushort c_maxHSSectorNumber
Maximum number of HS sectors in x and y, also the size of some of the the Look-Up-Tables (LUTs) below...
std::array< float, c_maxLUTSize > m_HSCosValuesLUT
cosine values of the Hough Space sector boarder coordinates
std::array< float, c_maxHSSectorNumber > m_HSCenterCosValuesLUT
cosine values of the Hough Space sector center coordinates
std::vector< VXDHoughState * > m_currentTrackCandidate
the current track candidate
ushort m_MinimumHSClusterSize
minimum cluster size of sectors belonging to intercepts in the Hough Space
void exposeParameters(ModuleParamList *moduleParamList, const std::string &prefix) override
Expose the parameters of the sub findlets.
void apply(std::vector< VXDHoughState > &hits, std::vector< std::vector< VXDHoughState * > > &rawTrackCandidates) override
Load in the prepared hits and create track candidates for further processing like hit filtering and f...
float m_verticalHoughSpaceSize
vertical size of the Hough Space, defaults to the value for u-side
static constexpr ushort c_xIndexBitMask
Bit mask for coordinate transformation, creates all-ones for the coordinate system conversion.
float m_maximumX
maximum x value of the Hough Space, defaults to the value for u-side
void DepthFirstSearch(const uint lastGlobalSectorIndex)
Perform depth first search recursive algorithm to find clusters in the Hough Space.
ushort m_MaximumHSClusterSizeY
maximum cluster size in y of sectors belonging to intercepts in the Hough Space
Simple container for hit information to be used during intercept finding.
void addParameter(const std::string &name, T &paramVariable, const std::string &description, const T &defaultValue)
Adds a new parameter to the module list.
Abstract base class for different kinds of events.
Cache containing the most important information of this state which will often be needed.
float yConformal
conformal transformed y coordinate of this hit
unsigned short layer
Geometrical Layer this state is based on.
float xConformal
conformal transformed x coordinate of this hit