Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-12-15 09:42:00

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/Algebra.hpp"
0012 #include "Acts/Definitions/Common.hpp"
0013 #include "Acts/Detector/DetectorVolumeVisitorConcept.hpp"
0014 #include "Acts/Detector/Portal.hpp"
0015 #include "Acts/Detector/PortalGenerators.hpp"
0016 #include "Acts/Geometry/Extent.hpp"
0017 #include "Acts/Geometry/GeometryContext.hpp"
0018 #include "Acts/Geometry/GeometryIdentifier.hpp"
0019 #include "Acts/Geometry/VolumeBounds.hpp"
0020 #include "Acts/Material/IVolumeMaterial.hpp"
0021 #include "Acts/Navigation/NavigationDelegates.hpp"
0022 #include "Acts/Navigation/NavigationState.hpp"
0023 #include "Acts/Surfaces/BoundaryTolerance.hpp"
0024 #include "Acts/Surfaces/SurfaceVisitorConcept.hpp"
0025 #include "Acts/Utilities/BoundingBox.hpp"
0026 #include "Acts/Utilities/Delegate.hpp"
0027 #include "Acts/Utilities/Helpers.hpp"
0028 
0029 #include <algorithm>
0030 #include <cstddef>
0031 #include <memory>
0032 #include <stdexcept>
0033 #include <string>
0034 #include <utility>
0035 #include <vector>
0036 
0037 namespace Acts {
0038 
0039 class Surface;
0040 class IVolumeMaterial;
0041 class VolumeBounds;
0042 
0043 namespace Experimental {
0044 
0045 class DetectorVolume;
0046 class Detector;
0047 
0048 /// A detector volume description which can be:
0049 ///
0050 /// @note A detector volume holds non-const objects internally
0051 /// that are allowed to be modified as long as the geometry
0052 /// is not yet closed. Using this, material can be attached,
0053 /// and GeometryIdentifier can be set at construction time.
0054 ///
0055 /// @note The construction of DetectorVolumes is done via a dedicated
0056 /// factory, this is necessary as then the shared_ptr is non-weak and it
0057 /// can be registered in the portal generator for further geometry processing.
0058 ///
0059 /// @note Navigation is always done by plain pointers, while
0060 /// object ownership is done by shared/unique pointers.
0061 class DetectorVolume : public std::enable_shared_from_this<DetectorVolume> {
0062  public:
0063   /// Type alias for axis-aligned bounding box of detector volume
0064   using BoundingBox =
0065       Acts::AxisAlignedBoundingBox<Acts::Experimental::DetectorVolume, double,
0066                                    3>;
0067 
0068   friend class DetectorVolumeFactory;
0069 
0070   /// Nested object store that holds the internal (non-const),
0071   /// reference counted objects and provides an external
0072   /// (const raw pointer) access
0073   ///
0074   /// @tparam internal_type is the internal storage representation,
0075   /// has to comply with std::shared_ptr semantics
0076   ///
0077   template <typename internal_type>
0078   struct ObjectStore {
0079     /// The internal storage vector
0080     std::vector<internal_type> internal = {};
0081 
0082     /// The external storage vector, const raw pointer
0083     std::vector<const typename internal_type::element_type*> external = {};
0084 
0085     /// Store constructor
0086     ///
0087     /// @param objects are the ones copied into the internal store
0088     explicit ObjectStore(std::vector<internal_type> objects)
0089         : internal(std::move(objects)) {
0090       external = unpackConstSmartPointers(internal);
0091     }
0092 
0093     ObjectStore() = default;
0094   };
0095 
0096  protected:
0097   /// Create a detector volume - with surfaces and/or inserted volumes
0098   ///
0099   /// @param gctx the geometry context while building - for future contextual store
0100   /// @param name the volume name
0101   /// @param transform the transform defining the volume position
0102   /// @param bounds the volume bounds
0103   /// @param surfaces are the contained surfaces of this volume
0104   /// @param volumes are the contains volumes of this volume
0105   /// @param externalNavigation is a Delegate to find the associated volume
0106   /// @param internalNavigation the navigation state updator for surfaces/portals
0107   ///
0108   /// @note throws exception if misconfigured: no bounds
0109   /// @note throws exception if ghe portal general or navigation
0110   ///       state updator delegates are not connected
0111   DetectorVolume(const GeometryContext& gctx, std::string name,
0112                  const Transform3& transform,
0113                  std::shared_ptr<VolumeBounds> bounds,
0114                  std::vector<std::shared_ptr<Surface>> surfaces,
0115                  std::vector<std::shared_ptr<DetectorVolume>> volumes,
0116                  ExternalNavigationDelegate externalNavigation,
0117                  InternalNavigationDelegate internalNavigation) noexcept(false);
0118 
0119   /// Create a detector volume - empty/gap volume constructor
0120   ///
0121   /// @param gctx the geometry context while building - for future contextual store
0122   /// @param name the volume name
0123   /// @param transform the transform defining the volume position
0124   /// @param bounds the volume bounds
0125   /// @param internalNavigation the navigation state updator for surfaces/portals
0126   ///
0127   /// @note throws exception if misconfigured: no bounds
0128   /// @note throws exception if ghe portal general or navigation
0129   ///       state updator delegates are not connected
0130   DetectorVolume(const GeometryContext& gctx, std::string name,
0131                  const Transform3& transform,
0132                  std::shared_ptr<VolumeBounds> bounds,
0133                  InternalNavigationDelegate internalNavigation) noexcept(false);
0134 
0135   // === Factory Methods ===
0136 
0137   /// @fn static std::shared_ptr<DetectorVolume> makeShared(const GeometryContext& gctx, std::string name, const Transform3& transform, std::shared_ptr<VolumeBounds> bounds, std::vector<std::shared_ptr<Surface>> surfaces, std::vector<std::shared_ptr<DetectorVolume>> volumes, ExternalNavigationDelegate externalNavigation, InternalNavigationDelegate internalNavigation)
0138   /// @brief Protected factory method for producing memory managed instances of DetectorVolume.
0139   ///
0140   /// This overload creates a detector volume with full navigation capabilities,
0141   /// supporting both external navigation (to other volumes) and internal
0142   /// navigation (within the volume). It can contain both surfaces and
0143   /// sub-volumes for complex detector hierarchies.
0144   ///
0145   /// @param gctx The geometry context for this call
0146   /// @param name The name of the detector volume
0147   /// @param transform The transform3 object of the volume
0148   /// @param bounds Shared pointer to the volume bounds
0149   /// @param surfaces Vector of shared pointers to surfaces
0150   /// @param volumes Vector of shared pointers to sub-volumes
0151   /// @param externalNavigation The external navigation delegate
0152   /// @param internalNavigation The internal navigation delegate
0153   ///
0154   /// @return A shared pointer to the created detector volume
0155   ///
0156   /// @note This is called by the @class DetectorVolumeFactory
0157   /// @note This is the full-featured factory method for complex detector volumes
0158   /// @note This is a protected static method for controlled object creation
0159   static std::shared_ptr<DetectorVolume> makeShared(
0160       const GeometryContext& gctx, std::string name,
0161       const Transform3& transform, std::shared_ptr<VolumeBounds> bounds,
0162       std::vector<std::shared_ptr<Surface>> surfaces,
0163       std::vector<std::shared_ptr<DetectorVolume>> volumes,
0164       ExternalNavigationDelegate externalNavigation,
0165       InternalNavigationDelegate internalNavigation);
0166 
0167   /// @fn static std::shared_ptr<DetectorVolume> makeShared(const GeometryContext& gctx, std::string name, const Transform3& transform, std::shared_ptr<VolumeBounds> bounds, InternalNavigationDelegate internalNavigation)
0168   /// @brief Protected factory method for producing memory managed instances of DetectorVolume.
0169   ///
0170   /// This overload creates a simpler detector volume with only internal
0171   /// navigation capabilities. It is suitable for volumes that don't need
0172   /// to navigate to other volumes or contain complex sub-structures.
0173   ///
0174   /// @param gctx The geometry context for this call
0175   /// @param name The name of the detector volume
0176   /// @param transform The transform3 object of the volume
0177   /// @param bounds Shared pointer to the volume bounds
0178   /// @param internalNavigation The internal navigation delegate
0179   ///
0180   /// @return A shared pointer to the created detector volume
0181   ///
0182   /// @note This is called by the @class DetectorVolumeFactory
0183   /// @note This is the simplified factory method for basic detector volumes
0184   /// @note This is a protected static method for controlled object creation
0185   static std::shared_ptr<DetectorVolume> makeShared(
0186       const GeometryContext& gctx, std::string name,
0187       const Transform3& transform, std::shared_ptr<VolumeBounds> bounds,
0188       InternalNavigationDelegate internalNavigation);
0189 
0190  public:
0191   /// Retrieve a @c std::shared_ptr for this surface (non-const version)
0192   ///
0193   /// @note Will error if this was not created through the @c makeShared factory
0194   ///       since it needs access to the original reference. In C++14 this is
0195   ///       undefined behavior (but most likely implemented as a @c bad_weak_ptr
0196   ///       exception), in C++17 it is defined as that exception.
0197   /// @note Only call this if you need shared ownership of this object.
0198   ///
0199   /// @return The shared pointer
0200   std::shared_ptr<DetectorVolume> getSharedPtr();
0201 
0202   /// Retrieve a @c std::shared_ptr for this surface (const version)
0203   ///
0204   /// @note Will error if this was not created through the @c makeShared factory
0205   ///       since it needs access to the original reference. In C++14 this is
0206   ///       undefined behavior, but most likely implemented as a @c bad_weak_ptr
0207   ///       exception, in C++17 it is defined as that exception.
0208   /// @note Only call this if you need shared ownership of this object.
0209   ///
0210   /// @return The shared pointer
0211   std::shared_ptr<const DetectorVolume> getSharedPtr() const;
0212 
0213   /// Const access to the transform
0214   ///
0215   /// @param gctx the geometry context
0216   ///
0217   /// @note the geometry context is currently ignored, but
0218   ///       is a placeholder for eventually misaligned volumes
0219   ///
0220   /// @return const reference to the contextual transform
0221   const Transform3& transform(
0222       const GeometryContext& gctx = GeometryContext()) const;
0223 
0224   /// Const access to the center
0225   ///
0226   /// @param gctx the geometry context
0227   ///
0228   /// @note the geometry context is currently ignored, but
0229   ///       is a placeholder for eventually misaligned volumes
0230   ///
0231   /// @return a contextually created center
0232   Vector3 center(const GeometryContext& gctx = GeometryContext()) const;
0233 
0234   /// Const access to the volume bounds
0235   ///
0236   /// @return const reference to the volume bounds object
0237   const VolumeBounds& volumeBounds() const;
0238 
0239   /// Check if a point is inside this volume. Subvolumes will not be checked.
0240   ///
0241   /// @param gctx the geometry context
0242   /// @param position the position for the inside check
0243   ///
0244   /// @return a bool to indicate inside/outside
0245   bool inside(const GeometryContext& gctx, const Vector3& position) const;
0246 
0247   /// Check if a point is exclusively inside this volume i.e. this point is not
0248   /// inside a subvolume.
0249   ///
0250   /// @param gctx the geometry context
0251   /// @param position the position for the inside check
0252   ///
0253   /// @return a bool to indicate inside/outside
0254   bool exclusivelyInside(const GeometryContext& gctx,
0255                          const Vector3& position) const;
0256 
0257   /// The Extent for this volume
0258   ///
0259   /// @param gctx is the geometry context
0260   /// @param nseg is the number of segments to approximate
0261   ///
0262   /// @return an Extent object
0263   Extent extent(const GeometryContext& gctx, std::size_t nseg = 1) const;
0264 
0265   /// Initialize/update the navigation status in this environment
0266   ///
0267   /// This method calls:
0268   ///
0269   /// - the local navigation delegate for candidate surfaces
0270   /// - the portal navigation delegate for candidate exit portals
0271   /// - set the current detector volume
0272   ///
0273   /// @param gctx is the current geometry context
0274   /// @param nState [in,out] is the detector navigation state to be updated
0275   ///
0276   void updateNavigationState(const GeometryContext& gctx,
0277                              NavigationState& nState) const;
0278 
0279   /// Non-const access to the portals
0280   ///
0281   /// @return the portal shared pointer store
0282   std::vector<std::shared_ptr<Portal>>& portalPtrs();
0283 
0284   /// Non-const access to the surfaces
0285   ///
0286   /// @return the surfaces shared pointer store
0287   std::vector<std::shared_ptr<Surface>>& surfacePtrs();
0288 
0289   /// Non-const access to the volumes
0290   ///
0291   /// @return the volumes shared pointer store
0292   std::vector<std::shared_ptr<DetectorVolume>>& volumePtrs();
0293 
0294   /// Const access to the detector portals
0295   ///
0296   /// @note an empty vector indicates a container volume
0297   /// that has not been properly connected
0298   ///
0299   /// @return a vector to const Portal raw pointers
0300   const std::vector<const Portal*>& portals() const;
0301 
0302   /// Const access to the surfaces
0303   ///
0304   /// @note an empty vector indicates either gap volume
0305   /// or container volume, a non-empty vector indicates
0306   /// a layer volume.
0307   ///
0308   /// @return a vector to const Surface raw pointers
0309   const std::vector<const Surface*>& surfaces() const;
0310 
0311   /// Const access to sub volumes
0312   ///
0313   /// @note and empty vector indicates this is either a
0314   /// gap volume or a layer volume, in any case it means
0315   /// the volume is on navigation level and the portals
0316   /// need to be connected
0317   ///
0318   /// @return a vector to const DetectorVolume raw pointers
0319   const std::vector<const DetectorVolume*>& volumes() const;
0320 
0321   /// Const access to the detector volume updator
0322   /// @return Reference to the external navigation delegate
0323   const ExternalNavigationDelegate& externalNavigation() const;
0324 
0325   /// @brief Visit all reachable surfaces of the detector
0326   ///
0327   /// @tparam visitor_t Type of the callable visitor
0328   ///
0329   /// @param visitor will be called for each found surface,
0330   /// it will be handed down to contained volumes and portals
0331   template <SurfaceVisitor visitor_t>
0332   void visitSurfaces(visitor_t&& visitor) const {
0333     for (const auto& s : surfaces()) {
0334       visitor(s);
0335     }
0336     for (const auto& p : portals()) {
0337       p->visitSurface(std::forward<visitor_t>(visitor));
0338     }
0339     for (const auto& v : volumes()) {
0340       v->visitSurfaces(std::forward<visitor_t>(visitor));
0341     }
0342   }
0343 
0344   /// @brief Visit all reachable surfaces of the detector - non-const
0345   ///
0346   /// @tparam visitor_t Type of the callable visitor
0347   ///
0348   /// @param visitor will be called for each found surface,
0349   /// it will be handed down to contained volumes and portals
0350   template <MutableSurfaceVisitor visitor_t>
0351   void visitMutableSurfaces(visitor_t&& visitor) {
0352     for (auto& s : surfacePtrs()) {
0353       visitor(s.get());
0354     }
0355     for (auto& p : portalPtrs()) {
0356       p->visitMutableSurface(std::forward<visitor_t>(visitor));
0357     }
0358     for (auto& v : volumePtrs()) {
0359       v->visitMutableSurfaces(std::forward<visitor_t>(visitor));
0360     }
0361   }
0362 
0363   /// @brief Visit all reachable detector volumes of the detector
0364   ///
0365   /// @tparam visitor_t Type of the callable visitor
0366   ///
0367   /// @param visitor will be handed to each root volume,
0368   /// eventually contained volumes within the root volumes are
0369   /// handled by the root volume
0370   ///
0371   /// @note if a context is needed for the visit, the vistitor has to provide
0372   /// it, e.g. as a private member
0373   template <DetectorVolumeVisitor visitor_t>
0374   void visitVolumes(visitor_t&& visitor) const {
0375     visitor(this);
0376     for (const auto& v : volumes()) {
0377       v->visitVolumes(std::forward<visitor_t>(visitor));
0378     }
0379   }
0380 
0381   /// @brief Visit all reachable detector volumes of the detector - non-const
0382   ///
0383   /// @tparam visitor_t Type of the callable visitor
0384   ///
0385   /// @param visitor will be handed to each root volume,
0386   /// eventually contained volumes within the root volumes are
0387   /// handled by the root volume
0388   ///
0389   /// @note if a context is needed for the visit, the vistitor has to provide
0390   /// it, e.g. as a private member
0391   template <MutableDetectorVolumeVisitor visitor_t>
0392   void visitMutableVolumes(visitor_t&& visitor) {
0393     visitor(this);
0394     for (auto& v : volumePtrs()) {
0395       v->visitMutableVolumes(std::forward<visitor_t>(visitor));
0396     }
0397   }
0398 
0399   /// This method allows to udate the navigation state updator
0400   /// module.
0401   ///
0402   /// @param internalNavigation the new navigation state updator for surfaces
0403   /// @param surfaces the surfaces the new navigation state updator points to
0404   /// @param volumes the volumes the new navigation state updator points to
0405   ///
0406   void assignInternalNavigation(
0407       InternalNavigationDelegate internalNavigation,
0408       const std::vector<std::shared_ptr<Surface>>& surfaces = {},
0409       const std::vector<std::shared_ptr<DetectorVolume>>& volumes = {});
0410 
0411   /// Const access to the navigation state updator
0412   /// @return Reference to the internal navigation delegate
0413   const InternalNavigationDelegate& internalNavigation() const;
0414 
0415   /// Update a portal given a portal index
0416   ///
0417   /// @param portal the portal to be updated
0418   /// @param pIndex the portal index
0419   ///
0420   /// @note throws exception if portal index out of bounds
0421   void updatePortal(std::shared_ptr<Portal> portal,
0422                     unsigned int pIndex) noexcept(false);
0423 
0424   /// Final closing of portal, i.e. this sets the end of world
0425   void closePortals();
0426 
0427   /// Assign the volume material description
0428   ///
0429   /// This method allows to load a material description during the
0430   /// detector geometry building, and assigning it (potentially multiple)
0431   /// times to detector volumes.
0432   ///
0433   /// @param material Material description associated to this volumw
0434   void assignVolumeMaterial(std::shared_ptr<const IVolumeMaterial> material);
0435 
0436   /// Const access to the volume amterial
0437   /// @return Pointer to the volume material, nullptr if none assigned
0438   const IVolumeMaterial* volumeMaterial() const;
0439 
0440   /// @return the name of the volume
0441   const std::string& name() const;
0442 
0443   /// @return the geometry identifier
0444   const GeometryIdentifier& geometryId() const;
0445 
0446   /// Set the geometry identifier
0447   /// @note no checking is done, it will overwrite any existing id
0448   ///
0449   /// @param geoID is the geometry Id that is set to the object
0450   void assignGeometryId(const GeometryIdentifier& geoID);
0451 
0452   /// Assign Detector to this volume (for back navigation issues)
0453   /// @param detector the parenting detector class
0454   void assignDetector(const Detector& detector);
0455 
0456   /// Const access to the detector
0457   /// @return Pointer to the parent detector, nullptr if none assigned
0458   const Detector* detector() const;
0459 
0460   /// Get the bounding box of this detector volume
0461   /// @return Const reference to the bounding box
0462   const BoundingBox& getBoundingBox() const;
0463 
0464  private:
0465   /// Internal construction method that calls the portal generator
0466   ///
0467   /// @param gctx the current geometry context object, e.g. alignment
0468   /// @param portalGenerator the generator for portals
0469   ///
0470   /// @note throws exception if provided parameters are inconsistent
0471   void construct(const GeometryContext& gctx,
0472                  const PortalGenerator& portalGenerator) noexcept(false);
0473 
0474   // Check containment - only in debug mode
0475   ///
0476   /// @param gctx the current geometry context object, e.g. alignment
0477   /// @param nseg is the number of segments to approximate
0478   ///
0479   /// @return a boolean indicating if the objects are properly contained
0480   bool checkContainment(const GeometryContext& gctx,
0481                         std::size_t nseg = 1) const;
0482 
0483   /// build the bounding box
0484   ///
0485   void createBoundingBox(const GeometryContext& gctx);
0486 
0487   /// Name of the volume
0488   std::string m_name = "Unnamed";
0489 
0490   /// Transform to place the bolume
0491   Transform3 m_transform = Transform3::Identity();
0492 
0493   /// Volume boundaries
0494   std::shared_ptr<VolumeBounds> m_bounds = nullptr;
0495 
0496   /// Portal store (internal/external)
0497   ObjectStore<std::shared_ptr<Portal>> m_portals;
0498 
0499   /// Surface store (internal/external)
0500   ObjectStore<std::shared_ptr<Surface>> m_surfaces;
0501 
0502   /// Volume store (internal/external)
0503   ObjectStore<std::shared_ptr<DetectorVolume>> m_volumes;
0504 
0505   /// BoundingBox
0506   std::shared_ptr<const BoundingBox> m_boundingBox;
0507 
0508   ExternalNavigationDelegate m_externalNavigation;
0509 
0510   /// The navigation state updator
0511   InternalNavigationDelegate m_internalNavigation;
0512 
0513   /// Volume material (optional)
0514   std::shared_ptr<const IVolumeMaterial> m_volumeMaterial = nullptr;
0515 
0516   /// GeometryIdentifier of this volume
0517   GeometryIdentifier m_geometryId{0};
0518 
0519   /// The detector it belongs to
0520   const Detector* m_detector = nullptr;
0521 };
0522 
0523 /// @brief  A detector volume factory which first constructs the detector volume
0524 /// and then constructs the portals. This ensures that the std::shared_ptr
0525 /// holding the detector volume is not weak when assigning to the portals.
0526 ///
0527 /// @note Optional containment check is invoked by setting the number
0528 /// of segments nSeg to be greater than 0
0529 class DetectorVolumeFactory {
0530  public:
0531   /// Create a detector volume - from factory
0532   /// @param portalGenerator Generator for volume portals
0533   /// @param gctx Geometry context for construction
0534   /// @param name Name of the detector volume
0535   /// @param transform Volume transformation
0536   /// @param bounds Volume boundaries
0537   /// @param surfaces Collection of surfaces within the volume
0538   /// @param volumes Collection of sub-volumes
0539   /// @param externalNavigation External navigation delegate
0540   /// @param internalNavigation Internal navigation delegate
0541   /// @param nSeg Number of segments for containment check
0542   /// @return Shared pointer to the constructed detector volume
0543   static std::shared_ptr<DetectorVolume> construct(
0544       const PortalGenerator& portalGenerator, const GeometryContext& gctx,
0545       const std::string& name, const Transform3& transform,
0546       std::shared_ptr<VolumeBounds> bounds,
0547       const std::vector<std::shared_ptr<Surface>>& surfaces,
0548       const std::vector<std::shared_ptr<DetectorVolume>>& volumes,
0549       ExternalNavigationDelegate externalNavigation,
0550       InternalNavigationDelegate internalNavigation, int nSeg = -1) {
0551     auto dVolume = DetectorVolume::makeShared(
0552         gctx, name, transform, std::move(bounds), surfaces, volumes,
0553         std::move(externalNavigation), std::move(internalNavigation));
0554     dVolume->construct(gctx, portalGenerator);
0555 
0556     /// Volume extent is constructed from the portals
0557     /// So the surface/subvolume containment
0558     /// check has to happen here
0559     if (nSeg > 0 && !dVolume->checkContainment(gctx, nSeg)) {
0560       throw std::invalid_argument(
0561           "DetectorVolume: surfaces or subvolumes are not contained by volume");
0562     }
0563     return dVolume;
0564   }
0565 
0566   /// Create a detector volume - from factory
0567   /// @param portalGenerator Generator for volume portals
0568   /// @param gctx Geometry context for construction
0569   /// @param name Name of the detector volume
0570   /// @param transform Volume transformation
0571   /// @param bounds Volume boundaries
0572   /// @param internalNavigation Internal navigation delegate
0573   /// @return Shared pointer to the constructed detector volume
0574   static std::shared_ptr<DetectorVolume> construct(
0575       const PortalGenerator& portalGenerator, const GeometryContext& gctx,
0576       std::string name, const Transform3& transform,
0577       std::shared_ptr<VolumeBounds> bounds,
0578       InternalNavigationDelegate internalNavigation) {
0579     auto dVolume = DetectorVolume::makeShared(gctx, std::move(name), transform,
0580                                               std::move(bounds),
0581                                               std::move(internalNavigation));
0582     dVolume->construct(gctx, portalGenerator);
0583     return dVolume;
0584   }
0585 };
0586 
0587 /// Helper extractors: all portals
0588 struct AllPortalsExtractor {
0589   /// Extract the portals from the volume
0590   ///
0591   /// @param gctx the geometry contextfor this extraction call
0592   /// @param nState is the current navigation state
0593   ///
0594   /// @return a vector of raw Portal pointers
0595   inline static const std::vector<const Portal*> extract(
0596       [[maybe_unused]] const GeometryContext& gctx,
0597       const NavigationState& nState) {
0598     if (nState.currentVolume == nullptr) {
0599       throw std::runtime_error(
0600           "AllPortalsExtractor: no detector volume given.");
0601     }
0602     return nState.currentVolume->portals();
0603   }
0604 };
0605 
0606 /// Helper extractors: all surfaces
0607 struct AllSurfacesExtractor {
0608   /// Extract the surfaces from the volume
0609   ///
0610   /// @param gctx the geometry contextfor this extraction call
0611   /// @param nState is the current navigation state
0612   /// @param indices is an ignored index vector
0613   ///
0614   /// @return a vector of raw Surface pointers
0615   inline static const std::vector<const Surface*> extract(
0616       [[maybe_unused]] const GeometryContext& gctx,
0617       const NavigationState& nState,
0618       [[maybe_unused]] const std::vector<std::size_t>& indices = {}) {
0619     if (nState.currentVolume == nullptr) {
0620       throw std::runtime_error(
0621           "AllSurfacesExtractor: no detector volume given.");
0622     }
0623     return nState.currentVolume->surfaces();
0624   }
0625 };
0626 
0627 /// Helper extractors: indexed surfaces
0628 struct IndexedSurfacesExtractor {
0629   /// Extract the surfaces from the volume
0630   ///
0631   /// @param gctx the geometry contextfor this extraction call
0632   /// @param nState is the current navigation state
0633   /// @param indices are access indices into the surfaces store
0634   ///
0635   /// @note no out of boudns checking is done
0636   ///
0637   /// @return a vector of raw Surface pointers
0638   inline static const std::vector<const Surface*> extract(
0639       [[maybe_unused]] const GeometryContext& gctx,
0640       const NavigationState& nState, const std::vector<std::size_t>& indices) {
0641     if (nState.currentVolume == nullptr) {
0642       throw std::runtime_error(
0643           "IndexedSurfacesExtractor: no detector volume given.");
0644     }
0645     // Get the surface container
0646     const auto& surfaces = nState.currentVolume->surfaces();
0647     // The extracted surfaces
0648     std::vector<const Surface*> eSurfaces;
0649     eSurfaces.reserve(indices.size());
0650     std::ranges::for_each(
0651         indices, [&](const auto& i) { eSurfaces.push_back(surfaces[i]); });
0652     return eSurfaces;
0653   }
0654 };
0655 
0656 /// Helper extractors: all sub volumes of a volume
0657 struct AllSubVolumesExtractor {
0658   /// Extract the sub volumes from the volume
0659   ///
0660   /// @param gctx the geometry contextfor this extraction call
0661   /// @param nState is the current navigation state
0662   /// @param indices are access indices into the volume store (ignored)
0663   ///
0664   /// @return a vector of raw DetectorVolume pointers
0665   inline static const std::vector<const DetectorVolume*> extract(
0666       [[maybe_unused]] const GeometryContext& gctx,
0667       const NavigationState& nState,
0668       [[maybe_unused]] const std::vector<std::size_t>& indices = {}) {
0669     if (nState.currentVolume == nullptr) {
0670       throw std::runtime_error(
0671           "AllSubVolumesExtractor: no detector volume given.");
0672     }
0673     return nState.currentVolume->volumes();
0674   }
0675 };
0676 
0677 /// Helper extractors: indexed sub volume of a volume
0678 struct IndexedSubVolumesExtractor {
0679   /// Extract the sub volumes from the volume
0680   ///
0681   /// @param gctx the geometry contextfor this extraction call
0682   /// @param nState is the current navigation state
0683   /// @param indices are access indices into the volume store
0684   ///
0685   /// @return a vector of raw DetectorVolume pointers
0686   inline static const std::vector<const DetectorVolume*> extract(
0687       [[maybe_unused]] const GeometryContext& gctx,
0688       const NavigationState& nState, const std::vector<std::size_t>& indices) {
0689     if (nState.currentVolume == nullptr) {
0690       throw std::runtime_error(
0691           "AllSubVolumesExtractor: no detector volume given.");
0692     }
0693     // Get the sub volumes container
0694     const auto& volumes = nState.currentVolume->volumes();
0695     // The extracted volumes
0696     std::vector<const DetectorVolume*> eVolumes;
0697     eVolumes.reserve(indices.size());
0698     std::ranges::for_each(
0699         indices, [&](const auto& i) { eVolumes.push_back(volumes[i]); });
0700     return eVolumes;
0701   }
0702 };
0703 
0704 }  // namespace Experimental
0705 }  // namespace Acts