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