Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-10-16 08:04:15

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