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