From 890c63e7ed28b7857790f58c8a7b0ca56aaafab9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Jacazio?= Date: Thu, 5 Mar 2026 09:28:21 +0100 Subject: [PATCH 01/12] Add macros --- .../Upgrades/ALICE3/macros/ALICE3Field.C | 45 ++++++++- .../ALICE3/macros/ALICE3FieldShortMagnet.C | 91 +++++++++++++++++++ .../Upgrades/ALICE3/macros/CMakeLists.txt | 5 +- 3 files changed, 138 insertions(+), 3 deletions(-) create mode 100644 Detectors/Upgrades/ALICE3/macros/ALICE3FieldShortMagnet.C diff --git a/Detectors/Upgrades/ALICE3/macros/ALICE3Field.C b/Detectors/Upgrades/ALICE3/macros/ALICE3Field.C index a721a91ed2dcc..96b7dd4bbe858 100644 --- a/Detectors/Upgrades/ALICE3/macros/ALICE3Field.C +++ b/Detectors/Upgrades/ALICE3/macros/ALICE3Field.C @@ -19,8 +19,8 @@ std::function field() double R2; double B1; double B2; - double beamStart = 500.; //[cm] - double tokGauss = 1. / 0.1; // conversion from Tesla to kGauss + const double beamStart = 500.; //[cm] + const double tokGauss = 1. / 0.1; // conversion from Tesla to kGauss bool isMagAbs = true; @@ -63,4 +63,45 @@ std::function field() b[2] = 0.; } }; +} + +void ALICE3Field() +{ + auto fieldFunc = field(); + // RZ plane visualization + TCanvas* cRZ = new TCanvas("cRZ", "Field in RZ plane", 800, 600); + TH2F* hRZ = new TH2F("hRZ", "Magnetic Field B_z in RZ plane;Z [m];R [m]", 100, -10, 10, 100, -5, 5); + hRZ->SetBit(TH1::kNoStats); // disable stats box + for (int i = 1; i <= hRZ->GetNbinsX(); i++) { + const double Z = hRZ->GetXaxis()->GetBinCenter(i); + for (int j = 1; j <= hRZ->GetNbinsY(); j++) { + const double R = hRZ->GetYaxis()->GetBinCenter(j); + const double pos[3] = {R * 100, 0, Z * 100}; // convert to cm + double b[3] = {0, 0, 0}; + fieldFunc(pos, b); + hRZ->SetBinContent(i, j, b[2]); + } + } + + hRZ->Draw("COLZ"); + cRZ->Update(); + + // XY plane visualization + TCanvas* cXY = new TCanvas("cXY", "Field in XY plane", 800, 600); + TH2F* hXY = new TH2F("hXY", "Magnetic Field B_z in XY plane;X [m];Y [m]", 100, -5, 5, 100, -5, 5); + hXY->SetBit(TH1::kNoStats); // disable stats box + + for (int i = 1; i <= hXY->GetNbinsX(); i++) { + const double X = hXY->GetXaxis()->GetBinCenter(i); + for (int j = 1; j <= hXY->GetNbinsY(); j++) { + const double Y = hXY->GetYaxis()->GetBinCenter(j); + const double pos[3] = {X * 100, Y * 100, 0}; // convert to cm + double b[3] = {0, 0, 0}; + fieldFunc(pos, b); + hXY->SetBinContent(i, j, b[2]); + } + } + + hXY->Draw("COLZ"); + cXY->Update(); } \ No newline at end of file diff --git a/Detectors/Upgrades/ALICE3/macros/ALICE3FieldShortMagnet.C b/Detectors/Upgrades/ALICE3/macros/ALICE3FieldShortMagnet.C new file mode 100644 index 0000000000000..b12adaa0cc530 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/macros/ALICE3FieldShortMagnet.C @@ -0,0 +1,91 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +// Author: J. E. Munoz Mendez jesus.munoz@cern.ch + +std::function field() +{ + return [](const double* x, double* b) { + const double Rc = 185.; //[cm] + const double R1 = 220.; //[cm] + const double R2 = 290.; //[cm] + const double B1 = 2.; //[T] + const double B2 = -Rc * Rc / ((R2 * R2 - R1 * R1) * B1); //[T] + const double beamStart = 370.; //[cm] + const double tokGauss = 1. / 0.1; // conversion from Tesla to kGauss + + const bool isMagAbs = true; + + const double r = sqrt(x[0] * x[0] + x[1] * x[1]); + if ((abs(x[2]) <= beamStart) && (r < Rc)) { // We are inside of the central region + b[0] = 0.; + b[1] = 0.; + b[2] = B1 * tokGauss; + } else if ((abs(x[2]) <= beamStart) && (r >= Rc && r < R1)) { // We are in the transition region + b[0] = 0.; + b[1] = 0.; + b[2] = 0.; + } else if ((abs(x[2]) <= beamStart) && (r >= R1 && r < R2)) { // We are within the magnet + b[0] = 0.; + b[1] = 0.; + if (isMagAbs) { + b[2] = B2 * tokGauss; + } else { + b[2] = 0.; + } + } else { // We are outside of the magnet + b[0] = 0.; + b[1] = 0.; + b[2] = 0.; + } + }; +} + +void ALICE3V3Magnet() +{ + auto fieldFunc = field(); + // RZ plane visualization + TCanvas* cRZ = new TCanvas("cRZ", "Field in RZ plane", 800, 600); + TH2F* hRZ = new TH2F("hRZ", "Magnetic Field B_z in RZ plane;Z [m];R [m]", 100, -10, 10, 100, -5, 5); + hRZ->SetBit(TH1::kNoStats); // disable stats box + for (int i = 1; i <= hRZ->GetNbinsX(); i++) { + const double Z = hRZ->GetXaxis()->GetBinCenter(i); + for (int j = 1; j <= hRZ->GetNbinsY(); j++) { + const double R = hRZ->GetYaxis()->GetBinCenter(j); + const double pos[3] = {R * 100, 0, Z * 100}; // convert to cm + double b[3] = {0, 0, 0}; + fieldFunc(pos, b); + hRZ->SetBinContent(i, j, b[2]); + } + } + + hRZ->Draw("COLZ"); + cRZ->Update(); + + // XY plane visualization + TCanvas* cXY = new TCanvas("cXY", "Field in XY plane", 800, 600); + TH2F* hXY = new TH2F("hXY", "Magnetic Field B_z in XY plane;X [m];Y [m]", 100, -5, 5, 100, -5, 5); + hXY->SetBit(TH1::kNoStats); // disable stats box + + for (int i = 1; i <= hXY->GetNbinsX(); i++) { + const double X = hXY->GetXaxis()->GetBinCenter(i); + for (int j = 1; j <= hXY->GetNbinsY(); j++) { + const double Y = hXY->GetYaxis()->GetBinCenter(j); + const double pos[3] = {X * 100, Y * 100, 0}; // convert to cm + double b[3] = {0, 0, 0}; + fieldFunc(pos, b); + hXY->SetBinContent(i, j, b[2]); + } + } + + hXY->Draw("COLZ"); + cXY->Update(); +} \ No newline at end of file diff --git a/Detectors/Upgrades/ALICE3/macros/CMakeLists.txt b/Detectors/Upgrades/ALICE3/macros/CMakeLists.txt index b31687cc85c0e..0a4f9031355a2 100644 --- a/Detectors/Upgrades/ALICE3/macros/CMakeLists.txt +++ b/Detectors/Upgrades/ALICE3/macros/CMakeLists.txt @@ -13,4 +13,7 @@ o2_add_test_root_macro(scanXX0.C LABELS alice3) o2_add_test_root_macro(plotHits.C - LABELS alice3) \ No newline at end of file + LABELS alice3) + +o2_add_test_root_macro(ALICE3FieldShortMagnet.C + LABELS alice3) From ff7d07d9664257fced95a824ef632421ee3d9662 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Jacazio?= Date: Thu, 5 Mar 2026 09:54:12 +0100 Subject: [PATCH 02/12] Add ACTSClusterer --- .../ALICE3/TRK/reconstruction/CMakeLists.txt | 20 +- .../include/TRKReconstruction/Clusterer.h | 22 +- .../include/TRKReconstruction/ClustererACTS.h | 43 ++++ .../TRK/reconstruction/src/ClustererACTS.cxx | 238 ++++++++++++++++++ .../src/TRKReconstructionLinkDef.h | 3 + .../include/TRKWorkflow/ClustererSpec.h | 9 + .../ALICE3/TRK/workflow/src/ClustererSpec.cxx | 36 ++- dependencies/O2Dependencies.cmake | 3 + 8 files changed, 352 insertions(+), 22 deletions(-) create mode 100644 Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/ClustererACTS.h create mode 100644 Detectors/Upgrades/ALICE3/TRK/reconstruction/src/ClustererACTS.cxx diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/CMakeLists.txt b/Detectors/Upgrades/ALICE3/TRK/reconstruction/CMakeLists.txt index b9866c7d6aa4d..81a75e209124a 100644 --- a/Detectors/Upgrades/ALICE3/TRK/reconstruction/CMakeLists.txt +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/CMakeLists.txt @@ -9,10 +9,15 @@ # granted to it by virtue of its status as an Intergovernmental Organization # or submit itself to any jurisdiction. +if(Acts_FOUND) + set(actsTarget Acts::Core) +endif() + o2_add_library(TRKReconstruction TARGETVARNAME targetName SOURCES src/TimeFrame.cxx src/Clusterer.cxx + $<$:src/ClustererACTS.cxx> PUBLIC_LINK_LIBRARIES O2::ITStracking O2::GPUCommon @@ -27,11 +32,22 @@ o2_add_library(TRKReconstruction O2::DataFormatsITS O2::TRKSimulation nlohmann_json::nlohmann_json + ${actsTarget} PRIVATE_LINK_LIBRARIES O2::Steer TBB::tbb) +if(Acts_FOUND) + target_compile_definitions(${targetName} PUBLIC O2_WITH_ACTS) +endif() + +set(dictHeaders include/TRKReconstruction/TimeFrame.h + include/TRKReconstruction/Clusterer.h) + +if(Acts_FOUND) + list(APPEND dictHeaders include/TRKReconstruction/ClustererACTS.h) +endif() + o2_target_root_dictionary(TRKReconstruction - HEADERS include/TRKReconstruction/TimeFrame.h - include/TRKReconstruction/Clusterer.h + HEADERS ${dictHeaders} LINKDEF src/TRKReconstructionLinkDef.h) diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/Clusterer.h b/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/Clusterer.h index abddafa312fb9..70518b2ace593 100644 --- a/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/Clusterer.h +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/Clusterer.h @@ -161,17 +161,17 @@ class Clusterer }; //---------------------------------------------- - void process(gsl::span digits, - gsl::span digitROFs, - std::vector& clusters, - std::vector& patterns, - std::vector& clusterROFs, - const ConstDigitTruth* digitLabels = nullptr, - ClusterTruth* clusterLabels = nullptr, - gsl::span digMC2ROFs = {}, - std::vector* clusterMC2ROFs = nullptr); - - private: + virtual void process(gsl::span digits, + gsl::span digitROFs, + std::vector& clusters, + std::vector& patterns, + std::vector& clusterROFs, + const ConstDigitTruth* digitLabels = nullptr, + ClusterTruth* clusterLabels = nullptr, + gsl::span digMC2ROFs = {}, + std::vector* clusterMC2ROFs = nullptr); + + protected: int mNHugeClus = 0; std::unique_ptr mThread; std::vector mSortIdx; ///< reusable per-ROF sort buffer diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/ClustererACTS.h b/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/ClustererACTS.h new file mode 100644 index 0000000000000..4111737d17a9f --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/ClustererACTS.h @@ -0,0 +1,43 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file ClustererACTS.h +/// \brief Definition of the TRK cluster finder + +#ifndef ALICEO2_TRK_CLUSTERERACTS_H +#define ALICEO2_TRK_CLUSTERERACTS_H + +#include "TRKReconstruction/Clusterer.h" + +namespace o2::trk +{ + +class GeometryTGeo; + +class ClustererACTS : public Clusterer +{ + public: + void process(gsl::span digits, + gsl::span digitROFs, + std::vector& clusters, + std::vector& patterns, + std::vector& clusterROFs, + const ConstDigitTruth* digitLabels = nullptr, + ClusterTruth* clusterLabels = nullptr, + gsl::span digMC2ROFs = {}, + std::vector* clusterMC2ROFs = nullptr) override; + + private: +}; + +} // namespace o2::trk + +#endif diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/ClustererACTS.cxx b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/ClustererACTS.cxx new file mode 100644 index 0000000000000..711480747ea37 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/ClustererACTS.cxx @@ -0,0 +1,238 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file ClustererACTS.cxx +/// \brief Implementation of the TRK cluster finder with the ACTS + +#include "TRKReconstruction/ClustererACTS.h" +#include "TRKBase/GeometryTGeo.h" +#include + +#include +#include + +using namespace o2::trk; + +struct Cell2D { + Cell2D(int rowv, int colv) : row(rowv), col(colv) {} + int row, col; + Acts::Ccl::Label label{Acts::Ccl::NO_LABEL}; +}; + +int getCellRow(const Cell2D& cell) +{ + return cell.row; +} + +int getCellColumn(const Cell2D& cell) +{ + return cell.col; +} + +bool operator==(const Cell2D& left, const Cell2D& right) +{ + return left.row == right.row && left.col == right.col; +} + +bool cellComp(const Cell2D& left, const Cell2D& right) +{ + return (left.row == right.row) ? left.col < right.col : left.row < right.row; +} + +struct Cluster2D { + std::vector cells; + std::size_t hash{0}; +}; + +void clusterAddCell(Cluster2D& cl, const Cell2D& cell) +{ + cl.cells.push_back(cell); +} + +void hash(Cluster2D& cl) +{ + std::ranges::sort(cl.cells, cellComp); + cl.hash = 0; + // for (const Cell2D& c : cl.cells) { + // boost::hash_combine(cl.hash, c.col); + // } +} + +bool clHashComp(const Cluster2D& left, const Cluster2D& right) +{ + return left.hash < right.hash; +} + +template +void genclusterw(int x, int y, int x0, int y0, int x1, int y1, + std::vector& cells, RNG& rng, double startp = 0.5, + double decayp = 0.9) +{ + std::vector add; + + auto maybe_add = [&](int x_, int y_) { + Cell2D c(x_, y_); + // if (std::uniform_real_distribution()(rng) < startp && + // !rangeContainsValue(cells, c)) { + // cells.push_back(c); + // add.push_back(c); + // } + }; + + // NORTH + if (y < y1) { + maybe_add(x, y + 1); + } + // NORTHEAST + if (x < x1 && y < y1) { + maybe_add(x + 1, y + 1); + } + // EAST + if (x < x1) { + maybe_add(x + 1, y); + } + // SOUTHEAST + if (x < x1 && y > y0) { + maybe_add(x + 1, y - 1); + } + // SOUTH + if (y > y0) { + maybe_add(x, y - 1); + } + // SOUTHWEST + if (x > x0 && y > y0) { + maybe_add(x - 1, y - 1); + } + // WEST + if (x > x0) { + maybe_add(x - 1, y); + } + // NORTHWEST + if (x > x0 && y < y1) { + maybe_add(x - 1, y + 1); + } + + for (Cell2D& c : add) { + genclusterw(c.row, c.col, x0, y0, x1, y1, cells, rng, startp * decayp, + decayp); + } +} + +template +Cluster2D gencluster(int x0, int y0, int x1, int y1, RNG& rng, + double startp = 0.5, double decayp = 0.9) +{ + int x0_ = x0 + 1; + int x1_ = x1 - 1; + int y0_ = y0 + 1; + int y1_ = y1 - 1; + + int x = std::uniform_int_distribution(x0_, x1_)(rng); + int y = std::uniform_int_distribution(y0_, y1_)(rng); + + std::vector cells = {Cell2D(x, y)}; + genclusterw(x, y, x0_, y0_, x1_, y1_, cells, rng, startp, decayp); + + Cluster2D cl; + cl.cells = std::move(cells); + + return cl; +} + +//__________________________________________________ +void ClustererACTS::process(gsl::span digits, + gsl::span digitROFs, + std::vector& clusters, + std::vector& patterns, + std::vector& clusterROFs, + const ConstDigitTruth* digitLabels, + ClusterTruth* clusterLabels, + gsl::span digMC2ROFs, + std::vector* clusterMC2ROFs) +{ + if (!mThread) { + mThread = std::make_unique(this); + } + + auto* geom = o2::trk::GeometryTGeo::Instance(); + + for (size_t iROF = 0; iROF < digitROFs.size(); ++iROF) { + const auto& inROF = digitROFs[iROF]; + const auto outFirst = static_cast(clusters.size()); + const int first = inROF.getFirstEntry(); + const int nEntries = inROF.getNEntries(); + + if (nEntries == 0) { + clusterROFs.emplace_back(inROF.getBCData(), inROF.getROFrame(), outFirst, 0); + continue; + } + + // template > + // requires(GridDim == 1 || GridDim == 2) + // void createClusters(Acts::Ccl::ClusteringData& data, + // CellCollection& cells, + // ClusterCollection& clusters, + // Connect&& connect = Connect()); + using Cell = Cell2D; + using CellCollection = std::vector; + using Cluster = Cluster2D; + using ClusterCollection = std::vector; + CellCollection cells; + Acts::Ccl::ClusteringData data; + ClusterCollection collection; + + Acts::Ccl::createClusters(data, + cells, + collection, + Acts::Ccl::DefaultConnect(false)); + + // Sort digit indices within this ROF by (chipID, col, row) so we can process + // chip by chip, column by column -- the same ordering the ALPIDE scanner expects. + mSortIdx.resize(nEntries); + std::iota(mSortIdx.begin(), mSortIdx.end(), first); + std::sort(mSortIdx.begin(), mSortIdx.end(), [&digits](int a, int b) { + const auto& da = digits[a]; + const auto& db = digits[b]; + if (da.getChipIndex() != db.getChipIndex()) { + return da.getChipIndex() < db.getChipIndex(); + } + if (da.getColumn() != db.getColumn()) { + return da.getColumn() < db.getColumn(); + } + return da.getRow() < db.getRow(); + }); + + // Process one chip at a time + int sliceStart = 0; + while (sliceStart < nEntries) { + const int chipFirst = sliceStart; + const uint16_t chipID = digits[mSortIdx[sliceStart]].getChipIndex(); + while (sliceStart < nEntries && digits[mSortIdx[sliceStart]].getChipIndex() == chipID) { + ++sliceStart; + } + const int chipN = sliceStart - chipFirst; + + mThread->processChip(digits, chipFirst, chipN, &clusters, &patterns, digitLabels, clusterLabels, geom); + } + + clusterROFs.emplace_back(inROF.getBCData(), inROF.getROFrame(), + outFirst, static_cast(clusters.size()) - outFirst); + } + + if (clusterMC2ROFs && !digMC2ROFs.empty()) { + clusterMC2ROFs->reserve(clusterMC2ROFs->size() + digMC2ROFs.size()); + for (const auto& in : digMC2ROFs) { + clusterMC2ROFs->emplace_back(in.eventRecordID, in.rofRecordID, in.minROF, in.maxROF); + } + } +} diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TRKReconstructionLinkDef.h b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TRKReconstructionLinkDef.h index 4eda22e350852..6ef7321f92e2f 100644 --- a/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TRKReconstructionLinkDef.h +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TRKReconstructionLinkDef.h @@ -17,5 +17,8 @@ #pragma link C++ class o2::trk::TimeFrame < 11> + ; #pragma link C++ class o2::trk::Clusterer + ; +#ifdef O2_WITH_ACTS +#pragma link C++ class o2::trk::ClustererACTS + ; +#endif #endif diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/ClustererSpec.h b/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/ClustererSpec.h index bacc1057c7b07..9cfab104ecdf9 100644 --- a/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/ClustererSpec.h +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/ClustererSpec.h @@ -15,6 +15,9 @@ #include "Framework/DataProcessorSpec.h" #include "Framework/Task.h" #include "TRKReconstruction/Clusterer.h" +#ifdef O2_WITH_ACTS +#include "TRKReconstruction/ClustererACTS.h" +#endif namespace o2::trk { @@ -29,7 +32,13 @@ class ClustererDPL : public o2::framework::Task private: bool mUseMC = true; int mNThreads = 1; +#ifdef O2_WITH_ACTS + bool mUseACTS = false; +#endif o2::trk::Clusterer mClusterer; +#ifdef O2_WITH_ACTS + o2::trk::ClustererACTS mClustererACTS; +#endif }; o2::framework::DataProcessorSpec getClustererSpec(bool useMC); diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/src/ClustererSpec.cxx b/Detectors/Upgrades/ALICE3/TRK/workflow/src/ClustererSpec.cxx index 8aec63d69206b..8ce3141fc7e27 100644 --- a/Detectors/Upgrades/ALICE3/TRK/workflow/src/ClustererSpec.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/src/ClustererSpec.cxx @@ -23,6 +23,9 @@ namespace o2::trk void ClustererDPL::init(o2::framework::InitContext& ic) { mNThreads = std::max(1, ic.options().get("nthreads")); +#ifdef O2_WITH_ACTS + mUseACTS = ic.options().get("useACTS"); +#endif } void ClustererDPL::run(o2::framework::ProcessingContext& pc) @@ -48,15 +51,30 @@ void ClustererDPL::run(o2::framework::ProcessingContext& pc) } o2::base::GeometryManager::loadGeometry("o2sim_geometry.root", false, true); - mClusterer.process(digits, - rofs, - clusters, - patterns, - clusterROFs, - mUseMC ? &labels : nullptr, - clusterLabels.get(), - mc2rofs, - mUseMC ? &clusterMC2ROFs : nullptr); +#ifdef O2_WITH_ACTS + if (mUseACTS) { + mClustererACTS.process(digits, + rofs, + clusters, + patterns, + clusterROFs, + mUseMC ? &labels : nullptr, + clusterLabels.get(), + mc2rofs, + mUseMC ? &clusterMC2ROFs : nullptr); + } else +#endif + { + mClusterer.process(digits, + rofs, + clusters, + patterns, + clusterROFs, + mUseMC ? &labels : nullptr, + clusterLabels.get(), + mc2rofs, + mUseMC ? &clusterMC2ROFs : nullptr); + } pc.outputs().snapshot(o2::framework::Output{"TRK", "COMPCLUSTERS", 0}, clusters); pc.outputs().snapshot(o2::framework::Output{"TRK", "PATTERNS", 0}, patterns); diff --git a/dependencies/O2Dependencies.cmake b/dependencies/O2Dependencies.cmake index 8addb87a1a16f..b0946e2955369 100644 --- a/dependencies/O2Dependencies.cmake +++ b/dependencies/O2Dependencies.cmake @@ -113,6 +113,9 @@ set_package_properties(fmt PROPERTIES TYPE REQUIRED) find_package(nlohmann_json) set_package_properties(nlohmann_json PROPERTIES TYPE REQUIRED) +find_package(Acts) +set_package_properties(Acts PROPERTIES TYPE OPTIONAL) + find_package(Boost 1.70 COMPONENTS container thread From 1c4631b6da35729cb559ec0aefa794ccc8297fe4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Jacazio?= Date: Wed, 11 Mar 2026 10:59:22 +0100 Subject: [PATCH 03/12] UpdateTOFPosition --- .../ALICE3/IOTOF/simulation/src/Detector.cxx | 50 ++++++++++--------- 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx index d4e34c582bbed..d6bf31e40bc72 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx @@ -61,10 +61,8 @@ void Detector::configLayers(bool itof, bool otof, bool ftof, bool btof, std::str const float x2x0) { - const float radiusInnerTof = 19.f; - const float radiusOuterTof = 85.f; - const float lengthInnerTof = 124.f; - float lengthOuterTof = 680.f; + const std::pair dInnerTof = {21.f, 124.f}; // Radius and length + std::pair dOuterTof = {95.f, 680.f}; // Radius and length std::pair radiusRangeDiskTof = {15.f, 100.f}; float zForwardTof = 370.f; LOG(info) << "Configuring IOTOF layers with '" << pattern << "' pattern"; @@ -74,49 +72,55 @@ void Detector::configLayers(bool itof, bool otof, bool ftof, bool btof, std::str ftof = false; btof = false; } else if (pattern == "v3b1a") { - lengthOuterTof = 500.f; + dOuterTof.second = 500.f; zForwardTof = 270.f; radiusRangeDiskTof = {30.f, 100.f}; } else if (pattern == "v3b1b") { - lengthOuterTof = 500.f; + dOuterTof.second = 500.f; zForwardTof = 200.f; radiusRangeDiskTof = {20.f, 68.f}; } else if (pattern == "v3b2a") { - lengthOuterTof = 440.f; + dOuterTof.second = 440.f; zForwardTof = 270.f; radiusRangeDiskTof = {30.f, 120.f}; } else if (pattern == "v3b2b") { - lengthOuterTof = 440.f; + dOuterTof.second = 440.f; zForwardTof = 200.f; radiusRangeDiskTof = {20.f, 68.f}; } else if (pattern == "v3b3") { - lengthOuterTof = 580.f; + dOuterTof.second = 580.f; zForwardTof = 200.f; radiusRangeDiskTof = {20.f, 68.f}; } else { LOG(fatal) << "IOTOF layer pattern " << pattern << " not recognized, exiting"; } if (itof) { // iTOF - mITOFLayer = itofSegmented ? ITOFLayer(std::string{GeometryTGeo::getITOFLayerPattern()}, - radiusInnerTof, 0.f, lengthInnerTof, 0.f, x2x0, ITOFLayer::kBarrelSegmented, - 24, 5.42, 10.0, 10) - : ITOFLayer(std::string{GeometryTGeo::getITOFLayerPattern()}, - radiusInnerTof, 0.f, lengthInnerTof, 0.f, x2x0, ITOFLayer::kBarrel); + const std::string name = GeometryTGeo::getITOFLayerPattern(); + const int nStaves = itofSegmented ? 24 : 0; // number of staves in segmented case + const double staveWidth = itofSegmented ? 5.42 : 0.0; // cm + const double staveTiltAngle = itofSegmented ? 10.0 : 0.0; // degrees + const int modulesPerStave = itofSegmented ? 10 : 0; // number of modules per stave in segmented case + mITOFLayer = ITOFLayer(name, + dInnerTof.first, 0.f, dInnerTof.second, 0.f, x2x0, ITOFLayer::kBarrelSegmented, + nStaves, staveWidth, staveTiltAngle, modulesPerStave); } if (otof) { // oTOF - mOTOFLayer = otofSegmented ? OTOFLayer(std::string{GeometryTGeo::getOTOFLayerPattern()}, - radiusOuterTof, 0.f, lengthOuterTof, 0.f, x2x0, OTOFLayer::kBarrelSegmented, - 62, 9.74, 5.0, 54) - : OTOFLayer(std::string{GeometryTGeo::getOTOFLayerPattern()}, - radiusOuterTof, 0.f, lengthOuterTof, 0.f, x2x0, OTOFLayer::kBarrel); + const std::string name = GeometryTGeo::getOTOFLayerPattern(); + const int nStaves = otofSegmented ? 62 : 0; // number of staves in segmented case + const double staveWidth = otofSegmented ? 9.74 : 0.0; // cm + const double staveTiltAngle = otofSegmented ? 5.0 : 0.0; // degrees + const int modulesPerStave = otofSegmented ? 54 : 0; // number of modules per stave in segmented case + mOTOFLayer = OTOFLayer(name, + dOuterTof.first, 0.f, dOuterTof.second, 0.f, x2x0, OTOFLayer::kBarrelSegmented, + nStaves, staveWidth, staveTiltAngle, modulesPerStave); } if (ftof) { - mFTOFLayer = FTOFLayer(std::string{GeometryTGeo::getFTOFLayerPattern()}, - radiusRangeDiskTof.first, radiusRangeDiskTof.second, 0.f, zForwardTof, x2x0, FTOFLayer::kDisk); // fTOF + const std::string name = GeometryTGeo::getFTOFLayerPattern(); + mFTOFLayer = FTOFLayer(name, radiusRangeDiskTof.first, radiusRangeDiskTof.second, 0.f, zForwardTof, x2x0, FTOFLayer::kDisk); // fTOF } if (btof) { - mBTOFLayer = BTOFLayer(std::string{GeometryTGeo::getBTOFLayerPattern()}, - radiusRangeDiskTof.first, radiusRangeDiskTof.second, 0.f, -zForwardTof, x2x0, BTOFLayer::kDisk); // bTOF + const std::string name = GeometryTGeo::getBTOFLayerPattern(); + mBTOFLayer = BTOFLayer(name, radiusRangeDiskTof.first, radiusRangeDiskTof.second, 0.f, -zForwardTof, x2x0, BTOFLayer::kDisk); // bTOF } } From 9a9b8b22b543e84715e9c605fa9b5aa0a88fb469 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Jacazio?= Date: Fri, 13 Mar 2026 18:19:35 +0100 Subject: [PATCH 04/12] Update ACTS clusterer --- Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Layer.cxx | 9 +++++---- .../ALICE3/TRK/reconstruction/src/ClustererACTS.cxx | 3 +++ .../Upgrades/ALICE3/TRK/workflow/src/ClustererSpec.cxx | 7 ++++++- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Layer.cxx b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Layer.cxx index 1744e4c4510bb..0d0983958c46f 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Layer.cxx +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Layer.cxx @@ -287,14 +287,15 @@ void OTOFLayer::createLayer(TGeoVolume* motherVolume) case kBarrelSegmented: { // First we create the volume for the whole layer, which will be used as mother volume for the segments const double avgRadius = 0.5 * (mInnerRadius + mOuterRadius); - TGeoTube* layer = new TGeoTube(mInnerRadius, mOuterRadius, mZLength / 2); + const double staveSizeX = mStaves.second; // cm + const double staveSizeY = mOuterRadius - mInnerRadius; // cm + const double staveSizeZ = mZLength; // cm + const double deltaForTilt = 0.5 * (std::sin(TMath::DegToRad() * mTiltAngle) * staveSizeX + std::cos(TMath::DegToRad() * mTiltAngle) * staveSizeY); // we increase the size of the layer to account for the tilt of the staves + TGeoTube* layer = new TGeoTube(mInnerRadius - deltaForTilt, mOuterRadius + deltaForTilt, mZLength / 2); TGeoVolume* layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir); setLayerStyle(layerVol); // Now we create the volume for a single stave - const double staveSizeX = mStaves.second; // cm - const double staveSizeY = mOuterRadius - mInnerRadius; // cm - const double staveSizeZ = mZLength; // cm TGeoBBox* stave = new TGeoBBox(staveSizeX * 0.5, staveSizeY * 0.5, staveSizeZ * 0.5); TGeoVolume* staveVol = new TGeoVolume(staveName, stave, medAir); setStaveStyle(staveVol); diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/ClustererACTS.cxx b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/ClustererACTS.cxx index 711480747ea37..984f5207bc5e2 100644 --- a/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/ClustererACTS.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/ClustererACTS.cxx @@ -21,6 +21,7 @@ using namespace o2::trk; +// Data formats for ACTS interface struct Cell2D { Cell2D(int rowv, int colv) : row(rowv), col(colv) {} int row, col; @@ -196,6 +197,8 @@ void ClustererACTS::process(gsl::span digits, collection, Acts::Ccl::DefaultConnect(false)); + LOG(debug) << "Clustering with ACTS, found " << collection.size() << " clusters in ROF " << iROF; + // Sort digit indices within this ROF by (chipID, col, row) so we can process // chip by chip, column by column -- the same ordering the ALPIDE scanner expects. mSortIdx.resize(nEntries); diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/src/ClustererSpec.cxx b/Detectors/Upgrades/ALICE3/TRK/workflow/src/ClustererSpec.cxx index 8ce3141fc7e27..568fe8b5dee29 100644 --- a/Detectors/Upgrades/ALICE3/TRK/workflow/src/ClustererSpec.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/src/ClustererSpec.cxx @@ -111,7 +111,12 @@ o2::framework::DataProcessorSpec getClustererSpec(bool useMC) inputs, outputs, o2::framework::AlgorithmSpec{o2::framework::adaptFromTask(useMC)}, - o2::framework::Options{{"nthreads", o2::framework::VariantType::Int, 1, {"Number of clustering threads"}}}}; + o2::framework::Options{{"nthreads", o2::framework::VariantType::Int, 1, {"Number of clustering threads"}} +#ifdef O2_WITH_ACTS + , + {"useACTS", o2::framework::VariantType::Bool, false, {"Use ACTS for clustering"}} +#endif + }}; } } // namespace o2::trk From ac196c39b588d832794ad08a0cf152b9d3b0ba9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Jacazio?= Date: Fri, 13 Mar 2026 19:05:49 +0100 Subject: [PATCH 05/12] Add ACTS clusterer --- .../TRK/reconstruction/src/ClustererACTS.cxx | 170 +++++++++++++++--- .../ALICE3/TRK/workflow/src/ClustererSpec.cxx | 2 + 2 files changed, 147 insertions(+), 25 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/ClustererACTS.cxx b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/ClustererACTS.cxx index 984f5207bc5e2..9b17ec7421ded 100644 --- a/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/ClustererACTS.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/ClustererACTS.cxx @@ -14,9 +14,11 @@ #include "TRKReconstruction/ClustererACTS.h" #include "TRKBase/GeometryTGeo.h" +#include "DataFormatsITSMFT/ClusterPattern.h" #include #include +#include #include using namespace o2::trk; @@ -176,29 +178,6 @@ void ClustererACTS::process(gsl::span digits, continue; } - // template > - // requires(GridDim == 1 || GridDim == 2) - // void createClusters(Acts::Ccl::ClusteringData& data, - // CellCollection& cells, - // ClusterCollection& clusters, - // Connect&& connect = Connect()); - using Cell = Cell2D; - using CellCollection = std::vector; - using Cluster = Cluster2D; - using ClusterCollection = std::vector; - CellCollection cells; - Acts::Ccl::ClusteringData data; - ClusterCollection collection; - - Acts::Ccl::createClusters(data, - cells, - collection, - Acts::Ccl::DefaultConnect(false)); - - LOG(debug) << "Clustering with ACTS, found " << collection.size() << " clusters in ROF " << iROF; - // Sort digit indices within this ROF by (chipID, col, row) so we can process // chip by chip, column by column -- the same ordering the ALPIDE scanner expects. mSortIdx.resize(nEntries); @@ -215,6 +194,17 @@ void ClustererACTS::process(gsl::span digits, return da.getRow() < db.getRow(); }); + // Type aliases for ACTS clustering + using Cell = Cell2D; + using CellCollection = std::vector; + using Cluster = Cluster2D; + using ClusterCollection = std::vector; + static constexpr int GridDim = 2; ///< Dimensionality of the clustering grid (2D for pixel detectors) + + CellCollection cells; // Input collection of cells (pixels) to be clustered + Acts::Ccl::ClusteringData data; // Internal data structure used by ACTS clustering algorithm + ClusterCollection clsCollection; // Output collection of clusters found by the algorithm + // Process one chip at a time int sliceStart = 0; while (sliceStart < nEntries) { @@ -225,9 +215,139 @@ void ClustererACTS::process(gsl::span digits, } const int chipN = sliceStart - chipFirst; - mThread->processChip(digits, chipFirst, chipN, &clusters, &patterns, digitLabels, clusterLabels, geom); - } + // Fill cells from digits for this chip + cells.clear(); + data.clear(); + clsCollection.clear(); + cells.reserve(chipN); + for (int i = chipFirst; i < chipFirst + chipN; ++i) { + const auto& digit = digits[mSortIdx[i]]; + cells.emplace_back(digit.getRow(), digit.getColumn()); + } + + LOG(debug) << "Clustering with ACTS on chip " << chipID << " " << cells.size() << " digits"; + Acts::Ccl::createClusters(data, + cells, + clsCollection, + Acts::Ccl::DefaultConnect(false)); + + LOG(debug) << " found " << clsCollection.size() << " clusters"; + + // Convert ACTS clusters to O2 clusters + for (const auto& actsCluster : clsCollection) { + if (actsCluster.cells.empty()) { + continue; + } + + // Calculate bounding box + uint16_t rowMin = static_cast(actsCluster.cells[0].row); + uint16_t rowMax = rowMin; + uint16_t colMin = static_cast(actsCluster.cells[0].col); + uint16_t colMax = colMin; + + for (const auto& cell : actsCluster.cells) { + rowMin = std::min(rowMin, static_cast(cell.row)); + rowMax = std::max(rowMax, static_cast(cell.row)); + colMin = std::min(colMin, static_cast(cell.col)); + colMax = std::max(colMax, static_cast(cell.col)); + } + + const uint16_t rowSpan = rowMax - rowMin + 1; + const uint16_t colSpan = colMax - colMin + 1; + + // Check if cluster needs splitting (too large for pattern encoding) + const bool isHuge = rowSpan > o2::itsmft::ClusterPattern::MaxRowSpan || + colSpan > o2::itsmft::ClusterPattern::MaxColSpan; + + if (isHuge) { + // Split huge cluster into MaxRowSpan x MaxColSpan tiles + LOG(warning) << "Splitting huge TRK cluster: chipID " << chipID + << ", rows " << rowMin << ":" << rowMax + << " cols " << colMin << ":" << colMax; + + for (uint16_t tileColMin = colMin; tileColMin <= colMax; + tileColMin = static_cast(tileColMin + o2::itsmft::ClusterPattern::MaxColSpan)) { + uint16_t tileColMax = std::min(colMax, static_cast(tileColMin + o2::itsmft::ClusterPattern::MaxColSpan - 1)); + + for (uint16_t tileRowMin = rowMin; tileRowMin <= rowMax; + tileRowMin = static_cast(tileRowMin + o2::itsmft::ClusterPattern::MaxRowSpan)) { + uint16_t tileRowMax = std::min(rowMax, static_cast(tileRowMin + o2::itsmft::ClusterPattern::MaxRowSpan - 1)); + + // Collect cells in this tile + std::vector> tileCells; + for (const auto& cell : actsCluster.cells) { + uint16_t r = static_cast(cell.row); + uint16_t c = static_cast(cell.col); + if (r >= tileRowMin && r <= tileRowMax && c >= tileColMin && c <= tileColMax) { + tileCells.emplace_back(r, c); + } + } + + if (tileCells.empty()) { + continue; + } + + uint16_t tileRowSpan = tileRowMax - tileRowMin + 1; + uint16_t tileColSpan = tileColMax - tileColMin + 1; + + // Encode pattern for this tile + std::array patt{}; + for (const auto& [r, c] : tileCells) { + uint32_t ir = r - tileRowMin; + uint32_t ic = c - tileColMin; + int nbit = ir * tileColSpan + ic; + patt[nbit >> 3] |= (0x1 << (7 - (nbit % 8))); + } + patterns.emplace_back(static_cast(tileRowSpan)); + patterns.emplace_back(static_cast(tileColSpan)); + const int nBytes = (tileRowSpan * tileColSpan + 7) / 8; + patterns.insert(patterns.end(), patt.begin(), patt.begin() + nBytes); + + // Create O2 cluster for this tile + o2::trk::Cluster cluster; + cluster.chipID = chipID; + cluster.row = tileRowMin; + cluster.col = tileColMin; + cluster.size = static_cast(tileCells.size()); + if (geom) { + cluster.subDetID = static_cast(geom->getSubDetID(chipID)); + cluster.layer = static_cast(geom->getLayer(chipID)); + cluster.disk = static_cast(geom->getDisk(chipID)); + } + clusters.emplace_back(cluster); + } + } + } else { + // Normal cluster - encode directly + std::array patt{}; + for (const auto& cell : actsCluster.cells) { + uint32_t ir = static_cast(cell.row - rowMin); + uint32_t ic = static_cast(cell.col - colMin); + int nbit = ir * colSpan + ic; + patt[nbit >> 3] |= (0x1 << (7 - (nbit % 8))); + } + patterns.emplace_back(static_cast(rowSpan)); + patterns.emplace_back(static_cast(colSpan)); + const int nBytes = (rowSpan * colSpan + 7) / 8; + patterns.insert(patterns.end(), patt.begin(), patt.begin() + nBytes); + + // Create O2 cluster + o2::trk::Cluster cluster; + cluster.chipID = chipID; + cluster.row = rowMin; + cluster.col = colMin; + cluster.size = static_cast(actsCluster.cells.size()); + if (geom) { + cluster.subDetID = static_cast(geom->getSubDetID(chipID)); + cluster.layer = static_cast(geom->getLayer(chipID)); + cluster.disk = static_cast(geom->getDisk(chipID)); + } + clusters.emplace_back(cluster); + } + } + LOG(debug) << " clusterization of chip " << chipID << " completed!"; + } clusterROFs.emplace_back(inROF.getBCData(), inROF.getROFrame(), outFirst, static_cast(clusters.size()) - outFirst); } diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/src/ClustererSpec.cxx b/Detectors/Upgrades/ALICE3/TRK/workflow/src/ClustererSpec.cxx index 568fe8b5dee29..5d9ac463b3f54 100644 --- a/Detectors/Upgrades/ALICE3/TRK/workflow/src/ClustererSpec.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/src/ClustererSpec.cxx @@ -53,6 +53,7 @@ void ClustererDPL::run(o2::framework::ProcessingContext& pc) #ifdef O2_WITH_ACTS if (mUseACTS) { + LOG(info) << "Running TRKClusterer with ACTS"; mClustererACTS.process(digits, rofs, clusters, @@ -65,6 +66,7 @@ void ClustererDPL::run(o2::framework::ProcessingContext& pc) } else #endif { + LOG(info) << "Running TRKClusterer"; mClusterer.process(digits, rofs, clusters, From 8257be3d0a87eb7e5bcc1b7a4fc6ed51bce0bc79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Jacazio?= Date: Fri, 13 Mar 2026 19:26:56 +0100 Subject: [PATCH 06/12] Update --- .../TRK/reconstruction/src/ClustererACTS.cxx | 35 +++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/ClustererACTS.cxx b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/ClustererACTS.cxx index 9b17ec7421ded..04e5e35acbc68 100644 --- a/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/ClustererACTS.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/ClustererACTS.cxx @@ -25,8 +25,9 @@ using namespace o2::trk; // Data formats for ACTS interface struct Cell2D { - Cell2D(int rowv, int colv) : row(rowv), col(colv) {} + Cell2D(int rowv, int colv, uint32_t digIdx = 0) : row(rowv), col(colv), digitIdx(digIdx) {} int row, col; + uint32_t digitIdx; ///< Index of the original digit (for MC label retrieval) Acts::Ccl::Label label{Acts::Ccl::NO_LABEL}; }; @@ -222,7 +223,7 @@ void ClustererACTS::process(gsl::span digits, cells.reserve(chipN); for (int i = chipFirst; i < chipFirst + chipN; ++i) { const auto& digit = digits[mSortIdx[i]]; - cells.emplace_back(digit.getRow(), digit.getColumn()); + cells.emplace_back(digit.getRow(), digit.getColumn(), mSortIdx[i]); } LOG(debug) << "Clustering with ACTS on chip " << chipID << " " << cells.size() << " digits"; @@ -303,6 +304,23 @@ void ClustererACTS::process(gsl::span digits, const int nBytes = (tileRowSpan * tileColSpan + 7) / 8; patterns.insert(patterns.end(), patt.begin(), patt.begin() + nBytes); + // Handle MC labels for this tile + if (clusterLabels && digitLabels) { + const auto clsIdx = static_cast(clusters.size()); + for (const auto& cell : actsCluster.cells) { + uint16_t r = static_cast(cell.row); + uint16_t c = static_cast(cell.col); + if (r >= tileRowMin && r <= tileRowMax && c >= tileColMin && c <= tileColMax) { + if (cell.digitIdx < digitLabels->getIndexedSize()) { + const auto& lbls = digitLabels->getLabels(cell.digitIdx); + for (const auto& lbl : lbls) { + clusterLabels->addElement(clsIdx, lbl); + } + } + } + } + } + // Create O2 cluster for this tile o2::trk::Cluster cluster; cluster.chipID = chipID; @@ -331,6 +349,19 @@ void ClustererACTS::process(gsl::span digits, const int nBytes = (rowSpan * colSpan + 7) / 8; patterns.insert(patterns.end(), patt.begin(), patt.begin() + nBytes); + // Handle MC labels + if (clusterLabels && digitLabels) { + const auto clsIdx = static_cast(clusters.size()); + for (const auto& cell : actsCluster.cells) { + if (cell.digitIdx < digitLabels->getIndexedSize()) { + const auto& lbls = digitLabels->getLabels(cell.digitIdx); + for (const auto& lbl : lbls) { + clusterLabels->addElement(clsIdx, lbl); + } + } + } + } + // Create O2 cluster o2::trk::Cluster cluster; cluster.chipID = chipID; From f65cbf71a7b56c6a51b733fe81fbcf40c97ea0bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Jacazio?= Date: Fri, 13 Mar 2026 19:28:47 +0100 Subject: [PATCH 07/12] Update --- Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx | 2 +- .../Upgrades/ALICE3/TRK/reconstruction/src/ClustererACTS.cxx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx index d6bf31e40bc72..b45d3861fa691 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx @@ -62,7 +62,7 @@ void Detector::configLayers(bool itof, bool otof, bool ftof, bool btof, std::str { const std::pair dInnerTof = {21.f, 124.f}; // Radius and length - std::pair dOuterTof = {95.f, 680.f}; // Radius and length + std::pair dOuterTof = {95.f, 680.f}; // Radius and length std::pair radiusRangeDiskTof = {15.f, 100.f}; float zForwardTof = 370.f; LOG(info) << "Configuring IOTOF layers with '" << pattern << "' pattern"; diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/ClustererACTS.cxx b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/ClustererACTS.cxx index 04e5e35acbc68..0cf7c26e0ea41 100644 --- a/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/ClustererACTS.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/ClustererACTS.cxx @@ -27,7 +27,7 @@ using namespace o2::trk; struct Cell2D { Cell2D(int rowv, int colv, uint32_t digIdx = 0) : row(rowv), col(colv), digitIdx(digIdx) {} int row, col; - uint32_t digitIdx; ///< Index of the original digit (for MC label retrieval) + uint32_t digitIdx; ///< Index of the original digit (for MC label retrieval) Acts::Ccl::Label label{Acts::Ccl::NO_LABEL}; }; From 260cd0d2b1f6b3fa8787d22e231476ad64465018 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Jacazio?= Date: Fri, 13 Mar 2026 20:05:47 +0100 Subject: [PATCH 08/12] Add ACTS tracker --- .../ALICE3/TRK/reconstruction/CMakeLists.txt | 4 +- .../include/TRKReconstruction/TrackerACTS.h | 158 ++++++++ .../TRK/reconstruction/src/TrackerACTS.cxx | 370 ++++++++++++++++++ 3 files changed, 531 insertions(+), 1 deletion(-) create mode 100644 Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/TrackerACTS.h create mode 100644 Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TrackerACTS.cxx diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/CMakeLists.txt b/Detectors/Upgrades/ALICE3/TRK/reconstruction/CMakeLists.txt index 81a75e209124a..59a7f47955938 100644 --- a/Detectors/Upgrades/ALICE3/TRK/reconstruction/CMakeLists.txt +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/CMakeLists.txt @@ -18,6 +18,7 @@ o2_add_library(TRKReconstruction SOURCES src/TimeFrame.cxx src/Clusterer.cxx $<$:src/ClustererACTS.cxx> + $<$:src/TrackerACTS.cxx> PUBLIC_LINK_LIBRARIES O2::ITStracking O2::GPUCommon @@ -45,7 +46,8 @@ set(dictHeaders include/TRKReconstruction/TimeFrame.h include/TRKReconstruction/Clusterer.h) if(Acts_FOUND) - list(APPEND dictHeaders include/TRKReconstruction/ClustererACTS.h) + list(APPEND dictHeaders include/TRKReconstruction/ClustererACTS.h + include/TRKReconstruction/TrackerACTS.h) endif() o2_target_root_dictionary(TRKReconstruction diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/TrackerACTS.h b/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/TrackerACTS.h new file mode 100644 index 0000000000000..f1d892e3bb1e2 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/TrackerACTS.h @@ -0,0 +1,158 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// +/// \file TrackerACTS.h +/// \brief +/// + +#ifndef ALICE3_INCLUDE_TRACKERACTS_H_ +#define ALICE3_INCLUDE_TRACKERACTS_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "ITStracking/Configuration.h" +#include "CommonConstants/MathConstants.h" +#include "ITStracking/Definitions.h" +#include "ITStracking/MathUtils.h" +#include "ITStracking/TimeFrame.h" +#include "ITStracking/TrackerTraits.h" +#include "ITStracking/Road.h" +#include "ITStracking/BoundedAllocator.h" + +#include "DataFormatsITS/TrackITS.h" +#include "SimulationDataFormat/MCCompLabel.h" + +namespace o2 +{ + +namespace gpu +{ +class GPUChainITS; +} +namespace its +{ + +template +class Tracker +{ + using LogFunc = std::function; + + public: + Tracker(TrackerTraits* traits); + + void adoptTimeFrame(TimeFrame& tf); + + void clustersToTracks( + const LogFunc& = [](const std::string& s) { std::cout << s << '\n'; }, + const LogFunc& = [](const std::string& s) { std::cerr << s << '\n'; }); + + void setParameters(const std::vector& p) { mTrkParams = p; } + void setMemoryPool(std::shared_ptr pool) { mMemoryPool = pool; } + std::vector& getParameters() { return mTrkParams; } + void setBz(float bz) { mTraits->setBz(bz); } + bool isMatLUT() const { return mTraits->isMatLUT(); } + void setNThreads(int n, std::shared_ptr& arena) { mTraits->setNThreads(n, arena); } + void printSummary() const; + void computeTracksMClabels(); + + private: + void initialiseTimeFrame(int iteration) { mTraits->initialiseTimeFrame(iteration); } + void computeTracklets(int iteration, int iROFslice, int iVertex) { mTraits->computeLayerTracklets(iteration, iROFslice, iVertex); } + void computeCells(int iteration) { mTraits->computeLayerCells(iteration); } + void findCellsNeighbours(int iteration) { mTraits->findCellsNeighbours(iteration); } + void findRoads(int iteration) { mTraits->findRoads(iteration); } + void findShortPrimaries() { mTraits->findShortPrimaries(); } + void extendTracks(int iteration) { mTraits->extendTracks(iteration); } + + // MC interaction + void computeRoadsMClabels(); + void rectifyClusterIndices(); + + template + float evaluateTask(void (Tracker::*task)(T...), std::string_view taskName, int iteration, LogFunc logger, F&&... args); + + TrackerTraits* mTraits = nullptr; /// Observer pointer, not owned by this class + TimeFrame* mTimeFrame = nullptr; /// Observer pointer, not owned by this class + + std::vector mTrkParams; + o2::gpu::GPUChainITS* mRecoChain = nullptr; + + unsigned int mNumberOfDroppedTFs{0}; + unsigned int mTimeFrameCounter{0}; + double mTotalTime{0}; + std::shared_ptr mMemoryPool; + + enum State { + TFInit = 0, + Trackleting, + Celling, + Neighbouring, + Roading, + NStates, + }; + State mCurState{TFInit}; + static constexpr std::array StateNames{"TimeFrame initialisation", "Tracklet finding", "Cell finding", "Neighbour finding", "Road finding"}; +}; + +template +template +float Tracker::evaluateTask(void (Tracker::*task)(T...), std::string_view taskName, int iteration, LogFunc logger, F&&... args) +{ + float diff{0.f}; + + if constexpr (constants::DoTimeBenchmarks) { + auto start = std::chrono::high_resolution_clock::now(); + (this->*task)(std::forward(args)...); + auto end = std::chrono::high_resolution_clock::now(); + + std::chrono::duration diff_t{end - start}; + diff = diff_t.count(); + + std::stringstream sstream; + if (taskName.empty()) { + sstream << diff << "\t"; + } else { + sstream << std::setw(2) << " - " << taskName << " completed in: " << diff << " ms"; + } + logger(sstream.str()); + + if (mTrkParams[0].SaveTimeBenchmarks) { + std::string taskNameStr(taskName); + std::transform(taskNameStr.begin(), taskNameStr.end(), taskNameStr.begin(), + [](unsigned char c) { return std::tolower(c); }); + std::replace(taskNameStr.begin(), taskNameStr.end(), ' ', '_'); + if (std::ofstream file{"its_time_benchmarks.txt", std::ios::app}) { + file << "trk:" << iteration << '\t' << taskNameStr << '\t' << diff << '\n'; + } + } + + } else { + (this->*task)(std::forward(args)...); + } + + return diff; +} + +} // namespace its +} // namespace o2 + +#endif /* ALICE3_INCLUDE_TRACKER_H_ */ diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TrackerACTS.cxx b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TrackerACTS.cxx new file mode 100644 index 0000000000000..256c2273cf765 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TrackerACTS.cxx @@ -0,0 +1,370 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// +/// \file TrackerACTS.cxx +/// \brief +/// + +#include "ITStracking/Tracker.h" + +#include "ITStracking/BoundedAllocator.h" +#include "ITStracking/Cell.h" +#include "ITStracking/Constants.h" +#include "ITStracking/IndexTableUtils.h" +#include "ITStracking/Tracklet.h" +#include "ITStracking/TrackerTraits.h" +#include "ITStracking/TrackingConfigParam.h" + +#include "ReconstructionDataFormats/Track.h" +#include +#include +#include +#include +#include + +namespace o2::trk +{ +using o2::its::constants::GB; + +template +Tracker::Tracker(TrackerTraits* traits) : mTraits(traits) +{ + /// Initialise standard configuration with 1 iteration + mTrkParams.resize(1); + if (traits->isGPU()) { + ITSGpuTrackingParamConfig::Instance().maybeOverride(); + ITSGpuTrackingParamConfig::Instance().printKeyValues(true, true); + } +} + +template +void Tracker::clustersToTracks(const LogFunc& logger, const LogFunc& error) +{ + LogFunc evalLog = [](const std::string&) {}; + + double total{0}; + mTraits->updateTrackingParameters(mTrkParams); + int maxNvertices{-1}; + if (mTrkParams[0].PerPrimaryVertexProcessing) { + for (int iROF{0}; iROF < mTimeFrame->getNrof(); ++iROF) { + int minRof = o2::gpu::CAMath::Max(0, iROF - mTrkParams[0].DeltaROF); + int maxRof = o2::gpu::CAMath::Min(mTimeFrame->getNrof(), iROF + mTrkParams[0].DeltaROF); + maxNvertices = std::max(maxNvertices, (int)mTimeFrame->getPrimaryVertices(minRof, maxRof).size()); + } + } + + int iteration{0}, iROFs{0}, iVertex{0}; + auto handleException = [&](const auto& err) { + LOGP(error, "Too much memory used during {} in iteration {} in ROF span {}-{} iVtx={}: {:.2f} GB. Current limit is {:.2f} GB, check the detector status and/or the selections.", + StateNames[mCurState], iteration, iROFs, iROFs + mTrkParams[iteration].nROFsPerIterations, iVertex, + (double)mTimeFrame->getArtefactsMemory() / GB, (double)mTrkParams[iteration].MaxMemory / GB); + if (typeid(err) != typeid(std::bad_alloc)) { // only print if the exceptions is different from what is expected + LOGP(error, "Exception: {}", err.what()); + } + if (mTrkParams[iteration].DropTFUponFailure) { + mMemoryPool->print(); + mTimeFrame->wipe(); + ++mNumberOfDroppedTFs; + error("...Dropping Timeframe..."); + } else { + throw err; + } + }; + + try { + for (iteration = 0; iteration < (int)mTrkParams.size(); ++iteration) { + mMemoryPool->setMaxMemory(mTrkParams[iteration].MaxMemory); + if (iteration == 3 && mTrkParams[0].DoUPCIteration) { + mTimeFrame->swapMasks(); + } + double timeTracklets{0.}, timeCells{0.}, timeNeighbours{0.}, timeRoads{0.}; + int nTracklets{0}, nCells{0}, nNeighbours{0}, nTracks{-static_cast(mTimeFrame->getNumberOfTracks())}; + int nROFsIterations = (mTrkParams[iteration].nROFsPerIterations > 0 && !mTimeFrame->isGPU()) ? mTimeFrame->getNrof() / mTrkParams[iteration].nROFsPerIterations + bool(mTimeFrame->getNrof() % mTrkParams[iteration].nROFsPerIterations) : 1; + iVertex = std::min(maxNvertices, 0); + logger(std::format("==== ITS {} Tracking iteration {} summary ====", mTraits->getName(), iteration)); + + total += evaluateTask(&Tracker::initialiseTimeFrame, StateNames[mCurState = TFInit], iteration, logger, iteration); + do { + for (iROFs = 0; iROFs < nROFsIterations; ++iROFs) { + timeTracklets += evaluateTask(&Tracker::computeTracklets, StateNames[mCurState = Trackleting], iteration, evalLog, iteration, iROFs, iVertex); + nTracklets += mTraits->getTFNumberOfTracklets(); + float trackletsPerCluster = mTraits->getTFNumberOfClusters() > 0 ? float(mTraits->getTFNumberOfTracklets()) / float(mTraits->getTFNumberOfClusters()) : 0.f; + if (trackletsPerCluster > mTrkParams[iteration].TrackletsPerClusterLimit) { + error(std::format("Too many tracklets per cluster ({}) in iteration {} in ROF span {}-{}:, check the detector status and/or the selections. Current limit is {}", + trackletsPerCluster, iteration, iROFs, iROFs + mTrkParams[iteration].nROFsPerIterations, mTrkParams[iteration].TrackletsPerClusterLimit)); + break; + } + timeCells += evaluateTask(&Tracker::computeCells, StateNames[mCurState = Celling], iteration, evalLog, iteration); + nCells += mTraits->getTFNumberOfCells(); + float cellsPerCluster = mTraits->getTFNumberOfClusters() > 0 ? float(mTraits->getTFNumberOfCells()) / float(mTraits->getTFNumberOfClusters()) : 0.f; + if (cellsPerCluster > mTrkParams[iteration].CellsPerClusterLimit) { + error(std::format("Too many cells per cluster ({}) in iteration {} in ROF span {}-{}, check the detector status and/or the selections. Current limit is {}", + cellsPerCluster, iteration, iROFs, iROFs + mTrkParams[iteration].nROFsPerIterations, mTrkParams[iteration].CellsPerClusterLimit)); + break; + } + timeNeighbours += evaluateTask(&Tracker::findCellsNeighbours, StateNames[mCurState = Neighbouring], iteration, evalLog, iteration); + nNeighbours += mTimeFrame->getNumberOfNeighbours(); + timeRoads += evaluateTask(&Tracker::findRoads, StateNames[mCurState = Roading], iteration, evalLog, iteration); + } + } while (++iVertex < maxNvertices); + logger(std::format(" - Tracklet finding: {} tracklets found in {:.2f} ms", nTracklets, timeTracklets)); + logger(std::format(" - Cell finding: {} cells found in {:.2f} ms", nCells, timeCells)); + logger(std::format(" - Neighbours finding: {} neighbours found in {:.2f} ms", nNeighbours, timeNeighbours)); + logger(std::format(" - Track finding: {} tracks found in {:.2f} ms", nTracks + mTimeFrame->getNumberOfTracks(), timeRoads)); + total += timeTracklets + timeCells + timeNeighbours + timeRoads; + if (mTraits->supportsExtendTracks() && mTrkParams[iteration].UseTrackFollower) { + int nExtendedTracks{-mTimeFrame->mNExtendedTracks}, nExtendedClusters{-mTimeFrame->mNExtendedUsedClusters}; + auto timeExtending = evaluateTask(&Tracker::extendTracks, "Extending tracks", iteration, evalLog, iteration); + total += timeExtending; + logger(std::format(" - Extending Tracks: {} extended tracks using {} clusters found in {:.2f} ms", nExtendedTracks + mTimeFrame->mNExtendedTracks, nExtendedClusters + mTimeFrame->mNExtendedUsedClusters, timeExtending)); + } + if (mTrkParams[iteration].PrintMemory) { + mMemoryPool->print(); + } + } + if (mTraits->supportsFindShortPrimaries() && mTrkParams[0].FindShortTracks) { + auto nTracksB = mTimeFrame->getNumberOfTracks(); + total += evaluateTask(&Tracker::findShortPrimaries, "Short primaries finding", 0, logger); + auto nTracksA = mTimeFrame->getNumberOfTracks(); + logger(std::format(" `-> found {} additional tracks", nTracksA - nTracksB)); + } + if constexpr (constants::DoTimeBenchmarks) { + logger(std::format("=== TimeFrame {} processing completed in: {:.2f} ms using {} thread(s) ===", mTimeFrameCounter, total, mTraits->getNThreads())); + } + } catch (const BoundedMemoryResource::MemoryLimitExceeded& err) { + handleException(err); + return; + } catch (const std::bad_alloc& err) { + handleException(err); + return; + } catch (const std::exception& err) { + error(std::format("Uncaught exception, all bets are off... {}", err.what())); + // clear tracks explicitly since if not fatalising on exception this may contain partial output + for (int iROF{0}; iROF < mTimeFrame->getNrof(); ++iROF) { + mTimeFrame->getTracks(iROF).clear(); + } + return; + } + + if (mTimeFrame->hasMCinformation()) { + computeTracksMClabels(); + } + rectifyClusterIndices(); + ++mTimeFrameCounter; + mTotalTime += total; + + if (mTrkParams[0].PrintMemory) { + mTimeFrame->printArtefactsMemory(); + mMemoryPool->print(); + } +} + +template +void Tracker::computeRoadsMClabels() +{ + /// Moore's Voting Algorithm + if (!mTimeFrame->hasMCinformation()) { + return; + } + + mTimeFrame->initialiseRoadLabels(); + + int roadsNum{static_cast(mTimeFrame->getRoads().size())}; + + for (int iRoad{0}; iRoad < roadsNum; ++iRoad) { + + auto& currentRoad{mTimeFrame->getRoads()[iRoad]}; + std::vector> occurrences; + bool isFakeRoad{false}; + bool isFirstRoadCell{true}; + + for (int iCell{0}; iCell < mTrkParams[0].CellsPerRoad(); ++iCell) { + const int currentCellIndex{currentRoad[iCell]}; + + if (currentCellIndex == constants::UnusedIndex) { + if (isFirstRoadCell) { + continue; + } else { + break; + } + } + + const auto& currentCell{mTimeFrame->getCells()[iCell][currentCellIndex]}; + + if (isFirstRoadCell) { + + const int cl0index{mTimeFrame->getClusters()[iCell][currentCell.getFirstClusterIndex()].clusterId}; + auto cl0labs{mTimeFrame->getClusterLabels(iCell, cl0index)}; + bool found{false}; + for (size_t iOcc{0}; iOcc < occurrences.size(); ++iOcc) { + std::pair& occurrence = occurrences[iOcc]; + for (const auto& label : cl0labs) { + if (label == occurrence.first) { + ++occurrence.second; + found = true; + // break; // uncomment to stop to the first hit + } + } + } + if (!found) { + for (const auto& label : cl0labs) { + occurrences.emplace_back(label, 1); + } + } + + const int cl1index{mTimeFrame->getClusters()[iCell + 1][currentCell.getSecondClusterIndex()].clusterId}; + + const auto& cl1labs{mTimeFrame->getClusterLabels(iCell + 1, cl1index)}; + found = false; + for (size_t iOcc{0}; iOcc < occurrences.size(); ++iOcc) { + std::pair& occurrence = occurrences[iOcc]; + for (auto& label : cl1labs) { + if (label == occurrence.first) { + ++occurrence.second; + found = true; + // break; // uncomment to stop to the first hit + } + } + } + if (!found) { + for (auto& label : cl1labs) { + occurrences.emplace_back(label, 1); + } + } + + isFirstRoadCell = false; + } + + const int cl2index{mTimeFrame->getClusters()[iCell + 2][currentCell.getThirdClusterIndex()].clusterId}; + const auto& cl2labs{mTimeFrame->getClusterLabels(iCell + 2, cl2index)}; + bool found{false}; + for (size_t iOcc{0}; iOcc < occurrences.size(); ++iOcc) { + std::pair& occurrence = occurrences[iOcc]; + for (auto& label : cl2labs) { + if (label == occurrence.first) { + ++occurrence.second; + found = true; + // break; // uncomment to stop to the first hit + } + } + } + if (!found) { + for (auto& label : cl2labs) { + occurrences.emplace_back(label, 1); + } + } + } + + std::sort(occurrences.begin(), occurrences.end(), [](auto e1, auto e2) { + return e1.second > e2.second; + }); + + auto maxOccurrencesValue = occurrences[0].first; + mTimeFrame->setRoadLabel(iRoad, maxOccurrencesValue.getRawValue(), isFakeRoad); + } +} + +template +void Tracker::computeTracksMClabels() +{ + for (int iROF{0}; iROF < mTimeFrame->getNrof(); ++iROF) { + for (auto& track : mTimeFrame->getTracks(iROF)) { + std::vector> occurrences; + occurrences.clear(); + + for (int iCluster = 0; iCluster < TrackITSExt::MaxClusters; ++iCluster) { + const int index = track.getClusterIndex(iCluster); + if (index == constants::UnusedIndex) { + continue; + } + auto labels = mTimeFrame->getClusterLabels(iCluster, index); + bool found{false}; + for (size_t iOcc{0}; iOcc < occurrences.size(); ++iOcc) { + std::pair& occurrence = occurrences[iOcc]; + for (const auto& label : labels) { + if (label == occurrence.first) { + ++occurrence.second; + found = true; + // break; // uncomment to stop to the first hit + } + } + } + if (!found) { + for (const auto& label : labels) { + occurrences.emplace_back(label, 1); + } + } + } + std::sort(std::begin(occurrences), std::end(occurrences), [](auto e1, auto e2) { + return e1.second > e2.second; + }); + + auto maxOccurrencesValue = occurrences[0].first; + uint32_t pattern = track.getPattern(); + // set fake clusters pattern + for (int ic{TrackITSExt::MaxClusters}; ic--;) { + auto clid = track.getClusterIndex(ic); + if (clid != constants::UnusedIndex) { + auto labelsSpan = mTimeFrame->getClusterLabels(ic, clid); + for (const auto& currentLabel : labelsSpan) { + if (currentLabel == maxOccurrencesValue) { + pattern |= 0x1 << (16 + ic); // set bit if correct + break; + } + } + } + } + track.setPattern(pattern); + if (occurrences[0].second < track.getNumberOfClusters()) { + maxOccurrencesValue.setFakeFlag(); + } + mTimeFrame->getTracksLabel(iROF).emplace_back(maxOccurrencesValue); + } + } +} + +template +void Tracker::rectifyClusterIndices() +{ + for (int iROF{0}; iROF < mTimeFrame->getNrof(); ++iROF) { + for (auto& track : mTimeFrame->getTracks(iROF)) { + for (int iCluster = 0; iCluster < TrackITSExt::MaxClusters; ++iCluster) { + const int index = track.getClusterIndex(iCluster); + if (index != constants::UnusedIndex) { + track.setExternalClusterIndex(iCluster, mTimeFrame->getClusterExternalIndex(iCluster, index)); + } + } + } + } +} + +template +void Tracker::adoptTimeFrame(TimeFrame& tf) +{ + mTimeFrame = &tf; + mTraits->adoptTimeFrame(&tf); +} + +template +void Tracker::printSummary() const +{ + auto avgTF = mTotalTime * 1.e-3 / ((mTimeFrameCounter > 0) ? (double)mTimeFrameCounter : -1.0); + auto avgTFwithDropped = mTotalTime * 1.e-3 / (((mTimeFrameCounter + mNumberOfDroppedTFs) > 0) ? (double)(mTimeFrameCounter + mNumberOfDroppedTFs) : -1.0); + LOGP(info, "Tracker summary: Processed {} TFs (dropped {}) in TOT={:.2f} s, AVG/TF={:.2f} ({:.2f}) s", mTimeFrameCounter, mNumberOfDroppedTFs, mTotalTime * 1.e-3, avgTF, avgTFwithDropped); +} + +template class Tracker<7>; +// ALICE3 upgrade +#ifdef ENABLE_UPGRADES +template class Tracker<11>; +#endif + +} // namespace o2::trk From 4c68d44e8b8695dd37d6512275f3648eb12c5d2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Jacazio?= Date: Fri, 13 Mar 2026 20:22:30 +0100 Subject: [PATCH 09/12] add ACTS proto-tracker --- .../include/TRKReconstruction/TrackerACTS.h | 30 +++++------ .../TRK/reconstruction/src/TrackerACTS.cxx | 52 +++++++++---------- 2 files changed, 41 insertions(+), 41 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/TrackerACTS.h b/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/TrackerACTS.h index f1d892e3bb1e2..6275385455108 100644 --- a/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/TrackerACTS.h +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/TrackerACTS.h @@ -48,26 +48,26 @@ namespace gpu { class GPUChainITS; } -namespace its +namespace trk { template -class Tracker +class TrackerACTS { using LogFunc = std::function; public: - Tracker(TrackerTraits* traits); + TrackerACTS(o2::its::TrackerTraits* traits); - void adoptTimeFrame(TimeFrame& tf); + void adoptTimeFrame(o2::its::TimeFrame& tf); void clustersToTracks( const LogFunc& = [](const std::string& s) { std::cout << s << '\n'; }, const LogFunc& = [](const std::string& s) { std::cerr << s << '\n'; }); - void setParameters(const std::vector& p) { mTrkParams = p; } - void setMemoryPool(std::shared_ptr pool) { mMemoryPool = pool; } - std::vector& getParameters() { return mTrkParams; } + void setParameters(const std::vector& p) { mTrkParams = p; } + void setMemoryPool(std::shared_ptr pool) { mMemoryPool = pool; } + std::vector& getParameters() { return mTrkParams; } void setBz(float bz) { mTraits->setBz(bz); } bool isMatLUT() const { return mTraits->isMatLUT(); } void setNThreads(int n, std::shared_ptr& arena) { mTraits->setNThreads(n, arena); } @@ -88,18 +88,18 @@ class Tracker void rectifyClusterIndices(); template - float evaluateTask(void (Tracker::*task)(T...), std::string_view taskName, int iteration, LogFunc logger, F&&... args); + float evaluateTask(void (TrackerACTS::*task)(T...), std::string_view taskName, int iteration, LogFunc logger, F&&... args); - TrackerTraits* mTraits = nullptr; /// Observer pointer, not owned by this class - TimeFrame* mTimeFrame = nullptr; /// Observer pointer, not owned by this class + o2::its::TrackerTraits* mTraits = nullptr; /// Observer pointer, not owned by this class + o2::its::TimeFrame* mTimeFrame = nullptr; /// Observer pointer, not owned by this class - std::vector mTrkParams; + std::vector mTrkParams; o2::gpu::GPUChainITS* mRecoChain = nullptr; unsigned int mNumberOfDroppedTFs{0}; unsigned int mTimeFrameCounter{0}; double mTotalTime{0}; - std::shared_ptr mMemoryPool; + std::shared_ptr mMemoryPool; enum State { TFInit = 0, @@ -115,11 +115,11 @@ class Tracker template template -float Tracker::evaluateTask(void (Tracker::*task)(T...), std::string_view taskName, int iteration, LogFunc logger, F&&... args) +float TrackerACTS::evaluateTask(void (TrackerACTS::*task)(T...), std::string_view taskName, int iteration, LogFunc logger, F&&... args) { float diff{0.f}; - if constexpr (constants::DoTimeBenchmarks) { + if constexpr (o2::its::constants::DoTimeBenchmarks) { auto start = std::chrono::high_resolution_clock::now(); (this->*task)(std::forward(args)...); auto end = std::chrono::high_resolution_clock::now(); @@ -152,7 +152,7 @@ float Tracker::evaluateTask(void (Tracker::*task)(T...), std:: return diff; } -} // namespace its +} // namespace trk } // namespace o2 #endif /* ALICE3_INCLUDE_TRACKER_H_ */ diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TrackerACTS.cxx b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TrackerACTS.cxx index 256c2273cf765..f8cacca28f20a 100644 --- a/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TrackerACTS.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TrackerACTS.cxx @@ -13,7 +13,7 @@ /// \brief /// -#include "ITStracking/Tracker.h" +#include "TRKReconstruction/TrackerACTS.h" #include "ITStracking/BoundedAllocator.h" #include "ITStracking/Cell.h" @@ -35,18 +35,18 @@ namespace o2::trk using o2::its::constants::GB; template -Tracker::Tracker(TrackerTraits* traits) : mTraits(traits) +TrackerACTS::TrackerACTS(o2::its::TrackerTraits* traits) : mTraits(traits) { /// Initialise standard configuration with 1 iteration mTrkParams.resize(1); if (traits->isGPU()) { - ITSGpuTrackingParamConfig::Instance().maybeOverride(); - ITSGpuTrackingParamConfig::Instance().printKeyValues(true, true); + o2::its::ITSGpuTrackingParamConfig::Instance().maybeOverride(); + o2::its::ITSGpuTrackingParamConfig::Instance().printKeyValues(true, true); } } template -void Tracker::clustersToTracks(const LogFunc& logger, const LogFunc& error) +void TrackerACTS::clustersToTracks(const LogFunc& logger, const LogFunc& error) { LogFunc evalLog = [](const std::string&) {}; @@ -91,10 +91,10 @@ void Tracker::clustersToTracks(const LogFunc& logger, const LogFunc& er iVertex = std::min(maxNvertices, 0); logger(std::format("==== ITS {} Tracking iteration {} summary ====", mTraits->getName(), iteration)); - total += evaluateTask(&Tracker::initialiseTimeFrame, StateNames[mCurState = TFInit], iteration, logger, iteration); + total += evaluateTask(&TrackerACTS::initialiseTimeFrame, StateNames[mCurState = TFInit], iteration, logger, iteration); do { for (iROFs = 0; iROFs < nROFsIterations; ++iROFs) { - timeTracklets += evaluateTask(&Tracker::computeTracklets, StateNames[mCurState = Trackleting], iteration, evalLog, iteration, iROFs, iVertex); + timeTracklets += evaluateTask(&TrackerACTS::computeTracklets, StateNames[mCurState = Trackleting], iteration, evalLog, iteration, iROFs, iVertex); nTracklets += mTraits->getTFNumberOfTracklets(); float trackletsPerCluster = mTraits->getTFNumberOfClusters() > 0 ? float(mTraits->getTFNumberOfTracklets()) / float(mTraits->getTFNumberOfClusters()) : 0.f; if (trackletsPerCluster > mTrkParams[iteration].TrackletsPerClusterLimit) { @@ -102,7 +102,7 @@ void Tracker::clustersToTracks(const LogFunc& logger, const LogFunc& er trackletsPerCluster, iteration, iROFs, iROFs + mTrkParams[iteration].nROFsPerIterations, mTrkParams[iteration].TrackletsPerClusterLimit)); break; } - timeCells += evaluateTask(&Tracker::computeCells, StateNames[mCurState = Celling], iteration, evalLog, iteration); + timeCells += evaluateTask(&TrackerACTS::computeCells, StateNames[mCurState = Celling], iteration, evalLog, iteration); nCells += mTraits->getTFNumberOfCells(); float cellsPerCluster = mTraits->getTFNumberOfClusters() > 0 ? float(mTraits->getTFNumberOfCells()) / float(mTraits->getTFNumberOfClusters()) : 0.f; if (cellsPerCluster > mTrkParams[iteration].CellsPerClusterLimit) { @@ -110,9 +110,9 @@ void Tracker::clustersToTracks(const LogFunc& logger, const LogFunc& er cellsPerCluster, iteration, iROFs, iROFs + mTrkParams[iteration].nROFsPerIterations, mTrkParams[iteration].CellsPerClusterLimit)); break; } - timeNeighbours += evaluateTask(&Tracker::findCellsNeighbours, StateNames[mCurState = Neighbouring], iteration, evalLog, iteration); + timeNeighbours += evaluateTask(&TrackerACTS::findCellsNeighbours, StateNames[mCurState = Neighbouring], iteration, evalLog, iteration); nNeighbours += mTimeFrame->getNumberOfNeighbours(); - timeRoads += evaluateTask(&Tracker::findRoads, StateNames[mCurState = Roading], iteration, evalLog, iteration); + timeRoads += evaluateTask(&TrackerACTS::findRoads, StateNames[mCurState = Roading], iteration, evalLog, iteration); } } while (++iVertex < maxNvertices); logger(std::format(" - Tracklet finding: {} tracklets found in {:.2f} ms", nTracklets, timeTracklets)); @@ -122,7 +122,7 @@ void Tracker::clustersToTracks(const LogFunc& logger, const LogFunc& er total += timeTracklets + timeCells + timeNeighbours + timeRoads; if (mTraits->supportsExtendTracks() && mTrkParams[iteration].UseTrackFollower) { int nExtendedTracks{-mTimeFrame->mNExtendedTracks}, nExtendedClusters{-mTimeFrame->mNExtendedUsedClusters}; - auto timeExtending = evaluateTask(&Tracker::extendTracks, "Extending tracks", iteration, evalLog, iteration); + auto timeExtending = evaluateTask(&TrackerACTS::extendTracks, "Extending tracks", iteration, evalLog, iteration); total += timeExtending; logger(std::format(" - Extending Tracks: {} extended tracks using {} clusters found in {:.2f} ms", nExtendedTracks + mTimeFrame->mNExtendedTracks, nExtendedClusters + mTimeFrame->mNExtendedUsedClusters, timeExtending)); } @@ -132,14 +132,14 @@ void Tracker::clustersToTracks(const LogFunc& logger, const LogFunc& er } if (mTraits->supportsFindShortPrimaries() && mTrkParams[0].FindShortTracks) { auto nTracksB = mTimeFrame->getNumberOfTracks(); - total += evaluateTask(&Tracker::findShortPrimaries, "Short primaries finding", 0, logger); + total += evaluateTask(&TrackerACTS::findShortPrimaries, "Short primaries finding", 0, logger); auto nTracksA = mTimeFrame->getNumberOfTracks(); logger(std::format(" `-> found {} additional tracks", nTracksA - nTracksB)); } - if constexpr (constants::DoTimeBenchmarks) { + if constexpr (o2::its::constants::DoTimeBenchmarks) { logger(std::format("=== TimeFrame {} processing completed in: {:.2f} ms using {} thread(s) ===", mTimeFrameCounter, total, mTraits->getNThreads())); } - } catch (const BoundedMemoryResource::MemoryLimitExceeded& err) { + } catch (const o2::its::BoundedMemoryResource::MemoryLimitExceeded& err) { handleException(err); return; } catch (const std::bad_alloc& err) { @@ -168,7 +168,7 @@ void Tracker::clustersToTracks(const LogFunc& logger, const LogFunc& er } template -void Tracker::computeRoadsMClabels() +void TrackerACTS::computeRoadsMClabels() { /// Moore's Voting Algorithm if (!mTimeFrame->hasMCinformation()) { @@ -189,7 +189,7 @@ void Tracker::computeRoadsMClabels() for (int iCell{0}; iCell < mTrkParams[0].CellsPerRoad(); ++iCell) { const int currentCellIndex{currentRoad[iCell]}; - if (currentCellIndex == constants::UnusedIndex) { + if (currentCellIndex == o2::its::constants::UnusedIndex) { if (isFirstRoadCell) { continue; } else { @@ -273,7 +273,7 @@ void Tracker::computeRoadsMClabels() } template -void Tracker::computeTracksMClabels() +void TrackerACTS::computeTracksMClabels() { for (int iROF{0}; iROF < mTimeFrame->getNrof(); ++iROF) { for (auto& track : mTimeFrame->getTracks(iROF)) { @@ -282,7 +282,7 @@ void Tracker::computeTracksMClabels() for (int iCluster = 0; iCluster < TrackITSExt::MaxClusters; ++iCluster) { const int index = track.getClusterIndex(iCluster); - if (index == constants::UnusedIndex) { + if (index == o2::its::constants::UnusedIndex) { continue; } auto labels = mTimeFrame->getClusterLabels(iCluster, index); @@ -312,7 +312,7 @@ void Tracker::computeTracksMClabels() // set fake clusters pattern for (int ic{TrackITSExt::MaxClusters}; ic--;) { auto clid = track.getClusterIndex(ic); - if (clid != constants::UnusedIndex) { + if (clid != o2::its::constants::UnusedIndex) { auto labelsSpan = mTimeFrame->getClusterLabels(ic, clid); for (const auto& currentLabel : labelsSpan) { if (currentLabel == maxOccurrencesValue) { @@ -332,13 +332,13 @@ void Tracker::computeTracksMClabels() } template -void Tracker::rectifyClusterIndices() +void TrackerACTS::rectifyClusterIndices() { for (int iROF{0}; iROF < mTimeFrame->getNrof(); ++iROF) { for (auto& track : mTimeFrame->getTracks(iROF)) { for (int iCluster = 0; iCluster < TrackITSExt::MaxClusters; ++iCluster) { const int index = track.getClusterIndex(iCluster); - if (index != constants::UnusedIndex) { + if (index != o2::its::constants::UnusedIndex) { track.setExternalClusterIndex(iCluster, mTimeFrame->getClusterExternalIndex(iCluster, index)); } } @@ -347,24 +347,24 @@ void Tracker::rectifyClusterIndices() } template -void Tracker::adoptTimeFrame(TimeFrame& tf) +void TrackerACTS::adoptTimeFrame(o2::its::TimeFrame& tf) { mTimeFrame = &tf; mTraits->adoptTimeFrame(&tf); } template -void Tracker::printSummary() const +void TrackerACTS::printSummary() const { auto avgTF = mTotalTime * 1.e-3 / ((mTimeFrameCounter > 0) ? (double)mTimeFrameCounter : -1.0); auto avgTFwithDropped = mTotalTime * 1.e-3 / (((mTimeFrameCounter + mNumberOfDroppedTFs) > 0) ? (double)(mTimeFrameCounter + mNumberOfDroppedTFs) : -1.0); - LOGP(info, "Tracker summary: Processed {} TFs (dropped {}) in TOT={:.2f} s, AVG/TF={:.2f} ({:.2f}) s", mTimeFrameCounter, mNumberOfDroppedTFs, mTotalTime * 1.e-3, avgTF, avgTFwithDropped); + LOGP(info, "TrackerACTS summary: Processed {} TFs (dropped {}) in TOT={:.2f} s, AVG/TF={:.2f} ({:.2f}) s", mTimeFrameCounter, mNumberOfDroppedTFs, mTotalTime * 1.e-3, avgTF, avgTFwithDropped); } -template class Tracker<7>; +template class TrackerACTS<7>; // ALICE3 upgrade #ifdef ENABLE_UPGRADES -template class Tracker<11>; +template class TrackerACTS<11>; #endif } // namespace o2::trk From daad74d6411425824cf393dcfab39b21f8a43824 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Jacazio?= Date: Tue, 17 Mar 2026 05:39:06 +0100 Subject: [PATCH 10/12] Trackeracts --- .../TRK/reconstruction/src/TRKReconstructionLinkDef.h | 1 + .../Upgrades/ALICE3/TRK/reconstruction/src/TrackerACTS.cxx | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TRKReconstructionLinkDef.h b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TRKReconstructionLinkDef.h index 6ef7321f92e2f..1f4c2193b91b1 100644 --- a/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TRKReconstructionLinkDef.h +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TRKReconstructionLinkDef.h @@ -19,6 +19,7 @@ #pragma link C++ class o2::trk::Clusterer + ; #ifdef O2_WITH_ACTS #pragma link C++ class o2::trk::ClustererACTS + ; + #endif #endif diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TrackerACTS.cxx b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TrackerACTS.cxx index f8cacca28f20a..074c2d173eaf5 100644 --- a/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TrackerACTS.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TrackerACTS.cxx @@ -280,7 +280,7 @@ void TrackerACTS::computeTracksMClabels() std::vector> occurrences; occurrences.clear(); - for (int iCluster = 0; iCluster < TrackITSExt::MaxClusters; ++iCluster) { + for (int iCluster = 0; iCluster < o2::its::TrackITSExt::MaxClusters; ++iCluster) { const int index = track.getClusterIndex(iCluster); if (index == o2::its::constants::UnusedIndex) { continue; @@ -310,7 +310,7 @@ void TrackerACTS::computeTracksMClabels() auto maxOccurrencesValue = occurrences[0].first; uint32_t pattern = track.getPattern(); // set fake clusters pattern - for (int ic{TrackITSExt::MaxClusters}; ic--;) { + for (int ic{o2::its::TrackITSExt::MaxClusters}; ic--;) { auto clid = track.getClusterIndex(ic); if (clid != o2::its::constants::UnusedIndex) { auto labelsSpan = mTimeFrame->getClusterLabels(ic, clid); @@ -336,7 +336,7 @@ void TrackerACTS::rectifyClusterIndices() { for (int iROF{0}; iROF < mTimeFrame->getNrof(); ++iROF) { for (auto& track : mTimeFrame->getTracks(iROF)) { - for (int iCluster = 0; iCluster < TrackITSExt::MaxClusters; ++iCluster) { + for (int iCluster = 0; iCluster < o2::its::TrackITSExt::MaxClusters; ++iCluster) { const int index = track.getClusterIndex(iCluster); if (index != o2::its::constants::UnusedIndex) { track.setExternalClusterIndex(iCluster, mTimeFrame->getClusterExternalIndex(iCluster, index)); From b1633ff6cfc505a8351e7d672760aa4e337247e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Jacazio?= Date: Tue, 17 Mar 2026 08:30:27 +0100 Subject: [PATCH 11/12] Remove TRKHIT --- .../include/TRKReconstruction/TimeFrame.h | 1 - .../ALICE3/TRK/simulation/CMakeLists.txt | 2 +- .../simulation/include/TRKSimulation/Hit.h | 132 +----------------- .../ALICE3/TRK/simulation/src/Hit.cxx | 18 --- 4 files changed, 5 insertions(+), 148 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/TimeFrame.h b/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/TimeFrame.h index f42a1c897efb6..c07767d50b113 100644 --- a/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/TimeFrame.h +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/TimeFrame.h @@ -33,7 +33,6 @@ namespace o2 { namespace trk { -class Hit; class GeometryTGeo; /// TRK TimeFrame class that extends ITS TimeFrame functionality diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/CMakeLists.txt b/Detectors/Upgrades/ALICE3/TRK/simulation/CMakeLists.txt index 10f117750d793..6d30d8d01bb12 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/CMakeLists.txt +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/CMakeLists.txt @@ -39,4 +39,4 @@ o2_target_root_dictionary(TRKSimulation include/TRKSimulation/VDLayer.h include/TRKSimulation/VDGeometryBuilder.h include/TRKSimulation/VDSensorRegistry.h - include/TRKSimulation/DPLDigitizerParam.h) \ No newline at end of file + include/TRKSimulation/DPLDigitizerParam.h) diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/Hit.h b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/Hit.h index a178c30069f14..4e725f5ed3d84 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/Hit.h +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/Hit.h @@ -15,135 +15,11 @@ #ifndef ALICEO2_TRK_POINT_H_ #define ALICEO2_TRK_POINT_H_ -#include "SimulationDataFormat/BaseHits.h" // for BasicXYZEHit -#include "Rtypes.h" // for Bool_t, Double_t, Int_t, Double32_t, etc -#include "TVector3.h" // for TVector3 -#include -#include "CommonUtils/ShmAllocator.h" +#include "ITSMFTSimulation/Hit.h" -namespace o2 +namespace o2::trk { -namespace trk -{ - -class Hit : public o2::BasicXYZEHit -{ - - public: - enum HitStatus_t { - kTrackEntering = 0x1, - kTrackInside = 0x1 << 1, - kTrackExiting = 0x1 << 2, - kTrackOut = 0x1 << 3, - kTrackStopped = 0x1 << 4, - kTrackAlive = 0x1 << 5 - }; - - /// Default constructor - Hit() = default; - - /// Class Constructor - /// \param trackID Index of MCTrack - /// \param detID Detector ID - /// \param startPos Coordinates at entrance to active volume [cm] - /// \param pos Coordinates to active volume [cm] - /// \param mom Momentum of track at entrance [GeV] - /// \param endTime Time at entrance [ns] - /// \param time Time since event start [ns] - /// \param eLoss Energy deposit [GeV] - /// \param startStatus: status at entrance - /// \param endStatus: status at exit - inline Hit(int trackID, unsigned short detID, const TVector3& startPos, const TVector3& pos, const TVector3& mom, double startE, - double endTime, double eLoss, unsigned char statusStart, unsigned char status); - - // Entrance position getters - math_utils::Point3D GetPosStart() const { return mPosStart; } - Float_t GetStartX() const { return mPosStart.X(); } - Float_t GetStartY() const { return mPosStart.Y(); } - Float_t GetStartZ() const { return mPosStart.Z(); } - template - void GetStartPosition(F& x, F& y, F& z) const - { - x = GetStartX(); - y = GetStartY(); - z = GetStartZ(); - } - // momentum getters - math_utils::Vector3D GetMomentum() const { return mMomentum; } - math_utils::Vector3D& GetMomentum() { return mMomentum; } - Float_t GetPx() const { return mMomentum.X(); } - Float_t GetPy() const { return mMomentum.Y(); } - Float_t GetPz() const { return mMomentum.Z(); } - Float_t GetE() const { return mE; } - Float_t GetTotalEnergy() const { return GetE(); } - - UChar_t GetStatusEnd() const { return mTrackStatusEnd; } - UChar_t GetStatusStart() const { return mTrackStatusStart; } - - Bool_t IsEntering() const { return mTrackStatusEnd & kTrackEntering; } - Bool_t IsInside() const { return mTrackStatusEnd & kTrackInside; } - Bool_t IsExiting() const { return mTrackStatusEnd & kTrackExiting; } - Bool_t IsOut() const { return mTrackStatusEnd & kTrackOut; } - Bool_t IsStopped() const { return mTrackStatusEnd & kTrackStopped; } - Bool_t IsAlive() const { return mTrackStatusEnd & kTrackAlive; } - - Bool_t IsEnteringStart() const { return mTrackStatusStart & kTrackEntering; } - Bool_t IsInsideStart() const { return mTrackStatusStart & kTrackInside; } - Bool_t IsExitingStart() const { return mTrackStatusStart & kTrackExiting; } - Bool_t IsOutStart() const { return mTrackStatusStart & kTrackOut; } - Bool_t IsStoppedStart() const { return mTrackStatusStart & kTrackStopped; } - Bool_t IsAliveStart() const { return mTrackStatusStart & kTrackAlive; } - - // Entrance position setter - void SetPosStart(const math_utils::Point3D& p) { mPosStart = p; } - - /// Output to screen - void Print(const Option_t* opt) const; - friend std::ostream& operator<<(std::ostream& of, const Hit& point) - { - of << "-I- Hit: O2its point for track " << point.GetTrackID() << " in detector " << point.GetDetectorID() << std::endl; - /* - of << " Position (" << point.fX << ", " << point.fY << ", " << point.fZ << ") cm" << std::endl; - of << " Momentum (" << point.fPx << ", " << point.fPy << ", " << point.fPz << ") GeV" << std::endl; - of << " Time " << point.fTime << " ns, Length " << point.fLength << " cm, Energy loss " - << point.fELoss * 1.0e06 << " keV" << std::endl; - */ - return of; - } - - private: - math_utils::Vector3D mMomentum; ///< momentum at entrance - math_utils::Point3D mPosStart; ///< position at entrance (base mPos give position on exit) - Float_t mE; ///< total energy at entrance - UChar_t mTrackStatusEnd; ///< MC status flag at exit - UChar_t mTrackStatusStart; ///< MC status at starting point - - ClassDefNV(Hit, 1); -}; - -Hit::Hit(int trackID, unsigned short detID, const TVector3& startPos, const TVector3& endPos, const TVector3& startMom, - double startE, double endTime, double eLoss, unsigned char startStatus, unsigned char endStatus) - : BasicXYZEHit(endPos.X(), endPos.Y(), endPos.Z(), endTime, eLoss, trackID, detID), - mMomentum(startMom.Px(), startMom.Py(), startMom.Pz()), - mPosStart(startPos.X(), startPos.Y(), startPos.Z()), - mE(startE), - mTrackStatusEnd(endStatus), - mTrackStatusStart(startStatus) -{ -} - -} // namespace trk -} // namespace o2 - -#ifdef USESHM -namespace std -{ -template <> -class allocator : public o2::utils::ShmAllocator -{ -}; -} // namespace std - -#endif +using Hit = o2::itsmft::Hit; // For now we rely on the same Hit class as ITSMFT, but we can extend it with TRK-specific information if needed in the future +} // namespace o2::trk #endif diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/Hit.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Hit.cxx index fe496bc59692f..8d386f493cf3f 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/Hit.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Hit.cxx @@ -14,21 +14,3 @@ #include "TRKSimulation/Hit.h" -#include -#include - -ClassImp(o2::trk::Hit); - -using std::cout; -using std::endl; -using namespace o2::trk; -using namespace o2; //::base; - -void Hit::Print(const Option_t* opt) const -{ - printf( - "Det: %5d Track: %6d E.loss: %.3e P: %+.3e %+.3e %+.3e\n" - "PosIn: %+.3e %+.3e %+.3e PosOut: %+.3e %+.3e %+.3e\n", - GetDetectorID(), GetTrackID(), GetEnergyLoss(), GetPx(), GetPy(), GetPz(), - GetStartX(), GetStartY(), GetStartZ(), GetX(), GetY(), GetZ()); -} From c255db4073becb28c3cae8dec115c93983f705d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Jacazio?= Date: Tue, 17 Mar 2026 08:31:06 +0100 Subject: [PATCH 12/12] TOF digitiser --- .../Upgrades/ALICE3/IOTOF/CMakeLists.txt | 3 +- .../IOTOF/DataFormatsIOTOF/CMakeLists.txt | 22 +++++++ .../include/DataFormatsIOTOF/Digit.h | 46 ++++++++++++++ .../src/DataFormatsIOTOFLinkDef.h | 20 ++++++ .../IOTOF/DataFormatsIOTOF/src/Digit.cxx | 21 +++++++ .../ALICE3/IOTOF/simulation/CMakeLists.txt | 5 +- .../include/IOTOFSimulation/Digitizer.h | 62 +++++++++++++++++++ .../ALICE3/IOTOF/simulation/src/Digitizer.cxx | 43 +++++++++++++ .../ALICE3/TRK/macros/test/CheckClusters.C | 24 +++---- 9 files changed, 233 insertions(+), 13 deletions(-) create mode 100644 Detectors/Upgrades/ALICE3/IOTOF/DataFormatsIOTOF/CMakeLists.txt create mode 100644 Detectors/Upgrades/ALICE3/IOTOF/DataFormatsIOTOF/include/DataFormatsIOTOF/Digit.h create mode 100644 Detectors/Upgrades/ALICE3/IOTOF/DataFormatsIOTOF/src/DataFormatsIOTOFLinkDef.h create mode 100644 Detectors/Upgrades/ALICE3/IOTOF/DataFormatsIOTOF/src/Digit.cxx create mode 100644 Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Digitizer.h create mode 100644 Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Digitizer.cxx diff --git a/Detectors/Upgrades/ALICE3/IOTOF/CMakeLists.txt b/Detectors/Upgrades/ALICE3/IOTOF/CMakeLists.txt index 83838a01d13f1..351b564eb1157 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/CMakeLists.txt +++ b/Detectors/Upgrades/ALICE3/IOTOF/CMakeLists.txt @@ -10,4 +10,5 @@ # or submit itself to any jurisdiction. add_subdirectory(base) -add_subdirectory(simulation) \ No newline at end of file +add_subdirectory(simulation) +add_subdirectory(DataFormatsIOTOF) diff --git a/Detectors/Upgrades/ALICE3/IOTOF/DataFormatsIOTOF/CMakeLists.txt b/Detectors/Upgrades/ALICE3/IOTOF/DataFormatsIOTOF/CMakeLists.txt new file mode 100644 index 0000000000000..534e6217807c5 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/IOTOF/DataFormatsIOTOF/CMakeLists.txt @@ -0,0 +1,22 @@ +# Copyright 2019-2020 CERN and copyright holders of ALICE O2. +# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +# All rights not expressly granted are reserved. +# +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. + +o2_add_library(DataFormatsIOTOF + SOURCES src/Digit.cxx + # SOURCES src/MCLabel.cxx + # SOURCES src/Cluster.cxx + PUBLIC_LINK_LIBRARIES O2::DataFormatsITSMFT) + +o2_target_root_dictionary(DataFormatsIOTOF + HEADERS include/DataFormatsIOTOF/Digit.h + # HEADERS include/DataFormatsIOTOF/MCLabel.h + # HEADERS include/DataFormatsIOTOF/Cluster.h + ) diff --git a/Detectors/Upgrades/ALICE3/IOTOF/DataFormatsIOTOF/include/DataFormatsIOTOF/Digit.h b/Detectors/Upgrades/ALICE3/IOTOF/DataFormatsIOTOF/include/DataFormatsIOTOF/Digit.h new file mode 100644 index 0000000000000..19b5dc3bcd72b --- /dev/null +++ b/Detectors/Upgrades/ALICE3/IOTOF/DataFormatsIOTOF/include/DataFormatsIOTOF/Digit.h @@ -0,0 +1,46 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// +/// \file Digit.h +/// \brief Definition of IOTOF digit class +/// \author Nicolò Jacazio, Università del Piemonte Orientale (IT) +/// \since 2026-03-17 +/// + +#ifndef ALICEO2_IOTOF_DIGIT_H +#define ALICEO2_IOTOF_DIGIT_H + +#include "DataFormatsITSMFT/Digit.h" + +namespace o2::iotof +{ +class Digit : public o2::itsmft::Digit +{ + public: + Digit() = default; + ~Digit() = default; + Digit(UShort_t chipindex = 0, UShort_t row = 0, UShort_t col = 0, Int_t charge = 0, double time = 0.) + : o2::itsmft::Digit(chipindex, row, col, charge), mTime(time) {}; + + // Setters + void setTime(double time) { mTime = time; } + + // Getters + double getTime() const { return mTime; } + + private: + double mTime = 0.; ///< Measured time (ns) + ClassDefNV(Digit, 1); +}; + +} // namespace o2::iotof +#endif // ALICEO2_IOTOF_DIGIT_H diff --git a/Detectors/Upgrades/ALICE3/IOTOF/DataFormatsIOTOF/src/DataFormatsIOTOFLinkDef.h b/Detectors/Upgrades/ALICE3/IOTOF/DataFormatsIOTOF/src/DataFormatsIOTOFLinkDef.h new file mode 100644 index 0000000000000..8a167df4d6c7b --- /dev/null +++ b/Detectors/Upgrades/ALICE3/IOTOF/DataFormatsIOTOF/src/DataFormatsIOTOFLinkDef.h @@ -0,0 +1,20 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifdef __CLING__ + +#pragma link off all globals; +#pragma link off all classes; +#pragma link off all functions; + +#pragma link C++ class o2::iotof::Digit + ; +// #pragma link C++ class std::vector < o2::iotof::Digit> + ; +#endif diff --git a/Detectors/Upgrades/ALICE3/IOTOF/DataFormatsIOTOF/src/Digit.cxx b/Detectors/Upgrades/ALICE3/IOTOF/DataFormatsIOTOF/src/Digit.cxx new file mode 100644 index 0000000000000..b15ecd94cd9af --- /dev/null +++ b/Detectors/Upgrades/ALICE3/IOTOF/DataFormatsIOTOF/src/Digit.cxx @@ -0,0 +1,21 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// +/// \file Digit.cxx +/// \brief Implementation of IOTOF digit class +/// \author Nicolò Jacazio, Università del Piemonte Orientale (IT) +/// \since 2026-03-17 +/// + +#include "DataFormatsIOTOF/Digit.h" + +using namespace o2::iotof; diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/CMakeLists.txt b/Detectors/Upgrades/ALICE3/IOTOF/simulation/CMakeLists.txt index 5e7cbd87bfd35..f3418d9065fcb 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/simulation/CMakeLists.txt +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/CMakeLists.txt @@ -12,11 +12,14 @@ o2_add_library(IOTOFSimulation SOURCES src/Layer.cxx src/Detector.cxx + src/Digitizer.cxx # src/IOTOFServices.cxx PUBLIC_LINK_LIBRARIES O2::IOTOFBase + O2::DataFormatsIOTOF O2::ITSMFTSimulation) o2_target_root_dictionary(IOTOFSimulation HEADERS include/IOTOFSimulation/Detector.h - include/IOTOFSimulation/Layer.h) + include/IOTOFSimulation/Layer.h + include/IOTOFSimulation/Digitizer.h) # include/IOTOFSimulation/IOTOFServices.h) \ No newline at end of file diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Digitizer.h b/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Digitizer.h new file mode 100644 index 0000000000000..3c828895097ed --- /dev/null +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Digitizer.h @@ -0,0 +1,62 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// +/// \file Digitizer.h +/// \brief Definition of the ALICE3 TOF digitizer +/// \author Nicolò Jacazio, Università del Piemonte Orientale (IT) +/// \since 2026-03-17 +/// + +#ifndef ALICEO2_IOTOF_DIGITIZER_H +#define ALICEO2_IOTOF_DIGITIZER_H + +#include "ITSMFTSimulation/Hit.h" +#include "DataFormatsITSMFT/Digit.h" +#include "DataFormatsIOTOF/Digit.h" +#include "DataFormatsITSMFT/ROFRecord.h" +#include "CommonDataFormat/InteractionRecord.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "SimulationDataFormat/MCTruthContainer.h" +#include "IOTOFBase/GeometryTGeo.h" + +namespace o2::iotof +{ + +class Digitizer +{ + public: + void setDigits(std::vector* dig) { mDigits = dig; } + void setMCLabels(o2::dataformats::MCTruthContainer* mclb) { mMCLabels = mclb; } + void setROFRecords(std::vector* rec) { mROFRecords = rec; } + + void init(); + + /// Steer conversion of hits to digits + void process(const std::vector* hits, int evID, int srcID); + + // provide the common iotof::GeometryTGeo to access matrices and segmentation + void setGeometry(const o2::iotof::GeometryTGeo* gm) { mGeometry = gm; } + + private: + void processHit(const o2::itsmft::Hit& hit, uint32_t& maxFr, int evID, int srcID); + + const o2::iotof::GeometryTGeo* mGeometry = nullptr; ///< IOTOF geometry + + // std::vector mChips; ///< Array of chips digits containers + + std::vector* mDigits = nullptr; //! output digits + std::vector* mROFRecords = nullptr; //! output ROF records + o2::dataformats::MCTruthContainer* mMCLabels = nullptr; //! output labels +}; +} // namespace o2::iotof + +#endif \ No newline at end of file diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Digitizer.cxx b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Digitizer.cxx new file mode 100644 index 0000000000000..c9578b5981960 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Digitizer.cxx @@ -0,0 +1,43 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// +/// \file Digitizer.cxx +/// \brief Implementation of the ALICE3 TOF digitizer +/// \author Nicolò Jacazio, Università del Piemonte Orientale (IT) +/// \since 2026-03-17 +/// + +#include "IOTOFSimulation/Digitizer.h" +#include "DetectorsRaw/HBFUtils.h" + +#include +#include +#include +#include +#include + +namespace o2::iotof +{ + +void Digitizer::init() +{ +} + +void Digitizer::process(const std::vector* hits, int evID, int srcID) +{ +} + +void Digitizer::processHit(const o2::itsmft::Hit& hit, uint32_t& maxFr, int evID, int srcID) +{ +} + +} // namespace o2::iotof diff --git a/Detectors/Upgrades/ALICE3/TRK/macros/test/CheckClusters.C b/Detectors/Upgrades/ALICE3/TRK/macros/test/CheckClusters.C index 327577102d86e..cf064b03889b7 100644 --- a/Detectors/Upgrades/ALICE3/TRK/macros/test/CheckClusters.C +++ b/Detectors/Upgrades/ALICE3/TRK/macros/test/CheckClusters.C @@ -56,17 +56,18 @@ void CheckClusters(const std::string& clusfile = "o2clus_trk.root", // ── Chip response (for hit-segment propagation to charge-collection plane) ── // Fetches the same AlpideSimResponse from CCDB as the digitizer (IT3/Calib/APTSResponse) // and computes Y-intersection planes with the same formulas from Digitizer::init() - auto& ccdbMgr = o2::ccdb::BasicCCDBManager::instance(); - ccdbMgr.setURL(ccdbUrl); - if (ccdbTimestamp > 0) { - ccdbMgr.setTimestamp(ccdbTimestamp); - } - auto* alpResp = ccdbMgr.get("IT3/Calib/APTSResponse"); - if (!alpResp) { - LOGP(fatal, "Cannot retrieve AlpideSimResponse from CCDB at {}", ccdbUrl); - return; - } - const float depthMax = alpResp->getDepthMax(); + // auto& ccdbMgr = o2::ccdb::BasicCCDBManager::instance(); + // ccdbMgr.setURL(ccdbUrl); + // if (ccdbTimestamp > 0) { + // ccdbMgr.setTimestamp(ccdbTimestamp); + // } + // auto* alpResp = ccdbMgr.get("IT3/Calib/APTSResponse"); + // if (!alpResp) { + // LOGP(fatal, "Cannot retrieve AlpideSimResponse from CCDB at {}", ccdbUrl); + // return; + // } + // const float depthMax = alpResp->getDepthMax(); + const float depthMax = 500; // ── Y-plane shifts: why VD and ML/OT need different values ──────────────── // @@ -154,6 +155,7 @@ void CheckClusters(const std::string& clusfile = "o2clus_trk.root", o2::dataformats::MCTruthContainer* clusLabArr = nullptr; std::vector mc2rofVec, *mc2rofVecP = &mc2rofVec; bool hasMC = (clusTree->GetBranch("TRKClusterMCTruth") != nullptr); + // hasMC = false; if (hasMC) { clusTree->SetBranchAddress("TRKClusterMCTruth", &clusLabArr); clusTree->SetBranchAddress("TRKClustersMC2ROF", &mc2rofVecP);