File indexing completed on 2025-01-18 09:12:51
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include <boost/test/data/test_case.hpp>
0010 #include <boost/test/tools/output_test_stream.hpp>
0011 #include <boost/test/unit_test.hpp>
0012
0013 #include "Acts/Definitions/Algebra.hpp"
0014 #include "Acts/Definitions/Alignment.hpp"
0015 #include "Acts/Definitions/Tolerance.hpp"
0016 #include "Acts/Definitions/Units.hpp"
0017 #include "Acts/Geometry/Extent.hpp"
0018 #include "Acts/Geometry/GeometryContext.hpp"
0019 #include "Acts/Geometry/Polyhedron.hpp"
0020 #include "Acts/Surfaces/AnnulusBounds.hpp"
0021 #include "Acts/Surfaces/DiscSurface.hpp"
0022 #include "Acts/Surfaces/RadialBounds.hpp"
0023 #include "Acts/Surfaces/Surface.hpp"
0024 #include "Acts/Surfaces/SurfaceBounds.hpp"
0025 #include "Acts/Surfaces/SurfaceMergingException.hpp"
0026 #include "Acts/Tests/CommonHelpers/DetectorElementStub.hpp"
0027 #include "Acts/Tests/CommonHelpers/FloatComparisons.hpp"
0028 #include "Acts/Utilities/BinningType.hpp"
0029 #include "Acts/Utilities/Intersection.hpp"
0030 #include "Acts/Utilities/Result.hpp"
0031 #include "Acts/Utilities/ThrowAssert.hpp"
0032 #include "Acts/Utilities/detail/periodic.hpp"
0033
0034 #include <cmath>
0035 #include <memory>
0036 #include <numbers>
0037 #include <ostream>
0038 #include <string>
0039
0040 using namespace Acts::UnitLiterals;
0041
0042 namespace Acts::Test {
0043
0044 GeometryContext tgContext = GeometryContext();
0045 auto logger = Acts::getDefaultLogger("UnitTests", Acts::Logging::VERBOSE);
0046
0047 BOOST_AUTO_TEST_SUITE(Surfaces)
0048
0049 BOOST_AUTO_TEST_CASE(DiscSurfaceConstruction) {
0050
0051
0052
0053 const double rMin = 1.;
0054 const double rMax = 5.;
0055 const double halfPhiSector = std::numbers::pi / 8.;
0056
0057
0058 BOOST_CHECK_NO_THROW(
0059 Surface::makeShared<DiscSurface>(Transform3::Identity(), rMin, rMax));
0060
0061
0062 Translation3 translation{0., 1., 2.};
0063 auto pTransform = Transform3(translation);
0064 BOOST_CHECK_NO_THROW(
0065 Surface::makeShared<DiscSurface>(pTransform, rMin, rMax, halfPhiSector));
0066
0067
0068 auto anotherDiscSurface =
0069 Surface::makeShared<DiscSurface>(pTransform, rMin, rMax, halfPhiSector);
0070
0071
0072
0073 auto copiedSurface = Surface::makeShared<DiscSurface>(*anotherDiscSurface);
0074 BOOST_TEST_MESSAGE("Copy constructed DiscSurface ok");
0075
0076
0077 BOOST_CHECK_NO_THROW(Surface::makeShared<DiscSurface>(
0078 tgContext, *anotherDiscSurface, pTransform));
0079
0080
0081 DetectorElementStub detElem;
0082 BOOST_CHECK_THROW(
0083 auto nullBounds = Surface::makeShared<DiscSurface>(nullptr, detElem),
0084 AssertionFailureException);
0085 }
0086
0087
0088 BOOST_AUTO_TEST_CASE(DiscSurfaceProperties) {
0089 const double rMin = 1.;
0090 const double rMax = 5.;
0091 const double halfPhiSector = std::numbers::pi / 8.;
0092
0093 const Vector3 origin3D{0, 0, 0};
0094
0095 auto discSurfaceObject = Surface::makeShared<DiscSurface>(
0096 Transform3::Identity(), rMin, rMax, halfPhiSector);
0097
0098
0099 BOOST_CHECK_EQUAL(discSurfaceObject->type(), Surface::Disc);
0100
0101
0102 Vector3 zAxis{0, 0, 1};
0103 BOOST_CHECK_EQUAL(discSurfaceObject->normal(tgContext), zAxis);
0104
0105
0106 Vector2 lpos(2., 0.05);
0107 BOOST_CHECK_EQUAL(discSurfaceObject->normal(tgContext, lpos), zAxis);
0108
0109
0110 BOOST_CHECK_EQUAL(
0111 discSurfaceObject->referencePosition(tgContext, AxisDirection::AxisRPhi),
0112 origin3D);
0113
0114
0115 BOOST_CHECK_EQUAL(discSurfaceObject->bounds().type(), SurfaceBounds::eDisc);
0116
0117 Vector3 ignoredMomentum{0., 0., 0.};
0118
0119 Vector3 point3DNotInSector{0., 1.2, 0};
0120 Vector3 point3DOnSurface{1.2, 0., 0};
0121 BOOST_CHECK(!discSurfaceObject->isOnSurface(tgContext, point3DNotInSector,
0122 ignoredMomentum,
0123 BoundaryTolerance::None()));
0124 BOOST_CHECK(!discSurfaceObject->isOnSurface(tgContext, point3DNotInSector,
0125 BoundaryTolerance::None()));
0126 BOOST_CHECK(discSurfaceObject->isOnSurface(
0127 tgContext, point3DOnSurface, ignoredMomentum, BoundaryTolerance::None()));
0128 BOOST_CHECK(discSurfaceObject->isOnSurface(tgContext, point3DOnSurface,
0129 BoundaryTolerance::None()));
0130
0131
0132 Vector3 returnedPosition{10.9, 8.7, 6.5};
0133 Vector3 expectedPosition{1.2, 0, 0};
0134 Vector2 rPhiOnDisc{1.2, 0.};
0135 Vector2 rPhiNotInSector{
0136 1.2, std::numbers::pi};
0137 returnedPosition =
0138 discSurfaceObject->localToGlobal(tgContext, rPhiOnDisc, ignoredMomentum);
0139 CHECK_CLOSE_ABS(returnedPosition, expectedPosition, 1e-6);
0140
0141 returnedPosition = discSurfaceObject->localToGlobal(
0142 tgContext, rPhiNotInSector, ignoredMomentum);
0143 Vector3 expectedNonPosition{-1.2, 0, 0};
0144 CHECK_CLOSE_ABS(returnedPosition, expectedNonPosition, 1e-6);
0145
0146
0147 Vector2 returnedLocalPosition{33., 44.};
0148 Vector2 expectedLocalPosition{1.2, 0.};
0149 returnedLocalPosition =
0150 discSurfaceObject
0151 ->globalToLocal(tgContext, point3DOnSurface, ignoredMomentum)
0152 .value();
0153 CHECK_CLOSE_ABS(returnedLocalPosition, expectedLocalPosition, 1e-6);
0154
0155
0156 returnedLocalPosition =
0157 discSurfaceObject
0158 ->globalToLocal(tgContext, point3DNotInSector, ignoredMomentum)
0159 .value();
0160
0161 Vector3 pointOutsideR{0., 100., 0};
0162 returnedLocalPosition =
0163 discSurfaceObject
0164 ->globalToLocal(tgContext, pointOutsideR, ignoredMomentum)
0165 .value();
0166
0167
0168 Vector2 rPhi1_1{std::numbers::sqrt2, std::numbers::pi / 4.};
0169 Vector2 cartesian1_1{1., 1.};
0170 CHECK_CLOSE_REL(discSurfaceObject->localPolarToCartesian(rPhi1_1),
0171 cartesian1_1, 1e-6);
0172
0173
0174 CHECK_CLOSE_REL(discSurfaceObject->localCartesianToPolar(cartesian1_1),
0175 rPhi1_1, 1e-6);
0176
0177
0178 CHECK_CLOSE_REL(discSurfaceObject->localPolarToLocalCartesian(rPhi1_1),
0179 cartesian1_1, 1e-6);
0180
0181
0182 Vector3 cartesian3D1_1{1., 1., 0.};
0183 CHECK_CLOSE_ABS(
0184 discSurfaceObject->localCartesianToGlobal(tgContext, cartesian1_1),
0185 cartesian3D1_1, 1e-6);
0186
0187
0188 CHECK_CLOSE_REL(
0189 discSurfaceObject->globalToLocalCartesian(tgContext, cartesian3D1_1),
0190 cartesian1_1, 1e-6);
0191
0192
0193 double projected3DMomentum = std::numbers::sqrt3 * 1.e6;
0194 Vector3 momentum{projected3DMomentum, projected3DMomentum,
0195 projected3DMomentum};
0196 Vector3 ignoredPosition = discSurfaceObject->center(tgContext);
0197 CHECK_CLOSE_REL(discSurfaceObject->pathCorrection(tgContext, ignoredPosition,
0198 momentum.normalized()),
0199 std::numbers::sqrt3, 0.01);
0200
0201
0202 Vector3 globalPosition{1.2, 0., -10.};
0203 Vector3 direction{0., 0., 1.};
0204 Vector3 expected{1.2, 0., 0.};
0205
0206
0207
0208 auto sfIntersection = discSurfaceObject
0209 ->intersect(tgContext, globalPosition, direction,
0210 BoundaryTolerance::Infinite())
0211 .closest();
0212 Intersection3D expectedIntersect{Vector3{1.2, 0., 0.}, 10.,
0213 IntersectionStatus::reachable};
0214 BOOST_CHECK(sfIntersection.isValid());
0215 CHECK_CLOSE_ABS(sfIntersection.position(), expectedIntersect.position(),
0216 1e-9);
0217 CHECK_CLOSE_ABS(sfIntersection.pathLength(), expectedIntersect.pathLength(),
0218 1e-9);
0219 BOOST_CHECK_EQUAL(sfIntersection.object(), discSurfaceObject.get());
0220
0221
0222 boost::test_tools::output_test_stream nameOuput;
0223 nameOuput << discSurfaceObject->name();
0224 BOOST_CHECK(nameOuput.is_equal("Acts::DiscSurface"));
0225 }
0226
0227
0228 BOOST_AUTO_TEST_CASE(DiscSurfaceAssignment) {
0229 const double rMin = 1.;
0230 const double rMax = 5.;
0231 const double halfPhiSector = std::numbers::pi / 8.;
0232
0233 auto discSurfaceObject = Surface::makeShared<DiscSurface>(
0234 Transform3::Identity(), rMin, rMax, halfPhiSector);
0235 auto assignedDisc =
0236 Surface::makeShared<DiscSurface>(Transform3::Identity(), 2.2, 4.4, 0.07);
0237
0238 BOOST_CHECK_NO_THROW(*assignedDisc = *discSurfaceObject);
0239 BOOST_CHECK((*assignedDisc) == (*discSurfaceObject));
0240 }
0241
0242
0243 BOOST_AUTO_TEST_CASE(DiscSurfaceExtent) {
0244 const double rMin = 1.;
0245 const double rMax = 5.;
0246
0247 auto pDisc =
0248 Surface::makeShared<DiscSurface>(Transform3::Identity(), 0., rMax);
0249 auto pDiscExtent = pDisc->polyhedronRepresentation(tgContext, 1).extent();
0250
0251 CHECK_CLOSE_ABS(0., pDiscExtent.min(AxisDirection::AxisZ),
0252 s_onSurfaceTolerance);
0253 CHECK_CLOSE_ABS(0., pDiscExtent.max(AxisDirection::AxisZ),
0254 s_onSurfaceTolerance);
0255 CHECK_CLOSE_ABS(0., pDiscExtent.min(AxisDirection::AxisR),
0256 s_onSurfaceTolerance);
0257 CHECK_CLOSE_ABS(rMax, pDiscExtent.max(AxisDirection::AxisR),
0258 s_onSurfaceTolerance);
0259 CHECK_CLOSE_ABS(-rMax, pDiscExtent.min(AxisDirection::AxisX),
0260 s_onSurfaceTolerance);
0261 CHECK_CLOSE_ABS(rMax, pDiscExtent.max(AxisDirection::AxisX),
0262 s_onSurfaceTolerance);
0263 CHECK_CLOSE_ABS(-rMax, pDiscExtent.min(AxisDirection::AxisY),
0264 s_onSurfaceTolerance);
0265 CHECK_CLOSE_ABS(rMax, pDiscExtent.max(AxisDirection::AxisY),
0266 s_onSurfaceTolerance);
0267 CHECK_CLOSE_ABS(-std::numbers::pi, pDiscExtent.min(AxisDirection::AxisPhi),
0268 s_onSurfaceTolerance);
0269 CHECK_CLOSE_ABS(std::numbers::pi, pDiscExtent.max(AxisDirection::AxisPhi),
0270 s_onSurfaceTolerance);
0271
0272 auto pRing =
0273 Surface::makeShared<DiscSurface>(Transform3::Identity(), rMin, rMax);
0274 auto pRingExtent = pRing->polyhedronRepresentation(tgContext, 1).extent();
0275
0276 CHECK_CLOSE_ABS(0., pRingExtent.min(AxisDirection::AxisZ),
0277 s_onSurfaceTolerance);
0278 CHECK_CLOSE_ABS(0., pRingExtent.max(AxisDirection::AxisZ),
0279 s_onSurfaceTolerance);
0280 CHECK_CLOSE_ABS(rMin, pRingExtent.min(AxisDirection::AxisR),
0281 s_onSurfaceTolerance);
0282 CHECK_CLOSE_ABS(rMax, pRingExtent.max(AxisDirection::AxisR),
0283 s_onSurfaceTolerance);
0284 CHECK_CLOSE_ABS(-rMax, pRingExtent.min(AxisDirection::AxisX),
0285 s_onSurfaceTolerance);
0286 CHECK_CLOSE_ABS(rMax, pRingExtent.max(AxisDirection::AxisX),
0287 s_onSurfaceTolerance);
0288 CHECK_CLOSE_ABS(-rMax, pRingExtent.min(AxisDirection::AxisY),
0289 s_onSurfaceTolerance);
0290 CHECK_CLOSE_ABS(rMax, pRingExtent.max(AxisDirection::AxisY),
0291 s_onSurfaceTolerance);
0292 }
0293
0294
0295 BOOST_AUTO_TEST_CASE(DiscSurfaceAlignment) {
0296 Translation3 translation{0., 1., 2.};
0297 Transform3 transform(translation);
0298 const double rMin = 1.;
0299 const double rMax = 5.;
0300 const double halfPhiSector = std::numbers::pi / 8.;
0301
0302 auto discSurfaceObject =
0303 Surface::makeShared<DiscSurface>(transform, rMin, rMax, halfPhiSector);
0304
0305 const auto& rotation = transform.rotation();
0306
0307 const Vector3 localZAxis = rotation.col(2);
0308
0309 CHECK_CLOSE_ABS(localZAxis, Vector3(0., 0., 1.), 1e-15);
0310
0311
0312 Vector3 globalPosition{0, 4, 2};
0313 Vector3 momentum{0, 0, 1};
0314 Vector3 direction = momentum.normalized();
0315
0316
0317 const AlignmentToPathMatrix& alignToPath =
0318 discSurfaceObject->alignmentToPathDerivative(tgContext, globalPosition,
0319 direction);
0320
0321 AlignmentToPathMatrix expAlignToPath = AlignmentToPathMatrix::Zero();
0322 expAlignToPath << 0, 0, 1, 3, 0, 0;
0323
0324 CHECK_CLOSE_ABS(alignToPath, expAlignToPath, 1e-10);
0325
0326
0327
0328 const auto& loc3DToLocBound =
0329 discSurfaceObject->localCartesianToBoundLocalDerivative(tgContext,
0330 globalPosition);
0331
0332 ActsMatrix<2, 3> expLoc3DToLocBound = ActsMatrix<2, 3>::Zero();
0333 expLoc3DToLocBound << 0, 1, 0, -1. / 3, 0, 0;
0334 CHECK_CLOSE_ABS(loc3DToLocBound, expLoc3DToLocBound, 1e-10);
0335 }
0336
0337 BOOST_AUTO_TEST_CASE(DiscSurfaceBinningPosition) {
0338 using namespace Acts::UnitLiterals;
0339 Vector3 s{5_mm, 7_mm, 10_cm};
0340 Transform3 trf;
0341 trf = Translation3(s) * AngleAxis3{0.5, Vector3::UnitZ()};
0342
0343 double minR = 300;
0344 double maxR = 330;
0345
0346 {
0347
0348 auto bounds =
0349 std::make_shared<RadialBounds>(minR, maxR, std::numbers::pi / 8, 0.1);
0350 auto disc = Acts::Surface::makeShared<Acts::DiscSurface>(trf, bounds);
0351
0352 Vector3 bp = disc->referencePosition(tgContext, AxisDirection::AxisR);
0353 double r = (bounds->rMax() + bounds->rMin()) / 2.0;
0354 double phi = bounds->get(RadialBounds::eAveragePhi);
0355 Vector3 exp = Vector3{r * std::cos(phi), r * std::sin(phi), 0};
0356 exp = trf * exp;
0357
0358 BOOST_CHECK_EQUAL(bp, exp);
0359 BOOST_CHECK_EQUAL(
0360 disc->referencePositionValue(tgContext, AxisDirection::AxisR),
0361 VectorHelpers::perp(exp));
0362
0363 bp = disc->referencePosition(tgContext, AxisDirection::AxisPhi);
0364 BOOST_CHECK_EQUAL(bp, exp);
0365 BOOST_CHECK_EQUAL(
0366 disc->referencePositionValue(tgContext, AxisDirection::AxisPhi),
0367 VectorHelpers::phi(exp));
0368
0369 for (auto b :
0370 {AxisDirection::AxisX, AxisDirection::AxisY, AxisDirection::AxisZ,
0371 AxisDirection::AxisEta, AxisDirection::AxisRPhi,
0372 AxisDirection::AxisTheta, AxisDirection::AxisMag}) {
0373 BOOST_TEST_CONTEXT("binValue: " << b) {
0374 BOOST_CHECK_EQUAL(disc->referencePosition(tgContext, b),
0375 disc->center(tgContext));
0376 }
0377 }
0378 }
0379
0380 {
0381
0382 double minPhiRel = -0.3;
0383 double maxPhiRel = 0.2;
0384 Vector2 origin{5_mm, 5_mm};
0385 auto bounds = std::make_shared<AnnulusBounds>(minR, maxR, minPhiRel,
0386 maxPhiRel, origin);
0387
0388 auto disc = Acts::Surface::makeShared<Acts::DiscSurface>(trf, bounds);
0389
0390 Vector3 bp = disc->referencePosition(tgContext, AxisDirection::AxisR);
0391 double r = (bounds->rMax() + bounds->rMin()) / 2.0;
0392 double phi = bounds->get(AnnulusBounds::eAveragePhi);
0393 Vector3 exp = Vector3{r * std::cos(phi), r * std::sin(phi), 0};
0394 exp = trf * exp;
0395
0396 BOOST_CHECK_EQUAL(bp, exp);
0397
0398 bp = disc->referencePosition(tgContext, AxisDirection::AxisPhi);
0399 BOOST_CHECK_EQUAL(bp, exp);
0400
0401 for (auto b :
0402 {AxisDirection::AxisX, AxisDirection::AxisY, AxisDirection::AxisZ,
0403 AxisDirection::AxisEta, AxisDirection::AxisRPhi,
0404 AxisDirection::AxisTheta, AxisDirection::AxisMag}) {
0405 BOOST_TEST_CONTEXT("binValue: " << b) {
0406 BOOST_CHECK_EQUAL(disc->referencePosition(tgContext, b),
0407 disc->center(tgContext));
0408 }
0409 }
0410 }
0411 }
0412
0413 BOOST_AUTO_TEST_SUITE(DiscSurfaceMerging)
0414
0415 namespace {
0416 std::shared_ptr<DiscSurface> makeDisc(const Transform3& transform, double rMin,
0417 double rMax,
0418 double halfPhi = std::numbers::pi,
0419 double avgPhi = 0) {
0420 return Surface::makeShared<DiscSurface>(
0421 transform, std::make_shared<RadialBounds>(rMin, rMax, halfPhi, avgPhi));
0422 }
0423
0424 }
0425
0426 BOOST_AUTO_TEST_CASE(IncompatibleBounds) {
0427 Logging::ScopedFailureThreshold ft{Logging::FATAL};
0428 Transform3 base = Transform3::Identity();
0429 auto discRadial = makeDisc(base, 30_mm, 100_mm);
0430 auto discTrap =
0431 Surface::makeShared<DiscSurface>(base, 20_mm, 40_mm, 100_mm, 150_mm);
0432 auto discTrap2 =
0433 Surface::makeShared<DiscSurface>(base, 20_mm, 40_mm, 30_mm, 100_mm);
0434
0435 BOOST_CHECK_THROW(
0436 discRadial->mergedWith(*discTrap, AxisDirection::AxisR, false, *logger),
0437
0438 SurfaceMergingException);
0439
0440 BOOST_CHECK_THROW(
0441 discTrap2->mergedWith(*discTrap, AxisDirection::AxisR, false, *logger),
0442 SurfaceMergingException);
0443 }
0444
0445 BOOST_AUTO_TEST_CASE(InvalidDetectorElement) {
0446 DetectorElementStub detElem;
0447
0448 auto bounds1 = std::make_shared<RadialBounds>(30_mm, 100_mm);
0449 auto disc1 = Surface::makeShared<DiscSurface>(bounds1, detElem);
0450
0451 auto bounds2 = std::make_shared<RadialBounds>(100_mm, 150_mm);
0452 auto disc2 = Surface::makeShared<DiscSurface>(bounds2, detElem);
0453
0454 BOOST_CHECK_THROW(
0455 disc1->mergedWith(*disc2, AxisDirection::AxisR, false, *logger),
0456 SurfaceMergingException);
0457 }
0458
0459 BOOST_DATA_TEST_CASE(IncompatibleRDirection,
0460 (boost::unit_test::data::xrange(-135, 180, 45) *
0461 boost::unit_test::data::make(Vector3{0_mm, 0_mm, 0_mm},
0462 Vector3{20_mm, 0_mm, 0_mm},
0463 Vector3{0_mm, 20_mm, 0_mm},
0464 Vector3{20_mm, 20_mm, 0_mm},
0465 Vector3{0_mm, 0_mm, 20_mm})),
0466 angle, offset) {
0467 Logging::ScopedFailureThreshold ft{Logging::FATAL};
0468
0469 Transform3 base =
0470 AngleAxis3(angle * 1_degree, Vector3::UnitX()) * Translation3(offset);
0471
0472 auto disc = makeDisc(base, 30_mm, 100_mm);
0473
0474
0475 auto discOverlap = makeDisc(base, 90_mm, 150_mm);
0476 BOOST_CHECK_THROW(disc->mergedWith(*discOverlap, Acts::AxisDirection::AxisR,
0477 false, *logger),
0478 SurfaceMergingException);
0479
0480
0481 auto discGap = makeDisc(base, 110_mm, 150_mm);
0482 BOOST_CHECK_THROW(
0483 disc->mergedWith(*discGap, Acts::AxisDirection::AxisR, false, *logger),
0484 SurfaceMergingException);
0485
0486 auto discShiftedZ = Surface::makeShared<DiscSurface>(
0487 base * Translation3{Vector3::UnitZ() * 10_mm}, 100_mm, 150_mm);
0488 BOOST_CHECK_THROW(disc->mergedWith(*discShiftedZ, Acts::AxisDirection::AxisR,
0489 false, *logger),
0490 SurfaceMergingException);
0491
0492 auto discShiftedXy = makeDisc(
0493 base * Translation3{Vector3{1_mm, 2_mm, 200_mm}}, 100_mm, 150_mm);
0494 BOOST_CHECK_THROW(disc->mergedWith(*discShiftedXy, Acts::AxisDirection::AxisZ,
0495 false, *logger),
0496 SurfaceMergingException);
0497
0498 auto discRotatedZ = makeDisc(base * AngleAxis3{10_degree, Vector3::UnitZ()},
0499 100_mm, 150_mm, 60_degree, 0_degree);
0500 BOOST_CHECK_THROW(disc->mergedWith(*discRotatedZ, Acts::AxisDirection::AxisR,
0501 false, *logger),
0502 SurfaceMergingException);
0503
0504 auto discRotatedX =
0505 makeDisc(base * AngleAxis3{10_degree, Vector3::UnitX()}, 100_mm, 150_mm);
0506 BOOST_CHECK_THROW(disc->mergedWith(*discRotatedX, Acts::AxisDirection::AxisR,
0507 false, *logger),
0508 SurfaceMergingException);
0509
0510
0511 auto discPhi1 = makeDisc(base, 30_mm, 100_mm, 10_degree, 40_degree);
0512 auto discPhi2 = makeDisc(base, 100_mm, 160_mm, 20_degree, 40_degree);
0513 auto discPhi3 = makeDisc(base, 100_mm, 160_mm, 10_degree, 50_degree);
0514 BOOST_CHECK_THROW(
0515 discPhi1->mergedWith(*discPhi2, AxisDirection::AxisR, false, *logger),
0516 SurfaceMergingException);
0517
0518 BOOST_CHECK_THROW(
0519 discPhi1->mergedWith(*discPhi3, AxisDirection::AxisR, false, *logger),
0520 SurfaceMergingException);
0521 }
0522
0523 BOOST_DATA_TEST_CASE(RDirection,
0524 (boost::unit_test::data::xrange(-135, 180, 45) *
0525 boost::unit_test::data::make(Vector3{0_mm, 0_mm, 0_mm},
0526 Vector3{20_mm, 0_mm, 0_mm},
0527 Vector3{0_mm, 20_mm, 0_mm},
0528 Vector3{20_mm, 20_mm, 0_mm},
0529 Vector3{0_mm, 0_mm, 20_mm})),
0530 angle, offset) {
0531 Transform3 base =
0532 AngleAxis3(angle * 1_degree, Vector3::UnitX()) * Translation3(offset);
0533
0534 auto disc = makeDisc(base, 30_mm, 100_mm);
0535
0536 auto disc2 =
0537 makeDisc(base * AngleAxis3(14_degree, Vector3::UnitZ()), 100_mm, 150_mm);
0538
0539 auto [disc3, reversed] =
0540 disc->mergedWith(*disc2, Acts::AxisDirection::AxisR, false, *logger);
0541 BOOST_REQUIRE_NE(disc3, nullptr);
0542 BOOST_CHECK(!reversed);
0543
0544 auto [disc3Reversed, reversed2] =
0545 disc2->mergedWith(*disc, Acts::AxisDirection::AxisR, false, *logger);
0546 BOOST_REQUIRE_NE(disc3Reversed, nullptr);
0547 BOOST_CHECK(disc3->bounds() == disc3Reversed->bounds());
0548 BOOST_CHECK(reversed2);
0549
0550 const auto* bounds = dynamic_cast<const RadialBounds*>(&disc3->bounds());
0551 BOOST_REQUIRE_NE(bounds, nullptr);
0552
0553 BOOST_CHECK_EQUAL(bounds->get(RadialBounds::eMinR), 30_mm);
0554 BOOST_CHECK_EQUAL(bounds->get(RadialBounds::eMaxR), 150_mm);
0555
0556
0557 BOOST_CHECK_EQUAL(base.matrix(), disc3->transform(tgContext).matrix());
0558
0559
0560 Transform3 expected12 = base;
0561 BOOST_CHECK_EQUAL(expected12.matrix(), disc3->transform(tgContext).matrix());
0562
0563 Transform3 expected21 = base * AngleAxis3(14_degree, Vector3::UnitZ());
0564 CHECK_CLOSE_OR_SMALL(disc3Reversed->transform(tgContext).matrix(),
0565 expected21.matrix(), 1e-6, 1e-10);
0566
0567
0568 auto discPhi1 = makeDisc(base, 30_mm, 100_mm, 10_degree, 40_degree);
0569 auto discPhi2 = makeDisc(base, 100_mm, 160_mm, 10_degree, 40_degree);
0570 auto [discPhi12, reversedPhi12] =
0571 discPhi1->mergedWith(*discPhi2, AxisDirection::AxisR, false, *logger);
0572 BOOST_REQUIRE_NE(discPhi12, nullptr);
0573
0574 const auto* boundsPhi12 =
0575 dynamic_cast<const RadialBounds*>(&discPhi12->bounds());
0576 BOOST_REQUIRE_NE(boundsPhi12, nullptr);
0577
0578 BOOST_CHECK_EQUAL(boundsPhi12->get(RadialBounds::eMinR), 30_mm);
0579 BOOST_CHECK_EQUAL(boundsPhi12->get(RadialBounds::eMaxR), 160_mm);
0580 BOOST_CHECK_EQUAL(boundsPhi12->get(RadialBounds::eHalfPhiSector), 10_degree);
0581 }
0582
0583 BOOST_DATA_TEST_CASE(IncompatiblePhiDirection,
0584 (boost::unit_test::data::xrange(-135, 180, 45) *
0585 boost::unit_test::data::make(Vector3{0_mm, 0_mm, 0_mm},
0586 Vector3{20_mm, 0_mm, 0_mm},
0587 Vector3{0_mm, 20_mm, 0_mm},
0588 Vector3{20_mm, 20_mm, 0_mm},
0589 Vector3{0_mm, 0_mm, 20_mm}) *
0590 boost::unit_test::data::xrange(-1300, 1300, 104)),
0591 angle, offset, phiShift) {
0592 Logging::ScopedFailureThreshold ft{Logging::FATAL};
0593 Transform3 base =
0594 AngleAxis3(angle * 1_degree, Vector3::UnitX()) * Translation3(offset);
0595
0596 auto a = [phiShift](double v) {
0597 return detail::radian_sym(v + phiShift * 1_degree);
0598 };
0599
0600 auto discPhi = makeDisc(base, 30_mm, 100_mm, 10_degree, a(40_degree));
0601
0602
0603 auto discPhi2 = makeDisc(base, 30_mm, 100_mm, 45_degree, a(85_degree));
0604 BOOST_CHECK_THROW(discPhi->mergedWith(*discPhi2, Acts::AxisDirection::AxisPhi,
0605 false, *logger),
0606 SurfaceMergingException);
0607
0608
0609 auto discPhi3 = makeDisc(base, 30_mm, 100_mm, 45_degree, a(105_degree));
0610 BOOST_CHECK_THROW(discPhi->mergedWith(*discPhi3, Acts::AxisDirection::AxisPhi,
0611 false, *logger),
0612 SurfaceMergingException);
0613
0614
0615 auto discPhi4 = makeDisc(base * Translation3{Vector3::UnitZ() * 20_mm}, 30_mm,
0616 100_mm, 45_degree, a(95_degree));
0617 BOOST_CHECK_THROW(discPhi->mergedWith(*discPhi4, Acts::AxisDirection::AxisPhi,
0618 false, *logger),
0619 SurfaceMergingException);
0620
0621
0622 auto discPhi5 = makeDisc(base, 100_mm, 150_mm, 45_degree, a(95_degree));
0623 BOOST_CHECK_THROW(discPhi->mergedWith(*discPhi5, Acts::AxisDirection::AxisPhi,
0624 false, *logger),
0625 SurfaceMergingException);
0626 }
0627
0628 BOOST_DATA_TEST_CASE(PhiDirection,
0629 (boost::unit_test::data::xrange(-135, 180, 45) *
0630 boost::unit_test::data::make(Vector3{0_mm, 0_mm, 0_mm},
0631 Vector3{20_mm, 0_mm, 0_mm},
0632 Vector3{0_mm, 20_mm, 0_mm},
0633 Vector3{20_mm, 20_mm, 0_mm},
0634 Vector3{0_mm, 0_mm, 20_mm}) *
0635 boost::unit_test::data::xrange(-1300, 1300, 104)),
0636 angle, offset, phiShift) {
0637 Transform3 base =
0638 AngleAxis3(angle * 1_degree, Vector3::UnitX()) * Translation3(offset);
0639
0640 auto a = [phiShift](double v) {
0641 return detail::radian_sym(v + phiShift * 1_degree);
0642 };
0643
0644 BOOST_TEST_CONTEXT("Internal rotation") {
0645 auto disc = makeDisc(base, 30_mm, 100_mm, 10_degree, a(40_degree));
0646 auto disc2 = makeDisc(base, 30_mm, 100_mm, 45_degree, a(95_degree));
0647
0648 auto [disc3, reversed] =
0649 disc->mergedWith(*disc2, Acts::AxisDirection::AxisPhi, false, *logger);
0650 BOOST_REQUIRE_NE(disc3, nullptr);
0651 BOOST_CHECK_EQUAL(base.matrix(), disc3->transform(tgContext).matrix());
0652 BOOST_CHECK(reversed);
0653
0654 auto [disc3Reversed, reversed2] =
0655 disc2->mergedWith(*disc, Acts::AxisDirection::AxisPhi, false, *logger);
0656 BOOST_REQUIRE_NE(disc3Reversed, nullptr);
0657 BOOST_CHECK(*disc3 == *disc3Reversed);
0658 BOOST_CHECK(!reversed2);
0659
0660 const auto* bounds = dynamic_cast<const RadialBounds*>(&disc3->bounds());
0661 BOOST_REQUIRE_NE(bounds, nullptr);
0662
0663 BOOST_CHECK_SMALL(
0664 detail::difference_periodic(bounds->get(RadialBounds::eAveragePhi),
0665 a(85_degree), 2 * std::numbers::pi),
0666 1e-6);
0667 BOOST_CHECK_CLOSE(bounds->get(RadialBounds::eHalfPhiSector), 55_degree,
0668 1e-6);
0669
0670 auto disc4 = makeDisc(base, 30_mm, 100_mm, 20_degree, a(170_degree));
0671 auto disc5 = makeDisc(base, 30_mm, 100_mm, 10_degree, a(-160_degree));
0672 auto [disc45, reversed45] =
0673 disc4->mergedWith(*disc5, Acts::AxisDirection::AxisPhi, false, *logger);
0674 BOOST_REQUIRE_NE(disc45, nullptr);
0675 BOOST_CHECK_EQUAL(base.matrix(), disc45->transform(tgContext).matrix());
0676 BOOST_CHECK(reversed45);
0677
0678 auto [disc54, reversed54] =
0679 disc5->mergedWith(*disc4, Acts::AxisDirection::AxisPhi, false, *logger);
0680 BOOST_REQUIRE_NE(disc54, nullptr);
0681 BOOST_CHECK(!reversed54);
0682
0683 BOOST_CHECK(*disc54 == *disc45);
0684
0685 const auto* bounds45 = dynamic_cast<const RadialBounds*>(&disc45->bounds());
0686 BOOST_REQUIRE_NE(bounds, nullptr);
0687
0688 BOOST_CHECK_SMALL(
0689 detail::difference_periodic(bounds45->get(RadialBounds::eAveragePhi),
0690 a(180_degree), 2 * std::numbers::pi),
0691 1e-6);
0692 BOOST_CHECK_CLOSE(bounds45->get(RadialBounds::eHalfPhiSector), 30_degree,
0693 1e-6);
0694
0695 auto disc6 = makeDisc(base, 30_mm, 100_mm, 90_degree, a(0_degree));
0696 auto disc7 = makeDisc(base, 30_mm, 100_mm, 90_degree, a(180_degree));
0697
0698 auto [disc67, reversed67] =
0699 disc6->mergedWith(*disc7, Acts::AxisDirection::AxisPhi, false, *logger);
0700 BOOST_REQUIRE_NE(disc67, nullptr);
0701 CHECK_CLOSE_OR_SMALL(disc67->transform(tgContext).matrix(), base.matrix(),
0702 1e-6, 1e-10);
0703 BOOST_CHECK(!reversed67);
0704
0705 auto [disc76, reversed76] =
0706 disc7->mergedWith(*disc6, Acts::AxisDirection::AxisPhi, false, *logger);
0707 BOOST_REQUIRE_NE(disc76, nullptr);
0708
0709 BOOST_CHECK(*disc76 != *disc67);
0710
0711 BOOST_CHECK_NE(disc76->bounds(), disc67->bounds());
0712
0713 BOOST_CHECK_EQUAL(disc76->transform(tgContext).matrix(),
0714 disc67->transform(tgContext).matrix());
0715
0716 BOOST_CHECK(!reversed76);
0717
0718 const auto* bounds67 = dynamic_cast<const RadialBounds*>(&disc67->bounds());
0719 BOOST_REQUIRE_NE(bounds67, nullptr);
0720 BOOST_CHECK_SMALL(
0721 detail::difference_periodic(bounds67->get(RadialBounds::eAveragePhi),
0722 a(90_degree), 2 * std::numbers::pi),
0723 1e-6);
0724 BOOST_CHECK_CLOSE(bounds67->get(RadialBounds::eHalfPhiSector), 180_degree,
0725 1e-6);
0726 }
0727
0728 BOOST_TEST_CONTEXT("External rotation") {
0729 Transform3 trf1 = base * AngleAxis3(a(40_degree), Vector3::UnitZ());
0730 auto disc = makeDisc(trf1, 30_mm, 100_mm, 10_degree, 0_degree);
0731 Transform3 trf2 = base * AngleAxis3(a(95_degree), Vector3::UnitZ());
0732 auto disc2 = makeDisc(trf2, 30_mm, 100_mm, 45_degree, 0_degree);
0733
0734 auto [disc3, reversed] =
0735 disc->mergedWith(*disc2, Acts::AxisDirection::AxisPhi, true, *logger);
0736 BOOST_REQUIRE_NE(disc3, nullptr);
0737 Transform3 trfExpected12 =
0738 base * AngleAxis3(a(85_degree), Vector3::UnitZ());
0739 CHECK_CLOSE_OR_SMALL(disc3->transform(tgContext).matrix(),
0740 trfExpected12.matrix(), 1e-6, 1e-10);
0741 BOOST_CHECK(reversed);
0742
0743 auto [disc3Reversed, reversed2] =
0744 disc2->mergedWith(*disc, Acts::AxisDirection::AxisPhi, true, *logger);
0745 BOOST_REQUIRE_NE(disc3Reversed, nullptr);
0746 BOOST_CHECK(*disc3 == *disc3Reversed);
0747 BOOST_CHECK(!reversed2);
0748
0749 const auto* bounds = dynamic_cast<const RadialBounds*>(&disc3->bounds());
0750 BOOST_REQUIRE_NE(bounds, nullptr);
0751
0752 BOOST_CHECK_EQUAL(bounds->get(RadialBounds::eAveragePhi), 0);
0753 BOOST_CHECK_CLOSE(bounds->get(RadialBounds::eHalfPhiSector), 55_degree,
0754 1e-6);
0755
0756 Transform3 trf4 = base * AngleAxis3(a(170_degree), Vector3::UnitZ());
0757 auto disc4 = makeDisc(trf4, 30_mm, 100_mm, 20_degree, 0_degree);
0758 Transform3 trf5 = base * AngleAxis3(a(-160_degree), Vector3::UnitZ());
0759 auto disc5 = makeDisc(trf5, 30_mm, 100_mm, 10_degree, 0_degree);
0760 auto [disc45, reversed45] =
0761 disc4->mergedWith(*disc5, Acts::AxisDirection::AxisPhi, true, *logger);
0762 BOOST_REQUIRE_NE(disc45, nullptr);
0763 Transform3 trfExpected45 =
0764 base * AngleAxis3(a(180_degree), Vector3::UnitZ());
0765 CHECK_CLOSE_OR_SMALL(disc45->transform(tgContext).matrix(),
0766 trfExpected45.matrix(), 1e-6, 1e-10);
0767 BOOST_CHECK(reversed45);
0768
0769 auto [disc54, reversed54] =
0770 disc5->mergedWith(*disc4, Acts::AxisDirection::AxisPhi, true, *logger);
0771 BOOST_REQUIRE_NE(disc54, nullptr);
0772 BOOST_CHECK(!reversed54);
0773
0774 BOOST_CHECK(*disc54 == *disc45);
0775
0776 const auto* bounds45 = dynamic_cast<const RadialBounds*>(&disc45->bounds());
0777 BOOST_REQUIRE_NE(bounds, nullptr);
0778
0779 BOOST_CHECK_EQUAL(bounds45->get(RadialBounds::eAveragePhi), 0);
0780 BOOST_CHECK_CLOSE(bounds45->get(RadialBounds::eHalfPhiSector), 30_degree,
0781 1e-6);
0782
0783 Transform3 trf6 = base * AngleAxis3(a(0_degree), Vector3::UnitZ());
0784 auto disc6 = makeDisc(trf6, 30_mm, 100_mm, 90_degree, 0_degree);
0785 Transform3 trf7 = base * AngleAxis3(a(180_degree), Vector3::UnitZ());
0786 auto disc7 = makeDisc(trf7, 30_mm, 100_mm, 90_degree, 0_degree);
0787 auto [disc67, reversed67] =
0788 disc6->mergedWith(*disc7, Acts::AxisDirection::AxisPhi, true, *logger);
0789 BOOST_REQUIRE_NE(disc67, nullptr);
0790 Transform3 trfExpected67 =
0791 base * AngleAxis3(a(90_degree), Vector3::UnitZ());
0792 CHECK_CLOSE_OR_SMALL(disc67->transform(tgContext).matrix(),
0793 trfExpected67.matrix(), 1e-6, 1e-10);
0794 BOOST_CHECK(!reversed67);
0795
0796 auto [disc76, reversed76] =
0797 disc7->mergedWith(*disc6, Acts::AxisDirection::AxisPhi, true, *logger);
0798 BOOST_REQUIRE_NE(disc76, nullptr);
0799
0800 BOOST_CHECK(*disc76 != *disc67);
0801 BOOST_CHECK_NE(disc76->transform(tgContext).matrix(),
0802 disc67->transform(tgContext).matrix());
0803
0804 BOOST_CHECK_EQUAL(disc76->bounds(), disc67->bounds());
0805
0806
0807 BOOST_CHECK(!reversed76);
0808
0809 const auto* bounds67 = dynamic_cast<const RadialBounds*>(&disc67->bounds());
0810 BOOST_REQUIRE_NE(bounds67, nullptr);
0811 BOOST_CHECK_EQUAL(bounds67->get(RadialBounds::eAveragePhi), 0);
0812 BOOST_CHECK_CLOSE(bounds67->get(RadialBounds::eHalfPhiSector), 180_degree,
0813 1e-6);
0814 }
0815 }
0816
0817 BOOST_AUTO_TEST_SUITE_END()
0818
0819 BOOST_AUTO_TEST_SUITE_END()
0820
0821 }