Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-10-23 08:19:41

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