From 2f7a5e7cfff7259d09129be007276208066e4ee5 Mon Sep 17 00:00:00 2001 From: Andreas Salzburger Date: Wed, 21 Aug 2024 16:39:44 +0200 Subject: [PATCH 01/13] introduce navigation stream --- Core/CMakeLists.txt | 1 + .../Acts/Navigation/NavigationStream.hpp | 79 +++++++++++++ .../Navigation/NavigationStreamHelper.hpp | 109 ++++++++++++++++++ Core/include/Acts/Utilities/Intersection.hpp | 21 +++- Core/src/Navigation/CMakeLists.txt | 5 + .../src/Navigation/NavigationStreamHelper.cpp | 90 +++++++++++++++ .../UnitTests/Core/Navigation/CMakeLists.txt | 1 + .../Core/Navigation/NavigationStreamTests.cpp | 88 ++++++++++++++ 8 files changed, 393 insertions(+), 1 deletion(-) create mode 100644 Core/include/Acts/Navigation/NavigationStream.hpp create mode 100644 Core/include/Acts/Navigation/NavigationStreamHelper.hpp create mode 100644 Core/src/Navigation/CMakeLists.txt create mode 100644 Core/src/Navigation/NavigationStreamHelper.cpp create mode 100644 Tests/UnitTests/Core/Navigation/NavigationStreamTests.cpp diff --git a/Core/CMakeLists.txt b/Core/CMakeLists.txt index b760ba576ed..eeb6bc8b92a 100644 --- a/Core/CMakeLists.txt +++ b/Core/CMakeLists.txt @@ -108,6 +108,7 @@ add_subdirectory(src/Detector) add_subdirectory(src/Geometry) add_subdirectory(src/MagneticField) add_subdirectory(src/Material) +add_subdirectory(src/Navigation) add_subdirectory(src/Propagator) add_subdirectory(src/Surfaces) add_subdirectory(src/TrackFinding) diff --git a/Core/include/Acts/Navigation/NavigationStream.hpp b/Core/include/Acts/Navigation/NavigationStream.hpp new file mode 100644 index 00000000000..e0b995e42f8 --- /dev/null +++ b/Core/include/Acts/Navigation/NavigationStream.hpp @@ -0,0 +1,79 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2024 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#pragma once + +#include "Acts/Surfaces/BoundaryTolerance.hpp" +#include "Acts/Utilities/Intersection.hpp" + +#include +#include +#include + +namespace Acts { + +// To be removed when the namespace Experimental is omitted +namespace Experimental { + class Portal; +} +using namespace Experimental; + +class Surface; + +/// @brief The NavigationStream is a container for the navigation candidates +/// +/// The current candidates are stored in a vector with a range defined +/// by a pair of indices. This implementation allows passed or unreachable +/// candidates to be shaddowed without removing them from the container. +/// +/// A surface proximity parameter can be used to chose which sort of intersection +/// path length update is needed. +struct NavigationStream { + /// This is a candidate type for a Surface intersection + using SurfaceIntersection = ObjectIntersection; + + /// This is a candidate object of the navigation stream + /// a Surface intersection + /// a Portal : set if the surface represents a portal + /// a BoundaryTolerance : the boundary tolerance used for the intersection + struct Candidate { + /// The intersection + SurfaceIntersection intersection = SurfaceIntersection::invalid(); + /// The portal + const Portal* portal = nullptr; + /// The boundary tolerance + BoundaryTolerance bTolerance = BoundaryTolerance::None(); + /// Convenience access to surface + const Surface& surface() const { + return *intersection.object(); + } + }; + + /// The candidates for the navigation + std::vector candidates; + + /// The currently active candidate range + size_t currentIndex = 0u; + + /// @brief Const-access the current candidate + const Candidate& currentCandidate() const { + return candidates.at(currentIndex); + } + + /// @brief Noncost-access the current candidate + Candidate& currentCandidate() { + return candidates.at(currentIndex); + } + + /// @brief Access the current candidate + std::size_t activeCandidates() const { + return (candidates.size() - currentIndex); + } +}; + +} // namespace Acts diff --git a/Core/include/Acts/Navigation/NavigationStreamHelper.hpp b/Core/include/Acts/Navigation/NavigationStreamHelper.hpp new file mode 100644 index 00000000000..3be1328de0d --- /dev/null +++ b/Core/include/Acts/Navigation/NavigationStreamHelper.hpp @@ -0,0 +1,109 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2024 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#pragma once + +#include "Acts/Definitions/Algebra.hpp" +#include "Acts/Detector/Portal.hpp" +#include "Acts/Geometry/GeometryContext.hpp" +#include "Acts/Navigation/NavigationStream.hpp" +#include "Acts/Surfaces/BoundaryTolerance.hpp" +#include "Acts/Utilities/Intersection.hpp" + +#include + +namespace Acts { + +using namespace Experimental; + +class Surface; + +/// Fillers and attachers for surfaces to act on the navigation state +class NavigationStreamHelper { + public: + /// Helper struct that allows to fill surfaces into the candidate vector it + /// allows to use common navigation structs for volume, portal, surfaces + /// + /// @param nStream the navigation stream that is being filled + /// @param surfaces the surfaces that are filled in + /// @param bTolerance the boundary tolerance used for the intersection + inline static void fillSurfaces(NavigationStream& nStream, + const std::vector& surfaces, + BoundaryTolerance bTolerance) { + std::for_each(surfaces.begin(), surfaces.end(), [&](const auto& s) { + nStream.candidates.push_back(NavigationStream::Candidate{ + ObjectIntersection(s), nullptr, bTolerance}); + }); + } + + /// Helper struct that allows to fill surfaces into the candidate vector it + /// allows to use common navigation structs for volume, portal, surfaces + /// + /// @param nStream the navigation stream that is being filled + /// @param portals the portals that are filled in + inline static void fillPortals(NavigationStream& nStream, + const std::vector& portals) { + std::for_each(portals.begin(), portals.end(), [&](const auto& p) { + nStream.candidates.push_back(NavigationStream::Candidate{ + ObjectIntersection(&(p->surface())), p, + BoundaryTolerance::None()}); + }); + } + + /// Initialize a stream that does not require a state object + /// + /// @param stream is the navigation stream to be updated + /// @param gctx is the geometry context + /// @param position is the current position + /// @param direction is the current direction + /// @param cTolerance is the candidate search tolerance + /// + /// @return true if the stream is active, false indicates that there are no valid candidates + inline static bool initializeStream(NavigationStream& stream, + const GeometryContext& gctx, + const Vector3& position, + const Vector3& direction, + BoundaryTolerance cTolerance) { + return processStream(stream, gctx, position, direction, true, cTolerance); + } + + /// Convenience method to upsate a stream from a new position, + /// this could be called from navigation delegates that do not require + /// a local state or from the navigator on the target stream + /// + /// @param stream is the navigation stream to be updated + /// @param gctx is the geometry context + /// @param position is the current position + /// @param direction is the current direction + /// + /// @return true if the stream is active, false indicate no valid candidates left + inline static bool updateStream(NavigationStream& stream, + const GeometryContext& gctx, + const Vector3& position, + const Vector3& direction) { + return processStream(stream, gctx, position, direction, false, + BoundaryTolerance::None()); + } + + private: + /// Private helper method that serves intialization and update for + /// + /// @param stream is the navigation stream to be updated + /// @param gctx is the geometry context + /// @param position is the current position + /// @param direction is the current direction + /// @param initial is the flag for the initial update + /// @param cTolerance is the candidate search tolerance (used only for initial updates) + static bool processStream(NavigationStream& stream, + const GeometryContext& gctx, + const Vector3& position, const Vector3& direction, + bool initial, BoundaryTolerance cTolerance); + +}; // namespace NavigationStreamHelper + +} // namespace Acts diff --git a/Core/include/Acts/Utilities/Intersection.hpp b/Core/include/Acts/Utilities/Intersection.hpp index 49dc23517b7..b6f7e7cd43d 100644 --- a/Core/include/Acts/Utilities/Intersection.hpp +++ b/Core/include/Acts/Utilities/Intersection.hpp @@ -69,12 +69,16 @@ class Intersection { /// Returns whether the intersection was successful or not constexpr bool isValid() const { return m_status != Status::missed; } + /// Returns the position of the interseciton constexpr const Position& position() const { return m_position; } + /// Returns the path length to the interseciton constexpr ActsScalar pathLength() const { return m_pathLength; } + /// Returns the intersection status enum constexpr Status status() const { return m_status; } + /// Static factory to creae an invalid instesection constexpr static Intersection invalid() { return Intersection(); } /// Comparison function for path length order i.e. intersection closest to @@ -145,6 +149,14 @@ class ObjectIntersection { const object_t* object, std::uint8_t index = 0) : m_intersection(intersection), m_object(object), m_index(index) {} + /// Invalid object intersection - only holding the object itself + /// + /// @param object is the object to be instersected + constexpr ObjectIntersection(const object_t* object) + : m_intersection(Intersection3D::invalid()), + m_object(object), + m_index(0) {} + /// Returns whether the intersection was successful or not /// @deprecated [[deprecated("Use isValid() instead")]] constexpr explicit operator bool() @@ -155,27 +167,34 @@ class ObjectIntersection { /// Returns whether the intersection was successful or not constexpr bool isValid() const { return m_intersection.isValid(); } + /// Returns the intersection constexpr const Intersection3D& intersection() const { return m_intersection; } + /// Returns the position of the interseciton constexpr const Intersection3D::Position& position() const { return m_intersection.position(); } + /// Returns the path length to the interseciton constexpr ActsScalar pathLength() const { return m_intersection.pathLength(); } + /// Returns the status of the interseciton constexpr Intersection3D::Status status() const { return m_intersection.status(); } + /// Returns the object that has been intersected constexpr const object_t* object() const { return m_object; } constexpr std::uint8_t index() const { return m_index; } - constexpr static ObjectIntersection invalid() { return ObjectIntersection(); } + constexpr static ObjectIntersection invalid() { + return ObjectIntersection(); + } constexpr static bool pathLengthOrder( const ObjectIntersection& aIntersection, diff --git a/Core/src/Navigation/CMakeLists.txt b/Core/src/Navigation/CMakeLists.txt new file mode 100644 index 00000000000..d9093d32a75 --- /dev/null +++ b/Core/src/Navigation/CMakeLists.txt @@ -0,0 +1,5 @@ +target_sources( + ActsCore + PRIVATE + NavigationStreamHelper.cpp +) diff --git a/Core/src/Navigation/NavigationStreamHelper.cpp b/Core/src/Navigation/NavigationStreamHelper.cpp new file mode 100644 index 00000000000..d32348dbb16 --- /dev/null +++ b/Core/src/Navigation/NavigationStreamHelper.cpp @@ -0,0 +1,90 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2024 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include "Acts/Navigation/NavigationStreamHelper.hpp" + +#include "Acts/Surfaces/Surface.hpp" + +bool Acts::NavigationStreamHelper::processStream(NavigationStream& stream, + const GeometryContext& gctx, + const Vector3& position, + const Vector3& direction, + bool initial, + BoundaryTolerance cTolerance) { + // Loop over the (currently valid) candidates + // + // Except for the initial update, the loop is stopped at the first reachable + // surface. + // + for (size_t& index = stream.currentIndex; index < stream.candidates.size(); ++index) { + + std::cout << "Processing candidate " << index << std::endl; + // Get the candidate, and resolve the tuple + NavigationStream::Candidate& candidate = stream.currentCandidate(); + auto& [sIntersection, portal, bTolerance] = candidate; + // Take the candidate Tolerance in case it is an intial update and not a portal + const BoundaryTolerance& tolerance = (portal || !initial) ? bTolerance : cTolerance; + + // Get the surface from the object intersection + const Surface* surface = sIntersection.object(); + // (re-)Intersect the surface + auto multiIntersection = surface->intersect( + gctx, position, direction, tolerance, s_onSurfaceTolerance); + + // Split them into valid intersections + for (auto& rsIntersection : multiIntersection.split()) { + // Skip negative solutions for inital update, overstepping can not happen + if (initial && rsIntersection.pathLength() < 0.) { + continue; + } + + // Skip wrong index solution for non-inital updates + if (!initial && rsIntersection.index() != sIntersection.index()) { + continue; + } + + // Valid solution is either on surface or updates the distance + if (rsIntersection.isValid()) { + std::cout << " is valid " << std::endl; + // Valid intersection, we assume ordering, update + sIntersection = rsIntersection; + if (!initial) { + // We are done with the current candidate + return true; + } + } + } + } + + // In the initial update case, we need to sort and estimate the range + std::sort( + stream.candidates.begin(), stream.candidates.end(), + [](const NavigationStream::Candidate& a, + const NavigationStream::Candidate& b) { + const auto& [aIntersection, aPortal, aTolerance] = a; + const auto& [bIntersection, bPortal, bTolerance] = b; + return (aIntersection.pathLength() < bIntersection.pathLength()) && + aIntersection.isValid(); + }); + + // The we find the first invalid candidate + auto firstInvalid = + std::find_if(stream.candidates.begin(), stream.candidates.end(), + [](const NavigationStream::Candidate& a) { + const auto& [aIntersection, aPortal, aTolerance] = a; + return !aIntersection.isValid(); + }); + + // Set the range and initialize + stream.candidates.resize(std::distance(stream.candidates.begin(), firstInvalid)); + if (stream.candidates.empty()) { + return false; + } + stream.currentIndex = 0; + return true; +} diff --git a/Tests/UnitTests/Core/Navigation/CMakeLists.txt b/Tests/UnitTests/Core/Navigation/CMakeLists.txt index 69ebc582396..edca953321d 100644 --- a/Tests/UnitTests/Core/Navigation/CMakeLists.txt +++ b/Tests/UnitTests/Core/Navigation/CMakeLists.txt @@ -1,6 +1,7 @@ add_unittest(PortalNavigation PortalNavigationTests.cpp) add_unittest(DetectorVolumeFinders DetectorVolumeFindersTests.cpp) add_unittest(NavigationState NavigationStateTests.cpp) +add_unittest(NavigationStream NavigationStreamTests.cpp) add_unittest(NavigationStateUpdaters NavigationStateUpdatersTests.cpp) add_unittest(DetectorNavigator DetectorNavigatorTests.cpp) add_unittest(MultiWireNavigation MultiWireNavigationTests.cpp) diff --git a/Tests/UnitTests/Core/Navigation/NavigationStreamTests.cpp b/Tests/UnitTests/Core/Navigation/NavigationStreamTests.cpp new file mode 100644 index 00000000000..a514e803e0b --- /dev/null +++ b/Tests/UnitTests/Core/Navigation/NavigationStreamTests.cpp @@ -0,0 +1,88 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2024 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include + +#include "Acts/Navigation/NavigationStream.hpp" +#include "Acts/Navigation/NavigationStreamHelper.hpp" +#include "Acts/Surfaces/PlaneSurface.hpp" +#include "Acts/Surfaces/RectangleBounds.hpp" + +namespace { + +std::vector> createPlaneSurfaces() { + auto dTransform = Acts::Transform3::Identity(); + auto rectangle = std::make_shared(10., 10.); + // This surface should not be reachable from (0.,0.,0.) position along z + auto surfaceA = Acts::Surface::makeShared( + dTransform.pretranslate(Acts::Vector3(50., 50., -20.)), rectangle); + // This surface should not be reachable from (0.,0.,0.) position along z with + // boundary check + auto surfaceB = Acts::Surface::makeShared( + dTransform.pretranslate(Acts::Vector3(0., 0., 100.)), rectangle); + auto surfaceC = Acts::Surface::makeShared( + dTransform.pretranslate(Acts::Vector3(0., 0., 200.)), rectangle); + auto surfaceD = Acts::Surface::makeShared( + dTransform.pretranslate(Acts::Vector3(0., 0., 400.)), rectangle); + + // Let's fill them shuffled + return {surfaceC, surfaceA, surfaceD, surfaceB}; +} + +} // namespace + +using namespace Acts; + +auto gContext = GeometryContext(); + +BOOST_AUTO_TEST_SUITE(Navigation) + +BOOST_AUTO_TEST_CASE(NavigationStream_InitializePlanes) { + // Create the punch of surfaces + auto surfaces = createPlaneSurfaces(); + + NavigationStream nStreamTemplate; + for (const auto& surface : surfaces) { + NavigationStreamHelper::fillSurfaces(nStreamTemplate, {surface.get()}, + Acts::BoundaryTolerance::None()); + } + BOOST_CHECK_EQUAL(nStreamTemplate.candidates.size(), 4); + + // (1) Run an initial update + // - from a position where all are reachable and valid + // - with infinite boundary tolerance + NavigationStream nStream = nStreamTemplate; + BOOST_CHECK(NavigationStreamHelper::initializeStream( + nStream, gContext, Acts::Vector3(0., 0., -30.), Acts::Vector3(0., 0., 1.), + BoundaryTolerance::Infinite())); + + BOOST_CHECK_EQUAL(nStream.activeCandidates(), 4u); + BOOST_CHECK_EQUAL(&nStream.currentCandidate().surface(), surfaces[1u].get()); + + // (2) Run an initial update + // - from a position where all but one are reachable + // - with infinite boundary tolerance + nStream = nStreamTemplate; + BOOST_CHECK(NavigationStreamHelper::initializeStream( + nStream, gContext, Acts::Vector3(0., 0., 0.), Acts::Vector3(0., 0., 1.), + BoundaryTolerance::Infinite())); + BOOST_CHECK_EQUAL(nStream.activeCandidates(), 3u); + BOOST_CHECK_EQUAL(&nStream.currentCandidate().surface(), surfaces[3u].get()); + + // (3) Run an initial update + // - from a position where all would be reachable, but + // - with no boundary tolerance + nStream = nStreamTemplate; + BOOST_CHECK(NavigationStreamHelper::initializeStream( + nStream, gContext, Acts::Vector3(0., 0., -100.), Acts::Vector3(0., 0., 1.), + BoundaryTolerance::None())); + BOOST_CHECK_EQUAL(nStream.activeCandidates(), 3u); + +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file From 20f37ef52da4283e47de94c62d5bd9a4cecd1e90 Mon Sep 17 00:00:00 2001 From: Andreas Salzburger Date: Wed, 21 Aug 2024 16:52:04 +0200 Subject: [PATCH 02/13] first set of Planar unit tets --- .../src/Navigation/NavigationStreamHelper.cpp | 5 +-- .../Core/Navigation/NavigationStreamTests.cpp | 45 ++++++++++++++----- 2 files changed, 34 insertions(+), 16 deletions(-) diff --git a/Core/src/Navigation/NavigationStreamHelper.cpp b/Core/src/Navigation/NavigationStreamHelper.cpp index d32348dbb16..63e398cfaaa 100644 --- a/Core/src/Navigation/NavigationStreamHelper.cpp +++ b/Core/src/Navigation/NavigationStreamHelper.cpp @@ -22,8 +22,6 @@ bool Acts::NavigationStreamHelper::processStream(NavigationStream& stream, // surface. // for (size_t& index = stream.currentIndex; index < stream.candidates.size(); ++index) { - - std::cout << "Processing candidate " << index << std::endl; // Get the candidate, and resolve the tuple NavigationStream::Candidate& candidate = stream.currentCandidate(); auto& [sIntersection, portal, bTolerance] = candidate; @@ -50,7 +48,6 @@ bool Acts::NavigationStreamHelper::processStream(NavigationStream& stream, // Valid solution is either on surface or updates the distance if (rsIntersection.isValid()) { - std::cout << " is valid " << std::endl; // Valid intersection, we assume ordering, update sIntersection = rsIntersection; if (!initial) { @@ -82,9 +79,9 @@ bool Acts::NavigationStreamHelper::processStream(NavigationStream& stream, // Set the range and initialize stream.candidates.resize(std::distance(stream.candidates.begin(), firstInvalid)); + stream.currentIndex = 0; if (stream.candidates.empty()) { return false; } - stream.currentIndex = 0; return true; } diff --git a/Tests/UnitTests/Core/Navigation/NavigationStreamTests.cpp b/Tests/UnitTests/Core/Navigation/NavigationStreamTests.cpp index a514e803e0b..67b47484ac1 100644 --- a/Tests/UnitTests/Core/Navigation/NavigationStreamTests.cpp +++ b/Tests/UnitTests/Core/Navigation/NavigationStreamTests.cpp @@ -16,19 +16,30 @@ namespace { std::vector> createPlaneSurfaces() { - auto dTransform = Acts::Transform3::Identity(); auto rectangle = std::make_shared(10., 10.); + // Surface A: // This surface should not be reachable from (0.,0.,0.) position along z - auto surfaceA = Acts::Surface::makeShared( - dTransform.pretranslate(Acts::Vector3(50., 50., -20.)), rectangle); + Acts::Transform3 aTransform = Acts::Transform3::Identity(); + aTransform.pretranslate(Acts::Vector3(0., 0., -20.)); + auto surfaceA = + Acts::Surface::makeShared(aTransform, rectangle); + // Surface B: // This surface should not be reachable from (0.,0.,0.) position along z with // boundary check - auto surfaceB = Acts::Surface::makeShared( - dTransform.pretranslate(Acts::Vector3(0., 0., 100.)), rectangle); - auto surfaceC = Acts::Surface::makeShared( - dTransform.pretranslate(Acts::Vector3(0., 0., 200.)), rectangle); - auto surfaceD = Acts::Surface::makeShared( - dTransform.pretranslate(Acts::Vector3(0., 0., 400.)), rectangle); + Acts::Transform3 bTransform = Acts::Transform3::Identity(); + bTransform.pretranslate(Acts::Vector3(50., 50., 100.)); + auto surfaceB = + Acts::Surface::makeShared(bTransform, rectangle); + // Surface C: + Acts::Transform3 cTransform = Acts::Transform3::Identity(); + cTransform.pretranslate(Acts::Vector3(0., 0., 200.)); + auto surfaceC = + Acts::Surface::makeShared(cTransform, rectangle); + // Surface D: + Acts::Transform3 dTransform = Acts::Transform3::Identity(); + dTransform.pretranslate(Acts::Vector3(0., 0., 400.)); + auto surfaceD = + Acts::Surface::makeShared(dTransform, rectangle); // Let's fill them shuffled return {surfaceC, surfaceA, surfaceD, surfaceB}; @@ -75,14 +86,24 @@ BOOST_AUTO_TEST_CASE(NavigationStream_InitializePlanes) { BOOST_CHECK_EQUAL(&nStream.currentCandidate().surface(), surfaces[3u].get()); // (3) Run an initial update - // - from a position where all would be reachable, but + // - from a position where all would be reachable, but // - with no boundary tolerance nStream = nStreamTemplate; BOOST_CHECK(NavigationStreamHelper::initializeStream( - nStream, gContext, Acts::Vector3(0., 0., -100.), Acts::Vector3(0., 0., 1.), - BoundaryTolerance::None())); + nStream, gContext, Acts::Vector3(0., 0., -100.), + Acts::Vector3(0., 0., 1.), BoundaryTolerance::None())); BOOST_CHECK_EQUAL(nStream.activeCandidates(), 3u); + // (4) Run an initial update + // - none of the surfaces should be reachable + nStream = nStreamTemplate; + BOOST_CHECK(!NavigationStreamHelper::initializeStream( + nStream, gContext, Acts::Vector3(0., 0., 0.), Acts::Vector3(1., 0., 0.), + BoundaryTolerance::Infinite())); + BOOST_CHECK_EQUAL(nStream.activeCandidates(), 0u); + BOOST_CHECK_THROW(nStream.currentCandidate(), std::out_of_range); + + } BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file From 906fa787792f3bad7c5b8f0cbf79892122c05a84 Mon Sep 17 00:00:00 2001 From: Andreas Salzburger Date: Thu, 22 Aug 2024 13:02:51 +0200 Subject: [PATCH 03/13] initialize and update tests --- .../Acts/Navigation/NavigationStream.hpp | 56 ++++-- .../Navigation/NavigationStreamHelper.hpp | 133 ++++++------ Core/include/Acts/Utilities/Intersection.hpp | 4 +- Core/src/Navigation/CMakeLists.txt | 6 +- .../src/Navigation/NavigationStreamHelper.cpp | 128 ++++++++---- .../Core/Navigation/NavigationStreamTests.cpp | 190 +++++++++++++++++- 6 files changed, 365 insertions(+), 152 deletions(-) diff --git a/Core/include/Acts/Navigation/NavigationStream.hpp b/Core/include/Acts/Navigation/NavigationStream.hpp index e0b995e42f8..4cef69b6c1c 100644 --- a/Core/include/Acts/Navigation/NavigationStream.hpp +++ b/Core/include/Acts/Navigation/NavigationStream.hpp @@ -19,7 +19,7 @@ namespace Acts { // To be removed when the namespace Experimental is omitted namespace Experimental { - class Portal; +class Portal; } using namespace Experimental; @@ -31,9 +31,20 @@ class Surface; /// by a pair of indices. This implementation allows passed or unreachable /// candidates to be shaddowed without removing them from the container. /// -/// A surface proximity parameter can be used to chose which sort of intersection -/// path length update is needed. +/// @todo the NavigationStream should hold also the current volume it is in +/// if it represents the geometry stream. +/// +/// A surface proximity parameter can be used to chose which sort of +/// intersection path length update is needed. struct NavigationStream { + /// @brief the Query ppoint for updating the navigation stream + struct QueryPoint { + /// The position of the query point + Vector3 position = Vector3::Zero(); + /// The direction of the query point + Vector3 direction = Vector3::Zero(); + }; + /// This is a candidate type for a Surface intersection using SurfaceIntersection = ObjectIntersection; @@ -41,17 +52,17 @@ struct NavigationStream { /// a Surface intersection /// a Portal : set if the surface represents a portal /// a BoundaryTolerance : the boundary tolerance used for the intersection - struct Candidate { - /// The intersection - SurfaceIntersection intersection = SurfaceIntersection::invalid(); - /// The portal - const Portal* portal = nullptr; - /// The boundary tolerance - BoundaryTolerance bTolerance = BoundaryTolerance::None(); - /// Convenience access to surface - const Surface& surface() const { - return *intersection.object(); - } + struct Candidate { + /// The intersection + SurfaceIntersection intersection = SurfaceIntersection::invalid(); + /// The portal + const Portal* portal = nullptr; + /// The boundary tolerance + BoundaryTolerance bTolerance = BoundaryTolerance::None(); + /// Convenience access to surface + const Surface& surface() const { return *intersection.object(); } + /// Cinvencience access to the path length + ActsScalar pathLength() const { return intersection.pathLength(); } }; /// The candidates for the navigation @@ -60,15 +71,24 @@ struct NavigationStream { /// The currently active candidate range size_t currentIndex = 0u; + /// Progress to next next candidate + /// + /// @return true if a next candidate is available + bool switchToNextCandidate() { + if (currentIndex < candidates.size()) { + ++currentIndex; + return true; + } + return false; + } + /// @brief Const-access the current candidate const Candidate& currentCandidate() const { return candidates.at(currentIndex); } - + /// @brief Noncost-access the current candidate - Candidate& currentCandidate() { - return candidates.at(currentIndex); - } + Candidate& currentCandidate() { return candidates.at(currentIndex); } /// @brief Access the current candidate std::size_t activeCandidates() const { diff --git a/Core/include/Acts/Navigation/NavigationStreamHelper.hpp b/Core/include/Acts/Navigation/NavigationStreamHelper.hpp index 3be1328de0d..c3bf0227b0f 100644 --- a/Core/include/Acts/Navigation/NavigationStreamHelper.hpp +++ b/Core/include/Acts/Navigation/NavigationStreamHelper.hpp @@ -24,86 +24,65 @@ using namespace Experimental; class Surface; /// Fillers and attachers for surfaces to act on the navigation state -class NavigationStreamHelper { - public: - /// Helper struct that allows to fill surfaces into the candidate vector it - /// allows to use common navigation structs for volume, portal, surfaces - /// - /// @param nStream the navigation stream that is being filled - /// @param surfaces the surfaces that are filled in - /// @param bTolerance the boundary tolerance used for the intersection - inline static void fillSurfaces(NavigationStream& nStream, - const std::vector& surfaces, - BoundaryTolerance bTolerance) { - std::for_each(surfaces.begin(), surfaces.end(), [&](const auto& s) { - nStream.candidates.push_back(NavigationStream::Candidate{ - ObjectIntersection(s), nullptr, bTolerance}); - }); - } +namespace NavigationStreamHelper { +/// Helper struct that allows to fill surfaces into the candidate vector it +/// allows to use common navigation structs for volume, portal, surfaces +/// +/// @param nStream the navigation stream that is being filled +/// @param surfaces the surfaces that are filled in +/// @param bTolerance the boundary tolerance used for the intersection +inline static void fillSurfaces(NavigationStream& nStream, + const std::vector& surfaces, + BoundaryTolerance bTolerance) { + std::for_each(surfaces.begin(), surfaces.end(), [&](const auto& s) { + nStream.candidates.push_back(NavigationStream::Candidate{ + ObjectIntersection(s), nullptr, bTolerance}); + }); +} - /// Helper struct that allows to fill surfaces into the candidate vector it - /// allows to use common navigation structs for volume, portal, surfaces - /// - /// @param nStream the navigation stream that is being filled - /// @param portals the portals that are filled in - inline static void fillPortals(NavigationStream& nStream, - const std::vector& portals) { - std::for_each(portals.begin(), portals.end(), [&](const auto& p) { - nStream.candidates.push_back(NavigationStream::Candidate{ - ObjectIntersection(&(p->surface())), p, - BoundaryTolerance::None()}); - }); - } +/// Helper struct that allows to fill surfaces into the candidate vector it +/// allows to use common navigation structs for volume, portal, surfaces +/// +/// @param nStream the navigation stream that is being filled +/// @param portals the portals that are filled in +inline static void fillPortals(NavigationStream& nStream, + const std::vector& portals) { + std::for_each(portals.begin(), portals.end(), [&](const auto& p) { + nStream.candidates.push_back(NavigationStream::Candidate{ + ObjectIntersection(&(p->surface())), p, + BoundaryTolerance::None()}); + }); +} - /// Initialize a stream that does not require a state object - /// - /// @param stream is the navigation stream to be updated - /// @param gctx is the geometry context - /// @param position is the current position - /// @param direction is the current direction - /// @param cTolerance is the candidate search tolerance - /// - /// @return true if the stream is active, false indicates that there are no valid candidates - inline static bool initializeStream(NavigationStream& stream, - const GeometryContext& gctx, - const Vector3& position, - const Vector3& direction, - BoundaryTolerance cTolerance) { - return processStream(stream, gctx, position, direction, true, cTolerance); - } +/// Initialize a stream that does not require a state object +/// +/// @param stream [in, out] is the navigation stream to be updated +/// @param gctx is the geometry context +/// @param queryPoint holds current position, direction, etc. +/// @param cTolerance is the candidate search tolerance +/// +/// This method will first de-duplicate the candidates on basis of the surface +/// pointer to make sure that the multi-intersections are handled correctly. +/// This will allow intializeStream() to be called even as a re-initialization +/// and still work correctly with at one time valid candidates. +/// +/// @return true if the stream is active, false indicates that there are no valid candidates +bool initializeStream(NavigationStream& stream, const GeometryContext& gctx, + const NavigationStream::QueryPoint& queryPoint, + BoundaryTolerance cTolerance); - /// Convenience method to upsate a stream from a new position, - /// this could be called from navigation delegates that do not require - /// a local state or from the navigator on the target stream - /// - /// @param stream is the navigation stream to be updated - /// @param gctx is the geometry context - /// @param position is the current position - /// @param direction is the current direction - /// - /// @return true if the stream is active, false indicate no valid candidates left - inline static bool updateStream(NavigationStream& stream, - const GeometryContext& gctx, - const Vector3& position, - const Vector3& direction) { - return processStream(stream, gctx, position, direction, false, - BoundaryTolerance::None()); - } +/// Convenience method to upsate a stream from a new position, +/// this could be called from navigation delegates that do not require +/// a local state or from the navigator on the target stream +/// +/// @param stream [in, out] is the navigation stream to be updated +/// @param gctx is the geometry context +/// @param queryPoint holds current position, direction, etc. +/// +/// @return true if the stream is active, false indicate no valid candidates left +bool updateStream(NavigationStream& stream, const GeometryContext& gctx, + const NavigationStream::QueryPoint& queryPoint); - private: - /// Private helper method that serves intialization and update for - /// - /// @param stream is the navigation stream to be updated - /// @param gctx is the geometry context - /// @param position is the current position - /// @param direction is the current direction - /// @param initial is the flag for the initial update - /// @param cTolerance is the candidate search tolerance (used only for initial updates) - static bool processStream(NavigationStream& stream, - const GeometryContext& gctx, - const Vector3& position, const Vector3& direction, - bool initial, BoundaryTolerance cTolerance); - -}; // namespace NavigationStreamHelper +} // namespace NavigationStreamHelper } // namespace Acts diff --git a/Core/include/Acts/Utilities/Intersection.hpp b/Core/include/Acts/Utilities/Intersection.hpp index b6f7e7cd43d..20889651bab 100644 --- a/Core/include/Acts/Utilities/Intersection.hpp +++ b/Core/include/Acts/Utilities/Intersection.hpp @@ -192,9 +192,7 @@ class ObjectIntersection { constexpr std::uint8_t index() const { return m_index; } - constexpr static ObjectIntersection invalid() { - return ObjectIntersection(); - } + constexpr static ObjectIntersection invalid() { return ObjectIntersection(); } constexpr static bool pathLengthOrder( const ObjectIntersection& aIntersection, diff --git a/Core/src/Navigation/CMakeLists.txt b/Core/src/Navigation/CMakeLists.txt index d9093d32a75..db5b48b417e 100644 --- a/Core/src/Navigation/CMakeLists.txt +++ b/Core/src/Navigation/CMakeLists.txt @@ -1,5 +1 @@ -target_sources( - ActsCore - PRIVATE - NavigationStreamHelper.cpp -) +target_sources(ActsCore PRIVATE NavigationStreamHelper.cpp) diff --git a/Core/src/Navigation/NavigationStreamHelper.cpp b/Core/src/Navigation/NavigationStreamHelper.cpp index 63e398cfaaa..20177405573 100644 --- a/Core/src/Navigation/NavigationStreamHelper.cpp +++ b/Core/src/Navigation/NavigationStreamHelper.cpp @@ -10,64 +10,69 @@ #include "Acts/Surfaces/Surface.hpp" -bool Acts::NavigationStreamHelper::processStream(NavigationStream& stream, - const GeometryContext& gctx, - const Vector3& position, - const Vector3& direction, - bool initial, - BoundaryTolerance cTolerance) { - // Loop over the (currently valid) candidates - // - // Except for the initial update, the loop is stopped at the first reachable - // surface. - // - for (size_t& index = stream.currentIndex; index < stream.candidates.size(); ++index) { - // Get the candidate, and resolve the tuple - NavigationStream::Candidate& candidate = stream.currentCandidate(); - auto& [sIntersection, portal, bTolerance] = candidate; - // Take the candidate Tolerance in case it is an intial update and not a portal - const BoundaryTolerance& tolerance = (portal || !initial) ? bTolerance : cTolerance; +bool Acts::NavigationStreamHelper::initializeStream( + NavigationStream& stream, const GeometryContext& gctx, + const NavigationStream::QueryPoint& queryPoint, + BoundaryTolerance cTolerance) { + // Position and direction from the query point + const Vector3& position = queryPoint.position; + const Vector3& direction = queryPoint.direction; + + // De-duplicate first (necessary to deal correctly with multiple + // intersections) - sort them by surface pointer + std::sort(stream.candidates.begin(), stream.candidates.end(), + [](const NavigationStream::Candidate& a, + const NavigationStream::Candidate& b) { + return (&a.surface()) < (&b.surface()); + }); + // Remove duplicates on basis of the surface pointer + stream.candidates.erase( + std::unique(stream.candidates.begin(), stream.candidates.end(), + [](const NavigationStream::Candidate& a, + const NavigationStream::Candidate& b) { + return (&a.surface()) == (&b.surface()); + }), + stream.candidates.end()); + // A container collecting additional valid ones from multi intersections + std::vector miCandidates = {}; + for (auto& [sIntersection, portal, bTolerance] : stream.candidates) { // Get the surface from the object intersection const Surface* surface = sIntersection.object(); - // (re-)Intersect the surface + // Intersect the surface auto multiIntersection = surface->intersect( - gctx, position, direction, tolerance, s_onSurfaceTolerance); + gctx, position, direction, cTolerance, s_onSurfaceTolerance); // Split them into valid intersections + bool miCandidate = false; for (auto& rsIntersection : multiIntersection.split()) { - // Skip negative solutions for inital update, overstepping can not happen - if (initial && rsIntersection.pathLength() < 0.) { - continue; - } - - // Skip wrong index solution for non-inital updates - if (!initial && rsIntersection.index() != sIntersection.index()) { + // Skip negative solutions + if (rsIntersection.pathLength() < 0.) { continue; } - // Valid solution is either on surface or updates the distance if (rsIntersection.isValid()) { - // Valid intersection, we assume ordering, update - sIntersection = rsIntersection; - if (!initial) { - // We are done with the current candidate - return true; + if (!miCandidate) { + sIntersection = rsIntersection; + miCandidate = true; + } else { + miCandidates.push_back( + NavigationStream::Candidate{rsIntersection, portal, bTolerance}); } } } } - // In the initial update case, we need to sort and estimate the range - std::sort( - stream.candidates.begin(), stream.candidates.end(), - [](const NavigationStream::Candidate& a, - const NavigationStream::Candidate& b) { - const auto& [aIntersection, aPortal, aTolerance] = a; - const auto& [bIntersection, bPortal, bTolerance] = b; - return (aIntersection.pathLength() < bIntersection.pathLength()) && - aIntersection.isValid(); - }); + // Append the multi intersection candidates + stream.candidates.insert(stream.candidates.end(), miCandidates.begin(), + miCandidates.end()); + + // Sort the candidates by path length + std::sort(stream.candidates.begin(), stream.candidates.end(), + [](const NavigationStream::Candidate& a, + const NavigationStream::Candidate& b) { + return a.intersection.pathLength() < b.intersection.pathLength(); + }); // The we find the first invalid candidate auto firstInvalid = @@ -78,10 +83,47 @@ bool Acts::NavigationStreamHelper::processStream(NavigationStream& stream, }); // Set the range and initialize - stream.candidates.resize(std::distance(stream.candidates.begin(), firstInvalid)); + stream.candidates.resize( + std::distance(stream.candidates.begin(), firstInvalid)); + stream.currentIndex = 0; if (stream.candidates.empty()) { return false; } return true; } + +bool Acts::NavigationStreamHelper::updateStream( + NavigationStream& stream, const GeometryContext& gctx, + const NavigationStream::QueryPoint& queryPoint) { + // Position and direction from the query point + const Vector3& position = queryPoint.position; + const Vector3& direction = queryPoint.direction; + + // Loop over the (currently valid) candidates and update + for (size_t& index = stream.currentIndex; index < stream.candidates.size(); + ++index) { + // Get the candidate, and resolve the tuple + NavigationStream::Candidate& candidate = stream.currentCandidate(); + auto& [sIntersection, portal, bTolerance] = candidate; + // Get the surface from the object intersection + const Surface* surface = sIntersection.object(); + // (re-)Intersect the surface + auto multiIntersection = surface->intersect( + gctx, position, direction, bTolerance, s_onSurfaceTolerance); + // Split them into valid intersections + for (auto& rsIntersection : multiIntersection.split()) { + // Skip wrong index solution + if (rsIntersection.index() != sIntersection.index()) { + continue; + } + // Valid solution is either on surface or updates the distance + if (rsIntersection.isValid()) { + sIntersection = rsIntersection; + return true; + } + } + } + // No candidate was reachable + return false; +} diff --git a/Tests/UnitTests/Core/Navigation/NavigationStreamTests.cpp b/Tests/UnitTests/Core/Navigation/NavigationStreamTests.cpp index 67b47484ac1..31303b4e0f0 100644 --- a/Tests/UnitTests/Core/Navigation/NavigationStreamTests.cpp +++ b/Tests/UnitTests/Core/Navigation/NavigationStreamTests.cpp @@ -10,11 +10,14 @@ #include "Acts/Navigation/NavigationStream.hpp" #include "Acts/Navigation/NavigationStreamHelper.hpp" +#include "Acts/Surfaces/CylinderSurface.hpp" #include "Acts/Surfaces/PlaneSurface.hpp" #include "Acts/Surfaces/RectangleBounds.hpp" +#include "Acts/Tests/CommonHelpers/FloatComparisons.hpp" namespace { +// This creates a set of plane surfaces along the z axis std::vector> createPlaneSurfaces() { auto rectangle = std::make_shared(10., 10.); // Surface A: @@ -45,6 +48,39 @@ std::vector> createPlaneSurfaces() { return {surfaceC, surfaceA, surfaceD, surfaceB}; } +// This creates a set of cylinder surfaces +std::vector> createCylinders() { + // Surface A: + // A concentric cylinder with a radius of 10 and a half length of 20 + Acts::Transform3 aTransform = Acts::Transform3::Identity(); + auto surfaceA = + Acts::Surface::makeShared(aTransform, 10., 20); + + // Surface B: + // A small cylinder sitting at 20, 20 + Acts::Transform3 bTransform = Acts::Transform3::Identity(); + bTransform.pretranslate(Acts::Vector3(20., 20., 0.)); + auto surfaceB = + Acts::Surface::makeShared(bTransform, 2., 10); + + // Surface C: + // A concentric cylinder with a radius of 40 and a half length of 20 + Acts::Transform3 cTransform = Acts::Transform3::Identity(); + auto surfaceC = + Acts::Surface::makeShared(cTransform, 40., 20); + + // Surface C: + // A concentric, but shifted cylinder with a radius of 50 and a half length of + // 5 + Acts::Transform3 dTransform = Acts::Transform3::Identity(); + dTransform.pretranslate(Acts::Vector3(0., 0., 10.)); + auto surfaceD = + Acts::Surface::makeShared(dTransform, 50., 5.); + + // Return in a shuffled order + return {surfaceC, surfaceB, surfaceA, surfaceD}; +} + } // namespace using namespace Acts; @@ -69,7 +105,7 @@ BOOST_AUTO_TEST_CASE(NavigationStream_InitializePlanes) { // - with infinite boundary tolerance NavigationStream nStream = nStreamTemplate; BOOST_CHECK(NavigationStreamHelper::initializeStream( - nStream, gContext, Acts::Vector3(0., 0., -30.), Acts::Vector3(0., 0., 1.), + nStream, gContext, {Vector3(0., 0., -30.), Vector3(0., 0., 1.)}, BoundaryTolerance::Infinite())); BOOST_CHECK_EQUAL(nStream.activeCandidates(), 4u); @@ -80,7 +116,7 @@ BOOST_AUTO_TEST_CASE(NavigationStream_InitializePlanes) { // - with infinite boundary tolerance nStream = nStreamTemplate; BOOST_CHECK(NavigationStreamHelper::initializeStream( - nStream, gContext, Acts::Vector3(0., 0., 0.), Acts::Vector3(0., 0., 1.), + nStream, gContext, {Vector3(0., 0., 0.), Vector3(0., 0., 1.)}, BoundaryTolerance::Infinite())); BOOST_CHECK_EQUAL(nStream.activeCandidates(), 3u); BOOST_CHECK_EQUAL(&nStream.currentCandidate().surface(), surfaces[3u].get()); @@ -90,20 +126,162 @@ BOOST_AUTO_TEST_CASE(NavigationStream_InitializePlanes) { // - with no boundary tolerance nStream = nStreamTemplate; BOOST_CHECK(NavigationStreamHelper::initializeStream( - nStream, gContext, Acts::Vector3(0., 0., -100.), - Acts::Vector3(0., 0., 1.), BoundaryTolerance::None())); + nStream, gContext, {Vector3(0., 0., -100.), Vector3(0., 0., 1.)}, + BoundaryTolerance::None())); BOOST_CHECK_EQUAL(nStream.activeCandidates(), 3u); // (4) Run an initial update // - none of the surfaces should be reachable nStream = nStreamTemplate; BOOST_CHECK(!NavigationStreamHelper::initializeStream( - nStream, gContext, Acts::Vector3(0., 0., 0.), Acts::Vector3(1., 0., 0.), + nStream, gContext, {Vector3(0., 0., 0.), Vector3(1., 0., 0.)}, BoundaryTolerance::Infinite())); BOOST_CHECK_EQUAL(nStream.activeCandidates(), 0u); BOOST_CHECK_THROW(nStream.currentCandidate(), std::out_of_range); + // (5) Test de-duplication + nStream = nStreamTemplate; + NavigationStreamHelper::fillSurfaces(nStreamTemplate, {surfaces[0].get()}, + Acts::BoundaryTolerance::None()); + // One surface is duplicated in the stream + BOOST_CHECK_EQUAL(nStreamTemplate.candidates.size(), 5u); + // Initialize stream reaches all surfaces, but also de-duplicates + BOOST_CHECK(NavigationStreamHelper::initializeStream( + nStream, gContext, {Vector3(0., 0., -100.), Vector3(0., 0., 1.)}, + BoundaryTolerance::Infinite())); + BOOST_CHECK_EQUAL(nStream.activeCandidates(), 4u); +} + +BOOST_AUTO_TEST_CASE(NavigationStream_UpdatePlanes) { + // Create the punch of surfaces + auto surfaces = createPlaneSurfaces(); + + // Surfaces are filled with no boundary tolerance, we require them to be + // reachable and intersections inside bounds + NavigationStream nStreamTemplate; + for (const auto& surface : surfaces) { + NavigationStreamHelper::fillSurfaces(nStreamTemplate, {surface.get()}, + Acts::BoundaryTolerance::None()); + } + BOOST_CHECK_EQUAL(nStreamTemplate.candidates.size(), 4); + + // Run an initial update + // - from a position where all are reachable and valid + // - with infinite boundary tolerance + NavigationStream::QueryPoint qPoint = {Vector3(0., 0., -30.), + Vector3(0., 0., 1.)}; + + NavigationStream nStream = nStreamTemplate; + BOOST_CHECK(NavigationStreamHelper::initializeStream( + nStream, gContext, qPoint, BoundaryTolerance::Infinite())); + BOOST_CHECK_EQUAL(nStream.activeCandidates(), 4u); + BOOST_CHECK_EQUAL(&nStream.currentCandidate().surface(), surfaces[1u].get()); + CHECK_CLOSE_ABS(nStream.currentCandidate().pathLength(), 10., + std::numeric_limits::epsilon()); + + // Let's push a bit closer to the surface + qPoint.position = Vector3(0., 0., -22.); + BOOST_CHECK(NavigationStreamHelper::updateStream(nStream, gContext, qPoint)); + // Surface unchanged, but the intersection should be closer + BOOST_CHECK_EQUAL(&nStream.currentCandidate().surface(), surfaces[1u].get()); + CHECK_CLOSE_ABS(nStream.currentCandidate().pathLength(), 2., + std::numeric_limits::epsilon()); + + // Uuuups, an overstep + qPoint.position = Vector3(0., 0., -19.5); + BOOST_CHECK(NavigationStreamHelper::updateStream(nStream, gContext, qPoint)); + // Surface still unchanged, but pathLength is now negative + BOOST_CHECK_EQUAL(&nStream.currentCandidate().surface(), surfaces[1u].get()); + CHECK_CLOSE_ABS(nStream.currentCandidate().pathLength(), -0.5, + std::numeric_limits::epsilon()); + + // Finally hit it + qPoint.position = Vector3(0., 0., -20.); + BOOST_CHECK(NavigationStreamHelper::updateStream(nStream, gContext, qPoint)); + // Surface still unchanged, however, now withL + // - pathlength smaller on surface tolerance, intersection status onSurface + BOOST_CHECK_EQUAL(&nStream.currentCandidate().surface(), surfaces[1u].get()); + CHECK_CLOSE_ABS( + nStream.currentCandidate().pathLength(), s_onSurfaceTolerance, + std::numeric_limits::epsilon() + s_onSurfaceTolerance); + BOOST_CHECK_EQUAL(nStream.currentCandidate().intersection.status(), + IntersectionStatus::onSurface); + // Let's say the stepper confirms this + BOOST_CHECK(nStream.switchToNextCandidate()); + // Surface is now surfaceB + BOOST_CHECK_EQUAL(&nStream.currentCandidate().surface(), surfaces[3u].get()); + // Distance should be the initial estimate from the intialializeStream() call + CHECK_CLOSE_ABS(nStream.currentCandidate().pathLength(), 130., + std::numeric_limits::epsilon()); + // Query update will re-evaluate this one: however, we will miss the surface + // due to outside bounds - and will switch to the next candidate: which sits + // at 200 and then will yield 220 + BOOST_CHECK(NavigationStreamHelper::updateStream(nStream, gContext, qPoint)); + CHECK_CLOSE_ABS(nStream.currentCandidate().pathLength(), 220., + std::numeric_limits::epsilon()); + // Oh noooo, an actor just kicked in and changed the direction + qPoint.direction = Vector3(0., 1., 1.).normalized(); + // All ist lost, no surface is reachable anymore + BOOST_CHECK(!NavigationStreamHelper::updateStream(nStream, gContext, qPoint)); +} + +BOOST_AUTO_TEST_CASE(NavigationStream_InitializeCylinders) { + // Create the cylinder setu + auto surfaces = createCylinders(); + + // Let us fill the surfaces into the navigation stream + NavigationStream nStreamTemplate; + for (const auto& surface : surfaces) { + NavigationStreamHelper::fillSurfaces(nStreamTemplate, {surface.get()}, + Acts::BoundaryTolerance::None()); + } + BOOST_CHECK_EQUAL(nStreamTemplate.activeCandidates(), 4u); + // (1) Run an initial update - from a position/direction where all are + // reachable + // - with infinite boundary tolerance + NavigationStream nStream = nStreamTemplate; + BOOST_CHECK(NavigationStreamHelper::initializeStream( + nStream, gContext, + {Vector3(0., 0., 0.), Vector3(1., 1., 0.).normalized()}, + BoundaryTolerance::Infinite())); + // We should have 5 candidates, as one cylinder is reachable twice + BOOST_CHECK_EQUAL(nStream.activeCandidates(), 5u); + // First one is inner candidate + BOOST_CHECK_EQUAL(&nStream.currentCandidate().surface(), surfaces[2].get()); + // Surface of 2nd and 3rd candidate should be the same + BOOST_CHECK_EQUAL(&nStream.candidates[1].surface(), surfaces[1].get()); + BOOST_CHECK_EQUAL(&nStream.candidates[2].surface(), surfaces[1].get()); + + // (2) Run an initial update - from a position/direction where only + // the concentric ones are reachable + // - with infinite boundary tolerance + nStream = nStreamTemplate; + BOOST_CHECK(NavigationStreamHelper::initializeStream( + nStream, gContext, {Vector3(0., 0., 0.), Vector3(1., 0., 0.)}, + BoundaryTolerance::Infinite())); + // We should have 3 candidates + BOOST_CHECK_EQUAL(nStream.activeCandidates(), 3u); + + // (3) Run an initial update - from a position/direction where only the + // concentric ones within bounds are reachable + nStream = nStreamTemplate; + BOOST_CHECK(NavigationStreamHelper::initializeStream( + nStream, gContext, {Vector3(0., 0., 0.), Vector3(1., 0., 0.)}, + BoundaryTolerance::None())); + // We should have 2 candidates + BOOST_CHECK_EQUAL(nStream.activeCandidates(), 2u); + + // (4) Run an initial update - from a position/direction where none are + // reachable + // - (even) with infinite boundary tolerance + nStream = nStreamTemplate; + BOOST_CHECK(!NavigationStreamHelper::initializeStream( + nStream, gContext, {Vector3(0., 0., 0.), Vector3(0., 0., 1.)}, + BoundaryTolerance::None())); + // We should have 0 candidates + BOOST_CHECK_EQUAL(nStream.activeCandidates(), 0u); + BOOST_CHECK_THROW(nStream.currentCandidate(), std::out_of_range); } -BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file +BOOST_AUTO_TEST_SUITE_END() From 4311cfb2d16e344b5c01e548a81b5a6869318359 Mon Sep 17 00:00:00 2001 From: Andreas Salzburger Date: Thu, 22 Aug 2024 13:11:48 +0200 Subject: [PATCH 04/13] fix spelling and size_t --- Core/include/Acts/Navigation/NavigationStream.hpp | 2 +- Core/src/Navigation/NavigationStreamHelper.cpp | 4 ++-- Tests/UnitTests/Core/Navigation/NavigationStreamTests.cpp | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Core/include/Acts/Navigation/NavigationStream.hpp b/Core/include/Acts/Navigation/NavigationStream.hpp index 4cef69b6c1c..37f51808566 100644 --- a/Core/include/Acts/Navigation/NavigationStream.hpp +++ b/Core/include/Acts/Navigation/NavigationStream.hpp @@ -69,7 +69,7 @@ struct NavigationStream { std::vector candidates; /// The currently active candidate range - size_t currentIndex = 0u; + std::size_t currentIndex = 0u; /// Progress to next next candidate /// diff --git a/Core/src/Navigation/NavigationStreamHelper.cpp b/Core/src/Navigation/NavigationStreamHelper.cpp index 20177405573..1e45d2e13ff 100644 --- a/Core/src/Navigation/NavigationStreamHelper.cpp +++ b/Core/src/Navigation/NavigationStreamHelper.cpp @@ -101,8 +101,8 @@ bool Acts::NavigationStreamHelper::updateStream( const Vector3& direction = queryPoint.direction; // Loop over the (currently valid) candidates and update - for (size_t& index = stream.currentIndex; index < stream.candidates.size(); - ++index) { + for (std::size_t& index = stream.currentIndex; + index < stream.candidates.size(); ++index) { // Get the candidate, and resolve the tuple NavigationStream::Candidate& candidate = stream.currentCandidate(); auto& [sIntersection, portal, bTolerance] = candidate; diff --git a/Tests/UnitTests/Core/Navigation/NavigationStreamTests.cpp b/Tests/UnitTests/Core/Navigation/NavigationStreamTests.cpp index 31303b4e0f0..2fe2ff57568 100644 --- a/Tests/UnitTests/Core/Navigation/NavigationStreamTests.cpp +++ b/Tests/UnitTests/Core/Navigation/NavigationStreamTests.cpp @@ -221,12 +221,12 @@ BOOST_AUTO_TEST_CASE(NavigationStream_UpdatePlanes) { std::numeric_limits::epsilon()); // Oh noooo, an actor just kicked in and changed the direction qPoint.direction = Vector3(0., 1., 1.).normalized(); - // All ist lost, no surface is reachable anymore + // All is lost, no surface is reachable anymore BOOST_CHECK(!NavigationStreamHelper::updateStream(nStream, gContext, qPoint)); } BOOST_AUTO_TEST_CASE(NavigationStream_InitializeCylinders) { - // Create the cylinder setu + // Create the cylinder setup auto surfaces = createCylinders(); // Let us fill the surfaces into the navigation stream From 51d6facd2d1313ed5321466cbd76d90da73f746c Mon Sep 17 00:00:00 2001 From: Andreas Salzburger Date: Thu, 22 Aug 2024 13:27:06 +0200 Subject: [PATCH 05/13] update comments --- .../Acts/Navigation/NavigationStream.hpp | 40 +++++++++---------- .../Navigation/NavigationStreamHelper.hpp | 9 ++--- 2 files changed, 22 insertions(+), 27 deletions(-) diff --git a/Core/include/Acts/Navigation/NavigationStream.hpp b/Core/include/Acts/Navigation/NavigationStream.hpp index 37f51808566..4b8736d3176 100644 --- a/Core/include/Acts/Navigation/NavigationStream.hpp +++ b/Core/include/Acts/Navigation/NavigationStream.hpp @@ -27,17 +27,16 @@ class Surface; /// @brief The NavigationStream is a container for the navigation candidates /// -/// The current candidates are stored in a vector with a range defined -/// by a pair of indices. This implementation allows passed or unreachable -/// candidates to be shaddowed without removing them from the container. +/// The current candidates are stored in a vector of candidates, where an index +/// is used to indicate the current active candidate. /// /// @todo the NavigationStream should hold also the current volume it is in /// if it represents the geometry stream. -/// -/// A surface proximity parameter can be used to chose which sort of -/// intersection path length update is needed. struct NavigationStream { - /// @brief the Query ppoint for updating the navigation stream + /// The query point for the navigation stream + /// + /// This holds the position and direction from which the navigation stream + /// should either be initialzed or updated. struct QueryPoint { /// The position of the query point Vector3 position = Vector3::Zero(); @@ -45,16 +44,15 @@ struct NavigationStream { Vector3 direction = Vector3::Zero(); }; - /// This is a candidate type for a Surface intersection - using SurfaceIntersection = ObjectIntersection; - - /// This is a candidate object of the navigation stream - /// a Surface intersection - /// a Portal : set if the surface represents a portal - /// a BoundaryTolerance : the boundary tolerance used for the intersection + /// This is a candidate object of the navigation stream, it holds: + /// + /// - a Surface intersection + /// - a Portal : set if the surface represents a portal + /// - a BoundaryTolerance : the boundary tolerance used for the intersection struct Candidate { /// The intersection - SurfaceIntersection intersection = SurfaceIntersection::invalid(); + ObjectIntersection intersection = + ObjectIntersection::invalid(); /// The portal const Portal* portal = nullptr; /// The boundary tolerance @@ -65,13 +63,13 @@ struct NavigationStream { ActsScalar pathLength() const { return intersection.pathLength(); } }; - /// The candidates for the navigation + /// The candidates of this navigation stream std::vector candidates; - /// The currently active candidate range + /// The currently active candidate std::size_t currentIndex = 0u; - /// Progress to next next candidate + /// Swtich to next next candidate /// /// @return true if a next candidate is available bool switchToNextCandidate() { @@ -82,15 +80,15 @@ struct NavigationStream { return false; } - /// @brief Const-access the current candidate + /// Const access the current candidate const Candidate& currentCandidate() const { return candidates.at(currentIndex); } - /// @brief Noncost-access the current candidate + /// Non-cost access the current candidate Candidate& currentCandidate() { return candidates.at(currentIndex); } - /// @brief Access the current candidate + /// The number of active candidates std::size_t activeCandidates() const { return (candidates.size() - currentIndex); } diff --git a/Core/include/Acts/Navigation/NavigationStreamHelper.hpp b/Core/include/Acts/Navigation/NavigationStreamHelper.hpp index c3bf0227b0f..43c1f4ee372 100644 --- a/Core/include/Acts/Navigation/NavigationStreamHelper.hpp +++ b/Core/include/Acts/Navigation/NavigationStreamHelper.hpp @@ -23,10 +23,8 @@ using namespace Experimental; class Surface; -/// Fillers and attachers for surfaces to act on the navigation state namespace NavigationStreamHelper { -/// Helper struct that allows to fill surfaces into the candidate vector it -/// allows to use common navigation structs for volume, portal, surfaces +/// Helper struct that allows to fill surfaces into the candidate vector /// /// @param nStream the navigation stream that is being filled /// @param surfaces the surfaces that are filled in @@ -40,8 +38,7 @@ inline static void fillSurfaces(NavigationStream& nStream, }); } -/// Helper struct that allows to fill surfaces into the candidate vector it -/// allows to use common navigation structs for volume, portal, surfaces +/// Helper struct that allows to fill portals into the candidate vector /// /// @param nStream the navigation stream that is being filled /// @param portals the portals that are filled in @@ -71,7 +68,7 @@ bool initializeStream(NavigationStream& stream, const GeometryContext& gctx, const NavigationStream::QueryPoint& queryPoint, BoundaryTolerance cTolerance); -/// Convenience method to upsate a stream from a new position, +/// Convenience method to update a stream from a new query point, /// this could be called from navigation delegates that do not require /// a local state or from the navigator on the target stream /// From 823f0b8b436c20ad080c87342d6ff4dc5f93227a Mon Sep 17 00:00:00 2001 From: Andreas Salzburger Date: Thu, 22 Aug 2024 14:22:35 +0200 Subject: [PATCH 06/13] add abortTarget flag --- .../Acts/Navigation/NavigationStream.hpp | 2 ++ .../Navigation/NavigationStreamHelper.hpp | 21 +++++++++++++++++-- .../src/Navigation/NavigationStreamHelper.cpp | 17 ++++++++------- 3 files changed, 30 insertions(+), 10 deletions(-) diff --git a/Core/include/Acts/Navigation/NavigationStream.hpp b/Core/include/Acts/Navigation/NavigationStream.hpp index 4b8736d3176..eecaf61da61 100644 --- a/Core/include/Acts/Navigation/NavigationStream.hpp +++ b/Core/include/Acts/Navigation/NavigationStream.hpp @@ -57,6 +57,8 @@ struct NavigationStream { const Portal* portal = nullptr; /// The boundary tolerance BoundaryTolerance bTolerance = BoundaryTolerance::None(); + /// Target flag: true if this Candidate represents an abortTarget + bool abortTarget = false; /// Convenience access to surface const Surface& surface() const { return *intersection.object(); } /// Cinvencience access to the path length diff --git a/Core/include/Acts/Navigation/NavigationStreamHelper.hpp b/Core/include/Acts/Navigation/NavigationStreamHelper.hpp index 43c1f4ee372..db4e954367b 100644 --- a/Core/include/Acts/Navigation/NavigationStreamHelper.hpp +++ b/Core/include/Acts/Navigation/NavigationStreamHelper.hpp @@ -24,17 +24,34 @@ using namespace Experimental; class Surface; namespace NavigationStreamHelper { + +/// Helper struct that allows to fill a surface into the candidate vector +/// +/// @param nStream the navigation stream that is being filled +/// @param surface the surface to be filled +/// @param bTolerance the boundary tolerance used for the intersection +/// @param abortTarget a boolean that indicates if this surface is an abort target +inline static void fillSurface(NavigationStream& nStream, + const Surface* surface, + BoundaryTolerance bTolerance, + bool abortTarget = false) { + nStream.candidates.push_back(NavigationStream::Candidate{ + ObjectIntersection(surface), nullptr, bTolerance, abortTarget}); +} + /// Helper struct that allows to fill surfaces into the candidate vector /// /// @param nStream the navigation stream that is being filled /// @param surfaces the surfaces that are filled in /// @param bTolerance the boundary tolerance used for the intersection +/// @param abortTarget a boolean that indicates if those surfaces are abort targets inline static void fillSurfaces(NavigationStream& nStream, const std::vector& surfaces, - BoundaryTolerance bTolerance) { + BoundaryTolerance bTolerance, + bool abortTarget = false) { std::for_each(surfaces.begin(), surfaces.end(), [&](const auto& s) { nStream.candidates.push_back(NavigationStream::Candidate{ - ObjectIntersection(s), nullptr, bTolerance}); + ObjectIntersection(s), nullptr, bTolerance, abortTarget}); }); } diff --git a/Core/src/Navigation/NavigationStreamHelper.cpp b/Core/src/Navigation/NavigationStreamHelper.cpp index 1e45d2e13ff..47d23df0358 100644 --- a/Core/src/Navigation/NavigationStreamHelper.cpp +++ b/Core/src/Navigation/NavigationStreamHelper.cpp @@ -36,7 +36,8 @@ bool Acts::NavigationStreamHelper::initializeStream( // A container collecting additional valid ones from multi intersections std::vector miCandidates = {}; - for (auto& [sIntersection, portal, bTolerance] : stream.candidates) { + for (auto& [sIntersection, portal, bTolerance, abortTarget] : + stream.candidates) { // Get the surface from the object intersection const Surface* surface = sIntersection.object(); // Intersect the surface @@ -75,12 +76,12 @@ bool Acts::NavigationStreamHelper::initializeStream( }); // The we find the first invalid candidate - auto firstInvalid = - std::find_if(stream.candidates.begin(), stream.candidates.end(), - [](const NavigationStream::Candidate& a) { - const auto& [aIntersection, aPortal, aTolerance] = a; - return !aIntersection.isValid(); - }); + auto firstInvalid = std::find_if( + stream.candidates.begin(), stream.candidates.end(), + [](const NavigationStream::Candidate& a) { + const auto& [aIntersection, aPortal, aTolerance, abortTarget] = a; + return !aIntersection.isValid(); + }); // Set the range and initialize stream.candidates.resize( @@ -105,7 +106,7 @@ bool Acts::NavigationStreamHelper::updateStream( index < stream.candidates.size(); ++index) { // Get the candidate, and resolve the tuple NavigationStream::Candidate& candidate = stream.currentCandidate(); - auto& [sIntersection, portal, bTolerance] = candidate; + auto& [sIntersection, portal, bTolerance, abortTarget] = candidate; // Get the surface from the object intersection const Surface* surface = sIntersection.object(); // (re-)Intersect the surface From bc992b3833bf7853d084d1ec916a38e0b426f273 Mon Sep 17 00:00:00 2001 From: Andreas Salzburger Date: Thu, 22 Aug 2024 14:23:54 +0200 Subject: [PATCH 07/13] spelling --- Core/include/Acts/Navigation/NavigationStream.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Core/include/Acts/Navigation/NavigationStream.hpp b/Core/include/Acts/Navigation/NavigationStream.hpp index eecaf61da61..cedaab9d474 100644 --- a/Core/include/Acts/Navigation/NavigationStream.hpp +++ b/Core/include/Acts/Navigation/NavigationStream.hpp @@ -36,7 +36,7 @@ struct NavigationStream { /// The query point for the navigation stream /// /// This holds the position and direction from which the navigation stream - /// should either be initialzed or updated. + /// should either be initialized or updated. struct QueryPoint { /// The position of the query point Vector3 position = Vector3::Zero(); @@ -71,7 +71,7 @@ struct NavigationStream { /// The currently active candidate std::size_t currentIndex = 0u; - /// Swtich to next next candidate + /// Switch to next next candidate /// /// @return true if a next candidate is available bool switchToNextCandidate() { From d82de5cb5d6402538fcf4cae80437e2f795ee671 Mon Sep 17 00:00:00 2001 From: Andreas Salzburger Date: Thu, 22 Aug 2024 15:02:52 +0200 Subject: [PATCH 08/13] Update NavigationStream.hpp --- Core/include/Acts/Navigation/NavigationStream.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Core/include/Acts/Navigation/NavigationStream.hpp b/Core/include/Acts/Navigation/NavigationStream.hpp index cedaab9d474..5d7730bb7ed 100644 --- a/Core/include/Acts/Navigation/NavigationStream.hpp +++ b/Core/include/Acts/Navigation/NavigationStream.hpp @@ -11,7 +11,6 @@ #include "Acts/Surfaces/BoundaryTolerance.hpp" #include "Acts/Utilities/Intersection.hpp" -#include #include #include From d0037f0a8eaa7c52efba204d64b1adc8a983ce03 Mon Sep 17 00:00:00 2001 From: Andreas Salzburger Date: Wed, 28 Aug 2024 17:27:06 +0200 Subject: [PATCH 09/13] adressing first set of PR comments --- .../Acts/Navigation/NavigationStream.hpp | 14 +++++- .../Navigation/NavigationStreamHelper.hpp | 20 ++++++--- Core/include/Acts/Utilities/Intersection.hpp | 13 ++---- .../src/Navigation/NavigationStreamHelper.cpp | 43 ++++++++++--------- .../Core/Navigation/NavigationStreamTests.cpp | 22 +++++----- 5 files changed, 63 insertions(+), 49 deletions(-) diff --git a/Core/include/Acts/Navigation/NavigationStream.hpp b/Core/include/Acts/Navigation/NavigationStream.hpp index 5d7730bb7ed..785fff2ab28 100644 --- a/Core/include/Acts/Navigation/NavigationStream.hpp +++ b/Core/include/Acts/Navigation/NavigationStream.hpp @@ -62,6 +62,18 @@ struct NavigationStream { const Surface& surface() const { return *intersection.object(); } /// Cinvencience access to the path length ActsScalar pathLength() const { return intersection.pathLength(); } + + /// Order along the path length + /// + /// @param aCandidate is the first candidate + /// @param bCandidate is the second candidate + /// + /// @return true if aCandidate is closer to the origin + constexpr static bool pathLengthOrder(const Candidate& aCandidate, + const Candidate& bCandidate) { + return ObjectIntersection::pathLengthOrder( + aCandidate.intersection, bCandidate.intersection); + } }; /// The candidates of this navigation stream @@ -90,7 +102,7 @@ struct NavigationStream { Candidate& currentCandidate() { return candidates.at(currentIndex); } /// The number of active candidates - std::size_t activeCandidates() const { + std::size_t remainingCandidates() const { return (candidates.size() - currentIndex); } }; diff --git a/Core/include/Acts/Navigation/NavigationStreamHelper.hpp b/Core/include/Acts/Navigation/NavigationStreamHelper.hpp index db4e954367b..60936a417cd 100644 --- a/Core/include/Acts/Navigation/NavigationStreamHelper.hpp +++ b/Core/include/Acts/Navigation/NavigationStreamHelper.hpp @@ -35,8 +35,9 @@ inline static void fillSurface(NavigationStream& nStream, const Surface* surface, BoundaryTolerance bTolerance, bool abortTarget = false) { - nStream.candidates.push_back(NavigationStream::Candidate{ - ObjectIntersection(surface), nullptr, bTolerance, abortTarget}); + nStream.candidates.push_back( + NavigationStream::Candidate{ObjectIntersection::invalid(surface), + nullptr, bTolerance, abortTarget}); } /// Helper struct that allows to fill surfaces into the candidate vector @@ -50,8 +51,9 @@ inline static void fillSurfaces(NavigationStream& nStream, BoundaryTolerance bTolerance, bool abortTarget = false) { std::for_each(surfaces.begin(), surfaces.end(), [&](const auto& s) { - nStream.candidates.push_back(NavigationStream::Candidate{ - ObjectIntersection(s), nullptr, bTolerance, abortTarget}); + nStream.candidates.push_back( + NavigationStream::Candidate{ObjectIntersection::invalid(s), + nullptr, bTolerance, abortTarget}); }); } @@ -63,7 +65,7 @@ inline static void fillPortals(NavigationStream& nStream, const std::vector& portals) { std::for_each(portals.begin(), portals.end(), [&](const auto& p) { nStream.candidates.push_back(NavigationStream::Candidate{ - ObjectIntersection(&(p->surface())), p, + ObjectIntersection::invalid(&(p->surface())), p, BoundaryTolerance::None()}); }); } @@ -74,6 +76,7 @@ inline static void fillPortals(NavigationStream& nStream, /// @param gctx is the geometry context /// @param queryPoint holds current position, direction, etc. /// @param cTolerance is the candidate search tolerance +/// @param onSurfaceTolerance is the tolerance for on-surface intersections /// /// This method will first de-duplicate the candidates on basis of the surface /// pointer to make sure that the multi-intersections are handled correctly. @@ -83,7 +86,8 @@ inline static void fillPortals(NavigationStream& nStream, /// @return true if the stream is active, false indicates that there are no valid candidates bool initializeStream(NavigationStream& stream, const GeometryContext& gctx, const NavigationStream::QueryPoint& queryPoint, - BoundaryTolerance cTolerance); + BoundaryTolerance cTolerance, + ActsScalar onSurfaceTolerance = s_onSurfaceTolerance); /// Convenience method to update a stream from a new query point, /// this could be called from navigation delegates that do not require @@ -92,10 +96,12 @@ bool initializeStream(NavigationStream& stream, const GeometryContext& gctx, /// @param stream [in, out] is the navigation stream to be updated /// @param gctx is the geometry context /// @param queryPoint holds current position, direction, etc. +/// @param onSurfaceTolerance is the tolerance for on-surface intersections /// /// @return true if the stream is active, false indicate no valid candidates left bool updateStream(NavigationStream& stream, const GeometryContext& gctx, - const NavigationStream::QueryPoint& queryPoint); + const NavigationStream::QueryPoint& queryPoint, + ActsScalar onSurfaceTolerance = s_onSurfaceTolerance); } // namespace NavigationStreamHelper diff --git a/Core/include/Acts/Utilities/Intersection.hpp b/Core/include/Acts/Utilities/Intersection.hpp index 20889651bab..30b986f6d24 100644 --- a/Core/include/Acts/Utilities/Intersection.hpp +++ b/Core/include/Acts/Utilities/Intersection.hpp @@ -149,14 +149,6 @@ class ObjectIntersection { const object_t* object, std::uint8_t index = 0) : m_intersection(intersection), m_object(object), m_index(index) {} - /// Invalid object intersection - only holding the object itself - /// - /// @param object is the object to be instersected - constexpr ObjectIntersection(const object_t* object) - : m_intersection(Intersection3D::invalid()), - m_object(object), - m_index(0) {} - /// Returns whether the intersection was successful or not /// @deprecated [[deprecated("Use isValid() instead")]] constexpr explicit operator bool() @@ -192,7 +184,10 @@ class ObjectIntersection { constexpr std::uint8_t index() const { return m_index; } - constexpr static ObjectIntersection invalid() { return ObjectIntersection(); } + constexpr static ObjectIntersection invalid( + const object_t* object = nullptr) { + return ObjectIntersection(Intersection3D::invalid(), object); + } constexpr static bool pathLengthOrder( const ObjectIntersection& aIntersection, diff --git a/Core/src/Navigation/NavigationStreamHelper.cpp b/Core/src/Navigation/NavigationStreamHelper.cpp index 47d23df0358..4d61c558f15 100644 --- a/Core/src/Navigation/NavigationStreamHelper.cpp +++ b/Core/src/Navigation/NavigationStreamHelper.cpp @@ -13,7 +13,7 @@ bool Acts::NavigationStreamHelper::initializeStream( NavigationStream& stream, const GeometryContext& gctx, const NavigationStream::QueryPoint& queryPoint, - BoundaryTolerance cTolerance) { + BoundaryTolerance cTolerance, ActsScalar onSurfaceTolerance) { // Position and direction from the query point const Vector3& position = queryPoint.position; const Vector3& direction = queryPoint.direction; @@ -34,30 +34,32 @@ bool Acts::NavigationStreamHelper::initializeStream( }), stream.candidates.end()); - // A container collecting additional valid ones from multi intersections - std::vector miCandidates = {}; + // A container collecting additional candidates from multiple + // valid interseciton + std::vector additionalCandidates = {}; for (auto& [sIntersection, portal, bTolerance, abortTarget] : stream.candidates) { // Get the surface from the object intersection const Surface* surface = sIntersection.object(); // Intersect the surface - auto multiIntersection = surface->intersect( - gctx, position, direction, cTolerance, s_onSurfaceTolerance); + auto multiIntersection = surface->intersect(gctx, position, direction, + cTolerance, onSurfaceTolerance); - // Split them into valid intersections - bool miCandidate = false; + // Split them into valid intersections, keep track of potentially + // additional candidates + bool originalCandidateUpdated = false; for (auto& rsIntersection : multiIntersection.split()) { - // Skip negative solutions - if (rsIntersection.pathLength() < 0.) { + // Skip negative solutions, respecting the on surface tolerance + if (rsIntersection.pathLength() < -onSurfaceTolerance) { continue; } // Valid solution is either on surface or updates the distance if (rsIntersection.isValid()) { - if (!miCandidate) { + if (!originalCandidateUpdated) { sIntersection = rsIntersection; - miCandidate = true; + originalCandidateUpdated = true; } else { - miCandidates.push_back( + additionalCandidates.push_back( NavigationStream::Candidate{rsIntersection, portal, bTolerance}); } } @@ -65,15 +67,13 @@ bool Acts::NavigationStreamHelper::initializeStream( } // Append the multi intersection candidates - stream.candidates.insert(stream.candidates.end(), miCandidates.begin(), - miCandidates.end()); + stream.candidates.insert(stream.candidates.end(), + additionalCandidates.begin(), + additionalCandidates.end()); // Sort the candidates by path length std::sort(stream.candidates.begin(), stream.candidates.end(), - [](const NavigationStream::Candidate& a, - const NavigationStream::Candidate& b) { - return a.intersection.pathLength() < b.intersection.pathLength(); - }); + NavigationStream::Candidate::pathLengthOrder); // The we find the first invalid candidate auto firstInvalid = std::find_if( @@ -96,7 +96,8 @@ bool Acts::NavigationStreamHelper::initializeStream( bool Acts::NavigationStreamHelper::updateStream( NavigationStream& stream, const GeometryContext& gctx, - const NavigationStream::QueryPoint& queryPoint) { + const NavigationStream::QueryPoint& queryPoint, + ActsScalar onSurfaceTolerance) { // Position and direction from the query point const Vector3& position = queryPoint.position; const Vector3& direction = queryPoint.direction; @@ -110,8 +111,8 @@ bool Acts::NavigationStreamHelper::updateStream( // Get the surface from the object intersection const Surface* surface = sIntersection.object(); // (re-)Intersect the surface - auto multiIntersection = surface->intersect( - gctx, position, direction, bTolerance, s_onSurfaceTolerance); + auto multiIntersection = surface->intersect(gctx, position, direction, + bTolerance, onSurfaceTolerance); // Split them into valid intersections for (auto& rsIntersection : multiIntersection.split()) { // Skip wrong index solution diff --git a/Tests/UnitTests/Core/Navigation/NavigationStreamTests.cpp b/Tests/UnitTests/Core/Navigation/NavigationStreamTests.cpp index 2fe2ff57568..900502d0442 100644 --- a/Tests/UnitTests/Core/Navigation/NavigationStreamTests.cpp +++ b/Tests/UnitTests/Core/Navigation/NavigationStreamTests.cpp @@ -108,7 +108,7 @@ BOOST_AUTO_TEST_CASE(NavigationStream_InitializePlanes) { nStream, gContext, {Vector3(0., 0., -30.), Vector3(0., 0., 1.)}, BoundaryTolerance::Infinite())); - BOOST_CHECK_EQUAL(nStream.activeCandidates(), 4u); + BOOST_CHECK_EQUAL(nStream.remainingCandidates(), 4u); BOOST_CHECK_EQUAL(&nStream.currentCandidate().surface(), surfaces[1u].get()); // (2) Run an initial update @@ -118,7 +118,7 @@ BOOST_AUTO_TEST_CASE(NavigationStream_InitializePlanes) { BOOST_CHECK(NavigationStreamHelper::initializeStream( nStream, gContext, {Vector3(0., 0., 0.), Vector3(0., 0., 1.)}, BoundaryTolerance::Infinite())); - BOOST_CHECK_EQUAL(nStream.activeCandidates(), 3u); + BOOST_CHECK_EQUAL(nStream.remainingCandidates(), 3u); BOOST_CHECK_EQUAL(&nStream.currentCandidate().surface(), surfaces[3u].get()); // (3) Run an initial update @@ -128,7 +128,7 @@ BOOST_AUTO_TEST_CASE(NavigationStream_InitializePlanes) { BOOST_CHECK(NavigationStreamHelper::initializeStream( nStream, gContext, {Vector3(0., 0., -100.), Vector3(0., 0., 1.)}, BoundaryTolerance::None())); - BOOST_CHECK_EQUAL(nStream.activeCandidates(), 3u); + BOOST_CHECK_EQUAL(nStream.remainingCandidates(), 3u); // (4) Run an initial update // - none of the surfaces should be reachable @@ -136,7 +136,7 @@ BOOST_AUTO_TEST_CASE(NavigationStream_InitializePlanes) { BOOST_CHECK(!NavigationStreamHelper::initializeStream( nStream, gContext, {Vector3(0., 0., 0.), Vector3(1., 0., 0.)}, BoundaryTolerance::Infinite())); - BOOST_CHECK_EQUAL(nStream.activeCandidates(), 0u); + BOOST_CHECK_EQUAL(nStream.remainingCandidates(), 0u); BOOST_CHECK_THROW(nStream.currentCandidate(), std::out_of_range); // (5) Test de-duplication @@ -149,7 +149,7 @@ BOOST_AUTO_TEST_CASE(NavigationStream_InitializePlanes) { BOOST_CHECK(NavigationStreamHelper::initializeStream( nStream, gContext, {Vector3(0., 0., -100.), Vector3(0., 0., 1.)}, BoundaryTolerance::Infinite())); - BOOST_CHECK_EQUAL(nStream.activeCandidates(), 4u); + BOOST_CHECK_EQUAL(nStream.remainingCandidates(), 4u); } BOOST_AUTO_TEST_CASE(NavigationStream_UpdatePlanes) { @@ -174,7 +174,7 @@ BOOST_AUTO_TEST_CASE(NavigationStream_UpdatePlanes) { NavigationStream nStream = nStreamTemplate; BOOST_CHECK(NavigationStreamHelper::initializeStream( nStream, gContext, qPoint, BoundaryTolerance::Infinite())); - BOOST_CHECK_EQUAL(nStream.activeCandidates(), 4u); + BOOST_CHECK_EQUAL(nStream.remainingCandidates(), 4u); BOOST_CHECK_EQUAL(&nStream.currentCandidate().surface(), surfaces[1u].get()); CHECK_CLOSE_ABS(nStream.currentCandidate().pathLength(), 10., std::numeric_limits::epsilon()); @@ -235,7 +235,7 @@ BOOST_AUTO_TEST_CASE(NavigationStream_InitializeCylinders) { NavigationStreamHelper::fillSurfaces(nStreamTemplate, {surface.get()}, Acts::BoundaryTolerance::None()); } - BOOST_CHECK_EQUAL(nStreamTemplate.activeCandidates(), 4u); + BOOST_CHECK_EQUAL(nStreamTemplate.remainingCandidates(), 4u); // (1) Run an initial update - from a position/direction where all are // reachable @@ -246,7 +246,7 @@ BOOST_AUTO_TEST_CASE(NavigationStream_InitializeCylinders) { {Vector3(0., 0., 0.), Vector3(1., 1., 0.).normalized()}, BoundaryTolerance::Infinite())); // We should have 5 candidates, as one cylinder is reachable twice - BOOST_CHECK_EQUAL(nStream.activeCandidates(), 5u); + BOOST_CHECK_EQUAL(nStream.remainingCandidates(), 5u); // First one is inner candidate BOOST_CHECK_EQUAL(&nStream.currentCandidate().surface(), surfaces[2].get()); // Surface of 2nd and 3rd candidate should be the same @@ -261,7 +261,7 @@ BOOST_AUTO_TEST_CASE(NavigationStream_InitializeCylinders) { nStream, gContext, {Vector3(0., 0., 0.), Vector3(1., 0., 0.)}, BoundaryTolerance::Infinite())); // We should have 3 candidates - BOOST_CHECK_EQUAL(nStream.activeCandidates(), 3u); + BOOST_CHECK_EQUAL(nStream.remainingCandidates(), 3u); // (3) Run an initial update - from a position/direction where only the // concentric ones within bounds are reachable @@ -270,7 +270,7 @@ BOOST_AUTO_TEST_CASE(NavigationStream_InitializeCylinders) { nStream, gContext, {Vector3(0., 0., 0.), Vector3(1., 0., 0.)}, BoundaryTolerance::None())); // We should have 2 candidates - BOOST_CHECK_EQUAL(nStream.activeCandidates(), 2u); + BOOST_CHECK_EQUAL(nStream.remainingCandidates(), 2u); // (4) Run an initial update - from a position/direction where none are // reachable @@ -280,7 +280,7 @@ BOOST_AUTO_TEST_CASE(NavigationStream_InitializeCylinders) { nStream, gContext, {Vector3(0., 0., 0.), Vector3(0., 0., 1.)}, BoundaryTolerance::None())); // We should have 0 candidates - BOOST_CHECK_EQUAL(nStream.activeCandidates(), 0u); + BOOST_CHECK_EQUAL(nStream.remainingCandidates(), 0u); BOOST_CHECK_THROW(nStream.currentCandidate(), std::out_of_range); } From 643cd98cf66735cf715b0b94ee7d4f7025c3d942 Mon Sep 17 00:00:00 2001 From: Andreas Salzburger Date: Thu, 29 Aug 2024 09:52:06 +0200 Subject: [PATCH 10/13] addressing PR comments --- .../Acts/Navigation/NavigationStream.hpp | 106 ++++++++++++++--- .../Navigation/NavigationStreamHelper.hpp | 108 ------------------ Core/src/Navigation/CMakeLists.txt | 2 +- ...nStreamHelper.cpp => NavigationStream.cpp} | 79 +++++++++---- .../Core/Navigation/NavigationStreamTests.cpp | 92 ++++++++------- 5 files changed, 189 insertions(+), 198 deletions(-) delete mode 100644 Core/include/Acts/Navigation/NavigationStreamHelper.hpp rename Core/src/Navigation/{NavigationStreamHelper.cpp => NavigationStream.cpp} (63%) diff --git a/Core/include/Acts/Navigation/NavigationStream.hpp b/Core/include/Acts/Navigation/NavigationStream.hpp index 785fff2ab28..45e1a30919f 100644 --- a/Core/include/Acts/Navigation/NavigationStream.hpp +++ b/Core/include/Acts/Navigation/NavigationStream.hpp @@ -8,6 +8,8 @@ #pragma once +#include "Acts/Definitions/Algebra.hpp" +#include "Acts/Geometry/GeometryContext.hpp" #include "Acts/Surfaces/BoundaryTolerance.hpp" #include "Acts/Utilities/Intersection.hpp" @@ -24,14 +26,14 @@ using namespace Experimental; class Surface; -/// @brief The NavigationStream is a container for the navigation candidates +/// The NavigationStream is a container for the navigation candidates that +/// are currentlu processed in a given context. The context could be local to a +/// volume, or global to an entire track following. /// /// The current candidates are stored in a vector of candidates, where an index /// is used to indicate the current active candidate. -/// -/// @todo the NavigationStream should hold also the current volume it is in -/// if it represents the geometry stream. -struct NavigationStream { +class NavigationStream { + public: /// The query point for the navigation stream /// /// This holds the position and direction from which the navigation stream @@ -56,8 +58,6 @@ struct NavigationStream { const Portal* portal = nullptr; /// The boundary tolerance BoundaryTolerance bTolerance = BoundaryTolerance::None(); - /// Target flag: true if this Candidate represents an abortTarget - bool abortTarget = false; /// Convenience access to surface const Surface& surface() const { return *intersection.object(); } /// Cinvencience access to the path length @@ -76,18 +76,12 @@ struct NavigationStream { } }; - /// The candidates of this navigation stream - std::vector candidates; - - /// The currently active candidate - std::size_t currentIndex = 0u; - /// Switch to next next candidate /// /// @return true if a next candidate is available bool switchToNextCandidate() { - if (currentIndex < candidates.size()) { - ++currentIndex; + if (m_currentIndex < m_candidates.size()) { + ++m_currentIndex; return true; } return false; @@ -95,16 +89,92 @@ struct NavigationStream { /// Const access the current candidate const Candidate& currentCandidate() const { - return candidates.at(currentIndex); + return m_candidates.at(m_currentIndex); } + /// Current Index + std::size_t currentIndex() const { return m_currentIndex; } + + /// Non-cost access the candidate vector + std::vector& candidates() { return m_candidates; } + + /// Const access the candidate vector + const std::vector& candidates() const { return m_candidates; } + /// Non-cost access the current candidate - Candidate& currentCandidate() { return candidates.at(currentIndex); } + /// + /// This will throw and out of bounds exception if the stream is not + /// valid anymore. + Candidate& currentCandidate() { return m_candidates.at(m_currentIndex); } /// The number of active candidates std::size_t remainingCandidates() const { - return (candidates.size() - currentIndex); + return (m_candidates.size() - m_currentIndex); } + + /// Fill one surface into the candidate vector + /// + /// @param surface the surface to be filled + /// @param bTolerance the boundary tolerance used for the intersection + void addSurfaceCandidate(const Surface* surface, + BoundaryTolerance bTolerance); + + /// Fill n surfaces into the candidate vector + /// + /// @param surfaces the surfaces that are filled in + /// @param bTolerance the boundary tolerance used for the intersection + void addSurfaceCandidates(const std::vector& surfaces, + BoundaryTolerance bTolerance); + + /// Fill one portal into the candidate vector + /// + /// @param nStream the navigation stream that is being filled + /// @param portal the portals that are filled in + void addPortalCandidate(const Portal* portal); + + /// Fill n portals into the candidate vector + /// + /// @param nStream the navigation stream that is being filled + /// @param portals the portals that are filled in + void addPortalCandidates(const std::vector& portals); + + /// Initialize the stream from a query point + /// + /// @param gctx is the geometry context + /// @param queryPoint holds current position, direction, etc. + /// @param cTolerance is the candidate search tolerance + /// @param onSurfaceTolerance is the tolerance for on-surface intersections + /// + /// This method will first de-duplicate the candidates on basis of the surface + /// pointer to make sure that the multi-intersections are handled correctly. + /// This will allow intializeStream() to be called even as a re-initialization + /// and still work correctly with at one time valid candidates. + /// + /// @return true if the stream is active, false indicates that there are no valid candidates + bool initialize(const GeometryContext& gctx, + const NavigationStream::QueryPoint& queryPoint, + BoundaryTolerance cTolerance, + ActsScalar onSurfaceTolerance = s_onSurfaceTolerance); + + /// Convenience method to update a stream from a new query point, + /// this could be called from navigation delegates that do not require + /// a local state or from the navigator on the target stream + /// + /// @param gctx is the geometry context + /// @param queryPoint holds current position, direction, etc. + /// @param onSurfaceTolerance is the tolerance for on-surface intersections + /// + /// @return true if the stream is active, false indicate no valid candidates left + bool update(const GeometryContext& gctx, + const NavigationStream::QueryPoint& queryPoint, + ActsScalar onSurfaceTolerance = s_onSurfaceTolerance); + + private: + /// The candidates of this navigation stream + std::vector m_candidates; + + /// The currently active candidate + std::size_t m_currentIndex = 0u; }; } // namespace Acts diff --git a/Core/include/Acts/Navigation/NavigationStreamHelper.hpp b/Core/include/Acts/Navigation/NavigationStreamHelper.hpp deleted file mode 100644 index 60936a417cd..00000000000 --- a/Core/include/Acts/Navigation/NavigationStreamHelper.hpp +++ /dev/null @@ -1,108 +0,0 @@ -// This file is part of the Acts project. -// -// Copyright (C) 2024 CERN for the benefit of the Acts project -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. - -#pragma once - -#include "Acts/Definitions/Algebra.hpp" -#include "Acts/Detector/Portal.hpp" -#include "Acts/Geometry/GeometryContext.hpp" -#include "Acts/Navigation/NavigationStream.hpp" -#include "Acts/Surfaces/BoundaryTolerance.hpp" -#include "Acts/Utilities/Intersection.hpp" - -#include - -namespace Acts { - -using namespace Experimental; - -class Surface; - -namespace NavigationStreamHelper { - -/// Helper struct that allows to fill a surface into the candidate vector -/// -/// @param nStream the navigation stream that is being filled -/// @param surface the surface to be filled -/// @param bTolerance the boundary tolerance used for the intersection -/// @param abortTarget a boolean that indicates if this surface is an abort target -inline static void fillSurface(NavigationStream& nStream, - const Surface* surface, - BoundaryTolerance bTolerance, - bool abortTarget = false) { - nStream.candidates.push_back( - NavigationStream::Candidate{ObjectIntersection::invalid(surface), - nullptr, bTolerance, abortTarget}); -} - -/// Helper struct that allows to fill surfaces into the candidate vector -/// -/// @param nStream the navigation stream that is being filled -/// @param surfaces the surfaces that are filled in -/// @param bTolerance the boundary tolerance used for the intersection -/// @param abortTarget a boolean that indicates if those surfaces are abort targets -inline static void fillSurfaces(NavigationStream& nStream, - const std::vector& surfaces, - BoundaryTolerance bTolerance, - bool abortTarget = false) { - std::for_each(surfaces.begin(), surfaces.end(), [&](const auto& s) { - nStream.candidates.push_back( - NavigationStream::Candidate{ObjectIntersection::invalid(s), - nullptr, bTolerance, abortTarget}); - }); -} - -/// Helper struct that allows to fill portals into the candidate vector -/// -/// @param nStream the navigation stream that is being filled -/// @param portals the portals that are filled in -inline static void fillPortals(NavigationStream& nStream, - const std::vector& portals) { - std::for_each(portals.begin(), portals.end(), [&](const auto& p) { - nStream.candidates.push_back(NavigationStream::Candidate{ - ObjectIntersection::invalid(&(p->surface())), p, - BoundaryTolerance::None()}); - }); -} - -/// Initialize a stream that does not require a state object -/// -/// @param stream [in, out] is the navigation stream to be updated -/// @param gctx is the geometry context -/// @param queryPoint holds current position, direction, etc. -/// @param cTolerance is the candidate search tolerance -/// @param onSurfaceTolerance is the tolerance for on-surface intersections -/// -/// This method will first de-duplicate the candidates on basis of the surface -/// pointer to make sure that the multi-intersections are handled correctly. -/// This will allow intializeStream() to be called even as a re-initialization -/// and still work correctly with at one time valid candidates. -/// -/// @return true if the stream is active, false indicates that there are no valid candidates -bool initializeStream(NavigationStream& stream, const GeometryContext& gctx, - const NavigationStream::QueryPoint& queryPoint, - BoundaryTolerance cTolerance, - ActsScalar onSurfaceTolerance = s_onSurfaceTolerance); - -/// Convenience method to update a stream from a new query point, -/// this could be called from navigation delegates that do not require -/// a local state or from the navigator on the target stream -/// -/// @param stream [in, out] is the navigation stream to be updated -/// @param gctx is the geometry context -/// @param queryPoint holds current position, direction, etc. -/// @param onSurfaceTolerance is the tolerance for on-surface intersections -/// -/// @return true if the stream is active, false indicate no valid candidates left -bool updateStream(NavigationStream& stream, const GeometryContext& gctx, - const NavigationStream::QueryPoint& queryPoint, - ActsScalar onSurfaceTolerance = s_onSurfaceTolerance); - -} // namespace NavigationStreamHelper - -} // namespace Acts diff --git a/Core/src/Navigation/CMakeLists.txt b/Core/src/Navigation/CMakeLists.txt index db5b48b417e..81cbf616d44 100644 --- a/Core/src/Navigation/CMakeLists.txt +++ b/Core/src/Navigation/CMakeLists.txt @@ -1 +1 @@ -target_sources(ActsCore PRIVATE NavigationStreamHelper.cpp) +target_sources(ActsCore PRIVATE NavigationStream.cpp) diff --git a/Core/src/Navigation/NavigationStreamHelper.cpp b/Core/src/Navigation/NavigationStream.cpp similarity index 63% rename from Core/src/Navigation/NavigationStreamHelper.cpp rename to Core/src/Navigation/NavigationStream.cpp index 4d61c558f15..7bbebf27593 100644 --- a/Core/src/Navigation/NavigationStreamHelper.cpp +++ b/Core/src/Navigation/NavigationStream.cpp @@ -6,12 +6,12 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -#include "Acts/Navigation/NavigationStreamHelper.hpp" - +#include "Acts/Navigation/NavigationStream.hpp" +#include "Acts/Detector/Portal.hpp" #include "Acts/Surfaces/Surface.hpp" -bool Acts::NavigationStreamHelper::initializeStream( - NavigationStream& stream, const GeometryContext& gctx, +bool Acts::NavigationStream::initialize( +const GeometryContext& gctx, const NavigationStream::QueryPoint& queryPoint, BoundaryTolerance cTolerance, ActsScalar onSurfaceTolerance) { // Position and direction from the query point @@ -20,25 +20,25 @@ bool Acts::NavigationStreamHelper::initializeStream( // De-duplicate first (necessary to deal correctly with multiple // intersections) - sort them by surface pointer - std::sort(stream.candidates.begin(), stream.candidates.end(), + std::sort(m_candidates.begin(), m_candidates.end(), [](const NavigationStream::Candidate& a, const NavigationStream::Candidate& b) { return (&a.surface()) < (&b.surface()); }); // Remove duplicates on basis of the surface pointer - stream.candidates.erase( - std::unique(stream.candidates.begin(), stream.candidates.end(), + m_candidates.erase( + std::unique(m_candidates.begin(), m_candidates.end(), [](const NavigationStream::Candidate& a, const NavigationStream::Candidate& b) { return (&a.surface()) == (&b.surface()); }), - stream.candidates.end()); + m_candidates.end()); // A container collecting additional candidates from multiple // valid interseciton std::vector additionalCandidates = {}; - for (auto& [sIntersection, portal, bTolerance, abortTarget] : - stream.candidates) { + for (auto& [sIntersection, portal, bTolerance ] : + m_candidates) { // Get the surface from the object intersection const Surface* surface = sIntersection.object(); // Intersect the surface @@ -67,35 +67,35 @@ bool Acts::NavigationStreamHelper::initializeStream( } // Append the multi intersection candidates - stream.candidates.insert(stream.candidates.end(), + m_candidates.insert(m_candidates.end(), additionalCandidates.begin(), additionalCandidates.end()); // Sort the candidates by path length - std::sort(stream.candidates.begin(), stream.candidates.end(), + std::sort(m_candidates.begin(), m_candidates.end(), NavigationStream::Candidate::pathLengthOrder); // The we find the first invalid candidate auto firstInvalid = std::find_if( - stream.candidates.begin(), stream.candidates.end(), + m_candidates.begin(), m_candidates.end(), [](const NavigationStream::Candidate& a) { - const auto& [aIntersection, aPortal, aTolerance, abortTarget] = a; + const auto& [aIntersection, aPortal, aTolerance ] = a; return !aIntersection.isValid(); }); // Set the range and initialize - stream.candidates.resize( - std::distance(stream.candidates.begin(), firstInvalid)); + m_candidates.resize( + std::distance(m_candidates.begin(), firstInvalid)); - stream.currentIndex = 0; - if (stream.candidates.empty()) { + m_currentIndex = 0; + if (m_candidates.empty()) { return false; } return true; } -bool Acts::NavigationStreamHelper::updateStream( - NavigationStream& stream, const GeometryContext& gctx, +bool Acts::NavigationStream::update( + const GeometryContext& gctx, const NavigationStream::QueryPoint& queryPoint, ActsScalar onSurfaceTolerance) { // Position and direction from the query point @@ -103,11 +103,10 @@ bool Acts::NavigationStreamHelper::updateStream( const Vector3& direction = queryPoint.direction; // Loop over the (currently valid) candidates and update - for (std::size_t& index = stream.currentIndex; - index < stream.candidates.size(); ++index) { + for (;m_currentIndex < m_candidates.size(); ++m_currentIndex) { // Get the candidate, and resolve the tuple - NavigationStream::Candidate& candidate = stream.currentCandidate(); - auto& [sIntersection, portal, bTolerance, abortTarget] = candidate; + NavigationStream::Candidate& candidate = currentCandidate(); + auto& [sIntersection, portal, bTolerance] = candidate; // Get the surface from the object intersection const Surface* surface = sIntersection.object(); // (re-)Intersect the surface @@ -129,3 +128,35 @@ bool Acts::NavigationStreamHelper::updateStream( // No candidate was reachable return false; } + + + void Acts::NavigationStream::addSurfaceCandidate(const Surface* surface, + BoundaryTolerance bTolerance) { + m_candidates.push_back(Candidate{ + ObjectIntersection::invalid(surface), nullptr, bTolerance}); + } + + +void Acts::NavigationStream::addSurfaceCandidates(const std::vector& surfaces, + BoundaryTolerance bTolerance) { + std::for_each(surfaces.begin(), surfaces.end(), [&](const auto* surface) { + m_candidates.push_back(Candidate{ObjectIntersection::invalid(surface), + nullptr, bTolerance}); + }); + } + +void Acts::NavigationStream::addPortalCandidate( + const Portal* portal) { + m_candidates.push_back(Candidate{ + ObjectIntersection::invalid(&(portal->surface())), portal, + BoundaryTolerance::None()}); + } + +void Acts::NavigationStream::addPortalCandidates( + const std::vector& portals) { + std::for_each(portals.begin(), portals.end(), [&](const auto& portal) { + m_candidates.push_back(Candidate{ + ObjectIntersection::invalid(&(portal->surface())), portal, + BoundaryTolerance::None()}); + }); + } \ No newline at end of file diff --git a/Tests/UnitTests/Core/Navigation/NavigationStreamTests.cpp b/Tests/UnitTests/Core/Navigation/NavigationStreamTests.cpp index 900502d0442..5ac91ca72bb 100644 --- a/Tests/UnitTests/Core/Navigation/NavigationStreamTests.cpp +++ b/Tests/UnitTests/Core/Navigation/NavigationStreamTests.cpp @@ -9,7 +9,6 @@ #include #include "Acts/Navigation/NavigationStream.hpp" -#include "Acts/Navigation/NavigationStreamHelper.hpp" #include "Acts/Surfaces/CylinderSurface.hpp" #include "Acts/Surfaces/PlaneSurface.hpp" #include "Acts/Surfaces/RectangleBounds.hpp" @@ -95,18 +94,18 @@ BOOST_AUTO_TEST_CASE(NavigationStream_InitializePlanes) { NavigationStream nStreamTemplate; for (const auto& surface : surfaces) { - NavigationStreamHelper::fillSurfaces(nStreamTemplate, {surface.get()}, - Acts::BoundaryTolerance::None()); + nStreamTemplate.addSurfaceCandidate(surface.get(), + Acts::BoundaryTolerance::None()); } - BOOST_CHECK_EQUAL(nStreamTemplate.candidates.size(), 4); + BOOST_CHECK_EQUAL(nStreamTemplate.remainingCandidates(), 4u); // (1) Run an initial update // - from a position where all are reachable and valid // - with infinite boundary tolerance NavigationStream nStream = nStreamTemplate; - BOOST_CHECK(NavigationStreamHelper::initializeStream( - nStream, gContext, {Vector3(0., 0., -30.), Vector3(0., 0., 1.)}, - BoundaryTolerance::Infinite())); + BOOST_CHECK(nStream.initialize(gContext, + {Vector3(0., 0., -30.), Vector3(0., 0., 1.)}, + BoundaryTolerance::Infinite())); BOOST_CHECK_EQUAL(nStream.remainingCandidates(), 4u); BOOST_CHECK_EQUAL(&nStream.currentCandidate().surface(), surfaces[1u].get()); @@ -115,9 +114,9 @@ BOOST_AUTO_TEST_CASE(NavigationStream_InitializePlanes) { // - from a position where all but one are reachable // - with infinite boundary tolerance nStream = nStreamTemplate; - BOOST_CHECK(NavigationStreamHelper::initializeStream( - nStream, gContext, {Vector3(0., 0., 0.), Vector3(0., 0., 1.)}, - BoundaryTolerance::Infinite())); + BOOST_CHECK(nStream.initialize(gContext, + {Vector3(0., 0., 0.), Vector3(0., 0., 1.)}, + BoundaryTolerance::Infinite())); BOOST_CHECK_EQUAL(nStream.remainingCandidates(), 3u); BOOST_CHECK_EQUAL(&nStream.currentCandidate().surface(), surfaces[3u].get()); @@ -125,30 +124,30 @@ BOOST_AUTO_TEST_CASE(NavigationStream_InitializePlanes) { // - from a position where all would be reachable, but // - with no boundary tolerance nStream = nStreamTemplate; - BOOST_CHECK(NavigationStreamHelper::initializeStream( - nStream, gContext, {Vector3(0., 0., -100.), Vector3(0., 0., 1.)}, - BoundaryTolerance::None())); + BOOST_CHECK(nStream.initialize(gContext, + {Vector3(0., 0., -100.), Vector3(0., 0., 1.)}, + BoundaryTolerance::None())); BOOST_CHECK_EQUAL(nStream.remainingCandidates(), 3u); // (4) Run an initial update // - none of the surfaces should be reachable nStream = nStreamTemplate; - BOOST_CHECK(!NavigationStreamHelper::initializeStream( - nStream, gContext, {Vector3(0., 0., 0.), Vector3(1., 0., 0.)}, - BoundaryTolerance::Infinite())); + BOOST_CHECK(!nStream.initialize(gContext, + {Vector3(0., 0., 0.), Vector3(1., 0., 0.)}, + BoundaryTolerance::Infinite())); BOOST_CHECK_EQUAL(nStream.remainingCandidates(), 0u); BOOST_CHECK_THROW(nStream.currentCandidate(), std::out_of_range); // (5) Test de-duplication nStream = nStreamTemplate; - NavigationStreamHelper::fillSurfaces(nStreamTemplate, {surfaces[0].get()}, - Acts::BoundaryTolerance::None()); + nStreamTemplate.addSurfaceCandidate(surfaces[0].get(), + Acts::BoundaryTolerance::None()); // One surface is duplicated in the stream - BOOST_CHECK_EQUAL(nStreamTemplate.candidates.size(), 5u); + BOOST_CHECK_EQUAL(nStreamTemplate.remainingCandidates(), 5u); // Initialize stream reaches all surfaces, but also de-duplicates - BOOST_CHECK(NavigationStreamHelper::initializeStream( - nStream, gContext, {Vector3(0., 0., -100.), Vector3(0., 0., 1.)}, - BoundaryTolerance::Infinite())); + BOOST_CHECK(nStream.initialize(gContext, + {Vector3(0., 0., -100.), Vector3(0., 0., 1.)}, + BoundaryTolerance::Infinite())); BOOST_CHECK_EQUAL(nStream.remainingCandidates(), 4u); } @@ -160,10 +159,10 @@ BOOST_AUTO_TEST_CASE(NavigationStream_UpdatePlanes) { // reachable and intersections inside bounds NavigationStream nStreamTemplate; for (const auto& surface : surfaces) { - NavigationStreamHelper::fillSurfaces(nStreamTemplate, {surface.get()}, - Acts::BoundaryTolerance::None()); + nStreamTemplate.addSurfaceCandidate(surface.get(), + Acts::BoundaryTolerance::None()); } - BOOST_CHECK_EQUAL(nStreamTemplate.candidates.size(), 4); + BOOST_CHECK_EQUAL(nStreamTemplate.remainingCandidates(), 4u); // Run an initial update // - from a position where all are reachable and valid @@ -172,8 +171,8 @@ BOOST_AUTO_TEST_CASE(NavigationStream_UpdatePlanes) { Vector3(0., 0., 1.)}; NavigationStream nStream = nStreamTemplate; - BOOST_CHECK(NavigationStreamHelper::initializeStream( - nStream, gContext, qPoint, BoundaryTolerance::Infinite())); + BOOST_CHECK( + nStream.initialize(gContext, qPoint, BoundaryTolerance::Infinite())); BOOST_CHECK_EQUAL(nStream.remainingCandidates(), 4u); BOOST_CHECK_EQUAL(&nStream.currentCandidate().surface(), surfaces[1u].get()); CHECK_CLOSE_ABS(nStream.currentCandidate().pathLength(), 10., @@ -181,7 +180,7 @@ BOOST_AUTO_TEST_CASE(NavigationStream_UpdatePlanes) { // Let's push a bit closer to the surface qPoint.position = Vector3(0., 0., -22.); - BOOST_CHECK(NavigationStreamHelper::updateStream(nStream, gContext, qPoint)); + BOOST_CHECK(nStream.update(gContext, qPoint)); // Surface unchanged, but the intersection should be closer BOOST_CHECK_EQUAL(&nStream.currentCandidate().surface(), surfaces[1u].get()); CHECK_CLOSE_ABS(nStream.currentCandidate().pathLength(), 2., @@ -189,7 +188,7 @@ BOOST_AUTO_TEST_CASE(NavigationStream_UpdatePlanes) { // Uuuups, an overstep qPoint.position = Vector3(0., 0., -19.5); - BOOST_CHECK(NavigationStreamHelper::updateStream(nStream, gContext, qPoint)); + BOOST_CHECK(nStream.update(gContext, qPoint)); // Surface still unchanged, but pathLength is now negative BOOST_CHECK_EQUAL(&nStream.currentCandidate().surface(), surfaces[1u].get()); CHECK_CLOSE_ABS(nStream.currentCandidate().pathLength(), -0.5, @@ -197,7 +196,7 @@ BOOST_AUTO_TEST_CASE(NavigationStream_UpdatePlanes) { // Finally hit it qPoint.position = Vector3(0., 0., -20.); - BOOST_CHECK(NavigationStreamHelper::updateStream(nStream, gContext, qPoint)); + BOOST_CHECK(nStream.update(gContext, qPoint)); // Surface still unchanged, however, now withL // - pathlength smaller on surface tolerance, intersection status onSurface BOOST_CHECK_EQUAL(&nStream.currentCandidate().surface(), surfaces[1u].get()); @@ -216,13 +215,13 @@ BOOST_AUTO_TEST_CASE(NavigationStream_UpdatePlanes) { // Query update will re-evaluate this one: however, we will miss the surface // due to outside bounds - and will switch to the next candidate: which sits // at 200 and then will yield 220 - BOOST_CHECK(NavigationStreamHelper::updateStream(nStream, gContext, qPoint)); + BOOST_CHECK(nStream.update(gContext, qPoint)); CHECK_CLOSE_ABS(nStream.currentCandidate().pathLength(), 220., std::numeric_limits::epsilon()); // Oh noooo, an actor just kicked in and changed the direction qPoint.direction = Vector3(0., 1., 1.).normalized(); // All is lost, no surface is reachable anymore - BOOST_CHECK(!NavigationStreamHelper::updateStream(nStream, gContext, qPoint)); + BOOST_CHECK(!nStream.update(gContext, qPoint)); } BOOST_AUTO_TEST_CASE(NavigationStream_InitializeCylinders) { @@ -232,7 +231,7 @@ BOOST_AUTO_TEST_CASE(NavigationStream_InitializeCylinders) { // Let us fill the surfaces into the navigation stream NavigationStream nStreamTemplate; for (const auto& surface : surfaces) { - NavigationStreamHelper::fillSurfaces(nStreamTemplate, {surface.get()}, + nStreamTemplate.addSurfaceCandidates({surface.get()}, Acts::BoundaryTolerance::None()); } BOOST_CHECK_EQUAL(nStreamTemplate.remainingCandidates(), 4u); @@ -241,34 +240,33 @@ BOOST_AUTO_TEST_CASE(NavigationStream_InitializeCylinders) { // reachable // - with infinite boundary tolerance NavigationStream nStream = nStreamTemplate; - BOOST_CHECK(NavigationStreamHelper::initializeStream( - nStream, gContext, - {Vector3(0., 0., 0.), Vector3(1., 1., 0.).normalized()}, + BOOST_CHECK(nStream.initialize( + gContext, {Vector3(0., 0., 0.), Vector3(1., 1., 0.).normalized()}, BoundaryTolerance::Infinite())); // We should have 5 candidates, as one cylinder is reachable twice BOOST_CHECK_EQUAL(nStream.remainingCandidates(), 5u); // First one is inner candidate BOOST_CHECK_EQUAL(&nStream.currentCandidate().surface(), surfaces[2].get()); // Surface of 2nd and 3rd candidate should be the same - BOOST_CHECK_EQUAL(&nStream.candidates[1].surface(), surfaces[1].get()); - BOOST_CHECK_EQUAL(&nStream.candidates[2].surface(), surfaces[1].get()); + BOOST_CHECK_EQUAL(&nStream.candidates()[1u].surface(), surfaces[1].get()); + BOOST_CHECK_EQUAL(&nStream.candidates()[2u].surface(), surfaces[1].get()); // (2) Run an initial update - from a position/direction where only // the concentric ones are reachable // - with infinite boundary tolerance nStream = nStreamTemplate; - BOOST_CHECK(NavigationStreamHelper::initializeStream( - nStream, gContext, {Vector3(0., 0., 0.), Vector3(1., 0., 0.)}, - BoundaryTolerance::Infinite())); + BOOST_CHECK(nStream.initialize(gContext, + {Vector3(0., 0., 0.), Vector3(1., 0., 0.)}, + BoundaryTolerance::Infinite())); // We should have 3 candidates BOOST_CHECK_EQUAL(nStream.remainingCandidates(), 3u); // (3) Run an initial update - from a position/direction where only the // concentric ones within bounds are reachable nStream = nStreamTemplate; - BOOST_CHECK(NavigationStreamHelper::initializeStream( - nStream, gContext, {Vector3(0., 0., 0.), Vector3(1., 0., 0.)}, - BoundaryTolerance::None())); + BOOST_CHECK(nStream.initialize(gContext, + {Vector3(0., 0., 0.), Vector3(1., 0., 0.)}, + BoundaryTolerance::None())); // We should have 2 candidates BOOST_CHECK_EQUAL(nStream.remainingCandidates(), 2u); @@ -276,9 +274,9 @@ BOOST_AUTO_TEST_CASE(NavigationStream_InitializeCylinders) { // reachable // - (even) with infinite boundary tolerance nStream = nStreamTemplate; - BOOST_CHECK(!NavigationStreamHelper::initializeStream( - nStream, gContext, {Vector3(0., 0., 0.), Vector3(0., 0., 1.)}, - BoundaryTolerance::None())); + BOOST_CHECK(!nStream.initialize(gContext, + {Vector3(0., 0., 0.), Vector3(0., 0., 1.)}, + BoundaryTolerance::None())); // We should have 0 candidates BOOST_CHECK_EQUAL(nStream.remainingCandidates(), 0u); BOOST_CHECK_THROW(nStream.currentCandidate(), std::out_of_range); From 92e39a7726cf9e60508fd75d9567544fb0c57728 Mon Sep 17 00:00:00 2001 From: Andreas Salzburger Date: Thu, 29 Aug 2024 09:53:47 +0200 Subject: [PATCH 11/13] run pre-commit --- Core/src/Navigation/NavigationStream.cpp | 92 +++++++++++------------- 1 file changed, 42 insertions(+), 50 deletions(-) diff --git a/Core/src/Navigation/NavigationStream.cpp b/Core/src/Navigation/NavigationStream.cpp index 7bbebf27593..5632aefab31 100644 --- a/Core/src/Navigation/NavigationStream.cpp +++ b/Core/src/Navigation/NavigationStream.cpp @@ -7,12 +7,12 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. #include "Acts/Navigation/NavigationStream.hpp" + #include "Acts/Detector/Portal.hpp" #include "Acts/Surfaces/Surface.hpp" bool Acts::NavigationStream::initialize( -const GeometryContext& gctx, - const NavigationStream::QueryPoint& queryPoint, + const GeometryContext& gctx, const NavigationStream::QueryPoint& queryPoint, BoundaryTolerance cTolerance, ActsScalar onSurfaceTolerance) { // Position and direction from the query point const Vector3& position = queryPoint.position; @@ -26,19 +26,17 @@ const GeometryContext& gctx, return (&a.surface()) < (&b.surface()); }); // Remove duplicates on basis of the surface pointer - m_candidates.erase( - std::unique(m_candidates.begin(), m_candidates.end(), - [](const NavigationStream::Candidate& a, - const NavigationStream::Candidate& b) { - return (&a.surface()) == (&b.surface()); - }), - m_candidates.end()); + m_candidates.erase(std::unique(m_candidates.begin(), m_candidates.end(), + [](const NavigationStream::Candidate& a, + const NavigationStream::Candidate& b) { + return (&a.surface()) == (&b.surface()); + }), + m_candidates.end()); // A container collecting additional candidates from multiple // valid interseciton std::vector additionalCandidates = {}; - for (auto& [sIntersection, portal, bTolerance ] : - m_candidates) { + for (auto& [sIntersection, portal, bTolerance] : m_candidates) { // Get the surface from the object intersection const Surface* surface = sIntersection.object(); // Intersect the surface @@ -67,25 +65,23 @@ const GeometryContext& gctx, } // Append the multi intersection candidates - m_candidates.insert(m_candidates.end(), - additionalCandidates.begin(), - additionalCandidates.end()); + m_candidates.insert(m_candidates.end(), additionalCandidates.begin(), + additionalCandidates.end()); // Sort the candidates by path length std::sort(m_candidates.begin(), m_candidates.end(), NavigationStream::Candidate::pathLengthOrder); // The we find the first invalid candidate - auto firstInvalid = std::find_if( - m_candidates.begin(), m_candidates.end(), - [](const NavigationStream::Candidate& a) { - const auto& [aIntersection, aPortal, aTolerance ] = a; - return !aIntersection.isValid(); - }); + auto firstInvalid = + std::find_if(m_candidates.begin(), m_candidates.end(), + [](const NavigationStream::Candidate& a) { + const auto& [aIntersection, aPortal, aTolerance] = a; + return !aIntersection.isValid(); + }); // Set the range and initialize - m_candidates.resize( - std::distance(m_candidates.begin(), firstInvalid)); + m_candidates.resize(std::distance(m_candidates.begin(), firstInvalid)); m_currentIndex = 0; if (m_candidates.empty()) { @@ -95,15 +91,14 @@ const GeometryContext& gctx, } bool Acts::NavigationStream::update( - const GeometryContext& gctx, - const NavigationStream::QueryPoint& queryPoint, + const GeometryContext& gctx, const NavigationStream::QueryPoint& queryPoint, ActsScalar onSurfaceTolerance) { // Position and direction from the query point const Vector3& position = queryPoint.position; const Vector3& direction = queryPoint.direction; // Loop over the (currently valid) candidates and update - for (;m_currentIndex < m_candidates.size(); ++m_currentIndex) { + for (; m_currentIndex < m_candidates.size(); ++m_currentIndex) { // Get the candidate, and resolve the tuple NavigationStream::Candidate& candidate = currentCandidate(); auto& [sIntersection, portal, bTolerance] = candidate; @@ -129,34 +124,31 @@ bool Acts::NavigationStream::update( return false; } +void Acts::NavigationStream::addSurfaceCandidate(const Surface* surface, + BoundaryTolerance bTolerance) { + m_candidates.push_back(Candidate{ + ObjectIntersection::invalid(surface), nullptr, bTolerance}); +} - void Acts::NavigationStream::addSurfaceCandidate(const Surface* surface, - BoundaryTolerance bTolerance) { +void Acts::NavigationStream::addSurfaceCandidates( + const std::vector& surfaces, BoundaryTolerance bTolerance) { + std::for_each(surfaces.begin(), surfaces.end(), [&](const auto* surface) { m_candidates.push_back(Candidate{ ObjectIntersection::invalid(surface), nullptr, bTolerance}); - } - - -void Acts::NavigationStream::addSurfaceCandidates(const std::vector& surfaces, - BoundaryTolerance bTolerance) { - std::for_each(surfaces.begin(), surfaces.end(), [&](const auto* surface) { - m_candidates.push_back(Candidate{ObjectIntersection::invalid(surface), - nullptr, bTolerance}); - }); - } + }); +} -void Acts::NavigationStream::addPortalCandidate( - const Portal* portal) { - m_candidates.push_back(Candidate{ - ObjectIntersection::invalid(&(portal->surface())), portal, - BoundaryTolerance::None()}); - } +void Acts::NavigationStream::addPortalCandidate(const Portal* portal) { + m_candidates.push_back( + Candidate{ObjectIntersection::invalid(&(portal->surface())), + portal, BoundaryTolerance::None()}); +} void Acts::NavigationStream::addPortalCandidates( - const std::vector& portals) { - std::for_each(portals.begin(), portals.end(), [&](const auto& portal) { - m_candidates.push_back(Candidate{ - ObjectIntersection::invalid(&(portal->surface())), portal, - BoundaryTolerance::None()}); - }); - } \ No newline at end of file + const std::vector& portals) { + std::for_each(portals.begin(), portals.end(), [&](const auto& portal) { + m_candidates.push_back( + Candidate{ObjectIntersection::invalid(&(portal->surface())), + portal, BoundaryTolerance::None()}); + }); +} From 586ed6a8b9bb6f4797743976cefdd33234e4f7c2 Mon Sep 17 00:00:00 2001 From: Andreas Salzburger Date: Thu, 29 Aug 2024 11:36:53 +0200 Subject: [PATCH 12/13] fix doc run --- Core/include/Acts/Navigation/NavigationStream.hpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/Core/include/Acts/Navigation/NavigationStream.hpp b/Core/include/Acts/Navigation/NavigationStream.hpp index 45e1a30919f..b267ec47472 100644 --- a/Core/include/Acts/Navigation/NavigationStream.hpp +++ b/Core/include/Acts/Navigation/NavigationStream.hpp @@ -128,13 +128,11 @@ class NavigationStream { /// Fill one portal into the candidate vector /// - /// @param nStream the navigation stream that is being filled /// @param portal the portals that are filled in void addPortalCandidate(const Portal* portal); /// Fill n portals into the candidate vector /// - /// @param nStream the navigation stream that is being filled /// @param portals the portals that are filled in void addPortalCandidates(const std::vector& portals); From a40b6de4b4151a8c4eb67069257b1729f0d4442e Mon Sep 17 00:00:00 2001 From: Andreas Salzburger Date: Thu, 29 Aug 2024 14:19:48 +0200 Subject: [PATCH 13/13] removing most of the SonarCloud issues --- .../Acts/Navigation/NavigationStream.hpp | 6 +- Core/src/Navigation/NavigationStream.cpp | 69 +++++++++---------- 2 files changed, 37 insertions(+), 38 deletions(-) diff --git a/Core/include/Acts/Navigation/NavigationStream.hpp b/Core/include/Acts/Navigation/NavigationStream.hpp index b267ec47472..f23fed9087c 100644 --- a/Core/include/Acts/Navigation/NavigationStream.hpp +++ b/Core/include/Acts/Navigation/NavigationStream.hpp @@ -117,14 +117,14 @@ class NavigationStream { /// @param surface the surface to be filled /// @param bTolerance the boundary tolerance used for the intersection void addSurfaceCandidate(const Surface* surface, - BoundaryTolerance bTolerance); + const BoundaryTolerance& bTolerance); /// Fill n surfaces into the candidate vector /// /// @param surfaces the surfaces that are filled in /// @param bTolerance the boundary tolerance used for the intersection void addSurfaceCandidates(const std::vector& surfaces, - BoundaryTolerance bTolerance); + const BoundaryTolerance& bTolerance); /// Fill one portal into the candidate vector /// @@ -151,7 +151,7 @@ class NavigationStream { /// @return true if the stream is active, false indicates that there are no valid candidates bool initialize(const GeometryContext& gctx, const NavigationStream::QueryPoint& queryPoint, - BoundaryTolerance cTolerance, + const BoundaryTolerance& cTolerance, ActsScalar onSurfaceTolerance = s_onSurfaceTolerance); /// Convenience method to update a stream from a new query point, diff --git a/Core/src/Navigation/NavigationStream.cpp b/Core/src/Navigation/NavigationStream.cpp index 5632aefab31..3d90c5bb0d4 100644 --- a/Core/src/Navigation/NavigationStream.cpp +++ b/Core/src/Navigation/NavigationStream.cpp @@ -11,31 +11,31 @@ #include "Acts/Detector/Portal.hpp" #include "Acts/Surfaces/Surface.hpp" -bool Acts::NavigationStream::initialize( - const GeometryContext& gctx, const NavigationStream::QueryPoint& queryPoint, - BoundaryTolerance cTolerance, ActsScalar onSurfaceTolerance) { +#include + +bool Acts::NavigationStream::initialize(const GeometryContext& gctx, + const QueryPoint& queryPoint, + const BoundaryTolerance& cTolerance, + ActsScalar onSurfaceTolerance) { // Position and direction from the query point const Vector3& position = queryPoint.position; const Vector3& direction = queryPoint.direction; // De-duplicate first (necessary to deal correctly with multiple // intersections) - sort them by surface pointer - std::sort(m_candidates.begin(), m_candidates.end(), - [](const NavigationStream::Candidate& a, - const NavigationStream::Candidate& b) { - return (&a.surface()) < (&b.surface()); - }); + std::ranges::sort(m_candidates, [](const Candidate& a, const Candidate& b) { + return (&a.surface()) < (&b.surface()); + }); // Remove duplicates on basis of the surface pointer m_candidates.erase(std::unique(m_candidates.begin(), m_candidates.end(), - [](const NavigationStream::Candidate& a, - const NavigationStream::Candidate& b) { + [](const Candidate& a, const Candidate& b) { return (&a.surface()) == (&b.surface()); }), m_candidates.end()); // A container collecting additional candidates from multiple // valid interseciton - std::vector additionalCandidates = {}; + std::vector additionalCandidates = {}; for (auto& [sIntersection, portal, bTolerance] : m_candidates) { // Get the surface from the object intersection const Surface* surface = sIntersection.object(); @@ -46,7 +46,7 @@ bool Acts::NavigationStream::initialize( // Split them into valid intersections, keep track of potentially // additional candidates bool originalCandidateUpdated = false; - for (auto& rsIntersection : multiIntersection.split()) { + for (const auto& rsIntersection : multiIntersection.split()) { // Skip negative solutions, respecting the on surface tolerance if (rsIntersection.pathLength() < -onSurfaceTolerance) { continue; @@ -57,8 +57,8 @@ bool Acts::NavigationStream::initialize( sIntersection = rsIntersection; originalCandidateUpdated = true; } else { - additionalCandidates.push_back( - NavigationStream::Candidate{rsIntersection, portal, bTolerance}); + additionalCandidates.emplace_back( + Candidate{rsIntersection, portal, bTolerance}); } } } @@ -69,16 +69,14 @@ bool Acts::NavigationStream::initialize( additionalCandidates.end()); // Sort the candidates by path length - std::sort(m_candidates.begin(), m_candidates.end(), - NavigationStream::Candidate::pathLengthOrder); + std::ranges::sort(m_candidates, Candidate::pathLengthOrder); // The we find the first invalid candidate auto firstInvalid = - std::find_if(m_candidates.begin(), m_candidates.end(), - [](const NavigationStream::Candidate& a) { - const auto& [aIntersection, aPortal, aTolerance] = a; - return !aIntersection.isValid(); - }); + std::ranges::find_if(m_candidates, [](const Candidate& a) { + const auto& [aIntersection, aPortal, aTolerance] = a; + return !aIntersection.isValid(); + }); // Set the range and initialize m_candidates.resize(std::distance(m_candidates.begin(), firstInvalid)); @@ -90,9 +88,9 @@ bool Acts::NavigationStream::initialize( return true; } -bool Acts::NavigationStream::update( - const GeometryContext& gctx, const NavigationStream::QueryPoint& queryPoint, - ActsScalar onSurfaceTolerance) { +bool Acts::NavigationStream::update(const GeometryContext& gctx, + const QueryPoint& queryPoint, + ActsScalar onSurfaceTolerance) { // Position and direction from the query point const Vector3& position = queryPoint.position; const Vector3& direction = queryPoint.direction; @@ -100,7 +98,7 @@ bool Acts::NavigationStream::update( // Loop over the (currently valid) candidates and update for (; m_currentIndex < m_candidates.size(); ++m_currentIndex) { // Get the candidate, and resolve the tuple - NavigationStream::Candidate& candidate = currentCandidate(); + Candidate& candidate = currentCandidate(); auto& [sIntersection, portal, bTolerance] = candidate; // Get the surface from the object intersection const Surface* surface = sIntersection.object(); @@ -108,7 +106,7 @@ bool Acts::NavigationStream::update( auto multiIntersection = surface->intersect(gctx, position, direction, bTolerance, onSurfaceTolerance); // Split them into valid intersections - for (auto& rsIntersection : multiIntersection.split()) { + for (const auto& rsIntersection : multiIntersection.split()) { // Skip wrong index solution if (rsIntersection.index() != sIntersection.index()) { continue; @@ -124,30 +122,31 @@ bool Acts::NavigationStream::update( return false; } -void Acts::NavigationStream::addSurfaceCandidate(const Surface* surface, - BoundaryTolerance bTolerance) { - m_candidates.push_back(Candidate{ +void Acts::NavigationStream::addSurfaceCandidate( + const Surface* surface, const BoundaryTolerance& bTolerance) { + m_candidates.emplace_back(Candidate{ ObjectIntersection::invalid(surface), nullptr, bTolerance}); } void Acts::NavigationStream::addSurfaceCandidates( - const std::vector& surfaces, BoundaryTolerance bTolerance) { - std::for_each(surfaces.begin(), surfaces.end(), [&](const auto* surface) { - m_candidates.push_back(Candidate{ + const std::vector& surfaces, + const BoundaryTolerance& bTolerance) { + std::ranges::for_each(surfaces, [&](const auto* surface) { + m_candidates.emplace_back(Candidate{ ObjectIntersection::invalid(surface), nullptr, bTolerance}); }); } void Acts::NavigationStream::addPortalCandidate(const Portal* portal) { - m_candidates.push_back( + m_candidates.emplace_back( Candidate{ObjectIntersection::invalid(&(portal->surface())), portal, BoundaryTolerance::None()}); } void Acts::NavigationStream::addPortalCandidates( const std::vector& portals) { - std::for_each(portals.begin(), portals.end(), [&](const auto& portal) { - m_candidates.push_back( + std::ranges::for_each(portals, [&](const auto& portal) { + m_candidates.emplace_back( Candidate{ObjectIntersection::invalid(&(portal->surface())), portal, BoundaryTolerance::None()}); });