Skip to content

Commit 1d645a9

Browse files
authored
Add TrackerACTS implementation for TRK reconstruction
1 parent 83adb35 commit 1d645a9

File tree

1 file changed

+306
-0
lines changed

1 file changed

+306
-0
lines changed
Lines changed: 306 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,306 @@
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

Comments
 (0)