Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:12:49

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