Skip to content

Commit 8826ab8

Browse files
committed
Add ACTS tracker
UpdateClsACTS U
1 parent 28cd933 commit 8826ab8

4 files changed

Lines changed: 145 additions & 12 deletions

File tree

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

Lines changed: 128 additions & 0 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,143 @@ 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+
float x = p[Acts::eFreePos0];
274+
float y = p[Acts::eFreePos1];
275+
float z = p[Acts::eFreePos2];
276+
float qOverP = p[Acts::eFreeQOverP];
277+
float pMag = 1.0f / std::abs(qOverP); // |p| = 1 / |q/p| (for unit charge)
278+
float px = p[Acts::eFreeDir0] * pMag;
279+
float py = p[Acts::eFreeDir1] * pMag;
280+
float pz = p[Acts::eFreeDir2] * pMag;
281+
282+
// Calculate track parameters in O2 format
283+
float pt = std::hypot(px, py);
284+
float phi = std::atan2(py, px);
285+
float theta = std::atan2(pt, pz);
286+
float eta = -std::log(std::tan(theta / 2.0f));
287+
288+
// Set cluster indices from seed
289+
track.setExternalClusterIndex(sp0->layer, sp0->clusterId);
290+
track.setExternalClusterIndex(sp1->layer, sp1->clusterId);
291+
track.setExternalClusterIndex(sp2->layer, sp2->clusterId);
292+
293+
// Set track parameters
294+
track.setX(x);
295+
track.setAlpha(phi);
296+
track.setY(y * std::cos(phi) - x * std::sin(phi));
297+
track.setZ(z);
298+
track.setSnp(std::sin(phi - track.getAlpha()));
299+
track.setTgl(std::tan(o2::constants::math::PIHalf - theta));
300+
301+
// q/pT = q/|p| * |p|/pT = qOverP * |p| / pT = qOverP / (pT / |p|) = qOverP / sin(theta)
302+
// Or simply: charge / pT where charge = sign(qOverP)
303+
float charge = (qOverP > 0) ? 1.0f : -1.0f;
304+
track.setQ2Pt(charge / pt);
305+
306+
LOG(info) << "Estimated track parameters";
241307
return true;
242308
}
243309

244310
template <int nLayers>
245311
void TrackerACTS<nLayers>::findTracks()
246312
{
313+
return; // For now we only create seeds, track finding and fitting will be implemented in the next iterations
314+
int nTracks = 0;
315+
316+
for (const auto& seed : mSeeds) {
317+
o2::its::TrackITSExt track;
318+
319+
LOG(info) << "Estimating track parameters for seed with quality (pT) = " << seed.quality;
320+
if (!estimateTrackParams(seed, track)) {
321+
continue;
322+
}
323+
324+
// Add track to TimeFrame
325+
const int rof = seed.middle->rof;
326+
if (mTimeFrame && rof >= 0 && rof < mTimeFrame->getNrof(0)) {
327+
LOG(info) << "Adding track to ROF " << rof;
328+
auto& tracks = mTimeFrame->getTracks();
329+
// tracks.emplace_back(track);
330+
++nTracks;
331+
}
332+
}
333+
LOG(info) << "Created " << nTracks << " tracks from " << mSeeds.size() << " seeds";
247334
}
248335

249336
template <int nLayers>
250337
void TrackerACTS<nLayers>::computeTracksMClabels()
251338
{
339+
return; // For now we skip MC labeling, will be implemented in the next iterations once we have track candidates to label
340+
if (!mTimeFrame || !mTimeFrame->hasMCinformation()) {
341+
return;
342+
}
343+
344+
// MC labeling using majority voting on cluster labels
345+
for (int iROF = 0; iROF < mTimeFrame->getNrof(0); ++iROF) {
346+
for (auto& track : mTimeFrame->getTracks()) {
347+
std::vector<std::pair<MCCompLabel, size_t>> labelCounts;
348+
349+
for (int iCluster = 0; iCluster < o2::its::TrackITSExt::MaxClusters; ++iCluster) {
350+
const int clusterIdx = track.getClusterIndex(iCluster);
351+
if (clusterIdx == o2::its::constants::UnusedIndex) {
352+
continue;
353+
}
354+
355+
auto clusterLabels = mTimeFrame->getClusterLabels(iCluster, clusterIdx);
356+
for (const auto& label : clusterLabels) {
357+
auto it = std::find_if(labelCounts.begin(), labelCounts.end(),
358+
[&label](const auto& p) { return p.first == label; });
359+
if (it != labelCounts.end()) {
360+
++(it->second);
361+
} else {
362+
labelCounts.emplace_back(label, 1);
363+
}
364+
}
365+
}
366+
367+
if (!labelCounts.empty()) {
368+
// Find label with most occurrences
369+
auto maxIt = std::max_element(labelCounts.begin(), labelCounts.end(),
370+
[](const auto& a, const auto& b) { return a.second < b.second; });
371+
372+
MCCompLabel trackLabel = maxIt->first;
373+
if (maxIt->second < static_cast<size_t>(track.getNumberOfClusters())) {
374+
trackLabel.setFakeFlag();
375+
}
376+
mTimeFrame->getTracksLabel().emplace_back(trackLabel);
377+
}
378+
}
379+
}
252380
}
253381

254382
template <int nLayers>

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
//

Detectors/Upgrades/ALICE3/TRKFT3/TRK/simulation/src/TRKLayer.cxx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,8 @@ void TRKCylindricalLayer::createLayer(TGeoVolume* motherVolume)
9191
TRKSegmentedLayer::TRKSegmentedLayer(int layerNumber, std::string layerName, float rInn, float tiltAngle, int numberOfStaves, int numberOfModules, float thickOrX2X0, MatBudgetParamMode mode)
9292
: TRKCylindricalLayer(layerNumber, layerName, rInn, numberOfModules * sModuleLength, thickOrX2X0, mode), mTiltAngle(tiltAngle), mNumberOfStaves(numberOfStaves), mNumberOfModules(numberOfModules)
9393
{
94-
assert(numberOfStaves % 2 == 0 && "Error: numberOfStaves must be even!");
94+
LOG(info) << "Creating segmented layer: id: " << mLayerNumber << " rInner: " << mInnerRadius << " rOuter: " << mOuterRadius << " zLength: " << mLength
95+
<< " x2X0: " << mX2X0 << " number of modules: " << mNumberOfModules;
9596
}
9697

9798
TGeoVolume* TRKSegmentedLayer::createSensor()

Detectors/Upgrades/ALICE3/TRKFT3/common/reconstruction/include/TRKReconstruction/Clusterer.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,9 @@ class Clusterer
176176
static o2::math_utils::Point3D<float> getClusterLocalCoordinates(const ClusterType& cluster, const uint8_t* patt,
177177
float yPlaneMLOT = 0.f) noexcept;
178178

179+
static o2::math_utils::Point3D<float> getClusterLocalCoordinates(const Cluster& cluster, const uint8_t* patt,
180+
float yPlaneMLOT = 0.f) noexcept;
181+
179182
protected:
180183
int mNHugeClus = 0;
181184
std::unique_ptr<ClustererThread> mThread;

0 commit comments

Comments
 (0)