Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:12:40

0001 // This file is part of the ACTS project.
0002 //
0003 // Copyright (C) 2016 CERN for the benefit of the ACTS project
0004 //
0005 // This Source Code Form is subject to the terms of the Mozilla Public
0006 // License, v. 2.0. If a copy of the MPL was not distributed with this
0007 // file, You can obtain one at https://mozilla.org/MPL/2.0/.
0008 
0009 #include <boost/test/tools/context.hpp>
0010 #include <boost/test/tools/old/interface.hpp>
0011 #include <boost/test/unit_test.hpp>
0012 #include <boost/test/unit_test_suite.hpp>
0013 
0014 #include "Acts/Definitions/Units.hpp"
0015 #include "Acts/Geometry/CuboidVolumeBounds.hpp"
0016 #include "Acts/Geometry/CutoutCylinderVolumeBounds.hpp"
0017 #include "Acts/Geometry/CylinderVolumeBounds.hpp"
0018 #include "Acts/Geometry/GridPortalLink.hpp"
0019 #include "Acts/Geometry/Portal.hpp"
0020 #include "Acts/Geometry/PortalShell.hpp"
0021 #include "Acts/Geometry/TrackingVolume.hpp"
0022 #include "Acts/Geometry/TrivialPortalLink.hpp"
0023 #include "Acts/Surfaces/SurfaceMergingException.hpp"
0024 
0025 #include <stdexcept>
0026 
0027 using namespace Acts::UnitLiterals;
0028 
0029 namespace Acts::Test {
0030 GeometryContext gctx;
0031 
0032 std::size_t getVolumeIndex() {
0033   static std::size_t i = 1;
0034   return i++;
0035 }
0036 
0037 auto makeVolume(auto&&... pars) {
0038   TrackingVolume vol(Transform3::Identity(),
0039                      std::make_shared<CylinderVolumeBounds>(
0040                          std::forward<decltype(pars)>(pars)...));
0041   vol.setVolumeName("cyl" + std::to_string(getVolumeIndex()));
0042   return vol;
0043 };
0044 
0045 auto logger = Acts::getDefaultLogger("UnitTests", Acts::Logging::VERBOSE);
0046 
0047 BOOST_AUTO_TEST_SUITE(PortalShellTests)
0048 
0049 BOOST_AUTO_TEST_CASE(ConstructionFromVolume) {
0050   // - Cylinder
0051   // |           | no phi | phi |
0052   // | --------- | ------ | --- |
0053   // | rMin > 0  | 1      | 3   |
0054   // | rMin == 0 | 2      | 4   |
0055 
0056   auto cyl1 = makeVolume(30_mm, 40_mm, 100_mm);
0057   auto cyl2 = makeVolume(0_mm, 40_mm, 100_mm);
0058   auto cyl3 = makeVolume(30_mm, 40_mm, 100_mm, 45_degree);
0059   auto cyl4 = makeVolume(0_mm, 40_mm, 100_mm, 45_degree);
0060 
0061   TrackingVolume boxVolume(
0062       Transform3::Identity(),
0063       std::make_shared<CuboidVolumeBounds>(10_mm, 10_mm, 10_mm));
0064 
0065   BOOST_CHECK_THROW(SingleCylinderPortalShell{boxVolume},
0066                     std::invalid_argument);
0067 
0068   SingleCylinderPortalShell shell1{cyl1};
0069   BOOST_CHECK_EQUAL(shell1.size(), 4);
0070 
0071   using enum CylinderVolumeBounds::Face;
0072 
0073   const auto* pDisc = shell1.portal(PositiveDisc);
0074   BOOST_REQUIRE_NE(pDisc, nullptr);
0075   BOOST_CHECK_EQUAL(
0076       pDisc
0077           ->resolveVolume(gctx, Vector3{35_mm, 0_mm, 100_mm}, -Vector3::UnitZ())
0078           .value(),
0079       &cyl1);
0080   BOOST_CHECK_EQUAL(
0081       pDisc->resolveVolume(gctx, Vector3{35_mm, 0_mm, 100_mm}, Vector3::UnitZ())
0082           .value(),
0083       nullptr);
0084 
0085   const auto* nDisc = shell1.portal(NegativeDisc);
0086   BOOST_REQUIRE_NE(nDisc, nullptr);
0087   BOOST_CHECK_EQUAL(nDisc
0088                         ->resolveVolume(gctx, Vector3{35_mm, 0_mm, -100_mm},
0089                                         -Vector3::UnitZ())
0090                         .value(),
0091                     nullptr);
0092   BOOST_CHECK_EQUAL(
0093       nDisc
0094           ->resolveVolume(gctx, Vector3{35_mm, 0_mm, -100_mm}, Vector3::UnitZ())
0095           .value(),
0096       &cyl1);
0097 
0098   const auto* oCyl = shell1.portal(OuterCylinder);
0099   BOOST_REQUIRE_NE(oCyl, nullptr);
0100   BOOST_CHECK_EQUAL(
0101       oCyl->resolveVolume(gctx, Vector3{40_mm, 0_mm, 10_mm}, Vector3::UnitX())
0102           .value(),
0103       nullptr);
0104   BOOST_CHECK_EQUAL(
0105       oCyl->resolveVolume(gctx, Vector3{40_mm, 0_mm, 10_mm}, -Vector3::UnitX())
0106           .value(),
0107       &cyl1);
0108 
0109   const auto* iCyl = shell1.portal(InnerCylinder);
0110   BOOST_REQUIRE_NE(iCyl, nullptr);
0111   BOOST_CHECK_EQUAL(
0112       iCyl->resolveVolume(gctx, Vector3{30_mm, 0_mm, 10_mm}, Vector3::UnitX())
0113           .value(),
0114       &cyl1);
0115   BOOST_CHECK_EQUAL(
0116       iCyl->resolveVolume(gctx, Vector3{30_mm, 0_mm, 10_mm}, -Vector3::UnitX())
0117           .value(),
0118       nullptr);
0119 
0120   SingleCylinderPortalShell shell2{cyl2};
0121   BOOST_CHECK_EQUAL(shell2.size(), 3);
0122 
0123   pDisc = shell2.portal(PositiveDisc);
0124   BOOST_REQUIRE_NE(pDisc, nullptr);
0125   BOOST_CHECK_EQUAL(
0126       pDisc
0127           ->resolveVolume(gctx, Vector3{35_mm, 0_mm, 100_mm}, -Vector3::UnitZ())
0128           .value(),
0129       &cyl2);
0130   BOOST_CHECK_EQUAL(
0131       pDisc->resolveVolume(gctx, Vector3{35_mm, 0_mm, 100_mm}, Vector3::UnitZ())
0132           .value(),
0133       nullptr);
0134 
0135   nDisc = shell2.portal(NegativeDisc);
0136   BOOST_REQUIRE_NE(nDisc, nullptr);
0137   BOOST_CHECK_EQUAL(nDisc
0138                         ->resolveVolume(gctx, Vector3{35_mm, 0_mm, -100_mm},
0139                                         -Vector3::UnitZ())
0140                         .value(),
0141                     nullptr);
0142   BOOST_CHECK_EQUAL(
0143       nDisc
0144           ->resolveVolume(gctx, Vector3{35_mm, 0_mm, -100_mm}, Vector3::UnitZ())
0145           .value(),
0146       &cyl2);
0147 
0148   oCyl = shell2.portal(OuterCylinder);
0149   BOOST_REQUIRE_NE(oCyl, nullptr);
0150   BOOST_CHECK_EQUAL(
0151       oCyl->resolveVolume(gctx, Vector3{40_mm, 0_mm, 10_mm}, Vector3::UnitX())
0152           .value(),
0153       nullptr);
0154   BOOST_CHECK_EQUAL(
0155       oCyl->resolveVolume(gctx, Vector3{40_mm, 0_mm, 10_mm}, -Vector3::UnitX())
0156           .value(),
0157       &cyl2);
0158 
0159   iCyl = shell2.portal(InnerCylinder);
0160   BOOST_CHECK_EQUAL(iCyl, nullptr);
0161 
0162   SingleCylinderPortalShell shell3{cyl3};
0163   BOOST_CHECK_EQUAL(shell3.size(), 6);
0164 
0165   pDisc = shell3.portal(PositiveDisc);
0166   BOOST_REQUIRE_NE(pDisc, nullptr);
0167   BOOST_CHECK_EQUAL(
0168       pDisc
0169           ->resolveVolume(gctx, Vector3{35_mm, 0_mm, 100_mm}, -Vector3::UnitZ())
0170           .value(),
0171       &cyl3);
0172   BOOST_CHECK_EQUAL(
0173       pDisc->resolveVolume(gctx, Vector3{35_mm, 0_mm, 100_mm}, Vector3::UnitZ())
0174           .value(),
0175       nullptr);
0176 
0177   nDisc = shell3.portal(NegativeDisc);
0178   BOOST_REQUIRE_NE(nDisc, nullptr);
0179   BOOST_CHECK_EQUAL(nDisc
0180                         ->resolveVolume(gctx, Vector3{35_mm, 0_mm, -100_mm},
0181                                         -Vector3::UnitZ())
0182                         .value(),
0183                     nullptr);
0184   BOOST_CHECK_EQUAL(
0185       nDisc
0186           ->resolveVolume(gctx, Vector3{35_mm, 0_mm, -100_mm}, Vector3::UnitZ())
0187           .value(),
0188       &cyl3);
0189 
0190   oCyl = shell3.portal(OuterCylinder);
0191   BOOST_REQUIRE_NE(oCyl, nullptr);
0192   BOOST_CHECK_EQUAL(
0193       oCyl->resolveVolume(gctx, Vector3{40_mm, 0_mm, 10_mm}, Vector3::UnitX())
0194           .value(),
0195       nullptr);
0196   BOOST_CHECK_EQUAL(
0197       oCyl->resolveVolume(gctx, Vector3{40_mm, 0_mm, 10_mm}, -Vector3::UnitX())
0198           .value(),
0199       &cyl3);
0200 
0201   iCyl = shell3.portal(InnerCylinder);
0202   BOOST_REQUIRE_NE(iCyl, nullptr);
0203   BOOST_CHECK_EQUAL(
0204       iCyl->resolveVolume(gctx, Vector3{30_mm, 0_mm, 10_mm}, Vector3::UnitX())
0205           .value(),
0206       &cyl3);
0207   BOOST_CHECK_EQUAL(
0208       iCyl->resolveVolume(gctx, Vector3{30_mm, 0_mm, 10_mm}, -Vector3::UnitX())
0209           .value(),
0210       nullptr);
0211 
0212   auto anglePoint = [](double angle, double r, double z) {
0213     return Vector3{r * std::cos(angle), r * std::sin(angle), z};
0214   };
0215 
0216   const auto* nPhi = shell3.portal(NegativePhiPlane);
0217   BOOST_REQUIRE_NE(nPhi, nullptr);
0218   Vector3 point = anglePoint(-45_degree, 35_mm, 10_mm);
0219   Vector3 dir = Vector3::UnitZ().cross(point).normalized();
0220   Vector3 idir = (-Vector3::UnitZ()).cross(point).normalized();
0221   BOOST_CHECK_EQUAL(nPhi->resolveVolume(gctx, point, dir).value(), nullptr);
0222   BOOST_CHECK_EQUAL(nPhi->resolveVolume(gctx, point, idir).value(), &cyl3);
0223 
0224   const auto* pPhi = shell3.portal(PositivePhiPlane);
0225   BOOST_REQUIRE_NE(pPhi, nullptr);
0226   point = anglePoint(45_degree, 35_mm, 10_mm);
0227   dir = Vector3::UnitZ().cross(point).normalized();
0228   idir = (-Vector3::UnitZ()).cross(point).normalized();
0229   BOOST_CHECK_EQUAL(pPhi->resolveVolume(gctx, point, dir).value(), nullptr);
0230   BOOST_CHECK_EQUAL(pPhi->resolveVolume(gctx, point, idir).value(), &cyl3);
0231 
0232   SingleCylinderPortalShell shell4{cyl4};
0233   BOOST_CHECK_EQUAL(shell4.size(), 5);
0234 
0235   pDisc = shell4.portal(PositiveDisc);
0236   BOOST_REQUIRE_NE(pDisc, nullptr);
0237   BOOST_CHECK_EQUAL(
0238       pDisc
0239           ->resolveVolume(gctx, Vector3{35_mm, 0_mm, 100_mm}, -Vector3::UnitZ())
0240           .value(),
0241       &cyl4);
0242   BOOST_CHECK_EQUAL(
0243       pDisc->resolveVolume(gctx, Vector3{35_mm, 0_mm, 100_mm}, Vector3::UnitZ())
0244           .value(),
0245       nullptr);
0246 
0247   nDisc = shell4.portal(NegativeDisc);
0248   BOOST_REQUIRE_NE(nDisc, nullptr);
0249   BOOST_CHECK_EQUAL(nDisc
0250                         ->resolveVolume(gctx, Vector3{35_mm, 0_mm, -100_mm},
0251                                         -Vector3::UnitZ())
0252                         .value(),
0253                     nullptr);
0254   BOOST_CHECK_EQUAL(
0255       nDisc
0256           ->resolveVolume(gctx, Vector3{35_mm, 0_mm, -100_mm}, Vector3::UnitZ())
0257           .value(),
0258       &cyl4);
0259 
0260   oCyl = shell4.portal(OuterCylinder);
0261   BOOST_REQUIRE_NE(oCyl, nullptr);
0262   BOOST_CHECK_EQUAL(
0263       oCyl->resolveVolume(gctx, Vector3{40_mm, 0_mm, 10_mm}, Vector3::UnitX())
0264           .value(),
0265       nullptr);
0266   BOOST_CHECK_EQUAL(
0267       oCyl->resolveVolume(gctx, Vector3{40_mm, 0_mm, 10_mm}, -Vector3::UnitX())
0268           .value(),
0269       &cyl4);
0270 
0271   iCyl = shell4.portal(InnerCylinder);
0272   BOOST_CHECK_EQUAL(iCyl, nullptr);
0273 
0274   nPhi = shell4.portal(NegativePhiPlane);
0275   BOOST_REQUIRE_NE(nPhi, nullptr);
0276   point = anglePoint(-45_degree, 35_mm, 10_mm);
0277   dir = Vector3::UnitZ().cross(point).normalized();
0278   idir = (-Vector3::UnitZ()).cross(point).normalized();
0279   BOOST_CHECK_EQUAL(nPhi->resolveVolume(gctx, point, dir).value(), nullptr);
0280   BOOST_CHECK_EQUAL(nPhi->resolveVolume(gctx, point, idir).value(), &cyl4);
0281 
0282   pPhi = shell4.portal(PositivePhiPlane);
0283   BOOST_REQUIRE_NE(pPhi, nullptr);
0284   point = anglePoint(45_degree, 35_mm, 10_mm);
0285   dir = Vector3::UnitZ().cross(point).normalized();
0286   idir = (-Vector3::UnitZ()).cross(point).normalized();
0287   BOOST_CHECK_EQUAL(pPhi->resolveVolume(gctx, point, dir).value(), nullptr);
0288   BOOST_CHECK_EQUAL(pPhi->resolveVolume(gctx, point, idir).value(), &cyl4);
0289 }
0290 
0291 //              outer cylinder
0292 //          +----------+----------+
0293 //          |          |          |
0294 // negative |          v          | positive
0295 //     disc +-->               <--+ disc
0296 //          |          ^          |
0297 //          |          |          |
0298 //          +----------+----------+
0299 //              inner cylinder
0300 
0301 BOOST_AUTO_TEST_CASE(PortalAssignment) {
0302   using enum CylinderVolumeBounds::Face;
0303   TrackingVolume vol(
0304       Transform3::Identity(),
0305       std::make_shared<CylinderVolumeBounds>(30_mm, 100_mm, 100_mm));
0306 
0307   SingleCylinderPortalShell shell{vol};
0308 
0309   const auto* iCyl = shell.portal(InnerCylinder);
0310   const auto* pDisc = shell.portal(PositiveDisc);
0311   auto* oCyl = shell.portal(OuterCylinder);
0312   auto* nDisc = shell.portal(NegativeDisc);
0313 
0314   // Setting new outer cylinder
0315   BOOST_REQUIRE_NE(oCyl, nullptr);
0316   auto* oCylLink = dynamic_cast<const TrivialPortalLink*>(
0317       oCyl->getLink(Direction::OppositeNormal()));
0318   BOOST_REQUIRE_NE(oCylLink, nullptr);
0319 
0320   auto grid = oCylLink->makeGrid(AxisDirection::AxisZ);
0321 
0322   auto portal2 =
0323       std::make_shared<Portal>(Direction::OppositeNormal(), std::move(grid));
0324   shell.setPortal(portal2, OuterCylinder);
0325   BOOST_CHECK_EQUAL(shell.portal(OuterCylinder), portal2.get());
0326 
0327   // Other portals should stay the same
0328   BOOST_CHECK_EQUAL(shell.portal(InnerCylinder), iCyl);
0329   BOOST_CHECK_EQUAL(shell.portal(PositiveDisc), pDisc);
0330   BOOST_CHECK_EQUAL(shell.portal(NegativeDisc), nDisc);
0331 
0332   // Setting new negative disc
0333   BOOST_REQUIRE_NE(nDisc, nullptr);
0334   auto* nDiscLink = dynamic_cast<const TrivialPortalLink*>(
0335       nDisc->getLink(Direction::AlongNormal()));
0336   BOOST_REQUIRE_NE(nDiscLink, nullptr);
0337 
0338   grid = nDiscLink->makeGrid(AxisDirection::AxisR);
0339 
0340   auto portal3 =
0341       std::make_shared<Portal>(Direction::AlongNormal(), std::move(grid));
0342   shell.setPortal(portal3, NegativeDisc);
0343   BOOST_CHECK_EQUAL(shell.portal(NegativeDisc), portal3.get());
0344 
0345   // Other portals should stay the same
0346   BOOST_CHECK_EQUAL(shell.portal(OuterCylinder), portal2.get());
0347   BOOST_CHECK_EQUAL(shell.portal(InnerCylinder), iCyl);
0348   BOOST_CHECK_EQUAL(shell.portal(PositiveDisc), pDisc);
0349 }
0350 
0351 BOOST_AUTO_TEST_SUITE(CylinderStack)
0352 BOOST_AUTO_TEST_CASE(ZDirection) {
0353   using enum CylinderVolumeBounds::Face;
0354   BOOST_TEST_CONTEXT("rMin>0") {
0355     TrackingVolume vol1(
0356         Transform3{Translation3{Vector3::UnitZ() * -100_mm}},
0357         std::make_shared<CylinderVolumeBounds>(30_mm, 100_mm, 100_mm));
0358 
0359     TrackingVolume vol2(
0360         Transform3{Translation3{Vector3::UnitZ() * 100_mm}},
0361         std::make_shared<CylinderVolumeBounds>(30_mm, 100_mm, 100_mm));
0362 
0363     SingleCylinderPortalShell shell1{vol1};
0364     SingleCylinderPortalShell shell2{vol2};
0365 
0366     BOOST_CHECK_NE(shell1.portal(PositiveDisc), shell2.portal(NegativeDisc));
0367 
0368     CylinderStackPortalShell stack{
0369         gctx, {&shell1, &shell2}, AxisDirection::AxisZ};
0370     BOOST_CHECK_EQUAL(stack.size(), 4);
0371 
0372     const auto* iCyl = stack.portal(InnerCylinder);
0373     BOOST_CHECK_EQUAL(shell1.portal(InnerCylinder), iCyl);
0374     BOOST_CHECK_EQUAL(shell2.portal(InnerCylinder), iCyl);
0375 
0376     const auto* oCyl = stack.portal(OuterCylinder);
0377     BOOST_CHECK_EQUAL(shell1.portal(OuterCylinder), oCyl);
0378     BOOST_CHECK_EQUAL(shell2.portal(OuterCylinder), oCyl);
0379 
0380     BOOST_CHECK_EQUAL(stack.portal(PositiveDisc), shell2.portal(PositiveDisc));
0381     BOOST_CHECK_EQUAL(stack.portal(NegativeDisc), shell1.portal(NegativeDisc));
0382 
0383     BOOST_CHECK_EQUAL(stack.portal(NegativePhiPlane), nullptr);
0384     BOOST_CHECK_EQUAL(stack.portalPtr(NegativePhiPlane), nullptr);
0385 
0386     BOOST_CHECK_EQUAL(stack.portal(PositivePhiPlane), nullptr);
0387     BOOST_CHECK_EQUAL(stack.portalPtr(PositivePhiPlane), nullptr);
0388 
0389     // Disc portals have been fused
0390     BOOST_CHECK_EQUAL(shell1.portal(PositiveDisc), shell2.portal(NegativeDisc));
0391 
0392     shell1 = SingleCylinderPortalShell{vol1};
0393     shell2 = SingleCylinderPortalShell{vol2};
0394 
0395     BOOST_CHECK_THROW(CylinderStackPortalShell(gctx, {&shell1, &shell2},
0396                                                AxisDirection::AxisR),
0397                       SurfaceMergingException);
0398   }
0399 
0400   BOOST_TEST_CONTEXT("rMin==0") {
0401     TrackingVolume vol1(
0402         Transform3{Translation3{Vector3::UnitZ() * -100_mm}},
0403         std::make_shared<CylinderVolumeBounds>(0_mm, 100_mm, 100_mm));
0404 
0405     TrackingVolume vol2(
0406         Transform3{Translation3{Vector3::UnitZ() * 100_mm}},
0407         std::make_shared<CylinderVolumeBounds>(0_mm, 100_mm, 100_mm));
0408 
0409     SingleCylinderPortalShell shell1{vol1};
0410     SingleCylinderPortalShell shell2{vol2};
0411 
0412     BOOST_CHECK_EQUAL(shell1.portal(InnerCylinder), nullptr);
0413     BOOST_CHECK_EQUAL(shell2.portal(InnerCylinder), nullptr);
0414 
0415     BOOST_CHECK_NE(shell1.portal(PositiveDisc), shell2.portal(NegativeDisc));
0416 
0417     CylinderStackPortalShell stack{
0418         gctx, {&shell1, &shell2}, AxisDirection::AxisZ};
0419     BOOST_CHECK_EQUAL(stack.size(), 3);
0420 
0421     // Disc portals have been fused
0422     BOOST_CHECK_EQUAL(shell1.portal(PositiveDisc), shell2.portal(NegativeDisc));
0423 
0424     const auto* iCyl = stack.portal(InnerCylinder);
0425     BOOST_CHECK_EQUAL(iCyl, nullptr);
0426 
0427     const auto* oCyl = stack.portal(OuterCylinder);
0428     BOOST_CHECK_EQUAL(shell1.portal(OuterCylinder), oCyl);
0429     BOOST_CHECK_EQUAL(shell2.portal(OuterCylinder), oCyl);
0430 
0431     BOOST_CHECK_EQUAL(stack.portal(PositiveDisc), shell2.portal(PositiveDisc));
0432     BOOST_CHECK_EQUAL(stack.portal(NegativeDisc), shell1.portal(NegativeDisc));
0433 
0434     BOOST_CHECK_EQUAL(stack.portal(NegativePhiPlane), nullptr);
0435     BOOST_CHECK_EQUAL(stack.portalPtr(NegativePhiPlane), nullptr);
0436 
0437     BOOST_CHECK_EQUAL(stack.portal(PositivePhiPlane), nullptr);
0438     BOOST_CHECK_EQUAL(stack.portalPtr(PositivePhiPlane), nullptr);
0439 
0440     shell1 = SingleCylinderPortalShell{vol1};
0441     shell2 = SingleCylinderPortalShell{vol2};
0442 
0443     BOOST_CHECK_THROW(CylinderStackPortalShell(gctx, {&shell1, &shell2},
0444                                                AxisDirection::AxisR),
0445                       SurfaceMergingException);
0446   }
0447 }
0448 
0449 BOOST_AUTO_TEST_CASE(RDirection) {
0450   using enum CylinderVolumeBounds::Face;
0451   BOOST_TEST_CONTEXT("rMin>0") {
0452     TrackingVolume vol1(
0453         Transform3::Identity(),
0454         std::make_shared<CylinderVolumeBounds>(30_mm, 100_mm, 100_mm));
0455 
0456     TrackingVolume vol2(
0457         Transform3::Identity(),
0458         std::make_shared<CylinderVolumeBounds>(100_mm, 150_mm, 100_mm));
0459 
0460     SingleCylinderPortalShell shell1{vol1};
0461     SingleCylinderPortalShell shell2{vol2};
0462 
0463     BOOST_CHECK_NE(shell1.portal(OuterCylinder), shell2.portal(InnerCylinder));
0464 
0465     CylinderStackPortalShell stack{
0466         gctx, {&shell1, &shell2}, AxisDirection::AxisR};
0467     BOOST_CHECK_EQUAL(stack.size(), 4);
0468 
0469     // Internal cylinder portals have been fused
0470     BOOST_CHECK_EQUAL(shell1.portal(OuterCylinder),
0471                       shell2.portal(InnerCylinder));
0472 
0473     const auto* nDisc = stack.portal(NegativeDisc);
0474     const auto* pDisc = stack.portal(PositiveDisc);
0475 
0476     BOOST_CHECK_EQUAL(shell1.portal(NegativeDisc), nDisc);
0477     BOOST_CHECK_EQUAL(shell2.portal(NegativeDisc), nDisc);
0478     BOOST_CHECK_EQUAL(shell1.portal(PositiveDisc), pDisc);
0479     BOOST_CHECK_EQUAL(shell2.portal(PositiveDisc), pDisc);
0480 
0481     BOOST_CHECK_EQUAL(stack.portal(InnerCylinder),
0482                       shell1.portal(InnerCylinder));
0483     BOOST_CHECK_EQUAL(stack.portal(OuterCylinder),
0484                       shell2.portal(OuterCylinder));
0485 
0486     BOOST_CHECK_EQUAL(stack.portal(NegativePhiPlane), nullptr);
0487     BOOST_CHECK_EQUAL(stack.portalPtr(NegativePhiPlane), nullptr);
0488 
0489     BOOST_CHECK_EQUAL(stack.portal(PositivePhiPlane), nullptr);
0490     BOOST_CHECK_EQUAL(stack.portalPtr(PositivePhiPlane), nullptr);
0491 
0492     shell1 = SingleCylinderPortalShell{vol1};
0493     shell2 = SingleCylinderPortalShell{vol2};
0494 
0495     BOOST_CHECK_THROW(CylinderStackPortalShell(gctx, {&shell1, &shell2},
0496                                                AxisDirection::AxisZ),
0497                       SurfaceMergingException);
0498   }
0499 
0500   BOOST_TEST_CONTEXT("rMin==0") {
0501     TrackingVolume vol1(
0502         Transform3::Identity(),
0503         std::make_shared<CylinderVolumeBounds>(0_mm, 100_mm, 100_mm));
0504 
0505     TrackingVolume vol2(
0506         Transform3::Identity(),
0507         std::make_shared<CylinderVolumeBounds>(100_mm, 150_mm, 100_mm));
0508 
0509     SingleCylinderPortalShell shell1{vol1};
0510     SingleCylinderPortalShell shell2{vol2};
0511 
0512     BOOST_CHECK_EQUAL(shell1.portal(InnerCylinder), nullptr);
0513     BOOST_CHECK_NE(shell1.portal(OuterCylinder), shell2.portal(InnerCylinder));
0514 
0515     CylinderStackPortalShell stack{
0516         gctx, {&shell1, &shell2}, AxisDirection::AxisR};
0517     BOOST_CHECK_EQUAL(stack.size(), 4);
0518 
0519     // Internal cylinder portals have been fused
0520     BOOST_CHECK_EQUAL(shell1.portal(OuterCylinder),
0521                       shell2.portal(InnerCylinder));
0522 
0523     const auto* nDisc = stack.portal(NegativeDisc);
0524     const auto* pDisc = stack.portal(PositiveDisc);
0525 
0526     BOOST_CHECK_EQUAL(shell1.portal(NegativeDisc), nDisc);
0527     BOOST_CHECK_EQUAL(shell2.portal(NegativeDisc), nDisc);
0528     BOOST_CHECK_EQUAL(shell1.portal(PositiveDisc), pDisc);
0529     BOOST_CHECK_EQUAL(shell2.portal(PositiveDisc), pDisc);
0530 
0531     BOOST_CHECK_EQUAL(stack.portal(InnerCylinder), nullptr);
0532     BOOST_CHECK_EQUAL(stack.portal(OuterCylinder),
0533                       shell2.portal(OuterCylinder));
0534 
0535     BOOST_CHECK_EQUAL(stack.portal(NegativePhiPlane), nullptr);
0536     BOOST_CHECK_EQUAL(stack.portalPtr(NegativePhiPlane), nullptr);
0537 
0538     BOOST_CHECK_EQUAL(stack.portal(PositivePhiPlane), nullptr);
0539     BOOST_CHECK_EQUAL(stack.portalPtr(PositivePhiPlane), nullptr);
0540 
0541     shell1 = SingleCylinderPortalShell{vol1};
0542     shell2 = SingleCylinderPortalShell{vol2};
0543 
0544     BOOST_CHECK_THROW(CylinderStackPortalShell(gctx, {&shell1, &shell2},
0545                                                AxisDirection::AxisZ),
0546                       std::invalid_argument);
0547   }
0548 }
0549 
0550 BOOST_AUTO_TEST_CASE(NestedStacks) {
0551   //   ^
0552   // r |    +---------------------------------+---------+
0553   //   |    |                                 |         |
0554   //   |    |                                 |         |
0555   //   |    |              vol2               |         |
0556   //   |    |                                 |         |
0557   //   |    |                                 |         |
0558   //   |    +---------------------------------+         |
0559   //   |    |                                 |         |
0560   //   |    |                                 |         |
0561   //   |    |               gap               |  vol3   |
0562   //   |    |                                 |         |
0563   //   |    |                                 |         |
0564   //   |    +---------------------------------+         |
0565   //   |    |                                 |         |
0566   //   |    |                                 |         |
0567   //   |    |              vol1               |         |
0568   //   |    |                                 |         |
0569   //   |    |                                 |         |
0570   //   |    +---------------------------------+---------+
0571   //   |
0572   //   +-------------------------------------------------->
0573   //                                                      z
0574 
0575   Transform3 base = Transform3::Identity();
0576 
0577   TrackingVolume vol1(
0578       base, std::make_shared<CylinderVolumeBounds>(23_mm, 48_mm, 200_mm),
0579       "PixelLayer0");
0580 
0581   TrackingVolume gap(
0582       base, std::make_shared<CylinderVolumeBounds>(48_mm, 250_mm, 200_mm),
0583       "Gap");
0584 
0585   TrackingVolume vol2(
0586       base, std::make_shared<CylinderVolumeBounds>(250_mm, 400_mm, 200_mm),
0587       "PixelLayer3");
0588 
0589   TrackingVolume vol3(
0590       base * Translation3{Vector3::UnitZ() * 300_mm},
0591       std::make_shared<CylinderVolumeBounds>(23_mm, 400_mm, 100_mm),
0592       "PixelEcPos");
0593 
0594   SingleCylinderPortalShell shell1{vol1};
0595   BOOST_CHECK(shell1.isValid());
0596   SingleCylinderPortalShell gapShell{gap};
0597   BOOST_CHECK(gapShell.isValid());
0598   SingleCylinderPortalShell shell2{vol2};
0599   BOOST_CHECK(shell2.isValid());
0600 
0601   CylinderStackPortalShell stack{
0602       gctx, {&shell1, &gapShell, &shell2}, AxisDirection::AxisR};
0603 
0604   BOOST_CHECK(stack.isValid());
0605 
0606   SingleCylinderPortalShell shell3{vol3};
0607   BOOST_CHECK(shell3.isValid());
0608 
0609   CylinderStackPortalShell stack2{
0610       gctx, {&stack, &shell3}, AxisDirection::AxisZ, *logger};
0611   BOOST_CHECK(stack2.isValid());
0612 
0613   using enum CylinderVolumeBounds::Face;
0614 
0615   auto lookup = [](auto& shell, CylinderPortalShell::Face face,
0616                    Vector3 position,
0617                    Vector3 direction) -> const TrackingVolume* {
0618     const auto* portal = shell.portal(face);
0619     BOOST_REQUIRE_NE(portal, nullptr);
0620     return portal->resolveVolume(gctx, position, direction).value();
0621   };
0622 
0623   // Interconnection in the r direction
0624 
0625   BOOST_CHECK_EQUAL(lookup(shell1, InnerCylinder, 23_mm * Vector3::UnitX(),
0626                            -Vector3::UnitX()),
0627                     nullptr);
0628   BOOST_CHECK_EQUAL(
0629       lookup(shell1, InnerCylinder, 23_mm * Vector3::UnitX(), Vector3::UnitX()),
0630       &vol1);
0631 
0632   BOOST_CHECK_EQUAL(
0633       lookup(shell1, OuterCylinder, 48_mm * Vector3::UnitX(), Vector3::UnitX()),
0634       &gap);
0635 
0636   BOOST_CHECK_EQUAL(lookup(gapShell, InnerCylinder, 48_mm * Vector3::UnitX(),
0637                            -Vector3::UnitX()),
0638                     &vol1);
0639 
0640   BOOST_CHECK_EQUAL(lookup(gapShell, OuterCylinder, 250_mm * Vector3::UnitX(),
0641                            Vector3::UnitX()),
0642                     &vol2);
0643 
0644   BOOST_CHECK_EQUAL(lookup(shell2, InnerCylinder, 250_mm * Vector3::UnitX(),
0645                            -Vector3::UnitX()),
0646                     &gap);
0647 
0648   BOOST_CHECK_EQUAL(lookup(shell2, OuterCylinder, 400_mm * Vector3::UnitX(),
0649                            Vector3::UnitX()),
0650                     nullptr);
0651 
0652   BOOST_CHECK_EQUAL(lookup(shell2, OuterCylinder, 400_mm * Vector3::UnitX(),
0653                            -Vector3::UnitX()),
0654                     &vol2);
0655 
0656   BOOST_CHECK_EQUAL(lookup(shell2, OuterCylinder, 400_mm * Vector3::UnitX(),
0657                            -Vector3::UnitX()),
0658                     &vol2);
0659 
0660   // Left connection
0661 
0662   BOOST_CHECK_EQUAL(lookup(shell1, NegativeDisc, Vector3(30_mm, 0, -200_mm),
0663                            -Vector3::UnitZ()),
0664                     nullptr);
0665 
0666   BOOST_CHECK_EQUAL(lookup(shell1, NegativeDisc, Vector3(30_mm, 0, -200_mm),
0667                            Vector3::UnitZ()),
0668                     &vol1);
0669 
0670   BOOST_CHECK_EQUAL(lookup(gapShell, NegativeDisc, Vector3(60_mm, 0, -200_mm),
0671                            -Vector3::UnitZ()),
0672                     nullptr);
0673 
0674   BOOST_CHECK_EQUAL(lookup(gapShell, NegativeDisc, Vector3(60_mm, 0, -200_mm),
0675                            Vector3::UnitZ()),
0676                     &gap);
0677 
0678   BOOST_CHECK_EQUAL(lookup(shell2, NegativeDisc, Vector3(300_mm, 0, -200_mm),
0679                            -Vector3::UnitZ()),
0680                     nullptr);
0681 
0682   BOOST_CHECK_EQUAL(lookup(shell2, NegativeDisc, Vector3(300_mm, 0, -200_mm),
0683                            Vector3::UnitZ()),
0684                     &vol2);
0685 
0686   // Right connection
0687 
0688   BOOST_CHECK_EQUAL(lookup(shell1, PositiveDisc, Vector3(30_mm, 0, 200_mm),
0689                            -Vector3::UnitZ()),
0690                     &vol1);
0691 
0692   BOOST_CHECK_EQUAL(
0693       lookup(shell1, PositiveDisc, Vector3(30_mm, 0, 200_mm), Vector3::UnitZ()),
0694       &vol3);
0695 
0696   BOOST_CHECK_EQUAL(lookup(gapShell, PositiveDisc, Vector3(60_mm, 0, 200_mm),
0697                            -Vector3::UnitZ()),
0698                     &gap);
0699 
0700   BOOST_CHECK_EQUAL(lookup(gapShell, PositiveDisc, Vector3(60_mm, 0, 200_mm),
0701                            Vector3::UnitZ()),
0702                     &vol3);
0703 
0704   BOOST_CHECK_EQUAL(lookup(shell2, PositiveDisc, Vector3(300_mm, 0, 200_mm),
0705                            -Vector3::UnitZ()),
0706                     &vol2);
0707 
0708   BOOST_CHECK_EQUAL(lookup(shell2, PositiveDisc, Vector3(300_mm, 0, 200_mm),
0709                            Vector3::UnitZ()),
0710                     &vol3);
0711 
0712   // Right outer connection
0713 
0714   BOOST_CHECK_EQUAL(lookup(shell3, PositiveDisc, Vector3(300_mm, 0, 400_mm),
0715                            -Vector3::UnitZ()),
0716                     &vol3);
0717 
0718   BOOST_CHECK_EQUAL(lookup(shell3, PositiveDisc, Vector3(300_mm, 0, 400_mm),
0719                            Vector3::UnitZ()),
0720                     nullptr);
0721 }
0722 
0723 BOOST_AUTO_TEST_CASE(Fill) {
0724   auto cyl1 = makeVolume(30_mm, 40_mm, 100_mm);
0725   auto cyl2 = makeVolume(0_mm, 50_mm, 110_mm);
0726 
0727   SingleCylinderPortalShell shell{cyl1};
0728 
0729   using enum CylinderVolumeBounds::Face;
0730   BOOST_CHECK_EQUAL(
0731       shell.portal(OuterCylinder)->getLink(Direction::AlongNormal()), nullptr);
0732   BOOST_CHECK_EQUAL(
0733       shell.portal(InnerCylinder)->getLink(Direction::OppositeNormal()),
0734       nullptr);
0735   BOOST_CHECK_EQUAL(
0736       shell.portal(PositiveDisc)->getLink(Direction::AlongNormal()), nullptr);
0737   BOOST_CHECK_EQUAL(
0738       shell.portal(NegativeDisc)->getLink(Direction::OppositeNormal()),
0739       nullptr);
0740 
0741   shell.fill(cyl2);
0742 
0743   BOOST_CHECK_NE(shell.portal(OuterCylinder)->getLink(Direction::AlongNormal()),
0744                  nullptr);
0745   BOOST_CHECK_NE(
0746       shell.portal(InnerCylinder)->getLink(Direction::OppositeNormal()),
0747       nullptr);
0748   BOOST_CHECK_NE(shell.portal(PositiveDisc)->getLink(Direction::AlongNormal()),
0749                  nullptr);
0750   BOOST_CHECK_NE(
0751       shell.portal(NegativeDisc)->getLink(Direction::OppositeNormal()),
0752       nullptr);
0753 }
0754 
0755 BOOST_AUTO_TEST_CASE(RegisterInto) {
0756   using enum CylinderVolumeBounds::Face;
0757   TrackingVolume vol1(
0758       Transform3::Identity(),
0759       std::make_shared<CylinderVolumeBounds>(0_mm, 100_mm, 100_mm));
0760 
0761   SingleCylinderPortalShell shell{vol1};
0762 
0763   BOOST_CHECK_EQUAL(vol1.portals().size(), 0);
0764 
0765   shell.applyToVolume();
0766   BOOST_CHECK_EQUAL(vol1.portals().size(), 3);
0767 }
0768 
0769 BOOST_AUTO_TEST_SUITE_END()  // CylinderStack
0770 BOOST_AUTO_TEST_SUITE_END()
0771 
0772 }  // namespace Acts::Test