From 35927c74974110d31e28671a6feced73b2b85cc7 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 1/7] 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 94ee8238e926a2eb1a3ee7f6e53b89eb72810723 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 2/7] 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 310235ebd8708a38de6191e6da96fd9fdba4ee58 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 3/7] 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 c223a47d634e8d0c9d6ca26b6962cfda1230a0c6 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 4/7] 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 da4ef840636b06396f64a84d3308082c91e0c927 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 5/7] 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 ca69069f90f5df2a4c33ff60d0d815494bb93899 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 6/7] 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 cd1d2e526676c4121a17839f3081e947b6d67186 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 7/7] 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}; };