Back to home page

sPhenix code displayed by LXR

 
 

    


File indexing completed on 2025-08-06 08:11:24

0001 // This file is part of the Acts project.
0002 //
0003 // Copyright (C) 2023 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 http://mozilla.org/MPL/2.0/.
0008 
0009 #include <boost/test/unit_test.hpp>
0010 
0011 #include "Acts/Definitions/Algebra.hpp"
0012 #include "Acts/Definitions/Direction.hpp"
0013 #include "Acts/Definitions/Units.hpp"
0014 #include "Acts/Detector/Detector.hpp"
0015 #include "Acts/Detector/DetectorVolume.hpp"
0016 #include "Acts/Detector/GeometryIdGenerator.hpp"
0017 #include "Acts/Detector/PortalGenerators.hpp"
0018 #include "Acts/Detector/detail/CuboidalDetectorHelper.hpp"
0019 #include "Acts/EventData/TrackParameters.hpp"
0020 #include "Acts/Geometry/CuboidVolumeBounds.hpp"
0021 #include "Acts/Geometry/GeometryContext.hpp"
0022 #include "Acts/MagneticField/MagneticFieldContext.hpp"
0023 #include "Acts/Navigation/DetectorNavigator.hpp"
0024 #include "Acts/Navigation/DetectorVolumeFinders.hpp"
0025 #include "Acts/Navigation/SurfaceCandidatesUpdaters.hpp"
0026 #include "Acts/Propagator/AbortList.hpp"
0027 #include "Acts/Propagator/ActionList.hpp"
0028 #include "Acts/Propagator/Propagator.hpp"
0029 #include "Acts/Propagator/StraightLineStepper.hpp"
0030 #include "Acts/Surfaces/CylinderSurface.hpp"
0031 #include "Acts/Surfaces/PlaneSurface.hpp"
0032 #include "Acts/Surfaces/RectangleBounds.hpp"
0033 #include "Acts/Tests/CommonHelpers/FloatComparisons.hpp"
0034 #include "Acts/Utilities/Logger.hpp"
0035 
0036 using namespace Acts::UnitLiterals;
0037 
0038 namespace Acts {
0039 class Surface;
0040 }  // namespace Acts
0041 
0042 Acts::GeometryContext geoContext;
0043 Acts::MagneticFieldContext mfContext;
0044 
0045 /// A simple action to record the navigation state
0046 struct StateRecorder {
0047   using result_type = std::vector<Acts::Experimental::DetectorNavigator::State>;
0048 
0049   template <typename propagator_state_t, typename stepper_t,
0050             typename navigator_t>
0051   void operator()(propagator_state_t& state, const stepper_t& /*stepper*/,
0052                   const navigator_t& /*navigator*/, result_type& result,
0053                   const Acts::Logger& /*logger*/) const {
0054     result.push_back(state.navigation);
0055   }
0056 };
0057 
0058 BOOST_AUTO_TEST_SUITE(DetectorNavigator)
0059 
0060 // Initialization tests
0061 BOOST_AUTO_TEST_CASE(DetectorNavigatorTestsInitialization) {
0062   auto bounds = std::make_unique<Acts::CuboidVolumeBounds>(3, 3, 3);
0063   auto volume = Acts::Experimental::DetectorVolumeFactory::construct(
0064       Acts::Experimental::defaultPortalAndSubPortalGenerator(), geoContext,
0065       "volume", Acts::Transform3::Identity(), std::move(bounds), {}, {},
0066       Acts::Experimental::tryNoVolumes(),
0067       Acts::Experimental::tryAllPortalsAndSurfaces());
0068   volume->assignGeometryId(1);
0069   auto detector = Acts::Experimental::Detector::makeShared(
0070       "detector", {volume}, Acts::Experimental::tryRootVolumes());
0071 
0072   using ActionList = Acts::ActionList<>;
0073   using AbortList = Acts::AbortList<>;
0074 
0075   auto options =
0076       Acts::PropagatorOptions<ActionList, AbortList>(geoContext, mfContext);
0077 
0078   auto stepper = Acts::StraightLineStepper();
0079 
0080   Acts::Vector4 pos(-2, 0, 0, 0);
0081   auto start = Acts::CurvilinearTrackParameters(
0082       pos, 0_degree, 90_degree, 1_e / 1_GeV, std::nullopt,
0083       Acts::ParticleHypothesis::electron());
0084 
0085   //
0086   // (1) Test for inactivity
0087   //
0088   // Run without anything present
0089   {
0090     Acts::Experimental::DetectorNavigator::Config navCfg;
0091     navCfg.resolveSensitive = false;
0092     navCfg.resolveMaterial = false;
0093     navCfg.resolvePassive = false;
0094 
0095     auto navigator = Acts::Experimental::DetectorNavigator(navCfg);
0096 
0097     auto propagator = Acts::Propagator<Acts::StraightLineStepper,
0098                                        Acts::Experimental::DetectorNavigator>(
0099         stepper, navigator);
0100 
0101     auto state = propagator.makeState(start, options);
0102 
0103     BOOST_CHECK_THROW(navigator.initialize(state, stepper),
0104                       std::invalid_argument);
0105 
0106     navigator.preStep(state, stepper);
0107     auto preStepState = state.navigation;
0108     BOOST_CHECK_EQUAL(preStepState.currentDetector, nullptr);
0109     BOOST_CHECK_EQUAL(preStepState.currentVolume, nullptr);
0110     BOOST_CHECK_EQUAL(preStepState.currentSurface, nullptr);
0111     BOOST_CHECK_EQUAL(preStepState.currentPortal, nullptr);
0112     BOOST_CHECK(preStepState.surfaceCandidates.empty());
0113 
0114     navigator.postStep(state, stepper);
0115     auto postStepState = state.navigation;
0116     BOOST_CHECK_EQUAL(postStepState.currentDetector, nullptr);
0117     BOOST_CHECK_EQUAL(postStepState.currentVolume, nullptr);
0118     BOOST_CHECK_EQUAL(postStepState.currentSurface, nullptr);
0119     BOOST_CHECK_EQUAL(postStepState.currentPortal, nullptr);
0120     BOOST_CHECK(postStepState.surfaceCandidates.empty());
0121   }
0122 
0123   // Run with geometry but without resolving
0124   {
0125     Acts::Experimental::DetectorNavigator::Config navCfg;
0126     navCfg.resolveSensitive = false;
0127     navCfg.resolveMaterial = false;
0128     navCfg.resolvePassive = false;
0129     navCfg.detector = detector.get();
0130 
0131     auto navigator = Acts::Experimental::DetectorNavigator(navCfg);
0132 
0133     auto propagator = Acts::Propagator<Acts::StraightLineStepper,
0134                                        Acts::Experimental::DetectorNavigator>(
0135         stepper, navigator);
0136 
0137     auto state = propagator.makeState(start, options);
0138 
0139     navigator.initialize(state, stepper);
0140 
0141     navigator.preStep(state, stepper);
0142     auto preStepState = state.navigation;
0143     BOOST_CHECK_EQUAL(preStepState.currentSurface, nullptr);
0144     BOOST_CHECK_EQUAL(preStepState.currentPortal, nullptr);
0145 
0146     navigator.postStep(state, stepper);
0147     auto postStepState = state.navigation;
0148     BOOST_CHECK_EQUAL(postStepState.currentSurface, nullptr);
0149     BOOST_CHECK_EQUAL(postStepState.currentPortal, nullptr);
0150   }
0151 
0152   //
0153   // (2) Initialization tests
0154   //
0155   // Run from endOfWorld
0156   {
0157     Acts::Vector4 posEoW(-20, 0, 0, 0);
0158     auto startEoW = Acts::CurvilinearTrackParameters(
0159         posEoW, 0_degree, 90_degree, 1_e / 1_GeV, std::nullopt,
0160         Acts::ParticleHypothesis::electron());
0161 
0162     Acts::Experimental::DetectorNavigator::Config navCfg;
0163     navCfg.detector = detector.get();
0164 
0165     auto navigator = Acts::Experimental::DetectorNavigator(navCfg);
0166 
0167     auto propagator = Acts::Propagator<Acts::StraightLineStepper,
0168                                        Acts::Experimental::DetectorNavigator>(
0169         stepper, navigator);
0170 
0171     auto state = propagator.makeState(startEoW, options);
0172 
0173     BOOST_CHECK(navigator.endOfWorldReached(state.navigation));
0174 
0175     BOOST_CHECK_THROW(navigator.initialize(state, stepper),
0176                       std::invalid_argument);
0177     auto initState = state.navigation;
0178     BOOST_CHECK_EQUAL(initState.currentVolume, nullptr);
0179     BOOST_CHECK_EQUAL(initState.currentSurface, nullptr);
0180     BOOST_CHECK_EQUAL(initState.currentPortal, nullptr);
0181     BOOST_CHECK(initState.surfaceCandidates.empty());
0182 
0183     navigator.preStep(state, stepper);
0184     auto preStepState = state.navigation;
0185     BOOST_CHECK_EQUAL(preStepState.currentVolume, nullptr);
0186     BOOST_CHECK_EQUAL(preStepState.currentSurface, nullptr);
0187     BOOST_CHECK_EQUAL(preStepState.currentPortal, nullptr);
0188     BOOST_CHECK(preStepState.surfaceCandidates.empty());
0189 
0190     navigator.postStep(state, stepper);
0191     auto postStepState = state.navigation;
0192     BOOST_CHECK_EQUAL(postStepState.currentVolume, nullptr);
0193     BOOST_CHECK_EQUAL(postStepState.currentSurface, nullptr);
0194     BOOST_CHECK_EQUAL(postStepState.currentPortal, nullptr);
0195     BOOST_CHECK(postStepState.surfaceCandidates.empty());
0196   }
0197 
0198   // Initialize properly
0199   {
0200     Acts::Experimental::DetectorNavigator::Config navCfg;
0201     navCfg.detector = detector.get();
0202 
0203     auto navigator = Acts::Experimental::DetectorNavigator(navCfg);
0204 
0205     auto propagator = Acts::Propagator<Acts::StraightLineStepper,
0206                                        Acts::Experimental::DetectorNavigator>(
0207         stepper, navigator);
0208 
0209     auto state = propagator.makeState(start, options);
0210 
0211     navigator.initialize(state, stepper);
0212     auto initState = state.navigation;
0213     BOOST_CHECK_EQUAL(initState.currentDetector, detector.get());
0214     BOOST_CHECK_EQUAL(
0215         initState.currentVolume,
0216         detector->findDetectorVolume(geoContext, start.position()));
0217     BOOST_CHECK_EQUAL(initState.currentSurface, nullptr);
0218     BOOST_CHECK_EQUAL(initState.currentPortal, nullptr);
0219     BOOST_CHECK_EQUAL(initState.surfaceCandidates.size(), 1);
0220   }
0221 }
0222 
0223 // Stadard forward and backward propagation
0224 // through cubic volumes with planar surfaces
0225 // and no surprises
0226 BOOST_AUTO_TEST_CASE(DetectorNavigatorTestsForwardBackward) {
0227   // Construct a cubic detector with 3 volumes
0228   Acts::RotationMatrix3 rotation;
0229   double angle = 90_degree;
0230   Acts::Vector3 xPos(cos(angle), 0., sin(angle));
0231   Acts::Vector3 yPos(0., 1., 0.);
0232   Acts::Vector3 zPos(-sin(angle), 0., cos(angle));
0233   rotation.col(0) = xPos;
0234   rotation.col(1) = yPos;
0235   rotation.col(2) = zPos;
0236 
0237   auto bounds1 = std::make_unique<Acts::CuboidVolumeBounds>(3, 3, 3);
0238   auto transform1 = Acts::Transform3::Identity();
0239   auto surface1 = Acts::Surface::makeShared<Acts::PlaneSurface>(
0240       transform1 * Acts::Transform3(rotation),
0241       std::make_shared<Acts::RectangleBounds>(2, 2));
0242   auto volume1 = Acts::Experimental::DetectorVolumeFactory::construct(
0243       Acts::Experimental::defaultPortalAndSubPortalGenerator(), geoContext,
0244       "volume1", transform1, std::move(bounds1), {surface1}, {},
0245       Acts::Experimental::tryNoVolumes(),
0246       Acts::Experimental::tryAllPortalsAndSurfaces());
0247 
0248   auto bounds2 = std::make_unique<Acts::CuboidVolumeBounds>(3, 3, 3);
0249   auto transform2 =
0250       Acts::Transform3::Identity() * Acts::Translation3(Acts::Vector3(6, 0, 0));
0251   auto surface2 = Acts::Surface::makeShared<Acts::PlaneSurface>(
0252       transform2 * Acts::Transform3(rotation),
0253       std::make_shared<Acts::RectangleBounds>(2, 2));
0254   auto volume2 = Acts::Experimental::DetectorVolumeFactory::construct(
0255       Acts::Experimental::defaultPortalAndSubPortalGenerator(), geoContext,
0256       "volume2", transform2, std::move(bounds2), {surface2}, {},
0257       Acts::Experimental::tryNoVolumes(),
0258       Acts::Experimental::tryAllPortalsAndSurfaces());
0259 
0260   auto bounds3 = std::make_unique<Acts::CuboidVolumeBounds>(3, 3, 3);
0261   auto transform3 = Acts::Transform3::Identity() *
0262                     Acts::Translation3(Acts::Vector3(12, 0, 0));
0263   auto surface3 = Acts::Surface::makeShared<Acts::PlaneSurface>(
0264       transform3 * Acts::Transform3(rotation),
0265       std::make_shared<Acts::RectangleBounds>(2, 2));
0266   auto volume3 = Acts::Experimental::DetectorVolumeFactory::construct(
0267       Acts::Experimental::defaultPortalAndSubPortalGenerator(), geoContext,
0268       "volume3", transform3, std::move(bounds3), {surface3}, {},
0269       Acts::Experimental::tryNoVolumes(),
0270       Acts::Experimental::tryAllPortalsAndSurfaces());
0271 
0272   std::vector<std::shared_ptr<Acts::Experimental::DetectorVolume>>
0273       detectorVolumes = {volume1, volume2, volume3};
0274 
0275   auto portalContainer =
0276       Acts::Experimental::detail::CuboidalDetectorHelper::connect(
0277           geoContext, detectorVolumes, Acts::BinningValue::binX, {},
0278           Acts::Logging::VERBOSE);
0279 
0280   // Make sure that the geometry ids are
0281   // independent of the potential Id generation
0282   // changes
0283   int id = 1;
0284 
0285   // Volume ids: 1-3
0286   for (auto& volume : detectorVolumes) {
0287     volume->assignGeometryId(id);
0288     id++;
0289   }
0290   // Intervolume portal ids: 6,7,10,11
0291   for (auto& volume : detectorVolumes) {
0292     for (auto& port : volume->portalPtrs()) {
0293       if (port->surface().geometryId() == 0) {
0294         port->surface().assignGeometryId(id);
0295         id++;
0296       }
0297     }
0298   }
0299   // Surface ids: 12-14
0300   for (auto& surf : {surface1, surface2, surface3}) {
0301     surf->assignGeometryId(id);
0302     id++;
0303   }
0304 
0305   auto detector = Acts::Experimental::Detector::makeShared(
0306       "cubicDetector", detectorVolumes, Acts::Experimental::tryRootVolumes());
0307 
0308   using ActionList = Acts::ActionList<StateRecorder>;
0309   using AbortList = Acts::AbortList<Acts::EndOfWorldReached>;
0310 
0311   Acts::Experimental::DetectorNavigator::Config navCfg;
0312   navCfg.detector = detector.get();
0313 
0314   auto stepper = Acts::StraightLineStepper();
0315 
0316   auto navigator = Acts::Experimental::DetectorNavigator(
0317       navCfg, Acts::getDefaultLogger("DetectorNavigator",
0318                                      Acts::Logging::Level::VERBOSE));
0319 
0320   auto options =
0321       Acts::PropagatorOptions<ActionList, AbortList>(geoContext, mfContext);
0322   options.direction = Acts::Direction::Forward;
0323 
0324   auto propagator = Acts::Propagator<Acts::StraightLineStepper,
0325                                      Acts::Experimental::DetectorNavigator>(
0326       stepper, navigator,
0327       Acts::getDefaultLogger("Propagator", Acts::Logging::Level::VERBOSE));
0328 
0329   // Forward and backward propagation
0330   // should be consistent between each other
0331   Acts::Vector4 posFwd(-2, 0, 0, 0);
0332   auto startFwd = Acts::CurvilinearTrackParameters(
0333       posFwd, 0_degree, 90_degree, 1_e / 1_GeV, std::nullopt,
0334       Acts::ParticleHypothesis::electron());
0335 
0336   auto resultFwd = propagator.propagate(startFwd, options).value();
0337   auto statesFwd = resultFwd.get<StateRecorder::result_type>();
0338 
0339   options.direction = Acts::Direction::Backward;
0340 
0341   Acts::Vector4 posBwd(14, 0, 0, 0);
0342   auto startBwd = Acts::CurvilinearTrackParameters(
0343       posBwd, 0_degree, 90_degree, 1_e / 1_GeV, std::nullopt,
0344       Acts::ParticleHypothesis::electron());
0345 
0346   auto resultBwd = propagator.propagate(startBwd, options).value();
0347   auto statesBwd = resultBwd.get<StateRecorder::result_type>();
0348 
0349   // 7 steps to reach the end of world
0350   // + 1 recording in the post-step
0351   // + 1 recording before the stepping loop
0352   BOOST_CHECK_EQUAL(statesFwd.size(), 9u);
0353   BOOST_CHECK_EQUAL(statesFwd.size(), statesBwd.size());
0354   BOOST_CHECK_EQUAL(statesFwd[0].surfaceCandidates.size(), 2u);
0355   BOOST_CHECK_EQUAL(statesBwd[0].surfaceCandidates.size(), 2u);
0356 
0357   // Action list call before the first step
0358   // Starting in the volume1
0359   BOOST_CHECK_EQUAL(statesFwd[0].currentVolume->geometryId(), 1);
0360   BOOST_CHECK_EQUAL(statesFwd[0].currentSurface, nullptr);
0361   BOOST_CHECK_EQUAL(statesFwd[0].currentPortal, nullptr);
0362 
0363   // Step to the surface inside volume1
0364   BOOST_CHECK_EQUAL(statesFwd[1].currentVolume->geometryId(), 1);
0365   BOOST_CHECK_EQUAL(statesFwd[1].currentSurface->geometryId(), 12);
0366   BOOST_CHECK_EQUAL(statesFwd[1].currentPortal, nullptr);
0367 
0368   // Step to the volume1|volume2 boundary
0369   BOOST_CHECK_EQUAL(statesFwd[2].currentVolume->geometryId(), 1);
0370   BOOST_CHECK_EQUAL(statesFwd[2].currentSurface, nullptr);
0371   BOOST_CHECK_EQUAL(statesFwd[2].currentPortal->surface().geometryId(), 7);
0372 
0373   // Step to the surface inside volume2
0374   BOOST_CHECK_EQUAL(statesFwd[3].currentVolume->geometryId(), 2);
0375   BOOST_CHECK_EQUAL(statesFwd[3].currentSurface->geometryId(), 13);
0376   BOOST_CHECK_EQUAL(statesFwd[3].currentPortal, nullptr);
0377 
0378   // Step to the volume2|volume3 boundary
0379   BOOST_CHECK_EQUAL(statesFwd[4].currentVolume->geometryId(), 2);
0380   BOOST_CHECK_EQUAL(statesFwd[4].currentSurface, nullptr);
0381   BOOST_CHECK_EQUAL(statesFwd[4].currentPortal->surface().geometryId(), 10);
0382 
0383   // Step to the surface inside volume3
0384   BOOST_CHECK_EQUAL(statesFwd[5].currentVolume->geometryId(), 3);
0385   BOOST_CHECK_EQUAL(statesFwd[5].currentSurface->geometryId(), 14);
0386   BOOST_CHECK_EQUAL(statesFwd[5].currentPortal, nullptr);
0387 
0388   // Step to the volume3|endOfWorld boundary
0389   BOOST_CHECK_EQUAL(statesFwd[6].currentVolume->geometryId(), 3);
0390   BOOST_CHECK_EQUAL(statesFwd[6].currentSurface, nullptr);
0391   BOOST_CHECK_EQUAL(statesFwd[6].currentPortal->surface().geometryId(), 11);
0392 
0393   // Step to the end of world
0394   BOOST_CHECK(navigator.endOfWorldReached(statesFwd[7]));
0395   BOOST_CHECK(navigator.endOfWorldReached(statesBwd[7]));
0396 
0397   // Action list call before the first step
0398   // Starting in the volume3
0399   BOOST_CHECK_EQUAL(statesBwd[6].currentVolume->geometryId(), 1);
0400   BOOST_CHECK_EQUAL(statesBwd[6].currentSurface, nullptr);
0401   BOOST_CHECK_EQUAL(statesBwd[6].currentPortal->surface().geometryId(), 6);
0402 
0403   // Step to the surface inside volume1
0404   BOOST_CHECK_EQUAL(statesBwd[5].currentVolume->geometryId(), 1);
0405   BOOST_CHECK_EQUAL(statesBwd[5].currentSurface->geometryId(), 12);
0406   BOOST_CHECK_EQUAL(statesBwd[5].currentPortal, nullptr);
0407 
0408   // Step to the volume1|volume2 boundary
0409   BOOST_CHECK_EQUAL(statesBwd[4].currentVolume->geometryId(), 2);
0410   BOOST_CHECK_EQUAL(statesBwd[4].currentSurface, nullptr);
0411   BOOST_CHECK_EQUAL(statesBwd[4].currentPortal->surface().geometryId(), 7);
0412 
0413   // Step to the surface inside volume2
0414   BOOST_CHECK_EQUAL(statesBwd[3].currentVolume->geometryId(), 2);
0415   BOOST_CHECK_EQUAL(statesBwd[3].currentSurface->geometryId(), 13);
0416   BOOST_CHECK_EQUAL(statesBwd[3].currentPortal, nullptr);
0417 
0418   // Step to the volume2|volume3 boundary
0419   BOOST_CHECK_EQUAL(statesBwd[2].currentVolume->geometryId(), 3);
0420   BOOST_CHECK_EQUAL(statesBwd[2].currentSurface, nullptr);
0421   BOOST_CHECK_EQUAL(statesBwd[2].currentPortal->surface().geometryId(), 10);
0422   BOOST_CHECK_EQUAL(statesBwd[2].surfaceCandidates.size(), 2u);
0423 
0424   // Step to the surface inside volume3
0425   BOOST_CHECK_EQUAL(statesBwd[1].currentVolume->geometryId(), 3);
0426   BOOST_CHECK_EQUAL(statesBwd[1].currentSurface->geometryId(), 14);
0427   BOOST_CHECK_EQUAL(statesBwd[1].currentPortal, nullptr);
0428 
0429   // Step to the volume3|endOfWorld boundary
0430   BOOST_CHECK_EQUAL(statesBwd[0].currentVolume->geometryId(), 3);
0431   BOOST_CHECK_EQUAL(statesBwd[0].currentSurface, nullptr);
0432   BOOST_CHECK_EQUAL(statesBwd[0].currentPortal, nullptr);
0433 }
0434 
0435 // Check how the navigator handles the case
0436 // when the same surface may be reached
0437 // in multiple points
0438 BOOST_AUTO_TEST_CASE(DetectorNavigatorTestsAmbiguity) {
0439   // Construct a cubic detector with a cylindrical surface
0440   auto bounds = std::make_unique<Acts::CuboidVolumeBounds>(10, 10, 10);
0441   auto surface = Acts::Surface::makeShared<Acts::CylinderSurface>(
0442       Acts::Transform3::Identity(),
0443       std::make_shared<Acts::CylinderBounds>(4, 9));
0444   auto volume = Acts::Experimental::DetectorVolumeFactory::construct(
0445       Acts::Experimental::defaultPortalAndSubPortalGenerator(), geoContext,
0446       "volume", Acts::Transform3::Identity(), std::move(bounds), {surface}, {},
0447       Acts::Experimental::tryNoVolumes(),
0448       Acts::Experimental::tryAllPortalsAndSurfaces());
0449 
0450   volume->assignGeometryId(1);
0451   surface->assignGeometryId(2);
0452   int id = 3;
0453   for (auto& port : volume->portalPtrs()) {
0454     port->surface().assignGeometryId(id);
0455     id++;
0456   }
0457 
0458   auto detector = Acts::Experimental::Detector::makeShared(
0459       "detector", {volume}, Acts::Experimental::tryRootVolumes());
0460 
0461   using ActionList = Acts::ActionList<StateRecorder>;
0462   using AbortList = Acts::AbortList<Acts::EndOfWorldReached>;
0463 
0464   Acts::Experimental::DetectorNavigator::Config navCfg;
0465   navCfg.detector = detector.get();
0466 
0467   auto stepper = Acts::StraightLineStepper();
0468 
0469   auto navigator = Acts::Experimental::DetectorNavigator(
0470       navCfg, Acts::getDefaultLogger("DetectorNavigator",
0471                                      Acts::Logging::Level::VERBOSE));
0472 
0473   auto options =
0474       Acts::PropagatorOptions<ActionList, AbortList>(geoContext, mfContext);
0475   options.direction = Acts::Direction::Forward;
0476 
0477   auto propagator = Acts::Propagator<Acts::StraightLineStepper,
0478                                      Acts::Experimental::DetectorNavigator>(
0479       stepper, navigator,
0480       Acts::getDefaultLogger("Propagator", Acts::Logging::Level::VERBOSE));
0481 
0482   // Depending on the direction, the same surface
0483   // may be reached in different points
0484   Acts::Vector4 pos(0, 0, 0, 0);
0485   auto start = Acts::CurvilinearTrackParameters(
0486       pos, 0_degree, 90_degree, 1_e / 1_GeV, std::nullopt,
0487       Acts::ParticleHypothesis::electron());
0488 
0489   // Has to properly handle propagation in the
0490   // forward and backward direction
0491   auto resultFwd = propagator.propagate(start, options).value();
0492   auto statesFwd = resultFwd.get<StateRecorder::result_type>();
0493 
0494   options.direction = Acts::Direction::Backward;
0495 
0496   auto resultBwd = propagator.propagate(start, options).value();
0497   auto statesBwd = resultBwd.get<StateRecorder::result_type>();
0498 
0499   // 3 steps to reach the end of world
0500   // + 1 recording in the post-step
0501   // + 1 recording before the stepping loop
0502   BOOST_CHECK_EQUAL(statesFwd.size(), 5u);
0503   BOOST_CHECK_EQUAL(statesFwd.size(), statesBwd.size());
0504   BOOST_CHECK_EQUAL(statesFwd[0].surfaceCandidates.size(), 2u);
0505   BOOST_CHECK_EQUAL(statesBwd[0].surfaceCandidates.size(), 2u);
0506 
0507   // Action list call before the first step
0508   // Starting in the volume
0509   BOOST_CHECK_EQUAL(statesFwd[0].currentVolume->geometryId(), 1);
0510   BOOST_CHECK_EQUAL(statesFwd[0].currentSurface, nullptr);
0511   BOOST_CHECK_EQUAL(statesFwd[0].currentPortal, nullptr);
0512 
0513   // Step to the cylindrical surface
0514   BOOST_CHECK_EQUAL(statesFwd[1].currentVolume->geometryId(), 1);
0515   BOOST_CHECK_EQUAL(statesFwd[1].currentSurface->geometryId(), 2);
0516   BOOST_CHECK_EQUAL(statesFwd[1].currentPortal, nullptr);
0517   BOOST_CHECK_EQUAL(statesFwd[1].surfaceCandidates.size(), 2u);
0518   CHECK_CLOSE_REL(statesFwd[1].position.x(), 4, 1e-6);
0519 
0520   // Step to the volume|endOfWorld boundary
0521   BOOST_CHECK_EQUAL(statesFwd[2].currentVolume->geometryId(), 1);
0522   BOOST_CHECK_EQUAL(statesFwd[2].currentSurface, nullptr);
0523   BOOST_CHECK_EQUAL(statesFwd[2].currentPortal->surface().geometryId(), 6);
0524 
0525   // Step to the end of world
0526   BOOST_CHECK(navigator.endOfWorldReached(statesFwd[3]));
0527   BOOST_CHECK(navigator.endOfWorldReached(statesBwd[3]));
0528 
0529   // Step to the endOfWorld|volume boundary
0530   BOOST_CHECK_EQUAL(statesBwd[2].currentVolume->geometryId(), 1);
0531   BOOST_CHECK_EQUAL(statesBwd[2].currentSurface, nullptr);
0532   BOOST_CHECK_EQUAL(statesBwd[2].currentPortal->surface().geometryId(), 5);
0533 
0534   // Step to the cylindrical surface
0535   BOOST_CHECK_EQUAL(statesBwd[1].currentVolume->geometryId(), 1);
0536   BOOST_CHECK_EQUAL(statesBwd[1].currentSurface->geometryId(), 2);
0537   BOOST_CHECK_EQUAL(statesBwd[1].currentPortal, nullptr);
0538   CHECK_CLOSE_REL(statesBwd[1].position.x(), -4, 1e-6);
0539 
0540   // Action list call before the first step
0541   // Starting in the volume
0542   BOOST_CHECK_EQUAL(statesBwd[0].currentVolume->geometryId(), 1);
0543   BOOST_CHECK_EQUAL(statesBwd[0].currentSurface, nullptr);
0544   BOOST_CHECK_EQUAL(statesBwd[0].currentPortal, nullptr);
0545 }
0546 
0547 // Check how the navigator handles the case
0548 // when the same surface has multiple valid
0549 // intersections
0550 BOOST_AUTO_TEST_CASE(DetectorNavigatorTestsMultipleIntersection) {
0551   // Construct a cubic detector with a cylindrical surface
0552   auto bounds = std::make_unique<Acts::CuboidVolumeBounds>(10, 10, 10);
0553   auto surface = Acts::Surface::makeShared<Acts::CylinderSurface>(
0554       Acts::Transform3::Identity(),
0555       std::make_shared<Acts::CylinderBounds>(4, 9));
0556   auto volume = Acts::Experimental::DetectorVolumeFactory::construct(
0557       Acts::Experimental::defaultPortalAndSubPortalGenerator(), geoContext,
0558       "volume", Acts::Transform3::Identity(), std::move(bounds), {surface}, {},
0559       Acts::Experimental::tryNoVolumes(),
0560       Acts::Experimental::tryAllPortalsAndSurfaces());
0561 
0562   volume->assignGeometryId(1);
0563   surface->assignGeometryId(2);
0564   int id = 3;
0565   for (auto& port : volume->portalPtrs()) {
0566     port->surface().assignGeometryId(id);
0567     id++;
0568   }
0569 
0570   auto detector = Acts::Experimental::Detector::makeShared(
0571       "detector", {volume}, Acts::Experimental::tryRootVolumes());
0572 
0573   using ActionList = Acts::ActionList<StateRecorder>;
0574   using AbortList = Acts::AbortList<Acts::EndOfWorldReached>;
0575 
0576   Acts::Experimental::DetectorNavigator::Config navCfg;
0577   navCfg.detector = detector.get();
0578 
0579   auto stepper = Acts::StraightLineStepper();
0580 
0581   auto navigator = Acts::Experimental::DetectorNavigator(
0582       navCfg, Acts::getDefaultLogger("DetectorNavigator",
0583                                      Acts::Logging::Level::VERBOSE));
0584 
0585   auto options =
0586       Acts::PropagatorOptions<ActionList, AbortList>(geoContext, mfContext);
0587   options.direction = Acts::Direction::Forward;
0588 
0589   auto propagator = Acts::Propagator<Acts::StraightLineStepper,
0590                                      Acts::Experimental::DetectorNavigator>(
0591       stepper, navigator,
0592       Acts::getDefaultLogger("Propagator", Acts::Logging::Level::VERBOSE));
0593 
0594   // Forward and backward propagation
0595   // should be consistent between each other
0596   // and the cylindrical surface should be
0597   // reached in two points during navigation
0598   Acts::Vector4 posFwd(-5, 0, 0, 0);
0599   auto startFwd = Acts::CurvilinearTrackParameters(
0600       posFwd, 0_degree, 90_degree, 1_e / 1_GeV, std::nullopt,
0601       Acts::ParticleHypothesis::electron());
0602 
0603   auto resultFwd = propagator.propagate(startFwd, options).value();
0604   auto statesFwd = resultFwd.get<StateRecorder::result_type>();
0605 
0606   options.direction = Acts::Direction::Backward;
0607   Acts::Vector4 posBwd(5, 0, 0, 0);
0608   auto startBwd = Acts::CurvilinearTrackParameters(
0609       posBwd, 0_degree, 90_degree, 1_e / 1_GeV, std::nullopt,
0610       Acts::ParticleHypothesis::electron());
0611 
0612   auto resultBwd = propagator.propagate(startBwd, options).value();
0613   auto statesBwd = resultBwd.get<StateRecorder::result_type>();
0614 
0615   // 4 steps to reach the end of world
0616   // + 1 recording in the post-step
0617   // + 1 recording before the stepping loop
0618   BOOST_CHECK_EQUAL(statesFwd.size(), 6u);
0619   BOOST_CHECK_EQUAL(statesFwd.size(), statesBwd.size());
0620   BOOST_CHECK_EQUAL(statesFwd[0].surfaceCandidates.size(), 3u);
0621   BOOST_CHECK_EQUAL(statesBwd[0].surfaceCandidates.size(), 3u);
0622 
0623   // Action list call before the first step
0624   // Starting in the volume
0625   BOOST_CHECK_EQUAL(statesFwd[0].currentVolume->geometryId(), 1);
0626   BOOST_CHECK_EQUAL(statesFwd[0].currentSurface, nullptr);
0627   BOOST_CHECK_EQUAL(statesFwd[0].currentPortal, nullptr);
0628 
0629   // First intersection of the cylindrical surface
0630   BOOST_CHECK_EQUAL(statesFwd[1].currentVolume->geometryId(), 1);
0631   BOOST_CHECK_EQUAL(statesFwd[1].currentSurface->geometryId(), 2);
0632   BOOST_CHECK_EQUAL(statesFwd[1].currentPortal, nullptr);
0633   CHECK_CLOSE_REL(statesFwd[1].position.x(), -4, 1e-6);
0634 
0635   // Second intersection of the cylindrical surface
0636   BOOST_CHECK_EQUAL(statesFwd[2].currentVolume->geometryId(), 1);
0637   BOOST_CHECK_EQUAL(statesFwd[2].currentSurface->geometryId(), 2);
0638   BOOST_CHECK_EQUAL(statesFwd[2].currentPortal, nullptr);
0639   CHECK_CLOSE_REL(statesFwd[2].position.x(), 4, 1e-6);
0640 
0641   // Step to the volume|endOfWorld boundary
0642   BOOST_CHECK_EQUAL(statesFwd[3].currentVolume->geometryId(), 1);
0643   BOOST_CHECK_EQUAL(statesFwd[3].currentSurface, nullptr);
0644   BOOST_CHECK_EQUAL(statesFwd[3].currentPortal->surface().geometryId(), 6);
0645 
0646   // Step to the end of world
0647   BOOST_CHECK(navigator.endOfWorldReached(statesFwd[4]));
0648   BOOST_CHECK(navigator.endOfWorldReached(statesBwd[4]));
0649 
0650   // Step to the endOfWorld|volume boundary
0651   BOOST_CHECK_EQUAL(statesBwd[3].currentVolume->geometryId(), 1);
0652   BOOST_CHECK_EQUAL(statesBwd[3].currentSurface, nullptr);
0653   BOOST_CHECK_EQUAL(statesBwd[3].currentPortal->surface().geometryId(), 5);
0654 
0655   // Second intersection of the cylindrical surface
0656   BOOST_CHECK_EQUAL(statesBwd[2].currentVolume->geometryId(), 1);
0657   BOOST_CHECK_EQUAL(statesBwd[2].currentSurface->geometryId(), 2);
0658   BOOST_CHECK_EQUAL(statesBwd[2].currentPortal, nullptr);
0659   CHECK_CLOSE_REL(statesBwd[2].position.x(), -4, 1e-6);
0660 
0661   // First intersection of the cylindrical surface
0662   BOOST_CHECK_EQUAL(statesBwd[1].currentVolume->geometryId(), 1);
0663   BOOST_CHECK_EQUAL(statesBwd[1].currentSurface->geometryId(), 2);
0664   BOOST_CHECK_EQUAL(statesBwd[1].currentPortal, nullptr);
0665   CHECK_CLOSE_REL(statesBwd[1].position.x(), 4, 1e-6);
0666 
0667   // Action list call before the first step
0668   // Starting in the volume
0669   BOOST_CHECK_EQUAL(statesBwd[0].currentVolume->geometryId(), 1);
0670   BOOST_CHECK_EQUAL(statesBwd[0].currentSurface, nullptr);
0671   BOOST_CHECK_EQUAL(statesBwd[0].currentPortal, nullptr);
0672 }
0673 
0674 BOOST_AUTO_TEST_SUITE_END()