From 80518bf5a1a898a326995babcbb499624f9bcf51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Jacazio?= Date: Tue, 17 Mar 2026 08:39:56 +0100 Subject: [PATCH 1/2] [ALICE3] TRK: allow ACTS clustering --- .../ALICE3/TRK/reconstruction/CMakeLists.txt | 22 ++++++++- .../include/TRKReconstruction/Clusterer.h | 22 ++++----- .../src/TRKReconstructionLinkDef.h | 4 ++ .../include/TRKWorkflow/ClustererSpec.h | 9 ++++ .../ALICE3/TRK/workflow/src/ClustererSpec.cxx | 45 ++++++++++++++----- 5 files changed, 79 insertions(+), 23 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/CMakeLists.txt b/Detectors/Upgrades/ALICE3/TRK/reconstruction/CMakeLists.txt index b9866c7d6aa4d..59a7f47955938 100644 --- a/Detectors/Upgrades/ALICE3/TRK/reconstruction/CMakeLists.txt +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/CMakeLists.txt @@ -9,10 +9,16 @@ # 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> + $<$:src/TrackerACTS.cxx> PUBLIC_LINK_LIBRARIES O2::ITStracking O2::GPUCommon @@ -27,11 +33,23 @@ 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 + include/TRKReconstruction/TrackerACTS.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/src/TRKReconstructionLinkDef.h b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TRKReconstructionLinkDef.h index 4eda22e350852..1f4c2193b91b1 100644 --- a/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TRKReconstructionLinkDef.h +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TRKReconstructionLinkDef.h @@ -17,5 +17,9 @@ #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..5d9ac463b3f54 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,32 @@ 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) { + LOG(info) << "Running TRKClusterer with ACTS"; + mClustererACTS.process(digits, + rofs, + clusters, + patterns, + clusterROFs, + mUseMC ? &labels : nullptr, + clusterLabels.get(), + mc2rofs, + mUseMC ? &clusterMC2ROFs : nullptr); + } else +#endif + { + LOG(info) << "Running TRKClusterer"; + 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); @@ -93,7 +113,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 b306cafbe02ddcb77ae6ac96e9661a5055077225 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Jacazio?= Date: Tue, 17 Mar 2026 08:43:14 +0100 Subject: [PATCH 2/2] Add ACTS clusterer --- .../include/TRKReconstruction/ClustererACTS.h | 43 ++ .../TRK/reconstruction/src/ClustererACTS.cxx | 392 ++++++++++++++++++ 2 files changed, 435 insertions(+) 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/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..0cf7c26e0ea41 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/ClustererACTS.cxx @@ -0,0 +1,392 @@ +// 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 "DataFormatsITSMFT/ClusterPattern.h" +#include + +#include +#include +#include + +using namespace o2::trk; + +// Data formats for ACTS interface +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) + 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; + } + + // 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(); + }); + + // 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) { + 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; + + // 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(), mSortIdx[i]); + } + + 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); + + // 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; + 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); + + // 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; + 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); + } + + 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); + } + } +}