Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-07-01 07:52:25

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