Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-07-01 07:54:07

0001 // This file is part of the ACTS project.
0002 //
0003 // Copyright (C) 2016 CERN for the benefit of the ACTS project
0004 //
0005 // This Source Code Form is subject to the terms of the Mozilla Public
0006 // License, v. 2.0. If a copy of the MPL was not distributed with this
0007 // file, You can obtain one at https://mozilla.org/MPL/2.0/.
0008 
0009 #include <boost/test/tools/old/interface.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/TrackParametrization.hpp"
0017 #include "Acts/Definitions/Units.hpp"
0018 #include "Acts/Geometry/Extent.hpp"
0019 #include "Acts/Geometry/GeometryContext.hpp"
0020 #include "Acts/Geometry/Polyhedron.hpp"
0021 #include "Acts/Surfaces/PlaneSurface.hpp"
0022 #include "Acts/Surfaces/RectangleBounds.hpp"
0023 #include "Acts/Surfaces/Surface.hpp"
0024 #include "Acts/Surfaces/SurfaceBounds.hpp"
0025 #include "Acts/Surfaces/SurfaceMergingException.hpp"
0026 #include "Acts/Surfaces/TrapezoidBounds.hpp"
0027 #include "Acts/Tests/CommonHelpers/DetectorElementStub.hpp"
0028 #include "Acts/Tests/CommonHelpers/FloatComparisons.hpp"
0029 #include "Acts/Utilities/Intersection.hpp"
0030 #include "Acts/Utilities/Result.hpp"
0031 #include "Acts/Utilities/ThrowAssert.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 // Create a test context
0043 GeometryContext tgContext = GeometryContext();
0044 
0045 BOOST_AUTO_TEST_SUITE(PlaneSurfaces)
0046 /// Unit test for creating compliant/non-compliant PlaneSurface object
0047 BOOST_AUTO_TEST_CASE(PlaneSurfaceConstruction) {
0048   /// Test default construction
0049   // default construction is deleted
0050 
0051   // bounds object, rectangle type
0052   auto rBounds = std::make_shared<const RectangleBounds>(3., 4.);
0053   /// Constructor with transform and bounds
0054   Translation3 translation{0., 1., 2.};
0055   auto pTransform = Transform3(translation);
0056 
0057   /// Constructor with transform
0058   BOOST_CHECK_EQUAL(
0059       Surface::makeShared<PlaneSurface>(pTransform, rBounds)->type(),
0060       Surface::Plane);
0061 
0062   /// Copy constructor
0063   auto planeSurfaceObject =
0064       Surface::makeShared<PlaneSurface>(pTransform, rBounds);
0065   auto copiedPlaneSurface =
0066       Surface::makeShared<PlaneSurface>(*planeSurfaceObject);
0067   BOOST_CHECK_EQUAL(copiedPlaneSurface->type(), Surface::Plane);
0068   BOOST_CHECK(*copiedPlaneSurface == *planeSurfaceObject);
0069 
0070   /// Copied and transformed
0071   auto copiedTransformedPlaneSurface = Surface::makeShared<PlaneSurface>(
0072       tgContext, *planeSurfaceObject, pTransform);
0073   BOOST_CHECK_EQUAL(copiedTransformedPlaneSurface->type(), Surface::Plane);
0074 
0075   /// Construct with nullptr bounds
0076   DetectorElementStub detElem;
0077   BOOST_CHECK_THROW(
0078       auto nullBounds = Surface::makeShared<PlaneSurface>(nullptr, detElem),
0079       AssertionFailureException);
0080 }
0081 
0082 /// Unit test for testing PlaneSurface properties
0083 BOOST_AUTO_TEST_CASE(PlaneSurfaceProperties) {
0084   // bounds object, rectangle type
0085   auto rBounds = std::make_shared<const RectangleBounds>(3., 4.);
0086 
0087   /// Test clone method
0088   Translation3 translation{0., 1., 2.};
0089   auto pTransform = Transform3(translation);
0090   auto planeSurfaceObject =
0091       Surface::makeShared<PlaneSurface>(pTransform, rBounds);
0092   // Is it in the right place?
0093   Translation3 translation2{0., 2., 4.};
0094   auto pTransform2 = Transform3(translation2);
0095   auto planeSurfaceObject2 =
0096       Surface::makeShared<PlaneSurface>(pTransform2, rBounds);
0097 
0098   /// Test type (redundant)
0099   BOOST_CHECK_EQUAL(planeSurfaceObject->type(), Surface::Plane);
0100 
0101   /// Test referencePosition
0102   Vector3 referencePosition{0., 1., 2.};
0103   BOOST_CHECK_EQUAL(
0104       planeSurfaceObject->referencePosition(tgContext, AxisDirection::AxisX),
0105       referencePosition);
0106 
0107   /// Test referenceFrame
0108   Vector3 arbitraryGlobalPosition{2., 2., 2.};
0109   Vector3 momentum{1.e6, 1.e6, 1.e6};
0110   RotationMatrix3 expectedFrame;
0111   expectedFrame << 1., 0., 0., 0., 1., 0., 0., 0., 1.;
0112 
0113   CHECK_CLOSE_OR_SMALL(planeSurfaceObject->referenceFrame(
0114                            tgContext, arbitraryGlobalPosition, momentum),
0115                        expectedFrame, 1e-6, 1e-9);
0116 
0117   /// Test normal, given 3D position
0118   Vector3 normal3D(0., 0., 1.);
0119   BOOST_CHECK_EQUAL(planeSurfaceObject->normal(tgContext), normal3D);
0120 
0121   /// Test bounds
0122   BOOST_CHECK_EQUAL(planeSurfaceObject->bounds().type(),
0123                     SurfaceBounds::eRectangle);
0124 
0125   /// Test localToGlobal
0126   Vector2 localPosition{1.5, 1.7};
0127   Vector3 globalPosition =
0128       planeSurfaceObject->localToGlobal(tgContext, localPosition, momentum);
0129   // expected position is the translated one
0130   Vector3 expectedPosition{1.5 + translation.x(), 1.7 + translation.y(),
0131                            translation.z()};
0132 
0133   CHECK_CLOSE_REL(globalPosition, expectedPosition, 1e-2);
0134 
0135   /// Testing globalToLocal
0136   localPosition =
0137       planeSurfaceObject->globalToLocal(tgContext, globalPosition, momentum)
0138           .value();
0139   Vector2 expectedLocalPosition{1.5, 1.7};
0140 
0141   CHECK_CLOSE_REL(localPosition, expectedLocalPosition, 1e-2);
0142 
0143   Vector3 globalPositionOff =
0144       globalPosition +
0145       planeSurfaceObject->normal(tgContext, localPosition) * 0.1;
0146 
0147   BOOST_CHECK(
0148       planeSurfaceObject->globalToLocal(tgContext, globalPositionOff, momentum)
0149           .error());
0150   BOOST_CHECK(planeSurfaceObject
0151                   ->globalToLocal(tgContext, globalPositionOff, momentum, 0.05)
0152                   .error());
0153   BOOST_CHECK(planeSurfaceObject
0154                   ->globalToLocal(tgContext, globalPositionOff, momentum, 0.2)
0155                   .ok());
0156 
0157   /// Test isOnSurface
0158   Vector3 offSurface{0, 1, -2.};
0159   BOOST_CHECK(planeSurfaceObject->isOnSurface(
0160       tgContext, globalPosition, momentum, BoundaryTolerance::None()));
0161   BOOST_CHECK(planeSurfaceObject->isOnSurface(tgContext, globalPosition,
0162                                               BoundaryTolerance::None()));
0163   BOOST_CHECK(!planeSurfaceObject->isOnSurface(tgContext, offSurface, momentum,
0164                                                BoundaryTolerance::None()));
0165   BOOST_CHECK(!planeSurfaceObject->isOnSurface(tgContext, offSurface,
0166                                                BoundaryTolerance::None()));
0167 
0168   /// Test intersection
0169   Vector3 direction{0., 0., 1.};
0170   auto sfIntersection = planeSurfaceObject
0171                             ->intersect(tgContext, offSurface, direction,
0172                                         BoundaryTolerance::None())
0173                             .closest();
0174   Intersection3D expectedIntersect{Vector3{0, 1, 2}, 4.,
0175                                    IntersectionStatus::reachable};
0176   BOOST_CHECK(sfIntersection.isValid());
0177   BOOST_CHECK_EQUAL(sfIntersection.position(), expectedIntersect.position());
0178   BOOST_CHECK_EQUAL(sfIntersection.pathLength(),
0179                     expectedIntersect.pathLength());
0180   BOOST_CHECK_EQUAL(sfIntersection.object(), planeSurfaceObject.get());
0181 
0182   /// Test pathCorrection
0183   CHECK_CLOSE_REL(planeSurfaceObject->pathCorrection(tgContext, offSurface,
0184                                                      momentum.normalized()),
0185                   std::numbers::sqrt3, 0.01);
0186 
0187   /// Test name
0188   BOOST_CHECK_EQUAL(planeSurfaceObject->name(),
0189                     std::string("Acts::PlaneSurface"));
0190 
0191   /// Test dump
0192   boost::test_tools::output_test_stream dumpOutput;
0193   dumpOutput << planeSurfaceObject->toStream(tgContext);
0194   BOOST_CHECK(dumpOutput.is_equal(
0195       "Acts::PlaneSurface\n"
0196       "     Center position  (x, y, z) = (0.0000, 1.0000, 2.0000)\n"
0197       "     Rotation:             colX = (1.000000, 0.000000, 0.000000)\n"
0198       "                           colY = (0.000000, 1.000000, 0.000000)\n"
0199       "                           colZ = (0.000000, 0.000000, 1.000000)\n"
0200       "     Bounds  : Acts::RectangleBounds:  (hlX, hlY) = (3.0000000, "
0201       "4.0000000)\n"
0202       "(lower left, upper right):\n"
0203       "-3.0000000 -4.0000000\n"
0204       "3.0000000 4.0000000"));
0205 }
0206 
0207 BOOST_AUTO_TEST_CASE(PlaneSurfaceEqualityOperators) {
0208   // rectangle bounds
0209   auto rBounds = std::make_shared<const RectangleBounds>(3., 4.);
0210   Translation3 translation{0., 1., 2.};
0211   auto pTransform = Transform3(translation);
0212   auto planeSurfaceObject =
0213       Surface::makeShared<PlaneSurface>(pTransform, rBounds);
0214   auto planeSurfaceObject2 =
0215       Surface::makeShared<PlaneSurface>(pTransform, rBounds);
0216 
0217   /// Test equality operator
0218   BOOST_CHECK(*planeSurfaceObject == *planeSurfaceObject2);
0219 
0220   BOOST_TEST_CHECKPOINT(
0221       "Create and then assign a PlaneSurface object to the existing one");
0222 
0223   /// Test assignment
0224   auto assignedPlaneSurface =
0225       Surface::makeShared<PlaneSurface>(Transform3::Identity(), nullptr);
0226   *assignedPlaneSurface = *planeSurfaceObject;
0227 
0228   /// Test equality of assigned to original
0229   BOOST_CHECK(*assignedPlaneSurface == *planeSurfaceObject);
0230 }
0231 
0232 /// Unit test for testing PlaneSurface extent via Polyhedron representation
0233 BOOST_AUTO_TEST_CASE(PlaneSurfaceExtent) {
0234   // First test - non-rotated
0235   static const Transform3 planeZX =
0236       AngleAxis3(-std::numbers::pi / 2., Vector3::UnitX()) *
0237       AngleAxis3(-std::numbers::pi / 2., Vector3::UnitZ()) *
0238       Transform3::Identity();
0239 
0240   double rHx = 2.;
0241   double rHy = 4.;
0242   double yPs = 3.;
0243   auto rBounds = std::make_shared<RectangleBounds>(rHx, rHy);
0244 
0245   auto plane = Surface::makeShared<PlaneSurface>(
0246       Transform3(Translation3(Vector3(0., yPs, 0.)) * planeZX), rBounds);
0247 
0248   auto planeExtent = plane->polyhedronRepresentation(tgContext, 1).extent();
0249 
0250   CHECK_CLOSE_ABS(planeExtent.min(AxisDirection::AxisZ), -rHx,
0251                   s_onSurfaceTolerance);
0252   CHECK_CLOSE_ABS(planeExtent.max(AxisDirection::AxisZ), rHx,
0253                   s_onSurfaceTolerance);
0254   CHECK_CLOSE_ABS(planeExtent.min(AxisDirection::AxisX), -rHy,
0255                   s_onSurfaceTolerance);
0256   CHECK_CLOSE_ABS(planeExtent.max(AxisDirection::AxisX), rHy,
0257                   s_onSurfaceTolerance);
0258   CHECK_CLOSE_ABS(planeExtent.min(AxisDirection::AxisY), yPs,
0259                   s_onSurfaceTolerance);
0260   CHECK_CLOSE_ABS(planeExtent.max(AxisDirection::AxisY), yPs,
0261                   s_onSurfaceTolerance);
0262   CHECK_CLOSE_ABS(planeExtent.min(AxisDirection::AxisR), yPs,
0263                   s_onSurfaceTolerance);
0264   CHECK_CLOSE_ABS(planeExtent.max(AxisDirection::AxisR), std::hypot(yPs, rHy),
0265                   s_onSurfaceTolerance);
0266 
0267   // Now rotate
0268   double alpha = 0.123;
0269   auto planeRot = Surface::makeShared<PlaneSurface>(
0270       Transform3(Translation3(Vector3(0., yPs, 0.)) *
0271                  AngleAxis3(alpha, Vector3(0., 0., 1.)) * planeZX),
0272       rBounds);
0273 
0274   auto planeExtentRot =
0275       planeRot->polyhedronRepresentation(tgContext, 1).extent();
0276   CHECK_CLOSE_ABS(planeExtentRot.min(AxisDirection::AxisZ), -rHx,
0277                   s_onSurfaceTolerance);
0278   CHECK_CLOSE_ABS(planeExtentRot.max(AxisDirection::AxisZ), rHx,
0279                   s_onSurfaceTolerance);
0280   CHECK_CLOSE_ABS(planeExtentRot.min(AxisDirection::AxisX),
0281                   -rHy * std::cos(alpha), s_onSurfaceTolerance);
0282   CHECK_CLOSE_ABS(planeExtentRot.max(AxisDirection::AxisX),
0283                   rHy * std::cos(alpha), s_onSurfaceTolerance);
0284   CHECK_CLOSE_ABS(planeExtentRot.min(AxisDirection::AxisY),
0285                   yPs - rHy * std::sin(alpha), s_onSurfaceTolerance);
0286   CHECK_CLOSE_ABS(planeExtentRot.max(AxisDirection::AxisY),
0287                   yPs + rHy * std::sin(alpha), s_onSurfaceTolerance);
0288   CHECK_CLOSE_ABS(planeExtentRot.min(AxisDirection::AxisR),
0289                   yPs * std::cos(alpha), s_onSurfaceTolerance);
0290 }
0291 
0292 BOOST_AUTO_TEST_CASE(RotatedTrapezoid) {
0293   const double shortHalfX = 100.;
0294   const double longHalfX = 200.;
0295   const double halfY = 300.;
0296   const double rotAngle = 45._degree;
0297 
0298   Vector2 edgePoint{longHalfX - 10., halfY};
0299 
0300   std::shared_ptr<TrapezoidBounds> bounds =
0301       std::make_shared<TrapezoidBounds>(shortHalfX, longHalfX, halfY);
0302 
0303   BOOST_CHECK(bounds->inside(edgePoint, BoundaryTolerance::None()));
0304   BOOST_CHECK(!bounds->inside(Eigen::Rotation2D(-rotAngle) * edgePoint,
0305                               BoundaryTolerance::None()));
0306 
0307   std::shared_ptr<TrapezoidBounds> rotatedBounds =
0308       std::make_shared<TrapezoidBounds>(shortHalfX, longHalfX, halfY, rotAngle);
0309 
0310   BOOST_CHECK(!rotatedBounds->inside(edgePoint, BoundaryTolerance::None()));
0311   BOOST_CHECK(rotatedBounds->inside(Eigen::Rotation2D(-rotAngle) * edgePoint,
0312                                     BoundaryTolerance::None()));
0313 }
0314 
0315 /// Unit test for testing PlaneSurface alignment derivatives
0316 BOOST_AUTO_TEST_CASE(PlaneSurfaceAlignment) {
0317   // bounds object, rectangle type
0318   auto rBounds = std::make_shared<const RectangleBounds>(3., 4.);
0319   // Test clone method
0320   Translation3 translation{0., 1., 2.};
0321   const double rotationAngle = std::numbers::pi / 2.;
0322   AngleAxis3 rotation(rotationAngle, Vector3::UnitY());
0323   RotationMatrix3 rotationMat = rotation.toRotationMatrix();
0324 
0325   auto pTransform = Transform3{translation * rotationMat};
0326   auto planeSurfaceObject =
0327       Surface::makeShared<PlaneSurface>(pTransform, rBounds);
0328 
0329   // The local frame z axis
0330   const Vector3 localZAxis = rotationMat.col(2);
0331   // Check the local z axis is aligned to global x axis
0332   CHECK_CLOSE_ABS(localZAxis, Vector3(1., 0., 0.), 1e-15);
0333 
0334   // Define the track (local) position and direction
0335   Vector2 localPosition{1, 2};
0336   Vector3 momentum{1, 0, 0};
0337   Vector3 direction = momentum.normalized();
0338   // Get the global position
0339   Vector3 globalPosition =
0340       planeSurfaceObject->localToGlobal(tgContext, localPosition, momentum);
0341 
0342   // (a) Test the derivative of path length w.r.t. alignment parameters
0343   const AlignmentToPathMatrix& alignToPath =
0344       planeSurfaceObject->alignmentToPathDerivative(tgContext, globalPosition,
0345                                                     direction);
0346   // The expected results
0347   AlignmentToPathMatrix expAlignToPath = AlignmentToPathMatrix::Zero();
0348   expAlignToPath << 1, 0, 0, 2, -1, 0;
0349 
0350   // Check if the calculated derivative is as expected
0351   CHECK_CLOSE_ABS(alignToPath, expAlignToPath, 1e-10);
0352 
0353   // (b) Test the derivative of bound track parameters local position w.r.t.
0354   // position in local 3D Cartesian coordinates
0355   const auto& loc3DToLocBound =
0356       planeSurfaceObject->localCartesianToBoundLocalDerivative(tgContext,
0357                                                                globalPosition);
0358   // For plane surface, this should be identity matrix
0359   CHECK_CLOSE_ABS(loc3DToLocBound, (ActsMatrix<2, 3>::Identity()), 1e-10);
0360 
0361   // (c) Test the derivative of bound parameters (only test loc0, loc1 here)
0362   // w.r.t. alignment parameters
0363   FreeVector derivatives = FreeVector::Zero();
0364   derivatives.head<3>() = direction;
0365   const AlignmentToBoundMatrix& alignToBound =
0366       planeSurfaceObject->alignmentToBoundDerivative(tgContext, globalPosition,
0367                                                      direction, derivatives);
0368   const AlignmentToPathMatrix alignToloc0 =
0369       alignToBound.block<1, 6>(eBoundLoc0, eAlignmentCenter0);
0370   const AlignmentToPathMatrix alignToloc1 =
0371       alignToBound.block<1, 6>(eBoundLoc1, eAlignmentCenter0);
0372   // The expected results
0373   AlignmentToPathMatrix expAlignToloc0;
0374   expAlignToloc0 << 0, 0, 1, 0, 0, 2;
0375   AlignmentToPathMatrix expAlignToloc1;
0376   expAlignToloc1 << 0, -1, 0, 0, 0, -1;
0377   // Check if the calculated derivatives are as expected
0378   CHECK_CLOSE_ABS(alignToloc0, expAlignToloc0, 1e-10);
0379   CHECK_CLOSE_ABS(alignToloc1, expAlignToloc1, 1e-10);
0380 }
0381 
0382 BOOST_AUTO_TEST_SUITE(PlaneSurfaceMerging)
0383 
0384 auto logger = Acts::getDefaultLogger("UnitTests", Acts::Logging::VERBOSE);
0385 
0386 // Create a test context
0387 GeometryContext gctx = GeometryContext();
0388 
0389 auto rBounds = std::make_shared<const RectangleBounds>(1., 2.);
0390 
0391 BOOST_AUTO_TEST_CASE(SurfaceOverlap) {
0392   // Correct orientation, overlapping along merging direction
0393   Translation3 offsetX{4., 0., 0.};
0394   Translation3 offsetY{0., 2., 0.};
0395 
0396   Transform3 base(Translation3::Identity());
0397   Transform3 otherX = base * offsetX;
0398   Transform3 otherY = base * offsetY;
0399 
0400   auto plane = Surface::makeShared<PlaneSurface>(base, rBounds);
0401   auto planeX = Surface::makeShared<PlaneSurface>(otherX, rBounds);
0402   auto planeY = Surface::makeShared<PlaneSurface>(otherY, rBounds);
0403 
0404   BOOST_CHECK_THROW(plane->mergedWith(*planeX, Acts::AxisDirection::AxisX),
0405                     SurfaceMergingException);
0406   BOOST_CHECK_THROW(plane->mergedWith(*planeY, Acts::AxisDirection::AxisY),
0407                     SurfaceMergingException);
0408 
0409   BOOST_CHECK_THROW(planeX->mergedWith(*plane, Acts::AxisDirection::AxisX),
0410                     SurfaceMergingException);
0411   BOOST_CHECK_THROW(planeY->mergedWith(*plane, Acts::AxisDirection::AxisY),
0412                     SurfaceMergingException);
0413 }
0414 
0415 BOOST_AUTO_TEST_CASE(SurfaceMisalignmentShift) {
0416   // Correct orientation, not aligned along orthogonal to merging direction
0417   Translation3 offsetX{2., 1., 0.};
0418   Translation3 offsetY{-1., 4., 0.};
0419   Translation3 offsetZ{0., 4., 1.};
0420 
0421   Transform3 base(Translation3::Identity());
0422   Transform3 otherX = base * offsetX;
0423   Transform3 otherY = base * offsetY;
0424   Transform3 otherZ = base * offsetZ;
0425 
0426   auto plane = Surface::makeShared<PlaneSurface>(base, rBounds);
0427   auto planeX = Surface::makeShared<PlaneSurface>(otherX, rBounds);
0428   auto planeY = Surface::makeShared<PlaneSurface>(otherY, rBounds);
0429   auto planeZ = Surface::makeShared<PlaneSurface>(otherZ, rBounds);
0430 
0431   BOOST_CHECK_THROW(plane->mergedWith(*planeX, Acts::AxisDirection::AxisX),
0432                     SurfaceMergingException);
0433   BOOST_CHECK_THROW(plane->mergedWith(*planeY, Acts::AxisDirection::AxisY),
0434                     SurfaceMergingException);
0435   BOOST_CHECK_THROW(plane->mergedWith(*planeZ, Acts::AxisDirection::AxisX),
0436                     SurfaceMergingException);
0437 
0438   BOOST_CHECK_THROW(planeX->mergedWith(*plane, Acts::AxisDirection::AxisX),
0439                     SurfaceMergingException);
0440   BOOST_CHECK_THROW(planeY->mergedWith(*plane, Acts::AxisDirection::AxisY),
0441                     SurfaceMergingException);
0442   BOOST_CHECK_THROW(planeZ->mergedWith(*plane, Acts::AxisDirection::AxisX),
0443                     SurfaceMergingException);
0444 }
0445 
0446 BOOST_AUTO_TEST_CASE(SurfaceMisalignedAngle) {
0447   // Correct positioning, rotated in different directions
0448   Translation3 offsetX{2., 0., 0.};
0449   Translation3 offsetY{0., 4., 0.};
0450 
0451   double angle = std::numbers::pi / 12;
0452   Transform3 base(Translation3::Identity());
0453   Transform3 otherX = base * offsetX * AngleAxis3(angle, Vector3::UnitZ());
0454   Transform3 otherY = base * offsetY * AngleAxis3(angle, Vector3::UnitY());
0455   Transform3 otherZ = base * offsetY * AngleAxis3(angle, Vector3::UnitZ());
0456 
0457   auto plane = Surface::makeShared<PlaneSurface>(base, rBounds);
0458   auto planeX = Surface::makeShared<PlaneSurface>(otherX, rBounds);
0459   auto planeY = Surface::makeShared<PlaneSurface>(otherY, rBounds);
0460   auto planeZ = Surface::makeShared<PlaneSurface>(otherZ, rBounds);
0461 
0462   BOOST_CHECK_THROW(plane->mergedWith(*planeX, Acts::AxisDirection::AxisX),
0463                     SurfaceMergingException);
0464   BOOST_CHECK_THROW(plane->mergedWith(*planeY, Acts::AxisDirection::AxisY),
0465                     SurfaceMergingException);
0466   BOOST_CHECK_THROW(plane->mergedWith(*planeZ, Acts::AxisDirection::AxisY),
0467                     SurfaceMergingException);
0468 
0469   BOOST_CHECK_THROW(planeX->mergedWith(*plane, Acts::AxisDirection::AxisX),
0470                     SurfaceMergingException);
0471   BOOST_CHECK_THROW(planeY->mergedWith(*plane, Acts::AxisDirection::AxisY),
0472                     SurfaceMergingException);
0473   BOOST_CHECK_THROW(planeZ->mergedWith(*plane, Acts::AxisDirection::AxisY),
0474                     SurfaceMergingException);
0475 }
0476 
0477 BOOST_AUTO_TEST_CASE(SurfaceDifferentBounds) {
0478   // Correct orientation and alignment, different bounds lengths along
0479   // orthogonal to merging direction
0480   Translation3 offset{2., 0., 0.};
0481 
0482   Transform3 base(Translation3::Identity());
0483   Transform3 other = base * offset;
0484 
0485   auto plane = Surface::makeShared<PlaneSurface>(base, rBounds);
0486 
0487   auto rBoundsOther = std::make_shared<const RectangleBounds>(2., 4.);
0488   auto planeOther = Surface::makeShared<PlaneSurface>(other, rBoundsOther);
0489 
0490   BOOST_CHECK_THROW(plane->mergedWith(*planeOther, Acts::AxisDirection::AxisX),
0491                     SurfaceMergingException);
0492 }
0493 
0494 BOOST_AUTO_TEST_CASE(XYDirection) {
0495   double angle = std::numbers::pi / 12;
0496   Translation3 offsetX{2., 0., 0.};
0497   Translation3 offsetY{0., 4., 0.};
0498 
0499   Transform3 base =
0500       AngleAxis3(angle, Vector3::UnitX()) * Translation3::Identity();
0501   Transform3 otherX = base * offsetX;
0502   Transform3 otherY = base * offsetY;
0503 
0504   auto plane = Surface::makeShared<PlaneSurface>(base, rBounds);
0505   auto planeX = Surface::makeShared<PlaneSurface>(otherX, rBounds);
0506   auto planeY = Surface::makeShared<PlaneSurface>(otherY, rBounds);
0507 
0508   BOOST_CHECK_THROW(plane->mergedWith(*planeX, Acts::AxisDirection::AxisZ),
0509                     SurfaceMergingException);
0510 
0511   auto expectedBoundsX = std::make_shared<const RectangleBounds>(2, 2);
0512   auto [planeXMerged, reversedX] =
0513       plane->mergedWith(*planeX, Acts::AxisDirection::AxisX, *logger);
0514   BOOST_REQUIRE_NE(planeXMerged, nullptr);
0515   BOOST_CHECK(!reversedX);
0516   BOOST_CHECK_EQUAL(planeXMerged->bounds(), *expectedBoundsX);
0517   BOOST_CHECK_EQUAL(planeXMerged->center(gctx), base * Vector3::UnitX() * 1);
0518 
0519   auto expectedBoundsY = std::make_shared<const RectangleBounds>(1, 4);
0520   auto [planeYMerged, reversedY] =
0521       plane->mergedWith(*planeY, Acts::AxisDirection::AxisY, *logger);
0522   BOOST_REQUIRE_NE(planeYMerged, nullptr);
0523   BOOST_CHECK(!reversedY);
0524   BOOST_CHECK_EQUAL(planeYMerged->bounds(), *expectedBoundsY);
0525   BOOST_CHECK_EQUAL(planeYMerged->center(gctx), base * Vector3::UnitY() * 2);
0526 
0527   auto [planeXMerged2, reversedX2] =
0528       planeX->mergedWith(*plane, Acts::AxisDirection::AxisX, *logger);
0529   BOOST_REQUIRE_NE(planeXMerged2, nullptr);
0530   BOOST_CHECK(planeXMerged->bounds() == planeXMerged2->bounds());
0531   BOOST_CHECK(reversedX2);
0532   BOOST_CHECK_EQUAL(planeXMerged2->bounds(), *expectedBoundsX);
0533   BOOST_CHECK_EQUAL(planeXMerged2->center(gctx), base * Vector3::UnitX() * 1);
0534 
0535   auto [planeYMerged2, reversedY2] =
0536       planeY->mergedWith(*plane, Acts::AxisDirection::AxisY, *logger);
0537   BOOST_REQUIRE_NE(planeYMerged2, nullptr);
0538   BOOST_CHECK(planeYMerged->bounds() == planeYMerged2->bounds());
0539   BOOST_CHECK(reversedY2);
0540   BOOST_CHECK_EQUAL(planeYMerged2->bounds(), *expectedBoundsY);
0541   BOOST_CHECK_EQUAL(planeYMerged2->center(gctx), base * Vector3::UnitY() * 2);
0542 }
0543 
0544 BOOST_AUTO_TEST_SUITE_END()
0545 BOOST_AUTO_TEST_SUITE_END()
0546 
0547 }  // namespace Acts::Test