Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-12-07 09:02:52

0001 // This file is part of the ACTS project.
0002 //
0003 // Copyright (C) 2016 CERN for the benefit of the ACTS project
0004 //
0005 // This Source Code Form is subject to the terms of the Mozilla Public
0006 // License, v. 2.0. If a copy of the MPL was not distributed with this
0007 // file, You can obtain one at https://mozilla.org/MPL/2.0/.
0008 
0009 #include <boost/test/unit_test.hpp>
0010 
0011 #include "Acts/Definitions/Algebra.hpp"
0012 #include "Acts/Definitions/Tolerance.hpp"
0013 #include "Acts/Definitions/Units.hpp"
0014 #include "Acts/Geometry/GeometryContext.hpp"
0015 #include "Acts/Surfaces/ConeSurface.hpp"
0016 #include "Acts/Surfaces/CylinderSurface.hpp"
0017 #include "Acts/Surfaces/PlaneSurface.hpp"
0018 #include "Acts/Surfaces/RectangleBounds.hpp"
0019 #include "Acts/Surfaces/StrawSurface.hpp"
0020 #include "Acts/Surfaces/Surface.hpp"
0021 #include "Acts/Utilities/Intersection.hpp"
0022 #include "ActsTests/CommonHelpers/FloatComparisons.hpp"
0023 
0024 #include <cmath>
0025 #include <memory>
0026 #include <numbers>
0027 
0028 using namespace Acts::UnitLiterals;
0029 
0030 using namespace Acts;
0031 
0032 namespace ActsTests {
0033 
0034 // Create a test context
0035 GeometryContext tgContext = GeometryContext();
0036 
0037 // Some random transform
0038 Transform3 aTransform = Transform3::Identity() *
0039                         Translation3(30_cm, 7_m, -87_mm) *
0040                         AngleAxis3(0.42, Vector3(-3., 1., 8).normalized());
0041 
0042 BOOST_AUTO_TEST_SUITE(SurfacesSuite)
0043 
0044 /// This tests the intersection with cylinders
0045 /// and looks for valid, non-valid, solutions
0046 BOOST_AUTO_TEST_CASE(CylinderIntersectionTests) {
0047   const double radius = 1_m;
0048   const double halfZ = 10_m;
0049 
0050   auto testCylinderIntersection = [&](const Transform3& transform) -> void {
0051     // A cylinder created aligned with a provided transform
0052     auto aCylinder =
0053         Surface::makeShared<CylinderSurface>(transform, radius, halfZ);
0054 
0055     // Linear transform
0056     auto lTransform = transform.linear();
0057 
0058     // An onCylinder solution
0059     Vector3 onCylinder = transform * Vector3(radius, 0., 0.);
0060     Vector3 outCylinder = transform * Vector3(-radius, 0.6 * radius, 90_cm);
0061     Vector3 atCenter = transform * Vector3(0., 0., 0.);
0062     Vector3 atEdge = transform * Vector3(0.5 * radius, 0., 0.99 * halfZ);
0063     // Simply along the x axis
0064     Vector3 alongX = lTransform * Vector3(1., 0., 0.);
0065     Vector3 transXY = lTransform * Vector3(1., 1., 0).normalized();
0066     Vector3 transTZ = lTransform * Vector3(1., 0., 1.).normalized();
0067 
0068     // Intersect without boundary check
0069     auto aIntersection = aCylinder->intersect(tgContext, onCylinder, alongX,
0070                                               BoundaryTolerance::None());
0071 
0072     // Check the validity of the intersection
0073     BOOST_CHECK(aIntersection[0].isValid());
0074     // The status of this one should be on surface
0075     BOOST_CHECK_EQUAL(aIntersection[0].status(), IntersectionStatus::reachable);
0076     // The intersection is at 2 meter distance
0077     CHECK_CLOSE_ABS(aIntersection[0].pathLength(), -2_m, s_onSurfaceTolerance);
0078     // There MUST be a second solution
0079     BOOST_CHECK(aIntersection[1].isValid());
0080     // The other intersection MUST be reachable
0081     BOOST_CHECK_EQUAL(aIntersection[1].status(), IntersectionStatus::onSurface);
0082 
0083     // Intersect from the center
0084     auto cIntersection = aCylinder->intersect(tgContext, atCenter, alongX,
0085                                               BoundaryTolerance::None());
0086 
0087     // Check the validity of the intersection
0088     BOOST_CHECK(cIntersection[0].isValid());
0089     // The status of this one MUST be reachable
0090     BOOST_CHECK_EQUAL(cIntersection[0].status(), IntersectionStatus::reachable);
0091     // There MUST be a second solution
0092     BOOST_CHECK(cIntersection[1].isValid());
0093     // The other intersection MUST be reachable
0094     BOOST_CHECK_EQUAL(cIntersection[1].status(), IntersectionStatus::reachable);
0095     // There MUST be one forward one backwards solution
0096     BOOST_CHECK_LT(
0097         cIntersection[1].pathLength() * cIntersection[0].pathLength(), 0);
0098 
0099     // Intersect from outside where both intersections are reachable
0100     auto oIntersection = aCylinder->intersect(tgContext, outCylinder, alongX,
0101                                               BoundaryTolerance::None());
0102 
0103     // Check the validity of the intersection
0104     BOOST_CHECK(oIntersection[0].isValid());
0105     // The status of this one MUST be reachable
0106     BOOST_CHECK_EQUAL(oIntersection[0].status(), IntersectionStatus::reachable);
0107     // There MUST be a second solution
0108     BOOST_CHECK(oIntersection[1].isValid());
0109     // The other intersection MUST be reachable
0110     BOOST_CHECK_EQUAL(oIntersection[1].status(), IntersectionStatus::reachable);
0111     // There MUST be one forward one backwards solution
0112     BOOST_CHECK_GT(
0113         oIntersection[1].pathLength() * oIntersection[0].pathLength(), 0);
0114 
0115     // Intersection from outside without chance of hitting the cylinder
0116     auto iIntersection = aCylinder->intersect(tgContext, outCylinder, transXY,
0117                                               BoundaryTolerance::Infinite());
0118 
0119     // Check the validity of the intersection
0120     BOOST_CHECK(!iIntersection[0].isValid());
0121 
0122     // From edge tests - wo boundary test
0123     auto eIntersection = aCylinder->intersect(tgContext, atEdge, transTZ,
0124                                               BoundaryTolerance::Infinite());
0125 
0126     // Check the validity of the intersection
0127     BOOST_CHECK(eIntersection[0].isValid());
0128     // This should be the positive one
0129     BOOST_CHECK_LT(eIntersection[0].pathLength(), 0.);
0130     // The status of this one should be reachable
0131     BOOST_CHECK_EQUAL(eIntersection[0].status(), IntersectionStatus::reachable);
0132     // There MUST be a second solution
0133     BOOST_CHECK(eIntersection[1].isValid());
0134     // The other intersection MUST be reachable
0135     BOOST_CHECK_EQUAL(eIntersection[1].status(), IntersectionStatus::reachable);
0136     // And be the negative one
0137     BOOST_CHECK_GT(eIntersection[1].pathLength(), 0.);
0138 
0139     // Now re-do with boundary check
0140     eIntersection = aCylinder->intersect(tgContext, atEdge, transTZ,
0141                                          BoundaryTolerance::None());
0142     // This should be the negative one
0143     BOOST_CHECK_LT(eIntersection[0].pathLength(), 0.);
0144     // The status of this one should be reachable
0145     BOOST_CHECK_EQUAL(eIntersection[0].status(), IntersectionStatus::reachable);
0146     // There MUST be a second solution
0147     BOOST_CHECK(!eIntersection[1].isValid());
0148     // The other intersection MUST NOT be reachable
0149     BOOST_CHECK_EQUAL(eIntersection[1].status(),
0150                       IntersectionStatus::unreachable);
0151     // And be the positive one
0152     BOOST_CHECK_GT(eIntersection[1].pathLength(), 0.);
0153   };
0154 
0155   // In a nominal world
0156   testCylinderIntersection(Transform3::Identity());
0157 
0158   // In a system somewhere away
0159   testCylinderIntersection(aTransform);
0160 }
0161 
0162 /// This tests the intersection with cylinders
0163 /// and looks for valid, non-valid, solutions
0164 BOOST_AUTO_TEST_CASE(ConeIntersectionTest) {
0165   const double alpha = std::numbers::pi / 4.;
0166 
0167   auto testConeIntersection = [&](const Transform3& transform) -> void {
0168     // A cone surface ready to use
0169     auto aCone = Surface::makeShared<ConeSurface>(transform, alpha, true);
0170 
0171     // Linear transform
0172     auto lTransform = transform.linear();
0173 
0174     // An onCylinder solution
0175     Vector3 onCone =
0176         transform * Vector3(std::numbers::sqrt2, std::numbers::sqrt2, 2.);
0177     Vector3 outCone = transform * Vector3(std::sqrt(4.), std::sqrt(4.), 2.);
0178     // Simply along the x axis
0179     Vector3 perpXY = lTransform * Vector3(1., -1., 0.).normalized();
0180     Vector3 transXY = lTransform * Vector3(1., 1., 0).normalized();
0181 
0182     // Intersect without boundary check with an on solution
0183     BOOST_CHECK(aCone->isOnSurface(tgContext, onCone, transXY,
0184                                    BoundaryTolerance::Infinite()));
0185     auto aIntersection =
0186         aCone->intersect(tgContext, onCone, transXY, BoundaryTolerance::None());
0187 
0188     // Check the validity of the intersection
0189     BOOST_CHECK(aIntersection[0].isValid());
0190     // The status of this one should be on surface
0191     BOOST_CHECK_EQUAL(aIntersection[0].status(), IntersectionStatus::reachable);
0192     // The intersection is at 4 mm distance
0193     CHECK_CLOSE_ABS(aIntersection[0].pathLength(), -4., s_onSurfaceTolerance);
0194     // There MUST be a second solution
0195     BOOST_CHECK(aIntersection[1].isValid());
0196     // The other intersection MUST be reachable
0197     BOOST_CHECK_EQUAL(aIntersection[1].status(), IntersectionStatus::onSurface);
0198 
0199     // Intersection from outside without chance of hitting the cylinder
0200     auto iIntersection = aCone->intersect(tgContext, outCone, perpXY,
0201                                           BoundaryTolerance::Infinite());
0202 
0203     // Check the validity of the intersection
0204     BOOST_CHECK(!iIntersection[0].isValid());
0205   };
0206 
0207   // In a nominal world
0208   testConeIntersection(Transform3::Identity());
0209 
0210   // In a system somewhere away
0211   testConeIntersection(aTransform);
0212 }
0213 
0214 /// This tests the intersection with planar surfaces (plane, disk)
0215 /// as those share the same PlanarHelper methods, only one test is
0216 /// sufficient
0217 /// - it looks for valid, non-valid, solutions
0218 BOOST_AUTO_TEST_CASE(PlanarIntersectionTest) {
0219   const double halfX = 1_m;
0220   const double halfY = 10_m;
0221 
0222   auto testPlanarIntersection = [&](const Transform3& transform) -> void {
0223     // A Plane created with a specific transform
0224     auto aPlane = Surface::makeShared<PlaneSurface>(
0225         transform, std::make_shared<RectangleBounds>(halfX, halfY));
0226 
0227     /// Forward intersection test
0228     Vector3 before = transform * Vector3(-50_cm, -1_m, -1_m);
0229     Vector3 onit = transform * Vector3(11_cm, -22_cm, 0_m);
0230     Vector3 after = transform * Vector3(33_cm, 12_mm, 1_m);
0231     Vector3 outside = transform * Vector3(2. * halfX, 2 * halfY, -1_mm);
0232 
0233     // Linear transform
0234     auto lTransform = transform.linear();
0235 
0236     // A direction that is non trivial
0237     Vector3 direction = lTransform * Vector3(4_mm, 8_mm, 50_cm).normalized();
0238     Vector3 parallel = lTransform * Vector3(1., 1., 0.).normalized();
0239 
0240     // Intersect forward
0241     auto fIntersection = aPlane->intersect(tgContext, before, direction,
0242                                            BoundaryTolerance::None());
0243 
0244     // The intersection MUST be valid
0245     BOOST_CHECK(fIntersection[0].isValid());
0246     // The intersection MUST be reachable
0247     BOOST_CHECK_EQUAL(fIntersection[0].status(), IntersectionStatus::reachable);
0248     // The path length MUST be positive
0249     BOOST_CHECK_GT(fIntersection[0].pathLength(), 0.);
0250     // The intersection MUST be unique
0251     BOOST_CHECK(!fIntersection[1].isValid());
0252 
0253     // On surface intersection
0254     auto oIntersection = aPlane->intersect(tgContext, onit, direction,
0255                                            BoundaryTolerance::None());
0256     // The intersection MUST be valid
0257     BOOST_CHECK(oIntersection[0].isValid());
0258     // The intersection MUST be reachable
0259     BOOST_CHECK_EQUAL(oIntersection[0].status(), IntersectionStatus::onSurface);
0260     // The path length MUST be positive
0261     BOOST_CHECK_LT(std::abs(oIntersection[0].pathLength()),
0262                    s_onSurfaceTolerance);
0263     // The intersection MUST be unique
0264     BOOST_CHECK(!oIntersection[1].isValid());
0265 
0266     // Intersect backwards
0267     auto bIntersection = aPlane->intersect(tgContext, after, direction,
0268                                            BoundaryTolerance::None());
0269     // The intersection MUST be valid
0270     BOOST_CHECK(bIntersection[0].isValid());
0271     // The intersection MUST be reachable
0272     BOOST_CHECK_EQUAL(bIntersection[0].status(), IntersectionStatus::reachable);
0273     // The path length MUST be negative
0274     BOOST_CHECK_LT(bIntersection[0].pathLength(), 0.);
0275     // The intersection MUST be unique
0276     BOOST_CHECK(!bIntersection[1].isValid());
0277 
0278     // An out of bounds attempt: missed
0279     auto mIntersection = aPlane->intersect(tgContext, outside, direction,
0280                                            BoundaryTolerance::None());
0281     // The intersection MUST NOT be valid
0282     BOOST_CHECK(!mIntersection[0].isValid());
0283     // The intersection MUST be reachable
0284     BOOST_CHECK_EQUAL(mIntersection[0].status(),
0285                       IntersectionStatus::unreachable);
0286     // The path length MUST be negative
0287     BOOST_CHECK_GT(mIntersection[0].pathLength(), 0.);
0288     // The intersection MUST be unique
0289     BOOST_CHECK(!mIntersection[1].isValid());
0290 
0291     // An invalid attempt
0292     auto iIntersection = aPlane->intersect(tgContext, before, parallel,
0293                                            BoundaryTolerance::None());
0294     // The intersection MUST NOT be valid
0295     BOOST_CHECK(!iIntersection[0].isValid());
0296     // The intersection MUST be reachable
0297     BOOST_CHECK_EQUAL(iIntersection[0].status(),
0298                       IntersectionStatus::unreachable);
0299     // The intersection MUST be unique
0300     BOOST_CHECK(!iIntersection[1].isValid());
0301   };
0302 
0303   // In a nominal world
0304   testPlanarIntersection(Transform3::Identity());
0305 
0306   // In a system somewhere away
0307   testPlanarIntersection(aTransform);
0308 }
0309 
0310 /// This tests the intersection with line like surfaces (straw, perigee)
0311 /// as those share the same methods, only one test is
0312 /// sufficient
0313 /// - it looks for valid, non-valid, solutions
0314 BOOST_AUTO_TEST_CASE(LineIntersectionTest) {
0315   const double radius = 1_m;
0316   const double halfZ = 10_m;
0317 
0318   auto testLineAppraoch = [&](const Transform3& transform) -> void {
0319     // A Plane created with a specific transform
0320     auto aLine = Surface::makeShared<StrawSurface>(transform, radius, halfZ);
0321 
0322     /// Forward intersection test
0323     Vector3 before = transform * Vector3(-50_cm, -1_m, -1_m);
0324     Vector3 onit1 = transform * Vector3(0_m, 0_m, 0_m);
0325     Vector3 onitP = transform * Vector3(1_cm, 0_m, 23_um);
0326     Vector3 after = transform * Vector3(33_cm, 12_mm, 1_m);
0327     Vector3 outside = transform * Vector3(2., 0., 100_m);
0328 
0329     // Linear transform
0330     auto lTransform = transform.linear();
0331     Vector3 direction = lTransform * Vector3(2_cm, 3_cm, 5_cm).normalized();
0332     Vector3 normalP = lTransform * Vector3(0, 1., 0.).normalized();
0333     Vector3 parallel = lTransform * Vector3(0, 0., 1.).normalized();
0334 
0335     // A random intersection form backward
0336     // Intersect forward
0337     auto fIntersection = aLine->intersect(tgContext, before, direction,
0338                                           BoundaryTolerance::None());
0339     // The intersection MUST be valid
0340     BOOST_CHECK(fIntersection[0].isValid());
0341     // The intersection MUST be reachable
0342     BOOST_CHECK_EQUAL(fIntersection[0].status(), IntersectionStatus::reachable);
0343     // The path length MUST be positive
0344     BOOST_CHECK_GT(fIntersection[0].pathLength(), 0.);
0345     // The intersection MUST be unique
0346     BOOST_CHECK(!fIntersection[1].isValid());
0347 
0348     // On surface intersection - on the straw with random direction
0349     auto oIntersection = aLine->intersect(tgContext, onit1, direction,
0350                                           BoundaryTolerance::None());
0351     // The intersection MUST be valid
0352     BOOST_CHECK(oIntersection[0].isValid());
0353     // The intersection MUST be reachable
0354     BOOST_CHECK_EQUAL(oIntersection[0].status(), IntersectionStatus::onSurface);
0355     // The path length MUST be positive
0356     BOOST_CHECK_LT(std::abs(oIntersection[0].pathLength()),
0357                    s_onSurfaceTolerance);
0358     // The intersection MUST be unique
0359     BOOST_CHECK(!oIntersection[1].isValid());
0360 
0361     // On surface intersecion - on the surface with normal vector
0362     oIntersection =
0363         aLine->intersect(tgContext, onitP, normalP, BoundaryTolerance::None());
0364     // The intersection MUST be valid
0365     BOOST_CHECK(oIntersection[0].isValid());
0366     // The intersection MUST be reachable
0367     BOOST_CHECK_EQUAL(oIntersection[0].status(), IntersectionStatus::onSurface);
0368     // The path length MUST be positive
0369     BOOST_CHECK_LT(std::abs(oIntersection[0].pathLength()),
0370                    s_onSurfaceTolerance);
0371     // The intersection MUST be unique
0372     BOOST_CHECK(!oIntersection[1].isValid());
0373 
0374     // Intersect backwards
0375     auto bIntersection = aLine->intersect(tgContext, after, direction,
0376                                           BoundaryTolerance::None());
0377     // The intersection MUST be valid
0378     BOOST_CHECK(bIntersection[0].isValid());
0379     // The intersection MUST be reachable
0380     BOOST_CHECK_EQUAL(bIntersection[0].status(), IntersectionStatus::reachable);
0381     // The path length MUST be negative
0382     BOOST_CHECK_LT(bIntersection[0].pathLength(), 0.);
0383     // The intersection MUST be unique
0384     BOOST_CHECK(!bIntersection[1].isValid());
0385 
0386     // An out of bounds attempt: missed
0387     auto mIntersection = aLine->intersect(tgContext, outside, direction,
0388                                           BoundaryTolerance::None());
0389     // The intersection MUST NOT be valid
0390     BOOST_CHECK(!mIntersection[0].isValid());
0391     // The intersection MUST be reachable
0392     BOOST_CHECK_EQUAL(mIntersection[0].status(),
0393                       IntersectionStatus::unreachable);
0394     // The path length MUST be negative
0395     BOOST_CHECK_LT(mIntersection[0].pathLength(), 0.);
0396     // The intersection MUST be unique
0397     BOOST_CHECK(!mIntersection[1].isValid());
0398 
0399     // An invalid attempt
0400     auto iIntersection = aLine->intersect(tgContext, before, parallel,
0401                                           BoundaryTolerance::None());
0402     // The intersection MUST NOT be valid
0403     BOOST_CHECK(!iIntersection[0].isValid());
0404     // The intersection MUST be reachable
0405     BOOST_CHECK_EQUAL(iIntersection[0].status(),
0406                       IntersectionStatus::unreachable);
0407     // The intersection MUST be unique
0408     BOOST_CHECK(!iIntersection[1].isValid());
0409   };
0410 
0411   // In a nominal world
0412   testLineAppraoch(Transform3::Identity());
0413 
0414   // In a system somewhere away
0415   testLineAppraoch(aTransform);
0416 }
0417 
0418 BOOST_AUTO_TEST_SUITE_END()
0419 
0420 }  // namespace ActsTests