File indexing completed on 2025-09-18 08:14:17
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/Tolerance.hpp"
0015 #include "Acts/Definitions/Units.hpp"
0016 #include "Acts/Geometry/Extent.hpp"
0017 #include "Acts/Geometry/GeometryContext.hpp"
0018 #include "Acts/Geometry/Polyhedron.hpp"
0019 #include "Acts/Surfaces/CylinderBounds.hpp"
0020 #include "Acts/Surfaces/CylinderSurface.hpp"
0021 #include "Acts/Surfaces/Surface.hpp"
0022 #include "Acts/Surfaces/SurfaceBounds.hpp"
0023 #include "Acts/Surfaces/SurfaceMergingException.hpp"
0024 #include "Acts/Tests/CommonHelpers/DetectorElementStub.hpp"
0025 #include "Acts/Tests/CommonHelpers/FloatComparisons.hpp"
0026 #include "Acts/Utilities/Intersection.hpp"
0027 #include "Acts/Utilities/Logger.hpp"
0028 #include "Acts/Utilities/Result.hpp"
0029 #include "Acts/Utilities/ThrowAssert.hpp"
0030 #include "Acts/Utilities/detail/periodic.hpp"
0031
0032 #include <cmath>
0033 #include <memory>
0034 #include <numbers>
0035 #include <string>
0036
0037 using namespace Acts::UnitLiterals;
0038
0039 namespace Acts::Test {
0040
0041 auto logger = Acts::getDefaultLogger("UnitTests", Acts::Logging::VERBOSE);
0042
0043
0044 GeometryContext testContext = GeometryContext();
0045
0046 BOOST_AUTO_TEST_SUITE(CylinderSurfaces)
0047
0048 BOOST_AUTO_TEST_CASE(CylinderSurfaceConstruction) {
0049
0050
0051
0052
0053 const double radius = 1.;
0054 const double halfZ = 10.;
0055 const double halfPhiSector = std::numbers::pi / 8.;
0056 const Translation3 translation{0., 1., 2.};
0057
0058 auto pTransform = Transform3(translation);
0059 BOOST_CHECK_EQUAL(
0060 Surface::makeShared<CylinderSurface>(pTransform, radius, halfZ)->type(),
0061 Surface::Cylinder);
0062
0063
0064 BOOST_CHECK_EQUAL(Surface::makeShared<CylinderSurface>(pTransform, radius,
0065 halfZ, halfPhiSector)
0066 ->type(),
0067 Surface::Cylinder);
0068
0069
0070 auto pCylinderBounds = std::make_shared<const CylinderBounds>(radius, halfZ);
0071 BOOST_CHECK_EQUAL(
0072 Surface::makeShared<CylinderSurface>(pTransform, pCylinderBounds)->type(),
0073 Surface::Cylinder);
0074
0075
0076 auto cylinderSurfaceObject =
0077 Surface::makeShared<CylinderSurface>(pTransform, radius, halfZ);
0078 auto copiedCylinderSurface =
0079 Surface::makeShared<CylinderSurface>(*cylinderSurfaceObject);
0080 BOOST_CHECK_EQUAL(copiedCylinderSurface->type(), Surface::Cylinder);
0081 BOOST_CHECK(*copiedCylinderSurface == *cylinderSurfaceObject);
0082
0083
0084 auto copiedTransformedCylinderSurface = Surface::makeShared<CylinderSurface>(
0085 testContext, *cylinderSurfaceObject, pTransform);
0086 BOOST_CHECK_EQUAL(copiedTransformedCylinderSurface->type(),
0087 Surface::Cylinder);
0088
0089
0090 BOOST_CHECK_THROW(auto nullBounds = Surface::makeShared<CylinderSurface>(
0091 Transform3::Identity(), nullptr),
0092 AssertionFailureException);
0093 }
0094
0095
0096 BOOST_AUTO_TEST_CASE(CylinderSurfaceProperties) {
0097
0098 const double radius = 1.;
0099 const double halfZ = 10.;
0100 const Translation3 translation{0., 1., 2.};
0101
0102 auto pTransform = Transform3(translation);
0103 auto cylinderSurfaceObject =
0104 Surface::makeShared<CylinderSurface>(pTransform, radius, halfZ);
0105
0106
0107 BOOST_CHECK_EQUAL(cylinderSurfaceObject->type(), Surface::Cylinder);
0108
0109
0110 Vector3 referencePosition{0., 1., 2.};
0111 CHECK_CLOSE_ABS(cylinderSurfaceObject->referencePosition(
0112 testContext, AxisDirection::AxisPhi),
0113 referencePosition, 1e-9);
0114
0115
0116 const double invSqrt2 = 1. / std::numbers::sqrt2;
0117 Vector3 globalPosition{invSqrt2, 1. - invSqrt2, 0.};
0118 Vector3 globalPositionZ{invSqrt2, 1. - invSqrt2, 2.};
0119 Vector3 momentum{15., 15., 15.};
0120 Vector3 momentum2{6.6, -3., 2.};
0121 RotationMatrix3 expectedFrame;
0122 expectedFrame << invSqrt2, 0., invSqrt2, invSqrt2, 0., -invSqrt2, 0., 1., 0.;
0123
0124 CHECK_CLOSE_OR_SMALL(cylinderSurfaceObject->referenceFrame(
0125 testContext, globalPosition, momentum),
0126 expectedFrame, 1e-6, 1e-9);
0127
0128 CHECK_CLOSE_OR_SMALL(cylinderSurfaceObject->referenceFrame(
0129 testContext, globalPositionZ, momentum2),
0130 expectedFrame, 1e-6, 1e-9);
0131
0132
0133 Vector3 origin{0., 0., 0.};
0134 Vector3 normal3D = {0., -1., 0.};
0135 CHECK_CLOSE_ABS(cylinderSurfaceObject->normal(testContext, origin), normal3D,
0136 1e-9);
0137
0138 Vector3 pos45deg = {invSqrt2, 1 + invSqrt2, 0.};
0139 Vector3 pos45degZ = {invSqrt2, 1 + invSqrt2, 4.};
0140 Vector3 normal45deg = {invSqrt2, invSqrt2, 0.};
0141
0142 CHECK_CLOSE_ABS(cylinderSurfaceObject->normal(testContext, pos45deg),
0143 normal45deg, 1e-6 * invSqrt2);
0144
0145 CHECK_CLOSE_ABS(cylinderSurfaceObject->normal(testContext, pos45degZ),
0146 normal45deg, 1e-6 * invSqrt2);
0147
0148
0149 Vector2 positionPiBy2(1., 0.);
0150 Vector3 normalAtPiBy2{std::cos(1.), std::sin(1.), 0.};
0151 CHECK_CLOSE_ABS(cylinderSurfaceObject->normal(testContext, positionPiBy2),
0152 normalAtPiBy2, 1e-9);
0153
0154
0155 Vector3 symmetryAxis{0., 0., 1.};
0156 CHECK_CLOSE_ABS(cylinderSurfaceObject->rotSymmetryAxis(testContext),
0157 symmetryAxis, 1e-9);
0158
0159
0160 BOOST_CHECK_EQUAL(cylinderSurfaceObject->bounds().type(),
0161 SurfaceBounds::eCylinder);
0162
0163
0164 Vector2 localPosition{0., 0.};
0165 globalPosition = cylinderSurfaceObject->localToGlobal(
0166 testContext, localPosition, momentum);
0167 Vector3 expectedPosition{1, 1, 2};
0168 BOOST_CHECK_EQUAL(globalPosition, expectedPosition);
0169
0170
0171 localPosition = cylinderSurfaceObject
0172 ->globalToLocal(testContext, globalPosition, momentum)
0173 .value();
0174 Vector2 expectedLocalPosition{0., 0.};
0175 BOOST_CHECK_EQUAL(localPosition, expectedLocalPosition);
0176
0177
0178 Vector3 offSurface{100, 1, 2};
0179 BOOST_CHECK(cylinderSurfaceObject->isOnSurface(
0180 testContext, globalPosition, momentum, BoundaryTolerance::None()));
0181 BOOST_CHECK(cylinderSurfaceObject->isOnSurface(testContext, globalPosition,
0182 BoundaryTolerance::None()));
0183 BOOST_CHECK(!cylinderSurfaceObject->isOnSurface(
0184 testContext, offSurface, momentum, BoundaryTolerance::None()));
0185 BOOST_CHECK(!cylinderSurfaceObject->isOnSurface(testContext, offSurface,
0186 BoundaryTolerance::None()));
0187
0188
0189 Vector3 direction{-1., 0, 0};
0190 auto sfIntersection = cylinderSurfaceObject->intersect(
0191 testContext, offSurface, direction, BoundaryTolerance::Infinite());
0192 Intersection3D expectedIntersect{Vector3{1, 1, 2}, 99.,
0193 IntersectionStatus::reachable};
0194 BOOST_CHECK(sfIntersection[0].isValid());
0195 CHECK_CLOSE_ABS(sfIntersection[0].position(), expectedIntersect.position(),
0196 1e-9);
0197 CHECK_CLOSE_ABS(sfIntersection[0].pathLength(),
0198 expectedIntersect.pathLength(), 1e-9);
0199
0200 BOOST_CHECK(sfIntersection[1].isValid());
0201
0202 double pn = sfIntersection[0].pathLength();
0203 double pa = sfIntersection[1].pathLength();
0204 BOOST_CHECK_LT(std::abs(pn), std::abs(pa));
0205 BOOST_CHECK_EQUAL(&sfIntersection.surface(), cylinderSurfaceObject.get());
0206
0207
0208 CHECK_CLOSE_REL(cylinderSurfaceObject->pathCorrection(testContext, offSurface,
0209 momentum.normalized()),
0210 std::numbers::sqrt3, 0.01);
0211
0212
0213 BOOST_CHECK_EQUAL(cylinderSurfaceObject->name(),
0214 std::string("Acts::CylinderSurface"));
0215
0216
0217 boost::test_tools::output_test_stream dumpOutput;
0218 std::string expected =
0219 "Acts::CylinderSurface\n\
0220 Center position (x, y, z) = (0.0000, 1.0000, 2.0000)\n\
0221 Rotation: colX = (1.000000, 0.000000, 0.000000)\n\
0222 colY = (0.000000, 1.000000, 0.000000)\n\
0223 colZ = (0.000000, 0.000000, 1.000000)\n\
0224 Bounds : Acts::CylinderBounds: (radius, halfLengthZ, halfPhiSector, averagePhi, bevelMinZ, bevelMaxZ) = (1.0000000, 10.0000000, 3.1415927, 0.0000000, 0.0000000, 0.0000000)";
0225 dumpOutput << cylinderSurfaceObject->toStream(testContext);
0226 BOOST_CHECK(dumpOutput.is_equal(expected));
0227 }
0228
0229 BOOST_AUTO_TEST_CASE(CylinderSurfaceEqualityOperators) {
0230 const double radius = 1.;
0231 const double halfZ = 10.;
0232 const Translation3 translation{0., 1., 2.};
0233
0234 auto pTransform = Transform3(translation);
0235 auto cylinderSurfaceObject =
0236 Surface::makeShared<CylinderSurface>(pTransform, radius, halfZ);
0237
0238 auto cylinderSurfaceObject2 =
0239 Surface::makeShared<CylinderSurface>(pTransform, radius, halfZ);
0240
0241
0242 BOOST_CHECK(*cylinderSurfaceObject == *cylinderSurfaceObject2);
0243
0244 BOOST_TEST_CHECKPOINT(
0245 "Create and then assign a CylinderSurface object to the existing one");
0246
0247 auto assignedCylinderSurface =
0248 Surface::makeShared<CylinderSurface>(Transform3::Identity(), 6.6, 5.4);
0249 *assignedCylinderSurface = *cylinderSurfaceObject;
0250
0251 BOOST_CHECK(*assignedCylinderSurface == *cylinderSurfaceObject);
0252 }
0253
0254
0255 BOOST_AUTO_TEST_CASE(CylinderSurfaceExtent) {
0256 using enum AxisDirection;
0257
0258
0259 const double radius = 1.;
0260 const double halfZ = 10.;
0261 const Translation3 translation{0., 0., 2.};
0262
0263 auto pTransform = Transform3(translation);
0264 auto cylinderSurface =
0265 Surface::makeShared<CylinderSurface>(pTransform, radius, halfZ);
0266
0267 auto cylinderExtent =
0268 cylinderSurface->polyhedronRepresentation(testContext, 1).extent();
0269
0270 CHECK_CLOSE_ABS(-8, cylinderExtent.min(AxisZ), s_onSurfaceTolerance);
0271 CHECK_CLOSE_ABS(12, cylinderExtent.max(AxisZ), s_onSurfaceTolerance);
0272 CHECK_CLOSE_ABS(radius, cylinderExtent.min(AxisR), s_onSurfaceTolerance);
0273 CHECK_CLOSE_ABS(radius, cylinderExtent.max(AxisR), s_onSurfaceTolerance);
0274 CHECK_CLOSE_ABS(-radius, cylinderExtent.min(AxisX), s_onSurfaceTolerance);
0275 CHECK_CLOSE_ABS(radius, cylinderExtent.max(AxisX), s_onSurfaceTolerance);
0276 CHECK_CLOSE_ABS(-radius, cylinderExtent.min(AxisY), s_onSurfaceTolerance);
0277 CHECK_CLOSE_ABS(radius, cylinderExtent.max(AxisY), s_onSurfaceTolerance);
0278 }
0279
0280
0281 BOOST_AUTO_TEST_CASE(CylinderSurfaceAlignment) {
0282 const double radius = 1.;
0283 const double halfZ = 10.;
0284 const Translation3 translation{0., 1., 2.};
0285
0286 auto pTransform = Transform3(translation);
0287 auto cylinderSurfaceObject =
0288 Surface::makeShared<CylinderSurface>(pTransform, radius, halfZ);
0289
0290 const auto& rotation = pTransform.rotation();
0291
0292 const Vector3 localZAxis = rotation.col(2);
0293
0294 CHECK_CLOSE_ABS(localZAxis, Vector3(0., 0., 1.), 1e-15);
0295
0296
0297 Vector3 globalPosition{0, 2, 2};
0298
0299
0300
0301 const auto& loc3DToLocBound =
0302 cylinderSurfaceObject->localCartesianToBoundLocalDerivative(
0303 testContext, globalPosition);
0304
0305 ActsMatrix<2, 3> expLoc3DToLocBound = ActsMatrix<2, 3>::Zero();
0306 expLoc3DToLocBound << -1, 0, 0, 0, 0, 1;
0307 CHECK_CLOSE_ABS(loc3DToLocBound, expLoc3DToLocBound, 1e-10);
0308 }
0309
0310 BOOST_AUTO_TEST_CASE(CylinderSurfaceBinningPosition) {
0311 using namespace Acts::UnitLiterals;
0312 Vector3 s{5_mm, 7_mm, 10_cm};
0313 Transform3 trf;
0314 trf = Translation3(s) * AngleAxis3{0.5, Vector3::UnitZ()};
0315
0316 double r = 300;
0317 double halfZ = 330;
0318 double averagePhi = 0.1;
0319
0320 auto bounds = std::make_shared<CylinderBounds>(r, halfZ, std::numbers::pi / 8,
0321 averagePhi);
0322 auto cylinder = Acts::Surface::makeShared<CylinderSurface>(trf, bounds);
0323
0324 Vector3 exp = Vector3{r * std::cos(averagePhi), r * std::sin(averagePhi), 0};
0325 exp = trf * exp;
0326
0327 Vector3 bp = cylinder->referencePosition(testContext, AxisDirection::AxisR);
0328 CHECK_CLOSE_ABS(bp, exp, 1e-10);
0329 CHECK_CLOSE_ABS(
0330 cylinder->referencePositionValue(testContext, AxisDirection::AxisR),
0331 VectorHelpers::perp(exp), 1e-10);
0332
0333 bp = cylinder->referencePosition(testContext, AxisDirection::AxisRPhi);
0334 CHECK_CLOSE_ABS(bp, exp, 1e-10);
0335 CHECK_CLOSE_ABS(
0336 cylinder->referencePositionValue(testContext, AxisDirection::AxisRPhi),
0337 VectorHelpers::phi(exp) * VectorHelpers::perp(exp), 1e-10);
0338
0339 for (auto b : {AxisDirection::AxisX, AxisDirection::AxisY,
0340 AxisDirection::AxisZ, AxisDirection::AxisEta,
0341 AxisDirection::AxisTheta, AxisDirection::AxisMag}) {
0342 BOOST_TEST_CONTEXT("binValue: " << b) {
0343 BOOST_CHECK_EQUAL(cylinder->referencePosition(testContext, b),
0344 cylinder->center(testContext));
0345 }
0346 }
0347 }
0348
0349 BOOST_AUTO_TEST_SUITE(CylinderSurfaceMerging)
0350
0351 BOOST_AUTO_TEST_CASE(InvalidDetectorElement) {
0352 DetectorElementStub detElem;
0353
0354 auto bounds = std::make_shared<CylinderBounds>(100_mm, 100_mm);
0355 auto cyl1 = Surface::makeShared<CylinderSurface>(bounds, detElem);
0356 auto cyl2 = Surface::makeShared<CylinderSurface>(bounds, detElem);
0357
0358 BOOST_CHECK_THROW(
0359 cyl1->mergedWith(*cyl2, Acts::AxisDirection::AxisR, false, *logger),
0360 SurfaceMergingException);
0361 }
0362
0363 BOOST_DATA_TEST_CASE(IncompatibleZDirection,
0364 (boost::unit_test::data::xrange(-135, 180, 45) *
0365 boost::unit_test::data::make(Vector3{0_mm, 0_mm, 0_mm},
0366 Vector3{20_mm, 0_mm, 0_mm},
0367 Vector3{0_mm, 20_mm, 0_mm},
0368 Vector3{20_mm, 20_mm, 0_mm},
0369 Vector3{0_mm, 0_mm, 20_mm})),
0370 angle, offset) {
0371 Logging::ScopedFailureThreshold ft{Logging::FATAL};
0372
0373 Transform3 base =
0374 AngleAxis3(angle * 1_degree, Vector3::UnitX()) * Translation3(offset);
0375
0376 auto cyl = Surface::makeShared<CylinderSurface>(base, 30_mm, 100_mm);
0377 auto cyl2 = Surface::makeShared<CylinderSurface>(
0378 base * Translation3{Vector3::UnitZ() * 200_mm}, 30_mm, 100_mm);
0379
0380 BOOST_CHECK_THROW(
0381 cyl->mergedWith(*cyl2, Acts::AxisDirection::AxisPhi, false, *logger),
0382 SurfaceMergingException);
0383
0384 auto cylShiftedXy = Surface::makeShared<CylinderSurface>(
0385 base * Translation3{Vector3{1_mm, 2_mm, 200_mm}}, 30_mm, 100_mm);
0386 BOOST_CHECK_THROW(cyl->mergedWith(*cylShiftedXy, Acts::AxisDirection::AxisZ,
0387 false, *logger),
0388 SurfaceMergingException);
0389
0390 auto cylRotatedX = Surface::makeShared<CylinderSurface>(
0391 base * AngleAxis3{10_degree, Vector3::UnitX()} *
0392 Translation3{Vector3::UnitZ() * 200_mm},
0393 30_mm, 100_mm);
0394 BOOST_CHECK_THROW(
0395 cyl->mergedWith(*cylRotatedX, Acts::AxisDirection::AxisZ, false, *logger),
0396 SurfaceMergingException);
0397
0398
0399 auto cyl3 = Surface::makeShared<CylinderSurface>(
0400 base * Translation3{Vector3::UnitZ() * 200_mm}, 35_mm, 100_mm);
0401 BOOST_CHECK_THROW(
0402 cyl->mergedWith(*cyl3, Acts::AxisDirection::AxisZ, false, *logger),
0403 SurfaceMergingException);
0404
0405
0406 auto cyl4 = Surface::makeShared<CylinderSurface>(
0407 base * Translation3{Vector3::UnitZ() * 200_mm}, 30_mm, 100_mm,
0408 std::numbers::pi, 0, std::numbers::pi / 8.);
0409 BOOST_CHECK_THROW(
0410 cyl->mergedWith(*cyl4, Acts::AxisDirection::AxisZ, false, *logger),
0411 SurfaceMergingException);
0412
0413 auto cyl5 = Surface::makeShared<CylinderSurface>(
0414 base * Translation3{Vector3::UnitZ() * 200_mm}, 30_mm, 100_mm,
0415 std::numbers::pi, 0, 0, std::numbers::pi / 8.);
0416 BOOST_CHECK_THROW(
0417 cyl->mergedWith(*cyl5, Acts::AxisDirection::AxisZ, false, *logger),
0418 SurfaceMergingException);
0419
0420
0421 auto cyl6 = Surface::makeShared<CylinderSurface>(
0422 base * Translation3{Vector3::UnitZ() * 150_mm}, 30_mm, 100_mm);
0423 BOOST_CHECK_THROW(
0424 cyl->mergedWith(*cyl6, Acts::AxisDirection::AxisZ, false, *logger),
0425 SurfaceMergingException);
0426
0427
0428 auto cyl7 = Surface::makeShared<CylinderSurface>(
0429 base * Translation3{Vector3::UnitZ() * 250_mm}, 30_mm, 100_mm);
0430 BOOST_CHECK_THROW(
0431 cyl->mergedWith(*cyl7, Acts::AxisDirection::AxisZ, false, *logger),
0432 SurfaceMergingException);
0433
0434
0435 auto cyl8 = Surface::makeShared<CylinderSurface>(
0436 base * AngleAxis3(14_degree, Vector3::UnitZ()) *
0437 Translation3{Vector3::UnitZ() * 200_mm},
0438 30_mm, 100_mm, 10_degree, 40_degree);
0439 BOOST_CHECK_THROW(
0440 cyl->mergedWith(*cyl8, Acts::AxisDirection::AxisZ, false, *logger),
0441 SurfaceMergingException);
0442
0443 auto cylPhi1 = Surface::makeShared<CylinderSurface>(Transform3::Identity(),
0444 30_mm, 100_mm, 45_degree);
0445 auto cylPhi2 = Surface::makeShared<CylinderSurface>(
0446 Transform3{Translation3{Vector3::UnitZ() * 150_mm}}, 30_mm, 50_mm,
0447 55_degree);
0448 BOOST_CHECK_THROW(
0449 cylPhi1->mergedWith(*cylPhi2, Acts::AxisDirection::AxisZ, false, *logger),
0450 SurfaceMergingException);
0451 }
0452
0453 BOOST_DATA_TEST_CASE(ZDirection,
0454 (boost::unit_test::data::xrange(-135, 180, 45) *
0455 boost::unit_test::data::make(Vector3{0_mm, 0_mm, 0_mm},
0456 Vector3{20_mm, 0_mm, 0_mm},
0457 Vector3{0_mm, 20_mm, 0_mm},
0458 Vector3{20_mm, 20_mm, 0_mm},
0459 Vector3{0_mm, 0_mm, 20_mm})),
0460 angle, offset) {
0461 Transform3 base =
0462 AngleAxis3(angle * 1_degree, Vector3::UnitX()) * Translation3(offset);
0463
0464 auto cyl = Surface::makeShared<CylinderSurface>(base, 30_mm, 100_mm);
0465
0466 auto cyl2 = Surface::makeShared<CylinderSurface>(
0467 base * AngleAxis3(14_degree, Vector3::UnitZ()) *
0468 Translation3{Vector3::UnitZ() * 200_mm},
0469 30_mm, 100_mm);
0470
0471 auto [cyl3, reversed] =
0472 cyl->mergedWith(*cyl2, Acts::AxisDirection::AxisZ, false, *logger);
0473 BOOST_REQUIRE_NE(cyl3, nullptr);
0474 BOOST_CHECK(!reversed);
0475
0476 auto [cyl3Reversed, reversed2] =
0477 cyl2->mergedWith(*cyl, Acts::AxisDirection::AxisZ, false, *logger);
0478 BOOST_REQUIRE_NE(cyl3Reversed, nullptr);
0479 BOOST_CHECK(cyl3->bounds() == cyl3Reversed->bounds());
0480 BOOST_CHECK(reversed2);
0481
0482 auto bounds = cyl3->bounds();
0483
0484 BOOST_CHECK_EQUAL(bounds.get(CylinderBounds::eR), 30_mm);
0485 BOOST_CHECK_EQUAL(bounds.get(CylinderBounds::eHalfLengthZ), 200_mm);
0486 BOOST_CHECK_EQUAL(bounds.get(CylinderBounds::eAveragePhi), 0_degree);
0487 BOOST_CHECK_EQUAL(bounds.get(CylinderBounds::eHalfPhiSector), 180_degree);
0488
0489
0490 Transform3 expected12 = base * Translation3{Vector3::UnitZ() * 100_mm};
0491 BOOST_CHECK_EQUAL(expected12.matrix(), cyl3->transform(testContext).matrix());
0492
0493 Transform3 expected21 = base * AngleAxis3(14_degree, Vector3::UnitZ()) *
0494 Translation3{Vector3::UnitZ() * 100_mm};
0495 CHECK_CLOSE_OR_SMALL(cyl3Reversed->transform(testContext).matrix(),
0496 expected21.matrix(), 1e-6, 1e-10);
0497
0498 auto cylPhi1 = Surface::makeShared<CylinderSurface>(Transform3::Identity(),
0499 30_mm, 100_mm, 45_degree);
0500 auto cylPhi2 = Surface::makeShared<CylinderSurface>(
0501 Transform3{Translation3{Vector3::UnitZ() * 150_mm}}, 30_mm, 50_mm,
0502 45_degree);
0503
0504 auto [cylPhi12, reversedPhy12] =
0505 cylPhi1->mergedWith(*cylPhi2, Acts::AxisDirection::AxisZ, false, *logger);
0506
0507 BOOST_REQUIRE_NE(cylPhi12, nullptr);
0508 auto boundsPhi12 = cylPhi12->bounds();
0509 BOOST_CHECK_EQUAL(boundsPhi12.get(CylinderBounds::eR), 30_mm);
0510 BOOST_CHECK_EQUAL(boundsPhi12.get(CylinderBounds::eHalfLengthZ), 150_mm);
0511 BOOST_CHECK_EQUAL(boundsPhi12.get(CylinderBounds::eAveragePhi), 0_degree);
0512 BOOST_CHECK_EQUAL(boundsPhi12.get(CylinderBounds::eHalfPhiSector), 45_degree);
0513 }
0514
0515 BOOST_DATA_TEST_CASE(IncompatibleRPhiDirection,
0516 (boost::unit_test::data::xrange(-135, 180, 45) *
0517 boost::unit_test::data::make(Vector3{0_mm, 0_mm, 0_mm},
0518 Vector3{20_mm, 0_mm, 0_mm},
0519 Vector3{0_mm, 20_mm, 0_mm},
0520 Vector3{20_mm, 20_mm, 0_mm},
0521 Vector3{0_mm, 0_mm, 20_mm}) *
0522 boost::unit_test::data::xrange(-1300, 1300, 104)),
0523 angle, offset, phiShift) {
0524 Logging::ScopedFailureThreshold ft{Logging::FATAL};
0525 Transform3 base =
0526 AngleAxis3(angle * 1_degree, Vector3::UnitX()) * Translation3(offset);
0527
0528 auto a = [phiShift](double v) {
0529 return detail::radian_sym(v + phiShift * 1_degree);
0530 };
0531
0532 auto cylPhi = Surface::makeShared<CylinderSurface>(base, 30_mm, 100_mm,
0533 10_degree, a(40_degree));
0534
0535
0536 auto cylPhi2 = Surface::makeShared<CylinderSurface>(base, 30_mm, 100_mm,
0537 45_degree, a(85_degree));
0538 BOOST_CHECK_THROW(cylPhi->mergedWith(*cylPhi2, Acts::AxisDirection::AxisRPhi,
0539 false, *logger),
0540 SurfaceMergingException);
0541
0542
0543 auto cylPhi3 = Surface::makeShared<CylinderSurface>(base, 30_mm, 100_mm,
0544 45_degree, a(105_degree));
0545 BOOST_CHECK_THROW(cylPhi->mergedWith(*cylPhi3, Acts::AxisDirection::AxisRPhi,
0546 false, *logger),
0547 SurfaceMergingException);
0548
0549
0550 auto cylPhi4 = Surface::makeShared<CylinderSurface>(
0551 base * Translation3{Vector3::UnitZ() * 20_mm}, 30_mm, 100_mm, 45_degree,
0552 a(95_degree));
0553 BOOST_CHECK_THROW(cylPhi->mergedWith(*cylPhi4, Acts::AxisDirection::AxisRPhi,
0554 false, *logger),
0555 SurfaceMergingException);
0556
0557
0558 auto cylPhi5 = Surface::makeShared<CylinderSurface>(base, 30_mm, 110_mm,
0559 45_degree, a(95_degree));
0560 BOOST_CHECK_THROW(cylPhi->mergedWith(*cylPhi5, Acts::AxisDirection::AxisRPhi,
0561 false, *logger),
0562 SurfaceMergingException);
0563 }
0564
0565 BOOST_DATA_TEST_CASE(RPhiDirection,
0566 (boost::unit_test::data::xrange(-135, 180, 45) *
0567 boost::unit_test::data::make(Vector3{0_mm, 0_mm, 0_mm},
0568 Vector3{20_mm, 0_mm, 0_mm},
0569 Vector3{0_mm, 20_mm, 0_mm},
0570 Vector3{20_mm, 20_mm, 0_mm},
0571 Vector3{0_mm, 0_mm, 20_mm}) *
0572 boost::unit_test::data::xrange(-1300, 1300, 104)),
0573 angle, offset, phiShift) {
0574 Transform3 base =
0575 AngleAxis3(angle * 1_degree, Vector3::UnitX()) * Translation3(offset);
0576
0577 auto a = [phiShift](double v) {
0578 return detail::radian_sym(v + phiShift * 1_degree);
0579 };
0580
0581 BOOST_TEST_CONTEXT("Internal rotation") {
0582 auto cyl = Surface::makeShared<CylinderSurface>(base, 30_mm, 100_mm,
0583 10_degree, a(40_degree));
0584 auto cyl2 = Surface::makeShared<CylinderSurface>(base, 30_mm, 100_mm,
0585 45_degree, a(95_degree));
0586
0587 auto [cyl3, reversed] =
0588 cyl->mergedWith(*cyl2, Acts::AxisDirection::AxisRPhi, false, *logger);
0589 BOOST_REQUIRE_NE(cyl3, nullptr);
0590 BOOST_CHECK_EQUAL(base.matrix(), cyl3->transform(testContext).matrix());
0591 BOOST_CHECK(reversed);
0592
0593 auto [cyl3Reversed, reversed2] =
0594 cyl2->mergedWith(*cyl, Acts::AxisDirection::AxisRPhi, false, *logger);
0595 BOOST_REQUIRE_NE(cyl3Reversed, nullptr);
0596 BOOST_CHECK(*cyl3 == *cyl3Reversed);
0597 BOOST_CHECK(!reversed2);
0598
0599 const auto& bounds = cyl3->bounds();
0600
0601 BOOST_CHECK_SMALL(
0602 detail::difference_periodic(bounds.get(CylinderBounds::eAveragePhi),
0603 a(85_degree), 2 * std::numbers::pi),
0604 1e-6);
0605 BOOST_CHECK_CLOSE(bounds.get(CylinderBounds::eHalfPhiSector), 55_degree,
0606 0.1);
0607
0608 auto cyl4 = Surface::makeShared<CylinderSurface>(base, 30_mm, 100_mm,
0609 20_degree, a(170_degree));
0610 auto cyl5 = Surface::makeShared<CylinderSurface>(base, 30_mm, 100_mm,
0611 10_degree, a(-160_degree));
0612 auto [cyl45, reversed45] =
0613 cyl4->mergedWith(*cyl5, Acts::AxisDirection::AxisRPhi, false, *logger);
0614 BOOST_REQUIRE_NE(cyl45, nullptr);
0615 BOOST_CHECK_EQUAL(base.matrix(), cyl45->transform(testContext).matrix());
0616 BOOST_CHECK(reversed45);
0617
0618 auto [cyl54, reversed54] =
0619 cyl5->mergedWith(*cyl4, Acts::AxisDirection::AxisRPhi, false, *logger);
0620 BOOST_REQUIRE_NE(cyl54, nullptr);
0621 BOOST_CHECK(!reversed54);
0622
0623 BOOST_CHECK(*cyl54 == *cyl45);
0624
0625 BOOST_CHECK_SMALL(detail::difference_periodic(
0626 cyl45->bounds().get(CylinderBounds::eAveragePhi),
0627 a(180_degree), 2 * std::numbers::pi),
0628 1e-6);
0629 BOOST_CHECK_CLOSE(cyl45->bounds().get(CylinderBounds::eHalfPhiSector),
0630 30_degree, 1e-6);
0631
0632 auto cyl6 = Surface::makeShared<CylinderSurface>(base, 30_mm, 100_mm,
0633 90_degree, a(90_degree));
0634 auto cyl7 = Surface::makeShared<CylinderSurface>(base, 30_mm, 100_mm,
0635 90_degree, a(-90_degree));
0636
0637 auto [cyl67, reversed67] =
0638 cyl6->mergedWith(*cyl7, Acts::AxisDirection::AxisRPhi, false, *logger);
0639 BOOST_REQUIRE_NE(cyl67, nullptr);
0640 BOOST_CHECK_EQUAL(base.matrix(), cyl67->transform(testContext).matrix());
0641
0642 auto [cyl76, reversed76] =
0643 cyl7->mergedWith(*cyl6, Acts::AxisDirection::AxisRPhi, false, *logger);
0644 BOOST_REQUIRE_NE(cyl76, nullptr);
0645 BOOST_CHECK_EQUAL(base.matrix(), cyl76->transform(testContext).matrix());
0646
0647
0648
0649 BOOST_CHECK(!reversed67);
0650 BOOST_CHECK(!reversed76);
0651
0652 BOOST_CHECK_SMALL(detail::difference_periodic(
0653 cyl67->bounds().get(CylinderBounds::eAveragePhi),
0654 a(180_degree), 2 * std::numbers::pi),
0655 1e-6);
0656 BOOST_CHECK_CLOSE(cyl67->bounds().get(CylinderBounds::eHalfPhiSector),
0657 180_degree, 1e-6);
0658 }
0659
0660 BOOST_TEST_CONTEXT("External rotation") {
0661 Transform3 trf1 = base * AngleAxis3(a(40_degree), Vector3::UnitZ());
0662 auto cyl1 = Surface::makeShared<CylinderSurface>(trf1, 30_mm, 100_mm,
0663 10_degree, 0_degree);
0664
0665 Transform3 trf2 = base * AngleAxis3(a(95_degree), Vector3::UnitZ());
0666 auto cyl2 = Surface::makeShared<CylinderSurface>(trf2, 30_mm, 100_mm,
0667 45_degree, 0_degree);
0668
0669 auto [cyl3, reversed] =
0670 cyl1->mergedWith(*cyl2, Acts::AxisDirection::AxisRPhi, true, *logger);
0671
0672 BOOST_REQUIRE_NE(cyl3, nullptr);
0673 Transform3 trfExpected12 =
0674 base * AngleAxis3(a(85_degree), Vector3::UnitZ());
0675 CHECK_CLOSE_OR_SMALL(cyl3->transform(testContext).matrix(),
0676 trfExpected12.matrix(), 1e-6, 1e-10);
0677 BOOST_CHECK(reversed);
0678
0679 BOOST_CHECK_EQUAL(cyl3->bounds().get(CylinderBounds::eAveragePhi), 0);
0680 BOOST_CHECK_CLOSE(cyl3->bounds().get(CylinderBounds::eHalfPhiSector),
0681 55_degree, 1e-6);
0682
0683 Transform3 trf4 = base * AngleAxis3(a(170_degree), Vector3::UnitZ());
0684 auto cyl4 = Surface::makeShared<CylinderSurface>(trf4, 30_mm, 100_mm,
0685 20_degree, 0_degree);
0686 Transform3 trf5 = base * AngleAxis3(a(-160_degree), Vector3::UnitZ());
0687 auto cyl5 = Surface::makeShared<CylinderSurface>(trf5, 30_mm, 100_mm,
0688 10_degree, 0_degree);
0689 auto [cyl45, reversed45] =
0690 cyl4->mergedWith(*cyl5, Acts::AxisDirection::AxisRPhi, true, *logger);
0691 BOOST_REQUIRE_NE(cyl45, nullptr);
0692 Transform3 trfExpected45 =
0693 base * AngleAxis3(a(180_degree), Vector3::UnitZ());
0694 CHECK_CLOSE_OR_SMALL(cyl45->transform(testContext).matrix(),
0695 trfExpected45.matrix(), 1e-6, 1e-10);
0696 BOOST_CHECK(reversed45);
0697
0698 auto [cyl54, reversed54] =
0699 cyl5->mergedWith(*cyl4, Acts::AxisDirection::AxisRPhi, true, *logger);
0700 BOOST_REQUIRE_NE(cyl54, nullptr);
0701 BOOST_CHECK(!reversed54);
0702
0703 BOOST_CHECK(*cyl54 == *cyl45);
0704
0705 BOOST_CHECK_EQUAL(cyl45->bounds().get(CylinderBounds::eAveragePhi), 0);
0706 BOOST_CHECK_CLOSE(cyl45->bounds().get(CylinderBounds::eHalfPhiSector),
0707 30_degree, 1e-6);
0708
0709 Transform3 trf6 = base * AngleAxis3(a(90_degree), Vector3::UnitZ());
0710 auto cyl6 = Surface::makeShared<CylinderSurface>(trf6, 30_mm, 100_mm,
0711 90_degree, 0_degree);
0712 Transform3 trf7 = base * AngleAxis3(a(-90_degree), Vector3::UnitZ());
0713 auto cyl7 = Surface::makeShared<CylinderSurface>(trf7, 30_mm, 100_mm,
0714 90_degree, 0_degree);
0715
0716 auto [cyl67, reversed67] =
0717 cyl6->mergedWith(*cyl7, Acts::AxisDirection::AxisRPhi, true, *logger);
0718 BOOST_REQUIRE_NE(cyl67, nullptr);
0719 Transform3 expected67 = trf6 * AngleAxis3(90_degree, Vector3::UnitZ());
0720 CHECK_CLOSE_OR_SMALL(cyl67->transform(testContext).matrix(),
0721 expected67.matrix(), 1e-6, 1e-10);
0722
0723 auto [cyl76, reversed76] =
0724 cyl7->mergedWith(*cyl6, Acts::AxisDirection::AxisRPhi, true, *logger);
0725 BOOST_REQUIRE_NE(cyl76, nullptr);
0726 Transform3 expected76 = trf7 * AngleAxis3(90_degree, Vector3::UnitZ());
0727 CHECK_CLOSE_OR_SMALL(cyl76->transform(testContext).matrix(),
0728 expected76.matrix(), 1e-6, 1e-10);
0729
0730
0731
0732 BOOST_CHECK(!reversed67);
0733 BOOST_CHECK(!reversed76);
0734
0735 BOOST_CHECK_EQUAL(cyl67->bounds().get(CylinderBounds::eAveragePhi), 0);
0736 BOOST_CHECK_CLOSE(cyl67->bounds().get(CylinderBounds::eHalfPhiSector),
0737 180_degree, 0.1);
0738 }
0739 }
0740
0741 BOOST_AUTO_TEST_SUITE_END()
0742 BOOST_AUTO_TEST_SUITE_END()
0743
0744 }