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 
0015 #include <cmath>
0016 #include <stdexcept>
0017 
0018 #include "NavigatorTelescope.hpp"
0019 
0020 using namespace Acts;
0021 using namespace Acts::UnitLiterals;
0022 using namespace ActsTests::NavigatorTelescope;
0023 
0024 namespace ActsTests {
0025 
0026 // Tests of `NavigatorPlainOptions::extendedSurfaces` for Gen1 and Gen3
0027 
0028 BOOST_AUTO_TEST_SUITE(NavigatorExtendedSurface)
0029 
0030 // Without an extension a surface the track misses is not targeted
0031 
0032 BOOST_AUTO_TEST_CASE(BaselineGen1) {
0033   ACTS_LOCAL_LOGGER(getDefaultLogger("Gen1", logLevel));
0034   Telescope telescope = makeTelescopeGen1();
0035   BOOST_REQUIRE(telescope.geometry->geometryVersion() ==
0036                 TrackingGeometry::GeometryVersion::Gen1);
0037   Navigator navigator = makeNavigator(telescope.geometry, logger());
0038 
0039   Navigator::Options options(gctx);
0040   NavigationTarget target = firstTarget(navigator, options, Vector3::Zero());
0041 
0042   BOOST_CHECK_NE(&target.surface(), telescope.top);
0043   BOOST_CHECK_NE(&target.surface(), telescope.bottom);
0044 }
0045 
0046 BOOST_AUTO_TEST_CASE(BaselineGen3) {
0047   ACTS_LOCAL_LOGGER(getDefaultLogger("Gen3", logLevel));
0048   Telescope telescope = makeTelescopeGen3(logger());
0049   BOOST_REQUIRE(telescope.geometry->geometryVersion() ==
0050                 TrackingGeometry::GeometryVersion::Gen3);
0051   Navigator navigator = makeNavigator(telescope.geometry, logger());
0052 
0053   Navigator::Options options(gctx);
0054   NavigationTarget target = firstTarget(navigator, options, Vector3::Zero());
0055 
0056   BOOST_CHECK_NE(&target.surface(), telescope.top);
0057   BOOST_CHECK_NE(&target.surface(), telescope.bottom);
0058 }
0059 
0060 // The bounds check is dropped, so the surface the track misses is targeted
0061 
0062 BOOST_AUTO_TEST_CASE(BoundsExtensionGen1) {
0063   ACTS_LOCAL_LOGGER(getDefaultLogger("Gen1", logLevel));
0064   Telescope telescope = makeTelescopeGen1();
0065   Navigator navigator = makeNavigator(telescope.geometry, logger());
0066 
0067   Navigator::Options options(gctx);
0068   options.registerExtendedSurface(*telescope.top);
0069   NavigationTarget target = firstTarget(navigator, options, Vector3::Zero());
0070 
0071   BOOST_REQUIRE(!target.isNone());
0072   BOOST_CHECK_EQUAL(&target.surface(), telescope.top);
0073 }
0074 
0075 // The policy adds the same surface with `None`. The extension must win.
0076 BOOST_AUTO_TEST_CASE(BoundsExtensionGen3) {
0077   ACTS_LOCAL_LOGGER(getDefaultLogger("Gen3", logLevel));
0078   Telescope telescope = makeTelescopeGen3(logger());
0079   Navigator navigator = makeNavigator(telescope.geometry, logger());
0080 
0081   Navigator::Options options(gctx);
0082   options.registerExtendedSurface(*telescope.top);
0083   NavigationTarget target = firstTarget(navigator, options, Vector3::Zero());
0084 
0085   BOOST_REQUIRE(!target.isNone());
0086   BOOST_CHECK_EQUAL(&target.surface(), telescope.top);
0087   BOOST_CHECK(!target.isPortalTarget());
0088 }
0089 
0090 // The tolerance is per entry, so a caller can ask for a bounds check
0091 
0092 BOOST_AUTO_TEST_CASE(BoundsCheckedGen1) {
0093   ACTS_LOCAL_LOGGER(getDefaultLogger("Gen1", logLevel));
0094   Telescope telescope = makeTelescopeGen1();
0095   Navigator navigator = makeNavigator(telescope.geometry, logger());
0096 
0097   Navigator::Options options(gctx);
0098   options.registerExtendedSurface(*telescope.top, BoundaryTolerance::None());
0099   NavigationTarget target = firstTarget(navigator, options, Vector3::Zero());
0100 
0101   BOOST_CHECK_NE(&target.surface(), telescope.top);
0102 }
0103 
0104 BOOST_AUTO_TEST_CASE(BoundsCheckedGen3) {
0105   ACTS_LOCAL_LOGGER(getDefaultLogger("Gen3", logLevel));
0106   Telescope telescope = makeTelescopeGen3(logger());
0107   Navigator navigator = makeNavigator(telescope.geometry, logger());
0108 
0109   Navigator::Options options(gctx);
0110   options.registerExtendedSurface(*telescope.top, BoundaryTolerance::None());
0111   NavigationTarget target = firstTarget(navigator, options, Vector3::Zero());
0112 
0113   BOOST_CHECK_NE(&target.surface(), telescope.top);
0114 }
0115 
0116 // The geometry has to hold the surface
0117 
0118 BOOST_AUTO_TEST_CASE(SurfaceOutsideTheGeometryThrows) {
0119   ACTS_LOCAL_LOGGER(getDefaultLogger("Gen3", logLevel));
0120   Telescope telescope = makeTelescopeGen3(logger());
0121   Navigator navigator = makeNavigator(telescope.geometry, logger());
0122 
0123   auto outOfGeometry = makeOutOfGeometrySurface({0, 0, 0.25_m});
0124   BOOST_REQUIRE(outOfGeometry->geometryId() == GeometryIdentifier{});
0125 
0126   Navigator::Options options(gctx);
0127   options.registerExtendedSurface(*outOfGeometry);
0128   Navigator::State state = navigator.makeState(options);
0129   Vector3 position = Vector3::Zero();
0130   const Vector3 direction = Vector3::UnitZ();
0131   BOOST_CHECK_THROW(static_cast<void>(navigator.initialize(
0132                         state, {.position = position, .direction = direction})),
0133                     std::invalid_argument);
0134 }
0135 
0136 // The track crosses the plane of the top surface at y = 1.2 m, outside the
0137 // volume, and leaves through the boundary at y = 0.95 m first.
0138 
0139 namespace {
0140 const Vector3 pokeStart{0, 0.9_m, 0};
0141 const Vector3 pokeDir = Vector3{0, 0.6, 1}.normalized();
0142 }  // namespace
0143 
0144 // Gen1 drops the crossing outside the volume explicitly.
0145 BOOST_AUTO_TEST_CASE(CrossingOutsideTheVolumeIsDroppedGen1) {
0146   ACTS_LOCAL_LOGGER(getDefaultLogger("Gen1", logLevel));
0147   Telescope telescope = makeTelescopeGen1();
0148   Navigator navigator = makeNavigator(telescope.geometry, logger());
0149 
0150   Navigator::Options options(gctx);
0151   options.registerExtendedSurface(*telescope.top);
0152   Navigator::State state = navigator.makeState(options);
0153   Vector3 position = pokeStart;
0154   BOOST_REQUIRE(
0155       navigator.initialize(state, {.position = position, .direction = pokeDir})
0156           .ok());
0157 
0158   NavigationTarget target = navigator.nextTarget(state, position, pokeDir);
0159   BOOST_REQUIRE(!target.isNone());
0160   BOOST_CHECK(target.surface().geometryId().boundary() != 0);
0161 }
0162 
0163 // Gen3 sorts the portal first.
0164 BOOST_AUTO_TEST_CASE(CrossingOutsideTheVolumeIsDroppedGen3) {
0165   ACTS_LOCAL_LOGGER(getDefaultLogger("Gen3", logLevel));
0166   Telescope telescope = makeTelescopeGen3(logger());
0167   Navigator navigator = makeNavigator(telescope.geometry, logger());
0168 
0169   Navigator::Options options(gctx);
0170   options.registerExtendedSurface(*telescope.top);
0171   Navigator::State state = navigator.makeState(options);
0172   Vector3 position = pokeStart;
0173   BOOST_REQUIRE(
0174       navigator.initialize(state, {.position = position, .direction = pokeDir})
0175           .ok());
0176 
0177   NavigationTarget target = navigator.nextTarget(state, position, pokeDir);
0178   BOOST_REQUIRE(!target.isNone());
0179   BOOST_CHECK(target.isPortalTarget());
0180 }
0181 
0182 // The extension applies in the volume (Gen3) or on the layer (Gen1)
0183 
0184 BOOST_AUTO_TEST_CASE(ScopedToItsVolumeGen3) {
0185   auto logger = getDefaultLogger("Gen3", logLevel);
0186 
0187   Blueprint::Config cfg;
0188   cfg.envelope = ExtentEnvelope{{
0189       .x = {20_mm, 20_mm},
0190       .y = {20_mm, 20_mm},
0191       .z = {20_mm, 20_mm},
0192   }};
0193   Blueprint root{cfg};
0194 
0195   // Two volumes along z. The far one holds a small surface off the axis.
0196   const Surface* extended = nullptr;
0197   root.addCuboidContainer("Stack", AxisDirection::AxisZ, [&](auto& stack) {
0198     stack.addChild(
0199         std::make_shared<StaticBlueprintNode>(std::make_unique<TrackingVolume>(
0200             Transform3{Translation3{Vector3{0, 0, -0.5_m}}},
0201             std::make_shared<CuboidVolumeBounds>(0.5_m, 0.5_m, 0.5_m),
0202             "near")));
0203 
0204     auto farVolume = std::make_unique<TrackingVolume>(
0205         Transform3{Translation3{Vector3{0, 0, 0.5_m}}},
0206         std::make_shared<CuboidVolumeBounds>(0.5_m, 0.5_m, 0.5_m), "far");
0207     auto surface = Surface::makeShared<PlaneSurface>(
0208         Transform3{Translation3{Vector3{0, 0.4_m, 0.5_m}}},
0209         std::make_shared<const RectangleBounds>(0.05_m, 0.05_m));
0210     extended = surface.get();
0211     farVolume->addSurface(std::move(surface));
0212     stack.addChild(std::make_shared<StaticBlueprintNode>(std::move(farVolume)));
0213   });
0214 
0215   auto geometry = root.construct({}, gctx, *logger);
0216   BOOST_REQUIRE(extended != nullptr);
0217   Navigator navigator = makeNavigator(
0218       std::shared_ptr<const TrackingGeometry>(std::move(geometry)), *logger);
0219 
0220   Navigator::Options options(gctx);
0221   options.registerExtendedSurface(*extended);
0222   Navigator::State state = navigator.makeState(options);
0223 
0224   Vector3 position{0, 0, -0.9_m};
0225   const Vector3 dir = Vector3::UnitZ();
0226   BOOST_REQUIRE(
0227       navigator.initialize(state, {.position = position, .direction = dir})
0228           .ok());
0229 
0230   // The navigator resolved the volume that holds the surface
0231   BOOST_REQUIRE_EQUAL(state.extendedSurfaces.size(), 1u);
0232   BOOST_REQUIRE(state.extendedSurfaces.front().volume != nullptr);
0233   BOOST_CHECK_EQUAL(state.extendedSurfaces.front().volume->volumeName(), "far");
0234 
0235   // In the near volume the portal is targeted, not the extended surface
0236   NavigationTarget target = navigator.nextTarget(state, position, dir);
0237   BOOST_REQUIRE(!target.isNone());
0238   BOOST_CHECK(target.isPortalTarget());
0239   stepOnto(position, dir, target.surface());
0240   navigator.handleSurfaceReached(state, position, dir, target.surface());
0241   BOOST_REQUIRE_EQUAL(state.currentVolume->volumeName(), "far");
0242 
0243   // In its own volume the relaxed bounds check applies and it is targeted
0244   target = navigator.nextTarget(state, position, dir);
0245   BOOST_REQUIRE(!target.isNone());
0246   BOOST_CHECK_EQUAL(&target.surface(), extended);
0247 }
0248 
0249 BOOST_AUTO_TEST_CASE(ScopedToItsLayerGen1) {
0250   ACTS_LOCAL_LOGGER(getDefaultLogger("Gen1", logLevel));
0251 
0252   CuboidVolumeBuilder::Config conf;
0253   conf.position = {0., 0., 0.};
0254   conf.length = {2_m, 2_m, 2_m};
0255 
0256   // One layer on the axis, a second one further out, off the axis
0257   CuboidVolumeBuilder::SurfaceConfig onAxis;
0258   onAxis.position = {0, 0, -0.3_m};
0259   onAxis.rBounds = std::make_shared<RectangleBounds>(0.1_m, 0.1_m);
0260   CuboidVolumeBuilder::LayerConfig nearLayer;
0261   nearLayer.binningDimension = AxisDirection::AxisZ;
0262   nearLayer.surfaceCfg.push_back(onAxis);
0263 
0264   CuboidVolumeBuilder::SurfaceConfig offAxis;
0265   offAxis.position = {0, 0.4_m, 0.3_m};
0266   offAxis.rBounds = std::make_shared<RectangleBounds>(0.05_m, 0.05_m);
0267   CuboidVolumeBuilder::LayerConfig farLayer;
0268   farLayer.binningDimension = AxisDirection::AxisZ;
0269   farLayer.surfaceCfg.push_back(offAxis);
0270 
0271   CuboidVolumeBuilder::VolumeConfig volume;
0272   volume.binningDimension = AxisDirection::AxisZ;
0273   volume.position = {0, 0, 0};
0274   volume.length = {1.9_m, 1.9_m, 1.9_m};
0275   volume.name = "telescope";
0276   volume.layerCfg.push_back(nearLayer);
0277   volume.layerCfg.push_back(farLayer);
0278   conf.volumeCfg.push_back(volume);
0279 
0280   CuboidVolumeBuilder cvb(conf);
0281   TrackingGeometryBuilder::Config tgbCfg;
0282   tgbCfg.trackingVolumeBuilders.push_back(
0283       [=](const auto& context, const auto& inner, const auto&) {
0284         return cvb.trackingVolume(context, inner, nullptr);
0285       });
0286   auto geometry = TrackingGeometryBuilder(tgbCfg).trackingGeometry(gctx);
0287 
0288   std::vector<const Surface*> surfaces;
0289   geometry->visitSurfaces([&](const Surface* sf) { surfaces.push_back(sf); });
0290   BOOST_REQUIRE_EQUAL(surfaces.size(), 2u);
0291   const Surface* extended = surfaces.at(1);
0292   BOOST_REQUIRE_EQUAL(extended->center(gctx).y(), 0.4_m);
0293 
0294   Navigator navigator = makeNavigator(
0295       std::shared_ptr<const TrackingGeometry>(std::move(geometry)), logger());
0296 
0297   Navigator::Options options(gctx);
0298   options.registerExtendedSurface(*extended);
0299   std::vector<const Surface*> reached =
0300       walk(navigator, options, Vector3{0, 0, -0.8_m}, Vector3::UnitZ());
0301 
0302   // The extension applies only while its layer is the current one, so the near
0303   // layer comes first
0304   BOOST_REQUIRE(!reached.empty());
0305   BOOST_CHECK_NE(reached.front(), extended);
0306   BOOST_CHECK_LT(
0307       std::abs(reached.front()->center(gctx).z() - onAxis.position.z()), 1_um);
0308 }
0309 
0310 BOOST_AUTO_TEST_SUITE_END()
0311 
0312 }  // namespace ActsTests