Skip to content

Commit f9beaf4

Browse files
committed
Add ACTS tracker
1 parent b6586b6 commit f9beaf4

2 files changed

Lines changed: 143 additions & 29 deletions

File tree

‎Detectors/Upgrades/ALICE3/GlobalReconstruction/reconstruction/src/TrackerACTS.cxx‎

Lines changed: 131 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
#include <Acts/Seeding/detail/CylindricalSpacePointGrid.hpp>
2929
#include <Acts/Utilities/GridBinFinder.hpp>
3030
#include <Acts/Utilities/RangeXD.hpp>
31+
#include <Acts/Definitions/TrackParametrization.hpp>
32+
#include <Acts/Seeding/EstimateTrackParamsFromSeed.hpp>
3133

3234
namespace o2::trk
3335
{
@@ -238,17 +240,128 @@ void TrackerACTS<nLayers>::createSeeds()
238240
template <int nLayers>
239241
bool TrackerACTS<nLayers>::estimateTrackParams(const SeedACTS& seed, o2::its::TrackITSExt& track) const
240242
{
243+
const SpacePoint* sp0 = seed.bottom;
244+
const SpacePoint* sp1 = seed.middle;
245+
const SpacePoint* sp2 = seed.top;
246+
247+
// Use ACTS parameter estimation
248+
Acts::Vector3 pos0{sp0->x, sp0->y, sp0->z};
249+
Acts::Vector3 pos1{sp1->x, sp1->y, sp1->z};
250+
Acts::Vector3 pos2{sp2->x, sp2->y, sp2->z};
251+
252+
// Magnetic field vector (along z-axis)
253+
Acts::Vector3 bField{0., 0., mBz * Acts::UnitConstants::T};
254+
255+
// Use the ACTS function with time parameter (t0 = 0)
256+
LOG(info) << "Calling ACTS estimateTrackParamsFromSeed with mag field " << mBz << " T";
257+
LOG(info) << "Seed space points: (" << pos0.transpose() << "), (" << pos1.transpose() << "), (" << pos2.transpose() << ")";
258+
259+
Acts::FreeVector params;
260+
try {
261+
params = Acts::estimateTrackParamsFromSeed(pos0, 0.0, pos1, pos2, bField);
262+
} catch (const std::exception& e) {
263+
LOG(fatal) << "ACTS parameter estimation failed: " << e.what();
264+
return false;
265+
}
266+
LOG(info) << "ACTS parameter estimation successful: x=" << params[Acts::eFreePos0] << " y=" << params[Acts::eFreePos1]
267+
<< " z=" << params[Acts::eFreePos2] << " q/p=" << params[Acts::eFreeQOverP];
268+
269+
// Extract parameters from ACTS format
270+
const auto& p = params;
271+
// ACTS FreeVector: x, y, z, t, dir_x, dir_y, dir_z, q/|p|
272+
// Direction components are normalized (unit vector)
273+
const float x = p[Acts::eFreePos0];
274+
const float y = p[Acts::eFreePos1];
275+
const float z = p[Acts::eFreePos2];
276+
const float px = p[Acts::eFreeDir0] * pMag;
277+
const float py = p[Acts::eFreeDir1] * pMag;
278+
const float pz = p[Acts::eFreeDir2] * pMag;
279+
const int charge = (p[Acts::eFreeQOverP] >= 0) ? 1 : -1;
280+
281+
// Set cluster indices from seed
282+
const int charge = std::sgn(p[Acts::eFreeQOverP]);
283+
track.set({x, y, z}, {px, py, pz}, charge, false, PID::Pion);
284+
track.setPxPyPz(px, py, pz);
285+
track.setPosition(x, y, z);
286+
287+
// q/pT = q/|p| * |p|/pT = qOverP * |p| / pT = qOverP / (pT / |p|) = qOverP / sin(theta)
288+
// Or simply: charge / pT where charge = sign(qOverP)
289+
track.setAbsCharge(charge);
290+
291+
LOG(info) << "Estimated track parameters";
241292
return true;
242293
}
243294

244295
template <int nLayers>
245296
void TrackerACTS<nLayers>::findTracks()
246297
{
298+
return; // For now we only create seeds, track finding and fitting will be implemented in the next iterations
299+
int nTracks = 0;
300+
301+
for (const auto& seed : mSeeds) {
302+
o2::its::TrackITSExt track;
303+
304+
LOG(info) << "Estimating track parameters for seed with quality (pT) = " << seed.quality;
305+
if (!estimateTrackParams(seed, track)) {
306+
continue;
307+
}
308+
309+
// Add track to TimeFrame
310+
const int rof = seed.middle->rof;
311+
if (mTimeFrame && rof >= 0 && rof < mTimeFrame->getNrof(0)) {
312+
LOG(info) << "Adding track to ROF " << rof;
313+
auto& tracks = mTimeFrame->getTracks();
314+
// tracks.emplace_back(track);
315+
++nTracks;
316+
}
317+
}
318+
LOG(info) << "Created " << nTracks << " tracks from " << mSeeds.size() << " seeds";
247319
}
248320

249321
template <int nLayers>
250322
void TrackerACTS<nLayers>::computeTracksMClabels()
251323
{
324+
return; // For now we skip MC labeling, will be implemented in the next iterations once we have track candidates to label
325+
if (!mTimeFrame || !mTimeFrame->hasMCinformation()) {
326+
return;
327+
}
328+
329+
// MC labeling using majority voting on cluster labels
330+
for (int iROF = 0; iROF < mTimeFrame->getNrof(0); ++iROF) {
331+
for (auto& track : mTimeFrame->getTracks()) {
332+
std::vector<std::pair<MCCompLabel, size_t>> labelCounts;
333+
334+
for (int iCluster = 0; iCluster < o2::its::TrackITSExt::MaxClusters; ++iCluster) {
335+
const int clusterIdx = track.getClusterIndex(iCluster);
336+
if (clusterIdx == o2::its::constants::UnusedIndex) {
337+
continue;
338+
}
339+
340+
auto clusterLabels = mTimeFrame->getClusterLabels(iCluster, clusterIdx);
341+
for (const auto& label : clusterLabels) {
342+
auto it = std::find_if(labelCounts.begin(), labelCounts.end(),
343+
[&label](const auto& p) { return p.first == label; });
344+
if (it != labelCounts.end()) {
345+
++(it->second);
346+
} else {
347+
labelCounts.emplace_back(label, 1);
348+
}
349+
}
350+
}
351+
352+
if (!labelCounts.empty()) {
353+
// Find label with most occurrences
354+
auto maxIt = std::max_element(labelCounts.begin(), labelCounts.end(),
355+
[](const auto& a, const auto& b) { return a.second < b.second; });
356+
357+
MCCompLabel trackLabel = maxIt->first;
358+
if (maxIt->second < static_cast<size_t>(track.getNumberOfClusters())) {
359+
trackLabel.setFakeFlag();
360+
}
361+
mTimeFrame->getTracksLabel().emplace_back(trackLabel);
362+
}
363+
}
364+
}
252365
}
253366

254367
template <int nLayers>
@@ -261,26 +374,26 @@ void TrackerACTS<nLayers>::clustersToTracks()
261374

262375
double totalTime = 0.;
263376
LOG(info) << "==== TRK ACTS Tracking ====";
264-
LOG(info) << "Processing " << mTimeFrame->getNrof() << " ROFs with B = " << mBz << " T";
377+
// LOG(info) << "Processing " << mTimeFrame->getNrof() << " ROFs with B = " << mBz << " T";
265378

266379
// 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-
}
380+
// for (int iROF = 0; iROF < mTimeFrame->getNrof(); ++iROF) {
381+
// LOG(info) << "Processing ROF " << iROF;
382+
// // Build space points
383+
// mCurState = SpacePointBuilding;
384+
// totalTime += evaluateTask([this, iROF]() { buildSpacePoints(iROF); },
385+
// StateNames[mCurState]);
386+
387+
// // Run seeding
388+
// mCurState = Seeding;
389+
// totalTime += evaluateTask([this]() { createSeeds(); },
390+
// StateNames[mCurState]);
391+
392+
// // Find tracks
393+
// mCurState = TrackFinding;
394+
// totalTime += evaluateTask([this]() { findTracks(); },
395+
// StateNames[mCurState]);
396+
// }
284397

