File indexing completed on 2026-11-09 09:28:43
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include <boost/test/unit_test.hpp>
0010
0011 #include "Acts/Geometry/Blueprint.hpp"
0012 #include "Acts/Geometry/CuboidVolumeBounds.hpp"
0013 #include "Acts/Geometry/CylinderVolumeBounds.hpp"
0014 #include "Acts/Geometry/LinearVolumeLookup.hpp"
0015 #include "Acts/Geometry/RzGridVolumeLookup.hpp"
0016 #include "Acts/Geometry/StaticBlueprintNode.hpp"
0017 #include "Acts/Geometry/TrackingGeometry.hpp"
0018 #include "Acts/Geometry/TrackingVolume.hpp"
0019 #include "Acts/Geometry/VolumePlacementBase.hpp"
0020
0021 #include <limits>
0022 #include <random>
0023 #include <sstream>
0024 #include <type_traits>
0025
0026 using namespace Acts;
0027
0028 static_assert(!std::is_copy_constructible_v<TrackingGeometry>);
0029 static_assert(!std::is_copy_assignable_v<TrackingGeometry>);
0030 namespace {
0031 auto gctx = GeometryContext::dangerouslyDefaultConstruct();
0032 std::shared_ptr<TrackingVolume> makeWorld() {
0033 auto world = std::make_shared<TrackingVolume>(
0034 Transform3::Identity(),
0035 std::make_shared<CylinderVolumeBounds>(0., 100., 100.), "world");
0036
0037 for (int i = 0; i < 16; ++i) {
0038 auto child = std::make_unique<TrackingVolume>(
0039 Transform3::Identity(),
0040 std::make_shared<CylinderVolumeBounds>(i * 4., i * 4. + 6., 20. + i),
0041 "shell");
0042 if (i == 2) {
0043 child->addVolume(std::make_unique<TrackingVolume>(
0044 Transform3::Identity(),
0045 std::make_shared<CylinderVolumeBounds>(9., 10., 5.), "nested"));
0046 }
0047 world->addVolume(std::move(child));
0048 }
0049 unsigned id = 0;
0050 world->apply([&](TrackingVolume& v) {
0051 v.assignGeometryId(GeometryIdentifier{}.withVolume(++id));
0052 });
0053 return world;
0054 }
0055 class MovingPlacement : public VolumePlacementBase {
0056 public:
0057 Transform3 transform = Transform3::Identity();
0058 Transform3 inverse = Transform3::Identity();
0059 const Transform3& localToGlobalTransform(
0060 const GeometryContext& ) const override {
0061 return transform;
0062 }
0063 const Transform3& portalLocalToGlobal(
0064 const GeometryContext& ,
0065 std::size_t ) const override {
0066 return transform;
0067 }
0068 const Transform3& globalToLocalTransform(
0069 const GeometryContext& ) const override {
0070 return inverse;
0071 }
0072 };
0073 }
0074
0075 BOOST_AUTO_TEST_CASE(RzLookupMatchesLinear) {
0076 auto world = makeWorld();
0077 const LinearVolumeLookup linear(*world, {});
0078 TrackingGeometry geometry(world, nullptr, {}, getDummyLogger(), false);
0079 std::mt19937 rng(42);
0080 std::uniform_real_distribution<double> uniform(-110., 110.);
0081 {
0082 RzGridVolumeLookup::Config cfg;
0083 geometry.highestTrackingVolume()->setVolumeLookup(
0084 std::make_unique<RzGridVolumeLookup>(gctx, *world, cfg));
0085 for (double tolerance : {-1e-4, 0., 1e-6, cfg.maxTolerance, 0.5}) {
0086 for (int i = 0; i < 3000; ++i) {
0087 Vector3 p(uniform(rng), uniform(rng), uniform(rng));
0088 BOOST_CHECK(*geometry.resolveLowestTrackingVolume(gctx, p, std::nullopt,
0089 nullptr, tolerance) ==
0090 linear.findLowest(gctx, p, tolerance));
0091 }
0092 for (int i = 0; i < 16; ++i) {
0093 for (double delta : {-2e-3, -1e-3, -1e-6, 0., 1e-6, 1e-3, 2e-3}) {
0094 for (Vector3 p : {Vector3(i * 4. + delta, 0., 0.),
0095 Vector3(i * 4. + 6. + delta, 0., 0.),
0096 Vector3(i * 4. + 2., 0., 20. + i + delta)}) {
0097 BOOST_CHECK(*geometry.resolveLowestTrackingVolume(
0098 gctx, p, std::nullopt, nullptr, tolerance) ==
0099 linear.findLowest(gctx, p, tolerance));
0100 }
0101 }
0102 }
0103 }
0104
0105 BOOST_CHECK(*geometry.resolveLowestTrackingVolume(gctx, {9.5, 0., 0.}) ==
0106 &world->volumes().at(1));
0107 geometry.highestTrackingVolume()->setVolumeLookup(nullptr);
0108 BOOST_CHECK(*geometry.resolveLowestTrackingVolume(gctx, {9.5, 0., 0.}) ==
0109 &world->volumes().at(1));
0110 BOOST_CHECK(geometry.highestTrackingVolume()->volumeLookup() == nullptr);
0111 }
0112 }
0113
0114 BOOST_AUTO_TEST_CASE(RzLookupFallbacks) {
0115 {
0116 auto world = makeWorld();
0117 RzGridVolumeLookup::Config cfg;
0118 {
0119 cfg.maxGridCells = 1;
0120 }
0121 RzGridVolumeLookup limited(gctx, *world, cfg);
0122 BOOST_CHECK(limited.findLowest(gctx, {40., 0., 0.}, 0.) ==
0123 world->lowestTrackingVolume(gctx, {40., 0., 0.}, 0.));
0124 world->addVolume(std::make_unique<TrackingVolume>(
0125 Transform3::Identity(),
0126 std::make_shared<CuboidVolumeBounds>(80., 80., 80.), "box"));
0127 RzGridVolumeLookup mixed(gctx, *world, cfg);
0128 BOOST_CHECK(mixed.findLowest(gctx, {70., 0., 0.}, 0.) ==
0129 world->lowestTrackingVolume(gctx, {70., 0., 0.}, 0.));
0130
0131 MovingPlacement placement;
0132 auto movingWorld = std::make_shared<TrackingVolume>(
0133 Transform3::Identity(),
0134 std::make_shared<CylinderVolumeBounds>(0., 100., 100.));
0135 movingWorld->addVolume(std::make_unique<TrackingVolume>(
0136 placement, std::make_shared<CylinderVolumeBounds>(0., 5., 5.)));
0137 RzGridVolumeLookup moving(gctx, *movingWorld, cfg);
0138 placement.transform.translation().x() = 50.;
0139 placement.inverse = placement.transform.inverse();
0140 BOOST_CHECK(moving.findLowest(gctx, {50., 0., 0.}, 0.) ==
0141 &movingWorld->volumes().at(0));
0142 }
0143 }
0144
0145 BOOST_AUTO_TEST_CASE(BlueprintSelectsVolumeLookup) {
0146 for (int type : {0, 1}) {
0147 Blueprint blueprint{Blueprint::Config{}};
0148 blueprint.addChild(
0149 std::make_shared<StaticBlueprintNode>(std::make_unique<TrackingVolume>(
0150 Transform3::Identity(),
0151 std::make_shared<CylinderVolumeBounds>(0., 10., 10.), "cylinder")));
0152 BlueprintOptions options;
0153 blueprint.setVolumeLookupFactory(
0154 [type](const GeometryContext& context, const TrackingVolume& volume,
0155 const Logger& logger) -> std::unique_ptr<IVolumeLookup> {
0156 if (type == 0) {
0157 return std::make_unique<LinearVolumeLookup>(
0158 volume, LinearVolumeLookup::Config{}, logger);
0159 }
0160 return std::make_unique<RzGridVolumeLookup>(
0161 context, volume, RzGridVolumeLookup::Config{}, logger);
0162 });
0163 auto geometry = blueprint.construct(options, gctx);
0164 geometry->highestTrackingVolume()->apply([&](TrackingVolume& volume) {
0165 if (&volume != geometry->highestTrackingVolume()) {
0166 BOOST_CHECK(volume.volumeLookup() == nullptr);
0167 return;
0168 }
0169 BOOST_REQUIRE(volume.volumeLookup() != nullptr);
0170 BOOST_CHECK((dynamic_cast<const LinearVolumeLookup*>(
0171 volume.volumeLookup()) != nullptr) == (type == 0));
0172 BOOST_CHECK((dynamic_cast<const RzGridVolumeLookup*>(
0173 volume.volumeLookup()) != nullptr) == (type == 1));
0174 });
0175 BOOST_CHECK(*geometry->resolveLowestTrackingVolume(gctx, {5., 0., 0.}) !=
0176 nullptr);
0177 }
0178 }
0179
0180 BOOST_AUTO_TEST_CASE(InvalidVolumeLookupConfig) {
0181 auto world = makeWorld();
0182 RzGridVolumeLookup::Config cfg;
0183 cfg.maxTolerance = -1;
0184 BOOST_CHECK_THROW(RzGridVolumeLookup(gctx, *world, cfg),
0185 std::invalid_argument);
0186 cfg.maxTolerance = 0;
0187 cfg.maxGridCells = 0;
0188 BOOST_CHECK_THROW(RzGridVolumeLookup(gctx, *world, cfg),
0189 std::invalid_argument);
0190 }
0191
0192 BOOST_AUTO_TEST_CASE(InvalidGridConfig) {
0193 auto world = makeWorld();
0194 RzGridVolumeLookup::Config cfg;
0195 cfg.maxGridCells = 0;
0196 BOOST_CHECK_THROW(RzGridVolumeLookup(gctx, *world, cfg),
0197 std::invalid_argument);
0198 }
0199
0200 BOOST_AUTO_TEST_CASE(LinearLookupWarnings) {
0201
0202 #ifdef ACTS_ENABLE_LOG_FAILURE_THRESHOLD
0203 Logging::ScopedFailureThreshold threshold{Logging::ERROR};
0204 #endif
0205 auto world = makeWorld();
0206 std::ostringstream output;
0207 auto logger = getDefaultLogger("LookupTest", Logging::WARNING, &output);
0208 LinearVolumeLookup atThreshold(*world, LinearVolumeLookup::Config{16},
0209 *logger);
0210 BOOST_CHECK(output.str().empty());
0211 LinearVolumeLookup aboveThreshold(*world, LinearVolumeLookup::Config{15},
0212 *logger);
0213 BOOST_CHECK(output.str().find("scans up to 16 siblings") !=
0214 std::string::npos);
0215 output.str("");
0216 LinearVolumeLookup zeroThreshold(*world, LinearVolumeLookup::Config{0},
0217 *logger);
0218 BOOST_CHECK(output.str().find("scans up to 16 siblings") !=
0219 std::string::npos);
0220 output.str("");
0221 LinearVolumeLookup suppressed(
0222 *world, LinearVolumeLookup::Config{std::nullopt}, *logger);
0223 BOOST_CHECK(output.str().empty());
0224 for (int i = 16; i < 65; ++i) {
0225 world->addVolume(std::make_unique<TrackingVolume>(
0226 Transform3::Identity(),
0227 std::make_shared<CylinderVolumeBounds>(0., 5., 5.), "extra"));
0228 }
0229 unsigned id = 0;
0230 world->apply([&](TrackingVolume& v) {
0231 v.assignGeometryId(GeometryIdentifier{}.withVolume(++id));
0232 });
0233
0234 auto lookup = std::make_unique<RzGridVolumeLookup>(
0235 gctx, *world, RzGridVolumeLookup::Config{}, *logger);
0236 world->setVolumeLookup(std::move(lookup));
0237 TrackingGeometry accelerated(world, nullptr, {}, *logger, false);
0238 BOOST_CHECK(output.str().empty());
0239 world->setVolumeLookup(nullptr);
0240 BOOST_CHECK(output.str().empty());
0241 BOOST_CHECK(world->volumeLookup() == nullptr);
0242 world->setVolumeLookup(std::make_unique<LinearVolumeLookup>(
0243 *world, LinearVolumeLookup::Config{}, *logger));
0244 BOOST_CHECK(output.str().find("scans up to 65 siblings") !=
0245 std::string::npos);
0246 }
0247
0248 BOOST_AUTO_TEST_CASE(AcceleratorFallbackWarnings) {
0249
0250 #ifdef ACTS_ENABLE_LOG_FAILURE_THRESHOLD
0251 Logging::ScopedFailureThreshold threshold{Logging::ERROR};
0252 #endif
0253 {
0254 std::ostringstream output;
0255 auto logger = getDefaultLogger("LookupTest", Logging::WARNING, &output);
0256 auto world = std::make_shared<TrackingVolume>(
0257 Transform3::Identity(),
0258 std::make_shared<CylinderVolumeBounds>(0., 100., 100.));
0259 auto container = std::make_unique<TrackingVolume>(
0260 Transform3::Identity(),
0261 std::make_shared<CylinderVolumeBounds>(0., 50., 50.));
0262 container->addVolume(std::make_unique<TrackingVolume>(
0263 Transform3::Identity(),
0264 std::make_shared<CuboidVolumeBounds>(2., 2., 2.)));
0265 world->addVolume(std::move(container));
0266 world->addVolume(std::make_unique<TrackingVolume>(
0267 Transform3::Identity(),
0268 std::make_shared<CylinderVolumeBounds>(50., 90., 50.)));
0269 RzGridVolumeLookup::Config cfg;
0270 cfg.fallbackWarningFraction = 0.3;
0271 RzGridVolumeLookup warns(gctx, *world, cfg, *logger);
0272 BOOST_CHECK(output.str().find("1/3 volumes in the indexed scope") !=
0273 std::string::npos);
0274 BOOST_CHECK(output.str().find("unsupported shape/orientation=1") !=
0275 std::string::npos);
0276 output.str("");
0277 cfg.fallbackWarningFraction = 1. / 3.;
0278 RzGridVolumeLookup equal(gctx, *world, cfg, *logger);
0279 BOOST_CHECK(output.str().empty());
0280 cfg.fallbackWarningFraction = 1.;
0281 RzGridVolumeLookup suppressed(gctx, world->volumes().at(0), cfg, *logger);
0282 BOOST_CHECK(output.str().empty());
0283
0284 MovingPlacement placement;
0285 auto movingWorld = std::make_shared<TrackingVolume>(
0286 Transform3::Identity(),
0287 std::make_shared<CylinderVolumeBounds>(0., 100., 100.));
0288 movingWorld->addVolume(std::make_unique<TrackingVolume>(
0289 placement, std::make_shared<CylinderVolumeBounds>(0., 10., 10.)));
0290 cfg.fallbackWarningFraction = 0.;
0291 RzGridVolumeLookup moving(gctx, *movingWorld, cfg, *logger);
0292 BOOST_CHECK(output.str().find("1/1 volumes in the indexed scope") !=
0293 std::string::npos);
0294 BOOST_CHECK(output.str().find("alignable=1") != std::string::npos);
0295 output.str("");
0296 {
0297 cfg.maxGridCells = 1;
0298 auto largeWorld = makeWorld();
0299 RzGridVolumeLookup limited(gctx, *largeWorld, cfg, *logger);
0300 BOOST_CHECK(output.str().find("grid cell limit=1") != std::string::npos);
0301 }
0302 for (double invalid :
0303 {-0.1, 1.1, std::numeric_limits<double>::quiet_NaN()}) {
0304 cfg.fallbackWarningFraction = invalid;
0305 BOOST_CHECK_THROW(RzGridVolumeLookup(gctx, *world, cfg, *logger),
0306 std::invalid_argument);
0307 }
0308 }
0309 }
0310
0311 BOOST_AUTO_TEST_CASE(BlueprintLookupWarningSettings) {
0312
0313 #ifdef ACTS_ENABLE_LOG_FAILURE_THRESHOLD
0314 Logging::ScopedFailureThreshold threshold{Logging::ERROR};
0315 #endif
0316 std::ostringstream output;
0317 auto logger = getDefaultLogger("BlueprintTest", Logging::WARNING, &output);
0318 for (int type : {0, 1}) {
0319 Blueprint blueprint{Blueprint::Config{}};
0320 blueprint.addChild(
0321 std::make_shared<StaticBlueprintNode>(std::make_unique<TrackingVolume>(
0322 Transform3::Identity(),
0323 std::make_shared<CuboidVolumeBounds>(10., 10., 10.), "cube")));
0324 BlueprintOptions options;
0325 blueprint.setVolumeLookupFactory(
0326 [type](const GeometryContext& context, const TrackingVolume& volume,
0327 const Logger& factoryLogger) -> std::unique_ptr<IVolumeLookup> {
0328 if (type == 0) {
0329 return std::make_unique<LinearVolumeLookup>(
0330 volume, LinearVolumeLookup::Config{std::nullopt},
0331 factoryLogger);
0332 }
0333 RzGridVolumeLookup::Config config;
0334 config.fallbackWarningFraction = 1;
0335 return std::make_unique<RzGridVolumeLookup>(context, volume, config,
0336 factoryLogger);
0337 });
0338 auto geometry = blueprint.construct(options, gctx, *logger);
0339 BOOST_CHECK(output.str().find("volume lookup") == std::string::npos);
0340 if (auto* linear = dynamic_cast<const LinearVolumeLookup*>(
0341 geometry->highestTrackingVolume()->volumeLookup())) {
0342 BOOST_CHECK(!linear->config().warningThreshold.has_value());
0343 }
0344 if (auto* grid = dynamic_cast<const RzGridVolumeLookup*>(
0345 geometry->highestTrackingVolume()->volumeLookup())) {
0346 BOOST_CHECK_EQUAL(grid->config().fallbackWarningFraction, 1);
0347 }
0348 }
0349 }
0350
0351 BOOST_AUTO_TEST_CASE(RecursiveLookupUsesEachVolumesPolicy) {
0352 class CountingLookup final : public IVolumeLookup {
0353 public:
0354 CountingLookup(const TrackingVolume& root, unsigned& calls)
0355 : m_calls(calls), m_linear(root, {}) {}
0356 const TrackingVolume* findLowest(const GeometryContext& context,
0357 const Vector3& position,
0358 double tolerance) const override {
0359 ++m_calls;
0360 return m_linear.findLowest(context, position, tolerance);
0361 }
0362
0363 private:
0364 unsigned& m_calls;
0365 LinearVolumeLookup m_linear;
0366 };
0367 {
0368 auto world = makeWorld();
0369 unsigned calls = 0;
0370 auto& child = world->volumes().at(1);
0371 child.setVolumeLookup(std::make_unique<CountingLookup>(child, calls));
0372 TrackingGeometry geometry(world, nullptr, {}, getDummyLogger(), false);
0373 BOOST_CHECK(*geometry.resolveLowestTrackingVolume(gctx, {9.5, 0., 0.}) ==
0374 &child);
0375 BOOST_CHECK_EQUAL(calls, 1);
0376 BOOST_CHECK(world->lowestTrackingVolume(gctx, {9.5, 0., 0.}) == &child);
0377 BOOST_CHECK_EQUAL(calls, 2);
0378 world->setVolumeLookup(std::make_unique<RzGridVolumeLookup>(
0379 gctx, *world, RzGridVolumeLookup::Config{}));
0380 BOOST_CHECK(world->lowestTrackingVolume(gctx, {9.5, 0., 0.}) == &child);
0381 BOOST_CHECK_EQUAL(calls, 3);
0382
0383
0384
0385 world->addVolume(std::make_unique<TrackingVolume>(
0386 Transform3::Identity(),
0387 std::make_shared<CuboidVolumeBounds>(1., 1., 1.)));
0388 world->setVolumeLookup(std::make_unique<RzGridVolumeLookup>(
0389 gctx, *world, RzGridVolumeLookup::Config{}, getDummyLogger()));
0390 BOOST_CHECK(*geometry.resolveLowestTrackingVolume(gctx, {9.5, 0., 0.}) ==
0391 &child);
0392 BOOST_CHECK_EQUAL(calls, 4);
0393 world->setVolumeLookup(nullptr);
0394 BOOST_CHECK(world->lowestTrackingVolume(gctx, {9.5, 0., 0.}) == &child);
0395 BOOST_CHECK_EQUAL(calls, 5);
0396 child.setVolumeLookup(nullptr);
0397 BOOST_CHECK(child.volumeLookup() == nullptr);
0398 }
0399 }
0400
0401 BOOST_AUTO_TEST_CASE(LookupBindsToFinalVolumeAddress) {
0402 auto world = makeWorld();
0403 TrackingVolume moved(std::move(*world));
0404 moved.setVolumeLookup(std::make_unique<RzGridVolumeLookup>(
0405 gctx, moved, RzGridVolumeLookup::Config{}));
0406 BOOST_CHECK(moved.lowestTrackingVolume(gctx, {90., 0., 0.}) == &moved);
0407 BOOST_CHECK(moved.lowestTrackingVolume(gctx, {9.5, 0., 0.}) ==
0408 &moved.volumes().at(1));
0409 moved.setVolumeLookup(std::make_unique<LinearVolumeLookup>(
0410 moved, LinearVolumeLookup::Config{}));
0411 BOOST_CHECK(moved.lowestTrackingVolume(gctx, {90., 0., 0.}) == &moved);
0412 BOOST_CHECK(moved.lowestTrackingVolume(gctx, {9.5, 0., 0.}) ==
0413 &moved.volumes().at(1));
0414 }
0415
0416 BOOST_AUTO_TEST_CASE(BlueprintLocalLookupFactories) {
0417 Blueprint::Config config;
0418 config.envelope[AxisDirection::AxisR] = {1., 1.};
0419 config.envelope[AxisDirection::AxisZ] = {1., 1.};
0420 Blueprint blueprint{config};
0421 auto region =
0422 std::make_shared<StaticBlueprintNode>(std::make_unique<TrackingVolume>(
0423 Transform3::Identity(),
0424 std::make_shared<CylinderVolumeBounds>(0., 20., 20.), "region"));
0425 region->addChild(
0426 std::make_shared<StaticBlueprintNode>(std::make_unique<TrackingVolume>(
0427 Transform3::Identity(),
0428 std::make_shared<CylinderVolumeBounds>(0., 10., 10.), "child")));
0429 blueprint.addChild(region);
0430 std::vector<std::string> calls;
0431 auto logger = getDefaultLogger("FactoryTest", Logging::WARNING);
0432 region->setVolumeLookupFactory([&](const GeometryContext& context,
0433 const TrackingVolume& volume,
0434 const Logger& passedLogger) {
0435 BOOST_CHECK(&context == &gctx);
0436 BOOST_CHECK(&passedLogger == logger.get());
0437 BOOST_CHECK_EQUAL(volume.volumes().size(), 1);
0438 BOOST_CHECK(volume.volumes().at(0).volumeLookup() == nullptr);
0439 calls.push_back(volume.volumeName());
0440 return std::make_unique<LinearVolumeLookup>(
0441 volume, LinearVolumeLookup::Config{}, passedLogger);
0442 });
0443 blueprint.setVolumeLookupFactory([&](const GeometryContext& context,
0444 const TrackingVolume& volume,
0445 const Logger& passedLogger) {
0446 BOOST_CHECK(&context == &gctx);
0447 BOOST_CHECK(&passedLogger == logger.get());
0448 BOOST_CHECK(dynamic_cast<const LinearVolumeLookup*>(
0449 volume.volumes().at(0).volumeLookup()) != nullptr);
0450 calls.push_back(volume.volumeName());
0451 return std::make_unique<RzGridVolumeLookup>(
0452 context, volume, RzGridVolumeLookup::Config{}, passedLogger);
0453 });
0454 auto geometry = blueprint.construct(BlueprintOptions{}, gctx, *logger);
0455 BOOST_REQUIRE_EQUAL(calls.size(), 2);
0456 BOOST_CHECK_EQUAL(calls[0], "region");
0457 BOOST_CHECK_EQUAL(calls[1], geometry->highestTrackingVolume()->volumeName());
0458 const auto* result =
0459 *geometry->resolveLowestTrackingVolume(gctx, {5., 0., 0.});
0460 BOOST_REQUIRE(result != nullptr);
0461 BOOST_CHECK_EQUAL(result->volumeName(), "child");
0462 }
0463
0464 BOOST_AUTO_TEST_CASE(BlueprintRejectsNullLookupFactoryResult) {
0465 for (bool onStatic : {false, true}) {
0466 Blueprint blueprint{Blueprint::Config{}};
0467 auto child =
0468 std::make_shared<StaticBlueprintNode>(std::make_unique<TrackingVolume>(
0469 Transform3::Identity(),
0470 std::make_shared<CylinderVolumeBounds>(0., 10., 10.), "child"));
0471 blueprint.addChild(child);
0472 VolumeLookupFactory factory = [](const GeometryContext&,
0473 const TrackingVolume&, const Logger&) {
0474 return std::unique_ptr<IVolumeLookup>{};
0475 };
0476 if (onStatic) {
0477 child->setVolumeLookupFactory(factory);
0478 } else {
0479 blueprint.setVolumeLookupFactory(factory);
0480 }
0481 BOOST_CHECK_THROW(blueprint.construct(BlueprintOptions{}, gctx),
0482 std::invalid_argument);
0483 }
0484 }
0485
0486 BOOST_AUTO_TEST_CASE(BlueprintDefaultLookupIsImplicit) {
0487 Blueprint blueprint{Blueprint::Config{}};
0488 blueprint.addChild(
0489 std::make_shared<StaticBlueprintNode>(std::make_unique<TrackingVolume>(
0490 Transform3::Identity(),
0491 std::make_shared<CylinderVolumeBounds>(0., 10., 10.), "child")));
0492 auto geometry = blueprint.construct(BlueprintOptions{}, gctx);
0493 geometry->highestTrackingVolume()->apply([](TrackingVolume& volume) {
0494 BOOST_CHECK(volume.volumeLookup() == nullptr);
0495 });
0496 BOOST_CHECK(*geometry->resolveLowestTrackingVolume(gctx, {5., 0., 0.}) !=
0497 nullptr);
0498 }
0499
0500 BOOST_AUTO_TEST_CASE(SubtreeLookupPreservesSiblingPriority) {
0501 auto makeVolume = [](double lo, double hi, const std::string& name) {
0502 return std::make_unique<TrackingVolume>(
0503 Transform3::Identity(),
0504 std::make_shared<CylinderVolumeBounds>(lo, hi, 50.), name);
0505 };
0506 auto world = makeVolume(0., 100., "world");
0507 auto first = makeVolume(0., 20., "first");
0508 auto inner = makeVolume(5., 15., "inner");
0509 inner->addVolume(makeVolume(5., 10., "leaf"));
0510 first->addVolume(std::move(inner));
0511 world->addVolume(std::move(first));
0512 auto second = makeVolume(0., 50., "second");
0513 second->addVolume(makeVolume(10., 14., "later-deeper"));
0514 world->addVolume(std::move(second));
0515 const std::vector<std::pair<double, std::string>> points{{7., "leaf"},
0516 {12., "inner"},
0517 {18., "first"},
0518 {30., "second"},
0519 {80., "world"}};
0520 {
0521 world->setVolumeLookup(std::make_unique<RzGridVolumeLookup>(
0522 gctx, *world, RzGridVolumeLookup::Config{}));
0523 world->visitVolumes([&](const TrackingVolume* volume) {
0524 if (volume != world.get()) {
0525 BOOST_CHECK(volume->volumeLookup() == nullptr);
0526 }
0527 });
0528 for (const auto& [radius, name] : points) {
0529 const auto* found = world->lowestTrackingVolume(gctx, {radius, 0., 0.});
0530 BOOST_REQUIRE(found != nullptr);
0531 BOOST_CHECK_EQUAL(found->volumeName(), name);
0532 }
0533 BOOST_CHECK(world->lowestTrackingVolume(gctx, {120., 0., 0.}) == nullptr);
0534 }
0535 }
0536
0537 BOOST_AUTO_TEST_CASE(SubtreeLookupDelegatesThroughLinearToAnotherIndex) {
0538 class CountingLinear final : public IVolumeLookup {
0539 public:
0540 CountingLinear(const TrackingVolume& root, unsigned& count)
0541 : m_count(count), m_linear(root, {}) {}
0542 const TrackingVolume* findLowest(const GeometryContext& context,
0543 const Vector3& position,
0544 double tolerance) const override {
0545 ++m_count;
0546 return m_linear.findLowest(context, position, tolerance);
0547 }
0548
0549 private:
0550 unsigned& m_count;
0551 LinearVolumeLookup m_linear;
0552 };
0553 auto makeVolume = [](double lo, double hi) {
0554 return std::make_unique<TrackingVolume>(
0555 Transform3::Identity(),
0556 std::make_shared<CylinderVolumeBounds>(lo, hi, 50.));
0557 };
0558 auto world = makeVolume(0., 100.);
0559 auto region = makeVolume(0., 50.);
0560 auto nested = makeVolume(0., 30.);
0561 auto leaf = makeVolume(10., 20.);
0562 const auto* expected = leaf.get();
0563 nested->addVolume(std::move(leaf));
0564 nested->setVolumeLookup(std::make_unique<RzGridVolumeLookup>(
0565 gctx, *nested, RzGridVolumeLookup::Config{}));
0566 region->addVolume(std::move(nested));
0567 unsigned calls = 0;
0568 region->setVolumeLookup(std::make_unique<CountingLinear>(*region, calls));
0569 world->addVolume(std::move(region));
0570 {
0571 std::ostringstream output;
0572 auto logger = getDefaultLogger("DelegationTest", Logging::WARNING, &output);
0573 RzGridVolumeLookup::Config config;
0574 {
0575
0576
0577
0578 config.maxGridCells = 9;
0579 }
0580 world->setVolumeLookup(
0581 std::make_unique<RzGridVolumeLookup>(gctx, *world, config, *logger));
0582 BOOST_CHECK(output.str().empty());
0583 const auto before = calls;
0584 BOOST_CHECK(world->lowestTrackingVolume(gctx, {15., 0., 0.}) == expected);
0585 BOOST_CHECK_EQUAL(calls, before + 1);
0586 BOOST_CHECK(world->lowestTrackingVolume(gctx, {40., 0., 0.}) ==
0587 &world->volumes().at(0));
0588 BOOST_CHECK_EQUAL(calls, before + 2);
0589 BOOST_CHECK(world->lowestTrackingVolume(gctx, {70., 0., 0.}) ==
0590 world.get());
0591 BOOST_CHECK_EQUAL(calls, before + 2);
0592 }
0593 }
0594
0595 BOOST_AUTO_TEST_CASE(StaticNodeCanAccelerateWithoutWorldPolicy) {
0596 Blueprint blueprint{Blueprint::Config{}};
0597 auto region =
0598 std::make_shared<StaticBlueprintNode>(std::make_unique<TrackingVolume>(
0599 Transform3::Identity(),
0600 std::make_shared<CylinderVolumeBounds>(0., 20., 20.), "region"));
0601 region->addChild(
0602 std::make_shared<StaticBlueprintNode>(std::make_unique<TrackingVolume>(
0603 Transform3::Identity(),
0604 std::make_shared<CylinderVolumeBounds>(0., 10., 10.), "leaf")));
0605 region->setVolumeLookupFactory([](const GeometryContext& context,
0606 const TrackingVolume& volume,
0607 const Logger& logger) {
0608 return std::make_unique<RzGridVolumeLookup>(
0609 context, volume, RzGridVolumeLookup::Config{}, logger);
0610 });
0611 blueprint.addChild(region);
0612 auto geometry = blueprint.construct(BlueprintOptions{}, gctx);
0613 BOOST_CHECK(geometry->highestTrackingVolume()->volumeLookup() == nullptr);
0614 const auto& builtRegion = geometry->highestTrackingVolume()->volumes().at(0);
0615 BOOST_CHECK(dynamic_cast<const RzGridVolumeLookup*>(
0616 builtRegion.volumeLookup()) != nullptr);
0617 BOOST_CHECK(builtRegion.volumes().at(0).volumeLookup() == nullptr);
0618 const auto* found =
0619 *geometry->resolveLowestTrackingVolume(gctx, {5., 0., 0.});
0620 BOOST_REQUIRE(found != nullptr);
0621 BOOST_CHECK_EQUAL(found->volumeName(), "leaf");
0622 }
0623
0624 BOOST_AUTO_TEST_CASE(LinearDiagnosticsRespectSubtreeBoundaries) {
0625
0626 #ifdef ACTS_ENABLE_LOG_FAILURE_THRESHOLD
0627 Logging::ScopedFailureThreshold threshold{Logging::ERROR};
0628 #endif
0629 auto world = std::make_unique<TrackingVolume>(
0630 Transform3::Identity(),
0631 std::make_shared<CylinderVolumeBounds>(0., 100., 100.));
0632 auto region = std::make_unique<TrackingVolume>(
0633 Transform3::Identity(),
0634 std::make_shared<CylinderVolumeBounds>(0., 50., 50.));
0635 for (int i = 0; i < 65; ++i) {
0636 region->addVolume(std::make_unique<TrackingVolume>(
0637 Transform3::Identity(),
0638 std::make_shared<CylinderVolumeBounds>(0., 10., 10.)));
0639 }
0640 auto* regionPtr = region.get();
0641 world->addVolume(std::move(region));
0642 std::ostringstream output;
0643 auto logger = getDefaultLogger("LinearScopeTest", Logging::WARNING, &output);
0644 LinearVolumeLookup scanned(*world, LinearVolumeLookup::Config{}, *logger);
0645 BOOST_CHECK(output.str().find("scans up to 65 siblings") !=
0646 std::string::npos);
0647 output.str("");
0648 regionPtr->setVolumeLookup(std::make_unique<RzGridVolumeLookup>(
0649 gctx, *regionPtr, RzGridVolumeLookup::Config{}));
0650 LinearVolumeLookup delegated(*world, LinearVolumeLookup::Config{}, *logger);
0651 BOOST_CHECK(output.str().empty());
0652 }
0653
0654 BOOST_AUTO_TEST_CASE(RzLookupCoincidentBounds) {
0655 auto world = std::make_shared<TrackingVolume>(
0656 Transform3::Identity(),
0657 std::make_shared<CylinderVolumeBounds>(0., 100., 100.));
0658
0659 for (int i = 0; i < 200; ++i) {
0660 world->addVolume(std::make_unique<TrackingVolume>(
0661 Transform3::Identity(),
0662 std::make_shared<CylinderVolumeBounds>(0., 10., 10.)));
0663 }
0664 {
0665 RzGridVolumeLookup lookup(gctx, *world, RzGridVolumeLookup::Config{});
0666 BOOST_CHECK(lookup.findLowest(gctx, {5., 0., 0.}, 0.) ==
0667 &world->volumes().at(0));
0668 BOOST_CHECK(lookup.findLowest(gctx, {15., 0., 0.}, 0.) == world.get());
0669 }
0670 }
0671
0672 BOOST_AUTO_TEST_CASE(RzLookupsRejectGen1Storage) {
0673 auto makeGen1 = [] {
0674 auto dense = std::make_shared<TrackingVolume>(
0675 Transform3::Identity(),
0676 std::make_shared<CylinderVolumeBounds>(0., 5., 5.));
0677 return std::make_unique<TrackingVolume>(
0678 Transform3::Identity(),
0679 std::make_shared<CylinderVolumeBounds>(0., 20., 20.), nullptr, nullptr,
0680 nullptr, MutableTrackingVolumeVector{dense}, "gen1");
0681 };
0682 {
0683 auto gen1 = makeGen1();
0684 BOOST_CHECK_THROW(
0685 (RzGridVolumeLookup(gctx, *gen1, RzGridVolumeLookup::Config{})),
0686 std::invalid_argument);
0687
0688 const Vector3 point(2., 0., 0.);
0689 const auto* dense = gen1->denseVolumes().front().get();
0690 BOOST_CHECK(gen1->lowestTrackingVolume(gctx, point) == dense);
0691 gen1->setVolumeLookup(std::make_unique<LinearVolumeLookup>(
0692 *gen1, LinearVolumeLookup::Config{}));
0693 BOOST_CHECK(gen1->lowestTrackingVolume(gctx, point) == dense);
0694
0695 auto world = makeWorld();
0696 auto* gen1Ptr = gen1.get();
0697 world->addVolume(std::move(gen1));
0698
0699 BOOST_CHECK_THROW(
0700 (RzGridVolumeLookup(gctx, *world, RzGridVolumeLookup::Config{})),
0701 std::invalid_argument);
0702 gen1Ptr->setVolumeLookup(nullptr);
0703 BOOST_CHECK_THROW(
0704 (RzGridVolumeLookup(gctx, *world, RzGridVolumeLookup::Config{})),
0705 std::invalid_argument);
0706 }
0707 }
0708
0709 BOOST_AUTO_TEST_CASE(RzGridNearIdentityRotation) {
0710 auto world = std::make_shared<TrackingVolume>(
0711 Transform3::Identity(),
0712 std::make_shared<CylinderVolumeBounds>(0., 100., 2000.));
0713 Transform3 transform = Transform3::Identity();
0714 transform.linear() = AngleAxis3(5e-13, Vector3::UnitY()).toRotationMatrix();
0715 auto child = std::make_unique<TrackingVolume>(
0716 transform, std::make_shared<CylinderVolumeBounds>(5., 20., 1000.));
0717 const auto* expected = child.get();
0718 world->addVolume(std::move(child));
0719 std::ostringstream output;
0720 auto logger = getDefaultLogger("RotationTest", Logging::WARNING, &output);
0721 RzGridVolumeLookup::Config config;
0722 config.fallbackWarningFraction = 0.;
0723 RzGridVolumeLookup lookup(gctx, *world, config, *logger);
0724 BOOST_CHECK(output.str().empty());
0725 for (double z : {-1000., 0., 1000.}) {
0726 for (double r : {5., 20.}) {
0727 const Vector3 point = transform * Vector3(r, 0., z);
0728 BOOST_CHECK(lookup.findLowest(gctx, point, 1e-6) == expected);
0729 }
0730 }
0731 }