diff --git a/Core/include/Acts/Geometry/detail/Layer.ipp b/Core/include/Acts/Geometry/detail/Layer.ipp index ed3d3ed1d3b35c5de8168048d88edceeddd6299d..ebbfeb6300a1e0544cf8dedbeaef5ad99d3dd4ad 100644 --- a/Core/include/Acts/Geometry/detail/Layer.ipp +++ b/Core/include/Acts/Geometry/detail/Layer.ipp @@ -246,16 +246,17 @@ const SurfaceIntersection Layer::surfaceOnApproach( sIntersection.intersection.pathLength *= std::copysign(1., options.navDir); return sIntersection; - } else if (sIntersection.alternatives.size() > 0.) { + } else if (sIntersection.alternative.status >= + Intersection::Status::reachable) { // Test the alternative - cLimit = sIntersection.alternatives[0].pathLength; + cLimit = sIntersection.alternative.pathLength; withinLimit = (cLimit > oLimit and cLimit * cLimit <= pLimit * pLimit + s_onSurfaceTolerance); - if (sIntersection.alternatives[0] and withinLimit) { + if (sIntersection.alternative and withinLimit) { // Set the right sign for the path length - sIntersection.alternatives[0].pathLength *= + sIntersection.alternative.pathLength *= std::copysign(1., options.navDir); - return SurfaceIntersection(sIntersection.alternatives[0], + return SurfaceIntersection(sIntersection.alternative, sIntersection.object); } } diff --git a/Core/include/Acts/Geometry/detail/TrackingVolume.ipp b/Core/include/Acts/Geometry/detail/TrackingVolume.ipp index a15dd93143347823208f44373938735c327fa917..16a5042eb13fe45869ecbbe7995ccbf49eb54526 100644 --- a/Core/include/Acts/Geometry/detail/TrackingVolume.ipp +++ b/Core/include/Acts/Geometry/detail/TrackingVolume.ipp @@ -110,15 +110,15 @@ std::vector<BoundaryIntersection> TrackingVolume::compatibleBoundaries( sIntersection.object); } // Check the alternative - if (!sIntersection.alternatives.empty()) { + if (sIntersection.alternative) { // Test the alternative - cLimit = sIntersection.alternatives[0].pathLength; + cLimit = sIntersection.alternative.pathLength; withinLimit = (cLimit > oLimit and cLimit * cLimit <= pLimit * pLimit + s_onSurfaceTolerance); - if (sIntersection.alternatives[0] and withinLimit) { - sIntersection.alternatives[0].pathLength *= + if (sIntersection.alternative and withinLimit) { + sIntersection.alternative.pathLength *= std::copysign(1., options.navDir); - return BoundaryIntersection(sIntersection.alternatives[0], bSurface, + return BoundaryIntersection(sIntersection.alternative, bSurface, sIntersection.object); } } diff --git a/Core/include/Acts/Propagator/DirectNavigator.hpp b/Core/include/Acts/Propagator/DirectNavigator.hpp index 93ef7986bb58a96a6f8a242d5c3dcbbefe59eaec..a71a1a41002f89dab8ba2a40782d6904507d0425 100644 --- a/Core/include/Acts/Propagator/DirectNavigator.hpp +++ b/Core/include/Acts/Propagator/DirectNavigator.hpp @@ -172,9 +172,8 @@ class DirectNavigator { // Intersect the next surface and go double navStep = nextIntersection.intersection.pathLength; double overstepLimit = stepper.overstepLimit(state.stepping); - if (navStep < overstepLimit and !nextIntersection.alternatives.empty() and - nextIntersection.alternatives[0]) { - navStep = nextIntersection.alternatives[0].pathLength; + if (navStep < overstepLimit and nextIntersection.alternative) { + navStep = nextIntersection.alternative.pathLength; } // Set the step size - this has to be outsourced to the Stepper diff --git a/Core/include/Acts/Propagator/detail/StandardAborters.hpp b/Core/include/Acts/Propagator/detail/StandardAborters.hpp index efbf57a4caed4760fae385c0bcdc6ab5ebd9eac9..93ec9c9d85378b373abe8bc18ca441612d3ba460 100644 --- a/Core/include/Acts/Propagator/detail/StandardAborters.hpp +++ b/Core/include/Acts/Propagator/detail/StandardAborters.hpp @@ -187,11 +187,9 @@ struct SurfaceReached { // Target is not reached, update the step size const double overstepLimit = stepper.overstepLimit(state.stepping); // Check the alternative solution - if (distance < overstepLimit and - sIntersection.alternatives.size() == 1 and - sIntersection.alternatives[0]) { + if (distance < overstepLimit and sIntersection.alternative) { // Update the distance to the alternative solution - distance = sIntersection.alternatives[0].pathLength; + distance = sIntersection.alternative.pathLength; } state.stepping.stepSize.update(state.stepping.navDir * distance, ConstrainedStep::aborter); diff --git a/Core/include/Acts/Surfaces/BoundaryCheck.hpp b/Core/include/Acts/Surfaces/BoundaryCheck.hpp index 3b53203bb85acbce678eb6b4f5525b4b73b78293..7ac3a9e7f59145f219bf2875ae39a37b9b07bfee 100644 --- a/Core/include/Acts/Surfaces/BoundaryCheck.hpp +++ b/Core/include/Acts/Surfaces/BoundaryCheck.hpp @@ -114,13 +114,19 @@ class BoundaryCheck { double distance(const Vector2D& point, const Vector2D& lowerLeft, const Vector2D& upperRight) const; - private: enum class Type { eNone, ///< disable boundary check eAbsolute, ///< absolute cut eChi2 ///< chi2-based cut with full correlations }; + /// Broadcast the type + Type type() const; + + // Broadcast the tolerance + const Vector2D& tolerance() const; + + private: /// Return a new BoundaryCheck with updated covariance. /// @param jacobian Tranform Jacobian for the covariance /// @warning This currently only transforms the covariance and does not work @@ -171,6 +177,14 @@ class BoundaryCheck { } // namespace Acts +inline Acts::BoundaryCheck::Type Acts::BoundaryCheck::type() const { + return m_type; +} + +inline const Acts::Vector2D& Acts::BoundaryCheck::tolerance() const { + return m_tolerance; +} + inline Acts::BoundaryCheck::BoundaryCheck(bool check) : m_weight(ActsSymMatrixD<2>::Identity()), m_tolerance(0, 0), diff --git a/Core/include/Acts/Surfaces/CylinderBounds.hpp b/Core/include/Acts/Surfaces/CylinderBounds.hpp index 2c8570a1fda5caee3d165a913ddcd22c1f07b347..2f3e77d4169e448be560ee541b8337ddb584d6ab 100644 --- a/Core/include/Acts/Surfaces/CylinderBounds.hpp +++ b/Core/include/Acts/Surfaces/CylinderBounds.hpp @@ -120,6 +120,9 @@ class CylinderBounds : public SurfaceBounds { /// This method returns the halflengthZ double halflengthZ() const; + /// Returns true for full phi coverage + bool coversFullAzimuth() const; + private: /// the bound radius, average, half phi and half Z double m_radius, m_avgPhi, m_halfPhi, m_halfZ; @@ -145,4 +148,9 @@ inline double CylinderBounds::halfPhiSector() const { inline double CylinderBounds::halflengthZ() const { return m_halfZ; } + +inline bool CylinderBounds::coversFullAzimuth() const { + return (m_halfPhi == M_PI); +} + } // namespace Acts \ No newline at end of file diff --git a/Core/include/Acts/Surfaces/CylinderSurface.hpp b/Core/include/Acts/Surfaces/CylinderSurface.hpp index 36ededdc0ea31f6be79e059a3f5d46ba8efa0c68..3d9381a9341e967f5ddaec5ebca2b0d3b97e0136 100644 --- a/Core/include/Acts/Surfaces/CylinderSurface.hpp +++ b/Core/include/Acts/Surfaces/CylinderSurface.hpp @@ -281,7 +281,7 @@ class CylinderSurface : public Surface { /// /// @return the quadratic equation detail::RealQuadraticEquation intersectionSolver( - const GeometryContext& gctx, const Vector3D& position, + const Transform3D& transform, const Vector3D& position, const Vector3D& direction) const; }; diff --git a/Core/include/Acts/Surfaces/DiscBounds.hpp b/Core/include/Acts/Surfaces/DiscBounds.hpp index 496a2d6364c5fbc4e5ce2bcd87166c62f8e2dce9..2a0b0a1c654b005ee93d7b3da4f93a7471e6a5ba 100644 --- a/Core/include/Acts/Surfaces/DiscBounds.hpp +++ b/Core/include/Acts/Surfaces/DiscBounds.hpp @@ -20,6 +20,13 @@ namespace Acts { /// common base class for all bounds that are in a r/phi frame /// - simply introduced to avoid wrong bound assigments to surfaces -class DiscBounds : public SurfaceBounds {}; +class DiscBounds : public SurfaceBounds { + public: + /// Returns true for full phi coverage + virtual bool coversFullAzimuth() const = 0; + + /// Checks if it's inside the radius + virtual bool insideRadialBounds(double R, double tolerance = 0.) const = 0; +}; } // namespace Acts \ No newline at end of file diff --git a/Core/include/Acts/Surfaces/DiscSurface.hpp b/Core/include/Acts/Surfaces/DiscSurface.hpp index 7e34d6715939f1aae3cf720c64b1d4232d59e54f..8b6ff52144e32273ea4fc3e4b221c6f278bba205 100644 --- a/Core/include/Acts/Surfaces/DiscSurface.hpp +++ b/Core/include/Acts/Surfaces/DiscSurface.hpp @@ -20,6 +20,7 @@ #include "Acts/Surfaces/Surface.hpp" #include "Acts/Surfaces/detail/PlanarHelper.hpp" #include "Acts/Utilities/Definitions.hpp" +#include "Acts/Utilities/Helpers.hpp" namespace Acts { diff --git a/Core/include/Acts/Surfaces/DiscTrapezoidalBounds.hpp b/Core/include/Acts/Surfaces/DiscTrapezoidalBounds.hpp index 54e6244d040b79e642bb9b6c318abb8b46ddba98..ce2259138a90384f2a33806145d8d4936db13afd 100644 --- a/Core/include/Acts/Surfaces/DiscTrapezoidalBounds.hpp +++ b/Core/include/Acts/Surfaces/DiscTrapezoidalBounds.hpp @@ -108,6 +108,13 @@ class DiscTrapezoidalBounds : public DiscBounds { /// This method returns the halflength in Y (this is Rmax -Rmin) double halflengthY() const; + /// Returns true for full phi coverage - obviously false here + bool coversFullAzimuth() const final; + + /// Checks if this is inside the radial coverage + /// given the a tolerance + bool insideRadialBounds(double R, double tolerance = 0.) const final; + private: double m_rMin, m_rMax, m_minHalfX, m_maxHalfX, m_avgPhi; double m_stereo; // TODO 2017-04-09 msmk: what is this good for? @@ -167,4 +174,13 @@ inline double DiscTrapezoidalBounds::halflengthY() const { return (hmax - hmin) / 2.0; } +inline bool DiscTrapezoidalBounds::coversFullAzimuth() const { + return false; +} + +inline bool DiscTrapezoidalBounds::insideRadialBounds(double R, + double tolerance) const { + return (R + tolerance > m_rMin and R - tolerance < m_rMax); +} + } // namespace Acts \ No newline at end of file diff --git a/Core/include/Acts/Surfaces/RadialBounds.hpp b/Core/include/Acts/Surfaces/RadialBounds.hpp index 956a4eb82d7f02a9928b34220e9636553093333a..b458991b7d8915b97e2ecadb7345868618cfbf10 100644 --- a/Core/include/Acts/Surfaces/RadialBounds.hpp +++ b/Core/include/Acts/Surfaces/RadialBounds.hpp @@ -100,6 +100,13 @@ class RadialBounds : public DiscBounds { /// Return method for the half phi span double halfPhiSector() const; + /// Returns true for full phi coverage + bool coversFullAzimuth() const final; + + /// Checks if this is inside the radial coverage + /// given the a tolerance + bool insideRadialBounds(double R, double tolerance = 0.) const final; + private: double m_rMin, m_rMax, m_avgPhi, m_halfPhi; @@ -126,4 +133,12 @@ inline double RadialBounds::halfPhiSector() const { return m_halfPhi; } +inline bool RadialBounds::coversFullAzimuth() const { + return (m_halfPhi == M_PI); +} + +inline bool RadialBounds::insideRadialBounds(double R, double tolerance) const { + return (R + tolerance > m_rMin and R - tolerance < m_rMax); +} + } // namespace Acts \ No newline at end of file diff --git a/Core/include/Acts/Surfaces/detail/ConeSurface.ipp b/Core/include/Acts/Surfaces/detail/ConeSurface.ipp index d6dd7122ee5c48f1d2a8223702f67f2f99d8bc69..0dc389f2a4522b41cfe89fe5bdb229928db8d2cf 100644 --- a/Core/include/Acts/Surfaces/detail/ConeSurface.ipp +++ b/Core/include/Acts/Surfaces/detail/ConeSurface.ipp @@ -108,12 +108,12 @@ inline SurfaceIntersection ConeSurface::intersect( status2 == Intersection::Status::missed) { // And add the alternative if (qe.solutions > 1) { - cIntersection.alternatives = {second}; + cIntersection.alternative = second; } } else { // And add the alternative if (qe.solutions > 1) { - cIntersection.alternatives = {first}; + cIntersection.alternative = first; } cIntersection.intersection = second; } diff --git a/Core/include/Acts/Surfaces/detail/CylinderSurface.ipp b/Core/include/Acts/Surfaces/detail/CylinderSurface.ipp index f5bf742e23b8b9597763c2a9168702e9fe898c85..c3f6a8a6bd343b95e64a1d5e1d29dce259addd02 100644 --- a/Core/include/Acts/Surfaces/detail/CylinderSurface.ipp +++ b/Core/include/Acts/Surfaces/detail/CylinderSurface.ipp @@ -17,13 +17,13 @@ inline const Vector3D CylinderSurface::rotSymmetryAxis( } inline detail::RealQuadraticEquation CylinderSurface::intersectionSolver( - const GeometryContext& gctx, const Vector3D& position, + const Transform3D& transform, const Vector3D& position, const Vector3D& direction) const { // Solve for radius R double R = bounds().r(); // Get the transformation matrtix - const auto& tMatrix = transform(gctx).matrix(); + const auto& tMatrix = transform.matrix(); Vector3D caxis = tMatrix.block<3, 1>(0, 2).transpose(); Vector3D ccenter = tMatrix.block<3, 1>(0, 3).transpose(); @@ -41,8 +41,9 @@ inline detail::RealQuadraticEquation CylinderSurface::intersectionSolver( inline Intersection CylinderSurface::intersectionEstimate( const GeometryContext& gctx, const Vector3D& position, const Vector3D& direction, const BoundaryCheck& bcheck) const { + const auto& gctxTransform = transform(gctx); // Solve the quadratic equation - auto qe = intersectionSolver(gctx, position, direction); + auto qe = intersectionSolver(gctxTransform, position, direction); // If no valid solution return a non-valid intersection if (qe.solutions == 0) { @@ -70,8 +71,10 @@ inline Intersection CylinderSurface::intersectionEstimate( inline SurfaceIntersection CylinderSurface::intersect( const GeometryContext& gctx, const Vector3D& position, const Vector3D& direction, const BoundaryCheck& bcheck) const { + const auto& gctxTransform = transform(gctx); + // Solve the quadratic equation - auto qe = intersectionSolver(gctx, position, direction); + auto qe = intersectionSolver(gctxTransform, position, direction); // If no valid solution return a non-valid surfaceIntersection if (qe.solutions == 0) { @@ -84,23 +87,48 @@ inline SurfaceIntersection CylinderSurface::intersect( qe.first * qe.first < s_onSurfaceTolerance * s_onSurfaceTolerance ? Intersection::Status::onSurface : Intersection::Status::reachable; - if (bcheck and not isOnSurface(gctx, solution1, direction, bcheck)) { - status1 = Intersection::Status::missed; - } + // Helper method for boundary check + auto boundaryCheck = + [&](const Vector3D& solution, + Intersection::Status status) -> Intersection::Status { + // No check to be done, return current status + if (!bcheck) + return status; + const auto& cBounds = bounds(); + if (cBounds.coversFullAzimuth() and + bcheck.type() == BoundaryCheck::Type::eAbsolute) { + // Project out the current Z value via local z axis + // Built-in local to global for speed reasons + const auto& tMatrix = gctxTransform.matrix(); + // Create the reference vector in local + const Vector3D vecLocal(solution - tMatrix.block<3, 1>(0, 3)); + double cZ = vecLocal.dot(tMatrix.block<3, 1>(0, 2)); + double tolerance = s_onSurfaceTolerance + bcheck.tolerance()[eLOC_Z]; + double hZ = cBounds.halflengthZ() + tolerance; + return (cZ * cZ < hZ * hZ) ? status : Intersection::Status::missed; + } + return (isOnSurface(gctx, solution, direction, bcheck) + ? status + : Intersection::Status::missed); + }; + // Check first solution for boundary compatiblity + status1 = boundaryCheck(solution1, status1); + // Set the intersection + Intersection first(solution1, qe.first, status1); + SurfaceIntersection cIntersection(first, this); + if (qe.solutions == 1) { + return cIntersection; + } // Check the validity of the second solution Vector3D solution2 = position + qe.second * direction; Intersection::Status status2 = qe.second * qe.second < s_onSurfaceTolerance * s_onSurfaceTolerance ? Intersection::Status::onSurface : Intersection::Status::reachable; - if (bcheck and not isOnSurface(gctx, solution2, direction, bcheck)) { - status2 = Intersection::Status::missed; - } - // Set the intersection - Intersection first(solution1, qe.first, status1); + // Check first solution for boundary compatiblity + status2 = boundaryCheck(solution2, status2); Intersection second(solution2, qe.second, status2); - SurfaceIntersection cIntersection(first, this); // Check one if its valid or neither is valid bool check1 = status1 != Intersection::Status::missed or (status1 == Intersection::Status::missed and @@ -109,14 +137,10 @@ inline SurfaceIntersection CylinderSurface::intersect( if ((check1 and qe.first * qe.first < qe.second * qe.second) or status2 == Intersection::Status::missed) { // And add the alternative - if (qe.solutions > 1) { - cIntersection.alternatives = {second}; - } + cIntersection.alternative = second; } else { // And add the alternative - if (qe.solutions > 1) { - cIntersection.alternatives = {first}; - } + cIntersection.alternative = first; cIntersection.intersection = second; } return cIntersection; diff --git a/Core/include/Acts/Surfaces/detail/DiscSurface.ipp b/Core/include/Acts/Surfaces/detail/DiscSurface.ipp index 7ac3ee399221ac9a2cd2ced487b843ab08a6c682..ed36924334331e085fa910a9defa87dcc148f508 100644 --- a/Core/include/Acts/Surfaces/detail/DiscSurface.ipp +++ b/Core/include/Acts/Surfaces/detail/DiscSurface.ipp @@ -110,15 +110,29 @@ inline const RotationMatrix3D DiscSurface::initJacobianToLocal( inline Intersection DiscSurface::intersectionEstimate( const GeometryContext& gctx, const Vector3D& position, const Vector3D& direction, const BoundaryCheck& bcheck) const { + // Get the contextual transform + auto gctxTransform = transform(gctx); // Use the intersection helper for planar surfaces auto intersection = - PlanarHelper::intersectionEstimate(transform(gctx), position, direction); - // Evaluate (if necessary in terms of boundaries) - // @todo: speed up isOnSurface - we know that it is on surface - // all we need is to check if it's inside bounds + PlanarHelper::intersectionEstimate(gctxTransform, position, direction); + // Evaluate boundary check if requested (and reachable) if (intersection.status != Intersection::Status::unreachable and bcheck and - not isOnSurface(gctx, intersection.position, direction, bcheck)) { - intersection.status = Intersection::Status::missed; + m_bounds != nullptr) { + // Built-in local to global for speed reasons + const auto& tMatrix = gctxTransform.matrix(); + const Vector3D vecLocal(intersection.position - tMatrix.block<3, 1>(0, 3)); + const Vector2D lcartesian = + tMatrix.block<3, 2>(0, 0).transpose() * vecLocal; + if (bcheck.type() == BoundaryCheck::Type::eAbsolute and + m_bounds->coversFullAzimuth()) { + double tolerance = s_onSurfaceTolerance + bcheck.tolerance()[eLOC_R]; + if (not m_bounds->insideRadialBounds(VectorHelpers::perp(lcartesian), + tolerance)) { + intersection.status = Intersection::Status::missed; + } + } else if (not insideBounds(localCartesianToPolar(lcartesian), bcheck)) { + intersection.status = Intersection::Status::missed; + } } return intersection; } diff --git a/Core/include/Acts/Surfaces/detail/LineSurface.ipp b/Core/include/Acts/Surfaces/detail/LineSurface.ipp index 036179c1903b2760d27f45425003a596f522db54..b120eb18cbc370bf656f240192b21a77eb3ff99a 100644 --- a/Core/include/Acts/Surfaces/detail/LineSurface.ipp +++ b/Core/include/Acts/Surfaces/detail/LineSurface.ipp @@ -114,14 +114,19 @@ inline Intersection LineSurface::intersectionEstimate( status = (u * u < s_onSurfaceTolerance * s_onSurfaceTolerance) ? Intersection::Status::onSurface : Intersection::Status::reachable; - // evaluate validaty in terms of bounds Vector3D result = (ma + u * ea); - // it just needs to be a insideBounds() check - // @todo there should be a faster check possible - if (bcheck and not isOnSurface(gctx, result, direction, bcheck)) { - status = Intersection::Status::missed; + // Evaluate the boundary check if requested + // m_bounds == nullptr prevents unecessary calulations for PerigeeSurface + if (bcheck and m_bounds) { + // At closest approach: check inside R or and inside Z + const Vector3D vecLocal(result - mb); + double cZ = vecLocal.dot(eb); + double hZ = m_bounds->halflengthZ() + s_onSurfaceTolerance; + if ((cZ * cZ > hZ * hZ) or ((vecLocal - cZ * eb).norm() > + m_bounds->r() + s_onSurfaceTolerance)) { + status = Intersection::Status::missed; + } } - // return the result with validity return Intersection(result, u, status); } // return a false intersection @@ -203,4 +208,4 @@ inline const BoundRowVector LineSurface::derivativeFactors( // build the combined normal & longitudinal components return (norm * (s_vec - pc * (long_mat * d_vec.asDiagonal() - jacobian.block<3, BoundParsDim>(4, 0)))); -} \ No newline at end of file +} diff --git a/Core/include/Acts/Surfaces/detail/PlaneSurface.ipp b/Core/include/Acts/Surfaces/detail/PlaneSurface.ipp index 16cbcbc98c8c84ddeef3261c69a04b01766c40c4..f7565fec88ccd8dbf741ce6ba396b33bfd98d8a5 100644 --- a/Core/include/Acts/Surfaces/detail/PlaneSurface.ipp +++ b/Core/include/Acts/Surfaces/detail/PlaneSurface.ipp @@ -25,22 +25,28 @@ inline const Vector3D PlaneSurface::binningPosition( inline double PlaneSurface::pathCorrection(const GeometryContext& gctx, const Vector3D& position, const Vector3D& direction) const { - /// We can ignore the global position here + // We can ignore the global position here return 1. / std::abs(Surface::normal(gctx, position).dot(direction)); } inline Intersection PlaneSurface::intersectionEstimate( const GeometryContext& gctx, const Vector3D& position, const Vector3D& direction, const BoundaryCheck& bcheck) const { + // Get the contextual transform + const auto& gctxTransform = transform(gctx); // Use the intersection helper for planar surfaces auto intersection = - PlanarHelper::intersectionEstimate(transform(gctx), position, direction); - // Evaluate (if necessary in terms of boundaries) - // @todo: speed up isOnSurface - we know that it is on surface - // all we need is to check if it's inside bounds - if (intersection.status != Intersection::Status::unreachable and bcheck and - not isOnSurface(gctx, intersection.position, direction, bcheck)) { - intersection.status = Intersection::Status::missed; + PlanarHelper::intersectionEstimate(gctxTransform, position, direction); + // Evaluate boundary check if requested (and reachable) + if (intersection.status != Intersection::Status::unreachable and bcheck) { + // Built-in local to global for speed reasons + const auto& tMatrix = gctxTransform.matrix(); + // Create the reference vector in local + const Vector3D vecLocal(intersection.position - tMatrix.block<3, 1>(0, 3)); + if (not insideBounds(tMatrix.block<3, 2>(0, 0).transpose() * vecLocal, + bcheck)) { + intersection.status = Intersection::Status::missed; + } } return intersection; } diff --git a/Core/include/Acts/Utilities/Intersection.hpp b/Core/include/Acts/Utilities/Intersection.hpp index b8b42f2eece9dd22546fdc257d6f022aaff4fde9..8c300e8ea10731287145598e940295c6c9c703a8 100644 --- a/Core/include/Acts/Utilities/Intersection.hpp +++ b/Core/include/Acts/Utilities/Intersection.hpp @@ -92,7 +92,7 @@ class ObjectIntersection { const representation_t* representation{nullptr}; /// The alternative intersections - std::vector<Intersection> alternatives = {}; + Intersection alternative{}; /// Default constructor ObjectIntersection() = default; @@ -123,7 +123,7 @@ class ObjectIntersection { /// @brief Smaller operator for ordering & sorting /// - /// This operator will ignore the alternatives, but simply + /// This operator will ignore the alternative, but simply /// order the representing intersection /// /// @param oi is the source intersection for comparison @@ -134,7 +134,7 @@ class ObjectIntersection { /// @brief Greater operator for ordering & sorting /// - /// This operator will ignore the alternatives, but simply + /// This operator will ignore the alternative, but simply /// order the representing intersection /// /// @param oi is the source intersection for comparison diff --git a/Tests/Core/Surfaces/CMakeLists.txt b/Tests/Core/Surfaces/CMakeLists.txt index d81e0ea276f2a6fc62a8bdcdd4082b41f5f6def4..90dd6847b6f658447eb9d1db36628e1078c2d5e7 100644 --- a/Tests/Core/Surfaces/CMakeLists.txt +++ b/Tests/Core/Surfaces/CMakeLists.txt @@ -47,6 +47,10 @@ add_executable (SurfaceIntersectionTests SurfaceIntersectionTests.cpp) target_link_libraries (SurfaceIntersectionTests PRIVATE ActsCore ActsTestsCommonHelpers ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) add_test (NAME SurfaceIntersectionUnitTest COMMAND SurfaceIntersectionTests) +add_executable (SurfaceIntersectionBenchmark SurfaceIntersectionBenchmark.cpp) +target_link_libraries (SurfaceIntersectionBenchmark PRIVATE ActsCore ActsTestsCommonHelpers ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) +add_test (NAME SurfaceIntersectionBenchmarkTest COMMAND SurfaceIntersectionBenchmark) + add_executable (InfiniteBoundsTests InfiniteBoundsTests.cpp) target_link_libraries (InfiniteBoundsTests PRIVATE ActsCore ActsTestsCommonHelpers ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) add_test (NAME InfiniteBoundsUnitTest COMMAND InfiniteBoundsTests) diff --git a/Tests/Core/Surfaces/CylinderSurfaceTests.cpp b/Tests/Core/Surfaces/CylinderSurfaceTests.cpp index 4748e3e1790ead75e537a6c10b5bd05c62463661..78c5a8b08a13c3f08bb2072981582d7370fd0166 100644 --- a/Tests/Core/Surfaces/CylinderSurfaceTests.cpp +++ b/Tests/Core/Surfaces/CylinderSurfaceTests.cpp @@ -191,11 +191,10 @@ BOOST_AUTO_TEST_CASE(CylinderSurfaceProperties) { testContext, offSurface, direction, false); BOOST_CHECK(bool(surfaceIntersect)); // there is a second solution & and it should be valid - BOOST_CHECK(surfaceIntersect.alternatives.size() == 1); - BOOST_CHECK(bool(*surfaceIntersect.alternatives.begin())); + BOOST_CHECK(surfaceIntersect.alternative); // And it's path should be further away then the primary solution double pn = surfaceIntersect.intersection.pathLength; - double pa = surfaceIntersect.alternatives[0].pathLength; + double pa = surfaceIntersect.alternative.pathLength; BOOST_CHECK(pn * pn < pa * pa); // /// Test pathCorrection diff --git a/Tests/Core/Surfaces/SurfaceIntersectionBenchmark.cpp b/Tests/Core/Surfaces/SurfaceIntersectionBenchmark.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7b58fa37e45020b9f640cb005d5589fda4f074a5 --- /dev/null +++ b/Tests/Core/Surfaces/SurfaceIntersectionBenchmark.cpp @@ -0,0 +1,126 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2019 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/. + +#define BOOST_TEST_MODULE Surface Intersection Benchmark + +#define BOOST_TEST_DYN_LINK +#include <boost/test/unit_test.hpp> +// leave blank line + +#include <boost/test/data/test_case.hpp> +// leave blank line + +#include <boost/test/output_test_stream.hpp> +// leave blank line + +#include <cmath> +#include "Acts/Geometry/GeometryContext.hpp" +#include "Acts/Surfaces/CylinderBounds.hpp" +#include "Acts/Surfaces/CylinderSurface.hpp" +#include "Acts/Surfaces/DiscSurface.hpp" +#include "Acts/Surfaces/PlaneSurface.hpp" +#include "Acts/Surfaces/RadialBounds.hpp" +#include "Acts/Surfaces/RectangleBounds.hpp" +#include "Acts/Surfaces/StrawSurface.hpp" +#include "Acts/Utilities/Units.hpp" + +namespace bdata = boost::unit_test::data; +namespace tt = boost::test_tools; +using namespace Acts::UnitLiterals; + +namespace Acts { +namespace Test { + +// Some randomness & number crunching +unsigned int ntests = 100; +unsigned int nrepts = 1000; +const bool boundaryCheck = false; +const bool testPlane = true; +const bool testDisc = true; +const bool testCylinder = true; +const bool testStraw = true; + +// Create a test context +GeometryContext tgContext = GeometryContext(); + +// Create a test plane in 10 m distance +// Some random transform +Transform3D at = Transform3D::Identity() * Translation3D(0_m, 0_m, 10_m) * + AngleAxis3D(0.15, Vector3D(1.2, 1.2, 0.12).normalized()); + +// Define the Plane surface +auto rb = std::make_shared<RectangleBounds>(1_m, 1_m); +auto aPlane = Surface::makeShared<PlaneSurface>( + std::make_shared<Transform3D>(at), std::move(rb)); + +// Define the Disc surface +auto db = std::make_shared<RadialBounds>(0.2_m, 1.2_m); +auto aDisc = Surface::makeShared<DiscSurface>(std::make_shared<Transform3D>(at), + std::move(db)); + +// Define a Cylinder surface +auto cb = std::make_shared<CylinderBounds>(10_m, 100_m); +auto aCylinder = Surface::makeShared<CylinderSurface>( + std::make_shared<Transform3D>(at), std::move(cb)); + +// Define a Straw surface +auto aStraw = Surface::makeShared<StrawSurface>( + std::make_shared<Transform3D>(at), 50_cm, 2_m); + +// The orgin of our attempts for plane, disc and cylinder +Vector3D origin(0., 0., 0.); + +// The origin for straw/line attempts +Vector3D originStraw(0.3_m, -0.2_m, 11_m); + +template <typename surface_t> +void intersectionTest(const surface_t& surface, double phi, double theta) { + // Shoot at it + double cosPhi = std::cos(phi); + double sinPhi = std::sin(phi); + double cosTheta = std::cos(theta); + double sinTheta = std::sin(theta); + + Vector3D direction(cosPhi * sinTheta, sinPhi * sinTheta, cosTheta); + + for (unsigned int ir = 0; ir < nrepts; ++ir) { + auto intersect = + surface.intersect(tgContext, origin, direction, boundaryCheck); + + (void)intersect; + } +} + +BOOST_DATA_TEST_CASE( + benchmark_surface_intersections, + bdata::random( + (bdata::seed = 21, + bdata::distribution = std::uniform_real_distribution<>(-M_PI, M_PI))) ^ + bdata::random((bdata::seed = 22, + bdata::distribution = + std::uniform_real_distribution<>(-0.3, 0.3))) ^ + bdata::xrange(ntests), + phi, theta, index) { + (void)index; + + if (testPlane) { + intersectionTest<PlaneSurface>(*aPlane, phi, theta); + } + if (testDisc) { + intersectionTest<DiscSurface>(*aDisc, phi, theta); + } + if (testCylinder) { + intersectionTest<CylinderSurface>(*aCylinder, phi, theta); + } + if (testStraw) { + intersectionTest<StrawSurface>(*aStraw, phi, theta + M_PI); + } +} + +} // namespace Test +} // namespace Acts diff --git a/Tests/Core/Surfaces/SurfaceIntersectionTests.cpp b/Tests/Core/Surfaces/SurfaceIntersectionTests.cpp index 33b4444a3475625b2d4fde7de46fa771a5a737b9..9c4afb65ce4822d49969065de208fbf5affe9089 100644 --- a/Tests/Core/Surfaces/SurfaceIntersectionTests.cpp +++ b/Tests/Core/Surfaces/SurfaceIntersectionTests.cpp @@ -79,12 +79,12 @@ BOOST_AUTO_TEST_CASE(CylinderIntersectionTests) { BOOST_CHECK(aIntersection.intersection.status == Intersection::Status::onSurface); // There MUST be a second solution - BOOST_CHECK(aIntersection.alternatives.size() == 1); + BOOST_CHECK(aIntersection.alternative); // The other intersection MUST be reachable - BOOST_CHECK(aIntersection.alternatives[0].status == + BOOST_CHECK(aIntersection.alternative.status == Intersection::Status::reachable); // The other intersection is at 2 meter distance - CHECK_CLOSE_ABS(aIntersection.alternatives[0].pathLength, -2_m, + CHECK_CLOSE_ABS(aIntersection.alternative.pathLength, -2_m, s_onSurfaceTolerance); // Intersect from the the center @@ -97,12 +97,12 @@ BOOST_AUTO_TEST_CASE(CylinderIntersectionTests) { BOOST_CHECK(cIntersection.intersection.status == Intersection::Status::reachable); // There MUST be a second solution - BOOST_CHECK(cIntersection.alternatives.size() == 1); + BOOST_CHECK(cIntersection.alternative); // The other intersection MUST be reachable - BOOST_CHECK(cIntersection.alternatives[0].status == + BOOST_CHECK(cIntersection.alternative.status == Intersection::Status::reachable); // There MUST be one forward one backwards solution - BOOST_CHECK(cIntersection.alternatives[0].pathLength * + BOOST_CHECK(cIntersection.alternative.pathLength * cIntersection.intersection.pathLength < 0); @@ -116,12 +116,12 @@ BOOST_AUTO_TEST_CASE(CylinderIntersectionTests) { BOOST_CHECK(oIntersection.intersection.status == Intersection::Status::reachable); // There MUST be a second solution - BOOST_CHECK(oIntersection.alternatives.size() == 1); + BOOST_CHECK(oIntersection.alternative); // The other intersection MUST be reachable - BOOST_CHECK(oIntersection.alternatives[0].status == + BOOST_CHECK(oIntersection.alternative.status == Intersection::Status::reachable); // There MUST be one forward one backwards solution - BOOST_CHECK(oIntersection.alternatives[0].pathLength * + BOOST_CHECK(oIntersection.alternative.pathLength * oIntersection.intersection.pathLength > 0); @@ -144,27 +144,27 @@ BOOST_AUTO_TEST_CASE(CylinderIntersectionTests) { BOOST_CHECK(eIntersection.intersection.status == Intersection::Status::reachable); // There MUST be a second solution - BOOST_CHECK(eIntersection.alternatives.size() == 1); + BOOST_CHECK(eIntersection.alternative); // The other intersection MUST be reachable - BOOST_CHECK(eIntersection.alternatives[0].status == + BOOST_CHECK(eIntersection.alternative.status == Intersection::Status::reachable); // And be the negative one - BOOST_CHECK(eIntersection.alternatives[0].pathLength < 0.); + BOOST_CHECK(eIntersection.alternative.pathLength < 0.); // Now re-do with boundary check eIntersection = aCylinder->intersect(tgContext, atEdge, transTZ, true); - // This should be the positive one + // This should be the negative one BOOST_CHECK(eIntersection.intersection.pathLength < 0.); // The status of this one should be reachable BOOST_CHECK(eIntersection.intersection.status == Intersection::Status::reachable); // There MUST be a second solution - BOOST_CHECK(eIntersection.alternatives.size() == 1); - // The other intersection MUST be reachable - BOOST_CHECK(eIntersection.alternatives[0].status == + BOOST_CHECK(!eIntersection.alternative); + // The other intersection MUST NOT be reachable + BOOST_CHECK(eIntersection.alternative.status == Intersection::Status::missed); - // And be the negative one - BOOST_CHECK(eIntersection.alternatives[0].pathLength > 0.); + // And be the positive one + BOOST_CHECK(eIntersection.alternative.pathLength > 0.); }; // In a nominal world @@ -205,12 +205,12 @@ BOOST_AUTO_TEST_CASE(ConeIntersectionTest) { Intersection::Status::onSurface); // There MUST be a second solution - BOOST_CHECK(aIntersection.alternatives.size() == 1); + BOOST_CHECK(aIntersection.alternative); // The other intersection MUST be reachable - BOOST_CHECK(aIntersection.alternatives[0].status == + BOOST_CHECK(aIntersection.alternative.status == Intersection::Status::reachable); // The other intersection is at 2 meter distance - CHECK_CLOSE_ABS(aIntersection.alternatives[0].pathLength, -4., + CHECK_CLOSE_ABS(aIntersection.alternative.pathLength, -4., s_onSurfaceTolerance); // Intersection from outside without chance of hitting the cylinder @@ -256,6 +256,7 @@ BOOST_AUTO_TEST_CASE(PlanarIntersectionTest) { // Intersect forward auto fIntersection = aPlane->intersect(tgContext, before, direction, true); + // The intersection MUST be valid BOOST_CHECK(fIntersection); // The intersection MUST be reachable @@ -264,7 +265,7 @@ BOOST_AUTO_TEST_CASE(PlanarIntersectionTest) { // The path length MUST be positive BOOST_CHECK(fIntersection.intersection.pathLength > 0.); // The intersection MUST be unique - BOOST_CHECK(fIntersection.alternatives.size() == 0); + BOOST_CHECK(!fIntersection.alternative); // On surface intersection auto oIntersection = aPlane->intersect(tgContext, onit, direction, true); @@ -277,7 +278,7 @@ BOOST_AUTO_TEST_CASE(PlanarIntersectionTest) { BOOST_CHECK(std::abs(oIntersection.intersection.pathLength) < s_onSurfaceTolerance); // The intersection MUST be unique - BOOST_CHECK(oIntersection.alternatives.size() == 0); + BOOST_CHECK(!oIntersection.alternative); // Intersect backwards auto bIntersection = aPlane->intersect(tgContext, after, direction, true); @@ -289,7 +290,7 @@ BOOST_AUTO_TEST_CASE(PlanarIntersectionTest) { // The path length MUST be negative BOOST_CHECK(bIntersection.intersection.pathLength < 0.); // The intersection MUST be unique - BOOST_CHECK(bIntersection.alternatives.size() == 0); + BOOST_CHECK(!bIntersection.alternative); // An out of bounds attempt: missed auto mIntersection = aPlane->intersect(tgContext, outside, direction, true); @@ -301,7 +302,7 @@ BOOST_AUTO_TEST_CASE(PlanarIntersectionTest) { // The path length MUST be negative BOOST_CHECK(mIntersection.intersection.pathLength > 0.); // The intersection MUST be unique - BOOST_CHECK(mIntersection.alternatives.size() == 0); + BOOST_CHECK(!mIntersection.alternative); // An invalid attempt auto iIntersection = aPlane->intersect(tgContext, before, parallel, true); @@ -311,7 +312,7 @@ BOOST_AUTO_TEST_CASE(PlanarIntersectionTest) { BOOST_CHECK(iIntersection.intersection.status == Intersection::Status::unreachable); // The intersection MUST be unique - BOOST_CHECK(iIntersection.alternatives.size() == 0); + BOOST_CHECK(!iIntersection.alternative); }; // In a nominal world @@ -358,7 +359,7 @@ BOOST_AUTO_TEST_CASE(LineIntersectionTest) { // The path length MUST be positive BOOST_CHECK(fIntersection.intersection.pathLength > 0.); // The intersection MUST be unique - BOOST_CHECK(fIntersection.alternatives.size() == 0); + BOOST_CHECK(!fIntersection.alternative); // On surface intersection - on the straw with random direction auto oIntersection = aLine->intersect(tgContext, onit1, direction, true); @@ -371,7 +372,7 @@ BOOST_AUTO_TEST_CASE(LineIntersectionTest) { BOOST_CHECK(std::abs(oIntersection.intersection.pathLength) < s_onSurfaceTolerance); // The intersection MUST be unique - BOOST_CHECK(oIntersection.alternatives.size() == 0); + BOOST_CHECK(!oIntersection.alternative); // On surface intersecion - on the surface with normal vector oIntersection = aLine->intersect(tgContext, onitP, normalP, true); @@ -384,7 +385,7 @@ BOOST_AUTO_TEST_CASE(LineIntersectionTest) { BOOST_CHECK(std::abs(oIntersection.intersection.pathLength) < s_onSurfaceTolerance); // The intersection MUST be unique - BOOST_CHECK(oIntersection.alternatives.size() == 0); + BOOST_CHECK(!oIntersection.alternative); // Intersect backwards auto bIntersection = aLine->intersect(tgContext, after, direction, true); @@ -396,7 +397,7 @@ BOOST_AUTO_TEST_CASE(LineIntersectionTest) { // The path length MUST be negative BOOST_CHECK(bIntersection.intersection.pathLength < 0.); // The intersection MUST be unique - BOOST_CHECK(bIntersection.alternatives.size() == 0); + BOOST_CHECK(!bIntersection.alternative); // An out of bounds attempt: missed auto mIntersection = aLine->intersect(tgContext, outside, direction, true); @@ -408,7 +409,7 @@ BOOST_AUTO_TEST_CASE(LineIntersectionTest) { // The path length MUST be negative BOOST_CHECK(mIntersection.intersection.pathLength < 0.); // The intersection MUST be unique - BOOST_CHECK(mIntersection.alternatives.size() == 0); + BOOST_CHECK(!mIntersection.alternative); // An invalid attempt auto iIntersection = aLine->intersect(tgContext, before, parallel, true); @@ -418,7 +419,7 @@ BOOST_AUTO_TEST_CASE(LineIntersectionTest) { BOOST_CHECK(iIntersection.intersection.status == Intersection::Status::unreachable); // The intersection MUST be unique - BOOST_CHECK(iIntersection.alternatives.size() == 0); + BOOST_CHECK(!iIntersection.alternative); }; // In a nominal world