diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TimeFrameGPU.h b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TimeFrameGPU.h index c87b3d36b9a6a..4518396959f18 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TimeFrameGPU.h +++ b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TimeFrameGPU.h @@ -25,7 +25,7 @@ namespace o2::its::gpu { template -class TimeFrameGPU final : public TimeFrame +class TimeFrameGPU : public TimeFrame { using typename TimeFrame::IndexTableUtilsN; using typename TimeFrame::ROFOverlapTableN; @@ -35,7 +35,7 @@ class TimeFrameGPU final : public TimeFrame public: TimeFrameGPU() = default; - ~TimeFrameGPU() final = default; + ~TimeFrameGPU() override = default; /// Most relevant operations void pushMemoryStack(const int); diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameGPU.cu b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameGPU.cu index b9091eebde377..235c7c30dc719 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameGPU.cu +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameGPU.cu @@ -694,4 +694,5 @@ void TimeFrameGPU::wipe() } template class TimeFrameGPU<7>; +template class TimeFrameGPU<11>; } // namespace o2::its::gpu diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx index 0359f2cfb0d03..6c135896f0a82 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx @@ -380,4 +380,7 @@ void TrackerTraitsGPU::setBz(float bz) } template class TrackerTraitsGPU<7>; +#ifdef ENABLE_UPGRADES +template class TrackerTraitsGPU<11>; +#endif } // namespace o2::its diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu index 49b8f19d68ea6..6d778f17dc932 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu @@ -1258,4 +1258,185 @@ template void computeTrackSeedHandler(TrackSeed<7>* trackSeeds, const o2::base::PropagatorF::MatCorrType matCorrType, o2::its::ExternalAllocator* alloc); +/// Explicit instantiation of ALICE3 handlers +#ifdef ENABLE_UPGRADES +template void countTrackletsInROFsHandler<11>(const IndexTableUtils<11>* utils, + const ROFMaskTable<11>::View& rofMask, + const int layer, + const ROFOverlapTable<11>::View& rofOverlaps, + const ROFVertexLookupTable<11>::View& vertexLUT, + const int vertexId, + const Vertex* vertices, + const int* rofPV, + const Cluster** clusters, + std::vector nClusters, + const int** ROFClusters, + const unsigned char** usedClusters, + const int** clustersIndexTables, + int** trackletsLUTs, + gsl::span trackletsLUTsHost, + const int iteration, + const float NSigmaCut, + bounded_vector& phiCuts, + const float resolutionPV, + std::array& minRs, + std::array& maxRs, + bounded_vector& resolutions, + std::vector& radii, + bounded_vector& mulScatAng, + o2::its::ExternalAllocator* alloc, + gpu::Streams& streams); + +template void computeTrackletsInROFsHandler<11>(const IndexTableUtils<11>* utils, + const ROFMaskTable<11>::View& rofMask, + const int layer, + const ROFOverlapTable<11>::View& rofOverlaps, + const ROFVertexLookupTable<11>::View& vertexLUT, + const int vertexId, + const Vertex* vertices, + const int* rofPV, + const Cluster** clusters, + std::vector nClusters, + const int** ROFClusters, + const unsigned char** usedClusters, + const int** clustersIndexTables, + Tracklet** tracklets, + gsl::span spanTracklets, + gsl::span nTracklets, + int** trackletsLUTs, + gsl::span trackletsLUTsHost, + const int iteration, + const float NSigmaCut, + bounded_vector& phiCuts, + const float resolutionPV, + std::array& minRs, + std::array& maxRs, + bounded_vector& resolutions, + std::vector& radii, + bounded_vector& mulScatAng, + o2::its::ExternalAllocator* alloc, + gpu::Streams& streams); + +template void countCellsHandler<11>(const Cluster** sortedClusters, + const Cluster** unsortedClusters, + const TrackingFrameInfo** tfInfo, + Tracklet** tracklets, + int** trackletsLUT, + const int nTracklets, + const int layer, + CellSeed* cells, + int** cellsLUTsArrayDevice, + int* cellsLUTsHost, + const float bz, + const float maxChi2ClusterAttachment, + const float cellDeltaTanLambdaSigma, + const float nSigmaCut, + const std::vector& layerxX0Host, + o2::its::ExternalAllocator* alloc, + gpu::Streams& streams); + +template void computeCellsHandler<11>(const Cluster** sortedClusters, + const Cluster** unsortedClusters, + const TrackingFrameInfo** tfInfo, + Tracklet** tracklets, + int** trackletsLUT, + const int nTracklets, + const int layer, + CellSeed* cells, + int** cellsLUTsArrayDevice, + int* cellsLUTsHost, + const float bz, + const float maxChi2ClusterAttachment, + const float cellDeltaTanLambdaSigma, + const float nSigmaCut, + const std::vector& layerxX0Host, + gpu::Streams& streams); + +template void countCellNeighboursHandler<11>(CellSeed** cellsLayersDevice, + int* neighboursLUT, + int** cellsLUTs, + gpuPair* cellNeighbours, + int* neighboursIndexTable, + const Tracklet** tracklets, + const float maxChi2ClusterAttachment, + const float bz, + const int layerIndex, + const unsigned int nCells, + const unsigned int nCellsNext, + const int maxCellNeighbours, + o2::its::ExternalAllocator* alloc, + gpu::Stream& stream); + +template void computeCellNeighboursHandler<11>(CellSeed** cellsLayersDevice, + int* neighboursLUT, + int** cellsLUTs, + gpuPair* cellNeighbours, + int* neighboursIndexTable, + const Tracklet** tracklets, + const float maxChi2ClusterAttachment, + const float bz, + const int layerIndex, + const unsigned int nCells, + const unsigned int nCellsNext, + const int maxCellNeighbours, + gpu::Stream& stream); + +template void processNeighboursHandler<11>(const int startLayer, + const int startLevel, + CellSeed** allCellSeeds, + CellSeed* currentCellSeeds, + std::array& nCells, + const unsigned char** usedClusters, + std::array& neighbours, + gsl::span neighboursDeviceLUTs, + const TrackingFrameInfo** foundTrackingFrameInfo, + bounded_vector>& seedsHost, + const float bz, + const float maxChi2ClusterAttachment, + const float maxChi2NDF, + const std::vector& layerxX0Host, + const o2::base::Propagator* propagator, + const o2::base::PropagatorF::MatCorrType matCorrType, + o2::its::ExternalAllocator* alloc); + +template void countTrackSeedHandler(TrackSeed<11>* trackSeeds, + const TrackingFrameInfo** foundTrackingFrameInfo, + const Cluster** unsortedClusters, + int* seedLUT, + const std::vector& layerRadiiHost, + const std::vector& minPtsHost, + const std::vector& layerxX0Host, + const unsigned int nSeeds, + const float bz, + const int startLevel, + const float maxChi2ClusterAttachment, + const float maxChi2NDF, + const int reseedIfShorter, + const bool repeatRefitOut, + const bool shiftRefToCluster, + const o2::base::Propagator* propagator, + const o2::base::PropagatorF::MatCorrType matCorrType, + o2::its::ExternalAllocator* alloc); + +template void computeTrackSeedHandler(TrackSeed<11>* trackSeeds, + const TrackingFrameInfo** foundTrackingFrameInfo, + const Cluster** unsortedClusters, + o2::its::TrackITSExt* tracks, + const int* seedLUT, + const std::vector& layerRadiiHost, + const std::vector& minPtsHost, + const std::vector& layerxX0Host, + const unsigned int nSeeds, + const unsigned int nTracks, + const float bz, + const int startLevel, + const float maxChi2ClusterAttachment, + const float maxChi2NDF, + const int reseedIfShorter, + const bool repeatRefitOut, + const bool shiftRefToCluster, + const o2::base::Propagator* propagator, + const o2::base::PropagatorF::MatCorrType matCorrType, + o2::its::ExternalAllocator* alloc); +#endif } // namespace o2::its diff --git a/Detectors/Upgrades/ALICE3/CMakeLists.txt b/Detectors/Upgrades/ALICE3/CMakeLists.txt index 0335e85007c01..334bb13064783 100644 --- a/Detectors/Upgrades/ALICE3/CMakeLists.txt +++ b/Detectors/Upgrades/ALICE3/CMakeLists.txt @@ -11,6 +11,7 @@ add_subdirectory(Passive) add_subdirectory(TRK) +add_subdirectory(GlobalReconstruction) add_subdirectory(ECal) add_subdirectory(FD3) add_subdirectory(FT3) diff --git a/Detectors/Upgrades/ALICE3/GlobalReconstruction/CMakeLists.txt b/Detectors/Upgrades/ALICE3/GlobalReconstruction/CMakeLists.txt new file mode 100644 index 0000000000000..208c0a60b364e --- /dev/null +++ b/Detectors/Upgrades/ALICE3/GlobalReconstruction/CMakeLists.txt @@ -0,0 +1,13 @@ +# 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. + +add_subdirectory(reconstruction) +add_subdirectory(workflow) diff --git a/Detectors/Upgrades/ALICE3/GlobalReconstruction/reconstruction/CMakeLists.txt b/Detectors/Upgrades/ALICE3/GlobalReconstruction/reconstruction/CMakeLists.txt new file mode 100644 index 0000000000000..8805c1885b079 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/GlobalReconstruction/reconstruction/CMakeLists.txt @@ -0,0 +1,74 @@ +# 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. + +if(Acts_FOUND) + set(actsTarget Acts::Core) +endif() + +set(alice3GlobalRecoGpuSources "") +set(alice3GlobalRecoGpuTargets "") +set(alice3GlobalRecoGpuPrivateTargets "") +if(CUDA_ENABLED) + find_package(CUDAToolkit REQUIRED) + list(APPEND alice3GlobalRecoGpuSources src/TimeFrameGPU.cxx src/GPUExternalAllocator.cxx) + list(APPEND alice3GlobalRecoGpuTargets O2::ITStrackingCUDA) + list(APPEND alice3GlobalRecoGpuPrivateTargets CUDA::cudart) +elseif(HIP_ENABLED) + list(APPEND alice3GlobalRecoGpuSources src/TimeFrameGPU.cxx src/GPUExternalAllocator.cxx) + list(APPEND alice3GlobalRecoGpuTargets O2::ITStrackingHIP) + list(APPEND alice3GlobalRecoGpuPrivateTargets hip::host) +endif() + +o2_add_library(ALICE3GlobalReconstruction + TARGETVARNAME targetName + SOURCES src/TimeFrame.cxx + ${alice3GlobalRecoGpuSources} + $<$:src/TrackerACTS.cxx> + PUBLIC_LINK_LIBRARIES + O2::ITStracking + O2::GPUCommon + Microsoft.GSL::GSL + O2::CommonConstants + O2::DataFormatsITSMFT + O2::DataFormatsTRK + O2::SimulationDataFormat + O2::ITSBase + O2::ITSReconstruction + O2::ITSMFTReconstruction + O2::DataFormatsITS + O2::TRKBase + O2::TRKReconstruction + O2::TRKSimulation + nlohmann_json::nlohmann_json + ${alice3GlobalRecoGpuTargets} + ${actsTarget} + PRIVATE_LINK_LIBRARIES + O2::Steer + TBB::tbb + ${alice3GlobalRecoGpuPrivateTargets}) + +if(alice3GlobalRecoGpuTargets) + target_compile_definitions(${targetName} PUBLIC TRK_HAS_GPU_TRACKING) +endif() + +if(CUDA_ENABLED) + target_include_directories(${targetName} PRIVATE ${CUDAToolkit_INCLUDE_DIRS}) +endif() + +if(CUDA_ENABLED) + target_compile_definitions(${targetName} PUBLIC TRK_HAS_CUDA_TRACKING) +elseif(HIP_ENABLED) + target_compile_definitions(${targetName} PUBLIC TRK_HAS_HIP_TRACKING) +endif() + +if(Acts_FOUND) + target_compile_definitions(${targetName} PUBLIC O2_WITH_ACTS) +endif() diff --git a/Detectors/Upgrades/ALICE3/GlobalReconstruction/reconstruction/include/ALICE3GlobalReconstruction/GPUExternalAllocator.h b/Detectors/Upgrades/ALICE3/GlobalReconstruction/reconstruction/include/ALICE3GlobalReconstruction/GPUExternalAllocator.h new file mode 100644 index 0000000000000..e873931a5a46c --- /dev/null +++ b/Detectors/Upgrades/ALICE3/GlobalReconstruction/reconstruction/include/ALICE3GlobalReconstruction/GPUExternalAllocator.h @@ -0,0 +1,65 @@ +// 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 ALICEO2_ALICE3GLOBALRECONSTRUCTION_GPUEXTERNALALLOCATOR_H +#define ALICEO2_ALICE3GLOBALRECONSTRUCTION_GPUEXTERNALALLOCATOR_H + +#include "ITStracking/ExternalAllocator.h" + +#include +#include +#include +#include +#include +#include + +namespace o2::trk +{ + +class GPUExternalAllocator final : public o2::its::ExternalAllocator +{ + public: + GPUExternalAllocator() = default; + ~GPUExternalAllocator(); + + void* allocate(size_t size) override; + void deallocate(char* ptr, size_t size) override; + void pushTagOnStack(uint64_t tag) override; + void popTagOffStack(uint64_t tag) override; + + void releaseAll(); + + private: + enum class AllocationSpace { Host, + Device }; + + struct AllocationMeta { + AllocationSpace space; + uint64_t tag; + bool stacked; + }; + + using MemoryType = std::underlying_type_t; + + void* allocateHost(size_t size); + void* allocateDevice(size_t size); + void freeAllocation(void* ptr, AllocationSpace space); + void removeFromTagLocked(uint64_t tag, void* ptr); + + std::mutex mMutex; + std::vector mTagStack; + std::unordered_map> mTaggedAllocations; + std::unordered_map mAllocations; +}; + +} // namespace o2::trk + +#endif diff --git a/Detectors/Upgrades/ALICE3/GlobalReconstruction/reconstruction/include/ALICE3GlobalReconstruction/TimeFrame.h b/Detectors/Upgrades/ALICE3/GlobalReconstruction/reconstruction/include/ALICE3GlobalReconstruction/TimeFrame.h new file mode 100644 index 0000000000000..6daefb2346e2c --- /dev/null +++ b/Detectors/Upgrades/ALICE3/GlobalReconstruction/reconstruction/include/ALICE3GlobalReconstruction/TimeFrame.h @@ -0,0 +1,35 @@ +// 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 TimeFrame.h +/// \brief CPU TRK TimeFrame wrapper. +/// + +#ifndef ALICEO2_ALICE3GLOBALRECONSTRUCTION_TIMEFRAME_H +#define ALICEO2_ALICE3GLOBALRECONSTRUCTION_TIMEFRAME_H + +#include "ALICE3GlobalReconstruction/TimeFrameMixin.h" +#include "ITStracking/TimeFrame.h" + +namespace o2::trk +{ + +template +class TimeFrame : public TimeFrameMixin> +{ + public: + TimeFrame() = default; + ~TimeFrame() override = default; +}; + +} // namespace o2::trk + +#endif // ALICEO2_ALICE3GLOBALRECONSTRUCTION_TIMEFRAME_H diff --git a/Detectors/Upgrades/ALICE3/GlobalReconstruction/reconstruction/include/ALICE3GlobalReconstruction/TimeFrameGPU.h b/Detectors/Upgrades/ALICE3/GlobalReconstruction/reconstruction/include/ALICE3GlobalReconstruction/TimeFrameGPU.h new file mode 100644 index 0000000000000..744fca166489f --- /dev/null +++ b/Detectors/Upgrades/ALICE3/GlobalReconstruction/reconstruction/include/ALICE3GlobalReconstruction/TimeFrameGPU.h @@ -0,0 +1,35 @@ +// 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 TimeFrameGPU.h +/// \brief GPU TRK TimeFrame wrapper. +/// + +#ifndef ALICEO2_ALICE3GLOBALRECONSTRUCTION_TIMEFRAMEGPU_H +#define ALICEO2_ALICE3GLOBALRECONSTRUCTION_TIMEFRAMEGPU_H + +#include "ALICE3GlobalReconstruction/TimeFrameMixin.h" +#include "ITStrackingGPU/TimeFrameGPU.h" + +namespace o2::trk +{ + +template +class TimeFrameGPU : public TimeFrameMixin> +{ + public: + TimeFrameGPU() = default; + ~TimeFrameGPU() override = default; +}; + +} // namespace o2::trk + +#endif // ALICEO2_ALICE3GLOBALRECONSTRUCTION_TIMEFRAMEGPU_H diff --git a/Detectors/Upgrades/ALICE3/GlobalReconstruction/reconstruction/include/ALICE3GlobalReconstruction/TimeFrameMixin.h b/Detectors/Upgrades/ALICE3/GlobalReconstruction/reconstruction/include/ALICE3GlobalReconstruction/TimeFrameMixin.h new file mode 100644 index 0000000000000..1d45ec812ab8f --- /dev/null +++ b/Detectors/Upgrades/ALICE3/GlobalReconstruction/reconstruction/include/ALICE3GlobalReconstruction/TimeFrameMixin.h @@ -0,0 +1,581 @@ +// 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 TimeFrameMixin.h +/// \brief Shared TRK TimeFrame helpers for CPU and GPU backends. +/// + +#ifndef ALICEO2_ALICE3GLOBALRECONSTRUCTION_TIMEFRAMEMIXIN_H +#define ALICEO2_ALICE3GLOBALRECONSTRUCTION_TIMEFRAMEMIXIN_H + +#include "CommonDataFormat/InteractionRecord.h" +#include "DataFormatsTRK/Cluster.h" +#include "DataFormatsTRK/ROFRecord.h" +#include "ITStracking/ROFLookupTables.h" +#include "ITStracking/TimeFrame.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "SimulationDataFormat/MCEventHeader.h" +#include "SimulationDataFormat/MCTruthContainer.h" +#include "SimulationDataFormat/DigitizationContext.h" +#include "Steer/MCKinematicsReader.h" +#include "TRKReconstruction/Clusterer.h" +#include "TRKSimulation/Hit.h" +#include "TRKBase/GeometryTGeo.h" +#include "TRKBase/SegmentationChip.h" +#include "Framework/Logger.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace o2::trk +{ + +template +class TimeFrameMixin : public Base +{ + public: + TimeFrameMixin() = default; + ~TimeFrameMixin() override = default; + + int loadROFsFromHitTree(TTree* hitsTree, GeometryTGeo* gman, const nlohmann::json& config); + + int loadROFrameData(const std::array, nLayers>& layerROFs, + const std::array, nLayers>& layerClusters, + const std::array, nLayers>& layerPatterns, + const std::array*, nLayers>* mcLabels = nullptr, + float yPlaneMLOT = 0.f); + + void getPrimaryVerticesFromMC(TTree* mcHeaderTree, int nRofs, Long64_t nEvents, int inROFpileup); + + void addTruthSeedingVertices(gsl::span rofs); + + void deriveAndInitTiming(const std::array, nLayers>& layerROFs); + + const o2::InteractionRecord& getTFAnchorIR() const noexcept { return mTFAnchorIR; } + + protected: + void initTimingTables(const std::array& timings); + void updateHostROFVertexLookupTable(); + + bool mTimingTablesInitialised{false}; + o2::InteractionRecord mTFAnchorIR{0, 0}; +}; + +template +void TimeFrameMixin::updateHostROFVertexLookupTable() +{ + static_cast*>(this)->updateROFVertexLookupTable(); +} + +template +void TimeFrameMixin::initTimingTables(const std::array& timings) +{ + if (mTimingTablesInitialised) { + return; + } + typename o2::its::TimeFrame::ROFOverlapTableN rofOverlapTable; + typename o2::its::TimeFrame::ROFVertexLookupTableN rofVertexLookupTable; + typename o2::its::TimeFrame::ROFMaskTableN rofMaskTable; + for (int iLayer{0}; iLayer < nLayers; ++iLayer) { + rofOverlapTable.defineLayer(iLayer, timings[iLayer]); + rofVertexLookupTable.defineLayer(iLayer, timings[iLayer]); + rofMaskTable.defineLayer(iLayer, timings[iLayer]); + } + rofOverlapTable.init(); + rofVertexLookupTable.init(); + rofMaskTable.init(); + rofMaskTable.resetMask(1u); + this->setROFOverlapTable(std::move(rofOverlapTable)); + this->setROFVertexLookupTable(std::move(rofVertexLookupTable)); + this->setMultiplicityCutMask(std::move(rofMaskTable)); + this->useMultiplictyMask(); + mTimingTablesInitialised = true; + + const auto maskView = this->getROFMaskView(); + for (int iLayer{0}; iLayer < nLayers; ++iLayer) { + LOGP(info, "TRK timing initialised: layer {}: {}", iLayer, timings[iLayer].asString()); + LOGP(info, "TRK ROF mask: {}", maskView.asString(iLayer)); + } +} + +template +void TimeFrameMixin::deriveAndInitTiming(const std::array, nLayers>& layerROFs) +{ + if (mTimingTablesInitialised) { + return; + } + + o2::InteractionRecord anchor{0, 0}; + bool haveAnchor = false; + for (const auto& span : layerROFs) { + if (span.empty()) { + continue; + } + const auto& first = span.front().getBCData(); + if (!haveAnchor || first.toLong() < anchor.toLong()) { + anchor = first; + haveAnchor = true; + } + } + mTFAnchorIR = anchor; + const int64_t anchorBC = anchor.toLong(); + + std::array timings{}; + for (int iLayer{0}; iLayer < nLayers; ++iLayer) { + const auto& span = layerROFs[iLayer]; + auto& t = timings[iLayer]; + t.mNROFsTF = static_cast(span.size()); + + if (span.size() >= 2) { + const int64_t delta = span[1].getBCData().toLong() - span[0].getBCData().toLong(); + if (delta > 0) { + t.mROFLength = static_cast(delta); + } else { + LOGP(warning, "TRK layer {}: non-positive BC delta between rofs[0] and rofs[1] ({}); falling back to mROFLength=1", iLayer, delta); + t.mROFLength = 1; + } + } else { + if (span.size() == 1) { + LOGP(warning, "TRK layer {}: only one input ROF — cannot derive mROFLength; falling back to mROFLength=1", iLayer); + } + t.mROFLength = 1; + } + + if (!span.empty()) { + const int64_t bias = span.front().getBCData().toLong() - anchorBC; + t.mROFBias = static_cast(bias); + } + t.mROFDelay = 0; + t.mROFAddTimeErr = 0; + } + + initTimingTables(timings); +} + +template +int TimeFrameMixin::loadROFsFromHitTree(TTree* hitsTree, GeometryTGeo* gman, const nlohmann::json& config) +{ + constexpr std::array startLayer{0, 3}; + const Long64_t nEvents = hitsTree->GetEntries(); + this->setIsStaggered(true); + + gman->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L) | o2::math_utils::bit2Mask(o2::math_utils::TransformType::L2G)); + + std::vector* trkHit = nullptr; + hitsTree->SetBranchAddress("TRKHit", &trkHit); + + const int inROFpileup{config.contains("inROFpileup") ? config["inROFpileup"].get() : 1}; + + const int nRofs = (nEvents + inROFpileup - 1) / inROFpileup; + std::array timings{}; + for (int iLayer{0}; iLayer < nLayers; ++iLayer) { + timings[iLayer].mNROFsTF = static_cast(nRofs); + timings[iLayer].mROFLength = 1; + } + this->initTimingTables(timings); + const auto& timing = this->getROFOverlapTableView().getLayer(0); + if (timing.mNROFsTF != static_cast(nRofs)) { + LOGP(fatal, "TRK: inconsistent number of ROFs across TFs: timing has {}, hit-tree path produced {}", timing.mNROFsTF, nRofs); + } + + for (int iLayer{0}; iLayer < nLayers; ++iLayer) { + this->mMinR[iLayer] = std::numeric_limits::max(); + this->mMaxR[iLayer] = std::numeric_limits::lowest(); + this->mROFramesClusters[iLayer].clear(); + this->mROFramesClusters[iLayer].resize(nRofs + 1, 0); + this->mUnsortedClusters[iLayer].clear(); + this->mTrackingFrameInfo[iLayer].clear(); + this->mClusterExternalIndices[iLayer].clear(); + this->mClusterSize[iLayer].clear(); + } + + std::array clusterCountPerLayer{}; + for (Long64_t iEvent = 0; iEvent < nEvents; ++iEvent) { + hitsTree->GetEntry(iEvent); + for (const auto& hit : *trkHit) { + if (gman->getDisk(hit.GetDetectorID()) != -1) { + continue; + } + int subDetID = gman->getSubDetID(hit.GetDetectorID()); + const int layer = startLayer[subDetID] + gman->getLayer(hit.GetDetectorID()); + if (layer >= nLayers) { + continue; + } + ++clusterCountPerLayer[layer]; + } + } + + for (int iLayer{0}; iLayer < nLayers; ++iLayer) { + this->mUnsortedClusters[iLayer].reserve(clusterCountPerLayer[iLayer]); + this->mTrackingFrameInfo[iLayer].reserve(clusterCountPerLayer[iLayer]); + this->mClusterExternalIndices[iLayer].reserve(clusterCountPerLayer[iLayer]); + this->mClusterSize[iLayer].reserve(clusterCountPerLayer[iLayer]); + } + + std::array resolution{0.001, 0.001, 0.001, 0.001, 0.004, 0.004, 0.004, 0.004, 0.004, 0.004, 0.004}; + if (config["geometry"]["pitch"].size() == nLayers) { + for (int iLayer{0}; iLayer < config["geometry"]["pitch"].size(); ++iLayer) { + LOGP(info, "Setting resolution for layer {} from config", iLayer); + LOGP(info, "Layer {} pitch {} cm", iLayer, config["geometry"]["pitch"][iLayer].get()); + resolution[iLayer] = config["geometry"]["pitch"][iLayer].get() / std::sqrt(12.f); + } + } + LOGP(info, "Number of active parts in VD: {}", gman->getNumberOfActivePartsVD()); + + std::array hitCounterPerLayer{}; + std::array*, nLayers> labels{}; + for (int iLayer{0}; iLayer < nLayers; ++iLayer) { + labels[iLayer] = new dataformats::MCTruthContainer(); + this->mClusterLabels[iLayer] = labels[iLayer]; + } + + int iRof{0}; + for (Long64_t iEvent = 0; iEvent < nEvents; ++iEvent) { + hitsTree->GetEntry(iEvent); + + for (auto& hit : *trkHit) { + if (gman->getDisk(hit.GetDetectorID()) != -1) { + continue; + } + int subDetID = gman->getSubDetID(hit.GetDetectorID()); + const int layer = startLayer[subDetID] + gman->getLayer(hit.GetDetectorID()); + + float alpha{0.f}; + o2::math_utils::Point3D gloXYZ; + o2::math_utils::Point3D trkXYZ; + float r{0.f}; + if (layer >= nLayers) { + continue; + } + if (layer >= 3) { + int chipID = hit.GetDetectorID(); + alpha = gman->getSensorRefAlphaMLOT(chipID); + const o2::math_utils::Transform3D& l2g = gman->getMatrixL2G(chipID); + auto locXYZ = l2g ^ (hit.GetPos()); + locXYZ.SetX(locXYZ.X() + gRandom->Gaus(0.0, resolution[layer])); + locXYZ.SetZ(locXYZ.Z() + gRandom->Gaus(0.0, resolution[layer])); + gloXYZ = gman->getMatrixL2G(chipID) * locXYZ; + trkXYZ = gman->getMatrixT2L(chipID - gman->getNumberOfActivePartsVD()) ^ locXYZ; + r = std::hypot(gloXYZ.X(), gloXYZ.Y()); + } else { + const auto& hitPos = hit.GetPos(); + r = std::hypot(hitPos.X(), hitPos.Y()); + alpha = std::atan2(hitPos.Y(), hitPos.X()) + gRandom->Gaus(0.0, resolution[layer] / r); + o2::math_utils::bringTo02Pi(alpha); + gloXYZ.SetX(r * std::cos(alpha)); + gloXYZ.SetY(r * std::sin(alpha)); + gloXYZ.SetZ(hitPos.Z() + gRandom->Gaus(0.0, resolution[layer])); + trkXYZ.SetX(r); + trkXYZ.SetY(0.f); + trkXYZ.SetZ(gloXYZ.Z()); + } + this->mMinR[layer] = std::min(this->mMinR[layer], r); + this->mMaxR[layer] = std::max(this->mMaxR[layer], r); + this->addTrackingFrameInfoToLayer(layer, gloXYZ.x(), gloXYZ.y(), gloXYZ.z(), trkXYZ.x(), alpha, + std::array{trkXYZ.y(), trkXYZ.z()}, + std::array{resolution[layer] * resolution[layer], 0., resolution[layer] * resolution[layer]}); + this->addClusterToLayer(layer, gloXYZ.x(), gloXYZ.y(), gloXYZ.z(), this->mUnsortedClusters[layer].size()); + const int layerHitCounter = hitCounterPerLayer[layer]++; + this->addClusterExternalIndexToLayer(layer, layerHitCounter); + this->mClusterSize[layer].push_back(1); + MCCompLabel label{hit.GetTrackID(), static_cast(iEvent), 0}; + labels[layer]->addElement(layerHitCounter, label); + } + trkHit->clear(); + + if ((iEvent + 1) % inROFpileup == 0 || iEvent == nEvents - 1) { + iRof++; + for (unsigned int iLayer{0}; iLayer < this->mUnsortedClusters.size(); ++iLayer) { + this->mROFramesClusters[iLayer][iRof] = this->mUnsortedClusters[iLayer].size(); + } + } + } + return nRofs; +} + +template +int TimeFrameMixin::loadROFrameData(const std::array, nLayers>& layerROFs, + const std::array, nLayers>& layerClusters, + const std::array, nLayers>& layerPatterns, + const std::array*, nLayers>* mcLabels, + float yPlaneMLOT) +{ + constexpr std::array startLayer{0, 3}; + this->setIsStaggered(true); + GeometryTGeo* geom = GeometryTGeo::Instance(); + geom->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L) | o2::math_utils::bit2Mask(o2::math_utils::TransformType::L2G)); + + if (!mTimingTablesInitialised) { + LOGP(fatal, "TRK::loadROFrameData: timing tables not initialised — call deriveAndInitTiming() first"); + } + int nRofs{0}; + for (const auto& rofs : layerROFs) { + nRofs = std::max(nRofs, static_cast(rofs.size())); + } + + for (int iLayer{0}; iLayer < nLayers; ++iLayer) { + const auto& timing = this->getROFOverlapTableView().getLayer(iLayer); + if (timing.mNROFsTF != static_cast(layerROFs[iLayer].size())) { + LOGP(fatal, "TRK: inconsistent number of ROFs on layer {}: timing has {}, cluster path received {}", iLayer, timing.mNROFsTF, layerROFs[iLayer].size()); + } + this->mMinR[iLayer] = std::numeric_limits::max(); + this->mMaxR[iLayer] = std::numeric_limits::lowest(); + this->mROFramesClusters[iLayer].clear(); + this->mROFramesClusters[iLayer].resize(layerROFs[iLayer].size() + 1, 0); + this->mUnsortedClusters[iLayer].clear(); + this->mTrackingFrameInfo[iLayer].clear(); + this->mClusterExternalIndices[iLayer].clear(); + this->mClusterSize[iLayer].clear(); + this->mUnsortedClusters[iLayer].reserve(layerClusters[iLayer].size()); + this->mTrackingFrameInfo[iLayer].reserve(layerClusters[iLayer].size()); + this->mClusterExternalIndices[iLayer].reserve(layerClusters[iLayer].size()); + this->mClusterSize[iLayer].reserve(layerClusters[iLayer].size()); + } + + for (int iLayer{0}; iLayer < nLayers; ++iLayer) { + const uint8_t* pattPtr = layerPatterns[iLayer].data(); + const uint8_t* pattEnd = pattPtr + layerPatterns[iLayer].size(); + + for (size_t iRof{0}; iRof < layerROFs[iLayer].size(); ++iRof) { + const auto& rof = layerROFs[iLayer][iRof]; + const int first = rof.getFirstEntry(); + const int last = first + rof.getNEntries(); + + for (int clusterId{first}; clusterId < last; ++clusterId) { + if (pattPtr + 2 > pattEnd) { + LOGP(error, "Pattern stream exhausted while decoding layer {} cluster {}", iLayer, clusterId); + break; + } + const uint8_t* pattForCluster = pattPtr; + const int nBytes = (pattForCluster[0] * pattForCluster[1] + 7) / 8; + if (pattPtr + 2 + nBytes > pattEnd) { + LOGP(error, "Pattern stream truncated for layer {} cluster {}", iLayer, clusterId); + break; + } + const int pattAdvance = 2 + nBytes; + + if (clusterId < 0 || clusterId >= static_cast(layerClusters[iLayer].size())) { + LOGP(warning, "Skipping out-of-range TRK cluster {} on layer {}", clusterId, iLayer); + pattPtr += pattAdvance; + continue; + } + + const auto& c = layerClusters[iLayer][clusterId]; + if (c.subDetID < 0 || c.subDetID > 1 || c.disk != -1) { + pattPtr += pattAdvance; + continue; + } + + const int clusterLayer = startLayer[c.subDetID] + c.layer; + if (clusterLayer != iLayer) { + LOGP(error, "Skipping cluster from layer {} found in TRK layer stream {}", clusterLayer, iLayer); + pattPtr += pattAdvance; + continue; + } + + auto locXYZ = Clusterer::getClusterLocalCoordinates(c, pattForCluster, yPlaneMLOT); + pattPtr += pattAdvance; + + const auto gloXYZ = geom->getMatrixL2G(c.chipID) * locXYZ; + + float alpha{0.f}; + o2::math_utils::Point3D trkXYZ; + if (c.subDetID == 1) { + alpha = geom->getSensorRefAlphaMLOT(c.chipID); + trkXYZ = geom->getMatrixT2L(c.chipID - geom->getNumberOfActivePartsVD()) ^ locXYZ; + } else { + const float r = std::hypot(gloXYZ.X(), gloXYZ.Y()); + alpha = std::atan2(gloXYZ.Y(), gloXYZ.X()); + o2::math_utils::bringTo02Pi(alpha); + trkXYZ.SetX(r); + trkXYZ.SetY(0.f); + trkXYZ.SetZ(gloXYZ.Z()); + } + + const float r = std::hypot(gloXYZ.X(), gloXYZ.Y()); + this->mMinR[iLayer] = std::min(this->mMinR[iLayer], r); + this->mMaxR[iLayer] = std::max(this->mMaxR[iLayer], r); + + const float sigmaY2 = (c.subDetID == 0) + ? 0.25f * SegmentationChip::PitchRowVD * SegmentationChip::PitchRowVD + : 0.25f * SegmentationChip::PitchRowMLOT * SegmentationChip::PitchRowMLOT; + const float sigmaZ2 = (c.subDetID == 0) + ? 0.25f * SegmentationChip::PitchColVD * SegmentationChip::PitchColVD + : 0.25f * SegmentationChip::PitchColMLOT * SegmentationChip::PitchColMLOT; + + this->addTrackingFrameInfoToLayer(iLayer, gloXYZ.x(), gloXYZ.y(), gloXYZ.z(), trkXYZ.x(), alpha, + std::array{trkXYZ.y(), trkXYZ.z()}, + std::array{sigmaY2, 0.f, sigmaZ2}); + this->addClusterToLayer(iLayer, gloXYZ.x(), gloXYZ.y(), gloXYZ.z(), this->mUnsortedClusters[iLayer].size()); + this->addClusterExternalIndexToLayer(iLayer, clusterId); + this->mClusterSize[iLayer].push_back(std::clamp(static_cast(c.size), 0u, 255u)); + } + + this->mROFramesClusters[iLayer][iRof + 1] = this->mUnsortedClusters[iLayer].size(); + } + } + + for (auto i = 0; i < this->mNTrackletsPerCluster.size(); ++i) { + this->mNTrackletsPerCluster[i].resize(this->mUnsortedClusters[1].size()); + this->mNTrackletsPerClusterSum[i].resize(this->mUnsortedClusters[1].size() + 1); + } + + if (mcLabels != nullptr) { + for (int iLayer{0}; iLayer < nLayers; ++iLayer) { + this->mClusterLabels[iLayer] = (*mcLabels)[iLayer]; + } + } + + return nRofs; +} + +template +void TimeFrameMixin::getPrimaryVerticesFromMC(TTree* mcHeaderTree, int nRofs, Long64_t nEvents, int inROFpileup) +{ + auto mcheader = new o2::dataformats::MCEventHeader; + mcHeaderTree->SetBranchAddress("MCEventHeader.", &mcheader); + + this->mPrimaryVertices.clear(); + this->mPrimaryVerticesLabels.clear(); + + const auto& clockLayer = this->getROFOverlapTableView().getClockLayer(); + const auto rofLength = clockLayer.mROFLength; + + int iRof{0}; + for (Long64_t iEvent = 0; iEvent < nEvents; ++iEvent) { + mcHeaderTree->GetEntry(iEvent); + o2::its::Vertex vertex; + vertex.setTimeStamp(o2::its::TimeEstBC{ + clockLayer.getROFStartInBC(iRof), + static_cast(rofLength)}); + vertex.setXYZ(mcheader->GetX(), mcheader->GetY(), mcheader->GetZ()); + vertex.setNContributors(30); + vertex.setChi2(0.f); + LOGP(debug, "ROF {}: Added primary vertex at ({}, {}, {})", iRof, mcheader->GetX(), mcheader->GetY(), mcheader->GetZ()); + this->addPrimaryVertex(vertex); + this->addPrimaryVertexLabel({o2::MCCompLabel{o2::MCCompLabel::maxTrackID(), static_cast(iEvent), 0, false}, 1.f}); + if ((iEvent + 1) % inROFpileup == 0 || iEvent == nEvents - 1) { + iRof++; + } + } + updateHostROFVertexLookupTable(); +} + +template +void TimeFrameMixin::addTruthSeedingVertices(gsl::span rofs) +{ + LOGP(info, "TRK: using truth seeds as vertices from DigitizationContext"); + this->mPrimaryVertices.clear(); + this->mPrimaryVerticesLabels.clear(); + + const auto dc = o2::steer::DigitizationContext::loadFromFile("collisioncontext.root"); + const auto irs = dc->getEventRecords(); + o2::steer::MCKinematicsReader mcReader(dc); + + std::vector rofStartBC(rofs.size()); + for (size_t i = 0; i < rofs.size(); ++i) { + rofStartBC[i] = rofs[i].getBCData().toLong(); + } + + const auto& clockLayer = this->getROFOverlapTableView().getClockLayer(); + const auto rofLength = clockLayer.mROFLength; + + using Vertex = o2::its::Vertex; + struct VertInfo { + std::pmr::vector vertices; + std::pmr::vector srcs; + std::pmr::vector events; + }; + std::map vertMap; + + const int iSrc = 0; + auto eveId2colId = dc->getCollisionIndicesForSource(iSrc); + for (int iEve{0}; iEve < mcReader.getNEvents(iSrc); ++iEve) { + const auto& ir = irs[eveId2colId[iEve]]; + if (!ir.isDummy()) { + const auto& eve = mcReader.getMCEventHeader(iSrc, iEve); + const int64_t evBC = ir.toLong(); + auto it = std::upper_bound(rofStartBC.begin(), rofStartBC.end(), evBC); + if (it != rofStartBC.begin()) { + --it; + int rofId = static_cast(std::distance(rofStartBC.begin(), it)); + auto* mr = this->mMemoryPool.get(); + if (!vertMap.contains(rofId)) { + vertMap[rofId] = { + .vertices = std::pmr::vector(mr), + .srcs = std::pmr::vector(mr), + .events = std::pmr::vector(mr), + }; + } + Vertex vert; + vert.setTimeStamp(o2::its::TimeEstBC{ + clockLayer.getROFStartInBC(rofId), + static_cast(rofLength)}); + vert.setNContributors(std::max(1L, std::ranges::count_if( + mcReader.getTracks(iSrc, iEve), + [](const auto& trk) { + return trk.isPrimary() && trk.GetPt() > 0.05 && std::abs(trk.GetEta()) < 1.1; + }))); + vert.setXYZ((float)eve.GetX(), (float)eve.GetY(), (float)eve.GetZ()); + vert.setChi2(1); + constexpr float cov = 50e-9f; + vert.setCov(cov, cov, cov, cov, cov, cov); + vertMap[rofId].vertices.push_back(vert); + vertMap[rofId].srcs.push_back(iSrc); + vertMap[rofId].events.push_back(iEve); + } + } + mcReader.releaseTracksForSourceAndEvent(iSrc, iEve); + } + + size_t nVerts{0}; + auto* mr = this->mMemoryPool.get(); + for (int iROF{0}; iROF < static_cast(rofs.size()); ++iROF) { + std::pmr::vector verts(mr); + std::pmr::vector> polls(mr); + if (vertMap.contains(iROF)) { + const auto& info = vertMap[iROF]; + verts = info.vertices; + nVerts += verts.size(); + for (size_t i{0}; i < verts.size(); ++i) { + o2::MCCompLabel lbl(o2::MCCompLabel::maxTrackID(), info.events[i], info.srcs[i], false); + polls.emplace_back(lbl, 1.f); + } + } + for (const auto& vert : verts) { + this->addPrimaryVertex(vert); + } + for (const auto& label : polls) { + this->addPrimaryVertexLabel(label); + } + } + updateHostROFVertexLookupTable(); + LOGP(info, "TRK truth seeding: {}/{} ROFs with {} vertices -> ={:.2f}", + vertMap.size(), rofs.size(), nVerts, + vertMap.size() > 0 ? (float)nVerts / (float)vertMap.size() : 0.f); +} + +} // namespace o2::trk + +#endif // ALICEO2_ALICE3GLOBALRECONSTRUCTION_TIMEFRAMEMIXIN_H diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/TrackerACTS.h b/Detectors/Upgrades/ALICE3/GlobalReconstruction/reconstruction/include/ALICE3GlobalReconstruction/TrackerACTS.h similarity index 96% rename from Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/TrackerACTS.h rename to Detectors/Upgrades/ALICE3/GlobalReconstruction/reconstruction/include/ALICE3GlobalReconstruction/TrackerACTS.h index 2910abf480961..ee69b32a23895 100644 --- a/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/TrackerACTS.h +++ b/Detectors/Upgrades/ALICE3/GlobalReconstruction/reconstruction/include/ALICE3GlobalReconstruction/TrackerACTS.h @@ -16,8 +16,8 @@ /// \since 2026-04-01 /// -#ifndef ALICE3_INCLUDE_TRACKERACTS_H_ -#define ALICE3_INCLUDE_TRACKERACTS_H_ +#ifndef ALICE3_GLOBALRECONSTRUCTION_INCLUDE_TRACKERACTS_H_ +#define ALICE3_GLOBALRECONSTRUCTION_INCLUDE_TRACKERACTS_H_ #include "Acts/Definitions/Units.hpp" #include "Framework/Logger.h" @@ -186,4 +186,4 @@ float TrackerACTS::evaluateTask(Func&& task, std::string_view taskName) } // namespace o2::trk -#endif /* ALICE3_INCLUDE_TRACKERACTS_H_ */ +#endif /* ALICE3_GLOBALRECONSTRUCTION_INCLUDE_TRACKERACTS_H_ */ diff --git a/Detectors/Upgrades/ALICE3/GlobalReconstruction/reconstruction/src/GPUExternalAllocator.cxx b/Detectors/Upgrades/ALICE3/GlobalReconstruction/reconstruction/src/GPUExternalAllocator.cxx new file mode 100644 index 0000000000000..df2a2c30b037a --- /dev/null +++ b/Detectors/Upgrades/ALICE3/GlobalReconstruction/reconstruction/src/GPUExternalAllocator.cxx @@ -0,0 +1,210 @@ +// 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. + +#if defined(TRK_HAS_CUDA_TRACKING) +#include +#elif defined(TRK_HAS_HIP_TRACKING) +#include +#endif + +#include "ALICE3GlobalReconstruction/GPUExternalAllocator.h" + +#include +#include +#include + +namespace +{ +#if defined(TRK_HAS_CUDA_TRACKING) +void checkGpuError(cudaError_t error, const char* call) +{ + if (error != cudaSuccess) { + throw std::runtime_error(std::string(call) + ": " + cudaGetErrorString(error)); + } +} +#elif defined(TRK_HAS_HIP_TRACKING) +void checkGpuError(hipError_t error, const char* call) +{ + if (error != hipSuccess) { + throw std::runtime_error(std::string(call) + ": " + hipGetErrorString(error)); + } +} +#endif +} // namespace + +namespace o2::trk +{ + +GPUExternalAllocator::~GPUExternalAllocator() +{ + releaseAll(); +} + +void* GPUExternalAllocator::allocate(size_t size) +{ + const auto type = static_cast(getType()); + const bool useHost = (type & static_cast(o2::gpu::GPUMemoryResource::MEMORY_HOST)) != 0; + const bool useStack = (type & static_cast(o2::gpu::GPUMemoryResource::MEMORY_STACK)) != 0; + + void* ptr = useHost ? allocateHost(size) : allocateDevice(size); + + std::lock_guard guard(mMutex); + const uint64_t tag = (useStack && !mTagStack.empty()) ? mTagStack.back() : 0; + mAllocations.emplace(ptr, AllocationMeta{useHost ? AllocationSpace::Host : AllocationSpace::Device, tag, useStack}); + if (useStack) { + mTaggedAllocations[tag].push_back(ptr); + } + + return ptr; +} + +void GPUExternalAllocator::deallocate(char* ptr, size_t) +{ + if (!ptr) { + return; + } + + AllocationMeta meta; + { + std::lock_guard guard(mMutex); + const auto found = mAllocations.find(ptr); + if (found == mAllocations.end()) { + return; + } + meta = found->second; + mAllocations.erase(found); + if (meta.stacked) { + removeFromTagLocked(meta.tag, ptr); + } + } + + freeAllocation(ptr, meta.space); +} + +void GPUExternalAllocator::pushTagOnStack(uint64_t tag) +{ + std::lock_guard guard(mMutex); + mTagStack.push_back(tag); +} + +void GPUExternalAllocator::popTagOffStack(uint64_t tag) +{ + std::vector> toFree; + { + std::lock_guard guard(mMutex); + if (mTagStack.empty() || mTagStack.back() != tag) { + throw std::runtime_error("GPUExternalAllocator tag stack mismatch"); + } + + const auto tagged = mTaggedAllocations.find(tag); + if (tagged != mTaggedAllocations.end()) { + toFree.reserve(tagged->second.size()); + for (void* ptr : tagged->second) { + const auto found = mAllocations.find(ptr); + if (found != mAllocations.end()) { + toFree.emplace_back(ptr, found->second.space); + mAllocations.erase(found); + } + } + mTaggedAllocations.erase(tagged); + } + + mTagStack.pop_back(); + } + + for (const auto& [ptr, space] : toFree) { + freeAllocation(ptr, space); + } +} + +void GPUExternalAllocator::releaseAll() +{ + std::vector> toFree; + { + std::lock_guard guard(mMutex); + toFree.reserve(mAllocations.size()); + for (const auto& [ptr, meta] : mAllocations) { + toFree.emplace_back(ptr, meta.space); + } + mAllocations.clear(); + mTaggedAllocations.clear(); + mTagStack.clear(); + } + + for (const auto& [ptr, space] : toFree) { + freeAllocation(ptr, space); + } +} + +void* GPUExternalAllocator::allocateHost(size_t size) +{ + void* ptr = nullptr; +#if defined(TRK_HAS_CUDA_TRACKING) + checkGpuError(cudaHostAlloc(&ptr, size, cudaHostAllocPortable), "cudaHostAlloc"); +#elif defined(TRK_HAS_HIP_TRACKING) + checkGpuError(hipHostMalloc(&ptr, size, hipHostMallocPortable), "hipHostMalloc"); +#else + throw std::runtime_error("GPUExternalAllocator built without a GPU backend"); +#endif + return ptr; +} + +void* GPUExternalAllocator::allocateDevice(size_t size) +{ + void* ptr = nullptr; +#if defined(TRK_HAS_CUDA_TRACKING) + checkGpuError(cudaMalloc(&ptr, size), "cudaMalloc"); +#elif defined(TRK_HAS_HIP_TRACKING) + checkGpuError(hipMalloc(&ptr, size), "hipMalloc"); +#else + throw std::runtime_error("GPUExternalAllocator built without a GPU backend"); +#endif + return ptr; +} + +void GPUExternalAllocator::freeAllocation(void* ptr, AllocationSpace space) +{ + if (!ptr) { + return; + } + +#if defined(TRK_HAS_CUDA_TRACKING) + if (space == AllocationSpace::Host) { + checkGpuError(cudaFreeHost(ptr), "cudaFreeHost"); + } else { + checkGpuError(cudaFree(ptr), "cudaFree"); + } +#elif defined(TRK_HAS_HIP_TRACKING) + if (space == AllocationSpace::Host) { + checkGpuError(hipHostFree(ptr), "hipHostFree"); + } else { + checkGpuError(hipFree(ptr), "hipFree"); + } +#else + (void)space; +#endif +} + +void GPUExternalAllocator::removeFromTagLocked(uint64_t tag, void* ptr) +{ + const auto tagged = mTaggedAllocations.find(tag); + if (tagged == mTaggedAllocations.end()) { + return; + } + + auto& entries = tagged->second; + entries.erase(std::remove(entries.begin(), entries.end(), ptr), entries.end()); + if (entries.empty()) { + mTaggedAllocations.erase(tagged); + } +} + +} // namespace o2::trk diff --git a/Detectors/Upgrades/ALICE3/GlobalReconstruction/reconstruction/src/TimeFrame.cxx b/Detectors/Upgrades/ALICE3/GlobalReconstruction/reconstruction/src/TimeFrame.cxx new file mode 100644 index 0000000000000..1f7997b2e3968 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/GlobalReconstruction/reconstruction/src/TimeFrame.cxx @@ -0,0 +1,25 @@ +// 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 TimeFrame.cxx +/// \brief Explicit instantiation of TimeFrameMixin and TimeFrame for the +/// ITS CPU base. Shared method bodies live in TimeFrameMixin.h. +/// + +#include "ALICE3GlobalReconstruction/TimeFrame.h" + +namespace o2::trk +{ + +template class TimeFrameMixin<11, o2::its::TimeFrame<11>>; +template class TimeFrame<11>; + +} // namespace o2::trk diff --git a/Detectors/Upgrades/ALICE3/GlobalReconstruction/reconstruction/src/TimeFrameGPU.cxx b/Detectors/Upgrades/ALICE3/GlobalReconstruction/reconstruction/src/TimeFrameGPU.cxx new file mode 100644 index 0000000000000..714ead765b005 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/GlobalReconstruction/reconstruction/src/TimeFrameGPU.cxx @@ -0,0 +1,25 @@ +// 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 TimeFrameGPU.cxx +/// \brief Explicit instantiation of TimeFrameMixin and TimeFrameGPU for the +/// ITS GPU base. Shared method bodies live in TimeFrameMixin.h. +/// + +#include "ALICE3GlobalReconstruction/TimeFrameGPU.h" + +namespace o2::trk +{ + +template class TimeFrameMixin<11, o2::its::gpu::TimeFrameGPU<11>>; +template class TimeFrameGPU<11>; + +} // namespace o2::trk diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TrackerACTS.cxx b/Detectors/Upgrades/ALICE3/GlobalReconstruction/reconstruction/src/TrackerACTS.cxx similarity index 98% rename from Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TrackerACTS.cxx rename to Detectors/Upgrades/ALICE3/GlobalReconstruction/reconstruction/src/TrackerACTS.cxx index 732a0acc14b66..e870ee934816f 100644 --- a/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TrackerACTS.cxx +++ b/Detectors/Upgrades/ALICE3/GlobalReconstruction/reconstruction/src/TrackerACTS.cxx @@ -16,7 +16,7 @@ /// \since 2026-04-01 /// -#include "TRKReconstruction/TrackerACTS.h" +#include "ALICE3GlobalReconstruction/TrackerACTS.h" #include #include @@ -261,10 +261,10 @@ void TrackerACTS::clustersToTracks() double totalTime = 0.; LOG(info) << "==== TRK ACTS Tracking ===="; - LOG(info) << "Processing " << mTimeFrame->getNrof(0) << " ROFs with B = " << mBz << " T"; + LOG(info) << "Processing " << mTimeFrame->getNrof() << " ROFs with B = " << mBz << " T"; // Process each ROF - for (int iROF = 0; iROF < mTimeFrame->getNrof(0); ++iROF) { + for (int iROF = 0; iROF < mTimeFrame->getNrof(); ++iROF) { LOG(info) << "Processing ROF " << iROF; // Build space points mCurState = SpacePointBuilding; diff --git a/Detectors/Upgrades/ALICE3/GlobalReconstruction/workflow/CMakeLists.txt b/Detectors/Upgrades/ALICE3/GlobalReconstruction/workflow/CMakeLists.txt new file mode 100644 index 0000000000000..be6add9c03483 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/GlobalReconstruction/workflow/CMakeLists.txt @@ -0,0 +1,35 @@ +# 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(ALICE3GlobalReconstructionWorkflow + TARGETVARNAME targetName + SOURCES src/TrackerSpec.cxx + src/TrackWriterSpec.cxx + src/RecoWorkflow.cxx + PUBLIC_LINK_LIBRARIES O2::Framework + O2::GPUWorkflow + O2::SimConfig + O2::DataFormatsITSMFT + O2::DataFormatsTRK + O2::SimulationDataFormat + O2::DPLUtils + O2::TRKBase + O2::TRKSimulation + O2::ALICE3GlobalReconstruction + nlohmann_json::nlohmann_json) + +o2_add_executable(reco-workflow + SOURCES src/alice3-global-reconstruction-workflow.cxx + COMPONENT_NAME alice3-global-reconstruction + PUBLIC_LINK_LIBRARIES O2::ALICE3GlobalReconstructionWorkflow + O2::TRKSimulation + O2::ALICE3GlobalReconstruction + O2::ITStracking) diff --git a/Detectors/Upgrades/ALICE3/GlobalReconstruction/workflow/README.md b/Detectors/Upgrades/ALICE3/GlobalReconstruction/workflow/README.md new file mode 100644 index 0000000000000..f22e95d6971db --- /dev/null +++ b/Detectors/Upgrades/ALICE3/GlobalReconstruction/workflow/README.md @@ -0,0 +1,133 @@ +# ALICE 3 Global Reconstruction Workflow + +This document describes how to run the ALICE 3 global reconstruction workflow and provides examples of configuration files. + +## Overview + +The global reconstruction workflow performs track reconstruction from simulated hits or TRK clusters, producing reconstructed tracks with MC truth labels. The workflow currently supports tracking using the Cellular Automaton (CA) algorithm. The output is stored to a ROOT file for offline analysis (example of QA macro provided in `TRK/macros/test/CheckTracksCA.C`). + +## Quick Start + +### Basic Command + +```bash +o2-alice3-global-reconstruction-reco-workflow --tracking-from-hits-config config_tracker.json -b +``` + +### Command Line Options + +- `--tracking-from-hits-config `: Path to tracking-from-hits configuration JSON file +- `--tracking-from-clusters-config `: Path to tracking-from-clusters configuration JSON file +- `--gpu-device `: Tracking device type (`1` CPU, `2` CUDA, `3` HIP) +- `-b`: Batch mode (no GUI) +- `--disable-root-output`: Skip writing tracks to ROOT file +- `--help`: Show all available options + +## Configuration File + +The tracking configuration is provided via a JSON file that specifies: +1. Input file paths +2. Geometry parameters (magnetic field, detector pitch) +3. Tracking algorithm parameters (can specify multiple iterations) + +### Example Configuration (`config_tracker.json`) + +```json +{ + "inputfiles": { + "hits": "o2sim_HitsTRK.root", + "geometry": "o2sim_geometry.root", + "mcHeader": "o2sim_MCHeader.root", + "kinematics": "o2sim_Kine.root" + }, + "geometry": { + "bz": 5.0, + "pitch": [0.001, 0.001, 0.001, 0.001, 0.004, 0.004, 0.004, 0.004, 0.004, 0.004, 0.004] + }, + "trackingparams": [{ + "NLayers": 11, + "DeltaROF": 0, + "LayerZ": [25.1, 25.1, 25.1, 64.2, 64.2, 64.2, 64.2, 64.2, 128.5, 128.5, 128.5], + "LayerRadii": [0.5, 1.2, 2.5, 7.05, 9.05, 12.05, 20.05, 30.05, 45.05, 60.5, 80.05], + "LayerxX0": [0.001, 0.001, 0.001, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01], + "LayerResolution": [0.0003, 0.0003, 0.0003, 0.0003, 0.0012, 0.0012, 0.0012, 0.0012, 0.0012, 0.0012, 0.0012], + "SystErrorY2": [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + "SystErrorZ2": [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + "AddTimeError": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "ZBins": 256, + "PhiBins": 128, + "nROFsPerIterations": -1, + "UseDiamond": false, + "Diamond": [0.0, 0.0, 0.0], + "AllowSharingFirstCluster": false, + "ClusterSharing": 0, + "MinTrackLength": 7, + "NSigmaCut": 10, + "PVres": 0.01, + "TrackletMinPt": 0.1, + "TrackletsPerClusterLimit": 2.0, + "CellDeltaTanLambdaSigma": 0.007, + "CellsPerClusterLimit": 2.0, + "MaxChi2ClusterAttachment": 60.0, + "MaxChi2NDF": 30.0, + "ReseedIfShorter": 6, + "MinPt": [0.0, 0.0, 0.0, 0.0, 0.0], + "StartLayerMask": 4095, + "RepeatRefitOut": false, + "ShiftRefToCluster": true, + "FindShortTracks": false, + "PerPrimaryVertexProcessing": false, + "SaveTimeBenchmarks": false, + "DoUPCIteration": false, + "FataliseUponFailure": true, + "UseTrackFollower": true, + "UseTrackFollowerTop": false, + "UseTrackFollowerBot": false, + "UseTrackFollowerMix": true, + "TrackFollowerNSigmaCutZ": 1.0, + "TrackFollowerNSigmaCutPhi": 1.0, + "createArtefactLabels": false, + "PrintMemory": false, + "DropTFUponFailure": false + }] +} +``` +Note that the `trackingparams` field can contain multiple sets of parameters for different iterations of the tracking algorithm. The example above shows a single iteration with 11 layers and it is **not** optimized. + +## Complete Workflow Example + +### 1. Run Simulation + +First, generate simulation data: + +```bash +o2-sim-serial-run5 -n 200 -g pythia8hi -m TRK --configKeyValues "Diamond.width[0]=0.01;Diamond.width[1]=0.01;Diamond.width[2]=5;TRKBase.layoutML=kTurboStaves;TRKBase.layoutOT=kStaggered;" +``` + +This produces, among other files: +- `o2sim_HitsTRK.root` +- `o2sim_geometry.root` +- `o2sim_MCHeader.root` +- `o2sim_Kine.root` +That will be used by the reconstruction as currently we do not have clusters. + +### 2. Run Reconstruction + +Execute the tracking workflow: + +```bash +o2-alice3-global-reconstruction-reco-workflow --tracking-from-hits-config config_tracker.json -b +``` + +This produces: +- `o2trac_trk.root`: Reconstructed tracks with MC labels + +### 3. Run Quality Assurance + +Analyze the tracking performance: + +```bash +root -l +.L CheckTracksCA.C+ +CheckTracksCA("o2trac_trk.root", "o2sim_Kine.root", "o2sim_HitsTRK.root", "trk_qa_output.root") +``` diff --git a/Detectors/Upgrades/ALICE3/GlobalReconstruction/workflow/include/ALICE3GlobalReconstructionWorkflow/RecoWorkflow.h b/Detectors/Upgrades/ALICE3/GlobalReconstruction/workflow/include/ALICE3GlobalReconstructionWorkflow/RecoWorkflow.h new file mode 100644 index 0000000000000..98a5176d5db44 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/GlobalReconstruction/workflow/include/ALICE3GlobalReconstructionWorkflow/RecoWorkflow.h @@ -0,0 +1,30 @@ +// 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_ALICE3_GLOBALRECONSTRUCTION_RECOWORKFLOW_H +#define O2_ALICE3_GLOBALRECONSTRUCTION_RECOWORKFLOW_H + +#include "Framework/WorkflowSpec.h" +#include "GPUDataTypesConfig.h" +#include + +namespace o2::trk::global_reco_workflow +{ + +o2::framework::WorkflowSpec getWorkflow(bool useMC, + const std::string& hitRecoConfig, + const std::string& clusterRecoConfig, + bool disableRootOutput = false, + o2::gpu::gpudatatypes::DeviceType dType = o2::gpu::gpudatatypes::DeviceType::CPU); + +} // namespace o2::trk::global_reco_workflow + +#endif diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/TrackWriterSpec.h b/Detectors/Upgrades/ALICE3/GlobalReconstruction/workflow/include/ALICE3GlobalReconstructionWorkflow/TrackWriterSpec.h similarity index 100% rename from Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/TrackWriterSpec.h rename to Detectors/Upgrades/ALICE3/GlobalReconstruction/workflow/include/ALICE3GlobalReconstructionWorkflow/TrackWriterSpec.h diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/TrackerSpec.h b/Detectors/Upgrades/ALICE3/GlobalReconstruction/workflow/include/ALICE3GlobalReconstructionWorkflow/TrackerSpec.h similarity index 84% rename from Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/TrackerSpec.h rename to Detectors/Upgrades/ALICE3/GlobalReconstruction/workflow/include/ALICE3GlobalReconstructionWorkflow/TrackerSpec.h index 304b32041c2dc..006bb4cbf5260 100644 --- a/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/TrackerSpec.h +++ b/Detectors/Upgrades/ALICE3/GlobalReconstruction/workflow/include/ALICE3GlobalReconstructionWorkflow/TrackerSpec.h @@ -22,6 +22,7 @@ #include #include "ITStracking/BoundedAllocator.h" +#include "ITStracking/ExternalAllocator.h" #include "ITStracking/TrackingInterface.h" #include "GPUDataTypesConfig.h" @@ -39,6 +40,7 @@ class TrackerDPL : public framework::Task TrackerDPL(std::shared_ptr gr, bool isMC, const std::string& hitRecoConfig, + const std::string& clusterRecoConfig, gpu::gpudatatypes::DeviceType dType = gpu::gpudatatypes::DeviceType::CPU); ~TrackerDPL() override = default; void init(framework::InitContext& ic) final; @@ -54,16 +56,20 @@ class TrackerDPL : public framework::Task // std::unique_ptr mChainITS = nullptr; // std::shared_ptr mGGCCDBRequest; // ITSTrackingInterface mITSTrackingInterface; + bool mIsMC{true}; + gpu::gpudatatypes::DeviceType mDeviceType{gpu::gpudatatypes::DeviceType::CPU}; std::shared_ptr mMemoryPool; + std::shared_ptr mGPUAllocator; std::shared_ptr mTaskArena; nlohmann::json mHitRecoConfig; + nlohmann::json mClusterRecoConfig; TStopwatch mTimer; #ifdef O2_WITH_ACTS bool mUseACTS = false; #endif }; -framework::DataProcessorSpec getTrackerSpec(bool useMC, const std::string& hitRecoConfig, gpu::gpudatatypes::DeviceType dType = gpu::gpudatatypes::DeviceType::CPU); +framework::DataProcessorSpec getTrackerSpec(bool useMC, const std::string& hitRecoConfig, const std::string& clusterRecoConfig, gpu::gpudatatypes::DeviceType dType = gpu::gpudatatypes::DeviceType::CPU); } // namespace o2::trk #endif /* O2_TRK_TRACKERDPL */ diff --git a/Detectors/Upgrades/ALICE3/GlobalReconstruction/workflow/src/RecoWorkflow.cxx b/Detectors/Upgrades/ALICE3/GlobalReconstruction/workflow/src/RecoWorkflow.cxx new file mode 100644 index 0000000000000..024bd3b4425f8 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/GlobalReconstruction/workflow/src/RecoWorkflow.cxx @@ -0,0 +1,40 @@ +// 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. + +#include "ALICE3GlobalReconstructionWorkflow/RecoWorkflow.h" +#include "ALICE3GlobalReconstructionWorkflow/TrackerSpec.h" +#include "ALICE3GlobalReconstructionWorkflow/TrackWriterSpec.h" +#include "Framework/Logger.h" + +namespace o2::trk::global_reco_workflow +{ + +framework::WorkflowSpec getWorkflow(bool useMC, + const std::string& hitRecoConfig, + const std::string& clusterRecoConfig, + bool disableRootOutput, + o2::gpu::gpudatatypes::DeviceType dtype) +{ + framework::WorkflowSpec specs; + + if (!hitRecoConfig.empty() || !clusterRecoConfig.empty()) { + LOG_IF(info, !hitRecoConfig.empty()) << "Using hit reco config from file " << hitRecoConfig; + LOG_IF(info, !clusterRecoConfig.empty()) << "Using cluster reco config from file " << clusterRecoConfig; + specs.emplace_back(o2::trk::getTrackerSpec(useMC, hitRecoConfig, clusterRecoConfig, dtype)); + if (!disableRootOutput) { + specs.emplace_back(o2::trk::getTrackWriterSpec(useMC)); + } + } + + return specs; +} + +} // namespace o2::trk::global_reco_workflow diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackWriterSpec.cxx b/Detectors/Upgrades/ALICE3/GlobalReconstruction/workflow/src/TrackWriterSpec.cxx similarity index 97% rename from Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackWriterSpec.cxx rename to Detectors/Upgrades/ALICE3/GlobalReconstruction/workflow/src/TrackWriterSpec.cxx index 1606c32a0ea78..9827c2fc2469d 100644 --- a/Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackWriterSpec.cxx +++ b/Detectors/Upgrades/ALICE3/GlobalReconstruction/workflow/src/TrackWriterSpec.cxx @@ -13,7 +13,7 @@ #include -#include "TRKWorkflow/TrackWriterSpec.h" +#include "ALICE3GlobalReconstructionWorkflow/TrackWriterSpec.h" #include "DPLUtils/MakeRootTreeWriterSpec.h" #include "DataFormatsITS/TrackITS.h" #include "SimulationDataFormat/MCCompLabel.h" diff --git a/Detectors/Upgrades/ALICE3/GlobalReconstruction/workflow/src/TrackerSpec.cxx b/Detectors/Upgrades/ALICE3/GlobalReconstruction/workflow/src/TrackerSpec.cxx new file mode 100644 index 0000000000000..455b733c96fbe --- /dev/null +++ b/Detectors/Upgrades/ALICE3/GlobalReconstruction/workflow/src/TrackerSpec.cxx @@ -0,0 +1,560 @@ +// 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. + +#include +#include +#include +#include +#include +#include +#include + +#include "CommonDataFormat/IRFrame.h" +#include "DataFormatsTRK/Cluster.h" +#include "DataFormatsTRK/ROFRecord.h" +#include "DetectorsBase/GeometryManager.h" +#include "ITStracking/TimeFrame.h" +#include "ITStracking/Configuration.h" +#include "Field/MagneticField.h" +#include "Field/MagFieldParam.h" +#include "Framework/ControlService.h" +#include "Framework/ConfigParamRegistry.h" +#include "Framework/CCDBParamSpec.h" +#include "ITStracking/TrackingConfigParam.h" +#include "SimulationDataFormat/MCEventHeader.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "SimulationDataFormat/MCTruthContainer.h" +#include "TRKBase/GeometryTGeo.h" +#include "TRKBase/SegmentationChip.h" +#include "TRKSimulation/Hit.h" +#include "ALICE3GlobalReconstruction/TimeFrame.h" +#ifdef TRK_HAS_GPU_TRACKING +#include "ALICE3GlobalReconstruction/TimeFrameGPU.h" +#include "ALICE3GlobalReconstruction/GPUExternalAllocator.h" +#include "ITStrackingGPU/TrackerTraitsGPU.h" +#endif +#include "ALICE3GlobalReconstructionWorkflow/TrackerSpec.h" +#include + +#ifdef O2_WITH_ACTS +#include "ALICE3GlobalReconstruction/TrackerACTS.h" +#endif + +#include +#include + +namespace o2 +{ +using namespace framework; +namespace trk +{ +using Vertex = o2::dataformats::Vertex>; + +TrackerDPL::TrackerDPL(std::shared_ptr gr, + bool isMC, + const std::string& hitRecoConfigFileName, + const std::string& clusterRecoConfigFileName, + o2::gpu::gpudatatypes::DeviceType dType) +{ + if (!hitRecoConfigFileName.empty()) { + std::ifstream configFile(hitRecoConfigFileName); + mHitRecoConfig = nlohmann::json::parse(configFile); + } + if (!clusterRecoConfigFileName.empty()) { + std::ifstream configFile(clusterRecoConfigFileName); + mClusterRecoConfig = nlohmann::json::parse(configFile); + } + mIsMC = isMC; + mDeviceType = dType; +} + +void TrackerDPL::init(InitContext& ic) +{ +#ifdef O2_WITH_ACTS + mUseACTS = ic.options().get("useACTS"); +#endif +} + +void TrackerDPL::stop() +{ + LOGF(info, "CPU Reconstruction total timing: Cpu: %.3e Real: %.3e s in %d slots", mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); +} + +std::vector TrackerDPL::createTrackingParamsFromConfig() +{ + std::vector trackingParams; + auto loadTrackingParamsFromJson = [](std::vector& trackingParams, const nlohmann::json& paramConfigJson) { + for (const auto& paramConfig : paramConfigJson) { + o2::its::TrackingParameters params; + + if (paramConfig.contains("NLayers")) { + params.NLayers = paramConfig["NLayers"].get(); + } + if (paramConfig.contains("ZBins")) { + params.ZBins = paramConfig["ZBins"].get(); + } + if (paramConfig.contains("PhiBins")) { + params.PhiBins = paramConfig["PhiBins"].get(); + } + if (paramConfig.contains("ClusterSharing")) { + params.ClusterSharing = paramConfig["ClusterSharing"].get(); + } + if (paramConfig.contains("MinTrackLength")) { + params.MinTrackLength = paramConfig["MinTrackLength"].get(); + } + if (paramConfig.contains("ReseedIfShorter")) { + params.ReseedIfShorter = paramConfig["ReseedIfShorter"].get(); + } + if (paramConfig.contains("StartLayerMask")) { + params.StartLayerMask = paramConfig["StartLayerMask"].get(); + } + + if (paramConfig.contains("NSigmaCut")) { + params.NSigmaCut = paramConfig["NSigmaCut"].get(); + } + if (paramConfig.contains("PVres")) { + params.PVres = paramConfig["PVres"].get(); + } + if (paramConfig.contains("TrackletMinPt")) { + params.TrackletMinPt = paramConfig["TrackletMinPt"].get(); + } + if (paramConfig.contains("CellDeltaTanLambdaSigma")) { + params.CellDeltaTanLambdaSigma = paramConfig["CellDeltaTanLambdaSigma"].get(); + } + if (paramConfig.contains("MaxChi2ClusterAttachment")) { + params.MaxChi2ClusterAttachment = paramConfig["MaxChi2ClusterAttachment"].get(); + } + if (paramConfig.contains("MaxChi2NDF")) { + params.MaxChi2NDF = paramConfig["MaxChi2NDF"].get(); + } + + if (paramConfig.contains("UseDiamond")) { + params.UseDiamond = paramConfig["UseDiamond"].get(); + } + if (paramConfig.contains("AllowSharingFirstCluster")) { + params.AllowSharingFirstCluster = paramConfig["AllowSharingFirstCluster"].get(); + } + if (paramConfig.contains("RepeatRefitOut")) { + params.RepeatRefitOut = paramConfig["RepeatRefitOut"].get(); + } + if (paramConfig.contains("ShiftRefToCluster")) { + params.ShiftRefToCluster = paramConfig["ShiftRefToCluster"].get(); + } + if (paramConfig.contains("PerPrimaryVertexProcessing")) { + params.PerPrimaryVertexProcessing = paramConfig["PerPrimaryVertexProcessing"].get(); + } + if (paramConfig.contains("SaveTimeBenchmarks")) { + params.SaveTimeBenchmarks = paramConfig["SaveTimeBenchmarks"].get(); + } + if (paramConfig.contains("DoUPCIteration")) { + params.DoUPCIteration = paramConfig["DoUPCIteration"].get(); + } + if (paramConfig.contains("FataliseUponFailure")) { + params.FataliseUponFailure = paramConfig["FataliseUponFailure"].get(); + } + if (paramConfig.contains("createArtefactLabels")) { + params.createArtefactLabels = paramConfig["createArtefactLabels"].get(); + } + if (paramConfig.contains("PrintMemory")) { + params.PrintMemory = paramConfig["PrintMemory"].get(); + } + if (paramConfig.contains("DropTFUponFailure")) { + params.DropTFUponFailure = paramConfig["DropTFUponFailure"].get(); + } + + if (paramConfig.contains("LayerZ")) { + params.LayerZ = paramConfig["LayerZ"].get>(); + } + if (paramConfig.contains("LayerRadii")) { + params.LayerRadii = paramConfig["LayerRadii"].get>(); + } + if (paramConfig.contains("LayerxX0")) { + params.LayerxX0 = paramConfig["LayerxX0"].get>(); + } + if (paramConfig.contains("LayerResolution")) { + params.LayerResolution = paramConfig["LayerResolution"].get>(); + } + if (paramConfig.contains("SystErrorY2")) { + params.SystErrorY2 = paramConfig["SystErrorY2"].get>(); + } + if (paramConfig.contains("SystErrorZ2")) { + params.SystErrorZ2 = paramConfig["SystErrorZ2"].get>(); + } + if (paramConfig.contains("MinPt")) { + params.MinPt = paramConfig["MinPt"].get>(); + } + if (paramConfig.contains("AddTimeError")) { + params.AddTimeError = paramConfig["AddTimeError"].get>(); + } + + if (paramConfig.contains("Diamond") && paramConfig["Diamond"].is_array() && paramConfig["Diamond"].size() == 3) { + params.Diamond[0] = paramConfig["Diamond"][0].get(); + params.Diamond[1] = paramConfig["Diamond"][1].get(); + params.Diamond[2] = paramConfig["Diamond"][2].get(); + } + + if (paramConfig.contains("MaxMemory")) { + params.MaxMemory = paramConfig["MaxMemory"].get(); + } + + if (paramConfig.contains("CorrType")) { + int corrTypeInt = paramConfig["CorrType"].get(); + params.CorrType = static_cast::MatCorrType>(corrTypeInt); + } + + const auto nLayers = static_cast(params.NLayers); + LOG_IF(fatal, params.LayerZ.size() != nLayers) << "Invalid ALICE3 TRK tracking parameter LayerZ: expected " << nLayers << " entries, got " << params.LayerZ.size(); + LOG_IF(fatal, params.LayerRadii.size() != nLayers) << "Invalid ALICE3 TRK tracking parameter LayerRadii: expected " << nLayers << " entries, got " << params.LayerRadii.size(); + LOG_IF(fatal, params.LayerxX0.size() != nLayers) << "Invalid ALICE3 TRK tracking parameter LayerxX0: expected " << nLayers << " entries, got " << params.LayerxX0.size(); + LOG_IF(fatal, params.LayerResolution.size() != nLayers) << "Invalid ALICE3 TRK tracking parameter LayerResolution: expected " << nLayers << " entries, got " << params.LayerResolution.size(); + LOG_IF(fatal, params.SystErrorY2.size() != nLayers) << "Invalid ALICE3 TRK tracking parameter SystErrorY2: expected " << nLayers << " entries, got " << params.SystErrorY2.size(); + LOG_IF(fatal, params.SystErrorZ2.size() != nLayers) << "Invalid ALICE3 TRK tracking parameter SystErrorZ2: expected " << nLayers << " entries, got " << params.SystErrorZ2.size(); + LOG_IF(fatal, params.AddTimeError.size() != nLayers) << "Invalid ALICE3 TRK tracking parameter AddTimeError: expected " << nLayers << " entries, got " << params.AddTimeError.size(); + + LOG_IF(fatal, params.MinTrackLength > params.NLayers) << "Invalid ALICE3 TRK tracking parameter MinTrackLength: expected <= NLayers (" << params.NLayers << "), got " << params.MinTrackLength; + const auto minPtSize = static_cast(params.NLayers - params.MinTrackLength + 1); + LOG_IF(fatal, params.MinPt.size() != minPtSize) << "Invalid ALICE3 TRK tracking parameter MinPt: expected " << minPtSize << " entries, got " << params.MinPt.size(); + + trackingParams.push_back(params); + } + }; + + if (mHitRecoConfig.contains("trackingparams") && mHitRecoConfig["trackingparams"].is_array()) { + loadTrackingParamsFromJson(trackingParams, mHitRecoConfig["trackingparams"]); + } else if (mClusterRecoConfig.contains("trackingparams") && mClusterRecoConfig["trackingparams"].is_array()) { + loadTrackingParamsFromJson(trackingParams, mClusterRecoConfig["trackingparams"]); + } else { + LOGP(fatal, "No trackingparams field found in configuration or it is not an array. Returning empty vector."); + return trackingParams; + } + + LOGP(info, "Loaded {} tracking parameter sets from configuration", trackingParams.size()); + return trackingParams; +} + +void TrackerDPL::run(ProcessingContext& pc) +{ + if (mMemoryPool.get() == nullptr) { + mMemoryPool = std::make_shared(); + } + if (mTaskArena.get() == nullptr) { + mTaskArena = std::make_shared(1); /// TODO: make it configurable + } + + auto trackingParams = createTrackingParamsFromConfig(); + + auto cput = mTimer.CpuTime(); + auto realt = mTimer.RealTime(); + mTimer.Start(false); + + const bool useGPU = mDeviceType != o2::gpu::gpudatatypes::DeviceType::CPU; +#ifndef TRK_HAS_GPU_TRACKING + if (useGPU) { + LOGP(fatal, "TRK GPU tracking was requested but this build has no TRK GPU tracking backend"); + } +#else +#ifdef TRK_HAS_CUDA_TRACKING + if (useGPU && mDeviceType != o2::gpu::gpudatatypes::DeviceType::CUDA) { + LOGP(fatal, "This build provides the CUDA TRK tracking backend only, but device type {} was requested", static_cast(mDeviceType)); + } +#elif defined(TRK_HAS_HIP_TRACKING) + if (useGPU && mDeviceType != o2::gpu::gpudatatypes::DeviceType::HIP) { + LOGP(fatal, "This build provides the HIP TRK tracking backend only, but device type {} was requested", static_cast(mDeviceType)); + } +#endif +#endif + + auto runTracking = [&](auto& timeFrame, auto& trackerTraits) { + o2::its::Tracker<11> itsTracker(&trackerTraits); + timeFrame.setMemoryPool(mMemoryPool); + trackerTraits.setMemoryPool(mMemoryPool); + trackerTraits.setNThreads(mTaskArena->max_concurrency(), mTaskArena); + trackerTraits.adoptTimeFrame(static_cast*>(&timeFrame)); + itsTracker.adoptTimeFrame(timeFrame); + trackerTraits.updateTrackingParameters(trackingParams); + + int nRofs{0}; + if (!mHitRecoConfig.empty()) { + TFile hitsFile(mHitRecoConfig["inputfiles"]["hits"].get().c_str(), "READ"); + TFile mcHeaderFile(mHitRecoConfig["inputfiles"]["mcHeader"].get().c_str(), "READ"); + TTree* hitsTree = hitsFile.Get("o2sim"); + std::vector* trkHit = nullptr; + hitsTree->SetBranchAddress("TRKHit", &trkHit); + + TTree* mcHeaderTree = mcHeaderFile.Get("o2sim"); + auto mcheader = new o2::dataformats::MCEventHeader; + mcHeaderTree->SetBranchAddress("MCEventHeader.", &mcheader); + + o2::base::GeometryManager::loadGeometry(mHitRecoConfig["inputfiles"]["geometry"].get().c_str(), false, true); + auto* gman = o2::trk::GeometryTGeo::Instance(); + + const Long64_t nEvents{hitsTree->GetEntries()}; + LOGP(info, "Starting {} reconstruction from hits for {} events", trackerTraits.getName(), nEvents); + + trackerTraits.setBz(mHitRecoConfig["geometry"]["bz"].get()); + auto field = new field::MagneticField("ALICE3Mag", "ALICE 3 Magnetic Field", mHitRecoConfig["geometry"]["bz"].get() / 5.f, 0.0, o2::field::MagFieldParam::k5kGUniform); + TGeoGlobalMagField::Instance()->SetField(field); + TGeoGlobalMagField::Instance()->Lock(); + + nRofs = timeFrame.loadROFsFromHitTree(hitsTree, gman, mHitRecoConfig); + const int inROFpileup{mHitRecoConfig.contains("inROFpileup") ? mHitRecoConfig["inROFpileup"].get() : 1}; + timeFrame.getPrimaryVerticesFromMC(mcHeaderTree, nRofs, nEvents, inROFpileup); + } else if (!mClusterRecoConfig.empty()) { + LOGP(info, "Starting {} reconstruction from clusters", trackerTraits.getName()); + + o2::base::GeometryManager::loadGeometry(mClusterRecoConfig["inputfiles"]["geometry"].get().c_str(), false, true); + o2::trk::GeometryTGeo::Instance(); + + trackerTraits.setBz(mClusterRecoConfig["geometry"]["bz"].get()); + auto field = new field::MagneticField("ALICE3Mag", "ALICE 3 Magnetic Field", mClusterRecoConfig["geometry"]["bz"].get() / 5.f, 0.0, o2::field::MagFieldParam::k5kGUniform); + TGeoGlobalMagField::Instance()->SetField(field); + TGeoGlobalMagField::Instance()->Lock(); + + constexpr int nLayers{11}; + std::array, nLayers> layerClusters; + std::array, nLayers> layerPatterns; + std::array, nLayers> layerROFs; + std::array*, nLayers> layerLabels{}; + + size_t nInputRofs{0}; + for (int iLayer = 0; iLayer < nLayers; ++iLayer) { + layerClusters[iLayer] = pc.inputs().get>(std::format("compClusters_{}", iLayer)); + layerPatterns[iLayer] = pc.inputs().get>(std::format("patterns_{}", iLayer)); + layerROFs[iLayer] = pc.inputs().get>(std::format("ROframes_{}", iLayer)); + nInputRofs = std::max(nInputRofs, layerROFs[iLayer].size()); + if (mIsMC) { + layerLabels[iLayer] = pc.inputs().get*>(std::format("trkmclabels_{}", iLayer)).release(); + } + } + + timeFrame.deriveAndInitTiming(layerROFs); + + std::vector truthSeedROFs; + truthSeedROFs.reserve(nInputRofs); + for (size_t iRof = 0; iRof < nInputRofs; ++iRof) { + for (int iLayer = 0; iLayer < nLayers; ++iLayer) { + if (iRof < layerROFs[iLayer].size()) { + truthSeedROFs.push_back(layerROFs[iLayer][iRof]); + break; + } + } + } + + const float yPlaneMLOT = 0.0010f; + nRofs = timeFrame.loadROFrameData(layerROFs, layerClusters, layerPatterns, mIsMC ? &layerLabels : nullptr, yPlaneMLOT); + timeFrame.addTruthSeedingVertices(truthSeedROFs); + } + + const auto trackingLoopStart = std::chrono::steady_clock::now(); + for (size_t iter{0}; iter < trackingParams.size(); ++iter) { + LOGP(info, "{}", trackingParams[iter].asString()); + trackerTraits.initialiseTimeFrame(iter); + trackerTraits.computeLayerTracklets(iter, -1); + LOGP(info, "Number of tracklets in iteration {}: {}", iter, timeFrame.getNumberOfTracklets()); + trackerTraits.computeLayerCells(iter); + LOGP(info, "Number of cells in iteration {}: {}", iter, timeFrame.getNumberOfCells()); + trackerTraits.findCellsNeighbours(iter); + LOGP(info, "Number of cell neighbours in iteration {}: {}", iter, timeFrame.getNumberOfNeighbours()); + trackerTraits.findRoads(iter); + LOGP(info, "Number of roads in iteration {}: {}", iter, timeFrame.getNumberOfTracks()); + } + const auto trackingLoopElapsedMs = std::chrono::duration_cast(std::chrono::steady_clock::now() - trackingLoopStart).count(); + LOGP(info, "Tracking iterations block took {} ms", trackingLoopElapsedMs); + + if (mIsMC) { + itsTracker.computeTracksMClabels(); + } + + const auto& tracks = timeFrame.getTracks(); + const auto& labels = timeFrame.getTracksLabel(); + std::vector allTracks(tracks.begin(), tracks.end()); + std::vector allLabels; + + int totalTracks = allTracks.size(); + int goodTracks = 0; + int fakeTracks = 0; + + if (mIsMC) { + allLabels.assign(labels.begin(), labels.end()); + for (const auto& label : allLabels) { + if (label.isFake()) { + ++fakeTracks; + } else { + ++goodTracks; + } + } + } + + LOGP(info, "=== Tracking Summary ==="); + LOGP(info, "Total tracks reconstructed: {}", totalTracks); + LOGP(info, "Good tracks: {} ({:.1f}%)", goodTracks, totalTracks > 0 ? 100.0 * goodTracks / totalTracks : 0); + LOGP(info, "Fake tracks: {} ({:.1f}%)", fakeTracks, totalTracks > 0 ? 100.0 * fakeTracks / totalTracks : 0); + + const auto& rofView = timeFrame.getROFOverlapTableView(); + const auto& clockLayer = rofView.getClockLayer(); + const int clockLayerId = rofView.getClock(); + const int64_t anchorBC = timeFrame.getTFAnchorIR().toLong(); + + int highestROF = static_cast(clockLayer.mNROFsTF); + for (const auto& trc : allTracks) { + highestROF = std::max(highestROF, static_cast(clockLayer.getROF(trc.getTimeStamp()))); + } + for (const auto& vtx : timeFrame.getPrimaryVertices()) { + highestROF = std::max(highestROF, static_cast(clockLayer.getROF(vtx.getTimeStamp().lower()))); + } + + std::vector allTrackROFs(highestROF); + for (size_t iROF = 0; iROF < allTrackROFs.size(); ++iROF) { + auto& rof = allTrackROFs[iROF]; + o2::InteractionRecord ir; + ir.setFromLong(anchorBC + static_cast(clockLayer.getROFStartInBC(iROF))); + rof.setBCData(ir); + rof.setROFrame(iROF); + rof.setFirstEntry(0); + rof.setNEntries(0); + } + + std::vector rofEntries(highestROF + 1, 0); + for (const auto& trc : allTracks) { + const int rof = static_cast(clockLayer.getROF(trc.getTimeStamp())); + if (rof >= 0 && rof < highestROF) { + ++rofEntries[rof]; + } + } + std::exclusive_scan(rofEntries.begin(), rofEntries.end(), rofEntries.begin(), 0); + + std::vector irFrames; + irFrames.reserve(allTrackROFs.size()); + const auto& maskView = timeFrame.getROFMaskView(); + const auto rofLenMinus1 = clockLayer.mROFLength > 0 ? clockLayer.mROFLength - 1 : 0; + for (size_t iROF = 0; iROF < allTrackROFs.size(); ++iROF) { + allTrackROFs[iROF].setFirstEntry(rofEntries[iROF]); + allTrackROFs[iROF].setNEntries(rofEntries[iROF + 1] - rofEntries[iROF]); + if (maskView.isROFEnabled(clockLayerId, static_cast(iROF))) { + const auto& bcStart = allTrackROFs[iROF].getBCData(); + auto& irFrame = irFrames.emplace_back(bcStart, bcStart + rofLenMinus1); + irFrame.info = allTrackROFs[iROF].getNEntries(); + } + } + + pc.outputs().snapshot(o2::framework::Output{"TRK", "TRACKS", 0}, allTracks); + pc.outputs().snapshot(o2::framework::Output{"TRK", "TRACKSROF", 0}, allTrackROFs); + pc.outputs().snapshot(o2::framework::Output{"TRK", "IRFRAMES", 0}, irFrames); + if (mIsMC) { + pc.outputs().snapshot(o2::framework::Output{"TRK", "TRACKSMCTR", 0}, allLabels); + } + + LOGP(info, "TRK pushed {} tracks in {} ROFs and {} IR frames{}", + allTracks.size(), allTrackROFs.size(), irFrames.size(), + mIsMC ? " (with MC labels)" : ""); + + timeFrame.wipe(); + }; + +#ifdef TRK_HAS_GPU_TRACKING + if (useGPU) { + o2::trk::TimeFrameGPU<11> timeFrame; + o2::its::TrackerTraitsGPU<11> itsTrackerTraits; + if (!mGPUAllocator) { + mGPUAllocator = std::make_shared(); + } + timeFrame.setFrameworkAllocator(mGPUAllocator.get()); + runTracking(timeFrame, itsTrackerTraits); + } else +#endif + { + o2::trk::TimeFrame<11> timeFrame; + o2::its::TrackerTraits<11> itsTrackerTraits; + runTracking(timeFrame, itsTrackerTraits); + } + + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(framework::QuitRequest::Me); + + mTimer.Stop(); + LOGP(info, "CPU Reconstruction time for this TF {} s (cpu), {} s (wall)", mTimer.CpuTime() - cput, mTimer.RealTime() - realt); +} + +void TrackerDPL::endOfStream(EndOfStreamContext& ec) +{ + LOGF(info, "TRK CA-Tracker total timing: Cpu: %.3e Real: %.3e s in %d slots", mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); +} + +DataProcessorSpec getTrackerSpec(bool useMC, const std::string& hitRecoConfig, const std::string& clusterRecoConfig, o2::gpu::gpudatatypes::DeviceType dType) +{ + std::vector inputs; + std::vector outputs; + outputs.emplace_back("TRK", "TRACKS", 0, Lifetime::Timeframe); + outputs.emplace_back("TRK", "TRACKSROF", 0, Lifetime::Timeframe); + outputs.emplace_back("TRK", "IRFRAMES", 0, Lifetime::Timeframe); + auto ggRequest = std::make_shared(false, // orbitResetTime + false, // GRPECS=true + false, // GRPLHCIF + false, // GRPMagField + false, // askMatLUT + o2::base::GRPGeomRequest::None, // geometry, but ignored until it will be put in the CCDB + inputs, + true); + + if (!hitRecoConfig.empty()) { + if (useMC) { + outputs.emplace_back("TRK", "TRACKSMCTR", 0, Lifetime::Timeframe); + } + return DataProcessorSpec{ + "trk-hits-tracker", + {}, + outputs, + AlgorithmSpec{adaptFromTask(ggRequest, + useMC, + hitRecoConfig, + clusterRecoConfig, + dType)}, + Options{ConfigParamSpec{"max-loops", VariantType::Int, 1, {"max number of loops"}} +#ifdef O2_WITH_ACTS + , + {"useACTS", o2::framework::VariantType::Bool, false, {"Use ACTS for tracking"}} +#endif + }}; + } + + inputs.emplace_back("dummy", "TRK", "DUMMY", 0, Lifetime::Timeframe); + + if (!clusterRecoConfig.empty()) { + inputs.pop_back(); + constexpr int nLayers{11}; + for (int iLayer = 0; iLayer < nLayers; ++iLayer) { + inputs.emplace_back(std::format("compClusters_{}", iLayer), "TRK", "COMPCLUSTERS", iLayer, Lifetime::Timeframe); + inputs.emplace_back(std::format("patterns_{}", iLayer), "TRK", "PATTERNS", iLayer, Lifetime::Timeframe); + inputs.emplace_back(std::format("ROframes_{}", iLayer), "TRK", "CLUSTERSROF", iLayer, Lifetime::Timeframe); + if (useMC) { + inputs.emplace_back(std::format("trkmclabels_{}", iLayer), "TRK", "CLUSTERSMCTR", iLayer, Lifetime::Timeframe); + } + } + } + + if (useMC) { + outputs.emplace_back("TRK", "TRACKSMCTR", 0, Lifetime::Timeframe); + } + + return DataProcessorSpec{ + "trk-tracker", + inputs, + outputs, + AlgorithmSpec{adaptFromTask(ggRequest, + useMC, + hitRecoConfig, + clusterRecoConfig, + dType)}, + Options{}}; +} + +} // namespace trk +} // namespace o2 diff --git a/Detectors/Upgrades/ALICE3/GlobalReconstruction/workflow/src/alice3-global-reconstruction-workflow.cxx b/Detectors/Upgrades/ALICE3/GlobalReconstruction/workflow/src/alice3-global-reconstruction-workflow.cxx new file mode 100644 index 0000000000000..7e9950f4def2e --- /dev/null +++ b/Detectors/Upgrades/ALICE3/GlobalReconstruction/workflow/src/alice3-global-reconstruction-workflow.cxx @@ -0,0 +1,65 @@ +// 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. + +#include "ALICE3GlobalReconstructionWorkflow/RecoWorkflow.h" +#include "CommonUtils/ConfigurableParam.h" + +#include "Framework/CallbacksPolicy.h" +#include "Framework/ConfigContext.h" +#include "Framework/CompletionPolicyHelpers.h" + +#include +#include + +using namespace o2::framework; + +void customize(std::vector& policies) +{ + // o2::raw::HBFUtilsInitializer::addNewTimeSliceCallback(policies); +} + +void customize(std::vector& policies) +{ + policies.push_back(CompletionPolicyHelpers::consumeWhenAllOrdered(".*(?:TRK|trk).*[W,w]riter.*")); +} + +void customize(std::vector& workflowOptions) +{ + std::vector options{ + {"disable-root-output", VariantType::Bool, false, {"do not write output root files"}}, + {"disable-mc", VariantType::Bool, false, {"disable MC propagation even if available"}}, + {"tracking-from-hits-config", VariantType::String, "", {"JSON file with tracking from hits configuration"}}, + {"tracking-from-clusters-config", VariantType::String, "", {"JSON file with tracking from clusters configuration"}}, + {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}, + {"gpu-device", VariantType::Int, 1, {"use gpu device: CPU=1,CUDA=2,HIP=3 (default: CPU)"}}}; + std::swap(workflowOptions, options); +} + +#include "Framework/runDataProcessing.h" +#include "Framework/Logger.h" + +WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) +{ + auto useMC = !configcontext.options().get("disable-mc"); + auto hitRecoConfig = configcontext.options().get("tracking-from-hits-config"); + auto clusterRecoConfig = configcontext.options().get("tracking-from-clusters-config"); + auto gpuDevice = static_cast(configcontext.options().get("gpu-device")); + auto disableRootOutput = configcontext.options().get("disable-root-output"); + o2::conf::ConfigurableParam::updateFromString(configcontext.options().get("configKeyValues")); + + if (hitRecoConfig.empty() && clusterRecoConfig.empty()) { + throw std::invalid_argument("no reconstruction input configured: provide either --tracking-from-hits-config or --tracking-from-clusters-config "); + } + + o2::conf::ConfigurableParam::writeINI("o2alice3globalrecoflow_configuration.ini"); + + return o2::trk::global_reco_workflow::getWorkflow(useMC, hitRecoConfig, clusterRecoConfig, disableRootOutput, gpuDevice); +} diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/CMakeLists.txt b/Detectors/Upgrades/ALICE3/TRK/reconstruction/CMakeLists.txt index b8cb6a88f7163..45ce53ba7c3a3 100644 --- a/Detectors/Upgrades/ALICE3/TRK/reconstruction/CMakeLists.txt +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/CMakeLists.txt @@ -15,28 +15,16 @@ endif() o2_add_library(TRKReconstruction TARGETVARNAME targetName - SOURCES src/TimeFrame.cxx - src/Clusterer.cxx + SOURCES src/Clusterer.cxx $<$:src/ClustererACTS.cxx> - $<$:src/TrackerACTS.cxx> PUBLIC_LINK_LIBRARIES - O2::ITStracking - O2::GPUCommon Microsoft.GSL::GSL - O2::CommonConstants O2::DataFormatsITSMFT O2::DataFormatsTRK O2::SimulationDataFormat - O2::ITSBase - O2::ITSReconstruction - O2::ITSMFTReconstruction - O2::DataFormatsITS - O2::TRKSimulation + O2::TRKBase nlohmann_json::nlohmann_json - ${actsTarget} - PRIVATE_LINK_LIBRARIES - O2::Steer - TBB::tbb) + ${actsTarget}) if(Acts_FOUND) target_compile_definitions(${targetName} PUBLIC O2_WITH_ACTS) diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/Clusterer.h b/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/Clusterer.h index bcd95155f533f..3d30eb5068efe 100644 --- a/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/Clusterer.h +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/Clusterer.h @@ -28,6 +28,7 @@ #include "SimulationDataFormat/MCCompLabel.h" #include "SimulationDataFormat/MCTruthContainer.h" #include "TRKBase/Specs.h" +#include "MathUtils/Cartesian.h" #include #include #include @@ -48,6 +49,7 @@ class Clusterer using Digit = o2::itsmft::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; @@ -166,7 +168,12 @@ class Clusterer std::vector& patterns, std::vector& clusterROFs, const ConstDigitTruth* digitLabels = nullptr, - ClusterTruth* clusterLabels = nullptr); + ClusterTruth* clusterLabels = nullptr, + gsl::span digMC2ROFs = {}, + std::vector* clusterMC2ROFs = nullptr); + + static o2::math_utils::Point3D getClusterLocalCoordinates(const Cluster& cluster, const uint8_t* patt, + float yPlaneMLOT = 0.f) noexcept; protected: int mNHugeClus = 0; diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/ClustererACTS.h b/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/ClustererACTS.h index 5d68193e5e375..37a148aa78afb 100644 --- a/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/ClustererACTS.h +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/ClustererACTS.h @@ -35,7 +35,9 @@ class ClustererACTS : public Clusterer std::vector& patterns, std::vector& clusterROFs, const ConstDigitTruth* digitLabels = nullptr, - ClusterTruth* clusterLabels = nullptr) override; + ClusterTruth* clusterLabels = nullptr, + gsl::span digMC2ROFs = {}, + std::vector* clusterMC2ROFs = nullptr) override; private: }; diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/TimeFrame.h b/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/TimeFrame.h deleted file mode 100644 index 005237fe28839..0000000000000 --- a/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/TimeFrame.h +++ /dev/null @@ -1,68 +0,0 @@ -// 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 TimeFrame.h -/// \brief TRK TimeFrame class derived from ITS TimeFrame -/// - -#ifndef ALICEO2_TRK_TIMEFRAME_H -#define ALICEO2_TRK_TIMEFRAME_H - -#include "ITStracking/TimeFrame.h" -#include "ITStracking/Constants.h" -#include "ITStracking/Configuration.h" -#include "SimulationDataFormat/MCCompLabel.h" -#include -#include -#include -#include - -#include - -class TTree; - -namespace o2 -{ -namespace trk -{ -class GeometryTGeo; - -/// TRK TimeFrame class that extends ITS TimeFrame functionality -/// This allows for customization of tracking algorithms specific to the TRK detector -template -class TimeFrame : public o2::its::TimeFrame -{ - public: - TimeFrame() = default; - ~TimeFrame() override = default; - - /// Override methods if needed for TRK-specific behavior - /// For now, we inherit all functionality from ITS TimeFrame - - /// Process hits from TTree to initialize ROFs - /// \param hitsTree Tree containing TRK hits - /// \param gman TRK geometry manager instance - /// \param config Configuration parameters for hit reconstruction - int loadROFsFromHitTree(TTree* hitsTree, GeometryTGeo* gman, const nlohmann::json& config); - - /// Add primary vertices from MC headers for each ROF - /// \param mcHeaderTree Tree containing MC event headers - /// \param nRofs Number of ROFs (Read-Out Frames) - /// \param nEvents Number of events to process - /// \param inROFpileup Number of events per ROF - /// \param rofLength ROF length in BCs (must match what was used in loadROFsFromHitTree) - void getPrimaryVerticesFromMC(TTree* mcHeaderTree, int nRofs, Long64_t nEvents, int inROFpileup, uint32_t rofLength = 198); -}; - -} // namespace trk -} // namespace o2 - -#endif // ALICEO2_TRK_TIMEFRAME_H diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/Clusterer.cxx b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/Clusterer.cxx index e0d689e4db5ed..d60d6900657ba 100644 --- a/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/Clusterer.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/Clusterer.cxx @@ -14,6 +14,7 @@ #include "TRKReconstruction/Clusterer.h" #include "TRKBase/GeometryTGeo.h" +#include "TRKBase/SegmentationChip.h" #include #include @@ -21,6 +22,51 @@ namespace o2::trk { +//__________________________________________________ +o2::math_utils::Point3D Clusterer::getClusterLocalCoordinates(const Cluster& cluster, const uint8_t* patt, + float yPlaneMLOT) noexcept +{ + const uint8_t rowSpan = *patt++; + const uint8_t colSpan = *patt++; + const int nBytes = (rowSpan * colSpan + 7) / 8; + + float cogDr{0.f}, cogDc{0.f}; + int nPix{0}, pixIdx{0}; + for (int ib = 0; ib < nBytes; ib++) { + const uint8_t byte = *patt++; + for (int bit = 7; bit >= 0 && pixIdx < rowSpan * colSpan; bit--, pixIdx++) { + if (byte & (1 << bit)) { + cogDr += pixIdx / colSpan; + cogDc += pixIdx % colSpan; + nPix++; + } + } + } + if (nPix > 1) { + cogDr /= nPix; + cogDc /= nPix; + } + + float x{0.f}, y{0.f}, z{0.f}; + SegmentationChip::detectorToLocalUnchecked(cluster.row, cluster.col, x, z, + cluster.subDetID, cluster.layer, cluster.disk); + + const float pitchRow = (cluster.subDetID == 0) ? SegmentationChip::PitchRowVD : SegmentationChip::PitchRowMLOT; + const float pitchCol = (cluster.subDetID == 0) ? SegmentationChip::PitchColVD : SegmentationChip::PitchColMLOT; + x -= cogDr * pitchRow; + z += cogDc * pitchCol; + + if (cluster.subDetID == 0) { + auto cv = SegmentationChip::flatToCurved(cluster.layer, x, 0.f); + x = cv.X(); + y = cv.Y(); + } else { + y = yPlaneMLOT; + } + + return {x, y, z}; +} + //__________________________________________________ void Clusterer::process(gsl::span digits, gsl::span digitROFs, @@ -28,7 +74,9 @@ void Clusterer::process(gsl::span digits, std::vector& patterns, std::vector& clusterROFs, const ConstDigitTruth* digitLabels, - ClusterTruth* clusterLabels) + ClusterTruth* clusterLabels, + gsl::span digMC2ROFs, + std::vector* clusterMC2ROFs) { if (!mThread) { mThread = std::make_unique(this); @@ -79,6 +127,13 @@ void Clusterer::process(gsl::span digits, clusterROFs.emplace_back(inROF.getBCData(), inROF.getROFrame(), outFirst, static_cast(clusters.size()) - outFirst); } + + if (clusterMC2ROFs && !digMC2ROFs.empty()) { + clusterMC2ROFs->reserve(clusterMC2ROFs->size() + digMC2ROFs.size()); + for (const auto& in : digMC2ROFs) { + clusterMC2ROFs->emplace_back(in.eventRecordID, in.rofRecordID, in.minROF, in.maxROF); + } + } } //__________________________________________________ diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/ClustererACTS.cxx b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/ClustererACTS.cxx index b764fcdd1cd79..2dbf56ae610e3 100644 --- a/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/ClustererACTS.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/ClustererACTS.cxx @@ -162,7 +162,9 @@ void ClustererACTS::process(gsl::span digits, std::vector& patterns, std::vector& clusterROFs, const ConstDigitTruth* digitLabels, - ClusterTruth* clusterLabels) + ClusterTruth* clusterLabels, + gsl::span digMC2ROFs, + std::vector* clusterMC2ROFs) { if (!mThread) { mThread = std::make_unique(this); diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TimeFrame.cxx b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TimeFrame.cxx deleted file mode 100644 index 957560aea8cae..0000000000000 --- a/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TimeFrame.cxx +++ /dev/null @@ -1,225 +0,0 @@ -// 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 TimeFrame.cxx -/// \brief TRK TimeFrame implementation -/// - -#include "TRKReconstruction/TimeFrame.h" -#include "TRKSimulation/Hit.h" -#include "TRKBase/GeometryTGeo.h" -#include "Framework/Logger.h" -#include "SimulationDataFormat/MCEventHeader.h" -#include -#include -#include -#include - -using o2::its::clearResizeBoundedVector; - -namespace o2::trk -{ - -template -int TimeFrame::loadROFsFromHitTree(TTree* hitsTree, GeometryTGeo* gman, const nlohmann::json& config) -{ - constexpr std::array startLayer{0, 3}; - const Long64_t nEvents = hitsTree->GetEntries(); - - gman->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L) | o2::math_utils::bit2Mask(o2::math_utils::TransformType::L2G)); - - std::vector* trkHit = nullptr; - hitsTree->SetBranchAddress("TRKHit", &trkHit); - - const int inROFpileup{config.contains("inROFpileup") ? config["inROFpileup"].get() : 1}; - - // Calculate number of ROFs - const int nRofs = (nEvents + inROFpileup - 1) / inROFpileup; - - // Set up ROF timing for all layers (no staggering in TRK simulation, all layers read out together) - constexpr uint32_t rofLength = 198; // ROF length in BC - o2::its::ROFOverlapTable overlapTable; - for (int iLayer = 0; iLayer < NLayers; ++iLayer) { - overlapTable.defineLayer(iLayer, nRofs, rofLength, 0, 0, 0); - } - overlapTable.init(); - this->setROFOverlapTable(overlapTable); - - // Set up the vertex lookup table timing (pre-allocate, vertices will be filled later) - o2::its::ROFVertexLookupTable vtxLookupTable; - for (int iLayer = 0; iLayer < NLayers; ++iLayer) { - vtxLookupTable.defineLayer(iLayer, nRofs, rofLength, 0, 0, 0); - } - vtxLookupTable.init(); // pre-allocate without vertices - this->setROFVertexLookupTable(vtxLookupTable); - - // Reset and prepare ROF data structures - for (int iLayer{0}; iLayer < NLayers; ++iLayer) { - this->mMinR[iLayer] = std::numeric_limits::max(); - this->mMaxR[iLayer] = std::numeric_limits::lowest(); - this->mROFramesClusters[iLayer].clear(); - this->mROFramesClusters[iLayer].resize(nRofs + 1, 0); - this->mUnsortedClusters[iLayer].clear(); - this->mTrackingFrameInfo[iLayer].clear(); - this->mClusterExternalIndices[iLayer].clear(); - } - - // Pre-count hits to reserve memory efficiently - std::array clusterCountPerLayer{}; - for (Long64_t iEvent = 0; iEvent < nEvents; ++iEvent) { - hitsTree->GetEntry(iEvent); - for (const auto& hit : *trkHit) { - if (gman->getDisk(hit.GetDetectorID()) != -1) { - continue; // skip non-barrel hits - } - int subDetID = gman->getSubDetID(hit.GetDetectorID()); - const int layer = startLayer[subDetID] + gman->getLayer(hit.GetDetectorID()); - if (layer >= NLayers) { - continue; - } - ++clusterCountPerLayer[layer]; - } - } - - // Reserve memory for all layers (mClusterSize is now per-layer) - for (int iLayer{0}; iLayer < NLayers; ++iLayer) { - this->mUnsortedClusters[iLayer].reserve(clusterCountPerLayer[iLayer]); - this->mTrackingFrameInfo[iLayer].reserve(clusterCountPerLayer[iLayer]); - this->mClusterExternalIndices[iLayer].reserve(clusterCountPerLayer[iLayer]); - clearResizeBoundedVector(this->mClusterSize[iLayer], clusterCountPerLayer[iLayer], this->mMemoryPool.get()); - } - - std::array resolution{0.001, 0.001, 0.001, 0.001, 0.004, 0.004, 0.004, 0.004, 0.004, 0.004, 0.004}; - if (config["geometry"]["pitch"].size() == static_cast(NLayers)) { - for (size_t iLayer{0}; iLayer < config["geometry"]["pitch"].size(); ++iLayer) { - LOGP(info, "Setting resolution for layer {} from config", iLayer); - LOGP(info, "Layer {} pitch {} cm", iLayer, config["geometry"]["pitch"][iLayer].get()); - resolution[iLayer] = config["geometry"]["pitch"][iLayer].get() / std::sqrt(12.f); - } - } - LOGP(info, "Number of active parts in VD: {}", gman->getNumberOfActivePartsVD()); - - // One shared MC label container for all layers - auto* labels = new dataformats::MCTruthContainer(); - - int hitCounter{0}; - int iRof{0}; // Current ROF index - for (Long64_t iEvent = 0; iEvent < nEvents; ++iEvent) { - hitsTree->GetEntry(iEvent); - - for (auto& hit : *trkHit) { - if (gman->getDisk(hit.GetDetectorID()) != -1) { - continue; // skip non-barrel hits for this test - } - int subDetID = gman->getSubDetID(hit.GetDetectorID()); - const int layer = startLayer[subDetID] + gman->getLayer(hit.GetDetectorID()); - - float alpha{0.f}; - o2::math_utils::Point3D gloXYZ; - o2::math_utils::Point3D trkXYZ; - float r{0.f}; - if (layer >= NLayers) { - continue; - } - if (layer >= 3) { - int chipID = hit.GetDetectorID(); - alpha = gman->getSensorRefAlphaMLOT(chipID); - const o2::math_utils::Transform3D& l2g = gman->getMatrixL2G(chipID); - auto locXYZ = l2g ^ (hit.GetPos()); - locXYZ.SetX(locXYZ.X() + gRandom->Gaus(0.0, resolution[layer])); - locXYZ.SetZ(locXYZ.Z() + gRandom->Gaus(0.0, resolution[layer])); - gloXYZ = gman->getMatrixL2G(chipID) * locXYZ; - trkXYZ = gman->getMatrixT2L(chipID - gman->getNumberOfActivePartsVD()) ^ locXYZ; - r = std::hypot(gloXYZ.X(), gloXYZ.Y()); - } else { - const auto& hitPos = hit.GetPos(); - r = std::hypot(hitPos.X(), hitPos.Y()); - alpha = std::atan2(hitPos.Y(), hitPos.X()) + gRandom->Gaus(0.0, resolution[layer] / r); - o2::math_utils::bringTo02Pi(alpha); - gloXYZ.SetX(r * std::cos(alpha)); - gloXYZ.SetY(r * std::sin(alpha)); - gloXYZ.SetZ(hitPos.Z() + gRandom->Gaus(0.0, resolution[layer])); - trkXYZ.SetX(r); - trkXYZ.SetY(0.f); - trkXYZ.SetZ(gloXYZ.Z()); - } - this->mMinR[layer] = std::min(this->mMinR[layer], r); - this->mMaxR[layer] = std::max(this->mMaxR[layer], r); - this->addTrackingFrameInfoToLayer(layer, gloXYZ.x(), gloXYZ.y(), gloXYZ.z(), trkXYZ.x(), alpha, - std::array{trkXYZ.y(), trkXYZ.z()}, - std::array{resolution[layer] * resolution[layer], 0., resolution[layer] * resolution[layer]}); - /// Rotate to the global frame - const int clusterIdxInLayer = this->mUnsortedClusters[layer].size(); - this->addClusterToLayer(layer, gloXYZ.x(), gloXYZ.y(), gloXYZ.z(), clusterIdxInLayer); - this->addClusterExternalIndexToLayer(layer, hitCounter); - MCCompLabel label{hit.GetTrackID(), static_cast(iEvent), 0}; - labels->addElement(hitCounter, label); - this->mClusterSize[layer][clusterIdxInLayer] = 1; - hitCounter++; - } - trkHit->clear(); - - // Update ROF structure when we complete an ROF or reach the last event - if ((iEvent + 1) % inROFpileup == 0 || iEvent == nEvents - 1) { - iRof++; - for (unsigned int iLayer{0}; iLayer < this->mUnsortedClusters.size(); ++iLayer) { - this->mROFramesClusters[iLayer][iRof] = this->mUnsortedClusters[iLayer].size(); // effectively calculating an exclusive sum - } - } - } - - // Set the shared labels container for all layers - for (int iLayer = 0; iLayer < NLayers; ++iLayer) { - this->mClusterLabels[iLayer] = labels; - } - - return nRofs; -} - -template -void TimeFrame::getPrimaryVerticesFromMC(TTree* mcHeaderTree, int nRofs, Long64_t nEvents, int inROFpileup, uint32_t rofLength) -{ - auto mcheader = new o2::dataformats::MCEventHeader; - mcHeaderTree->SetBranchAddress("MCEventHeader.", &mcheader); - - this->mPrimaryVertices.clear(); - - int iRof{0}; - for (Long64_t iEvent = 0; iEvent < nEvents; ++iEvent) { - mcHeaderTree->GetEntry(iEvent); - o2::its::Vertex vertex; - vertex.setXYZ(mcheader->GetX(), mcheader->GetY(), mcheader->GetZ()); - vertex.setNContributors(30); - vertex.setChi2(0.f); - - // Set proper BC timestamp for vertex-ROF compatibility - // The vertex timestamp is set to the center of its ROF with half-ROF as error - const uint32_t rofCenter = static_cast(rofLength * iRof + rofLength / 2); - const uint16_t rofHalf = static_cast(rofLength / 2); - vertex.setTimeStamp({rofCenter, rofHalf}); - - LOGP(debug, "ROF {}: Added primary vertex at ({}, {}, {}) with BC timestamp [{}, +/-{}]", - iRof, mcheader->GetX(), mcheader->GetY(), mcheader->GetZ(), rofCenter, rofHalf); - this->addPrimaryVertex(vertex); - if ((iEvent + 1) % inROFpileup == 0 || iEvent == nEvents - 1) { - iRof++; - } - } - this->mMultiplicityCutMask.resetMask(1u); /// all ROFs are valid with MC primary vertices. - - // Update the vertex lookup table with the newly added vertices - this->updateROFVertexLookupTable(); -} - -// Explicit template instantiation for TRK with 11 layers -template class TimeFrame<11>; - -} // namespace o2::trk diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/CMakeLists.txt b/Detectors/Upgrades/ALICE3/TRK/workflow/CMakeLists.txt index 42402fe6b62dc..e3309d78f47ea 100644 --- a/Detectors/Upgrades/ALICE3/TRK/workflow/CMakeLists.txt +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/CMakeLists.txt @@ -15,8 +15,6 @@ o2_add_library(TRKWorkflow src/DigitWriterSpec.cxx src/ClustererSpec.cxx src/ClusterWriterSpec.cxx - src/TrackerSpec.cxx - src/TrackWriterSpec.cxx src/RecoWorkflow.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2::GPUWorkflow @@ -35,5 +33,4 @@ o2_add_executable(reco-workflow COMPONENT_NAME alice3-trk PUBLIC_LINK_LIBRARIES O2::TRKWorkflow O2::TRKSimulation - O2::TRKReconstruction - O2::ITStracking) + O2::TRKReconstruction) diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/README.md b/Detectors/Upgrades/ALICE3/TRK/workflow/README.md index 1cdce15b72726..2afb599319217 100644 --- a/Detectors/Upgrades/ALICE3/TRK/workflow/README.md +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/README.md @@ -1,130 +1,11 @@ # TRK Reconstruction Workflow -This document describes how to run the TRK (ALICE 3 Tracker) reconstruction workflow and provides examples of configuration files. +This workflow handles TRK-local reconstruction devices such as digit reading and clusterization. -## Overview - -The TRK reconstruction workflow performs track reconstruction from simulated hits, producing reconstructed tracks with MC truth labels. The workflow currently supports the track reconstruction from hits using the Cellular Automaton (CA) algorithm. The ouput is stored to a ROOT file for offline analysis (example of QA macro provided in `macros/test/CheckTracksCA.C`). - -## Quick Start - -### Basic Command +## Basic Command ```bash -o2-alice3-trk-reco-workflow --tracking-from-hits-config config_tracker.json -b -``` - -### Command Line Options - -- `--tracking-from-hits-config `: Path to tracking configuration JSON file (required) -- `-b`: Batch mode (no GUI) -- `--disable-root-output`: Skip writing tracks to ROOT file -- `--help`: Show all available options - -## Configuration File - -The tracking configuration is provided via a JSON file that specifies: -1. Input file paths -2. Geometry parameters (magnetic field, detector pitch) -3. Tracking algorithm parameters (can specify multiple iterations) - -### Example Configuration (`config_tracker.json`) - -```json -{ - "inputfiles": { - "hits": "o2sim_HitsTRK.root", - "geometry": "o2sim_geometry.root", - "mcHeader": "o2sim_MCHeader.root", - "kinematics": "o2sim_Kine.root" - }, - "geometry": { - "bz": 5.0, - "pitch": [0.001, 0.001, 0.001, 0.001, 0.004, 0.004, 0.004, 0.004, 0.004, 0.004, 0.004] - }, - "trackingparams": [{ - "NLayers": 11, - "DeltaROF": 0, - "LayerZ": [25.1, 25.1, 25.1, 64.2, 64.2, 64.2, 64.2, 64.2, 128.5, 128.5, 128.5], - "LayerRadii": [0.5, 1.2, 2.5, 7.05, 9.05, 12.05, 20.05, 30.05, 45.05, 60.5, 80.05], - "LayerxX0": [0.001, 0.001, 0.001, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01], - "LayerResolution": [0.0003, 0.0003, 0.0003, 0.0003, 0.0012, 0.0012, 0.0012, 0.0012, 0.0012, 0.0012, 0.0012], - "SystErrorY2": [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], - "SystErrorZ2": [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], - "ZBins": 256, - "PhiBins": 128, - "nROFsPerIterations": -1, - "UseDiamond": false, - "Diamond": [0.0, 0.0, 0.0], - "AllowSharingFirstCluster": false, - "ClusterSharing": 0, - "MinTrackLength": 7, - "NSigmaCut": 10, - "PVres": 0.01, - "TrackletMinPt": 0.1, - "TrackletsPerClusterLimit": 2.0, - "CellDeltaTanLambdaSigma": 0.007, - "CellsPerClusterLimit": 2.0, - "MaxChi2ClusterAttachment": 60.0, - "MaxChi2NDF": 30.0, - "ReseedIfShorter": 6, - "MinPt": [0.0, 0.0, 0.0, 0.0], - "StartLayerMask": 4095, - "RepeatRefitOut": false, - "ShiftRefToCluster": true, - "FindShortTracks": false, - "PerPrimaryVertexProcessing": false, - "SaveTimeBenchmarks": false, - "DoUPCIteration": false, - "FataliseUponFailure": true, - "UseTrackFollower": true, - "UseTrackFollowerTop": false, - "UseTrackFollowerBot": false, - "UseTrackFollowerMix": true, - "TrackFollowerNSigmaCutZ": 1.0, - "TrackFollowerNSigmaCutPhi": 1.0, - "createArtefactLabels": false, - "PrintMemory": false, - "DropTFUponFailure": false - }] -} +o2-alice3-trk-reco-workflow -b ``` -Note that the `trackingparams` field can contain multiple sets of parameters for different iterations of the tracking algorithm. The example above shows a single iteration with 11 layers and it is **not** optimized. - -## Complete Workflow Example -### 1. Run Simulation - -First, generate simulation data: - -```bash -o2-sim-serial-run5 -n 200 -g pythia8hi -m TRK --configKeyValues "Diamond.width[0]=0.01;Diamond.width[1]=0.01;Diamond.width[2]=5;TRKBase.layoutML=kTurboStaves;TRKBase.layoutOT=kStaggered;" -``` - -This produces, among other files: -- `o2sim_HitsTRK.root` -- `o2sim_geometry.root` -- `o2sim_MCHeader.root` -- `o2sim_Kine.root` -That will be used by the reconstruction as currently we do not have clusters. - -### 2. Run Reconstruction - -Execute the tracking workflow: - -```bash -o2-alice3-trk-reco-workflow --tracking-from-hits-config config_tracker.json -b -``` - -This produces: -- `o2trac_trk.root`: Reconstructed tracks with MC labels - -### 3. Run Quality Assurance - -Analyze the tracking performance: - -```bash -root -l -.L CheckTracksCA.C+ -CheckTracksCA("o2trac_trk.root", "o2sim_Kine.root", "o2sim_HitsTRK.root", "trk_qa_output.root") -``` +Use `o2-alice3-global-reconstruction-reco-workflow` for ALICE 3 tracking from hits. diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/RecoWorkflow.h b/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/RecoWorkflow.h index 7046955a20c2e..863c5deae7241 100644 --- a/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/RecoWorkflow.h +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/RecoWorkflow.h @@ -13,8 +13,6 @@ #define O2_TRK_RECOWORKFLOW_H #include "Framework/WorkflowSpec.h" -#include "GPUDataTypesConfig.h" -#include namespace o2::trk { @@ -22,12 +20,9 @@ namespace reco_workflow { o2::framework::WorkflowSpec getWorkflow(bool useMC, - const std::string& hitRecoConfig, bool upstreamDigits = false, bool upstreamClusters = false, - bool disableRootOutput = false, - bool useGPUWF = false, - o2::gpu::gpudatatypes::DeviceType dType = o2::gpu::gpudatatypes::DeviceType::CPU); + bool disableRootOutput = false); } } // namespace o2::trk diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/src/RecoWorkflow.cxx b/Detectors/Upgrades/ALICE3/TRK/workflow/src/RecoWorkflow.cxx index d10feb4214f38..02895f42ac094 100644 --- a/Detectors/Upgrades/ALICE3/TRK/workflow/src/RecoWorkflow.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/src/RecoWorkflow.cxx @@ -13,8 +13,6 @@ #include "TRKWorkflow/ClustererSpec.h" #include "TRKWorkflow/ClusterWriterSpec.h" #include "TRKWorkflow/DigitReaderSpec.h" -#include "TRKWorkflow/TrackerSpec.h" -#include "TRKWorkflow/TrackWriterSpec.h" #include "Framework/CCDBParamSpec.h" #include @@ -23,12 +21,9 @@ namespace o2::trk::reco_workflow { framework::WorkflowSpec getWorkflow(bool useMC, - const std::string& hitRecoConfig, bool upstreamDigits, bool upstreamClusters, - bool disableRootOutput, - bool useGPUWF, - o2::gpu::gpudatatypes::DeviceType dtype) + bool disableRootOutput) { framework::WorkflowSpec specs; @@ -43,14 +38,6 @@ framework::WorkflowSpec getWorkflow(bool useMC, specs.emplace_back(o2::trk::getClusterWriterSpec(useMC)); } - if (!hitRecoConfig.empty()) { - LOGP(info, "Using hit reco config from file {}", hitRecoConfig); - specs.emplace_back(o2::trk::getTrackerSpec(useMC, hitRecoConfig, dtype)); - if (!disableRootOutput) { - specs.emplace_back(o2::trk::getTrackWriterSpec(useMC)); - } - } - return specs; } diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackerSpec.cxx b/Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackerSpec.cxx deleted file mode 100644 index c9d793a3ec78f..0000000000000 --- a/Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackerSpec.cxx +++ /dev/null @@ -1,439 +0,0 @@ -// 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. - -#include -#include - -#include "DetectorsBase/GeometryManager.h" -#include "ITStracking/TimeFrame.h" -#include "ITStracking/Configuration.h" -#include "Field/MagneticField.h" -#include "Field/MagFieldParam.h" -#include "Framework/ControlService.h" -#include "Framework/ConfigParamRegistry.h" -#include "Framework/CCDBParamSpec.h" -#include "SimulationDataFormat/MCEventHeader.h" -#include "SimulationDataFormat/MCCompLabel.h" -#include "TRKBase/GeometryTGeo.h" -#include "TRKBase/SegmentationChip.h" -#include "TRKSimulation/Hit.h" -#include "TRKReconstruction/TimeFrame.h" -#include "TRKWorkflow/TrackerSpec.h" -#include - -#ifdef O2_WITH_ACTS -#include "TRKReconstruction/TrackerACTS.h" -#endif - -#include -#include - -namespace o2 -{ -using namespace framework; -namespace trk -{ - -TrackerDPL::TrackerDPL(std::shared_ptr gr, - bool isMC, - const std::string& hitRecoConfigFileName, - o2::gpu::gpudatatypes::DeviceType dType) -{ - if (!hitRecoConfigFileName.empty()) { - std::ifstream configFile(hitRecoConfigFileName); - mHitRecoConfig = nlohmann::json::parse(configFile); - } - - // mITSTrackingInterface.setTrackingMode(trMode); -} - -void TrackerDPL::init(InitContext& ic) -{ - // mTimer.Stop(); - // mTimer.Reset(); - // o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); - // mChainITS.reset(mRecChain->AddChain()); - // mITSTrackingInterface.setTraitsFromProvider(mChainITS->GetITSVertexerTraits(), - // mChainITS->GetITSTrackerTraits(), - // mChainITS->GetITSTimeframe()); - -#ifdef O2_WITH_ACTS - mUseACTS = ic.options().get("useACTS"); -#endif -} - -void TrackerDPL::stop() -{ - LOGF(info, "CPU Reconstruction total timing: Cpu: %.3e Real: %.3e s in %d slots", mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); -} - -std::vector TrackerDPL::createTrackingParamsFromConfig() -{ - std::vector trackingParams; - - if (!mHitRecoConfig.contains("trackingparams") || !mHitRecoConfig["trackingparams"].is_array()) { - LOGP(fatal, "No trackingparams field found in configuration or it is not an array. Returning empty vector."); - return trackingParams; - } - - for (const auto& paramConfig : mHitRecoConfig["trackingparams"]) { - o2::its::TrackingParameters params; - - // Parse integer parameters - if (paramConfig.contains("NLayers")) { - params.NLayers = paramConfig["NLayers"].get(); - } - if (paramConfig.contains("ZBins")) { - params.ZBins = paramConfig["ZBins"].get(); - } - if (paramConfig.contains("PhiBins")) { - params.PhiBins = paramConfig["PhiBins"].get(); - } - if (paramConfig.contains("ClusterSharing")) { - params.ClusterSharing = paramConfig["ClusterSharing"].get(); - } - if (paramConfig.contains("MinTrackLength")) { - params.MinTrackLength = paramConfig["MinTrackLength"].get(); - } - if (paramConfig.contains("ReseedIfShorter")) { - params.ReseedIfShorter = paramConfig["ReseedIfShorter"].get(); - } - if (paramConfig.contains("StartLayerMask")) { - params.StartLayerMask = paramConfig["StartLayerMask"].get(); - } - - // Parse float parameters - if (paramConfig.contains("NSigmaCut")) { - params.NSigmaCut = paramConfig["NSigmaCut"].get(); - } - if (paramConfig.contains("PVres")) { - params.PVres = paramConfig["PVres"].get(); - } - if (paramConfig.contains("TrackletMinPt")) { - params.TrackletMinPt = paramConfig["TrackletMinPt"].get(); - } - if (paramConfig.contains("CellDeltaTanLambdaSigma")) { - params.CellDeltaTanLambdaSigma = paramConfig["CellDeltaTanLambdaSigma"].get(); - } - if (paramConfig.contains("MaxChi2ClusterAttachment")) { - params.MaxChi2ClusterAttachment = paramConfig["MaxChi2ClusterAttachment"].get(); - } - if (paramConfig.contains("MaxChi2NDF")) { - params.MaxChi2NDF = paramConfig["MaxChi2NDF"].get(); - } - // if (paramConfig.contains("TrackFollowerNSigmaCutZ")) { - // params.TrackFollowerNSigmaCutZ = paramConfig["TrackFollowerNSigmaCutZ"].get(); - // } - // if (paramConfig.contains("TrackFollowerNSigmaCutPhi")) { - // params.TrackFollowerNSigmaCutPhi = paramConfig["TrackFollowerNSigmaCutPhi"].get(); - // } - - // Parse boolean parameters - if (paramConfig.contains("UseDiamond")) { - params.UseDiamond = paramConfig["UseDiamond"].get(); - } - if (paramConfig.contains("AllowSharingFirstCluster")) { - params.AllowSharingFirstCluster = paramConfig["AllowSharingFirstCluster"].get(); - } - if (paramConfig.contains("RepeatRefitOut")) { - params.RepeatRefitOut = paramConfig["RepeatRefitOut"].get(); - } - if (paramConfig.contains("ShiftRefToCluster")) { - params.ShiftRefToCluster = paramConfig["ShiftRefToCluster"].get(); - } - // if (paramConfig.contains("FindShortTracks")) { - // params.FindShortTracks = paramConfig["FindShortTracks"].get(); - // } - if (paramConfig.contains("PerPrimaryVertexProcessing")) { - params.PerPrimaryVertexProcessing = paramConfig["PerPrimaryVertexProcessing"].get(); - } - if (paramConfig.contains("SaveTimeBenchmarks")) { - params.SaveTimeBenchmarks = paramConfig["SaveTimeBenchmarks"].get(); - } - if (paramConfig.contains("DoUPCIteration")) { - params.DoUPCIteration = paramConfig["DoUPCIteration"].get(); - } - if (paramConfig.contains("FataliseUponFailure")) { - params.FataliseUponFailure = paramConfig["FataliseUponFailure"].get(); - } - // if (paramConfig.contains("UseTrackFollower")) { - // params.UseTrackFollower = paramConfig["UseTrackFollower"].get(); - // } - // if (paramConfig.contains("UseTrackFollowerTop")) { - // params.UseTrackFollowerTop = paramConfig["UseTrackFollowerTop"].get(); - // } - // if (paramConfig.contains("UseTrackFollowerBot")) { - // params.UseTrackFollowerBot = paramConfig["UseTrackFollowerBot"].get(); - // } - // if (paramConfig.contains("UseTrackFollowerMix")) { - // params.UseTrackFollowerMix = paramConfig["UseTrackFollowerMix"].get(); - // } - if (paramConfig.contains("createArtefactLabels")) { - params.createArtefactLabels = paramConfig["createArtefactLabels"].get(); - } - if (paramConfig.contains("PrintMemory")) { - params.PrintMemory = paramConfig["PrintMemory"].get(); - } - if (paramConfig.contains("DropTFUponFailure")) { - params.DropTFUponFailure = paramConfig["DropTFUponFailure"].get(); - } - - // Parse vector parameters - if (paramConfig.contains("LayerZ")) { - params.LayerZ = paramConfig["LayerZ"].get>(); - } - if (paramConfig.contains("LayerRadii")) { - params.LayerRadii = paramConfig["LayerRadii"].get>(); - } - if (paramConfig.contains("LayerxX0")) { - params.LayerxX0 = paramConfig["LayerxX0"].get>(); - } - if (paramConfig.contains("LayerResolution")) { - params.LayerResolution = paramConfig["LayerResolution"].get>(); - } - if (paramConfig.contains("SystErrorY2")) { - params.SystErrorY2 = paramConfig["SystErrorY2"].get>(); - } - if (paramConfig.contains("SystErrorZ2")) { - params.SystErrorZ2 = paramConfig["SystErrorZ2"].get>(); - } - if (paramConfig.contains("MinPt")) { - params.MinPt = paramConfig["MinPt"].get>(); - } - - // Parse Diamond array - if (paramConfig.contains("Diamond") && paramConfig["Diamond"].is_array() && paramConfig["Diamond"].size() == 3) { - params.Diamond[0] = paramConfig["Diamond"][0].get(); - params.Diamond[1] = paramConfig["Diamond"][1].get(); - params.Diamond[2] = paramConfig["Diamond"][2].get(); - } - - // Parse size_t parameter - if (paramConfig.contains("MaxMemory")) { - params.MaxMemory = paramConfig["MaxMemory"].get(); - } - - // Parse CorrType enum - if (paramConfig.contains("CorrType")) { - int corrTypeInt = paramConfig["CorrType"].get(); - params.CorrType = static_cast::MatCorrType>(corrTypeInt); - } - - trackingParams.push_back(params); - } - - LOGP(info, "Loaded {} tracking parameter sets from configuration", trackingParams.size()); - return trackingParams; -} - -void TrackerDPL::run(ProcessingContext& pc) -{ - auto cput = mTimer.CpuTime(); - auto realt = mTimer.RealTime(); - mTimer.Start(false); - - if (!mHitRecoConfig.empty()) { - TFile hitsFile(mHitRecoConfig["inputfiles"]["hits"].get().c_str(), "READ"); - TFile mcHeaderFile(mHitRecoConfig["inputfiles"]["mcHeader"].get().c_str(), "READ"); - TTree* hitsTree = hitsFile.Get("o2sim"); - std::vector* trkHit = nullptr; - hitsTree->SetBranchAddress("TRKHit", &trkHit); - - TTree* mcHeaderTree = mcHeaderFile.Get("o2sim"); - auto mcheader = new o2::dataformats::MCEventHeader; - mcHeaderTree->SetBranchAddress("MCEventHeader.", &mcheader); - - o2::base::GeometryManager::loadGeometry(mHitRecoConfig["inputfiles"]["geometry"].get().c_str(), false, true); - auto* gman = o2::trk::GeometryTGeo::Instance(); - - const Long64_t nEvents{hitsTree->GetEntries()}; - LOGP(info, "Starting reconstruction from hits for {} events", nEvents); - - if (mMemoryPool.get() == nullptr) { - mMemoryPool = std::make_shared(); - } - if (mTaskArena.get() == nullptr) { - mTaskArena = std::make_shared(1); /// TODO: make it configurable - } - - o2::trk::TimeFrame<11> timeFrame; - o2::its::TrackerTraits<11> itsTrackerTraits; - o2::its::Tracker<11> itsTracker(&itsTrackerTraits); - timeFrame.setMemoryPool(mMemoryPool); - itsTrackerTraits.setMemoryPool(mMemoryPool); - itsTrackerTraits.setNThreads(mTaskArena->max_concurrency(), mTaskArena); - itsTrackerTraits.adoptTimeFrame(static_cast*>(&timeFrame)); - itsTrackerTraits.setBz(mHitRecoConfig["geometry"]["bz"].get()); - auto field = new field::MagneticField("ALICE3Mag", "ALICE 3 Magnetic Field", mHitRecoConfig["geometry"]["bz"].get() / 5.f, 0.0, o2::field::MagFieldParam::k5kGUniform); - TGeoGlobalMagField::Instance()->SetField(field); - TGeoGlobalMagField::Instance()->Lock(); - itsTracker.adoptTimeFrame(timeFrame); - - const int nRofs = timeFrame.loadROFsFromHitTree(hitsTree, gman, mHitRecoConfig); - const int inROFpileup{mHitRecoConfig.contains("inROFpileup") ? mHitRecoConfig["inROFpileup"].get() : 1}; - - // Add primary vertices from MC headers for each ROF - timeFrame.getPrimaryVerticesFromMC(mcHeaderTree, nRofs, nEvents, inROFpileup); - // Create tracking parameters from config and set them in the time frame - auto trackingParams = createTrackingParamsFromConfig(); - - itsTrackerTraits.updateTrackingParameters(trackingParams); - -#ifdef O2_WITH_ACTS - if (mUseACTS) { - LOG(info) << "Running the tracking with ACTS"; - o2::trk::TrackerACTS<11> actsTracker; - actsTracker.setBz(mHitRecoConfig["geometry"]["bz"].get()); - actsTracker.adoptTimeFrame(timeFrame); - actsTracker.clustersToTracks(); - } -#endif - - const auto trackingLoopStart = std::chrono::steady_clock::now(); - for (size_t iter{0}; iter < trackingParams.size(); ++iter) { - LOGP(info, "{}", trackingParams[iter].asString()); - timeFrame.initialise(iter, trackingParams[iter], 11, false); - itsTrackerTraits.computeLayerTracklets(iter, -1); - LOGP(info, "Number of tracklets in iteration {}: {}", iter, timeFrame.getNumberOfTracklets()); - itsTrackerTraits.computeLayerCells(iter); - LOGP(info, "Number of cells in iteration {}: {}", iter, timeFrame.getNumberOfCells()); - itsTrackerTraits.findCellsNeighbours(iter); - LOGP(info, "Number of cell neighbours in iteration {}: {}", iter, timeFrame.getNumberOfNeighbours()); - itsTrackerTraits.findRoads(iter); - LOGP(info, "Number of tracks in iteration {}: {}", iter, timeFrame.getNumberOfTracks()); - } - const auto trackingLoopElapsedMs = std::chrono::duration_cast(std::chrono::steady_clock::now() - trackingLoopStart).count(); - LOGP(info, "Tracking iterations block took {} ms", trackingLoopElapsedMs); - - itsTracker.computeTracksMClabels(); - - // Collect tracks and labels (flat vectors in the new interface) - const auto& tracks = timeFrame.getTracks(); - const auto& labels = timeFrame.getTracksLabel(); - - // Copy to output vectors (TrackITSExt -> TrackITS slicing for output compatibility) - std::vector allTracks(tracks.begin(), tracks.end()); - std::vector allLabels(labels.begin(), labels.end()); - - int totalTracks = allTracks.size(); - int goodTracks = 0; - int fakeTracks = 0; - - for (const auto& label : allLabels) { - if (label.isFake()) { - fakeTracks++; - } else { - goodTracks++; - } - } - - LOGP(info, "=== Tracking Summary ==="); - LOGP(info, "Total tracks reconstructed: {}", totalTracks); - LOGP(info, "Good tracks: {} ({:.1f}%)", goodTracks, totalTracks > 0 ? 100.0 * goodTracks / totalTracks : 0); - LOGP(info, "Fake tracks: {} ({:.1f}%)", fakeTracks, totalTracks > 0 ? 100.0 * fakeTracks / totalTracks : 0); - - // Stream tracks and labels to DPL output - pc.outputs().snapshot(o2::framework::Output{"TRK", "TRACKS", 0}, allTracks); - pc.outputs().snapshot(o2::framework::Output{"TRK", "TRACKSMCTR", 0}, allLabels); - - LOGP(info, "Tracks and MC labels streamed to output"); - - pc.services().get().endOfStream(); - pc.services().get().readyToQuit(framework::QuitRequest::Me); - } - - mTimer.Stop(); - LOGP(info, "CPU Reconstruction time for this TF {} s (cpu), {} s (wall)", mTimer.CpuTime() - cput, mTimer.RealTime() - realt); -} - -// void TrackerDPL::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) -// { -// // mITSTrackingInterface.finaliseCCDB(matcher, obj); -// } - -void TrackerDPL::endOfStream(EndOfStreamContext& ec) -{ - LOGF(info, "TRK CA-Tracker total timing: Cpu: %.3e Real: %.3e s in %d slots", mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); -} - -DataProcessorSpec getTrackerSpec(bool useMC, const std::string& hitRecoConfig, o2::gpu::gpudatatypes::DeviceType dType) -{ - std::vector inputs; - std::vector outputs; - outputs.emplace_back("TRK", "TRACKS", 0, Lifetime::Timeframe); - auto ggRequest = std::make_shared(false, // orbitResetTime - false, // GRPECS=true - false, // GRPLHCIF - false, // GRPMagField - false, // askMatLUT - o2::base::GRPGeomRequest::None, // geometry, but ignored until it will be put in the CCDB - inputs, - true); - - if (!hitRecoConfig.empty()) { - outputs.emplace_back("TRK", "TRACKSMCTR", 0, Lifetime::Timeframe); - return DataProcessorSpec{ - "trk-hits-tracker", - {}, - outputs, - AlgorithmSpec{adaptFromTask(ggRequest, - useMC, - hitRecoConfig, - dType)}, - Options{ConfigParamSpec{"max-loops", VariantType::Int, 1, {"max number of loops"}} -#ifdef O2_WITH_ACTS - , - {"useACTS", o2::framework::VariantType::Bool, false, {"Use ACTS for tracking"}} -#endif - }}; - } - - inputs.emplace_back("dummy", "TRK", "DUMMY", 0, Lifetime::Timeframe); - - constexpr bool expectClusterInputs = false; - if (expectClusterInputs) { - inputs.pop_back(); - inputs.emplace_back("compClusters", "TRK", "COMPCLUSTERS", 0, Lifetime::Timeframe); - inputs.emplace_back("patterns", "TRK", "PATTERNS", 0, Lifetime::Timeframe); - inputs.emplace_back("ROframes", "TRK", "CLUSTERSROF", 0, Lifetime::Timeframe); - } - - // inputs.emplace_back("itscldict", "TRK", "CLUSDICT", 0, Lifetime::Condition, ccdbParamSpec("ITS/Calib/ClusterDictionary")); - // inputs.emplace_back("TRK_almiraparam", "TRK", "ALMIRAPARAM", 0, Lifetime::Condition, ccdbParamSpec("TRK/Config/AlmiraParam")); - - // outputs.emplace_back("TRK", "TRACKCLSID", 0, Lifetime::Timeframe); - // outputs.emplace_back("TRK", "TRKTrackROF", 0, Lifetime::Timeframe); - // outputs.emplace_back("TRK", "VERTICES", 0, Lifetime::Timeframe); - // outputs.emplace_back("TRK", "VERTICESROF", 0, Lifetime::Timeframe); - // outputs.emplace_back("TRK", "IRFRAMES", 0, Lifetime::Timeframe); - - if (useMC) { - // inputs.emplace_back("trkmclabels", "TRK", "CLUSTERSMCTR", 0, Lifetime::Timeframe); - // inputs.emplace_back("TRKMC2ROframes", "TRK", "CLUSTERSMC2ROF", 0, Lifetime::Timeframe); - // outputs.emplace_back("TRK", "VERTICESMCTR", 0, Lifetime::Timeframe); - // outputs.emplace_back("TRK", "VERTICESMCPUR", 0, Lifetime::Timeframe); - // outputs.emplace_back("TRK", "TRACKSMCTR", 0, Lifetime::Timeframe); - // outputs.emplace_back("TRK", "TRKTrackMC2ROF", 0, Lifetime::Timeframe); - } - - return DataProcessorSpec{ - "trk-tracker", - inputs, - outputs, - AlgorithmSpec{adaptFromTask(ggRequest, - useMC, - hitRecoConfig, - dType)}, - Options{}}; -} - -} // namespace trk -} // namespace o2 diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/src/trk-reco-workflow.cxx b/Detectors/Upgrades/ALICE3/TRK/workflow/src/trk-reco-workflow.cxx index 166e6f65b4b2b..bd1d5acc9b9a7 100644 --- a/Detectors/Upgrades/ALICE3/TRK/workflow/src/trk-reco-workflow.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/src/trk-reco-workflow.cxx @@ -9,21 +9,8 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -// 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. - #include "TRKWorkflow/RecoWorkflow.h" #include "CommonUtils/ConfigurableParam.h" -#include "ITStracking/TrackingConfigParam.h" -#include "ITStracking/Configuration.h" #include "Framework/CallbacksPolicy.h" #include "Framework/ConfigContext.h" @@ -52,11 +39,7 @@ void customize(std::vector& workflowOptions) {"clusters-from-upstream", VariantType::Bool, false, {"clusters will be provided from upstream, skip clusterizer"}}, {"disable-root-output", VariantType::Bool, false, {"do not write output root files"}}, {"disable-mc", VariantType::Bool, false, {"disable MC propagation even if available"}}, - {"tracking-from-hits-config", VariantType::String, "", {"JSON file with tracking from hits configuration"}}, - {"disable-tracking", VariantType::Bool, false, {"disable tracking step"}}, - {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}, - {"use-gpu-workflow", VariantType::Bool, false, {"use GPU workflow (default: false)"}}, - {"gpu-device", VariantType::Int, 1, {"use gpu device: CPU=1,CUDA=2,HIP=3 (default: CPU)"}}}; + {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}}; std::swap(workflowOptions, options); } @@ -67,9 +50,6 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) { // 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"); - auto useGpuWF = configcontext.options().get("use-gpu-workflow"); - auto gpuDevice = static_cast(configcontext.options().get("gpu-device")); auto extDigits = configcontext.options().get("digits-from-upstream"); auto extClusters = configcontext.options().get("clusters-from-upstream"); auto disableRootOutput = configcontext.options().get("disable-root-output"); @@ -78,5 +58,5 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) // write the configuration used for the reco workflow o2::conf::ConfigurableParam::writeINI("o2itsrecoflow_configuration.ini"); - return o2::trk::reco_workflow::getWorkflow(useMC, hitRecoConfig, extDigits, extClusters, disableRootOutput, useGpuWF, gpuDevice); + return o2::trk::reco_workflow::getWorkflow(useMC, extDigits, extClusters, disableRootOutput); }