Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-07-15 08:13:41

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