Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-08-05 08:21:57

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/Navigation/NavigationStream.hpp"
0010 
0011 #include "Acts/Propagator/NavigationTarget.hpp"
0012 #include "Acts/Surfaces/BoundaryTolerance.hpp"
0013 #include "Acts/Surfaces/Surface.hpp"
0014 #include "Acts/Utilities/Enumerate.hpp"
0015 
0016 #include <algorithm>
0017 
0018 namespace Acts {
0019 
0020 bool NavigationStream::initialize(const GeometryContext& gctx,
0021                                   const QueryPoint& queryPoint,
0022                                   const BoundaryTolerance& cTolerance,
0023                                   const double onSurfaceTolerance,
0024                                   const bool candidatesAreUnique) {
0025   // Position and direction from the query point
0026   const Vector3& position = queryPoint.position;
0027   const Vector3& direction = queryPoint.direction;
0028 
0029   // De-duplicate by surface pointer first, so each surface is intersected only
0030   // once in this pass, keeping the first occurrence (in insertion order). This
0031   // reproduces the previous std::stable_sort + std::unique result (first-wins),
0032   // but in place: it avoids the temporary buffer that std::stable_sort
0033   // allocates on every call, which matters on this per-navigation hot path. The
0034   // candidate count per volume is small, so the quadratic scan is cheap — but
0035   // it is skipped entirely when the caller guarantees uniqueness. (Should a
0036   // duplicate slip through regardless, the post-sort unique pass below still
0037   // removes it; only the first-wins tolerance selection is then not enforced.)
0038   if (!candidatesAreUnique) {
0039     std::size_t writeIdx = 0;
0040     for (std::size_t readIdx = 0; readIdx < m_candidates.size(); ++readIdx) {
0041       const Surface* surface = &m_candidates[readIdx].surface();
0042       bool alreadySeen = false;
0043       for (std::size_t k = 0; k < writeIdx; ++k) {
0044         if (&m_candidates[k].surface() == surface) {
0045           alreadySeen = true;
0046           break;
0047         }
0048       }
0049       if (!alreadySeen) {
0050         if (writeIdx != readIdx) {
0051           m_candidates[writeIdx] = m_candidates[readIdx];
0052         }
0053         ++writeIdx;
0054       }
0055     }
0056     m_candidates.erase(m_candidates.begin() + writeIdx, m_candidates.end());
0057   }
0058 
0059   // Collect additional candidates for the second valid intersection. Reuse the
0060   // member scratch buffer to avoid a heap allocation on every call.
0061   std::vector<NavigationTarget>& additionalCandidates = m_additionalCandidates;
0062   additionalCandidates.clear();
0063   for (auto& candidate : m_candidates) {
0064     // Get the surface from the object intersection
0065     const Surface& surface = candidate.surface();
0066     // Intersect the surface
0067     auto multiIntersection = surface.intersect(gctx, position, direction,
0068                                                cTolerance, onSurfaceTolerance);
0069 
0070     bool firstValid = multiIntersection.at(0).isValid();
0071     bool secondValid = multiIntersection.at(1).isValid();
0072     if (firstValid && !secondValid) {
0073       if (multiIntersection.at(0).pathLength() < -onSurfaceTolerance) {
0074         continue;
0075       }
0076       candidate.intersection() = multiIntersection.at(0);
0077       candidate.intersectionIndex() = 0;
0078     } else if (!firstValid && secondValid) {
0079       if (multiIntersection.at(1).pathLength() < -onSurfaceTolerance) {
0080         continue;
0081       }
0082       candidate.intersection() = multiIntersection.at(1);
0083       candidate.intersectionIndex() = 1;
0084     } else {
0085       // Split them into valid intersections, keep track of potentially
0086       // additional candidates
0087       bool originalCandidateUpdated = false;
0088       for (auto [intersectionIndex, intersection] :
0089            enumerate(multiIntersection)) {
0090         // Skip negative solutions, respecting the on surface tolerance
0091         if (intersection.pathLength() < -onSurfaceTolerance) {
0092           continue;
0093         }
0094         // Valid solution is either on surface or updates the distance
0095         if (intersection.isValid()) {
0096           if (!originalCandidateUpdated) {
0097             candidate.intersection() = intersection;
0098             candidate.intersectionIndex() = intersectionIndex;
0099             originalCandidateUpdated = true;
0100           } else {
0101             NavigationTarget additionalCandidate = candidate;
0102             additionalCandidate.intersection() = intersection;
0103             additionalCandidate.intersectionIndex() = intersectionIndex;
0104             additionalCandidates.emplace_back(additionalCandidate);
0105           }
0106         }
0107       }
0108     }
0109   }
0110 
0111   // Append the multi intersection candidates
0112   m_candidates.insert(m_candidates.end(), additionalCandidates.begin(),
0113                       additionalCandidates.end());
0114 
0115   // Sort the candidates by path length
0116   std::ranges::sort(m_candidates, NavigationTarget::pathLengthOrder);
0117 
0118   // If we have duplicates, we expect them to be close by in path length, so we
0119   // don't need to re-sort Remove duplicates on basis of the surface pointer
0120 
0121   /// But but but... What about the surfaces with multiple intersections?
0122   auto nonUniqueRange = std::ranges::unique(
0123       m_candidates.begin(), m_candidates.end(),
0124       [](const NavigationTarget& a, const NavigationTarget& b) {
0125         return &a.surface() == &b.surface();
0126       });
0127   m_candidates.erase(nonUniqueRange.begin(), nonUniqueRange.end());
0128 
0129   // The we find the first invalid candidate
0130   auto firstInvalid = std::ranges::find_if(
0131       m_candidates,
0132       [](const NavigationTarget& a) { return !a.intersection().isValid(); });
0133 
0134   // Set the range and initialize
0135   m_candidates.resize(std::distance(m_candidates.begin(), firstInvalid),
0136                       NavigationTarget::None());
0137 
0138   m_currentIndex = 0;
0139   if (m_candidates.empty()) {
0140     return false;
0141   }
0142   return true;
0143 }
0144 
0145 bool NavigationStream::update(const GeometryContext& gctx,
0146                               const QueryPoint& queryPoint,
0147                               double onSurfaceTolerance) {
0148   // Loop over the (currently valid) candidates and update
0149   for (; m_currentIndex < m_candidates.size(); ++m_currentIndex) {
0150     // Get the candidate, and resolve the tuple
0151     NavigationTarget& candidate = currentCandidate();
0152     // Get the surface from the object intersection
0153     const Surface& surface = candidate.surface();
0154     // (re-)Intersect the surface
0155     auto multiIntersection =
0156         surface.intersect(gctx, queryPoint.position, queryPoint.direction,
0157                           candidate.boundaryTolerance(), onSurfaceTolerance);
0158     // Split them into valid intersections
0159     for (auto [intersectionIndex, intersection] :
0160          enumerate(multiIntersection)) {
0161       // Skip wrong index solution
0162       if (intersectionIndex != candidate.intersectionIndex()) {
0163         continue;
0164       }
0165       // Valid solution is either on surface or updates the distance
0166       if (intersection.isValid()) {
0167         candidate.intersection() = intersection;
0168         return true;
0169       }
0170     }
0171   }
0172   // No candidate was reachable
0173   return false;
0174 }
0175 
0176 void NavigationStream::reset() {
0177   m_candidates.clear();
0178   m_currentIndex = 0;
0179 }
0180 
0181 void NavigationStream::addSurfaceCandidate(
0182     const Surface& surface, const BoundaryTolerance& bTolerance) {
0183   m_candidates.emplace_back(Intersection3D::Invalid(), 0, surface, bTolerance);
0184 }
0185 
0186 void NavigationStream::addSurfaceCandidates(
0187     std::span<const Surface*> surfaces, const BoundaryTolerance& bTolerance) {
0188   m_candidates.reserve(m_candidates.size() + surfaces.size());
0189   std::ranges::for_each(surfaces, [&](const Surface* surface) {
0190     m_candidates.emplace_back(Intersection3D::Invalid(), 0, *surface,
0191                               bTolerance);
0192   });
0193 }
0194 
0195 void NavigationStream::addPortalCandidate(const Portal& portal) {
0196   m_candidates.emplace_back(Intersection3D::Invalid(), 0, portal,
0197                             BoundaryTolerance::None());
0198 }
0199 
0200 AppendOnlyNavigationStream::AppendOnlyNavigationStream(NavigationStream& stream)
0201     : m_stream{&stream} {}
0202 
0203 void AppendOnlyNavigationStream::addPortalCandidate(const Portal& portal) {
0204   m_stream->addPortalCandidate(portal);
0205 }
0206 
0207 void AppendOnlyNavigationStream::addSurfaceCandidate(
0208     const Surface& surface, const BoundaryTolerance& bTolerance) {
0209   m_stream->addSurfaceCandidate(surface, bTolerance);
0210 }
0211 
0212 }  // namespace Acts