|
| 1 | +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. |
| 2 | +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. |
| 3 | +// All rights not expressly granted are reserved. |
| 4 | +// |
| 5 | +// This software is distributed under the terms of the GNU General Public |
| 6 | +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". |
| 7 | +// |
| 8 | +// In applying this license CERN does not waive the privileges and immunities |
| 9 | +// granted to it by virtue of its status as an Intergovernmental Organization |
| 10 | +// or submit itself to any jurisdiction. |
| 11 | + |
| 12 | +/// |
| 13 | +/// \file TrackerACTS.cxx |
| 14 | +/// \brief TRK tracker using ACTS seeding and track finding |
| 15 | +/// \author Nicolò Jacazio, Università del Piemonte Orientale (IT) |
| 16 | +/// \since 2026-04-01 |
| 17 | +/// |
| 18 | + |
| 19 | +#include "TRKReconstruction/TrackerACTS.h" |
| 20 | + |
| 21 | +#include <Acts/EventData/Seed.hpp> |
| 22 | +#include <Acts/EventData/SpacePointContainer.hpp> |
| 23 | +#include <Acts/Seeding/BinnedGroup.hpp> |
| 24 | +#include <Acts/Seeding/SeedFilter.hpp> |
| 25 | +#include <Acts/Seeding/SeedFilterConfig.hpp> |
| 26 | +#include <Acts/Seeding/SeedFinder.hpp> |
| 27 | +#include <Acts/Seeding/SeedFinderConfig.hpp> |
| 28 | +#include <Acts/Seeding/detail/CylindricalSpacePointGrid.hpp> |
| 29 | +#include <Acts/Utilities/GridBinFinder.hpp> |
| 30 | +#include <Acts/Utilities/RangeXD.hpp> |
| 31 | + |
| 32 | +namespace o2::trk |
| 33 | +{ |
| 34 | + |
| 35 | +template <int nLayers> |
| 36 | +TrackerACTS<nLayers>::TrackerACTS() |
| 37 | +{ |
| 38 | + // Initialize space points storage per layer |
| 39 | + mSpacePointsPerLayer.resize(nLayers); |
| 40 | + mHistSpacePoints = new TH2F("hSpacePoints", "Space points; x (cm); y (cm)", 200, -100, 100, 200, -100, 100); |
| 41 | +} |
| 42 | + |
| 43 | +template <int nLayers> |
| 44 | +void TrackerACTS<nLayers>::adoptTimeFrame(o2::its::TimeFrame<nLayers>& tf) |
| 45 | +{ |
| 46 | + mTimeFrame = &tf; |
| 47 | +} |
| 48 | + |
| 49 | +template <int nLayers> |
| 50 | +void TrackerACTS<nLayers>::buildSpacePoints(int rof) |
| 51 | +{ |
| 52 | + mSpacePoints.clear(); |
| 53 | + for (auto& layerSPs : mSpacePointsPerLayer) { |
| 54 | + layerSPs.clear(); |
| 55 | + } |
| 56 | + |
| 57 | + // Get clusters from the TimeFrame and convert to space points |
| 58 | + for (int layer = 0; layer < nLayers; ++layer) { |
| 59 | + // For now we take unsorted clusters, as soon as the cluster trackin is in place we can piggy back on it and switch to the clusters |
| 60 | + auto clusters = mTimeFrame->getUnsortedClusters()[layer]; |
| 61 | + // Resize the clusters to the first 100 clusters for testing |
| 62 | + // clusters = clusters.subspan(0, std::min<size_t>(clusters.size(), 100)); |
| 63 | + LOG(debug) << "ACTSTracker: got " << clusters.size() << " clusters"; |
| 64 | + |
| 65 | + for (size_t iCluster = 0; iCluster < clusters.size(); ++iCluster) { |
| 66 | + const auto& cluster = clusters[iCluster]; |
| 67 | + |
| 68 | + SpacePoint sp; |
| 69 | + // Check that these are in global coordinates |
| 70 | + sp.x = cluster.xCoordinate * Acts::UnitConstants::cm; |
| 71 | + sp.y = cluster.yCoordinate * Acts::UnitConstants::cm; |
| 72 | + sp.z = cluster.zCoordinate * Acts::UnitConstants::cm; |
| 73 | + |
| 74 | + if (mHistSpacePoints) { |
| 75 | + mHistSpacePoints->Fill(sp.x / Acts::UnitConstants::cm, sp.y / Acts::UnitConstants::cm); |
| 76 | + } |
| 77 | + sp.layer = layer; |
| 78 | + sp.clusterId = static_cast<int>(iCluster); |
| 79 | + sp.rof = rof; |
| 80 | + |
| 81 | + // Position uncertainties (could be refined based on cluster properties) |
| 82 | + sp.varianceR = 0.01f; // ~100 um resolution squared |
| 83 | + sp.varianceZ = 0.01f; |
| 84 | + |
| 85 | + mSpacePoints.push_back(sp); |
| 86 | + } |
| 87 | + } |
| 88 | + |
| 89 | + // Build per-layer pointers for seeding |
| 90 | + for (auto& sp : mSpacePoints) { |
| 91 | + if (sp.layer >= 0 && sp.layer < nLayers) { |
| 92 | + mSpacePointsPerLayer[sp.layer].push_back(&sp); |
| 93 | + } |
| 94 | + } |
| 95 | +} |
| 96 | + |
| 97 | +template <int nLayers> |
| 98 | +void TrackerACTS<nLayers>::createSeeds() |
| 99 | +{ |
| 100 | + if (mSpacePoints.empty()) { |
| 101 | + LOGF(info, "No space points available for seeding"); |
| 102 | + return; |
| 103 | + } |
| 104 | + mSeeds.clear(); |
| 105 | + |
| 106 | + // Backend adaptor that exposes mSpacePoints to Acts::SpacePointContainer |
| 107 | + struct SpacePointBackend { |
| 108 | + using ValueType = SpacePoint; |
| 109 | + explicit SpacePointBackend(const std::vector<SpacePoint>& sps) : m_sps{&sps} {} |
| 110 | + std::size_t size_impl() const { return m_sps->size(); } |
| 111 | + float x_impl(std::size_t i) const { return (*m_sps)[i].x; } |
| 112 | + float y_impl(std::size_t i) const { return (*m_sps)[i].y; } |
| 113 | + float z_impl(std::size_t i) const { return (*m_sps)[i].z; } |
| 114 | + float varianceR_impl(std::size_t i) const { return (*m_sps)[i].varianceR; } |
| 115 | + float varianceZ_impl(std::size_t i) const { return (*m_sps)[i].varianceZ; } |
| 116 | + const SpacePoint& get_impl(std::size_t i) const { return (*m_sps)[i]; } |
| 117 | + std::any component_impl(Acts::HashedString /*key*/, std::size_t /*i*/) const |
| 118 | + { |
| 119 | + LOG(fatal) << "No additional components available for space points"; |
| 120 | + throw std::runtime_error("SpacePointBackend: no strip component available"); |
| 121 | + } |
| 122 | + const std::vector<SpacePoint>* m_sps; |
| 123 | + }; |
| 124 | + |
| 125 | + // Wrap mSpacePoints in an Acts space-point container |
| 126 | + SpacePointBackend backend{mSpacePoints}; |
| 127 | + |
| 128 | + // Configure the ACTS space point container |
| 129 | + Acts::SpacePointContainerConfig spContainerConfig; |
| 130 | + Acts::SpacePointContainerOptions spContainerOpts; |
| 131 | + spContainerOpts.beamPos = {0.f, 0.f}; |
| 132 | + Acts::SpacePointContainer<SpacePointBackend, Acts::detail::RefHolder> spContainer{spContainerConfig, spContainerOpts, backend}; |
| 133 | + |
| 134 | + // Configure the ACTS seed finder |
| 135 | + const unsigned int maxSeeds = static_cast<unsigned int>(mConfig.maxSeedsPerMiddleSP); |
| 136 | + Acts::SeedFilterConfig filterConfig; |
| 137 | + filterConfig.maxSeedsPerSpM = maxSeeds; |
| 138 | + |
| 139 | + // ACTS requires minPt / bFieldInZ >= rMax / 2 (minHelixRadius >= rMax/2). |
| 140 | + // Cap rMax so that the constraint is satisfied for the configured minPt and field. |
| 141 | + const float bFieldInZ = mBz * Acts::UnitConstants::T; |
| 142 | + const float safeRMax = 1.8f * mConfig.minPt / bFieldInZ; // 10% margin below the hard limit |
| 143 | + |
| 144 | + using SPProxy = typename Acts::SpacePointContainer<SpacePointBackend, Acts::detail::RefHolder>::SpacePointProxyType; |
| 145 | + Acts::SeedFinderConfig<SPProxy> finderConfig; |
| 146 | + finderConfig.rMin = 0.f; |
| 147 | + finderConfig.rMax = 100.f * Acts::UnitConstants::cm; |
| 148 | + finderConfig.zMin = mConfig.zMin; |
| 149 | + finderConfig.zMax = mConfig.zMax; |
| 150 | + finderConfig.deltaRMin = std::min(mConfig.deltaRMinBottom, mConfig.deltaRMinTop); |
| 151 | + finderConfig.deltaRMax = std::max(mConfig.deltaRMaxBottom, mConfig.deltaRMaxTop); |
| 152 | + finderConfig.deltaRMinBottomSP = mConfig.deltaRMinBottom; |
| 153 | + finderConfig.deltaRMaxBottomSP = mConfig.deltaRMaxBottom; |
| 154 | + finderConfig.deltaRMinTopSP = mConfig.deltaRMinTop; |
| 155 | + finderConfig.deltaRMaxTopSP = mConfig.deltaRMaxTop; |
| 156 | + finderConfig.collisionRegionMin = mConfig.collisionRegionMin; |
| 157 | + finderConfig.collisionRegionMax = mConfig.collisionRegionMax; |
| 158 | + finderConfig.cotThetaMax = mConfig.cotThetaMax; |
| 159 | + finderConfig.minPt = mConfig.minPt; |
| 160 | + finderConfig.impactMax = mConfig.maxImpactParameter; |
| 161 | + finderConfig.maxSeedsPerSpM = maxSeeds; |
| 162 | + finderConfig.sigmaScattering = 5.f; |
| 163 | + finderConfig.radLengthPerSeed = 0.05f; |
| 164 | + finderConfig.seedFilter = std::make_shared<Acts::SeedFilter<SPProxy>>(filterConfig); |
| 165 | + finderConfig = finderConfig.calculateDerivedQuantities(); |
| 166 | + Acts::SeedFinder<SPProxy, Acts::CylindricalSpacePointGrid<SPProxy>> seedFinder{finderConfig, |
| 167 | + Acts::getDefaultLogger("Finder", Acts::Logging::Level::VERBOSE)}; |
| 168 | + |
| 169 | + // Configure and create the cylindrical space-point grid |
| 170 | + Acts::CylindricalSpacePointGridConfig gridConfig; |
| 171 | + gridConfig.minPt = finderConfig.minPt; |
| 172 | + gridConfig.rMin = finderConfig.rMin; |
| 173 | + gridConfig.rMax = finderConfig.rMax; |
| 174 | + gridConfig.zMin = finderConfig.zMin; |
| 175 | + gridConfig.zMax = finderConfig.zMax; |
| 176 | + gridConfig.deltaRMax = finderConfig.deltaRMax; |
| 177 | + gridConfig.cotThetaMax = finderConfig.cotThetaMax; |
| 178 | + gridConfig.impactMax = finderConfig.impactMax; |
| 179 | + |
| 180 | + Acts::CylindricalSpacePointGridOptions gridOpts; |
| 181 | + gridOpts.bFieldInZ = bFieldInZ; |
| 182 | + |
| 183 | + Acts::SeedFinderOptions finderOpts; |
| 184 | + finderOpts.beamPos = spContainerOpts.beamPos; |
| 185 | + finderOpts.bFieldInZ = gridOpts.bFieldInZ; |
| 186 | + try { |
| 187 | + finderOpts = finderOpts.calculateDerivedQuantities(finderConfig); |
| 188 | + } catch (const std::exception& e) { |
| 189 | + LOG(fatal) << "Error in seed finder configuration: " << e.what(); |
| 190 | + return; |
| 191 | + } |
| 192 | + |
| 193 | + Acts::CylindricalSpacePointGrid<SPProxy> grid = Acts::CylindricalSpacePointGridCreator::createGrid<SPProxy>(gridConfig, gridOpts); |
| 194 | + try { |
| 195 | + Acts::CylindricalSpacePointGridCreator::fillGrid(finderConfig, finderOpts, grid, |
| 196 | + spContainer.begin(), spContainer.end()); |
| 197 | + } catch (const std::exception& e) { |
| 198 | + LOG(fatal) << "Error during grid creation/filling: " << e.what(); |
| 199 | + return; |
| 200 | + } |
| 201 | + LOG(debug) << "Grid created with " << grid.dimensions(); |
| 202 | + |
| 203 | + // Build the binned group and iterate over triplet combinations |
| 204 | + Acts::GridBinFinder<3ul> bottomBinFinder{1, std::vector<std::pair<int, int>>{}, 0}; |
| 205 | + Acts::GridBinFinder<3ul> topBinFinder{1, std::vector<std::pair<int, int>>{}, 0}; |
| 206 | + Acts::CylindricalBinnedGroup<SPProxy> spGroup{std::move(grid), bottomBinFinder, topBinFinder}; |
| 207 | + |
| 208 | + std::vector<std::vector<Acts::Seed<SPProxy>>> seedsPerGroup; |
| 209 | + typename Acts::SeedFinder<SPProxy, Acts::CylindricalSpacePointGrid<SPProxy>>::SeedingState seedingState; |
| 210 | + seedingState.spacePointMutableData.resize(spContainer.size()); |
| 211 | + const Acts::Range1D<float> rMiddleSPRange; |
| 212 | + for (auto [bottom, middle, top] : spGroup) { |
| 213 | + auto& v = seedsPerGroup.emplace_back(); |
| 214 | + try { |
| 215 | + seedFinder.createSeedsForGroup(finderOpts, seedingState, spGroup.grid(), v, bottom, middle, top, rMiddleSPRange); |
| 216 | + } catch (const std::exception& e) { |
| 217 | + LOG(fatal) << "Error during seed finding for a group: " << e.what(); |
| 218 | + return; |
| 219 | + } |
| 220 | + } |
| 221 | + LOG(debug) << "Seed finding completed, found " << seedsPerGroup.size() << " groups with seeds"; |
| 222 | + |
| 223 | + // Convert Acts seeds to the internal SeedACTS representation |
| 224 | + for (const auto& groupSeeds : seedsPerGroup) { |
| 225 | + for (const auto& actsSeed : groupSeeds) { |
| 226 | + SeedACTS seed; |
| 227 | + seed.bottom = &actsSeed.sp()[0]->externalSpacePoint(); |
| 228 | + seed.middle = &actsSeed.sp()[1]->externalSpacePoint(); |
| 229 | + seed.top = &actsSeed.sp()[2]->externalSpacePoint(); |
| 230 | + seed.quality = actsSeed.seedQuality(); |
| 231 | + mSeeds.push_back(seed); |
| 232 | + } |
| 233 | + } |
| 234 | + |
| 235 | + LOGF(info, "Created %zu seeds from %zu space points", mSeeds.size(), mSpacePoints.size()); |
| 236 | +} |
| 237 | + |
| 238 | +template <int nLayers> |
| 239 | +bool TrackerACTS<nLayers>::estimateTrackParams(const SeedACTS& seed, o2::its::TrackITSExt& track) const |
| 240 | +{ |
| 241 | + return true; |
| 242 | +} |
| 243 | + |
| 244 | +template <int nLayers> |
| 245 | +void TrackerACTS<nLayers>::findTracks() |
| 246 | +{ |
| 247 | +} |
| 248 | + |
| 249 | +template <int nLayers> |
| 250 | +void TrackerACTS<nLayers>::computeTracksMClabels() |
| 251 | +{ |
| 252 | +} |
| 253 | + |
| 254 | +template <int nLayers> |
| 255 | +void TrackerACTS<nLayers>::clustersToTracks() |
| 256 | +{ |
| 257 | + if (!mTimeFrame) { |
| 258 | + LOG(error) << "Cannot run TrackerACTS: No TimeFrame adopted"; |
| 259 | + return; |
| 260 | + } |
| 261 | + |
| 262 | + double totalTime = 0.; |
| 263 | + LOG(info) << "==== TRK ACTS Tracking ===="; |
| 264 | + LOG(info) << "Processing " << mTimeFrame->getNrof() << " ROFs with B = " << mBz << " T"; |
| 265 | + |
| 266 | + // Process each ROF |
| 267 | + for (int iROF = 0; iROF < mTimeFrame->getNrof(); ++iROF) { |
| 268 | + LOG(info) << "Processing ROF " << iROF; |
| 269 | + // Build space points |
| 270 | + mCurState = SpacePointBuilding; |
| 271 | + totalTime += evaluateTask([this, iROF]() { buildSpacePoints(iROF); }, |
| 272 | + StateNames[mCurState]); |
| 273 | + |
| 274 | + // Run seeding |
| 275 | + mCurState = Seeding; |
| 276 | + totalTime += evaluateTask([this]() { createSeeds(); }, |
| 277 | + StateNames[mCurState]); |
| 278 | + |
| 279 | + // Find tracks |
| 280 | + mCurState = TrackFinding; |
| 281 | + totalTime += evaluateTask([this]() { findTracks(); }, |
| 282 | + StateNames[mCurState]); |
| 283 | + } |
| 284 | + |
| 285 | + // MC labeling |
| 286 | + if (mTimeFrame->hasMCinformation()) { |
| 287 | + computeTracksMClabels(); |
| 288 | + } |
| 289 | + |
| 290 | + LOG(info) << "=== TimeFrame " << mTimeFrameCounter << " completed in: " << totalTime << " ms ==="; |
| 291 | + |
| 292 | + ++mTimeFrameCounter; |
| 293 | + mTotalTime += totalTime; |
| 294 | +} |
| 295 | + |
| 296 | +template <int nLayers> |
| 297 | +void TrackerACTS<nLayers>::printSummary() const |
| 298 | +{ |
| 299 | + float avgTF = mTimeFrameCounter > 0 ? static_cast<float>(mTotalTime) / mTimeFrameCounter : 0.f; |
| 300 | + LOGP(info, "TrackerACTS summary: Processed {} TFs in TOT={:.2f} ms, AVG/TF={:.2f} ms", |
| 301 | + mTimeFrameCounter, mTotalTime, avgTF); |
| 302 | +} |
| 303 | + |
| 304 | +// Explicit template instantiations |
| 305 | +template class TrackerACTS<11>; |
| 306 | +} // namespace o2::trk |
0 commit comments