Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-04-08 07:46:39

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