Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-10-11 08:09:15

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/Direction.hpp"
0013 #include "Acts/Definitions/Units.hpp"
0014 #include "Acts/Geometry/Blueprint.hpp"
0015 #include "Acts/Geometry/BlueprintOptions.hpp"
0016 #include "Acts/Geometry/CuboidPortalShell.hpp"
0017 #include "Acts/Geometry/CuboidVolumeBounds.hpp"
0018 #include "Acts/Geometry/GeometryContext.hpp"
0019 #include "Acts/Geometry/MultiWireVolumeBuilder.hpp"
0020 #include "Acts/Geometry/StaticBlueprintNode.hpp"
0021 #include "Acts/Geometry/TrackingGeometry.hpp"
0022 #include "Acts/Geometry/TrackingVolume.hpp"
0023 #include "Acts/Navigation/INavigationPolicy.hpp"
0024 #include "Acts/Navigation/NavigationStream.hpp"
0025 #include "Acts/Propagator/NavigationTarget.hpp"
0026 #include "Acts/Propagator/Navigator.hpp"
0027 #include "Acts/Surfaces/LineBounds.hpp"
0028 #include "Acts/Surfaces/PlaneSurface.hpp"
0029 #include "Acts/Surfaces/RectangleBounds.hpp"
0030 #include "Acts/Surfaces/StrawSurface.hpp"
0031 #include "Acts/Surfaces/Surface.hpp"
0032 #include "Acts/Utilities/Intersection.hpp"
0033 #include "Acts/Utilities/Logger.hpp"
0034 #include "Acts/Utilities/StringHelpers.hpp"
0035 #include "Acts/Utilities/TransformHelpers.hpp"
0036 #include "Acts/Utilities/UnitVectors.hpp"
0037 #include "Acts/Visualization/GeometryView3D.hpp"
0038 #include "Acts/Visualization/ObjVisualization3D.hpp"
0039 
0040 #include <cmath>
0041 #include <memory>
0042 #include <numbers>
0043 #include <vector>
0044 
0045 using namespace Acts;
0046 using namespace Acts::UnitLiterals;
0047 
0048 auto tContext = GeometryContext::dangerouslyDefaultConstruct();
0049 
0050 namespace ActsTests {
0051 
0052 BOOST_AUTO_TEST_SUITE(NavigationSuite)
0053 ACTS_LOCAL_LOGGER(getDefaultLogger("MultiWireNavigationTests",
0054                                    Logging::Level::VERBOSE));
0055 
0056 // advance pos to the intersection with the current target surface
0057 void step(const GeometryContext& geoCtx, Vector3& pos, const Vector3& dir,
0058           const NavigationTarget& target) {
0059   auto isect = target.surface()
0060                    .intersect(geoCtx, pos, dir, target.boundaryTolerance())
0061                    .closest();
0062   pos += isect.pathLength() * dir;
0063 }
0064 
0065 // build a grid of staggered straw surfaces for the first test
0066 void generateStrawSurfaces(const CuboidVolumeBounds& volBounds,
0067                            const Transform3& localToGlobal,
0068                            std::vector<std::shared_ptr<Surface>>& out,
0069                            ObjVisualization3D& vis) {
0070   constexpr double strawRadius = 5._cm;
0071   const double halfX = volBounds.get(CuboidVolumeBounds::eHalfLengthX);
0072   const double halfY = volBounds.get(CuboidVolumeBounds::eHalfLengthY);
0073   const double halfZ = volBounds.get(CuboidVolumeBounds::eHalfLengthZ);
0074 
0075   const auto nLayers = static_cast<std::size_t>(
0076       std::floor(2 * halfZ / (std::sqrt(3.) * strawRadius)));
0077   const auto nStraws =
0078       static_cast<std::size_t>(std::floor(((halfX) / strawRadius)));
0079   Vector3 ipos = {-halfX + strawRadius, -0., -halfZ + strawRadius};
0080   auto strawBounds = std::make_shared<LineBounds>(strawRadius, halfY - 0.5_mm);
0081 
0082   for (std::size_t i = 0; i < nLayers; i++) {
0083     for (std::size_t j = 0; j < nStraws; j++) {
0084       const Vector3 localPos{
0085           ipos.x() + (2 * j + (i) % 2) * strawRadius,  // x: across the layer
0086           0,                                           // y: layer-to-layer
0087           ipos.z() +
0088               i * std::sqrt(3.) * strawRadius};  // z: along the wire (centered)
0089       Transform3 tubetrans =
0090           Transform3(Translation3(localPos)) *
0091           Transform3(AngleAxis3{90._degree, Vector3::UnitX()});
0092       Transform3 trans = localToGlobal * tubetrans;
0093       auto newStraw = out.emplace_back(
0094           Surface::makeShared<StrawSurface>(trans, strawBounds));
0095       newStraw->assignIsSensitive(true);
0096       newStraw->assignGeometryId(
0097           GeometryIdentifier{}.withLayer(i + 1).withSensitive(j + 1));
0098       GeometryView3D::drawSurface(vis, *newStraw, tContext,
0099                                   Transform3::Identity());
0100     }
0101   }
0102 }
0103 
0104 // Test 1: Candidates initialization test inside a multiwire volume
0105 BOOST_AUTO_TEST_CASE(MultiLayer_NavigationPolicy) {
0106   std::vector<std::shared_ptr<Surface>> strawSurfaces{};
0107   ObjVisualization3D visualHelper{};
0108 
0109   auto volBounds = std::make_shared<CuboidVolumeBounds>(0.925_m, 1.2_m, 0.14_m);
0110   const Transform3 volTrans =
0111       Transform3(Translation3(Vector3(300., -150., 500.))) *  // translation
0112       Transform3(AngleAxis3(35._degree, Vector3::UnitZ())) *  // rotation
0113       Transform3(AngleAxis3(20._degree, Vector3::UnitX()));
0114 
0115   generateStrawSurfaces(*volBounds, volTrans, strawSurfaces, visualHelper);
0116 
0117   MultiWireVolumeBuilder::Config mwCfg;
0118   mwCfg.name = "MultiWireVolume";
0119   mwCfg.mlSurfaces = strawSurfaces;
0120   mwCfg.binning = {{AxisDirection::AxisX, 0u}, {AxisDirection::AxisZ, 0u}};
0121   mwCfg.shiftDirection = AxisDirection::AxisX;
0122   mwCfg.bounds = volBounds;
0123   mwCfg.transform = volTrans;
0124 
0125   MultiWireVolumeBuilder mwBuilder(mwCfg);
0126   std::unique_ptr<TrackingVolume> volume = mwBuilder.buildVolume();
0127 
0128   GeometryView3D::drawVolume(visualHelper, *volume, tContext,
0129                              Transform3::Identity());
0130 
0131   visualHelper.write("MultiLayerNavigation_test1.obj");
0132 
0133   SingleCuboidPortalShell portalShell{tContext, *volume};
0134   portalShell.applyToVolume();
0135 
0136   BOOST_CHECK(volume->volumes().empty());
0137   // 18 straws per layer and in total 3 layers
0138   BOOST_CHECK_EQUAL(volume->surfaces().size(), 54u);
0139   BOOST_CHECK_EQUAL(volume->portals().size(), 6u);
0140 
0141   NavigationStream main;
0142   AppendOnlyNavigationStream stream{main};
0143   Vector3 startPos = {0., 0., -199.};
0144   Vector3 startDir = {0., 0., 1.};
0145   NavigationArguments args{startPos, startDir};
0146 
0147   auto navFactory = mwBuilder.createNavigationPolicyFactory(tContext);
0148   volume->setNavigationPolicy(navFactory->build(tContext, *volume, logger()));
0149 
0150   NavigationPolicyStateManager stateManager;
0151   volume->navigationPolicy()->createState(tContext, args, stateManager,
0152                                           logger());
0153   auto policyState = stateManager.currentState();
0154   volume->initializeNavigationCandidates(tContext, args, policyState, stream,
0155                                          logger());
0156 
0157   BOOST_CHECK_EQUAL(main.candidates().size(), 9u);
0158 
0159   auto it = std::unique(main.candidates().begin(), main.candidates().end(),
0160                         [](const auto& lhs, const auto& rhs) {
0161                           return lhs.surface() == rhs.surface();
0162                         });
0163   BOOST_CHECK(it == main.candidates().end());
0164   startDir = makeDirectionFromPhiTheta(0._degree, 45._degree);
0165   args.direction = startDir;
0166 
0167   // clear the candidates and re initialize with new arguments
0168   main.reset();
0169   NavigationPolicyStateManager stateManager2;
0170   volume->navigationPolicy()->createState(tContext, args, stateManager2,
0171                                           logger());
0172   auto policyState2 = stateManager2.currentState();
0173   volume->initializeNavigationCandidates(tContext, args, policyState2, stream,
0174                                          logger());
0175   BOOST_CHECK_EQUAL(main.candidates().size(), 9u);
0176 }
0177 
0178 // // Test 2: navigate a ring of impact points around each straw's center and
0179 // check if target is reached
0180 BOOST_AUTO_TEST_CASE(MultiLayerNavigation_TargetSurfaces) {
0181   std::vector<std::shared_ptr<Surface>> straws{};
0182   ObjVisualization3D vis{};
0183 
0184   auto volBounds = std::make_shared<CuboidVolumeBounds>(0.925_m, 1.2_m, 0.14_m);
0185   const Transform3 volTrans =
0186       Transform3(Translation3(Vector3(0.3_m, -0.5_m, 0.5_m))) *  // translation
0187       Transform3(AngleAxis3(35._degree, Vector3::UnitZ())) *     // rotation
0188       Transform3(AngleAxis3(20._degree, Vector3::UnitX()));
0189 
0190   generateStrawSurfaces(*volBounds, volTrans, straws, vis);
0191 
0192   Blueprint::Config bpCfg{};
0193   bpCfg.envelope[AxisDirection::AxisX] = {20_mm, 20_mm};
0194   bpCfg.envelope[AxisDirection::AxisY] = {20_mm, 20_mm};
0195   bpCfg.envelope[AxisDirection::AxisZ] = {20_mm, 20_mm};
0196   Blueprint root{bpCfg};
0197 
0198   auto& container = root.addStaticVolume(
0199       Transform3::Identity(),
0200       std::make_shared<CuboidVolumeBounds>(25._m, 25._m, 25._m), "world");
0201 
0202   // start plane: launch tracks from the origin toward each straw.
0203   const Transform3 surfaceTrans =
0204       Transform3(Translation3(Vector3(0., 0., -1._m)));
0205 
0206   auto startSurface = Surface::makeShared<PlaneSurface>(
0207       surfaceTrans, std::make_shared<RectangleBounds>(10._m, 10._m));
0208 
0209   GeometryView3D::drawSurface(vis, *startSurface, tContext,
0210                               Transform3::Identity());
0211 
0212   {
0213     MultiWireVolumeBuilder::Config mwCfg{};
0214     mwCfg.name = "MultiWireVolume";
0215     mwCfg.mlSurfaces = straws;
0216     mwCfg.binning = {{AxisDirection::AxisX, 0u}, {AxisDirection::AxisZ, 0u}};
0217     mwCfg.shiftDirection = AxisDirection::AxisX;
0218     mwCfg.bounds = volBounds;
0219     mwCfg.transform = volTrans;
0220 
0221     MultiWireVolumeBuilder mwBuilder{mwCfg};
0222     auto volume = mwBuilder.buildVolume();
0223     container.addStaticVolume(std::move(volume))
0224         .setNavigationPolicyFactory(
0225             mwBuilder.createNavigationPolicyFactory(tContext));
0226   }
0227 
0228   std::shared_ptr<const TrackingGeometry> trkGeo =
0229       root.construct(BlueprintOptions{}, tContext, logger());
0230   BOOST_REQUIRE(trkGeo != nullptr);
0231 
0232   const auto* mwVolume = trkGeo->findVolumeByName("MultiWireVolume");
0233   BOOST_REQUIRE(mwVolume != nullptr);
0234   BOOST_CHECK(mwVolume->geometryId() != GeometryIdentifier{});
0235 
0236   trkGeo->visitVolumes([&](const TrackingVolume* vol) {
0237     GeometryView3D::drawVolume(vis, *vol, tContext, Transform3::Identity());
0238   });
0239 
0240   vis.write("MultiLayerNavTest_trkGeo.obj");
0241 
0242   // navigator configuration
0243   Navigator::Config navCfg;
0244   navCfg.trackingGeometry = std::move(trkGeo);
0245   navCfg.resolveSensitive = true;
0246   navCfg.resolveMaterial = true;
0247   navCfg.resolvePassive = false;
0248   Navigator navigator{navCfg, getDefaultLogger("MWNav", Logging::VERBOSE)};
0249   Vector3 start = {0., 0., -1._m};
0250   Vector3 dir = Vector3::UnitZ();
0251   Navigator::Options options{tContext};
0252   Navigator::State state = navigator.makeState(options);
0253   NavigationTarget target = navigator.nextTarget(state, start, dir);
0254   NavigatorInitializeArguments initArgs;
0255   initArgs.position = start;
0256   initArgs.direction = dir;
0257   BOOST_CHECK(navigator.initialize(state, initArgs).ok());
0258   BOOST_CHECK(!target.isNone());
0259   // expect to be a boundary
0260   BOOST_CHECK(target.isPortalTarget());
0261 
0262   // ring of impact points around each straw center
0263   constexpr std::size_t nSamples = 5;
0264   constexpr double rFrac = 0.5;
0265   constexpr double tubeR = 5._cm;
0266   for (std::size_t s = 0; s < straws.size(); ++s) {
0267     const auto& straw = straws.at(s);
0268     const Transform3 l2g = straw->localToGlobalTransform(tContext);
0269 
0270     for (std::size_t k = 0; k < nSamples; ++k) {
0271       const double phi = 2. * std::numbers::pi * k / nSamples;
0272       const Vector3 locPos =
0273           rFrac * tubeR * makeDirectionFromPhiTheta(phi, 90._degree);
0274       const Vector3 impact = l2g * locPos;
0275       ACTS_VERBOSE(__LINE__
0276                    << " - Start to target surface: " << straw->geometryId()
0277                    << ", impact position " << toString(impact) << ", local: "
0278                    << toString(locPos) << " phi: " << (phi / 1._degree));
0279       Vector3 pos = start;
0280       const Vector3 direction = (impact - pos).normalized();
0281 
0282       Navigator::State st = navigator.makeState(options);
0283       NavigatorInitializeArguments ia;
0284       ia.position = pos;
0285       ia.direction = direction;
0286       ia.startSurface =
0287           startSurface.get();  // no start surface; enter from outside
0288       if (!navigator.initialize(st, ia).ok()) {
0289         BOOST_CHECK(false);
0290         continue;
0291       }
0292 
0293       NavigationTarget tgt = navigator.nextTarget(st, pos, direction);
0294       ACTS_VERBOSE(__LINE__ << " - The start target is " << tgt);
0295       while (!tgt.isNone() &&
0296              tgt.surface().geometryId() != straw->geometryId()) {
0297         step(tContext, pos, direction, tgt);
0298         navigator.handleSurfaceReached(st, pos, direction, tgt.surface());
0299         tgt = navigator.nextTarget(st, pos, direction);
0300         ACTS_VERBOSE(__LINE__ << " - Continue to next target " << tgt);
0301       }
0302       ACTS_VERBOSE(__LINE__ << " - Final target is " << tgt);
0303       BOOST_CHECK(tgt.isSurfaceTarget());
0304 
0305       BOOST_CHECK_EQUAL(&tgt.surface(), straw.get());
0306     }
0307   }
0308 }
0309 
0310 BOOST_AUTO_TEST_SUITE_END()
0311 
0312 }  // namespace ActsTests