File indexing completed on 2026-10-01 08:02:22
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include <boost/test/data/test_case.hpp>
0010 #include <boost/test/unit_test.hpp>
0011
0012 #include "Acts/Definitions/Algebra.hpp"
0013 #include "Acts/Definitions/Tolerance.hpp"
0014 #include "Acts/Definitions/Units.hpp"
0015 #include "Acts/Geometry/Blueprint.hpp"
0016 #include "Acts/Geometry/BlueprintNode.hpp"
0017 #include "Acts/Geometry/ContainerBlueprintNode.hpp"
0018 #include "Acts/Geometry/CylinderPortalShell.hpp"
0019 #include "Acts/Geometry/CylinderVolumeBounds.hpp"
0020 #include "Acts/Geometry/GeometryContext.hpp"
0021 #include "Acts/Geometry/NavigationPolicyFactory.hpp"
0022 #include "Acts/Geometry/StaticBlueprintNode.hpp"
0023 #include "Acts/Geometry/TrackingVolume.hpp"
0024 #include "Acts/Navigation/CylinderNavigationPolicy.hpp"
0025 #include "Acts/Navigation/FrustumNavigationPolicy.hpp"
0026 #include "Acts/Navigation/INavigationPolicy.hpp"
0027 #include "Acts/Navigation/MultiNavigationPolicy.hpp"
0028 #include "Acts/Navigation/NavigationDelegate.hpp"
0029 #include "Acts/Navigation/NavigationStream.hpp"
0030 #include "Acts/Navigation/TryAllNavigationPolicy.hpp"
0031 #include "Acts/Utilities/Logger.hpp"
0032
0033 #include <boost/algorithm/string/join.hpp>
0034
0035 using namespace Acts;
0036 using namespace Acts::UnitLiterals;
0037 namespace bdata = boost::unit_test::data;
0038
0039 namespace ActsTests {
0040
0041 BOOST_AUTO_TEST_SUITE(NavigationSuite)
0042
0043 auto gctx = GeometryContext::dangerouslyDefaultConstruct();
0044 auto logger = getDefaultLogger("NavigationPolicyTests", Logging::VERBOSE);
0045
0046 struct APolicy : public INavigationPolicy {
0047 APolicy(const GeometryContext& , const TrackingVolume& ,
0048 const Logger& ) {}
0049
0050 void initializeCandidates(const GeometryContext& ,
0051 const NavigationArguments& ,
0052 NavigationPolicyState& ,
0053 AppendOnlyNavigationStream& ,
0054 const Logger& ) const {
0055 const_cast<APolicy*>(this)->executed = true;
0056 }
0057
0058 void connect(NavigationDelegate& delegate) const override {
0059 connectDefault<APolicy>(delegate);
0060 }
0061
0062 bool executed = false;
0063 };
0064
0065 struct BPolicy : public INavigationPolicy {
0066 struct Config {
0067 int value;
0068 };
0069
0070 BPolicy(const GeometryContext& , const TrackingVolume& ,
0071 const Logger& , Config config)
0072 : m_config(config) {}
0073
0074 void connect(NavigationDelegate& delegate) const override {
0075 connectDefault<BPolicy>(delegate);
0076 }
0077
0078 void initializeCandidates(const GeometryContext& ,
0079 const NavigationArguments& ,
0080 NavigationPolicyState& ,
0081 AppendOnlyNavigationStream& ,
0082 const Logger& ) const {
0083 const_cast<BPolicy*>(this)->executed = true;
0084 const_cast<BPolicy*>(this)->value = m_config.value;
0085 }
0086
0087 bool executed = false;
0088 int value = 0;
0089
0090 Config m_config;
0091 };
0092
0093 BOOST_AUTO_TEST_CASE(DirectTest) {
0094 TrackingVolume volume{
0095 Transform3::Identity(),
0096 std::make_shared<CylinderVolumeBounds>(250_mm, 400_mm, 310_mm),
0097 "PixelLayer3"};
0098
0099 MultiNavigationPolicy policy{
0100 std::make_unique<APolicy>(gctx, volume, *logger),
0101 std::make_unique<BPolicy>(gctx, volume, *logger,
0102 BPolicy::Config{.value = 4242})};
0103
0104 NavigationDelegate delegate;
0105 policy.connect(delegate);
0106
0107 NavigationStream main;
0108 AppendOnlyNavigationStream stream{main};
0109 NavigationArguments args{.position = Vector3::Zero(),
0110 .direction = Vector3::Zero()};
0111 NavigationPolicyStateManager stateManager;
0112 stateManager.pushState<MultiNavigationPolicy::State>();
0113 policy.createState(gctx, args, stateManager, *logger);
0114 auto policyState = stateManager.currentState();
0115 delegate(gctx, args, policyState, stream, *logger);
0116
0117 BOOST_REQUIRE_EQUAL(policy.policies().size(), 2);
0118 const auto& policyA = dynamic_cast<const APolicy&>(*policy.policies()[0]);
0119 const auto& policyB = dynamic_cast<const BPolicy&>(*policy.policies()[1]);
0120
0121 BOOST_CHECK(policyA.executed);
0122 BOOST_CHECK(policyB.executed);
0123 BOOST_CHECK_EQUAL(policyB.value, 4242);
0124 }
0125
0126 BOOST_AUTO_TEST_CASE(FactoryTest) {
0127 TrackingVolume volume{
0128 Transform3::Identity(),
0129 std::make_shared<CylinderVolumeBounds>(250_mm, 400_mm, 310_mm),
0130 "PixelLayer3"};
0131
0132 BPolicy::Config config{.value = 42};
0133
0134 std::function<std::unique_ptr<INavigationPolicy>(
0135 const GeometryContext&, const TrackingVolume&, const Logger&)>
0136 factory = NavigationPolicyFactory{}
0137 .add<APolicy>()
0138 .add<BPolicy>(config);
0139
0140 auto policyBase = factory(gctx, volume, *logger);
0141 auto policyBase2 = factory(gctx, volume, *logger);
0142
0143 auto& policy = dynamic_cast<MultiNavigationPolicy&>(*policyBase);
0144
0145 NavigationDelegate delegate;
0146 policy.connect(delegate);
0147
0148 NavigationStream main;
0149 AppendOnlyNavigationStream stream{main};
0150 NavigationArguments args{.position = Vector3::Zero(),
0151 .direction = Vector3::Zero()};
0152 NavigationPolicyStateManager stateManager;
0153 stateManager.pushState<MultiNavigationPolicy::State>();
0154 policy.createState(gctx, args, stateManager, *logger);
0155 auto policyState = stateManager.currentState();
0156 delegate(gctx, args, policyState, stream, *logger);
0157
0158 BOOST_REQUIRE_EQUAL(policy.policies().size(), 2);
0159 const auto& policyA = dynamic_cast<const APolicy&>(*policy.policies()[0]);
0160 const auto& policyB = dynamic_cast<const BPolicy&>(*policy.policies()[1]);
0161
0162 BOOST_CHECK(policyA.executed);
0163 BOOST_CHECK(policyB.executed);
0164 BOOST_CHECK_EQUAL(policyB.value, 42);
0165
0166 auto& policy2 = dynamic_cast<MultiNavigationPolicy&>(*policyBase2);
0167
0168 NavigationDelegate delegate2;
0169 policyBase2->connect(delegate2);
0170
0171 NavigationPolicyStateManager stateManager2;
0172 stateManager2.pushState<MultiNavigationPolicy::State>();
0173 policy2.createState(gctx, args, stateManager2, *logger);
0174 auto policyState2 = stateManager2.currentState();
0175 delegate2(gctx, args, policyState2, stream, *logger);
0176
0177 BOOST_REQUIRE_EQUAL(policy2.policies().size(), 2);
0178 const auto& policy2A = dynamic_cast<const APolicy&>(*policy2.policies()[0]);
0179 const auto& policy2B = dynamic_cast<const BPolicy&>(*policy2.policies()[1]);
0180
0181 BOOST_CHECK(policy2A.executed);
0182 BOOST_CHECK(policy2B.executed);
0183 BOOST_CHECK_EQUAL(policy2B.value, 42);
0184 }
0185
0186 BOOST_AUTO_TEST_CASE(AsUniquePtrTest) {
0187 TrackingVolume volume{
0188 Transform3::Identity(),
0189 std::make_shared<CylinderVolumeBounds>(250_mm, 400_mm, 310_mm),
0190 "PixelLayer3"};
0191
0192 std::unique_ptr<NavigationPolicyFactory> factory =
0193 NavigationPolicyFactory{}.add<APolicy>().asUniquePtr();
0194
0195 auto policyBase = factory->build(gctx, volume, *logger);
0196 auto& policy = dynamic_cast<MultiNavigationPolicy&>(*policyBase);
0197
0198 NavigationDelegate delegate;
0199 policyBase->connect(delegate);
0200
0201 NavigationStream main;
0202 AppendOnlyNavigationStream stream{main};
0203 NavigationArguments args{.position = Vector3::Zero(),
0204 .direction = Vector3::Zero()};
0205 NavigationPolicyStateManager stateManager;
0206 stateManager.pushState<MultiNavigationPolicy::State>();
0207 policy.createState(gctx, args, stateManager, *logger);
0208 auto policyState = stateManager.currentState();
0209 delegate(gctx, args, policyState, stream, *logger);
0210
0211 BOOST_REQUIRE_EQUAL(policy.policies().size(), 1);
0212 BOOST_CHECK(dynamic_cast<const APolicy&>(*policy.policies()[0]).executed);
0213 }
0214
0215 struct CPolicy : public INavigationPolicy {};
0216
0217 template <typename T>
0218 struct CPolicySpecialized : public CPolicy {
0219 struct Config {
0220 T value;
0221 };
0222
0223 CPolicySpecialized(const TrackingVolume& , Config config)
0224 : m_config(config) {}
0225
0226 void connect(NavigationDelegate& delegate) const override {
0227 connectDefault<CPolicySpecialized<T>>(delegate);
0228 }
0229
0230 void initializeCandidates(const GeometryContext& ,
0231 const NavigationArguments& ,
0232 NavigationPolicyState& ,
0233 AppendOnlyNavigationStream& ,
0234 const Logger& ) const {
0235 auto* self = const_cast<CPolicySpecialized<int>*>(this);
0236 self->executed = true;
0237 self->value = m_config.value;
0238 }
0239
0240 bool executed = false;
0241 int value = 0;
0242
0243 Config m_config;
0244 };
0245
0246 struct IsolatedConfig {
0247 int value;
0248 };
0249
0250 std::unique_ptr<INavigationPolicy> makeCPolicy(const GeometryContext& ,
0251 const TrackingVolume& volume,
0252 const Logger& ,
0253 IsolatedConfig config) {
0254
0255
0256 CPolicySpecialized<int>::Config config2{.value = config.value};
0257 return std::make_unique<CPolicySpecialized<int>>(volume, config2);
0258 }
0259
0260 BOOST_AUTO_TEST_CASE(IsolatedFactory) {
0261 TrackingVolume volume{
0262 Transform3::Identity(),
0263 std::make_shared<CylinderVolumeBounds>(250_mm, 400_mm, 310_mm),
0264 "PixelLayer3"};
0265
0266 IsolatedConfig config{.value = 44};
0267 auto factory =
0268 NavigationPolicyFactory{}.add<APolicy>().add(makeCPolicy, config);
0269
0270 auto factory2 =
0271 NavigationPolicyFactory{}.add(makeCPolicy, config).add<APolicy>();
0272
0273 auto policyBase = factory(gctx, volume, *logger);
0274 auto& policy = dynamic_cast<MultiNavigationPolicy&>(*policyBase);
0275
0276 NavigationDelegate delegate;
0277 policyBase->connect(delegate);
0278
0279 NavigationStream main;
0280 AppendOnlyNavigationStream stream{main};
0281 NavigationArguments args{.position = Vector3::Zero(),
0282 .direction = Vector3::Zero()};
0283 NavigationPolicyStateManager stateManager;
0284 stateManager.pushState<MultiNavigationPolicy::State>();
0285 policy.createState(gctx, args, stateManager, *logger);
0286 auto policyState = stateManager.currentState();
0287 delegate(gctx, args, policyState, stream, *logger);
0288
0289 BOOST_REQUIRE_EQUAL(policy.policies().size(), 2);
0290
0291 const auto& policyA = dynamic_cast<const APolicy&>(*policy.policies()[0]);
0292 const auto& cPolicy =
0293 dynamic_cast<const CPolicySpecialized<int>&>(*policy.policies()[1]);
0294
0295 BOOST_CHECK(policyA.executed);
0296 BOOST_CHECK(cPolicy.executed);
0297 BOOST_CHECK_EQUAL(cPolicy.value, 44);
0298 }
0299
0300 namespace {
0301
0302 std::vector<const Portal*> getTruth(const Vector3& position,
0303 const Vector3& direction,
0304 const Transform3& transform,
0305 const TrackingVolume& cylVolume,
0306 SingleCylinderPortalShell& shell,
0307 const Logger& logger, bool posOnly = true) {
0308 Vector3 gpos = transform * position;
0309 Vector3 gdir = transform.linear() * direction;
0310 TryAllNavigationPolicy tryAll(gctx, cylVolume, logger);
0311 NavigationArguments args{.position = gpos, .direction = gdir};
0312 NavigationStream main;
0313 AppendOnlyNavigationStream stream{main};
0314 auto gctx = GeometryContext::dangerouslyDefaultConstruct();
0315 NavigationPolicyStateManager stateManager;
0316 tryAll.createState(gctx, args, stateManager, logger);
0317 auto policyState = stateManager.currentState();
0318 tryAll.initializeCandidates(gctx, args, policyState, stream, logger);
0319 main.initialize(gctx, {gpos, gdir}, logger());
0320 std::vector<const Portal*> portals;
0321 for (auto& candidate : main.candidates()) {
0322 if (!candidate.intersection().isValid()) {
0323 continue;
0324 }
0325
0326 if (main.candidates().size() > 1 && posOnly &&
0327 !detail::checkPathLength(candidate.intersection().pathLength(),
0328 s_onSurfaceTolerance,
0329 std::numeric_limits<double>::max(), logger)) {
0330 continue;
0331 }
0332
0333 portals.push_back(&candidate.portal());
0334 }
0335
0336
0337 const Portal* outerCylinder = nullptr;
0338 const Portal* innerCylinder = nullptr;
0339 const Portal* positiveDisc = nullptr;
0340 const Portal* negativeDisc = nullptr;
0341
0342 for (const Portal* portal : portals) {
0343 if (portal ==
0344 shell.portal(CylinderVolumeBounds::Face::OuterCylinder).get()) {
0345 outerCylinder = portal;
0346 } else if (portal ==
0347 shell.portal(CylinderVolumeBounds::Face::InnerCylinder).get()) {
0348 innerCylinder = portal;
0349 } else if (portal ==
0350 shell.portal(CylinderVolumeBounds::Face::PositiveDisc).get()) {
0351 positiveDisc = portal;
0352 } else if (portal ==
0353 shell.portal(CylinderVolumeBounds::Face::NegativeDisc).get()) {
0354 negativeDisc = portal;
0355 }
0356 }
0357
0358
0359 std::vector<const Portal*> filteredPortals;
0360
0361
0362
0363 if ((innerCylinder != nullptr) && (outerCylinder != nullptr)) {
0364
0365 filteredPortals.push_back(innerCylinder);
0366 } else {
0367
0368 if (innerCylinder != nullptr) {
0369 filteredPortals.push_back(innerCylinder);
0370 }
0371 if (outerCylinder != nullptr) {
0372 filteredPortals.push_back(outerCylinder);
0373 }
0374 }
0375
0376
0377
0378 if (innerCylinder == nullptr) {
0379
0380 if (positiveDisc != nullptr) {
0381 filteredPortals.push_back(positiveDisc);
0382 }
0383 if (negativeDisc != nullptr) {
0384 filteredPortals.push_back(negativeDisc);
0385 }
0386 }
0387
0388
0389 return filteredPortals;
0390 }
0391
0392 std::vector<const Portal*> getSmart(const Vector3& position,
0393 const Vector3& direction,
0394 const Transform3& transform,
0395 CylinderNavigationPolicy& policy) {
0396 Vector3 gpos = transform * position;
0397 Vector3 gdir = transform.linear() * direction;
0398 NavigationArguments args{.position = gpos, .direction = gdir};
0399 NavigationStream main;
0400 auto gctx = GeometryContext::dangerouslyDefaultConstruct();
0401 AppendOnlyNavigationStream stream{main};
0402 NavigationPolicyStateManager stateManager;
0403 policy.createState(gctx, args, stateManager, *logger);
0404 auto policyState = stateManager.currentState();
0405 policy.initializeCandidates(gctx, args, policyState, stream, *logger);
0406
0407 std::vector<const Portal*> portals;
0408
0409
0410 for (auto& candidate : main.candidates()) {
0411 portals.push_back(&candidate.portal());
0412 }
0413 return portals;
0414 }
0415
0416 void checkEqual(const std::vector<const Portal*>& exp,
0417 const std::vector<const Portal*>& act,
0418 SingleCylinderPortalShell& shell) {
0419 auto which = [&](const Portal* p) -> std::string {
0420 if (p == shell.portal(CylinderVolumeBounds::Face::InnerCylinder).get()) {
0421 return "InnerCylinder";
0422 }
0423 if (p == shell.portal(CylinderVolumeBounds::Face::OuterCylinder).get()) {
0424 return "OuterCylinder";
0425 }
0426 if (p == shell.portal(CylinderVolumeBounds::Face::PositiveDisc).get()) {
0427 return "PositiveDisc";
0428 }
0429 if (p == shell.portal(CylinderVolumeBounds::Face::NegativeDisc).get()) {
0430 return "NegativeDisc";
0431 }
0432 BOOST_FAIL("Unknown portal");
0433 return "";
0434 };
0435
0436 std::set<const Portal*> expSet;
0437 std::set<const Portal*> actSet;
0438
0439 std::ranges::copy(exp, std::inserter(expSet, expSet.begin()));
0440 std::ranges::copy(act, std::inserter(actSet, actSet.begin()));
0441
0442 if (expSet != actSet) {
0443 BOOST_ERROR([&]() -> std::string {
0444 std::vector<std::string> exps;
0445 for (auto& p : exp) {
0446 exps.push_back(which(p));
0447 }
0448 std::vector<std::string> acts;
0449 for (auto& p : act) {
0450 acts.push_back(which(p));
0451 }
0452 return "[" + boost::algorithm::join(exps, ", ") + "] != [" +
0453 boost::algorithm::join(acts, ", ") + "]";
0454 }());
0455 }
0456 }
0457
0458 }
0459
0460 BOOST_DATA_TEST_CASE(
0461 CylinderPolicyTest,
0462 (bdata::xrange(-135, 180, 45) *
0463 bdata::make(Vector3{0_mm, 0_mm, 0_mm}, Vector3{20_mm, 0_mm, 0_mm},
0464 Vector3{0_mm, 20_mm, 0_mm}, Vector3{20_mm, 20_mm, 0_mm},
0465 Vector3{0_mm, 0_mm, 20_mm})),
0466 angle, offset) {
0467 using enum CylinderVolumeBounds::Face;
0468
0469 Transform3 transform = Transform3::Identity();
0470 transform *= AngleAxis3{angle * 1_degree, Vector3::UnitX()};
0471 transform *= Translation3{offset};
0472 auto cylBounds =
0473 std::make_shared<CylinderVolumeBounds>(100_mm, 400_mm, 300_mm);
0474 auto cylVolume =
0475 std::make_shared<TrackingVolume>(transform, cylBounds, "CylinderVolume");
0476 SingleCylinderPortalShell shell{gctx, *cylVolume};
0477 shell.applyToVolume();
0478
0479 {
0480 Vector3 position = Vector3::UnitX() * 150_mm;
0481 Vector3 direction = Vector3::UnitZ();
0482
0483 auto exp =
0484 getTruth(position, direction, transform, *cylVolume, shell, *logger);
0485
0486 BOOST_CHECK(exp.size() == 1);
0487 BOOST_CHECK(exp.at(0) == shell.portal(PositiveDisc).get());
0488
0489 CylinderNavigationPolicy policy(gctx, *cylVolume, *logger);
0490 auto act = getSmart(position, direction, transform, policy);
0491 checkEqual(exp, act, shell);
0492 }
0493
0494 {
0495 Vector3 position = Vector3::UnitX() * 150_mm;
0496 Vector3 direction = Vector3{1, 1, 0}.normalized();
0497
0498 auto exp =
0499 getTruth(position, direction, transform, *cylVolume, shell, *logger);
0500
0501 BOOST_CHECK(exp.size() == 1);
0502 BOOST_CHECK(exp.at(0) == shell.portal(OuterCylinder).get());
0503
0504 CylinderNavigationPolicy policy(gctx, *cylVolume, *logger);
0505 auto act = getSmart(position, direction, transform, policy);
0506 checkEqual(exp, act, shell);
0507 }
0508
0509 {
0510 Vector3 position = Vector3::UnitX() * 150_mm;
0511 Vector3 direction = Vector3{-1, 0, 0}.normalized();
0512
0513 auto exp =
0514 getTruth(position, direction, transform, *cylVolume, shell, *logger);
0515
0516 BOOST_CHECK(exp.size() == 1);
0517 BOOST_CHECK(exp.at(0) == shell.portal(InnerCylinder).get());
0518
0519 CylinderNavigationPolicy policy(gctx, *cylVolume, *logger);
0520 auto act = getSmart(position, direction, transform, policy);
0521 checkEqual(exp, act, shell);
0522 }
0523
0524 {
0525 Vector3 position = Vector3::UnitX() * 150_mm;
0526 Vector3 direction = -Vector3::UnitZ();
0527
0528 auto exp =
0529 getTruth(position, direction, transform, *cylVolume, shell, *logger);
0530
0531 BOOST_CHECK(exp.size() == 1);
0532 BOOST_CHECK(exp.at(0) == shell.portal(NegativeDisc).get());
0533
0534 CylinderNavigationPolicy policy(gctx, *cylVolume, *logger);
0535 auto act = getSmart(position, direction, transform, policy);
0536 checkEqual(exp, act, shell);
0537 }
0538
0539 {
0540 Vector3 position{50, -200, 0};
0541 Vector3 direction = Vector3{0, 1.5, 1}.normalized();
0542
0543 auto exp =
0544 getTruth(position, direction, transform, *cylVolume, shell, *logger);
0545
0546 BOOST_CHECK(exp.size() == 1);
0547 BOOST_CHECK(exp.at(0) == shell.portal(InnerCylinder).get());
0548
0549 CylinderNavigationPolicy policy(gctx, *cylVolume, *logger);
0550 auto act = getSmart(position, direction, transform, policy);
0551 checkEqual(exp, act, shell);
0552 }
0553
0554 {
0555 Vector3 position{50, -200, 0};
0556 Vector3 direction = Vector3{0, 1.2, 1}.normalized();
0557
0558 auto exp =
0559 getTruth(position, direction, transform, *cylVolume, shell, *logger);
0560
0561 BOOST_CHECK(exp.size() == 1);
0562 BOOST_CHECK(exp.at(0) == shell.portal(InnerCylinder).get());
0563
0564 CylinderNavigationPolicy policy(gctx, *cylVolume, *logger);
0565 auto act = getSmart(position, direction, transform, policy);
0566 checkEqual(exp, act, shell);
0567 }
0568
0569 {
0570 Vector3 position{50, -200, 0};
0571 Vector3 direction = Vector3{0, 0.9, 1}.normalized();
0572
0573 auto exp =
0574 getTruth(position, direction, transform, *cylVolume, shell, *logger);
0575
0576 BOOST_CHECK(exp.size() == 1);
0577 BOOST_CHECK(exp.at(0) == shell.portal(InnerCylinder).get());
0578
0579 CylinderNavigationPolicy policy(gctx, *cylVolume, *logger);
0580 auto act = getSmart(position, direction, transform, policy);
0581 checkEqual(exp, act, shell);
0582 }
0583
0584 {
0585 Vector3 position{20, -200, 0};
0586 Vector3 direction = Vector3{0.45, 0.9, 1}.normalized();
0587
0588 auto exp =
0589 getTruth(position, direction, transform, *cylVolume, shell, *logger);
0590
0591 BOOST_CHECK(exp.size() == 1);
0592 BOOST_CHECK(exp.at(0) == shell.portal(PositiveDisc).get());
0593
0594 CylinderNavigationPolicy policy(gctx, *cylVolume, *logger);
0595 auto act = getSmart(position, direction, transform, policy);
0596 checkEqual(exp, act, shell);
0597 }
0598
0599 {
0600 Vector3 position{20, -200, 0};
0601 Vector3 direction = Vector3{0.45, 0.9, -1}.normalized();
0602
0603 auto exp =
0604 getTruth(position, direction, transform, *cylVolume, shell, *logger);
0605
0606 BOOST_CHECK(exp.size() == 1);
0607 BOOST_CHECK(exp.at(0) == shell.portal(NegativeDisc).get());
0608
0609 CylinderNavigationPolicy policy(gctx, *cylVolume, *logger);
0610 auto act = getSmart(position, direction, transform, policy);
0611 checkEqual(exp, act, shell);
0612 }
0613
0614 {
0615 Vector3 position{400 * std::cos(std::numbers::pi / 4),
0616 400 * std::sin(std::numbers::pi / 4), 0};
0617 Vector3 direction = Vector3{0.45, -0.9, -0.1}.normalized();
0618
0619
0620
0621 auto exp = getTruth(position, direction, transform, *cylVolume, shell,
0622 *logger, false);
0623
0624 BOOST_CHECK(exp.size() == 1);
0625 BOOST_CHECK(exp.at(0) == shell.portal(OuterCylinder).get());
0626
0627 CylinderNavigationPolicy policy(gctx, *cylVolume, *logger);
0628 auto act = getSmart(position, direction, transform, policy);
0629 checkEqual(exp, act, shell);
0630 }
0631
0632 {
0633 Vector3 position{400 * std::cos(std::numbers::pi / 4),
0634 400 * std::sin(std::numbers::pi / 4), 0};
0635 Vector3 direction = Vector3{-0.3, -0.9, -0.1}.normalized();
0636
0637
0638
0639 auto exp = getTruth(position, direction, transform, *cylVolume, shell,
0640 *logger, false);
0641
0642 BOOST_CHECK(exp.size() == 1);
0643 BOOST_CHECK(exp.at(0) == shell.portal(OuterCylinder).get());
0644
0645 CylinderNavigationPolicy policy(gctx, *cylVolume, *logger);
0646 auto act = getSmart(position, direction, transform, policy);
0647 checkEqual(exp, act, shell);
0648 }
0649
0650 {
0651 Vector3 position{100 * std::cos(std::numbers::pi / 4),
0652 100 * std::sin(std::numbers::pi / 4), 0};
0653 double dangle = 0.1;
0654 Vector3 direction = Vector3{std::cos(dangle), std::sin(dangle), 0.01};
0655
0656
0657 auto exp =
0658 getTruth(position, direction, transform, *cylVolume, shell, *logger);
0659
0660 BOOST_CHECK(exp.size() == 1);
0661 BOOST_CHECK(exp.at(0) == shell.portal(OuterCylinder).get());
0662
0663 CylinderNavigationPolicy policy(gctx, *cylVolume, *logger);
0664 auto act = getSmart(position, direction, transform, policy);
0665 checkEqual(exp, act, shell);
0666 }
0667
0668 {
0669 Vector3 position{200 * std::cos(std::numbers::pi / 4),
0670 200 * std::sin(std::numbers::pi / 4), 0};
0671 Vector3 target{150 * std::cos(std::numbers::pi * 5 / 4),
0672 150 * std::sin(std::numbers::pi * 5 / 4), 300};
0673 Vector3 direction = (target - position).normalized();
0674
0675 auto exp =
0676 getTruth(position, direction, transform, *cylVolume, shell, *logger);
0677
0678 BOOST_CHECK_EQUAL(exp.size(), 1);
0679 BOOST_CHECK_EQUAL(exp.at(0), shell.portal(InnerCylinder).get());
0680
0681 CylinderNavigationPolicy policy(gctx, *cylVolume, *logger);
0682 auto act = getSmart(position, direction, transform, policy);
0683 checkEqual(exp, act, shell);
0684 }
0685 }
0686
0687 namespace {
0688
0689 std::mt19937 engine;
0690
0691 unsigned long seed() {
0692 static unsigned long s = 42;
0693 return s++;
0694 }
0695
0696 std::uniform_real_distribution<double> rDistOffBoundary{
0697 100 + 2 * s_onSurfaceTolerance, 400 - 2 * s_onSurfaceTolerance};
0698 std::uniform_real_distribution<double> zDistOffBoundary{
0699 -300_mm + 2 * s_onSurfaceTolerance, 300_mm - 2 * s_onSurfaceTolerance};
0700 std::uniform_real_distribution<double> phiDist{-std::numbers::pi,
0701 std::numbers::pi};
0702 std::uniform_real_distribution<double> thetaDist{0, std::numbers::pi};
0703
0704 }
0705
0706 BOOST_DATA_TEST_CASE(
0707 CylinderPolicyTestOffBoundary,
0708 bdata::random((bdata::engine = engine, bdata::seed = seed(),
0709 bdata::distribution = rDistOffBoundary)) ^
0710 bdata::random((bdata::engine = engine, bdata::seed = seed(),
0711 bdata::distribution = zDistOffBoundary)) ^
0712 bdata::random((bdata::engine = engine, bdata::seed = seed(),
0713 bdata::distribution = phiDist)) ^
0714 bdata::random((bdata::engine = engine, bdata::seed = seed(),
0715 bdata::distribution = phiDist)) ^
0716 bdata::random((bdata::engine = engine, bdata::seed = seed(),
0717 bdata::distribution = thetaDist)) ^
0718 bdata::xrange(100),
0719 r, z, phiPos, phiDir, theta, index) {
0720 static_cast<void>(index);
0721
0722 Transform3 transform = Transform3::Identity();
0723 auto cylBounds =
0724 std::make_shared<CylinderVolumeBounds>(100_mm, 400_mm, 300_mm);
0725 auto cylVolume =
0726 std::make_shared<TrackingVolume>(transform, cylBounds, "CylinderVolume");
0727 SingleCylinderPortalShell shell{gctx, *cylVolume};
0728 shell.applyToVolume();
0729
0730 Vector3 position{r * std::cos(phiPos), r * std::sin(phiPos), z};
0731 Vector3 direction{std::sin(theta) * std::cos(phiDir),
0732 std::sin(theta) * std::sin(phiDir), std::cos(theta)};
0733
0734 BOOST_CHECK(cylBounds->inside(position));
0735
0736 auto exp =
0737 getTruth(position, direction, transform, *cylVolume, shell, *logger);
0738
0739 CylinderNavigationPolicy policy(gctx, *cylVolume, *logger);
0740 auto act = getSmart(position, direction, transform, policy);
0741 checkEqual(exp, act, shell);
0742 }
0743
0744 BOOST_DATA_TEST_CASE(
0745 CylinderPolicyTestOnRBoundary,
0746 bdata::make(100, 400) *
0747 (bdata::random((bdata::engine = engine, bdata::seed = seed(),
0748 bdata::distribution = zDistOffBoundary)) ^
0749 bdata::random((bdata::engine = engine, bdata::seed = seed(),
0750 bdata::distribution = phiDist)) ^
0751 bdata::random((bdata::engine = engine, bdata::seed = seed(),
0752 bdata::distribution = phiDist)) ^
0753 bdata::random((bdata::engine = engine, bdata::seed = seed(),
0754 bdata::distribution = zDistOffBoundary)) ^
0755 bdata::xrange(100)),
0756 r, z, phiPos, phiTarget, zTarget, index) {
0757 static_cast<void>(index);
0758
0759 Transform3 transform = Transform3::Identity();
0760 auto cylBounds =
0761 std::make_shared<CylinderVolumeBounds>(100_mm, 400_mm, 300_mm);
0762 auto cylVolume =
0763 std::make_shared<TrackingVolume>(transform, cylBounds, "CylinderVolume");
0764 SingleCylinderPortalShell shell{gctx, *cylVolume};
0765 shell.applyToVolume();
0766
0767 Vector3 position{r * std::cos(phiPos), r * std::sin(phiPos), z};
0768 Vector3 target{r * std::cos(phiTarget), r * std::sin(phiTarget), zTarget};
0769 Vector3 direction = (target - position).normalized();
0770
0771 auto exp =
0772 getTruth(position, direction, transform, *cylVolume, shell, *logger);
0773
0774 CylinderNavigationPolicy policy(gctx, *cylVolume, *logger);
0775 auto act = getSmart(position, direction, transform, policy);
0776 checkEqual(exp, act, shell);
0777 }
0778
0779 BOOST_DATA_TEST_CASE(
0780 CylinderPolicyTestOnZBoundary,
0781 bdata::make(-300, 300) *
0782 (bdata::random((bdata::engine = engine, bdata::seed = seed(),
0783 bdata::distribution = rDistOffBoundary)) ^
0784 bdata::random((bdata::engine = engine, bdata::seed = seed(),
0785 bdata::distribution = phiDist)) ^
0786 bdata::random((bdata::engine = engine, bdata::seed = seed(),
0787 bdata::distribution = phiDist)) ^
0788 bdata::random((bdata::engine = engine, bdata::seed = seed(),
0789 bdata::distribution = zDistOffBoundary)) ^
0790 bdata::xrange(100)),
0791 z, r, phiPos, phiTarget, zTarget, index) {
0792 static_cast<void>(index);
0793 Transform3 transform = Transform3::Identity();
0794 auto cylBounds =
0795 std::make_shared<CylinderVolumeBounds>(100_mm, 400_mm, 300_mm);
0796 auto cylVolume =
0797 std::make_shared<TrackingVolume>(transform, cylBounds, "CylinderVolume");
0798 SingleCylinderPortalShell shell{gctx, *cylVolume};
0799 shell.applyToVolume();
0800
0801 Vector3 position{r * std::cos(phiPos), r * std::sin(phiPos),
0802 static_cast<double>(z)};
0803 Vector3 target{r * std::cos(phiTarget), r * std::sin(phiTarget), zTarget};
0804 Vector3 direction = (target - position).normalized();
0805
0806 BOOST_CHECK(cylBounds->inside(position));
0807
0808 auto exp =
0809 getTruth(position, direction, transform, *cylVolume, shell, *logger);
0810
0811 CylinderNavigationPolicy policy(gctx, *cylVolume, *logger);
0812 auto act = getSmart(position, direction, transform, policy);
0813 checkEqual(exp, act, shell);
0814 }
0815
0816 BOOST_AUTO_TEST_CASE(CylinderPolicyZeroInnerRadiusTest) {
0817
0818 Transform3 transform = Transform3::Identity();
0819
0820
0821 auto cylBounds = std::make_shared<CylinderVolumeBounds>(0_mm, 400_mm, 300_mm);
0822 auto cylVolume = std::make_shared<TrackingVolume>(transform, cylBounds,
0823 "ZeroInnerRadiusVolume");
0824
0825
0826
0827 {
0828 Acts::Logging::ScopedFailureThreshold log{Logging::FATAL};
0829 BOOST_CHECK_THROW(CylinderNavigationPolicy(gctx, *cylVolume, *logger),
0830 std::invalid_argument);
0831 }
0832 }
0833
0834 BOOST_AUTO_TEST_CASE(FrustumNavigationPolicyTest) {
0835
0836 Blueprint::Config cfg;
0837 cfg.envelope[AxisDirection::AxisZ] = {50_mm, 50_mm};
0838 cfg.envelope[AxisDirection::AxisY] = {50_mm, 50_mm};
0839 cfg.envelope[AxisDirection::AxisX] = {50_mm, 50_mm};
0840 auto root = std::make_unique<Blueprint>(cfg);
0841 auto& cub = root->addCuboidContainer("Container", AxisDirection::AxisZ);
0842 auto cubBounds = std::make_shared<CuboidVolumeBounds>(20_mm, 20_mm, 20_mm);
0843 auto childCub = std::make_unique<TrackingVolume>(Transform3::Identity(),
0844 cubBounds, "child");
0845 Acts::Experimental::FrustumNavigationPolicy::Config frustumConfig{2};
0846 Acts::GeometryIdentifier id = Acts::GeometryIdentifier().withVolume(1);
0847 childCub->assignGeometryId(id);
0848 auto cubNode =
0849 std::make_shared<Acts::StaticBlueprintNode>(std::move(childCub));
0850 int chamberId = 1;
0851 auto childBounds = std::make_shared<CuboidVolumeBounds>(5_mm, 5_mm, 5_mm);
0852 std::vector<std::shared_ptr<Acts::StaticBlueprintNode>> nodes;
0853 for (int x = -3; x < 4; x += 2) {
0854 for (int y = -3; y < 4; y += 2) {
0855 for (int z = -3; z < 4; z += 2) {
0856 auto childVol = std::make_unique<TrackingVolume>(
0857 Transform3::Identity() *
0858 Translation3{Vector3{x * 5_mm, y * 5_mm, z * 5_mm}},
0859 childBounds,
0860 "child_" + std::to_string(x) + "_" + std::to_string(y) + "_" +
0861 std::to_string(z));
0862 Acts::GeometryIdentifier chId = id.withLayer(chamberId++);
0863 childVol->assignGeometryId(chId);
0864 auto staticNode =
0865 std::make_shared<Acts::StaticBlueprintNode>(std::move(childVol));
0866 nodes.push_back(std::move(staticNode));
0867 cubNode->addChild(nodes.back());
0868 }
0869 }
0870 }
0871 std::shared_ptr<Acts::NavigationPolicyFactory> factory =
0872 std::make_shared<Acts::NavigationPolicyFactory>(
0873 Acts::NavigationPolicyFactory{}
0874 .add<Acts::Experimental::FrustumNavigationPolicy>(frustumConfig));
0875 cubNode->setNavigationPolicyFactory(factory);
0876 cub.addChild(cubNode);
0877 auto tGeometry = root->construct({}, gctx, *logger);
0878 const TrackingVolume* vol = tGeometry->findVolume(id);
0879 NavigationStream main;
0880 AppendOnlyNavigationStream stream{main};
0881 NavigationArguments args{.position = Vector3{1, 1, 1},
0882 .direction = Vector3{1, 1, 1}};
0883 NavigationPolicyStateManager stateManager;
0884 std::unique_ptr<Acts::Experimental::FrustumNavigationPolicy> frustumPolicy =
0885 std::make_unique<Acts::Experimental::FrustumNavigationPolicy>(
0886 gctx, *vol, *logger, frustumConfig);
0887 frustumPolicy->createState(gctx, args, stateManager, *logger);
0888 auto policyState = stateManager.currentState();
0889 frustumPolicy->initializeCandidates(gctx, args, policyState, stream, *logger);
0890 main.initialize(gctx, {Vector3{1, 1, 1}, Vector3{1, 1, 1}}, *logger);
0891 BOOST_CHECK_EQUAL(main.candidates().size(), 10);
0892 }
0893
0894 BOOST_AUTO_TEST_SUITE_END()
0895
0896 }