Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:11:01

0001 // This file is part of the ACTS project.
0002 //
0003 // Copyright (C) 2016 CERN for the benefit of the ACTS project
0004 //
0005 // This Source Code Form is subject to the terms of the Mozilla Public
0006 // License, v. 2.0. If a copy of the MPL was not distributed with this
0007 // file, You can obtain one at https://mozilla.org/MPL/2.0/.
0008 
0009 #include <algorithm>
0010 #include <tuple>
0011 
0012 template <class identifier_t>
0013 template <class PointType>
0014 void Acts::HoughTransformUtils::HoughPlane<identifier_t>::fill(
0015     const PointType& measurement, const HoughAxisRanges& axisRanges,
0016     LineParametrisation<PointType> linePar,
0017     LineParametrisation<PointType> widthPar, const identifier_t& identifier,
0018     unsigned layer, YieldType weight) {
0019   // loop over all bins in the first coordinate to populate the line
0020   for (std::size_t xBin = 0; xBin < m_cfg.nBinsX; xBin++) {
0021     // get the x-coordinate for the given bin
0022     auto x = binCenter(axisRanges.xMin, axisRanges.xMax, m_cfg.nBinsX, xBin);
0023     // now evaluate the line equation provided by the user
0024     CoordType y = linePar(x, measurement);
0025     CoordType dy = widthPar(x, measurement);
0026     // translate the y-coordinate range to a bin range
0027     int yBinDown =
0028         binIndex(axisRanges.yMin, axisRanges.yMax, m_cfg.nBinsY, y - dy);
0029     int yBinUp =
0030         binIndex(axisRanges.yMin, axisRanges.yMax, m_cfg.nBinsY, y + dy);
0031     // now we can loop over the bin range to fill the corresponding cells
0032     for (int yBin = yBinDown; yBin <= yBinUp; ++yBin) {
0033       // skip 'out of bounds' cases
0034       if (yBin >= static_cast<int>(m_cfg.nBinsY) || yBin < 0) {
0035         continue;
0036       }
0037       fillBin(xBin, yBin, identifier, layer, weight);
0038     }
0039   }
0040 }
0041 
0042 template <class identifier_t>
0043 std::span<const identifier_t, std::dynamic_extent>
0044 Acts::HoughTransformUtils::HoughCell<identifier_t>::getHits() const {
0045   std::span<const identifier_t, std::dynamic_extent> hits(m_hits.begin(),
0046                                                           m_iHit);
0047   return hits;
0048 }
0049 
0050 template <class identifier_t>
0051 std::span<const unsigned, std::dynamic_extent>
0052 Acts::HoughTransformUtils::HoughCell<identifier_t>::getLayers() const {
0053   std::span<const unsigned, std::dynamic_extent> layers(m_layers.begin(),
0054                                                         m_iLayer);
0055 
0056   return layers;
0057 }
0058 
0059 template <class identifier_t>
0060 void Acts::HoughTransformUtils::HoughCell<identifier_t>::fill(
0061     const identifier_t& identifier, unsigned layer, YieldType weight) {
0062   // add the hit to the list of hits in the cell
0063 
0064   if (m_iHit != 0 && m_hits[m_iHit - 1] == identifier) {
0065     return;
0066   }
0067 
0068   if (m_iHit == m_hits.size()) {
0069     m_hits.resize(m_hits.size() + m_assignBatch);
0070   }
0071 
0072   if (m_iLayer == m_layers.size()) {
0073     m_layers.resize(m_layers.size() + m_assignBatch);
0074   }
0075 
0076   m_hits[m_iHit] = identifier;
0077   m_layers[m_iLayer] = layer;
0078 
0079   m_iHit += 1;
0080   m_iLayer += 1;
0081 
0082   m_nHits += weight;
0083   m_nLayers += weight;
0084 }
0085 
0086 template <class identifier_t>
0087 void Acts::HoughTransformUtils::HoughCell<identifier_t>::reset() {
0088   m_nHits = 0;
0089   m_nLayers = 0;
0090 
0091   m_iLayer = 0;
0092   m_iHit = 0;
0093 }
0094 
0095 template <class identifier_t>
0096 Acts::HoughTransformUtils::HoughPlane<identifier_t>::HoughPlane(
0097     const HoughPlaneConfig& cfg)
0098     : m_cfg(cfg),
0099       m_houghHist(Axis(0, m_cfg.nBinsX, m_cfg.nBinsX),
0100                   Axis(0, m_cfg.nBinsY, m_cfg.nBinsY)) {
0101   // instantiate our histogram.
0102   // m_houghHist = HoughHist(Axis(0, m_cfg.nBinsX, m_cfg.nBinsX), Axis(0,
0103   // m_cfg.nBinsY, m_cfg.nBinsY));
0104 }
0105 template <class identifier_t>
0106 void Acts::HoughTransformUtils::HoughPlane<identifier_t>::fillBin(
0107     std::size_t binX, std::size_t binY, const identifier_t& identifier,
0108     unsigned layer, double w) {
0109   // mark that this bin was filled with non trivial content
0110 
0111   m_touchedBins.insert(globalBin({binX, binY}));
0112 
0113   // add content to the cell
0114   m_houghHist.atLocalBins({binX, binY}).fill(identifier, layer, w);
0115   // and update our cached maxima
0116   YieldType layers = nLayers(binX, binY);
0117   YieldType hits = nHits(binX, binY);
0118   if (layers > m_maxLayers) {
0119     m_maxLayers = layers;
0120     m_maxLocLayers = {binX, binY};
0121   }
0122   if (hits > m_maxHits) {
0123     m_maxHits = hits;
0124     m_maxLocHits = {binX, binY};
0125   }
0126 }
0127 
0128 template <class identifier_t>
0129 void Acts::HoughTransformUtils::HoughPlane<identifier_t>::reset() {
0130   // reset all bins that were previously filled
0131   // avoid calling this on empty cells to save time
0132   for (auto bin : getNonEmptyBins()) {
0133     m_houghHist.at(bin).reset();
0134   }
0135   // don't forget to reset our cached maxima
0136   m_maxHits = 0.;
0137   m_maxLayers = 0.;
0138   // and reset the list of nontrivial bins
0139   m_iBin = 0;
0140   m_touchedBins.clear();
0141 }
0142 
0143 template <class identifier_t>
0144 Acts::HoughTransformUtils::PeakFinders::LayerGuidedCombinatoric<identifier_t>::
0145     LayerGuidedCombinatoric(const LayerGuidedCombinatoricConfig& cfg)
0146     : m_cfg(cfg) {}
0147 
0148 template <class identifier_t>
0149 std::vector<typename Acts::HoughTransformUtils::PeakFinders::
0150                 LayerGuidedCombinatoric<identifier_t>::Maximum>
0151 Acts::HoughTransformUtils::PeakFinders::LayerGuidedCombinatoric<
0152     identifier_t>::findPeaks(const HoughPlane<identifier_t>& plane) const {
0153   // book the vector for the maxima
0154   std::vector<PeakFinders::LayerGuidedCombinatoric<identifier_t>::Maximum>
0155       maxima;
0156   // loop over the non empty bins
0157   for (auto nbin : plane.getNonEmptyBins()) {
0158     // and look for the ones that represent a maximum
0159 
0160     std::array<std::size_t, 2> xy = plane.axisBins(nbin);
0161 
0162     if (passThreshold(plane, xy[0], xy[1])) {
0163       // write out a maximum
0164       Maximum max;
0165       max.hitIdentifiers = plane.hitIds(xy[0], xy[1]);
0166       maxima.push_back(max);
0167     }
0168   }
0169   return maxima;
0170 }
0171 
0172 template <class identifier_t>
0173 bool Acts::HoughTransformUtils::PeakFinders::LayerGuidedCombinatoric<
0174     identifier_t>::passThreshold(const HoughPlane<identifier_t>& plane,
0175                                  std::size_t xBin, std::size_t yBin) const {
0176   // Check if we have sufficient layers for a maximum
0177   if (plane.nLayers(xBin, yBin) < m_cfg.threshold) {
0178     return false;
0179   }
0180   // if no local maximum is required, this is enough to classify as a max
0181   if (m_cfg.localMaxWindowSize == 0) {
0182     return true;
0183   }
0184   // otherwise, check for local maxima by looking at the surrounding cells
0185 
0186   /// loop over neighbours
0187   for (int dXbin = -m_cfg.localMaxWindowSize; dXbin <= m_cfg.localMaxWindowSize;
0188        dXbin++) {
0189     for (int dYbin = -m_cfg.localMaxWindowSize;
0190          dYbin <= m_cfg.localMaxWindowSize; dYbin++) {
0191       // exclude the candidate itself
0192       if (dYbin == 0 && dXbin == 0) {
0193         continue;
0194       }
0195       // out of bounds -> no need to test this bin
0196       if (static_cast<int>(xBin) + dXbin < 0 ||
0197           static_cast<int>(yBin) + dYbin < 0) {
0198         continue;
0199       }
0200       if (xBin + dXbin >= plane.nBinsX() || yBin + dYbin >= plane.nBinsY()) {
0201         continue;
0202       }
0203       // if a neighbour with more layers exist, this is not a minimum
0204       if (plane.nLayers(xBin + dXbin, yBin + dYbin) >
0205           plane.nLayers(xBin, yBin)) {
0206         return false;
0207       }
0208       // if the neighbour has fewer hits, we can move to the next neighbour
0209       if (plane.nLayers(xBin + dXbin, yBin + dYbin) <
0210           plane.nLayers(xBin, yBin)) {
0211         continue;
0212       }
0213 
0214       // we can still be in a plateau. In this case, resolve by counting hits
0215 
0216       // if the other bin with the same hit count has seen more clusters (
0217       // double hits in one layer), keep that one and reject the current
0218       if (plane.nHits(xBin + dXbin, yBin + dYbin) > plane.nHits(xBin, yBin)) {
0219         return false;
0220       }
0221       // and if we have completely identical hit and layer count, prefer bins in
0222       // the bottom (left) direction
0223       if (plane.nHits(xBin + dXbin, yBin + dYbin) == plane.nHits(xBin, yBin) &&
0224           (dYbin < 0 || (dYbin == 0 && dXbin < 0))) {
0225         return false;
0226       }
0227     }
0228   }
0229   return true;
0230 }
0231 template <class identifier_t>
0232 Acts::HoughTransformUtils::PeakFinders::IslandsAroundMax<
0233     identifier_t>::IslandsAroundMax(const IslandsAroundMaxConfig& cfg)
0234     : m_cfg(cfg) {}
0235 
0236 template <class identifier_t>
0237 std::vector<typename Acts::HoughTransformUtils::PeakFinders::IslandsAroundMax<
0238     identifier_t>::Maximum>
0239 Acts::HoughTransformUtils::PeakFinders::IslandsAroundMax<
0240     identifier_t>::findPeaks(const HoughPlane<identifier_t>& plane,
0241                              const HoughAxisRanges& ranges) {
0242   // check the global maximum hit count in the plane
0243   YieldType max = plane.maxHits();
0244   // and obtain the fraction of the max that is our cutoff for island formation
0245   YieldType min = std::max(m_cfg.threshold, m_cfg.fractionCutoff * max);
0246   // book a list for the candidates and the maxima
0247   const std::unordered_set<std::size_t>& nonEmptyBins{plane.getNonEmptyBins()};
0248   std::vector<std::size_t> candidates;
0249   candidates.reserve(nonEmptyBins.size());
0250   std::vector<Maximum> maxima;
0251   maxima.reserve(nonEmptyBins.size());
0252   // keep track of the yields in each non empty cell
0253   std::unordered_map<std::size_t, YieldType> yieldMap;
0254 
0255   // now loop over all non empty bins
0256   for (const std::size_t nbin : nonEmptyBins) {
0257     //  we only consider cells above threshold from now on.
0258     //  each is a potential candidate to seed or join an island
0259     //  We also add each to our yield map
0260 
0261     if (plane.nHits(nbin) > min) {
0262       candidates.push_back(nbin);
0263       yieldMap[nbin] = plane.nHits(nbin);
0264     }
0265   }
0266   // sort the candidate cells descending in content
0267   std::ranges::sort(candidates, std::greater{}, [&yieldMap](std::size_t c) {
0268     return std::make_tuple(yieldMap[c], c);
0269   });
0270 
0271   // now we build islands from the candidate cells, starting with the most
0272   // populated one
0273   std::vector<std::size_t> toExplore;
0274   std::vector<std::array<std::size_t, 2>> solution;
0275 
0276   // loop over candidate cells
0277   for (auto& cand : candidates) {
0278     // check the content again (if used in a previous island, this will now
0279     // report empty)
0280     if (yieldMap[cand] < min) {
0281       continue;
0282     }
0283     // CALL AXIS BINS HERE
0284     std::array<std::size_t, 2> xy = plane.axisBins(cand);
0285     // translate to parameter space for overlap veto
0286     CoordType xCand =
0287         binCenter(ranges.xMin, ranges.xMax, plane.nBinsX(), xy[0]);
0288     CoordType yCand =
0289         binCenter(ranges.yMin, ranges.yMax, plane.nBinsY(), xy[1]);
0290     // check if we are too close to a previously found maximum
0291     bool goodSpacing = true;
0292     for (auto& found : maxima) {
0293       if (std::abs(xCand - found.x) < m_cfg.minSpacingBetweenPeaks.first ||
0294           std::abs(yCand - found.y) < m_cfg.minSpacingBetweenPeaks.second) {
0295         goodSpacing = false;
0296         break;
0297       }
0298     }
0299     if (!goodSpacing) {
0300       continue;
0301     }
0302     // now we can extend the candidate into an island
0303     toExplore.clear();
0304     solution.clear();
0305     // initially, pass the candidate into the list of "neighbours" to explore
0306     // around an empty island
0307     toExplore.push_back(cand);
0308     // and incrementally add neighbours, filling the solution vector
0309     while (!toExplore.empty()) {
0310       extendMaximum(plane, solution, toExplore, min, yieldMap);
0311     }
0312     // nothing found? Next candidate!
0313     if (solution.empty()) {
0314       continue;
0315     }
0316     // We found an island
0317     Maximum maximum;
0318     CoordType max_x = 0;
0319     CoordType max_y = 0;
0320     CoordType pos_den = 0;
0321     CoordType ymax = -std::numeric_limits<CoordType>::max();
0322     CoordType ymin = std::numeric_limits<CoordType>::max();
0323     CoordType xmax = -std::numeric_limits<CoordType>::max();
0324     CoordType xmin = std::numeric_limits<CoordType>::max();
0325     // loop over cells in the island and get the weighted mean position.
0326     // Also collect all hit identifiers in the island and the maximum
0327     // extent (within the count threshold!) of the island
0328     for (auto& [xBin, yBin] : solution) {
0329       auto hidIds = plane.hitIds(xBin, yBin);
0330       maximum.hitIdentifiers.insert(std::make_move_iterator(hidIds.begin()),
0331                                     std::make_move_iterator(hidIds.end()));
0332       CoordType xHit =
0333           binCenter(ranges.xMin, ranges.xMax, plane.nBinsX(), xBin);
0334       CoordType yHit =
0335           binCenter(ranges.yMin, ranges.yMax, plane.nBinsY(), yBin);
0336       YieldType nHits = plane.nHits(xBin, yBin);
0337       max_x += xHit * nHits;
0338       max_y += yHit * nHits;
0339       pos_den += nHits;
0340       if (xHit > xmax) {
0341         xmax = xHit;
0342       }
0343       if (yHit > ymax) {
0344         ymax = yHit;
0345       }
0346       if (xHit < xmin) {
0347         xmin = xHit;
0348       }
0349       if (yHit < ymin) {
0350         ymin = yHit;
0351       }
0352     }
0353     // calculate mean position
0354     maximum.x = max_x / pos_den;
0355     maximum.y = max_y / pos_den;
0356     // calculate width as symmetrised band
0357     maximum.wx = 0.5 * (xmax - xmin);
0358     maximum.wy = 0.5 * (ymax - ymin);
0359     maxima.push_back(maximum);
0360   }
0361   return maxima;
0362 }
0363 
0364 template <class identifier_t>
0365 void Acts::HoughTransformUtils::PeakFinders::IslandsAroundMax<identifier_t>::
0366     extendMaximum(
0367         const Acts::HoughTransformUtils::HoughPlane<identifier_t>& houghPlane,
0368         std::vector<std::array<std::size_t, 2>>& inMaximum,
0369         std::vector<std::size_t>& toExplore, YieldType threshold,
0370         std::unordered_map<std::size_t, YieldType>& yieldMap) {
0371   // in this call, we explore the last element of the toExplore list.
0372   // Fetch it and pop it from the vector.
0373 
0374   const std::size_t candidate = toExplore.back();
0375   YieldType& yield{yieldMap[candidate]};
0376   toExplore.pop_back();
0377   // check if we are above threshold. Don't add this cell to the island if not
0378   if (yield < threshold) {
0379     return;
0380   }
0381   // This candidate is above threshold and should go on the island!
0382   auto nextCand = houghPlane.axisBins(candidate);
0383   // add it to the cell list for the island
0384   inMaximum.push_back(nextCand);
0385   // and "veto" the hit for further use via the yield map
0386   yieldMap[candidate] = -1.0f;
0387 
0388   // now we have to collect the non empty neighbours of this cell and check them
0389   // as well
0390   for (auto step : m_stepDirections) {
0391     std::array<unsigned long, 2> newCandxy = {nextCand[0] + step.first,
0392                                               nextCand[1] + step.second};
0393     // check if we are moving out of the bounds
0394     if (newCandxy[0] >= houghPlane.nBinsX() ||
0395         newCandxy[1] >= houghPlane.nBinsY()) {
0396       continue;
0397     }
0398     std::size_t newCand = houghPlane.globalBin({newCandxy[0], newCandxy[1]});
0399 
0400     // if the cell is above threshold, add it to our list of neighbours to
0401     // explore
0402     if (yieldMap[newCand] > threshold) {
0403       toExplore.push_back(newCand);
0404     }
0405   }
0406 }