Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:11:25

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/VolumeBounds.hpp"
0016 #include "Acts/Material/IMaterialDecorator.hpp"
0017 #include "Acts/Material/IVolumeMaterial.hpp"
0018 #include "Acts/Material/ProtoVolumeMaterial.hpp"
0019 #include "Acts/Navigation/INavigationPolicy.hpp"
0020 #include "Acts/Navigation/NavigationStream.hpp"
0021 #include "Acts/Propagator/Navigator.hpp"
0022 #include "Acts/Surfaces/RegularSurface.hpp"
0023 #include "Acts/Surfaces/Surface.hpp"
0024 #include "Acts/Utilities/BinningType.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::glueTrackingVolume(const GeometryContext& gctx,
0177                                         BoundarySurfaceFace bsfMine,
0178                                         TrackingVolume* neighbor,
0179                                         BoundarySurfaceFace bsfNeighbor) {
0180   // Find the connection of the two tracking volumes: AxisDirection::AxisR
0181   // returns the center except for cylindrical volumes
0182   Vector3 bPosition(referencePosition(gctx, AxisDirection::AxisR));
0183   Vector3 distance = Vector3(
0184       neighbor->referencePosition(gctx, AxisDirection::AxisR) - bPosition);
0185   // glue to the face
0186   std::shared_ptr<const BoundarySurfaceT<TrackingVolume>> bSurfaceMine =
0187       boundarySurfaces().at(bsfMine);
0188   // @todo - complex glueing could be possible with actual intersection for the
0189   // normal vector
0190   // Coerce the arbitrary position bPosition to be on the surface repr so we can
0191   // get a normal
0192   Vector3 nvector =
0193       bSurfaceMine->surfaceRepresentation().normal(gctx, bPosition);
0194   // estimate the orientation
0195   Direction dir = Direction::fromScalar(nvector.dot(distance));
0196   // The easy case :
0197   // - no glue volume descriptors on either side
0198   if ((m_glueVolumeDescriptor == nullptr) ||
0199       m_glueVolumeDescriptor->glueVolumes(bsfMine) == nullptr) {
0200     // the boundary orientation
0201     auto mutableBSurfaceMine =
0202         std::const_pointer_cast<BoundarySurfaceT<TrackingVolume>>(bSurfaceMine);
0203     mutableBSurfaceMine->attachVolume(neighbor, dir);
0204     // Make sure you keep the boundary material if there
0205     const Surface& neighborSurface =
0206         neighbor->m_boundarySurfaces.at(bsfNeighbor)->surfaceRepresentation();
0207     auto neighborMaterial = neighborSurface.surfaceMaterialSharedPtr();
0208     const Surface& mySurface = bSurfaceMine->surfaceRepresentation();
0209     auto myMaterial = mySurface.surfaceMaterialSharedPtr();
0210     // Keep the neighbor material
0211     if (myMaterial == nullptr && neighborMaterial != nullptr) {
0212       Surface* myMutbableSurface = const_cast<Surface*>(&mySurface);
0213       myMutbableSurface->assignSurfaceMaterial(neighborMaterial);
0214     }
0215     // Now set it to the neighbor volume
0216     (neighbor->m_boundarySurfaces).at(bsfNeighbor) = bSurfaceMine;
0217   }
0218 }
0219 
0220 void TrackingVolume::glueTrackingVolumes(
0221     const GeometryContext& gctx, BoundarySurfaceFace bsfMine,
0222     const std::shared_ptr<TrackingVolumeArray>& neighbors,
0223     BoundarySurfaceFace bsfNeighbor) {
0224   // find the connection of the two tracking volumes : AxisDirection::AxisR
0225   // returns the center except for cylindrical volumes
0226   std::shared_ptr<const TrackingVolume> nRefVolume =
0227       neighbors->arrayObjects().at(0);
0228   // get the distance
0229   Vector3 bPosition(referencePosition(gctx, AxisDirection::AxisR));
0230   Vector3 distance(nRefVolume->referencePosition(gctx, AxisDirection::AxisR) -
0231                    bPosition);
0232   // take the normal at the binning positio
0233   std::shared_ptr<const BoundarySurfaceT<TrackingVolume>> bSurfaceMine =
0234       boundarySurfaces().at(bsfMine);
0235   // @todo - complex glueing could be possible with actual intersection for the
0236   // normal vector
0237   // Coerce the arbitrary position bPosition to be on the surface repr so we can
0238   // get a normal
0239   Vector3 nvector =
0240       bSurfaceMine->surfaceRepresentation().normal(gctx, bPosition);
0241   // estimate the orientation
0242   Direction dir = Direction::fromScalar(nvector.dot(distance));
0243   // the easy case :
0244   // - no glue volume descriptors on either side
0245   if ((m_glueVolumeDescriptor == nullptr) ||
0246       !m_glueVolumeDescriptor->glueVolumes(bsfMine)) {
0247     // the boundary orientation
0248     auto mutableBSurfaceMine =
0249         std::const_pointer_cast<BoundarySurfaceT<TrackingVolume>>(bSurfaceMine);
0250     mutableBSurfaceMine->attachVolumeArray(neighbors, dir);
0251     // now set it to the neighbor volumes - the optised way
0252     for (auto& nVolume : neighbors->arrayObjects()) {
0253       auto mutableNVolume = std::const_pointer_cast<TrackingVolume>(nVolume);
0254       (mutableNVolume->m_boundarySurfaces).at(bsfNeighbor) = bSurfaceMine;
0255     }
0256   }
0257 }
0258 
0259 void TrackingVolume::assignBoundaryMaterial(
0260     std::shared_ptr<const ISurfaceMaterial> surfaceMaterial,
0261     BoundarySurfaceFace bsFace) {
0262   auto bSurface = m_boundarySurfaces.at(bsFace);
0263   RegularSurface* surface =
0264       const_cast<RegularSurface*>(&bSurface->surfaceRepresentation());
0265   surface->assignSurfaceMaterial(std::move(surfaceMaterial));
0266 }
0267 
0268 void TrackingVolume::updateBoundarySurface(
0269     BoundarySurfaceFace bsf,
0270     std::shared_ptr<const BoundarySurfaceT<TrackingVolume>> bs,
0271     bool checkmaterial) {
0272   if (checkmaterial) {
0273     auto cMaterialPtr = m_boundarySurfaces.at(bsf)
0274                             ->surfaceRepresentation()
0275                             .surfaceMaterialSharedPtr();
0276     auto bsMaterial = bs->surfaceRepresentation().surfaceMaterial();
0277     if (cMaterialPtr != nullptr && bsMaterial == nullptr) {
0278       RegularSurface* surface =
0279           const_cast<RegularSurface*>(&bs->surfaceRepresentation());
0280       surface->assignSurfaceMaterial(cMaterialPtr);
0281     }
0282   }
0283   m_boundarySurfaces.at(bsf) = std::move(bs);
0284 }
0285 
0286 void TrackingVolume::registerGlueVolumeDescriptor(
0287     std::unique_ptr<GlueVolumesDescriptor> gvd) {
0288   m_glueVolumeDescriptor = std::move(gvd);
0289 }
0290 
0291 GlueVolumesDescriptor& TrackingVolume::glueVolumesDescriptor() {
0292   if (m_glueVolumeDescriptor == nullptr) {
0293     m_glueVolumeDescriptor = std::make_unique<GlueVolumesDescriptor>();
0294   }
0295   return *m_glueVolumeDescriptor;
0296 }
0297 
0298 void TrackingVolume::synchronizeLayers(double envelope) const {
0299   // case a : Layers exist
0300   // msgstream << MSG::VERBOSE << "  -> synchronizing Layer dimensions of
0301   // TrackingVolume '" << volumeName() << "'." << endreq;
0302 
0303   if (m_confinedLayers) {
0304     // msgstream << MSG::VERBOSE << "  ---> working on " <<
0305     // m_confinedLayers->arrayObjects().size() << " (material+navigation)
0306     // layers." << endreq;
0307     for (auto& clayIter : m_confinedLayers->arrayObjects()) {
0308       if (clayIter) {
0309         // @todo implement synchronize layer
0310         //  if (clayIter->surfaceRepresentation().type() == Surface::Cylinder &&
0311         //  !(center().isApprox(clayIter->surfaceRepresentation().center())) )
0312         //      clayIter->resizeAndRepositionLayer(volumeBounds(),center(),envelope);
0313         //  else
0314         //      clayIter->resizeLayer(volumeBounds(),envelope);
0315       }  // else
0316       // msgstream << MSG::WARNING << "  ---> found 0 pointer to layer,
0317       // indicates problem." << endreq;
0318     }
0319   }
0320 
0321   // case b : container volume -> step down
0322   if (m_confinedVolumes) {
0323     // msgstream << MSG::VERBOSE << "  ---> no confined layers, working on " <<
0324     // m_confinedVolumes->arrayObjects().size() << " confined volumes." <<
0325     // endreq;
0326     for (auto& cVolumesIter : m_confinedVolumes->arrayObjects()) {
0327       cVolumesIter->synchronizeLayers(envelope);
0328     }
0329   }
0330 }
0331 
0332 void TrackingVolume::interlinkLayers() {
0333   if (m_confinedLayers) {
0334     auto& layers = m_confinedLayers->arrayObjects();
0335 
0336     // forward register the last one as the previous one
0337     //  first <- | -> second, first <- | -> second, first <- | -> second
0338     const Layer* lastLayer = nullptr;
0339     for (auto& layerPtr : layers) {
0340       // we'll need to mutate our confined layers to perform this operation
0341       Layer& mutableLayer = *(std::const_pointer_cast<Layer>(layerPtr));
0342       // register the layers
0343       mutableLayer.m_nextLayerUtility = m_confinedLayers->binUtility();
0344       mutableLayer.m_nextLayers.first = lastLayer;
0345       // register the volume
0346       mutableLayer.encloseTrackingVolume(*this);
0347       // remember the last layer
0348       lastLayer = &mutableLayer;
0349     }
0350     // backward loop
0351     lastLayer = nullptr;
0352     for (auto layerIter = layers.rbegin(); layerIter != layers.rend();
0353          ++layerIter) {
0354       // set the other next volume
0355       Layer& mutableLayer = *(std::const_pointer_cast<Layer>(*layerIter));
0356       mutableLayer.m_nextLayers.second = lastLayer;
0357       lastLayer = &mutableLayer;
0358     }
0359   }
0360 }
0361 
0362 void TrackingVolume::closeGeometry(
0363     const IMaterialDecorator* materialDecorator,
0364     std::unordered_map<GeometryIdentifier, const TrackingVolume*>& volumeMap,
0365     std::size_t& vol, const GeometryIdentifierHook& hook,
0366     const Logger& logger) {
0367   if (m_confinedVolumes && !volumes().empty()) {
0368     ACTS_ERROR(
0369         "TrackingVolume::closeGeometry: Volume "
0370         << volumeName()
0371         << " has both confined volumes and volumes. This is not supported.");
0372     throw std::invalid_argument("Volume has both confined volumes and volumes");
0373   }
0374 
0375   if (m_confinedLayers && !surfaces().empty()) {
0376     ACTS_ERROR(
0377         "TrackingVolume::closeGeometry: Volume "
0378         << volumeName()
0379         << " has both confined layers and surfaces. This is not supported.");
0380     throw std::invalid_argument("Volume has both confined layers and surfaces");
0381   }
0382 
0383   // we can construct the volume ID from this
0384   auto volumeID = GeometryIdentifier().setVolume(++vol);
0385   // assign the Volume ID to the volume itself
0386   auto thisVolume = const_cast<TrackingVolume*>(this);
0387   thisVolume->assignGeometryId(volumeID);
0388   ACTS_DEBUG("volumeID: " << volumeID << ", name: " << volumeName());
0389   // insert the volume into the map
0390   volumeMap[volumeID] = thisVolume;
0391 
0392   // assign the material if you have a decorator
0393   if (materialDecorator != nullptr) {
0394     materialDecorator->decorate(*thisVolume);
0395   }
0396   if (thisVolume->volumeMaterial() == nullptr &&
0397       thisVolume->motherVolume() != nullptr &&
0398       thisVolume->motherVolume()->volumeMaterial() != nullptr) {
0399     auto protoMaterial = dynamic_cast<const ProtoVolumeMaterial*>(
0400         thisVolume->motherVolume()->volumeMaterial());
0401     if (protoMaterial == nullptr) {
0402       thisVolume->assignVolumeMaterial(
0403           thisVolume->motherVolume()->volumeMaterialPtr());
0404     }
0405   }
0406 
0407   this->assignGeometryId(volumeID);
0408   // loop over the boundary surfaces
0409   GeometryIdentifier::Value iboundary = 0;
0410   // loop over the boundary surfaces
0411   for (auto& bSurfIter : boundarySurfaces()) {
0412     // get the intersection solution
0413     auto& bSurface = bSurfIter->surfaceRepresentation();
0414     // create the boundary surface id
0415     iboundary++;
0416     auto boundaryID = GeometryIdentifier(volumeID).setBoundary(iboundary);
0417     // now assign to the boundary surface
0418     auto& mutableBSurface = *(const_cast<RegularSurface*>(&bSurface));
0419     mutableBSurface.assignGeometryId(boundaryID);
0420     // Assign material if you have a decorator
0421     if (materialDecorator != nullptr) {
0422       materialDecorator->decorate(mutableBSurface);
0423     }
0424   }
0425 
0426   // A) this is NOT a container volume, volumeID is already incremented
0427   if (!m_confinedVolumes) {
0428     // loop over the confined layers
0429     if (m_confinedLayers) {
0430       GeometryIdentifier::Value ilayer = 0;
0431       // loop over the layers
0432       for (auto& layerPtr : m_confinedLayers->arrayObjects()) {
0433         // create the layer identification
0434         ilayer++;
0435         auto layerID = GeometryIdentifier(volumeID).setLayer(ilayer);
0436         // now close the geometry
0437         auto mutableLayerPtr = std::const_pointer_cast<Layer>(layerPtr);
0438         mutableLayerPtr->closeGeometry(materialDecorator, layerID, hook,
0439                                        logger);
0440       }
0441     }
0442   } else {
0443     // B) this is a container volume, go through sub volume
0444     // do the loop
0445     for (auto& volumesIter : m_confinedVolumes->arrayObjects()) {
0446       auto mutableVolumesIter =
0447           std::const_pointer_cast<TrackingVolume>(volumesIter);
0448       mutableVolumesIter->setMotherVolume(this);
0449       mutableVolumesIter->closeGeometry(materialDecorator, volumeMap, vol, hook,
0450                                         logger);
0451     }
0452   }
0453 
0454   if (!m_confinedDenseVolumes.empty()) {
0455     for (auto& volumesIter : m_confinedDenseVolumes) {
0456       auto mutableVolumesIter =
0457           std::const_pointer_cast<TrackingVolume>(volumesIter);
0458       mutableVolumesIter->setMotherVolume(this);
0459       mutableVolumesIter->closeGeometry(materialDecorator, volumeMap, vol, hook,
0460                                         logger);
0461     }
0462   }
0463 
0464   GeometryIdentifier::Value iportal = 0;
0465   for (auto& portal : portals()) {
0466     iportal++;
0467     auto portalId = GeometryIdentifier(volumeID).setBoundary(iportal);
0468     assert(portal.isValid() && "Invalid portal encountered during closing");
0469 
0470     portal.surface().assignGeometryId(portalId);
0471   }
0472 
0473   GeometryIdentifier::Value isensitive = 0;
0474 
0475   for (auto& surface : surfaces()) {
0476     if (surface.associatedDetectorElement() == nullptr) {
0477       continue;
0478     }
0479     isensitive++;
0480     auto sensitiveId = GeometryIdentifier(volumeID).setSensitive(isensitive);
0481     surface.assignGeometryId(sensitiveId);
0482   }
0483 
0484   for (auto& volume : volumes()) {
0485     volume.closeGeometry(materialDecorator, volumeMap, vol, hook, logger);
0486   }
0487 }
0488 
0489 // Returns the boundary surfaces ordered in probability to hit them based on
0490 boost::container::small_vector<BoundaryIntersection, 4>
0491 TrackingVolume::compatibleBoundaries(const GeometryContext& gctx,
0492                                      const Vector3& position,
0493                                      const Vector3& direction,
0494                                      const NavigationOptions<Surface>& options,
0495                                      const Logger& logger) const {
0496   ACTS_VERBOSE("Finding compatibleBoundaries");
0497 
0498   boost::container::small_vector<BoundaryIntersection, 4> intersections;
0499 
0500   // The limits for this navigation step
0501   double nearLimit = options.nearLimit;
0502   double farLimit = options.farLimit;
0503 
0504   // Helper function to test intersection
0505   auto checkIntersection =
0506       [&](SurfaceMultiIntersection& candidates,
0507           const BoundarySurface* boundary) -> BoundaryIntersection {
0508     for (const auto& intersection : candidates.split()) {
0509       if (!intersection.isValid()) {
0510         continue;
0511       }
0512 
0513       ACTS_VERBOSE("Check intersection with surface "
0514                    << boundary->surfaceRepresentation().geometryId());
0515       if (detail::checkPathLength(intersection.pathLength(), nearLimit,
0516                                   farLimit, logger)) {
0517         return BoundaryIntersection(intersection, boundary);
0518       }
0519     }
0520 
0521     ACTS_VERBOSE("No intersection accepted");
0522     return BoundaryIntersection(SurfaceIntersection::invalid(), nullptr);
0523   };
0524 
0525   /// Helper function to process boundary surfaces
0526   auto processBoundaries =
0527       [&](const TrackingVolumeBoundaries& boundaries) -> void {
0528     // Loop over the boundary surfaces
0529     for (auto& boundary : boundaries) {
0530       // Get the boundary surface pointer
0531       const auto& surface = boundary->surfaceRepresentation();
0532       ACTS_VERBOSE("Consider boundary surface " << surface.geometryId());
0533 
0534       // Exclude the boundary where you are on
0535       // TODO this is not optimal as we might exit via the same boundary (e.g.
0536       // cylinder)
0537       if (&surface == options.startObject) {
0538         ACTS_VERBOSE(" - Surface is excluded surface");
0539         continue;
0540       }
0541 
0542       auto candidates = surface.intersect(gctx, position, direction,
0543                                           options.boundaryTolerance);
0544       // Intersect and continue
0545       auto intersection = checkIntersection(candidates, boundary.get());
0546       if (intersection.first.isValid()) {
0547         ACTS_VERBOSE(" - Proceed with surface");
0548         intersections.push_back(intersection);
0549       } else {
0550         ACTS_VERBOSE(" - Surface intersecion invalid");
0551       }
0552     }
0553   };
0554 
0555   // Process the boundaries of the current volume
0556   const auto& surfaces = boundarySurfaces();
0557   ACTS_VERBOSE("Volume reports " << surfaces.size() << " boundary surfaces");
0558   processBoundaries(surfaces);
0559 
0560   // Process potential boundaries of contained volumes
0561   auto confinedDenseVolumes = denseVolumes();
0562   ACTS_VERBOSE("Volume reports " << confinedDenseVolumes.size()
0563                                  << " confined dense volumes");
0564   for (const auto& volume : confinedDenseVolumes) {
0565     const auto& surfacesConfined = volume->boundarySurfaces();
0566     ACTS_VERBOSE(" -> " << surfacesConfined.size() << " boundary surfaces");
0567     processBoundaries(surfacesConfined);
0568   }
0569 
0570   return intersections;
0571 }
0572 
0573 boost::container::small_vector<LayerIntersection, 10>
0574 TrackingVolume::compatibleLayers(
0575     const GeometryContext& gctx, const Vector3& position,
0576     const Vector3& direction, const NavigationOptions<Layer>& options) const {
0577   // the layer intersections which are valid
0578   boost::container::small_vector<LayerIntersection, 10> lIntersections;
0579 
0580   // the confinedLayers
0581   if (m_confinedLayers == nullptr) {
0582     return {};
0583   }
0584 
0585   // start layer given or not - test layer
0586   const Layer* tLayer = options.startObject != nullptr
0587                             ? static_cast<const Layer*>(options.startObject)
0588                             : associatedLayer(gctx, position);
0589   while (tLayer != nullptr) {
0590     // check if the layer needs resolving
0591     // - resolveSensitive -> always take layer if it has a surface array
0592     // - resolveMaterial -> always take layer if it has material
0593     // - resolvePassive -> always take, unless it's a navigation layer
0594     // skip the start object
0595     if (tLayer != options.startObject && tLayer->resolve(options)) {
0596       // if it's a resolveable start layer, you are by definition on it
0597       // layer on approach intersection
0598       auto atIntersection =
0599           tLayer->surfaceOnApproach(gctx, position, direction, options);
0600       // Intersection is ok - take it (move to surface on approach)
0601       if (atIntersection.isValid()) {
0602         // create a layer intersection
0603         lIntersections.push_back(LayerIntersection(atIntersection, tLayer));
0604       }
0605     }
0606     // move to next one or break because you reached the end layer
0607     tLayer = (tLayer == options.endObject)
0608                  ? nullptr
0609                  : tLayer->nextLayer(gctx, position, direction);
0610   }
0611 
0612   // In case of cylindrical layers we might resolve far intersection solutions
0613   // which are not valid for navigation. These are discarded here by checking
0614   // against the minimum path length.
0615   auto min = std::min_element(
0616       lIntersections.begin(), lIntersections.end(),
0617       [](const LayerIntersection& a, const LayerIntersection& b) {
0618         return a.first.pathLength() < b.first.pathLength();
0619       });
0620   std::rotate(lIntersections.begin(), min, lIntersections.end());
0621   lIntersections.resize(std::distance(min, lIntersections.end()),
0622                         {SurfaceIntersection::invalid(), nullptr});
0623 
0624   return lIntersections;
0625 }
0626 
0627 const std::string& TrackingVolume::volumeName() const {
0628   return m_name;
0629 }
0630 
0631 void TrackingVolume::setVolumeName(const std::string& volumeName) {
0632   m_name = volumeName;
0633 }
0634 
0635 const IVolumeMaterial* TrackingVolume::volumeMaterial() const {
0636   return m_volumeMaterial.get();
0637 }
0638 
0639 const std::shared_ptr<const IVolumeMaterial>&
0640 TrackingVolume::volumeMaterialPtr() const {
0641   return m_volumeMaterial;
0642 }
0643 
0644 void TrackingVolume::assignVolumeMaterial(
0645     std::shared_ptr<const IVolumeMaterial> material) {
0646   m_volumeMaterial = std::move(material);
0647 }
0648 
0649 const LayerArray* TrackingVolume::confinedLayers() const {
0650   return m_confinedLayers.get();
0651 }
0652 
0653 const MutableTrackingVolumeVector TrackingVolume::denseVolumes() const {
0654   return m_confinedDenseVolumes;
0655 }
0656 
0657 std::shared_ptr<const TrackingVolumeArray> TrackingVolume::confinedVolumes()
0658     const {
0659   return m_confinedVolumes;
0660 }
0661 
0662 const TrackingVolume* TrackingVolume::motherVolume() const {
0663   return m_motherVolume;
0664 }
0665 
0666 TrackingVolume* TrackingVolume::motherVolume() {
0667   return m_motherVolume;
0668 }
0669 
0670 void TrackingVolume::setMotherVolume(TrackingVolume* mvol) {
0671   m_motherVolume = mvol;
0672 }
0673 
0674 const Acts::Layer* TrackingVolume::associatedLayer(
0675     const GeometryContext& /*gctx*/, const Vector3& position) const {
0676   // confined static layers - highest hierarchy
0677   if (m_confinedLayers != nullptr) {
0678     return (m_confinedLayers->object(position).get());
0679   }
0680 
0681   // return the null pointer
0682   return nullptr;
0683 }
0684 
0685 TrackingVolume::VolumeRange TrackingVolume::volumes() const {
0686   return VolumeRange{m_volumes};
0687 }
0688 
0689 TrackingVolume::MutableVolumeRange TrackingVolume::volumes() {
0690   return MutableVolumeRange{m_volumes};
0691 }
0692 
0693 TrackingVolume& TrackingVolume::addVolume(
0694     std::unique_ptr<TrackingVolume> volume) {
0695   if (volume->motherVolume() != nullptr) {
0696     throw std::invalid_argument("Volume already has a mother volume");
0697   }
0698 
0699   volume->setMotherVolume(this);
0700   m_volumes.push_back(std::move(volume));
0701   return *m_volumes.back();
0702 }
0703 
0704 TrackingVolume::PortalRange TrackingVolume::portals() const {
0705   return PortalRange{m_portals};
0706 }
0707 
0708 TrackingVolume::MutablePortalRange TrackingVolume::portals() {
0709   return MutablePortalRange{m_portals};
0710 }
0711 
0712 void TrackingVolume::addPortal(std::shared_ptr<Portal> portal) {
0713   if (portal == nullptr) {
0714     throw std::invalid_argument("Portal is nullptr");
0715   }
0716   m_portals.push_back(std::move(portal));
0717 }
0718 
0719 TrackingVolume::SurfaceRange TrackingVolume::surfaces() const {
0720   return SurfaceRange{m_surfaces};
0721 }
0722 
0723 TrackingVolume::MutableSurfaceRange TrackingVolume::surfaces() {
0724   return MutableSurfaceRange{m_surfaces};
0725 }
0726 
0727 void TrackingVolume::addSurface(std::shared_ptr<Surface> surface) {
0728   if (surface == nullptr) {
0729     throw std::invalid_argument("Surface is nullptr");
0730   }
0731   m_surfaces.push_back(std::move(surface));
0732 }
0733 
0734 void TrackingVolume::visualize(IVisualization3D& helper,
0735                                const GeometryContext& gctx,
0736                                const ViewConfig& viewConfig,
0737                                const ViewConfig& portalViewConfig,
0738                                const ViewConfig& sensitiveViewConfig) const {
0739   helper.object(volumeName());
0740   Volume::visualize(helper, gctx, viewConfig);
0741 
0742   if (!surfaces().empty()) {
0743     helper.object(volumeName() + "_sensitives");
0744   }
0745   for (const auto& surface : surfaces()) {
0746     surface.visualize(helper, gctx, sensitiveViewConfig);
0747   }
0748 
0749   for (const auto& portal : portals()) {
0750     portal.surface().visualize(helper, gctx, portalViewConfig);
0751   }
0752 
0753   for (const auto& child : volumes()) {
0754     child.visualize(helper, gctx, viewConfig, portalViewConfig,
0755                     sensitiveViewConfig);
0756   }
0757 }
0758 
0759 void TrackingVolume::setNavigationPolicy(
0760     std::unique_ptr<INavigationPolicy> policy) {
0761   if (policy == nullptr) {
0762     throw std::invalid_argument("Navigation policy is nullptr");
0763   }
0764 
0765   m_navigationPolicy = std::move(policy);
0766   m_navigationPolicy->connect(m_navigationDelegate);
0767 }
0768 
0769 void TrackingVolume::initializeNavigationCandidates(
0770     const NavigationArguments& args, AppendOnlyNavigationStream& stream,
0771     const Logger& logger) const {
0772   m_navigationDelegate(args, stream, logger);
0773 }
0774 
0775 }  // namespace Acts