Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-01-06 09:23:50

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