File indexing completed on 2025-07-15 08:13:41
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include <boost/test/unit_test.hpp>
0010
0011 #include "Acts/Detector/Detector.hpp"
0012 #include "Acts/Detector/DetectorVolume.hpp"
0013 #include "Acts/Detector/GeometryIdGenerator.hpp"
0014 #include "Acts/Detector/PortalGenerators.hpp"
0015 #include "Acts/Detector/detail/CuboidalDetectorHelper.hpp"
0016 #include "Acts/EventData/detail/TestSourceLink.hpp"
0017 #include "Acts/Geometry/CuboidVolumeBounds.hpp"
0018 #include "Acts/Geometry/GeometryContext.hpp"
0019 #include "Acts/Navigation/DetectorVolumeFinders.hpp"
0020 #include "Acts/Navigation/InternalNavigation.hpp"
0021 #include "Acts/Seeding/PathSeeder.hpp"
0022 #include "Acts/Surfaces/BoundaryTolerance.hpp"
0023 #include "Acts/Surfaces/PlaneSurface.hpp"
0024 #include "Acts/Surfaces/RectangleBounds.hpp"
0025 #include "Acts/Utilities/Logger.hpp"
0026
0027 #include <numbers>
0028
0029 BOOST_AUTO_TEST_SUITE(PathSeeder)
0030
0031 using namespace Acts;
0032 using namespace Acts::UnitLiterals;
0033
0034 using Axis = Acts::Axis<AxisType::Equidistant, AxisBoundaryType::Open>;
0035 using Grid = Acts::Grid<std::vector<SourceLink>, Axis, Axis>;
0036
0037 GeometryContext gctx;
0038
0039
0040 const double halfY = 10.;
0041 const double halfZ = 10.;
0042 const double deltaX = 10.;
0043 const double deltaYZ = 1.;
0044
0045 const Vector4 trueVertex(-5., 0., 0., 0);
0046 const std::vector<double> truePhis = {-0.15, -0.1, -0.05, 0, 0.05, 0.1, 0.15};
0047 const double trueTheta = std::numbers::pi / 2.;
0048 const double trueQOverP = 1. / 1._GeV;
0049
0050
0051
0052 class NoFieldIntersectionFinder {
0053 public:
0054 double m_tol = 1e-4;
0055
0056 std::vector<const Surface*> m_surfaces;
0057
0058
0059
0060
0061 std::vector<std::pair<GeometryIdentifier, Vector2>> operator()(
0062 const GeometryContext& geoCtx,
0063 const BoundTrackParameters& trackParameters) const {
0064 Vector3 position = trackParameters.position(geoCtx);
0065 Vector3 direction = trackParameters.direction();
0066
0067 std::vector<std::pair<GeometryIdentifier, Vector2>> sIntersections;
0068
0069 for (auto& surface : m_surfaces) {
0070
0071 auto sMultiIntersection = surface->intersect(
0072 geoCtx, position, direction,
0073 BoundaryTolerance::AbsoluteCartesian(m_tol, m_tol));
0074
0075
0076 auto closestForward = sMultiIntersection.closestForward();
0077
0078
0079 if (closestForward.status() == IntersectionStatus::reachable &&
0080 closestForward.pathLength() > 0.0) {
0081 sIntersections.push_back(
0082 {closestForward.object()->geometryId(),
0083 surface
0084 ->globalToLocal(geoCtx, closestForward.position(),
0085 Vector3{0, 1, 0})
0086 .value()});
0087 continue;
0088 }
0089 }
0090 return sIntersections;
0091 }
0092 };
0093
0094
0095
0096
0097 class PathWidthProvider {
0098 public:
0099 std::pair<double, double> width;
0100
0101 std::pair<double, double> operator()(
0102 const GeometryContext& ,
0103 const GeometryIdentifier& ) const {
0104 return width;
0105 }
0106 };
0107
0108
0109
0110
0111 class TrackEstimator {
0112 public:
0113 Vector3 m_ip;
0114 SourceLinkSurfaceAccessor m_surfaceAccessor;
0115
0116 std::pair<BoundTrackParameters, BoundTrackParameters> operator()(
0117 const GeometryContext& geoCtx, const SourceLink& pivot) const {
0118 auto testSourceLink = pivot.get<detail::Test::TestSourceLink>();
0119 Vector3 pivot3 = m_surfaceAccessor(pivot)->localToGlobal(
0120 geoCtx, testSourceLink.parameters, Vector3{0, 1, 0});
0121
0122 Vector3 direction = (pivot3 - m_ip).normalized();
0123
0124 Vector4 ip = {m_ip.x(), m_ip.y(), m_ip.z(), 0};
0125 double qOverP = 1_e / 1._GeV;
0126 double phi = Acts::VectorHelpers::phi(direction);
0127 double theta = Acts::VectorHelpers::theta(direction);
0128 ParticleHypothesis particle = ParticleHypothesis::electron();
0129
0130 BoundTrackParameters ipParams = BoundTrackParameters::createCurvilinear(
0131 ip, phi, theta, qOverP, std::nullopt, particle);
0132 BoundTrackParameters firstLayerParams =
0133 BoundTrackParameters::createCurvilinear(ip, phi, theta, qOverP,
0134 std::nullopt, particle);
0135
0136 return {ipParams, firstLayerParams};
0137 }
0138 };
0139
0140
0141 struct ConstructSourceLinkGrid {
0142 SourceLinkSurfaceAccessor m_surfaceAccessor;
0143
0144 std::unordered_map<GeometryIdentifier, Grid> construct(
0145 std::vector<SourceLink> sourceLinks) {
0146
0147 std::unordered_map<GeometryIdentifier, Grid> lookupTable;
0148
0149
0150 for (int i : {14, 15, 16, 17}) {
0151 Axis xAxis(-halfY, halfY, 100);
0152 Axis yAxis(-halfZ, halfZ, 100);
0153
0154 Grid grid(std::make_tuple(xAxis, yAxis));
0155
0156 auto geoId = GeometryIdentifier().withSensitive(i);
0157
0158 lookupTable.insert({geoId, grid});
0159 }
0160
0161 for (auto& sl : sourceLinks) {
0162 auto tsl = sl.get<detail::Test::TestSourceLink>();
0163 auto id = tsl.m_geometryId;
0164
0165 auto bin = lookupTable.at(id).localBinsFromPosition(tsl.parameters);
0166 lookupTable.at(id).atLocalBins(bin).push_back(sl);
0167 }
0168
0169 return lookupTable;
0170 }
0171 };
0172
0173
0174 std::shared_ptr<Experimental::Detector> constructTelescopeDetector() {
0175 RotationMatrix3 rotation;
0176 double angle = 90_degree;
0177 Vector3 xPos(cos(angle), 0., sin(angle));
0178 Vector3 yPos(0., 1., 0.);
0179 Vector3 zPos(-sin(angle), 0., cos(angle));
0180 rotation.col(0) = xPos;
0181 rotation.col(1) = yPos;
0182 rotation.col(2) = zPos;
0183
0184
0185 auto surf0bounds = std::make_unique<RectangleBounds>(halfY, halfZ);
0186
0187 auto surf1bounds = std::make_unique<RectangleBounds>(halfY, halfZ);
0188
0189 auto surf2bounds = std::make_unique<RectangleBounds>(halfY, halfZ);
0190
0191 auto surf3bounds = std::make_unique<RectangleBounds>(halfY, halfZ);
0192
0193
0194 auto transform0 = Transform3::Identity();
0195 auto surf0 = Surface::makeShared<PlaneSurface>(
0196 transform0 * Transform3(rotation), std::move(surf0bounds));
0197
0198 auto transform1 =
0199 Transform3::Identity() * Translation3(Vector3(2 * deltaX, 0, 0));
0200 auto surf1 = Surface::makeShared<PlaneSurface>(
0201 transform1 * Transform3(rotation), std::move(surf1bounds));
0202
0203 auto transform2 =
0204 Transform3::Identity() * Translation3(Vector3(4 * deltaX, 0, 0));
0205 auto surf2 = Surface::makeShared<PlaneSurface>(
0206 transform2 * Transform3(rotation), std::move(surf2bounds));
0207
0208 auto transform3 =
0209 Transform3::Identity() * Translation3(Vector3(6 * deltaX, 0, 0));
0210 auto surf3 = Surface::makeShared<PlaneSurface>(
0211 transform3 * Transform3(rotation), std::move(surf3bounds));
0212
0213
0214 auto vol0bounds = std::make_unique<CuboidVolumeBounds>(
0215 deltaX, halfY + deltaYZ, halfZ + deltaYZ);
0216
0217 auto vol1bounds = std::make_unique<CuboidVolumeBounds>(
0218 deltaX, halfY + deltaYZ, halfZ + deltaYZ);
0219
0220 auto vol2bounds = std::make_unique<CuboidVolumeBounds>(
0221 deltaX, halfY + deltaYZ, halfZ + deltaYZ);
0222
0223 auto vol3bounds = std::make_unique<CuboidVolumeBounds>(
0224 deltaX, halfY + deltaYZ, halfZ + deltaYZ);
0225
0226
0227 auto vol0 = Experimental::DetectorVolumeFactory::construct(
0228 Experimental::defaultPortalAndSubPortalGenerator(), gctx, "vol0",
0229 transform0, std::move(vol0bounds), {surf0}, {},
0230 Experimental::tryNoVolumes(), Experimental::tryAllPortalsAndSurfaces());
0231
0232 auto vol1 = Experimental::DetectorVolumeFactory::construct(
0233 Experimental::defaultPortalAndSubPortalGenerator(), gctx, "vol1",
0234 transform1, std::move(vol1bounds), {surf1}, {},
0235 Experimental::tryNoVolumes(), Experimental::tryAllPortalsAndSurfaces());
0236
0237 auto vol2 = Experimental::DetectorVolumeFactory::construct(
0238 Experimental::defaultPortalAndSubPortalGenerator(), gctx, "vol2",
0239 transform2, std::move(vol2bounds), {surf2}, {},
0240 Experimental::tryNoVolumes(), Experimental::tryAllPortalsAndSurfaces());
0241
0242 auto vol3 = Experimental::DetectorVolumeFactory::construct(
0243 Experimental::defaultPortalAndSubPortalGenerator(), gctx, "vol3",
0244 transform3, std::move(vol3bounds), {surf3}, {},
0245 Experimental::tryNoVolumes(), Experimental::tryAllPortalsAndSurfaces());
0246
0247 std::vector<std::shared_ptr<Experimental::DetectorVolume>> volumes = {
0248 vol0, vol1, vol2, vol3};
0249
0250
0251 auto portalContainer = Experimental::detail::CuboidalDetectorHelper::connect(
0252 gctx, volumes, AxisDirection::AxisX, {}, Logging::INFO);
0253
0254
0255
0256
0257 int id = 1;
0258
0259
0260 for (auto& volume : volumes) {
0261 volume->assignGeometryId(Acts::GeometryIdentifier(id));
0262 id++;
0263 }
0264
0265 for (auto& volume : volumes) {
0266 for (auto& port : volume->portalPtrs()) {
0267 if (port->surface().geometryId() == Acts::GeometryIdentifier(0)) {
0268 port->surface().assignGeometryId(Acts::GeometryIdentifier(id));
0269 id++;
0270 }
0271 }
0272 }
0273
0274 for (auto& surf : {surf0, surf1, surf2, surf3}) {
0275 auto geoId = GeometryIdentifier().withSensitive(id);
0276 surf->assignGeometryId(geoId);
0277 id++;
0278 }
0279
0280 auto detector = Experimental::Detector::makeShared(
0281 "TelescopeDetector", volumes, Experimental::tryRootVolumes());
0282
0283 return detector;
0284 }
0285
0286 std::vector<SourceLink> createSourceLinks(
0287 const GeometryContext& geoCtx, const Experimental::Detector& detector) {
0288 NoFieldIntersectionFinder intersectionFinder;
0289
0290 for (auto volume : detector.volumes()) {
0291 for (auto surface : volume->surfaces()) {
0292 intersectionFinder.m_surfaces.push_back(surface);
0293 }
0294 }
0295
0296 std::vector<SourceLink> sourceLinks;
0297 for (double phi : truePhis) {
0298 BoundTrackParameters trackParameters =
0299 BoundTrackParameters::createCurvilinear(trueVertex, phi, trueTheta,
0300 trueQOverP, std::nullopt,
0301 ParticleHypothesis::electron());
0302
0303 auto intersections = intersectionFinder(geoCtx, trackParameters);
0304
0305 SquareMatrix2 cov = SquareMatrix2::Identity();
0306
0307 for (auto& [id, refPoint] : intersections) {
0308 detail::Test::TestSourceLink sourceLink(eBoundLoc0, eBoundLoc1, refPoint,
0309 cov, id, id.value());
0310
0311 SourceLink sl{sourceLink};
0312 sourceLinks.push_back(sl);
0313 }
0314 }
0315
0316 return sourceLinks;
0317 }
0318
0319 BOOST_AUTO_TEST_CASE(PathSeederZeroField) {
0320 using SurfaceAccessor =
0321 detail::Test::Experimental::TestSourceLinkSurfaceAccessor;
0322
0323
0324 auto detector = constructTelescopeDetector();
0325
0326
0327 auto sourceLinks = createSourceLinks(gctx, *detector);
0328
0329
0330 auto pathSeederCfg = Acts::PathSeeder::Config();
0331
0332
0333 SurfaceAccessor surfaceAccessor{*detector};
0334 auto sourceLinkGridConstructor = ConstructSourceLinkGrid();
0335 sourceLinkGridConstructor.m_surfaceAccessor
0336 .connect<&SurfaceAccessor::operator()>(&surfaceAccessor);
0337
0338
0339 std::unordered_map<GeometryIdentifier, Grid> sourceLinkGrid =
0340 sourceLinkGridConstructor.construct(sourceLinks);
0341
0342
0343
0344 TrackEstimator trackEstimator;
0345 trackEstimator.m_ip = Vector3(-5., 0., 0.);
0346 trackEstimator.m_surfaceAccessor.connect<&SurfaceAccessor::operator()>(
0347 &surfaceAccessor);
0348 pathSeederCfg.trackEstimator.connect<&TrackEstimator::operator()>(
0349 &trackEstimator);
0350
0351
0352 NoFieldIntersectionFinder intersectionFinder;
0353 for (auto volume : detector->volumes()) {
0354 for (auto surface : volume->surfaces()) {
0355 intersectionFinder.m_surfaces.push_back(surface);
0356 }
0357 }
0358 pathSeederCfg.intersectionFinder
0359 .connect<&NoFieldIntersectionFinder::operator()>(&intersectionFinder);
0360
0361
0362 PathWidthProvider pathWidthProvider;
0363
0364 pathSeederCfg.pathWidthProvider.connect<&PathWidthProvider::operator()>(
0365 &pathWidthProvider);
0366
0367 auto geoId = GeometryIdentifier().withSensitive(14);
0368 pathSeederCfg.refLayerIds.push_back(geoId);
0369
0370
0371 Acts::PathSeeder pathSeeder(pathSeederCfg);
0372
0373
0374 pathWidthProvider.width = {1e-3, 1e-3};
0375
0376 std::vector<Acts::PathSeeder::PathSeed> seeds;
0377
0378
0379 pathSeeder.findSeeds(gctx, sourceLinkGrid, seeds);
0380
0381
0382 BOOST_CHECK_EQUAL(seeds.size(), 7);
0383
0384 for (std::size_t i = 0; i < seeds.size(); i++) {
0385 auto seed = seeds.at(i);
0386 BOOST_CHECK_EQUAL(seed.second.size(), 4);
0387 BOOST_CHECK_EQUAL(seed.first.phi(), truePhis.at(i));
0388 BOOST_CHECK_EQUAL(seed.first.theta(), trueTheta);
0389 BOOST_CHECK_EQUAL(seed.first.qOverP(), trueQOverP);
0390 }
0391 }
0392
0393 BOOST_AUTO_TEST_SUITE_END()