Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-10-17 08:00:37

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/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 // Parameters for the geometry
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 // Intersection finding to get the
0054 // region of interest for seeding
0055 class NoFieldIntersectionFinder {
0056  public:
0057   double m_tol = 1e-4;
0058 
0059   std::vector<const Surface*> m_surfaces;
0060 
0061   // Find the intersections along the path
0062   // and return them in the order of the path
0063   // length
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     // Intersect the surfaces
0072     for (auto& surface : m_surfaces) {
0073       // Get the intersection
0074       auto sMultiIntersection =
0075           surface->intersect(geoCtx, position, direction,
0076                              BoundaryTolerance::AbsoluteEuclidean(m_tol));
0077 
0078       // Take the closest
0079       Intersection3D closestForward = sMultiIntersection.closestForward();
0080 
0081       // Store if the intersection is reachable
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 // A simple path width provider to set
0098 // the grid lookup boundaries around the
0099 // intersection point
0100 class PathWidthProvider {
0101  public:
0102   std::pair<double, double> width;
0103 
0104   std::pair<double, double> operator()(
0105       const GeometryContext& /*gctx*/,
0106       const GeometryIdentifier& /*geoId*/) const {
0107     return width;
0108   }
0109 };
0110 
0111 // Estimator of the particle's energy,
0112 // vertex, momentum direction at the IP
0113 // and the direction at the first hit
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 // Construct grid with the source links
0144 struct ConstructSourceLinkGrid {
0145   SourceLinkSurfaceAccessor m_surfaceAccessor;
0146 
0147   std::unordered_map<GeometryIdentifier, Grid> construct(
0148       std::vector<SourceLink> sourceLinks) {
0149     // Lookup table for each layer
0150     std::unordered_map<GeometryIdentifier, Grid> lookupTable;
0151 
0152     // Construct a binned grid for each layer
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     // Fill the grid with source links
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 // Construct a simple telescope detector
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   // Create a bunch of surface bounds
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   // Create a bunch of surfaces
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   // Create a bunch of volume bounds
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   // Create a bunch of volumes
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   // Connect the volumes
0254   auto portalContainer = Experimental::detail::CuboidalDetectorHelper::connect(
0255       gctx, volumes, AxisDirection::AxisX, {}, Logging::INFO);
0256 
0257   // Make sure that the geometry ids are
0258   // independent of the potential Id generation
0259   // changes
0260   int id = 1;
0261 
0262   // Volume ids: 1-3
0263   for (auto& volume : volumes) {
0264     volume->assignGeometryId(GeometryIdentifier(id));
0265     id++;
0266   }
0267   // Intervolume portal ids: 6,7,10,11
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   // Surface ids
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   // Create detector
0327   auto detector = constructTelescopeDetector();
0328 
0329   // Create source links
0330   auto sourceLinks = createSourceLinks(gctx, *detector);
0331 
0332   // Prepare the PathSeeder
0333   auto pathSeederCfg = PathSeeder::Config();
0334 
0335   // Grid to bin the source links
0336   SurfaceAccessor surfaceAccessor{*detector};
0337   auto sourceLinkGridConstructor = ConstructSourceLinkGrid();
0338   sourceLinkGridConstructor.m_surfaceAccessor
0339       .connect<&SurfaceAccessor::operator()>(&surfaceAccessor);
0340 
0341   // Create the grid
0342   std::unordered_map<GeometryIdentifier, Grid> sourceLinkGrid =
0343       sourceLinkGridConstructor.construct(sourceLinks);
0344 
0345   // Estimator of the IP and first hit
0346   // parameters of the track
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   // Intersection finder
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   // Path width provider
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   // Create the PathSeeder
0374   PathSeeder pathSeeder(pathSeederCfg);
0375 
0376   // Get the seeds
0377   pathWidthProvider.width = {1e-3, 1e-3};
0378 
0379   std::vector<PathSeeder::PathSeed> seeds;
0380 
0381   // SeedTreeContainer seeds;
0382   pathSeeder.findSeeds(gctx, sourceLinkGrid, seeds);
0383 
0384   // Check the seeds
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 }  // namespace ActsTests