Analysis Software
Documentation for sPHENIX simulation software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
TrackingVolume.cpp
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file TrackingVolume.cpp
1 // This file is part of the Acts project.
2 //
3 // Copyright (C) 2016-2019 CERN for the benefit of the Acts project
4 //
5 // This Source Code Form is subject to the terms of the Mozilla Public
6 // License, v. 2.0. If a copy of the MPL was not distributed with this
7 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 
10 
23 #include "Acts/Utilities/Ray.hpp"
24 
25 #include <algorithm>
26 #include <array>
27 #include <functional>
28 #include <ostream>
29 #include <string>
30 #include <tuple>
31 #include <type_traits>
32 #include <utility>
33 
34 namespace Acts {
35 class ISurfaceMaterial;
36 } // namespace Acts
37 
39  const Transform3& transform, VolumeBoundsPtr volbounds,
40  const std::shared_ptr<const TrackingVolumeArray>& containedVolumeArray,
41  const std::string& volumeName)
42  : Volume(transform, std::move(volbounds)),
43  m_volumeMaterial(nullptr),
44  m_boundarySurfaces(),
45  m_confinedLayers(nullptr),
46  m_confinedVolumes(containedVolumeArray),
47  m_name(volumeName) {
50 }
51 
52 // constructor for arguments
54  const Transform3& transform, VolumeBoundsPtr volumeBounds,
55  std::shared_ptr<const IVolumeMaterial> volumeMaterial,
56  std::unique_ptr<const LayerArray> staticLayerArray,
57  std::shared_ptr<const TrackingVolumeArray> containedVolumeArray,
58  MutableTrackingVolumeVector denseVolumeVector,
59  const std::string& volumeName)
60  : Volume(transform, std::move(volumeBounds)),
61  m_volumeMaterial(std::move(volumeMaterial)),
62  m_confinedLayers(std::move(staticLayerArray)),
63  m_confinedVolumes(std::move(containedVolumeArray)),
64  m_confinedDenseVolumes({}),
65  m_name(volumeName) {
66  createBoundarySurfaces();
67  interlinkLayers();
68  connectDenseBoundarySurfaces(denseVolumeVector);
69 }
70 
71 // constructor for arguments
73  const Transform3& transform, VolumeBoundsPtr volbounds,
74  std::vector<std::unique_ptr<Volume::BoundingBox>> boxStore,
75  std::vector<std::unique_ptr<const Volume>> descendants,
76  const Volume::BoundingBox* top,
77  std::shared_ptr<const IVolumeMaterial> volumeMaterial,
78  const std::string& volumeName)
79  : Volume(transform, std::move(volbounds)),
80  m_volumeMaterial(std::move(volumeMaterial)),
81  m_name(volumeName),
82  m_descendantVolumes(std::move(descendants)),
83  m_bvhTop(top) {
85  // we take a copy of the unique box pointers, but we want to
86  // store them as consts.
87  for (auto& uptr : boxStore) {
88  m_boundingBoxes.push_back(
89  std::unique_ptr<Volume::BoundingBox>(uptr.release()));
90  }
91 }
92 
94  delete m_glueVolumeDescriptor;
95 }
96 
98  const GeometryContext& /*gctx*/, const Vector3& position,
99  const double tol) const {
100  // confined static volumes - highest hierarchy
101  if (m_confinedVolumes) {
102  return (m_confinedVolumes->object(position).get());
103  }
104 
105  // search for dense volumes
106  if (!m_confinedDenseVolumes.empty()) {
107  for (auto& denseVolume : m_confinedDenseVolumes) {
108  if (denseVolume->inside(position, tol)) {
109  return denseVolume.get();
110  }
111  }
112  }
113 
114  // there is no lower sub structure
115  return this;
116 }
117 
119  const {
120  return (m_boundarySurfaces);
121 }
122 
124  MutableTrackingVolumeVector& confinedDenseVolumes) {
125  if (!confinedDenseVolumes.empty()) {
127  // Walk over each dense volume
128  for (auto& confDenseVol : confinedDenseVolumes) {
129  // Walk over each boundary surface of the volume
130  auto& boundSur = confDenseVol->boundarySurfaces();
131  for (unsigned int i = 0; i < boundSur.size(); i++) {
132  // Skip empty entries since we do not know the shape of the dense volume
133  // and therewith the used indices
134  if (boundSur.at(i) == nullptr) {
135  continue;
136  }
137 
138  // Use mother volume as the opposite direction of the already used
139  // direction
140  auto mutableBs =
142  boundSur.at(i));
143  if (mutableBs->m_oppositeVolume != nullptr &&
144  mutableBs->m_alongVolume == nullptr) {
145  dir = Direction::Positive;
146  mutableBs->attachVolume(this, dir);
147  } else {
148  if (mutableBs->m_oppositeVolume == nullptr &&
149  mutableBs->m_alongVolume != nullptr) {
150  dir = Direction::Negative;
151  mutableBs->attachVolume(this, dir);
152  }
153  }
154 
155  // Update the boundary
156  confDenseVol->updateBoundarySurface((BoundarySurfaceFace)i, mutableBs);
157  }
158  // Store the volume
159  m_confinedDenseVolumes.push_back(std::move(confDenseVol));
160  }
161  }
162 }
163 
165  using Boundary = BoundarySurfaceT<TrackingVolume>;
166 
167  // Transform Surfaces To BoundarySurfaces
168  auto orientedSurfaces = Volume::volumeBounds().orientedSurfaces(m_transform);
169 
170  m_boundarySurfaces.reserve(orientedSurfaces.size());
171  for (auto& osf : orientedSurfaces) {
172  TrackingVolume* opposite = nullptr;
173  TrackingVolume* along = nullptr;
174  if (osf.second == Direction::Negative) {
175  opposite = this;
176  } else {
177  along = this;
178  }
179  m_boundarySurfaces.push_back(std::make_shared<const Boundary>(
180  std::move(osf.first), opposite, along));
181  }
182 }
183 
185  BoundarySurfaceFace bsfMine,
186  TrackingVolume* neighbor,
187  BoundarySurfaceFace bsfNeighbor) {
188  // Find the connection of the two tracking volumes: binR returns the center
189  // except for cylindrical volumes
190  Vector3 bPosition(binningPosition(gctx, binR));
191  Vector3 distance = Vector3(neighbor->binningPosition(gctx, binR) - bPosition);
192  // glue to the face
193  std::shared_ptr<const BoundarySurfaceT<TrackingVolume>> bSurfaceMine =
194  boundarySurfaces().at(bsfMine);
195  // @todo - complex glueing could be possible with actual intersection for the
196  // normal vector
197  Vector3 nvector =
198  bSurfaceMine->surfaceRepresentation().normal(gctx, bPosition);
199  // estimate the orientation
200  Direction dir = Direction::fromScalar(nvector.dot(distance));
201  // The easy case :
202  // - no glue volume descriptors on either side
203  if ((m_glueVolumeDescriptor == nullptr) ||
204  m_glueVolumeDescriptor->glueVolumes(bsfMine) == nullptr) {
205  // the boundary orientation
206  auto mutableBSurfaceMine =
208  mutableBSurfaceMine->attachVolume(neighbor, dir);
209  // Make sure you keep the boundary material if there
210  const Surface& neighborSurface =
211  neighbor->m_boundarySurfaces.at(bsfNeighbor)->surfaceRepresentation();
212  auto neighborMaterial = neighborSurface.surfaceMaterialSharedPtr();
213  const Surface& mySurface = bSurfaceMine->surfaceRepresentation();
214  auto myMaterial = mySurface.surfaceMaterialSharedPtr();
215  // Keep the neighbor material
216  if (myMaterial == nullptr and neighborMaterial != nullptr) {
217  Surface* myMutbableSurface = const_cast<Surface*>(&mySurface);
218  myMutbableSurface->assignSurfaceMaterial(neighborMaterial);
219  }
220  // Now set it to the neighbor volume
221  (neighbor->m_boundarySurfaces).at(bsfNeighbor) = bSurfaceMine;
222  }
223 }
224 
226  const GeometryContext& gctx, BoundarySurfaceFace bsfMine,
227  const std::shared_ptr<TrackingVolumeArray>& neighbors,
228  BoundarySurfaceFace bsfNeighbor) {
229  // find the connection of the two tracking volumes : binR returns the center
230  // except for cylindrical volumes
231  std::shared_ptr<const TrackingVolume> nRefVolume =
232  neighbors->arrayObjects().at(0);
233  // get the distance
234  Vector3 bPosition(binningPosition(gctx, binR));
235  Vector3 distance =
236  Vector3(nRefVolume->binningPosition(gctx, binR) - bPosition);
237  // take the normal at the binning positio
238  std::shared_ptr<const BoundarySurfaceT<TrackingVolume>> bSurfaceMine =
239  boundarySurfaces().at(bsfMine);
240  // @todo - complex glueing could be possible with actual intersection for the
241  // normal vector
242  Vector3 nvector =
243  bSurfaceMine->surfaceRepresentation().normal(gctx, bPosition);
244  // estimate the orientation
245  Direction dir = Direction::fromScalar(nvector.dot(distance));
246  // the easy case :
247  // - no glue volume descriptors on either side
248  if ((m_glueVolumeDescriptor == nullptr) ||
249  !m_glueVolumeDescriptor->glueVolumes(bsfMine)) {
250  // the boundary orientation
251  auto mutableBSurfaceMine =
253  mutableBSurfaceMine->attachVolumeArray(neighbors, dir);
254  // now set it to the neighbor volumes - the optised way
255  for (auto& nVolume : neighbors->arrayObjects()) {
256  auto mutableNVolume = std::const_pointer_cast<TrackingVolume>(nVolume);
257  (mutableNVolume->m_boundarySurfaces).at(bsfNeighbor) = bSurfaceMine;
258  }
259  }
260 }
261 
263  std::shared_ptr<const ISurfaceMaterial> surfaceMaterial,
264  BoundarySurfaceFace bsFace) {
265  auto bSurface = m_boundarySurfaces.at(bsFace);
266  Surface* surface = const_cast<Surface*>(&bSurface->surfaceRepresentation());
267  surface->assignSurfaceMaterial(std::move(surfaceMaterial));
268 }
269 
272  std::shared_ptr<const BoundarySurfaceT<TrackingVolume>> bs,
273  bool checkmaterial) {
274  if (checkmaterial) {
275  auto cMaterialPtr = m_boundarySurfaces.at(bsf)
276  ->surfaceRepresentation()
277  .surfaceMaterialSharedPtr();
278  auto bsMaterial = bs->surfaceRepresentation().surfaceMaterial();
279  if (cMaterialPtr != nullptr && bsMaterial == nullptr) {
280  Surface* surface = const_cast<Surface*>(&bs->surfaceRepresentation());
281  surface->assignSurfaceMaterial(cMaterialPtr);
282  }
283  }
284  m_boundarySurfaces.at(bsf) = std::move(bs);
285 }
286 
288  GlueVolumesDescriptor* gvd) {
289  delete m_glueVolumeDescriptor;
290  m_glueVolumeDescriptor = gvd;
291 }
292 
294  if (m_glueVolumeDescriptor == nullptr) {
295  m_glueVolumeDescriptor = new GlueVolumesDescriptor;
296  }
297  return (*m_glueVolumeDescriptor);
298 }
299 
300 void Acts::TrackingVolume::synchronizeLayers(double envelope) const {
301  // case a : Layers exist
302  // msgstream << MSG::VERBOSE << " -> synchronizing Layer dimensions of
303  // TrackingVolume '" << volumeName() << "'." << endreq;
304 
305  if (m_confinedLayers) {
306  // msgstream << MSG::VERBOSE << " ---> working on " <<
307  // m_confinedLayers->arrayObjects().size() << " (material+navigation)
308  // layers." << endreq;
309  for (auto& clayIter : m_confinedLayers->arrayObjects()) {
310  if (clayIter) {
311  // @todo implement synchronize layer
312  // if (clayIter->surfaceRepresentation().type() == Surface::Cylinder &&
313  // !(center().isApprox(clayIter->surfaceRepresentation().center())) )
314  // clayIter->resizeAndRepositionLayer(volumeBounds(),center(),envelope);
315  // else
316  // clayIter->resizeLayer(volumeBounds(),envelope);
317  } // else
318  // msgstream << MSG::WARNING << " ---> found 0 pointer to layer,
319  // indicates problem." << endreq;
320  }
321  }
322 
323  // case b : container volume -> step down
324  if (m_confinedVolumes) {
325  // msgstream << MSG::VERBOSE << " ---> no confined layers, working on " <<
326  // m_confinedVolumes->arrayObjects().size() << " confined volumes." <<
327  // endreq;
328  for (auto& cVolumesIter : m_confinedVolumes->arrayObjects()) {
329  cVolumesIter->synchronizeLayers(envelope);
330  }
331  }
332 }
333 
335  if (m_confinedLayers) {
336  auto& layers = m_confinedLayers->arrayObjects();
337 
338  // forward register the last one as the previous one
339  // first <- | -> second, first <- | -> second, first <- | -> second
340  const Layer* lastLayer = nullptr;
341  for (auto& layerPtr : layers) {
342  // we'll need to mutate our confined layers to perform this operation
343  Layer& mutableLayer = *(std::const_pointer_cast<Layer>(layerPtr));
344  // register the layers
345  mutableLayer.m_nextLayerUtility = m_confinedLayers->binUtility();
346  mutableLayer.m_nextLayers.first = lastLayer;
347  // register the volume
348  mutableLayer.encloseTrackingVolume(*this);
349  // remember the last layer
350  lastLayer = &mutableLayer;
351  }
352  // backward loop
353  lastLayer = nullptr;
354  for (auto layerIter = layers.rbegin(); layerIter != layers.rend();
355  ++layerIter) {
356  // set the other next volume
357  Layer& mutableLayer = *(std::const_pointer_cast<Layer>(*layerIter));
358  mutableLayer.m_nextLayers.second = lastLayer;
359  lastLayer = &mutableLayer;
360  }
361  }
362 }
363 
365  const IMaterialDecorator* materialDecorator,
366  std::unordered_map<GeometryIdentifier, const TrackingVolume*>& volumeMap,
367  size_t& vol, const GeometryIdentifierHook& hook, const Logger& logger) {
368  // we can construct the volume ID from this
369  auto volumeID = GeometryIdentifier().setVolume(++vol);
370  // assign the Volume ID to the volume itself
371  auto thisVolume = const_cast<TrackingVolume*>(this);
372  thisVolume->assignGeometryId(volumeID);
373  ACTS_DEBUG("volumeID: " << volumeID << ", name: " << volumeName());
374  // insert the volume into the map
375  volumeMap[volumeID] = thisVolume;
376 
377  // assign the material if you have a decorator
378  if (materialDecorator != nullptr) {
379  materialDecorator->decorate(*thisVolume);
380  }
381  if (thisVolume->volumeMaterial() == nullptr &&
382  thisVolume->motherVolume() != nullptr &&
383  thisVolume->motherVolume()->volumeMaterial() != nullptr) {
384  auto protoMaterial = dynamic_cast<const Acts::ProtoVolumeMaterial*>(
385  thisVolume->motherVolume()->volumeMaterial());
386  if (protoMaterial == nullptr) {
387  thisVolume->assignVolumeMaterial(
388  thisVolume->motherVolume()->volumeMaterialSharedPtr());
389  }
390  }
391 
392  this->assignGeometryId(volumeID);
393  // loop over the boundary surfaces
394  GeometryIdentifier::Value iboundary = 0;
395  // loop over the boundary surfaces
396  for (auto& bSurfIter : boundarySurfaces()) {
397  // get the intersection solution
398  auto& bSurface = bSurfIter->surfaceRepresentation();
399  // create the boundary surface id
400  auto boundaryID = GeometryIdentifier(volumeID).setBoundary(++iboundary);
401  // now assign to the boundary surface
402  auto& mutableBSurface = *(const_cast<Surface*>(&bSurface));
403  mutableBSurface.assignGeometryId(boundaryID);
404  // Assign material if you have a decorator
405  if (materialDecorator != nullptr) {
406  materialDecorator->decorate(mutableBSurface);
407  }
408  }
409 
410  // A) this is NOT a container volume, volumeID is already incremented
411  if (!m_confinedVolumes) {
412  // loop over the confined layers
413  if (m_confinedLayers) {
414  GeometryIdentifier::Value ilayer = 0;
415  // loop over the layers
416  for (auto& layerPtr : m_confinedLayers->arrayObjects()) {
417  // create the layer identification
418  auto layerID = GeometryIdentifier(volumeID).setLayer(++ilayer);
419  // now close the geometry
420  auto mutableLayerPtr = std::const_pointer_cast<Layer>(layerPtr);
421  mutableLayerPtr->closeGeometry(materialDecorator, layerID, hook,
422  logger);
423  }
424  } else if (m_bvhTop != nullptr) {
425  GeometryIdentifier::Value isurface = 0;
426  for (const auto& descVol : m_descendantVolumes) {
427  // Attempt to cast to AbstractVolume: only one we'll handle
428  const AbstractVolume* avol =
429  dynamic_cast<const AbstractVolume*>(descVol.get());
430  if (avol != nullptr) {
431  const auto& bndSrf = avol->boundarySurfaces();
432  for (const auto& bnd : bndSrf) {
433  const auto& srf = bnd->surfaceRepresentation();
434  Surface* mutableSurfcePtr = const_cast<Surface*>(&srf);
435  auto geoID = GeometryIdentifier(volumeID).setSensitive(++isurface);
436  mutableSurfcePtr->assignGeometryId(geoID);
437  }
438  }
439  }
440  }
441  } else {
442  // B) this is a container volume, go through sub volume
443  // do the loop
444  for (auto& volumesIter : m_confinedVolumes->arrayObjects()) {
445  auto mutableVolumesIter =
447  mutableVolumesIter->setMotherVolume(this);
448  mutableVolumesIter->closeGeometry(materialDecorator, volumeMap, vol, hook,
449  logger);
450  }
451  }
452 
453  if (!m_confinedDenseVolumes.empty()) {
454  for (auto& volumesIter : m_confinedDenseVolumes) {
455  auto mutableVolumesIter =
457  mutableVolumesIter->setMotherVolume(this);
458  mutableVolumesIter->closeGeometry(materialDecorator, volumeMap, vol, hook,
459  logger);
460  }
461  }
462 }
463 
464 // Returns the boundary surfaces ordered in probability to hit them based on
465 boost::container::small_vector<Acts::BoundaryIntersection, 4>
467  const GeometryContext& gctx, const Vector3& position,
468  const Vector3& direction, const NavigationOptions<Surface>& options,
469  const Logger& logger) const {
470  ACTS_VERBOSE("Finding compatibleBoundaries");
471  // Loop over boundarySurfaces and calculate the intersection
472  auto excludeObject = options.startObject;
473  boost::container::small_vector<Acts::BoundaryIntersection, 4> bIntersections;
474 
475  // The Limits: current, path & overstepping
476  double pLimit = options.pathLimit;
477  double oLimit = 0;
478 
479  // Helper function to test intersection
480  auto checkIntersection =
481  [&](SurfaceMultiIntersection& smIntersection,
482  const BoundarySurface* bSurface) -> BoundaryIntersection {
483  for (const auto& sIntersection : smIntersection.split()) {
484  if (!sIntersection) {
485  continue;
486  }
487 
488  if (options.forceIntersectBoundaries) {
489  const bool coCriterion =
490  std::abs(sIntersection.pathLength()) < std::abs(oLimit);
491  ACTS_VERBOSE("Forcing intersection with surface "
492  << bSurface->surfaceRepresentation().geometryId());
493  if (coCriterion) {
494  ACTS_VERBOSE("Intersection forced successfully ");
495  ACTS_VERBOSE("- intersection path length "
496  << std::abs(sIntersection.pathLength())
497  << " < overstep limit " << std::abs(oLimit));
498  return BoundaryIntersection(sIntersection.intersection(), bSurface,
499  sIntersection.object(),
500  sIntersection.index());
501  }
502  ACTS_VERBOSE("Can't force intersection: ");
503  ACTS_VERBOSE("- intersection path length "
504  << std::abs(sIntersection.pathLength())
505  << " > overstep limit " << std::abs(oLimit));
506  }
507 
508  ACTS_VERBOSE("Check intersection with surface "
509  << bSurface->surfaceRepresentation().geometryId());
510  if (detail::checkIntersection<decltype(sIntersection.intersection()),
511  decltype(logger)>(
512  sIntersection.intersection(), pLimit, oLimit,
514  return BoundaryIntersection(sIntersection.intersection(), bSurface,
515  sIntersection.object(),
516  sIntersection.index());
517  }
518  }
519 
520  ACTS_VERBOSE("No intersection accepted");
522  };
523 
525  auto processBoundaries =
526  [&](const TrackingVolumeBoundaries& bSurfaces) -> void {
527  ACTS_VERBOSE("Processing boundaries");
528  // Loop over the boundary surfaces
529  for (auto& bsIter : bSurfaces) {
530  // Get the boundary surface pointer
531  const auto& bSurfaceRep = bsIter->surfaceRepresentation();
532  ACTS_VERBOSE("Consider boundary surface " << bSurfaceRep.geometryId()
533  << " :\n"
534  << std::tie(bSurfaceRep, gctx));
535 
536  // Exclude the boundary where you are on
537  if (excludeObject != &bSurfaceRep) {
538  auto bCandidate = bSurfaceRep.intersect(gctx, position, direction,
539  options.boundaryCheck);
540  // Intersect and continue
541  auto bIntersection = checkIntersection(bCandidate, bsIter.get());
542  if (bIntersection) {
543  ACTS_VERBOSE(" - Proceed with surface");
544  bIntersections.push_back(bIntersection);
545  } else {
546  ACTS_VERBOSE(" - Surface intersecion invalid");
547  }
548  } else {
549  ACTS_VERBOSE(" - Surface is excluded surface");
550  }
551  }
552  };
553 
554  // Process the boundaries of the current volume
555  auto& bSurfaces = boundarySurfaces();
556  ACTS_VERBOSE("Volume reports " << bSurfaces.size() << " boundary surfaces");
557  processBoundaries(bSurfaces);
558 
559  // Process potential boundaries of contained volumes
560  auto confinedDenseVolumes = denseVolumes();
561  ACTS_VERBOSE("Volume reports " << confinedDenseVolumes.size()
562  << " confined dense volumes");
563  for (const auto& dv : confinedDenseVolumes) {
564  auto& bSurfacesConfined = dv->boundarySurfaces();
565  ACTS_VERBOSE(" -> " << bSurfacesConfined.size() << " boundary surfaces");
566  processBoundaries(bSurfacesConfined);
567  }
568 
569  auto comparator = [](double a, double b) {
570  // sign function would be nice but ...
571  if ((a > 0 && b > 0) || (a < 0 && b < 0)) {
572  return a < b;
573  }
574  if (a > 0) { // b < 0
575  return true;
576  }
577  return false;
578  };
579 
580  std::sort(bIntersections.begin(), bIntersections.end(),
581  [&](const auto& a, const auto& b) {
582  return comparator(a.pathLength(), b.pathLength());
583  });
584  return bIntersections;
585 }
586 
587 boost::container::small_vector<Acts::LayerIntersection, 10>
589  const GeometryContext& gctx, const Vector3& position,
590  const Vector3& direction, const NavigationOptions<Layer>& options) const {
591  // the layer intersections which are valid
592  boost::container::small_vector<Acts::LayerIntersection, 10> lIntersections;
593 
594  // the confinedLayers
595  if (m_confinedLayers != nullptr) {
596  // start layer given or not - test layer
597  const Layer* tLayer = options.startObject != nullptr
598  ? options.startObject
599  : associatedLayer(gctx, position);
600  while (tLayer != nullptr) {
601  // check if the layer needs resolving
602  // - resolveSensitive -> always take layer if it has a surface array
603  // - resolveMaterial -> always take layer if it has material
604  // - resolvePassive -> always take, unless it's a navigation layer
605  // skip the start object
606  if (tLayer != options.startObject && tLayer->resolve(options)) {
607  // if it's a resolveable start layer, you are by definition on it
608  // layer on approach intersection
609  auto atIntersection =
610  tLayer->surfaceOnApproach(gctx, position, direction, options);
611  auto path = atIntersection.pathLength();
612  bool withinLimit = std::abs(path) <= std::abs(options.pathLimit);
613  // Intersection is ok - take it (move to surface on approach)
614  if (atIntersection &&
615  (atIntersection.object() != options.targetSurface) && withinLimit) {
616  // create a layer intersection
617  lIntersections.push_back(LayerIntersection(
618  atIntersection.intersection(), tLayer, atIntersection.object(),
619  atIntersection.index()));
620  }
621  }
622  // move to next one or break because you reached the end layer
623  tLayer = (tLayer == options.endObject)
624  ? nullptr
625  : tLayer->nextLayer(gctx, position, direction);
626  }
627  std::sort(lIntersections.begin(), lIntersections.end(),
629  }
630  // and return
631  return lIntersections;
632 }
633 
634 namespace {
635 template <typename T>
636 std::vector<const Acts::Volume*> intersectSearchHierarchy(
637  const T obj, const Acts::Volume::BoundingBox* lnode) {
638  std::vector<const Acts::Volume*> hits;
639  hits.reserve(20); // arbitrary
640  do {
641  if (lnode->intersect(obj)) {
642  if (lnode->hasEntity()) {
643  // found primitive
644  // check obb to limit false positives
645  const Acts::Volume* vol = lnode->entity();
646  const auto& obb = vol->orientedBoundingBox();
647  if (obb.intersect(obj.transformed(vol->itransform()))) {
648  hits.push_back(vol);
649  }
650  // we skip in any case, whether we actually hit the OBB or not
651  lnode = lnode->getSkip();
652  } else {
653  // go over children
654  lnode = lnode->getLeftChild();
655  }
656  } else {
657  lnode = lnode->getSkip();
658  }
659  } while (lnode != nullptr);
660 
661  return hits;
662 }
663 } // namespace
664 
665 std::vector<Acts::SurfaceIntersection>
667  const GeometryContext& gctx, const Vector3& position,
668  const Vector3& direction, double angle,
669  const NavigationOptions<Surface>& options) const {
670  std::vector<SurfaceIntersection> sIntersections;
671  sIntersections.reserve(20); // arbitrary
672 
673  // The limits for this navigation step
674  double pLimit = options.pathLimit;
675  double oLimit = options.overstepLimit;
676 
677  if (m_bvhTop == nullptr) {
678  return sIntersections;
679  }
680 
681  std::vector<const Volume*> hits;
682  if (angle == 0) {
683  // use ray
684  Ray3D obj(position, direction);
685  hits = intersectSearchHierarchy(std::move(obj), m_bvhTop);
686  } else {
687  Acts::Frustum<ActsScalar, 3, 4> obj(position, direction, angle);
688  hits = intersectSearchHierarchy(std::move(obj), m_bvhTop);
689  }
690 
691  // have cells, decompose to surfaces
692  for (const Volume* vol : hits) {
693  const AbstractVolume* avol = dynamic_cast<const AbstractVolume*>(vol);
694  const std::vector<std::shared_ptr<const BoundarySurfaceT<AbstractVolume>>>&
695  boundarySurfaces = avol->boundarySurfaces();
696  for (const auto& bs : boundarySurfaces) {
697  const Surface& srf = bs->surfaceRepresentation();
698  auto sfmi = srf.intersect(gctx, position, direction, false);
699  for (const auto& sfi : sfmi.split()) {
700  if (sfi and sfi.pathLength() > oLimit and sfi.pathLength() <= pLimit) {
701  sIntersections.push_back(sfi);
702  }
703  }
704  }
705  }
706 
707  // Sort according to the path length
708  std::sort(sIntersections.begin(), sIntersections.end(),
710 
711  return sIntersections;
712 }