From f957e946cc0759f73d152f2565310b6611745c95 Mon Sep 17 00:00:00 2001 From: Marcello Di Costanzo Date: Thu, 25 Jun 2026 10:21:05 +0200 Subject: [PATCH 1/8] First draft of IOTOF clusterizer interface --- .../Upgrades/ALICE3/IOTOF/CMakeLists.txt | 1 + .../IOTOF/DataFormatsIOTOF/CMakeLists.txt | 4 +- .../include/DataFormatsIOTOF/Cluster.h | 41 +++ .../IOTOF/DataFormatsIOTOF/src/Cluster.cxx | 28 ++ .../src/DataFormatsIOTOFLinkDef.h | 3 + .../ALICE3/IOTOF/base/src/GeometryTGeo.cxx | 9 +- .../ALICE3/IOTOF/macros/CMakeLists.txt | 3 + .../ALICE3/IOTOF/macros/CheckClustersIOTOF.C | 191 ++++++++++++++ .../ALICE3/IOTOF/macros/CheckDigitsIOTOF.C | 15 +- .../IOTOF/reconstruction/CMakeLists.txt | 23 ++ .../include/IOTOFReconstruction/Clusterer.h | 105 ++++++++ .../IOTOF/reconstruction/src/Clusterer.cxx | 247 ++++++++++++++++++ .../include/IOTOFSimulation/Segmentation.h | 10 +- .../ALICE3/IOTOF/workflow/CMakeLists.txt | 3 + .../include/IOTOFWorkflow/ClusterWriterSpec.h | 24 ++ .../include/IOTOFWorkflow/ClustererSpec.h | 40 +++ .../IOTOF/workflow/src/ClusterWriterSpec.cxx | 103 ++++++++ .../IOTOF/workflow/src/ClustererSpec.cxx | 126 +++++++++ .../IOTOF/workflow/src/RecoWorkflow.cxx | 15 +- .../workflow/src/iotof-reco-workflow.cxx | 6 +- 20 files changed, 984 insertions(+), 13 deletions(-) create mode 100644 Detectors/Upgrades/ALICE3/IOTOF/DataFormatsIOTOF/include/DataFormatsIOTOF/Cluster.h create mode 100644 Detectors/Upgrades/ALICE3/IOTOF/DataFormatsIOTOF/src/Cluster.cxx create mode 100644 Detectors/Upgrades/ALICE3/IOTOF/macros/CheckClustersIOTOF.C create mode 100644 Detectors/Upgrades/ALICE3/IOTOF/reconstruction/CMakeLists.txt create mode 100644 Detectors/Upgrades/ALICE3/IOTOF/reconstruction/include/IOTOFReconstruction/Clusterer.h create mode 100644 Detectors/Upgrades/ALICE3/IOTOF/reconstruction/src/Clusterer.cxx create mode 100644 Detectors/Upgrades/ALICE3/IOTOF/workflow/include/IOTOFWorkflow/ClusterWriterSpec.h create mode 100644 Detectors/Upgrades/ALICE3/IOTOF/workflow/include/IOTOFWorkflow/ClustererSpec.h create mode 100644 Detectors/Upgrades/ALICE3/IOTOF/workflow/src/ClusterWriterSpec.cxx create mode 100644 Detectors/Upgrades/ALICE3/IOTOF/workflow/src/ClustererSpec.cxx diff --git a/Detectors/Upgrades/ALICE3/IOTOF/CMakeLists.txt b/Detectors/Upgrades/ALICE3/IOTOF/CMakeLists.txt index a9964670a99c1..56702ca159a61 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/CMakeLists.txt +++ b/Detectors/Upgrades/ALICE3/IOTOF/CMakeLists.txt @@ -12,5 +12,6 @@ add_subdirectory(base) add_subdirectory(simulation) add_subdirectory(DataFormatsIOTOF) +add_subdirectory(reconstruction) add_subdirectory(workflow) add_subdirectory(macros) diff --git a/Detectors/Upgrades/ALICE3/IOTOF/DataFormatsIOTOF/CMakeLists.txt b/Detectors/Upgrades/ALICE3/IOTOF/DataFormatsIOTOF/CMakeLists.txt index 534e6217807c5..9e075aabb2cc0 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/DataFormatsIOTOF/CMakeLists.txt +++ b/Detectors/Upgrades/ALICE3/IOTOF/DataFormatsIOTOF/CMakeLists.txt @@ -12,11 +12,11 @@ o2_add_library(DataFormatsIOTOF SOURCES src/Digit.cxx # SOURCES src/MCLabel.cxx - # SOURCES src/Cluster.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 + HEADERS include/DataFormatsIOTOF/Cluster.h ) diff --git a/Detectors/Upgrades/ALICE3/IOTOF/DataFormatsIOTOF/include/DataFormatsIOTOF/Cluster.h b/Detectors/Upgrades/ALICE3/IOTOF/DataFormatsIOTOF/include/DataFormatsIOTOF/Cluster.h new file mode 100644 index 0000000000000..5e75c192ae0e3 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/IOTOF/DataFormatsIOTOF/include/DataFormatsIOTOF/Cluster.h @@ -0,0 +1,41 @@ +// 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. + +#ifndef ALICEO2_DATAFORMATSIOTOF_CLUSTER_H +#define ALICEO2_DATAFORMATSIOTOF_CLUSTER_H + +#include +#include +#include + +namespace o2::iotof +{ + +struct Cluster { + uint16_t chipID = 0; + uint16_t row = 0; + uint16_t col = 0; + uint16_t size = 1; + int16_t subDetID = -1; + int16_t layer = -1; + int16_t disk = -1; + float xCoord = 0.f; + float yCoord = 0.f; + float zCoord = 0.f; + + std::string asString() const; + + ClassDefNV(Cluster, 1); +}; + +} // namespace o2::iotof + +#endif diff --git a/Detectors/Upgrades/ALICE3/IOTOF/DataFormatsIOTOF/src/Cluster.cxx b/Detectors/Upgrades/ALICE3/IOTOF/DataFormatsIOTOF/src/Cluster.cxx new file mode 100644 index 0000000000000..eb58ca5057d90 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/IOTOF/DataFormatsIOTOF/src/Cluster.cxx @@ -0,0 +1,28 @@ +// 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. + +#include "DataFormatsIOTOF/Cluster.h" +#include + +ClassImp(o2::iotof::Cluster); + +namespace o2::iotof +{ + +std::string Cluster::asString() const +{ + std::ostringstream stream; + stream << "chip=" << chipID << " row=" << row << " col=" << col << " size=" << size + << " subDet=" << subDetID << " layer=" << layer << " disk=" << disk; + return stream.str(); +} + +} // namespace o2::iotof diff --git a/Detectors/Upgrades/ALICE3/IOTOF/DataFormatsIOTOF/src/DataFormatsIOTOFLinkDef.h b/Detectors/Upgrades/ALICE3/IOTOF/DataFormatsIOTOF/src/DataFormatsIOTOFLinkDef.h index 3f629289cf842..7e121273d3fab 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/DataFormatsIOTOF/src/DataFormatsIOTOFLinkDef.h +++ b/Detectors/Upgrades/ALICE3/IOTOF/DataFormatsIOTOF/src/DataFormatsIOTOFLinkDef.h @@ -18,6 +18,9 @@ #pragma link C++ class o2::iotof::Digit + ; #pragma link C++ class std::vector < o2::iotof::Digit> + ; +#pragma link C++ class o2::iotof::Cluster + ; +#pragma link C++ class std::vector < o2::iotof::Cluster> + ; + #pragma link C++ class o2::iotof::McLabelRef + ; #pragma link C++ class std::vector < o2::iotof::McLabelRef> + ; diff --git a/Detectors/Upgrades/ALICE3/IOTOF/base/src/GeometryTGeo.cxx b/Detectors/Upgrades/ALICE3/IOTOF/base/src/GeometryTGeo.cxx index 7b4711d8d3272..9f6485b92e08e 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/base/src/GeometryTGeo.cxx +++ b/Detectors/Upgrades/ALICE3/IOTOF/base/src/GeometryTGeo.cxx @@ -158,8 +158,8 @@ int GeometryTGeo::getIOTOFChipIndex(int lay, int sta, int mod, int chip) const bool GeometryTGeo::getIOTOFChipId(int index, int& lay, int& sta, int& mod, int& chip) const { - lay = getIOTOFLayer(index); - index -= getIOTOFFirstChipIndex(lay); + lay = getIOTOFLayer(index); // Get IOTOF layer + index -= getIOTOFFirstChipIndex(lay); // Get index relative to layer sta = mNumberOfStavesIOTOF[lay] > 0 ? index / mNumberOfChipsPerStaveIOTOF[lay] : -1; index %= mNumberOfChipsPerStaveIOTOF[lay]; mod = mNumberOfModulesIOTOF[lay] > 0 ? index / mNumberOfChipsPerModuleIOTOF[lay] : -1; @@ -284,13 +284,14 @@ void GeometryTGeo::Build(int loadTrans) mLastChipIndex[j] = numberOfChips - 1; } - LOG(info) << "numberOfChipsITOF = " << mNumberOfChipsIOTOF[0] << ", numberOfChipsOTOF = " << mNumberOfChipsIOTOF[1] << ", numberOfChips = " << numberOfChips << ", mNumberOfChipesPerStaveITOF" << mNumberOfChipsPerStaveIOTOF[0]; + LOG(info) << "[GeometryTGeo] numberOfChipsITOF = " << mNumberOfChipsIOTOF[0] << ", numberOfChipsOTOF = " << mNumberOfChipsIOTOF[1] << ", numberOfChips = " << numberOfChips << ", mNumberOfChipesPerStaveITOF" << mNumberOfChipsPerStaveIOTOF[0]; setSize(numberOfChips); defineSensors(); fillTrackingFramesCache(); fillMatrixCache(loadTrans); - // fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::L2G)); + // LOG(info) << "[GeometryTGeo] Filling matrix cache"; + // fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::L2G)); } void GeometryTGeo::defineSensors() diff --git a/Detectors/Upgrades/ALICE3/IOTOF/macros/CMakeLists.txt b/Detectors/Upgrades/ALICE3/IOTOF/macros/CMakeLists.txt index f56668d55ab91..efca7b8de1a9f 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/macros/CMakeLists.txt +++ b/Detectors/Upgrades/ALICE3/IOTOF/macros/CMakeLists.txt @@ -25,3 +25,6 @@ o2_add_test_root_macro(CheckDigitsIOTOF.C O2::DetectorsBase O2::Steer LABELS iotof COMPILE_ONLY) + +o2_add_test_root_macro(CheckClustersIOTOF.C + LABELS iotof COMPILE_ONLY) diff --git a/Detectors/Upgrades/ALICE3/IOTOF/macros/CheckClustersIOTOF.C b/Detectors/Upgrades/ALICE3/IOTOF/macros/CheckClustersIOTOF.C new file mode 100644 index 0000000000000..feb5dad24d0de --- /dev/null +++ b/Detectors/Upgrades/ALICE3/IOTOF/macros/CheckClustersIOTOF.C @@ -0,0 +1,191 @@ +// 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 CheckClustersIOTOF.C +/// \brief Simple macro to create clusters from TF3 digits + +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include +#include +#include +#include +#include +#include + +#include "IOTOFSimulation/Segmentation.h" +#include "IOTOFBase/IOTOFBaseParam.h" +#include "IOTOFBase/GeometryTGeo.h" +#include "DataFormatsIOTOF/Digit.h" +#include "DataFormatsIOTOF/Cluster.h" +#include "MathUtils/Utils.h" +#include "SimulationDataFormat/ConstMCTruthContainer.h" +#include "SimulationDataFormat/IOMCTruthContainerView.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "DetectorsBase/GeometryManager.h" + +#include "DataFormatsITSMFT/ROFRecord.h" + +#endif + +#define ENABLE_UPGRADES + +void CheckClustersIOTOF(std::string digiFilePath = "tf3digits.root", std::string clsFilePath = "tf3clusters.root", std::string inputGeomPath = "o2sim_geometry.root") +{ + gStyle->SetPalette(55); + + using namespace o2::base; + using namespace o2::iotof; + + using o2::iotof::Digit; + using o2::iotof::Cluster; + + o2::conf::ConfigurableParam::updateFromString("IOTOFBase.segmentedInnerTOF=true;IOTOFBase.segmentedOuterTOF=true;IOTOFBase.enableForwardTOF=false;IOTOFBase.enableBackwardTOF=false"); + + auto segGeom = o2::iotof::Segmentation::Instance(); + + // Geometry + o2::base::GeometryManager::loadGeometry(inputGeomPath); + auto* tofGeo = o2::iotof::GeometryTGeo::Instance(); + tofGeo->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::L2G)); + + // Digits + TFile* digiFile = TFile::Open(digiFilePath.data()); + TTree* digiTree = (TTree*)digiFile->Get("o2sim"); + std::vector* digitsArray{nullptr}; + digiTree->SetBranchAddress("TF3Digit", &digitsArray); + std::vector* digiRofRecordsArr{nullptr}; + digiTree->SetBranchAddress("TF3DigitROF", &digiRofRecordsArr); + auto& digiRofArr = *digiRofRecordsArr; + o2::dataformats::IOMCTruthContainerView* digiPlabelsArr{nullptr}; + digiTree->SetBranchAddress("TF3DigitMCTruth", &digiPlabelsArr); + digiTree->GetEntry(0); + + // Clusters + TFile* clsFile = TFile::Open(clsFilePath.data()); + TTree* clsTree = (TTree*)clsFile->Get("o2sim"); + std::vector* clsArray{nullptr}; + clsTree->SetBranchAddress("TF3ClusterComp", &clsArray); + std::vector* clsRofRecordsArr{nullptr}; + clsTree->SetBranchAddress("TF3ClusterROF", &clsRofRecordsArr); + auto& clsRofArr = *clsRofRecordsArr; + o2::dataformats::IOMCTruthContainerView* clsPlabelsArr{nullptr}; + clsTree->SetBranchAddress("TF3ClusterMCTruth", &clsPlabelsArr); + clsTree->GetEntry(0); + + // Start script + o2::dataformats::ConstMCTruthContainer labels; + clsPlabelsArr->copyandflatten(labels); + + auto clsTuple = new TNtuple("clsTuple", "clsTuple", "chip_id:x:y:z:subdet_id:row:col"); + clsTuple->SetDirectory(nullptr); + + TH1F* histXCoordCls = new TH1F("histXCoordCls", "histXCoordCls", 8000, -100, 100); + TH1F* histYCoordCls = new TH1F("histYCoordCls", "histYCoordCls", 8000, -100, 100); + TH1F* histZCoordCls = new TH1F("histZCoordCls", "histZCoordCls", 28000, -400, 400); + TH1F* histXCoordDigit = new TH1F("histXCoordDigit", "histXCoordDigit", 8000, -100, 100); + TH1F* histYCoordDigit = new TH1F("histYCoordDigit", "histYCoordDigit", 8000, -100, 100); + TH1F* histZCoordDigit = new TH1F("histZCoordDigit", "histZCoordDigit", 28000, -400, 400); + + // LOOP on : ROFRecord array + for (unsigned int iROF = 0; iROF < clsRofArr.size(); ++iROF) { + + const unsigned int rofIndex = clsRofArr[iROF].getFirstEntry(); + const unsigned int rofNEntries = clsRofArr[iROF].getNEntries(); + + // LOOP on : digits array + for (unsigned int iDigit = rofIndex; iDigit < rofIndex + rofNEntries; iDigit++) { + if (iDigit % 1000 == 0) { + std::cout << "Reading digit " << iDigit << " / " << digitsArray->size() << std::endl; + } + + Int_t iRow = (*digitsArray)[iDigit].getRow(); + Int_t iCol = (*digitsArray)[iDigit].getColumn(); + Int_t iDetID = (*digitsArray)[iDigit].getChipIndex(); + Int_t chipID = (*digitsArray)[iDigit].getChipIndex(); + Int_t subDetID = tofGeo->getIOTOFLayer(iDetID); + + Float_t x{0.f}, y{0.f}, z{0.f}; + if (subDetID >= 0) { + segGeom->detectorToLocal(iRow, iCol, x, z, subDetID); + } + + o2::math_utils::Point3D localDigitCoord(x, y, z); // local Digit + + const auto globalDigitCoord = tofGeo->getMatrixL2G(chipID)(localDigitCoord); // convert to global + histXCoordDigit->Fill(globalDigitCoord.X()); + histYCoordDigit->Fill(globalDigitCoord.Y()); + histZCoordDigit->Fill(globalDigitCoord.Z()); + } // end loop on digits array + + + // LOOP on : clusters array + for (unsigned int iCls = rofIndex; iCls < rofIndex + rofNEntries; iCls++) { + if (iCls % 1000 == 0) { + std::cout << "Reading cluster " << iCls << " / " << clsArray->size() << std::endl; + } + + Int_t iRow = (*clsArray)[iCls].row; + Int_t iCol = (*clsArray)[iCls].col; + Int_t chipID = (*clsArray)[iCls].chipID; + Int_t subDetID = tofGeo->getIOTOFLayer(chipID); + + Float_t x = 0.f, y = 0.f, z = 0.f; + if (subDetID >= 0) { + segGeom->detectorToLocal(iRow, iCol, x, z, subDetID); + } + + o2::math_utils::Point3D localClsCoords(x, y, z); // local Digit + const auto globalClsCoords = tofGeo->getMatrixL2G(chipID)(localClsCoords); // convert to global + std::cout << "Cluster " << iCls << ": chipID = " << chipID << ", X=" << globalClsCoords.x() << ", Y=" << globalClsCoords.y() << ", Z=" << globalClsCoords.z() << std::endl; + clsTuple->Fill((*clsArray)[iCls].chipID, + globalClsCoords.x(), + globalClsCoords.y(), + globalClsCoords.z(), + (*clsArray)[iCls].subDetID, + (*clsArray)[iCls].row, + (*clsArray)[iCls].col); + histXCoordCls->Fill(globalClsCoords.x()); + histYCoordCls->Fill(globalClsCoords.y()); + histZCoordCls->Fill(globalClsCoords.z()); + } // end loop on clusters array + + } // end loop on ROFRecords + + std::cout << "Cluster array size: " << clsTuple->GetEntries() << std::endl; + + // cluster maps in the xy and yz planes + auto canvXY = new TCanvas("canvXY", "", 1600, 800); + canvXY->Divide(2, 1); + canvXY->cd(1); + clsTuple->Draw("y:x>>h_y_vs_x_IOTOF(1000, -100, 100, 1000, -100, 100)", "", "colz"); + canvXY->cd(2); + clsTuple->Draw("y:z>>h_y_vs_z_IOTOF(1000, -400, 400, 1000, -100, 100)", "", "colz"); + canvXY->SaveAs("clusters_digits_y_vs_x_vs_z.pdf"); + + // z distributions + auto canvZ = new TCanvas("canvZ", "", 800, 800); + canvZ->cd(); + clsTuple->Draw("z>>h_z_IOTOF(500, -70, 70)", ""); + canvZ->SaveAs("clusters_digits_z.pdf"); + + TFile* outFile = new TFile("CheckClusters.root", "RECREATE"); + // Save all columns of the tuple as hists + clsTuple->Write(); + histXCoordCls->Write(); + histYCoordCls->Write(); + histZCoordCls->Write(); + histXCoordDigit->Write(); + histYCoordDigit->Write(); + histZCoordDigit->Write(); + outFile->Write(); + outFile->Close(); + +} diff --git a/Detectors/Upgrades/ALICE3/IOTOF/macros/CheckDigitsIOTOF.C b/Detectors/Upgrades/ALICE3/IOTOF/macros/CheckDigitsIOTOF.C index cd9e806ca24e7..aeacfd56ccb58 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/macros/CheckDigitsIOTOF.C +++ b/Detectors/Upgrades/ALICE3/IOTOF/macros/CheckDigitsIOTOF.C @@ -146,6 +146,9 @@ void CheckDigitsIOTOF(std::string digifile = "tf3digits.root", std::string hitfi plabelsArr->copyandflatten(labels); // LOOP on : ROFRecord array + TH1F* histXCoord = new TH1F("histXCoord", "histXCoord", 8000, -100, 100); + TH1F* histYCoord = new TH1F("histYCoord", "histYCoord", 8000, -100, 100); + TH1F* histZCoord = new TH1F("histZCoord", "histZCoord", 28000, -400, 400); for (unsigned int iROF = 0; iROF < rofArr.size(); ++iROF) { const unsigned int rofIndex = rofArr[iROF].getFirstEntry(); @@ -227,8 +230,11 @@ void CheckDigitsIOTOF(std::string digifile = "tf3digits.root", std::string hitfi locH.X(), locH.Z(), /// x and z of the hit in the local reference frame: hit global position -> hit local position xlc, zlc, /// x and z of the hit in the local frame: hit global position -> hit local position -> detector position (row, col) -> local position locHS.X() - locD.X(), locHS.Z() - locD.Z()); /// difference in x and z between the hit and the digit in the local frame + histXCoord->Fill(gloD.X()); + histYCoord->Fill(gloD.Y()); + histZCoord->Fill(gloD.Z()); nt2->Fill(chipID, gloD.Z(), locHS.X() - locHE.X(), locHS.Z() - locHE.Z()); /// differences between local hit start and hit end positions - + std::cout << "Digit " << iDigit << ": chipID = " << chipID << ", X=" << gloD.X() << ", Y=" << gloD.Y() << ", Z=" << gloD.Z() << std::endl; } // end loop on digits array } // end loop on ROFRecords @@ -293,4 +299,11 @@ void CheckDigitsIOTOF(std::string digifile = "tf3digits.root", std::string hitfi f->Write(); f->Close(); + + TFile* outFile = new TFile("CheckDigitsHists.root", "RECREATE"); + outFile->cd(); + histXCoord->Write(); + histYCoord->Write(); + histZCoord->Write(); + outFile->Close(); } diff --git a/Detectors/Upgrades/ALICE3/IOTOF/reconstruction/CMakeLists.txt b/Detectors/Upgrades/ALICE3/IOTOF/reconstruction/CMakeLists.txt new file mode 100644 index 0000000000000..110cc436a8e25 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/IOTOF/reconstruction/CMakeLists.txt @@ -0,0 +1,23 @@ +# 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(IOTOFReconstruction + TARGETVARNAME targetName + SOURCES src/Clusterer.cxx + PUBLIC_LINK_LIBRARIES + Microsoft.GSL::GSL + # O2::DataFormatsITSMFT + O2::DataFormatsIOTOF + O2::IOTOFBase + O2::IOTOFSimulation + # O2::SimulationDataFormat + # nlohmann_json::nlohmann_json + ) diff --git a/Detectors/Upgrades/ALICE3/IOTOF/reconstruction/include/IOTOFReconstruction/Clusterer.h b/Detectors/Upgrades/ALICE3/IOTOF/reconstruction/include/IOTOFReconstruction/Clusterer.h new file mode 100644 index 0000000000000..1b24fc9456e6f --- /dev/null +++ b/Detectors/Upgrades/ALICE3/IOTOF/reconstruction/include/IOTOFReconstruction/Clusterer.h @@ -0,0 +1,105 @@ +// 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 Clusterer.h +/// \brief Definition of the IOTOF cluster finder + +#ifndef ALICEO2_IOTOF_CLUSTERER_H +#define ALICEO2_IOTOF_CLUSTERER_H + +#include "DataFormatsIOTOF/Digit.h" +#include "DataFormatsITSMFT/ROFRecord.h" +#include "DataFormatsITSMFT/ClusterPattern.h" +#include "DataFormatsIOTOF/Cluster.h" +#include "SimulationDataFormat/ConstMCTruthContainer.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "SimulationDataFormat/MCTruthContainer.h" +#include "IOTOFBase/IOTOFBaseParam.h" +#include "MathUtils/Cartesian.h" +#include +#include +#include +#include +#include +#include + +namespace o2::iotof +{ + +class GeometryTGeo; + +class Clusterer +{ + public: + static constexpr int MaxLabels = 10; + + using Digit = o2::iotof::Digit; + using DigROFRecord = o2::itsmft::ROFRecord; + using DigMC2ROFRecord = o2::itsmft::MC2ROFRecord; + using ClusterTruth = o2::dataformats::MCTruthContainer; + using ConstDigitTruth = o2::dataformats::ConstMCTruthContainerView; + using Label = o2::MCCompLabel; + + //---------------------------------------------- + struct ClustererThread { + Clusterer* parent = nullptr; + // Column buffers data members in TRK, for now not needed in TF3 + + // Further struct members in TRK, for now not needed in TF3 + + std::array labelsBuff; ///< MC label buffer for one cluster + + // per-thread output (accumulated, then merged back by caller) + std::vector clusters; + std::vector patterns; + ClusterTruth labels; + + // Further reset column buffer in TRK, not included for now in TF3 + + void fetchMCLabels(uint32_t digID, const ConstDigitTruth* labelsDig, int& nfilled); + void finishChipSingleHitFast(gsl::span digits, uint32_t digitIdx, + const ConstDigitTruth* labelsDigPtr, ClusterTruth* labelsClusPtr, + GeometryTGeo* geom); + void processChip(gsl::span digits, int chipFirst, int chipN, + std::vector* clustersOut, std::vector* patternsOut, + const ConstDigitTruth* labelsDigPtr, ClusterTruth* labelsClusPtr, + GeometryTGeo* geom); + + explicit ClustererThread(Clusterer* par = nullptr) : parent(par) {} + ClustererThread(const ClustererThread&) = delete; + ClustererThread& operator=(const ClustererThread&) = delete; + }; + //---------------------------------------------- + + 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); + + static o2::math_utils::Point3D getClusterLocalCoordinates(const Cluster& cluster, + math_utils::Point3D& localClsCoords) noexcept; + + static o2::math_utils::Point3D getClusterGlobalCoordinates(const Cluster& cluster, + math_utils::Point3D& localClsCoords) noexcept; + + protected: + std::unique_ptr mThread; + std::vector mSortIdx; ///< reusable per-ROF sort buffer +}; + +} // namespace o2::iotof + +#endif diff --git a/Detectors/Upgrades/ALICE3/IOTOF/reconstruction/src/Clusterer.cxx b/Detectors/Upgrades/ALICE3/IOTOF/reconstruction/src/Clusterer.cxx new file mode 100644 index 0000000000000..10c1849fc594d --- /dev/null +++ b/Detectors/Upgrades/ALICE3/IOTOF/reconstruction/src/Clusterer.cxx @@ -0,0 +1,247 @@ +// 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 Clusterer.cxx +/// \brief Implementation of the IOTOF cluster finder + +#include "IOTOFReconstruction/Clusterer.h" +#include "IOTOFBase/GeometryTGeo.h" +#include "IOTOFSimulation/Segmentation.h" + +#include +#include + +namespace o2::iotof +{ + +//__________________________________________________ +o2::math_utils::Point3D Clusterer::getClusterGlobalCoordinates(const Cluster& cluster, math_utils::Point3D& coords) noexcept +{ + LOG(info) << "[Clusterer] getClusterGlobalCoordinates() called for cluster at chipID " << cluster.chipID + << ", row " << cluster.row << ", col " << cluster.col; + + Segmentation::Instance()->detectorToLocal(cluster.row, cluster.col, coords, cluster.subDetID); + LOG(info) << "[Clusterer] Cluster local coordinates: x=" << coords.x() << ", y=" << coords.y() << ", z=" << coords.z(); + GeometryTGeo::Instance()->getMatrixL2G(cluster.subDetID)(coords); + + LOG(info) << "[Clusterer] Cluster global coordinates: x=" << coords.x() << ", y=" << coords.y() << ", z=" << coords.z(); + return coords; +} + +//__________________________________________________ +o2::math_utils::Point3D Clusterer::getClusterLocalCoordinates(const Cluster& cluster, math_utils::Point3D& coords) noexcept +{ + LOG(info) << "[Clusterer] getClusterLocalCoordinates() called for cluster at chipID " << cluster.chipID + << ", row " << cluster.row << ", col " << cluster.col; + + Segmentation::Instance()->detectorToLocal(cluster.row, cluster.col, coords, cluster.subDetID); + + LOG(info) << "[Clusterer] Cluster local coordinates: x=" << coords.x() << ", y=" << coords.y() << ", z=" << coords.z(); + return coords; +} + +//__________________________________________________ +void Clusterer::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) +{ + LOG(info) << "[Clusterer] Entered process()"; + if (!mThread) { + mThread = std::make_unique(this); + } + + auto* geom = o2::iotof::GeometryTGeo::Instance(); + + LOG(info) << "[Clusterer] Processing " << digitROFs.size() << " digit ROFs, total digits: " << digits.size(); + for (size_t iROF = 0; iROF < digitROFs.size(); ++iROF) { + LOG(info) << "[Clusterer] Processing digit ROF " << iROF << "/" << digitROFs.size(); + 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) { + LOG(info) << "[Clusterer] Digit ROF " << iROF << " has no entries, skipping"; + clusterROFs.emplace_back(inROF.getBCData(), inROF.getROFrame(), outFirst, 0); + continue; + } + + // Sort digit indices within this ROF by (chipID, col, row) + // chip by chip, column by column (taken from TRK). + 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(); + }); + LOG(info) << "[Clusterer] Sorted " << nEntries << " digit indices for ROF " << iROF; + + // Process blocks of chips with the same chipID + 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; + + LOG(info) << ""; + LOG(info) << "[Clusterer] Processing chip " << chipID << " with " << chipN << " digits, next chip start from index " << sliceStart; + mThread->processChip(digits, chipFirst, chipN, &clusters, &patterns, digitLabels, clusterLabels, geom); + } + + LOG(info) << "[Clusterer] Finished processing digit ROF " << iROF << ", produced " << (clusters.size() - outFirst) << " clusters"; + clusterROFs.emplace_back(inROF.getBCData(), inROF.getROFrame(), + outFirst, static_cast(clusters.size()) - outFirst); + } + + LOG(info) << "[Clusterer] Finished processing all digit ROFs, total clusters produced: " << clusters.size(); + 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); + } + } +} + +//__________________________________________________ +void Clusterer::ClustererThread::processChip(gsl::span digits, + int chipFirst, int chipN, + std::vector* clustersOut, + std::vector* patternsOut, + const ConstDigitTruth* labelsDigPtr, + ClusterTruth* labelsClusPtr, + GeometryTGeo* geom) +{ + LOG(info) << ""; + LOG(info) << "[Clusterer] Entered processChip() for chip, will process " << chipN << " digits"; + // chipFirst and chipN are relative to mSortIdx (i.e. mSortIdx[chipFirst..chipFirst+chipN-1] + // are the global digit indices for this chip, already sorted by col then row). + // We use parent->mSortIdx to resolve the global index of each pixel. + const auto& sortIdx = parent->mSortIdx; + + // TRK has per-ROF readout, so multiple hits belonging to the same chip, i.e. chipN > 1, + // are handled with a preclusterer. TF3 still does not have per-ROF readout, so we + // use finishChipSingleHitFast on all hits for now. + for (auto i = 0; i < chipN; ++i) { + LOG(info) << "[Clusterer] Processing digit " << sortIdx[chipFirst + i] << " ... "; + finishChipSingleHitFast(digits, sortIdx[chipFirst + i], labelsDigPtr, labelsClusPtr, geom); + } + + // // TRK logic for per-ROF readout, not used for TF3 yet. + // if (chipN == 1) { + // LOG(info) << "[Clusterer] Processing single hit chip"; + // finishChipSingleHitFast(digits, sortIdx[chipFirst], labelsDigPtr, labelsClusPtr, geom); + // } else { + // LOG(info) << "[Clusterer] Processing multi-hit chip with " << chipN << " hits"; + // // Call to initChip() + // // Call to updateChip() + // // Call to finishChip() + // // Code for preclusters needed + // } + + // Flush per-thread output into the caller's containers + if (!clusters.empty()) { + clustersOut->insert(clustersOut->end(), clusters.begin(), clusters.end()); + clusters.clear(); + } + if (!patterns.empty()) { + patternsOut->insert(patternsOut->end(), patterns.begin(), patterns.end()); + patterns.clear(); + } + if (labelsClusPtr && labels.getNElements()) { + labelsClusPtr->mergeAtBack(labels); + labels.clear(); + } +} + +//__________________________________________________ +void Clusterer::ClustererThread::finishChipSingleHitFast(gsl::span digits, uint32_t digitIdx, + const ConstDigitTruth* labelsDigPtr, + ClusterTruth* labelsClusPtr, + GeometryTGeo* geom) +{ + const auto& d = digits[digitIdx]; + const uint16_t chipID = d.getChipIndex(); + const uint16_t row = d.getRow(); + const uint16_t col = d.getColumn(); + + if (labelsClusPtr) { + int nlab = 0; + fetchMCLabels(digitIdx, labelsDigPtr, nlab); + const auto cnt = static_cast(clusters.size()); + for (int i = nlab; i--;) { + labels.addElement(cnt, labelsBuff[i]); + } + } + + // 1×1 pattern: rowSpan=1, colSpan=1, one byte = 0x80 + patterns.emplace_back(1); + patterns.emplace_back(1); + patterns.emplace_back(0x80); + + Cluster cluster; + cluster.chipID = chipID; + cluster.row = row; + cluster.col = col; + cluster.size = 1; + if (geom) { + cluster.subDetID = geom->getIOTOFLayer(chipID); + } + math_utils::Point3D localClsCoords{0.f, 0.f, 0.f}; + getClusterLocalCoordinates(cluster, localClsCoords); + // getClusterGlobalCoordinates(cluster, localClsCoords); + cluster.xCoord = localClsCoords.x(); + cluster.yCoord = localClsCoords.y(); + cluster.zCoord = localClsCoords.z(); + + clusters.emplace_back(cluster); +} + +//__________________________________________________ +void Clusterer::ClustererThread::fetchMCLabels(uint32_t digID, const ConstDigitTruth* labelsDig, int& nfilled) +{ + if (nfilled >= MaxLabels) { + return; + } + if (!labelsDig || digID >= labelsDig->getIndexedSize()) { + return; + } + const auto& lbls = labelsDig->getLabels(digID); + for (int i = lbls.size(); i--;) { + int ic = nfilled; + for (; ic--;) { + if (labelsBuff[ic] == lbls[i]) { + return; // already present + } + } + labelsBuff[nfilled++] = lbls[i]; + if (nfilled >= MaxLabels) { + break; + } + } +} + +} // namespace o2::iotof diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Segmentation.h b/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Segmentation.h index cd0ab55bd03d7..0a7d78e6e75cb 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Segmentation.h +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Segmentation.h @@ -45,6 +45,15 @@ class Segmentation const float passiveEdgeSide, const float sensorLayerThicknessEff, const float sensorLayerThickness, const int subDetectorID); void configChip(const ChipSpecifics& specsConfig, const int subDetectorID); + float getPitchRow(const int subDetectorID) + { + return (subDetectorID == 0) ? mITofSpecsConfig.PitchRow : mOTofSpecsConfig.PitchRow; + } + float getPitchCol(const int subDetectorID) + { + return (subDetectorID == 0) ? mITofSpecsConfig.PitchCol : mOTofSpecsConfig.PitchCol; + } + /// Transformation from Geant detector centered local coordinates (cm) to /// Pixel cell numbers iRow and iCol. /// Returns kTRUE if point x,z is inside sensitive volume, kFALSE otherwise. @@ -122,7 +131,6 @@ class Segmentation detectorToLocalUnchecked(row, col, xRow, zCol, subDetectorID); return true; } - template bool detectorToLocal(L row, L col, math_utils::Point3D& loc, const int subDetectorID) { diff --git a/Detectors/Upgrades/ALICE3/IOTOF/workflow/CMakeLists.txt b/Detectors/Upgrades/ALICE3/IOTOF/workflow/CMakeLists.txt index 73ae6edd15cf4..14a0e215587d8 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/workflow/CMakeLists.txt +++ b/Detectors/Upgrades/ALICE3/IOTOF/workflow/CMakeLists.txt @@ -13,10 +13,13 @@ o2_add_library(IOTOFWorkflow TARGETVARNAME targetName SOURCES src/DigitReaderSpec.cxx src/DigitWriterSpec.cxx + src/ClustererSpec.cxx + src/ClusterWriterSpec.cxx src/RecoWorkflow.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2::DataFormatsIOTOF O2::DataFormatsITSMFT + O2::IOTOFReconstruction O2::DPLUtils ) diff --git a/Detectors/Upgrades/ALICE3/IOTOF/workflow/include/IOTOFWorkflow/ClusterWriterSpec.h b/Detectors/Upgrades/ALICE3/IOTOF/workflow/include/IOTOFWorkflow/ClusterWriterSpec.h new file mode 100644 index 0000000000000..90b75b5dbd9c9 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/IOTOF/workflow/include/IOTOFWorkflow/ClusterWriterSpec.h @@ -0,0 +1,24 @@ +// 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. + +#ifndef O2_IOTOF_CLUSTERWRITER +#define O2_IOTOF_CLUSTERWRITER + +#include "Framework/DataProcessorSpec.h" + +namespace o2::iotof +{ + +o2::framework::DataProcessorSpec getIOTOFClusterWriterSpec(bool useMC, bool dec); + +} // namespace o2::iotof + +#endif diff --git a/Detectors/Upgrades/ALICE3/IOTOF/workflow/include/IOTOFWorkflow/ClustererSpec.h b/Detectors/Upgrades/ALICE3/IOTOF/workflow/include/IOTOFWorkflow/ClustererSpec.h new file mode 100644 index 0000000000000..c735c35691a35 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/IOTOF/workflow/include/IOTOFWorkflow/ClustererSpec.h @@ -0,0 +1,40 @@ +// 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. + +#ifndef O2_IOTOF_CLUSTERERDPL +#define O2_IOTOF_CLUSTERERDPL + +#include "Framework/DataProcessorSpec.h" +#include "Framework/Task.h" +#include "IOTOFReconstruction/Clusterer.h" + +namespace o2::iotof +{ + +class ClustererDPL : public o2::framework::Task +{ + public: + ClustererDPL(bool useMC) : mUseMC(useMC) {} + void init(o2::framework::InitContext& ic) final; + void run(o2::framework::ProcessingContext& pc) final; + + private: + static constexpr int mLayers = 2; + bool mUseMC = true; + int mNThreads = 1; + o2::iotof::Clusterer mClusterer; +}; + +o2::framework::DataProcessorSpec getIOTOFClustererSpec(bool useMC); + +} // namespace o2::iotof + +#endif diff --git a/Detectors/Upgrades/ALICE3/IOTOF/workflow/src/ClusterWriterSpec.cxx b/Detectors/Upgrades/ALICE3/IOTOF/workflow/src/ClusterWriterSpec.cxx new file mode 100644 index 0000000000000..4f5fabd54404f --- /dev/null +++ b/Detectors/Upgrades/ALICE3/IOTOF/workflow/src/ClusterWriterSpec.cxx @@ -0,0 +1,103 @@ +// 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 ClusterWriterSpec.cxx + +#include +#include +#include +#include +#include + +#include "IOTOFWorkflow/ClusterWriterSpec.h" +#include "Framework/ConcreteDataMatcher.h" +#include "Framework/DataRef.h" +#include "DetectorsCommonDataFormats/DetID.h" +#include "DPLUtils/MakeRootTreeWriterSpec.h" +#include "DataFormatsIOTOF/Cluster.h" +#include "DataFormatsIOTOF/Digit.h" +#include "DataFormatsITSMFT/ROFRecord.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "SimulationDataFormat/ConstMCTruthContainer.h" +#include "SimulationDataFormat/IOMCTruthContainerView.h" + +using namespace o2::framework; + +namespace o2::iotof +{ + +template +using BranchDefinition = MakeRootTreeWriterSpec::BranchDefinition; +using ClustersType = std::vector; +using PatternsType = std::vector; +using ROFrameType = std::vector; + +DataProcessorSpec getClusterWriterSpec(bool mctruth, bool dec, o2::header::DataOrigin detOrig, o2::detectors::DetID detId) +{ + std::string detStr = o2::detectors::DetID::getName(detId); + std::string detStrL = dec ? "o2_" : ""; // for decoded digits prepend by o2 + detStrL += detStr; + std::transform(detStrL.begin(), detStrL.end(), detStrL.begin(), ::tolower); + auto logger = [](std::vector const& inClusters) { + LOG(info) << "RECEIVED CLUSTERS SIZE " << inClusters.size(); + }; + + // the callback to be set as hook for custom action when the writer is closed + auto finishWriting = [](TFile* outputfile, TTree* outputtree) { + const auto* brArr = outputtree->GetListOfBranches(); + int64_t nent = 0; + for (const auto* brc : *brArr) { + int64_t n = ((const TBranch*)brc)->GetEntries(); + if (nent && (nent != n)) { + LOG(error) << "Branches have different number of entries"; + } + nent = n; + } + outputtree->SetEntries(nent); + outputtree->Write("", TObject::kOverwrite); + outputfile->Close(); + }; + + // handler for labels + // This is necessary since we can't store the original label buffer in a ROOT entry -- as is -- if it exceeds a certain size. + // We therefore convert it to a special split class. + auto fillLabels = [](TBranch& branch, std::vector const& labelbuffer, DataRef const& /*ref*/) { + o2::dataformats::ConstMCTruthContainerView labels(labelbuffer); + LOG(info) << "WRITING " << labels.getNElements() << " LABELS "; + + o2::dataformats::IOMCTruthContainerView outputcontainer; + auto ptr = &outputcontainer; + auto br = framework::RootTreeWriter::remapBranch(branch, &ptr); + outputcontainer.adopt(labelbuffer); + br->Fill(); + br->ResetAddress(); + }; + + return MakeRootTreeWriterSpec((detStr + "ClusterWriter" + (dec ? "_dec" : "")).c_str(), + (detStrL + "clusters.root").c_str(), + MakeRootTreeWriterSpec::TreeAttributes{.name = "o2sim", .title = "Tree with TF3 clusters"}, + BranchDefinition{InputSpec{"tf3_compclus", detOrig, "COMPCLUSTERS", 0}, + (detStr + "ClusterComp").c_str()}, + BranchDefinition{InputSpec{"tf3_patterns", detOrig, "PATTERNS", 0}, + (detStr + "ClusterPatt").c_str()}, + BranchDefinition{InputSpec{"tf3_ROframes", detOrig, "CLUSTERSROF", 0}, + (detStr + "ClusterROF").c_str(), "cluster-rof-branch"}, + BranchDefinition>{InputSpec{"tf3_labels", detOrig, "CLUSTERSMCTR", 0}, + (detStr + "ClusterMCTruth").c_str(), + (mctruth ? 1 : 0), fillLabels})(); +} + +DataProcessorSpec getIOTOFClusterWriterSpec(bool mctruth, bool dec) +{ + return getClusterWriterSpec(mctruth, dec, o2::header::gDataOriginTF3, o2::detectors::DetID::TF3); +} + +} // namespace o2::iotof diff --git a/Detectors/Upgrades/ALICE3/IOTOF/workflow/src/ClustererSpec.cxx b/Detectors/Upgrades/ALICE3/IOTOF/workflow/src/ClustererSpec.cxx new file mode 100644 index 0000000000000..8a2042c476044 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/IOTOF/workflow/src/ClustererSpec.cxx @@ -0,0 +1,126 @@ +// 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. + +#include "IOTOFWorkflow/ClustererSpec.h" +#include "DetectorsBase/GeometryManager.h" +#include "DataFormatsIOTOF/Cluster.h" +#include "DataFormatsIOTOF/Digit.h" +#include "DataFormatsITSMFT/ROFRecord.h" +#include "Framework/ConfigParamRegistry.h" +#include "Framework/Logger.h" +#include "SimulationDataFormat/ConstMCTruthContainer.h" + +#include + +using namespace o2::framework; + +namespace o2::iotof +{ + +void ClustererDPL::init(o2::framework::InitContext& ic) +{ + mNThreads = std::max(1, ic.options().get("nthreads")); +} + +void ClustererDPL::run(o2::framework::ProcessingContext& pc) +{ + LOG(info) << "[ClustererDPL] Entered run() with " << mNThreads << " threads"; + o2::base::GeometryManager::loadGeometry("o2sim_geometry.root", false, true); + + LOG(info) << "[ClustererDPL] Geometry loaded"; + uint64_t totalClusters = 0; + + // Loop on layers to be added here, for now only one layer is processed + int iLayer = 0; + LOG(info) << "[ClustererDPL] Getting digits for layer " << iLayer; + auto digits = pc.inputs().get>(std::format("digits_{}", iLayer)); + auto rofs = pc.inputs().get>(std::format("ROframes_{}", iLayer)); + + LOG(info) << "[ClustererDPL] Got " << digits.size() << " digits and " << rofs.size() << " ROFs for layer " << iLayer; + gsl::span labelbuffer; + if (mUseMC) { + LOG(info) << "[ClustererDPL] Getting MC labels for layer " << iLayer; + labelbuffer = pc.inputs().get>(std::format("labels_{}", iLayer)); + LOG(info) << "[ClustererDPL] Got " << labelbuffer.size() << " bytes of MC labels for layer " << iLayer; + } + o2::dataformats::ConstMCTruthContainerView labels(labelbuffer); + LOG(info) << "[ClustererDPL] Got MC labels for layer " << iLayer; + + std::vector clusters; + std::vector patterns; + std::vector clusterROFs; + std::unique_ptr> clusterLabels; + if (mUseMC) { + clusterLabels = std::make_unique>(); + } + + LOG(info) << "[ClustererDPL] Running IOTOFClusterer on layer " << iLayer; + mClusterer.process(digits, + rofs, + clusters, + patterns, + clusterROFs, + mUseMC ? &labels : nullptr, + clusterLabels.get()); + LOG(info) << "[ClustererDPL] IOTOFClusterer produced " << clusters.size() << " clusters for layer " << iLayer; + const auto subspec = static_cast(0); + pc.outputs().snapshot(o2::framework::Output{"TF3", "COMPCLUSTERS", subspec}, clusters); + pc.outputs().snapshot(o2::framework::Output{"TF3", "PATTERNS", subspec}, patterns); + pc.outputs().snapshot(o2::framework::Output{"TF3", "CLUSTERSROF", subspec}, clusterROFs); + if (mUseMC) { + pc.outputs().snapshot(o2::framework::Output{"TF3", "CLUSTERSMCTR", subspec}, *clusterLabels); + } + totalClusters += clusters.size(); + LOGP(info, "[ClustererDPL] IOTOFClusterer layer {} pushed {} clusters in {} ROFs", iLayer, clusters.size(), clusterROFs.size()); + + LOGP(info, "[ClustererDPL] IOTOFClusterer produced {} clusters", totalClusters); +} + +o2::framework::DataProcessorSpec getClustererSpec(bool useMC) +{ + + LOG(info) << "[ClustererSpec] Creating DataProcessorSpec for IOTOFClusterer with useMC=" << useMC; + static constexpr int nLayers = 2; + std::vector inputs; + // Currently TF3 digits (unlike TRK) are not separated by layer, eventually per-layer reading here + int iLayer = 0; + inputs.emplace_back(std::format("digits_{}", iLayer), "TF3", "DIGITS", iLayer, o2::framework::Lifetime::Timeframe); + inputs.emplace_back(std::format("ROframes_{}", iLayer), "TF3", "DIGITSROF", iLayer, o2::framework::Lifetime::Timeframe); + if (useMC) { + inputs.emplace_back(std::format("labels_{}", iLayer), "TF3", "DIGITSMCTR", iLayer, o2::framework::Lifetime::Timeframe); + } + LOG(info) << "[ClustererSpec] Created " << inputs.size() << " input specifications for IOTOFClusterer"; + + std::vector outputs; + outputs.emplace_back("TF3", "COMPCLUSTERS", iLayer, o2::framework::Lifetime::Timeframe); + outputs.emplace_back("TF3", "PATTERNS", iLayer, o2::framework::Lifetime::Timeframe); + outputs.emplace_back("TF3", "CLUSTERSROF", iLayer, o2::framework::Lifetime::Timeframe); + if (useMC) { + outputs.emplace_back("TF3", "CLUSTERSMCTR", iLayer, o2::framework::Lifetime::Timeframe); + } + LOG(info) << "[ClustererSpec] Created " << outputs.size() << " output specifications for IOTOFClusterer"; + + LOG(info) << "[ClustererSpec] Returning ... "; + return o2::framework::DataProcessorSpec{ + "iotof-clusterer", + inputs, + outputs, + o2::framework::AlgorithmSpec{o2::framework::adaptFromTask(useMC)}, + o2::framework::Options{{"nthreads", o2::framework::VariantType::Int, 1, {"Number of clustering threads"}} + }}; +} + +DataProcessorSpec getIOTOFClustererSpec(bool mctruth) +{ + return getClustererSpec(mctruth); +} + +} // namespace o2::iotof diff --git a/Detectors/Upgrades/ALICE3/IOTOF/workflow/src/RecoWorkflow.cxx b/Detectors/Upgrades/ALICE3/IOTOF/workflow/src/RecoWorkflow.cxx index b9be173842d7d..ef9977812fc27 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/workflow/src/RecoWorkflow.cxx +++ b/Detectors/Upgrades/ALICE3/IOTOF/workflow/src/RecoWorkflow.cxx @@ -11,6 +11,8 @@ #include "IOTOFWorkflow/RecoWorkflow.h" #include "IOTOFWorkflow/DigitReaderSpec.h" +#include "IOTOFWorkflow/ClustererSpec.h" +#include "IOTOFWorkflow/ClusterWriterSpec.h" #include "Framework/CCDBParamSpec.h" #include @@ -24,19 +26,28 @@ framework::WorkflowSpec getWorkflow(bool useMC, bool upstreamClusters, bool disableRootOutput) { + LOG(info) << "[RecoWorkflow] ENTERING IOTOF RecoWorkflow.cxx"; framework::WorkflowSpec specs; + LOG(info) << "[RecoWorkflow] useMC: " << useMC; + LOG(info) << "[RecoWorkflow] upstreamDigits: " << upstreamDigits; + LOG(info) << "[RecoWorkflow] upstreamClusters: " << upstreamClusters; + LOG(info) << "[RecoWorkflow] disableRootOutput: " << disableRootOutput; if (!(upstreamDigits || upstreamClusters)) { + LOG(info) << "[RecoWorkflow] Adding DigitReaderSpec to workflow"; specs.emplace_back(o2::iotof::getIOTOFDigitReaderSpec(useMC, false, "tf3digits.root")); } if (!upstreamClusters) { - // specs.emplace_back(o2::iotof::getClustererSpec(useMC)); + LOG(info) << "[RecoWorkflow] Adding ClustererSpec to workflow"; + specs.emplace_back(o2::iotof::getIOTOFClustererSpec(useMC)); } if (!disableRootOutput) { - // specs.emplace_back(o2::iotof::getClusterWriterSpec(useMC)); + LOG(info) << "[RecoWorkflow] Adding ClusterWriterSpec to workflow"; + specs.emplace_back(o2::iotof::getIOTOFClusterWriterSpec(useMC, false)); } + LOG(info) << "[RecoWorkflow] IOTOF RecoWorkflow.cxx completed, starting execution of workflow with " << specs.size() << " specifications"; return specs; } diff --git a/Detectors/Upgrades/ALICE3/IOTOF/workflow/src/iotof-reco-workflow.cxx b/Detectors/Upgrades/ALICE3/IOTOF/workflow/src/iotof-reco-workflow.cxx index 0988262efe538..7b3c4722006ff 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/workflow/src/iotof-reco-workflow.cxx +++ b/Detectors/Upgrades/ALICE3/IOTOF/workflow/src/iotof-reco-workflow.cxx @@ -22,8 +22,6 @@ #include "IOTOFWorkflow/RecoWorkflow.h" #include "CommonUtils/ConfigurableParam.h" -// #include "ITStracking/TrackingConfigParam.h" -// #include "ITStracking/Configuration.h" #include "Framework/CallbacksPolicy.h" #include "Framework/ConfigContext.h" @@ -66,6 +64,7 @@ void customize(std::vector& workflowOptions) o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& configcontext) { + LOG(info) << "[iotof-reco-workflow] Entering iotof-reco-workflow.cxx defineDataProcessing function"; // Update the (declared) parameters if changed from the command line auto useMC = !configcontext.options().get("disable-mc"); // auto hitRecoConfig = configcontext.options().get("tracking-from-hits-config"); @@ -77,7 +76,8 @@ o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext co o2::conf::ConfigurableParam::updateFromString(configcontext.options().get("configKeyValues")); // write the configuration used for the reco workflow - o2::conf::ConfigurableParam::writeINI("o2itsrecoflow_configuration.ini"); + o2::conf::ConfigurableParam::writeINI("o2tf3recoflow_configuration.ini"); + LOG(info) << "[iotof-reco-workflow] About to call o2::iotof::reco_workflow::getWorkflow"; return o2::iotof::reco_workflow::getWorkflow(useMC, /*hitRecoConfig,*/ extDigits, extClusters, disableRootOutput /*, useGpuWF, gpuDevice*/); } From 3cbae3d8e0fd376e76971af7fc73d6bc655514e2 Mon Sep 17 00:00:00 2001 From: Marcello Di Costanzo Date: Mon, 29 Jun 2026 10:22:27 +0200 Subject: [PATCH 2/8] Add time info to clusters --- .../include/DataFormatsIOTOF/Cluster.h | 1 + .../ALICE3/IOTOF/macros/CheckClustersIOTOF.C | 7 ++++--- .../ALICE3/IOTOF/reconstruction/src/Clusterer.cxx | 13 +++++++------ 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/IOTOF/DataFormatsIOTOF/include/DataFormatsIOTOF/Cluster.h b/Detectors/Upgrades/ALICE3/IOTOF/DataFormatsIOTOF/include/DataFormatsIOTOF/Cluster.h index 5e75c192ae0e3..784666b372dbd 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/DataFormatsIOTOF/include/DataFormatsIOTOF/Cluster.h +++ b/Detectors/Upgrades/ALICE3/IOTOF/DataFormatsIOTOF/include/DataFormatsIOTOF/Cluster.h @@ -30,6 +30,7 @@ struct Cluster { float xCoord = 0.f; float yCoord = 0.f; float zCoord = 0.f; + float time = 0.f; std::string asString() const; diff --git a/Detectors/Upgrades/ALICE3/IOTOF/macros/CheckClustersIOTOF.C b/Detectors/Upgrades/ALICE3/IOTOF/macros/CheckClustersIOTOF.C index feb5dad24d0de..4fee28b651a4b 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/macros/CheckClustersIOTOF.C +++ b/Detectors/Upgrades/ALICE3/IOTOF/macros/CheckClustersIOTOF.C @@ -84,7 +84,7 @@ void CheckClustersIOTOF(std::string digiFilePath = "tf3digits.root", std::string o2::dataformats::ConstMCTruthContainer labels; clsPlabelsArr->copyandflatten(labels); - auto clsTuple = new TNtuple("clsTuple", "clsTuple", "chip_id:x:y:z:subdet_id:row:col"); + auto clsTuple = new TNtuple("clsTuple", "clsTuple", "chip_id:x:y:z:subdet_id:row:col:time"); clsTuple->SetDirectory(nullptr); TH1F* histXCoordCls = new TH1F("histXCoordCls", "histXCoordCls", 8000, -100, 100); @@ -144,14 +144,15 @@ void CheckClustersIOTOF(std::string digiFilePath = "tf3digits.root", std::string o2::math_utils::Point3D localClsCoords(x, y, z); // local Digit const auto globalClsCoords = tofGeo->getMatrixL2G(chipID)(localClsCoords); // convert to global - std::cout << "Cluster " << iCls << ": chipID = " << chipID << ", X=" << globalClsCoords.x() << ", Y=" << globalClsCoords.y() << ", Z=" << globalClsCoords.z() << std::endl; + std::cout << "Cluster " << iCls << ": chipID = " << chipID << ", X=" << globalClsCoords.x() << ", Y=" << globalClsCoords.y() << ", Z=" << globalClsCoords.z() << ", time=" << (*clsArray)[iCls].time << std::endl; clsTuple->Fill((*clsArray)[iCls].chipID, globalClsCoords.x(), globalClsCoords.y(), globalClsCoords.z(), (*clsArray)[iCls].subDetID, (*clsArray)[iCls].row, - (*clsArray)[iCls].col); + (*clsArray)[iCls].col, + (*clsArray)[iCls].time); histXCoordCls->Fill(globalClsCoords.x()); histYCoordCls->Fill(globalClsCoords.y()); histZCoordCls->Fill(globalClsCoords.z()); diff --git a/Detectors/Upgrades/ALICE3/IOTOF/reconstruction/src/Clusterer.cxx b/Detectors/Upgrades/ALICE3/IOTOF/reconstruction/src/Clusterer.cxx index 10c1849fc594d..3ba9d243443ca 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/reconstruction/src/Clusterer.cxx +++ b/Detectors/Upgrades/ALICE3/IOTOF/reconstruction/src/Clusterer.cxx @@ -30,7 +30,7 @@ o2::math_utils::Point3D Clusterer::getClusterGlobalCoordinates(const Clus Segmentation::Instance()->detectorToLocal(cluster.row, cluster.col, coords, cluster.subDetID); LOG(info) << "[Clusterer] Cluster local coordinates: x=" << coords.x() << ", y=" << coords.y() << ", z=" << coords.z(); - GeometryTGeo::Instance()->getMatrixL2G(cluster.subDetID)(coords); + GeometryTGeo::Instance()->getMatrixL2G(cluster.chipID)(coords); LOG(info) << "[Clusterer] Cluster global coordinates: x=" << coords.x() << ", y=" << coords.y() << ", z=" << coords.z(); return coords; @@ -183,10 +183,11 @@ void Clusterer::ClustererThread::finishChipSingleHitFast(gsl::span ClusterTruth* labelsClusPtr, GeometryTGeo* geom) { - const auto& d = digits[digitIdx]; - const uint16_t chipID = d.getChipIndex(); - const uint16_t row = d.getRow(); - const uint16_t col = d.getColumn(); + const auto& digit = digits[digitIdx]; + const uint16_t chipID = digit.getChipIndex(); + const uint16_t row = digit.getRow(); + const uint16_t col = digit.getColumn(); + const float time = digit.getTime(); if (labelsClusPtr) { int nlab = 0; @@ -216,7 +217,7 @@ void Clusterer::ClustererThread::finishChipSingleHitFast(gsl::span cluster.xCoord = localClsCoords.x(); cluster.yCoord = localClsCoords.y(); cluster.zCoord = localClsCoords.z(); - + cluster.time = time; clusters.emplace_back(cluster); } From 6e150681f79341f53b4a785c874aae7e41015963 Mon Sep 17 00:00:00 2001 From: Marcello Di Costanzo Date: Mon, 29 Jun 2026 14:21:10 +0200 Subject: [PATCH 3/8] Remove coordinates from Cluster struct --- .../include/DataFormatsIOTOF/Cluster.h | 3 -- .../ALICE3/IOTOF/macros/CheckClustersIOTOF.C | 1 - .../include/IOTOFReconstruction/Clusterer.h | 6 ---- .../IOTOF/reconstruction/src/Clusterer.cxx | 32 ------------------- 4 files changed, 42 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/IOTOF/DataFormatsIOTOF/include/DataFormatsIOTOF/Cluster.h b/Detectors/Upgrades/ALICE3/IOTOF/DataFormatsIOTOF/include/DataFormatsIOTOF/Cluster.h index 784666b372dbd..bc6df4004569e 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/DataFormatsIOTOF/include/DataFormatsIOTOF/Cluster.h +++ b/Detectors/Upgrades/ALICE3/IOTOF/DataFormatsIOTOF/include/DataFormatsIOTOF/Cluster.h @@ -27,9 +27,6 @@ struct Cluster { int16_t subDetID = -1; int16_t layer = -1; int16_t disk = -1; - float xCoord = 0.f; - float yCoord = 0.f; - float zCoord = 0.f; float time = 0.f; std::string asString() const; diff --git a/Detectors/Upgrades/ALICE3/IOTOF/macros/CheckClustersIOTOF.C b/Detectors/Upgrades/ALICE3/IOTOF/macros/CheckClustersIOTOF.C index 4fee28b651a4b..53aea1c0ed869 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/macros/CheckClustersIOTOF.C +++ b/Detectors/Upgrades/ALICE3/IOTOF/macros/CheckClustersIOTOF.C @@ -157,7 +157,6 @@ void CheckClustersIOTOF(std::string digiFilePath = "tf3digits.root", std::string histYCoordCls->Fill(globalClsCoords.y()); histZCoordCls->Fill(globalClsCoords.z()); } // end loop on clusters array - } // end loop on ROFRecords std::cout << "Cluster array size: " << clsTuple->GetEntries() << std::endl; diff --git a/Detectors/Upgrades/ALICE3/IOTOF/reconstruction/include/IOTOFReconstruction/Clusterer.h b/Detectors/Upgrades/ALICE3/IOTOF/reconstruction/include/IOTOFReconstruction/Clusterer.h index 1b24fc9456e6f..8caba7089464e 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/reconstruction/include/IOTOFReconstruction/Clusterer.h +++ b/Detectors/Upgrades/ALICE3/IOTOF/reconstruction/include/IOTOFReconstruction/Clusterer.h @@ -89,12 +89,6 @@ class Clusterer gsl::span digMC2ROFs = {}, std::vector* clusterMC2ROFs = nullptr); - static o2::math_utils::Point3D getClusterLocalCoordinates(const Cluster& cluster, - math_utils::Point3D& localClsCoords) noexcept; - - static o2::math_utils::Point3D getClusterGlobalCoordinates(const Cluster& cluster, - math_utils::Point3D& localClsCoords) noexcept; - protected: std::unique_ptr mThread; std::vector mSortIdx; ///< reusable per-ROF sort buffer diff --git a/Detectors/Upgrades/ALICE3/IOTOF/reconstruction/src/Clusterer.cxx b/Detectors/Upgrades/ALICE3/IOTOF/reconstruction/src/Clusterer.cxx index 3ba9d243443ca..22be3f759da75 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/reconstruction/src/Clusterer.cxx +++ b/Detectors/Upgrades/ALICE3/IOTOF/reconstruction/src/Clusterer.cxx @@ -22,32 +22,6 @@ namespace o2::iotof { -//__________________________________________________ -o2::math_utils::Point3D Clusterer::getClusterGlobalCoordinates(const Cluster& cluster, math_utils::Point3D& coords) noexcept -{ - LOG(info) << "[Clusterer] getClusterGlobalCoordinates() called for cluster at chipID " << cluster.chipID - << ", row " << cluster.row << ", col " << cluster.col; - - Segmentation::Instance()->detectorToLocal(cluster.row, cluster.col, coords, cluster.subDetID); - LOG(info) << "[Clusterer] Cluster local coordinates: x=" << coords.x() << ", y=" << coords.y() << ", z=" << coords.z(); - GeometryTGeo::Instance()->getMatrixL2G(cluster.chipID)(coords); - - LOG(info) << "[Clusterer] Cluster global coordinates: x=" << coords.x() << ", y=" << coords.y() << ", z=" << coords.z(); - return coords; -} - -//__________________________________________________ -o2::math_utils::Point3D Clusterer::getClusterLocalCoordinates(const Cluster& cluster, math_utils::Point3D& coords) noexcept -{ - LOG(info) << "[Clusterer] getClusterLocalCoordinates() called for cluster at chipID " << cluster.chipID - << ", row " << cluster.row << ", col " << cluster.col; - - Segmentation::Instance()->detectorToLocal(cluster.row, cluster.col, coords, cluster.subDetID); - - LOG(info) << "[Clusterer] Cluster local coordinates: x=" << coords.x() << ", y=" << coords.y() << ", z=" << coords.z(); - return coords; -} - //__________________________________________________ void Clusterer::process(gsl::span digits, gsl::span digitROFs, @@ -211,12 +185,6 @@ void Clusterer::ClustererThread::finishChipSingleHitFast(gsl::span if (geom) { cluster.subDetID = geom->getIOTOFLayer(chipID); } - math_utils::Point3D localClsCoords{0.f, 0.f, 0.f}; - getClusterLocalCoordinates(cluster, localClsCoords); - // getClusterGlobalCoordinates(cluster, localClsCoords); - cluster.xCoord = localClsCoords.x(); - cluster.yCoord = localClsCoords.y(); - cluster.zCoord = localClsCoords.z(); cluster.time = time; clusters.emplace_back(cluster); } From cc3a7e338c6d9070aad00e3b859ad1e7daeed6cb Mon Sep 17 00:00:00 2001 From: Marcello Di Costanzo Date: Mon, 29 Jun 2026 14:31:20 +0200 Subject: [PATCH 4/8] Change cluster time type to double --- .../IOTOF/DataFormatsIOTOF/include/DataFormatsIOTOF/Cluster.h | 2 +- .../Upgrades/ALICE3/IOTOF/reconstruction/src/Clusterer.cxx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/IOTOF/DataFormatsIOTOF/include/DataFormatsIOTOF/Cluster.h b/Detectors/Upgrades/ALICE3/IOTOF/DataFormatsIOTOF/include/DataFormatsIOTOF/Cluster.h index bc6df4004569e..ce588364331cc 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/DataFormatsIOTOF/include/DataFormatsIOTOF/Cluster.h +++ b/Detectors/Upgrades/ALICE3/IOTOF/DataFormatsIOTOF/include/DataFormatsIOTOF/Cluster.h @@ -27,7 +27,7 @@ struct Cluster { int16_t subDetID = -1; int16_t layer = -1; int16_t disk = -1; - float time = 0.f; + double time = 0.0; std::string asString() const; diff --git a/Detectors/Upgrades/ALICE3/IOTOF/reconstruction/src/Clusterer.cxx b/Detectors/Upgrades/ALICE3/IOTOF/reconstruction/src/Clusterer.cxx index 22be3f759da75..fc44f5de0a4a0 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/reconstruction/src/Clusterer.cxx +++ b/Detectors/Upgrades/ALICE3/IOTOF/reconstruction/src/Clusterer.cxx @@ -161,7 +161,7 @@ void Clusterer::ClustererThread::finishChipSingleHitFast(gsl::span const uint16_t chipID = digit.getChipIndex(); const uint16_t row = digit.getRow(); const uint16_t col = digit.getColumn(); - const float time = digit.getTime(); + const double time = digit.getTime(); if (labelsClusPtr) { int nlab = 0; From df0218f87d25fac335390bbd385621c7b66461d7 Mon Sep 17 00:00:00 2001 From: Marcello Di Costanzo Date: Tue, 30 Jun 2026 11:36:24 +0200 Subject: [PATCH 5/8] Implement matching cls-digit + simplify writer --- .../include/DataFormatsIOTOF/Cluster.h | 2 - .../IOTOF/DataFormatsIOTOF/src/Cluster.cxx | 4 +- .../ALICE3/IOTOF/macros/CheckClustersIOTOF.C | 81 ++++++++++++++++--- .../IOTOF/reconstruction/src/Clusterer.cxx | 3 +- .../IOTOF/workflow/src/ClusterWriterSpec.cxx | 40 ++------- .../IOTOF/workflow/src/ClustererSpec.cxx | 4 +- 6 files changed, 82 insertions(+), 52 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/IOTOF/DataFormatsIOTOF/include/DataFormatsIOTOF/Cluster.h b/Detectors/Upgrades/ALICE3/IOTOF/DataFormatsIOTOF/include/DataFormatsIOTOF/Cluster.h index ce588364331cc..e00203c693632 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/DataFormatsIOTOF/include/DataFormatsIOTOF/Cluster.h +++ b/Detectors/Upgrades/ALICE3/IOTOF/DataFormatsIOTOF/include/DataFormatsIOTOF/Cluster.h @@ -25,8 +25,6 @@ struct Cluster { uint16_t col = 0; uint16_t size = 1; int16_t subDetID = -1; - int16_t layer = -1; - int16_t disk = -1; double time = 0.0; std::string asString() const; diff --git a/Detectors/Upgrades/ALICE3/IOTOF/DataFormatsIOTOF/src/Cluster.cxx b/Detectors/Upgrades/ALICE3/IOTOF/DataFormatsIOTOF/src/Cluster.cxx index eb58ca5057d90..2a3fbf5f40025 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/DataFormatsIOTOF/src/Cluster.cxx +++ b/Detectors/Upgrades/ALICE3/IOTOF/DataFormatsIOTOF/src/Cluster.cxx @@ -20,8 +20,8 @@ namespace o2::iotof std::string Cluster::asString() const { std::ostringstream stream; - stream << "chip=" << chipID << " row=" << row << " col=" << col << " size=" << size - << " subDet=" << subDetID << " layer=" << layer << " disk=" << disk; + stream << "chip=" << chipID << " row=" << row << " col=" << col + << " size=" << size << " subDet=" << subDetID; return stream.str(); } diff --git a/Detectors/Upgrades/ALICE3/IOTOF/macros/CheckClustersIOTOF.C b/Detectors/Upgrades/ALICE3/IOTOF/macros/CheckClustersIOTOF.C index 53aea1c0ed869..78686ebff127e 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/macros/CheckClustersIOTOF.C +++ b/Detectors/Upgrades/ALICE3/IOTOF/macros/CheckClustersIOTOF.C @@ -64,9 +64,11 @@ void CheckClustersIOTOF(std::string digiFilePath = "tf3digits.root", std::string std::vector* digiRofRecordsArr{nullptr}; digiTree->SetBranchAddress("TF3DigitROF", &digiRofRecordsArr); auto& digiRofArr = *digiRofRecordsArr; - o2::dataformats::IOMCTruthContainerView* digiPlabelsArr{nullptr}; - digiTree->SetBranchAddress("TF3DigitMCTruth", &digiPlabelsArr); + o2::dataformats::IOMCTruthContainerView* digiLabelsArr{nullptr}; + digiTree->SetBranchAddress("TF3DigitMCTruth", &digiLabelsArr); digiTree->GetEntry(0); + o2::dataformats::ConstMCTruthContainer digiLabels; + digiLabelsArr->copyandflatten(digiLabels); // Clusters TFile* clsFile = TFile::Open(clsFilePath.data()); @@ -76,13 +78,21 @@ void CheckClustersIOTOF(std::string digiFilePath = "tf3digits.root", std::string std::vector* clsRofRecordsArr{nullptr}; clsTree->SetBranchAddress("TF3ClusterROF", &clsRofRecordsArr); auto& clsRofArr = *clsRofRecordsArr; - o2::dataformats::IOMCTruthContainerView* clsPlabelsArr{nullptr}; - clsTree->SetBranchAddress("TF3ClusterMCTruth", &clsPlabelsArr); + o2::dataformats::MCTruthContainer* clsLabels{nullptr}; + clsTree->SetBranchAddress("TF3ClusterMCTruth", &clsLabels); clsTree->GetEntry(0); - - // Start script - o2::dataformats::ConstMCTruthContainer labels; - clsPlabelsArr->copyandflatten(labels); + + // Summary of entries in all branches + std::cout << std::endl; + std::cout << "---> Number of digits: " << digitsArray->size() << std::endl; + std::cout << "---> Number of digit ROFs: " << digiRofArr.size() << std::endl; + std::cout << "---> Number of clusters: " << clsArray->size() << std::endl; + std::cout << "---> Number of cluster ROFs: " << clsRofArr.size() << std::endl; + std::cout << "---> Number of digits with MC label: " << digiLabels.getNElements() << std::endl; + std::cout << "---> Number of digits with MC label: " << digiLabels.getIndexedSize() << std::endl; + std::cout << "---> Number of clusters with MC label: " << clsLabels->getNElements() << std::endl; + std::cout << "---> Number of clusters with MC label: " << clsLabels->getIndexedSize() << std::endl; + std::cout << std::endl; auto clsTuple = new TNtuple("clsTuple", "clsTuple", "chip_id:x:y:z:subdet_id:row:col:time"); clsTuple->SetDirectory(nullptr); @@ -93,6 +103,21 @@ void CheckClustersIOTOF(std::string digiFilePath = "tf3digits.root", std::string TH1F* histXCoordDigit = new TH1F("histXCoordDigit", "histXCoordDigit", 8000, -100, 100); TH1F* histYCoordDigit = new TH1F("histYCoordDigit", "histYCoordDigit", 8000, -100, 100); TH1F* histZCoordDigit = new TH1F("histZCoordDigit", "histZCoordDigit", 28000, -400, 400); + TH1F* histXCoordRes = new TH1F("histXCoordRes", "histXCoordRes", 100, -0.05, 0.05); + TH1F* histYCoordRes = new TH1F("histYCoordRes", "histYCoordRes", 100, -0.05, 0.05); + TH1F* histZCoordRes = new TH1F("histZCoordRes", "histZCoordRes", 100, -0.05, 0.05); + TH1F* histTimeRes = new TH1F("histTimeRes", "histTimeRes", 100, -0.05, 0.05); + + // Load all digits upfront and build a lookup map + int nDigits = digiTree->GetEntries(); + std::unordered_map digitsLabels; + for (int iDigit = 0; iDigit < digitsArray->size(); ++iDigit) { + auto label = digiLabels.getLabels(iDigit)[0]; + if (!label.isValid()) { + continue; + } + digitsLabels.emplace(label, iDigit); + } // LOOP on : ROFRecord array for (unsigned int iROF = 0; iROF < clsRofArr.size(); ++iROF) { @@ -101,8 +126,9 @@ void CheckClustersIOTOF(std::string digiFilePath = "tf3digits.root", std::string const unsigned int rofNEntries = clsRofArr[iROF].getNEntries(); // LOOP on : digits array + std::cout << "\n\n ----> Starting loop on digits for ROF " << iROF << " with index " << rofIndex << " and nEntries " << rofNEntries << std::endl; for (unsigned int iDigit = rofIndex; iDigit < rofIndex + rofNEntries; iDigit++) { - if (iDigit % 1000 == 0) { + if (iDigit % 10000 == 0) { std::cout << "Reading digit " << iDigit << " / " << digitsArray->size() << std::endl; } @@ -127,8 +153,9 @@ void CheckClustersIOTOF(std::string digiFilePath = "tf3digits.root", std::string // LOOP on : clusters array + std::cout << "\n\n ----> Starting loop on clusters for ROF " << iROF << " with index " << rofIndex << " and nEntries " << rofNEntries << std::endl; for (unsigned int iCls = rofIndex; iCls < rofIndex + rofNEntries; iCls++) { - if (iCls % 1000 == 0) { + if (iCls % 10000 == 0) { std::cout << "Reading cluster " << iCls << " / " << clsArray->size() << std::endl; } @@ -136,6 +163,7 @@ void CheckClustersIOTOF(std::string digiFilePath = "tf3digits.root", std::string Int_t iCol = (*clsArray)[iCls].col; Int_t chipID = (*clsArray)[iCls].chipID; Int_t subDetID = tofGeo->getIOTOFLayer(chipID); + Float_t time = (*clsArray)[iCls].time; Float_t x = 0.f, y = 0.f, z = 0.f; if (subDetID >= 0) { @@ -144,7 +172,6 @@ void CheckClustersIOTOF(std::string digiFilePath = "tf3digits.root", std::string o2::math_utils::Point3D localClsCoords(x, y, z); // local Digit const auto globalClsCoords = tofGeo->getMatrixL2G(chipID)(localClsCoords); // convert to global - std::cout << "Cluster " << iCls << ": chipID = " << chipID << ", X=" << globalClsCoords.x() << ", Y=" << globalClsCoords.y() << ", Z=" << globalClsCoords.z() << ", time=" << (*clsArray)[iCls].time << std::endl; clsTuple->Fill((*clsArray)[iCls].chipID, globalClsCoords.x(), globalClsCoords.y(), @@ -156,6 +183,34 @@ void CheckClustersIOTOF(std::string digiFilePath = "tf3digits.root", std::string histXCoordCls->Fill(globalClsCoords.x()); histYCoordCls->Fill(globalClsCoords.y()); histZCoordCls->Fill(globalClsCoords.z()); + + // Match to digit + auto digitLabelFromCls = (clsLabels->getLabels(iCls))[0]; + auto digitEntry = digitsLabels.find(digitLabelFromCls); + + if (digitEntry == digitsLabels.end()) { + LOG(error) << "No matching digit for cluster " << iCls << " with label " << digitLabelFromCls.getRawValue(); + continue; + } + + int iDigit = digitEntry->second; + Int_t iRowFromDigit = (*digitsArray)[iDigit].getRow(); + Int_t iColFromDigit = (*digitsArray)[iDigit].getColumn(); + Int_t iChipIDFromDigit = (*digitsArray)[iDigit].getChipIndex(); + Int_t iSubDetIDFromDigit = tofGeo->getIOTOFLayer(iChipIDFromDigit); + Float_t timeFromDigit = (*digitsArray)[iDigit].getTime(); + + float xFromDigit = 0.f, yFromDigit = 0.f, zFromDigit = 0.f; + if (iSubDetIDFromDigit >= 0) { + segGeom->detectorToLocal(iRowFromDigit, iColFromDigit, xFromDigit, zFromDigit, iSubDetIDFromDigit); + } + + o2::math_utils::Point3D localDigitCoordFromDigit(xFromDigit, yFromDigit, zFromDigit); // local Digit + const auto globalDigitCoordFromDigit = tofGeo->getMatrixL2G(iChipIDFromDigit)(localDigitCoordFromDigit); // convert to global + histXCoordRes->Fill(globalClsCoords.x() - globalDigitCoordFromDigit.X()); + histYCoordRes->Fill(globalClsCoords.y() - globalDigitCoordFromDigit.Y()); + histZCoordRes->Fill(globalClsCoords.z() - globalDigitCoordFromDigit.Z()); + histTimeRes->Fill(time - timeFromDigit); } // end loop on clusters array } // end loop on ROFRecords @@ -185,6 +240,10 @@ void CheckClustersIOTOF(std::string digiFilePath = "tf3digits.root", std::string histXCoordDigit->Write(); histYCoordDigit->Write(); histZCoordDigit->Write(); + histXCoordRes->Write(); + histYCoordRes->Write(); + histZCoordRes->Write(); + histTimeRes->Write(); outFile->Write(); outFile->Close(); diff --git a/Detectors/Upgrades/ALICE3/IOTOF/reconstruction/src/Clusterer.cxx b/Detectors/Upgrades/ALICE3/IOTOF/reconstruction/src/Clusterer.cxx index fc44f5de0a4a0..28cc0b1100ab2 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/reconstruction/src/Clusterer.cxx +++ b/Detectors/Upgrades/ALICE3/IOTOF/reconstruction/src/Clusterer.cxx @@ -152,7 +152,8 @@ void Clusterer::ClustererThread::processChip(gsl::span digits, } //__________________________________________________ -void Clusterer::ClustererThread::finishChipSingleHitFast(gsl::span digits, uint32_t digitIdx, +void Clusterer::ClustererThread::finishChipSingleHitFast(gsl::span digits, + uint32_t digitIdx, const ConstDigitTruth* labelsDigPtr, ClusterTruth* labelsClusPtr, GeometryTGeo* geom) diff --git a/Detectors/Upgrades/ALICE3/IOTOF/workflow/src/ClusterWriterSpec.cxx b/Detectors/Upgrades/ALICE3/IOTOF/workflow/src/ClusterWriterSpec.cxx index 4f5fabd54404f..e9efbda5b8700 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/workflow/src/ClusterWriterSpec.cxx +++ b/Detectors/Upgrades/ALICE3/IOTOF/workflow/src/ClusterWriterSpec.cxx @@ -39,6 +39,7 @@ using BranchDefinition = MakeRootTreeWriterSpec::BranchDefinition; using ClustersType = std::vector; using PatternsType = std::vector; using ROFrameType = std::vector; +using LabelsType = o2::dataformats::MCTruthContainer; DataProcessorSpec getClusterWriterSpec(bool mctruth, bool dec, o2::header::DataOrigin detOrig, o2::detectors::DetID detId) { @@ -50,49 +51,20 @@ DataProcessorSpec getClusterWriterSpec(bool mctruth, bool dec, o2::header::DataO LOG(info) << "RECEIVED CLUSTERS SIZE " << inClusters.size(); }; - // the callback to be set as hook for custom action when the writer is closed - auto finishWriting = [](TFile* outputfile, TTree* outputtree) { - const auto* brArr = outputtree->GetListOfBranches(); - int64_t nent = 0; - for (const auto* brc : *brArr) { - int64_t n = ((const TBranch*)brc)->GetEntries(); - if (nent && (nent != n)) { - LOG(error) << "Branches have different number of entries"; - } - nent = n; - } - outputtree->SetEntries(nent); - outputtree->Write("", TObject::kOverwrite); - outputfile->Close(); - }; - - // handler for labels - // This is necessary since we can't store the original label buffer in a ROOT entry -- as is -- if it exceeds a certain size. - // We therefore convert it to a special split class. - auto fillLabels = [](TBranch& branch, std::vector const& labelbuffer, DataRef const& /*ref*/) { - o2::dataformats::ConstMCTruthContainerView labels(labelbuffer); - LOG(info) << "WRITING " << labels.getNElements() << " LABELS "; - - o2::dataformats::IOMCTruthContainerView outputcontainer; - auto ptr = &outputcontainer; - auto br = framework::RootTreeWriter::remapBranch(branch, &ptr); - outputcontainer.adopt(labelbuffer); - br->Fill(); - br->ResetAddress(); - }; return MakeRootTreeWriterSpec((detStr + "ClusterWriter" + (dec ? "_dec" : "")).c_str(), (detStrL + "clusters.root").c_str(), MakeRootTreeWriterSpec::TreeAttributes{.name = "o2sim", .title = "Tree with TF3 clusters"}, BranchDefinition{InputSpec{"tf3_compclus", detOrig, "COMPCLUSTERS", 0}, - (detStr + "ClusterComp").c_str()}, + (detStr + "ClusterComp").c_str(), + logger}, BranchDefinition{InputSpec{"tf3_patterns", detOrig, "PATTERNS", 0}, (detStr + "ClusterPatt").c_str()}, BranchDefinition{InputSpec{"tf3_ROframes", detOrig, "CLUSTERSROF", 0}, (detStr + "ClusterROF").c_str(), "cluster-rof-branch"}, - BranchDefinition>{InputSpec{"tf3_labels", detOrig, "CLUSTERSMCTR", 0}, - (detStr + "ClusterMCTruth").c_str(), - (mctruth ? 1 : 0), fillLabels})(); + BranchDefinition{InputSpec{"tf3_labels", detOrig, "CLUSTERSMCTR", 0}, + (detStr + "ClusterMCTruth").c_str()})(); + } DataProcessorSpec getIOTOFClusterWriterSpec(bool mctruth, bool dec) diff --git a/Detectors/Upgrades/ALICE3/IOTOF/workflow/src/ClustererSpec.cxx b/Detectors/Upgrades/ALICE3/IOTOF/workflow/src/ClustererSpec.cxx index 8a2042c476044..2f7d018380504 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/workflow/src/ClustererSpec.cxx +++ b/Detectors/Upgrades/ALICE3/IOTOF/workflow/src/ClustererSpec.cxx @@ -71,7 +71,7 @@ void ClustererDPL::run(o2::framework::ProcessingContext& pc) mUseMC ? &labels : nullptr, clusterLabels.get()); LOG(info) << "[ClustererDPL] IOTOFClusterer produced " << clusters.size() << " clusters for layer " << iLayer; - const auto subspec = static_cast(0); + const auto subspec = static_cast(iLayer); pc.outputs().snapshot(o2::framework::Output{"TF3", "COMPCLUSTERS", subspec}, clusters); pc.outputs().snapshot(o2::framework::Output{"TF3", "PATTERNS", subspec}, patterns); pc.outputs().snapshot(o2::framework::Output{"TF3", "CLUSTERSROF", subspec}, clusterROFs); @@ -80,7 +80,7 @@ void ClustererDPL::run(o2::framework::ProcessingContext& pc) } totalClusters += clusters.size(); LOGP(info, "[ClustererDPL] IOTOFClusterer layer {} pushed {} clusters in {} ROFs", iLayer, clusters.size(), clusterROFs.size()); - + LOGP(info, "[ClustererDPL] IOTOFClusterer layer {} pushed {} MC labels", iLayer, mUseMC ? clusterLabels->getNElements() : 0); LOGP(info, "[ClustererDPL] IOTOFClusterer produced {} clusters", totalClusters); } From 9d314dde8e50eeb9de4c0c55587193480082a5a7 Mon Sep 17 00:00:00 2001 From: Marcello Di Costanzo Date: Tue, 30 Jun 2026 13:47:11 +0200 Subject: [PATCH 6/8] Cleanup printouts --- .../ALICE3/IOTOF/base/src/GeometryTGeo.cxx | 11 ++++---- .../ALICE3/IOTOF/macros/CheckDigitsIOTOF.C | 15 +---------- .../IOTOF/reconstruction/CMakeLists.txt | 3 --- .../include/IOTOFReconstruction/Clusterer.h | 3 --- .../IOTOF/reconstruction/src/Clusterer.cxx | 24 +++++++---------- .../include/IOTOFSimulation/Segmentation.h | 10 +------ .../IOTOF/workflow/src/ClusterWriterSpec.cxx | 1 - .../IOTOF/workflow/src/ClustererSpec.cxx | 26 +++++++------------ .../IOTOF/workflow/src/RecoWorkflow.cxx | 13 +++------- .../workflow/src/iotof-reco-workflow.cxx | 4 +-- 10 files changed, 33 insertions(+), 77 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/IOTOF/base/src/GeometryTGeo.cxx b/Detectors/Upgrades/ALICE3/IOTOF/base/src/GeometryTGeo.cxx index 9f6485b92e08e..c4846e305c230 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/base/src/GeometryTGeo.cxx +++ b/Detectors/Upgrades/ALICE3/IOTOF/base/src/GeometryTGeo.cxx @@ -158,8 +158,8 @@ int GeometryTGeo::getIOTOFChipIndex(int lay, int sta, int mod, int chip) const bool GeometryTGeo::getIOTOFChipId(int index, int& lay, int& sta, int& mod, int& chip) const { - lay = getIOTOFLayer(index); // Get IOTOF layer - index -= getIOTOFFirstChipIndex(lay); // Get index relative to layer + lay = getIOTOFLayer(index); + index -= getIOTOFFirstChipIndex(lay); sta = mNumberOfStavesIOTOF[lay] > 0 ? index / mNumberOfChipsPerStaveIOTOF[lay] : -1; index %= mNumberOfChipsPerStaveIOTOF[lay]; mod = mNumberOfModulesIOTOF[lay] > 0 ? index / mNumberOfChipsPerModuleIOTOF[lay] : -1; @@ -284,14 +284,15 @@ void GeometryTGeo::Build(int loadTrans) mLastChipIndex[j] = numberOfChips - 1; } - LOG(info) << "[GeometryTGeo] numberOfChipsITOF = " << mNumberOfChipsIOTOF[0] << ", numberOfChipsOTOF = " << mNumberOfChipsIOTOF[1] << ", numberOfChips = " << numberOfChips << ", mNumberOfChipesPerStaveITOF" << mNumberOfChipsPerStaveIOTOF[0]; + LOG(info) << "TF3 geometry: numberOfChipsITOF = " << mNumberOfChipsIOTOF[0] << ", numberOfChipsOTOF = " + << mNumberOfChipsIOTOF[1] << ", numberOfChips = " << numberOfChips << ", mNumberOfChipesPerStaveITOF" + << mNumberOfChipsPerStaveIOTOF[0]; setSize(numberOfChips); defineSensors(); fillTrackingFramesCache(); fillMatrixCache(loadTrans); - // LOG(info) << "[GeometryTGeo] Filling matrix cache"; - // fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::L2G)); + // fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::L2G)); } void GeometryTGeo::defineSensors() diff --git a/Detectors/Upgrades/ALICE3/IOTOF/macros/CheckDigitsIOTOF.C b/Detectors/Upgrades/ALICE3/IOTOF/macros/CheckDigitsIOTOF.C index aeacfd56ccb58..cd9e806ca24e7 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/macros/CheckDigitsIOTOF.C +++ b/Detectors/Upgrades/ALICE3/IOTOF/macros/CheckDigitsIOTOF.C @@ -146,9 +146,6 @@ void CheckDigitsIOTOF(std::string digifile = "tf3digits.root", std::string hitfi plabelsArr->copyandflatten(labels); // LOOP on : ROFRecord array - TH1F* histXCoord = new TH1F("histXCoord", "histXCoord", 8000, -100, 100); - TH1F* histYCoord = new TH1F("histYCoord", "histYCoord", 8000, -100, 100); - TH1F* histZCoord = new TH1F("histZCoord", "histZCoord", 28000, -400, 400); for (unsigned int iROF = 0; iROF < rofArr.size(); ++iROF) { const unsigned int rofIndex = rofArr[iROF].getFirstEntry(); @@ -230,11 +227,8 @@ void CheckDigitsIOTOF(std::string digifile = "tf3digits.root", std::string hitfi locH.X(), locH.Z(), /// x and z of the hit in the local reference frame: hit global position -> hit local position xlc, zlc, /// x and z of the hit in the local frame: hit global position -> hit local position -> detector position (row, col) -> local position locHS.X() - locD.X(), locHS.Z() - locD.Z()); /// difference in x and z between the hit and the digit in the local frame - histXCoord->Fill(gloD.X()); - histYCoord->Fill(gloD.Y()); - histZCoord->Fill(gloD.Z()); nt2->Fill(chipID, gloD.Z(), locHS.X() - locHE.X(), locHS.Z() - locHE.Z()); /// differences between local hit start and hit end positions - std::cout << "Digit " << iDigit << ": chipID = " << chipID << ", X=" << gloD.X() << ", Y=" << gloD.Y() << ", Z=" << gloD.Z() << std::endl; + } // end loop on digits array } // end loop on ROFRecords @@ -299,11 +293,4 @@ void CheckDigitsIOTOF(std::string digifile = "tf3digits.root", std::string hitfi f->Write(); f->Close(); - - TFile* outFile = new TFile("CheckDigitsHists.root", "RECREATE"); - outFile->cd(); - histXCoord->Write(); - histYCoord->Write(); - histZCoord->Write(); - outFile->Close(); } diff --git a/Detectors/Upgrades/ALICE3/IOTOF/reconstruction/CMakeLists.txt b/Detectors/Upgrades/ALICE3/IOTOF/reconstruction/CMakeLists.txt index 110cc436a8e25..9a887bff8127c 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/reconstruction/CMakeLists.txt +++ b/Detectors/Upgrades/ALICE3/IOTOF/reconstruction/CMakeLists.txt @@ -14,10 +14,7 @@ o2_add_library(IOTOFReconstruction SOURCES src/Clusterer.cxx PUBLIC_LINK_LIBRARIES Microsoft.GSL::GSL - # O2::DataFormatsITSMFT O2::DataFormatsIOTOF O2::IOTOFBase O2::IOTOFSimulation - # O2::SimulationDataFormat - # nlohmann_json::nlohmann_json ) diff --git a/Detectors/Upgrades/ALICE3/IOTOF/reconstruction/include/IOTOFReconstruction/Clusterer.h b/Detectors/Upgrades/ALICE3/IOTOF/reconstruction/include/IOTOFReconstruction/Clusterer.h index 8caba7089464e..6633f9be8c3fc 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/reconstruction/include/IOTOFReconstruction/Clusterer.h +++ b/Detectors/Upgrades/ALICE3/IOTOF/reconstruction/include/IOTOFReconstruction/Clusterer.h @@ -17,13 +17,10 @@ #include "DataFormatsIOTOF/Digit.h" #include "DataFormatsITSMFT/ROFRecord.h" -#include "DataFormatsITSMFT/ClusterPattern.h" #include "DataFormatsIOTOF/Cluster.h" #include "SimulationDataFormat/ConstMCTruthContainer.h" #include "SimulationDataFormat/MCCompLabel.h" #include "SimulationDataFormat/MCTruthContainer.h" -#include "IOTOFBase/IOTOFBaseParam.h" -#include "MathUtils/Cartesian.h" #include #include #include diff --git a/Detectors/Upgrades/ALICE3/IOTOF/reconstruction/src/Clusterer.cxx b/Detectors/Upgrades/ALICE3/IOTOF/reconstruction/src/Clusterer.cxx index 28cc0b1100ab2..640101406b748 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/reconstruction/src/Clusterer.cxx +++ b/Detectors/Upgrades/ALICE3/IOTOF/reconstruction/src/Clusterer.cxx @@ -33,23 +33,23 @@ void Clusterer::process(gsl::span digits, gsl::span digMC2ROFs, std::vector* clusterMC2ROFs) { - LOG(info) << "[Clusterer] Entered process()"; + LOG(info) << "Clusterizing " << digitROFs.size() << " ROFs, total digits: " << digits.size(); + if (!mThread) { mThread = std::make_unique(this); } auto* geom = o2::iotof::GeometryTGeo::Instance(); - LOG(info) << "[Clusterer] Processing " << digitROFs.size() << " digit ROFs, total digits: " << digits.size(); for (size_t iROF = 0; iROF < digitROFs.size(); ++iROF) { - LOG(info) << "[Clusterer] Processing digit ROF " << iROF << "/" << digitROFs.size(); + LOG(debug) << "Processing digit ROF " << iROF << "/" << digitROFs.size(); 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) { - LOG(info) << "[Clusterer] Digit ROF " << iROF << " has no entries, skipping"; + LOG(debug) << "Digit ROF " << iROF << " has no entries, skipping"; clusterROFs.emplace_back(inROF.getBCData(), inROF.getROFrame(), outFirst, 0); continue; } @@ -69,7 +69,7 @@ void Clusterer::process(gsl::span digits, } return da.getRow() < db.getRow(); }); - LOG(info) << "[Clusterer] Sorted " << nEntries << " digit indices for ROF " << iROF; + LOG(debug) << "Found " << nEntries << " digits for ROF " << iROF; // Process blocks of chips with the same chipID int sliceStart = 0; @@ -81,17 +81,16 @@ void Clusterer::process(gsl::span digits, } const int chipN = sliceStart - chipFirst; - LOG(info) << ""; - LOG(info) << "[Clusterer] Processing chip " << chipID << " with " << chipN << " digits, next chip start from index " << sliceStart; + LOG(debug) << "Processing chip " << chipID << " with " << chipN << " digits, next chip start from index " << sliceStart; mThread->processChip(digits, chipFirst, chipN, &clusters, &patterns, digitLabels, clusterLabels, geom); } - LOG(info) << "[Clusterer] Finished processing digit ROF " << iROF << ", produced " << (clusters.size() - outFirst) << " clusters"; + LOG(debug) << "Finished processing digit ROF " << iROF << ", produced " << (clusters.size() - outFirst) << " clusters"; clusterROFs.emplace_back(inROF.getBCData(), inROF.getROFrame(), outFirst, static_cast(clusters.size()) - outFirst); } - LOG(info) << "[Clusterer] Finished processing all digit ROFs, total clusters produced: " << clusters.size(); + LOG(info) << "Finished processing all digit ROFs, total clusters produced: " << clusters.size(); if (clusterMC2ROFs && !digMC2ROFs.empty()) { clusterMC2ROFs->reserve(clusterMC2ROFs->size() + digMC2ROFs.size()); for (const auto& in : digMC2ROFs) { @@ -109,8 +108,6 @@ void Clusterer::ClustererThread::processChip(gsl::span digits, ClusterTruth* labelsClusPtr, GeometryTGeo* geom) { - LOG(info) << ""; - LOG(info) << "[Clusterer] Entered processChip() for chip, will process " << chipN << " digits"; // chipFirst and chipN are relative to mSortIdx (i.e. mSortIdx[chipFirst..chipFirst+chipN-1] // are the global digit indices for this chip, already sorted by col then row). // We use parent->mSortIdx to resolve the global index of each pixel. @@ -120,16 +117,15 @@ void Clusterer::ClustererThread::processChip(gsl::span digits, // are handled with a preclusterer. TF3 still does not have per-ROF readout, so we // use finishChipSingleHitFast on all hits for now. for (auto i = 0; i < chipN; ++i) { - LOG(info) << "[Clusterer] Processing digit " << sortIdx[chipFirst + i] << " ... "; finishChipSingleHitFast(digits, sortIdx[chipFirst + i], labelsDigPtr, labelsClusPtr, geom); } // // TRK logic for per-ROF readout, not used for TF3 yet. // if (chipN == 1) { - // LOG(info) << "[Clusterer] Processing single hit chip"; + // LOG(debug) << "Processing single hit chip"; // finishChipSingleHitFast(digits, sortIdx[chipFirst], labelsDigPtr, labelsClusPtr, geom); // } else { - // LOG(info) << "[Clusterer] Processing multi-hit chip with " << chipN << " hits"; + // LOG(debug) << "Processing multi-hit chip with " << chipN << " hits"; // // Call to initChip() // // Call to updateChip() // // Call to finishChip() diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Segmentation.h b/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Segmentation.h index 0a7d78e6e75cb..cd0ab55bd03d7 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Segmentation.h +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Segmentation.h @@ -45,15 +45,6 @@ class Segmentation const float passiveEdgeSide, const float sensorLayerThicknessEff, const float sensorLayerThickness, const int subDetectorID); void configChip(const ChipSpecifics& specsConfig, const int subDetectorID); - float getPitchRow(const int subDetectorID) - { - return (subDetectorID == 0) ? mITofSpecsConfig.PitchRow : mOTofSpecsConfig.PitchRow; - } - float getPitchCol(const int subDetectorID) - { - return (subDetectorID == 0) ? mITofSpecsConfig.PitchCol : mOTofSpecsConfig.PitchCol; - } - /// Transformation from Geant detector centered local coordinates (cm) to /// Pixel cell numbers iRow and iCol. /// Returns kTRUE if point x,z is inside sensitive volume, kFALSE otherwise. @@ -131,6 +122,7 @@ class Segmentation detectorToLocalUnchecked(row, col, xRow, zCol, subDetectorID); return true; } + template bool detectorToLocal(L row, L col, math_utils::Point3D& loc, const int subDetectorID) { diff --git a/Detectors/Upgrades/ALICE3/IOTOF/workflow/src/ClusterWriterSpec.cxx b/Detectors/Upgrades/ALICE3/IOTOF/workflow/src/ClusterWriterSpec.cxx index e9efbda5b8700..5bccbaf8ff487 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/workflow/src/ClusterWriterSpec.cxx +++ b/Detectors/Upgrades/ALICE3/IOTOF/workflow/src/ClusterWriterSpec.cxx @@ -51,7 +51,6 @@ DataProcessorSpec getClusterWriterSpec(bool mctruth, bool dec, o2::header::DataO LOG(info) << "RECEIVED CLUSTERS SIZE " << inClusters.size(); }; - return MakeRootTreeWriterSpec((detStr + "ClusterWriter" + (dec ? "_dec" : "")).c_str(), (detStrL + "clusters.root").c_str(), MakeRootTreeWriterSpec::TreeAttributes{.name = "o2sim", .title = "Tree with TF3 clusters"}, diff --git a/Detectors/Upgrades/ALICE3/IOTOF/workflow/src/ClustererSpec.cxx b/Detectors/Upgrades/ALICE3/IOTOF/workflow/src/ClustererSpec.cxx index 2f7d018380504..76415c4973510 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/workflow/src/ClustererSpec.cxx +++ b/Detectors/Upgrades/ALICE3/IOTOF/workflow/src/ClustererSpec.cxx @@ -32,27 +32,23 @@ void ClustererDPL::init(o2::framework::InitContext& ic) void ClustererDPL::run(o2::framework::ProcessingContext& pc) { - LOG(info) << "[ClustererDPL] Entered run() with " << mNThreads << " threads"; + LOG(info) << "Start running with " << mNThreads << " threads"; o2::base::GeometryManager::loadGeometry("o2sim_geometry.root", false, true); - LOG(info) << "[ClustererDPL] Geometry loaded"; uint64_t totalClusters = 0; // Loop on layers to be added here, for now only one layer is processed int iLayer = 0; - LOG(info) << "[ClustererDPL] Getting digits for layer " << iLayer; auto digits = pc.inputs().get>(std::format("digits_{}", iLayer)); auto rofs = pc.inputs().get>(std::format("ROframes_{}", iLayer)); - LOG(info) << "[ClustererDPL] Got " << digits.size() << " digits and " << rofs.size() << " ROFs for layer " << iLayer; + LOG(debug) << "Got " << digits.size() << " digits and " << rofs.size() << " ROFs for layer " << iLayer; gsl::span labelbuffer; if (mUseMC) { - LOG(info) << "[ClustererDPL] Getting MC labels for layer " << iLayer; labelbuffer = pc.inputs().get>(std::format("labels_{}", iLayer)); - LOG(info) << "[ClustererDPL] Got " << labelbuffer.size() << " bytes of MC labels for layer " << iLayer; + LOG(debug) << "Got " << labelbuffer.size() << " bytes of MC labels for layer " << iLayer; } o2::dataformats::ConstMCTruthContainerView labels(labelbuffer); - LOG(info) << "[ClustererDPL] Got MC labels for layer " << iLayer; std::vector clusters; std::vector patterns; @@ -62,7 +58,7 @@ void ClustererDPL::run(o2::framework::ProcessingContext& pc) clusterLabels = std::make_unique>(); } - LOG(info) << "[ClustererDPL] Running IOTOFClusterer on layer " << iLayer; + LOG(info) << "Running IOTOF Clusterer for layer " << iLayer; mClusterer.process(digits, rofs, clusters, @@ -70,7 +66,7 @@ void ClustererDPL::run(o2::framework::ProcessingContext& pc) clusterROFs, mUseMC ? &labels : nullptr, clusterLabels.get()); - LOG(info) << "[ClustererDPL] IOTOFClusterer produced " << clusters.size() << " clusters for layer " << iLayer; + LOG(info) << "Clusterization produced " << clusters.size() << " clusters for layer " << iLayer; const auto subspec = static_cast(iLayer); pc.outputs().snapshot(o2::framework::Output{"TF3", "COMPCLUSTERS", subspec}, clusters); pc.outputs().snapshot(o2::framework::Output{"TF3", "PATTERNS", subspec}, patterns); @@ -79,15 +75,12 @@ void ClustererDPL::run(o2::framework::ProcessingContext& pc) pc.outputs().snapshot(o2::framework::Output{"TF3", "CLUSTERSMCTR", subspec}, *clusterLabels); } totalClusters += clusters.size(); - LOGP(info, "[ClustererDPL] IOTOFClusterer layer {} pushed {} clusters in {} ROFs", iLayer, clusters.size(), clusterROFs.size()); - LOGP(info, "[ClustererDPL] IOTOFClusterer layer {} pushed {} MC labels", iLayer, mUseMC ? clusterLabels->getNElements() : 0); - LOGP(info, "[ClustererDPL] IOTOFClusterer produced {} clusters", totalClusters); + LOGP(info, "Pushed {} clusters in {} ROFs for layer {}", clusters.size(), clusterROFs.size(), iLayer); + LOGP(info, "Pushed {} MC labels for layer {}", mUseMC ? clusterLabels->getNElements() : 0, iLayer); } o2::framework::DataProcessorSpec getClustererSpec(bool useMC) { - - LOG(info) << "[ClustererSpec] Creating DataProcessorSpec for IOTOFClusterer with useMC=" << useMC; static constexpr int nLayers = 2; std::vector inputs; // Currently TF3 digits (unlike TRK) are not separated by layer, eventually per-layer reading here @@ -97,7 +90,7 @@ o2::framework::DataProcessorSpec getClustererSpec(bool useMC) if (useMC) { inputs.emplace_back(std::format("labels_{}", iLayer), "TF3", "DIGITSMCTR", iLayer, o2::framework::Lifetime::Timeframe); } - LOG(info) << "[ClustererSpec] Created " << inputs.size() << " input specifications for IOTOFClusterer"; + LOG(debug) << "Created " << inputs.size() << " input specifications for IOTOFClusterer"; std::vector outputs; outputs.emplace_back("TF3", "COMPCLUSTERS", iLayer, o2::framework::Lifetime::Timeframe); @@ -106,9 +99,8 @@ o2::framework::DataProcessorSpec getClustererSpec(bool useMC) if (useMC) { outputs.emplace_back("TF3", "CLUSTERSMCTR", iLayer, o2::framework::Lifetime::Timeframe); } - LOG(info) << "[ClustererSpec] Created " << outputs.size() << " output specifications for IOTOFClusterer"; + LOG(debug) << "Created " << outputs.size() << " output specifications for IOTOFClusterer"; - LOG(info) << "[ClustererSpec] Returning ... "; return o2::framework::DataProcessorSpec{ "iotof-clusterer", inputs, diff --git a/Detectors/Upgrades/ALICE3/IOTOF/workflow/src/RecoWorkflow.cxx b/Detectors/Upgrades/ALICE3/IOTOF/workflow/src/RecoWorkflow.cxx index ef9977812fc27..908884a981157 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/workflow/src/RecoWorkflow.cxx +++ b/Detectors/Upgrades/ALICE3/IOTOF/workflow/src/RecoWorkflow.cxx @@ -26,28 +26,23 @@ framework::WorkflowSpec getWorkflow(bool useMC, bool upstreamClusters, bool disableRootOutput) { - LOG(info) << "[RecoWorkflow] ENTERING IOTOF RecoWorkflow.cxx"; framework::WorkflowSpec specs; - LOG(info) << "[RecoWorkflow] useMC: " << useMC; - LOG(info) << "[RecoWorkflow] upstreamDigits: " << upstreamDigits; - LOG(info) << "[RecoWorkflow] upstreamClusters: " << upstreamClusters; - LOG(info) << "[RecoWorkflow] disableRootOutput: " << disableRootOutput; if (!(upstreamDigits || upstreamClusters)) { - LOG(info) << "[RecoWorkflow] Adding DigitReaderSpec to workflow"; + LOG(debug) << "Adding DigitReaderSpec to workflow"; specs.emplace_back(o2::iotof::getIOTOFDigitReaderSpec(useMC, false, "tf3digits.root")); } if (!upstreamClusters) { - LOG(info) << "[RecoWorkflow] Adding ClustererSpec to workflow"; + LOG(debug) << "Adding ClustererSpec to workflow"; specs.emplace_back(o2::iotof::getIOTOFClustererSpec(useMC)); } if (!disableRootOutput) { - LOG(info) << "[RecoWorkflow] Adding ClusterWriterSpec to workflow"; + LOG(debug) << "Adding ClusterWriterSpec to workflow"; specs.emplace_back(o2::iotof::getIOTOFClusterWriterSpec(useMC, false)); } - LOG(info) << "[RecoWorkflow] IOTOF RecoWorkflow.cxx completed, starting execution of workflow with " << specs.size() << " specifications"; + LOG(debug) << "Starting execution with " << specs.size() << " workflow specifications"; return specs; } diff --git a/Detectors/Upgrades/ALICE3/IOTOF/workflow/src/iotof-reco-workflow.cxx b/Detectors/Upgrades/ALICE3/IOTOF/workflow/src/iotof-reco-workflow.cxx index 7b3c4722006ff..4e2ab45c6f21f 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/workflow/src/iotof-reco-workflow.cxx +++ b/Detectors/Upgrades/ALICE3/IOTOF/workflow/src/iotof-reco-workflow.cxx @@ -64,7 +64,7 @@ void customize(std::vector& workflowOptions) o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& configcontext) { - LOG(info) << "[iotof-reco-workflow] Entering iotof-reco-workflow.cxx defineDataProcessing function"; + LOG(debug) << "Entering iotof-reco-workflow"; // Update the (declared) parameters if changed from the command line auto useMC = !configcontext.options().get("disable-mc"); // auto hitRecoConfig = configcontext.options().get("tracking-from-hits-config"); @@ -78,6 +78,6 @@ o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext co // write the configuration used for the reco workflow o2::conf::ConfigurableParam::writeINI("o2tf3recoflow_configuration.ini"); - LOG(info) << "[iotof-reco-workflow] About to call o2::iotof::reco_workflow::getWorkflow"; + LOG(debug) << "Calling o2::iotof::reco_workflow::getWorkflow"; return o2::iotof::reco_workflow::getWorkflow(useMC, /*hitRecoConfig,*/ extDigits, extClusters, disableRootOutput /*, useGpuWF, gpuDevice*/); } From 32156f6c99c8388717b801dcb3d04ddaf8d64913 Mon Sep 17 00:00:00 2001 From: Marcello Di Costanzo Date: Tue, 30 Jun 2026 14:44:33 +0200 Subject: [PATCH 7/8] Remove subdetID and geometry instance from Clusterer class --- .../include/DataFormatsIOTOF/Cluster.h | 1 - .../IOTOF/DataFormatsIOTOF/src/Cluster.cxx | 3 +-- .../ALICE3/IOTOF/macros/CheckClustersIOTOF.C | 3 +-- .../include/IOTOFReconstruction/Clusterer.h | 6 ++--- .../IOTOF/reconstruction/src/Clusterer.cxx | 23 +++++++------------ .../IOTOF/workflow/src/ClustererSpec.cxx | 2 -- .../IOTOF/workflow/src/RecoWorkflow.cxx | 4 ---- .../workflow/src/iotof-reco-workflow.cxx | 2 -- 8 files changed, 12 insertions(+), 32 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/IOTOF/DataFormatsIOTOF/include/DataFormatsIOTOF/Cluster.h b/Detectors/Upgrades/ALICE3/IOTOF/DataFormatsIOTOF/include/DataFormatsIOTOF/Cluster.h index e00203c693632..ad789c649c785 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/DataFormatsIOTOF/include/DataFormatsIOTOF/Cluster.h +++ b/Detectors/Upgrades/ALICE3/IOTOF/DataFormatsIOTOF/include/DataFormatsIOTOF/Cluster.h @@ -24,7 +24,6 @@ struct Cluster { uint16_t row = 0; uint16_t col = 0; uint16_t size = 1; - int16_t subDetID = -1; double time = 0.0; std::string asString() const; diff --git a/Detectors/Upgrades/ALICE3/IOTOF/DataFormatsIOTOF/src/Cluster.cxx b/Detectors/Upgrades/ALICE3/IOTOF/DataFormatsIOTOF/src/Cluster.cxx index 2a3fbf5f40025..6b5a4948900e7 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/DataFormatsIOTOF/src/Cluster.cxx +++ b/Detectors/Upgrades/ALICE3/IOTOF/DataFormatsIOTOF/src/Cluster.cxx @@ -20,8 +20,7 @@ namespace o2::iotof std::string Cluster::asString() const { std::ostringstream stream; - stream << "chip=" << chipID << " row=" << row << " col=" << col - << " size=" << size << " subDet=" << subDetID; + stream << "chip=" << chipID << " row=" << row << " col=" << col << " size=" << size; return stream.str(); } diff --git a/Detectors/Upgrades/ALICE3/IOTOF/macros/CheckClustersIOTOF.C b/Detectors/Upgrades/ALICE3/IOTOF/macros/CheckClustersIOTOF.C index 78686ebff127e..588a6be195c7e 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/macros/CheckClustersIOTOF.C +++ b/Detectors/Upgrades/ALICE3/IOTOF/macros/CheckClustersIOTOF.C @@ -94,7 +94,7 @@ void CheckClustersIOTOF(std::string digiFilePath = "tf3digits.root", std::string std::cout << "---> Number of clusters with MC label: " << clsLabels->getIndexedSize() << std::endl; std::cout << std::endl; - auto clsTuple = new TNtuple("clsTuple", "clsTuple", "chip_id:x:y:z:subdet_id:row:col:time"); + auto clsTuple = new TNtuple("clsTuple", "clsTuple", "chip_id:x:y:z:row:col:time"); clsTuple->SetDirectory(nullptr); TH1F* histXCoordCls = new TH1F("histXCoordCls", "histXCoordCls", 8000, -100, 100); @@ -176,7 +176,6 @@ void CheckClustersIOTOF(std::string digiFilePath = "tf3digits.root", std::string globalClsCoords.x(), globalClsCoords.y(), globalClsCoords.z(), - (*clsArray)[iCls].subDetID, (*clsArray)[iCls].row, (*clsArray)[iCls].col, (*clsArray)[iCls].time); diff --git a/Detectors/Upgrades/ALICE3/IOTOF/reconstruction/include/IOTOFReconstruction/Clusterer.h b/Detectors/Upgrades/ALICE3/IOTOF/reconstruction/include/IOTOFReconstruction/Clusterer.h index 6633f9be8c3fc..ef8e690278c0b 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/reconstruction/include/IOTOFReconstruction/Clusterer.h +++ b/Detectors/Upgrades/ALICE3/IOTOF/reconstruction/include/IOTOFReconstruction/Clusterer.h @@ -63,12 +63,10 @@ class Clusterer void fetchMCLabels(uint32_t digID, const ConstDigitTruth* labelsDig, int& nfilled); void finishChipSingleHitFast(gsl::span digits, uint32_t digitIdx, - const ConstDigitTruth* labelsDigPtr, ClusterTruth* labelsClusPtr, - GeometryTGeo* geom); + const ConstDigitTruth* labelsDigPtr, ClusterTruth* labelsClusPtr); void processChip(gsl::span digits, int chipFirst, int chipN, std::vector* clustersOut, std::vector* patternsOut, - const ConstDigitTruth* labelsDigPtr, ClusterTruth* labelsClusPtr, - GeometryTGeo* geom); + const ConstDigitTruth* labelsDigPtr, ClusterTruth* labelsClusPtr); explicit ClustererThread(Clusterer* par = nullptr) : parent(par) {} ClustererThread(const ClustererThread&) = delete; diff --git a/Detectors/Upgrades/ALICE3/IOTOF/reconstruction/src/Clusterer.cxx b/Detectors/Upgrades/ALICE3/IOTOF/reconstruction/src/Clusterer.cxx index 640101406b748..dcd7a5a695646 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/reconstruction/src/Clusterer.cxx +++ b/Detectors/Upgrades/ALICE3/IOTOF/reconstruction/src/Clusterer.cxx @@ -12,9 +12,9 @@ /// \file Clusterer.cxx /// \brief Implementation of the IOTOF cluster finder +#include "Framework/Logger.h" + #include "IOTOFReconstruction/Clusterer.h" -#include "IOTOFBase/GeometryTGeo.h" -#include "IOTOFSimulation/Segmentation.h" #include #include @@ -33,14 +33,12 @@ void Clusterer::process(gsl::span digits, gsl::span digMC2ROFs, std::vector* clusterMC2ROFs) { - LOG(info) << "Clusterizing " << digitROFs.size() << " ROFs, total digits: " << digits.size(); + LOG(info) << "Running clusterizer on " << digitROFs.size() << " ROFs, total digits: " << digits.size(); if (!mThread) { mThread = std::make_unique(this); } - auto* geom = o2::iotof::GeometryTGeo::Instance(); - for (size_t iROF = 0; iROF < digitROFs.size(); ++iROF) { LOG(debug) << "Processing digit ROF " << iROF << "/" << digitROFs.size(); const auto& inROF = digitROFs[iROF]; @@ -82,7 +80,7 @@ void Clusterer::process(gsl::span digits, const int chipN = sliceStart - chipFirst; LOG(debug) << "Processing chip " << chipID << " with " << chipN << " digits, next chip start from index " << sliceStart; - mThread->processChip(digits, chipFirst, chipN, &clusters, &patterns, digitLabels, clusterLabels, geom); + mThread->processChip(digits, chipFirst, chipN, &clusters, &patterns, digitLabels, clusterLabels); } LOG(debug) << "Finished processing digit ROF " << iROF << ", produced " << (clusters.size() - outFirst) << " clusters"; @@ -105,8 +103,7 @@ void Clusterer::ClustererThread::processChip(gsl::span digits, std::vector* clustersOut, std::vector* patternsOut, const ConstDigitTruth* labelsDigPtr, - ClusterTruth* labelsClusPtr, - GeometryTGeo* geom) + ClusterTruth* labelsClusPtr) { // chipFirst and chipN are relative to mSortIdx (i.e. mSortIdx[chipFirst..chipFirst+chipN-1] // are the global digit indices for this chip, already sorted by col then row). @@ -117,13 +114,13 @@ void Clusterer::ClustererThread::processChip(gsl::span digits, // are handled with a preclusterer. TF3 still does not have per-ROF readout, so we // use finishChipSingleHitFast on all hits for now. for (auto i = 0; i < chipN; ++i) { - finishChipSingleHitFast(digits, sortIdx[chipFirst + i], labelsDigPtr, labelsClusPtr, geom); + finishChipSingleHitFast(digits, sortIdx[chipFirst + i], labelsDigPtr, labelsClusPtr); } // // TRK logic for per-ROF readout, not used for TF3 yet. // if (chipN == 1) { // LOG(debug) << "Processing single hit chip"; - // finishChipSingleHitFast(digits, sortIdx[chipFirst], labelsDigPtr, labelsClusPtr, geom); + // finishChipSingleHitFast(digits, sortIdx[chipFirst], labelsDigPtr, labelsClusPtr); // } else { // LOG(debug) << "Processing multi-hit chip with " << chipN << " hits"; // // Call to initChip() @@ -151,8 +148,7 @@ void Clusterer::ClustererThread::processChip(gsl::span digits, void Clusterer::ClustererThread::finishChipSingleHitFast(gsl::span digits, uint32_t digitIdx, const ConstDigitTruth* labelsDigPtr, - ClusterTruth* labelsClusPtr, - GeometryTGeo* geom) + ClusterTruth* labelsClusPtr) { const auto& digit = digits[digitIdx]; const uint16_t chipID = digit.getChipIndex(); @@ -179,9 +175,6 @@ void Clusterer::ClustererThread::finishChipSingleHitFast(gsl::span cluster.row = row; cluster.col = col; cluster.size = 1; - if (geom) { - cluster.subDetID = geom->getIOTOFLayer(chipID); - } cluster.time = time; clusters.emplace_back(cluster); } diff --git a/Detectors/Upgrades/ALICE3/IOTOF/workflow/src/ClustererSpec.cxx b/Detectors/Upgrades/ALICE3/IOTOF/workflow/src/ClustererSpec.cxx index 76415c4973510..c1f588c44fb22 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/workflow/src/ClustererSpec.cxx +++ b/Detectors/Upgrades/ALICE3/IOTOF/workflow/src/ClustererSpec.cxx @@ -90,7 +90,6 @@ o2::framework::DataProcessorSpec getClustererSpec(bool useMC) if (useMC) { inputs.emplace_back(std::format("labels_{}", iLayer), "TF3", "DIGITSMCTR", iLayer, o2::framework::Lifetime::Timeframe); } - LOG(debug) << "Created " << inputs.size() << " input specifications for IOTOFClusterer"; std::vector outputs; outputs.emplace_back("TF3", "COMPCLUSTERS", iLayer, o2::framework::Lifetime::Timeframe); @@ -99,7 +98,6 @@ o2::framework::DataProcessorSpec getClustererSpec(bool useMC) if (useMC) { outputs.emplace_back("TF3", "CLUSTERSMCTR", iLayer, o2::framework::Lifetime::Timeframe); } - LOG(debug) << "Created " << outputs.size() << " output specifications for IOTOFClusterer"; return o2::framework::DataProcessorSpec{ "iotof-clusterer", diff --git a/Detectors/Upgrades/ALICE3/IOTOF/workflow/src/RecoWorkflow.cxx b/Detectors/Upgrades/ALICE3/IOTOF/workflow/src/RecoWorkflow.cxx index 908884a981157..70710ee5519f1 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/workflow/src/RecoWorkflow.cxx +++ b/Detectors/Upgrades/ALICE3/IOTOF/workflow/src/RecoWorkflow.cxx @@ -29,20 +29,16 @@ framework::WorkflowSpec getWorkflow(bool useMC, framework::WorkflowSpec specs; if (!(upstreamDigits || upstreamClusters)) { - LOG(debug) << "Adding DigitReaderSpec to workflow"; specs.emplace_back(o2::iotof::getIOTOFDigitReaderSpec(useMC, false, "tf3digits.root")); } if (!upstreamClusters) { - LOG(debug) << "Adding ClustererSpec to workflow"; specs.emplace_back(o2::iotof::getIOTOFClustererSpec(useMC)); } if (!disableRootOutput) { - LOG(debug) << "Adding ClusterWriterSpec to workflow"; specs.emplace_back(o2::iotof::getIOTOFClusterWriterSpec(useMC, false)); } - LOG(debug) << "Starting execution with " << specs.size() << " workflow specifications"; return specs; } diff --git a/Detectors/Upgrades/ALICE3/IOTOF/workflow/src/iotof-reco-workflow.cxx b/Detectors/Upgrades/ALICE3/IOTOF/workflow/src/iotof-reco-workflow.cxx index 4e2ab45c6f21f..dad3f1ce07874 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/workflow/src/iotof-reco-workflow.cxx +++ b/Detectors/Upgrades/ALICE3/IOTOF/workflow/src/iotof-reco-workflow.cxx @@ -64,7 +64,6 @@ void customize(std::vector& workflowOptions) o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& configcontext) { - LOG(debug) << "Entering iotof-reco-workflow"; // Update the (declared) parameters if changed from the command line auto useMC = !configcontext.options().get("disable-mc"); // auto hitRecoConfig = configcontext.options().get("tracking-from-hits-config"); @@ -78,6 +77,5 @@ o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext co // write the configuration used for the reco workflow o2::conf::ConfigurableParam::writeINI("o2tf3recoflow_configuration.ini"); - LOG(debug) << "Calling o2::iotof::reco_workflow::getWorkflow"; return o2::iotof::reco_workflow::getWorkflow(useMC, /*hitRecoConfig,*/ extDigits, extClusters, disableRootOutput /*, useGpuWF, gpuDevice*/); } From 9a97e8498242b5457bea9619a04b7805ddb3016a Mon Sep 17 00:00:00 2001 From: ALICE Action Bot Date: Tue, 30 Jun 2026 12:56:35 +0000 Subject: [PATCH 8/8] Please consider the following formatting changes --- .../Upgrades/ALICE3/IOTOF/base/src/GeometryTGeo.cxx | 4 ++-- .../Upgrades/ALICE3/IOTOF/macros/CheckClustersIOTOF.C | 10 ++++------ .../include/IOTOFReconstruction/Clusterer.h | 2 +- .../ALICE3/IOTOF/reconstruction/src/Clusterer.cxx | 10 +++++----- .../ALICE3/IOTOF/workflow/src/ClusterWriterSpec.cxx | 9 ++++----- .../ALICE3/IOTOF/workflow/src/ClustererSpec.cxx | 5 ++--- 6 files changed, 18 insertions(+), 22 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/IOTOF/base/src/GeometryTGeo.cxx b/Detectors/Upgrades/ALICE3/IOTOF/base/src/GeometryTGeo.cxx index c4846e305c230..905da9ddce754 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/base/src/GeometryTGeo.cxx +++ b/Detectors/Upgrades/ALICE3/IOTOF/base/src/GeometryTGeo.cxx @@ -284,8 +284,8 @@ void GeometryTGeo::Build(int loadTrans) mLastChipIndex[j] = numberOfChips - 1; } - LOG(info) << "TF3 geometry: numberOfChipsITOF = " << mNumberOfChipsIOTOF[0] << ", numberOfChipsOTOF = " - << mNumberOfChipsIOTOF[1] << ", numberOfChips = " << numberOfChips << ", mNumberOfChipesPerStaveITOF" + LOG(info) << "TF3 geometry: numberOfChipsITOF = " << mNumberOfChipsIOTOF[0] << ", numberOfChipsOTOF = " + << mNumberOfChipsIOTOF[1] << ", numberOfChips = " << numberOfChips << ", mNumberOfChipesPerStaveITOF" << mNumberOfChipsPerStaveIOTOF[0]; setSize(numberOfChips); diff --git a/Detectors/Upgrades/ALICE3/IOTOF/macros/CheckClustersIOTOF.C b/Detectors/Upgrades/ALICE3/IOTOF/macros/CheckClustersIOTOF.C index 588a6be195c7e..107e5a4d02bf8 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/macros/CheckClustersIOTOF.C +++ b/Detectors/Upgrades/ALICE3/IOTOF/macros/CheckClustersIOTOF.C @@ -44,8 +44,8 @@ void CheckClustersIOTOF(std::string digiFilePath = "tf3digits.root", std::string using namespace o2::base; using namespace o2::iotof; - using o2::iotof::Digit; using o2::iotof::Cluster; + using o2::iotof::Digit; o2::conf::ConfigurableParam::updateFromString("IOTOFBase.segmentedInnerTOF=true;IOTOFBase.segmentedOuterTOF=true;IOTOFBase.enableForwardTOF=false;IOTOFBase.enableBackwardTOF=false"); @@ -81,7 +81,7 @@ void CheckClustersIOTOF(std::string digiFilePath = "tf3digits.root", std::string o2::dataformats::MCTruthContainer* clsLabels{nullptr}; clsTree->SetBranchAddress("TF3ClusterMCTruth", &clsLabels); clsTree->GetEntry(0); - + // Summary of entries in all branches std::cout << std::endl; std::cout << "---> Number of digits: " << digitsArray->size() << std::endl; @@ -151,7 +151,6 @@ void CheckClustersIOTOF(std::string digiFilePath = "tf3digits.root", std::string histZCoordDigit->Fill(globalDigitCoord.Z()); } // end loop on digits array - // LOOP on : clusters array std::cout << "\n\n ----> Starting loop on clusters for ROF " << iROF << " with index " << rofIndex << " and nEntries " << rofNEntries << std::endl; for (unsigned int iCls = rofIndex; iCls < rofIndex + rofNEntries; iCls++) { @@ -170,7 +169,7 @@ void CheckClustersIOTOF(std::string digiFilePath = "tf3digits.root", std::string segGeom->detectorToLocal(iRow, iCol, x, z, subDetID); } - o2::math_utils::Point3D localClsCoords(x, y, z); // local Digit + o2::math_utils::Point3D localClsCoords(x, y, z); // local Digit const auto globalClsCoords = tofGeo->getMatrixL2G(chipID)(localClsCoords); // convert to global clsTuple->Fill((*clsArray)[iCls].chipID, globalClsCoords.x(), @@ -204,7 +203,7 @@ void CheckClustersIOTOF(std::string digiFilePath = "tf3digits.root", std::string segGeom->detectorToLocal(iRowFromDigit, iColFromDigit, xFromDigit, zFromDigit, iSubDetIDFromDigit); } - o2::math_utils::Point3D localDigitCoordFromDigit(xFromDigit, yFromDigit, zFromDigit); // local Digit + o2::math_utils::Point3D localDigitCoordFromDigit(xFromDigit, yFromDigit, zFromDigit); // local Digit const auto globalDigitCoordFromDigit = tofGeo->getMatrixL2G(iChipIDFromDigit)(localDigitCoordFromDigit); // convert to global histXCoordRes->Fill(globalClsCoords.x() - globalDigitCoordFromDigit.X()); histYCoordRes->Fill(globalClsCoords.y() - globalDigitCoordFromDigit.Y()); @@ -245,5 +244,4 @@ void CheckClustersIOTOF(std::string digiFilePath = "tf3digits.root", std::string histTimeRes->Write(); outFile->Write(); outFile->Close(); - } diff --git a/Detectors/Upgrades/ALICE3/IOTOF/reconstruction/include/IOTOFReconstruction/Clusterer.h b/Detectors/Upgrades/ALICE3/IOTOF/reconstruction/include/IOTOFReconstruction/Clusterer.h index ef8e690278c0b..252ecf8917377 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/reconstruction/include/IOTOFReconstruction/Clusterer.h +++ b/Detectors/Upgrades/ALICE3/IOTOF/reconstruction/include/IOTOFReconstruction/Clusterer.h @@ -52,7 +52,7 @@ class Clusterer // Further struct members in TRK, for now not needed in TF3 - std::array labelsBuff; ///< MC label buffer for one cluster + std::array labelsBuff; ///< MC label buffer for one cluster // per-thread output (accumulated, then merged back by caller) std::vector clusters; diff --git a/Detectors/Upgrades/ALICE3/IOTOF/reconstruction/src/Clusterer.cxx b/Detectors/Upgrades/ALICE3/IOTOF/reconstruction/src/Clusterer.cxx index dcd7a5a695646..edb9f71ac7f04 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/reconstruction/src/Clusterer.cxx +++ b/Detectors/Upgrades/ALICE3/IOTOF/reconstruction/src/Clusterer.cxx @@ -52,7 +52,7 @@ void Clusterer::process(gsl::span digits, continue; } - // Sort digit indices within this ROF by (chipID, col, row) + // Sort digit indices within this ROF by (chipID, col, row) // chip by chip, column by column (taken from TRK). mSortIdx.resize(nEntries); std::iota(mSortIdx.begin(), mSortIdx.end(), first); @@ -111,7 +111,7 @@ void Clusterer::ClustererThread::processChip(gsl::span digits, const auto& sortIdx = parent->mSortIdx; // TRK has per-ROF readout, so multiple hits belonging to the same chip, i.e. chipN > 1, - // are handled with a preclusterer. TF3 still does not have per-ROF readout, so we + // are handled with a preclusterer. TF3 still does not have per-ROF readout, so we // use finishChipSingleHitFast on all hits for now. for (auto i = 0; i < chipN; ++i) { finishChipSingleHitFast(digits, sortIdx[chipFirst + i], labelsDigPtr, labelsClusPtr); @@ -152,9 +152,9 @@ void Clusterer::ClustererThread::finishChipSingleHitFast(gsl::span { const auto& digit = digits[digitIdx]; const uint16_t chipID = digit.getChipIndex(); - const uint16_t row = digit.getRow(); - const uint16_t col = digit.getColumn(); - const double time = digit.getTime(); + const uint16_t row = digit.getRow(); + const uint16_t col = digit.getColumn(); + const double time = digit.getTime(); if (labelsClusPtr) { int nlab = 0; diff --git a/Detectors/Upgrades/ALICE3/IOTOF/workflow/src/ClusterWriterSpec.cxx b/Detectors/Upgrades/ALICE3/IOTOF/workflow/src/ClusterWriterSpec.cxx index 5bccbaf8ff487..4d63190be5d4c 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/workflow/src/ClusterWriterSpec.cxx +++ b/Detectors/Upgrades/ALICE3/IOTOF/workflow/src/ClusterWriterSpec.cxx @@ -36,10 +36,10 @@ namespace o2::iotof template using BranchDefinition = MakeRootTreeWriterSpec::BranchDefinition; -using ClustersType = std::vector; -using PatternsType = std::vector; -using ROFrameType = std::vector; -using LabelsType = o2::dataformats::MCTruthContainer; +using ClustersType = std::vector; +using PatternsType = std::vector; +using ROFrameType = std::vector; +using LabelsType = o2::dataformats::MCTruthContainer; DataProcessorSpec getClusterWriterSpec(bool mctruth, bool dec, o2::header::DataOrigin detOrig, o2::detectors::DetID detId) { @@ -63,7 +63,6 @@ DataProcessorSpec getClusterWriterSpec(bool mctruth, bool dec, o2::header::DataO (detStr + "ClusterROF").c_str(), "cluster-rof-branch"}, BranchDefinition{InputSpec{"tf3_labels", detOrig, "CLUSTERSMCTR", 0}, (detStr + "ClusterMCTruth").c_str()})(); - } DataProcessorSpec getIOTOFClusterWriterSpec(bool mctruth, bool dec) diff --git a/Detectors/Upgrades/ALICE3/IOTOF/workflow/src/ClustererSpec.cxx b/Detectors/Upgrades/ALICE3/IOTOF/workflow/src/ClustererSpec.cxx index c1f588c44fb22..79d823914727a 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/workflow/src/ClustererSpec.cxx +++ b/Detectors/Upgrades/ALICE3/IOTOF/workflow/src/ClustererSpec.cxx @@ -36,7 +36,7 @@ void ClustererDPL::run(o2::framework::ProcessingContext& pc) o2::base::GeometryManager::loadGeometry("o2sim_geometry.root", false, true); uint64_t totalClusters = 0; - + // Loop on layers to be added here, for now only one layer is processed int iLayer = 0; auto digits = pc.inputs().get>(std::format("digits_{}", iLayer)); @@ -104,8 +104,7 @@ 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"}}}}; } DataProcessorSpec getIOTOFClustererSpec(bool mctruth)