Back to home page

sPhenix code displayed by LXR

 
 

    


File indexing completed on 2026-07-16 08:07:30

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 #pragma once
0010 
0011 #include "Acts/Definitions/Tolerance.hpp"
0012 #include "Acts/Geometry/PortalLinkBase.hpp"
0013 #include "Acts/Geometry/TrackingVolume.hpp"
0014 #include "Acts/Surfaces/CylinderSurface.hpp"
0015 #include "Acts/Surfaces/DiscSurface.hpp"
0016 #include "Acts/Surfaces/PlaneSurface.hpp"
0017 #include "Acts/Utilities/AxisDefinitions.hpp"
0018 #include "Acts/Utilities/Grid.hpp"
0019 #include "Acts/Utilities/Logger.hpp"
0020 #include "Acts/Utilities/ThrowAssert.hpp"
0021 
0022 #include <iosfwd>
0023 
0024 namespace Acts {
0025 
0026 class IGrid;
0027 class TrivialPortalLink;
0028 
0029 template <typename... Axes>
0030   requires(sizeof...(Axes) <= 2)
0031 class GridPortalLinkT;
0032 
0033 /// GridPortalLink implements a subdivided surface where the target volume
0034 /// depends on the position on the surface. The link can be in two states:
0035 /// 1. One-dimensional binning with an associated *direction* to determine which
0036 ///    local coordinate to use for the lookup
0037 /// 2. Two-dimensional binning
0038 /// @note The grid dimensions and boundaries are **required** (and checked) to be
0039 ///       consistent with the surface bounds.
0040 class GridPortalLink : public PortalLinkBase {
0041  protected:
0042   /// Constructor from a surface and a direction, for initialization by derived
0043   /// class
0044   /// @param surface The surface
0045   /// @param direction The binning direction
0046   GridPortalLink(std::shared_ptr<RegularSurface> surface,
0047                  AxisDirection direction);
0048 
0049  public:
0050   /// Override the destructor so we can get away with forward declaration of
0051   /// `TrivialPortalLink`
0052   ~GridPortalLink() override;
0053 
0054   /// Factory function for a one-dimensional grid portal link, which allows
0055   /// using template deduction to figure out the right type
0056   /// @tparam axis_t The axis type
0057   /// @param surface The surface
0058   /// @param direction The binning direction
0059   /// @param axis The axis to use for the binning
0060   /// @note The axis boundaries are checked against the bounds of @p surface.
0061   /// @return A unique pointer to the grid portal link
0062   template <AxisConcept axis_t>
0063   static std::unique_ptr<GridPortalLinkT<axis_t>> make(
0064       std::shared_ptr<RegularSurface> surface, AxisDirection direction,
0065       axis_t&& axis) {
0066     using enum AxisDirection;
0067     if (dynamic_cast<const CylinderSurface*>(surface.get()) != nullptr) {
0068       if (direction != AxisZ && direction != AxisRPhi) {
0069         throw std::invalid_argument{"Invalid binning direction"};
0070       }
0071     } else if (dynamic_cast<const DiscSurface*>(surface.get()) != nullptr &&
0072                direction != AxisR && direction != AxisPhi) {
0073       throw std::invalid_argument{"Invalid binning direction"};
0074     } else if (dynamic_cast<const PlaneSurface*>(surface.get()) != nullptr &&
0075                direction != AxisX && direction != AxisY) {
0076       throw std::invalid_argument{"Invalid binning direction"};
0077     }
0078 
0079     return std::make_unique<GridPortalLinkT<axis_t>>(
0080         surface, direction, std::forward<axis_t>(axis));
0081   }
0082 
0083   /// Factory function for a two-dimensional grid portal link, which allows
0084   /// using template deduction to figure out the right type.
0085   /// @tparam axis_1_t The first axis type
0086   /// @tparam axis_2_t The second axis type
0087   /// @param surface The surface
0088   /// @param axis1 The first axis to use for the binning
0089   /// @param axis2 The second axis to use for the binning
0090   /// @note The axis boundaries are checked against the bounds of @p surface.
0091   /// @return A unique pointer to the grid portal link
0092   template <AxisConcept axis_1_t, AxisConcept axis_2_t>
0093   static std::unique_ptr<GridPortalLinkT<axis_1_t, axis_2_t>> make(
0094       std::shared_ptr<RegularSurface> surface, axis_1_t axis1, axis_2_t axis2) {
0095     std::optional<AxisDirection> direction;
0096     if (dynamic_cast<const CylinderSurface*>(surface.get()) != nullptr) {
0097       direction = AxisDirection::AxisRPhi;
0098     } else if (dynamic_cast<const DiscSurface*>(surface.get()) != nullptr) {
0099       direction = AxisDirection::AxisR;
0100     } else if (dynamic_cast<const PlaneSurface*>(surface.get()) != nullptr) {
0101       direction = AxisDirection::AxisX;
0102     }
0103 
0104     return std::make_unique<GridPortalLinkT<axis_1_t, axis_2_t>>(
0105         surface, direction.value(), std::move(axis1), std::move(axis2));
0106   }
0107 
0108   /// Factory function for an automatically sized one-dimensional grid. This produces a single-bin grid with boundaries taken from the bounds of @p surface along @p direction.
0109   /// @param surface The surface
0110   /// @param volume The tracking volume
0111   /// @param direction The binning direction
0112   /// @return A unique pointer to the grid portal link
0113   static std::unique_ptr<GridPortalLink> make(
0114       const std::shared_ptr<RegularSurface>& surface, TrackingVolume& volume,
0115       AxisDirection direction);
0116 
0117   /// Merge two grid portal links into a single one. The routine can merge
0118   /// one-dimensional, two-dimensional and mixed links. The merge will try to
0119   /// preserve equidistant binning in case bin widths match. Otherwise, the
0120   /// merge falls back to variable binning.
0121   ///
0122   /// 1D merge scenarios:
0123   ///
0124   /// ```
0125   /// +----------------------------------------------------------+
0126   /// |Colinear                                                  |
0127   /// |                                                          |
0128   /// | +-------+-------+-------+     +-------+-------+-------+  |
0129   /// | |       |       |       |     |       |       |       |  |
0130   /// | |       |       |       |  +  |       |       |       |  |
0131   /// | |       |       |       |     |       |       |       |  |
0132   /// | +-------+-------+-------+     +-------+-------+-------+  |
0133   /// |       +-------+-------+-------+-------+-------+-------+  |
0134   /// |       |       |       |       |       |       |       |  |
0135   /// |    =  |       |       |       |       |       |       |  |
0136   /// |       |       |       |       |       |       |       |  |
0137   /// |       +-------+-------+-------+-------+-------+-------+  |
0138   /// |                                                          |
0139   /// +----------------------------------------------------------+
0140   /// ```
0141   ///
0142   /// Two grid along a shared direction are merged along their shared direction
0143   ///
0144   /// ```
0145   /// +-------------------------------------------------+
0146   /// |Parallel                                         |
0147   /// |                                                 |
0148   /// | +-------+      +-------+     +-------+-------+  |
0149   /// | |       |      |       |     |       |       |  |
0150   /// | |       |      |       |     |       |       |  |
0151   /// | |       |      |       |     |       |       |  |
0152   /// | +-------+      +-------+     +-------+-------+  |
0153   /// | |       |      |       |     |       |       |  |
0154   /// | |       |  +   |       |  =  |       |       |  |
0155   /// | |       |      |       |     |       |       |  |
0156   /// | +-------+      +-------+     +-------+-------+  |
0157   /// | |       |      |       |     |       |       |  |
0158   /// | |       |      |       |     |       |       |  |
0159   /// | |       |      |       |     |       |       |  |
0160   /// | +-------+      +-------+     +-------+-------+  |
0161   /// |                                                 |
0162   /// +-------------------------------------------------+
0163   /// ```
0164   ///
0165   /// Two grids along a shared direction a merged in the direction that is
0166   /// orthogonal to their shared direction.
0167   ///
0168   /// ```
0169   /// +-------------------------------------------+
0170   /// |Perpendicular                              |
0171   /// |                                           |
0172   /// |  +-------+                                |
0173   /// |  |       |                                |
0174   /// |  |       |                                |
0175   /// |  |       |                                |
0176   /// |  +-------+     +-------+-------+-------+  |
0177   /// |  |       |     |       |       |       |  |
0178   /// |  |       |  +  |       |       |       |  |
0179   /// |  |       |     |       |       |       |  |
0180   /// |  +-------+     +-------+-------+-------+  |
0181   /// |  |       |                                |
0182   /// |  |       |                                |
0183   /// |  |       |     +-------+-------+-------+  |
0184   /// |  +-------+     |       |       |       |  |
0185   /// |                |       |       |       |  |
0186   /// |                |       |       |       |  |
0187   /// |                +-------+-------+-------+  |
0188   /// |                |       |       |       |  |
0189   /// |             =  |       |       |       |  |
0190   /// |                |       |       |       |  |
0191   /// |                +-------+-------+-------+  |
0192   /// |                |       |       |       |  |
0193   /// |                |       |       |       |  |
0194   /// |                |       |       |       |  |
0195   /// |                +-------+-------+-------+  |
0196   /// |                                           |
0197   /// +-------------------------------------------+
0198   /// ```
0199   ///
0200   /// Two grids whose directions are not shared are merged (ordering does not
0201   /// matter here). The routine will expand one of the grids to match the
0202   /// other's binning, by subdividing the grid in the as-of-yet unbinned
0203   /// direction, while filling all bins with the original bin contents.
0204   /// Afterwards, a conventional mixed-dimension merge is performed.
0205   ///
0206   /// Mixed merge scenarios
0207   /// The order is normalized by always taking the 2D grid as the left hand
0208   /// side. The 1D grid is expanded to match the binning in the as-of-yet
0209   /// unbinned direction with the binning taken from the 2D grid.
0210   ///
0211   /// ```
0212   /// +-----------------------------------------+
0213   /// |2D + 1D                                  |
0214   /// |                                         |
0215   /// | +-------+-------+     +-------+-------+ |
0216   /// | |       |       |     |       |       | |
0217   /// | |       |       |     |       |       | |
0218   /// | |       |       |     |       |       | |
0219   /// | +-------+-------+     +-------+-------+ |
0220   /// | |       |       |     |       |       | |
0221   /// | |       |       |     |       |       | |
0222   /// | |       |       |     |       |       | |
0223   /// | +-------+-------+  =  +-------+-------+ |
0224   /// | |       |       |     |       |       | |
0225   /// | |       |       |     |       |       | |
0226   /// | |       |       |     |       |       | |
0227   /// | +-------+-------+     +-------+-------+ |
0228   /// |         +             |       |       | |
0229   /// | +-------+-------+     |       |       | |
0230   /// | |       |       |     |       |       | |
0231   /// | |       |       |     +-------+-------+ |
0232   /// | |       |       |                       |
0233   /// | +-------+-------+                       |
0234   /// +-----------------------------------------+
0235   /// ```
0236   ///
0237   /// ```
0238   /// +--------------------------------------------------------------+
0239   /// |2D + 1D                                                       |
0240   /// |                                                              |
0241   /// | +-------+-------+    +-------+     +-------+-------+-------+ |
0242   /// | |       |       |    |       |     |       |       |       | |
0243   /// | |       |       |    |       |     |       |       |       | |
0244   /// | |       |       |    |       |     |       |       |       | |
0245   /// | +-------+-------+    +-------+     +-------+-------+-------+ |
0246   /// | |       |       |    |       |     |       |       |       | |
0247   /// | |       |       |  + |       |  =  |       |       |       | |
0248   /// | |       |       |    |       |     |       |       |       | |
0249   /// | +-------+-------+    +-------+     +-------+-------+-------+ |
0250   /// | |       |       |    |       |     |       |       |       | |
0251   /// | |       |       |    |       |     |       |       |       | |
0252   /// | |       |       |    |       |     |       |       |       | |
0253   /// | +-------+-------+    +-------+     +-------+-------+-------+ |
0254   /// |                                                              |
0255   /// +--------------------------------------------------------------+
0256   /// ```
0257   ///
0258   /// 2D merges
0259   /// The grids need to already share a common axis. If that is not the case,
0260   /// the merge routine returns a `nullptr`. This can be handled by composite
0261   /// merging as a fallback if needed.
0262   /// Ordering and direction does not matter here.
0263   ///
0264   /// ```
0265   /// +-----------------------------------------+
0266   /// |2D + 2D                                  |
0267   /// |                                         |
0268   /// | +-------+-------+     +-------+-------+ |
0269   /// | |       |       |     |       |       | |
0270   /// | |       |       |     |       |       | |
0271   /// | |       |       |     |       |       | |
0272   /// | +-------+-------+     +-------+-------+ |
0273   /// | |       |       |     |       |       | |
0274   /// | |       |       |  +  |       |       | |
0275   /// | |       |       |     |       |       | |
0276   /// | +-------+-------+     +-------+-------+ |
0277   /// | |       |       |     |       |       | |
0278   /// | |       |       |     |       |       | |
0279   /// | |       |       |     |       |       | |
0280   /// | +-------+-------+     +-------+-------+ |
0281   /// |                                         |
0282   /// |       +-------+-------+-------+-------+ |
0283   /// |       |       |       |       |       | |
0284   /// |       |       |       |       |       | |
0285   /// |       |       |       |       |       | |
0286   /// |       +-------+-------+-------+-------+ |
0287   /// |       |       |       |       |       | |
0288   /// |   =   |       |       |       |       | |
0289   /// |       |       |       |       |       | |
0290   /// |       +-------+-------+-------+-------+ |
0291   /// |       |       |       |       |       | |
0292   /// |       |       |       |       |       | |
0293   /// |       |       |       |       |       | |
0294   /// |       +-------+-------+-------+-------+ |
0295   /// |                                         |
0296   /// +-----------------------------------------+
0297   /// ```
0298   ///
0299   /// @param a The first grid portal link
0300   /// @param b The second grid portal link
0301   /// @param direction The merging direction
0302   /// @param logger The logger to use for messages
0303   /// @return The merged grid portal link or nullptr if grid merging did not succeed
0304   /// @note The returned pointer can be nullptr, if the grids
0305   ///       given are not mergeable, because their binnings are
0306   ///       not compatible. This is not an error case, and needs
0307   ///       to be handled by th caller! Invalid input is handled
0308   ///       via exceptions.
0309   static std::unique_ptr<PortalLinkBase> merge(
0310       const GridPortalLink& a, const GridPortalLink& b, AxisDirection direction,
0311       const Logger& logger = getDummyLogger());
0312 
0313   /// Return the associated grid in a type-erased form
0314   /// @return The grid
0315   virtual const IGrid& grid() const = 0;
0316 
0317   /// Return the associated grid in a type-erased form
0318   /// @return The grid
0319   virtual IGrid& grid() = 0;
0320 
0321   /// Set the volume on all grid bins
0322   /// @param volume The volume to set
0323   virtual void setVolume(TrackingVolume* volume) = 0;
0324 
0325   /// Get the number of dimensions of the grid
0326   /// @return The number of dimensions
0327   virtual unsigned int dim() const = 0;
0328 
0329   /// Expand a 1D grid to a 2D one, by using the provided axis along the
0330   /// *missing* direction.
0331   /// @param other The axis to use for the missing direction,
0332   ///              can be null for auto determination
0333   /// @return A unique pointer to the 2D grid portal link
0334   virtual std::unique_ptr<GridPortalLink> extendTo2d(
0335       const IAxis* other) const = 0;
0336 
0337   /// The binning direction of the grid
0338   /// @note For 2D grids, this will always be the loc0
0339   ///       direction, depending on the surface type.
0340   /// @return The binning direction
0341   AxisDirection direction() const { return m_direction; }
0342 
0343   /// Helper function to fill the bin contents after merging.
0344   /// This called by the merging routine, and requires access to the internal
0345   /// grid state.
0346   /// @param a The first grid portal link
0347   /// @param b The second grid portal link
0348   /// @param merged The merged grid portal link
0349   /// @param direction The merging direction
0350   /// @param logger The logger to use for messages
0351   static void fillMergedGrid(const GridPortalLink& a, const GridPortalLink& b,
0352                              GridPortalLink& merged, AxisDirection direction,
0353                              const Logger& logger);
0354 
0355   /// Helper function that prints a textual representation of the grid with the
0356   /// volume names.
0357   /// @param os The output stream
0358   void printContents(std::ostream& os) const;
0359 
0360   /// Get the artifact portal links
0361   /// @return Span of artifact portal links
0362   std::span<const TrivialPortalLink> artifactPortalLinks() const;
0363   /// Set the artifact portal links
0364   /// @param links Vector of trivial portal links to set
0365   void setArtifactPortalLinks(std::vector<TrivialPortalLink> links);
0366 
0367  protected:
0368   /// Helper function to check consistency for grid on a cylinder surface
0369   /// @param grid the grid to check
0370   /// @param direction The binning direction
0371   /// @param cyl The cylinder surface
0372   static void checkConsistency(const IGrid& grid, AxisDirection direction,
0373                                const CylinderSurface& cyl);
0374 
0375   /// Helper function to check consistency for grid on a disc surface
0376   /// @param grid the grid to check
0377   /// @param direction The binning direction
0378   /// @param disc The disc surface
0379   static void checkConsistency(const IGrid& grid, AxisDirection direction,
0380                                const DiscSurface& disc);
0381   /// Helper function to check consistency for grid on a plane surface
0382   /// @param grid the grid to check
0383   /// @param direction The binning direction
0384   /// @param plane The plane surface
0385   static void checkConsistency(const IGrid& grid, AxisDirection direction,
0386                                const PlaneSurface& plane);
0387   /// Expand a 1D grid to a 2D one for a cylinder surface
0388   /// @param surface The cylinder surface
0389   /// @param other The axis to use for the missing direction,
0390   ///              can be null for auto determination
0391   /// @return A unique pointer to the 2D grid portal link
0392   std::unique_ptr<GridPortalLink> extendTo2dImpl(
0393       const std::shared_ptr<CylinderSurface>& surface,
0394       const IAxis* other) const;
0395 
0396   /// Expand a 1D grid to a 2D one for a disc surface
0397   /// @param surface The disc surface
0398   /// @param other The axis to use for the missing direction,
0399   ///              can be null for auto determination
0400   /// @return A unique pointer to the 2D grid portal link
0401   std::unique_ptr<GridPortalLink> extendTo2dImpl(
0402       const std::shared_ptr<DiscSurface>& surface, const IAxis* other) const;
0403 
0404   /// Expand a 1D grid to a 2D one for a plane surface
0405   /// @param surface The plane surface
0406   /// @param other The axis to use for the missing direction,
0407   ///              can be null for auto determination
0408   /// @return A unique pointer to the 2D grid portal link
0409   std::unique_ptr<GridPortalLink> extendTo2dImpl(
0410       const std::shared_ptr<PlaneSurface>& surface, const IAxis* other) const;
0411 
0412   /// Helper enum to declare which local direction to fill
0413   enum class FillDirection {
0414     loc0,
0415     loc1,
0416   };
0417 
0418   /// Helper function to fill a 2D grid from a 1D grid, by extending all
0419   /// bins along the different direction.
0420   /// @param dir The direction to fill
0421   /// @param grid1d The 1D grid
0422   /// @param grid2d The 2D grid
0423   static void fillGrid1dTo2d(FillDirection dir, const GridPortalLink& grid1d,
0424                              GridPortalLink& grid2d);
0425 
0426  private:
0427   AxisDirection m_direction;
0428 
0429   /// Stores the trivial portal links that were used to build this grid portal
0430   /// link, if any
0431   std::vector<TrivialPortalLink> m_artifactPortalLinks;
0432 };
0433 
0434 /// Concrete class deriving from @c GridPortalLink that boxes a concrete grid for lookup.
0435 /// @tparam Axes The axis types of the grid
0436 template <typename... Axes>
0437   requires(sizeof...(Axes) <= 2)
0438 class GridPortalLinkT : public GridPortalLink {
0439  public:
0440   /// The internal grid type
0441   using GridType = Grid<const TrackingVolume*, Axes...>;
0442 
0443   /// The dimension of the grid
0444   static constexpr std::size_t DIM = sizeof...(Axes);
0445 
0446   /// Constructor from a surface, axes and direction
0447   /// @param surface The surface
0448   /// @param direction The binning direction
0449   /// @param axes The axes for the grid
0450   /// @note The axes are checked for consistency with the bounds of @p surface.
0451   GridPortalLinkT(std::shared_ptr<RegularSurface> surface,
0452                   AxisDirection direction, Axes&&... axes)
0453       : GridPortalLink(std::move(surface), direction),
0454         m_grid(std::tuple{std::move(axes)...}) {
0455     using enum AxisDirection;
0456 
0457     if (const auto* cylinder =
0458             dynamic_cast<const CylinderSurface*>(m_surface.get())) {
0459       checkConsistency(m_grid, direction, *cylinder);
0460 
0461       if (direction == AxisRPhi) {
0462         m_projection = &projection<CylinderSurface, AxisRPhi>;
0463       } else if (direction == AxisZ) {
0464         m_projection = &projection<CylinderSurface, AxisZ>;
0465       } else {
0466         throw std::invalid_argument{"Invalid binning direction"};
0467       }
0468 
0469     } else if (const auto* disc =
0470                    dynamic_cast<const DiscSurface*>(m_surface.get())) {
0471       checkConsistency(m_grid, direction, *disc);
0472 
0473       if (direction == AxisR) {
0474         m_projection = &projection<DiscSurface, AxisR>;
0475       } else if (direction == AxisDirection::AxisPhi) {
0476         m_projection = &projection<DiscSurface, AxisPhi>;
0477       } else {
0478         throw std::invalid_argument{"Invalid binning direction"};
0479       }
0480     } else if (const auto* plane =
0481                    dynamic_cast<const PlaneSurface*>(m_surface.get())) {
0482       checkConsistency(m_grid, direction, *plane);
0483 
0484       if (direction == AxisX) {
0485         m_projection = &projection<PlaneSurface, AxisX>;
0486       } else if (direction == AxisDirection::AxisY) {
0487         m_projection = &projection<PlaneSurface, AxisY>;
0488       } else {
0489         throw std::invalid_argument{"Invalid binning direction"};
0490       }
0491 
0492     } else {
0493       throw std::logic_error{"Surface type is not supported"};
0494     }
0495   }
0496 
0497   /// Get the grid
0498   /// @return The grid
0499   const GridType& grid() const override { return m_grid; }
0500 
0501   /// Get the grid
0502   /// @return The grid
0503   GridType& grid() override { return m_grid; }
0504 
0505   /// Get the number of dimensions of the grid
0506   /// @return The number of dimensions
0507   unsigned int dim() const override { return DIM; }
0508 
0509   /// Prints an identification to the output stream
0510   /// @param os The output stream
0511   void toStream(std::ostream& os) const override {
0512     os << "GridPortalLink<dim=" << dim() << ">";
0513   }
0514 
0515   /// Makes a 2D grid from a 1D grid by extending it using an optional axis
0516   /// @param other The axis to use for the missing direction,
0517   ///              can be null for auto determination
0518   /// @return A unique pointer to the 2D grid portal link
0519   std::unique_ptr<GridPortalLink> extendTo2d(
0520       const IAxis* other) const override {
0521     if constexpr (DIM == 2) {
0522       return std::make_unique<GridPortalLinkT<Axes...>>(*this);
0523     } else {
0524       if (auto cylinder =
0525               std::dynamic_pointer_cast<CylinderSurface>(m_surface)) {
0526         return extendTo2dImpl(cylinder, other);
0527       } else if (auto disc =
0528                      std::dynamic_pointer_cast<DiscSurface>(m_surface)) {
0529         return extendTo2dImpl(disc, other);
0530       } else if (auto plane =
0531                      std::dynamic_pointer_cast<PlaneSurface>(m_surface)) {
0532         return extendTo2dImpl(plane, other);
0533       } else {
0534         throw std::logic_error{
0535             "Surface type is not supported (this should not happen)"};
0536       }
0537     }
0538   }
0539 
0540   /// Set the volume on all grid bins
0541   /// @param volume The volume to set
0542   void setVolume(TrackingVolume* volume) override {
0543     auto loc = m_grid.numLocalBins();
0544     if constexpr (GridType::DIM == 1) {
0545       for (std::size_t i = 1; i <= loc[0]; i++) {
0546         m_grid.atLocalBins({i}) = volume;
0547       }
0548     } else {
0549       for (std::size_t i = 1; i <= loc[0]; i++) {
0550         for (std::size_t j = 1; j <= loc[1]; j++) {
0551           m_grid.atLocalBins({i, j}) = volume;
0552         }
0553       }
0554     }
0555   }
0556 
0557   /// Lookup the tracking volume at a position on the surface
0558   /// @param gctx The geometry context
0559   /// @param position The local position
0560   /// @param tolerance The tolerance for the lookup
0561   /// @note The position is required to be on the associated surface
0562   /// @return The tracking volume (can be null)
0563   Result<const TrackingVolume*> resolveVolume(
0564       const GeometryContext& gctx, const Vector3& position,
0565       double tolerance = s_onSurfaceTolerance) const override {
0566     auto res = m_surface->globalToLocal(gctx, position, tolerance);
0567     if (!res.ok()) {
0568       return res.error();
0569     }
0570 
0571     const Vector2& local = *res;
0572     return resolveVolume(gctx, local, tolerance);
0573   }
0574 
0575   /// Lookup the tracking volume at a position on the surface
0576   /// @param position The local position
0577   /// @note The position is required to be on the associated surface
0578   /// @return The tracking volume (can be null)
0579   Result<const TrackingVolume*> resolveVolume(
0580       const GeometryContext& /*gctx*/, const Vector2& position,
0581       double /*tolerance*/ = s_onSurfaceTolerance) const override {
0582     throw_assert(surface().insideBounds(position, BoundaryTolerance::None()),
0583                  "Checking volume outside of bounds");
0584     return m_grid.atPosition(m_projection(position));
0585   }
0586 
0587  private:
0588   /// Helper function that's assigned to project from the 2D local position to a
0589   /// possible 1D grid.
0590   template <class surface_t, AxisDirection direction>
0591   static ActsVector<DIM> projection(const Vector2& position) {
0592     using enum AxisDirection;
0593     if constexpr (DIM == 2) {
0594       return position;
0595     } else {
0596       if constexpr (std::is_same_v<surface_t, CylinderSurface>) {
0597         static_assert(direction == AxisRPhi || direction == AxisZ,
0598                       "Invalid binning direction");
0599 
0600         if constexpr (direction == AxisRPhi) {
0601           return ActsVector<1>{position[0]};
0602         } else if constexpr (direction == AxisZ) {
0603           return ActsVector<1>{position[1]};
0604         }
0605       } else if constexpr (std::is_same_v<surface_t, DiscSurface>) {
0606         static_assert(direction == AxisR || direction == AxisPhi,
0607                       "Invalid binning direction");
0608 
0609         if constexpr (direction == AxisR) {
0610           return ActsVector<1>{position[0]};
0611         } else if constexpr (direction == AxisPhi) {
0612           return ActsVector<1>{position[1]};
0613         }
0614       } else if constexpr (std::is_same_v<surface_t, PlaneSurface>) {
0615         static_assert(direction == AxisX || direction == AxisY,
0616                       "Invalid binning direction");
0617 
0618         if constexpr (direction == AxisX) {
0619           return ActsVector<1>{position[0]};
0620         } else if constexpr (direction == AxisY) {
0621           return ActsVector<1>{position[1]};
0622         }
0623       }
0624     }
0625   }
0626 
0627   GridType m_grid;
0628 
0629   /// Stores a function pointer that can project from 2D to 1D if needed
0630   ActsVector<DIM> (*m_projection)(const Vector2& position);
0631 };
0632 
0633 }  // namespace Acts