Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:10:46

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