File indexing completed on 2026-10-11 08:09:15
0001
0002
0003
0004
0005
0006
0007
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
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
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,
0086 0,
0087 ipos.z() +
0088 i * std::sqrt(3.) * strawRadius};
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
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.))) *
0112 Transform3(AngleAxis3(35._degree, Vector3::UnitZ())) *
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
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
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
0179
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))) *
0187 Transform3(AngleAxis3(35._degree, Vector3::UnitZ())) *
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
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
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
0260 BOOST_CHECK(target.isPortalTarget());
0261
0262
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();
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 }