From ec5288ef19c6b64b1e033dae75aa32692f0b4c68 Mon Sep 17 00:00:00 2001 From: Maurice Coquet Date: Fri, 5 Jun 2026 15:47:05 +0200 Subject: [PATCH 1/2] Draft of common ITS/MFT tracking classes --- .../workflow/src/mft-ca-tracker-workflow.cxx | 4 + Detectors/ITSMFT/common/CMakeLists.txt | 3 +- .../ITSMFT/common/tracking/CMakeLists.txt | 44 + .../include/ITSMFTTracking/BoundedAllocator.h | 287 ++++++ .../tracking/include/ITSMFTTracking/Cell.h | 177 ++++ .../tracking/include/ITSMFTTracking/Cluster.h | 86 ++ .../include/ITSMFTTracking/ClusterLines.h | 93 ++ .../include/ITSMFTTracking/Configuration.h | 154 ++++ .../include/ITSMFTTracking/Constants.h | 62 ++ .../ITSMFTTracking/ExternalAllocator.h | 86 ++ .../tracking/include/ITSMFTTracking/IOUtils.h | 122 +++ .../include/ITSMFTTracking/IndexTableUtils.h | 202 +++++ .../include/ITSMFTTracking/LayerMask.h | 115 +++ .../include/ITSMFTTracking/MathUtils.h | 126 +++ .../include/ITSMFTTracking/ROFLookupTables.h | 849 ++++++++++++++++++ .../include/ITSMFTTracking/TimeFrame.h | 610 +++++++++++++ .../ITSMFTTracking/TrackingConfigParam.h | 159 ++++ .../include/ITSMFTTracking/TrackingTopology.h | 219 +++++ .../include/ITSMFTTracking/Tracklet.h | 74 ++ .../tracking/include/ITSMFTTracking/Types.h | 42 + .../ITSMFT/common/tracking/src/Cluster.cxx | 87 ++ .../common/tracking/src/ClusterLines.cxx | 217 +++++ .../common/tracking/src/Configuration.cxx | 74 ++ .../ITSMFT/common/tracking/src/IOUtils.cxx | 211 +++++ .../tracking/src/ITSMFTTrackingLinkDef.h | 27 + .../ITSMFT/common/tracking/src/TimeFrame.cxx | 539 +++++++++++ .../tracking/src/TrackingConfigParam.cxx | 18 + 27 files changed, 4686 insertions(+), 1 deletion(-) create mode 100644 Detectors/ITSMFT/common/tracking/CMakeLists.txt create mode 100644 Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/BoundedAllocator.h create mode 100644 Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/Cell.h create mode 100644 Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/Cluster.h create mode 100644 Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/ClusterLines.h create mode 100644 Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/Configuration.h create mode 100644 Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/Constants.h create mode 100644 Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/ExternalAllocator.h create mode 100644 Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/IOUtils.h create mode 100644 Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/IndexTableUtils.h create mode 100644 Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/LayerMask.h create mode 100644 Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/MathUtils.h create mode 100644 Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/ROFLookupTables.h create mode 100644 Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/TimeFrame.h create mode 100644 Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/TrackingConfigParam.h create mode 100644 Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/TrackingTopology.h create mode 100644 Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/Tracklet.h create mode 100644 Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/Types.h create mode 100644 Detectors/ITSMFT/common/tracking/src/Cluster.cxx create mode 100644 Detectors/ITSMFT/common/tracking/src/ClusterLines.cxx create mode 100644 Detectors/ITSMFT/common/tracking/src/Configuration.cxx create mode 100644 Detectors/ITSMFT/common/tracking/src/IOUtils.cxx create mode 100644 Detectors/ITSMFT/common/tracking/src/ITSMFTTrackingLinkDef.h create mode 100644 Detectors/ITSMFT/common/tracking/src/TimeFrame.cxx create mode 100644 Detectors/ITSMFT/common/tracking/src/TrackingConfigParam.cxx diff --git a/Detectors/ITSMFT/MFT/workflow/src/mft-ca-tracker-workflow.cxx b/Detectors/ITSMFT/MFT/workflow/src/mft-ca-tracker-workflow.cxx index c8d236ce8983e..a42eb6f29f3c0 100644 --- a/Detectors/ITSMFT/MFT/workflow/src/mft-ca-tracker-workflow.cxx +++ b/Detectors/ITSMFT/MFT/workflow/src/mft-ca-tracker-workflow.cxx @@ -13,6 +13,7 @@ #include +#include "CommonUtils/ConfigurableParam.h" #include "Framework/ConfigParamSpec.h" #include "MFTWorkflow/CATrackerSpec.h" @@ -23,12 +24,15 @@ void customize(std::vector& workflowOptions) workflowOptions.push_back(ConfigParamSpec{"disable-mc", VariantType::Bool, false, {"disable MC labels"}}); workflowOptions.push_back(ConfigParamSpec{"use-geom", VariantType::Bool, false, {"use geometry from the global geometry manager"}}); workflowOptions.push_back(ConfigParamSpec{"use-irframes", VariantType::Bool, false, {"consume ITS IR frames"}}); + workflowOptions.push_back(ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings (e.g. MFTAlpideParam.roFrameLengthInBC=594)"}}); } #include "Framework/runDataProcessing.h" WorkflowSpec defineDataProcessing(ConfigContext const& config) { + o2::conf::ConfigurableParam::updateFromString(config.options().get("configKeyValues")); + const bool useMC = !config.options().get("disable-mc"); const bool useGeom = config.options().get("use-geom"); const bool useIRFrames = config.options().get("use-irframes"); diff --git a/Detectors/ITSMFT/common/CMakeLists.txt b/Detectors/ITSMFT/common/CMakeLists.txt index 3991f3e67a82b..a66278b2c09ce 100644 --- a/Detectors/ITSMFT/common/CMakeLists.txt +++ b/Detectors/ITSMFT/common/CMakeLists.txt @@ -13,4 +13,5 @@ add_subdirectory(base) add_subdirectory(simulation) add_subdirectory(reconstruction) add_subdirectory(workflow) -add_subdirectory(data) \ No newline at end of file +add_subdirectory(data) +add_subdirectory(tracking) diff --git a/Detectors/ITSMFT/common/tracking/CMakeLists.txt b/Detectors/ITSMFT/common/tracking/CMakeLists.txt new file mode 100644 index 0000000000000..1ff5203b1237a --- /dev/null +++ b/Detectors/ITSMFT/common/tracking/CMakeLists.txt @@ -0,0 +1,44 @@ +# 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(ITSMFTTracking + TARGETVARNAME targetName + SOURCES src/IOUtils.cxx + src/TrackingConfigParam.cxx + src/Configuration.cxx + src/Cluster.cxx + src/ClusterLines.cxx + src/TimeFrame.cxx + PUBLIC_LINK_LIBRARIES + O2::GPUCommon + O2::CommonConstants + O2::DetectorsCommonDataFormats + O2::DataFormatsITSMFT + O2::ITSMFTBase + O2::CommonUtils + O2::DetectorsBase + O2::MathUtils + Microsoft.GSL::GSL + O2::SimulationDataFormat + O2::ReconstructionDataFormats + PRIVATE_LINK_LIBRARIES + O2::Framework + O2::ITSBase + O2::MFTBase + O2::DataFormatsITS) + +o2_target_root_dictionary(ITSMFTTracking + HEADERS include/ITSMFTTracking/IOUtils.h + include/ITSMFTTracking/TrackingConfigParam.h + include/ITSMFTTracking/Configuration.h + include/ITSMFTTracking/IndexTableUtils.h + include/ITSMFTTracking/TimeFrame.h + LINKDEF src/ITSMFTTrackingLinkDef.h) diff --git a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/BoundedAllocator.h b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/BoundedAllocator.h new file mode 100644 index 0000000000000..b28ed0803446d --- /dev/null +++ b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/BoundedAllocator.h @@ -0,0 +1,287 @@ +// 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 BoundedAllocator.h +/// \brief +/// + +#ifndef ALICEO2_ITSMFT_TRACKING_INCLUDE_BOUNDEDALLOCATOR_H_ +#define ALICEO2_ITSMFT_TRACKING_INCLUDE_BOUNDEDALLOCATOR_H_ + +#include +#include +#include +#include +#include + +#if !defined(__HIPCC__) && !defined(__CUDACC__) +#include +#include +#include "GPUCommonLogger.h" +#endif +#include "ITSMFTTracking/ExternalAllocator.h" +#include "ITSMFTTracking/Constants.h" + +namespace o2::itsmft::tracking +{ + +// #define BOUNDED_MR_STATS +class BoundedMemoryResource final : public std::pmr::memory_resource +{ + public: + class MemoryLimitExceeded final : public std::bad_alloc + { + public: + MemoryLimitExceeded(size_t attempted, size_t used, size_t max) + { + char buf[256]; + if (attempted != 0) { + (void)snprintf(buf, sizeof(buf), "Reached set memory limit (attempted: %zu, used: %zu, max: %zu)", attempted, used, max); + } else { + (void)snprintf(buf, sizeof(buf), "New set maximum below current used (newMax: %zu, used: %zu)", max, used); + } + mMsg = buf; + } + const char* what() const noexcept final { return mMsg.c_str(); } + + private: + std::string mMsg; + }; + + BoundedMemoryResource(size_t maxBytes = std::numeric_limits::max(), + std::pmr::memory_resource* upstream = std::pmr::get_default_resource()) + : mMaxMemory(maxBytes), mUpstream(upstream) {} + + BoundedMemoryResource(ExternalAllocator* alloc, + size_t maxBytes = std::numeric_limits::max()) + : mMaxMemory(maxBytes), + mAdaptor(std::make_unique(alloc)), + mUpstream(mAdaptor.get()) {} + + void* do_allocate(size_t bytes, size_t alignment) final + { + size_t new_used{0}; + size_t current_used{mUsedMemory.load(std::memory_order_relaxed)}; + do { + new_used = current_used + bytes; + if (new_used > mMaxMemory.load(std::memory_order_relaxed)) { + mCountThrow.fetch_add(1, std::memory_order_relaxed); + throw MemoryLimitExceeded(new_used, current_used, + mMaxMemory.load(std::memory_order_relaxed)); + } + } while (!mUsedMemory.compare_exchange_weak(current_used, new_used, + std::memory_order_acq_rel, + std::memory_order_relaxed)); + + void* p{nullptr}; + try { + p = mUpstream->allocate(bytes, alignment); + } catch (...) { + mUsedMemory.fetch_sub(bytes, std::memory_order_relaxed); +#ifdef BOUNDED_MR_STATS + mStats.upstreamFailures.fetch_add(1, std::memory_order_relaxed); +#endif + throw; + } + +#ifdef BOUNDED_MR_STATS + size_t peak = mStats.peak.load(std::memory_order_relaxed); + while (new_used > peak && + !mStats.peak.compare_exchange_weak(peak, new_used, + std::memory_order_relaxed)) { + } + mStats.live.fetch_add(1, std::memory_order_relaxed); + mStats.nAlloc.fetch_add(1, std::memory_order_relaxed); + mStats.totalAlloc.fetch_add(bytes, std::memory_order_relaxed); + + size_t ma = mStats.maxAlign.load(std::memory_order_relaxed); + while (alignment > ma && !mStats.maxAlign.compare_exchange_weak(ma, alignment, std::memory_order_relaxed)) { + } +#endif + return p; + } + + void do_deallocate(void* p, size_t bytes, size_t alignment) final + { + mUpstream->deallocate(p, bytes, alignment); + mUsedMemory.fetch_sub(bytes, std::memory_order_relaxed); +#ifdef BOUNDED_MR_STATS + mStats.live.fetch_sub(1, std::memory_order_relaxed); + mStats.nFree.fetch_add(1, std::memory_order_relaxed); + mStats.totalFreed.fetch_add(bytes, std::memory_order_relaxed); +#endif + } + + bool do_is_equal(const std::pmr::memory_resource& other) const noexcept final + { + return this == &other; + } + + [[nodiscard]] size_t getUsedMemory() const noexcept + { + return mUsedMemory.load(std::memory_order_relaxed); + } + [[nodiscard]] size_t getMaxMemory() const noexcept + { + return mMaxMemory.load(std::memory_order_relaxed); + } + [[nodiscard]] size_t getThrowCount() const noexcept + { + return mCountThrow.load(std::memory_order_relaxed); + } + + void setMaxMemory(size_t max) + { + size_t current = mMaxMemory.load(std::memory_order_relaxed); + if (max == current) { + return; + } + for (;;) { + size_t used = mUsedMemory.load(std::memory_order_acquire); + if (used > max) { + mCountThrow.fetch_add(1, std::memory_order_relaxed); + throw MemoryLimitExceeded(0, used, max); + } + if (mMaxMemory.compare_exchange_weak(current, max, + std::memory_order_release, + std::memory_order_relaxed)) { + return; + } + if (current == max) { + return; + } + } + } + +#if !defined(__HIPCC__) && !defined(__CUDACC__) + std::string asString() const + { + const auto throw_ = mCountThrow.load(std::memory_order_relaxed); + const auto used = static_cast(mUsedMemory.load(std::memory_order_relaxed)); + const auto maxm = mMaxMemory.load(std::memory_order_relaxed); + std::string ret; + if (maxm == std::numeric_limits::max()) { + ret += std::format("maxthrow={} maxmem=unbounded used={:.2f} GB", throw_, used / constants::GB); + } else { + ret += std::format("maxthrow={} maxmem={:.2f} GB used={:.2f} GB ({:.2f}%)", throw_, (double)maxm / constants::GB, used / constants::GB, 100.0 * used / (double)maxm); + } +#ifdef BOUNDED_MR_STATS + ret += std::format(" peak={:.2f} GB live={} nAlloc={} nFree={} totalAlloc={:.2f} GB totalFreed={:.2f} GB maxAlign={} upstreamFail={}", + (float)mStats.peak.load(std::memory_order_relaxed) / constants::GB, + mStats.live.load(std::memory_order_relaxed), + mStats.nAlloc.load(std::memory_order_relaxed), + mStats.nFree.load(std::memory_order_relaxed), + (float)mStats.totalAlloc.load(std::memory_order_relaxed) / constants::GB, + (float)mStats.totalFreed.load(std::memory_order_relaxed) / constants::GB, + mStats.maxAlign.load(std::memory_order_relaxed), + mStats.upstreamFailures.load(std::memory_order_relaxed)); +#endif + return ret; + } + + void print() const + { + LOGP(info, "{}", asString()); + } +#endif + + private: + std::atomic mMaxMemory{std::numeric_limits::max()}; + std::atomic mCountThrow{0}; + std::atomic mUsedMemory{0}; + std::unique_ptr mAdaptor{nullptr}; + std::pmr::memory_resource* mUpstream{nullptr}; + +#ifdef BOUNDED_MR_STATS + struct Stats { + std::atomic peak{0}; + std::atomic live{0}; + std::atomic nAlloc{0}; + std::atomic nFree{0}; + std::atomic totalAlloc{0}; + std::atomic totalFreed{0}; + std::atomic maxAlign{0}; + std::atomic upstreamFailures{0}; + }; + Stats mStats{}; +#endif +}; + +template +using bounded_vector = std::pmr::vector; + +template +inline void deepVectorClear(std::vector& vec) +{ + std::vector().swap(vec); +} + +template +inline void deepVectorClear(bounded_vector& vec, std::pmr::memory_resource* mr = nullptr) +{ + std::pmr::memory_resource* tmr = (mr != nullptr) ? mr : vec.get_allocator().resource(); + vec.~bounded_vector(); + new (&vec) bounded_vector(std::pmr::polymorphic_allocator{tmr}); +} + +template +inline void deepVectorClear(std::vector>& vec, std::pmr::memory_resource* mr = nullptr) +{ + for (auto& v : vec) { + deepVectorClear(v, mr); + } +} + +template +inline void deepVectorClear(std::array, S>& arr, std::pmr::memory_resource* mr = nullptr) +{ + for (size_t i{0}; i < S; ++i) { + deepVectorClear(arr[i], mr); + } +} + +template +inline void clearResizeBoundedVector(bounded_vector& vec, size_t sz, std::pmr::memory_resource* mr = nullptr, T def = T()) +{ + std::pmr::memory_resource* tmr = (mr != nullptr) ? mr : vec.get_allocator().resource(); + vec.~bounded_vector(); + new (&vec) bounded_vector(sz, def, std::pmr::polymorphic_allocator{tmr}); +} + +template +inline void clearResizeBoundedVector(std::vector>& vec, size_t size, std::pmr::memory_resource* mr) +{ + vec.clear(); + vec.reserve(size); + for (size_t i = 0; i < size; ++i) { + vec.emplace_back(std::pmr::polymorphic_allocator>{mr}); + } +} + +template +inline void clearResizeBoundedArray(std::array, S>& arr, size_t size, std::pmr::memory_resource* mr = nullptr, T def = T()) +{ + for (size_t i{0}; i < S; ++i) { + clearResizeBoundedVector(arr[i], size, mr, def); + } +} + +template +inline std::vector toSTDVector(const bounded_vector& b) +{ + std::vector t(b.size()); + std::copy(b.cbegin(), b.cend(), t.begin()); + return t; +} + +} // namespace o2::itsmft::tracking + +#endif diff --git a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/Cell.h b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/Cell.h new file mode 100644 index 0000000000000..ab5faa6edfd4f --- /dev/null +++ b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/Cell.h @@ -0,0 +1,177 @@ +// 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 Cell.h +/// \brief +/// + +#ifndef ALICEO2_ITSMFT_TRACKING_INCLUDE_CACELL_H_ +#define ALICEO2_ITSMFT_TRACKING_INCLUDE_CACELL_H_ + +#include + +#include "ITSMFTTracking/Constants.h" +#include "ITSMFTTracking/LayerMask.h" +#include "ITSMFTTracking/Types.h" +#include "ReconstructionDataFormats/Track.h" +#include "GPUCommonDef.h" + +namespace o2::itsmft::tracking +{ + +struct CellNeighbour { + int cellTopology{-1}; + int cell{-1}; + int nextCellTopology{-1}; + int nextCell{-1}; + int level{-1}; +}; + +template +class SeedBase : public o2::track::TrackParCovF +{ + public: + GPUhd() LayerMask getHitLayerMask() const { return LayerMask{static_cast(getUserField())}; } + GPUhd() void setHitLayerMask(LayerMask mask) { setUserField(mask.value()); } + GPUhd() int getInnerLayer() const { return getHitLayerMask().first(); } + GPUhd() int getFirstTrackletIndex() const { return mTracklets[0]; }; + GPUhd() void setFirstTrackletIndex(int trkl) { mTracklets[0] = trkl; }; + GPUhd() int getSecondTrackletIndex() const { return mTracklets[1]; }; + GPUhd() void setSecondTrackletIndex(int trkl) { mTracklets[1] = trkl; }; + GPUhd() float getChi2() const { return mChi2; }; + GPUhd() void setChi2(float chi2) { mChi2 = chi2; }; + GPUhd() int getLevel() const { return mLevel; }; + GPUhd() void setLevel(int level) { mLevel = level; }; + GPUhd() int* getLevelPtr() { return &mLevel; } + GPUhd() auto& getTimeStamp() noexcept { return mTime; } + GPUhd() const auto& getTimeStamp() const noexcept { return mTime; } + + protected: + GPUhdDefault() SeedBase() = default; + GPUhdDefault() SeedBase(const SeedBase&) = default; + GPUhdDefault() ~SeedBase() = default; + GPUhdDefault() SeedBase(SeedBase&&) = default; + GPUhdDefault() SeedBase& operator=(const SeedBase&) = default; + GPUhdDefault() SeedBase& operator=(SeedBase&&) = default; + GPUhd() SeedBase(const o2::track::TrackParCovF& tpc, float chi2, int level, const TimeEstBC& time) + : o2::track::TrackParCovF(tpc), mChi2(chi2), mLevel(level), mTime(time) + { + } + GPUhd() auto& clustersRaw() { return mClusters; } + GPUhd() const auto& clustersRaw() const { return mClusters; } + + private: + float mChi2{constants::UnsetValue}; + int mLevel{constants::UnusedIndex}; + std::array mTracklets = constants::helpers::initArray(); + std::array mClusters = constants::helpers::initArray(); + TimeEstBC mTime; +}; + +/// CellSeed: connections of three clusters +class CellSeed final : public SeedBase +{ + using Base = SeedBase; + + public: + GPUhdDefault() CellSeed() = default; + GPUhd() CellSeed(int innerL, int cl0, int cl1, int cl2, int trkl0, int trkl1, const o2::track::TrackParCovF& tpc, float chi2, const TimeEstBC& time) + : CellSeed(LayerMask(innerL, innerL + 1, innerL + 2), cl0, cl1, cl2, trkl0, trkl1, tpc, chi2, time) + { + } + GPUhd() CellSeed(LayerMask hitLayerMask, int cl0, int cl1, int cl2, int trkl0, int trkl1, const o2::track::TrackParCovF& tpc, float chi2, const TimeEstBC& time) + : Base(tpc, chi2, 1, time) + { + setHitLayerMask(hitLayerMask); + auto& clusters = this->clustersRaw(); + clusters[0] = cl0; + clusters[1] = cl1; + clusters[2] = cl2; + setFirstTrackletIndex(trkl0); + setSecondTrackletIndex(trkl1); + } + GPUhdDefault() CellSeed(const CellSeed&) = default; + GPUhdDefault() ~CellSeed() = default; + GPUhdDefault() CellSeed(CellSeed&&) = default; + GPUhdDefault() CellSeed& operator=(const CellSeed&) = default; + GPUhdDefault() CellSeed& operator=(CellSeed&&) = default; + + GPUhd() int getFirstClusterIndex() const { return this->clustersRaw()[0]; }; + GPUhd() int getSecondClusterIndex() const { return this->clustersRaw()[1]; }; + GPUhd() int getThirdClusterIndex() const { return this->clustersRaw()[2]; }; + GPUhd() auto& getClusters() { return this->clustersRaw(); } + GPUhd() const auto& getClusters() const { return this->clustersRaw(); } + /// getCluster takes an ABSOLUTE layer index. Compact cluster slots are + /// mapped to absolute layers by set-bit order in the hit-layer mask. + GPUhd() int getCluster(int layer) const + { + const int slot = getHitLayerMask().slot(layer); + return (slot >= 0 && slot < constants::ClustersPerCell) ? this->clustersRaw()[slot] : constants::UnusedIndex; + } +}; + +/// TrackSeed: full-width working representation used during road finding. +/// processNeighbours extends the cluster list inward, so we need NLayers +/// absolute-indexed slots here. +template +class TrackSeed final : public SeedBase +{ + using Base = SeedBase; + + public: + GPUhdDefault() TrackSeed() = default; + GPUhd() TrackSeed(const CellSeed& cs) + : Base(static_cast(cs), cs.getChi2(), cs.getLevel(), cs.getTimeStamp()) + { + this->setHitLayerMask(cs.getHitLayerMask()); + this->setFirstTrackletIndex(cs.getFirstTrackletIndex()); + this->setSecondTrackletIndex(cs.getSecondTrackletIndex()); + auto& clusters = this->clustersRaw(); + int slot = 0; + const auto hitMask = cs.getHitLayerMask(); + for (int layer = 0; layer < NLayers; ++layer) { + if (hitMask.has(layer)) { + clusters[layer] = cs.getClusters()[slot++]; + } + } + } + GPUhdDefault() TrackSeed(const TrackSeed&) = default; + GPUhdDefault() ~TrackSeed() = default; + GPUhdDefault() TrackSeed(TrackSeed&&) = default; + GPUhdDefault() TrackSeed& operator=(const TrackSeed&) = default; + GPUhdDefault() TrackSeed& operator=(TrackSeed&&) = default; + + GPUhd() int getFirstClusterIndex() const { return getClusterBySlot(0); } + GPUhd() int getSecondClusterIndex() const { return getClusterBySlot(1); } + GPUhd() int getThirdClusterIndex() const { return getClusterBySlot(2); } + GPUhd() auto& getClusters() { return this->clustersRaw(); } + GPUhd() const auto& getClusters() const { return this->clustersRaw(); } + GPUhd() int getCluster(int layer) const { return this->clustersRaw()[layer]; } + + private: + GPUhd() int getClusterBySlot(int requestedSlot) const + { + int slot = 0; + const auto hitMask = this->getHitLayerMask(); + for (int layer = 0; layer < NLayers; ++layer) { + if (hitMask.has(layer)) { + if (slot++ == requestedSlot) { + return this->clustersRaw()[layer]; + } + } + } + return constants::UnusedIndex; + } +}; + +} // namespace o2::itsmft::tracking + +#endif /* ALICEO2_ITSMFT_TRACKING_INCLUDE_CACELL_H_ */ diff --git a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/Cluster.h b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/Cluster.h new file mode 100644 index 0000000000000..b4b6a74ee9eea --- /dev/null +++ b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/Cluster.h @@ -0,0 +1,86 @@ +// 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 Cluster.h +/// \brief +/// + +#ifndef ALICEO2_ITSMFT_TRACKING_INCLUDE_CACLUSTER_H_ +#define ALICEO2_ITSMFT_TRACKING_INCLUDE_CACLUSTER_H_ + +#include +#include "ITSMFTTracking/Constants.h" +#include "GPUCommonRtypes.h" +#include "GPUCommonDef.h" + +namespace o2::itsmft +{ +template +class IndexTableUtils; +} // namespace o2::itsmft + +namespace o2::itsmft::tracking +{ + +struct Cluster final { + GPUhdDefault() Cluster() = default; + GPUhd() Cluster(const float x, const float y, const float z, const int idx); + template + GPUhd() Cluster(const int, const IndexTableUtils& utils, const Cluster&); + template + GPUhd() Cluster(const int, const float3&, const IndexTableUtils& utils, const Cluster&); + GPUhdDefault() Cluster(const Cluster&) = default; + GPUhdDefault() Cluster(Cluster&&) noexcept = default; + GPUhdDefault() ~Cluster() = default; + + GPUhdDefault() Cluster& operator=(const Cluster&) = default; + GPUhdDefault() Cluster& operator=(Cluster&&) noexcept = default; + GPUhdDefault() bool operator==(const Cluster&) const = default; + + GPUhd() void print() const; + + float xCoordinate{-999.f}; + float yCoordinate{-999.f}; + float zCoordinate{-999.f}; + float phi{-999.f}; + float radius{-999.f}; + int clusterId{constants::UnusedIndex}; + int indexTableBinIndex{constants::UnusedIndex}; + + ClassDefNV(Cluster, 1); +}; + +struct TrackingFrameInfo final { + GPUhdDefault() TrackingFrameInfo() = default; + GPUhd() TrackingFrameInfo(float x, float y, float z, float xTF, float alpha, std::array&& posTF, std::array&& covTF); + GPUhdDefault() TrackingFrameInfo(const TrackingFrameInfo&) = default; + GPUhdDefault() TrackingFrameInfo(TrackingFrameInfo&&) noexcept = default; + GPUhdDefault() ~TrackingFrameInfo() = default; + + GPUhdDefault() TrackingFrameInfo& operator=(const TrackingFrameInfo&) = default; + GPUhdDefault() TrackingFrameInfo& operator=(TrackingFrameInfo&&) = default; + + GPUhd() void print() const; + + float xCoordinate{-999.f}; + float yCoordinate{-999.f}; + float zCoordinate{-999.f}; + float xTrackingFrame{-999.f}; + float alphaTrackingFrame{-999.f}; + std::array positionTrackingFrame = {-999.f, -999.f}; + std::array covarianceTrackingFrame = {-999.f, -999.f, -999.f}; + + ClassDefNV(TrackingFrameInfo, 1); +}; + +} // namespace o2::itsmft::tracking + +#endif /* ALICEO2_ITSMFT_TRACKING_INCLUDE_CACLUSTER_H_ */ diff --git a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/ClusterLines.h b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/ClusterLines.h new file mode 100644 index 0000000000000..330f384a503b3 --- /dev/null +++ b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/ClusterLines.h @@ -0,0 +1,93 @@ +// 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_ITSMFT_CLUSTERLINES_H +#define O2_ITSMFT_CLUSTERLINES_H + +#include +#include +#include +#include +#include +#include "ITSMFTTracking/Cluster.h" +#include "ITSMFTTracking/Constants.h" +#include "ITSMFTTracking/Tracklet.h" +#include "GPUCommonRtypes.h" + +namespace o2::itsmft::tracking +{ + +struct Line final { +#if !defined(__HIPCC__) && !defined(__CUDACC__) // hide the class completely for gpu-cc + using SVector3f = ROOT::Math::SVector; + using SMatrix3f = ROOT::Math::SMatrix>; + + Line() = default; + Line(const Tracklet&, const Cluster*, const Cluster*); + bool operator==(const Line&) const = default; + + static float getDistance2FromPoint(const Line& line, const std::array& point); + static float getDistanceFromPoint(const Line& line, const std::array& point); + static SMatrix3f getDCAComponents(const Line& line, const std::array& point); + static float getDCA2(const Line&, const Line&, const float precision = constants::Tolerance); + static float getDCA(const Line&, const Line&, const float precision = constants::Tolerance); + bool isEmpty() const noexcept; + void print() const; + + SVector3f originPoint; + SVector3f cosinesDirector; + TimeEstBC mTime; + + ClassDefNV(Line, 1); +#endif +}; + +class ClusterLines final +{ +#if !defined(__HIPCC__) && !defined(__CUDACC__) // hide the class completely for gpu-cc + using SMatrix3 = ROOT::Math::SMatrix>; + using SMatrix3f = ROOT::Math::SMatrix>; + using SVector3 = ROOT::Math::SVector; + + public: + ClusterLines() = default; + ClusterLines(const int firstLabel, const Line& firstLine, const int secondLabel, const Line& secondLine); + ClusterLines(gsl::span lineLabels, gsl::span lines); + void add(const int lineLabel, const Line& line); + void computeClusterCentroid(); + void accumulate(const Line& line); + bool isValid() const noexcept { return mIsValid; } + auto const& getVertex() const { return mVertex; } + const float* getRMS2() const { return mRMS2.Array(); } + float getAvgDistance2() const { return mAvgDistance2; } + auto getSize() const noexcept { return mLabels.size(); } + auto& getLabels() const noexcept { return mLabels; } + const auto& getTimeStamp() const noexcept { return mTime; } + bool operator==(const ClusterLines& rhs) const noexcept; + float getR2() const noexcept { return (mVertex[0] * mVertex[0]) + (mVertex[1] * mVertex[1]); } + float getR() const noexcept { return std::sqrt(getR2()); } + + protected: + SMatrix3 mAMatrix; // AX=B, symmetric normal matrix + SVector3 mBMatrix; // AX=B, right-hand side + std::array mVertex = {}; // cluster centroid position + SMatrix3f mRMS2; // symmetric matrix: diagonal is RMS2 + float mAvgDistance2 = 0.f; // substitute for chi2 + bool mIsValid = false; // true if linear system was solved successfully + TimeEstBC mTime; // time stamp + std::vector mLabels; // contributing labels + + ClassDefNV(ClusterLines, 1); +#endif +}; + +} // namespace o2::itsmft::tracking +#endif /* O2_ITSMFT_CLUSTERLINES_H */ diff --git a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/Configuration.h b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/Configuration.h new file mode 100644 index 0000000000000..150a364bc1466 --- /dev/null +++ b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/Configuration.h @@ -0,0 +1,154 @@ +// 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 Configuration.h +/// \brief Shared CA tracking configuration for ITS and MFT +/// + +#ifndef ALICEO2_ITSMFT_TRACKING_CONFIGURATION_H_ +#define ALICEO2_ITSMFT_TRACKING_CONFIGURATION_H_ + +#include +#ifndef GPUCA_GPUCODE_DEVICE +#include +#include +#include +#endif + +#include "CommonUtils/EnumFlags.h" +#include "DetectorsBase/Propagator.h" +#include "ITSMFTTracking/Constants.h" + +namespace o2::itsmft +{ + +inline constexpr int ClustersPerCell = 3; + +// Steering of dedicated steps in an iteration +enum class IterationStep : uint8_t { + FirstPass = 0, + RebuildClusterLUT, + UseUPCMask, + SelectUPCVertices, + ResetVertices, + SkipROFsAboveThreshold, + MarkVerticesAsUPC, +}; +using IterationSteps = o2::utils::EnumFlags; + +struct TrackingParameters { + int CellMinimumLevel() const noexcept + { + const int minClusters = MinTrackLength - (MaxHoles > 0 ? MaxHoles : 0); + const int effectiveMinClusters = minClusters > ClustersPerCell ? minClusters : ClustersPerCell; + return effectiveMinClusters - ClustersPerCell + 1; + } + int NeighboursPerRoad() const noexcept { return NLayers - 3; } + int CellsPerRoad() const noexcept { return NLayers - 2; } + int TrackletsPerRoad() const noexcept { return NLayers - 1; } + std::string asString() const; + + IterationSteps PassFlags{IterationStep::FirstPass, IterationStep::RebuildClusterLUT}; + int NLayers = tracking::constants::ITSNLayers; + std::vector AddTimeError = {0, 0, 0, 0, 0, 0, 0}; + std::vector LayerZ = {16.333f + 1, 16.333f + 1, 16.333f + 1, 42.140f + 1, 42.140f + 1, 73.745f + 1, 73.745f + 1}; + std::vector LayerColHalfExtent{}; // index-table column half extent (ITS: z, MFT: global x); falls back to LayerZ + float IndexRowMin{0.f}; // index-table row origin (MFT: global y min; unused for ITS phi-z) + float IndexRowMax{0.f}; // index-table row span end (MFT: global y max; 0 => TwoPI for ITS) + std::vector LayerRadii = {2.33959f, 3.14076f, 3.91924f, 19.6213f, 24.5597f, 34.388f, 39.3329f}; + std::vector LayerxX0 = {5.e-3f, 5.e-3f, 5.e-3f, 1.e-2f, 1.e-2f, 1.e-2f, 1.e-2f}; + std::vector LayerResolution = {5.e-4f, 5.e-4f, 5.e-4f, 5.e-4f, 5.e-4f, 5.e-4f, 5.e-4f}; + std::vector SystError2Row = {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}; // systematic error^2 along local row (ALPIDE X) per layer + std::vector SystError2Col = {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}; // systematic error^2 along local column (ALPIDE Z) per layer + int ColBins{256}; + int RowBins{128}; + bool UseDiamond = false; + float Diamond[3] = {0.f, 0.f, 0.f}; + float DiamondCov[6] = {25.e-6f, 0.f, 0.f, 25.e-6f, 0.f, 36.f}; + + /// General parameters + int MinTrackLength = 7; + int MaxHoles = 0; + uint16_t HoleLayerMask = 0; + float NSigmaCut = 5; + float PVres = 1.e-2f; + /// Trackleting cuts + float TrackletMinPt = 0.3f; + /// Cell finding cuts + float CellDeltaTanLambdaSigma = 0.007f; + /// Fitter parameters + o2::base::PropagatorImpl::MatCorrType CorrType = o2::base::PropagatorImpl::MatCorrType::USEMatCorrNONE; + float MaxChi2ClusterAttachment = 60.f; + float MaxChi2NDF = 30.f; + int ReseedIfShorter = 6; // reseed for the final fit track with the length shorter than this + std::vector MinPt = {0.f, 0.f, 0.f, 0.f}; + uint32_t StartLayerMask = 0x7F; + bool RepeatRefitOut = false; // repeat outward refit using inward refit as a seed + bool ShiftRefToCluster = true; // TrackFit: after update shift the linearization reference to cluster + bool PerPrimaryVertexProcessing = false; + bool SaveTimeBenchmarks = false; + bool DoUPCIteration = false; + bool FataliseUponFailure = true; + bool CreateArtefactLabels{false}; + bool PrintMemory = false; // print allocator usage in epilog report + size_t MaxMemory = std::numeric_limits::max(); + bool DropTFUponFailure = false; + + // Selections on tracks sharing clusters + bool AllowSharingFirstCluster = false; + float SharedClusterMaxDeltaPhi = 0.05f; // For tracks sharing clusters, maximum allowed delta phi at the cluster position + float SharedClusterMaxDeltaEta = 0.03f; // For tracks sharing clusters, maximum allowed delta eta at the cluster position + bool SharedClusterOppositeSign = false; // For tracks sharing clusters, require opposite sign of the tracklets + int SharedMaxClusters = 0; // Maximal allowed shared clusters (excluding first cluster) +}; + +struct VertexingParameters { + std::string asString() const; + + IterationSteps PassFlags{IterationStep::FirstPass, IterationStep::ResetVertices}; + std::vector LayerZ = {16.333f + 1, 16.333f + 1, 16.333f + 1, 42.140f + 1, 42.140f + 1, 73.745f + 1, 73.745f + 1}; + std::vector LayerRadii = {2.33959f, 3.14076f, 3.91924f, 19.6213f, 24.5597f, 34.388f, 39.3329f}; + int vertPerRofThreshold = 0; // Maximum number of vertices per ROF to trigger second a round + int ColBins = 1; + int RowBins = 128; + float zCut = -1.f; + float phiCut = -1.f; + float pairCut = -1.f; + float clusterCut = -1.f; + float coarseZWindow = -1.f; + float seedDedupZCut = -1.f; + float refitDedupZCut = -1.f; + float duplicateZCut = -1.f; + float finalSelectionZCut = -1.f; + float duplicateDistance2Cut = -1.f; + float tanLambdaCut = -1.f; + float NSigmaCut = -1; + float maxZPositionAllowed = -1.f; + int clusterContributorsCut = -1; + int suppressLowMultDebris = -1; + int seedMemberRadiusTime = -1; + int seedMemberRadiusZ = -1; + int maxTrackletsPerCluster = -1; + int phiSpan = -1; + int zSpan = -1; + bool SaveTimeBenchmarks = false; + + bool useTruthSeeding = false; // overwrite found vertices with MC events + + int nThreads = 1; + bool PrintMemory = false; // print allocator usage in epilog report + size_t MaxMemory = std::numeric_limits::max(); + bool DropTFUponFailure = false; +}; + +} // namespace o2::itsmft + +#endif /* ALICEO2_ITSMFT_TRACKING_CONFIGURATION_H_ */ diff --git a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/Constants.h b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/Constants.h new file mode 100644 index 0000000000000..10bc7b6e5ab09 --- /dev/null +++ b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/Constants.h @@ -0,0 +1,62 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// +/// \file Constants.h +/// \brief +/// + +#ifndef ALICEO2_ITSMFT_TRACKING_INCLUDE_CONSTANTS_H_ +#define ALICEO2_ITSMFT_TRACKING_INCLUDE_CONSTANTS_H_ + +#include +#include + +#include "DetectorsCommonDataFormats/DetID.h" + +namespace o2::itsmft::tracking::constants +{ + +constexpr int ITSNLayers = 7; +constexpr int MFTNLayers = 10; + +constexpr int nLayersForDet(o2::detectors::DetID::ID detId) +{ + return detId == o2::detectors::DetID::MFT ? MFTNLayers : ITSNLayers; +} + +constexpr float KB = 1024.f; +constexpr float MB = KB * KB; +constexpr float GB = MB * KB; +constexpr bool DoTimeBenchmarks = true; +constexpr bool SaveTimeBenchmarks = false; +constexpr float Tolerance = 1e-12; // numerical tolerance +constexpr int ClustersPerCell = 3; // number of clusters for a cell +constexpr int UnusedIndex = -1; // global unused flag +constexpr float UnsetValue = -999.f; // global unset value +constexpr float Radl = 9.36f; // Radiation length of Si [cm] +constexpr float Rho = 2.33f; // Density of Si [g/cm^3] +constexpr int MaxIter = 4; // Max. supported iterations +constexpr int MaxSelectedTrackletsPerCluster = 100; // vertexer: max lines per cluster + +namespace helpers +{ + +// initialize a std::array at compile time fully with T +template +constexpr std::array initArray() +{ + return [](std::index_sequence) { return std::array{(static_cast(Is), Value)...}; }(std::make_index_sequence{}); +} + +} // namespace helpers +} // namespace o2::itsmft::tracking::constants + +#endif /* ALICEO2_ITSMFT_TRACKING_INCLUDE_CONSTANTS_H_ */ diff --git a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/ExternalAllocator.h b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/ExternalAllocator.h new file mode 100644 index 0000000000000..42f0304dbd08e --- /dev/null +++ b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/ExternalAllocator.h @@ -0,0 +1,86 @@ +// 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 ExternalAllocator.h +/// \brief +/// + +#ifndef ALICEO2_ITSMFT_TRACKING_INCLUDE_EXTERNALALLOCATOR_H_ +#define ALICEO2_ITSMFT_TRACKING_INCLUDE_EXTERNALALLOCATOR_H_ + +#include +#include "GPUO2ExternalUser.h" +#include "Base/GPUMemoryResource.h" + +namespace o2::itsmft::tracking +{ + +class ExternalAllocator +{ + using Type = std::underlying_type_t; + + public: + virtual void deallocate(char*, size_t) = 0; + virtual void* allocate(size_t) = 0; + void* allocate(size_t s, Type type) + { + auto old = mType; + mType = type; + void* p = allocate(s); + mType = old; + return p; + } + void* allocateStack(size_t s) + { + return allocate(s, (o2::gpu::GPUMemoryResource::MEMORY_GPU | o2::gpu::GPUMemoryResource::MEMORY_STACK)); + } + virtual void pushTagOnStack(uint64_t) = 0; + virtual void popTagOffStack(uint64_t) = 0; + + void setType(Type t) noexcept { mType = t; } + Type getType() const noexcept { return mType; } + + protected: + Type mType; +}; + +class ExternalAllocatorAdaptor final : public std::pmr::memory_resource +{ + public: + explicit ExternalAllocatorAdaptor(ExternalAllocator* alloc) : mAlloc(alloc) {} + + protected: + void* do_allocate(size_t bytes, size_t alignment) override + { + void* p = mAlloc->allocate(bytes, o2::gpu::GPUMemoryResource::MemoryType::MEMORY_HOST); + if (!p) { + throw std::bad_alloc(); + } + return p; + } + + void do_deallocate(void* p, size_t bytes, size_t) override + { + mAlloc->deallocate(static_cast(p), bytes); + } + + bool do_is_equal(const std::pmr::memory_resource& other) const noexcept override + { + return this == &other; + } + + private: + ExternalAllocator* mAlloc; +}; + +} // namespace o2::itsmft::tracking + +#endif diff --git a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/IOUtils.h b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/IOUtils.h new file mode 100644 index 0000000000000..cc49bdcf35e20 --- /dev/null +++ b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/IOUtils.h @@ -0,0 +1,122 @@ +// 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 IOUtils.h +/// \brief Shared cluster I/O utilities for ITS and MFT (based on ITStracking/IOUtils.h) +/// + +#ifndef ALICEO2_ITSMFT_TRACKING_IOUTILS_H_ +#define ALICEO2_ITSMFT_TRACKING_IOUTILS_H_ + +#include +#include + +#include + +#include "DetectorsCommonDataFormats/DetID.h" +#include "ITSMFTBase/SegmentationAlpide.h" +#include "ReconstructionDataFormats/BaseCluster.h" +#include "DataFormatsITSMFT/ClusterPattern.h" +#include "DataFormatsITSMFT/CompCluster.h" +#include "DataFormatsITSMFT/TopologyDictionary.h" +#include "MathUtils/Cartesian.h" + +namespace o2::itsmft::tracking +{ +struct TrackingFrameInfo; +} + +namespace o2::itsmft::ioutils +{ + +constexpr float DefClusErrorRow = o2::itsmft::SegmentationAlpide::PitchRow * 0.5f; +constexpr float DefClusErrorCol = o2::itsmft::SegmentationAlpide::PitchCol * 0.5f; +constexpr float DefClusError2Row = DefClusErrorRow * DefClusErrorRow; +constexpr float DefClusError2Col = DefClusErrorCol * DefClusErrorCol; + +void fillMatrixCache(o2::detectors::DetID::ID detId); +int getClusterLayer(o2::detectors::DetID::ID detId, const CompClusterExt& cluster); + +template +unsigned int extractClusterSize(const CompClusterExt& c, iterator& iter, const TopologyDictionary* dict) +{ + auto pattID = c.getPatternID(); + if (pattID != CompCluster::InvalidPatternID) { + if (!dict->isGroup(pattID)) { + return dict->getNpixels(pattID); + } + ClusterPattern patt(iter); + return patt.getNPixels(); + } + ClusterPattern patt(iter); + return patt.getNPixels(); +} + +/// Decode a compact cluster into layer, size, and a TrackingFrameInfo (global + local frame). +template +void loadClusterTrackingFrameInfo(const CompClusterExt& c, + gsl::span::iterator& pattIt, + const TopologyDictionary* dict, + int& layer, + unsigned int& clusterSize, + tracking::TrackingFrameInfo& tfInfo, + bool applySysErrors = true); + +/// Convert compact clusters to 3D spacepoints. +/// \tparam DetId o2::detectors::DetID::ITS or DetID::MFT +template +void convertCompactClusters(gsl::span clusters, + gsl::span::iterator& pattIt, + std::vector>& output, + const TopologyDictionary* dict); + +template +o2::math_utils::Point3D extractClusterData(const CompClusterExt& c, iterator& iter, const TopologyDictionary* dict, T& sig2Row, T& sig2Col) +{ + auto pattID = c.getPatternID(); + sig2Row = DefClusError2Row; + sig2Col = DefClusError2Col; // Dummy COG errors (about half pixel size) + if (pattID != CompCluster::InvalidPatternID) { + sig2Row = dict->getErr2X(pattID); + sig2Col = dict->getErr2Z(pattID); + if (!dict->isGroup(pattID)) { + return dict->getClusterCoordinates(c); + } + ClusterPattern patt(iter); + return dict->getClusterCoordinates(c, patt); + } + ClusterPattern patt(iter); + return dict->getClusterCoordinates(c, patt, false); +} + +// same method returning coordinates as an array (suitable for the TGeoMatrix) +template +std::array extractClusterDataA(const CompClusterExt& c, iterator& iter, const TopologyDictionary* dict, T& sig2Row, T& sig2Col) +{ + auto pattID = c.getPatternID(); + sig2Row = DefClusError2Row; + sig2Col = DefClusError2Col; // Dummy COG errors (about half pixel size) + if (pattID != CompCluster::InvalidPatternID) { + sig2Row = dict->getErr2X(pattID); + sig2Col = dict->getErr2Z(pattID); + if (!dict->isGroup(pattID)) { + return dict->getClusterCoordinatesA(c); + } + ClusterPattern patt(iter); + return dict->getClusterCoordinatesA(c, patt); + } + ClusterPattern patt(iter); + return dict->getClusterCoordinatesA(c, patt, false); +} + +} // namespace o2::itsmft::ioutils + +#endif /* ALICEO2_ITSMFT_TRACKING_IOUTILS_H_ */ diff --git a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/IndexTableUtils.h b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/IndexTableUtils.h new file mode 100644 index 0000000000000..acfdb7c066982 --- /dev/null +++ b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/IndexTableUtils.h @@ -0,0 +1,202 @@ +// 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 IndexTableUtils.h +/// \brief Shared index-table utilities for ITS (phi-z) and MFT (x-y) +/// + +#ifndef ALICEO2_ITSMFT_TRACKING_INDEXTABLEUTILS_H_ +#define ALICEO2_ITSMFT_TRACKING_INDEXTABLEUTILS_H_ + +#include + +#include "CommonConstants/MathConstants.h" +#include "GPUCommonMath.h" +#include "GPUCommonDef.h" +#include "ITSMFTTracking/Configuration.h" + +namespace o2::itsmft +{ + +enum class IndexTableCoordType : uint8_t { PhiZ, XY }; + +namespace index_table_utils +{ +GPUhdi() float getNormalizedPhi(float phi) +{ + phi -= o2::constants::math::TwoPI * o2::gpu::GPUCommonMath::Floor(phi * (1.f / o2::constants::math::TwoPI)); + return phi; +} +} // namespace index_table_utils + +/// Row/column LUT helper (ITS: row=phi, col=z; MFT: row=y, col=x). +template +class IndexTableUtils +{ + public: + /// Configure LUT geometry. ITS (PhiZ): row = phi [0, TwoPI), col = z; MFT (XY): row = y, col = x. + void setIndexTableParams(IndexTableCoordType coordType, int nRowBins, int nColBins, + float rowMin, float rowMax, + const std::array& layerColHalfExtent) + { + mCoordType = coordType; + mRowOrigin = (coordType == IndexTableCoordType::PhiZ) ? 0.f : rowMin; + mRowCoordinateSpan = rowMax - rowMin; + mInverseRowBinSize = (mRowCoordinateSpan > 0.f) ? static_cast(nRowBins) / mRowCoordinateSpan : 0.f; + mNcolBins = nColBins; + mNrowBins = nRowBins; + for (int iLayer{0}; iLayer < nLayers; ++iLayer) { + mLayerColHalfExtent[iLayer] = layerColHalfExtent[iLayer]; + mInverseColBinSize[iLayer] = 0.5f * nColBins / layerColHalfExtent[iLayer]; + } + } + + /// Fill LUT geometry from any struct exposing RowBins, ColBins and LayerZ (ITS phi-z). + template + void setTrackingParameters(const T& params) + { + setIndexTableParams(IndexTableCoordType::PhiZ, params.RowBins, params.ColBins, + 0.f, o2::constants::math::TwoPI, layerColHalfExtentFrom(params)); + } + + /// Fill LUT geometry for MFT (row = global y, col = global x). + template + void setTrackingParametersXY(const T& params, float rowMin, float rowMax) + { + setIndexTableParams(IndexTableCoordType::XY, params.RowBins, params.ColBins, + rowMin, rowMax, layerColHalfExtentFrom(params)); + } + + GPUhdi() float getInverseColCoordinate(const int layerIndex) const + { + return 0.5f * mNcolBins / mLayerColHalfExtent[layerIndex]; + } + + GPUhdi() int getColBinIndex(const int layerIndex, const float colCoordinate) const + { + return (colCoordinate + mLayerColHalfExtent[layerIndex]) * mInverseColBinSize[layerIndex]; + } + + GPUhdi() int getRowBinIndex(const float rowCoordinate) const + { + if (mCoordType == IndexTableCoordType::PhiZ) { + return rowCoordinate * mInverseRowBinSize; + } + return (rowCoordinate - mRowOrigin) * mInverseRowBinSize; + } + + GPUhdi() int getBinIndex(const int colIndex, const int rowIndex) const + { + return o2::gpu::GPUCommonMath::Min(rowIndex * mNcolBins + colIndex, (mNcolBins * mNrowBins) - 1); + } + + GPUhdi() int countRowSelectedBins(const int* indexTable, const int rowBinIndex, + const int minColBinIndex, const int maxColBinIndex) const + { + const int firstBinIndex{getBinIndex(minColBinIndex, rowBinIndex)}; + const int maxBinIndex{firstBinIndex + maxColBinIndex - minColBinIndex + 1}; + + return indexTable[maxBinIndex] - indexTable[firstBinIndex]; + } + + void print() const; + + GPUhdi() int getNcolBins() const { return mNcolBins; } + GPUhdi() int getNrowBins() const { return mNrowBins; } + GPUhdi() float getLayerColHalfExtent(int i) const { return mLayerColHalfExtent[i]; } + GPUhdi() void setNcolBins(const int colBins) { mNcolBins = colBins; } + GPUhdi() void setNrowBins(const int rowBins) { mNrowBins = rowBins; } + GPUhdi() IndexTableCoordType getCoordType() const { return mCoordType; } + + private: + template + static std::array layerColHalfExtentFrom(const T& params) + { + std::array extents{}; + if constexpr (requires { params.LayerColHalfExtent; }) { + const auto& colExtents = params.LayerColHalfExtent.empty() ? params.LayerZ : params.LayerColHalfExtent; + for (int iLayer{0}; iLayer < nLayers && iLayer < static_cast(colExtents.size()); ++iLayer) { + extents[iLayer] = colExtents[iLayer]; + } + } else { + for (int iLayer{0}; iLayer < nLayers && iLayer < static_cast(params.LayerZ.size()); ++iLayer) { + extents[iLayer] = params.LayerZ[iLayer]; + } + } + return extents; + } + + int mNcolBins = 0; + int mNrowBins = 0; + float mInverseRowBinSize = 0.f; + float mRowOrigin = 0.f; + float mRowCoordinateSpan = o2::constants::math::TwoPI; + IndexTableCoordType mCoordType{IndexTableCoordType::PhiZ}; + std::array mLayerColHalfExtent{}; + std::array mInverseColBinSize{}; +}; + +template +void IndexTableUtils::print() const +{ + printf("NcolBins: %d, NrowBins: %d, InverseRowBinSize: %f\n", mNcolBins, mNrowBins, mInverseRowBinSize); + for (int iLayer{0}; iLayer < nLayers; ++iLayer) { + printf("Layer %d: ColHalfExtent: %f, InverseColBinSize: %f\n", iLayer, mLayerColHalfExtent[iLayer], mInverseColBinSize[iLayer]); + } +} + +/// ITS: row = phi, col = z. +template +GPUhdi() int4 getBinsPhiZ(float phi, const int layerIndex, + float z1, float z2, float maxDeltaCol, float maxDeltaRow, + const IndexTableUtils& utils) +{ + const float colRangeMin = o2::gpu::GPUCommonMath::Min(z1, z2) - maxDeltaCol; + const float rowRangeMin = (maxDeltaRow > o2::constants::math::PI) ? 0.f : phi - maxDeltaRow; + const float colRangeMax = o2::gpu::GPUCommonMath::Max(z1, z2) + maxDeltaCol; + const float rowRangeMax = (maxDeltaRow > o2::constants::math::PI) ? o2::constants::math::TwoPI : phi + maxDeltaRow; + + if (colRangeMax < -utils.getLayerColHalfExtent(layerIndex) || + colRangeMin > utils.getLayerColHalfExtent(layerIndex) || colRangeMin > colRangeMax) { + return int4{-1, -1, -1, -1}; + } + + return int4{o2::gpu::GPUCommonMath::Max(0, utils.getColBinIndex(layerIndex, colRangeMin)), + utils.getRowBinIndex(index_table_utils::getNormalizedPhi(rowRangeMin)), + o2::gpu::GPUCommonMath::Min(utils.getNcolBins() - 1, utils.getColBinIndex(layerIndex, colRangeMax)), + utils.getRowBinIndex(index_table_utils::getNormalizedPhi(rowRangeMax))}; +} + +/// MFT: row = y, col = x. +template +GPUhdi() int4 getBinsXY(float x, float y, const int layerIndex, + float x1, float x2, float maxDeltaCol, float maxDeltaRow, + const IndexTableUtils& utils) +{ + const float colRangeMin = o2::gpu::GPUCommonMath::Min(x1, x2) - maxDeltaCol; + const float rowRangeMin = y - maxDeltaRow; + const float colRangeMax = o2::gpu::GPUCommonMath::Max(x1, x2) + maxDeltaCol; + const float rowRangeMax = y + maxDeltaRow; + + const float colHalf = utils.getLayerColHalfExtent(layerIndex); + if (colRangeMax < -colHalf || colRangeMin > colHalf || colRangeMin > colRangeMax) { + return int4{-1, -1, -1, -1}; + } + + return int4{o2::gpu::GPUCommonMath::Max(0, utils.getColBinIndex(layerIndex, colRangeMin)), + o2::gpu::GPUCommonMath::Max(0, utils.getRowBinIndex(rowRangeMin)), + o2::gpu::GPUCommonMath::Min(utils.getNcolBins() - 1, utils.getColBinIndex(layerIndex, colRangeMax)), + utils.getRowBinIndex(rowRangeMax)}; +} + +} // namespace o2::itsmft + +#endif /* ALICEO2_ITSMFT_TRACKING_INDEXTABLEUTILS_H_ */ diff --git a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/LayerMask.h b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/LayerMask.h new file mode 100644 index 0000000000000..86a2528410f05 --- /dev/null +++ b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/LayerMask.h @@ -0,0 +1,115 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef ALICEO2_ITSMFT_TRACKING_INCLUDE_LAYERMASK_H_ +#define ALICEO2_ITSMFT_TRACKING_INCLUDE_LAYERMASK_H_ + +#include +#include + +#ifndef GPUCA_GPUCODE +#include +#include +#endif + +#include "GPUCommonDef.h" +#include "GPUCommonMath.h" +#include "ITSMFTTracking/Constants.h" + +namespace o2::itsmft::tracking +{ + +struct LayerMask { + GPUhdDefault() constexpr LayerMask() noexcept = default; + GPUhdDefault() constexpr LayerMask(uint16_t mask) noexcept : mBits{mask} {} + GPUhdDefault() constexpr LayerMask(int layer0, int layer1, int layer2) noexcept + : mBits{static_cast((uint16_t(1) << layer0) | (uint16_t(1) << layer1) | (uint16_t(1) << layer2))} + { + } + GPUhdi() constexpr operator uint16_t() const noexcept { return mBits; } + GPUhdi() constexpr uint16_t value() const noexcept { return mBits; } + GPUhdi() constexpr void set(int layer) noexcept { mBits |= (uint16_t(1) << layer); } + + GPUhdi() LayerMask operator~() const noexcept { return LayerMask{static_cast(~mBits)}; } + GPUhdi() LayerMask operator&(LayerMask other) const noexcept { return LayerMask{static_cast(mBits & other.mBits)}; } + GPUhdi() LayerMask operator|(LayerMask other) const noexcept { return LayerMask{static_cast(mBits | other.mBits)}; } + GPUhdi() LayerMask& operator&=(LayerMask other) noexcept + { + mBits &= other.mBits; + return *this; + } + GPUhdi() LayerMask& operator|=(LayerMask other) noexcept + { + mBits |= other.mBits; + return *this; + } + + GPUhdi() bool empty() const noexcept { return mBits == 0; } + GPUhdi() bool has(int layer) const noexcept { return mBits & (uint16_t(1) << layer); } + GPUhdi() bool isSubsetOf(LayerMask allowed) const noexcept { return (*this & ~allowed).empty(); } + GPUhdi() bool isAllowedHoleMask(int maxHoles, LayerMask allowedHoleMask) const noexcept + { + const int allowedHoles = maxHoles > 0 ? maxHoles : 0; + return count() <= allowedHoles && isSubsetOf(allowedHoleMask); + } + GPUhdi() bool isAllowed(int maxHoles, LayerMask allowedHoleMask) const noexcept + { + return holeMask().isAllowedHoleMask(maxHoles, allowedHoleMask); + } + GPUhdi() int length() const noexcept { return empty() ? 0 : last() - first() + 1; } + GPUhdi() int count() const noexcept { return static_cast(o2::gpu::GPUCommonMath::Popcount(mBits)); } + GPUhdi() int first() const noexcept { return mBits ? static_cast(o2::gpu::GPUCommonMath::Ctz(mBits)) : constants::UnusedIndex; } + GPUhdi() int last() const noexcept { return mBits ? 31 - static_cast(o2::gpu::GPUCommonMath::Clz(mBits)) : constants::UnusedIndex; } + GPUhdi() LayerMask holeMask() const noexcept + { + return empty() ? LayerMask{0} : (span(first(), last()) & ~(*this)); + } + + GPUhdi() int slot(int layer) const noexcept + { + if (!has(layer)) { + return constants::UnusedIndex; + } + const uint32_t lowerLayers = (uint32_t(1) << layer) - 1; + return static_cast(o2::gpu::GPUCommonMath::Popcount(static_cast(mBits) & lowerLayers)); + } + + static GPUhdi() LayerMask span(int fromLayer, int toLayer) noexcept + { + if (fromLayer > toLayer) { + return 0; + } + const uint32_t upper = (uint32_t(1) << (toLayer + 1)) - 1; + const uint32_t lower = (uint32_t(1) << fromLayer) - 1; + return static_cast(upper & ~lower); + } + + static GPUhdi() LayerMask skipped(int fromLayer, int toLayer) noexcept + { + return (toLayer - fromLayer <= 1) ? LayerMask{0} : span(fromLayer + 1, toLayer - 1); + } + +#ifndef GPUCA_GPUCODE + std::string asString() const { return fmt::format("{:016b}", mBits); } +#endif + + private: + uint16_t mBits{0}; +}; + +static_assert(std::is_standard_layout_v); +static_assert(std::is_trivially_copyable_v); +static_assert(sizeof(LayerMask) == sizeof(uint16_t)); +static_assert(alignof(LayerMask) == alignof(uint16_t)); + +} // namespace o2::itsmft::tracking + +#endif diff --git a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/MathUtils.h b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/MathUtils.h new file mode 100644 index 0000000000000..2ab2df14d0489 --- /dev/null +++ b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/MathUtils.h @@ -0,0 +1,126 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// +/// \file MathUtils.h +/// \brief +/// + +#ifndef O2_ITSMFT_TRACKING_MATHUTILS_H_ +#define O2_ITSMFT_TRACKING_MATHUTILS_H_ + +#include "CommonConstants/MathConstants.h" +#include "ITSMFTTracking/Constants.h" +#include "MathUtils/Utils.h" +#include "GPUCommonMath.h" +#include "GPUCommonDef.h" + +namespace o2::itsmft::tracking::math_utils +{ + +GPUhdi() float computePhi(float x, float y) +{ + return o2::math_utils::fastATan2(-y, -x) + o2::constants::math::PI; +} + +GPUhdi() float hypot(float x, float y) +{ + return o2::gpu::CAMath::Hypot(x, y); +} + +GPUhdi() float getNormalizedPhi(float phi) +{ + phi -= o2::constants::math::TwoPI * o2::gpu::CAMath::Floor(phi * (1.f / o2::constants::math::TwoPI)); + return phi; +} + +GPUhdi() float computeCurvature(float x1, float y1, float x2, float y2, float x3, float y3) +{ + // in case the triangle is degenerate we return infinite curvature. + const float area = ((x2 - x1) * (y3 - y1)) - ((x3 - x1) * (y2 - y1)); + if (o2::gpu::CAMath::Abs(area) < constants::Tolerance) { + return o2::constants::math::Almost0; + } + const float dx1 = x2 - x1, dy1 = y2 - y1; + const float dx2 = x3 - x2, dy2 = y3 - y2; + const float dx3 = x1 - x3, dy3 = y1 - y3; + const float d1 = o2::gpu::CAMath::Sqrt((dx1 * dx1) + (dy1 * dy1)); + const float d2 = o2::gpu::CAMath::Sqrt((dx2 * dx2) + (dy2 * dy2)); + const float d3 = o2::gpu::CAMath::Sqrt((dx3 * dx3) + (dy3 * dy3)); + return -2.f * area / (d1 * d2 * d3); +} + +GPUhdi() float computeCurvatureCentreX(float x1, float y1, float x2, float y2, float x3, float y3) +{ + // in case the triangle is degenerate we return set the centre to infinity. + float dx21 = x2 - x1, dx32 = x3 - x2; + if (o2::gpu::CAMath::Abs(dx21) < o2::itsmft::tracking::constants::Tolerance || + o2::gpu::CAMath::Abs(dx32) < o2::itsmft::tracking::constants::Tolerance) { // add small offset + x2 += 1e-4; + dx21 = x2 - x1; + dx32 = x3 - x2; + } + const float k1 = (y2 - y1) / dx21, k2 = (y3 - y2) / dx32; + if (o2::gpu::CAMath::Abs(k2 - k1) < o2::itsmft::tracking::constants::Tolerance) { + return o2::constants::math::VeryBig; + } + return 0.5f * (k1 * k2 * (y1 - y3) + k2 * (x1 + x2) - k1 * (x2 + x3)) / (k2 - k1); +} + +GPUhdi() float computeTanDipAngle(float x1, float y1, float x2, float y2, float z1, float z2) +{ + // in case the points vertically align we go to pos/neg infinity. + const float d = o2::gpu::CAMath::Hypot(x1 - x2, y1 - y2); + if (o2::gpu::CAMath::Abs(d) < o2::itsmft::tracking::constants::Tolerance) { + return ((z1 > z2) ? -1.f : 1.f) * o2::constants::math::VeryBig; + } + return (z1 - z2) / d; +} + +GPUhdi() float smallestAngleDifference(float a, float b) +{ + return o2::gpu::CAMath::Remainderf(b - a, o2::constants::math::TwoPI); +} + +GPUhdi() bool isPhiDifferenceBelow(const float phiA, const float phiB, const float phiCut) +{ + const float deltaPhi = o2::gpu::CAMath::Abs(phiA - phiB); + return deltaPhi < phiCut || deltaPhi > o2::constants::math::TwoPI - phiCut; +} + +GPUhdi() constexpr float Sq(float v) +{ + return v * v; +} + +GPUhdi() constexpr float SqSum(float v, float w) +{ + return Sq(v) + Sq(w); +} + +GPUhdi() constexpr float SqSum(float u, float v, float w) +{ + return Sq(u) + SqSum(v, w); +} + +GPUhdi() constexpr float SqDiff(float x, float y) +{ + return Sq(x - y); +} + +GPUhdi() float MSangle(float mass, float p, float xX0) +{ + float beta = p / o2::gpu::CAMath::Hypot(mass, p); + return 0.0136f * o2::gpu::CAMath::Sqrt(xX0) * (1.f + 0.038f * o2::gpu::CAMath::Log(xX0)) / (beta * p); +} + +} // namespace o2::itsmft::tracking::math_utils + +#endif diff --git a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/ROFLookupTables.h b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/ROFLookupTables.h new file mode 100644 index 0000000000000..05fecb88d675a --- /dev/null +++ b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/ROFLookupTables.h @@ -0,0 +1,849 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef ALICEO2_ITSMFT_TRACKING_INCLUDE_ROFOVERLAPTABLE_H_ +#define ALICEO2_ITSMFT_TRACKING_INCLUDE_ROFOVERLAPTABLE_H_ + +#include +#include +#include +#include +#include +#include + +#ifndef GPUCA_GPUCODE +#include +#include "Framework/Logger.h" +#endif + +#include "CommonConstants/LHCConstants.h" +#include "CommonDataFormat/RangeReference.h" +#include "ITSMFTTracking/Types.h" +#include "GPUCommonMath.h" +#include "GPUCommonDef.h" + +namespace o2::itsmft::tracking +{ + +// Layer timing definition +struct LayerTiming { + using BCType = TimeStampType; + BCType mNROFsTF{0}; // number of ROFs per timeframe + BCType mROFLength{0}; // ROF length in BC + BCType mROFDelay{0}; // delay of ROFs wrt start of first orbit in TF in BC + BCType mROFBias{0}; // bias wrt to the LHC clock in BC + BCType mROFAddTimeErr{0}; // additionally imposed uncertainty on ROF time in BC + + // return start of ROF in BC + // this does not account for the opt. error! + GPUhdi() BCType getROFStartInBC(BCType rofId) const noexcept + { + assert(rofId < mNROFsTF && rofId >= 0); + return (mROFLength * rofId) + mROFDelay + mROFBias; + } + + // return end of ROF in BCs + // this does not account for the opt. error! + GPUhdi() BCType getROFEndInBC(BCType rofId) const noexcept + { + assert(rofId < mNROFsTF); + return getROFStartInBC(rofId) + mROFLength; + } + + // return (clamped) time-interval of rof + GPUhdi() TimeEstBC getROFTimeBounds(BCType rofId, bool withError = false) const noexcept + { + if (withError) { + int64_t start = getROFStartInBC(rofId); + int64_t end = getROFEndInBC(rofId); + start = o2::gpu::CAMath::Max(start - mROFAddTimeErr, int64_t(0)); + end += mROFAddTimeErr; + return {static_cast(start), static_cast(end - start)}; + } + return {getROFStartInBC(rofId), static_cast(mROFLength)}; + } + + // return which ROF this BC belongs to + GPUhi() BCType getROF(BCType bc) const noexcept + { + const BCType offset = mROFDelay + mROFBias; + if (bc <= offset) { + return 0; + } + return (bc - offset) / mROFLength; + } + + // return which ROF this timestamp belongs by its lower edge + GPUhi() BCType getROF(TimeStamp ts) const noexcept + { + const BCType offset = mROFDelay + mROFBias; + const BCType bc = (ts.getTimeStamp() < ts.getTimeStampError()) ? BCType(0) : static_cast(o2::gpu::CAMath::Floor(ts.getTimeStamp() - ts.getTimeStampError())); + if (bc <= offset) { + return 0; + } + return (bc - offset) / mROFLength; + } + +#ifndef GPUCA_GPUCODE + GPUh() std::string asString() const + { + return std::format("NROFsPerTF {:4} ROFLength {:4} ({:4} per Orbit) ROFDelay {:4} ROFBias {:4} ROFAddTimeErr {:4}", mNROFsTF, mROFLength, (o2::constants::lhc::LHCMaxBunches / mROFLength), mROFDelay, mROFBias, mROFAddTimeErr); + } + + GPUh() void print() const + { + LOG(info) << asString(); + } +#endif +}; + +// Base class for lookup to define layers +template +class LayerTimingBase +{ + protected: + LayerTiming mLayers[NLayers]; + + public: + using T = LayerTiming::BCType; + LayerTimingBase() = default; + + GPUh() void defineLayer(int32_t layer, T nROFsTF, T rofLength, T rofDelay, T rofBias, T rofTE) + { + assert(layer >= 0 && layer < NLayers); + mLayers[layer] = {nROFsTF, rofLength, rofDelay, rofBias, rofTE}; + } + + GPUh() void defineLayer(int32_t layer, const LayerTiming& timing) + { + assert(layer >= 0 && layer < NLayers); + mLayers[layer] = timing; + } + + GPUhdi() const LayerTiming& getLayer(int32_t layer) const + { + assert(layer >= 0 && layer < NLayers); + return mLayers[layer]; + } + + GPUhdi() constexpr int32_t getEntries() noexcept { return NLayers; } + +#ifndef GPUCA_GPUCODE + GPUh() void print() const + { + LOGP(info, "Imposed time structure:"); + for (int32_t iL{0}; iL < NLayers; ++iL) { + LOGP(info, "\tLayer:{} {}", iL, mLayers[iL].asString()); + } + } +#endif +}; + +// GPU friendly view of the table below +template +struct ROFOverlapTableView { + const TableEntry* mFlatTable{nullptr}; + const TableIndex* mIndices{nullptr}; + const LayerTiming* mLayers{nullptr}; + + GPUhdi() const LayerTiming& getLayer(int32_t layer) const noexcept + { + assert(layer >= 0 && layer < NLayers); + return mLayers[layer]; + } + + GPUh() int32_t getClock() const noexcept + { + // we take the fastest layer as clock + int32_t fastest = 0; + uint32_t maxNROFs{0}; + for (int32_t iL{0}; iL < NLayers; ++iL) { + const auto& layer = getLayer(iL); + // by definition the fastest layer has the most ROFs + // this also solves the problem of a delay large than ROFLength + // if mNROFsTF is correct + if (layer.mNROFsTF > maxNROFs) { + fastest = iL; + maxNROFs = layer.mNROFsTF; + } + } + return fastest; + } + + GPUh() const LayerTiming& getClockLayer() const noexcept + { + return mLayers[getClock()]; + } + + GPUhdi() const TableEntry& getOverlap(int32_t from, int32_t to, size_t rofIdx) const noexcept + { + assert(from < NLayers && to < NLayers); + const size_t linearIdx = (from * NLayers) + to; + const auto& idx = mIndices[linearIdx]; + assert(rofIdx < idx.getEntries()); + return mFlatTable[idx.getFirstEntry() + rofIdx]; + } + + GPUhdi() bool doROFsOverlap(int32_t layer0, size_t rof0, int32_t layer1, size_t rof1) const noexcept + { + if (layer0 == layer1) { // layer is compatible with itself + return rof0 == rof1; + } + + assert(layer0 < NLayers && layer1 < NLayers); + const size_t linearIdx = (layer0 * NLayers) + layer1; + const auto& idx = mIndices[linearIdx]; + + if (rof0 >= idx.getEntries()) { + return false; + } + + const auto& overlap = mFlatTable[idx.getFirstEntry() + rof0]; + + if (overlap.getEntries() == 0) { + return false; + } + + const size_t firstCompatible = overlap.getFirstEntry(); + const size_t lastCompatible = firstCompatible + overlap.getEntries() - 1; + return rof1 >= firstCompatible && rof1 <= lastCompatible; + } + + GPUhdi() TimeEstBC getTimeStamp(int32_t layer0, size_t rof0, int32_t layer1, size_t rof1) const noexcept + { + assert(layer0 < NLayers && layer1 < NLayers); + assert(doROFsOverlap(layer0, rof0, layer1, rof1)); + // retrieves the combined timestamp + // e.g., taking one cluster from rof0 and one from rof1 + // and constructing a tracklet (doublet) what is its time + // this assumes that the rofs overlap, e.g. doROFsOverlap -> true + // get timestamp including margins from rof0 and rof1 + const auto t0 = mLayers[layer0].getROFTimeBounds(rof0, true); + const auto t1 = mLayers[layer1].getROFTimeBounds(rof1, true); + return t0 + t1; + } + +#ifndef GPUCA_GPUCODE + /// Print functions + GPUh() void printAll() const + { + for (int32_t i = 0; i < NLayers; ++i) { + for (int32_t j = 0; j < NLayers; ++j) { + if (i != j) { + printMapping(i, j); + } + } + } + printSummary(); + } + + GPUh() void printMapping(int32_t from, int32_t to) const + { + if (from == to) { + LOGP(error, "No self-lookup supported"); + return; + } + + constexpr int w_index = 10; + constexpr int w_first = 12; + constexpr int w_last = 12; + constexpr int w_count = 10; + + LOGF(info, "Overlap mapping: Layer %d -> Layer %d", from, to); + LOGP(info, "From: {}", mLayers[from].asString()); + LOGP(info, "To : {}", mLayers[to].asString()); + LOGF(info, "%*s | %*s | %*s | %*s", w_index, "ROF.index", w_first, "First.ROF", w_last, "Last.ROF", w_count, "Count"); + LOGF(info, "%.*s-+-%.*s-+-%.*s-+-%.*s", w_index, "----------", w_first, "------------", w_last, "------------", w_count, "----------"); + + const size_t linearIdx = (from * NLayers) + to; + const auto& idx = mIndices[linearIdx]; + for (int32_t i = 0; i < idx.getEntries(); ++i) { + const auto& overlap = getOverlap(from, to, i); + LOGF(info, "%*d | %*d | %*d | %*d", w_index, i, w_first, overlap.getFirstEntry(), w_last, overlap.getEntriesBound() - 1, w_count, overlap.getEntries()); + } + } + + GPUh() void printSummary() const + { + uint32_t totalEntries{0}; + size_t flatTableSize{0}; + + for (int32_t i = 0; i < NLayers; ++i) { + for (int32_t j = 0; j < NLayers; ++j) { + if (i != j) { + const size_t linearIdx = (i * NLayers) + j; + const auto& idx = mIndices[linearIdx]; + totalEntries += idx.getEntries(); + flatTableSize += idx.getEntries(); + } + } + } + + for (int32_t i = 0; i < NLayers; ++i) { + mLayers[i].print(); + } + + const uint32_t totalBytes = (flatTableSize * sizeof(TableEntry)) + (static_cast(NLayers * NLayers) * sizeof(TableIndex)); + LOGF(info, "------------------------------------------------------------"); + LOGF(info, "Total overlap table size: %u entries", totalEntries); + LOGF(info, "Flat table size: %zu entries", flatTableSize); + LOGF(info, "Total view size: %u bytes", totalBytes); + LOGF(info, "------------------------------------------------------------"); + } +#endif +}; + +// Precalculated lookup table to find overlapping ROFs in another layer given a ROF index in the current layer +template +class ROFOverlapTable : public LayerTimingBase +{ + public: + using T = LayerTimingBase::T; + using TableEntry = dataformats::RangeReference; + using TableIndex = dataformats::RangeReference; + + using View = ROFOverlapTableView; + ROFOverlapTable() = default; + + GPUh() void init() + { + std::vector table[NLayers][NLayers]; + for (int32_t i{0}; i < NLayers; ++i) { + for (int32_t j{0}; j < NLayers; ++j) { + if (i != j) { // we do not need self-lookup + buildMapping(i, j, table[i][j]); + } + } + } + flatten(table); + } + + GPUh() View getView() const + { + View view; + view.mFlatTable = mFlatTable.data(); + view.mIndices = mIndices; + view.mLayers = this->mLayers; + return view; + } + + GPUh() View getDeviceView(const TableEntry* deviceFlatTablePtr, const TableIndex* deviceIndicesPtr, const LayerTiming* deviceLayerTimingPtr) const + { + View view; + view.mFlatTable = deviceFlatTablePtr; + view.mIndices = deviceIndicesPtr; + view.mLayers = deviceLayerTimingPtr; + return view; + } + + GPUh() size_t getFlatTableSize() const noexcept { return mFlatTable.size(); } + static GPUh() constexpr size_t getIndicesSize() { return static_cast(NLayers * NLayers); } + + private: + GPUh() void buildMapping(int32_t from, int32_t to, std::vector& table) + { + const auto& layerFrom = this->mLayers[from]; + const auto& layerTo = this->mLayers[to]; + table.resize(layerFrom.mNROFsTF); + + for (int32_t iROF{0}; iROF < layerFrom.mNROFsTF; ++iROF) { + int64_t fromStart = o2::gpu::CAMath::Max((int64_t)layerFrom.getROFStartInBC(iROF) - (int64_t)layerFrom.mROFAddTimeErr, int64_t(0)); + int64_t fromEnd = (int64_t)layerFrom.getROFEndInBC(iROF) + layerFrom.mROFAddTimeErr; + + int32_t firstROFTo = o2::gpu::CAMath::Max(0, (int32_t)((fromStart - (int64_t)layerTo.mROFAddTimeErr - (int64_t)layerTo.mROFDelay - (int64_t)layerTo.mROFBias) / (int64_t)layerTo.mROFLength)); + auto lastROFTo = (int32_t)((fromEnd + (int64_t)layerTo.mROFAddTimeErr - (int64_t)layerTo.mROFDelay - (int64_t)layerTo.mROFBias - 1) / (int64_t)layerTo.mROFLength); + firstROFTo = o2::gpu::CAMath::Max(0, firstROFTo); + lastROFTo = o2::gpu::CAMath::Min((int32_t)layerTo.mNROFsTF - 1, lastROFTo); + + while (firstROFTo <= lastROFTo) { + int64_t toStart = o2::gpu::CAMath::Max((int64_t)layerTo.getROFStartInBC(firstROFTo) - (int64_t)layerTo.mROFAddTimeErr, int64_t(0)); + int64_t toEnd = (int64_t)layerTo.getROFEndInBC(firstROFTo) + layerTo.mROFAddTimeErr; + if (toEnd > fromStart && toStart < fromEnd) { + break; + } + ++firstROFTo; + } + while (lastROFTo >= firstROFTo) { + int64_t toStart = o2::gpu::CAMath::Max((int64_t)layerTo.getROFStartInBC(lastROFTo) - (int64_t)layerTo.mROFAddTimeErr, int64_t(0)); + int64_t toEnd = (int64_t)layerTo.getROFEndInBC(lastROFTo) + layerTo.mROFAddTimeErr; + if (toEnd > fromStart && toStart < fromEnd) { + break; + } + --lastROFTo; + } + int32_t count = (firstROFTo <= lastROFTo) ? (lastROFTo - firstROFTo + 1) : 0; + table[iROF] = {static_cast(firstROFTo), static_cast(count)}; + } + } + + GPUh() void flatten(const std::vector table[NLayers][NLayers]) + { + size_t total{0}; + for (int32_t i{0}; i < NLayers; ++i) { + for (int32_t j{0}; j < NLayers; ++j) { + if (i != j) { // we do not need self-lookup + total += table[i][j].size(); + } + } + } + + mFlatTable.reserve(total); + + for (int32_t i{0}; i < NLayers; ++i) { + for (int32_t j{0}; j < NLayers; ++j) { + size_t idx = (i * NLayers) + j; + if (i != j) { + mIndices[idx].setFirstEntry(static_cast(mFlatTable.size())); + mIndices[idx].setEntries(static_cast(table[i][j].size())); + mFlatTable.insert(mFlatTable.end(), table[i][j].begin(), table[i][j].end()); + } else { + mIndices[idx] = {0, 0}; + } + } + } + } + + TableIndex mIndices[NLayers * NLayers]; + std::vector mFlatTable; +}; + +// GPU friendly view of the table below +template +struct ROFVertexLookupTableView { + const TableEntry* mFlatTable{nullptr}; + const TableIndex* mIndices{nullptr}; + const LayerTiming* mLayers{nullptr}; + + GPUhdi() const LayerTiming& getLayer(int32_t layer) const noexcept + { + assert(layer >= 0 && layer < NLayers); + return mLayers[layer]; + } + + GPUhdi() const TableEntry& getVertices(int32_t layer, size_t rofIdx) const noexcept + { + assert(layer < NLayers); + const auto& idx = mIndices[layer]; + assert(rofIdx < idx.getEntries()); + return mFlatTable[idx.getFirstEntry() + rofIdx]; + } + + GPUh() int32_t getMaxVerticesPerROF() const noexcept + { + int32_t maxCount = 0; + for (int32_t layer = 0; layer < NLayers; ++layer) { + const auto& idx = mIndices[layer]; + for (int32_t i = 0; i < idx.getEntries(); ++i) { + const auto& entry = mFlatTable[idx.getFirstEntry() + i]; + maxCount = o2::gpu::CAMath::Max(maxCount, static_cast(entry.getEntries())); + } + } + return maxCount; + } + + // Check if a specific vertex is compatible with a given ROF + GPUhdi() bool isVertexCompatible(int32_t layer, size_t rofIdx, const Vertex& vertex) const noexcept + { + assert(layer < NLayers); + const auto& layerDef = mLayers[layer]; + int64_t rofLower = o2::gpu::CAMath::Max((int64_t)layerDef.getROFStartInBC(rofIdx) - (int64_t)layerDef.mROFAddTimeErr, int64_t(0)); + int64_t rofUpper = (int64_t)layerDef.getROFEndInBC(rofIdx) + layerDef.mROFAddTimeErr; + auto vLower = (int64_t)vertex.getTimeStamp().lower(); + auto vUpper = (int64_t)vertex.getTimeStamp().upper(); + return vUpper >= rofLower && vLower < rofUpper; + } + +#ifndef GPUCA_GPUCODE + GPUh() void printAll() const + { + for (int32_t i = 0; i < NLayers; ++i) { + printLayer(i); + } + printSummary(); + } + + GPUh() void printLayer(int32_t layer) const + { + constexpr int w_rof = 10; + constexpr int w_first = 12; + constexpr int w_last = 12; + constexpr int w_count = 10; + + LOGF(info, "Vertex lookup: Layer %d", layer); + LOGF(info, "%*s | %*s | %*s | %*s", w_rof, "ROF.index", w_first, "First.Vtx", w_last, "Last.Vtx", w_count, "Count"); + LOGF(info, "%.*s-+-%.*s-+-%.*s-+-%.*s", w_rof, "----------", w_first, "------------", w_last, "------------", w_count, "----------"); + + const auto& idx = mIndices[layer]; + for (int32_t i = 0; i < idx.getEntries(); ++i) { + const auto& entry = mFlatTable[idx.getFirstEntry() + i]; + int first = entry.getFirstEntry(); + int count = entry.getEntries(); + int last = first + count - 1; + LOGF(info, "%*d | %*d | %*d | %*d", w_rof, i, w_first, first, w_last, last, w_count, count); + } + } + + GPUh() void printSummary() const + { + uint32_t totalROFs{0}; + uint32_t totalVertexRefs{0}; + + for (int32_t i = 0; i < NLayers; ++i) { + const auto& idx = mIndices[i]; + totalROFs += idx.getEntries(); + + for (int32_t j = 0; j < idx.getEntries(); ++j) { + const auto& entry = mFlatTable[idx.getFirstEntry() + j]; + totalVertexRefs += entry.getEntries(); + } + } + + const uint32_t totalBytes = (totalROFs * sizeof(TableEntry)) + (NLayers * sizeof(TableIndex)); + LOGF(info, "------------------------------------------------------------"); + LOGF(info, "Total ROFs in table: %u", totalROFs); + LOGF(info, "Total vertex references: %u", totalVertexRefs); + LOGF(info, "Total view size: %u bytes", totalBytes); + LOGF(info, "------------------------------------------------------------"); + } +#endif +}; + +// Precalculated lookup table to find vertices compatible with ROFs +// Given a layer and ROF index, returns the range of vertices that overlap in time. +// The vertex time is defined as symmetrical [t0-e,t0+e] +// It needs to be guaranteed that the input vertices are sorted by their lower-bound! +// additionally compatibliyty has to be queried per vertex! +template +class ROFVertexLookupTable : public LayerTimingBase +{ + public: + using T = LayerTimingBase::T; + using BCType = LayerTiming::BCType; + using TableEntry = dataformats::RangeReference; + using TableIndex = dataformats::RangeReference; + using View = ROFVertexLookupTableView; + + ROFVertexLookupTable() = default; + + GPUh() size_t getFlatTableSize() const noexcept { return mFlatTable.size(); } + static GPUh() constexpr size_t getIndicesSize() { return NLayers; } + + // Build the lookup table given a sorted array of vertices + // vertices must be sorted by timestamp, then by error (secondary) + GPUh() void init(const Vertex* vertices, size_t nVertices) + { + if (nVertices > std::numeric_limits::max()) { + LOGF(fatal, "too many vertices %zu, max supported is %u", nVertices, std::numeric_limits::max()); + } + + std::vector table[NLayers]; + for (int32_t layer{0}; layer < NLayers; ++layer) { + buildMapping(layer, vertices, nVertices, table[layer]); + } + flatten(table); + } + + // Pre-allocated needed memory, then use update(...) + GPUh() void init() + { + size_t total{0}; + for (int32_t layer{0}; layer < NLayers; ++layer) { + total += this->mLayers[layer].mNROFsTF; + } + mFlatTable.resize(total, {0, 0}); + size_t offset = 0; + for (int32_t layer{0}; layer < NLayers; ++layer) { + size_t nROFs = this->mLayers[layer].mNROFsTF; + mIndices[layer].setFirstEntry(static_cast(offset)); + mIndices[layer].setEntries(static_cast(nROFs)); + offset += nROFs; + } + } + + // Recalculate lookup table with new vertices + GPUh() void update(const Vertex* vertices, size_t nVertices) + { + size_t offset = 0; + for (int32_t layer{0}; layer < NLayers; ++layer) { + const auto& idx = mIndices[layer]; + size_t nROFs = idx.getEntries(); + for (size_t iROF = 0; iROF < nROFs; ++iROF) { + updateROFMapping(layer, iROF, vertices, nVertices, offset + iROF); + } + offset += nROFs; + } + } + + GPUh() View getView() const + { + View view; + view.mFlatTable = mFlatTable.data(); + view.mIndices = mIndices; + view.mLayers = this->mLayers; + return view; + } + + GPUh() View getDeviceView(const TableEntry* deviceFlatTablePtr, const TableIndex* deviceIndicesPtr, const LayerTiming* deviceLayerTimingPtr) const + { + View view; + view.mFlatTable = deviceFlatTablePtr; + view.mIndices = deviceIndicesPtr; + view.mLayers = deviceLayerTimingPtr; + return view; + } + + private: + // Build the mapping for one layer + GPUh() void buildMapping(int32_t layer, const Vertex* vertices, size_t nVertices, std::vector& table) + { + const auto& layerDef = this->mLayers[layer]; + table.resize(layerDef.mNROFsTF); + size_t vertexSearchStart = 0; + for (int32_t iROF{0}; iROF < layerDef.mNROFsTF; ++iROF) { + int64_t rofLower = o2::gpu::CAMath::Max((int64_t)layerDef.getROFStartInBC(iROF) - (int64_t)layerDef.mROFAddTimeErr, int64_t(0)); + int64_t rofUpper = (int64_t)layerDef.getROFEndInBC(iROF) + layerDef.mROFAddTimeErr; + size_t lastVertex = binarySearchFirst(vertices, nVertices, vertexSearchStart, rofUpper); + size_t firstVertex = vertexSearchStart; + while (firstVertex < lastVertex) { + auto vUpper = (int64_t)vertices[firstVertex].getTimeStamp().upper(); + if (vUpper > rofLower) { + break; + } + ++firstVertex; + } + size_t count = (lastVertex > firstVertex) ? (lastVertex - firstVertex) : 0; + table[iROF] = {static_cast(firstVertex), static_cast(count)}; + vertexSearchStart = firstVertex; + } + } + + // Update a single ROF's vertex mapping + GPUh() void updateROFMapping(int32_t layer, size_t iROF, const Vertex* vertices, size_t nVertices, size_t flatTableIdx) + { + const auto& layerDef = this->mLayers[layer]; + int64_t rofLower = o2::gpu::CAMath::Max((int64_t)layerDef.getROFStartInBC(iROF) - (int64_t)layerDef.mROFAddTimeErr, int64_t(0)); + int64_t rofUpper = (int64_t)layerDef.getROFEndInBC(iROF) + layerDef.mROFAddTimeErr; + size_t lastVertex = binarySearchFirst(vertices, nVertices, 0, rofUpper); + size_t firstVertex = 0; + while (firstVertex < lastVertex) { + int64_t vUpper = (int64_t)vertices[firstVertex].getTimeStamp().getTimeStamp() + + (int64_t)vertices[firstVertex].getTimeStamp().getTimeStampError(); + if (vUpper > rofLower) { + break; + } + ++firstVertex; + } + size_t count = (lastVertex > firstVertex) ? (lastVertex - firstVertex) : 0; + mFlatTable[flatTableIdx].setFirstEntry(static_cast(firstVertex)); + mFlatTable[flatTableIdx].setEntries(static_cast(count)); + } + + // Binary search for first vertex where maxBC >= targetBC + GPUh() size_t binarySearchFirst(const Vertex* vertices, size_t nVertices, size_t searchStart, BCType targetBC) const + { + size_t left = searchStart; + size_t right = nVertices; + while (left < right) { + size_t mid = left + ((right - left) / 2); + int64_t lower = (int64_t)vertices[mid].getTimeStamp().getTimeStamp() - + (int64_t)vertices[mid].getTimeStamp().getTimeStampError(); + if (lower < targetBC) { + left = mid + 1; + } else { + right = mid; + } + } + return left; + } + + // Compress the temporary table into a single flat table + GPUh() void flatten(const std::vector table[NLayers]) + { + // Count total entries + size_t total{0}; + for (int32_t i{0}; i < NLayers; ++i) { + total += table[i].size(); + } + + mFlatTable.reserve(total); + + // Build flat table and indices + for (int32_t i{0}; i < NLayers; ++i) { + mIndices[i].setFirstEntry(static_cast(mFlatTable.size())); + mIndices[i].setEntries(static_cast(table[i].size())); + mFlatTable.insert(mFlatTable.end(), table[i].begin(), table[i].end()); + } + } + + TableIndex mIndices[NLayers]; + std::vector mFlatTable; +}; + +// GPU-friendly view of the ROF mask table +template +struct ROFMaskTableView { + const TableEntry* mFlatMask{nullptr}; + const TableIndex* mLayerROFOffsets{nullptr}; // size NLayers+1 + + GPUhdi() bool isROFEnabled(int32_t layer, int32_t rofId) const noexcept + { + assert(layer >= 0 && layer < NLayers); + return mFlatMask[mLayerROFOffsets[layer] + rofId] != 0u; + } + +#ifndef GPUCA_GPUCODE + GPUh() void printAll() const + { + for (int32_t i = 0; i < NLayers; ++i) { + printLayer(i); + } + } + + GPUh() void printLayer(int32_t layer) const + { + constexpr int w_rof = 10; + constexpr int w_active = 10; + int32_t nROFs = mLayerROFOffsets[layer + 1] - mLayerROFOffsets[layer]; + LOGF(info, "Mask table: Layer %d", layer); + LOGF(info, "%*s | %*s", w_rof, "ROF", w_active, "Enabled"); + LOGF(info, "%.*s-+-%.*s", w_rof, "----------", w_active, "----------"); + for (int32_t i = 0; i < nROFs; ++i) { + LOGF(info, "%*d | %*d", w_rof, i, w_active, (int)isROFEnabled(layer, i)); + } + } + + GPUh() std::string asString(int32_t layer) const + { + int32_t nROFs = mLayerROFOffsets[layer + 1] - mLayerROFOffsets[layer]; + int32_t enabledROFs = 0; + for (int32_t j = 0; j < nROFs; ++j) { + if (isROFEnabled(layer, j)) { + ++enabledROFs; + } + } + return std::format("ROFMask on Layer {} ROFs enabled: {}/{}", layer, enabledROFs, nROFs); + } + + GPUh() void print(int32_t layer) const + { + LOG(info) << asString(layer); + } +#endif +}; + +// Per-ROF per-layer boolean mask (uint8_t for GPU compatibility). +template +class ROFMaskTable : public LayerTimingBase +{ + public: + using T = LayerTimingBase::T; + using BCRange = dataformats::RangeReference; + using TableIndex = uint32_t; + using TableEntry = uint8_t; + using View = ROFMaskTableView; + + ROFMaskTable() = default; + GPUh() explicit ROFMaskTable(const LayerTimingBase& timingBase) : LayerTimingBase(timingBase) { init(); } + + GPUh() void init() + { + int32_t totalROFs = 0; + for (int32_t layer{0}; layer < NLayers; ++layer) { + mLayerROFOffsets[layer] = totalROFs; + totalROFs += this->getLayer(layer).mNROFsTF; + } + mLayerROFOffsets[NLayers] = totalROFs; // sentinel + mFlatMask.resize(totalROFs, 0u); + } + + GPUh() size_t getFlatMaskSize() const noexcept { return mFlatMask.size(); } + + GPUh() void setROFEnabled(int32_t layer, int32_t rofId, uint8_t state = 1) noexcept + { + assert(layer >= 0 && layer < NLayers); + assert(rofId >= 0 && rofId < mLayerROFOffsets[layer + 1] - mLayerROFOffsets[layer]); + mFlatMask[mLayerROFOffsets[layer] + rofId] = state; + } + + GPUh() void setROFsEnabled(int32_t layer, int32_t firstRof, int32_t nRofs, uint8_t state = 1) noexcept + { + assert(layer >= 0 && layer < NLayers); + assert(firstRof >= 0); + assert(firstRof + nRofs <= mLayerROFOffsets[layer + 1] - mLayerROFOffsets[layer]); + std::memset(mFlatMask.data() + mLayerROFOffsets[layer] + firstRof, state, nRofs); + } + + // Enable all ROFs in all layers that are time-compatible with the given BC range + GPUh() void selectROF(const BCRange& t) + { + const int32_t bcStart = t.getFirstEntry(); + const int32_t bcEnd = t.getEntriesBound(); + for (int32_t layer{0}; layer < NLayers; ++layer) { + const auto& lay = this->getLayer(layer); + const int32_t offset = mLayerROFOffsets[layer]; + for (int32_t rofId{0}; rofId < lay.mNROFsTF; ++rofId) { + if (static_cast(lay.getROFStartInBC(rofId)) < bcEnd && + static_cast(lay.getROFEndInBC(rofId)) > bcStart) { + mFlatMask[offset + rofId] = 1u; + } + } + } + } + + // Reset mask to 0, then enable all ROFs compatible with any of the given BC ranges + GPUh() void selectROFs(const std::vector& ts) + { + resetMask(); + for (const auto& t : ts) { + selectROF(t); + } + } + + GPUh() void resetMask(uint8_t s = 0u) + { + std::memset(mFlatMask.data(), s, mFlatMask.size()); + } + + GPUh() void invertMask() + { + std::ranges::transform(mFlatMask, mFlatMask.begin(), [](uint8_t x) { return 1 - x; }); + } + + GPUh() void swap(ROFMaskTable& other) noexcept + { + std::swap(mFlatMask, other.mFlatMask); + std::swap(mLayerROFOffsets, other.mLayerROFOffsets); + } + + GPUh() View getView() const + { + View view; + view.mFlatMask = mFlatMask.data(); + view.mLayerROFOffsets = mLayerROFOffsets; + return view; + } + + GPUh() View getDeviceView(const TableEntry* deviceFlatMaskPtr, const TableIndex* deviceOffsetPtr) const + { + View view; + view.mFlatMask = deviceFlatMaskPtr; + view.mLayerROFOffsets = deviceOffsetPtr; + return view; + } + + private: + TableIndex mLayerROFOffsets[NLayers + 1] = {0}; + std::vector mFlatMask; +}; + +} // namespace o2::itsmft::tracking + +#endif diff --git a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/TimeFrame.h b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/TimeFrame.h new file mode 100644 index 0000000000000..334c994f25b2e --- /dev/null +++ b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/TimeFrame.h @@ -0,0 +1,610 @@ +// 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_ITSMFT_TRACKING_TIMEFRAME_H_ +#define ALICEO2_ITSMFT_TRACKING_TIMEFRAME_H_ + +#include +#include +#include +#include +#include +#include + +#include "ITSMFTTracking/Types.h" +#include "ITSMFTTracking/Cell.h" +#include "ITSMFTTracking/Cluster.h" +#include "ITSMFTTracking/Configuration.h" +#include "ITSMFTTracking/ClusterLines.h" +#include "ITSMFTTracking/Tracklet.h" +#include "ITSMFTTracking/IndexTableUtils.h" +#include "ITSMFTTracking/ExternalAllocator.h" +#include "ITSMFTTracking/BoundedAllocator.h" +#include "ITSMFTTracking/ROFLookupTables.h" +#include "ITSMFTTracking/TrackingTopology.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "SimulationDataFormat/MCTruthContainer.h" + +#include "DetectorsBase/Propagator.h" +#include "DetectorsCommonDataFormats/DetID.h" + +namespace o2 +{ +namespace gpu +{ +class GPUChainITS; +} + +namespace itsmft +{ +class CompClusterExt; +class TopologyDictionary; +class ROFRecord; +} // namespace itsmft + +namespace itsmft::tracking +{ + +template +struct TimeFrame { + using IndexTableUtilsN = IndexTableUtils; + using ROFOverlapTableN = ROFOverlapTable; + using ROFVertexLookupTableN = ROFVertexLookupTable; + using ROFMaskTableN = ROFMaskTable; + using TrackingTopologyN = TrackingTopology; + using TrackSeedN = TrackSeed; + + TimeFrame() = default; + virtual ~TimeFrame() = default; + + const Vertex& getPrimaryVertex(const int ivtx) const { return mPrimaryVertices[ivtx]; } + auto& getPrimaryVertices() { return mPrimaryVertices; }; + auto getPrimaryVerticesNum() { return mPrimaryVertices.size(); }; + const auto& getPrimaryVertices() const { return mPrimaryVertices; }; + auto& getPrimaryVerticesLabels() { return mPrimaryVerticesLabels; }; + gsl::span getPrimaryVertices(int layer, int rofId) const; + void addPrimaryVertex(const Vertex& vertex); + void addPrimaryVertexLabel(const VertexLabel& label) { mPrimaryVerticesLabels.push_back(label); } + + // read-in data + void loadROFrameData(gsl::span rofs, + gsl::span clusters, + gsl::span::iterator& pattIt, + const itsmft::TopologyDictionary* dict, + int layer, + const dataformats::MCTruthContainer* mcLabels = nullptr, + o2::detectors::DetID::ID detId = o2::detectors::DetID::ITS); + o2::detectors::DetID::ID getDetId() const { return mDetId; } + void setDetId(o2::detectors::DetID::ID detId) { mDetId = detId; } + void resetROFrameData(int iLayer); + void prepareROFrameData(gsl::span clusters, int layer); + + int getTotalClusters() const; + bool empty() const { return getTotalClusters() == 0; } + int getSortedIndex(int rofId, int layer, int idx) const { return mROFramesClusters[layer][rofId] + idx; } + int getSortedStartIndex(const int rofId, const int layer) const { return mROFramesClusters[layer][rofId]; } + int getNrof(int layer) const { return mROFramesClusters[layer].size() - 1; } + + void resetBeamXY(const float x, const float y, const float w = 0); + void setBeamPosition(const float x, const float y, const float s2, const float base = 50.f, const float systematic = 0.f) + { + isBeamPositionOverridden = true; + resetBeamXY(x, y, s2 / o2::gpu::CAMath::Sqrt((base * base) + systematic)); + } + + float getBeamX() const { return mBeamPos[0]; } + float getBeamY() const { return mBeamPos[1]; } + std::array& getBeamXY() { return mBeamPos; } + + auto& getMinRs() { return mMinR; } + auto& getMaxRs() { return mMaxR; } + float getMinR(int layer) const { return mMinR[layer]; } + float getMaxR(int layer) const { return mMaxR[layer]; } + float getTransitionPhiCut(int transitionId) const { return mTransitionPhiCuts[transitionId]; } + float getTransitionMSAngle(int transitionId) const { return mTransitionMSAngles[transitionId]; } + auto& getTransitionPhiCuts() { return mTransitionPhiCuts; } + auto& getTransitionMSAngles() { return mTransitionMSAngles; } + float getPositionResolution(int layer) const { return mPositionResolution[layer]; } + auto& getPositionResolutions() { return mPositionResolution; } + + gsl::span getClustersOnLayer(int rofId, int layerId); + gsl::span getClustersOnLayer(int rofId, int layerId) const; + gsl::span getClustersPerROFrange(int rofMin, int range, int layerId) const; + gsl::span getUnsortedClustersOnLayer(int rofId, int layerId) const; + gsl::span getUsedClustersROF(int rofId, int layerId); + gsl::span getUsedClustersROF(int rofId, int layerId) const; + gsl::span getROFramesClustersPerROFrange(int rofMin, int range, int layerId) const; + gsl::span getROFrameClusters(int layerId) const; + gsl::span getNClustersROFrange(int rofMin, int range, int layerId) const; + gsl::span getIndexTable(int rofId, int layerId); + const auto& getTrackingFrameInfoOnLayer(int layerId) const { return mTrackingFrameInfo[layerId]; } + + // navigation tables + const auto& getIndexTableUtils() const { return mIndexTableUtils; } + const auto& getROFOverlapTable() const { return mROFOverlapTable; } + const auto& getROFOverlapTableView() const { return mROFOverlapTableView; } + const auto& getTrackerTopologies() const { return mTrackerTopologies; } + const auto& getTrackingTopologyView() const { return mTrackingTopologyView; } + void setROFOverlapTable(ROFOverlapTableN table) + { + mROFOverlapTable = std::move(table); + mROFOverlapTableView = mROFOverlapTable.getView(); + } + const auto& getROFVertexLookupTable() const { return mROFVertexLookupTable; } + const auto& getROFVertexLookupTableView() const { return mROFVertexLookupTableView; } + void setROFVertexLookupTable(ROFVertexLookupTableN table) + { + mROFVertexLookupTable = std::move(table); + mROFVertexLookupTableView = mROFVertexLookupTable.getView(); + } + void updateROFVertexLookupTable() { mROFVertexLookupTable.update(mPrimaryVertices.data(), mPrimaryVertices.size()); } + void setMultiplicityCutMask(ROFMaskTableN cutMask) + { + mMultiplicityCutMask = std::move(cutMask); + mROFMaskView = mROFMask->getView(); + } + void useMultiplictyMask() noexcept + { + mROFMask = &mMultiplicityCutMask; + mROFMaskView = mROFMask->getView(); + } + void setUPCCutMask(ROFMaskTableN cutMask) { mUPCCutMask = std::move(cutMask); } + void useUPCMask() noexcept + { + mROFMask = &mUPCCutMask; + mROFMaskView = mROFMask->getView(); + } + const auto& getROFMaskView() const { return mROFMaskView; } + + const TrackingFrameInfo& getClusterTrackingFrameInfo(int layerId, const Cluster& cl) const; + gsl::span getClusterLabels(int layerId, const Cluster& cl) const { return getClusterLabels(layerId, cl.clusterId); } + gsl::span getClusterLabels(int layerId, const int clId) const { return mClusterLabels[((mIsStaggered) ? layerId : 0)]->getLabels(mClusterExternalIndices[layerId][clId]); } + int getClusterExternalIndex(int layerId, const int clId) const { return mClusterExternalIndices[layerId][clId]; } + int getClusterSize(int layer, int clusterId) const { return mClusterSize[layer][clusterId]; } + void setClusterSize(int layer, bounded_vector& v) { mClusterSize[layer] = std::move(v); } + + auto& getTrackletsLabel(int layer) { return mTrackletLabels[layer]; } + auto& getCellsLabel(int layer) { return mCellLabels[layer]; } + + bool hasMCinformation() const { return mClusterLabels[0] != nullptr; } + void initVertexingTopology(const TrackingParameters& trkParam); + void initDefaultTrackingTopology(const TrackingParameters& trkParam, const int maxLayers = NLayers); + void initTrackerTopologies(gsl::span trkParams, const int maxLayers = NLayers); + void initialise(const TrackingParameters& trkParam, const int maxLayers = NLayers, const int iteration = constants::UnusedIndex); + + bool isClusterUsed(int layer, int clusterId) const { return mUsedClusters[layer][clusterId]; } + void markUsedCluster(int layer, int clusterId) { mUsedClusters[layer][clusterId] = true; } + gsl::span getUsedClusters(const int layer); + + auto& getTracklets() { return mTracklets; } + auto& getTrackletsLookupTable() { return mTrackletsLookupTable; } + + auto& getClusters() { return mClusters; } + auto& getUnsortedClusters() { return mUnsortedClusters; } + int getClusterROF(int iLayer, int iCluster); + auto& getCells() { return mCells; } + + auto& getCellsLookupTable() { return mCellsLookupTable; } + auto& getCellsNeighbours() { return mCellsNeighbours; } + auto& getCellsNeighboursTopology() { return mCellsNeighboursTopology; } + auto& getCellsNeighboursLUT() { return mCellsNeighboursLUT; } + auto& getTracks() { return mTracks; } + auto& getTracksLabel() { return mTracksLabel; } + auto& getLinesLabel(const int rofId) { return mLinesLabels[rofId]; } + + size_t getNumberOfClusters() const; + virtual size_t getNumberOfCells() const; + virtual size_t getNumberOfTracklets() const; + virtual size_t getNumberOfNeighbours() const; + size_t getNumberOfTracks() const; + size_t getNumberOfUsedClusters() const; + + /// memory management + void setMemoryPool(std::shared_ptr pool); + auto& getMemoryPool() const noexcept { return mMemoryPool; } + bool checkMemory(unsigned long max) { return getArtefactsMemory() < max; } + unsigned long getArtefactsMemory() const; + void printArtefactsMemory() const; + + /// staggering + void setIsStaggered(bool b) noexcept { mIsStaggered = b; } + + // Vertexer + void computeTrackletsPerROFScans(); + void computeTracletsPerClusterScans(); + int& getNTrackletsROF(int rofId, int combId) { return mNTrackletsPerROF[combId][rofId]; } + auto& getLines(int rofId) { return mLines[rofId]; } + int getNLinesTotal() const noexcept { return mTotalLines; } + void setNLinesTotal(uint32_t a) noexcept { mTotalLines = a; } + auto& getTrackletClusters(int rofId) { return mTrackletClusters[rofId]; } + gsl::span getFoundTracklets(int rofId, int combId) const; + gsl::span getFoundTracklets(int rofId, int combId); + gsl::span getLabelsFoundTracklets(int rofId, int combId) const; + gsl::span getNTrackletsCluster(int rofId, int combId); + gsl::span getExclusiveNTrackletsCluster(int rofId, int combId); + uint32_t getTotalTrackletsTF(const int iLayer) { return mTotalTracklets[iLayer]; } + int getTotalClustersPerROFrange(int rofMin, int range, int layerId) const; + // \Vertexer + + int hasBogusClusters() const { return std::accumulate(mBogusClusters.begin(), mBogusClusters.end(), 0); } + + void setBz(float bz) { mBz = bz; } + float getBz() const { return mBz; } + + /// State if memory will be externally managed by the GPU framework + ExternalAllocator* mExternalAllocator{nullptr}; + std::shared_ptr mExtMemoryPool; // host memory pool managed by the framework + auto getFrameworkAllocator() { return mExternalAllocator; }; + void setFrameworkAllocator(ExternalAllocator* ext); + bool hasFrameworkAllocator() const noexcept { return mExternalAllocator != nullptr; } + std::pmr::memory_resource* getMaybeFrameworkHostResource(bool forceHost = false) { return (hasFrameworkAllocator() && !forceHost) ? mExtMemoryPool.get() : mMemoryPool.get(); } + + // Propagator + const o2::base::PropagatorImpl* getDevicePropagator() const { return mPropagatorDevice; } + virtual void setDevicePropagator(const o2::base::PropagatorImpl* /*unused*/) {}; + + template + void addClusterToLayer(int layer, T&&... args); + template + void addTrackingFrameInfoToLayer(int layer, T&&... args); + void addTrackingFrameInfoToLayer(int layer, const TrackingFrameInfo& tfInfo) { mTrackingFrameInfo[layer].push_back(tfInfo); } + void addClusterExternalIndexToLayer(int layer, const int idx) { mClusterExternalIndices[layer].push_back(idx); } + + std::array, NLayers> mClusters; + std::array, NLayers> mTrackingFrameInfo; + std::array, NLayers> mClusterExternalIndices; + std::array, NLayers> mROFramesClusters; + std::array*, NLayers> mClusterLabels{nullptr}; + std::array, 2> mNTrackletsPerCluster; + std::array, 2> mNTrackletsPerClusterSum; + std::array, NLayers> mNClustersPerROF; + std::array, NLayers> mIndexTables; + std::vector> mTrackletsLookupTable; + std::array, NLayers> mUsedClusters; + + std::array, NLayers> mUnsortedClusters; + std::vector> mTracklets; + std::vector> mCells; + bounded_vector mTracks; + bounded_vector mTracksLabel; + std::vector> mCellsNeighbours; + std::vector> mCellsNeighboursTopology; + std::vector> mCellsLookupTable; + + const o2::base::PropagatorImpl* mPropagatorDevice = nullptr; // Needed only for GPU + o2::detectors::DetID::ID mDetId{o2::detectors::DetID::ITS}; + + virtual void wipe(); + + // interface + virtual bool isGPU() const noexcept { return false; } + virtual const char* getName() const noexcept { return "CPU"; } + + protected: + void prepareClusters(const TrackingParameters& trkParam, const int maxLayers = NLayers); + float mBz = 5.; + unsigned int mNTotalLowPtVertices = 0; + int mBeamPosWeight = 0; + std::array mBeamPos = {0.f, 0.f}; + bool isBeamPositionOverridden = false; + std::array mMinR; + std::array mMaxR; + bounded_vector mTransitionPhiCuts; + bounded_vector mTransitionMSAngles; + bounded_vector mPositionResolution; + std::array, NLayers> mClusterSize; + + bounded_vector> mPValphaX; /// PV x and alpha for track propagation + std::vector> mTrackletLabels; + std::vector> mCellLabels; + std::vector> mCellsNeighboursLUT; + bounded_vector mBogusClusters; /// keep track of clusters with wild coordinates + + // Vertexer + bounded_vector mPrimaryVertices; + bounded_vector mPrimaryVerticesLabels; + std::vector> mNTrackletsPerROF; + std::vector> mLines; + std::vector> mTrackletClusters; + std::array, 2> mTrackletsIndexROF; + std::vector> mLinesLabels; + std::array mTotalTracklets = {0, 0}; + uint32_t mTotalLines = 0; + // \Vertexer + + // lookup tables + IndexTableUtilsN mIndexTableUtils; + ROFOverlapTableN mROFOverlapTable; + ROFOverlapTableN::View mROFOverlapTableView; + TrackingTopologyN mVertexingTopology; + TrackingTopologyN mDefaultTrackingTopology; + std::vector mTrackerTopologies; + typename TrackingTopologyN::View mTrackingTopologyView; + ROFVertexLookupTableN mROFVertexLookupTable; + ROFVertexLookupTableN::View mROFVertexLookupTableView; + ROFMaskTableN mMultiplicityCutMask; + ROFMaskTableN mUPCCutMask; + ROFMaskTableN* mROFMask = &mMultiplicityCutMask; + ROFMaskTableN::View mROFMaskView; + + bool mIsStaggered{false}; + + std::shared_ptr mMemoryPool; +}; + +template +gsl::span TimeFrame::getPrimaryVertices(int layer, int rofId) const +{ + if (rofId < 0 || rofId >= getNrof(layer)) { + return {}; + } + const auto& entry = mROFVertexLookupTableView.getVertices(layer, rofId); + return {&mPrimaryVertices[entry.getFirstEntry()], static_cast::size_type>(entry.getEntries())}; +} + +template +inline void TimeFrame::resetBeamXY(const float x, const float y, const float w) +{ + mBeamPos[0] = x; + mBeamPos[1] = y; + mBeamPosWeight = w; +} + +template +inline gsl::span TimeFrame::getROFrameClusters(int layerId) const +{ + return {&mROFramesClusters[layerId][0], static_cast::size_type>(mROFramesClusters[layerId].size())}; +} + +template +inline gsl::span TimeFrame::getClustersOnLayer(int rofId, int layerId) +{ + if (rofId < 0 || rofId >= getNrof(layerId)) { + return {}; + } + int startIdx{mROFramesClusters[layerId][rofId]}; + return {&mClusters[layerId][startIdx], static_cast::size_type>(mROFramesClusters[layerId][rofId + 1] - startIdx)}; +} + +template +inline gsl::span TimeFrame::getClustersOnLayer(int rofId, int layerId) const +{ + if (rofId < 0 || rofId >= getNrof(layerId)) { + return {}; + } + int startIdx{mROFramesClusters[layerId][rofId]}; + return {&mClusters[layerId][startIdx], static_cast::size_type>(mROFramesClusters[layerId][rofId + 1] - startIdx)}; +} + +template +inline gsl::span TimeFrame::getUsedClustersROF(int rofId, int layerId) +{ + if (rofId < 0 || rofId >= getNrof(layerId)) { + return {}; + } + int startIdx{mROFramesClusters[layerId][rofId]}; + return {&mUsedClusters[layerId][startIdx], static_cast::size_type>(mROFramesClusters[layerId][rofId + 1] - startIdx)}; +} + +template +inline gsl::span TimeFrame::getUsedClustersROF(int rofId, int layerId) const +{ + if (rofId < 0 || rofId >= getNrof(layerId)) { + return {}; + } + int startIdx{mROFramesClusters[layerId][rofId]}; + return {&mUsedClusters[layerId][startIdx], static_cast::size_type>(mROFramesClusters[layerId][rofId + 1] - startIdx)}; +} + +template +inline gsl::span TimeFrame::getClustersPerROFrange(int rofMin, int range, int layerId) const +{ + if (rofMin < 0 || rofMin >= getNrof(layerId)) { + return {}; + } + int startIdx{mROFramesClusters[layerId][rofMin]}; // First cluster of rofMin + int endIdx{mROFramesClusters[layerId][o2::gpu::CAMath::Min(rofMin + range, getNrof(layerId))]}; + return {&mClusters[layerId][startIdx], static_cast::size_type>(endIdx - startIdx)}; +} + +template +inline gsl::span TimeFrame::getROFramesClustersPerROFrange(int rofMin, int range, int layerId) const +{ + int chkdRange{o2::gpu::CAMath::Min(range, getNrof(layerId) - rofMin)}; + return {&mROFramesClusters[layerId][rofMin], static_cast::size_type>(chkdRange)}; +} + +template +inline gsl::span TimeFrame::getNClustersROFrange(int rofMin, int range, int layerId) const +{ + int chkdRange{o2::gpu::CAMath::Min(range, getNrof(layerId) - rofMin)}; + return {&mNClustersPerROF[layerId][rofMin], static_cast::size_type>(chkdRange)}; +} + +template +inline int TimeFrame::getTotalClustersPerROFrange(int rofMin, int range, int layerId) const +{ + int startIdx{rofMin}; // First cluster of rofMin + int endIdx{o2::gpu::CAMath::Min(rofMin + range, getNrof(layerId))}; + return mROFramesClusters[layerId][endIdx] - mROFramesClusters[layerId][startIdx]; +} + +template +inline int TimeFrame::getClusterROF(int iLayer, int iCluster) +{ + return std::lower_bound(mROFramesClusters[iLayer].begin(), mROFramesClusters[iLayer].end(), iCluster + 1) - mROFramesClusters[iLayer].begin() - 1; +} + +template +inline gsl::span TimeFrame::getUnsortedClustersOnLayer(int rofId, int layerId) const +{ + if (rofId < 0 || rofId >= getNrof(layerId)) { + return {}; + } + int startIdx{mROFramesClusters[layerId][rofId]}; + return {&mUnsortedClusters[layerId][startIdx], static_cast::size_type>(mROFramesClusters[layerId][rofId + 1] - startIdx)}; +} + +template +inline gsl::span TimeFrame::getIndexTable(int rofId, int layer) +{ + if (rofId < 0 || rofId >= getNrof(layer)) { + return {}; + } + const int tableSize = mIndexTableUtils.getNrowBins() * mIndexTableUtils.getNcolBins() + 1; + return {&mIndexTables[layer][rofId * tableSize], static_cast::size_type>(tableSize)}; +} + +template +template +void TimeFrame::addClusterToLayer(int layer, T&&... values) +{ + mUnsortedClusters[layer].emplace_back(std::forward(values)...); +} + +template +template +void TimeFrame::addTrackingFrameInfoToLayer(int layer, T&&... values) +{ + mTrackingFrameInfo[layer].emplace_back(std::forward(values)...); +} + +template +inline gsl::span TimeFrame::getUsedClusters(const int layer) +{ + return {&mUsedClusters[layer][0], static_cast::size_type>(mUsedClusters[layer].size())}; +} + +template +inline gsl::span TimeFrame::getNTrackletsCluster(int rofId, int combId) +{ + if (rofId < 0 || rofId >= getNrof(1)) { + return {}; + } + auto startIdx{mROFramesClusters[1][rofId]}; + return {&mNTrackletsPerCluster[combId][startIdx], static_cast::size_type>(mROFramesClusters[1][rofId + 1] - startIdx)}; +} + +template +inline gsl::span TimeFrame::getExclusiveNTrackletsCluster(int rofId, int combId) +{ + if (rofId < 0 || rofId >= getNrof(1)) { + return {}; + } + auto clusStartIdx{mROFramesClusters[1][rofId]}; + + return {&mNTrackletsPerClusterSum[combId][clusStartIdx], static_cast::size_type>(mROFramesClusters[1][rofId + 1] - clusStartIdx)}; +} + +template +inline gsl::span TimeFrame::getFoundTracklets(int rofId, int combId) +{ + if (rofId < 0 || rofId >= getNrof(1) || mTracklets[combId].empty()) { + return {}; + } + auto startIdx{mNTrackletsPerROF[combId][rofId]}; + return {&mTracklets[combId][startIdx], static_cast::size_type>(mNTrackletsPerROF[combId][rofId + 1] - startIdx)}; +} + +template +inline gsl::span TimeFrame::getFoundTracklets(int rofId, int combId) const +{ + if (rofId < 0 || rofId >= getNrof(1)) { + return {}; + } + auto startIdx{mNTrackletsPerROF[combId][rofId]}; + return {&mTracklets[combId][startIdx], static_cast::size_type>(mNTrackletsPerROF[combId][rofId + 1] - startIdx)}; +} + +template +inline gsl::span TimeFrame::getLabelsFoundTracklets(int rofId, int combId) const +{ + if (rofId < 0 || rofId >= getNrof(1) || !hasMCinformation()) { + return {}; + } + auto startIdx{mNTrackletsPerROF[combId][rofId]}; + return {&mTrackletLabels[combId][startIdx], static_cast::size_type>(mNTrackletsPerROF[combId][rofId + 1] - startIdx)}; +} + +template +inline int TimeFrame::getTotalClusters() const +{ + size_t totalClusters{0}; + for (const auto& clusters : mUnsortedClusters) { + totalClusters += clusters.size(); + } + return int(totalClusters); +} + +template +inline size_t TimeFrame::getNumberOfClusters() const +{ + size_t nClusters{0}; + for (const auto& layer : mClusters) { + nClusters += layer.size(); + } + return nClusters; +} + +template +inline size_t TimeFrame::getNumberOfCells() const +{ + size_t nCells{0}; + for (const auto& layer : mCells) { + nCells += layer.size(); + } + return nCells; +} + +template +inline size_t TimeFrame::getNumberOfTracklets() const +{ + size_t nTracklets{0}; + for (const auto& layer : mTracklets) { + nTracklets += layer.size(); + } + return nTracklets; +} + +template +inline size_t TimeFrame::getNumberOfNeighbours() const +{ + size_t neigh{0}; + for (const auto& l : mCellsNeighbours) { + neigh += l.size(); + } + return neigh; +} + +template +inline size_t TimeFrame::getNumberOfTracks() const +{ + return mTracks.size(); +} + +template +inline size_t TimeFrame::getNumberOfUsedClusters() const +{ + size_t nClusters = 0; + for (const auto& layer : mUsedClusters) { + nClusters += std::count(layer.begin(), layer.end(), true); + } + return nClusters; +} + +using TimeFrameITS = TimeFrame; +using TimeFrameMFT = TimeFrame; + +} // namespace itsmft::tracking +} // namespace o2 + +#endif /* ALICEO2_ITSMFT_TRACKING_TIMEFRAME_H_ */ diff --git a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/TrackingConfigParam.h b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/TrackingConfigParam.h new file mode 100644 index 0000000000000..a3afd8d02ff8e --- /dev/null +++ b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/TrackingConfigParam.h @@ -0,0 +1,159 @@ +// 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_ITSMFT_TRACKING_CONFIG_PARAM_H_ +#define ALICEO2_ITSMFT_TRACKING_CONFIG_PARAM_H_ + +#include +#include + +#include "CommonUtils/ConfigurableParam.h" +#include "CommonUtils/ConfigurableParamHelper.h" +#include "DetectorsCommonDataFormats/DetID.h" +#include "ITSMFTTracking/Constants.h" + +namespace o2::itsmft +{ + +namespace tracking_constants +{ +constexpr int MaxIter = 4; +} // namespace tracking_constants + +/// ITS vertexer settings (not used for MFT) +struct VertexerParamConfig : public o2::conf::ConfigurableParamHelper { + bool saveTimeBenchmarks = false; // dump metrics on file + + int nIterations = 1; // Number of vertexing passes to perform. + int vertPerRofThreshold = 0; // Maximum number of vertices per ROF to trigger second a iteration. + + // geometrical cuts for tracklet selection for Pb-Pb + float zCut = 0.002f; + float phiCut = 0.005f; + float pairCut = 0.017321f; + float clusterCut = 0.170048f; + float coarseZWindow = 0.055458f; + float seedDedupZCut = 0.116685f; + float refitDedupZCut = 0.039855f; + float duplicateZCut = 0.200097f; + float finalSelectionZCut = 0.034535f; + float duplicateDistance2Cut = 0.005117f; + float tanLambdaCut = 0.002f; // tanLambda = deltaZ/deltaR + float nSigmaCut = 0.0164651f; + float maxZPositionAllowed = 25.f; // 4x sZ of the beam + + // Artefacts selections + int clusterContributorsCut = 3; // minimum number of contributors for an accepted final vertex + int suppressLowMultDebris = 16; // suppress all vertices below this threshold if a vertex was already found in a rof + int seedMemberRadiusTime = 0; + int seedMemberRadiusZ = 2; + int maxTrackletsPerCluster = 100; + int phiSpan = -1; + int zSpan = -1; + int ZBins = 1; // z-phi index table configutation: number of z bins + int PhiBins = 128; // z-phi index table configutation: number of phi bins + + bool useTruthSeeding{false}; // overwrite seeding vertices with MC truth + + int nThreads = 1; + bool printMemory = false; + size_t maxMemory = std::numeric_limits::max(); + bool dropTFUponFailure = false; + + O2ParamDef(VertexerParamConfig, "ITSVertexerParam"); +}; + +template +struct TrackerParamConfig : public o2::conf::ConfigurableParamHelper> { + static constexpr std::string_view getParamName() + { + return N == o2::detectors::DetID::ITS ? TrackerParamName[0] : TrackerParamName[1]; + } + + static constexpr int MinTrackLength = N == o2::detectors::DetID::ITS ? 4 : 5; + static constexpr int MaxTrackLength = N == o2::detectors::DetID::ITS ? o2::itsmft::tracking::constants::ITSNLayers + : o2::itsmft::tracking::constants::MFTNLayers; + + static constexpr int getNLayers() + { + return N == o2::detectors::DetID::ITS ? o2::itsmft::tracking::constants::ITSNLayers : o2::itsmft::tracking::constants::MFTNLayers; + } + + bool useMatCorrTGeo = false; // use full geometry to corect for material budget accounting in the fits. Default is to use the material budget LUT. + bool useFastMaterial = false; // use faster material approximation for material budget accounting in the fits. + int addTimeError[getNLayers()] = {0}; // configure the width of the window in BC to be considered for the tracking. + int minTrackLgtIter[tracking_constants::MaxIter] = {}; // minimum track length at each iteration, used only if >0, otherwise use code defaults + uint8_t startLayerMask[tracking_constants::MaxIter] = {}; // mask of start layer for this iteration (if >0) + int maxHolesIter[tracking_constants::MaxIter] = {}; // maximum number of missing internal layers allowed in the CA topology for each iteration + uint16_t holeLayerMaskIter[tracking_constants::MaxIter] = {}; // layers that may be skipped by the CA topology for each iteration + float minPtIterLgt[tracking_constants::MaxIter * (MaxTrackLength - MinTrackLength + 1)] = {}; // min.pT for given track length at this iteration, used only if >0, otherwise use code defaults + float sysErr2Row[getNLayers()] = {0}; // systematic error^2 along ALPIDE rows (local X) per layer + float sysErr2Col[getNLayers()] = {0}; // systematic error^2 along ALPIDE columns (local Z) per layer + float maxChi2ClusterAttachment = -1.f; + float maxChi2NDF = -1.f; + float nSigmaCut = -1.f; + float deltaTanLres = -1.f; + float minPt = -1.f; + float pvRes = -1.f; + int LUTbinsU = -1; // number of LUT bins along the first coordinate (ITS: phi, MFT: global x) + int LUTbinsV = -1; // number of LUT bins along the second coordinate (ITS: z, MFT: global y) + float diamondPos[3] = {0.f, 0.f, 0.f}; // override the position of the vertex + bool useDiamond = false; // enable overriding the vertex position + bool perPrimaryVertexProcessing = false; // perform the full tracking considering the vertex hypotheses one at the time. + bool saveTimeBenchmarks = false; // dump metrics on file + bool overrideBeamEstimation = false; // use beam position from meanVertex CCDB object + int trackingMode = -1; // -1: unset, 0=sync, 1=async, 2=cosmics used by gpuwf only + bool doUPCIteration = false; // Perform an additional iteration for UPC events on tagged vertices. You want to combine this config with VertexerParamConfig.nIterations=2 + int nIterations = tracking_constants::MaxIter; // overwrite the number of iterations + int reseedIfShorter = 6; // for the final refit reseed the track with circle if they are shorter than this value + bool shiftRefToCluster{true}; // TrackFit: after update shift the linearization reference to cluster + bool repeatRefitOut{false}; // repeat outward refit using inward refit as a seed + bool createArtefactLabels{false}; // create on-the-fly labels for the artefacts + + int nThreads = 1; + bool printMemory = false; + size_t maxMemory = std::numeric_limits::max(); + bool dropTFUponFailure = false; + bool fataliseUponFailure = true; // granular management of the fatalisation in async mode + + // Selections on tracks sharing clusters + bool allowSharingFirstCluster = false; // allow first cluster sharing among tracks + float sharedClusterMaxDeltaPhi = 0.05f; // Maximum allowed delta phi at the cluster position + float sharedClusterMaxDeltaEta = 0.03f; // Maximum allowed delta eta at the cluster position + bool sharedClusterOppositeSign = false; // Require opposite sign of the tracklets + + O2ParamDef(TrackerParamConfig, getParamName().data()); + + private: + static constexpr std::string_view TrackerParamName[2] = {"ITSCATrackerParam", "MFTCATrackerParam"}; +}; + +template +TrackerParamConfig TrackerParamConfig::sInstance; + +} // namespace o2::itsmft + +namespace framework +{ +template +struct is_messageable; +template <> +struct is_messageable : std::true_type { +}; +template <> +struct is_messageable> : std::true_type { +}; +template <> +struct is_messageable> : std::true_type { +}; +} // namespace framework + +#endif /* ALICEO2_ITSMFT_TRACKING_CONFIG_PARAM_H_ */ diff --git a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/TrackingTopology.h b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/TrackingTopology.h new file mode 100644 index 0000000000000..82f40c9e27bb2 --- /dev/null +++ b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/TrackingTopology.h @@ -0,0 +1,219 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef ALICEO2_ITSMFT_TRACKING_INCLUDE_TRACKINGTOPOLOGY_H_ +#define ALICEO2_ITSMFT_TRACKING_INCLUDE_TRACKINGTOPOLOGY_H_ + +#include +#include +#include +#include + +#ifndef GPUCA_GPUCODE +#include +#include +#include "Framework/Logger.h" +#endif + +#include "CommonDataFormat/RangeReference.h" +#include "GPUCommonDef.h" +#include "GPUCommonMath.h" +#include "ITSMFTTracking/LayerMask.h" + +namespace o2::itsmft::tracking +{ + +template +class TrackingTopology +{ + public: + using Id = uint8_t; + using Mask = LayerMask; + using Range = o2::dataformats::RangeReference; + static constexpr int MaxTransitions = (NLayers * (NLayers - 1)) / 2; + static constexpr int MaxCells = (NLayers * (NLayers - 1) * (NLayers - 2)) / 6; + static_assert(NLayers < std::numeric_limits::max()); + static_assert(MaxTransitions <= std::numeric_limits::max()); + static_assert(MaxCells <= std::numeric_limits::max()); + + // Describes from which layer to which layer the look-up happens + struct LayerTransition { + Id fromLayer{0}; + Id toLayer{0}; + }; + static_assert(std::is_standard_layout_v); + static_assert(std::is_trivially_copyable_v); + static_assert(sizeof(LayerTransition) == (2 * sizeof(Id))); + + // Describes from which LayerTransition a tracklet is allowed to originate + // and with which LayerTransition this can be combined additionally the hitMasked is cached + struct CellTopology { + Id firstTransition{0}; + Id secondTransition{0}; + Mask hitLayerMask{0}; + }; + static_assert(std::is_standard_layout_v); + static_assert(std::is_trivially_copyable_v); + static_assert(sizeof(CellTopology) == (2 * sizeof(Id)) + sizeof(Mask)); + + // GPU ready view of the underlying LUTs + struct View { + const LayerTransition* transitions{nullptr}; + const CellTopology* cells{nullptr}; + const Range* cellsByFirstTransitionIndex{nullptr}; + const Id* cellsByFirstTransition{nullptr}; + Id nTransitions{0}; + Id nCells{0}; + Id nCellsByFirstTransition{0}; + + GPUhdi() const LayerTransition& getTransition(Id id) const { return transitions[id]; } + GPUhdi() const CellTopology& getCell(Id id) const { return cells[id]; } + GPUhdi() Range getCellsStartingWithTransition(Id transitionId) const { return cellsByFirstTransitionIndex[transitionId]; } + +#ifndef GPUCA_GPUCODE + std::string asString() const + { + std::string out = fmt::format("TrackingTopology: transitions={} cells={}", nTransitions, nCells); + out += "\n transitions:"; + for (Id transitionId = 0; transitionId < nTransitions; ++transitionId) { + const auto& t = transitions[transitionId]; + out += fmt::format("\n {}: {} -> {}", transitionId, t.fromLayer, t.toLayer); + } + out += "\n cells:"; + for (Id cellId = 0; cellId < nCells; ++cellId) { + const auto& c = cells[cellId]; + const auto& first = transitions[c.firstTransition]; + const auto& second = transitions[c.secondTransition]; + out += fmt::format("\n {}: {} -> {} -> {} hitMask={} transitions=({}, {})", cellId, first.fromLayer, first.toLayer, second.toLayer, c.hitLayerMask.asString(), c.firstTransition, c.secondTransition); + } + return out; + } + + void print() const + { + LOGP(info, "{}", asString()); + } +#endif + }; + + void init(int maxLayers, int maxHoles, Mask holeLayerMask) + { + clear(); + mMaxLayers = o2::gpu::CAMath::Max(0, o2::gpu::CAMath::Min(maxLayers, NLayers)); + mMaxHoles = o2::gpu::CAMath::Max(maxHoles, 0); + mHoleLayerMask = holeLayerMask; + for (int fromLayer = 0; fromLayer < mMaxLayers; ++fromLayer) { + for (int toLayer = fromLayer + 1; toLayer < mMaxLayers; ++toLayer) { + if (Mask::skipped(fromLayer, toLayer).isAllowedHoleMask(mMaxHoles, mHoleLayerMask)) { + mTransitions[mNTransitions++] = LayerTransition{static_cast(fromLayer), static_cast(toLayer)}; + } + } + } + + for (Id firstId = 0; firstId < mNTransitions; ++firstId) { + const auto& first = mTransitions[firstId]; + for (Id secondId = 0; secondId < mNTransitions; ++secondId) { + const auto& second = mTransitions[secondId]; + if (first.toLayer != second.fromLayer) { + continue; + } + const Mask hitMask{first.fromLayer, first.toLayer, second.toLayer}; + if (hitMask.isAllowed(mMaxHoles, mHoleLayerMask)) { + mCells[mNCells++] = CellTopology{firstId, secondId, hitMask}; + } + } + } + + fillCellsByTransition(); + } + + View getView() const + { + return View{mTransitions.data(), + mCells.data(), + mCellsByFirstTransitionIndex.data(), + mCellsByFirstTransition.data(), + mNTransitions, + mNCells, + mNCellsByFirstTransition}; + } + + View getDeviceView(const LayerTransition* deviceTransitions, + const CellTopology* deviceCells, + const Range* deviceCellsByFirstTransitionIndex, + const Id* deviceCellsByFirstTransition) const + { + return View{deviceTransitions, + deviceCells, + deviceCellsByFirstTransitionIndex, + deviceCellsByFirstTransition, + mNTransitions, + mNCells, + mNCellsByFirstTransition}; + } + + const auto& getTransitions() const noexcept { return mTransitions; } + const auto& getCells() const noexcept { return mCells; } + const auto& getCellsByFirstTransitionIndex() const noexcept { return mCellsByFirstTransitionIndex; } + const auto& getCellsByFirstTransition() const noexcept { return mCellsByFirstTransition; } + Id getNTransitions() const noexcept { return mNTransitions; } + Id getNCells() const noexcept { return mNCells; } + Id getNCellsByFirstTransition() const noexcept { return mNCellsByFirstTransition; } + + private: + void clear() + { + mNTransitions = 0; + mNCells = 0; + mNCellsByFirstTransition = 0; + mTransitions.fill({}); + mCells.fill({}); + mCellsByFirstTransitionIndex.fill(Range{0, 0}); + mCellsByFirstTransition.fill(0); + } + + void fillCellsByTransition() + { + std::array counts{}; + for (Id cellId = 0; cellId < mNCells; ++cellId) { + ++counts[mCells[cellId].firstTransition]; + } + + Id offset = 0; + for (Id transitionId = 0; transitionId < mNTransitions; ++transitionId) { + mCellsByFirstTransitionIndex[transitionId].setFirstEntry(offset); + mCellsByFirstTransitionIndex[transitionId].setEntries(counts[transitionId]); + offset += counts[transitionId]; + } + + std::array cursor{}; + for (Id cellId = 0; cellId < mNCells; ++cellId) { + const Id transitionId = mCells[cellId].firstTransition; + mCellsByFirstTransition[mCellsByFirstTransitionIndex[transitionId].getFirstEntry() + cursor[transitionId]++] = cellId; + } + mNCellsByFirstTransition = offset; + } + + int mMaxLayers{0}; + int mMaxHoles{0}; + Mask mHoleLayerMask{0}; + Id mNTransitions{0}; + Id mNCells{0}; + Id mNCellsByFirstTransition{0}; + std::array mTransitions{}; + std::array mCells{}; + std::array mCellsByFirstTransitionIndex{}; + std::array mCellsByFirstTransition{}; +}; + +} // namespace o2::itsmft::tracking + +#endif diff --git a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/Tracklet.h b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/Tracklet.h new file mode 100644 index 0000000000000..0518aeef50f9e --- /dev/null +++ b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/Tracklet.h @@ -0,0 +1,74 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// +/// \file Tracklet.h +/// \brief +/// + +#ifndef ALICEO2_ITSMFT_TRACKING_INCLUDE_TRACKLET_H_ +#define ALICEO2_ITSMFT_TRACKING_INCLUDE_TRACKLET_H_ + +#include "ITSMFTTracking/Constants.h" +#include "ITSMFTTracking/Types.h" +#include "ITSMFTTracking/Cluster.h" +#include "GPUCommonRtypes.h" +#include "GPUCommonMath.h" +#include "GPUCommonDef.h" +#include "GPUCommonLogger.h" + +namespace o2::itsmft::tracking +{ + +// tracklets are entirely determined by their two cluster idx +struct Tracklet final { + GPUhdDefault() Tracklet() = default; + GPUhdi() Tracklet(const int firstClusterOrderingIndex, const int secondClusterOrderingIndex, + const Cluster& firstCluster, const Cluster& secondCluster, const TimeEstBC& t) + : firstClusterIndex(firstClusterOrderingIndex), + secondClusterIndex(secondClusterOrderingIndex), + tanLambda((firstCluster.zCoordinate - secondCluster.zCoordinate) / (firstCluster.radius - secondCluster.radius)), + phi(o2::gpu::GPUCommonMath::ATan2(firstCluster.yCoordinate - secondCluster.yCoordinate, firstCluster.xCoordinate - secondCluster.xCoordinate)), + mTime(t) {} + + GPUhdi() Tracklet(const int idx0, const int idx1, float tanL, float phi, const TimeEstBC& t) + : firstClusterIndex(idx0), + secondClusterIndex(idx1), + tanLambda(tanL), + phi(phi), + mTime(t) {} + GPUhdi() bool operator<(const Tracklet& o) const noexcept + { + return (firstClusterIndex != o.firstClusterIndex) ? firstClusterIndex < o.firstClusterIndex : secondClusterIndex < o.secondClusterIndex; + } + GPUhdi() bool operator==(const Tracklet& o) const noexcept + { + return firstClusterIndex == o.firstClusterIndex && secondClusterIndex == o.secondClusterIndex; + } + GPUhdi() bool isCompatible(const Tracklet& o) const { return mTime.isCompatible(o.mTime); } + GPUhd() void print() const + { + LOGP(info, "TRKLT: fClIdx:{} sClIdx:{} ts:{}+/-{} TgL={} Phi={}", firstClusterIndex, secondClusterIndex, mTime.getTimeStamp(), mTime.getTimeStampError(), tanLambda, phi); + } + GPUhd() auto& getTimeStamp() noexcept { return mTime; } + GPUhd() const auto& getTimeStamp() const noexcept { return mTime; } + + int firstClusterIndex{constants::UnusedIndex}; + int secondClusterIndex{constants::UnusedIndex}; + float tanLambda{constants::UnsetValue}; + float phi{constants::UnsetValue}; + TimeEstBC mTime; + + ClassDefNV(Tracklet, 1); +}; + +} // namespace o2::itsmft::tracking + +#endif /* ALICEO2_ITSMFT_TRACKING_INCLUDE_TRACKLET_H_ */ diff --git a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/Types.h b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/Types.h new file mode 100644 index 0000000000000..bafc09042d3da --- /dev/null +++ b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/Types.h @@ -0,0 +1,42 @@ +// 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 Types.h +/// \brief Shared data-format types for ITSMFT CA tracking +/// + +#ifndef ALICEO2_ITSMFT_TRACKING_TYPES_H_ +#define ALICEO2_ITSMFT_TRACKING_TYPES_H_ + +#include + +#include "CommonDataFormat/TimeStamp.h" +#include "DataFormatsITS/TimeEstBC.h" +#include "DataFormatsITS/TrackITS.h" +#include "DataFormatsITS/Vertex.h" + +namespace o2::itsmft::tracking +{ + +// BC time types used by ROFLookupTables (same layout as DataFormatsITS/TimeEstBC.h) +using TimeStampType = uint32_t; +using TimeStampErrorType = uint16_t; +using TimeStamp = o2::dataformats::TimeStampWithError; + +using TimeEstBC = o2::its::TimeEstBC; +using Vertex = o2::its::Vertex; +using VertexLabel = o2::its::VertexLabel; +using TrackITS = o2::its::TrackITS; +using TrackITSExt = o2::its::TrackITSExt; + +} // namespace o2::itsmft::tracking + +#endif /* ALICEO2_ITSMFT_TRACKING_TYPES_H_ */ diff --git a/Detectors/ITSMFT/common/tracking/src/Cluster.cxx b/Detectors/ITSMFT/common/tracking/src/Cluster.cxx new file mode 100644 index 0000000000000..be3760afada8f --- /dev/null +++ b/Detectors/ITSMFT/common/tracking/src/Cluster.cxx @@ -0,0 +1,87 @@ +// 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 Cluster.cxx +/// \brief +/// +#include "GPUCommonMath.h" +#include "GPUCommonArray.h" + +#include "ITSMFTTracking/Cluster.h" +#include "ITSMFTTracking/MathUtils.h" +#include "ITSMFTTracking/IndexTableUtils.h" + +using namespace o2::itsmft::tracking; + +using math_utils::computePhi; +using math_utils::getNormalizedPhi; + +Cluster::Cluster(const float x, const float y, const float z, const int index) + : xCoordinate{x}, + yCoordinate{y}, + zCoordinate{z}, + phi{getNormalizedPhi(computePhi(x, y))}, + radius{o2::gpu::GPUCommonMath::Hypot(x, y)}, + clusterId{index}, + indexTableBinIndex{0} +{ + // Nothing to do +} + +template +Cluster::Cluster(const int layerIndex, const IndexTableUtils& utils, const Cluster& other) + : xCoordinate{other.xCoordinate}, + yCoordinate{other.yCoordinate}, + zCoordinate{other.zCoordinate}, + phi{getNormalizedPhi(computePhi(other.xCoordinate, other.yCoordinate))}, + radius{o2::gpu::GPUCommonMath::Hypot(other.xCoordinate, other.yCoordinate)}, + clusterId{other.clusterId}, + indexTableBinIndex{utils.getBinIndex(utils.getColBinIndex(layerIndex, zCoordinate), + utils.getRowBinIndex(phi))} +//, montecarloId{ other.montecarloId } +{ + // Nothing to do +} + +template +Cluster::Cluster(const int layerIndex, const float3& primaryVertex, const IndexTableUtils& utils, const Cluster& other) + : xCoordinate{other.xCoordinate}, + yCoordinate{other.yCoordinate}, + zCoordinate{other.zCoordinate}, + phi{getNormalizedPhi( + computePhi(xCoordinate - primaryVertex.x, yCoordinate - primaryVertex.y))}, + radius{o2::gpu::GPUCommonMath::Hypot(xCoordinate - primaryVertex.x, yCoordinate - primaryVertex.y)}, + clusterId{other.clusterId}, + indexTableBinIndex{utils.getBinIndex(utils.getColBinIndex(layerIndex, zCoordinate), + utils.getRowBinIndex(phi))} +{ + // Nothing to do +} + +GPUhd() void Cluster::print() const +{ + printf("Cluster: %f %f %f %f %f %d %d\n", xCoordinate, yCoordinate, zCoordinate, phi, radius, clusterId, indexTableBinIndex); +} + +TrackingFrameInfo::TrackingFrameInfo(float x, float y, float z, float xTF, float alpha, std::array&& posTF, + std::array&& covTF) + : xCoordinate{x}, yCoordinate{y}, zCoordinate{z}, xTrackingFrame{xTF}, alphaTrackingFrame{alpha}, positionTrackingFrame{posTF}, covarianceTrackingFrame{covTF} +{ + // Nothing to do +} + +GPUhd() void TrackingFrameInfo::print() const +{ + printf("x: %f y: %f z: %f xTF: %f alphaTF: %f posTF: %f %f covTF: %f %f %f\n", + xCoordinate, yCoordinate, zCoordinate, xTrackingFrame, alphaTrackingFrame, + positionTrackingFrame[0], positionTrackingFrame[1], + covarianceTrackingFrame[0], covarianceTrackingFrame[1], covarianceTrackingFrame[2]); +} diff --git a/Detectors/ITSMFT/common/tracking/src/ClusterLines.cxx b/Detectors/ITSMFT/common/tracking/src/ClusterLines.cxx new file mode 100644 index 0000000000000..939cd85828f73 --- /dev/null +++ b/Detectors/ITSMFT/common/tracking/src/ClusterLines.cxx @@ -0,0 +1,217 @@ +// 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 "Framework/Logger.h" +#include "ITSMFTTracking/ClusterLines.h" + +namespace o2::itsmft::tracking +{ + +Line::Line(const Tracklet& tracklet, const Cluster* innerClusters, const Cluster* outerClusters) : mTime(tracklet.mTime) +{ + const auto& inner = innerClusters[tracklet.firstClusterIndex]; + const auto& outer = outerClusters[tracklet.secondClusterIndex]; + + originPoint = SVector3f(inner.xCoordinate, inner.yCoordinate, inner.zCoordinate); + cosinesDirector = SVector3f(outer.xCoordinate - inner.xCoordinate, + outer.yCoordinate - inner.yCoordinate, + outer.zCoordinate - inner.zCoordinate); + cosinesDirector /= std::sqrt(ROOT::Math::Dot(cosinesDirector, cosinesDirector)); +} + +float Line::getDistance2FromPoint(const Line& line, const std::array& point) +{ + const SVector3f p(point.data(), 3); + const SVector3f delta = p - line.originPoint; + const float proj = ROOT::Math::Dot(delta, line.cosinesDirector); + const SVector3f residual = delta - proj * line.cosinesDirector; + return ROOT::Math::Dot(residual, residual); +} + +float Line::getDistanceFromPoint(const Line& line, const std::array& point) +{ + return std::sqrt(getDistance2FromPoint(line, point)); +} + +float Line::getDCA2(const Line& firstLine, const Line& secondLine, const float precision) +{ + const SVector3f n = ROOT::Math::Cross(firstLine.cosinesDirector, secondLine.cosinesDirector); + const float norm2 = ROOT::Math::Dot(n, n); + + if (norm2 <= precision * precision) { + // lines are parallel, fall back to point-to-line distance + const SVector3f d = secondLine.originPoint - firstLine.originPoint; + const float proj = ROOT::Math::Dot(d, firstLine.cosinesDirector); + const SVector3f residual = d - proj * firstLine.cosinesDirector; + return ROOT::Math::Dot(residual, residual); + } + + const SVector3f delta = secondLine.originPoint - firstLine.originPoint; + const float numerator = ROOT::Math::Dot(delta, n); + return (numerator * numerator) / norm2; +} + +float Line::getDCA(const Line& firstLine, const Line& secondLine, const float precision) +{ + return std::sqrt(getDCA2(firstLine, secondLine, precision)); +} + +Line::SMatrix3f Line::getDCAComponents(const Line& line, const std::array& point) +{ + const SVector3f p(point.data(), 3); + const SVector3f delta = line.originPoint - p; + const float proj = ROOT::Math::Dot(line.cosinesDirector, delta); + const SVector3f residual = delta - proj * line.cosinesDirector; + + // symmetric 3x3: diagonal = residual components, off-diagonal = 2D projected distances + SMatrix3f m; + m(0, 0) = residual(0); + m(1, 1) = residual(1); + m(2, 2) = residual(2); + m(0, 1) = std::hypot(m(0, 0), m(1, 1)); + m(0, 2) = std::hypot(m(0, 0), m(2, 2)); + m(1, 2) = std::hypot(m(1, 1), m(2, 2)); + return m; +} + +bool Line::isEmpty() const noexcept +{ + return ROOT::Math::Dot(originPoint, originPoint) == 0.f && + ROOT::Math::Dot(cosinesDirector, cosinesDirector) == 0.f; +} + +void Line::print() const +{ + LOGP(info, "\tLine: originPoint = ({}, {}, {}), cosinesDirector = ({}, {}, {}) ts={}+-{}", + originPoint(0), originPoint(1), originPoint(2), + cosinesDirector(0), cosinesDirector(1), cosinesDirector(2), + mTime.getTimeStamp(), mTime.getTimeStampError()); +} + +// Accumulate the weighted normal equation contributions (A matrix and B vector) +// from a single line into the running sums. The covariance is assumed to be +// diagonal and uniform ({1,1,1}) so the weights simplify accordingly. +// The A matrix entry (i,j) = (delta_ij - d_i*d_j) / det, and the B vector +// entry b_i = sum_j d_j*(d_j*o_i - d_i*o_j) / det, where d = cosinesDirector +// and o = originPoint. +void ClusterLines::accumulate(const Line& line) +{ + const ROOT::Math::SVector d(line.cosinesDirector(0), line.cosinesDirector(1), line.cosinesDirector(2)); + const ROOT::Math::SVector o(line.originPoint(0), line.originPoint(1), line.originPoint(2)); + + // == 1 for normalised directors, kept for generality + const double det = ROOT::Math::Dot(d, d); + + // A matrix (symmetric): A_ij = (delta_ij * |d|^2 - d_i * d_j) / det + for (int i = 0; i < 3; ++i) { + for (int j = i; j < 3; ++j) { + mAMatrix(i, j) += ((i == j ? det : 0.) - d(i) * d(j)) / det; + } + } + + // B vector: b_i = (d_i * dot(d,o) - |d|^2 * o_i) / det + const double dDotO = ROOT::Math::Dot(d, o); + for (int i = 0; i < 3; ++i) { + mBMatrix(i) += (d(i) * dDotO - det * o(i)) / det; + } +} + +ClusterLines::ClusterLines(const int firstLabel, const Line& firstLine, const int secondLabel, const Line& secondLine) : mTime(firstLine.mTime) +{ + mTime += secondLine.mTime; + + mLabels.push_back(firstLabel); + if (secondLabel > 0) { + mLabels.push_back(secondLabel); // don't add info in case of beamline used + } + + accumulate(firstLine); + accumulate(secondLine); + computeClusterCentroid(); + + // RMS2: running mean update + mRMS2 = Line::getDCAComponents(firstLine, mVertex); + const auto tmpRMS2 = Line::getDCAComponents(secondLine, mVertex); + mRMS2 += (tmpRMS2 - mRMS2) * (1.f / static_cast(getSize())); + + // AvgDistance2 + mAvgDistance2 = Line::getDistance2FromPoint(firstLine, mVertex); + mAvgDistance2 += (Line::getDistance2FromPoint(secondLine, mVertex) - mAvgDistance2) / (float)getSize(); +} + +ClusterLines::ClusterLines(gsl::span lineLabels, gsl::span lines) +{ + if (lineLabels.size() < 2) { + return; + } + + mLabels.reserve(lineLabels.size()); + mTime = lines[lineLabels[0]].mTime; + for (size_t index = 0; index < lineLabels.size(); ++index) { + const auto lineLabel = lineLabels[index]; + if (index > 0) { + mTime += lines[lineLabel].mTime; + } + mLabels.push_back(lineLabel); + accumulate(lines[lineLabel]); + } + + computeClusterCentroid(); + if (!mIsValid) { + return; + } + + mRMS2 = Line::getDCAComponents(lines[lineLabels[0]], mVertex); + mAvgDistance2 = Line::getDistance2FromPoint(lines[lineLabels[0]], mVertex); + for (size_t index = 1; index < lineLabels.size(); ++index) { + const auto lineLabel = lineLabels[index]; + const auto tmpRMS2 = Line::getDCAComponents(lines[lineLabel], mVertex); + mRMS2 += (tmpRMS2 - mRMS2) * (1.f / static_cast(index + 1)); + mAvgDistance2 += (Line::getDistance2FromPoint(lines[lineLabel], mVertex) - mAvgDistance2) / static_cast(index + 1); + } +} + +void ClusterLines::add(const int lineLabel, const Line& line) +{ + mTime += line.mTime; + mLabels.push_back(lineLabel); + + accumulate(line); + computeClusterCentroid(); + mAvgDistance2 += (Line::getDistance2FromPoint(line, mVertex) - mAvgDistance2) / (float)getSize(); +} + +void ClusterLines::computeClusterCentroid() +{ + // Solve the 3x3 symmetric linear system AX = -B using SMatrix inversion. + // Invert() returns false if the matrix is singular or ill-conditioned. + SMatrix3 invA{mAMatrix}; + mIsValid = invA.Invert(); + if (!mIsValid) { + return; + } + + SVector3 result = invA * mBMatrix; + mVertex[0] = static_cast(-result(0)); + mVertex[1] = static_cast(-result(1)); + mVertex[2] = static_cast(-result(2)); +} + +bool ClusterLines::operator==(const ClusterLines& rhs) const noexcept +{ + return mRMS2 == rhs.mRMS2 && + mVertex == rhs.mVertex && + mLabels == rhs.mLabels && + mAvgDistance2 == rhs.mAvgDistance2; +} + +} // namespace o2::itsmft::tracking diff --git a/Detectors/ITSMFT/common/tracking/src/Configuration.cxx b/Detectors/ITSMFT/common/tracking/src/Configuration.cxx new file mode 100644 index 0000000000000..ce3b3926c5079 --- /dev/null +++ b/Detectors/ITSMFT/common/tracking/src/Configuration.cxx @@ -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. + +#include +#include +#include + +#include "ITSMFTTracking/Configuration.h" + +namespace o2::itsmft +{ + +std::string TrackingParameters::asString() const +{ + std::string str = std::format("NColB:{} NRowB:{} PerVtx:{} DropFail:{} TtklMinPt:{:.2f} MinCl:{}", ColBins, RowBins, PerPrimaryVertexProcessing, DropTFUponFailure, TrackletMinPt, MinTrackLength); + auto isSet = [](auto e) { return e >= 0; }; + auto isAnySet = [&isSet](auto v) { return !v.empty() && std::any_of(v.begin(), v.end(), isSet); }; + bool first = true; + for (int il = NLayers; il >= MinTrackLength; il--) { + int slot = NLayers - il; + if (slot < (int)MinPt.size() && MinPt[slot] > 0) { + if (first) { + first = false; + str += " MinPt: "; + } + str += std::format("L{}:{:.2f} ", il, MinPt[slot]); + } + } + if (isAnySet(SystError2Row) || isAnySet(SystError2Col)) { + str += " SystErrRow/Col:"; + for (size_t i = 0; i < SystError2Row.size(); i++) { + str += std::format("{:.2e}/{:.2e} ", SystError2Row[i], SystError2Col[i]); + } + } + if (isAnySet(AddTimeError)) { + str += " AddTimeError:"; + for (unsigned int i : AddTimeError) { + str += std::format("{} ", i); + } + } + if (SharedMaxClusters) { + str += std::format(" ShaMaxCls:{} ", SharedMaxClusters); + } + if (AllowSharingFirstCluster) { + str += std::format(" ShaClsDPhi:{} ShaClsDEta:{} ShaClsSign:{}", SharedClusterMaxDeltaPhi, SharedClusterMaxDeltaEta, SharedClusterOppositeSign); + } + if (MaxHoles) { + str += std::format(" MaxHoles:{} HoleMask:{:016b}", MaxHoles, HoleLayerMask); + } + if (std::numeric_limits::max() != MaxMemory) { + str += std::format(" MemLimit {:.2f} GB", double(MaxMemory) / (1024.f * 1024.f * 1024.f)); + } + return str; +} + +std::string VertexingParameters::asString() const +{ + std::string str = std::format("NColB:{} NRowB:{} MinVtxCont:{} SupLowMultDebris:{} MaxTrkltCls:{} ZCut:{} PhCut:{} PairCut:{} ClCut:{} SeedRad:{}x{}", + ColBins, RowBins, clusterContributorsCut, suppressLowMultDebris, maxTrackletsPerCluster, zCut, phiCut, pairCut, clusterCut, seedMemberRadiusTime, seedMemberRadiusZ); + if (std::numeric_limits::max() != MaxMemory) { + str += std::format(" MemLimit {:.2f} GB", double(MaxMemory) / (1024.f * 1024.f * 1024.f)); + } + return str; +} + +} // namespace o2::itsmft diff --git a/Detectors/ITSMFT/common/tracking/src/IOUtils.cxx b/Detectors/ITSMFT/common/tracking/src/IOUtils.cxx new file mode 100644 index 0000000000000..291ebce622169 --- /dev/null +++ b/Detectors/ITSMFT/common/tracking/src/IOUtils.cxx @@ -0,0 +1,211 @@ +// 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 IOUtils.cxx +/// \brief Shared cluster I/O utilities for ITS and MFT (based on ITStracking/IOUtils.cxx) +/// + +#include "ITSMFTTracking/IOUtils.h" +#include "ITSMFTTracking/Cluster.h" +#include "ITSMFTTracking/TrackingConfigParam.h" + +#include "Framework/Logger.h" +#include "ITSBase/GeometryTGeo.h" +#include "MFTBase/GeometryTGeo.h" +#include "MathUtils/Utils.h" + +namespace +{ +constexpr int PrimaryVertexLayerId{-1}; +constexpr int EventLabelsSeparator{-1}; + +template +bool shouldApplySysErrors() +{ + const auto& conf = o2::itsmft::TrackerParamConfig::Instance(); + for (int il = 0; il < o2::itsmft::TrackerParamConfig::getNLayers(); il++) { + if (conf.sysErr2Row[il] > 0.f || conf.sysErr2Col[il] > 0.f) { + return true; + } + } + return false; +} + +template +void loadClusterTrackingFrameInfoImpl(GeomT* geom, + const o2::itsmft::CompClusterExt& c, + gsl::span::iterator& pattIt, + const o2::itsmft::TopologyDictionary* dict, + int& layer, + unsigned int& clusterSize, + o2::itsmft::tracking::TrackingFrameInfo& tfInfo, + bool applySysErrors) +{ + const auto sensorID = c.getSensorID(); + layer = geom->getLayer(sensorID); + clusterSize = o2::itsmft::ioutils::extractClusterSize(c, pattIt, dict); + + float sigma2Row{0.f}; + float sigma2Col{0.f}; + const auto locXYZ = o2::itsmft::ioutils::extractClusterData(c, pattIt, dict, sigma2Row, sigma2Col); + if (applySysErrors && shouldApplySysErrors()) { + const auto layerId = geom->getLayer(sensorID); + const auto& conf = o2::itsmft::TrackerParamConfig::Instance(); + sigma2Row += conf.sysErr2Row[layerId]; + sigma2Col += conf.sysErr2Col[layerId]; + } + + if constexpr (DetId == o2::detectors::DetID::ITS) { + const auto trkXYZ = geom->getMatrixT2L(sensorID) ^ locXYZ; + const auto gloXYZ = geom->getMatrixL2G(sensorID) * locXYZ; + tfInfo = o2::itsmft::tracking::TrackingFrameInfo{ + gloXYZ.x(), gloXYZ.y(), gloXYZ.z(), trkXYZ.x(), geom->getSensorRefAlpha(sensorID), + std::array{trkXYZ.y(), trkXYZ.z()}, + std::array{sigma2Row, 0.f, sigma2Col}}; + } else { + const auto gloXYZ = geom->getMatrixL2G(sensorID) * locXYZ; + // ALPIDE row (local X) -> global X, column (local Z) -> global Y + tfInfo = o2::itsmft::tracking::TrackingFrameInfo{ + gloXYZ.x(), gloXYZ.y(), gloXYZ.z(), gloXYZ.x(), 0.f, + std::array{gloXYZ.y(), gloXYZ.z()}, + std::array{sigma2Row, 0.f, sigma2Col}}; + } +} + +template +void fillOutputClusters(GeomT* geom, + gsl::span clusters, + gsl::span::iterator& pattIt, + std::vector>& output, + const o2::itsmft::TopologyDictionary* dict, + const o2::itsmft::TrackerParamConfig& conf, + bool applyMisalignment) +{ + for (const auto& c : clusters) { + float sigma2Row{0.f}, sigma2Col{0.f}; + const float sigmaRowCol{0.f}; // row-column covariance (unused) + auto locXYZ = o2::itsmft::ioutils::extractClusterData(c, pattIt, dict, sigma2Row, sigma2Col); + if (applyMisalignment) { + const auto layerId = geom->getLayer(c.getSensorID()); + sigma2Row += conf.sysErr2Row[layerId]; + sigma2Col += conf.sysErr2Col[layerId]; + } + o2::math_utils::Point3D outXYZ{}; + if constexpr (DetId == o2::detectors::DetID::ITS) { + outXYZ = geom->getMatrixT2L(c.getSensorID()) ^ locXYZ; + } else { + outXYZ = geom->getMatrixL2G(c.getSensorID()) * locXYZ; + } + auto& cl3d = output.emplace_back(c.getSensorID(), outXYZ); + cl3d.setErrors(sigma2Row, sigma2Col, sigmaRowCol); + } +} + +} // namespace + +namespace o2::itsmft::ioutils +{ + +void fillMatrixCache(o2::detectors::DetID::ID detId) +{ + const auto mask = o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::L2G); + if (detId == o2::detectors::DetID::ITS) { + o2::its::GeometryTGeo::Instance()->fillMatrixCache(mask); + } else if (detId == o2::detectors::DetID::MFT) { + o2::mft::GeometryTGeo::Instance()->fillMatrixCache(mask); + } else { + LOGP(fatal, "Unsupported detector id {} in fillMatrixCache", static_cast(detId)); + } +} + +int getClusterLayer(o2::detectors::DetID::ID detId, const CompClusterExt& cluster) +{ + if (detId == o2::detectors::DetID::ITS) { + return o2::its::GeometryTGeo::Instance()->getLayer(cluster.getSensorID()); + } + if (detId == o2::detectors::DetID::MFT) { + return o2::mft::GeometryTGeo::Instance()->getLayer(cluster.getSensorID()); + } + LOGP(fatal, "Unsupported detector id {} in getClusterLayer", static_cast(detId)); + return -1; +} + +template +void loadClusterTrackingFrameInfo(const CompClusterExt& c, + gsl::span::iterator& pattIt, + const TopologyDictionary* dict, + int& layer, + unsigned int& clusterSize, + tracking::TrackingFrameInfo& tfInfo, + bool applySysErrors) +{ + if constexpr (DetId == o2::detectors::DetID::ITS) { + loadClusterTrackingFrameInfoImpl(o2::its::GeometryTGeo::Instance(), c, pattIt, dict, layer, clusterSize, tfInfo, applySysErrors); + } else { + loadClusterTrackingFrameInfoImpl(o2::mft::GeometryTGeo::Instance(), c, pattIt, dict, layer, clusterSize, tfInfo, applySysErrors); + } +} + +template void loadClusterTrackingFrameInfo(const CompClusterExt& c, + gsl::span::iterator& pattIt, + const TopologyDictionary* dict, + int& layer, + unsigned int& clusterSize, + tracking::TrackingFrameInfo& tfInfo, + bool applySysErrors); + +template void loadClusterTrackingFrameInfo(const CompClusterExt& c, + gsl::span::iterator& pattIt, + const TopologyDictionary* dict, + int& layer, + unsigned int& clusterSize, + tracking::TrackingFrameInfo& tfInfo, + bool applySysErrors); + +template +void convertCompactClusters(gsl::span clusters, + gsl::span::iterator& pattIt, + std::vector>& output, + const TopologyDictionary* dict) +{ + const auto mask = o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::L2G); + + const auto& conf = TrackerParamConfig::Instance(); + bool applyMisalignment = false; + for (int il = 0; il < TrackerParamConfig::getNLayers(); il++) { + if (conf.sysErr2Row[il] > 0.f || conf.sysErr2Col[il] > 0.f) { + applyMisalignment = true; + break; + } + } + + if constexpr (DetId == o2::detectors::DetID::ITS) { + auto* geom = o2::its::GeometryTGeo::Instance(); + geom->fillMatrixCache(mask); + fillOutputClusters(geom, clusters, pattIt, output, dict, conf, applyMisalignment); + } else { + auto* geom = o2::mft::GeometryTGeo::Instance(); + geom->fillMatrixCache(mask); + fillOutputClusters(geom, clusters, pattIt, output, dict, conf, applyMisalignment); + } +} + +template void convertCompactClusters(gsl::span clusters, + gsl::span::iterator& pattIt, + std::vector>& output, + const TopologyDictionary* dict); + +template void convertCompactClusters(gsl::span clusters, + gsl::span::iterator& pattIt, + std::vector>& output, + const TopologyDictionary* dict); + +} // namespace o2::itsmft::ioutils diff --git a/Detectors/ITSMFT/common/tracking/src/ITSMFTTrackingLinkDef.h b/Detectors/ITSMFT/common/tracking/src/ITSMFTTrackingLinkDef.h new file mode 100644 index 0000000000000..3deddc108e4b5 --- /dev/null +++ b/Detectors/ITSMFT/common/tracking/src/ITSMFTTrackingLinkDef.h @@ -0,0 +1,27 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifdef __CLING__ + +#pragma link off all globals; +#pragma link off all classes; +#pragma link off all functions; + +#pragma link C++ class o2::itsmft::VertexerParamConfig + ; +#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::itsmft::VertexerParamConfig> + ; + +#pragma link C++ class o2::itsmft::TrackerParamConfig < o2::detectors::DetID::ITS> + ; +#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::itsmft::TrackerParamConfig < o2::detectors::DetID::ITS>> + ; + +#pragma link C++ class o2::itsmft::TrackerParamConfig < o2::detectors::DetID::MFT> + ; +#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::itsmft::TrackerParamConfig < o2::detectors::DetID::MFT>> + ; + +#endif diff --git a/Detectors/ITSMFT/common/tracking/src/TimeFrame.cxx b/Detectors/ITSMFT/common/tracking/src/TimeFrame.cxx new file mode 100644 index 0000000000000..1f98dd65f522c --- /dev/null +++ b/Detectors/ITSMFT/common/tracking/src/TimeFrame.cxx @@ -0,0 +1,539 @@ +// 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 +/// + +#include + +#include "Framework/Logger.h" +#include "ITSMFTTracking/TimeFrame.h" +#include "ITSMFTTracking/IOUtils.h" +#include "ITSMFTTracking/MathUtils.h" +#include "DataFormatsITSMFT/CompCluster.h" +#include "DataFormatsITSMFT/ROFRecord.h" +#include "DataFormatsITSMFT/TopologyDictionary.h" +#include "ITSMFTTracking/BoundedAllocator.h" +#include "ITSMFTTracking/Constants.h" +#include "DetectorsCommonDataFormats/DetID.h" + +namespace +{ +struct ClusterHelper { + float rowCoord; + float r; + int bin; + int ind; +}; + +void loadClusterForDet(o2::detectors::DetID::ID detId, + const o2::itsmft::CompClusterExt& cluster, + gsl::span::iterator& pattIt, + const o2::itsmft::TopologyDictionary* dict, + int& layer, + unsigned int& clusterSize, + o2::itsmft::tracking::TrackingFrameInfo& tfInfo) +{ + switch (detId) { + case o2::detectors::DetID::ITS: + o2::itsmft::ioutils::loadClusterTrackingFrameInfo(cluster, pattIt, dict, layer, clusterSize, tfInfo); + break; + case o2::detectors::DetID::MFT: + o2::itsmft::ioutils::loadClusterTrackingFrameInfo(cluster, pattIt, dict, layer, clusterSize, tfInfo); + break; + default: + LOGP(fatal, "Unsupported detector id {} in loadROFrameData", static_cast(detId)); + } +} + +template +void configureIndexTableUtils(o2::itsmft::IndexTableUtils& utils, + o2::detectors::DetID::ID detId, + const o2::itsmft::TrackingParameters& params) +{ + if (detId == o2::detectors::DetID::MFT) { + constexpr float defaultYMin{-20.f}; + constexpr float defaultYMax{20.f}; + const bool hasRowRange = params.IndexRowMax != 0.f; + const float rowMin = hasRowRange ? params.IndexRowMin : defaultYMin; + const float rowMax = hasRowRange ? params.IndexRowMax : defaultYMax; + utils.setTrackingParametersXY(params, rowMin, rowMax); + } else { + utils.setTrackingParameters(params); + } +} +} // namespace + +namespace o2::itsmft::tracking +{ + +using o2::itsmft::IndexTableCoordType; +using o2::itsmft::IterationStep; +using o2::itsmft::TrackingParameters; + +template +void TimeFrame::addPrimaryVertex(const Vertex& vert) +{ + mPrimaryVertices.emplace_back(vert); + if (!isBeamPositionOverridden) { + const float w = vert.getNContributors(); + mBeamPos[0] = (mBeamPos[0] * mBeamPosWeight + vert.getX() * w) / (mBeamPosWeight + w); + mBeamPos[1] = (mBeamPos[1] * mBeamPosWeight + vert.getY() * w) / (mBeamPosWeight + w); + mBeamPosWeight += w; + } +} + +template +void TimeFrame::loadROFrameData(gsl::span rofs, + gsl::span clusters, + gsl::span::iterator& pattIt, + const itsmft::TopologyDictionary* dict, + int layer, + const dataformats::MCTruthContainer* mcLabels, + o2::detectors::DetID::ID detId) +{ + mDetId = detId; + if (NLayers != constants::nLayersForDet(detId)) { + LOGP(fatal, "TimeFrame<{}> is incompatible with detector {} (expected {} layers)", + NLayers, static_cast(detId), constants::nLayersForDet(detId)); + } + ioutils::fillMatrixCache(detId); + resetROFrameData(layer); + prepareROFrameData(clusters, layer); + + // check for missing/empty/unset rofs + // the code requires consistent monotonically increasing input without gaps + const auto& timing = mROFOverlapTableView.getLayer(layer >= 0 ? layer : 0); + if (timing.mNROFsTF != rofs.size()) { + LOGP(fatal, "Received inconsistent number of rofs on layer:{} expected:{} received:{}", layer, timing.mNROFsTF, rofs.size()); + } + + for (int32_t iRof{0}; iRof < rofs.size(); ++iRof) { + const auto& rof = rofs[iRof]; + for (int clusterId{rof.getFirstEntry()}; clusterId < rof.getFirstEntry() + rof.getNEntries(); ++clusterId) { + const auto& c = clusters[clusterId]; + int lay{0}; + unsigned int clusterSize{0}; + TrackingFrameInfo tfInfo; + loadClusterForDet(detId, c, pattIt, dict, lay, clusterSize, tfInfo); + mClusterSize[layer >= 0 ? layer : 0][clusterId] = std::clamp(clusterSize, 0u, 255u); + addTrackingFrameInfoToLayer(lay, tfInfo); + addClusterToLayer(lay, tfInfo.xCoordinate, tfInfo.yCoordinate, tfInfo.zCoordinate, mUnsortedClusters[lay].size()); + addClusterExternalIndexToLayer(lay, clusterId); + } + // effectively calculating an exclusive sum + if (layer >= 0) { + mROFramesClusters[layer][iRof + 1] = mUnsortedClusters[layer].size(); + } else { + for (unsigned int iL{0}; iL < mUnsortedClusters.size(); ++iL) { + mROFramesClusters[iL][iRof + 1] = mUnsortedClusters[iL].size(); + } + } + } + + if (layer == 1 || layer == -1) { + for (auto i = 0; i < mNTrackletsPerCluster.size(); ++i) { + mNTrackletsPerCluster[i].resize(mUnsortedClusters[1].size()); + mNTrackletsPerClusterSum[i].resize(mUnsortedClusters[1].size() + 1); + } + } + + if (mcLabels != nullptr) { + mClusterLabels[layer >= 0 ? layer : 0] = mcLabels; + } else { + mClusterLabels[layer >= 0 ? layer : 0] = nullptr; + } +} + +template +void TimeFrame::resetROFrameData(int layer) +{ + if (layer >= 0) { + deepVectorClear(mUnsortedClusters[layer], getMaybeFrameworkHostResource()); + deepVectorClear(mTrackingFrameInfo[layer], getMaybeFrameworkHostResource()); + deepVectorClear(mClusterExternalIndices[layer], mMemoryPool.get()); + clearResizeBoundedVector(mROFramesClusters[layer], mROFOverlapTableView.getLayer(layer).mNROFsTF + 1, getMaybeFrameworkHostResource()); + } else { + for (int iLayer{0}; iLayer < NLayers; ++iLayer) { + deepVectorClear(mUnsortedClusters[iLayer], getMaybeFrameworkHostResource()); + deepVectorClear(mTrackingFrameInfo[iLayer], getMaybeFrameworkHostResource()); + deepVectorClear(mClusterExternalIndices[iLayer], mMemoryPool.get()); + clearResizeBoundedVector(mROFramesClusters[iLayer], mROFOverlapTableView.getLayer(iLayer).mNROFsTF + 1, getMaybeFrameworkHostResource()); + } + } +} + +template +void TimeFrame::prepareROFrameData(gsl::span clusters, int layer) +{ + if (layer >= 0) { + mUnsortedClusters[layer].reserve(clusters.size()); + mTrackingFrameInfo[layer].reserve(clusters.size()); + mClusterExternalIndices[layer].reserve(clusters.size()); + clearResizeBoundedVector(mClusterSize[layer], clusters.size(), mMemoryPool.get()); + } else { + clearResizeBoundedVector(mClusterSize[0], clusters.size(), mMemoryPool.get()); + std::array clusterCountPerLayer{0}; + for (const auto& cls : clusters) { + ++clusterCountPerLayer[ioutils::getClusterLayer(mDetId, cls)]; + } + for (int iLayer{0}; iLayer < NLayers; ++iLayer) { + mUnsortedClusters[iLayer].reserve(clusterCountPerLayer[iLayer]); + mTrackingFrameInfo[iLayer].reserve(clusterCountPerLayer[iLayer]); + mClusterExternalIndices[iLayer].reserve(clusterCountPerLayer[iLayer]); + } + } +} + +template +void TimeFrame::prepareClusters(const TrackingParameters& trkParam, const int maxLayers) +{ + const int numBins{trkParam.RowBins * trkParam.ColBins}; + const int stride{numBins + 1}; + bounded_vector cHelper(mMemoryPool.get()); + bounded_vector clsPerBin(numBins, 0, mMemoryPool.get()); + bounded_vector lutPerBin(numBins, 0, mMemoryPool.get()); + for (int iLayer{0}, stopLayer = std::min(trkParam.NLayers, maxLayers); iLayer < stopLayer; ++iLayer) { + for (int rof{0}; rof < getNrof(iLayer); ++rof) { + if (!mROFMaskView.isROFEnabled(iLayer, rof)) { + continue; + } + const auto& unsortedClusters{getUnsortedClustersOnLayer(rof, iLayer)}; + const int clustersNum{static_cast(unsortedClusters.size())}; + auto* tableBase = mIndexTables[iLayer].data() + rof * stride; + + cHelper.resize(clustersNum); + + const bool useXYBinning = mIndexTableUtils.getCoordType() == IndexTableCoordType::XY; + for (int iCluster{0}; iCluster < clustersNum; ++iCluster) { + const Cluster& c = unsortedClusters[iCluster]; + ClusterHelper& h = cHelper[iCluster]; + + const float x = c.xCoordinate - (useXYBinning ? 0.f : mBeamPos[0]); + const float y = c.yCoordinate - (useXYBinning ? 0.f : mBeamPos[1]); + const float z = c.zCoordinate; + + const float rowCoord = useXYBinning ? c.yCoordinate : math_utils::computePhi(x, y); + const float colCoord = useXYBinning ? c.xCoordinate : z; + int colBin{mIndexTableUtils.getColBinIndex(iLayer, colCoord)}; + if (colBin < 0 || colBin >= trkParam.ColBins) { + colBin = std::clamp(colBin, 0, trkParam.ColBins - 1); + mBogusClusters[iLayer]++; + } + int bin = mIndexTableUtils.getBinIndex(colBin, mIndexTableUtils.getRowBinIndex(rowCoord)); + h.rowCoord = rowCoord; + h.r = math_utils::hypot(x, y); + mMinR[iLayer] = o2::gpu::GPUCommonMath::Min(h.r, mMinR[iLayer]); + mMaxR[iLayer] = o2::gpu::GPUCommonMath::Max(h.r, mMaxR[iLayer]); + h.bin = bin; + h.ind = clsPerBin[bin]++; + } + std::exclusive_scan(clsPerBin.begin(), clsPerBin.end(), lutPerBin.begin(), 0); + + auto clusters2beSorted{getClustersOnLayer(rof, iLayer)}; + for (int iCluster{0}; iCluster < clustersNum; ++iCluster) { + const ClusterHelper& h = cHelper[iCluster]; + Cluster& c = clusters2beSorted[lutPerBin[h.bin] + h.ind]; + + c = unsortedClusters[iCluster]; + c.phi = useXYBinning ? math_utils::computePhi(c.xCoordinate, c.yCoordinate) : h.rowCoord; + c.radius = h.r; + c.indexTableBinIndex = h.bin; + } + std::copy_n(lutPerBin.data(), clsPerBin.size(), tableBase); + std::fill_n(tableBase + clsPerBin.size(), stride - clsPerBin.size(), clustersNum); + + std::fill(clsPerBin.begin(), clsPerBin.end(), 0); + cHelper.clear(); + } + } +} + +template +void TimeFrame::initVertexingTopology(const TrackingParameters& trkParam) +{ + mVertexingTopology.init(3, trkParam.MaxHoles, LayerMask{trkParam.HoleLayerMask}); +} + +template +void TimeFrame::initDefaultTrackingTopology(const TrackingParameters& trkParam, const int maxLayers) +{ + mDefaultTrackingTopology.init(maxLayers, trkParam.MaxHoles, LayerMask{trkParam.HoleLayerMask}); +} + +template +void TimeFrame::initTrackerTopologies(gsl::span trkParams, const int maxLayers) +{ + mTrackerTopologies.resize(trkParams.size()); + for (size_t iteration = 0; iteration < trkParams.size(); ++iteration) { + const int iterationMaxLayers = std::min(maxLayers, trkParams[iteration].NLayers); + mTrackerTopologies[iteration].init(iterationMaxLayers, trkParams[iteration].MaxHoles, LayerMask{trkParams[iteration].HoleLayerMask}); + } +} + +template +void TimeFrame::initialise(const TrackingParameters& trkParam, const int maxLayers, const int iteration) +{ + mTrackingTopologyView = iteration != constants::UnusedIndex ? mTrackerTopologies[iteration].getView() : (maxLayers == 3 ? mVertexingTopology.getView() : mDefaultTrackingTopology.getView()); + + if (trkParam.PassFlags[IterationStep::FirstPass]) { + deepVectorClear(mTracks); + deepVectorClear(mTracksLabel); + deepVectorClear(mLines); + deepVectorClear(mLinesLabels); + if (trkParam.PassFlags[IterationStep::ResetVertices]) { + deepVectorClear(mPrimaryVertices); + deepVectorClear(mPrimaryVerticesLabels); + } + clearResizeBoundedVector(mLinesLabels, getNrof(1), mMemoryPool.get()); + configureIndexTableUtils(mIndexTableUtils, mDetId, trkParam); + clearResizeBoundedVector(mPositionResolution, trkParam.NLayers, mMemoryPool.get()); + clearResizeBoundedVector(mBogusClusters, trkParam.NLayers, mMemoryPool.get()); + deepVectorClear(mTrackletClusters); + for (unsigned int iLayer{0}; iLayer < std::min((int)mClusters.size(), maxLayers); ++iLayer) { + clearResizeBoundedVector(mClusters[iLayer], mUnsortedClusters[iLayer].size(), getMaybeFrameworkHostResource(maxLayers != NLayers)); + clearResizeBoundedVector(mUsedClusters[iLayer], mUnsortedClusters[iLayer].size(), getMaybeFrameworkHostResource(maxLayers != NLayers)); + mPositionResolution[iLayer] = o2::gpu::CAMath::Sqrt((0.5f * (trkParam.SystError2Col[iLayer] + trkParam.SystError2Row[iLayer])) + (trkParam.LayerResolution[iLayer] * trkParam.LayerResolution[iLayer])); + } + clearResizeBoundedVector(mLines, getNrof(1), mMemoryPool.get()); + clearResizeBoundedVector(mTrackletClusters, getNrof(1), mMemoryPool.get()); + + for (int iLayer{0}; iLayer < NLayers; ++iLayer) { + clearResizeBoundedVector(mIndexTables[iLayer], getNrof(iLayer) * ((trkParam.ColBins * trkParam.RowBins) + 1), getMaybeFrameworkHostResource()); + } + for (int iLayer{0}; iLayer < trkParam.NLayers; ++iLayer) { + if (trkParam.SystError2Row[iLayer] > 0.f || trkParam.SystError2Col[iLayer] > 0.f) { + for (auto& tfInfo : mTrackingFrameInfo[iLayer]) { + /// Account for alignment systematics in the cluster covariance matrix + tfInfo.covarianceTrackingFrame[0] += trkParam.SystError2Row[iLayer]; + tfInfo.covarianceTrackingFrame[2] += trkParam.SystError2Col[iLayer]; + } + } + } + + mMinR.fill(std::numeric_limits::max()); + mMaxR.fill(std::numeric_limits::min()); + } + clearResizeBoundedVector(mCells, mTrackingTopologyView.nCells, mMemoryPool.get()); + clearResizeBoundedVector(mCellsLookupTable, mTrackingTopologyView.nCells, mMemoryPool.get()); + clearResizeBoundedVector(mCellsNeighbours, mTrackingTopologyView.nCells, mMemoryPool.get()); + clearResizeBoundedVector(mCellsNeighboursTopology, mTrackingTopologyView.nCells, mMemoryPool.get()); + clearResizeBoundedVector(mCellsNeighboursLUT, mTrackingTopologyView.nCells, mMemoryPool.get()); + clearResizeBoundedVector(mCellLabels, mTrackingTopologyView.nCells, mMemoryPool.get()); + clearResizeBoundedVector(mTracklets, mTrackingTopologyView.nTransitions, mMemoryPool.get()); + clearResizeBoundedVector(mTrackletLabels, mTrackingTopologyView.nTransitions, mMemoryPool.get()); + clearResizeBoundedVector(mTrackletsLookupTable, mTrackingTopologyView.nTransitions, mMemoryPool.get()); + clearResizeBoundedVector(mTransitionPhiCuts, mTrackingTopologyView.nTransitions, mMemoryPool.get()); + clearResizeBoundedVector(mTransitionMSAngles, mTrackingTopologyView.nTransitions, mMemoryPool.get()); + mNTrackletsPerROF.resize(2); + for (auto& v : mNTrackletsPerROF) { + v = bounded_vector(getNrof(1) + 1, 0, mMemoryPool.get()); + } + if (trkParam.PassFlags[IterationStep::RebuildClusterLUT]) { + prepareClusters(trkParam, maxLayers); + } + mTotalTracklets = {0, 0}; + if (maxLayers < trkParam.NLayers) { // Vertexer only, but in both iterations + for (size_t iLayer{0}; iLayer < maxLayers; ++iLayer) { + deepVectorClear(mUsedClusters[iLayer]); + clearResizeBoundedVector(mUsedClusters[iLayer], mUnsortedClusters[iLayer].size(), mMemoryPool.get()); + } + } + + // estimate MS per layer + std::array msAngles{}; + for (unsigned int iLayer{0}; iLayer < NLayers; ++iLayer) { + msAngles[iLayer] = math_utils::MSangle(0.14f, trkParam.TrackletMinPt, trkParam.LayerxX0[iLayer]); + mPositionResolution[iLayer] = o2::gpu::CAMath::Sqrt((0.5f * (trkParam.SystError2Col[iLayer] + trkParam.SystError2Row[iLayer])) + (trkParam.LayerResolution[iLayer] * trkParam.LayerResolution[iLayer])); + } + + // for each transition calculate the phi-cuts + integrated MS + float oneOverR{0.001f * 0.3f * std::abs(mBz) / trkParam.TrackletMinPt}; + for (int transitionId{0}; transitionId < (int)mTracklets.size(); ++transitionId) { + const auto& transition = mTrackingTopologyView.getTransition(transitionId); + float ms2 = 0.; + for (int layer = transition.fromLayer; layer < transition.toLayer; ++layer) { + ms2 += math_utils::Sq(msAngles[layer]); + } + mTransitionMSAngles[transitionId] = o2::gpu::CAMath::Sqrt(ms2); + const float& r1 = trkParam.LayerRadii[transition.fromLayer]; + const float& r2 = trkParam.LayerRadii[transition.toLayer]; + oneOverR = (0.5 * oneOverR >= 1.f / r2) ? (2.f / r2) - o2::constants::math::Almost0 : oneOverR; + const float res1 = o2::gpu::CAMath::Hypot(trkParam.PVres, mPositionResolution[transition.fromLayer]); + const float res2 = o2::gpu::CAMath::Hypot(trkParam.PVres, mPositionResolution[transition.toLayer]); + const float cosTheta1half = o2::gpu::CAMath::Sqrt(1.f - math_utils::Sq(0.5f * r1 * oneOverR)); + const float cosTheta2half = o2::gpu::CAMath::Sqrt(1.f - math_utils::Sq(0.5f * r2 * oneOverR)); + float x = (r2 * cosTheta1half) - (r1 * cosTheta2half); + float delta = o2::gpu::CAMath::Sqrt(1.f / (1.f - 0.25f * math_utils::Sq(x * oneOverR)) * (math_utils::Sq((0.25f * r1 * r2 * math_utils::Sq(oneOverR) / cosTheta2half) + cosTheta1half) * math_utils::Sq(res1) + math_utils::Sq((0.25f * r1 * r2 * math_utils::Sq(oneOverR) / cosTheta1half) + cosTheta2half) * math_utils::Sq(res2))); + /// the expression std::asin(0.5f * x * oneOverR) is equivalent to std::aCos(0.5f * r1 * oneOverR) - std::acos(0.5 * r2 * oneOverR) + mTransitionPhiCuts[transitionId] = o2::gpu::CAMath::Min(o2::gpu::CAMath::ASin(0.5f * x * oneOverR) + 2.f * mTransitionMSAngles[transitionId] + delta, o2::constants::math::PI * 0.5f); + + // some cleanup + deepVectorClear(mTracklets[transitionId]); + deepVectorClear(mTrackletLabels[transitionId]); + deepVectorClear(mTrackletsLookupTable[transitionId]); + mTrackletsLookupTable[transitionId].resize(mClusters[transition.fromLayer].size() + 1, 0); + } + + for (int cellId{0}; cellId < (int)mCells.size(); ++cellId) { + deepVectorClear(mCells[cellId]); + deepVectorClear(mCellsLookupTable[cellId]); + deepVectorClear(mCellsNeighbours[cellId]); + deepVectorClear(mCellsNeighboursTopology[cellId]); + deepVectorClear(mCellsNeighboursLUT[cellId]); + deepVectorClear(mCellLabels[cellId]); + } +} + +template +unsigned long TimeFrame::getArtefactsMemory() const +{ + unsigned long size{0}; + for (const auto& trkl : mTracklets) { + size += sizeof(Tracklet) * trkl.size(); + } + for (const auto& cells : mCells) { + size += sizeof(CellSeed) * cells.size(); + } + for (const auto& cellsN : mCellsNeighbours) { + size += sizeof(int) * cellsN.size(); + } + for (const auto& cellsN : mCellsNeighboursTopology) { + size += sizeof(int) * cellsN.size(); + } + return size; +} + +template +void TimeFrame::printArtefactsMemory() const +{ + LOGP(info, "TimeFrame: Artefacts occupy {:.2f} MB", getArtefactsMemory() / constants::MB); +} + +template +void TimeFrame::computeTrackletsPerROFScans() +{ + for (ushort iLayer = 0; iLayer < 2; ++iLayer) { + for (unsigned int iRof{0}; iRof < getNrof(1); ++iRof) { + if (mROFMaskView.isROFEnabled(1, iRof)) { + mTotalTracklets[iLayer] += mNTrackletsPerROF[iLayer][iRof]; + } + } + std::exclusive_scan(mNTrackletsPerROF[iLayer].begin(), mNTrackletsPerROF[iLayer].end(), mNTrackletsPerROF[iLayer].begin(), 0); + std::exclusive_scan(mNTrackletsPerCluster[iLayer].begin(), mNTrackletsPerCluster[iLayer].end(), mNTrackletsPerClusterSum[iLayer].begin(), 0); + } +} + +template +void TimeFrame::setMemoryPool(std::shared_ptr pool) +{ + mMemoryPool = pool; + + auto initVector = [&](bounded_vector& vec, bool useExternal = false) { + std::pmr::memory_resource* mr = (useExternal) ? mExtMemoryPool.get() : mMemoryPool.get(); + deepVectorClear(vec, mr); + }; + + auto initContainers = [&](Container& container, bool useExternal = false) { + for (auto& v : container) { + initVector(v, useExternal); + } + }; + + // these will only reside on the host for the cpu part + initContainers(mClusterExternalIndices); + initContainers(mNTrackletsPerCluster); + initContainers(mNTrackletsPerClusterSum); + initContainers(mNClustersPerROF); + initVector(mPrimaryVertices); + initVector(mTransitionPhiCuts); + initVector(mTransitionMSAngles); + initVector(mPositionResolution); + initContainers(mClusterSize); + initVector(mPValphaX); + initVector(mBogusClusters); + initContainers(mTrackletsIndexROF); + initVector(mTracks); + initContainers(mTracklets); + initContainers(mCells); + initContainers(mCellsNeighbours); + initContainers(mCellsLookupTable); + // MC info (we don't know if we have MC) + initVector(mPrimaryVerticesLabels); + initContainers(mLinesLabels); + initContainers(mTrackletLabels); + initContainers(mCellLabels); + initVector(mTracksLabel); + // these will use possibly an externally provided allocator + initContainers(mClusters, hasFrameworkAllocator()); + initContainers(mUsedClusters, hasFrameworkAllocator()); + initContainers(mUnsortedClusters, hasFrameworkAllocator()); + initContainers(mIndexTables, hasFrameworkAllocator()); + initContainers(mTrackingFrameInfo, hasFrameworkAllocator()); + initContainers(mROFramesClusters, hasFrameworkAllocator()); +} + +template +void TimeFrame::setFrameworkAllocator(ExternalAllocator* ext) +{ + mExternalAllocator = ext; + mExtMemoryPool = std::make_shared(mExternalAllocator); +} + +template +void TimeFrame::wipe() +{ + deepVectorClear(mTracks); + deepVectorClear(mTracklets); + deepVectorClear(mCells); + deepVectorClear(mCellsNeighbours); + deepVectorClear(mCellsNeighboursTopology); + deepVectorClear(mCellsLookupTable); + deepVectorClear(mPrimaryVertices); + deepVectorClear(mTrackletsLookupTable); + deepVectorClear(mClusterExternalIndices); + deepVectorClear(mNTrackletsPerCluster); + deepVectorClear(mNTrackletsPerClusterSum); + deepVectorClear(mNClustersPerROF); + deepVectorClear(mTransitionPhiCuts); + deepVectorClear(mTransitionMSAngles); + deepVectorClear(mPositionResolution); + deepVectorClear(mClusterSize); + deepVectorClear(mPValphaX); + deepVectorClear(mBogusClusters); + deepVectorClear(mTrackletsIndexROF); + deepVectorClear(mTrackletClusters); + deepVectorClear(mLines); + // if we use the external host allocator then the assumption is that we + // don't clear the memory ourself + if (!hasFrameworkAllocator()) { + deepVectorClear(mClusters); + deepVectorClear(mUsedClusters); + deepVectorClear(mUnsortedClusters); + deepVectorClear(mIndexTables); + deepVectorClear(mTrackingFrameInfo); + deepVectorClear(mROFramesClusters); + } + // only needed to clear if we have MC info + if (hasMCinformation()) { + deepVectorClear(mLinesLabels); + deepVectorClear(mPrimaryVerticesLabels); + deepVectorClear(mTrackletLabels); + deepVectorClear(mCellLabels); + deepVectorClear(mTracksLabel); + } +} + +template class TimeFrame; +template class TimeFrame; + +} // namespace o2::itsmft::tracking diff --git a/Detectors/ITSMFT/common/tracking/src/TrackingConfigParam.cxx b/Detectors/ITSMFT/common/tracking/src/TrackingConfigParam.cxx new file mode 100644 index 0000000000000..fc90f9243a996 --- /dev/null +++ b/Detectors/ITSMFT/common/tracking/src/TrackingConfigParam.cxx @@ -0,0 +1,18 @@ +// 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 "ITSMFTTracking/TrackingConfigParam.h" + +O2ParamImpl(o2::itsmft::VertexerParamConfig); + +// force registration of templated params in the parameter database (see ClustererParam.cxx) +static auto& sTrackerParamITS = o2::itsmft::TrackerParamConfig::Instance(); +static auto& sTrackerParamMFT = o2::itsmft::TrackerParamConfig::Instance(); From 49c6052ad5934a325bd8e6b1cc3f2ecebabf8585 Mon Sep 17 00:00:00 2001 From: Maurice Coquet Date: Fri, 5 Jun 2026 16:08:04 +0200 Subject: [PATCH 2/2] Fixes --- Detectors/ITSMFT/common/CMakeLists.txt | 4 +- .../ITSMFT/common/tracking/CMakeLists.txt | 11 +- .../include/ITSMFTTracking/BoundedAllocator.h | 287 ------ .../tracking/include/ITSMFTTracking/Cell.h | 37 +- .../tracking/include/ITSMFTTracking/Cluster.h | 86 -- .../include/ITSMFTTracking/ClusterLines.h | 93 -- .../include/ITSMFTTracking/Constants.h | 31 +- .../ITSMFTTracking/ExternalAllocator.h | 86 -- .../tracking/include/ITSMFTTracking/IOUtils.h | 4 +- .../include/ITSMFTTracking/LayerMask.h | 12 +- .../include/ITSMFTTracking/MathUtils.h | 126 --- .../include/ITSMFTTracking/ROFLookupTables.h | 849 ------------------ .../include/ITSMFTTracking/TimeFrame.h | 55 +- .../ITSMFTTracking/TrackingConfigParam.h | 17 +- .../include/ITSMFTTracking/TrackingTopology.h | 8 +- .../include/ITSMFTTracking/Tracklet.h | 74 -- .../tracking/include/ITSMFTTracking/Types.h | 42 - .../ITSMFT/common/tracking/src/Cluster.cxx | 87 -- .../common/tracking/src/ClusterLines.cxx | 217 ----- .../ITSMFT/common/tracking/src/IOUtils.cxx | 14 +- .../ITSMFT/common/tracking/src/TimeFrame.cxx | 9 +- 21 files changed, 94 insertions(+), 2055 deletions(-) delete mode 100644 Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/BoundedAllocator.h delete mode 100644 Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/Cluster.h delete mode 100644 Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/ClusterLines.h delete mode 100644 Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/ExternalAllocator.h delete mode 100644 Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/MathUtils.h delete mode 100644 Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/ROFLookupTables.h delete mode 100644 Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/Tracklet.h delete mode 100644 Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/Types.h delete mode 100644 Detectors/ITSMFT/common/tracking/src/Cluster.cxx delete mode 100644 Detectors/ITSMFT/common/tracking/src/ClusterLines.cxx diff --git a/Detectors/ITSMFT/common/CMakeLists.txt b/Detectors/ITSMFT/common/CMakeLists.txt index a66278b2c09ce..5cfc80c16610a 100644 --- a/Detectors/ITSMFT/common/CMakeLists.txt +++ b/Detectors/ITSMFT/common/CMakeLists.txt @@ -10,8 +10,8 @@ # or submit itself to any jurisdiction. add_subdirectory(base) +add_subdirectory(tracking) add_subdirectory(simulation) add_subdirectory(reconstruction) add_subdirectory(workflow) -add_subdirectory(data) -add_subdirectory(tracking) +add_subdirectory(data) \ No newline at end of file diff --git a/Detectors/ITSMFT/common/tracking/CMakeLists.txt b/Detectors/ITSMFT/common/tracking/CMakeLists.txt index 1ff5203b1237a..b734a310f53e5 100644 --- a/Detectors/ITSMFT/common/tracking/CMakeLists.txt +++ b/Detectors/ITSMFT/common/tracking/CMakeLists.txt @@ -14,10 +14,9 @@ o2_add_library(ITSMFTTracking SOURCES src/IOUtils.cxx src/TrackingConfigParam.cxx src/Configuration.cxx - src/Cluster.cxx - src/ClusterLines.cxx src/TimeFrame.cxx PUBLIC_LINK_LIBRARIES + O2::ITStracking O2::GPUCommon O2::CommonConstants O2::DetectorsCommonDataFormats @@ -35,10 +34,8 @@ o2_add_library(ITSMFTTracking O2::MFTBase O2::DataFormatsITS) +# Only dictionary params (see ITSMFTTrackingLinkDef.h). TimeFrame and other +# headers pull ITStracking internals not exposed to rootcling include paths. o2_target_root_dictionary(ITSMFTTracking - HEADERS include/ITSMFTTracking/IOUtils.h - include/ITSMFTTracking/TrackingConfigParam.h - include/ITSMFTTracking/Configuration.h - include/ITSMFTTracking/IndexTableUtils.h - include/ITSMFTTracking/TimeFrame.h + HEADERS include/ITSMFTTracking/TrackingConfigParam.h LINKDEF src/ITSMFTTrackingLinkDef.h) diff --git a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/BoundedAllocator.h b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/BoundedAllocator.h deleted file mode 100644 index b28ed0803446d..0000000000000 --- a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/BoundedAllocator.h +++ /dev/null @@ -1,287 +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 BoundedAllocator.h -/// \brief -/// - -#ifndef ALICEO2_ITSMFT_TRACKING_INCLUDE_BOUNDEDALLOCATOR_H_ -#define ALICEO2_ITSMFT_TRACKING_INCLUDE_BOUNDEDALLOCATOR_H_ - -#include -#include -#include -#include -#include - -#if !defined(__HIPCC__) && !defined(__CUDACC__) -#include -#include -#include "GPUCommonLogger.h" -#endif -#include "ITSMFTTracking/ExternalAllocator.h" -#include "ITSMFTTracking/Constants.h" - -namespace o2::itsmft::tracking -{ - -// #define BOUNDED_MR_STATS -class BoundedMemoryResource final : public std::pmr::memory_resource -{ - public: - class MemoryLimitExceeded final : public std::bad_alloc - { - public: - MemoryLimitExceeded(size_t attempted, size_t used, size_t max) - { - char buf[256]; - if (attempted != 0) { - (void)snprintf(buf, sizeof(buf), "Reached set memory limit (attempted: %zu, used: %zu, max: %zu)", attempted, used, max); - } else { - (void)snprintf(buf, sizeof(buf), "New set maximum below current used (newMax: %zu, used: %zu)", max, used); - } - mMsg = buf; - } - const char* what() const noexcept final { return mMsg.c_str(); } - - private: - std::string mMsg; - }; - - BoundedMemoryResource(size_t maxBytes = std::numeric_limits::max(), - std::pmr::memory_resource* upstream = std::pmr::get_default_resource()) - : mMaxMemory(maxBytes), mUpstream(upstream) {} - - BoundedMemoryResource(ExternalAllocator* alloc, - size_t maxBytes = std::numeric_limits::max()) - : mMaxMemory(maxBytes), - mAdaptor(std::make_unique(alloc)), - mUpstream(mAdaptor.get()) {} - - void* do_allocate(size_t bytes, size_t alignment) final - { - size_t new_used{0}; - size_t current_used{mUsedMemory.load(std::memory_order_relaxed)}; - do { - new_used = current_used + bytes; - if (new_used > mMaxMemory.load(std::memory_order_relaxed)) { - mCountThrow.fetch_add(1, std::memory_order_relaxed); - throw MemoryLimitExceeded(new_used, current_used, - mMaxMemory.load(std::memory_order_relaxed)); - } - } while (!mUsedMemory.compare_exchange_weak(current_used, new_used, - std::memory_order_acq_rel, - std::memory_order_relaxed)); - - void* p{nullptr}; - try { - p = mUpstream->allocate(bytes, alignment); - } catch (...) { - mUsedMemory.fetch_sub(bytes, std::memory_order_relaxed); -#ifdef BOUNDED_MR_STATS - mStats.upstreamFailures.fetch_add(1, std::memory_order_relaxed); -#endif - throw; - } - -#ifdef BOUNDED_MR_STATS - size_t peak = mStats.peak.load(std::memory_order_relaxed); - while (new_used > peak && - !mStats.peak.compare_exchange_weak(peak, new_used, - std::memory_order_relaxed)) { - } - mStats.live.fetch_add(1, std::memory_order_relaxed); - mStats.nAlloc.fetch_add(1, std::memory_order_relaxed); - mStats.totalAlloc.fetch_add(bytes, std::memory_order_relaxed); - - size_t ma = mStats.maxAlign.load(std::memory_order_relaxed); - while (alignment > ma && !mStats.maxAlign.compare_exchange_weak(ma, alignment, std::memory_order_relaxed)) { - } -#endif - return p; - } - - void do_deallocate(void* p, size_t bytes, size_t alignment) final - { - mUpstream->deallocate(p, bytes, alignment); - mUsedMemory.fetch_sub(bytes, std::memory_order_relaxed); -#ifdef BOUNDED_MR_STATS - mStats.live.fetch_sub(1, std::memory_order_relaxed); - mStats.nFree.fetch_add(1, std::memory_order_relaxed); - mStats.totalFreed.fetch_add(bytes, std::memory_order_relaxed); -#endif - } - - bool do_is_equal(const std::pmr::memory_resource& other) const noexcept final - { - return this == &other; - } - - [[nodiscard]] size_t getUsedMemory() const noexcept - { - return mUsedMemory.load(std::memory_order_relaxed); - } - [[nodiscard]] size_t getMaxMemory() const noexcept - { - return mMaxMemory.load(std::memory_order_relaxed); - } - [[nodiscard]] size_t getThrowCount() const noexcept - { - return mCountThrow.load(std::memory_order_relaxed); - } - - void setMaxMemory(size_t max) - { - size_t current = mMaxMemory.load(std::memory_order_relaxed); - if (max == current) { - return; - } - for (;;) { - size_t used = mUsedMemory.load(std::memory_order_acquire); - if (used > max) { - mCountThrow.fetch_add(1, std::memory_order_relaxed); - throw MemoryLimitExceeded(0, used, max); - } - if (mMaxMemory.compare_exchange_weak(current, max, - std::memory_order_release, - std::memory_order_relaxed)) { - return; - } - if (current == max) { - return; - } - } - } - -#if !defined(__HIPCC__) && !defined(__CUDACC__) - std::string asString() const - { - const auto throw_ = mCountThrow.load(std::memory_order_relaxed); - const auto used = static_cast(mUsedMemory.load(std::memory_order_relaxed)); - const auto maxm = mMaxMemory.load(std::memory_order_relaxed); - std::string ret; - if (maxm == std::numeric_limits::max()) { - ret += std::format("maxthrow={} maxmem=unbounded used={:.2f} GB", throw_, used / constants::GB); - } else { - ret += std::format("maxthrow={} maxmem={:.2f} GB used={:.2f} GB ({:.2f}%)", throw_, (double)maxm / constants::GB, used / constants::GB, 100.0 * used / (double)maxm); - } -#ifdef BOUNDED_MR_STATS - ret += std::format(" peak={:.2f} GB live={} nAlloc={} nFree={} totalAlloc={:.2f} GB totalFreed={:.2f} GB maxAlign={} upstreamFail={}", - (float)mStats.peak.load(std::memory_order_relaxed) / constants::GB, - mStats.live.load(std::memory_order_relaxed), - mStats.nAlloc.load(std::memory_order_relaxed), - mStats.nFree.load(std::memory_order_relaxed), - (float)mStats.totalAlloc.load(std::memory_order_relaxed) / constants::GB, - (float)mStats.totalFreed.load(std::memory_order_relaxed) / constants::GB, - mStats.maxAlign.load(std::memory_order_relaxed), - mStats.upstreamFailures.load(std::memory_order_relaxed)); -#endif - return ret; - } - - void print() const - { - LOGP(info, "{}", asString()); - } -#endif - - private: - std::atomic mMaxMemory{std::numeric_limits::max()}; - std::atomic mCountThrow{0}; - std::atomic mUsedMemory{0}; - std::unique_ptr mAdaptor{nullptr}; - std::pmr::memory_resource* mUpstream{nullptr}; - -#ifdef BOUNDED_MR_STATS - struct Stats { - std::atomic peak{0}; - std::atomic live{0}; - std::atomic nAlloc{0}; - std::atomic nFree{0}; - std::atomic totalAlloc{0}; - std::atomic totalFreed{0}; - std::atomic maxAlign{0}; - std::atomic upstreamFailures{0}; - }; - Stats mStats{}; -#endif -}; - -template -using bounded_vector = std::pmr::vector; - -template -inline void deepVectorClear(std::vector& vec) -{ - std::vector().swap(vec); -} - -template -inline void deepVectorClear(bounded_vector& vec, std::pmr::memory_resource* mr = nullptr) -{ - std::pmr::memory_resource* tmr = (mr != nullptr) ? mr : vec.get_allocator().resource(); - vec.~bounded_vector(); - new (&vec) bounded_vector(std::pmr::polymorphic_allocator{tmr}); -} - -template -inline void deepVectorClear(std::vector>& vec, std::pmr::memory_resource* mr = nullptr) -{ - for (auto& v : vec) { - deepVectorClear(v, mr); - } -} - -template -inline void deepVectorClear(std::array, S>& arr, std::pmr::memory_resource* mr = nullptr) -{ - for (size_t i{0}; i < S; ++i) { - deepVectorClear(arr[i], mr); - } -} - -template -inline void clearResizeBoundedVector(bounded_vector& vec, size_t sz, std::pmr::memory_resource* mr = nullptr, T def = T()) -{ - std::pmr::memory_resource* tmr = (mr != nullptr) ? mr : vec.get_allocator().resource(); - vec.~bounded_vector(); - new (&vec) bounded_vector(sz, def, std::pmr::polymorphic_allocator{tmr}); -} - -template -inline void clearResizeBoundedVector(std::vector>& vec, size_t size, std::pmr::memory_resource* mr) -{ - vec.clear(); - vec.reserve(size); - for (size_t i = 0; i < size; ++i) { - vec.emplace_back(std::pmr::polymorphic_allocator>{mr}); - } -} - -template -inline void clearResizeBoundedArray(std::array, S>& arr, size_t size, std::pmr::memory_resource* mr = nullptr, T def = T()) -{ - for (size_t i{0}; i < S; ++i) { - clearResizeBoundedVector(arr[i], size, mr, def); - } -} - -template -inline std::vector toSTDVector(const bounded_vector& b) -{ - std::vector t(b.size()); - std::copy(b.cbegin(), b.cend(), t.begin()); - return t; -} - -} // namespace o2::itsmft::tracking - -#endif diff --git a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/Cell.h b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/Cell.h index ab5faa6edfd4f..b32ae812319ac 100644 --- a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/Cell.h +++ b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/Cell.h @@ -10,17 +10,18 @@ // or submit itself to any jurisdiction. /// /// \file Cell.h -/// \brief +/// \brief CA cell/track seed types with hole-layer support (ITS PR #15390) /// #ifndef ALICEO2_ITSMFT_TRACKING_INCLUDE_CACELL_H_ #define ALICEO2_ITSMFT_TRACKING_INCLUDE_CACELL_H_ +#include #include -#include "ITSMFTTracking/Constants.h" +#include "ITStracking/Constants.h" #include "ITSMFTTracking/LayerMask.h" -#include "ITSMFTTracking/Types.h" +#include "DataFormatsITS/TimeEstBC.h" #include "ReconstructionDataFormats/Track.h" #include "GPUCommonDef.h" @@ -61,7 +62,7 @@ class SeedBase : public o2::track::TrackParCovF GPUhdDefault() SeedBase(SeedBase&&) = default; GPUhdDefault() SeedBase& operator=(const SeedBase&) = default; GPUhdDefault() SeedBase& operator=(SeedBase&&) = default; - GPUhd() SeedBase(const o2::track::TrackParCovF& tpc, float chi2, int level, const TimeEstBC& time) + GPUhd() SeedBase(const o2::track::TrackParCovF& tpc, float chi2, int level, const o2::its::TimeEstBC& time) : o2::track::TrackParCovF(tpc), mChi2(chi2), mLevel(level), mTime(time) { } @@ -69,25 +70,24 @@ class SeedBase : public o2::track::TrackParCovF GPUhd() const auto& clustersRaw() const { return mClusters; } private: - float mChi2{constants::UnsetValue}; - int mLevel{constants::UnusedIndex}; - std::array mTracklets = constants::helpers::initArray(); - std::array mClusters = constants::helpers::initArray(); - TimeEstBC mTime; + float mChi2{o2::its::constants::UnsetValue}; + int mLevel{o2::its::constants::UnusedIndex}; + std::array mTracklets = o2::its::constants::helpers::initArray(); + std::array mClusters = o2::its::constants::helpers::initArray(); + o2::its::TimeEstBC mTime; }; -/// CellSeed: connections of three clusters -class CellSeed final : public SeedBase +class CellSeed final : public SeedBase { - using Base = SeedBase; + using Base = SeedBase; public: GPUhdDefault() CellSeed() = default; - GPUhd() CellSeed(int innerL, int cl0, int cl1, int cl2, int trkl0, int trkl1, const o2::track::TrackParCovF& tpc, float chi2, const TimeEstBC& time) + GPUhd() CellSeed(int innerL, int cl0, int cl1, int cl2, int trkl0, int trkl1, const o2::track::TrackParCovF& tpc, float chi2, const o2::its::TimeEstBC& time) : CellSeed(LayerMask(innerL, innerL + 1, innerL + 2), cl0, cl1, cl2, trkl0, trkl1, tpc, chi2, time) { } - GPUhd() CellSeed(LayerMask hitLayerMask, int cl0, int cl1, int cl2, int trkl0, int trkl1, const o2::track::TrackParCovF& tpc, float chi2, const TimeEstBC& time) + GPUhd() CellSeed(LayerMask hitLayerMask, int cl0, int cl1, int cl2, int trkl0, int trkl1, const o2::track::TrackParCovF& tpc, float chi2, const o2::its::TimeEstBC& time) : Base(tpc, chi2, 1, time) { setHitLayerMask(hitLayerMask); @@ -109,18 +109,13 @@ class CellSeed final : public SeedBase GPUhd() int getThirdClusterIndex() const { return this->clustersRaw()[2]; }; GPUhd() auto& getClusters() { return this->clustersRaw(); } GPUhd() const auto& getClusters() const { return this->clustersRaw(); } - /// getCluster takes an ABSOLUTE layer index. Compact cluster slots are - /// mapped to absolute layers by set-bit order in the hit-layer mask. GPUhd() int getCluster(int layer) const { const int slot = getHitLayerMask().slot(layer); - return (slot >= 0 && slot < constants::ClustersPerCell) ? this->clustersRaw()[slot] : constants::UnusedIndex; + return (slot >= 0 && slot < o2::its::constants::ClustersPerCell) ? this->clustersRaw()[slot] : o2::its::constants::UnusedIndex; } }; -/// TrackSeed: full-width working representation used during road finding. -/// processNeighbours extends the cluster list inward, so we need NLayers -/// absolute-indexed slots here. template class TrackSeed final : public SeedBase { @@ -168,7 +163,7 @@ class TrackSeed final : public SeedBase } } } - return constants::UnusedIndex; + return o2::its::constants::UnusedIndex; } }; diff --git a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/Cluster.h b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/Cluster.h deleted file mode 100644 index b4b6a74ee9eea..0000000000000 --- a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/Cluster.h +++ /dev/null @@ -1,86 +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 Cluster.h -/// \brief -/// - -#ifndef ALICEO2_ITSMFT_TRACKING_INCLUDE_CACLUSTER_H_ -#define ALICEO2_ITSMFT_TRACKING_INCLUDE_CACLUSTER_H_ - -#include -#include "ITSMFTTracking/Constants.h" -#include "GPUCommonRtypes.h" -#include "GPUCommonDef.h" - -namespace o2::itsmft -{ -template -class IndexTableUtils; -} // namespace o2::itsmft - -namespace o2::itsmft::tracking -{ - -struct Cluster final { - GPUhdDefault() Cluster() = default; - GPUhd() Cluster(const float x, const float y, const float z, const int idx); - template - GPUhd() Cluster(const int, const IndexTableUtils& utils, const Cluster&); - template - GPUhd() Cluster(const int, const float3&, const IndexTableUtils& utils, const Cluster&); - GPUhdDefault() Cluster(const Cluster&) = default; - GPUhdDefault() Cluster(Cluster&&) noexcept = default; - GPUhdDefault() ~Cluster() = default; - - GPUhdDefault() Cluster& operator=(const Cluster&) = default; - GPUhdDefault() Cluster& operator=(Cluster&&) noexcept = default; - GPUhdDefault() bool operator==(const Cluster&) const = default; - - GPUhd() void print() const; - - float xCoordinate{-999.f}; - float yCoordinate{-999.f}; - float zCoordinate{-999.f}; - float phi{-999.f}; - float radius{-999.f}; - int clusterId{constants::UnusedIndex}; - int indexTableBinIndex{constants::UnusedIndex}; - - ClassDefNV(Cluster, 1); -}; - -struct TrackingFrameInfo final { - GPUhdDefault() TrackingFrameInfo() = default; - GPUhd() TrackingFrameInfo(float x, float y, float z, float xTF, float alpha, std::array&& posTF, std::array&& covTF); - GPUhdDefault() TrackingFrameInfo(const TrackingFrameInfo&) = default; - GPUhdDefault() TrackingFrameInfo(TrackingFrameInfo&&) noexcept = default; - GPUhdDefault() ~TrackingFrameInfo() = default; - - GPUhdDefault() TrackingFrameInfo& operator=(const TrackingFrameInfo&) = default; - GPUhdDefault() TrackingFrameInfo& operator=(TrackingFrameInfo&&) = default; - - GPUhd() void print() const; - - float xCoordinate{-999.f}; - float yCoordinate{-999.f}; - float zCoordinate{-999.f}; - float xTrackingFrame{-999.f}; - float alphaTrackingFrame{-999.f}; - std::array positionTrackingFrame = {-999.f, -999.f}; - std::array covarianceTrackingFrame = {-999.f, -999.f, -999.f}; - - ClassDefNV(TrackingFrameInfo, 1); -}; - -} // namespace o2::itsmft::tracking - -#endif /* ALICEO2_ITSMFT_TRACKING_INCLUDE_CACLUSTER_H_ */ diff --git a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/ClusterLines.h b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/ClusterLines.h deleted file mode 100644 index 330f384a503b3..0000000000000 --- a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/ClusterLines.h +++ /dev/null @@ -1,93 +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. - -#ifndef O2_ITSMFT_CLUSTERLINES_H -#define O2_ITSMFT_CLUSTERLINES_H - -#include -#include -#include -#include -#include -#include "ITSMFTTracking/Cluster.h" -#include "ITSMFTTracking/Constants.h" -#include "ITSMFTTracking/Tracklet.h" -#include "GPUCommonRtypes.h" - -namespace o2::itsmft::tracking -{ - -struct Line final { -#if !defined(__HIPCC__) && !defined(__CUDACC__) // hide the class completely for gpu-cc - using SVector3f = ROOT::Math::SVector; - using SMatrix3f = ROOT::Math::SMatrix>; - - Line() = default; - Line(const Tracklet&, const Cluster*, const Cluster*); - bool operator==(const Line&) const = default; - - static float getDistance2FromPoint(const Line& line, const std::array& point); - static float getDistanceFromPoint(const Line& line, const std::array& point); - static SMatrix3f getDCAComponents(const Line& line, const std::array& point); - static float getDCA2(const Line&, const Line&, const float precision = constants::Tolerance); - static float getDCA(const Line&, const Line&, const float precision = constants::Tolerance); - bool isEmpty() const noexcept; - void print() const; - - SVector3f originPoint; - SVector3f cosinesDirector; - TimeEstBC mTime; - - ClassDefNV(Line, 1); -#endif -}; - -class ClusterLines final -{ -#if !defined(__HIPCC__) && !defined(__CUDACC__) // hide the class completely for gpu-cc - using SMatrix3 = ROOT::Math::SMatrix>; - using SMatrix3f = ROOT::Math::SMatrix>; - using SVector3 = ROOT::Math::SVector; - - public: - ClusterLines() = default; - ClusterLines(const int firstLabel, const Line& firstLine, const int secondLabel, const Line& secondLine); - ClusterLines(gsl::span lineLabels, gsl::span lines); - void add(const int lineLabel, const Line& line); - void computeClusterCentroid(); - void accumulate(const Line& line); - bool isValid() const noexcept { return mIsValid; } - auto const& getVertex() const { return mVertex; } - const float* getRMS2() const { return mRMS2.Array(); } - float getAvgDistance2() const { return mAvgDistance2; } - auto getSize() const noexcept { return mLabels.size(); } - auto& getLabels() const noexcept { return mLabels; } - const auto& getTimeStamp() const noexcept { return mTime; } - bool operator==(const ClusterLines& rhs) const noexcept; - float getR2() const noexcept { return (mVertex[0] * mVertex[0]) + (mVertex[1] * mVertex[1]); } - float getR() const noexcept { return std::sqrt(getR2()); } - - protected: - SMatrix3 mAMatrix; // AX=B, symmetric normal matrix - SVector3 mBMatrix; // AX=B, right-hand side - std::array mVertex = {}; // cluster centroid position - SMatrix3f mRMS2; // symmetric matrix: diagonal is RMS2 - float mAvgDistance2 = 0.f; // substitute for chi2 - bool mIsValid = false; // true if linear system was solved successfully - TimeEstBC mTime; // time stamp - std::vector mLabels; // contributing labels - - ClassDefNV(ClusterLines, 1); -#endif -}; - -} // namespace o2::itsmft::tracking -#endif /* O2_ITSMFT_CLUSTERLINES_H */ diff --git a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/Constants.h b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/Constants.h index 10bc7b6e5ab09..db24cd81c26d9 100644 --- a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/Constants.h +++ b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/Constants.h @@ -10,15 +10,12 @@ // or submit itself to any jurisdiction. /// /// \file Constants.h -/// \brief +/// \brief Detector-specific layer counts for shared ITSMFT CA tracking /// #ifndef ALICEO2_ITSMFT_TRACKING_INCLUDE_CONSTANTS_H_ #define ALICEO2_ITSMFT_TRACKING_INCLUDE_CONSTANTS_H_ -#include -#include - #include "DetectorsCommonDataFormats/DetID.h" namespace o2::itsmft::tracking::constants @@ -26,37 +23,13 @@ namespace o2::itsmft::tracking::constants constexpr int ITSNLayers = 7; constexpr int MFTNLayers = 10; +constexpr int MaxIter = 4; // same as o2::its::constants::MaxIter constexpr int nLayersForDet(o2::detectors::DetID::ID detId) { return detId == o2::detectors::DetID::MFT ? MFTNLayers : ITSNLayers; } -constexpr float KB = 1024.f; -constexpr float MB = KB * KB; -constexpr float GB = MB * KB; -constexpr bool DoTimeBenchmarks = true; -constexpr bool SaveTimeBenchmarks = false; -constexpr float Tolerance = 1e-12; // numerical tolerance -constexpr int ClustersPerCell = 3; // number of clusters for a cell -constexpr int UnusedIndex = -1; // global unused flag -constexpr float UnsetValue = -999.f; // global unset value -constexpr float Radl = 9.36f; // Radiation length of Si [cm] -constexpr float Rho = 2.33f; // Density of Si [g/cm^3] -constexpr int MaxIter = 4; // Max. supported iterations -constexpr int MaxSelectedTrackletsPerCluster = 100; // vertexer: max lines per cluster - -namespace helpers -{ - -// initialize a std::array at compile time fully with T -template -constexpr std::array initArray() -{ - return [](std::index_sequence) { return std::array{(static_cast(Is), Value)...}; }(std::make_index_sequence{}); -} - -} // namespace helpers } // namespace o2::itsmft::tracking::constants #endif /* ALICEO2_ITSMFT_TRACKING_INCLUDE_CONSTANTS_H_ */ diff --git a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/ExternalAllocator.h b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/ExternalAllocator.h deleted file mode 100644 index 42f0304dbd08e..0000000000000 --- a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/ExternalAllocator.h +++ /dev/null @@ -1,86 +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 ExternalAllocator.h -/// \brief -/// - -#ifndef ALICEO2_ITSMFT_TRACKING_INCLUDE_EXTERNALALLOCATOR_H_ -#define ALICEO2_ITSMFT_TRACKING_INCLUDE_EXTERNALALLOCATOR_H_ - -#include -#include "GPUO2ExternalUser.h" -#include "Base/GPUMemoryResource.h" - -namespace o2::itsmft::tracking -{ - -class ExternalAllocator -{ - using Type = std::underlying_type_t; - - public: - virtual void deallocate(char*, size_t) = 0; - virtual void* allocate(size_t) = 0; - void* allocate(size_t s, Type type) - { - auto old = mType; - mType = type; - void* p = allocate(s); - mType = old; - return p; - } - void* allocateStack(size_t s) - { - return allocate(s, (o2::gpu::GPUMemoryResource::MEMORY_GPU | o2::gpu::GPUMemoryResource::MEMORY_STACK)); - } - virtual void pushTagOnStack(uint64_t) = 0; - virtual void popTagOffStack(uint64_t) = 0; - - void setType(Type t) noexcept { mType = t; } - Type getType() const noexcept { return mType; } - - protected: - Type mType; -}; - -class ExternalAllocatorAdaptor final : public std::pmr::memory_resource -{ - public: - explicit ExternalAllocatorAdaptor(ExternalAllocator* alloc) : mAlloc(alloc) {} - - protected: - void* do_allocate(size_t bytes, size_t alignment) override - { - void* p = mAlloc->allocate(bytes, o2::gpu::GPUMemoryResource::MemoryType::MEMORY_HOST); - if (!p) { - throw std::bad_alloc(); - } - return p; - } - - void do_deallocate(void* p, size_t bytes, size_t) override - { - mAlloc->deallocate(static_cast(p), bytes); - } - - bool do_is_equal(const std::pmr::memory_resource& other) const noexcept override - { - return this == &other; - } - - private: - ExternalAllocator* mAlloc; -}; - -} // namespace o2::itsmft::tracking - -#endif diff --git a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/IOUtils.h b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/IOUtils.h index cc49bdcf35e20..d38426df13cac 100644 --- a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/IOUtils.h +++ b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/IOUtils.h @@ -29,7 +29,7 @@ #include "DataFormatsITSMFT/TopologyDictionary.h" #include "MathUtils/Cartesian.h" -namespace o2::itsmft::tracking +namespace o2::its { struct TrackingFrameInfo; } @@ -67,7 +67,7 @@ void loadClusterTrackingFrameInfo(const CompClusterExt& c, const TopologyDictionary* dict, int& layer, unsigned int& clusterSize, - tracking::TrackingFrameInfo& tfInfo, + o2::its::TrackingFrameInfo& tfInfo, bool applySysErrors = true); /// Convert compact clusters to 3D spacepoints. diff --git a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/LayerMask.h b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/LayerMask.h index 86a2528410f05..e9afc26a916bd 100644 --- a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/LayerMask.h +++ b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/LayerMask.h @@ -1,4 +1,4 @@ -// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// 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. // @@ -22,7 +22,7 @@ #include "GPUCommonDef.h" #include "GPUCommonMath.h" -#include "ITSMFTTracking/Constants.h" +#include "ITStracking/Constants.h" namespace o2::itsmft::tracking { @@ -66,8 +66,8 @@ struct LayerMask { } GPUhdi() int length() const noexcept { return empty() ? 0 : last() - first() + 1; } GPUhdi() int count() const noexcept { return static_cast(o2::gpu::GPUCommonMath::Popcount(mBits)); } - GPUhdi() int first() const noexcept { return mBits ? static_cast(o2::gpu::GPUCommonMath::Ctz(mBits)) : constants::UnusedIndex; } - GPUhdi() int last() const noexcept { return mBits ? 31 - static_cast(o2::gpu::GPUCommonMath::Clz(mBits)) : constants::UnusedIndex; } + GPUhdi() int first() const noexcept { return mBits ? static_cast(o2::gpu::GPUCommonMath::Ctz(mBits)) : o2::its::constants::UnusedIndex; } + GPUhdi() int last() const noexcept { return mBits ? 31 - static_cast(o2::gpu::GPUCommonMath::Clz(mBits)) : o2::its::constants::UnusedIndex; } GPUhdi() LayerMask holeMask() const noexcept { return empty() ? LayerMask{0} : (span(first(), last()) & ~(*this)); @@ -76,7 +76,7 @@ struct LayerMask { GPUhdi() int slot(int layer) const noexcept { if (!has(layer)) { - return constants::UnusedIndex; + return o2::its::constants::UnusedIndex; } const uint32_t lowerLayers = (uint32_t(1) << layer) - 1; return static_cast(o2::gpu::GPUCommonMath::Popcount(static_cast(mBits) & lowerLayers)); @@ -112,4 +112,4 @@ static_assert(alignof(LayerMask) == alignof(uint16_t)); } // namespace o2::itsmft::tracking -#endif +#endif /* ALICEO2_ITSMFT_TRACKING_INCLUDE_LAYERMASK_H_ */ diff --git a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/MathUtils.h b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/MathUtils.h deleted file mode 100644 index 2ab2df14d0489..0000000000000 --- a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/MathUtils.h +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright 2019-2026 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -/// -/// \file MathUtils.h -/// \brief -/// - -#ifndef O2_ITSMFT_TRACKING_MATHUTILS_H_ -#define O2_ITSMFT_TRACKING_MATHUTILS_H_ - -#include "CommonConstants/MathConstants.h" -#include "ITSMFTTracking/Constants.h" -#include "MathUtils/Utils.h" -#include "GPUCommonMath.h" -#include "GPUCommonDef.h" - -namespace o2::itsmft::tracking::math_utils -{ - -GPUhdi() float computePhi(float x, float y) -{ - return o2::math_utils::fastATan2(-y, -x) + o2::constants::math::PI; -} - -GPUhdi() float hypot(float x, float y) -{ - return o2::gpu::CAMath::Hypot(x, y); -} - -GPUhdi() float getNormalizedPhi(float phi) -{ - phi -= o2::constants::math::TwoPI * o2::gpu::CAMath::Floor(phi * (1.f / o2::constants::math::TwoPI)); - return phi; -} - -GPUhdi() float computeCurvature(float x1, float y1, float x2, float y2, float x3, float y3) -{ - // in case the triangle is degenerate we return infinite curvature. - const float area = ((x2 - x1) * (y3 - y1)) - ((x3 - x1) * (y2 - y1)); - if (o2::gpu::CAMath::Abs(area) < constants::Tolerance) { - return o2::constants::math::Almost0; - } - const float dx1 = x2 - x1, dy1 = y2 - y1; - const float dx2 = x3 - x2, dy2 = y3 - y2; - const float dx3 = x1 - x3, dy3 = y1 - y3; - const float d1 = o2::gpu::CAMath::Sqrt((dx1 * dx1) + (dy1 * dy1)); - const float d2 = o2::gpu::CAMath::Sqrt((dx2 * dx2) + (dy2 * dy2)); - const float d3 = o2::gpu::CAMath::Sqrt((dx3 * dx3) + (dy3 * dy3)); - return -2.f * area / (d1 * d2 * d3); -} - -GPUhdi() float computeCurvatureCentreX(float x1, float y1, float x2, float y2, float x3, float y3) -{ - // in case the triangle is degenerate we return set the centre to infinity. - float dx21 = x2 - x1, dx32 = x3 - x2; - if (o2::gpu::CAMath::Abs(dx21) < o2::itsmft::tracking::constants::Tolerance || - o2::gpu::CAMath::Abs(dx32) < o2::itsmft::tracking::constants::Tolerance) { // add small offset - x2 += 1e-4; - dx21 = x2 - x1; - dx32 = x3 - x2; - } - const float k1 = (y2 - y1) / dx21, k2 = (y3 - y2) / dx32; - if (o2::gpu::CAMath::Abs(k2 - k1) < o2::itsmft::tracking::constants::Tolerance) { - return o2::constants::math::VeryBig; - } - return 0.5f * (k1 * k2 * (y1 - y3) + k2 * (x1 + x2) - k1 * (x2 + x3)) / (k2 - k1); -} - -GPUhdi() float computeTanDipAngle(float x1, float y1, float x2, float y2, float z1, float z2) -{ - // in case the points vertically align we go to pos/neg infinity. - const float d = o2::gpu::CAMath::Hypot(x1 - x2, y1 - y2); - if (o2::gpu::CAMath::Abs(d) < o2::itsmft::tracking::constants::Tolerance) { - return ((z1 > z2) ? -1.f : 1.f) * o2::constants::math::VeryBig; - } - return (z1 - z2) / d; -} - -GPUhdi() float smallestAngleDifference(float a, float b) -{ - return o2::gpu::CAMath::Remainderf(b - a, o2::constants::math::TwoPI); -} - -GPUhdi() bool isPhiDifferenceBelow(const float phiA, const float phiB, const float phiCut) -{ - const float deltaPhi = o2::gpu::CAMath::Abs(phiA - phiB); - return deltaPhi < phiCut || deltaPhi > o2::constants::math::TwoPI - phiCut; -} - -GPUhdi() constexpr float Sq(float v) -{ - return v * v; -} - -GPUhdi() constexpr float SqSum(float v, float w) -{ - return Sq(v) + Sq(w); -} - -GPUhdi() constexpr float SqSum(float u, float v, float w) -{ - return Sq(u) + SqSum(v, w); -} - -GPUhdi() constexpr float SqDiff(float x, float y) -{ - return Sq(x - y); -} - -GPUhdi() float MSangle(float mass, float p, float xX0) -{ - float beta = p / o2::gpu::CAMath::Hypot(mass, p); - return 0.0136f * o2::gpu::CAMath::Sqrt(xX0) * (1.f + 0.038f * o2::gpu::CAMath::Log(xX0)) / (beta * p); -} - -} // namespace o2::itsmft::tracking::math_utils - -#endif diff --git a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/ROFLookupTables.h b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/ROFLookupTables.h deleted file mode 100644 index 05fecb88d675a..0000000000000 --- a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/ROFLookupTables.h +++ /dev/null @@ -1,849 +0,0 @@ -// Copyright 2019-2026 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#ifndef ALICEO2_ITSMFT_TRACKING_INCLUDE_ROFOVERLAPTABLE_H_ -#define ALICEO2_ITSMFT_TRACKING_INCLUDE_ROFOVERLAPTABLE_H_ - -#include -#include -#include -#include -#include -#include - -#ifndef GPUCA_GPUCODE -#include -#include "Framework/Logger.h" -#endif - -#include "CommonConstants/LHCConstants.h" -#include "CommonDataFormat/RangeReference.h" -#include "ITSMFTTracking/Types.h" -#include "GPUCommonMath.h" -#include "GPUCommonDef.h" - -namespace o2::itsmft::tracking -{ - -// Layer timing definition -struct LayerTiming { - using BCType = TimeStampType; - BCType mNROFsTF{0}; // number of ROFs per timeframe - BCType mROFLength{0}; // ROF length in BC - BCType mROFDelay{0}; // delay of ROFs wrt start of first orbit in TF in BC - BCType mROFBias{0}; // bias wrt to the LHC clock in BC - BCType mROFAddTimeErr{0}; // additionally imposed uncertainty on ROF time in BC - - // return start of ROF in BC - // this does not account for the opt. error! - GPUhdi() BCType getROFStartInBC(BCType rofId) const noexcept - { - assert(rofId < mNROFsTF && rofId >= 0); - return (mROFLength * rofId) + mROFDelay + mROFBias; - } - - // return end of ROF in BCs - // this does not account for the opt. error! - GPUhdi() BCType getROFEndInBC(BCType rofId) const noexcept - { - assert(rofId < mNROFsTF); - return getROFStartInBC(rofId) + mROFLength; - } - - // return (clamped) time-interval of rof - GPUhdi() TimeEstBC getROFTimeBounds(BCType rofId, bool withError = false) const noexcept - { - if (withError) { - int64_t start = getROFStartInBC(rofId); - int64_t end = getROFEndInBC(rofId); - start = o2::gpu::CAMath::Max(start - mROFAddTimeErr, int64_t(0)); - end += mROFAddTimeErr; - return {static_cast(start), static_cast(end - start)}; - } - return {getROFStartInBC(rofId), static_cast(mROFLength)}; - } - - // return which ROF this BC belongs to - GPUhi() BCType getROF(BCType bc) const noexcept - { - const BCType offset = mROFDelay + mROFBias; - if (bc <= offset) { - return 0; - } - return (bc - offset) / mROFLength; - } - - // return which ROF this timestamp belongs by its lower edge - GPUhi() BCType getROF(TimeStamp ts) const noexcept - { - const BCType offset = mROFDelay + mROFBias; - const BCType bc = (ts.getTimeStamp() < ts.getTimeStampError()) ? BCType(0) : static_cast(o2::gpu::CAMath::Floor(ts.getTimeStamp() - ts.getTimeStampError())); - if (bc <= offset) { - return 0; - } - return (bc - offset) / mROFLength; - } - -#ifndef GPUCA_GPUCODE - GPUh() std::string asString() const - { - return std::format("NROFsPerTF {:4} ROFLength {:4} ({:4} per Orbit) ROFDelay {:4} ROFBias {:4} ROFAddTimeErr {:4}", mNROFsTF, mROFLength, (o2::constants::lhc::LHCMaxBunches / mROFLength), mROFDelay, mROFBias, mROFAddTimeErr); - } - - GPUh() void print() const - { - LOG(info) << asString(); - } -#endif -}; - -// Base class for lookup to define layers -template -class LayerTimingBase -{ - protected: - LayerTiming mLayers[NLayers]; - - public: - using T = LayerTiming::BCType; - LayerTimingBase() = default; - - GPUh() void defineLayer(int32_t layer, T nROFsTF, T rofLength, T rofDelay, T rofBias, T rofTE) - { - assert(layer >= 0 && layer < NLayers); - mLayers[layer] = {nROFsTF, rofLength, rofDelay, rofBias, rofTE}; - } - - GPUh() void defineLayer(int32_t layer, const LayerTiming& timing) - { - assert(layer >= 0 && layer < NLayers); - mLayers[layer] = timing; - } - - GPUhdi() const LayerTiming& getLayer(int32_t layer) const - { - assert(layer >= 0 && layer < NLayers); - return mLayers[layer]; - } - - GPUhdi() constexpr int32_t getEntries() noexcept { return NLayers; } - -#ifndef GPUCA_GPUCODE - GPUh() void print() const - { - LOGP(info, "Imposed time structure:"); - for (int32_t iL{0}; iL < NLayers; ++iL) { - LOGP(info, "\tLayer:{} {}", iL, mLayers[iL].asString()); - } - } -#endif -}; - -// GPU friendly view of the table below -template -struct ROFOverlapTableView { - const TableEntry* mFlatTable{nullptr}; - const TableIndex* mIndices{nullptr}; - const LayerTiming* mLayers{nullptr}; - - GPUhdi() const LayerTiming& getLayer(int32_t layer) const noexcept - { - assert(layer >= 0 && layer < NLayers); - return mLayers[layer]; - } - - GPUh() int32_t getClock() const noexcept - { - // we take the fastest layer as clock - int32_t fastest = 0; - uint32_t maxNROFs{0}; - for (int32_t iL{0}; iL < NLayers; ++iL) { - const auto& layer = getLayer(iL); - // by definition the fastest layer has the most ROFs - // this also solves the problem of a delay large than ROFLength - // if mNROFsTF is correct - if (layer.mNROFsTF > maxNROFs) { - fastest = iL; - maxNROFs = layer.mNROFsTF; - } - } - return fastest; - } - - GPUh() const LayerTiming& getClockLayer() const noexcept - { - return mLayers[getClock()]; - } - - GPUhdi() const TableEntry& getOverlap(int32_t from, int32_t to, size_t rofIdx) const noexcept - { - assert(from < NLayers && to < NLayers); - const size_t linearIdx = (from * NLayers) + to; - const auto& idx = mIndices[linearIdx]; - assert(rofIdx < idx.getEntries()); - return mFlatTable[idx.getFirstEntry() + rofIdx]; - } - - GPUhdi() bool doROFsOverlap(int32_t layer0, size_t rof0, int32_t layer1, size_t rof1) const noexcept - { - if (layer0 == layer1) { // layer is compatible with itself - return rof0 == rof1; - } - - assert(layer0 < NLayers && layer1 < NLayers); - const size_t linearIdx = (layer0 * NLayers) + layer1; - const auto& idx = mIndices[linearIdx]; - - if (rof0 >= idx.getEntries()) { - return false; - } - - const auto& overlap = mFlatTable[idx.getFirstEntry() + rof0]; - - if (overlap.getEntries() == 0) { - return false; - } - - const size_t firstCompatible = overlap.getFirstEntry(); - const size_t lastCompatible = firstCompatible + overlap.getEntries() - 1; - return rof1 >= firstCompatible && rof1 <= lastCompatible; - } - - GPUhdi() TimeEstBC getTimeStamp(int32_t layer0, size_t rof0, int32_t layer1, size_t rof1) const noexcept - { - assert(layer0 < NLayers && layer1 < NLayers); - assert(doROFsOverlap(layer0, rof0, layer1, rof1)); - // retrieves the combined timestamp - // e.g., taking one cluster from rof0 and one from rof1 - // and constructing a tracklet (doublet) what is its time - // this assumes that the rofs overlap, e.g. doROFsOverlap -> true - // get timestamp including margins from rof0 and rof1 - const auto t0 = mLayers[layer0].getROFTimeBounds(rof0, true); - const auto t1 = mLayers[layer1].getROFTimeBounds(rof1, true); - return t0 + t1; - } - -#ifndef GPUCA_GPUCODE - /// Print functions - GPUh() void printAll() const - { - for (int32_t i = 0; i < NLayers; ++i) { - for (int32_t j = 0; j < NLayers; ++j) { - if (i != j) { - printMapping(i, j); - } - } - } - printSummary(); - } - - GPUh() void printMapping(int32_t from, int32_t to) const - { - if (from == to) { - LOGP(error, "No self-lookup supported"); - return; - } - - constexpr int w_index = 10; - constexpr int w_first = 12; - constexpr int w_last = 12; - constexpr int w_count = 10; - - LOGF(info, "Overlap mapping: Layer %d -> Layer %d", from, to); - LOGP(info, "From: {}", mLayers[from].asString()); - LOGP(info, "To : {}", mLayers[to].asString()); - LOGF(info, "%*s | %*s | %*s | %*s", w_index, "ROF.index", w_first, "First.ROF", w_last, "Last.ROF", w_count, "Count"); - LOGF(info, "%.*s-+-%.*s-+-%.*s-+-%.*s", w_index, "----------", w_first, "------------", w_last, "------------", w_count, "----------"); - - const size_t linearIdx = (from * NLayers) + to; - const auto& idx = mIndices[linearIdx]; - for (int32_t i = 0; i < idx.getEntries(); ++i) { - const auto& overlap = getOverlap(from, to, i); - LOGF(info, "%*d | %*d | %*d | %*d", w_index, i, w_first, overlap.getFirstEntry(), w_last, overlap.getEntriesBound() - 1, w_count, overlap.getEntries()); - } - } - - GPUh() void printSummary() const - { - uint32_t totalEntries{0}; - size_t flatTableSize{0}; - - for (int32_t i = 0; i < NLayers; ++i) { - for (int32_t j = 0; j < NLayers; ++j) { - if (i != j) { - const size_t linearIdx = (i * NLayers) + j; - const auto& idx = mIndices[linearIdx]; - totalEntries += idx.getEntries(); - flatTableSize += idx.getEntries(); - } - } - } - - for (int32_t i = 0; i < NLayers; ++i) { - mLayers[i].print(); - } - - const uint32_t totalBytes = (flatTableSize * sizeof(TableEntry)) + (static_cast(NLayers * NLayers) * sizeof(TableIndex)); - LOGF(info, "------------------------------------------------------------"); - LOGF(info, "Total overlap table size: %u entries", totalEntries); - LOGF(info, "Flat table size: %zu entries", flatTableSize); - LOGF(info, "Total view size: %u bytes", totalBytes); - LOGF(info, "------------------------------------------------------------"); - } -#endif -}; - -// Precalculated lookup table to find overlapping ROFs in another layer given a ROF index in the current layer -template -class ROFOverlapTable : public LayerTimingBase -{ - public: - using T = LayerTimingBase::T; - using TableEntry = dataformats::RangeReference; - using TableIndex = dataformats::RangeReference; - - using View = ROFOverlapTableView; - ROFOverlapTable() = default; - - GPUh() void init() - { - std::vector table[NLayers][NLayers]; - for (int32_t i{0}; i < NLayers; ++i) { - for (int32_t j{0}; j < NLayers; ++j) { - if (i != j) { // we do not need self-lookup - buildMapping(i, j, table[i][j]); - } - } - } - flatten(table); - } - - GPUh() View getView() const - { - View view; - view.mFlatTable = mFlatTable.data(); - view.mIndices = mIndices; - view.mLayers = this->mLayers; - return view; - } - - GPUh() View getDeviceView(const TableEntry* deviceFlatTablePtr, const TableIndex* deviceIndicesPtr, const LayerTiming* deviceLayerTimingPtr) const - { - View view; - view.mFlatTable = deviceFlatTablePtr; - view.mIndices = deviceIndicesPtr; - view.mLayers = deviceLayerTimingPtr; - return view; - } - - GPUh() size_t getFlatTableSize() const noexcept { return mFlatTable.size(); } - static GPUh() constexpr size_t getIndicesSize() { return static_cast(NLayers * NLayers); } - - private: - GPUh() void buildMapping(int32_t from, int32_t to, std::vector& table) - { - const auto& layerFrom = this->mLayers[from]; - const auto& layerTo = this->mLayers[to]; - table.resize(layerFrom.mNROFsTF); - - for (int32_t iROF{0}; iROF < layerFrom.mNROFsTF; ++iROF) { - int64_t fromStart = o2::gpu::CAMath::Max((int64_t)layerFrom.getROFStartInBC(iROF) - (int64_t)layerFrom.mROFAddTimeErr, int64_t(0)); - int64_t fromEnd = (int64_t)layerFrom.getROFEndInBC(iROF) + layerFrom.mROFAddTimeErr; - - int32_t firstROFTo = o2::gpu::CAMath::Max(0, (int32_t)((fromStart - (int64_t)layerTo.mROFAddTimeErr - (int64_t)layerTo.mROFDelay - (int64_t)layerTo.mROFBias) / (int64_t)layerTo.mROFLength)); - auto lastROFTo = (int32_t)((fromEnd + (int64_t)layerTo.mROFAddTimeErr - (int64_t)layerTo.mROFDelay - (int64_t)layerTo.mROFBias - 1) / (int64_t)layerTo.mROFLength); - firstROFTo = o2::gpu::CAMath::Max(0, firstROFTo); - lastROFTo = o2::gpu::CAMath::Min((int32_t)layerTo.mNROFsTF - 1, lastROFTo); - - while (firstROFTo <= lastROFTo) { - int64_t toStart = o2::gpu::CAMath::Max((int64_t)layerTo.getROFStartInBC(firstROFTo) - (int64_t)layerTo.mROFAddTimeErr, int64_t(0)); - int64_t toEnd = (int64_t)layerTo.getROFEndInBC(firstROFTo) + layerTo.mROFAddTimeErr; - if (toEnd > fromStart && toStart < fromEnd) { - break; - } - ++firstROFTo; - } - while (lastROFTo >= firstROFTo) { - int64_t toStart = o2::gpu::CAMath::Max((int64_t)layerTo.getROFStartInBC(lastROFTo) - (int64_t)layerTo.mROFAddTimeErr, int64_t(0)); - int64_t toEnd = (int64_t)layerTo.getROFEndInBC(lastROFTo) + layerTo.mROFAddTimeErr; - if (toEnd > fromStart && toStart < fromEnd) { - break; - } - --lastROFTo; - } - int32_t count = (firstROFTo <= lastROFTo) ? (lastROFTo - firstROFTo + 1) : 0; - table[iROF] = {static_cast(firstROFTo), static_cast(count)}; - } - } - - GPUh() void flatten(const std::vector table[NLayers][NLayers]) - { - size_t total{0}; - for (int32_t i{0}; i < NLayers; ++i) { - for (int32_t j{0}; j < NLayers; ++j) { - if (i != j) { // we do not need self-lookup - total += table[i][j].size(); - } - } - } - - mFlatTable.reserve(total); - - for (int32_t i{0}; i < NLayers; ++i) { - for (int32_t j{0}; j < NLayers; ++j) { - size_t idx = (i * NLayers) + j; - if (i != j) { - mIndices[idx].setFirstEntry(static_cast(mFlatTable.size())); - mIndices[idx].setEntries(static_cast(table[i][j].size())); - mFlatTable.insert(mFlatTable.end(), table[i][j].begin(), table[i][j].end()); - } else { - mIndices[idx] = {0, 0}; - } - } - } - } - - TableIndex mIndices[NLayers * NLayers]; - std::vector mFlatTable; -}; - -// GPU friendly view of the table below -template -struct ROFVertexLookupTableView { - const TableEntry* mFlatTable{nullptr}; - const TableIndex* mIndices{nullptr}; - const LayerTiming* mLayers{nullptr}; - - GPUhdi() const LayerTiming& getLayer(int32_t layer) const noexcept - { - assert(layer >= 0 && layer < NLayers); - return mLayers[layer]; - } - - GPUhdi() const TableEntry& getVertices(int32_t layer, size_t rofIdx) const noexcept - { - assert(layer < NLayers); - const auto& idx = mIndices[layer]; - assert(rofIdx < idx.getEntries()); - return mFlatTable[idx.getFirstEntry() + rofIdx]; - } - - GPUh() int32_t getMaxVerticesPerROF() const noexcept - { - int32_t maxCount = 0; - for (int32_t layer = 0; layer < NLayers; ++layer) { - const auto& idx = mIndices[layer]; - for (int32_t i = 0; i < idx.getEntries(); ++i) { - const auto& entry = mFlatTable[idx.getFirstEntry() + i]; - maxCount = o2::gpu::CAMath::Max(maxCount, static_cast(entry.getEntries())); - } - } - return maxCount; - } - - // Check if a specific vertex is compatible with a given ROF - GPUhdi() bool isVertexCompatible(int32_t layer, size_t rofIdx, const Vertex& vertex) const noexcept - { - assert(layer < NLayers); - const auto& layerDef = mLayers[layer]; - int64_t rofLower = o2::gpu::CAMath::Max((int64_t)layerDef.getROFStartInBC(rofIdx) - (int64_t)layerDef.mROFAddTimeErr, int64_t(0)); - int64_t rofUpper = (int64_t)layerDef.getROFEndInBC(rofIdx) + layerDef.mROFAddTimeErr; - auto vLower = (int64_t)vertex.getTimeStamp().lower(); - auto vUpper = (int64_t)vertex.getTimeStamp().upper(); - return vUpper >= rofLower && vLower < rofUpper; - } - -#ifndef GPUCA_GPUCODE - GPUh() void printAll() const - { - for (int32_t i = 0; i < NLayers; ++i) { - printLayer(i); - } - printSummary(); - } - - GPUh() void printLayer(int32_t layer) const - { - constexpr int w_rof = 10; - constexpr int w_first = 12; - constexpr int w_last = 12; - constexpr int w_count = 10; - - LOGF(info, "Vertex lookup: Layer %d", layer); - LOGF(info, "%*s | %*s | %*s | %*s", w_rof, "ROF.index", w_first, "First.Vtx", w_last, "Last.Vtx", w_count, "Count"); - LOGF(info, "%.*s-+-%.*s-+-%.*s-+-%.*s", w_rof, "----------", w_first, "------------", w_last, "------------", w_count, "----------"); - - const auto& idx = mIndices[layer]; - for (int32_t i = 0; i < idx.getEntries(); ++i) { - const auto& entry = mFlatTable[idx.getFirstEntry() + i]; - int first = entry.getFirstEntry(); - int count = entry.getEntries(); - int last = first + count - 1; - LOGF(info, "%*d | %*d | %*d | %*d", w_rof, i, w_first, first, w_last, last, w_count, count); - } - } - - GPUh() void printSummary() const - { - uint32_t totalROFs{0}; - uint32_t totalVertexRefs{0}; - - for (int32_t i = 0; i < NLayers; ++i) { - const auto& idx = mIndices[i]; - totalROFs += idx.getEntries(); - - for (int32_t j = 0; j < idx.getEntries(); ++j) { - const auto& entry = mFlatTable[idx.getFirstEntry() + j]; - totalVertexRefs += entry.getEntries(); - } - } - - const uint32_t totalBytes = (totalROFs * sizeof(TableEntry)) + (NLayers * sizeof(TableIndex)); - LOGF(info, "------------------------------------------------------------"); - LOGF(info, "Total ROFs in table: %u", totalROFs); - LOGF(info, "Total vertex references: %u", totalVertexRefs); - LOGF(info, "Total view size: %u bytes", totalBytes); - LOGF(info, "------------------------------------------------------------"); - } -#endif -}; - -// Precalculated lookup table to find vertices compatible with ROFs -// Given a layer and ROF index, returns the range of vertices that overlap in time. -// The vertex time is defined as symmetrical [t0-e,t0+e] -// It needs to be guaranteed that the input vertices are sorted by their lower-bound! -// additionally compatibliyty has to be queried per vertex! -template -class ROFVertexLookupTable : public LayerTimingBase -{ - public: - using T = LayerTimingBase::T; - using BCType = LayerTiming::BCType; - using TableEntry = dataformats::RangeReference; - using TableIndex = dataformats::RangeReference; - using View = ROFVertexLookupTableView; - - ROFVertexLookupTable() = default; - - GPUh() size_t getFlatTableSize() const noexcept { return mFlatTable.size(); } - static GPUh() constexpr size_t getIndicesSize() { return NLayers; } - - // Build the lookup table given a sorted array of vertices - // vertices must be sorted by timestamp, then by error (secondary) - GPUh() void init(const Vertex* vertices, size_t nVertices) - { - if (nVertices > std::numeric_limits::max()) { - LOGF(fatal, "too many vertices %zu, max supported is %u", nVertices, std::numeric_limits::max()); - } - - std::vector table[NLayers]; - for (int32_t layer{0}; layer < NLayers; ++layer) { - buildMapping(layer, vertices, nVertices, table[layer]); - } - flatten(table); - } - - // Pre-allocated needed memory, then use update(...) - GPUh() void init() - { - size_t total{0}; - for (int32_t layer{0}; layer < NLayers; ++layer) { - total += this->mLayers[layer].mNROFsTF; - } - mFlatTable.resize(total, {0, 0}); - size_t offset = 0; - for (int32_t layer{0}; layer < NLayers; ++layer) { - size_t nROFs = this->mLayers[layer].mNROFsTF; - mIndices[layer].setFirstEntry(static_cast(offset)); - mIndices[layer].setEntries(static_cast(nROFs)); - offset += nROFs; - } - } - - // Recalculate lookup table with new vertices - GPUh() void update(const Vertex* vertices, size_t nVertices) - { - size_t offset = 0; - for (int32_t layer{0}; layer < NLayers; ++layer) { - const auto& idx = mIndices[layer]; - size_t nROFs = idx.getEntries(); - for (size_t iROF = 0; iROF < nROFs; ++iROF) { - updateROFMapping(layer, iROF, vertices, nVertices, offset + iROF); - } - offset += nROFs; - } - } - - GPUh() View getView() const - { - View view; - view.mFlatTable = mFlatTable.data(); - view.mIndices = mIndices; - view.mLayers = this->mLayers; - return view; - } - - GPUh() View getDeviceView(const TableEntry* deviceFlatTablePtr, const TableIndex* deviceIndicesPtr, const LayerTiming* deviceLayerTimingPtr) const - { - View view; - view.mFlatTable = deviceFlatTablePtr; - view.mIndices = deviceIndicesPtr; - view.mLayers = deviceLayerTimingPtr; - return view; - } - - private: - // Build the mapping for one layer - GPUh() void buildMapping(int32_t layer, const Vertex* vertices, size_t nVertices, std::vector& table) - { - const auto& layerDef = this->mLayers[layer]; - table.resize(layerDef.mNROFsTF); - size_t vertexSearchStart = 0; - for (int32_t iROF{0}; iROF < layerDef.mNROFsTF; ++iROF) { - int64_t rofLower = o2::gpu::CAMath::Max((int64_t)layerDef.getROFStartInBC(iROF) - (int64_t)layerDef.mROFAddTimeErr, int64_t(0)); - int64_t rofUpper = (int64_t)layerDef.getROFEndInBC(iROF) + layerDef.mROFAddTimeErr; - size_t lastVertex = binarySearchFirst(vertices, nVertices, vertexSearchStart, rofUpper); - size_t firstVertex = vertexSearchStart; - while (firstVertex < lastVertex) { - auto vUpper = (int64_t)vertices[firstVertex].getTimeStamp().upper(); - if (vUpper > rofLower) { - break; - } - ++firstVertex; - } - size_t count = (lastVertex > firstVertex) ? (lastVertex - firstVertex) : 0; - table[iROF] = {static_cast(firstVertex), static_cast(count)}; - vertexSearchStart = firstVertex; - } - } - - // Update a single ROF's vertex mapping - GPUh() void updateROFMapping(int32_t layer, size_t iROF, const Vertex* vertices, size_t nVertices, size_t flatTableIdx) - { - const auto& layerDef = this->mLayers[layer]; - int64_t rofLower = o2::gpu::CAMath::Max((int64_t)layerDef.getROFStartInBC(iROF) - (int64_t)layerDef.mROFAddTimeErr, int64_t(0)); - int64_t rofUpper = (int64_t)layerDef.getROFEndInBC(iROF) + layerDef.mROFAddTimeErr; - size_t lastVertex = binarySearchFirst(vertices, nVertices, 0, rofUpper); - size_t firstVertex = 0; - while (firstVertex < lastVertex) { - int64_t vUpper = (int64_t)vertices[firstVertex].getTimeStamp().getTimeStamp() + - (int64_t)vertices[firstVertex].getTimeStamp().getTimeStampError(); - if (vUpper > rofLower) { - break; - } - ++firstVertex; - } - size_t count = (lastVertex > firstVertex) ? (lastVertex - firstVertex) : 0; - mFlatTable[flatTableIdx].setFirstEntry(static_cast(firstVertex)); - mFlatTable[flatTableIdx].setEntries(static_cast(count)); - } - - // Binary search for first vertex where maxBC >= targetBC - GPUh() size_t binarySearchFirst(const Vertex* vertices, size_t nVertices, size_t searchStart, BCType targetBC) const - { - size_t left = searchStart; - size_t right = nVertices; - while (left < right) { - size_t mid = left + ((right - left) / 2); - int64_t lower = (int64_t)vertices[mid].getTimeStamp().getTimeStamp() - - (int64_t)vertices[mid].getTimeStamp().getTimeStampError(); - if (lower < targetBC) { - left = mid + 1; - } else { - right = mid; - } - } - return left; - } - - // Compress the temporary table into a single flat table - GPUh() void flatten(const std::vector table[NLayers]) - { - // Count total entries - size_t total{0}; - for (int32_t i{0}; i < NLayers; ++i) { - total += table[i].size(); - } - - mFlatTable.reserve(total); - - // Build flat table and indices - for (int32_t i{0}; i < NLayers; ++i) { - mIndices[i].setFirstEntry(static_cast(mFlatTable.size())); - mIndices[i].setEntries(static_cast(table[i].size())); - mFlatTable.insert(mFlatTable.end(), table[i].begin(), table[i].end()); - } - } - - TableIndex mIndices[NLayers]; - std::vector mFlatTable; -}; - -// GPU-friendly view of the ROF mask table -template -struct ROFMaskTableView { - const TableEntry* mFlatMask{nullptr}; - const TableIndex* mLayerROFOffsets{nullptr}; // size NLayers+1 - - GPUhdi() bool isROFEnabled(int32_t layer, int32_t rofId) const noexcept - { - assert(layer >= 0 && layer < NLayers); - return mFlatMask[mLayerROFOffsets[layer] + rofId] != 0u; - } - -#ifndef GPUCA_GPUCODE - GPUh() void printAll() const - { - for (int32_t i = 0; i < NLayers; ++i) { - printLayer(i); - } - } - - GPUh() void printLayer(int32_t layer) const - { - constexpr int w_rof = 10; - constexpr int w_active = 10; - int32_t nROFs = mLayerROFOffsets[layer + 1] - mLayerROFOffsets[layer]; - LOGF(info, "Mask table: Layer %d", layer); - LOGF(info, "%*s | %*s", w_rof, "ROF", w_active, "Enabled"); - LOGF(info, "%.*s-+-%.*s", w_rof, "----------", w_active, "----------"); - for (int32_t i = 0; i < nROFs; ++i) { - LOGF(info, "%*d | %*d", w_rof, i, w_active, (int)isROFEnabled(layer, i)); - } - } - - GPUh() std::string asString(int32_t layer) const - { - int32_t nROFs = mLayerROFOffsets[layer + 1] - mLayerROFOffsets[layer]; - int32_t enabledROFs = 0; - for (int32_t j = 0; j < nROFs; ++j) { - if (isROFEnabled(layer, j)) { - ++enabledROFs; - } - } - return std::format("ROFMask on Layer {} ROFs enabled: {}/{}", layer, enabledROFs, nROFs); - } - - GPUh() void print(int32_t layer) const - { - LOG(info) << asString(layer); - } -#endif -}; - -// Per-ROF per-layer boolean mask (uint8_t for GPU compatibility). -template -class ROFMaskTable : public LayerTimingBase -{ - public: - using T = LayerTimingBase::T; - using BCRange = dataformats::RangeReference; - using TableIndex = uint32_t; - using TableEntry = uint8_t; - using View = ROFMaskTableView; - - ROFMaskTable() = default; - GPUh() explicit ROFMaskTable(const LayerTimingBase& timingBase) : LayerTimingBase(timingBase) { init(); } - - GPUh() void init() - { - int32_t totalROFs = 0; - for (int32_t layer{0}; layer < NLayers; ++layer) { - mLayerROFOffsets[layer] = totalROFs; - totalROFs += this->getLayer(layer).mNROFsTF; - } - mLayerROFOffsets[NLayers] = totalROFs; // sentinel - mFlatMask.resize(totalROFs, 0u); - } - - GPUh() size_t getFlatMaskSize() const noexcept { return mFlatMask.size(); } - - GPUh() void setROFEnabled(int32_t layer, int32_t rofId, uint8_t state = 1) noexcept - { - assert(layer >= 0 && layer < NLayers); - assert(rofId >= 0 && rofId < mLayerROFOffsets[layer + 1] - mLayerROFOffsets[layer]); - mFlatMask[mLayerROFOffsets[layer] + rofId] = state; - } - - GPUh() void setROFsEnabled(int32_t layer, int32_t firstRof, int32_t nRofs, uint8_t state = 1) noexcept - { - assert(layer >= 0 && layer < NLayers); - assert(firstRof >= 0); - assert(firstRof + nRofs <= mLayerROFOffsets[layer + 1] - mLayerROFOffsets[layer]); - std::memset(mFlatMask.data() + mLayerROFOffsets[layer] + firstRof, state, nRofs); - } - - // Enable all ROFs in all layers that are time-compatible with the given BC range - GPUh() void selectROF(const BCRange& t) - { - const int32_t bcStart = t.getFirstEntry(); - const int32_t bcEnd = t.getEntriesBound(); - for (int32_t layer{0}; layer < NLayers; ++layer) { - const auto& lay = this->getLayer(layer); - const int32_t offset = mLayerROFOffsets[layer]; - for (int32_t rofId{0}; rofId < lay.mNROFsTF; ++rofId) { - if (static_cast(lay.getROFStartInBC(rofId)) < bcEnd && - static_cast(lay.getROFEndInBC(rofId)) > bcStart) { - mFlatMask[offset + rofId] = 1u; - } - } - } - } - - // Reset mask to 0, then enable all ROFs compatible with any of the given BC ranges - GPUh() void selectROFs(const std::vector& ts) - { - resetMask(); - for (const auto& t : ts) { - selectROF(t); - } - } - - GPUh() void resetMask(uint8_t s = 0u) - { - std::memset(mFlatMask.data(), s, mFlatMask.size()); - } - - GPUh() void invertMask() - { - std::ranges::transform(mFlatMask, mFlatMask.begin(), [](uint8_t x) { return 1 - x; }); - } - - GPUh() void swap(ROFMaskTable& other) noexcept - { - std::swap(mFlatMask, other.mFlatMask); - std::swap(mLayerROFOffsets, other.mLayerROFOffsets); - } - - GPUh() View getView() const - { - View view; - view.mFlatMask = mFlatMask.data(); - view.mLayerROFOffsets = mLayerROFOffsets; - return view; - } - - GPUh() View getDeviceView(const TableEntry* deviceFlatMaskPtr, const TableIndex* deviceOffsetPtr) const - { - View view; - view.mFlatMask = deviceFlatMaskPtr; - view.mLayerROFOffsets = deviceOffsetPtr; - return view; - } - - private: - TableIndex mLayerROFOffsets[NLayers + 1] = {0}; - std::vector mFlatMask; -}; - -} // namespace o2::itsmft::tracking - -#endif diff --git a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/TimeFrame.h b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/TimeFrame.h index 334c994f25b2e..fe0ad66e7f65a 100644 --- a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/TimeFrame.h +++ b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/TimeFrame.h @@ -20,16 +20,21 @@ #include #include -#include "ITSMFTTracking/Types.h" +#include "DataFormatsITS/TrackITS.h" +#include "DataFormatsITS/Vertex.h" + +#include "ITStracking/BoundedAllocator.h" #include "ITSMFTTracking/Cell.h" -#include "ITSMFTTracking/Cluster.h" +#include "ITStracking/Cluster.h" +#include "ITStracking/ClusterLines.h" +#include "ITStracking/ExternalAllocator.h" +#include "ITStracking/ROFLookupTables.h" +#include "ITStracking/Tracklet.h" + #include "ITSMFTTracking/Configuration.h" -#include "ITSMFTTracking/ClusterLines.h" -#include "ITSMFTTracking/Tracklet.h" +#include "ITSMFTTracking/Constants.h" #include "ITSMFTTracking/IndexTableUtils.h" -#include "ITSMFTTracking/ExternalAllocator.h" -#include "ITSMFTTracking/BoundedAllocator.h" -#include "ITSMFTTracking/ROFLookupTables.h" +#include "ITSMFTTracking/LayerMask.h" #include "ITSMFTTracking/TrackingTopology.h" #include "SimulationDataFormat/MCCompLabel.h" #include "SimulationDataFormat/MCTruthContainer.h" @@ -54,14 +59,38 @@ class ROFRecord; namespace itsmft::tracking { +// Re-use ITS CA tracking data structures; only TimeFrame and index-table I/O are detector-aware. +using Cluster = o2::its::Cluster; +using TrackingFrameInfo = o2::its::TrackingFrameInfo; +using CellSeed = o2::itsmft::tracking::CellSeed; +using Tracklet = o2::its::Tracklet; +using LayerMask = o2::itsmft::tracking::LayerMask; +using BoundedMemoryResource = o2::its::BoundedMemoryResource; +template +using bounded_vector = o2::its::bounded_vector; +using ExternalAllocator = o2::its::ExternalAllocator; +using Line = o2::its::Line; +using ClusterLines = o2::its::ClusterLines; +using Vertex = o2::its::Vertex; +using VertexLabel = o2::its::VertexLabel; +using TrackITSExt = o2::its::TrackITSExt; + +namespace constants +{ +using namespace o2::its::constants; +using o2::itsmft::tracking::constants::ITSNLayers; +using o2::itsmft::tracking::constants::MFTNLayers; +using o2::itsmft::tracking::constants::nLayersForDet; +} // namespace constants + template struct TimeFrame { - using IndexTableUtilsN = IndexTableUtils; - using ROFOverlapTableN = ROFOverlapTable; - using ROFVertexLookupTableN = ROFVertexLookupTable; - using ROFMaskTableN = ROFMaskTable; - using TrackingTopologyN = TrackingTopology; - using TrackSeedN = TrackSeed; + using IndexTableUtilsN = o2::itsmft::IndexTableUtils; + using ROFOverlapTableN = o2::its::ROFOverlapTable; + using ROFVertexLookupTableN = o2::its::ROFVertexLookupTable; + using ROFMaskTableN = o2::its::ROFMaskTable; + using TrackingTopologyN = o2::itsmft::tracking::TrackingTopology; + using TrackSeedN = o2::itsmft::tracking::TrackSeed; TimeFrame() = default; virtual ~TimeFrame() = default; diff --git a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/TrackingConfigParam.h b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/TrackingConfigParam.h index a3afd8d02ff8e..119a5cbccbfb7 100644 --- a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/TrackingConfigParam.h +++ b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/TrackingConfigParam.h @@ -23,11 +23,6 @@ namespace o2::itsmft { -namespace tracking_constants -{ -constexpr int MaxIter = 4; -} // namespace tracking_constants - /// ITS vertexer settings (not used for MFT) struct VertexerParamConfig : public o2::conf::ConfigurableParamHelper { bool saveTimeBenchmarks = false; // dump metrics on file @@ -90,11 +85,11 @@ struct TrackerParamConfig : public o2::conf::ConfigurableParamHelper0, otherwise use code defaults - uint8_t startLayerMask[tracking_constants::MaxIter] = {}; // mask of start layer for this iteration (if >0) - int maxHolesIter[tracking_constants::MaxIter] = {}; // maximum number of missing internal layers allowed in the CA topology for each iteration - uint16_t holeLayerMaskIter[tracking_constants::MaxIter] = {}; // layers that may be skipped by the CA topology for each iteration - float minPtIterLgt[tracking_constants::MaxIter * (MaxTrackLength - MinTrackLength + 1)] = {}; // min.pT for given track length at this iteration, used only if >0, otherwise use code defaults + int minTrackLgtIter[tracking::constants::MaxIter] = {}; // minimum track length at each iteration, used only if >0, otherwise use code defaults + uint8_t startLayerMask[tracking::constants::MaxIter] = {}; // mask of start layer for this iteration (if >0) + int maxHolesIter[tracking::constants::MaxIter] = {}; // maximum number of missing internal layers allowed in the CA topology for each iteration + uint16_t holeLayerMaskIter[tracking::constants::MaxIter] = {}; // layers that may be skipped by the CA topology for each iteration + float minPtIterLgt[tracking::constants::MaxIter * (MaxTrackLength - MinTrackLength + 1)] = {}; // min.pT for given track length at this iteration, used only if >0, otherwise use code defaults float sysErr2Row[getNLayers()] = {0}; // systematic error^2 along ALPIDE rows (local X) per layer float sysErr2Col[getNLayers()] = {0}; // systematic error^2 along ALPIDE columns (local Z) per layer float maxChi2ClusterAttachment = -1.f; @@ -112,7 +107,7 @@ struct TrackerParamConfig : public o2::conf::ConfigurableParamHelper::max()); static_assert(MaxCells <= std::numeric_limits::max()); - // Describes from which layer to which layer the look-up happens struct LayerTransition { Id fromLayer{0}; Id toLayer{0}; @@ -53,8 +52,6 @@ class TrackingTopology static_assert(std::is_trivially_copyable_v); static_assert(sizeof(LayerTransition) == (2 * sizeof(Id))); - // Describes from which LayerTransition a tracklet is allowed to originate - // and with which LayerTransition this can be combined additionally the hitMasked is cached struct CellTopology { Id firstTransition{0}; Id secondTransition{0}; @@ -64,7 +61,6 @@ class TrackingTopology static_assert(std::is_trivially_copyable_v); static_assert(sizeof(CellTopology) == (2 * sizeof(Id)) + sizeof(Mask)); - // GPU ready view of the underlying LUTs struct View { const LayerTransition* transitions{nullptr}; const CellTopology* cells{nullptr}; @@ -216,4 +212,4 @@ class TrackingTopology } // namespace o2::itsmft::tracking -#endif +#endif /* ALICEO2_ITSMFT_TRACKING_INCLUDE_TRACKINGTOPOLOGY_H_ */ diff --git a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/Tracklet.h b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/Tracklet.h deleted file mode 100644 index 0518aeef50f9e..0000000000000 --- a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/Tracklet.h +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2019-2026 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -/// -/// \file Tracklet.h -/// \brief -/// - -#ifndef ALICEO2_ITSMFT_TRACKING_INCLUDE_TRACKLET_H_ -#define ALICEO2_ITSMFT_TRACKING_INCLUDE_TRACKLET_H_ - -#include "ITSMFTTracking/Constants.h" -#include "ITSMFTTracking/Types.h" -#include "ITSMFTTracking/Cluster.h" -#include "GPUCommonRtypes.h" -#include "GPUCommonMath.h" -#include "GPUCommonDef.h" -#include "GPUCommonLogger.h" - -namespace o2::itsmft::tracking -{ - -// tracklets are entirely determined by their two cluster idx -struct Tracklet final { - GPUhdDefault() Tracklet() = default; - GPUhdi() Tracklet(const int firstClusterOrderingIndex, const int secondClusterOrderingIndex, - const Cluster& firstCluster, const Cluster& secondCluster, const TimeEstBC& t) - : firstClusterIndex(firstClusterOrderingIndex), - secondClusterIndex(secondClusterOrderingIndex), - tanLambda((firstCluster.zCoordinate - secondCluster.zCoordinate) / (firstCluster.radius - secondCluster.radius)), - phi(o2::gpu::GPUCommonMath::ATan2(firstCluster.yCoordinate - secondCluster.yCoordinate, firstCluster.xCoordinate - secondCluster.xCoordinate)), - mTime(t) {} - - GPUhdi() Tracklet(const int idx0, const int idx1, float tanL, float phi, const TimeEstBC& t) - : firstClusterIndex(idx0), - secondClusterIndex(idx1), - tanLambda(tanL), - phi(phi), - mTime(t) {} - GPUhdi() bool operator<(const Tracklet& o) const noexcept - { - return (firstClusterIndex != o.firstClusterIndex) ? firstClusterIndex < o.firstClusterIndex : secondClusterIndex < o.secondClusterIndex; - } - GPUhdi() bool operator==(const Tracklet& o) const noexcept - { - return firstClusterIndex == o.firstClusterIndex && secondClusterIndex == o.secondClusterIndex; - } - GPUhdi() bool isCompatible(const Tracklet& o) const { return mTime.isCompatible(o.mTime); } - GPUhd() void print() const - { - LOGP(info, "TRKLT: fClIdx:{} sClIdx:{} ts:{}+/-{} TgL={} Phi={}", firstClusterIndex, secondClusterIndex, mTime.getTimeStamp(), mTime.getTimeStampError(), tanLambda, phi); - } - GPUhd() auto& getTimeStamp() noexcept { return mTime; } - GPUhd() const auto& getTimeStamp() const noexcept { return mTime; } - - int firstClusterIndex{constants::UnusedIndex}; - int secondClusterIndex{constants::UnusedIndex}; - float tanLambda{constants::UnsetValue}; - float phi{constants::UnsetValue}; - TimeEstBC mTime; - - ClassDefNV(Tracklet, 1); -}; - -} // namespace o2::itsmft::tracking - -#endif /* ALICEO2_ITSMFT_TRACKING_INCLUDE_TRACKLET_H_ */ diff --git a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/Types.h b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/Types.h deleted file mode 100644 index bafc09042d3da..0000000000000 --- a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/Types.h +++ /dev/null @@ -1,42 +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 Types.h -/// \brief Shared data-format types for ITSMFT CA tracking -/// - -#ifndef ALICEO2_ITSMFT_TRACKING_TYPES_H_ -#define ALICEO2_ITSMFT_TRACKING_TYPES_H_ - -#include - -#include "CommonDataFormat/TimeStamp.h" -#include "DataFormatsITS/TimeEstBC.h" -#include "DataFormatsITS/TrackITS.h" -#include "DataFormatsITS/Vertex.h" - -namespace o2::itsmft::tracking -{ - -// BC time types used by ROFLookupTables (same layout as DataFormatsITS/TimeEstBC.h) -using TimeStampType = uint32_t; -using TimeStampErrorType = uint16_t; -using TimeStamp = o2::dataformats::TimeStampWithError; - -using TimeEstBC = o2::its::TimeEstBC; -using Vertex = o2::its::Vertex; -using VertexLabel = o2::its::VertexLabel; -using TrackITS = o2::its::TrackITS; -using TrackITSExt = o2::its::TrackITSExt; - -} // namespace o2::itsmft::tracking - -#endif /* ALICEO2_ITSMFT_TRACKING_TYPES_H_ */ diff --git a/Detectors/ITSMFT/common/tracking/src/Cluster.cxx b/Detectors/ITSMFT/common/tracking/src/Cluster.cxx deleted file mode 100644 index be3760afada8f..0000000000000 --- a/Detectors/ITSMFT/common/tracking/src/Cluster.cxx +++ /dev/null @@ -1,87 +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 Cluster.cxx -/// \brief -/// -#include "GPUCommonMath.h" -#include "GPUCommonArray.h" - -#include "ITSMFTTracking/Cluster.h" -#include "ITSMFTTracking/MathUtils.h" -#include "ITSMFTTracking/IndexTableUtils.h" - -using namespace o2::itsmft::tracking; - -using math_utils::computePhi; -using math_utils::getNormalizedPhi; - -Cluster::Cluster(const float x, const float y, const float z, const int index) - : xCoordinate{x}, - yCoordinate{y}, - zCoordinate{z}, - phi{getNormalizedPhi(computePhi(x, y))}, - radius{o2::gpu::GPUCommonMath::Hypot(x, y)}, - clusterId{index}, - indexTableBinIndex{0} -{ - // Nothing to do -} - -template -Cluster::Cluster(const int layerIndex, const IndexTableUtils& utils, const Cluster& other) - : xCoordinate{other.xCoordinate}, - yCoordinate{other.yCoordinate}, - zCoordinate{other.zCoordinate}, - phi{getNormalizedPhi(computePhi(other.xCoordinate, other.yCoordinate))}, - radius{o2::gpu::GPUCommonMath::Hypot(other.xCoordinate, other.yCoordinate)}, - clusterId{other.clusterId}, - indexTableBinIndex{utils.getBinIndex(utils.getColBinIndex(layerIndex, zCoordinate), - utils.getRowBinIndex(phi))} -//, montecarloId{ other.montecarloId } -{ - // Nothing to do -} - -template -Cluster::Cluster(const int layerIndex, const float3& primaryVertex, const IndexTableUtils& utils, const Cluster& other) - : xCoordinate{other.xCoordinate}, - yCoordinate{other.yCoordinate}, - zCoordinate{other.zCoordinate}, - phi{getNormalizedPhi( - computePhi(xCoordinate - primaryVertex.x, yCoordinate - primaryVertex.y))}, - radius{o2::gpu::GPUCommonMath::Hypot(xCoordinate - primaryVertex.x, yCoordinate - primaryVertex.y)}, - clusterId{other.clusterId}, - indexTableBinIndex{utils.getBinIndex(utils.getColBinIndex(layerIndex, zCoordinate), - utils.getRowBinIndex(phi))} -{ - // Nothing to do -} - -GPUhd() void Cluster::print() const -{ - printf("Cluster: %f %f %f %f %f %d %d\n", xCoordinate, yCoordinate, zCoordinate, phi, radius, clusterId, indexTableBinIndex); -} - -TrackingFrameInfo::TrackingFrameInfo(float x, float y, float z, float xTF, float alpha, std::array&& posTF, - std::array&& covTF) - : xCoordinate{x}, yCoordinate{y}, zCoordinate{z}, xTrackingFrame{xTF}, alphaTrackingFrame{alpha}, positionTrackingFrame{posTF}, covarianceTrackingFrame{covTF} -{ - // Nothing to do -} - -GPUhd() void TrackingFrameInfo::print() const -{ - printf("x: %f y: %f z: %f xTF: %f alphaTF: %f posTF: %f %f covTF: %f %f %f\n", - xCoordinate, yCoordinate, zCoordinate, xTrackingFrame, alphaTrackingFrame, - positionTrackingFrame[0], positionTrackingFrame[1], - covarianceTrackingFrame[0], covarianceTrackingFrame[1], covarianceTrackingFrame[2]); -} diff --git a/Detectors/ITSMFT/common/tracking/src/ClusterLines.cxx b/Detectors/ITSMFT/common/tracking/src/ClusterLines.cxx deleted file mode 100644 index 939cd85828f73..0000000000000 --- a/Detectors/ITSMFT/common/tracking/src/ClusterLines.cxx +++ /dev/null @@ -1,217 +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 "Framework/Logger.h" -#include "ITSMFTTracking/ClusterLines.h" - -namespace o2::itsmft::tracking -{ - -Line::Line(const Tracklet& tracklet, const Cluster* innerClusters, const Cluster* outerClusters) : mTime(tracklet.mTime) -{ - const auto& inner = innerClusters[tracklet.firstClusterIndex]; - const auto& outer = outerClusters[tracklet.secondClusterIndex]; - - originPoint = SVector3f(inner.xCoordinate, inner.yCoordinate, inner.zCoordinate); - cosinesDirector = SVector3f(outer.xCoordinate - inner.xCoordinate, - outer.yCoordinate - inner.yCoordinate, - outer.zCoordinate - inner.zCoordinate); - cosinesDirector /= std::sqrt(ROOT::Math::Dot(cosinesDirector, cosinesDirector)); -} - -float Line::getDistance2FromPoint(const Line& line, const std::array& point) -{ - const SVector3f p(point.data(), 3); - const SVector3f delta = p - line.originPoint; - const float proj = ROOT::Math::Dot(delta, line.cosinesDirector); - const SVector3f residual = delta - proj * line.cosinesDirector; - return ROOT::Math::Dot(residual, residual); -} - -float Line::getDistanceFromPoint(const Line& line, const std::array& point) -{ - return std::sqrt(getDistance2FromPoint(line, point)); -} - -float Line::getDCA2(const Line& firstLine, const Line& secondLine, const float precision) -{ - const SVector3f n = ROOT::Math::Cross(firstLine.cosinesDirector, secondLine.cosinesDirector); - const float norm2 = ROOT::Math::Dot(n, n); - - if (norm2 <= precision * precision) { - // lines are parallel, fall back to point-to-line distance - const SVector3f d = secondLine.originPoint - firstLine.originPoint; - const float proj = ROOT::Math::Dot(d, firstLine.cosinesDirector); - const SVector3f residual = d - proj * firstLine.cosinesDirector; - return ROOT::Math::Dot(residual, residual); - } - - const SVector3f delta = secondLine.originPoint - firstLine.originPoint; - const float numerator = ROOT::Math::Dot(delta, n); - return (numerator * numerator) / norm2; -} - -float Line::getDCA(const Line& firstLine, const Line& secondLine, const float precision) -{ - return std::sqrt(getDCA2(firstLine, secondLine, precision)); -} - -Line::SMatrix3f Line::getDCAComponents(const Line& line, const std::array& point) -{ - const SVector3f p(point.data(), 3); - const SVector3f delta = line.originPoint - p; - const float proj = ROOT::Math::Dot(line.cosinesDirector, delta); - const SVector3f residual = delta - proj * line.cosinesDirector; - - // symmetric 3x3: diagonal = residual components, off-diagonal = 2D projected distances - SMatrix3f m; - m(0, 0) = residual(0); - m(1, 1) = residual(1); - m(2, 2) = residual(2); - m(0, 1) = std::hypot(m(0, 0), m(1, 1)); - m(0, 2) = std::hypot(m(0, 0), m(2, 2)); - m(1, 2) = std::hypot(m(1, 1), m(2, 2)); - return m; -} - -bool Line::isEmpty() const noexcept -{ - return ROOT::Math::Dot(originPoint, originPoint) == 0.f && - ROOT::Math::Dot(cosinesDirector, cosinesDirector) == 0.f; -} - -void Line::print() const -{ - LOGP(info, "\tLine: originPoint = ({}, {}, {}), cosinesDirector = ({}, {}, {}) ts={}+-{}", - originPoint(0), originPoint(1), originPoint(2), - cosinesDirector(0), cosinesDirector(1), cosinesDirector(2), - mTime.getTimeStamp(), mTime.getTimeStampError()); -} - -// Accumulate the weighted normal equation contributions (A matrix and B vector) -// from a single line into the running sums. The covariance is assumed to be -// diagonal and uniform ({1,1,1}) so the weights simplify accordingly. -// The A matrix entry (i,j) = (delta_ij - d_i*d_j) / det, and the B vector -// entry b_i = sum_j d_j*(d_j*o_i - d_i*o_j) / det, where d = cosinesDirector -// and o = originPoint. -void ClusterLines::accumulate(const Line& line) -{ - const ROOT::Math::SVector d(line.cosinesDirector(0), line.cosinesDirector(1), line.cosinesDirector(2)); - const ROOT::Math::SVector o(line.originPoint(0), line.originPoint(1), line.originPoint(2)); - - // == 1 for normalised directors, kept for generality - const double det = ROOT::Math::Dot(d, d); - - // A matrix (symmetric): A_ij = (delta_ij * |d|^2 - d_i * d_j) / det - for (int i = 0; i < 3; ++i) { - for (int j = i; j < 3; ++j) { - mAMatrix(i, j) += ((i == j ? det : 0.) - d(i) * d(j)) / det; - } - } - - // B vector: b_i = (d_i * dot(d,o) - |d|^2 * o_i) / det - const double dDotO = ROOT::Math::Dot(d, o); - for (int i = 0; i < 3; ++i) { - mBMatrix(i) += (d(i) * dDotO - det * o(i)) / det; - } -} - -ClusterLines::ClusterLines(const int firstLabel, const Line& firstLine, const int secondLabel, const Line& secondLine) : mTime(firstLine.mTime) -{ - mTime += secondLine.mTime; - - mLabels.push_back(firstLabel); - if (secondLabel > 0) { - mLabels.push_back(secondLabel); // don't add info in case of beamline used - } - - accumulate(firstLine); - accumulate(secondLine); - computeClusterCentroid(); - - // RMS2: running mean update - mRMS2 = Line::getDCAComponents(firstLine, mVertex); - const auto tmpRMS2 = Line::getDCAComponents(secondLine, mVertex); - mRMS2 += (tmpRMS2 - mRMS2) * (1.f / static_cast(getSize())); - - // AvgDistance2 - mAvgDistance2 = Line::getDistance2FromPoint(firstLine, mVertex); - mAvgDistance2 += (Line::getDistance2FromPoint(secondLine, mVertex) - mAvgDistance2) / (float)getSize(); -} - -ClusterLines::ClusterLines(gsl::span lineLabels, gsl::span lines) -{ - if (lineLabels.size() < 2) { - return; - } - - mLabels.reserve(lineLabels.size()); - mTime = lines[lineLabels[0]].mTime; - for (size_t index = 0; index < lineLabels.size(); ++index) { - const auto lineLabel = lineLabels[index]; - if (index > 0) { - mTime += lines[lineLabel].mTime; - } - mLabels.push_back(lineLabel); - accumulate(lines[lineLabel]); - } - - computeClusterCentroid(); - if (!mIsValid) { - return; - } - - mRMS2 = Line::getDCAComponents(lines[lineLabels[0]], mVertex); - mAvgDistance2 = Line::getDistance2FromPoint(lines[lineLabels[0]], mVertex); - for (size_t index = 1; index < lineLabels.size(); ++index) { - const auto lineLabel = lineLabels[index]; - const auto tmpRMS2 = Line::getDCAComponents(lines[lineLabel], mVertex); - mRMS2 += (tmpRMS2 - mRMS2) * (1.f / static_cast(index + 1)); - mAvgDistance2 += (Line::getDistance2FromPoint(lines[lineLabel], mVertex) - mAvgDistance2) / static_cast(index + 1); - } -} - -void ClusterLines::add(const int lineLabel, const Line& line) -{ - mTime += line.mTime; - mLabels.push_back(lineLabel); - - accumulate(line); - computeClusterCentroid(); - mAvgDistance2 += (Line::getDistance2FromPoint(line, mVertex) - mAvgDistance2) / (float)getSize(); -} - -void ClusterLines::computeClusterCentroid() -{ - // Solve the 3x3 symmetric linear system AX = -B using SMatrix inversion. - // Invert() returns false if the matrix is singular or ill-conditioned. - SMatrix3 invA{mAMatrix}; - mIsValid = invA.Invert(); - if (!mIsValid) { - return; - } - - SVector3 result = invA * mBMatrix; - mVertex[0] = static_cast(-result(0)); - mVertex[1] = static_cast(-result(1)); - mVertex[2] = static_cast(-result(2)); -} - -bool ClusterLines::operator==(const ClusterLines& rhs) const noexcept -{ - return mRMS2 == rhs.mRMS2 && - mVertex == rhs.mVertex && - mLabels == rhs.mLabels && - mAvgDistance2 == rhs.mAvgDistance2; -} - -} // namespace o2::itsmft::tracking diff --git a/Detectors/ITSMFT/common/tracking/src/IOUtils.cxx b/Detectors/ITSMFT/common/tracking/src/IOUtils.cxx index 291ebce622169..6ed2dfd8a13af 100644 --- a/Detectors/ITSMFT/common/tracking/src/IOUtils.cxx +++ b/Detectors/ITSMFT/common/tracking/src/IOUtils.cxx @@ -14,8 +14,8 @@ /// #include "ITSMFTTracking/IOUtils.h" -#include "ITSMFTTracking/Cluster.h" #include "ITSMFTTracking/TrackingConfigParam.h" +#include "ITStracking/Cluster.h" #include "Framework/Logger.h" #include "ITSBase/GeometryTGeo.h" @@ -46,7 +46,7 @@ void loadClusterTrackingFrameInfoImpl(GeomT* geom, const o2::itsmft::TopologyDictionary* dict, int& layer, unsigned int& clusterSize, - o2::itsmft::tracking::TrackingFrameInfo& tfInfo, + o2::its::TrackingFrameInfo& tfInfo, bool applySysErrors) { const auto sensorID = c.getSensorID(); @@ -66,14 +66,14 @@ void loadClusterTrackingFrameInfoImpl(GeomT* geom, if constexpr (DetId == o2::detectors::DetID::ITS) { const auto trkXYZ = geom->getMatrixT2L(sensorID) ^ locXYZ; const auto gloXYZ = geom->getMatrixL2G(sensorID) * locXYZ; - tfInfo = o2::itsmft::tracking::TrackingFrameInfo{ + tfInfo = o2::its::TrackingFrameInfo{ gloXYZ.x(), gloXYZ.y(), gloXYZ.z(), trkXYZ.x(), geom->getSensorRefAlpha(sensorID), std::array{trkXYZ.y(), trkXYZ.z()}, std::array{sigma2Row, 0.f, sigma2Col}}; } else { const auto gloXYZ = geom->getMatrixL2G(sensorID) * locXYZ; // ALPIDE row (local X) -> global X, column (local Z) -> global Y - tfInfo = o2::itsmft::tracking::TrackingFrameInfo{ + tfInfo = o2::its::TrackingFrameInfo{ gloXYZ.x(), gloXYZ.y(), gloXYZ.z(), gloXYZ.x(), 0.f, std::array{gloXYZ.y(), gloXYZ.z()}, std::array{sigma2Row, 0.f, sigma2Col}}; @@ -144,7 +144,7 @@ void loadClusterTrackingFrameInfo(const CompClusterExt& c, const TopologyDictionary* dict, int& layer, unsigned int& clusterSize, - tracking::TrackingFrameInfo& tfInfo, + o2::its::TrackingFrameInfo& tfInfo, bool applySysErrors) { if constexpr (DetId == o2::detectors::DetID::ITS) { @@ -159,7 +159,7 @@ template void loadClusterTrackingFrameInfo(const Comp const TopologyDictionary* dict, int& layer, unsigned int& clusterSize, - tracking::TrackingFrameInfo& tfInfo, + o2::its::TrackingFrameInfo& tfInfo, bool applySysErrors); template void loadClusterTrackingFrameInfo(const CompClusterExt& c, @@ -167,7 +167,7 @@ template void loadClusterTrackingFrameInfo(const Comp const TopologyDictionary* dict, int& layer, unsigned int& clusterSize, - tracking::TrackingFrameInfo& tfInfo, + o2::its::TrackingFrameInfo& tfInfo, bool applySysErrors); template diff --git a/Detectors/ITSMFT/common/tracking/src/TimeFrame.cxx b/Detectors/ITSMFT/common/tracking/src/TimeFrame.cxx index 1f98dd65f522c..27d486ecb9fd5 100644 --- a/Detectors/ITSMFT/common/tracking/src/TimeFrame.cxx +++ b/Detectors/ITSMFT/common/tracking/src/TimeFrame.cxx @@ -18,12 +18,10 @@ #include "Framework/Logger.h" #include "ITSMFTTracking/TimeFrame.h" #include "ITSMFTTracking/IOUtils.h" -#include "ITSMFTTracking/MathUtils.h" +#include "ITStracking/MathUtils.h" #include "DataFormatsITSMFT/CompCluster.h" #include "DataFormatsITSMFT/ROFRecord.h" #include "DataFormatsITSMFT/TopologyDictionary.h" -#include "ITSMFTTracking/BoundedAllocator.h" -#include "ITSMFTTracking/Constants.h" #include "DetectorsCommonDataFormats/DetID.h" namespace @@ -41,7 +39,7 @@ void loadClusterForDet(o2::detectors::DetID::ID detId, const o2::itsmft::TopologyDictionary* dict, int& layer, unsigned int& clusterSize, - o2::itsmft::tracking::TrackingFrameInfo& tfInfo) + o2::its::TrackingFrameInfo& tfInfo) { switch (detId) { case o2::detectors::DetID::ITS: @@ -76,6 +74,9 @@ void configureIndexTableUtils(o2::itsmft::IndexTableUtils& utils, namespace o2::itsmft::tracking { +using o2::its::clearResizeBoundedVector; +using o2::its::deepVectorClear; +namespace math_utils = o2::its::math_utils; using o2::itsmft::IndexTableCoordType; using o2::itsmft::IterationStep; using o2::itsmft::TrackingParameters;