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