File indexing completed on 2025-10-27 07:57:00
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include <boost/test/data/test_case.hpp>
0010 #include <boost/test/tools/context.hpp>
0011 #include <boost/test/tools/old/interface.hpp>
0012 #include <boost/test/unit_test.hpp>
0013 #include <boost/test/unit_test_suite.hpp>
0014
0015 #include "Acts/Definitions/Algebra.hpp"
0016 #include "Acts/Definitions/Units.hpp"
0017 #include "Acts/Geometry/CompositePortalLink.hpp"
0018 #include "Acts/Geometry/CuboidVolumeBounds.hpp"
0019 #include "Acts/Geometry/CylinderVolumeBounds.hpp"
0020 #include "Acts/Geometry/GridPortalLink.hpp"
0021 #include "Acts/Geometry/TrackingVolume.hpp"
0022 #include "Acts/Geometry/TrivialPortalLink.hpp"
0023 #include "Acts/Surfaces/CylinderSurface.hpp"
0024 #include "Acts/Surfaces/RadialBounds.hpp"
0025 #include "Acts/Surfaces/RectangleBounds.hpp"
0026 #include "Acts/Surfaces/SurfaceMergingException.hpp"
0027 #include "Acts/Utilities/AxisDefinitions.hpp"
0028 #include "Acts/Utilities/ThrowAssert.hpp"
0029 #include "ActsTests/CommonHelpers/FloatComparisons.hpp"
0030
0031 #include <cstdio>
0032 #include <iostream>
0033 #include <memory>
0034 #include <numbers>
0035 #include <stdexcept>
0036
0037 using namespace Acts;
0038 using namespace UnitLiterals;
0039
0040 namespace ActsTests {
0041
0042 auto logger = getDefaultLogger("UnitTests", Logging::VERBOSE);
0043
0044 struct Fixture {
0045 Logging::Level m_level;
0046 Fixture() {
0047 m_level = Logging::getFailureThreshold();
0048 Logging::setFailureThreshold(Logging::FATAL);
0049 }
0050
0051 ~Fixture() { Logging::setFailureThreshold(m_level); }
0052 };
0053
0054 std::shared_ptr<TrackingVolume> makeDummyVolume() {
0055 return std::make_shared<TrackingVolume>(
0056 Transform3::Identity(),
0057 std::make_shared<CylinderVolumeBounds>(30_mm, 40_mm, 100_mm));
0058 }
0059
0060 GeometryContext gctx;
0061
0062 template <typename T>
0063 std::unique_ptr<T> copy(const std::unique_ptr<T>& p) {
0064 return std::make_unique<T>(*p);
0065 }
0066
0067 template <typename link_t>
0068 void visitBins(const link_t& link,
0069 const std::function<void(const TrackingVolume*)>& func) {
0070 auto& grid = link.grid();
0071 auto loc = grid.numLocalBins();
0072 if constexpr (std::decay_t<decltype(grid)>::DIM == 1) {
0073 for (std::size_t i = 1; i <= loc[0]; i++) {
0074 func(grid.atLocalBins({i}));
0075 }
0076 } else {
0077 for (std::size_t i = 1; i <= loc[0]; i++) {
0078 for (std::size_t j = 1; j <= loc[1]; j++) {
0079 func(grid.atLocalBins({i, j}));
0080 }
0081 }
0082 }
0083 }
0084
0085 BOOST_FIXTURE_TEST_SUITE(GeometrySuite, Fixture)
0086
0087 BOOST_AUTO_TEST_SUITE(GridConstruction)
0088
0089 BOOST_AUTO_TEST_CASE(Cylinder) {
0090 BOOST_TEST_CONTEXT("1D") {
0091 auto cyl = Surface::makeShared<CylinderSurface>(Transform3::Identity(),
0092 30_mm, 100_mm);
0093
0094
0095 auto vol = std::make_shared<TrackingVolume>(
0096 Transform3::Identity(),
0097 std::make_shared<CylinderVolumeBounds>(30_mm, 40_mm, 100_mm));
0098
0099
0100 BOOST_CHECK_THROW(GridPortalLink::make(cyl, AxisDirection::AxisZ,
0101 Axis{AxisBound, 0, 5, 5}),
0102 std::invalid_argument);
0103
0104 auto grid1dCyl = GridPortalLink::make(cyl, AxisDirection::AxisZ,
0105 Axis{AxisBound, -100_mm, 100_mm, 10});
0106 BOOST_REQUIRE(grid1dCyl);
0107 grid1dCyl->setVolume(vol.get());
0108
0109
0110 BOOST_CHECK_THROW(GridPortalLink::make(cyl, AxisDirection::AxisRPhi,
0111 Axis{AxisBound, -180_degree * 30_mm,
0112 180_degree * 30_mm, 10}),
0113 std::invalid_argument);
0114
0115 auto grid1dCylRPhi = GridPortalLink::make(
0116 cyl, AxisDirection::AxisRPhi,
0117 Axis{AxisClosed, -180_degree * 30_mm, 180_degree * 30_mm, 10});
0118 BOOST_REQUIRE_NE(grid1dCylRPhi, nullptr);
0119 grid1dCylRPhi->setVolume(vol.get());
0120
0121 Axis axisExpected{AxisClosed, -180_degree * 30_mm, 180_degree * 30_mm, 10};
0122 BOOST_CHECK_EQUAL(grid1dCylRPhi->grid().axes().size(), 1);
0123 const auto& axis = *grid1dCylRPhi->grid().axes().front();
0124 BOOST_CHECK_EQUAL(axis, axisExpected);
0125
0126
0127 auto cyl2 = Surface::makeShared<CylinderSurface>(
0128 Transform3{Translation3{Vector3::UnitZ() * 150_mm}}, 30_mm, 50_mm);
0129
0130 auto grid1dCyl2 = GridPortalLink::make(cyl2, AxisDirection::AxisZ,
0131 Axis{AxisBound, -50_mm, 50_mm, 5});
0132
0133
0134 auto cylNonZeroAverage = Surface::makeShared<CylinderSurface>(
0135 Transform3::Identity(), 30_mm, 100_mm, 20_degree, 45_degree);
0136 BOOST_CHECK_THROW(
0137 GridPortalLink::make(cylNonZeroAverage, AxisDirection::AxisZ,
0138 Axis{AxisBound, -100_mm, 100_mm, 10}),
0139 std::invalid_argument);
0140
0141 auto checkAllBins = [&](const auto& link) {
0142 visitBins(link, [&](const TrackingVolume* content) {
0143 BOOST_CHECK_EQUAL(content, vol.get());
0144 });
0145 };
0146
0147 checkAllBins(*grid1dCyl);
0148 checkAllBins(*grid1dCylRPhi);
0149
0150
0151
0152 auto grid2dCyl1 = grid1dCyl->extendTo2d(nullptr);
0153 BOOST_REQUIRE(grid2dCyl1);
0154 BOOST_CHECK_EQUAL(grid2dCyl1->grid().axes().size(), 2);
0155 BOOST_CHECK_EQUAL(grid2dCyl1->surface().bounds(), cyl->bounds());
0156 const auto* axis1 = grid2dCyl1->grid().axes().front();
0157 const auto* axis2 = grid2dCyl1->grid().axes().back();
0158
0159 Axis axis1Expected{AxisClosed, -std::numbers::pi * 30_mm,
0160 std::numbers::pi * 30_mm, 1};
0161 BOOST_CHECK_EQUAL(*axis1, axis1Expected);
0162 Axis axis2Expected{AxisBound, -100_mm, 100_mm, 10};
0163 BOOST_CHECK_EQUAL(*axis2, axis2Expected);
0164
0165 auto& concrete = dynamic_cast<
0166 GridPortalLinkT<decltype(axis1Expected), decltype(axis2Expected)>&>(
0167 *grid2dCyl1);
0168
0169 checkAllBins(concrete);
0170
0171 Axis axis1Explicit{AxisClosed, -std::numbers::pi * 30_mm,
0172 std::numbers::pi * 30_mm, 13};
0173 auto grid2dCyl1Explicit = grid1dCyl->extendTo2d(&axis1Explicit);
0174 BOOST_REQUIRE(grid2dCyl1Explicit);
0175 BOOST_CHECK_EQUAL(grid2dCyl1Explicit->grid().axes().size(), 2);
0176 axis1 = grid2dCyl1Explicit->grid().axes().front();
0177 axis2 = grid2dCyl1Explicit->grid().axes().back();
0178
0179 BOOST_CHECK_EQUAL(*axis1, axis1Explicit);
0180 BOOST_CHECK_EQUAL(*axis2, axis2Expected);
0181
0182 auto& concrete2 = dynamic_cast<
0183 GridPortalLinkT<decltype(axis1Explicit), decltype(axis2Expected)>&>(
0184 *grid2dCyl1Explicit);
0185
0186 checkAllBins(concrete2);
0187
0188 auto cylPhi = Surface::makeShared<CylinderSurface>(
0189 Transform3::Identity(), 30_mm, 100_mm, 45_degree);
0190 std::unique_ptr<GridPortalLink> grid1dCylPhi = GridPortalLink::make(
0191 cylPhi, AxisDirection::AxisZ, Axis{AxisBound, -100_mm, 100_mm, 10});
0192
0193 grid1dCylPhi->setVolume(vol.get());
0194
0195
0196 BOOST_CHECK_THROW(GridPortalLink::make(cylPhi, AxisDirection::AxisRPhi,
0197 Axis{AxisClosed, -45_degree * 30_mm,
0198 45_degree * 30_mm, 10}),
0199 std::invalid_argument);
0200
0201 auto grid2dCylPhi = grid1dCylPhi->extendTo2d(nullptr);
0202 BOOST_CHECK_EQUAL(grid2dCylPhi->grid().axes().size(), 2);
0203 BOOST_CHECK_EQUAL(grid2dCylPhi->surface().bounds(), cylPhi->bounds());
0204 const auto* axis1Phi = grid2dCylPhi->grid().axes().front();
0205 const auto* axis2Phi = grid2dCylPhi->grid().axes().back();
0206
0207 Axis axis1PhiExpected{AxisBound, -45_degree * 30_mm, 45_degree * 30_mm, 1};
0208 BOOST_CHECK_EQUAL(*axis1Phi, axis1PhiExpected);
0209 Axis axis2PhiExpected{AxisBound, -100_mm, 100_mm, 10};
0210 BOOST_CHECK_EQUAL(*axis2Phi, axis2PhiExpected);
0211
0212 auto& concrete3 =
0213 dynamic_cast<GridPortalLinkT<decltype(axis1PhiExpected),
0214 decltype(axis2PhiExpected)>&>(
0215 *grid2dCylPhi);
0216
0217 checkAllBins(concrete3);
0218
0219 Axis axis1PhiExplicit{AxisBound, -45_degree * 30_mm, 45_degree * 30_mm, 13};
0220 auto grid2dCylPhiExplicit = grid1dCylPhi->extendTo2d(&axis1PhiExplicit);
0221 BOOST_REQUIRE(grid2dCylPhiExplicit);
0222 BOOST_CHECK_EQUAL(grid2dCylPhiExplicit->grid().axes().size(), 2);
0223 axis1Phi = grid2dCylPhiExplicit->grid().axes().front();
0224 axis2Phi = grid2dCylPhiExplicit->grid().axes().back();
0225 BOOST_CHECK_EQUAL(*axis1Phi, axis1PhiExplicit);
0226 BOOST_CHECK_EQUAL(*axis2Phi, axis2PhiExpected);
0227
0228 auto& concrete4 =
0229 dynamic_cast<GridPortalLinkT<decltype(axis1PhiExplicit),
0230 decltype(axis2PhiExpected)>&>(
0231 *grid2dCylPhiExplicit);
0232
0233 checkAllBins(concrete4);
0234 }
0235
0236 BOOST_TEST_CONTEXT("2D") {
0237 auto cyl = Surface::makeShared<CylinderSurface>(Transform3::Identity(),
0238 30_mm, 100_mm, 45_degree);
0239
0240
0241 BOOST_CHECK_THROW(GridPortalLink::make(cyl, Axis{AxisBound, 1, 2, 5},
0242 Axis{AxisBound, 3_mm, 4_mm, 5}),
0243 std::invalid_argument);
0244
0245
0246 BOOST_CHECK_THROW(GridPortalLink::make(cyl, Axis{AxisBound, 3_mm, 4_mm, 5},
0247 Axis{AxisBound, -100_mm, 100_m, 5}),
0248 std::invalid_argument);
0249
0250
0251 BOOST_CHECK_THROW(
0252 GridPortalLink::make(
0253 cyl, Axis{AxisBound, -45_degree * 30_mm, 45_degree * 30_mm, 5},
0254 Axis{AxisBound, -80_mm, 100_mm, 5}),
0255 std::invalid_argument);
0256
0257 auto grid1 = GridPortalLink::make(
0258 cyl, Axis{AxisBound, -45_degree * 30_mm, 45_degree * 30_mm, 5},
0259 Axis{AxisBound, -100_mm, 100_mm, 5});
0260
0261 auto cylFull = Surface::makeShared<CylinderSurface>(Transform3::Identity(),
0262 30_mm, 100_mm);
0263
0264
0265 BOOST_CHECK_THROW(GridPortalLink::make(cylFull,
0266 Axis{AxisBound, -180_degree * 30_mm,
0267 180_degree * 30_mm, 5},
0268 Axis{AxisBound, -100_mm, 100_mm, 5}),
0269 std::invalid_argument);
0270
0271 auto gridFull = GridPortalLink::make(
0272 cylFull, Axis{AxisClosed, -180_degree * 30_mm, 180_degree * 30_mm, 5},
0273 Axis{AxisBound, -100_mm, 100_mm, 5});
0274
0275 BOOST_CHECK_EQUAL(gridFull->grid().axes().size(), 2);
0276 BOOST_CHECK_EQUAL(gridFull->grid().axes().size(), 2);
0277 Axis axisFullExpected{AxisClosed, -180_degree * 30_mm, 180_degree * 30_mm,
0278 5};
0279 const auto& axisFull = *gridFull->grid().axes().front();
0280 BOOST_CHECK_EQUAL(axisFull, axisFullExpected);
0281 }
0282 }
0283
0284 BOOST_AUTO_TEST_CASE(Disc) {
0285 using enum AxisType;
0286 using enum AxisBoundaryType;
0287 BOOST_TEST_CONTEXT("1D") {
0288 auto disc1 =
0289 Surface::makeShared<DiscSurface>(Transform3::Identity(), 30_mm, 100_mm);
0290
0291
0292 auto vol = std::make_shared<TrackingVolume>(
0293 Transform3::Identity(),
0294 std::make_shared<CylinderVolumeBounds>(30_mm, 40_mm, 100_mm));
0295
0296 BOOST_CHECK_THROW(GridPortalLink::make(disc1, AxisDirection::AxisZ,
0297 Axis{AxisBound, 30_mm, 100_mm, 3}),
0298 std::invalid_argument);
0299
0300
0301 BOOST_CHECK_THROW(
0302 GridPortalLink::make(disc1, AxisDirection::AxisPhi,
0303 Axis{AxisBound, -180_degree, 180_degree, 3}),
0304 std::invalid_argument);
0305
0306 auto grid1 = GridPortalLink::make(disc1, AxisDirection::AxisR,
0307 Axis{AxisBound, 30_mm, 100_mm, 3});
0308 BOOST_REQUIRE_NE(grid1, nullptr);
0309 BOOST_CHECK_EQUAL(grid1->grid().axes().size(), 1);
0310 const auto& axis = *grid1->grid().axes().front();
0311 Axis axis1Expected{AxisBound, 30_mm, 100_mm, 3};
0312 BOOST_CHECK_EQUAL(axis, axis1Expected);
0313
0314 grid1->setVolume(vol.get());
0315
0316 auto discPhi = Surface::makeShared<DiscSurface>(Transform3::Identity(),
0317 30_mm, 100_mm, 45_degree);
0318
0319
0320 BOOST_CHECK_THROW(
0321 GridPortalLink::make(discPhi, AxisDirection::AxisPhi,
0322 Axis{AxisClosed, -45_degree, 45_degree, 3}),
0323 std::invalid_argument);
0324
0325 auto gridPhi =
0326 GridPortalLink::make(discPhi, AxisDirection::AxisPhi,
0327 Axis{AxisBound, -45_degree, 45_degree, 3});
0328 BOOST_REQUIRE_NE(gridPhi, nullptr);
0329 gridPhi->setVolume(vol.get());
0330
0331
0332 auto discNonZeroAverage = Surface::makeShared<DiscSurface>(
0333 Transform3::Identity(),
0334 std::make_shared<RadialBounds>(30_mm, 100_mm, 45_degree, 75_degree));
0335 BOOST_CHECK_THROW(
0336 GridPortalLink::make(discNonZeroAverage, AxisDirection::AxisR,
0337 Axis{AxisBound, 30_mm, 100_mm, 3}),
0338 std::invalid_argument);
0339
0340 BOOST_CHECK_EQUAL(gridPhi->grid().axes().size(), 1);
0341 const auto& axisPhi = *gridPhi->grid().axes().front();
0342 Axis axisPhi1Expected{AxisBound, -45_degree, 45_degree, 3};
0343 BOOST_CHECK_EQUAL(axisPhi, axisPhi1Expected);
0344
0345 auto checkAllBins = [&](const auto& grid) {
0346 visitBins(grid, [&](const TrackingVolume* content) {
0347 BOOST_CHECK_EQUAL(content, vol.get());
0348 });
0349 };
0350
0351 checkAllBins(*grid1);
0352 checkAllBins(*gridPhi);
0353
0354
0355 auto grid2d = grid1->extendTo2d(nullptr);
0356 BOOST_REQUIRE(grid2d);
0357 BOOST_CHECK_EQUAL(grid2d->grid().axes().size(), 2);
0358 const auto* axis1 = grid2d->grid().axes().front();
0359 BOOST_CHECK_EQUAL(*axis1, axis1Expected);
0360 const auto* axis2 = grid2d->grid().axes().back();
0361 BOOST_CHECK_CLOSE(axis2->getMin(), -180_degree, 1e-6);
0362 BOOST_CHECK_CLOSE(axis2->getMax(), 180_degree, 1e-6);
0363 BOOST_CHECK_EQUAL(axis2->getNBins(), 1);
0364 BOOST_CHECK_EQUAL(axis2->getType(), AxisType::Equidistant);
0365 BOOST_CHECK_EQUAL(axis2->getBoundaryType(), AxisBoundaryType::Closed);
0366
0367 checkAllBins(
0368 dynamic_cast<GridPortalLinkT<decltype(axisPhi1Expected),
0369 Axis<Equidistant, Closed>>&>(*grid2d));
0370
0371 Axis axis2Explicit{AxisClosed, -180_degree, 180_degree, 3};
0372 auto grid2dExplicit = grid1->extendTo2d(&axis2Explicit);
0373 BOOST_REQUIRE(grid2dExplicit);
0374 BOOST_CHECK_EQUAL(grid2dExplicit->grid().axes().size(), 2);
0375 axis1 = grid2dExplicit->grid().axes().front();
0376 axis2 = grid2dExplicit->grid().axes().back();
0377 BOOST_CHECK_EQUAL(*axis1, axis1Expected);
0378 BOOST_CHECK_EQUAL(*axis2, axis2Explicit);
0379
0380 checkAllBins(dynamic_cast<GridPortalLinkT<decltype(axisPhi1Expected),
0381 decltype(axis2Explicit)>&>(
0382 *grid2dExplicit));
0383
0384 auto gridPhiBinnedInR = GridPortalLink::make(
0385 discPhi, AxisDirection::AxisR, Axis{AxisBound, 30_mm, 100_mm, 3});
0386 gridPhiBinnedInR->setVolume(vol.get());
0387 auto grid2dPhiNonClosed = gridPhiBinnedInR->extendTo2d(nullptr);
0388 BOOST_REQUIRE(grid2dPhiNonClosed);
0389 BOOST_CHECK_EQUAL(grid2dPhiNonClosed->grid().axes().size(), 2);
0390 Axis gridPhiBinnedInRExpected{AxisBound, 30_mm, 100_mm, 3};
0391 BOOST_CHECK_EQUAL(*grid2dPhiNonClosed->grid().axes().front(),
0392 gridPhiBinnedInRExpected);
0393 const auto* axisPhiNonClosed = grid2dPhiNonClosed->grid().axes().back();
0394 BOOST_CHECK_CLOSE(axisPhiNonClosed->getMin(), -45_degree, 1e-6);
0395 BOOST_CHECK_CLOSE(axisPhiNonClosed->getMax(), 45_degree, 1e-6);
0396 BOOST_CHECK_EQUAL(axisPhiNonClosed->getNBins(), 1);
0397 BOOST_CHECK_EQUAL(axisPhiNonClosed->getType(), AxisType::Equidistant);
0398 BOOST_CHECK_EQUAL(axisPhiNonClosed->getBoundaryType(),
0399 AxisBoundaryType::Bound);
0400
0401 checkAllBins(
0402 dynamic_cast<GridPortalLinkT<decltype(gridPhiBinnedInRExpected),
0403 Axis<Equidistant, Bound>>&>(
0404 *grid2dPhiNonClosed));
0405
0406 Axis axisPhiNonClosedExplicit{AxisBound, -45_degree, 45_degree, 3};
0407 auto grid2dPhiNonClosedExplicit =
0408 gridPhiBinnedInR->extendTo2d(&axisPhiNonClosedExplicit);
0409 BOOST_REQUIRE(grid2dPhiNonClosedExplicit);
0410 BOOST_CHECK_EQUAL(grid2dPhiNonClosedExplicit->grid().axes().size(), 2);
0411 axisPhiNonClosed = grid2dPhiNonClosedExplicit->grid().axes().back();
0412 BOOST_CHECK_EQUAL(*axisPhiNonClosed, axisPhiNonClosedExplicit);
0413 BOOST_CHECK_EQUAL(*grid2dPhiNonClosedExplicit->grid().axes().front(),
0414 gridPhiBinnedInRExpected);
0415
0416 checkAllBins(
0417 dynamic_cast<GridPortalLinkT<decltype(gridPhiBinnedInRExpected),
0418 decltype(axisPhiNonClosedExplicit)>&>(
0419 *grid2dPhiNonClosedExplicit));
0420
0421 auto grid2dPhi = gridPhi->extendTo2d(nullptr);
0422 BOOST_REQUIRE(grid2dPhi);
0423 BOOST_CHECK_EQUAL(grid2dPhi->grid().axes().size(), 2);
0424 Axis axis2dPhiExpected{AxisBound, 30_mm, 100_mm, 1};
0425 BOOST_CHECK_EQUAL(*grid2dPhi->grid().axes().front(), axis2dPhiExpected);
0426 BOOST_CHECK_EQUAL(*grid2dPhi->grid().axes().back(), axisPhi1Expected);
0427
0428 checkAllBins(
0429 dynamic_cast<GridPortalLinkT<decltype(axis2dPhiExpected),
0430 decltype(axisPhi1Expected)>&>(*grid2dPhi));
0431
0432 Axis axis2dPhiExplicit{AxisBound, 30_mm, 100_mm, 3};
0433 auto grid2dPhiExplicit = gridPhi->extendTo2d(&axis2dPhiExplicit);
0434 BOOST_REQUIRE(grid2dPhiExplicit);
0435 BOOST_CHECK_EQUAL(grid2dPhiExplicit->grid().axes().size(), 2);
0436 BOOST_CHECK_EQUAL(*grid2dPhiExplicit->grid().axes().front(),
0437 axis2dPhiExplicit);
0438 BOOST_CHECK_EQUAL(*grid2dPhiExplicit->grid().axes().back(),
0439 axisPhi1Expected);
0440
0441 checkAllBins(dynamic_cast<GridPortalLinkT<decltype(axisPhi1Expected),
0442 decltype(axis2dPhiExplicit)>&>(
0443 *grid2dPhiExplicit));
0444 }
0445
0446 BOOST_TEST_CONTEXT("2D") {
0447 auto discPhi = Surface::makeShared<DiscSurface>(Transform3::Identity(),
0448 30_mm, 100_mm, 45_degree);
0449
0450 Axis rBad{AxisBound, 1, 2, 5};
0451 Axis rGood{AxisBound, 30_mm, 100_mm, 5};
0452 Axis phiBad{AxisBound, 1, 2, 5};
0453 Axis phiGood{AxisBound, -45_degree, 45_degree, 5};
0454
0455
0456 BOOST_CHECK_THROW(GridPortalLink::make(discPhi, rBad, phiBad),
0457 std::invalid_argument);
0458
0459 BOOST_CHECK_THROW(GridPortalLink::make(discPhi, rBad, phiGood),
0460 std::invalid_argument);
0461
0462 BOOST_CHECK_THROW(GridPortalLink::make(discPhi, rGood, phiBad),
0463 std::invalid_argument);
0464
0465 auto grid = GridPortalLink::make(discPhi, rGood, phiGood);
0466 BOOST_REQUIRE_NE(grid, nullptr);
0467 }
0468 }
0469
0470 BOOST_AUTO_TEST_CASE(Plane) {
0471 using enum AxisType;
0472 using enum AxisBoundaryType;
0473 BOOST_TEST_CONTEXT("1D") {
0474
0475 auto rBounds = std::make_shared<const RectangleBounds>(30_mm, 100_mm);
0476 auto planeX =
0477 Surface::makeShared<PlaneSurface>(Transform3::Identity(), rBounds);
0478 auto planeY =
0479 Surface::makeShared<PlaneSurface>(Transform3::Identity(), rBounds);
0480
0481
0482 auto vol = std::make_shared<TrackingVolume>(
0483 Transform3::Identity(),
0484 std::make_shared<CuboidVolumeBounds>(30_mm, 40_mm, 100_mm));
0485
0486
0487 auto gridX = GridPortalLink::make(planeX, AxisDirection::AxisX,
0488 Axis{AxisBound, -30_mm, 30_mm, 3});
0489 auto gridY = GridPortalLink::make(planeY, AxisDirection::AxisY,
0490 Axis{AxisBound, -100_mm, 100_mm, 3});
0491
0492
0493 BOOST_REQUIRE_NE(gridX, nullptr);
0494 BOOST_CHECK_EQUAL(gridX->grid().axes().size(), 1);
0495 const auto& axisX = *gridX->grid().axes().front();
0496 Axis axisXExpected{AxisBound, -30_mm, 30_mm, 3};
0497 BOOST_CHECK_EQUAL(axisX, axisXExpected);
0498
0499
0500 BOOST_REQUIRE_NE(gridY, nullptr);
0501 BOOST_CHECK_EQUAL(gridY->grid().axes().size(), 1);
0502 const auto& axisY = *gridY->grid().axes().front();
0503 Axis axisYExpected{AxisBound, -100_mm, 100_mm, 3};
0504 BOOST_CHECK_EQUAL(axisY, axisYExpected);
0505
0506
0507 auto checkAllBins = [&](const auto& grid) {
0508 visitBins(grid, [&](const TrackingVolume* content) {
0509 BOOST_CHECK_EQUAL(content, vol.get());
0510 });
0511 };
0512
0513 gridX->setVolume(vol.get());
0514 checkAllBins(*gridX);
0515
0516 gridY->setVolume(vol.get());
0517 checkAllBins(*gridY);
0518
0519
0520
0521
0522 auto gridX2d = gridX->extendTo2d(nullptr);
0523 BOOST_REQUIRE(gridX2d);
0524 BOOST_CHECK_EQUAL(gridX2d->grid().axes().size(), 2);
0525 const auto* axisX1 = gridX2d->grid().axes().front();
0526 BOOST_CHECK_EQUAL(*axisX1, axisXExpected);
0527 const auto* axisX2 = gridX2d->grid().axes().back();
0528 BOOST_CHECK_EQUAL(axisX2->getMin(), -100_mm);
0529 BOOST_CHECK_EQUAL(axisX2->getMax(), 100_mm);
0530 BOOST_CHECK_EQUAL(axisX2->getNBins(), 1);
0531 BOOST_CHECK_EQUAL(axisX2->getType(), AxisType::Equidistant);
0532 BOOST_CHECK_EQUAL(axisX2->getBoundaryType(), AxisBoundaryType::Bound);
0533
0534
0535 Axis axisYExplicit{AxisClosed, -100_mm, 100_mm, 3};
0536 auto gridX2dExplicit = gridX->extendTo2d(&axisYExplicit);
0537 BOOST_REQUIRE(gridX2dExplicit);
0538 BOOST_CHECK_EQUAL(gridX2dExplicit->grid().axes().size(), 2);
0539 axisX1 = gridX2dExplicit->grid().axes().front();
0540 axisX2 = gridX2dExplicit->grid().axes().back();
0541 BOOST_CHECK_EQUAL(*axisX1, axisXExpected);
0542 BOOST_CHECK_EQUAL(*axisX2, axisYExplicit);
0543
0544 checkAllBins(
0545 dynamic_cast<
0546 GridPortalLinkT<decltype(axisXExpected), decltype(axisYExplicit)>&>(
0547 *gridX2dExplicit));
0548
0549
0550 auto gridY2d = gridY->extendTo2d(nullptr);
0551 BOOST_REQUIRE(gridY2d);
0552 BOOST_CHECK_EQUAL(gridY2d->grid().axes().size(), 2);
0553 const auto* axisY1 = gridY2d->grid().axes().front();
0554 BOOST_CHECK_EQUAL(axisY1->getMin(), -30_mm);
0555 BOOST_CHECK_EQUAL(axisY1->getMax(), 30_mm);
0556 BOOST_CHECK_EQUAL(axisY1->getNBins(), 1);
0557 BOOST_CHECK_EQUAL(axisY1->getType(), AxisType::Equidistant);
0558 BOOST_CHECK_EQUAL(axisY1->getBoundaryType(), AxisBoundaryType::Bound);
0559 const auto* axisY2 = gridY2d->grid().axes().back();
0560 BOOST_CHECK_EQUAL(*axisY2, axisYExpected);
0561
0562
0563 Axis axisXExplicit{AxisClosed, -30_mm, 30_mm, 3};
0564 auto gridY2dExplicit = gridY->extendTo2d(&axisXExplicit);
0565 BOOST_REQUIRE(gridY2dExplicit);
0566 BOOST_CHECK_EQUAL(gridY2dExplicit->grid().axes().size(), 2);
0567 axisY1 = gridY2dExplicit->grid().axes().front();
0568 axisY2 = gridY2dExplicit->grid().axes().back();
0569 BOOST_CHECK_EQUAL(*axisY1, axisXExplicit);
0570 BOOST_CHECK_EQUAL(*axisY2, axisYExpected);
0571
0572 checkAllBins(
0573 dynamic_cast<
0574 GridPortalLinkT<decltype(axisXExplicit), decltype(axisYExpected)>&>(
0575 *gridY2dExplicit));
0576 }
0577 BOOST_TEST_CONTEXT("2D") {
0578 auto rBounds = std::make_shared<const RectangleBounds>(30_mm, 100_mm);
0579
0580 auto plane =
0581 Surface::makeShared<PlaneSurface>(Transform3::Identity(), rBounds);
0582
0583 Axis xAxis{AxisBound, -30_mm, 30_mm, 5};
0584 Axis yAxis{AxisBound, -100_mm, 100_mm, 5};
0585
0586 auto grid = GridPortalLink::make(plane, xAxis, yAxis);
0587 BOOST_REQUIRE_NE(grid, nullptr);
0588 }
0589 }
0590
0591 BOOST_AUTO_TEST_CASE(FromTrivial) {
0592 BOOST_TEST_CONTEXT("Cylinder") {
0593 auto cyl = Surface::makeShared<CylinderSurface>(Transform3::Identity(),
0594 30_mm, 100_mm);
0595
0596 auto vol = std::make_shared<TrackingVolume>(
0597 Transform3::Identity(),
0598 std::make_shared<CylinderVolumeBounds>(30_mm, 40_mm, 100_mm));
0599
0600 auto trivial = std::make_unique<TrivialPortalLink>(cyl, *vol);
0601 BOOST_REQUIRE(trivial);
0602
0603 BOOST_CHECK_EQUAL(trivial->resolveVolume(gctx, Vector2{1, 2}).value(),
0604 vol.get());
0605
0606 auto gridZ = trivial->makeGrid(AxisDirection::AxisZ);
0607 BOOST_REQUIRE(gridZ);
0608
0609 BOOST_CHECK_EQUAL(gridZ->grid().axes().size(), 1);
0610 BOOST_CHECK_EQUAL(gridZ->surface().bounds(), cyl->bounds());
0611 Axis axisZExpected{AxisBound, -100_mm, 100_mm, 1};
0612 BOOST_CHECK_EQUAL(*gridZ->grid().axes().front(), axisZExpected);
0613
0614 BOOST_CHECK_EQUAL(
0615 gridZ->resolveVolume(gctx, Vector2{20_degree * 30_mm, 90_mm}).value(),
0616 vol.get());
0617
0618 auto gridRPhi = trivial->makeGrid(AxisDirection::AxisRPhi);
0619 BOOST_REQUIRE(gridRPhi);
0620
0621 BOOST_CHECK_EQUAL(gridRPhi->grid().axes().size(), 1);
0622 BOOST_CHECK_EQUAL(gridRPhi->surface().bounds(), cyl->bounds());
0623 Axis axisRPhiExpected{AxisClosed, -std::numbers::pi * 30_mm,
0624 std::numbers::pi * 30_mm, 1};
0625 BOOST_CHECK_EQUAL(*gridRPhi->grid().axes().front(), axisRPhiExpected);
0626
0627 auto cylPhi = Surface::makeShared<CylinderSurface>(
0628 Transform3::Identity(), 30_mm, 100_mm, 30_degree);
0629
0630 auto trivialPhi = std::make_unique<TrivialPortalLink>(cylPhi, *vol);
0631 BOOST_REQUIRE(trivialPhi);
0632
0633 BOOST_CHECK_EQUAL(trivialPhi->resolveVolume(gctx, Vector2{1, 2}).value(),
0634 vol.get());
0635
0636 auto gridRPhiSector = trivialPhi->makeGrid(AxisDirection::AxisRPhi);
0637 BOOST_REQUIRE(gridRPhiSector);
0638
0639 BOOST_CHECK_EQUAL(
0640 gridRPhiSector->resolveVolume(gctx, Vector2{20_degree * 30_mm, 90_mm})
0641 .value(),
0642 vol.get());
0643
0644 BOOST_CHECK_EQUAL(gridRPhiSector->grid().axes().size(), 1);
0645 Axis axisRPhiSectorExpected{AxisBound, -30_degree * 30_mm,
0646 30_degree * 30_mm, 1};
0647 BOOST_CHECK_EQUAL(*gridRPhiSector->grid().axes().front(),
0648 axisRPhiSectorExpected);
0649 }
0650
0651 BOOST_TEST_CONTEXT("Disc") {
0652 auto disc =
0653 Surface::makeShared<DiscSurface>(Transform3::Identity(), 30_mm, 100_mm);
0654
0655 auto vol = std::make_shared<TrackingVolume>(
0656 Transform3::Identity(),
0657 std::make_shared<CylinderVolumeBounds>(30_mm, 40_mm, 100_mm));
0658
0659 auto trivial = std::make_unique<TrivialPortalLink>(disc, *vol);
0660 BOOST_REQUIRE(trivial);
0661
0662
0663 BOOST_CHECK_EQUAL(trivial->resolveVolume(gctx, Vector2{1, 2}).value(),
0664 vol.get());
0665
0666 auto gridR = trivial->makeGrid(AxisDirection::AxisR);
0667 BOOST_REQUIRE(gridR);
0668
0669 BOOST_CHECK_EQUAL(gridR->grid().axes().size(), 1);
0670 BOOST_CHECK_EQUAL(gridR->surface().bounds(), disc->bounds());
0671 Axis axisRExpected{AxisBound, 30_mm, 100_mm, 1};
0672 BOOST_CHECK_EQUAL(*gridR->grid().axes().front(), axisRExpected);
0673
0674 BOOST_CHECK_EQUAL(
0675 gridR->resolveVolume(gctx, Vector2{90_mm, 10_degree}).value(),
0676 vol.get());
0677
0678 auto gridPhi = trivial->makeGrid(AxisDirection::AxisPhi);
0679 BOOST_REQUIRE(gridPhi);
0680
0681 BOOST_CHECK_EQUAL(gridPhi->grid().axes().size(), 1);
0682 BOOST_CHECK_EQUAL(gridPhi->surface().bounds(), disc->bounds());
0683 Axis axisPhiExpected{AxisClosed, -std::numbers::pi, std::numbers::pi, 1};
0684 BOOST_CHECK_EQUAL(*gridPhi->grid().axes().front(), axisPhiExpected);
0685
0686 BOOST_CHECK_EQUAL(
0687 gridPhi->resolveVolume(gctx, Vector2{90_mm, 10_degree}).value(),
0688 vol.get());
0689 }
0690 BOOST_TEST_CONTEXT("Plane") {
0691 auto rBounds = std::make_shared<const RectangleBounds>(30_mm, 100_mm);
0692
0693 auto plane =
0694 Surface::makeShared<PlaneSurface>(Transform3::Identity(), rBounds);
0695
0696 auto vol = std::make_shared<TrackingVolume>(
0697 Transform3::Identity(),
0698 std::make_shared<CuboidVolumeBounds>(30_mm, 40_mm, 100_mm));
0699
0700 auto trivial = std::make_unique<TrivialPortalLink>(plane, *vol);
0701 BOOST_REQUIRE(trivial);
0702
0703
0704 BOOST_CHECK_EQUAL(
0705 trivial->resolveVolume(gctx, Vector2{10_mm, 20_mm}).value(), vol.get());
0706
0707 auto gridX = trivial->makeGrid(AxisDirection::AxisX);
0708 BOOST_REQUIRE(gridX);
0709
0710 BOOST_CHECK_EQUAL(gridX->grid().axes().size(), 1);
0711 BOOST_CHECK_EQUAL(gridX->surface().bounds(), plane->bounds());
0712 Axis axisXExpected{AxisBound, -30_mm, 30_mm, 1};
0713 BOOST_CHECK_EQUAL(*gridX->grid().axes().front(), axisXExpected);
0714
0715 BOOST_CHECK_EQUAL(gridX->resolveVolume(gctx, Vector2{20_mm, 10_mm}).value(),
0716 vol.get());
0717
0718 auto gridY = trivial->makeGrid(AxisDirection::AxisY);
0719 BOOST_REQUIRE(gridY);
0720
0721 BOOST_CHECK_EQUAL(gridY->grid().axes().size(), 1);
0722 BOOST_CHECK_EQUAL(gridY->surface().bounds(), plane->bounds());
0723 Axis axisYExpected{AxisBound, -100_mm, 100_mm, 1};
0724 BOOST_CHECK_EQUAL(*gridY->grid().axes().front(), axisYExpected);
0725
0726 BOOST_CHECK_EQUAL(gridY->resolveVolume(gctx, Vector2{15_mm, 20_mm}).value(),
0727 vol.get());
0728 }
0729 }
0730
0731 BOOST_AUTO_TEST_SUITE_END()
0732
0733 BOOST_AUTO_TEST_SUITE(GridMerging)
0734
0735 BOOST_AUTO_TEST_SUITE(Merging1dCylinder)
0736
0737 BOOST_AUTO_TEST_SUITE(ZDirection)
0738
0739 BOOST_AUTO_TEST_CASE(ColinearMerge) {
0740 auto vol1 = makeDummyVolume();
0741 auto vol2 = makeDummyVolume();
0742
0743 auto cyl = Surface::makeShared<CylinderSurface>(Transform3::Identity(), 30_mm,
0744 100_mm);
0745
0746 auto grid1dCyl = GridPortalLink::make(cyl, AxisDirection::AxisZ,
0747 Axis{AxisBound, -100_mm, 100_mm, 10});
0748 grid1dCyl->setVolume(vol1.get());
0749
0750
0751 auto cyl2 = Surface::makeShared<CylinderSurface>(
0752 Transform3{Translation3{Vector3::UnitZ() * 150_mm}}, 30_mm, 50_mm);
0753
0754 auto grid1dCyl2 = GridPortalLink::make(cyl2, AxisDirection::AxisZ,
0755 Axis{AxisBound, -50_mm, 50_mm, 5});
0756
0757 grid1dCyl2->setVolume(vol2.get());
0758
0759
0760 BOOST_CHECK_THROW(GridPortalLink::merge(*grid1dCyl, *grid1dCyl2,
0761 AxisDirection::AxisPhi, *logger),
0762 AssertionFailureException);
0763
0764
0765 BOOST_CHECK_THROW(GridPortalLink::merge(*grid1dCyl, *grid1dCyl2,
0766 AxisDirection::AxisRPhi, *logger),
0767 SurfaceMergingException);
0768
0769 BOOST_TEST_CONTEXT("Consistent equidistant") {
0770 auto mergedPtr = GridPortalLink::merge(*grid1dCyl, *grid1dCyl2,
0771 AxisDirection::AxisZ, *logger);
0772 BOOST_REQUIRE_NE(mergedPtr, nullptr);
0773 const auto* merged = dynamic_cast<const GridPortalLink*>(mergedPtr.get());
0774 BOOST_REQUIRE_NE(merged, nullptr);
0775 BOOST_CHECK_EQUAL(merged->grid().axes().size(), 1);
0776 const auto& axis = *merged->grid().axes().front();
0777 BOOST_CHECK_EQUAL(axis.getMin(), -150_mm);
0778 BOOST_CHECK_EQUAL(axis.getMax(), 150_mm);
0779 BOOST_CHECK_EQUAL(axis.getNBins(), 15);
0780 BOOST_CHECK_EQUAL(axis.getType(), AxisType::Equidistant);
0781 BOOST_CHECK_EQUAL(axis.getBoundaryType(), AxisBoundaryType::Bound);
0782 }
0783
0784 BOOST_TEST_CONTEXT("Inconsistent equidistant") {
0785 auto grid1dCyl2BinWidthChanged = GridPortalLink::make(
0786 cyl2, AxisDirection::AxisZ, Axis{AxisBound, -50_mm, 50_mm, 6});
0787
0788 auto mergedPtr = GridPortalLink::merge(
0789 *grid1dCyl, *grid1dCyl2BinWidthChanged, AxisDirection::AxisZ, *logger);
0790 BOOST_REQUIRE_NE(mergedPtr, nullptr);
0791 const auto* merged = dynamic_cast<const GridPortalLink*>(mergedPtr.get());
0792 BOOST_REQUIRE_NE(merged, nullptr);
0793 BOOST_CHECK_EQUAL(merged->grid().axes().size(), 1);
0794 const auto& axis = *merged->grid().axes().front();
0795 BOOST_CHECK_EQUAL(axis.getMin(), -150_mm);
0796 BOOST_CHECK_EQUAL(axis.getMax(), 150_mm);
0797 BOOST_CHECK_EQUAL(axis.getNBins(), 16);
0798 BOOST_CHECK_EQUAL(axis.getType(), AxisType::Variable);
0799 BOOST_CHECK_EQUAL(axis.getBoundaryType(), AxisBoundaryType::Bound);
0800 }
0801
0802 BOOST_TEST_CONTEXT("Right Variable") {
0803 auto gridLeft = GridPortalLink::make(cyl, AxisDirection::AxisZ,
0804 Axis{AxisBound, -100_mm, 100_mm, 10});
0805
0806 auto gridRight =
0807 GridPortalLink::make(cyl2, AxisDirection::AxisZ,
0808 Axis{AxisBound, {-50_mm, -10_mm, 10_mm, 50_mm}});
0809
0810 auto mergedPtr = GridPortalLink::merge(*gridLeft, *gridRight,
0811 AxisDirection::AxisZ, *logger);
0812 BOOST_REQUIRE_NE(mergedPtr, nullptr);
0813 const auto* merged = dynamic_cast<const GridPortalLink*>(mergedPtr.get());
0814 BOOST_REQUIRE_NE(merged, nullptr);
0815 BOOST_CHECK_EQUAL(merged->grid().axes().size(), 1);
0816 const auto& axis = *merged->grid().axes().front();
0817 BOOST_CHECK_EQUAL(axis.getMin(), -150_mm);
0818 BOOST_CHECK_EQUAL(axis.getMax(), 150_mm);
0819 BOOST_CHECK_EQUAL(axis.getNBins(), 13);
0820 BOOST_CHECK_EQUAL(axis.getType(), AxisType::Variable);
0821 BOOST_CHECK_EQUAL(axis.getBoundaryType(), AxisBoundaryType::Bound);
0822 }
0823
0824 BOOST_TEST_CONTEXT("Left Variable") {
0825 auto gridLeft =
0826 GridPortalLink::make(cyl, AxisDirection::AxisZ,
0827 Axis{AxisBound, {-100_mm, -80_mm, 10_mm, 100_mm}});
0828
0829 auto gridRight = GridPortalLink::make(cyl2, AxisDirection::AxisZ,
0830 Axis{AxisBound, -50_mm, 50_mm, 8});
0831
0832 auto mergedPtr = GridPortalLink::merge(*gridLeft, *gridRight,
0833 AxisDirection::AxisZ, *logger);
0834 BOOST_REQUIRE_NE(mergedPtr, nullptr);
0835 const auto* merged = dynamic_cast<const GridPortalLink*>(mergedPtr.get());
0836 BOOST_REQUIRE_NE(merged, nullptr);
0837 BOOST_CHECK_EQUAL(merged->grid().axes().size(), 1);
0838 const auto& axis = *merged->grid().axes().front();
0839 BOOST_CHECK_EQUAL(axis.getMin(), -150_mm);
0840 BOOST_CHECK_EQUAL(axis.getMax(), 150_mm);
0841 BOOST_CHECK_EQUAL(axis.getNBins(), 11);
0842 BOOST_CHECK_EQUAL(axis.getType(), AxisType::Variable);
0843 BOOST_CHECK_EQUAL(axis.getBoundaryType(), AxisBoundaryType::Bound);
0844 }
0845
0846 BOOST_TEST_CONTEXT("Both Variable") {
0847 auto gridLeft =
0848 GridPortalLink::make(cyl, AxisDirection::AxisZ,
0849 Axis{AxisBound, {-100_mm, -80_mm, 10_mm, 100_mm}});
0850
0851 auto gridRight =
0852 GridPortalLink::make(cyl2, AxisDirection::AxisZ,
0853 Axis{AxisBound, {-50_mm, -10_mm, 10_mm, 50_mm}});
0854
0855 auto mergedPtr = GridPortalLink::merge(*gridLeft, *gridRight,
0856 AxisDirection::AxisZ, *logger);
0857 BOOST_REQUIRE_NE(mergedPtr, nullptr);
0858 const auto* merged = dynamic_cast<const GridPortalLink*>(mergedPtr.get());
0859 BOOST_REQUIRE_NE(merged, nullptr);
0860 BOOST_CHECK_EQUAL(merged->grid().axes().size(), 1);
0861 const auto& axis = *merged->grid().axes().front();
0862 BOOST_CHECK_EQUAL(axis.getMin(), -150_mm);
0863 BOOST_CHECK_EQUAL(axis.getMax(), 150_mm);
0864 BOOST_CHECK_EQUAL(axis.getNBins(), 6);
0865 BOOST_CHECK_EQUAL(axis.getType(), AxisType::Variable);
0866 BOOST_CHECK_EQUAL(axis.getBoundaryType(), AxisBoundaryType::Bound);
0867 }
0868
0869 BOOST_TEST_CONTEXT("Non bound axis") {
0870 std::unique_ptr gridLeft =
0871 GridPortalLink::make(cyl, AxisDirection::AxisZ,
0872 Axis{AxisBound, {-100_mm, -80_mm, 10_mm, 100_mm}});
0873 std::unique_ptr gridRightClosed =
0874 GridPortalLink::make(cyl2, AxisDirection::AxisZ,
0875 Axis{AxisClosed, {-50_mm, -10_mm, 10_mm, 50_mm}});
0876 std::unique_ptr gridRightOpen =
0877 GridPortalLink::make(cyl2, AxisDirection::AxisZ,
0878 Axis{AxisOpen, {-50_mm, -10_mm, 10_mm, 50_mm}});
0879
0880 auto compositeLR = PortalLinkBase::merge(
0881 copy(gridLeft), copy(gridRightClosed), AxisDirection::AxisZ, *logger);
0882 BOOST_CHECK_NE(dynamic_cast<const CompositePortalLink*>(compositeLR.get()),
0883 nullptr);
0884 auto compositeRL = PortalLinkBase::merge(
0885 copy(gridLeft), copy(gridRightOpen), AxisDirection::AxisZ, *logger);
0886 BOOST_CHECK_NE(dynamic_cast<const CompositePortalLink*>(compositeRL.get()),
0887 nullptr);
0888 }
0889 }
0890
0891 BOOST_AUTO_TEST_CASE(ParallelMerge) {
0892
0893 auto cyl1 = Surface::makeShared<CylinderSurface>(Transform3::Identity(),
0894 30_mm, 100_mm, 35_degree);
0895
0896 auto grid1 = GridPortalLink::make(
0897 cyl1, AxisDirection::AxisRPhi,
0898
0899 Axis{AxisBound, -35_degree * 30_mm, 35_degree * 30_mm, 3});
0900 auto cyl2 = Surface::makeShared<CylinderSurface>(
0901 Transform3{Translation3{Vector3::UnitZ() * 150_mm}}, 30_mm, 50_mm,
0902 35_degree);
0903
0904 auto grid2 = GridPortalLink::make(
0905 cyl2, AxisDirection::AxisRPhi,
0906 Axis{AxisBound, -35_degree * 30_mm, 35_degree * 30_mm, 3});
0907
0908 auto merged12Ptr =
0909 GridPortalLink::merge(*grid1, *grid2, AxisDirection::AxisZ, *logger);
0910 BOOST_REQUIRE_NE(merged12Ptr, nullptr);
0911 auto merged12 = dynamic_cast<const GridPortalLink*>(merged12Ptr.get());
0912 BOOST_REQUIRE_NE(merged12, nullptr);
0913
0914 BOOST_REQUIRE_EQUAL(merged12->grid().axes().size(), 2);
0915
0916 const auto& axis1 = *merged12->grid().axes().front();
0917 const auto& axis2 = *merged12->grid().axes().back();
0918 Axis axis1Expected{AxisBound, -35_degree * 30_mm, 35_degree * 30_mm, 3};
0919 BOOST_CHECK_EQUAL(axis1, axis1Expected);
0920 Axis axis2Expected{AxisBound, {-150_mm, 50_mm, 150_mm}};
0921 BOOST_CHECK_EQUAL(axis2, axis2Expected);
0922 }
0923
0924 BOOST_AUTO_TEST_SUITE_END()
0925
0926 BOOST_AUTO_TEST_SUITE(RPhiDirection)
0927
0928 BOOST_AUTO_TEST_CASE(ColinearMerge) {
0929 auto cyl = Surface::makeShared<CylinderSurface>(Transform3::Identity(), 30_mm,
0930 100_mm);
0931 BOOST_CHECK_THROW(GridPortalLink::make(cyl, AxisDirection::AxisRPhi,
0932 Axis{AxisBound, 0, 5, 5}),
0933 std::invalid_argument);
0934
0935 auto cylNonZeroAverage = Surface::makeShared<CylinderSurface>(
0936 Transform3::Identity(), 30_mm, 100_mm, 20_degree, 45_degree);
0937
0938 BOOST_CHECK_THROW(
0939 GridPortalLink::make(
0940 cylNonZeroAverage, AxisDirection::AxisRPhi,
0941 Axis{AxisBound, -20_degree * 30_mm, 20_degree * 30_mm, 5}),
0942 std::invalid_argument);
0943
0944 BOOST_TEST_CONTEXT("Colinear merge in rPhi") {
0945 auto cylPhi1 = Surface::makeShared<CylinderSurface>(
0946 Transform3::Identity() * AngleAxis3(45_degree, Vector3::UnitZ()), 30_mm,
0947 100_mm, 20_degree, 0_degree);
0948
0949 auto cylPhi2 = Surface::makeShared<CylinderSurface>(
0950 Transform3::Identity() * AngleAxis3(105_degree, Vector3::UnitZ()),
0951 30_mm, 100_mm, 40_degree, 0_degree);
0952
0953 auto portalPhi1 = GridPortalLink::make(
0954 cylPhi1, AxisDirection::AxisRPhi,
0955 Axis{AxisBound, -20_degree * 30_mm, 20_degree * 30_mm, 5});
0956
0957 auto portalPhi2 = GridPortalLink::make(
0958 cylPhi2, AxisDirection::AxisRPhi,
0959 Axis{AxisBound, -40_degree * 30_mm, 40_degree * 30_mm, 10});
0960
0961 auto cylPhi3 = Surface::makeShared<CylinderSurface>(
0962 Transform3::Identity() * AngleAxis3(45_degree, Vector3::UnitZ()), 30_mm,
0963 100_mm, 90_degree, 0_degree);
0964
0965 auto cylPhi4 = Surface::makeShared<CylinderSurface>(
0966 Transform3::Identity() * AngleAxis3(-135_degree, Vector3::UnitZ()),
0967 30_mm, 100_mm, 90_degree, 0_degree);
0968
0969 auto portalPhi3 = GridPortalLink::make(
0970 cylPhi3, AxisDirection::AxisRPhi,
0971 Axis{AxisBound, -90_degree * 30_mm, 90_degree * 30_mm, 2});
0972
0973 auto portalPhi4 = GridPortalLink::make(
0974 cylPhi4, AxisDirection::AxisRPhi,
0975 Axis{AxisBound, -90_degree * 30_mm, 90_degree * 30_mm, 2});
0976
0977 BOOST_TEST_CONTEXT("Consistent equidistant") {
0978 auto portalMerged = GridPortalLink::merge(
0979 *portalPhi1, *portalPhi2, AxisDirection::AxisRPhi, *logger);
0980 BOOST_REQUIRE_NE(portalMerged, nullptr);
0981
0982 const auto* merged =
0983 dynamic_cast<const GridPortalLink*>(portalMerged.get());
0984 BOOST_REQUIRE_NE(merged, nullptr);
0985 BOOST_CHECK_EQUAL(merged->grid().axes().size(), 1);
0986 const auto& axis = *merged->grid().axes().front();
0987 BOOST_CHECK_CLOSE(axis.getMin(), -60_degree * 30_mm, 1e-9);
0988 BOOST_CHECK_CLOSE(axis.getMax(), 60_degree * 30_mm, 1e-9);
0989 BOOST_CHECK_EQUAL(axis.getNBins(), 15);
0990 BOOST_CHECK_EQUAL(axis.getType(), AxisType::Equidistant);
0991 BOOST_CHECK_EQUAL(axis.getBoundaryType(), AxisBoundaryType::Bound);
0992
0993
0994 auto portalMerged34 = GridPortalLink::merge(
0995 *portalPhi3, *portalPhi4, AxisDirection::AxisRPhi, *logger);
0996 BOOST_REQUIRE_NE(portalMerged34, nullptr);
0997
0998 const auto* merged34 =
0999 dynamic_cast<const GridPortalLink*>(portalMerged34.get());
1000 BOOST_REQUIRE_NE(merged34, nullptr);
1001 BOOST_CHECK_EQUAL(merged34->grid().axes().size(), 1);
1002 const auto& axis34 = *merged34->grid().axes().front();
1003 BOOST_CHECK_CLOSE(axis34.getMin(), -180_degree * 30_mm, 1e-9);
1004 BOOST_CHECK_CLOSE(axis34.getMax(), 180_degree * 30_mm, 1e-9);
1005 BOOST_CHECK_EQUAL(axis34.getNBins(), 4);
1006 BOOST_CHECK_EQUAL(axis34.getType(), AxisType::Equidistant);
1007 BOOST_CHECK_EQUAL(axis34.getBoundaryType(), AxisBoundaryType::Closed);
1008 }
1009
1010 BOOST_TEST_CONTEXT("Inconsistent equidistant") {
1011 auto portalPhi2Mod = GridPortalLink::make(
1012 cylPhi2, AxisDirection::AxisRPhi,
1013 Axis{AxisBound, -40_degree * 30_mm, 40_degree * 30_mm, 3});
1014
1015 auto portalMergedMod = GridPortalLink::merge(
1016 *portalPhi1, *portalPhi2Mod, AxisDirection::AxisRPhi, *logger);
1017 BOOST_REQUIRE_NE(portalMergedMod, nullptr);
1018
1019 const auto* merged12 =
1020 dynamic_cast<const GridPortalLink*>(portalMergedMod.get());
1021 BOOST_REQUIRE_NE(merged12, nullptr);
1022 BOOST_CHECK_EQUAL(merged12->grid().axes().size(), 1);
1023 const auto& axis12 = *merged12->grid().axes().front();
1024 BOOST_CHECK_CLOSE(axis12.getMin(), -60_degree * 30_mm, 1e-9);
1025 BOOST_CHECK_CLOSE(axis12.getMax(), 60_degree * 30_mm, 1e-9);
1026 BOOST_CHECK_EQUAL(axis12.getNBins(), 8);
1027 BOOST_CHECK_EQUAL(axis12.getType(), AxisType::Variable);
1028 BOOST_CHECK_EQUAL(axis12.getBoundaryType(), AxisBoundaryType::Bound);
1029
1030 std::vector<double> expected12 = {-31.4159, -17.4533, -3.49066,
1031 10.472, 14.6608, 18.8496,
1032 23.0383, 27.2271, 31.4159};
1033 CHECK_CLOSE_OR_SMALL(axis12.getBinEdges(), expected12, 1e-4, 10e-10);
1034
1035 auto portalPhi4Mod = GridPortalLink::make(
1036 cylPhi4, AxisDirection::AxisRPhi,
1037 Axis{AxisBound, -90_degree * 30_mm, 90_degree * 30_mm, 1});
1038
1039 auto portalMerged34 = GridPortalLink::merge(
1040 *portalPhi3, *portalPhi4Mod, AxisDirection::AxisRPhi, *logger);
1041 BOOST_REQUIRE_NE(portalMerged34, nullptr);
1042
1043 const auto* merged34 =
1044 dynamic_cast<const GridPortalLink*>(portalMerged34.get());
1045 BOOST_REQUIRE_NE(merged34, nullptr);
1046 BOOST_CHECK_EQUAL(merged34->grid().axes().size(), 1);
1047 const auto& axis34 = *merged34->grid().axes().front();
1048 BOOST_CHECK_CLOSE(axis34.getMin(), -180_degree * 30_mm, 1e-9);
1049 BOOST_CHECK_CLOSE(axis34.getMax(), 180_degree * 30_mm, 1e-9);
1050 BOOST_CHECK_EQUAL(axis34.getNBins(), 3);
1051 BOOST_CHECK_EQUAL(axis34.getType(), AxisType::Variable);
1052 BOOST_CHECK_EQUAL(axis34.getBoundaryType(), AxisBoundaryType::Closed);
1053
1054
1055
1056 std::vector<double> expected34 = {-94.2478, -47.1239, 0, 94.2478};
1057 CHECK_CLOSE_OR_SMALL(axis34.getBinEdges(), expected34, 1e-4, 10e-10);
1058 }
1059
1060 BOOST_TEST_CONTEXT("Left variable") {
1061 BOOST_TEST_CONTEXT("Non-closed") {
1062 auto gridLeft =
1063 GridPortalLink::make(cylPhi1, AxisDirection::AxisRPhi,
1064 Axis{AxisBound,
1065 {-20_degree * 30_mm, -10_degree * 30_mm,
1066 10_degree * 30_mm, 20_degree * 30_mm}});
1067 auto gridRight = GridPortalLink::make(
1068 cylPhi2, AxisDirection::AxisRPhi,
1069 Axis{AxisBound, -40_degree * 30_mm, 40_degree * 30_mm, 3});
1070
1071 auto mergedPtr = GridPortalLink::merge(
1072 *gridLeft, *gridRight, AxisDirection::AxisRPhi, *logger);
1073 BOOST_REQUIRE_NE(mergedPtr, nullptr);
1074
1075 const auto* merged =
1076 dynamic_cast<const GridPortalLink*>(mergedPtr.get());
1077 BOOST_REQUIRE_NE(merged, nullptr);
1078 const auto& axis = *merged->grid().axes().front();
1079 BOOST_CHECK_EQUAL(merged->grid().axes().size(), 1);
1080 BOOST_CHECK_CLOSE(axis.getMin(), -60_degree * 30_mm, 1e-9);
1081 BOOST_CHECK_CLOSE(axis.getMax(), 60_degree * 30_mm, 1e-9);
1082 BOOST_CHECK_EQUAL(axis.getNBins(), 6);
1083 BOOST_CHECK_EQUAL(axis.getType(), AxisType::Variable);
1084 BOOST_CHECK_EQUAL(axis.getBoundaryType(), AxisBoundaryType::Bound);
1085
1086 std::vector<double> expected = {-31.4159, -17.4533, -3.49066, 10.472,
1087 15.708, 26.1799, 31.4159};
1088 CHECK_CLOSE_OR_SMALL(axis.getBinEdges(), expected, 1e-4, 10e-10);
1089 }
1090
1091 BOOST_TEST_CONTEXT("Closed") {
1092 auto gridLeft = GridPortalLink::make(
1093 cylPhi4, AxisDirection::AxisRPhi,
1094 Axis{AxisBound,
1095 {-90_degree * 30_mm, 25_degree * 30_mm, 90_degree * 30_mm}});
1096
1097 auto gridRight = GridPortalLink::make(
1098 cylPhi3, AxisDirection::AxisRPhi,
1099 Axis{AxisBound, -90_degree * 30_mm, 90_degree * 30_mm, 3});
1100
1101 auto mergedPtr = GridPortalLink::merge(
1102 *gridLeft, *gridRight, AxisDirection::AxisRPhi, *logger);
1103 BOOST_REQUIRE_NE(mergedPtr, nullptr);
1104
1105 const auto* merged =
1106 dynamic_cast<const GridPortalLink*>(mergedPtr.get());
1107 BOOST_REQUIRE_NE(merged, nullptr);
1108 BOOST_CHECK_EQUAL(merged->grid().axes().size(), 1);
1109 const auto& axis = *merged->grid().axes().front();
1110 BOOST_CHECK_CLOSE(axis.getMin(), -180_degree * 30_mm, 1e-9);
1111 BOOST_CHECK_CLOSE(axis.getMax(), 180_degree * 30_mm, 1e-9);
1112 BOOST_CHECK_EQUAL(axis.getNBins(), 5);
1113 BOOST_CHECK_EQUAL(axis.getType(), AxisType::Variable);
1114 BOOST_CHECK_EQUAL(axis.getBoundaryType(), AxisBoundaryType::Closed);
1115
1116
1117
1118 std::vector<double> expected = {-94.2478, -34.0339, 0,
1119 31.4159, 62.8319, 94.2478};
1120 CHECK_CLOSE_OR_SMALL(axis.getBinEdges(), expected, 1e-4, 10e-10);
1121 }
1122 }
1123
1124 BOOST_TEST_CONTEXT("Right variable") {
1125 BOOST_TEST_CONTEXT("Non-closed") {
1126 auto gridLeft = GridPortalLink::make(
1127 cylPhi1, AxisDirection::AxisRPhi,
1128 Axis{AxisBound, -20_degree * 30_mm, 20_degree * 30_mm, 3});
1129 auto gridRight =
1130 GridPortalLink::make(cylPhi2, AxisDirection::AxisRPhi,
1131 Axis{AxisBound,
1132 {-40_degree * 30_mm, -10_degree * 30_mm,
1133 10_degree * 30_mm, 40_degree * 30_mm}});
1134
1135 auto mergedPtr = GridPortalLink::merge(
1136 *gridLeft, *gridRight, AxisDirection::AxisRPhi, *logger);
1137 BOOST_REQUIRE_NE(mergedPtr, nullptr);
1138
1139 const auto* merged =
1140 dynamic_cast<const GridPortalLink*>(mergedPtr.get());
1141 BOOST_REQUIRE_NE(merged, nullptr);
1142 const auto& axis = *merged->grid().axes().front();
1143 BOOST_CHECK_EQUAL(merged->grid().axes().size(), 1);
1144 BOOST_CHECK_CLOSE(axis.getMin(), -60_degree * 30_mm, 1e-9);
1145 BOOST_CHECK_CLOSE(axis.getMax(), 60_degree * 30_mm, 1e-9);
1146 BOOST_CHECK_EQUAL(axis.getNBins(), 6);
1147 BOOST_CHECK_EQUAL(axis.getType(), AxisType::Variable);
1148 BOOST_CHECK_EQUAL(axis.getBoundaryType(), AxisBoundaryType::Bound);
1149
1150 std::vector<double> expected = {-31.4159, -15.708, -5.23599, 10.472,
1151 17.4533, 24.4346, 31.4159};
1152 CHECK_CLOSE_OR_SMALL(axis.getBinEdges(), expected, 1e-4, 10e-10);
1153 }
1154
1155 BOOST_TEST_CONTEXT("Closed") {
1156 auto gridLeft = GridPortalLink::make(
1157 cylPhi4, AxisDirection::AxisRPhi,
1158 Axis{AxisBound, -90_degree * 30_mm, 90_degree * 30_mm, 3});
1159
1160 auto gridRight = GridPortalLink::make(
1161 cylPhi3, AxisDirection::AxisRPhi,
1162 Axis{AxisBound,
1163 {-90_degree * 30_mm, 25_degree * 30_mm, 90_degree * 30_mm}});
1164
1165 auto mergedPtr = GridPortalLink::merge(
1166 *gridLeft, *gridRight, AxisDirection::AxisRPhi, *logger);
1167 BOOST_REQUIRE_NE(mergedPtr, nullptr);
1168
1169 const auto* merged =
1170 dynamic_cast<const GridPortalLink*>(mergedPtr.get());
1171 BOOST_REQUIRE_NE(merged, nullptr);
1172 BOOST_CHECK_EQUAL(merged->grid().axes().size(), 1);
1173 const auto& axis = *merged->grid().axes().front();
1174 BOOST_CHECK_CLOSE(axis.getMin(), -180_degree * 30_mm, 1e-9);
1175 BOOST_CHECK_CLOSE(axis.getMax(), 180_degree * 30_mm, 1e-9);
1176 BOOST_CHECK_EQUAL(axis.getNBins(), 5);
1177 BOOST_CHECK_EQUAL(axis.getType(), AxisType::Variable);
1178 BOOST_CHECK_EQUAL(axis.getBoundaryType(), AxisBoundaryType::Closed);
1179
1180
1181
1182 std::vector<double> expected = {-94.2478, -62.8319, -31.4159,
1183 0, 60.2139, 94.2478};
1184 CHECK_CLOSE_OR_SMALL(axis.getBinEdges(), expected, 1e-4, 10e-10);
1185 }
1186 }
1187
1188 BOOST_TEST_CONTEXT("Both variable") {
1189 BOOST_TEST_CONTEXT("Non-closed") {
1190 auto gridLeft =
1191 GridPortalLink::make(cylPhi1, AxisDirection::AxisRPhi,
1192 Axis{AxisBound,
1193 {-20_degree * 30_mm, -10_degree * 30_mm,
1194 10_degree * 30_mm, 20_degree * 30_mm}});
1195 auto gridRight = GridPortalLink::make(
1196 cylPhi2, AxisDirection::AxisRPhi,
1197 Axis{AxisBound,
1198 {-40_degree * 30_mm, -5_degree * 30_mm, 40_degree * 30_mm}});
1199
1200 auto mergedPtr = GridPortalLink::merge(
1201 *gridLeft, *gridRight, AxisDirection::AxisRPhi, *logger);
1202 BOOST_REQUIRE_NE(mergedPtr, nullptr);
1203
1204 const auto* merged =
1205 dynamic_cast<const GridPortalLink*>(mergedPtr.get());
1206 BOOST_REQUIRE_NE(merged, nullptr);
1207 const auto& axis = *merged->grid().axes().front();
1208 BOOST_CHECK_EQUAL(merged->grid().axes().size(), 1);
1209 BOOST_CHECK_CLOSE(axis.getMin(), -60_degree * 30_mm, 1e-9);
1210 BOOST_CHECK_CLOSE(axis.getMax(), 60_degree * 30_mm, 1e-9);
1211 BOOST_CHECK_EQUAL(axis.getNBins(), 5);
1212 BOOST_CHECK_EQUAL(axis.getType(), AxisType::Variable);
1213 BOOST_CHECK_EQUAL(axis.getBoundaryType(), AxisBoundaryType::Bound);
1214
1215 std::vector<double> expected = {-31.4159, -13.09, 10.472,
1216 15.708, 26.1799, 31.4159};
1217 CHECK_CLOSE_OR_SMALL(axis.getBinEdges(), expected, 1e-4, 10e-10);
1218 }
1219
1220 BOOST_TEST_CONTEXT("Closed") {
1221 auto gridLeft = GridPortalLink::make(
1222 cylPhi4, AxisDirection::AxisRPhi,
1223 Axis{AxisBound,
1224 {-90_degree * 30_mm, 25_degree * 30_mm, 90_degree * 30_mm}});
1225
1226 auto gridRight =
1227 GridPortalLink::make(cylPhi3, AxisDirection::AxisRPhi,
1228 Axis{AxisBound,
1229 {-90_degree * 30_mm, -10_degree * 30_mm,
1230 10_degree * 30_mm, 90_degree * 30_mm}});
1231
1232 auto mergedPtr = GridPortalLink::merge(
1233 *gridLeft, *gridRight, AxisDirection::AxisRPhi, *logger);
1234 BOOST_REQUIRE_NE(mergedPtr, nullptr);
1235
1236 const auto* merged =
1237 dynamic_cast<const GridPortalLink*>(mergedPtr.get());
1238 BOOST_REQUIRE_NE(merged, nullptr);
1239 BOOST_CHECK_EQUAL(merged->grid().axes().size(), 1);
1240 const auto& axis = *merged->grid().axes().front();
1241 BOOST_CHECK_CLOSE(axis.getMin(), -180_degree * 30_mm, 1e-9);
1242 BOOST_CHECK_CLOSE(axis.getMax(), 180_degree * 30_mm, 1e-9);
1243 BOOST_CHECK_EQUAL(axis.getNBins(), 5);
1244 BOOST_CHECK_EQUAL(axis.getType(), AxisType::Variable);
1245 BOOST_CHECK_EQUAL(axis.getBoundaryType(), AxisBoundaryType::Closed);
1246
1247
1248
1249 std::vector<double> expected = {-94.2478, -34.0339, 0,
1250 41.8879, 52.3599, 94.2478};
1251 CHECK_CLOSE_OR_SMALL(axis.getBinEdges(), expected, 1e-4, 10e-10);
1252 }
1253 }
1254 }
1255 }
1256
1257 BOOST_AUTO_TEST_CASE(ParallelMerge) {
1258
1259 auto cylPhi1 = Surface::makeShared<CylinderSurface>(
1260 Transform3::Identity() * AngleAxis3(45_degree, Vector3::UnitZ()), 30_mm,
1261 100_mm, 20_degree, 0_degree);
1262
1263 auto cylPhi2 = Surface::makeShared<CylinderSurface>(
1264 Transform3::Identity() * AngleAxis3(85_degree, Vector3::UnitZ()), 30_mm,
1265 100_mm, 20_degree, 0_degree);
1266
1267 auto portalPhi1 = GridPortalLink::make(cylPhi1, AxisDirection::AxisZ,
1268 Axis{AxisBound, -100_mm, 100_mm, 5});
1269
1270 auto portalPhi2 = GridPortalLink::make(cylPhi2, AxisDirection::AxisZ,
1271 Axis{AxisBound, -100_mm, 100_mm, 5});
1272
1273 auto merged12Ptr = GridPortalLink::merge(*portalPhi1, *portalPhi2,
1274 AxisDirection::AxisRPhi, *logger);
1275 BOOST_REQUIRE_NE(merged12Ptr, nullptr);
1276 auto merged12 = dynamic_cast<const GridPortalLink*>(merged12Ptr.get());
1277 BOOST_REQUIRE_NE(merged12, nullptr);
1278
1279 const auto& axis1 = *merged12->grid().axes().front();
1280 const auto& axis2 = *merged12->grid().axes().back();
1281
1282 Axis axis1Expected{AxisBound, -40_degree * 30_mm, 40_degree * 30_mm, 2};
1283 BOOST_CHECK_EQUAL(axis1, axis1Expected);
1284 Axis axis2Expected{AxisBound, -100_mm, 100_mm, 5};
1285 BOOST_CHECK_EQUAL(axis2, axis2Expected);
1286 }
1287
1288 BOOST_AUTO_TEST_SUITE_END()
1289
1290 BOOST_AUTO_TEST_SUITE_END()
1291
1292 BOOST_AUTO_TEST_SUITE(Merging2dCylinder)
1293
1294 BOOST_AUTO_TEST_CASE(ZDirection) {
1295 BOOST_TEST_CONTEXT("Phi sector") {
1296 auto cyl1 = Surface::makeShared<CylinderSurface>(Transform3::Identity(),
1297 30_mm, 100_mm, 45_degree);
1298
1299
1300 auto grid1 = GridPortalLink::make(
1301 cyl1, Axis{AxisBound, -45_degree * 30_mm, 45_degree * 30_mm, 5},
1302 Axis{AxisBound, -100_mm, 100_mm, 5});
1303
1304 auto trf2 = Transform3{Translation3{Vector3::UnitZ() * 150_mm}};
1305 auto cyl2 =
1306 Surface::makeShared<CylinderSurface>(trf2, 30_mm, 50_mm, 45_degree);
1307
1308
1309 auto grid2 = GridPortalLink::make(
1310 cyl2, Axis{AxisBound, -45_degree * 30_mm, 45_degree * 30_mm, 5},
1311 Axis{AxisBound, -50_mm, 50_mm, 5});
1312
1313
1314
1315 auto mergedPtr =
1316 GridPortalLink::merge(*grid1, *grid2, AxisDirection::AxisZ, *logger);
1317 const auto* merged = dynamic_cast<const GridPortalLink*>(mergedPtr.get());
1318 BOOST_REQUIRE_NE(mergedPtr, nullptr);
1319
1320 const auto& axis1 = *merged->grid().axes().front();
1321 const auto& axis2 = *merged->grid().axes().back();
1322
1323 BOOST_CHECK_EQUAL(axis1.getMin(), -45_degree * 30_mm);
1324 BOOST_CHECK_EQUAL(axis1.getMax(), 45_degree * 30_mm);
1325 BOOST_CHECK_EQUAL(axis1.getNBins(), 5);
1326 BOOST_CHECK_EQUAL(axis1.getType(), AxisType::Equidistant);
1327 BOOST_CHECK_EQUAL(axis1.getBoundaryType(), AxisBoundaryType::Bound);
1328
1329 BOOST_CHECK_EQUAL(axis2.getMin(), -150_mm);
1330 BOOST_CHECK_EQUAL(axis2.getMax(), 150_mm);
1331 BOOST_CHECK_EQUAL(axis2.getNBins(), 10);
1332 BOOST_CHECK_EQUAL(axis2.getType(), AxisType::Variable);
1333 BOOST_CHECK_EQUAL(axis2.getBoundaryType(), AxisBoundaryType::Bound);
1334
1335 auto grid3 = GridPortalLink::make(
1336 cyl2, Axis{AxisBound, -45_degree * 30_mm, 45_degree * 30_mm, 3},
1337 Axis{AxisBound, -50_mm, 50_mm, 5});
1338
1339 auto composite = PortalLinkBase::merge(copy(grid1), copy(grid3),
1340 AxisDirection::AxisZ, *logger);
1341 BOOST_CHECK_NE(dynamic_cast<const CompositePortalLink*>(composite.get()),
1342 nullptr);
1343 }
1344
1345 BOOST_TEST_CONTEXT("Check wraparound for full circle in phi") {
1346 auto cyl1 = Surface::makeShared<CylinderSurface>(Transform3::Identity(),
1347 30_mm, 100_mm, 180_degree);
1348
1349
1350 auto grid1 = GridPortalLink::make(
1351 cyl1, Axis{AxisClosed, -180_degree * 30_mm, 180_degree * 30_mm, 5},
1352 Axis{AxisBound, -100_mm, 100_mm, 5});
1353
1354 auto trf2 = Transform3{Translation3{Vector3::UnitZ() * 150_mm}};
1355 auto cyl2 =
1356 Surface::makeShared<CylinderSurface>(trf2, 30_mm, 50_mm, 180_degree);
1357
1358
1359 auto grid2 = GridPortalLink::make(
1360 cyl2, Axis{AxisClosed, -180_degree * 30_mm, 180_degree * 30_mm, 5},
1361 Axis{AxisBound, -50_mm, 50_mm, 5});
1362
1363 auto mergedPtr =
1364 GridPortalLink::merge(*grid1, *grid2, AxisDirection::AxisZ, *logger);
1365 const auto* merged = dynamic_cast<const GridPortalLink*>(mergedPtr.get());
1366 BOOST_REQUIRE_NE(mergedPtr, nullptr);
1367
1368 const auto& axis1 = *merged->grid().axes().front();
1369 const auto& axis2 = *merged->grid().axes().back();
1370
1371 Axis axis1Expected{AxisClosed, -180_degree * 30_mm, 180_degree * 30_mm, 5};
1372 BOOST_CHECK_EQUAL(axis1, axis1Expected);
1373 Axis axis2Expected{AxisBound,
1374 {-150, -110, -70, -30, 10, 50, 70, 90, 110, 130, 150}};
1375 BOOST_CHECK_EQUAL(axis2, axis2Expected);
1376 }
1377 }
1378
1379 BOOST_AUTO_TEST_CASE(RPhiDirection) {
1380 auto cyl1 = Surface::makeShared<CylinderSurface>(Transform3::Identity(),
1381 30_mm, 100_mm, 45_degree);
1382
1383
1384 auto grid1 = GridPortalLink::make(
1385 cyl1, Axis{AxisBound, -45_degree * 30_mm, 45_degree * 30_mm, 5},
1386 Axis{AxisBound, -100_mm, 100_mm, 5});
1387 BOOST_REQUIRE_NE(grid1, nullptr);
1388
1389 auto trf2 = Transform3{AngleAxis3{90_degree, Vector3::UnitZ()}};
1390 auto cyl2 =
1391 Surface::makeShared<CylinderSurface>(trf2, 30_mm, 100_mm, 45_degree);
1392
1393
1394 auto grid2 = GridPortalLink::make(
1395 cyl2, Axis{AxisBound, -45_degree * 30_mm, 45_degree * 30_mm, 5},
1396 Axis{AxisBound, -100_mm, 100_mm, 5});
1397 BOOST_REQUIRE_NE(grid2, nullptr);
1398
1399
1400
1401 auto mergedPtr =
1402 GridPortalLink::merge(*grid1, *grid2, AxisDirection::AxisRPhi, *logger);
1403 const auto* merged = dynamic_cast<const GridPortalLink*>(mergedPtr.get());
1404 BOOST_REQUIRE_NE(mergedPtr, nullptr);
1405
1406 const auto& axis1 = *merged->grid().axes().front();
1407 const auto& axis2 = *merged->grid().axes().back();
1408 BOOST_CHECK_CLOSE(axis1.getMin(), -90_degree * 30_mm, 1e-8);
1409 BOOST_CHECK_CLOSE(axis1.getMax(), 90_degree * 30_mm, 1e-8);
1410 BOOST_CHECK_EQUAL(axis1.getNBins(), 10);
1411 BOOST_CHECK_EQUAL(axis1.getType(), AxisType::Equidistant);
1412 BOOST_CHECK_EQUAL(axis1.getBoundaryType(), AxisBoundaryType::Bound);
1413 Axis axis2Expected{AxisBound, -100_mm, 100_mm, 5};
1414 BOOST_CHECK_EQUAL(axis2, axis2Expected);
1415 }
1416
1417 BOOST_AUTO_TEST_SUITE_END()
1418
1419 BOOST_AUTO_TEST_SUITE(Merging1dDisc)
1420
1421 BOOST_AUTO_TEST_SUITE(RDirection)
1422
1423 BOOST_AUTO_TEST_CASE(ColinearMerge) {
1424
1425 auto disc1 =
1426 Surface::makeShared<DiscSurface>(Transform3::Identity(), 30_mm, 100_mm);
1427
1428 auto grid1 = GridPortalLink::make(disc1, AxisDirection::AxisR,
1429 Axis{AxisBound, 30_mm, 100_mm, 7});
1430
1431 auto disc2 =
1432 Surface::makeShared<DiscSurface>(Transform3::Identity(), 100_mm, 150_mm);
1433
1434 auto grid2 = GridPortalLink::make(disc2, AxisDirection::AxisR,
1435 Axis{AxisBound, 100_mm, 150_mm, 5});
1436
1437 auto mergedPtr =
1438 GridPortalLink::merge(*grid1, *grid2, AxisDirection::AxisR, *logger);
1439 BOOST_REQUIRE(mergedPtr);
1440 const auto* merged = dynamic_cast<const GridPortalLink*>(mergedPtr.get());
1441 BOOST_REQUIRE_NE(merged, nullptr);
1442
1443 BOOST_CHECK_EQUAL(merged->grid().axes().size(), 1);
1444 Axis axisExpected{AxisBound, 30_mm, 150_mm, 12};
1445 BOOST_CHECK_EQUAL(*merged->grid().axes().front(), axisExpected);
1446
1447
1448 auto discPhi1 = Surface::makeShared<DiscSurface>(Transform3::Identity(),
1449 30_mm, 100_mm, 30_degree);
1450
1451 auto discPhiGrid1 = GridPortalLink::make(discPhi1, AxisDirection::AxisR,
1452 Axis{AxisBound, 30_mm, 100_mm, 7});
1453
1454 auto discPhi2 = Surface::makeShared<DiscSurface>(Transform3::Identity(),
1455 100_mm, 150_mm, 30_degree);
1456
1457 auto discPhiGrid2 = GridPortalLink::make(discPhi2, AxisDirection::AxisR,
1458 Axis{AxisBound, 100_mm, 150_mm, 5});
1459
1460 auto mergedPhiPtr = GridPortalLink::merge(*discPhiGrid1, *discPhiGrid2,
1461 AxisDirection::AxisR, *logger);
1462 BOOST_REQUIRE(mergedPhiPtr);
1463 const auto* mergedPhi =
1464 dynamic_cast<const GridPortalLink*>(mergedPhiPtr.get());
1465 BOOST_REQUIRE_NE(mergedPhi, nullptr);
1466
1467 BOOST_CHECK_EQUAL(mergedPhi->grid().axes().size(), 1);
1468 BOOST_CHECK_EQUAL(*mergedPhi->grid().axes().front(), axisExpected);
1469 }
1470
1471 BOOST_AUTO_TEST_CASE(ParallelMerge) {
1472 auto disc1 =
1473 Surface::makeShared<DiscSurface>(Transform3::Identity(), 30_mm, 100_mm);
1474
1475 auto grid1 =
1476 GridPortalLink::make(disc1, AxisDirection::AxisPhi,
1477 Axis{AxisClosed, -180_degree, 180_degree, 5});
1478
1479 auto disc2 =
1480 Surface::makeShared<DiscSurface>(Transform3::Identity(), 100_mm, 150_mm);
1481
1482 auto grid2 =
1483 GridPortalLink::make(disc2, AxisDirection::AxisPhi,
1484 Axis{AxisClosed, -180_degree, 180_degree, 5});
1485
1486 auto mergedPtr =
1487 GridPortalLink::merge(*grid1, *grid2, AxisDirection::AxisR, *logger);
1488 BOOST_REQUIRE(mergedPtr);
1489 const auto* merged = dynamic_cast<const GridPortalLink*>(mergedPtr.get());
1490 BOOST_REQUIRE_NE(merged, nullptr);
1491
1492 BOOST_CHECK_EQUAL(merged->grid().axes().size(), 2);
1493 const auto& axis1 = *merged->grid().axes().front();
1494 const auto& axis2 = *merged->grid().axes().back();
1495 Axis axis1Expected{AxisBound, {30_mm, 100_mm, 150_mm}};
1496 BOOST_CHECK_EQUAL(axis1, axis1Expected);
1497 Axis axis2Expected{AxisClosed, -180_degree, 180_degree, 5};
1498 BOOST_CHECK_EQUAL(axis2, axis2Expected);
1499 }
1500
1501 BOOST_AUTO_TEST_SUITE_END()
1502
1503 BOOST_AUTO_TEST_SUITE(PhiDirection)
1504
1505 BOOST_AUTO_TEST_CASE(ColinearMerge) {
1506 auto disc1 = Surface::makeShared<DiscSurface>(Transform3::Identity(), 30_mm,
1507 100_mm, 30_degree);
1508
1509 auto grid1 = GridPortalLink::make(disc1, AxisDirection::AxisPhi,
1510 Axis{AxisBound, -30_degree, 30_degree, 3});
1511
1512 auto disc2 = Surface::makeShared<DiscSurface>(
1513 Transform3{AngleAxis3{90_degree, Vector3::UnitZ()}}, 30_mm, 100_mm,
1514 60_degree);
1515
1516 auto grid2 = GridPortalLink::make(disc2, AxisDirection::AxisPhi,
1517 Axis{AxisBound, -60_degree, 60_degree, 6});
1518
1519 auto mergedPtr =
1520 GridPortalLink::merge(*grid1, *grid2, AxisDirection::AxisPhi, *logger);
1521 BOOST_REQUIRE(mergedPtr);
1522 const auto* merged = dynamic_cast<const GridPortalLink*>(mergedPtr.get());
1523 BOOST_REQUIRE_NE(merged, nullptr);
1524
1525 BOOST_CHECK_EQUAL(merged->grid().axes().size(), 1);
1526 const auto& axis = *merged->grid().axes().front();
1527 BOOST_CHECK_CLOSE(axis.getMin(), -90_degree, 1e-6);
1528 BOOST_CHECK_CLOSE(axis.getMax(), 90_degree, 1e-6);
1529 BOOST_CHECK_EQUAL(axis.getNBins(), 9);
1530
1531
1532
1533 auto disc1Half = Surface::makeShared<DiscSurface>(
1534 Transform3{AngleAxis3{15_degree, Vector3::UnitZ()}}, 30_mm, 100_mm,
1535 90_degree);
1536
1537 auto grid1Half =
1538 GridPortalLink::make(disc1Half, AxisDirection::AxisPhi,
1539 Axis{AxisBound, -90_degree, 90_degree, 3});
1540
1541 auto disc2Half = Surface::makeShared<DiscSurface>(
1542 Transform3{AngleAxis3{-165_degree, Vector3::UnitZ()}}, 30_mm, 100_mm,
1543 90_degree);
1544
1545 auto grid2Half =
1546 GridPortalLink::make(disc2Half, AxisDirection::AxisPhi,
1547 Axis{AxisBound, -90_degree, 90_degree, 3});
1548
1549 auto mergedHalfPtr = GridPortalLink::merge(*grid1Half, *grid2Half,
1550 AxisDirection::AxisPhi, *logger);
1551 BOOST_REQUIRE(mergedHalfPtr);
1552 const auto* mergedHalf =
1553 dynamic_cast<const GridPortalLink*>(mergedHalfPtr.get());
1554 BOOST_REQUIRE_NE(mergedHalf, nullptr);
1555
1556 BOOST_CHECK_EQUAL(mergedHalf->grid().axes().size(), 1);
1557 Axis axisHalfExpected{AxisClosed, -180_degree, 180_degree, 6};
1558 BOOST_CHECK_EQUAL(axisHalfExpected, *mergedHalf->grid().axes().front());
1559 }
1560
1561 BOOST_AUTO_TEST_CASE(ParallelMerge) {
1562 auto disc1 = Surface::makeShared<DiscSurface>(Transform3::Identity(), 30_mm,
1563 100_mm, 30_degree);
1564
1565 auto grid1 = GridPortalLink::make(disc1, AxisDirection::AxisR,
1566 Axis{AxisBound, 30_mm, 100_mm, 3});
1567
1568 auto disc2 = Surface::makeShared<DiscSurface>(
1569 Transform3{AngleAxis3{90_degree, Vector3::UnitZ()}}, 30_mm, 100_mm,
1570 60_degree);
1571
1572 auto grid2 = GridPortalLink::make(disc2, AxisDirection::AxisR,
1573 Axis{AxisBound, 30_mm, 100_mm, 3});
1574
1575 auto mergedPtr =
1576 GridPortalLink::merge(*grid1, *grid2, AxisDirection::AxisPhi, *logger);
1577 BOOST_REQUIRE(mergedPtr);
1578 const auto* merged = dynamic_cast<const GridPortalLink*>(mergedPtr.get());
1579 BOOST_REQUIRE_NE(merged, nullptr);
1580
1581 BOOST_CHECK_EQUAL(merged->grid().axes().size(), 2);
1582 const auto& axis1 = *merged->grid().axes().front();
1583 const auto& axis2 = *merged->grid().axes().back();
1584
1585 Axis axis1Expected{AxisBound, 30_mm, 100_mm, 3};
1586 BOOST_CHECK_EQUAL(axis1, axis1Expected);
1587
1588 BOOST_CHECK_CLOSE(axis2.getMin(), -90_degree, 1e-6);
1589 BOOST_CHECK_CLOSE(axis2.getMax(), 90_degree, 1e-6);
1590 BOOST_CHECK_EQUAL(axis2.getNBins(), 2);
1591 BOOST_CHECK_CLOSE(axis2.getBinEdges().at(1), 30_degree, 1e-6);
1592 BOOST_CHECK_EQUAL(axis2.getType(), AxisType::Variable);
1593 BOOST_CHECK_EQUAL(axis2.getBoundaryType(), AxisBoundaryType::Bound);
1594 }
1595
1596 BOOST_AUTO_TEST_SUITE_END()
1597
1598 BOOST_AUTO_TEST_CASE(BinFilling) {
1599
1600
1601 auto vol1 = makeDummyVolume();
1602 auto vol2 = makeDummyVolume();
1603
1604 BOOST_TEST_CONTEXT("RDirection") {
1605 auto disc1 = Surface::makeShared<DiscSurface>(Transform3::Identity(), 30_mm,
1606 60_mm, 30_degree);
1607
1608 auto grid1 = GridPortalLink::make(disc1, AxisDirection::AxisR,
1609 Axis{AxisBound, 30_mm, 60_mm, 2});
1610
1611 grid1->setVolume(vol1.get());
1612
1613 auto disc2 = Surface::makeShared<DiscSurface>(Transform3::Identity(), 60_mm,
1614 90_mm, 30_degree);
1615
1616 auto grid2 = GridPortalLink::make(disc2, AxisDirection::AxisR,
1617 Axis{AxisBound, 60_mm, 90_mm, 2});
1618
1619 grid2->setVolume(vol2.get());
1620
1621 auto mergedPtr =
1622 GridPortalLink::merge(*grid1, *grid2, AxisDirection::AxisR, *logger);
1623
1624 using merged_type =
1625 GridPortalLinkT<Axis<AxisType::Equidistant, AxisBoundaryType::Bound>>;
1626
1627 const auto* merged = dynamic_cast<const merged_type*>(mergedPtr.get());
1628 BOOST_REQUIRE(merged);
1629
1630 grid1->printContents(std::cout);
1631 grid2->printContents(std::cout);
1632 merged->printContents(std::cout);
1633
1634 BOOST_CHECK_EQUAL(merged->grid().atLocalBins({1}), vol1.get());
1635 BOOST_CHECK_EQUAL(merged->grid().atLocalBins({2}), vol1.get());
1636 BOOST_CHECK_EQUAL(merged->grid().atLocalBins({3}), vol2.get());
1637 BOOST_CHECK_EQUAL(merged->grid().atLocalBins({4}), vol2.get());
1638 }
1639
1640 BOOST_TEST_CONTEXT("PhiDirection") {
1641 auto disc1 = Surface::makeShared<DiscSurface>(Transform3::Identity(), 30_mm,
1642 100_mm, 30_degree);
1643
1644 auto grid1 =
1645 GridPortalLink::make(disc1, AxisDirection::AxisPhi,
1646 Axis{AxisBound, -30_degree, 30_degree, 2});
1647
1648 grid1->setVolume(vol1.get());
1649
1650 auto disc2 = Surface::makeShared<DiscSurface>(
1651 Transform3{AngleAxis3{60_degree, Vector3::UnitZ()}}, 30_mm, 100_mm,
1652 30_degree);
1653
1654 auto grid2 =
1655 GridPortalLink::make(disc2, AxisDirection::AxisPhi,
1656 Axis{AxisBound, -30_degree, 30_degree, 2});
1657
1658 grid2->setVolume(vol2.get());
1659
1660 auto mergedPtr =
1661 GridPortalLink::merge(*grid1, *grid2, AxisDirection::AxisPhi, *logger);
1662 BOOST_REQUIRE(mergedPtr);
1663
1664 using merged_type =
1665 GridPortalLinkT<Axis<AxisType::Equidistant, AxisBoundaryType::Bound>>;
1666
1667 const auto* merged = dynamic_cast<const merged_type*>(mergedPtr.get());
1668 BOOST_REQUIRE(merged);
1669
1670 grid1->printContents(std::cout);
1671 grid2->printContents(std::cout);
1672 merged->printContents(std::cout);
1673
1674 BOOST_CHECK_EQUAL(merged->grid().atLocalBins({1}), vol2.get());
1675 BOOST_CHECK_EQUAL(merged->grid().atLocalBins({2}), vol2.get());
1676 BOOST_CHECK_EQUAL(merged->grid().atLocalBins({3}), vol1.get());
1677 BOOST_CHECK_EQUAL(merged->grid().atLocalBins({4}), vol1.get());
1678 }
1679 }
1680
1681 BOOST_AUTO_TEST_SUITE_END()
1682
1683 BOOST_AUTO_TEST_SUITE(Merging2dDisc)
1684
1685 BOOST_AUTO_TEST_CASE(RDirection) {
1686
1687 auto discPhi1 = Surface::makeShared<DiscSurface>(Transform3::Identity(),
1688 30_mm, 100_mm, 30_degree);
1689
1690 auto discPhiGrid1 =
1691 GridPortalLink::make(discPhi1, Axis{AxisBound, 30_mm, 100_mm, 7},
1692 Axis{AxisBound, -30_degree, 30_degree, 3});
1693
1694 auto discPhi2 = Surface::makeShared<DiscSurface>(Transform3::Identity(),
1695 100_mm, 150_mm, 30_degree);
1696
1697 auto discPhiGrid2 =
1698 GridPortalLink::make(discPhi2, Axis{AxisBound, 100_mm, 150_mm, 5},
1699 Axis{AxisBound, -30_degree, 30_degree, 3});
1700
1701 auto mergedPtr = GridPortalLink::merge(*discPhiGrid1, *discPhiGrid2,
1702 AxisDirection::AxisR, *logger);
1703 BOOST_REQUIRE(mergedPtr);
1704 const auto* merged = dynamic_cast<const GridPortalLink*>(mergedPtr.get());
1705 BOOST_REQUIRE_NE(merged, nullptr);
1706 BOOST_CHECK_EQUAL(merged->grid().axes().size(), 2);
1707 const auto& axis1 = *merged->grid().axes().front();
1708 const auto& axis2 = *merged->grid().axes().back();
1709
1710 BOOST_CHECK_EQUAL(axis1.getMin(), 30_mm);
1711 BOOST_CHECK_EQUAL(axis1.getMax(), 150_mm);
1712 BOOST_CHECK_EQUAL(axis1.getNBins(), 12);
1713 BOOST_CHECK_EQUAL(axis1.getType(), AxisType::Equidistant);
1714 BOOST_CHECK_EQUAL(axis1.getBoundaryType(), AxisBoundaryType::Bound);
1715 BOOST_CHECK_EQUAL(axis2.getMin(), -30_degree);
1716 BOOST_CHECK_EQUAL(axis2.getMax(), 30_degree);
1717 BOOST_CHECK_EQUAL(axis2.getNBins(), 3);
1718 BOOST_CHECK_EQUAL(axis2.getType(), AxisType::Equidistant);
1719 }
1720
1721 BOOST_AUTO_TEST_CASE(PhiDirection) {
1722
1723 auto disc1 = Surface::makeShared<DiscSurface>(Transform3::Identity(), 30_mm,
1724 100_mm, 30_degree);
1725
1726 auto grid1 = GridPortalLink::make(disc1, Axis{AxisBound, 30_mm, 100_mm, 3},
1727 Axis{AxisBound, -30_degree, 30_degree, 3});
1728
1729 auto disc2 = Surface::makeShared<DiscSurface>(
1730 Transform3{AngleAxis3{90_degree, Vector3::UnitZ()}}, 30_mm, 100_mm,
1731 60_degree);
1732
1733 auto grid2 = GridPortalLink::make(disc2, Axis{AxisBound, 30_mm, 100_mm, 3},
1734 Axis{AxisBound, -60_degree, 60_degree, 6});
1735
1736 auto mergedPtr =
1737 GridPortalLink::merge(*grid1, *grid2, AxisDirection::AxisPhi, *logger);
1738 BOOST_REQUIRE(mergedPtr);
1739 const auto* merged = dynamic_cast<const GridPortalLink*>(mergedPtr.get());
1740 BOOST_REQUIRE_NE(merged, nullptr);
1741
1742 BOOST_CHECK_EQUAL(merged->grid().axes().size(), 2);
1743 const auto& axis1 = *merged->grid().axes().front();
1744 const auto& axis2 = *merged->grid().axes().back();
1745
1746 BOOST_CHECK_EQUAL(axis1.getMin(), 30_mm);
1747 BOOST_CHECK_EQUAL(axis1.getMax(), 100_mm);
1748 BOOST_CHECK_EQUAL(axis1.getNBins(), 3);
1749 BOOST_CHECK_EQUAL(axis1.getType(), AxisType::Equidistant);
1750 BOOST_CHECK_EQUAL(axis1.getBoundaryType(), AxisBoundaryType::Bound);
1751 BOOST_CHECK_CLOSE(axis2.getMin(), -90_degree, 1e-6);
1752 BOOST_CHECK_CLOSE(axis2.getMax(), 90_degree, 1e-6);
1753 BOOST_CHECK_EQUAL(axis2.getNBins(), 9);
1754 BOOST_CHECK_EQUAL(axis2.getType(), AxisType::Equidistant);
1755 BOOST_CHECK_EQUAL(axis2.getBoundaryType(), AxisBoundaryType::Bound);
1756 }
1757
1758 BOOST_AUTO_TEST_CASE(BinFilling) {
1759
1760
1761 auto vol1 = std::make_shared<TrackingVolume>(
1762 Transform3::Identity(),
1763 std::make_shared<CylinderVolumeBounds>(30_mm, 40_mm, 100_mm));
1764
1765 auto vol2 = std::make_shared<TrackingVolume>(
1766 Transform3::Identity(),
1767 std::make_shared<CylinderVolumeBounds>(30_mm, 40_mm, 100_mm));
1768
1769 auto fillCheckerBoard = [&](auto& grid) {
1770 auto loc = grid.numLocalBins();
1771 for (std::size_t i = 1; i <= loc[0]; ++i) {
1772 for (std::size_t j = 1; j <= loc[1]; ++j) {
1773 grid.atLocalBins({i, j}) = (i + j) % 2 == 0 ? vol1.get() : vol2.get();
1774 }
1775 }
1776 };
1777
1778 auto checkCheckerBoard = [&](const auto& grid) {
1779 auto loc = grid.numLocalBins();
1780 for (std::size_t i = 1; i <= loc[0]; ++i) {
1781 for (std::size_t j = 1; j <= loc[1]; ++j) {
1782 const auto* vol = grid.atLocalBins({i, j});
1783 if (vol != ((i + j) % 2 == 0 ? vol1.get() : vol2.get())) {
1784 BOOST_ERROR("Is not a checkerboard pattern");
1785 return;
1786 }
1787 }
1788 }
1789 };
1790
1791 BOOST_TEST_CONTEXT("RDirection") {
1792 auto discPhi1 = Surface::makeShared<DiscSurface>(Transform3::Identity(),
1793 30_mm, 60_mm, 30_degree);
1794
1795 auto discPhiGrid1 =
1796 GridPortalLink::make(discPhi1, Axis{AxisBound, 30_mm, 60_mm, 2},
1797 Axis{AxisBound, -30_degree, 30_degree, 2});
1798
1799 fillCheckerBoard(discPhiGrid1->grid());
1800 checkCheckerBoard(discPhiGrid1->grid());
1801
1802 auto discPhi2 = Surface::makeShared<DiscSurface>(Transform3::Identity(),
1803 60_mm, 90_mm, 30_degree);
1804
1805 auto discPhiGrid2 =
1806 GridPortalLink::make(discPhi2, Axis{AxisBound, 60_mm, 90_mm, 2},
1807 Axis{AxisBound, -30_degree, 30_degree, 2});
1808
1809 fillCheckerBoard(discPhiGrid2->grid());
1810 checkCheckerBoard(discPhiGrid2->grid());
1811
1812 auto mergedPtr = GridPortalLink::merge(*discPhiGrid1, *discPhiGrid2,
1813 AxisDirection::AxisR, *logger);
1814
1815 using merged_type =
1816 GridPortalLinkT<Axis<AxisType::Equidistant, AxisBoundaryType::Bound>,
1817 Axis<AxisType::Equidistant, AxisBoundaryType::Bound>>;
1818
1819 const auto* merged = dynamic_cast<const merged_type*>(mergedPtr.get());
1820 BOOST_REQUIRE(merged);
1821 checkCheckerBoard(merged->grid());
1822
1823
1824 discPhiGrid1->setVolume(vol1.get());
1825 discPhiGrid2->setVolume(vol2.get());
1826
1827 mergedPtr = GridPortalLink::merge(*discPhiGrid1, *discPhiGrid2,
1828 AxisDirection::AxisR, *logger);
1829
1830 merged = dynamic_cast<const merged_type*>(mergedPtr.get());
1831 BOOST_REQUIRE(merged);
1832
1833 const auto* v1 = vol1.get();
1834 const auto* v2 = vol2.get();
1835
1836 std::vector<std::pair<Vector2, const TrackingVolume*>> locations = {
1837 {{40_mm, -20_degree}, v1}, {{40_mm, 20_degree}, v1},
1838 {{50_mm, -20_degree}, v1}, {{50_mm, 20_degree}, v1},
1839 {{70_mm, -20_degree}, v2}, {{70_mm, 20_degree}, v2},
1840 {{80_mm, -20_degree}, v2}, {{80_mm, 20_degree}, v2},
1841 };
1842
1843 for (const auto& [loc, vol] : locations) {
1844 BOOST_TEST_CONTEXT(loc.transpose())
1845 BOOST_CHECK_EQUAL(merged->resolveVolume(gctx, loc).value(), vol);
1846 }
1847
1848 std::vector<std::vector<const TrackingVolume*>> contents = {
1849 {v1, v1},
1850 {v1, v1},
1851 {v2, v2},
1852 {v2, v2},
1853 };
1854
1855 for (std::size_t i = 0; i < 4; ++i) {
1856 for (std::size_t j = 0; j < 2; ++j) {
1857 BOOST_CHECK_EQUAL(merged->grid().atLocalBins({i + 1, j + 1}),
1858 contents.at(i).at(j));
1859 }
1860 }
1861 }
1862
1863 BOOST_TEST_CONTEXT("PhiDirection") {
1864 auto disc1 = Surface::makeShared<DiscSurface>(Transform3::Identity(), 30_mm,
1865 100_mm, 30_degree);
1866
1867 auto grid1 =
1868 GridPortalLink::make(disc1, Axis{AxisBound, 30_mm, 100_mm, 2},
1869 Axis{AxisBound, -30_degree, 30_degree, 2});
1870 fillCheckerBoard(grid1->grid());
1871 checkCheckerBoard(grid1->grid());
1872
1873 auto disc2 = Surface::makeShared<DiscSurface>(
1874 Transform3{AngleAxis3{60_degree, Vector3::UnitZ()}}, 30_mm, 100_mm,
1875 30_degree);
1876
1877 auto grid2 =
1878 GridPortalLink::make(disc2, Axis{AxisBound, 30_mm, 100_mm, 2},
1879 Axis{AxisBound, -30_degree, 30_degree, 2});
1880 fillCheckerBoard(grid2->grid());
1881 checkCheckerBoard(grid2->grid());
1882
1883 auto mergedPtr =
1884 GridPortalLink::merge(*grid1, *grid2, AxisDirection::AxisPhi, *logger);
1885
1886 BOOST_REQUIRE(mergedPtr);
1887
1888 using merged_type =
1889 GridPortalLinkT<Axis<AxisType::Equidistant, AxisBoundaryType::Bound>,
1890 Axis<AxisType::Equidistant, AxisBoundaryType::Bound>>;
1891
1892 const auto* merged = dynamic_cast<const merged_type*>(mergedPtr.get());
1893 BOOST_REQUIRE(merged);
1894
1895 checkCheckerBoard(merged->grid());
1896
1897
1898 grid1->setVolume(vol1.get());
1899 grid2->setVolume(vol2.get());
1900
1901 mergedPtr =
1902 GridPortalLink::merge(*grid1, *grid2, AxisDirection::AxisPhi, *logger);
1903 merged = dynamic_cast<const merged_type*>(mergedPtr.get());
1904 BOOST_REQUIRE(merged);
1905
1906 const auto* v1 = vol1.get();
1907 const auto* v2 = vol2.get();
1908
1909 std::vector<std::pair<Vector2, const TrackingVolume*>> locations = {
1910 {{40_mm, -50_degree}, v2}, {{40_mm, -10_degree}, v2},
1911 {{50_mm, -50_degree}, v2}, {{50_mm, -10_degree}, v2},
1912 {{40_mm, 10_degree}, v1}, {{50_mm, 50_degree}, v1},
1913 {{50_mm, 10_degree}, v1}, {{50_mm, 50_degree}, v1},
1914 };
1915
1916 for (const auto& [loc, vol] : locations) {
1917 BOOST_TEST_CONTEXT(loc.transpose())
1918 BOOST_CHECK_EQUAL(merged->resolveVolume(gctx, loc).value(), vol);
1919 }
1920
1921 std::vector<std::vector<const TrackingVolume*>> contents = {
1922 {v2, v2, v1, v1},
1923 {v2, v2, v1, v1},
1924 };
1925
1926 for (std::size_t i = 0; i < 2; ++i) {
1927 for (std::size_t j = 0; j < 4; ++j) {
1928 BOOST_CHECK_EQUAL(merged->grid().atLocalBins({i + 1, j + 1}),
1929 contents.at(i).at(j));
1930 }
1931 }
1932 }
1933 }
1934
1935 BOOST_AUTO_TEST_SUITE_END()
1936
1937 BOOST_AUTO_TEST_SUITE(MergingMixedDisc)
1938
1939 BOOST_AUTO_TEST_CASE(RDirection) {
1940 auto discPhi1 = Surface::makeShared<DiscSurface>(Transform3::Identity(),
1941 30_mm, 100_mm, 30_degree);
1942
1943 auto discPhiGrid1 =
1944 GridPortalLink::make(discPhi1, Axis{AxisBound, 30_mm, 100_mm, 7},
1945 Axis{AxisBound, -30_degree, 30_degree, 3});
1946
1947 auto discPhi2 = Surface::makeShared<DiscSurface>(Transform3::Identity(),
1948 100_mm, 150_mm, 30_degree);
1949
1950 auto discPhiGrid21dPhi =
1951 GridPortalLink::make(discPhi2, AxisDirection::AxisPhi,
1952 Axis{AxisBound, -30_degree, 30_degree, 3});
1953
1954 auto merged12PhiPtr = GridPortalLink::merge(*discPhiGrid1, *discPhiGrid21dPhi,
1955 AxisDirection::AxisR, *logger);
1956 BOOST_REQUIRE(merged12PhiPtr);
1957 const auto* merged12Phi =
1958 dynamic_cast<const GridPortalLink*>(merged12PhiPtr.get());
1959 BOOST_REQUIRE_NE(merged12Phi, nullptr);
1960
1961 auto merged21PhiPtr = GridPortalLink::merge(*discPhiGrid21dPhi, *discPhiGrid1,
1962 AxisDirection::AxisR, *logger);
1963 BOOST_REQUIRE(merged21PhiPtr);
1964 const auto* merged21Phi =
1965 dynamic_cast<const GridPortalLink*>(merged21PhiPtr.get());
1966 BOOST_REQUIRE_NE(merged21Phi, nullptr);
1967
1968 BOOST_CHECK_EQUAL(merged12Phi->grid(), merged21Phi->grid());
1969
1970 BOOST_CHECK_EQUAL(merged12Phi->grid().axes().size(), 2);
1971 const auto& axis1 = *merged12Phi->grid().axes().front();
1972 const auto& axis2 = *merged12Phi->grid().axes().back();
1973
1974 BOOST_CHECK_EQUAL(axis1.getMin(), 30_mm);
1975 BOOST_CHECK_EQUAL(axis1.getMax(), 150_mm);
1976 BOOST_CHECK_EQUAL(axis1.getNBins(), 8);
1977 BOOST_CHECK_EQUAL(axis1.getType(), AxisType::Variable);
1978 BOOST_CHECK_EQUAL(axis1.getBoundaryType(), AxisBoundaryType::Bound);
1979 BOOST_CHECK_EQUAL(axis2.getMin(), -30_degree);
1980 BOOST_CHECK_EQUAL(axis2.getMax(), 30_degree);
1981 BOOST_CHECK_EQUAL(axis2.getNBins(), 3);
1982 BOOST_CHECK_EQUAL(axis2.getType(), AxisType::Equidistant);
1983 BOOST_CHECK_EQUAL(axis2.getBoundaryType(), AxisBoundaryType::Bound);
1984
1985 auto discPhiGrid21dR = GridPortalLink::make(
1986 discPhi2, AxisDirection::AxisR, Axis{AxisBound, 100_mm, 150_mm, 5});
1987
1988 auto merged12RPtr = GridPortalLink::merge(*discPhiGrid1, *discPhiGrid21dR,
1989 AxisDirection::AxisR, *logger);
1990 BOOST_REQUIRE(merged12RPtr);
1991 const auto* merged12R =
1992 dynamic_cast<const GridPortalLink*>(merged12RPtr.get());
1993 BOOST_REQUIRE_NE(merged12R, nullptr);
1994
1995 auto merged21RPtr = GridPortalLink::merge(*discPhiGrid21dR, *discPhiGrid1,
1996 AxisDirection::AxisR, *logger);
1997 BOOST_REQUIRE(merged21RPtr);
1998 const auto* merged21R =
1999 dynamic_cast<const GridPortalLink*>(merged21RPtr.get());
2000 BOOST_REQUIRE_NE(merged21R, nullptr);
2001 BOOST_CHECK_EQUAL(merged12R->grid(), merged21R->grid());
2002
2003 BOOST_CHECK_EQUAL(merged12R->grid().axes().size(), 2);
2004 const auto& axis1R = *merged12R->grid().axes().front();
2005 const auto& axis2R = *merged12R->grid().axes().back();
2006
2007 BOOST_CHECK_EQUAL(axis1R.getMin(), 30_mm);
2008 BOOST_CHECK_EQUAL(axis1R.getMax(), 150_mm);
2009 BOOST_CHECK_EQUAL(axis1R.getNBins(), 12);
2010 BOOST_CHECK_EQUAL(axis1R.getType(), AxisType::Equidistant);
2011 BOOST_CHECK_EQUAL(axis1R.getBoundaryType(), AxisBoundaryType::Bound);
2012 BOOST_CHECK_EQUAL(axis2R.getMin(), -30_degree);
2013 BOOST_CHECK_EQUAL(axis2R.getMax(), 30_degree);
2014 BOOST_CHECK_EQUAL(axis2R.getNBins(), 3);
2015 BOOST_CHECK_EQUAL(axis2R.getType(), AxisType::Equidistant);
2016 BOOST_CHECK_EQUAL(axis2R.getBoundaryType(), AxisBoundaryType::Bound);
2017 }
2018
2019 BOOST_AUTO_TEST_CASE(PhiDirection) {
2020 auto disc1 = Surface::makeShared<DiscSurface>(Transform3::Identity(), 30_mm,
2021 100_mm, 30_degree);
2022
2023 auto grid1 = GridPortalLink::make(disc1, Axis{AxisBound, 30_mm, 100_mm, 3},
2024 Axis{AxisBound, -30_degree, 30_degree, 3});
2025
2026 auto disc2 = Surface::makeShared<DiscSurface>(
2027 Transform3{AngleAxis3{90_degree, Vector3::UnitZ()}}, 30_mm, 100_mm,
2028 60_degree);
2029
2030 auto grid21dPhi =
2031 GridPortalLink::make(disc2, AxisDirection::AxisPhi,
2032
2033 Axis{AxisBound, -60_degree, 60_degree, 6});
2034
2035 auto merged12PhiPtr = GridPortalLink::merge(*grid1, *grid21dPhi,
2036 AxisDirection::AxisPhi, *logger);
2037 BOOST_REQUIRE(merged12PhiPtr);
2038 const auto* merged12Phi =
2039 dynamic_cast<const GridPortalLink*>(merged12PhiPtr.get());
2040 BOOST_REQUIRE_NE(merged12Phi, nullptr);
2041
2042 BOOST_CHECK_EQUAL(merged12Phi->grid().axes().size(), 2);
2043 const auto& axis1 = *merged12Phi->grid().axes().front();
2044 const auto& axis2 = *merged12Phi->grid().axes().back();
2045
2046 BOOST_CHECK_EQUAL(axis1.getMin(), 30_mm);
2047 BOOST_CHECK_EQUAL(axis1.getMax(), 100_mm);
2048 BOOST_CHECK_EQUAL(axis1.getNBins(), 3);
2049 BOOST_CHECK_EQUAL(axis1.getType(), AxisType::Equidistant);
2050 BOOST_CHECK_EQUAL(axis1.getBoundaryType(), AxisBoundaryType::Bound);
2051 BOOST_CHECK_CLOSE(axis2.getMin(), -90_degree, 1e-6);
2052 BOOST_CHECK_CLOSE(axis2.getMax(), 90_degree, 1e-6);
2053 BOOST_CHECK_EQUAL(axis2.getNBins(), 9);
2054 BOOST_CHECK_EQUAL(axis2.getType(), AxisType::Equidistant);
2055 BOOST_CHECK_EQUAL(axis2.getBoundaryType(), AxisBoundaryType::Bound);
2056
2057 auto grid21dR = GridPortalLink::make(disc2, AxisDirection::AxisR,
2058 Axis{AxisBound, 30_mm, 100_mm, 3});
2059
2060 auto merged12RPtr =
2061 GridPortalLink::merge(*grid1, *grid21dR, AxisDirection::AxisPhi, *logger);
2062 BOOST_REQUIRE(merged12RPtr);
2063 const auto* merged12R =
2064 dynamic_cast<const GridPortalLink*>(merged12RPtr.get());
2065 BOOST_REQUIRE_NE(merged12R, nullptr);
2066
2067 BOOST_CHECK_EQUAL(merged12R->grid().axes().size(), 2);
2068 const auto& axis1R = *merged12R->grid().axes().front();
2069 const auto& axis2R = *merged12R->grid().axes().back();
2070
2071 BOOST_CHECK_EQUAL(axis1R.getMin(), 30_mm);
2072 BOOST_CHECK_EQUAL(axis1R.getMax(), 100_mm);
2073 BOOST_CHECK_EQUAL(axis1R.getNBins(), 3);
2074 BOOST_CHECK_EQUAL(axis1R.getType(), AxisType::Equidistant);
2075 BOOST_CHECK_EQUAL(axis1R.getBoundaryType(), AxisBoundaryType::Bound);
2076 BOOST_CHECK_CLOSE(axis2R.getMin(), -90_degree, 1e-6);
2077 BOOST_CHECK_CLOSE(axis2R.getMax(), 90_degree, 1e-6);
2078 BOOST_CHECK_EQUAL(axis2R.getNBins(), 4);
2079 BOOST_CHECK_EQUAL(axis2R.getType(), AxisType::Variable);
2080 BOOST_CHECK_EQUAL(axis2R.getBoundaryType(), AxisBoundaryType::Bound);
2081 }
2082
2083 BOOST_AUTO_TEST_SUITE_END()
2084
2085 BOOST_AUTO_TEST_SUITE(MergingCrossDisc)
2086
2087 BOOST_AUTO_TEST_CASE(RDirection) {
2088
2089 auto vol1 = makeDummyVolume();
2090 auto vol2 = makeDummyVolume();
2091 auto vol3 = makeDummyVolume();
2092 auto vol4 = makeDummyVolume();
2093
2094 auto disc1 = Surface::makeShared<DiscSurface>(Transform3::Identity(), 30_mm,
2095 100_mm, 30_degree);
2096
2097 auto grid1 = GridPortalLink::make(disc1, AxisDirection::AxisR,
2098 Axis{AxisBound, 30_mm, 100_mm, 2});
2099 grid1->grid().atLocalBins({1}) = vol1.get();
2100 grid1->grid().atLocalBins({2}) = vol2.get();
2101
2102 auto disc2 = Surface::makeShared<DiscSurface>(Transform3::Identity(), 100_mm,
2103 150_mm, 30_degree);
2104
2105 auto grid2 = GridPortalLink::make(disc2, AxisDirection::AxisPhi,
2106 Axis{AxisBound, -30_degree, 30_degree, 2});
2107
2108 grid2->grid().atLocalBins({1}) = vol3.get();
2109 grid2->grid().atLocalBins({2}) = vol4.get();
2110
2111 auto mergedPtr =
2112 GridPortalLink::merge(*grid1, *grid2, AxisDirection::AxisR, *logger);
2113
2114 const auto* merged = dynamic_cast<const GridPortalLink*>(mergedPtr.get());
2115 BOOST_REQUIRE_NE(merged, nullptr);
2116
2117 BOOST_CHECK_EQUAL(merged->grid().axes().size(), 2);
2118 const auto& axis1 = *merged->grid().axes().front();
2119 const auto& axis2 = *merged->grid().axes().back();
2120 Axis axisExpectedR{AxisBound, {30_mm, 65_mm, 100_mm, 150_mm}};
2121 Axis axisExpectedPhi{AxisBound, -30_degree, 30_degree, 2};
2122 BOOST_CHECK_EQUAL(axis1, axisExpectedR);
2123 BOOST_CHECK_EQUAL(axis2, axisExpectedPhi);
2124
2125 std::vector<std::pair<Vector2, TrackingVolume*>> locations = {
2126 {{40_mm, -15_degree}, vol1.get()}, {{40_mm, 15_degree}, vol1.get()},
2127 {{90_mm, -15_degree}, vol2.get()}, {{90_mm, 15_degree}, vol2.get()},
2128
2129 {{110_mm, -15_degree}, vol3.get()}, {{110_mm, 15_degree}, vol4.get()},
2130 {{140_mm, -15_degree}, vol3.get()}, {{140_mm, 15_degree}, vol4.get()},
2131 };
2132
2133 for (const auto& [loc, vol] : locations) {
2134 BOOST_TEST_CONTEXT(loc.transpose())
2135 BOOST_CHECK_EQUAL(merged->resolveVolume(gctx, loc).value(), vol);
2136 }
2137
2138 grid1->printContents(std::cout);
2139 grid2->printContents(std::cout);
2140 merged->printContents(std::cout);
2141 }
2142
2143 BOOST_AUTO_TEST_CASE(PhiDirection) {
2144
2145
2146 auto vol1 = makeDummyVolume();
2147 auto vol2 = makeDummyVolume();
2148 auto vol3 = makeDummyVolume();
2149 auto vol4 = makeDummyVolume();
2150
2151 auto disc1 = Surface::makeShared<DiscSurface>(Transform3::Identity(), 30_mm,
2152 100_mm, 30_degree);
2153
2154 auto grid1 = GridPortalLink::make(disc1, AxisDirection::AxisR,
2155 Axis{AxisBound, 30_mm, 100_mm, 2});
2156
2157 grid1->grid().atLocalBins({1}) = vol1.get();
2158 grid1->grid().atLocalBins({2}) = vol2.get();
2159
2160 auto disc2 = Surface::makeShared<DiscSurface>(
2161 Transform3{AngleAxis3{90_degree, Vector3::UnitZ()}}, 30_mm, 100_mm,
2162 60_degree);
2163
2164 auto grid2 = GridPortalLink::make(disc2, AxisDirection::AxisPhi,
2165 Axis{AxisBound, -60_degree, 60_degree, 2});
2166
2167 grid2->grid().atLocalBins({1}) = vol3.get();
2168 grid2->grid().atLocalBins({2}) = vol4.get();
2169
2170 auto mergedPtr =
2171 GridPortalLink::merge(*grid1, *grid2, AxisDirection::AxisPhi, *logger);
2172
2173 using merged_type =
2174 GridPortalLinkT<Axis<AxisType::Equidistant, AxisBoundaryType::Bound>,
2175 Axis<AxisType::Equidistant, AxisBoundaryType::Bound>>;
2176
2177 const auto* merged = dynamic_cast<const merged_type*>(mergedPtr.get());
2178 BOOST_REQUIRE_NE(merged, nullptr);
2179
2180 BOOST_CHECK_EQUAL(merged->grid().axes().size(), 2);
2181 const auto& axis1 = *merged->grid().axes().front();
2182 const auto& axis2 = *merged->grid().axes().back();
2183 Axis axisExpectedR{AxisBound, 30_mm, 100_mm, 2};
2184 BOOST_CHECK_EQUAL(axis1, axisExpectedR);
2185
2186 BOOST_CHECK_CLOSE(axis2.getMin(), -90_degree, 1e-6);
2187 BOOST_CHECK_CLOSE(axis2.getMax(), 90_degree, 1e-6);
2188 BOOST_CHECK_EQUAL(axis2.getNBins(), 3);
2189 BOOST_CHECK_EQUAL(axis2.getType(), AxisType::Equidistant);
2190 BOOST_CHECK_EQUAL(axis2.getBoundaryType(), AxisBoundaryType::Bound);
2191
2192 std::vector<std::pair<Vector2, TrackingVolume*>> locations = {
2193 {{40_mm, 45_degree}, vol1.get()}, {{40_mm, 0_degree}, vol4.get()},
2194 {{40_mm, -80_degree}, vol3.get()}, {{90_mm, 45_degree}, vol2.get()},
2195 {{90_mm, 0_degree}, vol4.get()}, {{90_mm, -80_degree}, vol3.get()},
2196 };
2197
2198 grid1->printContents(std::cout);
2199 grid2->printContents(std::cout);
2200 merged->printContents(std::cout);
2201
2202 for (const auto& [loc, vol] : locations) {
2203 BOOST_TEST_CONTEXT((Vector2{loc[0], loc[1] / 1_degree}.transpose()))
2204 BOOST_CHECK_EQUAL(merged->resolveVolume(gctx, loc).value(), vol);
2205 }
2206 }
2207
2208 BOOST_AUTO_TEST_SUITE_END()
2209
2210 BOOST_AUTO_TEST_SUITE(Merging1dPlane)
2211
2212 BOOST_AUTO_TEST_CASE(ColinearMerge) {
2213 auto rBounds1 = std::make_shared<const RectangleBounds>(30_mm, 100_mm);
2214
2215 auto plane1 =
2216 Surface::makeShared<PlaneSurface>(Transform3::Identity(), rBounds1);
2217
2218 auto gridX1 = GridPortalLink::make(plane1, AxisDirection::AxisX,
2219 Axis{AxisBound, -30_mm, 30_mm, 6});
2220 auto gridY1 = GridPortalLink::make(plane1, AxisDirection::AxisY,
2221 Axis{AxisBound, -100_mm, 100_mm, 10});
2222
2223 auto rBounds2 = std::make_shared<const RectangleBounds>(80_mm, 100_mm);
2224 Translation3 offsetX{110_mm, 0., 0.};
2225 auto planeX2 = Surface::makeShared<PlaneSurface>(
2226 Transform3::Identity() * offsetX, rBounds2);
2227
2228 auto rBounds3 = std::make_shared<const RectangleBounds>(30_mm, 20_mm);
2229 Translation3 offsetY{0, 120_mm, 0.};
2230 auto planeY2 = Surface::makeShared<PlaneSurface>(
2231 Transform3::Identity() * offsetY, rBounds3);
2232
2233 auto gridX2 = GridPortalLink::make(planeX2, AxisDirection::AxisX,
2234 Axis{AxisBound, -80_mm, 80_mm, 16});
2235 auto gridY2 = GridPortalLink::make(planeY2, AxisDirection::AxisY,
2236 Axis{AxisBound, -20_mm, 20_mm, 2});
2237
2238 auto mergedPtrX =
2239 GridPortalLink::merge(*gridX1, *gridX2, AxisDirection::AxisX, *logger);
2240 BOOST_REQUIRE(mergedPtrX);
2241 const auto* mergedX = dynamic_cast<const GridPortalLink*>(mergedPtrX.get());
2242 BOOST_REQUIRE_NE(mergedX, nullptr);
2243
2244 BOOST_CHECK_EQUAL(mergedX->grid().axes().size(), 1);
2245 Axis axisXExpected{AxisBound, -110_mm, 110_mm, 22};
2246 BOOST_CHECK_EQUAL(*mergedX->grid().axes().front(), axisXExpected);
2247
2248 auto mergedPtrY =
2249 GridPortalLink::merge(*gridY1, *gridY2, AxisDirection::AxisY, *logger);
2250 BOOST_REQUIRE(mergedPtrY);
2251 const auto* mergedY = dynamic_cast<const GridPortalLink*>(mergedPtrY.get());
2252 BOOST_REQUIRE_NE(mergedY, nullptr);
2253
2254 BOOST_CHECK_EQUAL(mergedY->grid().axes().size(), 1);
2255 Axis axisYExpected{AxisBound, -120_mm, 120_mm, 12};
2256 BOOST_CHECK_EQUAL(*mergedY->grid().axes().front(), axisYExpected);
2257 }
2258
2259 BOOST_AUTO_TEST_CASE(ParallelMerge) {
2260 auto rBounds = std::make_shared<const RectangleBounds>(30_mm, 100_mm);
2261 auto plane1 =
2262 Surface::makeShared<PlaneSurface>(Transform3::Identity(), rBounds);
2263
2264 auto grid1X = GridPortalLink::make(plane1, AxisDirection::AxisX,
2265 Axis{AxisBound, -30_mm, 30_mm, 6});
2266
2267 auto grid1Y = GridPortalLink::make(plane1, AxisDirection::AxisY,
2268 Axis{AxisBound, -100_mm, 100_mm, 5});
2269
2270 Translation3 offsetX{60_mm, 0, 0.};
2271 auto plane2 = Surface::makeShared<PlaneSurface>(
2272 Transform3::Identity() * offsetX, rBounds);
2273 auto grid2 = GridPortalLink::make(plane2, AxisDirection::AxisY,
2274 Axis{AxisBound, -100_mm, 100_mm, 5});
2275
2276 Translation3 offsetY{0, 200_mm, 0.};
2277 auto plane3 = Surface::makeShared<PlaneSurface>(
2278 Transform3::Identity() * offsetY, rBounds);
2279 auto grid3 = GridPortalLink::make(plane3, AxisDirection::AxisX,
2280 Axis{AxisBound, -30_mm, 30_mm, 6});
2281
2282 auto mergedPtrX =
2283 GridPortalLink::merge(*grid1Y, *grid2, AxisDirection::AxisX, *logger);
2284 BOOST_REQUIRE(mergedPtrX);
2285 const auto* mergedX = dynamic_cast<const GridPortalLink*>(mergedPtrX.get());
2286 BOOST_REQUIRE_NE(mergedX, nullptr);
2287
2288 BOOST_CHECK_EQUAL(mergedX->grid().axes().size(), 2);
2289 const auto& axisX1 = *mergedX->grid().axes().front();
2290 const auto& axisX2 = *mergedX->grid().axes().back();
2291 Axis axisX1Expected{AxisBound, -60_mm, 60_mm, 2};
2292 BOOST_CHECK_EQUAL(axisX1, axisX1Expected);
2293 Axis axisX2Expected{AxisBound, -100_mm, 100_mm, 5};
2294 BOOST_CHECK_EQUAL(axisX2, axisX2Expected);
2295
2296 auto mergedPtrY =
2297 GridPortalLink::merge(*grid1X, *grid3, AxisDirection::AxisY, *logger);
2298 BOOST_REQUIRE(mergedPtrY);
2299 const auto* mergedY = dynamic_cast<const GridPortalLink*>(mergedPtrY.get());
2300 BOOST_REQUIRE_NE(mergedY, nullptr);
2301
2302 BOOST_CHECK_EQUAL(mergedY->grid().axes().size(), 2);
2303 const auto& axisY1 = *mergedY->grid().axes().front();
2304 const auto& axisY2 = *mergedY->grid().axes().back();
2305 Axis axisY1Expected{AxisBound, -30_mm, 30_mm, 6};
2306 BOOST_CHECK_EQUAL(axisY1, axisY1Expected);
2307 Axis axisY2Expected{AxisBound, -200_mm, 200_mm, 2};
2308 BOOST_CHECK_EQUAL(axisY2, axisY2Expected);
2309 }
2310
2311 BOOST_AUTO_TEST_CASE(BinFilling) {
2312
2313 auto vol1 = std::make_shared<TrackingVolume>(
2314 Transform3::Identity(),
2315 std::make_shared<CuboidVolumeBounds>(30_mm, 40_mm, 100_mm));
2316 auto vol2 = std::make_shared<TrackingVolume>(
2317 Transform3::Identity(),
2318 std::make_shared<CuboidVolumeBounds>(30_mm, 40_mm, 100_mm));
2319
2320 auto rBounds = std::make_shared<const RectangleBounds>(30_mm, 100_mm);
2321 auto plane1 =
2322 Surface::makeShared<PlaneSurface>(Transform3::Identity(), rBounds);
2323
2324 auto grid1X = GridPortalLink::make(plane1, AxisDirection::AxisX,
2325 Axis{AxisBound, -30_mm, 30_mm, 2});
2326 grid1X->setVolume(vol1.get());
2327
2328 auto grid1Y = GridPortalLink::make(plane1, AxisDirection::AxisY,
2329 Axis{AxisBound, -100_mm, 100_mm, 2});
2330 grid1Y->setVolume(vol1.get());
2331
2332 Translation3 offsetX{60_mm, 0., 0.};
2333 auto plane2 = Surface::makeShared<PlaneSurface>(
2334 Transform3::Identity() * offsetX, rBounds);
2335 auto grid2 = GridPortalLink::make(plane2, AxisDirection::AxisX,
2336 Axis{AxisBound, -30_mm, 30_mm, 2});
2337 grid2->setVolume(vol2.get());
2338
2339 Translation3 offsetY{0., 200_mm, 0.};
2340 auto plane3 = Surface::makeShared<PlaneSurface>(
2341 Transform3::Identity() * offsetY, rBounds);
2342 auto grid3 = GridPortalLink::make(plane3, AxisDirection::AxisY,
2343 Axis{AxisBound, -100_mm, 100_mm, 2});
2344 grid3->setVolume(vol2.get());
2345
2346 auto mergedPtrX =
2347 GridPortalLink::merge(*grid1X, *grid2, AxisDirection::AxisX, *logger);
2348
2349 auto mergedPtrY =
2350 GridPortalLink::merge(*grid1Y, *grid3, AxisDirection::AxisY, *logger);
2351
2352 using merged_type =
2353 GridPortalLinkT<Axis<AxisType::Equidistant, AxisBoundaryType::Bound>>;
2354
2355 const auto* mergedX = dynamic_cast<const merged_type*>(mergedPtrX.get());
2356 BOOST_REQUIRE(mergedX);
2357
2358 grid1X->printContents(std::cout);
2359 grid2->printContents(std::cout);
2360 mergedX->printContents(std::cout);
2361
2362 BOOST_CHECK_EQUAL(mergedX->grid().atLocalBins({1}), vol1.get());
2363 BOOST_CHECK_EQUAL(mergedX->grid().atLocalBins({2}), vol1.get());
2364 BOOST_CHECK_EQUAL(mergedX->grid().atLocalBins({3}), vol2.get());
2365 BOOST_CHECK_EQUAL(mergedX->grid().atLocalBins({4}), vol2.get());
2366
2367 const auto* mergedY = dynamic_cast<const merged_type*>(mergedPtrX.get());
2368 BOOST_REQUIRE(mergedY);
2369
2370 BOOST_CHECK_EQUAL(mergedY->grid().atLocalBins({1}), vol1.get());
2371 BOOST_CHECK_EQUAL(mergedY->grid().atLocalBins({2}), vol1.get());
2372 BOOST_CHECK_EQUAL(mergedY->grid().atLocalBins({3}), vol2.get());
2373 BOOST_CHECK_EQUAL(mergedY->grid().atLocalBins({4}), vol2.get());
2374
2375 grid1X->printContents(std::cout);
2376 grid2->printContents(std::cout);
2377 grid3->printContents(std::cout);
2378 mergedX->printContents(std::cout);
2379 mergedY->printContents(std::cout);
2380 }
2381
2382 BOOST_AUTO_TEST_SUITE_END()
2383
2384 BOOST_AUTO_TEST_SUITE(Merging2dPlane)
2385
2386 BOOST_AUTO_TEST_CASE(XYDirection) {
2387
2388 auto rBounds = std::make_shared<const RectangleBounds>(30_mm, 100_mm);
2389 auto plane1 =
2390 Surface::makeShared<PlaneSurface>(Transform3::Identity(), rBounds);
2391
2392 auto grid1 = GridPortalLink::make(plane1, Axis{AxisBound, -30_mm, 30_mm, 10},
2393 Axis{AxisBound, -100_mm, 100_mm, 3});
2394
2395 Translation3 offsetX{60_mm, 0., 0.};
2396 auto plane2 = Surface::makeShared<PlaneSurface>(
2397 Transform3::Identity() * offsetX, rBounds);
2398 auto grid2 = GridPortalLink::make(plane2, Axis{AxisBound, -30_mm, 30_mm, 10},
2399 Axis{AxisBound, -100_mm, 100_mm, 3});
2400
2401 Translation3 offsetY{0., 200_mm, 0.};
2402 auto plane3 = Surface::makeShared<PlaneSurface>(
2403 Transform3::Identity() * offsetY, rBounds);
2404 auto grid3 = GridPortalLink::make(plane3, Axis{AxisBound, -30_mm, 30_mm, 10},
2405 Axis{AxisBound, -100_mm, 100_mm, 3});
2406
2407 auto mergedPtrX =
2408 GridPortalLink::merge(*grid1, *grid2, AxisDirection::AxisX, *logger);
2409
2410 auto mergedPtrY =
2411 GridPortalLink::merge(*grid1, *grid3, AxisDirection::AxisY, *logger);
2412
2413 BOOST_REQUIRE(mergedPtrX);
2414 const auto* mergedX = dynamic_cast<const GridPortalLink*>(mergedPtrX.get());
2415 BOOST_REQUIRE_NE(mergedX, nullptr);
2416 BOOST_CHECK_EQUAL(mergedX->grid().axes().size(), 2);
2417 const auto& axisX1 = *mergedX->grid().axes().front();
2418 const auto& axisX2 = *mergedX->grid().axes().back();
2419
2420 BOOST_CHECK_EQUAL(axisX1.getMin(), -60_mm);
2421 BOOST_CHECK_EQUAL(axisX1.getMax(), 60_mm);
2422 BOOST_CHECK_EQUAL(axisX1.getNBins(), 20);
2423 BOOST_CHECK_EQUAL(axisX1.getType(), AxisType::Equidistant);
2424 BOOST_CHECK_EQUAL(axisX1.getBoundaryType(), AxisBoundaryType::Bound);
2425 BOOST_CHECK_EQUAL(axisX2.getMin(), -100_mm);
2426 BOOST_CHECK_EQUAL(axisX2.getMax(), 100_mm);
2427 BOOST_CHECK_EQUAL(axisX2.getNBins(), 3);
2428 BOOST_CHECK_EQUAL(axisX2.getType(), AxisType::Equidistant);
2429
2430 BOOST_REQUIRE(mergedPtrY);
2431 const auto* mergedY = dynamic_cast<const GridPortalLink*>(mergedPtrY.get());
2432 BOOST_REQUIRE_NE(mergedY, nullptr);
2433 BOOST_CHECK_EQUAL(mergedY->grid().axes().size(), 2);
2434 const auto& axisY1 = *mergedY->grid().axes().front();
2435 const auto& axisY2 = *mergedY->grid().axes().back();
2436
2437 BOOST_CHECK_EQUAL(axisY1.getMin(), -30_mm);
2438 BOOST_CHECK_EQUAL(axisY1.getMax(), 30_mm);
2439 BOOST_CHECK_EQUAL(axisY1.getNBins(), 10);
2440 BOOST_CHECK_EQUAL(axisY1.getType(), AxisType::Equidistant);
2441 BOOST_CHECK_EQUAL(axisY1.getBoundaryType(), AxisBoundaryType::Bound);
2442 BOOST_CHECK_EQUAL(axisY2.getMin(), -200_mm);
2443 BOOST_CHECK_EQUAL(axisY2.getMax(), 200_mm);
2444 BOOST_CHECK_EQUAL(axisY2.getNBins(), 6);
2445 BOOST_CHECK_EQUAL(axisY2.getType(), AxisType::Equidistant);
2446 }
2447
2448 BOOST_AUTO_TEST_CASE(BinFilling) {
2449
2450
2451 auto vol1 = std::make_shared<TrackingVolume>(
2452 Transform3::Identity(),
2453 std::make_shared<CuboidVolumeBounds>(30_mm, 40_mm, 100_mm));
2454
2455 auto vol2 = std::make_shared<TrackingVolume>(
2456 Transform3::Identity(),
2457 std::make_shared<CuboidVolumeBounds>(30_mm, 40_mm, 100_mm));
2458
2459 auto fillCheckerBoard = [&](auto& grid) {
2460 auto loc = grid.numLocalBins();
2461 for (std::size_t i = 1; i <= loc[0]; ++i) {
2462 for (std::size_t j = 1; j <= loc[1]; ++j) {
2463 grid.atLocalBins({i, j}) = (i + j) % 2 == 0 ? vol1.get() : vol2.get();
2464 }
2465 }
2466 };
2467
2468 auto checkCheckerBoard = [&](const auto& grid) {
2469 auto loc = grid.numLocalBins();
2470 for (std::size_t i = 1; i <= loc[0]; ++i) {
2471 for (std::size_t j = 1; j <= loc[1]; ++j) {
2472 const auto* vol = grid.atLocalBins({i, j});
2473 if (vol != ((i + j) % 2 == 0 ? vol1.get() : vol2.get())) {
2474 BOOST_ERROR("Is not a checkerboard pattern");
2475 return;
2476 }
2477 }
2478 }
2479 };
2480
2481
2482 auto rBounds = std::make_shared<const RectangleBounds>(30_mm, 100_mm);
2483 auto plane1 =
2484 Surface::makeShared<PlaneSurface>(Transform3::Identity(), rBounds);
2485
2486 auto grid1 = GridPortalLink::make(plane1, Axis{AxisBound, -30_mm, 30_mm, 2},
2487 Axis{AxisBound, -100_mm, 100_mm, 2});
2488
2489 Translation3 offsetX{60_mm, 0., 0.};
2490 auto plane2 = Surface::makeShared<PlaneSurface>(
2491 Transform3::Identity() * offsetX, rBounds);
2492 auto grid2 = GridPortalLink::make(plane2, Axis{AxisBound, -30_mm, 30_mm, 2},
2493 Axis{AxisBound, -100_mm, 100_mm, 2});
2494
2495 Translation3 offsetY{0., 200_mm, 0.};
2496 auto plane3 = Surface::makeShared<PlaneSurface>(
2497 Transform3::Identity() * offsetY, rBounds);
2498 auto grid3 = GridPortalLink::make(plane3, Axis{AxisBound, -30_mm, 30_mm, 2},
2499 Axis{AxisBound, -100_mm, 100_mm, 2});
2500
2501 fillCheckerBoard(grid1->grid());
2502 checkCheckerBoard(grid1->grid());
2503
2504 fillCheckerBoard(grid2->grid());
2505 checkCheckerBoard(grid2->grid());
2506
2507 fillCheckerBoard(grid3->grid());
2508 checkCheckerBoard(grid3->grid());
2509
2510 auto mergedPtrX =
2511 GridPortalLink::merge(*grid1, *grid2, AxisDirection::AxisX, *logger);
2512 auto mergedPtrY =
2513 GridPortalLink::merge(*grid1, *grid3, AxisDirection::AxisY, *logger);
2514
2515 using merged_type =
2516 GridPortalLinkT<Axis<AxisType::Equidistant, AxisBoundaryType::Bound>,
2517 Axis<AxisType::Equidistant, AxisBoundaryType::Bound>>;
2518
2519 const auto* mergedX = dynamic_cast<const merged_type*>(mergedPtrX.get());
2520 BOOST_REQUIRE(mergedX);
2521 checkCheckerBoard(mergedX->grid());
2522
2523 const auto* mergedY = dynamic_cast<const merged_type*>(mergedPtrY.get());
2524 BOOST_REQUIRE(mergedY);
2525 checkCheckerBoard(mergedY->grid());
2526
2527
2528 grid1->setVolume(vol1.get());
2529 grid2->setVolume(vol2.get());
2530 grid3->setVolume(vol2.get());
2531
2532 mergedPtrX =
2533 GridPortalLink::merge(*grid1, *grid2, AxisDirection::AxisX, *logger);
2534 mergedPtrY =
2535 GridPortalLink::merge(*grid1, *grid3, AxisDirection::AxisY, *logger);
2536
2537 mergedX = dynamic_cast<const merged_type*>(mergedPtrX.get());
2538 BOOST_REQUIRE(mergedX);
2539
2540 mergedY = dynamic_cast<const merged_type*>(mergedPtrY.get());
2541 BOOST_REQUIRE(mergedY);
2542
2543 const auto* v1 = vol1.get();
2544 const auto* v2 = vol2.get();
2545
2546 std::vector<std::pair<Vector2, const TrackingVolume*>> locationsX = {
2547 {{-45_mm, 0_mm}, v1},
2548 {{-15_mm, 0_mm}, v1},
2549 {{15_mm, 0_mm}, v2},
2550 {{45_mm, 0_mm}, v2},
2551 };
2552 std::vector<std::pair<Vector2, const TrackingVolume*>> locationsY = {
2553 {{0_mm, -150_mm}, v1},
2554 {{0_mm, -50_mm}, v1},
2555 {{0_mm, 50_mm}, v2},
2556 {{0_mm, 150_mm}, v2},
2557 };
2558
2559 for (const auto& [loc, vol] : locationsX) {
2560 BOOST_TEST_CONTEXT(loc.transpose())
2561 BOOST_CHECK_EQUAL(mergedX->resolveVolume(gctx, loc).value(), vol);
2562 }
2563 for (const auto& [loc, vol] : locationsY) {
2564 BOOST_TEST_CONTEXT(loc.transpose())
2565 BOOST_CHECK_EQUAL(mergedY->resolveVolume(gctx, loc).value(), vol);
2566 }
2567
2568 std::vector<std::vector<const TrackingVolume*>> contentsX = {
2569 {v1, v1},
2570 {v1, v1},
2571 {v2, v2},
2572 {v2, v2},
2573 };
2574 std::vector<std::vector<const TrackingVolume*>> contentsY = {
2575 {v1, v1, v2, v2},
2576 {v1, v1, v2, v2},
2577 };
2578
2579 for (std::size_t i = 0; i < 4; ++i) {
2580 for (std::size_t j = 0; j < 2; ++j) {
2581 BOOST_CHECK_EQUAL(mergedX->grid().atLocalBins({i + 1, j + 1}),
2582 contentsX.at(i).at(j));
2583 }
2584 }
2585 for (std::size_t i = 0; i < 2; ++i) {
2586 for (std::size_t j = 0; j < 4; ++j) {
2587 BOOST_CHECK_EQUAL(mergedY->grid().atLocalBins({i + 1, j + 1}),
2588 contentsY.at(i).at(j));
2589 }
2590 }
2591 }
2592
2593 BOOST_AUTO_TEST_SUITE_END()
2594
2595 BOOST_AUTO_TEST_SUITE_END()
2596
2597 BOOST_AUTO_TEST_CASE(CompositeConstruction) {
2598
2599 auto vol1 = std::make_shared<TrackingVolume>(
2600 Transform3::Identity(),
2601 std::make_shared<CylinderVolumeBounds>(30_mm, 40_mm, 100_mm));
2602
2603 auto vol2 = std::make_shared<TrackingVolume>(
2604 Transform3::Identity(),
2605 std::make_shared<CylinderVolumeBounds>(30_mm, 40_mm, 100_mm));
2606
2607 auto vol3 = std::make_shared<TrackingVolume>(
2608 Transform3::Identity(),
2609 std::make_shared<CylinderVolumeBounds>(30_mm, 40_mm, 100_mm));
2610
2611 auto disc1 =
2612 Surface::makeShared<DiscSurface>(Transform3::Identity(), 30_mm, 60_mm);
2613
2614 auto trivial1 = std::make_unique<TrivialPortalLink>(disc1, *vol1);
2615
2616 auto disc2 =
2617 Surface::makeShared<DiscSurface>(Transform3::Identity(), 60_mm, 90_mm);
2618 auto trivial2 = std::make_unique<TrivialPortalLink>(disc2, *vol2);
2619
2620 auto composite = std::make_unique<CompositePortalLink>(
2621 copy(trivial1), copy(trivial2), AxisDirection::AxisR);
2622
2623 auto compositeCopy = std::make_unique<CompositePortalLink>(
2624 copy(trivial1), copy(trivial2), AxisDirection::AxisR);
2625
2626 BOOST_CHECK_EQUAL(
2627 composite->resolveVolume(gctx, Vector2{40_mm, 0_degree}).value(),
2628 vol1.get());
2629 BOOST_CHECK_EQUAL(
2630 composite->resolveVolume(gctx, Vector2{70_mm, 0_degree}).value(),
2631 vol2.get());
2632
2633 BOOST_CHECK_EQUAL(composite->depth(), 1);
2634
2635
2636 auto cyl = Surface::makeShared<CylinderSurface>(Transform3::Identity(), 30_mm,
2637 40_mm);
2638 auto trivialCyl = std::make_unique<TrivialPortalLink>(cyl, *vol3);
2639 BOOST_CHECK_THROW(CompositePortalLink(copy(trivial1), copy(trivialCyl),
2640 AxisDirection::AxisR),
2641 std::invalid_argument);
2642
2643 auto disc3 =
2644 Surface::makeShared<DiscSurface>(Transform3::Identity(), 90_mm, 120_mm);
2645 auto trivial3 = std::make_unique<TrivialPortalLink>(disc3, *vol3);
2646
2647
2648 BOOST_CHECK_THROW(
2649 CompositePortalLink(copy(trivial1), copy(trivial3), AxisDirection::AxisR),
2650 SurfaceMergingException);
2651
2652
2653
2654 CompositePortalLink composite2(std::move(composite), copy(trivial3),
2655 AxisDirection::AxisR, false);
2656
2657 BOOST_CHECK_EQUAL(
2658 composite2.resolveVolume(gctx, Vector2{40_mm, 0_degree}).value(),
2659 vol1.get());
2660 BOOST_CHECK_EQUAL(
2661 composite2.resolveVolume(gctx, Vector2{70_mm, 0_degree}).value(),
2662 vol2.get());
2663 BOOST_CHECK_EQUAL(
2664 composite2.resolveVolume(gctx, Vector2{100_mm, 0_degree}).value(),
2665 vol3.get());
2666
2667
2668 BOOST_CHECK_EQUAL(composite2.depth(), 2);
2669
2670 CompositePortalLink composite2Flat(std::move(compositeCopy), copy(trivial3),
2671 AxisDirection::AxisR, true);
2672
2673
2674 BOOST_CHECK_EQUAL(composite2Flat.depth(), 1);
2675
2676 BOOST_CHECK_EQUAL(
2677 composite2Flat.resolveVolume(gctx, Vector2{40_mm, 0_degree}).value(),
2678 vol1.get());
2679 BOOST_CHECK_EQUAL(
2680 composite2Flat.resolveVolume(gctx, Vector2{70_mm, 0_degree}).value(),
2681 vol2.get());
2682 BOOST_CHECK_EQUAL(
2683 composite2Flat.resolveVolume(gctx, Vector2{100_mm, 0_degree}).value(),
2684 vol3.get());
2685 }
2686
2687 BOOST_AUTO_TEST_SUITE(PortalMerging)
2688
2689 BOOST_DATA_TEST_CASE(TrivialTrivial,
2690 (boost::unit_test::data::make(0, -135, 180, 45) *
2691 boost::unit_test::data::make(Vector3{0_mm, 0_mm, 0_mm},
2692 Vector3{20_mm, 0_mm, 0_mm},
2693 Vector3{0_mm, 20_mm, 0_mm},
2694 Vector3{20_mm, 20_mm, 0_mm},
2695 Vector3{0_mm, 0_mm, 20_mm})),
2696 angle, offset) {
2697 Transform3 base =
2698 AngleAxis3(angle * 1_degree, Vector3::UnitX()) * Translation3(offset);
2699
2700 BOOST_TEST_CONTEXT("RDirection") {
2701 auto vol1 = std::make_shared<TrackingVolume>(
2702 base, std::make_shared<CylinderVolumeBounds>(30_mm, 40_mm, 100_mm));
2703
2704 auto vol2 = std::make_shared<TrackingVolume>(
2705 base, std::make_shared<CylinderVolumeBounds>(30_mm, 40_mm, 100_mm));
2706
2707 auto vol3 = std::make_shared<TrackingVolume>(
2708 base, std::make_shared<CylinderVolumeBounds>(40_mm, 50_mm, 100_mm));
2709
2710 auto disc1 =
2711 Surface::makeShared<DiscSurface>(Transform3::Identity(), 30_mm, 60_mm);
2712
2713 auto disc2 =
2714 Surface::makeShared<DiscSurface>(Transform3::Identity(), 60_mm, 90_mm);
2715
2716 auto disc3 =
2717 Surface::makeShared<DiscSurface>(Transform3::Identity(), 90_mm, 120_mm);
2718
2719 auto trivial1 = std::make_unique<TrivialPortalLink>(disc1, *vol1);
2720 BOOST_REQUIRE(trivial1);
2721 auto trivial2 = std::make_unique<TrivialPortalLink>(disc2, *vol2);
2722 BOOST_REQUIRE(trivial2);
2723 auto trivial3 = std::make_unique<TrivialPortalLink>(disc3, *vol3);
2724 BOOST_REQUIRE(trivial3);
2725
2726 auto grid1 = trivial1->makeGrid(AxisDirection::AxisR);
2727 auto compGridTrivial = PortalLinkBase::merge(
2728 std::move(grid1), std::make_unique<TrivialPortalLink>(*trivial2),
2729 AxisDirection::AxisR, *logger);
2730 BOOST_REQUIRE(compGridTrivial);
2731 BOOST_CHECK_EQUAL(dynamic_cast<CompositePortalLink&>(*compGridTrivial)
2732 .makeGrid(gctx, *logger),
2733 nullptr);
2734
2735 auto composite =
2736 PortalLinkBase::merge(std::move(trivial1), std::move(trivial2),
2737 AxisDirection::AxisR, *logger);
2738 BOOST_REQUIRE(composite);
2739
2740 auto grid12 =
2741 dynamic_cast<CompositePortalLink&>(*composite).makeGrid(gctx, *logger);
2742 BOOST_REQUIRE(grid12);
2743
2744 BOOST_CHECK_EQUAL(
2745 grid12->resolveVolume(gctx, Vector2{40_mm, 0_degree}).value(),
2746 vol1.get());
2747
2748 BOOST_CHECK_EQUAL(
2749 grid12->resolveVolume(gctx, Vector2{70_mm, 0_degree}).value(),
2750 vol2.get());
2751
2752 composite = PortalLinkBase::merge(std::move(composite), std::move(trivial3),
2753 AxisDirection::AxisR, *logger);
2754 BOOST_REQUIRE(composite);
2755
2756 auto grid123 =
2757 dynamic_cast<CompositePortalLink&>(*composite).makeGrid(gctx, *logger);
2758 BOOST_REQUIRE(grid123);
2759
2760 BOOST_CHECK_EQUAL(
2761 grid123->resolveVolume(gctx, Vector2{40_mm, 0_degree}).value(),
2762 vol1.get());
2763
2764 BOOST_CHECK_EQUAL(
2765 grid123->resolveVolume(gctx, Vector2{70_mm, 0_degree}).value(),
2766 vol2.get());
2767
2768 BOOST_CHECK_EQUAL(
2769 grid123->resolveVolume(gctx, Vector2{100_mm, 0_degree}).value(),
2770 vol3.get());
2771 }
2772
2773 BOOST_TEST_CONTEXT("ZDirection") {
2774 auto vol1 = std::make_shared<TrackingVolume>(
2775 base, std::make_shared<CylinderVolumeBounds>(30_mm, 40_mm, 100_mm));
2776
2777 auto vol2 = std::make_shared<TrackingVolume>(
2778 base * Translation3{Vector3::UnitZ() * 200},
2779 std::make_shared<CylinderVolumeBounds>(30_mm, 40_mm, 100_mm));
2780
2781 auto vol3 = std::make_shared<TrackingVolume>(
2782 base * Translation3{Vector3::UnitZ() * 400},
2783 std::make_shared<CylinderVolumeBounds>(30_mm, 40_mm, 100_mm));
2784
2785 auto cyl1 = Surface::makeShared<CylinderSurface>(base, 40_mm, 100_mm);
2786
2787 auto cyl2 = Surface::makeShared<CylinderSurface>(
2788 base * Translation3{Vector3::UnitZ() * 200}, 40_mm, 100_mm);
2789
2790 auto cyl3 = Surface::makeShared<CylinderSurface>(
2791 base * Translation3{Vector3::UnitZ() * 400}, 40_mm, 100_mm);
2792
2793 auto trivial1 = std::make_unique<TrivialPortalLink>(cyl1, *vol1);
2794 BOOST_REQUIRE(trivial1);
2795 auto trivial2 = std::make_unique<TrivialPortalLink>(cyl2, *vol2);
2796 BOOST_REQUIRE(trivial2);
2797 auto trivial3 = std::make_unique<TrivialPortalLink>(cyl3, *vol3);
2798 BOOST_REQUIRE(trivial3);
2799
2800 auto grid1 = trivial1->makeGrid(AxisDirection::AxisZ);
2801 auto compGridTrivial = PortalLinkBase::merge(
2802 std::move(grid1), std::make_unique<TrivialPortalLink>(*trivial2),
2803 AxisDirection::AxisZ, *logger);
2804 BOOST_REQUIRE(compGridTrivial);
2805 BOOST_CHECK_EQUAL(dynamic_cast<CompositePortalLink&>(*compGridTrivial)
2806 .makeGrid(gctx, *logger),
2807 nullptr);
2808
2809 auto composite =
2810 PortalLinkBase::merge(std::move(trivial1), std::move(trivial2),
2811 AxisDirection::AxisZ, *logger);
2812 BOOST_REQUIRE(composite);
2813
2814 auto grid12 =
2815 dynamic_cast<CompositePortalLink&>(*composite).makeGrid(gctx, *logger);
2816 BOOST_REQUIRE(grid12);
2817
2818 BOOST_CHECK_EQUAL(
2819 grid12->resolveVolume(gctx, Vector2{40_mm, -40_mm}).value(),
2820 vol1.get());
2821
2822 BOOST_CHECK_EQUAL(
2823 grid12->resolveVolume(gctx, Vector2{40_mm, 40_mm}).value(), vol2.get());
2824
2825 composite = PortalLinkBase::merge(std::move(composite), std::move(trivial3),
2826 AxisDirection::AxisZ, *logger);
2827 BOOST_REQUIRE(composite);
2828
2829 auto grid123 =
2830 dynamic_cast<CompositePortalLink&>(*composite).makeGrid(gctx, *logger);
2831 BOOST_REQUIRE(grid123);
2832
2833 BOOST_CHECK_EQUAL(
2834 grid123->resolveVolume(gctx, Vector2{40_mm, -110_mm}).value(),
2835 vol1.get());
2836
2837 BOOST_CHECK_EQUAL(
2838 grid123->resolveVolume(gctx, Vector2{40_mm, -10_mm}).value(),
2839 vol2.get());
2840
2841 BOOST_CHECK_EQUAL(
2842 grid123->resolveVolume(gctx, Vector2{40_mm, 190_mm}).value(),
2843 vol3.get());
2844 }
2845 BOOST_TEST_CONTEXT("PlaneXDirection") {
2846 auto vol1 = std::make_shared<TrackingVolume>(
2847 Transform3::Identity(),
2848 std::make_shared<CuboidVolumeBounds>(30_mm, 40_mm, 100_mm));
2849
2850 auto vol2 = std::make_shared<TrackingVolume>(
2851 Transform3::Identity() * Translation3{Vector3::UnitX() * 60},
2852 std::make_shared<CuboidVolumeBounds>(30_mm, 40_mm, 100_mm));
2853
2854 auto vol3 = std::make_shared<TrackingVolume>(
2855 Transform3::Identity() * Translation3{Vector3::UnitX() * 120},
2856 std::make_shared<CuboidVolumeBounds>(30_mm, 40_mm, 100_mm));
2857
2858 auto rBounds = std::make_shared<const RectangleBounds>(30_mm, 40_mm);
2859 auto plane1 =
2860 Surface::makeShared<PlaneSurface>(Transform3::Identity(), rBounds);
2861
2862 auto plane2 = Surface::makeShared<PlaneSurface>(
2863 Transform3::Identity() * Translation3{Vector3::UnitX() * 60}, rBounds);
2864
2865 auto plane3 = Surface::makeShared<PlaneSurface>(
2866 Transform3::Identity() * Translation3{Vector3::UnitX() * 120}, rBounds);
2867
2868 auto trivial1 = std::make_unique<TrivialPortalLink>(plane1, *vol1);
2869 BOOST_REQUIRE(trivial1);
2870 auto trivial2 = std::make_unique<TrivialPortalLink>(plane2, *vol2);
2871 BOOST_REQUIRE(trivial2);
2872 auto trivial3 = std::make_unique<TrivialPortalLink>(plane3, *vol3);
2873 BOOST_REQUIRE(trivial3);
2874
2875 auto grid1 = trivial1->makeGrid(AxisDirection::AxisX);
2876 auto compGridTrivial = PortalLinkBase::merge(
2877 std::move(grid1), std::make_unique<TrivialPortalLink>(*trivial2),
2878 AxisDirection::AxisX, *logger);
2879 BOOST_REQUIRE(compGridTrivial);
2880 BOOST_CHECK_EQUAL(dynamic_cast<CompositePortalLink&>(*compGridTrivial)
2881 .makeGrid(gctx, *logger),
2882 nullptr);
2883
2884 auto composite =
2885 PortalLinkBase::merge(std::move(trivial1), std::move(trivial2),
2886 AxisDirection::AxisX, *logger);
2887 BOOST_REQUIRE(composite);
2888
2889 auto grid12 =
2890 dynamic_cast<CompositePortalLink&>(*composite).makeGrid(gctx, *logger);
2891 BOOST_REQUIRE(grid12);
2892
2893 BOOST_CHECK_EQUAL(grid12->resolveVolume(gctx, Vector2{-30_mm, 0}).value(),
2894 vol1.get());
2895
2896 BOOST_CHECK_EQUAL(grid12->resolveVolume(gctx, Vector2{30_mm, 0}).value(),
2897 vol2.get());
2898
2899 composite = PortalLinkBase::merge(std::move(composite), std::move(trivial3),
2900 AxisDirection::AxisX, *logger);
2901 BOOST_REQUIRE(composite);
2902
2903 auto grid123 =
2904 dynamic_cast<CompositePortalLink&>(*composite).makeGrid(gctx, *logger);
2905 BOOST_REQUIRE(grid123);
2906
2907 BOOST_CHECK_EQUAL(
2908 grid123->resolveVolume(gctx, Vector2{-80_mm, 0_mm}).value(),
2909 vol1.get());
2910
2911 BOOST_CHECK_EQUAL(grid123->resolveVolume(gctx, Vector2{0_mm, 0_mm}).value(),
2912 vol2.get());
2913
2914 BOOST_CHECK_EQUAL(
2915 grid123->resolveVolume(gctx, Vector2{80_mm, 0_mm}).value(), vol3.get());
2916 }
2917 BOOST_TEST_CONTEXT("PlaneYDirection") {
2918 auto vol1 = std::make_shared<TrackingVolume>(
2919 Transform3::Identity(),
2920 std::make_shared<CuboidVolumeBounds>(30_mm, 40_mm, 100_mm));
2921
2922 auto vol2 = std::make_shared<TrackingVolume>(
2923 Transform3::Identity() * Translation3{Vector3::UnitY() * 80},
2924 std::make_shared<CuboidVolumeBounds>(30_mm, 40_mm, 100_mm));
2925
2926 auto vol3 = std::make_shared<TrackingVolume>(
2927 Transform3::Identity() * Translation3{Vector3::UnitY() * 160},
2928 std::make_shared<CuboidVolumeBounds>(30_mm, 40_mm, 100_mm));
2929
2930 auto rBounds = std::make_shared<const RectangleBounds>(30_mm, 40_mm);
2931 auto plane1 =
2932 Surface::makeShared<PlaneSurface>(Transform3::Identity(), rBounds);
2933
2934 auto plane2 = Surface::makeShared<PlaneSurface>(
2935 Transform3::Identity() * Translation3{Vector3::UnitY() * 80}, rBounds);
2936
2937 auto plane3 = Surface::makeShared<PlaneSurface>(
2938 Transform3::Identity() * Translation3{Vector3::UnitY() * 160}, rBounds);
2939
2940 auto trivial1 = std::make_unique<TrivialPortalLink>(plane1, *vol1);
2941 BOOST_REQUIRE(trivial1);
2942 auto trivial2 = std::make_unique<TrivialPortalLink>(plane2, *vol2);
2943 BOOST_REQUIRE(trivial2);
2944 auto trivial3 = std::make_unique<TrivialPortalLink>(plane3, *vol3);
2945 BOOST_REQUIRE(trivial3);
2946
2947 auto grid1 = trivial1->makeGrid(AxisDirection::AxisY);
2948 auto compGridTrivial = PortalLinkBase::merge(
2949 std::move(grid1), std::make_unique<TrivialPortalLink>(*trivial2),
2950 AxisDirection::AxisY, *logger);
2951 BOOST_REQUIRE(compGridTrivial);
2952 BOOST_CHECK_EQUAL(dynamic_cast<CompositePortalLink&>(*compGridTrivial)
2953 .makeGrid(gctx, *logger),
2954 nullptr);
2955
2956 auto composite =
2957 PortalLinkBase::merge(std::move(trivial1), std::move(trivial2),
2958 AxisDirection::AxisY, *logger);
2959 BOOST_REQUIRE(composite);
2960
2961 auto grid12 =
2962 dynamic_cast<CompositePortalLink&>(*composite).makeGrid(gctx, *logger);
2963 BOOST_REQUIRE(grid12);
2964
2965 BOOST_CHECK_EQUAL(
2966 grid12->resolveVolume(gctx, Vector2{0_mm, -40_mm}).value(), vol1.get());
2967
2968 BOOST_CHECK_EQUAL(grid12->resolveVolume(gctx, Vector2{0_mm, 40_mm}).value(),
2969 vol2.get());
2970
2971 composite = PortalLinkBase::merge(std::move(composite), std::move(trivial3),
2972 AxisDirection::AxisY, *logger);
2973 BOOST_REQUIRE(composite);
2974
2975 auto grid123 =
2976 dynamic_cast<CompositePortalLink&>(*composite).makeGrid(gctx, *logger);
2977 BOOST_REQUIRE(grid123);
2978
2979 BOOST_CHECK_EQUAL(
2980 grid123->resolveVolume(gctx, Vector2{0_mm, -110_mm}).value(),
2981 vol1.get());
2982
2983 BOOST_CHECK_EQUAL(
2984 grid123->resolveVolume(gctx, Vector2{0_mm, -10_mm}).value(),
2985 vol2.get());
2986
2987 BOOST_CHECK_EQUAL(
2988 grid123->resolveVolume(gctx, Vector2{0_mm, 110_mm}).value(),
2989 vol3.get());
2990 }
2991 }
2992
2993 BOOST_AUTO_TEST_CASE(TrivialGridR) {
2994 auto vol1 = std::make_shared<TrackingVolume>(
2995 Transform3::Identity(),
2996 std::make_shared<CylinderVolumeBounds>(30_mm, 40_mm, 100_mm));
2997
2998 auto vol2 = std::make_shared<TrackingVolume>(
2999 Transform3::Identity(),
3000 std::make_shared<CylinderVolumeBounds>(30_mm, 40_mm, 100_mm));
3001
3002 auto disc1 =
3003 Surface::makeShared<DiscSurface>(Transform3::Identity(), 30_mm, 60_mm);
3004
3005 auto disc2 =
3006 Surface::makeShared<DiscSurface>(Transform3::Identity(), 60_mm, 90_mm);
3007
3008 auto trivial = std::make_unique<TrivialPortalLink>(disc2, *vol2);
3009 BOOST_REQUIRE(trivial);
3010
3011 auto gridPhi = GridPortalLink::make(
3012 disc1, AxisDirection::AxisPhi,
3013 Axis{AxisClosed, -std::numbers::pi, std::numbers::pi, 2});
3014 gridPhi->setVolume(vol1.get());
3015
3016 auto gridR = GridPortalLink::make(disc1, AxisDirection::AxisR,
3017 Axis{AxisBound, 30_mm, 60_mm, 2});
3018 gridR->setVolume(vol1.get());
3019
3020 BOOST_TEST_CONTEXT("Colinear") {
3021 auto merged = PortalLinkBase::merge(copy(trivial), copy(gridR),
3022 AxisDirection::AxisR, *logger);
3023 BOOST_REQUIRE(merged);
3024 BOOST_CHECK_NE(dynamic_cast<CompositePortalLink*>(merged.get()), nullptr);
3025 }
3026
3027 BOOST_TEST_CONTEXT("Orthogonal") {
3028 auto merged = PortalLinkBase::merge(copy(gridPhi), copy(trivial),
3029 AxisDirection::AxisR, *logger);
3030 BOOST_REQUIRE(merged);
3031 BOOST_CHECK_NE(dynamic_cast<CompositePortalLink*>(merged.get()), nullptr);
3032 }
3033 }
3034
3035 BOOST_AUTO_TEST_CASE(TrivialGridPhi) {
3036 auto vol1 = std::make_shared<TrackingVolume>(
3037 Transform3::Identity(),
3038 std::make_shared<CylinderVolumeBounds>(30_mm, 40_mm, 100_mm));
3039
3040 auto vol2 = std::make_shared<TrackingVolume>(
3041 Transform3::Identity(),
3042 std::make_shared<CylinderVolumeBounds>(30_mm, 40_mm, 100_mm));
3043
3044 auto disc1 = Surface::makeShared<DiscSurface>(Transform3::Identity(), 30_mm,
3045 100_mm, 30_degree);
3046
3047 auto disc2 = Surface::makeShared<DiscSurface>(
3048 Transform3{AngleAxis3{90_degree, Vector3::UnitZ()}}, 30_mm, 100_mm,
3049 60_degree);
3050
3051 auto trivial = std::make_unique<TrivialPortalLink>(disc2, *vol2);
3052 BOOST_REQUIRE(trivial);
3053
3054 auto gridPhi = GridPortalLink::make(
3055 disc1, AxisDirection::AxisPhi, Axis{AxisBound, -30_degree, 30_degree, 2});
3056 gridPhi->setVolume(vol1.get());
3057
3058 auto gridR = GridPortalLink::make(disc1, AxisDirection::AxisR,
3059 Axis{AxisBound, 30_mm, 100_mm, 2});
3060 gridR->setVolume(vol1.get());
3061
3062 BOOST_TEST_CONTEXT("Colinear") {
3063 auto merged = PortalLinkBase::merge(copy(trivial), copy(gridPhi),
3064 AxisDirection::AxisPhi, *logger);
3065 BOOST_REQUIRE(merged);
3066 BOOST_CHECK_NE(dynamic_cast<CompositePortalLink*>(merged.get()), nullptr);
3067 }
3068
3069 BOOST_TEST_CONTEXT("Orthogonal") {
3070 auto merged = PortalLinkBase::merge(copy(gridR), copy(trivial),
3071 AxisDirection::AxisPhi, *logger);
3072 BOOST_REQUIRE(merged);
3073 BOOST_CHECK_NE(dynamic_cast<CompositePortalLink*>(merged.get()), nullptr);
3074 }
3075 }
3076
3077 BOOST_AUTO_TEST_CASE(TrivialGridXY) {
3078 auto vol1 = std::make_shared<TrackingVolume>(
3079 Transform3::Identity(),
3080 std::make_shared<CuboidVolumeBounds>(30_mm, 30_mm, 30_mm));
3081
3082 auto vol2 = std::make_shared<TrackingVolume>(
3083 Transform3::Identity() * Translation3{Vector3::UnitX() * 60},
3084 std::make_shared<CuboidVolumeBounds>(30_mm, 30_mm, 30_mm));
3085
3086 auto rBounds = std::make_shared<const RectangleBounds>(30_mm, 30_mm);
3087 auto plane1 =
3088 Surface::makeShared<PlaneSurface>(Transform3::Identity(), rBounds);
3089
3090 auto plane2 = Surface::makeShared<PlaneSurface>(
3091 Transform3::Identity() * Translation3{Vector3::UnitX() * 60}, rBounds);
3092
3093 auto gridX = GridPortalLink::make(plane1, AxisDirection::AxisX,
3094 Axis{AxisBound, -30_mm, 30_mm, 2});
3095 gridX->setVolume(vol1.get());
3096
3097 auto gridY = GridPortalLink::make(plane1, AxisDirection::AxisY,
3098 Axis{AxisBound, -30_mm, 30_mm, 2});
3099 gridY->setVolume(vol1.get());
3100
3101 auto trivial = std::make_unique<TrivialPortalLink>(plane2, *vol2);
3102 BOOST_REQUIRE(trivial);
3103
3104 BOOST_TEST_CONTEXT("Colinear") {
3105 auto merged = PortalLinkBase::merge(copy(trivial), copy(gridX),
3106 AxisDirection::AxisX, *logger);
3107 BOOST_REQUIRE(merged);
3108 BOOST_CHECK_NE(dynamic_cast<CompositePortalLink*>(merged.get()), nullptr);
3109 }
3110
3111 BOOST_TEST_CONTEXT("Orthogonal") {
3112 auto merged = PortalLinkBase::merge(copy(gridY), copy(trivial),
3113 AxisDirection::AxisX, *logger);
3114 BOOST_REQUIRE(merged);
3115 BOOST_CHECK_NE(dynamic_cast<CompositePortalLink*>(merged.get()), nullptr);
3116 }
3117 }
3118
3119 BOOST_AUTO_TEST_CASE(CompositeOther) {
3120 auto vol1 = std::make_shared<TrackingVolume>(
3121 Transform3::Identity(),
3122 std::make_shared<CylinderVolumeBounds>(30_mm, 40_mm, 100_mm));
3123
3124 auto vol2 = std::make_shared<TrackingVolume>(
3125 Transform3::Identity(),
3126 std::make_shared<CylinderVolumeBounds>(30_mm, 40_mm, 100_mm));
3127
3128 auto vol3 = std::make_shared<TrackingVolume>(
3129 Transform3::Identity(),
3130 std::make_shared<CylinderVolumeBounds>(30_mm, 40_mm, 100_mm));
3131
3132 auto disc1 =
3133 Surface::makeShared<DiscSurface>(Transform3::Identity(), 30_mm, 60_mm);
3134 auto disc2 =
3135 Surface::makeShared<DiscSurface>(Transform3::Identity(), 60_mm, 90_mm);
3136 auto disc3 =
3137 Surface::makeShared<DiscSurface>(Transform3::Identity(), 90_mm, 120_mm);
3138
3139 auto grid1 = GridPortalLink::make(disc1, *vol1, AxisDirection::AxisR);
3140 auto trivial2 = std::make_unique<TrivialPortalLink>(disc2, *vol2);
3141
3142 auto composite12 = std::make_unique<CompositePortalLink>(
3143 std::move(grid1), std::move(trivial2), AxisDirection::AxisR);
3144
3145 BOOST_CHECK_EQUAL(composite12->depth(), 1);
3146 BOOST_CHECK_EQUAL(composite12->size(), 2);
3147
3148 auto trivial3 = std::make_unique<TrivialPortalLink>(disc3, *vol3);
3149
3150 auto composite123Ptr =
3151 PortalLinkBase::merge(std::move(composite12), std::move(trivial3),
3152 AxisDirection::AxisR, *logger);
3153
3154 const auto* composite123 =
3155 dynamic_cast<const CompositePortalLink*>(composite123Ptr.get());
3156 BOOST_REQUIRE(composite123);
3157
3158 BOOST_CHECK_EQUAL(composite123->depth(), 1);
3159
3160 BOOST_CHECK_EQUAL(
3161 composite123->resolveVolume(gctx, Vector2{40_mm, 0_degree}).value(),
3162 vol1.get());
3163 BOOST_CHECK_EQUAL(
3164 composite123->resolveVolume(gctx, Vector2{70_mm, 0_degree}).value(),
3165 vol2.get());
3166 BOOST_CHECK_EQUAL(
3167 composite123->resolveVolume(gctx, Vector2{100_mm, 0_degree}).value(),
3168 vol3.get());
3169
3170 BOOST_CHECK_EQUAL(composite123->size(), 3);
3171 }
3172
3173 BOOST_AUTO_TEST_SUITE_END()
3174
3175 BOOST_AUTO_TEST_SUITE_END()
3176
3177 }