From ec5288ef19c6b64b1e033dae75aa32692f0b4c68 Mon Sep 17 00:00:00 2001 From: Maurice Coquet Date: Fri, 5 Jun 2026 15:47:05 +0200 Subject: [PATCH 1/5] 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/5] 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; From c95ac648e62beab36bcf78d64fb1a85449ba57c8 Mon Sep 17 00:00:00 2001 From: Maurice Coquet Date: Mon, 8 Jun 2026 17:32:40 +0200 Subject: [PATCH 3/5] First Verison of CA-MFT tracking --- Detectors/ITSMFT/MFT/workflow/CMakeLists.txt | 2 + .../include/MFTWorkflow/CATrackerSpec.h | 33 +- .../ITSMFT/MFT/workflow/src/CATrackerSpec.cxx | 209 +++- .../workflow/src/mft-ca-tracker-workflow.cxx | 35 +- Detectors/ITSMFT/common/CMakeLists.txt | 2 +- .../ITSMFT/common/tracking/CMakeLists.txt | 32 +- .../include/ITSMFTTracking/CATrackTypes.h | 37 + .../include/ITSMFTTracking/CATracker.h | 77 ++ .../include/ITSMFTTracking/Configuration.h | 33 +- .../include/ITSMFTTracking/Constants.h | 24 + .../include/ITSMFTTracking/DetectorTraits.h | 152 +++ .../include/ITSMFTTracking/IndexTableUtils.h | 28 + .../include/ITSMFTTracking/MFTCATrack.h | 152 +++ .../include/ITSMFTTracking/MFTForwardRefit.h | 36 + .../include/ITSMFTTracking/TimeFrame.h | 14 +- .../tracking/include/ITSMFTTracking/Tracker.h | 21 + .../include/ITSMFTTracking/TrackerTraits.h | 87 ++ .../ITSMFTTracking/TrackingConfigParam.h | 22 +- .../ITSMFTTracking/TrackingInterface.h | 124 ++ .../include/ITSMFTTracking/TrackingParamRef.h | 47 + .../ITSMFT/common/tracking/src/CATracker.cxx | 199 +++ .../common/tracking/src/Configuration.cxx | 269 +++- .../common/tracking/src/DetectorTraits.cxx | 174 +++ .../ITSMFT/common/tracking/src/IOUtils.cxx | 92 +- .../tracking/src/ITSMFTTrackingLinkDef.h | 6 - .../common/tracking/src/MFTForwardRefit.cxx | 190 +++ .../ITSMFT/common/tracking/src/TimeFrame.cxx | 48 +- .../common/tracking/src/TrackerTraits.cxx | 1113 +++++++++++++++++ .../tracking/src/TrackingConfigParam.cxx | 11 +- .../common/tracking/src/TrackingInterface.cxx | 331 +++++ 30 files changed, 3414 insertions(+), 186 deletions(-) create mode 100644 Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/CATrackTypes.h create mode 100644 Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/CATracker.h create mode 100644 Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/DetectorTraits.h create mode 100644 Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/MFTCATrack.h create mode 100644 Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/MFTForwardRefit.h create mode 100644 Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/Tracker.h create mode 100644 Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/TrackerTraits.h create mode 100644 Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/TrackingInterface.h create mode 100644 Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/TrackingParamRef.h create mode 100644 Detectors/ITSMFT/common/tracking/src/CATracker.cxx create mode 100644 Detectors/ITSMFT/common/tracking/src/DetectorTraits.cxx create mode 100644 Detectors/ITSMFT/common/tracking/src/MFTForwardRefit.cxx create mode 100644 Detectors/ITSMFT/common/tracking/src/TrackerTraits.cxx create mode 100644 Detectors/ITSMFT/common/tracking/src/TrackingInterface.cxx diff --git a/Detectors/ITSMFT/MFT/workflow/CMakeLists.txt b/Detectors/ITSMFT/MFT/workflow/CMakeLists.txt index 5c78df4dd9651..cee277a16ad7f 100644 --- a/Detectors/ITSMFT/MFT/workflow/CMakeLists.txt +++ b/Detectors/ITSMFT/MFT/workflow/CMakeLists.txt @@ -22,11 +22,13 @@ o2_add_library(MFTWorkflow O2::SimConfig O2::SimulationDataFormat O2::ITSMFTReconstruction + O2::ITSMFTTracking O2::MFTTracking O2::MFTAssessment O2::DataFormatsMFT O2::ITSMFTWorkflow O2::ITStracking + TBB::tbb O2::MFTAlignment O2::GlobalTrackingWorkflowReaders) o2_add_executable(reco-workflow diff --git a/Detectors/ITSMFT/MFT/workflow/include/MFTWorkflow/CATrackerSpec.h b/Detectors/ITSMFT/MFT/workflow/include/MFTWorkflow/CATrackerSpec.h index ace31aa83af9b..c1414d2a79f8b 100644 --- a/Detectors/ITSMFT/MFT/workflow/include/MFTWorkflow/CATrackerSpec.h +++ b/Detectors/ITSMFT/MFT/workflow/include/MFTWorkflow/CATrackerSpec.h @@ -15,28 +15,26 @@ #define O2_MFT_CATRACKERSPEC_H_ #include -#include -#include - -#include "DataFormatsITSMFT/ROFRecord.h" -#include "DataFormatsITSMFT/TopologyDictionary.h" -#include "DataFormatsParameters/GRPObject.h" #include "DetectorsBase/GRPGeomHelper.h" #include "Framework/DataProcessorSpec.h" #include "Framework/Task.h" -#include "ITStracking/ROFLookupTables.h" +#include "ITSMFTTracking/Configuration.h" +#include "ITSMFTTracking/TrackingInterface.h" namespace o2::mft { +/// MFT CA tracker DPL task. Delegates reconstruction to ITSMFTTrackingInterfaceMFT. class CATrackerDPL : public o2::framework::Task { public: - static constexpr int NCALayers = 5; - using ROFOverlapTable = o2::its::ROFOverlapTable; - - CATrackerDPL(std::shared_ptr gr, bool useMC) : mGGCCDBRequest(std::move(gr)), mUseMC(useMC) {} + CATrackerDPL(std::shared_ptr gr, + bool useMC, + o2::itsmft::TrackingMode::Type trMode) + : mGGCCDBRequest(std::move(gr)), mUseMC(useMC), mTracking(useMC, trMode, false) + { + } ~CATrackerDPL() override = default; void init(framework::InitContext& ic) final; @@ -45,18 +43,15 @@ class CATrackerDPL : public o2::framework::Task private: void updateTimeDependentParams(framework::ProcessingContext& pc); - void initialiseROFTable(gsl::span rofs); - bool mUseMC = false; std::shared_ptr mGGCCDBRequest; - const o2::itsmft::TopologyDictionary* mDict = nullptr; - ROFOverlapTable mROFTable; - ROFOverlapTable::View mROFTableView; + bool mUseMC = false; + bool mTrackingInitialised = false; + o2::itsmft::tracking::ITSMFTTrackingInterfaceMFT mTracking; }; -/// create a processor spec that reads all MFT tracker inputs and provides the -/// future insertion point for wiring them into a generalized ITS TimeFrame. -o2::framework::DataProcessorSpec getCATrackerSpec(bool useMC, bool useGeom, bool useIRFrames); +o2::framework::DataProcessorSpec getCATrackerSpec(bool useMC, bool useGeom, bool useIRFrames, + o2::itsmft::TrackingMode::Type trMode); } // namespace o2::mft diff --git a/Detectors/ITSMFT/MFT/workflow/src/CATrackerSpec.cxx b/Detectors/ITSMFT/MFT/workflow/src/CATrackerSpec.cxx index 78ba9b9fc7a19..f98abd3806bb6 100644 --- a/Detectors/ITSMFT/MFT/workflow/src/CATrackerSpec.cxx +++ b/Detectors/ITSMFT/MFT/workflow/src/CATrackerSpec.cxx @@ -13,26 +13,24 @@ #include "MFTWorkflow/CATrackerSpec.h" +#include #include #include -#include "CommonConstants/LHCConstants.h" #include "CommonDataFormat/IRFrame.h" #include "DataFormatsITSMFT/CompCluster.h" -#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "DataFormatsITSMFT/ROFRecord.h" #include "DataFormatsITSMFT/TopologyDictionary.h" -#include "DataFormatsParameters/GRPObject.h" +#include "DataFormatsMFT/TrackMFT.h" #include "DetectorsBase/GeometryManager.h" -#include "DetectorsCommonDataFormats/DetectorNameConf.h" #include "Framework/CCDBParamSpec.h" -#include "Framework/ConfigParamRegistry.h" -#include "Framework/ControlService.h" #include "Framework/DataProcessorSpec.h" -#include "Framework/DeviceSpec.h" #include "Framework/Logger.h" +#include "ITSMFTTracking/MFTCATrack.h" #include "MFTBase/GeometryTGeo.h" +#include "MFTTracking/Constants.h" +#include "MFTTracking/MFTTrackingParam.h" #include "SimulationDataFormat/MCCompLabel.h" #include "SimulationDataFormat/MCTruthContainer.h" @@ -41,6 +39,74 @@ using namespace o2::framework; namespace o2::mft { +static_assert(o2::itsmft::tracking::ITSMFTTrackingInterfaceMFT::DetId == o2::detectors::DetID::MFT); +static_assert(o2::itsmft::tracking::constants::MFTNLayers == o2::mft::constants::mft::LayersNumber); + +namespace +{ +template +void fillMFTOutputs(const o2::itsmft::tracking::TimeFrameMFT& tf, + gsl::span inputROFs, + TracksVec& tracks, + ClusterIdxVec& clusterIndices, + ROFVec& trackROFs, + LabelsVec& trackLabels, + bool useMC) +{ + trackROFs.assign(inputROFs.begin(), inputROFs.end()); + for (auto& rof : trackROFs) { + rof.setFirstEntry(0); + rof.setNEntries(0); + } + + const auto& tracksIn = tf.getTracks(); + tracks.reserve(tracksIn.size()); + if (useMC) { + trackLabels.reserve(tracksIn.size()); + } + + const auto& clockLayer = tf.getROFOverlapTableView().getClockLayer(); + std::vector rofEntries(trackROFs.size() + 1, 0); + + for (size_t iTrk = 0; iTrk < tracksIn.size(); ++iTrk) { + const auto& src = tracksIn[iTrk]; + auto dst = src.getTrack(); + dst.setExternalClusterIndexOffset(clusterIndices.size()); + int nPoints = 0; + for (int layer = o2::itsmft::tracking::MFTCATrack::MaxClusters; layer--;) { + if (!src.hasHitOnLayer(layer)) { + continue; + } + const int extIdx = src.getClusterIndex(layer); + if (extIdx < 0) { + continue; + } + const int clsSize = src.getClusterSize(layer); + dst.setClusterSize(layer, clsSize > 0 ? clsSize : tf.getClusterSize(0, extIdx)); + clusterIndices.push_back(extIdx); + ++nPoints; + } + dst.setNumberOfPoints(nPoints); + tracks.push_back(dst); + + if (useMC && iTrk < tf.getTracksLabel().size()) { + trackLabels.push_back(tf.getTracksLabel()[iTrk]); + } + + const auto rof = clockLayer.getROF(src.getTimeStamp()); + if (rof >= 0 && rof < static_cast(trackROFs.size())) { + ++rofEntries[rof]; + } + } + + std::exclusive_scan(rofEntries.begin(), rofEntries.end(), rofEntries.begin(), 0); + for (size_t iROF = 0; iROF < trackROFs.size(); ++iROF) { + trackROFs[iROF].setFirstEntry(rofEntries[iROF]); + trackROFs[iROF].setNEntries(rofEntries[iROF + 1] - rofEntries[iROF]); + } +} +} // namespace + void CATrackerDPL::init(InitContext&) { o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); @@ -50,37 +116,64 @@ void CATrackerDPL::run(ProcessingContext& pc) { updateTimeDependentParams(pc); + auto rofsinput = pc.inputs().get>("ROframes"); + auto& trackROFs = pc.outputs().make>(Output{"MFT", "MFTTrackROF", 0}, + rofsinput.begin(), rofsinput.end()); + auto& allTracksMFT = pc.outputs().make>(Output{"MFT", "TRACKS", 0}); + auto& allClusIdx = pc.outputs().make>(Output{"MFT", "TRACKCLSID", 0}); + std::vector allTrackLabels; + + if (!mTracking.isActive()) { + return; + } + auto compClusters = pc.inputs().get>("compClusters"); gsl::span patterns = pc.inputs().get>("patterns"); - auto rofs = pc.inputs().get>("ROframes"); - initialiseROFTable(gsl::span(rofs.data(), rofs.size())); const dataformats::MCTruthContainer* labels = nullptr; - if (mUseMC) { + if (mUseMC && pc.inputs().getPos("labels") >= 0) { labels = pc.inputs().get*>("labels").release(); } - LOGP(info, "MFT CA input pulled {} compressed clusters, {} pattern bytes, {} RO frames, dictionary={}, MC labels={}", - compClusters.size(), patterns.size(), rofs.size(), mDict != nullptr, labels != nullptr); - - size_t nClusterRefs = 0; - for (const auto& rof : rofs) { - nClusterRefs += rof.getNEntries(); + gsl::span irFrames; + if (pc.inputs().getPos("IRFramesITS") >= 0) { + irFrames = pc.inputs().get>("IRFramesITS"); } - LOGP(info, "MFT CA input ROF cluster references: {}", nClusterRefs); - if (pc.inputs().getPos("IRFramesITS") >= 0) { - auto irFrames = pc.inputs().get>("IRFramesITS"); - LOGP(info, "MFT CA input pulled {} ITS IR frames", irFrames.size()); + LOGP(info, "MFT CA input pulled {} compressed clusters in {} RO frames ({} pattern bytes)", + compClusters.size(), rofsinput.size(), patterns.size()); + + mTracking.processTimeFrame(gsl::span(rofsinput.data(), rofsinput.size()), + gsl::span(compClusters.data(), compClusters.size()), + patterns, + labels, + irFrames); + + fillMFTOutputs(mTracking.getTimeFrame(), + gsl::span(rofsinput.data(), rofsinput.size()), + allTracksMFT, + allClusIdx, + trackROFs, + allTrackLabels, + mUseMC); + + LOGP(info, "MFT CA pushed {} tracks in {} ROFs", allTracksMFT.size(), trackROFs.size()); + + if (mUseMC) { + pc.outputs().snapshot(Output{"MFT", "TRACKSMCTR", 0}, allTrackLabels); + LOGP(info, "MFT CA pushed {} track MC labels", allTrackLabels.size()); } - // Next step: replace this inspection point with a loader into a generalized - // ITS TimeFrame interface that accepts MFT layer geometry and cluster data. + mTracking.clearTimeFrame(); } void CATrackerDPL::updateTimeDependentParams(ProcessingContext& pc) { o2::base::GRPGeomHelper::instance().checkUpdates(pc); + if (!mTrackingInitialised) { + mTrackingInitialised = true; + mTracking.initialise(); + } static bool initOnceDone = false; if (!initOnceDone) { initOnceDone = true; @@ -90,43 +183,11 @@ void CATrackerDPL::updateTimeDependentParams(ProcessingContext& pc) pc.inputs().get("cldict"); o2::mft::GeometryTGeo::Instance()->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::T2GRot, - o2::math_utils::TransformType::T2G)); + o2::math_utils::TransformType::T2G, + o2::math_utils::TransformType::L2G)); } } -void CATrackerDPL::initialiseROFTable(gsl::span rofs) -{ - if (!o2::base::GRPGeomHelper::instance().getGRPECS()->isDetContinuousReadOut(o2::detectors::DetID::MFT)) { - LOGP(fatal, "MFT CA tracker currently supports only continuous readout"); - } - - const auto& par = o2::itsmft::DPLAlpideParam::Instance(); - const int nOrbitsPerTF = o2::base::GRPGeomHelper::getNHBFPerTF(); - const int timingSourceLayer = 0; // MFT CA disks share timing for now; keep per-CA-layer definition for future staggering. - const unsigned int nROFsPerOrbit = o2::constants::lhc::LHCMaxBunches / par.getROFLengthInBC(timingSourceLayer); - const auto nROFsTF = nROFsPerOrbit * nOrbitsPerTF; - - ROFOverlapTable rofTable; - for (int caLayer = 0; caLayer < NCALayers; ++caLayer) { - const o2::its::LayerTiming timing{ - .mNROFsTF = nROFsTF, - .mROFLength = static_cast(par.getROFLengthInBC(timingSourceLayer)), - .mROFDelay = static_cast(par.getROFDelayInBC(timingSourceLayer)), - .mROFBias = static_cast(par.getROFBiasInBC(timingSourceLayer)), - .mROFAddTimeErr = 0}; - rofTable.defineLayer(caLayer, timing); - } - rofTable.init(); - mROFTable = std::move(rofTable); - mROFTableView = mROFTable.getView(); - - if (rofs.size() != nROFsTF) { - LOGP(warn, "MFT CA ROF count differs from continuous timing expectation: received {} expected {}", rofs.size(), nROFsTF); - } - LOGP(info, "MFT CA ROF lookup table initialised for {} CA layers, {} ROFs/TF, ROF length {} BC, bias {} BC", - NCALayers, nROFsTF, par.getROFLengthInBC(timingSourceLayer), par.getROFBiasInBC(timingSourceLayer)); -} - void CATrackerDPL::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) { if (o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj)) { @@ -134,17 +195,21 @@ void CATrackerDPL::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) } if (matcher == ConcreteDataMatcher("MFT", "CLUSDICT", 0)) { LOG(info) << "MFT CA input cluster dictionary updated"; - mDict = static_cast(obj); + mTracking.setClusterDictionary(static_cast(obj)); return; } if (matcher == ConcreteDataMatcher("MFT", "GEOMTGEO", 0)) { LOG(info) << "MFT CA input GeometryTGeo loaded from CCDB"; o2::mft::GeometryTGeo::adopt(static_cast(obj)); + o2::mft::GeometryTGeo::Instance()->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, + o2::math_utils::TransformType::T2GRot, + o2::math_utils::TransformType::T2G, + o2::math_utils::TransformType::L2G)); return; } } -DataProcessorSpec getCATrackerSpec(bool useMC, bool useGeom, bool useIRFrames) +DataProcessorSpec getCATrackerSpec(bool useMC, bool useGeom, bool useIRFrames, o2::itsmft::TrackingMode::Type trMode) { std::vector inputs; inputs.emplace_back("compClusters", "MFT", "COMPCLUSTERS", 0, Lifetime::Timeframe); @@ -155,27 +220,37 @@ DataProcessorSpec getCATrackerSpec(bool useMC, bool useGeom, bool useIRFrames) if (useMC) { inputs.emplace_back("labels", "MFT", "CLUSTERSMCTR", 0, Lifetime::Timeframe); } - if (useIRFrames) { + + const auto& trackingParam = MFTTrackingParam::Instance(); + if (useIRFrames || trackingParam.irFramesOnly) { inputs.emplace_back("IRFramesITS", "ITS", "IRFRAMES", 0, Lifetime::Timeframe); } - auto ggRequest = std::make_shared(false, // orbitResetTime - true, // GRPECS=true - false, // GRPLHCIF - true, // GRPMagField - false, // askMatLUT - useGeom ? o2::base::GRPGeomRequest::Aligned : o2::base::GRPGeomRequest::None, // geometry + auto ggRequest = std::make_shared(false, + true, + false, + true, + true, + useGeom ? o2::base::GRPGeomRequest::Aligned : o2::base::GRPGeomRequest::None, inputs, true); if (!useGeom) { ggRequest->addInput({"mftTGeo", "MFT", "GEOMTGEO", 0, Lifetime::Condition, framework::ccdbParamSpec("MFT/Config/Geometry")}, inputs); } + std::vector outputs; + outputs.emplace_back("MFT", "TRACKS", 0, Lifetime::Timeframe); + outputs.emplace_back("MFT", "MFTTrackROF", 0, Lifetime::Timeframe); + outputs.emplace_back("MFT", "TRACKCLSID", 0, Lifetime::Timeframe); + if (useMC) { + outputs.emplace_back("MFT", "TRACKSMCTR", 0, Lifetime::Timeframe); + } + return DataProcessorSpec{ "mft-ca-tracker", inputs, - {}, - AlgorithmSpec{adaptFromTask(ggRequest, useMC)}, + outputs, + AlgorithmSpec{adaptFromTask(ggRequest, useMC, trMode)}, Options{}}; } 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 a42eb6f29f3c0..47b6a0bc89033 100644 --- a/Detectors/ITSMFT/MFT/workflow/src/mft-ca-tracker-workflow.cxx +++ b/Detectors/ITSMFT/MFT/workflow/src/mft-ca-tracker-workflow.cxx @@ -12,32 +12,63 @@ /// @file mft-ca-tracker-workflow.cxx #include +#include #include "CommonUtils/ConfigurableParam.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" +#include "DetectorsRaw/HBFUtilsInitializer.h" +#include "Framework/CallbacksPolicy.h" +#include "Framework/CompletionPolicyHelpers.h" #include "Framework/ConfigParamSpec.h" +#include "ITSMFTTracking/Configuration.h" +#include "ITSMFTTracking/TrackingConfigParam.h" #include "MFTWorkflow/CATrackerSpec.h" +#include "MFTWorkflow/TrackWriterSpec.h" using namespace o2::framework; +void customize(std::vector& policies) +{ + o2::raw::HBFUtilsInitializer::addNewTimeSliceCallback(policies); +} + +void customize(std::vector& policies) +{ + policies.push_back(CompletionPolicyHelpers::consumeWhenAllOrdered(".*(?:MFT|mft).*[W,w]riter.*")); +} + void customize(std::vector& workflowOptions) { workflowOptions.push_back(ConfigParamSpec{"disable-mc", VariantType::Bool, false, {"disable MC labels"}}); + workflowOptions.push_back(ConfigParamSpec{"disable-root-output", VariantType::Bool, false, {"do not write output root files"}}); 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)"}}); + workflowOptions.push_back(ConfigParamSpec{"tracking-mode", VariantType::String, "sync", {"sync,async,cosmics,unset,off"}}); + workflowOptions.push_back(ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings (e.g. MFTCATrackerParam.nIterations=4;MFTAlpideParam.roFrameLengthInBC=594)"}}); + o2::itsmft::DPLAlpideParamInitializer::addMFTConfigOption(workflowOptions); + o2::raw::HBFUtilsInitializer::addConfigOption(workflowOptions); } #include "Framework/runDataProcessing.h" WorkflowSpec defineDataProcessing(ConfigContext const& config) { + std::ignore = o2::itsmft::TrackerParamConfig::Instance(); o2::conf::ConfigurableParam::updateFromString(config.options().get("configKeyValues")); const bool useMC = !config.options().get("disable-mc"); + const bool disableRootOutput = config.options().get("disable-root-output"); const bool useGeom = config.options().get("use-geom"); const bool useIRFrames = config.options().get("use-irframes"); + const auto trMode = o2::itsmft::TrackingMode::fromString(config.options().get("tracking-mode")); WorkflowSpec specs; - specs.emplace_back(o2::mft::getCATrackerSpec(useMC, useGeom, useIRFrames)); + specs.emplace_back(o2::mft::getCATrackerSpec(useMC, useGeom, useIRFrames, trMode)); + if (!disableRootOutput) { + specs.emplace_back(o2::mft::getTrackWriterSpec(useMC)); + } + + o2::raw::HBFUtilsInitializer hbfIni(config, specs); + return specs; } diff --git a/Detectors/ITSMFT/common/CMakeLists.txt b/Detectors/ITSMFT/common/CMakeLists.txt index 5cfc80c16610a..57664249f41f7 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(tracking) add_subdirectory(workflow) 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 b734a310f53e5..ae81520d636c2 100644 --- a/Detectors/ITSMFT/common/tracking/CMakeLists.txt +++ b/Detectors/ITSMFT/common/tracking/CMakeLists.txt @@ -9,13 +9,33 @@ # granted to it by virtue of its status as an Intergovernmental Organization # or submit itself to any jurisdiction. +# Lightweight param registration so MFTCATrackerParam keys in --configKeyValues +# are known to workflows that do not link O2::ITSMFTTracking (e.g. ctf-reader). +o2_add_library(ITSMFTTrackingParams + SOURCES src/TrackingConfigParam.cxx + PUBLIC_LINK_LIBRARIES O2::CommonUtils + O2::DetectorsCommonDataFormats) + +o2_target_root_dictionary(ITSMFTTrackingParams + HEADERS include/ITSMFTTracking/TrackingConfigParam.h + LINKDEF src/ITSMFTTrackingLinkDef.h) + +# Shared CA tracking infrastructure for ITS and MFT. Currently wired from the +# MFT CA tracker workflow via ITSMFTTrackingInterface; ITS production tracking +# remains on O2::ITSTrackingInterface / O2::ITStracking. +# Dependency is one-way (ITSMFTTracking -> ITStracking) to avoid a circular link. o2_add_library(ITSMFTTracking TARGETVARNAME targetName SOURCES src/IOUtils.cxx - src/TrackingConfigParam.cxx + src/DetectorTraits.cxx src/Configuration.cxx src/TimeFrame.cxx + src/TrackerTraits.cxx + src/CATracker.cxx + src/TrackingInterface.cxx + src/MFTForwardRefit.cxx PUBLIC_LINK_LIBRARIES + O2::ITSMFTTrackingParams O2::ITStracking O2::GPUCommon O2::CommonConstants @@ -28,14 +48,12 @@ o2_add_library(ITSMFTTracking Microsoft.GSL::GSL O2::SimulationDataFormat O2::ReconstructionDataFormats + O2::DataFormatsCalibration + O2::DataFormatsMFT + TBB::tbb PRIVATE_LINK_LIBRARIES O2::Framework O2::ITSBase O2::MFTBase + O2::MFTTracking 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/TrackingConfigParam.h - LINKDEF src/ITSMFTTrackingLinkDef.h) diff --git a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/CATrackTypes.h b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/CATrackTypes.h new file mode 100644 index 0000000000000..b82cf371e9c1a --- /dev/null +++ b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/CATrackTypes.h @@ -0,0 +1,37 @@ +// 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 CATrackTypes.h +/// \brief Per-detector CA track storage type +/// + +#ifndef ALICEO2_ITSMFT_TRACKING_CATRACKTYPES_H_ +#define ALICEO2_ITSMFT_TRACKING_CATRACKTYPES_H_ + +#include "DataFormatsITS/TrackITS.h" +#include "ITSMFTTracking/Constants.h" + +namespace o2::itsmft::tracking +{ + +class MFTCATrack; + +template +struct CATrackTypeHelper { + using type = o2::its::TrackITSExt; +}; + +template +using CATrackType = typename CATrackTypeHelper::type; + +} // namespace o2::itsmft::tracking + +#endif /* ALICEO2_ITSMFT_TRACKING_CATRACKTYPES_H_ */ diff --git a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/CATracker.h b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/CATracker.h new file mode 100644 index 0000000000000..7d71b5209a91b --- /dev/null +++ b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/CATracker.h @@ -0,0 +1,77 @@ +// 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 CATracker.h +/// \brief Shared CA tracker orchestrator (same role as ITStracking/Tracker.h) +/// + +#ifndef ALICEO2_ITSMFT_TRACKING_CATRACKER_H_ +#define ALICEO2_ITSMFT_TRACKING_CATRACKER_H_ + +#include +#include + +#include + +#include "ITSMFTTracking/Configuration.h" +#include "ITSMFTTracking/TimeFrame.h" +#include "ITSMFTTracking/TrackerTraits.h" + +namespace o2::itsmft::tracking +{ + +template +class Tracker +{ + public: + using TimeFrameN = TimeFrame; + using TrackerTraitsN = TrackerTraits; + + explicit Tracker(TrackerTraitsN* traits); + + void adoptTimeFrame(TimeFrameN& tf); + void setParameters(const std::vector& p) { mTrkParams = p; } + void setMemoryPool(std::shared_ptr pool) { mMemoryPool = pool; } + void setBz(float bz) { mTraits->setBz(bz); } + void setNThreads(int n, std::shared_ptr& arena) { mTraits->setNThreads(n, arena); } + + /// Run all configured iterations; returns elapsed ms or -1 on failure. + float clustersToTracks(); + + const TimeFrameN& getTimeFrame() const { return *mTimeFrame; } + TimeFrameN& getTimeFrame() { return *mTimeFrame; } + + private: + void initialiseTimeFrame(int iteration) { mTraits->initialiseTimeFrame(iteration); } + void computeTracklets(int iteration, int iVertex) { mTraits->computeLayerTracklets(iteration, iVertex); } + void computeCells(int iteration) { mTraits->computeLayerCells(iteration); } + void findCellsNeighbours(int iteration) { mTraits->findCellsNeighbours(iteration); } + void findRoads(int iteration) { mTraits->findRoads(iteration); } + void rectifyClusterIndices(); + void sortTracks(); + + TrackerTraitsN* mTraits = nullptr; + TimeFrameN* mTimeFrame = nullptr; + std::vector mTrkParams; + std::shared_ptr mMemoryPool; +}; + +template +using CATracker = Tracker; + +using TrackerITS = Tracker; +using TrackerMFT = Tracker; +using CATrackerITS = TrackerITS; +using CATrackerMFT = TrackerMFT; + +} // namespace o2::itsmft::tracking + +#endif /* ALICEO2_ITSMFT_TRACKING_CATRACKER_H_ */ diff --git a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/Configuration.h b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/Configuration.h index 150a364bc1466..8553c7c99c652 100644 --- a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/Configuration.h +++ b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/Configuration.h @@ -20,12 +20,15 @@ #ifndef GPUCA_GPUCODE_DEVICE #include #include +#include #include #endif #include "CommonUtils/EnumFlags.h" #include "DetectorsBase/Propagator.h" +#include "DetectorsCommonDataFormats/DetID.h" #include "ITSMFTTracking/Constants.h" +#include "ITSMFTTracking/LayerMask.h" namespace o2::itsmft { @@ -68,8 +71,8 @@ struct TrackingParameters { 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}; + int ColBins{256}; // ITS: ZBins + int RowBins{128}; // ITS: PhiBins 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}; @@ -77,20 +80,23 @@ struct TrackingParameters { /// General parameters int MinTrackLength = 7; int MaxHoles = 0; - uint16_t HoleLayerMask = 0; + tracking::LayerMask HoleLayerMask = 0; float NSigmaCut = 5; float PVres = 1.e-2f; /// Trackleting cuts float TrackletMinPt = 0.3f; /// Cell finding cuts float CellDeltaTanLambdaSigma = 0.007f; + float CellRoadRCut = 0.05f; // MFT: max distance to seed line (classic ROADclsRCut) + float TrackletMinAbsX = 0.f; // MFT: reject clusters/tracks with |x| below this (cm); 0 = disabled + bool PrintHemisphereStats = false; // MFT debug: log x<0 vs x>0 counts per stage /// 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; + tracking::LayerMask 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; @@ -110,6 +116,25 @@ struct TrackingParameters { int SharedMaxClusters = 0; // Maximal allowed shared clusters (excluding first cluster) }; +/// Reset tracking parameters to detector geometry defaults (ITS: struct defaults; MFT: MFTTracking/Constants.h). +void resetDetectorDefaults(TrackingParameters& params, o2::detectors::DetID::ID detId); + +namespace TrackingMode +{ +enum Type : int8_t { + Unset = -1, + Sync = 0, + Async = 1, + Cosmics = 2, + Off = 3, +}; + +Type fromString(std::string_view str); +std::string toString(Type mode); +std::vector getTrackingParameters(o2::detectors::DetID::ID detId, Type mode); + +} // namespace TrackingMode + struct VertexingParameters { std::string asString() const; diff --git a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/Constants.h b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/Constants.h index db24cd81c26d9..e04338dd401b1 100644 --- a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/Constants.h +++ b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/Constants.h @@ -12,6 +12,17 @@ /// \file Constants.h /// \brief Detector-specific layer counts for shared ITSMFT CA tracking /// +/// MFT CA layer model (see also TimeFrameMFT and MFT CA workflow): +/// - MFTNLayers = 10 half-disk CA layers (same index as GeometryTGeo::getLayer and +/// MFTTracking/Constants.h LayersNumber). This is the single NLayers used by +/// TrackingParameters, TimeFrame, ROFOverlapTable, and cluster sorting. +/// - Five physical disks are addressed as disk = halfLayer / 2 (see mftDiskForHalfLayer). +/// Do not use disk count as CA NLayers. +/// - ROFOverlapTable stores one LayerTiming per half-layer, filled from +/// MFTAlpideParam index L (roFrameLayer*InBC[L] with fallback to global defaults). +/// - Cluster input is one CLUSTERSROF vector per TF; loadROFrameData(..., layer=-1, ...) +/// distributes clusters to half-layers. All layers must then share the same mNROFsTF; +/// per-layer ROF length stagger is unsupported until the workflow provides per-layer ROFs. #ifndef ALICEO2_ITSMFT_TRACKING_INCLUDE_CONSTANTS_H_ #define ALICEO2_ITSMFT_TRACKING_INCLUDE_CONSTANTS_H_ @@ -23,6 +34,7 @@ namespace o2::itsmft::tracking::constants constexpr int ITSNLayers = 7; constexpr int MFTNLayers = 10; +constexpr int MFTDisks = 5; constexpr int MaxIter = 4; // same as o2::its::constants::MaxIter constexpr int nLayersForDet(o2::detectors::DetID::ID detId) @@ -30,6 +42,18 @@ constexpr int nLayersForDet(o2::detectors::DetID::ID detId) return detId == o2::detectors::DetID::MFT ? MFTNLayers : ITSNLayers; } +/// Physical disk index (0..MFTDisks-1) for half-layer L in [0, MFTNLayers). +constexpr int mftDiskForHalfLayer(int halfLayer) +{ + return halfLayer / 2; +} + +template +constexpr o2::detectors::DetID::ID detIdFromNLayers() +{ + return NLayers == MFTNLayers ? o2::detectors::DetID::MFT : o2::detectors::DetID::ITS; +} + } // namespace o2::itsmft::tracking::constants #endif /* ALICEO2_ITSMFT_TRACKING_INCLUDE_CONSTANTS_H_ */ diff --git a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/DetectorTraits.h b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/DetectorTraits.h new file mode 100644 index 0000000000000..500127579a012 --- /dev/null +++ b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/DetectorTraits.h @@ -0,0 +1,152 @@ +// 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 DetectorTraits.h +/// \brief Minimal detector-specific hooks for shared CA tracking (ITS vs MFT) +/// + +#ifndef ALICEO2_ITSMFT_TRACKING_DETECTOR_TRAITS_H_ +#define ALICEO2_ITSMFT_TRACKING_DETECTOR_TRAITS_H_ + +#include "DetectorsBase/Propagator.h" +#include "DetectorsCommonDataFormats/DetID.h" +#include "DataFormatsCalibration/MeanVertexObject.h" +#include "ITSMFTTracking/CATrackTypes.h" +#include "ITSMFTTracking/Configuration.h" +#include "ITSMFTTracking/Constants.h" +#include "ITSMFTTracking/IndexTableUtils.h" +#include "ITSMFTTracking/TimeFrame.h" +#include "ITStracking/BoundedAllocator.h" +#include "ITStracking/Cluster.h" +#include "ITStracking/Tracklet.h" +#include "MFTTracking/Constants.h" + +#include +#include + +namespace o2::itsmft::tracking +{ + +namespace detail +{ +inline float mftDistanceToSeedSquared(const o2::its::Cluster& c1, const o2::its::Cluster& c2, const o2::its::Cluster& c) +{ + const float dxSeed = c2.xCoordinate - c1.xCoordinate; + const float dySeed = c2.yCoordinate - c1.yCoordinate; + const float dzSeed = c2.zCoordinate - c1.zCoordinate; + if (std::abs(dzSeed) < 1e-9f) { + return std::numeric_limits::max(); + } + const float invdzSeed = (c.zCoordinate - c1.zCoordinate) / dzSeed; + const float xSeed = c1.xCoordinate + dxSeed * invdzSeed; + const float ySeed = c1.yCoordinate + dySeed * invdzSeed; + const float dx = c.xCoordinate - xSeed; + const float dy = c.yCoordinate - ySeed; + return dx * dx + dy * dy; +} + +inline float mftLayerZ(int layer) +{ + return o2::mft::constants::mft::LayerZCoordinate()[layer]; +} + +inline bool mftPassesMinAbsX(const o2::its::Cluster& cluster, float minAbsX) +{ + return minAbsX <= 0.f || std::abs(cluster.xCoordinate) >= minAbsX; +} + +/// Expected cluster z on toLayer for a straight line from the vertex through cluster on fromLayer. +inline float mftExpectedZAtLayer(float clusterZ, int fromLayer, int toLayer, float pvZ) +{ + const float zFrom = mftLayerZ(fromLayer); + const float dzLayers = mftLayerZ(toLayer) - zFrom; + const float denom = zFrom - pvZ; + if (std::abs(denom) < 1e-6f) { + return clusterZ + dzLayers; + } + return clusterZ + dzLayers * ((clusterZ - pvZ) / denom); +} + +/// ITS-style z-resolution estimate with layer-to-layer dz instead of delta-R (MFT forward disks). +inline float mftTrackletSigmaZ(float clusterZ, + float clusterRadius, + float pvZ, + float resolution, + float meanDeltaZ, + float msAngle) +{ + const float inverseR0 = 1.f / clusterRadius; + const float tanLambda = (clusterZ - pvZ) * inverseR0; + const float dz0 = clusterZ - pvZ; + const float sqInvDeltaZ0 = 1.f / (dz0 * dz0 + o2::its::constants::Tolerance); + return std::sqrt((resolution * resolution * tanLambda * tanLambda * + ((inverseR0 * inverseR0 + sqInvDeltaZ0) * meanDeltaZ * meanDeltaZ + 1.f)) + + (meanDeltaZ * msAngle) * (meanDeltaZ * msAngle)); +} + +/// Cone-projected transverse distance squared (replaces phi-difference cut for MFT). +template +inline float mftTrackletTransverseDist2(const o2::its::Cluster& current, + const o2::its::Cluster& next, + int fromLayer, + int toLayer) +{ + float xProj = 0.f; + float yProj = 0.f; + mftConeProject(current, fromLayer, toLayer, xProj, yProj); + const float dx = next.xCoordinate - xProj; + const float dy = next.yCoordinate - yProj; + return dx * dx + dy * dy; +} +} // namespace detail + +/// Per-detector differences in refit, track acceptance, and index-table setup. +/// Everything else stays in TrackerTraits and matches ITS line-for-line. +template +struct DetectorTraits { + using TrackType = CATrackType; + using TrackSeedN = TrackSeed; + using TimeFrameN = TimeFrame; + static constexpr o2::detectors::DetID::ID DetId = constants::detIdFromNLayers(); + + static bool refitSeed(const TrackSeedN& seed, + TrackType& track, + const TrackingParameters& params, + float bz, + TimeFrameN& tf, + const o2::its::TrackingFrameInfo* const tfInfos[NLayers], + const o2::its::Cluster* const unsortedClusters[NLayers], + const o2::base::PropagatorImpl* propagator); + + static void sortRefittedTracks(bounded_vector& tracks); + static void finalizeAcceptedTrack(TrackType& track); + static bool sameTrackSign(const TrackType& t1, const TrackType& t2); + + static void configureIndexTableUtils(IndexTableUtils& utils, const TrackingParameters& params); + + static bool validateMFTCellClusters(const o2::its::Cluster& c0, const o2::its::Cluster& c1, const o2::its::Cluster& c2, float r2Cut); + static bool mftCellsConnect(const o2::its::Cluster& cEnd, const o2::its::Cluster& cStart, float r2Cut); +}; + +template +struct TrackingLoadPolicy { + static void configureBeamPosition(TimeFrame& tf, + const TrackingParameters& p, + const o2::dataformats::MeanVertexObject* meanVertex, + bool overrideBeamEstimation); +}; + +template +using TrackingLoadPolicyN = TrackingLoadPolicy(), NLayers>; + +} // namespace o2::itsmft::tracking + +#endif /* ALICEO2_ITSMFT_TRACKING_DETECTOR_TRAITS_H_ */ diff --git a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/IndexTableUtils.h b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/IndexTableUtils.h index acfdb7c066982..9d9a2c859a670 100644 --- a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/IndexTableUtils.h +++ b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/IndexTableUtils.h @@ -22,6 +22,8 @@ #include "GPUCommonMath.h" #include "GPUCommonDef.h" #include "ITSMFTTracking/Configuration.h" +#include "ITStracking/Cluster.h" +#include "MFTTracking/Constants.h" namespace o2::itsmft { @@ -197,6 +199,32 @@ GPUhdi() int4 getBinsXY(float x, float y, const int layerIndex, utils.getRowBinIndex(rowRangeMax)}; } +/// MFT cone projection from one half-layer to another (used for x-y index-table lookup only). +template +GPUhdi() void mftConeProject(const o2::its::Cluster& cluster, int fromLayer, int toLayer, float& xProj, float& yProj) +{ + const auto& layerZ = o2::mft::constants::mft::LayerZCoordinate(); + const auto& invLayerZ = o2::mft::constants::mft::InverseLayerZCoordinate(); + const float scale = (layerZ[toLayer] - layerZ[fromLayer]) * invLayerZ[fromLayer]; + xProj = cluster.xCoordinate * (1.f + scale); + yProj = cluster.yCoordinate * (1.f + scale); +} + +/// Cluster-driven LUT window: phi-z for ITS, cone-projected x-y for MFT. +template +GPUhdi() int4 getBinsRectCluster(const o2::its::Cluster& cluster, int fromLayer, int toLayer, + float colRangeMin, float colRangeMax, float maxDeltaCol, float maxDeltaRow, + const IndexTableUtils& utils) +{ + if (utils.getCoordType() == IndexTableCoordType::XY) { + float xProj = 0.f; + float yProj = 0.f; + mftConeProject(cluster, fromLayer, toLayer, xProj, yProj); + return getBinsXY(xProj, yProj, toLayer, xProj, xProj, maxDeltaCol, maxDeltaRow, utils); + } + return getBinsPhiZ(cluster.phi, toLayer, colRangeMin, colRangeMax, maxDeltaCol, maxDeltaRow, utils); +} + } // namespace o2::itsmft #endif /* ALICEO2_ITSMFT_TRACKING_INDEXTABLEUTILS_H_ */ diff --git a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/MFTCATrack.h b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/MFTCATrack.h new file mode 100644 index 0000000000000..a4420b2e3d8e3 --- /dev/null +++ b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/MFTCATrack.h @@ -0,0 +1,152 @@ +// 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 MFTCATrack.h +/// \brief MFT CA track with native forward parameters and layer-indexed cluster refs +/// + +#ifndef ALICEO2_ITSMFT_TRACKING_MFTCATRACK_H_ +#define ALICEO2_ITSMFT_TRACKING_MFTCATRACK_H_ + +#include +#include + +#include "DataFormatsITS/TimeEstBC.h" +#include "DataFormatsITS/TrackITS.h" +#include "DataFormatsMFT/TrackMFT.h" +#include "ITSMFTTracking/CATrackTypes.h" +#include "ITStracking/Constants.h" + +namespace o2::itsmft::tracking +{ + +/// MFT CA output track: forward fit parameters plus ITS-style cluster indexing for CA bookkeeping. +class MFTCATrack +{ + enum UserBits { + kSharedClusters = 1 << 28 + }; + + public: + static constexpr int MaxClusters = o2::its::TrackITSExt::MaxClusters; + + MFTCATrack() + { + mIndex.fill(o2::its::constants::UnusedIndex); + } + + const o2::mft::TrackMFT& getTrack() const { return mTrack; } + o2::mft::TrackMFT& getTrack() { return mTrack; } + + float getChi2() const { return static_cast(mTrack.getTrackChi2()); } + float getPhi() const { return static_cast(mTrack.getPhi()); } + float getEta() const { return static_cast(mTrack.getEta()); } + double getCharge() const { return mTrack.getCharge(); } + + int getNumberOfClusters() const + { + int n{0}; + for (int layer = 0; layer < MaxClusters; ++layer) { + if (hasHitOnLayer(layer)) { + ++n; + } + } + return n; + } + + uint32_t getPattern() const { return mPattern; } + void setPattern(uint32_t p) { mPattern = p; } + bool hasHitOnLayer(uint32_t layer) const { return mPattern & (0x1u << layer); } + + uint32_t getFirstClusterLayer() const + { + uint32_t s{0}; + while (!(mPattern & (1u << s)) && s < MaxClusters) { + ++s; + } + return s; + } + + uint32_t getLastClusterLayer() const + { + uint32_t r{0}, v{mPattern & ((1u << MaxClusters) - 1)}; + while (v >>= 1) { + ++r; + } + return r; + } + + int getClusterIndex(int layer) const { return mIndex[layer]; } + + int getFirstLayerClusterIndex() const { return getClusterIndex(getFirstClusterLayer()); } + + void setClusterIndex(int layer, int idx) + { + mIndex[layer] = idx; + if (idx != o2::its::constants::UnusedIndex) { + mPattern |= 0x1u << layer; + } + } + + void setExternalClusterIndex(int layer, int idx, bool newCluster = false) + { + if (newCluster) { + mPattern |= 0x1u << layer; + } + mIndex[layer] = idx; + } + + void setClusterSize(int layer, int size) + { + if (layer >= 10) { + return; + } + if (size > 63) { + size = 63; + } + mClusterSizes &= ~(0x3fULL << (layer * 6)); + mClusterSizes |= (static_cast(size) << (layer * 6)); + } + + int getClusterSize(int layer) const + { + if (layer >= 10) { + return 0; + } + return (mClusterSizes >> (layer * 6)) & 0x3f; + } + + auto& getTimeStamp() { return mTime; } + const auto& getTimeStamp() const { return mTime; } + + void setSharedClusters(bool toggle = true) + { + mClusterSizes = toggle ? (mClusterSizes | kSharedClusters) : (mClusterSizes & ~kSharedClusters); + } + + bool hasSharedClusters() const { return mClusterSizes & kSharedClusters; } + + private: + o2::mft::TrackMFT mTrack; + std::array mIndex = {}; + uint32_t mPattern = 0; + o2::its::TimeStamp mTime; + uint64_t mClusterSizes = 0; +}; + +template <> +struct CATrackTypeHelper { + using type = MFTCATrack; +}; + +} // namespace o2::itsmft::tracking + +#endif /* ALICEO2_ITSMFT_TRACKING_MFTCATRACK_H_ */ diff --git a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/MFTForwardRefit.h b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/MFTForwardRefit.h new file mode 100644 index 0000000000000..121875df05e1f --- /dev/null +++ b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/MFTForwardRefit.h @@ -0,0 +1,36 @@ +// 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 MFTForwardRefit.h +/// \brief MFT-specific forward Kalman refit for CA track seeds +/// + +#ifndef ALICEO2_ITSMFT_TRACKING_MFTFORWARDREFIT_H_ +#define ALICEO2_ITSMFT_TRACKING_MFTFORWARDREFIT_H_ + +#include "ITSMFTTracking/Cell.h" +#include "ITSMFTTracking/Configuration.h" +#include "ITSMFTTracking/Constants.h" +#include "ITSMFTTracking/MFTCATrack.h" +#include "ITSMFTTracking/TimeFrame.h" + +namespace o2::itsmft::tracking +{ + +bool refitTrackFwd(const TrackSeed& seed, + MFTCATrack& track, + const TimeFrame& tf, + const TrackingParameters& params, + float bz); + +} // namespace o2::itsmft::tracking + +#endif /* ALICEO2_ITSMFT_TRACKING_MFTFORWARDREFIT_H_ */ diff --git a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/TimeFrame.h b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/TimeFrame.h index fe0ad66e7f65a..ff7455228b44c 100644 --- a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/TimeFrame.h +++ b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/TimeFrame.h @@ -31,6 +31,8 @@ #include "ITStracking/ROFLookupTables.h" #include "ITStracking/Tracklet.h" +#include "ITSMFTTracking/CATrackTypes.h" +#include "ITSMFTTracking/MFTCATrack.h" #include "ITSMFTTracking/Configuration.h" #include "ITSMFTTracking/Constants.h" #include "ITSMFTTracking/IndexTableUtils.h" @@ -219,6 +221,7 @@ struct TimeFrame { auto& getClusters() { return mClusters; } auto& getUnsortedClusters() { return mUnsortedClusters; } + const auto& getUnsortedClusters() const { return mUnsortedClusters; } int getClusterROF(int iLayer, int iCluster); auto& getCells() { return mCells; } @@ -227,7 +230,9 @@ struct TimeFrame { auto& getCellsNeighboursTopology() { return mCellsNeighboursTopology; } auto& getCellsNeighboursLUT() { return mCellsNeighboursLUT; } auto& getTracks() { return mTracks; } + const auto& getTracks() const { return mTracks; } auto& getTracksLabel() { return mTracksLabel; } + const auto& getTracksLabel() const { return mTracksLabel; } auto& getLinesLabel(const int rofId) { return mLinesLabels[rofId]; } size_t getNumberOfClusters() const; @@ -303,7 +308,7 @@ struct TimeFrame { std::array, NLayers> mUnsortedClusters; std::vector> mTracklets; std::vector> mCells; - bounded_vector mTracks; + bounded_vector> mTracks; bounded_vector mTracksLabel; std::vector> mCellsNeighbours; std::vector> mCellsNeighboursTopology; @@ -630,7 +635,14 @@ inline size_t TimeFrame::getNumberOfUsedClusters() const return nClusters; } +template +inline const TrackingFrameInfo& TimeFrame::getClusterTrackingFrameInfo(int layerId, const Cluster& cl) const +{ + return mTrackingFrameInfo[layerId][cl.clusterId]; +} + using TimeFrameITS = TimeFrame; +/// MFT CA TimeFrame: NLayers = MFTNLayers half-disks; see ITSMFTTracking/Constants.h. using TimeFrameMFT = TimeFrame; } // namespace itsmft::tracking diff --git a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/Tracker.h b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/Tracker.h new file mode 100644 index 0000000000000..ff1b3035050e5 --- /dev/null +++ b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/Tracker.h @@ -0,0 +1,21 @@ +// 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 Tracker.h +/// \brief ITS-style alias for the shared CA tracker orchestrator +/// + +#ifndef ALICEO2_ITSMFT_TRACKING_TRACKER_H_ +#define ALICEO2_ITSMFT_TRACKING_TRACKER_H_ + +#include "ITSMFTTracking/CATracker.h" + +#endif /* ALICEO2_ITSMFT_TRACKING_TRACKER_H_ */ diff --git a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/TrackerTraits.h b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/TrackerTraits.h new file mode 100644 index 0000000000000..b557ddcf59e55 --- /dev/null +++ b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/TrackerTraits.h @@ -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 TrackerTraits.h +/// \brief Shared CA tracker traits: same ITS-style tracklet/cell/road logic; MFT uses x-y LUT and forward refit +/// + +#ifndef ALICEO2_ITSMFT_TRACKING_TRACKERTRAITS_H_ +#define ALICEO2_ITSMFT_TRACKING_TRACKERTRAITS_H_ + +#include + +#include "ITSMFTTracking/CATrackTypes.h" +#include "ITSMFTTracking/Configuration.h" +#include "ITSMFTTracking/TimeFrame.h" +#include "ITStracking/BoundedAllocator.h" + +namespace o2::itsmft::tracking +{ + +template +class TrackerTraits +{ + public: + using TimeFrameN = TimeFrame; + using IndexTableUtilsN = o2::itsmft::IndexTableUtils; + using TrackSeedN = TrackSeed; + + virtual ~TrackerTraits() = default; + virtual void adoptTimeFrame(TimeFrameN* tf) { mTimeFrame = tf; } + virtual void initialiseTimeFrame(const int iteration) + { + mTimeFrame->initialise(mTrkParams[iteration], mTrkParams[iteration].NLayers, iteration); + } + + virtual void computeLayerTracklets(const int iteration, int iVertex); + virtual void computeLayerCells(const int iteration); + virtual void findCellsNeighbours(const int iteration); + virtual void findRoads(const int iteration); + + template + void processNeighbours(int iteration, int defaultCellTopologyId, int iLevel, const bounded_vector& currentCellSeed, const bounded_vector& currentCellId, const bounded_vector& currentCellTopologyId, bounded_vector& updatedCellSeed, bounded_vector& updatedCellId, bounded_vector& updatedCellTopologyId); + + void acceptTracks(int iteration, bounded_vector>& tracks, bounded_vector>& firstClusters); + void markTracks(int iteration); + + void updateTrackingParameters(const std::vector& trkPars) { mTrkParams = trkPars; } + TimeFrameN* getTimeFrame() { return mTimeFrame; } + + virtual void setBz(float bz); + float getBz() const { return mBz; } + virtual const char* getName() const noexcept { return "CPU"; } + virtual bool isGPU() const noexcept { return false; } + void setMemoryPool(std::shared_ptr pool) noexcept { mMemoryPool = pool; } + auto getMemoryPool() const noexcept { return mMemoryPool; } + + void setNThreads(int n, std::shared_ptr& arena); + int getNThreads() { return mTaskArena->max_concurrency(); } + + int getTFNumberOfClusters() const { return mTimeFrame->getNumberOfClusters(); } + int getTFNumberOfTracklets() const { return mTimeFrame->getNumberOfTracklets(); } + int getTFNumberOfCells() const { return mTimeFrame->getNumberOfCells(); } + + private: + std::shared_ptr mMemoryPool; + std::shared_ptr mTaskArena; + + protected: + TimeFrameN* mTimeFrame = nullptr; + std::vector mTrkParams; + float mBz{-999.f}; +}; + +using TrackerTraitsITS = TrackerTraits; +using TrackerTraitsMFT = TrackerTraits; + +} // namespace o2::itsmft::tracking + +#endif /* ALICEO2_ITSMFT_TRACKING_TRACKERTRAITS_H_ */ diff --git a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/TrackingConfigParam.h b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/TrackingConfigParam.h index 119a5cbccbfb7..bfb1e134af1ca 100644 --- a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/TrackingConfigParam.h +++ b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/TrackingConfigParam.h @@ -23,7 +23,8 @@ namespace o2::itsmft { -/// ITS vertexer settings (not used for MFT) +/// ITS vertexer settings (header only for now; not registered or used while ITS +/// tracking stays on O2::ITStracking) struct VertexerParamConfig : public o2::conf::ConfigurableParamHelper { bool saveTimeBenchmarks = false; // dump metrics on file @@ -98,16 +99,16 @@ struct TrackerParamConfig : public o2::conf::ConfigurableParamHelper0 counts per CA stage O2ParamDef(TrackerParamConfig, getParamName().data()); private: static constexpr std::string_view TrackerParamName[2] = {"ITSCATrackerParam", "MFTCATrackerParam"}; + + static_assert(N == o2::detectors::DetID::ITS || N == o2::detectors::DetID::MFT, "only DetID::ITS or DetID::MFT are allowed"); }; template diff --git a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/TrackingInterface.h b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/TrackingInterface.h new file mode 100644 index 0000000000000..969d726e94873 --- /dev/null +++ b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/TrackingInterface.h @@ -0,0 +1,124 @@ +// 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 TrackingInterface.h +/// \brief Shared DPL-facing CA tracking interface for ITS and MFT +/// + +#ifndef ALICEO2_ITSMFT_TRACKING_TRACKINGINTERFACE_H_ +#define ALICEO2_ITSMFT_TRACKING_TRACKINGINTERFACE_H_ + +#include +#include +#include + +#include + +#include "CommonDataFormat/IRFrame.h" +#include "DataFormatsCalibration/MeanVertexObject.h" +#include "DataFormatsITSMFT/CompCluster.h" +#include "DataFormatsITSMFT/ROFRecord.h" +#include "DataFormatsITSMFT/TopologyDictionary.h" +#include "DetectorsCommonDataFormats/DetID.h" +#include "ITSMFTTracking/DetectorTraits.h" +#include "ITSMFTTracking/Tracker.h" +#include "ITSMFTTracking/Configuration.h" +#include "ITSMFTTracking/Constants.h" +#include "ITSMFTTracking/TimeFrame.h" +#include "ITSMFTTracking/TrackerTraits.h" +#include "ITStracking/BoundedAllocator.h" +#include "ITStracking/ROFLookupTables.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "SimulationDataFormat/MCTruthContainer.h" + +namespace o2::itsmft::tracking +{ + +template +class ITSMFTTrackingInterface +{ + public: + static_assert(NLayers == constants::ITSNLayers || NLayers == constants::MFTNLayers, + "ITSMFTTrackingInterface supports ITS (7) and MFT (10) layer counts only"); + static constexpr o2::detectors::DetID::ID DetId = constants::detIdFromNLayers(); + + using TimeFrameN = TimeFrame; + using TrackerN = Tracker; + using TrackerTraitsN = TrackerTraits; + using ROFOverlapTableN = o2::its::ROFOverlapTable; + using ROFVertexLookupTableN = o2::its::ROFVertexLookupTable; + using ROFMaskTableN = o2::its::ROFMaskTable; + using BoundedMemoryResourceN = BoundedMemoryResource; + + ITSMFTTrackingInterface(bool useMC, o2::itsmft::TrackingMode::Type mode, bool overrideBeamEst); + + void setTrackingMode(o2::itsmft::TrackingMode::Type mode) { mTrackingMode = mode; } + void setClusterDictionary(const o2::itsmft::TopologyDictionary* dict) { mDict = dict; } + void setMeanVertex(const o2::dataformats::MeanVertexObject* v) { mMeanVertex = v; } + + void initialise(); + + /// Phase 2 (TimeFrame load) + Phase 3 (CA tracking). Returns tracker elapsed ms or -1 on failure. + /// Does not wipe the TimeFrame; call clearTimeFrame() after outputs are extracted. + float processTimeFrame(gsl::span rofs, + gsl::span clusters, + gsl::span patterns, + const o2::dataformats::MCTruthContainer* labels, + gsl::span irFrames = {}); + + void clearTimeFrame() { mTimeFrame.wipe(); } + + TimeFrameN& getTimeFrame() { return mTimeFrame; } + const TimeFrameN& getTimeFrame() const { return mTimeFrame; } + const std::vector& getTrackingParameters() const { return mTrackParams; } + bool isActive() const { return !mTrackParams.empty(); } + + protected: + virtual void onTimeFrameLoaded() {} + virtual void onTrackingFinished(float elapsedMs) {} + + private: + void resolveTrackingParameters(); + void initialiseMemoryPool(); + void initialiseTracker(); + void loadTimeFrame(gsl::span rofs, + gsl::span clusters, + gsl::span patterns, + const o2::dataformats::MCTruthContainer* labels, + gsl::span irFrames); + float runTracking(); + void configureROFLookupTables(); + void configureBeamPosition(); + void configureTrackingTopology(); + void configureROFMask(gsl::span rofs, + gsl::span irFrames); + void validateROFInput(gsl::span rofs) const; + + bool mUseMC = false; + bool mOverrideBeamEstimation = false; + o2::itsmft::TrackingMode::Type mTrackingMode = o2::itsmft::TrackingMode::Unset; + std::vector mTrackParams; + std::shared_ptr mMemoryPool; + std::unique_ptr mTrackerTraits; + std::unique_ptr mTracker; + const o2::itsmft::TopologyDictionary* mDict = nullptr; + const o2::dataformats::MeanVertexObject* mMeanVertex = nullptr; + TimeFrameN mTimeFrame; + int mMFTROFrameLengthInBC = 0; + bool mMFTTriggered = false; +}; + +using ITSMFTTrackingInterfaceITS = ITSMFTTrackingInterface; +using ITSMFTTrackingInterfaceMFT = ITSMFTTrackingInterface; + +} // namespace o2::itsmft::tracking + +#endif /* ALICEO2_ITSMFT_TRACKING_TRACKINGINTERFACE_H_ */ diff --git a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/TrackingParamRef.h b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/TrackingParamRef.h new file mode 100644 index 0000000000000..f0d3d4edacc92 --- /dev/null +++ b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/TrackingParamRef.h @@ -0,0 +1,47 @@ +// 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 TrackingParamRef.h +/// \brief Resolve CA tracker configurable params per detector without duplicating ITS registration +/// + +#ifndef ALICEO2_ITSMFT_TRACKING_TRACKINGPARAMREF_H_ +#define ALICEO2_ITSMFT_TRACKING_TRACKINGPARAMREF_H_ + +#include "DetectorsCommonDataFormats/DetID.h" +#include "ITSMFTTracking/Constants.h" +#include "ITSMFTTracking/TrackingConfigParam.h" +#include "ITStracking/TrackingConfigParam.h" + +namespace o2::itsmft::tracking +{ + +/// MFT uses o2::itsmft::TrackerParamConfig; ITS production params stay in O2::ITStracking. +template +struct TrackerParamRef; + +template <> +struct TrackerParamRef { + using Type = o2::itsmft::TrackerParamConfig; + static const Type& get() { return Type::Instance(); } + static constexpr int nLayers() { return Type::getNLayers(); } +}; + +template <> +struct TrackerParamRef { + using Type = o2::its::TrackerParamConfig; + static const Type& get() { return Type::Instance(); } + static constexpr int nLayers() { return constants::ITSNLayers; } +}; + +} // namespace o2::itsmft::tracking + +#endif /* ALICEO2_ITSMFT_TRACKING_TRACKINGPARAMREF_H_ */ diff --git a/Detectors/ITSMFT/common/tracking/src/CATracker.cxx b/Detectors/ITSMFT/common/tracking/src/CATracker.cxx new file mode 100644 index 0000000000000..c2b45d4bc69a3 --- /dev/null +++ b/Detectors/ITSMFT/common/tracking/src/CATracker.cxx @@ -0,0 +1,199 @@ +// 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 CATracker.cxx +/// \brief +/// + +#include "ITSMFTTracking/CATrackTypes.h" +#include "ITSMFTTracking/CATracker.h" + +#include +#include +#include + +#include "Framework/Logger.h" +#include "ITStracking/Constants.h" +#include "ITStracking/BoundedAllocator.h" +#include "MFTTracking/MFTTrackingParam.h" +#include "SimulationDataFormat/MCCompLabel.h" + +namespace o2::itsmft::tracking +{ + +namespace +{ +template +void computeTracksMClabels(TimeFrame& tf) +{ + auto& trackLabels = tf.getTracksLabel(); + trackLabels.clear(); + trackLabels.reserve(tf.getNumberOfTracks()); + + for (auto& track : tf.getTracks()) { + std::vector> occurrences; + for (int iLayer = 0; iLayer < NLayers; ++iLayer) { + const int index = track.getClusterIndex(iLayer); + if (index == constants::UnusedIndex) { + continue; + } + const auto labels = tf.getClusterLabels(iLayer, index); + bool found{false}; + for (auto& occurrence : occurrences) { + for (const auto& label : labels) { + if (label == occurrence.first) { + ++occurrence.second; + found = true; + } + } + } + if (!found) { + for (const auto& label : labels) { + occurrences.emplace_back(label, 1); + } + } + } + + MCCompLabel maxOccurrencesValue; + if (!occurrences.empty()) { + std::sort(occurrences.begin(), occurrences.end(), [](const auto& e1, const auto& e2) { + return e1.second > e2.second; + }); + maxOccurrencesValue = occurrences[0].first; + if constexpr (NLayers == constants::MFTNLayers) { + const float threshold = o2::mft::MFTTrackingParam::Instance().TrueTrackMCThreshold; + if (static_cast(occurrences[0].second) / track.getNumberOfClusters() < threshold) { + maxOccurrencesValue.setFakeFlag(); + } + } else if (occurrences[0].second < static_cast(track.getNumberOfClusters())) { + maxOccurrencesValue.setFakeFlag(); + } + } else { + maxOccurrencesValue.setFakeFlag(); + } + trackLabels.emplace_back(maxOccurrencesValue); + } +} +} // namespace + +template +Tracker::Tracker(TrackerTraits* traits) : mTraits(traits) +{ +} + +template +void Tracker::adoptTimeFrame(TimeFrame& tf) +{ + mTimeFrame = &tf; + mTraits->adoptTimeFrame(&tf); +} + +template +float Tracker::clustersToTracks() +{ + mTraits->updateTrackingParameters(mTrkParams); + + int maxNvertices{-1}; + if (mTrkParams[0].PerPrimaryVertexProcessing) { + maxNvertices = mTimeFrame->getROFVertexLookupTableView().getMaxVerticesPerROF(); + } + + float total{0.f}; + try { + for (int iteration = 0; iteration < static_cast(mTrkParams.size()); ++iteration) { + mMemoryPool->setMaxMemory(mTrkParams[iteration].MaxMemory); + if (mTrkParams[iteration].PassFlags[IterationStep::UseUPCMask]) { + mTimeFrame->useUPCMask(); + } + + int iVertex = std::min(maxNvertices, 0); + initialiseTimeFrame(iteration); + do { + computeTracklets(iteration, iVertex); + computeCells(iteration); + findCellsNeighbours(iteration); + findRoads(iteration); + } while (++iVertex < maxNvertices); + } + } catch (const BoundedMemoryResource::MemoryLimitExceeded& err) { + LOGP(error, "CA tracker exceeded memory limit: {}", err.what()); + if (mTrkParams[0].DropTFUponFailure) { + mTimeFrame->wipe(); + return -1.f; + } + throw; + } catch (const std::exception& err) { + LOGP(error, "CA tracker failed: {}", err.what()); + mTimeFrame->getTracks().clear(); + return -1.f; + } + + if (mTimeFrame->hasMCinformation()) { + computeTracksMClabels(*mTimeFrame); + } + rectifyClusterIndices(); + sortTracks(); + return total; +} + +template +void Tracker::rectifyClusterIndices() +{ + for (auto& track : mTimeFrame->getTracks()) { + for (int iCluster = 0; iCluster < CATrackType::MaxClusters; ++iCluster) { + const int index = track.getClusterIndex(iCluster); + if (index == constants::UnusedIndex) { + continue; + } + track.setExternalClusterIndex(iCluster, mTimeFrame->getClusterExternalIndex(iCluster, index)); + } + } +} + +template +void Tracker::sortTracks() +{ + auto& tracks = mTimeFrame->getTracks(); + bounded_vector indices(tracks.size(), mMemoryPool.get()); + std::iota(indices.begin(), indices.end(), 0); + std::sort(indices.begin(), indices.end(), [&tracks](size_t i, size_t j) { + const auto& a = tracks[i]; + const auto& b = tracks[j]; + const auto aLower = a.getTimeStamp().getTimeStamp() - a.getTimeStamp().getTimeStampError(); + const auto bLower = b.getTimeStamp().getTimeStamp() - b.getTimeStamp().getTimeStampError(); + if (aLower != bLower) { + return aLower < bLower; + } + return a.getChi2() < b.getChi2(); + }); + + bounded_vector> sortedTracks(mMemoryPool.get()); + sortedTracks.reserve(tracks.size()); + for (size_t idx : indices) { + sortedTracks.push_back(tracks[idx]); + } + tracks.swap(sortedTracks); + + if (mTimeFrame->hasMCinformation()) { + auto& trackLabels = mTimeFrame->getTracksLabel(); + bounded_vector sortedLabels(mMemoryPool.get()); + sortedLabels.reserve(trackLabels.size()); + for (size_t idx : indices) { + sortedLabels.push_back(trackLabels[idx]); + } + trackLabels.swap(sortedLabels); + } +} + +template class Tracker<7>; +template class Tracker<10>; + +} // namespace o2::itsmft::tracking diff --git a/Detectors/ITSMFT/common/tracking/src/Configuration.cxx b/Detectors/ITSMFT/common/tracking/src/Configuration.cxx index ce3b3926c5079..d2a330f79a832 100644 --- a/Detectors/ITSMFT/common/tracking/src/Configuration.cxx +++ b/Detectors/ITSMFT/common/tracking/src/Configuration.cxx @@ -10,10 +10,28 @@ // or submit itself to any jurisdiction. #include +#include +#include +#include #include #include +#include +#include +#include "DetectorsBase/Propagator.h" +#include "Framework/Logger.h" #include "ITSMFTTracking/Configuration.h" +#include "ITSMFTTracking/TrackingConfigParam.h" +#include "MFTTracking/Constants.h" + +namespace +{ +constexpr bool iequals(std::string_view a, std::string_view b) +{ + return std::equal(a.begin(), a.end(), b.begin(), b.end(), + [](char x, char y) { return std::tolower(x) == std::tolower(y); }); +} +} // namespace namespace o2::itsmft { @@ -53,7 +71,7 @@ std::string TrackingParameters::asString() const str += std::format(" ShaClsDPhi:{} ShaClsDEta:{} ShaClsSign:{}", SharedClusterMaxDeltaPhi, SharedClusterMaxDeltaEta, SharedClusterOppositeSign); } if (MaxHoles) { - str += std::format(" MaxHoles:{} HoleMask:{:016b}", MaxHoles, HoleLayerMask); + str += std::format(" MaxHoles:{} HoleMask:{}", MaxHoles, HoleLayerMask.asString()); } if (std::numeric_limits::max() != MaxMemory) { str += std::format(" MemLimit {:.2f} GB", double(MaxMemory) / (1024.f * 1024.f * 1024.f)); @@ -71,4 +89,253 @@ std::string VertexingParameters::asString() const return str; } +void resetDetectorDefaults(TrackingParameters& p, detectors::DetID::ID detId) +{ + if (detId == detectors::DetID::ITS) { + p = TrackingParameters{}; + p.MinPt.assign(TrackerParamConfig::MaxTrackLength - TrackerParamConfig::MinTrackLength + 1, 0.f); + return; + } + + if (detId == detectors::DetID::MFT) { + namespace mftc = o2::mft::constants; + namespace mft = mftc::mft; + constexpr int nLayers = tracking::constants::MFTNLayers; + + p = TrackingParameters{}; + p.NLayers = nLayers; + p.LayerZ.clear(); + p.LayerZ.reserve(nLayers); + for (float z : mft::LayerZCoordinate()) { + p.LayerZ.push_back(std::abs(z)); + } + p.LayerColHalfExtent.assign(mftc::index_table::RMax.begin(), mftc::index_table::RMax.end()); + p.IndexRowMin = -20.f; + p.IndexRowMax = 20.f; + p.LayerRadii.resize(nLayers); + for (int i{0}; i < nLayers; ++i) { + p.LayerRadii[i] = 0.5f * (mftc::index_table::RMin[i] + mftc::index_table::RMax[i]); + } + p.LayerxX0.assign(nLayers, 0.042f / mft::DisksNumber); // MFTRadLength / disks + p.LayerResolution.assign(nLayers, mft::Resolution); + p.SystError2Row.assign(nLayers, 0.f); + p.SystError2Col.assign(nLayers, 0.f); + p.AddTimeError.assign(nLayers, 0u); + p.ColBins = 64; + p.RowBins = 128; + p.UseDiamond = true; + p.PerPrimaryVertexProcessing = false; + p.StartLayerMask = (1u << nLayers) - 1u; + p.TrackletMinAbsX = 0.05f; + p.MinPt.assign(TrackerParamConfig::MaxTrackLength - TrackerParamConfig::MinTrackLength + 1, 0.f); + return; + } + + LOGP(fatal, "Unsupported detector id {} in resetDetectorDefaults", static_cast(detId)); +} + +namespace TrackingMode +{ + +Type fromString(std::string_view str) +{ + constexpr std::array smodes = { + std::pair{"sync", Sync}, + std::pair{"async", Async}, + std::pair{"cosmics", Cosmics}, + std::pair{"unset", Unset}, + std::pair{"off", Off}}; + + const auto it = std::find_if(smodes.begin(), smodes.end(), [&str](const auto& pair) { + return iequals(str, pair.first); + }); + if (it == smodes.end()) { + LOGP(fatal, "Unrecognized CA tracking mode '{}'", str); + } + return it->second; +} + +std::string toString(Type mode) +{ + switch (mode) { + case Sync: + return "sync"; + case Async: + return "async"; + case Cosmics: + return "cosmics"; + case Unset: + return "unset"; + case Off: + return "off"; + } + LOGP(fatal, "Unrecognized CA tracking mode {}", static_cast(mode)); + return ""; +} + +std::vector getTrackingParameters(detectors::DetID::ID detId, Type mode) +{ + if (detId == detectors::DetID::ITS) { + LOGP(fatal, "ITS CA tracking via O2::ITSMFTTracking is not enabled yet; use O2::ITStracking"); + } + if (detId != detectors::DetID::MFT) { + LOGP(fatal, "Unsupported detector id {} in getTrackingParameters", static_cast(detId)); + } + + const auto& tc = TrackerParamConfig::Instance(); + std::vector trackParams; + + if (mode == Off) { + return trackParams; + } + if (mode == Unset) { + LOGP(fatal, "CA tracking mode is unset; set --tracking-mode or {}.trackingMode", TrackerParamConfig::getParamName()); + } + + if (mode == Async) { + trackParams.resize(3); + for (auto& param : trackParams) { + resetDetectorDefaults(param, detId); + } + + trackParams[1].TrackletMinPt = 0.15f; + trackParams[1].CellDeltaTanLambdaSigma *= 2.f; + trackParams[2].TrackletMinPt = 0.08f; + trackParams[2].CellDeltaTanLambdaSigma *= 4.f; + + trackParams[0].MinPt[0] = 1.f / 12.f; // 10 clusters + trackParams[1].MinPt[0] = 1.f / 12.f; + + trackParams[2].MinTrackLength = TrackerParamConfig::MinTrackLength; + trackParams[2].MinPt[0] = 1.f / 12.f; // 10 cl + trackParams[2].MinPt[1] = 1.f / 8.f; // 9 cl + trackParams[2].MinPt[2] = 1.f / 5.f; // 8 cl + trackParams[2].MinPt[3] = 1.f / 3.f; // 7 cl + trackParams[2].MinPt[4] = 1.f / 2.f; // 6 cl + trackParams[2].MinPt[5] = 1.f / 1.f; // 5 cl + + for (int ip = 0; ip < static_cast(trackParams.size()); ip++) { + auto& param = trackParams[ip]; + if (ip < tracking::constants::MaxIter) { + if (tc.startLayerMask[ip] > 0) { + param.StartLayerMask = tc.startLayerMask[ip]; + } + if (tc.minTrackLgtIter[ip] > 0) { + param.MinTrackLength = tc.minTrackLgtIter[ip]; + } + for (int ilg = tc.MaxTrackLength; ilg >= tc.MinTrackLength; ilg--) { + const int lslot0 = tc.MaxTrackLength - ilg; + const int lslot = lslot0 + ip * (tc.MaxTrackLength - tc.MinTrackLength + 1); + if (tc.minPtIterLgt[lslot] > 0.f) { + param.MinPt[lslot0] = tc.minPtIterLgt[lslot]; + } + } + } + } + } else if (mode == Sync) { + trackParams.resize(1); + resetDetectorDefaults(trackParams[0], detId); + trackParams[0].MinTrackLength = TrackerParamConfig::MinTrackLength; + } else if (mode == Cosmics) { + trackParams.resize(1); + resetDetectorDefaults(trackParams[0], detId); + trackParams[0].MinTrackLength = TrackerParamConfig::MinTrackLength; + trackParams[0].CellDeltaTanLambdaSigma *= 10.f; + trackParams[0].ColBins = 32; + trackParams[0].RowBins = 64; + trackParams[0].PVres = 1.e5f; + trackParams[0].MaxChi2ClusterAttachment = 60.f; + trackParams[0].MaxChi2NDF = 40.f; + } else { + LOGP(fatal, "Unsupported CA tracking mode {}", toString(mode)); + } + + for (auto& param : trackParams) { + param.PassFlags.reset(); + } + if (!trackParams.empty()) { + trackParams[0].PassFlags.set(IterationStep::FirstPass, IterationStep::RebuildClusterLUT); + } + + const float bFactor = std::abs(o2::base::Propagator::Instance()->getNominalBz()) / 5.0066791f; + const float bFactorTracklets = bFactor < 0.01f ? 1.f : bFactor; + + for (auto& p : trackParams) { + p.TrackletMinPt *= bFactorTracklets; + for (int ilg = tc.MaxTrackLength; ilg >= tc.MinTrackLength; ilg--) { + const int lslot = tc.MaxTrackLength - ilg; + if (lslot < static_cast(p.MinPt.size())) { + p.MinPt[lslot] *= bFactor; + } + } + + p.ReseedIfShorter = tc.reseedIfShorter; + p.RepeatRefitOut = tc.repeatRefitOut; + p.ShiftRefToCluster = tc.shiftRefToCluster; + p.CreateArtefactLabels = tc.createArtefactLabels; + p.PrintMemory = tc.printMemory; + p.MaxMemory = tc.maxMemory; + p.DropTFUponFailure = tc.dropTFUponFailure; + p.SaveTimeBenchmarks = tc.saveTimeBenchmarks; + p.FataliseUponFailure = tc.fataliseUponFailure; + p.AllowSharingFirstCluster = tc.allowSharingFirstCluster; + p.SharedClusterMaxDeltaPhi = tc.sharedClusterMaxDeltaPhi; + p.SharedClusterMaxDeltaEta = tc.sharedClusterMaxDeltaEta; + p.SharedClusterOppositeSign = tc.sharedClusterOppositeSign; + p.PerPrimaryVertexProcessing = tc.perPrimaryVertexProcessing; + + const auto iter = &p - trackParams.data(); + if (iter < tracking::constants::MaxIter) { + p.MaxHoles = tc.maxHolesIter[iter]; + p.HoleLayerMask = tc.holeLayerMaskIter[iter]; + } + + if (tc.useMatCorrTGeo) { + p.CorrType = o2::base::PropagatorImpl::MatCorrType::USEMatCorrTGeo; + } else if (tc.useFastMaterial) { + p.CorrType = o2::base::PropagatorImpl::MatCorrType::USEMatCorrNONE; + } else { + p.CorrType = o2::base::PropagatorImpl::MatCorrType::USEMatCorrLUT; + } + + for (int i{0}; i < TrackerParamConfig::getNLayers(); ++i) { + p.SystError2Row[i] = tc.sysErr2Row[i] > 0 ? tc.sysErr2Row[i] : p.SystError2Row[i]; + p.SystError2Col[i] = tc.sysErr2Col[i] > 0 ? tc.sysErr2Col[i] : p.SystError2Col[i]; + p.AddTimeError[i] = tc.addTimeError[i]; + } + + p.MaxChi2ClusterAttachment = tc.maxChi2ClusterAttachment > 0 ? tc.maxChi2ClusterAttachment : p.MaxChi2ClusterAttachment; + p.MaxChi2NDF = tc.maxChi2NDF > 0 ? tc.maxChi2NDF : p.MaxChi2NDF; + p.ColBins = tc.LUTbinsU > 0 ? tc.LUTbinsU : p.ColBins; + p.RowBins = tc.LUTbinsV > 0 ? tc.LUTbinsV : p.RowBins; + p.PVres = tc.pvRes > 0 ? tc.pvRes : p.PVres; + p.NSigmaCut *= tc.nSigmaCut > 0 ? tc.nSigmaCut : 1.f; + p.CellDeltaTanLambdaSigma *= tc.deltaTanLres > 0 ? tc.deltaTanLres : 1.f; + p.TrackletMinPt *= tc.minPt > 0 ? tc.minPt : 1.f; + if (tc.cellRoadRCut > 0.f) { + p.CellRoadRCut = tc.cellRoadRCut; + } + if (tc.trackletMinAbsX >= 0.f) { + p.TrackletMinAbsX = tc.trackletMinAbsX; + } + p.PrintHemisphereStats = tc.printHemisphereStats; + for (int iD{0}; iD < 3; ++iD) { + p.Diamond[iD] = tc.diamondPos[iD]; + } + if (detId == detectors::DetID::MFT) { + p.UseDiamond = true; + p.PerPrimaryVertexProcessing = false; + } else { + p.UseDiamond = tc.useDiamond; + } + } + + if (trackParams.size() > static_cast(tc.nIterations)) { + trackParams.resize(tc.nIterations); + } + + return trackParams; +} + +} // namespace TrackingMode } // namespace o2::itsmft diff --git a/Detectors/ITSMFT/common/tracking/src/DetectorTraits.cxx b/Detectors/ITSMFT/common/tracking/src/DetectorTraits.cxx new file mode 100644 index 0000000000000..ec0494f781492 --- /dev/null +++ b/Detectors/ITSMFT/common/tracking/src/DetectorTraits.cxx @@ -0,0 +1,174 @@ +// 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 DetectorTraits.cxx +/// \brief Detector-specific refit and load hooks for shared CA tracking +/// + +#include "ITSMFTTracking/DetectorTraits.h" + +#include + +#include "Framework/Logger.h" +#include "ITSMFTTracking/MFTForwardRefit.h" +#include "ITSMFTTracking/TrackingParamRef.h" +#include "ITStracking/TrackHelpers.h" + +namespace o2::itsmft::tracking +{ + +namespace +{ +template +bool refitSeedITS(const TrackSeed& seed, + o2::its::TrackITSExt& track, + const TrackingParameters& params, + float bz, + const o2::its::TrackingFrameInfo* const tfInfos[NLayers], + const o2::its::Cluster* const unsortedClusters[NLayers], + const o2::base::PropagatorImpl* propagator) +{ + // Common TrackSeed matches o2::its::TrackSeed layout (hole-layer mask aside); refit uses ITS helper. + const auto& itsSeed = reinterpret_cast&>(seed); + return o2::its::track::refitTrack(itsSeed, + track, + params.MaxChi2ClusterAttachment, + params.MaxChi2NDF, + bz, + tfInfos, + unsortedClusters, + params.LayerxX0.data(), + params.LayerRadii.data(), + params.MinPt.data(), + propagator, + params.CorrType, + params.ReseedIfShorter, + params.ShiftRefToCluster, + params.RepeatRefitOut); +} +} // namespace + +template +bool DetectorTraits::refitSeed(const TrackSeedN& seed, + TrackType& track, + const TrackingParameters& params, + float bz, + TimeFrameN& tf, + const o2::its::TrackingFrameInfo* const tfInfos[NLayers], + const o2::its::Cluster* const unsortedClusters[NLayers], + const o2::base::PropagatorImpl* propagator) +{ + if constexpr (DetId == o2::detectors::DetID::MFT) { + return refitTrackFwd(seed, track, tf, params, bz); + } else { + return refitSeedITS(seed, track, params, bz, tfInfos, unsortedClusters, propagator); + } +} + +template +void DetectorTraits::sortRefittedTracks(bounded_vector& tracks) +{ + // Same ordering as o2::its::track::isBetter (longer track, then lower chi2). + std::sort(tracks.begin(), tracks.end(), [](const TrackType& a, const TrackType& b) { + const auto ncla = a.getNumberOfClusters(); + const auto nclb = b.getNumberOfClusters(); + return (ncla == nclb) ? (a.getChi2() < b.getChi2()) : ncla > nclb; + }); +} + +template +void DetectorTraits::finalizeAcceptedTrack(TrackType& track) +{ + if constexpr (DetId == o2::detectors::DetID::ITS) { + track.setUserField(0); + track.getParamOut().setUserField(0); + } +} + +template +bool DetectorTraits::sameTrackSign(const TrackType& t1, const TrackType& t2) +{ + if constexpr (DetId == o2::detectors::DetID::MFT) { + return t1.getCharge() == t2.getCharge(); + } else { + return t1.getSign() == t2.getSign(); + } +} + +template +bool DetectorTraits::validateMFTCellClusters(const o2::its::Cluster& c0, const o2::its::Cluster& c1, const o2::its::Cluster& c2, float r2Cut) +{ + if constexpr (DetId == o2::detectors::DetID::MFT) { + return detail::mftDistanceToSeedSquared(c0, c2, c1) < r2Cut && + detail::mftDistanceToSeedSquared(c0, c1, c2) < r2Cut && + detail::mftDistanceToSeedSquared(c1, c2, c0) < r2Cut; + } else { + return false; + } +} + +template +bool DetectorTraits::mftCellsConnect(const o2::its::Cluster& cEnd, const o2::its::Cluster& cStart, float r2Cut) +{ + if constexpr (DetId == o2::detectors::DetID::MFT) { + const float dx = cEnd.xCoordinate - cStart.xCoordinate; + const float dy = cEnd.yCoordinate - cStart.yCoordinate; + return dx * dx + dy * dy <= r2Cut; + } else { + return false; + } +} + +template +void DetectorTraits::configureIndexTableUtils(IndexTableUtils& utils, const TrackingParameters& params) +{ + if constexpr (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); + } +} + +template +void TrackingLoadPolicy::configureBeamPosition(TimeFrame& tf, + const TrackingParameters& p, + const o2::dataformats::MeanVertexObject* meanVertex, + bool overrideBeamEstimation) +{ + const auto& tc = TrackerParamRef::get(); + const float systErrY2 = p.SystError2Row.empty() ? 0.f : p.SystError2Row[0]; + const float layerRes = p.LayerResolution.empty() ? 0.f : p.LayerResolution[0]; + + if constexpr (DetId == o2::detectors::DetID::MFT) { + tf.setBeamPosition(p.Diamond[0], p.Diamond[1], p.DiamondCov[3], layerRes, systErrY2); + LOGP(info, "MFT CA vertex seed from diamond: x={:.4f} y={:.4f} z={:.4f}", + p.Diamond[0], p.Diamond[1], p.Diamond[2]); + } else if ((overrideBeamEstimation || tc.overrideBeamEstimation) && meanVertex != nullptr) { + tf.setBeamPosition(meanVertex->getX(), meanVertex->getY(), meanVertex->getSigmaY2(), layerRes, systErrY2); + LOGP(info, "ITS CA beam position from MeanVertex: x={:.4f} y={:.4f}", meanVertex->getX(), meanVertex->getY()); + } else if (p.UseDiamond) { + tf.setBeamPosition(p.Diamond[0], p.Diamond[1], p.DiamondCov[3], layerRes, systErrY2); + LOGP(info, "ITS CA beam position from diamond: x={:.4f} y={:.4f} z={:.4f}", + p.Diamond[0], p.Diamond[1], p.Diamond[2]); + } +} + +template struct DetectorTraits<7>; +template struct DetectorTraits<10>; +template struct TrackingLoadPolicy; +template struct TrackingLoadPolicy; + +} // namespace o2::itsmft::tracking diff --git a/Detectors/ITSMFT/common/tracking/src/IOUtils.cxx b/Detectors/ITSMFT/common/tracking/src/IOUtils.cxx index 6ed2dfd8a13af..da85b7b81a125 100644 --- a/Detectors/ITSMFT/common/tracking/src/IOUtils.cxx +++ b/Detectors/ITSMFT/common/tracking/src/IOUtils.cxx @@ -14,7 +14,7 @@ /// #include "ITSMFTTracking/IOUtils.h" -#include "ITSMFTTracking/TrackingConfigParam.h" +#include "ITSMFTTracking/TrackingParamRef.h" #include "ITStracking/Cluster.h" #include "Framework/Logger.h" @@ -30,10 +30,16 @@ 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; + const auto& conf = o2::itsmft::tracking::TrackerParamRef::get(); + for (int il = 0; il < o2::itsmft::tracking::TrackerParamRef::nLayers(); il++) { + if constexpr (DetId == o2::detectors::DetID::MFT) { + if (conf.sysErr2Row[il] > 0.f || conf.sysErr2Col[il] > 0.f) { + return true; + } + } else { + if (conf.sysErrY2[il] > 0.f || conf.sysErrZ2[il] > 0.f) { + return true; + } } } return false; @@ -50,17 +56,50 @@ void loadClusterTrackingFrameInfoImpl(GeomT* geom, bool applySysErrors) { const auto sensorID = c.getSensorID(); + if (sensorID < 0 || sensorID >= geom->getSize()) { + LOGP(fatal, "Cluster sensorID {} is out of geometry range [0, {})", sensorID, geom->getSize()); + } layer = geom->getLayer(sensorID); - clusterSize = o2::itsmft::ioutils::extractClusterSize(c, pattIt, dict); + if (layer < 0 || layer >= o2::itsmft::tracking::TrackerParamRef::nLayers()) { + LOGP(fatal, "Cluster sensorID {} maps to invalid layer {} (expected [0, {}))", + sensorID, layer, o2::itsmft::tracking::TrackerParamRef::nLayers()); + } + const auto pattID = c.getPatternID(); + if (pattID != o2::itsmft::CompCluster::InvalidPatternID && dict != nullptr && + (pattID < 0 || pattID >= dict->getSize())) { + LOGP(fatal, "Cluster patternID {} is out of dictionary range [0, {})", pattID, dict->getSize()); + } - float sigma2Row{0.f}; - float sigma2Col{0.f}; - const auto locXYZ = o2::itsmft::ioutils::extractClusterData(c, pattIt, dict, sigma2Row, sigma2Col); + float sigma2Row{o2::itsmft::ioutils::DefClusError2Row}; + float sigma2Col{o2::itsmft::ioutils::DefClusError2Col}; + o2::math_utils::Point3D locXYZ{}; + if (pattID != o2::itsmft::CompCluster::InvalidPatternID) { + sigma2Row = dict->getErr2X(pattID); + sigma2Col = dict->getErr2Z(pattID); + if (!dict->isGroup(pattID)) { + locXYZ = dict->getClusterCoordinates(c); + clusterSize = dict->getNpixels(pattID); + } else { + o2::itsmft::ClusterPattern patt(pattIt); + locXYZ = dict->getClusterCoordinates(c, patt); + clusterSize = patt.getNPixels(); + } + } else { + o2::itsmft::ClusterPattern patt(pattIt); + locXYZ = dict->getClusterCoordinates(c, patt, false); + clusterSize = patt.getNPixels(); + } 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::MFT) { + const auto& conf = o2::itsmft::tracking::TrackerParamRef::get(); + sigma2Row += conf.sysErr2Row[layerId]; + sigma2Col += conf.sysErr2Col[layerId]; + } else { + const auto& conf = o2::itsmft::tracking::TrackerParamRef::get(); + sigma2Row += conf.sysErrY2[layerId]; + sigma2Col += conf.sysErrZ2[layerId]; + } } if constexpr (DetId == o2::detectors::DetID::ITS) { @@ -71,6 +110,10 @@ void loadClusterTrackingFrameInfoImpl(GeomT* geom, std::array{trkXYZ.y(), trkXYZ.z()}, std::array{sigma2Row, 0.f, sigma2Col}}; } else { + if (!geom->getCacheL2G().isFilled() || geom->getCacheL2G().getSize() <= sensorID) { + LOGP(fatal, "MFT L2G matrix cache unavailable for sensorID {} (filled={}, size={})", + sensorID, geom->getCacheL2G().isFilled(), geom->getCacheL2G().getSize()); + } const auto gloXYZ = geom->getMatrixL2G(sensorID) * locXYZ; // ALPIDE row (local X) -> global X, column (local Z) -> global Y tfInfo = o2::its::TrackingFrameInfo{ @@ -86,7 +129,6 @@ void fillOutputClusters(GeomT* geom, gsl::span::iterator& pattIt, std::vector>& output, const o2::itsmft::TopologyDictionary* dict, - const o2::itsmft::TrackerParamConfig& conf, bool applyMisalignment) { for (const auto& c : clusters) { @@ -95,8 +137,15 @@ void fillOutputClusters(GeomT* geom, 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]; + if constexpr (DetId == o2::detectors::DetID::MFT) { + const auto& conf = o2::itsmft::tracking::TrackerParamRef::get(); + sigma2Row += conf.sysErr2Row[layerId]; + sigma2Col += conf.sysErr2Col[layerId]; + } else { + const auto& conf = o2::itsmft::tracking::TrackerParamRef::get(); + sigma2Row += conf.sysErrY2[layerId]; + sigma2Col += conf.sysErrZ2[layerId]; + } } o2::math_utils::Point3D outXYZ{}; if constexpr (DetId == o2::detectors::DetID::ITS) { @@ -178,23 +227,16 @@ void convertCompactClusters(gsl::span clusters, { 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; - } - } + const bool applyMisalignment = shouldApplySysErrors(); if constexpr (DetId == o2::detectors::DetID::ITS) { auto* geom = o2::its::GeometryTGeo::Instance(); geom->fillMatrixCache(mask); - fillOutputClusters(geom, clusters, pattIt, output, dict, conf, applyMisalignment); + fillOutputClusters(geom, clusters, pattIt, output, dict, applyMisalignment); } else { auto* geom = o2::mft::GeometryTGeo::Instance(); geom->fillMatrixCache(mask); - fillOutputClusters(geom, clusters, pattIt, output, dict, conf, applyMisalignment); + fillOutputClusters(geom, clusters, pattIt, output, dict, applyMisalignment); } } diff --git a/Detectors/ITSMFT/common/tracking/src/ITSMFTTrackingLinkDef.h b/Detectors/ITSMFT/common/tracking/src/ITSMFTTrackingLinkDef.h index 3deddc108e4b5..b63c36e55b80a 100644 --- a/Detectors/ITSMFT/common/tracking/src/ITSMFTTrackingLinkDef.h +++ b/Detectors/ITSMFT/common/tracking/src/ITSMFTTrackingLinkDef.h @@ -15,12 +15,6 @@ #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>> + ; diff --git a/Detectors/ITSMFT/common/tracking/src/MFTForwardRefit.cxx b/Detectors/ITSMFT/common/tracking/src/MFTForwardRefit.cxx new file mode 100644 index 0000000000000..c5672aa9ed93d --- /dev/null +++ b/Detectors/ITSMFT/common/tracking/src/MFTForwardRefit.cxx @@ -0,0 +1,190 @@ +// 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 MFTForwardRefit.cxx +/// \brief +/// + +#include "ITSMFTTracking/MFTForwardRefit.h" + +#include +#include + +#include "Framework/Logger.h" +#include "ITStracking/Constants.h" +#include "MFTTracking/Cluster.h" +#include "MFTTracking/Constants.h" +#include "MFTTracking/MFTTrackingParam.h" +#include "MFTTracking/TrackCA.h" +#include "MFTTracking/TrackFitter.h" + +namespace o2::itsmft::tracking +{ + +namespace +{ +template +bool buildTrackLTF(const TrackSeed& seed, + const TimeFrame& tf, + const TrackingParameters& params, + TrackLTFType& ltf) +{ + ltf = TrackLTFType(true); + const auto& unsorted = tf.getUnsortedClusters(); + const auto hitMask = seed.getHitLayerMask(); + + for (int layer = 0; layer < constants::MFTNLayers; ++layer) { + if (!hitMask.has(layer)) { + continue; + } + const int clIdx = seed.getCluster(layer); + if (clIdx == o2::its::constants::UnusedIndex) { + continue; + } + if (clIdx >= static_cast(unsorted[layer].size())) { + LOGP(warn, "MFT CA forward refit: invalid cluster index {} on layer {}", clIdx, layer); + return false; + } + const auto& cluster = unsorted[layer][clIdx]; + const auto& tfInfo = tf.getClusterTrackingFrameInfo(layer, cluster); + o2::mft::Cluster mftCluster{ + tfInfo.xCoordinate, + tfInfo.yCoordinate, + tfInfo.zCoordinate, + cluster.phi, + cluster.radius, + clIdx, + 0, + tfInfo.covarianceTrackingFrame[0], + tfInfo.covarianceTrackingFrame[2], + 0}; + const int extIdx = tf.getClusterExternalIndex(layer, clIdx); + const int clsSize = tf.getClusterSize(0, extIdx); + ltf.setPoint(mftCluster, layer, clIdx, {}, extIdx, clsSize); + } + + if (ltf.getNumberOfPoints() < params.MinTrackLength) { + return false; + } + ltf.sort(); + return true; +} + +void copyClusterRefs(const TrackSeed& seed, + const TimeFrame& tf, + MFTCATrack& track) +{ + track.setPattern(0); + const auto hitMask = seed.getHitLayerMask(); + for (int layer = 0; layer < constants::MFTNLayers; ++layer) { + if (!hitMask.has(layer)) { + track.setClusterIndex(layer, o2::its::constants::UnusedIndex); + continue; + } + const int clIdx = seed.getCluster(layer); + track.setClusterIndex(layer, clIdx); + const int extIdx = tf.getClusterExternalIndex(layer, clIdx); + track.setClusterSize(layer, tf.getClusterSize(0, extIdx)); + } +} + +template +bool fitTrackLTF(TrackLTFType& ltf, float bz) +{ + const auto& mftParam = o2::mft::MFTTrackingParam::Instance(); + o2::mft::TrackFitter fitter; + fitter.setBz(bz); + fitter.setMFTRadLength(mftParam.MFTRadLength); + fitter.setTrackModel(mftParam.trackmodel); + fitter.setAlignResiduals(mftParam.alignResidual); + + TrackLTFType outward = ltf; + if (!fitter.initTrack(ltf) || !fitter.fit(ltf)) { + return false; + } + if (!fitter.initTrack(outward, true) || !fitter.fit(outward, true)) { + return false; + } + ltf.setOutParam(outward); + return true; +} + +template +bool refitTrackFwdImpl(const TrackSeed& seed, + MFTCATrack& track, + const TimeFrame& tf, + const TrackingParameters& params, + float bz) +{ + TrackLTFType ltf; + if (!buildTrackLTF(seed, tf, params, ltf)) { + return false; + } + + if (!fitTrackLTF(ltf, bz)) { + return false; + } + + const int nCl = ltf.getNumberOfPoints(); + if (nCl < params.MinTrackLength) { + return false; + } + const float minPt = params.MinPt[constants::MFTNLayers - nCl]; + if (ltf.getPt() < minPt) { + return false; + } + const float chi2ndf = static_cast(ltf.getTrackChi2() / std::max(1, 2 * nCl - 5)); + if (chi2ndf > params.MaxChi2NDF) { + return false; + } + + if (params.TrackletMinAbsX > 0.f) { + if (std::abs(ltf.getX()) < params.TrackletMinAbsX) { + return false; + } + const auto hitMask = seed.getHitLayerMask(); + for (int layer = 0; layer < constants::MFTNLayers; ++layer) { + if (!hitMask.has(layer)) { + continue; + } + const int clIdx = seed.getCluster(layer); + if (clIdx == o2::its::constants::UnusedIndex) { + continue; + } + if (std::abs(tf.getUnsortedClusters()[layer][clIdx].xCoordinate) < params.TrackletMinAbsX) { + return false; + } + } + } + + auto& mftTr = track.getTrack(); + mftTr = static_cast(ltf); + mftTr.setCA(true); + copyClusterRefs(seed, tf, track); + return true; +} +} // namespace + +bool refitTrackFwd(const TrackSeed& seed, + MFTCATrack& track, + const TimeFrame& tf, + const TrackingParameters& params, + float bz) +{ + const auto& mftParam = o2::mft::MFTTrackingParam::Instance(); + const bool fieldOff = mftParam.forceZeroField || std::abs(bz) < 1e-6f; + if (fieldOff) { + return refitTrackFwdImpl(seed, track, tf, params, 0.f); + } + return refitTrackFwdImpl(seed, track, tf, params, bz); +} + +} // namespace o2::itsmft::tracking diff --git a/Detectors/ITSMFT/common/tracking/src/TimeFrame.cxx b/Detectors/ITSMFT/common/tracking/src/TimeFrame.cxx index 27d486ecb9fd5..516071289893c 100644 --- a/Detectors/ITSMFT/common/tracking/src/TimeFrame.cxx +++ b/Detectors/ITSMFT/common/tracking/src/TimeFrame.cxx @@ -16,8 +16,9 @@ #include #include "Framework/Logger.h" -#include "ITSMFTTracking/TimeFrame.h" +#include "ITSMFTTracking/DetectorTraits.h" #include "ITSMFTTracking/IOUtils.h" +#include "ITSMFTTracking/TimeFrame.h" #include "ITStracking/MathUtils.h" #include "DataFormatsITSMFT/CompCluster.h" #include "DataFormatsITSMFT/ROFRecord.h" @@ -32,43 +33,6 @@ struct ClusterHelper { 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::its::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 @@ -125,7 +89,11 @@ void TimeFrame::loadROFrameData(gsl::span int lay{0}; unsigned int clusterSize{0}; TrackingFrameInfo tfInfo; - loadClusterForDet(detId, c, pattIt, dict, lay, clusterSize, tfInfo); + if (detId == o2::detectors::DetID::MFT) { + o2::itsmft::ioutils::loadClusterTrackingFrameInfo(c, pattIt, dict, lay, clusterSize, tfInfo); + } else { + o2::itsmft::ioutils::loadClusterTrackingFrameInfo(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()); @@ -296,7 +264,7 @@ void TimeFrame::initialise(const TrackingParameters& trkParam, const in deepVectorClear(mPrimaryVerticesLabels); } clearResizeBoundedVector(mLinesLabels, getNrof(1), mMemoryPool.get()); - configureIndexTableUtils(mIndexTableUtils, mDetId, trkParam); + DetectorTraits::configureIndexTableUtils(mIndexTableUtils, trkParam); clearResizeBoundedVector(mPositionResolution, trkParam.NLayers, mMemoryPool.get()); clearResizeBoundedVector(mBogusClusters, trkParam.NLayers, mMemoryPool.get()); deepVectorClear(mTrackletClusters); diff --git a/Detectors/ITSMFT/common/tracking/src/TrackerTraits.cxx b/Detectors/ITSMFT/common/tracking/src/TrackerTraits.cxx new file mode 100644 index 0000000000000..83817233aa48f --- /dev/null +++ b/Detectors/ITSMFT/common/tracking/src/TrackerTraits.cxx @@ -0,0 +1,1113 @@ +// 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 TrackerTraits.cxx +/// \brief +/// + +#include +#include +#include +#include +#include + +#include +#include + +#include "DetectorsBase/Propagator.h" +#include "Framework/Logger.h" +#include "GPUCommonMath.h" +#include "ITStracking/BoundedAllocator.h" +#include "ITSMFTTracking/Cell.h" +#include "ITStracking/Constants.h" +#include "ITSMFTTracking/CATrackTypes.h" +#include "ITSMFTTracking/Configuration.h" +#include "ITSMFTTracking/Constants.h" +#include "ITSMFTTracking/DetectorTraits.h" +#include "ITSMFTTracking/IndexTableUtils.h" +#include "ITSMFTTracking/LayerMask.h" +#include "ITStracking/ROFLookupTables.h" +#include "ITSMFTTracking/TrackerTraits.h" +#include "ITStracking/TrackHelpers.h" +#include "ITStracking/Tracklet.h" + +namespace o2::itsmft::tracking +{ + +namespace math_utils = o2::its::math_utils; +using o2::its::deepVectorClear; +using o2::its::TimeEstBC; + +struct PassMode { + using OnePass = std::integral_constant; + using TwoPassCount = std::integral_constant; + using TwoPassInsert = std::integral_constant; +}; + +struct HemisphereStats { + int xNeg{0}; + int xPos{0}; + int xZero{0}; + void add(float x) + { + if (x < 0.f) { + ++xNeg; + } else if (x > 0.f) { + ++xPos; + } else { + ++xZero; + } + } + void log(const char* stage, int iteration) const + { + LOGP(info, "MFT CA iter {} {}: x<0={} x>0={} x=0={} total={}", + iteration, stage, xNeg, xPos, xZero, xNeg + xPos + xZero); + } +}; + +bool printHemisphereStatsEnabled(const TrackingParameters& params) +{ + static const bool fromEnv = [] { + const char* v = std::getenv("MFT_CA_PRINT_HEMISPHERE_STATS"); + return v != nullptr && v[0] != '\0' && v[0] != '0'; + }(); + return params.PrintHemisphereStats || fromEnv; +} + +template +void TrackerTraits::computeLayerTracklets(const int iteration, int iVertex) +{ + const auto topology = mTimeFrame->getTrackingTopologyView(); + for (int transitionId = 0; transitionId < topology.nTransitions; ++transitionId) { + mTimeFrame->getTracklets()[transitionId].clear(); + mTimeFrame->getTrackletsLabel(transitionId).clear(); + std::fill(mTimeFrame->getTrackletsLookupTable()[transitionId].begin(), mTimeFrame->getTrackletsLookupTable()[transitionId].end(), 0); + } + + const Vertex diamondVert(mTrkParams[iteration].Diamond, mTrkParams[iteration].DiamondCov, 1, 1.f); + gsl::span diamondSpan(&diamondVert, 1); + + mTaskArena->execute([&] { + auto forTracklets = [&](auto Tag, int transitionId, int pivotROF, int base, int& offset) -> int { + const auto& transition = topology.getTransition(transitionId); + if (!mTimeFrame->getROFMaskView().isROFEnabled(transition.fromLayer, pivotROF)) { + return 0; + } + gsl::span primaryVertices = mTrkParams[iteration].UseDiamond ? diamondSpan : mTimeFrame->getPrimaryVertices(transition.fromLayer, pivotROF); + if (primaryVertices.empty()) { + return 0; + } + const int startVtx = iVertex >= 0 ? iVertex : 0; + const int endVtx = iVertex >= 0 ? o2::gpu::CAMath::Min(iVertex + 1, int(primaryVertices.size())) : int(primaryVertices.size()); + if (endVtx <= startVtx || (iVertex + 1) > primaryVertices.size()) { + return 0; + } + + const auto& rofOverlap = mTimeFrame->getROFOverlapTableView().getOverlap(transition.fromLayer, transition.toLayer, pivotROF); + if (!rofOverlap.getEntries()) { + return 0; + } + + int localCount = 0; + auto& tracklets = mTimeFrame->getTracklets()[transitionId]; + auto layer0 = mTimeFrame->getClustersOnLayer(pivotROF, transition.fromLayer); + if (layer0.empty()) { + return 0; + } + + const float meanDeltaR = mTrkParams[iteration].LayerRadii[transition.toLayer] - mTrkParams[iteration].LayerRadii[transition.fromLayer]; + const float phiCut = mTimeFrame->getTransitionPhiCut(transitionId); + const float msAngle = mTimeFrame->getTransitionMSAngle(transitionId); + const bool useXYBins = mTimeFrame->getIndexTableUtils().getCoordType() == IndexTableCoordType::XY; + const bool useDiamond = mTrkParams[iteration].UseDiamond; + const bool isMFT = DetectorTraits::DetId == o2::detectors::DetID::MFT; + const float meanDeltaZ = isMFT ? detail::mftLayerZ(transition.toLayer) - detail::mftLayerZ(transition.fromLayer) : 0.f; + const float minAbsX = isMFT ? mTrkParams[iteration].TrackletMinAbsX : 0.f; + + for (int iCluster = 0; iCluster < int(layer0.size()); ++iCluster) { + const Cluster& currentCluster = layer0[iCluster]; + const int currentSortedIndex = mTimeFrame->getSortedIndex(pivotROF, transition.fromLayer, iCluster); + if (mTimeFrame->isClusterUsed(transition.fromLayer, currentCluster.clusterId)) { + continue; + } + if (isMFT && !detail::mftPassesMinAbsX(currentCluster, minAbsX)) { + continue; + } + const float inverseR0 = 1.f / currentCluster.radius; + + for (int iV = startVtx; iV < endVtx; ++iV) { + const auto& pv = primaryVertices[iV]; + if (!useDiamond && !mTimeFrame->getROFVertexLookupTableView().isVertexCompatible(transition.fromLayer, pivotROF, pv)) { + continue; + } + if (pv.isFlagSet(Vertex::Flags::UPCMode) != mTrkParams[iteration].PassFlags[IterationStep::SelectUPCVertices]) { + continue; + } + const float resolution = o2::gpu::CAMath::Sqrt(math_utils::Sq(mTimeFrame->getPositionResolution(transition.fromLayer)) + math_utils::Sq(mTrkParams[iteration].PVres) / float(pv.getNContributors())); + float colWindow = 0.f; + float rowWindow = 0.f; + float sigmaZ = 0.f; + float zWindowMin = 0.f; + float zWindowMax = 0.f; + float xProj = 0.f; + float yProj = 0.f; + if (isMFT) { + sigmaZ = detail::mftTrackletSigmaZ(currentCluster.zCoordinate, currentCluster.radius, pv.getZ(), resolution, meanDeltaZ, msAngle); + mftConeProject(currentCluster, transition.fromLayer, transition.toLayer, xProj, yProj); + const float zExpected = detail::mftExpectedZAtLayer(currentCluster.zCoordinate, transition.fromLayer, transition.toLayer, pv.getZ()); + zWindowMin = zExpected - sigmaZ * mTrkParams[iteration].NSigmaCut; + zWindowMax = zExpected + sigmaZ * mTrkParams[iteration].NSigmaCut; + const float absZDenom = o2::gpu::CAMath::Max(o2::gpu::CAMath::Abs(detail::mftLayerZ(transition.toLayer) - pv.getZ()), 1.e-3f); + const float sigmaX = sigmaZ * o2::gpu::CAMath::Abs(xProj / absZDenom); + colWindow = sigmaX * mTrkParams[iteration].NSigmaCut; + rowWindow = phiCut * currentCluster.radius; + } else { + const float tanLambda = (currentCluster.zCoordinate - pv.getZ()) * inverseR0; + zWindowMin = tanLambda * (mTimeFrame->getMinR(transition.toLayer) - currentCluster.radius) + currentCluster.zCoordinate; + zWindowMax = tanLambda * (mTimeFrame->getMaxR(transition.toLayer) - currentCluster.radius) + currentCluster.zCoordinate; + const float sqInvDeltaZ0 = 1.f / (math_utils::Sq(currentCluster.zCoordinate - pv.getZ()) + constants::Tolerance); + sigmaZ = o2::gpu::CAMath::Sqrt((math_utils::Sq(resolution) * math_utils::Sq(tanLambda) * ((math_utils::Sq(inverseR0) + sqInvDeltaZ0) * math_utils::Sq(meanDeltaR) + 1.f)) + math_utils::Sq(meanDeltaR * msAngle)); + colWindow = sigmaZ * mTrkParams[iteration].NSigmaCut; + rowWindow = phiCut; + } + const auto bins = o2::itsmft::getBinsRectCluster(currentCluster, transition.fromLayer, transition.toLayer, + zWindowMin, zWindowMax, colWindow, rowWindow, + mTimeFrame->getIndexTableUtils()); + if (bins.x < 0) { + continue; + } + int rowBinsNum = bins.w - bins.y + 1; + if (rowBinsNum < 0) { + rowBinsNum += mTrkParams[iteration].RowBins; + } + + for (int targetROF = rofOverlap.getFirstEntry(); targetROF < rofOverlap.getEntriesBound(); ++targetROF) { + if (!mTimeFrame->getROFMaskView().isROFEnabled(transition.toLayer, targetROF)) { + continue; + } + auto layer1 = mTimeFrame->getClustersOnLayer(targetROF, transition.toLayer); + if (layer1.empty()) { + continue; + } + const auto ts = mTimeFrame->getROFOverlapTableView().getTimeStamp(transition.fromLayer, pivotROF, transition.toLayer, targetROF); + if (!useDiamond && !ts.isCompatible(pv.getTimeStamp())) { + continue; + } + const auto& targetIndexTable = mTimeFrame->getIndexTable(targetROF, transition.toLayer); + const int colBinRange = (bins.z - bins.x) + 1; + for (int iRow = 0; iRow < rowBinsNum; ++iRow) { + const int iRowBin = useXYBins ? (bins.y + iRow) : ((bins.y + iRow) % mTrkParams[iteration].RowBins); + if (useXYBins && iRowBin >= mTrkParams[iteration].RowBins) { + break; + } + const int firstBinIdx = mTimeFrame->getIndexTableUtils().getBinIndex(bins.x, iRowBin); + const int maxBinIdx = firstBinIdx + colBinRange; + const int firstRow = targetIndexTable[firstBinIdx]; + const int lastRow = targetIndexTable[maxBinIdx]; + for (int iNext = firstRow; iNext < lastRow; ++iNext) { + if (iNext >= int(layer1.size())) { + break; + } + const Cluster& nextCluster = layer1[iNext]; + if (mTimeFrame->isClusterUsed(transition.toLayer, nextCluster.clusterId)) { + continue; + } + if (isMFT && !detail::mftPassesMinAbsX(nextCluster, minAbsX)) { + continue; + } + + bool acceptTracklet = false; + float tanL = 0.f; + if (isMFT) { + const float zExpected = detail::mftExpectedZAtLayer(currentCluster.zCoordinate, transition.fromLayer, transition.toLayer, pv.getZ()); + const float deltaZ = o2::gpu::CAMath::Abs(zExpected - nextCluster.zCoordinate); + const float transCut2 = rowWindow * rowWindow; + const float transDist2 = detail::mftTrackletTransverseDist2(currentCluster, nextCluster, transition.fromLayer, transition.toLayer); + if (sigmaZ > 0.f && deltaZ / sigmaZ < mTrkParams[iteration].NSigmaCut && transDist2 < transCut2) { + acceptTracklet = std::abs(meanDeltaZ) > 1.e-6f; + tanL = (currentCluster.zCoordinate - nextCluster.zCoordinate) / meanDeltaZ; + } + } else { + const float tanLambda = (currentCluster.zCoordinate - pv.getZ()) * inverseR0; + const float deltaZ = o2::gpu::CAMath::Abs((tanLambda * (nextCluster.radius - currentCluster.radius)) + currentCluster.zCoordinate - nextCluster.zCoordinate); + if (deltaZ / sigmaZ < mTrkParams[iteration].NSigmaCut && + math_utils::isPhiDifferenceBelow(currentCluster.phi, nextCluster.phi, phiCut)) { + acceptTracklet = true; + tanL = (currentCluster.zCoordinate - nextCluster.zCoordinate) / (currentCluster.radius - nextCluster.radius); + } + } + + if (acceptTracklet) { + const float phi{o2::gpu::CAMath::ATan2(currentCluster.yCoordinate - nextCluster.yCoordinate, currentCluster.xCoordinate - nextCluster.xCoordinate)}; + if constexpr (decltype(Tag)::value == PassMode::OnePass::value) { + tracklets.emplace_back(currentSortedIndex, mTimeFrame->getSortedIndex(targetROF, transition.toLayer, iNext), tanL, phi, ts); + } else if constexpr (decltype(Tag)::value == PassMode::TwoPassCount::value) { + ++localCount; + } else if constexpr (decltype(Tag)::value == PassMode::TwoPassInsert::value) { + const int idx = base + offset++; + tracklets[idx] = Tracklet(currentSortedIndex, mTimeFrame->getSortedIndex(targetROF, transition.toLayer, iNext), tanL, phi, ts); + } + } + } + } + } + } + } + return localCount; + }; + + int dummy{0}; + if (mTaskArena->max_concurrency() <= 1) { + for (int transitionId{0}; transitionId < topology.nTransitions; ++transitionId) { + const int fromLayer = topology.getTransition(transitionId).fromLayer; + const int startROF = 0, endROF = mTimeFrame->getROFOverlapTableView().getLayer(fromLayer).mNROFsTF; + for (int pivotROF{startROF}; pivotROF < endROF; ++pivotROF) { + forTracklets(PassMode::OnePass{}, transitionId, pivotROF, 0, dummy); + } + } + } else { + tbb::parallel_for(0, static_cast(topology.nTransitions), [&](const int transitionId) { + const int fromLayer = topology.getTransition(transitionId).fromLayer; + const int startROF = 0, endROF = mTimeFrame->getROFOverlapTableView().getLayer(fromLayer).mNROFsTF; + bounded_vector perROFCount((endROF - startROF) + 1, mMemoryPool.get()); + tbb::parallel_for(startROF, endROF, [&](const int pivotROF) { + perROFCount[pivotROF - startROF] = forTracklets(PassMode::TwoPassCount{}, transitionId, pivotROF, 0, dummy); + }); + std::exclusive_scan(perROFCount.begin(), perROFCount.end(), perROFCount.begin(), 0); + const int nTracklets = perROFCount.back(); + mTimeFrame->getTracklets()[transitionId].resize(nTracklets); + if (nTracklets == 0) { + return; + } + tbb::parallel_for(startROF, endROF, [&](const int pivotROF) { + int baseIdx = perROFCount[pivotROF - startROF]; + if (baseIdx == perROFCount[pivotROF + 1 - startROF]) { + return; + } + int localIdx = 0; + forTracklets(PassMode::TwoPassInsert{}, transitionId, pivotROF, baseIdx, localIdx); + }); + }); + } + + tbb::parallel_for(0, static_cast(topology.nTransitions), [&](const int transitionId) { + /// Sort tracklets & remove duplicates + // duplicates can exist simply since we evaluate per vertex + auto& trkl{mTimeFrame->getTracklets()[transitionId]}; + std::sort(trkl.begin(), trkl.end()); + trkl.erase(std::unique(trkl.begin(), trkl.end()), trkl.end()); + trkl.shrink_to_fit(); + auto& lut{mTimeFrame->getTrackletsLookupTable()[transitionId]}; + if (!trkl.empty()) { + for (const auto& tkl : trkl) { + lut[tkl.firstClusterIndex + 1]++; + } + std::inclusive_scan(lut.begin(), lut.end(), lut.begin()); + } + }); + + if constexpr (DetectorTraits::DetId == o2::detectors::DetID::MFT) { + if (printHemisphereStatsEnabled(mTrkParams[iteration])) { + HemisphereStats stats; + for (int transitionId{0}; transitionId < topology.nTransitions; ++transitionId) { + const auto& transition = topology.getTransition(transitionId); + for (const auto& trkl : mTimeFrame->getTracklets()[transitionId]) { + const int clId = mTimeFrame->getClusters()[transition.fromLayer][trkl.firstClusterIndex].clusterId; + stats.add(mTimeFrame->getUnsortedClusters()[transition.fromLayer][clId].xCoordinate); + } + } + stats.log("tracklets", iteration); + } + } + + /// Create tracklets labels + if (mTimeFrame->hasMCinformation() && mTrkParams[iteration].CreateArtefactLabels) { + tbb::parallel_for(0, static_cast(topology.nTransitions), [&](const int transitionId) { + const auto& transition = topology.getTransition(transitionId); + for (auto& trk : mTimeFrame->getTracklets()[transitionId]) { + MCCompLabel label; + int currentId{mTimeFrame->getClusters()[transition.fromLayer][trk.firstClusterIndex].clusterId}; + int nextId{mTimeFrame->getClusters()[transition.toLayer][trk.secondClusterIndex].clusterId}; + for (const auto& lab1 : mTimeFrame->getClusterLabels(transition.fromLayer, currentId)) { + for (const auto& lab2 : mTimeFrame->getClusterLabels(transition.toLayer, nextId)) { + if (lab1 == lab2 && lab1.isValid()) { + label = lab1; + break; + } + } + if (label.isValid()) { + break; + } + } + mTimeFrame->getTrackletsLabel(transitionId).emplace_back(label); + } + }); + } + }); +} + +template +void TrackerTraits::computeLayerCells(const int iteration) +{ + const auto topology = mTimeFrame->getTrackingTopologyView(); + for (int cellTopologyId = 0; cellTopologyId < topology.nCells; ++cellTopologyId) { + deepVectorClear(mTimeFrame->getCells()[cellTopologyId]); + deepVectorClear(mTimeFrame->getCellsLookupTable()[cellTopologyId]); + if (mTimeFrame->hasMCinformation() && mTrkParams[iteration].CreateArtefactLabels) { + deepVectorClear(mTimeFrame->getCellsLabel(cellTopologyId)); + } + } + + mTaskArena->execute([&] { + auto forTrackletCells = [&](auto Tag, int cellTopologyId, bounded_vector& layerCells, int iTracklet, int offset = 0) -> int { + const auto& cellTopology = topology.getCell(cellTopologyId); + const auto& firstTransition = topology.getTransition(cellTopology.firstTransition); + const auto& secondTransition = topology.getTransition(cellTopology.secondTransition); + const Tracklet& currentTracklet{mTimeFrame->getTracklets()[cellTopology.firstTransition][iTracklet]}; + const int nextLayerClusterIndex{currentTracklet.secondClusterIndex}; + const int nextLayerFirstTrackletIndex{mTimeFrame->getTrackletsLookupTable()[cellTopology.secondTransition][nextLayerClusterIndex]}; + const int nextLayerLastTrackletIndex{mTimeFrame->getTrackletsLookupTable()[cellTopology.secondTransition][nextLayerClusterIndex + 1]}; + int foundCells{0}; + for (int iNextTracklet{nextLayerFirstTrackletIndex}; iNextTracklet < nextLayerLastTrackletIndex; ++iNextTracklet) { + const Tracklet& nextTracklet{mTimeFrame->getTracklets()[cellTopology.secondTransition][iNextTracklet]}; + if (nextTracklet.firstClusterIndex != nextLayerClusterIndex) { + break; + } + if (!currentTracklet.getTimeStamp().isCompatible(nextTracklet.getTimeStamp())) { + continue; + } + + const float deltaTanLambdaSigma = std::abs(currentTracklet.tanLambda - nextTracklet.tanLambda) / mTrkParams[iteration].CellDeltaTanLambdaSigma; + if (deltaTanLambdaSigma < mTrkParams[iteration].NSigmaCut) { + + /// Track seed preparation. Clusters are numbered progressively from the innermost going outward. + const int clusId[3]{ + mTimeFrame->getClusters()[firstTransition.fromLayer][currentTracklet.firstClusterIndex].clusterId, + mTimeFrame->getClusters()[firstTransition.toLayer][nextTracklet.firstClusterIndex].clusterId, + mTimeFrame->getClusters()[secondTransition.toLayer][nextTracklet.secondClusterIndex].clusterId}; + const int hitLayers[3]{firstTransition.fromLayer, firstTransition.toLayer, secondTransition.toLayer}; + const auto& cluster1_glo = mTimeFrame->getUnsortedClusters()[firstTransition.fromLayer][clusId[0]]; + const auto& cluster2_glo = mTimeFrame->getUnsortedClusters()[firstTransition.toLayer][clusId[1]]; + const auto& cluster3_glo = mTimeFrame->getUnsortedClusters()[secondTransition.toLayer][clusId[2]]; + const auto& cluster3_tf = mTimeFrame->getTrackingFrameInfoOnLayer(secondTransition.toLayer)[clusId[2]]; + auto track{o2::its::track::buildTrackSeed(cluster1_glo, cluster2_glo, cluster3_tf, mBz)}; + + float chi2{0.f}; + bool good{false}; + if constexpr (DetectorTraits::DetId == o2::detectors::DetID::MFT) { + const float r2Cut = mTrkParams[iteration].CellRoadRCut * mTrkParams[iteration].CellRoadRCut; + good = DetectorTraits::validateMFTCellClusters(cluster1_glo, cluster2_glo, cluster3_glo, r2Cut); + } else { + for (int iC{2}; iC--;) { + const int hitLayer = hitLayers[iC]; + const TrackingFrameInfo& trackingHit = mTimeFrame->getTrackingFrameInfoOnLayer(hitLayer)[clusId[iC]]; + + if (!track.rotate(trackingHit.alphaTrackingFrame)) { + break; + } + + if (!track.propagateTo(trackingHit.xTrackingFrame, getBz())) { + break; + } + + if (!track.correctForMaterial(mTrkParams[iteration].LayerxX0[hitLayer], mTrkParams[iteration].LayerxX0[hitLayer] * constants::Radl * constants::Rho, true)) { + break; + } + + const auto predChi2{track.getPredictedChi2Quiet(trackingHit.positionTrackingFrame, trackingHit.covarianceTrackingFrame)}; + if (!iC && predChi2 > mTrkParams[iteration].MaxChi2ClusterAttachment) { + break; + } + + if (!track.o2::track::TrackParCov::update(trackingHit.positionTrackingFrame, trackingHit.covarianceTrackingFrame)) { + break; + } + + good = !iC; + chi2 += predChi2; + } + } + if (good) { + TimeEstBC ts = currentTracklet.getTimeStamp(); + ts += nextTracklet.getTimeStamp(); + if constexpr (decltype(Tag)::value == PassMode::OnePass::value) { + layerCells.emplace_back(cellTopology.hitLayerMask, clusId[0], clusId[1], clusId[2], iTracklet, iNextTracklet, track, chi2, ts); + ++foundCells; + } else if constexpr (decltype(Tag)::value == PassMode::TwoPassCount::value) { + ++foundCells; + } else if constexpr (decltype(Tag)::value == PassMode::TwoPassInsert::value) { + layerCells[offset++] = CellSeed(cellTopology.hitLayerMask, clusId[0], clusId[1], clusId[2], iTracklet, iNextTracklet, track, chi2, ts); + ++foundCells; + } else { + static_assert(false, "Unknown mode!"); + } + } + } + } + return foundCells; + }; + + for (int cellTopologyId = 0; cellTopologyId < topology.nCells; ++cellTopologyId) { + const auto& cellTopology = topology.getCell(cellTopologyId); + if (mTimeFrame->getTracklets()[cellTopology.firstTransition].empty() || + mTimeFrame->getTracklets()[cellTopology.secondTransition].empty()) { + continue; + } + + auto& layerCells = mTimeFrame->getCells()[cellTopologyId]; + const int currentLayerTrackletsNum{static_cast(mTimeFrame->getTracklets()[cellTopology.firstTransition].size())}; + bounded_vector perTrackletCount(currentLayerTrackletsNum + 1, 0, mMemoryPool.get()); + if (mTaskArena->max_concurrency() <= 1) { + for (int iTracklet{0}; iTracklet < currentLayerTrackletsNum; ++iTracklet) { + perTrackletCount[iTracklet] = forTrackletCells(PassMode::OnePass{}, cellTopologyId, layerCells, iTracklet); + } + std::exclusive_scan(perTrackletCount.begin(), perTrackletCount.end(), perTrackletCount.begin(), 0); + } else { + tbb::parallel_for(0, currentLayerTrackletsNum, [&](const int iTracklet) { + perTrackletCount[iTracklet] = forTrackletCells(PassMode::TwoPassCount{}, cellTopologyId, layerCells, iTracklet); + }); + + std::exclusive_scan(perTrackletCount.begin(), perTrackletCount.end(), perTrackletCount.begin(), 0); + auto totalCells{perTrackletCount.back()}; + if (totalCells == 0) { + auto& lut = mTimeFrame->getCellsLookupTable()[cellTopologyId]; + lut.resize(currentLayerTrackletsNum + 1); + std::fill(lut.begin(), lut.end(), 0); + continue; + } + layerCells.resize(totalCells); + + tbb::parallel_for(0, currentLayerTrackletsNum, [&](const int iTracklet) { + int offset = perTrackletCount[iTracklet]; + if (offset == perTrackletCount[iTracklet + 1]) { + return; + } + forTrackletCells(PassMode::TwoPassInsert{}, cellTopologyId, layerCells, iTracklet, offset); + }); + } + + auto& lut = mTimeFrame->getCellsLookupTable()[cellTopologyId]; + lut.resize(currentLayerTrackletsNum + 1); + std::copy_n(perTrackletCount.begin(), currentLayerTrackletsNum + 1, lut.begin()); + + if (mTimeFrame->hasMCinformation() && mTrkParams[iteration].CreateArtefactLabels) { + auto& labels = mTimeFrame->getCellsLabel(cellTopologyId); + labels.reserve(layerCells.size()); + for (const auto& cell : layerCells) { + MCCompLabel currentLab{mTimeFrame->getTrackletsLabel(cellTopology.firstTransition)[cell.getFirstTrackletIndex()]}; + MCCompLabel nextLab{mTimeFrame->getTrackletsLabel(cellTopology.secondTransition)[cell.getSecondTrackletIndex()]}; + labels.emplace_back(currentLab == nextLab ? currentLab : MCCompLabel()); + } + } + } + }); + + if constexpr (DetectorTraits::DetId == o2::detectors::DetID::MFT) { + if (printHemisphereStatsEnabled(mTrkParams[iteration])) { + HemisphereStats stats; + for (int cellTopologyId = 0; cellTopologyId < topology.nCells; ++cellTopologyId) { + const auto& cellTopology = topology.getCell(cellTopologyId); + const auto& firstTransition = topology.getTransition(cellTopology.firstTransition); + for (const auto& cell : mTimeFrame->getCells()[cellTopologyId]) { + const int clId = cell.getFirstClusterIndex(); + stats.add(mTimeFrame->getUnsortedClusters()[firstTransition.fromLayer][clId].xCoordinate); + } + } + stats.log("cells", iteration); + } + } + + for (int transitionId = 0; transitionId < topology.nTransitions; ++transitionId) { + deepVectorClear(mTimeFrame->getTracklets()[transitionId]); + deepVectorClear(mTimeFrame->getTrackletsLabel(transitionId)); + } +} + +template +void TrackerTraits::findCellsNeighbours(const int iteration) +{ + const auto topology = mTimeFrame->getTrackingTopologyView(); + mTaskArena->execute([&] { + std::vector> cellsNeighboursByTarget; + cellsNeighboursByTarget.reserve(topology.nCells); + for (int cellTopologyId{0}; cellTopologyId < topology.nCells; ++cellTopologyId) { + deepVectorClear(mTimeFrame->getCellsNeighbours()[cellTopologyId]); + deepVectorClear(mTimeFrame->getCellsNeighboursTopology()[cellTopologyId]); + deepVectorClear(mTimeFrame->getCellsNeighboursLUT()[cellTopologyId]); + cellsNeighboursByTarget.emplace_back(mMemoryPool.get()); + } + + for (int outerLayer{0}; outerLayer < NLayers; ++outerLayer) { + for (int cellTopologyId{0}; cellTopologyId < topology.nCells; ++cellTopologyId) { + const auto& cellTopology = topology.getCell(cellTopologyId); + if (cellTopology.hitLayerMask.last() != outerLayer || + mTimeFrame->getCells()[cellTopologyId].empty()) { + continue; + } + const auto successors = topology.getCellsStartingWithTransition(cellTopology.secondTransition); + if (!successors.getEntries()) { + continue; + } + + tbb::enumerable_thread_specific> sourceNeighbours([&]() { return bounded_vector{mMemoryPool.get()}; }); + tbb::parallel_for(0, static_cast(mTimeFrame->getCells()[cellTopologyId].size()), [&](const int iCell) { + auto& localNeighbours = sourceNeighbours.local(); + const auto& currentCellSeed{mTimeFrame->getCells()[cellTopologyId][iCell]}; + const int nextLayerTrackletIndex{currentCellSeed.getSecondTrackletIndex()}; + for (int iSuccessor{0}; iSuccessor < successors.getEntries(); ++iSuccessor) { + const int nextCellTopologyId = topology.cellsByFirstTransition[successors.getFirstEntry() + iSuccessor]; + if (mTimeFrame->getCells()[nextCellTopologyId].empty() || + mTimeFrame->getCellsLookupTable()[nextCellTopologyId].empty()) { + continue; + } + const auto& nextCellLUT = mTimeFrame->getCellsLookupTable()[nextCellTopologyId]; + if (nextLayerTrackletIndex + 1 >= static_cast(nextCellLUT.size())) { + continue; + } + const int nextLayerFirstCellIndex{nextCellLUT[nextLayerTrackletIndex]}; + const int nextLayerLastCellIndex{nextCellLUT[nextLayerTrackletIndex + 1]}; + for (int iNextCell{nextLayerFirstCellIndex}; iNextCell < nextLayerLastCellIndex; ++iNextCell) { + const auto& nextCellSeedRef{mTimeFrame->getCells()[nextCellTopologyId][iNextCell]}; + if (nextCellSeedRef.getFirstTrackletIndex() != nextLayerTrackletIndex || !currentCellSeed.getTimeStamp().isCompatible(nextCellSeedRef.getTimeStamp())) { + break; + } + + bool neighbourAccepted{false}; + if constexpr (DetectorTraits::DetId == o2::detectors::DetID::MFT) { + // Successor cell inner cluster must match current middle cluster (shared tracklet). + neighbourAccepted = currentCellSeed.getSecondClusterIndex() == nextCellSeedRef.getFirstClusterIndex(); + } else { + auto nextCellSeed{mTimeFrame->getCells()[nextCellTopologyId][iNextCell]}; /// copy + if (!nextCellSeed.rotate(currentCellSeed.getAlpha()) || + !nextCellSeed.propagateTo(currentCellSeed.getX(), getBz())) { + continue; + } + + float chi2 = currentCellSeed.getPredictedChi2(nextCellSeed); + if (chi2 > mTrkParams[iteration].MaxChi2ClusterAttachment) { + continue; + } + neighbourAccepted = true; + } + if (!neighbourAccepted) { + continue; + } + + const int nextLevel = currentCellSeed.getLevel() + 1; + localNeighbours.emplace_back(cellTopologyId, iCell, nextCellTopologyId, iNextCell, nextLevel); + } + } + }); + + bounded_vector count(topology.nCells, 0, mMemoryPool.get()); + for (const auto& localNeighbours : sourceNeighbours) { + for (const auto& neigh : localNeighbours) { + ++count[neigh.nextCellTopology]; + } + } + for (size_t i{0}; i < topology.nCells; ++i) { + cellsNeighboursByTarget[i].reserve(count[i]); + } + for (const auto& localNeighbours : sourceNeighbours) { + for (const auto& neigh : localNeighbours) { + cellsNeighboursByTarget[neigh.nextCellTopology].emplace_back(neigh); + if (neigh.level > mTimeFrame->getCells()[neigh.nextCellTopology][neigh.nextCell].getLevel()) { + mTimeFrame->getCells()[neigh.nextCellTopology][neigh.nextCell].setLevel(neigh.level); + } + } + } + } + } + + for (int cellTopologyId{0}; cellTopologyId < topology.nCells; ++cellTopologyId) { + auto& cellsNeighbours = cellsNeighboursByTarget[cellTopologyId]; + if (cellsNeighbours.empty()) { + continue; + } + + std::sort(cellsNeighbours.begin(), cellsNeighbours.end(), [](const auto& a, const auto& b) { + return a.nextCell < b.nextCell; + }); + + auto& cellsNeighbourLUT = mTimeFrame->getCellsNeighboursLUT()[cellTopologyId]; + cellsNeighbourLUT.assign(mTimeFrame->getCells()[cellTopologyId].size(), 0); + for (const auto& neigh : cellsNeighbours) { + ++cellsNeighbourLUT[neigh.nextCell]; + } + std::inclusive_scan(cellsNeighbourLUT.begin(), cellsNeighbourLUT.end(), cellsNeighbourLUT.begin()); + + mTimeFrame->getCellsNeighbours()[cellTopologyId].reserve(cellsNeighbours.size()); + mTimeFrame->getCellsNeighboursTopology()[cellTopologyId].reserve(cellsNeighbours.size()); + std::ranges::transform(cellsNeighbours, std::back_inserter(mTimeFrame->getCellsNeighbours()[cellTopologyId]), [](const auto& neigh) { return neigh.cell; }); + std::ranges::transform(cellsNeighbours, std::back_inserter(mTimeFrame->getCellsNeighboursTopology()[cellTopologyId]), [](const auto& neigh) { return neigh.cellTopology; }); + } + + // clean up LUTs + for (auto& cellLUT : mTimeFrame->getCellsLookupTable()) { + deepVectorClear(cellLUT); + } + }); +} + +template +template +void TrackerTraits::processNeighbours(int iteration, int defaultCellTopologyId, int iLevel, const bounded_vector& currentCellSeed, const bounded_vector& currentCellId, const bounded_vector& currentCellTopologyId, bounded_vector& updatedCellSeeds, bounded_vector& updatedCellsIds, bounded_vector& updatedCellsTopologyIds) +{ + auto propagator = o2::base::Propagator::Instance(); + + mTaskArena->execute([&] { + auto forCellNeighbours = [&](auto Tag, int iCell, int offset = 0) -> int { + const auto& currentCell{currentCellSeed[iCell]}; + const int cellTopologyId = currentCellTopologyId.empty() ? defaultCellTopologyId : currentCellTopologyId[iCell]; + + if constexpr (decltype(Tag)::value != PassMode::TwoPassInsert::value) { + if (currentCell.getLevel() != iLevel) { + return 0; + } + if (currentCellId.empty()) { + for (int layer = 0; layer < NLayers; ++layer) { + const int clusterIndex = currentCell.getCluster(layer); + if (clusterIndex != constants::UnusedIndex && mTimeFrame->isClusterUsed(layer, clusterIndex)) { + return 0; /// this we do only on the first iteration, hence the check on currentCellId + } + } + } + } + + const int cellId = currentCellId.empty() ? iCell : currentCellId[iCell]; + if (cellTopologyId < 0 || mTimeFrame->getCellsNeighboursLUT()[cellTopologyId].empty()) { + return 0; + } + const int startNeighbourId{cellId ? mTimeFrame->getCellsNeighboursLUT()[cellTopologyId][cellId - 1] : 0}; + const int endNeighbourId{mTimeFrame->getCellsNeighboursLUT()[cellTopologyId][cellId]}; + int foundSeeds{0}; + for (int iNeighbourCell{startNeighbourId}; iNeighbourCell < endNeighbourId; ++iNeighbourCell) { + const int neighbourCellTopologyId = mTimeFrame->getCellsNeighboursTopology()[cellTopologyId][iNeighbourCell]; + const int neighbourCellId = mTimeFrame->getCellsNeighbours()[cellTopologyId][iNeighbourCell]; + const auto& neighbourCell = mTimeFrame->getCells()[neighbourCellTopologyId][neighbourCellId]; + if (neighbourCell.getSecondTrackletIndex() != currentCell.getFirstTrackletIndex()) { + continue; + } + if (!currentCell.getTimeStamp().isCompatible(neighbourCell.getTimeStamp())) { + continue; + } + if (currentCell.getLevel() - 1 != neighbourCell.getLevel()) { + continue; + } + const int neighbourLayer = neighbourCell.getInnerLayer(); + const int neighbourCluster = neighbourCell.getFirstClusterIndex(); + if (mTimeFrame->isClusterUsed(neighbourLayer, neighbourCluster)) { + continue; + } + + /// Let's start the fitting procedure + TrackSeedN seed{currentCell}; + seed.getTimeStamp() = currentCell.getTimeStamp(); + seed.getTimeStamp() += neighbourCell.getTimeStamp(); + + if constexpr (DetectorTraits::DetId == o2::detectors::DetID::MFT) { + const float r2Cut = mTrkParams[iteration].CellRoadRCut * mTrkParams[iteration].CellRoadRCut; + int refLayerA{-1}; + int refLayerB{-1}; + int clIdxA{-1}; + int clIdxB{-1}; + for (int layer = NLayers - 1; layer >= 0; --layer) { + const int clIdx = currentCell.getCluster(layer); + if (clIdx == constants::UnusedIndex) { + continue; + } + if (refLayerA < 0) { + refLayerA = layer; + clIdxA = clIdx; + } else { + refLayerB = layer; + clIdxB = clIdx; + break; + } + } + if (refLayerB < 0) { + continue; + } + const auto& cA = mTimeFrame->getUnsortedClusters()[refLayerA][clIdxA]; + const auto& cB = mTimeFrame->getUnsortedClusters()[refLayerB][clIdxB]; + const auto& cN = mTimeFrame->getUnsortedClusters()[neighbourLayer][neighbourCluster]; + if (detail::mftDistanceToSeedSquared(cA, cB, cN) >= r2Cut) { + continue; + } + } else { + const auto& trHit = mTimeFrame->getTrackingFrameInfoOnLayer(neighbourLayer)[neighbourCluster]; + + if (!seed.rotate(trHit.alphaTrackingFrame)) { + continue; + } + + if (!propagator->propagateToX(seed, trHit.xTrackingFrame, getBz(), o2::base::PropagatorImpl::MAX_SIN_PHI, o2::base::PropagatorImpl::MAX_STEP, mTrkParams[iteration].CorrType)) { + continue; + } + + if (mTrkParams[iteration].CorrType == o2::base::PropagatorF::MatCorrType::USEMatCorrNONE) { + if (!seed.correctForMaterial(mTrkParams[iteration].LayerxX0[neighbourLayer], mTrkParams[iteration].LayerxX0[neighbourLayer] * constants::Radl * constants::Rho, true)) { + continue; + } + } + + auto predChi2{seed.getPredictedChi2Quiet(trHit.positionTrackingFrame, trHit.covarianceTrackingFrame)}; + if ((predChi2 > mTrkParams[iteration].MaxChi2ClusterAttachment) || predChi2 < 0.f) { + continue; + } + seed.setChi2(seed.getChi2() + predChi2); + if (!seed.o2::track::TrackParCov::update(trHit.positionTrackingFrame, trHit.covarianceTrackingFrame)) { + continue; + } + } + + if constexpr (decltype(Tag)::value != PassMode::TwoPassCount::value) { + seed.getClusters()[neighbourLayer] = neighbourCluster; + auto mask = seed.getHitLayerMask(); + mask.set(neighbourLayer); + seed.setHitLayerMask(mask); + seed.setLevel(neighbourCell.getLevel()); + seed.setFirstTrackletIndex(neighbourCell.getFirstTrackletIndex()); + seed.setSecondTrackletIndex(neighbourCell.getSecondTrackletIndex()); + } + + if constexpr (decltype(Tag)::value == PassMode::OnePass::value) { + updatedCellSeeds.push_back(seed); + updatedCellsIds.push_back(neighbourCellId); + updatedCellsTopologyIds.push_back(neighbourCellTopologyId); + } else if constexpr (decltype(Tag)::value == PassMode::TwoPassCount::value) { + ++foundSeeds; + } else if constexpr (decltype(Tag)::value == PassMode::TwoPassInsert::value) { + updatedCellSeeds[offset] = seed; + updatedCellsIds[offset] = neighbourCellId; + updatedCellsTopologyIds[offset++] = neighbourCellTopologyId; + } else { + static_assert(false, "Unknown mode!"); + } + } + return foundSeeds; + }; + + const int nCells = static_cast(currentCellSeed.size()); + if (mTaskArena->max_concurrency() <= 1) { + for (int iCell{0}; iCell < nCells; ++iCell) { + forCellNeighbours(PassMode::OnePass{}, iCell); + } + } else { + bounded_vector perCellCount(nCells + 1, 0, mMemoryPool.get()); + tbb::parallel_for(0, nCells, [&](const int iCell) { + perCellCount[iCell] = forCellNeighbours(PassMode::TwoPassCount{}, iCell); + }); + + std::exclusive_scan(perCellCount.begin(), perCellCount.end(), perCellCount.begin(), 0); + auto totalNeighbours{perCellCount.back()}; + if (totalNeighbours == 0) { + return; + } + updatedCellSeeds.resize(totalNeighbours); + updatedCellsIds.resize(totalNeighbours); + updatedCellsTopologyIds.resize(totalNeighbours); + + tbb::parallel_for(0, nCells, [&](const int iCell) { + int offset = perCellCount[iCell]; + if (offset == perCellCount[iCell + 1]) { + return; + } + forCellNeighbours(PassMode::TwoPassInsert{}, iCell, offset); + }); + } + }); +} + +template +void TrackerTraits::findRoads(const int iteration) +{ + bounded_vector> firstClusters(mTrkParams[iteration].NLayers, bounded_vector(mMemoryPool.get()), mMemoryPool.get()); + firstClusters.resize(mTrkParams[iteration].NLayers); + const auto propagator = o2::base::Propagator::Instance(); + const TrackingFrameInfo* tfInfos[NLayers]{}; + const Cluster* unsortedClusters[NLayers]{}; + for (int iLayer = 0; iLayer < NLayers; ++iLayer) { + tfInfos[iLayer] = mTimeFrame->getTrackingFrameInfoOnLayer(iLayer).data(); + unsortedClusters[iLayer] = mTimeFrame->getUnsortedClusters()[iLayer].data(); + } + const auto topology = mTimeFrame->getTrackingTopologyView(); + for (int startLevel{mTrkParams[iteration].CellsPerRoad()}; startLevel >= mTrkParams[iteration].CellMinimumLevel(); --startLevel) { + + auto seedFilter = [&](const auto& seed) { + return seed.getHitLayerMask().isAllowed(mTrkParams[iteration].MaxHoles, mTrkParams[iteration].HoleLayerMask) && + seed.getHitLayerMask().length() >= mTrkParams[iteration].MinTrackLength && + seed.getQ2Pt() <= 1.e3 && seed.getChi2() <= mTrkParams[iteration].MaxChi2NDF * ((startLevel + 2) * 2 - 5); + }; + + bounded_vector trackSeeds(mMemoryPool.get()); + for (int startCellTopologyId{0}; startCellTopologyId < topology.nCells; ++startCellTopologyId) { + const int startLayer = topology.getCell(startCellTopologyId).hitLayerMask.last(); + if (!(mTrkParams[iteration].StartLayerMask.has(startLayer)) || mTimeFrame->getCells()[startCellTopologyId].empty()) { + continue; + } + + bounded_vector lastCellId(mMemoryPool.get()), updatedCellId(mMemoryPool.get()); + bounded_vector lastCellTopologyId(mMemoryPool.get()), updatedCellTopologyId(mMemoryPool.get()); + bounded_vector lastCellSeed(mMemoryPool.get()), updatedCellSeed(mMemoryPool.get()); + + processNeighbours(iteration, startCellTopologyId, startLevel, mTimeFrame->getCells()[startCellTopologyId], lastCellId, lastCellTopologyId, updatedCellSeed, updatedCellId, updatedCellTopologyId); + + int level = startLevel; + while (level > 2 && !updatedCellSeed.empty()) { + lastCellSeed.swap(updatedCellSeed); + lastCellId.swap(updatedCellId); + lastCellTopologyId.swap(updatedCellTopologyId); + deepVectorClear(updatedCellSeed); /// tame the memory peaks + deepVectorClear(updatedCellId); /// tame the memory peaks + deepVectorClear(updatedCellTopologyId); + processNeighbours(iteration, constants::UnusedIndex, --level, lastCellSeed, lastCellId, lastCellTopologyId, updatedCellSeed, updatedCellId, updatedCellTopologyId); + } + deepVectorClear(lastCellId); /// tame the memory peaks + deepVectorClear(lastCellTopologyId); /// tame the memory peaks + deepVectorClear(lastCellSeed); /// tame the memory peaks + + if (!updatedCellSeed.empty()) { + trackSeeds.reserve(trackSeeds.size() + std::count_if(updatedCellSeed.begin(), updatedCellSeed.end(), seedFilter)); + std::copy_if(updatedCellSeed.begin(), updatedCellSeed.end(), std::back_inserter(trackSeeds), seedFilter); + } + } + + if (trackSeeds.empty()) { + continue; + } + + using TrackT = typename DetectorTraits::TrackType; + bounded_vector tracks(mMemoryPool.get()); + mTaskArena->execute([&] { + auto forSeed = [&](auto Tag, int iSeed, int offset = 0) { + TrackT temporaryTrack; + const bool refitSuccess = DetectorTraits::refitSeed(trackSeeds[iSeed], + temporaryTrack, + mTrkParams[iteration], + mBz, + *mTimeFrame, + tfInfos, + unsortedClusters, + propagator); + if (refitSuccess) { + if constexpr (decltype(Tag)::value == PassMode::OnePass::value) { + tracks.push_back(temporaryTrack); + } else if constexpr (decltype(Tag)::value == PassMode::TwoPassCount::value) { + // nothing to do + } else if constexpr (decltype(Tag)::value == PassMode::TwoPassInsert::value) { + tracks[offset] = temporaryTrack; + } else { + static_assert(false, "Unknown mode!"); + } + return 1; + } + return 0; + }; + + const int nSeeds = static_cast(trackSeeds.size()); + if (mTaskArena->max_concurrency() <= 1) { + for (int iSeed{0}; iSeed < nSeeds; ++iSeed) { + forSeed(PassMode::OnePass{}, iSeed); + } + } else { + bounded_vector perSeedCount(nSeeds + 1, 0, mMemoryPool.get()); + tbb::parallel_for(0, nSeeds, [&](const int iSeed) { + perSeedCount[iSeed] = forSeed(PassMode::TwoPassCount{}, iSeed); + }); + + std::exclusive_scan(perSeedCount.begin(), perSeedCount.end(), perSeedCount.begin(), 0); + auto totalTracks{perSeedCount.back()}; + if (totalTracks == 0) { + return; + } + tracks.resize(totalTracks); + + tbb::parallel_for(0, nSeeds, [&](const int iSeed) { + if (perSeedCount[iSeed] == perSeedCount[iSeed + 1]) { + return; + } + forSeed(PassMode::TwoPassInsert{}, iSeed, perSeedCount[iSeed]); + }); + } + + deepVectorClear(trackSeeds); + }); + + DetectorTraits::sortRefittedTracks(tracks); + const int nTracksBefore = static_cast(mTimeFrame->getTracks().size()); + acceptTracks(iteration, tracks, firstClusters); + + if constexpr (DetectorTraits::DetId == o2::detectors::DetID::MFT) { + if (printHemisphereStatsEnabled(mTrkParams[iteration])) { + LOGP(info, "MFT CA iter {} road seeds at level {}: {} refitted: {} accepted: {}", + iteration, startLevel, trackSeeds.size(), tracks.size(), mTimeFrame->getTracks().size() - nTracksBefore); + HemisphereStats stats; + for (int i = nTracksBefore; i < static_cast(mTimeFrame->getTracks().size()); ++i) { + const auto& track = mTimeFrame->getTracks()[i]; + const int firstLayer = track.getFirstClusterLayer(); + const int clId = track.getClusterIndex(firstLayer); + stats.add(mTimeFrame->getUnsortedClusters()[firstLayer][clId].xCoordinate); + } + stats.log("accepted tracks", iteration); + } + } + } + markTracks(iteration); +} + +template +void TrackerTraits::acceptTracks(int iteration, bounded_vector>& tracks, bounded_vector>& firstClusters) +{ + auto& trks = mTimeFrame->getTracks(); + trks.reserve(trks.size() + tracks.size()); + const float smallestROFHalf = mTimeFrame->getROFOverlapTableView().getClockLayer().mROFLength * 0.5f; + for (auto& track : tracks) { + int nShared = 0; + bool isFirstShared{false}; + int firstLayer{-1}, firstCluster{-1}; + for (int iLayer{0}; iLayer < mTrkParams[iteration].NLayers; ++iLayer) { + if (track.getClusterIndex(iLayer) == constants::UnusedIndex) { + continue; + } + bool isShared = mTimeFrame->isClusterUsed(iLayer, track.getClusterIndex(iLayer)); + nShared += int(isShared); + if (firstLayer < 0) { + firstCluster = track.getClusterIndex(iLayer); + isFirstShared = isShared && mTrkParams[iteration].AllowSharingFirstCluster && std::find(firstClusters[iLayer].begin(), firstClusters[iLayer].end(), firstCluster) != firstClusters[iLayer].end(); + firstLayer = iLayer; + } + } + + /// do not account for the first cluster in the shared clusters number if it is allowed + if (nShared - int(isFirstShared && mTrkParams[iteration].AllowSharingFirstCluster) > mTrkParams[iteration].SharedMaxClusters) { + continue; + } + + bool firstCls{true}, nominalCompatible{true}; + TimeEstBC nominalTS, expandedTS; + for (int iLayer{0}; iLayer < mTrkParams[iteration].NLayers; ++iLayer) { + if (track.getClusterIndex(iLayer) == constants::UnusedIndex) { + continue; + } + mTimeFrame->markUsedCluster(iLayer, track.getClusterIndex(iLayer)); + int currentROF = mTimeFrame->getClusterROF(iLayer, track.getClusterIndex(iLayer)); + const auto nominalROFTS = mTimeFrame->getROFOverlapTableView().getLayer(iLayer).getROFTimeBounds(currentROF); + const auto expandedROFTS = mTimeFrame->getROFOverlapTableView().getLayer(iLayer).getROFTimeBounds(currentROF, true); + if (firstCls) { + firstCls = false; + nominalTS = nominalROFTS; + expandedTS = expandedROFTS; + } else { + if (nominalCompatible) { + if (nominalTS.isCompatible(nominalROFTS)) { + nominalTS += nominalROFTS; + } else { + nominalCompatible = false; + } + } + if (!expandedTS.isCompatible(expandedROFTS)) { + LOGP(fatal, "TS {}+/-{} are incompatible with {}+/-{}, this should not happen!", expandedROFTS.getTimeStamp(), expandedROFTS.getTimeStampError(), expandedTS.getTimeStamp(), expandedTS.getTimeStampError()); + } + expandedTS += expandedROFTS; + } + } + track.getTimeStamp() = (nominalCompatible ? nominalTS : expandedTS).makeSymmetrical(); + // this is a sanity clamp + // we cannot be worse than the clock so we clamp to this + if (track.getTimeStamp().getTimeStampError() > smallestROFHalf) { + track.getTimeStamp().setTimeStampError(smallestROFHalf); + } + DetectorTraits::finalizeAcceptedTrack(track); + trks.emplace_back(track); + + if (mTrkParams[iteration].AllowSharingFirstCluster) { + firstClusters[firstLayer].push_back(firstCluster); + } + } +} + +template +void TrackerTraits::markTracks(int iteration) +{ + if (mTrkParams[iteration].AllowSharingFirstCluster) { + /// Now we have to set the shared cluster flag + auto& tracks = mTimeFrame->getTracks(); + + bounded_vector fclusSort(tracks.size(), mMemoryPool.get()); + std::iota(fclusSort.begin(), fclusSort.end(), 0); + std::sort(fclusSort.begin(), fclusSort.end(), [&tracks](int a, int b) { + return tracks[a].getClusterIndex(tracks[a].getFirstClusterLayer()) < tracks[b].getClusterIndex(tracks[b].getFirstClusterLayer()); + }); + + auto areTracksSelected = [this, iteration](const auto& t1, const auto& t2) { + const auto t1FirstLayer{t1.getFirstClusterLayer()}, t2FirstLayer{t2.getFirstClusterLayer()}; + if (t1FirstLayer != t2FirstLayer) { + return false; + } + if (mTimeFrame->getClusterROF(t1FirstLayer, t1.getClusterIndex(t1FirstLayer)) != mTimeFrame->getClusterROF(t2FirstLayer, t2.getClusterIndex(t2FirstLayer))) { + return false; + } + if (!math_utils::isPhiDifferenceBelow(t1.getPhi(), t2.getPhi(), mTrkParams[iteration].SharedClusterMaxDeltaPhi)) { + return false; + } + if (std::abs(t1.getEta() - t2.getEta()) > mTrkParams[iteration].SharedClusterMaxDeltaEta) { + return false; + } + if (mTrkParams[iteration].SharedClusterOppositeSign && DetectorTraits::sameTrackSign(t1, t2)) { + return false; + } + return true; + }; + + for (int i{0}; i < static_cast(fclusSort.size()); ++i) { + auto& track = tracks[fclusSort[i]]; + for (int j{i + 1}; j < static_cast(fclusSort.size()) && tracks[fclusSort[j]].getClusterIndex(tracks[fclusSort[j]].getFirstClusterLayer()) == track.getClusterIndex(track.getFirstClusterLayer()); ++j) { + auto& track2 = tracks[fclusSort[j]]; + if (areTracksSelected(track, track2)) { + track.setSharedClusters(); + track2.setSharedClusters(); + } + } + } + } +} + +template +void TrackerTraits::setBz(float bz) +{ + mBz = bz; + mTimeFrame->setBz(bz); +} + +template +void TrackerTraits::setNThreads(int n, std::shared_ptr& arena) +{ +#if defined(OPTIMISATION_OUTPUT) + mTaskArena = std::make_shared(1); +#else + if (arena == nullptr) { + mTaskArena = std::make_shared(std::abs(n)); + LOGP(info, "Setting tracker with {} threads.", n); + } else { + mTaskArena = arena; + } +#endif +} + +template class TrackerTraits<7>; +template class TrackerTraits<10>; +template void TrackerTraits<7>::processNeighbours(int, int, int, const bounded_vector&, const bounded_vector&, const bounded_vector&, bounded_vector>&, bounded_vector&, bounded_vector&); +template void TrackerTraits<7>::processNeighbours>(int, int, int, const bounded_vector>&, const bounded_vector&, const bounded_vector&, bounded_vector>&, bounded_vector&, bounded_vector&); +template void TrackerTraits<10>::processNeighbours(int, int, int, const bounded_vector&, const bounded_vector&, const bounded_vector&, bounded_vector>&, bounded_vector&, bounded_vector&); +template void TrackerTraits<10>::processNeighbours>(int, int, int, const bounded_vector>&, const bounded_vector&, const bounded_vector&, bounded_vector>&, bounded_vector&, bounded_vector&); + +} // namespace o2::itsmft::tracking diff --git a/Detectors/ITSMFT/common/tracking/src/TrackingConfigParam.cxx b/Detectors/ITSMFT/common/tracking/src/TrackingConfigParam.cxx index fc90f9243a996..9d68c2719a439 100644 --- a/Detectors/ITSMFT/common/tracking/src/TrackingConfigParam.cxx +++ b/Detectors/ITSMFT/common/tracking/src/TrackingConfigParam.cxx @@ -11,8 +11,9 @@ #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(); +namespace o2::itsmft +{ +// Ensure MFT CA params are registered in the global param database. +// ITS production tracking uses o2::its::TrackerParamConfig in O2::ITStracking. +static auto& sMFTCATrackerParam = TrackerParamConfig::Instance(); +} // namespace o2::itsmft diff --git a/Detectors/ITSMFT/common/tracking/src/TrackingInterface.cxx b/Detectors/ITSMFT/common/tracking/src/TrackingInterface.cxx new file mode 100644 index 0000000000000..d7347b13f8bfb --- /dev/null +++ b/Detectors/ITSMFT/common/tracking/src/TrackingInterface.cxx @@ -0,0 +1,331 @@ +// 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 TrackingInterface.cxx +/// \brief +/// + +#include "ITSMFTTracking/DetectorTraits.h" +#include "ITSMFTTracking/TrackingInterface.h" + +#include + +#include "ITStracking/Constants.h" + +#include + +#include "CommonConstants/LHCConstants.h" +#include "CommonDataFormat/IRFrame.h" +#include "CommonDataFormat/InteractionRecord.h" +#include "DataFormatsITSMFT/CompCluster.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" +#include "DataFormatsITSMFT/ROFRecord.h" +#include "DetectorsBase/GRPGeomHelper.h" +#include "DetectorsBase/Propagator.h" +#include "Framework/Logger.h" +#include "ITSMFTTracking/TrackingParamRef.h" +#include "MFTTracking/MFTTrackingParam.h" + +namespace o2::itsmft::tracking +{ + +namespace +{ +template +consteval const char* detName() +{ + return detId == o2::detectors::DetID::MFT ? "MFT" : "ITS"; +} + +bool rofOverlapsIRFrames(const o2::itsmft::ROFRecord& rof, int rofLengthInBC, gsl::span irFrames) +{ + o2::InteractionRecord rofStart{rof.getBCData()}; + o2::InteractionRecord rofEnd = rofStart + rofLengthInBC - 1; + o2::dataformats::IRFrame ref(rofStart, rofEnd); + for (const auto& ir : irFrames) { + if (ir.info > 0) { + const auto overlap = ref.getOverlap(ir); + if (overlap.isValid()) { + return true; + } + } + } + return false; +} +} // namespace + +template +ITSMFTTrackingInterface::ITSMFTTrackingInterface(bool useMC, + o2::itsmft::TrackingMode::Type mode, + bool overrideBeamEst) + : mUseMC(useMC), mTrackingMode(mode), mOverrideBeamEstimation(overrideBeamEst) +{ +} + +template +void ITSMFTTrackingInterface::initialise() +{ + resolveTrackingParameters(); + initialiseMemoryPool(); + initialiseTracker(); +} + +template +void ITSMFTTrackingInterface::resolveTrackingParameters() +{ + const auto& tc = o2::itsmft::tracking::TrackerParamRef::get(); + auto mode = mTrackingMode; + if (auto parmode = static_cast(tc.trackingMode); + mode == o2::itsmft::TrackingMode::Unset || + (parmode != o2::itsmft::TrackingMode::Unset && mode != parmode)) { + if (parmode != o2::itsmft::TrackingMode::Unset) { + LOGP(info, "{} CA tracking mode overwritten by configurable params from {} to {}", + detName(), o2::itsmft::TrackingMode::toString(mode), o2::itsmft::TrackingMode::toString(parmode)); + mode = parmode; + } + } + if (mode == o2::itsmft::TrackingMode::Unset) { + mode = o2::itsmft::TrackingMode::Sync; + } + mTrackingMode = mode; + mTrackParams = o2::itsmft::TrackingMode::getTrackingParameters(DetId, mode); + LOGP(info, "{} CA tracker initialized in {} mode with {} iteration(s)", + detName(), o2::itsmft::TrackingMode::toString(mode), mTrackParams.size()); + for (size_t i = 0; i < mTrackParams.size(); ++i) { + LOGP(info, " iteration {}: {}", i, mTrackParams[i].asString()); + } +} + +template +void ITSMFTTrackingInterface::initialiseMemoryPool() +{ + size_t maxMemory = std::numeric_limits::max(); + if (!mTrackParams.empty() && mTrackParams[0].MaxMemory != maxMemory) { + maxMemory = mTrackParams[0].MaxMemory; + } + mMemoryPool = std::make_shared(maxMemory); + mTimeFrame.setMemoryPool(mMemoryPool); +} + +template +void ITSMFTTrackingInterface::initialiseTracker() +{ + if (mTrackParams.empty()) { + return; + } + const auto& tc = o2::itsmft::tracking::TrackerParamRef::get(); + mTrackerTraits = std::make_unique(); + mTrackerTraits->setMemoryPool(mMemoryPool); + std::shared_ptr taskArena; + mTrackerTraits->setNThreads(tc.nThreads, taskArena); + mTracker = std::make_unique(mTrackerTraits.get()); +} + +template +float ITSMFTTrackingInterface::processTimeFrame(gsl::span rofs, + gsl::span clusters, + gsl::span patterns, + const o2::dataformats::MCTruthContainer* labels, + gsl::span irFrames) +{ + if (mTrackParams.empty()) { + LOGP(info, "{} CA tracking mode is off, skipping TimeFrame processing", detName()); + return 0.f; + } + if (mDict == nullptr) { + LOGP(fatal, "{} CA tracker cluster dictionary is not available", detName()); + } + + loadTimeFrame(rofs, clusters, patterns, labels, irFrames); + onTimeFrameLoaded(); + + const float elapsedMs = runTracking(); + onTrackingFinished(elapsedMs); + + return elapsedMs; +} + +template +void ITSMFTTrackingInterface::loadTimeFrame(gsl::span rofs, + gsl::span clusters, + gsl::span patterns, + const o2::dataformats::MCTruthContainer* labels, + gsl::span irFrames) +{ + configureROFLookupTables(); + validateROFInput(rofs); + + mTimeFrame.setBz(o2::base::Propagator::Instance()->getNominalBz()); + configureBeamPosition(); + + configureROFMask(rofs, irFrames); + + auto pattIt = patterns.begin(); + mTimeFrame.loadROFrameData(rofs, clusters, pattIt, mDict, -1, labels, DetId); + + configureTrackingTopology(); + + LOGP(info, "{} CA loaded {} clusters from {} ROFs into TimeFrame ({} pattern bytes, MC={})", + detName(), mTimeFrame.getTotalClusters(), rofs.size(), patterns.size(), labels != nullptr); + + for (int iLayer = 0; iLayer < NLayers; ++iLayer) { + LOGP(info, " layer {}: {} ROF slots", iLayer, mTimeFrame.getNrof(iLayer)); + } +} + +template +float ITSMFTTrackingInterface::runTracking() +{ + if (!mTracker || mTrackParams.empty()) { + return 0.f; + } + mTracker->adoptTimeFrame(mTimeFrame); + mTracker->setParameters(mTrackParams); + mTracker->setMemoryPool(mMemoryPool); + mTracker->setBz(mTimeFrame.getBz()); + const float elapsedMs = mTracker->clustersToTracks(); + if (elapsedMs < 0.f) { + LOGP(warn, "{} CA tracking failed for this TF", detName()); + return elapsedMs; + } + LOGP(info, "{} CA tracking produced {} tracks in {:.2f} ms", + detName(), mTimeFrame.getNumberOfTracks(), elapsedMs); + return elapsedMs; +} + +template +void ITSMFTTrackingInterface::configureTrackingTopology() +{ + if (mTrackParams.empty()) { + return; + } + mTimeFrame.initDefaultTrackingTopology(mTrackParams[0], NLayers); + mTimeFrame.initTrackerTopologies(mTrackParams); +} + +template +void ITSMFTTrackingInterface::configureBeamPosition() +{ + if (mTrackParams.empty()) { + return; + } + const auto& p = mTrackParams[0]; + TrackingLoadPolicyN::configureBeamPosition(mTimeFrame, p, mMeanVertex, mOverrideBeamEstimation); +} + +template +void ITSMFTTrackingInterface::configureROFLookupTables() +{ + if constexpr (DetId == o2::detectors::DetID::MFT) { + const bool continuous = o2::base::GRPGeomHelper::instance().getGRPECS()->isDetContinuousReadOut(DetId); + LOGP(info, "{} CA tracker RO: continuous = {}", detName(), continuous); + mMFTTriggered = !continuous; + const auto& alpParams = o2::itsmft::DPLAlpideParam::Instance(); + if (mMFTTriggered) { + mMFTROFrameLengthInBC = std::max(1, static_cast(alpParams.roFrameLengthTrig / (o2::constants::lhc::LHCBunchSpacingNS * 1e3))); + } else { + mMFTROFrameLengthInBC = alpParams.roFrameLengthInBC; + } + } + + const auto& par = o2::itsmft::DPLAlpideParam::Instance(); + const int nOrbitsPerTF = o2::base::GRPGeomHelper::getNHBFPerTF(); + + ROFOverlapTableN rofTable; + ROFVertexLookupTableN vtxTable; + for (int iLayer = 0; iLayer < NLayers; ++iLayer) { + const unsigned int nROFsPerOrbit = o2::constants::lhc::LHCMaxBunches / par.getROFLengthInBC(iLayer); + const o2::its::LayerTiming timing{ + .mNROFsTF = nROFsPerOrbit * static_cast(nOrbitsPerTF), + .mROFLength = static_cast(par.getROFLengthInBC(iLayer)), + .mROFDelay = static_cast(par.getROFDelayInBC(iLayer)), + .mROFBias = static_cast(par.getROFBiasInBC(iLayer)), + .mROFAddTimeErr = mTrackParams.empty() + ? static_cast(o2::itsmft::tracking::TrackerParamRef::get().addTimeError[iLayer]) + : mTrackParams[0].AddTimeError[iLayer]}; + rofTable.defineLayer(iLayer, timing); + vtxTable.defineLayer(iLayer, timing); + } + + const auto nROFsLayer0 = rofTable.getLayer(0).mNROFsTF; + for (int iLayer = 1; iLayer < NLayers; ++iLayer) { + if (rofTable.getLayer(iLayer).mNROFsTF != nROFsLayer0) { + LOGP(fatal, + "{} CA single CLUSTERSROF input requires identical mNROFsTF on all {} layers (layer 0: {}, layer {}: {})", + detName(), NLayers, nROFsLayer0, iLayer, rofTable.getLayer(iLayer).mNROFsTF); + } + } + + rofTable.init(); + mTimeFrame.setROFOverlapTable(std::move(rofTable)); + vtxTable.init(); + mTimeFrame.setROFVertexLookupTable(std::move(vtxTable)); +} + +template +void ITSMFTTrackingInterface::configureROFMask(gsl::span rofs, + gsl::span irFrames) +{ + ROFMaskTableN mask{mTimeFrame.getROFOverlapTable()}; + mask.resetMask(); + + if constexpr (DetId == o2::detectors::DetID::MFT) { + const auto& trackingParam = o2::mft::MFTTrackingParam::Instance(); + const bool useIrFilter = trackingParam.irFramesOnly && !irFrames.empty(); + + if (trackingParam.isMultCutRequested()) { + LOGP(info, "{} CA multiplicity filter: Min nClusters = {} ; Max nClusters = {}", + detName(), trackingParam.cutMultClusLow, trackingParam.cutMultClusHigh); + } + if (useIrFilter) { + LOGP(info, "{} CA IRFrame filter enabled with {} ITS IR frames", detName(), irFrames.size()); + } + + const auto nROFs = mTimeFrame.getROFOverlapTableView().getLayer(0).mNROFsTF; + for (int iRof = 0; iRof < static_cast(nROFs); ++iRof) { + bool accept = true; + if (iRof < static_cast(rofs.size())) { + if (useIrFilter) { + accept = rofOverlapsIRFrames(rofs[iRof], mMFTROFrameLengthInBC, irFrames); + } + if (accept && trackingParam.isMultCutRequested()) { + accept = trackingParam.isPassingMultCut(rofs[iRof].getNEntries()); + } + } + if (accept) { + for (int iLayer = 0; iLayer < NLayers; ++iLayer) { + mask.setROFEnabled(iLayer, iRof, 1); + } + } + } + } else { + for (int iLayer = 0; iLayer < NLayers; ++iLayer) { + mask.setROFsEnabled(iLayer, 0, mTimeFrame.getROFOverlapTableView().getLayer(iLayer).mNROFsTF, 1); + } + } + + mTimeFrame.setMultiplicityCutMask(std::move(mask)); +} + +template +void ITSMFTTrackingInterface::validateROFInput(gsl::span rofs) const +{ + const auto expectedROFsTF = mTimeFrame.getROFOverlapTableView().getLayer(0).mNROFsTF; + if (rofs.size() != expectedROFsTF) { + LOGP(warn, "{} CA ROF count differs from continuous timing expectation: received {} expected {}", + detName(), rofs.size(), expectedROFsTF); + } +} + +template class ITSMFTTrackingInterface<7>; +template class ITSMFTTrackingInterface<10>; + +} // namespace o2::itsmft::tracking From 30cd919640cf86feafa50e1dba454a35db148fba Mon Sep 17 00:00:00 2001 From: Maurice Coquet Date: Thu, 18 Jun 2026 13:12:59 +0200 Subject: [PATCH 4/5] Further implementation --- .../include/MFTWorkflow/TrackWriterSpec.h | 2 +- .../ITSMFT/MFT/workflow/src/CATrackerSpec.cxx | 30 +- .../MFT/workflow/src/TrackWriterSpec.cxx | 18 +- .../workflow/src/mft-ca-tracker-workflow.cxx | 2 +- .../include/ITSMFTTracking/CATrackTypes.h | 16 + .../tracking/include/ITSMFTTracking/Cell.h | 85 ++-- .../include/ITSMFTTracking/Configuration.h | 1 - .../include/ITSMFTTracking/DetectorTraits.h | 249 +++++++-- .../include/ITSMFTTracking/IndexTableUtils.h | 58 ++- .../include/ITSMFTTracking/MFTCATrack.h | 4 + .../include/ITSMFTTracking/MFTForwardRefit.h | 4 +- .../ITSMFTTracking/MFTFwdTrackHelpers.h | 481 ++++++++++++++++++ .../include/ITSMFTTracking/TimeFrame.h | 45 +- .../include/ITSMFTTracking/TrackerTraits.h | 3 +- .../ITSMFTTracking/TrackingConfigParam.h | 1 - .../common/tracking/src/Configuration.cxx | 1 - .../common/tracking/src/DetectorTraits.cxx | 135 ++++- .../common/tracking/src/MFTForwardRefit.cxx | 8 +- .../ITSMFT/common/tracking/src/TimeFrame.cxx | 86 +++- .../common/tracking/src/TrackerTraits.cxx | 352 +++++++------ 20 files changed, 1273 insertions(+), 308 deletions(-) create mode 100644 Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/MFTFwdTrackHelpers.h diff --git a/Detectors/ITSMFT/MFT/workflow/include/MFTWorkflow/TrackWriterSpec.h b/Detectors/ITSMFT/MFT/workflow/include/MFTWorkflow/TrackWriterSpec.h index 5a8d50939a25a..a3cab9723315e 100644 --- a/Detectors/ITSMFT/MFT/workflow/include/MFTWorkflow/TrackWriterSpec.h +++ b/Detectors/ITSMFT/MFT/workflow/include/MFTWorkflow/TrackWriterSpec.h @@ -26,7 +26,7 @@ namespace mft /// create a processor spec /// write MFT tracks a root file -o2::framework::DataProcessorSpec getTrackWriterSpec(bool useMC); +o2::framework::DataProcessorSpec getTrackWriterSpec(bool useMC, bool useCA = false); } // namespace mft } // namespace o2 diff --git a/Detectors/ITSMFT/MFT/workflow/src/CATrackerSpec.cxx b/Detectors/ITSMFT/MFT/workflow/src/CATrackerSpec.cxx index f98abd3806bb6..90e75b55aedfc 100644 --- a/Detectors/ITSMFT/MFT/workflow/src/CATrackerSpec.cxx +++ b/Detectors/ITSMFT/MFT/workflow/src/CATrackerSpec.cxx @@ -44,13 +44,17 @@ static_assert(o2::itsmft::tracking::constants::MFTNLayers == o2::mft::constants: namespace { -template +template void fillMFTOutputs(const o2::itsmft::tracking::TimeFrameMFT& tf, gsl::span inputROFs, TracksVec& tracks, ClusterIdxVec& clusterIndices, ROFVec& trackROFs, LabelsVec& trackLabels, + SeedPatternVec& seedPatterns, + ArtefactKeysVec& artefactLabelKeys, + ArtefactMaskVec& artefactTrackletMasks, + ArtefactMaskVec& artefactCellMasks, bool useMC) { trackROFs.assign(inputROFs.begin(), inputROFs.end()); @@ -61,6 +65,7 @@ void fillMFTOutputs(const o2::itsmft::tracking::TimeFrameMFT& tf, const auto& tracksIn = tf.getTracks(); tracks.reserve(tracksIn.size()); + seedPatterns.reserve(tracksIn.size()); if (useMC) { trackLabels.reserve(tracksIn.size()); } @@ -88,6 +93,7 @@ void fillMFTOutputs(const o2::itsmft::tracking::TimeFrameMFT& tf, } dst.setNumberOfPoints(nPoints); tracks.push_back(dst); + seedPatterns.push_back(src.getSeedPattern()); if (useMC && iTrk < tf.getTracksLabel().size()) { trackLabels.push_back(tf.getTracksLabel()[iTrk]); @@ -104,6 +110,16 @@ void fillMFTOutputs(const o2::itsmft::tracking::TimeFrameMFT& tf, trackROFs[iROF].setFirstEntry(rofEntries[iROF]); trackROFs[iROF].setNEntries(rofEntries[iROF + 1] - rofEntries[iROF]); } + + if (useMC) { + std::vector keys; + std::vector trackletMasks; + std::vector cellMasks; + tf.exportMCArtefactCoverage(keys, trackletMasks, cellMasks); + artefactLabelKeys.assign(keys.begin(), keys.end()); + artefactTrackletMasks.assign(trackletMasks.begin(), trackletMasks.end()); + artefactCellMasks.assign(cellMasks.begin(), cellMasks.end()); + } } } // namespace @@ -121,6 +137,10 @@ void CATrackerDPL::run(ProcessingContext& pc) rofsinput.begin(), rofsinput.end()); auto& allTracksMFT = pc.outputs().make>(Output{"MFT", "TRACKS", 0}); auto& allClusIdx = pc.outputs().make>(Output{"MFT", "TRACKCLSID", 0}); + auto& allSeedPatterns = pc.outputs().make>(Output{"MFT", "TRACKSEEDPAT", 0}); + auto& artefactLabelKeys = pc.outputs().make>(Output{"MFT", "TRACKMCEARTKEY", 0}); + auto& artefactTrackletMasks = pc.outputs().make>(Output{"MFT", "TRACKMCEARTTRK", 0}); + auto& artefactCellMasks = pc.outputs().make>(Output{"MFT", "TRACKMCEARTCELL", 0}); std::vector allTrackLabels; if (!mTracking.isActive()) { @@ -155,6 +175,10 @@ void CATrackerDPL::run(ProcessingContext& pc) allClusIdx, trackROFs, allTrackLabels, + allSeedPatterns, + artefactLabelKeys, + artefactTrackletMasks, + artefactCellMasks, mUseMC); LOGP(info, "MFT CA pushed {} tracks in {} ROFs", allTracksMFT.size(), trackROFs.size()); @@ -242,8 +266,12 @@ DataProcessorSpec getCATrackerSpec(bool useMC, bool useGeom, bool useIRFrames, o outputs.emplace_back("MFT", "TRACKS", 0, Lifetime::Timeframe); outputs.emplace_back("MFT", "MFTTrackROF", 0, Lifetime::Timeframe); outputs.emplace_back("MFT", "TRACKCLSID", 0, Lifetime::Timeframe); + outputs.emplace_back("MFT", "TRACKSEEDPAT", 0, Lifetime::Timeframe); if (useMC) { outputs.emplace_back("MFT", "TRACKSMCTR", 0, Lifetime::Timeframe); + outputs.emplace_back("MFT", "TRACKMCEARTKEY", 0, Lifetime::Timeframe); + outputs.emplace_back("MFT", "TRACKMCEARTTRK", 0, Lifetime::Timeframe); + outputs.emplace_back("MFT", "TRACKMCEARTCELL", 0, Lifetime::Timeframe); } return DataProcessorSpec{ diff --git a/Detectors/ITSMFT/MFT/workflow/src/TrackWriterSpec.cxx b/Detectors/ITSMFT/MFT/workflow/src/TrackWriterSpec.cxx index f8a848f6fde32..f0d0867673c9e 100644 --- a/Detectors/ITSMFT/MFT/workflow/src/TrackWriterSpec.cxx +++ b/Detectors/ITSMFT/MFT/workflow/src/TrackWriterSpec.cxx @@ -34,7 +34,7 @@ template using BranchDefinition = MakeRootTreeWriterSpec::BranchDefinition; using namespace o2::header; -DataProcessorSpec getTrackWriterSpec(bool useMC) +DataProcessorSpec getTrackWriterSpec(bool useMC, bool useCA) { // Spectators for logging // this is only to restore the original behavior @@ -53,6 +53,22 @@ DataProcessorSpec getTrackWriterSpec(bool useMC) tracksSizeGetter}, BranchDefinition>{InputSpec{"trackClIdx", "MFT", "TRACKCLSID", 0}, "MFTTrackClusIdx"}, + BranchDefinition>{InputSpec{"trackSeedPat", "MFT", "TRACKSEEDPAT", 0}, + "MFTTrackSeedPattern", + (useCA ? 1 : 0), + ""}, + BranchDefinition>{InputSpec{"artefactKeys", "MFT", "TRACKMCEARTKEY", 0}, + "MFTMCArtefactLabelKeys", + (useCA && useMC ? 1 : 0), + ""}, + BranchDefinition>{InputSpec{"artefactTrkMask", "MFT", "TRACKMCEARTTRK", 0}, + "MFTMCArtefactTrackletMask", + (useCA && useMC ? 1 : 0), + ""}, + BranchDefinition>{InputSpec{"artefactCellMask", "MFT", "TRACKMCEARTCELL", 0}, + "MFTMCArtefactCellMask", + (useCA && useMC ? 1 : 0), + ""}, BranchDefinition>{InputSpec{"ROframes", "MFT", "MFTTrackROF", 0}, "MFTTracksROF", logger}, 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 47b6a0bc89033..8439580f0d59f 100644 --- a/Detectors/ITSMFT/MFT/workflow/src/mft-ca-tracker-workflow.cxx +++ b/Detectors/ITSMFT/MFT/workflow/src/mft-ca-tracker-workflow.cxx @@ -65,7 +65,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& config) WorkflowSpec specs; specs.emplace_back(o2::mft::getCATrackerSpec(useMC, useGeom, useIRFrames, trMode)); if (!disableRootOutput) { - specs.emplace_back(o2::mft::getTrackWriterSpec(useMC)); + specs.emplace_back(o2::mft::getTrackWriterSpec(useMC, true)); } o2::raw::HBFUtilsInitializer hbfIni(config, specs); diff --git a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/CATrackTypes.h b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/CATrackTypes.h index b82cf371e9c1a..05e14e8cccc19 100644 --- a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/CATrackTypes.h +++ b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/CATrackTypes.h @@ -17,7 +17,10 @@ #define ALICEO2_ITSMFT_TRACKING_CATRACKTYPES_H_ #include "DataFormatsITS/TrackITS.h" +#include "ITSMFTTracking/Cell.h" #include "ITSMFTTracking/Constants.h" +#include "ReconstructionDataFormats/Track.h" +#include "ReconstructionDataFormats/TrackFwd.h" namespace o2::itsmft::tracking { @@ -32,6 +35,19 @@ struct CATrackTypeHelper { template using CATrackType = typename CATrackTypeHelper::type; +/// Per-detector track parametrization stored in CA cells and extended seeds. +template +struct CASeedTrackPar { + static constexpr o2::detectors::DetID::ID DetId = constants::detIdFromNLayers(); + using type = std::conditional_t; +}; + +template +using CellSeedN = CellSeedTpl::type>; + +template +using TrackSeedN = TrackSeedTpl::type>; + } // namespace o2::itsmft::tracking #endif /* ALICEO2_ITSMFT_TRACKING_CATRACKTYPES_H_ */ diff --git a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/Cell.h b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/Cell.h index b32ae812319ac..14d60af590837 100644 --- a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/Cell.h +++ b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/Cell.h @@ -18,11 +18,13 @@ #include #include +#include #include "ITStracking/Constants.h" #include "ITSMFTTracking/LayerMask.h" #include "DataFormatsITS/TimeEstBC.h" #include "ReconstructionDataFormats/Track.h" +#include "ReconstructionDataFormats/TrackFwd.h" #include "GPUCommonDef.h" namespace o2::itsmft::tracking @@ -36,12 +38,12 @@ struct CellNeighbour { int level{-1}; }; -template -class SeedBase : public o2::track::TrackParCovF +template +class SeedBase : public TrackParT { public: - GPUhd() LayerMask getHitLayerMask() const { return LayerMask{static_cast(getUserField())}; } - GPUhd() void setHitLayerMask(LayerMask mask) { setUserField(mask.value()); } + GPUhd() LayerMask getHitLayerMask() const { return LayerMask{mHitLayerMask}; } + GPUhd() void setHitLayerMask(LayerMask mask) { mHitLayerMask = mask.value(); } GPUhd() int getInnerLayer() const { return getHitLayerMask().first(); } GPUhd() int getFirstTrackletIndex() const { return mTracklets[0]; }; GPUhd() void setFirstTrackletIndex(int trkl) { mTracklets[0] = trkl; }; @@ -55,6 +57,17 @@ class SeedBase : public o2::track::TrackParCovF GPUhd() auto& getTimeStamp() noexcept { return mTime; } GPUhd() const auto& getTimeStamp() const noexcept { return mTime; } + /// Road-length filter: barrel q/pT² for ITS, (invQPt)² for forward MFT seeds. + GPUhd() float getQ2Pt() const + { + if constexpr (std::is_same_v) { + const float invQPt = static_cast(TrackParT::getInvQPt()); + return invQPt * invQPt; + } else { + return TrackParT::getQ2Pt(); + } + } + protected: GPUhdDefault() SeedBase() = default; GPUhdDefault() SeedBase(const SeedBase&) = default; @@ -62,14 +75,15 @@ 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 o2::its::TimeEstBC& time) - : o2::track::TrackParCovF(tpc), mChi2(chi2), mLevel(level), mTime(time) + GPUhd() SeedBase(const TrackParT& tpc, float chi2, int level, const o2::its::TimeEstBC& time) + : TrackParT(tpc), mChi2(chi2), mLevel(level), mTime(time) { } GPUhd() auto& clustersRaw() { return mClusters; } GPUhd() const auto& clustersRaw() const { return mClusters; } private: + uint16_t mHitLayerMask{0}; float mChi2{o2::its::constants::UnsetValue}; int mLevel{o2::its::constants::UnusedIndex}; std::array mTracklets = o2::its::constants::helpers::initArray(); @@ -77,32 +91,33 @@ class SeedBase : public o2::track::TrackParCovF o2::its::TimeEstBC mTime; }; -class CellSeed final : public SeedBase +template +class CellSeedTpl 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 o2::its::TimeEstBC& time) - : CellSeed(LayerMask(innerL, innerL + 1, innerL + 2), cl0, cl1, cl2, trkl0, trkl1, tpc, chi2, time) + GPUhdDefault() CellSeedTpl() = default; + GPUhd() CellSeedTpl(int innerL, int cl0, int cl1, int cl2, int trkl0, int trkl1, const TrackParT& tpc, float chi2, const o2::its::TimeEstBC& time) + : CellSeedTpl(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 o2::its::TimeEstBC& time) + GPUhd() CellSeedTpl(LayerMask hitLayerMask, int cl0, int cl1, int cl2, int trkl0, int trkl1, const TrackParT& tpc, float chi2, const o2::its::TimeEstBC& time) : Base(tpc, chi2, 1, time) { - setHitLayerMask(hitLayerMask); + this->setHitLayerMask(hitLayerMask); auto& clusters = this->clustersRaw(); clusters[0] = cl0; clusters[1] = cl1; clusters[2] = cl2; - setFirstTrackletIndex(trkl0); - setSecondTrackletIndex(trkl1); + this->setFirstTrackletIndex(trkl0); + this->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; + GPUhdDefault() CellSeedTpl(const CellSeedTpl&) = default; + GPUhdDefault() ~CellSeedTpl() = default; + GPUhdDefault() CellSeedTpl(CellSeedTpl&&) = default; + GPUhdDefault() CellSeedTpl& operator=(const CellSeedTpl&) = default; + GPUhdDefault() CellSeedTpl& operator=(CellSeedTpl&&) = default; GPUhd() int getFirstClusterIndex() const { return this->clustersRaw()[0]; }; GPUhd() int getSecondClusterIndex() const { return this->clustersRaw()[1]; }; @@ -111,20 +126,23 @@ class CellSeed final : public SeedBase GPUhd() const auto& getClusters() const { return this->clustersRaw(); } GPUhd() int getCluster(int layer) const { - const int slot = getHitLayerMask().slot(layer); + const int slot = this->getHitLayerMask().slot(layer); return (slot >= 0 && slot < o2::its::constants::ClustersPerCell) ? this->clustersRaw()[slot] : o2::its::constants::UnusedIndex; } }; -template -class TrackSeed final : public SeedBase +/// ITS default: barrel track parameters in cells. +using CellSeed = CellSeedTpl; + +template +class TrackSeedTpl final : public SeedBase { - using Base = SeedBase; + using Base = SeedBase; public: - GPUhdDefault() TrackSeed() = default; - GPUhd() TrackSeed(const CellSeed& cs) - : Base(static_cast(cs), cs.getChi2(), cs.getLevel(), cs.getTimeStamp()) + GPUhdDefault() TrackSeedTpl() = default; + GPUhd() TrackSeedTpl(const CellSeedTpl& cs) + : Base(static_cast(cs), cs.getChi2(), cs.getLevel(), cs.getTimeStamp()) { this->setHitLayerMask(cs.getHitLayerMask()); this->setFirstTrackletIndex(cs.getFirstTrackletIndex()); @@ -138,11 +156,11 @@ class TrackSeed final : public SeedBase } } } - GPUhdDefault() TrackSeed(const TrackSeed&) = default; - GPUhdDefault() ~TrackSeed() = default; - GPUhdDefault() TrackSeed(TrackSeed&&) = default; - GPUhdDefault() TrackSeed& operator=(const TrackSeed&) = default; - GPUhdDefault() TrackSeed& operator=(TrackSeed&&) = default; + GPUhdDefault() TrackSeedTpl(const TrackSeedTpl&) = default; + GPUhdDefault() ~TrackSeedTpl() = default; + GPUhdDefault() TrackSeedTpl(TrackSeedTpl&&) = default; + GPUhdDefault() TrackSeedTpl& operator=(const TrackSeedTpl&) = default; + GPUhdDefault() TrackSeedTpl& operator=(TrackSeedTpl&&) = default; GPUhd() int getFirstClusterIndex() const { return getClusterBySlot(0); } GPUhd() int getSecondClusterIndex() const { return getClusterBySlot(1); } @@ -167,6 +185,9 @@ class TrackSeed final : public SeedBase } }; +template +using TrackSeed = TrackSeedTpl; + } // namespace o2::itsmft::tracking #endif /* ALICEO2_ITSMFT_TRACKING_INCLUDE_CACELL_H_ */ diff --git a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/Configuration.h b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/Configuration.h index 8553c7c99c652..09196c5d3ae89 100644 --- a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/Configuration.h +++ b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/Configuration.h @@ -89,7 +89,6 @@ struct TrackingParameters { float CellDeltaTanLambdaSigma = 0.007f; float CellRoadRCut = 0.05f; // MFT: max distance to seed line (classic ROADclsRCut) float TrackletMinAbsX = 0.f; // MFT: reject clusters/tracks with |x| below this (cm); 0 = disabled - bool PrintHemisphereStats = false; // MFT debug: log x<0 vs x>0 counts per stage /// Fitter parameters o2::base::PropagatorImpl::MatCorrType CorrType = o2::base::PropagatorImpl::MatCorrType::USEMatCorrNONE; float MaxChi2ClusterAttachment = 60.f; diff --git a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/DetectorTraits.h b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/DetectorTraits.h index 500127579a012..c3a4eb14ada40 100644 --- a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/DetectorTraits.h +++ b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/DetectorTraits.h @@ -24,10 +24,13 @@ #include "ITSMFTTracking/Constants.h" #include "ITSMFTTracking/IndexTableUtils.h" #include "ITSMFTTracking/TimeFrame.h" +#include "ITSMFTTracking/Cell.h" #include "ITStracking/BoundedAllocator.h" #include "ITStracking/Cluster.h" #include "ITStracking/Tracklet.h" +#include "ReconstructionDataFormats/Track.h" #include "MFTTracking/Constants.h" +#include "ReconstructionDataFormats/TrackFwd.h" #include #include @@ -58,11 +61,6 @@ inline float mftLayerZ(int layer) return o2::mft::constants::mft::LayerZCoordinate()[layer]; } -inline bool mftPassesMinAbsX(const o2::its::Cluster& cluster, float minAbsX) -{ - return minAbsX <= 0.f || std::abs(cluster.xCoordinate) >= minAbsX; -} - /// Expected cluster z on toLayer for a straight line from the vertex through cluster on fromLayer. inline float mftExpectedZAtLayer(float clusterZ, int fromLayer, int toLayer, float pvZ) { @@ -75,36 +73,195 @@ inline float mftExpectedZAtLayer(float clusterZ, int fromLayer, int toLayer, flo return clusterZ + dzLayers * ((clusterZ - pvZ) / denom); } -/// ITS-style z-resolution estimate with layer-to-layer dz instead of delta-R (MFT forward disks). -inline float mftTrackletSigmaZ(float clusterZ, - float clusterRadius, +/// Per-layer forward MCS angle (TrackParCovFwd::addMCSEffect-style), for quadrature sum over layers. +inline float mftLayerMSAngle(int layer, const TrackingParameters& params) +{ + const float invP = 1.f / params.TrackletMinPt; + const float zLayer = mftLayerZ(layer); + const float rRef = params.LayerRadii[layer]; + const float tanlRef = (std::abs(rRef) > 1e-6f) ? zLayer / rRef : 0.f; + const float absTanl = std::abs(tanlRef); + const float cscLambda = (absTanl > 1e-6f) ? std::sqrt(1.f + tanlRef * tanlRef) / absTanl : 1e6f; + const float pathLengthOverX0 = params.LayerxX0[layer] * cscLambda; + return 0.0136f * invP * std::sqrt(pathLengthOverX0); +} + +/// Straight-line extrapolation of cluster to toLayer through the primary vertex. +inline void mftLineProject(float xCl, float yCl, float zCl, float pvX, float pvY, float pvZ, + int fromLayer, int toLayer, float& xProj, float& yProj) +{ + const float zFrom = mftLayerZ(fromLayer); + const float zTo = mftLayerZ(toLayer); + const float dz0 = zFrom - pvZ; + if (std::abs(dz0) < 1e-6f) { + xProj = xCl; + yProj = yCl; + return; + } + const float w = (zTo - pvZ) / dz0; + xProj = pvX + w * (xCl - pvX); + yProj = pvY + w * (yCl - pvY); +} + +/// Helix extrapolation at TrackletMinPt from cluster on fromLayer to toLayer (forward parametrization). +inline void mftHelixProject(float xCl, float yCl, float zCl, float pvX, float pvY, float pvZ, + int toLayer, float bz, float minPt, float& xProj, float& yProj) +{ + const float zTo = mftLayerZ(toLayer); + const float dxTan = xCl - pvX; + const float dyTan = yCl - pvY; + const float dzTan = zCl - pvZ; + const float drTan = std::sqrt(dxTan * dxTan + dyTan * dyTan); + float invQPt = (minPt > 0.f) ? 1.f / minPt : 0.f; + float tanl = (drTan > 1e-6f) ? -std::abs(dzTan) / drTan : -1.f; + float phi = (drTan > 1e-6f) ? std::atan2(dyTan, dxTan) : 0.f; + if (std::abs(bz) > 0.01f && std::abs(tanl) > 1e-6f) { + const float k = std::abs(o2::constants::math::B2C * bz); + const float hz = (bz > 0.f) ? 1.f : -1.f; + phi -= 0.5f * hz * invQPt * dzTan * k / tanl; + } + ROOT::Math::SVector params{xCl, yCl, phi, tanl, invQPt}; + ROOT::Math::SMatrix> cov{}; + cov(0, 0) = 1.; + cov(1, 1) = 1.; + cov(2, 2) = 1.; + cov(3, 3) = 1.; + const double qptSigma = std::clamp(static_cast(std::abs(invQPt)), 1., 10.); + cov(4, 4) = qptSigma * qptSigma; + o2::track::TrackParCovFwd track{zCl, params, cov, 0.}; + if (std::abs(bz) > 0.01f) { + track.propagateToZhelix(zTo, bz); + } else { + track.propagateToZlinear(zTo); + } + xProj = static_cast(track.getX()); + yProj = static_cast(track.getY()); +} + +/// Project cluster to toLayer: helix at min pT when B is on, otherwise vertex line. +inline void mftTrackletProject(float xCl, float yCl, float zCl, float pvX, float pvY, float pvZ, + int fromLayer, int toLayer, float bz, float minPt, + float& xProj, float& yProj) +{ + if (std::abs(bz) > 0.01f && minPt > 0.f) { + mftHelixProject(xCl, yCl, zCl, pvX, pvY, pvZ, toLayer, bz, minPt, xProj, yProj); + } else { + mftLineProject(xCl, yCl, zCl, pvX, pvY, pvZ, fromLayer, toLayer, xProj, yProj); + } +} + +/// Tracklet x/y resolution at toLayer: vertex line errors + MS + helix bending at min pT. +inline void mftTrackletSigmaXY(float x0, + float y0, + float pvX, + float pvY, float pvZ, - float resolution, + float sigma2X0, + float sigma2Y0, + float sigma2PvX, + float sigma2PvY, + float sigma2PvZ, + int fromLayer, + int toLayer, + float rLayerFrom, float meanDeltaZ, - float msAngle) + float msAngle, + float bendingAngle, + float xProj, + float yProj, + float& sigmaX, + float& sigmaY) +{ + const float zFrom = mftLayerZ(fromLayer); + const float zTo = mftLayerZ(toLayer); + const float dz0 = zFrom - pvZ; + const float tanlRef = (std::abs(rLayerFrom) > 1e-6f) ? zFrom / rLayerFrom : 0.f; + const float sigma2MS = meanDeltaZ * meanDeltaZ * msAngle * msAngle * (tanlRef * tanlRef + 1.f); + if (std::abs(dz0) < o2::its::constants::Tolerance) { + sigmaX = std::sqrt(sigma2X0 + sigma2PvX + sigma2MS); + sigmaY = std::sqrt(sigma2Y0 + sigma2PvY + sigma2MS); + } else { + const float w = (zTo - pvZ) / dz0; + const float invDz0 = w / dz0; + const float sigma2W = invDz0 * invDz0 * sigma2PvZ; + const float dx0 = x0 - pvX; + const float dy0 = y0 - pvY; + const float oneMinusW = 1.f - w; + sigmaX = std::sqrt(oneMinusW * oneMinusW * sigma2PvX + w * w * sigma2X0 + dx0 * dx0 * sigma2W + sigma2MS); + sigmaY = std::sqrt(oneMinusW * oneMinusW * sigma2PvY + w * w * sigma2Y0 + dy0 * dy0 * sigma2W + sigma2MS); + } + const float rProj = std::hypot(xProj, yProj); + if (rProj > 1e-6f && bendingAngle > 0.f) { + const float dr = rProj * bendingAngle; + const float invR = 1.f / rProj; + const float sinPhi = yProj * invR; + const float cosPhi = xProj * invR; + const float sigma2BendX = dr * dr * sinPhi * sinPhi; + const float sigma2BendY = dr * dr * cosPhi * cosPhi; + sigmaX = std::sqrt(sigmaX * sigmaX + sigma2BendX); + sigmaY = std::sqrt(sigmaY * sigmaY + sigma2BendY); + } +} + +/// Diamond vertex z extent used for conical R spread (legacy MFT CA indexing). +inline void mftDiamondZExtents(float pvZ, float pvSigmaZ, float nSigmaCut, float& zVtxMin, float& zVtxMax) { - const float inverseR0 = 1.f / clusterRadius; - const float tanLambda = (clusterZ - pvZ) * inverseR0; - const float dz0 = clusterZ - pvZ; - const float sqInvDeltaZ0 = 1.f / (dz0 * dz0 + o2::its::constants::Tolerance); - return std::sqrt((resolution * resolution * tanLambda * tanLambda * - ((inverseR0 * inverseR0 + sqInvDeltaZ0) * meanDeltaZ * meanDeltaZ + 1.f)) + - (meanDeltaZ * msAngle) * (meanDeltaZ * msAngle)); + const float zSpread = nSigmaCut * pvSigmaZ; + zVtxMin = pvZ - zSpread; + zVtxMax = pvZ + zSpread; } -/// Cone-projected transverse distance squared (replaces phi-difference cut for MFT). -template -inline float mftTrackletTransverseDist2(const o2::its::Cluster& current, - const o2::its::Cluster& next, - int fromLayer, - int toLayer) +/// Radial spread at toLayer from seed radius at fromLayer and diamond z extremes. +inline void mftRSpreadAtLayer(float seedRadius, + float zLayerFrom, + float zLayerTo, + float zVtxMin, + float zVtxMax, + float& rMin, + float& rMax) { - float xProj = 0.f; - float yProj = 0.f; - mftConeProject(current, fromLayer, toLayer, xProj, yProj); - const float dx = next.xCoordinate - xProj; - const float dy = next.yCoordinate - yProj; - return dx * dx + dy * dy; + const float absZFrom = std::abs(zLayerFrom); + const float absZTo = std::abs(zLayerTo); + const float denomMin = zVtxMax + absZFrom; + const float denomMax = absZFrom + zVtxMin; + rMin = (std::abs(denomMin) > 1.e-6f) ? seedRadius * (zVtxMax + absZTo) / denomMin : seedRadius; + rMax = (std::abs(denomMax) > 1.e-6f) ? seedRadius * (absZTo + zVtxMin) / denomMax : seedRadius; + if (rMin > rMax) { + const float tmp = rMin; + rMin = rMax; + rMax = tmp; + } +} + +/// Normalised transverse chi2 in cone-projected x-y using per-axis sigmas. +inline float mftTrackletTransverseChi2(float dx, float dy, float sigmaX, float sigmaY) +{ + const float invSigmaX2 = (sigmaX > 0.f) ? 1.f / (sigmaX * sigmaX) : 0.f; + const float invSigmaY2 = (sigmaY > 0.f) ? 1.f / (sigmaY * sigmaY) : 0.f; + return dx * dx * invSigmaX2 + dy * dy * invSigmaY2; +} + +/// Legacy MFT conical road scale: (1 + dz/z_ref)^2 for layerFrom -> layerTo. +inline float mftConicalRoadR2Scale(int layerFrom, int layerTo) +{ + const float zFrom = mftLayerZ(layerFrom); + if (std::abs(zFrom) < 1e-6f) { + return 1.f; + } + const float dCone = 1.f + (mftLayerZ(layerTo) - zFrom) / zFrom; + return dCone * dCone; +} + +/// Geometric road acceptance with conical scaling of CellRoadRCut (legacy ROADclsRCut behaviour). +inline bool mftGeometricRoadAccept(const o2::its::Cluster& cA, + const o2::its::Cluster& cB, + const o2::its::Cluster& cN, + int refLayerInner, + int neighbourLayer, + float roadRCut) +{ + const float r2Cut = roadRCut * roadRCut * mftConicalRoadR2Scale(refLayerInner, neighbourLayer); + return mftDistanceToSeedSquared(cA, cB, cN) < r2Cut; } } // namespace detail @@ -113,7 +270,8 @@ inline float mftTrackletTransverseDist2(const o2::its::Cluster& current, template struct DetectorTraits { using TrackType = CATrackType; - using TrackSeedN = TrackSeed; + using TrackSeedN = o2::itsmft::tracking::TrackSeedN; + using CellSeedN = o2::itsmft::tracking::CellSeedN; using TimeFrameN = TimeFrame; static constexpr o2::detectors::DetID::ID DetId = constants::detIdFromNLayers(); @@ -132,7 +290,36 @@ struct DetectorTraits { static void configureIndexTableUtils(IndexTableUtils& utils, const TrackingParameters& params); - static bool validateMFTCellClusters(const o2::its::Cluster& c0, const o2::its::Cluster& c1, const o2::its::Cluster& c2, float r2Cut); + /// ITS: helix fit on three clusters; MFT: forward fit (TrackParCovFwd) on three clusters. + static bool fitCellClusters(const int clusId[3], + const int hitLayers[3], + const TimeFrameN& tf, + const TrackingParameters& params, + float bz, + const o2::base::PropagatorImpl* propagator, + o2::track::TrackParCovF& track, + float& chi2); + + /// ITS: propagate barrel seed and χ²-update; MFT: forward-fit seed and attach neighbour cluster. + static bool attachNeighbourToSeed(TrackSeedN& seed, + int neighbourLayer, + int neighbourCluster, + const TimeFrameN& tf, + const TrackingParameters& params, + float bz, + const o2::base::PropagatorImpl* propagator); + + /// ITS: barrel χ² between connected cells; MFT: forward χ² between connected cells. + static bool cellsAreCompatible(const CellSeedN& currentCell, + const CellSeedN& nextCell, + const TimeFrameN& tf, + const TrackingParameters& params, + float bz); + + static bool validateMFTCellClusters(const o2::its::Cluster& c0, int layer0, + const o2::its::Cluster& c1, int layer1, + const o2::its::Cluster& c2, int layer2, + float r2Cut); static bool mftCellsConnect(const o2::its::Cluster& cEnd, const o2::its::Cluster& cStart, float r2Cut); }; diff --git a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/IndexTableUtils.h b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/IndexTableUtils.h index 9d9a2c859a670..3bf00c727d272 100644 --- a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/IndexTableUtils.h +++ b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/IndexTableUtils.h @@ -180,13 +180,14 @@ GPUhdi() int4 getBinsPhiZ(float phi, const int layerIndex, /// MFT: row = y, col = x. template GPUhdi() int4 getBinsXY(float x, float y, const int layerIndex, - float x1, float x2, float maxDeltaCol, float maxDeltaRow, + float x1, float x2, float y1, float y2, + float maxDeltaCol, float maxDeltaRow, const IndexTableUtils& utils) { const float colRangeMin = o2::gpu::GPUCommonMath::Min(x1, x2) - maxDeltaCol; - const float rowRangeMin = y - maxDeltaRow; + const float rowRangeMin = o2::gpu::GPUCommonMath::Min(y1, y2) - maxDeltaRow; const float colRangeMax = o2::gpu::GPUCommonMath::Max(x1, x2) + maxDeltaCol; - const float rowRangeMax = y + maxDeltaRow; + const float rowRangeMax = o2::gpu::GPUCommonMath::Max(y1, y2) + maxDeltaRow; const float colHalf = utils.getLayerColHalfExtent(layerIndex); if (colRangeMax < -colHalf || colRangeMin > colHalf || colRangeMin > colRangeMax) { @@ -199,28 +200,59 @@ GPUhdi() int4 getBinsXY(float x, float y, const int layerIndex, utils.getRowBinIndex(rowRangeMax)}; } -/// MFT cone projection from one half-layer to another (used for x-y index-table lookup only). +/// MFT: extrapolate cluster to toLayer on the line through the primary vertex. template -GPUhdi() void mftConeProject(const o2::its::Cluster& cluster, int fromLayer, int toLayer, float& xProj, float& yProj) +GPUhdi() void mftConeProject(const o2::its::Cluster& cluster, int fromLayer, int toLayer, + float pvX, float pvY, float pvZ, float& xProj, float& yProj) { const auto& layerZ = o2::mft::constants::mft::LayerZCoordinate(); - const auto& invLayerZ = o2::mft::constants::mft::InverseLayerZCoordinate(); - const float scale = (layerZ[toLayer] - layerZ[fromLayer]) * invLayerZ[fromLayer]; - xProj = cluster.xCoordinate * (1.f + scale); - yProj = cluster.yCoordinate * (1.f + scale); + const float zFrom = layerZ[fromLayer]; + const float zTo = layerZ[toLayer]; + const float dz0 = zFrom - pvZ; + if (o2::gpu::GPUCommonMath::Abs(dz0) < 1e-6f) { + xProj = cluster.xCoordinate; + yProj = cluster.yCoordinate; + return; + } + const float w = (zTo - pvZ) / dz0; + xProj = pvX + w * (cluster.xCoordinate - pvX); + yProj = pvY + w * (cluster.yCoordinate - pvY); +} + +/// MFT LUT window around a precomputed (x, y) projection on toLayer. +template +GPUhdi() int4 getBinsRectClusterAtProj(float xProj, float yProj, int toLayer, + float colRangeMin, float colRangeMax, float maxDeltaCol, float maxDeltaRow, + const IndexTableUtils& utils) +{ + const float rProj = o2::gpu::GPUCommonMath::Hypot(xProj, yProj); + float x1 = xProj; + float x2 = xProj; + float y1 = yProj; + float y2 = yProj; + if (rProj > 0.f) { + const float invRProj = 1.f / rProj; + x1 = colRangeMin * xProj * invRProj; + x2 = colRangeMax * xProj * invRProj; + y1 = colRangeMin * yProj * invRProj; + y2 = colRangeMax * yProj * invRProj; + } + return getBinsXY(xProj, yProj, toLayer, x1, x2, y1, y2, maxDeltaCol, maxDeltaRow, utils); } -/// Cluster-driven LUT window: phi-z for ITS, cone-projected x-y for MFT. +/// Cluster-driven LUT window: phi-z for ITS, projected x-y for MFT. +/// ITS: colRangeMin/Max = z window; MFT: colRangeMin/Max = rMin/rMax at toLayer from diamond z spread. template GPUhdi() int4 getBinsRectCluster(const o2::its::Cluster& cluster, int fromLayer, int toLayer, float colRangeMin, float colRangeMax, float maxDeltaCol, float maxDeltaRow, - const IndexTableUtils& utils) + const IndexTableUtils& utils, + float pvX = 0.f, float pvY = 0.f, float pvZ = 0.f) { if (utils.getCoordType() == IndexTableCoordType::XY) { float xProj = 0.f; float yProj = 0.f; - mftConeProject(cluster, fromLayer, toLayer, xProj, yProj); - return getBinsXY(xProj, yProj, toLayer, xProj, xProj, maxDeltaCol, maxDeltaRow, utils); + mftConeProject(cluster, fromLayer, toLayer, pvX, pvY, pvZ, xProj, yProj); + return getBinsRectClusterAtProj(xProj, yProj, toLayer, colRangeMin, colRangeMax, maxDeltaCol, maxDeltaRow, utils); } return getBinsPhiZ(cluster.phi, toLayer, colRangeMin, colRangeMax, maxDeltaCol, maxDeltaRow, utils); } diff --git a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/MFTCATrack.h b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/MFTCATrack.h index a4420b2e3d8e3..273b88c2f79af 100644 --- a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/MFTCATrack.h +++ b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/MFTCATrack.h @@ -127,6 +127,9 @@ class MFTCATrack auto& getTimeStamp() { return mTime; } const auto& getTimeStamp() const { return mTime; } + uint16_t getSeedPattern() const { return mSeedPattern; } + void setSeedPattern(uint16_t pattern) { mSeedPattern = pattern; } + void setSharedClusters(bool toggle = true) { mClusterSizes = toggle ? (mClusterSizes | kSharedClusters) : (mClusterSizes & ~kSharedClusters); @@ -139,6 +142,7 @@ class MFTCATrack std::array mIndex = {}; uint32_t mPattern = 0; o2::its::TimeStamp mTime; + uint16_t mSeedPattern{0}; uint64_t mClusterSizes = 0; }; diff --git a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/MFTForwardRefit.h b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/MFTForwardRefit.h index 121875df05e1f..4f95111d21a70 100644 --- a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/MFTForwardRefit.h +++ b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/MFTForwardRefit.h @@ -16,7 +16,7 @@ #ifndef ALICEO2_ITSMFT_TRACKING_MFTFORWARDREFIT_H_ #define ALICEO2_ITSMFT_TRACKING_MFTFORWARDREFIT_H_ -#include "ITSMFTTracking/Cell.h" +#include "ITSMFTTracking/CATrackTypes.h" #include "ITSMFTTracking/Configuration.h" #include "ITSMFTTracking/Constants.h" #include "ITSMFTTracking/MFTCATrack.h" @@ -25,7 +25,7 @@ namespace o2::itsmft::tracking { -bool refitTrackFwd(const TrackSeed& seed, +bool refitTrackFwd(const TrackSeedN& seed, MFTCATrack& track, const TimeFrame& tf, const TrackingParameters& params, diff --git a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/MFTFwdTrackHelpers.h b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/MFTFwdTrackHelpers.h new file mode 100644 index 0000000000000..64cecdf93385d --- /dev/null +++ b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/MFTFwdTrackHelpers.h @@ -0,0 +1,481 @@ +// 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 MFTFwdTrackHelpers.h +/// \brief Forward-track helpers for MFT CA cell fitting (TrackParCovFwd) +/// + +#ifndef ALICEO2_ITSMFT_TRACKING_MFTFWDTRACKHELPERS_H_ +#define ALICEO2_ITSMFT_TRACKING_MFTFWDTRACKHELPERS_H_ + +#include +#include +#include +#include + +#include "CommonConstants/MathConstants.h" +#include "ITSMFTTracking/Cell.h" +#include "ITSMFTTracking/CATrackTypes.h" +#include "ITSMFTTracking/Configuration.h" +#include "ITSMFTTracking/LayerMask.h" +#include "ITSMFTTracking/TimeFrame.h" +#include "ITStracking/Cluster.h" +#include "ITStracking/Constants.h" +#include "MFTTracking/Constants.h" +#include "ReconstructionDataFormats/TrackFwd.h" + +namespace o2::itsmft::tracking::detail +{ + +inline int mftLayerAtSlot(LayerMask mask, int requestedSlot) +{ + int slot = 0; + for (int layer = mask.first(); layer <= mask.last(); ++layer) { + if (!mask.has(layer)) { + continue; + } + if (slot++ == requestedSlot) { + return layer; + } + } + return o2::its::constants::UnusedIndex; +} + +namespace mftFwdSeedDefaults +{ +constexpr float kMinDr = 1.e-6f; +constexpr float kMinDz = 1.e-6f; +constexpr float kMinBz = 0.01f; +constexpr float kDefaultSigma2 = 1.f; +} // namespace mftFwdSeedDefaults + +/// Returns true when three clusters form a usable inward MFT seed (inner z > outer z, non-degenerate dr). +inline bool mftFwdCellSeedGeometryValid(const o2::its::Cluster& cInner, + const o2::its::Cluster& cMid, + const o2::its::Cluster& cOuter) +{ + using namespace mftFwdSeedDefaults; + if (cInner.zCoordinate <= cOuter.zCoordinate + kMinDz) { + return false; + } + const float dxTan = cMid.xCoordinate - cInner.xCoordinate; + const float dyTan = cMid.yCoordinate - cInner.yCoordinate; + const float dzTan = cMid.zCoordinate - cInner.zCoordinate; + const float drTan = std::sqrt(dxTan * dxTan + dyTan * dyTan); + if (drTan < kMinDr || std::abs(dzTan) < kMinDz) { + return false; + } + const float dxPhi = cOuter.xCoordinate - cInner.xCoordinate; + const float dyPhi = cOuter.yCoordinate - cInner.yCoordinate; + const float dzPhi = cOuter.zCoordinate - cInner.zCoordinate; + const float drPhi = std::sqrt(dxPhi * dxPhi + dyPhi * dyPhi); + return drPhi >= kMinDr && std::abs(dzPhi) >= kMinDz; +} + +/// Build a TrackParCovFwd seed at the outer cluster, inward-fitting convention (TrackFitter::initTrack). +/// Clusters must be ordered inner → mid → outer (MFT: inner has less negative z). +/// Reference point (x, y, z) is at cOuter; invQPt defaults to 1/minPt when B is on. +inline bool mftBuildFwdTrackSeedInward(const o2::its::Cluster& cInner, + const o2::its::Cluster& cMid, + const o2::its::Cluster& cOuter, + float bz, + float minPt, + o2::track::TrackParCovFwd& track, + float sigma2XOuter = mftFwdSeedDefaults::kDefaultSigma2, + float sigma2YOuter = mftFwdSeedDefaults::kDefaultSigma2) +{ + using namespace mftFwdSeedDefaults; + if (!mftFwdCellSeedGeometryValid(cInner, cMid, cOuter)) { + return false; + } + + const float x0 = cOuter.xCoordinate; + const float y0 = cOuter.yCoordinate; + const float z0 = cOuter.zCoordinate; + + const float dxTan = cMid.xCoordinate - cInner.xCoordinate; + const float dyTan = cMid.yCoordinate - cInner.yCoordinate; + const float dzTan = cMid.zCoordinate - cInner.zCoordinate; + const float drTan = std::sqrt(dxTan * dxTan + dyTan * dyTan); + + const float dxPhi = cOuter.xCoordinate - cInner.xCoordinate; + const float dyPhi = cOuter.yCoordinate - cInner.yCoordinate; + const float dzPhi = cOuter.zCoordinate - cInner.zCoordinate; + const float drPhi = std::sqrt(dxPhi * dxPhi + dyPhi * dyPhi); + + const float invQPt = (minPt > 0.f) ? 1.f / minPt : 0.f; + + float tanl{0.f}; + float phi{0.f}; + if (std::abs(bz) > kMinBz) { + // Magnet on: tan(lambda) from inner→mid, phi from inner→outer with bending correction (TrackFitter inward). + tanl = -std::abs(dzTan) / drTan; + phi = std::atan2(dyPhi, dxPhi); + if (std::abs(tanl) > kMinDz) { + const float k = std::abs(o2::constants::math::B2C * bz); + const float hz = (bz > 0.f) ? 1.f : -1.f; + phi -= 0.5f * hz * invQPt * dzPhi * k / tanl; + } + } else { + // Magnet off: both slopes from inner→outer (TrackFitter linear branch). + tanl = -std::abs(dzPhi) / drPhi; + phi = std::atan2(dyPhi, dxPhi); + } + + ROOT::Math::SVector params{x0, y0, phi, tanl, invQPt}; + ROOT::Math::SMatrix> cov{}; + cov(0, 0) = sigma2XOuter > 0.f ? sigma2XOuter : kDefaultSigma2; + cov(1, 1) = sigma2YOuter > 0.f ? sigma2YOuter : kDefaultSigma2; + cov(2, 2) = 1.; + cov(3, 3) = 1.; + const double qptSigma = std::clamp(static_cast(std::abs(invQPt)), 1., 10.); + cov(4, 4) = qptSigma * qptSigma; + + track = {z0, params, cov, 0.}; + return true; +} + +/// Convenience wrapper for CA cell building: clusters indexed inner → mid → outer in the time frame. +template +inline bool mftBuildFwdTrackSeedFromCellClusters(const int hitLayers[3], + const int clusIds[3], + const TimeFrame& tf, + float bz, + float minPt, + o2::track::TrackParCovFwd& track) +{ + const auto& cInner = tf.getUnsortedClusters()[hitLayers[0]][clusIds[0]]; + const auto& cMid = tf.getUnsortedClusters()[hitLayers[1]][clusIds[1]]; + const auto& cOuter = tf.getUnsortedClusters()[hitLayers[2]][clusIds[2]]; + const auto& tfOuter = tf.getTrackingFrameInfoOnLayer(hitLayers[2])[clusIds[2]]; + return mftBuildFwdTrackSeedInward(cInner, + cMid, + cOuter, + bz, + minPt, + track, + tfOuter.covarianceTrackingFrame[0], + tfOuter.covarianceTrackingFrame[2]); +} + +/// Legacy helper: always returns a track object; invalid geometry yields a default-constructed state. +inline o2::track::TrackParCovFwd mftBuildFwdTrackSeed(const o2::its::Cluster& cInner, + const o2::its::Cluster& cMid, + const o2::its::Cluster& cOuter, + float bz, + float minPt) +{ + o2::track::TrackParCovFwd track; + (void)mftBuildFwdTrackSeedInward(cInner, cMid, cOuter, bz, minPt, track); + return track; +} + +inline void mftFwdPropagateToZ(o2::track::TrackParCovFwd& track, float z, float bz) +{ + if (std::abs(bz) > 0.01f) { + track.propagateToZhelix(z, bz); + } else { + track.propagateToZlinear(z); + } +} + +inline float mftFwdPredictedChi2Quiet(const o2::track::TrackParCovFwd& track, float x, float y, float sigma2X, float sigma2Y) +{ + const float dx = x - static_cast(track.getX()); + const float dy = y - static_cast(track.getY()); + const float vx = static_cast(track.getSigma2X()) + sigma2X; + const float vy = static_cast(track.getSigma2Y()) + sigma2Y; + if (vx <= 0.f || vy <= 0.f) { + return o2::constants::math::VeryBig; + } + return dx * dx / vx + dy * dy / vy; +} + +inline float mftFwdGetPredictedChi2Quiet(const o2::track::TrackParCovFwd& current, const o2::track::TrackParCovFwd& rhs) +{ + ROOT::Math::SVector diff{ + rhs.getX() - current.getX(), + rhs.getY() - current.getY(), + rhs.getPhi() - current.getPhi(), + rhs.getTanl() - current.getTanl(), + rhs.getInvQPt() - current.getInvQPt()}; + auto cov = current.getCovariances(); + cov += rhs.getCovariances(); + if (!cov.Invert()) { + return o2::constants::math::VeryBig; + } + return static_cast(ROOT::Math::Similarity(cov, diff)); +} + +/// ITS getPredictedChi2 analogue: propagate next cell track to current z, compare 5-param states. +inline float mftFwdCellGetPredictedChi2(const o2::track::TrackParCovFwd& current, + o2::track::TrackParCovFwd& next, + float bz) +{ + mftFwdPropagateToZ(next, static_cast(current.getZ()), bz); + return mftFwdGetPredictedChi2Quiet(current, next); +} + +inline bool mftFwdAttachCluster(o2::track::TrackParCovFwd& track, + float z, + float x, + float y, + float sigma2X, + float sigma2Y, + float xOverX0, + float bz, + float maxChi2, + float& chi2, + bool checkChi2OnLast = false) +{ + mftFwdPropagateToZ(track, z, bz); + if (xOverX0 > 0.f) { + track.addMCSEffect(xOverX0); + } + const float predChi2 = mftFwdPredictedChi2Quiet(track, x, y, sigma2X, sigma2Y); + if (checkChi2OnLast && predChi2 > maxChi2) { + return false; + } + const std::array p{x, y}; + const std::array cov{sigma2X, sigma2Y}; + if (!track.update(p, cov)) { + return false; + } + chi2 += predChi2; + return true; +} + +template +inline bool mftFwdFitThreeClusters(o2::track::TrackParCovFwd& track, + const std::array& hitLayers, + const std::array& clusIds, + const TimeFrame& tf, + const TrackingParameters& params, + float bz, + float& chi2) +{ + chi2 = 0.f; + for (int iC{2}; iC >= 0; --iC) { + const int layer = hitLayers[iC]; + const int clIdx = clusIds[iC]; + const auto& tfInfo = tf.getTrackingFrameInfoOnLayer(layer)[clIdx]; + if (!mftFwdAttachCluster(track, + tfInfo.zCoordinate, + tfInfo.xCoordinate, + tfInfo.yCoordinate, + tfInfo.covarianceTrackingFrame[0], + tfInfo.covarianceTrackingFrame[2], + params.LayerxX0[layer], + bz, + params.MaxChi2ClusterAttachment, + chi2, + iC == 0)) { + return false; + } + } + return true; +} + +/// Build a forward seed from three cell clusters and fit with propagate / MCS / χ² / update. +template +inline bool mftFwdFitCellClusters(const int hitLayers[3], + const int clusIds[3], + const TimeFrame& tf, + const TrackingParameters& params, + float bz, + o2::track::TrackParCovFwd& track, + float& chi2) +{ + if (!mftBuildFwdTrackSeedFromCellClusters(hitLayers, clusIds, tf, bz, params.TrackletMinPt, track)) { + return false; + } + const std::array layersArr{hitLayers[0], hitLayers[1], hitLayers[2]}; + const std::array clusArr{clusIds[0], clusIds[1], clusIds[2]}; + return mftFwdFitThreeClusters(track, layersArr, clusArr, tf, params, bz, chi2); +} + +template +inline bool mftFwdFitCellSeedWithChi2(const CellSeedTpl& cell, + const TimeFrame& tf, + const TrackingParameters& params, + float bz, + o2::track::TrackParCovFwd& fwdTrack, + float& chi2) +{ + const int hitLayers[3]{ + mftLayerAtSlot(cell.getHitLayerMask(), 0), + mftLayerAtSlot(cell.getHitLayerMask(), 1), + mftLayerAtSlot(cell.getHitLayerMask(), 2)}; + const int clusId[3]{cell.getFirstClusterIndex(), cell.getSecondClusterIndex(), cell.getThirdClusterIndex()}; + return mftFwdFitCellClusters(hitLayers, clusId, tf, params, bz, fwdTrack, chi2); +} + +template +inline bool mftFwdFitCellSeed(const CellSeedTpl& cell, + const TimeFrame& tf, + const TrackingParameters& params, + float bz, + o2::track::TrackParCovFwd& fwdTrack) +{ + float chi2{0.f}; + return mftFwdFitCellSeedWithChi2(cell, tf, params, bz, fwdTrack, chi2); +} + +inline o2::track::TrackParCovF mftFwdToBarrelTrack(const o2::track::TrackParCovFwd& fwd) +{ + o2::track::TrackParCovF barrel; + fwd.toBarrelTrackParCov(barrel); + return barrel; +} + +template +inline std::vector> mftSortedHitsFromSeed(const SeedT& seed) +{ + std::vector> hits; + const auto mask = seed.getHitLayerMask(); + for (int layer = mask.first(); layer <= mask.last(); ++layer) { + if (!mask.has(layer)) { + continue; + } + const int clIdx = seed.getCluster(layer); + if (clIdx != o2::its::constants::UnusedIndex) { + hits.emplace_back(layer, clIdx); + } + } + std::sort(hits.begin(), hits.end(), [](const auto& a, const auto& b) { return a.first > b.first; }); + return hits; +} + +template +inline bool mftFwdFitSeedClusters(const SeedT& seed, + const TimeFrame& tf, + const TrackingParameters& params, + float bz, + o2::track::TrackParCovFwd& track, + float& chi2) +{ + const auto hits = mftSortedHitsFromSeed(seed); + if (hits.size() < 3) { + return false; + } + const auto& cOuter = tf.getUnsortedClusters()[hits[0].first][hits[0].second]; + const auto& cInner = tf.getUnsortedClusters()[hits.back().first][hits.back().second]; + const size_t midIdx = (hits.size() <= 3) ? 1u : (hits.size() / 2u); + const auto& cMid = tf.getUnsortedClusters()[hits[midIdx].first][hits[midIdx].second]; + std::array hitLayers{hits.back().first, hits[midIdx].first, hits[0].first}; + std::array clusIds{hits.back().second, hits[midIdx].second, hits[0].second}; + if (!mftBuildFwdTrackSeedInward(cInner, cMid, cOuter, bz, params.TrackletMinPt, track)) { + return false; + } + chi2 = 0.f; + if (!mftFwdFitThreeClusters(track, hitLayers, clusIds, tf, params, bz, chi2)) { + return false; + } + for (size_t i{3}; i < hits.size(); ++i) { + const auto [layer, clIdx] = hits[i]; + const auto& tfInfo = tf.getTrackingFrameInfoOnLayer(layer)[clIdx]; + if (!mftFwdAttachCluster(track, + tfInfo.zCoordinate, + tfInfo.xCoordinate, + tfInfo.yCoordinate, + tfInfo.covarianceTrackingFrame[0], + tfInfo.covarianceTrackingFrame[2], + params.LayerxX0[layer], + bz, + params.MaxChi2ClusterAttachment, + chi2, + true)) { + return false; + } + } + return true; +} + +/// Forward-fit neighbour attachment: propagate stored seed track, χ²-update at neighbour (ITS processNeighbours analogue). +template +inline bool mftFwdAttachNeighbourToSeed(const SeedT& seed, + int neighbourLayer, + int neighbourClIdx, + const TimeFrame& tf, + const TrackingParameters& params, + float bz, + o2::track::TrackParCovFwd& fwdTrack, + float& chi2) +{ + fwdTrack = static_cast(seed); + chi2 = seed.getChi2(); + const auto& trHit = tf.getTrackingFrameInfoOnLayer(neighbourLayer)[neighbourClIdx]; + return mftFwdAttachCluster(fwdTrack, + trHit.zCoordinate, + trHit.xCoordinate, + trHit.yCoordinate, + trHit.covarianceTrackingFrame[0], + trHit.covarianceTrackingFrame[2], + params.LayerxX0[neighbourLayer], + bz, + params.MaxChi2ClusterAttachment, + chi2, + true); +} + +/// χ² compatibility between connected cells using stored forward track states (ITS cellsConnect analogue). +inline bool mftFwdCellsAreCompatible(const CellSeedTpl& currentCell, + const CellSeedTpl& nextCell, + float bz, + float maxChi2) +{ + if (currentCell.getSecondClusterIndex() != nextCell.getFirstClusterIndex()) { + return false; + } + o2::track::TrackParCovFwd nextFwd = static_cast(nextCell); + const auto& currentFwd = static_cast(currentCell); + const float chi2 = mftFwdCellGetPredictedChi2(currentFwd, nextFwd, bz); + return chi2 <= maxChi2; +} + +/// Legacy helper: full refit of both cells then compare (expensive; prefer mftFwdCellsAreCompatible above). +template +inline bool mftFwdCellsAreCompatibleRefit(const CellSeedTpl& currentCell, + const CellSeedTpl& nextCell, + const TimeFrame& tf, + const TrackingParameters& params, + float bz) +{ + o2::track::TrackParCovFwd currentFwd; + o2::track::TrackParCovFwd nextFwd; + float chi2Current{0.f}; + float chi2Next{0.f}; + if (!mftFwdFitCellSeedWithChi2(currentCell, tf, params, bz, currentFwd, chi2Current) || + !mftFwdFitCellSeedWithChi2(nextCell, tf, params, bz, nextFwd, chi2Next)) { + return false; + } + mftFwdPropagateToZ(nextFwd, static_cast(currentFwd.getZ()), bz); + const float chi2 = mftFwdGetPredictedChi2Quiet(currentFwd, nextFwd); + return chi2 <= params.MaxChi2ClusterAttachment; +} + +/// Forward-fit road rescue: extrapolate from seed hits, attach neighbour only. +template +inline bool mftFwdRoadAttachNeighbour(const SeedT& seed, + int neighbourLayer, + int neighbourClIdx, + const TimeFrame& tf, + const TrackingParameters& params, + float bz, + o2::track::TrackParCovFwd& fwdTrack, + float& attachChi2) +{ + return mftFwdAttachNeighbourToSeed(seed, neighbourLayer, neighbourClIdx, tf, params, bz, fwdTrack, attachChi2); +} + +} // namespace o2::itsmft::tracking::detail + +#endif /* ALICEO2_ITSMFT_TRACKING_MFTFWDTRACKHELPERS_H_ */ diff --git a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/TimeFrame.h b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/TimeFrame.h index ff7455228b44c..d5464e75416c0 100644 --- a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/TimeFrame.h +++ b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/TimeFrame.h @@ -14,8 +14,9 @@ #define ALICEO2_ITSMFT_TRACKING_TIMEFRAME_H_ #include -#include +#include #include +#include #include #include #include @@ -64,7 +65,6 @@ 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; @@ -92,7 +92,8 @@ struct TimeFrame { using ROFVertexLookupTableN = o2::its::ROFVertexLookupTable; using ROFMaskTableN = o2::its::ROFMaskTable; using TrackingTopologyN = o2::itsmft::tracking::TrackingTopology; - using TrackSeedN = o2::itsmft::tracking::TrackSeed; + using CellSeedN = o2::itsmft::tracking::CellSeedN; + using TrackSeedN = o2::itsmft::tracking::TrackSeedN; TimeFrame() = default; virtual ~TimeFrame() = default; @@ -206,6 +207,41 @@ struct TimeFrame { auto& getTrackletsLabel(int layer) { return mTrackletLabels[layer]; } auto& getCellsLabel(int layer) { return mCellLabels[layer]; } + struct MCArtefactCoverage { + uint16_t trackletMask{0}; + uint16_t cellMask{0}; + }; + + void clearMCArtefactCoverage() { mMCArtefactCoverage.clear(); } + void recordMCArtefactTracklet(uint64_t labelKey, int fromLayer) + { + if (fromLayer >= 0 && fromLayer < NLayers - 1) { + mMCArtefactCoverage[labelKey].trackletMask |= static_cast(1u << fromLayer); + } + } + void recordMCArtefactCell(uint64_t labelKey, int fromLayer) + { + if (fromLayer >= 0 && fromLayer < NLayers - 1) { + mMCArtefactCoverage[labelKey].cellMask |= static_cast(1u << fromLayer); + } + } + void exportMCArtefactCoverage(std::vector& keys, + std::vector& trackletMasks, + std::vector& cellMasks) const + { + keys.clear(); + trackletMasks.clear(); + cellMasks.clear(); + keys.reserve(mMCArtefactCoverage.size()); + trackletMasks.reserve(mMCArtefactCoverage.size()); + cellMasks.reserve(mMCArtefactCoverage.size()); + for (const auto& entry : mMCArtefactCoverage) { + keys.push_back(entry.first); + trackletMasks.push_back(entry.second.trackletMask); + cellMasks.push_back(entry.second.cellMask); + } + } + bool hasMCinformation() const { return mClusterLabels[0] != nullptr; } void initVertexingTopology(const TrackingParameters& trkParam); void initDefaultTrackingTopology(const TrackingParameters& trkParam, const int maxLayers = NLayers); @@ -307,7 +343,7 @@ struct TimeFrame { std::array, NLayers> mUnsortedClusters; std::vector> mTracklets; - std::vector> mCells; + std::vector> mCells; bounded_vector> mTracks; bounded_vector mTracksLabel; std::vector> mCellsNeighbours; @@ -340,6 +376,7 @@ struct TimeFrame { bounded_vector> mPValphaX; /// PV x and alpha for track propagation std::vector> mTrackletLabels; std::vector> mCellLabels; + std::unordered_map mMCArtefactCoverage; std::vector> mCellsNeighboursLUT; bounded_vector mBogusClusters; /// keep track of clusters with wild coordinates diff --git a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/TrackerTraits.h b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/TrackerTraits.h index b557ddcf59e55..0c63cfaf0dde8 100644 --- a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/TrackerTraits.h +++ b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/TrackerTraits.h @@ -32,7 +32,8 @@ class TrackerTraits public: using TimeFrameN = TimeFrame; using IndexTableUtilsN = o2::itsmft::IndexTableUtils; - using TrackSeedN = TrackSeed; + using CellSeedN = typename TimeFrameN::CellSeedN; + using TrackSeedN = typename TimeFrameN::TrackSeedN; virtual ~TrackerTraits() = default; virtual void adoptTimeFrame(TimeFrameN* tf) { mTimeFrame = tf; } diff --git a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/TrackingConfigParam.h b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/TrackingConfigParam.h index bfb1e134af1ca..c697e4e54cbbe 100644 --- a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/TrackingConfigParam.h +++ b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/TrackingConfigParam.h @@ -127,7 +127,6 @@ struct TrackerParamConfig : public o2::conf::ConfigurableParamHelper0 counts per CA stage O2ParamDef(TrackerParamConfig, getParamName().data()); diff --git a/Detectors/ITSMFT/common/tracking/src/Configuration.cxx b/Detectors/ITSMFT/common/tracking/src/Configuration.cxx index d2a330f79a832..db6f1266f83cd 100644 --- a/Detectors/ITSMFT/common/tracking/src/Configuration.cxx +++ b/Detectors/ITSMFT/common/tracking/src/Configuration.cxx @@ -318,7 +318,6 @@ std::vector getTrackingParameters(detectors::DetID::ID detId if (tc.trackletMinAbsX >= 0.f) { p.TrackletMinAbsX = tc.trackletMinAbsX; } - p.PrintHemisphereStats = tc.printHemisphereStats; for (int iD{0}; iD < 3; ++iD) { p.Diamond[iD] = tc.diamondPos[iD]; } diff --git a/Detectors/ITSMFT/common/tracking/src/DetectorTraits.cxx b/Detectors/ITSMFT/common/tracking/src/DetectorTraits.cxx index ec0494f781492..d8014ed653176 100644 --- a/Detectors/ITSMFT/common/tracking/src/DetectorTraits.cxx +++ b/Detectors/ITSMFT/common/tracking/src/DetectorTraits.cxx @@ -19,6 +19,7 @@ #include "Framework/Logger.h" #include "ITSMFTTracking/MFTForwardRefit.h" +#include "ITSMFTTracking/MFTFwdTrackHelpers.h" #include "ITSMFTTracking/TrackingParamRef.h" #include "ITStracking/TrackHelpers.h" @@ -28,7 +29,7 @@ namespace o2::itsmft::tracking namespace { template -bool refitSeedITS(const TrackSeed& seed, +bool refitSeedITS(const typename DetectorTraits::TrackSeedN& seed, o2::its::TrackITSExt& track, const TrackingParameters& params, float bz, @@ -56,6 +57,125 @@ bool refitSeedITS(const TrackSeed& seed, } } // namespace +template +bool DetectorTraits::fitCellClusters(const int clusId[3], + const int hitLayers[3], + const TimeFrameN& tf, + const TrackingParameters& params, + float bz, + const o2::base::PropagatorImpl* propagator, + o2::track::TrackParCovF& track, + float& chi2) +{ + if constexpr (DetId == o2::detectors::DetID::MFT) { + o2::track::TrackParCovFwd fwdTrack; + if (!detail::mftFwdFitCellClusters(hitLayers, clusId, tf, params, bz, fwdTrack, chi2)) { + return false; + } + track = detail::mftFwdToBarrelTrack(fwdTrack); + return true; + } else { + const auto& cluster1_glo = tf.getUnsortedClusters()[hitLayers[0]][clusId[0]]; + const auto& cluster2_glo = tf.getUnsortedClusters()[hitLayers[1]][clusId[1]]; + const auto& cluster3_tf = tf.getTrackingFrameInfoOnLayer(hitLayers[2])[clusId[2]]; + track = o2::its::track::buildTrackSeed(cluster1_glo, cluster2_glo, cluster3_tf, bz); + chi2 = 0.f; + for (int iC{2}; iC--;) { + const int hitLayer = hitLayers[iC]; + const TrackingFrameInfo& trackingHit = tf.getTrackingFrameInfoOnLayer(hitLayer)[clusId[iC]]; + + if (!track.rotate(trackingHit.alphaTrackingFrame)) { + return false; + } + + if (!track.propagateTo(trackingHit.xTrackingFrame, bz)) { + return false; + } + + if (!track.correctForMaterial(params.LayerxX0[hitLayer], params.LayerxX0[hitLayer] * o2::its::constants::Radl * o2::its::constants::Rho, true)) { + return false; + } + + const auto predChi2{track.getPredictedChi2Quiet(trackingHit.positionTrackingFrame, trackingHit.covarianceTrackingFrame)}; + if (!iC && predChi2 > params.MaxChi2ClusterAttachment) { + return false; + } + + if (!track.o2::track::TrackParCov::update(trackingHit.positionTrackingFrame, trackingHit.covarianceTrackingFrame)) { + return false; + } + + if (!iC) { + chi2 += predChi2; + } + } + return true; + } +} + +template +bool DetectorTraits::attachNeighbourToSeed(TrackSeedN& seed, + int neighbourLayer, + int neighbourCluster, + const TimeFrameN& tf, + const TrackingParameters& params, + float bz, + const o2::base::PropagatorImpl* propagator) +{ + if constexpr (DetId == o2::detectors::DetID::MFT) { + o2::track::TrackParCovFwd fwdTrack; + float chi2 = seed.getChi2(); + if (!detail::mftFwdAttachNeighbourToSeed(seed, neighbourLayer, neighbourCluster, tf, params, bz, fwdTrack, chi2)) { + return false; + } + static_cast(seed) = fwdTrack; + seed.setChi2(chi2); + return true; + } else { + const auto& trHit = tf.getTrackingFrameInfoOnLayer(neighbourLayer)[neighbourCluster]; + + if (!seed.rotate(trHit.alphaTrackingFrame)) { + return false; + } + + if (!propagator->propagateToX(seed, trHit.xTrackingFrame, bz, o2::base::PropagatorImpl::MAX_SIN_PHI, o2::base::PropagatorImpl::MAX_STEP, params.CorrType)) { + return false; + } + + if (params.CorrType == o2::base::PropagatorF::MatCorrType::USEMatCorrNONE) { + if (!seed.correctForMaterial(params.LayerxX0[neighbourLayer], params.LayerxX0[neighbourLayer] * o2::its::constants::Radl * o2::its::constants::Rho, true)) { + return false; + } + } + + auto predChi2{seed.getPredictedChi2Quiet(trHit.positionTrackingFrame, trHit.covarianceTrackingFrame)}; + if ((predChi2 > params.MaxChi2ClusterAttachment) || predChi2 < 0.f) { + return false; + } + seed.setChi2(seed.getChi2() + predChi2); + return seed.o2::track::TrackParCov::update(trHit.positionTrackingFrame, trHit.covarianceTrackingFrame); + } +} + +template +bool DetectorTraits::cellsAreCompatible(const CellSeedN& currentCell, + const CellSeedN& nextCell, + const TimeFrameN& tf, + const TrackingParameters& params, + float bz) +{ + if constexpr (DetId == o2::detectors::DetID::MFT) { + return detail::mftFwdCellsAreCompatible(currentCell, nextCell, bz, params.MaxChi2ClusterAttachment); + } else { + auto nextCellSeed = nextCell; + if (!nextCellSeed.rotate(currentCell.getAlpha()) || + !nextCellSeed.propagateTo(currentCell.getX(), bz)) { + return false; + } + return currentCell.getPredictedChi2(nextCellSeed) <= params.MaxChi2ClusterAttachment; + } +} + template bool DetectorTraits::refitSeed(const TrackSeedN& seed, TrackType& track, @@ -69,7 +189,7 @@ bool DetectorTraits::refitSeed(const TrackSeedN& seed, if constexpr (DetId == o2::detectors::DetID::MFT) { return refitTrackFwd(seed, track, tf, params, bz); } else { - return refitSeedITS(seed, track, params, bz, tfInfos, unsortedClusters, propagator); + return refitSeedITS(seed, track, params, bz, tfInfos, unsortedClusters, propagator); } } @@ -104,12 +224,15 @@ bool DetectorTraits::sameTrackSign(const TrackType& t1, const TrackType } template -bool DetectorTraits::validateMFTCellClusters(const o2::its::Cluster& c0, const o2::its::Cluster& c1, const o2::its::Cluster& c2, float r2Cut) +bool DetectorTraits::validateMFTCellClusters(const o2::its::Cluster& c0, int layer0, + const o2::its::Cluster& c1, int layer1, + const o2::its::Cluster& c2, int layer2, + float r2Cut) { if constexpr (DetId == o2::detectors::DetID::MFT) { - return detail::mftDistanceToSeedSquared(c0, c2, c1) < r2Cut && - detail::mftDistanceToSeedSquared(c0, c1, c2) < r2Cut && - detail::mftDistanceToSeedSquared(c1, c2, c0) < r2Cut; + return detail::mftDistanceToSeedSquared(c0, c2, c1) < r2Cut * detail::mftConicalRoadR2Scale(layer0, layer1) && + detail::mftDistanceToSeedSquared(c0, c1, c2) < r2Cut * detail::mftConicalRoadR2Scale(layer0, layer2) && + detail::mftDistanceToSeedSquared(c1, c2, c0) < r2Cut * detail::mftConicalRoadR2Scale(layer1, layer0); } else { return false; } diff --git a/Detectors/ITSMFT/common/tracking/src/MFTForwardRefit.cxx b/Detectors/ITSMFT/common/tracking/src/MFTForwardRefit.cxx index c5672aa9ed93d..e0fe42d4be13f 100644 --- a/Detectors/ITSMFT/common/tracking/src/MFTForwardRefit.cxx +++ b/Detectors/ITSMFT/common/tracking/src/MFTForwardRefit.cxx @@ -32,7 +32,7 @@ namespace o2::itsmft::tracking namespace { template -bool buildTrackLTF(const TrackSeed& seed, +bool buildTrackLTF(const TrackSeedN& seed, const TimeFrame& tf, const TrackingParameters& params, TrackLTFType& ltf) @@ -78,7 +78,7 @@ bool buildTrackLTF(const TrackSeed& seed, return true; } -void copyClusterRefs(const TrackSeed& seed, +void copyClusterRefs(const TrackSeedN& seed, const TimeFrame& tf, MFTCATrack& track) { @@ -118,7 +118,7 @@ bool fitTrackLTF(TrackLTFType& ltf, float bz) } template -bool refitTrackFwdImpl(const TrackSeed& seed, +bool refitTrackFwdImpl(const TrackSeedN& seed, MFTCATrack& track, const TimeFrame& tf, const TrackingParameters& params, @@ -173,7 +173,7 @@ bool refitTrackFwdImpl(const TrackSeed& seed, } } // namespace -bool refitTrackFwd(const TrackSeed& seed, +bool refitTrackFwd(const TrackSeedN& seed, MFTCATrack& track, const TimeFrame& tf, const TrackingParameters& params, diff --git a/Detectors/ITSMFT/common/tracking/src/TimeFrame.cxx b/Detectors/ITSMFT/common/tracking/src/TimeFrame.cxx index 516071289893c..e2b9cab32d110 100644 --- a/Detectors/ITSMFT/common/tracking/src/TimeFrame.cxx +++ b/Detectors/ITSMFT/common/tracking/src/TimeFrame.cxx @@ -321,36 +321,69 @@ void TimeFrame::initialise(const TrackingParameters& trkParam, const in // 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]); + if constexpr (NLayers == constants::MFTNLayers) { + msAngles[iLayer] = detail::mftLayerMSAngle(iLayer, trkParam); + } else { + 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]); + if constexpr (NLayers == constants::MFTNLayers) { + 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.f; + 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.5f * 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)); + const float x = (r2 * cosTheta1half) - (r1 * cosTheta2half); + const 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))); + // For MFT: bending angle (rad) at TrackletMinPt, used to widen x-y tracklet windows. + 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); + + deepVectorClear(mTracklets[transitionId]); + deepVectorClear(mTrackletLabels[transitionId]); + deepVectorClear(mTrackletsLookupTable[transitionId]); + mTrackletsLookupTable[transitionId].resize(mClusters[transition.fromLayer].size() + 1, 0); + } + } else { + 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.f; + 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); + + deepVectorClear(mTracklets[transitionId]); + deepVectorClear(mTrackletLabels[transitionId]); + deepVectorClear(mTrackletsLookupTable[transitionId]); + mTrackletsLookupTable[transitionId].resize(mClusters[transition.fromLayer].size() + 1, 0); } - 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) { @@ -371,7 +404,7 @@ unsigned long TimeFrame::getArtefactsMemory() const size += sizeof(Tracklet) * trkl.size(); } for (const auto& cells : mCells) { - size += sizeof(CellSeed) * cells.size(); + size += sizeof(CellSeedN) * cells.size(); } for (const auto& cellsN : mCellsNeighbours) { size += sizeof(int) * cellsN.size(); @@ -499,6 +532,7 @@ void TimeFrame::wipe() deepVectorClear(mTrackletLabels); deepVectorClear(mCellLabels); deepVectorClear(mTracksLabel); + mMCArtefactCoverage.clear(); } } diff --git a/Detectors/ITSMFT/common/tracking/src/TrackerTraits.cxx b/Detectors/ITSMFT/common/tracking/src/TrackerTraits.cxx index 83817233aa48f..833eb5114f99b 100644 --- a/Detectors/ITSMFT/common/tracking/src/TrackerTraits.cxx +++ b/Detectors/ITSMFT/common/tracking/src/TrackerTraits.cxx @@ -16,7 +16,6 @@ #include #include #include -#include #include #include @@ -32,12 +31,14 @@ #include "ITSMFTTracking/Configuration.h" #include "ITSMFTTracking/Constants.h" #include "ITSMFTTracking/DetectorTraits.h" +#include "ITSMFTTracking/MFTFwdTrackHelpers.h" #include "ITSMFTTracking/IndexTableUtils.h" #include "ITSMFTTracking/LayerMask.h" #include "ITStracking/ROFLookupTables.h" #include "ITSMFTTracking/TrackerTraits.h" #include "ITStracking/TrackHelpers.h" #include "ITStracking/Tracklet.h" +#include "SimulationDataFormat/MCCompLabel.h" namespace o2::itsmft::tracking { @@ -52,35 +53,61 @@ struct PassMode { using TwoPassInsert = std::integral_constant; }; -struct HemisphereStats { - int xNeg{0}; - int xPos{0}; - int xZero{0}; - void add(float x) - { - if (x < 0.f) { - ++xNeg; - } else if (x > 0.f) { - ++xPos; - } else { - ++xZero; - } +namespace detail +{ +constexpr int kQEDSourceID = 99; + +bool isUsableMCLabel(const MCCompLabel& label) +{ + return label.isValid() && !label.isNoise() && label.getSourceID() != kQEDSourceID; +} + +uint64_t mcLabelKey(const MCCompLabel& label) +{ + return label.getRawValue() & MCCompLabel::maskFull; +} + +template +void recordMCTrackletCoverage(TimeFrame* tf, + const typename TrackingTopology::View& topology, + int transitionId) +{ + const auto& transition = topology.getTransition(static_cast::Id>(transitionId)); + if (transition.toLayer != transition.fromLayer + 1) { + return; } - void log(const char* stage, int iteration) const - { - LOGP(info, "MFT CA iter {} {}: x<0={} x>0={} x=0={} total={}", - iteration, stage, xNeg, xPos, xZero, xNeg + xPos + xZero); + const auto& labels = tf->getTrackletsLabel(transitionId); + for (const auto& lab : labels) { + if (isUsableMCLabel(lab)) { + tf->recordMCArtefactTracklet(mcLabelKey(lab), transition.fromLayer); + } } -}; +} -bool printHemisphereStatsEnabled(const TrackingParameters& params) +template +void recordMCCellCoverage(TimeFrame* tf, + const typename TrackingTopology::View& topology) { - static const bool fromEnv = [] { - const char* v = std::getenv("MFT_CA_PRINT_HEMISPHERE_STATS"); - return v != nullptr && v[0] != '\0' && v[0] != '0'; - }(); - return params.PrintHemisphereStats || fromEnv; + for (typename TrackingTopology::Id cellTopologyId = 0; cellTopologyId < topology.nCells; ++cellTopologyId) { + const auto& cellTopology = topology.getCell(cellTopologyId); + const auto& firstTransition = topology.getTransition(cellTopology.firstTransition); + const auto& secondTransition = topology.getTransition(cellTopology.secondTransition); + const auto& labels = tf->getCellsLabel(cellTopologyId); + for (const auto& lab : labels) { + if (!isUsableMCLabel(lab)) { + continue; + } + const uint64_t key = mcLabelKey(lab); + if (firstTransition.toLayer == firstTransition.fromLayer + 1) { + tf->recordMCArtefactCell(key, firstTransition.fromLayer); + } + if (secondTransition.toLayer == secondTransition.fromLayer + 1) { + tf->recordMCArtefactCell(key, secondTransition.fromLayer); + } + } + } } +} // namespace detail template void TrackerTraits::computeLayerTracklets(const int iteration, int iVertex) @@ -126,11 +153,9 @@ void TrackerTraits::computeLayerTracklets(const int iteration, int iVer const float meanDeltaR = mTrkParams[iteration].LayerRadii[transition.toLayer] - mTrkParams[iteration].LayerRadii[transition.fromLayer]; const float phiCut = mTimeFrame->getTransitionPhiCut(transitionId); const float msAngle = mTimeFrame->getTransitionMSAngle(transitionId); - const bool useXYBins = mTimeFrame->getIndexTableUtils().getCoordType() == IndexTableCoordType::XY; const bool useDiamond = mTrkParams[iteration].UseDiamond; const bool isMFT = DetectorTraits::DetId == o2::detectors::DetID::MFT; const float meanDeltaZ = isMFT ? detail::mftLayerZ(transition.toLayer) - detail::mftLayerZ(transition.fromLayer) : 0.f; - const float minAbsX = isMFT ? mTrkParams[iteration].TrackletMinAbsX : 0.f; for (int iCluster = 0; iCluster < int(layer0.size()); ++iCluster) { const Cluster& currentCluster = layer0[iCluster]; @@ -138,9 +163,6 @@ void TrackerTraits::computeLayerTracklets(const int iteration, int iVer if (mTimeFrame->isClusterUsed(transition.fromLayer, currentCluster.clusterId)) { continue; } - if (isMFT && !detail::mftPassesMinAbsX(currentCluster, minAbsX)) { - continue; - } const float inverseR0 = 1.f / currentCluster.radius; for (int iV = startVtx; iV < endVtx; ++iV) { @@ -151,36 +173,55 @@ void TrackerTraits::computeLayerTracklets(const int iteration, int iVer if (pv.isFlagSet(Vertex::Flags::UPCMode) != mTrkParams[iteration].PassFlags[IterationStep::SelectUPCVertices]) { continue; } - const float resolution = o2::gpu::CAMath::Sqrt(math_utils::Sq(mTimeFrame->getPositionResolution(transition.fromLayer)) + math_utils::Sq(mTrkParams[iteration].PVres) / float(pv.getNContributors())); float colWindow = 0.f; float rowWindow = 0.f; + float sigmaX = 0.f; + float sigmaY = 0.f; float sigmaZ = 0.f; - float zWindowMin = 0.f; - float zWindowMax = 0.f; + float lutRangeMin = 0.f; + float lutRangeMax = 0.f; float xProj = 0.f; float yProj = 0.f; if (isMFT) { - sigmaZ = detail::mftTrackletSigmaZ(currentCluster.zCoordinate, currentCluster.radius, pv.getZ(), resolution, meanDeltaZ, msAngle); - mftConeProject(currentCluster, transition.fromLayer, transition.toLayer, xProj, yProj); - const float zExpected = detail::mftExpectedZAtLayer(currentCluster.zCoordinate, transition.fromLayer, transition.toLayer, pv.getZ()); - zWindowMin = zExpected - sigmaZ * mTrkParams[iteration].NSigmaCut; - zWindowMax = zExpected + sigmaZ * mTrkParams[iteration].NSigmaCut; - const float absZDenom = o2::gpu::CAMath::Max(o2::gpu::CAMath::Abs(detail::mftLayerZ(transition.toLayer) - pv.getZ()), 1.e-3f); - const float sigmaX = sigmaZ * o2::gpu::CAMath::Abs(xProj / absZDenom); + const auto& tfInfo = mTimeFrame->getClusterTrackingFrameInfo(transition.fromLayer, currentCluster); + detail::mftTrackletProject(currentCluster.xCoordinate, currentCluster.yCoordinate, currentCluster.zCoordinate, + pv.getX(), pv.getY(), pv.getZ(), + transition.fromLayer, transition.toLayer, getBz(), mTrkParams[iteration].TrackletMinPt, + xProj, yProj); + detail::mftTrackletSigmaXY(currentCluster.xCoordinate, currentCluster.yCoordinate, + pv.getX(), pv.getY(), pv.getZ(), + tfInfo.covarianceTrackingFrame[0], tfInfo.covarianceTrackingFrame[2], + pv.getSigmaX2(), pv.getSigmaY2(), pv.getSigmaZ2(), + transition.fromLayer, transition.toLayer, + mTrkParams[iteration].LayerRadii[transition.fromLayer], + meanDeltaZ, msAngle, phiCut, xProj, yProj, sigmaX, sigmaY); + float zVtxMin = 0.f; + float zVtxMax = 0.f; + detail::mftDiamondZExtents(pv.getZ(), pv.getSigmaZ(), mTrkParams[iteration].NSigmaCut, zVtxMin, zVtxMax); + detail::mftRSpreadAtLayer(currentCluster.radius, + detail::mftLayerZ(transition.fromLayer), + detail::mftLayerZ(transition.toLayer), + zVtxMin, zVtxMax, + lutRangeMin, lutRangeMax); colWindow = sigmaX * mTrkParams[iteration].NSigmaCut; - rowWindow = phiCut * currentCluster.radius; + rowWindow = sigmaY * mTrkParams[iteration].NSigmaCut; } else { + const float resolution = o2::gpu::CAMath::Sqrt(math_utils::Sq(mTimeFrame->getPositionResolution(transition.fromLayer)) + math_utils::Sq(mTrkParams[iteration].PVres) / float(pv.getNContributors())); const float tanLambda = (currentCluster.zCoordinate - pv.getZ()) * inverseR0; - zWindowMin = tanLambda * (mTimeFrame->getMinR(transition.toLayer) - currentCluster.radius) + currentCluster.zCoordinate; - zWindowMax = tanLambda * (mTimeFrame->getMaxR(transition.toLayer) - currentCluster.radius) + currentCluster.zCoordinate; + lutRangeMin = tanLambda * (mTimeFrame->getMinR(transition.toLayer) - currentCluster.radius) + currentCluster.zCoordinate; + lutRangeMax = tanLambda * (mTimeFrame->getMaxR(transition.toLayer) - currentCluster.radius) + currentCluster.zCoordinate; const float sqInvDeltaZ0 = 1.f / (math_utils::Sq(currentCluster.zCoordinate - pv.getZ()) + constants::Tolerance); sigmaZ = o2::gpu::CAMath::Sqrt((math_utils::Sq(resolution) * math_utils::Sq(tanLambda) * ((math_utils::Sq(inverseR0) + sqInvDeltaZ0) * math_utils::Sq(meanDeltaR) + 1.f)) + math_utils::Sq(meanDeltaR * msAngle)); colWindow = sigmaZ * mTrkParams[iteration].NSigmaCut; rowWindow = phiCut; } - const auto bins = o2::itsmft::getBinsRectCluster(currentCluster, transition.fromLayer, transition.toLayer, - zWindowMin, zWindowMax, colWindow, rowWindow, - mTimeFrame->getIndexTableUtils()); + const auto bins = isMFT + ? o2::itsmft::getBinsRectClusterAtProj(xProj, yProj, transition.toLayer, + lutRangeMin, lutRangeMax, colWindow, rowWindow, + mTimeFrame->getIndexTableUtils()) + : o2::itsmft::getBinsRectCluster(currentCluster, transition.fromLayer, transition.toLayer, + lutRangeMin, lutRangeMax, colWindow, rowWindow, + mTimeFrame->getIndexTableUtils()); if (bins.x < 0) { continue; } @@ -204,8 +245,8 @@ void TrackerTraits::computeLayerTracklets(const int iteration, int iVer const auto& targetIndexTable = mTimeFrame->getIndexTable(targetROF, transition.toLayer); const int colBinRange = (bins.z - bins.x) + 1; for (int iRow = 0; iRow < rowBinsNum; ++iRow) { - const int iRowBin = useXYBins ? (bins.y + iRow) : ((bins.y + iRow) % mTrkParams[iteration].RowBins); - if (useXYBins && iRowBin >= mTrkParams[iteration].RowBins) { + const int iRowBin = isMFT ? (bins.y + iRow) : ((bins.y + iRow) % mTrkParams[iteration].RowBins); + if (isMFT && iRowBin >= mTrkParams[iteration].RowBins) { break; } const int firstBinIdx = mTimeFrame->getIndexTableUtils().getBinIndex(bins.x, iRowBin); @@ -220,18 +261,15 @@ void TrackerTraits::computeLayerTracklets(const int iteration, int iVer if (mTimeFrame->isClusterUsed(transition.toLayer, nextCluster.clusterId)) { continue; } - if (isMFT && !detail::mftPassesMinAbsX(nextCluster, minAbsX)) { - continue; - } bool acceptTracklet = false; float tanL = 0.f; if (isMFT) { - const float zExpected = detail::mftExpectedZAtLayer(currentCluster.zCoordinate, transition.fromLayer, transition.toLayer, pv.getZ()); - const float deltaZ = o2::gpu::CAMath::Abs(zExpected - nextCluster.zCoordinate); - const float transCut2 = rowWindow * rowWindow; - const float transDist2 = detail::mftTrackletTransverseDist2(currentCluster, nextCluster, transition.fromLayer, transition.toLayer); - if (sigmaZ > 0.f && deltaZ / sigmaZ < mTrkParams[iteration].NSigmaCut && transDist2 < transCut2) { + const float dx = nextCluster.xCoordinate - xProj; + const float dy = nextCluster.yCoordinate - yProj; + const float transChi2 = detail::mftTrackletTransverseChi2(dx, dy, sigmaX, sigmaY); + const float nSigmaCut2 = math_utils::Sq(mTrkParams[iteration].NSigmaCut); + if (transChi2 < nSigmaCut2) { acceptTracklet = std::abs(meanDeltaZ) > 1.e-6f; tanL = (currentCluster.zCoordinate - nextCluster.zCoordinate) / meanDeltaZ; } @@ -314,20 +352,6 @@ void TrackerTraits::computeLayerTracklets(const int iteration, int iVer } }); - if constexpr (DetectorTraits::DetId == o2::detectors::DetID::MFT) { - if (printHemisphereStatsEnabled(mTrkParams[iteration])) { - HemisphereStats stats; - for (int transitionId{0}; transitionId < topology.nTransitions; ++transitionId) { - const auto& transition = topology.getTransition(transitionId); - for (const auto& trkl : mTimeFrame->getTracklets()[transitionId]) { - const int clId = mTimeFrame->getClusters()[transition.fromLayer][trkl.firstClusterIndex].clusterId; - stats.add(mTimeFrame->getUnsortedClusters()[transition.fromLayer][clId].xCoordinate); - } - } - stats.log("tracklets", iteration); - } - } - /// Create tracklets labels if (mTimeFrame->hasMCinformation() && mTrkParams[iteration].CreateArtefactLabels) { tbb::parallel_for(0, static_cast(topology.nTransitions), [&](const int transitionId) { @@ -351,6 +375,12 @@ void TrackerTraits::computeLayerTracklets(const int iteration, int iVer } }); } + + if (mTimeFrame->hasMCinformation() && mTrkParams[iteration].CreateArtefactLabels) { + for (int transitionId = 0; transitionId < topology.nTransitions; ++transitionId) { + detail::recordMCTrackletCoverage(mTimeFrame, topology, transitionId); + } + } }); } @@ -367,7 +397,7 @@ void TrackerTraits::computeLayerCells(const int iteration) } mTaskArena->execute([&] { - auto forTrackletCells = [&](auto Tag, int cellTopologyId, bounded_vector& layerCells, int iTracklet, int offset = 0) -> int { + auto forTrackletCells = [&](auto Tag, int cellTopologyId, bounded_vector& layerCells, int iTracklet, int offset = 0) -> int { const auto& cellTopology = topology.getCell(cellTopologyId); const auto& firstTransition = topology.getTransition(cellTopology.firstTransition); const auto& secondTransition = topology.getTransition(cellTopology.secondTransition); @@ -394,18 +424,43 @@ void TrackerTraits::computeLayerCells(const int iteration) mTimeFrame->getClusters()[firstTransition.toLayer][nextTracklet.firstClusterIndex].clusterId, mTimeFrame->getClusters()[secondTransition.toLayer][nextTracklet.secondClusterIndex].clusterId}; const int hitLayers[3]{firstTransition.fromLayer, firstTransition.toLayer, secondTransition.toLayer}; - const auto& cluster1_glo = mTimeFrame->getUnsortedClusters()[firstTransition.fromLayer][clusId[0]]; - const auto& cluster2_glo = mTimeFrame->getUnsortedClusters()[firstTransition.toLayer][clusId[1]]; - const auto& cluster3_glo = mTimeFrame->getUnsortedClusters()[secondTransition.toLayer][clusId[2]]; - const auto& cluster3_tf = mTimeFrame->getTrackingFrameInfoOnLayer(secondTransition.toLayer)[clusId[2]]; - auto track{o2::its::track::buildTrackSeed(cluster1_glo, cluster2_glo, cluster3_tf, mBz)}; float chi2{0.f}; bool good{false}; if constexpr (DetectorTraits::DetId == o2::detectors::DetID::MFT) { + const auto& cluster1_glo = mTimeFrame->getUnsortedClusters()[firstTransition.fromLayer][clusId[0]]; + const auto& cluster2_glo = mTimeFrame->getUnsortedClusters()[firstTransition.toLayer][clusId[1]]; + const auto& cluster3_glo = mTimeFrame->getUnsortedClusters()[secondTransition.toLayer][clusId[2]]; const float r2Cut = mTrkParams[iteration].CellRoadRCut * mTrkParams[iteration].CellRoadRCut; - good = DetectorTraits::validateMFTCellClusters(cluster1_glo, cluster2_glo, cluster3_glo, r2Cut); + if (!DetectorTraits::validateMFTCellClusters(cluster1_glo, hitLayers[0], + cluster2_glo, hitLayers[1], + cluster3_glo, hitLayers[2], + r2Cut)) { + continue; + } + o2::track::TrackParCovFwd fwdTrack; + good = detail::mftFwdFitCellClusters(hitLayers, clusId, *mTimeFrame, mTrkParams[iteration], getBz(), fwdTrack, chi2); + if (good) { + TimeEstBC ts = currentTracklet.getTimeStamp(); + ts += nextTracklet.getTimeStamp(); + if constexpr (decltype(Tag)::value == PassMode::OnePass::value) { + layerCells.emplace_back(cellTopology.hitLayerMask, clusId[0], clusId[1], clusId[2], iTracklet, iNextTracklet, fwdTrack, chi2, ts); + ++foundCells; + } else if constexpr (decltype(Tag)::value == PassMode::TwoPassCount::value) { + ++foundCells; + } else if constexpr (decltype(Tag)::value == PassMode::TwoPassInsert::value) { + layerCells[offset++] = CellSeedN(cellTopology.hitLayerMask, clusId[0], clusId[1], clusId[2], iTracklet, iNextTracklet, fwdTrack, chi2, ts); + ++foundCells; + } else { + static_assert(false, "Unknown mode!"); + } + } } else { + const auto& cluster1_glo = mTimeFrame->getUnsortedClusters()[firstTransition.fromLayer][clusId[0]]; + const auto& cluster2_glo = mTimeFrame->getUnsortedClusters()[firstTransition.toLayer][clusId[1]]; + const auto& cluster3_tf = mTimeFrame->getTrackingFrameInfoOnLayer(secondTransition.toLayer)[clusId[2]]; + auto track{o2::its::track::buildTrackSeed(cluster1_glo, cluster2_glo, cluster3_tf, mBz)}; + for (int iC{2}; iC--;) { const int hitLayer = hitLayers[iC]; const TrackingFrameInfo& trackingHit = mTimeFrame->getTrackingFrameInfoOnLayer(hitLayer)[clusId[iC]]; @@ -434,20 +489,20 @@ void TrackerTraits::computeLayerCells(const int iteration) good = !iC; chi2 += predChi2; } - } - if (good) { - TimeEstBC ts = currentTracklet.getTimeStamp(); - ts += nextTracklet.getTimeStamp(); - if constexpr (decltype(Tag)::value == PassMode::OnePass::value) { - layerCells.emplace_back(cellTopology.hitLayerMask, clusId[0], clusId[1], clusId[2], iTracklet, iNextTracklet, track, chi2, ts); - ++foundCells; - } else if constexpr (decltype(Tag)::value == PassMode::TwoPassCount::value) { - ++foundCells; - } else if constexpr (decltype(Tag)::value == PassMode::TwoPassInsert::value) { - layerCells[offset++] = CellSeed(cellTopology.hitLayerMask, clusId[0], clusId[1], clusId[2], iTracklet, iNextTracklet, track, chi2, ts); - ++foundCells; - } else { - static_assert(false, "Unknown mode!"); + if (good) { + TimeEstBC ts = currentTracklet.getTimeStamp(); + ts += nextTracklet.getTimeStamp(); + if constexpr (decltype(Tag)::value == PassMode::OnePass::value) { + layerCells.emplace_back(cellTopology.hitLayerMask, clusId[0], clusId[1], clusId[2], iTracklet, iNextTracklet, track, chi2, ts); + ++foundCells; + } else if constexpr (decltype(Tag)::value == PassMode::TwoPassCount::value) { + ++foundCells; + } else if constexpr (decltype(Tag)::value == PassMode::TwoPassInsert::value) { + layerCells[offset++] = CellSeedN(cellTopology.hitLayerMask, clusId[0], clusId[1], clusId[2], iTracklet, iNextTracklet, track, chi2, ts); + ++foundCells; + } else { + static_assert(false, "Unknown mode!"); + } } } } @@ -510,19 +565,8 @@ void TrackerTraits::computeLayerCells(const int iteration) } }); - if constexpr (DetectorTraits::DetId == o2::detectors::DetID::MFT) { - if (printHemisphereStatsEnabled(mTrkParams[iteration])) { - HemisphereStats stats; - for (int cellTopologyId = 0; cellTopologyId < topology.nCells; ++cellTopologyId) { - const auto& cellTopology = topology.getCell(cellTopologyId); - const auto& firstTransition = topology.getTransition(cellTopology.firstTransition); - for (const auto& cell : mTimeFrame->getCells()[cellTopologyId]) { - const int clId = cell.getFirstClusterIndex(); - stats.add(mTimeFrame->getUnsortedClusters()[firstTransition.fromLayer][clId].xCoordinate); - } - } - stats.log("cells", iteration); - } + if (mTimeFrame->hasMCinformation() && mTrkParams[iteration].CreateArtefactLabels) { + detail::recordMCCellCoverage(mTimeFrame, topology); } for (int transitionId = 0; transitionId < topology.nTransitions; ++transitionId) { @@ -581,17 +625,20 @@ void TrackerTraits::findCellsNeighbours(const int iteration) } bool neighbourAccepted{false}; + auto nextCellSeed{mTimeFrame->getCells()[nextCellTopologyId][iNextCell]}; /// copy if constexpr (DetectorTraits::DetId == o2::detectors::DetID::MFT) { - // Successor cell inner cluster must match current middle cluster (shared tracklet). - neighbourAccepted = currentCellSeed.getSecondClusterIndex() == nextCellSeedRef.getFirstClusterIndex(); + neighbourAccepted = DetectorTraits::cellsAreCompatible(currentCellSeed, + nextCellSeed, + *mTimeFrame, + mTrkParams[iteration], + getBz()); } else { - auto nextCellSeed{mTimeFrame->getCells()[nextCellTopologyId][iNextCell]}; /// copy if (!nextCellSeed.rotate(currentCellSeed.getAlpha()) || !nextCellSeed.propagateTo(currentCellSeed.getX(), getBz())) { continue; } - float chi2 = currentCellSeed.getPredictedChi2(nextCellSeed); + const float chi2 = currentCellSeed.getPredictedChi2(nextCellSeed); if (chi2 > mTrkParams[iteration].MaxChi2ClusterAttachment) { continue; } @@ -713,60 +760,14 @@ void TrackerTraits::processNeighbours(int iteration, int defaultCellTop seed.getTimeStamp() = currentCell.getTimeStamp(); seed.getTimeStamp() += neighbourCell.getTimeStamp(); - if constexpr (DetectorTraits::DetId == o2::detectors::DetID::MFT) { - const float r2Cut = mTrkParams[iteration].CellRoadRCut * mTrkParams[iteration].CellRoadRCut; - int refLayerA{-1}; - int refLayerB{-1}; - int clIdxA{-1}; - int clIdxB{-1}; - for (int layer = NLayers - 1; layer >= 0; --layer) { - const int clIdx = currentCell.getCluster(layer); - if (clIdx == constants::UnusedIndex) { - continue; - } - if (refLayerA < 0) { - refLayerA = layer; - clIdxA = clIdx; - } else { - refLayerB = layer; - clIdxB = clIdx; - break; - } - } - if (refLayerB < 0) { - continue; - } - const auto& cA = mTimeFrame->getUnsortedClusters()[refLayerA][clIdxA]; - const auto& cB = mTimeFrame->getUnsortedClusters()[refLayerB][clIdxB]; - const auto& cN = mTimeFrame->getUnsortedClusters()[neighbourLayer][neighbourCluster]; - if (detail::mftDistanceToSeedSquared(cA, cB, cN) >= r2Cut) { - continue; - } - } else { - const auto& trHit = mTimeFrame->getTrackingFrameInfoOnLayer(neighbourLayer)[neighbourCluster]; - - if (!seed.rotate(trHit.alphaTrackingFrame)) { - continue; - } - - if (!propagator->propagateToX(seed, trHit.xTrackingFrame, getBz(), o2::base::PropagatorImpl::MAX_SIN_PHI, o2::base::PropagatorImpl::MAX_STEP, mTrkParams[iteration].CorrType)) { - continue; - } - - if (mTrkParams[iteration].CorrType == o2::base::PropagatorF::MatCorrType::USEMatCorrNONE) { - if (!seed.correctForMaterial(mTrkParams[iteration].LayerxX0[neighbourLayer], mTrkParams[iteration].LayerxX0[neighbourLayer] * constants::Radl * constants::Rho, true)) { - continue; - } - } - - auto predChi2{seed.getPredictedChi2Quiet(trHit.positionTrackingFrame, trHit.covarianceTrackingFrame)}; - if ((predChi2 > mTrkParams[iteration].MaxChi2ClusterAttachment) || predChi2 < 0.f) { - continue; - } - seed.setChi2(seed.getChi2() + predChi2); - if (!seed.o2::track::TrackParCov::update(trHit.positionTrackingFrame, trHit.covarianceTrackingFrame)) { - continue; - } + if (!DetectorTraits::attachNeighbourToSeed(seed, + neighbourLayer, + neighbourCluster, + *mTimeFrame, + mTrkParams[iteration], + getBz(), + propagator)) { + continue; } if constexpr (decltype(Tag)::value != PassMode::TwoPassCount::value) { @@ -899,6 +900,9 @@ void TrackerTraits::findRoads(const int iteration) unsortedClusters, propagator); if (refitSuccess) { + if constexpr (DetectorTraits::DetId == o2::detectors::DetID::MFT) { + temporaryTrack.setSeedPattern(trackSeeds[iSeed].getHitLayerMask().value()); + } if constexpr (decltype(Tag)::value == PassMode::OnePass::value) { tracks.push_back(temporaryTrack); } else if constexpr (decltype(Tag)::value == PassMode::TwoPassCount::value) { @@ -943,23 +947,7 @@ void TrackerTraits::findRoads(const int iteration) }); DetectorTraits::sortRefittedTracks(tracks); - const int nTracksBefore = static_cast(mTimeFrame->getTracks().size()); acceptTracks(iteration, tracks, firstClusters); - - if constexpr (DetectorTraits::DetId == o2::detectors::DetID::MFT) { - if (printHemisphereStatsEnabled(mTrkParams[iteration])) { - LOGP(info, "MFT CA iter {} road seeds at level {}: {} refitted: {} accepted: {}", - iteration, startLevel, trackSeeds.size(), tracks.size(), mTimeFrame->getTracks().size() - nTracksBefore); - HemisphereStats stats; - for (int i = nTracksBefore; i < static_cast(mTimeFrame->getTracks().size()); ++i) { - const auto& track = mTimeFrame->getTracks()[i]; - const int firstLayer = track.getFirstClusterLayer(); - const int clId = track.getClusterIndex(firstLayer); - stats.add(mTimeFrame->getUnsortedClusters()[firstLayer][clId].xCoordinate); - } - stats.log("accepted tracks", iteration); - } - } } markTracks(iteration); } @@ -1105,9 +1093,9 @@ void TrackerTraits::setNThreads(int n, std::shared_ptr template class TrackerTraits<7>; template class TrackerTraits<10>; -template void TrackerTraits<7>::processNeighbours(int, int, int, const bounded_vector&, const bounded_vector&, const bounded_vector&, bounded_vector>&, bounded_vector&, bounded_vector&); -template void TrackerTraits<7>::processNeighbours>(int, int, int, const bounded_vector>&, const bounded_vector&, const bounded_vector&, bounded_vector>&, bounded_vector&, bounded_vector&); -template void TrackerTraits<10>::processNeighbours(int, int, int, const bounded_vector&, const bounded_vector&, const bounded_vector&, bounded_vector>&, bounded_vector&, bounded_vector&); -template void TrackerTraits<10>::processNeighbours>(int, int, int, const bounded_vector>&, const bounded_vector&, const bounded_vector&, bounded_vector>&, bounded_vector&, bounded_vector&); +template void TrackerTraits<7>::processNeighbours::CellSeedN>(int, int, int, const bounded_vector::CellSeedN>&, const bounded_vector&, const bounded_vector&, bounded_vector::TrackSeedN>&, bounded_vector&, bounded_vector&); +template void TrackerTraits<7>::processNeighbours::TrackSeedN>(int, int, int, const bounded_vector::TrackSeedN>&, const bounded_vector&, const bounded_vector&, bounded_vector::TrackSeedN>&, bounded_vector&, bounded_vector&); +template void TrackerTraits<10>::processNeighbours::CellSeedN>(int, int, int, const bounded_vector::CellSeedN>&, const bounded_vector&, const bounded_vector&, bounded_vector::TrackSeedN>&, bounded_vector&, bounded_vector&); +template void TrackerTraits<10>::processNeighbours::TrackSeedN>(int, int, int, const bounded_vector::TrackSeedN>&, const bounded_vector&, const bounded_vector&, bounded_vector::TrackSeedN>&, bounded_vector&, bounded_vector&); } // namespace o2::itsmft::tracking From 090076e4d38e0d5bd59d53728e333b8a1f8e84a8 Mon Sep 17 00:00:00 2001 From: Maurice Coquet Date: Thu, 18 Jun 2026 18:00:39 +0200 Subject: [PATCH 5/5] First try of full implemtentation of MFT CA tracking --- .../ITSMFT/MFT/workflow/src/CATrackerSpec.cxx | 25 +- .../MFT/workflow/src/TrackWriterSpec.cxx | 12 - .../ITSMFT/common/tracking/CMakeLists.txt | 2 +- .../include/ITSMFTTracking/CATrackTypes.h | 53 -- .../include/ITSMFTTracking/CATracker.h | 4 +- .../tracking/include/ITSMFTTracking/Cell.h | 28 +- .../include/ITSMFTTracking/Configuration.h | 45 +- .../include/ITSMFTTracking/Constants.h | 59 -- .../include/ITSMFTTracking/DetectorTraits.h | 268 +-------- .../tracking/include/ITSMFTTracking/IOUtils.h | 26 +- .../include/ITSMFTTracking/MFTCATrack.h | 5 +- .../include/ITSMFTTracking/MFTForwardRefit.h | 36 -- .../ITSMFTTracking/MFTFwdTrackHelpers.h | 516 +++++++----------- .../include/ITSMFTTracking/TimeFrame.h | 50 +- .../include/ITSMFTTracking/TrackerTraits.h | 5 +- .../ITSMFTTracking/TrackingConfigParam.h | 29 +- .../ITSMFTTracking/TrackingInterface.h | 9 +- .../include/ITSMFTTracking/TrackingParamRef.h | 47 -- .../ITSMFT/common/tracking/src/CATracker.cxx | 3 +- .../common/tracking/src/Configuration.cxx | 7 +- .../common/tracking/src/DetectorTraits.cxx | 161 ------ .../ITSMFT/common/tracking/src/IOUtils.cxx | 62 +-- ...orwardRefit.cxx => MFTFwdTrackHelpers.cxx} | 125 ++--- .../ITSMFT/common/tracking/src/TimeFrame.cxx | 10 +- .../common/tracking/src/TrackerTraits.cxx | 175 +++--- .../common/tracking/src/TrackingInterface.cxx | 1 - 26 files changed, 450 insertions(+), 1313 deletions(-) delete mode 100644 Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/CATrackTypes.h delete mode 100644 Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/Constants.h delete mode 100644 Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/MFTForwardRefit.h delete mode 100644 Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/TrackingParamRef.h rename Detectors/ITSMFT/common/tracking/src/{MFTForwardRefit.cxx => MFTFwdTrackHelpers.cxx} (56%) diff --git a/Detectors/ITSMFT/MFT/workflow/src/CATrackerSpec.cxx b/Detectors/ITSMFT/MFT/workflow/src/CATrackerSpec.cxx index 90e75b55aedfc..7587f1bff9f93 100644 --- a/Detectors/ITSMFT/MFT/workflow/src/CATrackerSpec.cxx +++ b/Detectors/ITSMFT/MFT/workflow/src/CATrackerSpec.cxx @@ -40,11 +40,10 @@ namespace o2::mft { static_assert(o2::itsmft::tracking::ITSMFTTrackingInterfaceMFT::DetId == o2::detectors::DetID::MFT); -static_assert(o2::itsmft::tracking::constants::MFTNLayers == o2::mft::constants::mft::LayersNumber); namespace { -template +template void fillMFTOutputs(const o2::itsmft::tracking::TimeFrameMFT& tf, gsl::span inputROFs, TracksVec& tracks, @@ -52,9 +51,6 @@ void fillMFTOutputs(const o2::itsmft::tracking::TimeFrameMFT& tf, ROFVec& trackROFs, LabelsVec& trackLabels, SeedPatternVec& seedPatterns, - ArtefactKeysVec& artefactLabelKeys, - ArtefactMaskVec& artefactTrackletMasks, - ArtefactMaskVec& artefactCellMasks, bool useMC) { trackROFs.assign(inputROFs.begin(), inputROFs.end()); @@ -110,16 +106,6 @@ void fillMFTOutputs(const o2::itsmft::tracking::TimeFrameMFT& tf, trackROFs[iROF].setFirstEntry(rofEntries[iROF]); trackROFs[iROF].setNEntries(rofEntries[iROF + 1] - rofEntries[iROF]); } - - if (useMC) { - std::vector keys; - std::vector trackletMasks; - std::vector cellMasks; - tf.exportMCArtefactCoverage(keys, trackletMasks, cellMasks); - artefactLabelKeys.assign(keys.begin(), keys.end()); - artefactTrackletMasks.assign(trackletMasks.begin(), trackletMasks.end()); - artefactCellMasks.assign(cellMasks.begin(), cellMasks.end()); - } } } // namespace @@ -138,9 +124,6 @@ void CATrackerDPL::run(ProcessingContext& pc) auto& allTracksMFT = pc.outputs().make>(Output{"MFT", "TRACKS", 0}); auto& allClusIdx = pc.outputs().make>(Output{"MFT", "TRACKCLSID", 0}); auto& allSeedPatterns = pc.outputs().make>(Output{"MFT", "TRACKSEEDPAT", 0}); - auto& artefactLabelKeys = pc.outputs().make>(Output{"MFT", "TRACKMCEARTKEY", 0}); - auto& artefactTrackletMasks = pc.outputs().make>(Output{"MFT", "TRACKMCEARTTRK", 0}); - auto& artefactCellMasks = pc.outputs().make>(Output{"MFT", "TRACKMCEARTCELL", 0}); std::vector allTrackLabels; if (!mTracking.isActive()) { @@ -176,9 +159,6 @@ void CATrackerDPL::run(ProcessingContext& pc) trackROFs, allTrackLabels, allSeedPatterns, - artefactLabelKeys, - artefactTrackletMasks, - artefactCellMasks, mUseMC); LOGP(info, "MFT CA pushed {} tracks in {} ROFs", allTracksMFT.size(), trackROFs.size()); @@ -269,9 +249,6 @@ DataProcessorSpec getCATrackerSpec(bool useMC, bool useGeom, bool useIRFrames, o outputs.emplace_back("MFT", "TRACKSEEDPAT", 0, Lifetime::Timeframe); if (useMC) { outputs.emplace_back("MFT", "TRACKSMCTR", 0, Lifetime::Timeframe); - outputs.emplace_back("MFT", "TRACKMCEARTKEY", 0, Lifetime::Timeframe); - outputs.emplace_back("MFT", "TRACKMCEARTTRK", 0, Lifetime::Timeframe); - outputs.emplace_back("MFT", "TRACKMCEARTCELL", 0, Lifetime::Timeframe); } return DataProcessorSpec{ diff --git a/Detectors/ITSMFT/MFT/workflow/src/TrackWriterSpec.cxx b/Detectors/ITSMFT/MFT/workflow/src/TrackWriterSpec.cxx index f0d0867673c9e..d12fbd125b5b2 100644 --- a/Detectors/ITSMFT/MFT/workflow/src/TrackWriterSpec.cxx +++ b/Detectors/ITSMFT/MFT/workflow/src/TrackWriterSpec.cxx @@ -57,18 +57,6 @@ DataProcessorSpec getTrackWriterSpec(bool useMC, bool useCA) "MFTTrackSeedPattern", (useCA ? 1 : 0), ""}, - BranchDefinition>{InputSpec{"artefactKeys", "MFT", "TRACKMCEARTKEY", 0}, - "MFTMCArtefactLabelKeys", - (useCA && useMC ? 1 : 0), - ""}, - BranchDefinition>{InputSpec{"artefactTrkMask", "MFT", "TRACKMCEARTTRK", 0}, - "MFTMCArtefactTrackletMask", - (useCA && useMC ? 1 : 0), - ""}, - BranchDefinition>{InputSpec{"artefactCellMask", "MFT", "TRACKMCEARTCELL", 0}, - "MFTMCArtefactCellMask", - (useCA && useMC ? 1 : 0), - ""}, BranchDefinition>{InputSpec{"ROframes", "MFT", "MFTTrackROF", 0}, "MFTTracksROF", logger}, diff --git a/Detectors/ITSMFT/common/tracking/CMakeLists.txt b/Detectors/ITSMFT/common/tracking/CMakeLists.txt index ae81520d636c2..0aaf05f8d8e3f 100644 --- a/Detectors/ITSMFT/common/tracking/CMakeLists.txt +++ b/Detectors/ITSMFT/common/tracking/CMakeLists.txt @@ -33,7 +33,7 @@ o2_add_library(ITSMFTTracking src/TrackerTraits.cxx src/CATracker.cxx src/TrackingInterface.cxx - src/MFTForwardRefit.cxx + src/MFTFwdTrackHelpers.cxx PUBLIC_LINK_LIBRARIES O2::ITSMFTTrackingParams O2::ITStracking diff --git a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/CATrackTypes.h b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/CATrackTypes.h deleted file mode 100644 index 05e14e8cccc19..0000000000000 --- a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/CATrackTypes.h +++ /dev/null @@ -1,53 +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 CATrackTypes.h -/// \brief Per-detector CA track storage type -/// - -#ifndef ALICEO2_ITSMFT_TRACKING_CATRACKTYPES_H_ -#define ALICEO2_ITSMFT_TRACKING_CATRACKTYPES_H_ - -#include "DataFormatsITS/TrackITS.h" -#include "ITSMFTTracking/Cell.h" -#include "ITSMFTTracking/Constants.h" -#include "ReconstructionDataFormats/Track.h" -#include "ReconstructionDataFormats/TrackFwd.h" - -namespace o2::itsmft::tracking -{ - -class MFTCATrack; - -template -struct CATrackTypeHelper { - using type = o2::its::TrackITSExt; -}; - -template -using CATrackType = typename CATrackTypeHelper::type; - -/// Per-detector track parametrization stored in CA cells and extended seeds. -template -struct CASeedTrackPar { - static constexpr o2::detectors::DetID::ID DetId = constants::detIdFromNLayers(); - using type = std::conditional_t; -}; - -template -using CellSeedN = CellSeedTpl::type>; - -template -using TrackSeedN = TrackSeedTpl::type>; - -} // namespace o2::itsmft::tracking - -#endif /* ALICEO2_ITSMFT_TRACKING_CATRACKTYPES_H_ */ diff --git a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/CATracker.h b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/CATracker.h index 7d71b5209a91b..95230baa70c90 100644 --- a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/CATracker.h +++ b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/CATracker.h @@ -67,8 +67,8 @@ class Tracker template using CATracker = Tracker; -using TrackerITS = Tracker; -using TrackerMFT = Tracker; +using TrackerITS = Tracker; +using TrackerMFT = Tracker; using CATrackerITS = TrackerITS; using CATrackerMFT = TrackerMFT; diff --git a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/Cell.h b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/Cell.h index 14d60af590837..403f71b6de49d 100644 --- a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/Cell.h +++ b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/Cell.h @@ -20,9 +20,12 @@ #include #include -#include "ITStracking/Constants.h" -#include "ITSMFTTracking/LayerMask.h" +#include "DetectorsCommonDataFormats/DetID.h" +#include "DataFormatsITS/TrackITS.h" #include "DataFormatsITS/TimeEstBC.h" +#include "ITSMFTTracking/Configuration.h" +#include "ITSMFTTracking/LayerMask.h" +#include "ITStracking/Constants.h" #include "ReconstructionDataFormats/Track.h" #include "ReconstructionDataFormats/TrackFwd.h" #include "GPUCommonDef.h" @@ -188,6 +191,27 @@ class TrackSeedTpl final : public SeedBase template using TrackSeed = TrackSeedTpl; +template +struct CATrackTypeHelper { + using type = o2::its::TrackITSExt; +}; + +template +using CATrackType = typename CATrackTypeHelper::type; + +/// Per-detector track parametrization stored in CA cells and extended seeds. +template +struct CASeedTrackPar { + static constexpr o2::detectors::DetID::ID DetId = detIdFromNLayers(); + using type = std::conditional_t; +}; + +template +using CellSeedN = CellSeedTpl::type>; + +template +using TrackSeedN = TrackSeedTpl::type>; + } // namespace o2::itsmft::tracking #endif /* ALICEO2_ITSMFT_TRACKING_INCLUDE_CACELL_H_ */ diff --git a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/Configuration.h b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/Configuration.h index 09196c5d3ae89..4275d8c3e8c0c 100644 --- a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/Configuration.h +++ b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/Configuration.h @@ -27,8 +27,26 @@ #include "CommonUtils/EnumFlags.h" #include "DetectorsBase/Propagator.h" #include "DetectorsCommonDataFormats/DetID.h" -#include "ITSMFTTracking/Constants.h" #include "ITSMFTTracking/LayerMask.h" +#include "ITSMFTTracking/TrackingConfigParam.h" +#include "MFTTracking/Constants.h" +#include "ITStracking/TrackingConfigParam.h" + +namespace o2::itsmft::tracking +{ + +constexpr int nLayersForDet(o2::detectors::DetID::ID detId) +{ + return detId == o2::detectors::DetID::MFT ? o2::mft::constants::mft::LayersNumber : ITSNLayers; +} + +template +constexpr o2::detectors::DetID::ID detIdFromNLayers() +{ + return NLayers == o2::mft::constants::mft::LayersNumber ? o2::detectors::DetID::MFT : o2::detectors::DetID::ITS; +} + +} // namespace o2::itsmft::tracking namespace o2::itsmft { @@ -60,7 +78,7 @@ struct TrackingParameters { std::string asString() const; IterationSteps PassFlags{IterationStep::FirstPass, IterationStep::RebuildClusterLUT}; - int NLayers = tracking::constants::ITSNLayers; + int NLayers = tracking::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 @@ -175,4 +193,27 @@ struct VertexingParameters { } // namespace o2::itsmft +namespace o2::itsmft::tracking +{ + +/// MFT uses o2::itsmft::TrackerParamConfig; ITS production params stay in O2::ITStracking. +template +struct TrackerParamRef; + +template <> +struct TrackerParamRef { + using Type = o2::itsmft::TrackerParamConfig; + static const Type& get() { return Type::Instance(); } + static constexpr int nLayers() { return Type::getNLayers(); } +}; + +template <> +struct TrackerParamRef { + using Type = o2::its::TrackerParamConfig; + static const Type& get() { return Type::Instance(); } + static constexpr int nLayers() { return ITSNLayers; } +}; + +} // namespace o2::itsmft::tracking + #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 deleted file mode 100644 index e04338dd401b1..0000000000000 --- a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/Constants.h +++ /dev/null @@ -1,59 +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 Constants.h -/// \brief Detector-specific layer counts for shared ITSMFT CA tracking -/// -/// MFT CA layer model (see also TimeFrameMFT and MFT CA workflow): -/// - MFTNLayers = 10 half-disk CA layers (same index as GeometryTGeo::getLayer and -/// MFTTracking/Constants.h LayersNumber). This is the single NLayers used by -/// TrackingParameters, TimeFrame, ROFOverlapTable, and cluster sorting. -/// - Five physical disks are addressed as disk = halfLayer / 2 (see mftDiskForHalfLayer). -/// Do not use disk count as CA NLayers. -/// - ROFOverlapTable stores one LayerTiming per half-layer, filled from -/// MFTAlpideParam index L (roFrameLayer*InBC[L] with fallback to global defaults). -/// - Cluster input is one CLUSTERSROF vector per TF; loadROFrameData(..., layer=-1, ...) -/// distributes clusters to half-layers. All layers must then share the same mNROFsTF; -/// per-layer ROF length stagger is unsupported until the workflow provides per-layer ROFs. - -#ifndef ALICEO2_ITSMFT_TRACKING_INCLUDE_CONSTANTS_H_ -#define ALICEO2_ITSMFT_TRACKING_INCLUDE_CONSTANTS_H_ - -#include "DetectorsCommonDataFormats/DetID.h" - -namespace o2::itsmft::tracking::constants -{ - -constexpr int ITSNLayers = 7; -constexpr int MFTNLayers = 10; -constexpr int MFTDisks = 5; -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; -} - -/// Physical disk index (0..MFTDisks-1) for half-layer L in [0, MFTNLayers). -constexpr int mftDiskForHalfLayer(int halfLayer) -{ - return halfLayer / 2; -} - -template -constexpr o2::detectors::DetID::ID detIdFromNLayers() -{ - return NLayers == MFTNLayers ? o2::detectors::DetID::MFT : o2::detectors::DetID::ITS; -} - -} // namespace o2::itsmft::tracking::constants - -#endif /* ALICEO2_ITSMFT_TRACKING_INCLUDE_CONSTANTS_H_ */ diff --git a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/DetectorTraits.h b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/DetectorTraits.h index c3a4eb14ada40..7f7911b80690e 100644 --- a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/DetectorTraits.h +++ b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/DetectorTraits.h @@ -19,252 +19,17 @@ #include "DetectorsBase/Propagator.h" #include "DetectorsCommonDataFormats/DetID.h" #include "DataFormatsCalibration/MeanVertexObject.h" -#include "ITSMFTTracking/CATrackTypes.h" #include "ITSMFTTracking/Configuration.h" -#include "ITSMFTTracking/Constants.h" #include "ITSMFTTracking/IndexTableUtils.h" #include "ITSMFTTracking/TimeFrame.h" #include "ITSMFTTracking/Cell.h" #include "ITStracking/BoundedAllocator.h" #include "ITStracking/Cluster.h" #include "ITStracking/Tracklet.h" -#include "ReconstructionDataFormats/Track.h" -#include "MFTTracking/Constants.h" -#include "ReconstructionDataFormats/TrackFwd.h" - -#include -#include namespace o2::itsmft::tracking { -namespace detail -{ -inline float mftDistanceToSeedSquared(const o2::its::Cluster& c1, const o2::its::Cluster& c2, const o2::its::Cluster& c) -{ - const float dxSeed = c2.xCoordinate - c1.xCoordinate; - const float dySeed = c2.yCoordinate - c1.yCoordinate; - const float dzSeed = c2.zCoordinate - c1.zCoordinate; - if (std::abs(dzSeed) < 1e-9f) { - return std::numeric_limits::max(); - } - const float invdzSeed = (c.zCoordinate - c1.zCoordinate) / dzSeed; - const float xSeed = c1.xCoordinate + dxSeed * invdzSeed; - const float ySeed = c1.yCoordinate + dySeed * invdzSeed; - const float dx = c.xCoordinate - xSeed; - const float dy = c.yCoordinate - ySeed; - return dx * dx + dy * dy; -} - -inline float mftLayerZ(int layer) -{ - return o2::mft::constants::mft::LayerZCoordinate()[layer]; -} - -/// Expected cluster z on toLayer for a straight line from the vertex through cluster on fromLayer. -inline float mftExpectedZAtLayer(float clusterZ, int fromLayer, int toLayer, float pvZ) -{ - const float zFrom = mftLayerZ(fromLayer); - const float dzLayers = mftLayerZ(toLayer) - zFrom; - const float denom = zFrom - pvZ; - if (std::abs(denom) < 1e-6f) { - return clusterZ + dzLayers; - } - return clusterZ + dzLayers * ((clusterZ - pvZ) / denom); -} - -/// Per-layer forward MCS angle (TrackParCovFwd::addMCSEffect-style), for quadrature sum over layers. -inline float mftLayerMSAngle(int layer, const TrackingParameters& params) -{ - const float invP = 1.f / params.TrackletMinPt; - const float zLayer = mftLayerZ(layer); - const float rRef = params.LayerRadii[layer]; - const float tanlRef = (std::abs(rRef) > 1e-6f) ? zLayer / rRef : 0.f; - const float absTanl = std::abs(tanlRef); - const float cscLambda = (absTanl > 1e-6f) ? std::sqrt(1.f + tanlRef * tanlRef) / absTanl : 1e6f; - const float pathLengthOverX0 = params.LayerxX0[layer] * cscLambda; - return 0.0136f * invP * std::sqrt(pathLengthOverX0); -} - -/// Straight-line extrapolation of cluster to toLayer through the primary vertex. -inline void mftLineProject(float xCl, float yCl, float zCl, float pvX, float pvY, float pvZ, - int fromLayer, int toLayer, float& xProj, float& yProj) -{ - const float zFrom = mftLayerZ(fromLayer); - const float zTo = mftLayerZ(toLayer); - const float dz0 = zFrom - pvZ; - if (std::abs(dz0) < 1e-6f) { - xProj = xCl; - yProj = yCl; - return; - } - const float w = (zTo - pvZ) / dz0; - xProj = pvX + w * (xCl - pvX); - yProj = pvY + w * (yCl - pvY); -} - -/// Helix extrapolation at TrackletMinPt from cluster on fromLayer to toLayer (forward parametrization). -inline void mftHelixProject(float xCl, float yCl, float zCl, float pvX, float pvY, float pvZ, - int toLayer, float bz, float minPt, float& xProj, float& yProj) -{ - const float zTo = mftLayerZ(toLayer); - const float dxTan = xCl - pvX; - const float dyTan = yCl - pvY; - const float dzTan = zCl - pvZ; - const float drTan = std::sqrt(dxTan * dxTan + dyTan * dyTan); - float invQPt = (minPt > 0.f) ? 1.f / minPt : 0.f; - float tanl = (drTan > 1e-6f) ? -std::abs(dzTan) / drTan : -1.f; - float phi = (drTan > 1e-6f) ? std::atan2(dyTan, dxTan) : 0.f; - if (std::abs(bz) > 0.01f && std::abs(tanl) > 1e-6f) { - const float k = std::abs(o2::constants::math::B2C * bz); - const float hz = (bz > 0.f) ? 1.f : -1.f; - phi -= 0.5f * hz * invQPt * dzTan * k / tanl; - } - ROOT::Math::SVector params{xCl, yCl, phi, tanl, invQPt}; - ROOT::Math::SMatrix> cov{}; - cov(0, 0) = 1.; - cov(1, 1) = 1.; - cov(2, 2) = 1.; - cov(3, 3) = 1.; - const double qptSigma = std::clamp(static_cast(std::abs(invQPt)), 1., 10.); - cov(4, 4) = qptSigma * qptSigma; - o2::track::TrackParCovFwd track{zCl, params, cov, 0.}; - if (std::abs(bz) > 0.01f) { - track.propagateToZhelix(zTo, bz); - } else { - track.propagateToZlinear(zTo); - } - xProj = static_cast(track.getX()); - yProj = static_cast(track.getY()); -} - -/// Project cluster to toLayer: helix at min pT when B is on, otherwise vertex line. -inline void mftTrackletProject(float xCl, float yCl, float zCl, float pvX, float pvY, float pvZ, - int fromLayer, int toLayer, float bz, float minPt, - float& xProj, float& yProj) -{ - if (std::abs(bz) > 0.01f && minPt > 0.f) { - mftHelixProject(xCl, yCl, zCl, pvX, pvY, pvZ, toLayer, bz, minPt, xProj, yProj); - } else { - mftLineProject(xCl, yCl, zCl, pvX, pvY, pvZ, fromLayer, toLayer, xProj, yProj); - } -} - -/// Tracklet x/y resolution at toLayer: vertex line errors + MS + helix bending at min pT. -inline void mftTrackletSigmaXY(float x0, - float y0, - float pvX, - float pvY, - float pvZ, - float sigma2X0, - float sigma2Y0, - float sigma2PvX, - float sigma2PvY, - float sigma2PvZ, - int fromLayer, - int toLayer, - float rLayerFrom, - float meanDeltaZ, - float msAngle, - float bendingAngle, - float xProj, - float yProj, - float& sigmaX, - float& sigmaY) -{ - const float zFrom = mftLayerZ(fromLayer); - const float zTo = mftLayerZ(toLayer); - const float dz0 = zFrom - pvZ; - const float tanlRef = (std::abs(rLayerFrom) > 1e-6f) ? zFrom / rLayerFrom : 0.f; - const float sigma2MS = meanDeltaZ * meanDeltaZ * msAngle * msAngle * (tanlRef * tanlRef + 1.f); - if (std::abs(dz0) < o2::its::constants::Tolerance) { - sigmaX = std::sqrt(sigma2X0 + sigma2PvX + sigma2MS); - sigmaY = std::sqrt(sigma2Y0 + sigma2PvY + sigma2MS); - } else { - const float w = (zTo - pvZ) / dz0; - const float invDz0 = w / dz0; - const float sigma2W = invDz0 * invDz0 * sigma2PvZ; - const float dx0 = x0 - pvX; - const float dy0 = y0 - pvY; - const float oneMinusW = 1.f - w; - sigmaX = std::sqrt(oneMinusW * oneMinusW * sigma2PvX + w * w * sigma2X0 + dx0 * dx0 * sigma2W + sigma2MS); - sigmaY = std::sqrt(oneMinusW * oneMinusW * sigma2PvY + w * w * sigma2Y0 + dy0 * dy0 * sigma2W + sigma2MS); - } - const float rProj = std::hypot(xProj, yProj); - if (rProj > 1e-6f && bendingAngle > 0.f) { - const float dr = rProj * bendingAngle; - const float invR = 1.f / rProj; - const float sinPhi = yProj * invR; - const float cosPhi = xProj * invR; - const float sigma2BendX = dr * dr * sinPhi * sinPhi; - const float sigma2BendY = dr * dr * cosPhi * cosPhi; - sigmaX = std::sqrt(sigmaX * sigmaX + sigma2BendX); - sigmaY = std::sqrt(sigmaY * sigmaY + sigma2BendY); - } -} - -/// Diamond vertex z extent used for conical R spread (legacy MFT CA indexing). -inline void mftDiamondZExtents(float pvZ, float pvSigmaZ, float nSigmaCut, float& zVtxMin, float& zVtxMax) -{ - const float zSpread = nSigmaCut * pvSigmaZ; - zVtxMin = pvZ - zSpread; - zVtxMax = pvZ + zSpread; -} - -/// Radial spread at toLayer from seed radius at fromLayer and diamond z extremes. -inline void mftRSpreadAtLayer(float seedRadius, - float zLayerFrom, - float zLayerTo, - float zVtxMin, - float zVtxMax, - float& rMin, - float& rMax) -{ - const float absZFrom = std::abs(zLayerFrom); - const float absZTo = std::abs(zLayerTo); - const float denomMin = zVtxMax + absZFrom; - const float denomMax = absZFrom + zVtxMin; - rMin = (std::abs(denomMin) > 1.e-6f) ? seedRadius * (zVtxMax + absZTo) / denomMin : seedRadius; - rMax = (std::abs(denomMax) > 1.e-6f) ? seedRadius * (absZTo + zVtxMin) / denomMax : seedRadius; - if (rMin > rMax) { - const float tmp = rMin; - rMin = rMax; - rMax = tmp; - } -} - -/// Normalised transverse chi2 in cone-projected x-y using per-axis sigmas. -inline float mftTrackletTransverseChi2(float dx, float dy, float sigmaX, float sigmaY) -{ - const float invSigmaX2 = (sigmaX > 0.f) ? 1.f / (sigmaX * sigmaX) : 0.f; - const float invSigmaY2 = (sigmaY > 0.f) ? 1.f / (sigmaY * sigmaY) : 0.f; - return dx * dx * invSigmaX2 + dy * dy * invSigmaY2; -} - -/// Legacy MFT conical road scale: (1 + dz/z_ref)^2 for layerFrom -> layerTo. -inline float mftConicalRoadR2Scale(int layerFrom, int layerTo) -{ - const float zFrom = mftLayerZ(layerFrom); - if (std::abs(zFrom) < 1e-6f) { - return 1.f; - } - const float dCone = 1.f + (mftLayerZ(layerTo) - zFrom) / zFrom; - return dCone * dCone; -} - -/// Geometric road acceptance with conical scaling of CellRoadRCut (legacy ROADclsRCut behaviour). -inline bool mftGeometricRoadAccept(const o2::its::Cluster& cA, - const o2::its::Cluster& cB, - const o2::its::Cluster& cN, - int refLayerInner, - int neighbourLayer, - float roadRCut) -{ - const float r2Cut = roadRCut * roadRCut * mftConicalRoadR2Scale(refLayerInner, neighbourLayer); - return mftDistanceToSeedSquared(cA, cB, cN) < r2Cut; -} -} // namespace detail - /// Per-detector differences in refit, track acceptance, and index-table setup. /// Everything else stays in TrackerTraits and matches ITS line-for-line. template @@ -273,7 +38,7 @@ struct DetectorTraits { using TrackSeedN = o2::itsmft::tracking::TrackSeedN; using CellSeedN = o2::itsmft::tracking::CellSeedN; using TimeFrameN = TimeFrame; - static constexpr o2::detectors::DetID::ID DetId = constants::detIdFromNLayers(); + static constexpr o2::detectors::DetID::ID DetId = detIdFromNLayers(); static bool refitSeed(const TrackSeedN& seed, TrackType& track, @@ -284,43 +49,14 @@ struct DetectorTraits { const o2::its::Cluster* const unsortedClusters[NLayers], const o2::base::PropagatorImpl* propagator); - static void sortRefittedTracks(bounded_vector& tracks); - static void finalizeAcceptedTrack(TrackType& track); - static bool sameTrackSign(const TrackType& t1, const TrackType& t2); - static void configureIndexTableUtils(IndexTableUtils& utils, const TrackingParameters& params); - /// ITS: helix fit on three clusters; MFT: forward fit (TrackParCovFwd) on three clusters. - static bool fitCellClusters(const int clusId[3], - const int hitLayers[3], - const TimeFrameN& tf, - const TrackingParameters& params, - float bz, - const o2::base::PropagatorImpl* propagator, - o2::track::TrackParCovF& track, - float& chi2); - - /// ITS: propagate barrel seed and χ²-update; MFT: forward-fit seed and attach neighbour cluster. - static bool attachNeighbourToSeed(TrackSeedN& seed, - int neighbourLayer, - int neighbourCluster, - const TimeFrameN& tf, - const TrackingParameters& params, - float bz, - const o2::base::PropagatorImpl* propagator); - /// ITS: barrel χ² between connected cells; MFT: forward χ² between connected cells. static bool cellsAreCompatible(const CellSeedN& currentCell, const CellSeedN& nextCell, const TimeFrameN& tf, const TrackingParameters& params, float bz); - - static bool validateMFTCellClusters(const o2::its::Cluster& c0, int layer0, - const o2::its::Cluster& c1, int layer1, - const o2::its::Cluster& c2, int layer2, - float r2Cut); - static bool mftCellsConnect(const o2::its::Cluster& cEnd, const o2::its::Cluster& cStart, float r2Cut); }; template @@ -332,7 +68,7 @@ struct TrackingLoadPolicy { }; template -using TrackingLoadPolicyN = TrackingLoadPolicy(), NLayers>; +using TrackingLoadPolicyN = TrackingLoadPolicy(), NLayers>; } // namespace o2::itsmft::tracking diff --git a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/IOUtils.h b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/IOUtils.h index d38426df13cac..348f74ff78214 100644 --- a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/IOUtils.h +++ b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/IOUtils.h @@ -45,21 +45,6 @@ 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, @@ -79,7 +64,7 @@ void convertCompactClusters(gsl::span clusters, const TopologyDictionary* dict); template -o2::math_utils::Point3D extractClusterData(const CompClusterExt& c, iterator& iter, const TopologyDictionary* dict, T& sig2Row, T& sig2Col) +o2::math_utils::Point3D extractClusterData(const CompClusterExt& c, iterator& iter, const TopologyDictionary* dict, T& sig2Row, T& sig2Col, unsigned int* clusterSize = nullptr) { auto pattID = c.getPatternID(); sig2Row = DefClusError2Row; @@ -88,12 +73,21 @@ o2::math_utils::Point3D extractClusterData(const CompClusterExt& c, iterator& sig2Row = dict->getErr2X(pattID); sig2Col = dict->getErr2Z(pattID); if (!dict->isGroup(pattID)) { + if (clusterSize != nullptr) { + *clusterSize = dict->getNpixels(pattID); + } return dict->getClusterCoordinates(c); } ClusterPattern patt(iter); + if (clusterSize != nullptr) { + *clusterSize = patt.getNPixels(); + } return dict->getClusterCoordinates(c, patt); } ClusterPattern patt(iter); + if (clusterSize != nullptr) { + *clusterSize = patt.getNPixels(); + } return dict->getClusterCoordinates(c, patt, false); } diff --git a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/MFTCATrack.h b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/MFTCATrack.h index 273b88c2f79af..54b88c948cac6 100644 --- a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/MFTCATrack.h +++ b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/MFTCATrack.h @@ -22,8 +22,9 @@ #include "DataFormatsITS/TimeEstBC.h" #include "DataFormatsITS/TrackITS.h" #include "DataFormatsMFT/TrackMFT.h" -#include "ITSMFTTracking/CATrackTypes.h" +#include "ITSMFTTracking/Cell.h" #include "ITStracking/Constants.h" +#include "MFTTracking/Constants.h" namespace o2::itsmft::tracking { @@ -147,7 +148,7 @@ class MFTCATrack }; template <> -struct CATrackTypeHelper { +struct CATrackTypeHelper { using type = MFTCATrack; }; diff --git a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/MFTForwardRefit.h b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/MFTForwardRefit.h deleted file mode 100644 index 4f95111d21a70..0000000000000 --- a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/MFTForwardRefit.h +++ /dev/null @@ -1,36 +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 MFTForwardRefit.h -/// \brief MFT-specific forward Kalman refit for CA track seeds -/// - -#ifndef ALICEO2_ITSMFT_TRACKING_MFTFORWARDREFIT_H_ -#define ALICEO2_ITSMFT_TRACKING_MFTFORWARDREFIT_H_ - -#include "ITSMFTTracking/CATrackTypes.h" -#include "ITSMFTTracking/Configuration.h" -#include "ITSMFTTracking/Constants.h" -#include "ITSMFTTracking/MFTCATrack.h" -#include "ITSMFTTracking/TimeFrame.h" - -namespace o2::itsmft::tracking -{ - -bool refitTrackFwd(const TrackSeedN& seed, - MFTCATrack& track, - const TimeFrame& tf, - const TrackingParameters& params, - float bz); - -} // namespace o2::itsmft::tracking - -#endif /* ALICEO2_ITSMFT_TRACKING_MFTFORWARDREFIT_H_ */ diff --git a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/MFTFwdTrackHelpers.h b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/MFTFwdTrackHelpers.h index 64cecdf93385d..7302fb004109a 100644 --- a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/MFTFwdTrackHelpers.h +++ b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/MFTFwdTrackHelpers.h @@ -10,22 +10,20 @@ // or submit itself to any jurisdiction. /// /// \file MFTFwdTrackHelpers.h -/// \brief Forward-track helpers for MFT CA cell fitting (TrackParCovFwd) +/// \brief Forward-track helpers for MFT CA cell fitting and final track refit /// #ifndef ALICEO2_ITSMFT_TRACKING_MFTFWDTRACKHELPERS_H_ #define ALICEO2_ITSMFT_TRACKING_MFTFWDTRACKHELPERS_H_ -#include #include #include -#include +#include #include "CommonConstants/MathConstants.h" #include "ITSMFTTracking/Cell.h" -#include "ITSMFTTracking/CATrackTypes.h" #include "ITSMFTTracking/Configuration.h" -#include "ITSMFTTracking/LayerMask.h" +#include "ITSMFTTracking/MFTCATrack.h" #include "ITSMFTTracking/TimeFrame.h" #include "ITStracking/Cluster.h" #include "ITStracking/Constants.h" @@ -35,147 +33,97 @@ namespace o2::itsmft::tracking::detail { -inline int mftLayerAtSlot(LayerMask mask, int requestedSlot) -{ - int slot = 0; - for (int layer = mask.first(); layer <= mask.last(); ++layer) { - if (!mask.has(layer)) { - continue; - } - if (slot++ == requestedSlot) { - return layer; - } - } - return o2::its::constants::UnusedIndex; -} +/// MFT CA uses o2::mft::constants::mft::LayersNumber half-disk layers (same index as GeometryTGeo::getLayer). +/// Physical disk index is halfLayer / 2; ROFOverlapTable stores one LayerTiming per half-layer. -namespace mftFwdSeedDefaults +inline float mftLayerZ(int layer) { -constexpr float kMinDr = 1.e-6f; -constexpr float kMinDz = 1.e-6f; -constexpr float kMinBz = 0.01f; -constexpr float kDefaultSigma2 = 1.f; -} // namespace mftFwdSeedDefaults + return o2::mft::constants::mft::LayerZCoordinate()[layer]; +} -/// Returns true when three clusters form a usable inward MFT seed (inner z > outer z, non-degenerate dr). -inline bool mftFwdCellSeedGeometryValid(const o2::its::Cluster& cInner, - const o2::its::Cluster& cMid, - const o2::its::Cluster& cOuter) +inline float mftLayerMSAngle(int layer, const TrackingParameters& params) { - using namespace mftFwdSeedDefaults; - if (cInner.zCoordinate <= cOuter.zCoordinate + kMinDz) { - return false; - } - const float dxTan = cMid.xCoordinate - cInner.xCoordinate; - const float dyTan = cMid.yCoordinate - cInner.yCoordinate; - const float dzTan = cMid.zCoordinate - cInner.zCoordinate; - const float drTan = std::sqrt(dxTan * dxTan + dyTan * dyTan); - if (drTan < kMinDr || std::abs(dzTan) < kMinDz) { - return false; - } - const float dxPhi = cOuter.xCoordinate - cInner.xCoordinate; - const float dyPhi = cOuter.yCoordinate - cInner.yCoordinate; - const float dzPhi = cOuter.zCoordinate - cInner.zCoordinate; - const float drPhi = std::sqrt(dxPhi * dxPhi + dyPhi * dyPhi); - return drPhi >= kMinDr && std::abs(dzPhi) >= kMinDz; + const float invP = 1.f / params.TrackletMinPt; + const float zLayer = mftLayerZ(layer); + const float rRef = params.LayerRadii[layer]; + const float tanlRef = (std::abs(rRef) > 1e-6f) ? zLayer / rRef : 0.f; + const float absTanl = std::abs(tanlRef); + const float cscLambda = (absTanl > 1e-6f) ? std::sqrt(1.f + tanlRef * tanlRef) / absTanl : 1e6f; + return 0.0136f * invP * std::sqrt(params.LayerxX0[layer] * cscLambda); } -/// Build a TrackParCovFwd seed at the outer cluster, inward-fitting convention (TrackFitter::initTrack). -/// Clusters must be ordered inner → mid → outer (MFT: inner has less negative z). -/// Reference point (x, y, z) is at cOuter; invQPt defaults to 1/minPt when B is on. -inline bool mftBuildFwdTrackSeedInward(const o2::its::Cluster& cInner, - const o2::its::Cluster& cMid, - const o2::its::Cluster& cOuter, - float bz, - float minPt, - o2::track::TrackParCovFwd& track, - float sigma2XOuter = mftFwdSeedDefaults::kDefaultSigma2, - float sigma2YOuter = mftFwdSeedDefaults::kDefaultSigma2) +inline void mftTrackletProject(float xCl, float yCl, float zCl, float pvX, float pvY, float pvZ, + int fromLayer, int toLayer, float bz, float minPt, + float& xProj, float& yProj) { - using namespace mftFwdSeedDefaults; - if (!mftFwdCellSeedGeometryValid(cInner, cMid, cOuter)) { - return false; - } - - const float x0 = cOuter.xCoordinate; - const float y0 = cOuter.yCoordinate; - const float z0 = cOuter.zCoordinate; - - const float dxTan = cMid.xCoordinate - cInner.xCoordinate; - const float dyTan = cMid.yCoordinate - cInner.yCoordinate; - const float dzTan = cMid.zCoordinate - cInner.zCoordinate; - const float drTan = std::sqrt(dxTan * dxTan + dyTan * dyTan); - - const float dxPhi = cOuter.xCoordinate - cInner.xCoordinate; - const float dyPhi = cOuter.yCoordinate - cInner.yCoordinate; - const float dzPhi = cOuter.zCoordinate - cInner.zCoordinate; - const float drPhi = std::sqrt(dxPhi * dxPhi + dyPhi * dyPhi); - - const float invQPt = (minPt > 0.f) ? 1.f / minPt : 0.f; - - float tanl{0.f}; - float phi{0.f}; - if (std::abs(bz) > kMinBz) { - // Magnet on: tan(lambda) from inner→mid, phi from inner→outer with bending correction (TrackFitter inward). - tanl = -std::abs(dzTan) / drTan; - phi = std::atan2(dyPhi, dxPhi); - if (std::abs(tanl) > kMinDz) { + if (std::abs(bz) > 0.01f && minPt > 0.f) { + const float zTo = mftLayerZ(toLayer); + const float dxTan = xCl - pvX; + const float dyTan = yCl - pvY; + const float dzTan = zCl - pvZ; + const float drTan = std::sqrt(dxTan * dxTan + dyTan * dyTan); + float invQPt = 1.f / minPt; + float tanl = (drTan > 1e-6f) ? -std::abs(dzTan) / drTan : -1.f; + float phi = (drTan > 1e-6f) ? std::atan2(dyTan, dxTan) : 0.f; + if (std::abs(tanl) > 1e-6f) { const float k = std::abs(o2::constants::math::B2C * bz); const float hz = (bz > 0.f) ? 1.f : -1.f; - phi -= 0.5f * hz * invQPt * dzPhi * k / tanl; + phi -= 0.5f * hz * invQPt * dzTan * k / tanl; } + ROOT::Math::SVector params{xCl, yCl, phi, tanl, invQPt}; + ROOT::Math::SMatrix> cov{}; + cov(0, 0) = cov(1, 1) = cov(2, 2) = cov(3, 3) = 1.; + const double qptSigma = std::clamp(static_cast(std::abs(invQPt)), 1., 10.); + cov(4, 4) = qptSigma * qptSigma; + o2::track::TrackParCovFwd track{zCl, params, cov, 0.}; + track.propagateToZhelix(zTo, bz); + xProj = static_cast(track.getX()); + yProj = static_cast(track.getY()); } else { - // Magnet off: both slopes from inner→outer (TrackFitter linear branch). - tanl = -std::abs(dzPhi) / drPhi; - phi = std::atan2(dyPhi, dxPhi); + const float dz0 = mftLayerZ(fromLayer) - pvZ; + if (std::abs(dz0) < 1e-6f) { + xProj = xCl; + yProj = yCl; + return; + } + const float w = (mftLayerZ(toLayer) - pvZ) / dz0; + xProj = pvX + w * (xCl - pvX); + yProj = pvY + w * (yCl - pvY); } - - ROOT::Math::SVector params{x0, y0, phi, tanl, invQPt}; - ROOT::Math::SMatrix> cov{}; - cov(0, 0) = sigma2XOuter > 0.f ? sigma2XOuter : kDefaultSigma2; - cov(1, 1) = sigma2YOuter > 0.f ? sigma2YOuter : kDefaultSigma2; - cov(2, 2) = 1.; - cov(3, 3) = 1.; - const double qptSigma = std::clamp(static_cast(std::abs(invQPt)), 1., 10.); - cov(4, 4) = qptSigma * qptSigma; - - track = {z0, params, cov, 0.}; - return true; } -/// Convenience wrapper for CA cell building: clusters indexed inner → mid → outer in the time frame. -template -inline bool mftBuildFwdTrackSeedFromCellClusters(const int hitLayers[3], - const int clusIds[3], - const TimeFrame& tf, - float bz, - float minPt, - o2::track::TrackParCovFwd& track) +inline void mftTrackletSigmaXY(float x0, float y0, float pvX, float pvY, float pvZ, + float sigma2X0, float sigma2Y0, float sigma2PvX, float sigma2PvY, float sigma2PvZ, + int fromLayer, int toLayer, float rLayerFrom, float meanDeltaZ, float msAngle, + float bendingAngle, float xProj, float yProj, float& sigmaX, float& sigmaY) { - const auto& cInner = tf.getUnsortedClusters()[hitLayers[0]][clusIds[0]]; - const auto& cMid = tf.getUnsortedClusters()[hitLayers[1]][clusIds[1]]; - const auto& cOuter = tf.getUnsortedClusters()[hitLayers[2]][clusIds[2]]; - const auto& tfOuter = tf.getTrackingFrameInfoOnLayer(hitLayers[2])[clusIds[2]]; - return mftBuildFwdTrackSeedInward(cInner, - cMid, - cOuter, - bz, - minPt, - track, - tfOuter.covarianceTrackingFrame[0], - tfOuter.covarianceTrackingFrame[2]); -} - -/// Legacy helper: always returns a track object; invalid geometry yields a default-constructed state. -inline o2::track::TrackParCovFwd mftBuildFwdTrackSeed(const o2::its::Cluster& cInner, - const o2::its::Cluster& cMid, - const o2::its::Cluster& cOuter, - float bz, - float minPt) -{ - o2::track::TrackParCovFwd track; - (void)mftBuildFwdTrackSeedInward(cInner, cMid, cOuter, bz, minPt, track); - return track; + const float zFrom = mftLayerZ(fromLayer); + const float zTo = mftLayerZ(toLayer); + const float dz0 = zFrom - pvZ; + const float tanlRef = (std::abs(rLayerFrom) > 1e-6f) ? zFrom / rLayerFrom : 0.f; + const float sigma2MS = meanDeltaZ * meanDeltaZ * msAngle * msAngle * (tanlRef * tanlRef + 1.f); + if (std::abs(dz0) < o2::its::constants::Tolerance) { + sigmaX = std::sqrt(sigma2X0 + sigma2PvX + sigma2MS); + sigmaY = std::sqrt(sigma2Y0 + sigma2PvY + sigma2MS); + } else { + const float w = (zTo - pvZ) / dz0; + const float invDz0 = w / dz0; + const float sigma2W = invDz0 * invDz0 * sigma2PvZ; + const float dx0 = x0 - pvX; + const float dy0 = y0 - pvY; + const float oneMinusW = 1.f - w; + sigmaX = std::sqrt(oneMinusW * oneMinusW * sigma2PvX + w * w * sigma2X0 + dx0 * dx0 * sigma2W + sigma2MS); + sigmaY = std::sqrt(oneMinusW * oneMinusW * sigma2PvY + w * w * sigma2Y0 + dy0 * dy0 * sigma2W + sigma2MS); + } + const float rProj = std::hypot(xProj, yProj); + if (rProj > 1e-6f && bendingAngle > 0.f) { + const float dr = rProj * bendingAngle; + const float invR = 1.f / rProj; + const float sinPhi = yProj * invR; + const float cosPhi = xProj * invR; + sigmaX = std::sqrt(sigmaX * sigmaX + dr * dr * sinPhi * sinPhi); + sigmaY = std::sqrt(sigmaY * sigmaY + dr * dr * cosPhi * cosPhi); + } } inline void mftFwdPropagateToZ(o2::track::TrackParCovFwd& track, float z, float bz) @@ -187,7 +135,7 @@ inline void mftFwdPropagateToZ(o2::track::TrackParCovFwd& track, float z, float } } -inline float mftFwdPredictedChi2Quiet(const o2::track::TrackParCovFwd& track, float x, float y, float sigma2X, float sigma2Y) +inline float mftFwdPredictedChi2(const o2::track::TrackParCovFwd& track, float x, float y, float sigma2X, float sigma2Y) { const float dx = x - static_cast(track.getX()); const float dy = y - static_cast(track.getY()); @@ -199,7 +147,7 @@ inline float mftFwdPredictedChi2Quiet(const o2::track::TrackParCovFwd& track, fl return dx * dx / vx + dy * dy / vy; } -inline float mftFwdGetPredictedChi2Quiet(const o2::track::TrackParCovFwd& current, const o2::track::TrackParCovFwd& rhs) +inline float mftFwdStateChi2(const o2::track::TrackParCovFwd& current, const o2::track::TrackParCovFwd& rhs) { ROOT::Math::SVector diff{ rhs.getX() - current.getX(), @@ -215,32 +163,15 @@ inline float mftFwdGetPredictedChi2Quiet(const o2::track::TrackParCovFwd& curren return static_cast(ROOT::Math::Similarity(cov, diff)); } -/// ITS getPredictedChi2 analogue: propagate next cell track to current z, compare 5-param states. -inline float mftFwdCellGetPredictedChi2(const o2::track::TrackParCovFwd& current, - o2::track::TrackParCovFwd& next, - float bz) -{ - mftFwdPropagateToZ(next, static_cast(current.getZ()), bz); - return mftFwdGetPredictedChi2Quiet(current, next); -} - -inline bool mftFwdAttachCluster(o2::track::TrackParCovFwd& track, - float z, - float x, - float y, - float sigma2X, - float sigma2Y, - float xOverX0, - float bz, - float maxChi2, - float& chi2, - bool checkChi2OnLast = false) +inline bool mftFwdAttachCluster(o2::track::TrackParCovFwd& track, float z, float x, float y, + float sigma2X, float sigma2Y, float xOverX0, float bz, float maxChi2, + float& chi2, bool checkChi2OnLast = false) { mftFwdPropagateToZ(track, z, bz); if (xOverX0 > 0.f) { track.addMCSEffect(xOverX0); } - const float predChi2 = mftFwdPredictedChi2Quiet(track, x, y, sigma2X, sigma2Y); + const float predChi2 = mftFwdPredictedChi2(track, x, y, sigma2X, sigma2Y); if (checkChi2OnLast && predChi2 > maxChi2) { return false; } @@ -253,229 +184,148 @@ inline bool mftFwdAttachCluster(o2::track::TrackParCovFwd& track, return true; } -template -inline bool mftFwdFitThreeClusters(o2::track::TrackParCovFwd& track, - const std::array& hitLayers, - const std::array& clusIds, - const TimeFrame& tf, - const TrackingParameters& params, - float bz, - float& chi2) +/// Squared transverse distance from cluster c to the seed line c1→c2 (legacy MFT getDistanceToSeed). +inline float mftDistanceToSeedSquared(const o2::its::Cluster& c1, const o2::its::Cluster& c2, const o2::its::Cluster& c) { - chi2 = 0.f; - for (int iC{2}; iC >= 0; --iC) { - const int layer = hitLayers[iC]; - const int clIdx = clusIds[iC]; - const auto& tfInfo = tf.getTrackingFrameInfoOnLayer(layer)[clIdx]; - if (!mftFwdAttachCluster(track, - tfInfo.zCoordinate, - tfInfo.xCoordinate, - tfInfo.yCoordinate, - tfInfo.covarianceTrackingFrame[0], - tfInfo.covarianceTrackingFrame[2], - params.LayerxX0[layer], - bz, - params.MaxChi2ClusterAttachment, - chi2, - iC == 0)) { - return false; - } + const float dxSeed = c2.xCoordinate - c1.xCoordinate; + const float dySeed = c2.yCoordinate - c1.yCoordinate; + const float dzSeed = c2.zCoordinate - c1.zCoordinate; + if (std::abs(dzSeed) < 1e-9f) { + return std::numeric_limits::max(); } - return true; + const float invdzSeed = (c.zCoordinate - c1.zCoordinate) / dzSeed; + const float xSeed = c1.xCoordinate + dxSeed * invdzSeed; + const float ySeed = c1.yCoordinate + dySeed * invdzSeed; + const float dx = c.xCoordinate - xSeed; + const float dy = c.yCoordinate - ySeed; + return dx * dx + dy * dy; } -/// Build a forward seed from three cell clusters and fit with propagate / MCS / χ² / update. -template -inline bool mftFwdFitCellClusters(const int hitLayers[3], - const int clusIds[3], - const TimeFrame& tf, - const TrackingParameters& params, - float bz, - o2::track::TrackParCovFwd& track, - float& chi2) +/// Conical road scale (1 + dz/z_from)^2 between half-layers (legacy ROADclsRCut behaviour). +inline float mftConicalRoadR2Scale(int layerFrom, int layerTo) { - if (!mftBuildFwdTrackSeedFromCellClusters(hitLayers, clusIds, tf, bz, params.TrackletMinPt, track)) { - return false; + const float zFrom = mftLayerZ(layerFrom); + if (std::abs(zFrom) < 1e-6f) { + return 1.f; } - const std::array layersArr{hitLayers[0], hitLayers[1], hitLayers[2]}; - const std::array clusArr{clusIds[0], clusIds[1], clusIds[2]}; - return mftFwdFitThreeClusters(track, layersArr, clusArr, tf, params, bz, chi2); + const float dCone = 1.f + (mftLayerZ(layerTo) - zFrom) / zFrom; + return dCone * dCone; } -template -inline bool mftFwdFitCellSeedWithChi2(const CellSeedTpl& cell, - const TimeFrame& tf, - const TrackingParameters& params, - float bz, - o2::track::TrackParCovFwd& fwdTrack, - float& chi2) +/// Cheap geometric pre-cut before forward cell fit (CellRoadRCut / ROADclsRCut). +inline bool validateMFTCellClusters(const o2::its::Cluster& c0, int layer0, + const o2::its::Cluster& c1, int layer1, + const o2::its::Cluster& c2, int layer2, + float r2Cut) { - const int hitLayers[3]{ - mftLayerAtSlot(cell.getHitLayerMask(), 0), - mftLayerAtSlot(cell.getHitLayerMask(), 1), - mftLayerAtSlot(cell.getHitLayerMask(), 2)}; - const int clusId[3]{cell.getFirstClusterIndex(), cell.getSecondClusterIndex(), cell.getThirdClusterIndex()}; - return mftFwdFitCellClusters(hitLayers, clusId, tf, params, bz, fwdTrack, chi2); + return mftDistanceToSeedSquared(c0, c2, c1) < r2Cut * mftConicalRoadR2Scale(layer0, layer1) && + mftDistanceToSeedSquared(c0, c1, c2) < r2Cut * mftConicalRoadR2Scale(layer0, layer2) && + mftDistanceToSeedSquared(c1, c2, c0) < r2Cut * mftConicalRoadR2Scale(layer1, layer0); } +/// Build inward forward seed at the outer cluster and Kalman-fit the three cell clusters. template -inline bool mftFwdFitCellSeed(const CellSeedTpl& cell, - const TimeFrame& tf, - const TrackingParameters& params, - float bz, - o2::track::TrackParCovFwd& fwdTrack) -{ - float chi2{0.f}; - return mftFwdFitCellSeedWithChi2(cell, tf, params, bz, fwdTrack, chi2); -} - -inline o2::track::TrackParCovF mftFwdToBarrelTrack(const o2::track::TrackParCovFwd& fwd) +inline bool mftFwdFitCellClusters(const int hitLayers[3], const int clusIds[3], + const TimeFrame& tf, const TrackingParameters& params, + float bz, o2::track::TrackParCovFwd& track, float& chi2) { - o2::track::TrackParCovF barrel; - fwd.toBarrelTrackParCov(barrel); - return barrel; -} - -template -inline std::vector> mftSortedHitsFromSeed(const SeedT& seed) -{ - std::vector> hits; - const auto mask = seed.getHitLayerMask(); - for (int layer = mask.first(); layer <= mask.last(); ++layer) { - if (!mask.has(layer)) { - continue; - } - const int clIdx = seed.getCluster(layer); - if (clIdx != o2::its::constants::UnusedIndex) { - hits.emplace_back(layer, clIdx); - } + const auto& cInner = tf.getUnsortedClusters()[hitLayers[0]][clusIds[0]]; + const auto& cMid = tf.getUnsortedClusters()[hitLayers[1]][clusIds[1]]; + const auto& cOuter = tf.getUnsortedClusters()[hitLayers[2]][clusIds[2]]; + if (cInner.zCoordinate <= cOuter.zCoordinate + 1.e-6f) { + return false; } - std::sort(hits.begin(), hits.end(), [](const auto& a, const auto& b) { return a.first > b.first; }); - return hits; -} -template -inline bool mftFwdFitSeedClusters(const SeedT& seed, - const TimeFrame& tf, - const TrackingParameters& params, - float bz, - o2::track::TrackParCovFwd& track, - float& chi2) -{ - const auto hits = mftSortedHitsFromSeed(seed); - if (hits.size() < 3) { + const float dxTan = cMid.xCoordinate - cInner.xCoordinate; + const float dyTan = cMid.yCoordinate - cInner.yCoordinate; + const float dzTan = cMid.zCoordinate - cInner.zCoordinate; + const float drTan = std::sqrt(dxTan * dxTan + dyTan * dyTan); + const float dxPhi = cOuter.xCoordinate - cInner.xCoordinate; + const float dyPhi = cOuter.yCoordinate - cInner.yCoordinate; + const float dzPhi = cOuter.zCoordinate - cInner.zCoordinate; + const float drPhi = std::sqrt(dxPhi * dxPhi + dyPhi * dyPhi); + if (drTan < 1.e-6f || std::abs(dzTan) < 1.e-6f || drPhi < 1.e-6f || std::abs(dzPhi) < 1.e-6f) { return false; } - const auto& cOuter = tf.getUnsortedClusters()[hits[0].first][hits[0].second]; - const auto& cInner = tf.getUnsortedClusters()[hits.back().first][hits.back().second]; - const size_t midIdx = (hits.size() <= 3) ? 1u : (hits.size() / 2u); - const auto& cMid = tf.getUnsortedClusters()[hits[midIdx].first][hits[midIdx].second]; - std::array hitLayers{hits.back().first, hits[midIdx].first, hits[0].first}; - std::array clusIds{hits.back().second, hits[midIdx].second, hits[0].second}; - if (!mftBuildFwdTrackSeedInward(cInner, cMid, cOuter, bz, params.TrackletMinPt, track)) { - return false; + + const float minPt = params.TrackletMinPt; + const float invQPt = (minPt > 0.f) ? 1.f / minPt : 0.f; + float tanl{0.f}; + float phi{0.f}; + if (std::abs(bz) > 0.01f) { + tanl = -std::abs(dzTan) / drTan; + phi = std::atan2(dyPhi, dxPhi); + if (std::abs(tanl) > 1.e-6f) { + const float k = std::abs(o2::constants::math::B2C * bz); + const float hz = (bz > 0.f) ? 1.f : -1.f; + phi -= 0.5f * hz * invQPt * dzPhi * k / tanl; + } + } else { + tanl = -std::abs(dzPhi) / drPhi; + phi = std::atan2(dyPhi, dxPhi); } + + const auto& tfOuter = tf.getTrackingFrameInfoOnLayer(hitLayers[2])[clusIds[2]]; + ROOT::Math::SVector seedParams{cOuter.xCoordinate, cOuter.yCoordinate, phi, tanl, invQPt}; + ROOT::Math::SMatrix> seedCov{}; + seedCov(0, 0) = tfOuter.covarianceTrackingFrame[0] > 0.f ? tfOuter.covarianceTrackingFrame[0] : 1.f; + seedCov(1, 1) = tfOuter.covarianceTrackingFrame[2] > 0.f ? tfOuter.covarianceTrackingFrame[2] : 1.f; + seedCov(2, 2) = seedCov(3, 3) = 1.; + const double qptSigma = std::clamp(static_cast(std::abs(invQPt)), 1., 10.); + seedCov(4, 4) = qptSigma * qptSigma; + track = {cOuter.zCoordinate, seedParams, seedCov, 0.}; + chi2 = 0.f; - if (!mftFwdFitThreeClusters(track, hitLayers, clusIds, tf, params, bz, chi2)) { - return false; - } - for (size_t i{3}; i < hits.size(); ++i) { - const auto [layer, clIdx] = hits[i]; + for (int iC{2}; iC >= 0; --iC) { + const int layer = hitLayers[iC]; + const int clIdx = clusIds[iC]; const auto& tfInfo = tf.getTrackingFrameInfoOnLayer(layer)[clIdx]; - if (!mftFwdAttachCluster(track, - tfInfo.zCoordinate, - tfInfo.xCoordinate, - tfInfo.yCoordinate, - tfInfo.covarianceTrackingFrame[0], - tfInfo.covarianceTrackingFrame[2], - params.LayerxX0[layer], - bz, - params.MaxChi2ClusterAttachment, - chi2, - true)) { + if (!mftFwdAttachCluster(track, tfInfo.zCoordinate, tfInfo.xCoordinate, tfInfo.yCoordinate, + tfInfo.covarianceTrackingFrame[0], tfInfo.covarianceTrackingFrame[2], + params.LayerxX0[layer], bz, params.MaxChi2ClusterAttachment, chi2, iC == 0)) { return false; } } return true; } -/// Forward-fit neighbour attachment: propagate stored seed track, χ²-update at neighbour (ITS processNeighbours analogue). template -inline bool mftFwdAttachNeighbourToSeed(const SeedT& seed, - int neighbourLayer, - int neighbourClIdx, - const TimeFrame& tf, - const TrackingParameters& params, - float bz, - o2::track::TrackParCovFwd& fwdTrack, - float& chi2) +inline bool mftFwdAttachNeighbourToSeed(const SeedT& seed, int neighbourLayer, int neighbourClIdx, + const TimeFrame& tf, const TrackingParameters& params, + float bz, o2::track::TrackParCovFwd& fwdTrack, float& chi2) { fwdTrack = static_cast(seed); chi2 = seed.getChi2(); const auto& trHit = tf.getTrackingFrameInfoOnLayer(neighbourLayer)[neighbourClIdx]; - return mftFwdAttachCluster(fwdTrack, - trHit.zCoordinate, - trHit.xCoordinate, - trHit.yCoordinate, - trHit.covarianceTrackingFrame[0], - trHit.covarianceTrackingFrame[2], - params.LayerxX0[neighbourLayer], - bz, - params.MaxChi2ClusterAttachment, - chi2, - true); + return mftFwdAttachCluster(fwdTrack, trHit.zCoordinate, trHit.xCoordinate, trHit.yCoordinate, + trHit.covarianceTrackingFrame[0], trHit.covarianceTrackingFrame[2], + params.LayerxX0[neighbourLayer], bz, params.MaxChi2ClusterAttachment, chi2, true); } -/// χ² compatibility between connected cells using stored forward track states (ITS cellsConnect analogue). inline bool mftFwdCellsAreCompatible(const CellSeedTpl& currentCell, - const CellSeedTpl& nextCell, - float bz, - float maxChi2) + const CellSeedTpl& nextCell, + float bz, float maxChi2) { if (currentCell.getSecondClusterIndex() != nextCell.getFirstClusterIndex()) { return false; } o2::track::TrackParCovFwd nextFwd = static_cast(nextCell); const auto& currentFwd = static_cast(currentCell); - const float chi2 = mftFwdCellGetPredictedChi2(currentFwd, nextFwd, bz); - return chi2 <= maxChi2; -} - -/// Legacy helper: full refit of both cells then compare (expensive; prefer mftFwdCellsAreCompatible above). -template -inline bool mftFwdCellsAreCompatibleRefit(const CellSeedTpl& currentCell, - const CellSeedTpl& nextCell, - const TimeFrame& tf, - const TrackingParameters& params, - float bz) -{ - o2::track::TrackParCovFwd currentFwd; - o2::track::TrackParCovFwd nextFwd; - float chi2Current{0.f}; - float chi2Next{0.f}; - if (!mftFwdFitCellSeedWithChi2(currentCell, tf, params, bz, currentFwd, chi2Current) || - !mftFwdFitCellSeedWithChi2(nextCell, tf, params, bz, nextFwd, chi2Next)) { - return false; - } mftFwdPropagateToZ(nextFwd, static_cast(currentFwd.getZ()), bz); - const float chi2 = mftFwdGetPredictedChi2Quiet(currentFwd, nextFwd); - return chi2 <= params.MaxChi2ClusterAttachment; + return mftFwdStateChi2(currentFwd, nextFwd) <= maxChi2; } -/// Forward-fit road rescue: extrapolate from seed hits, attach neighbour only. -template -inline bool mftFwdRoadAttachNeighbour(const SeedT& seed, - int neighbourLayer, - int neighbourClIdx, - const TimeFrame& tf, - const TrackingParameters& params, - float bz, - o2::track::TrackParCovFwd& fwdTrack, - float& attachChi2) +} // namespace o2::itsmft::tracking::detail + +namespace o2::itsmft::tracking { - return mftFwdAttachNeighbourToSeed(seed, neighbourLayer, neighbourClIdx, tf, params, bz, fwdTrack, attachChi2); -} -} // namespace o2::itsmft::tracking::detail +bool refitTrackFwd(const TrackSeedN& seed, + MFTCATrack& track, + const TimeFrame& tf, + const TrackingParameters& params, + float bz); + +} // namespace o2::itsmft::tracking #endif /* ALICEO2_ITSMFT_TRACKING_MFTFWDTRACKHELPERS_H_ */ diff --git a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/TimeFrame.h b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/TimeFrame.h index d5464e75416c0..dd976bde96185 100644 --- a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/TimeFrame.h +++ b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/TimeFrame.h @@ -14,7 +14,6 @@ #define ALICEO2_ITSMFT_TRACKING_TIMEFRAME_H_ #include -#include #include #include #include @@ -32,10 +31,8 @@ #include "ITStracking/ROFLookupTables.h" #include "ITStracking/Tracklet.h" -#include "ITSMFTTracking/CATrackTypes.h" #include "ITSMFTTracking/MFTCATrack.h" #include "ITSMFTTracking/Configuration.h" -#include "ITSMFTTracking/Constants.h" #include "ITSMFTTracking/IndexTableUtils.h" #include "ITSMFTTracking/LayerMask.h" #include "ITSMFTTracking/TrackingTopology.h" @@ -80,9 +77,8 @@ 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; +using o2::itsmft::tracking::ITSNLayers; +using o2::itsmft::tracking::nLayersForDet; } // namespace constants template @@ -207,41 +203,6 @@ struct TimeFrame { auto& getTrackletsLabel(int layer) { return mTrackletLabels[layer]; } auto& getCellsLabel(int layer) { return mCellLabels[layer]; } - struct MCArtefactCoverage { - uint16_t trackletMask{0}; - uint16_t cellMask{0}; - }; - - void clearMCArtefactCoverage() { mMCArtefactCoverage.clear(); } - void recordMCArtefactTracklet(uint64_t labelKey, int fromLayer) - { - if (fromLayer >= 0 && fromLayer < NLayers - 1) { - mMCArtefactCoverage[labelKey].trackletMask |= static_cast(1u << fromLayer); - } - } - void recordMCArtefactCell(uint64_t labelKey, int fromLayer) - { - if (fromLayer >= 0 && fromLayer < NLayers - 1) { - mMCArtefactCoverage[labelKey].cellMask |= static_cast(1u << fromLayer); - } - } - void exportMCArtefactCoverage(std::vector& keys, - std::vector& trackletMasks, - std::vector& cellMasks) const - { - keys.clear(); - trackletMasks.clear(); - cellMasks.clear(); - keys.reserve(mMCArtefactCoverage.size()); - trackletMasks.reserve(mMCArtefactCoverage.size()); - cellMasks.reserve(mMCArtefactCoverage.size()); - for (const auto& entry : mMCArtefactCoverage) { - keys.push_back(entry.first); - trackletMasks.push_back(entry.second.trackletMask); - cellMasks.push_back(entry.second.cellMask); - } - } - bool hasMCinformation() const { return mClusterLabels[0] != nullptr; } void initVertexingTopology(const TrackingParameters& trkParam); void initDefaultTrackingTopology(const TrackingParameters& trkParam, const int maxLayers = NLayers); @@ -376,7 +337,6 @@ struct TimeFrame { bounded_vector> mPValphaX; /// PV x and alpha for track propagation std::vector> mTrackletLabels; std::vector> mCellLabels; - std::unordered_map mMCArtefactCoverage; std::vector> mCellsNeighboursLUT; bounded_vector mBogusClusters; /// keep track of clusters with wild coordinates @@ -678,9 +638,9 @@ inline const TrackingFrameInfo& TimeFrame::getClusterTrackingFrameInfo( return mTrackingFrameInfo[layerId][cl.clusterId]; } -using TimeFrameITS = TimeFrame; -/// MFT CA TimeFrame: NLayers = MFTNLayers half-disks; see ITSMFTTracking/Constants.h. -using TimeFrameMFT = TimeFrame; +using TimeFrameITS = TimeFrame; +/// MFT CA TimeFrame: NLayers = half-disk CA layers (see MFTFwdTrackHelpers.h). +using TimeFrameMFT = TimeFrame; } // namespace itsmft::tracking } // namespace o2 diff --git a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/TrackerTraits.h b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/TrackerTraits.h index 0c63cfaf0dde8..5cbe6553f9a5e 100644 --- a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/TrackerTraits.h +++ b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/TrackerTraits.h @@ -18,7 +18,6 @@ #include -#include "ITSMFTTracking/CATrackTypes.h" #include "ITSMFTTracking/Configuration.h" #include "ITSMFTTracking/TimeFrame.h" #include "ITStracking/BoundedAllocator.h" @@ -80,8 +79,8 @@ class TrackerTraits float mBz{-999.f}; }; -using TrackerTraitsITS = TrackerTraits; -using TrackerTraitsMFT = TrackerTraits; +using TrackerTraitsITS = TrackerTraits; +using TrackerTraitsMFT = TrackerTraits; } // namespace o2::itsmft::tracking diff --git a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/TrackingConfigParam.h b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/TrackingConfigParam.h index c697e4e54cbbe..8793b15c2285f 100644 --- a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/TrackingConfigParam.h +++ b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/TrackingConfigParam.h @@ -18,7 +18,16 @@ #include "CommonUtils/ConfigurableParam.h" #include "CommonUtils/ConfigurableParamHelper.h" #include "DetectorsCommonDataFormats/DetID.h" -#include "ITSMFTTracking/Constants.h" + +namespace o2::itsmft::tracking +{ +/// ITS CA layer count (matches legacy o2::its::TrackingParameters::NLayers default). +constexpr int ITSNLayers = 7; +/// MFT CA half-disk layer count (same as o2::mft::constants::mft::LayersNumber). +constexpr int MFTNLayers = 10; +/// Max CA iterations (same as o2::its::constants::MaxIter). +constexpr int MaxIter = 4; +} // namespace o2::itsmft::tracking namespace o2::itsmft { @@ -75,22 +84,22 @@ 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[o2::itsmft::tracking::MaxIter] = {}; // minimum track length at each iteration, used only if >0, otherwise use code defaults + uint8_t startLayerMask[o2::itsmft::tracking::MaxIter] = {}; // mask of start layer for this iteration (if >0) + int maxHolesIter[o2::itsmft::tracking::MaxIter] = {}; // maximum number of missing internal layers allowed in the CA topology for each iteration + uint16_t holeLayerMaskIter[o2::itsmft::tracking::MaxIter] = {}; // layers that may be skipped by the CA topology for each iteration + float minPtIterLgt[o2::itsmft::tracking::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; @@ -108,7 +117,7 @@ struct TrackerParamConfig : public o2::conf::ConfigurableParamHelper class ITSMFTTrackingInterface { public: - static_assert(NLayers == constants::ITSNLayers || NLayers == constants::MFTNLayers, + static_assert(NLayers == ITSNLayers || NLayers == o2::mft::constants::mft::LayersNumber, "ITSMFTTrackingInterface supports ITS (7) and MFT (10) layer counts only"); - static constexpr o2::detectors::DetID::ID DetId = constants::detIdFromNLayers(); + static constexpr o2::detectors::DetID::ID DetId = detIdFromNLayers(); using TimeFrameN = TimeFrame; using TrackerN = Tracker; @@ -116,8 +115,8 @@ class ITSMFTTrackingInterface bool mMFTTriggered = false; }; -using ITSMFTTrackingInterfaceITS = ITSMFTTrackingInterface; -using ITSMFTTrackingInterfaceMFT = ITSMFTTrackingInterface; +using ITSMFTTrackingInterfaceITS = ITSMFTTrackingInterface; +using ITSMFTTrackingInterfaceMFT = ITSMFTTrackingInterface; } // namespace o2::itsmft::tracking diff --git a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/TrackingParamRef.h b/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/TrackingParamRef.h deleted file mode 100644 index f0d3d4edacc92..0000000000000 --- a/Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/TrackingParamRef.h +++ /dev/null @@ -1,47 +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 TrackingParamRef.h -/// \brief Resolve CA tracker configurable params per detector without duplicating ITS registration -/// - -#ifndef ALICEO2_ITSMFT_TRACKING_TRACKINGPARAMREF_H_ -#define ALICEO2_ITSMFT_TRACKING_TRACKINGPARAMREF_H_ - -#include "DetectorsCommonDataFormats/DetID.h" -#include "ITSMFTTracking/Constants.h" -#include "ITSMFTTracking/TrackingConfigParam.h" -#include "ITStracking/TrackingConfigParam.h" - -namespace o2::itsmft::tracking -{ - -/// MFT uses o2::itsmft::TrackerParamConfig; ITS production params stay in O2::ITStracking. -template -struct TrackerParamRef; - -template <> -struct TrackerParamRef { - using Type = o2::itsmft::TrackerParamConfig; - static const Type& get() { return Type::Instance(); } - static constexpr int nLayers() { return Type::getNLayers(); } -}; - -template <> -struct TrackerParamRef { - using Type = o2::its::TrackerParamConfig; - static const Type& get() { return Type::Instance(); } - static constexpr int nLayers() { return constants::ITSNLayers; } -}; - -} // namespace o2::itsmft::tracking - -#endif /* ALICEO2_ITSMFT_TRACKING_TRACKINGPARAMREF_H_ */ diff --git a/Detectors/ITSMFT/common/tracking/src/CATracker.cxx b/Detectors/ITSMFT/common/tracking/src/CATracker.cxx index c2b45d4bc69a3..2a967dddd74b3 100644 --- a/Detectors/ITSMFT/common/tracking/src/CATracker.cxx +++ b/Detectors/ITSMFT/common/tracking/src/CATracker.cxx @@ -13,7 +13,6 @@ /// \brief /// -#include "ITSMFTTracking/CATrackTypes.h" #include "ITSMFTTracking/CATracker.h" #include @@ -68,7 +67,7 @@ void computeTracksMClabels(TimeFrame& tf) return e1.second > e2.second; }); maxOccurrencesValue = occurrences[0].first; - if constexpr (NLayers == constants::MFTNLayers) { + if constexpr (NLayers == o2::mft::constants::mft::LayersNumber) { const float threshold = o2::mft::MFTTrackingParam::Instance().TrueTrackMCThreshold; if (static_cast(occurrences[0].second) / track.getNumberOfClusters() < threshold) { maxOccurrencesValue.setFakeFlag(); diff --git a/Detectors/ITSMFT/common/tracking/src/Configuration.cxx b/Detectors/ITSMFT/common/tracking/src/Configuration.cxx index db6f1266f83cd..bd178dd52f0d1 100644 --- a/Detectors/ITSMFT/common/tracking/src/Configuration.cxx +++ b/Detectors/ITSMFT/common/tracking/src/Configuration.cxx @@ -22,6 +22,7 @@ #include "Framework/Logger.h" #include "ITSMFTTracking/Configuration.h" #include "ITSMFTTracking/TrackingConfigParam.h" +#include "ITStracking/Constants.h" #include "MFTTracking/Constants.h" namespace @@ -100,7 +101,7 @@ void resetDetectorDefaults(TrackingParameters& p, detectors::DetID::ID detId) if (detId == detectors::DetID::MFT) { namespace mftc = o2::mft::constants; namespace mft = mftc::mft; - constexpr int nLayers = tracking::constants::MFTNLayers; + constexpr int nLayers = o2::mft::constants::mft::LayersNumber; p = TrackingParameters{}; p.NLayers = nLayers; @@ -216,7 +217,7 @@ std::vector getTrackingParameters(detectors::DetID::ID detId for (int ip = 0; ip < static_cast(trackParams.size()); ip++) { auto& param = trackParams[ip]; - if (ip < tracking::constants::MaxIter) { + if (ip < o2::its::constants::MaxIter) { if (tc.startLayerMask[ip] > 0) { param.StartLayerMask = tc.startLayerMask[ip]; } @@ -285,7 +286,7 @@ std::vector getTrackingParameters(detectors::DetID::ID detId p.PerPrimaryVertexProcessing = tc.perPrimaryVertexProcessing; const auto iter = &p - trackParams.data(); - if (iter < tracking::constants::MaxIter) { + if (iter < o2::its::constants::MaxIter) { p.MaxHoles = tc.maxHolesIter[iter]; p.HoleLayerMask = tc.holeLayerMaskIter[iter]; } diff --git a/Detectors/ITSMFT/common/tracking/src/DetectorTraits.cxx b/Detectors/ITSMFT/common/tracking/src/DetectorTraits.cxx index d8014ed653176..56dbd86e78e3b 100644 --- a/Detectors/ITSMFT/common/tracking/src/DetectorTraits.cxx +++ b/Detectors/ITSMFT/common/tracking/src/DetectorTraits.cxx @@ -15,12 +15,8 @@ #include "ITSMFTTracking/DetectorTraits.h" -#include - #include "Framework/Logger.h" -#include "ITSMFTTracking/MFTForwardRefit.h" #include "ITSMFTTracking/MFTFwdTrackHelpers.h" -#include "ITSMFTTracking/TrackingParamRef.h" #include "ITStracking/TrackHelpers.h" namespace o2::itsmft::tracking @@ -57,106 +53,6 @@ bool refitSeedITS(const typename DetectorTraits::TrackSeedN& seed, } } // namespace -template -bool DetectorTraits::fitCellClusters(const int clusId[3], - const int hitLayers[3], - const TimeFrameN& tf, - const TrackingParameters& params, - float bz, - const o2::base::PropagatorImpl* propagator, - o2::track::TrackParCovF& track, - float& chi2) -{ - if constexpr (DetId == o2::detectors::DetID::MFT) { - o2::track::TrackParCovFwd fwdTrack; - if (!detail::mftFwdFitCellClusters(hitLayers, clusId, tf, params, bz, fwdTrack, chi2)) { - return false; - } - track = detail::mftFwdToBarrelTrack(fwdTrack); - return true; - } else { - const auto& cluster1_glo = tf.getUnsortedClusters()[hitLayers[0]][clusId[0]]; - const auto& cluster2_glo = tf.getUnsortedClusters()[hitLayers[1]][clusId[1]]; - const auto& cluster3_tf = tf.getTrackingFrameInfoOnLayer(hitLayers[2])[clusId[2]]; - track = o2::its::track::buildTrackSeed(cluster1_glo, cluster2_glo, cluster3_tf, bz); - chi2 = 0.f; - for (int iC{2}; iC--;) { - const int hitLayer = hitLayers[iC]; - const TrackingFrameInfo& trackingHit = tf.getTrackingFrameInfoOnLayer(hitLayer)[clusId[iC]]; - - if (!track.rotate(trackingHit.alphaTrackingFrame)) { - return false; - } - - if (!track.propagateTo(trackingHit.xTrackingFrame, bz)) { - return false; - } - - if (!track.correctForMaterial(params.LayerxX0[hitLayer], params.LayerxX0[hitLayer] * o2::its::constants::Radl * o2::its::constants::Rho, true)) { - return false; - } - - const auto predChi2{track.getPredictedChi2Quiet(trackingHit.positionTrackingFrame, trackingHit.covarianceTrackingFrame)}; - if (!iC && predChi2 > params.MaxChi2ClusterAttachment) { - return false; - } - - if (!track.o2::track::TrackParCov::update(trackingHit.positionTrackingFrame, trackingHit.covarianceTrackingFrame)) { - return false; - } - - if (!iC) { - chi2 += predChi2; - } - } - return true; - } -} - -template -bool DetectorTraits::attachNeighbourToSeed(TrackSeedN& seed, - int neighbourLayer, - int neighbourCluster, - const TimeFrameN& tf, - const TrackingParameters& params, - float bz, - const o2::base::PropagatorImpl* propagator) -{ - if constexpr (DetId == o2::detectors::DetID::MFT) { - o2::track::TrackParCovFwd fwdTrack; - float chi2 = seed.getChi2(); - if (!detail::mftFwdAttachNeighbourToSeed(seed, neighbourLayer, neighbourCluster, tf, params, bz, fwdTrack, chi2)) { - return false; - } - static_cast(seed) = fwdTrack; - seed.setChi2(chi2); - return true; - } else { - const auto& trHit = tf.getTrackingFrameInfoOnLayer(neighbourLayer)[neighbourCluster]; - - if (!seed.rotate(trHit.alphaTrackingFrame)) { - return false; - } - - if (!propagator->propagateToX(seed, trHit.xTrackingFrame, bz, o2::base::PropagatorImpl::MAX_SIN_PHI, o2::base::PropagatorImpl::MAX_STEP, params.CorrType)) { - return false; - } - - if (params.CorrType == o2::base::PropagatorF::MatCorrType::USEMatCorrNONE) { - if (!seed.correctForMaterial(params.LayerxX0[neighbourLayer], params.LayerxX0[neighbourLayer] * o2::its::constants::Radl * o2::its::constants::Rho, true)) { - return false; - } - } - - auto predChi2{seed.getPredictedChi2Quiet(trHit.positionTrackingFrame, trHit.covarianceTrackingFrame)}; - if ((predChi2 > params.MaxChi2ClusterAttachment) || predChi2 < 0.f) { - return false; - } - seed.setChi2(seed.getChi2() + predChi2); - return seed.o2::track::TrackParCov::update(trHit.positionTrackingFrame, trHit.covarianceTrackingFrame); - } -} - template bool DetectorTraits::cellsAreCompatible(const CellSeedN& currentCell, const CellSeedN& nextCell, @@ -193,63 +89,6 @@ bool DetectorTraits::refitSeed(const TrackSeedN& seed, } } -template -void DetectorTraits::sortRefittedTracks(bounded_vector& tracks) -{ - // Same ordering as o2::its::track::isBetter (longer track, then lower chi2). - std::sort(tracks.begin(), tracks.end(), [](const TrackType& a, const TrackType& b) { - const auto ncla = a.getNumberOfClusters(); - const auto nclb = b.getNumberOfClusters(); - return (ncla == nclb) ? (a.getChi2() < b.getChi2()) : ncla > nclb; - }); -} - -template -void DetectorTraits::finalizeAcceptedTrack(TrackType& track) -{ - if constexpr (DetId == o2::detectors::DetID::ITS) { - track.setUserField(0); - track.getParamOut().setUserField(0); - } -} - -template -bool DetectorTraits::sameTrackSign(const TrackType& t1, const TrackType& t2) -{ - if constexpr (DetId == o2::detectors::DetID::MFT) { - return t1.getCharge() == t2.getCharge(); - } else { - return t1.getSign() == t2.getSign(); - } -} - -template -bool DetectorTraits::validateMFTCellClusters(const o2::its::Cluster& c0, int layer0, - const o2::its::Cluster& c1, int layer1, - const o2::its::Cluster& c2, int layer2, - float r2Cut) -{ - if constexpr (DetId == o2::detectors::DetID::MFT) { - return detail::mftDistanceToSeedSquared(c0, c2, c1) < r2Cut * detail::mftConicalRoadR2Scale(layer0, layer1) && - detail::mftDistanceToSeedSquared(c0, c1, c2) < r2Cut * detail::mftConicalRoadR2Scale(layer0, layer2) && - detail::mftDistanceToSeedSquared(c1, c2, c0) < r2Cut * detail::mftConicalRoadR2Scale(layer1, layer0); - } else { - return false; - } -} - -template -bool DetectorTraits::mftCellsConnect(const o2::its::Cluster& cEnd, const o2::its::Cluster& cStart, float r2Cut) -{ - if constexpr (DetId == o2::detectors::DetID::MFT) { - const float dx = cEnd.xCoordinate - cStart.xCoordinate; - const float dy = cEnd.yCoordinate - cStart.yCoordinate; - return dx * dx + dy * dy <= r2Cut; - } else { - return false; - } -} - template void DetectorTraits::configureIndexTableUtils(IndexTableUtils& utils, const TrackingParameters& params) { diff --git a/Detectors/ITSMFT/common/tracking/src/IOUtils.cxx b/Detectors/ITSMFT/common/tracking/src/IOUtils.cxx index da85b7b81a125..2f12420730956 100644 --- a/Detectors/ITSMFT/common/tracking/src/IOUtils.cxx +++ b/Detectors/ITSMFT/common/tracking/src/IOUtils.cxx @@ -14,7 +14,7 @@ /// #include "ITSMFTTracking/IOUtils.h" -#include "ITSMFTTracking/TrackingParamRef.h" +#include "ITSMFTTracking/Configuration.h" #include "ITStracking/Cluster.h" #include "Framework/Logger.h" @@ -45,6 +45,19 @@ bool shouldApplySysErrors() return false; } +template +void addSysErrors(int layerId, float& sigma2Row, float& sigma2Col) +{ + const auto& conf = o2::itsmft::tracking::TrackerParamRef::get(); + if constexpr (DetId == o2::detectors::DetID::MFT) { + sigma2Row += conf.sysErr2Row[layerId]; + sigma2Col += conf.sysErr2Col[layerId]; + } else { + sigma2Row += conf.sysErrY2[layerId]; + sigma2Col += conf.sysErrZ2[layerId]; + } +} + template void loadClusterTrackingFrameInfoImpl(GeomT* geom, const o2::itsmft::CompClusterExt& c, @@ -70,36 +83,12 @@ void loadClusterTrackingFrameInfoImpl(GeomT* geom, LOGP(fatal, "Cluster patternID {} is out of dictionary range [0, {})", pattID, dict->getSize()); } - float sigma2Row{o2::itsmft::ioutils::DefClusError2Row}; - float sigma2Col{o2::itsmft::ioutils::DefClusError2Col}; - o2::math_utils::Point3D locXYZ{}; - if (pattID != o2::itsmft::CompCluster::InvalidPatternID) { - sigma2Row = dict->getErr2X(pattID); - sigma2Col = dict->getErr2Z(pattID); - if (!dict->isGroup(pattID)) { - locXYZ = dict->getClusterCoordinates(c); - clusterSize = dict->getNpixels(pattID); - } else { - o2::itsmft::ClusterPattern patt(pattIt); - locXYZ = dict->getClusterCoordinates(c, patt); - clusterSize = patt.getNPixels(); - } - } else { - o2::itsmft::ClusterPattern patt(pattIt); - locXYZ = dict->getClusterCoordinates(c, patt, false); - clusterSize = patt.getNPixels(); - } + float sigma2Row{0.f}; + float sigma2Col{0.f}; + const auto locXYZ = o2::itsmft::ioutils::extractClusterData(c, pattIt, dict, sigma2Row, sigma2Col, &clusterSize); + if (applySysErrors && shouldApplySysErrors()) { - const auto layerId = geom->getLayer(sensorID); - if constexpr (DetId == o2::detectors::DetID::MFT) { - const auto& conf = o2::itsmft::tracking::TrackerParamRef::get(); - sigma2Row += conf.sysErr2Row[layerId]; - sigma2Col += conf.sysErr2Col[layerId]; - } else { - const auto& conf = o2::itsmft::tracking::TrackerParamRef::get(); - sigma2Row += conf.sysErrY2[layerId]; - sigma2Col += conf.sysErrZ2[layerId]; - } + addSysErrors(layer, sigma2Row, sigma2Col); } if constexpr (DetId == o2::detectors::DetID::ITS) { @@ -134,18 +123,9 @@ void fillOutputClusters(GeomT* geom, 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); + const auto locXYZ = o2::itsmft::ioutils::extractClusterData(c, pattIt, dict, sigma2Row, sigma2Col); if (applyMisalignment) { - const auto layerId = geom->getLayer(c.getSensorID()); - if constexpr (DetId == o2::detectors::DetID::MFT) { - const auto& conf = o2::itsmft::tracking::TrackerParamRef::get(); - sigma2Row += conf.sysErr2Row[layerId]; - sigma2Col += conf.sysErr2Col[layerId]; - } else { - const auto& conf = o2::itsmft::tracking::TrackerParamRef::get(); - sigma2Row += conf.sysErrY2[layerId]; - sigma2Col += conf.sysErrZ2[layerId]; - } + addSysErrors(geom->getLayer(c.getSensorID()), sigma2Row, sigma2Col); } o2::math_utils::Point3D outXYZ{}; if constexpr (DetId == o2::detectors::DetID::ITS) { diff --git a/Detectors/ITSMFT/common/tracking/src/MFTForwardRefit.cxx b/Detectors/ITSMFT/common/tracking/src/MFTFwdTrackHelpers.cxx similarity index 56% rename from Detectors/ITSMFT/common/tracking/src/MFTForwardRefit.cxx rename to Detectors/ITSMFT/common/tracking/src/MFTFwdTrackHelpers.cxx index e0fe42d4be13f..3d786bf83f9db 100644 --- a/Detectors/ITSMFT/common/tracking/src/MFTForwardRefit.cxx +++ b/Detectors/ITSMFT/common/tracking/src/MFTFwdTrackHelpers.cxx @@ -9,13 +9,12 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. /// -/// \file MFTForwardRefit.cxx -/// \brief +/// \file MFTFwdTrackHelpers.cxx +/// \brief MFT CA final forward Kalman refit (legacy TrackFitter) /// -#include "ITSMFTTracking/MFTForwardRefit.h" +#include "ITSMFTTracking/MFTFwdTrackHelpers.h" -#include #include #include "Framework/Logger.h" @@ -23,7 +22,6 @@ #include "MFTTracking/Cluster.h" #include "MFTTracking/Constants.h" #include "MFTTracking/MFTTrackingParam.h" -#include "MFTTracking/TrackCA.h" #include "MFTTracking/TrackFitter.h" namespace o2::itsmft::tracking @@ -32,16 +30,17 @@ namespace o2::itsmft::tracking namespace { template -bool buildTrackLTF(const TrackSeedN& seed, - const TimeFrame& tf, - const TrackingParameters& params, - TrackLTFType& ltf) +bool refitTrackFwdImpl(const TrackSeedN& seed, + MFTCATrack& track, + const TimeFrame& tf, + const TrackingParameters& params, + float bz) { - ltf = TrackLTFType(true); + TrackLTFType ltf(true); const auto& unsorted = tf.getUnsortedClusters(); const auto hitMask = seed.getHitLayerMask(); - for (int layer = 0; layer < constants::MFTNLayers; ++layer) { + for (int layer = 0; layer < o2::mft::constants::mft::LayersNumber; ++layer) { if (!hitMask.has(layer)) { continue; } @@ -56,49 +55,18 @@ bool buildTrackLTF(const TrackSeedN& seed, const auto& cluster = unsorted[layer][clIdx]; const auto& tfInfo = tf.getClusterTrackingFrameInfo(layer, cluster); o2::mft::Cluster mftCluster{ - tfInfo.xCoordinate, - tfInfo.yCoordinate, - tfInfo.zCoordinate, - cluster.phi, - cluster.radius, - clIdx, - 0, - tfInfo.covarianceTrackingFrame[0], - tfInfo.covarianceTrackingFrame[2], - 0}; + tfInfo.xCoordinate, tfInfo.yCoordinate, tfInfo.zCoordinate, + cluster.phi, cluster.radius, clIdx, 0, + tfInfo.covarianceTrackingFrame[0], tfInfo.covarianceTrackingFrame[2], 0}; const int extIdx = tf.getClusterExternalIndex(layer, clIdx); - const int clsSize = tf.getClusterSize(0, extIdx); - ltf.setPoint(mftCluster, layer, clIdx, {}, extIdx, clsSize); + ltf.setPoint(mftCluster, layer, clIdx, {}, extIdx, tf.getClusterSize(0, extIdx)); } if (ltf.getNumberOfPoints() < params.MinTrackLength) { return false; } ltf.sort(); - return true; -} - -void copyClusterRefs(const TrackSeedN& seed, - const TimeFrame& tf, - MFTCATrack& track) -{ - track.setPattern(0); - const auto hitMask = seed.getHitLayerMask(); - for (int layer = 0; layer < constants::MFTNLayers; ++layer) { - if (!hitMask.has(layer)) { - track.setClusterIndex(layer, o2::its::constants::UnusedIndex); - continue; - } - const int clIdx = seed.getCluster(layer); - track.setClusterIndex(layer, clIdx); - const int extIdx = tf.getClusterExternalIndex(layer, clIdx); - track.setClusterSize(layer, tf.getClusterSize(0, extIdx)); - } -} -template -bool fitTrackLTF(TrackLTFType& ltf, float bz) -{ const auto& mftParam = o2::mft::MFTTrackingParam::Instance(); o2::mft::TrackFitter fitter; fitter.setBz(bz); @@ -107,42 +75,20 @@ bool fitTrackLTF(TrackLTFType& ltf, float bz) fitter.setAlignResiduals(mftParam.alignResidual); TrackLTFType outward = ltf; - if (!fitter.initTrack(ltf) || !fitter.fit(ltf)) { - return false; - } - if (!fitter.initTrack(outward, true) || !fitter.fit(outward, true)) { + if (!fitter.initTrack(ltf) || !fitter.fit(ltf) || + !fitter.initTrack(outward, true) || !fitter.fit(outward, true)) { return false; } ltf.setOutParam(outward); - return true; -} - -template -bool refitTrackFwdImpl(const TrackSeedN& seed, - MFTCATrack& track, - const TimeFrame& tf, - const TrackingParameters& params, - float bz) -{ - TrackLTFType ltf; - if (!buildTrackLTF(seed, tf, params, ltf)) { - return false; - } - - if (!fitTrackLTF(ltf, bz)) { - return false; - } const int nCl = ltf.getNumberOfPoints(); if (nCl < params.MinTrackLength) { return false; } - const float minPt = params.MinPt[constants::MFTNLayers - nCl]; - if (ltf.getPt() < minPt) { + if (ltf.getPt() < params.MinPt[o2::mft::constants::mft::LayersNumber - nCl]) { return false; } - const float chi2ndf = static_cast(ltf.getTrackChi2() / std::max(1, 2 * nCl - 5)); - if (chi2ndf > params.MaxChi2NDF) { + if (static_cast(ltf.getTrackChi2() / std::max(1, 2 * nCl - 5)) > params.MaxChi2NDF) { return false; } @@ -150,16 +96,13 @@ bool refitTrackFwdImpl(const TrackSeedN& seed, if (std::abs(ltf.getX()) < params.TrackletMinAbsX) { return false; } - const auto hitMask = seed.getHitLayerMask(); - for (int layer = 0; layer < constants::MFTNLayers; ++layer) { + for (int layer = 0; layer < o2::mft::constants::mft::LayersNumber; ++layer) { if (!hitMask.has(layer)) { continue; } const int clIdx = seed.getCluster(layer); - if (clIdx == o2::its::constants::UnusedIndex) { - continue; - } - if (std::abs(tf.getUnsortedClusters()[layer][clIdx].xCoordinate) < params.TrackletMinAbsX) { + if (clIdx != o2::its::constants::UnusedIndex && + std::abs(unsorted[layer][clIdx].xCoordinate) < params.TrackletMinAbsX) { return false; } } @@ -168,20 +111,30 @@ bool refitTrackFwdImpl(const TrackSeedN& seed, auto& mftTr = track.getTrack(); mftTr = static_cast(ltf); mftTr.setCA(true); - copyClusterRefs(seed, tf, track); + + track.setPattern(0); + for (int layer = 0; layer < o2::mft::constants::mft::LayersNumber; ++layer) { + if (!hitMask.has(layer)) { + track.setClusterIndex(layer, o2::its::constants::UnusedIndex); + continue; + } + const int clIdx = seed.getCluster(layer); + track.setClusterIndex(layer, clIdx); + const int extIdx = tf.getClusterExternalIndex(layer, clIdx); + track.setClusterSize(layer, tf.getClusterSize(0, extIdx)); + } return true; } } // namespace -bool refitTrackFwd(const TrackSeedN& seed, - MFTCATrack& track, - const TimeFrame& tf, - const TrackingParameters& params, - float bz) +bool refitTrackFwd(const TrackSeedN& seed, + MFTCATrack& track, + const TimeFrame& tf, + const TrackingParameters& params, + float bz) { const auto& mftParam = o2::mft::MFTTrackingParam::Instance(); - const bool fieldOff = mftParam.forceZeroField || std::abs(bz) < 1e-6f; - if (fieldOff) { + if (mftParam.forceZeroField || std::abs(bz) < 1e-6f) { return refitTrackFwdImpl(seed, track, tf, params, 0.f); } return refitTrackFwdImpl(seed, track, tf, params, bz); diff --git a/Detectors/ITSMFT/common/tracking/src/TimeFrame.cxx b/Detectors/ITSMFT/common/tracking/src/TimeFrame.cxx index e2b9cab32d110..252a78e68ff23 100644 --- a/Detectors/ITSMFT/common/tracking/src/TimeFrame.cxx +++ b/Detectors/ITSMFT/common/tracking/src/TimeFrame.cxx @@ -18,6 +18,7 @@ #include "Framework/Logger.h" #include "ITSMFTTracking/DetectorTraits.h" #include "ITSMFTTracking/IOUtils.h" +#include "ITSMFTTracking/MFTFwdTrackHelpers.h" #include "ITSMFTTracking/TimeFrame.h" #include "ITStracking/MathUtils.h" #include "DataFormatsITSMFT/CompCluster.h" @@ -321,7 +322,7 @@ void TimeFrame::initialise(const TrackingParameters& trkParam, const in // estimate MS per layer std::array msAngles{}; for (unsigned int iLayer{0}; iLayer < NLayers; ++iLayer) { - if constexpr (NLayers == constants::MFTNLayers) { + if constexpr (NLayers == o2::mft::constants::mft::LayersNumber) { msAngles[iLayer] = detail::mftLayerMSAngle(iLayer, trkParam); } else { msAngles[iLayer] = math_utils::MSangle(0.14f, trkParam.TrackletMinPt, trkParam.LayerxX0[iLayer]); @@ -330,7 +331,7 @@ void TimeFrame::initialise(const TrackingParameters& trkParam, const in } // for each transition calculate the phi-cuts + integrated MS - if constexpr (NLayers == constants::MFTNLayers) { + if constexpr (NLayers == o2::mft::constants::mft::LayersNumber) { 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); @@ -532,11 +533,10 @@ void TimeFrame::wipe() deepVectorClear(mTrackletLabels); deepVectorClear(mCellLabels); deepVectorClear(mTracksLabel); - mMCArtefactCoverage.clear(); } } -template class TimeFrame; -template class TimeFrame; +template class TimeFrame; +template class TimeFrame; } // namespace o2::itsmft::tracking diff --git a/Detectors/ITSMFT/common/tracking/src/TrackerTraits.cxx b/Detectors/ITSMFT/common/tracking/src/TrackerTraits.cxx index 833eb5114f99b..166e9f504ed40 100644 --- a/Detectors/ITSMFT/common/tracking/src/TrackerTraits.cxx +++ b/Detectors/ITSMFT/common/tracking/src/TrackerTraits.cxx @@ -27,9 +27,7 @@ #include "ITStracking/BoundedAllocator.h" #include "ITSMFTTracking/Cell.h" #include "ITStracking/Constants.h" -#include "ITSMFTTracking/CATrackTypes.h" #include "ITSMFTTracking/Configuration.h" -#include "ITSMFTTracking/Constants.h" #include "ITSMFTTracking/DetectorTraits.h" #include "ITSMFTTracking/MFTFwdTrackHelpers.h" #include "ITSMFTTracking/IndexTableUtils.h" @@ -53,62 +51,6 @@ struct PassMode { using TwoPassInsert = std::integral_constant; }; -namespace detail -{ -constexpr int kQEDSourceID = 99; - -bool isUsableMCLabel(const MCCompLabel& label) -{ - return label.isValid() && !label.isNoise() && label.getSourceID() != kQEDSourceID; -} - -uint64_t mcLabelKey(const MCCompLabel& label) -{ - return label.getRawValue() & MCCompLabel::maskFull; -} - -template -void recordMCTrackletCoverage(TimeFrame* tf, - const typename TrackingTopology::View& topology, - int transitionId) -{ - const auto& transition = topology.getTransition(static_cast::Id>(transitionId)); - if (transition.toLayer != transition.fromLayer + 1) { - return; - } - const auto& labels = tf->getTrackletsLabel(transitionId); - for (const auto& lab : labels) { - if (isUsableMCLabel(lab)) { - tf->recordMCArtefactTracklet(mcLabelKey(lab), transition.fromLayer); - } - } -} - -template -void recordMCCellCoverage(TimeFrame* tf, - const typename TrackingTopology::View& topology) -{ - for (typename TrackingTopology::Id cellTopologyId = 0; cellTopologyId < topology.nCells; ++cellTopologyId) { - const auto& cellTopology = topology.getCell(cellTopologyId); - const auto& firstTransition = topology.getTransition(cellTopology.firstTransition); - const auto& secondTransition = topology.getTransition(cellTopology.secondTransition); - const auto& labels = tf->getCellsLabel(cellTopologyId); - for (const auto& lab : labels) { - if (!isUsableMCLabel(lab)) { - continue; - } - const uint64_t key = mcLabelKey(lab); - if (firstTransition.toLayer == firstTransition.fromLayer + 1) { - tf->recordMCArtefactCell(key, firstTransition.fromLayer); - } - if (secondTransition.toLayer == secondTransition.fromLayer + 1) { - tf->recordMCArtefactCell(key, secondTransition.fromLayer); - } - } - } -} -} // namespace detail - template void TrackerTraits::computeLayerTracklets(const int iteration, int iVertex) { @@ -195,14 +137,22 @@ void TrackerTraits::computeLayerTracklets(const int iteration, int iVer transition.fromLayer, transition.toLayer, mTrkParams[iteration].LayerRadii[transition.fromLayer], meanDeltaZ, msAngle, phiCut, xProj, yProj, sigmaX, sigmaY); - float zVtxMin = 0.f; - float zVtxMax = 0.f; - detail::mftDiamondZExtents(pv.getZ(), pv.getSigmaZ(), mTrkParams[iteration].NSigmaCut, zVtxMin, zVtxMax); - detail::mftRSpreadAtLayer(currentCluster.radius, - detail::mftLayerZ(transition.fromLayer), - detail::mftLayerZ(transition.toLayer), - zVtxMin, zVtxMax, - lutRangeMin, lutRangeMax); + const float zSpread = mTrkParams[iteration].NSigmaCut * pv.getSigmaZ(); + const float zVtxMin = pv.getZ() - zSpread; + const float zVtxMax = pv.getZ() + zSpread; + const float zLayerFrom = detail::mftLayerZ(transition.fromLayer); + const float zLayerTo = detail::mftLayerZ(transition.toLayer); + const float absZFrom = std::abs(zLayerFrom); + const float absZTo = std::abs(zLayerTo); + const float denomMin = zVtxMax + absZFrom; + const float denomMax = absZFrom + zVtxMin; + lutRangeMin = (std::abs(denomMin) > 1.e-6f) ? currentCluster.radius * (zVtxMax + absZTo) / denomMin : currentCluster.radius; + lutRangeMax = (std::abs(denomMax) > 1.e-6f) ? currentCluster.radius * (absZTo + zVtxMin) / denomMax : currentCluster.radius; + if (lutRangeMin > lutRangeMax) { + const float tmp = lutRangeMin; + lutRangeMin = lutRangeMax; + lutRangeMax = tmp; + } colWindow = sigmaX * mTrkParams[iteration].NSigmaCut; rowWindow = sigmaY * mTrkParams[iteration].NSigmaCut; } else { @@ -267,7 +217,9 @@ void TrackerTraits::computeLayerTracklets(const int iteration, int iVer if (isMFT) { const float dx = nextCluster.xCoordinate - xProj; const float dy = nextCluster.yCoordinate - yProj; - const float transChi2 = detail::mftTrackletTransverseChi2(dx, dy, sigmaX, sigmaY); + const float invSigmaX2 = (sigmaX > 0.f) ? 1.f / (sigmaX * sigmaX) : 0.f; + const float invSigmaY2 = (sigmaY > 0.f) ? 1.f / (sigmaY * sigmaY) : 0.f; + const float transChi2 = dx * dx * invSigmaX2 + dy * dy * invSigmaY2; const float nSigmaCut2 = math_utils::Sq(mTrkParams[iteration].NSigmaCut); if (transChi2 < nSigmaCut2) { acceptTracklet = std::abs(meanDeltaZ) > 1.e-6f; @@ -375,12 +327,6 @@ void TrackerTraits::computeLayerTracklets(const int iteration, int iVer } }); } - - if (mTimeFrame->hasMCinformation() && mTrkParams[iteration].CreateArtefactLabels) { - for (int transitionId = 0; transitionId < topology.nTransitions; ++transitionId) { - detail::recordMCTrackletCoverage(mTimeFrame, topology, transitionId); - } - } }); } @@ -428,14 +374,14 @@ void TrackerTraits::computeLayerCells(const int iteration) float chi2{0.f}; bool good{false}; if constexpr (DetectorTraits::DetId == o2::detectors::DetID::MFT) { - const auto& cluster1_glo = mTimeFrame->getUnsortedClusters()[firstTransition.fromLayer][clusId[0]]; - const auto& cluster2_glo = mTimeFrame->getUnsortedClusters()[firstTransition.toLayer][clusId[1]]; - const auto& cluster3_glo = mTimeFrame->getUnsortedClusters()[secondTransition.toLayer][clusId[2]]; + const auto& cluster1_glo = mTimeFrame->getUnsortedClusters()[hitLayers[0]][clusId[0]]; + const auto& cluster2_glo = mTimeFrame->getUnsortedClusters()[hitLayers[1]][clusId[1]]; + const auto& cluster3_glo = mTimeFrame->getUnsortedClusters()[hitLayers[2]][clusId[2]]; const float r2Cut = mTrkParams[iteration].CellRoadRCut * mTrkParams[iteration].CellRoadRCut; - if (!DetectorTraits::validateMFTCellClusters(cluster1_glo, hitLayers[0], - cluster2_glo, hitLayers[1], - cluster3_glo, hitLayers[2], - r2Cut)) { + if (!detail::validateMFTCellClusters(cluster1_glo, hitLayers[0], + cluster2_glo, hitLayers[1], + cluster3_glo, hitLayers[2], + r2Cut)) { continue; } o2::track::TrackParCovFwd fwdTrack; @@ -565,10 +511,6 @@ void TrackerTraits::computeLayerCells(const int iteration) } }); - if (mTimeFrame->hasMCinformation() && mTrkParams[iteration].CreateArtefactLabels) { - detail::recordMCCellCoverage(mTimeFrame, topology); - } - for (int transitionId = 0; transitionId < topology.nTransitions; ++transitionId) { deepVectorClear(mTimeFrame->getTracklets()[transitionId]); deepVectorClear(mTimeFrame->getTrackletsLabel(transitionId)); @@ -760,14 +702,39 @@ void TrackerTraits::processNeighbours(int iteration, int defaultCellTop seed.getTimeStamp() = currentCell.getTimeStamp(); seed.getTimeStamp() += neighbourCell.getTimeStamp(); - if (!DetectorTraits::attachNeighbourToSeed(seed, - neighbourLayer, - neighbourCluster, - *mTimeFrame, - mTrkParams[iteration], - getBz(), - propagator)) { - continue; + if constexpr (DetectorTraits::DetId == o2::detectors::DetID::MFT) { + o2::track::TrackParCovFwd fwdTrack; + float chi2 = seed.getChi2(); + if (!detail::mftFwdAttachNeighbourToSeed(seed, neighbourLayer, neighbourCluster, *mTimeFrame, mTrkParams[iteration], getBz(), fwdTrack, chi2)) { + continue; + } + static_cast(seed) = fwdTrack; + seed.setChi2(chi2); + } else { + const auto& trHit = mTimeFrame->getTrackingFrameInfoOnLayer(neighbourLayer)[neighbourCluster]; + + if (!seed.rotate(trHit.alphaTrackingFrame)) { + continue; + } + + if (!propagator->propagateToX(seed, trHit.xTrackingFrame, getBz(), o2::base::PropagatorImpl::MAX_SIN_PHI, o2::base::PropagatorImpl::MAX_STEP, mTrkParams[iteration].CorrType)) { + continue; + } + + if (mTrkParams[iteration].CorrType == o2::base::PropagatorF::MatCorrType::USEMatCorrNONE) { + if (!seed.correctForMaterial(mTrkParams[iteration].LayerxX0[neighbourLayer], mTrkParams[iteration].LayerxX0[neighbourLayer] * o2::its::constants::Radl * o2::its::constants::Rho, true)) { + continue; + } + } + + auto predChi2{seed.getPredictedChi2Quiet(trHit.positionTrackingFrame, trHit.covarianceTrackingFrame)}; + if ((predChi2 > mTrkParams[iteration].MaxChi2ClusterAttachment) || predChi2 < 0.f) { + continue; + } + seed.setChi2(seed.getChi2() + predChi2); + if (!seed.o2::track::TrackParCov::update(trHit.positionTrackingFrame, trHit.covarianceTrackingFrame)) { + continue; + } } if constexpr (decltype(Tag)::value != PassMode::TwoPassCount::value) { @@ -946,7 +913,12 @@ void TrackerTraits::findRoads(const int iteration) deepVectorClear(trackSeeds); }); - DetectorTraits::sortRefittedTracks(tracks); + // Same ordering as o2::its::track::isBetter (longer track, then lower chi2). + std::sort(tracks.begin(), tracks.end(), [](const TrackT& a, const TrackT& b) { + const auto ncla = a.getNumberOfClusters(); + const auto nclb = b.getNumberOfClusters(); + return (ncla == nclb) ? (a.getChi2() < b.getChi2()) : ncla > nclb; + }); acceptTracks(iteration, tracks, firstClusters); } markTracks(iteration); @@ -1014,7 +986,10 @@ void TrackerTraits::acceptTracks(int iteration, bounded_vector smallestROFHalf) { track.getTimeStamp().setTimeStampError(smallestROFHalf); } - DetectorTraits::finalizeAcceptedTrack(track); + if constexpr (DetectorTraits::DetId == o2::detectors::DetID::ITS) { + track.setUserField(0); + track.getParamOut().setUserField(0); + } trks.emplace_back(track); if (mTrkParams[iteration].AllowSharingFirstCluster) { @@ -1050,8 +1025,16 @@ void TrackerTraits::markTracks(int iteration) if (std::abs(t1.getEta() - t2.getEta()) > mTrkParams[iteration].SharedClusterMaxDeltaEta) { return false; } - if (mTrkParams[iteration].SharedClusterOppositeSign && DetectorTraits::sameTrackSign(t1, t2)) { - return false; + if (mTrkParams[iteration].SharedClusterOppositeSign) { + if constexpr (DetectorTraits::DetId == o2::detectors::DetID::MFT) { + if (t1.getCharge() == t2.getCharge()) { + return false; + } + } else { + if (t1.getSign() == t2.getSign()) { + return false; + } + } } return true; }; diff --git a/Detectors/ITSMFT/common/tracking/src/TrackingInterface.cxx b/Detectors/ITSMFT/common/tracking/src/TrackingInterface.cxx index d7347b13f8bfb..77dbbe052350d 100644 --- a/Detectors/ITSMFT/common/tracking/src/TrackingInterface.cxx +++ b/Detectors/ITSMFT/common/tracking/src/TrackingInterface.cxx @@ -31,7 +31,6 @@ #include "DetectorsBase/GRPGeomHelper.h" #include "DetectorsBase/Propagator.h" #include "Framework/Logger.h" -#include "ITSMFTTracking/TrackingParamRef.h" #include "MFTTracking/MFTTrackingParam.h" namespace o2::itsmft::tracking