285398
// MC labeling
286399
if (mTimeFrame->hasMCinformation()) {

‎Detectors/Upgrades/ALICE3/TRKFT3/TRK/macros/test/CheckClusters.C‎

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -75,17 +75,18 @@ void CheckClusters(const std::string& clusfile = "o2clus_trk.root",
7575
// ── Chip response (for hit-segment propagation to charge-collection plane) ──
7676
// Fetches the same AlpideSimResponse from CCDB as the digitizer (IT3/Calib/APTSResponse)
7777
// and computes Y-intersection planes with the same formulas from Digitizer::init()
78-
auto& ccdbMgr = o2::ccdb::BasicCCDBManager::instance();
79-
ccdbMgr.setURL(ccdbUrl);
80-
if (ccdbTimestamp > 0) {
81-
ccdbMgr.setTimestamp(ccdbTimestamp);
82-
}
83-
auto* alpResp = ccdbMgr.get<o2::itsmft::AlpideSimResponse>("IT3/Calib/APTSResponse");
84-
if (!alpResp) {
85-
LOGP(fatal, "Cannot retrieve AlpideSimResponse from CCDB at {}", ccdbUrl);
86-
return;
87-
}
88-
const float depthMax = alpResp->getDepthMax();
78+
// auto& ccdbMgr = o2::ccdb::BasicCCDBManager::instance();
79+
// ccdbMgr.setURL(ccdbUrl);
80+
// if (ccdbTimestamp > 0) {
81+
// ccdbMgr.setTimestamp(ccdbTimestamp);
82+
// }
83+
// auto* alpResp = ccdbMgr.get<o2::itsmft::AlpideSimResponse>("IT3/Calib/APTSResponse");
84+
// if (!alpResp) {
85+
// LOGP(fatal, "Cannot retrieve AlpideSimResponse from CCDB at {}", ccdbUrl);
86+
// return;
87+
// }
88+
// const float depthMax = alpResp->getDepthMax();
89+
const float depthMax = 500;
8990

9091
// ── Y-plane shifts: why VD and ML/OT need different values ────────────────
9192
//

0 commit comments

Comments
 (0)