Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-09-30 08:04:28

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/Geometry/Blueprint.hpp"
0012 #include "Acts/Geometry/ContainerBlueprintNode.hpp"
0013 #include "Acts/Surfaces/BoundaryTolerance.hpp"
0014 #include "Acts/Surfaces/CylinderBounds.hpp"
0015 #include "Acts/Surfaces/CylinderSurface.hpp"
0016 #include "Acts/Surfaces/PerigeeSurface.hpp"
0017 
0018 #include <algorithm>
0019 
0020 #include "NavigatorTelescope.hpp"
0021 
0022 using namespace Acts;
0023 using namespace Acts::UnitLiterals;
0024 using namespace ActsTests::NavigatorTelescope;
0025 
0026 namespace ActsTests {
0027 
0028 // Tests of `NavigatorPlainOptions::additionalSurfaces` for Gen1 and Gen3
0029 
0030 namespace {
0031 
0032 /// Whether @p sub appears in @p full in the same order, gaps allowed.
0033 bool isSubsequence(const std::vector<const Surface*>& sub,
0034                    const std::vector<const Surface*>& full) {
0035   auto itr = full.begin();
0036   for (const Surface* surface : sub) {
0037     itr = std::find(itr, full.end(), surface);
0038     if (itr == full.end()) {
0039       return false;
0040     }
0041     ++itr;
0042   }
0043   return true;
0044 }
0045 
0046 }  // namespace
0047 
0048 BOOST_AUTO_TEST_SUITE(NavigatorAdditionalSurface)
0049 
0050 // By default a surface the track misses is targeted too
0051 
0052 BOOST_AUTO_TEST_CASE(OffPathGen1) {
0053   ACTS_LOCAL_LOGGER(getDefaultLogger("Gen1", logLevel));
0054   Telescope telescope = makeTelescopeGen1();
0055   Navigator navigator = makeNavigator(telescope.geometry, logger());
0056 
0057   auto additional = makeOutOfGeometrySurface({0, surfaceY, 0.25_m});
0058 
0059   Navigator::Options options(gctx);
0060   options.registerAdditionalSurface(*additional);
0061   NavigationTarget target = firstTarget(navigator, options, Vector3::Zero());
0062 
0063   BOOST_REQUIRE(!target.isNone());
0064   BOOST_CHECK_EQUAL(&target.surface(), additional.get());
0065 }
0066 
0067 BOOST_AUTO_TEST_CASE(OffPathGen3) {
0068   ACTS_LOCAL_LOGGER(getDefaultLogger("Gen3", logLevel));
0069   Telescope telescope = makeTelescopeGen3(logger());
0070   Navigator navigator = makeNavigator(telescope.geometry, logger());
0071 
0072   auto additional = makeOutOfGeometrySurface({0, surfaceY, 0.25_m});
0073 
0074   Navigator::Options options(gctx);
0075   options.registerAdditionalSurface(*additional);
0076   NavigationTarget target = firstTarget(navigator, options, Vector3::Zero());
0077 
0078   BOOST_REQUIRE(!target.isNone());
0079   BOOST_CHECK_EQUAL(&target.surface(), additional.get());
0080 }
0081 
0082 BOOST_AUTO_TEST_CASE(BoundsCheckedGen1) {
0083   ACTS_LOCAL_LOGGER(getDefaultLogger("Gen1", logLevel));
0084   Telescope telescope = makeTelescopeGen1();
0085   Navigator navigator = makeNavigator(telescope.geometry, logger());
0086 
0087   auto additional = makeOutOfGeometrySurface({0, surfaceY, 0.25_m});
0088 
0089   Navigator::Options options(gctx);
0090   options.registerAdditionalSurface(*additional, BoundaryTolerance::None());
0091   std::vector<const Surface*> reached =
0092       walk(navigator, options, Vector3::Zero(), Vector3::UnitZ());
0093 
0094   BOOST_CHECK_EQUAL(std::ranges::count(reached, additional.get()), 0);
0095 }
0096 
0097 BOOST_AUTO_TEST_CASE(BoundsCheckedGen3) {
0098   ACTS_LOCAL_LOGGER(getDefaultLogger("Gen3", logLevel));
0099   Telescope telescope = makeTelescopeGen3(logger());
0100   Navigator navigator = makeNavigator(telescope.geometry, logger());
0101 
0102   auto additional = makeOutOfGeometrySurface({0, surfaceY, 0.25_m});
0103 
0104   Navigator::Options options(gctx);
0105   options.registerAdditionalSurface(*additional, BoundaryTolerance::None());
0106   std::vector<const Surface*> reached =
0107       walk(navigator, options, Vector3::Zero(), Vector3::UnitZ());
0108 
0109   BOOST_CHECK_EQUAL(std::ranges::count(reached, additional.get()), 0);
0110 }
0111 
0112 // The candidates of the geometry are all still reached
0113 
0114 BOOST_AUTO_TEST_CASE(NothingOfTheGeometryIsSkippedGen1) {
0115   ACTS_LOCAL_LOGGER(getDefaultLogger("Gen1", logLevel));
0116   Telescope telescope = makeTelescopeGen1();
0117   Navigator navigator = makeNavigator(telescope.geometry, logger());
0118 
0119   // Between the origin and the two telescope surfaces
0120   auto additional = makeOutOfGeometrySurface({0, 0, 0.25_m});
0121 
0122   Navigator::Options plain(gctx);
0123   std::vector<const Surface*> baseline =
0124       walk(navigator, plain, Vector3::Zero(), Vector3::UnitZ(), 20);
0125 
0126   Navigator::Options options(gctx);
0127   options.registerAdditionalSurface(*additional);
0128   std::vector<const Surface*> reached =
0129       walk(navigator, options, Vector3::Zero(), Vector3::UnitZ(), 20);
0130 
0131   BOOST_REQUIRE(!baseline.empty());
0132   BOOST_CHECK_EQUAL(std::ranges::count(reached, additional.get()), 1);
0133   BOOST_CHECK(isSubsequence(baseline, reached));
0134 }
0135 
0136 BOOST_AUTO_TEST_CASE(NothingOfTheGeometryIsSkippedGen3) {
0137   ACTS_LOCAL_LOGGER(getDefaultLogger("Gen3", logLevel));
0138   Telescope telescope = makeTelescopeGen3(logger());
0139   Navigator navigator = makeNavigator(telescope.geometry, logger());
0140 
0141   auto additional = makeOutOfGeometrySurface({0, 0, 0.25_m});
0142 
0143   Navigator::Options plain(gctx);
0144   std::vector<const Surface*> baseline =
0145       walk(navigator, plain, Vector3::Zero(), Vector3::UnitZ(), 20);
0146 
0147   Navigator::Options options(gctx);
0148   options.registerAdditionalSurface(*additional);
0149   std::vector<const Surface*> reached =
0150       walk(navigator, options, Vector3::Zero(), Vector3::UnitZ(), 20);
0151 
0152   BOOST_REQUIRE(!baseline.empty());
0153   BOOST_CHECK_EQUAL(std::ranges::count(reached, additional.get()), 1);
0154   BOOST_CHECK(isSubsequence(baseline, reached));
0155 }
0156 
0157 // The surface crosses the track at z = 0.96 m, outside the telescope volume,
0158 // so the closer boundary wins.
0159 BOOST_AUTO_TEST_CASE(PortalWinsGen1) {
0160   ACTS_LOCAL_LOGGER(getDefaultLogger("Gen1", logLevel));
0161   Telescope telescope = makeTelescopeGen1();
0162   Navigator navigator = makeNavigator(telescope.geometry, logger());
0163 
0164   auto outside = makeOutOfGeometrySurface({0, 0, 0.96_m}, 10_m, 10_m);
0165 
0166   Navigator::Options options(gctx);
0167   options.registerAdditionalSurface(*outside);
0168   std::vector<const Surface*> reached =
0169       walk(navigator, options, Vector3::Zero(), Vector3::UnitZ(), 20);
0170 
0171   auto boundary = std::ranges::find_if(reached, [](const Surface* sf) {
0172     return sf->geometryId().boundary() != 0;
0173   });
0174   BOOST_REQUIRE(boundary != reached.end());
0175   BOOST_CHECK_EQUAL(std::count(reached.begin(), boundary, outside.get()), 0);
0176 }
0177 
0178 // The Gen3 world volume reaches z = 0.97 m, so the surface is reached there.
0179 BOOST_AUTO_TEST_CASE(PortalWinsGen3) {
0180   ACTS_LOCAL_LOGGER(getDefaultLogger("Gen3", logLevel));
0181   Telescope telescope = makeTelescopeGen3(logger());
0182   Navigator navigator = makeNavigator(telescope.geometry, logger());
0183 
0184   auto outside = makeOutOfGeometrySurface({0, 0, 0.96_m}, 10_m, 10_m);
0185 
0186   Navigator::Options options(gctx);
0187   options.registerAdditionalSurface(*outside);
0188   std::vector<const Surface*> reached =
0189       walk(navigator, options, Vector3::Zero(), Vector3::UnitZ(), 20);
0190 
0191   auto itr = std::ranges::find(reached, outside.get());
0192   BOOST_REQUIRE(itr != reached.end());
0193   BOOST_CHECK_EQUAL(std::ranges::count(reached, outside.get()), 1);
0194   // The portal of the telescope volume comes first
0195   BOOST_CHECK(std::ranges::any_of(reached.begin(), itr, [](const Surface* sf) {
0196     return sf->geometryId().boundary() != 0;
0197   }));
0198 }
0199 
0200 // A line surface, intersected at the point of closest approach
0201 BOOST_AUTO_TEST_CASE(PerigeeGen3) {
0202   ACTS_LOCAL_LOGGER(getDefaultLogger("Gen3", logLevel));
0203   Telescope telescope = makeTelescopeGen3(logger());
0204   Navigator navigator = makeNavigator(telescope.geometry, logger());
0205 
0206   auto perigee = Surface::makeShared<PerigeeSurface>(Vector3{0.1_m, 0, 0.25_m});
0207 
0208   Navigator::Options options(gctx);
0209   options.registerAdditionalSurface(*perigee);
0210   const Vector3 dir = Vector3{0.3, 0, 1}.normalized();
0211   std::vector<const Surface*> reached =
0212       walk(navigator, options, Vector3::Zero(), dir);
0213 
0214   BOOST_CHECK_EQUAL(std::ranges::count(reached, perigee.get()), 1);
0215 }
0216 
0217 // `dropAfterReached`. A straight walk cannot approach a surface twice, so the
0218 // propagation steps back in front of it.
0219 
0220 namespace {
0221 /// Reach @p additional, then ask for the next target from just before it.
0222 NavigationTarget targetOnSecondApproach(const Navigator& navigator,
0223                                         const Navigator::Options& options,
0224                                         const Surface& additional) {
0225   Navigator::State state = navigator.makeState(options);
0226   Vector3 position = Vector3::Zero();
0227   const Vector3 direction = Vector3::UnitZ();
0228   BOOST_REQUIRE(
0229       navigator
0230           .initialize(state, {.position = position, .direction = direction})
0231           .ok());
0232 
0233   NavigationTarget target = navigator.nextTarget(state, position, direction);
0234   BOOST_REQUIRE(!target.isNone());
0235   BOOST_REQUIRE_EQUAL(&target.surface(), &additional);
0236   stepOnto(position, direction, target.surface());
0237   navigator.handleSurfaceReached(state, position, direction, target.surface());
0238 
0239   // Approach it once more, from 1 mm in front of it
0240   position -= 1_mm * direction;
0241   return navigator.nextTarget(state, position, direction);
0242 }
0243 }  // namespace
0244 
0245 BOOST_AUTO_TEST_CASE(DroppedAfterReachedGen1) {
0246   ACTS_LOCAL_LOGGER(getDefaultLogger("Gen1", logLevel));
0247   Telescope telescope = makeTelescopeGen1();
0248   Navigator navigator = makeNavigator(telescope.geometry, logger());
0249 
0250   auto additional = makeOutOfGeometrySurface({0, 0, 0.25_m});
0251 
0252   Navigator::Options options(gctx);
0253   options.registerAdditionalSurface(*additional);
0254   NavigationTarget target =
0255       targetOnSecondApproach(navigator, options, *additional);
0256 
0257   BOOST_CHECK_NE(&target.surface(), additional.get());
0258 }
0259 
0260 BOOST_AUTO_TEST_CASE(KeptAfterReachedGen1) {
0261   ACTS_LOCAL_LOGGER(getDefaultLogger("Gen1", logLevel));
0262   Telescope telescope = makeTelescopeGen1();
0263   Navigator navigator = makeNavigator(telescope.geometry, logger());
0264 
0265   auto additional = makeOutOfGeometrySurface({0, 0, 0.25_m});
0266 
0267   Navigator::Options options(gctx);
0268   options.registerAdditionalSurface(*additional, BoundaryTolerance::Infinite(),
0269                                     nullptr, false);
0270   NavigationTarget target =
0271       targetOnSecondApproach(navigator, options, *additional);
0272 
0273   BOOST_REQUIRE(!target.isNone());
0274   BOOST_CHECK_EQUAL(&target.surface(), additional.get());
0275 }
0276 
0277 // A volume scopes the surface to that one volume
0278 
0279 BOOST_AUTO_TEST_CASE(ScopedToAVolumeGen3) {
0280   auto logger = getDefaultLogger("Gen3", logLevel);
0281 
0282   Blueprint::Config cfg;
0283   cfg.envelope = ExtentEnvelope{{
0284       .x = {20_mm, 20_mm},
0285       .y = {20_mm, 20_mm},
0286       .z = {20_mm, 20_mm},
0287   }};
0288   Blueprint root{cfg};
0289 
0290   root.addCuboidContainer("Stack", AxisDirection::AxisZ, [&](auto& stack) {
0291     stack.addChild(
0292         std::make_shared<StaticBlueprintNode>(std::make_unique<TrackingVolume>(
0293             Transform3{Translation3{Vector3{0, 0, -0.5_m}}},
0294             std::make_shared<CuboidVolumeBounds>(0.5_m, 0.5_m, 0.5_m),
0295             "near")));
0296     stack.addChild(
0297         std::make_shared<StaticBlueprintNode>(std::make_unique<TrackingVolume>(
0298             Transform3{Translation3{Vector3{0, 0, 0.5_m}}},
0299             std::make_shared<CuboidVolumeBounds>(0.5_m, 0.5_m, 0.5_m), "far")));
0300   });
0301 
0302   auto geometry = std::shared_ptr<const TrackingGeometry>(
0303       root.construct({}, gctx, *logger));
0304   Navigator navigator = makeNavigator(geometry, *logger);
0305 
0306   // Inside the near volume
0307   auto additional = makeOutOfGeometrySurface({0, 0, -0.5_m});
0308   const TrackingVolume* farVolume = geometry->findVolumeByName("far");
0309   BOOST_REQUIRE(farVolume != nullptr);
0310 
0311   const Vector3 start{0, 0, -0.9_m};
0312 
0313   // Scoped to the far volume, where the track never crosses it
0314   {
0315     Navigator::Options options(gctx);
0316     options.registerAdditionalSurface(*additional,
0317                                       BoundaryTolerance::Infinite(), farVolume);
0318     std::vector<const Surface*> reached =
0319         walk(navigator, options, start, Vector3::UnitZ(), 12);
0320     BOOST_CHECK_EQUAL(std::ranges::count(reached, additional.get()), 0);
0321   }
0322   // Unscoped, so the volume that holds the crossing offers it
0323   {
0324     Navigator::Options options(gctx);
0325     options.registerAdditionalSurface(*additional);
0326     std::vector<const Surface*> reached =
0327         walk(navigator, options, start, Vector3::UnitZ(), 12);
0328     BOOST_CHECK_EQUAL(std::ranges::count(reached, additional.get()), 1);
0329   }
0330 }
0331 
0332 // Several additional surfaces are offered in the order the track crosses them
0333 
0334 BOOST_AUTO_TEST_CASE(SeveralSurfacesInOrderGen3) {
0335   ACTS_LOCAL_LOGGER(getDefaultLogger("Gen3", logLevel));
0336   Telescope telescope = makeTelescopeGen3(logger());
0337   Navigator navigator = makeNavigator(telescope.geometry, logger());
0338 
0339   auto first = makeOutOfGeometrySurface({0, 0, 0.1_m});
0340   auto second = makeOutOfGeometrySurface({0, 0, 0.2_m});
0341   auto third = makeOutOfGeometrySurface({0, 0, 0.3_m});
0342 
0343   Navigator::Options options(gctx);
0344   options.registerAdditionalSurface(*third);
0345   options.registerAdditionalSurface(*first);
0346   options.registerAdditionalSurface(*second);
0347 
0348   std::vector<const Surface*> reached =
0349       walk(navigator, options, Vector3::Zero(), Vector3::UnitZ(), 20);
0350 
0351   std::vector<const Surface*> reachedAdditional;
0352   for (const Surface* surface : reached) {
0353     if (surface == first.get() || surface == second.get() ||
0354         surface == third.get()) {
0355       reachedAdditional.push_back(surface);
0356     }
0357   }
0358   const std::vector<const Surface*> expected{first.get(), second.get(),
0359                                              third.get()};
0360   BOOST_CHECK(reachedAdditional == expected);
0361 }
0362 
0363 // The closer solution is behind the propagation, so the one ahead is offered
0364 
0365 BOOST_AUTO_TEST_CASE(SecondSolutionAheadGen1) {
0366   ACTS_LOCAL_LOGGER(getDefaultLogger("Gen1", logLevel));
0367   Telescope telescope = makeTelescopeGen1();
0368   Navigator navigator = makeNavigator(telescope.geometry, logger());
0369 
0370   // A cylinder around the y axis, so a track along z crosses it at z = +-R
0371   constexpr double radius = 0.3_m;
0372   auto additional = Surface::makeShared<CylinderSurface>(
0373       Transform3{AngleAxis3{90._degree, Vector3::UnitX()}},
0374       std::make_shared<const CylinderBounds>(radius, 0.5_m));
0375 
0376   Navigator::Options options(gctx);
0377   options.registerAdditionalSurface(*additional);
0378   // Start closer to the solution behind than to the one ahead
0379   NavigationTarget target =
0380       firstTarget(navigator, options, {0, 0, -0.2 * radius});
0381 
0382   BOOST_REQUIRE(!target.isNone());
0383   BOOST_CHECK_EQUAL(&target.surface(), additional.get());
0384   BOOST_CHECK_GT(target.pathLength(), 0.);
0385 }
0386 
0387 BOOST_AUTO_TEST_SUITE_END()
0388 
0389 }  // namespace ActsTests