Back to home page

sPhenix code displayed by LXR

 
 

    


File indexing completed on 2025-08-05 08:09:38

0001 // This file is part of the Acts project.
0002 //
0003 // Copyright (C) 2016-2019 CERN for the benefit of the Acts project
0004 //
0005 // This Source Code Form is subject to the terms of the Mozilla Public
0006 // License, v. 2.0. If a copy of the MPL was not distributed with this
0007 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
0008 
0009 #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/VolumeBounds.hpp"
0015 #include "Acts/Material/IMaterialDecorator.hpp"
0016 #include "Acts/Material/IVolumeMaterial.hpp"
0017 #include "Acts/Material/ProtoVolumeMaterial.hpp"
0018 #include "Acts/Propagator/Navigator.hpp"
0019 #include "Acts/Surfaces/RegularSurface.hpp"
0020 #include "Acts/Surfaces/Surface.hpp"
0021 #include "Acts/Utilities/BinningType.hpp"
0022 #include "Acts/Utilities/TransformRange.hpp"
0023 
0024 #include <algorithm>
0025 #include <ostream>
0026 #include <string>
0027 #include <tuple>
0028 #include <utility>
0029 
0030 namespace Acts {
0031 class ISurfaceMaterial;
0032 
0033 // constructor for arguments
0034 TrackingVolume::TrackingVolume(
0035     const Transform3& transform,
0036     std::shared_ptr<const VolumeBounds> volumeBounds,
0037     std::shared_ptr<const IVolumeMaterial> volumeMaterial,
0038     std::unique_ptr<const LayerArray> staticLayerArray,
0039     std::shared_ptr<const TrackingVolumeArray> containedVolumeArray,
0040     MutableTrackingVolumeVector denseVolumeVector,
0041     const std::string& volumeName)
0042     : Volume(transform, std::move(volumeBounds)),
0043       m_confinedLayers(std::move(staticLayerArray)),
0044       m_confinedVolumes(std::move(containedVolumeArray)),
0045       m_confinedDenseVolumes({}),
0046       m_volumeMaterial(std::move(volumeMaterial)),
0047       m_name(volumeName) {
0048   createBoundarySurfaces();
0049   interlinkLayers();
0050   connectDenseBoundarySurfaces(denseVolumeVector);
0051 }
0052 
0053 TrackingVolume::TrackingVolume(const Volume& volume,
0054                                const std::string& volumeName)
0055     : TrackingVolume(volume.transform(), volume.volumeBoundsPtr(), nullptr,
0056                      volumeName) {}
0057 
0058 TrackingVolume::TrackingVolume(
0059     const Transform3& transform, std::shared_ptr<const VolumeBounds> volbounds,
0060     const std::shared_ptr<const TrackingVolumeArray>& containedVolumeArray,
0061     const std::string& volumeName)
0062     : TrackingVolume(transform, std::move(volbounds), nullptr, nullptr,
0063                      containedVolumeArray, {}, volumeName) {}
0064 
0065 TrackingVolume::~TrackingVolume() {
0066   delete m_glueVolumeDescriptor;
0067 }
0068 
0069 const TrackingVolume* TrackingVolume::lowestTrackingVolume(
0070     const GeometryContext& /*gctx*/, const Vector3& position,
0071     const double tol) const {
0072   // confined static volumes - highest hierarchy
0073   if (m_confinedVolumes) {
0074     return (m_confinedVolumes->object(position).get());
0075   }
0076 
0077   // search for dense volumes
0078   if (!m_confinedDenseVolumes.empty()) {
0079     for (auto& denseVolume : m_confinedDenseVolumes) {
0080       if (denseVolume->inside(position, tol)) {
0081         return denseVolume.get();
0082       }
0083     }
0084   }
0085 
0086   // there is no lower sub structure
0087   return this;
0088 }
0089 
0090 const TrackingVolumeBoundaries& TrackingVolume::boundarySurfaces() const {
0091   return (m_boundarySurfaces);
0092 }
0093 
0094 void TrackingVolume::connectDenseBoundarySurfaces(
0095     MutableTrackingVolumeVector& confinedDenseVolumes) {
0096   if (!confinedDenseVolumes.empty()) {
0097     Direction dir = Direction::Positive;
0098     // Walk over each dense volume
0099     for (auto& confDenseVol : confinedDenseVolumes) {
0100       // Walk over each boundary surface of the volume
0101       auto& boundSur = confDenseVol->boundarySurfaces();
0102       for (unsigned int i = 0; i < boundSur.size(); i++) {
0103         // Skip empty entries since we do not know the shape of the dense volume
0104         // and therewith the used indices
0105         if (boundSur.at(i) == nullptr) {
0106           continue;
0107         }
0108 
0109         // Use mother volume as the opposite direction of the already used
0110         // direction
0111         auto mutableBs =
0112             std::const_pointer_cast<BoundarySurfaceT<TrackingVolume>>(
0113                 boundSur.at(i));
0114         if (mutableBs->m_oppositeVolume != nullptr &&
0115             mutableBs->m_alongVolume == nullptr) {
0116           dir = Direction::Positive;
0117           mutableBs->attachVolume(this, dir);
0118         } else {
0119           if (mutableBs->m_oppositeVolume == nullptr &&
0120               mutableBs->m_alongVolume != nullptr) {
0121             dir = Direction::Negative;
0122             mutableBs->attachVolume(this, dir);
0123           }
0124         }
0125 
0126         // Update the boundary
0127         confDenseVol->updateBoundarySurface((BoundarySurfaceFace)i, mutableBs);
0128       }
0129       // Store the volume
0130       m_confinedDenseVolumes.push_back(std::move(confDenseVol));
0131     }
0132   }
0133 }
0134 
0135 void TrackingVolume::createBoundarySurfaces() {
0136   using Boundary = BoundarySurfaceT<TrackingVolume>;
0137 
0138   // Transform Surfaces To BoundarySurfaces
0139   auto orientedSurfaces = Volume::volumeBounds().orientedSurfaces(m_transform);
0140 
0141   m_boundarySurfaces.reserve(orientedSurfaces.size());
0142   for (auto& osf : orientedSurfaces) {
0143     TrackingVolume* opposite = nullptr;
0144     TrackingVolume* along = nullptr;
0145     if (osf.direction == Direction::OppositeNormal) {
0146       opposite = this;
0147     } else {
0148       along = this;
0149     }
0150     m_boundarySurfaces.push_back(std::make_shared<const Boundary>(
0151         std::move(osf.surface), opposite, along));
0152   }
0153 }
0154 
0155 void TrackingVolume::glueTrackingVolume(const GeometryContext& gctx,
0156                                         BoundarySurfaceFace bsfMine,
0157                                         TrackingVolume* neighbor,
0158                                         BoundarySurfaceFace bsfNeighbor) {
0159   // Find the connection of the two tracking volumes: binR returns the center
0160   // except for cylindrical volumes
0161   Vector3 bPosition(binningPosition(gctx, binR));
0162   Vector3 distance = Vector3(neighbor->binningPosition(gctx, binR) - bPosition);
0163   // glue to the face
0164   std::shared_ptr<const BoundarySurfaceT<TrackingVolume>> bSurfaceMine =
0165       boundarySurfaces().at(bsfMine);
0166   // @todo - complex glueing could be possible with actual intersection for the
0167   // normal vector
0168   // Coerce the arbitrary position bPosition to be on the surface repr so we can
0169   // get a normal
0170   Vector3 nvector =
0171       bSurfaceMine->surfaceRepresentation().normal(gctx, bPosition);
0172   // estimate the orientation
0173   Direction dir = Direction::fromScalar(nvector.dot(distance));
0174   // The easy case :
0175   // - no glue volume descriptors on either side
0176   if ((m_glueVolumeDescriptor == nullptr) ||
0177       m_glueVolumeDescriptor->glueVolumes(bsfMine) == nullptr) {
0178     // the boundary orientation
0179     auto mutableBSurfaceMine =
0180         std::const_pointer_cast<BoundarySurfaceT<TrackingVolume>>(bSurfaceMine);
0181     mutableBSurfaceMine->attachVolume(neighbor, dir);
0182     // Make sure you keep the boundary material if there
0183     const Surface& neighborSurface =
0184         neighbor->m_boundarySurfaces.at(bsfNeighbor)->surfaceRepresentation();
0185     auto neighborMaterial = neighborSurface.surfaceMaterialSharedPtr();
0186     const Surface& mySurface = bSurfaceMine->surfaceRepresentation();
0187     auto myMaterial = mySurface.surfaceMaterialSharedPtr();
0188     // Keep the neighbor material
0189     if (myMaterial == nullptr && neighborMaterial != nullptr) {
0190       Surface* myMutbableSurface = const_cast<Surface*>(&mySurface);
0191       myMutbableSurface->assignSurfaceMaterial(neighborMaterial);
0192     }
0193     // Now set it to the neighbor volume
0194     (neighbor->m_boundarySurfaces).at(bsfNeighbor) = bSurfaceMine;
0195   }
0196 }
0197 
0198 void TrackingVolume::glueTrackingVolumes(
0199     const GeometryContext& gctx, BoundarySurfaceFace bsfMine,
0200     const std::shared_ptr<TrackingVolumeArray>& neighbors,
0201     BoundarySurfaceFace bsfNeighbor) {
0202   // find the connection of the two tracking volumes : binR returns the center
0203   // except for cylindrical volumes
0204   std::shared_ptr<const TrackingVolume> nRefVolume =
0205       neighbors->arrayObjects().at(0);
0206   // get the distance
0207   Vector3 bPosition(binningPosition(gctx, binR));
0208   Vector3 distance =
0209       Vector3(nRefVolume->binningPosition(gctx, binR) - bPosition);
0210   // take the normal at the binning positio
0211   std::shared_ptr<const BoundarySurfaceT<TrackingVolume>> bSurfaceMine =
0212       boundarySurfaces().at(bsfMine);
0213   // @todo - complex glueing could be possible with actual intersection for the
0214   // normal vector
0215   // Coerce the arbitrary position bPosition to be on the surface repr so we can
0216   // get a normal
0217   Vector3 nvector =
0218       bSurfaceMine->surfaceRepresentation().normal(gctx, bPosition);
0219   // estimate the orientation
0220   Direction dir = Direction::fromScalar(nvector.dot(distance));
0221   // the easy case :
0222   // - no glue volume descriptors on either side
0223   if ((m_glueVolumeDescriptor == nullptr) ||
0224       !m_glueVolumeDescriptor->glueVolumes(bsfMine)) {
0225     // the boundary orientation
0226     auto mutableBSurfaceMine =
0227         std::const_pointer_cast<BoundarySurfaceT<TrackingVolume>>(bSurfaceMine);
0228     mutableBSurfaceMine->attachVolumeArray(neighbors, dir);
0229     // now set it to the neighbor volumes - the optised way
0230     for (auto& nVolume : neighbors->arrayObjects()) {
0231       auto mutableNVolume = std::const_pointer_cast<TrackingVolume>(nVolume);
0232       (mutableNVolume->m_boundarySurfaces).at(bsfNeighbor) = bSurfaceMine;
0233     }
0234   }
0235 }
0236 
0237 void TrackingVolume::assignBoundaryMaterial(
0238     std::shared_ptr<const ISurfaceMaterial> surfaceMaterial,
0239     BoundarySurfaceFace bsFace) {
0240   auto bSurface = m_boundarySurfaces.at(bsFace);
0241   RegularSurface* surface =
0242       const_cast<RegularSurface*>(&bSurface->surfaceRepresentation());
0243   surface->assignSurfaceMaterial(std::move(surfaceMaterial));
0244 }
0245 
0246 void TrackingVolume::updateBoundarySurface(
0247     BoundarySurfaceFace bsf,
0248     std::shared_ptr<const BoundarySurfaceT<TrackingVolume>> bs,
0249     bool checkmaterial) {
0250   if (checkmaterial) {
0251     auto cMaterialPtr = m_boundarySurfaces.at(bsf)
0252                             ->surfaceRepresentation()
0253                             .surfaceMaterialSharedPtr();
0254     auto bsMaterial = bs->surfaceRepresentation().surfaceMaterial();
0255     if (cMaterialPtr != nullptr && bsMaterial == nullptr) {
0256       RegularSurface* surface =
0257           const_cast<RegularSurface*>(&bs->surfaceRepresentation());
0258       surface->assignSurfaceMaterial(cMaterialPtr);
0259     }
0260   }
0261   m_boundarySurfaces.at(bsf) = std::move(bs);
0262 }
0263 
0264 void TrackingVolume::registerGlueVolumeDescriptor(GlueVolumesDescriptor* gvd) {
0265   delete m_glueVolumeDescriptor;
0266   m_glueVolumeDescriptor = gvd;
0267 }
0268 
0269 GlueVolumesDescriptor& TrackingVolume::glueVolumesDescriptor() {
0270   if (m_glueVolumeDescriptor == nullptr) {
0271     m_glueVolumeDescriptor = new GlueVolumesDescriptor;
0272   }
0273   return (*m_glueVolumeDescriptor);
0274 }
0275 
0276 void TrackingVolume::synchronizeLayers(double envelope) const {
0277   // case a : Layers exist
0278   // msgstream << MSG::VERBOSE << "  -> synchronizing Layer dimensions of
0279   // TrackingVolume '" << volumeName() << "'." << endreq;
0280 
0281   if (m_confinedLayers) {
0282     // msgstream << MSG::VERBOSE << "  ---> working on " <<
0283     // m_confinedLayers->arrayObjects().size() << " (material+navigation)
0284     // layers." << endreq;
0285     for (auto& clayIter : m_confinedLayers->arrayObjects()) {
0286       if (clayIter) {
0287         // @todo implement synchronize layer
0288         //  if (clayIter->surfaceRepresentation().type() == Surface::Cylinder &&
0289         //  !(center().isApprox(clayIter->surfaceRepresentation().center())) )
0290         //      clayIter->resizeAndRepositionLayer(volumeBounds(),center(),envelope);
0291         //  else
0292         //      clayIter->resizeLayer(volumeBounds(),envelope);
0293       }  // else
0294       // msgstream << MSG::WARNING << "  ---> found 0 pointer to layer,
0295       // indicates problem." << endreq;
0296     }
0297   }
0298 
0299   // case b : container volume -> step down
0300   if (m_confinedVolumes) {
0301     // msgstream << MSG::VERBOSE << "  ---> no confined layers, working on " <<
0302     // m_confinedVolumes->arrayObjects().size() << " confined volumes." <<
0303     // endreq;
0304     for (auto& cVolumesIter : m_confinedVolumes->arrayObjects()) {
0305       cVolumesIter->synchronizeLayers(envelope);
0306     }
0307   }
0308 }
0309 
0310 void TrackingVolume::interlinkLayers() {
0311   if (m_confinedLayers) {
0312     auto& layers = m_confinedLayers->arrayObjects();
0313 
0314     // forward register the last one as the previous one
0315     //  first <- | -> second, first <- | -> second, first <- | -> second
0316     const Layer* lastLayer = nullptr;
0317     for (auto& layerPtr : layers) {
0318       // we'll need to mutate our confined layers to perform this operation
0319       Layer& mutableLayer = *(std::const_pointer_cast<Layer>(layerPtr));
0320       // register the layers
0321       mutableLayer.m_nextLayerUtility = m_confinedLayers->binUtility();
0322       mutableLayer.m_nextLayers.first = lastLayer;
0323       // register the volume
0324       mutableLayer.encloseTrackingVolume(*this);
0325       // remember the last layer
0326       lastLayer = &mutableLayer;
0327     }
0328     // backward loop
0329     lastLayer = nullptr;
0330     for (auto layerIter = layers.rbegin(); layerIter != layers.rend();
0331          ++layerIter) {
0332       // set the other next volume
0333       Layer& mutableLayer = *(std::const_pointer_cast<Layer>(*layerIter));
0334       mutableLayer.m_nextLayers.second = lastLayer;
0335       lastLayer = &mutableLayer;
0336     }
0337   }
0338 }
0339 
0340 void TrackingVolume::closeGeometry(
0341     const IMaterialDecorator* materialDecorator,
0342     std::unordered_map<GeometryIdentifier, const TrackingVolume*>& volumeMap,
0343     std::size_t& vol, const GeometryIdentifierHook& hook,
0344     const Logger& logger) {
0345   // we can construct the volume ID from this
0346   auto volumeID = GeometryIdentifier().setVolume(++vol);
0347   // assign the Volume ID to the volume itself
0348   auto thisVolume = const_cast<TrackingVolume*>(this);
0349   thisVolume->assignGeometryId(volumeID);
0350   ACTS_DEBUG("volumeID: " << volumeID << ", name: " << volumeName());
0351   // insert the volume into the map
0352   volumeMap[volumeID] = thisVolume;
0353 
0354   // assign the material if you have a decorator
0355   if (materialDecorator != nullptr) {
0356     materialDecorator->decorate(*thisVolume);
0357   }
0358   if (thisVolume->volumeMaterial() == nullptr &&
0359       thisVolume->motherVolume() != nullptr &&
0360       thisVolume->motherVolume()->volumeMaterial() != nullptr) {
0361     auto protoMaterial = dynamic_cast<const ProtoVolumeMaterial*>(
0362         thisVolume->motherVolume()->volumeMaterial());
0363     if (protoMaterial == nullptr) {
0364       thisVolume->assignVolumeMaterial(
0365           thisVolume->motherVolume()->volumeMaterialSharedPtr());
0366     }
0367   }
0368 
0369   this->assignGeometryId(volumeID);
0370   // loop over the boundary surfaces
0371   GeometryIdentifier::Value iboundary = 0;
0372   // loop over the boundary surfaces
0373   for (auto& bSurfIter : boundarySurfaces()) {
0374     // get the intersection solution
0375     auto& bSurface = bSurfIter->surfaceRepresentation();
0376     // create the boundary surface id
0377     auto boundaryID = GeometryIdentifier(volumeID).setBoundary(++iboundary);
0378     // now assign to the boundary surface
0379     auto& mutableBSurface = *(const_cast<RegularSurface*>(&bSurface));
0380     mutableBSurface.assignGeometryId(boundaryID);
0381     // Assign material if you have a decorator
0382     if (materialDecorator != nullptr) {
0383       materialDecorator->decorate(mutableBSurface);
0384     }
0385   }
0386 
0387   // A) this is NOT a container volume, volumeID is already incremented
0388   if (!m_confinedVolumes) {
0389     // loop over the confined layers
0390     if (m_confinedLayers) {
0391       GeometryIdentifier::Value ilayer = 0;
0392       // loop over the layers
0393       for (auto& layerPtr : m_confinedLayers->arrayObjects()) {
0394         // create the layer identification
0395         auto layerID = GeometryIdentifier(volumeID).setLayer(++ilayer);
0396         // now close the geometry
0397         auto mutableLayerPtr = std::const_pointer_cast<Layer>(layerPtr);
0398         mutableLayerPtr->closeGeometry(materialDecorator, layerID, hook,
0399                                        logger);
0400       }
0401     }
0402   } else {
0403     // B) this is a container volume, go through sub volume
0404     // do the loop
0405     for (auto& volumesIter : m_confinedVolumes->arrayObjects()) {
0406       auto mutableVolumesIter =
0407           std::const_pointer_cast<TrackingVolume>(volumesIter);
0408       mutableVolumesIter->setMotherVolume(this);
0409       mutableVolumesIter->closeGeometry(materialDecorator, volumeMap, vol, hook,
0410                                         logger);
0411     }
0412   }
0413 
0414   if (!m_confinedDenseVolumes.empty()) {
0415     for (auto& volumesIter : m_confinedDenseVolumes) {
0416       auto mutableVolumesIter =
0417           std::const_pointer_cast<TrackingVolume>(volumesIter);
0418       mutableVolumesIter->setMotherVolume(this);
0419       mutableVolumesIter->closeGeometry(materialDecorator, volumeMap, vol, hook,
0420                                         logger);
0421     }
0422   }
0423 }
0424 
0425 // Returns the boundary surfaces ordered in probability to hit them based on
0426 boost::container::small_vector<BoundaryIntersection, 4>
0427 TrackingVolume::compatibleBoundaries(const GeometryContext& gctx,
0428                                      const Vector3& position,
0429                                      const Vector3& direction,
0430                                      const NavigationOptions<Surface>& options,
0431                                      const Logger& logger) const {
0432   ACTS_VERBOSE("Finding compatibleBoundaries");
0433   // Loop over boundarySurfaces and calculate the intersection
0434   auto excludeObject = options.startObject;
0435   boost::container::small_vector<BoundaryIntersection, 4> bIntersections;
0436 
0437   // The Limits: current, path & overstepping
0438   double nearLimit = 0;
0439   double farLimit = options.farLimit;
0440 
0441   // Helper function to test intersection
0442   auto checkIntersection =
0443       [&](SurfaceMultiIntersection& smIntersection,
0444           const BoundarySurface* bSurface) -> BoundaryIntersection {
0445     for (const auto& sIntersection : smIntersection.split()) {
0446       if (!sIntersection) {
0447         continue;
0448       }
0449 
0450       if (options.forceIntersectBoundaries) {
0451         const bool coCriterion =
0452             std::abs(sIntersection.pathLength()) < std::abs(nearLimit);
0453         ACTS_VERBOSE("Forcing intersection with surface "
0454                      << bSurface->surfaceRepresentation().geometryId());
0455         if (coCriterion) {
0456           ACTS_VERBOSE("Intersection forced successfully ");
0457           ACTS_VERBOSE("- intersection path length "
0458                        << std::abs(sIntersection.pathLength())
0459                        << " < overstep limit " << std::abs(nearLimit));
0460           return BoundaryIntersection(sIntersection, bSurface);
0461         }
0462         ACTS_VERBOSE("Can't force intersection: ");
0463         ACTS_VERBOSE("- intersection path length "
0464                      << std::abs(sIntersection.pathLength())
0465                      << " > overstep limit " << std::abs(nearLimit));
0466       }
0467 
0468       ACTS_VERBOSE("Check intersection with surface "
0469                    << bSurface->surfaceRepresentation().geometryId());
0470       if (detail::checkIntersection(sIntersection.intersection(), nearLimit,
0471                                     farLimit, logger)) {
0472         return BoundaryIntersection(sIntersection, bSurface);
0473       }
0474     }
0475 
0476     ACTS_VERBOSE("No intersection accepted");
0477     return BoundaryIntersection(SurfaceIntersection::invalid(), bSurface);
0478   };
0479 
0480   /// Helper function to process boundary surfaces
0481   auto processBoundaries =
0482       [&](const TrackingVolumeBoundaries& bSurfaces) -> void {
0483     ACTS_VERBOSE("Processing boundaries");
0484     // Loop over the boundary surfaces
0485     for (auto& bsIter : bSurfaces) {
0486       // Get the boundary surface pointer
0487       const auto& bSurfaceRep = bsIter->surfaceRepresentation();
0488       ACTS_VERBOSE("Consider boundary surface " << bSurfaceRep.geometryId()
0489                                                 << " :\n"
0490                                                 << std::tie(bSurfaceRep, gctx));
0491 
0492       // Exclude the boundary where you are on
0493       if (excludeObject != &bSurfaceRep) {
0494         auto bCandidate = bSurfaceRep.intersect(gctx, position, direction,
0495                                                 options.boundaryCheck);
0496         // Intersect and continue
0497         auto bIntersection = checkIntersection(bCandidate, bsIter.get());
0498         if (bIntersection.first) {
0499           ACTS_VERBOSE(" - Proceed with surface");
0500           bIntersections.push_back(bIntersection);
0501         } else {
0502           ACTS_VERBOSE(" - Surface intersecion invalid");
0503         }
0504       } else {
0505         ACTS_VERBOSE(" - Surface is excluded surface");
0506       }
0507     }
0508   };
0509 
0510   // Process the boundaries of the current volume
0511   auto& bSurfaces = boundarySurfaces();
0512   ACTS_VERBOSE("Volume reports " << bSurfaces.size() << " boundary surfaces");
0513   processBoundaries(bSurfaces);
0514 
0515   // Process potential boundaries of contained volumes
0516   auto confinedDenseVolumes = denseVolumes();
0517   ACTS_VERBOSE("Volume reports " << confinedDenseVolumes.size()
0518                                  << " confined dense volumes");
0519   for (const auto& dv : confinedDenseVolumes) {
0520     auto& bSurfacesConfined = dv->boundarySurfaces();
0521     ACTS_VERBOSE(" -> " << bSurfacesConfined.size() << " boundary surfaces");
0522     processBoundaries(bSurfacesConfined);
0523   }
0524 
0525   auto comparator = [](double a, double b) {
0526     // sign function would be nice but ...
0527     if ((a > 0 && b > 0) || (a < 0 && b < 0)) {
0528       return a < b;
0529     }
0530     if (a > 0) {  // b < 0
0531       return true;
0532     }
0533     return false;
0534   };
0535 
0536   std::sort(bIntersections.begin(), bIntersections.end(),
0537             [&](const BoundaryIntersection& a, const BoundaryIntersection& b) {
0538               return comparator(a.first.pathLength(), b.first.pathLength());
0539             });
0540   return bIntersections;
0541 }
0542 
0543 boost::container::small_vector<LayerIntersection, 10>
0544 TrackingVolume::compatibleLayers(
0545     const GeometryContext& gctx, const Vector3& position,
0546     const Vector3& direction, const NavigationOptions<Layer>& options) const {
0547   // the layer intersections which are valid
0548   boost::container::small_vector<LayerIntersection, 10> lIntersections;
0549 
0550   // the confinedLayers
0551   if (m_confinedLayers != nullptr) {
0552     // start layer given or not - test layer
0553     const Layer* tLayer = options.startObject != nullptr
0554                               ? options.startObject
0555                               : associatedLayer(gctx, position);
0556     while (tLayer != nullptr) {
0557       // check if the layer needs resolving
0558       // - resolveSensitive -> always take layer if it has a surface array
0559       // - resolveMaterial -> always take layer if it has material
0560       // - resolvePassive -> always take, unless it's a navigation layer
0561       // skip the start object
0562       if (tLayer != options.startObject && tLayer->resolve(options)) {
0563         // if it's a resolveable start layer, you are by definition on it
0564         // layer on approach intersection
0565         auto atIntersection =
0566             tLayer->surfaceOnApproach(gctx, position, direction, options);
0567         auto path = atIntersection.pathLength();
0568         bool withinLimit = std::abs(path) <= std::abs(options.farLimit);
0569         // Intersection is ok - take it (move to surface on approach)
0570         if (atIntersection && withinLimit) {
0571           // create a layer intersection
0572           lIntersections.push_back(LayerIntersection(atIntersection, tLayer));
0573         }
0574       }
0575       // move to next one or break because you reached the end layer
0576       tLayer = (tLayer == options.endObject)
0577                    ? nullptr
0578                    : tLayer->nextLayer(gctx, position, direction);
0579     }
0580     std::sort(lIntersections.begin(), lIntersections.end(),
0581               [](const LayerIntersection& a, const LayerIntersection& b) {
0582                 return SurfaceIntersection::pathLengthOrder(a.first, b.first);
0583               });
0584   }
0585   // and return
0586   return lIntersections;
0587 }
0588 
0589 const std::string& TrackingVolume::volumeName() const {
0590   return m_name;
0591 }
0592 
0593 const IVolumeMaterial* TrackingVolume::volumeMaterial() const {
0594   return m_volumeMaterial.get();
0595 }
0596 
0597 const std::shared_ptr<const IVolumeMaterial>&
0598 TrackingVolume::volumeMaterialSharedPtr() const {
0599   return m_volumeMaterial;
0600 }
0601 
0602 void TrackingVolume::assignVolumeMaterial(
0603     std::shared_ptr<const IVolumeMaterial> material) {
0604   m_volumeMaterial = std::move(material);
0605 }
0606 
0607 const LayerArray* TrackingVolume::confinedLayers() const {
0608   return m_confinedLayers.get();
0609 }
0610 
0611 const MutableTrackingVolumeVector TrackingVolume::denseVolumes() const {
0612   return m_confinedDenseVolumes;
0613 }
0614 
0615 std::shared_ptr<const TrackingVolumeArray> TrackingVolume::confinedVolumes()
0616     const {
0617   return m_confinedVolumes;
0618 }
0619 
0620 const TrackingVolume* TrackingVolume::motherVolume() const {
0621   return m_motherVolume;
0622 }
0623 
0624 TrackingVolume* TrackingVolume::motherVolume() {
0625   return m_motherVolume;
0626 }
0627 
0628 void TrackingVolume::setMotherVolume(TrackingVolume* mvol) {
0629   m_motherVolume = mvol;
0630 }
0631 
0632 const Acts::Layer* TrackingVolume::associatedLayer(
0633     const GeometryContext& /*gctx*/, const Vector3& position) const {
0634   // confined static layers - highest hierarchy
0635   if (m_confinedLayers != nullptr) {
0636     return (m_confinedLayers->object(position).get());
0637   }
0638 
0639   // return the null pointer
0640   return nullptr;
0641 }
0642 
0643 TrackingVolume::VolumeRange TrackingVolume::volumes() const {
0644   return VolumeRange{m_volumes};
0645 }
0646 
0647 TrackingVolume::MutableVolumeRange TrackingVolume::volumes() {
0648   return MutableVolumeRange{m_volumes};
0649 }
0650 
0651 TrackingVolume& TrackingVolume::addVolume(
0652     std::unique_ptr<TrackingVolume> volume) {
0653   if (volume->motherVolume() != nullptr) {
0654     throw std::invalid_argument("Volume already has a mother volume");
0655   }
0656 
0657   volume->setMotherVolume(this);
0658   m_volumes.push_back(std::move(volume));
0659   return *m_volumes.back();
0660 }
0661 
0662 }  // namespace Acts