Back to home page

sPhenix code displayed by LXR

 
 

    


File indexing completed on 2025-08-05 08:09:48

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