Back to home page

EIC code displayed by LXR

 
 

    


Warning, file /include/Acts/Detector/DetectorVolume.hpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

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