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