Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-12-16 09:22:23

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 cyl The cylinder surface
0370   void checkConsistency(const CylinderSurface& cyl) const;
0371 
0372   /// Helper function to check consistency for grid on a disc surface
0373   /// @param disc The disc surface
0374   void checkConsistency(const DiscSurface& disc) const;
0375 
0376   /// Helper function to check consistency for grid on a plane surface
0377   /// @param plane The plane surface
0378   void checkConsistency(const PlaneSurface& plane) const;
0379 
0380   /// Expand a 1D grid to a 2D one for a cylinder surface
0381   /// @param surface The cylinder surface
0382   /// @param other The axis to use for the missing direction,
0383   ///              can be null for auto determination
0384   /// @return A unique pointer to the 2D grid portal link
0385   std::unique_ptr<GridPortalLink> extendTo2dImpl(
0386       const std::shared_ptr<CylinderSurface>& surface,
0387       const IAxis* other) const;
0388 
0389   /// Expand a 1D grid to a 2D one for a disc surface
0390   /// @param surface The disc surface
0391   /// @param other The axis to use for the missing direction,
0392   ///              can be null for auto determination
0393   /// @return A unique pointer to the 2D grid portal link
0394   std::unique_ptr<GridPortalLink> extendTo2dImpl(
0395       const std::shared_ptr<DiscSurface>& surface, const IAxis* other) const;
0396 
0397   /// Expand a 1D grid to a 2D one for a plane surface
0398   /// @param surface The plane surface
0399   /// @param other The axis to use for the missing direction,
0400   ///              can be null for auto determination
0401   /// @return A unique pointer to the 2D grid portal link
0402   std::unique_ptr<GridPortalLink> extendTo2dImpl(
0403       const std::shared_ptr<PlaneSurface>& surface, const IAxis* other) const;
0404 
0405   /// Helper enum to declare which local direction to fill
0406   enum class FillDirection {
0407     loc0,
0408     loc1,
0409   };
0410 
0411   /// Helper function to fill a 2D grid from a 1D grid, by extending all
0412   /// bins along the different direction.
0413   /// @param dir The direction to fill
0414   /// @param grid1d The 1D grid
0415   /// @param grid2d The 2D grid
0416   static void fillGrid1dTo2d(FillDirection dir, const GridPortalLink& grid1d,
0417                              GridPortalLink& grid2d);
0418 
0419  private:
0420   AxisDirection m_direction;
0421 
0422   /// Stores the trivial portal links that were used to build this grid portal
0423   /// link, if any
0424   std::vector<TrivialPortalLink> m_artifactPortalLinks;
0425 };
0426 
0427 /// Concrete class deriving from @c GridPortalLink that boxes a concrete grid for lookup.
0428 /// @tparam Axes The axis types of the grid
0429 template <typename... Axes>
0430   requires(sizeof...(Axes) <= 2)
0431 class GridPortalLinkT : public GridPortalLink {
0432  public:
0433   /// The internal grid type
0434   using GridType = Grid<const TrackingVolume*, Axes...>;
0435 
0436   /// The dimension of the grid
0437   static constexpr std::size_t DIM = sizeof...(Axes);
0438 
0439   /// Constructor from a surface, axes and direction
0440   /// @param surface The surface
0441   /// @param direction The binning direction
0442   /// @param axes The axes for the grid
0443   /// @note The axes are checked for consistency with the bounds of @p surface.
0444   GridPortalLinkT(std::shared_ptr<RegularSurface> surface,
0445                   AxisDirection direction, Axes&&... axes)
0446       : GridPortalLink(std::move(surface), direction),
0447         m_grid(std::tuple{std::move(axes)...}) {
0448     using enum AxisDirection;
0449 
0450     if (const auto* cylinder =
0451             dynamic_cast<const CylinderSurface*>(m_surface.get())) {
0452       checkConsistency(*cylinder);
0453 
0454       if (direction == AxisRPhi) {
0455         m_projection = &projection<CylinderSurface, AxisRPhi>;
0456       } else if (direction == AxisZ) {
0457         m_projection = &projection<CylinderSurface, AxisZ>;
0458       } else {
0459         throw std::invalid_argument{"Invalid binning direction"};
0460       }
0461 
0462     } else if (const auto* disc =
0463                    dynamic_cast<const DiscSurface*>(m_surface.get())) {
0464       checkConsistency(*disc);
0465 
0466       if (direction == AxisR) {
0467         m_projection = &projection<DiscSurface, AxisR>;
0468       } else if (direction == AxisDirection::AxisPhi) {
0469         m_projection = &projection<DiscSurface, AxisPhi>;
0470       } else {
0471         throw std::invalid_argument{"Invalid binning direction"};
0472       }
0473     } else if (const auto* plane =
0474                    dynamic_cast<const PlaneSurface*>(m_surface.get())) {
0475       checkConsistency(*plane);
0476 
0477       if (direction == AxisX) {
0478         m_projection = &projection<PlaneSurface, AxisX>;
0479       } else if (direction == AxisDirection::AxisY) {
0480         m_projection = &projection<PlaneSurface, AxisY>;
0481       } else {
0482         throw std::invalid_argument{"Invalid binning direction"};
0483       }
0484 
0485     } else {
0486       throw std::logic_error{"Surface type is not supported"};
0487     }
0488   }
0489 
0490   /// Get the grid
0491   /// @return The grid
0492   const GridType& grid() const override { return m_grid; }
0493 
0494   /// Get the grid
0495   /// @return The grid
0496   GridType& grid() override { return m_grid; }
0497 
0498   /// Get the number of dimensions of the grid
0499   /// @return The number of dimensions
0500   unsigned int dim() const override { return DIM; }
0501 
0502   /// Prints an identification to the output stream
0503   /// @param os The output stream
0504   void toStream(std::ostream& os) const override {
0505     os << "GridPortalLink<dim=" << dim() << ">";
0506   }
0507 
0508   /// Makes a 2D grid from a 1D grid by extending it using an optional axis
0509   /// @param other The axis to use for the missing direction,
0510   ///              can be null for auto determination
0511   /// @return A unique pointer to the 2D grid portal link
0512   std::unique_ptr<GridPortalLink> extendTo2d(
0513       const IAxis* other) const override {
0514     if constexpr (DIM == 2) {
0515       return std::make_unique<GridPortalLinkT<Axes...>>(*this);
0516     } else {
0517       if (auto cylinder =
0518               std::dynamic_pointer_cast<CylinderSurface>(m_surface)) {
0519         return extendTo2dImpl(cylinder, other);
0520       } else if (auto disc =
0521                      std::dynamic_pointer_cast<DiscSurface>(m_surface)) {
0522         return extendTo2dImpl(disc, other);
0523       } else if (auto plane =
0524                      std::dynamic_pointer_cast<PlaneSurface>(m_surface)) {
0525         return extendTo2dImpl(plane, other);
0526       } else {
0527         throw std::logic_error{
0528             "Surface type is not supported (this should not happen)"};
0529       }
0530     }
0531   }
0532 
0533   /// Set the volume on all grid bins
0534   /// @param volume The volume to set
0535   void setVolume(TrackingVolume* volume) override {
0536     auto loc = m_grid.numLocalBins();
0537     if constexpr (GridType::DIM == 1) {
0538       for (std::size_t i = 1; i <= loc[0]; i++) {
0539         m_grid.atLocalBins({i}) = volume;
0540       }
0541     } else {
0542       for (std::size_t i = 1; i <= loc[0]; i++) {
0543         for (std::size_t j = 1; j <= loc[1]; j++) {
0544           m_grid.atLocalBins({i, j}) = volume;
0545         }
0546       }
0547     }
0548   }
0549 
0550   /// Lookup the tracking volume at a position on the surface
0551   /// @param gctx The geometry context
0552   /// @param position The local position
0553   /// @param tolerance The tolerance for the lookup
0554   /// @note The position is required to be on the associated surface
0555   /// @return The tracking volume (can be null)
0556   Result<const TrackingVolume*> resolveVolume(
0557       const GeometryContext& gctx, const Vector3& position,
0558       double tolerance = s_onSurfaceTolerance) const override {
0559     auto res = m_surface->globalToLocal(gctx, position, tolerance);
0560     if (!res.ok()) {
0561       return res.error();
0562     }
0563 
0564     const Vector2& local = *res;
0565     return resolveVolume(gctx, local, tolerance);
0566   }
0567 
0568   /// Lookup the tracking volume at a position on the surface
0569   /// @param position The local position
0570   /// @note The position is required to be on the associated surface
0571   /// @return The tracking volume (can be null)
0572   Result<const TrackingVolume*> resolveVolume(
0573       const GeometryContext& /*gctx*/, const Vector2& position,
0574       double /*tolerance*/ = s_onSurfaceTolerance) const override {
0575     throw_assert(surface().insideBounds(position, BoundaryTolerance::None()),
0576                  "Checking volume outside of bounds");
0577     return m_grid.atPosition(m_projection(position));
0578   }
0579 
0580  private:
0581   /// Helper function that's assigned to project from the 2D local position to a
0582   /// possible 1D grid.
0583   template <class surface_t, AxisDirection direction>
0584   static ActsVector<DIM> projection(const Vector2& position) {
0585     using enum AxisDirection;
0586     if constexpr (DIM == 2) {
0587       return position;
0588     } else {
0589       if constexpr (std::is_same_v<surface_t, CylinderSurface>) {
0590         static_assert(direction == AxisRPhi || direction == AxisZ,
0591                       "Invalid binning direction");
0592 
0593         if constexpr (direction == AxisRPhi) {
0594           return ActsVector<1>{position[0]};
0595         } else if constexpr (direction == AxisZ) {
0596           return ActsVector<1>{position[1]};
0597         }
0598       } else if constexpr (std::is_same_v<surface_t, DiscSurface>) {
0599         static_assert(direction == AxisR || direction == AxisPhi,
0600                       "Invalid binning direction");
0601 
0602         if constexpr (direction == AxisR) {
0603           return ActsVector<1>{position[0]};
0604         } else if constexpr (direction == AxisPhi) {
0605           return ActsVector<1>{position[1]};
0606         }
0607       } else if constexpr (std::is_same_v<surface_t, PlaneSurface>) {
0608         static_assert(direction == AxisX || direction == AxisY,
0609                       "Invalid binning direction");
0610 
0611         if constexpr (direction == AxisX) {
0612           return ActsVector<1>{position[0]};
0613         } else if constexpr (direction == AxisY) {
0614           return ActsVector<1>{position[1]};
0615         }
0616       }
0617     }
0618   }
0619 
0620   GridType m_grid;
0621 
0622   /// Stores a function pointer that can project from 2D to 1D if needed
0623   ActsVector<DIM> (*m_projection)(const Vector2& position);
0624 };
0625 
0626 }  // namespace Acts