Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-06-30 07:52:11

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 #include "Acts/Geometry/TrackingVolume.hpp"
0010 
0011 #include "Acts/Definitions/Direction.hpp"
0012 #include "Acts/Geometry/GeometryIdentifier.hpp"
0013 #include "Acts/Geometry/GlueVolumesDescriptor.hpp"
0014 #include "Acts/Geometry/Portal.hpp"
0015 #include "Acts/Geometry/TrackingGeometryVisitor.hpp"
0016 #include "Acts/Geometry/VolumeBounds.hpp"
0017 #include "Acts/Material/IMaterialDecorator.hpp"
0018 #include "Acts/Material/IVolumeMaterial.hpp"
0019 #include "Acts/Material/ProtoVolumeMaterial.hpp"
0020 #include "Acts/Navigation/INavigationPolicy.hpp"
0021 #include "Acts/Navigation/NavigationStream.hpp"
0022 #include "Acts/Propagator/Navigator.hpp"
0023 #include "Acts/Surfaces/RegularSurface.hpp"
0024 #include "Acts/Surfaces/Surface.hpp"
0025 #include "Acts/Utilities/Intersection.hpp"
0026 
0027 #include <algorithm>
0028 #include <memory>
0029 #include <ostream>
0030 #include <string>
0031 #include <utility>
0032 
0033 #include <boost/container/small_vector.hpp>
0034 
0035 namespace Acts {
0036 
0037 // constructor for arguments
0038 TrackingVolume::TrackingVolume(
0039     const Transform3& transform, std::shared_ptr<VolumeBounds> volumeBounds,
0040     std::shared_ptr<const IVolumeMaterial> volumeMaterial,
0041     std::unique_ptr<const LayerArray> staticLayerArray,
0042     std::shared_ptr<const TrackingVolumeArray> containedVolumeArray,
0043     MutableTrackingVolumeVector denseVolumeVector,
0044     const std::string& volumeName)
0045     : Volume(transform, std::move(volumeBounds)),
0046       m_confinedLayers(std::move(staticLayerArray)),
0047       m_confinedVolumes(std::move(containedVolumeArray)),
0048       m_confinedDenseVolumes({}),
0049       m_volumeMaterial(std::move(volumeMaterial)),
0050       m_name(volumeName) {
0051   createBoundarySurfaces();
0052   interlinkLayers();
0053   connectDenseBoundarySurfaces(denseVolumeVector);
0054 
0055   DelegateChainBuilder{m_navigationDelegate}
0056       .add<&INavigationPolicy::noopInitializeCandidates>()
0057       .store(m_navigationDelegate);
0058 }
0059 
0060 TrackingVolume::TrackingVolume(Volume& volume, const std::string& volumeName)
0061     : TrackingVolume(volume.transform(), volume.volumeBoundsPtr(), nullptr,
0062                      nullptr, nullptr, MutableTrackingVolumeVector{},
0063                      volumeName) {}
0064 
0065 TrackingVolume::TrackingVolume(const Transform3& transform,
0066                                std::shared_ptr<VolumeBounds> volbounds,
0067                                const std::string& volumeName)
0068     : TrackingVolume(transform, std::move(volbounds), nullptr, nullptr, nullptr,
0069                      {}, volumeName) {}
0070 
0071 TrackingVolume::~TrackingVolume() = default;
0072 TrackingVolume::TrackingVolume(TrackingVolume&&) noexcept = default;
0073 TrackingVolume& TrackingVolume::operator=(TrackingVolume&&) noexcept = default;
0074 
0075 const TrackingVolume* TrackingVolume::lowestTrackingVolume(
0076     const GeometryContext& gctx, const Vector3& position,
0077     const double tol) const {
0078   if (!inside(position, tol)) {
0079     return nullptr;
0080   }
0081 
0082   // confined static volumes - highest hierarchy
0083   if (m_confinedVolumes) {
0084     const TrackingVolume* volume = m_confinedVolumes->object(position).get();
0085     if (volume != nullptr) {
0086       return volume->lowestTrackingVolume(gctx, position, tol);
0087     }
0088   }
0089 
0090   // search for dense volumes
0091   if (!m_confinedDenseVolumes.empty()) {
0092     for (auto& denseVolume : m_confinedDenseVolumes) {
0093       if (denseVolume->inside(position, tol)) {
0094         return denseVolume.get();
0095       }
0096     }
0097   }
0098 
0099   // @TODO: Abstract this into an accelerateable structure
0100   for (const auto& volume : volumes()) {
0101     if (volume.inside(position, tol)) {
0102       return volume.lowestTrackingVolume(gctx, position, tol);
0103     }
0104   }
0105 
0106   // there is no lower sub structure
0107   return this;
0108 }
0109 
0110 const TrackingVolumeBoundaries& TrackingVolume::boundarySurfaces() const {
0111   return m_boundarySurfaces;
0112 }
0113 
0114 void TrackingVolume::connectDenseBoundarySurfaces(
0115     MutableTrackingVolumeVector& confinedDenseVolumes) {
0116   if (!confinedDenseVolumes.empty()) {
0117     Direction dir = Direction::Positive();
0118     // Walk over each dense volume
0119     for (auto& confDenseVol : confinedDenseVolumes) {
0120       // Walk over each boundary surface of the volume
0121       auto& boundSur = confDenseVol->boundarySurfaces();
0122       for (std::size_t i = 0; i < boundSur.size(); i++) {
0123         // Skip empty entries since we do not know the shape of the dense volume
0124         // and therewith the used indices
0125         if (boundSur.at(i) == nullptr) {
0126           continue;
0127         }
0128 
0129         // Use mother volume as the opposite direction of the already used
0130         // direction
0131         auto mutableBs =
0132             std::const_pointer_cast<BoundarySurfaceT<TrackingVolume>>(
0133                 boundSur.at(i));
0134         if (mutableBs->m_oppositeVolume != nullptr &&
0135             mutableBs->m_alongVolume == nullptr) {
0136           dir = Direction::Positive();
0137           mutableBs->attachVolume(this, dir);
0138         } else {
0139           if (mutableBs->m_oppositeVolume == nullptr &&
0140               mutableBs->m_alongVolume != nullptr) {
0141             dir = Direction::Negative();
0142             mutableBs->attachVolume(this, dir);
0143           }
0144         }
0145 
0146         // Update the boundary
0147         confDenseVol->updateBoundarySurface(static_cast<BoundarySurfaceFace>(i),
0148                                             mutableBs);
0149       }
0150       // Store the volume
0151       m_confinedDenseVolumes.push_back(std::move(confDenseVol));
0152     }
0153   }
0154 }
0155 
0156 void TrackingVolume::createBoundarySurfaces() {
0157   using Boundary = BoundarySurfaceT<TrackingVolume>;
0158 
0159   // Transform Surfaces To BoundarySurfaces
0160   auto orientedSurfaces = Volume::volumeBounds().orientedSurfaces(m_transform);
0161 
0162   m_boundarySurfaces.reserve(orientedSurfaces.size());
0163   for (auto& osf : orientedSurfaces) {
0164     TrackingVolume* opposite = nullptr;
0165     TrackingVolume* along = nullptr;
0166     if (osf.direction == Direction::OppositeNormal()) {
0167       opposite = this;
0168     } else {
0169       along = this;
0170     }
0171     m_boundarySurfaces.push_back(std::make_shared<const Boundary>(
0172         std::move(osf.surface), opposite, along));
0173   }
0174 }
0175 
0176 void TrackingVolume::clearBoundarySurfaces() {
0177   m_boundarySurfaces.clear();
0178 }
0179 
0180 void TrackingVolume::glueTrackingVolume(const GeometryContext& gctx,
0181                                         BoundarySurfaceFace bsfMine,
0182                                         TrackingVolume* neighbor,
0183                                         BoundarySurfaceFace bsfNeighbor) {
0184   // Find the connection of the two tracking volumes: AxisDirection::AxisR
0185   // returns the center except for cylindrical volumes
0186   Vector3 bPosition(referencePosition(gctx, AxisDirection::AxisR));
0187   Vector3 distance = Vector3(
0188       neighbor->referencePosition(gctx, AxisDirection::AxisR) - bPosition);
0189   // glue to the face
0190   std::shared_ptr<const BoundarySurfaceT<TrackingVolume>> bSurfaceMine =
0191       boundarySurfaces().at(bsfMine);
0192   // @todo - complex glueing could be possible with actual intersection for the
0193   // normal vector
0194   // Coerce the arbitrary position bPosition to be on the surface repr so we can
0195   // get a normal
0196   Vector3 nvector =
0197       bSurfaceMine->surfaceRepresentation().normal(gctx, bPosition);
0198   // estimate the orientation
0199   Direction dir = Direction::fromScalar(nvector.dot(distance));
0200   // The easy case :
0201   // - no glue volume descriptors on either side
0202   if ((m_glueVolumeDescriptor == nullptr) ||
0203       m_glueVolumeDescriptor->glueVolumes(bsfMine) == nullptr) {
0204     // the boundary orientation
0205     auto mutableBSurfaceMine =
0206         std::const_pointer_cast<BoundarySurfaceT<TrackingVolume>>(bSurfaceMine);
0207     mutableBSurfaceMine->attachVolume(neighbor, dir);
0208     // Make sure you keep the boundary material if there
0209     const Surface& neighborSurface =
0210         neighbor->m_boundarySurfaces.at(bsfNeighbor)->surfaceRepresentation();
0211     auto neighborMaterial = neighborSurface.surfaceMaterialSharedPtr();
0212     const Surface& mySurface = bSurfaceMine->surfaceRepresentation();
0213     auto myMaterial = mySurface.surfaceMaterialSharedPtr();
0214     // Keep the neighbor material
0215     if (myMaterial == nullptr && neighborMaterial != nullptr) {
0216       Surface* myMutbableSurface = const_cast<Surface*>(&mySurface);
0217       myMutbableSurface->assignSurfaceMaterial(neighborMaterial);
0218     }
0219     // Now set it to the neighbor volume
0220     (neighbor->m_boundarySurfaces).at(bsfNeighbor) = bSurfaceMine;
0221   }
0222 }
0223 
0224 void TrackingVolume::glueTrackingVolumes(
0225     const GeometryContext& gctx, BoundarySurfaceFace bsfMine,
0226     const std::shared_ptr<TrackingVolumeArray>& neighbors,
0227     BoundarySurfaceFace bsfNeighbor) {
0228   // find the connection of the two tracking volumes : AxisDirection::AxisR
0229   // returns the center except for cylindrical volumes
0230   std::shared_ptr<const TrackingVolume> nRefVolume =
0231       neighbors->arrayObjects().at(0);
0232   // get the distance
0233   Vector3 bPosition(referencePosition(gctx, AxisDirection::AxisR));
0234   Vector3 distance(nRefVolume->referencePosition(gctx, AxisDirection::AxisR) -
0235                    bPosition);
0236   // take the normal at the binning positio
0237   std::shared_ptr<const BoundarySurfaceT<TrackingVolume>> bSurfaceMine =
0238       boundarySurfaces().at(bsfMine);
0239   // @todo - complex glueing could be possible with actual intersection for the
0240   // normal vector
0241   // Coerce the arbitrary position bPosition to be on the surface repr so we can
0242   // get a normal
0243   Vector3 nvector =
0244       bSurfaceMine->surfaceRepresentation().normal(gctx, bPosition);
0245   // estimate the orientation
0246   Direction dir = Direction::fromScalar(nvector.dot(distance));
0247   // the easy case :
0248   // - no glue volume descriptors on either side
0249   if ((m_glueVolumeDescriptor == nullptr) ||
0250       !m_glueVolumeDescriptor->glueVolumes(bsfMine)) {
0251     // the boundary orientation
0252     auto mutableBSurfaceMine =
0253         std::const_pointer_cast<BoundarySurfaceT<TrackingVolume>>(bSurfaceMine);
0254     mutableBSurfaceMine->attachVolumeArray(neighbors, dir);
0255     // now set it to the neighbor volumes - the optised way
0256     for (auto& nVolume : neighbors->arrayObjects()) {
0257       auto mutableNVolume = std::const_pointer_cast<TrackingVolume>(nVolume);
0258       (mutableNVolume->m_boundarySurfaces).at(bsfNeighbor) = bSurfaceMine;
0259     }
0260   }
0261 }
0262 
0263 void TrackingVolume::assignBoundaryMaterial(
0264     std::shared_ptr<const ISurfaceMaterial> surfaceMaterial,
0265     BoundarySurfaceFace bsFace) {
0266   auto bSurface = m_boundarySurfaces.at(bsFace);
0267   RegularSurface* surface =
0268       const_cast<RegularSurface*>(&bSurface->surfaceRepresentation());
0269   surface->assignSurfaceMaterial(std::move(surfaceMaterial));
0270 }
0271 
0272 void TrackingVolume::updateBoundarySurface(
0273     BoundarySurfaceFace bsf,
0274     std::shared_ptr<const BoundarySurfaceT<TrackingVolume>> bs,
0275     bool checkmaterial) {
0276   if (checkmaterial) {
0277     auto cMaterialPtr = m_boundarySurfaces.at(bsf)
0278                             ->surfaceRepresentation()
0279                             .surfaceMaterialSharedPtr();
0280     auto bsMaterial = bs->surfaceRepresentation().surfaceMaterial();
0281     if (cMaterialPtr != nullptr && bsMaterial == nullptr) {
0282       RegularSurface* surface =
0283           const_cast<RegularSurface*>(&bs->surfaceRepresentation());
0284       surface->assignSurfaceMaterial(cMaterialPtr);
0285     }
0286   }
0287   m_boundarySurfaces.at(bsf) = std::move(bs);
0288 }
0289 
0290 void TrackingVolume::registerGlueVolumeDescriptor(
0291     std::unique_ptr<GlueVolumesDescriptor> gvd) {
0292   m_glueVolumeDescriptor = std::move(gvd);
0293 }
0294 
0295 GlueVolumesDescriptor& TrackingVolume::glueVolumesDescriptor() {
0296   if (m_glueVolumeDescriptor == nullptr) {
0297     m_glueVolumeDescriptor = std::make_unique<GlueVolumesDescriptor>();
0298   }
0299   return *m_glueVolumeDescriptor;
0300 }
0301 
0302 void TrackingVolume::synchronizeLayers(double envelope) const {
0303   // case a : Layers exist
0304   // msgstream << MSG::VERBOSE << "  -> synchronizing Layer dimensions of
0305   // TrackingVolume '" << volumeName() << "'." << endreq;
0306 
0307   if (m_confinedLayers) {
0308     // msgstream << MSG::VERBOSE << "  ---> working on " <<
0309     // m_confinedLayers->arrayObjects().size() << " (material+navigation)
0310     // layers." << endreq;
0311     for (auto& clayIter : m_confinedLayers->arrayObjects()) {
0312       if (clayIter) {
0313         // @todo implement synchronize layer
0314         //  if (clayIter->surfaceRepresentation().type() == Surface::Cylinder &&
0315         //  !(center().isApprox(clayIter->surfaceRepresentation().center())) )
0316         //      clayIter->resizeAndRepositionLayer(volumeBounds(),center(),envelope);
0317         //  else
0318         //      clayIter->resizeLayer(volumeBounds(),envelope);
0319       }  // else
0320       // msgstream << MSG::WARNING << "  ---> found 0 pointer to layer,
0321       // indicates problem." << endreq;
0322     }
0323   }
0324 
0325   // case b : container volume -> step down
0326   if (m_confinedVolumes) {
0327     // msgstream << MSG::VERBOSE << "  ---> no confined layers, working on " <<
0328     // m_confinedVolumes->arrayObjects().size() << " confined volumes." <<
0329     // endreq;
0330     for (auto& cVolumesIter : m_confinedVolumes->arrayObjects()) {
0331       cVolumesIter->synchronizeLayers(envelope);
0332     }
0333   }
0334 }
0335 
0336 void TrackingVolume::interlinkLayers() {
0337   if (m_confinedLayers) {
0338     auto& layers = m_confinedLayers->arrayObjects();
0339 
0340     // forward register the last one as the previous one
0341     //  first <- | -> second, first <- | -> second, first <- | -> second
0342     const Layer* lastLayer = nullptr;
0343     for (auto& layerPtr : layers) {
0344       // we'll need to mutate our confined layers to perform this operation
0345       Layer& mutableLayer = *(std::const_pointer_cast<Layer>(layerPtr));
0346       // register the layers
0347       mutableLayer.m_nextLayerUtility = m_confinedLayers->binUtility();
0348       mutableLayer.m_nextLayers.first = lastLayer;
0349       // register the volume
0350       mutableLayer.encloseTrackingVolume(*this);
0351       // remember the last layer
0352       lastLayer = &mutableLayer;
0353     }
0354     // backward loop
0355     lastLayer = nullptr;
0356     for (auto layerIter = layers.rbegin(); layerIter != layers.rend();
0357          ++layerIter) {
0358       // set the other next volume
0359       Layer& mutableLayer = *(std::const_pointer_cast<Layer>(*layerIter));
0360       mutableLayer.m_nextLayers.second = lastLayer;
0361       lastLayer = &mutableLayer;
0362     }
0363   }
0364 }
0365 
0366 // Returns the boundary surfaces ordered in probability to hit them based on
0367 boost::container::small_vector<BoundaryIntersection, 4>
0368 TrackingVolume::compatibleBoundaries(const GeometryContext& gctx,
0369                                      const Vector3& position,
0370                                      const Vector3& direction,
0371                                      const NavigationOptions<Surface>& options,
0372                                      const Logger& logger) const {
0373   ACTS_VERBOSE("Finding compatibleBoundaries");
0374 
0375   boost::container::small_vector<BoundaryIntersection, 4> intersections;
0376 
0377   // The limits for this navigation step
0378   double nearLimit = options.nearLimit;
0379   double farLimit = options.farLimit;
0380 
0381   // Helper function to test intersection
0382   auto checkIntersection =
0383       [&](SurfaceMultiIntersection& candidates,
0384           const BoundarySurface* boundary) -> BoundaryIntersection {
0385     for (const auto& intersection : candidates.split()) {
0386       if (!intersection.isValid()) {
0387         continue;
0388       }
0389 
0390       ACTS_VERBOSE("Check intersection with surface "
0391                    << boundary->surfaceRepresentation().geometryId());
0392       if (detail::checkPathLength(intersection.pathLength(), nearLimit,
0393                                   farLimit, logger)) {
0394         return BoundaryIntersection(intersection, boundary);
0395       }
0396     }
0397 
0398     ACTS_VERBOSE("No intersection accepted");
0399     return BoundaryIntersection(SurfaceIntersection::invalid(), nullptr);
0400   };
0401 
0402   /// Helper function to process boundary surfaces
0403   auto processBoundaries =
0404       [&](const TrackingVolumeBoundaries& boundaries) -> void {
0405     // Loop over the boundary surfaces
0406     for (auto& boundary : boundaries) {
0407       // Get the boundary surface pointer
0408       const auto& surface = boundary->surfaceRepresentation();
0409       ACTS_VERBOSE("Consider boundary surface " << surface.geometryId());
0410 
0411       // Exclude the boundary where you are on
0412       // TODO this is not optimal as we might exit via the same boundary (e.g.
0413       // cylinder)
0414       if (&surface == options.startObject) {
0415         ACTS_VERBOSE(" - Surface is excluded surface");
0416         continue;
0417       }
0418 
0419       auto candidates = surface.intersect(gctx, position, direction,
0420                                           options.boundaryTolerance);
0421       // Intersect and continue
0422       auto intersection = checkIntersection(candidates, boundary.get());
0423       if (intersection.first.isValid()) {
0424         ACTS_VERBOSE(" - Proceed with surface");
0425         intersections.push_back(intersection);
0426       } else {
0427         ACTS_VERBOSE(" - Surface intersecion invalid");
0428       }
0429     }
0430   };
0431 
0432   // Process the boundaries of the current volume
0433   const auto& surfaces = boundarySurfaces();
0434   ACTS_VERBOSE("Volume reports " << surfaces.size() << " boundary surfaces");
0435   processBoundaries(surfaces);
0436 
0437   // Process potential boundaries of contained volumes
0438   auto confinedDenseVolumes = denseVolumes();
0439   ACTS_VERBOSE("Volume reports " << confinedDenseVolumes.size()
0440                                  << " confined dense volumes");
0441   for (const auto& volume : confinedDenseVolumes) {
0442     const auto& surfacesConfined = volume->boundarySurfaces();
0443     ACTS_VERBOSE(" -> " << surfacesConfined.size() << " boundary surfaces");
0444     processBoundaries(surfacesConfined);
0445   }
0446 
0447   return intersections;
0448 }
0449 
0450 boost::container::small_vector<LayerIntersection, 10>
0451 TrackingVolume::compatibleLayers(
0452     const GeometryContext& gctx, const Vector3& position,
0453     const Vector3& direction, const NavigationOptions<Layer>& options) const {
0454   // the layer intersections which are valid
0455   boost::container::small_vector<LayerIntersection, 10> lIntersections;
0456 
0457   // the confinedLayers
0458   if (m_confinedLayers == nullptr) {
0459     return {};
0460   }
0461 
0462   // start layer given or not - test layer
0463   const Layer* tLayer = options.startObject != nullptr
0464                             ? static_cast<const Layer*>(options.startObject)
0465                             : associatedLayer(gctx, position);
0466   while (tLayer != nullptr) {
0467     // check if the layer needs resolving
0468     // - resolveSensitive -> always take layer if it has a surface array
0469     // - resolveMaterial -> always take layer if it has material
0470     // - resolvePassive -> always take, unless it's a navigation layer
0471     // skip the start object
0472     if (tLayer != options.startObject && tLayer->resolve(options)) {
0473       // if it's a resolveable start layer, you are by definition on it
0474       // layer on approach intersection
0475       auto atIntersection =
0476           tLayer->surfaceOnApproach(gctx, position, direction, options);
0477       // Intersection is ok - take it (move to surface on approach)
0478       if (atIntersection.isValid()) {
0479         // create a layer intersection
0480         lIntersections.push_back(LayerIntersection(atIntersection, tLayer));
0481       }
0482     }
0483     // move to next one or break because you reached the end layer
0484     tLayer = (tLayer == options.endObject)
0485                  ? nullptr
0486                  : tLayer->nextLayer(gctx, position, direction);
0487   }
0488 
0489   // In case of cylindrical layers we might resolve far intersection solutions
0490   // which are not valid for navigation. These are discarded here by checking
0491   // against the minimum path length.
0492   auto min = std::min_element(
0493       lIntersections.begin(), lIntersections.end(),
0494       [](const LayerIntersection& a, const LayerIntersection& b) {
0495         return a.first.pathLength() < b.first.pathLength();
0496       });
0497   std::rotate(lIntersections.begin(), min, lIntersections.end());
0498   lIntersections.resize(std::distance(min, lIntersections.end()),
0499                         {SurfaceIntersection::invalid(), nullptr});
0500 
0501   return lIntersections;
0502 }
0503 
0504 const std::string& TrackingVolume::volumeName() const {
0505   return m_name;
0506 }
0507 
0508 void TrackingVolume::setVolumeName(const std::string& volumeName) {
0509   m_name = volumeName;
0510 }
0511 
0512 const IVolumeMaterial* TrackingVolume::volumeMaterial() const {
0513   return m_volumeMaterial.get();
0514 }
0515 
0516 const std::shared_ptr<const IVolumeMaterial>&
0517 TrackingVolume::volumeMaterialPtr() const {
0518   return m_volumeMaterial;
0519 }
0520 
0521 void TrackingVolume::assignVolumeMaterial(
0522     std::shared_ptr<const IVolumeMaterial> material) {
0523   m_volumeMaterial = std::move(material);
0524 }
0525 
0526 const LayerArray* TrackingVolume::confinedLayers() const {
0527   return m_confinedLayers.get();
0528 }
0529 
0530 const MutableTrackingVolumeVector TrackingVolume::denseVolumes() const {
0531   return m_confinedDenseVolumes;
0532 }
0533 
0534 std::shared_ptr<const TrackingVolumeArray> TrackingVolume::confinedVolumes()
0535     const {
0536   return m_confinedVolumes;
0537 }
0538 
0539 const TrackingVolume* TrackingVolume::motherVolume() const {
0540   return m_motherVolume;
0541 }
0542 
0543 TrackingVolume* TrackingVolume::motherVolume() {
0544   return m_motherVolume;
0545 }
0546 
0547 void TrackingVolume::setMotherVolume(TrackingVolume* mvol) {
0548   m_motherVolume = mvol;
0549 }
0550 
0551 const Acts::Layer* TrackingVolume::associatedLayer(
0552     const GeometryContext& /*gctx*/, const Vector3& position) const {
0553   // confined static layers - highest hierarchy
0554   if (m_confinedLayers != nullptr) {
0555     return (m_confinedLayers->object(position).get());
0556   }
0557 
0558   // return the null pointer
0559   return nullptr;
0560 }
0561 
0562 TrackingVolume::VolumeRange TrackingVolume::volumes() const {
0563   return VolumeRange{m_volumes};
0564 }
0565 
0566 TrackingVolume::MutableVolumeRange TrackingVolume::volumes() {
0567   return MutableVolumeRange{m_volumes};
0568 }
0569 
0570 TrackingVolume& TrackingVolume::addVolume(
0571     std::unique_ptr<TrackingVolume> volume) {
0572   if (volume->motherVolume() != nullptr) {
0573     throw std::invalid_argument("Volume already has a mother volume");
0574   }
0575 
0576   volume->setMotherVolume(this);
0577   m_volumes.push_back(std::move(volume));
0578   return *m_volumes.back();
0579 }
0580 
0581 TrackingVolume::PortalRange TrackingVolume::portals() const {
0582   return PortalRange{m_portals};
0583 }
0584 
0585 TrackingVolume::MutablePortalRange TrackingVolume::portals() {
0586   return MutablePortalRange{m_portals};
0587 }
0588 
0589 void TrackingVolume::addPortal(std::shared_ptr<Portal> portal) {
0590   if (portal == nullptr) {
0591     throw std::invalid_argument("Portal is nullptr");
0592   }
0593   m_portals.push_back(std::move(portal));
0594 }
0595 
0596 TrackingVolume::SurfaceRange TrackingVolume::surfaces() const {
0597   return SurfaceRange{m_surfaces};
0598 }
0599 
0600 TrackingVolume::MutableSurfaceRange TrackingVolume::surfaces() {
0601   return MutableSurfaceRange{m_surfaces};
0602 }
0603 
0604 void TrackingVolume::addSurface(std::shared_ptr<Surface> surface) {
0605   if (surface == nullptr) {
0606     throw std::invalid_argument("Surface is nullptr");
0607   }
0608   m_surfaces.push_back(std::move(surface));
0609 }
0610 
0611 void TrackingVolume::visualize(IVisualization3D& helper,
0612                                const GeometryContext& gctx,
0613                                const ViewConfig& viewConfig,
0614                                const ViewConfig& portalViewConfig,
0615                                const ViewConfig& sensitiveViewConfig) const {
0616   helper.object(volumeName());
0617   if (viewConfig.visible) {
0618     Volume::visualize(helper, gctx, viewConfig);
0619   }
0620 
0621   if (sensitiveViewConfig.visible) {
0622     if (!surfaces().empty()) {
0623       helper.object(volumeName() + "_sensitives");
0624       for (const auto& surface : surfaces()) {
0625         surface.visualize(helper, gctx, sensitiveViewConfig);
0626       }
0627     }
0628   }
0629 
0630   if (portalViewConfig.visible) {
0631     helper.object(volumeName() + "_portals");
0632     for (const auto& portal : portals()) {
0633       portal.surface().visualize(helper, gctx, portalViewConfig);
0634     }
0635   }
0636 
0637   for (const auto& child : volumes()) {
0638     child.visualize(helper, gctx, viewConfig, portalViewConfig,
0639                     sensitiveViewConfig);
0640   }
0641 }
0642 
0643 void TrackingVolume::setNavigationPolicy(
0644     std::unique_ptr<INavigationPolicy> policy) {
0645   if (policy == nullptr) {
0646     throw std::invalid_argument("Navigation policy is nullptr");
0647   }
0648 
0649   m_navigationPolicy = std::move(policy);
0650   m_navigationPolicy->connect(m_navigationDelegate);
0651 }
0652 
0653 void TrackingVolume::initializeNavigationCandidates(
0654     const NavigationArguments& args, AppendOnlyNavigationStream& stream,
0655     const Logger& logger) const {
0656   m_navigationDelegate(args, stream, logger);
0657 }
0658 
0659 namespace {
0660 
0661 void visitLayer(const Acts::Layer& layer, TrackingGeometryVisitor& visitor) {
0662   visitor.visitLayer(layer);
0663   // Surfaces contained in the surface array
0664   if (layer.surfaceArray() != nullptr) {
0665     for (const auto& srf : layer.surfaceArray()->surfaces()) {
0666       visitor.visitSurface(*srf);
0667     }
0668   }
0669   visitor.visitSurface(layer.surfaceRepresentation());
0670   if (layer.approachDescriptor() != nullptr) {
0671     for (const auto& srf : layer.approachDescriptor()->containedSurfaces()) {
0672       visitor.visitSurface(*srf);
0673     }
0674   }
0675 }
0676 
0677 }  // namespace
0678 
0679 // @TODO: Unify once Gen1 is removed: should share most code between mutable and const
0680 void TrackingVolume::apply(TrackingGeometryVisitor& visitor) const {
0681   visitor.visitVolume(*this);
0682 
0683   // Visit the boundary surfaces
0684   for (const auto& bs : m_boundarySurfaces) {
0685     visitor.visitBoundarySurface(*bs);
0686     visitor.visitSurface(bs->surfaceRepresentation());
0687   }
0688 
0689   for (const auto& portal : portals()) {
0690     visitor.visitPortal(portal);
0691     visitor.visitSurface(portal.surface());
0692   }
0693 
0694   // Internal structure
0695   if (m_confinedLayers != nullptr) {
0696     std::ranges::for_each(
0697         m_confinedLayers->arrayObjects(),
0698         [&](const auto& layer) { visitLayer(*layer, visitor); });
0699   }
0700 
0701   if (m_confinedVolumes != nullptr) {
0702     // contains sub volumes
0703     for (const auto& volume : m_confinedVolumes->arrayObjects()) {
0704       volume->apply(visitor);
0705     }
0706   }
0707 
0708   for (const auto& surface : surfaces()) {
0709     visitor.visitSurface(surface);
0710   }
0711 
0712   for (const auto& volume : volumes()) {
0713     volume.apply(visitor);
0714   }
0715 }
0716 
0717 void Acts::TrackingVolume::apply(TrackingGeometryMutableVisitor& visitor) {
0718   // if the visitor is configured for inner--->outer volume visiting we visit
0719   // the children first
0720   if (visitor.visitDepthFirst()) {
0721     for (auto& volume : volumes()) {
0722       volume.apply(visitor);
0723     }
0724   }
0725 
0726   visitor.visitVolume(*this);
0727 
0728   // Visit the boundary surfaces
0729   // This does const casts because Gen1 substructure does not have transitive
0730   // const-ness
0731 
0732   // @TODO: Remove this when Gen1 is remoeved
0733 
0734   for (const auto& bs : m_boundarySurfaces) {
0735     visitor.visitBoundarySurface(
0736         const_cast<BoundarySurfaceT<TrackingVolume>&>(*bs));
0737     visitor.visitSurface(
0738         const_cast<RegularSurface&>(bs->surfaceRepresentation()));
0739   }
0740 
0741   for (auto& portal : portals()) {
0742     visitor.visitPortal(portal);
0743     visitor.visitSurface(portal.surface());
0744   }
0745 
0746   // Internal structure
0747   // This does const casts because Gen1 substructure does not have transitive
0748   // const-ness
0749   // @TODO: Remove this when Gen1 is remoeved
0750   if (m_confinedVolumes == nullptr) {
0751     // no sub volumes => loop over the confined layers
0752     if (m_confinedLayers != nullptr) {
0753       for (const auto& layer : m_confinedLayers->arrayObjects()) {
0754         visitor.visitLayer(const_cast<Layer&>(*layer));
0755         // Surfaces contained in the surface array
0756         if (layer->surfaceArray() != nullptr) {
0757           for (const auto& srf : layer->surfaceArray()->surfaces()) {
0758             visitor.visitSurface(const_cast<Surface&>(*srf));
0759           }
0760         }
0761         // Surfaces of the layer
0762         visitor.visitSurface(
0763             const_cast<Surface&>(layer->surfaceRepresentation()));
0764         // Approach surfaces of the layer
0765         if (layer->approachDescriptor() != nullptr) {
0766           for (const auto& srf :
0767                layer->approachDescriptor()->containedSurfaces()) {
0768             visitor.visitSurface(const_cast<Surface&>(*srf));
0769           }
0770         }
0771       }
0772     }
0773   } else {
0774     // contains sub volumes
0775     for (const auto& volume : m_confinedVolumes->arrayObjects()) {
0776       const_cast<TrackingVolume&>(*volume).apply(visitor);
0777     }
0778   }
0779 
0780   for (auto& surface : surfaces()) {
0781     visitor.visitSurface(surface);
0782   }
0783 
0784   if (!visitor.visitDepthFirst()) {
0785     // if the visitor is configured for outer--->inner volume visiting we visit
0786     // the children last
0787     for (auto& volume : volumes()) {
0788       volume.apply(visitor);
0789     }
0790   }
0791 }
0792 
0793 }  // namespace Acts