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