Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:12:44

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