Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-06-30 07:52:22

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 "ActsExamples/TrackFinding/HoughTransformSeeder.hpp"
0010 
0011 #include "Acts/Definitions/Algebra.hpp"
0012 #include "Acts/Definitions/Common.hpp"
0013 #include "Acts/Definitions/TrackParametrization.hpp"
0014 #include "Acts/EventData/SourceLink.hpp"
0015 #include "Acts/Geometry/TrackingGeometry.hpp"
0016 #include "Acts/Surfaces/Surface.hpp"
0017 #include "Acts/Utilities/Enumerate.hpp"
0018 #include "Acts/Utilities/MathHelpers.hpp"
0019 #include "ActsExamples/EventData/GeometryContainers.hpp"
0020 #include "ActsExamples/EventData/Index.hpp"
0021 #include "ActsExamples/EventData/IndexSourceLink.hpp"
0022 #include "ActsExamples/EventData/Measurement.hpp"
0023 #include "ActsExamples/EventData/ProtoTrack.hpp"
0024 #include "ActsExamples/Framework/AlgorithmContext.hpp"
0025 #include "ActsExamples/TrackFinding/DefaultHoughFunctions.hpp"
0026 #include "ActsExamples/Utilities/GroupBy.hpp"
0027 #include "ActsExamples/Utilities/Range.hpp"
0028 
0029 #include <algorithm>
0030 #include <cmath>
0031 #include <iterator>
0032 #include <ostream>
0033 #include <stdexcept>
0034 #include <variant>
0035 
0036 static inline int quant(double min, double max, unsigned nSteps, double val);
0037 static inline double unquant(double min, double max, unsigned nSteps, int step);
0038 template <typename T>
0039 static inline std::string to_string(std::vector<T> v);
0040 
0041 ActsExamples::HoughTransformSeeder::HoughTransformSeeder(
0042     ActsExamples::HoughTransformSeeder::Config cfg, Acts::Logging::Level lvl)
0043     : ActsExamples::IAlgorithm("HoughTransformSeeder", lvl),
0044       m_cfg(std::move(cfg)),
0045       m_logger(Acts::getDefaultLogger("HoughTransformSeeder", lvl)) {
0046   // require spacepoints or input measurements (or both), but at least one kind
0047   // of input
0048   bool foundInput = false;
0049   for (const auto& spName : m_cfg.inputSpacePoints) {
0050     if (!(spName.empty())) {
0051       foundInput = true;
0052     }
0053 
0054     auto& handle = m_inputSpacePoints.emplace_back(
0055         std::make_unique<ReadDataHandle<SimSpacePointContainer>>(
0056             this,
0057             "InputSpacePoints#" + std::to_string(m_inputSpacePoints.size())));
0058     handle->initialize(spName);
0059   }
0060   if (!(m_cfg.inputMeasurements.empty())) {
0061     foundInput = true;
0062   }
0063 
0064   if (!foundInput) {
0065     throw std::invalid_argument(
0066         "HoughTransformSeeder: Missing some kind of input (measurements of "
0067         "spacepoints)");
0068   }
0069 
0070   if (m_cfg.outputProtoTracks.empty()) {
0071     throw std::invalid_argument(
0072         "HoughTransformSeeder: Missing hough tracks output collection");
0073   }
0074   if (m_cfg.outputSeeds.empty()) {
0075     throw std::invalid_argument(
0076         "HoughTransformSeeder: Missing hough track seeds output collection");
0077   }
0078 
0079   m_outputProtoTracks.initialize(m_cfg.outputProtoTracks);
0080   m_inputMeasurements.initialize(m_cfg.inputMeasurements);
0081 
0082   if (!m_cfg.trackingGeometry) {
0083     throw std::invalid_argument(
0084         "HoughTransformSeeder: Missing tracking geometry");
0085   }
0086 
0087   if (m_cfg.geometrySelection.empty()) {
0088     throw std::invalid_argument(
0089         "HoughTransformSeeder: Missing geometry selection");
0090   }
0091   // ensure geometry selection contains only valid inputs
0092   for (const auto& geoId : m_cfg.geometrySelection) {
0093     if ((geoId.approach() != 0u) || (geoId.boundary() != 0u) ||
0094         (geoId.sensitive() != 0u)) {
0095       throw std::invalid_argument(
0096           "HoughTransformSeeder: Invalid geometry selection: only volume and "
0097           "layer are allowed to be set");
0098     }
0099   }
0100   // remove geometry selection duplicates
0101   //
0102   // the geometry selections must be mutually exclusive, i.e. if we have a
0103   // selection that contains both a volume and a layer within that same volume,
0104   // we would create the space points for the layer twice.
0105   auto isDuplicate = [](Acts::GeometryIdentifier ref,
0106                         Acts::GeometryIdentifier cmp) {
0107     // code assumes ref < cmp and that only volume and layer can be non-zero
0108     // root node always contains everything
0109     if (ref.volume() == 0) {
0110       return true;
0111     }
0112     // unequal volumes always means separate hierarchies
0113     if (ref.volume() != cmp.volume()) {
0114       return false;
0115     }
0116     // within the same volume hierarchy only consider layers
0117     return (ref.layer() == cmp.layer());
0118   };
0119   // sort geometry selection so the unique filtering works
0120   std::ranges::sort(m_cfg.geometrySelection,
0121                     std::less<Acts::GeometryIdentifier>{});
0122   auto geoSelBeg = m_cfg.geometrySelection.begin();
0123   auto geoSelEnd = m_cfg.geometrySelection.end();
0124   auto geoSelLastUnique = std::unique(geoSelBeg, geoSelEnd, isDuplicate);
0125   if (geoSelLastUnique != geoSelEnd) {
0126     ACTS_WARNING("Removed " << std::distance(geoSelLastUnique, geoSelEnd)
0127                             << " geometry selection duplicates");
0128     m_cfg.geometrySelection.erase(geoSelLastUnique, geoSelEnd);
0129   }
0130   ACTS_INFO("Hough geometry selection:");
0131   for (const auto& geoId : m_cfg.geometrySelection) {
0132     ACTS_INFO("  " << geoId);
0133   }
0134 
0135   // Fill convenience variables
0136   m_step_x = (m_cfg.xMax - m_cfg.xMin) / m_cfg.houghHistSize_x;
0137   m_step_y = (m_cfg.yMax - m_cfg.yMin) / m_cfg.houghHistSize_y;
0138   for (unsigned i = 0; i <= m_cfg.houghHistSize_x; i++) {
0139     m_bins_x.push_back(
0140         unquant(m_cfg.xMin, m_cfg.xMax, m_cfg.houghHistSize_x, i));
0141   }
0142   for (unsigned i = 0; i <= m_cfg.houghHistSize_y; i++) {
0143     m_bins_y.push_back(
0144         unquant(m_cfg.yMin, m_cfg.yMax, m_cfg.houghHistSize_y, i));
0145   }
0146 
0147   m_cfg.fieldCorrector
0148       .connect<&ActsExamples::DefaultHoughFunctions::fieldCorrectionDefault>();
0149   m_cfg.layerIDFinder
0150       .connect<&ActsExamples::DefaultHoughFunctions::findLayerIDDefault>();
0151   m_cfg.sliceTester
0152       .connect<&ActsExamples::DefaultHoughFunctions::inSliceDefault>();
0153 }
0154 
0155 ActsExamples::ProcessCode ActsExamples::HoughTransformSeeder::execute(
0156     const AlgorithmContext& ctx) const {
0157   // clear our Hough measurements out from the previous iteration, if at all
0158   houghMeasurementStructs.clear();
0159 
0160   // add SPs to the inputs
0161   addSpacePoints(ctx);
0162 
0163   // add ACTS measurements
0164   addMeasurements(ctx);
0165 
0166   static thread_local ProtoTrackContainer protoTracks;
0167   protoTracks.clear();
0168 
0169   // loop over our subregions and run the Hough Transform on each
0170   for (int subregion : m_cfg.subRegions) {
0171     ACTS_DEBUG("Processing subregion " << subregion);
0172     ActsExamples::HoughHist m_houghHist = createHoughHist(subregion);
0173 
0174     for (unsigned y = 0; y < m_cfg.houghHistSize_y; y++) {
0175       for (unsigned x = 0; x < m_cfg.houghHistSize_x; x++) {
0176         if (!passThreshold(m_houghHist, x, y)) {
0177           continue;
0178         }
0179 
0180         // Now we need to unpack the hits; there should be multiple track
0181         // candidates if we have multiple hits in a given layer. So the first
0182         // thing is to unpack the indices (which is what we need) by layer
0183 
0184         std::vector<std::vector<std::vector<Index>>> hitIndicesAll(
0185             m_cfg.nLayers);
0186         std::vector<std::size_t> nHitsPerLayer(m_cfg.nLayers);
0187         for (auto measurementIndex : m_houghHist.atLocalBins({y, x}).second) {
0188           HoughMeasurementStruct* meas =
0189               houghMeasurementStructs[measurementIndex].get();
0190           hitIndicesAll[meas->layer].push_back(meas->indices);
0191           nHitsPerLayer[meas->layer]++;
0192         }
0193 
0194         std::vector<std::vector<int>> combs = getComboIndices(nHitsPerLayer);
0195 
0196         // Loop over all combinations.
0197         for (auto [icomb, hit_indices] : Acts::enumerate(combs)) {
0198           ProtoTrack protoTrack;
0199           for (unsigned layer = 0; layer < m_cfg.nLayers; layer++) {
0200             if (hit_indices[layer] >= 0) {
0201               for (auto index : hitIndicesAll[layer][hit_indices[layer]]) {
0202                 protoTrack.push_back(index);
0203               }
0204             }
0205           }
0206           protoTracks.push_back(protoTrack);
0207         }
0208       }
0209     }
0210   }
0211   ACTS_DEBUG("Created " << protoTracks.size() << " proto track");
0212 
0213   m_outputProtoTracks(ctx, ProtoTrackContainer{protoTracks});
0214   // clear the vector
0215   houghMeasurementStructs.clear();
0216   return ActsExamples::ProcessCode::SUCCESS;
0217 }
0218 
0219 ActsExamples::HoughHist
0220 ActsExamples::HoughTransformSeeder::createLayerHoughHist(unsigned layer,
0221                                                          int subregion) const {
0222   ActsExamples::HoughHist houghHist(
0223       Axis(0, m_cfg.houghHistSize_y, m_cfg.houghHistSize_y),
0224       Axis(0, m_cfg.houghHistSize_x, m_cfg.houghHistSize_x));
0225   for (unsigned index = 0; index < houghMeasurementStructs.size(); index++) {
0226     HoughMeasurementStruct* meas = houghMeasurementStructs[index].get();
0227     if (meas->layer != layer) {
0228       continue;
0229     }
0230     if (!(m_cfg.sliceTester(meas->z, meas->layer, subregion)).value()) {
0231       continue;
0232     }
0233 
0234     // This scans over y (pT) because that is more efficient in memory
0235     for (unsigned y_ = 0; y_ < m_cfg.houghHistSize_y; y_++) {
0236       unsigned y_bin_min = y_;
0237       unsigned y_bin_max = (y_ + 1);
0238 
0239       // Find the min/max x bins
0240       auto xBins =
0241           yToXBins(y_bin_min, y_bin_max, meas->radius, meas->phi, meas->layer);
0242       // Update the houghHist
0243       for (unsigned y = y_bin_min; y < y_bin_max; y++) {
0244         for (unsigned x = xBins.first; x < xBins.second; x++) {
0245           houghHist.atLocalBins({y, x}).first++;
0246           houghHist.atLocalBins({y, x}).second.insert(index);
0247         }
0248       }
0249     }
0250   }
0251 
0252   return houghHist;
0253 }
0254 
0255 ActsExamples::HoughHist ActsExamples::HoughTransformSeeder::createHoughHist(
0256     int subregion) const {
0257   ActsExamples::HoughHist houghHist(
0258       Axis(0, m_cfg.houghHistSize_y, m_cfg.houghHistSize_y),
0259       Axis(0, m_cfg.houghHistSize_x, m_cfg.houghHistSize_x));
0260 
0261   for (unsigned i = 0; i < m_cfg.nLayers; i++) {
0262     HoughHist layerHoughHist = createLayerHoughHist(i, subregion);
0263     for (unsigned x = 0; x < m_cfg.houghHistSize_x; ++x) {
0264       for (unsigned y = 0; y < m_cfg.houghHistSize_y; ++y) {
0265         if (layerHoughHist.atLocalBins({y, x}).first > 0) {
0266           houghHist.atLocalBins({y, x}).first++;
0267           houghHist.atLocalBins({y, x}).second.insert(
0268               layerHoughHist.atLocalBins({y, x}).second.begin(),
0269               layerHoughHist.atLocalBins({y, x}).second.end());
0270         }
0271       }
0272     }
0273   }
0274 
0275   return houghHist;
0276 }
0277 
0278 bool ActsExamples::HoughTransformSeeder::passThreshold(
0279     HoughHist const& houghHist, unsigned x, unsigned y) const {
0280   // Pass window threshold
0281   unsigned width = m_cfg.threshold.size() / 2;
0282   if (x < width || m_cfg.houghHistSize_x - x < width) {
0283     return false;
0284   }
0285   for (unsigned i = 0; i < m_cfg.threshold.size(); i++) {
0286     if (houghHist.atLocalBins({y, x - width + i}).first < m_cfg.threshold[i]) {
0287       return false;
0288     }
0289   }
0290 
0291   // Pass local-maximum check, if used
0292   if (m_cfg.localMaxWindowSize != 0) {
0293     for (int j = -m_cfg.localMaxWindowSize; j <= m_cfg.localMaxWindowSize;
0294          j++) {
0295       for (int i = -m_cfg.localMaxWindowSize; i <= m_cfg.localMaxWindowSize;
0296            i++) {
0297         if (i == 0 && j == 0) {
0298           continue;
0299         }
0300         if (y + j < m_cfg.houghHistSize_y && x + i < m_cfg.houghHistSize_x) {
0301           if (houghHist.atLocalBins({y + j, x + i}).first >
0302               houghHist.atLocalBins({y, x}).first) {
0303             return false;
0304           }
0305           if (houghHist.atLocalBins({y + j, x + i}).first ==
0306               houghHist.atLocalBins({y, x}).first) {
0307             if (houghHist.atLocalBins({y + j, x + i}).second.size() >
0308                 houghHist.atLocalBins({y, x}).second.size()) {
0309               return false;
0310             }
0311             if (houghHist.atLocalBins({y + j, x + i}).second.size() ==
0312                     houghHist.atLocalBins({y, x}).second.size() &&
0313                 j <= 0 && i <= 0) {
0314               return false;  // favor bottom-left (low phi, low neg q/pt)
0315             }
0316           }
0317         }
0318       }
0319     }
0320   }
0321 
0322   return true;
0323 }
0324 
0325 ///////////////////////////////////////////////////////////////////////////////
0326 // Helpers
0327 
0328 // Quantizes val, given a range [min, max) split into nSteps. Returns the bin
0329 // below.
0330 static inline int quant(double min, double max, unsigned nSteps, double val) {
0331   return static_cast<int>((val - min) / (max - min) * nSteps);
0332 }
0333 
0334 // Returns the lower bound of the bin specified by step
0335 static inline double unquant(double min, double max, unsigned nSteps,
0336                              int step) {
0337   return min + (max - min) * step / nSteps;
0338 }
0339 
0340 template <typename T>
0341 static inline std::string to_string(std::vector<T> v) {
0342   std::ostringstream oss;
0343   oss << "[";
0344   if (!v.empty()) {
0345     std::copy(v.begin(), v.end() - 1, std::ostream_iterator<T>(oss, ", "));
0346     oss << v.back();
0347   }
0348   oss << "]";
0349   return oss.str();
0350 }
0351 
0352 double ActsExamples::HoughTransformSeeder::yToX(double y, double r,
0353                                                 double phi) const {
0354   double d0 = 0;  // d0 correction TO DO allow for this
0355   double x =
0356       asin(r * ActsExamples::HoughTransformSeeder::m_cfg.kA * y - d0 / r) + phi;
0357 
0358   if (m_cfg.fieldCorrector.connected()) {
0359     x += (m_cfg.fieldCorrector(0, y, r)).value();
0360   }
0361 
0362   return x;
0363 }
0364 
0365 // Find the min/max x bins of the hit's line, in each y bin. Max is exclusive.
0366 // Note this assumes yToX is monotonic. Returns {0, 0} if hit lies out of
0367 // bounds.
0368 std::pair<unsigned, unsigned> ActsExamples::HoughTransformSeeder::yToXBins(
0369     std::size_t yBin_min, std::size_t yBin_max, double r, double phi,
0370     unsigned layer) const {
0371   double x_min = yToX(m_bins_y[yBin_min], r, phi);
0372   double x_max = yToX(m_bins_y[yBin_max], r, phi);
0373   if (x_min > x_max) {
0374     std::swap(x_min, x_max);
0375   }
0376   if (x_max < m_cfg.xMin || x_min > m_cfg.xMax) {
0377     return {0, 0};  // out of bounds
0378   }
0379 
0380   // Get bins
0381   int x_bin_min = quant(m_cfg.xMin, m_cfg.xMax, m_cfg.houghHistSize_x, x_min);
0382   int x_bin_max = quant(m_cfg.xMin, m_cfg.xMax, m_cfg.houghHistSize_x, x_max) +
0383                   1;  // exclusive
0384 
0385   // Extend bins
0386   unsigned extend = getExtension(yBin_min, layer);
0387   x_bin_min -= extend;
0388   x_bin_max += extend;
0389 
0390   // Clamp bins
0391   if (x_bin_min < 0) {
0392     x_bin_min = 0;
0393   }
0394   if (x_bin_max > static_cast<int>(m_cfg.houghHistSize_x)) {
0395     x_bin_max = m_cfg.houghHistSize_x;
0396   }
0397 
0398   return {x_bin_min, x_bin_max};
0399 }
0400 
0401 // We allow variable extension based on the size of m_hitExtend_x. See comments
0402 // below.
0403 unsigned ActsExamples::HoughTransformSeeder::getExtension(
0404     unsigned y, unsigned layer) const {
0405   if (m_cfg.hitExtend_x.size() == m_cfg.nLayers) {
0406     return m_cfg.hitExtend_x[layer];
0407   }
0408 
0409   if (m_cfg.hitExtend_x.size() == m_cfg.nLayers * 2) {
0410     // different extension for low pt vs high pt, split in half but irrespective
0411     // of sign first nLayers entries of m_hitExtend_x is for low pt half, rest
0412     // are for high pt half
0413     if (y < m_cfg.houghHistSize_y / 4 || y > 3 * m_cfg.houghHistSize_y / 4) {
0414       return m_cfg.hitExtend_x[layer];
0415     }
0416 
0417     return m_cfg.hitExtend_x[m_cfg.nLayers + layer];
0418   }
0419   return 0;
0420 }
0421 
0422 /**
0423  * Given a list of sizes (of arrays), generates a list of all combinations of
0424  * indices to index one element from each array.
0425  *
0426  * For example, given [2 3], generates [(0 0) (1 0) (0 1) (1 1) (0 2) (1 2)].
0427  *
0428  * This basically amounts to a positional number system of where each digit has
0429  * its own base. The number of digits is sizes.size(), and the base of digit i
0430  * is sizes[i]. Then all combinations can be uniquely represented just by
0431  * counting from [0, nCombs).
0432  *
0433  * For a decimal number like 1357, you get the thousands digit with n / 1000 = n
0434  * / (10 * 10 * 10). So here, you get the 0th digit with n / (base_1 * base_2 *
0435  * base_3);
0436  */
0437 std::vector<std::vector<int>>
0438 ActsExamples::HoughTransformSeeder::getComboIndices(
0439     std::vector<std::size_t>& sizes) const {
0440   std::size_t nCombs = 1;
0441   std::vector<std::size_t> nCombs_prior(sizes.size());
0442   std::vector<int> temp(sizes.size(), 0);
0443 
0444   for (std::size_t i = 0; i < sizes.size(); i++) {
0445     if (sizes[i] > 0) {
0446       nCombs_prior[i] = nCombs;
0447       nCombs *= sizes[i];
0448     } else {
0449       temp[i] = -1;
0450     }
0451   }
0452 
0453   std::vector<std::vector<int>> combos(nCombs, temp);
0454 
0455   for (std::size_t icomb = 0; icomb < nCombs; icomb++) {
0456     std::size_t index = icomb;
0457     for (std::size_t isize = sizes.size() - 1; isize < sizes.size(); isize--) {
0458       if (sizes[isize] == 0) {
0459         continue;
0460       }
0461       combos[icomb][isize] = static_cast<int>(index / nCombs_prior[isize]);
0462       index = index % nCombs_prior[isize];
0463     }
0464   }
0465 
0466   return combos;
0467 }
0468 
0469 void ActsExamples::HoughTransformSeeder::addSpacePoints(
0470     const AlgorithmContext& ctx) const {
0471   // construct the combined input container of space point pointers from all
0472   // configured input sources.
0473   for (const auto& isp : m_inputSpacePoints) {
0474     const auto& spContainer = (*isp)(ctx);
0475     ACTS_DEBUG("Inserting " << spContainer.size() << " space points from "
0476                             << isp->key());
0477     for (auto& sp : spContainer) {
0478       double r = Acts::fastHypot(sp.x(), sp.y());
0479       double z = sp.z();
0480       float phi = std::atan2(sp.y(), sp.x());
0481       ResultUnsigned hitlayer = m_cfg.layerIDFinder(r).value();
0482       if (!(hitlayer.ok())) {
0483         continue;
0484       }
0485       std::vector<Index> indices;
0486       for (const auto& slink : sp.sourceLinks()) {
0487         const auto& islink = slink.get<IndexSourceLink>();
0488         indices.push_back(islink.index());
0489       }
0490 
0491       auto meas =
0492           std::shared_ptr<HoughMeasurementStruct>(new HoughMeasurementStruct(
0493               hitlayer.value(), phi, r, z, indices, HoughHitType::SP));
0494       houghMeasurementStructs.push_back(meas);
0495     }
0496   }
0497 }
0498 
0499 void ActsExamples::HoughTransformSeeder::addMeasurements(
0500     const AlgorithmContext& ctx) const {
0501   const auto& measurements = m_inputMeasurements(ctx);
0502 
0503   ACTS_DEBUG("Inserting " << measurements.size() << " space points from "
0504                           << m_cfg.inputMeasurements);
0505 
0506   for (Acts::GeometryIdentifier geoId : m_cfg.geometrySelection) {
0507     // select volume/layer depending on what is set in the geometry id
0508     auto range =
0509         selectLowestNonZeroGeometryObject(measurements.orderedIndices(), geoId);
0510     // groupByModule only works with geometry containers, not with an
0511     // arbitrary range. do the equivalent grouping manually
0512     auto groupedByModule = makeGroupBy(range, detail::GeometryIdGetter());
0513 
0514     for (const auto& [moduleGeoId, moduleSourceLinks] : groupedByModule) {
0515       // find corresponding surface
0516       const Acts::Surface* surface =
0517           m_cfg.trackingGeometry->findSurface(moduleGeoId);
0518       if (surface == nullptr) {
0519         ACTS_ERROR("Could not find surface " << moduleGeoId);
0520         return;
0521       }
0522 
0523       for (auto& sourceLink : moduleSourceLinks) {
0524         // extract a local position/covariance independent of the concrete
0525         // measurement content. since we do not know if and where the local
0526         // parameters are contained in the measurement parameters vector, they
0527         // are transformed to the bound space where we do know their location.
0528         // if the local parameters are not measured, this results in a
0529         // zero location, which is a reasonable default fall-back.
0530         const ConstVariableBoundMeasurementProxy measurement =
0531             measurements.getMeasurement(sourceLink.index());
0532 
0533         assert(measurement.contains(Acts::eBoundLoc0) &&
0534                "Measurement does not contain the required bound loc0");
0535         assert(measurement.contains(Acts::eBoundLoc1) &&
0536                "Measurement does not contain the required bound loc1");
0537 
0538         auto boundLoc0 = measurement.indexOf(Acts::eBoundLoc0);
0539         auto boundLoc1 = measurement.indexOf(Acts::eBoundLoc1);
0540 
0541         Acts::Vector2 localPos{measurement.parameters()[boundLoc0],
0542                                measurement.parameters()[boundLoc1]};
0543 
0544         // transform local position to global coordinates
0545         Acts::Vector3 globalFakeMom(1, 1, 1);
0546         Acts::Vector3 globalPos =
0547             surface->localToGlobal(ctx.geoContext, localPos, globalFakeMom);
0548         double r = globalPos.head<2>().norm();
0549         double phi = std::atan2(globalPos[Acts::ePos1], globalPos[Acts::ePos0]);
0550         double z = globalPos[Acts::ePos2];
0551         ResultUnsigned hitlayer = m_cfg.layerIDFinder(r);
0552         if (hitlayer.ok()) {
0553           std::vector<Index> index;
0554           index.push_back(sourceLink.index());
0555           auto houghMeas = std::shared_ptr<HoughMeasurementStruct>(
0556               new HoughMeasurementStruct(hitlayer.value(), phi, r, z, index,
0557                                          HoughHitType::MEASUREMENT));
0558           houghMeasurementStructs.push_back(houghMeas);
0559         }
0560       }
0561     }
0562   }
0563 }