Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-07-31 08:18:27

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/unit_test.hpp>
0010 
0011 #include "Acts/Definitions/Algebra.hpp"
0012 #include "Acts/Definitions/Units.hpp"
0013 #include "Acts/Geometry/Blueprint.hpp"
0014 #include "Acts/Geometry/ContainerBlueprintNode.hpp"
0015 #include "Acts/Geometry/CuboidVolumeBounds.hpp"
0016 #include "Acts/Geometry/GeometryContext.hpp"
0017 #include "Acts/Geometry/Portal.hpp"
0018 #include "Acts/Geometry/TrackingGeometry.hpp"
0019 #include "Acts/Geometry/TrackingVolume.hpp"
0020 #include "Acts/Surfaces/BoundaryTolerance.hpp"
0021 #include "Acts/Surfaces/Surface.hpp"
0022 #include "Acts/Surfaces/SurfaceError.hpp"
0023 #include "Acts/Utilities/AxisDefinitions.hpp"
0024 #include "Acts/Utilities/Logger.hpp"
0025 #include "ActsTests/CommonHelpers/CubicTrackingGeometry.hpp"
0026 
0027 #include <memory>
0028 #include <optional>
0029 
0030 using namespace Acts;
0031 using namespace Acts::UnitLiterals;
0032 
0033 namespace ActsTests {
0034 
0035 namespace {
0036 
0037 auto logger = getDefaultLogger("UnitTests", Logging::INFO);
0038 auto gctx = GeometryContext::dangerouslyDefaultConstruct();
0039 
0040 const TrackingVolume* findVolumeByName(const TrackingGeometry& trackingGeometry,
0041                                        const std::string& name) {
0042   const TrackingVolume* result = nullptr;
0043   trackingGeometry.apply([&](const TrackingVolume& volume) {
0044     if (volume.volumeName() == name) {
0045       result = &volume;
0046     }
0047   });
0048   return result;
0049 }
0050 
0051 }  // namespace
0052 
0053 BOOST_AUTO_TEST_SUITE(GeometrySuite)
0054 
0055 // Two cuboid volumes stacked in x with a shared (fused) portal at x=0. A
0056 // position on the shared portal surface is ambiguous between the two
0057 // volumes; the direction resolves the ambiguity.
0058 BOOST_AUTO_TEST_CASE(BoundaryVolumeResolutionGen3) {
0059   Blueprint::Config cfg;
0060   cfg.envelope[AxisDirection::AxisX] = {20_mm, 20_mm};
0061   cfg.envelope[AxisDirection::AxisY] = {20_mm, 20_mm};
0062   cfg.envelope[AxisDirection::AxisZ] = {20_mm, 20_mm};
0063   Blueprint root{cfg};
0064 
0065   root.addCuboidContainer("Stack", AxisDirection::AxisX, [&](auto& stack) {
0066     stack.addStaticVolume(
0067         Transform3{Translation3{Vector3{-100_mm, 0, 0}}},
0068         std::make_shared<CuboidVolumeBounds>(100_mm, 100_mm, 100_mm),
0069         "VolumeA");
0070     stack.addStaticVolume(
0071         Transform3{Translation3{Vector3{100_mm, 0, 0}}},
0072         std::make_shared<CuboidVolumeBounds>(100_mm, 100_mm, 100_mm),
0073         "VolumeB");
0074   });
0075 
0076   std::unique_ptr<TrackingGeometry> trackingGeometry =
0077       root.construct({}, gctx, *logger);
0078   BOOST_REQUIRE(trackingGeometry != nullptr);
0079   BOOST_CHECK(trackingGeometry->geometryVersion() ==
0080               TrackingGeometry::GeometryVersion::Gen3);
0081 
0082   const TrackingVolume* volumeA =
0083       findVolumeByName(*trackingGeometry, "VolumeA");
0084   const TrackingVolume* volumeB =
0085       findVolumeByName(*trackingGeometry, "VolumeB");
0086   BOOST_REQUIRE(volumeA != nullptr);
0087   BOOST_REQUIRE(volumeB != nullptr);
0088 
0089   // The two volumes touch at x=0
0090   const Vector3 onBoundary{0, 0, 0};
0091 
0092   // Find the portal shared between the two volumes at the touching faces.
0093   // Note that the merged lateral portals of the stack are also shared
0094   // between the two volumes, so the position is needed to disambiguate.
0095   const Portal* sharedPortal = nullptr;
0096   for (const Portal& pa : volumeA->portals()) {
0097     for (const Portal& pb : volumeB->portals()) {
0098       if (&pa == &pb &&
0099           pa.surface().isOnSurface(gctx, onBoundary, Vector3::UnitX(),
0100                                    BoundaryTolerance::None())) {
0101         sharedPortal = &pa;
0102       }
0103     }
0104   }
0105   BOOST_REQUIRE(sharedPortal != nullptr);
0106 
0107   const Surface& portalSurface = sharedPortal->surface();
0108 
0109   // The direction-aware lookup resolves the correct side of the boundary
0110   auto forward = trackingGeometry->resolveLowestTrackingVolume(
0111       gctx, onBoundary, Vector3::UnitX(), &portalSurface);
0112   BOOST_REQUIRE(forward.ok());
0113   BOOST_CHECK_EQUAL(*forward, volumeB);
0114 
0115   auto backward = trackingGeometry->resolveLowestTrackingVolume(
0116       gctx, onBoundary, -Vector3::UnitX(), &portalSurface);
0117   BOOST_REQUIRE(backward.ok());
0118   BOOST_CHECK_EQUAL(*backward, volumeA);
0119 
0120   // Without direction and surface hint the lookup is position based and
0121   // returns one of the two adjacent volumes
0122   auto plain = trackingGeometry->resolveLowestTrackingVolume(gctx, onBoundary);
0123   BOOST_REQUIRE(plain.ok());
0124   BOOST_CHECK(*plain == volumeA || *plain == volumeB);
0125 }
0126 
0127 // Two glued Gen1 cuboid volumes sharing a boundary surface at x=0
0128 BOOST_AUTO_TEST_CASE(BoundaryVolumeResolutionGen1) {
0129   CubicTrackingGeometry geometryBuilder{gctx};
0130   std::shared_ptr<const TrackingGeometry> trackingGeometry = geometryBuilder();
0131   BOOST_REQUIRE(trackingGeometry != nullptr);
0132   BOOST_CHECK(trackingGeometry->geometryVersion() ==
0133               TrackingGeometry::GeometryVersion::Gen1);
0134 
0135   const TrackingVolume* volume1 =
0136       trackingGeometry->resolveLowestTrackingVolume(gctx, Vector3{-1.5_m, 0, 0})
0137           .value();
0138   const TrackingVolume* volume2 =
0139       trackingGeometry->resolveLowestTrackingVolume(gctx, Vector3{1.5_m, 0, 0})
0140           .value();
0141   BOOST_REQUIRE(volume1 != nullptr);
0142   BOOST_REQUIRE(volume2 != nullptr);
0143   BOOST_CHECK_EQUAL(volume1->volumeName(), "Volume 1");
0144   BOOST_CHECK_EQUAL(volume2->volumeName(), "Volume 2");
0145 
0146   // Find the glued boundary surface between the two volumes
0147   const Vector3 onBoundary{0, 0, 0};
0148   auto findBoundarySurface = [&](const TrackingVolume& volume,
0149                                  const Vector3& position) -> const Surface* {
0150     for (const auto& boundary : volume.boundarySurfaces()) {
0151       const Surface& surface = boundary->surfaceRepresentation();
0152       if (surface.isOnSurface(gctx, position, Vector3::UnitX(),
0153                               BoundaryTolerance::None())) {
0154         return &surface;
0155       }
0156     }
0157     return nullptr;
0158   };
0159 
0160   const Surface* boundarySurface = findBoundarySurface(*volume1, onBoundary);
0161   BOOST_REQUIRE(boundarySurface != nullptr);
0162 
0163   // The direction-aware lookup resolves the correct side of the boundary
0164   auto forward = trackingGeometry->resolveLowestTrackingVolume(
0165       gctx, onBoundary, Vector3::UnitX(), boundarySurface);
0166   BOOST_REQUIRE(forward.ok());
0167   BOOST_CHECK_EQUAL(*forward, volume2);
0168 
0169   auto backward = trackingGeometry->resolveLowestTrackingVolume(
0170       gctx, onBoundary, -Vector3::UnitX(), boundarySurface);
0171   BOOST_REQUIRE(backward.ok());
0172   BOOST_CHECK_EQUAL(*backward, volume1);
0173 
0174   // Outer boundary of volume 1 with nothing attached on the far side: this
0175   // is the end of the world
0176   const Vector3 onOuterBoundary{-3_m, 0, 0};
0177   const Surface* outerSurface = findBoundarySurface(*volume1, onOuterBoundary);
0178   BOOST_REQUIRE(outerSurface != nullptr);
0179 
0180   auto outward = trackingGeometry->resolveLowestTrackingVolume(
0181       gctx, onOuterBoundary, -Vector3::UnitX(), outerSurface);
0182   BOOST_REQUIRE(outward.ok());
0183   BOOST_CHECK_EQUAL(*outward, nullptr);
0184 
0185   // Pointing back inside stays in the volume
0186   auto inward = trackingGeometry->resolveLowestTrackingVolume(
0187       gctx, onOuterBoundary, Vector3::UnitX(), outerSurface);
0188   BOOST_REQUIRE(inward.ok());
0189   BOOST_CHECK_EQUAL(*inward, volume1);
0190 
0191   // A position slightly off the boundary is resolved through it within the
0192   // given tolerance
0193   const Vector3 nearBoundary{0.01_mm, 0, 0};
0194   auto nearResult = trackingGeometry->resolveLowestTrackingVolume(
0195       gctx, nearBoundary, -Vector3::UnitX(), boundarySurface, 0.1_mm);
0196   BOOST_REQUIRE(nearResult.ok());
0197   BOOST_CHECK_EQUAL(*nearResult, volume1);
0198 }
0199 
0200 // A position that is on the plane of a boundary surface of the resolved
0201 // volume, but outside of its bounds, cannot be resolved through the boundary
0202 BOOST_AUTO_TEST_CASE(BoundaryVolumeResolutionOffBounds) {
0203   CubicTrackingGeometry geometryBuilder{gctx};
0204   std::shared_ptr<const TrackingGeometry> trackingGeometry = geometryBuilder();
0205   BOOST_REQUIRE(trackingGeometry != nullptr);
0206 
0207   const TrackingVolume* volume1 =
0208       trackingGeometry->resolveLowestTrackingVolume(gctx, Vector3{-1.5_m, 0, 0})
0209           .value();
0210   BOOST_REQUIRE(volume1 != nullptr);
0211 
0212   // The glued boundary surface between the two volumes at x=0 spans
0213   // |y| < 0.5 m and |z| < 0.5 m
0214   const Surface* boundarySurface = nullptr;
0215   for (const auto& boundary : volume1->boundarySurfaces()) {
0216     const Surface& surface = boundary->surfaceRepresentation();
0217     if (surface.isOnSurface(gctx, Vector3::Zero(), Vector3::UnitX(),
0218                             BoundaryTolerance::None())) {
0219       boundarySurface = &surface;
0220     }
0221   }
0222   BOOST_REQUIRE(boundarySurface != nullptr);
0223 
0224   // Grazing the volume edge: within the lookup tolerance of the volume, but
0225   // outside of the boundary surface bounds
0226   const Vector3 offBounds{0, 0.5_m + 0.5_mm, 0};
0227   const double tolerance = 1_mm;
0228   BOOST_REQUIRE(trackingGeometry
0229                     ->resolveLowestTrackingVolume(gctx, offBounds, std::nullopt,
0230                                                   nullptr, tolerance)
0231                     .value() != nullptr);
0232   BOOST_REQUIRE(!boundarySurface->isOnSurface(
0233       gctx, offBounds, Vector3::UnitX(), BoundaryTolerance::None(), tolerance));
0234 
0235   auto result = trackingGeometry->resolveLowestTrackingVolume(
0236       gctx, offBounds, Vector3::UnitX(), boundarySurface, tolerance);
0237   BOOST_CHECK(!result.ok());
0238   BOOST_CHECK(result.error() == SurfaceError::GlobalPositionNotOnSurface);
0239 }
0240 
0241 BOOST_AUTO_TEST_SUITE_END()
0242 
0243 }  // namespace ActsTests