File indexing completed on 2025-01-18 09:12:40
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/CylinderVolumeBounds.hpp"
0019 #include "Acts/Geometry/GridPortalLink.hpp"
0020 #include "Acts/Geometry/TrackingVolume.hpp"
0021 #include "Acts/Geometry/TrivialPortalLink.hpp"
0022 #include "Acts/Surfaces/CylinderSurface.hpp"
0023 #include "Acts/Surfaces/RadialBounds.hpp"
0024 #include "Acts/Surfaces/SurfaceMergingException.hpp"
0025 #include "Acts/Tests/CommonHelpers/FloatComparisons.hpp"
0026 #include "Acts/Utilities/AxisDefinitions.hpp"
0027 #include "Acts/Utilities/ThrowAssert.hpp"
0028
0029 #include <cstdio>
0030 #include <iostream>
0031 #include <memory>
0032 #include <numbers>
0033 #include <stdexcept>
0034
0035 using namespace Acts::UnitLiterals;
0036
0037 namespace Acts::Test {
0038
0039 auto logger = Acts::getDefaultLogger("UnitTests", Acts::Logging::VERBOSE);
0040
0041 struct Fixture {
0042 Logging::Level m_level;
0043 Fixture() {
0044 m_level = Acts::Logging::getFailureThreshold();
0045 Acts::Logging::setFailureThreshold(Acts::Logging::FATAL);
0046 }
0047
0048 ~Fixture() { Acts::Logging::setFailureThreshold(m_level); }
0049 };
0050
0051 std::shared_ptr<TrackingVolume> makeDummyVolume() {
0052 return std::make_shared<TrackingVolume>(
0053 Transform3::Identity(),
0054 std::make_shared<CylinderVolumeBounds>(30_mm, 40_mm, 100_mm));
0055 }
0056
0057 GeometryContext gctx;
0058
0059 template <typename T>
0060 std::unique_ptr<T> copy(const std::unique_ptr<T>& p) {
0061 return std::make_unique<T>(*p);
0062 }
0063
0064 template <typename link_t>
0065 void visitBins(const link_t& link,
0066 const std::function<void(const TrackingVolume*)>& func) {
0067 auto& grid = link.grid();
0068 auto loc = grid.numLocalBins();
0069 if constexpr (std::decay_t<decltype(grid)>::DIM == 1) {
0070 for (std::size_t i = 1; i <= loc[0]; i++) {
0071 func(grid.atLocalBins({i}));
0072 }
0073 } else {
0074 for (std::size_t i = 1; i <= loc[0]; i++) {
0075 for (std::size_t j = 1; j <= loc[1]; j++) {
0076 func(grid.atLocalBins({i, j}));
0077 }
0078 }
0079 }
0080 }
0081
0082 BOOST_FIXTURE_TEST_SUITE(Geometry, Fixture)
0083
0084 BOOST_AUTO_TEST_SUITE(GridConstruction)
0085
0086 BOOST_AUTO_TEST_CASE(Cylinder) {
0087 BOOST_TEST_CONTEXT("1D") {
0088 auto cyl = Surface::makeShared<CylinderSurface>(Transform3::Identity(),
0089 30_mm, 100_mm);
0090
0091
0092 auto vol = std::make_shared<TrackingVolume>(
0093 Transform3::Identity(),
0094 std::make_shared<CylinderVolumeBounds>(30_mm, 40_mm, 100_mm));
0095
0096
0097 BOOST_CHECK_THROW(GridPortalLink::make(cyl, AxisDirection::AxisZ,
0098 Axis{AxisBound, 0, 5, 5}),
0099 std::invalid_argument);
0100
0101 auto grid1dCyl = GridPortalLink::make(cyl, AxisDirection::AxisZ,
0102 Axis{AxisBound, -100_mm, 100_mm, 10});
0103 BOOST_REQUIRE(grid1dCyl);
0104 grid1dCyl->setVolume(vol.get());
0105
0106
0107 BOOST_CHECK_THROW(GridPortalLink::make(cyl, AxisDirection::AxisRPhi,
0108 Axis{AxisBound, -180_degree * 30_mm,
0109 180_degree * 30_mm, 10}),
0110 std::invalid_argument);
0111
0112 auto grid1dCylRPhi = GridPortalLink::make(
0113 cyl, AxisDirection::AxisRPhi,
0114 Axis{AxisClosed, -180_degree * 30_mm, 180_degree * 30_mm, 10});
0115 BOOST_REQUIRE_NE(grid1dCylRPhi, nullptr);
0116 grid1dCylRPhi->setVolume(vol.get());
0117
0118 Axis axisExpected{AxisClosed, -180_degree * 30_mm, 180_degree * 30_mm, 10};
0119 BOOST_CHECK_EQUAL(grid1dCylRPhi->grid().axes().size(), 1);
0120 const auto& axis = *grid1dCylRPhi->grid().axes().front();
0121 BOOST_CHECK_EQUAL(axis, axisExpected);
0122
0123
0124 auto cyl2 = Surface::makeShared<CylinderSurface>(
0125 Transform3{Translation3{Vector3::UnitZ() * 150_mm}}, 30_mm, 50_mm);
0126
0127 auto grid1dCyl2 = GridPortalLink::make(cyl2, AxisDirection::AxisZ,
0128 Axis{AxisBound, -50_mm, 50_mm, 5});
0129
0130
0131 auto cylNonZeroAverage = Surface::makeShared<CylinderSurface>(
0132 Transform3::Identity(), 30_mm, 100_mm, 20_degree, 45_degree);
0133 BOOST_CHECK_THROW(
0134 GridPortalLink::make(cylNonZeroAverage, AxisDirection::AxisZ,
0135 Axis{AxisBound, -100_mm, 100_mm, 10}),
0136 std::invalid_argument);
0137
0138 auto checkAllBins = [&](const auto& link) {
0139 visitBins(link, [&](const TrackingVolume* content) {
0140 BOOST_CHECK_EQUAL(content, vol.get());
0141 });
0142 };
0143
0144 checkAllBins(*grid1dCyl);
0145 checkAllBins(*grid1dCylRPhi);
0146
0147
0148
0149 auto grid2dCyl1 = grid1dCyl->extendTo2d(nullptr);
0150 BOOST_REQUIRE(grid2dCyl1);
0151 BOOST_CHECK_EQUAL(grid2dCyl1->grid().axes().size(), 2);
0152 BOOST_CHECK_EQUAL(grid2dCyl1->surface().bounds(), cyl->bounds());
0153 const auto* axis1 = grid2dCyl1->grid().axes().front();
0154 const auto* axis2 = grid2dCyl1->grid().axes().back();
0155
0156 Axis axis1Expected{AxisClosed, -std::numbers::pi * 30_mm,
0157 std::numbers::pi * 30_mm, 1};
0158 BOOST_CHECK_EQUAL(*axis1, axis1Expected);
0159 Axis axis2Expected{AxisBound, -100_mm, 100_mm, 10};
0160 BOOST_CHECK_EQUAL(*axis2, axis2Expected);
0161
0162 auto& concrete = dynamic_cast<
0163 GridPortalLinkT<decltype(axis1Expected), decltype(axis2Expected)>&>(
0164 *grid2dCyl1);
0165
0166 checkAllBins(concrete);
0167
0168 Axis axis1Explicit{AxisClosed, -std::numbers::pi * 30_mm,
0169 std::numbers::pi * 30_mm, 13};
0170 auto grid2dCyl1Explicit = grid1dCyl->extendTo2d(&axis1Explicit);
0171 BOOST_REQUIRE(grid2dCyl1Explicit);
0172 BOOST_CHECK_EQUAL(grid2dCyl1Explicit->grid().axes().size(), 2);
0173 axis1 = grid2dCyl1Explicit->grid().axes().front();
0174 axis2 = grid2dCyl1Explicit->grid().axes().back();
0175
0176 BOOST_CHECK_EQUAL(*axis1, axis1Explicit);
0177 BOOST_CHECK_EQUAL(*axis2, axis2Expected);
0178
0179 auto& concrete2 = dynamic_cast<
0180 GridPortalLinkT<decltype(axis1Explicit), decltype(axis2Expected)>&>(
0181 *grid2dCyl1Explicit);
0182
0183 checkAllBins(concrete2);
0184
0185 auto cylPhi = Surface::makeShared<CylinderSurface>(
0186 Transform3::Identity(), 30_mm, 100_mm, 45_degree);
0187 std::unique_ptr<GridPortalLink> grid1dCylPhi = GridPortalLink::make(
0188 cylPhi, AxisDirection::AxisZ, Axis{AxisBound, -100_mm, 100_mm, 10});
0189
0190 grid1dCylPhi->setVolume(vol.get());
0191
0192
0193 BOOST_CHECK_THROW(GridPortalLink::make(cylPhi, AxisDirection::AxisRPhi,
0194 Axis{AxisClosed, -45_degree * 30_mm,
0195 45_degree * 30_mm, 10}),
0196 std::invalid_argument);
0197
0198 auto grid2dCylPhi = grid1dCylPhi->extendTo2d(nullptr);
0199 BOOST_CHECK_EQUAL(grid2dCylPhi->grid().axes().size(), 2);
0200 BOOST_CHECK_EQUAL(grid2dCylPhi->surface().bounds(), cylPhi->bounds());
0201 const auto* axis1Phi = grid2dCylPhi->grid().axes().front();
0202 const auto* axis2Phi = grid2dCylPhi->grid().axes().back();
0203
0204 Axis axis1PhiExpected{AxisBound, -45_degree * 30_mm, 45_degree * 30_mm, 1};
0205 BOOST_CHECK_EQUAL(*axis1Phi, axis1PhiExpected);
0206 Axis axis2PhiExpected{AxisBound, -100_mm, 100_mm, 10};
0207 BOOST_CHECK_EQUAL(*axis2Phi, axis2PhiExpected);
0208
0209 auto& concrete3 =
0210 dynamic_cast<GridPortalLinkT<decltype(axis1PhiExpected),
0211 decltype(axis2PhiExpected)>&>(
0212 *grid2dCylPhi);
0213
0214 checkAllBins(concrete3);
0215
0216 Axis axis1PhiExplicit{AxisBound, -45_degree * 30_mm, 45_degree * 30_mm, 13};
0217 auto grid2dCylPhiExplicit = grid1dCylPhi->extendTo2d(&axis1PhiExplicit);
0218 BOOST_REQUIRE(grid2dCylPhiExplicit);
0219 BOOST_CHECK_EQUAL(grid2dCylPhiExplicit->grid().axes().size(), 2);
0220 axis1Phi = grid2dCylPhiExplicit->grid().axes().front();
0221 axis2Phi = grid2dCylPhiExplicit->grid().axes().back();
0222 BOOST_CHECK_EQUAL(*axis1Phi, axis1PhiExplicit);
0223 BOOST_CHECK_EQUAL(*axis2Phi, axis2PhiExpected);
0224
0225 auto& concrete4 =
0226 dynamic_cast<GridPortalLinkT<decltype(axis1PhiExplicit),
0227 decltype(axis2PhiExpected)>&>(
0228 *grid2dCylPhiExplicit);
0229
0230 checkAllBins(concrete4);
0231 }
0232
0233 BOOST_TEST_CONTEXT("2D") {
0234 auto cyl = Surface::makeShared<CylinderSurface>(Transform3::Identity(),
0235 30_mm, 100_mm, 45_degree);
0236
0237
0238 BOOST_CHECK_THROW(GridPortalLink::make(cyl, Axis{AxisBound, 1, 2, 5},
0239 Axis{AxisBound, 3_mm, 4_mm, 5}),
0240 std::invalid_argument);
0241
0242
0243 BOOST_CHECK_THROW(GridPortalLink::make(cyl, Axis{AxisBound, 3_mm, 4_mm, 5},
0244 Axis{AxisBound, -100_mm, 100_m, 5}),
0245 std::invalid_argument);
0246
0247
0248 BOOST_CHECK_THROW(
0249 GridPortalLink::make(
0250 cyl, Axis{AxisBound, -45_degree * 30_mm, 45_degree * 30_mm, 5},
0251 Axis{AxisBound, -80_mm, 100_mm, 5}),
0252 std::invalid_argument);
0253
0254 auto grid1 = GridPortalLink::make(
0255 cyl, Axis{AxisBound, -45_degree * 30_mm, 45_degree * 30_mm, 5},
0256 Axis{AxisBound, -100_mm, 100_mm, 5});
0257
0258 auto cylFull = Surface::makeShared<CylinderSurface>(Transform3::Identity(),
0259 30_mm, 100_mm);
0260
0261
0262 BOOST_CHECK_THROW(GridPortalLink::make(cylFull,
0263 Axis{AxisBound, -180_degree * 30_mm,
0264 180_degree * 30_mm, 5},
0265 Axis{AxisBound, -100_mm, 100_mm, 5}),
0266 std::invalid_argument);
0267
0268 auto gridFull = GridPortalLink::make(
0269 cylFull, Axis{AxisClosed, -180_degree * 30_mm, 180_degree * 30_mm, 5},
0270 Axis{AxisBound, -100_mm, 100_mm, 5});
0271
0272 BOOST_CHECK_EQUAL(gridFull->grid().axes().size(), 2);
0273 BOOST_CHECK_EQUAL(gridFull->grid().axes().size(), 2);
0274 Axis axisFullExpected{AxisClosed, -180_degree * 30_mm, 180_degree * 30_mm,
0275 5};
0276 const auto& axisFull = *gridFull->grid().axes().front();
0277 BOOST_CHECK_EQUAL(axisFull, axisFullExpected);
0278 }
0279 }
0280
0281 BOOST_AUTO_TEST_CASE(Disc) {
0282 using enum AxisType;
0283 using enum AxisBoundaryType;
0284 BOOST_TEST_CONTEXT("1D") {
0285 auto disc1 =
0286 Surface::makeShared<DiscSurface>(Transform3::Identity(), 30_mm, 100_mm);
0287
0288
0289 auto vol = std::make_shared<TrackingVolume>(
0290 Transform3::Identity(),
0291 std::make_shared<CylinderVolumeBounds>(30_mm, 40_mm, 100_mm));
0292
0293 BOOST_CHECK_THROW(GridPortalLink::make(disc1, AxisDirection::AxisZ,
0294 Axis{AxisBound, 30_mm, 100_mm, 3}),
0295 std::invalid_argument);
0296
0297
0298 BOOST_CHECK_THROW(
0299 GridPortalLink::make(disc1, AxisDirection::AxisPhi,
0300 Axis{AxisBound, -180_degree, 180_degree, 3}),
0301 std::invalid_argument);
0302
0303 auto grid1 = GridPortalLink::make(disc1, AxisDirection::AxisR,
0304 Axis{AxisBound, 30_mm, 100_mm, 3});
0305 BOOST_REQUIRE_NE(grid1, nullptr);
0306 BOOST_CHECK_EQUAL(grid1->grid().axes().size(), 1);
0307 const auto& axis = *grid1->grid().axes().front();
0308 Axis axis1Expected{AxisBound, 30_mm, 100_mm, 3};
0309 BOOST_CHECK_EQUAL(axis, axis1Expected);
0310
0311 grid1->setVolume(vol.get());
0312
0313 auto discPhi = Surface::makeShared<DiscSurface>(Transform3::Identity(),
0314 30_mm, 100_mm, 45_degree);
0315
0316
0317 BOOST_CHECK_THROW(
0318 GridPortalLink::make(discPhi, AxisDirection::AxisPhi,
0319 Axis{AxisClosed, -45_degree, 45_degree, 3}),
0320 std::invalid_argument);
0321
0322 auto gridPhi =
0323 GridPortalLink::make(discPhi, AxisDirection::AxisPhi,
0324 Axis{AxisBound, -45_degree, 45_degree, 3});
0325 BOOST_REQUIRE_NE(gridPhi, nullptr);
0326 gridPhi->setVolume(vol.get());
0327
0328
0329 auto discNonZeroAverage = Surface::makeShared<DiscSurface>(
0330 Transform3::Identity(),
0331 std::make_shared<RadialBounds>(30_mm, 100_mm, 45_degree, 75_degree));
0332 BOOST_CHECK_THROW(
0333 GridPortalLink::make(discNonZeroAverage, AxisDirection::AxisR,
0334 Axis{AxisBound, 30_mm, 100_mm, 3}),
0335 std::invalid_argument);
0336
0337 BOOST_CHECK_EQUAL(gridPhi->grid().axes().size(), 1);
0338 const auto& axisPhi = *gridPhi->grid().axes().front();
0339 Axis axisPhi1Expected{AxisBound, -45_degree, 45_degree, 3};
0340 BOOST_CHECK_EQUAL(axisPhi, axisPhi1Expected);
0341
0342 auto checkAllBins = [&](const auto& grid) {
0343 visitBins(grid, [&](const TrackingVolume* content) {
0344 BOOST_CHECK_EQUAL(content, vol.get());
0345 });
0346 };
0347
0348 checkAllBins(*grid1);
0349 checkAllBins(*gridPhi);
0350
0351
0352 auto grid2d = grid1->extendTo2d(nullptr);
0353 BOOST_REQUIRE(grid2d);
0354 BOOST_CHECK_EQUAL(grid2d->grid().axes().size(), 2);
0355 const auto* axis1 = grid2d->grid().axes().front();
0356 BOOST_CHECK_EQUAL(*axis1, axis1Expected);
0357 const auto* axis2 = grid2d->grid().axes().back();
0358 BOOST_CHECK_CLOSE(axis2->getMin(), -180_degree, 1e-6);
0359 BOOST_CHECK_CLOSE(axis2->getMax(), 180_degree, 1e-6);
0360 BOOST_CHECK_EQUAL(axis2->getNBins(), 1);
0361 BOOST_CHECK_EQUAL(axis2->getType(), AxisType::Equidistant);
0362 BOOST_CHECK_EQUAL(axis2->getBoundaryType(), AxisBoundaryType::Closed);
0363
0364 checkAllBins(
0365 dynamic_cast<GridPortalLinkT<decltype(axisPhi1Expected),
0366 Axis<Equidistant, Closed>>&>(*grid2d));
0367
0368 Axis axis2Explicit{AxisClosed, -180_degree, 180_degree, 3};
0369 auto grid2dExplicit = grid1->extendTo2d(&axis2Explicit);
0370 BOOST_REQUIRE(grid2dExplicit);
0371 BOOST_CHECK_EQUAL(grid2dExplicit->grid().axes().size(), 2);
0372 axis1 = grid2dExplicit->grid().axes().front();
0373 axis2 = grid2dExplicit->grid().axes().back();
0374 BOOST_CHECK_EQUAL(*axis1, axis1Expected);
0375 BOOST_CHECK_EQUAL(*axis2, axis2Explicit);
0376
0377 checkAllBins(dynamic_cast<GridPortalLinkT<decltype(axisPhi1Expected),
0378 decltype(axis2Explicit)>&>(
0379 *grid2dExplicit));
0380
0381 auto gridPhiBinnedInR = GridPortalLink::make(
0382 discPhi, AxisDirection::AxisR, Axis{AxisBound, 30_mm, 100_mm, 3});
0383 gridPhiBinnedInR->setVolume(vol.get());
0384 auto grid2dPhiNonClosed = gridPhiBinnedInR->extendTo2d(nullptr);
0385 BOOST_REQUIRE(grid2dPhiNonClosed);
0386 BOOST_CHECK_EQUAL(grid2dPhiNonClosed->grid().axes().size(), 2);
0387 Axis gridPhiBinnedInRExpected{AxisBound, 30_mm, 100_mm, 3};
0388 BOOST_CHECK_EQUAL(*grid2dPhiNonClosed->grid().axes().front(),
0389 gridPhiBinnedInRExpected);
0390 const auto* axisPhiNonClosed = grid2dPhiNonClosed->grid().axes().back();
0391 BOOST_CHECK_CLOSE(axisPhiNonClosed->getMin(), -45_degree, 1e-6);
0392 BOOST_CHECK_CLOSE(axisPhiNonClosed->getMax(), 45_degree, 1e-6);
0393 BOOST_CHECK_EQUAL(axisPhiNonClosed->getNBins(), 1);
0394 BOOST_CHECK_EQUAL(axisPhiNonClosed->getType(), AxisType::Equidistant);
0395 BOOST_CHECK_EQUAL(axisPhiNonClosed->getBoundaryType(),
0396 AxisBoundaryType::Bound);
0397
0398 checkAllBins(
0399 dynamic_cast<GridPortalLinkT<decltype(gridPhiBinnedInRExpected),
0400 Axis<Equidistant, Bound>>&>(
0401 *grid2dPhiNonClosed));
0402
0403 Axis axisPhiNonClosedExplicit{AxisBound, -45_degree, 45_degree, 3};
0404 auto grid2dPhiNonClosedExplicit =
0405 gridPhiBinnedInR->extendTo2d(&axisPhiNonClosedExplicit);
0406 BOOST_REQUIRE(grid2dPhiNonClosedExplicit);
0407 BOOST_CHECK_EQUAL(grid2dPhiNonClosedExplicit->grid().axes().size(), 2);
0408 axisPhiNonClosed = grid2dPhiNonClosedExplicit->grid().axes().back();
0409 BOOST_CHECK_EQUAL(*axisPhiNonClosed, axisPhiNonClosedExplicit);
0410 BOOST_CHECK_EQUAL(*grid2dPhiNonClosedExplicit->grid().axes().front(),
0411 gridPhiBinnedInRExpected);
0412
0413 checkAllBins(
0414 dynamic_cast<GridPortalLinkT<decltype(gridPhiBinnedInRExpected),
0415 decltype(axisPhiNonClosedExplicit)>&>(
0416 *grid2dPhiNonClosedExplicit));
0417
0418 auto grid2dPhi = gridPhi->extendTo2d(nullptr);
0419 BOOST_REQUIRE(grid2dPhi);
0420 BOOST_CHECK_EQUAL(grid2dPhi->grid().axes().size(), 2);
0421 Axis axis2dPhiExpected{AxisBound, 30_mm, 100_mm, 1};
0422 BOOST_CHECK_EQUAL(*grid2dPhi->grid().axes().front(), axis2dPhiExpected);
0423 BOOST_CHECK_EQUAL(*grid2dPhi->grid().axes().back(), axisPhi1Expected);
0424
0425 checkAllBins(
0426 dynamic_cast<GridPortalLinkT<decltype(axis2dPhiExpected),
0427 decltype(axisPhi1Expected)>&>(*grid2dPhi));
0428
0429 Axis axis2dPhiExplicit{AxisBound, 30_mm, 100_mm, 3};
0430 auto grid2dPhiExplicit = gridPhi->extendTo2d(&axis2dPhiExplicit);
0431 BOOST_REQUIRE(grid2dPhiExplicit);
0432 BOOST_CHECK_EQUAL(grid2dPhiExplicit->grid().axes().size(), 2);
0433 BOOST_CHECK_EQUAL(*grid2dPhiExplicit->grid().axes().front(),
0434 axis2dPhiExplicit);
0435 BOOST_CHECK_EQUAL(*grid2dPhiExplicit->grid().axes().back(),
0436 axisPhi1Expected);
0437
0438 checkAllBins(dynamic_cast<GridPortalLinkT<decltype(axisPhi1Expected),
0439 decltype(axis2dPhiExplicit)>&>(
0440 *grid2dPhiExplicit));
0441 }
0442
0443 BOOST_TEST_CONTEXT("2D") {
0444 auto discPhi = Surface::makeShared<DiscSurface>(Transform3::Identity(),
0445 30_mm, 100_mm, 45_degree);
0446
0447 Axis rBad{AxisBound, 1, 2, 5};
0448 Axis rGood{AxisBound, 30_mm, 100_mm, 5};
0449 Axis phiBad{AxisBound, 1, 2, 5};
0450 Axis phiGood{AxisBound, -45_degree, 45_degree, 5};
0451
0452
0453 BOOST_CHECK_THROW(GridPortalLink::make(discPhi, rBad, phiBad),
0454 std::invalid_argument);
0455
0456 BOOST_CHECK_THROW(GridPortalLink::make(discPhi, rBad, phiGood),
0457 std::invalid_argument);
0458
0459 BOOST_CHECK_THROW(GridPortalLink::make(discPhi, rGood, phiBad),
0460 std::invalid_argument);
0461
0462 auto grid = GridPortalLink::make(discPhi, rGood, phiGood);
0463 BOOST_REQUIRE_NE(grid, nullptr);
0464 }
0465 }
0466
0467 BOOST_AUTO_TEST_CASE(Plane) {
0468
0469 }
0470
0471 BOOST_AUTO_TEST_CASE(FromTrivial) {
0472 BOOST_TEST_CONTEXT("Cylinder") {
0473 auto cyl = Surface::makeShared<CylinderSurface>(Transform3::Identity(),
0474 30_mm, 100_mm);
0475
0476 auto vol = std::make_shared<TrackingVolume>(
0477 Transform3::Identity(),
0478 std::make_shared<CylinderVolumeBounds>(30_mm, 40_mm, 100_mm));
0479
0480 auto trivial = std::make_unique<TrivialPortalLink>(cyl, *vol);
0481 BOOST_REQUIRE(trivial);
0482
0483 BOOST_CHECK_EQUAL(trivial->resolveVolume(gctx, Vector2{1, 2}).value(),
0484 vol.get());
0485
0486 auto gridZ = trivial->makeGrid(AxisDirection::AxisZ);
0487 BOOST_REQUIRE(gridZ);
0488
0489 BOOST_CHECK_EQUAL(gridZ->grid().axes().size(), 1);
0490 BOOST_CHECK_EQUAL(gridZ->surface().bounds(), cyl->bounds());
0491 Axis axisZExpected{AxisBound, -100_mm, 100_mm, 1};
0492 BOOST_CHECK_EQUAL(*gridZ->grid().axes().front(), axisZExpected);
0493
0494 BOOST_CHECK_EQUAL(
0495 gridZ->resolveVolume(gctx, Vector2{20_degree * 30_mm, 90_mm}).value(),
0496 vol.get());
0497
0498 auto gridRPhi = trivial->makeGrid(AxisDirection::AxisRPhi);
0499 BOOST_REQUIRE(gridRPhi);
0500
0501 BOOST_CHECK_EQUAL(gridRPhi->grid().axes().size(), 1);
0502 BOOST_CHECK_EQUAL(gridRPhi->surface().bounds(), cyl->bounds());
0503 Axis axisRPhiExpected{AxisClosed, -std::numbers::pi * 30_mm,
0504 std::numbers::pi * 30_mm, 1};
0505 BOOST_CHECK_EQUAL(*gridRPhi->grid().axes().front(), axisRPhiExpected);
0506
0507 auto cylPhi = Surface::makeShared<CylinderSurface>(
0508 Transform3::Identity(), 30_mm, 100_mm, 30_degree);
0509
0510 auto trivialPhi = std::make_unique<TrivialPortalLink>(cylPhi, *vol);
0511 BOOST_REQUIRE(trivialPhi);
0512
0513 BOOST_CHECK_EQUAL(trivialPhi->resolveVolume(gctx, Vector2{1, 2}).value(),
0514 vol.get());
0515
0516 auto gridRPhiSector = trivialPhi->makeGrid(AxisDirection::AxisRPhi);
0517 BOOST_REQUIRE(gridRPhiSector);
0518
0519 BOOST_CHECK_EQUAL(
0520 gridRPhiSector->resolveVolume(gctx, Vector2{20_degree * 30_mm, 90_mm})
0521 .value(),
0522 vol.get());
0523
0524 BOOST_CHECK_EQUAL(gridRPhiSector->grid().axes().size(), 1);
0525 Axis axisRPhiSectorExpected{AxisBound, -30_degree * 30_mm,
0526 30_degree * 30_mm, 1};
0527 BOOST_CHECK_EQUAL(*gridRPhiSector->grid().axes().front(),
0528 axisRPhiSectorExpected);
0529 }
0530
0531 BOOST_TEST_CONTEXT("Disc") {
0532 auto disc =
0533 Surface::makeShared<DiscSurface>(Transform3::Identity(), 30_mm, 100_mm);
0534
0535 auto vol = std::make_shared<TrackingVolume>(
0536 Transform3::Identity(),
0537 std::make_shared<CylinderVolumeBounds>(30_mm, 40_mm, 100_mm));
0538
0539 auto trivial = std::make_unique<TrivialPortalLink>(disc, *vol);
0540 BOOST_REQUIRE(trivial);
0541
0542
0543 BOOST_CHECK_EQUAL(trivial->resolveVolume(gctx, Vector2{1, 2}).value(),
0544 vol.get());
0545
0546 auto gridR = trivial->makeGrid(AxisDirection::AxisR);
0547 BOOST_REQUIRE(gridR);
0548
0549 BOOST_CHECK_EQUAL(gridR->grid().axes().size(), 1);
0550 BOOST_CHECK_EQUAL(gridR->surface().bounds(), disc->bounds());
0551 Axis axisRExpected{AxisBound, 30_mm, 100_mm, 1};
0552 BOOST_CHECK_EQUAL(*gridR->grid().axes().front(), axisRExpected);
0553
0554 BOOST_CHECK_EQUAL(
0555 gridR->resolveVolume(gctx, Vector2{90_mm, 10_degree}).value(),
0556 vol.get());
0557
0558 auto gridPhi = trivial->makeGrid(AxisDirection::AxisPhi);
0559 BOOST_REQUIRE(gridPhi);
0560
0561 BOOST_CHECK_EQUAL(gridPhi->grid().axes().size(), 1);
0562 BOOST_CHECK_EQUAL(gridPhi->surface().bounds(), disc->bounds());
0563 Axis axisPhiExpected{AxisClosed, -std::numbers::pi, std::numbers::pi, 1};
0564 BOOST_CHECK_EQUAL(*gridPhi->grid().axes().front(), axisPhiExpected);
0565
0566 BOOST_CHECK_EQUAL(
0567 gridPhi->resolveVolume(gctx, Vector2{90_mm, 10_degree}).value(),
0568 vol.get());
0569 }
0570 }
0571
0572 BOOST_AUTO_TEST_SUITE_END()
0573
0574 BOOST_AUTO_TEST_SUITE(GridMerging)
0575
0576 BOOST_AUTO_TEST_SUITE(Merging1dCylinder)
0577
0578 BOOST_AUTO_TEST_SUITE(ZDirection)
0579
0580 BOOST_AUTO_TEST_CASE(ColinearMerge) {
0581 auto vol1 = makeDummyVolume();
0582 auto vol2 = makeDummyVolume();
0583
0584 auto cyl = Surface::makeShared<CylinderSurface>(Transform3::Identity(), 30_mm,
0585 100_mm);
0586
0587 auto grid1dCyl = GridPortalLink::make(cyl, AxisDirection::AxisZ,
0588 Axis{AxisBound, -100_mm, 100_mm, 10});
0589 grid1dCyl->setVolume(vol1.get());
0590
0591
0592 auto cyl2 = Surface::makeShared<CylinderSurface>(
0593 Transform3{Translation3{Vector3::UnitZ() * 150_mm}}, 30_mm, 50_mm);
0594
0595 auto grid1dCyl2 = GridPortalLink::make(cyl2, AxisDirection::AxisZ,
0596 Axis{AxisBound, -50_mm, 50_mm, 5});
0597
0598 grid1dCyl2->setVolume(vol2.get());
0599
0600
0601 BOOST_CHECK_THROW(GridPortalLink::merge(*grid1dCyl, *grid1dCyl2,
0602 AxisDirection::AxisPhi, *logger),
0603 AssertionFailureException);
0604
0605
0606 BOOST_CHECK_THROW(GridPortalLink::merge(*grid1dCyl, *grid1dCyl2,
0607 AxisDirection::AxisRPhi, *logger),
0608 SurfaceMergingException);
0609
0610 BOOST_TEST_CONTEXT("Consistent equidistant") {
0611 auto mergedPtr = GridPortalLink::merge(*grid1dCyl, *grid1dCyl2,
0612 AxisDirection::AxisZ, *logger);
0613 BOOST_REQUIRE_NE(mergedPtr, nullptr);
0614 const auto* merged = dynamic_cast<const GridPortalLink*>(mergedPtr.get());
0615 BOOST_REQUIRE_NE(merged, nullptr);
0616 BOOST_CHECK_EQUAL(merged->grid().axes().size(), 1);
0617 const auto& axis = *merged->grid().axes().front();
0618 BOOST_CHECK_EQUAL(axis.getMin(), -150_mm);
0619 BOOST_CHECK_EQUAL(axis.getMax(), 150_mm);
0620 BOOST_CHECK_EQUAL(axis.getNBins(), 15);
0621 BOOST_CHECK_EQUAL(axis.getType(), AxisType::Equidistant);
0622 BOOST_CHECK_EQUAL(axis.getBoundaryType(), AxisBoundaryType::Bound);
0623 }
0624
0625 BOOST_TEST_CONTEXT("Inconsistent equidistant") {
0626 auto grid1dCyl2BinWidthChanged = GridPortalLink::make(
0627 cyl2, AxisDirection::AxisZ, Axis{AxisBound, -50_mm, 50_mm, 6});
0628
0629 auto mergedPtr = GridPortalLink::merge(
0630 *grid1dCyl, *grid1dCyl2BinWidthChanged, AxisDirection::AxisZ, *logger);
0631 BOOST_REQUIRE_NE(mergedPtr, nullptr);
0632 const auto* merged = dynamic_cast<const GridPortalLink*>(mergedPtr.get());
0633 BOOST_REQUIRE_NE(merged, nullptr);
0634 BOOST_CHECK_EQUAL(merged->grid().axes().size(), 1);
0635 const auto& axis = *merged->grid().axes().front();
0636 BOOST_CHECK_EQUAL(axis.getMin(), -150_mm);
0637 BOOST_CHECK_EQUAL(axis.getMax(), 150_mm);
0638 BOOST_CHECK_EQUAL(axis.getNBins(), 16);
0639 BOOST_CHECK_EQUAL(axis.getType(), AxisType::Variable);
0640 BOOST_CHECK_EQUAL(axis.getBoundaryType(), AxisBoundaryType::Bound);
0641 }
0642
0643 BOOST_TEST_CONTEXT("Right Variable") {
0644 auto gridLeft = GridPortalLink::make(cyl, AxisDirection::AxisZ,
0645 Axis{AxisBound, -100_mm, 100_mm, 10});
0646
0647 auto gridRight =
0648 GridPortalLink::make(cyl2, AxisDirection::AxisZ,
0649 Axis{AxisBound, {-50_mm, -10_mm, 10_mm, 50_mm}});
0650
0651 auto mergedPtr = GridPortalLink::merge(*gridLeft, *gridRight,
0652 AxisDirection::AxisZ, *logger);
0653 BOOST_REQUIRE_NE(mergedPtr, nullptr);
0654 const auto* merged = dynamic_cast<const GridPortalLink*>(mergedPtr.get());
0655 BOOST_REQUIRE_NE(merged, nullptr);
0656 BOOST_CHECK_EQUAL(merged->grid().axes().size(), 1);
0657 const auto& axis = *merged->grid().axes().front();
0658 BOOST_CHECK_EQUAL(axis.getMin(), -150_mm);
0659 BOOST_CHECK_EQUAL(axis.getMax(), 150_mm);
0660 BOOST_CHECK_EQUAL(axis.getNBins(), 13);
0661 BOOST_CHECK_EQUAL(axis.getType(), AxisType::Variable);
0662 BOOST_CHECK_EQUAL(axis.getBoundaryType(), AxisBoundaryType::Bound);
0663 }
0664
0665 BOOST_TEST_CONTEXT("Left Variable") {
0666 auto gridLeft =
0667 GridPortalLink::make(cyl, AxisDirection::AxisZ,
0668 Axis{AxisBound, {-100_mm, -80_mm, 10_mm, 100_mm}});
0669
0670 auto gridRight = GridPortalLink::make(cyl2, AxisDirection::AxisZ,
0671 Axis{AxisBound, -50_mm, 50_mm, 8});
0672
0673 auto mergedPtr = GridPortalLink::merge(*gridLeft, *gridRight,
0674 AxisDirection::AxisZ, *logger);
0675 BOOST_REQUIRE_NE(mergedPtr, nullptr);
0676 const auto* merged = dynamic_cast<const GridPortalLink*>(mergedPtr.get());
0677 BOOST_REQUIRE_NE(merged, nullptr);
0678 BOOST_CHECK_EQUAL(merged->grid().axes().size(), 1);
0679 const auto& axis = *merged->grid().axes().front();
0680 BOOST_CHECK_EQUAL(axis.getMin(), -150_mm);
0681 BOOST_CHECK_EQUAL(axis.getMax(), 150_mm);
0682 BOOST_CHECK_EQUAL(axis.getNBins(), 11);
0683 BOOST_CHECK_EQUAL(axis.getType(), AxisType::Variable);
0684 BOOST_CHECK_EQUAL(axis.getBoundaryType(), AxisBoundaryType::Bound);
0685 }
0686
0687 BOOST_TEST_CONTEXT("Both Variable") {
0688 auto gridLeft =
0689 GridPortalLink::make(cyl, AxisDirection::AxisZ,
0690 Axis{AxisBound, {-100_mm, -80_mm, 10_mm, 100_mm}});
0691
0692 auto gridRight =
0693 GridPortalLink::make(cyl2, AxisDirection::AxisZ,
0694 Axis{AxisBound, {-50_mm, -10_mm, 10_mm, 50_mm}});
0695
0696 auto mergedPtr = GridPortalLink::merge(*gridLeft, *gridRight,
0697 AxisDirection::AxisZ, *logger);
0698 BOOST_REQUIRE_NE(mergedPtr, nullptr);
0699 const auto* merged = dynamic_cast<const GridPortalLink*>(mergedPtr.get());
0700 BOOST_REQUIRE_NE(merged, nullptr);
0701 BOOST_CHECK_EQUAL(merged->grid().axes().size(), 1);
0702 const auto& axis = *merged->grid().axes().front();
0703 BOOST_CHECK_EQUAL(axis.getMin(), -150_mm);
0704 BOOST_CHECK_EQUAL(axis.getMax(), 150_mm);
0705 BOOST_CHECK_EQUAL(axis.getNBins(), 6);
0706 BOOST_CHECK_EQUAL(axis.getType(), AxisType::Variable);
0707 BOOST_CHECK_EQUAL(axis.getBoundaryType(), AxisBoundaryType::Bound);
0708 }
0709
0710 BOOST_TEST_CONTEXT("Non bound axis") {
0711 std::unique_ptr gridLeft =
0712 GridPortalLink::make(cyl, AxisDirection::AxisZ,
0713 Axis{AxisBound, {-100_mm, -80_mm, 10_mm, 100_mm}});
0714 std::unique_ptr gridRightClosed =
0715 GridPortalLink::make(cyl2, AxisDirection::AxisZ,
0716 Axis{AxisClosed, {-50_mm, -10_mm, 10_mm, 50_mm}});
0717 std::unique_ptr gridRightOpen =
0718 GridPortalLink::make(cyl2, AxisDirection::AxisZ,
0719 Axis{AxisOpen, {-50_mm, -10_mm, 10_mm, 50_mm}});
0720
0721 auto compositeLR = PortalLinkBase::merge(
0722 copy(gridLeft), copy(gridRightClosed), AxisDirection::AxisZ, *logger);
0723 BOOST_CHECK_NE(dynamic_cast<const CompositePortalLink*>(compositeLR.get()),
0724 nullptr);
0725 auto compositeRL = PortalLinkBase::merge(
0726 copy(gridLeft), copy(gridRightOpen), AxisDirection::AxisZ, *logger);
0727 BOOST_CHECK_NE(dynamic_cast<const CompositePortalLink*>(compositeRL.get()),
0728 nullptr);
0729 }
0730 }
0731
0732 BOOST_AUTO_TEST_CASE(ParallelMerge) {
0733
0734 auto cyl1 = Surface::makeShared<CylinderSurface>(Transform3::Identity(),
0735 30_mm, 100_mm, 35_degree);
0736
0737 auto grid1 = GridPortalLink::make(
0738 cyl1, AxisDirection::AxisRPhi,
0739
0740 Axis{AxisBound, -35_degree * 30_mm, 35_degree * 30_mm, 3});
0741 auto cyl2 = Surface::makeShared<CylinderSurface>(
0742 Transform3{Translation3{Vector3::UnitZ() * 150_mm}}, 30_mm, 50_mm,
0743 35_degree);
0744
0745 auto grid2 = GridPortalLink::make(
0746 cyl2, AxisDirection::AxisRPhi,
0747 Axis{AxisBound, -35_degree * 30_mm, 35_degree * 30_mm, 3});
0748
0749 auto merged12Ptr =
0750 GridPortalLink::merge(*grid1, *grid2, AxisDirection::AxisZ, *logger);
0751 BOOST_REQUIRE_NE(merged12Ptr, nullptr);
0752 auto merged12 = dynamic_cast<const GridPortalLink*>(merged12Ptr.get());
0753 BOOST_REQUIRE_NE(merged12, nullptr);
0754
0755 BOOST_REQUIRE_EQUAL(merged12->grid().axes().size(), 2);
0756
0757 const auto& axis1 = *merged12->grid().axes().front();
0758 const auto& axis2 = *merged12->grid().axes().back();
0759 Axis axis1Expected{AxisBound, -35_degree * 30_mm, 35_degree * 30_mm, 3};
0760 BOOST_CHECK_EQUAL(axis1, axis1Expected);
0761 Axis axis2Expected{AxisBound, {-150_mm, 50_mm, 150_mm}};
0762 BOOST_CHECK_EQUAL(axis2, axis2Expected);
0763 }
0764
0765 BOOST_AUTO_TEST_SUITE_END()
0766
0767 BOOST_AUTO_TEST_SUITE(RPhiDirection)
0768
0769 BOOST_AUTO_TEST_CASE(ColinearMerge) {
0770 auto cyl = Surface::makeShared<CylinderSurface>(Transform3::Identity(), 30_mm,
0771 100_mm);
0772 BOOST_CHECK_THROW(GridPortalLink::make(cyl, AxisDirection::AxisRPhi,
0773 Axis{AxisBound, 0, 5, 5}),
0774 std::invalid_argument);
0775
0776 auto cylNonZeroAverage = Surface::makeShared<CylinderSurface>(
0777 Transform3::Identity(), 30_mm, 100_mm, 20_degree, 45_degree);
0778
0779 BOOST_CHECK_THROW(
0780 GridPortalLink::make(
0781 cylNonZeroAverage, AxisDirection::AxisRPhi,
0782 Axis{AxisBound, -20_degree * 30_mm, 20_degree * 30_mm, 5}),
0783 std::invalid_argument);
0784
0785 BOOST_TEST_CONTEXT("Colinear merge in rPhi") {
0786 auto cylPhi1 = Surface::makeShared<CylinderSurface>(
0787 Transform3::Identity() * AngleAxis3(45_degree, Vector3::UnitZ()), 30_mm,
0788 100_mm, 20_degree, 0_degree);
0789
0790 auto cylPhi2 = Surface::makeShared<CylinderSurface>(
0791 Transform3::Identity() * AngleAxis3(105_degree, Vector3::UnitZ()),
0792 30_mm, 100_mm, 40_degree, 0_degree);
0793
0794 auto portalPhi1 = GridPortalLink::make(
0795 cylPhi1, AxisDirection::AxisRPhi,
0796 Axis{AxisBound, -20_degree * 30_mm, 20_degree * 30_mm, 5});
0797
0798 auto portalPhi2 = GridPortalLink::make(
0799 cylPhi2, AxisDirection::AxisRPhi,
0800 Axis{AxisBound, -40_degree * 30_mm, 40_degree * 30_mm, 10});
0801
0802 auto cylPhi3 = Surface::makeShared<CylinderSurface>(
0803 Transform3::Identity() * AngleAxis3(45_degree, Vector3::UnitZ()), 30_mm,
0804 100_mm, 90_degree, 0_degree);
0805
0806 auto cylPhi4 = Surface::makeShared<CylinderSurface>(
0807 Transform3::Identity() * AngleAxis3(-135_degree, Vector3::UnitZ()),
0808 30_mm, 100_mm, 90_degree, 0_degree);
0809
0810 auto portalPhi3 = GridPortalLink::make(
0811 cylPhi3, AxisDirection::AxisRPhi,
0812 Axis{AxisBound, -90_degree * 30_mm, 90_degree * 30_mm, 2});
0813
0814 auto portalPhi4 = GridPortalLink::make(
0815 cylPhi4, AxisDirection::AxisRPhi,
0816 Axis{AxisBound, -90_degree * 30_mm, 90_degree * 30_mm, 2});
0817
0818 BOOST_TEST_CONTEXT("Consistent equidistant") {
0819 auto portalMerged = GridPortalLink::merge(
0820 *portalPhi1, *portalPhi2, AxisDirection::AxisRPhi, *logger);
0821 BOOST_REQUIRE_NE(portalMerged, nullptr);
0822
0823 const auto* merged =
0824 dynamic_cast<const GridPortalLink*>(portalMerged.get());
0825 BOOST_REQUIRE_NE(merged, nullptr);
0826 BOOST_CHECK_EQUAL(merged->grid().axes().size(), 1);
0827 const auto& axis = *merged->grid().axes().front();
0828 BOOST_CHECK_CLOSE(axis.getMin(), -60_degree * 30_mm, 1e-9);
0829 BOOST_CHECK_CLOSE(axis.getMax(), 60_degree * 30_mm, 1e-9);
0830 BOOST_CHECK_EQUAL(axis.getNBins(), 15);
0831 BOOST_CHECK_EQUAL(axis.getType(), AxisType::Equidistant);
0832 BOOST_CHECK_EQUAL(axis.getBoundaryType(), AxisBoundaryType::Bound);
0833
0834
0835 auto portalMerged34 = GridPortalLink::merge(
0836 *portalPhi3, *portalPhi4, AxisDirection::AxisRPhi, *logger);
0837 BOOST_REQUIRE_NE(portalMerged34, nullptr);
0838
0839 const auto* merged34 =
0840 dynamic_cast<const GridPortalLink*>(portalMerged34.get());
0841 BOOST_REQUIRE_NE(merged34, nullptr);
0842 BOOST_CHECK_EQUAL(merged34->grid().axes().size(), 1);
0843 const auto& axis34 = *merged34->grid().axes().front();
0844 BOOST_CHECK_CLOSE(axis34.getMin(), -180_degree * 30_mm, 1e-9);
0845 BOOST_CHECK_CLOSE(axis34.getMax(), 180_degree * 30_mm, 1e-9);
0846 BOOST_CHECK_EQUAL(axis34.getNBins(), 4);
0847 BOOST_CHECK_EQUAL(axis34.getType(), AxisType::Equidistant);
0848 BOOST_CHECK_EQUAL(axis34.getBoundaryType(), AxisBoundaryType::Closed);
0849 }
0850
0851 BOOST_TEST_CONTEXT("Inconsistent equidistant") {
0852 auto portalPhi2Mod = GridPortalLink::make(
0853 cylPhi2, AxisDirection::AxisRPhi,
0854 Axis{AxisBound, -40_degree * 30_mm, 40_degree * 30_mm, 3});
0855
0856 auto portalMergedMod = GridPortalLink::merge(
0857 *portalPhi1, *portalPhi2Mod, AxisDirection::AxisRPhi, *logger);
0858 BOOST_REQUIRE_NE(portalMergedMod, nullptr);
0859
0860 const auto* merged12 =
0861 dynamic_cast<const GridPortalLink*>(portalMergedMod.get());
0862 BOOST_REQUIRE_NE(merged12, nullptr);
0863 BOOST_CHECK_EQUAL(merged12->grid().axes().size(), 1);
0864 const auto& axis12 = *merged12->grid().axes().front();
0865 BOOST_CHECK_CLOSE(axis12.getMin(), -60_degree * 30_mm, 1e-9);
0866 BOOST_CHECK_CLOSE(axis12.getMax(), 60_degree * 30_mm, 1e-9);
0867 BOOST_CHECK_EQUAL(axis12.getNBins(), 8);
0868 BOOST_CHECK_EQUAL(axis12.getType(), AxisType::Variable);
0869 BOOST_CHECK_EQUAL(axis12.getBoundaryType(), AxisBoundaryType::Bound);
0870
0871 std::vector<double> expected12 = {-31.4159, -17.4533, -3.49066,
0872 10.472, 14.6608, 18.8496,
0873 23.0383, 27.2271, 31.4159};
0874 CHECK_CLOSE_OR_SMALL(axis12.getBinEdges(), expected12, 1e-4, 10e-10);
0875
0876 auto portalPhi4Mod = GridPortalLink::make(
0877 cylPhi4, AxisDirection::AxisRPhi,
0878 Axis{AxisBound, -90_degree * 30_mm, 90_degree * 30_mm, 1});
0879
0880 auto portalMerged34 = GridPortalLink::merge(
0881 *portalPhi3, *portalPhi4Mod, AxisDirection::AxisRPhi, *logger);
0882 BOOST_REQUIRE_NE(portalMerged34, nullptr);
0883
0884 const auto* merged34 =
0885 dynamic_cast<const GridPortalLink*>(portalMerged34.get());
0886 BOOST_REQUIRE_NE(merged34, nullptr);
0887 BOOST_CHECK_EQUAL(merged34->grid().axes().size(), 1);
0888 const auto& axis34 = *merged34->grid().axes().front();
0889 BOOST_CHECK_CLOSE(axis34.getMin(), -180_degree * 30_mm, 1e-9);
0890 BOOST_CHECK_CLOSE(axis34.getMax(), 180_degree * 30_mm, 1e-9);
0891 BOOST_CHECK_EQUAL(axis34.getNBins(), 3);
0892 BOOST_CHECK_EQUAL(axis34.getType(), AxisType::Variable);
0893 BOOST_CHECK_EQUAL(axis34.getBoundaryType(), AxisBoundaryType::Closed);
0894
0895
0896
0897 std::vector<double> expected34 = {-94.2478, -47.1239, 0, 94.2478};
0898 CHECK_CLOSE_OR_SMALL(axis34.getBinEdges(), expected34, 1e-4, 10e-10);
0899 }
0900
0901 BOOST_TEST_CONTEXT("Left variable") {
0902 BOOST_TEST_CONTEXT("Non-closed") {
0903 auto gridLeft =
0904 GridPortalLink::make(cylPhi1, AxisDirection::AxisRPhi,
0905 Axis{AxisBound,
0906 {-20_degree * 30_mm, -10_degree * 30_mm,
0907 10_degree * 30_mm, 20_degree * 30_mm}});
0908 auto gridRight = GridPortalLink::make(
0909 cylPhi2, AxisDirection::AxisRPhi,
0910 Axis{AxisBound, -40_degree * 30_mm, 40_degree * 30_mm, 3});
0911
0912 auto mergedPtr = GridPortalLink::merge(
0913 *gridLeft, *gridRight, AxisDirection::AxisRPhi, *logger);
0914 BOOST_REQUIRE_NE(mergedPtr, nullptr);
0915
0916 const auto* merged =
0917 dynamic_cast<const GridPortalLink*>(mergedPtr.get());
0918 BOOST_REQUIRE_NE(merged, nullptr);
0919 const auto& axis = *merged->grid().axes().front();
0920 BOOST_CHECK_EQUAL(merged->grid().axes().size(), 1);
0921 BOOST_CHECK_CLOSE(axis.getMin(), -60_degree * 30_mm, 1e-9);
0922 BOOST_CHECK_CLOSE(axis.getMax(), 60_degree * 30_mm, 1e-9);
0923 BOOST_CHECK_EQUAL(axis.getNBins(), 6);
0924 BOOST_CHECK_EQUAL(axis.getType(), AxisType::Variable);
0925 BOOST_CHECK_EQUAL(axis.getBoundaryType(), AxisBoundaryType::Bound);
0926
0927 std::vector<double> expected = {-31.4159, -17.4533, -3.49066, 10.472,
0928 15.708, 26.1799, 31.4159};
0929 CHECK_CLOSE_OR_SMALL(axis.getBinEdges(), expected, 1e-4, 10e-10);
0930 }
0931
0932 BOOST_TEST_CONTEXT("Closed") {
0933 auto gridLeft = GridPortalLink::make(
0934 cylPhi4, AxisDirection::AxisRPhi,
0935 Axis{AxisBound,
0936 {-90_degree * 30_mm, 25_degree * 30_mm, 90_degree * 30_mm}});
0937
0938 auto gridRight = GridPortalLink::make(
0939 cylPhi3, AxisDirection::AxisRPhi,
0940 Axis{AxisBound, -90_degree * 30_mm, 90_degree * 30_mm, 3});
0941
0942 auto mergedPtr = GridPortalLink::merge(
0943 *gridLeft, *gridRight, AxisDirection::AxisRPhi, *logger);
0944 BOOST_REQUIRE_NE(mergedPtr, nullptr);
0945
0946 const auto* merged =
0947 dynamic_cast<const GridPortalLink*>(mergedPtr.get());
0948 BOOST_REQUIRE_NE(merged, nullptr);
0949 BOOST_CHECK_EQUAL(merged->grid().axes().size(), 1);
0950 const auto& axis = *merged->grid().axes().front();
0951 BOOST_CHECK_CLOSE(axis.getMin(), -180_degree * 30_mm, 1e-9);
0952 BOOST_CHECK_CLOSE(axis.getMax(), 180_degree * 30_mm, 1e-9);
0953 BOOST_CHECK_EQUAL(axis.getNBins(), 5);
0954 BOOST_CHECK_EQUAL(axis.getType(), AxisType::Variable);
0955 BOOST_CHECK_EQUAL(axis.getBoundaryType(), AxisBoundaryType::Closed);
0956
0957
0958
0959 std::vector<double> expected = {-94.2478, -34.0339, 0,
0960 31.4159, 62.8319, 94.2478};
0961 CHECK_CLOSE_OR_SMALL(axis.getBinEdges(), expected, 1e-4, 10e-10);
0962 }
0963 }
0964
0965 BOOST_TEST_CONTEXT("Right variable") {
0966 BOOST_TEST_CONTEXT("Non-closed") {
0967 auto gridLeft = GridPortalLink::make(
0968 cylPhi1, AxisDirection::AxisRPhi,
0969 Axis{AxisBound, -20_degree * 30_mm, 20_degree * 30_mm, 3});
0970 auto gridRight =
0971 GridPortalLink::make(cylPhi2, AxisDirection::AxisRPhi,
0972 Axis{AxisBound,
0973 {-40_degree * 30_mm, -10_degree * 30_mm,
0974 10_degree * 30_mm, 40_degree * 30_mm}});
0975
0976 auto mergedPtr = GridPortalLink::merge(
0977 *gridLeft, *gridRight, AxisDirection::AxisRPhi, *logger);
0978 BOOST_REQUIRE_NE(mergedPtr, nullptr);
0979
0980 const auto* merged =
0981 dynamic_cast<const GridPortalLink*>(mergedPtr.get());
0982 BOOST_REQUIRE_NE(merged, nullptr);
0983 const auto& axis = *merged->grid().axes().front();
0984 BOOST_CHECK_EQUAL(merged->grid().axes().size(), 1);
0985 BOOST_CHECK_CLOSE(axis.getMin(), -60_degree * 30_mm, 1e-9);
0986 BOOST_CHECK_CLOSE(axis.getMax(), 60_degree * 30_mm, 1e-9);
0987 BOOST_CHECK_EQUAL(axis.getNBins(), 6);
0988 BOOST_CHECK_EQUAL(axis.getType(), AxisType::Variable);
0989 BOOST_CHECK_EQUAL(axis.getBoundaryType(), AxisBoundaryType::Bound);
0990
0991 std::vector<double> expected = {-31.4159, -15.708, -5.23599, 10.472,
0992 17.4533, 24.4346, 31.4159};
0993 CHECK_CLOSE_OR_SMALL(axis.getBinEdges(), expected, 1e-4, 10e-10);
0994 }
0995
0996 BOOST_TEST_CONTEXT("Closed") {
0997 auto gridLeft = GridPortalLink::make(
0998 cylPhi4, AxisDirection::AxisRPhi,
0999 Axis{AxisBound, -90_degree * 30_mm, 90_degree * 30_mm, 3});
1000
1001 auto gridRight = GridPortalLink::make(
1002 cylPhi3, AxisDirection::AxisRPhi,
1003 Axis{AxisBound,
1004 {-90_degree * 30_mm, 25_degree * 30_mm, 90_degree * 30_mm}});
1005
1006 auto mergedPtr = GridPortalLink::merge(
1007 *gridLeft, *gridRight, AxisDirection::AxisRPhi, *logger);
1008 BOOST_REQUIRE_NE(mergedPtr, nullptr);
1009
1010 const auto* merged =
1011 dynamic_cast<const GridPortalLink*>(mergedPtr.get());
1012 BOOST_REQUIRE_NE(merged, nullptr);
1013 BOOST_CHECK_EQUAL(merged->grid().axes().size(), 1);
1014 const auto& axis = *merged->grid().axes().front();
1015 BOOST_CHECK_CLOSE(axis.getMin(), -180_degree * 30_mm, 1e-9);
1016 BOOST_CHECK_CLOSE(axis.getMax(), 180_degree * 30_mm, 1e-9);
1017 BOOST_CHECK_EQUAL(axis.getNBins(), 5);
1018 BOOST_CHECK_EQUAL(axis.getType(), AxisType::Variable);
1019 BOOST_CHECK_EQUAL(axis.getBoundaryType(), AxisBoundaryType::Closed);
1020
1021
1022
1023 std::vector<double> expected = {-94.2478, -62.8319, -31.4159,
1024 0, 60.2139, 94.2478};
1025 CHECK_CLOSE_OR_SMALL(axis.getBinEdges(), expected, 1e-4, 10e-10);
1026 }
1027 }
1028
1029 BOOST_TEST_CONTEXT("Both variable") {
1030 BOOST_TEST_CONTEXT("Non-closed") {
1031 auto gridLeft =
1032 GridPortalLink::make(cylPhi1, AxisDirection::AxisRPhi,
1033 Axis{AxisBound,
1034 {-20_degree * 30_mm, -10_degree * 30_mm,
1035 10_degree * 30_mm, 20_degree * 30_mm}});
1036 auto gridRight = GridPortalLink::make(
1037 cylPhi2, AxisDirection::AxisRPhi,
1038 Axis{AxisBound,
1039 {-40_degree * 30_mm, -5_degree * 30_mm, 40_degree * 30_mm}});
1040
1041 auto mergedPtr = GridPortalLink::merge(
1042 *gridLeft, *gridRight, AxisDirection::AxisRPhi, *logger);
1043 BOOST_REQUIRE_NE(mergedPtr, nullptr);
1044
1045 const auto* merged =
1046 dynamic_cast<const GridPortalLink*>(mergedPtr.get());
1047 BOOST_REQUIRE_NE(merged, nullptr);
1048 const auto& axis = *merged->grid().axes().front();
1049 BOOST_CHECK_EQUAL(merged->grid().axes().size(), 1);
1050 BOOST_CHECK_CLOSE(axis.getMin(), -60_degree * 30_mm, 1e-9);
1051 BOOST_CHECK_CLOSE(axis.getMax(), 60_degree * 30_mm, 1e-9);
1052 BOOST_CHECK_EQUAL(axis.getNBins(), 5);
1053 BOOST_CHECK_EQUAL(axis.getType(), AxisType::Variable);
1054 BOOST_CHECK_EQUAL(axis.getBoundaryType(), AxisBoundaryType::Bound);
1055
1056 std::vector<double> expected = {-31.4159, -13.09, 10.472,
1057 15.708, 26.1799, 31.4159};
1058 CHECK_CLOSE_OR_SMALL(axis.getBinEdges(), expected, 1e-4, 10e-10);
1059 }
1060
1061 BOOST_TEST_CONTEXT("Closed") {
1062 auto gridLeft = GridPortalLink::make(
1063 cylPhi4, AxisDirection::AxisRPhi,
1064 Axis{AxisBound,
1065 {-90_degree * 30_mm, 25_degree * 30_mm, 90_degree * 30_mm}});
1066
1067 auto gridRight =
1068 GridPortalLink::make(cylPhi3, AxisDirection::AxisRPhi,
1069 Axis{AxisBound,
1070 {-90_degree * 30_mm, -10_degree * 30_mm,
1071 10_degree * 30_mm, 90_degree * 30_mm}});
1072
1073 auto mergedPtr = GridPortalLink::merge(
1074 *gridLeft, *gridRight, AxisDirection::AxisRPhi, *logger);
1075 BOOST_REQUIRE_NE(mergedPtr, nullptr);
1076
1077 const auto* merged =
1078 dynamic_cast<const GridPortalLink*>(mergedPtr.get());
1079 BOOST_REQUIRE_NE(merged, nullptr);
1080 BOOST_CHECK_EQUAL(merged->grid().axes().size(), 1);
1081 const auto& axis = *merged->grid().axes().front();
1082 BOOST_CHECK_CLOSE(axis.getMin(), -180_degree * 30_mm, 1e-9);
1083 BOOST_CHECK_CLOSE(axis.getMax(), 180_degree * 30_mm, 1e-9);
1084 BOOST_CHECK_EQUAL(axis.getNBins(), 5);
1085 BOOST_CHECK_EQUAL(axis.getType(), AxisType::Variable);
1086 BOOST_CHECK_EQUAL(axis.getBoundaryType(), AxisBoundaryType::Closed);
1087
1088
1089
1090 std::vector<double> expected = {-94.2478, -34.0339, 0,
1091 41.8879, 52.3599, 94.2478};
1092 CHECK_CLOSE_OR_SMALL(axis.getBinEdges(), expected, 1e-4, 10e-10);
1093 }
1094 }
1095 }
1096 }
1097
1098 BOOST_AUTO_TEST_CASE(ParallelMerge) {
1099
1100 auto cylPhi1 = Surface::makeShared<CylinderSurface>(
1101 Transform3::Identity() * AngleAxis3(45_degree, Vector3::UnitZ()), 30_mm,
1102 100_mm, 20_degree, 0_degree);
1103
1104 auto cylPhi2 = Surface::makeShared<CylinderSurface>(
1105 Transform3::Identity() * AngleAxis3(85_degree, Vector3::UnitZ()), 30_mm,
1106 100_mm, 20_degree, 0_degree);
1107
1108 auto portalPhi1 = GridPortalLink::make(cylPhi1, AxisDirection::AxisZ,
1109 Axis{AxisBound, -100_mm, 100_mm, 5});
1110
1111 auto portalPhi2 = GridPortalLink::make(cylPhi2, AxisDirection::AxisZ,
1112 Axis{AxisBound, -100_mm, 100_mm, 5});
1113
1114 auto merged12Ptr = GridPortalLink::merge(*portalPhi1, *portalPhi2,
1115 AxisDirection::AxisRPhi, *logger);
1116 BOOST_REQUIRE_NE(merged12Ptr, nullptr);
1117 auto merged12 = dynamic_cast<const GridPortalLink*>(merged12Ptr.get());
1118 BOOST_REQUIRE_NE(merged12, nullptr);
1119
1120 const auto& axis1 = *merged12->grid().axes().front();
1121 const auto& axis2 = *merged12->grid().axes().back();
1122
1123 Axis axis1Expected{AxisBound, -40_degree * 30_mm, 40_degree * 30_mm, 2};
1124 BOOST_CHECK_EQUAL(axis1, axis1Expected);
1125 Axis axis2Expected{AxisBound, -100_mm, 100_mm, 5};
1126 BOOST_CHECK_EQUAL(axis2, axis2Expected);
1127 }
1128
1129 BOOST_AUTO_TEST_SUITE_END()
1130
1131 BOOST_AUTO_TEST_SUITE_END()
1132
1133 BOOST_AUTO_TEST_SUITE(Merging2dCylinder)
1134
1135 BOOST_AUTO_TEST_CASE(ZDirection) {
1136 BOOST_TEST_CONTEXT("Phi sector") {
1137 auto cyl1 = Surface::makeShared<CylinderSurface>(Transform3::Identity(),
1138 30_mm, 100_mm, 45_degree);
1139
1140
1141 auto grid1 = GridPortalLink::make(
1142 cyl1, Axis{AxisBound, -45_degree * 30_mm, 45_degree * 30_mm, 5},
1143 Axis{AxisBound, -100_mm, 100_mm, 5});
1144
1145 auto trf2 = Transform3{Translation3{Vector3::UnitZ() * 150_mm}};
1146 auto cyl2 =
1147 Surface::makeShared<CylinderSurface>(trf2, 30_mm, 50_mm, 45_degree);
1148
1149
1150 auto grid2 = GridPortalLink::make(
1151 cyl2, Axis{AxisBound, -45_degree * 30_mm, 45_degree * 30_mm, 5},
1152 Axis{AxisBound, -50_mm, 50_mm, 5});
1153
1154
1155
1156 auto mergedPtr =
1157 GridPortalLink::merge(*grid1, *grid2, AxisDirection::AxisZ, *logger);
1158 const auto* merged = dynamic_cast<const GridPortalLink*>(mergedPtr.get());
1159 BOOST_REQUIRE_NE(mergedPtr, nullptr);
1160
1161 const auto& axis1 = *merged->grid().axes().front();
1162 const auto& axis2 = *merged->grid().axes().back();
1163
1164 BOOST_CHECK_EQUAL(axis1.getMin(), -45_degree * 30_mm);
1165 BOOST_CHECK_EQUAL(axis1.getMax(), 45_degree * 30_mm);
1166 BOOST_CHECK_EQUAL(axis1.getNBins(), 5);
1167 BOOST_CHECK_EQUAL(axis1.getType(), AxisType::Equidistant);
1168 BOOST_CHECK_EQUAL(axis1.getBoundaryType(), AxisBoundaryType::Bound);
1169
1170 BOOST_CHECK_EQUAL(axis2.getMin(), -150_mm);
1171 BOOST_CHECK_EQUAL(axis2.getMax(), 150_mm);
1172 BOOST_CHECK_EQUAL(axis2.getNBins(), 10);
1173 BOOST_CHECK_EQUAL(axis2.getType(), AxisType::Variable);
1174 BOOST_CHECK_EQUAL(axis2.getBoundaryType(), AxisBoundaryType::Bound);
1175
1176 auto grid3 = GridPortalLink::make(
1177 cyl2, Axis{AxisBound, -45_degree * 30_mm, 45_degree * 30_mm, 3},
1178 Axis{AxisBound, -50_mm, 50_mm, 5});
1179
1180 auto composite = PortalLinkBase::merge(copy(grid1), copy(grid3),
1181 AxisDirection::AxisZ, *logger);
1182 BOOST_CHECK_NE(dynamic_cast<const CompositePortalLink*>(composite.get()),
1183 nullptr);
1184 }
1185
1186 BOOST_TEST_CONTEXT("Check wraparound for full circle in phi") {
1187 auto cyl1 = Surface::makeShared<CylinderSurface>(Transform3::Identity(),
1188 30_mm, 100_mm, 180_degree);
1189
1190
1191 auto grid1 = GridPortalLink::make(
1192 cyl1, Axis{AxisClosed, -180_degree * 30_mm, 180_degree * 30_mm, 5},
1193 Axis{AxisBound, -100_mm, 100_mm, 5});
1194
1195 auto trf2 = Transform3{Translation3{Vector3::UnitZ() * 150_mm}};
1196 auto cyl2 =
1197 Surface::makeShared<CylinderSurface>(trf2, 30_mm, 50_mm, 180_degree);
1198
1199
1200 auto grid2 = GridPortalLink::make(
1201 cyl2, Axis{AxisClosed, -180_degree * 30_mm, 180_degree * 30_mm, 5},
1202 Axis{AxisBound, -50_mm, 50_mm, 5});
1203
1204 auto mergedPtr =
1205 GridPortalLink::merge(*grid1, *grid2, AxisDirection::AxisZ, *logger);
1206 const auto* merged = dynamic_cast<const GridPortalLink*>(mergedPtr.get());
1207 BOOST_REQUIRE_NE(mergedPtr, nullptr);
1208
1209 const auto& axis1 = *merged->grid().axes().front();
1210 const auto& axis2 = *merged->grid().axes().back();
1211
1212 Axis axis1Expected{AxisClosed, -180_degree * 30_mm, 180_degree * 30_mm, 5};
1213 BOOST_CHECK_EQUAL(axis1, axis1Expected);
1214 Axis axis2Expected{AxisBound,
1215 {-150, -110, -70, -30, 10, 50, 70, 90, 110, 130, 150}};
1216 BOOST_CHECK_EQUAL(axis2, axis2Expected);
1217 }
1218 }
1219
1220 BOOST_AUTO_TEST_CASE(RPhiDirection) {
1221 auto cyl1 = Surface::makeShared<CylinderSurface>(Transform3::Identity(),
1222 30_mm, 100_mm, 45_degree);
1223
1224
1225 auto grid1 = GridPortalLink::make(
1226 cyl1, Axis{AxisBound, -45_degree * 30_mm, 45_degree * 30_mm, 5},
1227 Axis{AxisBound, -100_mm, 100_mm, 5});
1228 BOOST_REQUIRE_NE(grid1, nullptr);
1229
1230 auto trf2 = Transform3{AngleAxis3{90_degree, Vector3::UnitZ()}};
1231 auto cyl2 =
1232 Surface::makeShared<CylinderSurface>(trf2, 30_mm, 100_mm, 45_degree);
1233
1234
1235 auto grid2 = GridPortalLink::make(
1236 cyl2, Axis{AxisBound, -45_degree * 30_mm, 45_degree * 30_mm, 5},
1237 Axis{AxisBound, -100_mm, 100_mm, 5});
1238 BOOST_REQUIRE_NE(grid2, nullptr);
1239
1240
1241
1242 auto mergedPtr =
1243 GridPortalLink::merge(*grid1, *grid2, AxisDirection::AxisRPhi, *logger);
1244 const auto* merged = dynamic_cast<const GridPortalLink*>(mergedPtr.get());
1245 BOOST_REQUIRE_NE(mergedPtr, nullptr);
1246
1247 const auto& axis1 = *merged->grid().axes().front();
1248 const auto& axis2 = *merged->grid().axes().back();
1249 BOOST_CHECK_CLOSE(axis1.getMin(), -90_degree * 30_mm, 1e-8);
1250 BOOST_CHECK_CLOSE(axis1.getMax(), 90_degree * 30_mm, 1e-8);
1251 BOOST_CHECK_EQUAL(axis1.getNBins(), 10);
1252 BOOST_CHECK_EQUAL(axis1.getType(), AxisType::Equidistant);
1253 BOOST_CHECK_EQUAL(axis1.getBoundaryType(), AxisBoundaryType::Bound);
1254 Axis axis2Expected{AxisBound, -100_mm, 100_mm, 5};
1255 BOOST_CHECK_EQUAL(axis2, axis2Expected);
1256 }
1257
1258 BOOST_AUTO_TEST_SUITE_END()
1259
1260 BOOST_AUTO_TEST_SUITE(Merging1dDisc)
1261
1262 BOOST_AUTO_TEST_SUITE(RDirection)
1263
1264 BOOST_AUTO_TEST_CASE(ColinearMerge) {
1265
1266 auto disc1 =
1267 Surface::makeShared<DiscSurface>(Transform3::Identity(), 30_mm, 100_mm);
1268
1269 auto grid1 = GridPortalLink::make(disc1, AxisDirection::AxisR,
1270 Axis{AxisBound, 30_mm, 100_mm, 7});
1271
1272 auto disc2 =
1273 Surface::makeShared<DiscSurface>(Transform3::Identity(), 100_mm, 150_mm);
1274
1275 auto grid2 = GridPortalLink::make(disc2, AxisDirection::AxisR,
1276 Axis{AxisBound, 100_mm, 150_mm, 5});
1277
1278 auto mergedPtr =
1279 GridPortalLink::merge(*grid1, *grid2, AxisDirection::AxisR, *logger);
1280 BOOST_REQUIRE(mergedPtr);
1281 const auto* merged = dynamic_cast<const GridPortalLink*>(mergedPtr.get());
1282 BOOST_REQUIRE_NE(merged, nullptr);
1283
1284 BOOST_CHECK_EQUAL(merged->grid().axes().size(), 1);
1285 Axis axisExpected{AxisBound, 30_mm, 150_mm, 12};
1286 BOOST_CHECK_EQUAL(*merged->grid().axes().front(), axisExpected);
1287
1288
1289 auto discPhi1 = Surface::makeShared<DiscSurface>(Transform3::Identity(),
1290 30_mm, 100_mm, 30_degree);
1291
1292 auto discPhiGrid1 = GridPortalLink::make(discPhi1, AxisDirection::AxisR,
1293 Axis{AxisBound, 30_mm, 100_mm, 7});
1294
1295 auto discPhi2 = Surface::makeShared<DiscSurface>(Transform3::Identity(),
1296 100_mm, 150_mm, 30_degree);
1297
1298 auto discPhiGrid2 = GridPortalLink::make(discPhi2, AxisDirection::AxisR,
1299 Axis{AxisBound, 100_mm, 150_mm, 5});
1300
1301 auto mergedPhiPtr = GridPortalLink::merge(*discPhiGrid1, *discPhiGrid2,
1302 AxisDirection::AxisR, *logger);
1303 BOOST_REQUIRE(mergedPhiPtr);
1304 const auto* mergedPhi =
1305 dynamic_cast<const GridPortalLink*>(mergedPhiPtr.get());
1306 BOOST_REQUIRE_NE(mergedPhi, nullptr);
1307
1308 BOOST_CHECK_EQUAL(mergedPhi->grid().axes().size(), 1);
1309 BOOST_CHECK_EQUAL(*mergedPhi->grid().axes().front(), axisExpected);
1310 }
1311
1312 BOOST_AUTO_TEST_CASE(ParallelMerge) {
1313 auto disc1 =
1314 Surface::makeShared<DiscSurface>(Transform3::Identity(), 30_mm, 100_mm);
1315
1316 auto grid1 =
1317 GridPortalLink::make(disc1, AxisDirection::AxisPhi,
1318 Axis{AxisClosed, -180_degree, 180_degree, 5});
1319
1320 auto disc2 =
1321 Surface::makeShared<DiscSurface>(Transform3::Identity(), 100_mm, 150_mm);
1322
1323 auto grid2 =
1324 GridPortalLink::make(disc2, AxisDirection::AxisPhi,
1325 Axis{AxisClosed, -180_degree, 180_degree, 5});
1326
1327 auto mergedPtr =
1328 GridPortalLink::merge(*grid1, *grid2, AxisDirection::AxisR, *logger);
1329 BOOST_REQUIRE(mergedPtr);
1330 const auto* merged = dynamic_cast<const GridPortalLink*>(mergedPtr.get());
1331 BOOST_REQUIRE_NE(merged, nullptr);
1332
1333 BOOST_CHECK_EQUAL(merged->grid().axes().size(), 2);
1334 const auto& axis1 = *merged->grid().axes().front();
1335 const auto& axis2 = *merged->grid().axes().back();
1336 Axis axis1Expected{AxisBound, {30_mm, 100_mm, 150_mm}};
1337 BOOST_CHECK_EQUAL(axis1, axis1Expected);
1338 Axis axis2Expected{AxisClosed, -180_degree, 180_degree, 5};
1339 BOOST_CHECK_EQUAL(axis2, axis2Expected);
1340 }
1341
1342 BOOST_AUTO_TEST_SUITE_END()
1343
1344 BOOST_AUTO_TEST_SUITE(PhiDirection)
1345
1346 BOOST_AUTO_TEST_CASE(ColinearMerge) {
1347 auto disc1 = Surface::makeShared<DiscSurface>(Transform3::Identity(), 30_mm,
1348 100_mm, 30_degree);
1349
1350 auto grid1 = GridPortalLink::make(disc1, AxisDirection::AxisPhi,
1351 Axis{AxisBound, -30_degree, 30_degree, 3});
1352
1353 auto disc2 = Surface::makeShared<DiscSurface>(
1354 Transform3{AngleAxis3{90_degree, Vector3::UnitZ()}}, 30_mm, 100_mm,
1355 60_degree);
1356
1357 auto grid2 = GridPortalLink::make(disc2, AxisDirection::AxisPhi,
1358 Axis{AxisBound, -60_degree, 60_degree, 6});
1359
1360 auto mergedPtr =
1361 GridPortalLink::merge(*grid1, *grid2, AxisDirection::AxisPhi, *logger);
1362 BOOST_REQUIRE(mergedPtr);
1363 const auto* merged = dynamic_cast<const GridPortalLink*>(mergedPtr.get());
1364 BOOST_REQUIRE_NE(merged, nullptr);
1365
1366 BOOST_CHECK_EQUAL(merged->grid().axes().size(), 1);
1367 const auto& axis = *merged->grid().axes().front();
1368 BOOST_CHECK_CLOSE(axis.getMin(), -90_degree, 1e-6);
1369 BOOST_CHECK_CLOSE(axis.getMax(), 90_degree, 1e-6);
1370 BOOST_CHECK_EQUAL(axis.getNBins(), 9);
1371
1372
1373
1374 auto disc1Half = Surface::makeShared<DiscSurface>(
1375 Transform3{AngleAxis3{15_degree, Vector3::UnitZ()}}, 30_mm, 100_mm,
1376 90_degree);
1377
1378 auto grid1Half =
1379 GridPortalLink::make(disc1Half, AxisDirection::AxisPhi,
1380 Axis{AxisBound, -90_degree, 90_degree, 3});
1381
1382 auto disc2Half = Surface::makeShared<DiscSurface>(
1383 Transform3{AngleAxis3{-165_degree, Vector3::UnitZ()}}, 30_mm, 100_mm,
1384 90_degree);
1385
1386 auto grid2Half =
1387 GridPortalLink::make(disc2Half, AxisDirection::AxisPhi,
1388 Axis{AxisBound, -90_degree, 90_degree, 3});
1389
1390 auto mergedHalfPtr = GridPortalLink::merge(*grid1Half, *grid2Half,
1391 AxisDirection::AxisPhi, *logger);
1392 BOOST_REQUIRE(mergedHalfPtr);
1393 const auto* mergedHalf =
1394 dynamic_cast<const GridPortalLink*>(mergedHalfPtr.get());
1395 BOOST_REQUIRE_NE(mergedHalf, nullptr);
1396
1397 BOOST_CHECK_EQUAL(mergedHalf->grid().axes().size(), 1);
1398 Axis axisHalfExpected{AxisClosed, -180_degree, 180_degree, 6};
1399 BOOST_CHECK_EQUAL(axisHalfExpected, *mergedHalf->grid().axes().front());
1400 }
1401
1402 BOOST_AUTO_TEST_CASE(ParallelMerge) {
1403 auto disc1 = Surface::makeShared<DiscSurface>(Transform3::Identity(), 30_mm,
1404 100_mm, 30_degree);
1405
1406 auto grid1 = GridPortalLink::make(disc1, AxisDirection::AxisR,
1407 Axis{AxisBound, 30_mm, 100_mm, 3});
1408
1409 auto disc2 = Surface::makeShared<DiscSurface>(
1410 Transform3{AngleAxis3{90_degree, Vector3::UnitZ()}}, 30_mm, 100_mm,
1411 60_degree);
1412
1413 auto grid2 = GridPortalLink::make(disc2, AxisDirection::AxisR,
1414 Axis{AxisBound, 30_mm, 100_mm, 3});
1415
1416 auto mergedPtr =
1417 GridPortalLink::merge(*grid1, *grid2, AxisDirection::AxisPhi, *logger);
1418 BOOST_REQUIRE(mergedPtr);
1419 const auto* merged = dynamic_cast<const GridPortalLink*>(mergedPtr.get());
1420 BOOST_REQUIRE_NE(merged, nullptr);
1421
1422 BOOST_CHECK_EQUAL(merged->grid().axes().size(), 2);
1423 const auto& axis1 = *merged->grid().axes().front();
1424 const auto& axis2 = *merged->grid().axes().back();
1425
1426 Axis axis1Expected{AxisBound, 30_mm, 100_mm, 3};
1427 BOOST_CHECK_EQUAL(axis1, axis1Expected);
1428
1429 BOOST_CHECK_CLOSE(axis2.getMin(), -90_degree, 1e-6);
1430 BOOST_CHECK_CLOSE(axis2.getMax(), 90_degree, 1e-6);
1431 BOOST_CHECK_EQUAL(axis2.getNBins(), 2);
1432 BOOST_CHECK_CLOSE(axis2.getBinEdges().at(1), 30_degree, 1e-6);
1433 BOOST_CHECK_EQUAL(axis2.getType(), AxisType::Variable);
1434 BOOST_CHECK_EQUAL(axis2.getBoundaryType(), AxisBoundaryType::Bound);
1435 }
1436
1437 BOOST_AUTO_TEST_SUITE_END()
1438
1439 BOOST_AUTO_TEST_CASE(BinFilling) {
1440
1441
1442 auto vol1 = makeDummyVolume();
1443 auto vol2 = makeDummyVolume();
1444
1445 BOOST_TEST_CONTEXT("RDirection") {
1446 auto disc1 = Surface::makeShared<DiscSurface>(Transform3::Identity(), 30_mm,
1447 60_mm, 30_degree);
1448
1449 auto grid1 = GridPortalLink::make(disc1, AxisDirection::AxisR,
1450 Axis{AxisBound, 30_mm, 60_mm, 2});
1451
1452 grid1->setVolume(vol1.get());
1453
1454 auto disc2 = Surface::makeShared<DiscSurface>(Transform3::Identity(), 60_mm,
1455 90_mm, 30_degree);
1456
1457 auto grid2 = GridPortalLink::make(disc2, AxisDirection::AxisR,
1458 Axis{AxisBound, 60_mm, 90_mm, 2});
1459
1460 grid2->setVolume(vol2.get());
1461
1462 auto mergedPtr =
1463 GridPortalLink::merge(*grid1, *grid2, AxisDirection::AxisR, *logger);
1464
1465 using merged_type =
1466 GridPortalLinkT<Axis<AxisType::Equidistant, AxisBoundaryType::Bound>>;
1467
1468 const auto* merged = dynamic_cast<const merged_type*>(mergedPtr.get());
1469 BOOST_REQUIRE(merged);
1470
1471 grid1->printContents(std::cout);
1472 grid2->printContents(std::cout);
1473 merged->printContents(std::cout);
1474
1475 BOOST_CHECK_EQUAL(merged->grid().atLocalBins({1}), vol1.get());
1476 BOOST_CHECK_EQUAL(merged->grid().atLocalBins({2}), vol1.get());
1477 BOOST_CHECK_EQUAL(merged->grid().atLocalBins({3}), vol2.get());
1478 BOOST_CHECK_EQUAL(merged->grid().atLocalBins({4}), vol2.get());
1479 }
1480
1481 BOOST_TEST_CONTEXT("PhiDirection") {
1482 auto disc1 = Surface::makeShared<DiscSurface>(Transform3::Identity(), 30_mm,
1483 100_mm, 30_degree);
1484
1485 auto grid1 =
1486 GridPortalLink::make(disc1, AxisDirection::AxisPhi,
1487 Axis{AxisBound, -30_degree, 30_degree, 2});
1488
1489 grid1->setVolume(vol1.get());
1490
1491 auto disc2 = Surface::makeShared<DiscSurface>(
1492 Transform3{AngleAxis3{60_degree, Vector3::UnitZ()}}, 30_mm, 100_mm,
1493 30_degree);
1494
1495 auto grid2 =
1496 GridPortalLink::make(disc2, AxisDirection::AxisPhi,
1497 Axis{AxisBound, -30_degree, 30_degree, 2});
1498
1499 grid2->setVolume(vol2.get());
1500
1501 auto mergedPtr =
1502 GridPortalLink::merge(*grid1, *grid2, AxisDirection::AxisPhi, *logger);
1503 BOOST_REQUIRE(mergedPtr);
1504
1505 using merged_type =
1506 GridPortalLinkT<Axis<AxisType::Equidistant, AxisBoundaryType::Bound>>;
1507
1508 const auto* merged = dynamic_cast<const merged_type*>(mergedPtr.get());
1509 BOOST_REQUIRE(merged);
1510
1511 grid1->printContents(std::cout);
1512 grid2->printContents(std::cout);
1513 merged->printContents(std::cout);
1514
1515 BOOST_CHECK_EQUAL(merged->grid().atLocalBins({1}), vol2.get());
1516 BOOST_CHECK_EQUAL(merged->grid().atLocalBins({2}), vol2.get());
1517 BOOST_CHECK_EQUAL(merged->grid().atLocalBins({3}), vol1.get());
1518 BOOST_CHECK_EQUAL(merged->grid().atLocalBins({4}), vol1.get());
1519 }
1520 }
1521
1522 BOOST_AUTO_TEST_SUITE_END()
1523
1524 BOOST_AUTO_TEST_SUITE(Merging2dDisc)
1525
1526 BOOST_AUTO_TEST_CASE(RDirection) {
1527
1528 auto discPhi1 = Surface::makeShared<DiscSurface>(Transform3::Identity(),
1529 30_mm, 100_mm, 30_degree);
1530
1531 auto discPhiGrid1 =
1532 GridPortalLink::make(discPhi1, Axis{AxisBound, 30_mm, 100_mm, 7},
1533 Axis{AxisBound, -30_degree, 30_degree, 3});
1534
1535 auto discPhi2 = Surface::makeShared<DiscSurface>(Transform3::Identity(),
1536 100_mm, 150_mm, 30_degree);
1537
1538 auto discPhiGrid2 =
1539 GridPortalLink::make(discPhi2, Axis{AxisBound, 100_mm, 150_mm, 5},
1540 Axis{AxisBound, -30_degree, 30_degree, 3});
1541
1542 auto mergedPtr = GridPortalLink::merge(*discPhiGrid1, *discPhiGrid2,
1543 AxisDirection::AxisR, *logger);
1544 BOOST_REQUIRE(mergedPtr);
1545 const auto* merged = dynamic_cast<const GridPortalLink*>(mergedPtr.get());
1546 BOOST_REQUIRE_NE(merged, nullptr);
1547 BOOST_CHECK_EQUAL(merged->grid().axes().size(), 2);
1548 const auto& axis1 = *merged->grid().axes().front();
1549 const auto& axis2 = *merged->grid().axes().back();
1550
1551 BOOST_CHECK_EQUAL(axis1.getMin(), 30_mm);
1552 BOOST_CHECK_EQUAL(axis1.getMax(), 150_mm);
1553 BOOST_CHECK_EQUAL(axis1.getNBins(), 12);
1554 BOOST_CHECK_EQUAL(axis1.getType(), AxisType::Equidistant);
1555 BOOST_CHECK_EQUAL(axis1.getBoundaryType(), AxisBoundaryType::Bound);
1556 BOOST_CHECK_EQUAL(axis2.getMin(), -30_degree);
1557 BOOST_CHECK_EQUAL(axis2.getMax(), 30_degree);
1558 BOOST_CHECK_EQUAL(axis2.getNBins(), 3);
1559 BOOST_CHECK_EQUAL(axis2.getType(), AxisType::Equidistant);
1560 }
1561
1562 BOOST_AUTO_TEST_CASE(PhiDirection) {
1563
1564 auto disc1 = Surface::makeShared<DiscSurface>(Transform3::Identity(), 30_mm,
1565 100_mm, 30_degree);
1566
1567 auto grid1 = GridPortalLink::make(disc1, Axis{AxisBound, 30_mm, 100_mm, 3},
1568 Axis{AxisBound, -30_degree, 30_degree, 3});
1569
1570 auto disc2 = Surface::makeShared<DiscSurface>(
1571 Transform3{AngleAxis3{90_degree, Vector3::UnitZ()}}, 30_mm, 100_mm,
1572 60_degree);
1573
1574 auto grid2 = GridPortalLink::make(disc2, Axis{AxisBound, 30_mm, 100_mm, 3},
1575 Axis{AxisBound, -60_degree, 60_degree, 6});
1576
1577 auto mergedPtr =
1578 GridPortalLink::merge(*grid1, *grid2, AxisDirection::AxisPhi, *logger);
1579 BOOST_REQUIRE(mergedPtr);
1580 const auto* merged = dynamic_cast<const GridPortalLink*>(mergedPtr.get());
1581 BOOST_REQUIRE_NE(merged, nullptr);
1582
1583 BOOST_CHECK_EQUAL(merged->grid().axes().size(), 2);
1584 const auto& axis1 = *merged->grid().axes().front();
1585 const auto& axis2 = *merged->grid().axes().back();
1586
1587 BOOST_CHECK_EQUAL(axis1.getMin(), 30_mm);
1588 BOOST_CHECK_EQUAL(axis1.getMax(), 100_mm);
1589 BOOST_CHECK_EQUAL(axis1.getNBins(), 3);
1590 BOOST_CHECK_EQUAL(axis1.getType(), AxisType::Equidistant);
1591 BOOST_CHECK_EQUAL(axis1.getBoundaryType(), AxisBoundaryType::Bound);
1592 BOOST_CHECK_CLOSE(axis2.getMin(), -90_degree, 1e-6);
1593 BOOST_CHECK_CLOSE(axis2.getMax(), 90_degree, 1e-6);
1594 BOOST_CHECK_EQUAL(axis2.getNBins(), 9);
1595 BOOST_CHECK_EQUAL(axis2.getType(), AxisType::Equidistant);
1596 BOOST_CHECK_EQUAL(axis2.getBoundaryType(), AxisBoundaryType::Bound);
1597 }
1598
1599 BOOST_AUTO_TEST_CASE(BinFilling) {
1600
1601
1602 auto vol1 = std::make_shared<TrackingVolume>(
1603 Transform3::Identity(),
1604 std::make_shared<CylinderVolumeBounds>(30_mm, 40_mm, 100_mm));
1605
1606 auto vol2 = std::make_shared<TrackingVolume>(
1607 Transform3::Identity(),
1608 std::make_shared<CylinderVolumeBounds>(30_mm, 40_mm, 100_mm));
1609
1610 auto fillCheckerBoard = [&](auto& grid) {
1611 auto loc = grid.numLocalBins();
1612 for (std::size_t i = 1; i <= loc[0]; ++i) {
1613 for (std::size_t j = 1; j <= loc[1]; ++j) {
1614 grid.atLocalBins({i, j}) = (i + j) % 2 == 0 ? vol1.get() : vol2.get();
1615 }
1616 }
1617 };
1618
1619 auto checkCheckerBoard = [&](const auto& grid) {
1620 auto loc = grid.numLocalBins();
1621 for (std::size_t i = 1; i <= loc[0]; ++i) {
1622 for (std::size_t j = 1; j <= loc[1]; ++j) {
1623 const auto* vol = grid.atLocalBins({i, j});
1624 if (vol != ((i + j) % 2 == 0 ? vol1.get() : vol2.get())) {
1625 BOOST_ERROR("Is not a checkerboard pattern");
1626 return;
1627 }
1628 }
1629 }
1630 };
1631
1632 BOOST_TEST_CONTEXT("RDirection") {
1633 auto discPhi1 = Surface::makeShared<DiscSurface>(Transform3::Identity(),
1634 30_mm, 60_mm, 30_degree);
1635
1636 auto discPhiGrid1 =
1637 GridPortalLink::make(discPhi1, Axis{AxisBound, 30_mm, 60_mm, 2},
1638 Axis{AxisBound, -30_degree, 30_degree, 2});
1639
1640 fillCheckerBoard(discPhiGrid1->grid());
1641 checkCheckerBoard(discPhiGrid1->grid());
1642
1643 auto discPhi2 = Surface::makeShared<DiscSurface>(Transform3::Identity(),
1644 60_mm, 90_mm, 30_degree);
1645
1646 auto discPhiGrid2 =
1647 GridPortalLink::make(discPhi2, Axis{AxisBound, 60_mm, 90_mm, 2},
1648 Axis{AxisBound, -30_degree, 30_degree, 2});
1649
1650 fillCheckerBoard(discPhiGrid2->grid());
1651 checkCheckerBoard(discPhiGrid2->grid());
1652
1653 auto mergedPtr = GridPortalLink::merge(*discPhiGrid1, *discPhiGrid2,
1654 AxisDirection::AxisR, *logger);
1655
1656 using merged_type =
1657 GridPortalLinkT<Axis<AxisType::Equidistant, AxisBoundaryType::Bound>,
1658 Axis<AxisType::Equidistant, AxisBoundaryType::Bound>>;
1659
1660 const auto* merged = dynamic_cast<const merged_type*>(mergedPtr.get());
1661 BOOST_REQUIRE(merged);
1662 checkCheckerBoard(merged->grid());
1663
1664
1665 discPhiGrid1->setVolume(vol1.get());
1666 discPhiGrid2->setVolume(vol2.get());
1667
1668 mergedPtr = GridPortalLink::merge(*discPhiGrid1, *discPhiGrid2,
1669 AxisDirection::AxisR, *logger);
1670
1671 merged = dynamic_cast<const merged_type*>(mergedPtr.get());
1672 BOOST_REQUIRE(merged);
1673
1674 const auto* v1 = vol1.get();
1675 const auto* v2 = vol2.get();
1676
1677 std::vector<std::pair<Vector2, const TrackingVolume*>> locations = {
1678 {{40_mm, -20_degree}, v1}, {{40_mm, 20_degree}, v1},
1679 {{50_mm, -20_degree}, v1}, {{50_mm, 20_degree}, v1},
1680 {{70_mm, -20_degree}, v2}, {{70_mm, 20_degree}, v2},
1681 {{80_mm, -20_degree}, v2}, {{80_mm, 20_degree}, v2},
1682 };
1683
1684 for (const auto& [loc, vol] : locations) {
1685 BOOST_TEST_CONTEXT(loc.transpose())
1686 BOOST_CHECK_EQUAL(merged->resolveVolume(gctx, loc).value(), vol);
1687 }
1688
1689 std::vector<std::vector<const TrackingVolume*>> contents = {
1690 {v1, v1},
1691 {v1, v1},
1692 {v2, v2},
1693 {v2, v2},
1694 };
1695
1696 for (std::size_t i = 0; i < 4; ++i) {
1697 for (std::size_t j = 0; j < 2; ++j) {
1698 BOOST_CHECK_EQUAL(merged->grid().atLocalBins({i + 1, j + 1}),
1699 contents.at(i).at(j));
1700 }
1701 }
1702 }
1703
1704 BOOST_TEST_CONTEXT("PhiDirection") {
1705 auto disc1 = Surface::makeShared<DiscSurface>(Transform3::Identity(), 30_mm,
1706 100_mm, 30_degree);
1707
1708 auto grid1 =
1709 GridPortalLink::make(disc1, Axis{AxisBound, 30_mm, 100_mm, 2},
1710 Axis{AxisBound, -30_degree, 30_degree, 2});
1711 fillCheckerBoard(grid1->grid());
1712 checkCheckerBoard(grid1->grid());
1713
1714 auto disc2 = Surface::makeShared<DiscSurface>(
1715 Transform3{AngleAxis3{60_degree, Vector3::UnitZ()}}, 30_mm, 100_mm,
1716 30_degree);
1717
1718 auto grid2 =
1719 GridPortalLink::make(disc2, Axis{AxisBound, 30_mm, 100_mm, 2},
1720 Axis{AxisBound, -30_degree, 30_degree, 2});
1721 fillCheckerBoard(grid2->grid());
1722 checkCheckerBoard(grid2->grid());
1723
1724 auto mergedPtr =
1725 GridPortalLink::merge(*grid1, *grid2, AxisDirection::AxisPhi, *logger);
1726
1727 BOOST_REQUIRE(mergedPtr);
1728
1729 using merged_type =
1730 GridPortalLinkT<Axis<AxisType::Equidistant, AxisBoundaryType::Bound>,
1731 Axis<AxisType::Equidistant, AxisBoundaryType::Bound>>;
1732
1733 const auto* merged = dynamic_cast<const merged_type*>(mergedPtr.get());
1734 BOOST_REQUIRE(merged);
1735
1736 checkCheckerBoard(merged->grid());
1737
1738
1739 grid1->setVolume(vol1.get());
1740 grid2->setVolume(vol2.get());
1741
1742 mergedPtr =
1743 GridPortalLink::merge(*grid1, *grid2, AxisDirection::AxisPhi, *logger);
1744 merged = dynamic_cast<const merged_type*>(mergedPtr.get());
1745 BOOST_REQUIRE(merged);
1746
1747 const auto* v1 = vol1.get();
1748 const auto* v2 = vol2.get();
1749
1750 std::vector<std::pair<Vector2, const TrackingVolume*>> locations = {
1751 {{40_mm, -50_degree}, v2}, {{40_mm, -10_degree}, v2},
1752 {{50_mm, -50_degree}, v2}, {{50_mm, -10_degree}, v2},
1753 {{40_mm, 10_degree}, v1}, {{50_mm, 50_degree}, v1},
1754 {{50_mm, 10_degree}, v1}, {{50_mm, 50_degree}, v1},
1755 };
1756
1757 for (const auto& [loc, vol] : locations) {
1758 BOOST_TEST_CONTEXT(loc.transpose())
1759 BOOST_CHECK_EQUAL(merged->resolveVolume(gctx, loc).value(), vol);
1760 }
1761
1762 std::vector<std::vector<const TrackingVolume*>> contents = {
1763 {v2, v2, v1, v1},
1764 {v2, v2, v1, v1},
1765 };
1766
1767 for (std::size_t i = 0; i < 2; ++i) {
1768 for (std::size_t j = 0; j < 4; ++j) {
1769 BOOST_CHECK_EQUAL(merged->grid().atLocalBins({i + 1, j + 1}),
1770 contents.at(i).at(j));
1771 }
1772 }
1773 }
1774 }
1775
1776 BOOST_AUTO_TEST_SUITE_END()
1777
1778 BOOST_AUTO_TEST_SUITE(MergingMixedDisc)
1779
1780 BOOST_AUTO_TEST_CASE(RDirection) {
1781 auto discPhi1 = Surface::makeShared<DiscSurface>(Transform3::Identity(),
1782 30_mm, 100_mm, 30_degree);
1783
1784 auto discPhiGrid1 =
1785 GridPortalLink::make(discPhi1, Axis{AxisBound, 30_mm, 100_mm, 7},
1786 Axis{AxisBound, -30_degree, 30_degree, 3});
1787
1788 auto discPhi2 = Surface::makeShared<DiscSurface>(Transform3::Identity(),
1789 100_mm, 150_mm, 30_degree);
1790
1791 auto discPhiGrid21dPhi =
1792 GridPortalLink::make(discPhi2, AxisDirection::AxisPhi,
1793 Axis{AxisBound, -30_degree, 30_degree, 3});
1794
1795 auto merged12PhiPtr = GridPortalLink::merge(*discPhiGrid1, *discPhiGrid21dPhi,
1796 AxisDirection::AxisR, *logger);
1797 BOOST_REQUIRE(merged12PhiPtr);
1798 const auto* merged12Phi =
1799 dynamic_cast<const GridPortalLink*>(merged12PhiPtr.get());
1800 BOOST_REQUIRE_NE(merged12Phi, nullptr);
1801
1802 auto merged21PhiPtr = GridPortalLink::merge(*discPhiGrid21dPhi, *discPhiGrid1,
1803 AxisDirection::AxisR, *logger);
1804 BOOST_REQUIRE(merged21PhiPtr);
1805 const auto* merged21Phi =
1806 dynamic_cast<const GridPortalLink*>(merged21PhiPtr.get());
1807 BOOST_REQUIRE_NE(merged21Phi, nullptr);
1808
1809 BOOST_CHECK_EQUAL(merged12Phi->grid(), merged21Phi->grid());
1810
1811 BOOST_CHECK_EQUAL(merged12Phi->grid().axes().size(), 2);
1812 const auto& axis1 = *merged12Phi->grid().axes().front();
1813 const auto& axis2 = *merged12Phi->grid().axes().back();
1814
1815 BOOST_CHECK_EQUAL(axis1.getMin(), 30_mm);
1816 BOOST_CHECK_EQUAL(axis1.getMax(), 150_mm);
1817 BOOST_CHECK_EQUAL(axis1.getNBins(), 8);
1818 BOOST_CHECK_EQUAL(axis1.getType(), AxisType::Variable);
1819 BOOST_CHECK_EQUAL(axis1.getBoundaryType(), AxisBoundaryType::Bound);
1820 BOOST_CHECK_EQUAL(axis2.getMin(), -30_degree);
1821 BOOST_CHECK_EQUAL(axis2.getMax(), 30_degree);
1822 BOOST_CHECK_EQUAL(axis2.getNBins(), 3);
1823 BOOST_CHECK_EQUAL(axis2.getType(), AxisType::Equidistant);
1824 BOOST_CHECK_EQUAL(axis2.getBoundaryType(), AxisBoundaryType::Bound);
1825
1826 auto discPhiGrid21dR = GridPortalLink::make(
1827 discPhi2, AxisDirection::AxisR, Axis{AxisBound, 100_mm, 150_mm, 5});
1828
1829 auto merged12RPtr = GridPortalLink::merge(*discPhiGrid1, *discPhiGrid21dR,
1830 AxisDirection::AxisR, *logger);
1831 BOOST_REQUIRE(merged12RPtr);
1832 const auto* merged12R =
1833 dynamic_cast<const GridPortalLink*>(merged12RPtr.get());
1834 BOOST_REQUIRE_NE(merged12R, nullptr);
1835
1836 auto merged21RPtr = GridPortalLink::merge(*discPhiGrid21dR, *discPhiGrid1,
1837 AxisDirection::AxisR, *logger);
1838 BOOST_REQUIRE(merged21RPtr);
1839 const auto* merged21R =
1840 dynamic_cast<const GridPortalLink*>(merged21RPtr.get());
1841 BOOST_REQUIRE_NE(merged21R, nullptr);
1842 BOOST_CHECK_EQUAL(merged12R->grid(), merged21R->grid());
1843
1844 BOOST_CHECK_EQUAL(merged12R->grid().axes().size(), 2);
1845 const auto& axis1R = *merged12R->grid().axes().front();
1846 const auto& axis2R = *merged12R->grid().axes().back();
1847
1848 BOOST_CHECK_EQUAL(axis1R.getMin(), 30_mm);
1849 BOOST_CHECK_EQUAL(axis1R.getMax(), 150_mm);
1850 BOOST_CHECK_EQUAL(axis1R.getNBins(), 12);
1851 BOOST_CHECK_EQUAL(axis1R.getType(), AxisType::Equidistant);
1852 BOOST_CHECK_EQUAL(axis1R.getBoundaryType(), AxisBoundaryType::Bound);
1853 BOOST_CHECK_EQUAL(axis2R.getMin(), -30_degree);
1854 BOOST_CHECK_EQUAL(axis2R.getMax(), 30_degree);
1855 BOOST_CHECK_EQUAL(axis2R.getNBins(), 3);
1856 BOOST_CHECK_EQUAL(axis2R.getType(), AxisType::Equidistant);
1857 BOOST_CHECK_EQUAL(axis2R.getBoundaryType(), AxisBoundaryType::Bound);
1858 }
1859
1860 BOOST_AUTO_TEST_CASE(PhiDirection) {
1861 auto disc1 = Surface::makeShared<DiscSurface>(Transform3::Identity(), 30_mm,
1862 100_mm, 30_degree);
1863
1864 auto grid1 = GridPortalLink::make(disc1, Axis{AxisBound, 30_mm, 100_mm, 3},
1865 Axis{AxisBound, -30_degree, 30_degree, 3});
1866
1867 auto disc2 = Surface::makeShared<DiscSurface>(
1868 Transform3{AngleAxis3{90_degree, Vector3::UnitZ()}}, 30_mm, 100_mm,
1869 60_degree);
1870
1871 auto grid21dPhi =
1872 GridPortalLink::make(disc2, AxisDirection::AxisPhi,
1873
1874 Axis{AxisBound, -60_degree, 60_degree, 6});
1875
1876 auto merged12PhiPtr = GridPortalLink::merge(*grid1, *grid21dPhi,
1877 AxisDirection::AxisPhi, *logger);
1878 BOOST_REQUIRE(merged12PhiPtr);
1879 const auto* merged12Phi =
1880 dynamic_cast<const GridPortalLink*>(merged12PhiPtr.get());
1881 BOOST_REQUIRE_NE(merged12Phi, nullptr);
1882
1883 BOOST_CHECK_EQUAL(merged12Phi->grid().axes().size(), 2);
1884 const auto& axis1 = *merged12Phi->grid().axes().front();
1885 const auto& axis2 = *merged12Phi->grid().axes().back();
1886
1887 BOOST_CHECK_EQUAL(axis1.getMin(), 30_mm);
1888 BOOST_CHECK_EQUAL(axis1.getMax(), 100_mm);
1889 BOOST_CHECK_EQUAL(axis1.getNBins(), 3);
1890 BOOST_CHECK_EQUAL(axis1.getType(), AxisType::Equidistant);
1891 BOOST_CHECK_EQUAL(axis1.getBoundaryType(), AxisBoundaryType::Bound);
1892 BOOST_CHECK_CLOSE(axis2.getMin(), -90_degree, 1e-6);
1893 BOOST_CHECK_CLOSE(axis2.getMax(), 90_degree, 1e-6);
1894 BOOST_CHECK_EQUAL(axis2.getNBins(), 9);
1895 BOOST_CHECK_EQUAL(axis2.getType(), AxisType::Equidistant);
1896 BOOST_CHECK_EQUAL(axis2.getBoundaryType(), AxisBoundaryType::Bound);
1897
1898 auto grid21dR = GridPortalLink::make(disc2, AxisDirection::AxisR,
1899 Axis{AxisBound, 30_mm, 100_mm, 3});
1900
1901 auto merged12RPtr =
1902 GridPortalLink::merge(*grid1, *grid21dR, AxisDirection::AxisPhi, *logger);
1903 BOOST_REQUIRE(merged12RPtr);
1904 const auto* merged12R =
1905 dynamic_cast<const GridPortalLink*>(merged12RPtr.get());
1906 BOOST_REQUIRE_NE(merged12R, nullptr);
1907
1908 BOOST_CHECK_EQUAL(merged12R->grid().axes().size(), 2);
1909 const auto& axis1R = *merged12R->grid().axes().front();
1910 const auto& axis2R = *merged12R->grid().axes().back();
1911
1912 BOOST_CHECK_EQUAL(axis1R.getMin(), 30_mm);
1913 BOOST_CHECK_EQUAL(axis1R.getMax(), 100_mm);
1914 BOOST_CHECK_EQUAL(axis1R.getNBins(), 3);
1915 BOOST_CHECK_EQUAL(axis1R.getType(), AxisType::Equidistant);
1916 BOOST_CHECK_EQUAL(axis1R.getBoundaryType(), AxisBoundaryType::Bound);
1917 BOOST_CHECK_CLOSE(axis2R.getMin(), -90_degree, 1e-6);
1918 BOOST_CHECK_CLOSE(axis2R.getMax(), 90_degree, 1e-6);
1919 BOOST_CHECK_EQUAL(axis2R.getNBins(), 4);
1920 BOOST_CHECK_EQUAL(axis2R.getType(), AxisType::Variable);
1921 BOOST_CHECK_EQUAL(axis2R.getBoundaryType(), AxisBoundaryType::Bound);
1922 }
1923
1924 BOOST_AUTO_TEST_SUITE_END()
1925
1926 BOOST_AUTO_TEST_SUITE(MergingCrossDisc)
1927
1928 BOOST_AUTO_TEST_CASE(RDirection) {
1929
1930 auto vol1 = makeDummyVolume();
1931 auto vol2 = makeDummyVolume();
1932 auto vol3 = makeDummyVolume();
1933 auto vol4 = makeDummyVolume();
1934
1935 auto disc1 = Surface::makeShared<DiscSurface>(Transform3::Identity(), 30_mm,
1936 100_mm, 30_degree);
1937
1938 auto grid1 = GridPortalLink::make(disc1, AxisDirection::AxisR,
1939 Axis{AxisBound, 30_mm, 100_mm, 2});
1940 grid1->grid().atLocalBins({1}) = vol1.get();
1941 grid1->grid().atLocalBins({2}) = vol2.get();
1942
1943 auto disc2 = Surface::makeShared<DiscSurface>(Transform3::Identity(), 100_mm,
1944 150_mm, 30_degree);
1945
1946 auto grid2 = GridPortalLink::make(disc2, AxisDirection::AxisPhi,
1947 Axis{AxisBound, -30_degree, 30_degree, 2});
1948
1949 grid2->grid().atLocalBins({1}) = vol3.get();
1950 grid2->grid().atLocalBins({2}) = vol4.get();
1951
1952 auto mergedPtr =
1953 GridPortalLink::merge(*grid1, *grid2, AxisDirection::AxisR, *logger);
1954
1955 const auto* merged = dynamic_cast<const GridPortalLink*>(mergedPtr.get());
1956 BOOST_REQUIRE_NE(merged, nullptr);
1957
1958 BOOST_CHECK_EQUAL(merged->grid().axes().size(), 2);
1959 const auto& axis1 = *merged->grid().axes().front();
1960 const auto& axis2 = *merged->grid().axes().back();
1961 Axis axisExpectedR{AxisBound, {30_mm, 65_mm, 100_mm, 150_mm}};
1962 Axis axisExpectedPhi{AxisBound, -30_degree, 30_degree, 2};
1963 BOOST_CHECK_EQUAL(axis1, axisExpectedR);
1964 BOOST_CHECK_EQUAL(axis2, axisExpectedPhi);
1965
1966 std::vector<std::pair<Vector2, TrackingVolume*>> locations = {
1967 {{40_mm, -15_degree}, vol1.get()}, {{40_mm, 15_degree}, vol1.get()},
1968 {{90_mm, -15_degree}, vol2.get()}, {{90_mm, 15_degree}, vol2.get()},
1969
1970 {{110_mm, -15_degree}, vol3.get()}, {{110_mm, 15_degree}, vol4.get()},
1971 {{140_mm, -15_degree}, vol3.get()}, {{140_mm, 15_degree}, vol4.get()},
1972 };
1973
1974 for (const auto& [loc, vol] : locations) {
1975 BOOST_TEST_CONTEXT(loc.transpose())
1976 BOOST_CHECK_EQUAL(merged->resolveVolume(gctx, loc).value(), vol);
1977 }
1978
1979 grid1->printContents(std::cout);
1980 grid2->printContents(std::cout);
1981 merged->printContents(std::cout);
1982 }
1983
1984 BOOST_AUTO_TEST_CASE(PhiDirection) {
1985
1986
1987 auto vol1 = makeDummyVolume();
1988 auto vol2 = makeDummyVolume();
1989 auto vol3 = makeDummyVolume();
1990 auto vol4 = makeDummyVolume();
1991
1992 auto disc1 = Surface::makeShared<DiscSurface>(Transform3::Identity(), 30_mm,
1993 100_mm, 30_degree);
1994
1995 auto grid1 = GridPortalLink::make(disc1, AxisDirection::AxisR,
1996 Axis{AxisBound, 30_mm, 100_mm, 2});
1997
1998 grid1->grid().atLocalBins({1}) = vol1.get();
1999 grid1->grid().atLocalBins({2}) = vol2.get();
2000
2001 auto disc2 = Surface::makeShared<DiscSurface>(
2002 Transform3{AngleAxis3{90_degree, Vector3::UnitZ()}}, 30_mm, 100_mm,
2003 60_degree);
2004
2005 auto grid2 = GridPortalLink::make(disc2, AxisDirection::AxisPhi,
2006 Axis{AxisBound, -60_degree, 60_degree, 2});
2007
2008 grid2->grid().atLocalBins({1}) = vol3.get();
2009 grid2->grid().atLocalBins({2}) = vol4.get();
2010
2011 auto mergedPtr =
2012 GridPortalLink::merge(*grid1, *grid2, AxisDirection::AxisPhi, *logger);
2013
2014 using merged_type =
2015 GridPortalLinkT<Axis<AxisType::Equidistant, AxisBoundaryType::Bound>,
2016 Axis<AxisType::Equidistant, AxisBoundaryType::Bound>>;
2017
2018 const auto* merged = dynamic_cast<const merged_type*>(mergedPtr.get());
2019 BOOST_REQUIRE_NE(merged, nullptr);
2020
2021 BOOST_CHECK_EQUAL(merged->grid().axes().size(), 2);
2022 const auto& axis1 = *merged->grid().axes().front();
2023 const auto& axis2 = *merged->grid().axes().back();
2024 Axis axisExpectedR{AxisBound, 30_mm, 100_mm, 2};
2025 BOOST_CHECK_EQUAL(axis1, axisExpectedR);
2026
2027 BOOST_CHECK_CLOSE(axis2.getMin(), -90_degree, 1e-6);
2028 BOOST_CHECK_CLOSE(axis2.getMax(), 90_degree, 1e-6);
2029 BOOST_CHECK_EQUAL(axis2.getNBins(), 3);
2030 BOOST_CHECK_EQUAL(axis2.getType(), AxisType::Equidistant);
2031 BOOST_CHECK_EQUAL(axis2.getBoundaryType(), AxisBoundaryType::Bound);
2032
2033 std::vector<std::pair<Vector2, TrackingVolume*>> locations = {
2034 {{40_mm, 45_degree}, vol1.get()}, {{40_mm, 0_degree}, vol4.get()},
2035 {{40_mm, -80_degree}, vol3.get()}, {{90_mm, 45_degree}, vol2.get()},
2036 {{90_mm, 0_degree}, vol4.get()}, {{90_mm, -80_degree}, vol3.get()},
2037 };
2038
2039 grid1->printContents(std::cout);
2040 grid2->printContents(std::cout);
2041 merged->printContents(std::cout);
2042
2043 for (const auto& [loc, vol] : locations) {
2044 BOOST_TEST_CONTEXT((Vector2{loc[0], loc[1] / 1_degree}.transpose()))
2045 BOOST_CHECK_EQUAL(merged->resolveVolume(gctx, loc).value(), vol);
2046 }
2047 }
2048
2049 BOOST_AUTO_TEST_SUITE_END()
2050
2051 BOOST_AUTO_TEST_SUITE_END()
2052
2053 BOOST_AUTO_TEST_CASE(CompositeConstruction) {
2054
2055 auto vol1 = std::make_shared<TrackingVolume>(
2056 Transform3::Identity(),
2057 std::make_shared<CylinderVolumeBounds>(30_mm, 40_mm, 100_mm));
2058
2059 auto vol2 = std::make_shared<TrackingVolume>(
2060 Transform3::Identity(),
2061 std::make_shared<CylinderVolumeBounds>(30_mm, 40_mm, 100_mm));
2062
2063 auto vol3 = std::make_shared<TrackingVolume>(
2064 Transform3::Identity(),
2065 std::make_shared<CylinderVolumeBounds>(30_mm, 40_mm, 100_mm));
2066
2067 auto disc1 =
2068 Surface::makeShared<DiscSurface>(Transform3::Identity(), 30_mm, 60_mm);
2069
2070 auto trivial1 = std::make_unique<TrivialPortalLink>(disc1, *vol1);
2071
2072 auto disc2 =
2073 Surface::makeShared<DiscSurface>(Transform3::Identity(), 60_mm, 90_mm);
2074 auto trivial2 = std::make_unique<TrivialPortalLink>(disc2, *vol2);
2075
2076 auto composite = std::make_unique<CompositePortalLink>(
2077 copy(trivial1), copy(trivial2), AxisDirection::AxisR);
2078
2079 auto compositeCopy = std::make_unique<CompositePortalLink>(
2080 copy(trivial1), copy(trivial2), AxisDirection::AxisR);
2081
2082 BOOST_CHECK_EQUAL(
2083 composite->resolveVolume(gctx, Vector2{40_mm, 0_degree}).value(),
2084 vol1.get());
2085 BOOST_CHECK_EQUAL(
2086 composite->resolveVolume(gctx, Vector2{70_mm, 0_degree}).value(),
2087 vol2.get());
2088
2089 BOOST_CHECK_EQUAL(composite->depth(), 1);
2090
2091
2092 auto cyl = Surface::makeShared<CylinderSurface>(Transform3::Identity(), 30_mm,
2093 40_mm);
2094 auto trivialCyl = std::make_unique<TrivialPortalLink>(cyl, *vol3);
2095 BOOST_CHECK_THROW(CompositePortalLink(copy(trivial1), copy(trivialCyl),
2096 AxisDirection::AxisR),
2097 std::invalid_argument);
2098
2099 auto disc3 =
2100 Surface::makeShared<DiscSurface>(Transform3::Identity(), 90_mm, 120_mm);
2101 auto trivial3 = std::make_unique<TrivialPortalLink>(disc3, *vol3);
2102
2103
2104 BOOST_CHECK_THROW(
2105 CompositePortalLink(copy(trivial1), copy(trivial3), AxisDirection::AxisR),
2106 SurfaceMergingException);
2107
2108
2109
2110 CompositePortalLink composite2(std::move(composite), copy(trivial3),
2111 AxisDirection::AxisR, false);
2112
2113 BOOST_CHECK_EQUAL(
2114 composite2.resolveVolume(gctx, Vector2{40_mm, 0_degree}).value(),
2115 vol1.get());
2116 BOOST_CHECK_EQUAL(
2117 composite2.resolveVolume(gctx, Vector2{70_mm, 0_degree}).value(),
2118 vol2.get());
2119 BOOST_CHECK_EQUAL(
2120 composite2.resolveVolume(gctx, Vector2{100_mm, 0_degree}).value(),
2121 vol3.get());
2122
2123
2124 BOOST_CHECK_EQUAL(composite2.depth(), 2);
2125
2126 CompositePortalLink composite2Flat(std::move(compositeCopy), copy(trivial3),
2127 AxisDirection::AxisR, true);
2128
2129
2130 BOOST_CHECK_EQUAL(composite2Flat.depth(), 1);
2131
2132 BOOST_CHECK_EQUAL(
2133 composite2Flat.resolveVolume(gctx, Vector2{40_mm, 0_degree}).value(),
2134 vol1.get());
2135 BOOST_CHECK_EQUAL(
2136 composite2Flat.resolveVolume(gctx, Vector2{70_mm, 0_degree}).value(),
2137 vol2.get());
2138 BOOST_CHECK_EQUAL(
2139 composite2Flat.resolveVolume(gctx, Vector2{100_mm, 0_degree}).value(),
2140 vol3.get());
2141 }
2142
2143 BOOST_AUTO_TEST_SUITE(PortalMerging)
2144
2145 BOOST_DATA_TEST_CASE(TrivialTrivial,
2146 (boost::unit_test::data::make(0, -135, 180, 45) *
2147 boost::unit_test::data::make(Vector3{0_mm, 0_mm, 0_mm},
2148 Vector3{20_mm, 0_mm, 0_mm},
2149 Vector3{0_mm, 20_mm, 0_mm},
2150 Vector3{20_mm, 20_mm, 0_mm},
2151 Vector3{0_mm, 0_mm, 20_mm})),
2152 angle, offset) {
2153 Transform3 base =
2154 AngleAxis3(angle * 1_degree, Vector3::UnitX()) * Translation3(offset);
2155
2156 BOOST_TEST_CONTEXT("RDirection") {
2157 auto vol1 = std::make_shared<TrackingVolume>(
2158 base, std::make_shared<CylinderVolumeBounds>(30_mm, 40_mm, 100_mm));
2159
2160 auto vol2 = std::make_shared<TrackingVolume>(
2161 base, std::make_shared<CylinderVolumeBounds>(30_mm, 40_mm, 100_mm));
2162
2163 auto vol3 = std::make_shared<TrackingVolume>(
2164 base, std::make_shared<CylinderVolumeBounds>(40_mm, 50_mm, 100_mm));
2165
2166 auto disc1 =
2167 Surface::makeShared<DiscSurface>(Transform3::Identity(), 30_mm, 60_mm);
2168
2169 auto disc2 =
2170 Surface::makeShared<DiscSurface>(Transform3::Identity(), 60_mm, 90_mm);
2171
2172 auto disc3 =
2173 Surface::makeShared<DiscSurface>(Transform3::Identity(), 90_mm, 120_mm);
2174
2175 auto trivial1 = std::make_unique<TrivialPortalLink>(disc1, *vol1);
2176 BOOST_REQUIRE(trivial1);
2177 auto trivial2 = std::make_unique<TrivialPortalLink>(disc2, *vol2);
2178 BOOST_REQUIRE(trivial2);
2179 auto trivial3 = std::make_unique<TrivialPortalLink>(disc3, *vol3);
2180 BOOST_REQUIRE(trivial3);
2181
2182 auto grid1 = trivial1->makeGrid(AxisDirection::AxisR);
2183 auto compGridTrivial = PortalLinkBase::merge(
2184 std::move(grid1), std::make_unique<TrivialPortalLink>(*trivial2),
2185 AxisDirection::AxisR, *logger);
2186 BOOST_REQUIRE(compGridTrivial);
2187 BOOST_CHECK_EQUAL(dynamic_cast<CompositePortalLink&>(*compGridTrivial)
2188 .makeGrid(gctx, *logger),
2189 nullptr);
2190
2191 auto composite =
2192 PortalLinkBase::merge(std::move(trivial1), std::move(trivial2),
2193 AxisDirection::AxisR, *logger);
2194 BOOST_REQUIRE(composite);
2195
2196 auto grid12 =
2197 dynamic_cast<CompositePortalLink&>(*composite).makeGrid(gctx, *logger);
2198 BOOST_REQUIRE(grid12);
2199
2200 BOOST_CHECK_EQUAL(
2201 grid12->resolveVolume(gctx, Vector2{40_mm, 0_degree}).value(),
2202 vol1.get());
2203
2204 BOOST_CHECK_EQUAL(
2205 grid12->resolveVolume(gctx, Vector2{70_mm, 0_degree}).value(),
2206 vol2.get());
2207
2208 composite = PortalLinkBase::merge(std::move(composite), std::move(trivial3),
2209 AxisDirection::AxisR, *logger);
2210 BOOST_REQUIRE(composite);
2211
2212 auto grid123 =
2213 dynamic_cast<CompositePortalLink&>(*composite).makeGrid(gctx, *logger);
2214 BOOST_REQUIRE(grid123);
2215
2216 BOOST_CHECK_EQUAL(
2217 grid123->resolveVolume(gctx, Vector2{40_mm, 0_degree}).value(),
2218 vol1.get());
2219
2220 BOOST_CHECK_EQUAL(
2221 grid123->resolveVolume(gctx, Vector2{70_mm, 0_degree}).value(),
2222 vol2.get());
2223
2224 BOOST_CHECK_EQUAL(
2225 grid123->resolveVolume(gctx, Vector2{100_mm, 0_degree}).value(),
2226 vol3.get());
2227 }
2228
2229 BOOST_TEST_CONTEXT("ZDirection") {
2230 auto vol1 = std::make_shared<TrackingVolume>(
2231 base, std::make_shared<CylinderVolumeBounds>(30_mm, 40_mm, 100_mm));
2232
2233 auto vol2 = std::make_shared<TrackingVolume>(
2234 base * Translation3{Vector3::UnitZ() * 200},
2235 std::make_shared<CylinderVolumeBounds>(30_mm, 40_mm, 100_mm));
2236
2237 auto vol3 = std::make_shared<TrackingVolume>(
2238 base * Translation3{Vector3::UnitZ() * 400},
2239 std::make_shared<CylinderVolumeBounds>(30_mm, 40_mm, 100_mm));
2240
2241 auto cyl1 = Surface::makeShared<CylinderSurface>(base, 40_mm, 100_mm);
2242
2243 auto cyl2 = Surface::makeShared<CylinderSurface>(
2244 base * Translation3{Vector3::UnitZ() * 200}, 40_mm, 100_mm);
2245
2246 auto cyl3 = Surface::makeShared<CylinderSurface>(
2247 base * Translation3{Vector3::UnitZ() * 400}, 40_mm, 100_mm);
2248
2249 auto trivial1 = std::make_unique<TrivialPortalLink>(cyl1, *vol1);
2250 BOOST_REQUIRE(trivial1);
2251 auto trivial2 = std::make_unique<TrivialPortalLink>(cyl2, *vol2);
2252 BOOST_REQUIRE(trivial2);
2253 auto trivial3 = std::make_unique<TrivialPortalLink>(cyl3, *vol3);
2254 BOOST_REQUIRE(trivial3);
2255
2256 auto grid1 = trivial1->makeGrid(AxisDirection::AxisZ);
2257 auto compGridTrivial = PortalLinkBase::merge(
2258 std::move(grid1), std::make_unique<TrivialPortalLink>(*trivial2),
2259 AxisDirection::AxisZ, *logger);
2260 BOOST_REQUIRE(compGridTrivial);
2261 BOOST_CHECK_EQUAL(dynamic_cast<CompositePortalLink&>(*compGridTrivial)
2262 .makeGrid(gctx, *logger),
2263 nullptr);
2264
2265 auto composite =
2266 PortalLinkBase::merge(std::move(trivial1), std::move(trivial2),
2267 AxisDirection::AxisZ, *logger);
2268 BOOST_REQUIRE(composite);
2269
2270 auto grid12 =
2271 dynamic_cast<CompositePortalLink&>(*composite).makeGrid(gctx, *logger);
2272 BOOST_REQUIRE(grid12);
2273
2274 BOOST_CHECK_EQUAL(
2275 grid12->resolveVolume(gctx, Vector2{40_mm, -40_mm}).value(),
2276 vol1.get());
2277
2278 BOOST_CHECK_EQUAL(
2279 grid12->resolveVolume(gctx, Vector2{40_mm, 40_mm}).value(), vol2.get());
2280
2281 composite = PortalLinkBase::merge(std::move(composite), std::move(trivial3),
2282 AxisDirection::AxisZ, *logger);
2283 BOOST_REQUIRE(composite);
2284
2285 auto grid123 =
2286 dynamic_cast<CompositePortalLink&>(*composite).makeGrid(gctx, *logger);
2287 BOOST_REQUIRE(grid123);
2288
2289 BOOST_CHECK_EQUAL(
2290 grid123->resolveVolume(gctx, Vector2{40_mm, -110_mm}).value(),
2291 vol1.get());
2292
2293 BOOST_CHECK_EQUAL(
2294 grid123->resolveVolume(gctx, Vector2{40_mm, -10_mm}).value(),
2295 vol2.get());
2296
2297 BOOST_CHECK_EQUAL(
2298 grid123->resolveVolume(gctx, Vector2{40_mm, 190_mm}).value(),
2299 vol3.get());
2300 }
2301 }
2302
2303 BOOST_AUTO_TEST_CASE(TrivialGridR) {
2304 auto vol1 = std::make_shared<TrackingVolume>(
2305 Transform3::Identity(),
2306 std::make_shared<CylinderVolumeBounds>(30_mm, 40_mm, 100_mm));
2307
2308 auto vol2 = std::make_shared<TrackingVolume>(
2309 Transform3::Identity(),
2310 std::make_shared<CylinderVolumeBounds>(30_mm, 40_mm, 100_mm));
2311
2312 auto disc1 =
2313 Surface::makeShared<DiscSurface>(Transform3::Identity(), 30_mm, 60_mm);
2314
2315 auto disc2 =
2316 Surface::makeShared<DiscSurface>(Transform3::Identity(), 60_mm, 90_mm);
2317
2318 auto trivial = std::make_unique<TrivialPortalLink>(disc2, *vol2);
2319 BOOST_REQUIRE(trivial);
2320
2321 auto gridPhi = GridPortalLink::make(
2322 disc1, AxisDirection::AxisPhi,
2323 Axis{AxisClosed, -std::numbers::pi, std::numbers::pi, 2});
2324 gridPhi->setVolume(vol1.get());
2325
2326 auto gridR = GridPortalLink::make(disc1, AxisDirection::AxisR,
2327 Axis{AxisBound, 30_mm, 60_mm, 2});
2328 gridR->setVolume(vol1.get());
2329
2330 BOOST_TEST_CONTEXT("Colinear") {
2331 auto merged = PortalLinkBase::merge(copy(trivial), copy(gridR),
2332 AxisDirection::AxisR, *logger);
2333 BOOST_REQUIRE(merged);
2334 BOOST_CHECK_NE(dynamic_cast<CompositePortalLink*>(merged.get()), nullptr);
2335 }
2336
2337 BOOST_TEST_CONTEXT("Orthogonal") {
2338 auto merged = PortalLinkBase::merge(copy(gridPhi), copy(trivial),
2339 AxisDirection::AxisR, *logger);
2340 BOOST_REQUIRE(merged);
2341 BOOST_CHECK_NE(dynamic_cast<CompositePortalLink*>(merged.get()), nullptr);
2342 }
2343 }
2344
2345 BOOST_AUTO_TEST_CASE(TrivialGridPhi) {
2346 auto vol1 = std::make_shared<TrackingVolume>(
2347 Transform3::Identity(),
2348 std::make_shared<CylinderVolumeBounds>(30_mm, 40_mm, 100_mm));
2349
2350 auto vol2 = std::make_shared<TrackingVolume>(
2351 Transform3::Identity(),
2352 std::make_shared<CylinderVolumeBounds>(30_mm, 40_mm, 100_mm));
2353
2354 auto disc1 = Surface::makeShared<DiscSurface>(Transform3::Identity(), 30_mm,
2355 100_mm, 30_degree);
2356
2357 auto disc2 = Surface::makeShared<DiscSurface>(
2358 Transform3{AngleAxis3{90_degree, Vector3::UnitZ()}}, 30_mm, 100_mm,
2359 60_degree);
2360
2361 auto trivial = std::make_unique<TrivialPortalLink>(disc2, *vol2);
2362 BOOST_REQUIRE(trivial);
2363
2364 auto gridPhi = GridPortalLink::make(
2365 disc1, AxisDirection::AxisPhi, Axis{AxisBound, -30_degree, 30_degree, 2});
2366 gridPhi->setVolume(vol1.get());
2367
2368 auto gridR = GridPortalLink::make(disc1, AxisDirection::AxisR,
2369 Axis{AxisBound, 30_mm, 100_mm, 2});
2370 gridR->setVolume(vol1.get());
2371
2372 BOOST_TEST_CONTEXT("Colinear") {
2373 auto merged = PortalLinkBase::merge(copy(trivial), copy(gridPhi),
2374 AxisDirection::AxisPhi, *logger);
2375 BOOST_REQUIRE(merged);
2376 BOOST_CHECK_NE(dynamic_cast<CompositePortalLink*>(merged.get()), nullptr);
2377 }
2378
2379 BOOST_TEST_CONTEXT("Orthogonal") {
2380 auto merged = PortalLinkBase::merge(copy(gridR), copy(trivial),
2381 AxisDirection::AxisPhi, *logger);
2382 BOOST_REQUIRE(merged);
2383 BOOST_CHECK_NE(dynamic_cast<CompositePortalLink*>(merged.get()), nullptr);
2384 }
2385 }
2386
2387 BOOST_AUTO_TEST_CASE(CompositeOther) {
2388 auto vol1 = std::make_shared<TrackingVolume>(
2389 Transform3::Identity(),
2390 std::make_shared<CylinderVolumeBounds>(30_mm, 40_mm, 100_mm));
2391
2392 auto vol2 = std::make_shared<TrackingVolume>(
2393 Transform3::Identity(),
2394 std::make_shared<CylinderVolumeBounds>(30_mm, 40_mm, 100_mm));
2395
2396 auto vol3 = std::make_shared<TrackingVolume>(
2397 Transform3::Identity(),
2398 std::make_shared<CylinderVolumeBounds>(30_mm, 40_mm, 100_mm));
2399
2400 auto disc1 =
2401 Surface::makeShared<DiscSurface>(Transform3::Identity(), 30_mm, 60_mm);
2402 auto disc2 =
2403 Surface::makeShared<DiscSurface>(Transform3::Identity(), 60_mm, 90_mm);
2404 auto disc3 =
2405 Surface::makeShared<DiscSurface>(Transform3::Identity(), 90_mm, 120_mm);
2406
2407 auto grid1 = GridPortalLink::make(disc1, *vol1, AxisDirection::AxisR);
2408 auto trivial2 = std::make_unique<TrivialPortalLink>(disc2, *vol2);
2409
2410 auto composite12 = std::make_unique<CompositePortalLink>(
2411 std::move(grid1), std::move(trivial2), AxisDirection::AxisR);
2412
2413 BOOST_CHECK_EQUAL(composite12->depth(), 1);
2414 BOOST_CHECK_EQUAL(composite12->size(), 2);
2415
2416 auto trivial3 = std::make_unique<TrivialPortalLink>(disc3, *vol3);
2417
2418 auto composite123Ptr =
2419 PortalLinkBase::merge(std::move(composite12), std::move(trivial3),
2420 AxisDirection::AxisR, *logger);
2421
2422 const auto* composite123 =
2423 dynamic_cast<const CompositePortalLink*>(composite123Ptr.get());
2424 BOOST_REQUIRE(composite123);
2425
2426 BOOST_CHECK_EQUAL(composite123->depth(), 1);
2427
2428 BOOST_CHECK_EQUAL(
2429 composite123->resolveVolume(gctx, Vector2{40_mm, 0_degree}).value(),
2430 vol1.get());
2431 BOOST_CHECK_EQUAL(
2432 composite123->resolveVolume(gctx, Vector2{70_mm, 0_degree}).value(),
2433 vol2.get());
2434 BOOST_CHECK_EQUAL(
2435 composite123->resolveVolume(gctx, Vector2{100_mm, 0_degree}).value(),
2436 vol3.get());
2437
2438 BOOST_CHECK_EQUAL(composite123->size(), 3);
2439 }
2440
2441 BOOST_AUTO_TEST_SUITE_END()
2442
2443 BOOST_AUTO_TEST_SUITE_END()
2444
2445 }