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