File indexing completed on 2026-10-19 08:35:44
0001
0002
0003
0004
0005
0006
0007
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/CompositePortalLink.hpp"
0016 #include "Acts/Geometry/CylinderVolumeBounds.hpp"
0017 #include "Acts/Geometry/GeometryContext.hpp"
0018 #include "Acts/Geometry/GridPortalLink.hpp"
0019 #include "Acts/Geometry/Portal.hpp"
0020 #include "Acts/Geometry/TrackingVolume.hpp"
0021 #include "Acts/Geometry/TrivialPortalLink.hpp"
0022 #include "Acts/Material/HomogeneousSurfaceMaterial.hpp"
0023 #include "Acts/Material/MergedMaterialMarker.hpp"
0024 #include "Acts/Material/ProtoSurfaceMaterial.hpp"
0025 #include "Acts/Surfaces/CylinderSurface.hpp"
0026 #include "Acts/Surfaces/DiscSurface.hpp"
0027 #include "Acts/Surfaces/RadialBounds.hpp"
0028 #include "Acts/Surfaces/Surface.hpp"
0029 #include "Acts/Surfaces/SurfaceMergingException.hpp"
0030 #include "Acts/Utilities/ThrowAssert.hpp"
0031
0032 #include <stdexcept>
0033
0034 using namespace Acts;
0035 using namespace Acts::UnitLiterals;
0036
0037 namespace ActsTests {
0038
0039 auto logger = getDefaultLogger("UnitTests", Logging::VERBOSE);
0040
0041 struct Fixture {
0042 Logging::Level m_level;
0043 Fixture() {
0044 m_level = Logging::getFailureThreshold();
0045 Logging::setFailureThreshold(Logging::FATAL);
0046 }
0047
0048 ~Fixture() { Logging::setFailureThreshold(m_level); }
0049 };
0050
0051 std::shared_ptr<TrackingVolume> makeDummyVolume() {
0052 return std::make_shared<TrackingVolume>(
0053 Transform3::Identity(),
0054 std::make_shared<CylinderVolumeBounds>(30_mm, 40_mm, 100_mm));
0055 }
0056
0057 auto gctx = GeometryContext::dangerouslyDefaultConstruct();
0058
0059 BOOST_FIXTURE_TEST_SUITE(GeometrySuite, Fixture)
0060
0061 BOOST_AUTO_TEST_SUITE(Portals)
0062 BOOST_AUTO_TEST_SUITE(Merging)
0063
0064 BOOST_AUTO_TEST_CASE(Cylinder) {
0065 auto vol1 = makeDummyVolume();
0066 vol1->setVolumeName("vol1");
0067 auto vol2 = makeDummyVolume();
0068 vol2->setVolumeName("vol2");
0069
0070 auto cyl1 = Surface::makeShared<CylinderSurface>(
0071 Transform3{Translation3{Vector3::UnitZ() * -100_mm}}, 50_mm, 100_mm);
0072
0073 auto cyl2 = Surface::makeShared<CylinderSurface>(
0074 Transform3{Translation3{Vector3::UnitZ() * 100_mm}}, 50_mm, 100_mm);
0075
0076 Portal portal1{Direction::AlongNormal(),
0077 std::make_unique<TrivialPortalLink>(cyl1, *vol1)};
0078 BOOST_CHECK(portal1.isValid());
0079
0080 BOOST_CHECK_EQUAL(
0081 portal1
0082 .resolveVolume(gctx, Vector3{50_mm, 0_mm, -100_mm}, Vector3::UnitX())
0083 .value(),
0084 vol1.get());
0085
0086 BOOST_CHECK_EQUAL(
0087 portal1
0088 .resolveVolume(gctx, Vector3{50_mm, 0_mm, -100_mm}, -Vector3::UnitX())
0089 .value(),
0090 nullptr);
0091
0092 Portal portal2{Direction::AlongNormal(), cyl2, *vol2};
0093 BOOST_CHECK(portal2.isValid());
0094
0095 BOOST_CHECK_EQUAL(
0096 portal2
0097 .resolveVolume(gctx, Vector3{50_mm, 0_mm, 100_mm}, -Vector3::UnitX())
0098 .value(),
0099 nullptr);
0100
0101 BOOST_CHECK_EQUAL(
0102 portal2
0103 .resolveVolume(gctx, Vector3{50_mm, 0_mm, 100_mm}, Vector3::UnitX())
0104 .value(),
0105 vol2.get());
0106
0107 Portal portal3{gctx, std::make_unique<TrivialPortalLink>(cyl2, *vol2),
0108 nullptr};
0109 BOOST_CHECK(portal3.isValid());
0110
0111 BOOST_CHECK_NE(portal3.getLink(Direction::AlongNormal()), nullptr);
0112 BOOST_CHECK_EQUAL(portal3.getLink(Direction::OppositeNormal()), nullptr);
0113
0114 Portal portal4{gctx, nullptr,
0115 std::make_unique<TrivialPortalLink>(cyl2, *vol2)};
0116 BOOST_CHECK(portal4.isValid());
0117
0118 BOOST_CHECK_EQUAL(portal4.getLink(Direction::AlongNormal()), nullptr);
0119 BOOST_CHECK_NE(portal4.getLink(Direction::OppositeNormal()), nullptr);
0120
0121
0122
0123
0124
0125
0126
0127
0128
0129
0130
0131 BOOST_CHECK_THROW(
0132 Portal::merge(gctx, portal1, portal4, AxisDirection::AxisZ, *logger),
0133 PortalMergingException);
0134
0135
0136
0137 BOOST_CHECK(portal1.isValid());
0138 BOOST_CHECK(portal2.isValid());
0139
0140 BOOST_CHECK_EQUAL(
0141 portal2.resolveVolume(gctx, Vector3{50_mm, 0_mm, 50_mm}, Vector3::UnitX())
0142 .value(),
0143 vol2.get());
0144
0145 BOOST_CHECK_EQUAL(
0146 portal2
0147 .resolveVolume(gctx, Vector3{50_mm, 0_mm, 50_mm}, -Vector3::UnitX())
0148 .value(),
0149 nullptr);
0150
0151
0152 BOOST_CHECK_THROW(
0153 Portal::merge(gctx, portal1, portal2, AxisDirection::AxisRPhi, *logger),
0154 SurfaceMergingException);
0155
0156
0157
0158 BOOST_CHECK(!portal1.isValid());
0159 BOOST_CHECK(!portal2.isValid());
0160
0161
0162
0163
0164
0165
0166
0167
0168
0169 portal1 = Portal{gctx, {.alongNormal = {cyl1, *vol1}}};
0170 portal2 = Portal{gctx, {.alongNormal = {cyl2, *vol2}}};
0171
0172 Portal merged12 =
0173 Portal::merge(gctx, portal1, portal2, AxisDirection::AxisZ, *logger);
0174 BOOST_CHECK_NE(merged12.getLink(Direction::AlongNormal()), nullptr);
0175 BOOST_CHECK_EQUAL(merged12.getLink(Direction::OppositeNormal()), nullptr);
0176
0177 auto composite12 = dynamic_cast<const CompositePortalLink*>(
0178 merged12.getLink(Direction::AlongNormal()));
0179 BOOST_REQUIRE_NE(composite12, nullptr);
0180
0181 BOOST_CHECK_EQUAL(
0182 merged12
0183 .resolveVolume(gctx, Vector3{50_mm, 0_mm, -50_mm}, Vector3::UnitX())
0184 .value(),
0185 vol1.get());
0186
0187 BOOST_CHECK_EQUAL(
0188 merged12
0189 .resolveVolume(gctx, Vector3{50_mm, 0_mm, 50_mm}, Vector3::UnitX())
0190 .value(),
0191 vol2.get());
0192
0193 BOOST_CHECK_EQUAL(
0194 merged12
0195 .resolveVolume(gctx, Vector3{50_mm, 0_mm, -50_mm}, -Vector3::UnitX())
0196 .value(),
0197 nullptr);
0198
0199 BOOST_CHECK_EQUAL(
0200 merged12
0201 .resolveVolume(gctx, Vector3{50_mm, 0_mm, 50_mm}, -Vector3::UnitX())
0202 .value(),
0203 nullptr);
0204
0205 portal1 = Portal{gctx, {.alongNormal = {cyl1, *vol1}}};
0206
0207
0208 BOOST_CHECK_THROW(
0209 Portal::merge(gctx, portal1, portal1, AxisDirection::AxisZ, *logger),
0210 PortalMergingException);
0211
0212
0213 portal1 = Portal{gctx, {.alongNormal = {cyl1, *vol1}}};
0214 portal2 = Portal{gctx, {.alongNormal = {cyl1, *vol2}}};
0215 BOOST_CHECK_THROW(
0216 Portal::merge(gctx, portal1, portal2, AxisDirection::AxisZ, *logger),
0217 AssertionFailureException);
0218
0219
0220 auto material = std::make_shared<HomogeneousSurfaceMaterial>(
0221 MaterialSlab::Nothing());
0222 cyl2->assignSurfaceMaterial(material);
0223 portal1 = Portal{gctx, {.alongNormal = {cyl1, *vol1}}};
0224 portal2 = Portal{gctx, {.alongNormal = {cyl2, *vol2}}};
0225 BOOST_CHECK_THROW(
0226 Portal::merge(gctx, portal1, portal2, AxisDirection::AxisZ, *logger),
0227 PortalMergingException);
0228 }
0229
0230 BOOST_AUTO_TEST_CASE(Disc) {
0231 auto vol1 = makeDummyVolume();
0232 vol1->setVolumeName("vol1");
0233 auto vol2 = makeDummyVolume();
0234 vol2->setVolumeName("vol2");
0235 auto vol3 = makeDummyVolume();
0236 vol3->setVolumeName("vol3");
0237 auto vol4 = makeDummyVolume();
0238 vol4->setVolumeName("vol4");
0239
0240 auto disc1 = Surface::makeShared<DiscSurface>(
0241 Transform3::Identity(), std::make_shared<RadialBounds>(50_mm, 100_mm));
0242
0243 auto disc2 = Surface::makeShared<DiscSurface>(
0244 Transform3::Identity(), std::make_shared<RadialBounds>(100_mm, 150_mm));
0245
0246 Portal portal1{
0247 gctx, {.alongNormal = {disc1, *vol1}, .oppositeNormal = {disc1, *vol2}}};
0248
0249 Portal portal2{
0250 gctx, {.alongNormal = {disc2, *vol3}, .oppositeNormal = {disc2, *vol4}}};
0251
0252 BOOST_CHECK(portal1.isValid());
0253 BOOST_CHECK(portal2.isValid());
0254
0255 BOOST_CHECK_EQUAL(
0256 portal1.resolveVolume(gctx, Vector3{55_mm, 0_mm, 0_mm}, Vector3::UnitZ())
0257 .value(),
0258 vol1.get());
0259 BOOST_CHECK_EQUAL(
0260 portal1.resolveVolume(gctx, Vector3{55_mm, 0_mm, 0_mm}, -Vector3::UnitZ())
0261 .value(),
0262 vol2.get());
0263
0264 BOOST_CHECK_EQUAL(
0265 portal2.resolveVolume(gctx, Vector3{105_mm, 0_mm, 0_mm}, Vector3::UnitZ())
0266 .value(),
0267 vol3.get());
0268 BOOST_CHECK_EQUAL(
0269 portal2
0270 .resolveVolume(gctx, Vector3{105_mm, 0_mm, 0_mm}, -Vector3::UnitZ())
0271 .value(),
0272 vol4.get());
0273
0274 BOOST_CHECK_THROW(
0275 Portal::merge(gctx, portal1, portal2, AxisDirection::AxisZ, *logger),
0276 AssertionFailureException);
0277
0278 BOOST_CHECK(portal1.isValid());
0279 BOOST_CHECK(portal2.isValid());
0280
0281 BOOST_CHECK_THROW(
0282 Portal::merge(gctx, portal1, portal2, AxisDirection::AxisPhi, *logger),
0283 SurfaceMergingException);
0284
0285
0286
0287 BOOST_CHECK(!portal1.isValid());
0288 BOOST_CHECK(!portal2.isValid());
0289
0290
0291 portal1 = Portal{
0292 gctx, {.alongNormal = {disc1, *vol1}, .oppositeNormal = {disc1, *vol2}}};
0293
0294 portal2 = Portal{
0295 gctx, {.alongNormal = {disc2, *vol3}, .oppositeNormal = {disc2, *vol4}}};
0296
0297
0298
0299
0300
0301
0302
0303
0304
0305
0306 Portal merged12 =
0307 Portal::merge(gctx, portal1, portal2, AxisDirection::AxisR, *logger);
0308
0309 BOOST_CHECK_EQUAL(
0310 merged12.resolveVolume(gctx, Vector3{55_mm, 0_mm, 0_mm}, Vector3::UnitZ())
0311 .value(),
0312 vol1.get());
0313 BOOST_CHECK_EQUAL(
0314 merged12
0315 .resolveVolume(gctx, Vector3{55_mm, 0_mm, 0_mm}, -Vector3::UnitZ())
0316 .value(),
0317 vol2.get());
0318
0319 BOOST_CHECK_EQUAL(
0320 merged12
0321 .resolveVolume(gctx, Vector3{105_mm, 0_mm, 0_mm}, Vector3::UnitZ())
0322 .value(),
0323 vol3.get());
0324 BOOST_CHECK_EQUAL(
0325 merged12
0326 .resolveVolume(gctx, Vector3{105_mm, 0_mm, 0_mm}, -Vector3::UnitZ())
0327 .value(),
0328 vol4.get());
0329
0330
0331 auto material = std::make_shared<HomogeneousSurfaceMaterial>(
0332 MaterialSlab::Nothing());
0333 disc2->assignSurfaceMaterial(material);
0334 disc2->assignGeometryId(GeometryIdentifier().withBoundary(7));
0335 portal1 = Portal{
0336 gctx, {.alongNormal = {disc1, *vol1}, .oppositeNormal = {disc1, *vol2}}};
0337 portal2 = Portal{
0338 gctx, {.alongNormal = {disc2, *vol3}, .oppositeNormal = {disc2, *vol4}}};
0339 BOOST_CHECK_THROW(
0340 Portal::merge(gctx, portal1, portal2, AxisDirection::AxisR, *logger),
0341 PortalMergingException);
0342
0343 disc2->assignSurfaceMaterial(std::make_shared<ProtoSurfaceMaterial>(
0344 BinUtility{}, MappingType::Default, "original/disc"));
0345
0346
0347
0348 portal1 = Portal{
0349 gctx, {.alongNormal = {disc1, *vol1}, .oppositeNormal = {disc1, *vol2}}};
0350 portal2 = Portal{
0351 gctx, {.alongNormal = {disc2, *vol3}, .oppositeNormal = {disc2, *vol4}}};
0352 Portal mergedKeepGoing =
0353 Portal::merge(gctx, portal1, portal2, AxisDirection::AxisR, *logger,
0354 PortalMaterialMergePolicy::eDiscardAndMark);
0355 BOOST_CHECK(mergedKeepGoing.isValid());
0356 BOOST_REQUIRE(mergedKeepGoing.surface().surfaceMaterial() != nullptr);
0357 BOOST_CHECK(dynamic_cast<const MergedMaterialMarker*>(
0358 mergedKeepGoing.surface().surfaceMaterial()) != nullptr);
0359 const auto& marker = dynamic_cast<const MergedMaterialMarker&>(
0360 *mergedKeepGoing.surface().surfaceMaterial());
0361 BOOST_REQUIRE_EQUAL(marker.origins().size(), 1u);
0362 BOOST_CHECK_EQUAL(*marker.origins()[0].materialKey, "original/disc");
0363 BOOST_CHECK(marker.origins()[0].geometryId == disc2->geometryId());
0364
0365 BOOST_CHECK(mergedKeepGoing.surface().surfaceMaterial()->materialSlab(
0366 Vector2{0., 0.}) == MaterialSlab::Nothing());
0367
0368
0369 disc2->assignSurfaceMaterial(
0370 std::make_unique<MergedMaterialMarker>(marker.origins()));
0371 disc1->assignSurfaceMaterial(std::make_shared<ProtoSurfaceMaterial>(
0372 BinUtility{}, MappingType::Default, "original/inner-disc"));
0373 portal1 = Portal{
0374 gctx, {.alongNormal = {disc1, *vol1}, .oppositeNormal = {disc1, *vol2}}};
0375 portal2 = Portal{
0376 gctx, {.alongNormal = {disc2, *vol3}, .oppositeNormal = {disc2, *vol4}}};
0377 auto nested =
0378 Portal::merge(gctx, portal1, portal2, AxisDirection::AxisR, *logger,
0379 PortalMaterialMergePolicy::eDiscardAndMark);
0380 const auto& nestedMarker = dynamic_cast<const MergedMaterialMarker&>(
0381 *nested.surface().surfaceMaterial());
0382 BOOST_REQUIRE_EQUAL(nestedMarker.origins().size(), 2u);
0383 BOOST_CHECK_EQUAL(*nestedMarker.origins()[0].materialKey,
0384 "original/inner-disc");
0385 BOOST_CHECK_EQUAL(*nestedMarker.origins()[1].materialKey, "original/disc");
0386 }
0387
0388 BOOST_AUTO_TEST_SUITE_END()
0389
0390 BOOST_AUTO_TEST_SUITE(Fusing)
0391
0392 BOOST_AUTO_TEST_CASE(Separated) {
0393 auto vol1 = makeDummyVolume();
0394 vol1->setVolumeName("vol1");
0395 auto vol2 = makeDummyVolume();
0396 vol2->setVolumeName("vol2");
0397
0398 auto cyl1 = Surface::makeShared<CylinderSurface>(Transform3::Identity(),
0399 50_mm, 100_mm);
0400
0401 auto cyl2 = Surface::makeShared<CylinderSurface>(Transform3::Identity(),
0402 60_mm, 100_mm);
0403
0404 Portal portal1{gctx, {.oppositeNormal = {cyl1, *vol1}}};
0405
0406 Portal portal2{gctx, {.alongNormal = {cyl2, *vol2}}};
0407
0408 BOOST_CHECK(portal1.isValid());
0409 BOOST_CHECK(portal2.isValid());
0410
0411
0412 BOOST_CHECK_THROW(Portal::fuse(gctx, portal1, portal2, *logger),
0413 PortalFusingException);
0414
0415 BOOST_CHECK(portal1.isValid());
0416 BOOST_CHECK(portal2.isValid());
0417
0418
0419 BOOST_CHECK_THROW(
0420 portal1.setLink(gctx, Direction::AlongNormal(), cyl2, *vol2),
0421 PortalFusingException);
0422 BOOST_CHECK_EQUAL(portal1.getLink(Direction::AlongNormal()), nullptr);
0423
0424 Portal portal1b{gctx, {.oppositeNormal = {cyl1, *vol1}}};
0425 BOOST_CHECK(portal1b.isValid());
0426
0427
0428
0429
0430
0431
0432
0433
0434
0435 BOOST_CHECK_THROW(Portal::fuse(gctx, portal1, portal1b, *logger),
0436 PortalFusingException);
0437 BOOST_CHECK(portal1.isValid());
0438 BOOST_CHECK(portal1b.isValid());
0439
0440 auto disc1 = Surface::makeShared<DiscSurface>(
0441 Transform3::Identity(), std::make_shared<RadialBounds>(50_mm, 100_mm));
0442
0443 auto disc2 = Surface::makeShared<DiscSurface>(
0444 Transform3{Translation3{Vector3{0, 0, 5_mm}}},
0445 std::make_shared<RadialBounds>(50_mm, 100_mm));
0446
0447
0448
0449
0450
0451
0452
0453
0454
0455 Portal portal2b{gctx, {.alongNormal = {disc2, *vol2}}};
0456
0457 BOOST_CHECK_THROW(Portal::fuse(gctx, portal2, portal2b, *logger),
0458 PortalFusingException);
0459 BOOST_CHECK(portal1.isValid());
0460 BOOST_CHECK(portal2.isValid());
0461
0462
0463
0464
0465
0466
0467
0468
0469
0470 Portal portal2c{
0471 gctx, {.alongNormal = {disc2, *vol1}, .oppositeNormal = {disc2, *vol2}}};
0472 BOOST_CHECK(portal2c.isValid());
0473
0474 BOOST_CHECK_THROW(Portal::fuse(gctx, portal2, portal2c, *logger),
0475 PortalFusingException);
0476 BOOST_CHECK(portal2.isValid());
0477 BOOST_CHECK(portal2c.isValid());
0478 }
0479
0480 BOOST_AUTO_TEST_CASE(Success) {
0481 auto vol1 = makeDummyVolume();
0482 vol1->setVolumeName("vol1");
0483 auto vol2 = makeDummyVolume();
0484 vol2->setVolumeName("vol2");
0485
0486 auto cyl1 = Surface::makeShared<CylinderSurface>(Transform3::Identity(),
0487 50_mm, 100_mm);
0488
0489 auto cyl2 = Surface::makeShared<CylinderSurface>(Transform3::Identity(),
0490 50_mm, 100_mm);
0491
0492 BOOST_CHECK(*cyl1 == *cyl2);
0493
0494
0495
0496
0497
0498
0499
0500
0501
0502 Portal portal1{gctx, {.oppositeNormal = {cyl1, *vol1}}};
0503 BOOST_CHECK_EQUAL(&portal1.getLink(Direction::OppositeNormal())->surface(),
0504 cyl1.get());
0505
0506 Portal portal2{gctx, {.alongNormal = {cyl2, *vol2}}};
0507
0508 BOOST_CHECK(portal1.isValid());
0509 BOOST_CHECK(portal2.isValid());
0510
0511 Portal portal3 = Portal::fuse(gctx, portal1, portal2, *logger);
0512
0513 BOOST_CHECK(!portal1.isValid());
0514 BOOST_CHECK(!portal2.isValid());
0515 BOOST_CHECK(portal3.isValid());
0516
0517 BOOST_CHECK_EQUAL(portal3.surface().surfaceMaterial(), nullptr);
0518
0519
0520 BOOST_CHECK_EQUAL(&portal3.surface(), cyl2.get());
0521
0522 BOOST_CHECK_EQUAL(&portal3.getLink(Direction::OppositeNormal())->surface(),
0523 cyl2.get());
0524 }
0525
0526 BOOST_AUTO_TEST_CASE(Material) {
0527 auto vol1 = makeDummyVolume();
0528 auto vol2 = makeDummyVolume();
0529
0530 auto cyl1 = Surface::makeShared<CylinderSurface>(Transform3::Identity(),
0531 50_mm, 100_mm);
0532
0533 auto cyl2 = Surface::makeShared<CylinderSurface>(Transform3::Identity(),
0534 50_mm, 100_mm);
0535
0536
0537
0538
0539
0540
0541
0542
0543
0544 Portal portal1{gctx, {.oppositeNormal = {cyl1, *vol1}}};
0545 Portal portal2{gctx, {.alongNormal = {cyl2, *vol2}}};
0546
0547 auto material = std::make_shared<HomogeneousSurfaceMaterial>(
0548 MaterialSlab::Nothing());
0549
0550 cyl1->assignSurfaceMaterial(material);
0551
0552 Portal portal12 = Portal::fuse(gctx, portal1, portal2, *logger);
0553
0554
0555 BOOST_CHECK_EQUAL(&portal12.surface(), cyl1.get());
0556 BOOST_CHECK_EQUAL(portal12.surface().surfaceMaterial(), material.get());
0557
0558
0559 portal1 = Portal{gctx, {.oppositeNormal = {cyl1, *vol1}}};
0560 portal2 = Portal{gctx, {.alongNormal = {cyl2, *vol2}}};
0561 cyl2->assignSurfaceMaterial(material);
0562
0563
0564 BOOST_CHECK_THROW(Portal::fuse(gctx, portal1, portal2, *logger),
0565 PortalFusingException);
0566
0567 BOOST_CHECK(portal1.isValid());
0568 BOOST_CHECK(portal2.isValid());
0569
0570 cyl1->assignSurfaceMaterial(nullptr);
0571
0572 portal12 = Portal::fuse(gctx, portal1, portal2, *logger);
0573
0574
0575 BOOST_CHECK_EQUAL(&portal12.surface(), cyl2.get());
0576 BOOST_CHECK_EQUAL(portal12.surface().surfaceMaterial(), material.get());
0577 }
0578
0579 BOOST_AUTO_TEST_CASE(GridCreationOnFuse) {
0580 Transform3 base = Transform3::Identity();
0581
0582 auto vol1 = std::make_shared<TrackingVolume>(
0583 base, std::make_shared<CylinderVolumeBounds>(30_mm, 40_mm, 100_mm));
0584
0585 auto vol2 = std::make_shared<TrackingVolume>(
0586 base, std::make_shared<CylinderVolumeBounds>(30_mm, 40_mm, 100_mm));
0587
0588 auto vol3 = std::make_shared<TrackingVolume>(
0589 base, std::make_shared<CylinderVolumeBounds>(40_mm, 50_mm, 100_mm));
0590
0591 auto vol4 = std::make_shared<TrackingVolume>(
0592 base, std::make_shared<CylinderVolumeBounds>(40_mm, 50_mm, 100_mm));
0593
0594 auto disc1 =
0595 Surface::makeShared<DiscSurface>(Transform3::Identity(), 30_mm, 60_mm);
0596
0597 auto disc2 =
0598 Surface::makeShared<DiscSurface>(Transform3::Identity(), 60_mm, 90_mm);
0599
0600 auto disc3 =
0601 Surface::makeShared<DiscSurface>(Transform3::Identity(), 90_mm, 120_mm);
0602
0603 auto trivial1 = std::make_unique<TrivialPortalLink>(disc1, *vol1);
0604 BOOST_REQUIRE(trivial1);
0605 auto trivial2 = std::make_unique<TrivialPortalLink>(disc2, *vol2);
0606 BOOST_REQUIRE(trivial2);
0607 auto trivial3 = std::make_unique<TrivialPortalLink>(disc3, *vol3);
0608 BOOST_REQUIRE(trivial3);
0609
0610 std::vector<std::unique_ptr<PortalLinkBase>> links;
0611 links.push_back(std::move(trivial1));
0612 links.push_back(std::move(trivial2));
0613 links.push_back(std::move(trivial3));
0614
0615 auto composite = std::make_unique<CompositePortalLink>(std::move(links),
0616 AxisDirection::AxisR);
0617
0618 auto discOpposite =
0619 Surface::makeShared<DiscSurface>(Transform3::Identity(), 30_mm, 120_mm);
0620
0621 auto trivialOpposite =
0622 std::make_unique<TrivialPortalLink>(discOpposite, *vol4);
0623
0624 Portal aPortal{gctx, std::move(composite), nullptr};
0625 Portal bPortal{gctx, nullptr, std::move(trivialOpposite)};
0626
0627 Portal fused = Portal::fuse(gctx, aPortal, bPortal, *logger);
0628
0629 BOOST_CHECK_NE(dynamic_cast<const TrivialPortalLink*>(
0630 fused.getLink(Direction::OppositeNormal())),
0631 nullptr);
0632
0633 const auto* grid = dynamic_cast<const GridPortalLink*>(
0634 fused.getLink(Direction::AlongNormal()));
0635 BOOST_REQUIRE_NE(grid, nullptr);
0636
0637 BOOST_CHECK_EQUAL(grid->grid().axes().front()->getNBins(), 3);
0638 }
0639
0640 BOOST_AUTO_TEST_SUITE_END()
0641
0642 BOOST_AUTO_TEST_CASE(Construction) {
0643 auto vol1 = makeDummyVolume();
0644
0645
0646 auto disc1 = Surface::makeShared<DiscSurface>(
0647 Transform3::Identity(), std::make_shared<RadialBounds>(50_mm, 100_mm));
0648
0649 auto disc2 = Surface::makeShared<DiscSurface>(
0650 Transform3{Translation3{Vector3{0, 0, 5_mm}}},
0651 std::make_shared<RadialBounds>(50_mm, 100_mm));
0652
0653 BOOST_CHECK_THROW(auto p = std::make_unique<Portal>(
0654 gctx, std::make_unique<TrivialPortalLink>(disc1, *vol1),
0655 std::make_unique<TrivialPortalLink>(disc2, *vol1)),
0656 PortalFusingException);
0657
0658 BOOST_CHECK_THROW((Portal{gctx, nullptr, nullptr}), std::invalid_argument);
0659 BOOST_CHECK_THROW(Portal(gctx, {}), std::invalid_argument);
0660 }
0661
0662 BOOST_AUTO_TEST_CASE(InvalidConstruction) {
0663 BOOST_CHECK_THROW(Portal(Direction::AlongNormal(), nullptr),
0664 std::invalid_argument);
0665
0666 auto vol1 = makeDummyVolume();
0667
0668 BOOST_CHECK_THROW(Portal(Direction::AlongNormal(), nullptr, *vol1),
0669 std::invalid_argument);
0670
0671 auto disc1 = Surface::makeShared<DiscSurface>(
0672 Transform3::Identity(), std::make_shared<RadialBounds>(50_mm, 100_mm));
0673 Portal portal(Direction::AlongNormal(), disc1, *vol1);
0674
0675 BOOST_CHECK_THROW(portal.setLink(gctx, Direction::AlongNormal(), nullptr),
0676 std::invalid_argument);
0677 }
0678
0679 BOOST_AUTO_TEST_CASE(PortalFill) {
0680 auto vol1 = makeDummyVolume();
0681 auto vol2 = makeDummyVolume();
0682
0683 auto cyl1 = Surface::makeShared<CylinderSurface>(Transform3::Identity(),
0684 50_mm, 100_mm);
0685
0686 Portal portal1{gctx, {.oppositeNormal = {cyl1, *vol1}}};
0687 Portal portal2{gctx, {.alongNormal = {cyl1, *vol2}}};
0688
0689
0690 Portal::fuse(gctx, portal1, portal2, *logger);
0691
0692 BOOST_CHECK_THROW(portal1.fill(*vol2), std::logic_error);
0693
0694 portal1 = Portal{gctx, {.oppositeNormal = {cyl1, *vol1}}};
0695 portal2 = Portal{gctx, {.alongNormal = {cyl1, *vol2}}};
0696
0697 BOOST_CHECK_EQUAL(portal1.getLink(Direction::AlongNormal()), nullptr);
0698 BOOST_CHECK_NE(portal1.getLink(Direction::OppositeNormal()), nullptr);
0699
0700 portal1.fill(*vol2);
0701 BOOST_CHECK_NE(portal1.getLink(Direction::AlongNormal()), nullptr);
0702 BOOST_CHECK_NE(portal1.getLink(Direction::OppositeNormal()), nullptr);
0703
0704 BOOST_CHECK_THROW(portal1.fill(*vol2), std::logic_error);
0705 }
0706
0707 BOOST_AUTO_TEST_SUITE_END()
0708
0709 BOOST_AUTO_TEST_SUITE_END()
0710
0711 }